From e5f26f9f2d4c51d73cdfd853b561b9cd365919b1 Mon Sep 17 00:00:00 2001 From: bnechyporenko Date: Wed, 13 Mar 2024 09:01:56 +0100 Subject: [PATCH 1/6] Fixing the lost of the initial state after a task recovery. Signed-off-by: bnechyporenko --- .../src/scaffolder/tasks/DatabaseTaskStore.ts | 11 +++++++++++ .../src/scaffolder/tasks/StorageTaskBroker.ts | 1 + plugins/scaffolder-node/src/tasks/types.ts | 1 + 3 files changed, 13 insertions(+) diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts index 2a39440775..5b99b5d0bc 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts @@ -268,6 +268,16 @@ export class DatabaseTaskStore implements TaskStore { return undefined; } + const getState = () => { + try { + return task.state ? JSON.parse(task.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 +287,7 @@ export class DatabaseTaskStore implements TaskStore { createdAt: task.created_at, createdBy: task.created_by ?? undefined, secrets, + state: getState(), }; }); } 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/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; }; /** From 1f09ac792e955a20bd9f967f8ed36f2ef23ebcc0 Mon Sep 17 00:00:00 2001 From: bnechyporenko Date: Wed, 13 Mar 2024 09:39:45 +0100 Subject: [PATCH 2/6] Fixing the lost of the initial state after a task recovery. Signed-off-by: bnechyporenko --- .../tasks/DatabaseTaskStore.test.ts | 38 +++++++++++++++++++ .../src/scaffolder/tasks/DatabaseTaskStore.ts | 6 ++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts index fe9cf3c661..bd0b84227c 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 }); + }); + 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 5b99b5d0bc..68f76cd3e4 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) : 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}`); @@ -536,14 +538,14 @@ export class DatabaseTaskStore implements TaskStore { status: 'open', last_heartbeat_at: this.db.fn.now(), }, - ['id', 'spec'], + ['id', 'spec', 'state'], ); taskIdsToRecover.push(...result.map(i => i.id)); 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({ From 984abfa575eb28e19d25eccdea48a8831fbe1127 Mon Sep 17 00:00:00 2001 From: bnechyporenko Date: Wed, 13 Mar 2024 11:39:30 +0100 Subject: [PATCH 3/6] Fixing the lost of the initial state after a task recovery. Signed-off-by: bnechyporenko --- .changeset/flat-kiwis-rush.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/flat-kiwis-rush.md 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. From 30258837b25ec3ff7ff66c997551a1ac3e2699d0 Mon Sep 17 00:00:00 2001 From: bnechyporenko Date: Wed, 13 Mar 2024 14:59:18 +0100 Subject: [PATCH 4/6] Fixing the lost of the initial state after a task recovery. Signed-off-by: bnechyporenko --- .../src/scaffolder/tasks/DatabaseTaskStore.test.ts | 2 +- .../src/scaffolder/tasks/DatabaseTaskStore.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts index bd0b84227c..ab80dcff04 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts @@ -196,7 +196,7 @@ describe('DatabaseTaskStore', () => { await store.claimTask(); const claimedTask = await store.getTask(taskId); - expect(claimedTask.state).toEqual({ state }); + expect(claimedTask.state).toEqual({ state: state.state }); }); it('should shutdown the running task', async () => { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts index 68f76cd3e4..752108cf6e 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts @@ -212,7 +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) : undefined; + const state = result.state ? JSON.parse(result.state).state : undefined; return { id: result.id, spec, @@ -272,7 +272,7 @@ export class DatabaseTaskStore implements TaskStore { const getState = () => { try { - return task.state ? JSON.parse(task.state) : undefined; + return task.state ? JSON.parse(task.state).state : undefined; } catch (error) { throw new Error( `Failed to parse state of the task '${task.id}', ${error}`, From d1101fd58244233b874c50e7ed3bc205915b57b7 Mon Sep 17 00:00:00 2001 From: bnechyporenko Date: Wed, 13 Mar 2024 21:26:03 +0100 Subject: [PATCH 5/6] updated api report Signed-off-by: bnechyporenko --- plugins/scaffolder-node/api-report.md | 1 + 1 file changed, 1 insertion(+) 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 From 3a6e0dfc78b25ec919547574ac7c05f1d68312b8 Mon Sep 17 00:00:00 2001 From: bnechyporenko Date: Wed, 13 Mar 2024 21:27:06 +0100 Subject: [PATCH 6/6] wip Signed-off-by: bnechyporenko --- .../src/scaffolder/tasks/DatabaseTaskStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts index 752108cf6e..ac391ea2b6 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts @@ -538,7 +538,7 @@ export class DatabaseTaskStore implements TaskStore { status: 'open', last_heartbeat_at: this.db.fn.now(), }, - ['id', 'spec', 'state'], + ['id', 'spec'], ); taskIdsToRecover.push(...result.map(i => i.id));