@mark1russell7/kind 0.11.12 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -1
- package/build/index.d.ts +105 -5
- package/build/index.js +385 -39
- package/build/index.js.map +1 -1
- package/build/{judge-n0rL_RR0.js → judge-BdykXjF2.js} +73 -12
- package/build/judge-BdykXjF2.js.map +1 -0
- package/build/{judge-BKe6dJ3O.d.ts → judge-Cnn42gih.d.ts} +72 -15
- package/build/judge.d.ts +2 -2
- package/build/judge.js +1 -1
- package/image/Dockerfile +1 -1
- package/image/agent.mjs +20 -6
- package/image/client.mjs +58 -1
- package/package.json +2 -1
- package/src/backends.ts +59 -4
- package/src/place.ts +43 -0
- package/build/judge-n0rL_RR0.js.map +0 -1
package/build/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { $ as rulesOf, A as RELATIVE, B as dropOf, C as observationEnd, D as reusesReset, E as referenceOf, F as TCP_STATE, G as kernelCounts, H as failuresOf, I as backendsAt, J as podsOf, K as outcomesOf, L as byClients, M as middle, N as quantileOf, O as unitOf, P as HBONE_PORT, Q as restartsAt, R as byNode, S as observationAfter, St as
|
|
1
|
+
import { $ as rulesOf, A as RELATIVE, B as dropOf, C as observationEnd, Ct as showBand, D as reusesReset, E as referenceOf, F as TCP_STATE, G as kernelCounts, H as failuresOf, I as backendsAt, J as podsOf, K as outcomesOf, L as byClients, M as middle, N as quantileOf, O as unitOf, P as HBONE_PORT, Q as restartsAt, R as byNode, S as observationAfter, St as secondsOf, T as propagationSource, U as intakeOf, V as emptyOf, W as isTunnelState, X as requestsOf, Y as recordsOf, Z as restartOf, _ as checkRun, _t as PORT, a as calibrationOf, at as sliceReadyAt, b as factsOf, bt as bandOf, c as predictionText, ct as socketsOf, d as FORMAT_V1, dt as tunnelsOf, et as sessionsAt, f as judge, ft as ztunnelPodsOf, g as LAST_SLACK, gt as LOG_LABEL, h as KIND_NOISE, ht as HEALTH_PORT, i as readRecord, it as sliceEmptyOf, j as medianInterval, k as updatesOf, l as timeSpread, lt as stoppingOf, m as upgradeCell, mt as CLIENT, n as kindCellRecordSchema, nt as signalsOf, o as cellsTable, ot as sliceReadyOf, p as upgrade, pt as ztunnelReplaced, q as podsAt, r as kindCellRecordV1Schema, rt as sliceDropOf, s as medianText, st as slicesOf, t as STAGES, tt as signalOf, u as FORMAT, ut as transitionOf, v as cyclesOf, vt as REDIS, w as propagationOf, x as judgeKind, xt as seconds, y as deletionsOf, yt as SERVER, z as deletionOf } from "./judge-BdykXjF2.js";
|
|
2
2
|
import { valueIn } from "@mark1russell7/quantity";
|
|
3
|
-
import { execFile, execFileSync } from "node:child_process";
|
|
4
3
|
import { chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
5
4
|
import { dirname, join } from "node:path";
|
|
5
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
@@ -27,8 +27,10 @@ const hexIp = (hex) => [
|
|
|
27
27
|
* - iptables: the `KUBE-SVC` chain of the Service jumps to a `KUBE-SEP` chain for each backend, and that chain does the DNAT.
|
|
28
28
|
* - nftables: the `service-` chain of the Service does the DNAT to each backend, or it names an `endpoint-` chain for each one.
|
|
29
29
|
* - IPVS: `/proc/net/ip_vs` has the line of the virtual service, then a line for each real server.
|
|
30
|
+
* - Cilium: the JSON of the services of the agent (`ciliumBackendsOf`).
|
|
30
31
|
*/
|
|
31
32
|
const backendsOf = (mode, text, rule) => {
|
|
33
|
+
if (mode === "cilium") return ciliumBackendsOf(text, rule);
|
|
32
34
|
const out = /* @__PURE__ */ new Set();
|
|
33
35
|
const lines = text.split("\n");
|
|
34
36
|
if (mode === "iptables") {
|
|
@@ -70,6 +72,45 @@ const backendsOf = (mode, text, rule) => {
|
|
|
70
72
|
}
|
|
71
73
|
return [...out].sort();
|
|
72
74
|
};
|
|
75
|
+
/** This function gives the services in a reply of the agent of Cilium. It refuses a reply that is not a JSON list. */
|
|
76
|
+
const ciliumServices = (text) => {
|
|
77
|
+
const data = JSON.parse(text);
|
|
78
|
+
if (!Array.isArray(data)) throw new Error("the reply of the agent of Cilium is not a list of services");
|
|
79
|
+
return data;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* This function gives the backends of a Service in the reply of `GET /v1/service` of the agent of
|
|
83
|
+
* Cilium 1.20, as `ip:port`, sorted. The model is `ServiceSpec` of `api/v1/openapi.yaml`. The
|
|
84
|
+
* frontend is the ClusterIP and the port of the rule, with the scope `external` or no scope.
|
|
85
|
+
*
|
|
86
|
+
* The backends are those in the state `active`, or with no state. If no backend is active, the
|
|
87
|
+
* backends are those in the state `terminating`. The BPF reconciler gives the slots of a service
|
|
88
|
+
* in this sequence (`pkg/loadbalancer/reconciler/bpf_reconciler.go`). Thus a new connection goes
|
|
89
|
+
* only to them. kube-proxy does the same with the endpoints that serve and stop. The other
|
|
90
|
+
* states take no new connection: `terminating-not-serving`, `quarantined` and `maintenance`.
|
|
91
|
+
*/
|
|
92
|
+
const ciliumBackendsOf = (text, rule) => {
|
|
93
|
+
const backends = ciliumServices(text).filter((service) => {
|
|
94
|
+
const front = service.spec?.["frontend-address"];
|
|
95
|
+
const protocol = (front?.protocol ?? "tcp").toLowerCase();
|
|
96
|
+
return front?.ip === rule.ip && front.port === rule.port && (protocol === "tcp" || protocol === "any") && (front.scope ?? "external") === "external";
|
|
97
|
+
}).flatMap((service) => service.spec?.["backend-addresses"] ?? []);
|
|
98
|
+
const of = (states) => [...new Set(backends.filter((b) => states.includes(b.state) && b.ip !== void 0).map((b) => `${b.ip}:${b.port ?? 0}`))].sort();
|
|
99
|
+
const active = of(["active", void 0]);
|
|
100
|
+
return active.length > 0 ? active : of(["terminating"]);
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* This function tells if a dump of the rules names the Service of a rule. The rules of kube-proxy
|
|
104
|
+
* name it as `namespace/name`. The JSON of Cilium names it in the flags of its frontend.
|
|
105
|
+
*/
|
|
106
|
+
const namesService = (mode, text, rule) => {
|
|
107
|
+
if (mode !== "cilium") return text.includes(`${rule.ns}/${rule.svc}`);
|
|
108
|
+
try {
|
|
109
|
+
return ciliumServices(text).some((service) => service.spec?.flags?.namespace === rule.ns && service.spec.flags.name === rule.svc);
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
73
114
|
//#endregion
|
|
74
115
|
//#region src/scenario.ts
|
|
75
116
|
const KUBE_PROXY_MODES = [
|
|
@@ -77,6 +118,9 @@ const KUBE_PROXY_MODES = [
|
|
|
77
118
|
"ipvs",
|
|
78
119
|
"nftables"
|
|
79
120
|
];
|
|
121
|
+
const DATAPLANES = [...KUBE_PROXY_MODES, "cilium"];
|
|
122
|
+
/** This function tells if a dataplane is a mode of kube-proxy. */
|
|
123
|
+
const isKubeProxyMode = (mode) => mode !== "cilium";
|
|
80
124
|
/**
|
|
81
125
|
* These are the node images of kind v0.33.0, from its release notes. The image of Kubernetes 1.37
|
|
82
126
|
* is v1.37.0. The research names v1.37.1 for some defaults, thus a default that changed in the
|
|
@@ -100,12 +144,16 @@ const ZONE_KEPT = "zone-a";
|
|
|
100
144
|
const ZONE_LOST = "zone-b";
|
|
101
145
|
/** This function gives the zone of the node at the position `at` of `nodeNames`, in a cluster of `count` nodes. */
|
|
102
146
|
const zoneOf = (at, count) => at === count - 1 && at > 0 ? ZONE_LOST : ZONE_KEPT;
|
|
103
|
-
/**
|
|
147
|
+
/**
|
|
148
|
+
* This function gives a scenario with the defaults of Kubernetes for kube-proxy, as explicit values.
|
|
149
|
+
* The dataplane `cilium` has no kube-proxy, and its CNI is Cilium.
|
|
150
|
+
*/
|
|
104
151
|
const scenario = (name, mode, workers, kubernetes = "1.37", zoned = false) => ({
|
|
105
152
|
name,
|
|
106
153
|
kubernetes,
|
|
107
154
|
workers,
|
|
108
|
-
|
|
155
|
+
dataplane: mode,
|
|
156
|
+
kubeProxy: isKubeProxyMode(mode) ? {
|
|
109
157
|
mode,
|
|
110
158
|
minSyncPeriod: {
|
|
111
159
|
value: 1,
|
|
@@ -115,20 +163,27 @@ const scenario = (name, mode, workers, kubernetes = "1.37", zoned = false) => ({
|
|
|
115
163
|
value: 30,
|
|
116
164
|
unit: "s"
|
|
117
165
|
}
|
|
118
|
-
},
|
|
119
|
-
cni: "kindnet",
|
|
166
|
+
} : null,
|
|
167
|
+
cni: isKubeProxyMode(mode) ? "kindnet" : "cilium",
|
|
120
168
|
podSubnet: "10.244.0.0/16",
|
|
121
169
|
serviceSubnet: "10.96.0.0/16",
|
|
122
170
|
...zoned ? { zoned } : {}
|
|
123
171
|
});
|
|
124
172
|
const NAME = /^[a-z0-9]([a-z0-9-]{0,30}[a-z0-9])?$/;
|
|
125
|
-
/**
|
|
173
|
+
/**
|
|
174
|
+
* This function gives the problems of a scenario. A problem is a name that kind refuses, no image for
|
|
175
|
+
* the version, or a count out of range. A kube-proxy or a CNI that does not agree with the dataplane is a problem too.
|
|
176
|
+
*/
|
|
126
177
|
const scenarioProblems = (s) => {
|
|
127
178
|
const problems = [];
|
|
128
179
|
if (!NAME.test(s.name)) problems.push(`the name ${s.name} is not lowercase letters, digits and dashes of 1 to 32 characters`);
|
|
129
180
|
if (NODE_IMAGES[s.kubernetes] === void 0) problems.push(`no node image is pinned for Kubernetes ${s.kubernetes}`);
|
|
130
181
|
if (!Number.isInteger(s.workers) || s.workers < 0 || s.workers > 8) problems.push(`${s.workers} workers is not a whole number from 0 to 8`);
|
|
131
182
|
if (cidrOverlap(s.podSubnet, s.serviceSubnet)) problems.push(`the pod subnet ${s.podSubnet} overlaps the service subnet ${s.serviceSubnet}`);
|
|
183
|
+
if (isKubeProxyMode(s.dataplane)) {
|
|
184
|
+
if (s.kubeProxy?.mode !== s.dataplane) problems.push(`the dataplane ${s.dataplane} needs kube-proxy in the mode ${s.dataplane}`);
|
|
185
|
+
if (s.cni !== "kindnet") problems.push(`the dataplane ${s.dataplane} needs the CNI kindnet`);
|
|
186
|
+
} else if (s.kubeProxy !== null || s.cni !== "cilium") problems.push("the dataplane cilium needs no kube-proxy and the CNI cilium");
|
|
132
187
|
return problems;
|
|
133
188
|
};
|
|
134
189
|
/** This function gives the names of the node containers, as kind names them: the control plane, then each worker. */
|
|
@@ -139,17 +194,26 @@ const goDuration = (amount) => {
|
|
|
139
194
|
if (!Number.isInteger(ms) || ms < 0) throw new RangeError(`${ms} ms is not a whole number of milliseconds`);
|
|
140
195
|
return ms % 1e3 === 0 ? `${ms / 1e3}s` : `${ms}ms`;
|
|
141
196
|
};
|
|
142
|
-
/** This function gives the KubeProxyConfiguration patch of a scenario, as kubeadm reads it. */
|
|
143
|
-
const kubeProxyPatch = (s) =>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
197
|
+
/** This function gives the KubeProxyConfiguration patch of a scenario, as kubeadm reads it. It refuses a scenario with no kube-proxy. */
|
|
198
|
+
const kubeProxyPatch = (s) => {
|
|
199
|
+
const proxy = s.kubeProxy;
|
|
200
|
+
if (proxy === null) throw new Error(`the scenario ${s.name} has no kube-proxy`);
|
|
201
|
+
return [
|
|
202
|
+
"apiVersion: kubeproxy.config.k8s.io/v1alpha1",
|
|
203
|
+
"kind: KubeProxyConfiguration",
|
|
204
|
+
`mode: ${proxy.mode}`,
|
|
205
|
+
`${proxy.mode}:`,
|
|
206
|
+
` minSyncPeriod: ${goDuration(proxy.minSyncPeriod)}`,
|
|
207
|
+
` syncPeriod: ${goDuration(proxy.syncPeriod)}`,
|
|
208
|
+
""
|
|
209
|
+
].join("\n");
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* This function generates the configuration of kind from a scenario. It refuses a scenario with a
|
|
213
|
+
* problem. The dataplane `cilium` gets no kindnet, as the kind guide of Cilium 1.20.4 tells
|
|
214
|
+
* (`Documentation/installation/kind-configure.rst`). It also gets no kube-proxy, because Cilium
|
|
215
|
+
* replaces it (`Documentation/network/kubernetes/kubeproxy-free.rst`).
|
|
216
|
+
*/
|
|
153
217
|
const kindConfig = (s) => {
|
|
154
218
|
const problems = scenarioProblems(s);
|
|
155
219
|
if (problems.length > 0) throw new Error(`the scenario ${s.name}: ${problems.join("; ")}`);
|
|
@@ -159,8 +223,8 @@ const kindConfig = (s) => {
|
|
|
159
223
|
apiVersion: "kind.x-k8s.io/v1alpha4",
|
|
160
224
|
networking: {
|
|
161
225
|
ipFamily: "ipv4",
|
|
162
|
-
kubeProxyMode: s.kubeProxy
|
|
163
|
-
disableDefaultCNI:
|
|
226
|
+
kubeProxyMode: s.kubeProxy?.mode ?? "none",
|
|
227
|
+
disableDefaultCNI: s.cni !== "kindnet",
|
|
164
228
|
podSubnet: s.podSubnet,
|
|
165
229
|
serviceSubnet: s.serviceSubnet
|
|
166
230
|
},
|
|
@@ -172,7 +236,7 @@ const kindConfig = (s) => {
|
|
|
172
236
|
...s.zoned === true ? { [ZONE_LABEL]: zoneOf(at, all.length) } : {}
|
|
173
237
|
}
|
|
174
238
|
})),
|
|
175
|
-
kubeadmConfigPatches: [kubeProxyPatch(s)]
|
|
239
|
+
kubeadmConfigPatches: s.kubeProxy === null ? [] : [kubeProxyPatch(s)]
|
|
176
240
|
};
|
|
177
241
|
};
|
|
178
242
|
/** This function gives the first and the last address of an IPv4 CIDR, as numbers. */
|
|
@@ -266,6 +330,30 @@ const ISTIOCTL = {
|
|
|
266
330
|
}
|
|
267
331
|
};
|
|
268
332
|
/**
|
|
333
|
+
* This release of Helm is v3.22.1, from 2026-10-15. The lab installs Cilium from its chart with it.
|
|
334
|
+
* The guide of Cilium 1.20.4 installs with Helm 3. The sums are from the `.sha256sum` files of
|
|
335
|
+
* `get.helm.sh`. The tar file for Windows has `helm.exe`, not `helm`, thus Windows has no asset.
|
|
336
|
+
*/
|
|
337
|
+
const HELM = {
|
|
338
|
+
name: "helm",
|
|
339
|
+
version: "v3.22.1",
|
|
340
|
+
entry: "helm",
|
|
341
|
+
assets: {
|
|
342
|
+
"linux-x64": {
|
|
343
|
+
url: "https://get.helm.sh/helm-v3.22.1-linux-amd64.tar.gz",
|
|
344
|
+
sha256: "896cf63684a42a723d61c980369d6e588330927deb13676ca0d85006dd6da0ea"
|
|
345
|
+
},
|
|
346
|
+
"linux-arm64": {
|
|
347
|
+
url: "https://get.helm.sh/helm-v3.22.1-linux-arm64.tar.gz",
|
|
348
|
+
sha256: "4c29e286b279d170fa048ddfc3e4e83dcd069ca9c52102766ba672d7149e694d"
|
|
349
|
+
},
|
|
350
|
+
"darwin-arm64": {
|
|
351
|
+
url: "https://get.helm.sh/helm-v3.22.1-darwin-arm64.tar.gz",
|
|
352
|
+
sha256: "e73da8a0b0d5193774fdff17ce6319c7436bb22ee229b45fd64f3c40368c753a"
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
/**
|
|
269
357
|
* This function gives the bytes of one file of a gzipped tar file. A header of the ustar format has
|
|
270
358
|
* 512 bytes. It has the name at 0, the size in octal at 124, the type at 156 and the prefix of the
|
|
271
359
|
* name at 345. The data follows the header, in blocks of 512 bytes. The match is on the last part of the
|
|
@@ -485,16 +573,17 @@ const createCluster = async (scenario, dir) => {
|
|
|
485
573
|
"-o",
|
|
486
574
|
"json"
|
|
487
575
|
]));
|
|
488
|
-
const
|
|
576
|
+
const config = scenario.kubeProxy === null ? "cilium-config" : "kube-proxy";
|
|
577
|
+
const data = JSON.parse(await kubectl([
|
|
489
578
|
"-n",
|
|
490
579
|
"kube-system",
|
|
491
580
|
"get",
|
|
492
581
|
"configmap",
|
|
493
|
-
|
|
582
|
+
config,
|
|
494
583
|
"-o",
|
|
495
584
|
"json"
|
|
496
|
-
]));
|
|
497
|
-
const mode = /^mode:\s*(\S*)/m.exec(
|
|
585
|
+
])).data;
|
|
586
|
+
const mode = scenario.kubeProxy === null ? ciliumMode(data) : /^mode:\s*(\S*)/m.exec(data["config.conf"] ?? "")?.[1] ?? "";
|
|
498
587
|
const version = JSON.parse(await kubectl([
|
|
499
588
|
"version",
|
|
500
589
|
"-o",
|
|
@@ -535,6 +624,7 @@ const createCluster = async (scenario, dir) => {
|
|
|
535
624
|
]).catch(() => void 0);
|
|
536
625
|
}
|
|
537
626
|
};
|
|
627
|
+
const wait = scenario.cni === "kindnet" ? ["--wait", "180s"] : [];
|
|
538
628
|
await command(kind, [
|
|
539
629
|
"create",
|
|
540
630
|
"cluster",
|
|
@@ -544,8 +634,7 @@ const createCluster = async (scenario, dir) => {
|
|
|
544
634
|
file,
|
|
545
635
|
"--kubeconfig",
|
|
546
636
|
kubeconfig,
|
|
547
|
-
|
|
548
|
-
"180s"
|
|
637
|
+
...wait
|
|
549
638
|
], {
|
|
550
639
|
env,
|
|
551
640
|
timeout: 9e5
|
|
@@ -553,6 +642,178 @@ const createCluster = async (scenario, dir) => {
|
|
|
553
642
|
return cluster;
|
|
554
643
|
};
|
|
555
644
|
//#endregion
|
|
645
|
+
//#region src/cilium.ts
|
|
646
|
+
/**
|
|
647
|
+
* This module installs Cilium 1.20.4 in a kind cluster with Helm, from images pinned by digest.
|
|
648
|
+
* Cilium is the CNI and replaces kube-proxy (the dataplane `cilium`). Each value of the install
|
|
649
|
+
* that is not a default of the chart is in `ciliumValues`, with its reason.
|
|
650
|
+
*/
|
|
651
|
+
/** This version is the release of Cilium of the lab, from 2026-10-23: the version of the chart and the tag of each image. */
|
|
652
|
+
const CILIUM_VERSION = "1.20.4";
|
|
653
|
+
/** This chart is the Helm chart of Cilium 1.20.4. The sha256 is of the file on `helm.cilium.io` (read on 2026-10-27). */
|
|
654
|
+
const CILIUM_CHART = {
|
|
655
|
+
url: "https://helm.cilium.io/cilium-1.20.4.tgz",
|
|
656
|
+
sha256: "505abff8385811df1a949776fc42e9f10697989a332acc86d6e8933efcb855ed"
|
|
657
|
+
};
|
|
658
|
+
/**
|
|
659
|
+
* These are the images that the install needs, pinned by the digests that the chart of 1.20.4 names
|
|
660
|
+
* (`values.yaml`). The agent, the operator and Envoy are the only images: the install has no
|
|
661
|
+
* node init, no preflight and no Hubble relay. The lab pulls each one by its digest, tags it
|
|
662
|
+
* (`localPin`), and loads it into each node. Thus no node pulls from a registry.
|
|
663
|
+
*/
|
|
664
|
+
const CILIUM_IMAGES = {
|
|
665
|
+
agent: "quay.io/cilium/cilium:v1.20.4@sha256:20fe92e4c86641233a450098c158d2d29085f3419931ab6bf681d2485dedf16e",
|
|
666
|
+
operator: "quay.io/cilium/operator-generic:v1.20.4@sha256:7a7a3489dc66ad67e420d285c8a8d2e169b0957ad15e120f593d27081bf38d82",
|
|
667
|
+
envoy: "quay.io/cilium/cilium-envoy:v1.39.5-1792733054-8c73b2816bc8239e27f1fd53b271884688137459@sha256:547ab45f6092f681d9f54f3be9fa8fb1e705c9c5dc2df388efe3a52aa6965629"
|
|
668
|
+
};
|
|
669
|
+
/**
|
|
670
|
+
* This function gives the Helm values of the install in a cluster. The values that are not the
|
|
671
|
+
* defaults of the chart:
|
|
672
|
+
* - `kubeProxyReplacement: true`, and the address of the API server on the control plane. With no
|
|
673
|
+
* kube-proxy, the agent cannot reach the API through its Service
|
|
674
|
+
* (`Documentation/network/kubernetes/kubeproxy-free.rst`).
|
|
675
|
+
* - `ipam.mode: kubernetes`: the agent takes the pod CIDR of each node from the node, as the kind
|
|
676
|
+
* guide tells (`Documentation/installation/kind.rst`). Thus the pod subnet of the scenario holds.
|
|
677
|
+
* - Each image is an `override`: the helper `cilium.image` of the chart then uses it as it is
|
|
678
|
+
* (`templates/_helpers.tpl`). `pullPolicy: Never`: each image is on each node.
|
|
679
|
+
* - The operator has one replica, not two. Its anti-affinity puts each replica on a different node,
|
|
680
|
+
* and no cell examines a failover of the operator.
|
|
681
|
+
*
|
|
682
|
+
* Socket LB stays at its default. KPR sets it on (`pkg/kpr/kpr.go`). It selects a backend at
|
|
683
|
+
* `connect()`. A connection keeps its backend, as with the conntrack NAT of kube-proxy. On the
|
|
684
|
+
* deletion of a backend, the agent destroys only its UDP sockets, because
|
|
685
|
+
* `LBSockTerminateAllProtos` is off by default (`pkg/loadbalancer/reconciler/termination.go`).
|
|
686
|
+
*/
|
|
687
|
+
const ciliumValues = (clusterName, images) => ({
|
|
688
|
+
kubeProxyReplacement: true,
|
|
689
|
+
k8sServiceHost: `${clusterName}-control-plane`,
|
|
690
|
+
k8sServicePort: 6443,
|
|
691
|
+
ipam: { mode: "kubernetes" },
|
|
692
|
+
image: {
|
|
693
|
+
override: images.agent,
|
|
694
|
+
pullPolicy: "Never"
|
|
695
|
+
},
|
|
696
|
+
operator: {
|
|
697
|
+
replicas: 1,
|
|
698
|
+
image: {
|
|
699
|
+
override: images.operator,
|
|
700
|
+
pullPolicy: "Never"
|
|
701
|
+
}
|
|
702
|
+
},
|
|
703
|
+
envoy: { image: {
|
|
704
|
+
override: images.envoy,
|
|
705
|
+
pullPolicy: "Never"
|
|
706
|
+
} }
|
|
707
|
+
});
|
|
708
|
+
/** These are the workloads in kube-system that the install waits for, then the DNS that waits for the CNI. */
|
|
709
|
+
const CILIUM_WORKLOADS = [
|
|
710
|
+
"daemonset/cilium",
|
|
711
|
+
"deployment/cilium-operator",
|
|
712
|
+
"daemonset/cilium-envoy"
|
|
713
|
+
];
|
|
714
|
+
/** This function gives the path of the chart in the cache, after a download when the cache does not have it. It refuses a file whose sha256 is not the pinned value. */
|
|
715
|
+
const ensureChart = async (cache = cacheFolder()) => {
|
|
716
|
+
const path = join(cache, "charts", `cilium-${CILIUM_VERSION}.tgz`);
|
|
717
|
+
const bytes = existsSync(path) ? readFileSync(path) : await download(CILIUM_CHART.url);
|
|
718
|
+
const found = sha256(bytes);
|
|
719
|
+
if (found !== CILIUM_CHART.sha256) throw new Error(`the chart of Cilium has the sha256 ${found}, not the pinned ${CILIUM_CHART.sha256}`);
|
|
720
|
+
if (!existsSync(path)) {
|
|
721
|
+
mkdirSync(join(cache, "charts"), { recursive: true });
|
|
722
|
+
writeFileSync(path, bytes);
|
|
723
|
+
}
|
|
724
|
+
return path;
|
|
725
|
+
};
|
|
726
|
+
/**
|
|
727
|
+
* This function installs Cilium in a cluster with no CNI and no kube-proxy. Helm keeps its own
|
|
728
|
+
* files in the folder of the cluster. The function waits for each workload, for each node to be
|
|
729
|
+
* ready, and for CoreDNS. If the install fails, the error has the pods of kube-system and the last
|
|
730
|
+
* lines of the log of the agent.
|
|
731
|
+
*/
|
|
732
|
+
const installCilium = async (cluster, images) => {
|
|
733
|
+
const helm = await ensureTool(HELM);
|
|
734
|
+
const chart = await ensureChart();
|
|
735
|
+
const file = join(cluster.dir, "cilium-values.json");
|
|
736
|
+
writeFileSync(file, `${JSON.stringify(ciliumValues(cluster.scenario.name, images), null, 2)}\n`);
|
|
737
|
+
const home = join(cluster.dir, "helm");
|
|
738
|
+
const env = {
|
|
739
|
+
HELM_CACHE_HOME: join(home, "cache"),
|
|
740
|
+
HELM_CONFIG_HOME: join(home, "config"),
|
|
741
|
+
HELM_DATA_HOME: join(home, "data")
|
|
742
|
+
};
|
|
743
|
+
try {
|
|
744
|
+
await command(helm, [
|
|
745
|
+
"install",
|
|
746
|
+
"cilium",
|
|
747
|
+
chart,
|
|
748
|
+
"-n",
|
|
749
|
+
"kube-system",
|
|
750
|
+
"--kubeconfig",
|
|
751
|
+
cluster.kubeconfig,
|
|
752
|
+
"-f",
|
|
753
|
+
file,
|
|
754
|
+
"--wait",
|
|
755
|
+
"--timeout",
|
|
756
|
+
"10m"
|
|
757
|
+
], {
|
|
758
|
+
env,
|
|
759
|
+
timeout: 9e5
|
|
760
|
+
});
|
|
761
|
+
for (const workload of CILIUM_WORKLOADS) await cluster.kubectl([
|
|
762
|
+
"-n",
|
|
763
|
+
"kube-system",
|
|
764
|
+
"rollout",
|
|
765
|
+
"status",
|
|
766
|
+
workload,
|
|
767
|
+
"--timeout=300s"
|
|
768
|
+
]);
|
|
769
|
+
await cluster.kubectl([
|
|
770
|
+
"wait",
|
|
771
|
+
"--for=condition=Ready",
|
|
772
|
+
"nodes",
|
|
773
|
+
"--all",
|
|
774
|
+
"--timeout=300s"
|
|
775
|
+
]);
|
|
776
|
+
await cluster.kubectl([
|
|
777
|
+
"-n",
|
|
778
|
+
"kube-system",
|
|
779
|
+
"rollout",
|
|
780
|
+
"status",
|
|
781
|
+
"deployment/coredns",
|
|
782
|
+
"--timeout=300s"
|
|
783
|
+
]);
|
|
784
|
+
} catch (error) {
|
|
785
|
+
const state = async (args) => (await cluster.kubectl([
|
|
786
|
+
"-n",
|
|
787
|
+
"kube-system",
|
|
788
|
+
...args
|
|
789
|
+
]).catch((why) => String(why))).slice(-4e3);
|
|
790
|
+
const pods = await state([
|
|
791
|
+
"get",
|
|
792
|
+
"pods",
|
|
793
|
+
"-o",
|
|
794
|
+
"wide"
|
|
795
|
+
]);
|
|
796
|
+
const agent = await state([
|
|
797
|
+
"logs",
|
|
798
|
+
"daemonset/cilium",
|
|
799
|
+
"-c",
|
|
800
|
+
"cilium-agent",
|
|
801
|
+
"--tail=80"
|
|
802
|
+
]);
|
|
803
|
+
throw new Error(`the install of Cilium failed: ${String(error)}\nthe pods of kube-system:\n${pods}\nthe log of the agent:\n${agent}`);
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
/** This is the setting of Cilium that a cluster manifest records: the version and the keys of `cilium-config` that the cells depend on. */
|
|
807
|
+
const CILIUM_CONFIG_KEYS = [
|
|
808
|
+
"kube-proxy-replacement",
|
|
809
|
+
"bpf-lb-sock",
|
|
810
|
+
"bpf-lb-algorithm",
|
|
811
|
+
"routing-mode",
|
|
812
|
+
"ipam"
|
|
813
|
+
];
|
|
814
|
+
/** This function gives the mode of a cluster manifest of the dataplane `cilium`, from the data of the configmap `cilium-config`. */
|
|
815
|
+
const ciliumMode = (data) => `cilium ${CILIUM_VERSION} ${CILIUM_CONFIG_KEYS.map((key) => `${key}=${data[key] ?? "default"}`).join(" ")}`;
|
|
816
|
+
//#endregion
|
|
556
817
|
//#region src/compose.ts
|
|
557
818
|
/** This name is the controller of the service, and its one route reads Redis when the service has Redis. */
|
|
558
819
|
const CONTROLLER = "work";
|
|
@@ -909,12 +1170,50 @@ const middleboxCommands = (peers, idle, port) => {
|
|
|
909
1170
|
*/
|
|
910
1171
|
const linkOf = (cell) => cell.trigger.partition === true ? "server" : cell.template === "T15" ? "servers" : "redis";
|
|
911
1172
|
/**
|
|
1173
|
+
* This function gives the placement of the sessions of the clients: the backend of each slot of each
|
|
1174
|
+
* client, by client. The clients come in the sequence of their names. The sessions on the target go
|
|
1175
|
+
* one to each client in turn: the first slot of each client, then the second slot of each client. Thus
|
|
1176
|
+
* no client has two more sessions on the target than another. The other slots go to the other
|
|
1177
|
+
* replicas in turn.
|
|
1178
|
+
*/
|
|
1179
|
+
const placementOf = (clients, connections, onTarget, target, others) => {
|
|
1180
|
+
if (!Number.isInteger(connections) || connections < 1) throw new RangeError(`a placement needs one session of each client at the least, not ${connections}`);
|
|
1181
|
+
if (!Number.isInteger(onTarget) || onTarget < 0 || onTarget > clients.length * connections) throw new RangeError(`${onTarget} sessions on the target is not a count of the ${clients.length * connections} sessions of the clients`);
|
|
1182
|
+
if (others.length === 0 && onTarget < clients.length * connections) throw new RangeError("a session off the target needs another replica");
|
|
1183
|
+
const sorted = [...clients].sort((a, b) => a.localeCompare(b));
|
|
1184
|
+
let other = 0;
|
|
1185
|
+
return new Map(sorted.map((client, at) => [client, Array.from({ length: connections }, (_, slot) => slot * sorted.length + at < onTarget ? target : others[other++ % others.length])]));
|
|
1186
|
+
};
|
|
1187
|
+
/**
|
|
1188
|
+
* This function puts the step `place` before the first SIGHUP of the clients, for a workload with a
|
|
1189
|
+
* placement (`Workload.onTarget`). A plan with no such SIGHUP cannot place the sessions.
|
|
1190
|
+
*/
|
|
1191
|
+
const placed = (cell, plan) => {
|
|
1192
|
+
const onTarget = cell.workload.onTarget;
|
|
1193
|
+
if (onTarget === void 0) return plan;
|
|
1194
|
+
if (cell.workload.connections === void 0) throw new Error(`${cell.name}: a placement needs the sessions of each client (connections)`);
|
|
1195
|
+
const prepare = plan.prepare ?? [];
|
|
1196
|
+
const at = prepare.findIndex((step) => step.do === "signal" && step.target === "clients" && step.signal === "SIGHUP");
|
|
1197
|
+
if (at === -1) throw new Error(`${cell.name}: the trigger ${cell.trigger.kind} has no SIGHUP of the clients before its fault, thus it cannot place the sessions`);
|
|
1198
|
+
return {
|
|
1199
|
+
...plan,
|
|
1200
|
+
prepare: [
|
|
1201
|
+
...prepare.slice(0, at),
|
|
1202
|
+
{
|
|
1203
|
+
do: "place",
|
|
1204
|
+
onTarget
|
|
1205
|
+
},
|
|
1206
|
+
...prepare.slice(at)
|
|
1207
|
+
]
|
|
1208
|
+
};
|
|
1209
|
+
};
|
|
1210
|
+
/**
|
|
912
1211
|
* This function gives the fault plan of a cell. A trigger with flaps (`Trigger.flaps`) gives them to
|
|
913
1212
|
* its plan: the runner then does the inject and the heal again for each flap. A plan with no hold or
|
|
914
1213
|
* no heal cannot flap.
|
|
915
1214
|
*/
|
|
916
1215
|
const planOf = (cell) => {
|
|
917
|
-
const plan = stepsOf(cell);
|
|
1216
|
+
const plan = placed(cell, stepsOf(cell));
|
|
918
1217
|
const flaps = cell.trigger.flaps;
|
|
919
1218
|
if (flaps === void 0 || flaps.length === 0) return plan;
|
|
920
1219
|
if (plan.hold === null || plan.heal.length === 0) throw new Error(`the trigger ${cell.trigger.kind} has no hold and heal, thus it cannot flap`);
|
|
@@ -1494,7 +1793,8 @@ const FILES = [
|
|
|
1494
1793
|
["drops.ts", "src/drops.ts"],
|
|
1495
1794
|
["eds.ts", "src/eds.ts"],
|
|
1496
1795
|
["lds.ts", "src/lds.ts"],
|
|
1497
|
-
["client-lines.ts", "src/client-lines.ts"]
|
|
1796
|
+
["client-lines.ts", "src/client-lines.ts"],
|
|
1797
|
+
["place.ts", "src/place.ts"]
|
|
1498
1798
|
];
|
|
1499
1799
|
const shell = process.platform === "win32";
|
|
1500
1800
|
/** This function tells if the package is in the workspace of its repository. From npm, it is not. */
|
|
@@ -3153,7 +3453,7 @@ const deploy = async (lab, cluster, cell, namespace, images) => {
|
|
|
3153
3453
|
"--timeout=300s"
|
|
3154
3454
|
]);
|
|
3155
3455
|
await lab.rules({
|
|
3156
|
-
mode: cluster.scenario.
|
|
3456
|
+
mode: cluster.scenario.dataplane,
|
|
3157
3457
|
ns: namespace,
|
|
3158
3458
|
svc: "lab",
|
|
3159
3459
|
ip: clusterIp,
|
|
@@ -3327,6 +3627,32 @@ const signalClients = async (ctx, signal) => {
|
|
|
3327
3627
|
t1: Math.max(...stamps.map((stamp) => stamp.t1))
|
|
3328
3628
|
};
|
|
3329
3629
|
};
|
|
3630
|
+
/** This path in the root of a client container holds its placement: the backend of each of its slots, as JSON. */
|
|
3631
|
+
const PLACE_FILE = "/tmp/lab-place";
|
|
3632
|
+
/**
|
|
3633
|
+
* This function writes the placement of the sessions of each client pod (`placementOf`). The target
|
|
3634
|
+
* is the first replica, and the other slots go to the other replicas. The next SIGHUP of the clients
|
|
3635
|
+
* reads it. The nodes go at the same time, and the stamp is the first act and the last end.
|
|
3636
|
+
*/
|
|
3637
|
+
const placeClients = async (ctx, onTarget, replicas) => {
|
|
3638
|
+
const connections = ctx.cell.workload.connections;
|
|
3639
|
+
if (connections === void 0) throw new Error("a placement needs the sessions of each client (connections)");
|
|
3640
|
+
const clients = [...ctx.tracker.pods.values()].filter((pod) => pod.labels["app"] === "client" && !pod.del && pod.node !== null);
|
|
3641
|
+
if (clients.length === 0 || replicas.length === 0) throw new Error(`a placement needs clients and replicas: ${clients.length} clients, ${replicas.length} replicas`);
|
|
3642
|
+
const placement = placementOf(clients.map((pod) => pod.name), connections, onTarget, replicas[0].pod, replicas.slice(1).map((replica) => replica.pod));
|
|
3643
|
+
const stamps = await Promise.all(clients.map(async (pod) => {
|
|
3644
|
+
const pid = await ctx.lab.pidOf(pod.node, ctx.namespace, pod.name, CLIENT);
|
|
3645
|
+
return ctx.lab.act(pod.node, {
|
|
3646
|
+
op: "write",
|
|
3647
|
+
path: `/proc/${pid}/root${PLACE_FILE}`,
|
|
3648
|
+
data: JSON.stringify(placement.get(pod.name))
|
|
3649
|
+
});
|
|
3650
|
+
}));
|
|
3651
|
+
return {
|
|
3652
|
+
t0: Math.min(...stamps.map((stamp) => stamp.t0)),
|
|
3653
|
+
t1: Math.max(...stamps.map((stamp) => stamp.t1))
|
|
3654
|
+
};
|
|
3655
|
+
};
|
|
3330
3656
|
/**
|
|
3331
3657
|
* This function gives the network namespace of each pod of a target, as a node and a PID in it. The
|
|
3332
3658
|
* pods are each client pod (`clients`), or each replica (`servers`).
|
|
@@ -3388,6 +3714,7 @@ const doStep = async (ctx, step, replicas, background) => {
|
|
|
3388
3714
|
pod: redis
|
|
3389
3715
|
};
|
|
3390
3716
|
}
|
|
3717
|
+
if (cluster.scenario.kubeProxy === null) throw new Error(`${ctx.cell.name}: the target kube-proxy needs kube-proxy, and the dataplane ${cluster.scenario.dataplane} has none`);
|
|
3391
3718
|
const worker = cluster.nodes[1] ?? cluster.nodes[0];
|
|
3392
3719
|
const proxy = JSON.parse(await cluster.kubectl([
|
|
3393
3720
|
"-n",
|
|
@@ -3535,6 +3862,7 @@ const doStep = async (ctx, step, replicas, background) => {
|
|
|
3535
3862
|
op: "zone",
|
|
3536
3863
|
lost: step.lost
|
|
3537
3864
|
});
|
|
3865
|
+
case "place": return placeClients(ctx, step.onTarget, replicas);
|
|
3538
3866
|
case "netem": {
|
|
3539
3867
|
if (step.target !== "redis") throw new Error(`a netem takes the target redis, not ${step.target}`);
|
|
3540
3868
|
const at = await where(step.target);
|
|
@@ -3817,16 +4145,22 @@ const runKindCells = async (cells, options) => {
|
|
|
3817
4145
|
const flavors = new Set(cells.flatMap((cell) => cell.mesh === void 0 ? [] : [cell.mesh.flavor]));
|
|
3818
4146
|
const pinned = /* @__PURE__ */ new Map();
|
|
3819
4147
|
for (const name of new Set([...flavors].flatMap((flavor) => MESH_IMAGES[flavor]))) pinned.set(name, await localPin(ISTIO_IMAGES[name]));
|
|
4148
|
+
const meshImages = flavors.size === 0 ? void 0 : {
|
|
4149
|
+
pilot: pinned.get("pilot"),
|
|
4150
|
+
proxy: pinned.get("proxy"),
|
|
4151
|
+
cni: pinned.get("cni"),
|
|
4152
|
+
ztunnel: pinned.get("ztunnel")
|
|
4153
|
+
};
|
|
4154
|
+
const cilium = cells.some((cell) => cell.mode === "cilium") ? {
|
|
4155
|
+
agent: await localPin(CILIUM_IMAGES.agent),
|
|
4156
|
+
operator: await localPin(CILIUM_IMAGES.operator),
|
|
4157
|
+
envoy: await localPin(CILIUM_IMAGES.envoy)
|
|
4158
|
+
} : void 0;
|
|
3820
4159
|
const images = {
|
|
3821
4160
|
lab: image,
|
|
3822
4161
|
redis: redisImage,
|
|
3823
4162
|
grpc: grpcImage,
|
|
3824
|
-
mesh:
|
|
3825
|
-
pilot: pinned.get("pilot"),
|
|
3826
|
-
proxy: pinned.get("proxy"),
|
|
3827
|
-
cni: pinned.get("cni"),
|
|
3828
|
-
ztunnel: pinned.get("ztunnel")
|
|
3829
|
-
}
|
|
4163
|
+
mesh: meshImages
|
|
3830
4164
|
};
|
|
3831
4165
|
const groups = /* @__PURE__ */ new Map();
|
|
3832
4166
|
for (const cell of cells) groups.set(clusterKey(cell), [...groups.get(clusterKey(cell)) ?? [], cell]);
|
|
@@ -3837,11 +4171,22 @@ const runKindCells = async (cells, options) => {
|
|
|
3837
4171
|
const s = scenario(`lab-${key}-${env}`, group[0].mode, group[0].workers, void 0, zoned(group[0]));
|
|
3838
4172
|
const dir = join(options.work ?? join(tmpdir(), "systems-kind-work"), s.name);
|
|
3839
4173
|
const mesh = group[0].mesh;
|
|
3840
|
-
options.log(`cluster ${s.name}: ${s.
|
|
4174
|
+
options.log(`cluster ${s.name}: ${s.dataplane}, ${s.workers} workers${mesh === void 0 ? "" : `, Istio ${mesh.flavor}`}`);
|
|
3841
4175
|
const cluster = await createCluster(s, dir);
|
|
3842
4176
|
let lab = null;
|
|
3843
4177
|
let loader = null;
|
|
3844
4178
|
try {
|
|
4179
|
+
if (s.cni === "cilium") {
|
|
4180
|
+
if (cilium === void 0) throw new Error(`${group[0].name}: the run has no images of Cilium`);
|
|
4181
|
+
for (const each of [
|
|
4182
|
+
cilium.agent,
|
|
4183
|
+
cilium.operator,
|
|
4184
|
+
cilium.envoy
|
|
4185
|
+
]) await cluster.load(each);
|
|
4186
|
+
const began = Date.now();
|
|
4187
|
+
await installCilium(cluster, cilium);
|
|
4188
|
+
options.log(` Cilium ${CILIUM_VERSION} installed in ${Math.round((Date.now() - began) / 100) / 10} s`);
|
|
4189
|
+
}
|
|
3845
4190
|
await cluster.load(image);
|
|
3846
4191
|
await cluster.load(redisImage);
|
|
3847
4192
|
if (grpcImage !== void 0 && group.some((cell) => stackOf(cell) === "grpc")) await cluster.load(grpcImage);
|
|
@@ -3912,7 +4257,8 @@ const runKindCells = async (cells, options) => {
|
|
|
3912
4257
|
lab: image,
|
|
3913
4258
|
redis: redisImage,
|
|
3914
4259
|
...stackOf(cell) === "grpc" ? { grpc: grpcImage } : {},
|
|
3915
|
-
...cell.mesh === void 0 ? {} : Object.fromEntries(MESH_IMAGES[cell.mesh.flavor].map((name) => [`istio-${name}`, ISTIO_IMAGES[name]]))
|
|
4260
|
+
...cell.mesh === void 0 ? {} : Object.fromEntries(MESH_IMAGES[cell.mesh.flavor].map((name) => [`istio-${name}`, ISTIO_IMAGES[name]])),
|
|
4261
|
+
...s.cni === "cilium" ? Object.fromEntries(Object.entries(CILIUM_IMAGES).map(([name, ref]) => [`cilium-${name}`, ref])) : {}
|
|
3916
4262
|
}
|
|
3917
4263
|
});
|
|
3918
4264
|
entry.loads.push(...loads);
|
|
@@ -4017,6 +4363,6 @@ const shardOf = (cells, index, count) => {
|
|
|
4017
4363
|
return out;
|
|
4018
4364
|
};
|
|
4019
4365
|
//#endregion
|
|
4020
|
-
export { ACCESS_LOG_FORMAT, CHANNEL_USE, CLIENT, CLIENT_ACCOUNT, CLOSE_LEAD, CONTROLLER, DOCUMENTED, DRAIN_RULES, EDS_EVERY, FLAG_CHECK, FORMAT, FORMAT_V1, HBONE_PORT, 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, MIDDLEBOX, NETEM_DRAIN, NETNS_DEFAULTS, NODE_IMAGES, NODE_LIFECYCLE, O04, O09, O10, O16, POD_LINK, POLICY, PORT, QUIET_LEAD, REDIS, REDIS_IMAGE, RELATIVE, ROUTE, RULES_EVERY, SERVER, SPREAD_LEAD, STAGES, SYSCTL, TCP_STATE, WATCHDOG, ZONE_KEPT, ZONE_LABEL, ZONE_LOST, ZTUNNEL_PODS, backendsAt, backendsOf, bandOf, byClients, byNode, cacheFolder, calibrationOf, capOf, cellsTable, certificateArgs, checkRun, cidrOverlap, cidrRange, clientObjects, clusterFilter, compose, createCluster, cyclesOf, deletes, deletionOf, deletionWindow, deletionsOf, describeCell, dropOf, emptyOf, ensureTool, estimatedTime, factsOf, failuresOf, faultSpan, followsZtunnel, goDuration, hostsOf, installMesh, intakeAfter, intakeOf, isTunnelState, istioOperator, judge, judgeKind, keptKernel, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, kindConfig, kubeProxyPatch, kubeletBand, labImage, labSource, latchBand, leafCertificates, learnWindow, lintCell, listenerCountsOf, localPin, medianInterval, medianText, meshKey, middle, middleboxCommands, namespaceObject, namespaceOf, nodeLossBand, nodeNames, nodeReturnBand, 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, sharedGate, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, sliceReadyAt, sliceReadyOf, slicesOf, socketsOf, stackOf, stageLabImage, stallRemovalBand, stopOf, stoppingOf, sysctlCommand, tarEntry, timeSpread, toolPath, transitionOf, tunnelsOf, unitOf, updatesOf, upgrade, upgradeCell, verdictsOf, writeResult, zoneOf, ztunnelCertificates, ztunnelEnvArgs, ztunnelPodsOf, ztunnelReplaced };
|
|
4366
|
+
export { ACCESS_LOG_FORMAT, CHANNEL_USE, CILIUM_CHART, CILIUM_CONFIG_KEYS, CILIUM_IMAGES, CILIUM_VERSION, CILIUM_WORKLOADS, CLIENT, CLIENT_ACCOUNT, CLOSE_LEAD, CONTROLLER, DATAPLANES, DOCUMENTED, DRAIN_RULES, EDS_EVERY, FLAG_CHECK, FORMAT, FORMAT_V1, HBONE_PORT, HEALTH_PORT, HELM, 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, MIDDLEBOX, NETEM_DRAIN, NETNS_DEFAULTS, NODE_IMAGES, NODE_LIFECYCLE, O04, O09, O10, O16, POD_LINK, POLICY, PORT, QUIET_LEAD, REDIS, REDIS_IMAGE, RELATIVE, ROUTE, RULES_EVERY, SERVER, SPREAD_LEAD, STAGES, SYSCTL, TCP_STATE, WATCHDOG, ZONE_KEPT, ZONE_LABEL, ZONE_LOST, ZTUNNEL_PODS, backendsAt, backendsOf, bandOf, byClients, byNode, cacheFolder, calibrationOf, capOf, cellsTable, certificateArgs, checkRun, cidrOverlap, cidrRange, ciliumBackendsOf, ciliumMode, ciliumValues, clientObjects, clusterFilter, compose, createCluster, cyclesOf, deletes, deletionOf, deletionWindow, deletionsOf, describeCell, dropOf, emptyOf, ensureChart, ensureTool, estimatedTime, factsOf, failuresOf, faultSpan, followsZtunnel, goDuration, hostsOf, installCilium, installMesh, intakeAfter, intakeOf, isKubeProxyMode, isTunnelState, istioOperator, judge, judgeKind, keptKernel, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, kindConfig, kubeProxyPatch, kubeletBand, labImage, labSource, latchBand, leafCertificates, learnWindow, lintCell, listenerCountsOf, localPin, medianInterval, medianText, meshKey, middle, middleboxCommands, namesService, namespaceObject, namespaceOf, nodeLossBand, nodeNames, nodeReturnBand, observationAfter, observationEnd, openKindLab, outboundCluster, outcomesOf, pausedWindow, placementOf, 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, sessionsAt, setZtunnelEnv, sha256, shardOf, sharedGate, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, sliceReadyAt, sliceReadyOf, slicesOf, socketsOf, stackOf, stageLabImage, stallRemovalBand, stopOf, stoppingOf, sysctlCommand, tarEntry, timeSpread, toolPath, transitionOf, tunnelsOf, unitOf, updatesOf, upgrade, upgradeCell, verdictsOf, writeResult, zoneOf, ztunnelCertificates, ztunnelEnvArgs, ztunnelPodsOf, ztunnelReplaced };
|
|
4021
4367
|
|
|
4022
4368
|
//# sourceMappingURL=index.js.map
|