@shipfox/api-runners 5.0.0 → 6.0.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 +6 -6
- package/CHANGELOG.md +61 -0
- package/dist/config.d.ts +5 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +53 -6
- package/dist/config.js.map +1 -1
- package/dist/core/capacity-assignments.d.ts +7 -0
- package/dist/core/capacity-assignments.d.ts.map +1 -0
- package/dist/core/capacity-assignments.js +6 -0
- package/dist/core/capacity-assignments.js.map +1 -0
- package/dist/core/demand.d.ts +6 -6
- package/dist/core/demand.d.ts.map +1 -1
- package/dist/core/demand.js +10 -10
- package/dist/core/demand.js.map +1 -1
- package/dist/core/entities/capacity-assignment.d.ts +9 -0
- package/dist/core/entities/capacity-assignment.d.ts.map +1 -0
- package/dist/core/entities/capacity-assignment.js +3 -0
- package/dist/core/entities/capacity-assignment.js.map +1 -0
- package/dist/core/entities/ephemeral-registration-token.d.ts +1 -1
- package/dist/core/entities/ephemeral-registration-token.d.ts.map +1 -1
- package/dist/core/entities/ephemeral-registration-token.js.map +1 -1
- package/dist/core/entities/provisioner-capability-snapshot.d.ts +13 -0
- package/dist/core/entities/provisioner-capability-snapshot.d.ts.map +1 -0
- package/dist/core/entities/provisioner-capability-snapshot.js +3 -0
- package/dist/core/entities/provisioner-capability-snapshot.js.map +1 -0
- package/dist/core/entities/provisioner-token.d.ts +10 -2
- package/dist/core/entities/provisioner-token.d.ts.map +1 -1
- package/dist/core/entities/provisioner-token.js.map +1 -1
- package/dist/core/entities/{provisioned-runner.d.ts → runner-instance.d.ts} +6 -6
- package/dist/core/entities/runner-instance.d.ts.map +1 -0
- package/dist/core/entities/runner-instance.js +3 -0
- package/dist/core/entities/runner-instance.js.map +1 -0
- package/dist/core/entities/runner-session.d.ts +4 -2
- package/dist/core/entities/runner-session.d.ts.map +1 -1
- package/dist/core/entities/runner-session.js.map +1 -1
- package/dist/core/ephemeral-registration-tokens.d.ts +5 -5
- package/dist/core/ephemeral-registration-tokens.d.ts.map +1 -1
- package/dist/core/ephemeral-registration-tokens.js +12 -12
- package/dist/core/ephemeral-registration-tokens.js.map +1 -1
- package/dist/core/errors.d.ts +16 -4
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +22 -4
- package/dist/core/errors.js.map +1 -1
- package/dist/core/index.d.ts +10 -4
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +6 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/job-executions.d.ts +2 -0
- package/dist/core/job-executions.d.ts.map +1 -1
- package/dist/core/job-executions.js +1 -2
- package/dist/core/job-executions.js.map +1 -1
- package/dist/core/maintenance.d.ts +1 -1
- package/dist/core/maintenance.d.ts.map +1 -1
- package/dist/core/maintenance.js +5 -5
- package/dist/core/maintenance.js.map +1 -1
- package/dist/core/provisioner-capability-snapshots.d.ts +5 -0
- package/dist/core/provisioner-capability-snapshots.d.ts.map +1 -0
- package/dist/core/provisioner-capability-snapshots.js +10 -0
- package/dist/core/provisioner-capability-snapshots.js.map +1 -0
- package/dist/core/provisioner-tokens.d.ts +10 -0
- package/dist/core/provisioner-tokens.d.ts.map +1 -1
- package/dist/core/provisioner-tokens.js +23 -1
- package/dist/core/provisioner-tokens.js.map +1 -1
- package/dist/core/rate-limit.d.ts.map +1 -1
- package/dist/core/rate-limit.js +2 -7
- package/dist/core/rate-limit.js.map +1 -1
- package/dist/core/runner-activation.d.ts +13 -0
- package/dist/core/runner-activation.d.ts.map +1 -0
- package/dist/core/runner-activation.js +36 -0
- package/dist/core/runner-activation.js.map +1 -0
- package/dist/core/runner-control-sessions.d.ts +46 -0
- package/dist/core/runner-control-sessions.d.ts.map +1 -0
- package/dist/core/runner-control-sessions.js +121 -0
- package/dist/core/runner-control-sessions.js.map +1 -0
- package/dist/core/runner-instances.d.ts +79 -0
- package/dist/core/runner-instances.d.ts.map +1 -0
- package/dist/core/runner-instances.js +168 -0
- package/dist/core/runner-instances.js.map +1 -0
- package/dist/core/runner-sessions.d.ts +8 -2
- package/dist/core/runner-sessions.d.ts.map +1 -1
- package/dist/core/runner-sessions.js +8 -5
- package/dist/core/runner-sessions.js.map +1 -1
- package/dist/db/capacity-assignments.d.ts +7 -0
- package/dist/db/capacity-assignments.d.ts.map +1 -0
- package/dist/db/capacity-assignments.js +72 -0
- package/dist/db/capacity-assignments.js.map +1 -0
- package/dist/db/db.d.ts +2837 -1009
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js +11 -2
- package/dist/db/db.js.map +1 -1
- package/dist/db/ephemeral-registration-tokens.d.ts +2 -2
- package/dist/db/ephemeral-registration-tokens.d.ts.map +1 -1
- package/dist/db/ephemeral-registration-tokens.js +21 -21
- package/dist/db/ephemeral-registration-tokens.js.map +1 -1
- package/dist/db/index.d.ts +7 -4
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +5 -2
- package/dist/db/index.js.map +1 -1
- package/dist/db/job-executions.d.ts +6 -6
- package/dist/db/job-executions.d.ts.map +1 -1
- package/dist/db/job-executions.js +20 -19
- package/dist/db/job-executions.js.map +1 -1
- package/dist/db/provisioner-capability-snapshots.d.ts +21 -0
- package/dist/db/provisioner-capability-snapshots.d.ts.map +1 -0
- package/dist/db/provisioner-capability-snapshots.js +57 -0
- package/dist/db/provisioner-capability-snapshots.js.map +1 -0
- package/dist/db/provisioner-tokens.d.ts +7 -2
- package/dist/db/provisioner-tokens.d.ts.map +1 -1
- package/dist/db/provisioner-tokens.js +59 -10
- package/dist/db/provisioner-tokens.js.map +1 -1
- package/dist/db/reservations.d.ts +18 -0
- package/dist/db/reservations.d.ts.map +1 -1
- package/dist/db/reservations.js +88 -1
- package/dist/db/reservations.js.map +1 -1
- package/dist/db/runner-instances.d.ts +91 -0
- package/dist/db/runner-instances.d.ts.map +1 -0
- package/dist/db/runner-instances.js +511 -0
- package/dist/db/runner-instances.js.map +1 -0
- package/dist/db/runner-sessions.d.ts +5 -0
- package/dist/db/runner-sessions.d.ts.map +1 -1
- package/dist/db/runner-sessions.js +48 -2
- package/dist/db/runner-sessions.js.map +1 -1
- package/dist/db/schema/capacity-assignments.d.ts +113 -0
- package/dist/db/schema/capacity-assignments.d.ts.map +1 -0
- package/dist/db/schema/capacity-assignments.js +29 -0
- package/dist/db/schema/capacity-assignments.js.map +1 -0
- package/dist/db/schema/ephemeral-registration-tokens.d.ts +2 -2
- package/dist/db/schema/ephemeral-registration-tokens.js +3 -3
- package/dist/db/schema/ephemeral-registration-tokens.js.map +1 -1
- package/dist/db/schema/provisioner-capability-snapshots.d.ts +207 -0
- package/dist/db/schema/provisioner-capability-snapshots.d.ts.map +1 -0
- package/dist/db/schema/provisioner-capability-snapshots.js +38 -0
- package/dist/db/schema/provisioner-capability-snapshots.js.map +1 -0
- package/dist/db/schema/provisioner-tokens.d.ts +19 -1
- package/dist/db/schema/provisioner-tokens.d.ts.map +1 -1
- package/dist/db/schema/provisioner-tokens.js +29 -3
- package/dist/db/schema/provisioner-tokens.js.map +1 -1
- package/dist/db/schema/runner-activation-tokens.d.ts +161 -0
- package/dist/db/schema/runner-activation-tokens.d.ts.map +1 -0
- package/dist/db/schema/runner-activation-tokens.js +27 -0
- package/dist/db/schema/runner-activation-tokens.js.map +1 -0
- package/dist/db/schema/runner-control-sessions.d.ts +338 -0
- package/dist/db/schema/runner-control-sessions.d.ts.map +1 -0
- package/dist/db/schema/runner-control-sessions.js +52 -0
- package/dist/db/schema/runner-control-sessions.js.map +1 -0
- package/dist/db/schema/{provisioned-runners.d.ts → runner-instances.d.ts} +88 -34
- package/dist/db/schema/runner-instances.d.ts.map +1 -0
- package/dist/db/schema/{provisioned-runners.js → runner-instances.js} +19 -14
- package/dist/db/schema/runner-instances.js.map +1 -0
- package/dist/db/schema/runner-sessions.d.ts +39 -5
- package/dist/db/schema/runner-sessions.d.ts.map +1 -1
- package/dist/db/schema/runner-sessions.js +13 -6
- package/dist/db/schema/runner-sessions.js.map +1 -1
- package/dist/db/schema/running-job-executions.d.ts +2 -2
- package/dist/db/schema/running-job-executions.js +4 -4
- package/dist/db/schema/running-job-executions.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -38
- package/dist/index.js.map +1 -1
- package/dist/installation-provisioning.d.ts +14 -0
- package/dist/installation-provisioning.d.ts.map +1 -0
- package/dist/installation-provisioning.js +7 -0
- package/dist/installation-provisioning.js.map +1 -0
- package/dist/metrics/index.d.ts +1 -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 +11 -7
- package/dist/metrics/instance.d.ts.map +1 -1
- package/dist/metrics/instance.js +13 -7
- package/dist/metrics/instance.js.map +1 -1
- package/dist/presentation/auth/index.d.ts +1 -0
- package/dist/presentation/auth/index.d.ts.map +1 -1
- package/dist/presentation/auth/index.js +1 -0
- package/dist/presentation/auth/index.js.map +1 -1
- package/dist/presentation/auth/provisioner-token-auth.d.ts.map +1 -1
- package/dist/presentation/auth/provisioner-token-auth.js +5 -1
- package/dist/presentation/auth/provisioner-token-auth.js.map +1 -1
- package/dist/presentation/auth/runner-control-session-auth.d.ts +4 -0
- package/dist/presentation/auth/runner-control-session-auth.d.ts.map +1 -0
- package/dist/presentation/auth/runner-control-session-auth.js +29 -0
- package/dist/presentation/auth/runner-control-session-auth.js.map +1 -0
- package/dist/presentation/auth/runner-registration-token-auth.d.ts +5 -1
- package/dist/presentation/auth/runner-registration-token-auth.d.ts.map +1 -1
- package/dist/presentation/auth/runner-registration-token-auth.js +21 -1
- package/dist/presentation/auth/runner-registration-token-auth.js.map +1 -1
- package/dist/presentation/dto/index.d.ts +2 -2
- package/dist/presentation/dto/index.d.ts.map +1 -1
- package/dist/presentation/dto/index.js +2 -2
- package/dist/presentation/dto/index.js.map +1 -1
- package/dist/presentation/dto/mint-registration-tokens.d.ts +5 -3
- package/dist/presentation/dto/mint-registration-tokens.d.ts.map +1 -1
- package/dist/presentation/dto/mint-registration-tokens.js +4 -4
- package/dist/presentation/dto/mint-registration-tokens.js.map +1 -1
- package/dist/presentation/dto/poll-demand.d.ts.map +1 -1
- package/dist/presentation/dto/poll-demand.js +7 -1
- package/dist/presentation/dto/poll-demand.js.map +1 -1
- package/dist/presentation/dto/provisioner-token.d.ts +1 -0
- package/dist/presentation/dto/provisioner-token.d.ts.map +1 -1
- package/dist/presentation/dto/provisioner-token.js +5 -1
- package/dist/presentation/dto/provisioner-token.js.map +1 -1
- package/dist/presentation/dto/{provisioned-runners.d.ts → runner-instances.d.ts} +9 -9
- package/dist/presentation/dto/runner-instances.d.ts.map +1 -0
- package/dist/presentation/dto/{provisioned-runners.js → runner-instances.js} +6 -6
- package/dist/presentation/dto/runner-instances.js.map +1 -0
- package/dist/presentation/index.d.ts +2 -2
- package/dist/presentation/index.d.ts.map +1 -1
- package/dist/presentation/index.js +2 -2
- package/dist/presentation/index.js.map +1 -1
- package/dist/presentation/inter-module.d.ts +5 -0
- package/dist/presentation/inter-module.d.ts.map +1 -0
- package/dist/presentation/inter-module.js +43 -0
- package/dist/presentation/inter-module.js.map +1 -0
- package/dist/presentation/routes/assign-capacity.d.ts +2 -0
- package/dist/presentation/routes/assign-capacity.d.ts.map +1 -0
- package/dist/presentation/routes/assign-capacity.js +44 -0
- package/dist/presentation/routes/assign-capacity.js.map +1 -0
- package/dist/presentation/routes/get-provisioner-me.d.ts.map +1 -1
- package/dist/presentation/routes/get-provisioner-me.js +5 -1
- package/dist/presentation/routes/get-provisioner-me.js.map +1 -1
- package/dist/presentation/routes/heartbeat.d.ts +2 -1
- package/dist/presentation/routes/heartbeat.d.ts.map +1 -1
- package/dist/presentation/routes/heartbeat.js +60 -50
- package/dist/presentation/routes/heartbeat.js.map +1 -1
- package/dist/presentation/routes/index.d.ts +3 -1
- package/dist/presentation/routes/index.d.ts.map +1 -1
- package/dist/presentation/routes/index.js +83 -50
- package/dist/presentation/routes/index.js.map +1 -1
- package/dist/presentation/routes/mint-registration-tokens.d.ts.map +1 -1
- package/dist/presentation/routes/mint-registration-tokens.js +5 -5
- package/dist/presentation/routes/mint-registration-tokens.js.map +1 -1
- package/dist/presentation/routes/planned-capacity.d.ts +3 -0
- package/dist/presentation/routes/planned-capacity.d.ts.map +1 -0
- package/dist/presentation/routes/planned-capacity.js +54 -0
- package/dist/presentation/routes/planned-capacity.js.map +1 -0
- package/dist/presentation/routes/poll-demand.d.ts +2 -0
- package/dist/presentation/routes/poll-demand.d.ts.map +1 -1
- package/dist/presentation/routes/poll-demand.js +86 -45
- package/dist/presentation/routes/poll-demand.js.map +1 -1
- package/dist/presentation/routes/reconcile-runner-instances.d.ts +2 -0
- package/dist/presentation/routes/reconcile-runner-instances.d.ts.map +1 -0
- package/dist/presentation/routes/reconcile-runner-instances.js +27 -0
- package/dist/presentation/routes/reconcile-runner-instances.js.map +1 -0
- package/dist/presentation/routes/register.d.ts +2 -1
- package/dist/presentation/routes/register.d.ts.map +1 -1
- package/dist/presentation/routes/register.js +55 -52
- package/dist/presentation/routes/register.js.map +1 -1
- package/dist/presentation/routes/report-runner-instances.d.ts +2 -0
- package/dist/presentation/routes/report-runner-instances.d.ts.map +1 -0
- package/dist/presentation/routes/report-runner-instances.js +37 -0
- package/dist/presentation/routes/report-runner-instances.js.map +1 -0
- package/dist/presentation/routes/request-job.d.ts +2 -1
- package/dist/presentation/routes/request-job.d.ts.map +1 -1
- package/dist/presentation/routes/request-job.js +39 -36
- package/dist/presentation/routes/request-job.js.map +1 -1
- package/dist/presentation/routes/runner-enrollment.d.ts +7 -0
- package/dist/presentation/routes/runner-enrollment.d.ts.map +1 -0
- package/dist/presentation/routes/runner-enrollment.js +185 -0
- package/dist/presentation/routes/runner-enrollment.js.map +1 -0
- package/dist/temporal/activities/index.d.ts +2 -2
- package/dist/temporal/activities/index.d.ts.map +1 -1
- package/dist/temporal/activities/index.js +2 -2
- package/dist/temporal/activities/index.js.map +1 -1
- package/dist/temporal/activities/maintenance-activities.d.ts +1 -1
- package/dist/temporal/activities/maintenance-activities.d.ts.map +1 -1
- package/dist/temporal/activities/maintenance-activities.js +3 -3
- package/dist/temporal/activities/maintenance-activities.js.map +1 -1
- package/dist/temporal/workflows/index.bundle.js +3 -3
- package/dist/temporal/workflows/stuck-job-detector.js +2 -2
- package/dist/temporal/workflows/stuck-job-detector.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0000_initial.sql +102 -24
- package/drizzle/meta/0000_snapshot.json +483 -63
- package/package.json +21 -16
- package/src/config.test.ts +0 -26
- package/src/config.ts +47 -6
- package/src/core/capacity-assignments.ts +10 -0
- package/src/core/demand.test.ts +29 -29
- package/src/core/demand.ts +22 -22
- package/src/core/entities/capacity-assignment.ts +8 -0
- package/src/core/entities/ephemeral-registration-token.ts +1 -1
- package/src/core/entities/provisioner-capability-snapshot.ts +12 -0
- package/src/core/entities/provisioner-token.ts +8 -2
- package/src/core/entities/{provisioned-runner.ts → runner-instance.ts} +5 -5
- package/src/core/entities/runner-session.ts +4 -2
- package/src/core/ephemeral-registration-tokens.test.ts +11 -11
- package/src/core/ephemeral-registration-tokens.ts +18 -20
- package/src/core/errors.ts +23 -4
- package/src/core/index.ts +39 -17
- package/src/core/job-executions.ts +3 -2
- package/src/core/maintenance.test.ts +13 -17
- package/src/core/maintenance.ts +5 -5
- package/src/core/provisioner-capability-snapshots.test.ts +35 -0
- package/src/core/provisioner-capability-snapshots.ts +12 -0
- package/src/core/provisioner-tokens.test.ts +20 -0
- package/src/core/provisioner-tokens.ts +33 -0
- package/src/core/rate-limit.test.ts +11 -27
- package/src/core/rate-limit.ts +3 -11
- package/src/core/runner-activation.ts +72 -0
- package/src/core/runner-control-sessions.ts +190 -0
- package/src/core/{provisioned-runners.test.ts → runner-instances.test.ts} +24 -24
- package/src/core/runner-instances.ts +310 -0
- package/src/core/runner-sessions.test.ts +112 -22
- package/src/core/runner-sessions.ts +25 -14
- package/src/db/capacity-assignments.test.ts +159 -0
- package/src/db/capacity-assignments.ts +116 -0
- package/src/db/db.ts +11 -2
- package/src/db/ephemeral-registration-tokens.test.ts +9 -9
- package/src/db/ephemeral-registration-tokens.ts +26 -26
- package/src/db/index.ts +31 -21
- package/src/db/job-executions.test.ts +13 -11
- package/src/db/job-executions.ts +33 -27
- package/src/db/provisioner-capability-snapshots.test.ts +175 -0
- package/src/db/provisioner-capability-snapshots.ts +130 -0
- package/src/db/provisioner-tokens.test.ts +139 -2
- package/src/db/provisioner-tokens.ts +123 -15
- package/src/db/reservations.test.ts +49 -0
- package/src/db/reservations.ts +146 -1
- package/src/db/{provisioned-runners.test.ts → runner-instances.test.ts} +429 -353
- package/src/db/runner-instances.ts +942 -0
- package/src/db/runner-sessions.test.ts +19 -6
- package/src/db/runner-sessions.ts +78 -2
- package/src/db/schema/capacity-assignments.ts +34 -0
- package/src/db/schema/ephemeral-registration-tokens.ts +4 -4
- package/src/db/schema/provisioner-capability-snapshots.ts +46 -0
- package/src/db/schema/provisioner-tokens.ts +33 -2
- package/src/db/schema/runner-activation-tokens.ts +22 -0
- package/src/db/schema/runner-control-sessions.ts +46 -0
- package/src/db/schema/{provisioned-runners.ts → runner-instances.ts} +26 -27
- package/src/db/schema/runner-sessions.ts +11 -7
- package/src/db/schema/running-job-executions.ts +5 -5
- package/src/index.test.ts +26 -0
- package/src/index.ts +39 -19
- package/src/installation-provisioning.ts +14 -0
- package/src/metrics/index.ts +5 -5
- package/src/metrics/instance.ts +25 -14
- package/src/presentation/auth/index.ts +4 -0
- package/src/presentation/auth/provisioner-token-auth.ts +8 -4
- package/src/presentation/auth/runner-control-session-auth.ts +31 -0
- package/src/presentation/auth/runner-registration-token-auth.ts +39 -3
- package/src/presentation/dto/index.ts +5 -5
- package/src/presentation/dto/mint-registration-tokens.ts +8 -15
- package/src/presentation/dto/poll-demand.ts +3 -1
- package/src/presentation/dto/provisioner-token.ts +5 -0
- package/src/presentation/dto/{provisioned-runners.ts → runner-instances.ts} +13 -15
- package/src/presentation/index.ts +2 -1
- package/src/presentation/inter-module.test.ts +15 -0
- package/src/presentation/inter-module.ts +53 -0
- package/src/presentation/routes/assign-capacity.ts +48 -0
- package/src/presentation/routes/get-provisioner-me.ts +3 -4
- package/src/presentation/routes/heartbeat.test.ts +4 -3
- package/src/presentation/routes/heartbeat.ts +67 -64
- package/src/presentation/routes/index.ts +93 -43
- package/src/presentation/routes/list-active-runners.test.ts +23 -23
- package/src/presentation/routes/manual-registration-tokens.test.ts +4 -3
- package/src/presentation/routes/mint-registration-tokens.test.ts +12 -12
- package/src/presentation/routes/mint-registration-tokens.ts +5 -7
- package/src/presentation/routes/planned-capacity.ts +49 -0
- package/src/presentation/routes/poll-demand-release.test.ts +4 -4
- package/src/presentation/routes/poll-demand.test.ts +161 -22
- package/src/presentation/routes/poll-demand.ts +105 -45
- package/src/presentation/routes/provisioner-me.test.ts +37 -3
- package/src/presentation/routes/{reconcile-provisioned-runners.test.ts → reconcile-runner-instances.test.ts} +66 -61
- package/src/presentation/routes/reconcile-runner-instances.ts +30 -0
- package/src/presentation/routes/register.test.ts +9 -5
- package/src/presentation/routes/register.ts +61 -57
- package/src/presentation/routes/{report-provisioned-runners.test.ts → report-runner-instances.test.ts} +28 -28
- package/src/presentation/routes/{report-provisioned-runners.ts → report-runner-instances.ts} +14 -14
- package/src/presentation/routes/request-job.test.ts +5 -3
- package/src/presentation/routes/request-job.ts +41 -37
- package/src/presentation/routes/runner-enrollment.test.ts +259 -0
- package/src/presentation/routes/runner-enrollment.ts +174 -0
- package/src/temporal/activities/index.ts +2 -2
- package/src/temporal/activities/maintenance-activities.test.ts +7 -7
- package/src/temporal/activities/maintenance-activities.ts +3 -3
- package/src/temporal/workflows/stuck-job-detector.test.ts +3 -3
- package/src/temporal/workflows/stuck-job-detector.ts +2 -2
- package/test/env.ts +1 -3
- package/test/factories/ephemeral-registration-token.ts +2 -2
- package/test/factories/provisioner-token.ts +3 -1
- package/test/factories/runner-instance.ts +57 -0
- package/test/factories/runner-session.ts +3 -1
- package/test/fixtures/auth-inter-module.ts +30 -0
- package/test/globalSetup.ts +5 -1
- package/test/index.ts +2 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/core/entities/provisioned-runner.d.ts.map +0 -1
- package/dist/core/entities/provisioned-runner.js +0 -3
- package/dist/core/entities/provisioned-runner.js.map +0 -1
- package/dist/core/provisioned-runners.d.ts +0 -67
- package/dist/core/provisioned-runners.d.ts.map +0 -1
- package/dist/core/provisioned-runners.js +0 -162
- package/dist/core/provisioned-runners.js.map +0 -1
- package/dist/db/provisioned-runners.d.ts +0 -79
- package/dist/db/provisioned-runners.d.ts.map +0 -1
- package/dist/db/provisioned-runners.js +0 -466
- package/dist/db/provisioned-runners.js.map +0 -1
- package/dist/db/schema/provisioned-runners.d.ts.map +0 -1
- package/dist/db/schema/provisioned-runners.js.map +0 -1
- package/dist/presentation/dto/provisioned-runners.d.ts.map +0 -1
- package/dist/presentation/dto/provisioned-runners.js.map +0 -1
- package/dist/presentation/routes/reconcile-provisioned-runners.d.ts +0 -2
- package/dist/presentation/routes/reconcile-provisioned-runners.d.ts.map +0 -1
- package/dist/presentation/routes/reconcile-provisioned-runners.js +0 -27
- package/dist/presentation/routes/reconcile-provisioned-runners.js.map +0 -1
- package/dist/presentation/routes/report-provisioned-runners.d.ts +0 -2
- package/dist/presentation/routes/report-provisioned-runners.d.ts.map +0 -1
- package/dist/presentation/routes/report-provisioned-runners.js +0 -37
- package/dist/presentation/routes/report-provisioned-runners.js.map +0 -1
- package/src/core/provisioned-runners.ts +0 -301
- package/src/db/provisioned-runners.ts +0 -886
- package/src/presentation/routes/reconcile-provisioned-runners.ts +0 -30
- package/test/factories/provisioned-runner.ts +0 -78
|
@@ -8,7 +8,7 @@ import {eq, sql} from 'drizzle-orm';
|
|
|
8
8
|
import {EmptyRequiredLabelsError, RunnerSessionExhaustedError} from '#core/errors.js';
|
|
9
9
|
import {claimJobExecution} from '#core/job-executions.js';
|
|
10
10
|
import {detectAndExpireStuckJobs} from '#core/maintenance.js';
|
|
11
|
-
import {pendingJobFactory, runnerSessionFactory} from '#test/index.js';
|
|
11
|
+
import {pendingJobFactory, runnerSessionFactory, runnersTestAuthClient} from '#test/index.js';
|
|
12
12
|
import {db} from './db.js';
|
|
13
13
|
import {
|
|
14
14
|
cancelRunnerJobs,
|
|
@@ -385,10 +385,10 @@ describe('claimPendingJobExecution', () => {
|
|
|
385
385
|
|
|
386
386
|
it('enforces a non-null session claim cap from the database', async () => {
|
|
387
387
|
const provisionerId = crypto.randomUUID();
|
|
388
|
-
const
|
|
388
|
+
const providerRunnerId = `provisioned-runner-${crypto.randomUUID()}`;
|
|
389
389
|
await db()
|
|
390
390
|
.update(runnerSessions)
|
|
391
|
-
.set({registrationTokenKind: 'ephemeral', maxClaims: 1, provisionerId,
|
|
391
|
+
.set({registrationTokenKind: 'ephemeral', maxClaims: 1, provisionerId, providerRunnerId})
|
|
392
392
|
.where(eq(runnerSessions.id, runnerSessionId));
|
|
393
393
|
await pendingJobFactory.create({workspaceId});
|
|
394
394
|
await pendingJobFactory.create({workspaceId});
|
|
@@ -403,10 +403,10 @@ describe('claimPendingJobExecution', () => {
|
|
|
403
403
|
|
|
404
404
|
it('does not spend a claim when a capped session polls an empty queue', async () => {
|
|
405
405
|
const provisionerId = crypto.randomUUID();
|
|
406
|
-
const
|
|
406
|
+
const providerRunnerId = `provisioned-runner-${crypto.randomUUID()}`;
|
|
407
407
|
await db()
|
|
408
408
|
.update(runnerSessions)
|
|
409
|
-
.set({registrationTokenKind: 'ephemeral', maxClaims: 1, provisionerId,
|
|
409
|
+
.set({registrationTokenKind: 'ephemeral', maxClaims: 1, provisionerId, providerRunnerId})
|
|
410
410
|
.where(eq(runnerSessions.id, runnerSessionId));
|
|
411
411
|
|
|
412
412
|
const empty = await claimPendingJobExecution({workspaceId, runnerSessionId, maxClaims: 1});
|
|
@@ -494,15 +494,15 @@ describe('claimPendingJobExecution', () => {
|
|
|
494
494
|
expect(running[0]?.requiredLabels).toEqual(created.requiredLabels);
|
|
495
495
|
expect(running[0]?.runnerLabels).toEqual(sessionLabels);
|
|
496
496
|
expect(running[0]?.provisionerId).toBeNull();
|
|
497
|
-
expect(running[0]?.
|
|
497
|
+
expect(running[0]?.providerRunnerId).toBeNull();
|
|
498
498
|
});
|
|
499
499
|
|
|
500
500
|
it('copies an ephemeral session provisioned-runner link onto the running job', async () => {
|
|
501
501
|
const provisionerId = crypto.randomUUID();
|
|
502
|
-
const
|
|
502
|
+
const providerRunnerId = `provisioned-runner-${crypto.randomUUID()}`;
|
|
503
503
|
await db()
|
|
504
504
|
.update(runnerSessions)
|
|
505
|
-
.set({registrationTokenKind: 'ephemeral', maxClaims: 1, provisionerId,
|
|
505
|
+
.set({registrationTokenKind: 'ephemeral', maxClaims: 1, provisionerId, providerRunnerId})
|
|
506
506
|
.where(eq(runnerSessions.id, runnerSessionId));
|
|
507
507
|
const created = await pendingJobFactory.create({workspaceId});
|
|
508
508
|
|
|
@@ -513,7 +513,7 @@ describe('claimPendingJobExecution', () => {
|
|
|
513
513
|
.from(runningJobExecutions)
|
|
514
514
|
.where(eq(runningJobExecutions.jobId, created.jobId));
|
|
515
515
|
expect(running?.provisionerId).toBe(provisionerId);
|
|
516
|
-
expect(running?.
|
|
516
|
+
expect(running?.providerRunnerId).toBe(providerRunnerId);
|
|
517
517
|
});
|
|
518
518
|
|
|
519
519
|
it('rejects a running job row with a partial provisioned-runner link', async () => {
|
|
@@ -563,7 +563,7 @@ describe('claimPendingJobExecution', () => {
|
|
|
563
563
|
labels: sessionLabels,
|
|
564
564
|
toolCapabilities: {harnesses: {pi: {tools: ['read']}}},
|
|
565
565
|
});
|
|
566
|
-
const
|
|
566
|
+
const underRunnerInstance = await runnerSessionFactory.create({
|
|
567
567
|
workspaceId,
|
|
568
568
|
labels: sessionLabels,
|
|
569
569
|
toolCapabilities: {harnesses: {pi: {tools: []}}},
|
|
@@ -579,7 +579,7 @@ describe('claimPendingJobExecution', () => {
|
|
|
579
579
|
|
|
580
580
|
const firstClaim = await claimPendingJobExecution({
|
|
581
581
|
workspaceId,
|
|
582
|
-
runnerSessionId:
|
|
582
|
+
runnerSessionId: underRunnerInstance.id,
|
|
583
583
|
});
|
|
584
584
|
const secondClaim = await claimPendingJobExecution({
|
|
585
585
|
workspaceId,
|
|
@@ -758,6 +758,7 @@ describe('claimJobExecution', () => {
|
|
|
758
758
|
const created = await pendingJobFactory.create({workspaceId});
|
|
759
759
|
|
|
760
760
|
const claimed = await claimJobExecution({
|
|
761
|
+
auth: runnersTestAuthClient,
|
|
761
762
|
workspaceId,
|
|
762
763
|
runnerSessionId,
|
|
763
764
|
sessionLabels,
|
|
@@ -781,6 +782,7 @@ describe('claimJobExecution', () => {
|
|
|
781
782
|
|
|
782
783
|
it('returns null and mints no token when the queue is empty', async () => {
|
|
783
784
|
const claimed = await claimJobExecution({
|
|
785
|
+
auth: runnersTestAuthClient,
|
|
784
786
|
workspaceId,
|
|
785
787
|
runnerSessionId,
|
|
786
788
|
sessionLabels,
|
package/src/db/job-executions.ts
CHANGED
|
@@ -110,19 +110,19 @@ export interface ActiveRunningJobExecution {
|
|
|
110
110
|
projectId: string;
|
|
111
111
|
runnerSessionId: string;
|
|
112
112
|
provisionerId: string | null;
|
|
113
|
-
|
|
113
|
+
providerRunnerId: string | null;
|
|
114
114
|
requiredLabels: string[];
|
|
115
115
|
runnerLabels: string[];
|
|
116
116
|
startedAt: Date;
|
|
117
117
|
lastHeartbeatAt: Date;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
export interface
|
|
120
|
+
export interface RunnerInstanceBoundJobExecution {
|
|
121
121
|
workflowRunId: string;
|
|
122
122
|
workflowRunAttemptId: string;
|
|
123
123
|
jobId: string;
|
|
124
124
|
jobExecutionId: string;
|
|
125
|
-
|
|
125
|
+
providerRunnerId: string;
|
|
126
126
|
startedAt: Date;
|
|
127
127
|
lastHeartbeatAt: Date;
|
|
128
128
|
cancellationRequestedAt: Date | null;
|
|
@@ -145,29 +145,35 @@ export async function claimPendingJobExecution(params: {
|
|
|
145
145
|
|
|
146
146
|
return await db().transaction(async (tx) => {
|
|
147
147
|
let provisionerId: string | null = null;
|
|
148
|
-
let
|
|
148
|
+
let providerRunnerId: string | null = null;
|
|
149
149
|
|
|
150
150
|
if (params.maxClaims !== null) {
|
|
151
151
|
const [session] = await tx
|
|
152
152
|
.select({
|
|
153
153
|
maxClaims: runnerSessions.maxClaims,
|
|
154
154
|
claimsUsed: runnerSessions.claimsUsed,
|
|
155
|
+
revokedAt: runnerSessions.revokedAt,
|
|
155
156
|
provisionerId: runnerSessions.provisionerId,
|
|
156
|
-
|
|
157
|
+
providerRunnerId: runnerSessions.providerRunnerId,
|
|
157
158
|
})
|
|
158
159
|
.from(runnerSessions)
|
|
159
160
|
.where(eq(runnerSessions.id, params.runnerSessionId))
|
|
160
161
|
.limit(1)
|
|
161
162
|
.for('update');
|
|
162
163
|
|
|
163
|
-
if (
|
|
164
|
+
if (
|
|
165
|
+
!session ||
|
|
166
|
+
session.revokedAt ||
|
|
167
|
+
session.maxClaims === null ||
|
|
168
|
+
session.claimsUsed >= session.maxClaims
|
|
169
|
+
) {
|
|
164
170
|
throw new RunnerSessionExhaustedError(params.runnerSessionId);
|
|
165
171
|
}
|
|
166
172
|
|
|
167
173
|
// Ephemeral sessions are the only capped sessions, and the DB check keeps
|
|
168
174
|
// their provisioned-runner link present as a pair.
|
|
169
175
|
provisionerId = session.provisionerId;
|
|
170
|
-
|
|
176
|
+
providerRunnerId = session.providerRunnerId;
|
|
171
177
|
}
|
|
172
178
|
|
|
173
179
|
// `id` is a uuidv7 (time-ordered), so it is a deterministic FIFO tiebreaker
|
|
@@ -206,7 +212,7 @@ export async function claimPendingJobExecution(params: {
|
|
|
206
212
|
projectId: row.projectId,
|
|
207
213
|
runnerSessionId: params.runnerSessionId,
|
|
208
214
|
provisionerId,
|
|
209
|
-
|
|
215
|
+
providerRunnerId,
|
|
210
216
|
requiredLabels: row.requiredLabels,
|
|
211
217
|
runnerLabels: params.sessionLabels,
|
|
212
218
|
})
|
|
@@ -409,7 +415,7 @@ export async function listActiveRunningJobExecutions(params: {
|
|
|
409
415
|
projectId: runningJobExecutions.projectId,
|
|
410
416
|
runnerSessionId: runningJobExecutions.runnerSessionId,
|
|
411
417
|
provisionerId: runningJobExecutions.provisionerId,
|
|
412
|
-
|
|
418
|
+
providerRunnerId: runningJobExecutions.providerRunnerId,
|
|
413
419
|
requiredLabels: runningJobExecutions.requiredLabels,
|
|
414
420
|
runnerLabels: runningJobExecutions.runnerLabels,
|
|
415
421
|
startedAt: runningJobExecutions.startedAt,
|
|
@@ -426,19 +432,19 @@ export async function listActiveRunningJobExecutions(params: {
|
|
|
426
432
|
.limit(params.limit ?? 1000);
|
|
427
433
|
}
|
|
428
434
|
|
|
429
|
-
export async function
|
|
435
|
+
export async function listRunningJobExecutionsByRunnerInstanceTx(
|
|
430
436
|
tx: Tx,
|
|
431
437
|
params: {
|
|
432
438
|
workspaceId: string;
|
|
433
439
|
provisionerId: string;
|
|
434
|
-
|
|
440
|
+
providerRunnerIds: string[];
|
|
435
441
|
},
|
|
436
|
-
): Promise<
|
|
437
|
-
if (params.
|
|
442
|
+
): Promise<RunnerInstanceBoundJobExecution[]> {
|
|
443
|
+
if (params.providerRunnerIds.length === 0) return [];
|
|
438
444
|
|
|
439
445
|
const duplicateRows = await tx
|
|
440
446
|
.select({
|
|
441
|
-
|
|
447
|
+
providerRunnerId: runningJobExecutions.providerRunnerId,
|
|
442
448
|
count: count(),
|
|
443
449
|
})
|
|
444
450
|
.from(runningJobExecutions)
|
|
@@ -446,21 +452,21 @@ export async function listRunningJobExecutionsByProvisionedRunnerTx(
|
|
|
446
452
|
and(
|
|
447
453
|
eq(runningJobExecutions.workspaceId, params.workspaceId),
|
|
448
454
|
eq(runningJobExecutions.provisionerId, params.provisionerId),
|
|
449
|
-
inArray(runningJobExecutions.
|
|
455
|
+
inArray(runningJobExecutions.providerRunnerId, params.providerRunnerIds),
|
|
450
456
|
),
|
|
451
457
|
)
|
|
452
|
-
.groupBy(runningJobExecutions.
|
|
458
|
+
.groupBy(runningJobExecutions.providerRunnerId)
|
|
453
459
|
.having(sql`count(*) > 1`);
|
|
454
460
|
|
|
455
|
-
const
|
|
456
|
-
row.
|
|
461
|
+
const duplicateRunnerInstanceIds = duplicateRows.flatMap((row) =>
|
|
462
|
+
row.providerRunnerId ? [row.providerRunnerId] : [],
|
|
457
463
|
);
|
|
458
|
-
if (
|
|
464
|
+
if (duplicateRunnerInstanceIds.length > 0) {
|
|
459
465
|
logger().warn(
|
|
460
466
|
{
|
|
461
467
|
workspaceId: params.workspaceId,
|
|
462
468
|
provisionerId: params.provisionerId,
|
|
463
|
-
|
|
469
|
+
providerRunnerIds: duplicateRunnerInstanceIds,
|
|
464
470
|
},
|
|
465
471
|
'multiple running job executions are bound to the same provisioned runner',
|
|
466
472
|
);
|
|
@@ -471,17 +477,17 @@ export async function listRunningJobExecutionsByProvisionedRunnerTx(
|
|
|
471
477
|
workflowRunAttemptId: string;
|
|
472
478
|
jobId: string;
|
|
473
479
|
jobExecutionId: string;
|
|
474
|
-
|
|
480
|
+
providerRunnerId: string;
|
|
475
481
|
startedAt: Date | string;
|
|
476
482
|
lastHeartbeatAt: Date | string;
|
|
477
483
|
cancellationRequestedAt: Date | string | null;
|
|
478
484
|
}>(sql`
|
|
479
|
-
SELECT DISTINCT ON (${runningJobExecutions.
|
|
485
|
+
SELECT DISTINCT ON (${runningJobExecutions.providerRunnerId})
|
|
480
486
|
${runningJobExecutions.workflowRunId} AS "workflowRunId",
|
|
481
487
|
${runningJobExecutions.workflowRunAttemptId} AS "workflowRunAttemptId",
|
|
482
488
|
${runningJobExecutions.jobId} AS "jobId",
|
|
483
489
|
${runningJobExecutions.jobExecutionId} AS "jobExecutionId",
|
|
484
|
-
${runningJobExecutions.
|
|
490
|
+
${runningJobExecutions.providerRunnerId} AS "providerRunnerId",
|
|
485
491
|
${runningJobExecutions.startedAt} AS "startedAt",
|
|
486
492
|
${runningJobExecutions.lastHeartbeatAt} AS "lastHeartbeatAt",
|
|
487
493
|
${runningJobExecutions.cancellationRequestedAt} AS "cancellationRequestedAt"
|
|
@@ -489,11 +495,11 @@ export async function listRunningJobExecutionsByProvisionedRunnerTx(
|
|
|
489
495
|
WHERE
|
|
490
496
|
${runningJobExecutions.workspaceId} = ${params.workspaceId}
|
|
491
497
|
AND ${runningJobExecutions.provisionerId} = ${params.provisionerId}
|
|
492
|
-
AND ${runningJobExecutions.
|
|
493
|
-
params.
|
|
498
|
+
AND ${runningJobExecutions.providerRunnerId} IN (${sql.join(
|
|
499
|
+
params.providerRunnerIds.map((providerRunnerId) => sql`${providerRunnerId}`),
|
|
494
500
|
sql`, `,
|
|
495
501
|
)})
|
|
496
|
-
ORDER BY ${runningJobExecutions.
|
|
502
|
+
ORDER BY ${runningJobExecutions.providerRunnerId}, ${runningJobExecutions.startedAt} DESC, ${runningJobExecutions.jobExecutionId} DESC
|
|
497
503
|
`);
|
|
498
504
|
|
|
499
505
|
return result.rows.map((row) => ({
|
|
@@ -501,7 +507,7 @@ export async function listRunningJobExecutionsByProvisionedRunnerTx(
|
|
|
501
507
|
workflowRunAttemptId: row.workflowRunAttemptId,
|
|
502
508
|
jobId: row.jobId,
|
|
503
509
|
jobExecutionId: row.jobExecutionId,
|
|
504
|
-
|
|
510
|
+
providerRunnerId: row.providerRunnerId,
|
|
505
511
|
startedAt: toDate(row.startedAt),
|
|
506
512
|
lastHeartbeatAt: toDate(row.lastHeartbeatAt),
|
|
507
513
|
cancellationRequestedAt: row.cancellationRequestedAt
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import {and, eq} from 'drizzle-orm';
|
|
2
|
+
import {db} from '#db/db.js';
|
|
3
|
+
import {
|
|
4
|
+
hasActiveWorkspaceProvisionerCapability,
|
|
5
|
+
listActiveWorkspaceProvisionerCapabilitySnapshots,
|
|
6
|
+
listStaleWorkspaceProvisionerCapabilitySnapshots,
|
|
7
|
+
publishWorkspaceProvisionerCapabilitySnapshot,
|
|
8
|
+
} from '#db/provisioner-capability-snapshots.js';
|
|
9
|
+
import {provisionerCapabilitySnapshots} from '#db/schema/provisioner-capability-snapshots.js';
|
|
10
|
+
import {provisionerTokens} from '#db/schema/provisioner-tokens.js';
|
|
11
|
+
import {provisionerTokenFactory} from '#test/index.js';
|
|
12
|
+
|
|
13
|
+
describe('workspace provisioner capability snapshots', () => {
|
|
14
|
+
let workspaceId: string;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
workspaceId = crypto.randomUUID();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('atomically replaces one provisioner complete template snapshot', async () => {
|
|
21
|
+
const provisioner = await provisionerTokenFactory.create({workspaceId});
|
|
22
|
+
|
|
23
|
+
await publishWorkspaceProvisionerCapabilitySnapshot({
|
|
24
|
+
workspaceId,
|
|
25
|
+
provisionerId: provisioner.id,
|
|
26
|
+
templates: [template('linux', ['linux'], 4, 1, 3), template('macos', ['macos'], 2, 0, 2)],
|
|
27
|
+
});
|
|
28
|
+
await publishWorkspaceProvisionerCapabilitySnapshot({
|
|
29
|
+
workspaceId,
|
|
30
|
+
provisionerId: provisioner.id,
|
|
31
|
+
templates: [template('linux-arm64', ['linux', 'arm64'], 0, 1, 0)],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const rows = await snapshotsFor(provisioner.id);
|
|
35
|
+
expect(rows).toEqual([
|
|
36
|
+
expect.objectContaining({
|
|
37
|
+
templateKey: 'linux-arm64',
|
|
38
|
+
labels: ['arm64', 'linux'],
|
|
39
|
+
availableSlots: 0,
|
|
40
|
+
starting: 1,
|
|
41
|
+
running: 0,
|
|
42
|
+
}),
|
|
43
|
+
]);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('keeps zero-slot overlapping capability active for every workspace provisioner', async () => {
|
|
47
|
+
const first = await provisionerTokenFactory.create({workspaceId});
|
|
48
|
+
const second = await provisionerTokenFactory.create({workspaceId});
|
|
49
|
+
|
|
50
|
+
await publishWorkspaceProvisionerCapabilitySnapshot({
|
|
51
|
+
workspaceId,
|
|
52
|
+
provisionerId: first.id,
|
|
53
|
+
templates: [template('linux', ['linux', 'x64'], 0, 0, 1)],
|
|
54
|
+
});
|
|
55
|
+
await publishWorkspaceProvisionerCapabilitySnapshot({
|
|
56
|
+
workspaceId,
|
|
57
|
+
provisionerId: second.id,
|
|
58
|
+
templates: [template('linux-gpu', ['linux', 'gpu'], 2, 1, 1)],
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const active = await listActiveWorkspaceProvisionerCapabilitySnapshots({
|
|
62
|
+
workspaceId,
|
|
63
|
+
windowSeconds: 60,
|
|
64
|
+
});
|
|
65
|
+
const hasLinux = await hasActiveWorkspaceProvisionerCapability({
|
|
66
|
+
workspaceId,
|
|
67
|
+
requiredLabels: ['linux'],
|
|
68
|
+
windowSeconds: 60,
|
|
69
|
+
});
|
|
70
|
+
const hasLinuxX64 = await hasActiveWorkspaceProvisionerCapability({
|
|
71
|
+
workspaceId,
|
|
72
|
+
requiredLabels: ['linux', 'x64'],
|
|
73
|
+
windowSeconds: 60,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(active).toEqual(
|
|
77
|
+
expect.arrayContaining([
|
|
78
|
+
expect.objectContaining({provisionerId: first.id, availableSlots: 0}),
|
|
79
|
+
expect.objectContaining({provisionerId: second.id, labels: ['gpu', 'linux']}),
|
|
80
|
+
]),
|
|
81
|
+
);
|
|
82
|
+
expect(hasLinux).toBe(true);
|
|
83
|
+
expect(hasLinuxX64).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('requires every advertised label to be present, not just an overlap', async () => {
|
|
87
|
+
const provisioner = await provisionerTokenFactory.create({workspaceId});
|
|
88
|
+
|
|
89
|
+
await publishWorkspaceProvisionerCapabilitySnapshot({
|
|
90
|
+
workspaceId,
|
|
91
|
+
provisionerId: provisioner.id,
|
|
92
|
+
templates: [template('linux', ['linux'], 1, 0, 0)],
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const hasLinuxArm64 = await hasActiveWorkspaceProvisionerCapability({
|
|
96
|
+
workspaceId,
|
|
97
|
+
requiredLabels: ['linux', 'arm64'],
|
|
98
|
+
windowSeconds: 60,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(hasLinuxArm64).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('excludes stale and revoked provisioner capability from active matching', async () => {
|
|
105
|
+
const staleProvisioner = await provisionerTokenFactory.create({workspaceId});
|
|
106
|
+
const revokedProvisioner = await provisionerTokenFactory.create({workspaceId});
|
|
107
|
+
|
|
108
|
+
await publishWorkspaceProvisionerCapabilitySnapshot({
|
|
109
|
+
workspaceId,
|
|
110
|
+
provisionerId: staleProvisioner.id,
|
|
111
|
+
templates: [template('stale-linux', ['linux'], 1, 0, 1)],
|
|
112
|
+
});
|
|
113
|
+
await publishWorkspaceProvisionerCapabilitySnapshot({
|
|
114
|
+
workspaceId,
|
|
115
|
+
provisionerId: revokedProvisioner.id,
|
|
116
|
+
templates: [template('revoked-macos', ['macos'], 1, 0, 1)],
|
|
117
|
+
});
|
|
118
|
+
await db()
|
|
119
|
+
.update(provisionerCapabilitySnapshots)
|
|
120
|
+
.set({advertisedAt: new Date(Date.now() - 61_000)})
|
|
121
|
+
.where(eq(provisionerCapabilitySnapshots.provisionerId, staleProvisioner.id));
|
|
122
|
+
await db()
|
|
123
|
+
.update(provisionerTokens)
|
|
124
|
+
.set({revokedAt: new Date()})
|
|
125
|
+
.where(eq(provisionerTokens.id, revokedProvisioner.id));
|
|
126
|
+
|
|
127
|
+
const active = await listActiveWorkspaceProvisionerCapabilitySnapshots({
|
|
128
|
+
workspaceId,
|
|
129
|
+
windowSeconds: 60,
|
|
130
|
+
});
|
|
131
|
+
const stale = await listStaleWorkspaceProvisionerCapabilitySnapshots({
|
|
132
|
+
workspaceId,
|
|
133
|
+
windowSeconds: 60,
|
|
134
|
+
});
|
|
135
|
+
const hasLinux = await hasActiveWorkspaceProvisionerCapability({
|
|
136
|
+
workspaceId,
|
|
137
|
+
requiredLabels: ['linux'],
|
|
138
|
+
windowSeconds: 60,
|
|
139
|
+
});
|
|
140
|
+
const hasMacos = await hasActiveWorkspaceProvisionerCapability({
|
|
141
|
+
workspaceId,
|
|
142
|
+
requiredLabels: ['macos'],
|
|
143
|
+
windowSeconds: 60,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect(active).toEqual([]);
|
|
147
|
+
expect(stale.map((snapshot) => snapshot.provisionerId).sort()).toEqual(
|
|
148
|
+
[staleProvisioner.id, revokedProvisioner.id].sort(),
|
|
149
|
+
);
|
|
150
|
+
expect(hasLinux).toBe(false);
|
|
151
|
+
expect(hasMacos).toBe(false);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
function snapshotsFor(provisionerId: string) {
|
|
155
|
+
return db()
|
|
156
|
+
.select()
|
|
157
|
+
.from(provisionerCapabilitySnapshots)
|
|
158
|
+
.where(
|
|
159
|
+
and(
|
|
160
|
+
eq(provisionerCapabilitySnapshots.workspaceId, workspaceId),
|
|
161
|
+
eq(provisionerCapabilitySnapshots.provisionerId, provisionerId),
|
|
162
|
+
),
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function template(
|
|
167
|
+
templateKey: string,
|
|
168
|
+
labels: string[],
|
|
169
|
+
availableSlots: number,
|
|
170
|
+
starting: number,
|
|
171
|
+
running: number,
|
|
172
|
+
) {
|
|
173
|
+
return {templateKey, labels, availableSlots, starting, running};
|
|
174
|
+
}
|
|
175
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {canonicalizeLabels} from '@shipfox/runner-labels';
|
|
2
|
+
import {and, arrayContains, eq, gt, isNotNull, isNull, lte, or, sql} from 'drizzle-orm';
|
|
3
|
+
import type {ProvisionerCapabilitySnapshot} from '#core/entities/provisioner-capability-snapshot.js';
|
|
4
|
+
import {db} from './db.js';
|
|
5
|
+
import type {ReservationTemplate} from './reservations.js';
|
|
6
|
+
import {
|
|
7
|
+
provisionerCapabilitySnapshots,
|
|
8
|
+
toProvisionerCapabilitySnapshot,
|
|
9
|
+
} from './schema/provisioner-capability-snapshots.js';
|
|
10
|
+
import {provisionerTokens} from './schema/provisioner-tokens.js';
|
|
11
|
+
|
|
12
|
+
export async function publishWorkspaceProvisionerCapabilitySnapshot(params: {
|
|
13
|
+
workspaceId: string;
|
|
14
|
+
provisionerId: string;
|
|
15
|
+
templates: ReservationTemplate[];
|
|
16
|
+
}): Promise<void> {
|
|
17
|
+
await db().transaction(async (tx) => {
|
|
18
|
+
await tx
|
|
19
|
+
.delete(provisionerCapabilitySnapshots)
|
|
20
|
+
.where(eq(provisionerCapabilitySnapshots.provisionerId, params.provisionerId));
|
|
21
|
+
|
|
22
|
+
if (params.templates.length === 0) return;
|
|
23
|
+
|
|
24
|
+
await tx.insert(provisionerCapabilitySnapshots).values(
|
|
25
|
+
params.templates.map((template) => ({
|
|
26
|
+
workspaceId: params.workspaceId,
|
|
27
|
+
provisionerId: params.provisionerId,
|
|
28
|
+
templateKey: template.templateKey,
|
|
29
|
+
labels: [...canonicalizeLabels(template.labels)],
|
|
30
|
+
availableSlots: template.availableSlots,
|
|
31
|
+
starting: template.starting,
|
|
32
|
+
running: template.running,
|
|
33
|
+
})),
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function listActiveWorkspaceProvisionerCapabilitySnapshots(params: {
|
|
39
|
+
workspaceId: string;
|
|
40
|
+
windowSeconds: number;
|
|
41
|
+
}): Promise<ProvisionerCapabilitySnapshot[]> {
|
|
42
|
+
const rows = await db()
|
|
43
|
+
.select({snapshot: provisionerCapabilitySnapshots})
|
|
44
|
+
.from(provisionerCapabilitySnapshots)
|
|
45
|
+
.innerJoin(
|
|
46
|
+
provisionerTokens,
|
|
47
|
+
eq(provisionerTokens.id, provisionerCapabilitySnapshots.provisionerId),
|
|
48
|
+
)
|
|
49
|
+
.where(and(...activeWorkspaceProvisionerCapabilityConditions(params)));
|
|
50
|
+
|
|
51
|
+
return rows.map(({snapshot}) => toProvisionerCapabilitySnapshot(snapshot));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function listStaleWorkspaceProvisionerCapabilitySnapshots(params: {
|
|
55
|
+
workspaceId: string;
|
|
56
|
+
windowSeconds: number;
|
|
57
|
+
}): Promise<ProvisionerCapabilitySnapshot[]> {
|
|
58
|
+
const rows = await db()
|
|
59
|
+
.select({snapshot: provisionerCapabilitySnapshots})
|
|
60
|
+
.from(provisionerCapabilitySnapshots)
|
|
61
|
+
.innerJoin(
|
|
62
|
+
provisionerTokens,
|
|
63
|
+
eq(provisionerTokens.id, provisionerCapabilitySnapshots.provisionerId),
|
|
64
|
+
)
|
|
65
|
+
.where(
|
|
66
|
+
and(
|
|
67
|
+
eq(provisionerCapabilitySnapshots.workspaceId, params.workspaceId),
|
|
68
|
+
or(
|
|
69
|
+
lte(
|
|
70
|
+
provisionerCapabilitySnapshots.advertisedAt,
|
|
71
|
+
sql`now() - (${params.windowSeconds} || ' seconds')::interval`,
|
|
72
|
+
),
|
|
73
|
+
eq(provisionerTokens.scope, 'installation'),
|
|
74
|
+
isNull(provisionerTokens.workspaceId),
|
|
75
|
+
provisionerTokenIsUnavailable(),
|
|
76
|
+
),
|
|
77
|
+
),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return rows.map(({snapshot}) => toProvisionerCapabilitySnapshot(snapshot));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function hasActiveWorkspaceProvisionerCapability(params: {
|
|
84
|
+
workspaceId: string;
|
|
85
|
+
requiredLabels: string[];
|
|
86
|
+
windowSeconds: number;
|
|
87
|
+
}): Promise<boolean> {
|
|
88
|
+
const [row] = await db()
|
|
89
|
+
.select({id: provisionerCapabilitySnapshots.id})
|
|
90
|
+
.from(provisionerCapabilitySnapshots)
|
|
91
|
+
.innerJoin(
|
|
92
|
+
provisionerTokens,
|
|
93
|
+
eq(provisionerTokens.id, provisionerCapabilitySnapshots.provisionerId),
|
|
94
|
+
)
|
|
95
|
+
.where(
|
|
96
|
+
and(
|
|
97
|
+
...activeWorkspaceProvisionerCapabilityConditions(params),
|
|
98
|
+
arrayContains(provisionerCapabilitySnapshots.labels, [
|
|
99
|
+
...canonicalizeLabels(params.requiredLabels),
|
|
100
|
+
]),
|
|
101
|
+
),
|
|
102
|
+
)
|
|
103
|
+
.limit(1);
|
|
104
|
+
|
|
105
|
+
return row !== undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function activeWorkspaceProvisionerCapabilityConditions(params: {
|
|
109
|
+
workspaceId: string;
|
|
110
|
+
windowSeconds: number;
|
|
111
|
+
}) {
|
|
112
|
+
return [
|
|
113
|
+
eq(provisionerCapabilitySnapshots.workspaceId, params.workspaceId),
|
|
114
|
+
eq(provisionerTokens.workspaceId, params.workspaceId),
|
|
115
|
+
eq(provisionerTokens.scope, 'workspace' as const),
|
|
116
|
+
isNull(provisionerTokens.revokedAt),
|
|
117
|
+
or(isNull(provisionerTokens.expiresAt), gt(provisionerTokens.expiresAt, sql`now()`)),
|
|
118
|
+
gt(
|
|
119
|
+
provisionerCapabilitySnapshots.advertisedAt,
|
|
120
|
+
sql`now() - (${params.windowSeconds} || ' seconds')::interval`,
|
|
121
|
+
),
|
|
122
|
+
];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function provisionerTokenIsUnavailable() {
|
|
126
|
+
return or(
|
|
127
|
+
isNotNull(provisionerTokens.revokedAt),
|
|
128
|
+
and(isNotNull(provisionerTokens.expiresAt), lte(provisionerTokens.expiresAt, sql`now()`)),
|
|
129
|
+
);
|
|
130
|
+
}
|