diff --git a/go.mod b/go.mod index cb47b270..f36c7f12 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7 github.com/getkin/kin-openapi v0.88.0 github.com/gofrs/uuid v4.0.0+incompatible // indirect + github.com/golang/mock v1.6.0 // indirect github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 // indirect github.com/graarh/golang-socketio v0.0.0-20170510162725-2c44953b9b5f diff --git a/go.sum b/go.sum index cbe3d6ca..37fb582d 100644 --- a/go.sum +++ b/go.sum @@ -52,6 +52,8 @@ github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPh github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -227,6 +229,7 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/ziflex/lecho/v3 v3.1.0 h1:65bSzSc0yw7EEhi44lMnkOI877ZzbE7tGDWfYCQXZwI= @@ -258,6 +261,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -266,6 +270,7 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211013171255-e13a2654a71e h1:Xj+JO91noE97IN6F/7WZxzC5QE6yENAQPrwIYhW3bsA= @@ -289,7 +294,9 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -320,6 +327,8 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/manager/api_impl/api_impl.go b/internal/manager/api_impl/api_impl.go index dd25efd4..b427839c 100644 --- a/internal/manager/api_impl/api_impl.go +++ b/internal/manager/api_impl/api_impl.go @@ -36,10 +36,14 @@ type Flamenco struct { persist PersistenceService } +// Generate mock implementations of these interfaces. +//go:generate go run github.com/golang/mock/mockgen -destination mocks/api_impl_mock.gen.go -package mocks gitlab.com/blender/flamenco-ng-poc/internal/manager/api_impl PersistenceService,JobCompiler + type PersistenceService interface { StoreAuthoredJob(ctx context.Context, authoredJob job_compilers.AuthoredJob) error FetchJob(ctx context.Context, jobID string) (*persistence.Job, error) FetchTask(ctx context.Context, taskID string) (*persistence.Task, error) + SaveTask(ctx context.Context, task *persistence.Task) error CreateWorker(ctx context.Context, w *persistence.Worker) error FetchWorker(ctx context.Context, uuid string) (*persistence.Worker, error) diff --git a/internal/manager/api_impl/jobs.go b/internal/manager/api_impl/jobs.go index e96e97a5..5bfdeb05 100644 --- a/internal/manager/api_impl/jobs.go +++ b/internal/manager/api_impl/jobs.go @@ -21,12 +21,15 @@ package api_impl * ***** END GPL LICENSE BLOCK ***** */ import ( + "context" "fmt" "net/http" "github.com/google/uuid" "github.com/labstack/echo/v4" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "gitlab.com/blender/flamenco-ng-poc/internal/manager/persistence" "gitlab.com/blender/flamenco-ng-poc/pkg/api" ) @@ -126,6 +129,7 @@ func (f *Flamenco) FetchJob(e echo.Context, jobId string) error { func (f *Flamenco) TaskUpdate(e echo.Context, taskID string) error { logger := requestLogger(e) + worker := requestWorkerOrPanic(e) if _, err := uuid.Parse(taskID); err != nil { logger.Debug().Msg("invalid task ID received") @@ -140,22 +144,63 @@ func (f *Flamenco) TaskUpdate(e echo.Context, taskID string) error { logger.Warn().Err(err).Msg("cannot fetch task") return sendAPIError(e, http.StatusNotFound, fmt.Sprintf("task %+v not found", taskID)) } + if dbTask == nil { + panic("task could not be fetched, but database gave no error either") + } - worker := requestWorker(e) + // Decode the request body. + var taskUpdate api.TaskUpdateJSONRequestBody + if err := e.Bind(&taskUpdate); err != nil { + logger.Warn().Err(err).Msg("bad request received") + return sendAPIError(e, http.StatusBadRequest, "invalid format") + } if dbTask.Worker == nil { logger.Warn(). - Str("requestingWorkerID", worker.UUID). Msg("worker trying to update task that's not assigned to any worker") return sendAPIError(e, http.StatusConflict, fmt.Sprintf("task %+v is not assigned to any worker, so also not to you", taskID)) } if dbTask.Worker.UUID != worker.UUID { logger.Warn(). - Str("requestingWorkerID", worker.UUID). Str("assignedWorkerID", dbTask.Worker.UUID). Msg("worker trying to update task that's assigned to another worker") return sendAPIError(e, http.StatusConflict, fmt.Sprintf("task %+v is not assigned to you, but to worker %v", taskID, dbTask.Worker.UUID)) } - // TODO: actually handle the task update. + // TODO: check whether this task may undergo the requested status change. + + if err := f.doTaskUpdate(ctx, logger, worker, dbTask, taskUpdate); err != nil { + return sendAPIError(e, http.StatusInternalServerError, "unable to handle status update: %v", err) + } + return e.String(http.StatusNoContent, "") } + +func (f *Flamenco) doTaskUpdate( + ctx context.Context, + logger zerolog.Logger, + w *persistence.Worker, + dbTask *persistence.Task, + update api.TaskUpdateJSONRequestBody, +) error { + if update.TaskStatus != nil { + // TODO: check that this status transition is valid. + // TODO: process this status transition. + newStatus := string(*update.TaskStatus) + logger.Info(). + Str("oldStatus", dbTask.Status). + Str("newStatus", newStatus). + Msg("task changing status") + dbTask.Status = newStatus + } + + if update.Activity != nil { + dbTask.Worker.LastActivity = *update.Activity + } + + if update.Log != nil { + // TODO: write log to disk. + logger.Warn().Msg("task logs are not yet handled") + } + + return f.persist.SaveTask(ctx, dbTask) +} diff --git a/internal/manager/api_impl/jobs_test.go b/internal/manager/api_impl/jobs_test.go new file mode 100644 index 00000000..377e09f2 --- /dev/null +++ b/internal/manager/api_impl/jobs_test.go @@ -0,0 +1,75 @@ +package api_impl + +import ( + "context" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "gitlab.com/blender/flamenco-ng-poc/internal/manager/persistence" + "gitlab.com/blender/flamenco-ng-poc/pkg/api" +) + +/* ***** BEGIN GPL LICENSE BLOCK ***** + * + * Original Code Copyright (C) 2022 Blender Foundation. + * + * This file is part of Flamenco. + * + * Flamenco is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Flamenco is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Flamenco. If not, see . + * + * ***** END GPL LICENSE BLOCK ***** */ + +func TestTaskUpdate(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + mf := newMockedFlamenco(mockCtrl) + worker := testWorker() + + // Construct the JSON request object. + s := func(value string) *string { return &value } + ts := func(value api.TaskStatus) *api.TaskStatus { return &value } + taskUpdate := api.TaskUpdateJSONRequestBody{ + Activity: s("testing"), + Log: s("line1\nline2\n"), + TaskStatus: ts(api.TaskStatusFailed), + } + + // Construct the task that's supposed to be updated. + taskID := "181eab68-1123-4790-93b1-94309a899411" + mockTask := persistence.Task{ + UUID: taskID, + Worker: &worker, + WorkerID: &worker.ID, + } + + // Expect the task to be fetched. + mf.persistence.EXPECT().FetchTask(gomock.Any(), taskID).Return(&mockTask, nil) + + // Expect the task to be saved. + var savedTask persistence.Task + mf.persistence.EXPECT().SaveTask(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, task *persistence.Task) error { + savedTask = *task + return nil + }) + + // Do the call. + echoCtx := mf.prepareMockedJSONRequest(&worker, taskUpdate) + err := mf.flamenco.TaskUpdate(echoCtx, taskID) + + // Check the saved task. + assert.NoError(t, err) + assert.Equal(t, mockTask.UUID, savedTask.UUID) +} diff --git a/internal/manager/api_impl/mocks/api_impl_mock.gen.go b/internal/manager/api_impl/mocks/api_impl_mock.gen.go new file mode 100644 index 00000000..d0cd5baf --- /dev/null +++ b/internal/manager/api_impl/mocks/api_impl_mock.gen.go @@ -0,0 +1,220 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: gitlab.com/blender/flamenco-ng-poc/internal/manager/api_impl (interfaces: PersistenceService,JobCompiler) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + job_compilers "gitlab.com/blender/flamenco-ng-poc/internal/manager/job_compilers" + persistence "gitlab.com/blender/flamenco-ng-poc/internal/manager/persistence" + api "gitlab.com/blender/flamenco-ng-poc/pkg/api" +) + +// MockPersistenceService is a mock of PersistenceService interface. +type MockPersistenceService struct { + ctrl *gomock.Controller + recorder *MockPersistenceServiceMockRecorder +} + +// MockPersistenceServiceMockRecorder is the mock recorder for MockPersistenceService. +type MockPersistenceServiceMockRecorder struct { + mock *MockPersistenceService +} + +// NewMockPersistenceService creates a new mock instance. +func NewMockPersistenceService(ctrl *gomock.Controller) *MockPersistenceService { + mock := &MockPersistenceService{ctrl: ctrl} + mock.recorder = &MockPersistenceServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockPersistenceService) EXPECT() *MockPersistenceServiceMockRecorder { + return m.recorder +} + +// CreateWorker mocks base method. +func (m *MockPersistenceService) CreateWorker(arg0 context.Context, arg1 *persistence.Worker) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateWorker", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateWorker indicates an expected call of CreateWorker. +func (mr *MockPersistenceServiceMockRecorder) CreateWorker(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateWorker", reflect.TypeOf((*MockPersistenceService)(nil).CreateWorker), arg0, arg1) +} + +// FetchJob mocks base method. +func (m *MockPersistenceService) FetchJob(arg0 context.Context, arg1 string) (*persistence.Job, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FetchJob", arg0, arg1) + ret0, _ := ret[0].(*persistence.Job) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FetchJob indicates an expected call of FetchJob. +func (mr *MockPersistenceServiceMockRecorder) FetchJob(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchJob", reflect.TypeOf((*MockPersistenceService)(nil).FetchJob), arg0, arg1) +} + +// FetchTask mocks base method. +func (m *MockPersistenceService) FetchTask(arg0 context.Context, arg1 string) (*persistence.Task, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FetchTask", arg0, arg1) + ret0, _ := ret[0].(*persistence.Task) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FetchTask indicates an expected call of FetchTask. +func (mr *MockPersistenceServiceMockRecorder) FetchTask(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchTask", reflect.TypeOf((*MockPersistenceService)(nil).FetchTask), arg0, arg1) +} + +// FetchWorker mocks base method. +func (m *MockPersistenceService) FetchWorker(arg0 context.Context, arg1 string) (*persistence.Worker, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FetchWorker", arg0, arg1) + ret0, _ := ret[0].(*persistence.Worker) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FetchWorker indicates an expected call of FetchWorker. +func (mr *MockPersistenceServiceMockRecorder) FetchWorker(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchWorker", reflect.TypeOf((*MockPersistenceService)(nil).FetchWorker), arg0, arg1) +} + +// SaveTask mocks base method. +func (m *MockPersistenceService) SaveTask(arg0 context.Context, arg1 *persistence.Task) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SaveTask", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// SaveTask indicates an expected call of SaveTask. +func (mr *MockPersistenceServiceMockRecorder) SaveTask(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveTask", reflect.TypeOf((*MockPersistenceService)(nil).SaveTask), arg0, arg1) +} + +// SaveWorker mocks base method. +func (m *MockPersistenceService) SaveWorker(arg0 context.Context, arg1 *persistence.Worker) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SaveWorker", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// SaveWorker indicates an expected call of SaveWorker. +func (mr *MockPersistenceServiceMockRecorder) SaveWorker(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveWorker", reflect.TypeOf((*MockPersistenceService)(nil).SaveWorker), arg0, arg1) +} + +// SaveWorkerStatus mocks base method. +func (m *MockPersistenceService) SaveWorkerStatus(arg0 context.Context, arg1 *persistence.Worker) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SaveWorkerStatus", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// SaveWorkerStatus indicates an expected call of SaveWorkerStatus. +func (mr *MockPersistenceServiceMockRecorder) SaveWorkerStatus(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveWorkerStatus", reflect.TypeOf((*MockPersistenceService)(nil).SaveWorkerStatus), arg0, arg1) +} + +// ScheduleTask mocks base method. +func (m *MockPersistenceService) ScheduleTask(arg0 *persistence.Worker) (*persistence.Task, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ScheduleTask", arg0) + ret0, _ := ret[0].(*persistence.Task) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ScheduleTask indicates an expected call of ScheduleTask. +func (mr *MockPersistenceServiceMockRecorder) ScheduleTask(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScheduleTask", reflect.TypeOf((*MockPersistenceService)(nil).ScheduleTask), arg0) +} + +// StoreAuthoredJob mocks base method. +func (m *MockPersistenceService) StoreAuthoredJob(arg0 context.Context, arg1 job_compilers.AuthoredJob) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StoreAuthoredJob", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// StoreAuthoredJob indicates an expected call of StoreAuthoredJob. +func (mr *MockPersistenceServiceMockRecorder) StoreAuthoredJob(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreAuthoredJob", reflect.TypeOf((*MockPersistenceService)(nil).StoreAuthoredJob), arg0, arg1) +} + +// MockJobCompiler is a mock of JobCompiler interface. +type MockJobCompiler struct { + ctrl *gomock.Controller + recorder *MockJobCompilerMockRecorder +} + +// MockJobCompilerMockRecorder is the mock recorder for MockJobCompiler. +type MockJobCompilerMockRecorder struct { + mock *MockJobCompiler +} + +// NewMockJobCompiler creates a new mock instance. +func NewMockJobCompiler(ctrl *gomock.Controller) *MockJobCompiler { + mock := &MockJobCompiler{ctrl: ctrl} + mock.recorder = &MockJobCompilerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockJobCompiler) EXPECT() *MockJobCompilerMockRecorder { + return m.recorder +} + +// Compile mocks base method. +func (m *MockJobCompiler) Compile(arg0 context.Context, arg1 api.SubmittedJob) (*job_compilers.AuthoredJob, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Compile", arg0, arg1) + ret0, _ := ret[0].(*job_compilers.AuthoredJob) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Compile indicates an expected call of Compile. +func (mr *MockJobCompilerMockRecorder) Compile(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Compile", reflect.TypeOf((*MockJobCompiler)(nil).Compile), arg0, arg1) +} + +// ListJobTypes mocks base method. +func (m *MockJobCompiler) ListJobTypes() api.AvailableJobTypes { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListJobTypes") + ret0, _ := ret[0].(api.AvailableJobTypes) + return ret0 +} + +// ListJobTypes indicates an expected call of ListJobTypes. +func (mr *MockJobCompilerMockRecorder) ListJobTypes() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListJobTypes", reflect.TypeOf((*MockJobCompiler)(nil).ListJobTypes)) +} diff --git a/internal/manager/api_impl/test_support.go b/internal/manager/api_impl/test_support.go new file mode 100644 index 00000000..8e69364f --- /dev/null +++ b/internal/manager/api_impl/test_support.go @@ -0,0 +1,85 @@ +package api_impl + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + + "github.com/golang/mock/gomock" + "github.com/labstack/echo/v4" + "gitlab.com/blender/flamenco-ng-poc/internal/manager/api_impl/mocks" + "gitlab.com/blender/flamenco-ng-poc/internal/manager/persistence" + "gitlab.com/blender/flamenco-ng-poc/pkg/api" + "gorm.io/gorm" +) + +/* ***** BEGIN GPL LICENSE BLOCK ***** + * + * Original Code Copyright (C) 2022 Blender Foundation. + * + * This file is part of Flamenco. + * + * Flamenco is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Flamenco is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Flamenco. If not, see . + * + * ***** END GPL LICENSE BLOCK ***** */ + +type mockedFlamenco struct { + flamenco *Flamenco + jobCompiler *mocks.MockJobCompiler + persistence *mocks.MockPersistenceService +} + +func newMockedFlamenco(mockCtrl *gomock.Controller) mockedFlamenco { + jc := mocks.NewMockJobCompiler(mockCtrl) + ps := mocks.NewMockPersistenceService(mockCtrl) + f := NewFlamenco(jc, ps) + + return mockedFlamenco{ + flamenco: f, + jobCompiler: jc, + persistence: ps, + } +} + +func (mf *mockedFlamenco) prepareMockedJSONRequest(worker *persistence.Worker, requestBody interface{}) echo.Context { + + e := echo.New() + + bodyBytes, err := json.MarshalIndent(requestBody, "", " ") + if err != nil { + panic(err) + } + + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(bodyBytes)) + req.Header.Add(echo.HeaderContentType, "application/json") + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + requestWorkerStore(c, worker) + + return c +} + +func testWorker() persistence.Worker { + return persistence.Worker{ + Model: gorm.Model{ID: 1}, + UUID: "e7632d62-c3b8-4af0-9e78-01752928952c", + Name: "дрон", + Address: "fe80::5054:ff:fede:2ad7", + LastActivity: "", + Platform: "linux", + Software: "3.0", + Status: api.WorkerStatusAwake, + SupportedTaskTypes: "blender,ffmpeg,file-management,misc", + } +} diff --git a/internal/manager/api_impl/worker_auth.go b/internal/manager/api_impl/worker_auth.go index 4f3c9c2f..4b2f703f 100644 --- a/internal/manager/api_impl/worker_auth.go +++ b/internal/manager/api_impl/worker_auth.go @@ -72,13 +72,17 @@ func WorkerAuth(ctx context.Context, authInfo *openapi3filter.AuthenticationInpu return authInfo.NewError(errAuthBad) } - // Store the Worker in the request context, so that it doesn't need to be fetched again later. - reqCtx := context.WithValue(req.Context(), workerKey, w) - echo.SetRequest(req.WithContext(reqCtx)) - + requestWorkerStore(echo, w) return nil } +// Store the Worker in the request context, so that it doesn't need to be fetched again later. +func requestWorkerStore(e echo.Context, w *persistence.Worker) { + req := e.Request() + reqCtx := context.WithValue(req.Context(), workerKey, w) + e.SetRequest(req.WithContext(reqCtx)) +} + // requestWorker returns the Worker associated with this HTTP request, or nil if there is none. func requestWorker(e echo.Context) *persistence.Worker { ctx := e.Request().Context() diff --git a/internal/manager/persistence/jobs.go b/internal/manager/persistence/jobs.go index 6dd98c84..c6294826 100644 --- a/internal/manager/persistence/jobs.go +++ b/internal/manager/persistence/jobs.go @@ -211,3 +211,10 @@ func (db *DB) FetchTask(ctx context.Context, taskUUID string) (*Task, error) { return &dbTask, nil } + +func (db *DB) SaveTask(ctx context.Context, t *Task) error { + if err := db.gormDB.Save(t).Error; err != nil { + return fmt.Errorf("error saving task: %w", err) + } + return nil +}