mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-25 19:56:09 +01:00
Add immutable tag API
Signed-off-by: stonezdj <stonezdj@gmail.com>
This commit is contained in:
parent
ec559b0585
commit
cc22a175b9
@ -3989,7 +3989,124 @@ 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/ImmutableTagRule'
|
||||
'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: immutabletagrule
|
||||
in: body
|
||||
schema:
|
||||
$ref: '#/definitions/ImmutableTagRule'
|
||||
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: immutabletagrule
|
||||
in: body
|
||||
schema:
|
||||
$ref: '#/definitions/ImmutableTagRule'
|
||||
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.
|
||||
'/retentions/metadatas':
|
||||
get:
|
||||
summary: Get Retention Metadatas
|
||||
@ -6269,10 +6386,22 @@ definitions:
|
||||
type: integer
|
||||
retained:
|
||||
type: integer
|
||||
|
||||
QuotaSwitcher:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
description: The quota is enable or disable
|
||||
ImmutableTagRule:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
project_id:
|
||||
type: integer
|
||||
format: int64
|
||||
tag_filter:
|
||||
type: string
|
||||
enabled:
|
||||
type: boolean
|
||||
|
@ -15,7 +15,8 @@ func CreateImmutableRule(ir *models.ImmutableRule) (int64, error) {
|
||||
}
|
||||
|
||||
// UpdateImmutableRule update the immutable rules
|
||||
func UpdateImmutableRule(projectID int, ir *models.ImmutableRule) (int64, error) {
|
||||
func UpdateImmutableRule(projectID int64, ir *models.ImmutableRule) (int64, error) {
|
||||
ir.ProjectID = projectID
|
||||
o := GetOrmer()
|
||||
return o.Update(ir, "TagFilter")
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ const (
|
||||
ResourceReplicationTask = Resource("replication-task")
|
||||
ResourceRepository = Resource("repository")
|
||||
ResourceTagRetention = Resource("tag-retention")
|
||||
ResourceImmutableTag = Resource("immutable-tag")
|
||||
ResourceRepositoryLabel = Resource("repository-label")
|
||||
ResourceRepositoryTag = Resource("repository-tag")
|
||||
ResourceRepositoryTagLabel = Resource("repository-tag-label")
|
||||
|
@ -95,6 +95,11 @@ var (
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionOperate},
|
||||
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionUpdate},
|
||||
|
@ -68,6 +68,11 @@ var (
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionOperate},
|
||||
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionList},
|
||||
@ -153,6 +158,11 @@ var (
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionOperate},
|
||||
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceImmutableTag, Action: rbac.ActionList},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionList},
|
||||
|
@ -180,6 +180,7 @@ func runCodeCheckingCases(t *testing.T, cases ...*codeCheckingCase) {
|
||||
if c.postFunc != nil {
|
||||
if err := c.postFunc(resp); err != nil {
|
||||
t.Logf("error in running post function: %v", err)
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +177,8 @@ 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")
|
||||
|
166
src/core/api/immutabletagrule.go
Normal file
166
src/core/api/immutabletagrule.go
Normal file
@ -0,0 +1,166 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
"github.com/goharbor/harbor/src/pkg/immutabletag"
|
||||
"github.com/goharbor/harbor/src/pkg/retention/policy/rule"
|
||||
)
|
||||
|
||||
// ImmutableTagRuleAPI ...
|
||||
type ImmutableTagRuleAPI struct {
|
||||
BaseController
|
||||
manager immutabletag.RuleManager
|
||||
projectID int64
|
||||
ID int64
|
||||
}
|
||||
|
||||
// Prepare validates the user and projectID
|
||||
func (itr *ImmutableTagRuleAPI) Prepare() {
|
||||
itr.BaseController.Prepare()
|
||||
if !itr.SecurityCtx.IsAuthenticated() {
|
||||
itr.SendUnAuthorizedError(errors.New("Unauthorized"))
|
||||
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.SendBadRequestError(errors.New(text))
|
||||
return
|
||||
}
|
||||
itr.projectID = pid
|
||||
|
||||
ruleID, err := itr.GetInt64FromPath(":id")
|
||||
if err == nil || ruleID > 0 {
|
||||
itr.ID = ruleID
|
||||
}
|
||||
|
||||
itr.manager = immutabletag.NewDefaultRuleManager()
|
||||
|
||||
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.manager.QueryImmutableRuleByProjectID(itr.projectID)
|
||||
if err != nil {
|
||||
itr.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
itr.WriteJSONData(rules)
|
||||
}
|
||||
|
||||
// Post create immutable tag rule
|
||||
func (itr *ImmutableTagRuleAPI) Post() {
|
||||
ir := &models.ImmutableRule{}
|
||||
if err := itr.DecodeJSONReq(ir); err != nil {
|
||||
itr.SendBadRequestError(fmt.Errorf("the filter must be a valid json, failed to parse json, error %+v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if !isValidSelectorJSON(ir.TagFilter) {
|
||||
itr.SendBadRequestError(fmt.Errorf("the filter should be a valid json"))
|
||||
return
|
||||
}
|
||||
|
||||
ir.ProjectID = itr.projectID
|
||||
id, err := itr.manager.CreateImmutableRule(ir)
|
||||
if err != nil {
|
||||
itr.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
itr.Redirect(http.StatusCreated, strconv.FormatInt(id, 10))
|
||||
|
||||
}
|
||||
|
||||
// Delete delete immutable tag rule
|
||||
func (itr *ImmutableTagRuleAPI) Delete() {
|
||||
if itr.ID <= 0 {
|
||||
itr.SendBadRequestError(fmt.Errorf("invalid immutable rule id %d", itr.ID))
|
||||
return
|
||||
}
|
||||
_, err := itr.manager.DeleteImmutableRule(itr.ID)
|
||||
if err != nil {
|
||||
itr.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Put update an immutable tag rule
|
||||
func (itr *ImmutableTagRuleAPI) Put() {
|
||||
ir := &models.ImmutableRule{}
|
||||
if err := itr.DecodeJSONReq(ir); err != nil {
|
||||
itr.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
ir.ID = itr.ID
|
||||
ir.ProjectID = itr.projectID
|
||||
|
||||
if itr.ID <= 0 {
|
||||
itr.SendBadRequestError(fmt.Errorf("invalid immutable rule id %d", itr.ID))
|
||||
return
|
||||
}
|
||||
if len(ir.TagFilter) == 0 {
|
||||
if _, err := itr.manager.EnableImmutableRule(itr.ID, ir.Enabled); err != nil {
|
||||
itr.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
||||
if !isValidSelectorJSON(ir.TagFilter) {
|
||||
itr.SendBadRequestError(fmt.Errorf("the filter should be a valid json"))
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := itr.manager.UpdateImmutableRule(itr.ID, ir); err != nil {
|
||||
itr.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func isValidSelectorJSON(filter string) bool {
|
||||
tagSector := &rule.Metadata{}
|
||||
err := json.Unmarshal([]byte(filter), tagSector)
|
||||
if err != nil {
|
||||
log.Errorf("The json is %v", filter)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
264
src/core/api/immutabletagrule_test.go
Normal file
264
src/core/api/immutabletagrule_test.go
Normal file
@ -0,0 +1,264 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
"github.com/goharbor/harbor/src/pkg/immutabletag"
|
||||
)
|
||||
|
||||
func TestImmutableTagRuleAPI_List(t *testing.T) {
|
||||
|
||||
tagFilter := `{
|
||||
"id":0,
|
||||
"priority":0,
|
||||
"disabled":false,
|
||||
"action":"immutable",
|
||||
"template":"immutable_template",
|
||||
"tag_selectors":[{"kind":"doublestar","decoration":"matches","pattern":"**"}],
|
||||
"scope_selectors":{"repository":[{"kind":"doublestar","decoration":"repoMatches","pattern":"**"}]}
|
||||
}`
|
||||
|
||||
mgr := immutabletag.NewDefaultRuleManager()
|
||||
id, err := mgr.CreateImmutableRule(&models.ImmutableRule{ProjectID: 1, TagFilter: tagFilter})
|
||||
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 []models.ImmutableRule
|
||||
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].TagFilter != tagFilter {
|
||||
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) {
|
||||
|
||||
tagFilter := `{
|
||||
"id":0,
|
||||
"priority":0,
|
||||
"disabled":false,
|
||||
"action":"immutable",
|
||||
"template":"immutable_template",
|
||||
"tag_selectors":[{"kind":"doublestar","decoration":"matches","pattern":"**"}],
|
||||
"scope_selectors":{"repository":[{"kind":"doublestar","decoration":"repoMatches","pattern":"**"}]}
|
||||
}`
|
||||
body := &models.ImmutableRule{ProjectID: 1, TagFilter: tagFilter}
|
||||
cases := []*codeCheckingCase{
|
||||
// 401
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: "/api/projects/1/immutabletagrules",
|
||||
bodyJSON: body,
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
// 200
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: "/api/projects/1/immutabletagrules",
|
||||
credential: admin,
|
||||
bodyJSON: body,
|
||||
},
|
||||
code: http.StatusCreated,
|
||||
},
|
||||
// 200
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: "/api/projects/1/immutabletagrules",
|
||||
credential: projAdmin,
|
||||
bodyJSON: body,
|
||||
},
|
||||
code: http.StatusCreated,
|
||||
},
|
||||
// 403
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPost,
|
||||
url: "/api/projects/1/immutabletagrules",
|
||||
credential: projGuest,
|
||||
bodyJSON: body,
|
||||
},
|
||||
code: http.StatusForbidden,
|
||||
},
|
||||
}
|
||||
runCodeCheckingCases(t, cases...)
|
||||
|
||||
}
|
||||
|
||||
func TestImmutableTagRuleAPI_Put(t *testing.T) {
|
||||
tagFilter := `{
|
||||
"id":0,
|
||||
"priority":0,
|
||||
"disabled":false,
|
||||
"action":"immutable",
|
||||
"template":"immutable_template",
|
||||
"tag_selectors":[{"kind":"doublestar","decoration":"matches","pattern":"**"}],
|
||||
"scope_selectors":{"repository":[{"kind":"doublestar","decoration":"repoMatches","pattern":"**"}]}
|
||||
}`
|
||||
tagFilter2 := `{
|
||||
"id":0,
|
||||
"priority":0,
|
||||
"disabled":false,
|
||||
"action":"immutable",
|
||||
"template":"immutable_template",
|
||||
"tag_selectors":[{"kind":"doublestar","decoration":"matches","pattern":"release-1.6.0"}],
|
||||
"scope_selectors":{"repository":[{"kind":"doublestar","decoration":"repoMatches","pattern":"regids"}]}
|
||||
}`
|
||||
|
||||
mgr := immutabletag.NewDefaultRuleManager()
|
||||
id, err := mgr.CreateImmutableRule(&models.ImmutableRule{ProjectID: 1, TagFilter: tagFilter})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer mgr.DeleteImmutableRule(id)
|
||||
url := fmt.Sprintf("/api/projects/1/immutabletagrules/%d", id)
|
||||
body := &models.ImmutableRule{ID: id, ProjectID: 1, TagFilter: tagFilter2}
|
||||
cases := []*codeCheckingCase{
|
||||
// 401
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: url,
|
||||
bodyJSON: body,
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
// 200
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: url,
|
||||
credential: admin,
|
||||
bodyJSON: body,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
// 200
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: url,
|
||||
credential: projAdmin,
|
||||
bodyJSON: body,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
// 403
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodPut,
|
||||
url: url,
|
||||
credential: projGuest,
|
||||
bodyJSON: body,
|
||||
},
|
||||
code: http.StatusForbidden,
|
||||
},
|
||||
}
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
||||
|
||||
func TestImmutableTagRuleAPI_Delete(t *testing.T) {
|
||||
tagFilter := `{
|
||||
"id":0,
|
||||
"priority":0,
|
||||
"disabled":false,
|
||||
"action":"immutable",
|
||||
"template":"immutable_template",
|
||||
"tag_selectors":[{"kind":"doublestar","decoration":"matches","pattern":"**"}],
|
||||
"scope_selectors":{"repository":[{"kind":"doublestar","decoration":"repoMatches","pattern":"**"}]}
|
||||
}`
|
||||
mgr := immutabletag.NewDefaultRuleManager()
|
||||
id, err := mgr.CreateImmutableRule(&models.ImmutableRule{ProjectID: 1, TagFilter: tagFilter})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer mgr.DeleteImmutableRule(id)
|
||||
|
||||
url := fmt.Sprintf("/api/projects/1/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,
|
||||
},
|
||||
// 200
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodDelete,
|
||||
url: url,
|
||||
credential: projAdmin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
}
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
@ -121,6 +121,9 @@ func initRouters() {
|
||||
|
||||
beego.Router("/api/projects/:pid([0-9]+)/webhook/jobs/", &api.NotificationJobAPI{}, "get:List")
|
||||
|
||||
beego.Router("/api/projects/:pid([0-9]+)/immutabletagrules", &api.ImmutableTagRuleAPI{}, "get:List;post:Post")
|
||||
beego.Router("/api/projects/:pid([0-9]+)/immutabletagrules/:id([0-9]+)", &api.ImmutableTagRuleAPI{})
|
||||
|
||||
beego.Router("/api/internal/configurations", &api.ConfigAPI{}, "get:GetInternalConfig;put:Put")
|
||||
beego.Router("/api/configurations", &api.ConfigAPI{}, "get:Get;put:Put")
|
||||
beego.Router("/api/statistics", &api.StatisticAPI{})
|
||||
@ -164,6 +167,8 @@ func initRouters() {
|
||||
beego.Router("/api/retentions/:id/executions", &api.RetentionAPI{}, "get:ListRetentionExecs")
|
||||
beego.Router("/api/retentions/:id/executions/:eid/tasks", &api.RetentionAPI{}, "get:ListRetentionExecTasks")
|
||||
beego.Router("/api/retentions/:id/executions/:eid/tasks/:tid", &api.RetentionAPI{}, "get:GetRetentionExecTaskLog")
|
||||
beego.Router("/api/projects/:pid([0-9]+)/immutabletagrules", &api.ImmutableTagRuleAPI{}, "get:List;post:Post")
|
||||
beego.Router("/api/projects/:pid([0-9]+)/immutabletagrules/:id([0-9]+)", &api.ImmutableTagRuleAPI{})
|
||||
|
||||
beego.Router("/v2/*", &controllers.RegistryProxy{}, "*:Handle")
|
||||
|
||||
|
59
src/pkg/immutabletag/rulemanager.go
Normal file
59
src/pkg/immutabletag/rulemanager.go
Normal file
@ -0,0 +1,59 @@
|
||||
package immutabletag
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
)
|
||||
|
||||
// RuleManager ...
|
||||
type RuleManager interface {
|
||||
// CreateImmutableRule creates the Immutable Rule
|
||||
CreateImmutableRule(ir *models.ImmutableRule) (int64, error)
|
||||
// UpdateImmutableRule update the immutable rules
|
||||
UpdateImmutableRule(projectID int64, ir *models.ImmutableRule) (int64, error)
|
||||
// EnableImmutableRule enable/disable immutable rules
|
||||
EnableImmutableRule(id int64, enabled bool) (int64, error)
|
||||
// GetImmutableRule get immutable rule
|
||||
GetImmutableRule(id int64) (*models.ImmutableRule, error)
|
||||
// QueryImmutableRuleByProjectID get all immutable rule by project
|
||||
QueryImmutableRuleByProjectID(projectID int64) ([]models.ImmutableRule, error)
|
||||
// QueryEnabledImmutableRuleByProjectID get all enabled immutable rule by project
|
||||
QueryEnabledImmutableRuleByProjectID(projectID int64) ([]models.ImmutableRule, error)
|
||||
// DeleteImmutableRule delete the immutable rule
|
||||
DeleteImmutableRule(id int64) (int64, error)
|
||||
}
|
||||
|
||||
type defaultRuleManager struct{}
|
||||
|
||||
func (drm *defaultRuleManager) CreateImmutableRule(ir *models.ImmutableRule) (int64, error) {
|
||||
return dao.CreateImmutableRule(ir)
|
||||
}
|
||||
|
||||
func (drm *defaultRuleManager) UpdateImmutableRule(projectID int64, ir *models.ImmutableRule) (int64, error) {
|
||||
return dao.UpdateImmutableRule(projectID, ir)
|
||||
}
|
||||
|
||||
func (drm *defaultRuleManager) EnableImmutableRule(id int64, enabled bool) (int64, error) {
|
||||
return dao.ToggleImmutableRule(id, enabled)
|
||||
}
|
||||
|
||||
func (drm *defaultRuleManager) GetImmutableRule(id int64) (*models.ImmutableRule, error) {
|
||||
return dao.GetImmutableRule(id)
|
||||
}
|
||||
|
||||
func (drm *defaultRuleManager) QueryImmutableRuleByProjectID(projectID int64) ([]models.ImmutableRule, error) {
|
||||
return dao.QueryImmutableRuleByProjectID(projectID)
|
||||
}
|
||||
|
||||
func (drm *defaultRuleManager) QueryEnabledImmutableRuleByProjectID(projectID int64) ([]models.ImmutableRule, error) {
|
||||
return dao.QueryEnabledImmutableRuleByProjectID(projectID)
|
||||
}
|
||||
|
||||
func (drm *defaultRuleManager) DeleteImmutableRule(id int64) (int64, error) {
|
||||
return dao.DeleteImmutableRule(id)
|
||||
}
|
||||
|
||||
// NewDefaultRuleManager return a new instance of defaultRuleManager
|
||||
func NewDefaultRuleManager() RuleManager {
|
||||
return &defaultRuleManager{}
|
||||
}
|
Loading…
Reference in New Issue
Block a user