diff --git a/src/controller/systemartifact/callback.go b/src/controller/systemartifact/callback.go new file mode 100644 index 000000000..dd2aaac63 --- /dev/null +++ b/src/controller/systemartifact/callback.go @@ -0,0 +1,32 @@ +package systemartifact + +import ( + "context" + "github.com/goharbor/harbor/src/jobservice/logger" + "github.com/goharbor/harbor/src/lib/log" + "github.com/goharbor/harbor/src/pkg/scheduler" + "github.com/goharbor/harbor/src/pkg/task" +) + +const ( + SystemArtifactCleanupCallback = "SYSTEM_ARTIFACT_CLEANUP" +) + +var ( + cleanupController = Ctl +) + +func init() { + if err := scheduler.RegisterCallbackFunc(SystemArtifactCleanupCallback, cleanupCallBack); err != nil { + log.Fatalf("failed to register the callback for the system artifact cleanup schedule, error %v", err) + } +} + +func cleanupCallBack(ctx context.Context, param string) error { + + err := cleanupController.Start(ctx, true, task.ExecutionTriggerSchedule) + if err != nil { + logger.Errorf("System artifact cleanup job encountered errors: %v", err) + } + return err +} diff --git a/src/controller/systemartifact/callback_test.go b/src/controller/systemartifact/callback_test.go new file mode 100644 index 000000000..37b85e9a1 --- /dev/null +++ b/src/controller/systemartifact/callback_test.go @@ -0,0 +1,56 @@ +package systemartifact + +import ( + "context" + "errors" + "github.com/goharbor/harbor/src/pkg/task" + "github.com/goharbor/harbor/src/testing/controller/systemartifact" + "github.com/goharbor/harbor/src/testing/mock" + testifymock "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "testing" +) + +type CallbackTestSuite struct { + suite.Suite + cleanupController *systemartifact.Controller +} + +func (suite *CallbackTestSuite) SetupSuite() { + suite.cleanupController = &systemartifact.Controller{} + cleanupController = suite.cleanupController +} + +func (suite *CallbackTestSuite) TestCleanupCallbackSuccess() { + { + ctx := context.TODO() + suite.cleanupController.On("Start", mock.Anything, mock.Anything, mock.Anything).Return(nil) + triggerScheduleMatcher := testifymock.MatchedBy(func(trigger string) bool { + return trigger == task.ExecutionTriggerSchedule + }) + err := cleanupCallBack(ctx, "") + suite.NoErrorf(err, "Unexpected error : %v", err) + suite.cleanupController.AssertCalled(suite.T(), "Start", mock.Anything, true, triggerScheduleMatcher) + } + { + suite.cleanupController = nil + suite.cleanupController = &systemartifact.Controller{} + cleanupController = suite.cleanupController + } + + { + ctx := context.TODO() + suite.cleanupController.On("Start", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("test error")) + triggerScheduleMatcher := testifymock.MatchedBy(func(trigger string) bool { + return trigger == task.ExecutionTriggerSchedule + }) + err := cleanupCallBack(ctx, "") + suite.Error(err) + suite.cleanupController.AssertCalled(suite.T(), "Start", mock.Anything, true, triggerScheduleMatcher) + } + +} + +func TestCallbackTestSuite(t *testing.T) { + suite.Run(t, &CallbackTestSuite{}) +} diff --git a/src/controller/systemartifact/execution.go b/src/controller/systemartifact/execution.go new file mode 100644 index 000000000..a38a1c0da --- /dev/null +++ b/src/controller/systemartifact/execution.go @@ -0,0 +1,156 @@ +package systemartifact + +import ( + "context" + "github.com/goharbor/harbor/src/jobservice/job" + "github.com/goharbor/harbor/src/jobservice/logger" + "github.com/goharbor/harbor/src/lib/log" + "github.com/goharbor/harbor/src/lib/orm" + "github.com/goharbor/harbor/src/lib/q" + "github.com/goharbor/harbor/src/lib/retry" + "github.com/goharbor/harbor/src/pkg/scheduler" + "github.com/goharbor/harbor/src/pkg/systemartifact" + "github.com/goharbor/harbor/src/pkg/task" + "time" +) + +const ( + VendorTypeSystemArtifactCleanup = "SYSTEM_ARTIFACT_CLEANUP" + cronTypeDaily = "Daily" + cronSpec = "0 0 0 * * *" +) + +var ( + sched = scheduler.Sched +) + +func init() { + task.SetExecutionSweeperCount(VendorTypeSystemArtifactCleanup, 50) +} + +var Ctl = NewController() + +type Controller interface { + Start(ctx context.Context, async bool, trigger string) error +} + +func NewController() Controller { + return &controller{ + execMgr: task.ExecMgr, + taskMgr: task.Mgr, + systemArtifactMgr: systemartifact.Mgr, + makeCtx: orm.Context, + } +} + +type controller struct { + execMgr task.ExecutionManager + taskMgr task.Manager + systemArtifactMgr systemartifact.Manager + makeCtx func() context.Context +} + +func (c *controller) Start(ctx context.Context, async bool, trigger string) error { + execId, err := c.execMgr.Create(ctx, VendorTypeSystemArtifactCleanup, 0, trigger) + if err != nil { + return err + } + // cleanup job would always be scheduled in async mode in production + // allowing for sync mode execution only for test mode purposes + // if there are any trigger settings then pass them to the cleanup manager first + jobParams := job.Parameters{} + + if !async { + err := c.createCleanupTask(ctx, jobParams, execId) + if err != nil { + log.Errorf("failed to create system artifact clean-up task: %v", err) + return err + } + + logger.Info("Created job for scan data export successfully") + return nil + } + go func(ctx context.Context) { + err := retry.Retry(func() error { + _, err := c.execMgr.Get(ctx, execId) + return err + }) + if err != nil { + log.Errorf("failed to get the execution %d for the export data cleanup job", execId) + return + } + err = c.createCleanupTask(ctx, jobParams, execId) + if err != nil { + logger.Errorf("Encountered error in scan data artifact cleanup : %v", err) + return + } + }(c.makeCtx()) + + return nil +} + +func (c *controller) createCleanupTask(ctx context.Context, jobParams job.Parameters, execId int64) error { + j := &task.Job{ + Name: job.SystemArtifactCleanup, + Metadata: &job.Metadata{ + JobKind: job.KindGeneric, + }, + Parameters: jobParams, + } + + _, err := c.taskMgr.Create(ctx, execId, j) + + if err != nil { + logger.Errorf("Unable to create a scan data export job in clean-up mode : %v", err) + c.markError(ctx, execId, err) + return err + } + return nil +} + +func (c *controller) markError(ctx context.Context, executionID int64, err error) { + + // try to stop the execution first in case that some tasks are already created + if err := c.execMgr.StopAndWait(ctx, executionID, 10*time.Second); err != nil { + logger.Errorf("failed to stop the execution %d: %v", executionID, err) + } + if err := c.execMgr.MarkError(ctx, executionID, err.Error()); err != nil { + logger.Errorf("failed to mark error for the execution %d: %v", executionID, err) + } +} + +// ScheduleCleanupTask schedules a system artifact cleanup task +func ScheduleCleanupTask(ctx context.Context) { + scheduleSystemArtifactCleanJob(ctx) +} + +func scheduleSystemArtifactCleanJob(ctx context.Context) { + schedule, err := getSystemArtifactCleanupSchedule(ctx) + if err != nil { + return + } + if schedule != nil { + logger.Debugf(" Export data cleanup job already scheduled with ID : %v.", schedule.ID) + return + } + scheduleId, err := sched.Schedule(ctx, VendorTypeSystemArtifactCleanup, 0, cronTypeDaily, cronSpec, SystemArtifactCleanupCallback, nil, nil) + if err != nil { + log.Errorf("Encountered error when scheduling scan data export cleanup job : %v", err) + return + } + log.Infof("Scheduled scan data export cleanup job with ID : %v", scheduleId) +} + +func getSystemArtifactCleanupSchedule(ctx context.Context) (*scheduler.Schedule, error) { + query := q.New(map[string]interface{}{"vendor_type": VendorTypeSystemArtifactCleanup}) + schedules, err := sched.ListSchedules(ctx, query) + if err != nil { + logger.Errorf("Unable to check if export data cleanup job is already scheduled : %v", err) + return nil, err + } + if len(schedules) > 0 { + logger.Infof("Found export data cleanup job with schedule id : %v", schedules[0].ID) + return schedules[0], nil + } + return nil, nil +} diff --git a/src/controller/systemartifact/execution_test.go b/src/controller/systemartifact/execution_test.go new file mode 100644 index 000000000..2c33000f6 --- /dev/null +++ b/src/controller/systemartifact/execution_test.go @@ -0,0 +1,217 @@ +package systemartifact + +import ( + "context" + "github.com/goharbor/harbor/src/lib/orm" + scheduler2 "github.com/goharbor/harbor/src/pkg/scheduler" + "github.com/goharbor/harbor/src/pkg/task" + ormtesting "github.com/goharbor/harbor/src/testing/lib/orm" + "github.com/goharbor/harbor/src/testing/mock" + "github.com/goharbor/harbor/src/testing/pkg/scheduler" + "github.com/goharbor/harbor/src/testing/pkg/systemartifact" + testingTask "github.com/goharbor/harbor/src/testing/pkg/task" + "github.com/pkg/errors" + testifymock "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "testing" +) + +type SystemArtifactCleanupTestSuite struct { + suite.Suite + execMgr *testingTask.ExecutionManager + taskMgr *testingTask.Manager + cleanupMgr *systemartifact.Manager + ctl *controller + sched *scheduler.Scheduler +} + +func (suite *SystemArtifactCleanupTestSuite) SetupSuite() { +} + +func (suite *SystemArtifactCleanupTestSuite) TestStartCleanup() { + suite.taskMgr = &testingTask.Manager{} + suite.execMgr = &testingTask.ExecutionManager{} + suite.cleanupMgr = &systemartifact.Manager{} + suite.ctl = &controller{ + execMgr: suite.execMgr, + taskMgr: suite.taskMgr, + systemArtifactMgr: suite.cleanupMgr, + makeCtx: func() context.Context { return orm.NewContext(nil, &ormtesting.FakeOrmer{}) }, + } + + { + + ctx := context.TODO() + + executionID := int64(1) + taskId := int64(1) + + suite.execMgr.On("Create", ctx, "SYSTEM_ARTIFACT_CLEANUP", int64(0), "SCHEDULE").Return(executionID, nil).Once() + + suite.taskMgr.On("Create", ctx, executionID, mock.Anything).Return(taskId, nil).Once() + + suite.execMgr.On("MarkDone", ctx, executionID, mock.Anything).Return(nil).Once() + + err := suite.ctl.Start(ctx, false, "SCHEDULE") + suite.NoError(err) + jobMatcher := testifymock.MatchedBy(func(j *task.Job) bool { + return "SYSTEM_ARTIFACT_CLEANUP" == j.Name + }) + suite.taskMgr.AssertCalled(suite.T(), "Create", ctx, executionID, jobMatcher) + } +} + +func (suite *SystemArtifactCleanupTestSuite) TestStartCleanupErrorDuringCreate() { + suite.taskMgr = &testingTask.Manager{} + suite.execMgr = &testingTask.ExecutionManager{} + suite.cleanupMgr = &systemartifact.Manager{} + suite.ctl = &controller{ + execMgr: suite.execMgr, + taskMgr: suite.taskMgr, + systemArtifactMgr: suite.cleanupMgr, + makeCtx: func() context.Context { return orm.NewContext(nil, &ormtesting.FakeOrmer{}) }, + } + + { + + ctx := context.TODO() + + executionID := int64(1) + + suite.execMgr.On( + "Create", ctx, "SYSTEM_ARTIFACT_CLEANUP", int64(0), "SCHEDULE", + ).Return(int64(0), errors.New("test error")).Once() + + suite.execMgr.On("MarkDone", ctx, executionID, mock.Anything).Return(nil).Once() + + err := suite.ctl.Start(ctx, false, "SCHEDULE") + suite.Error(err) + } +} + +func (suite *SystemArtifactCleanupTestSuite) TestStartCleanupErrorDuringTaskCreate() { + suite.taskMgr = &testingTask.Manager{} + suite.execMgr = &testingTask.ExecutionManager{} + suite.cleanupMgr = &systemartifact.Manager{} + suite.ctl = &controller{ + execMgr: suite.execMgr, + taskMgr: suite.taskMgr, + systemArtifactMgr: suite.cleanupMgr, + makeCtx: func() context.Context { return orm.NewContext(nil, &ormtesting.FakeOrmer{}) }, + } + + { + + ctx := context.TODO() + + executionID := int64(1) + taskId := int64(0) + + suite.execMgr.On( + "Create", ctx, "SYSTEM_ARTIFACT_CLEANUP", int64(0), "SCHEDULE", + ).Return(executionID, nil).Once() + + suite.taskMgr.On("Create", ctx, executionID, mock.Anything).Return(taskId, errors.New("test error")).Once() + + suite.execMgr.On("MarkError", ctx, executionID, mock.Anything).Return(nil).Once() + suite.execMgr.On("StopAndWait", ctx, executionID, mock.Anything).Return(nil).Once() + + err := suite.ctl.Start(ctx, false, "SCHEDULE") + suite.Error(err) + } +} + +func (suite *SystemArtifactCleanupTestSuite) TestScheduleCleanupJobNoPreviousSchedule() { + suite.taskMgr = &testingTask.Manager{} + suite.execMgr = &testingTask.ExecutionManager{} + suite.cleanupMgr = &systemartifact.Manager{} + suite.sched = &scheduler.Scheduler{} + + suite.ctl = &controller{ + execMgr: suite.execMgr, + taskMgr: suite.taskMgr, + systemArtifactMgr: suite.cleanupMgr, + makeCtx: func() context.Context { return orm.NewContext(nil, &ormtesting.FakeOrmer{}) }, + } + + var extraAttrs map[string]interface{} + suite.sched.On("Schedule", mock.Anything, + VendorTypeSystemArtifactCleanup, int64(0), cronTypeDaily, cronSpec, SystemArtifactCleanupCallback, nil, extraAttrs).Return(int64(1), nil) + suite.sched.On("ListSchedules", mock.Anything, mock.Anything).Return(make([]*scheduler2.Schedule, 0), nil) + sched = suite.sched + ctx := context.TODO() + + ScheduleCleanupTask(ctx) + + suite.sched.AssertCalled(suite.T(), "Schedule", mock.Anything, + VendorTypeSystemArtifactCleanup, int64(0), cronTypeDaily, cronSpec, SystemArtifactCleanupCallback, nil, extraAttrs) +} + +func (suite *SystemArtifactCleanupTestSuite) TestScheduleCleanupJobPreviousSchedule() { + suite.taskMgr = &testingTask.Manager{} + suite.execMgr = &testingTask.ExecutionManager{} + suite.cleanupMgr = &systemartifact.Manager{} + suite.sched = &scheduler.Scheduler{} + + suite.ctl = &controller{ + execMgr: suite.execMgr, + taskMgr: suite.taskMgr, + systemArtifactMgr: suite.cleanupMgr, + makeCtx: func() context.Context { return orm.NewContext(nil, &ormtesting.FakeOrmer{}) }, + } + + var extraAttrs map[string]interface{} + suite.sched.On("Schedule", mock.Anything, + VendorTypeSystemArtifactCleanup, int64(0), cronTypeDaily, cronSpec, SystemArtifactCleanupCallback, nil, extraAttrs).Return(int64(1), nil) + + existingSchedule := scheduler2.Schedule{ID: int64(10)} + suite.sched.On("ListSchedules", mock.Anything, mock.Anything).Return([]*scheduler2.Schedule{&existingSchedule}, nil) + sched = suite.sched + ctx := context.TODO() + + ScheduleCleanupTask(ctx) + + suite.sched.AssertNotCalled(suite.T(), "Schedule", mock.Anything, + VendorTypeSystemArtifactCleanup, int64(0), cronTypeDaily, cronSpec, SystemArtifactCleanupCallback, nil, extraAttrs) +} + +func (suite *SystemArtifactCleanupTestSuite) TestScheduleCleanupJobPreviousScheduleError() { + suite.taskMgr = &testingTask.Manager{} + suite.execMgr = &testingTask.ExecutionManager{} + suite.cleanupMgr = &systemartifact.Manager{} + suite.sched = &scheduler.Scheduler{} + + suite.ctl = &controller{ + execMgr: suite.execMgr, + taskMgr: suite.taskMgr, + systemArtifactMgr: suite.cleanupMgr, + makeCtx: func() context.Context { return orm.NewContext(nil, &ormtesting.FakeOrmer{}) }, + } + + suite.sched.On("Schedule", mock.Anything, + VendorTypeSystemArtifactCleanup, int64(0), cronTypeDaily, cronSpec, SystemArtifactCleanupCallback, nil, mock.Anything).Return(int64(1), nil) + + suite.sched.On("ListSchedules", mock.Anything, mock.Anything).Return(nil, errors.New("test error")) + sched = suite.sched + ctx := context.TODO() + + ScheduleCleanupTask(ctx) + + extraAttributesMatcher := testifymock.MatchedBy(func(attrs map[string]interface{}) bool { + return len(attrs) == 0 + }) + suite.sched.AssertNotCalled(suite.T(), "Schedule", mock.Anything, + VendorTypeSystemArtifactCleanup, int64(0), cronTypeDaily, cronSpec, SystemArtifactCleanupCallback, nil, extraAttributesMatcher) +} + +func (suite *SystemArtifactCleanupTestSuite) TearDownSuite() { + suite.execMgr = nil + suite.taskMgr = nil + suite.cleanupMgr = nil + suite.ctl = nil + suite.sched = nil +} + +func TestScanDataExportExecutionTestSuite(t *testing.T) { + suite.Run(t, &SystemArtifactCleanupTestSuite{}) +} diff --git a/src/core/main.go b/src/core/main.go index de4133f48..ec14435de 100755 --- a/src/core/main.go +++ b/src/core/main.go @@ -25,6 +25,8 @@ import ( "syscall" "time" + "github.com/goharbor/harbor/src/controller/systemartifact" + "github.com/beego/beego" "github.com/goharbor/harbor/src/core/session" @@ -227,6 +229,7 @@ func main() { if err != nil { log.Warningf("oidc.FixEmptySubIss() errors out, error: %v", err) } + systemartifact.ScheduleCleanupTask(ctx) beego.RunWithMiddleWares("", middlewares.MiddleWares()...) } diff --git a/src/jobservice/job/impl/systemartifact/cleanup.go b/src/jobservice/job/impl/systemartifact/cleanup.go new file mode 100644 index 000000000..1ea7f366d --- /dev/null +++ b/src/jobservice/job/impl/systemartifact/cleanup.go @@ -0,0 +1,45 @@ +package systemartifact + +import ( + "github.com/goharbor/harbor/src/jobservice/job" + "github.com/goharbor/harbor/src/pkg/systemartifact" +) + +type Cleanup struct { + sysArtifactManager systemartifact.Manager +} + +func (c *Cleanup) MaxFails() uint { + return 1 +} + +func (c *Cleanup) MaxCurrency() uint { + return 1 +} + +func (c *Cleanup) ShouldRetry() bool { + return true +} + +func (c *Cleanup) Validate(params job.Parameters) error { + return nil +} + +func (c *Cleanup) Run(ctx job.Context, params job.Parameters) error { + logger := ctx.GetLogger() + logger.Infof("Running system data artifact cleanup job...") + c.init() + numRecordsDeleted, totalSizeReclaimed, err := c.sysArtifactManager.Cleanup(ctx.SystemContext()) + if err != nil { + logger.Errorf("Error when executing system artifact cleanup job: %v", err) + return err + } + logger.Infof("Num System artifacts cleaned up: %d, Total space reclaimed: %d.", numRecordsDeleted, totalSizeReclaimed) + return nil +} + +func (c *Cleanup) init() { + if c.sysArtifactManager == nil { + c.sysArtifactManager = systemartifact.NewManager() + } +} diff --git a/src/jobservice/job/impl/systemartifact/cleanup_test.go b/src/jobservice/job/impl/systemartifact/cleanup_test.go new file mode 100644 index 000000000..18a132a82 --- /dev/null +++ b/src/jobservice/job/impl/systemartifact/cleanup_test.go @@ -0,0 +1,64 @@ +package systemartifact + +import ( + "errors" + "github.com/goharbor/harbor/src/jobservice/job" + mockjobservice "github.com/goharbor/harbor/src/testing/jobservice" + "github.com/goharbor/harbor/src/testing/mock" + "github.com/goharbor/harbor/src/testing/pkg/systemartifact" + "github.com/stretchr/testify/suite" + "testing" +) + +type SystemArtifactCleanupSuite struct { + suite.Suite + sysArtifactMgr *systemartifact.Manager + job *Cleanup +} + +func (suite *SystemArtifactCleanupSuite) SetupTest() { + suite.sysArtifactMgr = &systemartifact.Manager{} + suite.job = &Cleanup{sysArtifactManager: suite.sysArtifactMgr} +} + +func (suite *SystemArtifactCleanupSuite) TestRun() { + mock.OnAnything(suite.sysArtifactMgr, "Cleanup").Return(int64(100), int64(100), nil) + params := job.Parameters{} + ctx := &mockjobservice.MockJobContext{} + + err := suite.job.Run(ctx, params) + suite.NoError(err) + // assert that job manager is invoked in this mode + suite.sysArtifactMgr.AssertCalled(suite.T(), "Cleanup", mock.Anything) +} + +func (suite *SystemArtifactCleanupSuite) TestRunFailure() { + mock.OnAnything(suite.sysArtifactMgr, "Cleanup").Return(int64(0), int64(0), errors.New("test error")) + params := job.Parameters{} + ctx := &mockjobservice.MockJobContext{} + + err := suite.job.Run(ctx, params) + suite.Error(err) + // assert that job manager is invoked in this mode + suite.sysArtifactMgr.AssertCalled(suite.T(), "Cleanup", mock.Anything) +} + +func (suite *SystemArtifactCleanupSuite) TestMaxFails() { + suite.Equal(uint(1), suite.job.MaxFails()) +} + +func (suite *SystemArtifactCleanupSuite) TestMaxConcurrency() { + suite.Equal(uint(1), suite.job.MaxCurrency()) +} + +func (suite *SystemArtifactCleanupSuite) TestShouldRetry() { + suite.Equal(true, suite.job.ShouldRetry()) +} + +func (suite *SystemArtifactCleanupSuite) TestValidate() { + suite.NoError(suite.job.Validate(job.Parameters{})) +} + +func TestSystemArtifactCleanupSuite(t *testing.T) { + suite.Run(t, &SystemArtifactCleanupSuite{}) +} diff --git a/src/jobservice/job/known_jobs.go b/src/jobservice/job/known_jobs.go index ec67ed095..69710bef9 100644 --- a/src/jobservice/job/known_jobs.go +++ b/src/jobservice/job/known_jobs.go @@ -36,4 +36,6 @@ const ( P2PPreheat = "P2P_PREHEAT" // PurgeAudit : the name of purge audit job PurgeAudit = "PURGE_AUDIT" + // SystemArtifactCleanup : the name of the SystemArtifact cleanup job + SystemArtifactCleanup = "SYSTEM_ARTIFACT_CLEANUP" ) diff --git a/src/jobservice/runtime/bootstrap.go b/src/jobservice/runtime/bootstrap.go index 98042cd9e..c0d26c739 100644 --- a/src/jobservice/runtime/bootstrap.go +++ b/src/jobservice/runtime/bootstrap.go @@ -24,6 +24,8 @@ import ( "syscall" "time" + "github.com/goharbor/harbor/src/jobservice/job/impl/systemartifact" + "github.com/goharbor/harbor/src/jobservice/api" "github.com/goharbor/harbor/src/jobservice/common/utils" "github.com/goharbor/harbor/src/jobservice/config" @@ -319,9 +321,10 @@ func (bs *Bootstrap) loadAndRunRedisWorkerPool( // In v2.2 we migrate the scheduled replication, garbage collection and scan all to // the scheduler mechanism, the following three jobs are kept for the legacy jobs // and they can be removed after several releases - "IMAGE_REPLICATE": (*legacy.ReplicationScheduler)(nil), - "IMAGE_GC": (*legacy.GarbageCollectionScheduler)(nil), - "IMAGE_SCAN_ALL": (*legacy.ScanAllScheduler)(nil), + "IMAGE_REPLICATE": (*legacy.ReplicationScheduler)(nil), + "IMAGE_GC": (*legacy.GarbageCollectionScheduler)(nil), + "IMAGE_SCAN_ALL": (*legacy.ScanAllScheduler)(nil), + job.SystemArtifactCleanup: (*systemartifact.Cleanup)(nil), }); err != nil { // exit return nil, err diff --git a/src/testing/controller/controller.go b/src/testing/controller/controller.go index b74ca58d5..68623c6e5 100644 --- a/src/testing/controller/controller.go +++ b/src/testing/controller/controller.go @@ -30,3 +30,4 @@ package controller //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 +//go:generate mockery --case snake --dir ../../controller/systemartifact --name Controller --output ./systemartifact --outpkg systemartifact