@kici-dev/orchestrator 0.5.0 → 0.6.1
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/dist/__test-helpers__/fake-scaler-state-store.d.ts +24 -0
- package/dist/__test-helpers__/mock-db-query.d.ts +130 -0
- package/dist/__test-helpers__/mock-db.d.ts +23 -0
- package/dist/agent/dispatcher.d.ts +81 -13
- package/dist/agent/host-roster.d.ts +37 -5
- package/dist/agent/registry.d.ts +2 -0
- package/dist/app.d.ts +33 -6
- package/dist/approvals/apply-decision.d.ts +67 -19
- package/dist/approvals/held-run-release-wiring.d.ts +45 -0
- package/dist/approvals/independent-wiring.d.ts +55 -0
- package/dist/approvals/resume-router.d.ts +9 -0
- package/dist/approvals/triggerer-subject.d.ts +37 -0
- package/dist/cache/dep-cache.d.ts +54 -10
- package/dist/cache/global-eval-round-cache.d.ts +7 -0
- package/dist/cli/api-client.d.ts +18 -0
- package/dist/cli/commands/cluster-settings.d.ts +11 -0
- package/dist/cli/commands/diagnose.d.ts +6 -0
- package/dist/cli/commands/held-run.d.ts +58 -0
- package/dist/cli/commands/trust-policy.d.ts +117 -12
- package/dist/cli/kici-admin.d.ts +10 -0
- package/dist/cli.js +2272 -1025
- package/dist/cluster/cluster-identity.d.ts +3 -3
- package/dist/cluster/cluster-settings-reader.d.ts +7 -1
- package/dist/cluster/coordinator.d.ts +44 -0
- package/dist/cluster/index.d.ts +4 -0
- package/dist/cluster/peer-handler.d.ts +19 -0
- package/dist/cluster/peer-registry.d.ts +17 -0
- package/dist/cluster/plan-headroom-store.d.ts +26 -0
- package/dist/cluster/worker-eviction.d.ts +69 -0
- package/dist/cold-store/load-event-log-range.d.ts +10 -3
- package/dist/cold-store/load-secret-audit-log-range.d.ts +4 -3
- package/dist/cold-store/tables/secret-audit-log.d.ts +4 -3
- package/dist/config.d.ts +21 -21
- package/dist/contexts/held-runs.d.ts +123 -16
- package/dist/contexts/protection/aggregate.d.ts +3 -2
- package/dist/contexts/protection/branch-gate.d.ts +15 -0
- package/dist/contexts/protection/concurrency-gate.d.ts +8 -2
- package/dist/contexts/protection/pipeline.d.ts +18 -0
- package/dist/contexts/release-queued-holds.d.ts +35 -0
- package/dist/dashboard/attestation-filters.d.ts +8 -0
- package/dist/dashboard/handler.d.ts +6 -24
- package/dist/db/migrations/116_org_plan_headroom.d.ts +4 -0
- package/dist/db/migrations/117_invoke_gate_proxy_jobs.d.ts +4 -0
- package/dist/db/migrations/118_invoke_gate_scheduling.d.ts +4 -0
- package/dist/db/migrations/119_scaler_ha_ownership.d.ts +4 -0
- package/dist/db/migrations/120_cluster_settings_scaler_reaper.d.ts +4 -0
- package/dist/db/migrations/121_org_settings_allow_untrusted_dockerfile_builds.d.ts +19 -0
- package/dist/db/migrations/122_host_roster_scaler_managed.d.ts +25 -0
- package/dist/db/migrations/123_workflow_registrations_default_branch.d.ts +24 -0
- package/dist/db/migrations/124_cluster_settings_provision_backoff.d.ts +4 -0
- package/dist/db/migrations/125_org_trust_directory.d.ts +30 -0
- package/dist/db/migrations/126_held_runs_posted_pending_check.d.ts +28 -0
- package/dist/db/migrations/127_org_trust_policy_approval_expiry_seconds.d.ts +26 -0
- package/dist/db/migrations/128_execution_runs_global_eval_round.d.ts +23 -0
- package/dist/db/migrations/129_execution_runs_dispatch_routing_key.d.ts +24 -0
- package/dist/db/migrations/130_scaler_provision_outcomes.d.ts +32 -0
- package/dist/db/types.d.ts +368 -10
- package/dist/entry-helpers.d.ts +28 -0
- package/dist/events/event-emitter.d.ts +22 -0
- package/dist/events/event-router.d.ts +87 -1
- package/dist/events/types.d.ts +2 -23
- package/dist/git/credential-broker.d.ts +94 -0
- package/dist/git/inherited-secret.d.ts +24 -0
- package/dist/git/installation-token.d.ts +38 -0
- package/dist/git/job-context.d.ts +13 -0
- package/dist/index.js +324 -84
- package/dist/metrics/agent-metrics-aggregator.d.ts +2 -2
- package/dist/metrics/prometheus.d.ts +111 -3
- package/dist/oidc/oidc-mint-registration.d.ts +7 -3
- package/dist/orchestrator-core.d.ts +79 -23
- package/dist/pipeline/direct-ingress-deps.d.ts +100 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +302 -31
- package/dist/pipeline/global-eval-round.d.ts +14 -0
- package/dist/pipeline/internal-event-pipeline.d.ts +140 -0
- package/dist/pipeline/invoke-gate.d.ts +164 -0
- package/dist/pipeline/needs-scheduler.d.ts +13 -0
- package/dist/pipeline/process-webhook.d.ts +119 -31
- package/dist/pipeline/processor.d.ts +226 -21
- package/dist/pipeline/rerun.d.ts +21 -0
- package/dist/pipeline/resume-workflow.d.ts +40 -7
- package/dist/pipeline/route-or-dispatch-jobs.d.ts +9 -0
- package/dist/pipeline/security-hold-check.d.ts +217 -0
- package/dist/pipeline/undispatched-hold-checks.d.ts +23 -0
- package/dist/policy/dashboard-write-policy-listener.d.ts +62 -0
- package/dist/policy/dashboard-write-policy.d.ts +14 -0
- package/dist/provider-registry.d.ts +10 -2
- package/dist/providers/github/check-status-poster.d.ts +11 -0
- package/dist/providers/github/index.d.ts +0 -2
- package/dist/providers/github/normalizer.d.ts +3 -2
- package/dist/providers/local/normalizer.d.ts +3 -5
- package/dist/providers/universal-git/index.d.ts +11 -2
- package/dist/queue/job-queue.d.ts +43 -1
- package/dist/registration/extractor.d.ts +3 -3
- package/dist/registration/registration-index.d.ts +7 -0
- package/dist/registration/registration-store.d.ts +22 -0
- package/dist/reporting/check-run-reporter.d.ts +94 -1
- package/dist/reporting/check-run-summary.d.ts +25 -1
- package/dist/reporting/execution-tracker.d.ts +207 -2
- package/dist/reporting/run-aggregator.d.ts +4 -14
- package/dist/reporting/step-display-order.d.ts +43 -0
- package/dist/routes/admin-held-runs.d.ts +142 -0
- package/dist/routes/admin-trust-policy.d.ts +34 -6
- package/dist/routes/admin.d.ts +9 -0
- package/dist/scaler/backend-factory.d.ts +55 -0
- package/dist/scaler/bare-metal-backend.d.ts +43 -5
- package/dist/scaler/claim-store.d.ts +119 -0
- package/dist/scaler/config.d.ts +4 -0
- package/dist/scaler/container-backend.d.ts +18 -4
- package/dist/scaler/container-routing.d.ts +23 -0
- package/dist/scaler/container-spawn.d.ts +28 -0
- package/dist/scaler/event-backend.d.ts +124 -0
- package/dist/scaler/event-provision-reaper.d.ts +350 -0
- package/dist/scaler/failure-tracker.d.ts +1 -1
- package/dist/scaler/firecracker-backend.d.ts +33 -6
- package/dist/scaler/index.d.ts +11 -1
- package/dist/scaler/label-matcher.d.ts +4 -3
- package/dist/scaler/manager.d.ts +899 -72
- package/dist/scaler/resolve-container-auth.d.ts +45 -0
- package/dist/scaler/scaler-events.d.ts +11 -0
- package/dist/scaler/scaler-state-store.d.ts +294 -10
- package/dist/scaler/types.d.ts +131 -13
- package/dist/scaler/warm-pool.d.ts +132 -29
- package/dist/security/comment-handler.d.ts +50 -11
- package/dist/security/identity-link.d.ts +60 -0
- package/dist/security/lock-source.d.ts +11 -13
- package/dist/security/reduced-privilege-note.d.ts +59 -0
- package/dist/security/trust-directory-store.d.ts +233 -0
- package/dist/security/trust-policy-gate.d.ts +94 -59
- package/dist/security/trust-policy-store.d.ts +3 -0
- package/dist/security/trust-resolver.d.ts +14 -98
- package/dist/server.d.ts +11 -1
- package/dist/server.js +17684 -7351
- package/dist/stale-detector/gate-deadline-detector.d.ts +52 -0
- package/dist/stale-detector/stale-run-detector.d.ts +61 -3
- package/dist/standalone.js +24238 -13822
- package/dist/worker/in-memory-job-queue.d.ts +1 -1
- package/dist/ws/agent-handler.d.ts +28 -4
- package/dist/ws/dashboard-context-handler.d.ts +28 -4
- package/dist/ws/dashboard-global-workflows-handler.d.ts +6 -0
- package/dist/ws/failure-messages.d.ts +2 -0
- package/dist/ws/git-credential-relay.d.ts +39 -0
- package/dist/ws/oidc-token-relay.d.ts +6 -11
- package/dist/ws/platform-client.d.ts +54 -9
- package/installer-image-digests.json +3 -3
- package/package.json +24 -19
- package/sbom.spdx.json +864 -822
- package/dist/approvals/team-membership-lookup.d.ts +0 -13
- package/dist/providers/github/contributor-resolver.d.ts +0 -30
- package/dist/security/contributor-cache.d.ts +0 -83
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a job's container spec into something a runtime can pull with.
|
|
3
|
+
*
|
|
4
|
+
* The lock carries credential REFERENCES (`<context>:<secret-name>`), never
|
|
5
|
+
* material — so somebody has to turn those into an authconfig, and three call
|
|
6
|
+
* sites need the identical answer: the agent's container sandbox, the container
|
|
7
|
+
* scaler backend, and the bare-metal backend in container mode. One resolver
|
|
8
|
+
* keeps them from drifting into three subtly different readings of the same
|
|
9
|
+
* lock field.
|
|
10
|
+
*/
|
|
11
|
+
import type { LockJob } from '@kici-dev/engine';
|
|
12
|
+
import type { ResolvedContainerSpawn } from './types.js';
|
|
13
|
+
export type { ResolvedContainerSpawn };
|
|
14
|
+
/** The `container` field as the lock carries it. */
|
|
15
|
+
type LockContainer = LockJob['container'];
|
|
16
|
+
export interface ResolveContainerSpawnDeps {
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a qualified `<context>:<secret-name>` reference, or `undefined`
|
|
19
|
+
* when no such secret exists.
|
|
20
|
+
*/
|
|
21
|
+
resolveSecret: (qualifiedRef: string) => Promise<string | undefined>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve just the registry credentials for a job's container.
|
|
25
|
+
*
|
|
26
|
+
* Split out of {@link resolveContainerSpawn} because the two consumers diverge
|
|
27
|
+
* for a dockerfile build: there is no image for the SCALER to spawn an agent
|
|
28
|
+
* from, but the AGENT still needs credentials to pull the Dockerfile's own
|
|
29
|
+
* `FROM` base.
|
|
30
|
+
*
|
|
31
|
+
* Returns `undefined` when the job declared no credentials at all.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveContainerRegistryAuth(container: LockContainer, deps: ResolveContainerSpawnDeps): Promise<NonNullable<ResolvedContainerSpawn['authconfig']> | undefined>;
|
|
34
|
+
/**
|
|
35
|
+
* Turn a lock `container` field into an image plus, when the job declared
|
|
36
|
+
* credentials, an authconfig for the registry that image lives in.
|
|
37
|
+
*
|
|
38
|
+
* Returns `undefined` for a job with no container at all — and for a job that
|
|
39
|
+
* BUILDS its image, because the scaler cannot spawn an agent from an image that
|
|
40
|
+
* does not exist yet. That job is run by an ordinary agent, which nests the
|
|
41
|
+
* container after it has cloned and built. So a caller can treat "no container",
|
|
42
|
+
* "container without auth" and "container built from a dockerfile" distinctly.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveContainerSpawn(container: LockContainer, deps: ResolveContainerSpawnDeps): Promise<ResolvedContainerSpawn | undefined>;
|
|
45
|
+
//# sourceMappingURL=resolve-container-auth.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator-side entry point for the event scaler's event vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* The schemas and constants themselves live in `@kici-dev/engine` so the SDK —
|
|
5
|
+
* the only KiCI package a workflow file may import — shares one source of truth
|
|
6
|
+
* with the emitting side. This module re-exports them (plus the reserved
|
|
7
|
+
* prefix) so orchestrator call sites keep a single local import for the whole
|
|
8
|
+
* vocabulary.
|
|
9
|
+
*/
|
|
10
|
+
export { KICI_EVENT_NAME_PREFIX, SCALER_EVENT_NAMES, ScaleDownReason, ScalerScaleUpPayload, ScalerScaleDownPayload, } from '@kici-dev/engine';
|
|
11
|
+
//# sourceMappingURL=scaler-events.d.ts.map
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
2
|
import type { Database } from '../db/types.js';
|
|
3
3
|
import type { ScalerEvent } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Snapshot of a spawning-agent record. Mirrors the row shape in
|
|
6
6
|
* `scaler_spawning_agents`.
|
|
7
|
+
*
|
|
8
|
+
* The ownership and self-describing fields are optional: a row written before a
|
|
9
|
+
* coordinator instance id was known carries none of them, and an absent
|
|
10
|
+
* `ownerInstanceId` means "unknown owner", never "owned by someone else".
|
|
7
11
|
*/
|
|
8
12
|
export interface SpawningAgentSnapshot {
|
|
9
13
|
agentId: string;
|
|
@@ -13,6 +17,82 @@ export interface SpawningAgentSnapshot {
|
|
|
13
17
|
jobId?: string;
|
|
14
18
|
boundJobId?: string;
|
|
15
19
|
spawnedAt: Date;
|
|
20
|
+
/** The coordinator instance that spawned the agent. */
|
|
21
|
+
ownerInstanceId?: string;
|
|
22
|
+
/** The coordinator the agent actually reached when it registered. */
|
|
23
|
+
adoptedBy?: string;
|
|
24
|
+
/** When the adopting coordinator claimed the agent. */
|
|
25
|
+
adoptedAt?: Date;
|
|
26
|
+
/** The scaler's mandatory labels, copied onto the row at spawn time. */
|
|
27
|
+
mandatoryLabels?: string[];
|
|
28
|
+
/** The scaler's provisioning targets, copied onto the row at spawn time. */
|
|
29
|
+
provisioningTargets?: string[];
|
|
30
|
+
/** The scaler's roles, copied onto the row at spawn time. */
|
|
31
|
+
roles?: string[];
|
|
32
|
+
/** The scaler backend that spawned the agent. */
|
|
33
|
+
backendType?: string;
|
|
34
|
+
}
|
|
35
|
+
/** A pending provisioning claim, as written by the event scaler's claim store. */
|
|
36
|
+
export interface PendingClaimRow {
|
|
37
|
+
claimHash: string;
|
|
38
|
+
claimPrefix: string;
|
|
39
|
+
agentId: string;
|
|
40
|
+
scalerName: string;
|
|
41
|
+
labels: string[];
|
|
42
|
+
agentTokenTtlMs: number;
|
|
43
|
+
orchestratorUrl: string;
|
|
44
|
+
expiresAt: Date;
|
|
45
|
+
}
|
|
46
|
+
/** What a successful `redeemClaim` returns. */
|
|
47
|
+
export interface RedeemedClaim {
|
|
48
|
+
agentId: string;
|
|
49
|
+
labels: string[];
|
|
50
|
+
agentTokenTtlMs: number;
|
|
51
|
+
orchestratorUrl: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The critical section `withScalerCapLock` hands its caller: the scaler's
|
|
55
|
+
* cluster-wide spawn count, plus the one write that changes it.
|
|
56
|
+
*/
|
|
57
|
+
export interface ScalerCapSlot {
|
|
58
|
+
/**
|
|
59
|
+
* Live event-scaler spawn rows for this scaler across every coordinator.
|
|
60
|
+
* Read inside the advisory-locked transaction, so no other holder of the
|
|
61
|
+
* lock can change it while the caller decides.
|
|
62
|
+
*/
|
|
63
|
+
clusterActiveCount: number;
|
|
64
|
+
/**
|
|
65
|
+
* Claim a slot by writing the spawn row in the same transaction the count
|
|
66
|
+
* was read in. Counting without claiming here bounds nothing: the spawn row
|
|
67
|
+
* is otherwise written well after the check, so every coordinator arriving
|
|
68
|
+
* in that window reads the same pre-spawn count and admits.
|
|
69
|
+
*/
|
|
70
|
+
reserve(snapshot: SpawningAgentSnapshot): Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
/** A row the event-provision reaper may need to tear down. */
|
|
73
|
+
export interface ReapCandidate {
|
|
74
|
+
agentId: string;
|
|
75
|
+
scalerName: string;
|
|
76
|
+
provisioningTargets: string[];
|
|
77
|
+
ownerInstanceId?: string;
|
|
78
|
+
adoptedBy?: string;
|
|
79
|
+
/**
|
|
80
|
+
* When the provision was requested. The reaper ages every row on this and
|
|
81
|
+
* never on `adopted_at`: a coordinator that re-adopts its own agent refreshes
|
|
82
|
+
* `adopted_at`, so a restart loop on the adopter would keep resetting the
|
|
83
|
+
* clock on its own stranded provision and the row would never age out.
|
|
84
|
+
*/
|
|
85
|
+
spawnedAt: Date;
|
|
86
|
+
/**
|
|
87
|
+
* The run this provision was spawned for, when it was job-bound. Carried so a
|
|
88
|
+
* teardown for a provision that never registered can be attributed back to the
|
|
89
|
+
* job that is still waiting on it — the reaper is leader-gated, so the
|
|
90
|
+
* coordinator that condemns the row is often not the one that spawned it and
|
|
91
|
+
* has no in-memory spawning entry to read the identity from.
|
|
92
|
+
*/
|
|
93
|
+
runId?: string;
|
|
94
|
+
/** The queued job this provision was spawned for. Absent for a warm fill. */
|
|
95
|
+
boundJobId?: string;
|
|
16
96
|
}
|
|
17
97
|
/**
|
|
18
98
|
* Snapshot of an agent-job correlation. Mirrors the row shape in
|
|
@@ -32,13 +112,18 @@ export interface ReservationSnapshot {
|
|
|
32
112
|
scalerName: string;
|
|
33
113
|
cpus: number;
|
|
34
114
|
memBytes: number;
|
|
115
|
+
/**
|
|
116
|
+
* The coordinator instance holding the reservation. Absent means "unknown
|
|
117
|
+
* owner", never "held by someone else".
|
|
118
|
+
*/
|
|
119
|
+
ownerInstanceId?: string;
|
|
35
120
|
}
|
|
36
121
|
/**
|
|
37
122
|
* DB persistence for `ScalerManager` HA-critical state.
|
|
38
123
|
*
|
|
39
|
-
* Backed by
|
|
40
|
-
* `scaler_reservations` — so a Raft leader switch /
|
|
41
|
-
* longer:
|
|
124
|
+
* Backed by four tables — `scaler_spawning_agents`, `scaler_agent_jobs`,
|
|
125
|
+
* `scaler_reservations`, `scaler_pending_claims` — so a Raft leader switch /
|
|
126
|
+
* coord crash no longer:
|
|
42
127
|
*
|
|
43
128
|
* - orphans an agent that is mid-spawn (lost `boundJobId` → eager
|
|
44
129
|
* dispatch silently downgraded to a generic queue drain),
|
|
@@ -59,26 +144,225 @@ export interface ReservationSnapshot {
|
|
|
59
144
|
* correlation are observability, not correctness. A coord crash before
|
|
60
145
|
* `correlateAgentToJob()` runs accepts losing those events (see the
|
|
61
146
|
* wishlist for the rationale).
|
|
147
|
+
*
|
|
148
|
+
* Ownership columns (`owner_instance_id`, `adopted_by`) let several
|
|
149
|
+
* coordinators behind one shared endpoint divide the same tables between them.
|
|
150
|
+
* A NULL owner means the owner is unknown — it never means "owned by another
|
|
151
|
+
* instance", so no query may read a NULL owner as somebody else's row.
|
|
152
|
+
*/
|
|
153
|
+
/**
|
|
154
|
+
* Postgres `lock_not_available`. Raised when the `lock_timeout` the cap
|
|
155
|
+
* transaction sets expires with the advisory lock still held elsewhere —
|
|
156
|
+
* a contended lock on a healthy database, which reads very differently from a
|
|
157
|
+
* database that could not be reached at all.
|
|
62
158
|
*/
|
|
159
|
+
export declare const PG_LOCK_NOT_AVAILABLE = "55P03";
|
|
63
160
|
export declare class ScalerStateStore {
|
|
64
161
|
private readonly db;
|
|
65
|
-
|
|
162
|
+
private readonly lockWaitMs?;
|
|
163
|
+
/**
|
|
164
|
+
* `lockWaitMs` bounds how long `withScalerCapLock` waits to acquire the
|
|
165
|
+
* advisory lock. It is the orchestrator's existing `dbPoolAcquireTimeoutMs`
|
|
166
|
+
* — the same answer to "how long may a caller block waiting to get hold of a
|
|
167
|
+
* database resource before we fail" — not a timeout of its own. Omitted, the
|
|
168
|
+
* wait is unbounded except by `statement_timeout`, which is the behaviour a
|
|
169
|
+
* store built without it has always had.
|
|
170
|
+
*/
|
|
171
|
+
constructor(db: Kysely<Database>, lockWaitMs?: number | undefined);
|
|
66
172
|
upsertSpawningAgent(snapshot: SpawningAgentSnapshot): Promise<void>;
|
|
173
|
+
/**
|
|
174
|
+
* Run a cap check plus its slot claim for one scaler under a cluster-wide
|
|
175
|
+
* lock.
|
|
176
|
+
*
|
|
177
|
+
* `runWithReservationLock` in the manager is an in-process promise chain:
|
|
178
|
+
* correct within an instance, blind across them. For an event scaler that
|
|
179
|
+
* means N coordinators each provision up to `maxAgents` cloud instances, so
|
|
180
|
+
* the number an operator writes is not the number they are billed for. The
|
|
181
|
+
* advisory lock is transaction-scoped, so it releases on commit or rollback
|
|
182
|
+
* with no unlock path to leak.
|
|
183
|
+
*
|
|
184
|
+
* The count and the claim share the transaction on purpose — see
|
|
185
|
+
* `ScalerCapSlot.reserve`. `fn` therefore runs inside an open transaction:
|
|
186
|
+
* keep it short, and never await anything outside the database in it.
|
|
187
|
+
*
|
|
188
|
+
* READ COMMITTED is pinned rather than inherited. The lock is taken first
|
|
189
|
+
* and the count read second, so each statement needs its own snapshot for
|
|
190
|
+
* the second holder to see the first one's committed row. Under REPEATABLE
|
|
191
|
+
* READ the snapshot is taken at the FIRST statement — before the lock is
|
|
192
|
+
* granted — so both coordinators would read the same pre-claim count and
|
|
193
|
+
* both admit, reintroducing the exact defect this exists to close. Pinning
|
|
194
|
+
* it means a server-side `default_transaction_isolation` change cannot do
|
|
195
|
+
* that silently.
|
|
196
|
+
*
|
|
197
|
+
* A caller that cannot get the lock within `lockWaitMs` fails rather than
|
|
198
|
+
* queuing: the manager holds its process-wide reservation lock across this
|
|
199
|
+
* call, so an unbounded wait here stalls every other backend's scale
|
|
200
|
+
* request too.
|
|
201
|
+
*/
|
|
202
|
+
withScalerCapLock<T>(scalerName: string, fn: (slot: ScalerCapSlot) => Promise<T> | T): Promise<T>;
|
|
67
203
|
deleteSpawningAgent(agentId: string): Promise<void>;
|
|
204
|
+
/**
|
|
205
|
+
* Delete a spawning-agent row only while no instance has adopted it.
|
|
206
|
+
*
|
|
207
|
+
* The spawning instance is never told that a peer adopted its agent, so its
|
|
208
|
+
* in-memory entry survives adoption and its stale-entry prune fires five
|
|
209
|
+
* minutes later. An unconditional delete there would drop the row whose
|
|
210
|
+
* `adopted_by` is the only durable record of a live provision — and an
|
|
211
|
+
* adopted event agent legitimately runs for hours, so the reaper would find
|
|
212
|
+
* nothing to tear down if the adopting instance then died. The
|
|
213
|
+
* `adopted_by IS NULL` predicate keeps the prune to rows nobody claimed.
|
|
214
|
+
*/
|
|
215
|
+
deleteUnadoptedSpawningAgent(agentId: string): Promise<void>;
|
|
216
|
+
/**
|
|
217
|
+
* The instance that adopted a provision, or null while nobody has.
|
|
218
|
+
*
|
|
219
|
+
* The counterpart read to `deleteUnadoptedSpawningAgent`'s predicate, for the
|
|
220
|
+
* caller that must not act rather than must not delete: the spawning instance
|
|
221
|
+
* is never told that a peer adopted its agent, so a stale in-memory entry is
|
|
222
|
+
* no evidence the provision failed.
|
|
223
|
+
*
|
|
224
|
+
* Reads the LIVE spawn row first and the durable outcome second, because the
|
|
225
|
+
* spawn row is deleted on teardown and its absence is ambiguous — it is both
|
|
226
|
+
* "never adopted" and "adopted, then torn down". The outcome row survives the
|
|
227
|
+
* delete and answers the second case positively.
|
|
228
|
+
*
|
|
229
|
+
* A provision with neither row still answers null, and the prune treats that
|
|
230
|
+
* as "no evidence" and reports — which is what a single coordinator wants and
|
|
231
|
+
* what keeps every coordinator backing off on a real provisioning outage. See
|
|
232
|
+
* `ScalerManager.reportPrunedProvisionFailure`.
|
|
233
|
+
*/
|
|
234
|
+
provisionAdopter(agentId: string): Promise<string | null>;
|
|
235
|
+
/**
|
|
236
|
+
* Every spawning-agent row in the table, whoever owns it. Not for recovery —
|
|
237
|
+
* hydrating a peer's in-flight spawns as our own lets the spawn-timeout
|
|
238
|
+
* reaper destroy agents that peer is still waiting on. Recovery uses
|
|
239
|
+
* `listSpawningAgentsForOwner`.
|
|
240
|
+
*/
|
|
68
241
|
listSpawningAgents(): Promise<SpawningAgentSnapshot[]>;
|
|
69
242
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
243
|
+
* Claim ownership of a spawning agent that registered on this instance.
|
|
244
|
+
* Conditional on the row being unclaimed **or already claimed by this same
|
|
245
|
+
* instance**, so exactly one instance adopts even if the agent flaps between
|
|
246
|
+
* instances behind a load balancer, while a re-registration on the adopter
|
|
247
|
+
* itself still resolves. Without the second arm a restart is a silent leak:
|
|
248
|
+
* recovery rehydrates the row, the stale-spawn prune drops the in-memory
|
|
249
|
+
* entry but spares the row, and the still-live agent's next registration
|
|
250
|
+
* finds neither — it reads as a static agent, so no `scale-down` is ever
|
|
251
|
+
* emitted and the reaper spares it because the agent is registered and its
|
|
252
|
+
* adopter is alive. Gated on `backend_type = 'event'`: a local-backend
|
|
253
|
+
* agent's compute is pinned to another host, so adopting its bookkeeping
|
|
254
|
+
* would be a lie.
|
|
255
|
+
*
|
|
256
|
+
* The adoption is also recorded in `scaler_provision_outcomes`, in the SAME
|
|
257
|
+
* transaction as the stamp. The spawn row is deleted on teardown, so its
|
|
258
|
+
* `adopted_by` is not a durable answer to "was this provision ever adopted?"
|
|
259
|
+
* — and the stale-spawn prune needs exactly that answer to tell a dead
|
|
260
|
+
* external provision from a healthy one it was not told about. Writing
|
|
261
|
+
* it here rather than at the two call sites is deliberate: this method is the
|
|
262
|
+
* single writer of `adopted_by`, so no adoption path can miss the record.
|
|
263
|
+
* Splitting the two writes would let a crash leave an adopted row whose
|
|
264
|
+
* adoption the prune cannot see, which is the very ambiguity being removed.
|
|
265
|
+
*/
|
|
266
|
+
adoptSpawningAgent(agentId: string, instanceId: string): Promise<SpawningAgentSnapshot | null>;
|
|
267
|
+
/**
|
|
268
|
+
* Every spawning-agent row this instance owns. Scopes recovery so an instance
|
|
269
|
+
* rehydrates only its own bookkeeping. A row with a NULL `owner_instance_id`
|
|
270
|
+
* has an unknown owner and is deliberately not returned to anybody.
|
|
271
|
+
*/
|
|
272
|
+
listSpawningAgentsForOwner(instanceId: string): Promise<SpawningAgentSnapshot[]>;
|
|
273
|
+
/**
|
|
274
|
+
* Every event row that is either past its spawn deadline without ever being
|
|
275
|
+
* adopted, or adopted by some instance. The reaper decides which to tear down;
|
|
276
|
+
* this only narrows the scan. Deliberately NOT a blanket `spawned_at < cutoff`
|
|
277
|
+
* delete: an adopted event agent legitimately runs for hours.
|
|
74
278
|
*/
|
|
75
|
-
|
|
279
|
+
listReapCandidates(spawnCutoff: Date): Promise<ReapCandidate[]>;
|
|
76
280
|
upsertAgentJob(snapshot: AgentJobCorrelationSnapshot): Promise<void>;
|
|
77
281
|
deleteAgentJob(agentId: string): Promise<void>;
|
|
78
282
|
listAgentJobs(): Promise<AgentJobCorrelationSnapshot[]>;
|
|
79
283
|
upsertReservation(snapshot: ReservationSnapshot): Promise<void>;
|
|
80
284
|
deleteReservation(agentId: string): Promise<void>;
|
|
285
|
+
/**
|
|
286
|
+
* Every reservation row in the table, whoever holds it. Not for recovery —
|
|
287
|
+
* counting a peer's reservations against our own caps double-books the
|
|
288
|
+
* cluster. Recovery uses `listReservationsForOwner`.
|
|
289
|
+
*/
|
|
81
290
|
listReservations(): Promise<ReservationSnapshot[]>;
|
|
291
|
+
/**
|
|
292
|
+
* Every reservation this instance holds. Scopes recovery so an instance
|
|
293
|
+
* rehydrates only its own usage. A row with a NULL `owner_instance_id` has an
|
|
294
|
+
* unknown owner and is deliberately not returned to anybody.
|
|
295
|
+
*/
|
|
296
|
+
listReservationsForOwner(instanceId: string): Promise<ReservationSnapshot[]>;
|
|
297
|
+
registerClaim(row: PendingClaimRow): Promise<void>;
|
|
298
|
+
/**
|
|
299
|
+
* Consume a claim and return its spec, or null when the code is unknown,
|
|
300
|
+
* already consumed, or expired. The UPDATE is the consumption: it commits
|
|
301
|
+
* before any mint is attempted, so two concurrent claims of one code — on the
|
|
302
|
+
* same instance or on different ones — can never both mint.
|
|
303
|
+
*/
|
|
304
|
+
redeemClaim(claimHash: string): Promise<RedeemedClaim | null>;
|
|
305
|
+
/**
|
|
306
|
+
* Why a redeem failed. Called ONLY on the failure path, so the happy path
|
|
307
|
+
* stays one round trip.
|
|
308
|
+
*/
|
|
309
|
+
describeClaim(claimHash: string): Promise<{
|
|
310
|
+
consumed: boolean;
|
|
311
|
+
expired: boolean;
|
|
312
|
+
} | null>;
|
|
313
|
+
invalidateClaimsForAgent(agentId: string): Promise<void>;
|
|
314
|
+
/**
|
|
315
|
+
* Delete every claim whose `expires_at` passed before `cutoff`. Returns how
|
|
316
|
+
* many rows went.
|
|
317
|
+
*
|
|
318
|
+
* Nothing else removes a claim on a timer: `redeemClaim` only marks
|
|
319
|
+
* `consumed_at`, and `invalidateClaimsForAgent` runs on a teardown path a
|
|
320
|
+
* stranded provision never reaches. So a claim outlives its agent whenever a
|
|
321
|
+
* registration unwinds after the claim was written, or whenever the
|
|
322
|
+
* coordinator that would have torn the agent down crashed first — and the row
|
|
323
|
+
* then sits in the table forever. Purging on `expires_at` covers both,
|
|
324
|
+
* consumed or not, because an expired claim can never be redeemed again.
|
|
325
|
+
*
|
|
326
|
+
* The caller subtracts a retention grace from now, so `describeClaim` can
|
|
327
|
+
* still tell a late redeemer "expired" rather than "unknown code" for a while
|
|
328
|
+
* after the deadline. Backed by `idx_scaler_pending_claims_expires_at`.
|
|
329
|
+
*/
|
|
330
|
+
purgeExpiredClaims(cutoff: Date): Promise<number>;
|
|
331
|
+
/**
|
|
332
|
+
* Record the reaper's teardown verdict for one provision.
|
|
333
|
+
*
|
|
334
|
+
* Deliberately additive: it writes only the `condemned_*` columns. A
|
|
335
|
+
* `heartbeat-timeout` condemns a provision that WAS adopted, so clearing the
|
|
336
|
+
* adoption here would put the stale-spawn prune straight back to reporting a
|
|
337
|
+
* healthy provision as a failed one — the exact misattribution this table
|
|
338
|
+
* exists to remove.
|
|
339
|
+
*
|
|
340
|
+
* Called only once the teardown was actually delivered. An emit that reached
|
|
341
|
+
* nobody leaves the spawn row in place for the reaper to retry, so there is
|
|
342
|
+
* no verdict yet to record.
|
|
343
|
+
*
|
|
344
|
+
* The `condemned_*` half is the forensic side of the record and has no
|
|
345
|
+
* production reader: `provisionAdopter` answers from `adopted_by` alone, so
|
|
346
|
+
* the prune's verdict does not depend on it. It is written because the row
|
|
347
|
+
* is the only surviving account of what became of a provision once the spawn
|
|
348
|
+
* row is gone — which is what an operator investigating a torn-down
|
|
349
|
+
* provision, and the E2E that pins this behaviour, read it for.
|
|
350
|
+
*/
|
|
351
|
+
recordProvisionCondemned(agentId: string, scalerName: string, reason: string): Promise<void>;
|
|
352
|
+
/**
|
|
353
|
+
* Delete outcome rows that can no longer be asked about; returns rows deleted.
|
|
354
|
+
*
|
|
355
|
+
* Two predicates, and the second is the load-bearing one. The cutoff alone is
|
|
356
|
+
* not safe: recovery rehydrates an in-memory spawning entry from
|
|
357
|
+
* `scaler_spawning_agents`, and a rehydrated entry is immediately stale, so
|
|
358
|
+
* it asks about its provision on the very next prune however old the
|
|
359
|
+
* provision is. While the spawn row exists the outcome must stay. Once the
|
|
360
|
+
* spawn row is gone, nothing can rehydrate an entry for it, and the caller's
|
|
361
|
+
* retention floor covers the one prune window still in flight.
|
|
362
|
+
*
|
|
363
|
+
* Backed by `idx_scaler_provision_outcomes_updated_at`.
|
|
364
|
+
*/
|
|
365
|
+
purgeProvisionOutcomes(cutoff: Date): Promise<number>;
|
|
82
366
|
}
|
|
83
367
|
/**
|
|
84
368
|
* Aggregate event surface for "the scaler manager fully replayed its
|
package/dist/scaler/types.d.ts
CHANGED
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
* Provides the foundational types that all scaler backends, the ScalerManager,
|
|
5
5
|
* and the configuration layer depend on.
|
|
6
6
|
*/
|
|
7
|
+
import { ImagePullPolicy } from '@kici-dev/shared/container-runtime';
|
|
7
8
|
import { z } from 'zod';
|
|
8
9
|
import { ScalerEventType } from '@kici-dev/engine';
|
|
10
|
+
import type { ScaleDownReason } from './scaler-events.js';
|
|
9
11
|
import type { ResourceRequest, ResourceSpec, ScalerBackendType, ScalerPlatform } from '@kici-dev/engine';
|
|
10
12
|
export type { ResourceRequest, ResourceSpec } from '@kici-dev/engine';
|
|
11
13
|
export { ScalerEventType } from '@kici-dev/engine';
|
|
@@ -58,11 +60,43 @@ export interface EffectiveLimits {
|
|
|
58
60
|
* select the exact container a trigger produced instead of guessing among
|
|
59
61
|
* concurrent kici-managed containers. Absent for unbound spawns (warm pool).
|
|
60
62
|
*/
|
|
63
|
+
/**
|
|
64
|
+
* A job's container spec with its registry credentials already resolved.
|
|
65
|
+
*
|
|
66
|
+
* The lock carries credential REFERENCES; this is what a runtime can actually
|
|
67
|
+
* pull with. Produced by `resolveContainerSpawn`, consumed by the container
|
|
68
|
+
* sandbox and by both spawning backends.
|
|
69
|
+
*/
|
|
70
|
+
export interface ResolvedContainerSpawn {
|
|
71
|
+
image: string;
|
|
72
|
+
authconfig?: {
|
|
73
|
+
username: string;
|
|
74
|
+
password: string;
|
|
75
|
+
serveraddress: string;
|
|
76
|
+
};
|
|
77
|
+
env?: Record<string, string>;
|
|
78
|
+
}
|
|
61
79
|
export interface SpawnContext {
|
|
62
80
|
/** Execution job id the spawn is bound to. */
|
|
63
81
|
boundJobId?: string;
|
|
64
82
|
/** Execution run id the bound job belongs to. */
|
|
65
83
|
runId?: string;
|
|
84
|
+
/**
|
|
85
|
+
* The job's own container image plus resolved registry credentials, when the
|
|
86
|
+
* job declared one. Present means "spawn THIS image with the KiCI runtime
|
|
87
|
+
* injected" rather than the pool's fixed agent image.
|
|
88
|
+
*/
|
|
89
|
+
container?: ResolvedContainerSpawn;
|
|
90
|
+
/**
|
|
91
|
+
* Plain platform-taint tokens (`windows`, `macos`, `arm64`) for the pool this
|
|
92
|
+
* spawn belongs to, derived by `ScalerManager` from the same resolved
|
|
93
|
+
* platform its taint gate uses. A backend forwards them to
|
|
94
|
+
* `scalerAgentLabels()` so the agent registers carrying the very tokens the
|
|
95
|
+
* gate demands — without them a tainted pool spawns agents no job can be
|
|
96
|
+
* dispatched to. A backend must never derive them itself: the manager is the
|
|
97
|
+
* single source, and computing them twice is the defect this field closes.
|
|
98
|
+
*/
|
|
99
|
+
platformTaints?: readonly string[];
|
|
66
100
|
}
|
|
67
101
|
/**
|
|
68
102
|
* Network policy controlling RFC1918 and internet access for agents in this label set.
|
|
@@ -82,13 +116,12 @@ export interface NetworkPolicy {
|
|
|
82
116
|
* - `Always`: re-pull on every spawn. Set this on a label set that tracks a
|
|
83
117
|
* moving tag (e.g. `:latest`) or otherwise needs a fresh image each spawn.
|
|
84
118
|
* - `Never`: never pull; fail if the image is absent.
|
|
119
|
+
*
|
|
120
|
+
* Defined alongside `pullImageIfMissing` in `@kici-dev/shared` — the agent
|
|
121
|
+
* pulls job images through the same helper — and re-exported here so the scaler
|
|
122
|
+
* config schema and every operator-facing value stay exactly where they were.
|
|
85
123
|
*/
|
|
86
|
-
export
|
|
87
|
-
Always: "Always";
|
|
88
|
-
IfNotPresent: "IfNotPresent";
|
|
89
|
-
Never: "Never";
|
|
90
|
-
}>;
|
|
91
|
-
export type ImagePullPolicy = z.infer<typeof ImagePullPolicy>;
|
|
124
|
+
export { ImagePullPolicy };
|
|
92
125
|
/**
|
|
93
126
|
* Configuration for a single label-set mapping within a scaler backend.
|
|
94
127
|
* Maps an exact set of labels to the agent provisioning details.
|
|
@@ -196,6 +229,22 @@ export type ValidationResult = {
|
|
|
196
229
|
valid: false;
|
|
197
230
|
errors: string[];
|
|
198
231
|
};
|
|
232
|
+
/**
|
|
233
|
+
* Optional context passed to `ScalerBackend.destroy`. The event backend surfaces
|
|
234
|
+
* `reason` on its scale-down event; local backends ignore it.
|
|
235
|
+
*/
|
|
236
|
+
export interface ScalerDestroyContext {
|
|
237
|
+
/** Why the teardown was requested (idle, job-complete, drain, …). */
|
|
238
|
+
reason?: ScaleDownReason;
|
|
239
|
+
/**
|
|
240
|
+
* Where an event backend must deliver the teardown, overriding its live
|
|
241
|
+
* config. Set from the spawn record when the agent was adopted from another
|
|
242
|
+
* coordinator, so a teardown addresses the targets the provision was spawned
|
|
243
|
+
* with even if `provisioningTargets` has been edited since. Ignored by the
|
|
244
|
+
* local backends, which deliver nothing.
|
|
245
|
+
*/
|
|
246
|
+
targets?: string[];
|
|
247
|
+
}
|
|
199
248
|
/**
|
|
200
249
|
* Common interface for all scaler backends.
|
|
201
250
|
* Each backend manages a specific pool of agents for specific label sets.
|
|
@@ -209,8 +258,11 @@ export interface ScalerBackend {
|
|
|
209
258
|
readonly logsSource?: string;
|
|
210
259
|
/** Label sets this backend can provision */
|
|
211
260
|
readonly labelSets: LabelSetConfig[];
|
|
212
|
-
/**
|
|
213
|
-
|
|
261
|
+
/**
|
|
262
|
+
* Per-backend maximum agents. Updated by `reload` so a config change to
|
|
263
|
+
* `maxAgents` applies without an orchestrator restart.
|
|
264
|
+
*/
|
|
265
|
+
maxAgents: number;
|
|
214
266
|
/**
|
|
215
267
|
* Whether this backend spawns its agents on the orchestrator's own host.
|
|
216
268
|
* True for bare-metal and Firecracker (local processes / local VMs) and for
|
|
@@ -248,9 +300,46 @@ export interface ScalerBackend {
|
|
|
248
300
|
signal?: AbortSignal): Promise<ManagedAgent>;
|
|
249
301
|
/**
|
|
250
302
|
* Destroy a specific managed agent.
|
|
251
|
-
* Docker: docker rm -f; Bare-metal: SIGTERM -> SIGKILL
|
|
303
|
+
* Docker: docker rm -f; Bare-metal: SIGTERM -> SIGKILL.
|
|
304
|
+
*
|
|
305
|
+
* @param context - Optional teardown context. The event backend carries
|
|
306
|
+
* `reason` onto its `kici.scaler.scale-down` event so a teardown workflow
|
|
307
|
+
* (and the timeline) can distinguish an idle reap from a job-complete
|
|
308
|
+
* teardown or a spawn timeout. Local backends accept and ignore it.
|
|
252
309
|
*/
|
|
253
|
-
destroy(managedId: string): Promise<void>;
|
|
310
|
+
destroy(managedId: string, context?: ScalerDestroyContext): Promise<void>;
|
|
311
|
+
/**
|
|
312
|
+
* Reclaim the HOST-LOCAL compute of a managed agent this backend no longer
|
|
313
|
+
* tracks in memory, from whatever durable host state survived the loss.
|
|
314
|
+
*
|
|
315
|
+
* `destroy` is keyed off the in-memory agent map, so an orchestrator restart
|
|
316
|
+
* makes it a silent no-op while the VM or host process keeps running. This
|
|
317
|
+
* hook is the restart-surviving half: it reads the backend's own on-host
|
|
318
|
+
* artifacts for `managedId` and reclaims them.
|
|
319
|
+
*
|
|
320
|
+
* Two properties every implementation MUST hold:
|
|
321
|
+
*
|
|
322
|
+
* - **Host-local evidence only.** Reclaim nothing unless an artifact for
|
|
323
|
+
* exactly this `managedId` exists on THIS host — that artifact is the proof
|
|
324
|
+
* this backend spawned it. A coordinator must never be able to reach across
|
|
325
|
+
* and reap a peer's compute.
|
|
326
|
+
* - **Caller supplies the orphan verdict.** The hook force-reclaims a *live*
|
|
327
|
+
* instance, which is precisely what the liveness-driven orphan sweeps
|
|
328
|
+
* refuse to do on their own. Only call it where the agent is known to be
|
|
329
|
+
* unowned.
|
|
330
|
+
*
|
|
331
|
+
* Optional: a backend whose compute is not host-local — the event backend's
|
|
332
|
+
* customer cloud instance — cannot implement it at all. An implementation may
|
|
333
|
+
* also cover only part of its own backend, when the rest keeps nothing durable
|
|
334
|
+
* to read. Bare metal is that case: a container-mode agent carries
|
|
335
|
+
* `kici-agent-id` / `kici-scaler-name` labels on the host and is reclaimed,
|
|
336
|
+
* while a plain-process agent records its PID in the in-memory entry alone, so
|
|
337
|
+
* a restart loses it and the hook reports nothing to reclaim.
|
|
338
|
+
*
|
|
339
|
+
* @returns `true` when host-local state for `managedId` was found and
|
|
340
|
+
* reclaimed, `false` when there was nothing here to reclaim.
|
|
341
|
+
*/
|
|
342
|
+
reapUnowned?(managedId: string): Promise<boolean>;
|
|
254
343
|
/**
|
|
255
344
|
* Get the LogCapture for a managed agent (optional -- container and bare-metal backends
|
|
256
345
|
* support stdout capture. Firecracker handles log forwarding internally via file tailing
|
|
@@ -273,10 +362,23 @@ export interface ScalerBackend {
|
|
|
273
362
|
*/
|
|
274
363
|
ensureHostReady?(): Promise<void>;
|
|
275
364
|
/**
|
|
276
|
-
* Reload configuration (called on SIGHUP).
|
|
277
|
-
*
|
|
365
|
+
* Reload configuration (called on config reload / SIGHUP).
|
|
366
|
+
*
|
|
367
|
+
* `opts.maxAgents`, when present, replaces the population cap. `opts.entry`
|
|
368
|
+
* is the whole new config entry, for a backend that reads more than its
|
|
369
|
+
* label sets off it. On an invalid result NOTHING is applied.
|
|
370
|
+
*/
|
|
371
|
+
reload(labelSets: LabelSetConfig[], opts?: {
|
|
372
|
+
maxAgents?: number;
|
|
373
|
+
entry?: ScalerEntry;
|
|
374
|
+
}): ValidationResult;
|
|
375
|
+
/**
|
|
376
|
+
* The config entry this backend is currently serving, for backends that hold
|
|
377
|
+
* one. Read by the reload rollback so a rejected reload can restore it
|
|
378
|
+
* alongside `labelSets` and `maxAgents`. Undefined for backends that keep no
|
|
379
|
+
* entry (container, bare-metal, Firecracker all read only their label sets).
|
|
278
380
|
*/
|
|
279
|
-
|
|
381
|
+
readonly currentEntry?: ScalerEntry;
|
|
280
382
|
}
|
|
281
383
|
/**
|
|
282
384
|
* Parsed and validated scaler configuration from YAML.
|
|
@@ -438,6 +540,22 @@ export interface ScalerEntry {
|
|
|
438
540
|
* binaries. Default false.
|
|
439
541
|
*/
|
|
440
542
|
requireSudo?: boolean;
|
|
543
|
+
/**
|
|
544
|
+
* Workflow refs (e.g. `org/infra`) the reserved scale-up / scale-down events
|
|
545
|
+
* are delivered to. The customer's provisioning / teardown workflows
|
|
546
|
+
* subscribe with `kiciEvent()`. Required for a `type: event` scaler.
|
|
547
|
+
*/
|
|
548
|
+
provisioningTargets?: string[];
|
|
549
|
+
/**
|
|
550
|
+
* Seconds a pending provisioning claim code stays valid before it expires.
|
|
551
|
+
* @default 300
|
|
552
|
+
*/
|
|
553
|
+
claimTtlSeconds?: number;
|
|
554
|
+
/**
|
|
555
|
+
* Seconds the ephemeral agent token minted for a claimed provision stays
|
|
556
|
+
* valid. @default 600
|
|
557
|
+
*/
|
|
558
|
+
agentTokenTtlSeconds?: number;
|
|
441
559
|
}
|
|
442
560
|
/**
|
|
443
561
|
* Warm pool configuration for pre-provisioned idle agents.
|