From 8b4fdfc2cc0a05e7953b6a9c3ede73a0d7042b34 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Tue, 5 Dec 2017 14:13:01 +0800 Subject: [PATCH] Add unit tests for replication related methods --- .travis.yml | 4 +- src/common/utils/test/policy_manager.go | 45 ++++++ .../utils/test/replication_controllter.go | 26 ++++ src/replication/core/controller.go | 63 +++++--- src/replication/core/controller_test.go | 144 ++++++++++++++++++ .../event/start_replication_handler.go | 3 +- .../event/start_replication_handler_test.go | 4 + src/replication/models/filter_item.go | 34 ++--- src/replication/models/filter_test.go | 45 ++++++ src/replication/models/trigger.go | 14 ++ src/replication/models/trigger_test.go | 41 +++++ src/replication/policy/manager.go | 45 ++++-- src/replication/policy/manager_test.go | 60 ++++++++ .../source/repository_convertor_test.go | 13 +- src/replication/source/tag_convertor_test.go | 9 -- src/replication/target/target.go | 38 +++++ src/ui/api/harborapi_test.go | 5 + src/ui/api/replication.go | 2 +- src/ui/api/replication_policy.go | 14 +- src/ui/api/replication_policy_test.go | 72 ++++----- src/ui/main.go | 4 +- 21 files changed, 564 insertions(+), 121 deletions(-) create mode 100644 src/common/utils/test/policy_manager.go create mode 100644 src/common/utils/test/replication_controllter.go create mode 100644 src/replication/core/controller_test.go create mode 100644 src/replication/models/filter_test.go create mode 100644 src/replication/models/trigger_test.go create mode 100644 src/replication/policy/manager_test.go create mode 100644 src/replication/target/target.go diff --git a/.travis.yml b/.travis.yml index 849deb7f3..294124bba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -89,8 +89,8 @@ script: - sudo mkdir -p ./make/common/config/registry/ - sudo mv ./tests/reg_config.yml ./make/common/config/registry/config.yml - sudo docker-compose -f ./make/docker-compose.test.yml up -d - - go list ./... | grep -v -E 'vendor|tests' | xargs -L1 fgt golint - - go list ./... | grep -v -E 'vendor|tests' | xargs -L1 go vet + - go list ./... | grep -v -E 'vendor|tests|test' | xargs -L1 fgt golint + - go list ./... | grep -v -E 'vendor|tests|test' | xargs -L1 go vet - export MYSQL_HOST=$IP - export REGISTRY_URL=$IP:5000 - echo $REGISTRY_URL diff --git a/src/common/utils/test/policy_manager.go b/src/common/utils/test/policy_manager.go new file mode 100644 index 000000000..492c88e00 --- /dev/null +++ b/src/common/utils/test/policy_manager.go @@ -0,0 +1,45 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "github.com/vmware/harbor/src/replication" + "github.com/vmware/harbor/src/replication/models" +) + +type FakePolicyManager struct { +} + +func (f *FakePolicyManager) GetPolicies(query models.QueryParameter) ([]models.ReplicationPolicy, error) { + return []models.ReplicationPolicy{}, nil +} + +func (f *FakePolicyManager) GetPolicy(id int64) (models.ReplicationPolicy, error) { + return models.ReplicationPolicy{ + ID: 1, + Trigger: &models.Trigger{ + Kind: replication.TriggerKindManual, + }, + }, nil +} +func (f *FakePolicyManager) CreatePolicy(policy models.ReplicationPolicy) (int64, error) { + return 1, nil +} +func (f *FakePolicyManager) UpdatePolicy(models.ReplicationPolicy) error { + return nil +} +func (f *FakePolicyManager) RemovePolicy(int64) error { + return nil +} diff --git a/src/common/utils/test/replication_controllter.go b/src/common/utils/test/replication_controllter.go new file mode 100644 index 000000000..adff7ae7a --- /dev/null +++ b/src/common/utils/test/replication_controllter.go @@ -0,0 +1,26 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +type FakeReplicatoinController struct { + FakePolicyManager +} + +func (f *FakeReplicatoinController) Init() error { + return nil +} +func (f *FakeReplicatoinController) Replicate(policyID int64, metadata ...map[string]interface{}) error { + return nil +} diff --git a/src/replication/core/controller.go b/src/replication/core/controller.go index 1bcc094a8..a3db273c9 100644 --- a/src/replication/core/controller.go +++ b/src/replication/core/controller.go @@ -1,26 +1,50 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package core import ( "fmt" - "github.com/vmware/harbor/src/common/dao" common_models "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/replication" "github.com/vmware/harbor/src/replication/models" "github.com/vmware/harbor/src/replication/policy" "github.com/vmware/harbor/src/replication/source" + "github.com/vmware/harbor/src/replication/target" "github.com/vmware/harbor/src/replication/trigger" ) -//Controller is core module to cordinate and control the overall workflow of the +// Controller defines the methods that a replicatoin controllter should implement +type Controller interface { + policy.Manager + Init() error + Replicate(policyID int64, metadata ...map[string]interface{}) error +} + +//DefaultController is core module to cordinate and control the overall workflow of the //replication modules. -type Controller struct { +type DefaultController struct { //Indicate whether the controller has been initialized or not initialized bool //Manage the policies - policyManager *policy.Manager + policyManager policy.Manager + + //Manage the targets + targetManager target.Manager //Handle the things related with source sourcer *source.Sourcer @@ -31,7 +55,7 @@ type Controller struct { //Keep controller as singleton instance var ( - DefaultController = NewController(ControllerConfig{}) //Use default data + GlobalController Controller = NewDefaultController(ControllerConfig{}) //Use default data ) //ControllerConfig includes related configurations required by the controller @@ -40,18 +64,19 @@ type ControllerConfig struct { CacheCapacity int } -//NewController is the constructor of Controller. -func NewController(config ControllerConfig) *Controller { +//NewDefaultController is the constructor of DefaultController. +func NewDefaultController(config ControllerConfig) *DefaultController { //Controller refer the default instances - return &Controller{ - policyManager: policy.NewManager(), + return &DefaultController{ + policyManager: policy.NewDefaultManager(), + targetManager: target.NewDefaultManager(), sourcer: source.NewSourcer(), triggerManager: trigger.NewManager(config.CacheCapacity), } } //Init will initialize the controller and the sub components -func (ctl *Controller) Init() error { +func (ctl *DefaultController) Init() error { if ctl.initialized { return nil } @@ -92,7 +117,7 @@ func (ctl *Controller) Init() error { } //CreatePolicy is used to create a new policy and enable it if necessary -func (ctl *Controller) CreatePolicy(newPolicy models.ReplicationPolicy) (int64, error) { +func (ctl *DefaultController) CreatePolicy(newPolicy models.ReplicationPolicy) (int64, error) { id, err := ctl.policyManager.CreatePolicy(newPolicy) if err != nil { return 0, err @@ -108,7 +133,7 @@ func (ctl *Controller) CreatePolicy(newPolicy models.ReplicationPolicy) (int64, //UpdatePolicy will update the policy with new content. //Parameter updatedPolicy must have the ID of the updated policy. -func (ctl *Controller) UpdatePolicy(updatedPolicy models.ReplicationPolicy) error { +func (ctl *DefaultController) UpdatePolicy(updatedPolicy models.ReplicationPolicy) error { // TODO check pre-conditions id := updatedPolicy.ID @@ -154,7 +179,7 @@ func (ctl *Controller) UpdatePolicy(updatedPolicy models.ReplicationPolicy) erro } //RemovePolicy will remove the specified policy and clean the related settings -func (ctl *Controller) RemovePolicy(policyID int64) error { +func (ctl *DefaultController) RemovePolicy(policyID int64) error { // TODO check pre-conditions policy, err := ctl.policyManager.GetPolicy(policyID) @@ -174,18 +199,18 @@ func (ctl *Controller) RemovePolicy(policyID int64) error { } //GetPolicy is delegation of GetPolicy of Policy.Manager -func (ctl *Controller) GetPolicy(policyID int64) (models.ReplicationPolicy, error) { +func (ctl *DefaultController) GetPolicy(policyID int64) (models.ReplicationPolicy, error) { return ctl.policyManager.GetPolicy(policyID) } //GetPolicies is delegation of GetPoliciemodels.ReplicationPolicy{}s of Policy.Manager -func (ctl *Controller) GetPolicies(query models.QueryParameter) ([]models.ReplicationPolicy, error) { +func (ctl *DefaultController) GetPolicies(query models.QueryParameter) ([]models.ReplicationPolicy, error) { return ctl.policyManager.GetPolicies(query) } //Replicate starts one replication defined in the specified policy; //Can be launched by the API layer and related triggers. -func (ctl *Controller) Replicate(policyID int64, metadata ...map[string]interface{}) error { +func (ctl *DefaultController) Replicate(policyID int64, metadata ...map[string]interface{}) error { policy, err := ctl.GetPolicy(policyID) if err != nil { return err @@ -210,7 +235,7 @@ func (ctl *Controller) Replicate(policyID int64, metadata ...map[string]interfac targets := []*common_models.RepTarget{} for _, targetID := range policy.TargetIDs { - target, err := dao.GetRepTarget(targetID) + target, err := ctl.targetManager.GetTarget(targetID) if err != nil { return err } @@ -259,13 +284,13 @@ func buildFilterChain(policy *models.ReplicationPolicy, sourcer *source.Sourcer) } repositoryPattern = fmt.Sprintf("%s/%s", projectPattern, repositoryPattern) - tagPattern, exist := patternMap[replication.FilterItemKindProject] + tagPattern, exist := patternMap[replication.FilterItemKindTag] if !exist { tagPattern = replication.PatternMatchAll } tagPattern = fmt.Sprintf("%s:%s", repositoryPattern, tagPattern) - if policy.Trigger.Kind == replication.TriggerKindImmediate { + if policy.Trigger != nil && policy.Trigger.Kind == replication.TriggerKindImmediate { // build filter chain for immediate trigger policy filters = append(filters, source.NewPatternFilter(replication.FilterItemKindTag, tagPattern)) diff --git a/src/replication/core/controller_test.go b/src/replication/core/controller_test.go new file mode 100644 index 000000000..bbfa671f8 --- /dev/null +++ b/src/replication/core/controller_test.go @@ -0,0 +1,144 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package core + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/vmware/harbor/src/common/utils/test" + "github.com/vmware/harbor/src/replication" + "github.com/vmware/harbor/src/replication/models" + "github.com/vmware/harbor/src/replication/source" +) + +func TestMain(m *testing.M) { + // set the policy manager used by GlobalController with a fake policy manager + controller := GlobalController.(*DefaultController) + controller.policyManager = &test.FakePolicyManager{} + os.Exit(m.Run()) +} + +func TestNewDefaultController(t *testing.T) { + controller := NewDefaultController(ControllerConfig{}) + assert.NotNil(t, controller) +} + +func TestInit(t *testing.T) { + assert.Nil(t, GlobalController.Init()) +} + +func TestCreatePolicy(t *testing.T) { + _, err := GlobalController.CreatePolicy(models.ReplicationPolicy{}) + assert.Nil(t, err) +} + +func TestUpdatePolicy(t *testing.T) { + assert.Nil(t, GlobalController.UpdatePolicy(models.ReplicationPolicy{ + ID: 2, + Trigger: &models.Trigger{ + Kind: replication.TriggerKindManual, + }, + })) +} + +func TestRemovePolicy(t *testing.T) { + assert.Nil(t, GlobalController.RemovePolicy(1)) +} + +func TestGetPolicy(t *testing.T) { + _, err := GlobalController.GetPolicy(1) + assert.Nil(t, err) +} + +func TestGetPolicies(t *testing.T) { + _, err := GlobalController.GetPolicies(models.QueryParameter{}) + assert.Nil(t, err) +} + +func TestReplicate(t *testing.T) { + // TODO +} + +func TestGetCandidates(t *testing.T) { + policy := &models.ReplicationPolicy{ + ID: 1, + Filters: []models.Filter{ + models.Filter{ + Kind: replication.FilterItemKindTag, + Pattern: ".*", + }, + }, + Trigger: &models.Trigger{ + Kind: replication.TriggerKindImmediate, + }, + } + + sourcer := source.NewSourcer() + + candidates := []models.FilterItem{ + models.FilterItem{ + Kind: replication.FilterItemKindTag, + Value: "library/hello-world:release-1.0", + }, + models.FilterItem{ + Kind: replication.FilterItemKindTag, + Value: "library/hello-world:latest", + }, + } + result := getCandidates(policy, sourcer, candidates...) + assert.Equal(t, 2, len(result)) + + policy.Filters = []models.Filter{ + models.Filter{ + Kind: replication.FilterItemKindTag, + Pattern: "release-.*", + }, + } + result = getCandidates(policy, sourcer, candidates...) + assert.Equal(t, 1, len(result)) +} + +func TestBuildFilterChain(t *testing.T) { + policy := &models.ReplicationPolicy{ + ID: 1, + Filters: []models.Filter{ + models.Filter{ + Kind: replication.FilterItemKindProject, + Pattern: "*", + }, + models.Filter{ + Kind: replication.FilterItemKindRepository, + Pattern: "*", + }, + models.Filter{ + Kind: replication.FilterItemKindTag, + Pattern: "*", + }, + }, + } + + sourcer := source.NewSourcer() + + chain := buildFilterChain(policy, sourcer) + assert.Equal(t, 3, len(chain.Filters())) + + policy.Trigger = &models.Trigger{ + Kind: replication.TriggerKindImmediate, + } + chain = buildFilterChain(policy, sourcer) + assert.Equal(t, 1, len(chain.Filters())) +} diff --git a/src/replication/event/start_replication_handler.go b/src/replication/event/start_replication_handler.go index 82b8bce40..7e29ef542 100644 --- a/src/replication/event/start_replication_handler.go +++ b/src/replication/event/start_replication_handler.go @@ -43,8 +43,7 @@ func (srh *StartReplicationHandler) Handle(value interface{}) error { } //Start replication - - return core.DefaultController.Replicate(notification.PolicyID, notification.Metadata) + return core.GlobalController.Replicate(notification.PolicyID, notification.Metadata) } //IsStateful implements the same method of notification handler interface diff --git a/src/replication/event/start_replication_handler_test.go b/src/replication/event/start_replication_handler_test.go index 30404b270..88ba61fbc 100644 --- a/src/replication/event/start_replication_handler_test.go +++ b/src/replication/event/start_replication_handler_test.go @@ -18,10 +18,14 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/vmware/harbor/src/common/utils/test" + "github.com/vmware/harbor/src/replication/core" "github.com/vmware/harbor/src/replication/event/notification" ) func TestHandle(t *testing.T) { + core.GlobalController = &test.FakeReplicatoinController{} + handler := &StartReplicationHandler{} assert.NotNil(t, handler.Handle(nil)) diff --git a/src/replication/models/filter_item.go b/src/replication/models/filter_item.go index 101fd6438..82497dd90 100644 --- a/src/replication/models/filter_item.go +++ b/src/replication/models/filter_item.go @@ -1,12 +1,19 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package models -import ( - "fmt" - - "github.com/astaxie/beego/validation" - "github.com/vmware/harbor/src/replication" -) - //FilterItem is the general data model represents the filtering resources which are used as input and output for the filters. type FilterItem struct { @@ -26,16 +33,3 @@ type FilterItem struct { //To append more additional information if required by the filter. Metadata map[string]interface{} `json:"metadata"` } - -// Valid ... -func (f *FilterItem) Valid(v *validation.Validation) { - if !(f.Kind == replication.FilterItemKindProject || - f.Kind == replication.FilterItemKindRepository || - f.Kind == replication.FilterItemKindTag) { - v.SetError("kind", fmt.Sprintf("invalid filter kind: %s", f.Kind)) - } - - if len(f.Value) == 0 { - v.SetError("value", "filter value can not be empty") - } -} diff --git a/src/replication/models/filter_test.go b/src/replication/models/filter_test.go new file mode 100644 index 000000000..4026f0e20 --- /dev/null +++ b/src/replication/models/filter_test.go @@ -0,0 +1,45 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package models + +import ( + "testing" + + "github.com/astaxie/beego/validation" + "github.com/stretchr/testify/assert" + "github.com/vmware/harbor/src/replication" +) + +func TestValid(t *testing.T) { + cases := map[*Filter]bool{ + &Filter{}: true, + &Filter{ + Kind: "invalid_kind", + }: true, + &Filter{ + Kind: replication.FilterItemKindRepository, + }: true, + &Filter{ + Kind: replication.FilterItemKindRepository, + Pattern: "*", + }: false, + } + + for filter, hasError := range cases { + v := &validation.Validation{} + filter.Valid(v) + assert.Equal(t, hasError, v.HasErrors()) + } +} diff --git a/src/replication/models/trigger.go b/src/replication/models/trigger.go index fa62166cb..27c2afa69 100644 --- a/src/replication/models/trigger.go +++ b/src/replication/models/trigger.go @@ -1,3 +1,17 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package models import ( diff --git a/src/replication/models/trigger_test.go b/src/replication/models/trigger_test.go new file mode 100644 index 000000000..b591995e9 --- /dev/null +++ b/src/replication/models/trigger_test.go @@ -0,0 +1,41 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package models + +import ( + "testing" + + "github.com/astaxie/beego/validation" + "github.com/stretchr/testify/assert" + "github.com/vmware/harbor/src/replication" +) + +func TestValidOfTrigger(t *testing.T) { + cases := map[*Trigger]bool{ + &Trigger{}: true, + &Trigger{ + Kind: "invalid_kind", + }: true, + &Trigger{ + Kind: replication.TriggerKindImmediate, + }: false, + } + + for filter, hasError := range cases { + v := &validation.Validation{} + filter.Valid(v) + assert.Equal(t, hasError, v.HasErrors()) + } +} diff --git a/src/replication/policy/manager.go b/src/replication/policy/manager.go index 3f8a35f17..bdce2740a 100644 --- a/src/replication/policy/manager.go +++ b/src/replication/policy/manager.go @@ -1,3 +1,17 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package policy import ( @@ -10,16 +24,25 @@ import ( "github.com/vmware/harbor/src/ui/config" ) -//Manager provides replication policy CURD capabilities. -type Manager struct{} +// Manager defines the method a policy manger should implement +type Manager interface { + GetPolicies(models.QueryParameter) ([]models.ReplicationPolicy, error) + GetPolicy(int64) (models.ReplicationPolicy, error) + CreatePolicy(models.ReplicationPolicy) (int64, error) + UpdatePolicy(models.ReplicationPolicy) error + RemovePolicy(int64) error +} -//NewManager is the constructor of Manager. -func NewManager() *Manager { - return &Manager{} +//DefaultManager provides replication policy CURD capabilities. +type DefaultManager struct{} + +//NewDefaultManager is the constructor of DefaultManager. +func NewDefaultManager() *DefaultManager { + return &DefaultManager{} } //GetPolicies returns all the policies -func (m *Manager) GetPolicies(query models.QueryParameter) ([]models.ReplicationPolicy, error) { +func (m *DefaultManager) GetPolicies(query models.QueryParameter) ([]models.ReplicationPolicy, error) { result := []models.ReplicationPolicy{} //TODO support more query conditions other than name and project ID policies, err := dao.FilterRepPolicies(query.Name, query.ProjectID) @@ -39,7 +62,7 @@ func (m *Manager) GetPolicies(query models.QueryParameter) ([]models.Replication } //GetPolicy returns the policy with the specified ID -func (m *Manager) GetPolicy(policyID int64) (models.ReplicationPolicy, error) { +func (m *DefaultManager) GetPolicy(policyID int64) (models.ReplicationPolicy, error) { policy, err := dao.GetRepPolicy(policyID) if err != nil { return models.ReplicationPolicy{}, err @@ -48,7 +71,6 @@ func (m *Manager) GetPolicy(policyID int64) (models.ReplicationPolicy, error) { return convertFromPersistModel(policy) } -// TODO add UT func convertFromPersistModel(policy *persist_models.RepPolicy) (models.ReplicationPolicy, error) { if policy == nil { return models.ReplicationPolicy{}, nil @@ -90,7 +112,6 @@ func convertFromPersistModel(policy *persist_models.RepPolicy) (models.Replicati return ply, nil } -// TODO add ut func convertToPersistModel(policy models.ReplicationPolicy) (*persist_models.RepPolicy, error) { ply := &persist_models.RepPolicy{ ID: policy.ID, @@ -131,7 +152,7 @@ func convertToPersistModel(policy models.ReplicationPolicy) (*persist_models.Rep //CreatePolicy creates a new policy with the provided data; //If creating failed, error will be returned; //If creating succeed, ID of the new created policy will be returned. -func (m *Manager) CreatePolicy(policy models.ReplicationPolicy) (int64, error) { +func (m *DefaultManager) CreatePolicy(policy models.ReplicationPolicy) (int64, error) { now := time.Now() policy.CreationTime = now policy.UpdateTime = now @@ -144,7 +165,7 @@ func (m *Manager) CreatePolicy(policy models.ReplicationPolicy) (int64, error) { //UpdatePolicy updates the policy; //If updating failed, error will be returned. -func (m *Manager) UpdatePolicy(policy models.ReplicationPolicy) error { +func (m *DefaultManager) UpdatePolicy(policy models.ReplicationPolicy) error { policy.UpdateTime = time.Now() ply, err := convertToPersistModel(policy) if err != nil { @@ -155,6 +176,6 @@ func (m *Manager) UpdatePolicy(policy models.ReplicationPolicy) error { //RemovePolicy removes the specified policy; //If removing failed, error will be returned. -func (m *Manager) RemovePolicy(policyID int64) error { +func (m *DefaultManager) RemovePolicy(policyID int64) error { return dao.DeleteRepPolicy(policyID) } diff --git a/src/replication/policy/manager_test.go b/src/replication/policy/manager_test.go new file mode 100644 index 000000000..62337aff7 --- /dev/null +++ b/src/replication/policy/manager_test.go @@ -0,0 +1,60 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package policy + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vmware/harbor/src/replication/models" +) + +func TestConvertToPersistModel(t *testing.T) { + var id, projectID, targetID int64 = 1, 1, 1 + name := "policy01" + replicateDeletion := true + trigger := &models.Trigger{ + Kind: "trigger_kind", + } + filters := []models.Filter{ + models.Filter{ + Kind: "filter_kind", + Pattern: "filter_pattern", + }, + } + policy := models.ReplicationPolicy{ + ID: id, + Name: name, + ReplicateDeletion: replicateDeletion, + ProjectIDs: []int64{projectID}, + TargetIDs: []int64{targetID}, + Trigger: trigger, + Filters: filters, + } + + ply, err := convertToPersistModel(policy) + require.Nil(t, err) + assert.Equal(t, id, ply.ID) + assert.Equal(t, name, ply.Name) + assert.Equal(t, replicateDeletion, ply.ReplicateDeletion) + assert.Equal(t, projectID, ply.ProjectID) + assert.Equal(t, targetID, ply.TargetID) + tg, _ := json.Marshal(trigger) + assert.Equal(t, string(tg), ply.Trigger) + ft, _ := json.Marshal(filters) + assert.Equal(t, string(ft), ply.Filters) +} diff --git a/src/replication/source/repository_convertor_test.go b/src/replication/source/repository_convertor_test.go index 48e223232..f260f113d 100644 --- a/src/replication/source/repository_convertor_test.go +++ b/src/replication/source/repository_convertor_test.go @@ -27,9 +27,6 @@ func TestRepositoryConvert(t *testing.T) { models.FilterItem{ Kind: replication.FilterItemKindProject, Value: "library", - Metadata: map[string]interface{}{ - "public": true, - }, }, models.FilterItem{ Kind: replication.FilterItemKindRepository, @@ -39,16 +36,10 @@ func TestRepositoryConvert(t *testing.T) { models.FilterItem{ Kind: replication.FilterItemKindRepository, Value: "library/ubuntu", - Metadata: map[string]interface{}{ - "public": true, - }, }, models.FilterItem{ Kind: replication.FilterItemKindRepository, Value: "library/centos", - Metadata: map[string]interface{}{ - "public": true, - }, }, } @@ -88,10 +79,10 @@ func (f *fakeRegistryAdaptor) GetRepository(name string, namespace string) model func (f *fakeRegistryAdaptor) GetTags(repositoryName string, namespace string) []models.Tag { return []models.Tag{ models.Tag{ - Name: "library/ubuntu:14.04", + Name: "14.04", }, models.Tag{ - Name: "library/ubuntu:16.04", + Name: "16.04", }, } } diff --git a/src/replication/source/tag_convertor_test.go b/src/replication/source/tag_convertor_test.go index 151192bbb..6e64f86d2 100644 --- a/src/replication/source/tag_convertor_test.go +++ b/src/replication/source/tag_convertor_test.go @@ -27,9 +27,6 @@ func TestTagConvert(t *testing.T) { models.FilterItem{ Kind: replication.FilterItemKindRepository, Value: "library/ubuntu", - Metadata: map[string]interface{}{ - "public": true, - }, }, models.FilterItem{ Kind: replication.FilterItemKindProject, @@ -39,16 +36,10 @@ func TestTagConvert(t *testing.T) { models.FilterItem{ Kind: replication.FilterItemKindTag, Value: "library/ubuntu:14.04", - Metadata: map[string]interface{}{ - "public": true, - }, }, models.FilterItem{ Kind: replication.FilterItemKindTag, Value: "library/ubuntu:16.04", - Metadata: map[string]interface{}{ - "public": true, - }, }, } diff --git a/src/replication/target/target.go b/src/replication/target/target.go new file mode 100644 index 000000000..ab8e815e5 --- /dev/null +++ b/src/replication/target/target.go @@ -0,0 +1,38 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package target + +import ( + "github.com/vmware/harbor/src/common/dao" + "github.com/vmware/harbor/src/common/models" +) + +// Manager defines the methods that a target manager should implement +type Manager interface { + GetTarget(int64) (*models.RepTarget, error) +} + +// DefaultManager implement the Manager interface +type DefaultManager struct{} + +// NewDefaultManager returns an instance of DefaultManger +func NewDefaultManager() *DefaultManager { + return &DefaultManager{} +} + +// GetTarget ... +func (d *DefaultManager) GetTarget(id int64) (*models.RepTarget, error) { + return dao.GetRepTarget(id) +} diff --git a/src/ui/api/harborapi_test.go b/src/ui/api/harborapi_test.go index 987b69ca2..7e62499c5 100644 --- a/src/ui/api/harborapi_test.go +++ b/src/ui/api/harborapi_test.go @@ -40,6 +40,7 @@ import ( "github.com/dghubble/sling" //for test env prepare + "github.com/vmware/harbor/src/replication/core" _ "github.com/vmware/harbor/src/replication/event" _ "github.com/vmware/harbor/src/ui/auth/db" _ "github.com/vmware/harbor/src/ui/auth/ldap" @@ -133,6 +134,10 @@ func init() { _ = updateInitPassword(1, "Harbor12345") + if err := core.GlobalController.Init(); err != nil { + log.Fatalf("failed to initialize GlobalController: %v", err) + } + //syncRegistry if err := SyncRegistry(config.GlobalProjectMgr); err != nil { log.Fatalf("failed to sync repositories from registry: %v", err) diff --git a/src/ui/api/replication.go b/src/ui/api/replication.go index 43bd2289c..37a8ec6ce 100644 --- a/src/ui/api/replication.go +++ b/src/ui/api/replication.go @@ -49,7 +49,7 @@ func (r *ReplicationAPI) Post() { replication := &models.Replication{} r.DecodeJSONReqAndValidate(replication) - policy, err := core.DefaultController.GetPolicy(replication.PolicyID) + policy, err := core.GlobalController.GetPolicy(replication.PolicyID) if err != nil { r.HandleInternalServerError(fmt.Sprintf("failed to get replication policy %d: %v", replication.PolicyID, err)) return diff --git a/src/ui/api/replication_policy.go b/src/ui/api/replication_policy.go index 955ed2fec..da452331b 100644 --- a/src/ui/api/replication_policy.go +++ b/src/ui/api/replication_policy.go @@ -51,7 +51,7 @@ func (pa *RepPolicyAPI) Prepare() { // Get ... func (pa *RepPolicyAPI) Get() { id := pa.GetIDFromURL() - policy, err := core.DefaultController.GetPolicy(id) + policy, err := core.GlobalController.GetPolicy(id) if err != nil { log.Errorf("failed to get policy %d: %v", id, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) @@ -87,7 +87,7 @@ func (pa *RepPolicyAPI) List() { result := []*api_models.ReplicationPolicy{} - policies, err := core.DefaultController.GetPolicies(queryParam) + policies, err := core.GlobalController.GetPolicies(queryParam) if err != nil { log.Errorf("failed to get policies: %v, query parameters: %v", err, queryParam) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) @@ -139,7 +139,7 @@ func (pa *RepPolicyAPI) Post() { } } - id, err := core.DefaultController.CreatePolicy(convertToRepPolicy(policy)) + id, err := core.GlobalController.CreatePolicy(convertToRepPolicy(policy)) if err != nil { pa.HandleInternalServerError(fmt.Sprintf("failed to create policy: %v", err)) return @@ -154,7 +154,7 @@ func (pa *RepPolicyAPI) Post() { func (pa *RepPolicyAPI) Put() { id := pa.GetIDFromURL() - originalPolicy, err := core.DefaultController.GetPolicy(id) + originalPolicy, err := core.GlobalController.GetPolicy(id) if err != nil { log.Errorf("failed to get policy %d: %v", id, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) @@ -197,7 +197,7 @@ func (pa *RepPolicyAPI) Put() { } } - if err = core.DefaultController.UpdatePolicy(convertToRepPolicy(policy)); err != nil { + if err = core.GlobalController.UpdatePolicy(convertToRepPolicy(policy)); err != nil { pa.HandleInternalServerError(fmt.Sprintf("failed to update policy %d: %v", id, err)) return } @@ -207,7 +207,7 @@ func (pa *RepPolicyAPI) Put() { func (pa *RepPolicyAPI) Delete() { id := pa.GetIDFromURL() - policy, err := core.DefaultController.GetPolicy(id) + policy, err := core.GlobalController.GetPolicy(id) if err != nil { log.Errorf("failed to get policy %d: %v", id, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) @@ -232,7 +232,7 @@ func (pa *RepPolicyAPI) Delete() { } } - if err = core.DefaultController.RemovePolicy(id); err != nil { + if err = core.GlobalController.RemovePolicy(id); err != nil { log.Errorf("failed to delete policy %d: %v", id, err) pa.CustomAbort(http.StatusInternalServerError, "") } diff --git a/src/ui/api/replication_policy_test.go b/src/ui/api/replication_policy_test.go index 5e2b2f2f5..0bf688105 100644 --- a/src/ui/api/replication_policy_test.go +++ b/src/ui/api/replication_policy_test.go @@ -123,10 +123,10 @@ func TestRepPolicyAPIPost(t *testing.T) { ID: targetID, }, }, - Filters: []rep_models.FilterItem{ - rep_models.FilterItem{ - Kind: "invalid_filter_kind", - Value: "", + Filters: []rep_models.Filter{ + rep_models.Filter{ + Kind: "invalid_filter_kind", + Pattern: "", }, }, }, @@ -151,10 +151,10 @@ func TestRepPolicyAPIPost(t *testing.T) { ID: targetID, }, }, - Filters: []rep_models.FilterItem{ - rep_models.FilterItem{ - Kind: replication.FilterItemKindRepository, - Value: "*", + Filters: []rep_models.Filter{ + rep_models.Filter{ + Kind: replication.FilterItemKindRepository, + Pattern: "*", }, }, Trigger: &rep_models.Trigger{ @@ -182,10 +182,10 @@ func TestRepPolicyAPIPost(t *testing.T) { ID: targetID, }, }, - Filters: []rep_models.FilterItem{ - rep_models.FilterItem{ - Kind: replication.FilterItemKindRepository, - Value: "*", + Filters: []rep_models.Filter{ + rep_models.Filter{ + Kind: replication.FilterItemKindRepository, + Pattern: "*", }, }, Trigger: &rep_models.Trigger{ @@ -213,10 +213,10 @@ func TestRepPolicyAPIPost(t *testing.T) { ID: 10000, }, }, - Filters: []rep_models.FilterItem{ - rep_models.FilterItem{ - Kind: replication.FilterItemKindRepository, - Value: "*", + Filters: []rep_models.Filter{ + rep_models.Filter{ + Kind: replication.FilterItemKindRepository, + Pattern: "*", }, }, Trigger: &rep_models.Trigger{ @@ -244,10 +244,10 @@ func TestRepPolicyAPIPost(t *testing.T) { ID: targetID, }, }, - Filters: []rep_models.FilterItem{ - rep_models.FilterItem{ - Kind: replication.FilterItemKindRepository, - Value: "*", + Filters: []rep_models.Filter{ + rep_models.Filter{ + Kind: replication.FilterItemKindRepository, + Pattern: "*", }, }, Trigger: &rep_models.Trigger{ @@ -374,10 +374,10 @@ func TestRepPolicyAPIPut(t *testing.T) { ID: targetID, }, }, - Filters: []rep_models.FilterItem{ - rep_models.FilterItem{ - Kind: replication.FilterItemKindRepository, - Value: "*", + Filters: []rep_models.Filter{ + rep_models.Filter{ + Kind: replication.FilterItemKindRepository, + Pattern: "*", }, }, Trigger: &rep_models.Trigger{ @@ -405,10 +405,10 @@ func TestRepPolicyAPIPut(t *testing.T) { ID: targetID, }, }, - Filters: []rep_models.FilterItem{ - rep_models.FilterItem{ - Kind: replication.FilterItemKindRepository, - Value: "*", + Filters: []rep_models.Filter{ + rep_models.Filter{ + Kind: replication.FilterItemKindRepository, + Pattern: "*", }, }, Trigger: &rep_models.Trigger{ @@ -463,10 +463,10 @@ func TestConvertToRepPolicy(t *testing.T) { ID: 1, Name: "policy", Description: "description", - Filters: []rep_models.FilterItem{ - rep_models.FilterItem{ - Kind: "filter_kind_01", - Value: "*", + Filters: []rep_models.Filter{ + rep_models.Filter{ + Kind: "filter_kind_01", + Pattern: "*", }, }, ReplicateDeletion: true, @@ -490,10 +490,10 @@ func TestConvertToRepPolicy(t *testing.T) { ID: 1, Name: "policy", Description: "description", - Filters: []rep_models.FilterItem{ - rep_models.FilterItem{ - Kind: "filter_kind_01", - Value: "*", + Filters: []rep_models.Filter{ + rep_models.Filter{ + Kind: "filter_kind_01", + Pattern: "*", }, }, ReplicateDeletion: true, diff --git a/src/ui/main.go b/src/ui/main.go index e25a10e29..12f500de3 100644 --- a/src/ui/main.go +++ b/src/ui/main.go @@ -132,8 +132,8 @@ func main() { notifier.Publish(notifier.ScanAllPolicyTopic, notifier.ScanPolicyNotification{Type: scanAllPolicy.Type, DailyTime: (int64)(dailyTime)}) } - if err := core.DefaultController.Init(); err != nil { - log.Errorf("failed to initialize DefaultContorllter: %v", err) + if err := core.GlobalController.Init(); err != nil { + log.Errorf("failed to initialize the replication controller: %v", err) } filter.Init()