diff --git a/api/v2.0/swagger.yaml b/api/v2.0/swagger.yaml index eb874e131..d9e5ba559 100644 --- a/api/v2.0/swagger.yaml +++ b/api/v2.0/swagger.yaml @@ -814,6 +814,151 @@ paths: $ref: '#/responses/404' '500': $ref: '#/responses/500' + /projects/{project_name}/preheat/policies: + post: + summary: Create a preheat policy under a project + description: Create a preheat policy under a project + tags: + - preheat + operationId: CreatePolicy + parameters: + - $ref: '#/parameters/requestId' + - $ref: '#/parameters/projectName' + - name: policy + in: body + description: The policy schema info + required: true + schema: + $ref: '#/definitions/PreheatPolicy' + responses: + '201': + description: Create policy success + schema: + $ref: '#/responses/201' + '400': + $ref: '#/responses/400' + '401': + $ref: '#/responses/401' + '403': + $ref: '#/responses/403' + '409': + $ref: '#/responses/409' + '500': + $ref: '#/responses/500' + get: + summary: List preheat policies + description: List preheat policies + tags: + - preheat + operationId: ListPolicies + parameters: + - $ref: '#/parameters/requestId' + - $ref: '#/parameters/projectName' + - $ref: '#/parameters/page' + - $ref: '#/parameters/pageSize' + - $ref: '#/parameters/query' + responses: + '200': + description: List preheat policies success + headers: + X-Total-Count: + description: The total count of tags + type: integer + Link: + description: Link refers to the previous page and next page + type: string + schema: + type: array + items: + $ref: '#/definitions/PreheatPolicy' + '400': + $ref: '#/responses/400' + '401': + $ref: '#/responses/401' + '403': + $ref: '#/responses/403' + '500': + $ref: '#/responses/500' + /projects/{project_name}/preheat/policies/{preheat_policy_name}: + get: + summary: Get a preheat policy + description: Get a preheat policy + tags: + - preheat + operationId: GetPolicy + parameters: + - $ref: '#/parameters/requestId' + - $ref: '#/parameters/projectName' + - $ref: '#/parameters/preheatPolicyName' + responses: + '200': + description: Get a preheat policy success + schema: + $ref: '#/definitions/PreheatPolicy' + '400': + $ref: '#/responses/400' + '401': + $ref: '#/responses/401' + '404': + $ref: '#/responses/404' + '403': + $ref: '#/responses/403' + '500': + $ref: '#/responses/500' + put: + summary: Update preheat policy + description: Update preheat policy + tags: + - preheat + operationId: UpdatePolicy + parameters: + - $ref: '#/parameters/requestId' + - $ref: '#/parameters/projectName' + - $ref: '#/parameters/preheatPolicyName' + - name: policy + in: body + description: The policy schema info + required: true + schema: + $ref: '#/definitions/PreheatPolicy' + responses: + '200': + $ref: '#/responses/200' + '400': + $ref: '#/responses/400' + '401': + $ref: '#/responses/401' + '404': + $ref: '#/responses/404' + '403': + $ref: '#/responses/403' + '409': + $ref: '#/responses/409' + '500': + $ref: '#/responses/500' + delete: + summary: Delete a preheat policy + description: Delete a preheat policy + tags: + - preheat + operationId: DeletePolicy + parameters: + - $ref: '#/parameters/requestId' + - $ref: '#/parameters/projectName' + - $ref: '#/parameters/preheatPolicyName' + responses: + '200': + $ref: '#/responses/200' + '400': + $ref: '#/responses/400' + '401': + $ref: '#/responses/401' + '404': + $ref: '#/responses/404' + '403': + $ref: '#/responses/403' + '500': + $ref: '#/responses/500' parameters: query: name: q @@ -880,6 +1025,13 @@ parameters: description: Instance ID required: true type: integer + preheatPolicyName: + name: preheat_policy_name + in: path + description: Preheat Policy Name + required: true + type: string + responses: '200': description: Success @@ -1360,3 +1512,38 @@ definitions: id: type: integer description: ID of instance created + PreheatPolicy: + type: object + properties: + id: + type: integer + description: The ID of preheat policy + name: + type: string + description: The Name of preheat policy + description: + type: string + description: The Description of preheat policy + project_id: + type: integer + description: The ID of preheat policy project + provider_id: + type: integer + description: The ID of preheat policy provider + filters: + type: string + description: The Filters of preheat policy + trigger: + type: string + description: The Trigger of preheat policy + enabled: + type: boolean + description: Whether the preheat policy enabled + creation_time: + type: string + format: date-time + description: The Create Time of preheat policy + update_time: + type: string + format: date-time + description: The Update Time of preheat policy diff --git a/src/controller/p2p/preheat/controller.go b/src/controller/p2p/preheat/controller.go index ad4d9592a..ae4888f0a 100644 --- a/src/controller/p2p/preheat/controller.go +++ b/src/controller/p2p/preheat/controller.go @@ -6,9 +6,10 @@ import ( "time" "github.com/goharbor/harbor/src/lib/q" - "github.com/goharbor/harbor/src/pkg/p2p/preheat/instance" + policyModels "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/policy" providerModels "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/provider" + "github.com/goharbor/harbor/src/pkg/p2p/preheat/policy" "github.com/goharbor/harbor/src/pkg/p2p/preheat/provider" ) @@ -85,6 +86,25 @@ type Controller interface { // Any problems met, a non nil error will be returned // UpdateInstance(ctx context.Context, instance *providerModels.Instance, properties ...string) error + + // do not provide another policy controller, mixed in preheat controller + + // CountPolicy returns the total count of the policy. + CountPolicy(ctx context.Context, query *q.Query) (int64, error) + // CreatePolicy creates the policy. + CreatePolicy(ctx context.Context, schema *policyModels.Schema) (int64, error) + // GetPolicy gets the policy by id. + GetPolicy(ctx context.Context, id int64) (*policyModels.Schema, error) + // GetPolicyByName gets the policy by name. + GetPolicyByName(ctx context.Context, projectID int64, name string) (*policyModels.Schema, error) + // UpdatePolicy updates the policy. + UpdatePolicy(ctx context.Context, schema *policyModels.Schema, props ...string) error + // DeletePolicy deletes the policy by id. + DeletePolicy(ctx context.Context, id int64) error + // ListPolicies lists policies by query. + ListPolicies(ctx context.Context, query *q.Query) ([]*policyModels.Schema, error) + // ListPoliciesByProject lists policies by project. + ListPoliciesByProject(ctx context.Context, project int64, query *q.Query) ([]*policyModels.Schema, error) } var _ Controller = (*controller)(nil) @@ -94,32 +114,35 @@ var _ Controller = (*controller)(nil) type controller struct { // For instance iManager instance.Manager + // For policy + pManager policy.Manager } // NewController is constructor of controller func NewController() Controller { return &controller{ iManager: instance.Mgr, + pManager: policy.Mgr, } } // GetAvailableProviders implements @Controller.GetAvailableProviders -func (cc *controller) GetAvailableProviders() ([]*provider.Metadata, error) { +func (c *controller) GetAvailableProviders() ([]*provider.Metadata, error) { return provider.ListProviders() } // CountInstance implements @Controller.CountInstance -func (cc *controller) CountInstance(ctx context.Context, query *q.Query) (int64, error) { - return cc.iManager.Count(ctx, query) +func (c *controller) CountInstance(ctx context.Context, query *q.Query) (int64, error) { + return c.iManager.Count(ctx, query) } -// List implements @Controller.ListInstance -func (cc *controller) ListInstance(ctx context.Context, query *q.Query) ([]*providerModels.Instance, error) { - return cc.iManager.List(ctx, query) +// ListInstance implements @Controller.ListInstance +func (c *controller) ListInstance(ctx context.Context, query *q.Query) ([]*providerModels.Instance, error) { + return c.iManager.List(ctx, query) } // CreateInstance implements @Controller.CreateInstance -func (cc *controller) CreateInstance(ctx context.Context, instance *providerModels.Instance) (int64, error) { +func (c *controller) CreateInstance(ctx context.Context, instance *providerModels.Instance) (int64, error) { if instance == nil { return 0, errors.New("nil instance object provided") } @@ -130,7 +153,7 @@ func (cc *controller) CreateInstance(ctx context.Context, instance *providerMode "endpoint": instance.Endpoint, }, } - num, err := cc.iManager.Count(ctx, query) + num, err := c.iManager.Count(ctx, query) if err != nil { return 0, err } @@ -145,24 +168,64 @@ func (cc *controller) CreateInstance(ctx context.Context, instance *providerMode instance.SetupTimestamp = time.Now().Unix() - return cc.iManager.Save(ctx, instance) + return c.iManager.Save(ctx, instance) } -// Delete implements @Controller.Delete -func (cc *controller) DeleteInstance(ctx context.Context, id int64) error { - return cc.iManager.Delete(ctx, id) +// DeleteInstance implements @Controller.Delete +func (c *controller) DeleteInstance(ctx context.Context, id int64) error { + return c.iManager.Delete(ctx, id) } -// Update implements @Controller.Update -func (cc *controller) UpdateInstance(ctx context.Context, instance *providerModels.Instance, properties ...string) error { +// UpdateInstance implements @Controller.Update +func (c *controller) UpdateInstance(ctx context.Context, instance *providerModels.Instance, properties ...string) error { if len(properties) == 0 { return errors.New("no properties provided to update") } - return cc.iManager.Update(ctx, instance, properties...) + return c.iManager.Update(ctx, instance, properties...) } -// Get implements @Controller.Get -func (cc *controller) GetInstance(ctx context.Context, id int64) (*providerModels.Instance, error) { - return cc.iManager.Get(ctx, id) +// GetInstance implements @Controller.Get +func (c *controller) GetInstance(ctx context.Context, id int64) (*providerModels.Instance, error) { + return c.iManager.Get(ctx, id) +} + +// CountPolicy returns the total count of the policy. +func (c *controller) CountPolicy(ctx context.Context, query *q.Query) (int64, error) { + return c.pManager.Count(ctx, query) +} + +// CreatePolicy creates the policy. +func (c *controller) CreatePolicy(ctx context.Context, schema *policyModels.Schema) (int64, error) { + return c.pManager.Create(ctx, schema) +} + +// GetPolicy gets the policy by id. +func (c *controller) GetPolicy(ctx context.Context, id int64) (*policyModels.Schema, error) { + return c.pManager.Get(ctx, id) +} + +// GetPolicyByName gets the policy by name. +func (c *controller) GetPolicyByName(ctx context.Context, projectID int64, name string) (*policyModels.Schema, error) { + return c.pManager.GetByName(ctx, projectID, name) +} + +// UpdatePolicy updates the policy. +func (c *controller) UpdatePolicy(ctx context.Context, schema *policyModels.Schema, props ...string) error { + return c.pManager.Update(ctx, schema, props...) +} + +// DeletePolicy deletes the policy by id. +func (c *controller) DeletePolicy(ctx context.Context, id int64) error { + return c.pManager.Delete(ctx, id) +} + +// ListPolicies lists policies by query. +func (c *controller) ListPolicies(ctx context.Context, query *q.Query) ([]*policyModels.Schema, error) { + return c.pManager.ListPolicies(ctx, query) +} + +// ListPoliciesByProject lists policies by project. +func (c *controller) ListPoliciesByProject(ctx context.Context, project int64, query *q.Query) ([]*policyModels.Schema, error) { + return c.pManager.ListPoliciesByProject(ctx, project, query) } diff --git a/src/controller/p2p/preheat/controllor_test.go b/src/controller/p2p/preheat/controllor_test.go index bee479f57..4744982c9 100644 --- a/src/controller/p2p/preheat/controllor_test.go +++ b/src/controller/p2p/preheat/controllor_test.go @@ -6,9 +6,11 @@ import ( "testing" "github.com/goharbor/harbor/src/core/config" + "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/policy" providerModel "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/provider" "github.com/goharbor/harbor/src/pkg/p2p/preheat/provider" "github.com/goharbor/harbor/src/testing/pkg/p2p/preheat/instance" + pmocks "github.com/goharbor/harbor/src/testing/pkg/p2p/preheat/policy" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" @@ -16,24 +18,28 @@ import ( type preheatSuite struct { suite.Suite - ctx context.Context - controller Controller - fackManager *instance.FakeManager + ctx context.Context + controller Controller + fakeInstanceMgr *instance.FakeManager + fakePolicyMgr *pmocks.FakeManager } func TestPreheatSuite(t *testing.T) { t.Log("Start TestPreheatSuite") - fackManager := &instance.FakeManager{} + fakeInstanceMgr := &instance.FakeManager{} + fakePolicyMgr := &pmocks.FakeManager{} var c = &controller{ - iManager: fackManager, + iManager: fakeInstanceMgr, + pManager: fakePolicyMgr, } assert.NotNil(t, c) suite.Run(t, &preheatSuite{ - ctx: context.Background(), - controller: c, - fackManager: fackManager, + ctx: context.Background(), + controller: c, + fakeInstanceMgr: fakeInstanceMgr, + fakePolicyMgr: fakePolicyMgr, }) } @@ -45,7 +51,7 @@ func TestNewController(t *testing.T) { func (s *preheatSuite) SetupSuite() { config.Init() - s.fackManager.On("List", mock.Anything, mock.Anything).Return([]*providerModel.Instance{ + s.fakeInstanceMgr.On("List", mock.Anything, mock.Anything).Return([]*providerModel.Instance{ { ID: 1, Vendor: "dragonfly", @@ -54,16 +60,16 @@ func (s *preheatSuite) SetupSuite() { Enabled: true, }, }, nil) - s.fackManager.On("Save", mock.Anything, mock.Anything).Return(int64(1), nil) - s.fackManager.On("Count", mock.Anything, &providerModel.Instance{Endpoint: "http://localhost"}).Return(int64(1), nil) - s.fackManager.On("Count", mock.Anything, mock.Anything).Return(int64(0), nil) - s.fackManager.On("Delete", mock.Anything, int64(1)).Return(nil) - s.fackManager.On("Delete", mock.Anything, int64(0)).Return(errors.New("not found")) - s.fackManager.On("Get", mock.Anything, int64(1)).Return(&providerModel.Instance{ + s.fakeInstanceMgr.On("Save", mock.Anything, mock.Anything).Return(int64(1), nil) + s.fakeInstanceMgr.On("Count", mock.Anything, &providerModel.Instance{Endpoint: "http://localhost"}).Return(int64(1), nil) + s.fakeInstanceMgr.On("Count", mock.Anything, mock.Anything).Return(int64(0), nil) + s.fakeInstanceMgr.On("Delete", mock.Anything, int64(1)).Return(nil) + s.fakeInstanceMgr.On("Delete", mock.Anything, int64(0)).Return(errors.New("not found")) + s.fakeInstanceMgr.On("Get", mock.Anything, int64(1)).Return(&providerModel.Instance{ ID: 1, Endpoint: "http://localhost", }, nil) - s.fackManager.On("Get", mock.Anything, int64(0)).Return(nil, errors.New("not found")) + s.fakeInstanceMgr.On("Get", mock.Anything, int64(0)).Return(nil, errors.New("not found")) } func (s *preheatSuite) TestGetAvailableProviders() { @@ -136,14 +142,14 @@ func (s *preheatSuite) TestDeleteInstance() { func (s *preheatSuite) TestUpdateInstance() { // TODO: test update more - s.fackManager.On("Update", s.ctx, nil).Return(errors.New("no properties provided to update")) + s.fakeInstanceMgr.On("Update", s.ctx, nil).Return(errors.New("no properties provided to update")) err := s.controller.UpdateInstance(s.ctx, nil) s.Error(err) err = s.controller.UpdateInstance(s.ctx, &providerModel.Instance{ID: 0}) s.Error(err) - s.fackManager.On("Update", mock.Anything, mock.Anything, mock.Anything).Return(nil) + s.fakeInstanceMgr.On("Update", mock.Anything, mock.Anything, mock.Anything).Return(nil) err = s.controller.UpdateInstance(s.ctx, &providerModel.Instance{ID: 1}, "enabled") s.NoError(err) } @@ -153,3 +159,57 @@ func (s *preheatSuite) TestGetInstance() { s.NoError(err) s.NotNil(inst) } + +func (s *preheatSuite) TestCountPolicy() { + s.fakePolicyMgr.On("Count", s.ctx, mock.Anything).Return(int64(1), nil) + id, err := s.controller.CountPolicy(s.ctx, nil) + s.NoError(err) + s.Equal(int64(1), id) +} + +func (s *preheatSuite) TestCreatePolicy() { + s.fakePolicyMgr.On("Create", s.ctx, mock.Anything).Return(int64(1), nil) + id, err := s.controller.CreatePolicy(s.ctx, nil) + s.NoError(err) + s.Equal(int64(1), id) +} + +func (s *preheatSuite) TestGetPolicy() { + s.fakePolicyMgr.On("Get", s.ctx, int64(1)).Return(&policy.Schema{Name: "test"}, nil) + p, err := s.controller.GetPolicy(s.ctx, 1) + s.NoError(err) + s.Equal("test", p.Name) +} + +func (s *preheatSuite) TestGetPolicyByName() { + s.fakePolicyMgr.On("GetByName", s.ctx, int64(1), "test").Return(&policy.Schema{Name: "test"}, nil) + p, err := s.controller.GetPolicyByName(s.ctx, 1, "test") + s.NoError(err) + s.Equal("test", p.Name) +} + +func (s *preheatSuite) TestUpdatePolicy() { + s.fakePolicyMgr.On("Update", s.ctx, mock.Anything, mock.Anything).Return(nil) + err := s.controller.UpdateInstance(s.ctx, nil, "") + s.NoError(err) +} + +func (s *preheatSuite) TestDeletePolicy() { + s.fakePolicyMgr.On("Delete", s.ctx, int64(1)).Return(nil) + err := s.controller.DeletePolicy(s.ctx, 1) + s.NoError(err) +} + +func (s *preheatSuite) TestListPolicies() { + s.fakePolicyMgr.On("ListPolicies", s.ctx, mock.Anything).Return([]*policy.Schema{}, nil) + p, err := s.controller.ListPolicies(s.ctx, nil) + s.NoError(err) + s.NotNil(p) +} + +func (s *preheatSuite) TestListPoliciesByProject() { + s.fakePolicyMgr.On("ListPoliciesByProject", s.ctx, int64(1), mock.Anything).Return([]*policy.Schema{}, nil) + p, err := s.controller.ListPoliciesByProject(s.ctx, 1, nil) + s.NoError(err) + s.NotNil(p) +} diff --git a/src/pkg/p2p/preheat/dao/policy/dao.go b/src/pkg/p2p/preheat/dao/policy/dao.go index 99fbe8b50..e1a863cdb 100644 --- a/src/pkg/p2p/preheat/dao/policy/dao.go +++ b/src/pkg/p2p/preheat/dao/policy/dao.go @@ -34,6 +34,8 @@ type DAO interface { Update(ctx context.Context, schema *policy.Schema, props ...string) (err error) // Get the policy schema by id Get(ctx context.Context, id int64) (schema *policy.Schema, err error) + // Get the policy schema by name + GetByName(ctx context.Context, projectID int64, name string) (schema *policy.Schema, err error) // Delete the policy schema by id Delete(ctx context.Context, id int64) (err error) // List policy schemas by query @@ -122,6 +124,25 @@ func (d *dao) Get(ctx context.Context, id int64) (schema *policy.Schema, err err return schema, nil } +// GetByName gets a policy schema by name. +func (d *dao) GetByName(ctx context.Context, projectID int64, name string) (schema *policy.Schema, err error) { + var ormer beego_orm.Ormer + ormer, err = orm.FromContext(ctx) + if err != nil { + return + } + + schema = &policy.Schema{Name: name, ProjectID: projectID} + if err = ormer.Read(schema, "Name", "ProjectID"); err != nil { + if e := orm.AsNotFoundError(err, "policy %s not found", name); e != nil { + err = e + } + return nil, err + } + + return schema, nil +} + // Delete a policy schema by id. func (d *dao) Delete(ctx context.Context, id int64) (err error) { var ormer beego_orm.Ormer diff --git a/src/pkg/p2p/preheat/dao/policy/dao_test.go b/src/pkg/p2p/preheat/dao/policy/dao_test.go index 1c3ba84a8..b32eec45c 100644 --- a/src/pkg/p2p/preheat/dao/policy/dao_test.go +++ b/src/pkg/p2p/preheat/dao/policy/dao_test.go @@ -107,6 +107,19 @@ func (d *daoTestSuite) TestGet() { d.True(errors.IsErr(err, errors.NotFoundCode)) } +// GetByName tests get a policy schema by name. +func (d *daoTestSuite) TestGetByName() { + policy, err := d.dao.GetByName(d.ctx, 1, "default-policy") + d.Require().Nil(err) + d.Require().NotNil(policy) + d.Equal(d.defaultPolicy.Name, policy.Name, "get a default policy") + + // not found + _, err = d.dao.GetByName(d.ctx, 2, "default-policy") + d.Require().NotNil(err) + d.True(errors.IsErr(err, errors.NotFoundCode)) +} + // Update tests update a policy schema. func (d *daoTestSuite) TestUpdate() { newDesc := "test update" diff --git a/src/pkg/p2p/preheat/policy/manager.go b/src/pkg/p2p/preheat/policy/manager.go index 1f6170a3f..a951bfb70 100644 --- a/src/pkg/p2p/preheat/policy/manager.go +++ b/src/pkg/p2p/preheat/policy/manager.go @@ -35,6 +35,8 @@ type Manager interface { Update(ctx context.Context, schema *policy.Schema, props ...string) (err error) // Get the policy schema by id Get(ctx context.Context, id int64) (schema *policy.Schema, err error) + // GetByName the policy schema by id + GetByName(ctx context.Context, projectID int64, name string) (schema *policy.Schema, err error) // Delete the policy schema by id Delete(ctx context.Context, id int64) (err error) // List policy schemas by query @@ -74,6 +76,11 @@ func (m *manager) Get(ctx context.Context, id int64) (schema *policy.Schema, err return m.dao.Get(ctx, id) } +// Get the policy schema by name +func (m *manager) GetByName(ctx context.Context, projectID int64, name string) (schema *policy.Schema, err error) { + return m.dao.GetByName(ctx, projectID, name) +} + // Delete the policy schema by id func (m *manager) Delete(ctx context.Context, id int64) (err error) { return m.dao.Delete(ctx, id) diff --git a/src/pkg/p2p/preheat/policy/manager_test.go b/src/pkg/p2p/preheat/policy/manager_test.go index a89915101..4df5c3936 100644 --- a/src/pkg/p2p/preheat/policy/manager_test.go +++ b/src/pkg/p2p/preheat/policy/manager_test.go @@ -48,6 +48,14 @@ func (f *fakeDao) Get(ctx context.Context, id int64) (*policy.Schema, error) { } return schema, args.Error(1) } +func (f *fakeDao) GetByName(ctx context.Context, projectID int64, name string) (*policy.Schema, error) { + args := f.Called() + var schema *policy.Schema + if args.Get(0) != nil { + schema = args.Get(0).(*policy.Schema) + } + return schema, args.Error(1) +} func (f *fakeDao) Delete(ctx context.Context, id int64) error { args := f.Called() return args.Error(0) @@ -112,6 +120,13 @@ func (m *managerTestSuite) TestGet() { m.Require().Nil(err) } +// TestGetByName tests Get method. +func (m *managerTestSuite) TestGetByName() { + m.dao.On("GetByName").Return(nil, nil) + _, err := m.mgr.Get(nil, 1) + m.Require().Nil(err) +} + // TestDelete tests Delete method. func (m *managerTestSuite) TestDelete() { m.dao.On("Delete").Return(nil) diff --git a/src/server/v2.0/handler/preheat.go b/src/server/v2.0/handler/preheat.go index 1f43753f4..4ff863f1c 100644 --- a/src/server/v2.0/handler/preheat.go +++ b/src/server/v2.0/handler/preheat.go @@ -2,9 +2,16 @@ package handler import ( "context" + "errors" + "time" + + "github.com/go-openapi/strfmt" + + "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/policy" "github.com/go-openapi/runtime/middleware" preheatCtl "github.com/goharbor/harbor/src/controller/p2p/preheat" + projectCtl "github.com/goharbor/harbor/src/controller/project" "github.com/goharbor/harbor/src/pkg/p2p/preheat/provider" "github.com/goharbor/harbor/src/server/v2.0/models" "github.com/goharbor/harbor/src/server/v2.0/restapi" @@ -14,6 +21,7 @@ import ( func newPreheatAPI() *preheatAPI { return &preheatAPI{ preheatCtl: preheatCtl.Ctl, + projectCtl: projectCtl.Ctl, } } @@ -22,6 +30,7 @@ var _ restapi.PreheatAPI = (*preheatAPI)(nil) type preheatAPI struct { BaseAPI preheatCtl preheatCtl.Controller + projectCtl projectCtl.Controller } func (api *preheatAPI) Prepare(ctx context.Context, operation string, params interface{}) middleware.Responder { @@ -80,3 +89,149 @@ func convertProvidersToFrontend(backend []*provider.Metadata) (frontend []*model } return } + +// GetPolicy is Get a preheat policy +func (api *preheatAPI) GetPolicy(ctx context.Context, params operation.GetPolicyParams) middleware.Responder { + project, err := api.projectCtl.GetByName(ctx, params.ProjectName) + if err != nil { + return api.SendError(ctx, err) + } + + var payload *models.PreheatPolicy + policy, err := api.preheatCtl.GetPolicyByName(ctx, project.ProjectID, params.PreheatPolicyName) + if err != nil { + return api.SendError(ctx, err) + } + + payload, err = convertPolicyToPayload(policy) + if err != nil { + return api.SendError(ctx, err) + } + return operation.NewGetPolicyOK().WithPayload(payload) +} + +// CreatePolicy is Create a preheat policy under a project +func (api *preheatAPI) CreatePolicy(ctx context.Context, params operation.CreatePolicyParams) middleware.Responder { + policy, err := convertParamPolicyToModelPolicy(params.Policy) + if err != nil { + return api.SendError(ctx, err) + } + + _, err = api.preheatCtl.CreatePolicy(ctx, policy) + if err != nil { + return api.SendError(ctx, err) + } + return operation.NewCreatePolicyCreated() +} + +// UpdatePolicy is Update preheat policy +func (api *preheatAPI) UpdatePolicy(ctx context.Context, params operation.UpdatePolicyParams) middleware.Responder { + policy, err := convertParamPolicyToModelPolicy(params.Policy) + if err != nil { + return api.SendError(ctx, err) + } + + err = api.preheatCtl.UpdatePolicy(ctx, policy) + if err != nil { + return api.SendError(ctx, err) + } + return operation.NewUpdatePolicyOK() +} + +// DeletePolicy is Delete a preheat policy +func (api *preheatAPI) DeletePolicy(ctx context.Context, params operation.DeletePolicyParams) middleware.Responder { + project, err := api.projectCtl.GetByName(ctx, params.ProjectName) + if err != nil { + return api.SendError(ctx, err) + } + + policy, err := api.preheatCtl.GetPolicyByName(ctx, project.ProjectID, params.PreheatPolicyName) + if err != nil { + return api.SendError(ctx, err) + } + + err = api.preheatCtl.DeletePolicy(ctx, policy.ID) + if err != nil { + return api.SendError(ctx, err) + } + + return operation.NewDeleteInstanceOK() +} + +// ListPolicies is List preheat policies +func (api *preheatAPI) ListPolicies(ctx context.Context, params operation.ListPoliciesParams) middleware.Responder { + project, err := api.projectCtl.GetByName(ctx, params.ProjectName) + if err != nil { + return api.SendError(ctx, err) + } + + query, err := api.BuildQuery(ctx, params.Q, params.Page, params.PageSize) + if err != nil { + return api.SendError(ctx, err) + } + + if query != nil { + query.Keywords["project_id"] = project.ProjectID + } + + total, err := api.preheatCtl.CountPolicy(ctx, query) + if err != nil { + return api.SendError(ctx, err) + } + + policies, err := api.preheatCtl.ListPolicies(ctx, query) + if err != nil { + return api.SendError(ctx, err) + } + + var payload []*models.PreheatPolicy + for _, policy := range policies { + p, err := convertPolicyToPayload(policy) + if err != nil { + return api.SendError(ctx, err) + } + payload = append(payload, p) + } + return operation.NewListPoliciesOK().WithPayload(payload).WithXTotalCount(total). + WithLink(api.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()) +} + +// convertPolicyToPayload converts model policy to swagger model +func convertPolicyToPayload(policy *policy.Schema) (*models.PreheatPolicy, error) { + if policy == nil { + return nil, errors.New("policy can not be nil") + } + + return &models.PreheatPolicy{ + CreationTime: strfmt.DateTime(policy.CreatedAt), + Description: policy.Description, + Enabled: policy.Enabled, + Filters: policy.FiltersStr, + ID: policy.ID, + Name: policy.Name, + ProjectID: policy.ProjectID, + ProviderID: policy.ProviderID, + Trigger: policy.TriggerStr, + UpdateTime: strfmt.DateTime(policy.UpdatedTime), + }, nil +} + +// convertParamPolicyToPolicy converts params policy to pkg model policy +func convertParamPolicyToModelPolicy(model *models.PreheatPolicy) (*policy.Schema, error) { + if model == nil { + return nil, errors.New("policy can not be nil") + } + + return &policy.Schema{ + ID: model.ID, + Name: model.Name, + Description: model.Description, + ProjectID: model.ProjectID, + ProviderID: model.ProviderID, + FiltersStr: model.Filters, + TriggerStr: model.Trigger, + Enabled: model.Enabled, + CreatedAt: time.Time(model.CreationTime), + UpdatedTime: time.Time(model.UpdateTime), + }, nil +} diff --git a/src/server/v2.0/handler/preheat_test.go b/src/server/v2.0/handler/preheat_test.go index 9e7a34cac..27e862d88 100644 --- a/src/server/v2.0/handler/preheat_test.go +++ b/src/server/v2.0/handler/preheat_test.go @@ -3,9 +3,13 @@ package handler import ( "reflect" "testing" + "time" + "github.com/go-openapi/strfmt" + "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/policy" "github.com/goharbor/harbor/src/pkg/p2p/preheat/provider" "github.com/goharbor/harbor/src/server/v2.0/models" + "github.com/stretchr/testify/assert" ) func Test_convertProvidersToFrontend(t *testing.T) { @@ -31,3 +35,118 @@ func Test_convertProvidersToFrontend(t *testing.T) { }) } } + +func Test_convertPolicyToPayload(t *testing.T) { + tests := []struct { + name string + input *policy.Schema + expect *models.PreheatPolicy + shouldErr bool + }{ + { + name: "should error", + input: nil, + expect: nil, + shouldErr: true, + }, + { + name: "should success", + input: &policy.Schema{ + ID: 0, + Name: "abc", + Description: "test case", + ProjectID: 0, + ProviderID: 0, + Filters: nil, + FiltersStr: "", + Trigger: nil, + TriggerStr: "", + Enabled: false, + CreatedAt: time.Time{}, + UpdatedTime: time.Time{}, + }, + expect: &models.PreheatPolicy{ + CreationTime: strfmt.DateTime{}, + Description: "test case", + Enabled: false, + Filters: "", + ID: 0, + Name: "abc", + ProjectID: 0, + ProviderID: 0, + Trigger: "", + UpdateTime: strfmt.DateTime{}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual, err := convertPolicyToPayload(tt.input) + if !tt.shouldErr { + if !assert.Equal(t, tt.expect, actual) { + t.Errorf("convertPolicyToPayload() = %#v, want %#v", actual, tt.expect) + } + } else { + assert.NotNil(t, err) + } + }) + } +} + +func Test_convertParamPolicyToModelPolicy(t *testing.T) { + tests := []struct { + name string + input *models.PreheatPolicy + expect *policy.Schema + shouldErr bool + }{ + { + name: "should err", + input: nil, + expect: nil, + shouldErr: true, + }, + { + name: "should success", + input: &models.PreheatPolicy{ + CreationTime: strfmt.DateTime{}, + Description: "test case", + Enabled: false, + Filters: "", + ID: 0, + Name: "abc", + ProjectID: 0, + ProviderID: 0, + Trigger: "", + UpdateTime: strfmt.DateTime{}, + }, + expect: &policy.Schema{ + ID: 0, + Name: "abc", + Description: "test case", + ProjectID: 0, + ProviderID: 0, + Filters: nil, + FiltersStr: "", + Trigger: nil, + TriggerStr: "", + Enabled: false, + CreatedAt: time.Time{}, + UpdatedTime: time.Time{}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual, err := convertParamPolicyToModelPolicy(tt.input) + if !tt.shouldErr { + if !assert.Equal(t, tt.expect, actual) { + t.Errorf("convertParamPolicyToModelPolicy() = %#v, want %#v", actual, tt.expect) + } + } else { + assert.NotNil(t, err) + } + }) + } +} diff --git a/src/testing/pkg/p2p/preheat/policy/manager.go b/src/testing/pkg/p2p/preheat/policy/manager.go index e9d01f05c..c52e7f64d 100644 --- a/src/testing/pkg/p2p/preheat/policy/manager.go +++ b/src/testing/pkg/p2p/preheat/policy/manager.go @@ -95,6 +95,29 @@ func (_m *FakeManager) Get(ctx context.Context, id int64) (*modelspolicy.Schema, return r0, r1 } +// GetByName provides a mock function with given fields: ctx, projectId, name +func (_m *FakeManager) GetByName(ctx context.Context, projectId int64, name string) (*modelspolicy.Schema, error) { + ret := _m.Called(ctx, projectId, name) + + var r0 *modelspolicy.Schema + if rf, ok := ret.Get(0).(func(context.Context, int64, string) *modelspolicy.Schema); ok { + r0 = rf(ctx, projectId, name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*modelspolicy.Schema) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, int64, string) error); ok { + r1 = rf(ctx, projectId, name) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // ListPolicies provides a mock function with given fields: ctx, query func (_m *FakeManager) ListPolicies(ctx context.Context, query *q.Query) ([]*modelspolicy.Schema, error) { ret := _m.Called(ctx, query)