diff --git a/.changeset/flat-kiwis-rush.md b/.changeset/flat-kiwis-rush.md new file mode 100644 index 0000000000..585b731eed --- /dev/null +++ b/.changeset/flat-kiwis-rush.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-scaffolder-node': patch +--- + +Fixing the lost of the initial state after a task recovery. diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts index fe9cf3c661..ab80dcff04 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts @@ -161,6 +161,44 @@ describe('DatabaseTaskStore', () => { expect(claimedTask.status).toBe('processing'); }); + it('should restore the state of the task after the task recovery', async () => { + const { store } = await createStore(); + const { taskId } = await store.createTask({ + spec: {} as TaskSpec, + createdBy: 'me', + }); + + const task = await store.getTask(taskId); + expect(task.status).toBe('open'); + await store.claimTask(); + + const state = { + state: { + checkpoints: { + 'v1.task.checkpoint.deploy.to.stg': { + status: 'success', + value: true, + }, + 'v1.task.checkpoint.deploy.to.pro': { + status: 'success', + value: true, + }, + }, + }, + }; + + await store.saveTaskState({ + taskId, + state, + }); + + await store.recoverTasks({ timeout: { milliseconds: 0 } }); + await store.claimTask(); + + const claimedTask = await store.getTask(taskId); + expect(claimedTask.state).toEqual({ state: state.state }); + }); + it('should shutdown the running task', async () => { const { store } = await createStore(); const { taskId } = await store.createTask({ diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts index 2a39440775..ac391ea2b6 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts @@ -212,6 +212,7 @@ export class DatabaseTaskStore implements TaskStore { try { const spec = JSON.parse(result.spec); const secrets = result.secrets ? JSON.parse(result.secrets) : undefined; + const state = result.state ? JSON.parse(result.state).state : undefined; return { id: result.id, spec, @@ -220,6 +221,7 @@ export class DatabaseTaskStore implements TaskStore { createdAt: parseSqlDateToIsoString(result.created_at), createdBy: result.created_by ?? undefined, secrets, + state, }; } catch (error) { throw new Error(`Failed to parse spec of task '${taskId}', ${error}`); @@ -268,6 +270,16 @@ export class DatabaseTaskStore implements TaskStore { return undefined; } + const getState = () => { + try { + return task.state ? JSON.parse(task.state).state : undefined; + } catch (error) { + throw new Error( + `Failed to parse state of the task '${task.id}', ${error}`, + ); + } + }; + const secrets = this.parseTaskSecrets(task); return { id: task.id, @@ -277,6 +289,7 @@ export class DatabaseTaskStore implements TaskStore { createdAt: task.created_at, createdBy: task.created_by ?? undefined, secrets, + state: getState(), }; }); } @@ -532,7 +545,7 @@ export class DatabaseTaskStore implements TaskStore { for (const { id, spec } of result) { const taskSpec = JSON.parse(spec as string) as TaskSpec; - await this.db('task_events').insert({ + await tx('task_events').insert({ task_id: id, event_type: 'recovered', body: JSON.stringify({ diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts index a2018fe127..29a22716ba 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts @@ -314,6 +314,7 @@ export class StorageTaskBroker implements TaskBroker { spec: pendingTask.spec, secrets: pendingTask.secrets, createdBy: pendingTask.createdBy, + state: pendingTask.state, }, this.storage, abortController.signal, diff --git a/plugins/scaffolder-node/api-report.md b/plugins/scaffolder-node/api-report.md index f1362f5a8a..c757301249 100644 --- a/plugins/scaffolder-node/api-report.md +++ b/plugins/scaffolder-node/api-report.md @@ -286,6 +286,7 @@ export type SerializedTask = { lastHeartbeatAt?: string; createdBy?: string; secrets?: TaskSecrets; + state?: JsonObject; }; // @public diff --git a/plugins/scaffolder-node/src/tasks/types.ts b/plugins/scaffolder-node/src/tasks/types.ts index a072bb5f5e..aef2c5f360 100644 --- a/plugins/scaffolder-node/src/tasks/types.ts +++ b/plugins/scaffolder-node/src/tasks/types.ts @@ -59,6 +59,7 @@ export type SerializedTask = { lastHeartbeatAt?: string; createdBy?: string; secrets?: TaskSecrets; + state?: JsonObject; }; /**