update gc api to support raw cron string

Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
wang yan 2019-03-04 14:06:20 +08:00
parent dffb971366
commit e373167546
5 changed files with 36 additions and 66 deletions

View File

@ -4671,15 +4671,10 @@ definitions:
properties: properties:
type: type:
type: string type: string
description: The schedule type. The valid values are daily weekly and None. 'None' means to cancel the schedule. description: The schedule type. The valid values are hourly, daily weekly, custom and None. 'None' means to cancel the schedule.
weekday: cron:
type: integer type: string
format: int8 description: A cron expression, a time-based job scheduler.
description: 'Optional, only used when the type is weekly. The valid values are 1-7.'
offtime:
type: integer
format: int64
description: 'The time offset with the UTC 00:00 in seconds.'
SearchResult: SearchResult:
type: object type: object
description: The chart search result item description: The chart search result item

View File

@ -22,16 +22,20 @@ import (
"github.com/astaxie/beego/validation" "github.com/astaxie/beego/validation"
"github.com/goharbor/harbor/src/common/job" "github.com/goharbor/harbor/src/common/job"
"github.com/goharbor/harbor/src/common/job/models" "github.com/goharbor/harbor/src/common/job/models"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/core/config" "github.com/goharbor/harbor/src/core/config"
"github.com/robfig/cron"
) )
const ( const (
// ScheduleHourly : 'Hourly'
ScheduleHourly = "Hourly"
// ScheduleDaily : 'Daily' // ScheduleDaily : 'Daily'
ScheduleDaily = "Daily" ScheduleDaily = "Daily"
// ScheduleWeekly : 'Weekly' // ScheduleWeekly : 'Weekly'
ScheduleWeekly = "Weekly" ScheduleWeekly = "Weekly"
// ScheduleCustom : 'Custom'
ScheduleCustom = "Custom"
// ScheduleManual : 'Manual' // ScheduleManual : 'Manual'
ScheduleManual = "Manual" ScheduleManual = "Manual"
// ScheduleNone : 'None' // ScheduleNone : 'None'
@ -48,12 +52,10 @@ type GCReq struct {
// ScheduleParam defines the parameter of schedule trigger // ScheduleParam defines the parameter of schedule trigger
type ScheduleParam struct { type ScheduleParam struct {
// Daily, Weekly, Manual, None // Daily, Weekly, Custom, Manual, None
Type string `json:"type"` Type string `json:"type"`
// Optional, only used when type is 'weekly' // The cron string of scheduled job
Weekday int8 `json:"weekday"` Cron string `json:"cron"`
// The time offset with the UTC 00:00 in seconds
Offtime int64 `json:"offtime"`
} }
// GCRep holds the response of query gc // GCRep holds the response of query gc
@ -75,9 +77,9 @@ func (gr *GCReq) Valid(v *validation.Validation) {
return return
} }
switch gr.Schedule.Type { switch gr.Schedule.Type {
case ScheduleDaily, ScheduleWeekly: case ScheduleHourly, ScheduleDaily, ScheduleWeekly, ScheduleCustom:
if gr.Schedule.Offtime < 0 || gr.Schedule.Offtime > 3600*24 { if _, err := cron.Parse(gr.Schedule.Cron); err != nil {
v.SetError("offtime", fmt.Sprintf("Invalid schedule trigger parameter offtime: %d", gr.Schedule.Offtime)) v.SetError("cron", fmt.Sprintf("Invalid schedule trigger parameter cron: %s", gr.Schedule.Cron))
} }
case ScheduleManual, ScheduleNone: case ScheduleManual, ScheduleNone:
default: default:
@ -85,26 +87,15 @@ func (gr *GCReq) Valid(v *validation.Validation) {
} }
} }
// ToJob converts request to a job reconiged by job service. // ToJob converts request to a job recognized by job service.
func (gr *GCReq) ToJob() (*models.JobData, error) { func (gr *GCReq) ToJob() *models.JobData {
metadata := &models.JobMetadata{ metadata := &models.JobMetadata{
JobKind: gr.JobKind(), JobKind: gr.JobKind(),
Cron: gr.Schedule.Cron,
// GC job must be unique ... // GC job must be unique ...
IsUnique: true, IsUnique: true,
} }
switch gr.Schedule.Type {
case ScheduleDaily:
h, m, s := utils.ParseOfftime(gr.Schedule.Offtime)
metadata.Cron = fmt.Sprintf("%d %d %d * * *", s, m, h)
case ScheduleWeekly:
h, m, s := utils.ParseOfftime(gr.Schedule.Offtime)
metadata.Cron = fmt.Sprintf("%d %d %d * * %d", s, m, h, gr.Schedule.Weekday%7)
case ScheduleManual, ScheduleNone:
default:
return nil, fmt.Errorf("unsupported schedule trigger type: %s", gr.Schedule.Type)
}
jobData := &models.JobData{ jobData := &models.JobData{
Name: job.ImageGC, Name: job.ImageGC,
Parameters: gr.Parameters, Parameters: gr.Parameters,
@ -112,7 +103,7 @@ func (gr *GCReq) ToJob() (*models.JobData, error) {
StatusHook: fmt.Sprintf("%s/service/notifications/jobs/adminjob/%d", StatusHook: fmt.Sprintf("%s/service/notifications/jobs/adminjob/%d",
config.InternalCoreURL(), gr.ID), config.InternalCoreURL(), gr.ID),
} }
return jobData, nil return jobData
} }
// IsPeriodic ... // IsPeriodic ...
@ -123,7 +114,7 @@ func (gr *GCReq) IsPeriodic() bool {
// JobKind ... // JobKind ...
func (gr *GCReq) JobKind() string { func (gr *GCReq) JobKind() string {
switch gr.Schedule.Type { switch gr.Schedule.Type {
case ScheduleDaily, ScheduleWeekly: case ScheduleHourly, ScheduleDaily, ScheduleWeekly, ScheduleCustom:
return job.JobKindPeriodic return job.JobKindPeriodic
case ScheduleManual: case ScheduleManual:
return job.JobKindGeneric return job.JobKindGeneric

View File

@ -41,16 +41,15 @@ func TestMain(m *testing.M) {
func TestToJob(t *testing.T) { func TestToJob(t *testing.T) {
schedule := &ScheduleParam{ schedule := &ScheduleParam{
Type: "Daily", Type: "Daily",
Offtime: 200, Cron: "20 3 0 * * *",
} }
adminjob := &GCReq{ adminjob := &GCReq{
Schedule: schedule, Schedule: schedule,
} }
job, err := adminjob.ToJob() job := adminjob.ToJob()
assert.Nil(t, err)
assert.Equal(t, job.Name, "IMAGE_GC") assert.Equal(t, job.Name, "IMAGE_GC")
assert.Equal(t, job.Metadata.JobKind, common_job.JobKindPeriodic) assert.Equal(t, job.Metadata.JobKind, common_job.JobKindPeriodic)
assert.Equal(t, job.Metadata.Cron, "20 3 0 * * *") assert.Equal(t, job.Metadata.Cron, "20 3 0 * * *")
@ -65,29 +64,15 @@ func TestToJobManual(t *testing.T) {
Schedule: schedule, Schedule: schedule,
} }
job, err := adminjob.ToJob() job := adminjob.ToJob()
assert.Nil(t, err)
assert.Equal(t, job.Name, "IMAGE_GC") assert.Equal(t, job.Name, "IMAGE_GC")
assert.Equal(t, job.Metadata.JobKind, common_job.JobKindGeneric) assert.Equal(t, job.Metadata.JobKind, common_job.JobKindGeneric)
} }
func TestToJobErr(t *testing.T) {
schedule := &ScheduleParam{
Type: "test",
}
adminjob := &GCReq{
Schedule: schedule,
}
_, err := adminjob.ToJob()
assert.NotNil(t, err)
}
func TestIsPeriodic(t *testing.T) { func TestIsPeriodic(t *testing.T) {
schedule := &ScheduleParam{ schedule := &ScheduleParam{
Type: "Daily", Type: "Daily",
Offtime: 200, Cron: "20 3 0 * * *",
} }
adminjob := &GCReq{ adminjob := &GCReq{
@ -100,8 +85,8 @@ func TestIsPeriodic(t *testing.T) {
func TestJobKind(t *testing.T) { func TestJobKind(t *testing.T) {
schedule := &ScheduleParam{ schedule := &ScheduleParam{
Type: "Daily", Type: "Daily",
Offtime: 200, Cron: "20 3 0 * * *",
} }
adminjob := &GCReq{ adminjob := &GCReq{
Schedule: schedule, Schedule: schedule,
@ -121,12 +106,12 @@ func TestJobKind(t *testing.T) {
func TestCronString(t *testing.T) { func TestCronString(t *testing.T) {
schedule := &ScheduleParam{ schedule := &ScheduleParam{
Type: "Daily", Type: "Daily",
Offtime: 102, Cron: "20 3 0 * * *",
} }
adminjob := &GCReq{ adminjob := &GCReq{
Schedule: schedule, Schedule: schedule,
} }
cronStr := adminjob.CronString() cronStr := adminjob.CronString()
assert.True(t, strings.EqualFold(cronStr, "{\"type\":\"Daily\",\"Weekday\":0,\"Offtime\":102}")) assert.True(t, strings.EqualFold(cronStr, "{\"type\":\"Daily\",\"Cron\":\"20 3 0 * * *\"}"))
} }

View File

@ -214,7 +214,7 @@ func (gc *GCAPI) GetLog() {
// submitJob submits a job to job service per request // submitJob submits a job to job service per request
func (gc *GCAPI) submitJob(gr *models.GCReq) { func (gc *GCAPI) submitJob(gr *models.GCReq) {
// cannot post multiple schdule for GC job. // cannot post multiple schedule for GC job.
if gr.IsPeriodic() { if gr.IsPeriodic() {
jobs, err := dao.GetAdminJobs(&common_models.AdminJobQuery{ jobs, err := dao.GetAdminJobs(&common_models.AdminJobQuery{
Name: common_job.ImageGC, Name: common_job.ImageGC,
@ -243,7 +243,7 @@ func (gc *GCAPI) submitJob(gr *models.GCReq) {
gr.Parameters = map[string]interface{}{ gr.Parameters = map[string]interface{}{
"redis_url_reg": os.Getenv("_REDIS_URL_REG"), "redis_url_reg": os.Getenv("_REDIS_URL_REG"),
} }
job, err := gr.ToJob() job := gr.ToJob()
if err != nil { if err != nil {
gc.HandleInternalServerError(fmt.Sprintf("%v", err)) gc.HandleInternalServerError(fmt.Sprintf("%v", err))
return return

View File

@ -54,7 +54,7 @@ func TestConvertToGCRep(t *testing.T) {
ID: 1, ID: 1,
Name: "IMAGE_GC", Name: "IMAGE_GC",
Kind: "Generic", Kind: "Generic",
Cron: "{\"Type\":\"Manual\",\"Weekday\":0,\"Offtime\":0}", Cron: "{\"Type\":\"Daily\",\"Cron\":\"20 3 0 * * *\"}",
Status: "pending", Status: "pending",
Deleted: false, Deleted: false,
}, },
@ -63,9 +63,8 @@ func TestConvertToGCRep(t *testing.T) {
Name: "IMAGE_GC", Name: "IMAGE_GC",
Kind: "Generic", Kind: "Generic",
Schedule: &api_modes.ScheduleParam{ Schedule: &api_modes.ScheduleParam{
Type: "Manual", Type: "Daily",
Weekday: 0, Cron: "20 3 0 * * *",
Offtime: 0,
}, },
Status: "pending", Status: "pending",
Deleted: false, Deleted: false,