@mark1russell7/kind 0.13.4 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -105,6 +105,12 @@ Each pod of a cluster shares the kernel of the Docker VM. Thus each stamp of the
105
105
 
106
106
  Before each run, the lab deletes the conntrack entries in SYN_SENT to the ClusterIP on each node (`forgetHandshakes`). A note of the run gives each count. Such an entry is a SYN with no answer, and it keeps its NAT for 120 s. A new SYN from the same port goes where the first one went, and it gets no answer either. The clients send requests between the runs too. Thus the restore of one run left such entries, and a client that took the port again in the next run timed out (T07, 2026-10-17).
107
107
 
108
+ Before each run, the lab waits for a quiet VM (`quiet.ts`). It waits for no lab container of another agent, and for an idle share. The clients of the cell run at that time, and the agent of each node reads the conntrack table each second. Thus the idle share has the load of the cell in it, and a flat share voided a heavy cell with no other load (SYS-95). The gate reads the cell against itself:
109
+
110
+ - **The baseline.** Before the first run of an environment, the lab waits until the cell is 120 s old, and it takes 3 samples. A baseline sample has no field `needed`. The baseline is the median of those samples, and the highest median of the environments holds, because a foreign load only lowers it.
111
+ - **The share.** A run needs the baseline less the margin, between the floor and the flat share of 60%. Thus a cell at rest at 60% or more keeps its gate. A warm-up with no baseline needs only the floor. Each sample of the gate keeps its share in the field `needed`.
112
+ - **The test of the gate.** A cell with `plannedLoad` starts a process in the loader after the baseline, and the process keeps that count of CPUs busy. The gate must refuse that run, and thus the run is void.
113
+
108
114
  ## The stacks
109
115
 
110
116
  From 0.2.0, a cell names its stack (`stack`): its server and its clients. A cell without a stack has the service.
