mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-17 04:11:24 +01:00
Merge pull request #10844 from ywk253100/200225_artifact
Use the repository name of artifact model
This commit is contained in:
commit
e9d09c705e
@ -56,9 +56,6 @@ WHERE ordered_art.seq=1;
|
||||
|
||||
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*/
|
||||
DELETE FROM artifact
|
||||
WHERE id NOT IN (
|
||||
|
@ -25,7 +25,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/api/artifact/abstractor/resolver"
|
||||
ierror "github.com/goharbor/harbor/src/internal/error"
|
||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/pkg/repository"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
@ -43,24 +42,18 @@ type Abstractor interface {
|
||||
// NewAbstractor returns an instance of the default abstractor
|
||||
func NewAbstractor() Abstractor {
|
||||
return &abstractor{
|
||||
repoMgr: repository.Mgr,
|
||||
blobFetcher: blob.Fcher,
|
||||
}
|
||||
}
|
||||
|
||||
type abstractor struct {
|
||||
repoMgr repository.Manager
|
||||
blobFetcher blob.Fetcher
|
||||
}
|
||||
|
||||
// TODO add white list for supported artifact type
|
||||
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
|
||||
manifestMediaType, content, err := a.blobFetcher.FetchManifest(repository.Name, artifact.Digest)
|
||||
manifestMediaType, content, err := a.blobFetcher.FetchManifest(artifact.RepositoryName, artifact.Digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -18,12 +18,10 @@ import (
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
"github.com/goharbor/harbor/src/api/artifact/abstractor/resolver"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
ierror "github.com/goharbor/harbor/src/internal/error"
|
||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob"
|
||||
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"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
@ -203,16 +201,13 @@ type abstractorTestSuite struct {
|
||||
suite.Suite
|
||||
abstractor Abstractor
|
||||
fetcher *blob.FakeFetcher
|
||||
repoMgr *repository.FakeManager
|
||||
resolver *tresolver.FakeResolver
|
||||
}
|
||||
|
||||
func (a *abstractorTestSuite) SetupTest() {
|
||||
a.fetcher = &blob.FakeFetcher{}
|
||||
a.repoMgr = &repository.FakeManager{}
|
||||
a.resolver = &tresolver.FakeResolver{}
|
||||
a.abstractor = &abstractor{
|
||||
repoMgr: a.repoMgr,
|
||||
blobFetcher: a.fetcher,
|
||||
}
|
||||
}
|
||||
@ -220,7 +215,6 @@ func (a *abstractorTestSuite) SetupTest() {
|
||||
// docker manifest v1
|
||||
func (a *abstractorTestSuite) TestAbstractMetadataOfV1Manifest() {
|
||||
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.resolver.On("ArtifactType").Return(fakeArtifactType)
|
||||
a.resolver.On("ResolveMetadata").Return(nil)
|
||||
@ -238,7 +232,6 @@ func (a *abstractorTestSuite) TestAbstractMetadataOfV1Manifest() {
|
||||
// docker manifest v2
|
||||
func (a *abstractorTestSuite) TestAbstractMetadataOfV2Manifest() {
|
||||
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.resolver.On("ArtifactType").Return(fakeArtifactType)
|
||||
a.resolver.On("ResolveMetadata").Return(nil)
|
||||
@ -257,7 +250,6 @@ func (a *abstractorTestSuite) TestAbstractMetadataOfV2Manifest() {
|
||||
// OCI index
|
||||
func (a *abstractorTestSuite) TestAbstractMetadataOfIndex() {
|
||||
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.resolver.On("ArtifactType").Return(fakeArtifactType)
|
||||
a.resolver.On("ResolveMetadata").Return(nil)
|
||||
@ -275,7 +267,6 @@ func (a *abstractorTestSuite) TestAbstractMetadataOfIndex() {
|
||||
|
||||
// OCI index
|
||||
func (a *abstractorTestSuite) TestAbstractMetadataOfUnsupported() {
|
||||
a.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
|
||||
a.fetcher.On("FetchManifest").Return("unsupported-manifest", []byte{}, nil)
|
||||
artifact := &artifact.Artifact{
|
||||
ID: 1,
|
||||
|
@ -24,7 +24,6 @@ import (
|
||||
ierror "github.com/goharbor/harbor/src/internal/error"
|
||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/pkg/chart"
|
||||
"github.com/goharbor/harbor/src/pkg/repository"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
@ -41,7 +40,6 @@ const (
|
||||
|
||||
func init() {
|
||||
resolver := &resolver{
|
||||
repoMgr: repository.Mgr,
|
||||
blobFetcher: blob.Fcher,
|
||||
chartOperator: chart.Optr,
|
||||
}
|
||||
@ -56,22 +54,17 @@ func init() {
|
||||
}
|
||||
|
||||
type resolver struct {
|
||||
repoMgr repository.Manager
|
||||
blobFetcher blob.Fetcher
|
||||
chartOperator chart.Operator
|
||||
}
|
||||
|
||||
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{}
|
||||
if err := json.Unmarshal(manifest, m); err != nil {
|
||||
return err
|
||||
}
|
||||
digest := m.Config.Digest.String()
|
||||
layer, err := r.blobFetcher.FetchLayer(repository.Name, digest)
|
||||
layer, err := r.blobFetcher.FetchLayer(artifact.RepositoryName, digest)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
repository, err := r.repoMgr.Get(ctx, artifact.RepositoryID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, content, err := r.blobFetcher.FetchManifest(repository.Name, artifact.Digest)
|
||||
_, content, err := r.blobFetcher.FetchManifest(artifact.RepositoryName, artifact.Digest)
|
||||
if err != nil {
|
||||
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.
|
||||
layerDgst := layer.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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -15,13 +15,11 @@
|
||||
package chart
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
ierror "github.com/goharbor/harbor/src/internal/error"
|
||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||
chartserver "github.com/goharbor/harbor/src/pkg/chart"
|
||||
"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/repository"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"testing"
|
||||
@ -30,17 +28,14 @@ import (
|
||||
type resolverTestSuite struct {
|
||||
suite.Suite
|
||||
resolver *resolver
|
||||
repoMgr *repository.FakeManager
|
||||
blobFetcher *blob.FakeFetcher
|
||||
chartOptr *chart.FakeOpertaor
|
||||
}
|
||||
|
||||
func (r *resolverTestSuite) SetupTest() {
|
||||
r.repoMgr = &repository.FakeManager{}
|
||||
r.blobFetcher = &blob.FakeFetcher{}
|
||||
r.chartOptr = &chart.FakeOpertaor{}
|
||||
r.resolver = &resolver{
|
||||
repoMgr: r.repoMgr,
|
||||
blobFetcher: r.blobFetcher,
|
||||
chartOperator: r.chartOptr,
|
||||
}
|
||||
@ -92,11 +87,9 @@ func (r *resolverTestSuite) TestResolveMetadata() {
|
||||
"appVersion": "1.8.2"
|
||||
}`
|
||||
artifact := &artifact.Artifact{}
|
||||
r.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
|
||||
r.blobFetcher.On("FetchLayer").Return([]byte(config), nil)
|
||||
err := r.resolver.ResolveMetadata(nil, []byte(content), artifact)
|
||||
r.Require().Nil(err)
|
||||
r.repoMgr.AssertExpectations(r.T())
|
||||
r.blobFetcher.AssertExpectations(r.T())
|
||||
r.Assert().Equal("1.1.2", artifact.ExtraAttrs["version"].(string))
|
||||
r.Assert().Equal("1.8.2", artifact.ExtraAttrs["appVersion"].(string))
|
||||
@ -158,7 +151,6 @@ func (r *resolverTestSuite) TestResolveAddition() {
|
||||
}
|
||||
|
||||
artifact := &artifact.Artifact{}
|
||||
r.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
|
||||
r.blobFetcher.On("FetchManifest").Return("", []byte(chartManifest), nil)
|
||||
r.blobFetcher.On("FetchLayer").Return([]byte(chartYaml), nil)
|
||||
r.chartOptr.On("GetDetails").Return(chartDetails, nil)
|
||||
|
@ -23,7 +23,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
ierror "github.com/goharbor/harbor/src/internal/error"
|
||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/pkg/repository"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
@ -35,7 +34,6 @@ const (
|
||||
|
||||
func init() {
|
||||
resolver := &resolver{
|
||||
repoMgr: repository.Mgr,
|
||||
argMgr: artifact.Mgr,
|
||||
blobFetcher: blob.Fcher,
|
||||
}
|
||||
@ -50,7 +48,6 @@ func init() {
|
||||
}
|
||||
|
||||
type resolver struct {
|
||||
repoMgr repository.Manager
|
||||
argMgr artifact.Manager
|
||||
blobFetcher blob.Fetcher
|
||||
}
|
||||
@ -65,7 +62,7 @@ func (r *resolver) ResolveMetadata(ctx context.Context, manifest []byte, art *ar
|
||||
for _, mani := range index.Manifests {
|
||||
digest := mani.Digest.String()
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
@ -84,12 +81,8 @@ func (r *resolver) ResolveMetadata(ctx context.Context, manifest []byte, art *ar
|
||||
}
|
||||
|
||||
// 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
|
||||
_, cfgMani, err := r.blobFetcher.FetchManifest(repository.Name, cfgManiDgt)
|
||||
_, cfgMani, err := r.blobFetcher.FetchManifest(art.RepositoryName, cfgManiDgt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -99,7 +92,7 @@ func (r *resolver) ResolveMetadata(ctx context.Context, manifest []byte, art *ar
|
||||
}
|
||||
cfgDgt := m.Config.Digest.String()
|
||||
// get the config layer
|
||||
cfg, err := r.blobFetcher.FetchLayer(repository.Name, cfgDgt)
|
||||
cfg, err := r.blobFetcher.FetchLayer(art.RepositoryName, cfgDgt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -15,12 +15,10 @@
|
||||
package cnab
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
ierror "github.com/goharbor/harbor/src/internal/error"
|
||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob"
|
||||
testingartifact "github.com/goharbor/harbor/src/testing/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/testing/pkg/repository"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
)
|
||||
@ -28,17 +26,14 @@ import (
|
||||
type resolverTestSuite struct {
|
||||
suite.Suite
|
||||
resolver *resolver
|
||||
repoMgr *repository.FakeManager
|
||||
artMgr *testingartifact.FakeManager
|
||||
blobFetcher *blob.FakeFetcher
|
||||
}
|
||||
|
||||
func (r *resolverTestSuite) SetupTest() {
|
||||
r.repoMgr = &repository.FakeManager{}
|
||||
r.artMgr = &testingartifact.FakeManager{}
|
||||
r.blobFetcher = &blob.FakeFetcher{}
|
||||
r.resolver = &resolver{
|
||||
repoMgr: r.repoMgr,
|
||||
argMgr: r.artMgr,
|
||||
blobFetcher: r.blobFetcher,
|
||||
}
|
||||
@ -115,7 +110,6 @@ func (r *resolverTestSuite) TestResolveMetadata() {
|
||||
}`
|
||||
art := &artifact.Artifact{}
|
||||
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("FetchLayer").Return([]byte(config), nil)
|
||||
err := r.resolver.ResolveMetadata(nil, []byte(index), art)
|
||||
|
@ -58,7 +58,7 @@ func (i *indexResolver) ResolveMetadata(ctx context.Context, manifest []byte, ar
|
||||
for _, mani := range index.Manifests {
|
||||
digest := mani.Digest.String()
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
ierror "github.com/goharbor/harbor/src/internal/error"
|
||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/pkg/repository"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
@ -37,7 +36,6 @@ const (
|
||||
|
||||
func init() {
|
||||
rslver := &manifestV2Resolver{
|
||||
repoMgr: repository.Mgr,
|
||||
blobFetcher: blob.Fcher,
|
||||
}
|
||||
mediaTypes := []string{
|
||||
@ -56,21 +54,16 @@ func init() {
|
||||
|
||||
// manifestV2Resolver resolve artifact with OCI manifest and docker v2 manifest
|
||||
type manifestV2Resolver struct {
|
||||
repoMgr repository.Manager
|
||||
blobFetcher blob.Fetcher
|
||||
}
|
||||
|
||||
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{}
|
||||
if err := json.Unmarshal(content, manifest); err != nil {
|
||||
return err
|
||||
}
|
||||
digest := manifest.Config.Digest.String()
|
||||
layer, err := m.blobFetcher.FetchLayer(repository.Name, digest)
|
||||
layer, err := m.blobFetcher.FetchLayer(artifact.RepositoryName, digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -93,11 +86,7 @@ func (m *manifestV2Resolver) ResolveAddition(ctx context.Context, artifact *arti
|
||||
return nil, ierror.New(nil).WithCode(ierror.BadRequestCode).
|
||||
WithMessage("addition %s isn't supported for %s(manifest version 2)", addition, ArtifactTypeImage)
|
||||
}
|
||||
repository, err := m.repoMgr.Get(ctx, artifact.RepositoryID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, content, err := m.blobFetcher.FetchManifest(repository.Name, artifact.Digest)
|
||||
_, content, err := m.blobFetcher.FetchManifest(artifact.RepositoryName, artifact.Digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -105,7 +94,7 @@ func (m *manifestV2Resolver) ResolveAddition(ctx context.Context, artifact *arti
|
||||
if err := json.Unmarshal(content, manifest); err != nil {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -15,11 +15,9 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
ierror "github.com/goharbor/harbor/src/internal/error"
|
||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/testing/api/artifact/abstractor/blob"
|
||||
"github.com/goharbor/harbor/src/testing/pkg/repository"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
)
|
||||
@ -123,15 +121,12 @@ var (
|
||||
type manifestV2ResolverTestSuite struct {
|
||||
suite.Suite
|
||||
resolver *manifestV2Resolver
|
||||
repoMgr *repository.FakeManager
|
||||
blobFetcher *blob.FakeFetcher
|
||||
}
|
||||
|
||||
func (m *manifestV2ResolverTestSuite) SetupTest() {
|
||||
m.repoMgr = &repository.FakeManager{}
|
||||
m.blobFetcher = &blob.FakeFetcher{}
|
||||
m.resolver = &manifestV2Resolver{
|
||||
repoMgr: m.repoMgr,
|
||||
blobFetcher: m.blobFetcher,
|
||||
}
|
||||
|
||||
@ -139,11 +134,9 @@ func (m *manifestV2ResolverTestSuite) SetupTest() {
|
||||
|
||||
func (m *manifestV2ResolverTestSuite) TestResolveMetadata() {
|
||||
artifact := &artifact.Artifact{}
|
||||
m.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
|
||||
m.blobFetcher.On("FetchLayer").Return([]byte(config), nil)
|
||||
err := m.resolver.ResolveMetadata(nil, []byte(manifest), artifact)
|
||||
m.Require().Nil(err)
|
||||
m.repoMgr.AssertExpectations(m.T())
|
||||
m.blobFetcher.AssertExpectations(m.T())
|
||||
m.Assert().Equal("amd64", artifact.ExtraAttrs["architecture"].(string))
|
||||
m.Assert().Equal("linux", artifact.ExtraAttrs["os"].(string))
|
||||
@ -156,7 +149,6 @@ func (m *manifestV2ResolverTestSuite) TestResolveAddition() {
|
||||
|
||||
// build history
|
||||
artifact := &artifact.Artifact{}
|
||||
m.repoMgr.On("Get").Return(&models.RepoRecord{}, nil)
|
||||
m.blobFetcher.On("FetchManifest").Return("", []byte(manifest), nil)
|
||||
m.blobFetcher.On("FetchLayer").Return([]byte(config), nil)
|
||||
addition, err := m.resolver.ResolveAddition(nil, artifact, AdditionTypeBuildHistory)
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/api/artifact/abstractor"
|
||||
"github.com/goharbor/harbor/src/api/artifact/abstractor/resolver"
|
||||
"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/internal"
|
||||
"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
|
||||
// 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
|
||||
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.
|
||||
// The artifacts that referenced by others and without tags are not counted
|
||||
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)
|
||||
// Delete the artifact specified by ID. All tags attached to the artifact are deleted as well
|
||||
Delete(ctx context.Context, id int64) (err error)
|
||||
// Copy the artifact whose ID is specified by "srcArtID" into the repository specified by "dstRepoID"
|
||||
Copy(ctx context.Context, srcArtID, dstRepoID int64) (id int64, err error)
|
||||
// Copy the artifact specified by "srcRepo" and "reference" into the repository specified by "dstRepo"
|
||||
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(ctx context.Context, query *q.Query, option *TagOption) (tags []*Tag, err error)
|
||||
// CreateTag creates a tag
|
||||
@ -127,46 +126,47 @@ type controller struct {
|
||||
regCli registry.Client
|
||||
}
|
||||
|
||||
func (c *controller) Ensure(ctx context.Context, repositoryID int64, digest string, tags ...string) (bool, int64, error) {
|
||||
created, id, err := c.ensureArtifact(ctx, repositoryID, digest)
|
||||
func (c *controller) Ensure(ctx context.Context, repository, digest string, tags ...string) (bool, int64, error) {
|
||||
created, artifact, err := c.ensureArtifact(ctx, repository, digest)
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
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 created, id, nil
|
||||
return created, artifact.ID, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
art, err := c.artMgr.GetByDigest(ctx, repositoryID, digest)
|
||||
func (c *controller) ensureArtifact(ctx context.Context, repository, digest string) (bool, *artifact.Artifact, error) {
|
||||
art, err := c.artMgr.GetByDigest(ctx, repository, digest)
|
||||
// the artifact already exists under the repository, return directly
|
||||
if err == nil {
|
||||
return false, art.ID, nil
|
||||
return false, art, nil
|
||||
}
|
||||
|
||||
// got other error when get the artifact, return the error
|
||||
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
|
||||
repository, err := c.repoMgr.Get(ctx, repositoryID)
|
||||
repo, err := c.repoMgr.GetByName(ctx, repository)
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
return false, nil, err
|
||||
}
|
||||
artifact := &artifact.Artifact{
|
||||
ProjectID: repository.ProjectID,
|
||||
RepositoryID: repositoryID,
|
||||
Digest: digest,
|
||||
PushTime: time.Now(),
|
||||
ProjectID: repo.ProjectID,
|
||||
RepositoryID: repo.RepositoryID,
|
||||
RepositoryName: repository,
|
||||
Digest: digest,
|
||||
PushTime: time.Now(),
|
||||
}
|
||||
// abstract the metadata for the artifact
|
||||
if err = c.abstractor.AbstractMetadata(ctx, artifact); err != nil {
|
||||
return false, 0, err
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
// populate the artifact type
|
||||
@ -177,15 +177,16 @@ func (c *controller) ensureArtifact(ctx context.Context, repositoryID int64, dig
|
||||
if err != nil {
|
||||
// if got conflict error, try to get the artifact again
|
||||
if ierror.IsConflictErr(err) {
|
||||
art, err = c.artMgr.GetByDigest(ctx, repositoryID, digest)
|
||||
art, err = c.artMgr.GetByDigest(ctx, repository, digest)
|
||||
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 {
|
||||
@ -236,10 +237,6 @@ func (c *controller) List(ctx context.Context, query *q.Query, option *Option) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.populateRepositoryName(ctx, arts...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var artifacts []*Artifact
|
||||
for _, art := range arts {
|
||||
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) {
|
||||
repo, err := c.repoMgr.GetByName(ctx, repository)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
art, err := c.artMgr.GetByDigest(ctx, repo.RepositoryID, digest)
|
||||
art, err := c.artMgr.GetByDigest(ctx, repository, digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -377,14 +370,10 @@ func (c *controller) deleteDeeply(ctx context.Context, id int64, isRoot bool) er
|
||||
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{
|
||||
MediaType: art.MediaType,
|
||||
ManifestMediaType: art.ManifestMediaType,
|
||||
RepositoryName: repo.Name,
|
||||
RepositoryName: art.RepositoryName,
|
||||
Digest: art.Digest,
|
||||
})
|
||||
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
|
||||
}
|
||||
|
||||
func (c *controller) Copy(ctx context.Context, srcArtID, dstRepoID int64) (int64, error) {
|
||||
srcArt, err := c.Get(ctx, srcArtID, &Option{WithTag: true})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
func (c *controller) Copy(ctx context.Context, srcRepo, reference, dstRepo string) (int64, error) {
|
||||
return c.copyDeeply(ctx, srcRepo, reference, dstRepo, true)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return 0, err
|
||||
}
|
||||
dstRepo, err := c.repoMgr.Get(ctx, dstRepoID)
|
||||
|
||||
srcArt, err := c.GetByReference(ctx, srcRepo, reference, option)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
_, err = c.artMgr.GetByDigest(ctx, dstRepoID, srcArt.Digest)
|
||||
// the artifact already exists in the destination repository
|
||||
digest := srcArt.Digest
|
||||
|
||||
// check the existence of artifact in the destination repository
|
||||
dstArt, err := c.GetByReference(ctx, dstRepo, digest, option)
|
||||
if err == nil {
|
||||
return 0, ierror.New(nil).WithCode(ierror.ConflictCode).
|
||||
WithMessage("the artifact %s already exists under the repository %s",
|
||||
srcArt.Digest, dstRepo.Name)
|
||||
// return conflict error if the root parent artifact already exists under the destination repository
|
||||
if isRoot {
|
||||
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) {
|
||||
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
|
||||
var tags []string
|
||||
for _, tag := range srcArt.Tags {
|
||||
tags = append(tags, tag.Name)
|
||||
}
|
||||
return c.copyDeeply(ctx, srcRepo, srcArt, dstRepo, 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...)
|
||||
// ensure the parent artifact exist in the database
|
||||
_, id, err := c.Ensure(ctx, dstRepo, digest, tags...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -468,7 +459,11 @@ func (c *controller) ListTags(ctx context.Context, query *q.Query, option *TagOp
|
||||
}
|
||||
var tags []*Tag
|
||||
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
|
||||
}
|
||||
@ -517,19 +512,9 @@ func (c *controller) assembleArtifact(ctx context.Context, art *artifact.Artifac
|
||||
artifact := &Artifact{
|
||||
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
|
||||
c.populateAdditionLinks(ctx, artifact)
|
||||
|
||||
if option == nil {
|
||||
return artifact
|
||||
}
|
||||
@ -539,39 +524,9 @@ func (c *controller) assembleArtifact(ctx context.Context, art *artifact.Artifac
|
||||
if option.WithLabel {
|
||||
c.populateLabels(ctx, artifact)
|
||||
}
|
||||
// populate addition links
|
||||
c.populateAdditionLinks(ctx, 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) {
|
||||
tags, err := c.tagMgr.List(ctx, &q.Query{
|
||||
Keywords: map[string]interface{}{
|
||||
@ -583,46 +538,51 @@ func (c *controller) populateTags(ctx context.Context, art *Artifact, option *Ta
|
||||
return
|
||||
}
|
||||
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
|
||||
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{
|
||||
Tag: *tag,
|
||||
}
|
||||
if option == nil {
|
||||
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 {
|
||||
c.populateImmutableStatus(ctx, t)
|
||||
c.populateImmutableStatus(ctx, art, t)
|
||||
}
|
||||
if option.WithSignature {
|
||||
if a, err := c.artMgr.Get(ctx, t.ArtifactID); err != nil {
|
||||
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)
|
||||
}
|
||||
c.populateTagSignature(ctx, art, t, option)
|
||||
}
|
||||
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 {
|
||||
chk, err := signature.GetManager().GetCheckerByRepo(ctx, repo)
|
||||
chk, err := signature.GetManager().GetCheckerByRepo(ctx, artifact.RepositoryName)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
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) {
|
||||
@ -634,25 +594,6 @@ func (c *controller) populateLabels(ctx context.Context, art *Artifact) {
|
||||
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) {
|
||||
types := descriptor.ListAdditionTypes(artifact.MediaType)
|
||||
if len(types) > 0 {
|
||||
|
@ -39,6 +39,8 @@ import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
// TODO find another way to test artifact controller, it's hard to maintain currently
|
||||
|
||||
type fakeAbstractor struct {
|
||||
mock.Mock
|
||||
}
|
||||
@ -107,6 +109,13 @@ func (c *controllerTestSuite) SetupTest() {
|
||||
}
|
||||
|
||||
func (c *controllerTestSuite) TestAssembleTag() {
|
||||
art := &artifact.Artifact{
|
||||
ID: 1,
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
RepositoryName: "library/hello-world",
|
||||
Digest: "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180",
|
||||
}
|
||||
tg := &tag.Tag{
|
||||
ID: 1,
|
||||
RepositoryID: 1,
|
||||
@ -119,13 +128,8 @@ func (c *controllerTestSuite) TestAssembleTag() {
|
||||
WithImmutableStatus: true,
|
||||
}
|
||||
|
||||
c.repoMgr.On("Get").Return(&models.RepoRecord{
|
||||
ProjectID: 1,
|
||||
Name: "hello-world",
|
||||
}, 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.Equal(tag.ID, tg.ID)
|
||||
c.Equal(true, tag.Immutable)
|
||||
@ -154,9 +158,6 @@ func (c *controllerTestSuite) TestAssembleArtifact() {
|
||||
PullTime: time.Now(),
|
||||
}
|
||||
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")
|
||||
lb := &models.Label{
|
||||
ID: 1,
|
||||
@ -185,25 +186,25 @@ func (c *controllerTestSuite) TestEnsureArtifact() {
|
||||
c.artMgr.On("GetByDigest").Return(&artifact.Artifact{
|
||||
ID: 1,
|
||||
}, 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.False(created)
|
||||
c.Equal(int64(1), id)
|
||||
c.Equal(int64(1), art.ID)
|
||||
|
||||
// reset the mock
|
||||
c.SetupTest()
|
||||
|
||||
// the artifact doesn't exist
|
||||
c.repoMgr.On("Get").Return(&models.RepoRecord{
|
||||
c.repoMgr.On("GetByName").Return(&models.RepoRecord{
|
||||
ProjectID: 1,
|
||||
}, nil)
|
||||
c.artMgr.On("GetByDigest").Return(nil, ierror.NotFoundError(nil))
|
||||
c.artMgr.On("Create").Return(1, 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.True(created)
|
||||
c.Equal(int64(1), id)
|
||||
c.Equal(int64(1), art.ID)
|
||||
}
|
||||
|
||||
func (c *controllerTestSuite) TestEnsureTag() {
|
||||
@ -252,7 +253,7 @@ func (c *controllerTestSuite) TestEnsure() {
|
||||
digest := "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180"
|
||||
|
||||
// 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,
|
||||
}, 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("Create").Return(1, 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.repoMgr.AssertExpectations(c.T())
|
||||
c.artMgr.AssertExpectations(c.T())
|
||||
@ -508,7 +509,12 @@ func (c *controllerTestSuite) TestDeleteDeeply() {
|
||||
|
||||
func (c *controllerTestSuite) TestCopy() {
|
||||
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)
|
||||
c.artMgr.On("GetByDigest").Return(nil, ierror.NotFoundError(nil))
|
||||
c.tagMgr.On("List").Return([]*tag.Tag{
|
||||
@ -525,7 +531,7 @@ func (c *controllerTestSuite) TestCopy() {
|
||||
c.abstractor.On("AbstractMetadata").Return(nil)
|
||||
c.artMgr.On("Create").Return(1, 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)
|
||||
}
|
||||
|
||||
@ -538,6 +544,7 @@ func (c *controllerTestSuite) TestListTags() {
|
||||
ArtifactID: 1,
|
||||
},
|
||||
}, nil)
|
||||
c.artMgr.On("Get").Return(&artifact.Artifact{}, nil)
|
||||
tags, err := c.ctl.ListTags(nil, nil, nil)
|
||||
c.Require().Nil(err)
|
||||
c.Len(tags, 1)
|
||||
|
@ -32,8 +32,8 @@ type DAO interface {
|
||||
List(ctx context.Context, query *q.Query) (artifacts []*Artifact, err error)
|
||||
// Get the artifact specified by ID
|
||||
Get(ctx context.Context, id int64) (*Artifact, error)
|
||||
// GetByDigest returns the artifact specified by repository ID and digest
|
||||
GetByDigest(ctx context.Context, repositoryID int64, digest string) (artifact *Artifact, err error)
|
||||
// GetByDigest returns the artifact specified by repository and digest
|
||||
GetByDigest(ctx context.Context, repository, digest string) (artifact *Artifact, err error)
|
||||
// Create the artifact
|
||||
Create(ctx context.Context, artifact *Artifact) (id int64, err error)
|
||||
// Delete the artifact specified by ID
|
||||
@ -118,11 +118,11 @@ func (d *dao) Get(ctx context.Context, id int64) (*Artifact, error) {
|
||||
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{
|
||||
Keywords: map[string]interface{}{
|
||||
"RepositoryID": repositoryID,
|
||||
"Digest": digest,
|
||||
"RepositoryName": repository,
|
||||
"Digest": digest,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@ -134,7 +134,7 @@ func (d *dao) GetByDigest(ctx context.Context, repositoryID int64, digest string
|
||||
}
|
||||
if len(artifacts) == 0 {
|
||||
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
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ func (d *daoTestSuite) SetupTest() {
|
||||
ManifestMediaType: v1.MediaTypeImageIndex,
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
RepositoryName: "library/hello-world",
|
||||
Digest: "parent_digest",
|
||||
PushTime: now,
|
||||
PullTime: now,
|
||||
@ -72,6 +73,7 @@ func (d *daoTestSuite) SetupTest() {
|
||||
ManifestMediaType: v1.MediaTypeImageManifest,
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
RepositoryName: "library/hello-world",
|
||||
Digest: "child_digest_01",
|
||||
Size: 1024,
|
||||
PushTime: now,
|
||||
@ -88,6 +90,7 @@ func (d *daoTestSuite) SetupTest() {
|
||||
ManifestMediaType: v1.MediaTypeImageManifest,
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
RepositoryName: "library/hello-world",
|
||||
Digest: "child_digest_02",
|
||||
Size: 1024,
|
||||
PushTime: now,
|
||||
@ -324,12 +327,12 @@ func (d *daoTestSuite) TestGet() {
|
||||
|
||||
func (d *daoTestSuite) TestGetByDigest() {
|
||||
// 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.True(ierror.IsErr(err, ierror.NotFoundCode))
|
||||
|
||||
// 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().NotNil(artifact)
|
||||
d.Equal(d.childArt02ID, artifact.ID)
|
||||
|
@ -33,6 +33,7 @@ type Artifact struct {
|
||||
ManifestMediaType string `orm:"column(manifest_media_type)"` // the media type of manifest/index
|
||||
ProjectID int64 `orm:"column(project_id)"` // needed for quota
|
||||
RepositoryID int64 `orm:"column(repository_id)"`
|
||||
RepositoryName string `orm:"column(repository_name)"`
|
||||
Digest string `orm:"column(digest)"`
|
||||
Size int64 `orm:"column(size)"`
|
||||
PushTime time.Time `orm:"column(push_time)"`
|
||||
|
@ -37,8 +37,8 @@ type Manager interface {
|
||||
List(ctx context.Context, query *q.Query) (artifacts []*Artifact, err error)
|
||||
// Get the artifact specified by the ID
|
||||
Get(ctx context.Context, id int64) (artifact *Artifact, err error)
|
||||
// GetByDigest returns the artifact specified by repository ID and digest
|
||||
GetByDigest(ctx context.Context, repositoryID int64, digest string) (artifact *Artifact, err error)
|
||||
// GetByDigest returns the artifact specified by repository and digest
|
||||
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
|
||||
// already exist
|
||||
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)
|
||||
}
|
||||
|
||||
func (m *manager) GetByDigest(ctx context.Context, repositoryID int64, digest string) (*Artifact, error) {
|
||||
art, err := m.dao.GetByDigest(ctx, repositoryID, digest)
|
||||
func (m *manager) GetByDigest(ctx context.Context, repository, digest string) (*Artifact, error) {
|
||||
art, err := m.dao.GetByDigest(ctx, repository, digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ func (f *fakeDao) Get(ctx context.Context, id int64) (*dao.Artifact, error) {
|
||||
args := f.Called()
|
||||
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()
|
||||
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("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().NotNil(artifact)
|
||||
m.Equal(art.ID, artifact.ID)
|
||||
|
@ -33,6 +33,7 @@ type Artifact struct {
|
||||
ManifestMediaType string `json:"manifest_media_type"` // the media type of manifest/index
|
||||
ProjectID int64 `json:"project_id"`
|
||||
RepositoryID int64 `json:"repository_id"`
|
||||
RepositoryName string `json:"repository_name"`
|
||||
Digest string `json:"digest"`
|
||||
Size int64 `json:"size"`
|
||||
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
|
||||
Annotations map[string]string `json:"annotations"`
|
||||
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
|
||||
@ -52,6 +51,7 @@ func (a *Artifact) From(art *dao.Artifact) {
|
||||
a.ManifestMediaType = art.ManifestMediaType
|
||||
a.ProjectID = art.ProjectID
|
||||
a.RepositoryID = art.RepositoryID
|
||||
a.RepositoryName = art.RepositoryName
|
||||
a.Digest = art.Digest
|
||||
a.Size = art.Size
|
||||
a.PushTime = art.PushTime
|
||||
@ -79,6 +79,7 @@ func (a *Artifact) To() *dao.Artifact {
|
||||
ManifestMediaType: a.ManifestMediaType,
|
||||
ProjectID: a.ProjectID,
|
||||
RepositoryID: a.RepositoryID,
|
||||
RepositoryName: a.RepositoryName,
|
||||
Digest: a.Digest,
|
||||
Size: a.Size,
|
||||
PushTime: a.PushTime,
|
||||
|
@ -173,7 +173,7 @@ func (suite *DaoTestSuite) TestFindBlobsShouldUnassociatedWithProject() {
|
||||
artifact1 := 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, artifact2, projectID, 10)
|
||||
|
||||
|
@ -92,7 +92,7 @@ func (suite *ManagerTestSuite) TestCleanupAssociationsForProject() {
|
||||
artifact1 := 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, artifact2, projectID, 10)
|
||||
|
||||
|
@ -110,15 +110,16 @@ func (suite *HandlerSuite) addProject(projectName string) int64 {
|
||||
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{
|
||||
Type: "Docker-Image",
|
||||
ProjectID: pid,
|
||||
RepositoryID: repositoryID,
|
||||
Digest: dgt,
|
||||
Size: 1024,
|
||||
PushTime: time.Now(),
|
||||
PullTime: time.Now(),
|
||||
Type: "Docker-Image",
|
||||
ProjectID: pid,
|
||||
RepositoryID: repositoryID,
|
||||
RepositoryName: repositoryName,
|
||||
Digest: dgt,
|
||||
Size: 1024,
|
||||
PushTime: time.Now(),
|
||||
PullTime: time.Now(),
|
||||
}
|
||||
afid, err := artifact.Mgr.Create(ctx, af)
|
||||
suite.Nil(err, fmt.Sprintf("Add artifact failed for %d", repositoryID))
|
||||
@ -185,7 +186,7 @@ func (suite *HandlerSuite) TestPutDeleteManifestCreated() {
|
||||
projectID := suite.addProject(projectName)
|
||||
immuRuleID := suite.addImmutableRule(projectID)
|
||||
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")
|
||||
|
||||
defer func() {
|
||||
|
@ -70,7 +70,7 @@ func putManifest(w http.ResponseWriter, req *http.Request) {
|
||||
reference := router.Param(req.Context(), ":reference")
|
||||
|
||||
// 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 {
|
||||
serror.SendError(w, err)
|
||||
return
|
||||
@ -98,7 +98,7 @@ func putManifest(w http.ResponseWriter, req *http.Request) {
|
||||
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 {
|
||||
serror.SendError(w, err)
|
||||
return
|
||||
|
@ -81,11 +81,7 @@ func (a *artifactAPI) ListArtifacts(ctx context.Context, params operation.ListAr
|
||||
if params.PageSize != nil {
|
||||
query.PageSize = *(params.PageSize)
|
||||
}
|
||||
repository, err := a.repoCtl.GetByName(ctx, fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName))
|
||||
if err != nil {
|
||||
return a.SendError(ctx, err)
|
||||
}
|
||||
query.Keywords["RepositoryID"] = repository.RepositoryID
|
||||
query.Keywords["RepositoryName"] = fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName)
|
||||
|
||||
// set option
|
||||
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 {
|
||||
return a.SendError(ctx, err)
|
||||
}
|
||||
srcRepo, srcRef, err := parse(params.From)
|
||||
|
||||
srcRepo, ref, err := parse(params.From)
|
||||
if err != nil {
|
||||
return a.SendError(ctx, err)
|
||||
}
|
||||
|
||||
srcPro, _ := utils.ParseRepository(srcRepo)
|
||||
if err = a.RequireProjectAccess(ctx, srcPro, rbac.ActionRead, rbac.ResourceArtifact); err != nil {
|
||||
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 {
|
||||
return a.SendError(ctx, err)
|
||||
}
|
||||
_, id, err := a.repoCtl.Ensure(ctx, params.ProjectName+"/"+params.RepositoryName)
|
||||
if err != nil {
|
||||
return a.SendError(ctx, err)
|
||||
}
|
||||
id, err = a.artCtl.Copy(ctx, srcArt.ID, id)
|
||||
|
||||
id, err = a.artCtl.Copy(ctx, srcRepo, ref, dstRepo)
|
||||
if err != nil {
|
||||
return a.SendError(ctx, err)
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ type FakeController struct {
|
||||
}
|
||||
|
||||
// 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()
|
||||
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 ...
|
||||
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()
|
||||
return int64(args.Int(0)), args.Error(1)
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ func (f *FakeManager) Get(ctx context.Context, id int64) (*artifact.Artifact, er
|
||||
}
|
||||
|
||||
// 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()
|
||||
var art *artifact.Artifact
|
||||
if args.Get(0) != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user