@backstage/plugin-scaffolder-backend 1.26.0-next.0 → 1.26.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/alpha/package.json +1 -1
- package/dist/ScaffolderPlugin.cjs.js +168 -0
- package/dist/ScaffolderPlugin.cjs.js.map +1 -0
- package/dist/alpha.cjs.js +7 -196
- package/dist/alpha.cjs.js.map +1 -1
- package/dist/deprecated.cjs.js +15 -0
- package/dist/deprecated.cjs.js.map +1 -0
- package/dist/index.cjs.js +57 -134
- package/dist/index.cjs.js.map +1 -1
- package/dist/lib/templating/SecureTemplater.cjs.js +169 -0
- package/dist/lib/templating/SecureTemplater.cjs.js.map +1 -0
- package/dist/lib/templating/filters.cjs.js +26 -0
- package/dist/lib/templating/filters.cjs.js.map +1 -0
- package/dist/lib/templating/helpers.cjs.js +13 -0
- package/dist/lib/templating/helpers.cjs.js.map +1 -0
- package/dist/scaffolder/actions/TemplateActionRegistry.cjs.js +30 -0
- package/dist/scaffolder/actions/TemplateActionRegistry.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/catalog/fetch.cjs.js +93 -0
- package/dist/scaffolder/actions/builtin/catalog/fetch.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/catalog/fetch.examples.cjs.js +43 -0
- package/dist/scaffolder/actions/builtin/catalog/fetch.examples.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/catalog/register.cjs.js +142 -0
- package/dist/scaffolder/actions/builtin/catalog/register.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/catalog/register.examples.cjs.js +28 -0
- package/dist/scaffolder/actions/builtin/catalog/register.examples.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/catalog/write.cjs.js +74 -0
- package/dist/scaffolder/actions/builtin/catalog/write.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/catalog/write.examples.cjs.js +56 -0
- package/dist/scaffolder/actions/builtin/catalog/write.examples.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/createBuiltinActions.cjs.js +156 -0
- package/dist/scaffolder/actions/builtin/createBuiltinActions.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/debug/log.cjs.js +66 -0
- package/dist/scaffolder/actions/builtin/debug/log.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/debug/log.examples.cjs.js +58 -0
- package/dist/scaffolder/actions/builtin/debug/log.examples.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/debug/wait.cjs.js +66 -0
- package/dist/scaffolder/actions/builtin/debug/wait.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/debug/wait.examples.cjs.js +58 -0
- package/dist/scaffolder/actions/builtin/debug/wait.examples.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/fetch/plain.cjs.js +56 -0
- package/dist/scaffolder/actions/builtin/fetch/plain.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/fetch/plain.examples.cjs.js +44 -0
- package/dist/scaffolder/actions/builtin/fetch/plain.examples.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/fetch/plainFile.cjs.js +56 -0
- package/dist/scaffolder/actions/builtin/fetch/plainFile.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/fetch/plainFile.examples.cjs.js +29 -0
- package/dist/scaffolder/actions/builtin/fetch/plainFile.examples.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/fetch/template.cjs.js +241 -0
- package/dist/scaffolder/actions/builtin/fetch/template.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/fetch/template.examples.cjs.js +35 -0
- package/dist/scaffolder/actions/builtin/fetch/template.examples.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/fetch/templateFile.cjs.js +119 -0
- package/dist/scaffolder/actions/builtin/fetch/templateFile.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/fetch/templateFile.examples.cjs.js +34 -0
- package/dist/scaffolder/actions/builtin/fetch/templateFile.examples.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/filesystem/delete.cjs.js +54 -0
- package/dist/scaffolder/actions/builtin/filesystem/delete.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/filesystem/delete.examples.cjs.js +44 -0
- package/dist/scaffolder/actions/builtin/filesystem/delete.examples.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/filesystem/rename.cjs.js +83 -0
- package/dist/scaffolder/actions/builtin/filesystem/rename.cjs.js.map +1 -0
- package/dist/scaffolder/actions/builtin/filesystem/rename.examples.cjs.js +48 -0
- package/dist/scaffolder/actions/builtin/filesystem/rename.examples.cjs.js.map +1 -0
- package/dist/scaffolder/actions/deprecated.cjs.js +74 -0
- package/dist/scaffolder/actions/deprecated.cjs.js.map +1 -0
- package/dist/scaffolder/dryrun/DecoratedActionsRegistry.cjs.js +57 -0
- package/dist/scaffolder/dryrun/DecoratedActionsRegistry.cjs.js.map +1 -0
- package/dist/scaffolder/dryrun/createDryRunner.cjs.js +97 -0
- package/dist/scaffolder/dryrun/createDryRunner.cjs.js.map +1 -0
- package/dist/scaffolder/tasks/DatabaseTaskStore.cjs.js +430 -0
- package/dist/scaffolder/tasks/DatabaseTaskStore.cjs.js.map +1 -0
- package/dist/scaffolder/tasks/DatabaseWorkspaceProvider.cjs.js +22 -0
- package/dist/scaffolder/tasks/DatabaseWorkspaceProvider.cjs.js.map +1 -0
- package/dist/scaffolder/tasks/NunjucksWorkflowRunner.cjs.js +545 -0
- package/dist/scaffolder/tasks/NunjucksWorkflowRunner.cjs.js.map +1 -0
- package/dist/scaffolder/tasks/StorageTaskBroker.cjs.js +318 -0
- package/dist/scaffolder/tasks/StorageTaskBroker.cjs.js.map +1 -0
- package/dist/scaffolder/tasks/TaskWorker.cjs.js +110 -0
- package/dist/scaffolder/tasks/TaskWorker.cjs.js.map +1 -0
- package/dist/scaffolder/tasks/WorkspaceService.cjs.js +50 -0
- package/dist/scaffolder/tasks/WorkspaceService.cjs.js.map +1 -0
- package/dist/scaffolder/tasks/dbUtil.cjs.js +20 -0
- package/dist/scaffolder/tasks/dbUtil.cjs.js.map +1 -0
- package/dist/scaffolder/tasks/helper.cjs.js +46 -0
- package/dist/scaffolder/tasks/helper.cjs.js.map +1 -0
- package/dist/scaffolder/tasks/logger.cjs.js +156 -0
- package/dist/scaffolder/tasks/logger.cjs.js.map +1 -0
- package/dist/scaffolder/tasks/taskRecoveryHelper.cjs.js +18 -0
- package/dist/scaffolder/tasks/taskRecoveryHelper.cjs.js.map +1 -0
- package/dist/service/conditionExports.cjs.js +26 -0
- package/dist/service/conditionExports.cjs.js.map +1 -0
- package/dist/service/helpers.cjs.js +92 -0
- package/dist/service/helpers.cjs.js.map +1 -0
- package/dist/service/router.cjs.js +640 -0
- package/dist/service/router.cjs.js.map +1 -0
- package/dist/service/rules.cjs.js +97 -0
- package/dist/service/rules.cjs.js.map +1 -0
- package/dist/util/checkPermissions.cjs.js +25 -0
- package/dist/util/checkPermissions.cjs.js.map +1 -0
- package/dist/util/metrics.cjs.js +24 -0
- package/dist/util/metrics.cjs.js.map +1 -0
- package/package.json +30 -30
- package/dist/cjs/router-CC-UhVkG.cjs.js +0 -4101
- package/dist/cjs/router-CC-UhVkG.cjs.js.map +0 -1
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
|
+
var errors = require('@backstage/errors');
|
|
5
|
+
var uuid = require('uuid');
|
|
6
|
+
var luxon = require('luxon');
|
|
7
|
+
var taskRecoveryHelper = require('./taskRecoveryHelper.cjs.js');
|
|
8
|
+
var dbUtil = require('./dbUtil.cjs.js');
|
|
9
|
+
var alpha = require('@backstage/plugin-scaffolder-node/alpha');
|
|
10
|
+
var helpers = require('../../service/helpers.cjs.js');
|
|
11
|
+
|
|
12
|
+
const migrationsDir = backendPluginApi.resolvePackagePath(
|
|
13
|
+
"@backstage/plugin-scaffolder-backend",
|
|
14
|
+
"migrations"
|
|
15
|
+
);
|
|
16
|
+
function isPluginDatabaseManager(opt) {
|
|
17
|
+
return opt.getClient !== void 0;
|
|
18
|
+
}
|
|
19
|
+
const parseSqlDateToIsoString = (input) => {
|
|
20
|
+
if (typeof input === "string") {
|
|
21
|
+
const parsed = luxon.DateTime.fromSQL(input, { zone: "UTC" });
|
|
22
|
+
if (!parsed.isValid) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Failed to parse database timestamp '${input}', ${parsed.invalidReason}: ${parsed.invalidExplanation}`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return parsed.toISO();
|
|
28
|
+
}
|
|
29
|
+
return input;
|
|
30
|
+
};
|
|
31
|
+
class DatabaseTaskStore {
|
|
32
|
+
db;
|
|
33
|
+
static async create(options) {
|
|
34
|
+
const { database } = options;
|
|
35
|
+
const client = await this.getClient(database);
|
|
36
|
+
await this.runMigrations(database, client);
|
|
37
|
+
return new DatabaseTaskStore(client);
|
|
38
|
+
}
|
|
39
|
+
isRecoverableTask(spec) {
|
|
40
|
+
return ["startOver"].includes(
|
|
41
|
+
spec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ?? "none"
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
parseSpec({ spec, id }) {
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(spec);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw new Error(`Failed to parse spec of task '${id}', ${error}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
parseTaskSecrets(taskRow) {
|
|
52
|
+
try {
|
|
53
|
+
return taskRow.secrets ? JSON.parse(taskRow.secrets) : void 0;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Failed to parse secrets of task '${taskRow.id}', ${error}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
static async getClient(database) {
|
|
61
|
+
if (isPluginDatabaseManager(database)) {
|
|
62
|
+
return database.getClient();
|
|
63
|
+
}
|
|
64
|
+
return database;
|
|
65
|
+
}
|
|
66
|
+
static async runMigrations(database, client) {
|
|
67
|
+
if (!isPluginDatabaseManager(database)) {
|
|
68
|
+
await client.migrate.latest({
|
|
69
|
+
directory: migrationsDir
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (!database.migrations?.skip) {
|
|
74
|
+
await client.migrate.latest({
|
|
75
|
+
directory: migrationsDir
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
constructor(client) {
|
|
80
|
+
this.db = client;
|
|
81
|
+
}
|
|
82
|
+
async list(options) {
|
|
83
|
+
const { createdBy, status, pagination, order, filters } = options ?? {};
|
|
84
|
+
const queryBuilder = this.db("tasks");
|
|
85
|
+
if (createdBy || filters?.createdBy) {
|
|
86
|
+
const arr = helpers.flattenParams(
|
|
87
|
+
createdBy,
|
|
88
|
+
filters?.createdBy
|
|
89
|
+
);
|
|
90
|
+
queryBuilder.whereIn("created_by", [...new Set(arr)]);
|
|
91
|
+
}
|
|
92
|
+
if (status || filters?.status) {
|
|
93
|
+
const arr = helpers.flattenParams(
|
|
94
|
+
status,
|
|
95
|
+
filters?.status
|
|
96
|
+
);
|
|
97
|
+
queryBuilder.whereIn("status", [...new Set(arr)]);
|
|
98
|
+
}
|
|
99
|
+
if (order) {
|
|
100
|
+
order.forEach((f) => {
|
|
101
|
+
queryBuilder.orderBy(f.field, f.order);
|
|
102
|
+
});
|
|
103
|
+
} else {
|
|
104
|
+
queryBuilder.orderBy("created_at", "desc");
|
|
105
|
+
}
|
|
106
|
+
const countQuery = queryBuilder.clone();
|
|
107
|
+
countQuery.count("tasks.id", { as: "count" });
|
|
108
|
+
if (pagination?.limit !== void 0) {
|
|
109
|
+
queryBuilder.limit(pagination.limit);
|
|
110
|
+
}
|
|
111
|
+
if (pagination?.offset !== void 0) {
|
|
112
|
+
queryBuilder.offset(pagination.offset);
|
|
113
|
+
}
|
|
114
|
+
const [results, [{ count }]] = await Promise.all([
|
|
115
|
+
queryBuilder.select(),
|
|
116
|
+
countQuery
|
|
117
|
+
]);
|
|
118
|
+
const tasks = results.map((result) => ({
|
|
119
|
+
id: result.id,
|
|
120
|
+
spec: JSON.parse(result.spec),
|
|
121
|
+
status: result.status,
|
|
122
|
+
createdBy: result.created_by ?? void 0,
|
|
123
|
+
lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at),
|
|
124
|
+
createdAt: parseSqlDateToIsoString(result.created_at)
|
|
125
|
+
}));
|
|
126
|
+
return { tasks, totalTasks: count };
|
|
127
|
+
}
|
|
128
|
+
async getTask(taskId) {
|
|
129
|
+
const [result] = await this.db("tasks").where({ id: taskId }).select();
|
|
130
|
+
if (!result) {
|
|
131
|
+
throw new errors.NotFoundError(`No task with id '${taskId}' found`);
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const spec = JSON.parse(result.spec);
|
|
135
|
+
const secrets = result.secrets ? JSON.parse(result.secrets) : void 0;
|
|
136
|
+
const state = result.state ? JSON.parse(result.state).state : void 0;
|
|
137
|
+
return {
|
|
138
|
+
id: result.id,
|
|
139
|
+
spec,
|
|
140
|
+
status: result.status,
|
|
141
|
+
lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at),
|
|
142
|
+
createdAt: parseSqlDateToIsoString(result.created_at),
|
|
143
|
+
createdBy: result.created_by ?? void 0,
|
|
144
|
+
secrets,
|
|
145
|
+
state
|
|
146
|
+
};
|
|
147
|
+
} catch (error) {
|
|
148
|
+
throw new Error(`Failed to parse spec of task '${taskId}', ${error}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async createTask(options) {
|
|
152
|
+
const taskId = uuid.v4();
|
|
153
|
+
await this.db("tasks").insert({
|
|
154
|
+
id: taskId,
|
|
155
|
+
spec: JSON.stringify(options.spec),
|
|
156
|
+
secrets: options.secrets ? JSON.stringify(options.secrets) : void 0,
|
|
157
|
+
created_by: options.createdBy ?? null,
|
|
158
|
+
status: "open"
|
|
159
|
+
});
|
|
160
|
+
return { taskId };
|
|
161
|
+
}
|
|
162
|
+
async claimTask() {
|
|
163
|
+
return this.db.transaction(async (tx) => {
|
|
164
|
+
const [task] = await tx("tasks").where({
|
|
165
|
+
status: "open"
|
|
166
|
+
}).limit(1).select();
|
|
167
|
+
if (!task) {
|
|
168
|
+
return void 0;
|
|
169
|
+
}
|
|
170
|
+
const spec = this.parseSpec(task);
|
|
171
|
+
const updateCount = await tx("tasks").where({ id: task.id, status: "open" }).update({
|
|
172
|
+
status: "processing",
|
|
173
|
+
last_heartbeat_at: this.db.fn.now(),
|
|
174
|
+
// remove the secrets for non-recoverable tasks when moving to processing state.
|
|
175
|
+
secrets: this.isRecoverableTask(spec) ? task.secrets : null
|
|
176
|
+
});
|
|
177
|
+
if (updateCount < 1) {
|
|
178
|
+
return void 0;
|
|
179
|
+
}
|
|
180
|
+
const getState = () => {
|
|
181
|
+
try {
|
|
182
|
+
return task.state ? JSON.parse(task.state).state : void 0;
|
|
183
|
+
} catch (error) {
|
|
184
|
+
throw new Error(
|
|
185
|
+
`Failed to parse state of the task '${task.id}', ${error}`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
const secrets = this.parseTaskSecrets(task);
|
|
190
|
+
return {
|
|
191
|
+
id: task.id,
|
|
192
|
+
spec,
|
|
193
|
+
status: "processing",
|
|
194
|
+
lastHeartbeatAt: task.last_heartbeat_at,
|
|
195
|
+
createdAt: task.created_at,
|
|
196
|
+
createdBy: task.created_by ?? void 0,
|
|
197
|
+
secrets,
|
|
198
|
+
state: getState()
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
async heartbeatTask(taskId) {
|
|
203
|
+
const updateCount = await this.db("tasks").where({ id: taskId, status: "processing" }).update({
|
|
204
|
+
last_heartbeat_at: this.db.fn.now()
|
|
205
|
+
});
|
|
206
|
+
if (updateCount === 0) {
|
|
207
|
+
throw new errors.ConflictError(`No running task with taskId ${taskId} found`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async listStaleTasks(options) {
|
|
211
|
+
const { timeoutS } = options;
|
|
212
|
+
const heartbeatInterval = dbUtil.intervalFromNowTill(timeoutS, this.db);
|
|
213
|
+
const rawRows = await this.db("tasks").where("status", "processing").andWhere("last_heartbeat_at", "<=", heartbeatInterval);
|
|
214
|
+
const tasks = rawRows.map((row) => ({
|
|
215
|
+
recovery: JSON.parse(row.spec).EXPERIMENTAL_recovery,
|
|
216
|
+
taskId: row.id
|
|
217
|
+
}));
|
|
218
|
+
return { tasks };
|
|
219
|
+
}
|
|
220
|
+
async completeTask(options) {
|
|
221
|
+
const { taskId, status, eventBody } = options;
|
|
222
|
+
let oldStatus;
|
|
223
|
+
if (["failed", "completed", "cancelled"].includes(status)) {
|
|
224
|
+
oldStatus = "processing";
|
|
225
|
+
} else {
|
|
226
|
+
throw new Error(
|
|
227
|
+
`Invalid status update of run '${taskId}' to status '${status}'`
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
await this.db.transaction(async (tx) => {
|
|
231
|
+
const [task] = await tx("tasks").where({
|
|
232
|
+
id: taskId
|
|
233
|
+
}).limit(1).select();
|
|
234
|
+
const updateTask = async (criteria) => {
|
|
235
|
+
const updateCount = await tx("tasks").where(criteria).update({
|
|
236
|
+
status,
|
|
237
|
+
secrets: null
|
|
238
|
+
});
|
|
239
|
+
if (updateCount !== 1) {
|
|
240
|
+
throw new errors.ConflictError(
|
|
241
|
+
`Failed to update status to '${status}' for taskId ${taskId}`
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
await tx("task_events").insert({
|
|
245
|
+
task_id: taskId,
|
|
246
|
+
event_type: "completion",
|
|
247
|
+
body: JSON.stringify(eventBody)
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
if (status === "cancelled") {
|
|
251
|
+
await updateTask({
|
|
252
|
+
id: taskId
|
|
253
|
+
});
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (task.status === "cancelled") {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (!task) {
|
|
260
|
+
throw new Error(`No task with taskId ${taskId} found`);
|
|
261
|
+
}
|
|
262
|
+
if (task.status !== oldStatus) {
|
|
263
|
+
throw new errors.ConflictError(
|
|
264
|
+
`Refusing to update status of run '${taskId}' to status '${status}' as it is currently '${task.status}', expected '${oldStatus}'`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
await updateTask({
|
|
268
|
+
id: taskId,
|
|
269
|
+
status: oldStatus
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
async emitLogEvent(options) {
|
|
274
|
+
const { taskId, body } = options;
|
|
275
|
+
const serializedBody = JSON.stringify(body);
|
|
276
|
+
await this.db("task_events").insert({
|
|
277
|
+
task_id: taskId,
|
|
278
|
+
event_type: "log",
|
|
279
|
+
body: serializedBody
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
async getTaskState({ taskId }) {
|
|
283
|
+
const [result] = await this.db("tasks").where({ id: taskId }).select("state");
|
|
284
|
+
return result.state ? JSON.parse(result.state) : void 0;
|
|
285
|
+
}
|
|
286
|
+
async saveTaskState(options) {
|
|
287
|
+
if (options.state) {
|
|
288
|
+
const serializedState = JSON.stringify({ state: options.state });
|
|
289
|
+
await this.db("tasks").where({ id: options.taskId }).update({
|
|
290
|
+
state: serializedState
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
async listEvents(options) {
|
|
295
|
+
const { isTaskRecoverable, taskId, after } = options;
|
|
296
|
+
const rawEvents = await this.db("task_events").where({
|
|
297
|
+
task_id: taskId
|
|
298
|
+
}).andWhere((builder) => {
|
|
299
|
+
if (typeof after === "number") {
|
|
300
|
+
builder.where("id", ">", after).orWhere("event_type", "completion");
|
|
301
|
+
}
|
|
302
|
+
}).orderBy("id").select();
|
|
303
|
+
const events = rawEvents.map((event) => {
|
|
304
|
+
try {
|
|
305
|
+
const body = JSON.parse(event.body);
|
|
306
|
+
return {
|
|
307
|
+
id: Number(event.id),
|
|
308
|
+
isTaskRecoverable,
|
|
309
|
+
taskId,
|
|
310
|
+
body,
|
|
311
|
+
type: event.event_type,
|
|
312
|
+
createdAt: parseSqlDateToIsoString(event.created_at)
|
|
313
|
+
};
|
|
314
|
+
} catch (error) {
|
|
315
|
+
throw new Error(
|
|
316
|
+
`Failed to parse event body from event taskId=${taskId} id=${event.id}, ${error}`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
return taskRecoveryHelper.trimEventsTillLastRecovery(events);
|
|
321
|
+
}
|
|
322
|
+
async shutdownTask(options) {
|
|
323
|
+
const { taskId } = options;
|
|
324
|
+
const message = `This task was marked as stale as it exceeded its timeout`;
|
|
325
|
+
const statusStepEvents = (await this.listEvents({ taskId })).events.filter(
|
|
326
|
+
({ body }) => body?.stepId
|
|
327
|
+
);
|
|
328
|
+
const completedSteps = statusStepEvents.filter(
|
|
329
|
+
({ body: { status } }) => status === "failed" || status === "completed"
|
|
330
|
+
).map((step) => step.body.stepId);
|
|
331
|
+
const hungProcessingSteps = statusStepEvents.filter(({ body: { status } }) => status === "processing").map((event) => event.body.stepId).filter((step) => !completedSteps.includes(step));
|
|
332
|
+
for (const step of hungProcessingSteps) {
|
|
333
|
+
await this.emitLogEvent({
|
|
334
|
+
taskId,
|
|
335
|
+
body: {
|
|
336
|
+
message,
|
|
337
|
+
stepId: step,
|
|
338
|
+
status: "failed"
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
await this.completeTask({
|
|
343
|
+
taskId,
|
|
344
|
+
status: "failed",
|
|
345
|
+
eventBody: {
|
|
346
|
+
message
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
async rehydrateWorkspace(options) {
|
|
351
|
+
const [result] = await this.db("tasks").where({ id: options.taskId }).select("workspace");
|
|
352
|
+
await alpha.restoreWorkspace({
|
|
353
|
+
path: options.targetPath,
|
|
354
|
+
buffer: result.workspace
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
async cleanWorkspace({ taskId }) {
|
|
358
|
+
await this.db("tasks").where({ id: taskId }).update({
|
|
359
|
+
workspace: null
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
async serializeWorkspace(options) {
|
|
363
|
+
if (options.path) {
|
|
364
|
+
const workspace = (await alpha.serializeWorkspace(options)).contents;
|
|
365
|
+
await this.db("tasks").where({ id: options.taskId }).update({
|
|
366
|
+
workspace
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async cancelTask(options) {
|
|
371
|
+
const { taskId, body } = options;
|
|
372
|
+
const serializedBody = JSON.stringify(body);
|
|
373
|
+
await this.db("task_events").insert({
|
|
374
|
+
task_id: taskId,
|
|
375
|
+
event_type: "cancelled",
|
|
376
|
+
body: serializedBody
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
async retryTask(options) {
|
|
380
|
+
await this.db.transaction(async (tx) => {
|
|
381
|
+
const result = await tx("tasks").where("id", options.taskId).update(
|
|
382
|
+
{
|
|
383
|
+
status: "open",
|
|
384
|
+
last_heartbeat_at: this.db.fn.now()
|
|
385
|
+
},
|
|
386
|
+
["id", "spec"]
|
|
387
|
+
);
|
|
388
|
+
for (const { id, spec } of result) {
|
|
389
|
+
const taskSpec = JSON.parse(spec);
|
|
390
|
+
await tx("task_events").where("task_id", id).andWhere((q) => q.whereIn("event_type", ["cancelled", "completion"])).del();
|
|
391
|
+
await tx("task_events").insert({
|
|
392
|
+
task_id: id,
|
|
393
|
+
event_type: "recovered",
|
|
394
|
+
body: JSON.stringify({
|
|
395
|
+
recoverStrategy: taskSpec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ?? "none"
|
|
396
|
+
})
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
async recoverTasks(options) {
|
|
402
|
+
const taskIdsToRecover = [];
|
|
403
|
+
const timeoutS = luxon.Duration.fromObject(options.timeout).as("seconds");
|
|
404
|
+
await this.db.transaction(async (tx) => {
|
|
405
|
+
const heartbeatInterval = dbUtil.intervalFromNowTill(timeoutS, this.db);
|
|
406
|
+
const result = await tx("tasks").where("status", "processing").andWhere("last_heartbeat_at", "<=", heartbeatInterval).update(
|
|
407
|
+
{
|
|
408
|
+
status: "open",
|
|
409
|
+
last_heartbeat_at: this.db.fn.now()
|
|
410
|
+
},
|
|
411
|
+
["id", "spec"]
|
|
412
|
+
);
|
|
413
|
+
taskIdsToRecover.push(...result.map((i) => i.id));
|
|
414
|
+
for (const { id, spec } of result) {
|
|
415
|
+
const taskSpec = JSON.parse(spec);
|
|
416
|
+
await tx("task_events").insert({
|
|
417
|
+
task_id: id,
|
|
418
|
+
event_type: "recovered",
|
|
419
|
+
body: JSON.stringify({
|
|
420
|
+
recoverStrategy: taskSpec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ?? "none"
|
|
421
|
+
})
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
return { ids: taskIdsToRecover };
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
exports.DatabaseTaskStore = DatabaseTaskStore;
|
|
430
|
+
//# sourceMappingURL=DatabaseTaskStore.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatabaseTaskStore.cjs.js","sources":["../../../src/scaffolder/tasks/DatabaseTaskStore.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject } from '@backstage/types';\nimport { PluginDatabaseManager } from '@backstage/backend-common';\nimport { resolvePackagePath } from '@backstage/backend-plugin-api';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\nimport {\n TaskStore,\n TaskStoreCreateTaskOptions,\n TaskStoreCreateTaskResult,\n TaskStoreEmitOptions,\n TaskStoreListEventsOptions,\n TaskStoreRecoverTaskOptions,\n TaskStoreShutDownTaskOptions,\n} from './types';\nimport {\n SerializedTask,\n SerializedTaskEvent,\n TaskEventType,\n TaskSecrets,\n TaskStatus,\n} from '@backstage/plugin-scaffolder-node';\nimport { DateTime, Duration } from 'luxon';\nimport { TaskRecovery, TaskSpec } from '@backstage/plugin-scaffolder-common';\nimport { trimEventsTillLastRecovery } from './taskRecoveryHelper';\nimport { intervalFromNowTill } from './dbUtil';\nimport {\n restoreWorkspace,\n serializeWorkspace,\n} from '@backstage/plugin-scaffolder-node/alpha';\nimport { flattenParams } from '../../service/helpers';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-scaffolder-backend',\n 'migrations',\n);\n\nexport type RawDbTaskRow = {\n id: string;\n spec: string;\n status: TaskStatus;\n state?: string;\n last_heartbeat_at?: string;\n created_at: string;\n created_by: string | null;\n secrets?: string | null;\n workspace?: Buffer;\n};\n\nexport type RawDbTaskEventRow = {\n id: number;\n task_id: string;\n body: string;\n event_type: TaskEventType;\n created_at: string;\n};\n\n/**\n * DatabaseTaskStore\n *\n * @public\n */\nexport type DatabaseTaskStoreOptions = {\n database: PluginDatabaseManager | Knex;\n};\n\n/**\n * Type guard to help DatabaseTaskStore understand when database is PluginDatabaseManager vs. when database is a Knex instance.\n *\n * * @public\n */\nfunction isPluginDatabaseManager(\n opt: PluginDatabaseManager | Knex,\n): opt is PluginDatabaseManager {\n return (opt as PluginDatabaseManager).getClient !== undefined;\n}\n\nconst parseSqlDateToIsoString = <T>(input: T): T | string => {\n if (typeof input === 'string') {\n const parsed = DateTime.fromSQL(input, { zone: 'UTC' });\n if (!parsed.isValid) {\n throw new Error(\n `Failed to parse database timestamp '${input}', ${parsed.invalidReason}: ${parsed.invalidExplanation}`,\n );\n }\n return parsed.toISO()!;\n }\n\n return input;\n};\n\n/**\n * DatabaseTaskStore\n *\n * @public\n */\nexport class DatabaseTaskStore implements TaskStore {\n private readonly db: Knex;\n\n static async create(\n options: DatabaseTaskStoreOptions,\n ): Promise<DatabaseTaskStore> {\n const { database } = options;\n const client = await this.getClient(database);\n\n await this.runMigrations(database, client);\n\n return new DatabaseTaskStore(client);\n }\n\n private isRecoverableTask(spec: TaskSpec): boolean {\n return ['startOver'].includes(\n spec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ?? 'none',\n );\n }\n\n private parseSpec({ spec, id }: { spec: string; id: string }): TaskSpec {\n try {\n return JSON.parse(spec);\n } catch (error) {\n throw new Error(`Failed to parse spec of task '${id}', ${error}`);\n }\n }\n\n private parseTaskSecrets(taskRow: RawDbTaskRow): TaskSecrets | undefined {\n try {\n return taskRow.secrets ? JSON.parse(taskRow.secrets) : undefined;\n } catch (error) {\n throw new Error(\n `Failed to parse secrets of task '${taskRow.id}', ${error}`,\n );\n }\n }\n\n private static async getClient(\n database: PluginDatabaseManager | Knex,\n ): Promise<Knex> {\n if (isPluginDatabaseManager(database)) {\n return database.getClient();\n }\n\n return database;\n }\n\n private static async runMigrations(\n database: PluginDatabaseManager | Knex,\n client: Knex,\n ): Promise<void> {\n if (!isPluginDatabaseManager(database)) {\n await client.migrate.latest({\n directory: migrationsDir,\n });\n\n return;\n }\n\n if (!database.migrations?.skip) {\n await client.migrate.latest({\n directory: migrationsDir,\n });\n }\n }\n\n private constructor(client: Knex) {\n this.db = client;\n }\n\n async list(options: {\n createdBy?: string;\n status?: TaskStatus;\n filters?: {\n createdBy?: string | string[];\n status?: TaskStatus | TaskStatus[];\n };\n pagination?: {\n limit?: number;\n offset?: number;\n };\n order?: { order: 'asc' | 'desc'; field: string }[];\n }): Promise<{ tasks: SerializedTask[]; totalTasks?: number }> {\n const { createdBy, status, pagination, order, filters } = options ?? {};\n const queryBuilder = this.db<RawDbTaskRow & { count: number }>('tasks');\n\n if (createdBy || filters?.createdBy) {\n const arr: string[] = flattenParams<string>(\n createdBy,\n filters?.createdBy,\n );\n queryBuilder.whereIn('created_by', [...new Set(arr)]);\n }\n\n if (status || filters?.status) {\n const arr: TaskStatus[] = flattenParams<TaskStatus>(\n status,\n filters?.status,\n );\n queryBuilder.whereIn('status', [...new Set(arr)]);\n }\n\n if (order) {\n order.forEach(f => {\n queryBuilder.orderBy(f.field, f.order);\n });\n } else {\n queryBuilder.orderBy('created_at', 'desc');\n }\n\n const countQuery = queryBuilder.clone();\n countQuery.count('tasks.id', { as: 'count' });\n\n if (pagination?.limit !== undefined) {\n queryBuilder.limit(pagination.limit);\n }\n\n if (pagination?.offset !== undefined) {\n queryBuilder.offset(pagination.offset);\n }\n\n const [results, [{ count }]] = await Promise.all([\n queryBuilder.select(),\n countQuery,\n ]);\n\n const tasks = results.map(result => ({\n id: result.id,\n spec: JSON.parse(result.spec),\n status: result.status,\n createdBy: result.created_by ?? undefined,\n lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at),\n createdAt: parseSqlDateToIsoString(result.created_at),\n }));\n\n return { tasks, totalTasks: count };\n }\n\n async getTask(taskId: string): Promise<SerializedTask> {\n const [result] = await this.db<RawDbTaskRow>('tasks')\n .where({ id: taskId })\n .select();\n if (!result) {\n throw new NotFoundError(`No task with id '${taskId}' found`);\n }\n try {\n const spec = JSON.parse(result.spec);\n const secrets = result.secrets ? JSON.parse(result.secrets) : undefined;\n const state = result.state ? JSON.parse(result.state).state : undefined;\n return {\n id: result.id,\n spec,\n status: result.status,\n lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at),\n createdAt: parseSqlDateToIsoString(result.created_at),\n createdBy: result.created_by ?? undefined,\n secrets,\n state,\n };\n } catch (error) {\n throw new Error(`Failed to parse spec of task '${taskId}', ${error}`);\n }\n }\n\n async createTask(\n options: TaskStoreCreateTaskOptions,\n ): Promise<TaskStoreCreateTaskResult> {\n const taskId = uuid();\n await this.db<RawDbTaskRow>('tasks').insert({\n id: taskId,\n spec: JSON.stringify(options.spec),\n secrets: options.secrets ? JSON.stringify(options.secrets) : undefined,\n created_by: options.createdBy ?? null,\n status: 'open',\n });\n return { taskId };\n }\n\n async claimTask(): Promise<SerializedTask | undefined> {\n return this.db.transaction(async tx => {\n const [task] = await tx<RawDbTaskRow>('tasks')\n .where({\n status: 'open',\n })\n .limit(1)\n .select();\n\n if (!task) {\n return undefined;\n }\n\n const spec = this.parseSpec(task);\n\n const updateCount = await tx<RawDbTaskRow>('tasks')\n .where({ id: task.id, status: 'open' })\n .update({\n status: 'processing',\n last_heartbeat_at: this.db.fn.now(),\n // remove the secrets for non-recoverable tasks when moving to processing state.\n secrets: this.isRecoverableTask(spec) ? task.secrets : null,\n });\n\n if (updateCount < 1) {\n return undefined;\n }\n\n const getState = () => {\n try {\n return task.state ? JSON.parse(task.state).state : undefined;\n } catch (error) {\n throw new Error(\n `Failed to parse state of the task '${task.id}', ${error}`,\n );\n }\n };\n\n const secrets = this.parseTaskSecrets(task);\n return {\n id: task.id,\n spec,\n status: 'processing',\n lastHeartbeatAt: task.last_heartbeat_at,\n createdAt: task.created_at,\n createdBy: task.created_by ?? undefined,\n secrets,\n state: getState(),\n };\n });\n }\n\n async heartbeatTask(taskId: string): Promise<void> {\n const updateCount = await this.db<RawDbTaskRow>('tasks')\n .where({ id: taskId, status: 'processing' })\n .update({\n last_heartbeat_at: this.db.fn.now(),\n });\n if (updateCount === 0) {\n throw new ConflictError(`No running task with taskId ${taskId} found`);\n }\n }\n\n async listStaleTasks(options: { timeoutS: number }): Promise<{\n tasks: { taskId: string; recovery?: TaskRecovery }[];\n }> {\n const { timeoutS } = options;\n const heartbeatInterval = intervalFromNowTill(timeoutS, this.db);\n const rawRows = await this.db<RawDbTaskRow>('tasks')\n .where('status', 'processing')\n .andWhere('last_heartbeat_at', '<=', heartbeatInterval);\n const tasks = rawRows.map(row => ({\n recovery: (JSON.parse(row.spec) as TaskSpec).EXPERIMENTAL_recovery,\n taskId: row.id,\n }));\n return { tasks };\n }\n\n async completeTask(options: {\n taskId: string;\n status: TaskStatus;\n eventBody: JsonObject;\n }): Promise<void> {\n const { taskId, status, eventBody } = options;\n\n let oldStatus: TaskStatus;\n if (['failed', 'completed', 'cancelled'].includes(status)) {\n oldStatus = 'processing';\n } else {\n throw new Error(\n `Invalid status update of run '${taskId}' to status '${status}'`,\n );\n }\n\n await this.db.transaction(async tx => {\n const [task] = await tx<RawDbTaskRow>('tasks')\n .where({\n id: taskId,\n })\n .limit(1)\n .select();\n\n const updateTask = async (criteria: {\n id: string;\n status?: TaskStatus;\n }) => {\n const updateCount = await tx<RawDbTaskRow>('tasks')\n .where(criteria)\n .update({\n status,\n secrets: null,\n });\n\n if (updateCount !== 1) {\n throw new ConflictError(\n `Failed to update status to '${status}' for taskId ${taskId}`,\n );\n }\n\n await tx<RawDbTaskEventRow>('task_events').insert({\n task_id: taskId,\n event_type: 'completion',\n body: JSON.stringify(eventBody),\n });\n };\n\n if (status === 'cancelled') {\n await updateTask({\n id: taskId,\n });\n return;\n }\n\n if (task.status === 'cancelled') {\n return;\n }\n\n if (!task) {\n throw new Error(`No task with taskId ${taskId} found`);\n }\n if (task.status !== oldStatus) {\n throw new ConflictError(\n `Refusing to update status of run '${taskId}' to status '${status}' ` +\n `as it is currently '${task.status}', expected '${oldStatus}'`,\n );\n }\n\n await updateTask({\n id: taskId,\n status: oldStatus,\n });\n });\n }\n\n async emitLogEvent(\n options: TaskStoreEmitOptions<{ message: string } & JsonObject>,\n ): Promise<void> {\n const { taskId, body } = options;\n const serializedBody = JSON.stringify(body);\n await this.db<RawDbTaskEventRow>('task_events').insert({\n task_id: taskId,\n event_type: 'log',\n body: serializedBody,\n });\n }\n\n async getTaskState({ taskId }: { taskId: string }): Promise<\n | {\n state: JsonObject;\n }\n | undefined\n > {\n const [result] = await this.db<RawDbTaskRow>('tasks')\n .where({ id: taskId })\n .select('state');\n return result.state ? JSON.parse(result.state) : undefined;\n }\n\n async saveTaskState(options: {\n taskId: string;\n state?: JsonObject;\n }): Promise<void> {\n if (options.state) {\n const serializedState = JSON.stringify({ state: options.state });\n await this.db<RawDbTaskRow>('tasks')\n .where({ id: options.taskId })\n .update({\n state: serializedState,\n });\n }\n }\n\n async listEvents(\n options: TaskStoreListEventsOptions,\n ): Promise<{ events: SerializedTaskEvent[] }> {\n const { isTaskRecoverable, taskId, after } = options;\n const rawEvents = await this.db<RawDbTaskEventRow>('task_events')\n .where({\n task_id: taskId,\n })\n .andWhere(builder => {\n if (typeof after === 'number') {\n builder.where('id', '>', after).orWhere('event_type', 'completion');\n }\n })\n .orderBy('id')\n .select();\n\n const events = rawEvents.map(event => {\n try {\n const body = JSON.parse(event.body) as JsonObject;\n return {\n id: Number(event.id),\n isTaskRecoverable,\n taskId,\n body,\n type: event.event_type,\n createdAt: parseSqlDateToIsoString(event.created_at),\n };\n } catch (error) {\n throw new Error(\n `Failed to parse event body from event taskId=${taskId} id=${event.id}, ${error}`,\n );\n }\n });\n\n return trimEventsTillLastRecovery(events);\n }\n\n async shutdownTask(options: TaskStoreShutDownTaskOptions): Promise<void> {\n const { taskId } = options;\n const message = `This task was marked as stale as it exceeded its timeout`;\n\n const statusStepEvents = (await this.listEvents({ taskId })).events.filter(\n ({ body }) => body?.stepId,\n );\n\n const completedSteps = statusStepEvents\n .filter(\n ({ body: { status } }) => status === 'failed' || status === 'completed',\n )\n .map(step => step.body.stepId);\n\n const hungProcessingSteps = statusStepEvents\n .filter(({ body: { status } }) => status === 'processing')\n .map(event => event.body.stepId)\n .filter(step => !completedSteps.includes(step));\n\n for (const step of hungProcessingSteps) {\n await this.emitLogEvent({\n taskId,\n body: {\n message,\n stepId: step,\n status: 'failed',\n },\n });\n }\n\n await this.completeTask({\n taskId,\n status: 'failed',\n eventBody: {\n message,\n },\n });\n }\n\n async rehydrateWorkspace(options: {\n taskId: string;\n targetPath: string;\n }): Promise<void> {\n const [result] = await this.db<RawDbTaskRow>('tasks')\n .where({ id: options.taskId })\n .select('workspace');\n\n await restoreWorkspace({\n path: options.targetPath,\n buffer: result.workspace,\n });\n }\n\n async cleanWorkspace({ taskId }: { taskId: string }): Promise<void> {\n await this.db('tasks').where({ id: taskId }).update({\n workspace: null,\n });\n }\n\n async serializeWorkspace(options: {\n path: string;\n taskId: string;\n }): Promise<void> {\n if (options.path) {\n const workspace = (await serializeWorkspace(options)).contents;\n await this.db<RawDbTaskRow>('tasks')\n .where({ id: options.taskId })\n .update({\n workspace,\n });\n }\n }\n\n async cancelTask(\n options: TaskStoreEmitOptions<{ message: string } & JsonObject>,\n ): Promise<void> {\n const { taskId, body } = options;\n const serializedBody = JSON.stringify(body);\n await this.db<RawDbTaskEventRow>('task_events').insert({\n task_id: taskId,\n event_type: 'cancelled',\n body: serializedBody,\n });\n }\n\n async retryTask?(options: { taskId: string }): Promise<void> {\n await this.db.transaction(async tx => {\n const result = await tx<RawDbTaskRow>('tasks')\n .where('id', options.taskId)\n .update(\n {\n status: 'open',\n last_heartbeat_at: this.db.fn.now(),\n },\n ['id', 'spec'],\n );\n\n for (const { id, spec } of result) {\n const taskSpec = JSON.parse(spec as string) as TaskSpec;\n\n /**\n * Once task is picked up, all event types are replayed.\n * We have to remove cancelled or completion event_type as these are as actions for frontend to perform.\n * In contrary, we send 'recovered' event_type to reset the state on the frontend side.\n *\n */\n await tx<RawDbTaskEventRow>('task_events')\n .where('task_id', id)\n .andWhere(q => q.whereIn('event_type', ['cancelled', 'completion']))\n .del();\n\n await tx<RawDbTaskEventRow>('task_events').insert({\n task_id: id,\n event_type: 'recovered',\n body: JSON.stringify({\n recoverStrategy:\n taskSpec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ?? 'none',\n }),\n });\n }\n });\n }\n\n async recoverTasks(\n options: TaskStoreRecoverTaskOptions,\n ): Promise<{ ids: string[] }> {\n const taskIdsToRecover: string[] = [];\n const timeoutS = Duration.fromObject(options.timeout).as('seconds');\n\n await this.db.transaction(async tx => {\n const heartbeatInterval = intervalFromNowTill(timeoutS, this.db);\n\n const result = await tx<RawDbTaskRow>('tasks')\n .where('status', 'processing')\n .andWhere('last_heartbeat_at', '<=', heartbeatInterval)\n .update(\n {\n status: 'open',\n last_heartbeat_at: this.db.fn.now(),\n },\n ['id', 'spec'],\n );\n\n taskIdsToRecover.push(...result.map(i => i.id));\n\n for (const { id, spec } of result) {\n const taskSpec = JSON.parse(spec as string) as TaskSpec;\n await tx<RawDbTaskEventRow>('task_events').insert({\n task_id: id,\n event_type: 'recovered',\n body: JSON.stringify({\n recoverStrategy:\n taskSpec.EXPERIMENTAL_recovery?.EXPERIMENTAL_strategy ?? 'none',\n }),\n });\n }\n });\n\n return { ids: taskIdsToRecover };\n }\n}\n"],"names":["resolvePackagePath","DateTime","flattenParams","NotFoundError","uuid","ConflictError","intervalFromNowTill","trimEventsTillLastRecovery","restoreWorkspace","serializeWorkspace","Duration"],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class DatabaseWorkspaceProvider {
|
|
4
|
+
constructor(storage) {
|
|
5
|
+
this.storage = storage;
|
|
6
|
+
}
|
|
7
|
+
static create(storage) {
|
|
8
|
+
return new DatabaseWorkspaceProvider(storage);
|
|
9
|
+
}
|
|
10
|
+
async serializeWorkspace(options) {
|
|
11
|
+
await this.storage.serializeWorkspace?.(options);
|
|
12
|
+
}
|
|
13
|
+
async rehydrateWorkspace(options) {
|
|
14
|
+
return this.storage.rehydrateWorkspace?.(options);
|
|
15
|
+
}
|
|
16
|
+
async cleanWorkspace(options) {
|
|
17
|
+
return this.storage.cleanWorkspace?.(options);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
exports.DatabaseWorkspaceProvider = DatabaseWorkspaceProvider;
|
|
22
|
+
//# sourceMappingURL=DatabaseWorkspaceProvider.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatabaseWorkspaceProvider.cjs.js","sources":["../../../src/scaffolder/tasks/DatabaseWorkspaceProvider.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { TaskStore } from './types';\n\nimport { WorkspaceProvider } from '@backstage/plugin-scaffolder-node/alpha';\n\nexport class DatabaseWorkspaceProvider implements WorkspaceProvider {\n static create(storage: TaskStore) {\n return new DatabaseWorkspaceProvider(storage);\n }\n\n private constructor(private readonly storage: TaskStore) {}\n\n public async serializeWorkspace(options: {\n path: string;\n taskId: string;\n }): Promise<void> {\n await this.storage.serializeWorkspace?.(options);\n }\n\n public async rehydrateWorkspace(options: {\n taskId: string;\n targetPath: string;\n }): Promise<void> {\n return this.storage.rehydrateWorkspace?.(options);\n }\n\n public async cleanWorkspace(options: { taskId: string }): Promise<void> {\n return this.storage.cleanWorkspace?.(options);\n }\n}\n"],"names":[],"mappings":";;AAoBO,MAAM,yBAAuD,CAAA;AAAA,EAK1D,YAA6B,OAAoB,EAAA;AAApB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA,CAAA;AAAA,GAAqB;AAAA,EAJ1D,OAAO,OAAO,OAAoB,EAAA;AAChC,IAAO,OAAA,IAAI,0BAA0B,OAAO,CAAA,CAAA;AAAA,GAC9C;AAAA,EAIA,MAAa,mBAAmB,OAGd,EAAA;AAChB,IAAM,MAAA,IAAA,CAAK,OAAQ,CAAA,kBAAA,GAAqB,OAAO,CAAA,CAAA;AAAA,GACjD;AAAA,EAEA,MAAa,mBAAmB,OAGd,EAAA;AAChB,IAAO,OAAA,IAAA,CAAK,OAAQ,CAAA,kBAAA,GAAqB,OAAO,CAAA,CAAA;AAAA,GAClD;AAAA,EAEA,MAAa,eAAe,OAA4C,EAAA;AACtE,IAAO,OAAA,IAAA,CAAK,OAAQ,CAAA,cAAA,GAAiB,OAAO,CAAA,CAAA;AAAA,GAC9C;AACF;;;;"}
|