Merge pull request #16865 from stonezdj/22may17_purge_audit_log_rest_api

Add REST API for purge audit log
This commit is contained in:
stonezdj(Daojun Zhang) 2022-05-25 10:08:47 +08:00 committed by GitHub
commit 4637af8866
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 945 additions and 6 deletions

View File

@ -4174,6 +4174,170 @@ paths:
$ref: '#/responses/403'
'500':
$ref: '#/responses/500'
/system/purgeaudit:
get:
summary: Get purge job results.
description: get purge job execution history.
tags:
- purge
operationId: getPurgeHistory
parameters:
- $ref: '#/parameters/requestId'
- $ref: '#/parameters/query'
- $ref: '#/parameters/sort'
- $ref: '#/parameters/page'
- $ref: '#/parameters/pageSize'
responses:
'200':
description: Get purge job results successfully.
headers:
X-Total-Count:
description: The total count of history
type: integer
Link:
description: Link refers to the previous page and next page
type: string
schema:
type: array
items:
$ref: '#/definitions/ExecHistory'
'401':
$ref: '#/responses/401'
'403':
$ref: '#/responses/403'
'500':
$ref: '#/responses/500'
/system/purgeaudit/{purge_id}:
get:
summary: Get purge job status.
description: This endpoint let user get purge job status filtered by specific ID.
operationId: getPurgeJob
parameters:
- $ref: '#/parameters/requestId'
- $ref: '#/parameters/purgeId'
tags:
- purge
responses:
'200':
description: Get purge job results successfully.
schema:
$ref: '#/definitions/ExecHistory'
'401':
$ref: '#/responses/401'
'403':
$ref: '#/responses/403'
'404':
$ref: '#/responses/404'
'500':
$ref: '#/responses/500'
/system/purgeaudit/{purge_id}/log:
get:
summary: Get purge job log.
description: This endpoint let user get purge job logs filtered by specific ID.
operationId: getPurgeJobLog
parameters:
- $ref: '#/parameters/requestId'
- $ref: '#/parameters/purgeId'
tags:
- purge
produces:
- text/plain
responses:
'200':
description: Get successfully.
schema:
type: string
'400':
$ref: '#/responses/400'
'401':
$ref: '#/responses/401'
'403':
$ref: '#/responses/403'
'404':
$ref: '#/responses/404'
'500':
$ref: '#/responses/500'
/system/purgeaudit/schedule:
get:
summary: Get purge's schedule.
description: This endpoint is for get schedule of purge job.
operationId: getPurgeSchedule
tags:
- purge
parameters:
- $ref: '#/parameters/requestId'
responses:
'200':
description: Get purge job's schedule.
schema:
$ref: '#/definitions/ExecHistory'
'401':
$ref: '#/responses/401'
'403':
$ref: '#/responses/403'
'500':
$ref: '#/responses/500'
post:
summary: Create a purge job schedule.
description: |
This endpoint is for update purge job schedule.
operationId: createPurgeSchedule
parameters:
- $ref: '#/parameters/requestId'
- name: schedule
in: body
required: true
schema:
$ref: '#/definitions/Schedule'
description: |
The purge job's schedule, it is a json object.
The sample format is
{"parameters":{"audit_retention_hour":168,"dry_run":true, "include_operations":"create,delete,pull"},"schedule":{"type":"Hourly","cron":"0 0 * * * *"}}
the include_operation should be a comma separated string, e.g. create,delete,pull, if it is empty, no operation will be purged.
tags:
- purge
responses:
'201':
$ref: '#/responses/201'
'400':
$ref: '#/responses/400'
'401':
$ref: '#/responses/401'
'403':
$ref: '#/responses/403'
'500':
$ref: '#/responses/500'
put:
summary: Update purge job's schedule.
description: |
This endpoint is for update purge job schedule.
operationId: updatePurgeSchedule
parameters:
- $ref: '#/parameters/requestId'
- name: schedule
in: body
required: true
schema:
$ref: '#/definitions/Schedule'
description: |
The purge job's schedule, it is a json object.
The sample format is
{"parameters":{"audit_retention_hour":168,"dry_run":true, "include_operations":"create,delete,pull"},"schedule":{"type":"Hourly","cron":"0 0 * * * *"}}
the include_operation should be a comma separated string, e.g. create,delete,pull, if it is empty, no operation will be purged.
tags:
- purge
responses:
'200':
description: Updated purge's schedule successfully.
'400':
$ref: '#/responses/400'
'401':
$ref: '#/responses/401'
'403':
$ref: '#/responses/403'
'500':
$ref: '#/responses/500'
/system/CVEAllowlist:
get:
summary: Get the system level allowlist of CVE.
@ -5523,6 +5687,13 @@ parameters:
required: true
type: integer
format: int64
purgeId:
name: purge_id
in: path
description: The ID of the purge log
required: true
type: integer
format: int64
labelId:
name: label_id
in: path
@ -7224,6 +7395,37 @@ definitions:
type: string
format: date-time
description: the update time of gc job.
ExecHistory:
type: object
properties:
id:
type: integer
description: the id of purge job.
job_name:
type: string
description: the job name of purge job.
job_kind:
type: string
description: the job kind of purge job.
job_parameters:
type: string
description: the job parameters of purge job.
schedule:
$ref: '#/definitions/ScheduleObj'
job_status:
type: string
description: the status of purge job.
deleted:
type: boolean
description: if purge job was deleted.
creation_time:
type: string
format: date-time
description: the creation time of purge job.
update_time:
type: string
format: date-time
description: the update time of purge job.
Schedule:
type: object
properties:

View File

@ -0,0 +1,28 @@
// 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 jobservice
import "time"
// Execution model for replication
type Execution struct {
ID int64
Status string
StatusMessage string
Trigger string
ExtraAttrs map[string]interface{}
StartTime time.Time
EndTime time.Time
}

View File

@ -308,8 +308,8 @@ func (bs *Bootstrap) loadAndRunRedisWorkerPool(
job.SampleJob: (*sample.Job)(nil),
// Functional jobs
job.ImageScanJob: (*scan.Job)(nil),
job.GarbageCollection: (*gc.GarbageCollector)(nil),
job.PurgeAudit: (*purge.Job)(nil),
job.GarbageCollection: (*gc.GarbageCollector)(nil),
job.Replication: (*replication.Replication)(nil),
job.Retention: (*retention.Job)(nil),
scheduler.JobNameScheduler: (*scheduler.PeriodicJob)(nil),

View File

@ -14,7 +14,11 @@
package lib
import "strings"
import (
"golang.org/x/text/cases"
"golang.org/x/text/language"
"strings"
)
// TrimsLineBreaks trims line breaks in string.
func TrimLineBreaks(s string) string {
@ -22,3 +26,9 @@ func TrimLineBreaks(s string) string {
escaped = strings.ReplaceAll(escaped, "\r", "")
return escaped
}
// Title uppercase the first character, and lower case the rest, for example covert MANUAL to Manual
func Title(s string) string {
title := cases.Title(language.Und)
return title.String(strings.ToLower(s))
}

View File

@ -32,3 +32,23 @@ def
actual := TrimLineBreaks(s)
assert.Equal(expect, actual, "should trim line breaks")
}
func TestTitle(t *testing.T) {
type args struct {
s string
}
tests := []struct {
name string
args args
want string
}{
{"upper case", args{"MANUAL"}, "Manual"},
{"lower case", args{"manual"}, "Manual"},
{"empty", args{""}, ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, Title(tt.args.s), "Title(%v)", tt.args.s)
})
}
}

View File

