@mark1russell7/harness 0.11.22 → 0.11.24
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 +9 -2
- package/build/{cell-BxBZUssi.d.ts → cell-wInJC-GH.d.ts} +12 -3
- package/build/index.d.ts +7 -4
- package/build/index.js +47 -13
- package/build/index.js.map +1 -1
- package/build/schema.d.ts +1 -1
- package/image/go-http/main.go +14 -0
- package/image/node-http/client-undici.mjs +39 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -125,6 +125,9 @@ A step-down (`step-down`) moves the leadership to the first follower (`rabbitmq-
|
|
|
125
125
|
- **An HTTP client** (`Cell.client`: `go-http`, `http-agent` or `undici`) is a client of the harness from its own image. It sends a GET of the root, then a second request (`probe.http`):
|
|
126
126
|
- after the signal (`inflight`), into the fault
|
|
127
127
|
- or by itself, a gap after its first reply (`gap`), for the keep-alive race.
|
|
128
|
+
- **A client of a keyed limiter.** The flag `limiter-key` (`probe.http.options`) makes the client of undici or of Go send a key of its process in the header `x-limiter-key`. Thus each run has its own count in the limiter of the target.
|
|
129
|
+
- The flag `retry` sends each request of undici through its `RetryAgent`, at the defaults of undici. Each `retry-` flag sets one option over them.
|
|
130
|
+
- The client of undici then writes an `attempt` line for each send of a request, with its number `n` and its socket. A `RetryAgent` that gives up gives `UND_ERR_REQ_RETRY` on the `error` line, with the status of the last reply and the count of sends.
|
|
128
131
|
- **The lines of an HTTP client.** It writes the lines of the TCP client, with the number `i` of the request on each line of a request. It also writes the connection of each try of a request (`request`, then `retry`), with its local address and a flag for a reused connection. Each connection gets a `conn` line, and each end of a connection a line (`eof`, `conn-error`, `closed`). The end of the second request is a `done` line.
|
|
129
132
|
- **A target with a script.** A target can take files before it starts (`Target.files`), for example the script of a Node server on the stock Node image. A target that writes JSON lines (`Target.lines`) gives them to each run (`Run.server`). The manifest holds the sha256 of each file and the first line of the target.
|
|
130
133
|
- **The WebSocket client** (`Cell.client` is `procedures-ws`) is the `WebSocketTransport` of `@procedures/client` 0.7.0: the `client` program of the `procedures` subject. The cell gives each of its settings (`probe.ws`): the class of its sockets, the heartbeat, the reconnect, and the times of a call.
|
|
@@ -247,7 +250,8 @@ ioredis sets the keepalive of its socket itself, after it connects. Thus a cell
|
|
|
247
250
|
- **A close of the server** (`server-close`) makes the server close the connection of each client, with `Target.close`: `CLIENT KILL` for Redis.
|
|
248
251
|
- **A stall of the application** (`app-stall`) injects nothing. The server process holds the reply, because the request asks for it (`HttpProbe.hold`), and its kernel stays up.
|
|
249
252
|
- **An event at a proxy** (`hop-event`, `Cell.event`) is a signal to the program of the proxy, or a query on its console. Examples are a reload of the configuration, a stop of a proxy and PAUSE on the console of PgBouncer.
|
|
250
|
-
- A signal goes to each process of `ProxyHop.program`: HUP, TERM or
|
|
253
|
+
- A signal goes to each process of `ProxyHop.program`: HUP, TERM, INT, STOP or CONT (`HopSignal`). A sidecar in the network and process namespaces of the proxy sends it (`inject signal NAME HUP`). The lab starts that sidecar at the first signal.
|
|
254
|
+
- STOP blocks the event loop of the proxy: its processes stop, and its kernel keeps each socket. With a hold (`Cell.hold`), CONT thaws it at that time after the stop, from inside the VM (`--at`). The lift thaws a proxy that its hold did not thaw.
|
|
251
255
|
- A query goes from the injector of the proxy to its console on 127.0.0.1, as a user that the console trusts (`inject query`). The injector speaks the simple query of the PostgreSQL protocol. Its stamps bracket the query and its ReadyForQuery. After the run, the harness sends the `undo` of the query, for example RESUME after PAUSE.
|
|
252
256
|
- Without `inFlight`, the event comes first, and the request meets it. With `inFlight`, the request goes first, and the event comes while it is in flight. A run whose order is not that order is void.
|
|
253
257
|
- **A fault on a group** acts on the part that `Cell.faultAt` names by its role:
|
|
@@ -331,7 +335,10 @@ Each run starts from kernels that keep nothing from the run before it (`lab.forg
|
|
|
331
335
|
- The band counts from its reference on that socket. It ends at a line of the client (`ends`), or at the report of the kernel on the socket.
|
|
332
336
|
- A band that a line ends is a timer of the driver. It counts from the last segment with data that the socket received or sent. The idle timer of a Node socket starts again at each read and each write of data.
|
|
333
337
|
- A band can name that segment as its reference (`last-data`). For example, an idle gRPC client goes to IDLE at a GOAWAY of its proxy. The idle timer of the proxy counts from the last data of the stream.
|
|
334
|
-
- A reference that this judge does not know (for example `ping-sent`) does not come. Thus its band is a finding. Before 0.11.22, such a reference counted from the first close of a socket of the peer. Through a proxy, that close can be the idle close of a socket toward the target.
|
|
338
|
+
- A reference that this judge does not know (for example `ping-sent`) does not come. Thus its band is a finding. Before 0.11.22, such a reference counted from the first close of a socket of the peer. Through a proxy, that close can be the idle close of a socket toward the target. Since 0.11.23, the TCP judge does the same.
|
|
339
|
+
- A retry after a refusal counts from that refusal (`last-reply`), for example a 429 with `Retry-After`. That is the last segment with data that a socket of the client received before the line that ends the band. Each band gets its own, thus each retry counts from the refusal before it.
|
|
340
|
+
- The retry can go on a new connection. For example, undici closes an idle socket 3 s after a reply of a server at its defaults, before a retry 30 s later. Thus `last-reply` reads each socket of the client.
|
|
341
|
+
- The bands from `last-reply` follow one another. Thus the deadline of the client and the wait of the run hold their sum: five retries of 30 s take 150 s (`longestOf`).
|
|
335
342
|
- The MongoDB client is ready when its first write has its reply and its monitor streams. The monitor sends its first awaited hello `minHeartbeatFrequencyMS` (500 ms) after its first check. A fault before that hello meets the start of the stream, not the stream that a cell measures.
|
|
336
343
|
- Each later band (`then`) has its own reference and line. A later band outside its band, or absent, is a finding. So is a line that the prediction names as quiet, after the fault.
|
|
337
344
|
- For a silent prediction, a flow of an open socket of the driver that a router forgot is a finding.
|
|
@@ -130,8 +130,12 @@ interface ProxyHop {
|
|
|
130
130
|
/**
|
|
131
131
|
* A signal of a hop event. HUP reloads the configuration of a pooler. TERM stops a proxy, as a stop of
|
|
132
132
|
* its container does. INT stops docker-proxy, as Docker does when it removes a port binding.
|
|
133
|
+
*
|
|
134
|
+
* STOP blocks the event loop of a proxy: its processes stop, and its kernel keeps each socket. Thus
|
|
135
|
+
* ACKs go on, and the data of each peer waits in its receive queue. With the hold of the cell
|
|
136
|
+
* (`Cell.hold`), CONT thaws it at the end of the hold, from inside the VM.
|
|
133
137
|
*/
|
|
134
|
-
type HopSignal = "HUP" | "TERM" | "INT";
|
|
138
|
+
type HopSignal = "HUP" | "TERM" | "INT" | "STOP" | "CONT";
|
|
135
139
|
/**
|
|
136
140
|
* An event at a proxy hop (`hop-event`). A signal goes to each process of the program of the hop
|
|
137
141
|
* (`ProxyHop.program`). A query goes to the console of the hop from its own namespace, as a user
|
|
@@ -312,8 +316,13 @@ interface Toxic {
|
|
|
312
316
|
*
|
|
313
317
|
* A connect timer counts from the change of its socket to SYN_SENT (`syn-sent`). A pooler connects
|
|
314
318
|
* to the server by itself, thus the kernel stamps that connect, and no line of the client does.
|
|
319
|
+
*
|
|
320
|
+
* A retry of a client after a refusal counts from the refusal (`last-reply`), for example a 429 with
|
|
321
|
+
* `Retry-After`. That is the last segment with data that a socket of the client received before
|
|
322
|
+
* the line that ends the band. The retry can go on a new connection. Each band of a driver has
|
|
323
|
+
* its own, thus each retry counts from the refusal before it.
|
|
315
324
|
*/
|
|
316
|
-
type Reference = "last-received" | "last-data" | "first-retransmission" | "request-sent" | "peer-closed" | "fault" | "client-start" | "ping-sent" | "first-sent" | "syn-sent";
|
|
325
|
+
type Reference = "last-received" | "last-reply" | "last-data" | "first-retransmission" | "request-sent" | "peer-closed" | "fault" | "client-start" | "ping-sent" | "first-sent" | "syn-sent";
|
|
317
326
|
/**
|
|
318
327
|
* This prediction tells when a connection moves again after a stall. That occurs at the first
|
|
319
328
|
* firing of its retransmission timer after the stall stops, and its reply comes back one round
|
|
@@ -1123,4 +1132,4 @@ interface RunTopology {
|
|
|
1123
1132
|
}
|
|
1124
1133
|
//#endregion
|
|
1125
1134
|
export { Subject as $, Prediction as A, RequestPrediction as B, KernelRecord as C, MiddleboxForm as D, MiddleboxExpectation as E, Recovers as F, RunPart as G, RttRange as H, Reference as I, SentinelStage as J, RunTopology as K, ReplicaSet as L, ProxyHop as M, ProxyTime as N, Observer as O, Reconnects as P, StageBand as Q, ReplicaSetStage as R, ImageSource as S, MiddleboxEvent as T, Run as U, RouterHop as V, RunGroup as W, ShippedImage as X, Shipped as Y, Stage as Z, Hop as _, Cluster as a, latestEndOf as at, HopSignal as b, DriverBand as c, FaultPlace as d, Target as et, Group as f, HarnessImage as g, GroupRole as h, ClientImage as i, isHopPlace as it, ProbeEvent as j, PinnedImage as k, DriverStep as l, GroupPrediction as m, Cell as n, Toxic as nt, ClusterClientAt as o, GroupPlace as p, SentinelEvent as q, Client as r, WsProbe as rt, ClusterStage as s, BuildImage as t, Topology as tt, Fault as u, HopEvent as v, LineMatch as w, HttpProbe as x, HopPlace as y, Replies as z };
|
|
1126
|
-
//# sourceMappingURL=cell-
|
|
1135
|
+
//# sourceMappingURL=cell-wInJC-GH.d.ts.map
|
package/build/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as Subject, A as Prediction, B as RequestPrediction, C as KernelRecord, D as MiddleboxForm, E as MiddleboxExpectation, F as Recovers, G as RunPart, H as RttRange, I as Reference, J as SentinelStage, K as RunTopology, L as ReplicaSet, M as ProxyHop, N as ProxyTime, O as Observer, P as Reconnects, Q as StageBand, R as ReplicaSetStage, S as ImageSource, T as MiddleboxEvent, U as Run, V as RouterHop, W as RunGroup, X as ShippedImage, Y as Shipped, Z as Stage, _ as Hop, a as Cluster, at as latestEndOf, b as HopSignal, c as DriverBand, d as FaultPlace, et as Target, f as Group, g as HarnessImage, h as GroupRole, i as ClientImage, it as isHopPlace, j as ProbeEvent, k as PinnedImage, l as DriverStep, m as GroupPrediction, n as Cell, nt as Toxic, o as ClusterClientAt, p as GroupPlace, q as SentinelEvent, r as Client, rt as WsProbe, s as ClusterStage, t as BuildImage, tt as Topology, u as Fault, v as HopEvent, w as LineMatch, x as HttpProbe, y as HopPlace, z as Replies } from "./cell-
|
|
1
|
+
import { $ as Subject, A as Prediction, B as RequestPrediction, C as KernelRecord, D as MiddleboxForm, E as MiddleboxExpectation, F as Recovers, G as RunPart, H as RttRange, I as Reference, J as SentinelStage, K as RunTopology, L as ReplicaSet, M as ProxyHop, N as ProxyTime, O as Observer, P as Reconnects, Q as StageBand, R as ReplicaSetStage, S as ImageSource, T as MiddleboxEvent, U as Run, V as RouterHop, W as RunGroup, X as ShippedImage, Y as Shipped, Z as Stage, _ as Hop, a as Cluster, at as latestEndOf, b as HopSignal, c as DriverBand, d as FaultPlace, et as Target, f as Group, g as HarnessImage, h as GroupRole, i as ClientImage, it as isHopPlace, j as ProbeEvent, k as PinnedImage, l as DriverStep, m as GroupPrediction, n as Cell, nt as Toxic, o as ClusterClientAt, p as GroupPlace, q as SentinelEvent, r as Client, rt as WsProbe, s as ClusterStage, t as BuildImage, tt as Topology, u as Fault, v as HopEvent, w as LineMatch, x as HttpProbe, y as HopPlace, z as Replies } from "./cell-wInJC-GH.js";
|
|
2
2
|
import { Quantity } from "@mark1russell7/quantity";
|
|
3
3
|
import "testcontainers";
|
|
4
4
|
//#region src/application.d.ts
|
|
@@ -135,9 +135,11 @@ export declare const socketAt: (events: readonly ProbeEvent[], role: string, at:
|
|
|
135
135
|
* - `peer-closed`: the change of state of the socket of the target, when its process dies.
|
|
136
136
|
* - `fault`: the start of the commit of the fault. `client-start`: the `connecting` line.
|
|
137
137
|
* - `first-sent`: the first segment with data that the socket sends after the fault.
|
|
138
|
+
* - `last-reply`: the refusal that a retry follows. It is the last segment with data that a socket
|
|
139
|
+
* of the client received before the end of the band (`end`). The retry can go on another socket.
|
|
138
140
|
* A reference that this judge does not know (`ping-sent`) does not come: its band is a finding.
|
|
139
141
|
*/
|
|
140
|
-
export declare const referenceOf: (run: Run, from: Reference, role: string, data: boolean) => number | undefined;
|
|
142
|
+
export declare const referenceOf: (run: Run, from: Reference, role: string, data: boolean, end?: number) => number | undefined;
|
|
141
143
|
/**
|
|
142
144
|
* This function reads one run of a driver cell.
|
|
143
145
|
* - The band of the cell counts from its reference on its socket. It ends at its line, or at the
|
|
@@ -939,9 +941,10 @@ interface Lab {
|
|
|
939
941
|
* This method applies an event at a proxy hop (`HopEvent`), or its undo. A signal goes from a
|
|
940
942
|
* sidecar in the network and process namespaces of the hop. The lab starts that sidecar at the
|
|
941
943
|
* first signal of the hop. A query goes from the injector of the hop to its console on 127.0.0.1. The
|
|
942
|
-
* injector stamps each one. An event with no undo gives null for its undo.
|
|
944
|
+
* injector stamps each one. An event with no undo gives null for its undo. A signal can wait for an
|
|
945
|
+
* instant on CLOCK_MONOTONIC (`at`), as the thaw at the end of a hold does.
|
|
943
946
|
*/
|
|
944
|
-
event(event: HopEvent, undo?: boolean): Promise<{
|
|
947
|
+
event(event: HopEvent, undo?: boolean, at?: number): Promise<{
|
|
945
948
|
t0: number;
|
|
946
949
|
t1: number;
|
|
947
950
|
} | null>;
|
package/build/index.js
CHANGED
|
@@ -741,14 +741,17 @@ const outsetOf = (run) => {
|
|
|
741
741
|
* - `peer-closed`: the change of state of the socket of the target, when its process dies.
|
|
742
742
|
* - `fault`: the start of the commit of the fault. `client-start`: the `connecting` line.
|
|
743
743
|
* - `first-sent`: the first segment with data that the socket sends after the fault.
|
|
744
|
+
* - `last-reply`: the refusal that a retry follows. It is the last segment with data that a socket
|
|
745
|
+
* of the client received before the end of the band (`end`). The retry can go on another socket.
|
|
744
746
|
* A reference that this judge does not know (`ping-sent`) does not come: its band is a finding.
|
|
745
747
|
*/
|
|
746
|
-
const referenceOf = (run, from, role, data) => {
|
|
748
|
+
const referenceOf = (run, from, role, data, end = Infinity) => {
|
|
747
749
|
const ready = run.events.find((event) => event.ev === "ready");
|
|
748
750
|
const before = run.fault?.t1 ?? ready?.t ?? Infinity;
|
|
749
751
|
if (from === "fault") return run.fault?.t0;
|
|
750
752
|
if (from === "client-start") return (run.events.find((event) => event.ev === "connecting") ?? run.events[0])?.t;
|
|
751
753
|
if (from === "request-sent") return run.events.filter((event) => event.ev === "sent")[1]?.t;
|
|
754
|
+
if (from === "last-reply") return run.kernel.filter((record) => record.netns === run.netns && record.mono < end && record.ev === "rx" && (record.seg?.len.value ?? 0) > 0).at(-1)?.mono;
|
|
752
755
|
const { socket, place } = kernelOf(run, role, before);
|
|
753
756
|
if (place === void 0) return void 0;
|
|
754
757
|
if (from === "last-received" || from === "last-data") {
|
|
@@ -812,13 +815,19 @@ const reportOf = (place, after) => {
|
|
|
812
815
|
outcome: { kind: "eof" }
|
|
813
816
|
};
|
|
814
817
|
};
|
|
815
|
-
/**
|
|
818
|
+
/**
|
|
819
|
+
* This function gives the latest instant of the bands of a driver prediction, in seconds from their
|
|
820
|
+
* references. A band from the refusal before it (`last-reply`) follows the band before it. Thus the
|
|
821
|
+
* times of those bands add: five retries of 30 s end 150 s after the first refusal.
|
|
822
|
+
*/
|
|
816
823
|
const longestOf = (cell) => {
|
|
817
824
|
const { prediction } = cell;
|
|
818
825
|
if (prediction.detects === null) return valueIn(prediction.window, "s");
|
|
819
|
-
const
|
|
826
|
+
const then = prediction.driver?.then ?? [];
|
|
827
|
+
const steps = then.map((step) => valueIn(step.high, "s"));
|
|
820
828
|
const own = prediction.detects ? valueIn(prediction.high, "s") : valueIn(prediction.window, "s");
|
|
821
|
-
|
|
829
|
+
const chained = [...prediction.detects && prediction.from === "last-reply" ? [own] : [], ...then.filter((step) => step.from === "last-reply").map((step) => valueIn(step.high, "s"))];
|
|
830
|
+
return Math.max(own, ...steps, chained.reduce((total, high) => total + high, 0));
|
|
822
831
|
};
|
|
823
832
|
/**
|
|
824
833
|
* This function tells if a run of a driver cell has its outcome, so that its wait can stop. For a
|
|
@@ -956,7 +965,7 @@ const measureDriver = (cell, run) => {
|
|
|
956
965
|
}
|
|
957
966
|
const stepTimes = (driver.then ?? []).map((step) => {
|
|
958
967
|
const line = lineAfter(events, step.ends, after);
|
|
959
|
-
const from = referenceOf(run, step.from, step.socket ?? driver.socket, true);
|
|
968
|
+
const from = referenceOf(run, step.from, step.socket ?? driver.socket, true, line?.t);
|
|
960
969
|
if (line === void 0 || from === void 0) {
|
|
961
970
|
findings.push(line === void 0 ? `${step.what}: ${wordsOf(step.ends)} never came` : `${step.what}: its reference (${step.from}) never came`);
|
|
962
971
|
return {
|
|
@@ -986,7 +995,7 @@ const measureDriver = (cell, run) => {
|
|
|
986
995
|
finding: findings[0] ?? null
|
|
987
996
|
};
|
|
988
997
|
const line = lineAfter(events, driver.reply.ends, after);
|
|
989
|
-
const from = referenceOf(run, driver.reply.from, driver.socket, true);
|
|
998
|
+
const from = referenceOf(run, driver.reply.from, driver.socket, true, line?.t);
|
|
990
999
|
if (line === void 0 || from === void 0) return {
|
|
991
1000
|
...base,
|
|
992
1001
|
...nothing,
|
|
@@ -1025,7 +1034,7 @@ const measureDriver = (cell, run) => {
|
|
|
1025
1034
|
void: voided,
|
|
1026
1035
|
finding: findings[0] ?? `nothing detected it: ${prediction.timer} never fired`
|
|
1027
1036
|
};
|
|
1028
|
-
const reference = referenceOf(run, prediction.from, driver.socket, driver.ends !== void 0);
|
|
1037
|
+
const reference = referenceOf(run, prediction.from, driver.socket, driver.ends !== void 0, ended);
|
|
1029
1038
|
if (reference === void 0) return {
|
|
1030
1039
|
...base,
|
|
1031
1040
|
...nothing,
|
|
@@ -1488,7 +1497,7 @@ const count = (event, field) => event === void 0 ? 0 : Number(event[field] ?? 0)
|
|
|
1488
1497
|
/** This function gives the view of the client on the same interval: TCP_INFO, with a sample each 2 ms. */
|
|
1489
1498
|
const socketVantage = (cell, events, ready, request) => {
|
|
1490
1499
|
const { from } = cell.prediction.detects === true ? cell.prediction : { from: void 0 };
|
|
1491
|
-
if (from === void 0 || from === "peer-closed" || from === "fault" || from === "ping-sent" || from === "first-sent" || from === "last-data") return null;
|
|
1500
|
+
if (from === void 0 || from === "peer-closed" || from === "fault" || from === "ping-sent" || from === "first-sent" || from === "last-data" || from === "last-reply") return null;
|
|
1492
1501
|
const ended = events.find((event) => event.ev === "error" || event.ev === "eof");
|
|
1493
1502
|
const reference = from === "last-received" ? ready : from === "request-sent" ? request : events.find((event) => event.ev === "kernel" && event.t >= request.t && count(event, "retransmits") > 0);
|
|
1494
1503
|
return ended === void 0 || reference === void 0 ? null : between(reference.t, ended.t);
|
|
@@ -1829,7 +1838,7 @@ const measureSocket = (cell, run) => {
|
|
|
1829
1838
|
if (from === "fault") return run.fault?.t0;
|
|
1830
1839
|
if (from === "client-start") return (events.find((event) => event.ev === "connecting") ?? events[0])?.t;
|
|
1831
1840
|
if (from === "ping-sent") return firstUnanswered(pings, socket, ended)?.at;
|
|
1832
|
-
const record = from === "last-received" ? socket.filter((each) => each.ev === "rx" && each.mono <= before).at(-1) : from === "last-data" ? socket.filter((each) => each.ev === "rx" && (each.seg?.len.value ?? 0) > 0 && each.mono <= (ended ?? before)).at(-1) : from === "first-retransmission" ? retransmitted.find((each) => each.why === "rto" && each.mono > before) : from === "first-sent" ? socket.find((each) => each.ev === "tx" && each.mono > before && (each.seg?.len.value ?? 0) > 0) : from === "syn-sent" ? socket.find((each) => each.ev === "state" && newState(each) === 2) : kernel.find((each) => each.netns === peerNetns && each.cookie === peerCookie && each.ev === "state" && oldState(each) === 1);
|
|
1841
|
+
const record = from === "last-received" ? socket.filter((each) => each.ev === "rx" && each.mono <= before).at(-1) : from === "last-data" ? socket.filter((each) => each.ev === "rx" && (each.seg?.len.value ?? 0) > 0 && each.mono <= (ended ?? before)).at(-1) : from === "last-reply" ? socket.filter((each) => each.ev === "rx" && (each.seg?.len.value ?? 0) > 0 && each.mono < (ended ?? Infinity)).at(-1) : from === "first-retransmission" ? retransmitted.find((each) => each.why === "rto" && each.mono > before) : from === "first-sent" ? socket.find((each) => each.ev === "tx" && each.mono > before && (each.seg?.len.value ?? 0) > 0) : from === "syn-sent" ? socket.find((each) => each.ev === "state" && newState(each) === 2) : from === "peer-closed" ? kernel.find((each) => each.netns === peerNetns && each.cookie === peerCookie && each.ev === "state" && oldState(each) === 1) : void 0;
|
|
1833
1842
|
if (record === void 0 && from === "peer-closed" && peerCookie === void 0) return proxiedClose(run);
|
|
1834
1843
|
return record?.mono;
|
|
1835
1844
|
};
|
|
@@ -2794,7 +2803,7 @@ const openLab = async (target, kind = "tcp", topology) => {
|
|
|
2794
2803
|
...when
|
|
2795
2804
|
]);
|
|
2796
2805
|
},
|
|
2797
|
-
async event(event, undo = false) {
|
|
2806
|
+
async event(event, undo = false, at) {
|
|
2798
2807
|
const hop = hopNamed(event.hop);
|
|
2799
2808
|
if (hop.hop.kind !== "proxy") throw new Error(`the hop ${event.hop} is a router: an event goes to a proxy`);
|
|
2800
2809
|
if ("signal" in event) {
|
|
@@ -2807,11 +2816,13 @@ const openLab = async (target, kind = "tcp", topology) => {
|
|
|
2807
2816
|
hopSignallers.set(event.hop, sidecar);
|
|
2808
2817
|
opened.push(sidecar);
|
|
2809
2818
|
}
|
|
2819
|
+
const when = at === void 0 ? [] : ["--at", String(Math.round(at))];
|
|
2810
2820
|
return stamped(sidecar, [
|
|
2811
2821
|
"/opt/harness/inject",
|
|
2812
2822
|
"signal",
|
|
2813
2823
|
program,
|
|
2814
|
-
event.signal
|
|
2824
|
+
event.signal,
|
|
2825
|
+
...when
|
|
2815
2826
|
]);
|
|
2816
2827
|
}
|
|
2817
2828
|
const query = undo ? event.console.undo : event.console.query;
|
|
@@ -5315,9 +5326,23 @@ const once = async (lab, cell, environment, index, dry = false) => {
|
|
|
5315
5326
|
case "server-close":
|
|
5316
5327
|
await lab.serverClose();
|
|
5317
5328
|
return null;
|
|
5318
|
-
case "hop-event":
|
|
5329
|
+
case "hop-event": {
|
|
5319
5330
|
if (cell.event === void 0) throw new Error(`${cell.name} names no event`);
|
|
5320
|
-
|
|
5331
|
+
const stamp = await lab.event(cell.event);
|
|
5332
|
+
if ("signal" in cell.event && cell.event.signal === "STOP" && cell.hold !== void 0 && stamp !== null) {
|
|
5333
|
+
const thaw = {
|
|
5334
|
+
hop: cell.event.hop,
|
|
5335
|
+
signal: "CONT"
|
|
5336
|
+
};
|
|
5337
|
+
thawing = lab.event(thaw, false, stamp.t0 + valueIn(cell.hold, "s") * 1e9).then((thawed) => {
|
|
5338
|
+
if (thawed === null) throw new Error(`the hop ${thaw.hop} gave no stamp for CONT`);
|
|
5339
|
+
heal = thawed;
|
|
5340
|
+
return thawed;
|
|
5341
|
+
});
|
|
5342
|
+
thawing.catch(() => void 0);
|
|
5343
|
+
}
|
|
5344
|
+
return stamp;
|
|
5345
|
+
}
|
|
5321
5346
|
default: return lab.fault("blackhole", faultAt);
|
|
5322
5347
|
}
|
|
5323
5348
|
};
|
|
@@ -5337,6 +5362,15 @@ const once = async (lab, cell, environment, index, dry = false) => {
|
|
|
5337
5362
|
case "app-stall":
|
|
5338
5363
|
case "none": return;
|
|
5339
5364
|
case "hop-event":
|
|
5365
|
+
if (cell.event !== void 0 && "signal" in cell.event && cell.event.signal === "STOP") {
|
|
5366
|
+
const thaw = {
|
|
5367
|
+
hop: cell.event.hop,
|
|
5368
|
+
signal: "CONT"
|
|
5369
|
+
};
|
|
5370
|
+
if (thawing !== null) await thawing.catch(() => lab.event(thaw));
|
|
5371
|
+
else await lab.event(thaw);
|
|
5372
|
+
return;
|
|
5373
|
+
}
|
|
5340
5374
|
if (cell.event !== void 0) await lab.event(cell.event, true);
|
|
5341
5375
|
return;
|
|
5342
5376
|
case "unreachable": return lab.lift("reject", faultAt, { code: cell.icmp ?? "host" });
|