package/build/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as unitOf, $n as cidrRange, $t as OUTSIDE, A as TriggerV1, An as Reference, At as recordsOf, B as RunCheck, Bn as Workload, Bt as sliceReadyAt, C as KindCellResult, Cn as MeshFlavor, Ct as intakeOf, D as PredictionV1, Dn as PodSettings, Dt as periodOf, E as LoadSample, En as PermittedWindow, Et as outcomesOf, F as KIND_NOISE, Fn as Trigger, Ft as sessionsAt, G as factsOf, Gn as KindScenario, Gt as transitionOf, H as checkRun, Hn as Dataplane, Ht as slicesOf, I as KindDecision, In as TriggerKind, It as signalOf, J as observationEnd, Jn as NodeImage, Jt as ztunnelReplaced, K as judgeKind, Kn as KubeProxyMode, Kt as tunnelsOf, L as KindVerdict, Ln as VipAnnouncerName, Lt as signalsOf, M as upgrade, Mn as ResetReuse, Mt as restartOf, N as upgradeCell, Nn as Stack, Nt as restartsAt, O as RunCheckV1, On as Prediction, Ot as podsAt, P as Deleted, Pn as Template, Pt as rulesOf, Q as reusesReset, Qn as cidrOverlap, Qt as LOG_LABEL, R as LAST_SLACK, Rn as VipSettings, Rt as sliceDropOf, S as KindCellRecordV1, Sn as Mesh, St as failuresOf, T as KindVerdictV1, Tn as NonePrediction, Tt as kernelCounts, U as cyclesOf, Un as KUBE_PROXY_MODES, Ut as socketsOf, V as RunFacts, Vn as DATAPLANES, Vt as sliceReadyOf, W as deletionsOf, Wn as KindConfig, Wt as stoppingOf, X as propagationSource, Xn as ZONE_LABEL, Xt as HEALTH_PORT, Y as propagationOf, Yn as ZONE_KEPT, Yt as CLIENT, Z as referenceOf, Zn as ZONE_LOST, Zt as LB_CLASS, _ as ClusterManifest, _n as EdgeProtocol, _t as byClients, a as STAGES, an as backlogEnd, ar as scenario, at as PodLine, b as FORMAT_V1, bn as KindRun, bt as dropOf, c as readRecord, cn as overshootBand, ct as Request, d as calibrationOf, dn as secondsOf, dt as SliceLine, en as PORT, er as goDuration, et as updatesOf, f as cellsTable, fn as showBand, ft as SocketLine, g as BandV1, gn as Declaration, gt as backendsAt, h as timeSpread, hn as Cycle, ht as ZtunnelPodLine, i as quantileOf, in as CountBand, ir as nodeNames, it as Period, j as judge, jn as Replica, jt as requestsOf, k as RunFactsV1, kn as RedisDeclaration, kt as podsOf, l as Judged, ln as requestBand, lt as RulesLine, m as predictionText, mn as Band, mt as TunnelLine, n as medianInterval, nn as SERVER, nr as kindConfig, nt as HBONE_PORT, o as kindCellRecordSchema, on as bandOf, or as scenarioProblems, ot as PropagationSource, p as medianText, pn as BacklogTerm, pt as TCP_STATE, q as observationAfter, qn as NODE_IMAGES, qt as ztunnelPodsOf, r as middle, rn as VIP_ANNOTATION, rr as kubeProxyPatch, rt as Line, s as kindCellRecordV1Schema, sn as drainBand, sr as zoneOf, st as RecordLine, t as RELATIVE, tn as REDIS, tr as isKubeProxyMode, tt as Endpoint, u as TimeSpread, un as seconds, ut as Sessions, v as EnvironmentManifest, vn as Flap, vt as byNode, w as KindCellV1, wn as MeshMode, wt as isTunnelState, x as KindCellRecord, xn as KubeletProbe, xt as emptyOf, y as FORMAT, yn as KindCell, yt as deletionOf, z as Propagated, zn as WindowPrediction, zt as sliceEmptyOf } from "./judge-BZFTT0RL.js";
1
+ import { $ as factsOf, $n as DATAPLANES, $t as transitionOf, A as KindCellResult, An as KubeletProbe, At as failuresOf, B as upgradeCell, Bn as ReconnectsPrediction, Bt as restartOf, C as BandV1, Cn as Band, Ct as ZtunnelPodLine, D as FORMAT_V1, Dn as Flap, Dt as deletionOf, E as FORMAT, En as EdgeProtocol, Et as byNode, F as RunCheckV1, Fn as NonePrediction, Ft as periodOf, G as LAST_SLACK, Gn as Stack, Gt as signalsOf, H as KIND_NOISE, Hn as Reference, Ht as rulesOf, I as RunFactsV1, In as PermittedWindow, It as podsAt, J as RunCheck, Jn as TriggerKind, Jt as sliceReadyAt, K as Propagated, Kn as Template, Kt as sliceDropOf, L as TriggerV1, Ln as PodSettings, Lt as podsOf, M as KindVerdictV1, Mn as Mesh, Mt as isTunnelState, N as LoadSample, Nn as MeshFlavor, Nt as kernelCounts, O as KindCellRecord, On as KindCell, Ot as dropOf, P as PredictionV1, Pn as MeshMode, Pt as outcomesOf, Q as deletionsOf, Qn as Workload, Qt as stoppingOf, R as judge, Rn as Prediction, Rt as recordsOf, S as timeSpread, Sn as Backoff, St as TunnelLine, T as EnvironmentManifest, Tn as Declaration, Tt as byClients, U as KindDecision, Un as Replica, Ut as sessionsAt, V as Deleted, Vn as RedisDeclaration, Vt as restartsAt, W as KindVerdict, Wn as ResetReuse, Wt as signalOf, X as checkRun, Xn as VipSettings, Xt as slicesOf, Y as RunFacts, Yn as VipAnnouncerName, Yt as sliceReadyOf, Z as cyclesOf, Zn as WindowPrediction, Zt as socketsOf, _ as TimeSpread, _n as requestBand, _r as scenario, _t as RulesLine, a as judgeReconnects, an as LB_CLASS, ar as NODE_IMAGES, at as referenceOf, b as medianText, bn as showBand, bt as SocketLine, c as RELATIVE, cn as PORT, cr as ZONE_LABEL, ct as updatesOf, d as quantileOf, dn as VIP_ANNOTATION, dr as cidrRange, dt as Line, en as tunnelsOf, er as Dataplane, et as judgeKind, f as STAGES, fn as CountBand, fr as goDuration, ft as Period, g as Judged, gn as overshootBand, gr as nodeNames, gt as Request, h as readRecord, hn as drainBand, hr as kubeProxyPatch, ht as RecordLine, i as Try, in as HEALTH_PORT, ir as KubeProxyMode, it as propagationSource, j as KindCellV1, jn as MemberlistSettings, jt as intakeOf, k as KindCellRecordV1, kn as KindRun, kt as emptyOf, l as medianInterval, ln as REDIS, lr as ZONE_LOST, lt as Endpoint, m as kindCellRecordV1Schema, mn as bandOf, mr as kindConfig, mt as PropagationSource, n as Return, nn as ztunnelReplaced, nr as KindConfig, nt as observationEnd, o as latestReturn, on as LOG_LABEL, or as NodeImage, ot as reusesReset, p as kindCellRecordSchema, pn as backlogEnd, pr as isKubeProxyMode, pt as PodLine, q as ReconnectsFacts, qn as Trigger, qt as sliceEmptyOf, r as Schedule, rn as CLIENT, rr as KindScenario, rt as propagationOf, s as triesOf, sn as OUTSIDE, sr as ZONE_KEPT, st as unitOf, t as Reconnects, tn as ztunnelPodsOf, tr as KUBE_PROXY_MODES, tt as observationAfter, u as middle, un as SERVER, ur as cidrOverlap, ut as HBONE_PORT, v as calibrationOf, vn as seconds, vr as scenarioProblems, vt as Sessions, w as ClusterManifest, wn as Cycle, wt as backendsAt, x as predictionText, xn as BacklogTerm, xt as TCP_STATE, y as cellsTable, yn as secondsOf, yr as zoneOf, yt as SliceLine, z as upgrade, zn as PulsarSettings, zt as requestsOf } from "./judge-DLn2PLxb.js";
2
2
  import { Quantity } from "@mark1russell7/quantity";
3
3
  import { CheckFn, Coupling, Finding, HealthServer, Poll, Scenario, ServiceBuilder, ServiceDescription, Stage, StopWay, Timing, TransportImpl, Verdict } from "@mark1russell7/service";
4
4
  import { BuildImage } from "@mark1russell7/harness";
@@ -88,8 +88,12 @@ interface PinnedChart {
88
88
  readonly url: string;
89
89
  readonly sha256: string;
90
90
  }