@ -114,7 +114,13 @@ func (s *scheduler) Schedule(ctx context.Context, vendorType string, vendorID in
return 0, err
}
sched.CallbackFuncParam = string(paramsData)
params := map[string]interface{}{}
if len(paramsData) > 0 {
err = json.Unmarshal(paramsData, &params)
if err != nil {
log.Debugf("current paramsData is not a json string")
}
}
extrasData, err := json.Marshal(extraAttrs)
if err != nil {
return 0, err
@ -129,7 +135,7 @@ func (s *scheduler) Schedule(ctx context.Context, vendorType string, vendorID in
return 0, err
}
execID, err := s.execMgr.Create(ctx, JobNameScheduler, id, task.ExecutionTriggerManual)
execID, err := s.execMgr.Create(ctx, JobNameScheduler, id, task.ExecutionTriggerManual, params)
if err != nil {
return 0, err
}

View File

@ -66,7 +66,7 @@ func (s *schedulerTestSuite) TestSchedule() {
// failed to submit to jobservice
s.dao.On("Create", mock.Anything, mock.Anything).Return(int64(1), nil)
s.execMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
s.execMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
s.taskMgr.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
s.taskMgr.On("Get", mock.Anything, mock.Anything).Return(&task.Task{
ID: 1,
@ -84,7 +84,7 @@ func (s *schedulerTestSuite) TestSchedule() {
s.SetupTest()
// pass
s.execMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
s.execMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
s.dao.On("Create", mock.Anything, mock.Anything).Return(int64(1), nil)
s.taskMgr.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
s.taskMgr.On("Get", mock.Anything, mock.Anything).Return(&task.Task{

View File

@ -64,6 +64,7 @@ func New() http.Handler {
HealthAPI: newHealthAPI(),
StatisticAPI: newStatisticAPI(),
ProjectMetadataAPI: newProjectMetadaAPI(),
PurgeAPI: newPurgeAPI(),
})
if err != nil {
log.Fatal(err)

View File

@ -0,0 +1,57 @@
// 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 model
import (
"github.com/go-openapi/strfmt"
"github.com/goharbor/harbor/src/lib"
"github.com/goharbor/harbor/src/server/v2.0/models"
"strings"
"time"
)
// ExecHistory execution history
type ExecHistory struct {
Schedule *ScheduleParam `json:"schedule"`
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"`
}
// ToSwagger converts the history to the swagger model
func (h *ExecHistory) ToSwagger() *models.ExecHistory {
return &models.ExecHistory{
ID: h.ID,
JobName: h.Name,
JobKind: h.Kind,
JobParameters: h.Parameters,
Deleted: h.Deleted,
JobStatus: h.Status,
Schedule: &models.ScheduleObj{
// covert MANUAL to Manual because the type of the ScheduleObj
// must be 'Hourly', 'Daily', 'Weekly', 'Custom', 'Manual' and 'None'
Type: lib.Title(strings.ToLower(h.Schedule.Type)),
Cron: h.Schedule.Cron,
},
CreationTime: strfmt.DateTime(h.CreationTime),
UpdateTime: strfmt.DateTime(h.UpdateTime),
}
}

View File

@ -0,0 +1,301 @@
// 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 handler
import (
"context"
"encoding/json"
"fmt"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/controller/jobservice"
pg "github.com/goharbor/harbor/src/controller/purge"
"github.com/goharbor/harbor/src/controller/task"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/q"
taskPkg "github.com/goharbor/harbor/src/pkg/task"
"github.com/goharbor/harbor/src/server/v2.0/handler/model"
"github.com/goharbor/harbor/src/server/v2.0/models"
"github.com/goharbor/harbor/src/server/v2.0/restapi/operations/purge"
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/purge"
"path"
)
type purgeAPI struct {
BaseAPI
purgeCtr pg.Controller
schedulerCtl jobservice.SchedulerController
taskCtl task.Controller
executionCtl task.ExecutionController
}
func newPurgeAPI() *purgeAPI {
return &purgeAPI{
purgeCtr: pg.Ctrl,
schedulerCtl: jobservice.SchedulerCtl,
taskCtl: task.Ctl,
executionCtl: task.ExecutionCtl,
}
}
func (p *purgeAPI) CreatePurgeSchedule(ctx context.Context, params purge.CreatePurgeScheduleParams) middleware.Responder {
if err := p.RequireSystemAccess(ctx, rbac.ActionCreate, rbac.ResourcePurgeAuditLog); err != nil {
return p.SendError(ctx, err)
}
if err := verifyCreateRequest(params); err != nil {
return p.SendError(ctx, err)
}
id, err := p.kick(ctx, pg.VendorType, params.Schedule.Schedule.Type, params.Schedule.Schedule.Cron, params.Schedule.Parameters)
if err != nil {
return p.SendError(ctx, err)
}
location := path.Join(params.HTTPRequest.URL.Path, fmt.Sprintf("../%d", id))
return purge.NewCreatePurgeScheduleCreated().WithLocation(location)
}
func verifyCreateRequest(params purge.CreatePurgeScheduleParams) error {
if params.Schedule == nil || params.Schedule.Schedule == nil {
return errors.BadRequestError(fmt.Errorf("schedule cann't be empty"))
}
if len(params.Schedule.Parameters) == 0 {
return errors.BadRequestError(fmt.Errorf("schedule parameter cann't be empty"))
}
if _, exist := params.Schedule.Parameters[common.PurgeAuditRetentionHour]; !exist {
return errors.BadRequestError(fmt.Errorf("audit_retention_hour should provide"))
}
if _, exist := params.Schedule.Parameters[common.PurgeAuditIncludeOperations]; !exist {
return errors.BadRequestError(fmt.Errorf("include_operations should provide"))
}
return nil
}
func (p *purgeAPI) kick(ctx context.Context, vendorType string, scheType string, cron string, parameters map[string]interface{}) (int64, error) {
if parameters == nil {
parameters = make(map[string]interface{})
}
var err error
var id int64
policy := pg.JobPolicy{
ExtraAttrs: parameters,
}
if dryRun, ok := parameters[common.PurgeAuditDryRun].(bool); ok {
policy.DryRun = dryRun
}
if includeOperations, ok := parameters[common.PurgeAuditIncludeOperations].(string); ok {
policy.IncludeOperations = includeOperations
}
if retentionHour, ok := parameters[common.PurgeAuditRetentionHour]; ok {
if rh, ok := retentionHour.(json.Number); ok {
ret, err := rh.Int64()
if err != nil {
return 0, errors.BadRequestError(fmt.Errorf("failed to convert audit_retention_hour, error: %v", err))
}
policy.RetentionHour = int(ret)
}
}
switch scheType {
case ScheduleManual:
id, err = p.purgeCtr.Start(ctx, policy, taskPkg.ExecutionTriggerManual)
case ScheduleNone:
// delete the schedule of purge
err = p.schedulerCtl.Delete(ctx, vendorType)
case ScheduleHourly, ScheduleDaily, ScheduleWeekly, ScheduleCustom:
err = p.updateSchedule(ctx, vendorType, scheType, cron, policy, parameters)
}
return id, err
}
func (p *purgeAPI) updateSchedule(ctx context.Context, vendorType, cronType, cron string, policy pg.JobPolicy, extraParams map[string]interface{}) error {
if err := p.schedulerCtl.Delete(ctx, vendorType); err != nil {
return err
}
return p.createSchedule(ctx, vendorType, cronType, cron, policy, extraParams)
}
func (p *purgeAPI) GetPurgeHistory(ctx context.Context, params purge.GetPurgeHistoryParams) middleware.Responder {
if err := p.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourcePurgeAuditLog); err != nil {
return p.SendError(ctx, err)
}
query, err := p.BuildQuery(ctx, params.Q, params.Sort, params.Page, params.PageSize)
query.Keywords["VendorType"] = pg.VendorType
if err != nil {
return p.SendError(ctx, err)
}
total, err := p.executionCtl.Count(ctx, query)
if err != nil {
return p.SendError(ctx, err)
}
execs, err := p.executionCtl.List(ctx, query)
if err != nil {
p.SendError(ctx, err)
}
var hs []*model.ExecHistory
for _, exec := range execs {
extraAttrsString, err := json.Marshal(exec.ExtraAttrs)
if err != nil {
return p.SendError(ctx, err)
}
hs = append(hs, &model.ExecHistory{
ID: exec.ID,
Name: pg.VendorType,
Kind: exec.Trigger,
Parameters: string(extraAttrsString),
Schedule: &model.ScheduleParam{
Type: exec.Trigger,
},
Status: exec.Status,
CreationTime: exec.StartTime,
UpdateTime: exec.EndTime,
})
}
var results []*models.ExecHistory
for _, h := range hs {
results = append(results, h.ToSwagger())
}
return operation.NewGetPurgeHistoryOK().
WithXTotalCount(total).
WithLink(p.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
WithPayload(results)
}
func (p *purgeAPI) GetPurgeJob(ctx context.Context, params purge.GetPurgeJobParams) middleware.Responder {
if err := p.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourcePurgeAuditLog); err != nil {
return p.SendError(ctx, err)
}
exec, err := p.executionCtl.Get(ctx, params.PurgeID)
if err != nil {
return p.SendError(ctx, err)
}
extraAttrsString, err := json.Marshal(exec.ExtraAttrs)
if err != nil {
return p.SendError(ctx, err)
}
res := &model.ExecHistory{
ID: exec.ID,
Name: pg.VendorType,
Kind: exec.Trigger,
Parameters: string(extraAttrsString),
Status: exec.Status,
Schedule: &model.ScheduleParam{
Type: exec.Trigger,
},
CreationTime: exec.StartTime,
UpdateTime: exec.EndTime,
}
return operation.NewGetPurgeJobOK().WithPayload(res.ToSwagger())
}
func (p *purgeAPI) GetPurgeJobLog(ctx context.Context, params purge.GetPurgeJobLogParams) middleware.Responder {
if err := p.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourcePurgeAuditLog); err != nil {
return p.SendError(ctx, err)
}
tasks, err := p.taskCtl.List(ctx, q.New(q.KeyWords{
"ExecutionID": params.PurgeID,
"VendorType": pg.VendorType,
}))
if err != nil {
return p.SendError(ctx, err)
}
if len(tasks) == 0 {
return p.SendError(ctx,
errors.New(nil).WithCode(errors.NotFoundCode).
WithMessage("purge job with execution ID: %d taskLog is not found", params.PurgeID))
}
taskLog, err := p.taskCtl.GetLog(ctx, tasks[0].ID)
if err != nil {
return p.SendError(ctx, err)
}
return operation.NewGetPurgeJobLogOK().WithPayload(string(taskLog))
}
func (p *purgeAPI) GetPurgeSchedule(ctx context.Context, params purge.GetPurgeScheduleParams) middleware.Responder {
if err := p.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourcePurgeAuditLog); err != nil {
return p.SendError(ctx, err)
}
sch, err := p.schedulerCtl.Get(ctx, pg.VendorType)
if errors.IsNotFoundErr(err) {
return operation.NewGetPurgeScheduleOK()
}
if err != nil {
return p.SendError(ctx, err)
}
execHistory := &models.ExecHistory{
ID: sch.ID,
JobName: "",
JobKind: sch.CRON,
JobParameters: pg.String(sch.ExtraAttrs),
Deleted: false,
JobStatus: sch.Status,
Schedule: &models.ScheduleObj{
Cron: sch.CRON,
Type: sch.CRONType,
},
CreationTime: strfmt.DateTime(sch.CreationTime),
UpdateTime: strfmt.DateTime(sch.UpdateTime),
}
return operation.NewGetPurgeScheduleOK().WithPayload(execHistory)
}
func (p *purgeAPI) UpdatePurgeSchedule(ctx context.Context, params purge.UpdatePurgeScheduleParams) middleware.Responder {
if err := p.RequireSystemAccess(ctx, rbac.ActionUpdate, rbac.ResourcePurgeAuditLog); err != nil {
return p.SendError(ctx, err)
}
if err := verifyUpdateRequest(params); err != nil {
return p.SendError(ctx, err)
}
_, err := p.kick(ctx, pg.VendorType, params.Schedule.Schedule.Type, params.Schedule.Schedule.Cron, params.Schedule.Parameters)
if err != nil {
return p.SendError(ctx, err)
}
return operation.NewUpdatePurgeScheduleOK()
}
func verifyUpdateRequest(params operation.UpdatePurgeScheduleParams) error {
if params.Schedule == nil || params.Schedule.Schedule == nil {
return errors.BadRequestError(fmt.Errorf("schedule cann't be empty"))
}
if len(params.Schedule.Parameters) == 0 {
return errors.BadRequestError(fmt.Errorf("schedule parameter cann't be empty"))
}
if _, exist := params.Schedule.Parameters[common.PurgeAuditRetentionHour]; !exist {
return errors.BadRequestError(fmt.Errorf("audit_retention_hour should provide"))
}
if _, exist := params.Schedule.Parameters[common.PurgeAuditIncludeOperations]; !exist {
return errors.BadRequestError(fmt.Errorf("include_operations should provide"))
}
return nil
}
func (p *purgeAPI) createSchedule(ctx context.Context, vendorType string, cronType string, cron string, policy pg.JobPolicy, extraParam map[string]interface{}) error {
if cron == "" {
return errors.New(nil).WithCode(errors.BadRequestCode).
WithMessage("empty cron string for schedule")
}
_, err := p.schedulerCtl.Create(ctx, vendorType, cronType, cron, pg.SchedulerCallback, policy, extraParam)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,69 @@
// 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 handler
import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/server/v2.0/models"
"github.com/goharbor/harbor/src/server/v2.0/restapi/operations/purge"
"testing"
)
func Test_verifyUpdateRequest(t *testing.T) {
type args struct {
params purge.UpdatePurgeScheduleParams
}
tests := []struct {
name string
args args
wantErr bool
}{
{"normal", args{purge.UpdatePurgeScheduleParams{Schedule: &models.Schedule{Schedule: &models.ScheduleObj{}, Parameters: map[string]interface{}{common.PurgeAuditRetentionHour: "168", common.PurgeAuditIncludeOperations: "pull"}}}}, false},
{"missing_schedule", args{purge.UpdatePurgeScheduleParams{Schedule: &models.Schedule{Parameters: map[string]interface{}{common.PurgeAuditRetentionHour: "168", common.PurgeAuditIncludeOperations: "pull"}}}}, true},
{"missing_retention_hour", args{purge.UpdatePurgeScheduleParams{Schedule: &models.Schedule{Schedule: &models.ScheduleObj{}, Parameters: map[string]interface{}{common.PurgeAuditIncludeOperations: "pull"}}}}, true},
{"missing_operations", args{purge.UpdatePurgeScheduleParams{Schedule: &models.Schedule{Schedule: &models.ScheduleObj{}, Parameters: map[string]interface{}{common.PurgeAuditRetentionHour: "168"}}}}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := verifyUpdateRequest(tt.args.params)
if tt.wantErr != (err != nil) {
t.Error("test failed")
}
})
}
}
func Test_verifyCreateRequest(t *testing.T) {
type args struct {
params purge.CreatePurgeScheduleParams
}
tests := []struct {
name string
args args
wantErr bool
}{
{"normal", args{purge.CreatePurgeScheduleParams{Schedule: &models.Schedule{Schedule: &models.ScheduleObj{}, Parameters: map[string]interface{}{common.PurgeAuditRetentionHour: "168", common.PurgeAuditIncludeOperations: "pull"}}}}, false},
{"missing_schedule", args{purge.CreatePurgeScheduleParams{Schedule: &models.Schedule{Parameters: map[string]interface{}{common.PurgeAuditRetentionHour: "168", common.PurgeAuditIncludeOperations: "pull"}}}}, true},
{"missing_retention_hour", args{purge.CreatePurgeScheduleParams{Schedule: &models.Schedule{Schedule: &models.ScheduleObj{}, Parameters: map[string]interface{}{common.PurgeAuditIncludeOperations: "pull"}}}}, true},
{"missing_operations", args{purge.CreatePurgeScheduleParams{Schedule: &models.Schedule{Schedule: &models.ScheduleObj{}, Parameters: map[string]interface{}{common.PurgeAuditRetentionHour: "168"}}}}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := verifyCreateRequest(tt.args.params)
if tt.wantErr != (err != nil) {
t.Error("test failed")
}
})
}
}

View File

@ -29,3 +29,4 @@ package controller
//go:generate mockery --case snake --dir ../../controller/user --name Controller --output ./user --outpkg user
//go:generate mockery --case snake --dir ../../controller/repository --name Controller --output ./repository --outpkg repository
//go:generate mockery --case snake --dir ../../controller/purge --name Controller --output ./purge --outpkg purge
//go:generate mockery --case snake --dir ../../controller/jobservice --name SchedulerController --output ./jobservice --outpkg jobservice

View File

@ -0,0 +1,84 @@
// Code generated by mockery v2.1.0. DO NOT EDIT.
package jobservice
import (
context "context"
jobservice "github.com/goharbor/harbor/src/controller/jobservice"
mock "github.com/stretchr/testify/mock"
q "github.com/goharbor/harbor/src/lib/q"
)
// ExecutionController is an autogenerated mock type for the ExecutionController type
type ExecutionController struct {
mock.Mock
}
// Count provides a mock function with given fields: ctx, vendorType, query
func (_m *ExecutionController) Count(ctx context.Context, vendorType string, query *q.Query) (int64, error) {
ret := _m.Called(ctx, vendorType, query)
var r0 int64
if rf, ok := ret.Get(0).(func(context.Context, string, *q.Query) int64); ok {
r0 = rf(ctx, vendorType, query)
} else {
r0 = ret.Get(0).(int64)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string, *q.Query) error); ok {
r1 = rf(ctx, vendorType, query)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Get provides a mock function with given fields: ctx, vendorType, executionID
func (_m *ExecutionController) Get(ctx context.Context, vendorType string, executionID int64) (*jobservice.Execution, error) {
ret := _m.Called(ctx, vendorType, executionID)
var r0 *jobservice.Execution
if rf, ok := ret.Get(0).(func(context.Context, string, int64) *jobservice.Execution); ok {
r0 = rf(ctx, vendorType, executionID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*jobservice.Execution)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string, int64) error); ok {
r1 = rf(ctx, vendorType, executionID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// List provides a mock function with given fields: ctx, vendorType, query
func (_m *ExecutionController) List(ctx context.Context, vendorType string, query *q.Query) ([]*jobservice.Execution, error) {
ret := _m.Called(ctx, vendorType, query)
var r0 []*jobservice.Execution
if rf, ok := ret.Get(0).(func(context.Context, string, *q.Query) []*jobservice.Execution); ok {
r0 = rf(ctx, vendorType, query)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*jobservice.Execution)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string, *q.Query) error); ok {
r1 = rf(ctx, vendorType, query)
} else {
r1 = ret.Error(1)
}
return r0, r1
}

View File

@ -0,0 +1,74 @@
// Code generated by mockery v2.1.0. DO NOT EDIT.
package jobservice
import (
context "context"
mock "github.com/stretchr/testify/mock"
scheduler "github.com/goharbor/harbor/src/pkg/scheduler"
)
// SchedulerController is an autogenerated mock type for the SchedulerController type
type SchedulerController struct {
mock.Mock
}
// Create provides a mock function with given fields: ctx, vendorType, cronType, cron, callbackFuncName, policy, extrasParam
func (_m *SchedulerController) Create(ctx context.Context, vendorType string, cronType string, cron string, callbackFuncName string, policy interface{}, extrasParam map[string]interface{}) (int64, error) {
ret := _m.Called(ctx, vendorType, cronType, cron, callbackFuncName, policy, extrasParam)
var r0 int64
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, interface{}, map[string]interface{}) int64); ok {
r0 = rf(ctx, vendorType, cronType, cron, callbackFuncName, policy, extrasParam)
} else {
r0 = ret.Get(0).(int64)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string, interface{}, map[string]interface{}) error); ok {
r1 = rf(ctx, vendorType, cronType, cron, callbackFuncName, policy, extrasParam)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Delete provides a mock function with given fields: ctx, vendorType
func (_m *SchedulerController) Delete(ctx context.Context, vendorType string) error {
ret := _m.Called(ctx, vendorType)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, vendorType)
} else {
r0 = ret.Error(0)
}
return r0
}
// Get provides a mock function with given fields: ctx, vendorType
func (_m *SchedulerController) Get(ctx context.Context, vendorType string) (*scheduler.Schedule, error) {
ret := _m.Called(ctx, vendorType)
var r0 *scheduler.Schedule
if rf, ok := ret.Get(0).(func(context.Context, string) *scheduler.Schedule); ok {
r0 = rf(ctx, vendorType)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*scheduler.Schedule)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, vendorType)
} else {
r1 = ret.Error(1)
}
return r0, r1
}

