retention api

Change-Id: I70f2c34d6bb96ecf4cb5359e2b1ab2dbb99fdbf9
This commit is contained in:
Ziming Zhang 2019-07-09 15:29:19 +08:00
parent 44ad142d86
commit 2c2025102b
9 changed files with 394 additions and 10 deletions

View File

@ -24,6 +24,7 @@ import (
"github.com/goharbor/harbor/src/core/service/notifications/jobs"
"github.com/goharbor/harbor/src/core/service/notifications/registry"
"github.com/goharbor/harbor/src/core/service/token"
retentionCtl "github.com/goharbor/harbor/src/pkg/retention/controllers"
"github.com/astaxie/beego"
)
@ -139,6 +140,16 @@ func initRouters() {
beego.Router("/api/registries/:id/info", &api.RegistryAPI{}, "get:GetInfo")
beego.Router("/api/registries/:id/namespace", &api.RegistryAPI{}, "get:GetNamespace")
beego.Router("/api/retentions/:id", &retentionCtl.RetentionAPI{}, "get:GetRetention")
beego.Router("/api/retentions", &retentionCtl.RetentionAPI{}, "post:CreateRetention")
beego.Router("/api/retentions/:id", &retentionCtl.RetentionAPI{}, "put:UpdateRetention")
beego.Router("/api/retentions/:id", &retentionCtl.RetentionAPI{}, "delete:DeleteRetention")
beego.Router("/api/retentions/:id/executions", &retentionCtl.RetentionAPI{}, "delete:TriggerRetentionExec")
beego.Router("/api/retentions/:id/executions/:eid", &retentionCtl.RetentionAPI{}, "delete:OperateRetentionExec")
beego.Router("/api/retentions/:id/executions/:eid", &retentionCtl.RetentionAPI{}, "get:GetRetentionExec")
beego.Router("/api/retentions/:id/executions", &retentionCtl.RetentionAPI{}, "get:ListRetentionExec")
beego.Router("/api/retentions/:id/executions/:eid/histories", &retentionCtl.RetentionAPI{}, "get:ListRetentionExecHistory")
beego.Router("/v2/*", &controllers.RegistryProxy{}, "*:Handle")
// APIs for chart repository

View File

@ -0,0 +1,74 @@
package controllers
import (
"github.com/goharbor/harbor/src/core/api"
"github.com/goharbor/harbor/src/pkg/retention"
)
type RetentionAPI struct {
api.BaseController
manager retention.Manager
}
// Prepare validates the user
func (t *RetentionAPI) Prepare() {
t.BaseController.Prepare()
t.manager = retention.NewManager()
}
func (r *RetentionAPI) GetRetention() {
id, err := r.GetIDFromURL()
if err != nil {
r.SendBadRequestError(err)
return
}
p, err := r.manager.GetPolicy(id)
if err != nil {
r.SendBadRequestError(err)
return
}
r.Data["json"] = p
r.ServeJSON()
}
func (r *RetentionAPI) CreateRetention() {
r.manager.CreatePolicy(nil)
}
func (r *RetentionAPI) UpdateRetention() {
_, err := r.GetIDFromURL()
if err != nil {
r.SendBadRequestError(err)
return
}
r.manager.UpdatePolicy(nil)
}
func (r *RetentionAPI) DeleteRetention() {
id, err := r.GetIDFromURL()
if err != nil {
r.SendBadRequestError(err)
return
}
r.manager.DeletePolicy(id)
}
func (r *RetentionAPI) TriggerRetentionExec() {
//r.manager.CreateExecution(nil)
}
func (r *RetentionAPI) OperateRetentionExec() {
r.manager.UpdateExecution(nil)
}
func (r *RetentionAPI) GetRetentionExec() {
//r.manager.GetExecution(eid)
}
func (r *RetentionAPI) ListRetentionExec() {
r.manager.ListExecutions(nil)
}
func (r *RetentionAPI) ListRetentionExecHistory() {
//r.manager.ListHistories(eid, nil)
}

View File

@ -0,0 +1,64 @@
package models
import (
"github.com/astaxie/beego/orm"
"time"
)
func init() {
orm.RegisterModel(
new (RetentionPolicy),
new (RetentionExecution),
new (RetentionTask),
new (RetentionScheduleJob),
)
}
type RetentionPolicy struct {
ID int64 `orm:"pk;auto;column(id)" json:"id"`
// 'system', 'project' and 'repository'
ScopeLevel string
ScopeReference int64
TriggerKind string
// json format, include algorithm, rules, exclusions
Data string
CreateTime time.Time
UpdateTime time.Time
}
type RetentionExecution struct {
ID int64 `orm:"pk;auto;column(id)" json:"id"`
PolicyID int64
Status string
StatusText string
Dry bool
// manual, scheduled
Trigger string
Total int
Succeed int
Failed int
InProgress int
Stopped int
StartTime time.Time
EndTime time.Time
}
type RetentionTask struct {
ID int64
ExecutionID int64
// image, chart
ResourceType string
Resource string
Status string
StartTime time.Time
EndTime time.Time
}
type RetentionScheduleJob struct {
ID int64
Status string
PolicyID int64
JobID int64
CreateTime time.Time
UpdateTime time.Time
}

View File

@ -0,0 +1,37 @@
package dao
import (
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/pkg/retention/dao/models"
)
func CreatePolicy(p *models.RetentionPolicy) (int64, error) {
o := dao.GetOrmer()
return o.Insert(p)
}
func UpdatePolicy(p *models.RetentionPolicy) error {
o := dao.GetOrmer()
_, err := o.Update(p)
return err
}
func DeletePolicy(id int64) error {
o := dao.GetOrmer()
_, err := o.Delete(&models.RetentionPolicy{
ID: id,
})
return err
}
func GetPolicy(id int64) (*models.RetentionPolicy, error) {
o := dao.GetOrmer()
p := &models.RetentionPolicy{
ID: id,
}
if err := o.Read(p); err != nil {
return nil, err
} else {
return p, nil
}
}

View File

@ -0,0 +1,96 @@
package dao
import (
"encoding/json"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/pkg/retention/policy"
"github.com/goharbor/harbor/src/pkg/retention/policy/rule"
"github.com/stretchr/testify/assert"
"os"
"strings"
"testing"
"time"
"github.com/goharbor/harbor/src/pkg/retention/dao/models"
)
func TestMain(m *testing.M) {
dao.PrepareTestForPostgresSQL()
os.Exit(m.Run())
}
func TestPolicy(t *testing.T) {
p := &policy.Metadata{
ID: 1,
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: []*rule.Selector{
{
Kind: "regularExpression",
Decoration: "matches",
Pattern: ".+",
},
},
},
},
Trigger: &policy.Trigger{
Kind: "Schedule",
Settings: map[string]interface{}{
"cron": "* 22 11 * * *",
},
},
Scope: &policy.Scope{
Level: "project",
Reference: 1,
},
}
p1 := &models.RetentionPolicy{
ScopeLevel: p.Scope.Level,
TriggerKind: p.Trigger.Kind,
CreateTime: time.Now(),
UpdateTime: time.Now(),
}
data, _ := json.Marshal(p)
p1.Data = string(data)
id, err := CreatePolicy(p1)
assert.Nil(t, err)
assert.True(t, id > 0)
p1, err = GetPolicy(id)
assert.Nil(t, err)
assert.EqualValues(t, "project", p1.ScopeLevel)
p1.ScopeLevel = "test"
err = UpdatePolicy(p1)
assert.Nil(t, err)
p1, err = GetPolicy(id)
assert.Nil(t, err)
assert.EqualValues(t, "test", p1.ScopeLevel)
err = DeletePolicy(id)
assert.Nil(t, err)
p1, err = GetPolicy(id)
assert.NotNil(t, err)
assert.True(t, strings.Contains(err.Error(), "no row found"))
}

