mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 18:55:18 +01:00
Handle the policy from previous versions
Handle the policy from previous versions Signed-off-by: Wenkai Yin <yinw@vmware.com>
This commit is contained in:
parent
4a9250e00f
commit
5a65480594
@ -122,8 +122,8 @@ func (r *ReplicationOperationAPI) CreateExecution() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
trigger := r.GetString("trigger", model.TriggerTypeManual)
|
trigger := r.GetString("trigger", string(model.TriggerTypeManual))
|
||||||
executionID, err := ng.OperationCtl.StartReplication(policy, nil, trigger)
|
executionID, err := ng.OperationCtl.StartReplication(policy, nil, model.TriggerType(trigger))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.HandleInternalServerError(fmt.Sprintf("failed to start replication for policy %d: %v", execution.PolicyID, err))
|
r.HandleInternalServerError(fmt.Sprintf("failed to start replication for policy %d: %v", execution.PolicyID, err))
|
||||||
return
|
return
|
||||||
|
@ -25,7 +25,7 @@ import (
|
|||||||
|
|
||||||
type fakedOperationController struct{}
|
type fakedOperationController struct{}
|
||||||
|
|
||||||
func (f *fakedOperationController) StartReplication(policy *model.Policy, resource *model.Resource, trigger string) (int64, error) {
|
func (f *fakedOperationController) StartReplication(policy *model.Policy, resource *model.Resource, trigger model.TriggerType) (int64, error) {
|
||||||
return 1, nil
|
return 1, nil
|
||||||
}
|
}
|
||||||
func (f *fakedOperationController) StopReplication(int64) error {
|
func (f *fakedOperationController) StopReplication(int64) error {
|
||||||
|
@ -2,6 +2,8 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/replication/ng/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -65,18 +67,18 @@ type ExecutionFieldsName struct {
|
|||||||
|
|
||||||
// Execution holds information about once replication execution.
|
// Execution holds information about once replication execution.
|
||||||
type Execution struct {
|
type Execution struct {
|
||||||
ID int64 `orm:"pk;auto;column(id)" json:"id"`
|
ID int64 `orm:"pk;auto;column(id)" json:"id"`
|
||||||
PolicyID int64 `orm:"column(policy_id)" json:"policy_id"`
|
PolicyID int64 `orm:"column(policy_id)" json:"policy_id"`
|
||||||
Status string `orm:"column(status)" json:"status"`
|
Status string `orm:"column(status)" json:"status"`
|
||||||
StatusText string `orm:"column(status_text)" json:"status_text"`
|
StatusText string `orm:"column(status_text)" json:"status_text"`
|
||||||
Total int `orm:"column(total)" json:"total"`
|
Total int `orm:"column(total)" json:"total"`
|
||||||
Failed int `orm:"column(failed)" json:"failed"`
|
Failed int `orm:"column(failed)" json:"failed"`
|
||||||
Succeed int `orm:"column(succeed)" json:"succeed"`
|
Succeed int `orm:"column(succeed)" json:"succeed"`
|
||||||
InProgress int `orm:"column(in_progress)" json:"in_progress"`
|
InProgress int `orm:"column(in_progress)" json:"in_progress"`
|
||||||
Stopped int `orm:"column(stopped)" json:"stopped"`
|
Stopped int `orm:"column(stopped)" json:"stopped"`
|
||||||
Trigger string `orm:"column(trigger)" json:"trigger"`
|
Trigger model.TriggerType `orm:"column(trigger)" json:"trigger"`
|
||||||
StartTime time.Time `orm:"column(start_time)" json:"start_time"`
|
StartTime time.Time `orm:"column(start_time)" json:"start_time"`
|
||||||
EndTime time.Time `orm:"column(end_time)" json:"end_time"`
|
EndTime time.Time `orm:"column(end_time)" json:"end_time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TaskPropsName defines the names of fields of Task
|
// TaskPropsName defines the names of fields of Task
|
||||||
|
@ -28,7 +28,7 @@ import (
|
|||||||
|
|
||||||
type fakedOperationController struct{}
|
type fakedOperationController struct{}
|
||||||
|
|
||||||
func (f *fakedOperationController) StartReplication(policy *model.Policy, resource *model.Resource, trigger string) (int64, error) {
|
func (f *fakedOperationController) StartReplication(policy *model.Policy, resource *model.Resource, trigger model.TriggerType) (int64, error) {
|
||||||
return 1, nil
|
return 1, nil
|
||||||
}
|
}
|
||||||
func (f *fakedOperationController) StopReplication(int64) error {
|
func (f *fakedOperationController) StopReplication(int64) error {
|
||||||
|
@ -24,14 +24,14 @@ import (
|
|||||||
|
|
||||||
// const definition
|
// const definition
|
||||||
const (
|
const (
|
||||||
FilterTypeResource = "resource"
|
FilterTypeResource FilterType = "resource"
|
||||||
FilterTypeName = "name"
|
FilterTypeName FilterType = "name"
|
||||||
FilterTypeTag = "tag"
|
FilterTypeTag FilterType = "tag"
|
||||||
FilterTypeLabel = "label"
|
FilterTypeLabel FilterType = "label"
|
||||||
|
|
||||||
TriggerTypeManual = "manual"
|
TriggerTypeManual TriggerType = "manual"
|
||||||
TriggerTypeScheduled = "scheduled"
|
TriggerTypeScheduled TriggerType = "scheduled"
|
||||||
TriggerTypeEventBased = "event_based"
|
TriggerTypeEventBased TriggerType = "event_based"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Policy defines the structure of a replication policy
|
// Policy defines the structure of a replication policy
|
||||||
|
@ -33,7 +33,7 @@ import (
|
|||||||
// stop, query, etc.
|
// stop, query, etc.
|
||||||
type Controller interface {
|
type Controller interface {
|
||||||
// trigger is used to specified that what this replication is triggered by
|
// trigger is used to specified that what this replication is triggered by
|
||||||
StartReplication(policy *model.Policy, resource *model.Resource, trigger string) (int64, error)
|
StartReplication(policy *model.Policy, resource *model.Resource, trigger model.TriggerType) (int64, error)
|
||||||
StopReplication(int64) error
|
StopReplication(int64) error
|
||||||
ListExecutions(...*models.ExecutionQuery) (int64, []*models.Execution, error)
|
ListExecutions(...*models.ExecutionQuery) (int64, []*models.Execution, error)
|
||||||
GetExecution(int64) (*models.Execution, error)
|
GetExecution(int64) (*models.Execution, error)
|
||||||
@ -58,7 +58,7 @@ type controller struct {
|
|||||||
scheduler scheduler.Scheduler
|
scheduler scheduler.Scheduler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *controller) StartReplication(policy *model.Policy, resource *model.Resource, trigger string) (int64, error) {
|
func (c *controller) StartReplication(policy *model.Policy, resource *model.Resource, trigger model.TriggerType) (int64, error) {
|
||||||
if resource != nil && len(resource.Metadata.Vtags) != 1 {
|
if resource != nil && len(resource.Metadata.Vtags) != 1 {
|
||||||
return 0, fmt.Errorf("the length of Vtags must be 1: %v", resource.Metadata.Vtags)
|
return 0, fmt.Errorf("the length of Vtags must be 1: %v", resource.Metadata.Vtags)
|
||||||
}
|
}
|
||||||
@ -186,7 +186,7 @@ func (c *controller) GetTaskLog(taskID int64) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create the execution record in database
|
// create the execution record in database
|
||||||
func createExecution(mgr execution.Manager, policyID int64, trigger string) (int64, error) {
|
func createExecution(mgr execution.Manager, policyID int64, trigger model.TriggerType) (int64, error) {
|
||||||
id, err := mgr.Create(&models.Execution{
|
id, err := mgr.Create(&models.Execution{
|
||||||
PolicyID: policyID,
|
PolicyID: policyID,
|
||||||
Trigger: trigger,
|
Trigger: trigger,
|
||||||
|
@ -28,7 +28,7 @@ type fakedOperationController struct {
|
|||||||
status string
|
status string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakedOperationController) StartReplication(*model.Policy, *model.Resource, string) (int64, error) {
|
func (f *fakedOperationController) StartReplication(*model.Policy, *model.Resource, model.TriggerType) (int64, error) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
func (f *fakedOperationController) StopReplication(int64) error {
|
func (f *fakedOperationController) StopReplication(int64) error {
|
||||||
|
@ -17,9 +17,11 @@ package manager
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
"github.com/goharbor/harbor/src/replication/ng/dao"
|
"github.com/goharbor/harbor/src/replication/ng/dao"
|
||||||
persist_models "github.com/goharbor/harbor/src/replication/ng/dao/models"
|
persist_models "github.com/goharbor/harbor/src/replication/ng/dao/models"
|
||||||
"github.com/goharbor/harbor/src/replication/ng/model"
|
"github.com/goharbor/harbor/src/replication/ng/model"
|
||||||
@ -61,34 +63,19 @@ func convertFromPersistModel(policy *persist_models.RepPolicy) (*model.Policy, e
|
|||||||
ply.SrcNamespaces = strings.Split(policy.SrcNamespaces, ",")
|
ply.SrcNamespaces = strings.Split(policy.SrcNamespaces, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO need to consider the consistence with the policies from previous versions
|
|
||||||
// of Harbor
|
|
||||||
// both for filter and trigger
|
|
||||||
|
|
||||||
// 2. parse Filters
|
// 2. parse Filters
|
||||||
if len(policy.Filters) > 0 {
|
filters, err := parseFilters(policy.Filters)
|
||||||
filters := []*model.Filter{}
|
if err != nil {
|
||||||
if err := json.Unmarshal([]byte(policy.Filters), &filters); err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// convert the type of value from string to model.ResourceType if the filter
|
|
||||||
// is a resource type filter
|
|
||||||
for _, filter := range filters {
|
|
||||||
if filter.Type == model.FilterTypeResource {
|
|
||||||
filter.Value = (model.ResourceType)(filter.Value.(string))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ply.Filters = filters
|
|
||||||
}
|
}
|
||||||
|
ply.Filters = filters
|
||||||
|
|
||||||
// 3. parse Trigger
|
// 3. parse Trigger
|
||||||
if len(policy.Trigger) > 0 {
|
trigger, err := parseTrigger(policy.Trigger)
|
||||||
trigger := &model.Trigger{}
|
if err != nil {
|
||||||
if err := json.Unmarshal([]byte(policy.Trigger), trigger); err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ply.Trigger = trigger
|
|
||||||
}
|
}
|
||||||
|
ply.Trigger = trigger
|
||||||
|
|
||||||
return &ply, nil
|
return &ply, nil
|
||||||
}
|
}
|
||||||
@ -216,3 +203,114 @@ func (m *DefaultManager) Update(policy *model.Policy, props ...string) error {
|
|||||||
func (m *DefaultManager) Remove(policyID int64) error {
|
func (m *DefaultManager) Remove(policyID int64) error {
|
||||||
return dao.DeleteRepPolicy(policyID)
|
return dao.DeleteRepPolicy(policyID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type filter struct {
|
||||||
|
Type model.FilterType `json:"type"`
|
||||||
|
Value interface{} `json:"value"`
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Pattern string `json:"pattern"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type trigger struct {
|
||||||
|
Type model.TriggerType `json:"type"`
|
||||||
|
Settings *model.TriggerSettings `json:"trigger_settings"`
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
ScheduleParam *scheduleParam `json:"schedule_param"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type scheduleParam struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Weekday int8 `json:"weekday"`
|
||||||
|
Offtime int64 `json:"offtime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFilters(str string) ([]*model.Filter, error) {
|
||||||
|
if len(str) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
items := []*filter{}
|
||||||
|
if err := json.Unmarshal([]byte(str), &items); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
filters := []*model.Filter{}
|
||||||
|
for _, item := range items {
|
||||||
|
filter := &model.Filter{
|
||||||
|
Type: item.Type,
|
||||||
|
Value: item.Value,
|
||||||
|
}
|
||||||
|
// keep backwards compatibility
|
||||||
|
if len(filter.Type) == 0 {
|
||||||
|
switch item.Kind {
|
||||||
|
case "repository":
|
||||||
|
filter.Type = model.FilterTypeName
|
||||||
|
case "tag":
|
||||||
|
filter.Type = model.FilterTypeTag
|
||||||
|
case "label":
|
||||||
|
// TODO if we support the label filter, remove the checking logic here
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
log.Warningf("unknown filter type: %s", filter.Type)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if filter.Value == nil {
|
||||||
|
filter.Value = item.Pattern
|
||||||
|
}
|
||||||
|
// convert the type of value from string to model.ResourceType if the filter
|
||||||
|
// is a resource type filter
|
||||||
|
if filter.Type == model.FilterTypeResource {
|
||||||
|
filter.Value = (model.ResourceType)(filter.Value.(string))
|
||||||
|
}
|
||||||
|
filters = append(filters, filter)
|
||||||
|
}
|
||||||
|
return filters, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTrigger(str string) (*model.Trigger, error) {
|
||||||
|
if len(str) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
item := &trigger{}
|
||||||
|
if err := json.Unmarshal([]byte(str), item); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
trigger := &model.Trigger{
|
||||||
|
Type: item.Type,
|
||||||
|
Settings: item.Settings,
|
||||||
|
}
|
||||||
|
// keep backwards compatibility
|
||||||
|
if len(trigger.Type) == 0 {
|
||||||
|
switch item.Kind {
|
||||||
|
case "Manual":
|
||||||
|
trigger.Type = model.TriggerTypeManual
|
||||||
|
case "Immediate":
|
||||||
|
trigger.Type = model.TriggerTypeEventBased
|
||||||
|
case "Scheduled":
|
||||||
|
trigger.Type = model.TriggerTypeScheduled
|
||||||
|
trigger.Settings = &model.TriggerSettings{
|
||||||
|
Cron: parseScheduleParamToCron(item.ScheduleParam),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Warningf("unknown trigger type: %s", item.Kind)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trigger, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseScheduleParamToCron(param *scheduleParam) string {
|
||||||
|
if param == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
offtime := param.Offtime
|
||||||
|
offtime = offtime % (3600 * 24)
|
||||||
|
hour := int(offtime / 3600)
|
||||||
|
offtime = offtime % 3600
|
||||||
|
minute := int(offtime / 60)
|
||||||
|
second := int(offtime % 60)
|
||||||
|
if param.Type == "Weekly" {
|
||||||
|
return fmt.Sprintf("%d %d %d * * %d", second, minute, hour, param.Weekday%7)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d %d %d * * *", second, minute, hour)
|
||||||
|
}
|
||||||
|
@ -203,3 +203,70 @@ func TestNewDefaultManager(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseFilters(t *testing.T) {
|
||||||
|
// nil filter string
|
||||||
|
str := ""
|
||||||
|
filters, err := parseFilters(str)
|
||||||
|
require.Nil(t, err)
|
||||||
|
assert.Nil(t, filters)
|
||||||
|
// only contains the fields that introduced in the latest version
|
||||||
|
str = `[{"type":"name","value":"library/hello-world"}]`
|
||||||
|
filters, err = parseFilters(str)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 1, len(filters))
|
||||||
|
assert.Equal(t, model.FilterTypeName, filters[0].Type)
|
||||||
|
assert.Equal(t, "library/hello-world", filters[0].Value.(string))
|
||||||
|
// contains "kind" from previous versions
|
||||||
|
str = `[{"kind":"repository","value":"library/hello-world"}]`
|
||||||
|
filters, err = parseFilters(str)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 1, len(filters))
|
||||||
|
assert.Equal(t, model.FilterTypeName, filters[0].Type)
|
||||||
|
assert.Equal(t, "library/hello-world", filters[0].Value.(string))
|
||||||
|
// contains "pattern" from previous versions
|
||||||
|
str = `[{"kind":"repository","pattern":"library/hello-world"}]`
|
||||||
|
filters, err = parseFilters(str)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 1, len(filters))
|
||||||
|
assert.Equal(t, model.FilterTypeName, filters[0].Type)
|
||||||
|
assert.Equal(t, "library/hello-world", filters[0].Value.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseTrigger(t *testing.T) {
|
||||||
|
// nil trigger string
|
||||||
|
str := ""
|
||||||
|
trigger, err := parseTrigger(str)
|
||||||
|
require.Nil(t, err)
|
||||||
|
assert.Nil(t, trigger)
|
||||||
|
// only contains the fields that introduced in the latest version
|
||||||
|
str = `{"type":"scheduled", "trigger_settings":{"cron":"1 * * * * *"}}`
|
||||||
|
trigger, err = parseTrigger(str)
|
||||||
|
require.Nil(t, err)
|
||||||
|
assert.Equal(t, model.TriggerTypeScheduled, trigger.Type)
|
||||||
|
assert.Equal(t, "1 * * * * *", trigger.Settings.Cron)
|
||||||
|
// contains "kind" from previous versions
|
||||||
|
str = `{"kind":"Manual"}`
|
||||||
|
trigger, err = parseTrigger(str)
|
||||||
|
require.Nil(t, err)
|
||||||
|
assert.Equal(t, model.TriggerTypeManual, trigger.Type)
|
||||||
|
assert.Nil(t, trigger.Settings)
|
||||||
|
// contains "kind" from previous versions
|
||||||
|
str = `{"kind":"Immediate"}`
|
||||||
|
trigger, err = parseTrigger(str)
|
||||||
|
require.Nil(t, err)
|
||||||
|
assert.Equal(t, model.TriggerTypeEventBased, trigger.Type)
|
||||||
|
assert.Nil(t, trigger.Settings)
|
||||||
|
// contains "schedule_param" from previous versions
|
||||||
|
str = `{"kind":"Scheduled","schedule_param":{"type":"Weekly","weekday":1,"offtime":0}}`
|
||||||
|
trigger, err = parseTrigger(str)
|
||||||
|
require.Nil(t, err)
|
||||||
|
assert.Equal(t, model.TriggerTypeScheduled, trigger.Type)
|
||||||
|
assert.Equal(t, "0 0 0 * * 1", trigger.Settings.Cron)
|
||||||
|
// contains "schedule_param" from previous versions
|
||||||
|
str = `{"kind":"Scheduled","schedule_param":{"type":"Daily","offtime":0}}`
|
||||||
|
trigger, err = parseTrigger(str)
|
||||||
|
require.Nil(t, err)
|
||||||
|
assert.Equal(t, model.TriggerTypeScheduled, trigger.Type)
|
||||||
|
assert.Equal(t, "0 0 0 * * *", trigger.Settings.Cron)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user