Unverified Commit a2938c5d authored by stonezdj(Daojun Zhang)'s avatar stonezdj(Daojun Zhang) Committed by GitHub
Browse files

Merge pull request #9274 from wy65701436/immu-refatctor

refactor immutable dao code to align the new structure under pkg
parents 0a85acac 8317100c
package dao
import (
"testing"
"github.com/goharbor/harbor/src/common/models"
)
func TestCreateImmutableRule(t *testing.T) {
ir := &models.ImmutableRule{TagFilter: "**", ProjectID: 1}
id, err := CreateImmutableRule(ir)
if err != nil {
t.Errorf("error: %+v", err)
}
if id <= 0 {
t.Error("Can not create immutable tag rule")
}
_, err = DeleteImmutableRule(id)
if err != nil {
t.Errorf("error: %+v", err)
}
}
func TestUpdateImmutableRule(t *testing.T) {
ir := &models.ImmutableRule{TagFilter: "**", ProjectID: 1}
id, err := CreateImmutableRule(ir)
if err != nil {
t.Errorf("error: %+v", err)
}
if id <= 0 {
t.Error("Can not create immutable tag rule")
}
updatedIR := &models.ImmutableRule{ID: id, TagFilter: "1.2.0", ProjectID: 1}
updatedCnt, err := UpdateImmutableRule(1, updatedIR)
if err != nil {
t.Errorf("error: %+v", err)
}
if updatedCnt <= 0 {
t.Error("Failed to update immutable id")
}
newIr, err := GetImmutableRule(id)
if err != nil {
t.Errorf("error: %+v", err)
}
if newIr.TagFilter != "1.2.0" {
t.Error("Failed to update immutable tag")
}
defer DeleteImmutableRule(id)
}
func TestEnableImmutableRule(t *testing.T) {
ir := &models.ImmutableRule{TagFilter: "**", ProjectID: 1}
id, err := CreateImmutableRule(ir)
if err != nil {
t.Errorf("error: %+v", err)
}
if id <= 0 {
t.Error("Can not create immutable tag rule")
}
ToggleImmutableRule(id, false)
newIr, err := GetImmutableRule(id)
if err != nil {
t.Errorf("error: %+v", err)
}
if newIr.Enabled != false {
t.Error("Failed to disable the immutable rule")
}
defer DeleteImmutableRule(id)
}
func TestGetImmutableRuleByProject(t *testing.T) {
irs := []*models.ImmutableRule{
{TagFilter: "version1", ProjectID: 99},
{TagFilter: "version2", ProjectID: 99},
{TagFilter: "version3", ProjectID: 99},
{TagFilter: "version4", ProjectID: 99},
}
for _, ir := range irs {
CreateImmutableRule(ir)
}
qrs, err := QueryImmutableRuleByProjectID(99)
if err != nil {
t.Errorf("error: %+v", err)
}
if len(qrs) != 4 {
t.Error("Failed to query 4 rows!")
}
defer ExecuteBatchSQL([]string{"delete from immutable_tag_rule where project_id = 99 "})
}
func TestGetEnabledImmutableRuleByProject(t *testing.T) {
irs := []*models.ImmutableRule{
{TagFilter: "version1", ProjectID: 99},
{TagFilter: "version2", ProjectID: 99},
{TagFilter: "version3", ProjectID: 99},
{TagFilter: "version4", ProjectID: 99},
}
for i, ir := range irs {
id, _ := CreateImmutableRule(ir)
if i == 1 {
ToggleImmutableRule(id, false)
}
}
qrs, err := QueryEnabledImmutableRuleByProjectID(99)
if err != nil {
t.Errorf("error: %+v", err)
}
if len(qrs) != 3 {
t.Errorf("Failed to query 3 rows!, got %v", len(qrs))
}
defer ExecuteBatchSQL([]string{"delete from immutable_tag_rule where project_id = 99 "})
}
......@@ -46,6 +46,5 @@ func init() {
new(CVEWhitelist),
new(Quota),
new(QuotaUsage),
new(ImmutableRule),
)
}
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"
"github.com/goharbor/harbor/src/pkg/immutabletag/model"
)
// ImmutableTagRuleAPI ...
type ImmutableTagRuleAPI struct {
BaseController
manager immutabletag.RuleManager
ctr immutabletag.APIController
projectID int64
ID int64
}
......@@ -49,7 +46,7 @@ func (itr *ImmutableTagRuleAPI) Prepare() {
itr.ID = ruleID
}
itr.manager = immutabletag.NewDefaultRuleManager()
itr.ctr = immutabletag.NewAPIController(immutabletag.NewDefaultRuleManager())
if strings.EqualFold(itr.Ctx.Request.Method, "get") {
if !itr.requireAccess(rbac.ActionList) {
......@@ -77,7 +74,7 @@ func (itr *ImmutableTagRuleAPI) requireAccess(action rbac.Action) bool {
// List list all immutable tag rules of current project
func (itr *ImmutableTagRuleAPI) List() {
rules, err := itr.manager.QueryImmutableRuleByProjectID(itr.projectID)
rules, err := itr.ctr.ListImmutableRules(itr.projectID)
if err != nil {
itr.SendInternalServerError(err)
return
......@@ -87,19 +84,14 @@ func (itr *ImmutableTagRuleAPI) List() {
// 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))
ir := &model.Metadata{}
isValid, err := itr.DecodeJSONReqAndValidate(ir)
if !isValid {
itr.SendBadRequestError(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)
id, err := itr.ctr.CreateImmutableRule(ir)
if err != nil {
itr.SendInternalServerError(err)
return
......@@ -114,7 +106,7 @@ func (itr *ImmutableTagRuleAPI) Delete() {
itr.SendBadRequestError(fmt.Errorf("invalid immutable rule id %d", itr.ID))
return
}
_, err := itr.manager.DeleteImmutableRule(itr.ID)
err := itr.ctr.DeleteImmutableRule(itr.ID)
if err != nil {
itr.SendInternalServerError(err)
return
......@@ -123,9 +115,9 @@ func (itr *ImmutableTagRuleAPI) Delete() {
// Put update an immutable tag rule
func (itr *ImmutableTagRuleAPI) Put() {
ir := &models.ImmutableRule{}
ir := &model.Metadata{}
if err := itr.DecodeJSONReq(ir); err != nil {
itr.SendInternalServerError(err)
itr.SendBadRequestError(err)
return
}
ir.ID = itr.ID
......@@ -135,32 +127,9 @@ func (itr *ImmutableTagRuleAPI) Put() {
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
if err := itr.ctr.UpdateImmutableRule(itr.projectID, ir); err != nil {
itr.SendInternalServerError(err)
return
}
return true
}
......@@ -7,24 +7,34 @@ import (
"net/http/httptest"
"testing"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/pkg/immutabletag"
"github.com/goharbor/harbor/src/pkg/immutabletag/model"
)
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":"**"}]}
}`
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(&models.ImmutableRule{ProjectID: 1, TagFilter: tagFilter})
id, err := mgr.CreateImmutableRule(metadata)
if err != nil {
t.Error(err)
}
......@@ -46,7 +56,7 @@ func TestImmutableTagRuleAPI_List(t *testing.T) {
credential: admin,
},
postFunc: func(responseRecorder *httptest.ResponseRecorder) error {
var rules []models.ImmutableRule
var rules []model.Metadata
err := json.Unmarshal([]byte(responseRecorder.Body.String()), &rules)
if err != nil {
return err
......@@ -54,7 +64,7 @@ func TestImmutableTagRuleAPI_List(t *testing.T) {
if len(rules) <= 0 {
return fmt.Errorf("no rules found")
}
if rules[0].TagFilter != tagFilter {
if rules[0].TagSelectors[0].Kind != "doublestar" {
return fmt.Errorf("rule is not expected. actual: %v", responseRecorder.Body.String())
}
return nil
......@@ -86,43 +96,68 @@ func TestImmutableTagRuleAPI_List(t *testing.T) {
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}
// body := `{
// "id":0,
// "projectID":1,
// "priority":0,
// "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: body,
bodyJSON: metadata,
},
code: http.StatusUnauthorized,
},
// 200
// 201
{
request: &testingRequest{
method: http.MethodPost,
url: "/api/projects/1/immutabletagrules",
credential: admin,
bodyJSON: body,
bodyJSON: metadata,
},
code: http.StatusCreated,
},
// 200
// 201
{
request: &testingRequest{
method: http.MethodPost,
url: "/api/projects/1/immutabletagrules",
credential: projAdmin,
bodyJSON: body,
bodyJSON: metadata,
},
code: http.StatusCreated,
},
......@@ -132,7 +167,7 @@ func TestImmutableTagRuleAPI_Post(t *testing.T) {
method: http.MethodPost,
url: "/api/projects/1/immutabletagrules",
credential: projGuest,
bodyJSON: body,
bodyJSON: metadata,
},
code: http.StatusForbidden,
},
......@@ -142,40 +177,63 @@ func TestImmutableTagRuleAPI_Post(t *testing.T) {
}
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"}]}
}`
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(&models.ImmutableRule{ProjectID: 1, TagFilter: tagFilter})
id, err := mgr.CreateImmutableRule(metadata)
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,
bodyJSON: metadata2,
},
code: http.StatusUnauthorized,
},
......@@ -185,7 +243,7 @@ func TestImmutableTagRuleAPI_Put(t *testing.T) {
method: http.MethodPut,
url: url,
credential: admin,
bodyJSON: body,
bodyJSON: metadata2,
},
code: http.StatusOK,
},
......@@ -195,7 +253,7 @@ func TestImmutableTagRuleAPI_Put(t *testing.T) {
method: http.MethodPut,
url: url,
credential: projAdmin,
bodyJSON: body,
bodyJSON: metadata2,
},
code: http.StatusOK,
},
......@@ -205,7 +263,7 @@ func TestImmutableTagRuleAPI_Put(t *testing.T) {
method: http.MethodPut,
url: url,
credential: projGuest,
bodyJSON: body,
bodyJSON: metadata2,
},
code: http.StatusForbidden,
},
......@@ -214,17 +272,29 @@ func TestImmutableTagRuleAPI_Put(t *testing.T) {
}
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":"**"}]}
}`
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(&models.ImmutableRule{ProjectID: 1, TagFilter: tagFilter})
id, err := mgr.CreateImmutableRule(metadata)
if err != nil {
t.Error(err)
}
......
package immutabletag
import (
"github.com/goharbor/harbor/src/pkg/immutabletag/model"
)
// APIController to handle the requests related with immutabletag
type APIController 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)
}