mirror of
https://github.com/goharbor/harbor.git
synced 2025-03-12 06:32:50 +01:00
Merge pull request #8592 from bitsf/tag_retention_conflict_rule
check rule conflict
This commit is contained in:
commit
d2fbb98a8d
@ -1,6 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -41,30 +42,6 @@ func (r *RetentionAPI) GetMetadatas() {
|
|||||||
data := `
|
data := `
|
||||||
{
|
{
|
||||||
"templates": [
|
"templates": [
|
||||||
{
|
|
||||||
"rule_template": "lastXDays",
|
|
||||||
"display_text": "the images from the last # days",
|
|
||||||
"action": "retain",
|
|
||||||
"params": [
|
|
||||||
{
|
|
||||||
"type": "int",
|
|
||||||
"unit": "DAYS",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rule_template": "latestActiveK",
|
|
||||||
"display_text": "the most recent active # images",
|
|
||||||
"action": "retain",
|
|
||||||
"params": [
|
|
||||||
{
|
|
||||||
"type": "int",
|
|
||||||
"unit": "COUNT",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"rule_template": "latestPushedK",
|
"rule_template": "latestPushedK",
|
||||||
"display_text": "the most recently pushed # images",
|
"display_text": "the most recently pushed # images",
|
||||||
@ -194,6 +171,10 @@ func (r *RetentionAPI) CreateRetention() {
|
|||||||
r.SendBadRequestError(err)
|
r.SendBadRequestError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err = r.checkRuleConflict(p); err != nil {
|
||||||
|
r.SendConflictError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
if !r.requireAccess(p, rbac.ActionCreate) {
|
if !r.requireAccess(p, rbac.ActionCreate) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -241,6 +222,10 @@ func (r *RetentionAPI) UpdateRetention() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.ID = id
|
p.ID = id
|
||||||
|
if err = r.checkRuleConflict(p); err != nil {
|
||||||
|
r.SendConflictError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
if !r.requireAccess(p, rbac.ActionUpdate) {
|
if !r.requireAccess(p, rbac.ActionUpdate) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -250,6 +235,21 @@ func (r *RetentionAPI) UpdateRetention() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RetentionAPI) checkRuleConflict(p *policy.Metadata) error {
|
||||||
|
temp := make(map[string]int)
|
||||||
|
for n, rule := range p.Rules {
|
||||||
|
tid := rule.ID
|
||||||
|
rule.ID = 0
|
||||||
|
bs, _ := json.Marshal(rule)
|
||||||
|
if old, exists := temp[string(bs)]; exists {
|
||||||
|
return fmt.Errorf("rule %d is conflict with rule %d", n, old)
|
||||||
|
}
|
||||||
|
temp[string(bs)] = tid
|
||||||
|
rule.ID = tid
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// TriggerRetentionExec Trigger Retention Execution
|
// TriggerRetentionExec Trigger Retention Execution
|
||||||
func (r *RetentionAPI) TriggerRetentionExec() {
|
func (r *RetentionAPI) TriggerRetentionExec() {
|
||||||
id, err := r.GetIDFromURL()
|
id, err := r.GetIDFromURL()
|
||||||
|
@ -143,6 +143,87 @@ func TestCreatePolicy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
code: http.StatusBadRequest,
|
code: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
request: &testingRequest{
|
||||||
|
method: http.MethodPost,
|
||||||
|
url: "/api/retentions",
|
||||||
|
bodyJSON: &policy.Metadata{
|
||||||
|
Algorithm: "or",
|
||||||
|
Rules: []rule.Metadata{
|
||||||
|
{
|
||||||
|
ID: 1,
|
||||||
|
Priority: 1,
|
||||||
|
Template: "recentXdays",
|
||||||
|
Parameters: rule.Parameters{
|
||||||
|
"num": 10,
|
||||||
|
},
|
||||||
|
TagSelectors: []*rule.Selector{
|
||||||
|
{
|
||||||
|
Kind: "label",
|
||||||
|
Decoration: "with",
|
||||||
|
Pattern: "latest",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Kind: "regularExpression",
|
||||||
|
Decoration: "matches",
|
||||||
|
Pattern: "release-[\\d\\.]+",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ScopeSelectors: map[string][]*rule.Selector{
|
||||||
|
"repository": {
|
||||||
|
{
|
||||||
|
Kind: "regularExpression",
|
||||||
|
Decoration: "matches",
|
||||||
|
Pattern: ".+",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 2,
|
||||||
|
Priority: 1,
|
||||||
|
Template: "recentXdays",
|
||||||
|
Parameters: rule.Parameters{
|
||||||
|
"num": 10,
|
||||||
|
},
|
||||||
|
TagSelectors: []*rule.Selector{
|
||||||
|
{
|
||||||
|
Kind: "label",
|
||||||
|
Decoration: "with",
|
||||||
|
Pattern: "latest",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Kind: "regularExpression",
|
||||||
|
Decoration: "matches",
|
||||||
|
Pattern: "release-[\\d\\.]+",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ScopeSelectors: map[string][]*rule.Selector{
|
||||||
|
"repository": {
|
||||||
|
{
|
||||||
|
Kind: "regularExpression",
|
||||||
|
Decoration: "matches",
|
||||||
|
Pattern: ".+",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trigger: &policy.Trigger{
|
||||||
|
Kind: "Schedule",
|
||||||
|
Settings: map[string]interface{}{
|
||||||
|
"cron": "* 22 11 * * *",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Scope: &policy.Scope{
|
||||||
|
Level: "project",
|
||||||
|
Reference: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
credential: sysAdmin,
|
||||||
|
},
|
||||||
|
code: http.StatusConflict,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
runCodeCheckingCases(t, cases...)
|
runCodeCheckingCases(t, cases...)
|
||||||
@ -267,6 +348,87 @@ func TestPolicy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
code: http.StatusOK,
|
code: http.StatusOK,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
request: &testingRequest{
|
||||||
|
method: http.MethodPut,
|
||||||
|
url: fmt.Sprintf("/api/retentions/%d", id),
|
||||||
|
bodyJSON: &policy.Metadata{
|
||||||
|
Algorithm: "or",
|
||||||
|
Rules: []rule.Metadata{
|
||||||
|
{
|
||||||
|
ID: 1,
|
||||||
|
Priority: 1,
|
||||||
|
Template: "recentXdays",
|
||||||
|
Parameters: rule.Parameters{
|
||||||
|
"num": 10,
|
||||||
|
},
|
||||||
|
TagSelectors: []*rule.Selector{
|
||||||
|
{
|
||||||
|
Kind: "label",
|
||||||
|
Decoration: "with",
|
||||||
|
Pattern: "latest",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Kind: "regularExpression",
|
||||||
|
Decoration: "matches",
|
||||||
|
Pattern: "release-[\\d\\.]+",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ScopeSelectors: map[string][]*rule.Selector{
|
||||||
|
"repository": {
|
||||||
|
{
|
||||||
|
Kind: "regularExpression",
|
||||||
|
Decoration: "matches",
|
||||||
|
Pattern: "b.+",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 2,
|
||||||
|
Priority: 1,
|
||||||
|
Template: "recentXdays",
|
||||||
|
Parameters: rule.Parameters{
|
||||||
|
"num": 10,
|
||||||
|
},
|
||||||
|
TagSelectors: []*rule.Selector{
|
||||||
|
{
|
||||||
|
Kind: "label",
|
||||||
|
Decoration: "with",
|
||||||
|
Pattern: "latest",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Kind: "regularExpression",
|
||||||
|
Decoration: "matches",
|
||||||
|
Pattern: "release-[\\d\\.]+",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ScopeSelectors: map[string][]*rule.Selector{
|
||||||
|
"repository": {
|
||||||
|
{
|
||||||
|
Kind: "regularExpression",
|
||||||
|
Decoration: "matches",
|
||||||
|
Pattern: "b.+",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trigger: &policy.Trigger{
|
||||||
|
Kind: "Schedule",
|
||||||
|
Settings: map[string]interface{}{
|
||||||
|
"cron": "* 22 11 * * *",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Scope: &policy.Scope{
|
||||||
|
Level: "project",
|
||||||
|
Reference: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
credential: sysAdmin,
|
||||||
|
},
|
||||||
|
code: http.StatusConflict,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
request: &testingRequest{
|
request: &testingRequest{
|
||||||
method: http.MethodPost,
|
method: http.MethodPost,
|
||||||
|
Loading…
Reference in New Issue
Block a user