refactor immutable rule (#14371)

Migrate immutable realted APIs to v2 swagger

Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
Wang Yan 2021-03-08 17:10:12 +08:00 committed by GitHub
parent f0dd9e5aeb
commit 85254ccc22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1011 additions and 1193 deletions

View File

@ -2069,126 +2069,6 @@ paths:
description: User have no permission to list webhook jobs of the project.
'500':
description: Unexpected internal errors.
'/projects/{project_id}/immutabletagrules':
get:
summary: List all immutable tag rules of current project
description: |
This endpoint returns the immutable tag rules of a project
parameters:
- name: project_id
in: path
type: integer
format: int64
required: true
description: Relevant project ID.
tags:
- Products
responses:
'200':
description: List project immutable tag rules successfully.
schema:
type: array
items:
$ref: '#/definitions/ImmutableRule'
'400':
description: Illegal format of provided ID value.
'401':
description: User need to log in first.
'403':
description: User have no permission to list immutable tag rules of the project.
'500':
description: Unexpected internal errors.
post:
summary: Add an immutable tag rule to current project
description: |
This endpoint add an immutable tag rule to the project
parameters:
- name: project_id
in: path
type: integer
format: int64
required: true
description: Relevant project ID.
- name: ImmutableRule
in: body
required: true
schema:
$ref: '#/definitions/ImmutableRule'
tags:
- Products
responses:
'200':
description: Add the immutable tag rule successfully.
'400':
description: Illegal format of provided ID value.
'401':
description: User need to log in first.
'403':
description: User have no permission to get immutable tag rule of the project.
'500':
description: Internal server errors.
'/projects/{project_id}/immutabletagrules/{id}':
put:
summary: Update the immutable tag rule or enable or disable the rule
parameters:
- name: project_id
in: path
type: integer
format: int64
required: true
description: Relevant project ID.
- name: id
in: path
type: integer
format: int64
required: true
description: Immutable tag rule ID.
- name: ImmutableRule
in: body
required: true
schema:
$ref: '#/definitions/ImmutableRule'
tags:
- Products
responses:
'200':
description: Update the immutable tag rule successfully.
'400':
description: Illegal format of provided ID value.
'401':
description: User need to log in first.
'403':
description: User have no permission to update the immutable tag rule of the project.
'500':
description: Internal server errors.
delete:
summary: Delete the immutable tag rule.
parameters:
- name: project_id
in: path
type: integer
format: int64
required: true
description: Relevant project ID.
- name: id
in: path
type: integer
format: int64
required: true
description: Immutable tag rule ID.
tags:
- Products
responses:
'200':
description: Delete the immutable tag rule successfully.
'400':
description: Illegal format of provided ID value.
'401':
description: User need to log in first.
'403':
description: User have no permission to delete immutable tags of the project.
'500':
description: Internal server errors.
responses:
OK:
description: 'Success'
@ -3422,45 +3302,6 @@ definitions:
type: string
description: Webhook supportted notify type.
example: 'http'
ImmutableRule:
type: object
properties:
id:
type: integer
priority:
type: integer
disabled:
type: boolean
action:
type: string
template:
type: string
params:
type: object
additionalProperties:
type: object
tag_selectors:
type: array
items:
$ref: '#/definitions/ImmutableSelector'
scope_selectors:
type: object
additionalProperties:
type: array
items:
$ref: '#/definitions/ImmutableSelector'
ImmutableSelector:
type: object
properties:
kind:
type: string
decoration:
type: string
pattern:
type: string
extras:
type: string
parameters:
query:

View File

@ -1702,6 +1702,120 @@ paths:
$ref: '#/responses/404'
'500':
$ref: '#/responses/500'
'/projects/{project_name_or_id}/immutabletagrules':
get:
summary: List all immutable tag rules of current project
description: |
This endpoint returns the immutable tag rules of a project
tags:
- immutable
operationId: ListImmuRules
parameters:
- $ref: '#/parameters/requestId'
- $ref: '#/parameters/isResourceName'
- $ref: '#/parameters/projectNameOrId'
- $ref: '#/parameters/page'
- $ref: '#/parameters/pageSize'
- $ref: '#/parameters/query'
responses:
'200':
description: Success
headers:
X-Total-Count:
description: The total count of immutable tag
type: integer
Link:
description: Link refers to the previous page and next page
type: string
schema:
type: array
items:
$ref: '#/definitions/ImmutableRule'
'400':
$ref: '#/responses/400'
'401':
$ref: '#/responses/401'
'403':
$ref: '#/responses/403'
'500':
$ref: '#/responses/500'
post:
summary: Add an immutable tag rule to current project
description: |
This endpoint add an immutable tag rule to the project
tags:
- immutable
operationId: CreateImmuRule
parameters:
- $ref: '#/parameters/requestId'
- $ref: '#/parameters/isResourceName'
- $ref: '#/parameters/projectNameOrId'
- name: ImmutableRule
in: body
required: true
schema:
$ref: '#/definitions/ImmutableRule'
responses:
'201':
$ref: '#/responses/201'
'400':
$ref: '#/responses/400'
'401':
$ref: '#/responses/401'
'403':
$ref: '#/responses/403'
'404':
$ref: '#/responses/404'
'500':
$ref: '#/responses/500'
'/projects/{project_name_or_id}/immutabletagrules/{immutable_rule_id}':
put:
summary: Update the immutable tag rule or enable or disable the rule
tags:
- immutable
operationId: UpdateImmuRule
parameters:
- $ref: '#/parameters/requestId'
- $ref: '#/parameters/isResourceName'
- $ref: '#/parameters/projectNameOrId'
- $ref: '#/parameters/immutableRuleId'
- name: ImmutableRule
in: body
required: true
schema:
$ref: '#/definitions/ImmutableRule'
responses:
'200':
$ref: '#/responses/200'
'400':
$ref: '#/responses/400'
'401':
$ref: '#/responses/401'
'403':
$ref: '#/responses/403'
'500':
$ref: '#/responses/500'
delete:
summary: Delete the immutable tag rule.
tags:
- immutable
operationId: DeleteImmuRule
parameters:
- $ref: '#/parameters/requestId'
- $ref: '#/parameters/isResourceName'
- $ref: '#/parameters/projectNameOrId'
- $ref: '#/parameters/immutableRuleId'
responses:
'200':
$ref: '#/responses/200'
'400':
$ref: '#/responses/400'
'401':
$ref: '#/responses/401'
'403':
$ref: '#/responses/403'
'500':
$ref: '#/responses/500'
/icons/{digest}:
get:
summary: Get artifact icon
@ -3251,6 +3365,14 @@ parameters:
required: true
type: integer
format: int64
immutableRuleId:
name: immutable_rule_id
in: path
description: The ID of the immutable rule
required: true
type: integer
format: int64
responses:
'200':
description: Success
@ -5046,3 +5168,42 @@ definitions:
import:
package: "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
alias: v1
ImmutableRule:
type: object
properties:
id:
type: integer
priority:
type: integer
disabled:
type: boolean
action:
type: string
template:
type: string
params:
type: object
additionalProperties:
type: object
tag_selectors:
type: array
items:
$ref: '#/definitions/ImmutableSelector'
scope_selectors:
type: object
additionalProperties:
type: array
items:
$ref: '#/definitions/ImmutableSelector'
ImmutableSelector:
type: object
properties:
kind:
type: string
decoration:
type: string
pattern:
type: string
extras:
type: string

View File

@ -39,8 +39,8 @@ import (
"github.com/goharbor/harbor/src/pkg/artifactrash"
"github.com/goharbor/harbor/src/pkg/artifactrash/model"
"github.com/goharbor/harbor/src/pkg/blob"
"github.com/goharbor/harbor/src/pkg/immutabletag/match"
"github.com/goharbor/harbor/src/pkg/immutabletag/match/rule"
"github.com/goharbor/harbor/src/pkg/immutable/match"
"github.com/goharbor/harbor/src/pkg/immutable/match/rule"
"github.com/goharbor/harbor/src/pkg/label"
"github.com/goharbor/harbor/src/pkg/notification"
"github.com/goharbor/harbor/src/pkg/notifier/event"

View File

@ -16,6 +16,7 @@ package artifact
import (
"context"
"github.com/goharbor/harbor/src/testing/pkg/immutable"
"testing"
"time"
@ -36,7 +37,6 @@ import (
arttesting "github.com/goharbor/harbor/src/testing/pkg/artifact"
artrashtesting "github.com/goharbor/harbor/src/testing/pkg/artifactrash"
"github.com/goharbor/harbor/src/testing/pkg/blob"
immutesting "github.com/goharbor/harbor/src/testing/pkg/immutabletag"
"github.com/goharbor/harbor/src/testing/pkg/label"
"github.com/goharbor/harbor/src/testing/pkg/registry"
repotesting "github.com/goharbor/harbor/src/testing/pkg/repository"
@ -66,7 +66,7 @@ type controllerTestSuite struct {
tagCtl *tagtesting.FakeController
labelMgr *label.FakeManager
abstractor *fakeAbstractor
immutableMtr *immutesting.FakeMatcher
immutableMtr *immutable.FakeMatcher
regCli *registry.FakeClient
}
@ -78,7 +78,7 @@ func (c *controllerTestSuite) SetupTest() {
c.tagCtl = &tagtesting.FakeController{}
c.labelMgr = &label.FakeManager{}
c.abstractor = &fakeAbstractor{}
c.immutableMtr = &immutesting.FakeMatcher{}
c.immutableMtr = &immutable.FakeMatcher{}
c.regCli = &registry.FakeClient{}
c.ctl = &controller{
repoMgr: c.repoMgr,

View File

@ -0,0 +1,88 @@
package immutable
import (
"context"
"fmt"
"github.com/goharbor/harbor/src/lib/q"
"github.com/goharbor/harbor/src/pkg/immutable"
"github.com/goharbor/harbor/src/pkg/immutable/model"
)
var (
// Ctr is a global variable for the default immutable controller implementation
Ctr = NewAPIController(immutable.NewDefaultRuleManager())
)
// Controller to handle the requests related with immutable
type Controller interface {
// GetImmutableRule ...
GetImmutableRule(ctx context.Context, id int64) (*model.Metadata, error)
// CreateImmutableRule ...
CreateImmutableRule(ctx context.Context, m *model.Metadata) (int64, error)
// DeleteImmutableRule ...
DeleteImmutableRule(ctx context.Context, id int64) error
// UpdateImmutableRule ...
UpdateImmutableRule(ctx context.Context, projectID int64, m *model.Metadata) error
// ListImmutableRules ...
ListImmutableRules(ctx context.Context, query *q.Query) ([]*model.Metadata, error)
// Count count the immutable rules
Count(ctx context.Context, query *q.Query) (int64, error)
}
// DefaultAPIController ...
type DefaultAPIController struct {
manager immutable.Manager
}
// GetImmutableRule ...
func (r *DefaultAPIController) GetImmutableRule(ctx context.Context, id int64) (*model.Metadata, error) {
return r.manager.GetImmutableRule(ctx, id)
}
// DeleteImmutableRule ...
func (r *DefaultAPIController) DeleteImmutableRule(ctx context.Context, id int64) error {
return r.manager.DeleteImmutableRule(ctx, id)
}
// CreateImmutableRule ...
func (r *DefaultAPIController) CreateImmutableRule(ctx context.Context, m *model.Metadata) (int64, error) {
return r.manager.CreateImmutableRule(ctx, m)
}
// UpdateImmutableRule ...
func (r *DefaultAPIController) UpdateImmutableRule(ctx context.Context, projectID int64, m *model.Metadata) error {
m0, err := r.manager.GetImmutableRule(ctx, m.ID)
if err != nil {
return err
}
if m0 == nil {
return fmt.Errorf("the immutable tag rule is not found id:%v", m.ID)
}
if m0.Disabled != m.Disabled {
return r.manager.EnableImmutableRule(ctx, m.ID, m.Disabled)
}
return r.manager.UpdateImmutableRule(ctx, projectID, m)
}
// ListImmutableRules ...
func (r *DefaultAPIController) ListImmutableRules(ctx context.Context, query *q.Query) ([]*model.Metadata, error) {
return r.manager.ListImmutableRules(ctx, query)
}
// Count count the immutable rules
func (r *DefaultAPIController) Count(ctx context.Context, query *q.Query) (int64, error) {
return r.manager.Count(ctx, query)
}
// NewAPIController ...
func NewAPIController(immutableMgr immutable.Manager) Controller {
return &DefaultAPIController{
manager: immutableMgr,
}
}

View File

@ -1,19 +1,22 @@
package immutabletag
package immutable
import (
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/lib/q"
"testing"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils/test"
"github.com/goharbor/harbor/src/pkg/immutabletag/model"
"github.com/goharbor/harbor/src/pkg/immutable/model"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
type ControllerTestSuite struct {
suite.Suite
htesting.Suite
ctr Controller
t *testing.T
assert *assert.Assertions
@ -28,7 +31,7 @@ func (s *ControllerTestSuite) SetupSuite() {
s.t = s.T()
s.assert = assert.New(s.t)
s.require = require.New(s.t)
s.ctr = ImmuCtr
s.ctr = Ctr
}
func (s *ControllerTestSuite) TestImmutableRule() {
@ -65,7 +68,7 @@ func (s *ControllerTestSuite) TestImmutableRule() {
},
},
}
s.ruleID, err = s.ctr.CreateImmutableRule(rule)
s.ruleID, err = s.ctr.CreateImmutableRule(orm.Context(), rule)
s.require.Nil(err)
update := &model.Metadata{
@ -92,10 +95,10 @@ func (s *ControllerTestSuite) TestImmutableRule() {
},
Disabled: false,
}
err = s.ctr.UpdateImmutableRule(projectID, update)
err = s.ctr.UpdateImmutableRule(orm.Context(), projectID, update)
s.require.Nil(err)
getRule, err := s.ctr.GetImmutableRule(s.ruleID)
getRule, err := s.ctr.GetImmutableRule(orm.Context(), s.ruleID)
s.require.Nil(err)
s.require.Equal("postgres", getRule.ScopeSelectors["repository"][0].Pattern)
@ -123,9 +126,9 @@ func (s *ControllerTestSuite) TestImmutableRule() {
},
Disabled: true,
}
err = s.ctr.UpdateImmutableRule(projectID, update2)
err = s.ctr.UpdateImmutableRule(orm.Context(), projectID, update2)
s.require.Nil(err)
getRule, err = s.ctr.GetImmutableRule(s.ruleID)
getRule, err = s.ctr.GetImmutableRule(orm.Context(), s.ruleID)
s.require.Nil(err)
s.require.True(getRule.Disabled)
@ -151,10 +154,10 @@ func (s *ControllerTestSuite) TestImmutableRule() {
},
},
}
s.ruleID, err = s.ctr.CreateImmutableRule(rule2)
s.ruleID, err = s.ctr.CreateImmutableRule(orm.Context(), rule2)
s.require.Nil(err)
rules, err := s.ctr.ListImmutableRules(projectID)
rules, err := s.ctr.ListImmutableRules(orm.Context(), q.New(q.KeyWords{"ProjectID": projectID}))
s.require.Nil(err)
s.require.Equal(2, len(rules))
@ -162,7 +165,7 @@ func (s *ControllerTestSuite) TestImmutableRule() {
// TearDownSuite clears env for test suite
func (s *ControllerTestSuite) TearDownSuite() {
err := s.ctr.DeleteImmutableRule(s.ruleID)
err := s.ctr.DeleteImmutableRule(orm.Context(), s.ruleID)
require.NoError(s.T(), err, "delete immutable rule")
}

View File

@ -23,8 +23,8 @@ import (
"github.com/goharbor/harbor/src/lib/q"
"github.com/goharbor/harbor/src/lib/selector"
"github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/immutabletag/match"
"github.com/goharbor/harbor/src/pkg/immutabletag/match/rule"
"github.com/goharbor/harbor/src/pkg/immutable/match"
"github.com/goharbor/harbor/src/pkg/immutable/match/rule"
"github.com/goharbor/harbor/src/pkg/signature"
"github.com/goharbor/harbor/src/pkg/tag"
model_tag "github.com/goharbor/harbor/src/pkg/tag/model/tag"
@ -221,7 +221,7 @@ func (c *controller) populateImmutableStatus(ctx context.Context, tag *Tag) {
return
}
_, repoName := utils.ParseRepository(artifact.RepositoryName)
matched, err := c.immutableMtr.Match(artifact.ProjectID, selector.Candidate{
matched, err := c.immutableMtr.Match(ctx, artifact.ProjectID, selector.Candidate{
Repository: repoName,
Tags: []string{tag.Name},
NamespaceID: artifact.ProjectID,

View File

@ -23,7 +23,7 @@ import (
"github.com/goharbor/harbor/src/pkg/tag/model/tag"
ormtesting "github.com/goharbor/harbor/src/testing/lib/orm"
"github.com/goharbor/harbor/src/testing/pkg/artifact"
immutesting "github.com/goharbor/harbor/src/testing/pkg/immutabletag"
"github.com/goharbor/harbor/src/testing/pkg/immutable"
"github.com/goharbor/harbor/src/testing/pkg/repository"
tagtesting "github.com/goharbor/harbor/src/testing/pkg/tag"
"github.com/stretchr/testify/suite"
@ -37,14 +37,14 @@ type controllerTestSuite struct {
repoMgr *repository.FakeManager
artMgr *artifact.FakeManager
tagMgr *tagtesting.FakeManager
immutableMtr *immutesting.FakeMatcher
immutableMtr *immutable.FakeMatcher
}
func (c *controllerTestSuite) SetupTest() {
c.repoMgr = &repository.FakeManager{}
c.artMgr = &artifact.FakeManager{}
c.tagMgr = &tagtesting.FakeManager{}
c.immutableMtr = &immutesting.FakeMatcher{}
c.immutableMtr = &immutable.FakeMatcher{}
c.ctl = &controller{
tagMgr: c.tagMgr,
artMgr: c.artMgr,

View File

@ -133,8 +133,6 @@ func init() {
beego.Router("/api/projects/:pid([0-9]+)/webhook/policies/test", &NotificationPolicyAPI{}, "post:Test")
beego.Router("/api/projects/:pid([0-9]+)/webhook/lasttrigger", &NotificationPolicyAPI{}, "get:ListGroupByEventType")
beego.Router("/api/projects/:pid([0-9]+)/webhook/jobs/", &NotificationJobAPI{}, "get:List")
beego.Router("/api/projects/:pid([0-9]+)/immutabletagrules", &ImmutableTagRuleAPI{}, "get:List;post:Post")
beego.Router("/api/projects/:pid([0-9]+)/immutabletagrules/:id([0-9]+)", &ImmutableTagRuleAPI{})
// Charts are controlled under projects
chartRepositoryAPIType := &ChartRepositoryAPI{}
beego.Router("/api/chartrepo/health", chartRepositoryAPIType, "get:GetHealthStatus")

View File

@ -1,158 +0,0 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package api
import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/pkg/immutabletag"
"github.com/goharbor/harbor/src/pkg/immutabletag/model"
)
// ImmutableTagRuleAPI ...
type ImmutableTagRuleAPI struct {
BaseController
ctr immutabletag.Controller
projectID int64
ID int64
}
// Prepare validates the user and projectID
func (itr *ImmutableTagRuleAPI) Prepare() {
itr.BaseController.Prepare()
// Check access permissions
if !itr.RequireAuthenticated() {
return
}
pid, err := itr.GetInt64FromPath(":pid")
if err != nil || pid <= 0 {
text := "invalid project ID: "
if err != nil {
text += err.Error()
} else {
text += fmt.Sprintf("%d", pid)
}
itr.SendError(errors.New(err).WithCode(errors.BadRequestCode))
return
}
itr.projectID = pid
itr.ctr = immutabletag.ImmuCtr
ruleID, err := itr.GetInt64FromPath(":id")
if err == nil || ruleID > 0 {
itr.ID = ruleID
itRule, err := itr.ctr.GetImmutableRule(itr.ID)
if err != nil {
itr.SendError(err)
return
}
if itRule.ProjectID != itr.projectID {
err := fmt.Errorf("immutable tag rule %v not found", itr.ID)
itr.SendError(errors.New(err).WithCode(errors.NotFoundCode))
return
}
}
if strings.EqualFold(itr.Ctx.Request.Method, "get") {
if !itr.requireAccess(rbac.ActionList) {
return
}
} else if strings.EqualFold(itr.Ctx.Request.Method, "put") {
if !itr.requireAccess(rbac.ActionUpdate) {
return
}
} else if strings.EqualFold(itr.Ctx.Request.Method, "post") {
if !itr.requireAccess(rbac.ActionCreate) {
return
}
} else if strings.EqualFold(itr.Ctx.Request.Method, "delete") {
if !itr.requireAccess(rbac.ActionDelete) {
return
}
}
}
func (itr *ImmutableTagRuleAPI) requireAccess(action rbac.Action) bool {
return itr.RequireProjectAccess(itr.projectID, action, rbac.ResourceImmutableTag)
}
// List list all immutable tag rules of current project
func (itr *ImmutableTagRuleAPI) List() {
rules, err := itr.ctr.ListImmutableRules(itr.projectID)
if err != nil {
itr.SendError(err)
return
}
itr.WriteJSONData(rules)
}
// Post create immutable tag rule
func (itr *ImmutableTagRuleAPI) Post() {
ir := &model.Metadata{}
isValid, err := itr.DecodeJSONReqAndValidate(ir)
if !isValid {
itr.SendError(errors.New(err).WithCode(errors.BadRequestCode))
return
}
ir.ProjectID = itr.projectID
id, err := itr.ctr.CreateImmutableRule(ir)
if err != nil {
itr.SendError(err)
return
}
itr.Redirect(http.StatusCreated, strconv.FormatInt(id, 10))
}
// Delete delete immutable tag rule
func (itr *ImmutableTagRuleAPI) Delete() {
if itr.ID <= 0 {
err := fmt.Errorf("invalid immutable rule id %d", itr.ID)
itr.SendError(errors.New(err).WithCode(errors.BadRequestCode))
return
}
err := itr.ctr.DeleteImmutableRule(itr.ID)
if err != nil {
itr.SendError(err)
return
}
}
// Put update an immutable tag rule
func (itr *ImmutableTagRuleAPI) Put() {
ir := &model.Metadata{}
if err := itr.DecodeJSONReq(ir); err != nil {
itr.SendError(errors.New(err).WithCode(errors.BadRequestCode))
return
}
ir.ID = itr.ID
ir.ProjectID = itr.projectID
if itr.ID <= 0 {
err := fmt.Errorf("invalid immutable rule id %d", itr.ID)
itr.SendError(errors.New(err).WithCode(errors.BadRequestCode))
return
}
if err := itr.ctr.UpdateImmutableRule(itr.projectID, ir); err != nil {
itr.SendError(err)
return
}
}

View File

@ -1,365 +0,0 @@
package api
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/goharbor/harbor/src/pkg/immutabletag"
"github.com/goharbor/harbor/src/pkg/immutabletag/model"
)
func TestImmutableTagRuleAPI_List(t *testing.T) {
metadata := &model.Metadata{
ProjectID: 1,
Disabled: false,
TagSelectors: []*model.Selector{
{
Kind: "doublestar",
Decoration: "matches",
Pattern: "release-[\\d\\.]+",
},
},
ScopeSelectors: map[string][]*model.Selector{
"repository": {
{
Kind: "doublestar",
Decoration: "matches",
Pattern: ".+",
},
},
},
}
mgr := immutabletag.NewDefaultRuleManager()
id, err := mgr.CreateImmutableRule(metadata)
if err != nil {
t.Error(err)
}
defer mgr.DeleteImmutableRule(id)
cases := []*codeCheckingCase{
// 401
{
request: &testingRequest{
method: http.MethodGet,
url: "/api/projects/1/immutabletagrules",
},
code: http.StatusUnauthorized,
},
// 200
{
request: &testingRequest{
method: http.MethodGet,
url: "/api/projects/1/immutabletagrules",
credential: admin,
},
postFunc: func(responseRecorder *httptest.ResponseRecorder) error {
var rules []model.Metadata
err := json.Unmarshal([]byte(responseRecorder.Body.String()), &rules)
if err != nil {
return err
}
if len(rules) <= 0 {
return fmt.Errorf("no rules found")
}
if rules[0].TagSelectors[0].Kind != "doublestar" {
return fmt.Errorf("rule is not expected. actual: %v", responseRecorder.Body.String())
}
return nil
},
code: http.StatusOK,
},
// 200
{
request: &testingRequest{
method: http.MethodGet,
url: "/api/projects/1/immutabletagrules",
credential: projAdmin,
},
code: http.StatusOK,
},
// 403
{
request: &testingRequest{
method: http.MethodGet,
url: "/api/projects/1/immutabletagrules",
credential: projGuest,
},
code: http.StatusForbidden,
},
}
runCodeCheckingCases(t, cases...)
}
func TestImmutableTagRuleAPI_Post(t *testing.T) {
// body := `{
// "projectID":1,
// "priority":0,
// "template": "immutable_template",
// "action": "immutable",
// "disabled":false,
// "action":"immutable",
// "template":"immutable_template",
// "tag_selectors":[{"kind":"doublestar","decoration":"matches","pattern":"**"}],
// "scope_selectors":{"repository":[{"kind":"doublestar","decoration":"repoMatches","pattern":"**"}]}
// }`
metadata := &model.Metadata{
ProjectID: 1,
Disabled: false,
Priority: 0,
Template: "immutable_template",
Action: "immutable",
TagSelectors: []*model.Selector{
{
Kind: "doublestar",
Decoration: "matches",
Pattern: "release-[\\d\\.]+",
},
},
ScopeSelectors: map[string][]*model.Selector{
"repository": {
{
Kind: "doublestar",
Decoration: "matches",
Pattern: ".+",
},
},
},
}
cases := []*codeCheckingCase{
// 401
{
request: &testingRequest{
method: http.MethodPost,
url: "/api/projects/1/immutabletagrules",
bodyJSON: metadata,
},
code: http.StatusUnauthorized,
},
// 201
{
request: &testingRequest{
method: http.MethodPost,
url: "/api/projects/1/immutabletagrules",
credential: admin,
bodyJSON: metadata,
},
code: http.StatusCreated,
},
// 409
{
request: &testingRequest{
method: http.MethodPost,
url: "/api/projects/1/immutabletagrules",
credential: projAdmin,
bodyJSON: metadata,
},
code: http.StatusConflict,
},
// 403
{
request: &testingRequest{
method: http.MethodPost,
url: "/api/projects/1/immutabletagrules",
credential: projGuest,
bodyJSON: metadata,
},
code: http.StatusForbidden,
},
}
runCodeCheckingCases(t, cases...)
}
func TestImmutableTagRuleAPI_Put(t *testing.T) {
metadata := &model.Metadata{
ProjectID: 1,
Disabled: false,
TagSelectors: []*model.Selector{
{
Kind: "doublestar",
Decoration: "matches",
Pattern: "release-[\\d\\.]+",
},
},
ScopeSelectors: map[string][]*model.Selector{
"repository": {
{
Kind: "doublestar",
Decoration: "matches",
Pattern: ".+",
},
},
},
}
metadata2 := &model.Metadata{
ProjectID: 1,
Disabled: false,
TagSelectors: []*model.Selector{
{
Kind: "doublestar",
Decoration: "matches",
Pattern: "latest",
},
},
ScopeSelectors: map[string][]*model.Selector{
"repository": {
{
Kind: "doublestar",
Decoration: "matches",
Pattern: ".+",
},
},
},
}
mgr := immutabletag.NewDefaultRuleManager()
id, err := mgr.CreateImmutableRule(metadata)
if err != nil {
t.Error(err)
}
defer mgr.DeleteImmutableRule(id)
url := fmt.Sprintf("/api/projects/1/immutabletagrules/%d", id)
url2 := fmt.Sprintf("/api/projects/3/immutabletagrules/%d", id)
cases := []*codeCheckingCase{
// 401
{
request: &testingRequest{
method: http.MethodPut,
url: url,
bodyJSON: metadata2,
},
code: http.StatusUnauthorized,
},
// 200
{
request: &testingRequest{
method: http.MethodPut,
url: url,
credential: admin,
bodyJSON: metadata2,
},
code: http.StatusOK,
},
// 200
{
request: &testingRequest{
method: http.MethodPut,
url: url,
credential: projAdmin,
bodyJSON: metadata2,
},
code: http.StatusOK,
},
// 403
{
request: &testingRequest{
method: http.MethodPut,
url: url,
credential: projGuest,
bodyJSON: metadata2,
},
code: http.StatusForbidden,
},
// 404
{
request: &testingRequest{
method: http.MethodPut,
url: url2,
credential: projAdmin,
bodyJSON: metadata2,
},
code: http.StatusNotFound,
},
}
runCodeCheckingCases(t, cases...)
}
func TestImmutableTagRuleAPI_Delete(t *testing.T) {
metadata := &model.Metadata{
ProjectID: 1,
Disabled: false,
TagSelectors: []*model.Selector{
{
Kind: "doublestar",
Decoration: "matches",
Pattern: "latest",
},
},
ScopeSelectors: map[string][]*model.Selector{
"repository": {
{
Kind: "doublestar",
Decoration: "matches",
Pattern: ".+",
},
},
},
}
mgr := immutabletag.NewDefaultRuleManager()
id, err := mgr.CreateImmutableRule(metadata)
if err != nil {
t.Error(err)
}
defer mgr.DeleteImmutableRule(id)
url := fmt.Sprintf("/api/projects/1/immutabletagrules/%d", id)
wrongURL := fmt.Sprintf("/api/projects/3/immutabletagrules/%d", id)
cases := []*codeCheckingCase{
// 401
{
request: &testingRequest{
method: http.MethodDelete,
url: url,
},
code: http.StatusUnauthorized,
},
// 403
{
request: &testingRequest{
method: http.MethodDelete,
url: url,
credential: projGuest,
},
code: http.StatusForbidden,
},
// 404
{
request: &testingRequest{
method: http.MethodDelete,
url: wrongURL,
credential: projAdmin,
},
code: http.StatusNotFound,
},
// 200
{
request: &testingRequest{
method: http.MethodDelete,
url: url,
credential: projAdmin,
},
code: http.StatusOK,
},
// 404
{
request: &testingRequest{
method: http.MethodDelete,
url: url,
credential: projAdmin,
},
code: http.StatusNotFound,
},
}
runCodeCheckingCases(t, cases...)
}

View File

@ -0,0 +1,142 @@
package dao
import (
"context"
"github.com/goharbor/harbor/src/lib/q"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/pkg/immutable/dao/model"
)
// DAO defines the interface to access the ImmutableRule data model
type DAO interface {
CreateImmutableRule(ctx context.Context, ir *model.ImmutableRule) (int64, error)
UpdateImmutableRule(ctx context.Context, projectID int64, ir *model.ImmutableRule) error
ToggleImmutableRule(ctx context.Context, id int64, status bool) error
GetImmutableRule(ctx context.Context, id int64) (*model.ImmutableRule, error)
Count(ctx context.Context, query *q.Query) (int64, error)
ListImmutableRules(ctx context.Context, query *q.Query) ([]*model.ImmutableRule, error)
DeleteImmutableRule(ctx context.Context, id int64) error
}
// New creates a default implementation for DAO
func New() DAO {
return &iDao{}
}
type iDao struct{}
// CreateImmutableRule creates the Immutable Rule
func (i *iDao) CreateImmutableRule(ctx context.Context, ir *model.ImmutableRule) (int64, error) {
ormer, err := orm.FromContext(ctx)
if err != nil {
return 0, err
}
ir.Disabled = false
id, err := ormer.Insert(ir)
if err != nil {
if e := orm.AsConflictError(err, "immutable rule already exists"); e != nil {
err = e
}
}
return id, err
}
// UpdateImmutableRule update the immutable rules
func (i *iDao) UpdateImmutableRule(ctx context.Context, projectID int64, ir *model.ImmutableRule) error {
ir.ProjectID = projectID
ormer, err := orm.FromContext(ctx)
if err != nil {
return err
}
n, err := ormer.Update(ir, "TagFilter")
if err != nil {
return err
}
if n == 0 {
return errors.NotFoundError(nil).WithMessage("immutable %d not found", ir.ID)
}
return nil
}
// ToggleImmutableRule enable/disable immutable rules
func (i *iDao) ToggleImmutableRule(ctx context.Context, id int64, status bool) error {
ormer, err := orm.FromContext(ctx)
if err != nil {
return err
}
ir := &model.ImmutableRule{ID: id, Disabled: status}
n, err := ormer.Update(ir, "Disabled")
if err != nil {
return err
}
if n == 0 {
return errors.NotFoundError(nil).WithMessage("immutable %d not found", ir.ID)
}
return nil
}
// GetImmutableRule get immutable rule
func (i *iDao) GetImmutableRule(ctx context.Context, id int64) (*model.ImmutableRule, error) {
ormer, err := orm.FromContext(ctx)
if err != nil {
return nil, err
}
ir := &model.ImmutableRule{ID: id}
if err = ormer.Read(ir); err != nil {
if e := orm.AsNotFoundError(err, "immutable rule %d not found", id); e != nil {
err = e
}
return nil, err
}
return ir, nil
}
// QueryImmutableRuleByProjectID get all immutable rule by project
func (i *iDao) ListImmutableRules(ctx context.Context, query *q.Query) ([]*model.ImmutableRule, error) {
rules := []*model.ImmutableRule{}
qs, err := orm.QuerySetter(ctx, &model.ImmutableRule{}, query)
if err != nil {
return nil, err
}
if query.Sorting != "" {
qs = qs.OrderBy(query.Sorting)
}
if _, err = qs.All(&rules); err != nil {
return nil, err
}
return rules, nil
}
// Count ...
func (i *iDao) Count(ctx context.Context, query *q.Query) (int64, error) {
query = q.MustClone(query)
query.Sorting = ""
query.PageNumber = 0
query.PageSize = 0
qs, err := orm.QuerySetter(ctx, &model.ImmutableRule{}, query)
if err != nil {
return 0, err
}
return qs.Count()
}
// DeleteImmutableRule delete the immutable rule
func (i *iDao) DeleteImmutableRule(ctx context.Context, id int64) error {
ormer, err := orm.FromContext(ctx)
if err != nil {
return err
}
ir := &model.ImmutableRule{ID: id}
n, err := ormer.Delete(ir)
if err != nil {
return err
}
if n == 0 {
return errors.NotFoundError(nil).WithMessage("immutable rule %d not found", id)
}
return nil
}

View File

@ -1,21 +1,23 @@
package dao
import (
"github.com/goharbor/harbor/src/lib/q"
"strings"
"testing"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/pkg/immutabletag/dao/model"
"github.com/goharbor/harbor/src/pkg/immutable/dao/model"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
type immutableRuleDaoTestSuite struct {
suite.Suite
htesting.Suite
require *require.Assertions
assert *assert.Assertions
dao ImmutableRuleDao
dao DAO
id int64
}
@ -28,52 +30,51 @@ func (t *immutableRuleDaoTestSuite) SetupSuite() {
func (t *immutableRuleDaoTestSuite) TestCreateImmutableRule() {
ir := &model.ImmutableRule{TagFilter: "**", ProjectID: 1}
id, err := t.dao.CreateImmutableRule(ir)
id, err := t.dao.CreateImmutableRule(t.Context(), ir)
t.require.Nil(err)
t.require.True(id > 0, "Can not create immutable tag rule")
// insert duplicate rows
ir2 := &model.ImmutableRule{TagFilter: "**", ProjectID: 1}
id2, err := t.dao.CreateImmutableRule(ir2)
t.require.True(strings.Contains(err.Error(), "duplicate key"))
id2, err := t.dao.CreateImmutableRule(t.Context(), ir2)
t.require.True(strings.Contains(err.Error(), "immutable rule already exist"))
t.require.Equal(int64(0), id2)
_, err = t.dao.DeleteImmutableRule(id)
err = t.dao.DeleteImmutableRule(t.Context(), id)
t.require.Nil(err)
}
func (t *immutableRuleDaoTestSuite) TestUpdateImmutableRule() {
ir := &model.ImmutableRule{TagFilter: "**", ProjectID: 1}
id, err := t.dao.CreateImmutableRule(ir)
id, err := t.dao.CreateImmutableRule(t.Context(), ir)
t.require.Nil(err)
t.require.True(id > 0, "Can not create immutable tag rule")
updatedIR := &model.ImmutableRule{ID: id, TagFilter: "1.2.0", ProjectID: 1}
updatedCnt, err := t.dao.UpdateImmutableRule(1, updatedIR)
err = t.dao.UpdateImmutableRule(t.Context(), 1, updatedIR)
t.require.Nil(err)
t.require.True(updatedCnt > 0, "Failed to update immutable id")
newIr, err := t.dao.GetImmutableRule(id)
newIr, err := t.dao.GetImmutableRule(t.Context(), id)
t.require.Nil(err)
t.require.True(newIr.TagFilter == "1.2.0", "Failed to update immutable tag")
defer t.dao.DeleteImmutableRule(id)
defer t.dao.DeleteImmutableRule(t.Context(), id)
}
func (t *immutableRuleDaoTestSuite) TestEnableImmutableRule() {
ir := &model.ImmutableRule{TagFilter: "**", ProjectID: 1}
id, err := t.dao.CreateImmutableRule(ir)
id, err := t.dao.CreateImmutableRule(t.Context(), ir)
t.require.Nil(err)
t.require.True(id > 0, "Can not create immutable tag rule")
t.dao.ToggleImmutableRule(id, true)
newIr, err := t.dao.GetImmutableRule(id)
t.dao.ToggleImmutableRule(t.Context(), id, true)
newIr, err := t.dao.GetImmutableRule(t.Context(), id)
t.require.Nil(err)
t.require.True(newIr.Disabled, "Failed to disable the immutable rule")
defer t.dao.DeleteImmutableRule(id)
defer t.dao.DeleteImmutableRule(t.Context(), id)
}
func (t *immutableRuleDaoTestSuite) TestGetImmutableRuleByProject() {
@ -84,10 +85,10 @@ func (t *immutableRuleDaoTestSuite) TestGetImmutableRuleByProject() {
{TagFilter: "version4", ProjectID: 99},
}
for _, ir := range irs {
t.dao.CreateImmutableRule(ir)
t.dao.CreateImmutableRule(t.Context(), ir)
}
qrs, err := t.dao.QueryImmutableRuleByProjectID(99)
qrs, err := t.dao.ListImmutableRules(t.Context(), q.New(q.KeyWords{"ProjectID": 99}))
t.require.Nil(err)
t.require.True(len(qrs) == 4, "Failed to query 4 rows!")
@ -102,13 +103,13 @@ func (t *immutableRuleDaoTestSuite) TestGetEnabledImmutableRuleByProject() {
{TagFilter: "version4", ProjectID: 99},
}
for i, ir := range irs {
id, _ := t.dao.CreateImmutableRule(ir)
id, _ := t.dao.CreateImmutableRule(t.Context(), ir)
if i == 1 {
t.dao.ToggleImmutableRule(id, true)
t.dao.ToggleImmutableRule(t.Context(), id, true)
}
}
qrs, err := t.dao.QueryEnabledImmutableRuleByProjectID(99)
qrs, err := t.dao.ListImmutableRules(t.Context(), q.New(q.KeyWords{"ProjectID": 99, "Disabled": "false"}))
t.require.Nil(err)
t.require.True(len(qrs) == 3, "Failed to query 3 rows!, got %v", len(qrs))

View File

@ -0,0 +1,113 @@
package immutable
import (
"context"
"encoding/json"
"github.com/goharbor/harbor/src/lib/q"
"sort"
"github.com/goharbor/harbor/src/pkg/immutable/dao"
dao_model "github.com/goharbor/harbor/src/pkg/immutable/dao/model"
"github.com/goharbor/harbor/src/pkg/immutable/model"
)
var (
// Mgr is a global variable for the default immutablerule manager implementation
Mgr = NewDefaultRuleManager()
)
// Manager ...
type Manager interface {
// CreateImmutableRule creates the Immutable Rule
CreateImmutableRule(ctx context.Context, m *model.Metadata) (int64, error)
// UpdateImmutableRule update the immutable rules
UpdateImmutableRule(ctx context.Context, projectID int64, ir *model.Metadata) error
// EnableImmutableRule enable/disable immutable rules
EnableImmutableRule(ctx context.Context, id int64, enabled bool) error
// GetImmutableRule get immutable rule
GetImmutableRule(ctx context.Context, id int64) (*model.Metadata, error)
// Count count the immutable rules
Count(ctx context.Context, query *q.Query) (int64, error)
// ListImmutableRules list the immutable rules
ListImmutableRules(ctx context.Context, query *q.Query) ([]*model.Metadata, error)
// DeleteImmutableRule delete the immutable rule
DeleteImmutableRule(ctx context.Context, id int64) error
}
type defaultRuleManager struct {
dao dao.DAO
}
func (drm *defaultRuleManager) CreateImmutableRule(ctx context.Context, ir *model.Metadata) (int64, error) {
daoRule := &dao_model.ImmutableRule{}
daoRule.Disabled = ir.Disabled
daoRule.ProjectID = ir.ProjectID
data, _ := json.Marshal(ir)
daoRule.TagFilter = string(data)
return drm.dao.CreateImmutableRule(ctx, daoRule)
}
func (drm *defaultRuleManager) UpdateImmutableRule(ctx context.Context, projectID int64, ir *model.Metadata) error {
daoRule := &dao_model.ImmutableRule{}
data, _ := json.Marshal(ir)
daoRule.ID = ir.ID
daoRule.TagFilter = string(data)
return drm.dao.UpdateImmutableRule(ctx, projectID, daoRule)
}
func (drm *defaultRuleManager) EnableImmutableRule(ctx context.Context, id int64, enabled bool) error {
return drm.dao.ToggleImmutableRule(ctx, id, enabled)
}
func (drm *defaultRuleManager) GetImmutableRule(ctx context.Context, id int64) (*model.Metadata, error) {
daoRule, err := drm.dao.GetImmutableRule(ctx, id)
if err != nil {
return nil, err
}
rule := &model.Metadata{}
if daoRule == nil {
return nil, nil
}
if err = json.Unmarshal([]byte(daoRule.TagFilter), rule); err != nil {
return nil, err
}
rule.ID = daoRule.ID
rule.Disabled = daoRule.Disabled
return rule, nil
}
func (drm *defaultRuleManager) ListImmutableRules(ctx context.Context, query *q.Query) ([]*model.Metadata, error) {
daoRules, err := drm.dao.ListImmutableRules(ctx, query)
if err != nil {
return nil, err
}
rules := make([]*model.Metadata, 0)
for _, daoRule := range daoRules {
rule := model.Metadata{}
if err = json.Unmarshal([]byte(daoRule.TagFilter), &rule); err != nil {
return nil, err
}
rule.ID = daoRule.ID
rule.Disabled = daoRule.Disabled
rules = append(rules, &rule)
}
sort.Slice(rules, func(i, j int) bool {
return rules[i].ID < rules[j].ID
})
return rules, nil
}
func (drm *defaultRuleManager) Count(ctx context.Context, query *q.Query) (int64, error) {
return drm.dao.Count(ctx, query)
}
func (drm *defaultRuleManager) DeleteImmutableRule(ctx context.Context, id int64) error {
return drm.dao.DeleteImmutableRule(ctx, id)
}
// NewDefaultRuleManager return a new instance of defaultRuleManager
func NewDefaultRuleManager() Manager {
return &defaultRuleManager{
dao: dao.New(),
}
}

View File

@ -1,8 +1,11 @@
package immutabletag
package immutable
import (
dao_model "github.com/goharbor/harbor/src/pkg/immutabletag/dao/model"
"github.com/goharbor/harbor/src/pkg/immutabletag/model"
"context"
"github.com/goharbor/harbor/src/lib/q"
dao_model "github.com/goharbor/harbor/src/pkg/immutable/dao/model"
"github.com/goharbor/harbor/src/pkg/immutable/model"
"github.com/goharbor/harbor/src/testing/pkg/immutable/dao"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
@ -12,64 +15,12 @@ import (
"testing"
)
type mockImmutableDao struct {
mock.Mock
}
func (m *mockImmutableDao) CreateImmutableRule(ir *dao_model.ImmutableRule) (int64, error) {
args := m.Called(ir)
return int64(args.Int(0)), args.Error(1)
}
func (m *mockImmutableDao) UpdateImmutableRule(projectID int64, ir *dao_model.ImmutableRule) (int64, error) {
args := m.Called(ir)
return int64(0), args.Error(1)
}
func (m *mockImmutableDao) QueryImmutableRuleByProjectID(projectID int64) ([]dao_model.ImmutableRule, error) {
args := m.Called()
var irs []dao_model.ImmutableRule
if args.Get(0) != nil {
irs = args.Get(0).([]dao_model.ImmutableRule)
}
return irs, args.Error(1)
}
func (m *mockImmutableDao) QueryEnabledImmutableRuleByProjectID(projectID int64) ([]dao_model.ImmutableRule, error) {
args := m.Called()
var irs []dao_model.ImmutableRule
if args.Get(0) != nil {
irs = args.Get(0).([]dao_model.ImmutableRule)
}
return irs, args.Error(1)
}
func (m *mockImmutableDao) DeleteImmutableRule(id int64) (int64, error) {
args := m.Called(id)
return int64(args.Int(0)), args.Error(1)
}
func (m *mockImmutableDao) ToggleImmutableRule(id int64, enabled bool) (int64, error) {
args := m.Called(id)
return int64(args.Int(0)), args.Error(1)
}
func (m *mockImmutableDao) GetImmutableRule(id int64) (*dao_model.ImmutableRule, error) {
args := m.Called(id)
var ir *dao_model.ImmutableRule
if args.Get(0) != nil {
ir = args.Get(0).(*dao_model.ImmutableRule)
}
return ir, args.Error(1)
}
type managerTestingSuite struct {
suite.Suite
t *testing.T
assert *assert.Assertions
require *require.Assertions
mockImmutableDao *mockImmutableDao
mockImmutableDao *dao.DAO
}
func (m *managerTestingSuite) SetupSuite() {
@ -87,7 +38,7 @@ func (m *managerTestingSuite) TearDownSuite() {
}
func (m *managerTestingSuite) SetupTest() {
m.mockImmutableDao = &mockImmutableDao{}
m.mockImmutableDao = &dao.DAO{}
Mgr = &defaultRuleManager{
dao: m.mockImmutableDao,
}
@ -98,15 +49,15 @@ func TestManagerTestingSuite(t *testing.T) {
}
func (m *managerTestingSuite) TestCreateImmutableRule() {
m.mockImmutableDao.On("CreateImmutableRule", mock.Anything).Return(1, nil)
id, err := Mgr.CreateImmutableRule(&model.Metadata{})
m.mockImmutableDao.AssertCalled(m.t, "CreateImmutableRule", mock.Anything)
m.mockImmutableDao.On("CreateImmutableRule", mock.Anything, mock.Anything).Return(int64(1), nil)
id, err := Mgr.CreateImmutableRule(context.Background(), &model.Metadata{})
m.mockImmutableDao.AssertCalled(m.t, "CreateImmutableRule", mock.Anything, mock.Anything)
m.require.Nil(err)
m.assert.Equal(int64(1), id)
}
func (m *managerTestingSuite) TestQueryImmutableRuleByProjectID() {
m.mockImmutableDao.On("QueryImmutableRuleByProjectID", mock.Anything).Return([]dao_model.ImmutableRule{
m.mockImmutableDao.On("ListImmutableRules", mock.Anything, mock.Anything).Return([]*dao_model.ImmutableRule{
{
ID: 1,
ProjectID: 1,
@ -125,15 +76,15 @@ func (m *managerTestingSuite) TestQueryImmutableRuleByProjectID() {
"\"tag_selectors\":[{\"kind\":\"doublestar\",\"decoration\":\"matches\",\"pattern\":\"**\"}]," +
"\"scope_selectors\":{\"repository\":[{\"kind\":\"doublestar\",\"decoration\":\"repoMatches\",\"pattern\":\"**\"}]}}",
}}, nil)
irs, err := Mgr.QueryImmutableRuleByProjectID(int64(1))
m.mockImmutableDao.AssertCalled(m.t, "QueryImmutableRuleByProjectID", mock.Anything)
irs, err := Mgr.ListImmutableRules(context.Background(), &q.Query{})
m.mockImmutableDao.AssertCalled(m.t, "ListImmutableRules", mock.Anything, mock.Anything)
m.require.Nil(err)
m.assert.Equal(len(irs), 2)
m.assert.Equal(irs[1].Disabled, false)
}
func (m *managerTestingSuite) TestQueryEnabledImmutableRuleByProjectID() {
m.mockImmutableDao.On("QueryEnabledImmutableRuleByProjectID", mock.Anything).Return([]dao_model.ImmutableRule{
m.mockImmutableDao.On("ListImmutableRules", mock.Anything, mock.Anything).Return([]*dao_model.ImmutableRule{
{
ID: 1,
ProjectID: 1,
@ -152,15 +103,15 @@ func (m *managerTestingSuite) TestQueryEnabledImmutableRuleByProjectID() {
"\"tag_selectors\":[{\"kind\":\"doublestar\",\"decoration\":\"matches\",\"pattern\":\"**\"}]," +
"\"scope_selectors\":{\"repository\":[{\"kind\":\"doublestar\",\"decoration\":\"repoMatches\",\"pattern\":\"**\"}]}}",
}}, nil)
irs, err := Mgr.QueryEnabledImmutableRuleByProjectID(int64(1))
m.mockImmutableDao.AssertCalled(m.t, "QueryEnabledImmutableRuleByProjectID", mock.Anything)
irs, err := Mgr.ListImmutableRules(context.Background(), &q.Query{})
m.mockImmutableDao.AssertCalled(m.t, "ListImmutableRules", mock.Anything, mock.Anything)
m.require.Nil(err)
m.assert.Equal(len(irs), 2)
m.assert.Equal(irs[0].Disabled, false)
m.assert.Equal(irs[0].Disabled, true)
}
func (m *managerTestingSuite) TestGetImmutableRule() {
m.mockImmutableDao.On("GetImmutableRule", mock.Anything).Return(&dao_model.ImmutableRule{
m.mockImmutableDao.On("GetImmutableRule", mock.Anything, mock.Anything).Return(&dao_model.ImmutableRule{
ID: 1,
ProjectID: 1,
Disabled: true,
@ -169,33 +120,30 @@ func (m *managerTestingSuite) TestGetImmutableRule() {
"\"tag_selectors\":[{\"kind\":\"doublestar\",\"decoration\":\"matches\",\"pattern\":\"**\"}]," +
"\"scope_selectors\":{\"repository\":[{\"kind\":\"doublestar\",\"decoration\":\"repoMatches\",\"pattern\":\"**\"}]}}",
}, nil)
ir, err := Mgr.GetImmutableRule(1)
m.mockImmutableDao.AssertCalled(m.t, "GetImmutableRule", mock.Anything)
ir, err := Mgr.GetImmutableRule(context.Background(), 1)
m.mockImmutableDao.AssertCalled(m.t, "GetImmutableRule", mock.Anything, mock.Anything)
m.require.Nil(err)
m.require.NotNil(ir)
m.assert.Equal(int64(1), ir.ID)
}
func (m *managerTestingSuite) TestUpdateImmutableRule() {
m.mockImmutableDao.On("UpdateImmutableRule", mock.Anything).Return(1, nil)
id, err := Mgr.UpdateImmutableRule(int64(1), &model.Metadata{})
m.mockImmutableDao.AssertCalled(m.t, "UpdateImmutableRule", mock.Anything)
m.mockImmutableDao.On("UpdateImmutableRule", mock.Anything, mock.Anything, mock.Anything).Return(nil)
err := Mgr.UpdateImmutableRule(context.Background(), int64(1), &model.Metadata{})
m.mockImmutableDao.AssertCalled(m.t, "UpdateImmutableRule", mock.Anything, mock.Anything, mock.Anything)
m.require.Nil(err)
m.assert.Equal(int64(0), id)
}
func (m *managerTestingSuite) TestEnableImmutableRule() {
m.mockImmutableDao.On("ToggleImmutableRule", mock.Anything).Return(1, nil)
id, err := Mgr.EnableImmutableRule(int64(1), true)
m.mockImmutableDao.AssertCalled(m.t, "ToggleImmutableRule", mock.Anything)
m.mockImmutableDao.On("ToggleImmutableRule", mock.Anything, mock.Anything, mock.Anything).Return(nil)
err := Mgr.EnableImmutableRule(context.Background(), int64(1), true)
m.mockImmutableDao.AssertCalled(m.t, "ToggleImmutableRule", mock.Anything, mock.Anything, mock.Anything)
m.require.Nil(err)
m.assert.Equal(int64(1), id)
}
func (m *managerTestingSuite) TestDeleteImmutableRule() {
m.mockImmutableDao.On("DeleteImmutableRule", mock.Anything).Return(1, nil)
id, err := Mgr.DeleteImmutableRule(int64(1))
m.mockImmutableDao.AssertCalled(m.t, "DeleteImmutableRule", mock.Anything)
m.mockImmutableDao.On("DeleteImmutableRule", mock.Anything, mock.Anything).Return(nil)
err := Mgr.DeleteImmutableRule(context.Background(), int64(1))
m.mockImmutableDao.AssertCalled(m.t, "DeleteImmutableRule", mock.Anything, mock.Anything)
m.require.Nil(err)
m.assert.Equal(int64(1), id)
}

View File

@ -1,11 +1,12 @@
package match
import (
"context"
"github.com/goharbor/harbor/src/lib/selector"
)
// ImmutableTagMatcher ...
type ImmutableTagMatcher interface {
// Match whether the candidate is in the immutable list
Match(pid int64, c selector.Candidate) (bool, error)
Match(ctx context.Context, pid int64, c selector.Candidate) (bool, error)
}

View File

@ -1,21 +1,23 @@
package rule
import (
"context"
"github.com/goharbor/harbor/src/controller/immutable"
"github.com/goharbor/harbor/src/lib/q"
iselector "github.com/goharbor/harbor/src/lib/selector"
"github.com/goharbor/harbor/src/lib/selector/selectors/index"
"github.com/goharbor/harbor/src/pkg/immutabletag"
"github.com/goharbor/harbor/src/pkg/immutabletag/match"
"github.com/goharbor/harbor/src/pkg/immutabletag/model"
"github.com/goharbor/harbor/src/pkg/immutable/match"
"github.com/goharbor/harbor/src/pkg/immutable/model"
)
// Matcher ...
type Matcher struct {
rules []model.Metadata
rules []*model.Metadata
}
// Match ...
func (rm *Matcher) Match(pid int64, c iselector.Candidate) (bool, error) {
if err := rm.getImmutableRules(pid); err != nil {
func (rm *Matcher) Match(ctx context.Context, pid int64, c iselector.Candidate) (bool, error) {
if err := rm.getImmutableRules(ctx, pid); err != nil {
return false, err
}
@ -70,8 +72,8 @@ func (rm *Matcher) Match(pid int64, c iselector.Candidate) (bool, error) {
return false, nil
}
func (rm *Matcher) getImmutableRules(pid int64) error {
rules, err := immutabletag.ImmuCtr.ListImmutableRules(pid)
func (rm *Matcher) getImmutableRules(ctx context.Context, pid int64) error {
rules, err := immutable.Ctr.ListImmutableRules(ctx, q.New(q.KeyWords{"ProjectID": pid}))
if err != nil {
return err
}

View File

@ -2,9 +2,10 @@ package rule
import (
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/controller/immutable"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/lib/selector"
"github.com/goharbor/harbor/src/pkg/immutabletag"
"github.com/goharbor/harbor/src/pkg/immutabletag/model"
"github.com/goharbor/harbor/src/pkg/immutable/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
@ -18,7 +19,7 @@ type MatchTestSuite struct {
t *testing.T
assert *assert.Assertions
require *require.Assertions
ctr immutabletag.Controller
ctr immutable.Controller
ruleID int64
ruleID2 int64
}
@ -28,7 +29,7 @@ func (s *MatchTestSuite) SetupSuite() {
s.t = s.T()
s.assert = assert.New(s.t)
s.require = require.New(s.t)
s.ctr = immutabletag.ImmuCtr
s.ctr = immutable.Ctr
}
func (s *MatchTestSuite) TestImmuMatch() {
@ -77,11 +78,11 @@ func (s *MatchTestSuite) TestImmuMatch() {
},
}
id, err := s.ctr.CreateImmutableRule(rule)
id, err := s.ctr.CreateImmutableRule(orm.Context(), rule)
s.ruleID = id
s.require.Nil(err)
id, err = s.ctr.CreateImmutableRule(rule2)
id, err = s.ctr.CreateImmutableRule(orm.Context(), rule2)
s.ruleID2 = id
s.require.Nil(err)
@ -93,7 +94,7 @@ func (s *MatchTestSuite) TestImmuMatch() {
Repository: "redis",
Tags: []string{"release-1.10"},
}
isMatch, err := match.Match(1, c1)
isMatch, err := match.Match(orm.Context(), 1, c1)
s.require.Equal(isMatch, true)
s.require.Nil(err)
@ -104,7 +105,7 @@ func (s *MatchTestSuite) TestImmuMatch() {
Tags: []string{"1.10"},
Kind: selector.Image,
}
isMatch, err = match.Match(1, c2)
isMatch, err = match.Match(orm.Context(), 1, c2)
s.require.Equal(isMatch, false)
s.require.Nil(err)
@ -115,7 +116,7 @@ func (s *MatchTestSuite) TestImmuMatch() {
Tags: []string{"9.4.8"},
Kind: selector.Image,
}
isMatch, err = match.Match(1, c3)
isMatch, err = match.Match(orm.Context(), 1, c3)
s.require.Equal(isMatch, true)
s.require.Nil(err)
@ -126,17 +127,17 @@ func (s *MatchTestSuite) TestImmuMatch() {
Tags: []string{"world"},
Kind: selector.Image,
}
isMatch, err = match.Match(1, c4)
isMatch, err = match.Match(orm.Context(), 1, c4)
s.require.Equal(isMatch, false)
s.require.Nil(err)
}
// TearDownSuite clears env for test suite
func (s *MatchTestSuite) TearDownSuite() {
err := s.ctr.DeleteImmutableRule(s.ruleID)
err := s.ctr.DeleteImmutableRule(orm.Context(), s.ruleID)
require.NoError(s.T(), err, "delete immutable")
err = s.ctr.DeleteImmutableRule(s.ruleID2)
err = s.ctr.DeleteImmutableRule(orm.Context(), s.ruleID2)
require.NoError(s.T(), err, "delete immutable")
}

View File

@ -1,80 +0,0 @@
package immutabletag
import (
"fmt"
"github.com/goharbor/harbor/src/pkg/immutabletag/model"
)
var (
// ImmuCtr is a global variable for the default immutable controller implementation
ImmuCtr = NewAPIController(NewDefaultRuleManager())
)
// Controller to handle the requests related with immutabletag
type Controller interface {
// GetImmutableRule ...
GetImmutableRule(id int64) (*model.Metadata, error)
// CreateImmutableRule ...
CreateImmutableRule(m *model.Metadata) (int64, error)
// DeleteImmutableRule ...
DeleteImmutableRule(id int64) error
// UpdateImmutableRule ...
UpdateImmutableRule(pid int64, m *model.Metadata) error
// ListImmutableRules ...
ListImmutableRules(pid int64) ([]model.Metadata, error)
}
// DefaultAPIController ...
type DefaultAPIController struct {
manager Manager
}
// GetImmutableRule ...
func (r *DefaultAPIController) GetImmutableRule(id int64) (*model.Metadata, error) {
return r.manager.GetImmutableRule(id)
}
// DeleteImmutableRule ...
func (r *DefaultAPIController) DeleteImmutableRule(id int64) error {
_, err := r.manager.DeleteImmutableRule(id)
return err
}
// CreateImmutableRule ...
func (r *DefaultAPIController) CreateImmutableRule(m *model.Metadata) (int64, error) {
return r.manager.CreateImmutableRule(m)
}
// UpdateImmutableRule ...
func (r *DefaultAPIController) UpdateImmutableRule(pid int64, m *model.Metadata) error {
m0, err := r.manager.GetImmutableRule(m.ID)
if err != nil {
return err
}
if m0 == nil {
return fmt.Errorf("the immutable tag rule is not found id:%v", m.ID)
}
if m0.Disabled != m.Disabled {
_, err := r.manager.EnableImmutableRule(m.ID, m.Disabled)
return err
}
_, err = r.manager.UpdateImmutableRule(pid, m)
return err
}
// ListImmutableRules ...
func (r *DefaultAPIController) ListImmutableRules(pid int64) ([]model.Metadata, error) {
return r.manager.QueryImmutableRuleByProjectID(pid)
}
// NewAPIController ...
func NewAPIController(immutableMgr Manager) Controller {
return &DefaultAPIController{
manager: immutableMgr,
}
}

View File

@ -1,116 +0,0 @@
package dao
import (
"fmt"
"github.com/astaxie/beego/orm"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/pkg/immutabletag/dao/model"
)
// ImmutableRuleDao defines the interface to access the ImmutableRule data model
type ImmutableRuleDao interface {
CreateImmutableRule(ir *model.ImmutableRule) (int64, error)
UpdateImmutableRule(projectID int64, ir *model.ImmutableRule) (int64, error)
ToggleImmutableRule(id int64, status bool) (int64, error)
GetImmutableRule(id int64) (*model.ImmutableRule, error)
QueryImmutableRuleByProjectID(projectID int64) ([]model.ImmutableRule, error)
QueryEnabledImmutableRuleByProjectID(projectID int64) ([]model.ImmutableRule, error)
DeleteImmutableRule(id int64) (int64, error)
}
// New creates a default implementation for ImmutableRuleDao
func New() ImmutableRuleDao {
return &immutableRuleDao{}
}
type immutableRuleDao struct{}
// CreateImmutableRule creates the Immutable Rule
func (i *immutableRuleDao) CreateImmutableRule(ir *model.ImmutableRule) (int64, error) {
ir.Disabled = false
o := dao.GetOrmer()
id, err := o.Insert(ir)
if err != nil {
if dao.IsDupRecErr(err) {
return id, errors.ConflictError(err)
}
return id, err
}
return id, nil
}
// UpdateImmutableRule update the immutable rules
func (i *immutableRuleDao) UpdateImmutableRule(projectID int64, ir *model.ImmutableRule) (int64, error) {
ir.ProjectID = projectID
o := dao.GetOrmer()
id, err := o.Update(ir, "TagFilter")
if err != nil {
if errors.Is(err, orm.ErrNoRows) {
return id, errors.NotFoundError(err)
}
return id, err
}
return id, nil
}
// ToggleImmutableRule enable/disable immutable rules
func (i *immutableRuleDao) ToggleImmutableRule(id int64, status bool) (int64, error) {
o := dao.GetOrmer()
ir := &model.ImmutableRule{ID: id, Disabled: status}
id, err := o.Update(ir, "Disabled")
if err != nil {
if errors.Is(err, orm.ErrNoRows) {
return id, errors.NotFoundError(err)
}
return id, err
}
return id, nil
}
// GetImmutableRule get immutable rule
func (i *immutableRuleDao) GetImmutableRule(id int64) (*model.ImmutableRule, error) {
o := dao.GetOrmer()
ir := &model.ImmutableRule{ID: id}
err := o.Read(ir)
if err != nil {
if errors.Is(err, orm.ErrNoRows) {
return nil, errors.New(err).WithCode(errors.NotFoundCode).
WithMessage(fmt.Sprintf("the immutable rule %d is not found.", id))
}
return nil, err
}
return ir, nil
}
// QueryImmutableRuleByProjectID get all immutable rule by project
func (i *immutableRuleDao) QueryImmutableRuleByProjectID(projectID int64) ([]model.ImmutableRule, error) {
o := dao.GetOrmer()
qs := o.QueryTable(&model.ImmutableRule{}).Filter("ProjectID", projectID)
r := make([]model.ImmutableRule, 0)
_, err := qs.All(&r)
if err != nil {
return nil, fmt.Errorf("failed to get immutable tag rule by projectID %d, error: %w", projectID, err)
}
return r, nil
}
// QueryEnabledImmutableRuleByProjectID get all enabled immutable rule by project
func (i *immutableRuleDao) QueryEnabledImmutableRuleByProjectID(projectID int64) ([]model.ImmutableRule, error) {
o := dao.GetOrmer()
qs := o.QueryTable(&model.ImmutableRule{}).Filter("ProjectID", projectID).Filter("Disabled", false)
var r []model.ImmutableRule
_, err := qs.All(&r)
if err != nil {
return nil, fmt.Errorf("failed to get enabled immutable tag rule for by projectID %d, error: %w", projectID, err)
}
return r, nil
}
// DeleteImmutableRule delete the immutable rule
func (i *immutableRuleDao) DeleteImmutableRule(id int64) (int64, error) {
o := dao.GetOrmer()
ir := &model.ImmutableRule{ID: id}
return o.Delete(ir)
}

View File

@ -1,124 +0,0 @@
package immutabletag
import (
"encoding/json"
"sort"
"github.com/goharbor/harbor/src/pkg/immutabletag/dao"
dao_model "github.com/goharbor/harbor/src/pkg/immutabletag/dao/model"
"github.com/goharbor/harbor/src/pkg/immutabletag/model"
)
var (
// Mgr is a global variable for the default immutablerule manager implementation
Mgr = NewDefaultRuleManager()
)
// Manager ...
type Manager interface {
// CreateImmutableRule creates the Immutable Rule
CreateImmutableRule(m *model.Metadata) (int64, error)
// UpdateImmutableRule update the immutable rules
UpdateImmutableRule(projectID int64, ir *model.Metadata) (int64, error)
// EnableImmutableRule enable/disable immutable rules
EnableImmutableRule(id int64, enabled bool) (int64, error)
// GetImmutableRule get immutable rule
GetImmutableRule(id int64) (*model.Metadata, error)
// QueryImmutableRuleByProjectID get all immutable rule by project
QueryImmutableRuleByProjectID(projectID int64) ([]model.Metadata, error)
// QueryEnabledImmutableRuleByProjectID get all enabled immutable rule by project
QueryEnabledImmutableRuleByProjectID(projectID int64) ([]model.Metadata, error)
// DeleteImmutableRule delete the immutable rule
DeleteImmutableRule(id int64) (int64, error)
}
type defaultRuleManager struct {
dao dao.ImmutableRuleDao
}
func (drm *defaultRuleManager) CreateImmutableRule(ir *model.Metadata) (int64, error) {
daoRule := &dao_model.ImmutableRule{}
daoRule.Disabled = ir.Disabled
daoRule.ProjectID = ir.ProjectID
data, _ := json.Marshal(ir)
daoRule.TagFilter = string(data)
return drm.dao.CreateImmutableRule(daoRule)
}
func (drm *defaultRuleManager) UpdateImmutableRule(projectID int64, ir *model.Metadata) (int64, error) {
daoRule := &dao_model.ImmutableRule{}
data, _ := json.Marshal(ir)
daoRule.ID = ir.ID
daoRule.TagFilter = string(data)
return drm.dao.UpdateImmutableRule(projectID, daoRule)
}
func (drm *defaultRuleManager) EnableImmutableRule(id int64, enabled bool) (int64, error) {
return drm.dao.ToggleImmutableRule(id, enabled)
}
func (drm *defaultRuleManager) GetImmutableRule(id int64) (*model.Metadata, error) {
daoRule, err := drm.dao.GetImmutableRule(id)
if err != nil {
return nil, err
}
rule := &model.Metadata{}
if daoRule == nil {
return nil, nil
}
if err = json.Unmarshal([]byte(daoRule.TagFilter), rule); err != nil {
return nil, err
}
rule.ID = daoRule.ID
rule.Disabled = daoRule.Disabled
return rule, nil
}
func (drm *defaultRuleManager) QueryImmutableRuleByProjectID(projectID int64) ([]model.Metadata, error) {
daoRules, err := drm.dao.QueryImmutableRuleByProjectID(projectID)
if err != nil {
return nil, err
}
rules := make([]model.Metadata, 0)
for _, daoRule := range daoRules {
rule := model.Metadata{}
if err = json.Unmarshal([]byte(daoRule.TagFilter), &rule); err != nil {
return nil, err
}
rule.ID = daoRule.ID
rule.Disabled = daoRule.Disabled
rules = append(rules, rule)
}
sort.Slice(rules, func(i, j int) bool {
return rules[i].ID < rules[j].ID
})
return rules, nil
}
func (drm *defaultRuleManager) QueryEnabledImmutableRuleByProjectID(projectID int64) ([]model.Metadata, error) {
daoRules, err := drm.dao.QueryEnabledImmutableRuleByProjectID(projectID)
if err != nil {
return nil, err
}
var rules []model.Metadata
for _, daoRule := range daoRules {
rule := model.Metadata{}
if err = json.Unmarshal([]byte(daoRule.TagFilter), &rule); err != nil {
return nil, err
}
rule.ID = daoRule.ID
rules = append(rules, rule)
}
return rules, nil
}
func (drm *defaultRuleManager) DeleteImmutableRule(id int64) (int64, error) {
return drm.dao.DeleteImmutableRule(id)
}
// NewDefaultRuleManager return a new instance of defaultRuleManager
func NewDefaultRuleManager() Manager {
return &defaultRuleManager{
dao: dao.New(),
}
}

View File

@ -111,7 +111,7 @@ func (pj *Job) Run(ctx job.Context, params job.Parameters) error {
}
// Run the flow
results, err := processor.Process(allCandidates)
results, err := processor.Process(ctx.SystemContext(), allCandidates)
if err != nil {
return logError(myLogger, err)
}

View File

@ -15,6 +15,7 @@
package index
import (
"context"
"github.com/goharbor/harbor/src/lib/selector"
"testing"
"time"
@ -57,7 +58,7 @@ func (suite *IndexTestSuite) TestGet() {
require.NoError(suite.T(), err)
require.NotNil(suite.T(), p)
results, err := p.Perform(suite.candidates)
results, err := p.Perform(context.TODO(), suite.candidates)
require.NoError(suite.T(), err)
assert.Equal(suite.T(), 1, len(results))
assert.Condition(suite.T(), func() (success bool) {
@ -77,7 +78,7 @@ type fakePerformer struct {
}
// Perform the artifacts
func (p *fakePerformer) Perform(candidates []*selector.Candidate) (results []*selector.Result, err error) {
func (p *fakePerformer) Perform(ctx context.Context, candidates []*selector.Candidate) (results []*selector.Result, err error) {
for _, c := range candidates {
results = append(results, &selector.Result{
Target: c,

View File

@ -15,10 +15,11 @@
package action
import (
"context"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/selector"
"github.com/goharbor/harbor/src/pkg/immutabletag/match/rule"
"github.com/goharbor/harbor/src/pkg/immutable/match/rule"
"github.com/goharbor/harbor/src/pkg/retention/dep"
)
@ -37,7 +38,7 @@ type Performer interface {
// Returns:
// []*art.Result : result infos
// error : common error if any errors occurred
Perform(candidates []*selector.Candidate) ([]*selector.Result, error)
Perform(ctx context.Context, candidates []*selector.Candidate) ([]*selector.Result, error)
}
// PerformerFactory is factory method for creating Performer
@ -51,7 +52,7 @@ type retainAction struct {
}
// Perform the action
func (ra *retainAction) Perform(candidates []*selector.Candidate) (results []*selector.Result, err error) {
func (ra *retainAction) Perform(ctx context.Context, candidates []*selector.Candidate) (results []*selector.Result, err error) {
retainedShare := make(map[string]bool)
immutableShare := make(map[string]bool)
for _, c := range candidates {
@ -62,7 +63,7 @@ func (ra *retainAction) Perform(candidates []*selector.Candidate) (results []*se
if _, ok := retainedShare[c.Hash()]; ok {
continue
}
if isImmutable(c) {
if isImmutable(ctx, c) {
immutableShare[c.Hash()] = true
}
}
@ -91,11 +92,11 @@ func (ra *retainAction) Perform(candidates []*selector.Candidate) (results []*se
return
}
func isImmutable(c *selector.Candidate) bool {
func isImmutable(ctx context.Context, c *selector.Candidate) bool {
projectID := c.NamespaceID
repo := c.Repository
_, repoName := utils.ParseRepository(repo)
matched, err := rule.NewRuleMatcher().Match(projectID, selector.Candidate{
matched, err := rule.NewRuleMatcher().Match(ctx, projectID, selector.Candidate{
Repository: repoName,
Tags: c.Tags,
NamespaceID: projectID,

View File

@ -16,13 +16,14 @@ package action
import (
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/controller/immutable"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/lib/selector"
"github.com/goharbor/harbor/src/pkg/immutabletag"
"testing"
"time"
"github.com/goharbor/harbor/src/lib/errors"
immumodel "github.com/goharbor/harbor/src/pkg/immutabletag/model"
immumodel "github.com/goharbor/harbor/src/pkg/immutable/model"
"github.com/goharbor/harbor/src/pkg/retention/dep"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -93,7 +94,7 @@ func (suite *TestPerformerSuite) TestPerform() {
},
}
results, err := p.Perform(candidates)
results, err := p.Perform(orm.Context(), candidates)
require.NoError(suite.T(), err)
require.Equal(suite.T(), 1, len(results))
require.NotNil(suite.T(), results[0].Target)
@ -171,10 +172,10 @@ func (suite *TestPerformerSuite) TestPerformImmutable() {
},
},
}
imid, e := immutabletag.ImmuCtr.CreateImmutableRule(rule)
imid, e := immutable.Ctr.CreateImmutableRule(orm.Context(), rule)
assert.NoError(suite.T(), e)
defer func() {
assert.NoError(suite.T(), immutabletag.ImmuCtr.DeleteImmutableRule(imid))
assert.NoError(suite.T(), immutable.Ctr.DeleteImmutableRule(orm.Context(), imid))
}()
candidates := []*selector.Candidate{
@ -190,7 +191,7 @@ func (suite *TestPerformerSuite) TestPerformImmutable() {
},
}
results, err := p.Perform(candidates)
results, err := p.Perform(orm.Context(), candidates)
require.NoError(suite.T(), err)
require.Equal(suite.T(), 3, len(results))
for _, r := range results {

View File

@ -15,6 +15,7 @@
package or
import (
"context"
"github.com/goharbor/harbor/src/lib/selector"
"sync"
@ -59,7 +60,7 @@ func New(parameters []*alg.Parameter) alg.Processor {
}
// Process the candidates with the rules
func (p *processor) Process(artifacts []*selector.Candidate) ([]*selector.Result, error) {
func (p *processor) Process(ctx context.Context, artifacts []*selector.Candidate) ([]*selector.Result, error) {
if len(artifacts) == 0 {
log.Debug("no artifacts to retention")
return make([]*selector.Result, 0), nil
@ -181,7 +182,7 @@ func (p *processor) Process(artifacts []*selector.Candidate) ([]*selector.Result
cl := hash.toList()
if pf, ok := p.performers[act]; ok {
if theRes, err := pf.Perform(cl); err != nil {
if theRes, err := pf.Perform(ctx, cl); err != nil {
attachedErr = err
} else {
results = append(results, theRes...)

View File

@ -17,6 +17,7 @@ package or
import (
"errors"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/lib/selector"
"testing"
"time"
@ -111,7 +112,7 @@ func (suite *ProcessorTestSuite) TestProcess() {
p := New(params)
results, err := p.Process(suite.all)
results, err := p.Process(orm.Context(), suite.all)
require.NoError(suite.T(), err)
assert.Equal(suite.T(), 1, len(results))
assert.Condition(suite.T(), func() bool {
@ -142,7 +143,7 @@ func (suite *ProcessorTestSuite) TestProcess2() {
p := New(params)
results, err := p.Process(suite.all)
results, err := p.Process(orm.Context(), suite.all)
require.NoError(suite.T(), err)
assert.Equal(suite.T(), 1, len(results))
assert.Condition(suite.T(), func() bool {

View File

@ -15,6 +15,7 @@
package alg
import (
"context"
"github.com/goharbor/harbor/src/lib/selector"
"github.com/goharbor/harbor/src/pkg/retention/policy/action"
"github.com/goharbor/harbor/src/pkg/retention/policy/rule"
@ -32,7 +33,7 @@ type Processor interface {
// Returns:
// []*art.Result : the processed results
// error : common error object if any errors occurred
Process(artifacts []*selector.Candidate) ([]*selector.Result, error)
Process(ctx context.Context, artifacts []*selector.Candidate) ([]*selector.Result, error)
}
// Parameter for constructing a processor

View File

@ -16,6 +16,7 @@ package policy
import (
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/lib/selector"
"testing"
"time"
@ -148,7 +149,7 @@ func (suite *TestBuilderSuite) TestBuild() {
require.NoError(suite.T(), err)
require.NotNil(suite.T(), p)
results, err := p.Process(suite.all)
results, err := p.Process(orm.Context(), suite.all)
require.NoError(suite.T(), err)
assert.Equal(suite.T(), 1, len(results))
assert.Condition(suite.T(), func() (success bool) {

View File

@ -3,6 +3,7 @@ package immutable
import (
"context"
"fmt"
"github.com/goharbor/harbor/src/controller/immutable"
"math/rand"
"net/http"
"net/http/httptest"
@ -15,8 +16,7 @@ import (
"github.com/goharbor/harbor/src/lib"
internal_orm "github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/immutabletag"
immu_model "github.com/goharbor/harbor/src/pkg/immutabletag/model"
immu_model "github.com/goharbor/harbor/src/pkg/immutable/model"
"github.com/goharbor/harbor/src/pkg/repository"
"github.com/goharbor/harbor/src/pkg/tag"
tag_model "github.com/goharbor/harbor/src/pkg/tag/model/tag"
@ -141,7 +141,7 @@ func (suite *HandlerSuite) addImmutableRule(pid int64) int64 {
},
},
}
id, err := immutabletag.ImmuCtr.CreateImmutableRule(metadata)
id, err := immutable.Ctr.CreateImmutableRule(internal_orm.Context(), metadata)
require.NoError(suite.T(), err, "nil error expected but got %s", err)
return id
}
@ -163,7 +163,7 @@ func (suite *HandlerSuite) TestPutDeleteManifestCreated() {
artifact.Mgr.Delete(ctx, afID)
repository.Mgr.Delete(ctx, repoID)
tag.Mgr.Delete(ctx, tagID)
immutabletag.ImmuCtr.DeleteImmutableRule(immuRuleID)
immutable.Ctr.DeleteImmutableRule(internal_orm.Context(), immuRuleID)
}()
code1 := doPutManifestRequest(projectID, projectName, "photon", "release-1.10", dgt)

View File

@ -48,6 +48,7 @@ func New() http.Handler {
GCAPI: newGCAPI(),
QuotaAPI: newQuotaAPI(),
RetentionAPI: newRetentionAPI(),
ImmutableAPI: newImmutableAPI(),
OidcAPI: newOIDCAPI(),
})
if err != nil {

View File

@ -0,0 +1,143 @@
package handler
import (
"context"
"errors"
"fmt"
"github.com/go-openapi/runtime/middleware"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/controller/immutable"
"github.com/goharbor/harbor/src/controller/project"
"github.com/goharbor/harbor/src/lib"
"github.com/goharbor/harbor/src/pkg/immutable/model"
handler_model "github.com/goharbor/harbor/src/server/v2.0/handler/model"
"github.com/goharbor/harbor/src/server/v2.0/models"
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/immutable"
"strings"
)
func newImmutableAPI() *immutableAPI {
return &immutableAPI{
immuCtl: immutable.Ctr,
projectCtr: project.Ctl,
}
}
type immutableAPI struct {
BaseAPI
immuCtl immutable.Controller
projectCtr project.Controller
}
func (ia *immutableAPI) CreateImmuRule(ctx context.Context, params operation.CreateImmuRuleParams) middleware.Responder {
projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName)
if err := ia.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionCreate, rbac.ResourceImmutableTag); err != nil {
return ia.SendError(ctx, err)
}
metadata := model.Metadata{}
lib.JSONCopy(&metadata, params.ImmutableRule)
projectID, err := ia.getProjectID(ctx, projectNameOrID)
if err != nil {
return ia.SendError(ctx, err)
}
metadata.ProjectID = projectID
id, err := ia.immuCtl.CreateImmutableRule(ctx, &metadata)
if err != nil {
return ia.SendError(ctx, err)
}
location := fmt.Sprintf("%s/%d", strings.TrimSuffix(params.HTTPRequest.URL.Path, "/"), id)
return operation.NewCreateImmuRuleCreated().WithLocation(location)
}
func (ia *immutableAPI) DeleteImmuRule(ctx context.Context, params operation.DeleteImmuRuleParams) middleware.Responder {
projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName)
if err := ia.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionDelete, rbac.ResourceImmutableTag); err != nil {
return ia.SendError(ctx, err)
}
if err := ia.immuCtl.DeleteImmutableRule(ctx, params.ImmutableRuleID); err != nil {
return ia.SendError(ctx, err)
}
return operation.NewDeleteImmuRuleOK()
}
func (ia *immutableAPI) UpdateImmuRule(ctx context.Context, params operation.UpdateImmuRuleParams) middleware.Responder {
projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName)
if err := ia.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionUpdate, rbac.ResourceImmutableTag); err != nil {
return ia.SendError(ctx, err)
}
metadata := model.Metadata{}
lib.JSONCopy(&metadata, params.ImmutableRule)
projectID, err := ia.getProjectID(ctx, projectNameOrID)
if err != nil {
return ia.SendError(ctx, err)
}
metadata.ProjectID = projectID
if err := ia.immuCtl.UpdateImmutableRule(ctx, projectID, &metadata); err != nil {
return ia.SendError(ctx, err)
}
return operation.NewUpdateImmuRuleOK()
}
func (ia *immutableAPI) ListImmuRules(ctx context.Context, params operation.ListImmuRulesParams) middleware.Responder {
projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName)
if err := ia.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionList, rbac.ResourceImmutableTag); err != nil {
return ia.SendError(ctx, err)
}
query, err := ia.BuildQuery(ctx, params.Q, params.Page, params.PageSize)
if err != nil {
return ia.SendError(ctx, err)
}
projectID, err := ia.getProjectID(ctx, projectNameOrID)
if err != nil {
return ia.SendError(ctx, err)
}
query.Keywords["ProjectID"] = projectID
total, err := ia.immuCtl.Count(ctx, query)
if err != nil {
return ia.SendError(ctx, err)
}
rules, err := ia.immuCtl.ListImmutableRules(ctx, query)
if err != nil {
return ia.SendError(ctx, err)
}
var results []*models.ImmutableRule
for _, r := range rules {
results = append(results, handler_model.NewImmutableRule(r).ToSwagger())
}
return operation.NewListImmuRulesOK().
WithXTotalCount(total).
WithLink(ia.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
WithPayload(results)
}
func (ia *immutableAPI) getProjectID(ctx context.Context, projectNameOrID interface{}) (int64, error) {
projectName, ok := projectNameOrID.(string)
if ok {
p, err := ia.projectCtr.Get(ctx, projectName, project.Metadata(false))
if err != nil {
return 0, err
}
return p.ProjectID, nil
}
projectID, ok := projectNameOrID.(int64)
if ok {
return projectID, nil
}
return 0, errors.New("unknown project identifier type")
}

View File

@ -0,0 +1,61 @@
package model
import (
pkg_model "github.com/goharbor/harbor/src/pkg/immutable/model"
"github.com/goharbor/harbor/src/server/v2.0/models"
)
// ImmutableRule ...
type ImmutableRule struct {
*pkg_model.Metadata
}
// ToSwagger ...
func (ir *ImmutableRule) ToSwagger() *models.ImmutableRule {
return &models.ImmutableRule{
ID: ir.ID,
Disabled: ir.Disabled,
Action: ir.Action,
Priority: int64(ir.Priority),
ScopeSelectors: ir.ToScopeSelectors(),
TagSelectors: ir.ToTagSelectors(),
Template: ir.Template,
}
}
// ToTagSelectors ...
func (ir *ImmutableRule) ToTagSelectors() []*models.ImmutableSelector {
var results []*models.ImmutableSelector
for _, t := range ir.TagSelectors {
results = append(results, &models.ImmutableSelector{
Decoration: t.Decoration,
Kind: t.Kind,
Pattern: t.Pattern,
})
}
return results
}
// ToScopeSelectors ...
func (ir *ImmutableRule) ToScopeSelectors() map[string][]models.ImmutableSelector {
results := map[string][]models.ImmutableSelector{}
for k, v := range ir.ScopeSelectors {
var scopeSelectors []models.ImmutableSelector
for _, s := range v {
scopeSelectors = append(scopeSelectors, models.ImmutableSelector{
Decoration: s.Decoration,
Kind: s.Kind,
Pattern: s.Pattern,
})
}
results[k] = scopeSelectors
}
return results
}
// NewImmutableRule ...
func NewImmutableRule(meta *pkg_model.Metadata) *ImmutableRule {
return &ImmutableRule{
Metadata: meta,
}
}

View File

@ -55,9 +55,6 @@ func registerLegacyRoutes() {
beego.Router("/api/"+version+"/projects/:pid([0-9]+)/webhook/events", &api.NotificationPolicyAPI{}, "get:GetSupportedEventTypes")
beego.Router("/api/"+version+"/projects/:pid([0-9]+)/webhook/jobs/", &api.NotificationJobAPI{}, "get:List")
beego.Router("/api/"+version+"/projects/:pid([0-9]+)/immutabletagrules", &api.ImmutableTagRuleAPI{}, "get:List;post:Post")
beego.Router("/api/"+version+"/projects/:pid([0-9]+)/immutabletagrules/:id([0-9]+)", &api.ImmutableTagRuleAPI{})
beego.Router("/api/"+version+"/configurations", &api.ConfigAPI{}, "get:Get;put:Put")
beego.Router("/api/"+version+"/statistics", &api.StatisticAPI{})
beego.Router("/api/"+version+"/labels", &api.LabelAPI{}, "post:Post;get:List")
@ -70,9 +67,6 @@ func registerLegacyRoutes() {
beego.Router("/api/"+version+"/registries/:id/info", &api.RegistryAPI{}, "get:GetInfo")
beego.Router("/api/"+version+"/registries/:id/namespace", &api.RegistryAPI{}, "get:GetNamespace")
beego.Router("/api/"+version+"/projects/:pid([0-9]+)/immutabletagrules", &api.ImmutableTagRuleAPI{}, "get:List;post:Post")
beego.Router("/api/"+version+"/projects/:pid([0-9]+)/immutabletagrules/:id([0-9]+)", &api.ImmutableTagRuleAPI{})
// APIs for chart repository
if config.WithChartMuseum() {
// Labels for chart

View File

@ -0,0 +1,148 @@
// Code generated by mockery v2.1.0. DO NOT EDIT.
package dao
import (
context "context"
mock "github.com/stretchr/testify/mock"
model "github.com/goharbor/harbor/src/pkg/immutable/dao/model"
q "github.com/goharbor/harbor/src/lib/q"
)
// DAO is an autogenerated mock type for the DAO type
type DAO struct {
mock.Mock
}
// Count provides a mock function with given fields: ctx, query
func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) {
ret := _m.Called(ctx, query)
var r0 int64
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok {
r0 = rf(ctx, query)
} else {
r0 = ret.Get(0).(int64)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
r1 = rf(ctx, query)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CreateImmutableRule provides a mock function with given fields: ctx, ir
func (_m *DAO) CreateImmutableRule(ctx context.Context, ir *model.ImmutableRule) (int64, error) {
ret := _m.Called(ctx, ir)
var r0 int64
if rf, ok := ret.Get(0).(func(context.Context, *model.ImmutableRule) int64); ok {
r0 = rf(ctx, ir)
} else {
r0 = ret.Get(0).(int64)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *model.ImmutableRule) error); ok {
r1 = rf(ctx, ir)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DeleteImmutableRule provides a mock function with given fields: ctx, id
func (_m *DAO) DeleteImmutableRule(ctx context.Context, id int64) error {
ret := _m.Called(ctx, id)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// GetImmutableRule provides a mock function with given fields: ctx, id
func (_m *DAO) GetImmutableRule(ctx context.Context, id int64) (*model.ImmutableRule, error) {
ret := _m.Called(ctx, id)
var r0 *model.ImmutableRule
if rf, ok := ret.Get(0).(func(context.Context, int64) *model.ImmutableRule); ok {
r0 = rf(ctx, id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.ImmutableRule)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
r1 = rf(ctx, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListImmutableRules provides a mock function with given fields: ctx, query
func (_m *DAO) ListImmutableRules(ctx context.Context, query *q.Query) ([]*model.ImmutableRule, error) {
ret := _m.Called(ctx, query)
var r0 []*model.ImmutableRule
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*model.ImmutableRule); ok {
r0 = rf(ctx, query)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.ImmutableRule)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
r1 = rf(ctx, query)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ToggleImmutableRule provides a mock function with given fields: ctx, id, status
func (_m *DAO) ToggleImmutableRule(ctx context.Context, id int64, status bool) error {
ret := _m.Called(ctx, id, status)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64, bool) error); ok {
r0 = rf(ctx, id, status)
} else {
r0 = ret.Error(0)
}
return r0
}
// UpdateImmutableRule provides a mock function with given fields: ctx, projectID, ir
func (_m *DAO) UpdateImmutableRule(ctx context.Context, projectID int64, ir *model.ImmutableRule) error {
ret := _m.Called(ctx, projectID, ir)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64, *model.ImmutableRule) error); ok {
r0 = rf(ctx, projectID, ir)
} else {
r0 = ret.Error(0)
}
return r0
}

View File

@ -1,6 +1,7 @@
package immutabletag
package immutable
import (
"context"
"github.com/goharbor/harbor/src/lib/selector"
"github.com/stretchr/testify/mock"
)
@ -11,7 +12,7 @@ type FakeMatcher struct {
}
// Match ...
func (f *FakeMatcher) Match(pid int64, c selector.Candidate) (bool, error) {
func (f *FakeMatcher) Match(ctx context.Context, pid int64, c selector.Candidate) (bool, error) {
args := f.Called()
return args.Bool(0), args.Error(1)
}

View File

@ -34,3 +34,4 @@ package pkg
//go:generate mockery --case snake --dir ../../pkg/robot --name Manager --output ./robot --outpkg robot
//go:generate mockery --case snake --dir ../../pkg/robot/dao --name DAO --output ./robot/dao --outpkg dao
//go:generate mockery --case snake --dir ../../pkg/repository/dao --name DAO --output ./repository/dao --outpkg dao
//go:generate mockery --case snake --dir ../../pkg/immutable/dao --name DAO --output ./immutable/dao --outpkg dao

View File

@ -28,7 +28,7 @@ def get_endpoint():
def _create_client(server, credential, debug, api_type="products"):
cfg = None
if api_type in ('projectv2', 'artifact', 'repository', 'scanner', 'scan', 'scanall', 'preheat', 'quota', 'replication', 'robot', 'gc', 'retention'):
if api_type in ('projectv2', 'artifact', 'repository', 'scanner', 'scan', 'scanall', 'preheat', 'quota', 'replication', 'robot', 'gc', 'retention', "immutable"):
cfg = v2_swagger_client.Configuration()
else:
cfg = swagger_client.Configuration()
@ -65,6 +65,7 @@ def _create_client(server, credential, debug, api_type="products"):
"robot": v2_swagger_client.RobotApi(v2_swagger_client.ApiClient(cfg)),
"gc": v2_swagger_client.GcApi(v2_swagger_client.ApiClient(cfg)),
"retention": v2_swagger_client.RetentionApi(v2_swagger_client.ApiClient(cfg)),
"immutable": v2_swagger_client.ImmutableApi(v2_swagger_client.ApiClient(cfg)),
}.get(api_type,'Error: Wrong API type')
def _assert_status_code(expect_code, return_code, err_msg = r"HTTPS status code s not as we expected. Expected {}, while actual HTTPS status code is {}."):

View File

@ -1,16 +1,19 @@
# -*- coding: utf-8 -*-
import base
import swagger_client
from swagger_client.rest import ApiException
import v2_swagger_client
from v2_swagger_client.rest import ApiException
class Tag_Immutability(base.Base, object):
def __init__(self):
super(Tag_Immutability,self).__init__(api_type = "immutable")
class Tag_Immutability(base.Base):
def create_tag_immutability_policy_rule(self, project_id, selector_repository_decoration = "repoMatches",
selector_repository="**", selector_tag_decoration = "matches",
selector_tag="**", expect_status_code = 201, **kwargs):
#repoExcludes,excludes
client = self._get_client(**kwargs)
immutable_rule = swagger_client.ImmutableRule(
immutable_rule = v2_swagger_client.ImmutableRule(
action="immutable",
template="immutable_template",
priority = 0,
@ -32,7 +35,7 @@ class Tag_Immutability(base.Base):
]
)
try:
_, status_code, header = client.projects_project_id_immutabletagrules_post_with_http_info(project_id, immutable_rule)
_, status_code, header = client.create_immu_rule_with_http_info(project_id, immutable_rule)
except ApiException as e:
base._assert_status_code(expect_status_code, e.status)
else:
@ -42,11 +45,13 @@ class Tag_Immutability(base.Base):
def list_tag_immutability_policy_rules(self, project_id, **kwargs):
client = self._get_client(**kwargs)
return client.projects_project_id_immutabletagrules_get(project_id)
return client.list_immu_rules_with_http_info(project_id)
def get_rule(self, project_id, rule_id, **kwargs):
rules = self.list_tag_immutability_policy_rules(project_id, **kwargs)
for r in rules:
if len(rules) <= 0:
return None
for r in rules[0]:
if r.id == rule_id:
return r
return None
@ -67,7 +72,7 @@ class Tag_Immutability(base.Base):
rule.disabled = disabled
client = self._get_client(**kwargs)
try:
_, status_code, header = client.projects_project_id_immutabletagrules_id_put_with_http_info(project_id, rule_id, rule)
_, status_code, header = client.update_immu_rule_with_http_info(project_id, rule_id, rule)
except ApiException as e:
base._assert_status_code(expect_status_code, e.status)
if expect_response_body is not None: