@mark1russell7/kind 0.11.8 → 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/build/index.d.ts +15 -3
- package/build/index.js +69 -9
- package/build/index.js.map +1 -1
- package/build/{judge-DnB9wEwC.d.ts → judge-BBHSeu4i.d.ts} +57 -4
- package/build/{judge-Cs91dOGO.js → judge-ZoLw3AXV.js} +146 -16
- package/build/judge-ZoLw3AXV.js.map +1 -0
- package/build/judge.d.ts +2 -2
- package/build/judge.js +1 -1
- package/package.json +2 -2
- package/build/judge-Cs91dOGO.js.map +0 -1
|
@@ -198,6 +198,24 @@ interface Trigger<A = Quantity> {
|
|
|
198
198
|
* not ready (S2, a failed prober).
|
|
199
199
|
*/
|
|
200
200
|
readonly serves?: boolean | undefined;
|
|
201
|
+
/**
|
|
202
|
+
* These flaps are the faults that come again after the first one, in their sequence. Each flap
|
|
203
|
+
* starts `rest` after the heal before it, with the steps of the inject, and its heal comes `hold`
|
|
204
|
+
* after its own fault. The first fault keeps the hold of the trigger. The steps of the prepare come
|
|
205
|
+
* one time, before the first fault. Only a trigger with a hold and a heal can flap. Without flaps,
|
|
206
|
+
* the fault comes one time.
|
|
207
|
+
*/
|
|
208
|
+
readonly flaps?: readonly Flap[] | undefined;
|
|
209
|
+
}
|
|
210
|
+
/** A flap is one more fault of a trigger: the rest from the heal before it, and its hold. */
|
|
211
|
+
interface Flap {
|
|
212
|
+
readonly rest: Quantity;
|
|
213
|
+
readonly hold: Quantity;
|
|
214
|
+
}
|
|
215
|
+
/** A cycle is the stamps of one fault of a run and of its heal. */
|
|
216
|
+
interface Cycle {
|
|
217
|
+
readonly fault: number;
|
|
218
|
+
readonly heal: number;
|
|
201
219
|
}
|
|
202
220
|
/** A kubelet probe is the period, the failure threshold and the timeout of one probe of the pod. */
|
|
203
221
|
interface KubeletProbe {
|
|
@@ -303,7 +321,9 @@ interface Band<A = Quantity> {
|
|
|
303
321
|
* - `intake`: the stop of the intake of the target pod, as its service records it.
|
|
304
322
|
* - `fault`: the commit of the fault, as the injector or the agent stamps it.
|
|
305
323
|
* - `resume`: the end of a pause of kube-proxy, as the agent stamps it.
|
|
306
|
-
* - `heal`: the start of the first act that ends the fault, for example the delete of a policy.
|
|
324
|
+
* - `heal`: the start of the first act that ends the fault, for example the delete of a policy. With
|
|
325
|
+
* flaps (`Trigger.flaps`), it is the heal of the last fault. A window or a removal with `flaps`
|
|
326
|
+
* takes the fault and the heal of each cycle instead.
|
|
307
327
|
* - `restart`: the first deletion timestamp of a ztunnel pod in the run, as the watcher receives it.
|
|
308
328
|
*/
|
|
309
329
|
type Reference = "deletion" | "intake" | "fault" | "resume" | "heal" | "restart";
|
|
@@ -327,7 +347,9 @@ interface ResetReuse<A = Quantity> {
|
|
|
327
347
|
* With `each`, each replica of the run that gets a deletion has a window of its own, from its own
|
|
328
348
|
* references. A request is late only when it is outside each of these windows.
|
|
329
349
|
* - `removal`: the Service has no ready endpoint, and the rules of each node have no backend, in the band.
|
|
330
|
-
* With `window`, the failed requests also start in that window.
|
|
350
|
+
* With `window`, the failed requests also start in that window. With `flaps`, the band is after
|
|
351
|
+
* the fault of each cycle. At each later fault, the Service must have a ready endpoint, and
|
|
352
|
+
* the rules of each node a backend. Thus the Service came back after the heal before it.
|
|
331
353
|
* - `leaves`: the target replica leaves the EndpointSlice and the rules of each node in the band.
|
|
332
354
|
* With `window`, the failed requests also start in that window.
|
|
333
355
|
* - `restarts`: each replica receives SIGTERM in the band, or no replica does.
|
|
@@ -349,6 +371,7 @@ type Prediction<A = Quantity> = {
|
|
|
349
371
|
readonly kind: "removal";
|
|
350
372
|
readonly band: Band<A>;
|
|
351
373
|
readonly window?: WindowPrediction<A> | undefined;
|
|
374
|
+
readonly flaps?: boolean | undefined;
|
|
352
375
|
readonly why: string;
|
|
353
376
|
} | {
|
|
354
377
|
readonly kind: "leaves";
|
|
@@ -425,6 +448,13 @@ interface WindowPrediction<A = Quantity> {
|
|
|
425
448
|
* earlier drop only closes the window earlier.
|
|
426
449
|
*/
|
|
427
450
|
readonly propagation?: Band<A> | undefined;
|
|
451
|
+
/**
|
|
452
|
+
* This flag gives a window to each cycle of the run (`KindRun.acts.cycles`): each fault and its
|
|
453
|
+
* heal. The references `fault` and `heal` are then those of that cycle. The window has no
|
|
454
|
+
* other reference. A failure in a rest between two windows fails the run. With `certain`, each
|
|
455
|
+
* window has a failed request, and its last one comes near the end of that window.
|
|
456
|
+
*/
|
|
457
|
+
readonly flaps?: boolean | undefined;
|
|
428
458
|
/** These outcomes are the only failures that the window permits. A failure with another outcome fails the run. Without them, each outcome but `ok` can fail. */
|
|
429
459
|
readonly outcomes?: readonly string[] | undefined;
|
|
430
460
|
readonly why: string;
|
|
@@ -456,6 +486,8 @@ interface KindRun {
|
|
|
456
486
|
readonly fault: number | null;
|
|
457
487
|
readonly heal: number | null;
|
|
458
488
|
readonly resume: number | null;
|
|
489
|
+
/** These cycles are each fault and its heal, the first one too, for a trigger with flaps. A run without flaps has none. */
|
|
490
|
+
readonly cycles?: readonly Cycle[] | undefined;
|
|
459
491
|
};
|
|
460
492
|
/** This replica is the target of the trigger, or null for a fault on Redis. */
|
|
461
493
|
readonly target: Replica | null;
|
|
@@ -730,6 +762,13 @@ declare const dropOf: (lines: readonly Line[], node: string, backend: string, af
|
|
|
730
762
|
declare const emptyOf: (lines: readonly Line[], node: string, after: number) => RulesLine | null;
|
|
731
763
|
/** This function gives when an EndpointSlice of the Service first had no ready endpoint, after a time. */
|
|
732
764
|
declare const sliceEmptyOf: (lines: readonly Line[], svc: string, after: number) => number | null;
|
|
765
|
+
/** This function gives when an EndpointSlice of the Service first had a ready endpoint, after a time. */
|
|
766
|
+
declare const sliceReadyOf: (lines: readonly Line[], svc: string, after: number) => number | null;
|
|
767
|
+
/**
|
|
768
|
+
* This function tells if the latest EndpointSlice of the Service at a time had a ready endpoint. A
|
|
769
|
+
* slice line comes only at a change, thus with no line before the time, it gives null.
|
|
770
|
+
*/
|
|
771
|
+
declare const sliceReadyAt: (lines: readonly Line[], svc: string, at: number) => boolean | null;
|
|
733
772
|
/** This function gives when the endpoint of an address was first not ready in the EndpointSlice of the Service, after a time. */
|
|
734
773
|
declare const sliceDropOf: (lines: readonly Line[], svc: string, ip: string, after: number) => number | null;
|
|
735
774
|
/** This function gives the latest state of each pod at a time, by its name. A deleted pod is not in it. */
|
|
@@ -798,6 +837,15 @@ interface RunFacts<A = Quantity> {
|
|
|
798
837
|
readonly healUpdates?: Readonly<Record<string, Propagated<A> | null>> | undefined;
|
|
799
838
|
/** These are the first close of the socket of each client after the reference (`ends`), by client, or null for none. */
|
|
800
839
|
readonly closes?: Readonly<Record<string, A | null>> | undefined;
|
|
840
|
+
/**
|
|
841
|
+
* These are the times of each cycle of a removal with flaps, in the sequence of the cycles. `empty` is
|
|
842
|
+
* when the EndpointSlice had no ready endpoint, after the fault of the cycle. `ready` is when it had a
|
|
843
|
+
* ready endpoint again, after the heal of the cycle. A time that the run does not have is null.
|
|
844
|
+
*/
|
|
845
|
+
readonly cycles?: ReadonlyArray<{
|
|
846
|
+
readonly empty: A | null;
|
|
847
|
+
readonly ready: A | null;
|
|
848
|
+
}> | undefined;
|
|
801
849
|
}
|
|
802
850
|
/** A deleted replica is a replica of a run that got a deletion. It gives its pod, and the stamps of its intake (or null) and of its deletion. */
|
|
803
851
|
interface Deleted {
|
|
@@ -856,6 +904,11 @@ declare const reusesReset: (request: Request, requests: readonly Request[], reus
|
|
|
856
904
|
* The references `deletion` and `intake` are those of the target, or of the replica `pod`.
|
|
857
905
|
*/
|
|
858
906
|
declare const referenceOf: (run: Pick<KindRun, "start" | "acts" | "target" | "lines">, from: Reference, pod?: string | undefined) => number | null;
|
|
907
|
+
/**
|
|
908
|
+
* This function gives the cycles of a run: each fault and its heal (`KindRun.acts.cycles`). A run
|
|
909
|
+
* without cycles gives its one fault and heal, or none when it does not have both stamps.
|
|
910
|
+
*/
|
|
911
|
+
declare const cyclesOf: (run: Pick<KindRun, "acts">) => readonly Cycle[];
|
|
859
912
|
/**
|
|
860
913
|
* This function gives the source of the propagation of a cell. In a sidecar mesh, the client sidecar
|
|
861
914
|
* picks the backend of each call from its endpoints (EDS), thus the source is `eds`. Each other cell
|
|
@@ -1067,5 +1120,5 @@ declare const quantileOf: (sorted: readonly number[], p: number) => number;
|
|
|
1067
1120
|
*/
|
|
1068
1121
|
declare const medianInterval: (sorted: readonly number[]) => [number, number];
|
|
1069
1122
|
//#endregion
|
|
1070
|
-
export {
|
|
1071
|
-
//# sourceMappingURL=judge-
|
|
1123
|
+
export { unitOf as $, secondsOf as $t, TriggerV1 as A, cidrOverlap as An, restartsAt as At, RunCheck as B, stoppingOf as Bt, KindCellResult as C, Workload as Cn, kernelCounts as Ct, PredictionV1 as D, KubeProxyMode as Dn, recordsOf as Dt, LoadSample as E, KindScenario as En, podsOf as Et, KIND_NOISE as F, nodeNames as Fn, sliceEmptyOf as Ft, factsOf as G, CLIENT as Gt, checkRun as H, tunnelsOf as Ht, KindDecision as I, scenario as In, sliceReadyAt as It, observationEnd as J, PORT as Jt, judgeKind as K, HEALTH_PORT as Kt, KindVerdict as L, scenarioProblems as Ln, sliceReadyOf as Lt, upgrade as M, goDuration as Mn, signalOf as Mt, upgradeCell as N, kindConfig as Nn, signalsOf as Nt, RunCheckV1 as O, NODE_IMAGES as On, requestsOf as Ot, Deleted as P, kubeProxyPatch as Pn, sliceDropOf as Pt, reusesReset as Q, seconds as Qt, LAST_SLACK as R, slicesOf as Rt, KindCellRecordV1 as S, WindowPrediction as Sn, isTunnelState as St, KindVerdictV1 as T, KindConfig as Tn, podsAt as Tt, cyclesOf as U, ztunnelPodsOf as Ut, RunFacts as V, transitionOf as Vt, deletionsOf as W, ztunnelReplaced as Wt, propagationSource as X, SERVER as Xt, propagationOf as Y, REDIS as Yt, referenceOf as Z, bandOf as Zt, ClusterManifest as _, ResetReuse as _n, deletionOf as _t, STAGES as a, Flap as an, PropagationSource as at, FORMAT_V1 as b, Trigger as bn, failuresOf as bt, readRecord as c, KubeletProbe as cn, RulesLine as ct, calibrationOf as d, MeshMode as dn, TCP_STATE as dt, showBand as en, updatesOf as et, cellsTable as f, PodSettings as fn, TunnelLine as ft, BandV1 as g, Replica as gn, byNode as gt, timeSpread as h, Reference as hn, byClients as ht, quantileOf as i, EdgeProtocol as in, PodLine as it, judge as j, cidrRange as jn, rulesOf as jt, RunFactsV1 as k, NodeImage as kn, restartOf as kt, Judged as l, Mesh as ln, SliceLine as lt, predictionText as m, RedisDeclaration as mn, backendsAt as mt, medianInterval as n, Cycle as nn, HBONE_PORT as nt, kindCellRecordSchema as o, KindCell as on, RecordLine as ot, medianText as p, Prediction as pn, ZtunnelPodLine as pt, observationAfter as q, LOG_LABEL as qt, middle as r, Declaration as rn, Line as rt, kindCellRecordV1Schema as s, KindRun as sn, Request as st, RELATIVE as t, Band as tn, Endpoint as tt, TimeSpread as u, MeshFlavor as un, SocketLine as ut, EnvironmentManifest as v, Stack as vn, dropOf as vt, KindCellV1 as w, KUBE_PROXY_MODES as wn, outcomesOf as wt, KindCellRecord as x, TriggerKind as xn, intakeOf as xt, FORMAT as y, Template as yn, emptyOf as yt, Propagated as z, socketsOf as zt };
|
|
1124
|
+
//# sourceMappingURL=judge-BBHSeu4i.d.ts.map
|
|
@@ -121,6 +121,16 @@ const dropOf = (lines, node, backend, after, source = "rules") => rulesOf(lines,
|
|
|
121
121
|
const emptyOf = (lines, node, after) => rulesOf(lines).find((line) => line.node === node && line.t >= after && line.b.length === 0) ?? null;
|
|
122
122
|
/** This function gives when an EndpointSlice of the Service first had no ready endpoint, after a time. */
|
|
123
123
|
const sliceEmptyOf = (lines, svc, after) => slicesOf(lines, svc).find((slice) => slice.t >= after && !slice.eps.some((endpoint) => endpoint.r === true))?.t ?? null;
|
|
124
|
+
/** This function gives when an EndpointSlice of the Service first had a ready endpoint, after a time. */
|
|
125
|
+
const sliceReadyOf = (lines, svc, after) => slicesOf(lines, svc).find((slice) => slice.t >= after && slice.eps.some((endpoint) => endpoint.r === true))?.t ?? null;
|
|
126
|
+
/**
|
|
127
|
+
* This function tells if the latest EndpointSlice of the Service at a time had a ready endpoint. A
|
|
128
|
+
* slice line comes only at a change, thus with no line before the time, it gives null.
|
|
129
|
+
*/
|
|
130
|
+
const sliceReadyAt = (lines, svc, at) => {
|
|
131
|
+
const latest = slicesOf(lines, svc).filter((slice) => slice.t <= at).at(-1);
|
|
132
|
+
return latest === void 0 ? null : latest.eps.some((endpoint) => endpoint.r === true);
|
|
133
|
+
};
|
|
124
134
|
/** This function gives when the endpoint of an address was first not ready in the EndpointSlice of the Service, after a time. */
|
|
125
135
|
const sliceDropOf = (lines, svc, ip, after) => slicesOf(lines, svc).find((slice) => slice.t >= after && !slice.eps.some((endpoint) => endpoint.ip === ip && endpoint.r === true))?.t ?? null;
|
|
126
136
|
/** This function gives the latest state of each pod at a time, by its name. A deleted pod is not in it. */
|
|
@@ -264,6 +274,18 @@ const referenceOf = (run, from, pod = run.target?.pod) => {
|
|
|
264
274
|
return intakeOf(lines, pod, run.start);
|
|
265
275
|
};
|
|
266
276
|
/**
|
|
277
|
+
* This function gives the cycles of a run: each fault and its heal (`KindRun.acts.cycles`). A run
|
|
278
|
+
* without cycles gives its one fault and heal, or none when it does not have both stamps.
|
|
279
|
+
*/
|
|
280
|
+
const cyclesOf = (run) => {
|
|
281
|
+
if (run.acts.cycles !== void 0) return run.acts.cycles;
|
|
282
|
+
const { fault, heal } = run.acts;
|
|
283
|
+
return fault === null || heal === null ? [] : [{
|
|
284
|
+
fault,
|
|
285
|
+
heal
|
|
286
|
+
}];
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
267
289
|
* This function gives the source of the propagation of a cell. In a sidecar mesh, the client sidecar
|
|
268
290
|
* picks the backend of each call from its endpoints (EDS), thus the source is `eds`. Each other cell
|
|
269
291
|
* goes through the rules of kube-proxy (`rules`).
|
|
@@ -398,6 +420,74 @@ const factsOf = (cell, run) => {
|
|
|
398
420
|
} : {}
|
|
399
421
|
};
|
|
400
422
|
};
|
|
423
|
+
/**
|
|
424
|
+
* This function judges a removal with flaps. In each cycle, the Service has no ready endpoint, and the
|
|
425
|
+
* rules of each node have no backend, in the band after its fault. Each time comes before the fault of
|
|
426
|
+
* the next cycle. At the fault of each cycle after the first, the Service has a ready endpoint and the
|
|
427
|
+
* rules of each node a backend. After the last heal, the Service has a ready endpoint again. The main
|
|
428
|
+
* value is the empty Service of the first cycle.
|
|
429
|
+
*/
|
|
430
|
+
const removalOfCycles = (cell, run, prediction, done) => {
|
|
431
|
+
const lines = run.lines;
|
|
432
|
+
const cycles = cyclesOf(run);
|
|
433
|
+
if (cycles.length === 0) return done(false, "the run has no cycle of a fault and its heal", null);
|
|
434
|
+
const band = secondsOf(prediction.band);
|
|
435
|
+
const nodes = [...new Set(lines.filter((line) => line.ev === "rules").map((line) => String(line["node"])))].sort();
|
|
436
|
+
const problems = [];
|
|
437
|
+
const times = [];
|
|
438
|
+
let rules = {};
|
|
439
|
+
cycles.forEach((cycle, k) => {
|
|
440
|
+
const n = k + 1;
|
|
441
|
+
const next = cycles[k + 1]?.fault ?? run.end;
|
|
442
|
+
if (k > 0) {
|
|
443
|
+
if (sliceReadyAt(lines, "lab", cycle.fault) !== true) problems.push(`the Service had no ready endpoint at the fault of the cycle ${n}`);
|
|
444
|
+
const backends = backendsAt(lines, cycle.fault);
|
|
445
|
+
for (const node of nodes) if ((backends.get(node) ?? []).length === 0) problems.push(`the rules of ${node} had no backend at the fault of the cycle ${n}`);
|
|
446
|
+
}
|
|
447
|
+
const empty = sliceEmptyOf(lines, "lab", cycle.fault);
|
|
448
|
+
const value = empty === null || empty > next ? null : sec(empty - cycle.fault);
|
|
449
|
+
if (value === null) problems.push(`the Service kept a ready endpoint in the cycle ${n}`);
|
|
450
|
+
else if (!inBand(value, band)) problems.push(`the EndpointSlice had no ready endpoint at ${value} s in the cycle ${n}`);
|
|
451
|
+
const own = {};
|
|
452
|
+
for (const node of nodes) {
|
|
453
|
+
const drop = emptyOf(lines, node, cycle.fault);
|
|
454
|
+
const at = drop === null || drop.t > next ? null : sec(drop.t - cycle.fault);
|
|
455
|
+
own[node] = at === null ? null : seconds(at);
|
|
456
|
+
if (at === null) problems.push(`the rules of ${node} kept a backend in the cycle ${n}`);
|
|
457
|
+
else if (!inBand(at, {
|
|
458
|
+
lo: band.lo,
|
|
459
|
+
hi: band.hi + .05
|
|
460
|
+
})) problems.push(`the rules of ${node} had no backend at ${at} s in the cycle ${n}`);
|
|
461
|
+
}
|
|
462
|
+
if (k === 0) rules = own;
|
|
463
|
+
const ready = sliceReadyOf(lines, "lab", cycle.heal);
|
|
464
|
+
const back = ready === null || ready > next ? null : sec(ready - cycle.heal);
|
|
465
|
+
if (k === cycles.length - 1 && back === null) problems.push("the Service had no ready endpoint after the last heal");
|
|
466
|
+
times.push({
|
|
467
|
+
empty: value === null ? null : seconds(value),
|
|
468
|
+
ready: back === null ? null : seconds(back)
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
const first = times[0].empty === null ? null : valueIn(times[0].empty, "s");
|
|
472
|
+
const inner = prediction.window === void 0 ? null : checkRun({
|
|
473
|
+
...cell,
|
|
474
|
+
prediction: prediction.window
|
|
475
|
+
}, run);
|
|
476
|
+
if (inner !== null && !inner.ok) problems.push(inner.why);
|
|
477
|
+
const empties = times.map((each) => each.empty === null ? "none" : `${valueIn(each.empty, "s")} s`).join(", ");
|
|
478
|
+
const agrees = `the Service was empty in each of ${cycles.length} cycles, at ${empties} after its fault${inner === null ? "" : `; ${inner.why}`}`;
|
|
479
|
+
const more = {
|
|
480
|
+
...inner === null ? {} : inner.facts,
|
|
481
|
+
...first === null ? {} : { sliceEmpty: seconds(first) },
|
|
482
|
+
rules,
|
|
483
|
+
cycles: times
|
|
484
|
+
};
|
|
485
|
+
const checked = done(problems.length === 0, problems.length === 0 ? agrees : problems.join("; "), first, more);
|
|
486
|
+
return inner?.tail === void 0 ? checked : {
|
|
487
|
+
...checked,
|
|
488
|
+
tail: inner.tail
|
|
489
|
+
};
|
|
490
|
+
};
|
|
401
491
|
/** This function judges one run against the prediction of its cell. */
|
|
402
492
|
const checkRun = (cell, run) => {
|
|
403
493
|
const lines = run.lines;
|
|
@@ -434,10 +524,23 @@ const checkRun = (cell, run) => {
|
|
|
434
524
|
};
|
|
435
525
|
const pods = each ? (facts.deletions ?? []).map((deleted) => deleted.pod) : [run.target?.pod];
|
|
436
526
|
if (pods.length === 0) return done(false, "no replica of the run got a deletion", null);
|
|
527
|
+
const flaps = prediction.flaps === true;
|
|
528
|
+
const cyclic = (from) => from === "fault" || from === "heal";
|
|
529
|
+
if (flaps && (each || !cyclic(prediction.start.from) || prediction.end !== null && !cyclic(prediction.end.from))) return done(false, "a window with flaps has no each, and its references are the fault and the heal", null);
|
|
530
|
+
const cycles = flaps ? cyclesOf(run) : [];
|
|
531
|
+
if (flaps && cycles.length === 0) return done(false, "the run has no cycle of a fault and its heal", null);
|
|
532
|
+
const sources = flaps ? cycles.map((cycle) => ({
|
|
533
|
+
pod: run.target?.pod,
|
|
534
|
+
cycle
|
|
535
|
+
})) : pods.map((pod) => ({
|
|
536
|
+
pod,
|
|
537
|
+
cycle: void 0
|
|
538
|
+
}));
|
|
437
539
|
const windows = [];
|
|
438
|
-
for (const pod of
|
|
439
|
-
const
|
|
440
|
-
const
|
|
540
|
+
for (const { pod, cycle } of sources) {
|
|
541
|
+
const at = (from) => cycle !== void 0 && cyclic(from) ? cycle[from] : referenceOf(run, from, pod);
|
|
542
|
+
const start = at(prediction.start.from);
|
|
543
|
+
const end = prediction.end === null ? null : at(prediction.end.from);
|
|
441
544
|
if (start === null || prediction.end !== null && end === null) return done(false, `the run has no ${start === null ? prediction.start.from : prediction.end.from} reference${each ? ` for ${pod}` : ""}`, null);
|
|
442
545
|
const origin = each ? referenceOf(run, "deletion", pod) : facts.deletion ?? (cell.trigger.kind === "crash" ? run.acts.fault : null);
|
|
443
546
|
const ip = run.replicas.find((replica) => replica.pod === pod)?.ip;
|
|
@@ -504,19 +607,27 @@ const checkRun = (cell, run) => {
|
|
|
504
607
|
const wrong = allowed === void 0 ? [] : relevant.filter((request) => !late.includes(request) && !allowed.includes(request.o));
|
|
505
608
|
const bounds = (window) => `[${sec(window.lo - window.start)}, ${sec(window.hi - window.start)}] s`;
|
|
506
609
|
const problems = [];
|
|
507
|
-
if (outside.length > 0) problems.push(each ? `${outside.length} failed requests outside the window of each deleted replica: ${windows.map((window) => `${window.pod} ${bounds(window)}`).join(", ")} after its ${prediction.start.from}` : `${outside.length} failed requests outside ${bounds(windows[0])} after ${prediction.start.from}`);
|
|
610
|
+
if (outside.length > 0) problems.push(each ? `${outside.length} failed requests outside the window of each deleted replica: ${windows.map((window) => `${window.pod} ${bounds(window)}`).join(", ")} after its ${prediction.start.from}` : flaps ? `${outside.length} failed requests outside the window of each cycle: ${windows.map((window, k) => `${k + 1} ${bounds(window)}`).join(", ")} after its ${prediction.start.from}` : `${outside.length} failed requests outside ${bounds(windows[0])} after ${prediction.start.from}`);
|
|
508
611
|
if (others.length > 0) problems.push(`${others.length} failed requests on nodes whose kube-proxy did not pause`);
|
|
509
612
|
if (wrong.length > 0) problems.push(`${wrong.length} failed requests in the window ended ${JSON.stringify(outcomesOf(wrong))}, not ${allowed.join(", ")}`);
|
|
510
613
|
if (prediction.certain && relevant.length === 0) problems.push("no request failed in a window that the law opens");
|
|
511
614
|
const final = windows.reduce((a, b) => (b.end ?? Number.NEGATIVE_INFINITY) > (a.end ?? Number.NEGATIVE_INFINITY) ? b : a);
|
|
512
|
-
if (
|
|
615
|
+
if (flaps && prediction.certain && relevant.length > 0) windows.forEach((window, k) => {
|
|
616
|
+
const own = relevant.filter((request) => request.t >= window.lo && request.t <= window.hi);
|
|
617
|
+
if (own.length === 0) problems.push(`no request failed in the window of the cycle ${k + 1}`);
|
|
618
|
+
else if (window.end !== null) {
|
|
619
|
+
const earliest = window.end + closes.lo * 1e3 - NOISE - SLACK;
|
|
620
|
+
if (own.at(-1).t < earliest) problems.push(`the last failed request of the cycle ${k + 1} started ${sec(earliest - own.at(-1).t)} s before its window can end`);
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
if (!flaps && prediction.certain && final.end !== null && relevant.length > 0) {
|
|
513
624
|
const last = relevant.at(-1).t;
|
|
514
625
|
const earliest = final.end + closes.lo * 1e3 - NOISE - SLACK;
|
|
515
626
|
if (last < earliest) problems.push(`the last failed request started ${sec(earliest - last)} s before the window can end`);
|
|
516
627
|
}
|
|
517
628
|
const inside = relevant.filter((request) => !late.includes(request));
|
|
518
629
|
const span = inside.length === 0 ? 0 : sec(inside.at(-1).t - inside[0].t);
|
|
519
|
-
const where = each ? `the windows of ${windows.length} deleted replicas` : "the window";
|
|
630
|
+
const where = each ? `the windows of ${windows.length} deleted replicas` : flaps ? `the windows of ${windows.length} cycles` : "the window";
|
|
520
631
|
const agrees = reused.length === 0 ? `${relevant.length} failed requests, all in ${where} (span ${span} s)` : `${inside.length} failed requests in ${where} (span ${span} s), and ${reused.length} on a port that a reset closed`;
|
|
521
632
|
const checked = done(problems.length === 0, problems.length === 0 ? agrees : problems.join("; "), relevant.length);
|
|
522
633
|
if (problems.length > 0) return checked;
|
|
@@ -546,6 +657,7 @@ const checkRun = (cell, run) => {
|
|
|
546
657
|
case "removal": {
|
|
547
658
|
const fault = run.acts.fault;
|
|
548
659
|
if (fault === null) return done(false, "the run has no fault stamp", null);
|
|
660
|
+
if (prediction.flaps === true) return removalOfCycles(cell, run, prediction, done);
|
|
549
661
|
const empty = sliceEmptyOf(lines, "lab", fault);
|
|
550
662
|
if (empty === null) return done(false, "the Service kept a ready endpoint", null);
|
|
551
663
|
const value = sec(empty - fault);
|
|
@@ -992,7 +1104,7 @@ const upgradeFacts = (facts) => {
|
|
|
992
1104
|
by: seconds(old.by)
|
|
993
1105
|
};
|
|
994
1106
|
const byKey = (record) => Object.fromEntries(Object.entries(record).map(([key, old]) => [key, propagated(old)]));
|
|
995
|
-
const { sliceEmpty, rules, left, signals, updates, healUpdates, closes, ...rest } = facts;
|
|
1107
|
+
const { sliceEmpty, rules, left, signals, updates, healUpdates, closes, cycles, ...rest } = facts;
|
|
996
1108
|
return {
|
|
997
1109
|
...rest,
|
|
998
1110
|
intakeAfterDeletion: time$1(facts.intakeAfterDeletion),
|
|
@@ -1004,7 +1116,11 @@ const upgradeFacts = (facts) => {
|
|
|
1004
1116
|
...rules === void 0 ? {} : { rules: Object.fromEntries(Object.entries(rules).map(([node, old]) => [node, time$1(old)])) },
|
|
1005
1117
|
...left === void 0 ? {} : { left: seconds(left) },
|
|
1006
1118
|
...signals === void 0 ? {} : { signals: signals.map((old) => time$1(old)) },
|
|
1007
|
-
...closes === void 0 ? {} : { closes: Object.fromEntries(Object.entries(closes).map(([client, old]) => [client, time$1(old)])) }
|
|
1119
|
+
...closes === void 0 ? {} : { closes: Object.fromEntries(Object.entries(closes).map(([client, old]) => [client, time$1(old)])) },
|
|
1120
|
+
...cycles === void 0 ? {} : { cycles: cycles.map((old) => ({
|
|
1121
|
+
empty: time$1(old.empty),
|
|
1122
|
+
ready: time$1(old.ready)
|
|
1123
|
+
})) }
|
|
1008
1124
|
};
|
|
1009
1125
|
};
|
|
1010
1126
|
/**
|
|
@@ -1074,8 +1190,8 @@ const atBand = (from, band) => {
|
|
|
1074
1190
|
const predictionText = (prediction) => {
|
|
1075
1191
|
switch (prediction.kind) {
|
|
1076
1192
|
case "none": return prediction.outcomes === void 0 ? "no failure" : `no ${prediction.outcomes.join(", ")}`;
|
|
1077
|
-
case "window": return `failures only from ${atBand(prediction.start.from, prediction.start.band)}${prediction.end === null ? "" : ` to ${atBand(prediction.end.from, prediction.end.band)}`}${prediction.each === true ? ", of each deleted replica" : ""}${prediction.nodes === "paused" ? ", on the paused node" : ""}${prediction.certain ? ", at least one" : ""}${prediction.outcomes === void 0 ? "" : `, each one ${prediction.outcomes.join(" or ")}`}${prediction.afterReset === void 0 ? "" : `, or a ${prediction.afterReset.outcome} up to ${format(prediction.afterReset.life)} after a reset on its port`}${prediction.propagation === void 0 ? "" : `, the rules drop the replica in ${showBand(prediction.propagation)}`}`;
|
|
1078
|
-
case "removal": return `Service empty in ${showBand(prediction.band)}${prediction.window === void 0 ? "" : `; ${predictionText(prediction.window)}`}`;
|
|
1193
|
+
case "window": return `failures only from ${atBand(prediction.start.from, prediction.start.band)}${prediction.end === null ? "" : ` to ${atBand(prediction.end.from, prediction.end.band)}`}${prediction.each === true ? ", of each deleted replica" : ""}${prediction.flaps === true ? ", in each cycle" : ""}${prediction.nodes === "paused" ? ", on the paused node" : ""}${prediction.certain ? ", at least one" : ""}${prediction.outcomes === void 0 ? "" : `, each one ${prediction.outcomes.join(" or ")}`}${prediction.afterReset === void 0 ? "" : `, or a ${prediction.afterReset.outcome} up to ${format(prediction.afterReset.life)} after a reset on its port`}${prediction.propagation === void 0 ? "" : `, the rules drop the replica in ${showBand(prediction.propagation)}`}`;
|
|
1194
|
+
case "removal": return `Service empty in ${showBand(prediction.band)}${prediction.flaps === true ? " after each fault, and ready again before the next" : ""}${prediction.window === void 0 ? "" : `; ${predictionText(prediction.window)}`}`;
|
|
1079
1195
|
case "leaves": return `the replica leaves in ${showBand(prediction.band)}${prediction.window === void 0 ? "" : `; ${predictionText(prediction.window)}`}`;
|
|
1080
1196
|
case "restarts": return prediction.band === null ? "no restart" : `each replica SIGTERM in ${showBand(prediction.band)}`;
|
|
1081
1197
|
case "stays": return "stays ready, no refusal";
|
|
@@ -1107,7 +1223,7 @@ const cellsTable = (judged) => [
|
|
|
1107
1223
|
...judged.map((each) => {
|
|
1108
1224
|
const { cell, verdict } = each;
|
|
1109
1225
|
const runs = verdict.voided === 0 ? `${verdict.counted}` : `${verdict.counted} (+${verdict.voided} void)`;
|
|
1110
|
-
return `| ${cell.name} | ${cell.id} | ${cell.mode} | ${cell.trigger.kind} | ${predictionText(cell.prediction)} | ${verdict.decision} | ${runs} | ${medianText(each)} |`;
|
|
1226
|
+
return `| ${cell.name} | ${cell.id} | ${cell.mode} | ${cell.trigger.kind}${cell.trigger.flaps === void 0 || cell.trigger.flaps.length === 0 ? "" : ` (${cell.trigger.flaps.length + 1} faults)`} | ${predictionText(cell.prediction)} | ${verdict.decision} | ${runs} | ${medianText(each)} |`;
|
|
1111
1227
|
})
|
|
1112
1228
|
].join("\n");
|
|
1113
1229
|
/**
|
|
@@ -1286,7 +1402,11 @@ const run = z.object({
|
|
|
1286
1402
|
acts: z.object({
|
|
1287
1403
|
fault: z.number().nullable(),
|
|
1288
1404
|
heal: z.number().nullable(),
|
|
1289
|
-
resume: z.number().nullable()
|
|
1405
|
+
resume: z.number().nullable(),
|
|
1406
|
+
cycles: z.array(z.object({
|
|
1407
|
+
fault: z.number(),
|
|
1408
|
+
heal: z.number()
|
|
1409
|
+
})).optional()
|
|
1290
1410
|
}),
|
|
1291
1411
|
target: replica.nullable(),
|
|
1292
1412
|
paused: z.string().nullable(),
|
|
@@ -1360,6 +1480,7 @@ const recordOf = (a, main) => {
|
|
|
1360
1480
|
}).optional(),
|
|
1361
1481
|
each: z.boolean().optional(),
|
|
1362
1482
|
propagation: band.optional(),
|
|
1483
|
+
flaps: z.boolean().optional(),
|
|
1363
1484
|
outcomes: z.array(z.string()).optional(),
|
|
1364
1485
|
why: z.string()
|
|
1365
1486
|
});
|
|
@@ -1374,6 +1495,7 @@ const recordOf = (a, main) => {
|
|
|
1374
1495
|
kind: z.literal("removal"),
|
|
1375
1496
|
band,
|
|
1376
1497
|
window: window.optional(),
|
|
1498
|
+
flaps: z.boolean().optional(),
|
|
1377
1499
|
why: z.string()
|
|
1378
1500
|
}),
|
|
1379
1501
|
z.object({
|
|
@@ -1440,7 +1562,11 @@ const recordOf = (a, main) => {
|
|
|
1440
1562
|
quiet: z.boolean().optional(),
|
|
1441
1563
|
close: z.boolean().optional(),
|
|
1442
1564
|
partition: z.boolean().optional(),
|
|
1443
|
-
serves: z.boolean().optional()
|
|
1565
|
+
serves: z.boolean().optional(),
|
|
1566
|
+
flaps: z.array(z.object({
|
|
1567
|
+
rest: time,
|
|
1568
|
+
hold: time
|
|
1569
|
+
})).optional()
|
|
1444
1570
|
}),
|
|
1445
1571
|
pod: z.object({
|
|
1446
1572
|
replicas: count,
|
|
@@ -1494,7 +1620,11 @@ const recordOf = (a, main) => {
|
|
|
1494
1620
|
after: a.nullable(),
|
|
1495
1621
|
by: a
|
|
1496
1622
|
}).nullable()).optional(),
|
|
1497
|
-
closes: z.record(z.string(), a.nullable()).optional()
|
|
1623
|
+
closes: z.record(z.string(), a.nullable()).optional(),
|
|
1624
|
+
cycles: z.array(z.object({
|
|
1625
|
+
empty: a.nullable(),
|
|
1626
|
+
ready: a.nullable()
|
|
1627
|
+
})).optional()
|
|
1498
1628
|
});
|
|
1499
1629
|
return {
|
|
1500
1630
|
cell,
|
|
@@ -1556,6 +1686,6 @@ const readRecord = (data) => {
|
|
|
1556
1686
|
throw new Error(`a kind cell record has the format ${FORMAT} or ${FORMAT_V1}, not ${String(format)}`);
|
|
1557
1687
|
};
|
|
1558
1688
|
//#endregion
|
|
1559
|
-
export {
|
|
1689
|
+
export { rulesOf as $, RELATIVE as A, dropOf as B, observationEnd as C, reusesReset as D, referenceOf as E, TCP_STATE as F, kernelCounts as G, failuresOf as H, backendsAt as I, podsOf as J, outcomesOf as K, byClients as L, middle as M, quantileOf as N, unitOf as O, HBONE_PORT as P, restartsAt as Q, byNode as R, observationAfter as S, showBand as St, propagationSource as T, intakeOf as U, emptyOf as V, isTunnelState as W, requestsOf as X, recordsOf as Y, restartOf as Z, checkRun as _, REDIS as _t, calibrationOf as a, sliceReadyOf as at, factsOf as b, seconds as bt, predictionText as c, stoppingOf as ct, FORMAT_V1 as d, ztunnelPodsOf as dt, signalOf as et, judge as f, ztunnelReplaced as ft, LAST_SLACK as g, PORT as gt, KIND_NOISE as h, LOG_LABEL as ht, readRecord as i, sliceReadyAt as it, medianInterval as j, updatesOf as k, timeSpread as l, transitionOf as lt, upgradeCell as m, HEALTH_PORT as mt, kindCellRecordSchema as n, sliceDropOf as nt, cellsTable as o, slicesOf as ot, upgrade as p, CLIENT as pt, podsAt as q, kindCellRecordV1Schema as r, sliceEmptyOf as rt, medianText as s, socketsOf as st, STAGES as t, signalsOf as tt, FORMAT as u, tunnelsOf as ut, cyclesOf as v, SERVER as vt, propagationOf as w, judgeKind as x, secondsOf as xt, deletionsOf as y, bandOf as yt, deletionOf as z };
|
|
1560
1690
|
|
|
1561
|
-
//# sourceMappingURL=judge-
|
|
1691
|
+
//# sourceMappingURL=judge-ZoLw3AXV.js.map
|