91
- /** This function gives the path of a chart in the cache, after a download when the cache does not have it. It refuses a file whose sha256 is not the pinned value. */
92
- export declare const ensurePinnedChart: (chart: PinnedChart, cache?: string) => Promise<string>;
91
+ /**
92
+ * This function gives the path of a chart in the cache, after a download when the cache does not have it.
93
+ * It refuses a download whose sha256 is not the pinned value. A file in the cache with a different
94
+ * sha256 (a torn write) is downloaded again, and each write replaces the file in one step.
95
+ */
96
+ export declare const ensurePinnedChart: (chart: PinnedChart, cache?: string, get?: (url: string) => Promise<Uint8Array>) => Promise<string>;
93
97
  /**
94
98
  * This function installs a pinned chart in a cluster with the values `values`. It writes the values
95
99
  * to a file in the folder of the cluster. Helm waits for each workload of the chart, for up to 10 min.
@@ -228,6 +232,8 @@ export declare const hostsOf: (text: string, cluster: string) => ClusterHosts;
228
232
  * its network namespace only, with each client pod as a peer: the partition of one replica.
229
233
  * - `redis` is the Redis pod.
230
234
  * - `kube-proxy` is kube-proxy on the first worker.
235
+ * - `speaker` is the speaker of MetalLB on the owner of the VIP (SYS-57). Only a signal takes it.
236
+ * - `broker` is the JVM of the Pulsar broker that owns the topic of the cell (`KindCell.pulsar`).
231
237
  * - `servers` is each replica of the service. Only the injector, a middlebox and a sysctl take it.
232
238
  * Each one acts in the network namespace of each replica, with each client pod as a peer. A REJECT
233
239
  * resets only the sender of the segment that it matches, thus the rule goes where the segments of
@@ -235,7 +241,7 @@ export declare const hostsOf: (text: string, cluster: string) => ClusterHosts;
235
241
  * - `clients` is the process of each client pod. Only a signal, a sysctl and a drop of DNS take it.
236
242
  * They are the gap of `idle`, a sysctl of the namespace of each client pod, and `dns-blackhole`.
237
243
  */
238
- type Target = "server" | "redis" | "kube-proxy" | "servers" | "clients";
244
+ type Target = "server" | "redis" | "kube-proxy" | "broker" | "servers" | "clients" | "speaker";
239
245
  /** A step is one act of a fault, in the sequence of the plan. */
