diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4d571509..41064a62 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -2284,9 +2284,9 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -8737,12 +8737,12 @@ } }, "globule": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", - "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz", + "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==", "requires": { "glob": "~7.1.1", - "lodash": "~4.17.10", + "lodash": "~4.17.12", "minimatch": "~3.0.2" } }, @@ -12006,9 +12006,9 @@ } }, "node-sass": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz", - "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz", + "integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==", "requires": { "async-foreach": "^0.1.3", "chalk": "^1.1.1", @@ -12017,7 +12017,7 @@ "get-stdin": "^4.0.1", "glob": "^7.0.3", "in-publish": "^2.0.0", - "lodash": "^4.17.11", + "lodash": "^4.17.15", "meow": "^3.7.0", "mkdirp": "^0.5.1", "nan": "^2.13.2", @@ -12046,6 +12046,11 @@ "supports-color": "^2.0.0" } }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -19840,9 +19845,9 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", diff --git a/frontend/package.json b/frontend/package.json index 13e0fd0c..bbdf37b9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -76,7 +76,7 @@ "babel-eslint": "^10.0.1", "eslint": "^5.16.0", "eslint-plugin-vue": "^5.0.0", - "node-sass": "^4.9.0", + "node-sass": "^4.13.1", "sass-loader": "^7.1.0", "vue-runtime-helpers": "^1.0.1", "vue-template-compiler": "^2.6.10" diff --git a/frontend/src/views/pipeline/detail.vue b/frontend/src/views/pipeline/detail.vue index 4b00bcdd..4156f378 100755 --- a/frontend/src/views/pipeline/detail.vue +++ b/frontend/src/views/pipeline/detail.vue @@ -86,6 +86,7 @@ {{ props.row.status }} {{ calculateDuration(props.row.startdate, props.row.finishdate) }} + {{ props.row.started_reason }} @@ -165,6 +166,10 @@ export default { label: 'Duration', field: 'duration' }, + { + label: 'Reason', + field: 'reason' + }, { label: 'Action', field: 'action' diff --git a/gaia.go b/gaia.go index a9345b4b..da7952bc 100644 --- a/gaia.go +++ b/gaia.go @@ -139,6 +139,15 @@ const ( // ExecutablePermission is the permission used for gaia created executables. ExecutablePermission = 0700 + + // StartReasonRemote label for pipelines which were triggered through a remote token. + StartReasonRemote = "remote" + + // StartReasonManual label for pipelines which were triggered through the admin site. + StartReasonManual = "manual" + + // StartReasonScheduled label for pipelines which were triggered automated process, i.e. cron job. + StartReasonScheduled = "scheduled" ) // User is the user object @@ -254,6 +263,7 @@ type PipelineRun struct { ID int `json:"id"` PipelineID int `json:"pipelineid"` StartDate time.Time `json:"startdate,omitempty"` + StartReason string `json:"started_reason"` FinishDate time.Time `json:"finishdate,omitempty"` ScheduleDate time.Time `json:"scheduledate,omitempty"` Status PipelineRunStatus `json:"status,omitempty"` diff --git a/handlers/pipeline.go b/handlers/pipeline.go index 7e09a1e8..7b5babba 100644 --- a/handlers/pipeline.go +++ b/handlers/pipeline.go @@ -196,7 +196,7 @@ func PipelineUpdate(c echo.Context) error { // Iterate over all cron schedules. for _, schedule := range p.PeriodicSchedules { err := foundPipeline.CronInst.AddFunc(schedule, func() { - _, err := schedulerService.SchedulePipeline(&foundPipeline, []*gaia.Argument{}) + _, err := schedulerService.SchedulePipeline(&foundPipeline, gaia.StartReasonScheduled, []*gaia.Argument{}) if err != nil { gaia.Cfg.Logger.Error("cannot schedule pipeline from periodic schedule", "error", err, "pipeline", foundPipeline) return @@ -342,7 +342,7 @@ func PipelineTrigger(c echo.Context) error { schedulerService, _ := services.SchedulerService() var args []*gaia.Argument _ = c.Bind(&args) - pipelineRun, err := schedulerService.SchedulePipeline(&foundPipeline, args) + pipelineRun, err := schedulerService.SchedulePipeline(&foundPipeline, gaia.StartReasonRemote, args) if err != nil { return c.String(http.StatusBadRequest, err.Error()) } else if pipelineRun != nil { @@ -456,7 +456,7 @@ func PipelineStart(c echo.Context) error { foundPipeline.Docker = docker if foundPipeline.Name != "" { - pipelineRun, err := schedulerService.SchedulePipeline(&foundPipeline, args) + pipelineRun, err := schedulerService.SchedulePipeline(&foundPipeline, gaia.StartReasonManual, args) if err != nil { return c.String(http.StatusBadRequest, err.Error()) } else if pipelineRun != nil { diff --git a/handlers/pipeline_test.go b/handlers/pipeline_test.go index 1ea5948e..14bdc851 100644 --- a/handlers/pipeline_test.go +++ b/handlers/pipeline_test.go @@ -28,7 +28,7 @@ type mockScheduleService struct { err error } -func (ms *mockScheduleService) SchedulePipeline(p *gaia.Pipeline, args []*gaia.Argument) (*gaia.PipelineRun, error) { +func (ms *mockScheduleService) SchedulePipeline(p *gaia.Pipeline, startReason string, args []*gaia.Argument) (*gaia.PipelineRun, error) { return ms.pipelineRun, ms.err } @@ -442,7 +442,7 @@ func TestPipelineStart(t *testing.T) { t.Fatalf("expected response code %v got %v", http.StatusCreated, rec.Code) } - expectedBody := `{"uniqueid":"","id":999,"pipelineid":0,"startdate":"0001-01-01T00:00:00Z","finishdate":"0001-01-01T00:00:00Z","scheduledate":"0001-01-01T00:00:00Z"} + expectedBody := `{"uniqueid":"","id":999,"pipelineid":0,"startdate":"0001-01-01T00:00:00Z","started_reason":"","finishdate":"0001-01-01T00:00:00Z","scheduledate":"0001-01-01T00:00:00Z"} ` body, _ := ioutil.ReadAll(rec.Body) if string(body) != expectedBody { diff --git a/workers/agent/agent_test.go b/workers/agent/agent_test.go index c9db855e..978d852f 100644 --- a/workers/agent/agent_test.go +++ b/workers/agent/agent_test.go @@ -42,7 +42,7 @@ type mockScheduler struct { func (ms *mockScheduler) SetPipelineJobs(p *gaia.Pipeline) error { return ms.err } func (ms *mockScheduler) GetFreeWorkers() int32 { return int32(0) } -func (ms *mockScheduler) SchedulePipeline(p *gaia.Pipeline, args []*gaia.Argument) (*gaia.PipelineRun, error) { +func (ms *mockScheduler) SchedulePipeline(p *gaia.Pipeline, startedBy string, args []*gaia.Argument) (*gaia.PipelineRun, error) { return nil, ms.err } diff --git a/workers/pipeline/create_pipeline_test.go b/workers/pipeline/create_pipeline_test.go index bf8f8392..a89b0118 100644 --- a/workers/pipeline/create_pipeline_test.go +++ b/workers/pipeline/create_pipeline_test.go @@ -32,7 +32,7 @@ type mockScheduler struct { } func (ms *mockScheduler) Init() {} -func (ms *mockScheduler) SchedulePipeline(p *gaia.Pipeline, args []*gaia.Argument) (*gaia.PipelineRun, error) { +func (ms *mockScheduler) SchedulePipeline(p *gaia.Pipeline, startedBy string, args []*gaia.Argument) (*gaia.PipelineRun, error) { return nil, nil } func (ms *mockScheduler) SetPipelineJobs(p *gaia.Pipeline) error { return ms.Error } diff --git a/workers/pipeline/ticker.go b/workers/pipeline/ticker.go index 5dd44318..812d4fcd 100644 --- a/workers/pipeline/ticker.go +++ b/workers/pipeline/ticker.go @@ -217,7 +217,7 @@ func checkActivePipelines() { // Iterate over all cron schedules. for _, schedule := range pipeline.PeriodicSchedules { err := pipeline.CronInst.AddFunc(schedule, func() { - _, err := schedulerService.SchedulePipeline(pipeline, []*gaia.Argument{}) + _, err := schedulerService.SchedulePipeline(pipeline, gaia.StartReasonScheduled, []*gaia.Argument{}) if err != nil { gaia.Cfg.Logger.Error("cannot schedule pipeline from periodic schedule", "error", err, "pipeline", pipeline) return diff --git a/workers/scheduler/scheduler.go b/workers/scheduler/scheduler.go index 28f56327..0c730f2a 100644 --- a/workers/scheduler/scheduler.go +++ b/workers/scheduler/scheduler.go @@ -62,7 +62,7 @@ var ( // GaiaScheduler is a job scheduler for gaia pipeline runs. type GaiaScheduler interface { Init() - SchedulePipeline(p *gaia.Pipeline, args []*gaia.Argument) (*gaia.PipelineRun, error) + SchedulePipeline(p *gaia.Pipeline, startedBy string, args []*gaia.Argument) (*gaia.PipelineRun, error) SetPipelineJobs(p *gaia.Pipeline) error StopPipelineRun(p *gaia.Pipeline, runID int) error GetFreeWorkers() int32 @@ -368,7 +368,7 @@ var schedulerLock = sync.RWMutex{} // SchedulePipeline schedules a pipeline. We create a new schedule object // and save it in our store. The scheduler will later pick this up and will continue the work. -func (s *Scheduler) SchedulePipeline(p *gaia.Pipeline, args []*gaia.Argument) (*gaia.PipelineRun, error) { +func (s *Scheduler) SchedulePipeline(p *gaia.Pipeline, startedReason string, args []*gaia.Argument) (*gaia.PipelineRun, error) { // Introduce a semaphore locking here because this function can be called // in parallel if multiple users happen to trigger a pipeline run at the same time. @@ -443,6 +443,7 @@ func (s *Scheduler) SchedulePipeline(p *gaia.Pipeline, args []*gaia.Argument) (* PipelineType: p.Type, PipelineTags: p.Tags, Docker: p.Docker, + StartReason: startedReason, } // Put run into store diff --git a/workers/scheduler/scheduler_test.go b/workers/scheduler/scheduler_test.go index 9351e1ea..ba7d6dfb 100644 --- a/workers/scheduler/scheduler_test.go +++ b/workers/scheduler/scheduler_test.go @@ -407,7 +407,7 @@ func TestSchedulePipeline(t *testing.T) { t.Fatal(err) } s.Init() - _, err = s.SchedulePipeline(&p, prepareArgs()) + _, err = s.SchedulePipeline(&p, gaia.StartReasonManual, prepareArgs()) if err != nil { t.Fatal(err) } @@ -453,11 +453,11 @@ func TestSchedulePipelineParallel(t *testing.T) { var wg sync.WaitGroup wg.Add(2) go func() { - run1, _ = s.SchedulePipeline(&p1, prepareArgs()) + run1, _ = s.SchedulePipeline(&p1, gaia.StartReasonManual, prepareArgs()) wg.Done() }() go func() { - run2, _ = s.SchedulePipeline(&p2, prepareArgs()) + run2, _ = s.SchedulePipeline(&p2, gaia.StartReasonManual, prepareArgs()) wg.Done() }() wg.Wait() @@ -488,7 +488,7 @@ func TestSchedule(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = s.SchedulePipeline(&p, prepareArgs()) + _, err = s.SchedulePipeline(&p, gaia.StartReasonManual, prepareArgs()) if err != nil { t.Fatal(err) }