@mark1russell7/kind 0.5.0 → 0.7.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 +22 -0
- package/build/index.d.ts +23 -5
- package/build/index.js +143 -7
- package/build/index.js.map +1 -1
- package/build/{judge-BS9pk96o.d.ts → judge-B1nSdbS0.d.ts} +98 -30
- package/build/{judge-Bkbs7VVG.js → judge-CmiF-Fcs.js} +192 -59
- package/build/judge-CmiF-Fcs.js.map +1 -0
- package/build/judge.d.ts +2 -2
- package/build/judge.js +1 -1
- package/image/Dockerfile +1 -1
- package/image/client-grpc.mjs +9 -0
- package/package.json +2 -1
- package/src/client-lines.ts +60 -0
- package/build/judge-Bkbs7VVG.js.map +0 -1
package/README.md
CHANGED
|
@@ -137,6 +137,13 @@ From 0.5.0, the lab installs the flavor `ambient` too:
|
|
|
137
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
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
139
|
|
|
140
|
+
From 0.7.0, a cell can restart ztunnel, and it can set the environment of ztunnel:
|
|
141
|
+
|
|
142
|
+
- **The watcher.** It follows the ztunnel pods for each cell in the flavor `ambient`, also for a cell of sidecar pods there. The lines of the ztunnel pods are the evidence of a restart that no pod of the cell sees.
|
|
143
|
+
- **The environment.** `mesh.ztunnel` gives variables of the DaemonSet of ztunnel for one cell, for example `CONNECTION_TERMINATION_DEADLINE`. Before the cell, `setZtunnelEnv` sets them with `kubectl set env` (`ztunnelEnvArgs`). Then it waits for the rollout, and until no old ztunnel pod is left. After the cell, it removes them in the same way.
|
|
144
|
+
- **The reference `restart`.** It is the first deletion timestamp of a ztunnel pod in the run, as the watcher receives it (`restartOf`). A run of the trigger `ztunnel-restart` ends when each deleted ztunnel pod is gone, and each node has a ready new one (`ztunnelReplaced`).
|
|
145
|
+
- **The DaemonSet.** The chart of ztunnel sets `maxSurge` 1. Then the controller of the DaemonSet of Kubernetes v1.37.0 skips a pod with a deletion timestamp. It makes a new pod on that node at once (`daemon_controller.go`, lines 863–924). Thus the new ztunnel runs beside the old one while the old one drains.
|
|
146
|
+
|
|
140
147
|
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.
|
|
141
148
|
|
|
142
149
|
## The image
|
|
@@ -152,6 +159,8 @@ The lab image (`image/`) holds the service of a cell on `@mark1russell7/service`
|
|
|
152
159
|
- **The seams** of the service are a flag file that fails its liveness check, and SIGUSR2. SIGUSR2 blocks its event loop for the time in a file.
|
|
153
160
|
- **The clients** (`client.mjs`) send requests on new connections to the ClusterIP. Each request gives one line with its outcome: ok, 503, refused, reset, timeout or unreachable. From 0.1.3, each line also gives the local port of its connection (`lp`), a refused one's too. A conntrack entry is per port. Thus the requests on the port of a failure show the connection before it, and the pod that it went to.
|
|
154
161
|
- **The gRPC clients** (`client-grpc.mjs`) hold one channel of grpc-js 1.14.6 to the ClusterIP, and send a call of `grpc.health.v1.Health/Check` each interval on it. Each call gives the line of a request of `client.mjs`, with the pod that answered (the header `x-pod`). A call that fails at once names the errno of the last connect: refused, reset or unreachable. A call of grpc-js has no deadline at its defaults. Thus a call with no answer in the timeout is a timeout, and the client cancels it.
|
|
162
|
+
- From 0.6.0, each socket of the channel also gives a `sock` line at its connect and at its close (`client-lines.ts`). Each line gives the local port of the socket (`lp`). The close line keeps the port of the connect, and it gives the code of the error of the socket (`err`), or null.
|
|
163
|
+
- From 0.6.0, SIGUSR1 stops the calls of the client, and SIGUSR2 starts them again. Each signal gives a `gap` line. The channel and its socket stay open in the gap. A listener of SIGUSR1 replaces the start of the inspector of Node at that signal.
|
|
155
164
|
|
|
156
165
|
## The triggers
|
|
157
166
|
|
|
@@ -172,6 +181,8 @@ The lab image (`image/`) holds the service of a cell on `@mark1russell7/service`
|
|
|
172
181
|
| `rollout` | no Redis pod, then a rolling update |
|
|
173
182
|
| `cpu-throttle` | `cpu.max` in the cgroup of the container of the first replica |
|
|
174
183
|
| `authorization-policy` | `kubectl apply` of a DENY AuthorizationPolicy on the server pods for the principal of an account (`account`, the account `default` of the clients when not given), then `kubectl delete` after the hold |
|
|
184
|
+
| `idle` | SIGUSR1 to the gRPC client of each client pod, and SIGUSR2 after the hold: a gap of the calls (the target `clients`). The restore sends SIGUSR2 again |
|
|
185
|
+
| `ztunnel-restart` | `kubectl delete pod` of the ztunnel pod of each node, with its grace (`ZTUNNEL_PODS`). The flow of each client crosses the ztunnel of its own node. The restore waits for the rollout of the DaemonSet |
|
|
175
186
|
|
|
176
187
|
A cgroup path that is not the scope of one container is refused. A write to the root of the node throttles or freezes each pod of the node.
|
|
177
188
|
|
|
@@ -219,6 +230,17 @@ From 0.4.0, a window can count from the heal (`heal`), the first act that ends t
|
|
|
219
230
|
- 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.
|
|
220
231
|
- `calibrationOf` gives the updates after the apply by node, and the updates after the delete (`heal`).
|
|
221
232
|
|
|
233
|
+
From 0.6.0, the prediction `ends` is the end of the socket of each client, from the `sock` lines. A client of the run is a client with an answer before the reference.
|
|
234
|
+
|
|
235
|
+
- With a band, the first close of a socket of each client after the reference must be in the band ± δ. It must be the socket of its last answer. The main value is the middle of these times, in `s`.
|
|
236
|
+
- With no band, no socket of a client can close after the reference. The first request of each client after the heal must be `ok` on the socket of its last answer. The main value is the count of the closes, in `{socket}`.
|
|
237
|
+
- With a window (`window`), the failed requests must also agree with that window. The observation lasts until each socket can end, and until each request of the window ended.
|
|
238
|
+
- The facts of a run give the first close of each client after the reference (`closes`).
|
|
239
|
+
|
|
240
|
+
A new member of the union `TriggerKind`, `ztunnel-restart`, and a new member of the union `Reference`, `restart`, break a reader that switches on them. Each is a breaking change before 1.0.0, thus the version is 0.7.0. The record format stays `/2`, with the optional field `mesh.ztunnel`.
|
|
241
|
+
|
|
242
|
+
A new member of the union `Prediction`, `ends`, and a new member of the union `TriggerKind`, `idle`, break a reader that switches on them. The union `Target` has the new member `clients` too. Each is a breaking change before 1.0.0, thus the version is 0.6.0. The record format stays `/2`, with the optional field `closes`: the lines `sock` and `gap` are new lines of a run.
|
|
243
|
+
|
|
222
244
|
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
245
|
|
|
224
246
|
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`.
|
package/build/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as updatesOf, $t as
|
|
1
|
+
import { $ as updatesOf, $t as MeshFlavor, A as TriggerV1, At as sliceEmptyOf, B as RunCheck, Bt as PORT, C as KindCellResult, Cn as kubeProxyPatch, Ct as requestsOf, D as PredictionV1, Dt as signalOf, E as LoadSample, En as scenarioProblems, Et as rulesOf, F as KIND_NOISE, Ft as ztunnelPodsOf, G as judgeKind, Gt as secondsOf, H as checkRun, Ht as SERVER, I as KindDecision, It as ztunnelReplaced, J as propagationOf, Jt as Declaration, K as observationAfter, Kt as showBand, L as KindVerdict, Lt as CLIENT, M as upgrade, Mt as socketsOf, N as upgradeCell, Nt as stoppingOf, O as RunCheckV1, Ot as signalsOf, P as Deleted, Pt as transitionOf, Q as unitOf, Qt as Mesh, R as LAST_SLACK, Rt as HEALTH_PORT, S as KindCellRecordV1, Sn as kindConfig, St as recordsOf, T as KindVerdictV1, Tn as scenario, Tt as restartsAt, U as deletionsOf, Ut as bandOf, V as RunFacts, Vt as REDIS, W as factsOf, Wt as seconds, X as referenceOf, Xt as KindRun, Y as propagationSource, Yt as KindCell, Z as reusesReset, Zt as KubeletProbe, _ as ClusterManifest, _n as NODE_IMAGES, _t as intakeOf, a as STAGES, an as Replica, at as Request, b as FORMAT_V1, bn as cidrRange, bt as podsAt, c as readRecord, cn as Template, ct as SocketLine, d as calibrationOf, dn as WindowPrediction, dt as byClients, en as MeshMode, et as Endpoint, f as cellsTable, fn as Workload, ft as byNode, g as BandV1, gn as KubeProxyMode, gt as failuresOf, h as timeSpread, hn as KindScenario, ht as emptyOf, i as quantileOf, in as Reference, it as RecordLine, j as judge, jt as slicesOf, k as RunFactsV1, kt as sliceDropOf, l as Judged, ln as Trigger, lt as ZtunnelPodLine, m as predictionText, mn as KindConfig, mt as dropOf, n as medianInterval, nn as Prediction, nt as PodLine, o as kindCellRecordSchema, on as ResetReuse, ot as RulesLine, p as medianText, pn as KUBE_PROXY_MODES, pt as deletionOf, q as observationEnd, qt as Band, r as middle, rn as RedisDeclaration, rt as PropagationSource, s as kindCellRecordV1Schema, sn as Stack, st as SliceLine, t as RELATIVE, tn as PodSettings, tt as Line, u as TimeSpread, un as TriggerKind, ut as backendsAt, v as EnvironmentManifest, vn as NodeImage, vt as kernelCounts, w as KindCellV1, wn as nodeNames, wt as restartOf, x as KindCellRecord, xn as goDuration, xt as podsOf, y as FORMAT, yn as cidrOverlap, yt as outcomesOf, z as Propagated, zt as LOG_LABEL } from "./judge-B1nSdbS0.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";
|
|
@@ -114,8 +114,9 @@ export declare const hostsOf: (text: string, cluster: string) => ClusterHosts;
|
|
|
114
114
|
* - `servers` is each replica of the service. Only the injector takes it: a rule in the network
|
|
115
115
|
* namespace of each replica, with each client pod as a peer. A REJECT resets only the sender of
|
|
116
116
|
* the segment that it matches, thus the rule goes where the segments of the clients arrive.
|
|
117
|
+
* - `clients` is the process of each client pod. Only a signal takes it: the gap of the trigger `idle`.
|
|
117
118
|
*/
|
|
118
|
-
type Target = "server" | "redis" | "kube-proxy" | "servers";
|
|
119
|
+
type Target = "server" | "redis" | "kube-proxy" | "servers" | "clients";
|
|
119
120
|
/** A step is one act of a fault, in the sequence of the plan. */
|
|
120
121
|
type Step = {
|
|
121
122
|
readonly do: "kubectl";
|
|
@@ -124,7 +125,7 @@ type Step = {
|
|
|
124
125
|
} | {
|
|
125
126
|
readonly do: "signal";
|
|
126
127
|
readonly target: Target;
|
|
127
|
-
readonly signal: "SIGKILL" | "SIGSTOP" | "SIGCONT";
|
|
128
|
+
readonly signal: "SIGKILL" | "SIGSTOP" | "SIGCONT" | "SIGUSR1" | "SIGUSR2";
|
|
128
129
|
} | {
|
|
129
130
|
readonly do: "pause";
|
|
130
131
|
readonly target: Target;
|
|
@@ -168,6 +169,8 @@ interface FaultPlan {
|
|
|
168
169
|
export declare const POLICY = "lab-deny";
|
|
169
170
|
/** This service account is the account of the clients: their pods name no other account. */
|
|
170
171
|
export declare const CLIENT_ACCOUNT = "default";
|
|
172
|
+
/** This selector gives the ztunnel pods in istio-system: the label `app` of the chart of ztunnel. */
|
|
173
|
+
export declare const ZTUNNEL_PODS = "app=ztunnel";
|
|
171
174
|
/**
|
|
172
175
|
* This function gives the AuthorizationPolicy of the trigger `authorization-policy`. It denies each
|
|
173
176
|
* request to the server pods from the principal of the service account `account`. Istio gives a
|
|
@@ -267,8 +270,23 @@ export declare const serverCluster: (namespace: string) => string;
|
|
|
267
270
|
export declare const EDS_EVERY: Quantity;
|
|
268
271
|
/** This time is the interval of the LDS poll of each server sidecar, the same as the poll of EDS. */
|
|
269
272
|
export declare const LDS_EVERY: Quantity;
|
|
270
|
-
/**
|
|
273
|
+
/**
|
|
274
|
+
* This function tells if the watcher follows each ztunnel pod and its log for a cell: a cell in the
|
|
275
|
+
* flavor `ambient`. A cell of sidecar pods in that flavor needs the lines of the ztunnel pods too.
|
|
276
|
+
* They show the restart of ztunnel, which its pods do not see.
|
|
277
|
+
*/
|
|
271
278
|
export declare const followsZtunnel: (cell: Pick<KindCell, "mesh">) => boolean;
|
|
279
|
+
/**
|
|
280
|
+
* This function gives the arguments of kubectl that set the environment of ztunnel for a cell, or
|
|
281
|
+
* remove it (`undo`). The variables go in the sequence of their names.
|
|
282
|
+
*/
|
|
283
|
+
export declare const ztunnelEnvArgs: (env: Readonly<Record<string, string>>, undo: boolean) => string[];
|
|
284
|
+
/**
|
|
285
|
+
* This function sets the environment of ztunnel for a cell, or removes it (`undo`). Then it waits for
|
|
286
|
+
* the rollout of the DaemonSet, and until no old ztunnel pod is left. An old pod drains for its
|
|
287
|
+
* deadline after the rollout replaces it. Thus no run starts beside a ztunnel pod that is going.
|
|
288
|
+
*/
|
|
289
|
+
export declare const setZtunnelEnv: (cluster: Cluster, env: Readonly<Record<string, string>>, undo: boolean) => Promise<void>;
|
|
272
290
|
/** This function tells if the lab polls the LDS updates of the server sidecars of a cell: a policy in a sidecar mesh. */
|
|
273
291
|
export declare const pollsListeners: (cell: Pick<KindCell, "mesh" | "trigger">) => boolean;
|
|
274
292
|
//#endregion
|
|
@@ -628,5 +646,5 @@ export declare const toolPath: (tool: Tool, key?: string, cache?: string) => str
|
|
|
628
646
|
*/
|
|
629
647
|
export declare const ensureTool: (tool: Tool, key?: string, cache?: string) => Promise<string>;
|
|
630
648
|
//#endregion
|
|
631
|
-
export { type Band, type BandV1, CLIENT, type Cluster, type ClusterHosts, type ClusterManifest, type Credentials, type Declaration, type Deleted, type DeletionWindow, type EdsSpec, Endpoint, type EnvironmentManifest, FORMAT, FORMAT_V1, type FaultPlan, HEALTH_PORT, type Judged, KIND_NOISE, KUBE_PROXY_MODES, type KindCell, type KindCellRecord, type KindCellRecordV1, type KindCellResult, type KindCellV1, type KindConfig, type KindDecision, type KindLab, type KindRun, type KindScenario, type KindVerdict, type KindVerdictV1, type KubeProxyMode, type KubeletProbe, LAST_SLACK, LOG_LABEL, type LdsSpec, Line, type ListenerCounts, type LoadSample, type Mesh, type MeshFlavor, type MeshImages, type MeshMode, NODE_IMAGES, type NodeImage, PORT, type Parts, PodLine, type PodSettings, type Prediction, type PredictionV1, type Propagated, type Propagation, PropagationSource, REDIS, RELATIVE, RecordLine, type RedisDeclaration, type Reference, type Replica, Request, type ResetReuse, RulesLine, type RunCheck, type RunCheckV1, type RunFacts, type RunFactsV1, type RunOptions, SERVER, STAGES, type ServiceRule, SliceLine, type Stack, type Stamp, type Step, type Target, type Template, type TimeSpread, type Tool, type Trigger, type TriggerKind, type TriggerV1, type Verdicts, type Workload, backendsAt, bandOf, byClients, byNode, calibrationOf, cellsTable, checkRun, cidrOverlap, cidrRange, deletionOf, deletionsOf, dropOf, emptyOf, factsOf, failuresOf, goDuration, intakeOf, judge, judgeKind, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, kindConfig, kubeProxyPatch, medianInterval, medianText, middle, nodeNames, observationAfter, observationEnd, outcomesOf, podsAt, podsOf, predictionText, propagationOf, propagationSource, quantileOf, readRecord, recordsOf, referenceOf, requestsOf, restartsAt, reusesReset, rulesOf, scenario, scenarioProblems, seconds, secondsOf, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, slicesOf, stoppingOf, timeSpread, transitionOf, unitOf, updatesOf, upgrade, upgradeCell };
|
|
649
|
+
export { type Band, type BandV1, CLIENT, type Cluster, type ClusterHosts, type ClusterManifest, type Credentials, type Declaration, type Deleted, type DeletionWindow, type EdsSpec, Endpoint, type EnvironmentManifest, FORMAT, FORMAT_V1, type FaultPlan, HEALTH_PORT, type Judged, KIND_NOISE, KUBE_PROXY_MODES, type KindCell, type KindCellRecord, type KindCellRecordV1, type KindCellResult, type KindCellV1, type KindConfig, type KindDecision, type KindLab, type KindRun, type KindScenario, type KindVerdict, type KindVerdictV1, type KubeProxyMode, type KubeletProbe, LAST_SLACK, LOG_LABEL, type LdsSpec, Line, type ListenerCounts, type LoadSample, type Mesh, type MeshFlavor, type MeshImages, type MeshMode, NODE_IMAGES, type NodeImage, PORT, type Parts, PodLine, type PodSettings, type Prediction, type PredictionV1, type Propagated, type Propagation, PropagationSource, REDIS, RELATIVE, RecordLine, type RedisDeclaration, type Reference, type Replica, Request, type ResetReuse, RulesLine, type RunCheck, type RunCheckV1, type RunFacts, type RunFactsV1, type RunOptions, SERVER, STAGES, type ServiceRule, SliceLine, SocketLine, type Stack, type Stamp, type Step, type Target, type Template, type TimeSpread, type Tool, type Trigger, type TriggerKind, type TriggerV1, type Verdicts, type WindowPrediction, type Workload, ZtunnelPodLine, backendsAt, bandOf, byClients, byNode, calibrationOf, cellsTable, checkRun, cidrOverlap, cidrRange, deletionOf, deletionsOf, dropOf, emptyOf, factsOf, failuresOf, goDuration, intakeOf, judge, judgeKind, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, kindConfig, kubeProxyPatch, medianInterval, medianText, middle, nodeNames, observationAfter, observationEnd, outcomesOf, podsAt, podsOf, predictionText, propagationOf, propagationSource, quantileOf, readRecord, recordsOf, referenceOf, requestsOf, restartOf, restartsAt, reusesReset, rulesOf, scenario, scenarioProblems, seconds, secondsOf, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, slicesOf, socketsOf, stoppingOf, timeSpread, transitionOf, unitOf, updatesOf, upgrade, upgradeCell, ztunnelPodsOf, ztunnelReplaced };
|
|
632
650
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as sliceEmptyOf, A as medianInterval, B as intakeOf, C as propagationOf, D as unitOf, E as reusesReset, F as byNode, G as recordsOf, H as outcomesOf, I as deletionOf, J as restartsAt, K as requestsOf, L as dropOf, M as quantileOf, N as backendsAt, O as updatesOf, P as byClients, Q as sliceDropOf, R as emptyOf, S as observationEnd, T as referenceOf, U as podsAt, V as kernelCounts, W as podsOf, X as signalOf, Y as rulesOf, Z as signalsOf, _ as checkRun, a as calibrationOf, at as ztunnelReplaced, b as judgeKind, c as predictionText, ct as LOG_LABEL, d as FORMAT_V1, dt as SERVER, et as slicesOf, f as judge, ft as bandOf, g as LAST_SLACK, h as KIND_NOISE, ht as showBand, i as readRecord, it as ztunnelPodsOf, j as middle, k as RELATIVE, l as timeSpread, lt as PORT, m as upgradeCell, mt as secondsOf, n as kindCellRecordSchema, nt as stoppingOf, o as cellsTable, ot as CLIENT, p as upgrade, pt as seconds, q as restartOf, r as kindCellRecordV1Schema, rt as transitionOf, s as medianText, st as HEALTH_PORT, t as STAGES, tt as socketsOf, u as FORMAT, ut as REDIS, v as deletionsOf, w as propagationSource, x as observationAfter, y as factsOf, z as failuresOf } from "./judge-CmiF-Fcs.js";
|
|
2
2
|
import { valueIn } from "@mark1russell7/quantity";
|
|
3
3
|
import { execFile, execFileSync } from "node:child_process";
|
|
4
4
|
import { chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -637,6 +637,8 @@ const DELETION_COST = "controller.kubernetes.io/pod-deletion-cost";
|
|
|
637
637
|
const POLICY = "lab-deny";
|
|
638
638
|
/** This service account is the account of the clients: their pods name no other account. */
|
|
639
639
|
const CLIENT_ACCOUNT = "default";
|
|
640
|
+
/** This selector gives the ztunnel pods in istio-system: the label `app` of the chart of ztunnel. */
|
|
641
|
+
const ZTUNNEL_PODS = "app=ztunnel";
|
|
640
642
|
/**
|
|
641
643
|
* This function gives the AuthorizationPolicy of the trigger `authorization-policy`. It denies each
|
|
642
644
|
* request to the server pods from the principal of the service account `account`. Istio gives a
|
|
@@ -950,6 +952,51 @@ const planOf = (cell) => {
|
|
|
950
952
|
]
|
|
951
953
|
}]
|
|
952
954
|
};
|
|
955
|
+
case "idle": return {
|
|
956
|
+
inject: [{
|
|
957
|
+
do: "signal",
|
|
958
|
+
target: "clients",
|
|
959
|
+
signal: "SIGUSR1"
|
|
960
|
+
}],
|
|
961
|
+
hold: needs(trigger.hold, "a hold, the time of the gap"),
|
|
962
|
+
heal: [{
|
|
963
|
+
do: "signal",
|
|
964
|
+
target: "clients",
|
|
965
|
+
signal: "SIGUSR2"
|
|
966
|
+
}],
|
|
967
|
+
restore: [{
|
|
968
|
+
do: "signal",
|
|
969
|
+
target: "clients",
|
|
970
|
+
signal: "SIGUSR2"
|
|
971
|
+
}]
|
|
972
|
+
};
|
|
973
|
+
case "ztunnel-restart": return {
|
|
974
|
+
inject: [{
|
|
975
|
+
do: "kubectl",
|
|
976
|
+
args: [
|
|
977
|
+
"-n",
|
|
978
|
+
"istio-system",
|
|
979
|
+
"delete",
|
|
980
|
+
"pod",
|
|
981
|
+
"-l",
|
|
982
|
+
ZTUNNEL_PODS,
|
|
983
|
+
"--wait=false"
|
|
984
|
+
]
|
|
985
|
+
}],
|
|
986
|
+
hold: null,
|
|
987
|
+
heal: [],
|
|
988
|
+
restore: [{
|
|
989
|
+
do: "kubectl",
|
|
990
|
+
args: [
|
|
991
|
+
"-n",
|
|
992
|
+
"istio-system",
|
|
993
|
+
"rollout",
|
|
994
|
+
"status",
|
|
995
|
+
"daemonset/ztunnel",
|
|
996
|
+
"--timeout=300s"
|
|
997
|
+
]
|
|
998
|
+
}]
|
|
999
|
+
};
|
|
953
1000
|
}
|
|
954
1001
|
};
|
|
955
1002
|
/** This function tells if a trigger stops a pod of the service with its grace: a deletion, an eviction, a scale-in or a rollout. */
|
|
@@ -986,7 +1033,8 @@ const FILES = [
|
|
|
986
1033
|
["conntrack.ts", "src/conntrack.ts"],
|
|
987
1034
|
["drops.ts", "src/drops.ts"],
|
|
988
1035
|
["eds.ts", "src/eds.ts"],
|
|
989
|
-
["lds.ts", "src/lds.ts"]
|
|
1036
|
+
["lds.ts", "src/lds.ts"],
|
|
1037
|
+
["client-lines.ts", "src/client-lines.ts"]
|
|
990
1038
|
];
|
|
991
1039
|
const shell = process.platform === "win32";
|
|
992
1040
|
/** This function gives each file of a staged context, with its path in the context, in a stable sequence. */
|
|
@@ -1277,8 +1325,55 @@ const LDS_EVERY = {
|
|
|
1277
1325
|
value: 50,
|
|
1278
1326
|
unit: "ms"
|
|
1279
1327
|
};
|
|
1280
|
-
/**
|
|
1281
|
-
|
|
1328
|
+
/**
|
|
1329
|
+
* This function tells if the watcher follows each ztunnel pod and its log for a cell: a cell in the
|
|
1330
|
+
* flavor `ambient`. A cell of sidecar pods in that flavor needs the lines of the ztunnel pods too.
|
|
1331
|
+
* They show the restart of ztunnel, which its pods do not see.
|
|
1332
|
+
*/
|
|
1333
|
+
const followsZtunnel = (cell) => cell.mesh?.flavor === "ambient";
|
|
1334
|
+
/**
|
|
1335
|
+
* This function gives the arguments of kubectl that set the environment of ztunnel for a cell, or
|
|
1336
|
+
* remove it (`undo`). The variables go in the sequence of their names.
|
|
1337
|
+
*/
|
|
1338
|
+
const ztunnelEnvArgs = (env, undo) => [
|
|
1339
|
+
"-n",
|
|
1340
|
+
"istio-system",
|
|
1341
|
+
"set",
|
|
1342
|
+
"env",
|
|
1343
|
+
"daemonset/ztunnel",
|
|
1344
|
+
...Object.keys(env).sort().map((key) => undo ? `${key}-` : `${key}=${env[key]}`)
|
|
1345
|
+
];
|
|
1346
|
+
/**
|
|
1347
|
+
* This function sets the environment of ztunnel for a cell, or removes it (`undo`). Then it waits for
|
|
1348
|
+
* the rollout of the DaemonSet, and until no old ztunnel pod is left. An old pod drains for its
|
|
1349
|
+
* deadline after the rollout replaces it. Thus no run starts beside a ztunnel pod that is going.
|
|
1350
|
+
*/
|
|
1351
|
+
const setZtunnelEnv = async (cluster, env, undo) => {
|
|
1352
|
+
await cluster.kubectl(ztunnelEnvArgs(env, undo));
|
|
1353
|
+
await cluster.kubectl([
|
|
1354
|
+
"-n",
|
|
1355
|
+
"istio-system",
|
|
1356
|
+
"rollout",
|
|
1357
|
+
"status",
|
|
1358
|
+
"daemonset/ztunnel",
|
|
1359
|
+
"--timeout=300s"
|
|
1360
|
+
]);
|
|
1361
|
+
const until = Date.now() + 12e4;
|
|
1362
|
+
for (;;) {
|
|
1363
|
+
if (JSON.parse(await cluster.kubectl([
|
|
1364
|
+
"-n",
|
|
1365
|
+
"istio-system",
|
|
1366
|
+
"get",
|
|
1367
|
+
"pods",
|
|
1368
|
+
"-l",
|
|
1369
|
+
"app=ztunnel",
|
|
1370
|
+
"-o",
|
|
1371
|
+
"json"
|
|
1372
|
+
])).items.every((item) => item.metadata.deletionTimestamp === void 0)) return;
|
|
1373
|
+
if (Date.now() > until) throw new Error("an old ztunnel pod is still going 120 s after the rollout");
|
|
1374
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
1375
|
+
}
|
|
1376
|
+
};
|
|
1282
1377
|
/** This function tells if the lab polls the LDS updates of the server sidecars of a cell: a policy in a sidecar mesh. */
|
|
1283
1378
|
const pollsListeners = (cell) => cell.mesh?.mode === "sidecar" && cell.trigger.kind === "authorization-policy";
|
|
1284
1379
|
//#endregion
|
|
@@ -2591,6 +2686,38 @@ const injectServers = async (ctx, action, replicas) => {
|
|
|
2591
2686
|
t1: Math.max(...all.map((stamp) => stamp.t1))
|
|
2592
2687
|
};
|
|
2593
2688
|
};
|
|
2689
|
+
/**
|
|
2690
|
+
* This function sends a signal to the process of each client pod: the target `clients`. The nodes go
|
|
2691
|
+
* at the same time. SIGUSR1 finds the PID of each client, and SIGUSR2 uses it again, thus the heal of
|
|
2692
|
+
* a gap comes soon after its hold. When that PID fails, the client started again, and the lab finds
|
|
2693
|
+
* its PID. The stamp is the first act and the last end.
|
|
2694
|
+
*/
|
|
2695
|
+
const signalClients = async (ctx, signal) => {
|
|
2696
|
+
const clients = [...ctx.tracker.pods.values()].filter((pod) => pod.labels["app"] === "client" && !pod.del && pod.node !== null);
|
|
2697
|
+
if (clients.length === 0) throw new Error(`the signal ${signal} needs clients`);
|
|
2698
|
+
const stamps = await Promise.all(clients.map(async (pod) => {
|
|
2699
|
+
const node = pod.node;
|
|
2700
|
+
const known = signal === "SIGUSR2" ? ctx.pids.get(pod.name) : void 0;
|
|
2701
|
+
if (known !== void 0) try {
|
|
2702
|
+
return await ctx.lab.act(node, {
|
|
2703
|
+
op: "signal",
|
|
2704
|
+
pid: known,
|
|
2705
|
+
signal
|
|
2706
|
+
});
|
|
2707
|
+
} catch {}
|
|
2708
|
+
const pid = await ctx.lab.pidOf(node, ctx.namespace, pod.name, CLIENT);
|
|
2709
|
+
ctx.pids.set(pod.name, pid);
|
|
2710
|
+
return ctx.lab.act(node, {
|
|
2711
|
+
op: "signal",
|
|
2712
|
+
pid,
|
|
2713
|
+
signal
|
|
2714
|
+
});
|
|
2715
|
+
}));
|
|
2716
|
+
return {
|
|
2717
|
+
t0: Math.min(...stamps.map((stamp) => stamp.t0)),
|
|
2718
|
+
t1: Math.max(...stamps.map((stamp) => stamp.t1))
|
|
2719
|
+
};
|
|
2720
|
+
};
|
|
2594
2721
|
/** This function does one step of a fault plan. It gives the stamp of its act, or null for an act without a stamp. */
|
|
2595
2722
|
const doStep = async (ctx, step, replicas, background) => {
|
|
2596
2723
|
const { lab, cluster, namespace } = ctx;
|
|
@@ -2598,6 +2725,7 @@ const doStep = async (ctx, step, replicas, background) => {
|
|
|
2598
2725
|
const fill = (args) => args.map((arg) => arg.replaceAll("{namespace}", namespace).replaceAll(POD, target.pod).replaceAll(NODE, target.node).replaceAll(VERSION, String(ctx.version)));
|
|
2599
2726
|
const where = async (which) => {
|
|
2600
2727
|
if (which === "servers") throw new Error("the target servers is a target of the injector only");
|
|
2728
|
+
if (which === "clients") throw new Error("the target clients is a target of a signal only");
|
|
2601
2729
|
if (which === "server") return {
|
|
2602
2730
|
node: target.node,
|
|
2603
2731
|
pid: await lab.pidOf(target.node, namespace, target.pod, "app"),
|
|
@@ -2649,6 +2777,7 @@ const doStep = async (ctx, step, replicas, background) => {
|
|
|
2649
2777
|
};
|
|
2650
2778
|
}
|
|
2651
2779
|
case "signal": {
|
|
2780
|
+
if (step.target === "clients") return signalClients(ctx, step.signal);
|
|
2652
2781
|
const at = await where(step.target);
|
|
2653
2782
|
return lab.act(at.node, {
|
|
2654
2783
|
op: "signal",
|
|
@@ -2738,6 +2867,7 @@ const over = (ctx, run, lines) => {
|
|
|
2738
2867
|
].includes(kind) && (ctx.cell.template === "T07" || ctx.cell.template === "T15")) return kind === "scale-in" || kind === "node-drain" ? run.replicas.some((replica) => !ctx.tracker.pods.has(replica.pod)) : !ctx.tracker.pods.has(run.target.pod);
|
|
2739
2868
|
if (kind === "rolling-update" || kind === "rollout") return run.replicas.every((replica) => !ctx.tracker.pods.has(replica.pod));
|
|
2740
2869
|
if (kind === "app-stall") return lines.some((line) => line.ev === "stall" && line["phase"] === "end");
|
|
2870
|
+
if (kind === "ztunnel-restart") return ztunnelReplaced(lines, ctx.cluster.nodes);
|
|
2741
2871
|
return true;
|
|
2742
2872
|
};
|
|
2743
2873
|
/** This function gives the replica that the trigger stopped. For a scale-in or a drain, the controller chooses it. */
|
|
@@ -2997,7 +3127,9 @@ const runKindCells = async (cells, options) => {
|
|
|
2997
3127
|
const tracker = new Tracker();
|
|
2998
3128
|
const off = lab.listen(tracker.see);
|
|
2999
3129
|
const loads = [];
|
|
3130
|
+
const ztunnelEnv = cell.mesh?.ztunnel;
|
|
3000
3131
|
try {
|
|
3132
|
+
if (ztunnelEnv !== void 0) await setZtunnelEnv(cluster, ztunnelEnv, false);
|
|
3001
3133
|
const clusterIp = await deploy(lab, cluster, cell, namespace, images);
|
|
3002
3134
|
for (const name of [...lab.own(), loader.getName().replace(/^\//, "")]) ours.add(name);
|
|
3003
3135
|
const ctx = {
|
|
@@ -3059,6 +3191,7 @@ const runKindCells = async (cells, options) => {
|
|
|
3059
3191
|
"--wait=true",
|
|
3060
3192
|
"--timeout=300s"
|
|
3061
3193
|
]).catch((error) => options.log(` ${String(error)}`));
|
|
3194
|
+
if (ztunnelEnv !== void 0) await setZtunnelEnv(cluster, ztunnelEnv, true).catch((error) => options.log(` the undo of the environment of ztunnel failed: ${String(error)}`));
|
|
3062
3195
|
}
|
|
3063
3196
|
}
|
|
3064
3197
|
} finally {
|
|
@@ -3095,11 +3228,14 @@ const estimate = (cell, environments = 2) => {
|
|
|
3095
3228
|
"app-stall": hold + 40,
|
|
3096
3229
|
"cpu-throttle": hold + 40,
|
|
3097
3230
|
rollout: hold + 50,
|
|
3098
|
-
"authorization-policy": hold + 20
|
|
3231
|
+
"authorization-policy": hold + 20,
|
|
3232
|
+
idle: hold + 5,
|
|
3233
|
+
"ztunnel-restart": 40
|
|
3099
3234
|
};
|
|
3100
3235
|
const after = observationAfter(cell);
|
|
3101
3236
|
const observed = after === null ? 0 : hold + valueIn(after, "s");
|
|
3102
|
-
const
|
|
3237
|
+
const closes = cell.prediction.kind === "ends" && cell.prediction.band !== null ? valueIn(cell.prediction.band.hi, "s") + 3 : 0;
|
|
3238
|
+
const each = 25 + Math.max(observed, closes, trigger[cell.trigger.kind] ?? (cell.template === "T18" ? hold + 35 : 15 + valueIn(cell.pod.preStop, "s")));
|
|
3103
3239
|
return (cell.mesh === void 0 ? 60 : 180) * environments + (cell.runs + environments) * each;
|
|
3104
3240
|
};
|
|
3105
3241
|
/**
|
|
@@ -3131,6 +3267,6 @@ const shardOf = (cells, index, count) => {
|
|
|
3131
3267
|
return out;
|
|
3132
3268
|
};
|
|
3133
3269
|
//#endregion
|
|
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 };
|
|
3270
|
+
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, ZTUNNEL_PODS, 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, restartOf, restartsAt, reusesReset, rulesOf, runKindCells, scenario, scenarioOf, scenarioProblems, seconds, secondsOf, serverCluster, serverObjects, setZtunnelEnv, sha256, shardOf, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, slicesOf, socketsOf, stackOf, stageLabImage, stallRemovalBand, stoppingOf, tarEntry, timeSpread, toolPath, transitionOf, unitOf, updatesOf, upgrade, upgradeCell, verdictsOf, writeResult, ztunnelEnvArgs, ztunnelPodsOf, ztunnelReplaced };
|
|
3135
3271
|
|
|
3136
3272
|
//# sourceMappingURL=index.js.map
|