From 08676f48f48a1390bd81b9420b6e83afda3c1ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 30 May 2022 18:48:52 +0200 Subject: [PATCH] Manager: implement `fetchWorkers` OpenAPI operation --- internal/manager/api_impl/api_impl.go | 1 + .../api_impl/mocks/api_impl_mock.gen.go | 15 +++++ internal/manager/api_impl/worker_mgt.go | 39 +++++++++++++ internal/manager/api_impl/worker_mgt_test.go | 55 +++++++++++++++++++ internal/manager/persistence/workers.go | 9 +++ internal/manager/persistence/workers_test.go | 49 +++++++++++++++++ 6 files changed, 168 insertions(+) create mode 100644 internal/manager/api_impl/worker_mgt.go create mode 100644 internal/manager/api_impl/worker_mgt_test.go diff --git a/internal/manager/api_impl/api_impl.go b/internal/manager/api_impl/api_impl.go index 8196b6bf..6e2e08e7 100644 --- a/internal/manager/api_impl/api_impl.go +++ b/internal/manager/api_impl/api_impl.go @@ -54,6 +54,7 @@ type PersistenceService interface { CreateWorker(ctx context.Context, w *persistence.Worker) error FetchWorker(ctx context.Context, uuid string) (*persistence.Worker, error) + FetchWorkers(ctx context.Context) ([]*persistence.Worker, error) SaveWorker(ctx context.Context, w *persistence.Worker) error SaveWorkerStatus(ctx context.Context, w *persistence.Worker) error diff --git a/internal/manager/api_impl/mocks/api_impl_mock.gen.go b/internal/manager/api_impl/mocks/api_impl_mock.gen.go index b9792088..9460b85c 100644 --- a/internal/manager/api_impl/mocks/api_impl_mock.gen.go +++ b/internal/manager/api_impl/mocks/api_impl_mock.gen.go @@ -114,6 +114,21 @@ func (mr *MockPersistenceServiceMockRecorder) FetchWorker(arg0, arg1 interface{} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchWorker", reflect.TypeOf((*MockPersistenceService)(nil).FetchWorker), arg0, arg1) } +// FetchWorkers mocks base method. +func (m *MockPersistenceService) FetchWorkers(arg0 context.Context) ([]*persistence.Worker, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FetchWorkers", arg0) + ret0, _ := ret[0].([]*persistence.Worker) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FetchWorkers indicates an expected call of FetchWorkers. +func (mr *MockPersistenceServiceMockRecorder) FetchWorkers(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchWorkers", reflect.TypeOf((*MockPersistenceService)(nil).FetchWorkers), arg0) +} + // QueryJobTaskSummaries mocks base method. func (m *MockPersistenceService) QueryJobTaskSummaries(arg0 context.Context, arg1 string) ([]*persistence.Task, error) { m.ctrl.T.Helper() diff --git a/internal/manager/api_impl/worker_mgt.go b/internal/manager/api_impl/worker_mgt.go new file mode 100644 index 00000000..f792d7f3 --- /dev/null +++ b/internal/manager/api_impl/worker_mgt.go @@ -0,0 +1,39 @@ +package api_impl + +// SPDX-License-Identifier: GPL-3.0-or-later + +import ( + "net/http" + + "git.blender.org/flamenco/internal/manager/persistence" + "git.blender.org/flamenco/pkg/api" + "github.com/labstack/echo/v4" +) + +func (f *Flamenco) FetchWorkers(e echo.Context) error { + dbWorkers, err := f.persist.FetchWorkers(e.Request().Context()) + if err != nil { + return sendAPIError(e, http.StatusInternalServerError, "error fetching workers: %v", err) + } + + apiWorkers := make([]api.WorkerSummary, len(dbWorkers)) + for i := range dbWorkers { + apiWorkers[i] = workerSummary(*dbWorkers[i]) + } + + return e.JSON(http.StatusOK, api.WorkerList{ + Workers: apiWorkers, + }) +} + +func workerSummary(w persistence.Worker) api.WorkerSummary { + summary := api.WorkerSummary{ + Id: w.UUID, + Nickname: w.Name, + Status: w.Status, + } + if w.StatusRequested != "" { + summary.StatusRequested = &w.StatusRequested + } + return summary +} diff --git a/internal/manager/api_impl/worker_mgt_test.go b/internal/manager/api_impl/worker_mgt_test.go new file mode 100644 index 00000000..749dcd83 --- /dev/null +++ b/internal/manager/api_impl/worker_mgt_test.go @@ -0,0 +1,55 @@ +package api_impl + +// SPDX-License-Identifier: GPL-3.0-or-later + +import ( + "net/http" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + + "git.blender.org/flamenco/internal/manager/persistence" + "git.blender.org/flamenco/pkg/api" +) + +func TestFetchWorkers(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + mf := newMockedFlamenco(mockCtrl) + worker1 := testWorker() + worker2 := worker1 + worker2.ID = 4 + worker2.UUID = "f07b6d53-16ec-40a8-a7b4-a9cc8547f790" + worker2.Status = api.WorkerStatusAwake + worker2.StatusRequested = api.WorkerStatusAsleep + + mf.persistence.EXPECT().FetchWorkers(gomock.Any()). + Return([]*persistence.Worker{&worker1, &worker2}, nil) + + echo := mf.prepareMockedRequest(nil) + err := mf.flamenco.FetchWorkers(echo) + assert.NoError(t, err) + + // Check the response + workers := api.WorkerList{ + Workers: []api.WorkerSummary{ + { + Id: worker1.UUID, + Nickname: worker1.Name, + Status: worker1.Status, + StatusRequested: nil, + }, + { + Id: worker2.UUID, + Nickname: worker2.Name, + Status: worker2.Status, + StatusRequested: &worker2.StatusRequested, + }, + }, + } + assertResponseJSON(t, echo, http.StatusOK, workers) + resp := getRecordedResponse(echo) + assert.Equal(t, http.StatusOK, resp.StatusCode) +} diff --git a/internal/manager/persistence/workers.go b/internal/manager/persistence/workers.go index 6b8f3178..82c8cdad 100644 --- a/internal/manager/persistence/workers.go +++ b/internal/manager/persistence/workers.go @@ -50,6 +50,15 @@ func (db *DB) FetchWorker(ctx context.Context, uuid string) (*Worker, error) { return &w, nil } +func (db *DB) FetchWorkers(ctx context.Context) ([]*Worker, error) { + workers := make([]*Worker, 0) + tx := db.gormDB.WithContext(ctx).Model(&Worker{}).Scan(&workers) + if tx.Error != nil { + return nil, tx.Error + } + return workers, nil +} + func (db *DB) SaveWorkerStatus(ctx context.Context, w *Worker) error { err := db.gormDB.WithContext(ctx). Model(w). diff --git a/internal/manager/persistence/workers_test.go b/internal/manager/persistence/workers_test.go index 228c0701..1eacda38 100644 --- a/internal/manager/persistence/workers_test.go +++ b/internal/manager/persistence/workers_test.go @@ -103,3 +103,52 @@ func TestSaveWorker(t *testing.T) { assert.Equal(t, updatedWorker.Name, fetchedWorker.Name, "non-status fields should also have been updated") assert.Equal(t, updatedWorker.Software, fetchedWorker.Software, "non-status fields should also have been updated") } + +func TestFetchWorkers(t *testing.T) { + ctx, cancel, db := persistenceTestFixtures(t, 1*time.Second) + defer cancel() + + // No workers + workers, err := db.FetchWorkers(ctx) + if !assert.NoError(t, err) { + t.Fatal("error fetching empty list of workers, no use in continuing the test") + } + assert.Empty(t, workers) + + linuxWorker := Worker{ + UUID: uuid.New(), + Name: "дрон", + Address: "fe80::5054:ff:fede:2ad7", + LastActivity: "", + Platform: "linux", + Software: "3.0", + Status: api.WorkerStatusAwake, + SupportedTaskTypes: "blender,ffmpeg,file-management", + } + + // One worker: + err = db.CreateWorker(ctx, &linuxWorker) + assert.NoError(t, err) + + workers, err = db.FetchWorkers(ctx) + assert.NoError(t, err) + assert.Equal(t, []*Worker{&linuxWorker}, workers) + + // Two workers: + windowsWorker := Worker{ + UUID: uuid.New(), + Name: "очиститель окон", + Address: "fe80::c000:d000:::3", + LastActivity: "nothing", + Platform: "windows", + Software: "3.0", + Status: api.WorkerStatusOffline, + SupportedTaskTypes: "blender,ffmpeg,file-management", + } + err = db.CreateWorker(ctx, &windowsWorker) + assert.NoError(t, err) + + workers, err = db.FetchWorkers(ctx) + assert.NoError(t, err) + assert.Equal(t, []*Worker{&linuxWorker, &windowsWorker}, workers) +}