From 7a60bb70e01b0527fd335ef749d1802ceb549c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 20 Oct 2022 13:13:35 +0200 Subject: [PATCH] Manager: implement job check endpoint --- internal/manager/api_impl/jobs.go | 69 +++++++++++++++++++------- internal/manager/api_impl/jobs_test.go | 46 +++++++++++++++++ 2 files changed, 98 insertions(+), 17 deletions(-) diff --git a/internal/manager/api_impl/jobs.go b/internal/manager/api_impl/jobs.go index 88061921..63d2a906 100644 --- a/internal/manager/api_impl/jobs.go +++ b/internal/manager/api_impl/jobs.go @@ -3,6 +3,7 @@ package api_impl // SPDX-License-Identifier: GPL-3.0-or-later import ( + "context" "errors" "fmt" "math" @@ -76,25 +77,10 @@ func (f *Flamenco) SubmitJob(e echo.Context) error { logger.Info().Msg("new Flamenco job received") ctx := e.Request().Context() - submittedJob := api.SubmittedJob(job) - - // Replace the special "manager" platform with the Manager's actual platform. - if submittedJob.SubmitterPlatform == "manager" { - submittedJob.SubmitterPlatform = runtime.GOOS - } - - if submittedJob.TypeEtag == nil || *submittedJob.TypeEtag == "" { - logger.Warn().Msg("job submitted without job type etag, refresh the job types in the Blender add-on") - } - - // Before compiling the job, replace the two-way variables. This ensures all - // the tasks also use those. - replaceTwoWayVariables(f.config, &submittedJob) - - authoredJob, err := f.jobCompiler.Compile(ctx, submittedJob) + authoredJob, err := f.compileSubmittedJob(ctx, logger, api.SubmittedJob(job)) switch { case errors.Is(err, job_compilers.ErrJobTypeBadEtag): - logger.Warn().Err(err).Msg("rejecting submitted job because its settings are outdated, refresh the job type") + logger.Info().Err(err).Msg("rejecting submitted job because its settings are outdated, refresh the job type") return sendAPIError(e, http.StatusPreconditionFailed, "rejecting job because its settings are outdated, refresh the job type") case err != nil: logger.Warn().Err(err).Msg("error compiling job") @@ -125,6 +111,55 @@ func (f *Flamenco) SubmitJob(e echo.Context) error { return e.JSON(http.StatusOK, apiJob) } +func (f *Flamenco) SubmitJobCheck(e echo.Context) error { + logger := requestLogger(e) + + var job api.SubmitJobCheckJSONRequestBody + if err := e.Bind(&job); err != nil { + logger.Warn().Err(err).Msg("bad request received") + return sendAPIError(e, http.StatusBadRequest, "invalid format") + } + + logger = logger.With(). + Str("type", job.Type). + Str("name", job.Name). + Logger() + logger.Info().Msg("checking Flamenco job") + + ctx := e.Request().Context() + submittedJob := api.SubmittedJob(job) + _, err := f.compileSubmittedJob(ctx, logger, submittedJob) + switch { + case errors.Is(err, job_compilers.ErrJobTypeBadEtag): + logger.Warn().Err(err).Msg("rejecting submitted job because its settings are outdated, refresh the job type") + return sendAPIError(e, http.StatusPreconditionFailed, "rejecting job because its settings are outdated, refresh the job type") + case err != nil: + logger.Warn().Err(err).Msg("error compiling job") + // TODO: make this a more specific error object for this API call. + return sendAPIError(e, http.StatusBadRequest, err.Error()) + } + + return e.NoContent(http.StatusNoContent) +} + +// compileSubmittedJob performs some processing of the job and compiles it. +func (f *Flamenco) compileSubmittedJob(ctx context.Context, logger zerolog.Logger, submittedJob api.SubmittedJob) (*job_compilers.AuthoredJob, error) { + // Replace the special "manager" platform with the Manager's actual platform. + if submittedJob.SubmitterPlatform == "manager" { + submittedJob.SubmitterPlatform = runtime.GOOS + } + + if submittedJob.TypeEtag == nil || *submittedJob.TypeEtag == "" { + logger.Warn().Msg("job submitted without job type etag, refresh the job types in the Blender add-on") + } + + // Before compiling the job, replace the two-way variables. This ensures all + // the tasks also use those. + replaceTwoWayVariables(f.config, &submittedJob) + + return f.jobCompiler.Compile(ctx, submittedJob) +} + // SetJobStatus is used by the web interface to change a job's status. func (f *Flamenco) SetJobStatus(e echo.Context, jobID string) error { logger := requestLogger(e) diff --git a/internal/manager/api_impl/jobs_test.go b/internal/manager/api_impl/jobs_test.go index eef218af..5df46825 100644 --- a/internal/manager/api_impl/jobs_test.go +++ b/internal/manager/api_impl/jobs_test.go @@ -283,6 +283,52 @@ func TestGetJobTypeUnknown(t *testing.T) { }) } +func TestSubmitJobCheckWithEtag(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + mf := newMockedFlamenco(mockCtrl) + + submittedJob := api.SubmittedJob{ + Name: "поднео посао", + Type: "test", + Priority: 50, + SubmitterPlatform: "linux", + TypeEtag: ptr("bad etag"), + } + + mf.jobCompiler.EXPECT().Compile(gomock.Any(), submittedJob). + Return(nil, job_compilers.ErrJobTypeBadEtag) + mf.expectConvertTwoWayVariables(t, config.VariableAudienceWorkers, "linux", map[string]string{}).AnyTimes() + + // Expect the job to be rejected. + { + echoCtx := mf.prepareMockedJSONRequest(submittedJob) + err := mf.flamenco.SubmitJobCheck(echoCtx) + assert.NoError(t, err) + assertResponseAPIError(t, echoCtx, + http.StatusPreconditionFailed, "rejecting job because its settings are outdated, refresh the job type") + } + + // Expect the job compiler to be called. + authoredJob := job_compilers.AuthoredJob{ + JobID: "afc47568-bd9d-4368-8016-e91d945db36d", + Name: submittedJob.Name, + JobType: submittedJob.Type, + Priority: submittedJob.Priority, + Status: api.JobStatusUnderConstruction, + Created: mf.clock.Now(), + } + mf.jobCompiler.EXPECT().Compile(gomock.Any(), gomock.Any()).Return(&authoredJob, nil) + + { // Expect the job with the right etag to be accepted. + submittedJob.TypeEtag = ptr("correct etag") + echoCtx := mf.prepareMockedJSONRequest(submittedJob) + err := mf.flamenco.SubmitJobCheck(echoCtx) + assert.NoError(t, err) + } +} + func TestGetJobTypeError(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish()