diff --git a/internal/manager/api_impl/worker_mgt.go b/internal/manager/api_impl/worker_mgt.go index f792d7f3..6eab634e 100644 --- a/internal/manager/api_impl/worker_mgt.go +++ b/internal/manager/api_impl/worker_mgt.go @@ -3,9 +3,11 @@ package api_impl // SPDX-License-Identifier: GPL-3.0-or-later import ( + "errors" "net/http" "git.blender.org/flamenco/internal/manager/persistence" + "git.blender.org/flamenco/internal/uuid" "git.blender.org/flamenco/pkg/api" "github.com/labstack/echo/v4" ) @@ -26,6 +28,29 @@ func (f *Flamenco) FetchWorkers(e echo.Context) error { }) } +func (f *Flamenco) FetchWorker(e echo.Context, workerUUID string) error { + logger := requestLogger(e) + logger = logger.With().Str("worker", workerUUID).Logger() + + if !uuid.IsValid(workerUUID) { + return sendAPIError(e, http.StatusBadRequest, "not a valid UUID") + } + + dbWorker, err := f.persist.FetchWorker(e.Request().Context(), workerUUID) + if errors.Is(err, persistence.ErrWorkerNotFound) { + logger.Debug().Msg("non-existent worker requested") + return sendAPIError(e, http.StatusNotFound, "worker %q not found", workerUUID) + } + if err != nil { + logger.Error().Err(err).Msg("error fetching worker") + return sendAPIError(e, http.StatusInternalServerError, "error fetching worker: %v", err) + } + + logger.Debug().Msg("fetched worker") + apiWorker := workerDBtoAPI(dbWorker) + return e.JSON(http.StatusOK, apiWorker) +} + func workerSummary(w persistence.Worker) api.WorkerSummary { summary := api.WorkerSummary{ Id: w.UUID, @@ -37,3 +62,21 @@ func workerSummary(w persistence.Worker) api.WorkerSummary { } return summary } + +func workerDBtoAPI(dbWorker *persistence.Worker) api.Worker { + apiWorker := api.Worker{ + Id: dbWorker.UUID, + IpAddress: dbWorker.Address, + Nickname: dbWorker.Name, + Platform: dbWorker.Platform, + Status: dbWorker.Status, + SupportedTaskTypes: dbWorker.TaskTypes(), + Version: dbWorker.Software, + } + + if dbWorker.StatusRequested != "" { + apiWorker.StatusRequested = &dbWorker.StatusRequested + } + + return apiWorker +} diff --git a/internal/manager/api_impl/worker_mgt_test.go b/internal/manager/api_impl/worker_mgt_test.go index 749dcd83..2a2d52bf 100644 --- a/internal/manager/api_impl/worker_mgt_test.go +++ b/internal/manager/api_impl/worker_mgt_test.go @@ -3,6 +3,8 @@ package api_impl // SPDX-License-Identifier: GPL-3.0-or-later import ( + "errors" + "fmt" "net/http" "testing" @@ -53,3 +55,63 @@ func TestFetchWorkers(t *testing.T) { resp := getRecordedResponse(echo) assert.Equal(t, http.StatusOK, resp.StatusCode) } + +func TestFetchWorker(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + mf := newMockedFlamenco(mockCtrl) + worker := testWorker() + workerUUID := worker.UUID + + // Test without worker in the database. + mf.persistence.EXPECT().FetchWorker(gomock.Any(), workerUUID). + Return(nil, fmt.Errorf("wrapped: %w", persistence.ErrWorkerNotFound)) + echo := mf.prepareMockedRequest(nil) + err := mf.flamenco.FetchWorker(echo, workerUUID) + assert.NoError(t, err) + assertResponseAPIError(t, echo, http.StatusNotFound, fmt.Sprintf("worker %q not found", workerUUID)) + + // Test database error fetching worker. + mf.persistence.EXPECT().FetchWorker(gomock.Any(), workerUUID). + Return(nil, errors.New("some unknown error")) + echo = mf.prepareMockedRequest(nil) + err = mf.flamenco.FetchWorker(echo, workerUUID) + assert.NoError(t, err) + assertResponseAPIError(t, echo, http.StatusInternalServerError, "error fetching worker: some unknown error") + + // Test with worker that doesn't have a status change requested. + mf.persistence.EXPECT().FetchWorker(gomock.Any(), workerUUID).Return(&worker, nil) + + echo = mf.prepareMockedRequest(nil) + err = mf.flamenco.FetchWorker(echo, workerUUID) + assert.NoError(t, err) + assertResponseJSON(t, echo, http.StatusOK, api.Worker{ + Id: workerUUID, + Nickname: "дрон", + IpAddress: "fe80::5054:ff:fede:2ad7", + Platform: "linux", + Version: "3.0", + Status: api.WorkerStatusAwake, + SupportedTaskTypes: []string{"blender", "ffmpeg", "file-management", "misc"}, + }) + + // Test with worker that does have a status change requested. + requestedStatus := api.WorkerStatusAsleep + worker.StatusRequested = requestedStatus + mf.persistence.EXPECT().FetchWorker(gomock.Any(), workerUUID).Return(&worker, nil) + + echo = mf.prepareMockedRequest(nil) + err = mf.flamenco.FetchWorker(echo, worker.UUID) + assert.NoError(t, err) + assertResponseJSON(t, echo, http.StatusOK, api.Worker{ + Id: workerUUID, + Nickname: "дрон", + IpAddress: "fe80::5054:ff:fede:2ad7", + Platform: "linux", + Version: "3.0", + Status: api.WorkerStatusAwake, + StatusRequested: &requestedStatus, + SupportedTaskTypes: []string{"blender", "ffmpeg", "file-management", "misc"}, + }) +}