set the immutable status on getting/listting tag

Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
wang yan 2020-02-04 17:34:56 +08:00
parent 5cf0481b51
commit 596a6261ca
14 changed files with 99 additions and 42 deletions

View File

@ -433,6 +433,9 @@ definitions:
type: string
format: date-time
description: The latest pull time of the tag
immutable:
type: boolean
description: The immutable status of the tag
ExtraAttrs:
type: object
additionalProperties:

View File

@ -18,6 +18,10 @@ import (
"context"
"fmt"
"github.com/goharbor/harbor/src/api/artifact/abstractor"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/pkg/art"
"github.com/goharbor/harbor/src/pkg/immutabletag/match"
"github.com/goharbor/harbor/src/pkg/immutabletag/match/rule"
"github.com/opencontainers/go-digest"
// registry image resolvers
_ "github.com/goharbor/harbor/src/api/artifact/abstractor/resolver/image"
@ -76,20 +80,22 @@ type Controller interface {
// NewController creates an instance of the default artifact controller
func NewController() Controller {
return &controller{
repoMgr: repository.Mgr,
artMgr: artifact.Mgr,
tagMgr: tag.Mgr,
abstractor: abstractor.NewAbstractor(),
repoMgr: repository.Mgr,
artMgr: artifact.Mgr,
tagMgr: tag.Mgr,
abstractor: abstractor.NewAbstractor(),
immutableMtr: rule.NewRuleMatcher(),
}
}
// TODO concurrency summary
type controller struct {
repoMgr repository.Manager
artMgr artifact.Manager
tagMgr tag.Manager
abstractor abstractor.Abstractor
repoMgr repository.Manager
artMgr artifact.Manager
tagMgr tag.Manager
abstractor abstractor.Abstractor
immutableMtr match.ImmutableTagMatcher
}
func (c *controller) Ensure(ctx context.Context, repositoryID int64, digest string, tags ...string) (bool, int64, error) {
@ -304,7 +310,7 @@ func (c *controller) ListTags(ctx context.Context, query *q.Query, option *TagOp
}
func (c *controller) DeleteTag(ctx context.Context, tagID int64) error {
// immutable checking is covered in middleware
// Immutable checking is covered in middleware
// TODO check signature
// TODO delete label
// TODO fire delete tag event
@ -374,8 +380,28 @@ func (c *controller) assembleTag(ctx context.Context, tag *tm.Tag, option *TagOp
return t
}
if option.WithImmutableStatus {
// TODO populate immutable status
repo, err := c.repoMgr.Get(ctx, tag.RepositoryID)
if err != nil {
log.Error(err)
} else {
t.Immutable = c.isImmutable(repo.ProjectID, repo.Name, tag.Name)
}
}
// TODO populate signature on tag level?
return t
}
// check whether the tag is Immutable
func (c *controller) isImmutable(projectID int64, repo string, tag string) bool {
_, repoName := utils.ParseRepository(repo)
matched, err := c.immutableMtr.Match(projectID, art.Candidate{
Repository: repoName,
Tag: tag,
NamespaceID: projectID,
})
if err != nil {
log.Error(err)
return false
}
return matched
}

View File

@ -22,6 +22,7 @@ import (
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/tag/model/tag"
arttesting "github.com/goharbor/harbor/src/testing/pkg/artifact"
immutesting "github.com/goharbor/harbor/src/testing/pkg/immutabletag"
repotesting "github.com/goharbor/harbor/src/testing/pkg/repository"
tagtesting "github.com/goharbor/harbor/src/testing/pkg/tag"
"github.com/stretchr/testify/mock"
@ -41,11 +42,12 @@ func (f *fakeAbstractor) Abstract(ctx context.Context, artifact *artifact.Artifa
type controllerTestSuite struct {
suite.Suite
ctl *controller
repoMgr *repotesting.FakeManager
artMgr *arttesting.FakeManager
tagMgr *tagtesting.FakeManager
abstractor *fakeAbstractor
ctl *controller
repoMgr *repotesting.FakeManager
artMgr *arttesting.FakeManager
tagMgr *tagtesting.FakeManager
abstractor *fakeAbstractor
immutableMtr *immutesting.FakeMatcher
}
func (c *controllerTestSuite) SetupTest() {
@ -53,11 +55,13 @@ func (c *controllerTestSuite) SetupTest() {
c.artMgr = &arttesting.FakeManager{}
c.tagMgr = &tagtesting.FakeManager{}
c.abstractor = &fakeAbstractor{}
c.immutableMtr = &immutesting.FakeMatcher{}
c.ctl = &controller{
repoMgr: c.repoMgr,
artMgr: c.artMgr,
tagMgr: c.tagMgr,
abstractor: c.abstractor,
repoMgr: c.repoMgr,
artMgr: c.artMgr,
tagMgr: c.tagMgr,
abstractor: c.abstractor,
immutableMtr: c.immutableMtr,
}
}
@ -74,9 +78,16 @@ 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)
c.Require().NotNil(tag)
c.Equal(tag.ID, tg.ID)
c.Equal(true, tag.Immutable)
// TODO check other fields of option
}
@ -392,8 +403,8 @@ func (c *controllerTestSuite) TestListTags() {
{
ID: 1,
RepositoryID: 1,
ArtifactID: 1,
Name: "latest",
ArtifactID: 1,
},
}, nil)
total, tags, err := c.ctl.ListTags(nil, nil, nil)
@ -401,6 +412,7 @@ func (c *controllerTestSuite) TestListTags() {
c.Equal(int64(1), total)
c.Len(tags, 1)
c.tagMgr.AssertExpectations(c.T())
c.Equal(tags[0].Immutable, false)
// TODO check other properties: label, etc
}

View File

@ -69,6 +69,7 @@ func (a *Artifact) ToSwagger() *models.Artifact {
PullTime: strfmt.DateTime(tag.PullTime),
PushTime: strfmt.DateTime(tag.PushTime),
RepositoryID: tag.RepositoryID,
Immutable: tag.Immutable,
})
}
for resource, links := range a.SubResourceLinks {
@ -88,7 +89,8 @@ func (a *Artifact) ToSwagger() *models.Artifact {
// Tag is the overall view of tag
type Tag struct {
tag.Tag
// TODO add other attrs: signature, label, immutable status, etc
Immutable bool
// TODO add other attrs: signature, label, etc
}
// Resource defines the specific resource of different artifacts: build history for image, values.yaml for chart, etc

View File

@ -825,7 +825,7 @@ func populateAuthor(detail *models.TagDetail) {
// check whether the tag is immutable
func isImmutable(projectID int64, repo string, tag string) bool {
_, repoName := utils.ParseRepository(repo)
matched, err := rule.NewRuleMatcher(projectID).Match(art.Candidate{
matched, err := rule.NewRuleMatcher().Match(projectID, art.Candidate{
Repository: repoName,
Tag: tag,
NamespaceID: projectID,

View File

@ -45,7 +45,7 @@ func (dmf *delmfInterceptor) HandleRequest(req *http.Request) (err error) {
for _, af := range afs {
_, repoName := common_util.ParseRepository(dmf.mf.Repository)
var matched bool
matched, err = rule.NewRuleMatcher(dmf.mf.ProjectID).Match(art.Candidate{
matched, err = rule.NewRuleMatcher().Match(dmf.mf.ProjectID, art.Candidate{
Repository: repoName,
Tag: af.Tag,
NamespaceID: dmf.mf.ProjectID,

View File

@ -29,7 +29,7 @@ func (pmf *pushmfInterceptor) HandleRequest(req *http.Request) (err error) {
_, repoName := common_util.ParseRepository(pmf.mf.Repository)
var matched bool
matched, err = rule.NewRuleMatcher(pmf.mf.ProjectID).Match(art.Candidate{
matched, err = rule.NewRuleMatcher().Match(pmf.mf.ProjectID, art.Candidate{
Repository: repoName,
Tag: pmf.mf.Tag,
NamespaceID: pmf.mf.ProjectID,

View File

@ -7,5 +7,5 @@ import (
// ImmutableTagMatcher ...
type ImmutableTagMatcher interface {
// Match whether the candidate is in the immutable list
Match(c art.Candidate) (bool, error)
Match(pid int64, c art.Candidate) (bool, error)
}

View File

@ -10,13 +10,12 @@ import (
// Matcher ...
type Matcher struct {
pid int64
rules []model.Metadata
}
// Match ...
func (rm *Matcher) Match(c art.Candidate) (bool, error) {
if err := rm.getImmutableRules(); err != nil {
func (rm *Matcher) Match(pid int64, c art.Candidate) (bool, error) {
if err := rm.getImmutableRules(pid); err != nil {
return false, err
}
@ -71,8 +70,8 @@ func (rm *Matcher) Match(c art.Candidate) (bool, error) {
return false, nil
}
func (rm *Matcher) getImmutableRules() error {
rules, err := immutabletag.ImmuCtr.ListImmutableRules(rm.pid)
func (rm *Matcher) getImmutableRules(pid int64) error {
rules, err := immutabletag.ImmuCtr.ListImmutableRules(pid)
if err != nil {
return err
}
@ -81,8 +80,6 @@ func (rm *Matcher) getImmutableRules() error {
}
// NewRuleMatcher ...
func NewRuleMatcher(pid int64) match.ImmutableTagMatcher {
return &Matcher{
pid: pid,
}
func NewRuleMatcher() match.ImmutableTagMatcher {
return &Matcher{}
}

View File

@ -85,7 +85,7 @@ func (s *MatchTestSuite) TestImmuMatch() {
s.ruleID2 = id
s.require.Nil(err)
match := NewRuleMatcher(1)
match := NewRuleMatcher()
c1 := art.Candidate{
NamespaceID: 1,
@ -93,7 +93,7 @@ func (s *MatchTestSuite) TestImmuMatch() {
Repository: "redis",
Tag: "release-1.10",
}
isMatch, err := match.Match(c1)
isMatch, err := match.Match(1, c1)
s.require.Equal(isMatch, true)
s.require.Nil(err)
@ -104,7 +104,7 @@ func (s *MatchTestSuite) TestImmuMatch() {
Tag: "1.10",
Kind: art.Image,
}
isMatch, err = match.Match(c2)
isMatch, err = match.Match(1, c2)
s.require.Equal(isMatch, false)
s.require.Nil(err)
@ -115,7 +115,7 @@ func (s *MatchTestSuite) TestImmuMatch() {
Tag: "9.4.8",
Kind: art.Image,
}
isMatch, err = match.Match(c3)
isMatch, err = match.Match(1, c3)
s.require.Equal(isMatch, true)
s.require.Nil(err)
@ -126,7 +126,7 @@ func (s *MatchTestSuite) TestImmuMatch() {
Tag: "world",
Kind: art.Image,
}
isMatch, err = match.Match(c4)
isMatch, err = match.Match(1, c4)
s.require.Equal(isMatch, false)
s.require.Nil(err)
}

View File

@ -104,7 +104,7 @@ func isImmutable(c *art.Candidate) bool {
repo := c.Repository
tag := c.Tag
_, repoName := utils.ParseRepository(repo)
matched, err := rule.NewRuleMatcher(projectID).Match(art.Candidate{
matched, err := rule.NewRuleMatcher().Match(projectID, art.Candidate{
Repository: repoName,
Tag: tag,
NamespaceID: projectID,

View File

@ -84,7 +84,7 @@ func handleDelete(req *http.Request) error {
for _, tag := range tags {
var matched bool
matched, err = rule.NewRuleMatcher(mf.ProjectID).Match(art.Candidate{
matched, err = rule.NewRuleMatcher().Match(mf.ProjectID, art.Candidate{
Repository: repoName,
Tag: tag.Name,
NamespaceID: mf.ProjectID,

View File

@ -47,7 +47,7 @@ func handlePush(req *http.Request) error {
_, repoName := common_util.ParseRepository(mf.Repository)
var matched bool
matched, err := rule.NewRuleMatcher(mf.ProjectID).Match(art.Candidate{
matched, err := rule.NewRuleMatcher().Match(mf.ProjectID, art.Candidate{
Repository: repoName,
Tag: mf.Tag,
NamespaceID: mf.ProjectID,

View File

@ -0,0 +1,17 @@
package immutabletag
import (
"github.com/goharbor/harbor/src/pkg/art"
"github.com/stretchr/testify/mock"
)
// FakeMatcher ...
type FakeMatcher struct {
mock.Mock
}
// Match ...
func (f *FakeMatcher) Match(pid int64, c art.Candidate) (bool, error) {
args := f.Called()
return args.Bool(0), args.Error(1)
}