mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-26 04:05:40 +01:00
set the immutable status on getting/listting tag
Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
parent
5cf0481b51
commit
596a6261ca
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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{}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
17
src/testing/pkg/immutabletag/matcher.go
Normal file
17
src/testing/pkg/immutabletag/matcher.go
Normal 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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user