@mark1russell7/kind 0.1.8 → 0.2.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 +26 -2
- package/build/index.d.ts +26 -6
- package/build/index.js +159 -45
- package/build/index.js.map +1 -1
- package/build/{judge-a7yCzwZc.d.ts → judge-Dwa6MqP4.d.ts} +76 -14
- package/build/{judge-BbTAxRSd.js → judge-reWPMVr4.js} +100 -40
- package/build/judge-reWPMVr4.js.map +1 -0
- package/build/judge.d.ts +2 -2
- package/build/judge.js +1 -1
- package/image/Dockerfile +6 -6
- package/image/client-grpc.mjs +97 -0
- package/image/npm-lock.json +326 -0
- package/image/package.json +1 -0
- package/package.json +4 -3
- package/build/judge-BbTAxRSd.js.map +0 -1
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ The unit of the main value of a run comes from the prediction of its cell:
|
|
|
54
54
|
|
|
55
55
|
These values stay numbers in the two formats:
|
|
56
56
|
|
|
57
|
-
- **A stamp** is a reading of CLOCK_MONOTONIC in milliseconds, a point on a clock, not an amount. The stamps are `start`, `end` and `acts` of a run, `t` of each line, and `first`, `last` and `
|
|
57
|
+
- **A stamp** is a reading of CLOCK_MONOTONIC in milliseconds, a point on a clock, not an amount. The stamps are `start`, `end` and `acts` of a run, `t` of each line, and `first`, `last`, `deletion` and `deletions` of the facts.
|
|
58
58
|
- **A line** stays as its source wrote it. The image writes it, and the record keeps it.
|
|
59
59
|
- **A count** or **a share**: the replicas, the thresholds, the runs and the counts of a verdict. The jitter of a poll and the idle share of a load sample are shares.
|
|
60
60
|
|
|
@@ -89,6 +89,20 @@ Each pod of a cluster shares the kernel of the Docker VM. Thus each stamp of the
|
|
|
89
89
|
|
|
90
90
|
Before each run, the lab deletes the conntrack entries in SYN_SENT to the ClusterIP on each node (`forgetHandshakes`). A note of the run gives each count. Such an entry is a SYN with no answer, and it keeps its NAT for 120 s. A new SYN from the same port goes where the first one went, and it gets no answer either. The clients send requests between the runs too. Thus the restore of one run left such entries, and a client that took the port again in the next run timed out (T07, 2026-10-17).
|
|
91
91
|
|
|
92
|
+
## The stacks
|
|
93
|
+
|
|
94
|
+
From 0.2.0, a cell names its stack (`stack`): its server and its clients. A cell without a stack has the service.
|
|
95
|
+
|
|
96
|
+
- **`service`** is the service of this repository with its declaration, and the HTTP clients. Each request opens a new connection, thus kube-proxy picks a backend for each request.
|
|
97
|
+
- **`grpc`** is the server of grpc-go v1.84.0 from the image of the harness, and the gRPC clients of the lab image. A client holds one channel. Thus kube-proxy picks a backend only when grpc-js connects again: after a GOAWAY of the server, or after the end of the socket.
|
|
98
|
+
- The probes are the gRPC probes of the kubelet on the port of the server.
|
|
99
|
+
- The lines of grpc-go name their pod and stamp in milliseconds (`--name`, `--clock ms`). Its line `stopping` gives the intake and the SIGTERM of a pod (`stoppingOf`): then GracefulStop closes the listener and sends GOAWAY.
|
|
100
|
+
- The clients start after the server is ready. Each failed connect of a channel makes the backoff of grpc-js longer, up to 120 s.
|
|
101
|
+
- The target of a run is the replica that answers the most clients. A channel stays on its replica, and the channels go to the replica that stays after a run. Thus a target by name has no client in most runs.
|
|
102
|
+
- The cell has no declaration (`declaration` is null), thus `describeCell` refuses it. The server grpc-go has no seam for `liveness-restart`, thus its manifest refuses that trigger.
|
|
103
|
+
|
|
104
|
+
A declaration that can be null changes the type `KindCell`. That is a breaking change before 1.0.0, thus the version is 0.2.0.
|
|
105
|
+
|
|
92
106
|
## The image
|
|
93
107
|
|
|
94
108
|
The lab image (`image/`) holds the service of a cell on `@mark1russell7/service`, its clients, the agent and the watcher, on Node 24.21.0. Its base is pinned by digest, and `npm-lock.json` pins the packages from npm. `stageLabImage()` stages its context, and `labImage()` tags the image by its content.
|
|
@@ -101,6 +115,7 @@ The lab image (`image/`) holds the service of a cell on `@mark1russell7/service`
|
|
|
101
115
|
- **The knobs of the listener.** `server_idle_close` is 6 s, the real idle close of Node 24.21: `keepAliveTimeout` of 5 s and its buffer of 1 s. The sweep of idle connections comes each second after the stop of the intake.
|
|
102
116
|
- **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.
|
|
103
117
|
- **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.
|
|
118
|
+
- **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.
|
|
104
119
|
|
|
105
120
|
## The triggers
|
|
106
121
|
|
|
@@ -111,7 +126,7 @@ The lab image (`image/`) holds the service of a cell on `@mark1russell7/service`
|
|
|
111
126
|
| `pod-deletion` | `kubectl delete pod` of the first replica |
|
|
112
127
|
| `rolling-update` | a new value of an environment variable of the Deployment |
|
|
113
128
|
| `node-drain` | `kubectl drain` of the node of the first replica, then `uncordon` |
|
|
114
|
-
| `scale-in` | one replica less, then back |
|
|
129
|
+
| `scale-in` | one replica less, then back. For the stack `grpc`, the first replica first gets a deletion cost of -1, thus its ReplicaSet removes it |
|
|
115
130
|
| `crash` | SIGKILL of the process of the service, from the node |
|
|
116
131
|
| `liveness-restart` | the flag file that fails the liveness check of the first replica |
|
|
117
132
|
| `freeze-process` | SIGSTOP and SIGCONT: of kube-proxy on one node across a deletion (T07), or of Redis (T18) |
|
|
@@ -139,6 +154,15 @@ The plan's `event:clock-jump` has no trigger. A step of CLOCK_REALTIME in the VM
|
|
|
139
154
|
|
|
140
155
|
From 0.1.8, a run can agree with the law but be past its calibrated band (D-EXTEND). A request failed after the window, but before the rules of its node dropped the target. The check of that run tells it (`tail`), and its cell is `extend`. Such a cell does not hold. A request after the drop is still late, thus its run fails the cell.
|
|
141
156
|
|
|
157
|
+
From 0.2.1, a window can be one window for each deleted replica (`each`). A rolling update deletes each old replica in turn, and a client of gRPC can see each of these deletions.
|
|
158
|
+
|
|
159
|
+
- The judge makes one window for each replica of the run (`replicas`) with a deletion after the start of the run. Each window counts from the intake and the deletion of its own replica.
|
|
160
|
+
- A request is late only when it is outside each of these windows. A request after a window and before the rules of its node dropped the replica of that window is a `tail`.
|
|
161
|
+
- The facts of each run give these replicas, with the stamps of their intake and their deletion (`deletions`).
|
|
162
|
+
- `predictionText` adds `of each deleted replica`. A window without `each` stays the window of the target.
|
|
163
|
+
|
|
164
|
+
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.
|
|
165
|
+
|
|
142
166
|
## Tests
|
|
143
167
|
|
|
144
168
|
`pnpm --filter @mark1russell7/kind test` starts the tests of the configuration, the tools, the parser of the rules and the fault mapping. It also starts the tests of the predictions, the verdict and the judge of the real records. They do not need Docker.
|
package/build/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as RecordLine, $t as KindConfig, A as TriggerV1, At as REDIS, B as RunCheck, Bt as KubeletProbe, C as KindCellResult, Ct as slicesOf, D as PredictionV1, Dt as HEALTH_PORT, E as LoadSample, Et as CLIENT, F as KIND_NOISE, Ft as showBand, G as judgeKind, Gt as Replica, H as checkRun, Ht as Prediction, I as KindDecision, It as Band, J as reusesReset, Jt as Template, K as propagationOf, Kt as ResetReuse, L as KindVerdict, Lt as Declaration, M as upgrade, Mt as bandOf, N as upgradeCell, Nt as seconds, O as RunCheckV1, Ot as LOG_LABEL, P as Deleted, Pt as secondsOf, Q as PodLine, Qt as KUBE_PROXY_MODES, R as LAST_SLACK, Rt as KindCell, S as KindCellRecordV1, St as sliceEmptyOf, T as KindVerdictV1, Tt as transitionOf, U as deletionsOf, Ut as RedisDeclaration, V as RunFacts, Vt as PodSettings, W as factsOf, Wt as Reference, X as Endpoint, Xt as TriggerKind, Y as unitOf, Yt as Trigger, Z as Line, Zt as Workload, _ as ClusterManifest, _t as restartsAt, a as STAGES, an as cidrRange, at as byNode, b as FORMAT_V1, bt as signalsOf, c as readRecord, cn as kubeProxyPatch, ct as emptyOf, d as calibrationOf, dn as scenarioProblems, dt as kernelCounts, en as KindScenario, et as Request, f as cellsTable, ft as outcomesOf, g as BandV1, gt as requestsOf, h as timeSpread, ht as recordsOf, i as quantileOf, in as cidrOverlap, it as byClients, j as judge, jt as SERVER, k as RunFactsV1, kt as PORT, l as Judged, ln as nodeNames, lt as failuresOf, m as predictionText, mt as podsOf, n as medianInterval, nn as NODE_IMAGES, nt as SliceLine, o as kindCellRecordSchema, on as goDuration, ot as deletionOf, p as medianText, pt as podsAt, q as referenceOf, qt as Stack, r as middle, rn as NodeImage, rt as backendsAt, s as kindCellRecordV1Schema, sn as kindConfig, st as dropOf, t as RELATIVE, tn as KubeProxyMode, tt as RulesLine, u as TimeSpread, un as scenario, ut as intakeOf, v as EnvironmentManifest, vt as rulesOf, w as KindCellV1, wt as stoppingOf, x as KindCellRecord, xt as sliceDropOf, y as FORMAT, yt as signalOf, z as Propagated, zt as KindRun } from "./judge-Dwa6MqP4.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";
|
|
@@ -205,17 +205,37 @@ export declare const openKindLab: (cluster: Cluster, image: string) => Promise<K
|
|
|
205
205
|
export declare const localPin: (image: string) => Promise<string>;
|
|
206
206
|
//#endregion
|
|
207
207
|
//#region src/manifests.d.ts
|
|
208
|
+
/**
|
|
209
|
+
* This module generates the Kubernetes objects of a cell. They are its namespace, its Redis, the
|
|
210
|
+
* server as a Deployment behind a Service, and one client on each node. The server is the service
|
|
211
|
+
* or grpc-go, by the stack of the cell. Each setting of the pod comes from the cell. The rollout
|
|
212
|
+
* strategy and the probe fields state the defaults of Kubernetes as values.
|
|
213
|
+
*/
|
|
214
|
+
/** This function gives the stack of a cell: the service, when the cell names no stack. */
|
|
215
|
+
export declare const stackOf: (cell: Pick<KindCell, "stack">) => Stack;
|
|
208
216
|
/** This image is Redis 7.4, the stock target of the harness, pinned by digest. */
|
|
209
217
|
export declare const REDIS_IMAGE = "redis:7.4@sha256:c6eabf748fc7a61dbb5a705c78bcf3d6377b1127a97d0ce965c11c44ba46896f";
|
|
210
218
|
/** This function gives the namespace of a cell: `lab-` and 10 digits of the sha256 of its name. */
|
|
211
219
|
export declare const namespaceOf: (cell: Pick<KindCell, "name">) => string;
|
|
212
|
-
/**
|
|
213
|
-
|
|
220
|
+
/**
|
|
221
|
+
* This function gives a probe of the pod, with each field of the cell. The probe of the service is an
|
|
222
|
+
* HTTP GET of its health server (`path`). The probe of grpc-go (`path` null) is the gRPC probe of the
|
|
223
|
+
* kubelet. It is a Check of the service "" on the port of the server, and the answer is SERVING until
|
|
224
|
+
* GracefulStop.
|
|
225
|
+
*/
|
|
226
|
+
export declare const probeOf: (path: string | null, probe: KubeletProbe) => Record<string, unknown>;
|
|
214
227
|
/** These are the objects of Redis: one replica behind a Service, with no persistence. */
|
|
215
228
|
export declare const redisObjects: (namespace: string, image: string) => unknown[];
|
|
216
|
-
/**
|
|
229
|
+
/**
|
|
230
|
+
* These are the objects of the server: a Service of type ClusterIP, and the Deployment with the
|
|
231
|
+
* settings of the cell. The image is the lab image for the service, or the image of grpc-go.
|
|
232
|
+
*/
|
|
217
233
|
export declare const serverObjects: (cell: KindCell, namespace: string, image: string, version?: number) => unknown[];
|
|
218
|
-
/**
|
|
234
|
+
/**
|
|
235
|
+
* These are the objects of the clients: a DaemonSet with one client on each node, the control plane
|
|
236
|
+
* too. The client of the service is `client.mjs`, and the client of grpc-go is `client-grpc.mjs`.
|
|
237
|
+
* Each one is in the lab image, and each one writes the same lines.
|
|
238
|
+
*/
|
|
219
239
|
export declare const clientObjects: (cell: KindCell, namespace: string, image: string, target: string) => unknown[];
|
|
220
240
|
//#endregion
|
|
221
241
|
//#region src/predict.d.ts
|
|
@@ -395,5 +415,5 @@ export declare const toolPath: (tool: Tool, key?: string, cache?: string) => str
|
|
|
395
415
|
*/
|
|
396
416
|
export declare const ensureTool: (tool: Tool, key?: string, cache?: string) => Promise<string>;
|
|
397
417
|
//#endregion
|
|
398
|
-
export { type Band, type BandV1, CLIENT, type Cluster, type ClusterManifest, type Credentials, type Declaration, type DeletionWindow, 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, Line, type LoadSample, NODE_IMAGES, type NodeImage, PORT, type Parts, PodLine, type PodSettings, type Prediction, type PredictionV1, type Propagated, type Propagation, 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 Stamp, type Step, type Target, type Template, type TimeSpread, type Tool, type Trigger, type TriggerKind, type TriggerV1, type Verdicts, type Workload, backendsAt, bandOf, byNode, calibrationOf, cellsTable, checkRun, cidrOverlap, cidrRange, deletionOf, dropOf, emptyOf, factsOf, failuresOf, goDuration, intakeOf, judge, judgeKind, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, kindConfig, kubeProxyPatch, medianInterval, medianText, middle, nodeNames, outcomesOf, podsAt, podsOf, predictionText, propagationOf, quantileOf, readRecord, recordsOf, referenceOf, requestsOf, restartsAt, reusesReset, rulesOf, scenario, scenarioProblems, seconds, secondsOf, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, slicesOf, timeSpread, transitionOf, unitOf, upgrade, upgradeCell };
|
|
418
|
+
export { type Band, type BandV1, CLIENT, type Cluster, type ClusterManifest, type Credentials, type Declaration, type Deleted, type DeletionWindow, 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, Line, type LoadSample, NODE_IMAGES, type NodeImage, PORT, type Parts, PodLine, type PodSettings, type Prediction, type PredictionV1, type Propagated, type Propagation, 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, outcomesOf, podsAt, podsOf, predictionText, propagationOf, quantileOf, readRecord, recordsOf, referenceOf, requestsOf, restartsAt, reusesReset, rulesOf, scenario, scenarioProblems, seconds, secondsOf, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, slicesOf, stoppingOf, timeSpread, transitionOf, unitOf, upgrade, upgradeCell };
|
|
399
419
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as HEALTH_PORT, A as byClients, B as podsOf, C as reusesReset, D as middle, E as medianInterval, F as failuresOf, G as signalOf, H as requestsOf, I as intakeOf, J as sliceEmptyOf, K as signalsOf, L as kernelCounts, M as deletionOf, N as dropOf, O as quantileOf, P as emptyOf, Q as CLIENT, R as outcomesOf, S as referenceOf, T as RELATIVE, U as restartsAt, V as recordsOf, W as rulesOf, X as stoppingOf, Y as slicesOf, Z as transitionOf, _ as checkRun, a as calibrationOf, at as seconds, b as judgeKind, c as predictionText, d as FORMAT_V1, et as LOG_LABEL, f as judge, g as LAST_SLACK, h as KIND_NOISE, i as readRecord, it as bandOf, j as byNode, k as backendsAt, l as timeSpread, m as upgradeCell, n as kindCellRecordSchema, nt as REDIS, o as cellsTable, ot as secondsOf, p as upgrade, q as sliceDropOf, r as kindCellRecordV1Schema, rt as SERVER, s as medianText, st as showBand, t as STAGES, tt as PORT, u as FORMAT, v as deletionsOf, w as unitOf, x as propagationOf, y as factsOf, z as podsAt } from "./judge-reWPMVr4.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";
|
|
@@ -9,7 +9,7 @@ import { fileURLToPath } from "node:url";
|
|
|
9
9
|
import { failSpan, lint, service } from "@mark1russell7/service";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import { tmpdir } from "node:os";
|
|
12
|
-
import { harnessImage } from "@mark1russell7/harness";
|
|
12
|
+
import { harnessImage, imageOf } from "@mark1russell7/harness";
|
|
13
13
|
import { redisClient } from "@mark1russell7/service/redis";
|
|
14
14
|
//#region src/backends.ts
|
|
15
15
|
const hexIp = (hex) => [
|
|
@@ -520,6 +520,8 @@ const POD = "{pod}";
|
|
|
520
520
|
const NODE = "{node}";
|
|
521
521
|
/** This value marks the next version of the Deployment, for a rollout. */
|
|
522
522
|
const VERSION = "{version}";
|
|
523
|
+
/** This annotation of a pod is its deletion cost for its ReplicaSet: a lower cost goes first at a scale-in. */
|
|
524
|
+
const DELETION_COST = "controller.kubernetes.io/pod-deletion-cost";
|
|
523
525
|
/** This function gives the fault plan of a cell. */
|
|
524
526
|
const planOf = (cell) => {
|
|
525
527
|
const ns = ["-n", "{namespace}"];
|
|
@@ -577,7 +579,17 @@ const planOf = (cell) => {
|
|
|
577
579
|
}]
|
|
578
580
|
};
|
|
579
581
|
case "scale-in": return {
|
|
580
|
-
inject: [{
|
|
582
|
+
inject: [...cell.stack === "grpc" ? [{
|
|
583
|
+
do: "kubectl",
|
|
584
|
+
args: [
|
|
585
|
+
...ns,
|
|
586
|
+
"annotate",
|
|
587
|
+
"pod",
|
|
588
|
+
POD,
|
|
589
|
+
`${DELETION_COST}=-1`,
|
|
590
|
+
"--overwrite"
|
|
591
|
+
]
|
|
592
|
+
}] : [], {
|
|
581
593
|
do: "kubectl",
|
|
582
594
|
args: [
|
|
583
595
|
...ns,
|
|
@@ -800,6 +812,7 @@ const FILES = [
|
|
|
800
812
|
["npm-lock.json", "image/npm-lock.json"],
|
|
801
813
|
["app.mjs", "image/app.mjs"],
|
|
802
814
|
["client.mjs", "image/client.mjs"],
|
|
815
|
+
["client-grpc.mjs", "image/client-grpc.mjs"],
|
|
803
816
|
["agent.mjs", "image/agent.mjs"],
|
|
804
817
|
["watch.mjs", "image/watch.mjs"],
|
|
805
818
|
["compose.ts", "src/compose.ts"],
|
|
@@ -1220,6 +1233,14 @@ const localPin = async (image) => {
|
|
|
1220
1233
|
};
|
|
1221
1234
|
//#endregion
|
|
1222
1235
|
//#region src/manifests.ts
|
|
1236
|
+
/**
|
|
1237
|
+
* This module generates the Kubernetes objects of a cell. They are its namespace, its Redis, the
|
|
1238
|
+
* server as a Deployment behind a Service, and one client on each node. The server is the service
|
|
1239
|
+
* or grpc-go, by the stack of the cell. Each setting of the pod comes from the cell. The rollout
|
|
1240
|
+
* strategy and the probe fields state the defaults of Kubernetes as values.
|
|
1241
|
+
*/
|
|
1242
|
+
/** This function gives the stack of a cell: the service, when the cell names no stack. */
|
|
1243
|
+
const stackOf = (cell) => cell.stack ?? "service";
|
|
1223
1244
|
/** This image is Redis 7.4, the stock target of the harness, pinned by digest. */
|
|
1224
1245
|
const REDIS_IMAGE = "redis:7.4@sha256:c6eabf748fc7a61dbb5a705c78bcf3d6377b1127a97d0ce965c11c44ba46896f";
|
|
1225
1246
|
const whole = (amount, what) => {
|
|
@@ -1229,18 +1250,28 @@ const whole = (amount, what) => {
|
|
|
1229
1250
|
};
|
|
1230
1251
|
/** This function gives the namespace of a cell: `lab-` and 10 digits of the sha256 of its name. */
|
|
1231
1252
|
const namespaceOf = (cell) => `lab-${createHash("sha256").update(cell.name).digest("hex").slice(0, 10)}`;
|
|
1232
|
-
/**
|
|
1253
|
+
/**
|
|
1254
|
+
* This function gives a probe of the pod, with each field of the cell. The probe of the service is an
|
|
1255
|
+
* HTTP GET of its health server (`path`). The probe of grpc-go (`path` null) is the gRPC probe of the
|
|
1256
|
+
* kubelet. It is a Check of the service "" on the port of the server, and the answer is SERVING until
|
|
1257
|
+
* GracefulStop.
|
|
1258
|
+
*/
|
|
1233
1259
|
const probeOf = (path, probe) => ({
|
|
1234
|
-
httpGet: {
|
|
1260
|
+
...path === null ? { grpc: { port: PORT } } : { httpGet: {
|
|
1235
1261
|
path,
|
|
1236
1262
|
port: HEALTH_PORT
|
|
1237
|
-
},
|
|
1263
|
+
} },
|
|
1238
1264
|
initialDelaySeconds: 0,
|
|
1239
|
-
periodSeconds: whole(probe.period, `the period of ${path}`),
|
|
1240
|
-
timeoutSeconds: whole(probe.timeout, `the timeout of ${path}`),
|
|
1265
|
+
periodSeconds: whole(probe.period, `the period of ${path ?? "the gRPC probe"}`),
|
|
1266
|
+
timeoutSeconds: whole(probe.timeout, `the timeout of ${path ?? "the gRPC probe"}`),
|
|
1241
1267
|
failureThreshold: probe.threshold,
|
|
1242
1268
|
successThreshold: 1
|
|
1243
1269
|
});
|
|
1270
|
+
/** This variable is the name of the pod, from the API: the service and grpc-go put it in their lines. */
|
|
1271
|
+
const POD_ENV = {
|
|
1272
|
+
name: "POD_NAME",
|
|
1273
|
+
valueFrom: { fieldRef: { fieldPath: "metadata.name" } }
|
|
1274
|
+
};
|
|
1244
1275
|
/** This function gives the environment of the app: its config as JSON, and the name of its pod. */
|
|
1245
1276
|
const appEnv = (cell) => [{
|
|
1246
1277
|
name: "CONFIG",
|
|
@@ -1250,10 +1281,44 @@ const appEnv = (cell) => [{
|
|
|
1250
1281
|
healthPort: HEALTH_PORT,
|
|
1251
1282
|
declaration: cell.declaration
|
|
1252
1283
|
})
|
|
1253
|
-
},
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1284
|
+
}, POD_ENV];
|
|
1285
|
+
/**
|
|
1286
|
+
* This function gives the container of the server of a cell. The service runs `app.mjs` of the lab
|
|
1287
|
+
* image with its declaration. The server grpc-go runs from the image of the harness. Its lines name
|
|
1288
|
+
* the pod and stamp in milliseconds. Its replies name the pod in the header `x-pod`. Kubernetes
|
|
1289
|
+
* expands `$(POD_NAME)` in the command.
|
|
1290
|
+
*/
|
|
1291
|
+
const serverContainer = (cell, version) => {
|
|
1292
|
+
const env = {
|
|
1293
|
+
name: "LAB_VERSION",
|
|
1294
|
+
value: String(version)
|
|
1295
|
+
};
|
|
1296
|
+
if (stackOf(cell) === "service") return {
|
|
1297
|
+
command: ["node", "app.mjs"],
|
|
1298
|
+
env: [...appEnv(cell), env],
|
|
1299
|
+
ports: [{ containerPort: PORT }, { containerPort: HEALTH_PORT }],
|
|
1300
|
+
...cell.pod.startup === null ? {} : { startupProbe: probeOf("/startup", cell.pod.startup) },
|
|
1301
|
+
readinessProbe: probeOf("/readiness", cell.pod.readiness),
|
|
1302
|
+
livenessProbe: probeOf("/liveness", cell.pod.liveness)
|
|
1303
|
+
};
|
|
1304
|
+
if (cell.trigger.kind === "liveness-restart") throw new Error(`${cell.name}: grpc-go has no seam for liveness-restart`);
|
|
1305
|
+
return {
|
|
1306
|
+
command: [
|
|
1307
|
+
"grpc-go-server",
|
|
1308
|
+
"--port",
|
|
1309
|
+
String(PORT),
|
|
1310
|
+
"--name",
|
|
1311
|
+
"$(POD_NAME)",
|
|
1312
|
+
"--clock",
|
|
1313
|
+
"ms"
|
|
1314
|
+
],
|
|
1315
|
+
env: [POD_ENV, env],
|
|
1316
|
+
ports: [{ containerPort: PORT }],
|
|
1317
|
+
...cell.pod.startup === null ? {} : { startupProbe: probeOf(null, cell.pod.startup) },
|
|
1318
|
+
readinessProbe: probeOf(null, cell.pod.readiness),
|
|
1319
|
+
livenessProbe: probeOf(null, cell.pod.liveness)
|
|
1320
|
+
};
|
|
1321
|
+
};
|
|
1257
1322
|
/** These are the objects of Redis: one replica behind a Service, with no persistence. */
|
|
1258
1323
|
const redisObjects = (namespace, image) => [{
|
|
1259
1324
|
apiVersion: "apps/v1",
|
|
@@ -1308,9 +1373,29 @@ const redisObjects = (namespace, image) => [{
|
|
|
1308
1373
|
}]
|
|
1309
1374
|
}
|
|
1310
1375
|
}];
|
|
1311
|
-
/**
|
|
1376
|
+
/**
|
|
1377
|
+
* This function gives the preStop hook of a sleep, or nothing for no sleep. The hook is the native
|
|
1378
|
+
* sleep action of the kubelet. The API server refuses a sleep action that is longer than the grace
|
|
1379
|
+
* (`validateSleepAction`, `pkg/apis/core/validation/validation.go` at v1.37.0, line 3483). Thus a
|
|
1380
|
+
* longer sleep is a command in the container, `sleep` from `/bin/sh`, as public manifests write it.
|
|
1381
|
+
* The kubelet starts that command with no timeout (`pkg/kubelet/lifecycle/handlers.go`, line 81).
|
|
1382
|
+
*/
|
|
1383
|
+
const preStopOf = (preStop, grace) => {
|
|
1384
|
+
if (preStop === 0) return {};
|
|
1385
|
+
if (preStop <= grace) return { lifecycle: { preStop: { sleep: { seconds: preStop } } } };
|
|
1386
|
+
return { lifecycle: { preStop: { exec: { command: [
|
|
1387
|
+
"/bin/sh",
|
|
1388
|
+
"-c",
|
|
1389
|
+
`sleep ${preStop}`
|
|
1390
|
+
] } } } };
|
|
1391
|
+
};
|
|
1392
|
+
/**
|
|
1393
|
+
* These are the objects of the server: a Service of type ClusterIP, and the Deployment with the
|
|
1394
|
+
* settings of the cell. The image is the lab image for the service, or the image of grpc-go.
|
|
1395
|
+
*/
|
|
1312
1396
|
const serverObjects = (cell, namespace, image, version = 1) => {
|
|
1313
1397
|
const preStop = whole(cell.pod.preStop, "the preStop sleep");
|
|
1398
|
+
const grace = whole(cell.pod.grace, "the grace period");
|
|
1314
1399
|
return [{
|
|
1315
1400
|
apiVersion: "v1",
|
|
1316
1401
|
kind: "Service",
|
|
@@ -1321,7 +1406,7 @@ const serverObjects = (cell, namespace, image, version = 1) => {
|
|
|
1321
1406
|
spec: {
|
|
1322
1407
|
selector: { app: "lab" },
|
|
1323
1408
|
ports: [{
|
|
1324
|
-
name: "http",
|
|
1409
|
+
name: stackOf(cell) === "grpc" ? "grpc" : "http",
|
|
1325
1410
|
port: 80,
|
|
1326
1411
|
targetPort: PORT
|
|
1327
1412
|
}]
|
|
@@ -1352,7 +1437,7 @@ const serverObjects = (cell, namespace, image, version = 1) => {
|
|
|
1352
1437
|
"lab.systems/version": String(version)
|
|
1353
1438
|
} },
|
|
1354
1439
|
spec: {
|
|
1355
|
-
terminationGracePeriodSeconds:
|
|
1440
|
+
terminationGracePeriodSeconds: grace,
|
|
1356
1441
|
enableServiceLinks: true,
|
|
1357
1442
|
topologySpreadConstraints: [{
|
|
1358
1443
|
maxSkew: 1,
|
|
@@ -1364,23 +1449,19 @@ const serverObjects = (cell, namespace, image, version = 1) => {
|
|
|
1364
1449
|
name: "app",
|
|
1365
1450
|
image,
|
|
1366
1451
|
imagePullPolicy: "Never",
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
name: "LAB_VERSION",
|
|
1370
|
-
value: String(version)
|
|
1371
|
-
}],
|
|
1372
|
-
ports: [{ containerPort: PORT }, { containerPort: HEALTH_PORT }],
|
|
1373
|
-
...cell.pod.startup === null ? {} : { startupProbe: probeOf("/startup", cell.pod.startup) },
|
|
1374
|
-
readinessProbe: probeOf("/readiness", cell.pod.readiness),
|
|
1375
|
-
livenessProbe: probeOf("/liveness", cell.pod.liveness),
|
|
1376
|
-
...preStop === 0 ? {} : { lifecycle: { preStop: { sleep: { seconds: preStop } } } }
|
|
1452
|
+
...serverContainer(cell, version),
|
|
1453
|
+
...preStopOf(preStop, grace)
|
|
1377
1454
|
}]
|
|
1378
1455
|
}
|
|
1379
1456
|
}
|
|
1380
1457
|
}
|
|
1381
1458
|
}];
|
|
1382
1459
|
};
|
|
1383
|
-
/**
|
|
1460
|
+
/**
|
|
1461
|
+
* These are the objects of the clients: a DaemonSet with one client on each node, the control plane
|
|
1462
|
+
* too. The client of the service is `client.mjs`, and the client of grpc-go is `client-grpc.mjs`.
|
|
1463
|
+
* Each one is in the lab image, and each one writes the same lines.
|
|
1464
|
+
*/
|
|
1384
1465
|
const clientObjects = (cell, namespace, image, target) => [{
|
|
1385
1466
|
apiVersion: "apps/v1",
|
|
1386
1467
|
kind: "DaemonSet",
|
|
@@ -1407,7 +1488,7 @@ const clientObjects = (cell, namespace, image, target) => [{
|
|
|
1407
1488
|
name: CLIENT,
|
|
1408
1489
|
image,
|
|
1409
1490
|
imagePullPolicy: "Never",
|
|
1410
|
-
command: ["node", "client.mjs"],
|
|
1491
|
+
command: ["node", stackOf(cell) === "grpc" ? "client-grpc.mjs" : "client.mjs"],
|
|
1411
1492
|
env: [
|
|
1412
1493
|
{
|
|
1413
1494
|
name: "TARGET",
|
|
@@ -1694,6 +1775,7 @@ const lintCell = (description, cell, propagation) => lint(description, scenarioO
|
|
|
1694
1775
|
const verdictsOf = (findings) => Object.fromEntries(findings.map((finding) => [finding.rule, finding.verdict]));
|
|
1695
1776
|
/** This function gives the description of the service of a cell, as the app of the image composes it. Nothing connects. */
|
|
1696
1777
|
const describeCell = (cell) => {
|
|
1778
|
+
if (cell.declaration === null) throw new Error(`${cell.name}: the server is not the service, thus the cell has no declaration to describe`);
|
|
1697
1779
|
const redis = cell.declaration.redis;
|
|
1698
1780
|
const client = redis === null ? void 0 : redisClient("redis", {
|
|
1699
1781
|
host: "redis",
|
|
@@ -1713,12 +1795,16 @@ const RULES_EVERY = {
|
|
|
1713
1795
|
};
|
|
1714
1796
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1715
1797
|
const slug = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
1798
|
+
/** This function tells if a cell has Redis: only a declaration of the service can have it. */
|
|
1799
|
+
const hasRedis = (cell) => cell.declaration !== null && cell.declaration.redis !== null;
|
|
1716
1800
|
/** This tracker keeps the latest state of the cell from the lines: each pod, each slice, the backends of each node and each client. */
|
|
1717
1801
|
var Tracker = class {
|
|
1718
1802
|
pods = /* @__PURE__ */ new Map();
|
|
1719
1803
|
slices = /* @__PURE__ */ new Map();
|
|
1720
1804
|
backends = /* @__PURE__ */ new Map();
|
|
1721
1805
|
clients = /* @__PURE__ */ new Map();
|
|
1806
|
+
/** This map is the backend of the last answer of each client, by client. */
|
|
1807
|
+
answers = /* @__PURE__ */ new Map();
|
|
1722
1808
|
last = 0;
|
|
1723
1809
|
see = (line) => {
|
|
1724
1810
|
if (typeof line.t === "number" && line.t > this.last && line.ev === "req") this.last = line.t;
|
|
@@ -1740,6 +1826,7 @@ var Tracker = class {
|
|
|
1740
1826
|
if (request.o === "ok") client.ok = Math.max(client.ok, request.t);
|
|
1741
1827
|
else client.failed = Math.max(client.failed, request.t);
|
|
1742
1828
|
this.clients.set(request.p, client);
|
|
1829
|
+
if (request.o === "ok" && request.b !== void 0) this.answers.set(request.p, request.b);
|
|
1743
1830
|
}
|
|
1744
1831
|
};
|
|
1745
1832
|
servers() {
|
|
@@ -1754,7 +1841,7 @@ const unsteady = (ctx) => {
|
|
|
1754
1841
|
const busy = servers.find((pod) => pod.del || !pod.ready);
|
|
1755
1842
|
if (busy !== void 0) return `${busy.name} is not ready`;
|
|
1756
1843
|
if (new Set(servers.map((pod) => pod.labels["pod-template-hash"])).size > 1) return "two versions of the replicas";
|
|
1757
|
-
if (cell
|
|
1844
|
+
if (hasRedis(cell)) {
|
|
1758
1845
|
const redis = [...tracker.pods.values()].filter((pod) => pod.labels["app"] === REDIS);
|
|
1759
1846
|
if (redis.length !== 1 || !redis[0].ready || redis[0].del) return "Redis is not ready";
|
|
1760
1847
|
}
|
|
@@ -1819,16 +1906,20 @@ const quiet = async (ctx, ours) => {
|
|
|
1819
1906
|
await sleep(3e4);
|
|
1820
1907
|
}
|
|
1821
1908
|
};
|
|
1822
|
-
/**
|
|
1823
|
-
|
|
1909
|
+
/**
|
|
1910
|
+
* This function deploys a cell in its namespace, with the watcher, the poll of the rules and the
|
|
1911
|
+
* clients. A client of gRPC starts after the server is ready: each failed connect of a channel makes
|
|
1912
|
+
* the backoff of grpc-js longer, up to 120 s.
|
|
1913
|
+
*/
|
|
1914
|
+
const deploy = async (lab, cluster, cell, namespace, images) => {
|
|
1824
1915
|
await cluster.apply([{
|
|
1825
1916
|
apiVersion: "v1",
|
|
1826
1917
|
kind: "Namespace",
|
|
1827
1918
|
metadata: { name: namespace }
|
|
1828
1919
|
}]);
|
|
1829
1920
|
await lab.watch([namespace]);
|
|
1830
|
-
if (cell
|
|
1831
|
-
await cluster.apply(redisObjects(namespace,
|
|
1921
|
+
if (hasRedis(cell)) {
|
|
1922
|
+
await cluster.apply(redisObjects(namespace, images.redis));
|
|
1832
1923
|
await cluster.kubectl([
|
|
1833
1924
|
"-n",
|
|
1834
1925
|
namespace,
|
|
@@ -1838,7 +1929,9 @@ const deploy = async (lab, cluster, cell, namespace, redisImage) => {
|
|
|
1838
1929
|
"--timeout=180s"
|
|
1839
1930
|
]);
|
|
1840
1931
|
}
|
|
1841
|
-
|
|
1932
|
+
const grpc = stackOf(cell) === "grpc";
|
|
1933
|
+
if (grpc && images.grpc === void 0) throw new Error(`${cell.name}: the cluster has no image of grpc-go`);
|
|
1934
|
+
await cluster.apply(serverObjects(cell, namespace, grpc ? images.grpc : lab.image));
|
|
1842
1935
|
const clusterIp = (await cluster.kubectl([
|
|
1843
1936
|
"-n",
|
|
1844
1937
|
namespace,
|
|
@@ -1848,7 +1941,8 @@ const deploy = async (lab, cluster, cell, namespace, redisImage) => {
|
|
|
1848
1941
|
"-o",
|
|
1849
1942
|
"jsonpath={.spec.clusterIP}"
|
|
1850
1943
|
])).trim();
|
|
1851
|
-
|
|
1944
|
+
const clients = clientObjects(cell, namespace, lab.image, `${clusterIp}:80`);
|
|
1945
|
+
if (!grpc) await cluster.apply(clients);
|
|
1852
1946
|
await cluster.kubectl([
|
|
1853
1947
|
"-n",
|
|
1854
1948
|
namespace,
|
|
@@ -1857,6 +1951,7 @@ const deploy = async (lab, cluster, cell, namespace, redisImage) => {
|
|
|
1857
1951
|
`deployment/lab`,
|
|
1858
1952
|
"--timeout=300s"
|
|
1859
1953
|
]);
|
|
1954
|
+
if (grpc) await cluster.apply(clients);
|
|
1860
1955
|
await cluster.kubectl([
|
|
1861
1956
|
"-n",
|
|
1862
1957
|
namespace,
|
|
@@ -1889,13 +1984,21 @@ const probeThePods = async (ctx) => {
|
|
|
1889
1984
|
await ctx.lab.probe([...new Set(netns)]);
|
|
1890
1985
|
ctx.probed = key;
|
|
1891
1986
|
};
|
|
1892
|
-
/**
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1987
|
+
/**
|
|
1988
|
+
* This function gives the replicas of the server at this time, in the sequence of their names. The
|
|
1989
|
+
* first one is the target of a run. A client of gRPC holds its connection to one replica, and the
|
|
1990
|
+
* channels go to the replica that stays after a run. Thus for the stack `grpc`, the first replica
|
|
1991
|
+
* is the one that answers the most clients (`byClients`).
|
|
1992
|
+
*/
|
|
1993
|
+
const replicasOf = (ctx) => {
|
|
1994
|
+
const replicas = ctx.tracker.servers().map((pod) => ({
|
|
1995
|
+
pod: pod.name,
|
|
1996
|
+
node: pod.node ?? "",
|
|
1997
|
+
ip: pod.ip ?? "",
|
|
1998
|
+
version: pod.labels["pod-template-hash"] ?? ""
|
|
1999
|
+
}));
|
|
2000
|
+
return stackOf(ctx.cell) === "grpc" ? byClients(replicas, ctx.tracker.answers) : replicas;
|
|
2001
|
+
};
|
|
1899
2002
|
/** This function does one step of a fault plan. It gives the stamp of its act, or null for an act without a stamp. */
|
|
1900
2003
|
const doStep = async (ctx, step, replicas, background) => {
|
|
1901
2004
|
const { lab, cluster, namespace } = ctx;
|
|
@@ -2063,7 +2166,7 @@ const runOnce = async (ctx, env, index, warmup, ours) => {
|
|
|
2063
2166
|
if (await steady(ctx, 3e5) !== null) return empty("the cell was not at rest after the wait for a quiet VM", await lab.now());
|
|
2064
2167
|
const replicas = replicasOf(ctx);
|
|
2065
2168
|
const restarts = new Map(ctx.tracker.servers().map((pod) => [pod.name, pod.cs.reduce((sum, status) => sum + status.rc, 0)]));
|
|
2066
|
-
const target = cell
|
|
2169
|
+
const target = hasRedis(cell) && [
|
|
2067
2170
|
"rst",
|
|
2068
2171
|
"blackhole",
|
|
2069
2172
|
"freeze-process",
|
|
@@ -2206,6 +2309,15 @@ const runKindCells = async (cells, options) => {
|
|
|
2206
2309
|
const environments = options.environments ?? 2;
|
|
2207
2310
|
const image = await labImage();
|
|
2208
2311
|
const redisImage = await localPin(REDIS_IMAGE);
|
|
2312
|
+
const grpcImage = cells.some((cell) => stackOf(cell) === "grpc") ? await imageOf({
|
|
2313
|
+
kind: "shipped",
|
|
2314
|
+
name: "grpc-go"
|
|
2315
|
+
}) : void 0;
|
|
2316
|
+
const images = {
|
|
2317
|
+
lab: image,
|
|
2318
|
+
redis: redisImage,
|
|
2319
|
+
grpc: grpcImage
|
|
2320
|
+
};
|
|
2209
2321
|
const groups = /* @__PURE__ */ new Map();
|
|
2210
2322
|
for (const cell of cells) groups.set(`${cell.mode}-${cell.workers}`, [...groups.get(`${cell.mode}-${cell.workers}`) ?? [], cell]);
|
|
2211
2323
|
const results = [];
|
|
@@ -2221,6 +2333,7 @@ const runKindCells = async (cells, options) => {
|
|
|
2221
2333
|
try {
|
|
2222
2334
|
await cluster.load(image);
|
|
2223
2335
|
await cluster.load(redisImage);
|
|
2336
|
+
if (grpcImage !== void 0 && group.some((cell) => stackOf(cell) === "grpc")) await cluster.load(grpcImage);
|
|
2224
2337
|
lab = await openKindLab(cluster, image);
|
|
2225
2338
|
const { GenericContainer } = await import("testcontainers");
|
|
2226
2339
|
const Shared = class extends GenericContainer {
|
|
@@ -2239,7 +2352,7 @@ const runKindCells = async (cells, options) => {
|
|
|
2239
2352
|
const off = lab.listen(tracker.see);
|
|
2240
2353
|
const loads = [];
|
|
2241
2354
|
try {
|
|
2242
|
-
const clusterIp = await deploy(lab, cluster, cell, namespace,
|
|
2355
|
+
const clusterIp = await deploy(lab, cluster, cell, namespace, images);
|
|
2243
2356
|
for (const name of [...lab.own(), loader.getName().replace(/^\//, "")]) ours.add(name);
|
|
2244
2357
|
const ctx = {
|
|
2245
2358
|
lab,
|
|
@@ -2271,7 +2384,8 @@ const runKindCells = async (cells, options) => {
|
|
|
2271
2384
|
cluster: manifest,
|
|
2272
2385
|
images: {
|
|
2273
2386
|
lab: image,
|
|
2274
|
-
redis: redisImage
|
|
2387
|
+
redis: redisImage,
|
|
2388
|
+
...stackOf(cell) === "grpc" ? { grpc: grpcImage } : {}
|
|
2275
2389
|
}
|
|
2276
2390
|
});
|
|
2277
2391
|
entry.loads.push(...loads);
|
|
@@ -2360,6 +2474,6 @@ const shardOf = (cells, index, count) => {
|
|
|
2360
2474
|
return out;
|
|
2361
2475
|
};
|
|
2362
2476
|
//#endregion
|
|
2363
|
-
export { CLIENT, CONTROLLER, DOCUMENTED, DRAIN_RULES, FLAG_CHECK, FORMAT, FORMAT_V1, HEALTH_PORT, KEPT_KERNEL, KIND, KIND_NOISE, KUBECTL, KUBE_PROXY_MODES, LAST_SLACK, LOG_LABEL, NODE_IMAGES, O04, O09, O10, O16, PORT, REDIS, REDIS_IMAGE, RELATIVE, ROUTE, RULES_EVERY, SERVER, STAGES, WATCHDOG, backendsAt, backendsOf, bandOf, byNode, cacheFolder, calibrationOf, capOf, cellsTable, checkRun, cidrOverlap, cidrRange, clientObjects, compose, createCluster, deletes, deletionOf, deletionWindow, describeCell, dropOf, emptyOf, ensureTool, estimatedTime, factsOf, failuresOf, goDuration, intakeAfter, intakeOf, judge, judgeKind, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, kindConfig, kubeProxyPatch, kubeletBand, labImage, latchBand, lintCell, localPin, medianInterval, medianText, middle, namespaceOf, nodeNames, openKindLab, outcomesOf, pausedWindow, planOf, platformKey, podsAt, podsOf, predictionText, probeOf, propagationOf, quantileOf, readRecord, recordsOf, redisObjects, referenceOf, removalBand, requestsOf, restartBand, restartsAt, reusesReset, rulesOf, runKindCells, scenario, scenarioOf, scenarioProblems, seconds, secondsOf, serverObjects, sha256, shardOf, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, slicesOf, stageLabImage, stallRemovalBand, timeSpread, toolPath, transitionOf, unitOf, upgrade, upgradeCell, verdictsOf, writeResult };
|
|
2477
|
+
export { CLIENT, CONTROLLER, DOCUMENTED, DRAIN_RULES, FLAG_CHECK, FORMAT, FORMAT_V1, HEALTH_PORT, KEPT_KERNEL, KIND, KIND_NOISE, KUBECTL, KUBE_PROXY_MODES, LAST_SLACK, LOG_LABEL, NODE_IMAGES, O04, O09, O10, O16, PORT, REDIS, REDIS_IMAGE, RELATIVE, ROUTE, RULES_EVERY, SERVER, STAGES, WATCHDOG, backendsAt, backendsOf, bandOf, byClients, byNode, cacheFolder, calibrationOf, capOf, cellsTable, checkRun, cidrOverlap, cidrRange, clientObjects, compose, createCluster, deletes, deletionOf, deletionWindow, deletionsOf, describeCell, dropOf, emptyOf, ensureTool, estimatedTime, factsOf, failuresOf, goDuration, intakeAfter, intakeOf, judge, judgeKind, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, kindConfig, kubeProxyPatch, kubeletBand, labImage, latchBand, lintCell, localPin, medianInterval, medianText, middle, namespaceOf, nodeNames, openKindLab, outcomesOf, pausedWindow, planOf, platformKey, podsAt, podsOf, predictionText, probeOf, propagationOf, quantileOf, readRecord, recordsOf, redisObjects, referenceOf, removalBand, requestsOf, restartBand, restartsAt, reusesReset, rulesOf, runKindCells, scenario, scenarioOf, scenarioProblems, seconds, secondsOf, serverObjects, sha256, shardOf, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, slicesOf, stackOf, stageLabImage, stallRemovalBand, stoppingOf, timeSpread, toolPath, transitionOf, unitOf, upgrade, upgradeCell, verdictsOf, writeResult };
|
|
2364
2478
|
|
|
2365
2479
|
//# sourceMappingURL=index.js.map
|