@shipfox/api-runners 2.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 +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +187 -0
- package/LICENSE +21 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +208 -0
- package/dist/config.js.map +1 -0
- package/dist/core/demand.d.ts +33 -0
- package/dist/core/demand.d.ts.map +1 -0
- package/dist/core/demand.js +169 -0
- package/dist/core/demand.js.map +1 -0
- package/dist/core/entities/ephemeral-registration-token.d.ts +14 -0
- package/dist/core/entities/ephemeral-registration-token.d.ts.map +1 -0
- package/dist/core/entities/ephemeral-registration-token.js +3 -0
- package/dist/core/entities/ephemeral-registration-token.js.map +1 -0
- package/dist/core/entities/manual-registration-token.d.ts +12 -0
- package/dist/core/entities/manual-registration-token.d.ts.map +1 -0
- package/dist/core/entities/manual-registration-token.js +3 -0
- package/dist/core/entities/manual-registration-token.js.map +1 -0
- package/dist/core/entities/provisioned-runner.d.ts +24 -0
- package/dist/core/entities/provisioned-runner.d.ts.map +1 -0
- package/dist/core/entities/provisioned-runner.js +3 -0
- package/dist/core/entities/provisioned-runner.js.map +1 -0
- package/dist/core/entities/provisioner-token.d.ts +18 -0
- package/dist/core/entities/provisioner-token.d.ts.map +1 -0
- package/dist/core/entities/provisioner-token.js +3 -0
- package/dist/core/entities/provisioner-token.js.map +1 -0
- package/dist/core/entities/reservation.d.ts +10 -0
- package/dist/core/entities/reservation.d.ts.map +1 -0
- package/dist/core/entities/reservation.js +3 -0
- package/dist/core/entities/reservation.js.map +1 -0
- package/dist/core/entities/runner-session.d.ts +18 -0
- package/dist/core/entities/runner-session.d.ts.map +1 -0
- package/dist/core/entities/runner-session.js +3 -0
- package/dist/core/entities/runner-session.js.map +1 -0
- package/dist/core/ephemeral-registration-tokens.d.ts +31 -0
- package/dist/core/ephemeral-registration-tokens.d.ts.map +1 -0
- package/dist/core/ephemeral-registration-tokens.js +61 -0
- package/dist/core/ephemeral-registration-tokens.js.map +1 -0
- package/dist/core/errors.d.ts +62 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +92 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/index.d.ts +14 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +10 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/job-executions.d.ts +14 -0
- package/dist/core/job-executions.d.ts.map +1 -0
- package/dist/core/job-executions.js +37 -0
- package/dist/core/job-executions.js.map +1 -0
- package/dist/core/maintenance-policy.d.ts +2 -0
- package/dist/core/maintenance-policy.d.ts.map +1 -0
- package/dist/core/maintenance-policy.js +3 -0
- package/dist/core/maintenance-policy.js.map +1 -0
- package/dist/core/maintenance.d.ts +33 -0
- package/dist/core/maintenance.d.ts.map +1 -0
- package/dist/core/maintenance.js +55 -0
- package/dist/core/maintenance.js.map +1 -0
- package/dist/core/manual-registration-tokens.d.ts +17 -0
- package/dist/core/manual-registration-tokens.d.ts.map +1 -0
- package/dist/core/manual-registration-tokens.js +28 -0
- package/dist/core/manual-registration-tokens.js.map +1 -0
- package/dist/core/provisioned-runners.d.ts +67 -0
- package/dist/core/provisioned-runners.d.ts.map +1 -0
- package/dist/core/provisioned-runners.js +162 -0
- package/dist/core/provisioned-runners.js.map +1 -0
- package/dist/core/provisioner-tokens.d.ts +20 -0
- package/dist/core/provisioner-tokens.d.ts.map +1 -0
- package/dist/core/provisioner-tokens.js +36 -0
- package/dist/core/provisioner-tokens.js.map +1 -0
- package/dist/core/rate-limit.d.ts +35 -0
- package/dist/core/rate-limit.d.ts.map +1 -0
- package/dist/core/rate-limit.js +74 -0
- package/dist/core/rate-limit.js.map +1 -0
- package/dist/core/runner-sessions.d.ts +26 -0
- package/dist/core/runner-sessions.d.ts.map +1 -0
- package/dist/core/runner-sessions.js +41 -0
- package/dist/core/runner-sessions.js.map +1 -0
- package/dist/core/runner-tool-capabilities.d.ts +24 -0
- package/dist/core/runner-tool-capabilities.d.ts.map +1 -0
- package/dist/core/runner-tool-capabilities.js +43 -0
- package/dist/core/runner-tool-capabilities.js.map +1 -0
- package/dist/db/db.d.ts +4478 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +36 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/ephemeral-registration-tokens.d.ts +47 -0
- package/dist/db/ephemeral-registration-tokens.d.ts.map +1 -0
- package/dist/db/ephemeral-registration-tokens.js +170 -0
- package/dist/db/ephemeral-registration-tokens.js.map +1 -0
- package/dist/db/index.d.ts +19 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +15 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/job-executions.d.ts +128 -0
- package/dist/db/job-executions.d.ts.map +1 -0
- package/dist/db/job-executions.js +354 -0
- package/dist/db/job-executions.js.map +1 -0
- package/dist/db/manual-registration-tokens.d.ts +16 -0
- package/dist/db/manual-registration-tokens.d.ts.map +1 -0
- package/dist/db/manual-registration-tokens.js +37 -0
- package/dist/db/manual-registration-tokens.js.map +1 -0
- package/dist/db/provisioned-runners.d.ts +79 -0
- package/dist/db/provisioned-runners.d.ts.map +1 -0
- package/dist/db/provisioned-runners.js +466 -0
- package/dist/db/provisioned-runners.js.map +1 -0
- package/dist/db/provisioner-tokens.d.ts +26 -0
- package/dist/db/provisioner-tokens.d.ts.map +1 -0
- package/dist/db/provisioner-tokens.js +54 -0
- package/dist/db/provisioner-tokens.js.map +1 -0
- package/dist/db/rate-limits.d.ts +18 -0
- package/dist/db/rate-limits.d.ts.map +1 -0
- package/dist/db/rate-limits.js +44 -0
- package/dist/db/rate-limits.js.map +1 -0
- package/dist/db/reservations.d.ts +48 -0
- package/dist/db/reservations.d.ts.map +1 -0
- package/dist/db/reservations.js +162 -0
- package/dist/db/reservations.js.map +1 -0
- package/dist/db/runner-sessions.d.ts +18 -0
- package/dist/db/runner-sessions.d.ts.map +1 -0
- package/dist/db/runner-sessions.js +38 -0
- package/dist/db/runner-sessions.js.map +1 -0
- package/dist/db/schema/common.d.ts +2 -0
- package/dist/db/schema/common.d.ts.map +1 -0
- package/dist/db/schema/common.js +4 -0
- package/dist/db/schema/common.js.map +1 -0
- package/dist/db/schema/ephemeral-registration-tokens.d.ts +199 -0
- package/dist/db/schema/ephemeral-registration-tokens.d.ts.map +1 -0
- package/dist/db/schema/ephemeral-registration-tokens.js +45 -0
- package/dist/db/schema/ephemeral-registration-tokens.js.map +1 -0
- package/dist/db/schema/manual-registration-tokens.d.ts +165 -0
- package/dist/db/schema/manual-registration-tokens.d.ts.map +1 -0
- package/dist/db/schema/manual-registration-tokens.js +40 -0
- package/dist/db/schema/manual-registration-tokens.js.map +1 -0
- package/dist/db/schema/outbox.d.ts +195 -0
- package/dist/db/schema/outbox.d.ts.map +1 -0
- package/dist/db/schema/outbox.js +5 -0
- package/dist/db/schema/outbox.js.map +1 -0
- package/dist/db/schema/pending-job-executions.d.ts +189 -0
- package/dist/db/schema/pending-job-executions.d.ts.map +1 -0
- package/dist/db/schema/pending-job-executions.js +21 -0
- package/dist/db/schema/pending-job-executions.js.map +1 -0
- package/dist/db/schema/provisioned-runners.d.ts +379 -0
- package/dist/db/schema/provisioned-runners.d.ts.map +1 -0
- package/dist/db/schema/provisioned-runners.js +83 -0
- package/dist/db/schema/provisioned-runners.js.map +1 -0
- package/dist/db/schema/provisioner-tokens.d.ts +216 -0
- package/dist/db/schema/provisioner-tokens.d.ts.map +1 -0
- package/dist/db/schema/provisioner-tokens.js +48 -0
- package/dist/db/schema/provisioner-tokens.js.map +1 -0
- package/dist/db/schema/rate-limits.d.ts +145 -0
- package/dist/db/schema/rate-limits.d.ts.map +1 -0
- package/dist/db/schema/rate-limits.js +25 -0
- package/dist/db/schema/rate-limits.js.map +1 -0
- package/dist/db/schema/reservations.d.ts +157 -0
- package/dist/db/schema/reservations.d.ts.map +1 -0
- package/dist/db/schema/reservations.js +34 -0
- package/dist/db/schema/reservations.js.map +1 -0
- package/dist/db/schema/runner-sessions.d.ts +298 -0
- package/dist/db/schema/runner-sessions.d.ts.map +1 -0
- package/dist/db/schema/runner-sessions.js +61 -0
- package/dist/db/schema/runner-sessions.js.map +1 -0
- package/dist/db/schema/running-job-executions.d.ts +334 -0
- package/dist/db/schema/running-job-executions.d.ts.map +1 -0
- package/dist/db/schema/running-job-executions.js +41 -0
- package/dist/db/schema/running-job-executions.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics/index.d.ts +3 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +4 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/instance.d.ts +34 -0
- package/dist/metrics/instance.d.ts.map +1 -0
- package/dist/metrics/instance.js +60 -0
- package/dist/metrics/instance.js.map +1 -0
- package/dist/metrics/service.d.ts +2 -0
- package/dist/metrics/service.d.ts.map +1 -0
- package/dist/metrics/service.js +21 -0
- package/dist/metrics/service.js.map +1 -0
- package/dist/presentation/auth/index.d.ts +3 -0
- package/dist/presentation/auth/index.d.ts.map +1 -0
- package/dist/presentation/auth/index.js +4 -0
- package/dist/presentation/auth/index.js.map +1 -0
- package/dist/presentation/auth/provisioner-token-auth.d.ts +3 -0
- package/dist/presentation/auth/provisioner-token-auth.d.ts.map +1 -0
- package/dist/presentation/auth/provisioner-token-auth.js +81 -0
- package/dist/presentation/auth/provisioner-token-auth.js.map +1 -0
- package/dist/presentation/auth/runner-registration-token-auth.d.ts +17 -0
- package/dist/presentation/auth/runner-registration-token-auth.d.ts.map +1 -0
- package/dist/presentation/auth/runner-registration-token-auth.js +77 -0
- package/dist/presentation/auth/runner-registration-token-auth.js.map +1 -0
- package/dist/presentation/dto/active-provisioner.d.ts +8 -0
- package/dist/presentation/dto/active-provisioner.d.ts.map +1 -0
- package/dist/presentation/dto/active-provisioner.js +10 -0
- package/dist/presentation/dto/active-provisioner.js.map +1 -0
- package/dist/presentation/dto/index.d.ts +7 -0
- package/dist/presentation/dto/index.d.ts.map +1 -0
- package/dist/presentation/dto/index.js +8 -0
- package/dist/presentation/dto/index.js.map +1 -0
- package/dist/presentation/dto/manual-registration-token.d.ts +12 -0
- package/dist/presentation/dto/manual-registration-token.d.ts.map +1 -0
- package/dist/presentation/dto/manual-registration-token.js +14 -0
- package/dist/presentation/dto/manual-registration-token.js.map +1 -0
- package/dist/presentation/dto/mint-registration-tokens.d.ts +5 -0
- package/dist/presentation/dto/mint-registration-tokens.d.ts.map +1 -0
- package/dist/presentation/dto/mint-registration-tokens.js +19 -0
- package/dist/presentation/dto/mint-registration-tokens.js.map +1 -0
- package/dist/presentation/dto/poll-demand.d.ts +4 -0
- package/dist/presentation/dto/poll-demand.d.ts.map +1 -0
- package/dist/presentation/dto/poll-demand.js +19 -0
- package/dist/presentation/dto/poll-demand.js.map +1 -0
- package/dist/presentation/dto/provisioned-runners.d.ts +38 -0
- package/dist/presentation/dto/provisioned-runners.d.ts.map +1 -0
- package/dist/presentation/dto/provisioned-runners.js +44 -0
- package/dist/presentation/dto/provisioned-runners.js.map +1 -0
- package/dist/presentation/dto/provisioner-token.d.ts +15 -0
- package/dist/presentation/dto/provisioner-token.d.ts.map +1 -0
- package/dist/presentation/dto/provisioner-token.js +17 -0
- package/dist/presentation/dto/provisioner-token.js.map +1 -0
- package/dist/presentation/index.d.ts +4 -0
- package/dist/presentation/index.d.ts.map +1 -0
- package/dist/presentation/index.js +5 -0
- package/dist/presentation/index.js.map +1 -0
- package/dist/presentation/routes/create-manual-registration-token.d.ts +2 -0
- package/dist/presentation/routes/create-manual-registration-token.d.ts.map +1 -0
- package/dist/presentation/routes/create-manual-registration-token.js +44 -0
- package/dist/presentation/routes/create-manual-registration-token.js.map +1 -0
- package/dist/presentation/routes/create-provisioner-token.d.ts +2 -0
- package/dist/presentation/routes/create-provisioner-token.d.ts.map +1 -0
- package/dist/presentation/routes/create-provisioner-token.js +42 -0
- package/dist/presentation/routes/create-provisioner-token.js.map +1 -0
- package/dist/presentation/routes/get-provisioner-me.d.ts +2 -0
- package/dist/presentation/routes/get-provisioner-me.d.ts.map +1 -0
- package/dist/presentation/routes/get-provisioner-me.js +22 -0
- package/dist/presentation/routes/get-provisioner-me.js.map +1 -0
- package/dist/presentation/routes/heartbeat.d.ts +2 -0
- package/dist/presentation/routes/heartbeat.d.ts.map +1 -0
- package/dist/presentation/routes/heartbeat.js +64 -0
- package/dist/presentation/routes/heartbeat.js.map +1 -0
- package/dist/presentation/routes/index.d.ts +4 -0
- package/dist/presentation/routes/index.d.ts.map +1 -0
- package/dist/presentation/routes/index.js +97 -0
- package/dist/presentation/routes/index.js.map +1 -0
- package/dist/presentation/routes/list-active-provisioners.d.ts +2 -0
- package/dist/presentation/routes/list-active-provisioners.d.ts.map +1 -0
- package/dist/presentation/routes/list-active-provisioners.js +32 -0
- package/dist/presentation/routes/list-active-provisioners.js.map +1 -0
- package/dist/presentation/routes/list-active-runners.d.ts +2 -0
- package/dist/presentation/routes/list-active-runners.d.ts.map +1 -0
- package/dist/presentation/routes/list-active-runners.js +32 -0
- package/dist/presentation/routes/list-active-runners.js.map +1 -0
- package/dist/presentation/routes/list-manual-registration-tokens.d.ts +2 -0
- package/dist/presentation/routes/list-manual-registration-tokens.d.ts.map +1 -0
- package/dist/presentation/routes/list-manual-registration-tokens.js +32 -0
- package/dist/presentation/routes/list-manual-registration-tokens.js.map +1 -0
- package/dist/presentation/routes/list-provisioner-tokens.d.ts +2 -0
- package/dist/presentation/routes/list-provisioner-tokens.d.ts.map +1 -0
- package/dist/presentation/routes/list-provisioner-tokens.js +32 -0
- package/dist/presentation/routes/list-provisioner-tokens.js.map +1 -0
- package/dist/presentation/routes/mint-registration-tokens.d.ts +2 -0
- package/dist/presentation/routes/mint-registration-tokens.d.ts.map +1 -0
- package/dist/presentation/routes/mint-registration-tokens.js +74 -0
- package/dist/presentation/routes/mint-registration-tokens.js.map +1 -0
- package/dist/presentation/routes/poll-demand.d.ts +2 -0
- package/dist/presentation/routes/poll-demand.d.ts.map +1 -0
- package/dist/presentation/routes/poll-demand.js +53 -0
- package/dist/presentation/routes/poll-demand.js.map +1 -0
- package/dist/presentation/routes/rate-limit.d.ts +4 -0
- package/dist/presentation/routes/rate-limit.d.ts.map +1 -0
- package/dist/presentation/routes/rate-limit.js +94 -0
- package/dist/presentation/routes/rate-limit.js.map +1 -0
- package/dist/presentation/routes/reconcile-provisioned-runners.d.ts +2 -0
- package/dist/presentation/routes/reconcile-provisioned-runners.d.ts.map +1 -0
- package/dist/presentation/routes/reconcile-provisioned-runners.js +27 -0
- package/dist/presentation/routes/reconcile-provisioned-runners.js.map +1 -0
- package/dist/presentation/routes/register.d.ts +2 -0
- package/dist/presentation/routes/register.d.ts.map +1 -0
- package/dist/presentation/routes/register.js +63 -0
- package/dist/presentation/routes/register.js.map +1 -0
- package/dist/presentation/routes/report-provisioned-runners.d.ts +2 -0
- package/dist/presentation/routes/report-provisioned-runners.d.ts.map +1 -0
- package/dist/presentation/routes/report-provisioned-runners.js +37 -0
- package/dist/presentation/routes/report-provisioned-runners.js.map +1 -0
- package/dist/presentation/routes/request-job.d.ts +2 -0
- package/dist/presentation/routes/request-job.d.ts.map +1 -0
- package/dist/presentation/routes/request-job.js +45 -0
- package/dist/presentation/routes/request-job.js.map +1 -0
- package/dist/presentation/routes/revoke-manual-registration-token.d.ts +2 -0
- package/dist/presentation/routes/revoke-manual-registration-token.d.ts.map +1 -0
- package/dist/presentation/routes/revoke-manual-registration-token.js +42 -0
- package/dist/presentation/routes/revoke-manual-registration-token.js.map +1 -0
- package/dist/presentation/routes/revoke-provisioner-token.d.ts +2 -0
- package/dist/presentation/routes/revoke-provisioner-token.d.ts.map +1 -0
- package/dist/presentation/routes/revoke-provisioner-token.js +44 -0
- package/dist/presentation/routes/revoke-provisioner-token.js.map +1 -0
- package/dist/presentation/routes/workspace-membership.d.ts +6 -0
- package/dist/presentation/routes/workspace-membership.d.ts.map +1 -0
- package/dist/presentation/routes/workspace-membership.js +6 -0
- package/dist/presentation/routes/workspace-membership.js.map +1 -0
- package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.d.ts +3 -0
- package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.js +14 -0
- package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.js.map +1 -0
- package/dist/temporal/activities/index.d.ts +9 -0
- package/dist/temporal/activities/index.d.ts.map +1 -0
- package/dist/temporal/activities/index.js +12 -0
- package/dist/temporal/activities/index.js.map +1 -0
- package/dist/temporal/activities/maintenance-activities.d.ts +31 -0
- package/dist/temporal/activities/maintenance-activities.d.ts.map +1 -0
- package/dist/temporal/activities/maintenance-activities.js +18 -0
- package/dist/temporal/activities/maintenance-activities.js.map +1 -0
- package/dist/temporal/constants.d.ts +2 -0
- package/dist/temporal/constants.d.ts.map +1 -0
- package/dist/temporal/constants.js +3 -0
- package/dist/temporal/constants.js.map +1 -0
- package/dist/temporal/workflows/index.d.ts +2 -0
- package/dist/temporal/workflows/index.d.ts.map +1 -0
- package/dist/temporal/workflows/index.js +3 -0
- package/dist/temporal/workflows/index.js.map +1 -0
- package/dist/temporal/workflows/stuck-job-detector.d.ts +2 -0
- package/dist/temporal/workflows/stuck-job-detector.d.ts.map +1 -0
- package/dist/temporal/workflows/stuck-job-detector.js +61 -0
- package/dist/temporal/workflows/stuck-job-detector.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/drizzle/0000_initial.sql +185 -0
- package/drizzle/meta/0000_snapshot.json +1582 -0
- package/drizzle/meta/_journal.json +13 -0
- package/drizzle.config.ts +7 -0
- package/package.json +82 -0
- package/src/config.test.ts +374 -0
- package/src/config.ts +330 -0
- package/src/core/demand.test.ts +301 -0
- package/src/core/demand.ts +239 -0
- package/src/core/entities/ephemeral-registration-token.ts +13 -0
- package/src/core/entities/manual-registration-token.ts +11 -0
- package/src/core/entities/provisioned-runner.ts +30 -0
- package/src/core/entities/provisioner-token.ts +16 -0
- package/src/core/entities/reservation.ts +9 -0
- package/src/core/entities/runner-session.ts +18 -0
- package/src/core/ephemeral-registration-tokens.test.ts +108 -0
- package/src/core/ephemeral-registration-tokens.ts +112 -0
- package/src/core/errors.ts +128 -0
- package/src/core/index.ts +56 -0
- package/src/core/job-executions.ts +47 -0
- package/src/core/maintenance-policy.ts +1 -0
- package/src/core/maintenance.test.ts +251 -0
- package/src/core/maintenance.ts +74 -0
- package/src/core/manual-registration-tokens.test.ts +44 -0
- package/src/core/manual-registration-tokens.ts +51 -0
- package/src/core/provisioned-runners.test.ts +101 -0
- package/src/core/provisioned-runners.ts +301 -0
- package/src/core/provisioner-tokens.test.ts +61 -0
- package/src/core/provisioner-tokens.ts +61 -0
- package/src/core/rate-limit.test.ts +158 -0
- package/src/core/rate-limit.ts +121 -0
- package/src/core/runner-sessions.test.ts +209 -0
- package/src/core/runner-sessions.ts +66 -0
- package/src/core/runner-tool-capabilities.test.ts +141 -0
- package/src/core/runner-tool-capabilities.ts +75 -0
- package/src/db/db.ts +38 -0
- package/src/db/ephemeral-registration-tokens.test.ts +332 -0
- package/src/db/ephemeral-registration-tokens.ts +348 -0
- package/src/db/index.ts +90 -0
- package/src/db/job-executions.test.ts +1500 -0
- package/src/db/job-executions.ts +647 -0
- package/src/db/manual-registration-tokens.test.ts +99 -0
- package/src/db/manual-registration-tokens.ts +87 -0
- package/src/db/provisioned-runners.test.ts +1812 -0
- package/src/db/provisioned-runners.ts +886 -0
- package/src/db/provisioner-tokens.test.ts +133 -0
- package/src/db/provisioner-tokens.ts +142 -0
- package/src/db/rate-limits.test.ts +74 -0
- package/src/db/rate-limits.ts +69 -0
- package/src/db/reservations.test.ts +399 -0
- package/src/db/reservations.ts +296 -0
- package/src/db/runner-sessions.test.ts +153 -0
- package/src/db/runner-sessions.ts +97 -0
- package/src/db/schema/common.ts +3 -0
- package/src/db/schema/ephemeral-registration-tokens.ts +58 -0
- package/src/db/schema/manual-registration-tokens.ts +40 -0
- package/src/db/schema/outbox.ts +4 -0
- package/src/db/schema/pending-job-executions.ts +25 -0
- package/src/db/schema/provisioned-runners.ts +89 -0
- package/src/db/schema/provisioner-tokens.ts +50 -0
- package/src/db/schema/rate-limits.ts +27 -0
- package/src/db/schema/reservations.ts +38 -0
- package/src/db/schema/runner-sessions.ts +77 -0
- package/src/db/schema/running-job-executions.ts +50 -0
- package/src/index.ts +63 -0
- package/src/metrics/index.ts +13 -0
- package/src/metrics/instance.ts +115 -0
- package/src/metrics/service.ts +22 -0
- package/src/presentation/auth/index.ts +6 -0
- package/src/presentation/auth/provisioner-token-auth.ts +76 -0
- package/src/presentation/auth/runner-registration-token-auth.ts +105 -0
- package/src/presentation/dto/active-provisioner.ts +15 -0
- package/src/presentation/dto/index.ts +13 -0
- package/src/presentation/dto/manual-registration-token.ts +23 -0
- package/src/presentation/dto/mint-registration-tokens.ts +35 -0
- package/src/presentation/dto/poll-demand.ts +20 -0
- package/src/presentation/dto/provisioned-runners.ts +88 -0
- package/src/presentation/dto/provisioner-token.ts +29 -0
- package/src/presentation/index.ts +6 -0
- package/src/presentation/routes/create-manual-registration-token.ts +43 -0
- package/src/presentation/routes/create-provisioner-token.ts +41 -0
- package/src/presentation/routes/get-provisioner-me.ts +22 -0
- package/src/presentation/routes/heartbeat.test.ts +325 -0
- package/src/presentation/routes/heartbeat.ts +80 -0
- package/src/presentation/routes/index.ts +86 -0
- package/src/presentation/routes/list-active-provisioners.ts +25 -0
- package/src/presentation/routes/list-active-runners.test.ts +366 -0
- package/src/presentation/routes/list-active-runners.ts +25 -0
- package/src/presentation/routes/list-manual-registration-tokens.ts +25 -0
- package/src/presentation/routes/list-provisioner-tokens.ts +25 -0
- package/src/presentation/routes/manual-registration-tokens.test.ts +213 -0
- package/src/presentation/routes/mint-registration-tokens.test.ts +453 -0
- package/src/presentation/routes/mint-registration-tokens.ts +82 -0
- package/src/presentation/routes/poll-demand-release.test.ts +80 -0
- package/src/presentation/routes/poll-demand.test.ts +323 -0
- package/src/presentation/routes/poll-demand.ts +56 -0
- package/src/presentation/routes/provisioner-me.test.ts +172 -0
- package/src/presentation/routes/provisioner-tokens.test.ts +219 -0
- package/src/presentation/routes/rate-limit.ts +115 -0
- package/src/presentation/routes/reconcile-provisioned-runners.test.ts +380 -0
- package/src/presentation/routes/reconcile-provisioned-runners.ts +30 -0
- package/src/presentation/routes/register.test.ts +453 -0
- package/src/presentation/routes/register.ts +75 -0
- package/src/presentation/routes/report-provisioned-runners.test.ts +249 -0
- package/src/presentation/routes/report-provisioned-runners.ts +40 -0
- package/src/presentation/routes/request-job.test.ts +222 -0
- package/src/presentation/routes/request-job.ts +47 -0
- package/src/presentation/routes/revoke-manual-registration-token.ts +35 -0
- package/src/presentation/routes/revoke-provisioner-token.ts +37 -0
- package/src/presentation/routes/workspace-membership.ts +9 -0
- package/src/presentation/subscribers/on-workflows-job-execution-timed-out.test.ts +147 -0
- package/src/presentation/subscribers/on-workflows-job-execution-timed-out.ts +17 -0
- package/src/temporal/activities/index.ts +17 -0
- package/src/temporal/activities/maintenance-activities.test.ts +104 -0
- package/src/temporal/activities/maintenance-activities.ts +41 -0
- package/src/temporal/constants.ts +1 -0
- package/src/temporal/workflows/index.ts +1 -0
- package/src/temporal/workflows/stuck-job-detector.test.ts +93 -0
- package/src/temporal/workflows/stuck-job-detector.ts +67 -0
- package/test/env.ts +11 -0
- package/test/factories/ephemeral-registration-token.ts +41 -0
- package/test/factories/manual-registration-token.ts +37 -0
- package/test/factories/pending-job.ts +44 -0
- package/test/factories/provisioned-runner.ts +78 -0
- package/test/factories/provisioner-token.ts +41 -0
- package/test/factories/reservation.ts +33 -0
- package/test/factories/runner-session.ts +32 -0
- package/test/globalSetup.ts +23 -0
- package/test/index.ts +16 -0
- package/test/setup.ts +17 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +8 -0
- package/vitest.config.ts +14 -0
package/src/config.ts
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import {REGISTRATION_TOKEN_BATCH_HARD_MAX} from '@shipfox/api-runners-dto';
|
|
2
|
+
import {bool, createConfig, num, str} from '@shipfox/config';
|
|
3
|
+
import {STUCK_JOB_THRESHOLD_SECONDS} from '#core/maintenance-policy.js';
|
|
4
|
+
|
|
5
|
+
const EPHEMERAL_REGISTRATION_TOKEN_TTL_HARD_MAX_SECONDS = 3600;
|
|
6
|
+
|
|
7
|
+
export const config = createConfig({
|
|
8
|
+
EPHEMERAL_REGISTRATION_TOKEN_TTL_SECONDS: num({
|
|
9
|
+
desc: `Lifetime of a provisioner-minted runner registration token, in seconds. The token can be exchanged once by a runner before this time passes. Set this between 1 and ${EPHEMERAL_REGISTRATION_TOKEN_TTL_HARD_MAX_SECONDS}.`,
|
|
10
|
+
default: 300,
|
|
11
|
+
}),
|
|
12
|
+
REGISTRATION_TOKEN_BATCH_MAX: num({
|
|
13
|
+
desc: `Maximum number of runner registration tokens a provisioner can mint in one batch request. Set this between 1 and ${REGISTRATION_TOKEN_BATCH_HARD_MAX}.`,
|
|
14
|
+
default: 500,
|
|
15
|
+
}),
|
|
16
|
+
PROVISIONER_MINT_RATE_LIMIT_MAX_REQUESTS: num({
|
|
17
|
+
desc: 'Maximum number of batch runner registration token mint requests one provisioner can make per rate-limit window.',
|
|
18
|
+
default: 120,
|
|
19
|
+
}),
|
|
20
|
+
PROVISIONER_MINT_RATE_LIMIT_WINDOW_SECONDS: num({
|
|
21
|
+
desc: 'Rate-limit window for provisioner batch runner registration token mint requests, in seconds.',
|
|
22
|
+
default: 60,
|
|
23
|
+
}),
|
|
24
|
+
EPHEMERAL_REGISTER_RATE_LIMIT_MAX_REQUESTS: num({
|
|
25
|
+
desc: 'Maximum number of runner registration attempts one ephemeral registration token can make per rate-limit window.',
|
|
26
|
+
default: 5,
|
|
27
|
+
}),
|
|
28
|
+
EPHEMERAL_REGISTER_RATE_LIMIT_WINDOW_SECONDS: num({
|
|
29
|
+
desc: 'Rate-limit window for runner registration attempts with one ephemeral registration token, in seconds.',
|
|
30
|
+
default: 60,
|
|
31
|
+
}),
|
|
32
|
+
RUNNERS_RATE_LIMIT_TIMEOUT_MS: num({
|
|
33
|
+
desc: 'Maximum time, in milliseconds, a runners rate-limit storage check may wait before the request fails closed.',
|
|
34
|
+
default: 250,
|
|
35
|
+
}),
|
|
36
|
+
RATE_LIMIT_IDENTIFIER_SECRET: str({
|
|
37
|
+
desc: 'Optional secret used to HMAC identifiers before storing rate-limit counters. Leave it unset to derive a stable key from AUTH_JWT_SECRET.',
|
|
38
|
+
default: undefined,
|
|
39
|
+
}),
|
|
40
|
+
RESERVATION_TTL_SECONDS: num({
|
|
41
|
+
desc: 'Lifetime of a count-based runner reservation, in seconds. Expired reservations stop counting against queued demand.',
|
|
42
|
+
default: 60,
|
|
43
|
+
}),
|
|
44
|
+
RESERVATION_LONG_POLL_MAX_WAIT_SECONDS: num({
|
|
45
|
+
desc: 'Maximum time the provisioner demand poll endpoint waits for reservable demand before returning, in seconds.',
|
|
46
|
+
default: 30,
|
|
47
|
+
}),
|
|
48
|
+
RESERVATION_POLL_INTERVAL_MS: num({
|
|
49
|
+
desc: 'Initial interval between demand re-checks while a provisioner request is waiting, in milliseconds.',
|
|
50
|
+
default: 1000,
|
|
51
|
+
}),
|
|
52
|
+
RESERVATION_POLL_MAX_INTERVAL_MS: num({
|
|
53
|
+
desc: 'Maximum backoff interval between demand re-checks while a provisioner request is waiting, in milliseconds.',
|
|
54
|
+
default: 5000,
|
|
55
|
+
}),
|
|
56
|
+
RUNNER_ACTIVE_WINDOW_SECONDS: num({
|
|
57
|
+
desc: 'Time window, in seconds, used to list active runners from recent heartbeats and provisioned runner reports.',
|
|
58
|
+
default: 60,
|
|
59
|
+
}),
|
|
60
|
+
RUNNER_TOOL_CAPABILITIES_STALE_AFTER_SECONDS: num({
|
|
61
|
+
desc: 'Time window, in seconds, after which a runner tool capability report is treated as stale. Set this higher than the runner heartbeat interval so active runners keep their advertised tools fresh.',
|
|
62
|
+
default: 60,
|
|
63
|
+
}),
|
|
64
|
+
RUNNER_NO_FIRST_HEARTBEAT_GRACE_SECONDS: num({
|
|
65
|
+
desc: 'Grace window, in seconds, before maintenance expires a claimed job that has not sent its first heartbeat. Set this lower than the normal stuck-job threshold so startup crashes release work quickly.',
|
|
66
|
+
default: 60,
|
|
67
|
+
}),
|
|
68
|
+
RUNNER_RECONCILE_TERMINATE_GRACE_SECONDS: num({
|
|
69
|
+
desc: 'Grace window, in seconds, before reconcile marks an absent provisioned runner as terminated. Set this higher than the provisioner report interval so a transient empty or partial observed set does not kill a live runner.',
|
|
70
|
+
default: 120,
|
|
71
|
+
}),
|
|
72
|
+
RUNNER_STALE_PROVISIONED_RUNNER_THRESHOLD_SECONDS: num({
|
|
73
|
+
desc: 'Time, in seconds, after which a provisioned runner with no recent report, no live provisioner, no live runner session, and no running job is marked failed by backend maintenance.',
|
|
74
|
+
default: 300,
|
|
75
|
+
}),
|
|
76
|
+
RUNNER_STALE_PROVISIONED_RUNNER_REAPER_LIMIT: num({
|
|
77
|
+
desc: 'Maximum number of stale provisioned runners the maintenance worker marks failed in one run. Higher values clear backlogs faster but hold database locks longer.',
|
|
78
|
+
default: 100,
|
|
79
|
+
}),
|
|
80
|
+
RUNNER_SESSION_LIVENESS_THROTTLE_SECONDS: num({
|
|
81
|
+
desc: 'Minimum time, in seconds, between runner session liveness writes from job request polls. Set this lower than RUNNER_STALE_PROVISIONED_RUNNER_THRESHOLD_SECONDS so active idle runners stay fresh.',
|
|
82
|
+
default: 10,
|
|
83
|
+
}),
|
|
84
|
+
RUNNER_SESSION_MANUAL_RETENTION_DAYS: num({
|
|
85
|
+
desc: 'How long manual runner sessions are retained before maintenance deletes them, in days. Set this longer than AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN so a valid session token never outlives its row.',
|
|
86
|
+
default: 30,
|
|
87
|
+
}),
|
|
88
|
+
RUNNER_SESSION_EPHEMERAL_RETENTION_DAYS: num({
|
|
89
|
+
desc: 'How long ephemeral runner sessions are retained before maintenance deletes them, in days. Set this longer than AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN so a valid session token never outlives its row.',
|
|
90
|
+
default: 7,
|
|
91
|
+
}),
|
|
92
|
+
RUNNER_SESSION_GC_BATCH_SIZE: num({
|
|
93
|
+
desc: 'Maximum number of expired runner sessions maintenance deletes in one pass.',
|
|
94
|
+
default: 1000,
|
|
95
|
+
}),
|
|
96
|
+
RUNNER_EPHEMERAL_TOKEN_RETENTION_DAYS: num({
|
|
97
|
+
desc: 'How long consumed or expired ephemeral registration tokens are retained for audit and debugging before maintenance deletes them, in days. Active tokens that are neither consumed nor expired are never deleted. This window is independent of the runner-session retention windows.',
|
|
98
|
+
default: 7,
|
|
99
|
+
}),
|
|
100
|
+
RUNNER_EPHEMERAL_TOKEN_GC_BATCH_SIZE: num({
|
|
101
|
+
desc: 'Maximum number of consumed or expired ephemeral registration tokens maintenance deletes in one pass.',
|
|
102
|
+
default: 1000,
|
|
103
|
+
}),
|
|
104
|
+
PROVISIONER_ACTIVE_WINDOW_SECONDS: num({
|
|
105
|
+
desc: 'Time window, in seconds, used to list active provisioners from recent authenticated requests.',
|
|
106
|
+
default: 120,
|
|
107
|
+
}),
|
|
108
|
+
PROVISIONER_LAST_SEEN_THROTTLE_SECONDS: num({
|
|
109
|
+
desc: 'Minimum time, in seconds, between last-seen writes for one provisioner token.',
|
|
110
|
+
default: 10,
|
|
111
|
+
}),
|
|
112
|
+
PROVISIONED_RUNNER_COUNT_DIVERGENCE_TEMPLATE_KEY_LABEL_ENABLED: bool({
|
|
113
|
+
desc: 'Whether runners_provisioned_runner_count_divergence includes template_key as a metric label. Use true or false. Defaults to false because template keys can create high-cardinality metric series. Set true only when template keys are bounded and stable.',
|
|
114
|
+
default: false,
|
|
115
|
+
}),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (
|
|
119
|
+
!Number.isInteger(config.EPHEMERAL_REGISTRATION_TOKEN_TTL_SECONDS) ||
|
|
120
|
+
config.EPHEMERAL_REGISTRATION_TOKEN_TTL_SECONDS < 1 ||
|
|
121
|
+
config.EPHEMERAL_REGISTRATION_TOKEN_TTL_SECONDS >
|
|
122
|
+
EPHEMERAL_REGISTRATION_TOKEN_TTL_HARD_MAX_SECONDS
|
|
123
|
+
) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`EPHEMERAL_REGISTRATION_TOKEN_TTL_SECONDS (${config.EPHEMERAL_REGISTRATION_TOKEN_TTL_SECONDS}) must be a whole number of seconds between 1 and ${EPHEMERAL_REGISTRATION_TOKEN_TTL_HARD_MAX_SECONDS}.`,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (
|
|
130
|
+
!Number.isInteger(config.REGISTRATION_TOKEN_BATCH_MAX) ||
|
|
131
|
+
config.REGISTRATION_TOKEN_BATCH_MAX < 1 ||
|
|
132
|
+
config.REGISTRATION_TOKEN_BATCH_MAX > REGISTRATION_TOKEN_BATCH_HARD_MAX
|
|
133
|
+
) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
`REGISTRATION_TOKEN_BATCH_MAX (${config.REGISTRATION_TOKEN_BATCH_MAX}) must be a whole number between 1 and ${REGISTRATION_TOKEN_BATCH_HARD_MAX}.`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (const [name, value] of [
|
|
140
|
+
['PROVISIONER_MINT_RATE_LIMIT_MAX_REQUESTS', config.PROVISIONER_MINT_RATE_LIMIT_MAX_REQUESTS],
|
|
141
|
+
['PROVISIONER_MINT_RATE_LIMIT_WINDOW_SECONDS', config.PROVISIONER_MINT_RATE_LIMIT_WINDOW_SECONDS],
|
|
142
|
+
['EPHEMERAL_REGISTER_RATE_LIMIT_MAX_REQUESTS', config.EPHEMERAL_REGISTER_RATE_LIMIT_MAX_REQUESTS],
|
|
143
|
+
[
|
|
144
|
+
'EPHEMERAL_REGISTER_RATE_LIMIT_WINDOW_SECONDS',
|
|
145
|
+
config.EPHEMERAL_REGISTER_RATE_LIMIT_WINDOW_SECONDS,
|
|
146
|
+
],
|
|
147
|
+
['RUNNERS_RATE_LIMIT_TIMEOUT_MS', config.RUNNERS_RATE_LIMIT_TIMEOUT_MS],
|
|
148
|
+
] as const) {
|
|
149
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
150
|
+
throw new Error(`${name} (${value}) must be a whole number >= 1.`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!Number.isInteger(config.RESERVATION_TTL_SECONDS) || config.RESERVATION_TTL_SECONDS < 1) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
`RESERVATION_TTL_SECONDS (${config.RESERVATION_TTL_SECONDS}) must be a whole number of seconds >= 1.`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (
|
|
161
|
+
!Number.isInteger(config.RESERVATION_LONG_POLL_MAX_WAIT_SECONDS) ||
|
|
162
|
+
config.RESERVATION_LONG_POLL_MAX_WAIT_SECONDS < 0
|
|
163
|
+
) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`RESERVATION_LONG_POLL_MAX_WAIT_SECONDS (${config.RESERVATION_LONG_POLL_MAX_WAIT_SECONDS}) must be a whole number of seconds >= 0.`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (
|
|
170
|
+
!Number.isInteger(config.RESERVATION_POLL_INTERVAL_MS) ||
|
|
171
|
+
config.RESERVATION_POLL_INTERVAL_MS < 1
|
|
172
|
+
) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
`RESERVATION_POLL_INTERVAL_MS (${config.RESERVATION_POLL_INTERVAL_MS}) must be a whole number of milliseconds >= 1.`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (
|
|
179
|
+
!Number.isInteger(config.RESERVATION_POLL_MAX_INTERVAL_MS) ||
|
|
180
|
+
config.RESERVATION_POLL_MAX_INTERVAL_MS < config.RESERVATION_POLL_INTERVAL_MS
|
|
181
|
+
) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`RESERVATION_POLL_MAX_INTERVAL_MS (${config.RESERVATION_POLL_MAX_INTERVAL_MS}) must be a whole number of milliseconds >= RESERVATION_POLL_INTERVAL_MS (${config.RESERVATION_POLL_INTERVAL_MS}).`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (
|
|
188
|
+
!Number.isInteger(config.RUNNER_ACTIVE_WINDOW_SECONDS) ||
|
|
189
|
+
config.RUNNER_ACTIVE_WINDOW_SECONDS < 1
|
|
190
|
+
) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
`RUNNER_ACTIVE_WINDOW_SECONDS (${config.RUNNER_ACTIVE_WINDOW_SECONDS}) must be a whole number of seconds >= 1.`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (
|
|
197
|
+
!Number.isInteger(config.RUNNER_TOOL_CAPABILITIES_STALE_AFTER_SECONDS) ||
|
|
198
|
+
config.RUNNER_TOOL_CAPABILITIES_STALE_AFTER_SECONDS < 1
|
|
199
|
+
) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
`RUNNER_TOOL_CAPABILITIES_STALE_AFTER_SECONDS (${config.RUNNER_TOOL_CAPABILITIES_STALE_AFTER_SECONDS}) must be a whole number of seconds >= 1.`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (
|
|
206
|
+
!Number.isInteger(config.RUNNER_NO_FIRST_HEARTBEAT_GRACE_SECONDS) ||
|
|
207
|
+
config.RUNNER_NO_FIRST_HEARTBEAT_GRACE_SECONDS < 1 ||
|
|
208
|
+
config.RUNNER_NO_FIRST_HEARTBEAT_GRACE_SECONDS >= STUCK_JOB_THRESHOLD_SECONDS
|
|
209
|
+
) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
`RUNNER_NO_FIRST_HEARTBEAT_GRACE_SECONDS (${config.RUNNER_NO_FIRST_HEARTBEAT_GRACE_SECONDS}) must be a whole number of seconds >= 1 and < ${STUCK_JOB_THRESHOLD_SECONDS}.`,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (
|
|
216
|
+
!Number.isInteger(config.RUNNER_RECONCILE_TERMINATE_GRACE_SECONDS) ||
|
|
217
|
+
config.RUNNER_RECONCILE_TERMINATE_GRACE_SECONDS < 1
|
|
218
|
+
) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`RUNNER_RECONCILE_TERMINATE_GRACE_SECONDS (${config.RUNNER_RECONCILE_TERMINATE_GRACE_SECONDS}) must be a whole number of seconds >= 1.`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (
|
|
225
|
+
!Number.isInteger(config.RUNNER_STALE_PROVISIONED_RUNNER_THRESHOLD_SECONDS) ||
|
|
226
|
+
config.RUNNER_STALE_PROVISIONED_RUNNER_THRESHOLD_SECONDS < 1
|
|
227
|
+
) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
`RUNNER_STALE_PROVISIONED_RUNNER_THRESHOLD_SECONDS (${config.RUNNER_STALE_PROVISIONED_RUNNER_THRESHOLD_SECONDS}) must be a whole number of seconds >= 1.`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (
|
|
234
|
+
!Number.isInteger(config.RUNNER_STALE_PROVISIONED_RUNNER_REAPER_LIMIT) ||
|
|
235
|
+
config.RUNNER_STALE_PROVISIONED_RUNNER_REAPER_LIMIT < 1
|
|
236
|
+
) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
`RUNNER_STALE_PROVISIONED_RUNNER_REAPER_LIMIT (${config.RUNNER_STALE_PROVISIONED_RUNNER_REAPER_LIMIT}) must be a whole number >= 1.`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (
|
|
243
|
+
!Number.isInteger(config.RUNNER_SESSION_LIVENESS_THROTTLE_SECONDS) ||
|
|
244
|
+
config.RUNNER_SESSION_LIVENESS_THROTTLE_SECONDS < 1
|
|
245
|
+
) {
|
|
246
|
+
throw new Error(
|
|
247
|
+
`RUNNER_SESSION_LIVENESS_THROTTLE_SECONDS (${config.RUNNER_SESSION_LIVENESS_THROTTLE_SECONDS}) must be a whole number of seconds >= 1.`,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (
|
|
252
|
+
!Number.isInteger(config.RUNNER_SESSION_MANUAL_RETENTION_DAYS) ||
|
|
253
|
+
config.RUNNER_SESSION_MANUAL_RETENTION_DAYS < 1
|
|
254
|
+
) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
`RUNNER_SESSION_MANUAL_RETENTION_DAYS (${config.RUNNER_SESSION_MANUAL_RETENTION_DAYS}) must be a whole number of days >= 1.`,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (
|
|
261
|
+
!Number.isInteger(config.RUNNER_SESSION_EPHEMERAL_RETENTION_DAYS) ||
|
|
262
|
+
config.RUNNER_SESSION_EPHEMERAL_RETENTION_DAYS < 1
|
|
263
|
+
) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
`RUNNER_SESSION_EPHEMERAL_RETENTION_DAYS (${config.RUNNER_SESSION_EPHEMERAL_RETENTION_DAYS}) must be a whole number of days >= 1.`,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (
|
|
270
|
+
!Number.isInteger(config.RUNNER_SESSION_GC_BATCH_SIZE) ||
|
|
271
|
+
config.RUNNER_SESSION_GC_BATCH_SIZE < 1
|
|
272
|
+
) {
|
|
273
|
+
throw new Error(
|
|
274
|
+
`RUNNER_SESSION_GC_BATCH_SIZE (${config.RUNNER_SESSION_GC_BATCH_SIZE}) must be a whole number >= 1.`,
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (
|
|
279
|
+
!Number.isInteger(config.RUNNER_EPHEMERAL_TOKEN_RETENTION_DAYS) ||
|
|
280
|
+
config.RUNNER_EPHEMERAL_TOKEN_RETENTION_DAYS < 1
|
|
281
|
+
) {
|
|
282
|
+
throw new Error(
|
|
283
|
+
`RUNNER_EPHEMERAL_TOKEN_RETENTION_DAYS (${config.RUNNER_EPHEMERAL_TOKEN_RETENTION_DAYS}) must be a whole number of days >= 1.`,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (
|
|
288
|
+
!Number.isInteger(config.RUNNER_EPHEMERAL_TOKEN_GC_BATCH_SIZE) ||
|
|
289
|
+
config.RUNNER_EPHEMERAL_TOKEN_GC_BATCH_SIZE < 1
|
|
290
|
+
) {
|
|
291
|
+
throw new Error(
|
|
292
|
+
`RUNNER_EPHEMERAL_TOKEN_GC_BATCH_SIZE (${config.RUNNER_EPHEMERAL_TOKEN_GC_BATCH_SIZE}) must be a whole number >= 1.`,
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (
|
|
297
|
+
!Number.isInteger(config.PROVISIONER_ACTIVE_WINDOW_SECONDS) ||
|
|
298
|
+
config.PROVISIONER_ACTIVE_WINDOW_SECONDS < 1
|
|
299
|
+
) {
|
|
300
|
+
throw new Error(
|
|
301
|
+
`PROVISIONER_ACTIVE_WINDOW_SECONDS (${config.PROVISIONER_ACTIVE_WINDOW_SECONDS}) must be a whole number of seconds >= 1.`,
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (
|
|
306
|
+
!Number.isInteger(config.PROVISIONER_LAST_SEEN_THROTTLE_SECONDS) ||
|
|
307
|
+
config.PROVISIONER_LAST_SEEN_THROTTLE_SECONDS < 1
|
|
308
|
+
) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
`PROVISIONER_LAST_SEEN_THROTTLE_SECONDS (${config.PROVISIONER_LAST_SEEN_THROTTLE_SECONDS}) must be a whole number of seconds >= 1.`,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (
|
|
315
|
+
config.RUNNER_STALE_PROVISIONED_RUNNER_THRESHOLD_SECONDS <=
|
|
316
|
+
config.PROVISIONER_LAST_SEEN_THROTTLE_SECONDS
|
|
317
|
+
) {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`RUNNER_STALE_PROVISIONED_RUNNER_THRESHOLD_SECONDS (${config.RUNNER_STALE_PROVISIONED_RUNNER_THRESHOLD_SECONDS}) must be greater than PROVISIONER_LAST_SEEN_THROTTLE_SECONDS (${config.PROVISIONER_LAST_SEEN_THROTTLE_SECONDS}).`,
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (
|
|
324
|
+
config.RUNNER_STALE_PROVISIONED_RUNNER_THRESHOLD_SECONDS <=
|
|
325
|
+
config.RUNNER_SESSION_LIVENESS_THROTTLE_SECONDS
|
|
326
|
+
) {
|
|
327
|
+
throw new Error(
|
|
328
|
+
`RUNNER_STALE_PROVISIONED_RUNNER_THRESHOLD_SECONDS (${config.RUNNER_STALE_PROVISIONED_RUNNER_THRESHOLD_SECONDS}) must be greater than RUNNER_SESSION_LIVENESS_THROTTLE_SECONDS (${config.RUNNER_SESSION_LIVENESS_THROTTLE_SECONDS}).`,
|
|
329
|
+
);
|
|
330
|
+
}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import {
|
|
2
|
+
calculateProvisionedRunnerCountDivergences,
|
|
3
|
+
nextBackoffInterval,
|
|
4
|
+
pollDemand,
|
|
5
|
+
shouldReturn,
|
|
6
|
+
} from '#core/demand.js';
|
|
7
|
+
|
|
8
|
+
describe('shouldReturn', () => {
|
|
9
|
+
const emptyResult = {stats: [], reservations: [], terminateProvisionedRunnerIds: []};
|
|
10
|
+
|
|
11
|
+
it('returns true for observe-only requests', () => {
|
|
12
|
+
const result = shouldReturn(emptyResult, 0, 1, false);
|
|
13
|
+
|
|
14
|
+
expect(result).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('returns true when no capacity is advertised', () => {
|
|
18
|
+
const result = shouldReturn(emptyResult, 1, 0, false);
|
|
19
|
+
|
|
20
|
+
expect(result).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('returns true when reservations were granted', () => {
|
|
24
|
+
const result = shouldReturn(
|
|
25
|
+
{
|
|
26
|
+
stats: [],
|
|
27
|
+
reservations: [
|
|
28
|
+
{reservationId: crypto.randomUUID(), labels: ['linux'], count: 1, expiresAt: new Date()},
|
|
29
|
+
],
|
|
30
|
+
terminateProvisionedRunnerIds: [],
|
|
31
|
+
},
|
|
32
|
+
1,
|
|
33
|
+
1,
|
|
34
|
+
false,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
expect(result).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('returns true when terminate intents exist', () => {
|
|
41
|
+
const result = shouldReturn(
|
|
42
|
+
{...emptyResult, terminateProvisionedRunnerIds: ['provisioned-runner-1']},
|
|
43
|
+
1,
|
|
44
|
+
1,
|
|
45
|
+
false,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(result).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('returns true when the deadline passed', () => {
|
|
52
|
+
const result = shouldReturn(emptyResult, 1, 1, true);
|
|
53
|
+
|
|
54
|
+
expect(result).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('returns false while demand is fully reserved and the deadline has not passed', () => {
|
|
58
|
+
const result = shouldReturn(emptyResult, 1, 1, false);
|
|
59
|
+
|
|
60
|
+
expect(result).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('pollDemand', () => {
|
|
65
|
+
it('returns immediately when wait seconds is zero', async () => {
|
|
66
|
+
const result = await pollDemand({
|
|
67
|
+
workspaceId: crypto.randomUUID(),
|
|
68
|
+
provisionerId: crypto.randomUUID(),
|
|
69
|
+
maxReservations: 1,
|
|
70
|
+
waitSeconds: 0,
|
|
71
|
+
ttlSeconds: 60,
|
|
72
|
+
terminateIntentLimit: 1000,
|
|
73
|
+
templates: [
|
|
74
|
+
{templateKey: 'linux', labels: ['linux'], availableSlots: 1, starting: 0, running: 0},
|
|
75
|
+
],
|
|
76
|
+
signal: new AbortController().signal,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
expect(result).toEqual({stats: [], reservations: [], terminateProvisionedRunnerIds: []});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('returns immediately when terminate intents exist without reservation demand', async () => {
|
|
83
|
+
vi.resetModules();
|
|
84
|
+
const pollDemandAndReserveTx = vi.fn().mockResolvedValue({stats: [], reservations: []});
|
|
85
|
+
const listProvisionerTerminateIntentRowsTx = vi
|
|
86
|
+
.fn()
|
|
87
|
+
.mockResolvedValue([{provisionedRunnerId: 'provisioned-runner-1', reason: 'job-cancelled'}]);
|
|
88
|
+
const listActiveProvisionedRunnerCountsByTemplateTx = vi.fn().mockResolvedValue([]);
|
|
89
|
+
vi.doMock('#db/db.js', () => ({
|
|
90
|
+
db: () => ({transaction: (callback: (tx: unknown) => Promise<unknown>) => callback({})}),
|
|
91
|
+
}));
|
|
92
|
+
vi.doMock('#db/reservations.js', () => ({
|
|
93
|
+
deleteReservationsByIds: vi.fn(),
|
|
94
|
+
pollDemandAndReserveTx,
|
|
95
|
+
}));
|
|
96
|
+
vi.doMock('#db/provisioned-runners.js', () => ({
|
|
97
|
+
listActiveProvisionedRunnerCountsByTemplateTx,
|
|
98
|
+
listProvisionerTerminateIntentRowsTx,
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const {pollDemand: mockedPollDemand} = await import('#core/demand.js');
|
|
103
|
+
|
|
104
|
+
const result = await mockedPollDemand({
|
|
105
|
+
workspaceId: crypto.randomUUID(),
|
|
106
|
+
provisionerId: crypto.randomUUID(),
|
|
107
|
+
maxReservations: 1,
|
|
108
|
+
waitSeconds: 60,
|
|
109
|
+
ttlSeconds: 60,
|
|
110
|
+
terminateIntentLimit: 1000,
|
|
111
|
+
templates: [
|
|
112
|
+
{templateKey: 'linux', labels: ['linux'], availableSlots: 1, starting: 0, running: 0},
|
|
113
|
+
],
|
|
114
|
+
signal: new AbortController().signal,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(result).toEqual({
|
|
118
|
+
stats: [],
|
|
119
|
+
reservations: [],
|
|
120
|
+
terminateProvisionedRunnerIds: ['provisioned-runner-1'],
|
|
121
|
+
});
|
|
122
|
+
expect(pollDemandAndReserveTx).toHaveBeenCalledOnce();
|
|
123
|
+
expect(listProvisionerTerminateIntentRowsTx).toHaveBeenCalledOnce();
|
|
124
|
+
} finally {
|
|
125
|
+
vi.doUnmock('#db/db.js');
|
|
126
|
+
vi.doUnmock('#db/reservations.js');
|
|
127
|
+
vi.doUnmock('#db/provisioned-runners.js');
|
|
128
|
+
vi.resetModules();
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('does not calculate divergence for a non-returned long-poll retry', async () => {
|
|
133
|
+
vi.resetModules();
|
|
134
|
+
const abortController = new AbortController();
|
|
135
|
+
const pollDemandAndReserveTx = vi.fn().mockResolvedValue({stats: [], reservations: []});
|
|
136
|
+
const listProvisionerTerminateIntentRowsTx = vi.fn().mockImplementation(() => {
|
|
137
|
+
abortController.abort();
|
|
138
|
+
return [];
|
|
139
|
+
});
|
|
140
|
+
const listActiveProvisionedRunnerCountsByTemplateTx = vi.fn().mockResolvedValue([]);
|
|
141
|
+
vi.doMock('#db/db.js', () => ({
|
|
142
|
+
db: () => ({transaction: (callback: (tx: unknown) => Promise<unknown>) => callback({})}),
|
|
143
|
+
}));
|
|
144
|
+
vi.doMock('#db/reservations.js', () => ({
|
|
145
|
+
deleteReservationsByIds: vi.fn(),
|
|
146
|
+
pollDemandAndReserveTx,
|
|
147
|
+
}));
|
|
148
|
+
vi.doMock('#db/provisioned-runners.js', () => ({
|
|
149
|
+
listActiveProvisionedRunnerCountsByTemplateTx,
|
|
150
|
+
listProvisionerTerminateIntentRowsTx,
|
|
151
|
+
}));
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const {pollDemand: mockedPollDemand} = await import('#core/demand.js');
|
|
155
|
+
|
|
156
|
+
const result = await mockedPollDemand({
|
|
157
|
+
workspaceId: crypto.randomUUID(),
|
|
158
|
+
provisionerId: crypto.randomUUID(),
|
|
159
|
+
maxReservations: 1,
|
|
160
|
+
waitSeconds: 60,
|
|
161
|
+
ttlSeconds: 60,
|
|
162
|
+
terminateIntentLimit: 1000,
|
|
163
|
+
templates: [
|
|
164
|
+
{templateKey: 'linux', labels: ['linux'], availableSlots: 1, starting: 0, running: 0},
|
|
165
|
+
],
|
|
166
|
+
signal: abortController.signal,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(result).toEqual({stats: [], reservations: [], terminateProvisionedRunnerIds: []});
|
|
170
|
+
expect(listActiveProvisionedRunnerCountsByTemplateTx).not.toHaveBeenCalled();
|
|
171
|
+
} finally {
|
|
172
|
+
vi.doUnmock('#db/db.js');
|
|
173
|
+
vi.doUnmock('#db/reservations.js');
|
|
174
|
+
vi.doUnmock('#db/provisioned-runners.js');
|
|
175
|
+
vi.resetModules();
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('includes template_key on divergence metrics when the env flag is enabled', async () => {
|
|
180
|
+
vi.resetModules();
|
|
181
|
+
vi.stubEnv('PROVISIONED_RUNNER_COUNT_DIVERGENCE_TEMPLATE_KEY_LABEL_ENABLED', 'true');
|
|
182
|
+
const divergenceAdd = vi.fn();
|
|
183
|
+
const pollDemandAndReserveTx = vi.fn().mockResolvedValue({stats: [], reservations: []});
|
|
184
|
+
const listProvisionerTerminateIntentRowsTx = vi.fn().mockResolvedValue([]);
|
|
185
|
+
const listActiveProvisionedRunnerCountsByTemplateTx = vi
|
|
186
|
+
.fn()
|
|
187
|
+
.mockResolvedValue([{templateKey: 'linux', state: 'running', count: 2}]);
|
|
188
|
+
vi.doMock('#db/db.js', () => ({
|
|
189
|
+
db: () => ({transaction: (callback: (tx: unknown) => Promise<unknown>) => callback({})}),
|
|
190
|
+
}));
|
|
191
|
+
vi.doMock('#db/reservations.js', () => ({
|
|
192
|
+
deleteReservationsByIds: vi.fn(),
|
|
193
|
+
pollDemandAndReserveTx,
|
|
194
|
+
}));
|
|
195
|
+
vi.doMock('#db/provisioned-runners.js', () => ({
|
|
196
|
+
listActiveProvisionedRunnerCountsByTemplateTx,
|
|
197
|
+
listProvisionerTerminateIntentRowsTx,
|
|
198
|
+
}));
|
|
199
|
+
vi.doMock('#metrics/instance.js', () => ({
|
|
200
|
+
provisionedRunnerCountDivergenceCount: {add: divergenceAdd},
|
|
201
|
+
provisionedRunnerTerminateIntentIssuedCount: {add: vi.fn()},
|
|
202
|
+
}));
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const {pollDemand: mockedPollDemand} = await import('#core/demand.js');
|
|
206
|
+
|
|
207
|
+
const result = await mockedPollDemand({
|
|
208
|
+
workspaceId: crypto.randomUUID(),
|
|
209
|
+
provisionerId: crypto.randomUUID(),
|
|
210
|
+
maxReservations: 0,
|
|
211
|
+
waitSeconds: 0,
|
|
212
|
+
ttlSeconds: 60,
|
|
213
|
+
terminateIntentLimit: 1000,
|
|
214
|
+
templates: [
|
|
215
|
+
{templateKey: 'linux', labels: ['linux'], availableSlots: 1, starting: 0, running: 1},
|
|
216
|
+
],
|
|
217
|
+
signal: new AbortController().signal,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
expect(result).toEqual({stats: [], reservations: [], terminateProvisionedRunnerIds: []});
|
|
221
|
+
expect(divergenceAdd).toHaveBeenCalledWith(1, {
|
|
222
|
+
template_key: 'linux',
|
|
223
|
+
state: 'running',
|
|
224
|
+
direction: 'backend-higher',
|
|
225
|
+
});
|
|
226
|
+
} finally {
|
|
227
|
+
vi.unstubAllEnvs();
|
|
228
|
+
vi.doUnmock('#db/db.js');
|
|
229
|
+
vi.doUnmock('#db/reservations.js');
|
|
230
|
+
vi.doUnmock('#db/provisioned-runners.js');
|
|
231
|
+
vi.doUnmock('#metrics/instance.js');
|
|
232
|
+
vi.resetModules();
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe('calculateProvisionedRunnerCountDivergences', () => {
|
|
238
|
+
it('returns no divergences when advertised and backend counts match', () => {
|
|
239
|
+
const result = calculateProvisionedRunnerCountDivergences({
|
|
240
|
+
advertisedTemplates: [template('linux', 1, 2)],
|
|
241
|
+
backendCounts: [
|
|
242
|
+
{templateKey: 'linux', state: 'starting', count: 1},
|
|
243
|
+
{templateKey: 'linux', state: 'running', count: 2},
|
|
244
|
+
],
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
expect(result).toEqual([]);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('detects backend-higher and advertised-higher counts', () => {
|
|
251
|
+
const result = calculateProvisionedRunnerCountDivergences({
|
|
252
|
+
advertisedTemplates: [template('linux', 1, 5)],
|
|
253
|
+
backendCounts: [
|
|
254
|
+
{templateKey: 'linux', state: 'starting', count: 3},
|
|
255
|
+
{templateKey: 'linux', state: 'running', count: 2},
|
|
256
|
+
],
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
expect(result).toEqual([
|
|
260
|
+
{templateKey: 'linux', state: 'running', direction: 'advertised-higher', delta: 3},
|
|
261
|
+
{templateKey: 'linux', state: 'starting', direction: 'backend-higher', delta: 2},
|
|
262
|
+
]);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('aggregates duplicate advertised template keys before comparing', () => {
|
|
266
|
+
const result = calculateProvisionedRunnerCountDivergences({
|
|
267
|
+
advertisedTemplates: [template('linux', 1, 1), template('linux', 2, 3)],
|
|
268
|
+
backendCounts: [
|
|
269
|
+
{templateKey: 'linux', state: 'starting', count: 3},
|
|
270
|
+
{templateKey: 'linux', state: 'running', count: 4},
|
|
271
|
+
],
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
expect(result).toEqual([]);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('detects backend-only template keys as backend-higher', () => {
|
|
278
|
+
const result = calculateProvisionedRunnerCountDivergences({
|
|
279
|
+
advertisedTemplates: [template('linux', 0, 0)],
|
|
280
|
+
backendCounts: [{templateKey: 'gpu', state: 'running', count: 2}],
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
expect(result).toEqual([
|
|
284
|
+
{templateKey: 'gpu', state: 'running', direction: 'backend-higher', delta: 2},
|
|
285
|
+
]);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
function template(templateKey: string, starting: number, running: number) {
|
|
289
|
+
return {templateKey, labels: ['linux'], availableSlots: 1, starting, running};
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
describe('nextBackoffInterval', () => {
|
|
294
|
+
it('grows by 1.5x until the configured ceiling', () => {
|
|
295
|
+
const grown = nextBackoffInterval(1000);
|
|
296
|
+
const capped = nextBackoffInterval(5000);
|
|
297
|
+
|
|
298
|
+
expect(grown).toBe(1500);
|
|
299
|
+
expect(capped).toBe(5000);
|
|
300
|
+
});
|
|
301
|
+
});
|