@shipfox/api-runners 12.3.0 → 12.4.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 +22 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -2
- package/dist/config.js.map +1 -1
- package/dist/core/demand.d.ts +4 -2
- package/dist/core/demand.d.ts.map +1 -1
- package/dist/core/demand.js +22 -6
- package/dist/core/demand.js.map +1 -1
- package/dist/core/entities/reservation.d.ts +1 -0
- package/dist/core/entities/reservation.d.ts.map +1 -1
- package/dist/core/entities/reservation.js.map +1 -1
- package/dist/core/entities/runner-instance.d.ts +3 -0
- package/dist/core/entities/runner-instance.d.ts.map +1 -1
- package/dist/core/entities/runner-instance.js.map +1 -1
- package/dist/core/runner-activation.d.ts +4 -1
- package/dist/core/runner-activation.d.ts.map +1 -1
- package/dist/core/runner-activation.js +32 -3
- package/dist/core/runner-activation.js.map +1 -1
- package/dist/core/runner-control-sessions.d.ts +8 -4
- package/dist/core/runner-control-sessions.d.ts.map +1 -1
- package/dist/core/runner-control-sessions.js +55 -9
- 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 +1 -0
- package/dist/core/runner-instances.js.map +1 -1
- package/dist/db/db.d.ts +68 -0
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/ephemeral-registration-tokens.d.ts.map +1 -1
- package/dist/db/ephemeral-registration-tokens.js +1 -1
- package/dist/db/ephemeral-registration-tokens.js.map +1 -1
- package/dist/db/reservation-locks.d.ts +6 -0
- package/dist/db/reservation-locks.d.ts.map +1 -0
- package/dist/db/reservation-locks.js +15 -0
- package/dist/db/reservation-locks.js.map +1 -0
- package/dist/db/reservations.d.ts +17 -3
- package/dist/db/reservations.d.ts.map +1 -1
- package/dist/db/reservations.js +246 -51
- package/dist/db/reservations.js.map +1 -1
- package/dist/db/runner-assignments.d.ts +17 -0
- package/dist/db/runner-assignments.d.ts.map +1 -1
- package/dist/db/runner-assignments.js +103 -8
- package/dist/db/runner-assignments.js.map +1 -1
- package/dist/db/runner-instances.d.ts +2 -2
- package/dist/db/runner-instances.d.ts.map +1 -1
- package/dist/db/runner-instances.js +124 -24
- package/dist/db/runner-instances.js.map +1 -1
- package/dist/db/runner-states.d.ts +2 -0
- package/dist/db/runner-states.d.ts.map +1 -0
- package/dist/db/runner-states.js +7 -0
- package/dist/db/runner-states.js.map +1 -0
- package/dist/db/schema/reservations.d.ts +18 -0
- package/dist/db/schema/reservations.d.ts.map +1 -1
- package/dist/db/schema/reservations.js +7 -1
- package/dist/db/schema/reservations.js.map +1 -1
- package/dist/db/schema/runner-instances.d.ts +18 -0
- package/dist/db/schema/runner-instances.d.ts.map +1 -1
- package/dist/db/schema/runner-instances.js +8 -0
- package/dist/db/schema/runner-instances.js.map +1 -1
- package/dist/metrics/index.d.ts +2 -2
- 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 +24 -2
- package/dist/metrics/instance.d.ts.map +1 -1
- package/dist/metrics/instance.js +23 -0
- package/dist/metrics/instance.js.map +1 -1
- package/dist/presentation/dto/poll-demand.d.ts.map +1 -1
- package/dist/presentation/dto/poll-demand.js +3 -0
- package/dist/presentation/dto/poll-demand.js.map +1 -1
- package/dist/presentation/dto/runner-instances.d.ts +4 -1
- package/dist/presentation/dto/runner-instances.d.ts.map +1 -1
- package/dist/presentation/dto/runner-instances.js +4 -1
- package/dist/presentation/dto/runner-instances.js.map +1 -1
- package/dist/presentation/routes/assign-runner-instances.d.ts.map +1 -1
- package/dist/presentation/routes/assign-runner-instances.js +3 -3
- package/dist/presentation/routes/assign-runner-instances.js.map +1 -1
- package/dist/presentation/routes/poll-demand.d.ts.map +1 -1
- package/dist/presentation/routes/poll-demand.js +1 -0
- package/dist/presentation/routes/poll-demand.js.map +1 -1
- package/dist/presentation/routes/reconcile-runner-instances.d.ts.map +1 -1
- package/dist/presentation/routes/reconcile-runner-instances.js +4 -2
- package/dist/presentation/routes/reconcile-runner-instances.js.map +1 -1
- package/dist/presentation/routes/runner-enrollment.d.ts +4 -1
- package/dist/presentation/routes/runner-enrollment.d.ts.map +1 -1
- package/dist/presentation/routes/runner-enrollment.js +11 -6
- package/dist/presentation/routes/runner-enrollment.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0005_previous_lenny_balinger.sql +5 -0
- package/drizzle/meta/0005_snapshot.json +2220 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +2 -2
- package/src/config.test.ts +22 -0
- package/src/config.ts +15 -2
- package/src/core/demand.test.ts +192 -3
- package/src/core/demand.ts +44 -4
- package/src/core/entities/reservation.ts +1 -0
- package/src/core/entities/runner-instance.ts +4 -0
- package/src/core/runner-activation.ts +48 -2
- package/src/core/runner-control-sessions.test.ts +64 -3
- package/src/core/runner-control-sessions.ts +62 -9
- package/src/core/runner-instances.test.ts +19 -0
- package/src/core/runner-instances.ts +2 -0
- package/src/core/runner-sessions.test.ts +121 -1
- package/src/db/ephemeral-registration-tokens.test.ts +20 -1
- package/src/db/ephemeral-registration-tokens.ts +1 -0
- package/src/db/reservation-locks.ts +22 -0
- package/src/db/reservations.test.ts +733 -22
- package/src/db/reservations.ts +408 -144
- package/src/db/runner-assignments.test.ts +105 -1
- package/src/db/runner-assignments.ts +178 -9
- package/src/db/runner-instances.test.ts +265 -2
- package/src/db/runner-instances.ts +215 -55
- package/src/db/runner-states.ts +7 -0
- package/src/db/schema/reservations.ts +5 -1
- package/src/db/schema/runner-instances.ts +9 -0
- package/src/metrics/index.ts +10 -1
- package/src/metrics/instance.ts +59 -2
- package/src/metrics/service.test.ts +8 -1
- package/src/presentation/dto/poll-demand.ts +3 -0
- package/src/presentation/dto/runner-instances.ts +8 -1
- package/src/presentation/routes/assign-runner-instances.ts +6 -2
- package/src/presentation/routes/late-runner-enrollment.test.ts +219 -2
- package/src/presentation/routes/poll-demand.test.ts +56 -0
- package/src/presentation/routes/poll-demand.ts +1 -0
- package/src/presentation/routes/reconcile-runner-instances.test.ts +34 -2
- package/src/presentation/routes/reconcile-runner-instances.ts +7 -1
- package/src/presentation/routes/runner-enrollment.test.ts +316 -3
- package/src/presentation/routes/runner-enrollment.ts +8 -5
- package/test/factories/reservation.ts +2 -0
- package/test/factories/runner-instance.ts +6 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -146,6 +146,28 @@ describe('reportRunnerInstances', () => {
|
|
|
146
146
|
expect(rows[0]?.state).toBe('running');
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
+
it('preserves launch kind when a provider report updates the runner row', async () => {
|
|
150
|
+
await providerRunnerFactory.create({
|
|
151
|
+
workspaceId,
|
|
152
|
+
provisionerId,
|
|
153
|
+
providerRunnerId: 'demand-runner',
|
|
154
|
+
launchKind: 'demand',
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
await reportRunnerInstances({
|
|
158
|
+
scope: 'workspace',
|
|
159
|
+
workspaceId,
|
|
160
|
+
provisionerId,
|
|
161
|
+
events: [event({providerRunnerId: 'demand-runner', state: 'running'})],
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const [row] = await db()
|
|
165
|
+
.select({launchKind: providerRunners.launchKind})
|
|
166
|
+
.from(providerRunners)
|
|
167
|
+
.where(eq(providerRunners.providerRunnerId, 'demand-runner'));
|
|
168
|
+
expect(row?.launchKind).toBe('demand');
|
|
169
|
+
});
|
|
170
|
+
|
|
149
171
|
it('uses state progression to dedupe equal-timestamp provisioned runner reports', async () => {
|
|
150
172
|
const reportedAt = new Date();
|
|
151
173
|
|
|
@@ -366,6 +388,58 @@ describe('reportRunnerInstances', () => {
|
|
|
366
388
|
expect(rows[0]?.reservationId).toBe(reservationId);
|
|
367
389
|
});
|
|
368
390
|
|
|
391
|
+
it('does not adopt a bound reservation id from an unclaimed report', async () => {
|
|
392
|
+
const reservationId = await createReservation(1, {kind: 'bound'});
|
|
393
|
+
await db()
|
|
394
|
+
.insert(providerRunners)
|
|
395
|
+
.values({
|
|
396
|
+
workspaceId,
|
|
397
|
+
provisionerId,
|
|
398
|
+
providerRunnerId: 'bound-reservation-runner',
|
|
399
|
+
state: 'starting',
|
|
400
|
+
reportedAt: new Date('2025-01-01T00:00:00.000Z'),
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
await reportRunnerInstances({
|
|
404
|
+
scope: 'workspace',
|
|
405
|
+
workspaceId,
|
|
406
|
+
provisionerId,
|
|
407
|
+
events: [
|
|
408
|
+
event({
|
|
409
|
+
providerRunnerId: 'bound-reservation-runner',
|
|
410
|
+
reservationId,
|
|
411
|
+
state: 'running',
|
|
412
|
+
reportedAt: new Date('2025-01-01T00:01:00.000Z'),
|
|
413
|
+
}),
|
|
414
|
+
],
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
const rows = await providerRunnerRowsFor({workspaceId, provisionerId});
|
|
418
|
+
expect(rows[0]?.reservationId).toBeNull();
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('does not let reports exceed reservation capacity when creating projection rows', async () => {
|
|
422
|
+
const reservationId = await createReservation(1);
|
|
423
|
+
|
|
424
|
+
await reportRunnerInstances({
|
|
425
|
+
scope: 'workspace',
|
|
426
|
+
workspaceId,
|
|
427
|
+
provisionerId,
|
|
428
|
+
events: [
|
|
429
|
+
event({providerRunnerId: 'provisioned-runner-1', reservationId}),
|
|
430
|
+
event({providerRunnerId: 'provisioned-runner-2', reservationId}),
|
|
431
|
+
],
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
const rows = await providerRunnerRowsFor({workspaceId, provisionerId});
|
|
435
|
+
expect(rows.find((row) => row.providerRunnerId === 'provisioned-runner-1')?.reservationId).toBe(
|
|
436
|
+
reservationId,
|
|
437
|
+
);
|
|
438
|
+
expect(
|
|
439
|
+
rows.find((row) => row.providerRunnerId === 'provisioned-runner-2')?.reservationId,
|
|
440
|
+
).toBeNull();
|
|
441
|
+
});
|
|
442
|
+
|
|
369
443
|
it('does not let equal-timestamp lower-priority reports flip terminal state', async () => {
|
|
370
444
|
const reservationId = await createReservation(1);
|
|
371
445
|
const reportedAt = new Date('2025-01-01T00:00:00.000Z');
|
|
@@ -554,8 +628,15 @@ describe('reportRunnerInstances', () => {
|
|
|
554
628
|
expect(active).toEqual([]);
|
|
555
629
|
});
|
|
556
630
|
|
|
557
|
-
it('releases one reservation unit for a terminal
|
|
631
|
+
it('releases one reservation unit for a terminal runner with an intended reservation', async () => {
|
|
558
632
|
const reservationId = await createReservation(2);
|
|
633
|
+
await providerRunnerFactory.create({
|
|
634
|
+
workspaceId,
|
|
635
|
+
provisionerId,
|
|
636
|
+
providerRunnerId: 'provisioned-runner-1',
|
|
637
|
+
intendedReservationId: reservationId,
|
|
638
|
+
state: 'running',
|
|
639
|
+
});
|
|
559
640
|
|
|
560
641
|
const result = await reportRunnerInstances({
|
|
561
642
|
scope: 'workspace',
|
|
@@ -571,6 +652,30 @@ describe('reportRunnerInstances', () => {
|
|
|
571
652
|
expect(providerRunnerRows[0]?.reservationReleasedAt).toBeInstanceOf(Date);
|
|
572
653
|
});
|
|
573
654
|
|
|
655
|
+
it('does not let an unrecognized terminal report consume reservation capacity', async () => {
|
|
656
|
+
const reservationId = await createReservation(1);
|
|
657
|
+
|
|
658
|
+
const result = await reportRunnerInstances({
|
|
659
|
+
scope: 'workspace',
|
|
660
|
+
workspaceId,
|
|
661
|
+
provisionerId,
|
|
662
|
+
events: [event({providerRunnerId: 'unrecognized-runner', reservationId, state: 'failed'})],
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
const reservationRows = await reservationRowsFor({workspaceId, provisionerId});
|
|
666
|
+
const providerRunnerRows = await providerRunnerRowsFor({workspaceId, provisionerId});
|
|
667
|
+
expect(result).toEqual({accepted: 1, reservationsReleased: 0, terminateIntentsHonored: []});
|
|
668
|
+
expect(reservationRows[0]?.count).toBe(1);
|
|
669
|
+
expect(providerRunnerRows).toHaveLength(1);
|
|
670
|
+
expect(providerRunnerRows[0]).toMatchObject({
|
|
671
|
+
providerRunnerId: 'unrecognized-runner',
|
|
672
|
+
reservationId: null,
|
|
673
|
+
intendedReservationId: null,
|
|
674
|
+
reservationReleasedAt: null,
|
|
675
|
+
state: 'failed',
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
|
|
574
679
|
it('releases an intended reservation for a runner that dies before enrollment', async () => {
|
|
575
680
|
const reservationId = await createReservation(2);
|
|
576
681
|
const [runner] = await db()
|
|
@@ -741,6 +846,13 @@ describe('reportRunnerInstances', () => {
|
|
|
741
846
|
|
|
742
847
|
it('releases a reservation only once across repeated terminal reports', async () => {
|
|
743
848
|
const reservationId = await createReservation(2);
|
|
849
|
+
await providerRunnerFactory.create({
|
|
850
|
+
workspaceId,
|
|
851
|
+
provisionerId,
|
|
852
|
+
providerRunnerId: 'provisioned-runner-1',
|
|
853
|
+
intendedReservationId: reservationId,
|
|
854
|
+
state: 'running',
|
|
855
|
+
});
|
|
744
856
|
|
|
745
857
|
await reportRunnerInstances({
|
|
746
858
|
scope: 'workspace',
|
|
@@ -762,6 +874,13 @@ describe('reportRunnerInstances', () => {
|
|
|
762
874
|
|
|
763
875
|
it('does not let a newer running report revive a terminal provisioned runner', async () => {
|
|
764
876
|
const reservationId = await createReservation(2);
|
|
877
|
+
await providerRunnerFactory.create({
|
|
878
|
+
workspaceId,
|
|
879
|
+
provisionerId,
|
|
880
|
+
providerRunnerId: 'provisioned-runner-1',
|
|
881
|
+
intendedReservationId: reservationId,
|
|
882
|
+
state: 'running',
|
|
883
|
+
});
|
|
765
884
|
const failedAt = new Date();
|
|
766
885
|
await reportRunnerInstances({
|
|
767
886
|
scope: 'workspace',
|
|
@@ -800,6 +919,13 @@ describe('reportRunnerInstances', () => {
|
|
|
800
919
|
|
|
801
920
|
it('tracks provider cleanup as terminated', async () => {
|
|
802
921
|
const reservationId = await createReservation(2);
|
|
922
|
+
await providerRunnerFactory.create({
|
|
923
|
+
workspaceId,
|
|
924
|
+
provisionerId,
|
|
925
|
+
providerRunnerId: 'provisioned-runner-1',
|
|
926
|
+
intendedReservationId: reservationId,
|
|
927
|
+
state: 'running',
|
|
928
|
+
});
|
|
803
929
|
|
|
804
930
|
const result = await reportRunnerInstances({
|
|
805
931
|
scope: 'workspace',
|
|
@@ -821,6 +947,20 @@ describe('reportRunnerInstances', () => {
|
|
|
821
947
|
|
|
822
948
|
it('releases multiple units from the same reservation in one batch', async () => {
|
|
823
949
|
const reservationId = await createReservation(3);
|
|
950
|
+
await providerRunnerFactory.create({
|
|
951
|
+
workspaceId,
|
|
952
|
+
provisionerId,
|
|
953
|
+
providerRunnerId: 'provisioned-runner-1',
|
|
954
|
+
intendedReservationId: reservationId,
|
|
955
|
+
state: 'running',
|
|
956
|
+
});
|
|
957
|
+
await providerRunnerFactory.create({
|
|
958
|
+
workspaceId,
|
|
959
|
+
provisionerId,
|
|
960
|
+
providerRunnerId: 'provisioned-runner-2',
|
|
961
|
+
intendedReservationId: reservationId,
|
|
962
|
+
state: 'running',
|
|
963
|
+
});
|
|
824
964
|
|
|
825
965
|
const result = await reportRunnerInstances({
|
|
826
966
|
scope: 'workspace',
|
|
@@ -839,6 +979,13 @@ describe('reportRunnerInstances', () => {
|
|
|
839
979
|
|
|
840
980
|
it('deletes a one-unit reservation instead of violating the positive count check', async () => {
|
|
841
981
|
const reservationId = await createReservation(1);
|
|
982
|
+
await providerRunnerFactory.create({
|
|
983
|
+
workspaceId,
|
|
984
|
+
provisionerId,
|
|
985
|
+
providerRunnerId: 'provisioned-runner-1',
|
|
986
|
+
intendedReservationId: reservationId,
|
|
987
|
+
state: 'running',
|
|
988
|
+
});
|
|
842
989
|
|
|
843
990
|
const result = await reportRunnerInstances({
|
|
844
991
|
scope: 'workspace',
|
|
@@ -862,6 +1009,13 @@ describe('reportRunnerInstances', () => {
|
|
|
862
1009
|
});
|
|
863
1010
|
const [reservation] = await reservationRowsFor({workspaceId, provisionerId});
|
|
864
1011
|
if (!reservation) throw new Error('Expected reservation');
|
|
1012
|
+
await providerRunnerFactory.create({
|
|
1013
|
+
workspaceId,
|
|
1014
|
+
provisionerId,
|
|
1015
|
+
providerRunnerId: 'provisioned-runner-1',
|
|
1016
|
+
intendedReservationId: reservation.id,
|
|
1017
|
+
state: 'running',
|
|
1018
|
+
});
|
|
865
1019
|
|
|
866
1020
|
const result = await reportRunnerInstances({
|
|
867
1021
|
scope: 'workspace',
|
|
@@ -1012,13 +1166,14 @@ describe('reportRunnerInstances', () => {
|
|
|
1012
1166
|
|
|
1013
1167
|
async function createReservation(
|
|
1014
1168
|
count: number,
|
|
1015
|
-
overrides?: {workspaceId?: string; provisionerId?: string},
|
|
1169
|
+
overrides?: {kind?: 'bound' | 'launch'; workspaceId?: string; provisionerId?: string},
|
|
1016
1170
|
): Promise<string> {
|
|
1017
1171
|
await reservationFactory.create({
|
|
1018
1172
|
workspaceId: overrides?.workspaceId ?? workspaceId,
|
|
1019
1173
|
provisionerId: overrides?.provisionerId ?? provisionerId,
|
|
1020
1174
|
requiredLabels: ['linux'],
|
|
1021
1175
|
count,
|
|
1176
|
+
kind: overrides?.kind ?? 'launch',
|
|
1022
1177
|
expiresAt: new Date(Date.now() + 60_000),
|
|
1023
1178
|
});
|
|
1024
1179
|
const [reservation] = await db()
|
|
@@ -1157,6 +1312,106 @@ describe('listProvisionerTerminateIntents', () => {
|
|
|
1157
1312
|
expect(result).toEqual([{providerRunnerId: 'provisioned-runner-1', reason: 'job-cancelled'}]);
|
|
1158
1313
|
});
|
|
1159
1314
|
|
|
1315
|
+
it('returns an activation-timeout intent for a stale demand-backed runner', async () => {
|
|
1316
|
+
await createRunnerInstance({
|
|
1317
|
+
providerRunnerId: 'stale-demand-runner',
|
|
1318
|
+
launchKind: 'demand',
|
|
1319
|
+
createdAt: new Date(Date.now() - 301_000),
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
const result = await db().transaction((tx) =>
|
|
1323
|
+
listProvisionerTerminateIntentRowsTx(tx, {workspaceId, provisionerId, limit: 1000}),
|
|
1324
|
+
);
|
|
1325
|
+
const [runner] = await providerRunnerRowsFor({workspaceId, provisionerId});
|
|
1326
|
+
|
|
1327
|
+
expect(result).toEqual([
|
|
1328
|
+
{providerRunnerId: 'stale-demand-runner', reason: 'activation-timeout'},
|
|
1329
|
+
]);
|
|
1330
|
+
expect(runner?.reservationReleasedAt).toBeInstanceOf(Date);
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1333
|
+
it('does not reclaim warm, manual, active, or already-activated runners', async () => {
|
|
1334
|
+
const [liveReservation] = await db()
|
|
1335
|
+
.insert(reservations)
|
|
1336
|
+
.values({
|
|
1337
|
+
workspaceId,
|
|
1338
|
+
provisionerId,
|
|
1339
|
+
requiredLabels: ['linux'],
|
|
1340
|
+
count: 1,
|
|
1341
|
+
expiresAt: new Date(Date.now() + 60_000),
|
|
1342
|
+
})
|
|
1343
|
+
.returning({id: reservations.id});
|
|
1344
|
+
if (!liveReservation) throw new Error('Expected live reservation');
|
|
1345
|
+
|
|
1346
|
+
const staleCreatedAt = new Date(Date.now() - 301_000);
|
|
1347
|
+
await createRunnerInstance({
|
|
1348
|
+
providerRunnerId: 'stale-warm-runner',
|
|
1349
|
+
launchKind: 'warm',
|
|
1350
|
+
createdAt: staleCreatedAt,
|
|
1351
|
+
});
|
|
1352
|
+
await createRunnerInstance({
|
|
1353
|
+
providerRunnerId: 'stale-manual-runner',
|
|
1354
|
+
launchKind: 'manual',
|
|
1355
|
+
createdAt: staleCreatedAt,
|
|
1356
|
+
});
|
|
1357
|
+
await createRunnerInstance({
|
|
1358
|
+
providerRunnerId: 'live-demand-runner',
|
|
1359
|
+
launchKind: 'demand',
|
|
1360
|
+
reservationId: liveReservation.id,
|
|
1361
|
+
createdAt: staleCreatedAt,
|
|
1362
|
+
});
|
|
1363
|
+
await db()
|
|
1364
|
+
.insert(providerRunners)
|
|
1365
|
+
.values({
|
|
1366
|
+
workspaceId,
|
|
1367
|
+
provisionerId,
|
|
1368
|
+
intendedReservationId: liveReservation.id,
|
|
1369
|
+
providerRunnerId: 'live-intended-demand-runner',
|
|
1370
|
+
launchKind: 'demand',
|
|
1371
|
+
state: 'running',
|
|
1372
|
+
labels: ['linux'],
|
|
1373
|
+
reportedAt: staleCreatedAt,
|
|
1374
|
+
createdAt: staleCreatedAt,
|
|
1375
|
+
updatedAt: staleCreatedAt,
|
|
1376
|
+
});
|
|
1377
|
+
await createRunnerInstance({
|
|
1378
|
+
providerRunnerId: 'activated-demand-runner',
|
|
1379
|
+
launchKind: 'demand',
|
|
1380
|
+
runnerSessionId: crypto.randomUUID(),
|
|
1381
|
+
createdAt: staleCreatedAt,
|
|
1382
|
+
});
|
|
1383
|
+
|
|
1384
|
+
const result = await listProvisionerTerminateIntents({workspaceId, provisionerId, limit: 1000});
|
|
1385
|
+
|
|
1386
|
+
expect(result).toEqual([]);
|
|
1387
|
+
});
|
|
1388
|
+
|
|
1389
|
+
it('marks activation-timeout retries after releasing the runner reservation', async () => {
|
|
1390
|
+
await createRunnerInstance({
|
|
1391
|
+
providerRunnerId: 'retryable-demand-runner',
|
|
1392
|
+
launchKind: 'demand',
|
|
1393
|
+
createdAt: new Date(Date.now() - 301_000),
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
const first = await db().transaction((tx) =>
|
|
1397
|
+
listProvisionerTerminateIntentRowsTx(tx, {workspaceId, provisionerId, limit: 1000}),
|
|
1398
|
+
);
|
|
1399
|
+
const second = await db().transaction((tx) =>
|
|
1400
|
+
listProvisionerTerminateIntentRowsTx(tx, {workspaceId, provisionerId, limit: 1000}),
|
|
1401
|
+
);
|
|
1402
|
+
|
|
1403
|
+
expect(first).toEqual([
|
|
1404
|
+
{providerRunnerId: 'retryable-demand-runner', reason: 'activation-timeout'},
|
|
1405
|
+
]);
|
|
1406
|
+
expect(second).toEqual([
|
|
1407
|
+
{
|
|
1408
|
+
providerRunnerId: 'retryable-demand-runner',
|
|
1409
|
+
reason: 'activation-timeout',
|
|
1410
|
+
activationTimeoutRetry: true,
|
|
1411
|
+
},
|
|
1412
|
+
]);
|
|
1413
|
+
});
|
|
1414
|
+
|
|
1160
1415
|
it('excludes active provisioned runners whose latest bound job is healthy', async () => {
|
|
1161
1416
|
await createRunnerInstance({providerRunnerId: 'provisioned-runner-1'});
|
|
1162
1417
|
await insertRunningJobRow({
|
|
@@ -1272,12 +1527,20 @@ describe('listProvisionerTerminateIntents', () => {
|
|
|
1272
1527
|
providerRunnerId: string;
|
|
1273
1528
|
provisionerId?: string;
|
|
1274
1529
|
state?: 'starting' | 'running' | 'stopping' | 'stopped' | 'failed' | 'terminated';
|
|
1530
|
+
launchKind?: 'demand' | 'warm' | 'manual';
|
|
1531
|
+
createdAt?: Date;
|
|
1532
|
+
reservationId?: string | null;
|
|
1533
|
+
runnerSessionId?: string | null;
|
|
1275
1534
|
}) {
|
|
1276
1535
|
return await providerRunnerFactory.create({
|
|
1277
1536
|
workspaceId,
|
|
1278
1537
|
provisionerId: params.provisionerId ?? provisionerId,
|
|
1279
1538
|
providerRunnerId: params.providerRunnerId,
|
|
1280
1539
|
state: params.state ?? 'running',
|
|
1540
|
+
launchKind: params.launchKind ?? 'manual',
|
|
1541
|
+
createdAt: params.createdAt ?? new Date(),
|
|
1542
|
+
reservationId: params.reservationId ?? null,
|
|
1543
|
+
runnerSessionId: params.runnerSessionId ?? null,
|
|
1281
1544
|
});
|
|
1282
1545
|
}
|
|
1283
1546
|
});
|