Flattening repository path components excepting the last one

Flattening repository path components excepting the last one in replication

Fixes #15072

Signed-off-by: Wenkai Yin <yinw@vmware.com>
This commit is contained in:
Wenkai Yin 2021-06-08 15:39:45 +08:00
parent 1f9ab2b735
commit 120be22988
2 changed files with 28 additions and 32 deletions

View File

@ -17,13 +17,13 @@ package flow
import ( import (
"fmt" "fmt"
"github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/errors"
"path"
"strings" "strings"
repctlmodel "github.com/goharbor/harbor/src/controller/replication/model" repctlmodel "github.com/goharbor/harbor/src/controller/replication/model"
"github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/lib/log"
adp "github.com/goharbor/harbor/src/pkg/reg/adapter" adp "github.com/goharbor/harbor/src/pkg/reg/adapter"
"github.com/goharbor/harbor/src/pkg/reg/model" "github.com/goharbor/harbor/src/pkg/reg/model"
"github.com/goharbor/harbor/src/pkg/reg/util"
) )
// get/create the source registry, destination registry, source adapter and destination adapter // get/create the source registry, destination registry, source adapter and destination adapter
@ -192,38 +192,34 @@ func getResourceName(res *model.Resource) string {
return fmt.Sprintf("%s [%d item(s) in total]", meta.Repository.Name, n) return fmt.Sprintf("%s [%d item(s) in total]", meta.Repository.Name, n)
} }
// repository:a/b/c namespace:n replaceCount: -1 -> n/c // repository:a/b/c/image namespace:n replaceCount: -1 -> n/image
// repository:a/b/c namespace:n replaceCount: 0 -> n/a/b/c // repository:a/b/c/image namespace:n replaceCount: 0 -> n/a/b/c/image
// repository:a/b/c namespace:n replaceCount: 1 -> n/b/c // repository:a/b/c/image namespace:n replaceCount: 1 -> n/b/c/image
// repository:a/b/c namespace:n replaceCount: 2 -> n/c // repository:a/b/c/image namespace:n replaceCount: 2 -> n/c/image
// repository:a/b/c namespace:n replaceCount: 3 -> n // repository:a/b/c/image namespace:n replaceCount: 3 -> n/image
// repository:a/b/c/image namespace:n replaceCount: 4 -> error
func replaceNamespace(repository string, namespace string, replaceCount int8, dstRepoComponentPathType string) (string, error) { func replaceNamespace(repository string, namespace string, replaceCount int8, dstRepoComponentPathType string) (string, error) {
if len(namespace) == 0 { if len(namespace) == 0 {
return repository, nil return repository, nil
} }
// legacy logic to keep backward compatibility
if replaceCount < 0 {
_, rest := util.ParseRepository(repository)
return fmt.Sprintf("%s/%s", namespace, rest), nil
}
var dstRepo string
srcRepoPathComponents := strings.Split(repository, "/") srcRepoPathComponents := strings.Split(repository, "/")
srcLength := len(srcRepoPathComponents) srcLength := len(srcRepoPathComponents)
var dstRepoPrefix string
switch { switch {
case replaceCount == 0: case replaceCount < 0: // legacy logic to keep backward compatibility
dstRepo = fmt.Sprintf("%s/%s", namespace, repository) dstRepoPrefix = namespace
case int(replaceCount) == srcLength: case int(replaceCount) > srcLength-1: // invalid replace count
dstRepo = namespace
case int(replaceCount) > srcLength:
return "", errors.New(nil).WithCode(errors.BadRequestCode). return "", errors.New(nil).WithCode(errors.BadRequestCode).
WithMessage("the source repository %q contains only %d path components %v, but the destination namespace flattening level is %d", WithMessage("the source repository %q contains only %d path components %v excepting the last one, but the destination namespace flattening level is %d",
repository, srcLength, srcRepoPathComponents, replaceCount) repository, srcLength-1, srcRepoPathComponents[:srcLength-1], replaceCount)
default: default:
dstRepo = fmt.Sprintf("%s/%s", namespace, strings.Join(srcRepoPathComponents[replaceCount:], "/")) dstRepoPrefix = namespace + "/" + strings.Join(srcRepoPathComponents[replaceCount:srcLength-1], "/")
} }
name := srcRepoPathComponents[srcLength-1] // the last part of the repository path components, we'll keep it as the same with the source
dstRepo := path.Join(dstRepoPrefix, name)
dstRepoPathComponents := strings.Split(dstRepo, "/") dstRepoPathComponents := strings.Split(dstRepo, "/")
dstLength := len(dstRepoPathComponents) dstLength := len(dstRepoPathComponents)
switch dstRepoComponentPathType { switch dstRepoComponentPathType {

View File

@ -157,7 +157,7 @@ func (s *stageTestSuite) TestReplaceNamespace() {
// replace count > actual sub strings // replace count > actual sub strings
repository = "a/b" repository = "a/b"
namespace = "n" namespace = "n"
replaceCount = 3 replaceCount = 2
result, err = replaceNamespace(repository, namespace, replaceCount, "") result, err = replaceNamespace(repository, namespace, replaceCount, "")
s.Require().NotNil(err) s.Require().NotNil(err)
@ -185,20 +185,20 @@ func (s *stageTestSuite) TestReplaceNamespace() {
s.Require().Nil(err) s.Require().Nil(err)
s.Equal("n/c", result) s.Equal("n/c", result)
// replace count = 3 // the generated destination namespace contains 3 path component, but the destination registry requires only 2
repository = "a/b/c" repository = "a/b/c"
namespace = "n" namespace = "n"
replaceCount = 3 replaceCount = 1
result, err = replaceNamespace(repository, namespace, replaceCount, model.RepositoryPathComponentTypeOnlyTwo)
s.Require().NotNil(err)
// replace count =0, repository contains no "/"
repository = "a"
namespace = "n"
replaceCount = 0
result, err = replaceNamespace(repository, namespace, replaceCount, "") result, err = replaceNamespace(repository, namespace, replaceCount, "")
s.Require().Nil(err) s.Require().Nil(err)
s.Equal("n", result) s.Equal("n/a", 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) { func TestStage(t *testing.T) {