mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-17 22:27:49 +01:00
Merge pull request #3666 from ywk253100/171120_watch_list
Implement the methods of WatchList
This commit is contained in:
commit
4161ce5459
@ -185,6 +185,17 @@ create table replication_job (
|
||||
INDEX poid_uptime (policy_id, update_time)
|
||||
);
|
||||
|
||||
create table replication_immediate_trigger (
|
||||
id int NOT NULL AUTO_INCREMENT,
|
||||
policy_id int NOT NULL,
|
||||
namespace varchar(256) NOT NULL,
|
||||
on_push tinyint(1) NOT NULL DEFAULT 0,
|
||||
on_deletion tinyint(1) NOT NULL DEFAULT 0,
|
||||
creation_time timestamp default CURRENT_TIMESTAMP,
|
||||
update_time timestamp default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
create table img_scan_job (
|
||||
id int NOT NULL AUTO_INCREMENT,
|
||||
status varchar(64) NOT NULL,
|
||||
|
@ -176,6 +176,16 @@ create table replication_job (
|
||||
update_time timestamp default CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
create table replication_immediate_trigger (
|
||||
id INTEGER PRIMARY KEY,
|
||||
policy_id int NOT NULL,
|
||||
namespace varchar(256) NOT NULL,
|
||||
on_push tinyint(1) NOT NULL DEFAULT 0,
|
||||
on_deletion tinyint(1) NOT NULL DEFAULT 0,
|
||||
creation_time timestamp default CURRENT_TIMESTAMP,
|
||||
update_time timestamp default CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
|
||||
create table img_scan_job (
|
||||
id INTEGER PRIMARY KEY,
|
||||
|
62
src/common/dao/watch_item.go
Normal file
62
src/common/dao/watch_item.go
Normal file
@ -0,0 +1,62 @@
|
||||
// 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 dao
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
)
|
||||
|
||||
// DefaultDatabaseWatchItemDAO is an instance of DatabaseWatchItemDAO
|
||||
var DefaultDatabaseWatchItemDAO WatchItemDAO = &DatabaseWatchItemDAO{}
|
||||
|
||||
// WatchItemDAO defines operations about WatchItem
|
||||
type WatchItemDAO interface {
|
||||
Add(*models.WatchItem) (int64, error)
|
||||
DeleteByPolicyID(int64) error
|
||||
Get(namespace, operation string) ([]models.WatchItem, error)
|
||||
}
|
||||
|
||||
// DatabaseWatchItemDAO implements interface WatchItemDAO for database
|
||||
type DatabaseWatchItemDAO struct{}
|
||||
|
||||
// Add a WatchItem
|
||||
func (d *DatabaseWatchItemDAO) Add(item *models.WatchItem) (int64, error) {
|
||||
now := time.Now()
|
||||
item.CreationTime = now
|
||||
item.UpdateTime = now
|
||||
return GetOrmer().Insert(item)
|
||||
}
|
||||
|
||||
// DeleteByPolicyID deletes the WatchItem specified by policy ID
|
||||
func (d *DatabaseWatchItemDAO) DeleteByPolicyID(policyID int64) error {
|
||||
_, err := GetOrmer().QueryTable(&models.WatchItem{}).Filter("PolicyID", policyID).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
// Get returns WatchItem list according to the namespace and operation
|
||||
func (d *DatabaseWatchItemDAO) Get(namespace, operation string) ([]models.WatchItem, error) {
|
||||
qs := GetOrmer().QueryTable(&models.WatchItem{}).Filter("Namespace", namespace)
|
||||
if operation == "push" {
|
||||
qs = qs.Filter("OnPush", 1)
|
||||
} else if operation == "delete" {
|
||||
qs = qs.Filter("OnDeletion", 1)
|
||||
}
|
||||
|
||||
items := []models.WatchItem{}
|
||||
_, err := qs.All(&items)
|
||||
return items, err
|
||||
}
|
71
src/common/dao/watch_item_test.go
Normal file
71
src/common/dao/watch_item_test.go
Normal file
@ -0,0 +1,71 @@
|
||||
// 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 dao
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
)
|
||||
|
||||
func TestMethodsOfWatchItem(t *testing.T) {
|
||||
targetID, err := AddRepTarget(models.RepTarget{
|
||||
Name: "test_target_for_watch_item",
|
||||
URL: "http://127.0.0.1",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer DeleteRepTarget(targetID)
|
||||
|
||||
policyID, err := AddRepPolicy(models.RepPolicy{
|
||||
Name: "test_policy_for_watch_item",
|
||||
ProjectID: 1,
|
||||
TargetID: targetID,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
defer DeleteRepPolicy(policyID)
|
||||
|
||||
item := &models.WatchItem{
|
||||
PolicyID: policyID,
|
||||
Namespace: "library",
|
||||
OnPush: false,
|
||||
OnDeletion: true,
|
||||
}
|
||||
|
||||
// test Add
|
||||
id, err := DefaultDatabaseWatchItemDAO.Add(item)
|
||||
require.Nil(t, err)
|
||||
|
||||
// test Get: operation-push
|
||||
items, err := DefaultDatabaseWatchItemDAO.Get("library", "push")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 0, len(items))
|
||||
|
||||
// test Get: operation-delete
|
||||
items, err = DefaultDatabaseWatchItemDAO.Get("library", "delete")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 1, len(items))
|
||||
assert.Equal(t, id, items[0].ID)
|
||||
assert.Equal(t, "library", items[0].Namespace)
|
||||
assert.True(t, items[0].OnDeletion)
|
||||
|
||||
// test DeleteByPolicyID
|
||||
err = DefaultDatabaseWatchItemDAO.DeleteByPolicyID(policyID)
|
||||
require.Nil(t, err)
|
||||
items, err = DefaultDatabaseWatchItemDAO.Get("library", "delete")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 0, len(items))
|
||||
}
|
@ -29,5 +29,6 @@ func init() {
|
||||
new(ScanJob),
|
||||
new(RepoRecord),
|
||||
new(ImgScanOverview),
|
||||
new(ClairVulnTimestamp))
|
||||
new(ClairVulnTimestamp),
|
||||
new(WatchItem))
|
||||
}
|
||||
|
35
src/common/models/watch_item.go
Normal file
35
src/common/models/watch_item.go
Normal file
@ -0,0 +1,35 @@
|
||||
// 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 (
|
||||
"time"
|
||||
)
|
||||
|
||||
// WatchItem ...
|
||||
type WatchItem struct {
|
||||
ID int64 `orm:"pk;auto;column(id)" json:"id"`
|
||||
PolicyID int64 `orm:"column(policy_id)" json:"policy_id"`
|
||||
Namespace string `orm:"column(namespace)" json:"namespace"`
|
||||
OnDeletion bool `orm:"column(on_deletion)" json:"on_deletion"`
|
||||
OnPush bool `orm:"column(on_push)" json:"on_push"`
|
||||
CreationTime time.Time `orm:"column(creation_time)" json:"creation_time"`
|
||||
UpdateTime time.Time `orm:"column(update_time)" json:"update_time"`
|
||||
}
|
||||
|
||||
//TableName ...
|
||||
func (w *WatchItem) TableName() string {
|
||||
return "replication_immediate_trigger"
|
||||
}
|
@ -8,22 +8,16 @@ const (
|
||||
//FilterItemKindTag : Kind of filter item is 'tag'
|
||||
FilterItemKindTag = "tag"
|
||||
|
||||
//TODO: Refactor constants
|
||||
|
||||
//TriggerKindManually : kind of trigger is 'manully'
|
||||
TriggerKindManually = "manually"
|
||||
//TriggerKindImmediately : kind of trigger is 'immediately'
|
||||
TriggerKindImmediately = "immediately"
|
||||
|
||||
//AdaptorKindHarbor : Kind of adaptor of Harbor
|
||||
AdaptorKindHarbor = "Harbor"
|
||||
|
||||
//TriggerKindImmediate : Kind of trigger is 'Immediate'
|
||||
TriggerKindImmediate = "Immediate"
|
||||
TriggerKindImmediate = "immediate"
|
||||
//TriggerKindSchedule : Kind of trigger is 'Schedule'
|
||||
TriggerKindSchedule = "Schedule"
|
||||
TriggerKindSchedule = "schedule"
|
||||
//TriggerKindManual : Kind of trigger is 'Manual'
|
||||
TriggerKindManual = "Manual"
|
||||
TriggerKindManual = "manual"
|
||||
|
||||
//TriggerScheduleDaily : type of scheduling is 'daily'
|
||||
TriggerScheduleDaily = "daily"
|
||||
//TriggerScheduleWeekly : type of scheduling is 'weekly'
|
||||
|
@ -55,7 +55,6 @@ func (ctl *Controller) Init() error {
|
||||
|
||||
//Build query parameters
|
||||
triggerNames := []string{
|
||||
replication.TriggerKindImmediate,
|
||||
replication.TriggerKindSchedule,
|
||||
}
|
||||
queryName := ""
|
||||
@ -73,7 +72,7 @@ func (ctl *Controller) Init() error {
|
||||
}
|
||||
if policies != nil && len(policies) > 0 {
|
||||
for _, policy := range policies {
|
||||
if err := ctl.triggerManager.SetupTrigger(policy.ID, *policy.Trigger); err != nil {
|
||||
if err := ctl.triggerManager.SetupTrigger(&policy); err != nil {
|
||||
//TODO: Log error
|
||||
fmt.Printf("Error: %s", err)
|
||||
//TODO:Update the status of policy
|
||||
@ -96,7 +95,8 @@ func (ctl *Controller) CreatePolicy(newPolicy models.ReplicationPolicy) (int64,
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if err = ctl.triggerManager.SetupTrigger(id, *newPolicy.Trigger); err != nil {
|
||||
newPolicy.ID = id
|
||||
if err = ctl.triggerManager.SetupTrigger(&newPolicy); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@ -118,15 +118,34 @@ func (ctl *Controller) UpdatePolicy(updatedPolicy models.ReplicationPolicy) erro
|
||||
return fmt.Errorf("policy %d not found", id)
|
||||
}
|
||||
|
||||
if err = ctl.triggerManager.UnsetTrigger(id, *originPolicy.Trigger); err != nil {
|
||||
return err
|
||||
reset := false
|
||||
if updatedPolicy.Trigger.Kind != originPolicy.Trigger.Kind {
|
||||
reset = true
|
||||
} else {
|
||||
switch updatedPolicy.Trigger.Kind {
|
||||
case replication.TriggerKindSchedule:
|
||||
if updatedPolicy.Trigger.Param != originPolicy.Trigger.Param {
|
||||
reset = true
|
||||
}
|
||||
case replication.TriggerKindImmediate:
|
||||
// Always reset immediate trigger as it is relevent with namespaces
|
||||
reset = true
|
||||
default:
|
||||
// manual trigger, no need to reset
|
||||
}
|
||||
}
|
||||
|
||||
if err = ctl.policyManager.UpdatePolicy(updatedPolicy); err != nil {
|
||||
return err
|
||||
if reset {
|
||||
if err = ctl.triggerManager.UnsetTrigger(id, *originPolicy.Trigger); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ctl.policyManager.UpdatePolicy(updatedPolicy); err != nil {
|
||||
return err
|
||||
}
|
||||
return ctl.triggerManager.SetupTrigger(&updatedPolicy)
|
||||
}
|
||||
|
||||
return ctl.triggerManager.SetupTrigger(id, *updatedPolicy.Trigger)
|
||||
return ctl.policyManager.UpdatePolicy(updatedPolicy)
|
||||
}
|
||||
|
||||
//RemovePolicy will remove the specified policy and clean the related settings
|
||||
|
@ -14,6 +14,7 @@ type ReplicationPolicy struct {
|
||||
Trigger *Trigger //The trigger of the replication
|
||||
ProjectIDs []int64 //Projects attached to this policy
|
||||
TargetIDs []int64
|
||||
Namespaces []string // The namespaces are used to set immediate trigger
|
||||
CreationTime time.Time
|
||||
UpdateTime time.Time
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ type Trigger struct {
|
||||
|
||||
// Valid ...
|
||||
func (t *Trigger) Valid(v *validation.Validation) {
|
||||
if !(t.Kind == replication.TriggerKindImmediately ||
|
||||
t.Kind == replication.TriggerKindManually ||
|
||||
if !(t.Kind == replication.TriggerKindImmediate ||
|
||||
t.Kind == replication.TriggerKindManual ||
|
||||
t.Kind == replication.TriggerKindSchedule) {
|
||||
v.SetError("kind", fmt.Sprintf("invalid trigger kind: %s", t.Kind))
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
package trigger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/vmware/harbor/src/replication"
|
||||
)
|
||||
|
||||
@ -26,19 +24,20 @@ func (st *ImmediateTrigger) Kind() string {
|
||||
|
||||
//Setup is the implementation of same method defined in Trigger interface
|
||||
func (st *ImmediateTrigger) Setup() error {
|
||||
if st.params.PolicyID <= 0 || len(st.params.Namespace) == 0 {
|
||||
return errors.New("Invalid parameters for Immediate trigger")
|
||||
}
|
||||
|
||||
//TODO: Need more complicated logic here to handle partial updates
|
||||
wt := WatchItem{
|
||||
PolicyID: st.params.PolicyID,
|
||||
Namespace: st.params.Namespace,
|
||||
OnDeletion: st.params.OnDeletion,
|
||||
OnPush: true,
|
||||
}
|
||||
for _, namespace := range st.params.Namespaces {
|
||||
wt := WatchItem{
|
||||
PolicyID: st.params.PolicyID,
|
||||
Namespace: namespace,
|
||||
OnDeletion: st.params.OnDeletion,
|
||||
OnPush: true,
|
||||
}
|
||||
|
||||
return DefaultWatchList.Add(wt)
|
||||
if err := DefaultWatchList.Add(wt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//Unset is the implementation of same method defined in Trigger interface
|
||||
|
@ -2,7 +2,9 @@ package trigger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/replication"
|
||||
"github.com/vmware/harbor/src/replication/models"
|
||||
)
|
||||
@ -50,25 +52,24 @@ func (m *Manager) RemoveTrigger(policyID int64) error {
|
||||
}
|
||||
*/
|
||||
|
||||
//SetupTrigger will create the new trigger based on the provided json parameters.
|
||||
//SetupTrigger will create the new trigger based on the provided policy.
|
||||
//If failed, an error will be returned.
|
||||
func (m *Manager) SetupTrigger(policyID int64, trigger models.Trigger) error {
|
||||
if policyID <= 0 {
|
||||
return errors.New("Invalid policy ID")
|
||||
}
|
||||
|
||||
if len(trigger.Kind) == 0 {
|
||||
return errors.New("Invalid replication trigger definition")
|
||||
func (m *Manager) SetupTrigger(policy *models.ReplicationPolicy) error {
|
||||
if policy == nil || policy.Trigger == nil {
|
||||
log.Debug("empty policy or trigger, skip trigger setup")
|
||||
return nil
|
||||
}
|
||||
|
||||
trigger := policy.Trigger
|
||||
switch trigger.Kind {
|
||||
case replication.TriggerKindSchedule:
|
||||
param := ScheduleParam{}
|
||||
if err := param.Parse(trigger.Param); err != nil {
|
||||
return err
|
||||
}
|
||||
//Append policy ID info
|
||||
param.PolicyID = policyID
|
||||
//Append policy ID and whether replicate deletion
|
||||
param.PolicyID = policy.ID
|
||||
param.OnDeletion = policy.ReplicateDeletion
|
||||
|
||||
newTrigger := NewScheduleTrigger(param)
|
||||
if err := newTrigger.Setup(); err != nil {
|
||||
@ -79,16 +80,19 @@ func (m *Manager) SetupTrigger(policyID int64, trigger models.Trigger) error {
|
||||
if err := param.Parse(trigger.Param); err != nil {
|
||||
return err
|
||||
}
|
||||
//Append policy ID info
|
||||
param.PolicyID = policyID
|
||||
//Append policy ID and whether replicate deletion
|
||||
param.PolicyID = policy.ID
|
||||
param.OnDeletion = policy.ReplicateDeletion
|
||||
param.Namespaces = policy.Namespaces
|
||||
|
||||
newTrigger := NewImmediateTrigger(param)
|
||||
if err := newTrigger.Setup(); err != nil {
|
||||
return err
|
||||
}
|
||||
case replication.TriggerKindManual:
|
||||
// do nothing
|
||||
default:
|
||||
//Treat as manual trigger
|
||||
break
|
||||
return fmt.Errorf("invalid trigger type: %s", policy.Trigger.Kind)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -1,9 +1,5 @@
|
||||
package trigger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
//NOTES: Whether replicate the existing images when the type of trigger is
|
||||
//'Immediate' is a once-effective setting which will not be persisted
|
||||
// and kept as one parameter of 'Immediate' trigger. It will only be
|
||||
@ -14,13 +10,13 @@ type ImmediateParam struct {
|
||||
//Basic parameters
|
||||
BasicParam
|
||||
|
||||
//Namepace
|
||||
Namespace string
|
||||
//Namepaces
|
||||
Namespaces []string
|
||||
}
|
||||
|
||||
//Parse is the implementation of same method in TriggerParam interface
|
||||
//NOTES: No need to implement this method for 'Immediate' trigger as
|
||||
//it does not have any parameters with json format.
|
||||
func (ip ImmediateParam) Parse(param string) error {
|
||||
return errors.New("Should NOT be called as it's not implemented")
|
||||
return nil
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
package trigger
|
||||
|
||||
import (
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
)
|
||||
|
||||
//DefaultWatchList is the default instance of WatchList
|
||||
var DefaultWatchList = &WatchList{}
|
||||
|
||||
@ -24,15 +29,37 @@ type WatchItem struct {
|
||||
|
||||
//Add item to the list and persist into DB
|
||||
func (wl *WatchList) Add(item WatchItem) error {
|
||||
return nil
|
||||
_, err := dao.DefaultDatabaseWatchItemDAO.Add(
|
||||
&models.WatchItem{
|
||||
PolicyID: item.PolicyID,
|
||||
Namespace: item.Namespace,
|
||||
OnPush: item.OnPush,
|
||||
OnDeletion: item.OnDeletion,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
//Remove the specified watch item from list
|
||||
func (wl *WatchList) Remove(policyID int64) error {
|
||||
return nil
|
||||
return dao.DefaultDatabaseWatchItemDAO.DeleteByPolicyID(policyID)
|
||||
}
|
||||
|
||||
//Get the watch items according to the namespace and operation
|
||||
func (wl *WatchList) Get(namespace, operation string) ([]WatchItem, error) {
|
||||
return []WatchItem{}, nil
|
||||
items, err := dao.DefaultDatabaseWatchItemDAO.Get(namespace, operation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
watchItems := []WatchItem{}
|
||||
for _, item := range items {
|
||||
watchItems = append(watchItems, WatchItem{
|
||||
PolicyID: item.PolicyID,
|
||||
Namespace: item.Namespace,
|
||||
OnPush: item.OnPush,
|
||||
OnDeletion: item.OnDeletion,
|
||||
})
|
||||
}
|
||||
|
||||
return watchItems, nil
|
||||
}
|
||||
|
108
src/replication/trigger/watch_list_test.go
Normal file
108
src/replication/trigger/watch_list_test.go
Normal file
@ -0,0 +1,108 @@
|
||||
// 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 trigger
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
)
|
||||
|
||||
type fakeWatchItemDAO struct {
|
||||
items []models.WatchItem
|
||||
}
|
||||
|
||||
func (f *fakeWatchItemDAO) Add(item *models.WatchItem) (int64, error) {
|
||||
f.items = append(f.items, *item)
|
||||
return int64(len(f.items) + 1), nil
|
||||
}
|
||||
|
||||
// Delete the WatchItem specified by policy ID
|
||||
func (f *fakeWatchItemDAO) DeleteByPolicyID(policyID int64) error {
|
||||
for i, item := range f.items {
|
||||
if item.PolicyID == policyID {
|
||||
f.items = append(f.items[:i], f.items[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns WatchItem list according to the namespace and operation
|
||||
func (f *fakeWatchItemDAO) Get(namespace, operation string) ([]models.WatchItem, error) {
|
||||
items := []models.WatchItem{}
|
||||
for _, item := range f.items {
|
||||
if item.Namespace != namespace {
|
||||
continue
|
||||
}
|
||||
|
||||
if operation == "push" {
|
||||
if item.OnPush {
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
|
||||
if operation == "delete" {
|
||||
if item.OnDeletion {
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func TestMethodsOfWatchList(t *testing.T) {
|
||||
dao.DefaultDatabaseWatchItemDAO = &fakeWatchItemDAO{}
|
||||
|
||||
var policyID int64 = 1
|
||||
|
||||
// test Add
|
||||
item := WatchItem{
|
||||
PolicyID: policyID,
|
||||
Namespace: "library",
|
||||
OnDeletion: true,
|
||||
OnPush: false,
|
||||
}
|
||||
|
||||
err := DefaultWatchList.Add(item)
|
||||
require.Nil(t, err)
|
||||
|
||||
// test Get: non-exist namespace
|
||||
items, err := DefaultWatchList.Get("non-exist-namespace", "delete")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 0, len(items))
|
||||
|
||||
// test Get: non-exist operation
|
||||
items, err = DefaultWatchList.Get("library", "non-exist-operation")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 0, len(items))
|
||||
|
||||
// test Get: valid params
|
||||
items, err = DefaultWatchList.Get("library", "delete")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 1, len(items))
|
||||
assert.Equal(t, policyID, items[0].PolicyID)
|
||||
|
||||
// test Remove
|
||||
err = DefaultWatchList.Remove(policyID)
|
||||
require.Nil(t, err)
|
||||
items, err = DefaultWatchList.Get("library", "delete")
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 0, len(items))
|
||||
}
|
@ -113,15 +113,16 @@ func (pa *RepPolicyAPI) Post() {
|
||||
|
||||
// check the existence of projects
|
||||
for _, project := range policy.Projects {
|
||||
exist, err := pa.ProjectMgr.Exists(project.ProjectID)
|
||||
pro, err := pa.ProjectMgr.Get(project.ProjectID)
|
||||
if err != nil {
|
||||
pa.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %d", project.ProjectID), err)
|
||||
return
|
||||
}
|
||||
if !exist {
|
||||
if pro == nil {
|
||||
pa.HandleNotFound(fmt.Sprintf("project %d not found", project.ProjectID))
|
||||
return
|
||||
}
|
||||
project.Name = pro.Name
|
||||
}
|
||||
|
||||
// check the existence of targets
|
||||
@ -168,6 +169,34 @@ func (pa *RepPolicyAPI) Put() {
|
||||
|
||||
policy.ID = id
|
||||
|
||||
// check the existence of projects
|
||||
for _, project := range policy.Projects {
|
||||
pro, err := pa.ProjectMgr.Get(project.ProjectID)
|
||||
if err != nil {
|
||||
pa.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %d", project.ProjectID), err)
|
||||
return
|
||||
}
|
||||
if pro == nil {
|
||||
pa.HandleNotFound(fmt.Sprintf("project %d not found", project.ProjectID))
|
||||
return
|
||||
}
|
||||
project.Name = pro.Name
|
||||
}
|
||||
|
||||
// check the existence of targets
|
||||
for _, target := range policy.Targets {
|
||||
t, err := dao.GetRepTarget(target.ID)
|
||||
if err != nil {
|
||||
pa.HandleInternalServerError(fmt.Sprintf("failed to get target %d: %v", target.ID, err))
|
||||
return
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
pa.HandleNotFound(fmt.Sprintf("target %d not found", target.ID))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err = core.DefaultController.UpdatePolicy(convertToRepPolicy(policy)); err != nil {
|
||||
pa.HandleInternalServerError(fmt.Sprintf("failed to update policy %d: %v", id, err))
|
||||
return
|
||||
@ -274,6 +303,7 @@ func convertToRepPolicy(policy *api_models.ReplicationPolicy) rep_models.Replica
|
||||
|
||||
for _, project := range policy.Projects {
|
||||
ply.ProjectIDs = append(ply.ProjectIDs, project.ProjectID)
|
||||
ply.Namespaces = append(ply.Namespaces, project.Name)
|
||||
}
|
||||
|
||||
for _, target := range policy.Targets {
|
||||
|
@ -189,7 +189,7 @@ func TestRepPolicyAPIPost(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Trigger: &rep_models.Trigger{
|
||||
Kind: replication.TriggerKindManually,
|
||||
Kind: replication.TriggerKindManual,
|
||||
},
|
||||
},
|
||||
credential: sysAdmin,
|
||||
@ -220,7 +220,7 @@ func TestRepPolicyAPIPost(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Trigger: &rep_models.Trigger{
|
||||
Kind: replication.TriggerKindManually,
|
||||
Kind: replication.TriggerKindManual,
|
||||
},
|
||||
},
|
||||
credential: sysAdmin,
|
||||
@ -251,7 +251,7 @@ func TestRepPolicyAPIPost(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Trigger: &rep_models.Trigger{
|
||||
Kind: replication.TriggerKindManually,
|
||||
Kind: replication.TriggerKindManual,
|
||||
},
|
||||
},
|
||||
credential: sysAdmin,
|
||||
@ -412,7 +412,7 @@ func TestRepPolicyAPIPut(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Trigger: &rep_models.Trigger{
|
||||
Kind: replication.TriggerKindImmediately,
|
||||
Kind: replication.TriggerKindImmediate,
|
||||
},
|
||||
},
|
||||
credential: sysAdmin,
|
||||
@ -477,6 +477,7 @@ func TestConvertToRepPolicy(t *testing.T) {
|
||||
Projects: []*models.Project{
|
||||
&models.Project{
|
||||
ProjectID: 1,
|
||||
Name: "library",
|
||||
},
|
||||
},
|
||||
Targets: []*models.RepTarget{
|
||||
@ -501,6 +502,7 @@ func TestConvertToRepPolicy(t *testing.T) {
|
||||
Param: "{param}",
|
||||
},
|
||||
ProjectIDs: []int64{1},
|
||||
Namespaces: []string{"library"},
|
||||
TargetIDs: []int64{1},
|
||||
},
|
||||
},
|
||||
|
@ -61,3 +61,4 @@ Changelog for harbor database schema
|
||||
|
||||
- add column `filters` to table `replication_policy`
|
||||
- add column `replicate_deletion` to table `replication_policy`
|
||||
- create table `replication_immediate_trigger`
|
Loading…
Reference in New Issue
Block a user