From 3831e82b202c66a7e1e1eaab43cb1827ac1215e0 Mon Sep 17 00:00:00 2001 From: He Weiwei Date: Tue, 22 Dec 2020 11:48:16 +0800 Subject: [PATCH] refactor: remove code of admin job (#13819) Remove code of admin job as it's not needed by scan all/gc now. Signed-off-by: He Weiwei --- .../postgresql/0050_2.2.0_schema.up.sql | 7 +- src/common/dao/admin_job.go | 160 -------- src/common/dao/admin_job_test.go | 145 ------- src/common/models/adminjob.go | 81 ---- src/common/models/base.go | 1 - src/controller/gc/callback.go | 22 ++ src/core/api/admin_job.go | 364 ------------------ src/core/api/config.go | 10 +- src/core/api/harborapi_test.go | 31 -- src/core/api/models/admin_job.go | 209 ---------- src/core/api/models/admin_job_test.go | 169 -------- src/core/api/models/execution.go | 15 - src/core/middlewares/middlewares_test.go | 2 - .../service/notifications/admin/handler.go | 117 ------ .../service/notifications/jobs/handler.go | 6 - src/server/route.go | 9 +- src/testing/apitests/apilib/admin_job.go | 35 -- src/testing/apitests/apilib/admin_job_req.go | 39 -- 18 files changed, 41 insertions(+), 1381 deletions(-) delete mode 100644 src/common/dao/admin_job.go delete mode 100644 src/common/dao/admin_job_test.go delete mode 100644 src/common/models/adminjob.go delete mode 100644 src/core/api/admin_job.go delete mode 100644 src/core/api/models/admin_job.go delete mode 100644 src/core/api/models/admin_job_test.go delete mode 100644 src/core/api/models/execution.go delete mode 100644 src/core/service/notifications/admin/handler.go delete mode 100644 src/testing/apitests/apilib/admin_job.go delete mode 100644 src/testing/apitests/apilib/admin_job_req.go diff --git a/make/migrations/postgresql/0050_2.2.0_schema.up.sql b/make/migrations/postgresql/0050_2.2.0_schema.up.sql index 38e742eb9..ad6c1bffb 100644 --- a/make/migrations/postgresql/0050_2.2.0_schema.up.sql +++ b/make/migrations/postgresql/0050_2.2.0_schema.up.sql @@ -1,5 +1,5 @@ /* -Fixes issue https://github.com/goharbor/harbor/issues/13317 +Fixes issue https://github.com/goharbor/harbor/issues/13317 Ensure the role_id of maintainer is 4 and the role_id of limited guest is 5 */ UPDATE role SET role_id=4 WHERE name='maintainer' AND role_id!=4; @@ -159,7 +159,7 @@ BEGIN ELSIF rep_exec.status = 'Succeed' THEN status = 'Success'; END IF; - + INSERT INTO execution (vendor_type, vendor_id, status, status_message, revision, trigger, start_time, end_time) VALUES ('REPLICATION', rep_exec.policy_id, status, rep_exec.status_text, 0, trigger, rep_exec.start_time, rep_exec.end_time) RETURNING id INTO new_exec_id; UPDATE replication_execution SET new_execution_id=new_exec_id WHERE id=rep_exec.id; @@ -455,6 +455,9 @@ BEGIN END LOOP; END $$; +/* admin_job no more needed, drop it */ +DROP TABLE IF EXISTS admin_job; + /*migrate robot_token_duration from minutes to days if exist*/ DO $$ DECLARE diff --git a/src/common/dao/admin_job.go b/src/common/dao/admin_job.go deleted file mode 100644 index a2b9e356d..000000000 --- a/src/common/dao/admin_job.go +++ /dev/null @@ -1,160 +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 dao - -import ( - "time" - - "github.com/astaxie/beego/orm" - "github.com/goharbor/harbor/src/common/models" - "github.com/goharbor/harbor/src/lib/log" -) - -// AddAdminJob ... -func AddAdminJob(job *models.AdminJob) (int64, error) { - o := GetOrmer() - if len(job.Status) == 0 { - job.Status = models.JobPending - } - sql := "insert into admin_job (job_name, job_parameters, job_kind, status, job_uuid, cron_str, creation_time, update_time) values (?, ?, ?, ?, ?, ?, ?, ?) RETURNING id" - var id int64 - now := time.Now() - err := o.Raw(sql, job.Name, job.Parameters, job.Kind, job.Status, job.UUID, job.Cron, now, now).QueryRow(&id) - if err != nil { - return 0, err - } - - return id, nil -} - -// GetAdminJob ... -func GetAdminJob(id int64) (*models.AdminJob, error) { - o := GetOrmer() - aj := models.AdminJob{ID: id} - err := o.Read(&aj) - if err == orm.ErrNoRows { - return nil, err - } - return &aj, nil -} - -// DeleteAdminJob ... -func DeleteAdminJob(id int64) error { - o := GetOrmer() - _, err := o.Raw(`update admin_job - set deleted = true where id = ?`, id).Exec() - return err -} - -// UpdateAdminJobStatus ... -func UpdateAdminJobStatus(id int64, status string, statusCode uint16, revision int64) error { - o := GetOrmer() - qt := o.QueryTable(&models.AdminJob{}) - - // The generated sql statement example:{ - // - // UPDATE "admin_job" SET "update_time" = $1, "status" = $2, "status_code" = $3, "revision" = $4 - // WHERE "id" IN ( SELECT T0."id" FROM "admin_job" T0 WHERE - // ( T0."revision" = $5 AND T0."status_code" < $6 ) OR ( T0."revision" < $7 ) - // AND T0."id" = $8 ) - // - // } - cond := orm.NewCondition() - c1 := cond.And("revision", revision).And("status_code__lt", statusCode) - c2 := cond.And("revision__lt", revision) - c := cond.AndCond(c1).OrCond(c2) - - data := make(orm.Params) - data["status"] = status - data["status_code"] = statusCode - data["revision"] = revision - data["update_time"] = time.Now() - - n, err := qt.SetCond(c).Filter("id", id).Update(data) - - if n == 0 { - log.Warningf("no records are updated when updating admin job %d", id) - } - - return err -} - -// SetAdminJobUUID ... -func SetAdminJobUUID(id int64, uuid string) error { - o := GetOrmer() - j := models.AdminJob{ - ID: id, - UUID: uuid, - } - n, err := o.Update(&j, "UUID") - if n == 0 { - log.Warningf("no records are updated when updating admin job %d", id) - } - return err -} - -// GetTop10AdminJobsOfName ... -func GetTop10AdminJobsOfName(name string) ([]*models.AdminJob, error) { - o := GetOrmer() - jobs := []*models.AdminJob{} - n, err := o.Raw(`select * from admin_job - where deleted = false and job_name = ? order by id desc limit 10`, name).QueryRows(&jobs) - if err != nil { - return nil, err - } - - if n == 0 { - return nil, nil - } - return jobs, err -} - -// GetAdminJobs get admin jobs bases on query conditions -func GetAdminJobs(query *models.AdminJobQuery) ([]*models.AdminJob, error) { - adjs := []*models.AdminJob{} - qs := adminQueryConditions(query) - if query.Size > 0 { - qs = qs.Limit(query.Size) - if query.Page > 0 { - qs = qs.Offset((query.Page - 1) * query.Size) - } - } - _, err := qs.All(&adjs) - return adjs, err -} - -// adminQueryConditions -func adminQueryConditions(query *models.AdminJobQuery) orm.QuerySeter { - qs := GetOrmer().QueryTable(&models.AdminJob{}) - - if query.ID > 0 { - qs = qs.Filter("ID", query.ID) - } - if len(query.Kind) > 0 { - qs = qs.Filter("Kind", query.Kind) - } - if len(query.Name) > 0 { - qs = qs.Filter("Name", query.Name) - } - if len(query.Status) > 0 { - qs = qs.Filter("Status", query.Status) - } - if len(query.UUID) > 0 { - qs = qs.Filter("UUID", query.UUID) - } - qs = qs.Filter("Deleted", false) - return qs.OrderBy("-ID") - -} diff --git a/src/common/dao/admin_job_test.go b/src/common/dao/admin_job_test.go deleted file mode 100644 index fecda5444..000000000 --- a/src/common/dao/admin_job_test.go +++ /dev/null @@ -1,145 +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 dao - -import ( - "fmt" - "testing" - - "github.com/goharbor/harbor/src/common/models" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" -) - -// AdminJobSuite is a test suite for testing admin job -type AdminJobSuite struct { - suite.Suite - - job0 *models.AdminJob - ids []int64 -} - -// TestAdminJob is the entry point of AdminJobSuite -func TestAdminJob(t *testing.T) { - suite.Run(t, &AdminJobSuite{}) -} - -// SetupSuite prepares testing env for the suite -func (suite *AdminJobSuite) SetupSuite() { - job := &models.AdminJob{ - Name: "job", - Kind: "jobKind", - } - - job0 := &models.AdminJob{ - Name: "GC", - Kind: "testKind", - Parameters: "{test:test}", - } - - suite.ids = make([]int64, 0) - - // add - id, err := AddAdminJob(job0) - require.NoError(suite.T(), err) - job0.ID = id - suite.job0 = job0 - suite.ids = append(suite.ids, id) - - id1, err := AddAdminJob(job) - require.NoError(suite.T(), err) - suite.ids = append(suite.ids, id1) -} - -// TearDownSuite cleans testing env -func (suite *AdminJobSuite) TearDownSuite() { - for _, id := range suite.ids { - err := DeleteAdminJob(id) - suite.NoError(err, fmt.Sprintf("clear admin job: %d", id)) - } -} - -// TestAdminJobBase ... -func (suite *AdminJobSuite) TestAdminJobBase() { - // get - job1, err := GetAdminJob(suite.job0.ID) - require.Nil(suite.T(), err) - suite.Equal(job1.ID, suite.job0.ID) - suite.Equal(job1.Name, suite.job0.Name) - suite.Equal(job1.Parameters, suite.job0.Parameters) - - // set uuid - err = SetAdminJobUUID(suite.job0.ID, "f5ef34f4cb3588d663176132") - require.Nil(suite.T(), err) - job3, err := GetAdminJob(suite.job0.ID) - require.Nil(suite.T(), err) - suite.Equal(job3.UUID, "f5ef34f4cb3588d663176132") - - // get admin jobs - query := &models.AdminJobQuery{ - Name: "job", - } - jobs, err := GetAdminJobs(query) - suite.Equal(len(jobs), 1) - - // get top 10 - jobs, _ = GetTop10AdminJobsOfName("job") - suite.Equal(len(jobs), 1) -} - -// TestAdminJobUpdateStatus ... -func (suite *AdminJobSuite) TestAdminJobUpdateStatus() { - // update status - err := UpdateAdminJobStatus(suite.job0.ID, "testStatus", 1, 10000) - require.Nil(suite.T(), err) - - job2, err := GetAdminJob(suite.job0.ID) - require.Nil(suite.T(), err) - suite.Equal(job2.Status, "testStatus") - - // Update status with same rev - err = UpdateAdminJobStatus(suite.job0.ID, "testStatus3", 3, 10000) - require.Nil(suite.T(), err) - - job3, err := GetAdminJob(suite.job0.ID) - require.Nil(suite.T(), err) - suite.Equal(job3.Status, "testStatus3") - - // Update status with same rev, previous status - err = UpdateAdminJobStatus(suite.job0.ID, "testStatus2", 2, 10000) - require.Nil(suite.T(), err) - - job4, err := GetAdminJob(suite.job0.ID) - require.Nil(suite.T(), err) - // No status change - suite.Equal(job4.Status, "testStatus3") - - // Update status with previous rev - err = UpdateAdminJobStatus(suite.job0.ID, "testStatus4", 4, 9999) - require.Nil(suite.T(), err) - - job5, err := GetAdminJob(suite.job0.ID) - require.Nil(suite.T(), err) - // No status change - suite.Equal(job5.Status, "testStatus3") - - // Update status with latest rev - err = UpdateAdminJobStatus(suite.job0.ID, "testStatus", 1, 10001) - require.Nil(suite.T(), err) - - job6, err := GetAdminJob(suite.job0.ID) - require.Nil(suite.T(), err) - suite.Equal(job6.Status, "testStatus") -} diff --git a/src/common/models/adminjob.go b/src/common/models/adminjob.go deleted file mode 100644 index 0cb192957..000000000 --- a/src/common/models/adminjob.go +++ /dev/null @@ -1,81 +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 models - -import ( - "fmt" - "time" -) - -const ( - // AdminJobTable is table name for admin job - AdminJobTable = "admin_job" -) - -// AdminJob ... -type AdminJob struct { - ID int64 `orm:"pk;auto;column(id)" json:"id"` - Name string `orm:"column(job_name)" json:"job_name"` - Kind string `orm:"column(job_kind)" json:"job_kind"` - Parameters string `orm:"column(job_parameters)" json:"job_parameters"` - Cron string `orm:"column(cron_str)" json:"cron_str"` - Status string `orm:"column(status)" json:"job_status"` - UUID string `orm:"column(job_uuid)" json:"-"` - Revision int64 `orm:"column(revision)" json:"-"` - StatusCode uint16 `orm:"column(status_code)" json:"-"` - Deleted bool `orm:"column(deleted)" json:"deleted"` - CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"` - UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"` -} - -// TableName is required by by beego orm to map AdminJob to table AdminJob -func (a *AdminJob) TableName() string { - return AdminJobTable -} - -// AdminJobQuery : query parameters for adminjob -type AdminJobQuery struct { - ID int64 - Name string - Kind string - Status string - UUID string - Deleted bool - Pagination -} - -// ScheduleParam ... -type ScheduleParam struct { - Type string `json:"type"` - Weekday int8 `json:"weekday"` - Offtime int64 `json:"offtime"` -} - -// ParseScheduleParamToCron ... -func ParseScheduleParamToCron(param *ScheduleParam) string { - if param == nil { - return "" - } - offtime := param.Offtime - offtime = offtime % (3600 * 24) - hour := int(offtime / 3600) - offtime = offtime % 3600 - minute := int(offtime / 60) - second := int(offtime % 60) - if param.Type == "Weekly" { - return fmt.Sprintf("%d %d %d * * %d", second, minute, hour, param.Weekday%7) - } - return fmt.Sprintf("%d %d %d * * *", second, minute, hour) -} diff --git a/src/common/models/base.go b/src/common/models/base.go index cc919da1c..654dfd7d2 100644 --- a/src/common/models/base.go +++ b/src/common/models/base.go @@ -29,7 +29,6 @@ func init() { new(Label), new(ResourceLabel), new(UserGroup), - new(AdminJob), new(JobLog), new(OIDCUser), new(NotificationPolicy), diff --git a/src/controller/gc/callback.go b/src/controller/gc/callback.go index d6d79fce5..63cf2958b 100644 --- a/src/controller/gc/callback.go +++ b/src/controller/gc/callback.go @@ -4,6 +4,10 @@ import ( "context" "encoding/json" "fmt" + + "github.com/goharbor/harbor/src/controller/quota" + "github.com/goharbor/harbor/src/core/config" + "github.com/goharbor/harbor/src/jobservice/job" "github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/lib/orm" "github.com/goharbor/harbor/src/pkg/scheduler" @@ -15,6 +19,14 @@ func init() { if err != nil { log.Fatalf("failed to registry GC call back, %v", err) } + + if err := task.RegisterTaskStatusChangePostFunc(job.ImageGC, gcTaskStatusChange); err != nil { + log.Fatalf("failed to register the task status change post for the gc job, error %v", err) + } + + if err := task.RegisterTaskStatusChangePostFunc(job.ImageGCReadOnly, gcTaskStatusChange); err != nil { + log.Fatalf("failed to register the task status change post for the gc readonly job, error %v", err) + } } func gcCallback(ctx context.Context, p string) error { @@ -25,3 +37,13 @@ func gcCallback(ctx context.Context, p string) error { _, err := Ctl.Start(orm.Context(), *param, task.ExecutionTriggerSchedule) return err } + +func gcTaskStatusChange(ctx context.Context, taskID int64, status string) error { + if status == job.SuccessStatus.String() && config.QuotaPerProjectEnable() { + go func() { + quota.RefreshForProjects(orm.Context()) + }() + } + + return nil +} diff --git a/src/core/api/admin_job.go b/src/core/api/admin_job.go deleted file mode 100644 index b0b959b94..000000000 --- a/src/core/api/admin_job.go +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright 2018 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 api - -import ( - "fmt" - "math" - "net/http" - "strconv" - "time" - - "github.com/goharbor/harbor/src/common/dao" - common_http "github.com/goharbor/harbor/src/common/http" - common_job "github.com/goharbor/harbor/src/common/job" - common_models "github.com/goharbor/harbor/src/common/models" - "github.com/goharbor/harbor/src/core/api/models" - utils_core "github.com/goharbor/harbor/src/core/utils" - "github.com/goharbor/harbor/src/lib/errors" - "github.com/goharbor/harbor/src/lib/log" -) - -// AJAPI manages the CRUD of admin job and its schedule, any API wants to handle manual and cron job like ScanAll and GC cloud reuse it. -type AJAPI struct { - BaseController -} - -// Prepare validates the URL and parms, it needs the system admin permission. -func (aj *AJAPI) Prepare() { - aj.BaseController.Prepare() -} - -// updateSchedule update a schedule of admin job. -func (aj *AJAPI) updateSchedule(ajr models.AdminJobReq) { - if ajr.Schedule.Type == models.ScheduleManual { - aj.SendInternalServerError((fmt.Errorf("fail to update admin job schedule as wrong schedule type: %s", ajr.Schedule.Type))) - return - } - - query := &common_models.AdminJobQuery{ - Name: ajr.Name, - Kind: common_job.JobKindPeriodic, - } - jobs, err := dao.GetAdminJobs(query) - if err != nil { - aj.SendInternalServerError(err) - return - } - if len(jobs) != 1 { - aj.SendInternalServerError(errors.New("fail to update admin job schedule as we found more than one schedule in system, please ensure that only one schedule left for your job")) - return - } - - // stop the scheduled job and remove it. - if err = utils_core.GetJobServiceClient().PostAction(jobs[0].UUID, common_job.JobActionStop); err != nil { - _, ok := err.(*common_job.StatusBehindError) - if !ok { - if e, ok := err.(*common_http.Error); !ok || e.Code != http.StatusNotFound { - aj.SendInternalServerError(err) - return - } - } - } - - if err = dao.DeleteAdminJob(jobs[0].ID); err != nil { - aj.SendInternalServerError(err) - return - } - - // Set schedule to None means to cancel the schedule, won't add new job. - if ajr.Schedule.Type != models.ScheduleNone { - aj.submit(&ajr) - } -} - -// get get a execution of admin job by ID -func (aj *AJAPI) get(id int64) { - jobs, err := dao.GetAdminJobs(&common_models.AdminJobQuery{ - ID: id, - }) - if err != nil { - aj.SendInternalServerError(fmt.Errorf("failed to get admin jobs: %v", err)) - return - } - if len(jobs) == 0 { - aj.SendNotFoundError(errors.New("no admin job found")) - return - } - - adminJobRep, err := convertToAdminJobRep(jobs[0]) - if err != nil { - aj.SendInternalServerError(fmt.Errorf("failed to convert admin job response: %v", err)) - return - } - - aj.Data["json"] = adminJobRep - aj.ServeJSON() -} - -// list list all executions of admin job by name -func (aj *AJAPI) list(name string) { - jobs, err := dao.GetTop10AdminJobsOfName(name) - if err != nil { - aj.SendInternalServerError(fmt.Errorf("failed to get admin jobs: %v", err)) - return - } - - AdminJobReps := []*models.AdminJobRep{} - for _, job := range jobs { - AdminJobRep, err := convertToAdminJobRep(job) - if err != nil { - aj.SendInternalServerError(fmt.Errorf("failed to convert admin job response: %v", err)) - return - } - AdminJobReps = append(AdminJobReps, &AdminJobRep) - } - - aj.Data["json"] = AdminJobReps - aj.ServeJSON() -} - -// getSchedule gets admin job schedule ... -func (aj *AJAPI) getSchedule(name string) { - result := models.AdminJobRep{} - - jobs, err := dao.GetAdminJobs(&common_models.AdminJobQuery{ - Name: name, - Kind: common_job.JobKindPeriodic, - }) - if err != nil { - aj.SendInternalServerError(fmt.Errorf("failed to get admin jobs: %v", err)) - return - } - if len(jobs) > 1 { - aj.SendInternalServerError(errors.New("get more than one scheduled admin job, make sure there has only one")) - return - } - - if len(jobs) != 0 { - adminJobRep, err := convertToAdminJobRep(jobs[0]) - if err != nil { - aj.SendInternalServerError(fmt.Errorf("failed to convert admin job response: %v", err)) - return - } - result.Schedule = adminJobRep.Schedule - result.Parameters = adminJobRep.Parameters - } - - aj.Data["json"] = result - aj.ServeJSON() -} - -// getLog ... -func (aj *AJAPI) getLog(id int64) { - job, err := dao.GetAdminJob(id) - if err != nil { - log.Errorf("Failed to load job data for job: %d, error: %v", id, err) - aj.SendInternalServerError(errors.New("Failed to get Job data")) - return - } - if job == nil { - log.Errorf("Failed to get admin job: %d", id) - aj.SendNotFoundError(errors.New("Failed to get Job")) - return - } - - var jobID string - // to get the latest execution job id, then to query job log. - if job.Kind == common_job.JobKindPeriodic { - exes, err := utils_core.GetJobServiceClient().GetExecutions(job.UUID) - if err != nil { - aj.SendInternalServerError(err) - return - } - if len(exes) == 0 { - aj.SendNotFoundError(errors.New("no execution log found")) - return - } - // get the latest terminal status execution. - for _, exe := range exes { - if exe.Info.Status == "Error" || exe.Info.Status == "Success" { - jobID = exe.Info.JobID - break - } - } - // no execution found - if jobID == "" { - aj.SendNotFoundError(errors.New("no execution log found")) - return - } - - } else { - jobID = job.UUID - } - - logBytes, err := utils_core.GetJobServiceClient().GetJobLog(jobID) - if err != nil { - if httpErr, ok := err.(*common_http.Error); ok { - aj.RenderError(httpErr.Code, "") - log.Errorf(fmt.Sprintf("failed to get log of job %d: %d %s", - id, httpErr.Code, httpErr.Message)) - return - } - aj.SendInternalServerError(fmt.Errorf("Failed to get job logs, uuid: %s, error: %v", job.UUID, err)) - return - } - aj.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Length"), strconv.Itoa(len(logBytes))) - aj.Ctx.ResponseWriter.Header().Set(http.CanonicalHeaderKey("Content-Type"), "text/plain") - _, err = aj.Ctx.ResponseWriter.Write(logBytes) - if err != nil { - aj.SendInternalServerError(fmt.Errorf("Failed to write job logs, uuid: %s, error: %v", job.UUID, err)) - } -} - -// submit submits a job to job service per request -func (aj *AJAPI) submit(ajr *models.AdminJobReq) { - // when the schedule is saved as None without any schedule, just return 200 and do nothing. - if ajr.Schedule == nil || ajr.Schedule.Type == models.ScheduleNone { - return - } - - // cannot post multiple schedule for admin job. - if ajr.IsPeriodic() { - jobs, err := dao.GetAdminJobs(&common_models.AdminJobQuery{ - Name: ajr.Name, - Kind: common_job.JobKindPeriodic, - }) - if err != nil { - aj.SendInternalServerError(fmt.Errorf("failed to get admin jobs: %v", err)) - return - } - if len(jobs) != 0 { - aj.SendPreconditionFailedError(errors.New("fail to set schedule for admin job as always had one, please delete it firstly then to re-schedule")) - return - } - } else { - // So far, it should be a generic job for the manually trigger case. - // Only needs to care the 1st generic job. - // Check if there are still ongoing scan jobs triggered by the previous admin job. - // TODO: REPLACE WITH TASK MANAGER METHODS IN FUTURE - jb, err := aj.getLatestAdminJob(ajr.Name, common_job.JobKindGeneric) - if err != nil { - aj.SendInternalServerError(errors.Wrap(err, "AJAPI")) - return - } - - if jb != nil { - // With a reasonable timeout duration - if jb.UpdateTime.Add(2 * time.Hour).After(time.Now()) { - if isOnGoing(jb.Status) { - err := errors.Errorf("reject job submitting: job %s with ID %d is %s", jb.Name, jb.ID, jb.Status) - aj.SendConflictError(errors.Wrap(err, "submit : AJAPI")) - return - } - } - } - } - - id, err := dao.AddAdminJob(&common_models.AdminJob{ - Name: ajr.Name, - Kind: ajr.JobKind(), - Cron: ajr.CronString(), - Parameters: ajr.ParamString(), - }) - if err != nil { - aj.SendInternalServerError(err) - return - } - ajr.ID = id - job := ajr.ToJob() - - // submit job to job service - log.Debugf("submitting admin job to job service") - uuid, err := utils_core.GetJobServiceClient().SubmitJob(job) - if err != nil { - if err := dao.DeleteAdminJob(id); err != nil { - log.Debugf("Failed to delete admin job, err: %v", err) - } - aj.ParseAndHandleError("failed to submit admin job", err) - return - } - if err := dao.SetAdminJobUUID(id, uuid); err != nil { - aj.SendInternalServerError(err) - return - } -} - -func (aj *AJAPI) getLatestAdminJob(name, kind string) (*common_models.AdminJob, error) { - query := &common_models.AdminJobQuery{ - Name: name, - Kind: kind, - } - query.Size = 1 - query.Page = 1 - - jbs, err := dao.GetAdminJobs(query) - - if err != nil { - return nil, err - } - - if len(jbs) == 0 { - // Not exist - return nil, nil - } - - // Return the latest one (with biggest ID) - return jbs[0], nil -} - -func convertToAdminJobRep(job *common_models.AdminJob) (models.AdminJobRep, error) { - if job == nil { - return models.AdminJobRep{}, nil - } - - AdminJobRep := models.AdminJobRep{ - ID: job.ID, - Name: job.Name, - Kind: job.Kind, - Status: job.Status, - Parameters: job.Parameters, - CreationTime: job.CreationTime, - UpdateTime: job.UpdateTime, - } - - if len(job.Cron) > 0 { - schedule, err := models.ConvertSchedule(job.Cron) - if err != nil { - return models.AdminJobRep{}, err - } - AdminJobRep.Schedule = &schedule - } - return AdminJobRep, nil -} - -func progress(completed, total uint) string { - if total == 0 { - return fmt.Sprintf("0%s", "%") - } - - v := float64(completed) - vv := float64(total) - - p := (int)(math.Round((v / vv) * 100)) - - return fmt.Sprintf("%d%s", p, "%") -} - -func isOnGoing(status string) bool { - return status == common_models.JobRunning || - status == common_models.JobScheduled || - status == common_models.JobPending -} diff --git a/src/core/api/config.go b/src/core/api/config.go index f5451ade8..b16a7a124 100644 --- a/src/core/api/config.go +++ b/src/core/api/config.go @@ -24,7 +24,6 @@ import ( "github.com/goharbor/harbor/src/common/config/metadata" "github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/common/security" - "github.com/goharbor/harbor/src/core/api/models" corecfg "github.com/goharbor/harbor/src/core/config" "github.com/goharbor/harbor/src/lib/log" ) @@ -148,6 +147,13 @@ func checkUnmodifiable(mgr *config.CfgManager, cfgs map[string]interface{}, keys return } +// ScanAllPolicy is represent the json request and object for scan all policy +// Only for migrating from the legacy schedule. +type ScanAllPolicy struct { + Type string `json:"type"` + Param map[string]interface{} `json:"parameter,omitempty"` +} + // delete sensitive attrs and add editable field to every attr func convertForGet(cfg map[string]interface{}) (map[string]*value, error) { result := map[string]*value{} @@ -161,7 +167,7 @@ func convertForGet(cfg map[string]interface{}) (map[string]*value, error) { } if _, ok := cfg[common.ScanAllPolicy]; !ok { - cfg[common.ScanAllPolicy] = models.ScanAllPolicy{ + cfg[common.ScanAllPolicy] = ScanAllPolicy{ Type: "none", // For legacy compatible } } diff --git a/src/core/api/harborapi_test.go b/src/core/api/harborapi_test.go index c392a4f6a..cf3e93d06 100644 --- a/src/core/api/harborapi_test.go +++ b/src/core/api/harborapi_test.go @@ -35,7 +35,6 @@ import ( "github.com/goharbor/harbor/src/common/job/test" "github.com/goharbor/harbor/src/common/models" testutils "github.com/goharbor/harbor/src/common/utils/test" - api_models "github.com/goharbor/harbor/src/core/api/models" apimodels "github.com/goharbor/harbor/src/core/api/models" _ "github.com/goharbor/harbor/src/core/auth/db" _ "github.com/goharbor/harbor/src/core/auth/ldap" @@ -860,36 +859,6 @@ func (a testapi) DeleteMeta(authInfor usrInfo, projectID int64, name string) (in return code, string(body), err } -func (a testapi) AddScanAll(authInfor usrInfo, adminReq apilib.AdminJobReq) (int, error) { - _sling := sling.New().Post(a.basePath) - - path := "/api/system/scanAll/schedule" - - _sling = _sling.Path(path) - - // body params - _sling = _sling.BodyJSON(adminReq) - var httpStatusCode int - var err error - - httpStatusCode, _, err = request(_sling, jsonAcceptHeader, authInfor) - - return httpStatusCode, err -} - -func (a testapi) ScanAllScheduleGet(authInfo usrInfo) (int, api_models.AdminJobSchedule, error) { - _sling := sling.New().Get(a.basePath) - path := "/api/system/scanAll/schedule" - _sling = _sling.Path(path) - httpStatusCode, body, err := request(_sling, jsonAcceptHeader, authInfo) - var successPayLoad api_models.AdminJobSchedule - if 200 == httpStatusCode && nil == err { - err = json.Unmarshal(body, &successPayLoad) - } - - return httpStatusCode, successPayLoad, err -} - func (a testapi) RegistryGet(authInfo usrInfo, registryID int64) (*model.Registry, int, error) { _sling := sling.New().Base(a.basePath).Get(fmt.Sprintf("/api/registries/%d", registryID)) code, body, err := request(_sling, jsonAcceptHeader, authInfo) diff --git a/src/core/api/models/admin_job.go b/src/core/api/models/admin_job.go deleted file mode 100644 index 62e69926a..000000000 --- a/src/core/api/models/admin_job.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2018 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 models - -import ( - "encoding/json" - "fmt" - "strings" - "time" - - "github.com/astaxie/beego/validation" - "github.com/goharbor/harbor/src/common/job" - "github.com/goharbor/harbor/src/common/job/models" - common_models "github.com/goharbor/harbor/src/common/models" - common_utils "github.com/goharbor/harbor/src/common/utils" - "github.com/goharbor/harbor/src/core/config" - "github.com/goharbor/harbor/src/lib/log" - "github.com/robfig/cron" -) - -const ( - // ScheduleHourly : 'Hourly' - ScheduleHourly = "Hourly" - // ScheduleDaily : 'Daily' - ScheduleDaily = "Daily" - // ScheduleWeekly : 'Weekly' - ScheduleWeekly = "Weekly" - // ScheduleCustom : 'Custom' - ScheduleCustom = "Custom" - // ScheduleManual : 'Manual' - ScheduleManual = "Manual" - // ScheduleNone : 'None' - ScheduleNone = "None" -) - -// AdminJobReq holds request information for admin job -type AdminJobReq struct { - AdminJobSchedule - Name string `json:"name"` - Status string `json:"status"` - ID int64 `json:"id"` - Parameters map[string]interface{} `json:"parameters"` -} - -// AdminJobSchedule ... -type AdminJobSchedule struct { - Schedule *ScheduleParam `json:"schedule"` -} - -// ScheduleParam defines the parameter of schedule trigger -type ScheduleParam struct { - // Daily, Weekly, Custom, Manual, None - Type string `json:"type"` - // The cron string of scheduled job - Cron string `json:"cron"` -} - -// AdminJobRep holds the response of query admin job -type AdminJobRep struct { - AdminJobSchedule - ID int64 `json:"id"` - Name string `json:"job_name"` - Kind string `json:"job_kind"` - Parameters string `json:"job_parameters"` - Status string `json:"job_status"` - UUID string `json:"-"` - Deleted bool `json:"deleted"` - CreationTime time.Time `json:"creation_time"` - UpdateTime time.Time `json:"update_time"` -} - -// Valid validates the schedule type of a admin job request. -// Only scheduleHourly, ScheduleDaily, ScheduleWeekly, ScheduleCustom, ScheduleManual, ScheduleNone are accepted. -func (ar *AdminJobReq) Valid(v *validation.Validation) { - if ar.Schedule == nil { - return - } - switch ar.Schedule.Type { - case ScheduleHourly, ScheduleDaily, ScheduleWeekly, ScheduleCustom: - if _, err := cron.Parse(ar.Schedule.Cron); err != nil { - v.SetError("cron", fmt.Sprintf("Invalid schedule trigger parameter cron: %s", ar.Schedule.Cron)) - } - case ScheduleManual, ScheduleNone: - default: - v.SetError("kind", fmt.Sprintf("Invalid schedule kind: %s", ar.Schedule.Type)) - } -} - -// ToJob converts request to a job recognized by job service. -func (ar *AdminJobReq) ToJob() *models.JobData { - metadata := &models.JobMetadata{ - JobKind: ar.JobKind(), - Cron: ar.Schedule.Cron, - // GC job must be unique ... - IsUnique: true, - } - - jobData := &models.JobData{ - Name: ar.Name, - Parameters: ar.Parameters, - Metadata: metadata, - StatusHook: fmt.Sprintf("%s/service/notifications/jobs/adminjob/%d", - config.InternalCoreURL(), ar.ID), - } - - // Append admin job ID as job parameter - if jobData.Parameters == nil { - jobData.Parameters = make(models.Parameters) - } - // As string - jobData.Parameters["admin_job_id"] = fmt.Sprintf("%d", ar.ID) - - return jobData -} - -// IsPeriodic ... -func (ar *AdminJobReq) IsPeriodic() bool { - return ar.JobKind() == job.JobKindPeriodic -} - -// JobKind ... -func (ar *AdminJobReq) JobKind() string { - switch ar.Schedule.Type { - case ScheduleHourly, ScheduleDaily, ScheduleWeekly, ScheduleCustom: - return job.JobKindPeriodic - case ScheduleManual: - return job.JobKindGeneric - default: - return "" - } -} - -// CronString ... -func (ar *AdminJobReq) CronString() string { - str, err := json.Marshal(ar.Schedule) - if err != nil { - log.Debugf("failed to marshal json error, %v", err) - return "" - } - return string(str) -} - -// ParamString ... -func (ar *AdminJobReq) ParamString() string { - str, err := json.Marshal(ar.Parameters) - if err != nil { - log.Debugf("failed to marshal json error, %v", err) - return "" - } - return string(str) -} - -// ConvertSchedule converts different kinds of cron string into one standard for UI to show. -// in the latest design, it uses {"type":"Daily","cron":"0 0 0 * * *"} as the cron item. -// As for supporting migration from older version, it needs to convert {"parameter":{"daily_time":0},"type":"daily"} -// and {"type":"Daily","weekday":0,"offtime":57600} into one standard. -func ConvertSchedule(cronStr string) (ScheduleParam, error) { - if cronStr == "" { - return ScheduleParam{}, nil - } - - convertedSchedule := ScheduleParam{} - convertedSchedule.Type = "custom" - - if strings.Contains(cronStr, "parameter") { - scheduleModel := ScanAllPolicy{} - if err := json.Unmarshal([]byte(cronStr), &scheduleModel); err != nil { - return ScheduleParam{}, err - } - h, m, s := common_utils.ParseOfftime(int64(scheduleModel.Param["daily_time"].(float64))) - cron := fmt.Sprintf("%d %d %d * * *", s, m, h) - convertedSchedule.Cron = cron - return convertedSchedule, nil - } else if strings.Contains(cronStr, "offtime") { - scheduleModel := common_models.ScheduleParam{} - if err := json.Unmarshal([]byte(cronStr), &scheduleModel); err != nil { - return ScheduleParam{}, err - } - convertedSchedule.Cron = common_models.ParseScheduleParamToCron(&scheduleModel) - return convertedSchedule, nil - } else if strings.Contains(cronStr, "cron") { - scheduleModel := ScheduleParam{} - if err := json.Unmarshal([]byte(cronStr), &scheduleModel); err != nil { - return ScheduleParam{}, err - } - return scheduleModel, nil - } - - return ScheduleParam{}, fmt.Errorf("unsupported cron format, %s", cronStr) -} - -// ScanAllPolicy is represent the json request and object for scan all policy -// Only for migrating from the legacy schedule. -type ScanAllPolicy struct { - Type string `json:"type"` - Param map[string]interface{} `json:"parameter,omitempty"` -} diff --git a/src/core/api/models/admin_job_test.go b/src/core/api/models/admin_job_test.go deleted file mode 100644 index 458435d59..000000000 --- a/src/core/api/models/admin_job_test.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2018 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 models - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/goharbor/harbor/src/common" - common_job "github.com/goharbor/harbor/src/common/job" - "github.com/goharbor/harbor/src/common/utils/test" - "github.com/goharbor/harbor/src/core/config" - "os" - "strings" -) - -var testConfig = map[string]interface{}{ - common.DefaultCoreEndpoint: "test", -} - -func TestMain(m *testing.M) { - - test.InitDatabaseFromEnv() - config.Init() - config.Upload(testConfig) - os.Exit(m.Run()) - -} - -func TestToJob(t *testing.T) { - - adminJobSchedule := AdminJobSchedule{ - Schedule: &ScheduleParam{ - Type: "Daily", - Cron: "20 3 0 * * *", - }, - } - - adminjob := &AdminJobReq{ - Name: common_job.ImageGC, - AdminJobSchedule: adminJobSchedule, - } - - job := adminjob.ToJob() - assert.Equal(t, job.Name, "IMAGE_GC") - assert.Equal(t, job.Metadata.JobKind, common_job.JobKindPeriodic) - assert.Equal(t, job.Metadata.Cron, "20 3 0 * * *") -} - -func TestToJobManual(t *testing.T) { - - adminJobSchedule := AdminJobSchedule{ - Schedule: &ScheduleParam{ - Type: "Manual", - }, - } - - adminjob := &AdminJobReq{ - AdminJobSchedule: adminJobSchedule, - Name: common_job.ImageGC, - } - - job := adminjob.ToJob() - assert.Equal(t, job.Name, "IMAGE_GC") - assert.Equal(t, job.Metadata.JobKind, common_job.JobKindGeneric) -} - -func TestIsPeriodic(t *testing.T) { - - adminJobSchedule := AdminJobSchedule{ - Schedule: &ScheduleParam{ - Type: "Daily", - Cron: "20 3 0 * * *", - }, - } - - adminjob := &AdminJobReq{ - AdminJobSchedule: adminJobSchedule, - } - - isPeriodic := adminjob.IsPeriodic() - assert.Equal(t, isPeriodic, true) -} - -func TestJobKind(t *testing.T) { - - adminJobSchedule := AdminJobSchedule{ - Schedule: &ScheduleParam{ - Type: "Daily", - Cron: "20 3 0 * * *", - }, - } - - adminjob := &AdminJobReq{ - AdminJobSchedule: adminJobSchedule, - } - - kind := adminjob.JobKind() - assert.Equal(t, kind, "Periodic") - - adminJobSchedule1 := AdminJobSchedule{ - Schedule: &ScheduleParam{ - Type: "Manual", - }, - } - adminjob1 := &AdminJobReq{ - AdminJobSchedule: adminJobSchedule1, - } - kind1 := adminjob1.JobKind() - assert.Equal(t, kind1, "Generic") -} - -func TestCronString(t *testing.T) { - - adminJobSchedule := AdminJobSchedule{ - Schedule: &ScheduleParam{ - Type: "Daily", - Cron: "20 3 0 * * *", - }, - } - - adminjob := &AdminJobReq{ - AdminJobSchedule: adminJobSchedule, - } - cronStr := adminjob.CronString() - assert.True(t, strings.EqualFold(cronStr, "{\"type\":\"Daily\",\"Cron\":\"20 3 0 * * *\"}")) -} - -func TestParamString(t *testing.T) { - adminJobPara := make(map[string]interface{}) - adminJobPara["key1"] = "value1" - adminJobPara["key2"] = true - adminJobPara["key3"] = 88 - - adminjob := &AdminJobReq{ - Parameters: adminJobPara, - } - paramStr := adminjob.ParamString() - assert.True(t, strings.EqualFold(paramStr, "{\"key1\":\"value1\",\"key2\":true,\"key3\":88}")) -} - -func TestConvertSchedule(t *testing.T) { - schedule1 := "{\"type\":\"Daily\",\"cron\":\"20 3 0 * * *\"}" - converted1, err1 := ConvertSchedule(schedule1) - assert.Nil(t, err1) - assert.Equal(t, converted1.Cron, "20 3 0 * * *") - - schedule2 := "{\"type\":\"Daily\",\"weekday\":0,\"offtime\":57720}" - converted2, err2 := ConvertSchedule(schedule2) - assert.Nil(t, err2) - assert.Equal(t, converted2.Cron, "0 2 16 * * *") - - schedule3 := "{\"parameter\":{\"daily_time\":57720},\"type\":\"daily\"}" - converted3, err3 := ConvertSchedule(schedule3) - assert.Nil(t, err3) - assert.Equal(t, converted3.Cron, "0 2 16 * * *") -} diff --git a/src/core/api/models/execution.go b/src/core/api/models/execution.go deleted file mode 100644 index e5445c27f..000000000 --- a/src/core/api/models/execution.go +++ /dev/null @@ -1,15 +0,0 @@ -package models - -import ( - "time" -) - -// Execution defines the data model used in API level -type Execution struct { - ID int64 `json:"id"` - Status string `json:"status"` - TriggerMode string `json:"trigger_mode"` - Duration int `json:"duration"` - SuccessRate string `json:"success_rate"` - StartTime time.Time `json:"start_time"` -} diff --git a/src/core/middlewares/middlewares_test.go b/src/core/middlewares/middlewares_test.go index 355dcd1a0..4909acc49 100644 --- a/src/core/middlewares/middlewares_test.go +++ b/src/core/middlewares/middlewares_test.go @@ -33,8 +33,6 @@ func Test_readonlySkipper(t *testing.T) { {"login get", args{httptest.NewRequest(http.MethodGet, "/c/login", nil)}, false}, {"onboard", args{httptest.NewRequest(http.MethodPost, "/c/oidc/onboard", nil)}, true}, {"user exist", args{httptest.NewRequest(http.MethodPost, "/c/userExists", nil)}, true}, - {"user exist", args{httptest.NewRequest(http.MethodPost, "/service/notifications/jobs/adminjob/123456", nil)}, true}, - {"user exist", args{httptest.NewRequest(http.MethodPost, "/service/notifications/jobs/adminjob/abcdefg", nil)}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/src/core/service/notifications/admin/handler.go b/src/core/service/notifications/admin/handler.go deleted file mode 100644 index 9e192d71a..000000000 --- a/src/core/service/notifications/admin/handler.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2018 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 admin - -import ( - "context" - "encoding/json" - - o "github.com/astaxie/beego/orm" - "github.com/goharbor/harbor/src/common/dao" - "github.com/goharbor/harbor/src/common/job" - job_model "github.com/goharbor/harbor/src/common/job/models" - "github.com/goharbor/harbor/src/common/models" - "github.com/goharbor/harbor/src/controller/quota" - "github.com/goharbor/harbor/src/core/config" - "github.com/goharbor/harbor/src/core/service/notifications" - j "github.com/goharbor/harbor/src/jobservice/job" - "github.com/goharbor/harbor/src/lib/log" - "github.com/goharbor/harbor/src/lib/orm" -) - -var statusMap = map[string]string{ - job.JobServiceStatusPending: models.JobPending, - job.JobServiceStatusRunning: models.JobRunning, - job.JobServiceStatusStopped: models.JobStopped, - job.JobServiceStatusCancelled: models.JobCanceled, - job.JobServiceStatusError: models.JobError, - job.JobServiceStatusSuccess: models.JobFinished, - job.JobServiceStatusScheduled: models.JobScheduled, -} - -// Handler handles request on /service/notifications/jobs/adminjob/*, which listens to the webhook of jobservice. -type Handler struct { - notifications.BaseHandler - id int64 - UUID string - status string - UpstreamJobID string - revision int64 - jobName string - checkIn string - statusCode uint16 -} - -// Prepare ... -func (h *Handler) Prepare() { - h.BaseHandler.Prepare() - var data job_model.JobStatusChange - err := json.Unmarshal(h.Ctx.Input.CopyBody(1<<32), &data) - if err != nil { - log.Errorf("Failed to decode job status change, error: %v", err) - h.Abort("200") - return - } - id, err := h.GetInt64FromPath(":id") - if err != nil { - log.Errorf("Failed to get job ID, error: %v", err) - // Avoid job service from resending... - h.Abort("200") - return - } - h.id = id - // UpstreamJobID is the periodic job id - if data.Metadata.UpstreamJobID != "" { - h.UUID = data.Metadata.UpstreamJobID - } else { - h.UUID = data.JobID - } - - status, ok := statusMap[data.Status] - if !ok { - log.Infof("drop the job status update event: job id-%d, status-%s", h.id, status) - h.Abort("200") - return - } - h.statusCode = (uint16)(j.Status(data.Status).Code()) - h.status = status - h.revision = data.Metadata.Revision - h.jobName = data.Metadata.JobName - h.checkIn = data.CheckIn -} - -// HandleAdminJob handles the webhook of admin jobs -func (h *Handler) HandleAdminJob() { - log.Infof("received admin job status update event: job-%d, job_uuid-%s, status-%s, revision-%d", h.id, h.UUID, h.status, h.revision) - - // create the mapping relationship between the jobs in database and jobservice - if err := dao.SetAdminJobUUID(h.id, h.UUID); err != nil { - h.SendInternalServerError(err) - return - } - if err := dao.UpdateAdminJobStatus(h.id, h.status, h.statusCode, h.revision); err != nil { - log.Errorf("Failed to update job status, id: %d, status: %s", h.id, h.status) - h.SendInternalServerError(err) - return - } - - if h.jobName == job.ImageGC && h.status == models.JobFinished { - go func() { - if config.QuotaPerProjectEnable() { - quota.RefreshForProjects(orm.NewContext(context.TODO(), o.NewOrm())) - } - }() - } -} diff --git a/src/core/service/notifications/jobs/handler.go b/src/core/service/notifications/jobs/handler.go index 92538710c..eb43562e3 100755 --- a/src/core/service/notifications/jobs/handler.go +++ b/src/core/service/notifications/jobs/handler.go @@ -89,12 +89,6 @@ func (h *Handler) Prepare() { } } -// HandleScan handles the webhook of scan job -func (h *Handler) HandleScan() { - // legacy handler for the scan job - return -} - // HandleRetentionTask handles the webhook of retention task func (h *Handler) HandleRetentionTask() { taskID := h.id diff --git a/src/server/route.go b/src/server/route.go index 25713a2b9..82a212838 100644 --- a/src/server/route.go +++ b/src/server/route.go @@ -22,13 +22,16 @@ import ( "github.com/goharbor/harbor/src/core/api" "github.com/goharbor/harbor/src/core/config" "github.com/goharbor/harbor/src/core/controllers" - "github.com/goharbor/harbor/src/core/service/notifications/admin" "github.com/goharbor/harbor/src/core/service/notifications/jobs" "github.com/goharbor/harbor/src/core/service/token" "github.com/goharbor/harbor/src/server/handler" "github.com/goharbor/harbor/src/server/router" ) +func ignoreNotification(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + func registerRoutes() { // API version router.NewRoute().Method(http.MethodGet).Path("/api/version").HandlerFunc(GetAPIVersion) @@ -46,10 +49,10 @@ func registerRoutes() { beego.Router("/api/internal/switchquota", &api.InternalAPI{}, "put:SwitchQuota") beego.Router("/api/internal/syncquota", &api.InternalAPI{}, "post:SyncQuota") - beego.Router("/service/notifications/jobs/adminjob/:id([0-9]+)", &admin.Handler{}, "post:HandleAdminJob") beego.Router("/service/notifications/jobs/webhook/:id([0-9]+)", &jobs.Handler{}, "post:HandleNotificationJob") beego.Router("/service/notifications/jobs/retention/task/:id([0-9]+)", &jobs.Handler{}, "post:HandleRetentionTask") - beego.Router("/service/notifications/jobs/scan/:uuid", &jobs.Handler{}, "post:HandleScan") + router.NewRoute().Method(http.MethodPost).Path("/service/notifications/jobs/adminjob/:id([0-9]+)").Handler(handler.NewJobStatusHandler()) // legacy job status hook endpoint for adminjob + router.NewRoute().Method(http.MethodPost).Path("/service/notifications/jobs/scan/:uuid").HandlerFunc(ignoreNotification) // ignore legacy scan job notifaction router.NewRoute().Method(http.MethodPost).Path("/service/notifications/schedules/:id([0-9]+)").Handler(handler.NewJobStatusHandler()) // legacy job status hook endpoint for scheduler router.NewRoute().Method(http.MethodPost).Path("/service/notifications/jobs/replication/:id([0-9]+)").Handler(handler.NewJobStatusHandler()) // legacy job status hook endpoint for replication scheduler router.NewRoute().Method(http.MethodPost).Path("/service/notifications/jobs/replication/task/:id([0-9]+)").Handler(handler.NewJobStatusHandler()) // legacy job status hook endpoint for replication task diff --git a/src/testing/apitests/apilib/admin_job.go b/src/testing/apitests/apilib/admin_job.go deleted file mode 100644 index 0bfe7bd83..000000000 --- a/src/testing/apitests/apilib/admin_job.go +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Harbor API - * - * These APIs provide services for manipulating Harbor project. - * - * OpenAPI spec version: 0.3.0 - * - * Generated by: https://github.com/swagger-api/swagger-codegen.git - * - * 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 apilib - -// AdminJob ... -type AdminJob struct { - ID int64 `json:"id,omitempty"` - Name string `json:"job_name,omitempty"` - Kind string `json:"job_kind,omitempty"` - Status string `json:"job_status,omitempty"` - UUID string `json:"uuid,omitempty"` - Deleted bool `json:"deleted,omitempty"` - CreationTime string `json:"creation_time,omitempty"` - UpdateTime string `json:"update_time,omitempty"` -} diff --git a/src/testing/apitests/apilib/admin_job_req.go b/src/testing/apitests/apilib/admin_job_req.go deleted file mode 100644 index 740126820..000000000 --- a/src/testing/apitests/apilib/admin_job_req.go +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Harbor API - * - * These APIs provide services for manipulating Harbor project. - * - * OpenAPI spec version: 0.3.0 - * - * Generated by: https://github.com/swagger-api/swagger-codegen.git - * - * 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 apilib - -// AdminJobReq holds request information for admin job -type AdminJobReq struct { - Schedule *ScheduleParam `json:"schedule,omitempty"` - Name string `json:"name"` - Status string `json:"status,omitempty"` - ID int64 `json:"id,omitempty"` - Parameters map[string]interface{} `json:"parameters"` -} - -// ScheduleParam ... -type ScheduleParam struct { - Type string `json:"type,omitempty"` - Weekday int8 `json:"Weekday,omitempty"` - Offtime int64 `json:"Offtime,omitempty"` -}