mirror of
https://github.com/goharbor/harbor.git
synced 2025-03-11 14:09:55 +01:00
map retention with policy (#8313)
Signed-off-by: Ziming Zhang <zziming@vmware.com> Implement the API and controller of tag retention - API handler - retention controller - dao
This commit is contained in:
parent
9437b489ac
commit
43c2af9857
@ -22,19 +22,18 @@ create table retention_policy
|
||||
|
||||
create table retention_execution
|
||||
(
|
||||
id integer PRIMARY KEY NOT NULL,
|
||||
id serial PRIMARY KEY NOT NULL,
|
||||
policy_id integer,
|
||||
status varchar(20),
|
||||
status_text text,
|
||||
dry boolean,
|
||||
dry_run boolean,
|
||||
trigger varchar(20),
|
||||
total integer,
|
||||
succeed integer,
|
||||
failed integer,
|
||||
in_progress integer,
|
||||
stopped integer,
|
||||
start_time time,
|
||||
end_time time
|
||||
start_time timestamp,
|
||||
end_time timestamp
|
||||
);
|
||||
|
||||
create table retention_task
|
||||
|
@ -131,21 +131,6 @@ func (b *BaseAPI) GetIDFromURL() (int64, error) {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// GetSpecialIDFromURL checks the ID with special name in request URL
|
||||
func (b *BaseAPI) GetSpecialIDFromURL(name string) (int64, error) {
|
||||
idStr := b.Ctx.Input.Param(":" + name)
|
||||
if len(idStr) == 0 {
|
||||
return 0, fmt.Errorf("invalid %s in URL", name)
|
||||
}
|
||||
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil || id <= 0 {
|
||||
return 0, errors.New("invalid ID in URL")
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// SetPaginationHeader set"Link" and "X-Total-Count" header for pagination request
|
||||
func (b *BaseAPI) SetPaginationHeader(total, page, pageSize int64) {
|
||||
b.Ctx.ResponseWriter.Header().Set("X-Total-Count", strconv.FormatInt(total, 10))
|
||||
|
@ -27,6 +27,8 @@ const (
|
||||
ActionUpdate = Action("update")
|
||||
ActionDelete = Action("delete")
|
||||
ActionList = Action("list")
|
||||
|
||||
ActionOperate = Action("operate")
|
||||
)
|
||||
|
||||
// const resource variables
|
||||
@ -46,6 +48,7 @@ const (
|
||||
ResourceReplicationExecution = Resource("replication-execution")
|
||||
ResourceReplicationTask = Resource("replication-task")
|
||||
ResourceRepository = Resource("repository")
|
||||
ResourceTagRetention = Resource("tag-retention")
|
||||
ResourceRepositoryLabel = Resource("repository-label")
|
||||
ResourceRepositoryTag = Resource("repository-tag")
|
||||
ResourceRepositoryTagLabel = Resource("repository-tag-label")
|
||||
|
@ -88,6 +88,13 @@ var (
|
||||
{Resource: rbac.ResourceReplicationTask, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceReplicationTask, Action: rbac.ActionDelete},
|
||||
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionOperate},
|
||||
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceLabel, Action: rbac.ActionUpdate},
|
||||
|
@ -61,6 +61,13 @@ var (
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionPull},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionPush},
|
||||
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionOperate},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionList},
|
||||
@ -133,6 +140,13 @@ var (
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionPush},
|
||||
{Resource: rbac.ResourceRepository, Action: rbac.ActionPull},
|
||||
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionRead},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionUpdate},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionList},
|
||||
{Resource: rbac.ResourceTagRetention, Action: rbac.ActionOperate},
|
||||
|
||||
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionCreate},
|
||||
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionDelete},
|
||||
{Resource: rbac.ResourceRepositoryLabel, Action: rbac.ActionList},
|
||||
|
@ -16,6 +16,8 @@ package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/goharbor/harbor/src/pkg/retention"
|
||||
"github.com/goharbor/harbor/src/pkg/scheduler"
|
||||
"net/http"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
@ -39,8 +41,12 @@ const (
|
||||
|
||||
// the managers/controllers used globally
|
||||
var (
|
||||
projectMgr project.Manager
|
||||
repositoryMgr repository.Manager
|
||||
projectMgr project.Manager
|
||||
repositoryMgr repository.Manager
|
||||
retentionScheduler scheduler.Scheduler
|
||||
retentionMgr retention.Manager
|
||||
retentionLauncher retention.Launcher
|
||||
retentionController retention.APIController
|
||||
)
|
||||
|
||||
// BaseController ...
|
||||
@ -108,7 +114,24 @@ func Init() error {
|
||||
// init repository manager
|
||||
initRepositoryManager()
|
||||
|
||||
return nil
|
||||
initRetentionScheduler()
|
||||
|
||||
retentionMgr = retention.NewManager()
|
||||
|
||||
retentionLauncher = retention.NewLauncher(projectMgr, repositoryMgr, retentionMgr)
|
||||
|
||||
retentionController = retention.NewAPIController(projectMgr, repositoryMgr, retentionScheduler, retentionLauncher)
|
||||
|
||||
callbackFun := func(p interface{}) error {
|
||||
r, ok := p.(retention.TriggerParam)
|
||||
if ok {
|
||||
return retentionController.TriggerRetentionExec(r.PolicyID, r.Trigger, false)
|
||||
}
|
||||
return errors.New("bad retention callback param")
|
||||
}
|
||||
err := scheduler.Register(retention.RetentionSchedulerCallback, callbackFun)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func initChartController() error {
|
||||
@ -133,3 +156,7 @@ func initProjectManager() {
|
||||
func initRepositoryManager() {
|
||||
repositoryMgr = repository.New(projectMgr, chartController)
|
||||
}
|
||||
|
||||
func initRetentionScheduler() {
|
||||
retentionScheduler = scheduler.GlobalScheduler
|
||||
}
|
||||
|
392
src/core/api/retention.go
Normal file
392
src/core/api/retention.go
Normal file
@ -0,0 +1,392 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/common/rbac"
|
||||
"github.com/goharbor/harbor/src/core/filter"
|
||||
"github.com/goharbor/harbor/src/core/promgr"
|
||||
"github.com/goharbor/harbor/src/pkg/retention"
|
||||
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
||||
"github.com/goharbor/harbor/src/pkg/retention/q"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// RetentionAPI ...
|
||||
type RetentionAPI struct {
|
||||
BaseController
|
||||
pm promgr.ProjectManager
|
||||
}
|
||||
|
||||
// Prepare validates the user
|
||||
func (r *RetentionAPI) Prepare() {
|
||||
r.BaseController.Prepare()
|
||||
if !r.SecurityCtx.IsAuthenticated() {
|
||||
r.SendUnAuthorizedError(errors.New("UnAuthorized"))
|
||||
return
|
||||
}
|
||||
pm, e := filter.GetProjectManager(r.Ctx.Request)
|
||||
if e != nil {
|
||||
r.SendInternalServerError(e)
|
||||
return
|
||||
}
|
||||
r.pm = pm
|
||||
|
||||
}
|
||||
|
||||
// GetMetadatas Get Metadatas
|
||||
func (r *RetentionAPI) GetMetadatas() {
|
||||
data := `
|
||||
{
|
||||
"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": "latestK",
|
||||
"display_text": "the most recently pushed # images",
|
||||
"action": "retain",
|
||||
"params": [
|
||||
{
|
||||
"type": "int",
|
||||
"unit": "COUNT",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rule_template": "latestPulledK",
|
||||
"display_text": "the most recently pulled # images",
|
||||
"action": "retain",
|
||||
"params": [
|
||||
{
|
||||
"type": "int",
|
||||
"unit": "COUNT",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rule_template": "always",
|
||||
"display_text": "always",
|
||||
"action": "retain",
|
||||
"params": [
|
||||
{
|
||||
"type": "int",
|
||||
"unit": "COUNT",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"scope_selectors": [
|
||||
{
|
||||
"display_text": "Repositories",
|
||||
"kind": "doublestar",
|
||||
"decorations": [
|
||||
"repoMatches",
|
||||
"repoExcludes"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tag_selectors": [
|
||||
{
|
||||
"display_text": "Labels",
|
||||
"kind": "label",
|
||||
"decorations": [
|
||||
"withLabels",
|
||||
"withoutLabels"
|
||||
]
|
||||
},
|
||||
{
|
||||
"display_text": "Tags",
|
||||
"kind": "doublestar",
|
||||
"decorations": [
|
||||
"matches",
|
||||
"excludes"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
r.WriteJSONData(data)
|
||||
}
|
||||
|
||||
// GetRetention Get Retention
|
||||
func (r *RetentionAPI) GetRetention() {
|
||||
id, err := r.GetIDFromURL()
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
p, err := retentionController.GetRetention(id)
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
if !r.requireAccess(p, rbac.ActionRead) {
|
||||
return
|
||||
}
|
||||
r.WriteJSONData(p)
|
||||
}
|
||||
|
||||
// CreateRetention Create Retention
|
||||
func (r *RetentionAPI) CreateRetention() {
|
||||
p := &policy.Metadata{}
|
||||
isValid, err := r.DecodeJSONReqAndValidate(p)
|
||||
if !isValid {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
if !r.requireAccess(p, rbac.ActionCreate) {
|
||||
return
|
||||
}
|
||||
switch p.Scope.Level {
|
||||
case policy.ScopeLevelProject:
|
||||
if p.Scope.Reference <= 0 {
|
||||
r.SendBadRequestError(fmt.Errorf("Invalid Project id %d", p.Scope.Reference))
|
||||
return
|
||||
}
|
||||
|
||||
proj, err := r.pm.Get(p.Scope.Reference)
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
}
|
||||
if proj == nil {
|
||||
r.SendBadRequestError(fmt.Errorf("Invalid Project id %d", p.Scope.Reference))
|
||||
}
|
||||
default:
|
||||
r.SendBadRequestError(fmt.Errorf("scope %s is not support", p.Scope.Level))
|
||||
return
|
||||
}
|
||||
if err = retentionController.CreateRetention(p); err != nil {
|
||||
r.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
if err := r.pm.GetMetadataManager().Add(p.Scope.Reference,
|
||||
map[string]string{"retention_id": strconv.FormatInt(p.Scope.Reference, 10)}); err != nil {
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateRetention Update Retention
|
||||
func (r *RetentionAPI) UpdateRetention() {
|
||||
id, err := r.GetIDFromURL()
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
p := &policy.Metadata{}
|
||||
isValid, err := r.DecodeJSONReqAndValidate(p)
|
||||
if !isValid {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
p.ID = id
|
||||
if !r.requireAccess(p, rbac.ActionUpdate) {
|
||||
return
|
||||
}
|
||||
if err = retentionController.UpdateRetention(p); err != nil {
|
||||
r.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TriggerRetentionExec Trigger Retention Execution
|
||||
func (r *RetentionAPI) TriggerRetentionExec() {
|
||||
id, err := r.GetIDFromURL()
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
d := &struct {
|
||||
DryRun bool `json:"dry_run"`
|
||||
}{
|
||||
DryRun: false,
|
||||
}
|
||||
isValid, err := r.DecodeJSONReqAndValidate(d)
|
||||
if !isValid {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
p, err := retentionController.GetRetention(id)
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
if !r.requireAccess(p, rbac.ActionUpdate) {
|
||||
return
|
||||
}
|
||||
if err = retentionController.TriggerRetentionExec(id, retention.ExecutionTriggerManual, d.DryRun); err != nil {
|
||||
r.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// OperateRetentionExec Operate Retention Execution
|
||||
func (r *RetentionAPI) OperateRetentionExec() {
|
||||
id, err := r.GetIDFromURL()
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
eid, err := r.GetInt64FromPath(":eid")
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
a := &struct {
|
||||
Action string `json:"action" valid:"Required"`
|
||||
}{}
|
||||
isValid, err := r.DecodeJSONReqAndValidate(a)
|
||||
if !isValid {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
p, err := retentionController.GetRetention(id)
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
if !r.requireAccess(p, rbac.ActionUpdate) {
|
||||
return
|
||||
}
|
||||
if err = retentionController.OperateRetentionExec(eid, a.Action); err != nil {
|
||||
r.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ListRetentionExecs List Retention Execution
|
||||
func (r *RetentionAPI) ListRetentionExecs() {
|
||||
id, err := r.GetIDFromURL()
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
page, size, err := r.GetPaginationParams()
|
||||
if err != nil {
|
||||
r.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
query := &q.Query{
|
||||
PageNumber: page,
|
||||
PageSize: size,
|
||||
}
|
||||
p, err := retentionController.GetRetention(id)
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
if !r.requireAccess(p, rbac.ActionList) {
|
||||
return
|
||||
}
|
||||
execs, err := retentionController.ListRetentionExecs(id, query)
|
||||
if err != nil {
|
||||
r.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
r.WriteJSONData(execs)
|
||||
}
|
||||
|
||||
// ListRetentionExecTasks List Retention Execution Tasks
|
||||
func (r *RetentionAPI) ListRetentionExecTasks() {
|
||||
id, err := r.GetIDFromURL()
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
eid, err := r.GetInt64FromPath(":eid")
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
page, size, err := r.GetPaginationParams()
|
||||
if err != nil {
|
||||
r.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
query := &q.Query{
|
||||
PageNumber: page,
|
||||
PageSize: size,
|
||||
}
|
||||
p, err := retentionController.GetRetention(id)
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
if !r.requireAccess(p, rbac.ActionList) {
|
||||
return
|
||||
}
|
||||
his, err := retentionController.ListRetentionExecTasks(eid, query)
|
||||
if err != nil {
|
||||
r.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
r.WriteJSONData(his)
|
||||
}
|
||||
|
||||
// GetRetentionExecTaskLog Get Retention Execution Task log
|
||||
func (r *RetentionAPI) GetRetentionExecTaskLog() {
|
||||
tid, err := r.GetInt64FromPath(":tid")
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
log, err := retentionController.GetRetentionExecTaskLog(tid)
|
||||
if err != nil {
|
||||
r.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
w := r.Ctx.ResponseWriter
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(log)
|
||||
}
|
||||
|
||||
func (r *RetentionAPI) requireAccess(p *policy.Metadata, action rbac.Action, subresources ...rbac.Resource) bool {
|
||||
var hasPermission bool
|
||||
|
||||
switch p.Scope.Level {
|
||||
case "project":
|
||||
if len(subresources) == 0 {
|
||||
subresources = append(subresources, rbac.ResourceTagRetention)
|
||||
}
|
||||
resource := rbac.NewProjectNamespace(p.Scope.Reference).Resource(subresources...)
|
||||
hasPermission = r.SecurityCtx.Can(action, resource)
|
||||
default:
|
||||
hasPermission = r.SecurityCtx.IsSysAdmin()
|
||||
}
|
||||
|
||||
if !hasPermission {
|
||||
if !r.SecurityCtx.IsAuthenticated() {
|
||||
r.SendUnAuthorizedError(errors.New("UnAuthorized"))
|
||||
} else {
|
||||
r.SendForbiddenError(errors.New(r.SecurityCtx.GetUsername()))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
@ -25,7 +25,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/core/service/notifications/registry"
|
||||
"github.com/goharbor/harbor/src/core/service/notifications/scheduler"
|
||||
"github.com/goharbor/harbor/src/core/service/token"
|
||||
retentionCtl "github.com/goharbor/harbor/src/pkg/retention/controllers"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
@ -142,15 +141,15 @@ 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{}, "post:TriggerRetentionExec")
|
||||
beego.Router("/api/retentions/:id/executions/:eid", &retentionCtl.RetentionAPI{}, "put: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("/api/retentions/metadatas", &api.RetentionAPI{}, "get:GetMetadatas")
|
||||
beego.Router("/api/retentions/:id", &api.RetentionAPI{}, "get:GetRetention")
|
||||
beego.Router("/api/retentions", &api.RetentionAPI{}, "post:CreateRetention")
|
||||
beego.Router("/api/retentions/:id", &api.RetentionAPI{}, "put:UpdateRetention")
|
||||
beego.Router("/api/retentions/:id/executions", &api.RetentionAPI{}, "post:TriggerRetentionExec")
|
||||
beego.Router("/api/retentions/:id/executions/:eid", &api.RetentionAPI{}, "patch:OperateRetentionExec")
|
||||
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("/v2/*", &controllers.RegistryProxy{}, "*:Handle")
|
||||
|
||||
|
@ -14,17 +14,259 @@
|
||||
|
||||
package retention
|
||||
|
||||
import "github.com/goharbor/harbor/src/jobservice/job"
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/pkg/project"
|
||||
"github.com/goharbor/harbor/src/pkg/repository"
|
||||
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
||||
"github.com/goharbor/harbor/src/pkg/retention/q"
|
||||
"github.com/goharbor/harbor/src/pkg/scheduler"
|
||||
"time"
|
||||
)
|
||||
|
||||
// APIController to handle the requests related with retention
|
||||
type APIController interface {
|
||||
// Handle the related hooks from the job service and launch the corresponding actions if needed
|
||||
//
|
||||
// Arguments:
|
||||
// policyID string : uuid of the retention policy
|
||||
// PolicyID string : uuid of the retention policy
|
||||
// event *job.StatusChange : event object sent by job service
|
||||
//
|
||||
// Returns:
|
||||
// common error object if any errors occurred
|
||||
HandleHook(policyID string, event *job.StatusChange) error
|
||||
|
||||
GetRetention(id int64) (*policy.Metadata, error)
|
||||
|
||||
CreateRetention(p *policy.Metadata) error
|
||||
|
||||
UpdateRetention(p *policy.Metadata) error
|
||||
|
||||
DeleteRetention(id int64) error
|
||||
|
||||
TriggerRetentionExec(policyID int64, trigger string, dryRun bool) error
|
||||
|
||||
OperateRetentionExec(eid int64, action string) error
|
||||
|
||||
ListRetentionExecs(policyID int64, query *q.Query) ([]*Execution, error)
|
||||
|
||||
ListRetentionExecTasks(executionID int64, query *q.Query) ([]*Task, error)
|
||||
|
||||
GetRetentionExecTaskLog(taskID int64) ([]byte, error)
|
||||
}
|
||||
|
||||
// DefaultAPIController ...
|
||||
type DefaultAPIController struct {
|
||||
manager Manager
|
||||
launcher Launcher
|
||||
projectManager project.Manager
|
||||
repositoryMgr repository.Manager
|
||||
scheduler scheduler.Scheduler
|
||||
}
|
||||
|
||||
const (
|
||||
// RetentionSchedulerCallback ...
|
||||
RetentionSchedulerCallback = "RetentionSchedulerCallback"
|
||||
)
|
||||
|
||||
// TriggerParam ...
|
||||
type TriggerParam struct {
|
||||
PolicyID int64
|
||||
Trigger string
|
||||
}
|
||||
|
||||
// GetRetention Get Retention
|
||||
func (r *DefaultAPIController) GetRetention(id int64) (*policy.Metadata, error) {
|
||||
return r.manager.GetPolicy(id)
|
||||
}
|
||||
|
||||
// CreateRetention Create Retention
|
||||
func (r *DefaultAPIController) CreateRetention(p *policy.Metadata) error {
|
||||
if p.Trigger.Kind == policy.TriggerKindSchedule {
|
||||
if p.Trigger.Settings != nil {
|
||||
cron, ok := p.Trigger.Settings[policy.TriggerSettingsCron]
|
||||
if ok {
|
||||
jobid, err := r.scheduler.Schedule(cron.(string), RetentionSchedulerCallback, TriggerParam{
|
||||
PolicyID: p.ID,
|
||||
Trigger: ExecutionTriggerSchedule,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Trigger.References[policy.TriggerReferencesJobid] = jobid
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, err := r.manager.CreatePolicy(p); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateRetention Update Retention
|
||||
func (r *DefaultAPIController) UpdateRetention(p *policy.Metadata) error {
|
||||
p0, err := r.manager.GetPolicy(p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
needUn := false
|
||||
needSch := false
|
||||
|
||||
if p0.Trigger.Kind != p.Trigger.Kind {
|
||||
if p0.Trigger.Kind == policy.TriggerKindSchedule {
|
||||
needUn = true
|
||||
}
|
||||
|
||||
if p.Trigger.Kind == policy.TriggerKindSchedule {
|
||||
needSch = true
|
||||
}
|
||||
} else {
|
||||
switch p.Trigger.Kind {
|
||||
case policy.TriggerKindSchedule:
|
||||
if p0.Trigger.Settings["cron"] != p.Trigger.Settings["cron"] {
|
||||
// unschedule old
|
||||
if len(p0.Trigger.References[policy.TriggerReferencesJobid].(string)) > 0 {
|
||||
needUn = true
|
||||
}
|
||||
// schedule new
|
||||
if len(p.Trigger.Settings[policy.TriggerSettingsCron].(string)) > 0 {
|
||||
// valid cron
|
||||
needSch = true
|
||||
}
|
||||
}
|
||||
case "":
|
||||
|
||||
default:
|
||||
return fmt.Errorf("Not support Trigger %s", p.Trigger.Kind)
|
||||
}
|
||||
}
|
||||
if needUn {
|
||||
err = r.scheduler.UnSchedule(p0.Trigger.References[policy.TriggerReferencesJobid].(int64))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if needSch {
|
||||
jobid, err := r.scheduler.Schedule(p.Trigger.Settings[policy.TriggerSettingsCron].(string), RetentionSchedulerCallback, TriggerParam{
|
||||
PolicyID: p.ID,
|
||||
Trigger: ExecutionTriggerSchedule,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Trigger.References[policy.TriggerReferencesJobid] = jobid
|
||||
}
|
||||
|
||||
return r.manager.UpdatePolicy(p)
|
||||
}
|
||||
|
||||
// DeleteRetention Delete Retention
|
||||
func (r *DefaultAPIController) DeleteRetention(id int64) error {
|
||||
p, err := r.manager.GetPolicy(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p.Trigger.Kind == policy.TriggerKindSchedule && len(p.Trigger.Settings[policy.TriggerSettingsCron].(string)) > 0 {
|
||||
err = r.scheduler.UnSchedule(p.Trigger.References[policy.TriggerReferencesJobid].(int64))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return r.manager.DeletePolicyAndExec(id)
|
||||
}
|
||||
|
||||
// TriggerRetentionExec Trigger Retention Execution
|
||||
func (r *DefaultAPIController) TriggerRetentionExec(policyID int64, trigger string, dryRun bool) error {
|
||||
p, err := r.manager.GetPolicy(policyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exec := &Execution{
|
||||
PolicyID: policyID,
|
||||
StartTime: time.Now(),
|
||||
Status: ExecutionStatusInProgress,
|
||||
Trigger: trigger,
|
||||
DryRun: dryRun,
|
||||
}
|
||||
id, err := r.manager.CreateExecution(exec)
|
||||
// TODO launcher with DryRun param
|
||||
num, err := r.launcher.Launch(p, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if num == 0 {
|
||||
exec := &Execution{
|
||||
ID: id,
|
||||
EndTime: time.Now(),
|
||||
Status: ExecutionStatusSucceed,
|
||||
}
|
||||
err = r.manager.UpdateExecution(exec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
// OperateRetentionExec Operate Retention Execution
|
||||
func (r *DefaultAPIController) OperateRetentionExec(eid int64, action string) error {
|
||||
e, err := r.manager.GetExecution(eid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exec := &Execution{}
|
||||
switch action {
|
||||
case "stop":
|
||||
if e.Status != ExecutionStatusInProgress {
|
||||
return fmt.Errorf("Can't abort, current status is %s", e.Status)
|
||||
}
|
||||
exec.ID = eid
|
||||
exec.Status = ExecutionStatusStopped
|
||||
exec.EndTime = time.Now()
|
||||
// TODO stop the execution
|
||||
default:
|
||||
return fmt.Errorf("not support action %s", action)
|
||||
}
|
||||
|
||||
return r.manager.UpdateExecution(exec)
|
||||
}
|
||||
|
||||
// ListRetentionExecs List Retention Executions
|
||||
func (r *DefaultAPIController) ListRetentionExecs(policyID int64, query *q.Query) ([]*Execution, error) {
|
||||
return r.manager.ListExecutions(policyID, query)
|
||||
}
|
||||
|
||||
// ListRetentionExecTasks List Retention Execution Histories
|
||||
func (r *DefaultAPIController) ListRetentionExecTasks(executionID int64, query *q.Query) ([]*Task, error) {
|
||||
q1 := &q.TaskQuery{
|
||||
ExecutionID: executionID,
|
||||
PageNumber: query.PageNumber,
|
||||
PageSize: query.PageSize,
|
||||
}
|
||||
return r.manager.ListTasks(q1)
|
||||
}
|
||||
|
||||
// GetRetentionExecTaskLog Get Retention Execution Task Log
|
||||
func (r *DefaultAPIController) GetRetentionExecTaskLog(taskID int64) ([]byte, error) {
|
||||
return r.manager.GetTaskLog(taskID)
|
||||
}
|
||||
|
||||
// HandleHook HandleHook
|
||||
func (r *DefaultAPIController) HandleHook(policyID string, event *job.StatusChange) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
// NewAPIController ...
|
||||
func NewAPIController(projectManager project.Manager, repositoryMgr repository.Manager, scheduler scheduler.Scheduler, retentionLauncher Launcher) APIController {
|
||||
return &DefaultAPIController{
|
||||
manager: NewManager(),
|
||||
launcher: retentionLauncher,
|
||||
projectManager: projectManager,
|
||||
repositoryMgr: repositoryMgr,
|
||||
scheduler: scheduler,
|
||||
}
|
||||
}
|
||||
|
@ -1,170 +0,0 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/core/api"
|
||||
"github.com/goharbor/harbor/src/pkg/retention"
|
||||
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
||||
"github.com/goharbor/harbor/src/pkg/retention/q"
|
||||
)
|
||||
|
||||
// RetentionAPI ...
|
||||
type RetentionAPI struct {
|
||||
api.BaseController
|
||||
manager retention.Manager
|
||||
}
|
||||
|
||||
// Prepare validates the user
|
||||
func (r *RetentionAPI) Prepare() {
|
||||
r.BaseController.Prepare()
|
||||
r.manager = retention.NewManager()
|
||||
}
|
||||
|
||||
// GetRetention Get Retention
|
||||
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()
|
||||
}
|
||||
|
||||
// CreateRetention Create Retention
|
||||
func (r *RetentionAPI) CreateRetention() {
|
||||
p := &policy.Metadata{}
|
||||
isValid, err := r.DecodeJSONReqAndValidate(p)
|
||||
if !isValid {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
|
||||
r.manager.CreatePolicy(p)
|
||||
}
|
||||
|
||||
// UpdateRetention Update Retention
|
||||
func (r *RetentionAPI) UpdateRetention() {
|
||||
id, err := r.GetIDFromURL()
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
p := &policy.Metadata{}
|
||||
isValid, err := r.DecodeJSONReqAndValidate(p)
|
||||
if !isValid {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
p.ID = id
|
||||
r.manager.UpdatePolicy(p)
|
||||
}
|
||||
|
||||
// DeleteRetention Delete Retention
|
||||
func (r *RetentionAPI) DeleteRetention() {
|
||||
id, err := r.GetIDFromURL()
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
r.manager.DeletePolicy(id)
|
||||
}
|
||||
|
||||
// TriggerRetentionExec Trigger Retention Execution
|
||||
func (r *RetentionAPI) TriggerRetentionExec() {
|
||||
id, err := r.GetIDFromURL()
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
exec := &retention.Execution{
|
||||
PolicyID: id,
|
||||
StartTime: time.Now(),
|
||||
Status: "Running",
|
||||
}
|
||||
r.manager.CreateExecution(exec)
|
||||
}
|
||||
|
||||
// OperateRetentionExec Operate Retention Execution
|
||||
func (r *RetentionAPI) OperateRetentionExec() {
|
||||
eid, err := r.GetSpecialIDFromURL("eid")
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
exec := &retention.Execution{}
|
||||
isValid, err := r.DecodeJSONReqAndValidate(exec)
|
||||
if !isValid {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
exec.ID = eid
|
||||
r.manager.UpdateExecution(nil)
|
||||
}
|
||||
|
||||
// GetRetentionExec Get Retention Execution
|
||||
func (r *RetentionAPI) GetRetentionExec() {
|
||||
eid, err := r.GetSpecialIDFromURL("eid")
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
exec, err := r.manager.GetExecution(eid)
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
r.Data["json"] = exec
|
||||
r.ServeJSON()
|
||||
}
|
||||
|
||||
// ListRetentionExec List Retention Execution
|
||||
func (r *RetentionAPI) ListRetentionExec() {
|
||||
page, size, err := r.GetPaginationParams()
|
||||
if err != nil {
|
||||
r.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
query := &q.Query{
|
||||
PageNumber: page,
|
||||
PageSize: size,
|
||||
}
|
||||
execs, err := r.manager.ListExecutions(query)
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
r.Data["json"] = execs
|
||||
r.ServeJSON()
|
||||
}
|
||||
|
||||
// ListRetentionExecHistory List Retention Execution Histories
|
||||
func (r *RetentionAPI) ListRetentionExecHistory() {
|
||||
eid, err := r.GetSpecialIDFromURL("eid")
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
page, size, err := r.GetPaginationParams()
|
||||
if err != nil {
|
||||
r.SendInternalServerError(err)
|
||||
return
|
||||
}
|
||||
tasks, err := r.manager.ListTasks(&q.TaskQuery{
|
||||
ExecutionID: eid,
|
||||
PageNumber: page,
|
||||
PageSize: size,
|
||||
})
|
||||
if err != nil {
|
||||
r.SendBadRequestError(err)
|
||||
return
|
||||
}
|
||||
r.Data["json"] = tasks
|
||||
r.ServeJSON()
|
||||
}
|
@ -30,11 +30,10 @@ type RetentionPolicy struct {
|
||||
|
||||
// RetentionExecution Retention Execution
|
||||
type RetentionExecution struct {
|
||||
ID int64 `orm:"pk;auto;column(id)" json:"id"`
|
||||
PolicyID int64
|
||||
Status string
|
||||
StatusText string
|
||||
Dry bool
|
||||
ID int64 `orm:"pk;auto;column(id)" json:"id"`
|
||||
PolicyID int64 `orm:"column(policy_id)"`
|
||||
Status string
|
||||
DryRun bool
|
||||
// manual, scheduled
|
||||
Trigger string
|
||||
Total int
|
||||
@ -46,18 +45,6 @@ type RetentionExecution struct {
|
||||
EndTime time.Time
|
||||
}
|
||||
|
||||
/*
|
||||
// RetentionTask Retention Task
|
||||
type RetentionTask struct {
|
||||
ID int64
|
||||
ExecutionID int64
|
||||
RuleID int
|
||||
RuleDisplayText string
|
||||
Artifact string
|
||||
Timestamp time.Time
|
||||
}
|
||||
*/
|
||||
|
||||
// RetentionTask ...
|
||||
type RetentionTask struct {
|
||||
ID int64 `orm:"pk;auto;column(id)"`
|
||||
@ -69,10 +56,10 @@ type RetentionTask struct {
|
||||
|
||||
// RetentionScheduleJob Retention Schedule Job
|
||||
type RetentionScheduleJob struct {
|
||||
ID int64
|
||||
ID int64 `orm:"pk;auto;column(id)" json:"id"`
|
||||
Status string
|
||||
PolicyID int64
|
||||
JobID int64
|
||||
PolicyID int64 `orm:"column(policy_id)"`
|
||||
JobID int64 `orm:"column(job_id)"`
|
||||
CreateTime time.Time
|
||||
UpdateTime time.Time
|
||||
}
|
||||
|
@ -16,15 +16,26 @@ func CreatePolicy(p *models.RetentionPolicy) (int64, error) {
|
||||
}
|
||||
|
||||
// UpdatePolicy Update Policy
|
||||
func UpdatePolicy(p *models.RetentionPolicy) error {
|
||||
func UpdatePolicy(p *models.RetentionPolicy, cols ...string) error {
|
||||
o := dao.GetOrmer()
|
||||
_, err := o.Update(p)
|
||||
_, err := o.Update(p, cols...)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeletePolicy Delete Policy
|
||||
func DeletePolicy(id int64) error {
|
||||
// DeletePolicyAndExec Delete Policy and Exec
|
||||
func DeletePolicyAndExec(id int64) error {
|
||||
o := dao.GetOrmer()
|
||||
if _, err := o.Raw("delete from retention_task where execution_id in (select id from retention_execution where policy_id = ?) ", id).Exec(); err != nil {
|
||||
return nil
|
||||
}
|
||||
if _, err := o.Raw("delete from retention_execution where policy_id = ?", id).Exec(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := o.Delete(&models.RetentionExecution{
|
||||
PolicyID: id,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := o.Delete(&models.RetentionPolicy{
|
||||
ID: id,
|
||||
})
|
||||
@ -50,9 +61,9 @@ func CreateExecution(e *models.RetentionExecution) (int64, error) {
|
||||
}
|
||||
|
||||
// UpdateExecution Update Execution
|
||||
func UpdateExecution(e *models.RetentionExecution) error {
|
||||
func UpdateExecution(e *models.RetentionExecution, cols ...string) error {
|
||||
o := dao.GetOrmer()
|
||||
_, err := o.Update(e)
|
||||
_, err := o.Update(e, cols...)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -78,10 +89,14 @@ func GetExecution(id int64) (*models.RetentionExecution, error) {
|
||||
}
|
||||
|
||||
// ListExecutions List Executions
|
||||
func ListExecutions(query *q.Query) ([]*models.RetentionExecution, error) {
|
||||
func ListExecutions(policyID int64, query *q.Query) ([]*models.RetentionExecution, error) {
|
||||
o := dao.GetOrmer()
|
||||
qs := o.QueryTable(new(models.RetentionExecution))
|
||||
qs.Limit(query.PageSize, (query.PageNumber-1)*query.PageSize)
|
||||
|
||||
qs = qs.Filter("policy_id", policyID)
|
||||
if query != nil {
|
||||
qs = qs.Limit(query.PageSize, (query.PageNumber-1)*query.PageSize)
|
||||
}
|
||||
var execs []*models.RetentionExecution
|
||||
_, err := qs.All(&execs)
|
||||
if err != nil {
|
||||
@ -95,8 +110,10 @@ func ListExecutions(query *q.Query) ([]*models.RetentionExecution, error) {
|
||||
func ListExecHistories(executionID int64, query *q.Query) ([]*models.RetentionTask, error) {
|
||||
o := dao.GetOrmer()
|
||||
qs := o.QueryTable(new(models.RetentionTask))
|
||||
qs.Filter("Execution_ID", executionID)
|
||||
qs.Limit(query.PageSize, (query.PageNumber-1)*query.PageSize)
|
||||
qs = qs.Filter("Execution_ID", executionID)
|
||||
if query != nil {
|
||||
qs = qs.Limit(query.PageSize, (query.PageNumber-1)*query.PageSize)
|
||||
}
|
||||
var tasks []*models.RetentionTask
|
||||
_, err := qs.All(&tasks)
|
||||
if err != nil {
|
||||
|
@ -91,7 +91,7 @@ func TestPolicy(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.EqualValues(t, "test", p1.ScopeLevel)
|
||||
|
||||
err = DeletePolicy(id)
|
||||
err = DeletePolicyAndExec(id)
|
||||
assert.Nil(t, err)
|
||||
|
||||
p1, err = GetPolicy(id)
|
||||
@ -99,6 +99,86 @@ func TestPolicy(t *testing.T) {
|
||||
assert.True(t, strings.Contains(err.Error(), "no row found"))
|
||||
}
|
||||
|
||||
func TestExecution(t *testing.T) {
|
||||
p := &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: ".+",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
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)
|
||||
|
||||
policyID, err := CreatePolicy(p1)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, policyID > 0)
|
||||
|
||||
e := &models.RetentionExecution{
|
||||
PolicyID: policyID,
|
||||
Status: "Running",
|
||||
DryRun: false,
|
||||
Trigger: "manual",
|
||||
Total: 10,
|
||||
StartTime: time.Now(),
|
||||
}
|
||||
id, err := CreateExecution(e)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, id > 0)
|
||||
|
||||
e1, err := GetExecution(id)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, e1)
|
||||
assert.EqualValues(t, id, e1.ID)
|
||||
|
||||
es, err := ListExecutions(policyID, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.EqualValues(t, 1, len(es))
|
||||
}
|
||||
|
||||
func TestTask(t *testing.T) {
|
||||
task := &models.RetentionTask{
|
||||
ExecutionID: 1,
|
||||
|
@ -82,7 +82,7 @@ func (f *fakeRetentionManager) CreatePolicy(p *policy.Metadata) (int64, error) {
|
||||
func (f *fakeRetentionManager) UpdatePolicy(p *policy.Metadata) error {
|
||||
return nil
|
||||
}
|
||||
func (f *fakeRetentionManager) DeletePolicy(ID int64) error {
|
||||
func (f *fakeRetentionManager) DeletePolicyAndExec(ID int64) error {
|
||||
return nil
|
||||
}
|
||||
func (f *fakeRetentionManager) GetPolicy(ID int64) (*policy.Metadata, error) {
|
||||
@ -109,11 +109,11 @@ func (f *fakeRetentionManager) UpdateTask(task *Task, cols ...string) error {
|
||||
func (f *fakeRetentionManager) GetTaskLog(taskID int64) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (f *fakeRetentionManager) ListExecutions(query *q.Query) ([]*Execution, error) {
|
||||
func (f *fakeRetentionManager) ListExecutions(policyID int64, query *q.Query) ([]*Execution, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (f *fakeRetentionManager) AppendHistory(history *History) error {
|
||||
return nil
|
||||
func (f *fakeRetentionManager) AppendHistory(history *History) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (f *fakeRetentionManager) ListHistories(executionID int64, query *q.Query) ([]*History, error) {
|
||||
return nil, nil
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/astaxie/beego/orm"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/pkg/retention/dao"
|
||||
@ -35,7 +36,7 @@ type Manager interface {
|
||||
UpdatePolicy(p *policy.Metadata) error
|
||||
// Delete the specified policy
|
||||
// No actual use so far
|
||||
DeletePolicy(ID int64) error
|
||||
DeletePolicyAndExec(ID int64) error
|
||||
// Get the specified policy
|
||||
GetPolicy(ID int64) (*policy.Metadata, error)
|
||||
// Create a new retention execution
|
||||
@ -45,7 +46,7 @@ type Manager interface {
|
||||
// Get the specified execution
|
||||
GetExecution(eid int64) (*Execution, error)
|
||||
// List execution histories
|
||||
ListExecutions(query *q.Query) ([]*Execution, error)
|
||||
ListExecutions(policyID int64, query *q.Query) ([]*Execution, error)
|
||||
// List tasks histories
|
||||
ListTasks(query ...*q.TaskQuery) ([]*Task, error)
|
||||
// Create a new retention task
|
||||
@ -62,7 +63,7 @@ type DefaultManager struct {
|
||||
|
||||
// CreatePolicy Create Policy
|
||||
func (d *DefaultManager) CreatePolicy(p *policy.Metadata) (int64, error) {
|
||||
var p1 *models.RetentionPolicy
|
||||
p1 := &models.RetentionPolicy{}
|
||||
p1.ScopeLevel = p.Scope.Level
|
||||
p1.TriggerKind = p.Trigger.Kind
|
||||
data, _ := json.Marshal(p)
|
||||
@ -74,7 +75,7 @@ func (d *DefaultManager) CreatePolicy(p *policy.Metadata) (int64, error) {
|
||||
|
||||
// UpdatePolicy Update Policy
|
||||
func (d *DefaultManager) UpdatePolicy(p *policy.Metadata) error {
|
||||
var p1 *models.RetentionPolicy
|
||||
p1 := &models.RetentionPolicy{}
|
||||
p1.ID = p.ID
|
||||
p1.ScopeLevel = p.Scope.Level
|
||||
p1.TriggerKind = p.Trigger.Kind
|
||||
@ -83,55 +84,63 @@ func (d *DefaultManager) UpdatePolicy(p *policy.Metadata) error {
|
||||
p.ID = p1.ID
|
||||
p1.Data = string(data)
|
||||
p1.UpdateTime = time.Now()
|
||||
return dao.UpdatePolicy(p1)
|
||||
return dao.UpdatePolicy(p1, "scope_level", "trigger_kind", "data", "update_time")
|
||||
}
|
||||
|
||||
// DeletePolicy Delete Policy
|
||||
func (d *DefaultManager) DeletePolicy(id int64) error {
|
||||
return dao.DeletePolicy(id)
|
||||
// DeletePolicyAndExec Delete Policy
|
||||
func (d *DefaultManager) DeletePolicyAndExec(id int64) error {
|
||||
return dao.DeletePolicyAndExec(id)
|
||||
}
|
||||
|
||||
// GetPolicy Get Policy
|
||||
func (d *DefaultManager) GetPolicy(id int64) (*policy.Metadata, error) {
|
||||
p1, err := dao.GetPolicy(id)
|
||||
if err != nil {
|
||||
if err == orm.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var p *policy.Metadata
|
||||
p := &policy.Metadata{}
|
||||
if err = json.Unmarshal([]byte(p1.Data), p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.ID = id
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// CreateExecution Create Execution
|
||||
func (d *DefaultManager) CreateExecution(execution *Execution) (int64, error) {
|
||||
var exec *models.RetentionExecution
|
||||
exec := &models.RetentionExecution{}
|
||||
exec.PolicyID = execution.PolicyID
|
||||
exec.StartTime = time.Now()
|
||||
exec.DryRun = execution.DryRun
|
||||
exec.Status = "Running"
|
||||
exec.Trigger = "manual"
|
||||
return dao.CreateExecution(exec)
|
||||
}
|
||||
|
||||
// UpdateExecution Update Execution
|
||||
func (d *DefaultManager) UpdateExecution(execution *Execution) error {
|
||||
var exec *models.RetentionExecution
|
||||
exec := &models.RetentionExecution{}
|
||||
exec.ID = execution.ID
|
||||
exec.PolicyID = execution.PolicyID
|
||||
exec.StartTime = time.Now()
|
||||
exec.Status = "Running"
|
||||
return dao.UpdateExecution(exec)
|
||||
exec.EndTime = execution.EndTime
|
||||
exec.Status = execution.Status
|
||||
return dao.UpdateExecution(exec, "end_time", "status")
|
||||
}
|
||||
|
||||
// ListExecutions List Executions
|
||||
func (d *DefaultManager) ListExecutions(query *q.Query) ([]*Execution, error) {
|
||||
execs, err := dao.ListExecutions(query)
|
||||
func (d *DefaultManager) ListExecutions(policyID int64, query *q.Query) ([]*Execution, error) {
|
||||
execs, err := dao.ListExecutions(policyID, query)
|
||||
if err != nil {
|
||||
if err == orm.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var execs1 []*Execution
|
||||
for _, e := range execs {
|
||||
var e1 *Execution
|
||||
e1 := &Execution{}
|
||||
e1.ID = e.ID
|
||||
e1.PolicyID = e.PolicyID
|
||||
e1.Status = e.Status
|
||||
@ -148,7 +157,7 @@ func (d *DefaultManager) GetExecution(eid int64) (*Execution, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var e1 *Execution
|
||||
e1 := &Execution{}
|
||||
e1.ID = e.ID
|
||||
e1.PolicyID = e.PolicyID
|
||||
e1.Status = e.Status
|
||||
@ -175,6 +184,9 @@ func (d *DefaultManager) CreateTask(task *Task) (int64, error) {
|
||||
func (d *DefaultManager) ListTasks(query ...*q.TaskQuery) ([]*Task, error) {
|
||||
ts, err := dao.ListTask(query...)
|
||||
if err != nil {
|
||||
if err == orm.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
tasks := []*Task{}
|
||||
@ -212,39 +224,6 @@ func (d *DefaultManager) GetTaskLog(taskID int64) ([]byte, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
/*
|
||||
// ListHistories List Histories
|
||||
func (d *DefaultManager) ListHistories(executionID int64, query *q.Query) ([]*History, error) {
|
||||
his, err := dao.ListExecHistories(executionID, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var his1 []*History
|
||||
for _, h := range his {
|
||||
var h1 *History
|
||||
h1.ExecutionID = h.ExecutionID
|
||||
h1.Artifact = h.Artifact
|
||||
h1.Rule.ID = h.RuleID
|
||||
h1.Rule.DisplayText = h.RuleDisplayText
|
||||
h1.Timestamp = h.Timestamp
|
||||
his1 = append(his1, h1)
|
||||
}
|
||||
return his1, nil
|
||||
}
|
||||
|
||||
// AppendHistory Append History
|
||||
func (d *DefaultManager) AppendHistory(h *History) error {
|
||||
var h1 *models.RetentionTask
|
||||
h1.ExecutionID = h.ExecutionID
|
||||
h1.Artifact = h.Artifact
|
||||
h1.RuleID = h.Rule.ID
|
||||
h1.RuleDisplayText = h.Rule.DisplayText
|
||||
h1.Timestamp = h.Timestamp
|
||||
_, err := dao.AppendExecHistory(h1)
|
||||
return err
|
||||
}
|
||||
*/
|
||||
|
||||
// NewManager ...
|
||||
func NewManager() Manager {
|
||||
return &DefaultManager{}
|
||||
|
203
src/pkg/retention/manager_test.go
Normal file
203
src/pkg/retention/manager_test.go
Normal file
@ -0,0 +1,203 @@
|
||||
package retention
|
||||
|
||||
import (
|
||||
"github.com/goharbor/harbor/src/pkg/retention/q"
|
||||
"github.com/stretchr/testify/require"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
dao.PrepareTestForPostgresSQL()
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestPolicy(t *testing.T) {
|
||||
m := NewManager()
|
||||
p1 := &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: ".+",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Trigger: &policy.Trigger{
|
||||
Kind: "Schedule",
|
||||
Settings: map[string]interface{}{
|
||||
"cron": "* 22 11 * * *",
|
||||
},
|
||||
},
|
||||
Scope: &policy.Scope{
|
||||
Level: "project",
|
||||
Reference: 1,
|
||||
},
|
||||
}
|
||||
|
||||
id, err := m.CreatePolicy(p1)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, id > 0)
|
||||
|
||||
p1, err = m.GetPolicy(id)
|
||||
assert.Nil(t, err)
|
||||
assert.EqualValues(t, "project", p1.Scope.Level)
|
||||
assert.True(t, p1.ID > 0)
|
||||
|
||||
p1.Scope.Level = "test"
|
||||
err = m.UpdatePolicy(p1)
|
||||
assert.Nil(t, err)
|
||||
p1, err = m.GetPolicy(id)
|
||||
assert.Nil(t, err)
|
||||
assert.EqualValues(t, "test", p1.Scope.Level)
|
||||
|
||||
err = m.DeletePolicyAndExec(id)
|
||||
assert.Nil(t, err)
|
||||
|
||||
p1, err = m.GetPolicy(id)
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, p1)
|
||||
}
|
||||
|
||||
func TestExecution(t *testing.T) {
|
||||
m := NewManager()
|
||||
p1 := &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: ".+",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Trigger: &policy.Trigger{
|
||||
Kind: "Schedule",
|
||||
Settings: map[string]interface{}{
|
||||
"cron": "* 22 11 * * *",
|
||||
},
|
||||
},
|
||||
Scope: &policy.Scope{
|
||||
Level: "project",
|
||||
Reference: 1,
|
||||
},
|
||||
}
|
||||
|
||||
policyID, err := m.CreatePolicy(p1)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, policyID > 0)
|
||||
|
||||
e1 := &Execution{
|
||||
PolicyID: policyID,
|
||||
StartTime: time.Now(),
|
||||
Status: ExecutionStatusInProgress,
|
||||
Trigger: ExecutionTriggerManual,
|
||||
DryRun: false,
|
||||
}
|
||||
id, err := m.CreateExecution(e1)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, id > 0)
|
||||
|
||||
e1, err = m.GetExecution(id)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, e1)
|
||||
assert.EqualValues(t, id, e1.ID)
|
||||
|
||||
e1.Status = ExecutionStatusFailed
|
||||
err = m.UpdateExecution(e1)
|
||||
assert.Nil(t, err)
|
||||
|
||||
e1, err = m.GetExecution(id)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, e1)
|
||||
assert.EqualValues(t, ExecutionStatusFailed, e1.Status)
|
||||
|
||||
es, err := m.ListExecutions(policyID, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.EqualValues(t, 1, len(es))
|
||||
}
|
||||
|
||||
func TestTask(t *testing.T) {
|
||||
m := NewManager()
|
||||
task := &Task{
|
||||
ExecutionID: 1,
|
||||
Status: TaskStatusPending,
|
||||
StartTime: time.Now(),
|
||||
}
|
||||
// create
|
||||
id, err := m.CreateTask(task)
|
||||
require.Nil(t, err)
|
||||
|
||||
// update
|
||||
task.ID = id
|
||||
task.Status = TaskStatusInProgress
|
||||
err = m.UpdateTask(task, "Status")
|
||||
require.Nil(t, err)
|
||||
|
||||
// list
|
||||
tasks, err := m.ListTasks(&q.TaskQuery{
|
||||
ExecutionID: 1,
|
||||
Status: TaskStatusInProgress,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(tasks))
|
||||
assert.Equal(t, int64(1), tasks[0].ExecutionID)
|
||||
assert.Equal(t, TaskStatusInProgress, tasks[0].Status)
|
||||
|
||||
task.Status = TaskStatusFailed
|
||||
err = m.UpdateTask(task, "Status")
|
||||
require.Nil(t, err)
|
||||
}
|
@ -31,6 +31,9 @@ const (
|
||||
|
||||
CandidateKindImage string = "image"
|
||||
CandidateKindChart string = "chart"
|
||||
|
||||
ExecutionTriggerManual string = "Manual"
|
||||
ExecutionTriggerSchedule string = "Schedule"
|
||||
)
|
||||
|
||||
// Execution of retention
|
||||
@ -40,6 +43,8 @@ type Execution struct {
|
||||
StartTime time.Time `json:"start_time"`
|
||||
EndTime time.Time `json:"end_time,omitempty"`
|
||||
Status string `json:"status"`
|
||||
Trigger string `json:"Trigger"`
|
||||
DryRun bool `json:"dry_run"`
|
||||
}
|
||||
|
||||
// Task of retention
|
||||
@ -53,6 +58,7 @@ type Task struct {
|
||||
|
||||
// History of retention
|
||||
type History struct {
|
||||
ID int64 `json:"id,omitempty"`
|
||||
ExecutionID int64 `json:"execution_id"`
|
||||
Rule struct {
|
||||
ID int `json:"id"`
|
||||
|
@ -21,6 +21,17 @@ import (
|
||||
const (
|
||||
// AlgorithmOR for OR algorithm
|
||||
AlgorithmOR = "or"
|
||||
|
||||
// TriggerKindSchedule Schedule
|
||||
TriggerKindSchedule = "Schedule"
|
||||
|
||||
// TriggerReferencesJobid job_id
|
||||
TriggerReferencesJobid = "job_id"
|
||||
// TriggerSettingsCron cron
|
||||
TriggerSettingsCron = "cron"
|
||||
|
||||
// ScopeLevelProject project
|
||||
ScopeLevelProject = "project"
|
||||
)
|
||||
|
||||
// Metadata of policy
|
||||
@ -30,16 +41,16 @@ type Metadata struct {
|
||||
|
||||
// Algorithm applied to the rules
|
||||
// "OR" / "AND"
|
||||
Algorithm string `json:"algorithm"`
|
||||
Algorithm string `json:"algorithm" valid:"Required;Match(/^(OR|AND)$/)"`
|
||||
|
||||
// Rule collection
|
||||
Rules []rule.Metadata `json:"rules"`
|
||||
|
||||
// Trigger about how to launch the policy
|
||||
Trigger *Trigger `json:"trigger"`
|
||||
Trigger *Trigger `json:"trigger" valid:"Required"`
|
||||
|
||||
// Which scope the policy will be applied to
|
||||
Scope *Scope `json:"scope"`
|
||||
Scope *Scope `json:"scope" valid:"Required"`
|
||||
|
||||
// The max number of rules in a policy
|
||||
Capacity int `json:"cap"`
|
||||
@ -64,9 +75,9 @@ type Trigger struct {
|
||||
type Scope struct {
|
||||
// Scope level declaration
|
||||
// 'system', 'project' and 'repository'
|
||||
Level string `json:"level"`
|
||||
Level string `json:"level" valid:"Required;Match(/^(project)$/)"`
|
||||
|
||||
// The reference identity for the specified level
|
||||
// 0 for 'system', project ID for 'project' and repo ID for 'repository'
|
||||
Reference int64 `json:"ref"`
|
||||
Reference int64 `json:"ref" valid:"Required"`
|
||||
}
|
||||
|
@ -20,38 +20,38 @@ type Metadata struct {
|
||||
ID int `json:"id"`
|
||||
|
||||
// Priority of rule when doing calculating
|
||||
Priority int `json:"priority"`
|
||||
Priority int `json:"priority" valid:"Required"`
|
||||
|
||||
// Action of the rule performs
|
||||
// "retain"
|
||||
Action string `json:"action"`
|
||||
Action string `json:"action" valid:"Required"`
|
||||
|
||||
// Template ID
|
||||
Template string `json:"template"`
|
||||
Template string `json:"template" valid:"Required"`
|
||||
|
||||
// The parameters of this rule
|
||||
Parameters Parameters `json:"params"`
|
||||
|
||||
// Selector attached to the rule for filtering tags
|
||||
TagSelectors []*Selector `json:"tag_selectors"`
|
||||
TagSelectors []*Selector `json:"tag_selectors" valid:"Required"`
|
||||
|
||||
// Selector attached to the rule for filtering scope (e.g: repositories or namespaces)
|
||||
ScopeSelectors map[string][]*Selector `json:"scope_selectors"`
|
||||
ScopeSelectors map[string][]*Selector `json:"scope_selectors" valid:"Required"`
|
||||
}
|
||||
|
||||
// Selector to narrow down the list
|
||||
type Selector struct {
|
||||
// Kind of the selector
|
||||
// "regularExpression" or "label"
|
||||
Kind string `json:"kind"`
|
||||
Kind string `json:"kind" valid:"Required"`
|
||||
|
||||
// Decorated the selector
|
||||
// for "regularExpression" : "matches" and "excludes"
|
||||
// for "label" : "with" and "without"
|
||||
Decoration string `json:"decoration"`
|
||||
Decoration string `json:"decoration" valid:"Required"`
|
||||
|
||||
// Param for the selector
|
||||
Pattern string `json:"pattern"`
|
||||
Pattern string `json:"pattern" valid:"Required"`
|
||||
}
|
||||
|
||||
// Parameters of rule, indexed by the key
|
||||
|
@ -1,37 +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 retention
|
||||
|
||||
// Scheduler of launching retention jobs
|
||||
type Scheduler interface {
|
||||
// Schedule the job to periodically run the retentions
|
||||
//
|
||||
// Arguments:
|
||||
// policyID string : uuid of the retention policy
|
||||
// cron string : cron pattern like `0-59/5 12 * * * *`
|
||||
// Returns:
|
||||
// the returned job ID
|
||||
// common error object if any errors occurred
|
||||
Schedule(policyID string, cron string) (string, error)
|
||||
|
||||
// Unschedule the specified retention policy
|
||||
//
|
||||
// Arguments:
|
||||
// policyID string : uuid of the retention policy
|
||||
//
|
||||
// Returns:
|
||||
// common error object if any errors occurred
|
||||
UnSchedule(policyID string) error
|
||||
}
|
Loading…
Reference in New Issue
Block a user