diff --git a/src/api/artifact/controller.go b/src/api/artifact/controller.go index 1af26ca6c..b6c49877d 100644 --- a/src/api/artifact/controller.go +++ b/src/api/artifact/controller.go @@ -274,30 +274,76 @@ func (c *controller) getByTag(ctx context.Context, repository, tag string, optio } func (c *controller) Delete(ctx context.Context, id int64) error { - // remove labels added to the artifact - if err := c.labelMgr.RemoveAllFrom(ctx, id); err != nil { + return c.deleteDeeply(ctx, id, true) +} + +// "isRoot" is used to specify whether the artifact is the root parent artifact +// the error handling logic for the root parent artifact and others is different +func (c *controller) deleteDeeply(ctx context.Context, id int64, isRoot bool) error { + art, err := c.Get(ctx, id, &Option{WithTag: true}) + if err != nil { + // return nil if the nonexistent artifact isn't the root parent + if !isRoot && ierror.IsErr(err, ierror.NotFoundCode) { + return nil + } return err } - // delete all tags that attached to the artifact - _, tags, err := c.tagMgr.List(ctx, &q.Query{ + // the child artifact is referenced by some tags, skip + if !isRoot && len(art.Tags) > 0 { + return nil + } + parents, err := c.artMgr.ListReferences(ctx, &q.Query{ Keywords: map[string]interface{}{ - "artifact_id": id, + "ChildID": id, }, }) if err != nil { return err } - for _, tag := range tags { - if err = c.DeleteTag(ctx, tag.ID); err != nil { + if len(parents) > 0 { + // the root artifact is referenced by other artifacts + if isRoot { + return ierror.New(nil).WithCode(ierror.ViolateForeignKeyConstraintCode). + WithMessage("the deleting artifact is referenced by others") + } + // the child artifact is referenced by other artifacts, skip + return nil + } + // delete child artifacts if contains any + for _, reference := range art.References { + // delete reference + if err = c.artMgr.DeleteReference(ctx, reference.ID); err != nil && + !ierror.IsErr(err, ierror.NotFoundCode) { + return err + } + if err = c.deleteDeeply(ctx, reference.ChildID, false); err != nil { return err } } - if err := c.artMgr.Delete(ctx, id); err != nil { + // delete all tags that attached to the root artifact + if isRoot { + if err = c.tagMgr.DeleteOfArtifact(ctx, id); err != nil { + return err + } + } + + // remove labels added to the artifact + if err := c.labelMgr.RemoveAllFrom(ctx, id); err != nil { + return err + } + + // delete the artifact itself + if err = c.artMgr.Delete(ctx, art.ID); err != nil { + // the child artifact doesn't exist, skip + if !isRoot && ierror.IsErr(err, ierror.NotFoundCode) { + return nil + } return err } // TODO fire delete artifact event + return nil } @@ -361,6 +407,8 @@ func (c *controller) assembleArtifact(ctx context.Context, art *artifact.Artifac artifact := &Artifact{ Artifact: *art, } + // populate addition links + c.populateAdditionLinks(ctx, artifact) if option == nil { return artifact } diff --git a/src/api/artifact/controller_test.go b/src/api/artifact/controller_test.go index 5ad9fe0b2..0f462bc9d 100644 --- a/src/api/artifact/controller_test.go +++ b/src/api/artifact/controller_test.go @@ -277,7 +277,6 @@ func (c *controllerTestSuite) TestList() { c.repoMgr.On("Get").Return(&models.RepoRecord{ Name: "library/hello-world", }, nil) - c.abstractor.On("ListSupportedAdditions").Return([]string{"BUILD_HISTORY"}) total, artifacts, err := c.ctl.List(nil, query, option) c.Require().Nil(err) c.Equal(int64(1), total) @@ -292,7 +291,7 @@ func (c *controllerTestSuite) TestGet() { ID: 1, RepositoryID: 1, }, nil) - c.abstractor.On("ListSupportedAdditions").Return([]string{"BUILD_HISTORY"}) + c.repoMgr.On("Get").Return(&models.RepoRecord{}, nil) art, err := c.ctl.Get(nil, 1, nil) c.Require().Nil(err) c.Require().NotNil(art) @@ -305,7 +304,6 @@ func (c *controllerTestSuite) TestGetByDigest() { RepositoryID: 1, }, nil) c.artMgr.On("GetByDigest").Return(nil, ierror.NotFoundError(nil)) - c.abstractor.On("ListSupportedAdditions").Return([]string{"BUILD_HISTORY"}) art, err := c.ctl.getByDigest(nil, "library/hello-world", "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180", nil) c.Require().NotNil(err) @@ -322,7 +320,7 @@ func (c *controllerTestSuite) TestGetByDigest() { ID: 1, RepositoryID: 1, }, nil) - c.abstractor.On("ListSupportedAdditions").Return([]string{"BUILD_HISTORY"}) + c.repoMgr.On("Get").Return(&models.RepoRecord{}, nil) art, err = c.ctl.getByDigest(nil, "library/hello-world", "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180", nil) c.Require().Nil(err) @@ -336,7 +334,6 @@ func (c *controllerTestSuite) TestGetByTag() { RepositoryID: 1, }, nil) c.tagMgr.On("List").Return(0, nil, nil) - c.abstractor.On("ListSupportedAdditions").Return([]string{"BUILD_HISTORY"}) art, err := c.ctl.getByTag(nil, "library/hello-world", "latest", nil) c.Require().NotNil(err) c.True(ierror.IsErr(err, ierror.NotFoundCode)) @@ -359,7 +356,7 @@ func (c *controllerTestSuite) TestGetByTag() { c.artMgr.On("Get").Return(&artifact.Artifact{ ID: 1, }, nil) - c.abstractor.On("ListSupportedAdditions").Return([]string{"BUILD_HISTORY"}) + c.repoMgr.On("Get").Return(&models.RepoRecord{}, nil) art, err = c.ctl.getByTag(nil, "library/hello-world", "latest", nil) c.Require().Nil(err) c.Require().NotNil(art) @@ -375,7 +372,7 @@ func (c *controllerTestSuite) TestGetByReference() { ID: 1, RepositoryID: 1, }, nil) - c.abstractor.On("ListSupportedAdditions").Return([]string{"BUILD_HISTORY"}) + c.repoMgr.On("Get").Return(&models.RepoRecord{}, nil) art, err := c.ctl.GetByReference(nil, "library/hello-world", "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180", nil) c.Require().Nil(err) @@ -400,26 +397,85 @@ func (c *controllerTestSuite) TestGetByReference() { c.artMgr.On("Get").Return(&artifact.Artifact{ ID: 1, }, nil) - c.abstractor.On("ListSupportedAdditions").Return([]string{"BUILD_HISTORY"}) + c.repoMgr.On("Get").Return(&models.RepoRecord{}, nil) art, err = c.ctl.GetByReference(nil, "library/hello-world", "latest", nil) c.Require().Nil(err) c.Require().NotNil(art) c.Equal(int64(1), art.ID) } -func (c *controllerTestSuite) TestDelete() { - c.artMgr.On("Delete").Return(nil) +func (c *controllerTestSuite) TestDeleteDeeply() { + // root artifact and doesn't exist + c.artMgr.On("Get").Return(nil, ierror.NotFoundError(nil)) + err := c.ctl.deleteDeeply(nil, 1, true) + c.Require().NotNil(err) + c.Assert().True(ierror.IsErr(err, ierror.NotFoundCode)) + + // reset the mock + c.SetupTest() + + // child artifact and doesn't exist + c.artMgr.On("Get").Return(nil, ierror.NotFoundError(nil)) + err = c.ctl.deleteDeeply(nil, 1, false) + c.Require().Nil(err) + + // reset the mock + c.SetupTest() + + // child artifact and contains tags + c.artMgr.On("Get").Return(&artifact.Artifact{ID: 1}, nil) c.tagMgr.On("List").Return(0, []*tag.Tag{ { ID: 1, }, }, nil) - c.tagMgr.On("Delete").Return(nil) - c.labelMgr.On("RemoveAllFrom").Return(nil) - err := c.ctl.Delete(nil, 1) + c.repoMgr.On("Get").Return(&models.RepoRecord{}, nil) + err = c.ctl.deleteDeeply(nil, 1, false) + c.Require().Nil(err) + + // reset the mock + c.SetupTest() + + // root artifact is referenced by other artifacts + c.artMgr.On("Get").Return(&artifact.Artifact{ID: 1}, nil) + c.tagMgr.On("List").Return(0, nil, nil) + c.repoMgr.On("Get").Return(&models.RepoRecord{}, nil) + c.artMgr.On("ListReferences").Return([]*artifact.Reference{ + { + ID: 1, + }, + }, nil) + err = c.ctl.deleteDeeply(nil, 1, true) + c.Require().NotNil(err) + + // reset the mock + c.SetupTest() + + // child artifact contains no tag but referenced by other artifacts + c.artMgr.On("Get").Return(&artifact.Artifact{ID: 1}, nil) + c.tagMgr.On("List").Return(0, nil, nil) + c.repoMgr.On("Get").Return(&models.RepoRecord{}, nil) + c.artMgr.On("ListReferences").Return([]*artifact.Reference{ + { + ID: 1, + }, + }, nil) + err = c.ctl.deleteDeeply(nil, 1, false) + c.Require().Nil(err) + + // reset the mock + c.SetupTest() + + // root artifact is referenced by other artifacts + c.artMgr.On("Get").Return(&artifact.Artifact{ID: 1}, nil) + c.tagMgr.On("List").Return(0, nil, nil) + c.repoMgr.On("Get").Return(&models.RepoRecord{}, nil) + c.artMgr.On("ListReferences").Return(nil, nil) + c.tagMgr.On("DeleteOfArtifact").Return(nil) + c.artMgr.On("Delete").Return(nil) + c.labelMgr.On("RemoveAllFrom").Return(nil) + err = c.ctl.deleteDeeply(nil, 1, true) c.Require().Nil(err) - c.artMgr.AssertExpectations(c.T()) - c.tagMgr.AssertExpectations(c.T()) } func (c *controllerTestSuite) TestListTags() { diff --git a/src/api/artifact/model.go b/src/api/artifact/model.go index b5c424709..7a1f5386d 100644 --- a/src/api/artifact/model.go +++ b/src/api/artifact/model.go @@ -24,22 +24,22 @@ import ( // Artifact is the overall view of artifact type Artifact struct { artifact.Artifact - Tags []*Tag // the list of tags that attached to the artifact - AdditionLinks map[string]*AdditionLink // the resource link for build history(image), values.yaml(chart), dependency(chart), etc - Labels []*cmodels.Label + Tags []*Tag `json:"tags"` // the list of tags that attached to the artifact + AdditionLinks map[string]*AdditionLink `json:"addition_links"` // the resource link for build history(image), values.yaml(chart), dependency(chart), etc + Labels []*cmodels.Label `json:"labels"` } // Tag is the overall view of tag type Tag struct { tag.Tag - Immutable bool - Signed bool + Immutable bool `json:"immutable"` + Signed bool `json:"signed"` } // AdditionLink is a link via that the addition can be fetched type AdditionLink struct { - HREF string - Absolute bool // specify the href is an absolute URL or not + HREF string `json:"href"` + Absolute bool `json:"absolute"` // specify the href is an absolute URL or not } // Option is used to specify the properties returned when listing/getting artifacts diff --git a/src/pkg/artifact/dao/dao.go b/src/pkg/artifact/dao/dao.go index 0a9969b49..ada009aa1 100644 --- a/src/pkg/artifact/dao/dao.go +++ b/src/pkg/artifact/dao/dao.go @@ -43,6 +43,8 @@ type DAO interface { CreateReference(ctx context.Context, reference *ArtifactReference) (id int64, err error) // ListReferences lists the artifact references according to the query ListReferences(ctx context.Context, query *q.Query) (references []*ArtifactReference, err error) + // DeleteReference specified by ID + DeleteReference(ctx context.Context, id int64) (err error) // DeleteReferences deletes the references referenced by the artifact specified by parent ID DeleteReferences(ctx context.Context, parentID int64) (err error) } @@ -213,6 +215,22 @@ func (d *dao) ListReferences(ctx context.Context, query *q.Query) ([]*ArtifactRe } return references, nil } + +func (d *dao) DeleteReference(ctx context.Context, id int64) error { + ormer, err := orm.FromContext(ctx) + if err != nil { + return err + } + n, err := ormer.Delete(&ArtifactReference{ID: id}) + if err != nil { + return err + } + if n == 0 { + return ierror.NotFoundError(nil).WithMessage("artifact reference %d not found", id) + } + return nil +} + func (d *dao) DeleteReferences(ctx context.Context, parentID int64) error { // make sure the parent artifact exist _, err := d.Get(ctx, parentID) diff --git a/src/pkg/artifact/dao/dao_test.go b/src/pkg/artifact/dao/dao_test.go index f516ec753..e952500e3 100644 --- a/src/pkg/artifact/dao/dao_test.go +++ b/src/pkg/artifact/dao/dao_test.go @@ -425,6 +425,13 @@ func (d *daoTestSuite) TestListReferences() { d.Equal(d.reference01ID, references[0].ID) } +func (d *daoTestSuite) TestDeleteReference() { + // not exist + err := d.dao.DeleteReference(d.ctx, 10000) + d.Require().NotNil(err) + d.True(ierror.IsErr(err, ierror.NotFoundCode)) +} + func (d *daoTestSuite) TestDeleteReferences() { // happy pass is covered in TearDownTest diff --git a/src/pkg/artifact/manager.go b/src/pkg/artifact/manager.go index f522afbf1..007abf8df 100644 --- a/src/pkg/artifact/manager.go +++ b/src/pkg/artifact/manager.go @@ -16,9 +16,10 @@ package artifact import ( "context" + "time" + "github.com/goharbor/harbor/src/pkg/artifact/dao" "github.com/goharbor/harbor/src/pkg/q" - "time" ) var ( @@ -43,6 +44,10 @@ type Manager interface { Delete(ctx context.Context, id int64) (err error) // UpdatePullTime updates the pull time of the artifact UpdatePullTime(ctx context.Context, artifactID int64, time time.Time) (err error) + // ListReferences according to the query + ListReferences(ctx context.Context, query *q.Query) (references []*Reference, err error) + // DeleteReference specified by ID + DeleteReference(ctx context.Context, id int64) (err error) } // NewManager returns an instance of the default manager @@ -122,29 +127,43 @@ func (m *manager) UpdatePullTime(ctx context.Context, artifactID int64, time tim }, "PullTime") } +func (m *manager) ListReferences(ctx context.Context, query *q.Query) ([]*Reference, error) { + references, err := m.dao.ListReferences(ctx, query) + if err != nil { + return nil, err + } + var refs []*Reference + for _, reference := range references { + ref := &Reference{} + ref.From(reference) + art, err := m.dao.Get(ctx, reference.ChildID) + if err != nil { + return nil, err + } + ref.ChildDigest = art.Digest + refs = append(refs, ref) + } + return refs, nil +} + +func (m *manager) DeleteReference(ctx context.Context, id int64) error { + return m.dao.DeleteReference(ctx, id) +} + // assemble the artifact with references populated func (m *manager) assemble(ctx context.Context, art *dao.Artifact) (*Artifact, error) { artifact := &Artifact{} // convert from database object artifact.From(art) // populate the references - refs, err := m.dao.ListReferences(ctx, &q.Query{ + references, err := m.ListReferences(ctx, &q.Query{ Keywords: map[string]interface{}{ - "parent_id": artifact.ID, + "ParentID": artifact.ID, }, }) if err != nil { return nil, err } - for _, ref := range refs { - reference := &Reference{} - reference.From(ref) - art, err := m.dao.Get(ctx, reference.ChildID) - if err != nil { - return nil, err - } - reference.ChildDigest = art.Digest - artifact.References = append(artifact.References, reference) - } + artifact.References = references return artifact, nil } diff --git a/src/pkg/artifact/manager_test.go b/src/pkg/artifact/manager_test.go index a48ecb542..22d275d78 100644 --- a/src/pkg/artifact/manager_test.go +++ b/src/pkg/artifact/manager_test.go @@ -64,6 +64,10 @@ func (f *fakeDao) ListReferences(ctx context.Context, query *q.Query) ([]*dao.Ar args := f.Called() return args.Get(0).([]*dao.ArtifactReference), args.Error(1) } +func (f *fakeDao) DeleteReference(ctx context.Context, id int64) error { + args := f.Called() + return args.Error(0) +} func (f *fakeDao) DeleteReferences(ctx context.Context, parentID int64) error { args := f.Called() return args.Error(0) @@ -223,6 +227,31 @@ func (m *managerTestSuite) TestUpdatePullTime() { m.dao.AssertExpectations(m.T()) } +func (m *managerTestSuite) TestListReferences() { + m.dao.On("ListReferences").Return([]*dao.ArtifactReference{ + { + ID: 1, + ParentID: 1, + ChildID: 2, + }, + }, nil) + m.dao.On("Get").Return(&dao.Artifact{ + ID: 1, + Digest: "digest", + }, nil) + references, err := m.mgr.ListReferences(nil, nil) + m.Require().Nil(err) + m.Require().Len(references, 1) + m.Equal(int64(1), references[0].ID) + m.Equal("digest", references[0].ChildDigest) +} + +func (m *managerTestSuite) TestDeleteReference() { + m.dao.On("DeleteReference").Return(nil) + err := m.mgr.DeleteReference(nil, 1) + m.Require().Nil(err) +} + func TestManager(t *testing.T) { suite.Run(t, &managerTestSuite{}) } diff --git a/src/pkg/artifact/model.go b/src/pkg/artifact/model.go index 13343b0cf..6eb4787f2 100644 --- a/src/pkg/artifact/model.go +++ b/src/pkg/artifact/model.go @@ -27,19 +27,19 @@ import ( // underlying concrete detail and provides an unified artifact view // for all users. type Artifact struct { - ID int64 - Type string // image, chart, etc - MediaType string // the media type of artifact. Mostly, it's the value of `manifest.config.mediatype` - ManifestMediaType string // the media type of manifest/index - ProjectID int64 - RepositoryID int64 - Digest string - Size int64 - PushTime time.Time - PullTime time.Time - ExtraAttrs map[string]interface{} // only contains the simple attributes specific for the different artifact type, most of them should come from the config layer - Annotations map[string]string - References []*Reference // child artifacts referenced by the parent artifact if the artifact is an index + ID int64 `json:"id"` + Type string `json:"type"` // image, chart, etc + MediaType string `json:"media_type"` // the media type of artifact. Mostly, it's the value of `manifest.config.mediatype` + ManifestMediaType string `json:"manifest_media_type"` // the media type of manifest/index + ProjectID int64 `json:"project_id"` + RepositoryID int64 `json:"repository_id"` + Digest string `json:"digest"` + Size int64 `json:"size"` + PushTime time.Time `json:"push_time"` + PullTime time.Time `json:"pull_time"` + 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 } // From converts the database level artifact to the business level object @@ -101,14 +101,16 @@ func (a *Artifact) To() *dao.Artifact { // Reference records the child artifact referenced by parent artifact type Reference struct { - ParentID int64 - ChildID int64 - ChildDigest string // As we only provide the API based on digest rather than ID, the digest of child artifact is needed + ID int64 `json:"id"` + ParentID int64 `json:"parent_id"` + ChildID int64 `json:"child_id"` + ChildDigest string `json:"child_digest"` // As we only provide the API based on digest rather than ID, the digest of child artifact is needed Platform *v1.Platform } // From converts the data level reference to business level func (r *Reference) From(ref *dao.ArtifactReference) { + r.ID = ref.ID r.ParentID = ref.ParentID r.ChildID = ref.ChildID if len(ref.Platform) > 0 { @@ -122,6 +124,7 @@ func (r *Reference) From(ref *dao.ArtifactReference) { // To converts the reference to data level object func (r *Reference) To() *dao.ArtifactReference { ref := &dao.ArtifactReference{ + ID: r.ID, ParentID: r.ParentID, ChildID: r.ChildID, } diff --git a/src/pkg/tag/dao/dao.go b/src/pkg/tag/dao/dao.go index ed45ffdb2..f3cbf4b86 100644 --- a/src/pkg/tag/dao/dao.go +++ b/src/pkg/tag/dao/dao.go @@ -41,6 +41,8 @@ type DAO interface { Update(ctx context.Context, tag *tag.Tag, props ...string) (err error) // Delete the tag specified by ID Delete(ctx context.Context, id int64) (err error) + // DeleteOfArtifact deletes all tags attached to the artifact + DeleteOfArtifact(ctx context.Context, artifactID int64) (err error) } // New returns an instance of the default DAO @@ -141,3 +143,16 @@ func (d *dao) Delete(ctx context.Context, id int64) error { } return nil } + +func (d *dao) DeleteOfArtifact(ctx context.Context, artifactID int64) error { + qs, err := orm.QuerySetter(ctx, &tag.Tag{}, &q.Query{ + Keywords: map[string]interface{}{ + "ArtifactID": artifactID, + }, + }) + if err != nil { + return err + } + _, err = qs.Delete() + return err +} diff --git a/src/pkg/tag/dao/dao_test.go b/src/pkg/tag/dao/dao_test.go index b92f9dcb8..4ac3df9d6 100644 --- a/src/pkg/tag/dao/dao_test.go +++ b/src/pkg/tag/dao/dao_test.go @@ -61,7 +61,6 @@ func (d *daoTestSuite) TearDownSuite() { } func (d *daoTestSuite) SetupTest() { - tag := &tag.Tag{ RepositoryID: 1000, ArtifactID: d.artifactID, @@ -223,6 +222,53 @@ func (d *daoTestSuite) TestUpdate() { d.Equal(ierror.NotFoundCode, e.Code) } +func (d *daoTestSuite) TestDeleteOfArtifact() { + artifactID, err := d.artDAO.Create(d.ctx, &artdao.Artifact{ + Type: "IMAGE", + MediaType: "application/vnd.oci.image.config.v1+json", + ManifestMediaType: "application/vnd.oci.image.manifest.v1+json", + ProjectID: 1, + RepositoryID: 1000, + Digest: "sha256:digest02", + }) + d.Require().Nil(err) + defer d.artDAO.Delete(d.ctx, artifactID) + + tag1 := &tag.Tag{ + RepositoryID: 1000, + ArtifactID: artifactID, + Name: "tag1", + } + _, err = d.dao.Create(d.ctx, tag1) + d.Require().Nil(err) + tag2 := &tag.Tag{ + RepositoryID: 1000, + ArtifactID: artifactID, + Name: "tag2", + } + _, err = d.dao.Create(d.ctx, tag2) + d.Require().Nil(err) + + tags, err := d.dao.List(d.ctx, &q.Query{ + Keywords: map[string]interface{}{ + "ArtifactID": artifactID, + }, + }) + d.Require().Nil(err) + d.Require().Len(tags, 2) + + err = d.dao.DeleteOfArtifact(d.ctx, artifactID) + d.Require().Nil(err) + + tags, err = d.dao.List(d.ctx, &q.Query{ + Keywords: map[string]interface{}{ + "ArtifactID": artifactID, + }, + }) + d.Require().Nil(err) + d.Require().Len(tags, 0) +} + func TestDaoTestSuite(t *testing.T) { suite.Run(t, &daoTestSuite{}) } diff --git a/src/pkg/tag/manager.go b/src/pkg/tag/manager.go index 891a3c0c4..8cc3762f3 100644 --- a/src/pkg/tag/manager.go +++ b/src/pkg/tag/manager.go @@ -38,6 +38,8 @@ type Manager interface { Update(ctx context.Context, tag *tag.Tag, props ...string) (err error) // Delete the tag specified by ID Delete(ctx context.Context, id int64) (err error) + // DeleteOfArtifact deletes all tags attached to the artifact + DeleteOfArtifact(ctx context.Context, artifactID int64) (err error) } // NewManager creates an instance of the default tag manager @@ -78,3 +80,7 @@ func (m *manager) Update(ctx context.Context, tag *tag.Tag, props ...string) err func (m *manager) Delete(ctx context.Context, id int64) error { return m.dao.Delete(ctx, id) } + +func (m *manager) DeleteOfArtifact(ctx context.Context, artifactID int64) error { + return m.dao.DeleteOfArtifact(ctx, artifactID) +} diff --git a/src/pkg/tag/manager_test.go b/src/pkg/tag/manager_test.go index ff482a12c..ca02e8731 100644 --- a/src/pkg/tag/manager_test.go +++ b/src/pkg/tag/manager_test.go @@ -52,6 +52,10 @@ func (f *fakeDao) Delete(ctx context.Context, id int64) error { args := f.Called() return args.Error(0) } +func (f *fakeDao) DeleteOfArtifact(ctx context.Context, artifactID int64) error { + args := f.Called() + return args.Error(0) +} type managerTestSuite struct { suite.Suite @@ -113,6 +117,12 @@ func (m *managerTestSuite) TestDelete() { m.dao.AssertExpectations(m.T()) } +func (m *managerTestSuite) TestDeleteOfArtifact() { + m.dao.On("DeleteOfArtifact", mock.Anything).Return(nil) + err := m.mgr.DeleteOfArtifact(nil, 1) + m.Require().Nil(err) +} + func TestManager(t *testing.T) { suite.Run(t, &managerTestSuite{}) } diff --git a/src/pkg/tag/model/tag/model.go b/src/pkg/tag/model/tag/model.go index d83c6e558..5e6edb99f 100644 --- a/src/pkg/tag/model/tag/model.go +++ b/src/pkg/tag/model/tag/model.go @@ -20,10 +20,10 @@ import ( // Tag model in database type Tag struct { - ID int64 `orm:"pk;auto;column(id)"` - RepositoryID int64 `orm:"column(repository_id)"` // tags are the resources of repository, one repository only contains one same name tag - ArtifactID int64 `orm:"column(artifact_id)"` // the artifact ID that the tag attaches to, it changes when pushing a same name but different digest artifact - Name string `orm:"column(name)"` - PushTime time.Time `orm:"column(push_time)"` - PullTime time.Time `orm:"column(pull_time)"` + ID int64 `orm:"pk;auto;column(id)" json:"id"` + RepositoryID int64 `orm:"column(repository_id)" json:"repository_id"` // tags are the resources of repository, one repository only contains one same name tag + ArtifactID int64 `orm:"column(artifact_id)" json:"artifact_id"` // the artifact ID that the tag attaches to, it changes when pushing a same name but different digest artifact + Name string `orm:"column(name)" json:"name"` + PushTime time.Time `orm:"column(push_time)" json:"push_time"` + PullTime time.Time `orm:"column(pull_time)" json:"pull_time"` } diff --git a/src/testing/pkg/artifact/manager.go b/src/testing/pkg/artifact/manager.go index fa117202d..db5132995 100644 --- a/src/testing/pkg/artifact/manager.go +++ b/src/testing/pkg/artifact/manager.go @@ -74,3 +74,19 @@ func (f *FakeManager) UpdatePullTime(ctx context.Context, artifactID int64, time args := f.Called() return args.Error(0) } + +// ListReferences ... +func (f *FakeManager) ListReferences(ctx context.Context, query *q.Query) ([]*artifact.Reference, error) { + args := f.Called() + var references []*artifact.Reference + if args.Get(0) != nil { + references = args.Get(0).([]*artifact.Reference) + } + return references, args.Error(1) +} + +// DeleteReference ... +func (f *FakeManager) DeleteReference(ctx context.Context, id int64) error { + args := f.Called() + return args.Error(0) +} diff --git a/src/testing/pkg/tag/manager.go b/src/testing/pkg/tag/manager.go index 133704390..10b0a5fe5 100644 --- a/src/testing/pkg/tag/manager.go +++ b/src/testing/pkg/tag/manager.go @@ -63,3 +63,9 @@ func (f *FakeManager) Delete(ctx context.Context, id int64) error { args := f.Called() return args.Error(0) } + +// DeleteOfArtifact ... +func (f *FakeManager) DeleteOfArtifact(ctx context.Context, artifactID int64) error { + args := f.Called() + return args.Error(0) +}