mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-26 10:38:00 +01:00
Declare the capability of supported repository path component for registries
Declare the capability of supported repository path component for registries fixes #14981 fixes #14980 Signed-off-by: Wenkai Yin <yinw@vmware.com>
This commit is contained in:
parent
37d161c141
commit
741793a553
@ -78,7 +78,11 @@ func (c *copyFlow) Run(ctx context.Context) error {
|
||||
}
|
||||
|
||||
srcResources = assembleSourceResources(srcResources, c.policy)
|
||||
dstResources, err := assembleDestinationResources(srcResources, c.policy)
|
||||
info, err := dstAdapter.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dstResources, err := assembleDestinationResources(srcResources, c.policy, info.SupportedRepositoryPathComponentType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -45,8 +45,16 @@ func NewDeletionFlow(executionID int64, policy *repctlmodel.Policy, resources ..
|
||||
}
|
||||
|
||||
func (d *deletionFlow) Run(ctx context.Context) error {
|
||||
_, dstAdapter, err := initialize(d.policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srcResources := assembleSourceResources(d.resources, d.policy)
|
||||
dstResources, err := assembleDestinationResources(srcResources, d.policy)
|
||||
info, err := dstAdapter.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dstResources, err := assembleDestinationResources(srcResources, d.policy, info.SupportedRepositoryPathComponentType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ package flow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/goharbor/harbor/src/pkg/reg/adapter"
|
||||
"testing"
|
||||
|
||||
repctlmodel "github.com/goharbor/harbor/src/controller/replication/model"
|
||||
@ -30,15 +31,27 @@ type deletionFlowTestSuite struct {
|
||||
}
|
||||
|
||||
func (d *deletionFlowTestSuite) TestRun() {
|
||||
adp := &mockAdapter{}
|
||||
factory := &mockFactory{}
|
||||
factory.On("AdapterPattern").Return(nil)
|
||||
factory.On("Create", mock.Anything).Return(adp, nil)
|
||||
adapter.RegisterFactory("TEST_FOR_DELETION_FLOW", factory)
|
||||
|
||||
adp.On("Info").Return(&model.RegistryInfo{
|
||||
SupportedResourceTypes: []string{
|
||||
model.ResourceTypeArtifact,
|
||||
},
|
||||
}, nil)
|
||||
|
||||
taskMgr := &task.Manager{}
|
||||
taskMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||
|
||||
policy := &repctlmodel.Policy{
|
||||
SrcRegistry: &model.Registry{
|
||||
Type: model.RegistryTypeHarbor,
|
||||
Type: "TEST_FOR_DELETION_FLOW",
|
||||
},
|
||||
DestRegistry: &model.Registry{
|
||||
Type: model.RegistryTypeHarbor,
|
||||
Type: "TEST_FOR_DELETION_FLOW",
|
||||
},
|
||||
}
|
||||
resources := []*model.Resource{
|
||||
|
@ -124,10 +124,10 @@ func assembleSourceResources(resources []*model.Resource,
|
||||
|
||||
// assemble the destination resources by filling the metadata, registry and override properties
|
||||
func assembleDestinationResources(resources []*model.Resource,
|
||||
policy *repctlmodel.Policy) ([]*model.Resource, error) {
|
||||
policy *repctlmodel.Policy, dstRepoComponentPathType string) ([]*model.Resource, error) {
|
||||
var result []*model.Resource
|
||||
for _, resource := range resources {
|
||||
name, err := replaceNamespace(resource.Metadata.Repository.Name, policy.DestNamespace, policy.DestNamespaceReplaceCount)
|
||||
name, err := replaceNamespace(resource.Metadata.Repository.Name, policy.DestNamespace, policy.DestNamespaceReplaceCount, dstRepoComponentPathType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -197,7 +197,7 @@ func getResourceName(res *model.Resource) string {
|
||||
// repository:a/b/c namespace:n replaceCount: 1 -> n/b/c
|
||||
// repository:a/b/c namespace:n replaceCount: 2 -> n/c
|
||||
// repository:a/b/c namespace:n replaceCount: 3 -> n
|
||||
func replaceNamespace(repository string, namespace string, replaceCount int8) (string, error) {
|
||||
func replaceNamespace(repository string, namespace string, replaceCount int8, dstRepoComponentPathType string) (string, error) {
|
||||
if len(namespace) == 0 {
|
||||
return repository, nil
|
||||
}
|
||||
@ -208,18 +208,36 @@ func replaceNamespace(repository string, namespace string, replaceCount int8) (s
|
||||
return fmt.Sprintf("%s/%s", namespace, rest), nil
|
||||
}
|
||||
|
||||
subs := strings.Split(repository, "/")
|
||||
len := len(subs)
|
||||
var dstRepo string
|
||||
srcRepoPathComponents := strings.Split(repository, "/")
|
||||
srcLength := len(srcRepoPathComponents)
|
||||
switch {
|
||||
case replaceCount == 0:
|
||||
return fmt.Sprintf("%s/%s", namespace, repository), nil
|
||||
case int(replaceCount) == len:
|
||||
return namespace, nil
|
||||
case int(replaceCount) > len:
|
||||
dstRepo = fmt.Sprintf("%s/%s", namespace, repository)
|
||||
case int(replaceCount) == srcLength:
|
||||
dstRepo = namespace
|
||||
case int(replaceCount) > srcLength:
|
||||
return "", errors.New(nil).WithCode(errors.BadRequestCode).
|
||||
WithMessage("the repository %s contains only %d substrings, but the destination namespace replace count is %d",
|
||||
repository, len, replaceCount)
|
||||
WithMessage("the source repository %q contains only %d path components %v, but the destination namespace flattening level is %d",
|
||||
repository, srcLength, srcRepoPathComponents, replaceCount)
|
||||
default:
|
||||
return fmt.Sprintf("%s/%s", namespace, strings.Join(subs[replaceCount:], "/")), nil
|
||||
dstRepo = fmt.Sprintf("%s/%s", namespace, strings.Join(srcRepoPathComponents[replaceCount:], "/"))
|
||||
}
|
||||
|
||||
dstRepoPathComponents := strings.Split(dstRepo, "/")
|
||||
dstLength := len(dstRepoPathComponents)
|
||||
switch dstRepoComponentPathType {
|
||||
case model.RepositoryPathComponentTypeOnlyTwo:
|
||||
if dstLength != 2 {
|
||||
return "", errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("the destination repository %q contains %d path components %v, but the destination registry only supports 2",
|
||||
dstRepo, dstLength, dstRepoPathComponents)
|
||||
}
|
||||
case model.RepositoryPathComponentTypeAtLeastTwo:
|
||||
if dstLength < 2 {
|
||||
return "", errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("the destination repository %q contains only %d path components %v, but the destination registry requires at least 2",
|
||||
dstRepo, dstLength, dstRepoPathComponents)
|
||||
}
|
||||
}
|
||||
|
||||
return dstRepo, nil
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ func (s *stageTestSuite) TestAssembleDestinationResources() {
|
||||
DestNamespaceReplaceCount: -1,
|
||||
Override: true,
|
||||
}
|
||||
res, err := assembleDestinationResources(resources, policy)
|
||||
res, err := assembleDestinationResources(resources, policy, "")
|
||||
s.Require().Nil(err)
|
||||
s.Len(res, 1)
|
||||
s.Equal(model.ResourceTypeChart, res[0].Type)
|
||||
@ -126,7 +126,7 @@ func (s *stageTestSuite) TestReplaceNamespace() {
|
||||
namespace string = ""
|
||||
replaceCount int8 = 0
|
||||
)
|
||||
result, err := replaceNamespace(repository, namespace, replaceCount)
|
||||
result, err := replaceNamespace(repository, namespace, replaceCount, "")
|
||||
s.Require().Nil(err)
|
||||
s.Equal("c", result)
|
||||
|
||||
@ -134,7 +134,7 @@ func (s *stageTestSuite) TestReplaceNamespace() {
|
||||
repository = "c"
|
||||
namespace = "n"
|
||||
replaceCount = -1
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount)
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount, "")
|
||||
s.Require().Nil(err)
|
||||
s.Equal("n/c", result)
|
||||
|
||||
@ -142,7 +142,7 @@ func (s *stageTestSuite) TestReplaceNamespace() {
|
||||
repository = "b/c"
|
||||
namespace = "n"
|
||||
replaceCount = -1
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount)
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount, "")
|
||||
s.Require().Nil(err)
|
||||
s.Equal("n/c", result)
|
||||
|
||||
@ -150,7 +150,7 @@ func (s *stageTestSuite) TestReplaceNamespace() {
|
||||
repository = "a/b/c"
|
||||
namespace = "n"
|
||||
replaceCount = -1
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount)
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount, "")
|
||||
s.Require().Nil(err)
|
||||
s.Equal("n/c", result)
|
||||
|
||||
@ -158,14 +158,14 @@ func (s *stageTestSuite) TestReplaceNamespace() {
|
||||
repository = "a/b"
|
||||
namespace = "n"
|
||||
replaceCount = 3
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount)
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount, "")
|
||||
s.Require().NotNil(err)
|
||||
|
||||
// replace count = 0
|
||||
repository = "a/b/c"
|
||||
namespace = "n"
|
||||
replaceCount = 0
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount)
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount, "")
|
||||
s.Require().Nil(err)
|
||||
s.Equal("n/a/b/c", result)
|
||||
|
||||
@ -173,7 +173,7 @@ func (s *stageTestSuite) TestReplaceNamespace() {
|
||||
repository = "a/b/c"
|
||||
namespace = "n"
|
||||
replaceCount = 1
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount)
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount, "")
|
||||
s.Require().Nil(err)
|
||||
s.Equal("n/b/c", result)
|
||||
|
||||
@ -181,7 +181,7 @@ func (s *stageTestSuite) TestReplaceNamespace() {
|
||||
repository = "a/b/c"
|
||||
namespace = "n"
|
||||
replaceCount = 2
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount)
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount, "")
|
||||
s.Require().Nil(err)
|
||||
s.Equal("n/c", result)
|
||||
|
||||
@ -189,10 +189,16 @@ func (s *stageTestSuite) TestReplaceNamespace() {
|
||||
repository = "a/b/c"
|
||||
namespace = "n"
|
||||
replaceCount = 3
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount)
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount, "")
|
||||
s.Require().Nil(err)
|
||||
s.Equal("n", result)
|
||||
|
||||
// the generated destination namespace contains 1 path component, but the destination registry requires at least 2
|
||||
repository = "a/b/c"
|
||||
namespace = "n"
|
||||
replaceCount = 3
|
||||
result, err = replaceNamespace(repository, namespace, replaceCount, model.RepositoryPathComponentTypeAtLeastTwo)
|
||||
s.Require().NotNil(err)
|
||||
}
|
||||
|
||||
func TestStage(t *testing.T) {
|
||||
|
@ -90,6 +90,7 @@ func (a *adapter) Info() (*model.RegistryInfo, error) {
|
||||
model.TriggerTypeManual,
|
||||
model.TriggerTypeScheduled,
|
||||
},
|
||||
SupportedRepositoryPathComponentType: model.RepositoryPathComponentTypeOnlyTwo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ func TestInfo(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(info.SupportedResourceTypes))
|
||||
assert.Equal(t, model.ResourceTypeImage, info.SupportedResourceTypes[0])
|
||||
assert.Equal(t, model.RepositoryPathComponentTypeOnlyTwo, info.SupportedRepositoryPathComponentType)
|
||||
}
|
||||
|
||||
func TestListCandidateNamespaces(t *testing.T) {
|
||||
|
@ -114,6 +114,7 @@ func (a *Adapter) Info() (*model.RegistryInfo, error) {
|
||||
model.TriggerTypeManual,
|
||||
model.TriggerTypeScheduled,
|
||||
},
|
||||
SupportedRepositoryPathComponentType: model.RepositoryPathComponentTypeAtLeastTwo,
|
||||
}
|
||||
|
||||
enabled, err := a.Client.ChartRegistryEnabled()
|
||||
|
@ -55,6 +55,7 @@ func TestInfo(t *testing.T) {
|
||||
assert.Equal(t, 2, len(info.SupportedResourceTypes))
|
||||
assert.Equal(t, model.ResourceTypeImage, info.SupportedResourceTypes[0])
|
||||
assert.Equal(t, model.ResourceTypeChart, info.SupportedResourceTypes[1])
|
||||
assert.Equal(t, model.RepositoryPathComponentTypeAtLeastTwo, info.SupportedRepositoryPathComponentType)
|
||||
server.Close()
|
||||
|
||||
// chart museum disabled
|
||||
|
@ -70,6 +70,9 @@ const (
|
||||
Healthy = "healthy"
|
||||
// Unhealthy indicates registry is unhealthy
|
||||
Unhealthy = "unhealthy"
|
||||
|
||||
RepositoryPathComponentTypeOnlyTwo = "ONLY_TWO"
|
||||
RepositoryPathComponentTypeAtLeastTwo = "AT_LEAST_TWO"
|
||||
)
|
||||
|
||||
// Credential keeps the access key and/or secret for the related registry
|
||||
@ -130,11 +133,12 @@ type CredentialPattern struct {
|
||||
|
||||
// RegistryInfo provides base info and capability declarations of the registry
|
||||
type RegistryInfo struct {
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
SupportedResourceTypes []string `json:"-"`
|
||||
SupportedResourceFilters []*FilterStyle `json:"supported_resource_filters"`
|
||||
SupportedTriggers []string `json:"supported_triggers"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
SupportedResourceTypes []string `json:"-"`
|
||||
SupportedResourceFilters []*FilterStyle `json:"supported_resource_filters"`
|
||||
SupportedTriggers []string `json:"supported_triggers"`
|
||||
SupportedRepositoryPathComponentType string `json:"supported_repository_path_component_type"` // how many path components are allowed in the repository name
|
||||
}
|
||||
|
||||
// AdapterPattern provides base info and capability declarations of the registry
|
||||
|
Loading…
Reference in New Issue
Block a user