@mark1russell7/kind 0.4.1 → 0.5.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/README.md +15 -3
- package/build/index.d.ts +46 -10
- package/build/index.js +218 -50
- package/build/index.js.map +1 -1
- package/build/{judge-DMuI3aDn.d.ts → judge-BS9pk96o.d.ts} +2 -2
- package/build/judge.d.ts +1 -1
- package/image/watch.mjs +30 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -90,6 +90,9 @@ The tests read three real records of `/1` (`test/records/`): a cell that holds,
|
|
|
90
90
|
- `listener_modified` counts each update of a listener, and `listener_in_place_updated` counts each update of its filter chains only.
|
|
91
91
|
- Each change of the counters gives an `lds` line with the start of the poll before it. The first line of a poller has no poll before it, thus it is no update (`updatesOf`).
|
|
92
92
|
- **The injector of each node** is the image of the harness, in the PID namespace of the node. `crictl` on the node gives the PID of a container, and `nsenter -n` enters the network namespace of its pod. The injector of the harness then applies its rules there.
|
|
93
|
+
- From 0.5.0, the target `servers` puts a rule in the namespace of each replica, with each client pod as a peer. A REJECT resets only the sender of the segment that it matches, thus the rule goes where the segments of the clients arrive.
|
|
94
|
+
- The rules of all replicas commit at one instant on CLOCK_MONOTONIC (`--at`, 700 ms after the start). Thus no client meets one replica with the rule and another without it. The clear uses the PIDs of the fault again, thus the heal comes soon after its hold.
|
|
95
|
+
- The lab reads the tables of each replica with their counters (`tables`): the nat table after the fault, and the filter table before the clear. A `tables` line in the record shows if the rule matched the clients beside the rules of istio-cni.
|
|
93
96
|
- **The watcher** is a container on the network of the cluster. It watches the pods, the EndpointSlices and the events through the API, and it follows the log of each pod of the cell.
|
|
94
97
|
- From 0.3.0, it also follows the sidecar of Istio (`istio-proxy`), and a pod line gives the state of the sidecar (`sc`). Each line of the sidecar becomes a `proxy` line, stamped at its arrival. Its own stamps are of the wall clock, thus no band reads them.
|
|
95
98
|
- The access line of a call that succeeded at its first try is only counted, in a `proxy-ok` line each second. The record keeps each failure, each retry and each local reply, with its response flags and its upstream host.
|
|
@@ -118,15 +121,22 @@ A declaration that can be null changes the type `KindCell`. That is a breaking c
|
|
|
118
121
|
|
|
119
122
|
From 0.3.0, a cell can name a mesh (`mesh`): Istio 1.31.3 in its cluster. This is the template `T15`, the lifecycle of T07 through two mesh hops.
|
|
120
123
|
|
|
121
|
-
- **The field.** `mesh.flavor` is the install: `sidecar` is istiod only. `mesh.mode` is the data plane of the pods of the cell.
|
|
124
|
+
- **The field.** `mesh.flavor` is the install: `sidecar` is istiod only, and `ambient` is istiod, istio-cni and ztunnel. `mesh.mode` is the data plane of the pods of the cell.
|
|
122
125
|
- **The cluster.** The cells of one kube-proxy mode, one node count and one mesh flavor share a cluster. `shardOf` keeps them together, and in each mode the cells with no mesh come first.
|
|
123
|
-
- **The install.** `installMesh` writes the IstioOperator of the flavor (`istioOperator`), and `istioctl install` applies it with the kubeconfig of the cluster. Then the lab waits for
|
|
126
|
+
- **The install.** `installMesh` writes the IstioOperator of the flavor (`istioOperator`), and `istioctl install` applies it with the kubeconfig of the cluster. Then the lab waits for each workload of the flavor (`MESH_WORKLOADS`). A failed install gives the pods of istio-system and the log of istio-cni in its error.
|
|
124
127
|
- **The values.** The profile is `minimal`, because the profile `default` also has an ingress gateway. The pod of istiod requests 256 MiB, with one replica and no autoscaler. The access log of each sidecar goes to its output as JSON (`ACCESS_LOG_FORMAT`).
|
|
125
128
|
- **The images.** `ISTIO_IMAGES` pins istiod and the proxy by the digest of their index on Docker Hub. The lab pulls each one by its digest and tags it with that digest (`localPin`). Then `kind load` puts it on each node.
|
|
126
129
|
- **No pull.** Each image is a full reference, thus the charts use it as it is. The init container of the injector gets the image of the proxy too. `imagePullPolicy` is `Never`, thus an image that the lab did not load fails the install.
|
|
127
130
|
- **The namespace.** In the sidecar mode, the namespace of the cell has the label `istio-injection=enabled` (`namespaceObject`). The injector then puts a native sidecar in each pod: an init container that runs beside the app, because each kubelet is at least 1.33.
|
|
128
131
|
- **The probes.** The injector changes the gRPC probe of grpc-go into an HTTP probe of pilot-agent. Then pilot-agent sends the gRPC check to the app, with the service of the probe.
|
|
129
132
|
|
|
133
|
+
From 0.5.0, the lab installs the flavor `ambient` too:
|
|
134
|
+
|
|
135
|
+
- **The values.** The profile is `ambient`: istiod, istio-cni and ztunnel on each node, and no gateway. `ISTIO_IMAGES` also pins istio-cni and ztunnel, and `MESH_IMAGES` gives the images of each flavor. The chart of ztunnel reads only its own `imagePullPolicy`, thus the operator sets it to `Never` there too.
|
|
136
|
+
- **The namespace.** In the ambient mode, the namespace of the cell has the label `istio.io/dataplane-mode=ambient`. Then istio-cni redirects the traffic of each pod to ztunnel in the network namespace of the pod. A pod has no sidecar.
|
|
137
|
+
- **The kernel.** istio-cni writes iptables rules in each pod and on each node. Thus `kind.yml` loads their modules first, and an ambient cell waits when one does not load.
|
|
138
|
+
- **The log of ztunnel.** For a cell in the ambient mode, the watcher follows each ztunnel pod in istio-system (`followsZtunnel`). Each line of its log becomes a `ztunnel` line, stamped at its arrival, and each ztunnel pod is a `ztunnel-pod` line. No band reads them.
|
|
139
|
+
|
|
130
140
|
A new member of the union `Template` breaks a reader that switches on it. That is a breaking change before 1.0.0, thus the version is 0.3.0. The record format stays `/2`, with the optional field `mesh`. A judge before 0.3.0 cannot read a record of T15.
|
|
131
141
|
|
|
132
142
|
## The image
|
|
@@ -156,7 +166,7 @@ The lab image (`image/`) holds the service of a cell on `@mark1russell7/service`
|
|
|
156
166
|
| `crash` | SIGKILL of the process of the service, from the node |
|
|
157
167
|
| `liveness-restart` | the flag file that fails the liveness check of the first replica |
|
|
158
168
|
| `freeze-process` | SIGSTOP and SIGCONT: of kube-proxy on one node across a deletion (T07), or of Redis (T18) |
|
|
159
|
-
| `rst`, `blackhole` | the injector of the harness in the namespace of Redis, with each replica as a peer |
|
|
169
|
+
| `rst`, `blackhole` | the injector of the harness in the namespace of Redis, with each replica as a peer. In T15, the injector in the namespace of each replica, with each client pod as a peer (the target `servers`) |
|
|
160
170
|
| `peer-gone-without-close` | a black hole on Redis, then no Redis pod: its namespace goes away with its rules |
|
|
161
171
|
| `app-stall` | the event loop of the first replica blocked |
|
|
162
172
|
| `rollout` | no Redis pod, then a rolling update |
|
|
@@ -209,6 +219,8 @@ From 0.4.0, a window can count from the heal (`heal`), the first act that ends t
|
|
|
209
219
|
- A denial after the window, before the last update after the delete and one more call, is a `tail`. A denied call has no pod, because the sidecar answers it. Thus the judge reads the last update of each server sidecar.
|
|
210
220
|
- `calibrationOf` gives the updates after the apply by node, and the updates after the delete (`heal`).
|
|
211
221
|
|
|
222
|
+
A new member of the union `Target`, `servers`, breaks a reader that switches on it. `KindLab` also has a new method, `tables`, which each implementation must have. Each is a breaking change before 1.0.0, thus the version is 0.5.0. The record format stays `/2`: the lines `tables`, `ztunnel` and `ztunnel-pod` are new lines of a run.
|
|
223
|
+
|
|
212
224
|
A new member of the union `TriggerKind` breaks a reader that switches on it. That is a breaking change before 1.0.0, thus the version is 0.4.0. The record format stays `/2`, with the optional fields `account`, `outcomes`, `updates` and `healUpdates`, and the reference `heal`.
|
|
213
225
|
|
|
214
226
|
A preStop sleep that is longer than the grace is a command in the container (`sh -c "sleep 60"`). The API server refuses a sleep action that is longer than the grace.
|
package/build/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as updatesOf, $t as Replica, A as TriggerV1, At as transitionOf, B as RunCheck, Bt as showBand, C as KindCellResult, Ct as rulesOf, D as PredictionV1, Dt as sliceEmptyOf, E as LoadSample, Et as sliceDropOf, F as KIND_NOISE, Ft as REDIS, G as judgeKind, Gt as KubeletProbe, H as checkRun, Ht as Declaration, I as KindDecision, It as SERVER, J as propagationOf, Jt as MeshMode, K as observationAfter, Kt as Mesh, L as KindVerdict, Lt as bandOf, M as upgrade, Mt as HEALTH_PORT, N as upgradeCell, Nt as LOG_LABEL, O as RunCheckV1, Ot as slicesOf, P as Deleted, Pt as PORT, Q as unitOf, Qt as Reference, R as LAST_SLACK, Rt as seconds, S as KindCellRecordV1, St as restartsAt, T as KindVerdictV1, Tt as signalsOf, U as deletionsOf, Ut as KindCell, V as RunFacts, Vt as Band, W as factsOf, Wt as KindRun, X as referenceOf, Xt as Prediction, Y as propagationSource, Yt as PodSettings, Z as reusesReset, Zt as RedisDeclaration, _ as ClusterManifest, _n as nodeNames, _t as outcomesOf, a as STAGES, an as Workload, at as Request, b as FORMAT_V1, bt as recordsOf, c as readRecord, cn as KindScenario, ct as backendsAt, d as calibrationOf, dn as NodeImage, dt as deletionOf, en as ResetReuse, et as Endpoint, f as cellsTable, fn as cidrOverlap, ft as dropOf, g as BandV1, gn as kubeProxyPatch, gt as kernelCounts, h as timeSpread, hn as kindConfig, ht as intakeOf, i as quantileOf, in as TriggerKind, it as RecordLine, j as judge, jt as CLIENT, k as RunFactsV1, kt as stoppingOf, l as Judged, ln as KubeProxyMode, lt as byClients, m as predictionText, mn as goDuration, mt as failuresOf, n as medianInterval, nn as Template, nt as PodLine, o as kindCellRecordSchema, on as KUBE_PROXY_MODES, ot as RulesLine, p as medianText, pn as cidrRange, pt as emptyOf, q as observationEnd, qt as MeshFlavor, r as middle, rn as Trigger, rt as PropagationSource, s as kindCellRecordV1Schema, sn as KindConfig, st as SliceLine, t as RELATIVE, tn as Stack, tt as Line, u as TimeSpread, un as NODE_IMAGES, ut as byNode, v as EnvironmentManifest, vn as scenario, vt as podsAt, w as KindCellV1, wt as signalOf, x as KindCellRecord, xt as requestsOf, y as FORMAT, yn as scenarioProblems, yt as podsOf, z as Propagated, zt as secondsOf } from "./judge-
|
|
1
|
+
import { $ as updatesOf, $t as Replica, A as TriggerV1, At as transitionOf, B as RunCheck, Bt as showBand, C as KindCellResult, Ct as rulesOf, D as PredictionV1, Dt as sliceEmptyOf, E as LoadSample, Et as sliceDropOf, F as KIND_NOISE, Ft as REDIS, G as judgeKind, Gt as KubeletProbe, H as checkRun, Ht as Declaration, I as KindDecision, It as SERVER, J as propagationOf, Jt as MeshMode, K as observationAfter, Kt as Mesh, L as KindVerdict, Lt as bandOf, M as upgrade, Mt as HEALTH_PORT, N as upgradeCell, Nt as LOG_LABEL, O as RunCheckV1, Ot as slicesOf, P as Deleted, Pt as PORT, Q as unitOf, Qt as Reference, R as LAST_SLACK, Rt as seconds, S as KindCellRecordV1, St as restartsAt, T as KindVerdictV1, Tt as signalsOf, U as deletionsOf, Ut as KindCell, V as RunFacts, Vt as Band, W as factsOf, Wt as KindRun, X as referenceOf, Xt as Prediction, Y as propagationSource, Yt as PodSettings, Z as reusesReset, Zt as RedisDeclaration, _ as ClusterManifest, _n as nodeNames, _t as outcomesOf, a as STAGES, an as Workload, at as Request, b as FORMAT_V1, bt as recordsOf, c as readRecord, cn as KindScenario, ct as backendsAt, d as calibrationOf, dn as NodeImage, dt as deletionOf, en as ResetReuse, et as Endpoint, f as cellsTable, fn as cidrOverlap, ft as dropOf, g as BandV1, gn as kubeProxyPatch, gt as kernelCounts, h as timeSpread, hn as kindConfig, ht as intakeOf, i as quantileOf, in as TriggerKind, it as RecordLine, j as judge, jt as CLIENT, k as RunFactsV1, kt as stoppingOf, l as Judged, ln as KubeProxyMode, lt as byClients, m as predictionText, mn as goDuration, mt as failuresOf, n as medianInterval, nn as Template, nt as PodLine, o as kindCellRecordSchema, on as KUBE_PROXY_MODES, ot as RulesLine, p as medianText, pn as cidrRange, pt as emptyOf, q as observationEnd, qt as MeshFlavor, r as middle, rn as Trigger, rt as PropagationSource, s as kindCellRecordV1Schema, sn as KindConfig, st as SliceLine, t as RELATIVE, tn as Stack, tt as Line, u as TimeSpread, un as NODE_IMAGES, ut as byNode, v as EnvironmentManifest, vn as scenario, vt as podsAt, w as KindCellV1, wt as signalOf, x as KindCellRecord, xt as requestsOf, y as FORMAT, yn as scenarioProblems, yt as podsOf, z as Propagated, zt as secondsOf } from "./judge-BS9pk96o.js";
|
|
2
2
|
import { Quantity } from "@mark1russell7/quantity";
|
|
3
3
|
import { CheckFn, Finding, HealthServer, Poll, Rule, Scenario, ServiceBuilder, ServiceDescription, Stage, Timing, TransportImpl, Verdict } from "@mark1russell7/service";
|
|
4
4
|
import { RedisClient } from "@mark1russell7/service/redis";
|
|
@@ -111,8 +111,11 @@ export declare const hostsOf: (text: string, cluster: string) => ClusterHosts;
|
|
|
111
111
|
* - `server` is the first replica of the service, in the sequence of its name.
|
|
112
112
|
* - `redis` is the Redis pod.
|
|
113
113
|
* - `kube-proxy` is kube-proxy on the first worker.
|
|
114
|
+
* - `servers` is each replica of the service. Only the injector takes it: a rule in the network
|
|
115
|
+
* namespace of each replica, with each client pod as a peer. A REJECT resets only the sender of
|
|
116
|
+
* the segment that it matches, thus the rule goes where the segments of the clients arrive.
|
|
114
117
|
*/
|
|
115
|
-
type Target = "server" | "redis" | "kube-proxy";
|
|
118
|
+
type Target = "server" | "redis" | "kube-proxy" | "servers";
|
|
116
119
|
/** A step is one act of a fault, in the sequence of the plan. */
|
|
117
120
|
type Step = {
|
|
118
121
|
readonly do: "kubectl";
|
|
@@ -200,15 +203,20 @@ export declare const labImage: () => Promise<string>;
|
|
|
200
203
|
/** This version is the release of Istio of the lab: the tag of istioctl and of each image. */
|
|
201
204
|
export declare const ISTIO_VERSION = "1.31.3";
|
|
202
205
|
/**
|
|
203
|
-
* These are the images of
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
206
|
+
* These are the images of Istio, pinned by the digest of their index on Docker Hub (read on
|
|
207
|
+
* 2026-10-24). `gcr.io/istio-release` has no final tag of 1.31. The lab pulls each one by its digest,
|
|
208
|
+
* tags it with that digest (`localPin`), and loads it into each node. Thus no node pulls from a
|
|
209
|
+
* registry. The flavor `sidecar` needs istiod and the proxy. The flavor `ambient` also needs
|
|
210
|
+
* istio-cni and ztunnel (`MESH_IMAGES`).
|
|
207
211
|
*/
|
|
208
212
|
export declare const ISTIO_IMAGES: {
|
|
209
213
|
readonly pilot: string;
|
|
210
214
|
readonly proxy: string;
|
|
215
|
+
readonly cni: string;
|
|
216
|
+
readonly ztunnel: string;
|
|
211
217
|
};
|
|
218
|
+
/** These are the images that each flavor installs, by their names in `ISTIO_IMAGES`. */
|
|
219
|
+
export declare const MESH_IMAGES: Readonly<Record<MeshFlavor, ReadonlyArray<keyof typeof ISTIO_IMAGES>>>;
|
|
212
220
|
/**
|
|
213
221
|
* This format is the JSON access log of each sidecar. Each field is a command of the formatter of
|
|
214
222
|
* Envoy v1.39.4. The fields give the status, the response flags and their details, and the gRPC
|
|
@@ -217,10 +225,12 @@ export declare const ISTIO_IMAGES: {
|
|
|
217
225
|
* (`image/watch.mjs`).
|
|
218
226
|
*/
|
|
219
227
|
export declare const ACCESS_LOG_FORMAT: Readonly<Record<string, string>>;
|
|
220
|
-
/** These are the local tags of the images, after the pull by digest. */
|
|
228
|
+
/** These are the local tags of the images, after the pull by digest. The flavor `ambient` needs `cni` and `ztunnel`. */
|
|
221
229
|
interface MeshImages {
|
|
222
230
|
readonly pilot: string;
|
|
223
231
|
readonly proxy: string;
|
|
232
|
+
readonly cni?: string | undefined;
|
|
233
|
+
readonly ztunnel?: string | undefined;
|
|
224
234
|
}
|
|
225
235
|
/**
|
|
226
236
|
* This function gives the IstioOperator of a flavor. The values that are not the defaults of Istio:
|
|
@@ -232,12 +242,21 @@ interface MeshImages {
|
|
|
232
242
|
* reports the whole runner, and no cell scales istiod.
|
|
233
243
|
* - The access log of each sidecar goes to its output as JSON (`ACCESS_LOG_FORMAT`).
|
|
234
244
|
*
|
|
235
|
-
* The flavor `ambient`
|
|
245
|
+
* The flavor `ambient` has the profile `ambient`: istiod, istio-cni and ztunnel, and no gateway
|
|
246
|
+
* (manifests/profiles/ambient.yaml of Istio 1.31.3). Its two other images are full references too
|
|
247
|
+
* (templates/daemonset.yaml of the chart of istio-cni, lines 93–96, and of ztunnel, lines 65–68).
|
|
248
|
+
* The chart of istio-cni takes the pull policy of `global`. The template of ztunnel reads only its own
|
|
249
|
+
* `imagePullPolicy`, thus the operator sets it there too.
|
|
236
250
|
*/
|
|
237
251
|
export declare const istioOperator: (flavor: MeshFlavor, images: MeshImages) => Record<string, unknown>;
|
|
252
|
+
/** These are the workloads of each flavor in istio-system that the install waits for. */
|
|
253
|
+
export declare const MESH_WORKLOADS: Readonly<Record<MeshFlavor, readonly string[]>>;
|
|
238
254
|
/**
|
|
239
255
|
* This function installs the mesh of a flavor in a cluster with `istioctl install` and the operator
|
|
240
|
-
* of the flavor. Then it waits for
|
|
256
|
+
* of the flavor. Then it waits for each workload of the flavor. The kubeconfig of the cluster goes to
|
|
257
|
+
* istioctl. When the install fails, the error has the pods of istio-system and the last lines of the
|
|
258
|
+
* log of istio-cni. Thus the log of the job shows the cause, for example a module that the kernel of
|
|
259
|
+
* the runner does not have.
|
|
241
260
|
*/
|
|
242
261
|
export declare const installMesh: (cluster: Cluster, flavor: MeshFlavor, images: MeshImages) => Promise<void>;
|
|
243
262
|
/** This function gives the key of the mesh of a cell for its cluster: its flavor, or `none`. */
|
|
@@ -248,6 +267,8 @@ export declare const serverCluster: (namespace: string) => string;
|
|
|
248
267
|
export declare const EDS_EVERY: Quantity;
|
|
249
268
|
/** This time is the interval of the LDS poll of each server sidecar, the same as the poll of EDS. */
|
|
250
269
|
export declare const LDS_EVERY: Quantity;
|
|
270
|
+
/** This function tells if the watcher follows the log of each ztunnel for a cell: a cell in ambient mode. */
|
|
271
|
+
export declare const followsZtunnel: (cell: Pick<KindCell, "mesh">) => boolean;
|
|
251
272
|
/** This function tells if the lab polls the LDS updates of the server sidecars of a cell: a policy in a sidecar mesh. */
|
|
252
273
|
export declare const pollsListeners: (cell: Pick<KindCell, "mesh" | "trigger">) => boolean;
|
|
253
274
|
//#endregion
|
|
@@ -292,7 +313,14 @@ interface KindLab {
|
|
|
292
313
|
take(): Line[];
|
|
293
314
|
/** This method adds a listener of each line, and gives the function that removes it. */
|
|
294
315
|
listen(listener: (line: Line) => void): () => void;
|
|
295
|
-
|
|
316
|
+
/**
|
|
317
|
+
* This method starts the watcher of the API on the namespaces. With `ztunnel`, it also follows the
|
|
318
|
+
* log of each ztunnel in istio-system: lines `ztunnel-pod` and `ztunnel`, which no tracker of the
|
|
319
|
+
* cell reads.
|
|
320
|
+
*/
|
|
321
|
+
watch(namespaces: readonly string[], options?: {
|
|
322
|
+
readonly ztunnel?: boolean | undefined;
|
|
323
|
+
}): Promise<void>;
|
|
296
324
|
probe(netns: readonly number[]): Promise<void>;
|
|
297
325
|
stopProbe(): Promise<void>;
|
|
298
326
|
/** This method asks each agent to poll the backends of a Service in the rules of its node, `every` apart, or to stop with null. */
|
|
@@ -312,6 +340,12 @@ interface KindLab {
|
|
|
312
340
|
lds(spec: LdsSpec | null): Promise<void>;
|
|
313
341
|
act(node: string, spec: Readonly<Record<string, unknown>>): Promise<Stamp>;
|
|
314
342
|
inject(node: string, pid: number, args: readonly string[]): Promise<Stamp>;
|
|
343
|
+
/**
|
|
344
|
+
* This method gives the rules of one table of iptables in the network namespace of a PID, with
|
|
345
|
+
* their counters (`iptables-save -c`). The injector reads them. The method also adds them to the
|
|
346
|
+
* lines as a line `tables`: evidence of which rules matched.
|
|
347
|
+
*/
|
|
348
|
+
tables(node: string, pid: number, table: string): Promise<string>;
|
|
315
349
|
now(): Promise<number>;
|
|
316
350
|
pidOf(node: string, namespace: string, pod: string, container: string): Promise<number>;
|
|
317
351
|
netnsOf(node: string, pid: number): Promise<number>;
|
|
@@ -370,6 +404,8 @@ export declare const namespaceOf: (cell: Pick<KindCell, "name">) => string;
|
|
|
370
404
|
/**
|
|
371
405
|
* This function gives the Namespace object of a cell. In the sidecar mode of a mesh, the label
|
|
372
406
|
* `istio-injection=enabled` makes the injector of Istio put a sidecar in each pod of the namespace.
|
|
407
|
+
* In the ambient mode, the label `istio.io/dataplane-mode=ambient` makes istio-cni redirect the
|
|
408
|
+
* traffic of each pod to ztunnel. The listeners of ztunnel are in the network namespace of the pod.
|
|
373
409
|
*/
|
|
374
410
|
export declare const namespaceObject: (cell: Pick<KindCell, "mesh">, namespace: string) => Record<string, unknown>;
|
|
375
411
|
/**
|
package/build/index.js
CHANGED
|
@@ -295,6 +295,22 @@ const writeWhole = (path, bytes, executable) => {
|
|
|
295
295
|
if (executable) chmodSync(partial, 493);
|
|
296
296
|
renameSync(partial, path);
|
|
297
297
|
};
|
|
298
|
+
/** These are the waits in ms before the second and the third try of a download. A runner can fail to connect for a moment. */
|
|
299
|
+
const DOWNLOAD_WAITS = [2e3, 8e3];
|
|
300
|
+
/**
|
|
301
|
+
* This function gives the bytes of a URL. It tries again after each wait when the connection fails or
|
|
302
|
+
* the answer is a 5xx. It refuses each other answer that is not ok at once.
|
|
303
|
+
*/
|
|
304
|
+
const download = async (url, waits = DOWNLOAD_WAITS) => {
|
|
305
|
+
for (let attempt = 0;; attempt++) {
|
|
306
|
+
const response = await fetch(url, { redirect: "follow" }).catch((error) => error instanceof Error ? error : new Error(String(error)));
|
|
307
|
+
if (response instanceof Response && response.ok) return new Uint8Array(await response.arrayBuffer());
|
|
308
|
+
const failure = response instanceof Response ? /* @__PURE__ */ new Error(`the download of ${url} answered ${response.status}`) : response;
|
|
309
|
+
const wait = waits[attempt];
|
|
310
|
+
if (response instanceof Response && response.status < 500 || wait === void 0) throw failure;
|
|
311
|
+
await new Promise((resolve) => setTimeout(resolve, wait));
|
|
312
|
+
}
|
|
313
|
+
};
|
|
298
314
|
/**
|
|
299
315
|
* This function gives the path of a tool, after a download when the cache does not have it. It
|
|
300
316
|
* refuses bytes whose sha256 is not the pinned value, from the cache or from the download. For a
|
|
@@ -314,9 +330,7 @@ const ensureTool = async (tool, key = platformKey(), cache = cacheFolder()) => {
|
|
|
314
330
|
if (found !== asset.sha256) throw new Error(`${kept} has the sha256 ${found}, not the pinned ${asset.sha256}`);
|
|
315
331
|
if (tool.entry === void 0) return path;
|
|
316
332
|
} else {
|
|
317
|
-
|
|
318
|
-
if (!response.ok) throw new Error(`the download of ${asset.url} answered ${response.status}`);
|
|
319
|
-
bytes = new Uint8Array(await response.arrayBuffer());
|
|
333
|
+
bytes = await download(asset.url);
|
|
320
334
|
const found = sha256(bytes);
|
|
321
335
|
if (found !== asset.sha256) throw new Error(`${asset.url} has the sha256 ${found}, not the pinned ${asset.sha256}`);
|
|
322
336
|
writeWhole(kept, bytes, executable && tool.entry === void 0);
|
|
@@ -642,6 +656,12 @@ const policyObject = (account) => ({
|
|
|
642
656
|
rules: [{ from: [{ source: { principals: [`cluster.local/ns/{namespace}/sa/${account}`] } }] }]
|
|
643
657
|
}
|
|
644
658
|
});
|
|
659
|
+
/**
|
|
660
|
+
* This function gives the target of a reset or a black hole of a cell. In T07 and T18 it is Redis,
|
|
661
|
+
* with the replicas as peers. In T15 the fault is on the mesh leg between the client pods and the
|
|
662
|
+
* replicas. It is a rule in each replica, with the client pods as peers.
|
|
663
|
+
*/
|
|
664
|
+
const linkOf = (cell) => cell.template === "T15" ? "servers" : "redis";
|
|
645
665
|
/** This function gives the fault plan of a cell. */
|
|
646
666
|
const planOf = (cell) => {
|
|
647
667
|
const ns = ["-n", "{namespace}"];
|
|
@@ -794,13 +814,13 @@ const planOf = (cell) => {
|
|
|
794
814
|
case "rst": return {
|
|
795
815
|
inject: [{
|
|
796
816
|
do: "inject",
|
|
797
|
-
target:
|
|
817
|
+
target: linkOf(cell),
|
|
798
818
|
action: "rst"
|
|
799
819
|
}],
|
|
800
820
|
hold: needs(trigger.hold, "a hold"),
|
|
801
821
|
heal: [{
|
|
802
822
|
do: "inject",
|
|
803
|
-
target:
|
|
823
|
+
target: linkOf(cell),
|
|
804
824
|
action: "clear-rst"
|
|
805
825
|
}],
|
|
806
826
|
restore: []
|
|
@@ -808,13 +828,13 @@ const planOf = (cell) => {
|
|
|
808
828
|
case "blackhole": return {
|
|
809
829
|
inject: [{
|
|
810
830
|
do: "inject",
|
|
811
|
-
target:
|
|
831
|
+
target: linkOf(cell),
|
|
812
832
|
action: "blackhole"
|
|
813
833
|
}],
|
|
814
834
|
hold: needs(trigger.hold, "a hold"),
|
|
815
835
|
heal: [{
|
|
816
836
|
do: "inject",
|
|
817
|
-
target:
|
|
837
|
+
target: linkOf(cell),
|
|
818
838
|
action: "clear"
|
|
819
839
|
}],
|
|
820
840
|
restore: []
|
|
@@ -1078,14 +1098,27 @@ const labImage = () => {
|
|
|
1078
1098
|
/** This version is the release of Istio of the lab: the tag of istioctl and of each image. */
|
|
1079
1099
|
const ISTIO_VERSION = "1.31.3";
|
|
1080
1100
|
/**
|
|
1081
|
-
* These are the images of
|
|
1082
|
-
*
|
|
1083
|
-
*
|
|
1084
|
-
*
|
|
1101
|
+
* These are the images of Istio, pinned by the digest of their index on Docker Hub (read on
|
|
1102
|
+
* 2026-10-24). `gcr.io/istio-release` has no final tag of 1.31. The lab pulls each one by its digest,
|
|
1103
|
+
* tags it with that digest (`localPin`), and loads it into each node. Thus no node pulls from a
|
|
1104
|
+
* registry. The flavor `sidecar` needs istiod and the proxy. The flavor `ambient` also needs
|
|
1105
|
+
* istio-cni and ztunnel (`MESH_IMAGES`).
|
|
1085
1106
|
*/
|
|
1086
1107
|
const ISTIO_IMAGES = {
|
|
1087
1108
|
pilot: "istio/pilot:1.31.3@sha256:6ad1e816c4d4767ba3f30a6286a96d121851ddb23d33e20736758acb5f3f4ea9",
|
|
1088
|
-
proxy: "istio/proxyv2:1.31.3@sha256:eb74eacea98d3fb1e239ea84016085155478586a554158676b9b67d960151084"
|
|
1109
|
+
proxy: "istio/proxyv2:1.31.3@sha256:eb74eacea98d3fb1e239ea84016085155478586a554158676b9b67d960151084",
|
|
1110
|
+
cni: "istio/install-cni:1.31.3@sha256:e98782038634ef4fadaf78b53dd8fa4134fda3d65838ead5fc1edef245451c0f",
|
|
1111
|
+
ztunnel: "istio/ztunnel:1.31.3@sha256:2c27371b3139ecbf4f5a017354bd540889482441712e88cd8e654ead5a3eb5b2"
|
|
1112
|
+
};
|
|
1113
|
+
/** These are the images that each flavor installs, by their names in `ISTIO_IMAGES`. */
|
|
1114
|
+
const MESH_IMAGES = {
|
|
1115
|
+
sidecar: ["pilot", "proxy"],
|
|
1116
|
+
ambient: [
|
|
1117
|
+
"pilot",
|
|
1118
|
+
"proxy",
|
|
1119
|
+
"cni",
|
|
1120
|
+
"ztunnel"
|
|
1121
|
+
]
|
|
1089
1122
|
};
|
|
1090
1123
|
/**
|
|
1091
1124
|
* This format is the JSON access log of each sidecar. Each field is a command of the formatter of
|
|
@@ -1119,10 +1152,15 @@ const ACCESS_LOG_FORMAT = {
|
|
|
1119
1152
|
* reports the whole runner, and no cell scales istiod.
|
|
1120
1153
|
* - The access log of each sidecar goes to its output as JSON (`ACCESS_LOG_FORMAT`).
|
|
1121
1154
|
*
|
|
1122
|
-
* The flavor `ambient`
|
|
1155
|
+
* The flavor `ambient` has the profile `ambient`: istiod, istio-cni and ztunnel, and no gateway
|
|
1156
|
+
* (manifests/profiles/ambient.yaml of Istio 1.31.3). Its two other images are full references too
|
|
1157
|
+
* (templates/daemonset.yaml of the chart of istio-cni, lines 93–96, and of ztunnel, lines 65–68).
|
|
1158
|
+
* The chart of istio-cni takes the pull policy of `global`. The template of ztunnel reads only its own
|
|
1159
|
+
* `imagePullPolicy`, thus the operator sets it there too.
|
|
1123
1160
|
*/
|
|
1124
1161
|
const istioOperator = (flavor, images) => {
|
|
1125
|
-
|
|
1162
|
+
const ambient = flavor === "ambient";
|
|
1163
|
+
if (ambient && (images.cni === void 0 || images.ztunnel === void 0)) throw new Error("the flavor ambient needs the images of istio-cni and ztunnel");
|
|
1126
1164
|
return {
|
|
1127
1165
|
apiVersion: "install.istio.io/v1alpha1",
|
|
1128
1166
|
kind: "IstioOperator",
|
|
@@ -1131,7 +1169,7 @@ const istioOperator = (flavor, images) => {
|
|
|
1131
1169
|
namespace: "istio-system"
|
|
1132
1170
|
},
|
|
1133
1171
|
spec: {
|
|
1134
|
-
profile: "minimal",
|
|
1172
|
+
profile: ambient ? "ambient" : "minimal",
|
|
1135
1173
|
hub: "docker.io/istio",
|
|
1136
1174
|
tag: ISTIO_VERSION,
|
|
1137
1175
|
meshConfig: {
|
|
@@ -1153,37 +1191,77 @@ const istioOperator = (flavor, images) => {
|
|
|
1153
1191
|
cpu: "500m",
|
|
1154
1192
|
memory: "256Mi"
|
|
1155
1193
|
} }
|
|
1156
|
-
}
|
|
1194
|
+
},
|
|
1195
|
+
...ambient ? {
|
|
1196
|
+
cni: { image: images.cni },
|
|
1197
|
+
ztunnel: {
|
|
1198
|
+
image: images.ztunnel,
|
|
1199
|
+
imagePullPolicy: "Never"
|
|
1200
|
+
}
|
|
1201
|
+
} : {}
|
|
1157
1202
|
}
|
|
1158
1203
|
}
|
|
1159
1204
|
};
|
|
1160
1205
|
};
|
|
1206
|
+
/** These are the workloads of each flavor in istio-system that the install waits for. */
|
|
1207
|
+
const MESH_WORKLOADS = {
|
|
1208
|
+
sidecar: ["deployment/istiod"],
|
|
1209
|
+
ambient: [
|
|
1210
|
+
"deployment/istiod",
|
|
1211
|
+
"daemonset/istio-cni-node",
|
|
1212
|
+
"daemonset/ztunnel"
|
|
1213
|
+
]
|
|
1214
|
+
};
|
|
1161
1215
|
/**
|
|
1162
1216
|
* This function installs the mesh of a flavor in a cluster with `istioctl install` and the operator
|
|
1163
|
-
* of the flavor. Then it waits for
|
|
1217
|
+
* of the flavor. Then it waits for each workload of the flavor. The kubeconfig of the cluster goes to
|
|
1218
|
+
* istioctl. When the install fails, the error has the pods of istio-system and the last lines of the
|
|
1219
|
+
* log of istio-cni. Thus the log of the job shows the cause, for example a module that the kernel of
|
|
1220
|
+
* the runner does not have.
|
|
1164
1221
|
*/
|
|
1165
1222
|
const installMesh = async (cluster, flavor, images) => {
|
|
1166
1223
|
const istioctl = await ensureTool(ISTIOCTL);
|
|
1167
1224
|
const file = join(cluster.dir, `istio-${flavor}.json`);
|
|
1168
1225
|
writeFileSync(file, `${JSON.stringify(istioOperator(flavor, images), null, 2)}\n`);
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1226
|
+
try {
|
|
1227
|
+
await command(istioctl, [
|
|
1228
|
+
"install",
|
|
1229
|
+
"--kubeconfig",
|
|
1230
|
+
cluster.kubeconfig,
|
|
1231
|
+
"-y",
|
|
1232
|
+
"-f",
|
|
1233
|
+
file,
|
|
1234
|
+
"--readiness-timeout",
|
|
1235
|
+
"10m0s"
|
|
1236
|
+
], { timeout: 9e5 });
|
|
1237
|
+
for (const workload of MESH_WORKLOADS[flavor]) await cluster.kubectl([
|
|
1238
|
+
"-n",
|
|
1239
|
+
"istio-system",
|
|
1240
|
+
"rollout",
|
|
1241
|
+
"status",
|
|
1242
|
+
workload,
|
|
1243
|
+
"--timeout=300s"
|
|
1244
|
+
]);
|
|
1245
|
+
} catch (error) {
|
|
1246
|
+
const state = async (args) => (await cluster.kubectl([
|
|
1247
|
+
"-n",
|
|
1248
|
+
"istio-system",
|
|
1249
|
+
...args
|
|
1250
|
+
]).catch((why) => String(why))).slice(-4e3);
|
|
1251
|
+
const pods = await state([
|
|
1252
|
+
"get",
|
|
1253
|
+
"pods",
|
|
1254
|
+
"-o",
|
|
1255
|
+
"wide"
|
|
1256
|
+
]);
|
|
1257
|
+
const cni = flavor === "ambient" ? `\nthe log of istio-cni:\n${await state([
|
|
1258
|
+
"logs",
|
|
1259
|
+
"daemonset/istio-cni-node",
|
|
1260
|
+
"--tail=60",
|
|
1261
|
+
"--all-containers"
|
|
1262
|
+
])}` : "";
|
|
1263
|
+
throw new Error(`the install of Istio ${flavor} failed: ${String(error)}\nthe pods of istio-system:\n${pods}${cni}`);
|
|
1264
|
+
}
|
|
1187
1265
|
};
|
|
1188
1266
|
/** This function gives the key of the mesh of a cell for its cluster: its flavor, or `none`. */
|
|
1189
1267
|
const meshKey = (cell) => cell.mesh?.flavor ?? "none";
|
|
@@ -1199,6 +1277,8 @@ const LDS_EVERY = {
|
|
|
1199
1277
|
value: 50,
|
|
1200
1278
|
unit: "ms"
|
|
1201
1279
|
};
|
|
1280
|
+
/** This function tells if the watcher follows the log of each ztunnel for a cell: a cell in ambient mode. */
|
|
1281
|
+
const followsZtunnel = (cell) => cell.mesh?.mode === "ambient";
|
|
1202
1282
|
/** This function tells if the lab polls the LDS updates of the server sidecars of a cell: a policy in a sidecar mesh. */
|
|
1203
1283
|
const pollsListeners = (cell) => cell.mesh?.mode === "sidecar" && cell.trigger.kind === "authorization-policy";
|
|
1204
1284
|
//#endregion
|
|
@@ -1386,12 +1466,13 @@ const openKindLab = async (cluster, image) => {
|
|
|
1386
1466
|
listeners.add(listener);
|
|
1387
1467
|
return () => listeners.delete(listener);
|
|
1388
1468
|
},
|
|
1389
|
-
async watch(namespaces) {
|
|
1469
|
+
async watch(namespaces, options) {
|
|
1390
1470
|
if (watcher !== null) await watcher.stop({ timeout: 0 }).catch(() => void 0);
|
|
1391
1471
|
const kube = await cluster.credentials();
|
|
1392
1472
|
watcher = await new GenericContainer(image).withNetworkMode(cluster.network).withEnvironment({
|
|
1393
1473
|
KUBE: JSON.stringify(kube),
|
|
1394
|
-
NAMESPACES: namespaces.join(",")
|
|
1474
|
+
NAMESPACES: namespaces.join(","),
|
|
1475
|
+
ZTUNNEL: options?.ztunnel === true ? "1" : "0"
|
|
1395
1476
|
}).withCommand(["node", "watch.mjs"]).withLogConsumer(consume((line) => push(line))).start();
|
|
1396
1477
|
},
|
|
1397
1478
|
async probe(netns) {
|
|
@@ -1514,6 +1595,28 @@ const openKindLab = async (cluster, image) => {
|
|
|
1514
1595
|
});
|
|
1515
1596
|
return stamp;
|
|
1516
1597
|
},
|
|
1598
|
+
async tables(node, pid, table) {
|
|
1599
|
+
const t = await lab.now();
|
|
1600
|
+
const text = await output(sidecarOf(node).injector, [
|
|
1601
|
+
"nsenter",
|
|
1602
|
+
"-t",
|
|
1603
|
+
String(pid),
|
|
1604
|
+
"-n",
|
|
1605
|
+
"iptables-save",
|
|
1606
|
+
"-c",
|
|
1607
|
+
"-t",
|
|
1608
|
+
table
|
|
1609
|
+
]);
|
|
1610
|
+
push({
|
|
1611
|
+
ev: "tables",
|
|
1612
|
+
t,
|
|
1613
|
+
node,
|
|
1614
|
+
pid,
|
|
1615
|
+
table,
|
|
1616
|
+
text: text.slice(0, 8e3)
|
|
1617
|
+
});
|
|
1618
|
+
return text;
|
|
1619
|
+
},
|
|
1517
1620
|
async now() {
|
|
1518
1621
|
return (await lab.act(firstNode, { op: "now" })).t0;
|
|
1519
1622
|
},
|
|
@@ -1600,16 +1703,23 @@ const whole = (amount, what) => {
|
|
|
1600
1703
|
};
|
|
1601
1704
|
/** This function gives the namespace of a cell: `lab-` and 10 digits of the sha256 of its name. */
|
|
1602
1705
|
const namespaceOf = (cell) => `lab-${createHash("sha256").update(cell.name).digest("hex").slice(0, 10)}`;
|
|
1706
|
+
/** These are the labels of the namespace of each mode of a mesh. */
|
|
1707
|
+
const MESH_LABELS = {
|
|
1708
|
+
sidecar: { "istio-injection": "enabled" },
|
|
1709
|
+
ambient: { "istio.io/dataplane-mode": "ambient" }
|
|
1710
|
+
};
|
|
1603
1711
|
/**
|
|
1604
1712
|
* This function gives the Namespace object of a cell. In the sidecar mode of a mesh, the label
|
|
1605
1713
|
* `istio-injection=enabled` makes the injector of Istio put a sidecar in each pod of the namespace.
|
|
1714
|
+
* In the ambient mode, the label `istio.io/dataplane-mode=ambient` makes istio-cni redirect the
|
|
1715
|
+
* traffic of each pod to ztunnel. The listeners of ztunnel are in the network namespace of the pod.
|
|
1606
1716
|
*/
|
|
1607
1717
|
const namespaceObject = (cell, namespace) => ({
|
|
1608
1718
|
apiVersion: "v1",
|
|
1609
1719
|
kind: "Namespace",
|
|
1610
1720
|
metadata: {
|
|
1611
1721
|
name: namespace,
|
|
1612
|
-
...cell.mesh
|
|
1722
|
+
...cell.mesh === void 0 ? {} : { labels: { ...MESH_LABELS[cell.mesh.mode] } }
|
|
1613
1723
|
}
|
|
1614
1724
|
});
|
|
1615
1725
|
/**
|
|
@@ -2296,7 +2406,7 @@ const quiet = async (ctx, ours) => {
|
|
|
2296
2406
|
*/
|
|
2297
2407
|
const deploy = async (lab, cluster, cell, namespace, images) => {
|
|
2298
2408
|
await cluster.apply([namespaceObject(cell, namespace)]);
|
|
2299
|
-
await lab.watch([namespace]);
|
|
2409
|
+
await lab.watch([namespace], { ztunnel: followsZtunnel(cell) });
|
|
2300
2410
|
if (hasRedis(cell)) {
|
|
2301
2411
|
await cluster.apply(redisObjects(namespace, images.redis));
|
|
2302
2412
|
await cluster.kubectl([
|
|
@@ -2432,12 +2542,62 @@ const replicasOf = (ctx) => {
|
|
|
2432
2542
|
}));
|
|
2433
2543
|
return stackOf(ctx.cell) === "grpc" ? byClients(replicas, ctx.tracker.answers) : replicas;
|
|
2434
2544
|
};
|
|
2545
|
+
/**
|
|
2546
|
+
* This time is how long before the commit of its rules the lab starts the injector in each replica.
|
|
2547
|
+
* It is more than the execs of one node before its commit: a read of its tables and the injector.
|
|
2548
|
+
*/
|
|
2549
|
+
const INJECT_LEAD_MS = 700;
|
|
2550
|
+
/**
|
|
2551
|
+
* This function puts the rules of an action in the network namespace of each replica, with each
|
|
2552
|
+
* client pod as a peer: the target `servers`. The rules of all replicas commit at one instant on
|
|
2553
|
+
* CLOCK_MONOTONIC (`--at`). Thus no client meets one replica with the rule and one without it. The
|
|
2554
|
+
* replicas of one node go one after the other, and the nodes at the same time.
|
|
2555
|
+
*
|
|
2556
|
+
* The fault finds the PID of each replica, and the clear uses it again: thus the heal comes soon
|
|
2557
|
+
* after its hold. Before the commit of a clear, the lab reads the filter table of each replica. Its
|
|
2558
|
+
* counters show if the rule matched the clients beside the rules of istio-cni. After the commit of a
|
|
2559
|
+
* fault, the lab reads the nat table, with the redirect of istio-cni. The stamp is the first commit
|
|
2560
|
+
* and the last end.
|
|
2561
|
+
*/
|
|
2562
|
+
const injectServers = async (ctx, action, replicas) => {
|
|
2563
|
+
const clients = [...ctx.tracker.pods.values()].filter((pod) => pod.labels["app"] === "client" && !pod.del && pod.ip !== null).map((pod) => pod.ip).sort();
|
|
2564
|
+
if (clients.length === 0 || replicas.length === 0) throw new Error(`the injector needs clients and replicas: ${clients.length} clients, ${replicas.length} replicas`);
|
|
2565
|
+
const clear = action.startsWith("clear");
|
|
2566
|
+
const byNode = /* @__PURE__ */ new Map();
|
|
2567
|
+
for (const replica of replicas) {
|
|
2568
|
+
const pid = (clear ? ctx.pids.get(replica.pod) : void 0) ?? await ctx.lab.pidOf(replica.node, ctx.namespace, replica.pod, "app");
|
|
2569
|
+
ctx.pids.set(replica.pod, pid);
|
|
2570
|
+
byNode.set(replica.node, [...byNode.get(replica.node) ?? [], pid]);
|
|
2571
|
+
}
|
|
2572
|
+
const at = await ctx.lab.now() + INJECT_LEAD_MS;
|
|
2573
|
+
const args = [
|
|
2574
|
+
action,
|
|
2575
|
+
clients.join(","),
|
|
2576
|
+
"--at",
|
|
2577
|
+
String(Math.round(at * 1e6))
|
|
2578
|
+
];
|
|
2579
|
+
const stamps = await Promise.all([...byNode].map(async ([node, pids]) => {
|
|
2580
|
+
const out = [];
|
|
2581
|
+
for (const pid of pids) {
|
|
2582
|
+
if (clear) await ctx.lab.tables(node, pid, "filter");
|
|
2583
|
+
out.push(await ctx.lab.inject(node, pid, args));
|
|
2584
|
+
}
|
|
2585
|
+
return out;
|
|
2586
|
+
}));
|
|
2587
|
+
if (!clear) for (const [node, pids] of byNode) for (const pid of pids) await ctx.lab.tables(node, pid, "nat");
|
|
2588
|
+
const all = stamps.flat();
|
|
2589
|
+
return {
|
|
2590
|
+
t0: Math.min(...all.map((stamp) => stamp.t0)),
|
|
2591
|
+
t1: Math.max(...all.map((stamp) => stamp.t1))
|
|
2592
|
+
};
|
|
2593
|
+
};
|
|
2435
2594
|
/** This function does one step of a fault plan. It gives the stamp of its act, or null for an act without a stamp. */
|
|
2436
2595
|
const doStep = async (ctx, step, replicas, background) => {
|
|
2437
2596
|
const { lab, cluster, namespace } = ctx;
|
|
2438
2597
|
const target = replicas[0];
|
|
2439
2598
|
const fill = (args) => args.map((arg) => arg.replaceAll("{namespace}", namespace).replaceAll(POD, target.pod).replaceAll(NODE, target.node).replaceAll(VERSION, String(ctx.version)));
|
|
2440
2599
|
const where = async (which) => {
|
|
2600
|
+
if (which === "servers") throw new Error("the target servers is a target of the injector only");
|
|
2441
2601
|
if (which === "server") return {
|
|
2442
2602
|
node: target.node,
|
|
2443
2603
|
pid: await lab.pidOf(target.node, namespace, target.pod, "app"),
|
|
@@ -2511,6 +2671,7 @@ const doStep = async (ctx, step, replicas, background) => {
|
|
|
2511
2671
|
};
|
|
2512
2672
|
}
|
|
2513
2673
|
case "inject": {
|
|
2674
|
+
if (step.target === "servers") return injectServers(ctx, step.action, replicas);
|
|
2514
2675
|
const at = await where(step.target);
|
|
2515
2676
|
let first = null;
|
|
2516
2677
|
for (const replica of replicas) {
|
|
@@ -2777,14 +2938,19 @@ const runKindCells = async (cells, options) => {
|
|
|
2777
2938
|
kind: "shipped",
|
|
2778
2939
|
name: "grpc-go"
|
|
2779
2940
|
}) : void 0;
|
|
2941
|
+
const flavors = new Set(cells.flatMap((cell) => cell.mesh === void 0 ? [] : [cell.mesh.flavor]));
|
|
2942
|
+
const pinned = /* @__PURE__ */ new Map();
|
|
2943
|
+
for (const name of new Set([...flavors].flatMap((flavor) => MESH_IMAGES[flavor]))) pinned.set(name, await localPin(ISTIO_IMAGES[name]));
|
|
2780
2944
|
const images = {
|
|
2781
2945
|
lab: image,
|
|
2782
2946
|
redis: redisImage,
|
|
2783
2947
|
grpc: grpcImage,
|
|
2784
|
-
mesh:
|
|
2785
|
-
pilot:
|
|
2786
|
-
proxy:
|
|
2787
|
-
|
|
2948
|
+
mesh: flavors.size === 0 ? void 0 : {
|
|
2949
|
+
pilot: pinned.get("pilot"),
|
|
2950
|
+
proxy: pinned.get("proxy"),
|
|
2951
|
+
cni: pinned.get("cni"),
|
|
2952
|
+
ztunnel: pinned.get("ztunnel")
|
|
2953
|
+
}
|
|
2788
2954
|
};
|
|
2789
2955
|
const groups = /* @__PURE__ */ new Map();
|
|
2790
2956
|
for (const cell of cells) groups.set(clusterKey(cell), [...groups.get(clusterKey(cell)) ?? [], cell]);
|
|
@@ -2805,7 +2971,11 @@ const runKindCells = async (cells, options) => {
|
|
|
2805
2971
|
if (grpcImage !== void 0 && group.some((cell) => stackOf(cell) === "grpc")) await cluster.load(grpcImage);
|
|
2806
2972
|
if (mesh !== void 0) {
|
|
2807
2973
|
if (images.mesh === void 0) throw new Error(`${group[0].name}: the run has no images of Istio`);
|
|
2808
|
-
for (const
|
|
2974
|
+
for (const name of MESH_IMAGES[mesh.flavor]) {
|
|
2975
|
+
const each = images.mesh[name];
|
|
2976
|
+
if (each === void 0) throw new Error(`${group[0].name}: the run has no image ${name} of Istio`);
|
|
2977
|
+
await cluster.load(each);
|
|
2978
|
+
}
|
|
2809
2979
|
const began = Date.now();
|
|
2810
2980
|
await installMesh(cluster, mesh.flavor, images.mesh);
|
|
2811
2981
|
options.log(` Istio ${mesh.flavor} installed in ${Math.round((Date.now() - began) / 100) / 10} s`);
|
|
@@ -2844,7 +3014,8 @@ const runKindCells = async (cells, options) => {
|
|
|
2844
3014
|
version: 1,
|
|
2845
3015
|
probed: "",
|
|
2846
3016
|
listened: "",
|
|
2847
|
-
expected: cell.pod.replicas
|
|
3017
|
+
expected: cell.pod.replicas,
|
|
3018
|
+
pids: /* @__PURE__ */ new Map()
|
|
2848
3019
|
};
|
|
2849
3020
|
const share = Math.ceil(cell.runs / environments) - (env < cell.runs % environments || cell.runs % environments === 0 ? 0 : 1);
|
|
2850
3021
|
const runs = [await runOnce(ctx, env, -1, true, ours)];
|
|
@@ -2863,10 +3034,7 @@ const runKindCells = async (cells, options) => {
|
|
|
2863
3034
|
lab: image,
|
|
2864
3035
|
redis: redisImage,
|
|
2865
3036
|
...stackOf(cell) === "grpc" ? { grpc: grpcImage } : {},
|
|
2866
|
-
...cell.mesh === void 0 ? {} : {
|
|
2867
|
-
"istio-pilot": ISTIO_IMAGES.pilot,
|
|
2868
|
-
"istio-proxy": ISTIO_IMAGES.proxy
|
|
2869
|
-
}
|
|
3037
|
+
...cell.mesh === void 0 ? {} : Object.fromEntries(MESH_IMAGES[cell.mesh.flavor].map((name) => [`istio-${name}`, ISTIO_IMAGES[name]]))
|
|
2870
3038
|
}
|
|
2871
3039
|
});
|
|
2872
3040
|
entry.loads.push(...loads);
|
|
@@ -2963,6 +3131,6 @@ const shardOf = (cells, index, count) => {
|
|
|
2963
3131
|
return out;
|
|
2964
3132
|
};
|
|
2965
3133
|
//#endregion
|
|
2966
|
-
export { ACCESS_LOG_FORMAT, CLIENT, CLIENT_ACCOUNT, CONTROLLER, DOCUMENTED, DRAIN_RULES, EDS_EVERY, FLAG_CHECK, FORMAT, FORMAT_V1, HEALTH_PORT, ISTIOCTL, ISTIO_IMAGES, ISTIO_VERSION, KEPT_KERNEL, KIND, KIND_NOISE, KUBECTL, KUBE_PROXY_MODES, LAST_SLACK, LDS_EVERY, LDS_FILTER, LOG_LABEL, NODE_IMAGES, O04, O09, O10, O16, POLICY, PORT, REDIS, REDIS_IMAGE, RELATIVE, ROUTE, RULES_EVERY, SERVER, STAGES, WATCHDOG, backendsAt, backendsOf, bandOf, byClients, byNode, cacheFolder, calibrationOf, capOf, cellsTable, checkRun, cidrOverlap, cidrRange, clientObjects, clusterFilter, compose, createCluster, deletes, deletionOf, deletionWindow, deletionsOf, describeCell, dropOf, emptyOf, ensureTool, estimatedTime, factsOf, failuresOf, goDuration, hostsOf, installMesh, intakeAfter, intakeOf, istioOperator, judge, judgeKind, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, kindConfig, kubeProxyPatch, kubeletBand, labImage, latchBand, lintCell, listenerCountsOf, localPin, medianInterval, medianText, meshKey, middle, namespaceObject, namespaceOf, nodeNames, observationAfter, observationEnd, openKindLab, outboundCluster, outcomesOf, pausedWindow, planOf, platformKey, podsAt, podsOf, policyObject, pollsListeners, predictionText, probeOf, propagationOf, propagationSource, quantileOf, readRecord, recordsOf, redisObjects, referenceOf, removalBand, requestsOf, restartBand, restartsAt, reusesReset, rulesOf, runKindCells, scenario, scenarioOf, scenarioProblems, seconds, secondsOf, serverCluster, serverObjects, sha256, shardOf, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, slicesOf, stackOf, stageLabImage, stallRemovalBand, stoppingOf, tarEntry, timeSpread, toolPath, transitionOf, unitOf, updatesOf, upgrade, upgradeCell, verdictsOf, writeResult };
|
|
3134
|
+
export { ACCESS_LOG_FORMAT, CLIENT, CLIENT_ACCOUNT, CONTROLLER, DOCUMENTED, DRAIN_RULES, EDS_EVERY, FLAG_CHECK, FORMAT, FORMAT_V1, HEALTH_PORT, ISTIOCTL, ISTIO_IMAGES, ISTIO_VERSION, KEPT_KERNEL, KIND, KIND_NOISE, KUBECTL, KUBE_PROXY_MODES, LAST_SLACK, LDS_EVERY, LDS_FILTER, LOG_LABEL, MESH_IMAGES, MESH_WORKLOADS, NODE_IMAGES, O04, O09, O10, O16, POLICY, PORT, REDIS, REDIS_IMAGE, RELATIVE, ROUTE, RULES_EVERY, SERVER, STAGES, WATCHDOG, backendsAt, backendsOf, bandOf, byClients, byNode, cacheFolder, calibrationOf, capOf, cellsTable, checkRun, cidrOverlap, cidrRange, clientObjects, clusterFilter, compose, createCluster, deletes, deletionOf, deletionWindow, deletionsOf, describeCell, dropOf, emptyOf, ensureTool, estimatedTime, factsOf, failuresOf, followsZtunnel, goDuration, hostsOf, installMesh, intakeAfter, intakeOf, istioOperator, judge, judgeKind, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, kindConfig, kubeProxyPatch, kubeletBand, labImage, latchBand, lintCell, listenerCountsOf, localPin, medianInterval, medianText, meshKey, middle, namespaceObject, namespaceOf, nodeNames, observationAfter, observationEnd, openKindLab, outboundCluster, outcomesOf, pausedWindow, planOf, platformKey, podsAt, podsOf, policyObject, pollsListeners, predictionText, probeOf, propagationOf, propagationSource, quantileOf, readRecord, recordsOf, redisObjects, referenceOf, removalBand, requestsOf, restartBand, restartsAt, reusesReset, rulesOf, runKindCells, scenario, scenarioOf, scenarioProblems, seconds, secondsOf, serverCluster, serverObjects, sha256, shardOf, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, slicesOf, stackOf, stageLabImage, stallRemovalBand, stoppingOf, tarEntry, timeSpread, toolPath, transitionOf, unitOf, updatesOf, upgrade, upgradeCell, verdictsOf, writeResult };
|
|
2967
3135
|
|
|
2968
3136
|
//# sourceMappingURL=index.js.map
|