@shipfox/api-runners 12.0.0 → 12.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +30 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +24 -2
- package/dist/config.js.map +1 -1
- package/dist/core/errors.d.ts +4 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +6 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/rate-limit.d.ts.map +1 -1
- package/dist/core/runner-control-sessions.d.ts.map +1 -1
- package/dist/core/runner-control-sessions.js +23 -9
- package/dist/core/runner-control-sessions.js.map +1 -1
- package/dist/core/runner-instances.d.ts +1 -0
- package/dist/core/runner-instances.d.ts.map +1 -1
- package/dist/core/runner-instances.js.map +1 -1
- package/dist/core/runner-labels.d.ts +11 -0
- package/dist/core/runner-labels.d.ts.map +1 -0
- package/dist/core/runner-labels.js +41 -0
- package/dist/core/runner-labels.js.map +1 -0
- package/dist/core/runner-sessions.d.ts.map +1 -1
- package/dist/core/runner-sessions.js +5 -1
- package/dist/core/runner-sessions.js.map +1 -1
- package/dist/db/db.d.ts +64 -64
- package/dist/db/ephemeral-registration-tokens.d.ts.map +1 -1
- package/dist/db/ephemeral-registration-tokens.js +12 -2
- package/dist/db/ephemeral-registration-tokens.js.map +1 -1
- package/dist/db/reservations.d.ts.map +1 -1
- package/dist/db/reservations.js +15 -5
- package/dist/db/reservations.js.map +1 -1
- package/dist/db/runner-instances.d.ts +1 -0
- package/dist/db/runner-instances.d.ts.map +1 -1
- package/dist/db/runner-instances.js +6 -4
- package/dist/db/runner-instances.js.map +1 -1
- package/dist/db/runner-sessions.d.ts.map +1 -1
- package/dist/db/runner-sessions.js +12 -1
- package/dist/db/runner-sessions.js.map +1 -1
- package/dist/db/schema/admin-command-results.d.ts +1 -1
- package/dist/db/schema/ephemeral-registration-tokens.d.ts +1 -1
- package/dist/db/schema/manual-registration-tokens.d.ts +1 -1
- package/dist/db/schema/outbox.d.ts +1 -1
- package/dist/db/schema/pending-job-executions.d.ts +3 -3
- package/dist/db/schema/provisioner-capability-snapshots.d.ts +3 -3
- package/dist/db/schema/provisioner-tokens.d.ts +2 -2
- package/dist/db/schema/rate-limits.d.ts +1 -1
- package/dist/db/schema/reservations.d.ts +3 -3
- package/dist/db/schema/runner-activation-tokens.d.ts +1 -1
- package/dist/db/schema/runner-control-sessions.d.ts +2 -2
- package/dist/db/schema/runner-instances.d.ts +4 -4
- package/dist/db/schema/runner-instances.d.ts.map +1 -1
- package/dist/db/schema/runner-instances.js +1 -0
- package/dist/db/schema/runner-instances.js.map +1 -1
- package/dist/db/schema/runner-sessions.d.ts +4 -4
- package/dist/db/schema/running-job-executions.d.ts +5 -5
- package/dist/metrics/index.d.ts +2 -1
- package/dist/metrics/index.d.ts.map +1 -1
- package/dist/metrics/index.js +1 -1
- package/dist/metrics/index.js.map +1 -1
- package/dist/metrics/instance.d.ts +13 -8
- package/dist/metrics/instance.d.ts.map +1 -1
- package/dist/metrics/instance.js +8 -0
- package/dist/metrics/instance.js.map +1 -1
- package/dist/presentation/dto/admin-provisioner-token.d.ts +1 -1
- package/dist/presentation/dto/admin-provisioner-token.d.ts.map +1 -1
- package/dist/presentation/dto/admin-runner-instances.d.ts +2 -2
- package/dist/presentation/dto/admin-runner-instances.d.ts.map +1 -1
- package/dist/presentation/routes/poll-demand.d.ts.map +1 -1
- package/dist/presentation/routes/poll-demand.js +12 -6
- package/dist/presentation/routes/poll-demand.js.map +1 -1
- package/dist/presentation/routes/rate-limit.d.ts.map +1 -1
- package/dist/presentation/routes/register.d.ts.map +1 -1
- package/dist/presentation/routes/register.js +9 -1
- package/dist/presentation/routes/register.js.map +1 -1
- package/dist/presentation/routes/report-runner-instances.d.ts.map +1 -1
- package/dist/presentation/routes/report-runner-instances.js +1 -0
- package/dist/presentation/routes/report-runner-instances.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0004_huge_skreet.sql +1 -0
- package/drizzle/meta/0004_snapshot.json +2194 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +9 -9
- package/src/config.test.ts +81 -0
- package/src/config.ts +39 -2
- package/src/core/errors.ts +9 -0
- package/src/core/index.ts +1 -0
- package/src/core/runner-control-sessions.test.ts +239 -0
- package/src/core/runner-control-sessions.ts +29 -10
- package/src/core/runner-instances.ts +1 -0
- package/src/core/runner-labels.test.ts +53 -0
- package/src/core/runner-labels.ts +51 -0
- package/src/core/runner-sessions.test.ts +34 -1
- package/src/core/runner-sessions.ts +8 -1
- package/src/db/ephemeral-registration-tokens.ts +15 -1
- package/src/db/reservations.test.ts +169 -0
- package/src/db/reservations.ts +42 -8
- package/src/db/runner-instances.test.ts +45 -1
- package/src/db/runner-instances.ts +7 -2
- package/src/db/runner-sessions.ts +14 -1
- package/src/db/schema/runner-instances.ts +3 -0
- package/src/metrics/index.ts +2 -0
- package/src/metrics/instance.ts +18 -0
- package/src/presentation/routes/poll-demand.test.ts +99 -3
- package/src/presentation/routes/poll-demand.ts +18 -6
- package/src/presentation/routes/register.test.ts +116 -2
- package/src/presentation/routes/register.ts +7 -0
- package/src/presentation/routes/report-runner-instances.test.ts +33 -0
- package/src/presentation/routes/report-runner-instances.ts +1 -0
- package/src/presentation/routes/runner-enrollment.test.ts +65 -5
- package/src/temporal/activities/maintenance-activities.test.ts +24 -31
- package/test/env.ts +1 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/runner-control-sessions.ts"],"sourcesContent":["import type {RunnerToolCapabilitiesDto} from '@shipfox/api-runners-dto';\nimport type {NodePgDatabase} from '@shipfox/node-drizzle';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {extractDisplayPrefix, generateOpaqueToken, hashOpaqueToken} from '@shipfox/node-tokens';\nimport {canonicalizeLabels} from '@shipfox/runner-labels';\nimport {and, eq, gt, isNull, notInArray, or, sql} from 'drizzle-orm';\nimport {config} from '#config.js';\nimport {\n ReservationExpiredError,\n ReservationNotFoundError,\n RunnerInstanceAlreadyAssignedError,\n RunnerInstanceNotAssignableError,\n} from '#core/errors.js';\nimport {db, type schema, type Tx} from '#db/db.js';\nimport {assignRunnerInstancesTx} from '#db/runner-assignments.js';\nimport {terminalStates} from '#db/runner-instances.js';\nimport {provisionerTokens} from '#db/schema/provisioner-tokens.js';\nimport {runnerBootstrapTokens, runnerControlSessions} from '#db/schema/runner-control-sessions.js';\nimport {providerRunners} from '#db/schema/runner-instances.js';\nimport {issueRunnerActivationTokenTx} from './runner-activation.js';\n\nexport class RunnerBootstrapTokenInvalidError extends Error {\n constructor() {\n super('Runner bootstrap token is invalid, expired, or has already been used');\n this.name = 'RunnerBootstrapTokenInvalidError';\n }\n}\n\nexport class RunnerControlSessionInvalidError extends Error {\n constructor() {\n super('Runner control session is invalid, expired, or closed');\n this.name = 'RunnerControlSessionInvalidError';\n }\n}\n\nexport async function createRunnerInstancesWithBootstrapTokens(params: {\n provisionerId: string;\n providerKind?: string | null;\n runnerInstances: Array<{\n templateKey?: string | null;\n reservationId?: string | null;\n }>;\n ttlSeconds: number;\n}): Promise<Array<{runnerInstanceId: string; bootstrapToken: string}>> {\n const expiresAt = new Date(Date.now() + params.ttlSeconds * 1000);\n return await db().transaction(async (tx) => {\n const instances = await tx\n .insert(providerRunners)\n .values(\n params.runnerInstances.map((runner) => ({\n provisionerId: params.provisionerId,\n providerKind: params.providerKind ?? null,\n templateKey: runner.templateKey ?? null,\n intendedReservationId: runner.reservationId ?? null,\n state: 'starting' as const,\n labels: [],\n reportedAt: new Date(),\n })),\n )\n .returning({id: providerRunners.id});\n const results = instances.map((instance) => ({\n runnerInstanceId: instance.id,\n bootstrapToken: generateOpaqueToken('runnerBootstrapToken'),\n }));\n await tx.insert(runnerBootstrapTokens).values(\n results.map((result) => ({\n runnerInstanceId: result.runnerInstanceId,\n provisionerId: params.provisionerId,\n hashedToken: hashOpaqueToken(result.bootstrapToken),\n prefix: extractDisplayPrefix(result.bootstrapToken),\n expiresAt,\n })),\n );\n return results;\n });\n}\n\nexport async function exchangeRunnerBootstrapToken(params: {\n rawToken: string;\n ttlSeconds: number;\n}): Promise<{runnerInstanceId: string; controlSessionToken: string; expiresAt: Date}> {\n const controlSessionToken = generateOpaqueToken('runnerControlSession');\n const expiresAt = new Date(Date.now() + params.ttlSeconds * 1000);\n return await db().transaction(async (tx) => {\n const [bootstrap] = await tx\n .update(runnerBootstrapTokens)\n .set({consumedAt: sql`now()`})\n .where(\n and(\n eq(runnerBootstrapTokens.hashedToken, hashOpaqueToken(params.rawToken)),\n isNull(runnerBootstrapTokens.consumedAt),\n isNull(runnerBootstrapTokens.revokedAt),\n gt(runnerBootstrapTokens.expiresAt, sql`now()`),\n ),\n )\n .returning();\n if (!bootstrap) throw new RunnerBootstrapTokenInvalidError();\n const [session] = await tx\n .insert(runnerControlSessions)\n .values({\n runnerInstanceId: bootstrap.runnerInstanceId,\n provisionerId: bootstrap.provisionerId,\n hashedToken: hashOpaqueToken(controlSessionToken),\n prefix: extractDisplayPrefix(controlSessionToken),\n expiresAt,\n })\n .returning({id: runnerControlSessions.id});\n if (!session) throw new Error('Runner control session insert returned no row');\n return {runnerInstanceId: bootstrap.runnerInstanceId, controlSessionToken, expiresAt};\n });\n}\n\nexport async function resolveRunnerControlSession(rawToken: string) {\n const [session] = await db()\n .select({\n id: runnerControlSessions.id,\n runnerInstanceId: runnerControlSessions.runnerInstanceId,\n provisionerId: runnerControlSessions.provisionerId,\n })\n .from(runnerControlSessions)\n .leftJoin(provisionerTokens, eq(provisionerTokens.id, runnerControlSessions.provisionerId))\n .where(\n and(\n eq(runnerControlSessions.hashedToken, hashOpaqueToken(rawToken)),\n isNull(runnerControlSessions.closedAt),\n or(isNull(provisionerTokens.id), isNull(provisionerTokens.revokedAt)),\n gt(runnerControlSessions.expiresAt, sql`now()`),\n ),\n )\n .limit(1);\n return session;\n}\n\nexport async function enrollRunnerControlSession(params: {\n runnerInstanceId: string;\n provisionerId: string;\n labels: string[];\n capabilities?: RunnerToolCapabilitiesDto | null;\n providerKind: string;\n protocolVersion: string;\n}): Promise<string | null> {\n return await db().transaction(async (tx) => {\n const [current] = await tx\n .select({intendedReservationId: providerRunners.intendedReservationId})\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n ),\n );\n if (!current) throw new RunnerControlSessionInvalidError();\n if (current.intendedReservationId)\n await tx.execute(\n sql`select pg_advisory_xact_lock(hashtext(${`runners_assignment:${params.provisionerId}:${current.intendedReservationId}`}))`,\n );\n\n const [updated] = await tx\n .update(providerRunners)\n .set({\n labels: [...canonicalizeLabels(params.labels)],\n providerKind: params.providerKind,\n protocolVersion: params.protocolVersion,\n capabilities: params.capabilities ?? null,\n state: 'running',\n reportedAt: sql`now()`,\n updatedAt: sql`now()`,\n })\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n )\n .returning({\n id: providerRunners.id,\n intendedReservationId: providerRunners.intendedReservationId,\n });\n if (!updated) throw new RunnerControlSessionInvalidError();\n\n await updateRunnerControlSessionLastSeen(tx, params.runnerInstanceId, params.provisionerId);\n\n const intendedReservationId = updated.intendedReservationId;\n if (!intendedReservationId) return null;\n\n try {\n return await tx.transaction(async (promotionTx) => {\n await assignRunnerInstancesTx(promotionTx, {\n provisionerId: params.provisionerId,\n reservationId: intendedReservationId,\n runnerInstanceIds: [params.runnerInstanceId],\n });\n return await issueRunnerActivationTokenTx(promotionTx, {\n runnerInstanceId: params.runnerInstanceId,\n provisionerId: params.provisionerId,\n ttlSeconds: config.RUNNER_ACTIVATION_TOKEN_TTL_SECONDS,\n });\n });\n } catch (error) {\n const expectedFailure =\n error instanceof ReservationExpiredError ||\n error instanceof ReservationNotFoundError ||\n error instanceof RunnerInstanceAlreadyAssignedError ||\n error instanceof RunnerInstanceNotAssignableError;\n const details = {\n err: error,\n runnerInstanceId: params.runnerInstanceId,\n provisionerId: params.provisionerId,\n reservationId: intendedReservationId,\n };\n if (expectedFailure)\n logger().debug(details, 'Runner reservation could not be promoted during enrollment');\n else\n logger().error(\n details,\n 'Unexpected failure promoting runner reservation during enrollment',\n );\n return null;\n }\n });\n}\n\nexport async function attachRunnerControlProviderId(params: {\n runnerInstanceId: string;\n provisionerId: string;\n providerRunnerId: string;\n}): Promise<boolean> {\n const rows = await db()\n .update(providerRunners)\n .set({providerRunnerId: params.providerRunnerId, updatedAt: sql`now()`})\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n isNull(providerRunners.providerRunnerId),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n )\n .returning({id: providerRunners.id});\n return rows.length === 1;\n}\n\nexport async function touchRunnerControlSession(runnerInstanceId: string, provisionerId: string) {\n await updateRunnerControlSessionLastSeen(db(), runnerInstanceId, provisionerId);\n await db()\n .update(providerRunners)\n .set({reportedAt: sql`now()`, updatedAt: sql`now()`})\n .where(\n and(\n eq(providerRunners.id, runnerInstanceId),\n eq(providerRunners.provisionerId, provisionerId),\n ),\n );\n}\n\nasync function updateRunnerControlSessionLastSeen(\n tx: Tx | NodePgDatabase<typeof schema>,\n runnerInstanceId: string,\n provisionerId: string,\n) {\n await tx\n .update(runnerControlSessions)\n .set({lastSeenAt: sql`now()`})\n .where(\n and(\n eq(runnerControlSessions.runnerInstanceId, runnerInstanceId),\n eq(runnerControlSessions.provisionerId, provisionerId),\n isNull(runnerControlSessions.closedAt),\n ),\n );\n}\n"],"names":["logger","extractDisplayPrefix","generateOpaqueToken","hashOpaqueToken","canonicalizeLabels","and","eq","gt","isNull","notInArray","or","sql","config","ReservationExpiredError","ReservationNotFoundError","RunnerInstanceAlreadyAssignedError","RunnerInstanceNotAssignableError","db","assignRunnerInstancesTx","terminalStates","provisionerTokens","runnerBootstrapTokens","runnerControlSessions","providerRunners","issueRunnerActivationTokenTx","RunnerBootstrapTokenInvalidError","Error","name","RunnerControlSessionInvalidError","createRunnerInstancesWithBootstrapTokens","params","expiresAt","Date","now","ttlSeconds","transaction","tx","instances","insert","values","runnerInstances","map","runner","provisionerId","providerKind","templateKey","intendedReservationId","reservationId","state","labels","reportedAt","returning","id","results","instance","runnerInstanceId","bootstrapToken","result","hashedToken","prefix","exchangeRunnerBootstrapToken","controlSessionToken","bootstrap","update","set","consumedAt","where","rawToken","revokedAt","session","resolveRunnerControlSession","select","from","leftJoin","closedAt","limit","enrollRunnerControlSession","current","execute","updated","protocolVersion","capabilities","updatedAt","updateRunnerControlSessionLastSeen","promotionTx","runnerInstanceIds","RUNNER_ACTIVATION_TOKEN_TTL_SECONDS","error","expectedFailure","details","err","debug","attachRunnerControlProviderId","rows","providerRunnerId","length","touchRunnerControlSession","lastSeenAt"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/core/runner-control-sessions.ts"],"sourcesContent":["import type {RunnerToolCapabilitiesDto} from '@shipfox/api-runners-dto';\nimport type {NodePgDatabase} from '@shipfox/node-drizzle';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {extractDisplayPrefix, generateOpaqueToken, hashOpaqueToken} from '@shipfox/node-tokens';\nimport {and, eq, gt, isNull, notInArray, or, sql} from 'drizzle-orm';\nimport {config} from '#config.js';\nimport {\n ReservationExpiredError,\n ReservationNotFoundError,\n RunnerInstanceAlreadyAssignedError,\n RunnerInstanceNotAssignableError,\n} from '#core/errors.js';\nimport {sanitizeRunnerLabels} from '#core/runner-labels.js';\nimport {db, type schema, type Tx} from '#db/db.js';\nimport {assignRunnerInstancesTx} from '#db/runner-assignments.js';\nimport {terminalStates} from '#db/runner-instances.js';\nimport {provisionerTokens} from '#db/schema/provisioner-tokens.js';\nimport {runnerBootstrapTokens, runnerControlSessions} from '#db/schema/runner-control-sessions.js';\nimport {providerRunners} from '#db/schema/runner-instances.js';\nimport {\n type RunnerReservationPromotionFailureReason,\n recordRunnerReservationPromotionFailure,\n} from '#metrics/index.js';\nimport {issueRunnerActivationTokenTx} from './runner-activation.js';\n\nexport class RunnerBootstrapTokenInvalidError extends Error {\n constructor() {\n super('Runner bootstrap token is invalid, expired, or has already been used');\n this.name = 'RunnerBootstrapTokenInvalidError';\n }\n}\n\nexport class RunnerControlSessionInvalidError extends Error {\n constructor() {\n super('Runner control session is invalid, expired, or closed');\n this.name = 'RunnerControlSessionInvalidError';\n }\n}\n\nexport async function createRunnerInstancesWithBootstrapTokens(params: {\n provisionerId: string;\n providerKind?: string | null;\n runnerInstances: Array<{\n templateKey?: string | null;\n reservationId?: string | null;\n }>;\n ttlSeconds: number;\n}): Promise<Array<{runnerInstanceId: string; bootstrapToken: string}>> {\n const expiresAt = new Date(Date.now() + params.ttlSeconds * 1000);\n return await db().transaction(async (tx) => {\n const instances = await tx\n .insert(providerRunners)\n .values(\n params.runnerInstances.map((runner) => ({\n provisionerId: params.provisionerId,\n providerKind: params.providerKind ?? null,\n templateKey: runner.templateKey ?? null,\n intendedReservationId: runner.reservationId ?? null,\n state: 'starting' as const,\n labels: [],\n reportedAt: new Date(),\n })),\n )\n .returning({id: providerRunners.id});\n const results = instances.map((instance) => ({\n runnerInstanceId: instance.id,\n bootstrapToken: generateOpaqueToken('runnerBootstrapToken'),\n }));\n await tx.insert(runnerBootstrapTokens).values(\n results.map((result) => ({\n runnerInstanceId: result.runnerInstanceId,\n provisionerId: params.provisionerId,\n hashedToken: hashOpaqueToken(result.bootstrapToken),\n prefix: extractDisplayPrefix(result.bootstrapToken),\n expiresAt,\n })),\n );\n return results;\n });\n}\n\nexport async function exchangeRunnerBootstrapToken(params: {\n rawToken: string;\n ttlSeconds: number;\n}): Promise<{runnerInstanceId: string; controlSessionToken: string; expiresAt: Date}> {\n const controlSessionToken = generateOpaqueToken('runnerControlSession');\n const expiresAt = new Date(Date.now() + params.ttlSeconds * 1000);\n return await db().transaction(async (tx) => {\n const [bootstrap] = await tx\n .update(runnerBootstrapTokens)\n .set({consumedAt: sql`now()`})\n .where(\n and(\n eq(runnerBootstrapTokens.hashedToken, hashOpaqueToken(params.rawToken)),\n isNull(runnerBootstrapTokens.consumedAt),\n isNull(runnerBootstrapTokens.revokedAt),\n gt(runnerBootstrapTokens.expiresAt, sql`now()`),\n ),\n )\n .returning();\n if (!bootstrap) throw new RunnerBootstrapTokenInvalidError();\n const [session] = await tx\n .insert(runnerControlSessions)\n .values({\n runnerInstanceId: bootstrap.runnerInstanceId,\n provisionerId: bootstrap.provisionerId,\n hashedToken: hashOpaqueToken(controlSessionToken),\n prefix: extractDisplayPrefix(controlSessionToken),\n expiresAt,\n })\n .returning({id: runnerControlSessions.id});\n if (!session) throw new Error('Runner control session insert returned no row');\n return {runnerInstanceId: bootstrap.runnerInstanceId, controlSessionToken, expiresAt};\n });\n}\n\nexport async function resolveRunnerControlSession(rawToken: string) {\n const [session] = await db()\n .select({\n id: runnerControlSessions.id,\n runnerInstanceId: runnerControlSessions.runnerInstanceId,\n provisionerId: runnerControlSessions.provisionerId,\n })\n .from(runnerControlSessions)\n .leftJoin(provisionerTokens, eq(provisionerTokens.id, runnerControlSessions.provisionerId))\n .where(\n and(\n eq(runnerControlSessions.hashedToken, hashOpaqueToken(rawToken)),\n isNull(runnerControlSessions.closedAt),\n or(isNull(provisionerTokens.id), isNull(provisionerTokens.revokedAt)),\n gt(runnerControlSessions.expiresAt, sql`now()`),\n ),\n )\n .limit(1);\n return session;\n}\n\nexport async function enrollRunnerControlSession(params: {\n runnerInstanceId: string;\n provisionerId: string;\n labels: string[];\n capabilities?: RunnerToolCapabilitiesDto | null;\n providerKind: string;\n protocolVersion: string;\n}): Promise<string | null> {\n return await db().transaction(async (tx) => {\n const [current] = await tx\n .select({\n intendedReservationId: providerRunners.intendedReservationId,\n provisionerScope: provisionerTokens.scope,\n })\n .from(providerRunners)\n .innerJoin(provisionerTokens, eq(provisionerTokens.id, providerRunners.provisionerId))\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n ),\n );\n if (!current) throw new RunnerControlSessionInvalidError();\n if (current.intendedReservationId)\n await tx.execute(\n sql`select pg_advisory_xact_lock(hashtext(${`runners_assignment:${params.provisionerId}:${current.intendedReservationId}`}))`,\n );\n\n const [updated] = await tx\n .update(providerRunners)\n .set({\n labels: sanitizeRunnerLabels(params.labels, {\n scope: current.provisionerScope,\n source: 'runner control enrollment',\n }),\n providerKind: params.providerKind,\n protocolVersion: params.protocolVersion,\n capabilities: params.capabilities ?? null,\n state: 'running',\n reportedAt: sql`now()`,\n updatedAt: sql`now()`,\n })\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n )\n .returning({\n id: providerRunners.id,\n intendedReservationId: providerRunners.intendedReservationId,\n });\n if (!updated) throw new RunnerControlSessionInvalidError();\n\n await updateRunnerControlSessionLastSeen(tx, params.runnerInstanceId, params.provisionerId);\n\n const intendedReservationId = updated.intendedReservationId;\n if (!intendedReservationId) return null;\n\n try {\n return await tx.transaction(async (promotionTx) => {\n await assignRunnerInstancesTx(promotionTx, {\n provisionerId: params.provisionerId,\n reservationId: intendedReservationId,\n runnerInstanceIds: [params.runnerInstanceId],\n });\n return await issueRunnerActivationTokenTx(promotionTx, {\n runnerInstanceId: params.runnerInstanceId,\n provisionerId: params.provisionerId,\n ttlSeconds: config.RUNNER_ACTIVATION_TOKEN_TTL_SECONDS,\n });\n });\n } catch (error) {\n const expectedFailureReason = getRunnerReservationPromotionFailureReason(error);\n const details = {\n err: error,\n runnerInstanceId: params.runnerInstanceId,\n provisionerId: params.provisionerId,\n reservationId: intendedReservationId,\n };\n if (expectedFailureReason) {\n recordRunnerReservationPromotionFailure(expectedFailureReason);\n logger().debug(details, 'Runner reservation could not be promoted during enrollment');\n } else {\n logger().error(\n details,\n 'Unexpected failure promoting runner reservation during enrollment',\n );\n }\n return null;\n }\n });\n}\n\nfunction getRunnerReservationPromotionFailureReason(\n error: unknown,\n): RunnerReservationPromotionFailureReason | null {\n if (error instanceof ReservationExpiredError) return 'reservation-expired';\n if (error instanceof ReservationNotFoundError) return 'reservation-not-found';\n if (error instanceof RunnerInstanceAlreadyAssignedError) return 'already-assigned';\n if (error instanceof RunnerInstanceNotAssignableError) return 'not-assignable';\n return null;\n}\n\nexport async function attachRunnerControlProviderId(params: {\n runnerInstanceId: string;\n provisionerId: string;\n providerRunnerId: string;\n}): Promise<boolean> {\n const rows = await db()\n .update(providerRunners)\n .set({providerRunnerId: params.providerRunnerId, updatedAt: sql`now()`})\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n isNull(providerRunners.providerRunnerId),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n )\n .returning({id: providerRunners.id});\n return rows.length === 1;\n}\n\nexport async function touchRunnerControlSession(runnerInstanceId: string, provisionerId: string) {\n await updateRunnerControlSessionLastSeen(db(), runnerInstanceId, provisionerId);\n await db()\n .update(providerRunners)\n .set({reportedAt: sql`now()`, updatedAt: sql`now()`})\n .where(\n and(\n eq(providerRunners.id, runnerInstanceId),\n eq(providerRunners.provisionerId, provisionerId),\n ),\n );\n}\n\nasync function updateRunnerControlSessionLastSeen(\n tx: Tx | NodePgDatabase<typeof schema>,\n runnerInstanceId: string,\n provisionerId: string,\n) {\n await tx\n .update(runnerControlSessions)\n .set({lastSeenAt: sql`now()`})\n .where(\n and(\n eq(runnerControlSessions.runnerInstanceId, runnerInstanceId),\n eq(runnerControlSessions.provisionerId, provisionerId),\n isNull(runnerControlSessions.closedAt),\n ),\n );\n}\n"],"names":["logger","extractDisplayPrefix","generateOpaqueToken","hashOpaqueToken","and","eq","gt","isNull","notInArray","or","sql","config","ReservationExpiredError","ReservationNotFoundError","RunnerInstanceAlreadyAssignedError","RunnerInstanceNotAssignableError","sanitizeRunnerLabels","db","assignRunnerInstancesTx","terminalStates","provisionerTokens","runnerBootstrapTokens","runnerControlSessions","providerRunners","recordRunnerReservationPromotionFailure","issueRunnerActivationTokenTx","RunnerBootstrapTokenInvalidError","Error","name","RunnerControlSessionInvalidError","createRunnerInstancesWithBootstrapTokens","params","expiresAt","Date","now","ttlSeconds","transaction","tx","instances","insert","values","runnerInstances","map","runner","provisionerId","providerKind","templateKey","intendedReservationId","reservationId","state","labels","reportedAt","returning","id","results","instance","runnerInstanceId","bootstrapToken","result","hashedToken","prefix","exchangeRunnerBootstrapToken","controlSessionToken","bootstrap","update","set","consumedAt","where","rawToken","revokedAt","session","resolveRunnerControlSession","select","from","leftJoin","closedAt","limit","enrollRunnerControlSession","current","provisionerScope","scope","innerJoin","execute","updated","source","protocolVersion","capabilities","updatedAt","updateRunnerControlSessionLastSeen","promotionTx","runnerInstanceIds","RUNNER_ACTIVATION_TOKEN_TTL_SECONDS","error","expectedFailureReason","getRunnerReservationPromotionFailureReason","details","err","debug","attachRunnerControlProviderId","rows","providerRunnerId","length","touchRunnerControlSession","lastSeenAt"],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import type { RunnerInstance, RunnerInstanceState } from '#core/entities/runner-
|
|
|
2
2
|
import { type RunnerInstanceReportEvent } from '#db/index.js';
|
|
3
3
|
import type { RunnerInstanceBoundJobExecution } from '#db/job-executions.js';
|
|
4
4
|
export interface ReportRunnerInstancesParams {
|
|
5
|
+
scope: 'installation' | 'workspace';
|
|
5
6
|
workspaceId: string | null;
|
|
6
7
|
provisionerId: string;
|
|
7
8
|
events: RunnerInstanceReportEvent[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner-instances.d.ts","sourceRoot":"","sources":["../../src/core/runner-instances.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,cAAc,EAAE,mBAAmB,EAAC,MAAM,mCAAmC,CAAC;AAC3F,OAAO,EAKL,KAAK,yBAAyB,EAG/B,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAEV,+BAA+B,EAChC,MAAM,uBAAuB,CAAC;AAW/B,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,yBAAyB,EAAE,CAAC;CACrC;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnB;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,8BAA8B;IAC7C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,yBAAyB,EAAE,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,WAAW,CAAC;AAC1D,KAAK,4BAA4B,GAAG,eAAe,GAAG,gBAAgB,CAAC;AAEvE,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,EAAE,IAAI,CAAC;IACtB,uBAAuB,EAAE,IAAI,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,wBAAwB;IACvC,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAClC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,iBAAiB,EAAE,2BAA2B,GAAG,IAAI,CAAC;IACtD,aAAa,EAAE,sBAAsB,CAAC;IACtC,mBAAmB,EAAE,4BAA4B,GAAG,IAAI,CAAC;CAC1D;AAED,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,iCAAiC,EAAE,MAAM,EAAE,CAAC;CAC7C;AAED,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAE7E,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,iBAAiB,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;CAC9B;AAED,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,2BAA2B,CAAC,CAYtC;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,8BAA8B,GACrC,OAAO,CAAC,8BAA8B,CAAC,CA4BzC;AAED,wBAAgB,oCAAoC,CAAC,MAAM,EAAE;IAC3D,yBAAyB,EAAE,MAAM,EAAE,CAAC;IACpC,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,oCAAoC,EAAE,GAAG,CAAC,MAAM,EAAE,+BAA+B,CAAC,CAAC;CACpF,GAAG,wBAAwB,EAAE,CAuB7B;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAa9F;AAcD,wBAAgB,aAAa,CAC3B,GAAG,EAAE,cAAc,GAAG,SAAS,EAC/B,iBAAiB,EAAE,+BAA+B,GAAG,SAAS,GAC7D,sBAAsB,CAExB"}
|
|
1
|
+
{"version":3,"file":"runner-instances.d.ts","sourceRoot":"","sources":["../../src/core/runner-instances.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/runner-instances.ts"],"sourcesContent":["import type {RunnerInstance, RunnerInstanceState} from '#core/entities/runner-instance.js';\nimport {\n attachRunnerInstanceProviderId as attachRunnerInstanceProviderIdDb,\n isTerminalState,\n listActiveRunnerInstances,\n listActiveRunningJobExecutions,\n type RunnerInstanceReportEvent,\n reconcileRunnerInstances as reconcileRunnerInstancesDb,\n reportRunnerInstances as reportRunnerInstancesDb,\n} from '#db/index.js';\nimport type {\n ActiveRunningJobExecution,\n RunnerInstanceBoundJobExecution,\n} from '#db/job-executions.js';\nimport {\n providerRunnerAbsentTerminatedCount,\n providerRunnerReconcileCallCount,\n providerRunnerReportCount,\n providerRunnerTerminateIntentHonoredCount,\n providerRunnerTerminateIntentIssuedCount,\n reservationReleasedCount,\n} from '#metrics/instance.js';\nimport {config} from '../config.js';\n\nexport interface ReportRunnerInstancesParams {\n workspaceId: string | null;\n provisionerId: string;\n events: RunnerInstanceReportEvent[];\n}\n\nexport function attachRunnerInstanceProviderId(params: {\n runnerInstanceId: string;\n provisionerId: string;\n providerRunnerId: string;\n}): Promise<boolean> {\n return attachRunnerInstanceProviderIdDb(params);\n}\n\nexport interface ReportRunnerInstancesResult {\n accepted: number;\n reservationsReleased: number;\n}\n\nexport interface ReconcileRunnerInstancesParams {\n workspaceId: string | null;\n provisionerId: string;\n observedRunnerInstanceIds: string[];\n}\n\nexport type ReconcileDesiredIntent = 'keep' | 'terminate';\ntype ReconcileDesiredIntentReason = 'job-cancelled' | 'terminal-state';\n\nexport interface ReconciledBoundJobExecution {\n jobId: string;\n jobExecutionId: string;\n workflowRunAttemptId: string;\n lastHeartbeatAt: Date;\n cancellationRequestedAt: Date | null;\n}\n\nexport interface ReconciledRunnerInstance {\n providerRunnerId: string;\n state: RunnerInstanceState | null;\n reservationId: string | null;\n runnerSessionId: string | null;\n boundJobExecution: ReconciledBoundJobExecution | null;\n desiredIntent: ReconcileDesiredIntent;\n desiredIntentReason: ReconcileDesiredIntentReason | null;\n}\n\nexport interface ReconcileRunnerInstancesResult {\n runners: ReconciledRunnerInstance[];\n terminatedAbsentRunnerInstanceIds: string[];\n}\n\nexport type ActiveRunnerState = 'starting' | 'running' | 'stopping' | 'busy';\n\nexport interface ActiveRunner {\n runnerSessionId: string | null;\n providerRunnerId: string | null;\n provisionerId: string | null;\n state: ActiveRunnerState;\n labels: string[];\n templateKey: string | null;\n providerKind: string | null;\n jobId: string | null;\n workflowRunAttemptId: string | null;\n projectId: string | null;\n reportedAt: Date | null;\n lastHeartbeatAt: Date | null;\n}\n\nexport async function reportRunnerInstances(\n params: ReportRunnerInstancesParams,\n): Promise<ReportRunnerInstancesResult> {\n const result = await reportRunnerInstancesDb(params);\n\n for (const event of params.events) {\n providerRunnerReportCount.add(1, {state: event.state});\n }\n for (const intent of result.terminateIntentsHonored) {\n providerRunnerTerminateIntentHonoredCount.add(1, {reason: intent.reason});\n }\n if (result.reservationsReleased > 0) reservationReleasedCount.add(result.reservationsReleased);\n\n return result;\n}\n\nexport async function reconcileRunnerInstances(\n params: ReconcileRunnerInstancesParams,\n): Promise<ReconcileRunnerInstancesResult> {\n const result = await reconcileRunnerInstancesDb({\n ...params,\n terminateGraceSeconds: config.RUNNER_RECONCILE_TERMINATE_GRACE_SECONDS,\n });\n\n if (result.reservationsReleased > 0) reservationReleasedCount.add(result.reservationsReleased);\n providerRunnerReconcileCallCount.add(1);\n if (result.absentIds.length > 0) providerRunnerAbsentTerminatedCount.add(result.absentIds.length);\n\n const runners = reconcileRunnerInstancesFromDbResult({\n observedRunnerInstanceIds: params.observedRunnerInstanceIds,\n observedRows: result.observedRows,\n boundJobExecutionsByRunnerInstanceId: result.boundJobExecutionsByRunnerInstanceId,\n });\n for (const runner of runners) {\n if (runner.desiredIntentReason) {\n providerRunnerTerminateIntentIssuedCount.add(1, {\n surface: 'reconcile',\n reason: runner.desiredIntentReason,\n });\n }\n }\n\n return {\n runners,\n terminatedAbsentRunnerInstanceIds: result.absentIds,\n };\n}\n\nexport function reconcileRunnerInstancesFromDbResult(params: {\n observedRunnerInstanceIds: string[];\n observedRows: RunnerInstance[];\n boundJobExecutionsByRunnerInstanceId: Map<string, RunnerInstanceBoundJobExecution>;\n}): ReconciledRunnerInstance[] {\n const rowsByRunnerInstanceId = new Map(\n params.observedRows.map((row) => [row.providerRunnerId, row]),\n );\n\n return params.observedRunnerInstanceIds.map((providerRunnerId) => {\n const row = rowsByRunnerInstanceId.get(providerRunnerId);\n const boundJobExecution = params.boundJobExecutionsByRunnerInstanceId.get(providerRunnerId);\n\n const desiredIntentReason = getDesiredIntentReason(row, boundJobExecution);\n\n return {\n providerRunnerId,\n state: row?.state ?? null,\n reservationId: row?.reservationId ?? null,\n runnerSessionId: row?.runnerSessionId ?? null,\n boundJobExecution: boundJobExecution\n ? toReconciledBoundJobExecution(boundJobExecution)\n : null,\n desiredIntent: desiredIntentReason ? 'terminate' : 'keep',\n desiredIntentReason,\n };\n });\n}\n\nexport async function listActiveRunners(params: {workspaceId: string}): Promise<ActiveRunner[]> {\n const [providerRunnerRows, jobExecutionRows] = await Promise.all([\n listActiveRunnerInstances({\n workspaceId: params.workspaceId,\n windowSeconds: config.RUNNER_ACTIVE_WINDOW_SECONDS,\n }),\n listActiveRunningJobExecutions({\n workspaceId: params.workspaceId,\n windowSeconds: config.RUNNER_ACTIVE_WINDOW_SECONDS,\n }),\n ]);\n\n return mergeActiveRunners(providerRunnerRows, jobExecutionRows);\n}\n\nfunction toReconciledBoundJobExecution(\n jobExecution: RunnerInstanceBoundJobExecution,\n): ReconciledBoundJobExecution {\n return {\n jobId: jobExecution.jobId,\n jobExecutionId: jobExecution.jobExecutionId,\n workflowRunAttemptId: jobExecution.workflowRunAttemptId,\n lastHeartbeatAt: jobExecution.lastHeartbeatAt,\n cancellationRequestedAt: jobExecution.cancellationRequestedAt,\n };\n}\n\nexport function desiredIntent(\n row: RunnerInstance | undefined,\n boundJobExecution: RunnerInstanceBoundJobExecution | undefined,\n): ReconcileDesiredIntent {\n return getDesiredIntentReason(row, boundJobExecution) ? 'terminate' : 'keep';\n}\n\nfunction getDesiredIntentReason(\n row: RunnerInstance | undefined,\n boundJobExecution: RunnerInstanceBoundJobExecution | undefined,\n): ReconcileDesiredIntentReason | null {\n if (!row) return null;\n if (isTerminalState(row.state)) return 'terminal-state';\n if (boundJobExecution?.cancellationRequestedAt) return 'job-cancelled';\n return null;\n}\n\nfunction mergeActiveRunners(\n providerRunners: RunnerInstance[],\n jobExecutions: ActiveRunningJobExecution[],\n): ActiveRunner[] {\n const jobExecutionsByRunnerSessionId = new Map<string, ActiveRunningJobExecution[]>();\n const jobExecutionsByRunnerInstanceId = new Map<string, ActiveRunningJobExecution[]>();\n for (const jobExecution of jobExecutions) {\n const runnerJobExecutions =\n jobExecutionsByRunnerSessionId.get(jobExecution.runnerSessionId) ?? [];\n runnerJobExecutions.push(jobExecution);\n jobExecutionsByRunnerSessionId.set(jobExecution.runnerSessionId, runnerJobExecutions);\n\n if (jobExecution.provisionerId && jobExecution.providerRunnerId) {\n const key = providerRunnerKey(jobExecution.provisionerId, jobExecution.providerRunnerId);\n const providerRunnerJobExecutions = jobExecutionsByRunnerInstanceId.get(key) ?? [];\n providerRunnerJobExecutions.push(jobExecution);\n jobExecutionsByRunnerInstanceId.set(key, providerRunnerJobExecutions);\n }\n }\n\n const merged: ActiveRunner[] = [];\n const usedJobExecutionIds = new Set<string>();\n\n for (const providerRunner of providerRunners) {\n const providerRunnerJobExecutions =\n jobExecutionsByRunnerInstanceId.get(\n providerRunnerKey(providerRunner.provisionerId, providerRunner.providerRunnerId),\n ) ??\n (providerRunner.runnerSessionId\n ? jobExecutionsByRunnerSessionId.get(providerRunner.runnerSessionId)\n : undefined);\n if (!providerRunnerJobExecutions || providerRunnerJobExecutions.length === 0) {\n merged.push(toActiveRunner(providerRunner, undefined));\n continue;\n }\n\n let emitted = false;\n for (const jobExecution of providerRunnerJobExecutions) {\n if (usedJobExecutionIds.has(jobExecution.jobExecutionId)) continue;\n usedJobExecutionIds.add(jobExecution.jobExecutionId);\n merged.push(toActiveRunner(providerRunner, jobExecution));\n emitted = true;\n }\n if (!emitted) merged.push(toActiveRunner(providerRunner, undefined));\n }\n\n for (const jobExecution of jobExecutions) {\n if (usedJobExecutionIds.has(jobExecution.jobExecutionId)) continue;\n merged.push(toActiveRunner(undefined, jobExecution));\n }\n\n return merged.sort(compareActiveRunners);\n}\n\nfunction providerRunnerKey(provisionerId: string, providerRunnerId: string): string {\n return `${provisionerId}:${providerRunnerId}`;\n}\n\nfunction toActiveRunner(\n providerRunner: RunnerInstance | undefined,\n jobExecution: ActiveRunningJobExecution | undefined,\n): ActiveRunner {\n return {\n runnerSessionId: providerRunner?.runnerSessionId ?? jobExecution?.runnerSessionId ?? null,\n providerRunnerId: providerRunner?.providerRunnerId ?? jobExecution?.providerRunnerId ?? null,\n provisionerId: providerRunner?.provisionerId ?? jobExecution?.provisionerId ?? null,\n state: jobExecution ? 'busy' : toActiveRunnerState(providerRunner?.state ?? 'running'),\n labels: providerRunner?.labels ?? jobExecution?.runnerLabels ?? [],\n templateKey: providerRunner?.templateKey ?? null,\n providerKind: providerRunner?.providerKind ?? null,\n jobId: jobExecution?.jobId ?? null,\n workflowRunAttemptId: jobExecution?.workflowRunAttemptId ?? null,\n projectId: jobExecution?.projectId ?? null,\n reportedAt: providerRunner?.reportedAt ?? null,\n lastHeartbeatAt: jobExecution?.lastHeartbeatAt ?? null,\n };\n}\n\nfunction toActiveRunnerState(state: RunnerInstanceState): ActiveRunnerState {\n if (state === 'starting' || state === 'stopping') return state;\n return 'running';\n}\n\nfunction compareActiveRunners(a: ActiveRunner, b: ActiveRunner): number {\n const aTime = Math.max(a.lastHeartbeatAt?.getTime() ?? 0, a.reportedAt?.getTime() ?? 0);\n const bTime = Math.max(b.lastHeartbeatAt?.getTime() ?? 0, b.reportedAt?.getTime() ?? 0);\n return bTime - aTime;\n}\n"],"names":["attachRunnerInstanceProviderId","attachRunnerInstanceProviderIdDb","isTerminalState","listActiveRunnerInstances","listActiveRunningJobExecutions","reconcileRunnerInstances","reconcileRunnerInstancesDb","reportRunnerInstances","reportRunnerInstancesDb","providerRunnerAbsentTerminatedCount","providerRunnerReconcileCallCount","providerRunnerReportCount","providerRunnerTerminateIntentHonoredCount","providerRunnerTerminateIntentIssuedCount","reservationReleasedCount","config","params","result","event","events","add","state","intent","terminateIntentsHonored","reason","reservationsReleased","terminateGraceSeconds","RUNNER_RECONCILE_TERMINATE_GRACE_SECONDS","absentIds","length","runners","reconcileRunnerInstancesFromDbResult","observedRunnerInstanceIds","observedRows","boundJobExecutionsByRunnerInstanceId","runner","desiredIntentReason","surface","terminatedAbsentRunnerInstanceIds","rowsByRunnerInstanceId","Map","map","row","providerRunnerId","get","boundJobExecution","getDesiredIntentReason","reservationId","runnerSessionId","toReconciledBoundJobExecution","desiredIntent","listActiveRunners","providerRunnerRows","jobExecutionRows","Promise","all","workspaceId","windowSeconds","RUNNER_ACTIVE_WINDOW_SECONDS","mergeActiveRunners","jobExecution","jobId","jobExecutionId","workflowRunAttemptId","lastHeartbeatAt","cancellationRequestedAt","providerRunners","jobExecutions","jobExecutionsByRunnerSessionId","jobExecutionsByRunnerInstanceId","runnerJobExecutions","push","set","provisionerId","key","providerRunnerKey","providerRunnerJobExecutions","merged","usedJobExecutionIds","Set","providerRunner","undefined","toActiveRunner","emitted","has","sort","compareActiveRunners","toActiveRunnerState","labels","runnerLabels","templateKey","providerKind","projectId","reportedAt","a","b","aTime","Math","max","getTime","bTime"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/core/runner-instances.ts"],"sourcesContent":["import type {RunnerInstance, RunnerInstanceState} from '#core/entities/runner-instance.js';\nimport {\n attachRunnerInstanceProviderId as attachRunnerInstanceProviderIdDb,\n isTerminalState,\n listActiveRunnerInstances,\n listActiveRunningJobExecutions,\n type RunnerInstanceReportEvent,\n reconcileRunnerInstances as reconcileRunnerInstancesDb,\n reportRunnerInstances as reportRunnerInstancesDb,\n} from '#db/index.js';\nimport type {\n ActiveRunningJobExecution,\n RunnerInstanceBoundJobExecution,\n} from '#db/job-executions.js';\nimport {\n providerRunnerAbsentTerminatedCount,\n providerRunnerReconcileCallCount,\n providerRunnerReportCount,\n providerRunnerTerminateIntentHonoredCount,\n providerRunnerTerminateIntentIssuedCount,\n reservationReleasedCount,\n} from '#metrics/instance.js';\nimport {config} from '../config.js';\n\nexport interface ReportRunnerInstancesParams {\n scope: 'installation' | 'workspace';\n workspaceId: string | null;\n provisionerId: string;\n events: RunnerInstanceReportEvent[];\n}\n\nexport function attachRunnerInstanceProviderId(params: {\n runnerInstanceId: string;\n provisionerId: string;\n providerRunnerId: string;\n}): Promise<boolean> {\n return attachRunnerInstanceProviderIdDb(params);\n}\n\nexport interface ReportRunnerInstancesResult {\n accepted: number;\n reservationsReleased: number;\n}\n\nexport interface ReconcileRunnerInstancesParams {\n workspaceId: string | null;\n provisionerId: string;\n observedRunnerInstanceIds: string[];\n}\n\nexport type ReconcileDesiredIntent = 'keep' | 'terminate';\ntype ReconcileDesiredIntentReason = 'job-cancelled' | 'terminal-state';\n\nexport interface ReconciledBoundJobExecution {\n jobId: string;\n jobExecutionId: string;\n workflowRunAttemptId: string;\n lastHeartbeatAt: Date;\n cancellationRequestedAt: Date | null;\n}\n\nexport interface ReconciledRunnerInstance {\n providerRunnerId: string;\n state: RunnerInstanceState | null;\n reservationId: string | null;\n runnerSessionId: string | null;\n boundJobExecution: ReconciledBoundJobExecution | null;\n desiredIntent: ReconcileDesiredIntent;\n desiredIntentReason: ReconcileDesiredIntentReason | null;\n}\n\nexport interface ReconcileRunnerInstancesResult {\n runners: ReconciledRunnerInstance[];\n terminatedAbsentRunnerInstanceIds: string[];\n}\n\nexport type ActiveRunnerState = 'starting' | 'running' | 'stopping' | 'busy';\n\nexport interface ActiveRunner {\n runnerSessionId: string | null;\n providerRunnerId: string | null;\n provisionerId: string | null;\n state: ActiveRunnerState;\n labels: string[];\n templateKey: string | null;\n providerKind: string | null;\n jobId: string | null;\n workflowRunAttemptId: string | null;\n projectId: string | null;\n reportedAt: Date | null;\n lastHeartbeatAt: Date | null;\n}\n\nexport async function reportRunnerInstances(\n params: ReportRunnerInstancesParams,\n): Promise<ReportRunnerInstancesResult> {\n const result = await reportRunnerInstancesDb(params);\n\n for (const event of params.events) {\n providerRunnerReportCount.add(1, {state: event.state});\n }\n for (const intent of result.terminateIntentsHonored) {\n providerRunnerTerminateIntentHonoredCount.add(1, {reason: intent.reason});\n }\n if (result.reservationsReleased > 0) reservationReleasedCount.add(result.reservationsReleased);\n\n return result;\n}\n\nexport async function reconcileRunnerInstances(\n params: ReconcileRunnerInstancesParams,\n): Promise<ReconcileRunnerInstancesResult> {\n const result = await reconcileRunnerInstancesDb({\n ...params,\n terminateGraceSeconds: config.RUNNER_RECONCILE_TERMINATE_GRACE_SECONDS,\n });\n\n if (result.reservationsReleased > 0) reservationReleasedCount.add(result.reservationsReleased);\n providerRunnerReconcileCallCount.add(1);\n if (result.absentIds.length > 0) providerRunnerAbsentTerminatedCount.add(result.absentIds.length);\n\n const runners = reconcileRunnerInstancesFromDbResult({\n observedRunnerInstanceIds: params.observedRunnerInstanceIds,\n observedRows: result.observedRows,\n boundJobExecutionsByRunnerInstanceId: result.boundJobExecutionsByRunnerInstanceId,\n });\n for (const runner of runners) {\n if (runner.desiredIntentReason) {\n providerRunnerTerminateIntentIssuedCount.add(1, {\n surface: 'reconcile',\n reason: runner.desiredIntentReason,\n });\n }\n }\n\n return {\n runners,\n terminatedAbsentRunnerInstanceIds: result.absentIds,\n };\n}\n\nexport function reconcileRunnerInstancesFromDbResult(params: {\n observedRunnerInstanceIds: string[];\n observedRows: RunnerInstance[];\n boundJobExecutionsByRunnerInstanceId: Map<string, RunnerInstanceBoundJobExecution>;\n}): ReconciledRunnerInstance[] {\n const rowsByRunnerInstanceId = new Map(\n params.observedRows.map((row) => [row.providerRunnerId, row]),\n );\n\n return params.observedRunnerInstanceIds.map((providerRunnerId) => {\n const row = rowsByRunnerInstanceId.get(providerRunnerId);\n const boundJobExecution = params.boundJobExecutionsByRunnerInstanceId.get(providerRunnerId);\n\n const desiredIntentReason = getDesiredIntentReason(row, boundJobExecution);\n\n return {\n providerRunnerId,\n state: row?.state ?? null,\n reservationId: row?.reservationId ?? null,\n runnerSessionId: row?.runnerSessionId ?? null,\n boundJobExecution: boundJobExecution\n ? toReconciledBoundJobExecution(boundJobExecution)\n : null,\n desiredIntent: desiredIntentReason ? 'terminate' : 'keep',\n desiredIntentReason,\n };\n });\n}\n\nexport async function listActiveRunners(params: {workspaceId: string}): Promise<ActiveRunner[]> {\n const [providerRunnerRows, jobExecutionRows] = await Promise.all([\n listActiveRunnerInstances({\n workspaceId: params.workspaceId,\n windowSeconds: config.RUNNER_ACTIVE_WINDOW_SECONDS,\n }),\n listActiveRunningJobExecutions({\n workspaceId: params.workspaceId,\n windowSeconds: config.RUNNER_ACTIVE_WINDOW_SECONDS,\n }),\n ]);\n\n return mergeActiveRunners(providerRunnerRows, jobExecutionRows);\n}\n\nfunction toReconciledBoundJobExecution(\n jobExecution: RunnerInstanceBoundJobExecution,\n): ReconciledBoundJobExecution {\n return {\n jobId: jobExecution.jobId,\n jobExecutionId: jobExecution.jobExecutionId,\n workflowRunAttemptId: jobExecution.workflowRunAttemptId,\n lastHeartbeatAt: jobExecution.lastHeartbeatAt,\n cancellationRequestedAt: jobExecution.cancellationRequestedAt,\n };\n}\n\nexport function desiredIntent(\n row: RunnerInstance | undefined,\n boundJobExecution: RunnerInstanceBoundJobExecution | undefined,\n): ReconcileDesiredIntent {\n return getDesiredIntentReason(row, boundJobExecution) ? 'terminate' : 'keep';\n}\n\nfunction getDesiredIntentReason(\n row: RunnerInstance | undefined,\n boundJobExecution: RunnerInstanceBoundJobExecution | undefined,\n): ReconcileDesiredIntentReason | null {\n if (!row) return null;\n if (isTerminalState(row.state)) return 'terminal-state';\n if (boundJobExecution?.cancellationRequestedAt) return 'job-cancelled';\n return null;\n}\n\nfunction mergeActiveRunners(\n providerRunners: RunnerInstance[],\n jobExecutions: ActiveRunningJobExecution[],\n): ActiveRunner[] {\n const jobExecutionsByRunnerSessionId = new Map<string, ActiveRunningJobExecution[]>();\n const jobExecutionsByRunnerInstanceId = new Map<string, ActiveRunningJobExecution[]>();\n for (const jobExecution of jobExecutions) {\n const runnerJobExecutions =\n jobExecutionsByRunnerSessionId.get(jobExecution.runnerSessionId) ?? [];\n runnerJobExecutions.push(jobExecution);\n jobExecutionsByRunnerSessionId.set(jobExecution.runnerSessionId, runnerJobExecutions);\n\n if (jobExecution.provisionerId && jobExecution.providerRunnerId) {\n const key = providerRunnerKey(jobExecution.provisionerId, jobExecution.providerRunnerId);\n const providerRunnerJobExecutions = jobExecutionsByRunnerInstanceId.get(key) ?? [];\n providerRunnerJobExecutions.push(jobExecution);\n jobExecutionsByRunnerInstanceId.set(key, providerRunnerJobExecutions);\n }\n }\n\n const merged: ActiveRunner[] = [];\n const usedJobExecutionIds = new Set<string>();\n\n for (const providerRunner of providerRunners) {\n const providerRunnerJobExecutions =\n jobExecutionsByRunnerInstanceId.get(\n providerRunnerKey(providerRunner.provisionerId, providerRunner.providerRunnerId),\n ) ??\n (providerRunner.runnerSessionId\n ? jobExecutionsByRunnerSessionId.get(providerRunner.runnerSessionId)\n : undefined);\n if (!providerRunnerJobExecutions || providerRunnerJobExecutions.length === 0) {\n merged.push(toActiveRunner(providerRunner, undefined));\n continue;\n }\n\n let emitted = false;\n for (const jobExecution of providerRunnerJobExecutions) {\n if (usedJobExecutionIds.has(jobExecution.jobExecutionId)) continue;\n usedJobExecutionIds.add(jobExecution.jobExecutionId);\n merged.push(toActiveRunner(providerRunner, jobExecution));\n emitted = true;\n }\n if (!emitted) merged.push(toActiveRunner(providerRunner, undefined));\n }\n\n for (const jobExecution of jobExecutions) {\n if (usedJobExecutionIds.has(jobExecution.jobExecutionId)) continue;\n merged.push(toActiveRunner(undefined, jobExecution));\n }\n\n return merged.sort(compareActiveRunners);\n}\n\nfunction providerRunnerKey(provisionerId: string, providerRunnerId: string): string {\n return `${provisionerId}:${providerRunnerId}`;\n}\n\nfunction toActiveRunner(\n providerRunner: RunnerInstance | undefined,\n jobExecution: ActiveRunningJobExecution | undefined,\n): ActiveRunner {\n return {\n runnerSessionId: providerRunner?.runnerSessionId ?? jobExecution?.runnerSessionId ?? null,\n providerRunnerId: providerRunner?.providerRunnerId ?? jobExecution?.providerRunnerId ?? null,\n provisionerId: providerRunner?.provisionerId ?? jobExecution?.provisionerId ?? null,\n state: jobExecution ? 'busy' : toActiveRunnerState(providerRunner?.state ?? 'running'),\n labels: providerRunner?.labels ?? jobExecution?.runnerLabels ?? [],\n templateKey: providerRunner?.templateKey ?? null,\n providerKind: providerRunner?.providerKind ?? null,\n jobId: jobExecution?.jobId ?? null,\n workflowRunAttemptId: jobExecution?.workflowRunAttemptId ?? null,\n projectId: jobExecution?.projectId ?? null,\n reportedAt: providerRunner?.reportedAt ?? null,\n lastHeartbeatAt: jobExecution?.lastHeartbeatAt ?? null,\n };\n}\n\nfunction toActiveRunnerState(state: RunnerInstanceState): ActiveRunnerState {\n if (state === 'starting' || state === 'stopping') return state;\n return 'running';\n}\n\nfunction compareActiveRunners(a: ActiveRunner, b: ActiveRunner): number {\n const aTime = Math.max(a.lastHeartbeatAt?.getTime() ?? 0, a.reportedAt?.getTime() ?? 0);\n const bTime = Math.max(b.lastHeartbeatAt?.getTime() ?? 0, b.reportedAt?.getTime() ?? 0);\n return bTime - aTime;\n}\n"],"names":["attachRunnerInstanceProviderId","attachRunnerInstanceProviderIdDb","isTerminalState","listActiveRunnerInstances","listActiveRunningJobExecutions","reconcileRunnerInstances","reconcileRunnerInstancesDb","reportRunnerInstances","reportRunnerInstancesDb","providerRunnerAbsentTerminatedCount","providerRunnerReconcileCallCount","providerRunnerReportCount","providerRunnerTerminateIntentHonoredCount","providerRunnerTerminateIntentIssuedCount","reservationReleasedCount","config","params","result","event","events","add","state","intent","terminateIntentsHonored","reason","reservationsReleased","terminateGraceSeconds","RUNNER_RECONCILE_TERMINATE_GRACE_SECONDS","absentIds","length","runners","reconcileRunnerInstancesFromDbResult","observedRunnerInstanceIds","observedRows","boundJobExecutionsByRunnerInstanceId","runner","desiredIntentReason","surface","terminatedAbsentRunnerInstanceIds","rowsByRunnerInstanceId","Map","map","row","providerRunnerId","get","boundJobExecution","getDesiredIntentReason","reservationId","runnerSessionId","toReconciledBoundJobExecution","desiredIntent","listActiveRunners","providerRunnerRows","jobExecutionRows","Promise","all","workspaceId","windowSeconds","RUNNER_ACTIVE_WINDOW_SECONDS","mergeActiveRunners","jobExecution","jobId","jobExecutionId","workflowRunAttemptId","lastHeartbeatAt","cancellationRequestedAt","providerRunners","jobExecutions","jobExecutionsByRunnerSessionId","jobExecutionsByRunnerInstanceId","runnerJobExecutions","push","set","provisionerId","key","providerRunnerKey","providerRunnerJobExecutions","merged","usedJobExecutionIds","Set","providerRunner","undefined","toActiveRunner","emitted","has","sort","compareActiveRunners","toActiveRunnerState","labels","runnerLabels","templateKey","providerKind","projectId","reportedAt","a","b","aTime","Math","max","getTime","bTime"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type RunnerLabelScope = 'installation' | 'workspace' | 'manual';
|
|
2
|
+
type SanitizeRunnerLabelsParams = {
|
|
3
|
+
scope: RunnerLabelScope;
|
|
4
|
+
source: string;
|
|
5
|
+
logLevel?: 'debug' | 'warn';
|
|
6
|
+
reservedLabels?: readonly string[];
|
|
7
|
+
};
|
|
8
|
+
export declare function sanitizeRunnerLabels(labels: readonly string[], params: SanitizeRunnerLabelsParams): string[];
|
|
9
|
+
export declare function sanitizeRunnerLabelsOrThrow(labels: readonly string[], params: SanitizeRunnerLabelsParams): string[];
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=runner-labels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner-labels.d.ts","sourceRoot":"","sources":["../../src/core/runner-labels.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEvE,KAAK,0BAA0B,GAAG;IAChC,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC,CAAC;AAqBF,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,MAAM,EAAE,0BAA0B,GACjC,MAAM,EAAE,CAEV;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,MAAM,EAAE,0BAA0B,GACjC,MAAM,EAAE,CAOV"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { logger } from '@shipfox/node-opentelemetry';
|
|
2
|
+
import { canonicalizeLabels } from '@shipfox/runner-labels';
|
|
3
|
+
import { runnerReservedLabels } from '#config.js';
|
|
4
|
+
import { RunnerLabelsReservedError } from './errors.js';
|
|
5
|
+
function sanitizeCanonicalRunnerLabels(canonicalLabels, params) {
|
|
6
|
+
if (params.scope === 'installation') return [
|
|
7
|
+
...canonicalLabels
|
|
8
|
+
];
|
|
9
|
+
const reservedLabels = new Set(canonicalizeLabels(params.reservedLabels ?? runnerReservedLabels));
|
|
10
|
+
if (reservedLabels.size === 0) return [
|
|
11
|
+
...canonicalLabels
|
|
12
|
+
];
|
|
13
|
+
const removedLabels = canonicalLabels.filter((label)=>reservedLabels.has(label));
|
|
14
|
+
if (removedLabels.length === 0) return [
|
|
15
|
+
...canonicalLabels
|
|
16
|
+
];
|
|
17
|
+
const details = {
|
|
18
|
+
source: params.source,
|
|
19
|
+
removedLabels
|
|
20
|
+
};
|
|
21
|
+
if (params.logLevel === 'debug') logger().debug(details, 'Removed reserved runner labels from a non-installation source');
|
|
22
|
+
else logger().warn(details, 'Removed reserved runner labels from a non-installation source');
|
|
23
|
+
return canonicalLabels.filter((label)=>!reservedLabels.has(label));
|
|
24
|
+
}
|
|
25
|
+
export function sanitizeRunnerLabels(labels, params) {
|
|
26
|
+
return sanitizeCanonicalRunnerLabels([
|
|
27
|
+
...canonicalizeLabels(labels)
|
|
28
|
+
], params);
|
|
29
|
+
}
|
|
30
|
+
export function sanitizeRunnerLabelsOrThrow(labels, params) {
|
|
31
|
+
const canonicalLabels = [
|
|
32
|
+
...canonicalizeLabels(labels)
|
|
33
|
+
];
|
|
34
|
+
const sanitizedLabels = sanitizeCanonicalRunnerLabels(canonicalLabels, params);
|
|
35
|
+
if (sanitizedLabels.length === 0 && canonicalLabels.length > 0) {
|
|
36
|
+
throw new RunnerLabelsReservedError(canonicalLabels);
|
|
37
|
+
}
|
|
38
|
+
return sanitizedLabels;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=runner-labels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/runner-labels.ts"],"sourcesContent":["import {logger} from '@shipfox/node-opentelemetry';\nimport {canonicalizeLabels} from '@shipfox/runner-labels';\nimport {runnerReservedLabels} from '#config.js';\nimport {RunnerLabelsReservedError} from './errors.js';\n\nexport type RunnerLabelScope = 'installation' | 'workspace' | 'manual';\n\ntype SanitizeRunnerLabelsParams = {\n scope: RunnerLabelScope;\n source: string;\n logLevel?: 'debug' | 'warn';\n reservedLabels?: readonly string[];\n};\n\nfunction sanitizeCanonicalRunnerLabels(\n canonicalLabels: readonly string[],\n params: SanitizeRunnerLabelsParams,\n): string[] {\n if (params.scope === 'installation') return [...canonicalLabels];\n\n const reservedLabels = new Set(canonicalizeLabels(params.reservedLabels ?? runnerReservedLabels));\n if (reservedLabels.size === 0) return [...canonicalLabels];\n\n const removedLabels = canonicalLabels.filter((label) => reservedLabels.has(label));\n if (removedLabels.length === 0) return [...canonicalLabels];\n\n const details = {source: params.source, removedLabels};\n if (params.logLevel === 'debug')\n logger().debug(details, 'Removed reserved runner labels from a non-installation source');\n else logger().warn(details, 'Removed reserved runner labels from a non-installation source');\n return canonicalLabels.filter((label) => !reservedLabels.has(label));\n}\n\nexport function sanitizeRunnerLabels(\n labels: readonly string[],\n params: SanitizeRunnerLabelsParams,\n): string[] {\n return sanitizeCanonicalRunnerLabels([...canonicalizeLabels(labels)], params);\n}\n\nexport function sanitizeRunnerLabelsOrThrow(\n labels: readonly string[],\n params: SanitizeRunnerLabelsParams,\n): string[] {\n const canonicalLabels = [...canonicalizeLabels(labels)];\n const sanitizedLabels = sanitizeCanonicalRunnerLabels(canonicalLabels, params);\n if (sanitizedLabels.length === 0 && canonicalLabels.length > 0) {\n throw new RunnerLabelsReservedError(canonicalLabels);\n }\n return sanitizedLabels;\n}\n"],"names":["logger","canonicalizeLabels","runnerReservedLabels","RunnerLabelsReservedError","sanitizeCanonicalRunnerLabels","canonicalLabels","params","scope","reservedLabels","Set","size","removedLabels","filter","label","has","length","details","source","logLevel","debug","warn","sanitizeRunnerLabels","labels","sanitizeRunnerLabelsOrThrow","sanitizedLabels"],"mappings":"AAAA,SAAQA,MAAM,QAAO,8BAA8B;AACnD,SAAQC,kBAAkB,QAAO,yBAAyB;AAC1D,SAAQC,oBAAoB,QAAO,aAAa;AAChD,SAAQC,yBAAyB,QAAO,cAAc;AAWtD,SAASC,8BACPC,eAAkC,EAClCC,MAAkC;IAElC,IAAIA,OAAOC,KAAK,KAAK,gBAAgB,OAAO;WAAIF;KAAgB;IAEhE,MAAMG,iBAAiB,IAAIC,IAAIR,mBAAmBK,OAAOE,cAAc,IAAIN;IAC3E,IAAIM,eAAeE,IAAI,KAAK,GAAG,OAAO;WAAIL;KAAgB;IAE1D,MAAMM,gBAAgBN,gBAAgBO,MAAM,CAAC,CAACC,QAAUL,eAAeM,GAAG,CAACD;IAC3E,IAAIF,cAAcI,MAAM,KAAK,GAAG,OAAO;WAAIV;KAAgB;IAE3D,MAAMW,UAAU;QAACC,QAAQX,OAAOW,MAAM;QAAEN;IAAa;IACrD,IAAIL,OAAOY,QAAQ,KAAK,SACtBlB,SAASmB,KAAK,CAACH,SAAS;SACrBhB,SAASoB,IAAI,CAACJ,SAAS;IAC5B,OAAOX,gBAAgBO,MAAM,CAAC,CAACC,QAAU,CAACL,eAAeM,GAAG,CAACD;AAC/D;AAEA,OAAO,SAASQ,qBACdC,MAAyB,EACzBhB,MAAkC;IAElC,OAAOF,8BAA8B;WAAIH,mBAAmBqB;KAAQ,EAAEhB;AACxE;AAEA,OAAO,SAASiB,4BACdD,MAAyB,EACzBhB,MAAkC;IAElC,MAAMD,kBAAkB;WAAIJ,mBAAmBqB;KAAQ;IACvD,MAAME,kBAAkBpB,8BAA8BC,iBAAiBC;IACvE,IAAIkB,gBAAgBT,MAAM,KAAK,KAAKV,gBAAgBU,MAAM,GAAG,GAAG;QAC9D,MAAM,IAAIZ,0BAA0BE;IACtC;IACA,OAAOmB;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner-sessions.d.ts","sourceRoot":"","sources":["../../src/core/runner-sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,0BAA0B,CAAC;AAOxE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"runner-sessions.d.ts","sourceRoot":"","sources":["../../src/core/runner-sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,0BAA0B,CAAC;AAOxE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,8BAA8B,CAAC;AAIhE,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,aAAa,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,QAAQ,GAAG,WAAW,GAAG,YAAY,CAAC;IAC5C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,MAAM,4BAA4B,GACpC;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB,GACD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;CAC1B,GACD;IAAC,IAAI,EAAE,YAAY,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,CAAC;AAEzE,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,IAAI,EAAE,qBAAqB,CAAC;IAC5B,UAAU,EAAE,4BAA4B,CAAC;IACzC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,gBAAgB,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAC;CACrD,GAAG,OAAO,CAAC,2BAA2B,CAAC,CA2CvC"}
|
|
@@ -2,8 +2,12 @@ import { canonicalizeLabels } from '@shipfox/runner-labels';
|
|
|
2
2
|
import { createRunnerSessionConsumingEphemeralToken } from '#db/ephemeral-registration-tokens.js';
|
|
3
3
|
import { createRunnerSession, createRunnerSessionConsumingActivationToken } from '#db/runner-sessions.js';
|
|
4
4
|
import { EmptyRunnerLabelsError } from './errors.js';
|
|
5
|
+
import { sanitizeRunnerLabelsOrThrow } from './runner-labels.js';
|
|
5
6
|
export async function registerRunnerSession(params) {
|
|
6
|
-
const labels =
|
|
7
|
+
const labels = params.credential.kind === 'manual' ? sanitizeRunnerLabelsOrThrow(params.labels, {
|
|
8
|
+
scope: 'manual',
|
|
9
|
+
source: 'manual runner registration'
|
|
10
|
+
}) : [
|
|
7
11
|
...canonicalizeLabels(params.labels)
|
|
8
12
|
];
|
|
9
13
|
if (labels.length === 0) throw new EmptyRunnerLabelsError();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/runner-sessions.ts"],"sourcesContent":["import type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';\nimport type {RunnerToolCapabilitiesDto} from '@shipfox/api-runners-dto';\nimport {canonicalizeLabels} from '@shipfox/runner-labels';\nimport {createRunnerSessionConsumingEphemeralToken} from '#db/ephemeral-registration-tokens.js';\nimport {\n createRunnerSession,\n createRunnerSessionConsumingActivationToken,\n} from '#db/runner-sessions.js';\nimport type {RunnerSession} from './entities/runner-session.js';\nimport {EmptyRunnerLabelsError} from './errors.js';\n\nexport interface RegisterRunnerSessionResult {\n session: RunnerSession;\n sessionToken: string;\n mode: 'manual' | 'ephemeral' | 'activation';\n maxClaims: number | null;\n}\n\nexport type RunnerRegistrationCredential =\n | {\n kind: 'manual';\n registrationTokenId: string;\n workspaceId: string;\n }\n | {\n kind: 'ephemeral';\n ephemeralTokenId: string;\n workspaceId: string;\n provisionerId: string;\n reservationId: string | null;\n providerRunnerId: string;\n }\n | {kind: 'activation'; activationTokenId: string; workspaceId: string};\n\nexport async function registerRunnerSession(params: {\n auth: AuthInterModuleClient;\n credential: RunnerRegistrationCredential;\n labels: string[];\n toolCapabilities?: RunnerToolCapabilitiesDto | null;\n}): Promise<RegisterRunnerSessionResult> {\n const labels
|
|
1
|
+
{"version":3,"sources":["../../src/core/runner-sessions.ts"],"sourcesContent":["import type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';\nimport type {RunnerToolCapabilitiesDto} from '@shipfox/api-runners-dto';\nimport {canonicalizeLabels} from '@shipfox/runner-labels';\nimport {createRunnerSessionConsumingEphemeralToken} from '#db/ephemeral-registration-tokens.js';\nimport {\n createRunnerSession,\n createRunnerSessionConsumingActivationToken,\n} from '#db/runner-sessions.js';\nimport type {RunnerSession} from './entities/runner-session.js';\nimport {EmptyRunnerLabelsError} from './errors.js';\nimport {sanitizeRunnerLabelsOrThrow} from './runner-labels.js';\n\nexport interface RegisterRunnerSessionResult {\n session: RunnerSession;\n sessionToken: string;\n mode: 'manual' | 'ephemeral' | 'activation';\n maxClaims: number | null;\n}\n\nexport type RunnerRegistrationCredential =\n | {\n kind: 'manual';\n registrationTokenId: string;\n workspaceId: string;\n }\n | {\n kind: 'ephemeral';\n ephemeralTokenId: string;\n workspaceId: string;\n provisionerId: string;\n reservationId: string | null;\n providerRunnerId: string;\n }\n | {kind: 'activation'; activationTokenId: string; workspaceId: string};\n\nexport async function registerRunnerSession(params: {\n auth: AuthInterModuleClient;\n credential: RunnerRegistrationCredential;\n labels: string[];\n toolCapabilities?: RunnerToolCapabilitiesDto | null;\n}): Promise<RegisterRunnerSessionResult> {\n const labels =\n params.credential.kind === 'manual'\n ? sanitizeRunnerLabelsOrThrow(params.labels, {\n scope: 'manual',\n source: 'manual runner registration',\n })\n : [...canonicalizeLabels(params.labels)];\n if (labels.length === 0) throw new EmptyRunnerLabelsError();\n\n const mode = params.credential.kind;\n const maxClaims = params.credential.kind === 'manual' ? null : 1;\n const session =\n params.credential.kind === 'manual'\n ? await createRunnerSession({\n workspaceId: params.credential.workspaceId,\n scope: 'workspace',\n registrationTokenId: params.credential.registrationTokenId,\n labels,\n toolCapabilities: params.toolCapabilities ?? null,\n })\n : params.credential.kind === 'ephemeral'\n ? await createRunnerSessionConsumingEphemeralToken({\n ephemeralTokenId: params.credential.ephemeralTokenId,\n workspaceId: params.credential.workspaceId,\n labels,\n toolCapabilities: params.toolCapabilities ?? null,\n maxClaims: 1,\n })\n : await createRunnerSessionConsumingActivationToken({\n activationTokenId: params.credential.activationTokenId,\n labels,\n toolCapabilities: params.toolCapabilities ?? null,\n });\n const {token: sessionToken} = await params.auth.mintRunnerSessionToken({\n runnerSessionId: session.id,\n workspaceId: session.workspaceId,\n scope: session.scope,\n labels: session.labels,\n maxClaims,\n });\n\n return {session, sessionToken, mode, maxClaims};\n}\n"],"names":["canonicalizeLabels","createRunnerSessionConsumingEphemeralToken","createRunnerSession","createRunnerSessionConsumingActivationToken","EmptyRunnerLabelsError","sanitizeRunnerLabelsOrThrow","registerRunnerSession","params","labels","credential","kind","scope","source","length","mode","maxClaims","session","workspaceId","registrationTokenId","toolCapabilities","ephemeralTokenId","activationTokenId","token","sessionToken","auth","mintRunnerSessionToken","runnerSessionId","id"],"mappings":"AAEA,SAAQA,kBAAkB,QAAO,yBAAyB;AAC1D,SAAQC,0CAA0C,QAAO,uCAAuC;AAChG,SACEC,mBAAmB,EACnBC,2CAA2C,QACtC,yBAAyB;AAEhC,SAAQC,sBAAsB,QAAO,cAAc;AACnD,SAAQC,2BAA2B,QAAO,qBAAqB;AAyB/D,OAAO,eAAeC,sBAAsBC,MAK3C;IACC,MAAMC,SACJD,OAAOE,UAAU,CAACC,IAAI,KAAK,WACvBL,4BAA4BE,OAAOC,MAAM,EAAE;QACzCG,OAAO;QACPC,QAAQ;IACV,KACA;WAAIZ,mBAAmBO,OAAOC,MAAM;KAAE;IAC5C,IAAIA,OAAOK,MAAM,KAAK,GAAG,MAAM,IAAIT;IAEnC,MAAMU,OAAOP,OAAOE,UAAU,CAACC,IAAI;IACnC,MAAMK,YAAYR,OAAOE,UAAU,CAACC,IAAI,KAAK,WAAW,OAAO;IAC/D,MAAMM,UACJT,OAAOE,UAAU,CAACC,IAAI,KAAK,WACvB,MAAMR,oBAAoB;QACxBe,aAAaV,OAAOE,UAAU,CAACQ,WAAW;QAC1CN,OAAO;QACPO,qBAAqBX,OAAOE,UAAU,CAACS,mBAAmB;QAC1DV;QACAW,kBAAkBZ,OAAOY,gBAAgB,IAAI;IAC/C,KACAZ,OAAOE,UAAU,CAACC,IAAI,KAAK,cACzB,MAAMT,2CAA2C;QAC/CmB,kBAAkBb,OAAOE,UAAU,CAACW,gBAAgB;QACpDH,aAAaV,OAAOE,UAAU,CAACQ,WAAW;QAC1CT;QACAW,kBAAkBZ,OAAOY,gBAAgB,IAAI;QAC7CJ,WAAW;IACb,KACA,MAAMZ,4CAA4C;QAChDkB,mBAAmBd,OAAOE,UAAU,CAACY,iBAAiB;QACtDb;QACAW,kBAAkBZ,OAAOY,gBAAgB,IAAI;IAC/C;IACR,MAAM,EAACG,OAAOC,YAAY,EAAC,GAAG,MAAMhB,OAAOiB,IAAI,CAACC,sBAAsB,CAAC;QACrEC,iBAAiBV,QAAQW,EAAE;QAC3BV,aAAaD,QAAQC,WAAW;QAChCN,OAAOK,QAAQL,KAAK;QACpBH,QAAQQ,QAAQR,MAAM;QACtBO;IACF;IAEA,OAAO;QAACC;QAASO;QAAcT;QAAMC;IAAS;AAChD"}
|