@namzu/sandbox 26.0.0 → 27.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +20 -7
  3. package/dist/backends/firecracker/protocol.d.ts +46 -0
  4. package/dist/backends/firecracker/protocol.d.ts.map +1 -1
  5. package/dist/backends/firecracker/protocol.js +31 -0
  6. package/dist/backends/firecracker/protocol.js.map +1 -1
  7. package/dist/backends/firecracker/transport.d.ts +15 -1
  8. package/dist/backends/firecracker/transport.d.ts.map +1 -1
  9. package/dist/backends/firecracker/transport.js +16 -0
  10. package/dist/backends/firecracker/transport.js.map +1 -1
  11. package/dist/backends/kubernetes/allocation.d.ts +115 -0
  12. package/dist/backends/kubernetes/allocation.d.ts.map +1 -0
  13. package/dist/backends/kubernetes/allocation.js +130 -0
  14. package/dist/backends/kubernetes/allocation.js.map +1 -0
  15. package/dist/backends/kubernetes/egress-policy.d.ts +111 -35
  16. package/dist/backends/kubernetes/egress-policy.d.ts.map +1 -1
  17. package/dist/backends/kubernetes/egress-policy.js +81 -41
  18. package/dist/backends/kubernetes/egress-policy.js.map +1 -1
  19. package/dist/backends/kubernetes/identity.d.ts +129 -0
  20. package/dist/backends/kubernetes/identity.d.ts.map +1 -1
  21. package/dist/backends/kubernetes/identity.js +150 -0
  22. package/dist/backends/kubernetes/identity.js.map +1 -1
  23. package/dist/backends/kubernetes/index.d.ts +23 -1
  24. package/dist/backends/kubernetes/index.d.ts.map +1 -1
  25. package/dist/backends/kubernetes/index.js +22 -3
  26. package/dist/backends/kubernetes/index.js.map +1 -1
  27. package/dist/backends/kubernetes/objects.d.ts +76 -0
  28. package/dist/backends/kubernetes/objects.d.ts.map +1 -1
  29. package/dist/backends/kubernetes/objects.js +61 -0
  30. package/dist/backends/kubernetes/objects.js.map +1 -1
  31. package/dist/backends/kubernetes/per-sandbox-fence.d.ts +194 -0
  32. package/dist/backends/kubernetes/per-sandbox-fence.d.ts.map +1 -0
  33. package/dist/backends/kubernetes/per-sandbox-fence.js +522 -0
  34. package/dist/backends/kubernetes/per-sandbox-fence.js.map +1 -0
  35. package/dist/backends/kubernetes/per-sandbox-policy.d.ts +45 -3
  36. package/dist/backends/kubernetes/per-sandbox-policy.d.ts.map +1 -1
  37. package/dist/backends/kubernetes/per-sandbox-policy.js +98 -8
  38. package/dist/backends/kubernetes/per-sandbox-policy.js.map +1 -1
  39. package/dist/backends/kubernetes/sandbox.d.ts +4 -9
  40. package/dist/backends/kubernetes/sandbox.d.ts.map +1 -1
  41. package/dist/backends/kubernetes/sandbox.js +4 -9
  42. package/dist/backends/kubernetes/sandbox.js.map +1 -1
  43. package/dist/backends/kubernetes/transport.d.ts +108 -1
  44. package/dist/backends/kubernetes/transport.d.ts.map +1 -1
  45. package/dist/backends/kubernetes/transport.js +150 -6
  46. package/dist/backends/kubernetes/transport.js.map +1 -1
  47. package/dist/backends/kubernetes/workspace-allocate.d.ts +206 -0
  48. package/dist/backends/kubernetes/workspace-allocate.d.ts.map +1 -0
  49. package/dist/backends/kubernetes/workspace-allocate.js +332 -0
  50. package/dist/backends/kubernetes/workspace-allocate.js.map +1 -0
  51. package/dist/backends/kubernetes/workspace-observe.d.ts +187 -0
  52. package/dist/backends/kubernetes/workspace-observe.d.ts.map +1 -0
  53. package/dist/backends/kubernetes/workspace-observe.js +343 -0
  54. package/dist/backends/kubernetes/workspace-observe.js.map +1 -0
  55. package/dist/backends/kubernetes/workspace-per-sandbox.d.ts +94 -0
  56. package/dist/backends/kubernetes/workspace-per-sandbox.d.ts.map +1 -0
  57. package/dist/backends/kubernetes/workspace-per-sandbox.js +119 -0
  58. package/dist/backends/kubernetes/workspace-per-sandbox.js.map +1 -0
  59. package/dist/backends/kubernetes/workspace.d.ts +139 -5
  60. package/dist/backends/kubernetes/workspace.d.ts.map +1 -1
  61. package/dist/backends/kubernetes/workspace.js +434 -36
  62. package/dist/backends/kubernetes/workspace.js.map +1 -1
  63. package/dist/index.d.ts +83 -7
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +100 -6
  66. package/dist/index.js.map +1 -1
  67. package/package.json +3 -3
  68. package/src/backends/firecracker/protocol.ts +47 -0
  69. package/src/backends/firecracker/transport.ts +31 -0
  70. package/src/backends/kubernetes/allocation.ts +199 -0
  71. package/src/backends/kubernetes/egress-policy.ts +165 -42
  72. package/src/backends/kubernetes/identity.ts +210 -0
  73. package/src/backends/kubernetes/index.ts +61 -2
  74. package/src/backends/kubernetes/objects.ts +98 -0
  75. package/src/backends/kubernetes/per-sandbox-fence.ts +800 -0
  76. package/src/backends/kubernetes/per-sandbox-policy.ts +152 -17
  77. package/src/backends/kubernetes/sandbox.ts +4 -9
  78. package/src/backends/kubernetes/transport.ts +203 -4
  79. package/src/backends/kubernetes/workspace-allocate.ts +607 -0
  80. package/src/backends/kubernetes/workspace-observe.ts +590 -0
  81. package/src/backends/kubernetes/workspace-per-sandbox.ts +179 -0
  82. package/src/backends/kubernetes/workspace.ts +655 -34
  83. package/src/index.ts +193 -3
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Allocating a workspace without adopting or waking anything, bound to an
3
+ * allocation the host recorded first.
4
+ *
5
+ * `createKubernetesWorkspace` is create-or-adopt: a 409 turns into "this is
6
+ * mine from before", and the pod may be resumed before it returns. A host
7
+ * that persists its intent BEFORE creating something durable, and has to
8
+ * prove afterwards that what it finds is exactly what it created, needs the
9
+ * opposite verbs:
10
+ *
11
+ * - {@link resolveKubernetesExecutionProfile} — what a workspace created
12
+ * from this configuration would run, as content plus one fingerprint.
13
+ * Creates nothing. The host persists the result, then allocates.
14
+ * - {@link allocateKubernetesWorkspace} — ONE create request. A same-name
15
+ * object is a typed conflict and is never touched; the pod is never
16
+ * resumed; the answer is an acknowledgement carrying the new Sandbox's
17
+ * uid, not readiness.
18
+ * - {@link recoverKubernetesWorkspaceAllocation} — the only way out of a
19
+ * create whose reply never arrived. Read-only.
20
+ *
21
+ * ## Fingerprint canonicalisation
22
+ *
23
+ * The execution profile fingerprint is `sha256:<hex>` of
24
+ * `JSON.stringify(canonical(content))`, where `canonical` sorts the keys of
25
+ * every object by UTF-16 code unit, drops members whose value is
26
+ * `undefined`, keeps arrays in order, and `JSON.stringify` adds no
27
+ * whitespace. `content` is {@link KubernetesExecutionProfile.content}.
28
+ */
29
+ import { type KubernetesObservedAllocation, type KubernetesWorkspaceAllocation, type KubernetesWorkspaceAllocationMismatch } from './allocation.js';
30
+ import { type KubernetesBackendInternalConfig } from './index.js';
31
+ import { KUBERNETES_PER_SANDBOX_PROFILE_VARIANT } from './workspace-per-sandbox.js';
32
+ /** `resolveKubernetesExecutionProfile`'s options. */
33
+ export interface KubernetesExecutionProfileOptions {
34
+ /** The SandboxTemplate to resolve; defaults to `config.sandboxTemplateName`. */
35
+ readonly sandboxTemplateName?: string;
36
+ /**
37
+ * The workspace the profile is for. Only read when
38
+ * `config.egress.perSandbox` is configured: the per-sandbox selector
39
+ * label's VALUE is the Sandbox's name, so `podTemplateHash` is per
40
+ * workspace there. Without it that hash is taken over the template
41
+ * WITHOUT the label, and is not the revision a handle reports. The
42
+ * fingerprint never depends on it.
43
+ */
44
+ readonly workspaceId?: string;
45
+ readonly signal?: AbortSignal;
46
+ }
47
+ /**
48
+ * What a workspace created from this configuration would run.
49
+ *
50
+ * - `fingerprint` — `sha256:<hex>` over {@link content}; the value stored
51
+ * in the profile annotation and the one `expectedAllocation.executionProfile`
52
+ * carries.
53
+ * - `podTemplateHash` — the revision of the pod template alone, the same
54
+ * value `templateRevision` reports on a handle.
55
+ * - `variant` — `'core/no-network+per-sandbox'` when
56
+ * `config.egress.perSandbox` is configured over a core `no-network`
57
+ * baseline, and ABSENT otherwise (over another baseline the content still
58
+ * changes the fingerprint but claims no name), so the profile of every
59
+ * configuration that predates the option keeps its fingerprint byte for
60
+ * byte. Present only after the admission fence that
61
+ * bounds the host's per-sandbox writes was read and found to enforce:
62
+ * resolving the profile is how a host learns it is admitted.
63
+ * - `content` — what was fingerprinted, so a host can persist exactly it:
64
+ * the pod template after this backend's overlays, the template's volume
65
+ * claim templates, the egress policy as translated from `config.egress`
66
+ * (kind, name, manifest — config-derived, not read from the cluster), and
67
+ * the ingress verification settings.
68
+ */
69
+ export interface KubernetesExecutionProfile {
70
+ readonly fingerprint: string;
71
+ readonly podTemplateHash: string;
72
+ readonly sandboxTemplateName: string;
73
+ readonly variant?: typeof KUBERNETES_PER_SANDBOX_PROFILE_VARIANT;
74
+ readonly content: Readonly<Record<string, unknown>>;
75
+ }
76
+ /**
77
+ * Resolve and fingerprint what a workspace created from `config` would run —
78
+ * the pod template with this backend's overlays, the template's disks, the
79
+ * egress policy translated from `config.egress` and the ingress settings —
80
+ * without creating, waking or writing anything. One GET: the
81
+ * SandboxTemplate.
82
+ *
83
+ * The host persists the result, then passes `fingerprint` as
84
+ * `allocation.executionProfile` to {@link allocateKubernetesWorkspace}, which
85
+ * refuses to create if the profile resolves differently by then.
86
+ */
87
+ export declare function resolveKubernetesExecutionProfile(config: KubernetesBackendInternalConfig, options?: KubernetesExecutionProfileOptions): Promise<KubernetesExecutionProfile>;
88
+ /** {@link allocateKubernetesWorkspace}'s options. */
89
+ export interface KubernetesWorkspaceAllocateOptions {
90
+ readonly workspaceId: string;
91
+ readonly allocation: KubernetesWorkspaceAllocation;
92
+ /** The holder epoch to create the workspace under; see `createKubernetesWorkspace`. */
93
+ readonly epoch?: number;
94
+ readonly sandboxTemplateName?: string;
95
+ readonly signal?: AbortSignal;
96
+ }
97
+ /**
98
+ * Acknowledgement that the API server accepted the create. It says the
99
+ * object exists and which one it is; it does not say the pod is scheduled,
100
+ * the disk is bound or the guest answers — open it with
101
+ * `openKubernetesWorkspace({ waitForReady: true })` for that.
102
+ */
103
+ export interface KubernetesWorkspaceAllocationReceipt {
104
+ readonly workspaceId: string;
105
+ readonly sandboxName: string;
106
+ readonly namespace: string;
107
+ /** The created Sandbox's `metadata.uid`. */
108
+ readonly sandboxUid: string;
109
+ /**
110
+ * What the object carries. From `allocate` the profile fingerprint is
111
+ * always present; from recovery it is whatever the annotation holds.
112
+ */
113
+ readonly allocation: KubernetesWorkspaceAllocation;
114
+ readonly epoch?: number;
115
+ /** The pod-template revision stored with the object. */
116
+ readonly templateRevision: string;
117
+ }
118
+ /** Why a create's outcome is unknown. */
119
+ export type KubernetesAllocationUnknownReason = 'timeout' | 'connection' | 'server-error' | 'aborted' | 'unreadable-reply';
120
+ /**
121
+ * The create request was sent and no usable reply came back, so the object
122
+ * may or may not exist. The request was NOT retried and will not be: resolve
123
+ * it with {@link recoverKubernetesWorkspaceAllocation}.
124
+ */
125
+ export interface KubernetesWorkspaceAllocationUnknown {
126
+ readonly outcome: 'unknown';
127
+ readonly reason: KubernetesAllocationUnknownReason;
128
+ readonly workspaceId: string;
129
+ readonly sandboxName: string;
130
+ /** What was sent, including the resolved profile fingerprint. */
131
+ readonly allocation: Required<KubernetesWorkspaceAllocation>;
132
+ readonly epoch?: number;
133
+ /** The failure that made it unknown. */
134
+ readonly error: unknown;
135
+ }
136
+ export type KubernetesWorkspaceAllocationResult = {
137
+ readonly outcome: 'created';
138
+ readonly receipt: KubernetesWorkspaceAllocationReceipt;
139
+ } | KubernetesWorkspaceAllocationUnknown;
140
+ /**
141
+ * Create a workspace Sandbox, exactly once, bound to `allocation`. Never
142
+ * adopts, never resumes, never retries.
143
+ *
144
+ * Before the request, and read-only: the same egress and ingress checks
145
+ * `createKubernetesWorkspace` makes, the block-disk check, and — when
146
+ * `allocation.executionProfile` is given — that the profile resolves to it
147
+ * ({@link KubernetesExecutionProfileDriftError} otherwise). A refusal there
148
+ * sends no create.
149
+ *
150
+ * Then ONE POST, which carries the allocation annotations (see the
151
+ * `*_ANNOTATION_KEY` constants), the holder epoch when one is passed, and
152
+ * the pod-template revision:
153
+ *
154
+ * - 201 → `{ outcome: 'created', receipt }`.
155
+ * - 409 → {@link KubernetesWorkspaceAllocationConflictError}; the existing
156
+ * object was not read, touched or woken.
157
+ * - a timeout, a reset connection, a 5xx or a caller abort after the request
158
+ * was sent, or a reply with no uid → `{ outcome: 'unknown' }`. The POST is
159
+ * not repeated.
160
+ * - any other rejection (401, 403, 404, 4xx) rejects as itself: the server
161
+ * answered, and nothing was created.
162
+ */
163
+ export declare function allocateKubernetesWorkspace(config: KubernetesBackendInternalConfig, options: KubernetesWorkspaceAllocateOptions): Promise<KubernetesWorkspaceAllocationResult>;
164
+ /** {@link recoverKubernetesWorkspaceAllocation}'s options. */
165
+ export interface KubernetesWorkspaceAllocationRecoverOptions {
166
+ readonly workspaceId: string;
167
+ /** The allocation that was sent; `executionProfile` is compared when present. */
168
+ readonly allocation: KubernetesWorkspaceAllocation;
169
+ /** When given, a workspace held at a higher epoch refuses the read. */
170
+ readonly epoch?: number;
171
+ readonly signal?: AbortSignal;
172
+ }
173
+ /**
174
+ * What a read-only look found after an unknown create.
175
+ *
176
+ * - `created` — a Sandbox stands there carrying exactly the allocation;
177
+ * `receipt` is the one the lost reply would have been.
178
+ * - `absent` — no Sandbox stands there at the moment of the read. A create
179
+ * still in flight at the API server could land afterwards, so this is a
180
+ * point-in-time answer: allocate again with the SAME allocation is safe
181
+ * (a late landing makes it a typed conflict, and recovery then says
182
+ * `created`), a different allocation under this id is not.
183
+ * - `different` — something else stands there; `mismatch` says what differs.
184
+ */
185
+ export type KubernetesWorkspaceAllocationRecovery = {
186
+ readonly state: 'created';
187
+ readonly receipt: KubernetesWorkspaceAllocationReceipt;
188
+ } | {
189
+ readonly state: 'absent';
190
+ readonly workspaceId: string;
191
+ readonly sandboxName: string;
192
+ } | {
193
+ readonly state: 'different';
194
+ readonly workspaceId: string;
195
+ readonly sandboxName: string;
196
+ readonly mismatch: KubernetesWorkspaceAllocationMismatch;
197
+ readonly observed: KubernetesObservedAllocation;
198
+ readonly sandboxUid: string | undefined;
199
+ };
200
+ /**
201
+ * Settle an unknown create by reading: one GET of the Sandbox, compared with
202
+ * the expected allocation. Never creates, wakes or writes. A read that did
203
+ * not answer rejects as itself — it is never reported as `absent`.
204
+ */
205
+ export declare function recoverKubernetesWorkspaceAllocation(config: KubernetesBackendInternalConfig, options: KubernetesWorkspaceAllocationRecoverOptions): Promise<KubernetesWorkspaceAllocationRecovery>;
206
+ //# sourceMappingURL=workspace-allocate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-allocate.d.ts","sourceRoot":"","sources":["../../../src/backends/kubernetes/workspace-allocate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Allocating a workspace without adopting or waking anything, bound to an
3
+ * allocation the host recorded first.
4
+ *
5
+ * `createKubernetesWorkspace` is create-or-adopt: a 409 turns into "this is
6
+ * mine from before", and the pod may be resumed before it returns. A host
7
+ * that persists its intent BEFORE creating something durable, and has to
8
+ * prove afterwards that what it finds is exactly what it created, needs the
9
+ * opposite verbs:
10
+ *
11
+ * - {@link resolveKubernetesExecutionProfile} — what a workspace created
12
+ * from this configuration would run, as content plus one fingerprint.
13
+ * Creates nothing. The host persists the result, then allocates.
14
+ * - {@link allocateKubernetesWorkspace} — ONE create request. A same-name
15
+ * object is a typed conflict and is never touched; the pod is never
16
+ * resumed; the answer is an acknowledgement carrying the new Sandbox's
17
+ * uid, not readiness.
18
+ * - {@link recoverKubernetesWorkspaceAllocation} — the only way out of a
19
+ * create whose reply never arrived. Read-only.
20
+ *
21
+ * ## Fingerprint canonicalisation
22
+ *
23
+ * The execution profile fingerprint is `sha256:<hex>` of
24
+ * `JSON.stringify(canonical(content))`, where `canonical` sorts the keys of
25
+ * every object by UTF-16 code unit, drops members whose value is
26
+ * `undefined`, keeps arrays in order, and `JSON.stringify` adds no
27
+ * whitespace. `content` is {@link KubernetesExecutionProfile.content}.
28
+ */
29
+ import { KubernetesExecutionProfileDriftError, KubernetesWorkspaceAllocationConflictError, assertAllocation, firstAllocationMismatch, observedAllocation, } from './allocation.js';
30
+ import { assertEgressPolicyIsEnforceable, assertEgressProfileIsUsable, assertPerSandboxEgressIsUsable, composeAdditionalPodLabels, perSandboxEgressLabelKey, } from './egress-policy.js';
31
+ import { DEFAULT_AGENT_PORT, buildEgressBoundary, buildIngressVerifier, buildSandboxBody, clientAccess, clientOptions, readSandboxTemplate, sandboxPodLabels, } from './index.js';
32
+ import { ingressVerificationEnabled, resolveIngressEngine } from './ingress-policy.js';
33
+ import { KubernetesAlreadyGoneError, KubernetesApiError, KubernetesApiTimeoutError, KubernetesConflictError, createKubernetesClient, } from './k8s-client.js';
34
+ import { ALLOCATION_ID_ANNOTATION_KEY, ALLOCATION_SCOPE_ANNOTATION_KEY, EXECUTION_PROFILE_ANNOTATION_KEY, HOLDER_EPOCH_ANNOTATION_KEY, POD_TEMPLATE_HASH_ANNOTATION_KEY, canonicalFingerprint, readHolderEpoch, sandboxCollectionPath, sandboxPath, } from './objects.js';
35
+ import { buildAdmissionFence } from './per-sandbox-policy.js';
36
+ import { KUBERNETES_PER_SANDBOX_PROFILE_VARIANT, perSandboxWorkspaceLabels, withoutPerSandboxLabel, } from './workspace-per-sandbox.js';
37
+ import { KubernetesWorkspacePreconditionError, assertBlockModeWorkspaceDisk, assertHolderEpoch, buildPodTemplateRefresh, workspaceSandboxName, } from './workspace.js';
38
+ /** Bumped when the shape of {@link KubernetesExecutionProfile.content} changes. */
39
+ const EXECUTION_PROFILE_CONTENT_VERSION = 1;
40
+ async function planProfile(config, sandboxTemplateName, signal, workspaceName) {
41
+ assertPerSandboxEgressIsUsable(config.egress);
42
+ assertEgressProfileIsUsable(config.egress);
43
+ if (config.egress) {
44
+ assertEgressPolicyIsEnforceable(config.egress.policy, config.egress.engine ?? 'core', config.egress.ciliumNarrowing);
45
+ }
46
+ const client = createKubernetesClient(clientAccess(config), clientOptions(config));
47
+ const templateName = sandboxTemplateName ?? config.sandboxTemplateName;
48
+ const boundary = buildEgressBoundary(client, config, templateName);
49
+ const template = await readSandboxTemplate(client, config.namespace, templateName, signal);
50
+ assertBlockModeWorkspaceDisk(`SandboxTemplate ${templateName} in namespace ${config.namespace}`, template.podTemplate, template.volumeClaimTemplates);
51
+ // Per-sandbox egress: the profile is admitted only when the fence that
52
+ // bounds the host's writes verifies, read here so a configuration whose
53
+ // fence is missing or inert never yields a fingerprint to persist.
54
+ const perSandbox = config.egress?.perSandbox;
55
+ if (perSandbox !== undefined) {
56
+ await buildAdmissionFence(client, perSandbox, config.namespace).assertApplied(signal);
57
+ }
58
+ const perSandboxKey = perSandboxEgressLabelKey(config.egress);
59
+ // The variant is named for what it is: a core `no-network` baseline plus
60
+ // per-sandbox widening. Over any other baseline `perSandbox` still
61
+ // changes the fingerprint (it is in the content) but claims no name, so a
62
+ // host that admits only the named variant refuses the rest.
63
+ const carriesVariant = perSandbox !== undefined &&
64
+ config.egress?.policy.kind === 'no-network' &&
65
+ (config.egress.engine ?? 'core') === 'core';
66
+ // The label's value is the Sandbox's name, which the profile does not
67
+ // know: the fingerprinted template is built without it, and the body and
68
+ // the revision stamped on the object are built with it.
69
+ const stableLabels = composeAdditionalPodLabels(config.egress);
70
+ const podLabels = composeAdditionalPodLabels(config.egress, perSandboxKey !== undefined && workspaceName !== undefined
71
+ ? { [perSandboxKey]: workspaceName }
72
+ : undefined);
73
+ const stableRefresh = buildPodTemplateRefresh(template, templateName, config.runtimeClassName, stableLabels);
74
+ const refresh = perSandboxKey !== undefined && workspaceName !== undefined
75
+ ? buildPodTemplateRefresh(template, templateName, config.runtimeClassName, podLabels)
76
+ : stableRefresh;
77
+ const translated = await boundary?.translatedPolicy();
78
+ const ingressOn = ingressVerificationEnabled(config.ingress);
79
+ const content = {
80
+ version: EXECUTION_PROFILE_CONTENT_VERSION,
81
+ namespace: config.namespace,
82
+ sandboxTemplateName: templateName,
83
+ podTemplate: stableRefresh.podTemplate,
84
+ volumeClaimTemplates: stableRefresh.volumeClaimTemplates ?? null,
85
+ egress: translated === undefined
86
+ ? null
87
+ : {
88
+ kind: translated.kind,
89
+ policyKind: translated.policyKind,
90
+ name: translated.name,
91
+ manifest: translated.manifest,
92
+ },
93
+ ingress: ingressOn
94
+ ? {
95
+ verified: true,
96
+ engine: resolveIngressEngine(config.ingress, config.egress?.engine),
97
+ agentPort: config.agentPort ?? DEFAULT_AGENT_PORT,
98
+ }
99
+ : { verified: false },
100
+ // Absent without perSandbox, so an existing profile's fingerprint is
101
+ // unchanged. With it, the variant name and everything the host's
102
+ // per-sandbox writes depend on: a fence renamed, a different
103
+ // permitted-hosts ConfigMap or another selector key is another
104
+ // profile, and a workspace allocated under one is not adopted under
105
+ // the other.
106
+ ...(perSandbox !== undefined && perSandboxKey !== undefined
107
+ ? {
108
+ ...(carriesVariant ? { variant: KUBERNETES_PER_SANDBOX_PROFILE_VARIANT } : {}),
109
+ perSandbox: {
110
+ engine: perSandbox.engine,
111
+ labelKey: perSandboxKey,
112
+ admissionPolicyName: perSandbox.admissionPolicyName,
113
+ admissionPolicyBindingName: perSandbox.admissionPolicyBindingName ?? `${perSandbox.admissionPolicyName}-binding`,
114
+ hostIdentity: perSandbox.hostIdentity ?? null,
115
+ permittedHostsParam: perSandbox.permittedHostsParam ?? null,
116
+ narrowing: perSandbox.narrowing ?? null,
117
+ },
118
+ }
119
+ : {}),
120
+ };
121
+ return {
122
+ client,
123
+ templateName,
124
+ template,
125
+ boundary,
126
+ podLabels,
127
+ workspacePodLabels: sandboxPodLabels(template, templateName, podLabels),
128
+ profile: {
129
+ fingerprint: canonicalFingerprint(content),
130
+ podTemplateHash: refresh.hash,
131
+ sandboxTemplateName: templateName,
132
+ ...(carriesVariant ? { variant: KUBERNETES_PER_SANDBOX_PROFILE_VARIANT } : {}),
133
+ content,
134
+ },
135
+ };
136
+ }
137
+ /**
138
+ * Resolve and fingerprint what a workspace created from `config` would run —
139
+ * the pod template with this backend's overlays, the template's disks, the
140
+ * egress policy translated from `config.egress` and the ingress settings —
141
+ * without creating, waking or writing anything. One GET: the
142
+ * SandboxTemplate.
143
+ *
144
+ * The host persists the result, then passes `fingerprint` as
145
+ * `allocation.executionProfile` to {@link allocateKubernetesWorkspace}, which
146
+ * refuses to create if the profile resolves differently by then.
147
+ */
148
+ export async function resolveKubernetesExecutionProfile(config, options = {}) {
149
+ options.signal?.throwIfAborted();
150
+ return (await planProfile(config, options.sandboxTemplateName, options.signal, options.workspaceId !== undefined ? workspaceSandboxName(options.workspaceId) : undefined)).profile;
151
+ }
152
+ function unknownReason(err, signal) {
153
+ if (err instanceof KubernetesApiTimeoutError)
154
+ return 'timeout';
155
+ if (signal?.aborted === true)
156
+ return 'aborted';
157
+ if (err instanceof KubernetesApiError) {
158
+ if (err.transport === 'connect')
159
+ return 'connection';
160
+ if (err.status !== undefined && err.status >= 500)
161
+ return 'server-error';
162
+ }
163
+ return undefined;
164
+ }
165
+ /**
166
+ * Create a workspace Sandbox, exactly once, bound to `allocation`. Never
167
+ * adopts, never resumes, never retries.
168
+ *
169
+ * Before the request, and read-only: the same egress and ingress checks
170
+ * `createKubernetesWorkspace` makes, the block-disk check, and — when
171
+ * `allocation.executionProfile` is given — that the profile resolves to it
172
+ * ({@link KubernetesExecutionProfileDriftError} otherwise). A refusal there
173
+ * sends no create.
174
+ *
175
+ * Then ONE POST, which carries the allocation annotations (see the
176
+ * `*_ANNOTATION_KEY` constants), the holder epoch when one is passed, and
177
+ * the pod-template revision:
178
+ *
179
+ * - 201 → `{ outcome: 'created', receipt }`.
180
+ * - 409 → {@link KubernetesWorkspaceAllocationConflictError}; the existing
181
+ * object was not read, touched or woken.
182
+ * - a timeout, a reset connection, a 5xx or a caller abort after the request
183
+ * was sent, or a reply with no uid → `{ outcome: 'unknown' }`. The POST is
184
+ * not repeated.
185
+ * - any other rejection (401, 403, 404, 4xx) rejects as itself: the server
186
+ * answered, and nothing was created.
187
+ */
188
+ export async function allocateKubernetesWorkspace(config, options) {
189
+ options.signal?.throwIfAborted();
190
+ const { workspaceId, signal } = options;
191
+ assertAllocation(options.allocation, 'allocateKubernetesWorkspace');
192
+ const epoch = assertHolderEpoch(options.epoch, 'allocateKubernetesWorkspace');
193
+ const name = workspaceSandboxName(workspaceId);
194
+ const plan = await planProfile(config, options.sandboxTemplateName, signal, name);
195
+ const { fingerprint } = plan.profile;
196
+ if (options.allocation.executionProfile !== undefined &&
197
+ options.allocation.executionProfile !== fingerprint) {
198
+ throw new KubernetesExecutionProfileDriftError(workspaceId, options.allocation.executionProfile, fingerprint);
199
+ }
200
+ // The same pre-create checks the create-or-adopt path makes, against the
201
+ // labels this POST stamps. All GETs; a refusal leaves nothing behind.
202
+ const subject = `to allocate workspace ${workspaceId} as Sandbox ${name} in namespace ${config.namespace}`;
203
+ if (config.egress)
204
+ await plan.boundary?.verifyNamedObject(signal);
205
+ const verifyIngress = buildIngressVerifier(plan.client, config);
206
+ if (verifyIngress !== undefined)
207
+ await verifyIngress(plan.workspacePodLabels, subject, signal);
208
+ // Without the per-sandbox label, for the reason `createKubernetesWorkspace`
209
+ // gives: the only policy selecting by it is this workspace's own.
210
+ await plan.boundary?.verifyUnion(withoutPerSandboxLabel(plan.workspacePodLabels, perSandboxWorkspaceLabels(config.egress, name)), subject, signal);
211
+ const stored = {
212
+ id: options.allocation.id,
213
+ scopeFingerprint: options.allocation.scopeFingerprint,
214
+ executionProfile: fingerprint,
215
+ };
216
+ const body = buildSandboxBody({
217
+ namespace: config.namespace,
218
+ name,
219
+ template: plan.template,
220
+ sandboxTemplateName: plan.templateName,
221
+ podLabels: plan.podLabels,
222
+ annotations: {
223
+ ...(epoch !== undefined ? { [HOLDER_EPOCH_ANNOTATION_KEY]: String(epoch) } : {}),
224
+ [POD_TEMPLATE_HASH_ANNOTATION_KEY]: plan.profile.podTemplateHash,
225
+ [ALLOCATION_ID_ANNOTATION_KEY]: stored.id,
226
+ [ALLOCATION_SCOPE_ANNOTATION_KEY]: stored.scopeFingerprint,
227
+ [EXECUTION_PROFILE_ANNOTATION_KEY]: stored.executionProfile,
228
+ },
229
+ ...(config.runtimeClassName !== undefined ? { runtimeClassName: config.runtimeClassName } : {}),
230
+ });
231
+ const unknown = (reason, error) => ({
232
+ outcome: 'unknown',
233
+ reason,
234
+ workspaceId,
235
+ sandboxName: name,
236
+ allocation: stored,
237
+ ...(epoch !== undefined ? { epoch } : {}),
238
+ error,
239
+ });
240
+ let created;
241
+ try {
242
+ // The one and only create. `client.request` does not retry.
243
+ created = await plan.client.request('POST', sandboxCollectionPath(config.namespace), body, signal);
244
+ }
245
+ catch (err) {
246
+ if (err instanceof KubernetesConflictError) {
247
+ throw new KubernetesWorkspaceAllocationConflictError(workspaceId, name);
248
+ }
249
+ const reason = unknownReason(err, signal);
250
+ if (reason === undefined)
251
+ throw err;
252
+ return unknown(reason, err);
253
+ }
254
+ const sandboxUid = created?.metadata?.uid;
255
+ if (typeof sandboxUid !== 'string' || sandboxUid === '') {
256
+ return unknown('unreadable-reply', new Error('the create reply carried no metadata.uid'));
257
+ }
258
+ return {
259
+ outcome: 'created',
260
+ receipt: {
261
+ workspaceId,
262
+ sandboxName: name,
263
+ namespace: config.namespace,
264
+ sandboxUid,
265
+ allocation: stored,
266
+ ...(epoch !== undefined ? { epoch } : {}),
267
+ templateRevision: plan.profile.podTemplateHash,
268
+ },
269
+ };
270
+ }
271
+ /**
272
+ * Settle an unknown create by reading: one GET of the Sandbox, compared with
273
+ * the expected allocation. Never creates, wakes or writes. A read that did
274
+ * not answer rejects as itself — it is never reported as `absent`.
275
+ */
276
+ export async function recoverKubernetesWorkspaceAllocation(config, options) {
277
+ options.signal?.throwIfAborted();
278
+ const { workspaceId } = options;
279
+ assertAllocation(options.allocation, 'recoverKubernetesWorkspaceAllocation');
280
+ const epoch = assertHolderEpoch(options.epoch, 'recoverKubernetesWorkspaceAllocation');
281
+ const name = workspaceSandboxName(workspaceId);
282
+ const client = createKubernetesClient(clientAccess(config), clientOptions(config));
283
+ let sandbox;
284
+ try {
285
+ sandbox = await client.request('GET', sandboxPath(config.namespace, name), undefined, options.signal);
286
+ }
287
+ catch (err) {
288
+ if (!(err instanceof KubernetesAlreadyGoneError))
289
+ throw err;
290
+ }
291
+ if (sandbox === undefined)
292
+ return { state: 'absent', workspaceId, sandboxName: name };
293
+ if (epoch !== undefined) {
294
+ const held = readHolderEpoch(sandbox.metadata);
295
+ if (held.epoch === undefined || held.epoch > epoch) {
296
+ throw new KubernetesWorkspacePreconditionError('recoverKubernetesWorkspaceAllocation', workspaceId, name, epoch, held.epoch, held.annotation);
297
+ }
298
+ }
299
+ const mismatch = firstAllocationMismatch(sandbox, options.allocation);
300
+ const sandboxUid = sandbox.metadata?.uid;
301
+ if (mismatch !== undefined || typeof sandboxUid !== 'string' || sandboxUid === '') {
302
+ return {
303
+ state: 'different',
304
+ workspaceId,
305
+ sandboxName: name,
306
+ mismatch: mismatch?.kind ?? 'allocation-id',
307
+ observed: observedAllocation(sandbox),
308
+ sandboxUid,
309
+ };
310
+ }
311
+ const observed = observedAllocation(sandbox);
312
+ return {
313
+ state: 'created',
314
+ receipt: {
315
+ workspaceId,
316
+ sandboxName: name,
317
+ namespace: config.namespace,
318
+ sandboxUid,
319
+ allocation: {
320
+ id: options.allocation.id,
321
+ scopeFingerprint: options.allocation.scopeFingerprint,
322
+ ...(observed.executionProfile !== undefined
323
+ ? { executionProfile: observed.executionProfile }
324
+ : {}),
325
+ },
326
+ ...(epoch !== undefined ? { epoch } : {}),
327
+ // Present: the template-content check above refuses an object without it.
328
+ templateRevision: sandbox.metadata?.annotations?.[POD_TEMPLATE_HASH_ANNOTATION_KEY] ?? '',
329
+ },
330
+ };
331
+ }
332
+ //# sourceMappingURL=workspace-allocate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-allocate.js","sourceRoot":"","sources":["../../../src/backends/kubernetes/workspace-allocate.ts"],"names":[],"mappings":"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"}