@mark1russell7/kind 0.7.0 → 0.8.2

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.
@@ -118,13 +118,17 @@ type Stack = "service" | "grpc";
118
118
  * - `crash` stops the process of the service with SIGKILL from the node.
119
119
  * - `liveness-restart` fails the liveness check of one replica, and the kubelet restarts it.
120
120
  * - `freeze-process` stops a process with SIGSTOP from the node: kube-proxy on one node (T07), or Redis (T18).
121
- * - `rst` and `blackhole` are the rules of the injector of the harness in the namespace of Redis.
121
+ * - `rst` and `blackhole` are the rules of the injector of the harness in the namespace of Redis. In
122
+ * T15 they are in the namespace of each replica. A black hole of T15 can stop the calls of the
123
+ * clients before the fault (`quiet`). It can also set `tcp_retries2` of each client pod (`retries`).
122
124
  * - `peer-gone-without-close` drops the segments of Redis, and then Redis goes away.
123
125
  * - `app-stall` blocks the event loop of one replica.
124
126
  * - `rollout` is a rolling update while Redis is down.
125
127
  * - `cpu-throttle` sets `cpu.max` in the cgroup of one replica.
126
128
  * - `authorization-policy` applies an AuthorizationPolicy of Istio that denies the clients, for the hold (T15).
127
129
  * - `idle` stops the calls of each client with SIGUSR1, and starts them again with SIGUSR2 after the hold: a gap (T15).
130
+ * The gap can have a stateful middlebox in each replica (`middlebox`), or a new channel that each
131
+ * client closes before the gap (`close`).
128
132
  * - `ztunnel-restart` deletes the ztunnel pod of each node with its grace. The DaemonSet makes a new pod on each node (T15).
129
133
  * - `none` injects nothing: a calibration measures the path itself.
130
134
  */
@@ -150,6 +154,29 @@ interface Trigger<A = Quantity> {
150
154
  * and denies no call.
151
155
  */
152
156
  readonly account?: string | undefined;
157
+ /**
158
+ * This time is the idle timeout of a stateful middlebox in the network namespace of each replica
159
+ * (T15). The middlebox is there from before the fault to the end of the run. It forgets a flow of
160
+ * the mesh leg after this time with no segment. Then it drops each later segment of the flow: the
161
+ * silent-drop form of a router. Each segment of the flow refreshes the timeout, thus the PING of
162
+ * ztunnel can keep an idle flow. Conntrack of the namespace stays as it is (`middleboxCommands`).
163
+ */
164
+ readonly middlebox?: Quantity | undefined;
165
+ /** This count is `net.ipv4.tcp_retries2` in the network namespace of each client pod for the run (T15): the HBONE socket of its ztunnel is there. */
166
+ readonly retries?: number | undefined;
167
+ /**
168
+ * This flag stops the calls of each client before the fault, and starts them again after the heal
169
+ * (T15 `blackhole`). Thus no data of the app is on the mesh leg in the fault. The PING of ztunnel
170
+ * is the first segment that gets no answer.
171
+ */
172
+ readonly quiet?: boolean | undefined;
173
+ /**
174
+ * This flag gives each client a new channel at the fault of `idle` (T15). The client closes its
175
+ * channel before the fault. At the fault it opens a new channel and calls on it for a short time,
176
+ * then it closes it. Thus the HBONE connection of the client carries no stream in the gap, and its
177
+ * last use is at the fault. The heal opens a new channel again.
178
+ */
179
+ readonly close?: boolean | undefined;
153
180
  }
154
181
  /** A kubelet probe is the period, the failure threshold and the timeout of one probe of the pod. */
