Fix issue 29505: Retries with EXPERIMENTAL_strategy doesn't handle authentication properly

Signed-off-by: Bogdan Nechyporenko <bnechyporenko@bol.com>
This commit is contained in:
Bogdan Nechyporenko
2025-05-09 07:31:25 +02:00
parent 6e362e6fa7
commit ec42f8ed8e
8 changed files with 46 additions and 10 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-scaffolder-backend': patch
'@backstage/plugin-scaffolder-node': patch
---
Generating new tokens on each Scaffolder Task Retry
+8 -2
View File
@@ -502,7 +502,10 @@ export class DatabaseTaskStore implements TaskStore {
targetPath: string;
}): Promise<void>;
// (undocumented)
retryTask?(options: { taskId: string }): Promise<void>;
retryTask?(options: {
secrets?: TaskSecrets_2;
taskId: string;
}): Promise<void>;
// (undocumented)
saveTaskState(options: { taskId: string; state?: JsonObject }): Promise<void>;
// (undocumented)
@@ -769,7 +772,10 @@ export interface TaskStore {
targetPath: string;
}): Promise<void>;
// (undocumented)
retryTask?(options: { taskId: string }): Promise<void>;
retryTask?(options: {
secrets?: TaskSecrets_2;
taskId: string;
}): Promise<void>;
// (undocumented)
saveTaskState?(options: {
taskId: string;
@@ -654,12 +654,18 @@ export class DatabaseTaskStore implements TaskStore {
});
}
async retryTask?(options: { taskId: string }): Promise<void> {
async retryTask?(options: {
secrets?: TaskSecrets;
taskId: string;
}): Promise<void> {
const { secrets, taskId } = options;
await this.db.transaction(async tx => {
const result = await tx<RawDbTaskRow>('tasks')
.where('id', options.taskId)
.where('id', taskId)
.update(
{
...(secrets && { secrets: JSON.stringify(secrets) }),
status: 'open',
last_heartbeat_at: this.db.fn.now(),
},
@@ -503,8 +503,11 @@ export class StorageTaskBroker implements TaskBroker {
});
}
async retry?(taskId: string): Promise<void> {
await this.storage.retryTask?.({ taskId });
async retry?(options: {
secrets?: TaskSecrets;
taskId: string;
}): Promise<void> {
await this.storage.retryTask?.(options);
this.signalDispatch();
}
}
@@ -171,7 +171,7 @@ export interface TaskStore {
options: TaskStoreCreateTaskOptions,
): Promise<TaskStoreCreateTaskResult>;
retryTask?(options: { taskId: string }): Promise<void>;
retryTask?(options: { secrets?: TaskSecrets; taskId: string }): Promise<void>;
recoverTasks?(
options: TaskStoreRecoverTaskOptions,
@@ -828,7 +828,22 @@ export async function createRouter(
await auditorEvent?.success();
await taskBroker.retry?.(taskId);
const { token } = await auth.getPluginRequestToken({
onBehalfOf: credentials,
targetPluginId: 'catalog',
});
const secrets: InternalTaskSecrets = {
...req.body.secrets,
backstageToken: token,
__initiatorCredentials: JSON.stringify({
...credentials,
// credentials.token is nonenumerable and will not be serialized, so we need to add it explicitly
token: (credentials as any).token,
}),
};
await taskBroker.retry?.({ secrets, taskId });
res.status(201).json({ id: taskId });
} catch (err) {
await auditorEvent?.fail({ error: err });
+1 -1
View File
@@ -414,7 +414,7 @@ export interface TaskBroker {
// (undocumented)
recoverTasks?(): Promise<void>;
// (undocumented)
retry?(taskId: string): Promise<void>;
retry?(options: { secrets?: TaskSecrets; taskId: string }): Promise<void>;
// (undocumented)
vacuumTasks(options: { timeoutS: number }): Promise<void>;
}
+1 -1
View File
@@ -165,7 +165,7 @@ export interface TaskContext {
export interface TaskBroker {
cancel?(taskId: string): Promise<void>;
retry?(taskId: string): Promise<void>;
retry?(options: { secrets?: TaskSecrets; taskId: string }): Promise<void>;
claim(): Promise<TaskContext>;