mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-25 00:51:24 +01:00
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:
parent
7857528e45
commit
02c2647e1e
@ -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