155
182
  interface KubeletProbe {
@@ -257,6 +284,8 @@ interface ResetReuse<A = Quantity> {
257
284
  * - `ends`: the socket of each client ends in the band after the reference `from`. With no band, no
258
285
  * socket of a client ends in the run. Then the first request of each client after the heal goes on
259
286
  * the socket of its last request before the fault. With `window`, the failed requests also start in that window.
287
+ * With `of: "tunnel"`, the HBONE connection of the ztunnel of each client ends in the band instead.
288
+ * With `answer`, the close of the peer in that band is an answer to the close of the client.
260
289
  * - `measured`: no document bounds the outcome. The run keeps the measurement.
261
290
  */
262
291
  type Prediction<A = Quantity> = {
@@ -290,6 +319,20 @@ type Prediction<A = Quantity> = {
290
319
  readonly band: Band<A> | null;
291
320
  /** This window is where the failed requests start, or none when the prediction does not test them. */
292
321
  readonly window?: WindowPrediction<A> | undefined;
322
+ /**
323
+ * This text names what ends. `socket`, the default, is the socket of the channel of each client.
324
+ * `tunnel` is the HBONE connection of the ztunnel of each client to the replica of its last answer
325
+ * before the heal (T15 ambient). Its side of the client must close it: the peer does not close
326
+ * first. Then the first request of each client after the heal must succeed, on a new tunnel.
327
+ */
328
+ readonly of?: "socket" | "tunnel" | undefined;
329
+ /**
330
+ * This band is where the peer can answer the close of the side of the client with its own
331
+ * close, after `from` (only with `of: "tunnel"`). A change of a tunnel from ESTABLISHED to
332
+ * CLOSE_WAIT in it agrees: the FIN of the peer came first, but as an answer. Before it, the
333
+ * peer closed first. Without it, only FIN_WAIT1 agrees.
334
+ */
335
+ readonly answer?: Band<A> | undefined;
293
336
  readonly why: string;
294
337
  } | {
295
338
  readonly kind: "measured";
@@ -536,7 +579,33 @@ interface SocketLine {
536
579
  readonly n: string;
537
580
  readonly lp: number | null;
538
581
  readonly err?: string | null;
539
- }
582
+ /** This address is the local address of the socket at its connect: the address of the client pod. Lines before 0.8.0 do not have it. */
583
+ readonly la?: string | null;
584
+ }
585
+ /** This port is the HBONE port of ztunnel in each pod of the ambient mesh of Istio 1.31.3: the mesh leg ends there. */
586
+ declare const HBONE_PORT = 15008;
587
+ /** These are the states of a TCP socket of Linux that a tunnel line names (include/net/tcp_states.h of Linux v6.17, lines 12–28). */
588
+ declare const TCP_STATE: {
589
+ readonly established: 1;
590
+ readonly finWait1: 4;
591
+ readonly closeWait: 8;
592
+ };
593
+ /**
594
+ * A tunnel line is a change of the state of a TCP socket with the HBONE port at one end, from the
595
+ * prober (`kernel.state`). `src` and `dst` are each an address and a port, `a.b.c.d:port`. The stamp
596
+ * `t` is in ms on CLOCK_MONOTONIC.
597
+ */
598
+ interface TunnelLine {
599
+ readonly t: number;
600
+ readonly src: string;
601
+ readonly dst: string;
602
+ readonly old: number;
603
+ readonly new: number;
604
+ }
605
+ /** This function tells if a kernel record is a change of the state of a socket of the HBONE port. */
606
+ declare const isTunnelState: (record: Readonly<Record<string, unknown>>) => boolean;
607
+ /** This function gives the tunnel lines of a run, in sequence of their stamps. */
608
+ declare const tunnelsOf: (lines: readonly Line[]) => TunnelLine[];
540
609
  declare const requestsOf: (lines: readonly Line[]) => Request[];
541
610
  declare const podsOf: (lines: readonly Line[]) => PodLine[];
542
611
  declare const slicesOf: (lines: readonly Line[], svc: string) => SliceLine[];
@@ -944,5 +1013,5 @@ declare const quantileOf: (sorted: readonly number[], p: number) => number;
944
1013
  */
945
1014
  declare const medianInterval: (sorted: readonly number[]) => [number, number];
946
1015
  //#endregion
947
- export { updatesOf as $, MeshFlavor as $t, TriggerV1 as A, sliceEmptyOf as At, RunCheck as B, PORT as Bt, KindCellResult as C, kubeProxyPatch as Cn, requestsOf as Ct, PredictionV1 as D, signalOf as Dt, LoadSample as E, scenarioProblems as En, rulesOf as Et, KIND_NOISE as F, ztunnelPodsOf as Ft, judgeKind as G, secondsOf as Gt, checkRun as H, SERVER as Ht, KindDecision as I, ztunnelReplaced as It, propagationOf as J, Declaration as Jt, observationAfter as K, showBand as Kt, KindVerdict as L, CLIENT as Lt, upgrade as M, socketsOf as Mt, upgradeCell as N, stoppingOf as Nt, RunCheckV1 as O, signalsOf as Ot, Deleted as P, transitionOf as Pt, unitOf as Q, Mesh as Qt, LAST_SLACK as R, HEALTH_PORT as Rt, KindCellRecordV1 as S, kindConfig as Sn, recordsOf as St, KindVerdictV1 as T, scenario as Tn, restartsAt as Tt, deletionsOf as U, bandOf as Ut, RunFacts as V, REDIS as Vt, factsOf as W, seconds as Wt, referenceOf as X, KindRun as Xt, propagationSource as Y, KindCell as Yt, reusesReset as Z, KubeletProbe as Zt, ClusterManifest as _, NODE_IMAGES as _n, intakeOf as _t, STAGES as a, Replica as an, Request as at, FORMAT_V1 as b, cidrRange as bn, podsAt as bt, readRecord as c, Template as cn, SocketLine as ct, calibrationOf as d, WindowPrediction as dn, byClients as dt, MeshMode as en, Endpoint as et, cellsTable as f, Workload as fn, byNode as ft, BandV1 as g, KubeProxyMode as gn, failuresOf as gt, timeSpread as h, KindScenario as hn, emptyOf as ht, quantileOf as i, Reference as in, RecordLine as it, judge as j, slicesOf as jt, RunFactsV1 as k, sliceDropOf as kt, Judged as l, Trigger as ln, ZtunnelPodLine as lt, predictionText as m, KindConfig as mn, dropOf as mt, medianInterval as n, Prediction as nn, PodLine as nt, kindCellRecordSchema as o, ResetReuse as on, RulesLine as ot, medianText as p, KUBE_PROXY_MODES as pn, deletionOf as pt, observationEnd as q, Band as qt, middle as r, RedisDeclaration as rn, PropagationSource as rt, kindCellRecordV1Schema as s, Stack as sn, SliceLine as st, RELATIVE as t, PodSettings as tn, Line as tt, TimeSpread as u, TriggerKind as un, backendsAt as ut, EnvironmentManifest as v, NodeImage as vn, kernelCounts as vt, KindCellV1 as w, nodeNames as wn, restartOf as wt, KindCellRecord as x, goDuration as xn, podsOf as xt, FORMAT as y, cidrOverlap as yn, outcomesOf as yt, Propagated as z, LOG_LABEL as zt };
948
- //# sourceMappingURL=judge-B1nSdbS0.d.ts.map
1016
+ export { updatesOf as $, Declaration as $t, TriggerV1 as A, scenario as An, rulesOf as At, RunCheck as B, ztunnelPodsOf as Bt, KindCellResult as C, NodeImage as Cn, outcomesOf as Ct, PredictionV1 as D, kindConfig as Dn, requestsOf as Dt, LoadSample as E, goDuration as En, recordsOf as Et, KIND_NOISE as F, slicesOf as Ft, judgeKind as G, PORT as Gt, checkRun as H, CLIENT as Ht, KindDecision as I, socketsOf as It, propagationOf as J, bandOf as Jt, observationAfter as K, REDIS as Kt, KindVerdict as L, stoppingOf as Lt, upgrade as M, signalsOf as Mt, upgradeCell as N, sliceDropOf as Nt, RunCheckV1 as O, kubeProxyPatch as On, restartOf as Ot, Deleted as P, sliceEmptyOf as Pt, unitOf as Q, Band as Qt, LAST_SLACK as R, transitionOf as Rt, KindCellRecordV1 as S, NODE_IMAGES as Sn, kernelCounts as St, KindVerdictV1 as T, cidrRange as Tn, podsOf as Tt, deletionsOf as U, HEALTH_PORT as Ut, RunFacts as V, ztunnelReplaced as Vt, factsOf as W, LOG_LABEL as Wt, referenceOf as X, secondsOf as Xt, propagationSource as Y, seconds as Yt, reusesReset as Z, showBand as Zt, ClusterManifest as _, Workload as _n, dropOf as _t, STAGES as a, MeshMode as an, RecordLine as at, FORMAT_V1 as b, KindScenario as bn, intakeOf as bt, readRecord as c, RedisDeclaration as cn, SliceLine as ct, calibrationOf as d, ResetReuse as dn, TunnelLine as dt, KindCell as en, Endpoint as et, cellsTable as f, Stack as fn, ZtunnelPodLine as ft, BandV1 as g, WindowPrediction as gn, deletionOf as gt, timeSpread as h, TriggerKind as hn, byNode as ht, quantileOf as i, MeshFlavor as in, PropagationSource as it, judge as j, scenarioProblems as jn, signalOf as jt, RunFactsV1 as k, nodeNames as kn, restartsAt as kt, Judged as l, Reference as ln, SocketLine as lt, predictionText as m, Trigger as mn, byClients as mt, medianInterval as n, KubeletProbe as nn, Line as nt, kindCellRecordSchema as o, PodSettings as on, Request as ot, medianText as p, Template as pn, backendsAt as pt, observationEnd as q, SERVER as qt, middle as r, Mesh as rn, PodLine as rt, kindCellRecordV1Schema as s, Prediction as sn, RulesLine as st, RELATIVE as t, KindRun as tn, HBONE_PORT as tt, TimeSpread as u, Replica as un, TCP_STATE as ut, EnvironmentManifest as v, KUBE_PROXY_MODES as vn, emptyOf as vt, KindCellV1 as w, cidrOverlap as wn, podsAt as wt, KindCellRecord as x, KubeProxyMode as xn, isTunnelState as xt, FORMAT as y, KindConfig as yn, failuresOf as yt, Propagated as z, tunnelsOf as zt };
1017
+ //# sourceMappingURL=judge-gpC49mVw.d.ts.map
package/build/judge.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as updatesOf, $t as MeshFlavor, A as TriggerV1, At as sliceEmptyOf, B as RunCheck, Bt as PORT, C as KindCellResult, Ct as requestsOf, D as PredictionV1, Dt as signalOf, E as LoadSample, Et as rulesOf, F as KIND_NOISE, Ft as ztunnelPodsOf, G as judgeKind, Gt as secondsOf, H as checkRun, Ht as SERVER, I as KindDecision, It as ztunnelReplaced, J as propagationOf, Jt as Declaration, K as observationAfter, Kt as showBand, L as KindVerdict, Lt as CLIENT, M as upgrade, Mt as socketsOf, N as upgradeCell, Nt as stoppingOf, O as RunCheckV1, Ot as signalsOf, P as Deleted, Pt as transitionOf, Q as unitOf, Qt as Mesh, R as LAST_SLACK, Rt as HEALTH_PORT, S as KindCellRecordV1, St as recordsOf, T as KindVerdictV1, Tt as restartsAt, U as deletionsOf, Ut as bandOf, V as RunFacts, Vt as REDIS, W as factsOf, Wt as seconds, X as referenceOf, Xt as KindRun, Y as propagationSource, Yt as KindCell, Z as reusesReset, Zt as KubeletProbe, _ as ClusterManifest, _t as intakeOf, a as STAGES, an as Replica, at as Request, b as FORMAT_V1, bt as podsAt, c as readRecord, cn as Template, ct as SocketLine, d as calibrationOf, dn as WindowPrediction, dt as byClients, en as MeshMode, et as Endpoint, f as cellsTable, fn as Workload, ft as byNode, g as BandV1, gn as KubeProxyMode, gt as failuresOf, h as timeSpread, ht as emptyOf, i as quantileOf, in as Reference, it as RecordLine, j as judge, jt as slicesOf, k as RunFactsV1, kt as sliceDropOf, l as Judged, ln as Trigger, lt as ZtunnelPodLine, m as predictionText, mt as dropOf, n as medianInterval, nn as Prediction, nt as PodLine, o as kindCellRecordSchema, on as ResetReuse, ot as RulesLine, p as medianText, pt as deletionOf, q as observationEnd, qt as Band, r as middle, rn as RedisDeclaration, rt as PropagationSource, s as kindCellRecordV1Schema, sn as Stack, st as SliceLine, t as RELATIVE, tn as PodSettings, tt as Line, u as TimeSpread, un as TriggerKind, ut as backendsAt, v as EnvironmentManifest, vt as kernelCounts, w as KindCellV1, wt as restartOf, x as KindCellRecord, xt as podsOf, y as FORMAT, yt as outcomesOf, z as Propagated, zt as LOG_LABEL } from "./judge-B1nSdbS0.js";
2
- export { type Band, type BandV1, CLIENT, type ClusterManifest, type Declaration, type Deleted, Endpoint, type EnvironmentManifest, FORMAT, FORMAT_V1, HEALTH_PORT, type Judged, KIND_NOISE, type KindCell, type KindCellRecord, type KindCellRecordV1, type KindCellResult, type KindCellV1, type KindDecision, type KindRun, type KindVerdict, type KindVerdictV1, type KubeProxyMode, type KubeletProbe, LAST_SLACK, LOG_LABEL, Line, type LoadSample, type Mesh, type MeshFlavor, type MeshMode, PORT, PodLine, type PodSettings, type Prediction, type PredictionV1, type Propagated, PropagationSource, REDIS, RELATIVE, RecordLine, type RedisDeclaration, type Reference, type Replica, Request, type ResetReuse, RulesLine, type RunCheck, type RunCheckV1, type RunFacts, type RunFactsV1, SERVER, STAGES, SliceLine, SocketLine, type Stack, type Template, type TimeSpread, type Trigger, type TriggerKind, type TriggerV1, type WindowPrediction, type Workload, ZtunnelPodLine, backendsAt, bandOf, byClients, byNode, calibrationOf, cellsTable, checkRun, deletionOf, deletionsOf, dropOf, emptyOf, factsOf, failuresOf, intakeOf, judge, judgeKind, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, medianInterval, medianText, middle, observationAfter, observationEnd, outcomesOf, podsAt, podsOf, predictionText, propagationOf, propagationSource, quantileOf, readRecord, recordsOf, referenceOf, requestsOf, restartOf, restartsAt, reusesReset, rulesOf, seconds, secondsOf, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, slicesOf, socketsOf, stoppingOf, timeSpread, transitionOf, unitOf, updatesOf, upgrade, upgradeCell, ztunnelPodsOf, ztunnelReplaced };
1
+ import { $ as updatesOf, $t as Declaration, A as TriggerV1, At as rulesOf, B as RunCheck, Bt as ztunnelPodsOf, C as KindCellResult, Ct as outcomesOf, D as PredictionV1, Dt as requestsOf, E as LoadSample, 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, Mt as signalsOf, N as upgradeCell, Nt as sliceDropOf, O as RunCheckV1, 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, St as kernelCounts, T as KindVerdictV1, 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 Workload, _t as dropOf, a as STAGES, an as MeshMode, at as RecordLine, b as FORMAT_V1, bt as intakeOf, c as readRecord, cn as RedisDeclaration, ct as SliceLine, d as calibrationOf, dn as ResetReuse, dt as TunnelLine, en as KindCell, et as Endpoint, f as cellsTable, fn as Stack, ft as ZtunnelPodLine, g as BandV1, gn as WindowPrediction, gt as deletionOf, h as timeSpread, hn as TriggerKind, ht as byNode, i as quantileOf, in as MeshFlavor, it as PropagationSource, j as judge, jt as signalOf, k as RunFactsV1, kt as restartsAt, l as Judged, ln as Reference, lt as SocketLine, m as predictionText, mn as Trigger, mt as byClients, n as medianInterval, nn as KubeletProbe, nt as Line, o as kindCellRecordSchema, on as PodSettings, ot as Request, p as medianText, pn as Template, pt as backendsAt, q as observationEnd, qt as SERVER, r as middle, rn as Mesh, rt as PodLine, s as kindCellRecordV1Schema, sn as Prediction, st as RulesLine, t as RELATIVE, tn as KindRun, tt as HBONE_PORT, u as TimeSpread, un as Replica, ut as TCP_STATE, v as EnvironmentManifest, vt as emptyOf, w as KindCellV1, wt as podsAt, x as KindCellRecord, xn as KubeProxyMode, xt as isTunnelState, y as FORMAT, yt as failuresOf, z as Propagated, zt as tunnelsOf } from "./judge-gpC49mVw.js";
2
+ export { type Band, type BandV1, CLIENT, type ClusterManifest, type Declaration, type Deleted, Endpoint, type EnvironmentManifest, FORMAT, FORMAT_V1, HBONE_PORT, HEALTH_PORT, type Judged, KIND_NOISE, type KindCell, type KindCellRecord, type KindCellRecordV1, type KindCellResult, type KindCellV1, type KindDecision, type KindRun, type KindVerdict, type KindVerdictV1, type KubeProxyMode, type KubeletProbe, LAST_SLACK, LOG_LABEL, Line, type LoadSample, type Mesh, type MeshFlavor, type MeshMode, PORT, PodLine, type PodSettings, type Prediction, type PredictionV1, type Propagated, PropagationSource, REDIS, RELATIVE, RecordLine, type RedisDeclaration, type Reference, type Replica, Request, type ResetReuse, RulesLine, type RunCheck, type RunCheckV1, type RunFacts, type RunFactsV1, SERVER, STAGES, SliceLine, SocketLine, type Stack, TCP_STATE, type Template, type TimeSpread, type Trigger, type TriggerKind, type TriggerV1, TunnelLine, type WindowPrediction, type Workload, ZtunnelPodLine, backendsAt, bandOf, byClients, byNode, calibrationOf, cellsTable, checkRun, deletionOf, deletionsOf, dropOf, emptyOf, factsOf, failuresOf, intakeOf, isTunnelState, judge, judgeKind, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, medianInterval, medianText, middle, observationAfter, observationEnd, outcomesOf, podsAt, podsOf, predictionText, propagationOf, propagationSource, quantileOf, readRecord, recordsOf, referenceOf, requestsOf, restartOf, restartsAt, reusesReset, rulesOf, seconds, secondsOf, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, slicesOf, socketsOf, stoppingOf, timeSpread, transitionOf, tunnelsOf, unitOf, updatesOf, upgrade, upgradeCell, ztunnelPodsOf, ztunnelReplaced };
package/build/judge.js CHANGED
@@ -1 +1 @@
1
- export { ot as CLIENT, u as FORMAT, d as FORMAT_V1, st as HEALTH_PORT, h as KIND_NOISE, g as LAST_SLACK, ct as LOG_LABEL, lt as PORT, ut as REDIS, k as RELATIVE, dt as SERVER, t as STAGES, N as backendsAt, ft as bandOf, P as byClients, F as byNode, a as calibrationOf, o as cellsTable, _ as checkRun, I as deletionOf, v as deletionsOf, L as dropOf, R as emptyOf, y as factsOf, z as failuresOf, B as intakeOf, f as judge, b as judgeKind, V as kernelCounts, n as kindCellRecordSchema, r as kindCellRecordV1Schema, A as medianInterval, s as medianText, j as middle, x as observationAfter, S as observationEnd, H as outcomesOf, U as podsAt, W as podsOf, c as predictionText, C as propagationOf, w as propagationSource, M as quantileOf, i as readRecord, G as recordsOf, T as referenceOf, K as requestsOf, q as restartOf, J as restartsAt, E as reusesReset, Y as rulesOf, pt as seconds, mt as secondsOf, ht as showBand, X as signalOf, Z as signalsOf, Q as sliceDropOf, $ as sliceEmptyOf, et as slicesOf, tt as socketsOf, nt as stoppingOf, l as timeSpread, rt as transitionOf, D as unitOf, O as updatesOf, p as upgrade, m as upgradeCell, it as ztunnelPodsOf, at as ztunnelReplaced } from "./judge-CmiF-Fcs.js";
1
+ export { ut as CLIENT, u as FORMAT, d as FORMAT_V1, N as HBONE_PORT, dt as HEALTH_PORT, h as KIND_NOISE, g as LAST_SLACK, ft as LOG_LABEL, pt as PORT, mt as REDIS, k as RELATIVE, ht as SERVER, t as STAGES, P as TCP_STATE, F as backendsAt, gt as bandOf, I as byClients, L as byNode, a as calibrationOf, o as cellsTable, _ as checkRun, R as deletionOf, v as deletionsOf, z as dropOf, B as emptyOf, y as factsOf, V as failuresOf, H as intakeOf, U as isTunnelState, f as judge, b as judgeKind, W as kernelCounts, n as kindCellRecordSchema, r as kindCellRecordV1Schema, A as medianInterval, s as medianText, j as middle, x as observationAfter, S as observationEnd, G as outcomesOf, K as podsAt, q as podsOf, c as predictionText, C as propagationOf, w as propagationSource, M as quantileOf, i as readRecord, J as recordsOf, T as referenceOf, Y as requestsOf, X as restartOf, Z as restartsAt, E as reusesReset, Q as rulesOf, _t as seconds, vt as secondsOf, yt as showBand, $ as signalOf, et as signalsOf, tt as sliceDropOf, nt as sliceEmptyOf, rt as slicesOf, it as socketsOf, at as stoppingOf, l as timeSpread, ot as transitionOf, st as tunnelsOf, D as unitOf, O as updatesOf, p as upgrade, m as upgradeCell, ct as ztunnelPodsOf, lt as ztunnelReplaced } from "./judge-B6W_gFv0.js";
@@ -17,6 +17,8 @@
17
17
  // Each socket of the channel also gives a line `sock` at its connect and at its close (client-lines.ts).
18
18
  // SIGUSR1 stops the calls, and SIGUSR2 starts them again: the gap of the trigger `idle`. A listener of
19
19
  // SIGUSR1 replaces the start of the inspector of Node at that signal. The channel stays open in the gap.
20
+ // SIGHUP stops the calls and closes the channel (internal-channel.ts:839-861). Then the next SIGUSR2
21
+ // opens a new channel. The connect line of a socket also gives its local address (`la`).
20
22
  import net from "node:net";
21
23
  import { followSocket, gapOf } from "./client-lines.ts";
22
24
 
@@ -42,6 +44,12 @@ const portOf = (socket) => {
42
44
  socket?._handle?.getsockname?.(out);
43
45
  return typeof out.port === "number" ? out.port : null;
44
46
  };
47
+ // the local address of a socket, or null once its handle is closed
48
+ const addressOf = (socket) => {
49
+ const out = {};
50
+ socket?._handle?.getsockname?.(out);
51
+ return typeof out.address === "string" ? out.address : null;
52
+ };
45
53
 
46
54
  // grpc-js connects with net.connect (transport.ts:830), on the module object that this patch changes.
47
55
  // It loads after the patch. With an address and no host name, connect() comes before net.connect
@@ -54,13 +62,19 @@ net.connect = function (...args) {
54
62
  socket.once("connect", () => {
55
63
  lp = portOf(socket) ?? lp;
56
64
  });
57
- followSocket(socket, () => portOf(socket), say, now);
65
+ followSocket(socket, () => portOf(socket), say, now, () => addressOf(socket));
58
66
  return socket;
59
67
  };
60
68
 
61
- const gap = gapOf((signal, listener) => process.on(signal, listener), say, now);
62
-
63
69
  const grpc = await import("@grpc/grpc-js");
70
+ const open = () => new grpc.Client(`${host}:${port}`, grpc.credentials.createInsecure(), {});
71
+ let client = open();
72
+ const gap = gapOf((signal, listener) => process.on(signal, listener), say, now, {
73
+ close: () => client.close(),
74
+ open: () => {
75
+ client = open();
76
+ },
77
+ });
64
78
 
65
79
  // ---- the messages of grpc.health.v1, encoded by hand: the request of the service "", and the status of the reply
66
80
  const encodeRequest = () => Buffer.alloc(0);
@@ -77,8 +91,6 @@ const outcomeOf = (error) => {
77
91
  return "error";
78
92
  };
79
93
 
80
- const client = new grpc.Client(`${host}:${port}`, grpc.credentials.createInsecure(), {});
81
-
82
94
  const fire = () => {
83
95
  if (gap.stopped) return;
84
96
  const t = now();
package/image/watch.mjs CHANGED
@@ -37,10 +37,26 @@ const flush = () => {
37
37
  setInterval(flush, 50);
38
38
  const say = (line) => pending.push(`${JSON.stringify(line)}\n`);
39
39
 
40
+ /**
41
+ * A handler gives this value for an ERROR event of 410 Gone: the API has no history from the last
42
+ * resource version. The status of the response is 200, thus only the event tells. The watch then
43
+ * opens again with no resource version, and the API gives each object again as ADDED.
44
+ */
45
+ const GONE = Symbol("gone");
46
+ /** the result of a handler for an ERROR event: GONE for an expired resource version, else null */
47
+ const errorOf = (status) => (status?.code === 410 ? GONE : null);
48
+
40
49
  /** a stream of lines from the API, again after each end, from the last resource version it saw */
41
50
  const stream = (path, query, onLine, again = true) => {
42
51
  let version = null;
43
52
  const open = () => {
53
+ // An error and a close can both end one request: only the first opens the next one.
54
+ let ended = false;
55
+ const reopen = (delay) => {
56
+ if (!again || ended) return;
57
+ ended = true;
58
+ setTimeout(open, delay);
59
+ };
44
60
  const search = new URLSearchParams(query);
45
61
  if (version !== null) search.set("resourceVersion", version);
46
62
  const req = https.request(
@@ -50,7 +66,7 @@ const stream = (path, query, onLine, again = true) => {
50
66
  say({ ev: "watch-fail", t: now(), path, status: res.statusCode });
51
67
  res.resume();
52
68
  if (res.statusCode === 410) version = null;
53
- if (again) setTimeout(open, 500);
69
+ reopen(500);
54
70
  return;
55
71
  }
56
72
  let partial = "";
@@ -63,18 +79,18 @@ const stream = (path, query, onLine, again = true) => {
63
79
  for (const text of split) {
64
80
  if (text.trim() === "") continue;
65
81
  const next = onLine(text, t);
66
- if (typeof next === "string") version = next;
82
+ if (next === GONE) version = null;
83
+ else if (typeof next === "string") version = next;
67
84
  }
68
85
  });
69
- res.on("end", () => {
70
- if (again) setTimeout(open, 100);
71
- });
72
- res.on("error", () => undefined);
86
+ // A response that the connection cuts has no end, only its error and its close.
87
+ res.on("error", (error) => say({ ev: "watch-fail", t: now(), path, why: String(error.message) }));
88
+ res.on("close", () => reopen(100));
73
89
  },
74
90
  );
75
91
  req.on("error", (error) => {
76
92
  say({ ev: "watch-fail", t: now(), path, why: String(error.message) });
77
- if (again) setTimeout(open, 500);
93
+ reopen(500);
78
94
  });
79
95
  req.end();
80
96
  };
@@ -149,7 +165,7 @@ for (const ns of namespaces) {
149
165
  if (event.type === "BOOKMARK") return pod?.metadata?.resourceVersion;
150
166
  if (event.type === "ERROR") {
151
167
  say({ ev: "watch-fail", t, path: "pods", status: pod?.code });
152
- return null;
168
+ return errorOf(pod);
153
169
  }
154
170
  const ready = (pod.status?.conditions ?? []).find((condition) => condition.type === "Ready")?.status === "True";
155
171
  const containers = (pod.status?.containerStatuses ?? []).map((status) => ({
@@ -189,7 +205,7 @@ for (const ns of namespaces) {
189
205
  const event = JSON.parse(text);
190
206
  const slice = event.object;
191
207
  if (event.type === "BOOKMARK") return slice?.metadata?.resourceVersion;
192
- if (event.type === "ERROR") return null;
208
+ if (event.type === "ERROR") return errorOf(slice);
193
209
  say({
194
210
  ev: "slice",
195
211
  t,
@@ -212,7 +228,7 @@ for (const ns of namespaces) {
212
228
  const event = JSON.parse(text);
213
229
  const object = event.object;
214
230
  if (event.type === "BOOKMARK") return object?.metadata?.resourceVersion;
215
- if (event.type === "ERROR") return null;
231
+ if (event.type === "ERROR") return errorOf(object);
216
232
  say({
217
233
  ev: "event",
218
234
  t,
@@ -236,7 +252,7 @@ if (ztunnel) {
236
252
  if (event.type === "BOOKMARK") return pod?.metadata?.resourceVersion;
237
253
  if (event.type === "ERROR") {
238
254
  say({ ev: "watch-fail", t, path: "ztunnel pods", status: pod?.code });
239
- return null;
255
+ return errorOf(pod);
240
256
  }
241
257
  const containers = (pod.status?.containerStatuses ?? []).map((status) => ({ n: status.name, r: status.ready, rc: status.restartCount, st: stateOf(status.state), id: status.containerID ?? null }));
242
258
  say({ ev: "ztunnel-pod", t, type: event.type, name: pod.metadata.name, node: pod.spec?.nodeName ?? null, ip: pod.status?.podIP ?? null, del: pod.metadata.deletionTimestamp !== undefined, cs: containers });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@mark1russell7/kind",
4
- "version": "0.7.0",
4
+ "version": "0.8.2",
5
5
  "description": "Kubernetes in Docker as a lab for connection timers: a kind cluster from a scenario, faults in the namespaces of its pods, observations on the one monotonic clock of the VM, and predictions from the ordering law. Its judge entry reads and judges a raw record without Docker.",
6
6
  "keywords": [
7
7
  "kubernetes",
@@ -65,9 +65,9 @@
65
65
  "node": ">=22.19"
66
66
  },
67
67
  "dependencies": {
68
- "@mark1russell7/harness": "^0.12.3",
68
+ "@mark1russell7/harness": "^0.12.4",
69
69
  "@mark1russell7/quantity": "^0.2.0",
70
- "@mark1russell7/service": "^0.5.9",
70
+ "@mark1russell7/service": "^0.5.11",
71
71
  "testcontainers": "12.2.0"
72
72
  },
73
73
  "peerDependencies": {
@@ -20,8 +20,11 @@ export type Say = (line: Readonly<Record<string, unknown>>) => void;
20
20
  *
21
21
  * A prediction `ends` reads the close line: the end of the socket of the channel. A FIN of the peer
22
22
  * and a reset each end it.
23
+ *
24
+ * With `address`, the connect line also gives the local address of the socket (`la`): the address of
25
+ * the pod. A prediction of the tunnels finds the HBONE connection of the ztunnel of the pod by it.
23
26
  */
24
- export const followSocket = (socket: WatchedSocket, port: () => number | null, say: Say, now: () => number): void => {
27
+ export const followSocket = (socket: WatchedSocket, port: () => number | null, say: Say, now: () => number, address?: () => string | null): void => {
25
28
  let lp = port();
26
29
  let error: string | null = null;
27
30
  socket.once("error", (failure) => {
@@ -29,32 +32,60 @@ export const followSocket = (socket: WatchedSocket, port: () => number | null, s
29
32
  });
30
33
  socket.once("connect", () => {
31
34
  lp = port() ?? lp;
32
- say({ ev: "sock", what: "connect", t: now(), lp });
35
+ say({ ev: "sock", what: "connect", t: now(), lp, ...(address === undefined ? {} : { la: address() }) });
33
36
  });
34
37
  socket.once("close", (hadError) => {
35
38
  say({ ev: "sock", what: "close", t: now(), lp, err: hadError ? (error ?? "error") : null });
36
39
  });
37
40
  };
38
41
 
39
- /** A gap is the state of the calls of a client: `stopped` from SIGUSR1 to the next SIGUSR2. */
42
+ /**
43
+ * A gap is the state of the calls of a client. It is `stopped` from SIGUSR1 or SIGHUP to the next
44
+ * SIGUSR2. It is `closed` from SIGHUP to the next SIGUSR2.
45
+ */
40
46
  export interface Gap {
41
47
  readonly stopped: boolean;
48
+ readonly closed: boolean;
42
49
  }
43
50
 
51
+ /** A channel is the part of the channel of a client that a gap closes and opens again. */
52
+ export interface Channel {
53
+ close(): void;
54
+ open(): void;
55
+ }
56
+
57
+ /** These are the signals of a gap. */
58
+ export type GapSignal = "SIGUSR1" | "SIGUSR2" | "SIGHUP";
59
+
44
60
  /**
45
61
  * This function stops the calls of a client at SIGUSR1, and it starts them again at SIGUSR2. That is
46
62
  * the gap of the trigger `idle`. Each signal writes a line `gap` with the stamp of `now`, also when it
47
63
  * changes nothing. The channel and its socket stay open in the gap.
64
+ *
65
+ * With a `channel`, SIGHUP also stops the calls and closes the channel (`close`). Then the next
66
+ * SIGUSR2 opens a new channel before it starts the calls (`open`, a line `gap` with `open: true`).
48
67
  */
49
- export const gapOf = (on: (signal: "SIGUSR1" | "SIGUSR2", listener: () => void) => unknown, say: Say, now: () => number): Gap => {
50
- const gap = { stopped: false };
68
+ export const gapOf = (on: (signal: GapSignal, listener: () => void) => unknown, say: Say, now: () => number, channel?: Channel): Gap => {
69
+ const gap = { stopped: false, closed: false };
51
70
  on("SIGUSR1", () => {
52
71
  gap.stopped = true;
53
72
  say({ ev: "gap", what: "stop", t: now() });
54
73
  });
55
74
  on("SIGUSR2", () => {
75
+ const open = gap.closed && channel !== undefined;
76
+ if (open) {
77
+ channel.open();
78
+ gap.closed = false;
79
+ }
56
80
  gap.stopped = false;
57
- say({ ev: "gap", what: "start", t: now() });
81
+ say({ ev: "gap", what: "start", t: now(), ...(open ? { open: true } : {}) });
58
82
  });
83
+ if (channel !== undefined)
84
+ on("SIGHUP", () => {
85
+ gap.stopped = true;
86
+ if (!gap.closed) channel.close();
87
+ gap.closed = true;
88
+ say({ ev: "gap", what: "close", t: now() });
89
+ });
59
90
  return gap;
60
91
  };