@namzu/sandbox 13.0.0 → 15.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1147 -0
- package/README.md +447 -0
- package/dist/backends/aci-standby-pool/index.d.ts.map +1 -1
- package/dist/backends/aci-standby-pool/index.js +13 -1
- package/dist/backends/aci-standby-pool/index.js.map +1 -1
- package/dist/backends/docker/index.d.ts.map +1 -1
- package/dist/backends/docker/index.js +19 -1
- package/dist/backends/docker/index.js.map +1 -1
- package/dist/backends/firecracker/index.d.ts.map +1 -1
- package/dist/backends/firecracker/index.js +12 -2
- package/dist/backends/firecracker/index.js.map +1 -1
- package/dist/backends/firecracker/protocol.d.ts +481 -8
- package/dist/backends/firecracker/protocol.d.ts.map +1 -1
- package/dist/backends/firecracker/protocol.js +136 -0
- package/dist/backends/firecracker/protocol.js.map +1 -1
- package/dist/backends/firecracker/transport.d.ts +642 -14
- package/dist/backends/firecracker/transport.d.ts.map +1 -1
- package/dist/backends/firecracker/transport.js +1307 -34
- package/dist/backends/firecracker/transport.js.map +1 -1
- package/dist/backends/kubernetes/egress-policy.d.ts +1296 -0
- package/dist/backends/kubernetes/egress-policy.d.ts.map +1 -0
- package/dist/backends/kubernetes/egress-policy.js +2458 -0
- package/dist/backends/kubernetes/egress-policy.js.map +1 -0
- package/dist/backends/kubernetes/identity.d.ts +193 -0
- package/dist/backends/kubernetes/identity.d.ts.map +1 -0
- package/dist/backends/kubernetes/identity.js +147 -0
- package/dist/backends/kubernetes/identity.js.map +1 -0
- package/dist/backends/kubernetes/index.d.ts +1019 -0
- package/dist/backends/kubernetes/index.d.ts.map +1 -0
- package/dist/backends/kubernetes/index.js +1756 -0
- package/dist/backends/kubernetes/index.js.map +1 -0
- package/dist/backends/kubernetes/ingress-policy.d.ts +375 -0
- package/dist/backends/kubernetes/ingress-policy.d.ts.map +1 -0
- package/dist/backends/kubernetes/ingress-policy.js +1050 -0
- package/dist/backends/kubernetes/ingress-policy.js.map +1 -0
- package/dist/backends/kubernetes/k8s-client.d.ts +334 -0
- package/dist/backends/kubernetes/k8s-client.d.ts.map +1 -0
- package/dist/backends/kubernetes/k8s-client.js +553 -0
- package/dist/backends/kubernetes/k8s-client.js.map +1 -0
- package/dist/backends/kubernetes/lease.d.ts +145 -0
- package/dist/backends/kubernetes/lease.d.ts.map +1 -0
- package/dist/backends/kubernetes/lease.js +201 -0
- package/dist/backends/kubernetes/lease.js.map +1 -0
- package/dist/backends/kubernetes/objects.d.ts +702 -0
- package/dist/backends/kubernetes/objects.d.ts.map +1 -0
- package/dist/backends/kubernetes/objects.js +518 -0
- package/dist/backends/kubernetes/objects.js.map +1 -0
- package/dist/backends/kubernetes/per-sandbox-policy.d.ts +219 -0
- package/dist/backends/kubernetes/per-sandbox-policy.d.ts.map +1 -0
- package/dist/backends/kubernetes/per-sandbox-policy.js +407 -0
- package/dist/backends/kubernetes/per-sandbox-policy.js.map +1 -0
- package/dist/backends/kubernetes/privilege-probe.d.ts +136 -0
- package/dist/backends/kubernetes/privilege-probe.d.ts.map +1 -0
- package/dist/backends/kubernetes/privilege-probe.js +185 -0
- package/dist/backends/kubernetes/privilege-probe.js.map +1 -0
- package/dist/backends/kubernetes/rbac.d.ts +153 -0
- package/dist/backends/kubernetes/rbac.d.ts.map +1 -0
- package/dist/backends/kubernetes/rbac.js +177 -0
- package/dist/backends/kubernetes/rbac.js.map +1 -0
- package/dist/backends/kubernetes/sandbox.d.ts +190 -0
- package/dist/backends/kubernetes/sandbox.d.ts.map +1 -0
- package/dist/backends/kubernetes/sandbox.js +433 -0
- package/dist/backends/kubernetes/sandbox.js.map +1 -0
- package/dist/backends/kubernetes/transport.d.ts +1048 -0
- package/dist/backends/kubernetes/transport.d.ts.map +1 -0
- package/dist/backends/kubernetes/transport.js +2093 -0
- package/dist/backends/kubernetes/transport.js.map +1 -0
- package/dist/backends/kubernetes/workspace.d.ts +1512 -0
- package/dist/backends/kubernetes/workspace.d.ts.map +1 -0
- package/dist/backends/kubernetes/workspace.js +3703 -0
- package/dist/backends/kubernetes/workspace.js.map +1 -0
- package/dist/backends/remote-execution-controller.d.ts +14 -0
- package/dist/backends/remote-execution-controller.d.ts.map +1 -1
- package/dist/backends/remote-execution-controller.js.map +1 -1
- package/dist/index.d.ts +350 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +344 -34
- package/dist/index.js.map +1 -1
- package/dist/testing/sandbox-conformance.d.ts +227 -0
- package/dist/testing/sandbox-conformance.d.ts.map +1 -0
- package/dist/testing/sandbox-conformance.js +896 -0
- package/dist/testing/sandbox-conformance.js.map +1 -0
- package/package.json +5 -4
- package/src/backends/aci-standby-pool/index.ts +16 -1
- package/src/backends/docker/index.ts +22 -1
- package/src/backends/firecracker/index.ts +14 -2
- package/src/backends/firecracker/protocol.ts +541 -6
- package/src/backends/firecracker/transport.ts +1687 -64
- package/src/backends/kubernetes/egress-policy.ts +3448 -0
- package/src/backends/kubernetes/identity.ts +261 -0
- package/src/backends/kubernetes/index.ts +2670 -0
- package/src/backends/kubernetes/ingress-policy.ts +1344 -0
- package/src/backends/kubernetes/k8s-client.ts +742 -0
- package/src/backends/kubernetes/lease.ts +254 -0
- package/src/backends/kubernetes/objects.ts +983 -0
- package/src/backends/kubernetes/per-sandbox-policy.ts +542 -0
- package/src/backends/kubernetes/privilege-probe.ts +261 -0
- package/src/backends/kubernetes/rbac.ts +192 -0
- package/src/backends/kubernetes/sandbox.ts +593 -0
- package/src/backends/kubernetes/transport.ts +2895 -0
- package/src/backends/kubernetes/workspace.ts +5640 -0
- package/src/backends/remote-execution-controller.ts +14 -0
- package/src/index.ts +838 -35
- package/src/testing/sandbox-conformance.ts +1202 -0
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire shapes and paths for the agent-sandbox CRDs this backend touches.
|
|
3
|
+
*
|
|
4
|
+
* Every field name here was read off the CRDs a real cluster serves —
|
|
5
|
+
* `kubectl get crd -o json` against agent-sandbox v1.0.2 on a kind cluster —
|
|
6
|
+
* and cross-checked against the upstream `sandbox_types.go` /
|
|
7
|
+
* `sandboxclaim_types.go` doc comments. Where the two disagree the served CRD
|
|
8
|
+
* wins, because it is what the API server validates against: the Go source on
|
|
9
|
+
* upstream main has already moved `Sandbox`'s `shutdownTime` /
|
|
10
|
+
* `shutdownPolicy` under a `lifecycle` block, and v1beta1 as served still
|
|
11
|
+
* carries them at the top of `spec`.
|
|
12
|
+
*
|
|
13
|
+
* The shapes are deliberately partial. This backend reads four fields out of a
|
|
14
|
+
* Sandbox status and writes three into a claim spec; typing the rest of a
|
|
15
|
+
* PodSpec would be a vendored copy of the core API that goes stale on its own
|
|
16
|
+
* schedule. A pod template read from a SandboxTemplate is carried through as
|
|
17
|
+
* an opaque record for exactly that reason — it is copied, never interpreted.
|
|
18
|
+
*
|
|
19
|
+
* Two groups, both `v1beta1` and both singular-versioned today:
|
|
20
|
+
* - `agents.x-k8s.io` → sandboxes
|
|
21
|
+
* - `extensions.agents.x-k8s.io` → sandboxtemplates, sandboxwarmpools,
|
|
22
|
+
* sandboxclaims
|
|
23
|
+
*/
|
|
24
|
+
/** Group serving the `Sandbox` kind. */
|
|
25
|
+
export declare const SANDBOX_API_GROUP = "agents.x-k8s.io";
|
|
26
|
+
/** Group serving `SandboxTemplate`, `SandboxWarmPool` and `SandboxClaim`. */
|
|
27
|
+
export declare const SANDBOX_EXTENSIONS_API_GROUP = "extensions.agents.x-k8s.io";
|
|
28
|
+
/** The only version either group serves in agent-sandbox v1.0.2. */
|
|
29
|
+
export declare const SANDBOX_API_VERSION = "v1beta1";
|
|
30
|
+
/** `status.conditions[].type` both kinds report readiness under. */
|
|
31
|
+
export declare const READY_CONDITION = "Ready";
|
|
32
|
+
export interface KubernetesObjectMeta {
|
|
33
|
+
readonly name?: string;
|
|
34
|
+
readonly namespace?: string;
|
|
35
|
+
readonly uid?: string;
|
|
36
|
+
/**
|
|
37
|
+
* RFC 3339, written by the API server on admission and never by a client.
|
|
38
|
+
* Read only to report how old a workspace is — see
|
|
39
|
+
* `workspace.ts`'s `listKubernetesWorkspaces`.
|
|
40
|
+
*/
|
|
41
|
+
readonly creationTimestamp?: string;
|
|
42
|
+
/**
|
|
43
|
+
* The object's version as this read saw it, written by the API server on
|
|
44
|
+
* every write and never by a client.
|
|
45
|
+
*
|
|
46
|
+
* It is here for exactly one use and no other: the fallback `test` clause
|
|
47
|
+
* of a holder-epoch patch aimed at an object that does not carry the
|
|
48
|
+
* annotation yet, and the `preconditions.resourceVersion` of a fenced
|
|
49
|
+
* DELETE — both INSIDE the one read-write pair that read it. It is never
|
|
50
|
+
* stored on a handle, never carried across calls and never streamed, so
|
|
51
|
+
* the "no watch, no informers, no resourceVersion tracking" invariant
|
|
52
|
+
* `k8s-client.ts` states still holds. See {@link buildHolderEpochPatch}.
|
|
53
|
+
*/
|
|
54
|
+
readonly resourceVersion?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Set the moment a DELETE is accepted, long before the object goes away.
|
|
57
|
+
* A pod that carries one is on its way out and must never be bound to —
|
|
58
|
+
* see {@link isPodLive}.
|
|
59
|
+
*/
|
|
60
|
+
readonly deletionTimestamp?: string;
|
|
61
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
62
|
+
readonly annotations?: Readonly<Record<string, string>>;
|
|
63
|
+
}
|
|
64
|
+
/** `metav1.Condition`, as both CRDs embed it. */
|
|
65
|
+
export interface KubernetesCondition {
|
|
66
|
+
readonly type: string;
|
|
67
|
+
readonly status: 'True' | 'False' | 'Unknown';
|
|
68
|
+
readonly reason?: string;
|
|
69
|
+
readonly message?: string;
|
|
70
|
+
readonly lastTransitionTime?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* True only for an explicit `status: 'True'`. An absent condition, an
|
|
74
|
+
* `Unknown` and a `False` are all "not yet", never "assume so" — the
|
|
75
|
+
* controller writes `Unknown` while it is still deciding.
|
|
76
|
+
*/
|
|
77
|
+
export declare function isConditionTrue(conditions: readonly KubernetesCondition[] | undefined, type: string): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* `SandboxClaim.spec.lifecycle`.
|
|
80
|
+
*
|
|
81
|
+
* `shutdownTime` is the only one of the three that bounds a claim whose owner
|
|
82
|
+
* disappeared: the controller deletes the claim's resources once the wall
|
|
83
|
+
* clock reaches it, whatever the claim is doing. `ttlSecondsAfterFinished`
|
|
84
|
+
* reads like the leak guard and is not one — upstream's own comment says "the
|
|
85
|
+
* timer starts from the mirrored Finished condition's LastTransitionTime", so
|
|
86
|
+
* a claim whose host crashed before finishing never starts that clock.
|
|
87
|
+
*/
|
|
88
|
+
export interface SandboxClaimLifecycle {
|
|
89
|
+
/** RFC 3339. Absolute expiry; the claim never expires without it. */
|
|
90
|
+
readonly shutdownTime?: string;
|
|
91
|
+
/**
|
|
92
|
+
* What happens to the claim OBJECT at expiry. `Retain` (the CRD default)
|
|
93
|
+
* deletes the Sandbox, Pod and Service but leaves the claim behind, so a
|
|
94
|
+
* host that crashes daily accumulates claims forever.
|
|
95
|
+
*/
|
|
96
|
+
readonly shutdownPolicy?: 'Delete' | 'DeleteForeground' | 'Retain';
|
|
97
|
+
readonly ttlSecondsAfterFinished?: number;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* `SandboxClaim.spec`. `warmPoolRef` is REQUIRED by the CRD, which is why
|
|
101
|
+
* there is no such thing as a pool-less claim and the no-pool path has to
|
|
102
|
+
* create a Sandbox directly.
|
|
103
|
+
*
|
|
104
|
+
* `env` and `volumeClaimTemplates` exist on this spec and are deliberately
|
|
105
|
+
* absent from this type: setting either forces the claim to cold-start rather
|
|
106
|
+
* than adopt a warm pool sandbox, which is the one thing the warm path exists
|
|
107
|
+
* to avoid. A field that cannot be named cannot be set by accident.
|
|
108
|
+
*
|
|
109
|
+
* `additionalPodMetadata` is the exception, and the reason the rule above is
|
|
110
|
+
* about COLD STARTS rather than about claim-time metadata in general: labels
|
|
111
|
+
* are merged into an adopted warm sandbox without one. Measured on
|
|
112
|
+
* agent-sandbox v1.0.2 — two claims out of one two-replica pool, each
|
|
113
|
+
* carrying a different label value, both binding a replica that already
|
|
114
|
+
* existed, and the controller patching the label onto the running pod and
|
|
115
|
+
* into the Sandbox's own podTemplate. See `egress-policy.ts`'s profile
|
|
116
|
+
* support.
|
|
117
|
+
*/
|
|
118
|
+
export interface SandboxClaimResourceSpec {
|
|
119
|
+
readonly warmPoolRef: {
|
|
120
|
+
readonly name: string;
|
|
121
|
+
};
|
|
122
|
+
readonly lifecycle?: SandboxClaimLifecycle;
|
|
123
|
+
/**
|
|
124
|
+
* Labels (and annotations, which this backend never sets) the controller
|
|
125
|
+
* merges onto the pod it binds. Warm-safe — see the type comment.
|
|
126
|
+
*/
|
|
127
|
+
readonly additionalPodMetadata?: {
|
|
128
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* `SandboxClaim.status`. `sandbox` is the whole reason the claim path reads
|
|
133
|
+
* status back: an adopted pool sandbox keeps the generated name the pool gave
|
|
134
|
+
* it, so the bound object is routinely NOT named after the claim.
|
|
135
|
+
*/
|
|
136
|
+
export interface SandboxClaimResourceStatus {
|
|
137
|
+
readonly conditions?: readonly KubernetesCondition[];
|
|
138
|
+
readonly sandbox?: {
|
|
139
|
+
readonly name?: string;
|
|
140
|
+
readonly podIPs?: readonly string[];
|
|
141
|
+
readonly serviceFQDN?: string;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
export interface SandboxClaimResource {
|
|
145
|
+
readonly apiVersion?: string;
|
|
146
|
+
readonly kind?: string;
|
|
147
|
+
readonly metadata?: KubernetesObjectMeta;
|
|
148
|
+
readonly spec?: SandboxClaimResourceSpec;
|
|
149
|
+
readonly status?: SandboxClaimResourceStatus;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* A `GET` of the claims COLLECTION. Read by `readKubernetesTaskCapacity` (a
|
|
153
|
+
* count) and `releaseKubernetesTaskSandboxes` (the names to `DELETE`) — both
|
|
154
|
+
* in `index.ts`. Same "no watch, no `continue`" shape as
|
|
155
|
+
* {@link SandboxListResource}, for the same reason: this backend does no
|
|
156
|
+
* watch at all.
|
|
157
|
+
*/
|
|
158
|
+
export interface SandboxClaimListResource {
|
|
159
|
+
readonly items?: readonly SandboxClaimResource[];
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* `SandboxWarmPool.spec`/`.status`, read by `readKubernetesTaskCapacity`
|
|
163
|
+
* alone — the first place in this backend that reads a `SandboxWarmPool`
|
|
164
|
+
* rather than only naming one in a claim's `warmPoolRef`. Partial in the
|
|
165
|
+
* same way every other shape here is: `replicas` and `readyReplicas` are the
|
|
166
|
+
* two fields a capacity read needs, off the exact same object
|
|
167
|
+
* `k8s/scripts/acquire-p50.mjs` already polls by hand.
|
|
168
|
+
*/
|
|
169
|
+
export interface SandboxWarmPoolResource {
|
|
170
|
+
readonly metadata?: KubernetesObjectMeta;
|
|
171
|
+
readonly spec?: {
|
|
172
|
+
readonly replicas?: number;
|
|
173
|
+
};
|
|
174
|
+
readonly status?: {
|
|
175
|
+
readonly readyReplicas?: number;
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* `podTemplate` on a Sandbox or a SandboxTemplate. `spec` is a core `PodSpec`,
|
|
180
|
+
* carried opaquely: this backend copies one from a template into a Sandbox and
|
|
181
|
+
* overlays at most `runtimeClassName`.
|
|
182
|
+
*/
|
|
183
|
+
export interface SandboxPodTemplate {
|
|
184
|
+
readonly metadata?: KubernetesObjectMeta;
|
|
185
|
+
readonly spec: Readonly<Record<string, unknown>>;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* One `spec.volumeClaimTemplates` entry.
|
|
189
|
+
*
|
|
190
|
+
* Partial in the same way every other shape here is: a workspace's disk is
|
|
191
|
+
* COPIED verbatim from the `SandboxTemplate` that declares it, and only the
|
|
192
|
+
* two fields this backend has to reason about are named — the entry's own
|
|
193
|
+
* `metadata.name`, which is how the controller wires the mount (StatefulSet
|
|
194
|
+
* style: the PVC is created as `<entry name>-<sandbox name>` and no explicit
|
|
195
|
+
* `volumes:` entry is needed in the podTemplate), and `spec.volumeMode`,
|
|
196
|
+
* which decides whether the guest gets a raw block device or a filesystem
|
|
197
|
+
* passthrough. The index signatures carry everything else across untouched.
|
|
198
|
+
*/
|
|
199
|
+
export interface SandboxVolumeClaimTemplate {
|
|
200
|
+
readonly metadata?: KubernetesObjectMeta;
|
|
201
|
+
readonly spec?: {
|
|
202
|
+
readonly volumeMode?: string;
|
|
203
|
+
readonly [field: string]: unknown;
|
|
204
|
+
};
|
|
205
|
+
readonly [field: string]: unknown;
|
|
206
|
+
}
|
|
207
|
+
export interface SandboxResourceSpec {
|
|
208
|
+
readonly operatingMode?: 'Running' | 'Suspended';
|
|
209
|
+
readonly podTemplate: SandboxPodTemplate;
|
|
210
|
+
/** Create a headless Service, and with it a `status.serviceFQDN`. */
|
|
211
|
+
readonly service?: boolean;
|
|
212
|
+
readonly shutdownPolicy?: 'Delete' | 'Retain';
|
|
213
|
+
/** RFC 3339, top-level on v1beta1 Sandbox (NOT under `lifecycle`). */
|
|
214
|
+
readonly shutdownTime?: string;
|
|
215
|
+
/**
|
|
216
|
+
* CEL-immutable on the served CRD ("volumeClaimTemplates is immutable"),
|
|
217
|
+
* which is why a workspace's disk has to be in the spec from creation and
|
|
218
|
+
* cannot be attached to a sandbox that is already running.
|
|
219
|
+
*/
|
|
220
|
+
readonly volumeClaimTemplates?: readonly SandboxVolumeClaimTemplate[];
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* `Sandbox.status`. Note what is NOT here: a pod name. The backing pod is
|
|
224
|
+
* named after the Sandbox itself in v1.0.2, and `selector` — a serialised
|
|
225
|
+
* label selector, e.g. `agents.x-k8s.io/sandbox-name-hash=<hash>` — is the
|
|
226
|
+
* only thing in the API that finds the pod without relying on that.
|
|
227
|
+
*/
|
|
228
|
+
export interface SandboxResourceStatus {
|
|
229
|
+
readonly conditions?: readonly KubernetesCondition[];
|
|
230
|
+
readonly nodeName?: string;
|
|
231
|
+
readonly podIPs?: readonly string[];
|
|
232
|
+
readonly selector?: string;
|
|
233
|
+
readonly service?: string;
|
|
234
|
+
readonly serviceFQDN?: string;
|
|
235
|
+
}
|
|
236
|
+
export interface SandboxResource {
|
|
237
|
+
readonly apiVersion?: string;
|
|
238
|
+
readonly kind?: string;
|
|
239
|
+
readonly metadata?: KubernetesObjectMeta;
|
|
240
|
+
readonly spec?: SandboxResourceSpec;
|
|
241
|
+
readonly status?: SandboxResourceStatus;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* A `GET` of the sandboxes COLLECTION. `items` is the only field anything
|
|
245
|
+
* here reads: this backend does no watch, so `metadata.resourceVersion` and
|
|
246
|
+
* `continue` have nothing to feed — the namespace a deployment gives its
|
|
247
|
+
* sandboxes holds tens of objects, not the thousands that would make a page
|
|
248
|
+
* boundary a real answer rather than a truncated one.
|
|
249
|
+
*/
|
|
250
|
+
export interface SandboxListResource {
|
|
251
|
+
readonly items?: readonly SandboxResource[];
|
|
252
|
+
}
|
|
253
|
+
export interface SandboxTemplateResource {
|
|
254
|
+
readonly metadata?: KubernetesObjectMeta;
|
|
255
|
+
readonly spec?: {
|
|
256
|
+
readonly podTemplate?: SandboxPodTemplate;
|
|
257
|
+
readonly service?: boolean;
|
|
258
|
+
readonly volumeClaimTemplates?: readonly SandboxVolumeClaimTemplate[];
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* `metadata.uid` is the per-instance agent bind token; `deletionTimestamp`
|
|
263
|
+
* and `phase` exist only to answer "is this the pod that uid belongs to, or
|
|
264
|
+
* the one being deleted?" — see {@link isPodLive}.
|
|
265
|
+
*
|
|
266
|
+
* `podIP` is read by the `pod-ip` address mode only, and deliberately from
|
|
267
|
+
* the SAME object the uid comes from: an address taken from one pod and a
|
|
268
|
+
* token taken from another is the mismatch that reports as a flat
|
|
269
|
+
* `unauthorized` with nothing pointing at the pod that was replaced in
|
|
270
|
+
* between. Both spellings are carried because a dual-stack cluster fills
|
|
271
|
+
* `podIPs` and single-stack clusters have always filled `podIP`; the API
|
|
272
|
+
* server sets `podIP` to the first entry of `podIPs` on every cluster that
|
|
273
|
+
* sets either, so {@link readPodIP} prefers it and falls back.
|
|
274
|
+
*/
|
|
275
|
+
export interface PodResource {
|
|
276
|
+
readonly metadata?: KubernetesObjectMeta;
|
|
277
|
+
readonly status?: {
|
|
278
|
+
readonly phase?: string;
|
|
279
|
+
readonly podIP?: string;
|
|
280
|
+
readonly podIPs?: readonly {
|
|
281
|
+
readonly ip?: string;
|
|
282
|
+
}[];
|
|
283
|
+
/**
|
|
284
|
+
* `status.conditions` — read on ONE path only, and never on a healthy
|
|
285
|
+
* one: after an acquire has already run out of readiness budget,
|
|
286
|
+
* `PodScheduled=False` with reason `Unschedulable` is what separates
|
|
287
|
+
* "the cluster has no room" from "the sandbox is just slow". Nothing
|
|
288
|
+
* waits on a pod condition: `Ready` here is the kubelet's view of the
|
|
289
|
+
* container, and readiness on this backend is the Sandbox's own
|
|
290
|
+
* `Ready`, which is what {@link isConditionTrue} is called with
|
|
291
|
+
* everywhere else. See `index.ts`'s `diagnoseUnreadyPod`.
|
|
292
|
+
*/
|
|
293
|
+
readonly conditions?: readonly KubernetesCondition[];
|
|
294
|
+
/**
|
|
295
|
+
* `status.containerStatuses` — read on the same one path, for the same
|
|
296
|
+
* one question. A container stuck in `waiting` with an image-pull
|
|
297
|
+
* reason is a permanent failure wearing the clothes of a slow start,
|
|
298
|
+
* and it is the only one of those this backend can name from the API.
|
|
299
|
+
*/
|
|
300
|
+
readonly containerStatuses?: readonly PodContainerStatus[];
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
/** The single `status.containerStatuses` field the diagnosis above reads. */
|
|
304
|
+
export interface PodContainerStatus {
|
|
305
|
+
readonly name?: string;
|
|
306
|
+
readonly state?: {
|
|
307
|
+
readonly waiting?: {
|
|
308
|
+
readonly reason?: string;
|
|
309
|
+
readonly message?: string;
|
|
310
|
+
};
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
/** The pod's own address, whichever of the two fields this cluster fills. */
|
|
314
|
+
export declare function readPodIP(pod: PodResource | undefined): string | undefined;
|
|
315
|
+
export interface PodListResource {
|
|
316
|
+
readonly items?: readonly PodResource[];
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* A pod whose uid is still worth binding to: not being deleted, and not in a
|
|
320
|
+
* phase it cannot leave.
|
|
321
|
+
*
|
|
322
|
+
* The case this exists for is resume. A resumed sandbox's pod keeps the
|
|
323
|
+
* SAME NAME and gets a new uid and a new IP, so while the outgoing pod is
|
|
324
|
+
* terminating a `GET` by that name answers with the pod on its way out, and a
|
|
325
|
+
* list by the sandbox's selector returns every pod still carrying its labels,
|
|
326
|
+
* that one included. Binding to the terminating pod's uid produces a token the
|
|
327
|
+
* new agent refuses, and the failure arrives as a flat `unauthorized` with
|
|
328
|
+
* nothing pointing at the race that caused it.
|
|
329
|
+
*/
|
|
330
|
+
export declare function isPodLive(pod: PodResource | undefined): boolean;
|
|
331
|
+
/**
|
|
332
|
+
* A pod whose containers have stopped: it is still an object, and nothing in
|
|
333
|
+
* it is executing any more.
|
|
334
|
+
*
|
|
335
|
+
* NOT the negation of {@link isPodLive}, and the gap between the two is the
|
|
336
|
+
* whole point. A terminating pod — `deletionTimestamp` set, phase still
|
|
337
|
+
* `Running` — is not live (never bind to it: its uid is about to stop being
|
|
338
|
+
* a valid token) and not stopped either (its process is still running, and on
|
|
339
|
+
* a workspace it is still writing to the caller's block device until it exits
|
|
340
|
+
* or `terminationGracePeriodSeconds` runs out). A suspend that treated the
|
|
341
|
+
* timestamp as "gone" would resolve mid-drain and promise a quiesced disk it
|
|
342
|
+
* had not waited for.
|
|
343
|
+
*/
|
|
344
|
+
export declare function isPodStopped(pod: PodResource | undefined): boolean;
|
|
345
|
+
/**
|
|
346
|
+
* Backend-owned pod label naming which `SandboxTemplate` a Sandbox's pod was
|
|
347
|
+
* built from.
|
|
348
|
+
*
|
|
349
|
+
* agent-sandbox's OWN template-adoption controller selects pods by a
|
|
350
|
+
* controller-owned label, `agents.x-k8s.io/sandbox-template-ref-hash` — but
|
|
351
|
+
* that label is written only onto a Sandbox ADOPTED out of a
|
|
352
|
+
* `SandboxWarmPool` (the controller re-parents ownership and re-labels on
|
|
353
|
+
* bind). A Sandbox this backend POSTs directly (the pool-less path in
|
|
354
|
+
* `index.ts`'s `buildSandboxBody`) is never adopted, so it never gets that
|
|
355
|
+
* label — a direct Sandbox's pod would carry nothing a `NetworkPolicy`
|
|
356
|
+
* could reliably select it by. This backend writes its own label instead, on
|
|
357
|
+
* every Sandbox it creates, pooled or direct, so `egress-policy.ts`'s
|
|
358
|
+
* translated `NetworkPolicy` has one selector that always matches.
|
|
359
|
+
*
|
|
360
|
+
* `namzu.ai` matches the published domain (`packages/cli/package.json`'s
|
|
361
|
+
* `homepage`); there is no pre-existing Kubernetes label or annotation
|
|
362
|
+
* prefix anywhere in this repo to follow instead — the closest existing
|
|
363
|
+
* convention, `NAMZU_AGENT_*` / `NAMZU_SANDBOX_*` env vars, is not a
|
|
364
|
+
* label-safe shape.
|
|
365
|
+
*
|
|
366
|
+
* W8's `SandboxTemplate` manifests MUST set this same label (value = the
|
|
367
|
+
* template's own name) on their `podTemplate.metadata.labels`, so a POOLED
|
|
368
|
+
* sandbox's pod carries it too — the pool's pods are built from that
|
|
369
|
+
* template's `podTemplate` directly, not through `buildSandboxBody`, so
|
|
370
|
+
* nothing here can put it there for them. Skipping that step means a
|
|
371
|
+
* translated `NetworkPolicy`'s `podSelector` matches only sandboxes this
|
|
372
|
+
* backend created directly and none of the pooled ones — see
|
|
373
|
+
* `docs/sdk/kubernetes-sandbox.md`'s egress section.
|
|
374
|
+
*/
|
|
375
|
+
export declare const SANDBOX_TEMPLATE_LABEL_KEY = "sandbox.namzu.ai/template";
|
|
376
|
+
/**
|
|
377
|
+
* Backend-owned annotation naming when a Sandbox's `spec.operatingMode` was
|
|
378
|
+
* last changed BY THIS BACKEND, RFC 3339.
|
|
379
|
+
*
|
|
380
|
+
* It exists because nothing already on the object answers the question, and
|
|
381
|
+
* an inventory that never wakes a workspace is the reason to ask it: a
|
|
382
|
+
* retention pass deleting the workspaces nobody has resumed for a month reads
|
|
383
|
+
* this and `metadata.creationTimestamp` and nothing else.
|
|
384
|
+
*
|
|
385
|
+
* The obvious candidate is the controller's own `Suspended` condition and its
|
|
386
|
+
* `lastTransitionTime`, and upstream's `sandbox_types.go` rules it out in the
|
|
387
|
+
* same breath it documents it: "the controller does not currently remove this
|
|
388
|
+
* condition when the Sandbox is resumed", so after a resume the condition is
|
|
389
|
+
* still True and its timestamp still names the suspend that preceded it. The
|
|
390
|
+
* `Ready` condition's timestamp is no better — it moves for every pod that
|
|
391
|
+
* comes and goes, a crash-restart included, and a workspace whose pod
|
|
392
|
+
* restarted has not changed operating mode at all.
|
|
393
|
+
*
|
|
394
|
+
* So the two patches that DO change the mode stamp the moment they were sent,
|
|
395
|
+
* and the value is exactly that: the host's clock at the moment it asked, not
|
|
396
|
+
* the cluster's at the moment it applied. It is an inventory column, never a
|
|
397
|
+
* lock or an ordering, and nothing in this backend reads it back to make a
|
|
398
|
+
* decision. A Sandbox whose mode has never been changed since it was created
|
|
399
|
+
* carries no annotation at all, and is reported without one rather than with
|
|
400
|
+
* a guess.
|
|
401
|
+
*
|
|
402
|
+
* Same prefix as {@link SANDBOX_TEMPLATE_LABEL_KEY}, for the same reason.
|
|
403
|
+
*/
|
|
404
|
+
export declare const OPERATING_MODE_CHANGED_AT_ANNOTATION_KEY = "sandbox.namzu.ai/operating-mode-changed-at";
|
|
405
|
+
/**
|
|
406
|
+
* Backend-owned annotation carrying the HOLDER EPOCH: a decimal integer the
|
|
407
|
+
* host raises whenever authority over this workspace moves to another
|
|
408
|
+
* process.
|
|
409
|
+
*
|
|
410
|
+
* A workspace id is a name, not a lock, and a host that drives one workspace
|
|
411
|
+
* from more than one process has to decide which of them may suspend, resume
|
|
412
|
+
* or delete it. Checking its own epoch and then calling `suspend()` does not
|
|
413
|
+
* close the race, because the write that follows is a separate request and
|
|
414
|
+
* the API server accepts it. So the epoch is stored HERE, on the object every
|
|
415
|
+
* lifecycle write targets, and every such write carries it as a condition in
|
|
416
|
+
* the same request — see {@link buildHolderEpochPatch}.
|
|
417
|
+
*
|
|
418
|
+
* The rule: a write carrying epoch `e` applies when the stored epoch is `<=
|
|
419
|
+
* e`, and sets the stored epoch to `e` in the same request. A stored epoch
|
|
420
|
+
* greater than `e` refuses it. An object with NO annotation reads as 0, so
|
|
421
|
+
* every workspace created before this existed accepts its first
|
|
422
|
+
* epoch-carrying write.
|
|
423
|
+
*
|
|
424
|
+
* It is the caller's number, never this backend's: nothing here invents,
|
|
425
|
+
* increments or persists an epoch of its own, and a call that passes none
|
|
426
|
+
* sends exactly the requests it always sent.
|
|
427
|
+
*
|
|
428
|
+
* Same prefix as {@link SANDBOX_TEMPLATE_LABEL_KEY} and
|
|
429
|
+
* {@link OPERATING_MODE_CHANGED_AT_ANNOTATION_KEY}, for the same reason.
|
|
430
|
+
*/
|
|
431
|
+
export declare const HOLDER_EPOCH_ANNOTATION_KEY = "sandbox.namzu.ai/holder-epoch";
|
|
432
|
+
/**
|
|
433
|
+
* Backend-owned annotation carrying a hash of the pod template a Sandbox was
|
|
434
|
+
* last built with.
|
|
435
|
+
*
|
|
436
|
+
* A Sandbox's `spec.podTemplate` is a COPY of the SandboxTemplate's, taken
|
|
437
|
+
* once, and the controller rebuilds every replacement pod from that copy
|
|
438
|
+
* rather than from the template — so a workspace kept for weeks runs the pod
|
|
439
|
+
* spec it was created with, and an edit to the template (a new image tag, a
|
|
440
|
+
* memory limit, a grace period, an env entry) reaches only workspaces created
|
|
441
|
+
* after it. Nothing on the object answers "is this copy still the template's
|
|
442
|
+
* current one": the two are separate objects with separate
|
|
443
|
+
* `resourceVersion`s, and comparing the templates field by field on every
|
|
444
|
+
* open would be a second, weaker copy of the overlay rules.
|
|
445
|
+
*
|
|
446
|
+
* So the value is a hash of exactly what was written: the template's
|
|
447
|
+
* `podTemplate` AFTER this backend's own overlays (the template label and the
|
|
448
|
+
* configured `runtimeClassName`), which is the object the Sandbox carries.
|
|
449
|
+
* Hashing before the overlays would report drift on every workspace whose
|
|
450
|
+
* RuntimeClass this backend chose.
|
|
451
|
+
*
|
|
452
|
+
* Written by the workspace paths only — the create POST and the refresh patch
|
|
453
|
+
* — and read back as `templateRevision` on a handle. A task sandbox is
|
|
454
|
+
* ephemeral and has nothing to drift from, so its create body is unchanged.
|
|
455
|
+
* A workspace created before this existed carries no annotation and reports
|
|
456
|
+
* `templateRevision: undefined`, which reads honestly as "unknown", never as
|
|
457
|
+
* "current".
|
|
458
|
+
*
|
|
459
|
+
* Same prefix as {@link SANDBOX_TEMPLATE_LABEL_KEY}, for the same reason.
|
|
460
|
+
*/
|
|
461
|
+
export declare const POD_TEMPLATE_HASH_ANNOTATION_KEY = "sandbox.namzu.ai/pod-template-hash";
|
|
462
|
+
/**
|
|
463
|
+
* `sha256:<hex>` over the pod template, for
|
|
464
|
+
* {@link POD_TEMPLATE_HASH_ANNOTATION_KEY}.
|
|
465
|
+
*
|
|
466
|
+
* It is an identity, not a checksum of anything security-relevant: two hosts
|
|
467
|
+
* running the same release against the same template must compute the same
|
|
468
|
+
* string, and a template edit must change it. Nothing here compares it
|
|
469
|
+
* against a value an untrusted party chose.
|
|
470
|
+
*/
|
|
471
|
+
export declare function podTemplateHash(podTemplate: SandboxPodTemplate): string;
|
|
472
|
+
/**
|
|
473
|
+
* One RFC 6902 operation, in the only three shapes this backend sends.
|
|
474
|
+
*
|
|
475
|
+
* `test` is the condition, `add` is every mutation. `add` rather than
|
|
476
|
+
* `replace` throughout: RFC 6902 §4.1 says that on a JSON object member `add`
|
|
477
|
+
* creates the member when it is missing and replaces its value when it is
|
|
478
|
+
* present, while §4.3's `replace` fails outright on a missing one — and
|
|
479
|
+
* `spec.operatingMode` is absent on a Sandbox that has never been suspended,
|
|
480
|
+
* as is the epoch annotation on every workspace created before this release.
|
|
481
|
+
* A `replace` would turn both of those ordinary cases into a rejected patch.
|
|
482
|
+
*
|
|
483
|
+
* So where a design or an issue says the wire carries `replace /spec/…`, this
|
|
484
|
+
* is that write: on a member that is already there the two operations are the
|
|
485
|
+
* same write, and on one that is not, only this one lands.
|
|
486
|
+
*/
|
|
487
|
+
export interface JsonPatchOperation {
|
|
488
|
+
readonly op: 'test' | 'add';
|
|
489
|
+
readonly path: string;
|
|
490
|
+
readonly value: unknown;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* One JSON Pointer reference token (RFC 6901 §3): `~` becomes `~0` and `/`
|
|
494
|
+
* becomes `~1`, in that order — the reverse order would turn a literal `~1`
|
|
495
|
+
* into a slash.
|
|
496
|
+
*
|
|
497
|
+
* An annotation key always contains a `/` (`sandbox.namzu.ai/holder-epoch`),
|
|
498
|
+
* so the pointer to one is unusable without this.
|
|
499
|
+
*/
|
|
500
|
+
export declare function escapeJsonPointerSegment(token: string): string;
|
|
501
|
+
/** Pointer to one annotation on an object's own metadata. */
|
|
502
|
+
export declare function annotationPointer(key: string): string;
|
|
503
|
+
/** What one read of an object saw about its holder epoch. */
|
|
504
|
+
export interface HolderEpochReading {
|
|
505
|
+
/**
|
|
506
|
+
* The stored epoch: the annotation parsed, or `0` when there is none.
|
|
507
|
+
*
|
|
508
|
+
* `undefined` means the annotation is PRESENT and is not a decimal
|
|
509
|
+
* integer, which no version of this backend writes. It is reported as
|
|
510
|
+
* unreadable rather than as 0 on purpose: reading a value this code does
|
|
511
|
+
* not understand as "nobody holds this workspace" would let a write
|
|
512
|
+
* overwrite a fence somebody else established, which is the one thing the
|
|
513
|
+
* annotation exists to prevent.
|
|
514
|
+
*/
|
|
515
|
+
readonly epoch?: number;
|
|
516
|
+
/**
|
|
517
|
+
* The annotation exactly as stored, and the value the `test` clause
|
|
518
|
+
* carries. Absent when the object has no such annotation.
|
|
519
|
+
*/
|
|
520
|
+
readonly annotation?: string;
|
|
521
|
+
/** `metadata.annotations` existed at all — decides which `add` is sent. */
|
|
522
|
+
readonly hasAnnotations: boolean;
|
|
523
|
+
/** `metadata.resourceVersion`, for the fallback `test` and a fenced DELETE. */
|
|
524
|
+
readonly resourceVersion?: string;
|
|
525
|
+
}
|
|
526
|
+
/** Read {@link HOLDER_EPOCH_ANNOTATION_KEY} off an object's metadata. */
|
|
527
|
+
export declare function readHolderEpoch(meta: KubernetesObjectMeta | undefined): HolderEpochReading;
|
|
528
|
+
/**
|
|
529
|
+
* Read {@link POD_TEMPLATE_HASH_ANNOTATION_KEY} off an object's metadata.
|
|
530
|
+
*
|
|
531
|
+
* Absent — an object created before this existed, or one an older release
|
|
532
|
+
* refreshed — is `undefined`, which reads as "unknown" everywhere it is
|
|
533
|
+
* consumed. It is deliberately never compared as an empty string: a
|
|
534
|
+
* revision nobody recorded is not a revision that differs.
|
|
535
|
+
*/
|
|
536
|
+
export declare function readPodTemplateHash(meta: KubernetesObjectMeta | undefined): string | undefined;
|
|
537
|
+
/** A stored epoch of `epoch` or lower lets a write carrying `epoch` through. */
|
|
538
|
+
export declare function holderEpochAllows(reading: HolderEpochReading, epoch: number): boolean;
|
|
539
|
+
/** What {@link buildHolderEpochPatch} is asked to write, and under what condition. */
|
|
540
|
+
export interface HolderEpochPatchInput {
|
|
541
|
+
/** The metadata the GET returned. The condition is built from THIS read. */
|
|
542
|
+
readonly reading: HolderEpochReading;
|
|
543
|
+
/**
|
|
544
|
+
* The epoch the write carries, and the one it stores.
|
|
545
|
+
*
|
|
546
|
+
* ABSENT is the unfenced conditional write: no epoch clause is tested and
|
|
547
|
+
* no epoch annotation is written, and {@link tests} then carries the whole
|
|
548
|
+
* condition — which is why an epoch-less call with no `tests` is refused
|
|
549
|
+
* below rather than sent unconditionally. A workspace refresh is the one
|
|
550
|
+
* caller: its condition is `spec.operatingMode`, and a host that has not
|
|
551
|
+
* opted into the fence must not acquire one by asking for a new pod
|
|
552
|
+
* template.
|
|
553
|
+
*/
|
|
554
|
+
readonly epoch?: number;
|
|
555
|
+
/**
|
|
556
|
+
* Further `test` clauses composed into the SAME body.
|
|
557
|
+
*
|
|
558
|
+
* This is the seam a second condition uses instead of a second request: a
|
|
559
|
+
* write that also has to assert, say, `spec.operatingMode` passes its
|
|
560
|
+
* clause here and the object is still written under one atomic patch. Two
|
|
561
|
+
* call sites each sending their own conditional patch would be two writes
|
|
562
|
+
* and two chances to lose a race between them.
|
|
563
|
+
*/
|
|
564
|
+
readonly tests?: readonly JsonPatchOperation[];
|
|
565
|
+
/** Written to `spec.operatingMode`; omitted leaves the mode alone. */
|
|
566
|
+
readonly operatingMode?: 'Running' | 'Suspended';
|
|
567
|
+
/**
|
|
568
|
+
* RFC 3339 stamp for {@link OPERATING_MODE_CHANGED_AT_ANNOTATION_KEY}.
|
|
569
|
+
* Passed only by a write that actually CHANGES the mode — the annotation
|
|
570
|
+
* says when the mode last changed, and a write that merely restamps the
|
|
571
|
+
* epoch has not changed it.
|
|
572
|
+
*/
|
|
573
|
+
readonly operatingModeChangedAt?: string;
|
|
574
|
+
/**
|
|
575
|
+
* Further annotations written in the SAME body, merged with the epoch's
|
|
576
|
+
* and the mode stamp's rather than sent after them.
|
|
577
|
+
*
|
|
578
|
+
* {@link POD_TEMPLATE_HASH_ANNOTATION_KEY} is the only caller: the hash
|
|
579
|
+
* has to land with the pod template it describes, or a patch that applied
|
|
580
|
+
* half of the pair would leave the object claiming a revision it is not
|
|
581
|
+
* running.
|
|
582
|
+
*/
|
|
583
|
+
readonly annotations?: Readonly<Record<string, string>>;
|
|
584
|
+
/**
|
|
585
|
+
* Written to `spec.podTemplate`, replacing it WHOLE — which is the reason
|
|
586
|
+
* this is a JSON Patch at all. A merge patch recurses into maps, so a
|
|
587
|
+
* `nodeSelector` entry the template dropped would survive in the object
|
|
588
|
+
* and the pod would keep a constraint nobody can see in the template any
|
|
589
|
+
* more.
|
|
590
|
+
*/
|
|
591
|
+
readonly podTemplate?: SandboxPodTemplate;
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* The one conditional-write builder this backend has, and the only place a
|
|
595
|
+
* JSON Patch body is composed.
|
|
596
|
+
*
|
|
597
|
+
* Every clause is decided from ONE read, and the whole thing goes up as one
|
|
598
|
+
* request: the condition and the mutation are in the same body, so there is
|
|
599
|
+
* no window between checking and writing for another holder to fit into.
|
|
600
|
+
*
|
|
601
|
+
* Three shapes, decided by what that read saw:
|
|
602
|
+
*
|
|
603
|
+
* - the object carries the epoch annotation ⇒ `test` it by its exact stored
|
|
604
|
+
* string, then `add` the new value over it;
|
|
605
|
+
* - the object carries annotations but not this one ⇒ `test`
|
|
606
|
+
* `/metadata/resourceVersion` instead, then `add` the member;
|
|
607
|
+
* - the object carries no `metadata.annotations` at all ⇒ the same
|
|
608
|
+
* `resourceVersion` test, then `add` the map whole, because there is no
|
|
609
|
+
* member to add one to.
|
|
610
|
+
*
|
|
611
|
+
* The `resourceVersion` fallback is the migration case and nothing more: it
|
|
612
|
+
* fires once, on a workspace created before this release, and from the first
|
|
613
|
+
* epoch write onwards the annotation is what is tested. That matters because
|
|
614
|
+
* a controller status write moves `resourceVersion` without touching the
|
|
615
|
+
* annotation, and under the annotation test those are simply not conditions
|
|
616
|
+
* this write is interested in.
|
|
617
|
+
*
|
|
618
|
+
* A call carrying NO epoch skips all three: it tests only what
|
|
619
|
+
* {@link HolderEpochPatchInput.tests} carries and writes no epoch annotation,
|
|
620
|
+
* which is how a workspace refresh conditions itself on `spec.operatingMode`
|
|
621
|
+
* without fencing a host that never asked for a fence.
|
|
622
|
+
*
|
|
623
|
+
* Every `test` precedes every mutation, which RFC 6902 requires of a
|
|
624
|
+
* condition: operations apply in order, so a `test` written after an `add`
|
|
625
|
+
* would be testing this patch's own work. Mutations go up in a fixed order —
|
|
626
|
+
* annotations, then `spec.podTemplate`, then `spec.operatingMode` — so the
|
|
627
|
+
* body a given input produces is one body and a test can assert it.
|
|
628
|
+
*/
|
|
629
|
+
export declare function buildHolderEpochPatch(input: HolderEpochPatchInput): readonly JsonPatchOperation[];
|
|
630
|
+
/** `{ [SANDBOX_TEMPLATE_LABEL_KEY]: sandboxTemplateName }`, as a matchLabels-ready object. */
|
|
631
|
+
export declare function sandboxTemplateLabel(sandboxTemplateName: string): Readonly<Record<string, string>>;
|
|
632
|
+
/** Core `NetworkPolicy` — a stock resource, no CRD. */
|
|
633
|
+
export declare const CORE_NETWORK_POLICY_API_GROUP = "networking.k8s.io";
|
|
634
|
+
export declare const CORE_NETWORK_POLICY_API_VERSION = "v1";
|
|
635
|
+
/**
|
|
636
|
+
* Cilium's FQDN-capable policy CRD. Only reached when
|
|
637
|
+
* `KubernetesEgressConfig.engine` is explicitly `'cilium'` — see
|
|
638
|
+
* `egress-policy.ts`.
|
|
639
|
+
*/
|
|
640
|
+
export declare const CILIUM_NETWORK_POLICY_API_GROUP = "cilium.io";
|
|
641
|
+
export declare const CILIUM_NETWORK_POLICY_API_VERSION = "v2";
|
|
642
|
+
export declare function claimCollectionPath(namespace: string): string;
|
|
643
|
+
export declare function claimPath(namespace: string, name: string): string;
|
|
644
|
+
/** The claims collection, narrowed to a `labelSelector` — same shape as {@link podListPath}. */
|
|
645
|
+
export declare function claimListPath(namespace: string, labelSelector: string): string;
|
|
646
|
+
export declare function warmPoolPath(namespace: string, name: string): string;
|
|
647
|
+
export declare function sandboxCollectionPath(namespace: string): string;
|
|
648
|
+
export declare function sandboxPath(namespace: string, name: string): string;
|
|
649
|
+
export declare function sandboxTemplatePath(namespace: string, name: string): string;
|
|
650
|
+
/**
|
|
651
|
+
* The PVC the controller creates for one `volumeClaimTemplates` entry:
|
|
652
|
+
* `<entry name>-<sandbox name>`, in the Sandbox's own namespace.
|
|
653
|
+
*
|
|
654
|
+
* Written out here rather than derived at each call site because it is a
|
|
655
|
+
* NAME the controller owns, not one this backend chooses — a release that
|
|
656
|
+
* changes it breaks every read of it at once, and the one place to notice
|
|
657
|
+
* that is a function whose whole body is the convention.
|
|
658
|
+
*/
|
|
659
|
+
export declare function persistentVolumeClaimPath(namespace: string, sandboxName: string, claimTemplateName: string): string;
|
|
660
|
+
export declare function podPath(namespace: string, name: string): string;
|
|
661
|
+
/** The whole pods collection, unfiltered. Read by `readKubernetesTaskCapacity` alone. */
|
|
662
|
+
export declare function podCollectionPath(namespace: string): string;
|
|
663
|
+
export declare function podListPath(namespace: string, labelSelector: string): string;
|
|
664
|
+
export declare function networkPolicyCollectionPath(namespace: string): string;
|
|
665
|
+
export declare function networkPolicyPath(namespace: string, name: string): string;
|
|
666
|
+
export declare function ciliumNetworkPolicyCollectionPath(namespace: string): string;
|
|
667
|
+
export declare function ciliumNetworkPolicyPath(namespace: string, name: string): string;
|
|
668
|
+
/**
|
|
669
|
+
* Core Kubernetes' own admission-policy resources — `ValidatingAdmissionPolicy`
|
|
670
|
+
* and its binding, both CLUSTER-scoped and both stock since v1.30, so a
|
|
671
|
+
* cluster that serves this group needs nothing installed.
|
|
672
|
+
*
|
|
673
|
+
* Read by exactly one caller: the per-sandbox policy fence
|
|
674
|
+
* (`per-sandbox-policy.ts`), which refuses to write a `CiliumNetworkPolicy`
|
|
675
|
+
* at all unless an operator has applied the policy that bounds what this
|
|
676
|
+
* host may write. Never written by this backend — the fence is the operator's
|
|
677
|
+
* object, reviewed by whoever has cluster-admin, and a host that could create
|
|
678
|
+
* its own fence would not have one.
|
|
679
|
+
*/
|
|
680
|
+
export declare const ADMISSION_REGISTRATION_API_GROUP = "admissionregistration.k8s.io";
|
|
681
|
+
export declare const ADMISSION_REGISTRATION_API_VERSION = "v1";
|
|
682
|
+
export declare function validatingAdmissionPolicyPath(name: string): string;
|
|
683
|
+
export declare function validatingAdmissionPolicyBindingPath(name: string): string;
|
|
684
|
+
/**
|
|
685
|
+
* An `ownerReferences` entry, as this backend writes it.
|
|
686
|
+
*
|
|
687
|
+
* `controller` and `blockOwnerDeletion` are deliberately ABSENT rather than
|
|
688
|
+
* written as `false`. `blockOwnerDeletion: true` would make the API server's
|
|
689
|
+
* `OwnerReferencesPermissionEnforcement` admission plugin demand `update` on
|
|
690
|
+
* the OWNER's `finalizers` subresource — a verb no Role in this repo grants
|
|
691
|
+
* and no host needs — so a field whose only legal value here is `false` is
|
|
692
|
+
* one the body is better off not carrying at all. Garbage collection does not
|
|
693
|
+
* need either field: a dependent whose owners are all gone is deleted, owning
|
|
694
|
+
* controller or not.
|
|
695
|
+
*/
|
|
696
|
+
export interface KubernetesOwnerReference {
|
|
697
|
+
readonly apiVersion: string;
|
|
698
|
+
readonly kind: string;
|
|
699
|
+
readonly name: string;
|
|
700
|
+
readonly uid: string;
|
|
701
|
+
}
|
|
702
|
+
//# sourceMappingURL=objects.d.ts.map
|