View File

@ -0,0 +1,86 @@
// Code generated by mockery v2.1.0. DO NOT EDIT.
package jobservice
import (
context "context"
jobservice "github.com/goharbor/harbor/src/controller/jobservice"
mock "github.com/stretchr/testify/mock"
q "github.com/goharbor/harbor/src/lib/q"
)
// TaskController is an autogenerated mock type for the TaskController type
type TaskController struct {
mock.Mock
}
// Get provides a mock function with given fields: ctx, vendorType, id
func (_m *TaskController) Get(ctx context.Context, vendorType string, id int64) (*jobservice.Task, error) {
ret := _m.Called(ctx, vendorType, id)
var r0 *jobservice.Task
if rf, ok := ret.Get(0).(func(context.Context, string, int64) *jobservice.Task); ok {
r0 = rf(ctx, vendorType, id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*jobservice.Task)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string, int64) error); ok {
r1 = rf(ctx, vendorType, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetLog provides a mock function with given fields: ctx, vendorType, id
func (_m *TaskController) GetLog(ctx context.Context, vendorType string, id int64) ([]byte, error) {
ret := _m.Called(ctx, vendorType, id)
var r0 []byte
if rf, ok := ret.Get(0).(func(context.Context, string, int64) []byte); ok {
r0 = rf(ctx, vendorType, id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]byte)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string, int64) error); ok {
r1 = rf(ctx, vendorType, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// List provides a mock function with given fields: ctx, vendorType, query
func (_m *TaskController) List(ctx context.Context, vendorType string, query *q.Query) ([]*jobservice.Task, error) {
ret := _m.Called(ctx, vendorType, query)
var r0 []*jobservice.Task
if rf, ok := ret.Get(0).(func(context.Context, string, *q.Query) []*jobservice.Task); ok {
r0 = rf(ctx, vendorType, query)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*jobservice.Task)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string, *q.Query) error); ok {
r1 = rf(ctx, vendorType, query)
} else {
r1 = ret.Error(1)
}
return r0, r1
}