@shipfox/api-runners 12.1.0 → 12.3.0
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/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +35 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +24 -2
- package/dist/config.js.map +1 -1
- package/dist/core/errors.d.ts +4 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +6 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/rate-limit.d.ts.map +1 -1
- package/dist/core/runner-control-sessions.d.ts.map +1 -1
- package/dist/core/runner-control-sessions.js +37 -11
- package/dist/core/runner-control-sessions.js.map +1 -1
- package/dist/core/runner-instances.d.ts +1 -0
- package/dist/core/runner-instances.d.ts.map +1 -1
- package/dist/core/runner-instances.js.map +1 -1
- package/dist/core/runner-labels.d.ts +11 -0
- package/dist/core/runner-labels.d.ts.map +1 -0
- package/dist/core/runner-labels.js +41 -0
- package/dist/core/runner-labels.js.map +1 -0
- package/dist/core/runner-sessions.d.ts.map +1 -1
- package/dist/core/runner-sessions.js +5 -1
- package/dist/core/runner-sessions.js.map +1 -1
- package/dist/db/db.d.ts +64 -64
- package/dist/db/ephemeral-registration-tokens.d.ts.map +1 -1
- package/dist/db/ephemeral-registration-tokens.js +12 -2
- package/dist/db/ephemeral-registration-tokens.js.map +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/reservations.d.ts.map +1 -1
- package/dist/db/reservations.js +36 -10
- package/dist/db/reservations.js.map +1 -1
- package/dist/db/runner-instances.d.ts +4 -0
- package/dist/db/runner-instances.d.ts.map +1 -1
- package/dist/db/runner-instances.js +16 -5
- package/dist/db/runner-instances.js.map +1 -1
- package/dist/db/runner-sessions.d.ts.map +1 -1
- package/dist/db/runner-sessions.js +33 -15
- package/dist/db/runner-sessions.js.map +1 -1
- package/dist/db/schema/admin-command-results.d.ts +1 -1
- package/dist/db/schema/ephemeral-registration-tokens.d.ts +1 -1
- package/dist/db/schema/manual-registration-tokens.d.ts +1 -1
- package/dist/db/schema/outbox.d.ts +1 -1
- package/dist/db/schema/pending-job-executions.d.ts +3 -3
- package/dist/db/schema/provisioner-capability-snapshots.d.ts +3 -3
- package/dist/db/schema/provisioner-tokens.d.ts +2 -2
- package/dist/db/schema/rate-limits.d.ts +1 -1
- package/dist/db/schema/reservations.d.ts +3 -3
- package/dist/db/schema/runner-activation-tokens.d.ts +1 -1
- package/dist/db/schema/runner-control-sessions.d.ts +2 -2
- package/dist/db/schema/runner-instances.d.ts +4 -4
- package/dist/db/schema/runner-instances.d.ts.map +1 -1
- package/dist/db/schema/runner-instances.js +1 -0
- package/dist/db/schema/runner-instances.js.map +1 -1
- package/dist/db/schema/runner-sessions.d.ts +4 -4
- package/dist/db/schema/running-job-executions.d.ts +5 -5
- package/dist/metrics/index.d.ts +2 -1
- package/dist/metrics/index.d.ts.map +1 -1
- package/dist/metrics/index.js +1 -1
- package/dist/metrics/index.js.map +1 -1
- package/dist/metrics/instance.d.ts +13 -8
- package/dist/metrics/instance.d.ts.map +1 -1
- package/dist/metrics/instance.js +8 -0
- package/dist/metrics/instance.js.map +1 -1
- package/dist/metrics/service.d.ts.map +1 -1
- package/dist/metrics/service.js +20 -4
- package/dist/metrics/service.js.map +1 -1
- package/dist/presentation/dto/admin-provisioner-token.d.ts +1 -1
- package/dist/presentation/dto/admin-provisioner-token.d.ts.map +1 -1
- package/dist/presentation/dto/admin-runner-instances.d.ts +2 -2
- package/dist/presentation/dto/admin-runner-instances.d.ts.map +1 -1
- package/dist/presentation/routes/poll-demand.d.ts.map +1 -1
- package/dist/presentation/routes/poll-demand.js +12 -6
- package/dist/presentation/routes/poll-demand.js.map +1 -1
- package/dist/presentation/routes/rate-limit.d.ts.map +1 -1
- package/dist/presentation/routes/register.d.ts.map +1 -1
- package/dist/presentation/routes/register.js +9 -1
- package/dist/presentation/routes/register.js.map +1 -1
- package/dist/presentation/routes/report-runner-instances.d.ts.map +1 -1
- package/dist/presentation/routes/report-runner-instances.js +1 -0
- package/dist/presentation/routes/report-runner-instances.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0004_huge_skreet.sql +1 -0
- package/drizzle/meta/0004_snapshot.json +2194 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +9 -9
- package/src/config.test.ts +81 -0
- package/src/config.ts +39 -2
- package/src/core/errors.ts +9 -0
- package/src/core/index.ts +1 -0
- package/src/core/runner-control-sessions.test.ts +404 -0
- package/src/core/runner-control-sessions.ts +51 -11
- package/src/core/runner-instances.ts +1 -0
- package/src/core/runner-labels.test.ts +53 -0
- package/src/core/runner-labels.ts +51 -0
- package/src/core/runner-sessions.test.ts +34 -1
- package/src/core/runner-sessions.ts +8 -1
- package/src/db/ephemeral-registration-tokens.ts +15 -1
- package/src/db/index.ts +1 -0
- package/src/db/reservations.test.ts +537 -0
- package/src/db/reservations.ts +116 -17
- package/src/db/runner-instances.test.ts +276 -1
- package/src/db/runner-instances.ts +39 -3
- package/src/db/runner-sessions.ts +39 -15
- package/src/db/schema/runner-instances.ts +3 -0
- package/src/metrics/index.ts +2 -0
- package/src/metrics/instance.ts +18 -0
- package/src/metrics/service.test.ts +99 -0
- package/src/metrics/service.ts +24 -4
- package/src/presentation/routes/late-runner-enrollment.test.ts +230 -0
- package/src/presentation/routes/poll-demand.test.ts +116 -3
- package/src/presentation/routes/poll-demand.ts +18 -6
- package/src/presentation/routes/register.test.ts +116 -2
- package/src/presentation/routes/register.ts +7 -0
- package/src/presentation/routes/report-runner-instances.test.ts +33 -0
- package/src/presentation/routes/report-runner-instances.ts +1 -0
- package/src/presentation/routes/runner-enrollment.test.ts +65 -5
- package/src/temporal/activities/maintenance-activities.test.ts +24 -31
- package/test/env.ts +1 -0
- package/test/fixtures/late-runner-enrollment.ts +103 -0
- package/test/index.ts +5 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/src/db/reservations.ts
CHANGED
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
isNotNull,
|
|
11
11
|
isNull,
|
|
12
12
|
lt,
|
|
13
|
+
notExists,
|
|
14
|
+
or,
|
|
13
15
|
sql,
|
|
14
16
|
} from 'drizzle-orm';
|
|
15
17
|
import type {Tx} from './db.js';
|
|
@@ -66,6 +68,12 @@ export interface InstallationPollDemandAndReserveParams {
|
|
|
66
68
|
onReservations?: (reservations: ReservationGrant[]) => void;
|
|
67
69
|
}
|
|
68
70
|
|
|
71
|
+
type DemandScope = 'installation' | 'workspace';
|
|
72
|
+
|
|
73
|
+
type PollDemandAndReserveLockedParams = PollDemandAndReserveParams & {
|
|
74
|
+
scope: DemandScope;
|
|
75
|
+
};
|
|
76
|
+
|
|
69
77
|
interface NormalizedTemplate {
|
|
70
78
|
templateKey: string;
|
|
71
79
|
labels: string[];
|
|
@@ -91,7 +99,7 @@ export async function pollDemandAndReserveTx(
|
|
|
91
99
|
params: PollDemandAndReserveParams,
|
|
92
100
|
): Promise<{stats: DemandStat[]; reservations: ReservationGrant[]}> {
|
|
93
101
|
await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${params.workspaceId}))`);
|
|
94
|
-
return await pollDemandAndReserveLockedTx(tx, params);
|
|
102
|
+
return await pollDemandAndReserveLockedTx(tx, {...params, scope: 'workspace'});
|
|
95
103
|
}
|
|
96
104
|
|
|
97
105
|
export async function pollInstallationDemandAndReserve(
|
|
@@ -125,6 +133,7 @@ export async function pollInstallationDemandAndReserve(
|
|
|
125
133
|
availableSlots: template.remainingSlots,
|
|
126
134
|
})),
|
|
127
135
|
capabilityWindowSeconds: params.capabilityWindowSeconds,
|
|
136
|
+
scope: 'installation',
|
|
128
137
|
});
|
|
129
138
|
});
|
|
130
139
|
results.push(result);
|
|
@@ -157,7 +166,7 @@ function consumeInstallationTemplateSlots(
|
|
|
157
166
|
|
|
158
167
|
async function pollDemandAndReserveLockedTx(
|
|
159
168
|
tx: Tx,
|
|
160
|
-
params:
|
|
169
|
+
params: PollDemandAndReserveLockedParams,
|
|
161
170
|
): Promise<{stats: DemandStat[]; reservations: ReservationGrant[]}> {
|
|
162
171
|
let demandRows = (
|
|
163
172
|
await tx
|
|
@@ -264,6 +273,7 @@ async function pollDemandAndReserveLockedTx(
|
|
|
264
273
|
requiredLabels: demand.requiredLabels,
|
|
265
274
|
count: grant,
|
|
266
275
|
workspaceId: params.workspaceId,
|
|
276
|
+
scope: params.scope,
|
|
267
277
|
});
|
|
268
278
|
reservedAfterGrant += grant;
|
|
269
279
|
grants.push({
|
|
@@ -295,18 +305,61 @@ async function bindIdleRunnerInstancesTx(
|
|
|
295
305
|
workspaceId: string;
|
|
296
306
|
requiredLabels: string[];
|
|
297
307
|
count: number;
|
|
308
|
+
scope: DemandScope;
|
|
298
309
|
},
|
|
299
310
|
): Promise<void> {
|
|
311
|
+
// Reservation expiry is the activation grace deadline. A live reservation
|
|
312
|
+
// protects a runner that is still booting; an expired or missing row is stale.
|
|
313
|
+
const canBindRunner = () =>
|
|
314
|
+
and(
|
|
315
|
+
or(
|
|
316
|
+
and(isNull(providerRunners.workspaceId), isNull(providerRunners.reservationId)),
|
|
317
|
+
and(
|
|
318
|
+
isNotNull(providerRunners.reservationId),
|
|
319
|
+
params.scope === 'installation'
|
|
320
|
+
? undefined
|
|
321
|
+
: or(
|
|
322
|
+
isNull(providerRunners.workspaceId),
|
|
323
|
+
eq(providerRunners.workspaceId, params.workspaceId),
|
|
324
|
+
),
|
|
325
|
+
notExists(
|
|
326
|
+
tx
|
|
327
|
+
.select({id: reservations.id})
|
|
328
|
+
.from(reservations)
|
|
329
|
+
.where(
|
|
330
|
+
and(
|
|
331
|
+
eq(reservations.id, providerRunners.reservationId),
|
|
332
|
+
gt(reservations.expiresAt, sql`now()`),
|
|
333
|
+
),
|
|
334
|
+
),
|
|
335
|
+
),
|
|
336
|
+
),
|
|
337
|
+
),
|
|
338
|
+
or(
|
|
339
|
+
isNull(providerRunners.intendedReservationId),
|
|
340
|
+
notExists(
|
|
341
|
+
tx
|
|
342
|
+
.select({id: reservations.id})
|
|
343
|
+
.from(reservations)
|
|
344
|
+
.where(
|
|
345
|
+
and(
|
|
346
|
+
eq(reservations.id, providerRunners.intendedReservationId),
|
|
347
|
+
gt(reservations.expiresAt, sql`now()`),
|
|
348
|
+
),
|
|
349
|
+
),
|
|
350
|
+
),
|
|
351
|
+
),
|
|
352
|
+
isNull(providerRunners.reservationReleasedAt),
|
|
353
|
+
isNull(providerRunners.runnerSessionId),
|
|
354
|
+
);
|
|
355
|
+
|
|
300
356
|
const idleRunners = await tx
|
|
301
357
|
.select({id: providerRunners.id})
|
|
302
358
|
.from(providerRunners)
|
|
303
359
|
.where(
|
|
304
360
|
and(
|
|
305
361
|
eq(providerRunners.provisionerId, params.provisionerId),
|
|
306
|
-
|
|
307
|
-
isNull(providerRunners.reservationId),
|
|
308
|
-
isNull(providerRunners.intendedReservationId),
|
|
309
|
-
isNull(providerRunners.runnerSessionId),
|
|
362
|
+
canBindRunner(),
|
|
310
363
|
isNotNull(providerRunners.providerRunnerId),
|
|
311
364
|
eq(providerRunners.state, 'running'),
|
|
312
365
|
arrayContains(providerRunners.labels, params.requiredLabels),
|
|
@@ -331,11 +384,26 @@ async function bindIdleRunnerInstancesTx(
|
|
|
331
384
|
|
|
332
385
|
if (idleRunners.length === 0) return;
|
|
333
386
|
|
|
387
|
+
await tx
|
|
388
|
+
.update(runnerActivationTokens)
|
|
389
|
+
.set({revokedAt: sql`now()`})
|
|
390
|
+
.where(
|
|
391
|
+
and(
|
|
392
|
+
inArray(
|
|
393
|
+
runnerActivationTokens.runnerInstanceId,
|
|
394
|
+
idleRunners.map((runner) => runner.id),
|
|
395
|
+
),
|
|
396
|
+
isNull(runnerActivationTokens.consumedAt),
|
|
397
|
+
isNull(runnerActivationTokens.revokedAt),
|
|
398
|
+
),
|
|
399
|
+
);
|
|
400
|
+
|
|
334
401
|
await tx
|
|
335
402
|
.update(providerRunners)
|
|
336
403
|
.set({
|
|
337
404
|
workspaceId: params.workspaceId,
|
|
338
405
|
reservationId: params.reservationId,
|
|
406
|
+
intendedReservationId: null,
|
|
339
407
|
assignedAt: sql`now()`,
|
|
340
408
|
updatedAt: sql`now()`,
|
|
341
409
|
})
|
|
@@ -345,9 +413,7 @@ async function bindIdleRunnerInstancesTx(
|
|
|
345
413
|
providerRunners.id,
|
|
346
414
|
idleRunners.map((runner) => runner.id),
|
|
347
415
|
),
|
|
348
|
-
|
|
349
|
-
isNull(providerRunners.reservationId),
|
|
350
|
-
isNull(providerRunners.runnerSessionId),
|
|
416
|
+
canBindRunner(),
|
|
351
417
|
),
|
|
352
418
|
);
|
|
353
419
|
}
|
|
@@ -432,16 +498,26 @@ async function deleteReservationsWithCleanupTx(
|
|
|
432
498
|
): Promise<number> {
|
|
433
499
|
if (ids.length === 0) return 0;
|
|
434
500
|
|
|
435
|
-
const
|
|
436
|
-
.select({
|
|
501
|
+
const affectedRunners = await tx
|
|
502
|
+
.select({
|
|
503
|
+
id: providerRunners.id,
|
|
504
|
+
reservationId: providerRunners.reservationId,
|
|
505
|
+
intendedReservationId: providerRunners.intendedReservationId,
|
|
506
|
+
runnerSessionId: providerRunners.runnerSessionId,
|
|
507
|
+
reservationReleasedAt: providerRunners.reservationReleasedAt,
|
|
508
|
+
})
|
|
437
509
|
.from(providerRunners)
|
|
438
510
|
.where(
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
511
|
+
or(
|
|
512
|
+
and(
|
|
513
|
+
inArray(providerRunners.reservationId, ids),
|
|
514
|
+
isNull(providerRunners.runnerSessionId),
|
|
515
|
+
isNull(providerRunners.reservationReleasedAt),
|
|
516
|
+
),
|
|
517
|
+
inArray(providerRunners.intendedReservationId, ids),
|
|
443
518
|
),
|
|
444
519
|
)
|
|
520
|
+
.orderBy(asc(providerRunners.id))
|
|
445
521
|
.for('update');
|
|
446
522
|
const reservationRows = await tx
|
|
447
523
|
.select({id: reservations.id})
|
|
@@ -457,8 +533,20 @@ async function deleteReservationsWithCleanupTx(
|
|
|
457
533
|
|
|
458
534
|
if (reservationIds.length === 0) return 0;
|
|
459
535
|
|
|
460
|
-
const assignedRunnerIds =
|
|
461
|
-
.filter(
|
|
536
|
+
const assignedRunnerIds = affectedRunners
|
|
537
|
+
.filter(
|
|
538
|
+
(runner) =>
|
|
539
|
+
runner.reservationId &&
|
|
540
|
+
reservationIds.includes(runner.reservationId) &&
|
|
541
|
+
!runner.runnerSessionId &&
|
|
542
|
+
!runner.reservationReleasedAt,
|
|
543
|
+
)
|
|
544
|
+
.map((runner) => runner.id);
|
|
545
|
+
const intendedRunnerIds = affectedRunners
|
|
546
|
+
.filter(
|
|
547
|
+
(runner) =>
|
|
548
|
+
runner.intendedReservationId && reservationIds.includes(runner.intendedReservationId),
|
|
549
|
+
)
|
|
462
550
|
.map((runner) => runner.id);
|
|
463
551
|
if (assignedRunnerIds.length > 0) {
|
|
464
552
|
await tx
|
|
@@ -487,6 +575,17 @@ async function deleteReservationsWithCleanupTx(
|
|
|
487
575
|
),
|
|
488
576
|
);
|
|
489
577
|
}
|
|
578
|
+
if (intendedRunnerIds.length > 0) {
|
|
579
|
+
await tx
|
|
580
|
+
.update(providerRunners)
|
|
581
|
+
.set({intendedReservationId: null, updatedAt: sql`now()`})
|
|
582
|
+
.where(
|
|
583
|
+
and(
|
|
584
|
+
inArray(providerRunners.id, intendedRunnerIds),
|
|
585
|
+
inArray(providerRunners.intendedReservationId, reservationIds),
|
|
586
|
+
),
|
|
587
|
+
);
|
|
588
|
+
}
|
|
490
589
|
|
|
491
590
|
const deleted = await tx
|
|
492
591
|
.delete(reservations)
|