mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-30 06:03:45 +01:00
Fix replication bugs (#7470)
1. Only return the event based trigger for local Harbor 2. Valid the trigger pattern and cron string when creating/updating policies 3. Set the schema as "http" if it isn't specified when creating/updating registries Signed-off-by: Wenkai Yin <yinw@vmware.com>
This commit is contained in:
parent
823d9c04a9
commit
d8310cc708
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
common_http "github.com/goharbor/harbor/src/common/http"
|
common_http "github.com/goharbor/harbor/src/common/http"
|
||||||
"github.com/goharbor/harbor/src/common/utils"
|
"github.com/goharbor/harbor/src/common/utils"
|
||||||
@ -206,6 +207,10 @@ func (t *RegistryAPI) Post() {
|
|||||||
t.SendConflictError(fmt.Errorf("name '%s' is already used", r.Name))
|
t.SendConflictError(fmt.Errorf("name '%s' is already used", r.Name))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
i := strings.Index(r.URL, "://")
|
||||||
|
if i == -1 {
|
||||||
|
r.URL = fmt.Sprintf("http://%s", r.URL)
|
||||||
|
}
|
||||||
|
|
||||||
status, err := registry.CheckHealthStatus(r)
|
status, err := registry.CheckHealthStatus(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -411,6 +416,10 @@ func (t *RegistryAPI) GetInfo() {
|
|||||||
t.SendInternalServerError(fmt.Errorf("failed to get registry info %d: %v", id, err))
|
t.SendInternalServerError(fmt.Errorf("failed to get registry info %d: %v", id, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// currently, only the local Harbor registry supports the event based trigger, append it here
|
||||||
|
if id == 0 {
|
||||||
|
info.SupportedTriggers = append(info.SupportedTriggers, model.TriggerTypeEventBased)
|
||||||
|
}
|
||||||
t.WriteJSONData(process(info))
|
t.WriteJSONData(process(info))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,6 @@ func (a *adapter) Info() (*model.RegistryInfo, error) {
|
|||||||
SupportedTriggers: []model.TriggerType{
|
SupportedTriggers: []model.TriggerType{
|
||||||
model.TriggerTypeManual,
|
model.TriggerTypeManual,
|
||||||
model.TriggerTypeScheduled,
|
model.TriggerTypeScheduled,
|
||||||
model.TriggerTypeEventBased,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ func TestInfo(t *testing.T) {
|
|||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
assert.Equal(t, model.RegistryTypeHarbor, info.Type)
|
assert.Equal(t, model.RegistryTypeHarbor, info.Type)
|
||||||
assert.Equal(t, 2, len(info.SupportedResourceFilters))
|
assert.Equal(t, 2, len(info.SupportedResourceFilters))
|
||||||
assert.Equal(t, 3, len(info.SupportedTriggers))
|
assert.Equal(t, 2, len(info.SupportedTriggers))
|
||||||
assert.Equal(t, 2, len(info.SupportedResourceTypes))
|
assert.Equal(t, 2, len(info.SupportedResourceTypes))
|
||||||
assert.Equal(t, model.ResourceTypeImage, info.SupportedResourceTypes[0])
|
assert.Equal(t, model.ResourceTypeImage, info.SupportedResourceTypes[0])
|
||||||
assert.Equal(t, model.ResourceTypeChart, info.SupportedResourceTypes[1])
|
assert.Equal(t, model.ResourceTypeChart, info.SupportedResourceTypes[1])
|
||||||
@ -67,7 +67,7 @@ func TestInfo(t *testing.T) {
|
|||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
assert.Equal(t, model.RegistryTypeHarbor, info.Type)
|
assert.Equal(t, model.RegistryTypeHarbor, info.Type)
|
||||||
assert.Equal(t, 2, len(info.SupportedResourceFilters))
|
assert.Equal(t, 2, len(info.SupportedResourceFilters))
|
||||||
assert.Equal(t, 3, len(info.SupportedTriggers))
|
assert.Equal(t, 2, len(info.SupportedTriggers))
|
||||||
assert.Equal(t, 1, len(info.SupportedResourceTypes))
|
assert.Equal(t, 1, len(info.SupportedResourceTypes))
|
||||||
assert.Equal(t, model.ResourceTypeImage, info.SupportedResourceTypes[0])
|
assert.Equal(t, model.ResourceTypeImage, info.SupportedResourceTypes[0])
|
||||||
server.Close()
|
server.Close()
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
|
|
||||||
"github.com/astaxie/beego/validation"
|
"github.com/astaxie/beego/validation"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
|
"github.com/robfig/cron"
|
||||||
)
|
)
|
||||||
|
|
||||||
// const definition
|
// const definition
|
||||||
@ -88,10 +89,20 @@ func (p *Policy) Valid(v *validation.Validation) {
|
|||||||
|
|
||||||
// valid the filters
|
// valid the filters
|
||||||
for _, filter := range p.Filters {
|
for _, filter := range p.Filters {
|
||||||
if filter.Type != FilterTypeResource &&
|
value, ok := filter.Value.(string)
|
||||||
filter.Type != FilterTypeName &&
|
if !ok {
|
||||||
filter.Type != FilterTypeTag &&
|
v.SetError("filters", "the type of filter value isn't string")
|
||||||
filter.Type != FilterTypeLabel {
|
break
|
||||||
|
}
|
||||||
|
switch filter.Type {
|
||||||
|
case FilterTypeResource:
|
||||||
|
rt := ResourceType(value)
|
||||||
|
if !(rt == ResourceTypeImage || rt == ResourceTypeChart) {
|
||||||
|
v.SetError("filters", fmt.Sprintf("invalid resource filter: %s", value))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case FilterTypeName, FilterTypeTag, FilterTypeLabel:
|
||||||
|
default:
|
||||||
v.SetError("filters", "invalid filter type")
|
v.SetError("filters", "invalid filter type")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -99,15 +110,20 @@ func (p *Policy) Valid(v *validation.Validation) {
|
|||||||
|
|
||||||
// valid trigger
|
// valid trigger
|
||||||
if p.Trigger != nil {
|
if p.Trigger != nil {
|
||||||
if p.Trigger.Type != TriggerTypeManual &&
|
switch p.Trigger.Type {
|
||||||
p.Trigger.Type != TriggerTypeScheduled &&
|
case TriggerTypeManual, TriggerTypeEventBased:
|
||||||
p.Trigger.Type != TriggerTypeEventBased {
|
case TriggerTypeScheduled:
|
||||||
|
if p.Trigger.Settings == nil || len(p.Trigger.Settings.Cron) == 0 {
|
||||||
|
v.SetError("trigger", fmt.Sprintf("the cron string cannot be empty when the trigger type is %s", TriggerTypeScheduled))
|
||||||
|
} else {
|
||||||
|
_, err := cron.Parse(p.Trigger.Settings.Cron)
|
||||||
|
if err != nil {
|
||||||
|
v.SetError("trigger", fmt.Sprintf("invalid cron string for scheduled trigger: %s", p.Trigger.Settings.Cron))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
v.SetError("trigger", "invalid trigger type")
|
v.SetError("trigger", "invalid trigger type")
|
||||||
}
|
}
|
||||||
if p.Trigger.Type == TriggerTypeScheduled &&
|
|
||||||
(p.Trigger.Settings == nil || len(p.Trigger.Settings.Cron) == 0) {
|
|
||||||
v.SetError("trigger", fmt.Sprintf("the cron string cannot be empty when the trigger type is %s", TriggerTypeScheduled))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/astaxie/beego/validation"
|
"github.com/astaxie/beego/validation"
|
||||||
@ -69,6 +70,48 @@ func TestValidOfPolicy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
pass: false,
|
pass: false,
|
||||||
},
|
},
|
||||||
|
// invalid filter
|
||||||
|
{
|
||||||
|
policy: &Policy{
|
||||||
|
Name: "policy01",
|
||||||
|
SrcRegistry: &Registry{
|
||||||
|
ID: 0,
|
||||||
|
},
|
||||||
|
DestRegistry: &Registry{
|
||||||
|
ID: 1,
|
||||||
|
},
|
||||||
|
Filters: []*Filter{
|
||||||
|
{
|
||||||
|
Type: FilterTypeResource,
|
||||||
|
Value: "invalid_resource_type",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pass: false,
|
||||||
|
},
|
||||||
|
// invalid filter
|
||||||
|
{
|
||||||
|
policy: &Policy{
|
||||||
|
Name: "policy01",
|
||||||
|
SrcRegistry: &Registry{
|
||||||
|
ID: 0,
|
||||||
|
},
|
||||||
|
DestRegistry: &Registry{
|
||||||
|
ID: 1,
|
||||||
|
},
|
||||||
|
Filters: []*Filter{
|
||||||
|
{
|
||||||
|
Type: FilterTypeResource,
|
||||||
|
Value: ResourceTypeImage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: FilterTypeName,
|
||||||
|
Value: "a[",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pass: false,
|
||||||
|
},
|
||||||
// invalid trigger
|
// invalid trigger
|
||||||
{
|
{
|
||||||
policy: &Policy{
|
policy: &Policy{
|
||||||
@ -113,6 +156,35 @@ func TestValidOfPolicy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
pass: false,
|
pass: false,
|
||||||
},
|
},
|
||||||
|
// invalid cron
|
||||||
|
{
|
||||||
|
policy: &Policy{
|
||||||
|
Name: "policy01",
|
||||||
|
SrcRegistry: &Registry{
|
||||||
|
ID: 0,
|
||||||
|
},
|
||||||
|
DestRegistry: &Registry{
|
||||||
|
ID: 1,
|
||||||
|
},
|
||||||
|
Filters: []*Filter{
|
||||||
|
{
|
||||||
|
Type: FilterTypeResource,
|
||||||
|
Value: "image",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: FilterTypeName,
|
||||||
|
Value: "library/**",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trigger: &Trigger{
|
||||||
|
Type: TriggerTypeScheduled,
|
||||||
|
Settings: &TriggerSettings{
|
||||||
|
Cron: "* * *",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pass: false,
|
||||||
|
},
|
||||||
// pass
|
// pass
|
||||||
{
|
{
|
||||||
policy: &Policy{
|
policy: &Policy{
|
||||||
@ -124,15 +196,19 @@ func TestValidOfPolicy(t *testing.T) {
|
|||||||
ID: 1,
|
ID: 1,
|
||||||
},
|
},
|
||||||
Filters: []*Filter{
|
Filters: []*Filter{
|
||||||
|
{
|
||||||
|
Type: FilterTypeResource,
|
||||||
|
Value: "image",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Type: FilterTypeName,
|
Type: FilterTypeName,
|
||||||
Value: "library",
|
Value: "library/**",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Trigger: &Trigger{
|
Trigger: &Trigger{
|
||||||
Type: TriggerTypeScheduled,
|
Type: TriggerTypeScheduled,
|
||||||
Settings: &TriggerSettings{
|
Settings: &TriggerSettings{
|
||||||
Cron: "* * *",
|
Cron: "* * * * * *",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -140,7 +216,8 @@ func TestValidOfPolicy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for i, c := range cases {
|
||||||
|
fmt.Printf("running case %d ...\n", i)
|
||||||
v := &validation.Validation{}
|
v := &validation.Validation{}
|
||||||
c.policy.Valid(v)
|
c.policy.Valid(v)
|
||||||
assert.Equal(t, c.pass, len(v.Errors) == 0)
|
assert.Equal(t, c.pass, len(v.Errors) == 0)
|
||||||
|
@ -18,6 +18,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
|
|
||||||
"github.com/bmatcuk/doublestar"
|
"github.com/bmatcuk/doublestar"
|
||||||
"github.com/goharbor/harbor/src/common/utils/registry"
|
"github.com/goharbor/harbor/src/common/utils/registry"
|
||||||
)
|
)
|
||||||
@ -27,7 +29,12 @@ func Match(pattern, str string) (bool, error) {
|
|||||||
if len(pattern) == 0 {
|
if len(pattern) == 0 {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
return doublestar.Match(pattern, str)
|
match, err := doublestar.Match(pattern, str)
|
||||||
|
if err == doublestar.ErrBadPattern {
|
||||||
|
log.Warningf("failed to match the string %s against pattern %s: %v", str, pattern, err)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return match, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSpecificRepositoryName if the name not contains any char of "*?[{\\]}^,",
|
// IsSpecificRepositoryName if the name not contains any char of "*?[{\\]}^,",
|
||||||
|
@ -67,6 +67,11 @@ func TestMatch(t *testing.T) {
|
|||||||
str: "1.01",
|
str: "1.01",
|
||||||
match: false,
|
match: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
pattern: "a[",
|
||||||
|
str: "aaa",
|
||||||
|
match: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
match, err := Match(c.pattern, c.str)
|
match, err := Match(c.pattern, c.str)
|
||||||
|
Loading…
Reference in New Issue
Block a user