Fix error fetching non-existing log tail

A task can exist in the database but not have any log stored on disk yet.
This is now returned as `204 No Content` instead of an internal server
error.

The web interface is also adjusted to cope with this.
This commit is contained in:
Sybren A. Stüvel 2022-05-30 19:23:10 +02:00
parent a3d885e710
commit ce07a46455
4 changed files with 64 additions and 2 deletions

View File

@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"net/http"
"os"
"github.com/labstack/echo/v4"
@ -197,13 +198,23 @@ func (f *Flamenco) FetchTaskLogTail(e echo.Context, taskID string) error {
logger.Error().Err(err).Msg("error fetching task")
return sendAPIError(e, http.StatusInternalServerError, "error fetching task: %v", err)
}
logger = logger.With().Str("job", dbTask.Job.UUID).Logger()
tail, err := f.logStorage.Tail(dbTask.Job.UUID, taskID)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
logger.Debug().Msg("task tail unavailable, task has no log on disk")
return e.NoContent(http.StatusNoContent)
}
logger.Error().Err(err).Msg("unable to fetch task log tail")
return sendAPIError(e, http.StatusInternalServerError, "error fetching task log tail: %v", err)
}
if tail == "" {
logger.Debug().Msg("task tail unavailable, on-disk task log is empty")
return e.NoContent(http.StatusNoContent)
}
logger.Debug().Msg("fetched task tail")
return e.String(http.StatusOK, tail)
}

View File

@ -4,7 +4,9 @@ package api_impl
import (
"errors"
"fmt"
"net/http"
"os"
"testing"
"time"
@ -187,3 +189,46 @@ func TestSetJobStatus_happy(t *testing.T) {
assertResponseEmpty(t, echoCtx)
}
func TestFetchTaskLogTail(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
mf := newMockedFlamenco(mockCtrl)
jobID := "18a9b096-d77e-438c-9be2-74397038298b"
taskID := "2e020eee-20f8-4e95-8dcf-65f7dfc3ebab"
dbJob := persistence.Job{
UUID: jobID,
Name: "test job",
Status: api.JobStatusActive,
Settings: persistence.StringInterfaceMap{},
Metadata: persistence.StringStringMap{},
}
dbTask := persistence.Task{
UUID: taskID,
Job: &dbJob,
Name: "test task",
}
// The task can be found, but has no on-disk task log.
// This should not cause any error, but instead be returned as "no content".
mf.persistence.EXPECT().FetchTask(gomock.Any(), taskID).Return(&dbTask, nil)
mf.logStorage.EXPECT().Tail(jobID, taskID).
Return("", fmt.Errorf("wrapped error: %w", os.ErrNotExist))
echoCtx := mf.prepareMockedRequest(nil)
err := mf.flamenco.FetchTaskLogTail(echoCtx, taskID)
assert.NoError(t, err)
assertResponseEmpty(t, echoCtx)
// Check that a 204 No Content is also returned when the task log file on disk exists, but is empty.
mf.persistence.EXPECT().FetchTask(gomock.Any(), taskID).Return(&dbTask, nil)
mf.logStorage.EXPECT().Tail(jobID, taskID).
Return("", fmt.Errorf("wrapped error: %w", os.ErrNotExist))
echoCtx = mf.prepareMockedRequest(nil)
err = mf.flamenco.FetchTaskLogTail(echoCtx, taskID)
assert.NoError(t, err)
assertResponseEmpty(t, echoCtx)
}

View File

@ -84,9 +84,13 @@ func TestLogTail(t *testing.T) {
jobID := "25c5a51c-e0dd-44f7-9f87-74f3d1fbbd8c"
taskID := "20ff9d06-53ec-4019-9e2e-1774f05f170a"
err := s.Write(zerolog.Nop(), jobID, taskID, "Just a single line")
assert.NoError(t, err)
contents, err := s.Tail(jobID, taskID)
assert.ErrorIs(t, err, os.ErrNotExist)
assert.Equal(t, "", contents)
err = s.Write(zerolog.Nop(), jobID, taskID, "Just a single line")
assert.NoError(t, err)
contents, err = s.Tail(jobID, taskID)
assert.NoError(t, err)
assert.Equal(t, "Just a single line\n", string(contents))

View File

@ -50,6 +50,8 @@ export const useTaskLog = defineStore('taskLog', {
* @param {string} logChunk
*/
addChunk(logChunk) {
if (!logChunk) return;
const lines = logChunk.trimEnd().split('\n');
if (lines.length == 0)
return;