@mark1russell7/kind 0.11.7 → 0.11.9

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 CHANGED
@@ -211,7 +211,7 @@ The plan's `event:clock-jump` has no trigger. A step of CLOCK_REALTIME in the VM
211
211
 
212
212
  `predict.ts` gives each band from the declaration, the pod settings and the calibration of the propagation:
213
213
 
214
- - **A deletion.** The listener of a leaving pod stays open for its preStop sleep and the hold of `fail readiness`. A connection fails when the rules of its node select the pod after that. `rules.ts` gives O04 with the hold as data, and `lint()` decides it.
214
+ - **A deletion.** The listener of a leaving pod stays open for its preStop sleep and the hold of `fail readiness`. A connection fails when the rules of its node select the pod after that. The entry `@mark1russell7/service/rules` gives O04 with the hold as data, and `lint()` decides it. Kind gives the drain rules of that entry again (`DRAIN_RULES`, `O04`, `O09`, `O10`, `O16`).
215
215
  - **The way that a plan stops a pod.** `stopOf(cell)` reads it from the trigger: a deletion, a liveness restart, a crash, or null when the plan stops no pod. `lintCell()` gives each cell only the drain rules that guard its way (`lintStop()` of the service). A plan that stops no pod gets each drain rule. A crash gets no drain rule, and its finding of `stop` depends.
216
216
  - **A pause of kube-proxy** keeps the rules of one node for its hold. The window on that node ends one propagation after the pause. A SYN_SENT entry of an earlier run lives up to 122 s, thus the window also opens up to 122 s before the intake. `predictionText` writes each end of a window with its band, for example `the intake + [-122, 4.605] s`.
217
217
  - **A reset from a leaving pod** leaves the conntrack entry of its flow in CLOSE for 10 s, with the NAT to that pod. From 0.1.6, a window can give that life (`afterReset`). Then a request outside the window can fail with the outcome of the rule (`reusesReset`). The condition is a reset of the same client on the same port, up to that life before the request. The catalog gives it to a deletion without a hold, in the modes with the NAT in conntrack.
package/build/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { $ as updatesOf, $t as Declaration, A as TriggerV1, An as nodeNames, At as rulesOf, B as RunCheck, Bt as ztunnelPodsOf, C as KindCellResult, Cn as NODE_IMAGES, Ct as outcomesOf, D as PredictionV1, Dn as goDuration, Dt as requestsOf, E as LoadSample, En as cidrRange, Et as recordsOf, F as KIND_NOISE, Ft as slicesOf, G as judgeKind, Gt as PORT, H as checkRun, Ht as CLIENT, I as KindDecision, It as socketsOf, J as propagationOf, Jt as bandOf, K as observationAfter, Kt as REDIS, L as KindVerdict, Lt as stoppingOf, M as upgrade, Mn as scenarioProblems, Mt as signalsOf, N as upgradeCell, Nt as sliceDropOf, O as RunCheckV1, On as kindConfig, Ot as restartOf, P as Deleted, Pt as sliceEmptyOf, Q as unitOf, Qt as Band, R as LAST_SLACK, Rt as transitionOf, S as KindCellRecordV1, Sn as KubeProxyMode, St as kernelCounts, T as KindVerdictV1, Tn as cidrOverlap, Tt as podsOf, U as deletionsOf, Ut as HEALTH_PORT, V as RunFacts, Vt as ztunnelReplaced, W as factsOf, Wt as LOG_LABEL, X as referenceOf, Xt as secondsOf, Y as propagationSource, Yt as seconds, Z as reusesReset, Zt as showBand, _ as ClusterManifest, _n as WindowPrediction, _t as dropOf, a as STAGES, an as MeshFlavor, at as RecordLine, b as FORMAT_V1, bn as KindConfig, bt as intakeOf, c as readRecord, cn as Prediction, ct as SliceLine, d as calibrationOf, dn as Replica, dt as TunnelLine, en as EdgeProtocol, et as Endpoint, f as cellsTable, fn as ResetReuse, ft as ZtunnelPodLine, g as BandV1, gn as TriggerKind, gt as deletionOf, h as timeSpread, hn as Trigger, ht as byNode, i as quantileOf, in as Mesh, it as PropagationSource, j as judge, jn as scenario, jt as signalOf, k as RunFactsV1, kn as kubeProxyPatch, kt as restartsAt, l as Judged, ln as RedisDeclaration, lt as SocketLine, m as predictionText, mn as Template, mt as byClients, n as medianInterval, nn as KindRun, nt as Line, o as kindCellRecordSchema, on as MeshMode, ot as Request, p as medianText, pn as Stack, pt as backendsAt, q as observationEnd, qt as SERVER, r as middle, rn as KubeletProbe, rt as PodLine, s as kindCellRecordV1Schema, sn as PodSettings, st as RulesLine, t as RELATIVE, tn as KindCell, tt as HBONE_PORT, u as TimeSpread, un as Reference, ut as TCP_STATE, v as EnvironmentManifest, vn as Workload, vt as emptyOf, w as KindCellV1, wn as NodeImage, wt as podsAt, x as KindCellRecord, xn as KindScenario, xt as isTunnelState, y as FORMAT, yn as KUBE_PROXY_MODES, yt as failuresOf, z as Propagated, zt as tunnelsOf } from "./judge-DnB9wEwC.js";
1
+ import { $ as unitOf, $t as secondsOf, A as TriggerV1, An as cidrOverlap, At as restartsAt, B as RunCheck, Bt as stoppingOf, C as KindCellResult, Cn as Workload, Ct as kernelCounts, D as PredictionV1, Dn as KubeProxyMode, Dt as recordsOf, E as LoadSample, En as KindScenario, Et as podsOf, F as KIND_NOISE, Fn as nodeNames, Ft as sliceEmptyOf, G as factsOf, Gt as CLIENT, H as checkRun, Ht as tunnelsOf, I as KindDecision, In as scenario, It as sliceReadyAt, J as observationEnd, Jt as PORT, K as judgeKind, Kt as HEALTH_PORT, L as KindVerdict, Ln as scenarioProblems, Lt as sliceReadyOf, M as upgrade, Mn as goDuration, Mt as signalOf, N as upgradeCell, Nn as kindConfig, Nt as signalsOf, O as RunCheckV1, On as NODE_IMAGES, Ot as requestsOf, P as Deleted, Pn as kubeProxyPatch, Pt as sliceDropOf, Q as reusesReset, Qt as seconds, R as LAST_SLACK, Rt as slicesOf, S as KindCellRecordV1, Sn as WindowPrediction, St as isTunnelState, T as KindVerdictV1, Tn as KindConfig, Tt as podsAt, U as cyclesOf, Ut as ztunnelPodsOf, V as RunFacts, Vt as transitionOf, W as deletionsOf, Wt as ztunnelReplaced, X as propagationSource, Xt as SERVER, Y as propagationOf, Yt as REDIS, Z as referenceOf, Zt as bandOf, _ as ClusterManifest, _n as ResetReuse, _t as deletionOf, a as STAGES, an as Flap, at as PropagationSource, b as FORMAT_V1, bn as Trigger, bt as failuresOf, c as readRecord, cn as KubeletProbe, ct as RulesLine, d as calibrationOf, dn as MeshMode, dt as TCP_STATE, en as showBand, et as updatesOf, f as cellsTable, fn as PodSettings, ft as TunnelLine, g as BandV1, gn as Replica, gt as byNode, h as timeSpread, hn as Reference, ht as byClients, i as quantileOf, in as EdgeProtocol, it as PodLine, j as judge, jn as cidrRange, jt as rulesOf, k as RunFactsV1, kn as NodeImage, kt as restartOf, l as Judged, ln as Mesh, lt as SliceLine, m as predictionText, mn as RedisDeclaration, mt as backendsAt, n as medianInterval, nn as Cycle, nt as HBONE_PORT, o as kindCellRecordSchema, on as KindCell, ot as RecordLine, p as medianText, pn as Prediction, pt as ZtunnelPodLine, q as observationAfter, qt as LOG_LABEL, r as middle, rn as Declaration, rt as Line, s as kindCellRecordV1Schema, sn as KindRun, st as Request, t as RELATIVE, tn as Band, tt as Endpoint, u as TimeSpread, un as MeshFlavor, ut as SocketLine, v as EnvironmentManifest, vn as Stack, vt as dropOf, w as KindCellV1, wn as KUBE_PROXY_MODES, wt as outcomesOf, x as KindCellRecord, xn as TriggerKind, xt as intakeOf, y as FORMAT, yn as Template, yt as emptyOf, z as Propagated, zt as socketsOf } from "./judge-BBHSeu4i.js";
2
2
  import { Quantity } from "@mark1russell7/quantity";