View File

@ -15,32 +15,134 @@
package retention
import (
"encoding/json"
"github.com/goharbor/harbor/src/pkg/retention/dao"
"github.com/goharbor/harbor/src/pkg/retention/dao/models"
"github.com/goharbor/harbor/src/pkg/retention/policy"
"github.com/goharbor/harbor/src/pkg/retention/q"
"time"
)
// Manager defines operations of managing policy
type Manager interface {
// Create new policy and return uuid
CreatePolicy(p *policy.Metadata) (string, error)
CreatePolicy(p *policy.Metadata) (int64, error)
// Update the existing policy
// Full update
UpdatePolicy(p *policy.Metadata) error
// Delete the specified policy
// No actual use so far
DeletePolicy(ID string) error
DeletePolicy(ID int64) error
// Get the specified policy
GetPolicy(ID string) (*policy.Metadata, error)
GetPolicy(ID int64) (*policy.Metadata, error)
// Create a new retention execution
CreateExecution(execution *Execution) (string, error)
// Update the specified execution
UpdateExecution(execution *Execution) error
// Get the specified execution
GetExecution(eid string) (*Execution, error)
GetExecution(eid int64) (*Execution, error)
// List execution histories
ListExecutions(query *q.Query) ([]*Execution, error)
// Add new history
AppendHistory(history *History) error
// List all the histories marked by the specified execution
ListHistories(executionID string, query *q.Query) ([]*History, error)
ListHistories(executionID int64, query *q.Query) ([]*History, error)
}
type DefaultManager struct {
}
func (d *DefaultManager) CreatePolicy(p *policy.Metadata) (int64, error) {
var p1 *models.RetentionPolicy
p1.ScopeLevel = p.Scope.Level
p1.TriggerKind = p.Trigger.Kind
data, _ := json.Marshal(p)
p1.Data = string(data)
p1.CreateTime = time.Now()
p1.UpdateTime = p1.CreateTime
return dao.CreatePolicy(p1)
}
func (d *DefaultManager) UpdatePolicy(p *policy.Metadata) error {
var p1 *models.RetentionPolicy
p1.ID = p.ID
p1.ScopeLevel = p.Scope.Level
p1.TriggerKind = p.Trigger.Kind
data, _ := json.Marshal(p)
p1.Data = string(data)
p1.UpdateTime = time.Now()
return dao.UpdatePolicy(p1)
}
func (d *DefaultManager) DeletePolicy(id int64) error {
return dao.DeletePolicy(id)
}
func (d *DefaultManager) GetPolicy(id int64) (*policy.Metadata, error) {
if p1,err:=dao.GetPolicy(id);err!=nil{
return nil,err
}else{
var p *policy.Metadata
if err=json.Unmarshal([]byte(p1.Data), p);err!=nil{
return nil,err
}else{
return p,nil
}
}
}
func (d *DefaultManager) CreateExecution(execution *Execution) (string, error) {
panic("implement me")
}
func (d *DefaultManager) UpdateExecution(execution *Execution) error {
panic("implement me")
}
func (d *DefaultManager) ListExecutions(query *q.Query) ([]*Execution, error) {
return []*Execution{
{
ID: 1,
PolicyID: 1,
StartTime: time.Now().Add(-time.Minute),
EndTime: time.Now(),
Status: "Success",
},
{
ID: 2,
PolicyID: 1,
StartTime: time.Now().Add(-time.Minute),
EndTime: time.Now(),
Status: "Failed",
},
{
ID: 3,
PolicyID: 1,
StartTime: time.Now().Add(-time.Minute),
EndTime: time.Now(),
Status: "Running",
},
}, nil
}
func (d *DefaultManager) GetExecution(eid int64) (*Execution, error) {
return &Execution{
ID: 1,
PolicyID: 1,
StartTime: time.Now().Add(-time.Minute),
EndTime: time.Now(),
Status: "Success",
}, nil
}
func (d *DefaultManager) ListHistories(executionID int64, query *q.Query) ([]*History, error) {
panic("implement me")
}
func (d *DefaultManager) AppendHistory(history *History) error {
panic("implement me")
}
func NewManager() Manager {
return &DefaultManager{}
}

View File

@ -18,8 +18,8 @@ import "time"
// Execution of retention
type Execution struct {
ID string `json:"id"`
PolicyID string `json:"policy_id"`
ID int `json:"id"`
PolicyID int `json:"policy_id"`
StartTime time.Time `json:"start_time"`
EndTime time.Time `json:"end_time"`
Status string `json:"status"`
@ -37,7 +37,7 @@ type TaskSubmitResult struct {
type History struct {
ExecutionID string `json:"execution_id"`
Rule struct {
ID string `json:"id"`
ID int `json:"id"`
DisplayText string `json:"display_text"`
} `json:"rule_id"`
// full path: :ns/:repo:tag

View File

@ -26,7 +26,7 @@ const (
// Metadata of policy
type Metadata struct {
// UUID of the policy
ID string `json:"id"`
ID int64 `json:"-"`
// Algorithm applied to the rules
// "OR" / "AND"

View File

@ -17,7 +17,7 @@ package rule
// Metadata of the retention rule
type Metadata struct {
// UUID of rule
ID string `json:"id"`
ID int `json:"id"`
// Priority of rule when doing calculating
Priority int `json:"priority"`