Use the repository name of artifact model

As we store the repository name in the artifact table, we can use it direclty in the code to reduce the database query

Signed-off-by: Wenkai Yin <yinw@vmware.com>
This commit is contained in:
Wenkai Yin 2020-02-25 21:40:49 +08:00
parent 7857528e45
commit 02c2647e1e
25 changed files with 178 additions and 297 deletions

View File

@ -56,9 +56,6 @@ WHERE ordered_art.seq=1;
ALTER TABLE artifact DROP COLUMN tag; ALTER TABLE artifact DROP COLUMN tag;
/*TODO: remove this after insert the repository_name when create artifact*/
ALTER TABLE artifact ALTER COLUMN repository_name DROP NOT NULL;
/*remove the duplicate artifact rows*/ /*remove the duplicate artifact rows*/
DELETE FROM artifact DELETE FROM artifact
WHERE id NOT IN ( WHERE id NOT IN (

View File

@ -25,7 +25,6 @@ import (
"github.com/goharbor/harbor/src/api/artifact/abstractor/resolver" "github.com/goharbor/harbor/src/api/artifact/abstractor/resolver"
ierror "github.com/goharbor/harbor/src/internal/error" ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/repository"
"github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/image-spec/specs-go/v1"
) )
@ -43,24 +42,18 @@ type Abstractor interface {
// NewAbstractor returns an instance of the default abstractor // NewAbstractor returns an instance of the default abstractor
func NewAbstractor() Abstractor { func NewAbstractor() Abstractor {
return &abstractor{ return &abstractor{
repoMgr: repository.Mgr,
blobFetcher: blob.Fcher, blobFetcher: blob.Fcher,
} }
} }
type abstractor struct { type abstractor struct {
repoMgr repository.Manager
blobFetcher blob.Fetcher blobFetcher blob.Fetcher
} }
// TODO add white list for supported artifact type // TODO add white list for supported artifact type
func (a *abstractor) AbstractMetadata(ctx context.Context, artifact *artifact.Artifact) error { func (a *abstractor) AbstractMetadata(ctx context.Context, artifact *artifact.Artifact) error {
repository, err := a.repoMgr.Get(ctx, artifact.RepositoryID)
if err != nil {
return err
}
// read manifest content // read manifest content
manifestMediaType, content, err := a.blobFetcher.FetchManifest(repository.Name, artifact.Digest) manifestMediaType, content, err := a.blobFetcher.FetchManifest(artifact.RepositoryName, artifact.Digest)
if err != nil { if err != nil {
return err return err
} }

View File

@ -18,12 +18,10 @@ import (
"github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/manifest/schema2"
"github.com/goharbor/harbor/src/api/artifact/abstractor/resolver" "github.com/goharbor/harbor/src/api/artifact/abstractor/resolver"
"github.com/goharbor/harbor/src/common/models"
ierror "github.com/goharbor/harbor/src/internal/error" ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob" "github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob"
tresolver "github.com/goharbor/harbor/src/testing/api/artifact/abstractor/resolver" tresolver "github.com/goharbor/harbor/src/testing/api/artifact/abstractor/resolver"
"github.com/goharbor/harbor/src/testing/pkg/repository"
v1 "github.com/opencontainers/image-spec/specs-go/v1" v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"testing" "testing"
@ -203,16 +201,13 @@ type abstractorTestSuite struct {
suite.Suite suite.Suite
abstractor Abstractor abstractor Abstractor
fetcher *blob.FakeFetcher fetcher *blob.FakeFetcher
repoMgr *repository.FakeManager
resolver *tresolver.FakeResolver resolver *tresolver.FakeResolver
} }
func (a *abstractorTestSuite) SetupTest() { func (a *abstractorTestSuite) SetupTest() {
a.fetcher = &blob.FakeFetcher{} a.fetcher = &blob.FakeFetcher{}
a.repoMgr = &repository.FakeManager{}
a.resolver = &tresolver.FakeResolver{} a.resolver = &tresolver.FakeResolver{}
a.abstractor = &abstractor{ a.abstractor = &abstractor{
repoMgr: a.repoMgr,
blobFetcher: a.fetcher, blobFetcher: a.fetcher,
} }
} }
@ -220,7 +215,6 @@ func (a *abstractorTestSuite) SetupTest() {
// docker manifest v1 // docker manifest v1
func (a *abstractorTestSuite) TestAbstractMetadataOfV1Manifest() { func (a *abstractorTestSuite) TestAbstractMetadataOfV1Manifest() {
resolver.Register(a.resolver, schema1.MediaTypeSignedManifest) resolver.Register(a.resolver, schema1.MediaTypeSignedManifest)
a.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
a.fetcher.On("FetchManifest").Return(schema1.MediaTypeSignedManifest, []byte(v1Manifest), nil) a.fetcher.On("FetchManifest").Return(schema1.MediaTypeSignedManifest, []byte(v1Manifest), nil)
a.resolver.On("ArtifactType").Return(fakeArtifactType) a.resolver.On("ArtifactType").Return(fakeArtifactType)
a.resolver.On("ResolveMetadata").Return(nil) a.resolver.On("ResolveMetadata").Return(nil)
@ -238,7 +232,6 @@ func (a *abstractorTestSuite) TestAbstractMetadataOfV1Manifest() {
// docker manifest v2 // docker manifest v2
func (a *abstractorTestSuite) TestAbstractMetadataOfV2Manifest() { func (a *abstractorTestSuite) TestAbstractMetadataOfV2Manifest() {
resolver.Register(a.resolver, schema2.MediaTypeImageConfig) resolver.Register(a.resolver, schema2.MediaTypeImageConfig)
a.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
a.fetcher.On("FetchManifest").Return(schema2.MediaTypeManifest, []byte(v2Manifest), nil) a.fetcher.On("FetchManifest").Return(schema2.MediaTypeManifest, []byte(v2Manifest), nil)
a.resolver.On("ArtifactType").Return(fakeArtifactType) a.resolver.On("ArtifactType").Return(fakeArtifactType)
a.resolver.On("ResolveMetadata").Return(nil) a.resolver.On("ResolveMetadata").Return(nil)
@ -257,7 +250,6 @@ func (a *abstractorTestSuite) TestAbstractMetadataOfV2Manifest() {
// OCI index // OCI index
func (a *abstractorTestSuite) TestAbstractMetadataOfIndex() { func (a *abstractorTestSuite) TestAbstractMetadataOfIndex() {
resolver.Register(a.resolver, v1.MediaTypeImageIndex) resolver.Register(a.resolver, v1.MediaTypeImageIndex)
a.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
a.fetcher.On("FetchManifest").Return(v1.MediaTypeImageIndex, []byte(index), nil) a.fetcher.On("FetchManifest").Return(v1.MediaTypeImageIndex, []byte(index), nil)
a.resolver.On("ArtifactType").Return(fakeArtifactType) a.resolver.On("ArtifactType").Return(fakeArtifactType)
a.resolver.On("ResolveMetadata").Return(nil) a.resolver.On("ResolveMetadata").Return(nil)
@ -275,7 +267,6 @@ func (a *abstractorTestSuite) TestAbstractMetadataOfIndex() {
// OCI index // OCI index
func (a *abstractorTestSuite) TestAbstractMetadataOfUnsupported() { func (a *abstractorTestSuite) TestAbstractMetadataOfUnsupported() {
a.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
a.fetcher.On("FetchManifest").Return("unsupported-manifest", []byte{}, nil) a.fetcher.On("FetchManifest").Return("unsupported-manifest", []byte{}, nil)
artifact := &artifact.Artifact{ artifact := &artifact.Artifact{
ID: 1, ID: 1,

View File

@ -24,7 +24,6 @@ import (
ierror "github.com/goharbor/harbor/src/internal/error" ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/chart" "github.com/goharbor/harbor/src/pkg/chart"
"github.com/goharbor/harbor/src/pkg/repository"
v1 "github.com/opencontainers/image-spec/specs-go/v1" v1 "github.com/opencontainers/image-spec/specs-go/v1"
) )
@ -41,7 +40,6 @@ const (
func init() { func init() {
resolver := &resolver{ resolver := &resolver{
repoMgr: repository.Mgr,
blobFetcher: blob.Fcher, blobFetcher: blob.Fcher,
chartOperator: chart.Optr, chartOperator: chart.Optr,
} }
@ -56,22 +54,17 @@ func init() {
} }
type resolver struct { type resolver struct {
repoMgr repository.Manager
blobFetcher blob.Fetcher blobFetcher blob.Fetcher
chartOperator chart.Operator chartOperator chart.Operator
} }
func (r *resolver) ResolveMetadata(ctx context.Context, manifest []byte, artifact *artifact.Artifact) error { func (r *resolver) ResolveMetadata(ctx context.Context, manifest []byte, artifact *artifact.Artifact) error {
repository, err := r.repoMgr.Get(ctx, artifact.RepositoryID)
if err != nil {
return err
}
m := &v1.Manifest{} m := &v1.Manifest{}
if err := json.Unmarshal(manifest, m); err != nil { if err := json.Unmarshal(manifest, m); err != nil {
return err return err
} }
digest := m.Config.Digest.String() digest := m.Config.Digest.String()
layer, err := r.blobFetcher.FetchLayer(repository.Name, digest) layer, err := r.blobFetcher.FetchLayer(artifact.RepositoryName, digest)
if err != nil { if err != nil {
return err return err
} }
@ -95,11 +88,7 @@ func (r *resolver) ResolveAddition(ctx context.Context, artifact *artifact.Artif
WithMessage("addition %s isn't supported for %s", addition, ArtifactTypeChart) WithMessage("addition %s isn't supported for %s", addition, ArtifactTypeChart)
} }
repository, err := r.repoMgr.Get(ctx, artifact.RepositoryID) _, content, err := r.blobFetcher.FetchManifest(artifact.RepositoryName, artifact.Digest)
if err != nil {
return nil, err
}
_, content, err := r.blobFetcher.FetchManifest(repository.Name, artifact.Digest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -112,7 +101,7 @@ func (r *resolver) ResolveAddition(ctx context.Context, artifact *artifact.Artif
// chart do have two layers, one is config, we should resolve the other one. // chart do have two layers, one is config, we should resolve the other one.
layerDgst := layer.Digest.String() layerDgst := layer.Digest.String()
if layerDgst != manifest.Config.Digest.String() { if layerDgst != manifest.Config.Digest.String() {
content, err = r.blobFetcher.FetchLayer(repository.Name, layerDgst) content, err = r.blobFetcher.FetchLayer(artifact.RepositoryName, layerDgst)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -15,13 +15,11 @@
package chart package chart
import ( import (
"github.com/goharbor/harbor/src/common/models"
ierror "github.com/goharbor/harbor/src/internal/error" ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/artifact"
chartserver "github.com/goharbor/harbor/src/pkg/chart" chartserver "github.com/goharbor/harbor/src/pkg/chart"
"github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob" "github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob"
"github.com/goharbor/harbor/src/testing/pkg/chart" "github.com/goharbor/harbor/src/testing/pkg/chart"
"github.com/goharbor/harbor/src/testing/pkg/repository"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
"testing" "testing"
@ -30,17 +28,14 @@ import (
type resolverTestSuite struct { type resolverTestSuite struct {
suite.Suite suite.Suite
resolver *resolver resolver *resolver
repoMgr *repository.FakeManager
blobFetcher *blob.FakeFetcher blobFetcher *blob.FakeFetcher
chartOptr *chart.FakeOpertaor chartOptr *chart.FakeOpertaor
} }
func (r *resolverTestSuite) SetupTest() { func (r *resolverTestSuite) SetupTest() {
r.repoMgr = &repository.FakeManager{}
r.blobFetcher = &blob.FakeFetcher{} r.blobFetcher = &blob.FakeFetcher{}
r.chartOptr = &chart.FakeOpertaor{} r.chartOptr = &chart.FakeOpertaor{}
r.resolver = &resolver{ r.resolver = &resolver{
repoMgr: r.repoMgr,
blobFetcher: r.blobFetcher, blobFetcher: r.blobFetcher,
chartOperator: r.chartOptr, chartOperator: r.chartOptr,
} }
@ -92,11 +87,9 @@ func (r *resolverTestSuite) TestResolveMetadata() {
"appVersion": "1.8.2" "appVersion": "1.8.2"
}` }`
artifact := &artifact.Artifact{} artifact := &artifact.Artifact{}
r.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
r.blobFetcher.On("FetchLayer").Return([]byte(config), nil) r.blobFetcher.On("FetchLayer").Return([]byte(config), nil)
err := r.resolver.ResolveMetadata(nil, []byte(content), artifact) err := r.resolver.ResolveMetadata(nil, []byte(content), artifact)
r.Require().Nil(err) r.Require().Nil(err)
r.repoMgr.AssertExpectations(r.T())
r.blobFetcher.AssertExpectations(r.T()) r.blobFetcher.AssertExpectations(r.T())
r.Assert().Equal("1.1.2", artifact.ExtraAttrs["version"].(string)) r.Assert().Equal("1.1.2", artifact.ExtraAttrs["version"].(string))
r.Assert().Equal("1.8.2", artifact.ExtraAttrs["appVersion"].(string)) r.Assert().Equal("1.8.2", artifact.ExtraAttrs["appVersion"].(string))
@ -158,7 +151,6 @@ func (r *resolverTestSuite) TestResolveAddition() {
} }
artifact := &artifact.Artifact{} artifact := &artifact.Artifact{}
r.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
r.blobFetcher.On("FetchManifest").Return("", []byte(chartManifest), nil) r.blobFetcher.On("FetchManifest").Return("", []byte(chartManifest), nil)
r.blobFetcher.On("FetchLayer").Return([]byte(chartYaml), nil) r.blobFetcher.On("FetchLayer").Return([]byte(chartYaml), nil)
r.chartOptr.On("GetDetails").Return(chartDetails, nil) r.chartOptr.On("GetDetails").Return(chartDetails, nil)

View File

@ -23,7 +23,6 @@ import (
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
ierror "github.com/goharbor/harbor/src/internal/error" ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/repository"
v1 "github.com/opencontainers/image-spec/specs-go/v1" v1 "github.com/opencontainers/image-spec/specs-go/v1"
) )
@ -35,7 +34,6 @@ const (
func init() { func init() {
resolver := &resolver{ resolver := &resolver{
repoMgr: repository.Mgr,
argMgr: artifact.Mgr, argMgr: artifact.Mgr,
blobFetcher: blob.Fcher, blobFetcher: blob.Fcher,
} }
@ -50,7 +48,6 @@ func init() {
} }
type resolver struct { type resolver struct {
repoMgr repository.Manager
argMgr artifact.Manager argMgr artifact.Manager
blobFetcher blob.Fetcher blobFetcher blob.Fetcher
} }
@ -65,7 +62,7 @@ func (r *resolver) ResolveMetadata(ctx context.Context, manifest []byte, art *ar
for _, mani := range index.Manifests { for _, mani := range index.Manifests {
digest := mani.Digest.String() digest := mani.Digest.String()
// make sure the child artifact exist // make sure the child artifact exist
ar, err := r.argMgr.GetByDigest(ctx, art.RepositoryID, digest) ar, err := r.argMgr.GetByDigest(ctx, art.RepositoryName, digest)
if err != nil { if err != nil {
return err return err
} }
@ -84,12 +81,8 @@ func (r *resolver) ResolveMetadata(ctx context.Context, manifest []byte, art *ar
} }
// resolve the config of CNAB // resolve the config of CNAB
repository, err := r.repoMgr.Get(ctx, art.RepositoryID)
if err != nil {
return err
}
// get the manifest that the config layer is referenced by // get the manifest that the config layer is referenced by
_, cfgMani, err := r.blobFetcher.FetchManifest(repository.Name, cfgManiDgt) _, cfgMani, err := r.blobFetcher.FetchManifest(art.RepositoryName, cfgManiDgt)
if err != nil { if err != nil {
return err return err
} }
@ -99,7 +92,7 @@ func (r *resolver) ResolveMetadata(ctx context.Context, manifest []byte, art *ar
} }
cfgDgt := m.Config.Digest.String() cfgDgt := m.Config.Digest.String()
// get the config layer // get the config layer
cfg, err := r.blobFetcher.FetchLayer(repository.Name, cfgDgt) cfg, err := r.blobFetcher.FetchLayer(art.RepositoryName, cfgDgt)
if err != nil { if err != nil {
return err return err
} }

View File

@ -15,12 +15,10 @@
package cnab package cnab
import ( import (
"github.com/goharbor/harbor/src/common/models"
ierror "github.com/goharbor/harbor/src/internal/error" ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob" "github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob"
testingartifact "github.com/goharbor/harbor/src/testing/pkg/artifact" testingartifact "github.com/goharbor/harbor/src/testing/pkg/artifact"
"github.com/goharbor/harbor/src/testing/pkg/repository"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"testing" "testing"
) )
@ -28,17 +26,14 @@ import (
type resolverTestSuite struct { type resolverTestSuite struct {
suite.Suite suite.Suite
resolver *resolver resolver *resolver
repoMgr *repository.FakeManager
artMgr *testingartifact.FakeManager artMgr *testingartifact.FakeManager
blobFetcher *blob.FakeFetcher blobFetcher *blob.FakeFetcher
} }
func (r *resolverTestSuite) SetupTest() { func (r *resolverTestSuite) SetupTest() {
r.repoMgr = &repository.FakeManager{}
r.artMgr = &testingartifact.FakeManager{} r.artMgr = &testingartifact.FakeManager{}
r.blobFetcher = &blob.FakeFetcher{} r.blobFetcher = &blob.FakeFetcher{}
r.resolver = &resolver{ r.resolver = &resolver{
repoMgr: r.repoMgr,
argMgr: r.artMgr, argMgr: r.artMgr,
blobFetcher: r.blobFetcher, blobFetcher: r.blobFetcher,
} }
@ -115,7 +110,6 @@ func (r *resolverTestSuite) TestResolveMetadata() {
}` }`
art := &artifact.Artifact{} art := &artifact.Artifact{}
r.artMgr.On("GetByDigest").Return(&artifact.Artifact{ID: 1}, nil) r.artMgr.On("GetByDigest").Return(&artifact.Artifact{ID: 1}, nil)
r.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
r.blobFetcher.On("FetchManifest").Return("", []byte(manifest), nil) r.blobFetcher.On("FetchManifest").Return("", []byte(manifest), nil)
r.blobFetcher.On("FetchLayer").Return([]byte(config), nil) r.blobFetcher.On("FetchLayer").Return([]byte(config), nil)
err := r.resolver.ResolveMetadata(nil, []byte(index), art) err := r.resolver.ResolveMetadata(nil, []byte(index), art)

View File

@ -58,7 +58,7 @@ func (i *indexResolver) ResolveMetadata(ctx context.Context, manifest []byte, ar
for _, mani := range index.Manifests { for _, mani := range index.Manifests {
digest := mani.Digest.String() digest := mani.Digest.String()
// make sure the child artifact exist // make sure the child artifact exist
ar, err := i.artMgr.GetByDigest(ctx, art.RepositoryID, digest) ar, err := i.artMgr.GetByDigest(ctx, art.RepositoryName, digest)
if err != nil { if err != nil {
return err return err
} }

View File

@ -24,7 +24,6 @@ import (
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
ierror "github.com/goharbor/harbor/src/internal/error" ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/repository"
"github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/image-spec/specs-go/v1"
) )
@ -37,7 +36,6 @@ const (
func init() { func init() {
rslver := &manifestV2Resolver{ rslver := &manifestV2Resolver{
repoMgr: repository.Mgr,
blobFetcher: blob.Fcher, blobFetcher: blob.Fcher,
} }
mediaTypes := []string{ mediaTypes := []string{
@ -56,21 +54,16 @@ func init() {
// manifestV2Resolver resolve artifact with OCI manifest and docker v2 manifest // manifestV2Resolver resolve artifact with OCI manifest and docker v2 manifest
type manifestV2Resolver struct { type manifestV2Resolver struct {
repoMgr repository.Manager
blobFetcher blob.Fetcher blobFetcher blob.Fetcher
} }
func (m *manifestV2Resolver) ResolveMetadata(ctx context.Context, content []byte, artifact *artifact.Artifact) error { func (m *manifestV2Resolver) ResolveMetadata(ctx context.Context, content []byte, artifact *artifact.Artifact) error {
repository, err := m.repoMgr.Get(ctx, artifact.RepositoryID)
if err != nil {
return err
}
manifest := &v1.Manifest{} manifest := &v1.Manifest{}
if err := json.Unmarshal(content, manifest); err != nil { if err := json.Unmarshal(content, manifest); err != nil {
return err return err
} }
digest := manifest.Config.Digest.String() digest := manifest.Config.Digest.String()
layer, err := m.blobFetcher.FetchLayer(repository.Name, digest) layer, err := m.blobFetcher.FetchLayer(artifact.RepositoryName, digest)
if err != nil { if err != nil {
return err return err
} }
@ -93,11 +86,7 @@ func (m *manifestV2Resolver) ResolveAddition(ctx context.Context, artifact *arti
return nil, ierror.New(nil).WithCode(ierror.BadRequestCode). return nil, ierror.New(nil).WithCode(ierror.BadRequestCode).
WithMessage("addition %s isn't supported for %s(manifest version 2)", addition, ArtifactTypeImage) WithMessage("addition %s isn't supported for %s(manifest version 2)", addition, ArtifactTypeImage)
} }
repository, err := m.repoMgr.Get(ctx, artifact.RepositoryID) _, content, err := m.blobFetcher.FetchManifest(artifact.RepositoryName, artifact.Digest)
if err != nil {
return nil, err
}
_, content, err := m.blobFetcher.FetchManifest(repository.Name, artifact.Digest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -105,7 +94,7 @@ func (m *manifestV2Resolver) ResolveAddition(ctx context.Context, artifact *arti
if err := json.Unmarshal(content, manifest); err != nil { if err := json.Unmarshal(content, manifest); err != nil {
return nil, err return nil, err
} }
content, err = m.blobFetcher.FetchLayer(repository.Name, manifest.Config.Digest.String()) content, err = m.blobFetcher.FetchLayer(artifact.RepositoryName, manifest.Config.Digest.String())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -15,11 +15,9 @@
package image package image
import ( import (
"github.com/goharbor/harbor/src/common/models"
ierror "github.com/goharbor/harbor/src/internal/error" ierror "github.com/goharbor/harbor/src/internal/error"
"github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob" "github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob"
"github.com/goharbor/harbor/src/testing/pkg/repository"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"testing" "testing"
) )
@ -123,15 +121,12 @@ var (
type manifestV2ResolverTestSuite struct { type manifestV2ResolverTestSuite struct {
suite.Suite suite.Suite
resolver *manifestV2Resolver resolver *manifestV2Resolver
repoMgr *repository.FakeManager
blobFetcher *blob.FakeFetcher blobFetcher *blob.FakeFetcher
} }
func (m *manifestV2ResolverTestSuite) SetupTest() { func (m *manifestV2ResolverTestSuite) SetupTest() {
m.repoMgr = &repository.FakeManager{}
m.blobFetcher = &blob.FakeFetcher{} m.blobFetcher = &blob.FakeFetcher{}
m.resolver = &manifestV2Resolver{ m.resolver = &manifestV2Resolver{
repoMgr: m.repoMgr,
blobFetcher: m.blobFetcher, blobFetcher: m.blobFetcher,
} }
@ -139,11 +134,9 @@ func (m *manifestV2ResolverTestSuite) SetupTest() {
func (m *manifestV2ResolverTestSuite) TestResolveMetadata() { func (m *manifestV2ResolverTestSuite) TestResolveMetadata() {
artifact := &artifact.Artifact{} artifact := &artifact.Artifact{}
m.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
m.blobFetcher.On("FetchLayer").Return([]byte(config), nil) m.blobFetcher.On("FetchLayer").Return([]byte(config), nil)
err := m.resolver.ResolveMetadata(nil, []byte(manifest), artifact) err := m.resolver.ResolveMetadata(nil, []byte(manifest), artifact)
m.Require().Nil(err) m.Require().Nil(err)
m.repoMgr.AssertExpectations(m.T())
m.blobFetcher.AssertExpectations(m.T()) m.blobFetcher.AssertExpectations(m.T())
m.Assert().Equal("amd64", artifact.ExtraAttrs["architecture"].(string)) m.Assert().Equal("amd64", artifact.ExtraAttrs["architecture"].(string))
m.Assert().Equal("linux", artifact.ExtraAttrs["os"].(string)) m.Assert().Equal("linux", artifact.ExtraAttrs["os"].(string))
@ -156,7 +149,6 @@ func (m *manifestV2ResolverTestSuite) TestResolveAddition() {
// build history // build history
artifact := &artifact.Artifact{} artifact := &artifact.Artifact{}
m.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
m.blobFetcher.On("FetchManifest").Return("", []byte(manifest), nil) m.blobFetcher.On("FetchManifest").Return("", []byte(manifest), nil)
m.blobFetcher.On("FetchLayer").Return([]byte(config), nil) m.blobFetcher.On("FetchLayer").Return([]byte(config), nil)
addition, err := m.resolver.ResolveAddition(nil, artifact, AdditionTypeBuildHistory) addition, err := m.resolver.ResolveAddition(nil, artifact, AdditionTypeBuildHistory)

View File

@ -20,7 +20,6 @@ import (
"github.com/goharbor/harbor/src/api/artifact/abstractor" "github.com/goharbor/harbor/src/api/artifact/abstractor"
"github.com/goharbor/harbor/src/api/artifact/abstractor/resolver" "github.com/goharbor/harbor/src/api/artifact/abstractor/resolver"
"github.com/goharbor/harbor/src/api/artifact/descriptor" "github.com/goharbor/harbor/src/api/artifact/descriptor"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils" "github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/internal" "github.com/goharbor/harbor/src/internal"
"github.com/goharbor/harbor/src/pkg/art" "github.com/goharbor/harbor/src/pkg/art"
@ -61,7 +60,7 @@ type Controller interface {
// creates it if it doesn't exist. If tags are provided, ensure they exist // creates it if it doesn't exist. If tags are provided, ensure they exist
// and are attached to the artifact. If the tags don't exist, create them first. // and are attached to the artifact. If the tags don't exist, create them first.
// The "created" will be set as true when the artifact is created // The "created" will be set as true when the artifact is created
Ensure(ctx context.Context, repositoryID int64, digest string, tags ...string) (created bool, id int64, err error) Ensure(ctx context.Context, repository, digest string, tags ...string) (created bool, id int64, err error)
// Count returns the total count of artifacts according to the query. // Count returns the total count of artifacts according to the query.
// The artifacts that referenced by others and without tags are not counted // The artifacts that referenced by others and without tags are not counted
Count(ctx context.Context, query *q.Query) (total int64, err error) Count(ctx context.Context, query *q.Query) (total int64, err error)
@ -75,8 +74,8 @@ type Controller interface {
GetByReference(ctx context.Context, repository, reference string, option *Option) (artifact *Artifact, err error) GetByReference(ctx context.Context, repository, reference string, option *Option) (artifact *Artifact, err error)
// Delete the artifact specified by ID. All tags attached to the artifact are deleted as well // Delete the artifact specified by ID. All tags attached to the artifact are deleted as well
Delete(ctx context.Context, id int64) (err error) Delete(ctx context.Context, id int64) (err error)
// Copy the artifact whose ID is specified by "srcArtID" into the repository specified by "dstRepoID" // Copy the artifact specified by "srcRepo" and "reference" into the repository specified by "dstRepo"
Copy(ctx context.Context, srcArtID, dstRepoID int64) (id int64, err error) Copy(ctx context.Context, srcRepo, reference, dstRepo string) (id int64, err error)
// ListTags lists the tags according to the query, specify the properties returned with option // ListTags lists the tags according to the query, specify the properties returned with option
ListTags(ctx context.Context, query *q.Query, option *TagOption) (tags []*Tag, err error) ListTags(ctx context.Context, query *q.Query, option *TagOption) (tags []*Tag, err error)
// CreateTag creates a tag // CreateTag creates a tag
@ -127,46 +126,47 @@ type controller struct {
regCli registry.Client regCli registry.Client
} }
func (c *controller) Ensure(ctx context.Context, repositoryID int64, digest string, tags ...string) (bool, int64, error) { func (c *controller) Ensure(ctx context.Context, repository, digest string, tags ...string) (bool, int64, error) {
created, id, err := c.ensureArtifact(ctx, repositoryID, digest) created, artifact, err := c.ensureArtifact(ctx, repository, digest)
if err != nil { if err != nil {
return false, 0, err return false, 0, err
} }
for _, tag := range tags { for _, tag := range tags {
if err = c.ensureTag(ctx, repositoryID, id, tag); err != nil { if err = c.ensureTag(ctx, artifact.RepositoryID, artifact.ID, tag); err != nil {
return false, 0, err return false, 0, err
} }
} }
return created, id, nil return created, artifact.ID, nil
} }
// ensure the artifact exists under the repository, create it if doesn't exist. // ensure the artifact exists under the repository, create it if doesn't exist.
func (c *controller) ensureArtifact(ctx context.Context, repositoryID int64, digest string) (bool, int64, error) { func (c *controller) ensureArtifact(ctx context.Context, repository, digest string) (bool, *artifact.Artifact, error) {
art, err := c.artMgr.GetByDigest(ctx, repositoryID, digest) art, err := c.artMgr.GetByDigest(ctx, repository, digest)
// the artifact already exists under the repository, return directly // the artifact already exists under the repository, return directly
if err == nil { if err == nil {
return false, art.ID, nil return false, art, nil
} }
// got other error when get the artifact, return the error // got other error when get the artifact, return the error
if !ierror.IsErr(err, ierror.NotFoundCode) { if !ierror.IsErr(err, ierror.NotFoundCode) {
return false, 0, err return false, nil, err
} }
// the artifact doesn't exist under the repository, create it first // the artifact doesn't exist under the repository, create it first
repository, err := c.repoMgr.Get(ctx, repositoryID) repo, err := c.repoMgr.GetByName(ctx, repository)
if err != nil { if err != nil {
return false, 0, err return false, nil, err
} }
artifact := &artifact.Artifact{ artifact := &artifact.Artifact{
ProjectID: repository.ProjectID, ProjectID: repo.ProjectID,
RepositoryID: repositoryID, RepositoryID: repo.RepositoryID,
Digest: digest, RepositoryName: repository,
PushTime: time.Now(), Digest: digest,
PushTime: time.Now(),
} }
// abstract the metadata for the artifact // abstract the metadata for the artifact
if err = c.abstractor.AbstractMetadata(ctx, artifact); err != nil { if err = c.abstractor.AbstractMetadata(ctx, artifact); err != nil {
return false, 0, err return false, nil, err
} }
// populate the artifact type // populate the artifact type
@ -177,15 +177,16 @@ func (c *controller) ensureArtifact(ctx context.Context, repositoryID int64, dig
if err != nil { if err != nil {
// if got conflict error, try to get the artifact again // if got conflict error, try to get the artifact again
if ierror.IsConflictErr(err) { if ierror.IsConflictErr(err) {
art, err = c.artMgr.GetByDigest(ctx, repositoryID, digest) art, err = c.artMgr.GetByDigest(ctx, repository, digest)
if err == nil { if err == nil {
return false, art.ID, nil return false, art, nil
} }
return false, 0, err return false, nil, err
} }
return false, 0, err return false, nil, err
} }
return true, id, nil artifact.ID = id
return true, artifact, nil
} }
func (c *controller) ensureTag(ctx context.Context, repositoryID, artifactID int64, name string) error { func (c *controller) ensureTag(ctx context.Context, repositoryID, artifactID int64, name string) error {
@ -236,10 +237,6 @@ func (c *controller) List(ctx context.Context, query *q.Query, option *Option) (
return nil, err return nil, err
} }
if err := c.populateRepositoryName(ctx, arts...); err != nil {
return nil, err
}
var artifacts []*Artifact var artifacts []*Artifact
for _, art := range arts { for _, art := range arts {
artifacts = append(artifacts, c.assembleArtifact(ctx, art, option)) artifacts = append(artifacts, c.assembleArtifact(ctx, art, option))
@ -265,11 +262,7 @@ func (c *controller) GetByReference(ctx context.Context, repository, reference s
} }
func (c *controller) getByDigest(ctx context.Context, repository, digest string, option *Option) (*Artifact, error) { func (c *controller) getByDigest(ctx context.Context, repository, digest string, option *Option) (*Artifact, error) {
repo, err := c.repoMgr.GetByName(ctx, repository) art, err := c.artMgr.GetByDigest(ctx, repository, digest)
if err != nil {
return nil, err
}
art, err := c.artMgr.GetByDigest(ctx, repo.RepositoryID, digest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -377,14 +370,10 @@ func (c *controller) deleteDeeply(ctx context.Context, id int64, isRoot bool) er
return err return err
} }
repo, err := c.repoMgr.Get(ctx, art.RepositoryID)
if err != nil && !ierror.IsErr(err, ierror.NotFoundCode) {
return err
}
_, err = c.artrashMgr.Create(ctx, &model.ArtifactTrash{ _, err = c.artrashMgr.Create(ctx, &model.ArtifactTrash{
MediaType: art.MediaType, MediaType: art.MediaType,
ManifestMediaType: art.ManifestMediaType, ManifestMediaType: art.ManifestMediaType,
RepositoryName: repo.Name, RepositoryName: art.RepositoryName,
Digest: art.Digest, Digest: art.Digest,
}) })
if err != nil && !ierror.IsErr(err, ierror.ConflictCode) { if err != nil && !ierror.IsErr(err, ierror.ConflictCode) {
@ -395,61 +384,63 @@ func (c *controller) deleteDeeply(ctx context.Context, id int64, isRoot bool) er
return nil return nil
} }
func (c *controller) Copy(ctx context.Context, srcArtID, dstRepoID int64) (int64, error) { func (c *controller) Copy(ctx context.Context, srcRepo, reference, dstRepo string) (int64, error) {
srcArt, err := c.Get(ctx, srcArtID, &Option{WithTag: true}) return c.copyDeeply(ctx, srcRepo, reference, dstRepo, true)
if err != nil { }
return 0, err
// as we call the docker registry APIs in the registry client directly,
// this bypass our own logic(ensure, fire event, etc.) inside the registry handlers,
// these logic must be covered explicitly here.
// "copyDeeply" iterates the child artifacts and copy them first
func (c *controller) copyDeeply(ctx context.Context, srcRepo, reference, dstRepo string, isRoot bool) (int64, error) {
var option *Option
// only get the tags of the root parent
if isRoot {
option = &Option{WithTag: true}
} }
srcRepo, err := c.repoMgr.Get(ctx, srcArt.RepositoryID)
if err != nil { srcArt, err := c.GetByReference(ctx, srcRepo, reference, option)
return 0, err
}
dstRepo, err := c.repoMgr.Get(ctx, dstRepoID)
if err != nil { if err != nil {
return 0, err return 0, err
} }
_, err = c.artMgr.GetByDigest(ctx, dstRepoID, srcArt.Digest) digest := srcArt.Digest
// the artifact already exists in the destination repository
// check the existence of artifact in the destination repository
dstArt, err := c.GetByReference(ctx, dstRepo, digest, option)
if err == nil { if err == nil {
return 0, ierror.New(nil).WithCode(ierror.ConflictCode). // return conflict error if the root parent artifact already exists under the destination repository
WithMessage("the artifact %s already exists under the repository %s", if isRoot {
srcArt.Digest, dstRepo.Name) return 0, ierror.New(nil).WithCode(ierror.ConflictCode).
WithMessage("the artifact %s@%s already exists", dstRepo, digest)
}
// the child artifact already under the destination repository, skip
return dstArt.ID, nil
} }
if !ierror.IsErr(err, ierror.NotFoundCode) { if !ierror.IsErr(err, ierror.NotFoundCode) {
return 0, err return 0, err
} }
// the artifact doesn't exist under the destination repository, continue to copy
// copy child artifacts if contains any
for _, reference := range srcArt.References {
if _, err = c.copyDeeply(ctx, srcRepo, reference.ChildDigest, dstRepo, false); err != nil {
return 0, err
}
}
// copy the parent artifact into the backend docker registry
if err := c.regCli.Copy(srcRepo, digest, dstRepo, digest, false); err != nil {
return 0, err
}
// only copy the tags of outermost artifact // only copy the tags of outermost artifact
var tags []string var tags []string
for _, tag := range srcArt.Tags { for _, tag := range srcArt.Tags {
tags = append(tags, tag.Name) tags = append(tags, tag.Name)
} }
return c.copyDeeply(ctx, srcRepo, srcArt, dstRepo, tags...) // ensure the parent artifact exist in the database
} _, id, err := c.Ensure(ctx, dstRepo, digest, tags...)
// as we call the docker registry APIs in the registry client directly,
// this bypass our own logic(ensure, fire event, etc.) inside the registry handlers,
// these logic must be covered explicitly here.
// "copyDeeply" iterates the child artifacts and copy them first
func (c *controller) copyDeeply(ctx context.Context, srcRepo *models.RepoRecord, srcArt *Artifact,
dstRepo *models.RepoRecord, tags ...string) (int64, error) {
// copy child artifacts if contains any
for _, reference := range srcArt.References {
childArt, err := c.Get(ctx, reference.ChildID, nil)
if err != nil {
return 0, err
}
if _, err = c.copyDeeply(ctx, srcRepo, childArt, dstRepo); err != nil {
return 0, err
}
}
// copy the parent artifact
if err := c.regCli.Copy(srcRepo.Name, srcArt.Digest,
dstRepo.Name, srcArt.Digest, false); err != nil {
return 0, err
}
_, id, err := c.Ensure(ctx, dstRepo.RepositoryID, srcArt.Digest, tags...)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -468,7 +459,11 @@ func (c *controller) ListTags(ctx context.Context, query *q.Query, option *TagOp
} }
var tags []*Tag var tags []*Tag
for _, tg := range tgs { for _, tg := range tgs {
tags = append(tags, c.assembleTag(ctx, tg, option)) art, err := c.artMgr.Get(ctx, tg.ArtifactID)
if err != nil {
return nil, err
}
tags = append(tags, c.assembleTag(ctx, art, tg, option))
} }
return tags, nil return tags, nil
} }
@ -517,19 +512,9 @@ func (c *controller) assembleArtifact(ctx context.Context, art *artifact.Artifac
artifact := &Artifact{ artifact := &Artifact{
Artifact: *art, Artifact: *art,
} }
if artifact.RepositoryName == "" {
repo, err := c.repoMgr.Get(ctx, artifact.RepositoryID)
if err != nil {
log.Errorf("get repository %d failed, error: %v", artifact.RepositoryID, err)
return artifact
}
artifact.RepositoryName = repo.Name
}
// populate addition links // populate addition links
c.populateAdditionLinks(ctx, artifact) c.populateAdditionLinks(ctx, artifact)
if option == nil { if option == nil {
return artifact return artifact
} }
@ -539,39 +524,9 @@ func (c *controller) assembleArtifact(ctx context.Context, art *artifact.Artifac
if option.WithLabel { if option.WithLabel {
c.populateLabels(ctx, artifact) c.populateLabels(ctx, artifact)
} }
// populate addition links
c.populateAdditionLinks(ctx, artifact)
return artifact return artifact
} }
func (c *controller) populateRepositoryName(ctx context.Context, artifacts ...*artifact.Artifact) error {
var ids []int64
for _, artifact := range artifacts {
ids = append(ids, artifact.RepositoryID)
}
repositories, err := c.repoMgr.List(ctx, &q.Query{Keywords: map[string]interface{}{"repository_id__in": ids}})
if err != nil {
return err
}
mp := make(map[int64]string, len(repositories))
for _, repository := range repositories {
mp[repository.RepositoryID] = repository.Name
}
for _, artifact := range artifacts {
repositoryName, ok := mp[artifact.RepositoryID]
if !ok {
return ierror.NotFoundError(nil).WithMessage("repository %d not found", artifact.RepositoryID)
}
artifact.RepositoryName = repositoryName
}
return nil
}
func (c *controller) populateTags(ctx context.Context, art *Artifact, option *TagOption) { func (c *controller) populateTags(ctx context.Context, art *Artifact, option *TagOption) {
tags, err := c.tagMgr.List(ctx, &q.Query{ tags, err := c.tagMgr.List(ctx, &q.Query{
Keywords: map[string]interface{}{ Keywords: map[string]interface{}{
@ -583,46 +538,51 @@ func (c *controller) populateTags(ctx context.Context, art *Artifact, option *Ta
return return
} }
for _, tag := range tags { for _, tag := range tags {
art.Tags = append(art.Tags, c.assembleTag(ctx, tag, option)) art.Tags = append(art.Tags, c.assembleTag(ctx, &art.Artifact, tag, option))
} }
} }
// assemble several part into a single tag // assemble several part into a single tag
func (c *controller) assembleTag(ctx context.Context, tag *tm.Tag, option *TagOption) *Tag { func (c *controller) assembleTag(ctx context.Context, art *artifact.Artifact, tag *tm.Tag, option *TagOption) *Tag {
t := &Tag{ t := &Tag{
Tag: *tag, Tag: *tag,
} }
if option == nil { if option == nil {
return t return t
} }
repo, err := c.repoMgr.Get(ctx, tag.RepositoryID)
if err != nil {
log.Errorf("Failed to get repo for tag: %s, error: %v", tag.Name, err)
return t
}
if option.WithImmutableStatus { if option.WithImmutableStatus {
c.populateImmutableStatus(ctx, t) c.populateImmutableStatus(ctx, art, t)
} }
if option.WithSignature { if option.WithSignature {
if a, err := c.artMgr.Get(ctx, t.ArtifactID); err != nil { c.populateTagSignature(ctx, art, t, option)
log.Errorf("Failed to get artifact for tag: %s, error: %v, skip populating signature", t.Name, err)
} else {
c.populateTagSignature(ctx, repo.Name, t, a.Digest, option)
}
} }
return t return t
} }
func (c *controller) populateTagSignature(ctx context.Context, repo string, tag *Tag, digest string, option *TagOption) { func (c *controller) populateImmutableStatus(ctx context.Context, artifact *artifact.Artifact, tag *Tag) {
_, repoName := utils.ParseRepository(artifact.RepositoryName)
matched, err := c.immutableMtr.Match(artifact.ProjectID, art.Candidate{
Repository: repoName,
Tags: []string{tag.Name},
NamespaceID: artifact.ProjectID,
})
if err != nil {
log.Error(err)
return
}
tag.Immutable = matched
}
func (c *controller) populateTagSignature(ctx context.Context, artifact *artifact.Artifact, tag *Tag, option *TagOption) {
if option.SignatureChecker == nil { if option.SignatureChecker == nil {
chk, err := signature.GetManager().GetCheckerByRepo(ctx, repo) chk, err := signature.GetManager().GetCheckerByRepo(ctx, artifact.RepositoryName)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return return
} }
option.SignatureChecker = chk option.SignatureChecker = chk
} }
tag.Signed = option.SignatureChecker.IsTagSigned(tag.Name, digest) tag.Signed = option.SignatureChecker.IsTagSigned(tag.Name, artifact.Digest)
} }
func (c *controller) populateLabels(ctx context.Context, art *Artifact) { func (c *controller) populateLabels(ctx context.Context, art *Artifact) {
@ -634,25 +594,6 @@ func (c *controller) populateLabels(ctx context.Context, art *Artifact) {
art.Labels = labels art.Labels = labels
} }
func (c *controller) populateImmutableStatus(ctx context.Context, tag *Tag) {
repo, err := c.repoMgr.Get(ctx, tag.RepositoryID)
if err != nil {
log.Error(err)
return
}
_, repoName := utils.ParseRepository(repo.Name)
matched, err := c.immutableMtr.Match(repo.ProjectID, art.Candidate{
Repository: repoName,
Tags: []string{tag.Name},
NamespaceID: repo.ProjectID,
})
if err != nil {
log.Error(err)
return
}
tag.Immutable = matched
}
func (c *controller) populateAdditionLinks(ctx context.Context, artifact *Artifact) { func (c *controller) populateAdditionLinks(ctx context.Context, artifact *Artifact) {
types := descriptor.ListAdditionTypes(artifact.MediaType) types := descriptor.ListAdditionTypes(artifact.MediaType)
if len(types) > 0 { if len(types) > 0 {

View File

@ -39,6 +39,8 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
// TODO find another way to test artifact controller, it's hard to maintain currently
type fakeAbstractor struct { type fakeAbstractor struct {
mock.Mock mock.Mock
} }
@ -107,6 +109,13 @@ func (c *controllerTestSuite) SetupTest() {
} }
func (c *controllerTestSuite) TestAssembleTag() { func (c *controllerTestSuite) TestAssembleTag() {
art := &artifact.Artifact{
ID: 1,
ProjectID: 1,
RepositoryID: 1,
RepositoryName: "library/hello-world",
Digest: "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180",
}
tg := &tag.Tag{ tg := &tag.Tag{
ID: 1, ID: 1,
RepositoryID: 1, RepositoryID: 1,
@ -119,13 +128,8 @@ func (c *controllerTestSuite) TestAssembleTag() {
WithImmutableStatus: true, WithImmutableStatus: true,
} }
c.repoMgr.On("Get").Return(&models.RepoRecord{
ProjectID: 1,
Name: "hello-world",
}, nil)
c.immutableMtr.On("Match").Return(true, nil) c.immutableMtr.On("Match").Return(true, nil)
tag := c.ctl.assembleTag(nil, tg, option) tag := c.ctl.assembleTag(nil, art, tg, option)
c.Require().NotNil(tag) c.Require().NotNil(tag)
c.Equal(tag.ID, tg.ID) c.Equal(tag.ID, tg.ID)
c.Equal(true, tag.Immutable) c.Equal(true, tag.Immutable)
@ -154,9 +158,6 @@ func (c *controllerTestSuite) TestAssembleArtifact() {
PullTime: time.Now(), PullTime: time.Now(),
} }
c.tagMgr.On("List").Return([]*tag.Tag{tg}, nil) c.tagMgr.On("List").Return([]*tag.Tag{tg}, nil)
c.repoMgr.On("Get").Return(&models.RepoRecord{
Name: "library/hello-world",
}, nil)
ctx := internal.SetAPIVersion(nil, "2.0") ctx := internal.SetAPIVersion(nil, "2.0")
lb := &models.Label{ lb := &models.Label{
ID: 1, ID: 1,
@ -185,25 +186,25 @@ func (c *controllerTestSuite) TestEnsureArtifact() {
c.artMgr.On("GetByDigest").Return(&artifact.Artifact{ c.artMgr.On("GetByDigest").Return(&artifact.Artifact{
ID: 1, ID: 1,
}, nil) }, nil)
created, id, err := c.ctl.ensureArtifact(nil, 1, digest) created, art, err := c.ctl.ensureArtifact(nil, "library/hello-world", digest)
c.Require().Nil(err) c.Require().Nil(err)
c.False(created) c.False(created)
c.Equal(int64(1), id) c.Equal(int64(1), art.ID)
// reset the mock // reset the mock
c.SetupTest() c.SetupTest()
// the artifact doesn't exist // the artifact doesn't exist
c.repoMgr.On("Get").Return(&models.RepoRecord{ c.repoMgr.On("GetByName").Return(&models.RepoRecord{
ProjectID: 1, ProjectID: 1,
}, nil) }, nil)
c.artMgr.On("GetByDigest").Return(nil, ierror.NotFoundError(nil)) c.artMgr.On("GetByDigest").Return(nil, ierror.NotFoundError(nil))
c.artMgr.On("Create").Return(1, nil) c.artMgr.On("Create").Return(1, nil)
c.abstractor.On("AbstractMetadata").Return(nil) c.abstractor.On("AbstractMetadata").Return(nil)
created, id, err = c.ctl.ensureArtifact(nil, 1, digest) created, art, err = c.ctl.ensureArtifact(nil, "library/hello-world", digest)
c.Require().Nil(err) c.Require().Nil(err)
c.True(created) c.True(created)
c.Equal(int64(1), id) c.Equal(int64(1), art.ID)
} }
func (c *controllerTestSuite) TestEnsureTag() { func (c *controllerTestSuite) TestEnsureTag() {
@ -252,7 +253,7 @@ func (c *controllerTestSuite) TestEnsure() {
digest := "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180" digest := "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180"
// both the artifact and the tag don't exist // both the artifact and the tag don't exist
c.repoMgr.On("Get").Return(&models.RepoRecord{ c.repoMgr.On("GetByName").Return(&models.RepoRecord{
ProjectID: 1, ProjectID: 1,
}, nil) }, nil)
c.artMgr.On("GetByDigest").Return(nil, ierror.NotFoundError(nil)) c.artMgr.On("GetByDigest").Return(nil, ierror.NotFoundError(nil))
@ -260,7 +261,7 @@ func (c *controllerTestSuite) TestEnsure() {
c.tagMgr.On("List").Return([]*tag.Tag{}, nil) c.tagMgr.On("List").Return([]*tag.Tag{}, nil)
c.tagMgr.On("Create").Return(1, nil) c.tagMgr.On("Create").Return(1, nil)
c.abstractor.On("AbstractMetadata").Return(nil) c.abstractor.On("AbstractMetadata").Return(nil)
_, id, err := c.ctl.Ensure(nil, 1, digest, "latest") _, id, err := c.ctl.Ensure(nil, "library/hello-world", digest, "latest")
c.Require().Nil(err) c.Require().Nil(err)
c.repoMgr.AssertExpectations(c.T()) c.repoMgr.AssertExpectations(c.T())
c.artMgr.AssertExpectations(c.T()) c.artMgr.AssertExpectations(c.T())
@ -508,7 +509,12 @@ func (c *controllerTestSuite) TestDeleteDeeply() {
func (c *controllerTestSuite) TestCopy() { func (c *controllerTestSuite) TestCopy() {
c.artMgr.On("Get").Return(&artifact.Artifact{ c.artMgr.On("Get").Return(&artifact.Artifact{
ID: 1, ID: 1,
Digest: "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180",
}, nil)
c.repoMgr.On("GetByName").Return(&models.RepoRecord{
RepositoryID: 1,
Name: "library/hello-world",
}, nil) }, nil)
c.artMgr.On("GetByDigest").Return(nil, ierror.NotFoundError(nil)) c.artMgr.On("GetByDigest").Return(nil, ierror.NotFoundError(nil))
c.tagMgr.On("List").Return([]*tag.Tag{ c.tagMgr.On("List").Return([]*tag.Tag{
@ -525,7 +531,7 @@ func (c *controllerTestSuite) TestCopy() {
c.abstractor.On("AbstractMetadata").Return(nil) c.abstractor.On("AbstractMetadata").Return(nil)
c.artMgr.On("Create").Return(1, nil) c.artMgr.On("Create").Return(1, nil)
c.regCli.On("Copy").Return(nil) c.regCli.On("Copy").Return(nil)
_, err := c.ctl.Copy(nil, 1, 1) _, err := c.ctl.Copy(nil, "library/hello-world", "latest", "library/hello-world2")
c.Require().Nil(err) c.Require().Nil(err)
} }
@ -538,6 +544,7 @@ func (c *controllerTestSuite) TestListTags() {
ArtifactID: 1, ArtifactID: 1,
}, },
}, nil) }, nil)
c.artMgr.On("Get").Return(&artifact.Artifact{}, nil)
tags, err := c.ctl.ListTags(nil, nil, nil) tags, err := c.ctl.ListTags(nil, nil, nil)
c.Require().Nil(err) c.Require().Nil(err)
c.Len(tags, 1) c.Len(tags, 1)

View File

@ -32,8 +32,8 @@ type DAO interface {
List(ctx context.Context, query *q.Query) (artifacts []*Artifact, err error) List(ctx context.Context, query *q.Query) (artifacts []*Artifact, err error)
// Get the artifact specified by ID // Get the artifact specified by ID
Get(ctx context.Context, id int64) (*Artifact, error) Get(ctx context.Context, id int64) (*Artifact, error)
// GetByDigest returns the artifact specified by repository ID and digest // GetByDigest returns the artifact specified by repository and digest
GetByDigest(ctx context.Context, repositoryID int64, digest string) (artifact *Artifact, err error) GetByDigest(ctx context.Context, repository, digest string) (artifact *Artifact, err error)
// Create the artifact // Create the artifact
Create(ctx context.Context, artifact *Artifact) (id int64, err error) Create(ctx context.Context, artifact *Artifact) (id int64, err error)
// Delete the artifact specified by ID // Delete the artifact specified by ID
@ -118,11 +118,11 @@ func (d *dao) Get(ctx context.Context, id int64) (*Artifact, error) {
return artifact, nil return artifact, nil
} }
func (d *dao) GetByDigest(ctx context.Context, repositoryID int64, digest string) (*Artifact, error) { func (d *dao) GetByDigest(ctx context.Context, repository, digest string) (*Artifact, error) {
qs, err := orm.QuerySetter(ctx, &Artifact{}, &q.Query{ qs, err := orm.QuerySetter(ctx, &Artifact{}, &q.Query{
Keywords: map[string]interface{}{ Keywords: map[string]interface{}{
"RepositoryID": repositoryID, "RepositoryName": repository,
"Digest": digest, "Digest": digest,
}, },
}) })
if err != nil { if err != nil {
@ -134,7 +134,7 @@ func (d *dao) GetByDigest(ctx context.Context, repositoryID int64, digest string
} }
if len(artifacts) == 0 { if len(artifacts) == 0 {
return nil, ierror.New(nil).WithCode(ierror.NotFoundCode). return nil, ierror.New(nil).WithCode(ierror.NotFoundCode).
WithMessage("artifact %s under the repository %d not found", digest, repositoryID) WithMessage("artifact %s@%s not found", repository, digest)
} }
return artifacts[0], nil return artifacts[0], nil
} }

View File

@ -57,6 +57,7 @@ func (d *daoTestSuite) SetupTest() {
ManifestMediaType: v1.MediaTypeImageIndex, ManifestMediaType: v1.MediaTypeImageIndex,
ProjectID: 1, ProjectID: 1,
RepositoryID: 1, RepositoryID: 1,
RepositoryName: "library/hello-world",
Digest: "parent_digest", Digest: "parent_digest",
PushTime: now, PushTime: now,
PullTime: now, PullTime: now,
@ -72,6 +73,7 @@ func (d *daoTestSuite) SetupTest() {
ManifestMediaType: v1.MediaTypeImageManifest, ManifestMediaType: v1.MediaTypeImageManifest,
ProjectID: 1, ProjectID: 1,
RepositoryID: 1, RepositoryID: 1,
RepositoryName: "library/hello-world",
Digest: "child_digest_01", Digest: "child_digest_01",
Size: 1024, Size: 1024,
PushTime: now, PushTime: now,
@ -88,6 +90,7 @@ func (d *daoTestSuite) SetupTest() {
ManifestMediaType: v1.MediaTypeImageManifest, ManifestMediaType: v1.MediaTypeImageManifest,
ProjectID: 1, ProjectID: 1,
RepositoryID: 1, RepositoryID: 1,
RepositoryName: "library/hello-world",
Digest: "child_digest_02", Digest: "child_digest_02",
Size: 1024, Size: 1024,
PushTime: now, PushTime: now,
@ -324,12 +327,12 @@ func (d *daoTestSuite) TestGet() {
func (d *daoTestSuite) TestGetByDigest() { func (d *daoTestSuite) TestGetByDigest() {
// get the non-exist artifact // get the non-exist artifact
_, err := d.dao.GetByDigest(d.ctx, 1, "non_existing_digest") _, err := d.dao.GetByDigest(d.ctx, "library/hello-world", "non_existing_digest")
d.Require().NotNil(err) d.Require().NotNil(err)
d.True(ierror.IsErr(err, ierror.NotFoundCode)) d.True(ierror.IsErr(err, ierror.NotFoundCode))
// get the exist artifact // get the exist artifact
artifact, err := d.dao.GetByDigest(d.ctx, 1, "child_digest_02") artifact, err := d.dao.GetByDigest(d.ctx, "library/hello-world", "child_digest_02")
d.Require().Nil(err) d.Require().Nil(err)
d.Require().NotNil(artifact) d.Require().NotNil(artifact)
d.Equal(d.childArt02ID, artifact.ID) d.Equal(d.childArt02ID, artifact.ID)

View File

@ -33,6 +33,7 @@ type Artifact struct {
ManifestMediaType string `orm:"column(manifest_media_type)"` // the media type of manifest/index ManifestMediaType string `orm:"column(manifest_media_type)"` // the media type of manifest/index
ProjectID int64 `orm:"column(project_id)"` // needed for quota ProjectID int64 `orm:"column(project_id)"` // needed for quota
RepositoryID int64 `orm:"column(repository_id)"` RepositoryID int64 `orm:"column(repository_id)"`
RepositoryName string `orm:"column(repository_name)"`
Digest string `orm:"column(digest)"` Digest string `orm:"column(digest)"`
Size int64 `orm:"column(size)"` Size int64 `orm:"column(size)"`
PushTime time.Time `orm:"column(push_time)"` PushTime time.Time `orm:"column(push_time)"`

View File

@ -37,8 +37,8 @@ type Manager interface {
List(ctx context.Context, query *q.Query) (artifacts []*Artifact, err error) List(ctx context.Context, query *q.Query) (artifacts []*Artifact, err error)
// Get the artifact specified by the ID // Get the artifact specified by the ID
Get(ctx context.Context, id int64) (artifact *Artifact, err error) Get(ctx context.Context, id int64) (artifact *Artifact, err error)
// GetByDigest returns the artifact specified by repository ID and digest // GetByDigest returns the artifact specified by repository and digest
GetByDigest(ctx context.Context, repositoryID int64, digest string) (artifact *Artifact, err error) GetByDigest(ctx context.Context, repository, digest string) (artifact *Artifact, err error)
// Create the artifact. If the artifact is an index, make sure all the artifacts it references // Create the artifact. If the artifact is an index, make sure all the artifacts it references
// already exist // already exist
Create(ctx context.Context, artifact *Artifact) (id int64, err error) Create(ctx context.Context, artifact *Artifact) (id int64, err error)
@ -94,8 +94,8 @@ func (m *manager) Get(ctx context.Context, id int64) (*Artifact, error) {
return m.assemble(ctx, art) return m.assemble(ctx, art)
} }
func (m *manager) GetByDigest(ctx context.Context, repositoryID int64, digest string) (*Artifact, error) { func (m *manager) GetByDigest(ctx context.Context, repository, digest string) (*Artifact, error) {
art, err := m.dao.GetByDigest(ctx, repositoryID, digest) art, err := m.dao.GetByDigest(ctx, repository, digest)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -40,7 +40,7 @@ func (f *fakeDao) Get(ctx context.Context, id int64) (*dao.Artifact, error) {
args := f.Called() args := f.Called()
return args.Get(0).(*dao.Artifact), args.Error(1) return args.Get(0).(*dao.Artifact), args.Error(1)
} }
func (f *fakeDao) GetByDigest(ctx context.Context, repositoryID int64, digest string) (*dao.Artifact, error) { func (f *fakeDao) GetByDigest(ctx context.Context, repository, digest string) (*dao.Artifact, error) {
args := f.Called() args := f.Called()
return args.Get(0).(*dao.Artifact), args.Error(1) return args.Get(0).(*dao.Artifact), args.Error(1)
} }
@ -195,7 +195,7 @@ func (m *managerTestSuite) TestGetByDigest() {
} }
m.dao.On("GetByDigest", mock.Anything).Return(art, nil) m.dao.On("GetByDigest", mock.Anything).Return(art, nil)
m.dao.On("ListReferences").Return([]*dao.ArtifactReference{}, nil) m.dao.On("ListReferences").Return([]*dao.ArtifactReference{}, nil)
artifact, err := m.mgr.GetByDigest(nil, 1, "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180") artifact, err := m.mgr.GetByDigest(nil, "library/hello-world", "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180")
m.Require().Nil(err) m.Require().Nil(err)
m.Require().NotNil(artifact) m.Require().NotNil(artifact)
m.Equal(art.ID, artifact.ID) m.Equal(art.ID, artifact.ID)

View File

@ -33,6 +33,7 @@ type Artifact struct {
ManifestMediaType string `json:"manifest_media_type"` // the media type of manifest/index ManifestMediaType string `json:"manifest_media_type"` // the media type of manifest/index
ProjectID int64 `json:"project_id"` ProjectID int64 `json:"project_id"`
RepositoryID int64 `json:"repository_id"` RepositoryID int64 `json:"repository_id"`
RepositoryName string `json:"repository_name"`
Digest string `json:"digest"` Digest string `json:"digest"`
Size int64 `json:"size"` Size int64 `json:"size"`
PushTime time.Time `json:"push_time"` PushTime time.Time `json:"push_time"`
@ -40,8 +41,6 @@ type Artifact struct {
ExtraAttrs map[string]interface{} `json:"extra_attrs"` // only contains the simple attributes specific for the different artifact type, most of them should come from the config layer ExtraAttrs map[string]interface{} `json:"extra_attrs"` // only contains the simple attributes specific for the different artifact type, most of them should come from the config layer
Annotations map[string]string `json:"annotations"` Annotations map[string]string `json:"annotations"`
References []*Reference `json:"references"` // child artifacts referenced by the parent artifact if the artifact is an index References []*Reference `json:"references"` // child artifacts referenced by the parent artifact if the artifact is an index
RepositoryName string `json:"-"` // repository name, eg: library/photon
} }
// From converts the database level artifact to the business level object // From converts the database level artifact to the business level object
@ -52,6 +51,7 @@ func (a *Artifact) From(art *dao.Artifact) {
a.ManifestMediaType = art.ManifestMediaType a.ManifestMediaType = art.ManifestMediaType
a.ProjectID = art.ProjectID a.ProjectID = art.ProjectID
a.RepositoryID = art.RepositoryID a.RepositoryID = art.RepositoryID
a.RepositoryName = art.RepositoryName
a.Digest = art.Digest a.Digest = art.Digest
a.Size = art.Size a.Size = art.Size
a.PushTime = art.PushTime a.PushTime = art.PushTime
@ -79,6 +79,7 @@ func (a *Artifact) To() *dao.Artifact {
ManifestMediaType: a.ManifestMediaType, ManifestMediaType: a.ManifestMediaType,
ProjectID: a.ProjectID, ProjectID: a.ProjectID,
RepositoryID: a.RepositoryID, RepositoryID: a.RepositoryID,
RepositoryName: a.RepositoryName,
Digest: a.Digest, Digest: a.Digest,
Size: a.Size, Size: a.Size,
PushTime: a.PushTime, PushTime: a.PushTime,

View File

@ -173,7 +173,7 @@ func (suite *DaoTestSuite) TestFindBlobsShouldUnassociatedWithProject() {
artifact1 := suite.DigestString() artifact1 := suite.DigestString()
artifact2 := suite.DigestString() artifact2 := suite.DigestString()
sql := `INSERT INTO artifact ("type", media_type, manifest_media_type, digest, project_id, repository_id) VALUES ('image', 'media_type', 'manifest_media_type', ?, ?, ?)` sql := `INSERT INTO artifact ("type", media_type, manifest_media_type, digest, project_id, repository_id, repository_name) VALUES ('image', 'media_type', 'manifest_media_type', ?, ?, ?, 'library/hello-world')`
suite.ExecSQL(sql, artifact1, projectID, 10) suite.ExecSQL(sql, artifact1, projectID, 10)
suite.ExecSQL(sql, artifact2, projectID, 10) suite.ExecSQL(sql, artifact2, projectID, 10)

View File

@ -92,7 +92,7 @@ func (suite *ManagerTestSuite) TestCleanupAssociationsForProject() {
artifact1 := suite.DigestString() artifact1 := suite.DigestString()
artifact2 := suite.DigestString() artifact2 := suite.DigestString()
sql := `INSERT INTO artifact ("type", media_type, manifest_media_type, digest, project_id, repository_id) VALUES ('image', 'media_type', 'manifest_media_type', ?, ?, ?)` sql := `INSERT INTO artifact ("type", media_type, manifest_media_type, digest, project_id, repository_id, repository_name) VALUES ('image', 'media_type', 'manifest_media_type', ?, ?, ?, 'library/hello-world')`
suite.ExecSQL(sql, artifact1, projectID, 10) suite.ExecSQL(sql, artifact1, projectID, 10)
suite.ExecSQL(sql, artifact2, projectID, 10) suite.ExecSQL(sql, artifact2, projectID, 10)

View File

@ -110,15 +110,16 @@ func (suite *HandlerSuite) addProject(projectName string) int64 {
return projectID return projectID
} }
func (suite *HandlerSuite) addArt(ctx context.Context, pid, repositoryID int64, dgt string) int64 { func (suite *HandlerSuite) addArt(ctx context.Context, pid, repositoryID int64, repositoryName, dgt string) int64 {
af := &artifact.Artifact{ af := &artifact.Artifact{
Type: "Docker-Image", Type: "Docker-Image",
ProjectID: pid, ProjectID: pid,
RepositoryID: repositoryID, RepositoryID: repositoryID,
Digest: dgt, RepositoryName: repositoryName,
Size: 1024, Digest: dgt,
PushTime: time.Now(), Size: 1024,
PullTime: time.Now(), PushTime: time.Now(),
PullTime: time.Now(),
} }
afid, err := artifact.Mgr.Create(ctx, af) afid, err := artifact.Mgr.Create(ctx, af)
suite.Nil(err, fmt.Sprintf("Add artifact failed for %d", repositoryID)) suite.Nil(err, fmt.Sprintf("Add artifact failed for %d", repositoryID))
@ -185,7 +186,7 @@ func (suite *HandlerSuite) TestPutDeleteManifestCreated() {
projectID := suite.addProject(projectName) projectID := suite.addProject(projectName)
immuRuleID := suite.addImmutableRule(projectID) immuRuleID := suite.addImmutableRule(projectID)
repoID := suite.addRepo(ctx, projectID, repoName) repoID := suite.addRepo(ctx, projectID, repoName)
afID := suite.addArt(ctx, projectID, repoID, dgt) afID := suite.addArt(ctx, projectID, repoID, repoName, dgt)
tagID := suite.addTags(ctx, repoID, afID, "release-1.10") tagID := suite.addTags(ctx, repoID, afID, "release-1.10")
defer func() { defer func() {

View File

@ -70,7 +70,7 @@ func putManifest(w http.ResponseWriter, req *http.Request) {
reference := router.Param(req.Context(), ":reference") reference := router.Param(req.Context(), ":reference")
// make sure the repository exist before pushing the manifest // make sure the repository exist before pushing the manifest
_, repositoryID, err := repository.Ctl.Ensure(req.Context(), repo) _, _, err := repository.Ctl.Ensure(req.Context(), repo)
if err != nil { if err != nil {
serror.SendError(w, err) serror.SendError(w, err)
return return
@ -98,7 +98,7 @@ func putManifest(w http.ResponseWriter, req *http.Request) {
tags = append(tags, reference) tags = append(tags, reference)
} }
_, _, err = artifact.Ctl.Ensure(req.Context(), repositoryID, dgt, tags...) _, _, err = artifact.Ctl.Ensure(req.Context(), repo, dgt, tags...)
if err != nil { if err != nil {
serror.SendError(w, err) serror.SendError(w, err)
return return

View File

@ -81,11 +81,7 @@ func (a *artifactAPI) ListArtifacts(ctx context.Context, params operation.ListAr
if params.PageSize != nil { if params.PageSize != nil {
query.PageSize = *(params.PageSize) query.PageSize = *(params.PageSize)
} }
repository, err := a.repoCtl.GetByName(ctx, fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName)) query.Keywords["RepositoryName"] = fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName)
if err != nil {
return a.SendError(ctx, err)
}
query.Keywords["RepositoryID"] = repository.RepositoryID
// set option // set option
option := option(params.WithTag, params.WithImmutableStatus, option := option(params.WithTag, params.WithImmutableStatus,
@ -155,23 +151,24 @@ func (a *artifactAPI) CopyArtifact(ctx context.Context, params operation.CopyArt
if err := a.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionCreate, rbac.ResourceArtifact); err != nil { if err := a.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionCreate, rbac.ResourceArtifact); err != nil {
return a.SendError(ctx, err) return a.SendError(ctx, err)
} }
srcRepo, srcRef, err := parse(params.From)
srcRepo, ref, err := parse(params.From)
if err != nil { if err != nil {
return a.SendError(ctx, err) return a.SendError(ctx, err)
} }
srcPro, _ := utils.ParseRepository(srcRepo) srcPro, _ := utils.ParseRepository(srcRepo)
if err = a.RequireProjectAccess(ctx, srcPro, rbac.ActionRead, rbac.ResourceArtifact); err != nil { if err = a.RequireProjectAccess(ctx, srcPro, rbac.ActionRead, rbac.ResourceArtifact); err != nil {
return a.SendError(ctx, err) return a.SendError(ctx, err)
} }
srcArt, err := a.artCtl.GetByReference(ctx, srcRepo, srcRef, &artifact.Option{WithTag: true})
dstRepo := fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName)
_, id, err := a.repoCtl.Ensure(ctx, dstRepo)
if err != nil { if err != nil {
return a.SendError(ctx, err) return a.SendError(ctx, err)
} }
_, id, err := a.repoCtl.Ensure(ctx, params.ProjectName+"/"+params.RepositoryName)
if err != nil { id, err = a.artCtl.Copy(ctx, srcRepo, ref, dstRepo)
return a.SendError(ctx, err)
}
id, err = a.artCtl.Copy(ctx, srcArt.ID, id)
if err != nil { if err != nil {
return a.SendError(ctx, err) return a.SendError(ctx, err)
} }

View File

@ -29,7 +29,7 @@ type FakeController struct {
} }
// Ensure ... // Ensure ...
func (f *FakeController) Ensure(ctx context.Context, repositoryID int64, digest string, tags ...string) (bool, int64, error) { func (f *FakeController) Ensure(ctx context.Context, repository, digest string, tags ...string) (bool, int64, error) {
args := f.Called() args := f.Called()
return args.Bool(0), int64(args.Int(1)), args.Error(2) return args.Bool(0), int64(args.Int(1)), args.Error(2)
} }
@ -77,7 +77,7 @@ func (f *FakeController) Delete(ctx context.Context, id int64) (err error) {
} }
// Copy ... // Copy ...
func (f *FakeController) Copy(ctx context.Context, srcArtID, dstRepoID int64) (int64, error) { func (f *FakeController) Copy(ctx context.Context, srcRepo, ref, dstRepo string) (int64, error) {
args := f.Called() args := f.Called()
return int64(args.Int(0)), args.Error(1) return int64(args.Int(0)), args.Error(1)
} }

View File

@ -54,7 +54,7 @@ func (f *FakeManager) Get(ctx context.Context, id int64) (*artifact.Artifact, er
} }
// GetByDigest ... // GetByDigest ...
func (f *FakeManager) GetByDigest(ctx context.Context, repositoryID int64, digest string) (*artifact.Artifact, error) { func (f *FakeManager) GetByDigest(ctx context.Context, repository, digest string) (*artifact.Artifact, error) {
args := f.Called() args := f.Called()
var art *artifact.Artifact var art *artifact.Artifact
if args.Get(0) != nil { if args.Get(0) != nil {