3
- import { CheckFn, Finding, HealthServer, Poll, Rule, Scenario, ServiceBuilder, ServiceDescription, Stage, StopWay, Timing, TransportImpl, Verdict } from "@mark1russell7/service";
3
+ import { CheckFn, Finding, HealthServer, Poll, Scenario, ServiceBuilder, ServiceDescription, Stage, StopWay, Timing, TransportImpl, Verdict } from "@mark1russell7/service";
4
4
  import { BuildImage } from "@mark1russell7/harness";
5
5
  import { RedisClient } from "@mark1russell7/service/redis";
6
+ import { DRAIN_RULES, O04, O09, O10, O16 } from "@mark1russell7/service/rules";
6
7
  //#region src/backends.d.ts
7
8
  /** A service rule names the Service whose backends the agent reads: its namespace, its name, and its ClusterIP and port for IPVS. */
8
9
  interface ServiceRule {
@@ -176,6 +177,7 @@ type Step = {
176
177
  * - `inject` starts the trigger. The stamp of its first act is the `fault` reference.
177
178
  * - `heal` ends the fault, `hold` after the fault. A trigger that ends by itself has no heal.
178
179
  * - `restore` brings the cell back to its steady state after the run.
180
+ * - `flaps` are the faults after the first one. A plan without them has one fault.
179
181
  */
180
182
  interface FaultPlan {
181
183
  readonly prepare?: readonly Step[] | undefined;
@@ -183,6 +185,8 @@ interface FaultPlan {
183
185
  readonly hold: Quantity | null;
184
186
  readonly heal: readonly Step[];
185
187
  readonly restore: readonly Step[];
188
+ /** These flaps are the faults after the first one (`Trigger.flaps`). Each one does the inject and the heal again. */
189
+ readonly flaps?: readonly Flap[] | undefined;
186
190
  }
187
191
  /** This name is the name of the AuthorizationPolicy of the trigger `authorization-policy`. */
188
192
  export declare const POLICY = "lab-deny";
@@ -260,8 +264,17 @@ export declare const sysctlCommand: (key: string, value: string) => string[];
260
264
  * old flow of the peer. A cell of the middlebox opens no new flow in its gap.
261
265
  */
262
266
  export declare const middleboxCommands: (peers: readonly string[], idle: Quantity | null, port: number) => string[][];
263
- /** This function gives the fault plan of a cell. */
267
+ /**
268
+ * This function gives the fault plan of a cell. A trigger with flaps (`Trigger.flaps`) gives them to
269
+ * its plan: the runner then does the inject and the heal again for each flap. A plan with no hold or
270
+ * no heal cannot flap.
271
+ */
264
272
  export declare const planOf: (cell: KindCell) => FaultPlan;
273
+ /**
274
+ * This function gives the time from the first fault of a trigger to its last heal. It is the hold,
275
+ * then the rest and the hold of each flap. A trigger with no hold gives zero.
276
+ */
277
+ export declare const faultSpan: (trigger: Pick<KindCell["trigger"], "hold" | "flaps">) => Quantity;
265
278
  /** 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. */
266
279
  export declare const deletes: (cell: KindCell) => boolean;
267
280
  //#endregion
@@ -680,24 +693,6 @@ interface LearnWindow {
680
693
  export declare const learnWindow: (declaration: Declaration, readiness: KubeletProbe, propagation: Propagation, workload: Workload) => LearnWindow;
681
694
  //#endregion
682
695
  //#region src/rules.d.ts
683
- /**
684
- * These are the rules of the ordering law that a drain in Kubernetes meets, as data. The caller of
685
- * `lint()` gives the rules, thus they are here and not in the service.
686
- */
687
- /**
688
- * O04 with the hold of the service. The listener of a pod stays open through the preStop sleep and
689
- * then through the hold of `fail readiness`. Thus the sum of the two must cover the propagation of
690
- * the endpoints. The rule of the research is the case without a hold.
691
- */
692
- export declare const O04: Rule;
693
- /** O09 is W8: readiness detection plus propagation is no longer than liveness detection plus the preStop sleep. */
694
- export declare const O09: Rule;
695
- /** O10: the preStop sleep plus the drain of the process fits in the grace period. */
696
- export declare const O10: Rule;
697
- /** O16: the cooperative window is shorter than the drain, which is shorter than the grace less the preStop sleep. */
698
- export declare const O16: Rule;
699
- /** These are the rules of the drain. A kind cell gets the rules that guard the way that its plan stops a pod. */
700
- export declare const DRAIN_RULES: readonly Rule[];
701
696
  /**
702
697
  * This function gives the scenario of a cell for `lint()`: the preStop sleep, the grace, the probe
703
698
  * period and the propagation. The probe windows are the period times the threshold of each probe.
@@ -817,5 +812,5 @@ export declare const toolPath: (tool: Tool, key?: string, cache?: string) => str
817
812
  */
818
813
  export declare const ensureTool: (tool: Tool, key?: string, cache?: string) => Promise<string>;
819
814
  //#endregion
820
- export { type Band, type BandV1, CLIENT, type Cluster, type ClusterHosts, type ClusterManifest, type Credentials, type Declaration, type Deleted, type DeletionWindow, type EdgeProtocol, type EdsSpec, Endpoint, type EnvironmentManifest, FORMAT, FORMAT_V1, type FaultPlan, HBONE_PORT, 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, type LeafCertificate, type LearnWindow, 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, TCP_STATE, type Target, type Template, type TimeSpread, type Tool, type Trigger, type TriggerKind, type TriggerV1, TunnelLine, type Verdicts, type WindowPrediction, type Workload, ZtunnelPodLine, backendsAt, bandOf, byClients, byNode, calibrationOf, cellsTable, checkRun, cidrOverlap, cidrRange, deletionOf, deletionsOf, dropOf, emptyOf, factsOf, failuresOf, goDuration, intakeOf, isTunnelState, 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, tunnelsOf, unitOf, updatesOf, upgrade, upgradeCell, ztunnelPodsOf, ztunnelReplaced };
815
+ export { type Band, type BandV1, CLIENT, type Cluster, type ClusterHosts, type ClusterManifest, type Credentials, type Cycle, DRAIN_RULES, type Declaration, type Deleted, type DeletionWindow, type EdgeProtocol, type EdsSpec, Endpoint, type EnvironmentManifest, FORMAT, FORMAT_V1, type FaultPlan, type Flap, HBONE_PORT, 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, type LeafCertificate, type LearnWindow, Line, type ListenerCounts, type LoadSample, type Mesh, type MeshFlavor, type MeshImages, type MeshMode, NODE_IMAGES, type NodeImage, O04, O09, O10, O16, 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, TCP_STATE, type Target, type Template, type TimeSpread, type Tool, type Trigger, type TriggerKind, type TriggerV1, TunnelLine, type Verdicts, type WindowPrediction, type Workload, ZtunnelPodLine, backendsAt, bandOf, byClients, byNode, calibrationOf, cellsTable, checkRun, cidrOverlap, cidrRange, cyclesOf, deletionOf, deletionsOf, dropOf, emptyOf, factsOf, failuresOf, goDuration, intakeOf, isTunnelState, 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, sliceReadyAt, sliceReadyOf, slicesOf, socketsOf, stoppingOf, timeSpread, transitionOf, tunnelsOf, unitOf, updatesOf, upgrade, upgradeCell, ztunnelPodsOf, ztunnelReplaced };
821
816
  //# sourceMappingURL=index.d.ts.map
package/build/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as signalOf, A as medianInterval, B as emptyOf, C as propagationOf, D as unitOf, E as reusesReset, F as backendsAt, G as outcomesOf, H as intakeOf, I as byClients, J as recordsOf, K as podsAt, L as byNode, M as quantileOf, N as HBONE_PORT, O as updatesOf, P as TCP_STATE, Q as rulesOf, R as deletionOf, S as observationEnd, T as referenceOf, U as isTunnelState, V as failuresOf, W as kernelCounts, X as restartOf, Y as requestsOf, Z as restartsAt, _ as checkRun, _t as seconds, a as calibrationOf, at as stoppingOf, b as judgeKind, c as predictionText, ct as ztunnelPodsOf, d as FORMAT_V1, dt as HEALTH_PORT, et as signalsOf, f as judge, ft as LOG_LABEL, g as LAST_SLACK, gt as bandOf, h as KIND_NOISE, ht as SERVER, i as readRecord, it as socketsOf, j as middle, k as RELATIVE, l as timeSpread, lt as ztunnelReplaced, m as upgradeCell, mt as REDIS, n as kindCellRecordSchema, nt as sliceEmptyOf, o as cellsTable, ot as transitionOf, p as upgrade, pt as PORT, q as podsOf, r as kindCellRecordV1Schema, rt as slicesOf, s as medianText, st as tunnelsOf, t as STAGES, tt as sliceDropOf, u as FORMAT, ut as CLIENT, v as deletionsOf, vt as secondsOf, w as propagationSource, x as observationAfter, y as factsOf, yt as showBand, z as dropOf } from "./judge-Cs91dOGO.js";
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 showBand, 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 REDIS, a as calibrationOf, at as sliceReadyOf, b as factsOf, bt as seconds, c as predictionText, ct as stoppingOf, d as FORMAT_V1, dt as ztunnelPodsOf, et as signalOf, f as judge, ft as ztunnelReplaced, g as LAST_SLACK, gt as PORT, h as KIND_NOISE, ht as LOG_LABEL, i as readRecord, it as sliceReadyAt, j as medianInterval, k as updatesOf, l as timeSpread, lt as transitionOf, m as upgradeCell, mt as HEALTH_PORT, n as kindCellRecordSchema, nt as sliceDropOf, o as cellsTable, ot as slicesOf, p as upgrade, pt as CLIENT, q as podsAt, r as kindCellRecordV1Schema, rt as sliceEmptyOf, s as medianText, st as socketsOf, t as STAGES, tt as signalsOf, u as FORMAT, ut as tunnelsOf, v as cyclesOf, vt as SERVER, w as propagationOf, x as judgeKind, xt as secondsOf, y as deletionsOf, yt as bandOf, z as deletionOf } from "./judge-ZoLw3AXV.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, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
@@ -12,6 +12,7 @@ import { createRequire } from "node:module";
12
12
  import { tmpdir } from "node:os";
13
13
  import { harnessImage, imageOf } from "@mark1russell7/harness";
14
14
  import { redisClient } from "@mark1russell7/service/redis";
15
+ import { DRAIN_RULES, DRAIN_RULES as DRAIN_RULES$1, O04, O09, O10, O16 } from "@mark1russell7/service/rules";
15
16
  //#region src/backends.ts
16
17
  const hexIp = (hex) => [
17
18
  0,
@@ -853,8 +854,36 @@ const middleboxCommands = (peers, idle, port) => {
853
854
  * is a rule in the target replica only, with the client pods as peers.
854
855
  */
855
856
  const linkOf = (cell) => cell.trigger.partition === true ? "server" : cell.template === "T15" ? "servers" : "redis";
856
- /** This function gives the fault plan of a cell. */
857
+ /**
858
+ * This function gives the fault plan of a cell. A trigger with flaps (`Trigger.flaps`) gives them to
859
+ * its plan: the runner then does the inject and the heal again for each flap. A plan with no hold or
860
+ * no heal cannot flap.
861
+ */
857
862
  const planOf = (cell) => {
863
+ const plan = stepsOf(cell);
864
+ const flaps = cell.trigger.flaps;
865
+ if (flaps === void 0 || flaps.length === 0) return plan;
866
+ 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`);
867
+ for (const flap of flaps) if (valueIn(flap.rest, "ms") <= 0 || valueIn(flap.hold, "ms") <= 0) throw new Error("each flap has a rest and a hold above zero");
868
+ return {
869
+ ...plan,
870
+ flaps
871
+ };
872
+ };
873
+ /**
874
+ * This function gives the time from the first fault of a trigger to its last heal. It is the hold,
875
+ * then the rest and the hold of each flap. A trigger with no hold gives zero.
876
+ */
877
+ const faultSpan = (trigger) => {
878
+ const hold = trigger.hold === void 0 ? 0 : valueIn(trigger.hold, "s");
879
+ const flaps = (trigger.flaps ?? []).reduce((sum, flap) => sum + valueIn(flap.rest, "s") + valueIn(flap.hold, "s"), 0);
880
+ return {
881
+ value: Math.round((hold + flaps) * 1e6) / 1e6,
882
+ unit: "s"
883
+ };
884
+ };
885
+ /** This function gives the steps of the fault plan of a cell, with no flaps. */
886
+ const stepsOf = (cell) => {
858
887
  const ns = ["-n", "{namespace}"];
859
888
  const trigger = cell.trigger;
860
889
  const replicas = cell.pod.replicas;
@@ -2653,130 +2682,6 @@ const learnWindow = (declaration, readiness, propagation, workload) => {
2653
2682
  };
2654
2683
  //#endregion
2655
2684
  //#region src/rules.ts
2656
- /**
2657
- * These are the rules of the ordering law that a drain in Kubernetes meets, as data. The caller of
2658
- * `lint()` gives the rules, thus they are here and not in the service.
2659
- */
2660
- /**
2661
- * O04 with the hold of the service. The listener of a pod stays open through the preStop sleep and
2662
- * then through the hold of `fail readiness`. Thus the sum of the two must cover the propagation of
2663
- * the endpoints. The rule of the research is the case without a hold.
2664
- */
2665
- const O04 = {
2666
- id: "O04",
2667
- rule: "the preStop sleep plus the hold of fail readiness is at least the endpoint propagation",
2668
- window: "new connections reach a pod whose listener is closed",
2669
- expr: {
2670
- lhs: { terms: [{
2671
- knob: "prestop_sleep",
2672
- coef: 1,
2673
- at: "pod"
2674
- }, {
2675
- knob: "readiness_hold",
2676
- coef: 1,
2677
- at: "process"
2678
- }] },
2679
- op: ">=",
2680
- rhs: { terms: [{
2681
- knob: "propagation_bound",
2682
- coef: 1,
2683
- at: "cluster"
2684
- }] }
2685
- }
2686
- };
2687
- /** O09 is W8: readiness detection plus propagation is no longer than liveness detection plus the preStop sleep. */
2688
- const O09 = {
2689
- id: "O09",
2690
- rule: "readiness detection plus endpoint propagation is no longer than liveness detection plus the preStop sleep",
2691
- window: "traffic reaches a container that its liveness probe kills",
2692
- expr: {
2693
- lhs: { terms: [{
2694
- knob: "readiness_detection_window",
2695
- coef: 1,
2696
- at: "pod"
2697
- }, {
2698
- knob: "propagation_bound",
2699
- coef: 1,
2700
- at: "cluster"
2701
- }] },
2702
- op: "<=",
2703
- rhs: { terms: [{
2704
- knob: "liveness_detection_window",
2705
- coef: 1,
2706
- at: "pod"
2707
- }, {
2708
- knob: "prestop_sleep",
2709
- coef: 1,
2710
- at: "pod"
2711
- }] }
2712
- }
2713
- };
2714
- /** O10: the preStop sleep plus the drain of the process fits in the grace period. */
2715
- const O10 = {
2716
- id: "O10",
2717
- rule: "the preStop sleep plus the shutdown of the process fits in the grace period",
2718
- window: "work in flight at SIGKILL is cut, and each connection closes at once",
2719
- expr: {
2720
- lhs: { terms: [{
2721
- knob: "prestop_sleep",
2722
- coef: 1,
2723
- at: "pod"
2724
- }, {
2725
- knob: "process_drain_time",
2726
- coef: 1,
2727
- at: "process"
2728
- }] },
2729
- op: "<=",
2730
- rhs: { terms: [{
2731
- knob: "termination_grace",
2732
- coef: 1,
2733
- at: "pod"
2734
- }] }
2735
- }
2736
- };
2737
- /** O16: the cooperative window is shorter than the drain, which is shorter than the grace less the preStop sleep. */
2738
- const O16 = {
2739
- id: "O16",
2740
- rule: "the cooperative window is shorter than the drain, which is shorter than the grace less the preStop sleep",
2741
- window: "handlers are cut before they answer, or the drain itself gets SIGKILL",
2742
- expr: {
2743
- lhs: { terms: [{
2744
- knob: "cooperative_window",
2745
- coef: 1,
2746
- at: "process"
2747
- }] },
2748
- op: "<",
2749
- rhs: { terms: [{
2750
- knob: "process_drain_time",
2751
- coef: 1,
2752
- at: "process"
2753
- }] }
2754
- },
2755
- expr_more: [{
2756
- lhs: { terms: [{
2757
- knob: "process_drain_time",
2758
- coef: 1,
2759
- at: "process"
2760
- }] },
2761
- op: "<",
2762
- rhs: { terms: [{
2763
- knob: "termination_grace",
2764
- coef: 1,
2765
- at: "pod"
2766
- }, {
2767
- knob: "prestop_sleep",
2768
- coef: -1,
2769
- at: "pod"
2770
- }] }
2771
- }]
2772
- };
2773
- /** These are the rules of the drain. A kind cell gets the rules that guard the way that its plan stops a pod. */
2774
- const DRAIN_RULES = [
2775
- O04,
2776
- O09,
2777
- O10,
2778
- O16
2779
- ];
2780
2685
  const seconds$1 = (amount) => valueIn(amount, "s");
2781
2686
  const s = (value) => ({
2782
2687
  value,
@@ -2835,7 +2740,7 @@ const stopOf = (cell) => {
2835
2740
  const lintCell = (description, cell, propagation) => {
2836
2741
  const way = stopOf(cell);
2837
2742
  const scenario = scenarioOf(cell, propagation);
2838
- return way === null ? lint(description, scenario, DRAIN_RULES) : lintStop(description, scenario, DRAIN_RULES, way);
2743
+ return way === null ? lint(description, scenario, DRAIN_RULES$1) : lintStop(description, scenario, DRAIN_RULES$1, way);
2839
2744
  };
2840
2745
  /** This function gives the verdict of each drain rule, by its identifier. */
2841
2746
  const verdictsOf = (findings) => Object.fromEntries(findings.map((finding) => [finding.rule, finding.verdict]));
@@ -3500,16 +3405,46 @@ const runOnce = async (ctx, env, index, warmup, ours) => {
3500
3405
  if (stamp !== null && fault === null) fault = stamp.t0;
3501
3406
  }
3502
3407
  let heal = null;
3503
- if (plan.hold !== null) {
3408
+ const healAfter = async (from, hold) => {
3504
3409
  const now = await lab.now();
3505
- await sleep(Math.max(0, valueIn(plan.hold, "ms") - (now - (fault ?? now))));
3410
+ await sleep(Math.max(0, valueIn(hold, "ms") - (now - (from ?? now))));
3411
+ let stamped = null;
3506
3412
  for (const step of plan.heal) try {
3507
3413
  const stamp = await doStep(ctx, step, replicas, background);
3508
- if (stamp !== null && heal === null) heal = stamp.t0;
3414
+ if (stamp !== null && stamped === null) stamped = stamp.t0;
3509
3415
  } catch (error) {
3510
3416
  notes.push(`the heal ${step.do} failed: ${String(error)}`);
3511
3417
  }
3418
+ return stamped;
3419
+ };
3420
+ const cycles = [];
3421
+ if (plan.hold !== null) {
3422
+ heal = await healAfter(fault, plan.hold);
3423
+ if (fault !== null && heal !== null) cycles.push({
3424
+ fault,
3425
+ heal
3426
+ });
3427
+ for (const flap of plan.flaps ?? []) {
3428
+ const now = await lab.now();
3429
+ await sleep(Math.max(0, valueIn(flap.rest, "ms") - (now - (heal ?? now))));
3430
+ let again = null;
3431
+ for (const step of plan.inject) try {
3432
+ const stamp = await doStep(ctx, step, replicas, background);
3433
+ if (stamp !== null && again === null) again = stamp.t0;
3434
+ } catch (error) {
3435
+ notes.push(`the inject ${step.do} of a flap failed: ${String(error)}`);
3436
+ }
3437
+ const healed = await healAfter(again, flap.hold);
3438
+ if (again !== null && healed !== null) cycles.push({
3439
+ fault: again,
3440
+ heal: healed
3441
+ });
3442
+ heal = healed ?? heal;
3443
+ }
3512
3444
  }
3445
+ const flapped = (plan.flaps ?? []).length > 0;
3446
+ if (flapped && cycles.length !== (plan.flaps ?? []).length + 1) notes.push(`the run has ${cycles.length} cycles with stamps of the ${(plan.flaps ?? []).length + 1} of the trigger`);
3447
+ const flapping = flapped ? { cycles } : {};
3513
3448
  const settled = await Promise.allSettled(background);
3514
3449
  for (const each of settled) if (each.status === "fulfilled" && typeof each.value === "object" && each.value !== null && "t1" in each.value) resume = each.value.t1;
3515
3450
  const limit = Date.now() + 24e4 + valueIn(cell.pod.grace, "ms") + valueIn(cell.pod.preStop, "ms");
@@ -3524,7 +3459,8 @@ const runOnce = async (ctx, env, index, warmup, ours) => {
3524
3459
  acts: {
3525
3460
  fault,
3526
3461
  heal,
3527
- resume
3462
+ resume,
3463
+ ...flapping
3528
3464
  },
3529
3465
  target: stoppedOf(cell, replicas, target, lab.lines),
3530
3466
  replicas,
@@ -3573,7 +3509,8 @@ const runOnce = async (ctx, env, index, warmup, ours) => {
3573
3509
  acts: {
3574
3510
  fault,
3575
3511
  heal,
3576
- resume
3512
+ resume,
3513
+ ...flapping
3577
3514
  },
3578
3515
  target: stopped,
3579
3516
  paused,
@@ -3791,7 +3728,7 @@ const runKindCells = async (cells, options) => {
3791
3728
  };
3792
3729
  /** This function gives the estimate of a cell in seconds, for the arithmetic of the shards. */
3793
3730
  const estimate = (cell, environments = 2) => {
3794
- const hold = cell.trigger.hold === void 0 ? 0 : valueIn(cell.trigger.hold, "s");
3731
+ const hold = valueIn(faultSpan(cell.trigger), "s");
3795
3732
  const trigger = {
3796
3733
  "liveness-restart": 100,
3797
3734
  crash: 30,
@@ -3840,6 +3777,6 @@ const shardOf = (cells, index, count) => {
3840
3777
  return out;
3841
3778
  };
3842
3779
  //#endregion
3843
- 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, NETNS_DEFAULTS, NODE_IMAGES, O04, O09, O10, O16, POLICY, PORT, QUIET_LEAD, REDIS, REDIS_IMAGE, RELATIVE, ROUTE, RULES_EVERY, SERVER, SPREAD_LEAD, STAGES, SYSCTL, TCP_STATE, WATCHDOG, ZTUNNEL_PODS, backendsAt, backendsOf, bandOf, byClients, byNode, cacheFolder, calibrationOf, capOf, cellsTable, certificateArgs, checkRun, cidrOverlap, cidrRange, clientObjects, clusterFilter, compose, createCluster, deletes, deletionOf, deletionWindow, deletionsOf, describeCell, dropOf, emptyOf, ensureTool, estimatedTime, factsOf, failuresOf, 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, 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, stopOf, stoppingOf, sysctlCommand, tarEntry, timeSpread, toolPath, transitionOf, tunnelsOf, unitOf, updatesOf, upgrade, upgradeCell, verdictsOf, writeResult, ztunnelCertificates, ztunnelEnvArgs, ztunnelPodsOf, ztunnelReplaced };
3780
+ 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, NETNS_DEFAULTS, NODE_IMAGES, O04, O09, O10, O16, POLICY, PORT, QUIET_LEAD, REDIS, REDIS_IMAGE, RELATIVE, ROUTE, RULES_EVERY, SERVER, SPREAD_LEAD, STAGES, SYSCTL, TCP_STATE, WATCHDOG, 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, 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, sliceReadyAt, sliceReadyOf, slicesOf, socketsOf, stackOf, stageLabImage, stallRemovalBand, stopOf, stoppingOf, sysctlCommand, tarEntry, timeSpread, toolPath, transitionOf, tunnelsOf, unitOf, updatesOf, upgrade, upgradeCell, verdictsOf, writeResult, ztunnelCertificates, ztunnelEnvArgs, ztunnelPodsOf, ztunnelReplaced };
3844
3781
 
3845
3782
  //# sourceMappingURL=index.js.map