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:
Wenkai Yin 2021-05-28 10:42:16 +08:00
parent 37d161c141
commit 741793a553
10 changed files with 88 additions and 31 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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{

View File

@ -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
}

View File

@ -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) {

View File

@ -90,6 +90,7 @@ func (a *adapter) Info() (*model.RegistryInfo, error) {
model.TriggerTypeManual,
model.TriggerTypeScheduled,
},
SupportedRepositoryPathComponentType: model.RepositoryPathComponentTypeOnlyTwo,
}, nil
}

View File

@ -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) {

View File

@ -114,6 +114,7 @@ func (a *Adapter) Info() (*model.RegistryInfo, error) {
model.TriggerTypeManual,
model.TriggerTypeScheduled,
},
SupportedRepositoryPathComponentType: model.RepositoryPathComponentTypeAtLeastTwo,
}
enabled, err := a.Client.ChartRegistryEnabled()

View File

@ -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

View File

@ -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