240
246
  type Step = {
241
247
  readonly do: "kubectl";
@@ -379,7 +385,8 @@ export declare const DNS_PORT = 53;
379
385
  export declare const dnsCommands: (on: boolean) => string[][];
380
386
  /**
381
387
  * This function gives the commands of the injector that put a stateful middlebox in its network
382
- * namespace, with each peer, on the local port `port`. With `idle` null, they remove it.
388
+ * namespace, with each peer, on the local port `port`. With `idle` null, they remove the parts of these
389
+ * peers. A run removes what the table of the namespace holds (`middleboxRemoval`).
383
390
  *
384
391
  * The middlebox forgets a flow of the port after `idle` with no segment. Then it drops each later
385
392
  * segment of it: the silent-drop form of the router of the harness. Conntrack stays as it is. The
@@ -402,6 +409,16 @@ export declare const dnsCommands: (on: boolean) => string[][];
402
409
  * old flow of the peer. A cell of the middlebox opens no new flow in its gap.
403
410
  */
404
411
  export declare const middleboxCommands: (peers: readonly string[], idle: Quantity | null, port: number) => string[][];
412
+ /**
413
+ * This function gives the commands that remove a middlebox from a network namespace, from the filter
414
+ * table that `iptables-save -c` gives for it. An empty list means that the namespace has no part of it.
415
+ *
416
+ * The commands remove each part that the table holds, and only those. The lab stops at the first
417
+ * command that fails. Thus a removal from a list of the peers stops at a jump that is not there, and
418
+ * its chains stay. For example, a set that failed half way added no jumps, or a client pod has a new
419
+ * address. Then the next set of the namespace fails at its `-N`.
420
+ */
421
+ export declare const middleboxRemoval: (saved: string) => string[][];
405
422
  /**
406
423
  * This function gives the placement of the sessions of the clients: the backend of each slot of each
407
424
  * client, by client. The clients come in the sequence of their names. The sessions on the target go
@@ -844,8 +861,8 @@ interface VipAnnouncer {
844
861
  readonly settle: Quantity;
845
862
  /** This function gives the VIP of a subnet, with `owner` its holder among `nodes`. */
846
863
  readonly vipOf: (subnet: string, owner: string, nodes: readonly string[]) => string;
847
- /** This function installs the announcer, with the local tags of its images, by the names of `images`. */
848
- readonly install: (cluster: Cluster, local: Readonly<Record<string, string>>, owner: string, nodes: readonly string[], vip: string) => Promise<void>;
864
+ /** This function installs the announcer, with the local tags of its images, by the names of `images`. MetalLB uses the settings of memberlist of `settings`. */
865
+ readonly install: (cluster: Cluster, local: Readonly<Record<string, string>>, owner: string, nodes: readonly string[], vip: string, settings?: VipSettings) => Promise<void>;
849
866
  /** This function makes `owner` the holder of the VIP before a run. It gives null, or why not. */
850
867
  readonly hold: (cluster: Cluster, owner: string, nodes: readonly string[], namespace: string, service: string) => Promise<string | null>;
851
868
  /** This function gives the state of the announcer after a run, for the line `vip`. */
@@ -875,8 +892,8 @@ export declare const macsAt: (found: ReadonlyArray<{
875
892
  * a usable speaker (speaker/layer2_controller.go, lines 83–129). A usable speaker is a member of
876
893
  * memberlist that is not dead (internal/speakerlist/speakerlist.go, lines 303–320).
877
894
  * - The speakers start memberlist from `DefaultLANConfig` (speakerlist.go, line 72). A file
878
- * /etc/metallb/memberlist/config.json replaces that config (lines 77–84). The install makes no
879
- * such file, thus each value is the default of hashicorp/memberlist v0.5.1.
895
+ * /etc/metallb/memberlist/config.json replaces that config (lines 77–84). Without settings of
896
+ * memberlist, the install makes no such file. Then each value is the default of memberlist v0.5.1.
880
897
  * - Each event of memberlist (a join, a leave or an update) calls `ForceSync` (speakerlist.go, lines
881
898
  * 339–349). `ForceSync` reconciles each Service at once, with no delay (internal/k8s/k8s.go, lines
882
899
  * 181–184, and internal/k8s/controllers/service_controller_reload.go, lines 62–118).
@@ -925,6 +942,18 @@ export declare const MEMBERLIST: {
925
942
  readonly pushPullInterval: Quantity;
926
943
  readonly gossipInterval: Quantity;
927
944
  };
945
+ /**
946
+ * These are the values of the config file of memberlist, as `DefaultLANConfig` of v0.5.1 gives them
947
+ * (config.go, lines 302–341). The speaker needs each field of the file, and it reads a time as a
948
+ * duration of Go (speakerlist.go, lines 355–432). The chart mounts the ConfigMap
949
+ * `metallb-memberlist-config` at /etc/metallb/memberlist, if the ConfigMap exists
950
+ * (charts/metallb/templates/speaker.yaml, lines 195–198 and 391–392). The key is `config.json`.
951
+ */
952
+ export declare const MEMBERLIST_FILE: Readonly<Record<string, string | number | boolean>>;
953
+ /** This function gives the config file of memberlist with `settings`. Each other value is the default of `MEMBERLIST_FILE`. */
954
+ export declare const memberlistFileOf: (settings: MemberlistSettings) => Record<string, string | number | boolean>;
955
+ /** This function gives the terms of memberlist with `settings`, or the defaults (`MEMBERLIST`) without them. */
956
+ export declare const memberlistTermsOf: (settings: MemberlistSettings | undefined) => typeof MEMBERLIST;
928
957
  /** These are the gratuitous ARPs of the owner at each reconcile: one at once, then one each `every` until `for` (announcer.go, lines 172–182). */
929
958
  export declare const METALLB_SPAM: {
930
959
  readonly every: Quantity;
@@ -980,14 +1009,151 @@ export declare const holdMetallb: (cluster: Pick<Cluster, "kubectl">, owner: str
980
1009
  * and a pool of the VIP. The webhook of the controller checks the pool, thus the function tries the
981
1010
  * pool again until the webhook answers. A failed install gives the pods of MetalLB and the tail of
982
1011
  * their logs.
1012
+ *
1013
+ * With `memberlist`, the ConfigMap of the config file comes before the speakers. Then the function
1014
+ * checks that each speaker logged the load of the file (speakerlist.go, line 393). Without it, the
1015
+ * function checks that the ConfigMap does not exist.
983
1016
  */
984
- export declare const installMetallb: (cluster: Cluster, images: MetallbImages, nodes: readonly string[], vip: string) => Promise<void>;
1017
+ export declare const installMetallb: (cluster: Cluster, images: MetallbImages, nodes: readonly string[], vip: string, memberlist?: MemberlistSettings) => Promise<void>;
985
1018
  /**
986
1019
  * This function gives the record of MetalLB after a run: the nodes that announce the Service, and
987
1020
  * some lines of each speaker. The lines are about memberlist and the announces, with the times of the kubelet.
988
1021
  */
989
1022
  export declare const metallbRecord: (cluster: Pick<Cluster, "kubectl">, namespace: string, service: string) => Promise<Record<string, unknown>>;
990
1023
  //#endregion
1024
+ //#region src/pulsar.d.ts
1025
+ /**
1026
+ * This module installs Apache Pulsar 5.0.0 in a kind cluster (SYS-56): one ZooKeeper, one bookie and
1027
+ * two brokers, from the Helm chart 4.8.0. It also gives the producers of the cell: a Python script
1028
+ * in the image of Pulsar, with its client 3.13.0 over the C++ client 4.2.0. Each producer sends to
1029
+ * one topic, and a thread of it looks up the owner of that topic at each probe.
1030
+ *
1031
+ * The fault is a SIGKILL of the JVM of the broker that owns the topic. Each term of the return of a
1032
+ * producer comes from a pinned source:
1033
+ * - The owner of a bundle is an ephemeral znode of the session of its broker. A lookup gives the
1034
+ * owner from the cache of the ownership, with no check of its life (pulsar-broker
1035
+ * NamespaceService.java at v5.0.0, lines 489–508). Thus each lookup names the stopped broker
1036
+ * until its session expires.
1037
+ * - The session timeout is `metadataStoreSessionTimeoutMillis`, 30000 (conf/broker.conf at v5.0.0,
1038
+ * line 187, and the chart, values.yaml line 1474). ZKMetadataStore.java gives it to ZooKeeper (line 105).
1039
+ * - The client of ZooKeeper 3.9.6 pings after 9 s with no send (ClientCnxn.java, lines 422, 1184
1040
+ * and 1263–1268). That is half of the read timeout of 2/3 of the session, less 1 s.
1041
+ * The server rounds each expiry up to the next tick of 2000 ms (ExpiryQueue.java, lines 53–54
1042
+ * and 87, and conf/zookeeper.conf of Pulsar, line 21). Thus the session expires 21–32 s after the stop.
1043
+ * - The broker that stays then takes the bundle at the next lookup.
1044
+ * - The chart starts a broker again only after its old znode is gone (templates/broker-statefulset.yaml,
1045
+ * lines 296–302). Thus the stopped broker cannot take the bundle back first.
1046
+ */
1047
+ /** This version is the release of Pulsar of the lab. */
1048
+ export declare const PULSAR_VERSION = "5.0.0";
1049
+ /**
1050
+ * This chart is the Helm chart 4.8.0 of Pulsar. The sha256 is of the file of the release. The URL is
1051
+ * on the archive of Apache, which keeps each release. The mirror downloads.apache.org keeps only the
1052
+ * latest releases.
1053
+ */
1054
+ export declare const PULSAR_CHART: PinnedChart;
1055
+ /**
1056
+ * This image is Pulsar 5.0.0 for linux/amd64, pinned by the digest of the manifest of the platform.
1057
+ * It has ZooKeeper 3.9.6, BookKeeper, the broker, Python 3 and the Python client 3.13.0. The lab
1058
+ * pulls it by its digest, tags it (`localPin`), and loads it into each node.
1059
+ */
1060
+ export declare const PULSAR_IMAGE = "apachepulsar/pulsar:5.0.0@sha256:11e9a885121e16be91da77fdd3cfc3d4cbbece4700271d75e949585dbd3a5a5b";
1061
+ /** This namespace holds Pulsar. The release of the chart is `pulsar` too. */
1062
+ export declare const PULSAR_NAMESPACE = "pulsar";
1063
+ /** This name is the app of the producers of a cell. */
1064
+ export declare const PRODUCER = "producer";
1065
+ /** This topic is the topic of the producers. */
1066
+ export declare const PULSAR_TOPIC = "persistent://public/default/lab";
1067
+ /** This URL is the Service of the brokers for the binary protocol. */
1068
+ export declare const PULSAR_SERVICE: string;
1069
+ /** This URL is the lookup of the owner of the topic, on the Service of the brokers. */
1070
+ export declare const PULSAR_LOOKUP: string;
1071
+ /** This time is the session timeout of each broker in ZooKeeper (`metadataStoreSessionTimeoutMillis`). */
1072
+ export declare const SESSION: Quantity;
1073
+ /** This time is the tick of ZooKeeper (`tickTime`). The server rounds each expiry up to a tick. */
1074
+ export declare const TICK: Quantity;
1075
+ /** This time is the longest idle time of the client of ZooKeeper before its ping: the half of 2/3 of the session, less 1 s. */
1076
+ export declare const PING: Quantity;
1077
+ /**
1078
+ * This backoff is the backoff of a producer of the C++ client 4.2.0, with the defaults of the Python
1079
+ * client 3.13.0:
1080
+ * - The initial delay is 100 ms and the most is 60 s (lib/ClientConfigurationImpl.h, lines 39–40).
1081
+ * - The mandatory stop is the send timeout less 100 ms, and at least 100 ms (lib/ProducerImpl.cc,
1082
+ * lines 55–57). The send timeout of `create_producer` is 30000 ms (pulsar/__init__.py, line 1015).
1083
+ * - The jitter takes 0–9 % of each delay (lib/Backoff.cc, line 54).
1084
+ * - A try that fails takes the lookup and the connect, which a reset ends at once. The lab gives it 100 ms.
1085
+ */
1086
+ export declare const PRODUCER_BACKOFF: Backoff;
1087
+ /**
1088
+ * This function gives the values of the chart: ZooKeeper, one bookie and two brokers, with no
1089
+ * proxy, no autorecovery, no toolset and no monitor. The bookie keeps one copy of each entry, and
1090
+ * the brokers do not move a bundle by load. Each image is the local image, with no pull.
1091
+ */
1092
+ export declare const pulsarValues: (image: string) => Record<string, unknown>;
1093
+ /**
1094
+ * This script is a producer of the cell. Each line that it writes is JSON, with the time of
1095
+ * CLOCK_MONOTONIC in milliseconds, the pod and the node:
1096
+ * - `send`: one send, at its start, with its time to the answer and its outcome. A send is `ok` within
1097
+ * the timeout, `late` after it, or the name of the result of the client (for example `timeout`).
1098
+ * - `lookup`: one lookup of the owner of the topic, with the host of the owner.
1099
+ * - `pulsar`: one line of the C++ client about the connection of the producer.
1100
+ */
1101
+ export declare const PRODUCER_SCRIPT = "import json, logging, os, sys, threading, time, urllib.request\nimport pulsar\n\nPOD = os.environ[\"POD_NAME\"]\nNODE = os.environ[\"NODE_NAME\"]\nEVERY = int(os.environ[\"EVERY_MS\"]) / 1000\nTIMEOUT = int(os.environ[\"TIMEOUT_MS\"])\nPROBE = int(os.environ[\"PROBE_MS\"]) / 1000\nLOCK = threading.Lock()\nKEPT = (\"Schedule reconnection\", \"Getting connection from pool\", \"Created producer\", \"Connection closed\", \"Disconnected\", \"Lookup response\", \"Failed to\", \"Finished connecting\")\n\n\ndef now():\n return time.monotonic() * 1000\n\n\ndef say(**line):\n text = json.dumps({**line, \"p\": POD, \"n\": NODE}, separators=(\",\", \":\"))\n with LOCK:\n sys.stdout.write(text + \"\\n\")\n sys.stdout.flush()\n\n\nclass Relay(logging.Handler):\n def emit(self, record):\n message = record.getMessage()\n if any(word in message for word in KEPT):\n say(ev=\"pulsar\", t=now(), m=message[:400])\n\n\ndef probe():\n while True:\n start = now()\n owner = None\n outcome = \"ok\"\n try:\n with urllib.request.urlopen(os.environ[\"LOOKUP\"], timeout=1) as answer:\n data = json.loads(answer.read())\n url = data.get(\"brokerUrl\") or \"\"\n owner = url.split(\"://\", 1)[-1].rsplit(\":\", 1)[0] or None\n except Exception as error:\n outcome = type(error).__name__.lower()\n say(ev=\"lookup\", t=start, d=round(now() - start, 3), o=outcome, owner=owner)\n time.sleep(max(0.0, PROBE - (now() - start) / 1000))\n\n\ndef answered(start):\n def done(result, message_id):\n took = now() - start\n if result == pulsar.Result.Ok:\n outcome = \"ok\" if took <= TIMEOUT else \"late\"\n else:\n outcome = getattr(result, \"name\", str(result).split(\".\")[-1]).lower()\n say(ev=\"send\", t=start, d=round(took, 3), o=outcome)\n return done\n\n\nlogger = logging.getLogger(\"lab\")\nlogger.setLevel(logging.INFO)\nlogger.propagate = False\nlogger.addHandler(Relay())\nthreading.Thread(target=probe, daemon=True).start()\nclient = pulsar.Client(os.environ[\"SERVICE\"], logger=logger)\nwhile True:\n try:\n producer = client.create_producer(os.environ[\"TOPIC\"])\n break\n except Exception as error:\n say(ev=\"text\", t=now(), text=\"create_producer: \" + str(error)[:200])\n time.sleep(2)\nat = time.monotonic()\nwhile True:\n start = now()\n try:\n producer.send_async(b\"lab\", answered(start))\n except Exception as error:\n say(ev=\"send\", t=start, d=0, o=\"refused-\" + type(error).__name__.lower())\n at += EVERY\n time.sleep(max(0.0, at - time.monotonic()))\n";
1102
+ /**
1103
+ * This function gives the producers of a cell in its namespace. They are the script in a ConfigMap,
1104
+ * and a Deployment of `clients` pods in the local image of Pulsar, one on each worker.
1105
+ */
1106
+ export declare const pulsarObjects: (cell: KindCell, namespace: string, image: string) => unknown[];
1107
+ /** A broker is a pod of a broker: its name, its node, its address, and if it is ready. */
1108
+ interface Broker {
1109
+ readonly pod: string;
1110
+ readonly node: string;
1111
+ readonly ip: string;
1112
+ readonly ready: boolean;
1113
+ }
1114
+ /** This function gives the pods of the brokers, in the sequence of their names. */
1115
+ export declare const brokersOf: (cluster: Pick<Cluster, "kubectl">) => Promise<Broker[]>;
1116
+ /** This function gives the broker that a host of a lookup names. Its pod is the first label of the host, or its address is the host. */
1117
+ export declare const brokerOfHost: (brokers: readonly Broker[], host: string) => Broker | undefined;
1118
+ /** This is the state of the producers that the hold reads. It has the last lookup and the last sends of each producer, and the last time of a line. */
1119
+ interface ProducerState {
1120
+ readonly owners: ReadonlyMap<string, {
1121
+ readonly t: number;
1122
+ readonly owner: string | null;
1123
+ }>;
1124
+ readonly sends: ReadonlyMap<string, {
1125
+ readonly ok: number;
1126
+ readonly failed: number;
1127
+ }>;
1128
+ readonly last: number;
1129
+ }
1130
+ /**
1131
+ * This function tells why the producers are not at rest, or gives the host of the owner of the
1132
+ * topic. At rest, each of `clients` producers had an answer in the last second and no failure in
1133
+ * the last 2 s. The last lookup of each one is in the last 2 s, and each names the same owner.
1134
+ */
1135
+ export declare const ownerAtRest: (state: ProducerState, clients: number) => {
1136
+ readonly owner: string;
1137
+ } | string;
1138
+ /**
1139
+ * This function waits until each broker is ready and the producers are at rest (`ownerAtRest`). It
1140
+ * gives the broker that owns the topic and the host that the lookups name, or why it could not in `within` ms.
1141
+ */
1142
+ export declare const holdPulsar: (cluster: Pick<Cluster, "kubectl">, state: () => ProducerState, clients: number, within?: number) => Promise<{
1143
+ readonly broker: Broker;
1144
+ readonly host: string;
1145
+ } | string>;
1146
+ /**
1147
+ * This function installs Pulsar in a cluster, with its image on each node. A failed install gives
1148
+ * the pods of Pulsar and the tail of their logs.
1149
+ */
1150
+ export declare const installPulsar: (cluster: Cluster, image: string) => Promise<void>;
1151
+ /**
1152
+ * This function gives the record of Pulsar after a run. It has the brokers, and the kept lines of
1153
+ * each broker and of its last container, with the times of the kubelet.
1154
+ */
1155
+ export declare const pulsarRecord: (cluster: Pick<Cluster, "kubectl">) => Promise<Record<string, unknown>>;
1156
+ //#endregion
991
1157
  //#region src/lds.d.ts
992
1158
  /**
993
1159
  * This filter of RE2 selects the two counters of the listener manager of Envoy that an update of LDS
@@ -1224,6 +1390,60 @@ interface LearnWindow {
1224
1390
  */
1225
1391
  export declare const learnWindow: (declaration: Declaration, readiness: KubeletProbe, propagation: Propagation, workload: Workload) => LearnWindow;
1226
1392
  //#endregion
1393
+ //#region src/quiet.d.ts
1394
+ /**
1395
+ * The quiet gate asks for an idle share of the VM before a run. The clients of the cell run at that
1396
+ * time, and the agent of each node reads the whole conntrack table each second. Thus the idle share
1397
+ * has the load of the cell in it. A flat share can void a heavy cell with no other load (SYS-95).
1398
+ * The gate reads the cell against itself. The baseline is the idle share of the settled cell, and
1399
+ * a foreign load shows as a fall from the baseline.
1400
+ */
1401
+ /** These numbers are the terms of the gate. Each share is a share of all the CPUs of the VM. */
1402
+ interface QuietGate {
1403
+ /** This share is enough for each cell, as it was before SYS-95. */
1404
+ readonly flat: number;
1405
+ /** This share is the most fall from the baseline that the gate lets through. */
1406
+ readonly margin: number;
1407
+ /** This share is the least that the gate lets through, with any baseline. */
1408
+ readonly floor: number;
1409
+ /** This time in milliseconds is the age of a cell at its first baseline sample. */
1410
+ readonly settle: number;
1411
+ /** This count is the samples of a baseline. */
1412
+ readonly samples: number;
1413
+ }
1414
+ /**
1415
+ * These are the terms of the lab. The flat share is the share of SYS-42. The other terms come from the
1416
+ * calibration of SYS-95, over the 9 records of SYS-57, with no foreign container in them:
1417
+ *
1418
+ * - The margin is the 99th percentile of the fall from the settled median of an environment: 4.88
1419
+ * points of 93 samples, rounded up.
1420
+ * - The floor is the lowest settled median of an environment, 48 %, minus the margin.
1421
+ * - The time to settle holds the slowest environment. Its conntrack table was at its plateau at 105 s.
1422
+ */
1423
+ export declare const QUIET_GATE: QuietGate;
1424
+ /**
1425
+ * This function gives the idle share that a run needs. With no baseline, it is the floor: a warm-up
1426
+ * counts for nothing, and its table is not full yet. A cell that is at rest at the flat share or more
1427
+ * keeps the flat share.
1428
+ */
1429
+ export declare const neededIdle: (gate: QuietGate, baseline: number | null) => number;
1430
+ /** This function gives the reason of a sample that the gate refuses. The site reads it back with `NOT_QUIET`. */
1431
+ export declare const quietWhy: (idle: number, needed: number, foreign: readonly string[]) => string;
1432
+ /**
1433
+ * This form reads the void of a run that the gate refuses. Its groups are the wait in seconds, the idle share, the share
1434
+ * of the gate and the other lab containers. A record from before SYS-95 has no share of the gate.
1435
+ */
1436
+ export declare const NOT_QUIET: RegExp;
1437
+ /**
1438
+ * This function gives the next baseline of a cell from the samples of one environment. Samples with
1439
+ * a foreign container do not count. The highest median holds, because a foreign load only lowers
1440
+ * the idle share. Thus a load in one environment does not move the gate of the other one.
1441
+ */
1442
+ export declare const baselineOf: (previous: number | null, samples: ReadonlyArray<{
1443
+ readonly idle: number;
1444
+ readonly foreign: readonly string[];
1445
+ }>) => number | null;
1446
+ //#endregion
1227
1447
  //#region src/rules.d.ts
1228
1448
  /**
1229
1449
  * This function gives the scenario of a cell for `lint()`: the preStop sleep, the grace, the probe
@@ -1270,6 +1490,8 @@ interface RunOptions {
1270
1490
  */
1271
1491
  readonly work?: string;
1272
1492
  }
1493
+ /** This function gives the name of the cluster of a cell in an environment. Kind permits 32 characters at most: lowercase letters, digits and dashes. */
1494
+ export declare const clusterNameOf: (cell: KindCell, env: number) => string;
1273
1495
  /** This function writes the result of a cell: its raw record, and one line of its verdict in `summary.ndjson`. */
1274
1496
  export declare const writeResult: (out: string, result: KindCellResult) => void;
1275
1497
  /**
@@ -1351,5 +1573,5 @@ export declare const toolPath: (tool: Tool, key?: string, cache?: string) => str
1351
1573
  */
1352
1574
  export declare const ensureTool: (tool: Tool, key?: string, cache?: string) => Promise<string>;
1353
1575
  //#endregion
1354
- export { type Announcement, type BacklogTerm, type Band, type BandV1, CLIENT, type CiliumImages, type Cluster, type ClusterHosts, type ClusterManifest, type CountBand, type Credentials, type Cycle, DATAPLANES, DRAIN_RULES, type Dataplane, 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 KubeVipPod, type KubeletProbe, LAST_SLACK, LB_CLASS, LOG_LABEL, type LdsSpec, type LeafCertificate, type LearnWindow, Line, type ListenerCounts, type LoadSample, type Mesh, type MeshFlavor, type MeshImages, type MeshMode, type MetallbImages, NODE_IMAGES, type NodeImage, type NodeLifecycle, type NonePrediction, O04, O09, O10, O16, OUTSIDE, type OutsideSpec, PORT, type Parts, Period, type PermittedWindow, type PinnedChart, 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, Sessions, 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, VIP_ANNOTATION, type Verdicts, type VipAnnouncer, type VipAnnouncerName, type VipLease, type VipNetwork, type VipService, type VipSettings, type WindowPrediction, type Workload, ZONE_KEPT, ZONE_LABEL, ZONE_LOST, ZtunnelPodLine, backendsAt, backlogEnd, bandOf, byClients, byNode, calibrationOf, cellsTable, checkRun, cidrOverlap, cidrRange, cyclesOf, deletionOf, deletionsOf, drainBand, dropOf, emptyOf, factsOf, failuresOf, goDuration, intakeOf, isKubeProxyMode, isTunnelState, judge, judgeKind, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, kindConfig, kubeProxyPatch, medianInterval, medianText, middle, nodeNames, observationAfter, observationEnd, outcomesOf, overshootBand, periodOf, podsAt, podsOf, predictionText, propagationOf, propagationSource, quantileOf, readRecord, recordsOf, referenceOf, requestBand, requestsOf, restartOf, restartsAt, reusesReset, rulesOf, scenario, scenarioProblems, seconds, secondsOf, sessionsAt, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, sliceReadyAt, sliceReadyOf, slicesOf, socketsOf, stoppingOf, timeSpread, transitionOf, tunnelsOf, unitOf, updatesOf, upgrade, upgradeCell, zoneOf, ztunnelPodsOf, ztunnelReplaced };
1576
+ export { type Announcement, type BacklogTerm, type Backoff, type Band, type BandV1, type Broker, CLIENT, type CiliumImages, type Cluster, type ClusterHosts, type ClusterManifest, type CountBand, type Credentials, type Cycle, DATAPLANES, DRAIN_RULES, type Dataplane, 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 KubeVipPod, type KubeletProbe, LAST_SLACK, LB_CLASS, LOG_LABEL, type LdsSpec, type LeafCertificate, type LearnWindow, Line, type ListenerCounts, type LoadSample, type MemberlistSettings, type Mesh, type MeshFlavor, type MeshImages, type MeshMode, type MetallbImages, NODE_IMAGES, type NodeImage, type NodeLifecycle, type NonePrediction, O04, O09, O10, O16, OUTSIDE, type OutsideSpec, PORT, type Parts, Period, type PermittedWindow, type PinnedChart, PodLine, type PodSettings, type Prediction, type PredictionV1, type ProducerState, type Propagated, type Propagation, PropagationSource, type PulsarSettings, type QuietGate, REDIS, RELATIVE, type Reconnects, type ReconnectsFacts, type ReconnectsPrediction, RecordLine, type RedisDeclaration, type Reference, type Replica, Request, type ResetReuse, type Return, RulesLine, type RunCheck, type RunCheckV1, type RunFacts, type RunFactsV1, type RunOptions, SERVER, STAGES, type Schedule, type ServiceRule, Sessions, SliceLine, SocketLine, type Stack, type Stamp, type Step, TCP_STATE, type Target, type Template, type TimeSpread, type Tool, type Trigger, type TriggerKind, type TriggerV1, type Try, TunnelLine, VIP_ANNOTATION, type Verdicts, type VipAnnouncer, type VipAnnouncerName, type VipLease, type VipNetwork, type VipService, type VipSettings, type WindowPrediction, type Workload, ZONE_KEPT, ZONE_LABEL, ZONE_LOST, ZtunnelPodLine, backendsAt, backlogEnd, bandOf, byClients, byNode, calibrationOf, cellsTable, checkRun, cidrOverlap, cidrRange, cyclesOf, deletionOf, deletionsOf, drainBand, dropOf, emptyOf, factsOf, failuresOf, goDuration, intakeOf, isKubeProxyMode, isTunnelState, judge, judgeKind, judgeReconnects, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, kindConfig, kubeProxyPatch, latestReturn, medianInterval, medianText, middle, nodeNames, observationAfter, observationEnd, outcomesOf, overshootBand, periodOf, podsAt, podsOf, predictionText, propagationOf, propagationSource, quantileOf, readRecord, recordsOf, referenceOf, requestBand, requestsOf, restartOf, restartsAt, reusesReset, rulesOf, scenario, scenarioProblems, seconds, secondsOf, sessionsAt, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, sliceReadyAt, sliceReadyOf, slicesOf, socketsOf, stoppingOf, timeSpread, transitionOf, triesOf, tunnelsOf, unitOf, updatesOf, upgrade, upgradeCell, zoneOf, ztunnelPodsOf, ztunnelReplaced };
1355
1577
  //# sourceMappingURL=index.d.ts.map