@mark1russell7/harness 0.11.13 → 0.11.15
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 +5 -0
- package/build/{cell-COlAmUZ-.d.ts → cell-DOoU1LRf.d.ts} +32 -3
- package/build/index.d.ts +14 -3
- package/build/index.js +67 -5
- package/build/index.js.map +1 -1
- package/build/schema.d.ts +1 -1
- package/image/native/src/bin/inject.rs +123 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -213,6 +213,10 @@ ioredis sets the keepalive of its socket itself, after it connects. Thus a cell
|
|
|
213
213
|
- **A toxic** (`toxic`, `Cell.toxic`) is one toxic of Toxiproxy, added through its HTTP API by `inject http`, with monotonic stamps around the request. Its name is `fault`, and the harness removes it after the run. A cell whose fault is another one adds its toxic first: a slow close, then the close of the server.
|
|
214
214
|
- **A close of the server** (`server-close`) makes the server close the connection of each client, with `Target.close`: `CLIENT KILL` for Redis.
|
|
215
215
|
- **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.
|
|
216
|
+
- **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 and PAUSE on the console of PgBouncer.
|
|
217
|
+
- A signal goes to each process of `ProxyHop.program`. 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.
|
|
218
|
+
- 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.
|
|
219
|
+
- 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.
|
|
216
220
|
- **A fault on a group** acts on the part that `Cell.faultAt` names by its role:
|
|
217
221
|
- A black hole (`blackhole`) drops each segment between the part and each peer. A reset (`rst`) answers each segment from a peer with a reset, thus the part refuses each connection.
|
|
218
222
|
- A partial partition (`partition`) is a black hole between the part and some of its peers (`GroupPlace.cuts`). `group` cuts the part from the other members and the Sentinels, and `clients` cuts it from the client.
|
|
@@ -335,6 +339,7 @@ The distribution test needs a simulator, which does not exist at this time.
|
|
|
335
339
|
- A replica set cell opens a replica set environment (`openReplicaSetLab`), and each run is `replicaSetOnce`. A run with a hold continues to the end of the hold, thus its record shows what the set did in all of it.
|
|
336
340
|
- A driver cell (`prediction.driver`) waits for the end of its band and of each later band. A silent driver cell waits for its window.
|
|
337
341
|
- **The observer.** A cell observes the client, the server, or a socket of a proxy toward one side (`observer`). A cell on the server side measures the socket of the target for the connection of the client. The run waits for the error of that socket. `freeze-process` freezes the peer of the observer: the target for the client, the client for the server.
|
|
342
|
+
- A socket of a proxy toward the target is the socket that carries the request of the run. It is the first socket of the hop that sends data to the target after the request. A pooler can reuse a server connection from the warm-up of the environment. Then the first socket that it opens in the run is not the socket of the request. A run without such a socket uses the first socket that the run opens.
|
|
338
343
|
- A cell on the server side sets the sysctls of the target through `targetSysctls`, with a privileged sidecar in the namespace of the target. That sidecar starts only when a cell asks for it.
|
|
339
344
|
- Its in-flight data is the reply of the server. Thus the client sends `probe.request` first, for example a BLPOP with a timeout, and the fault occurs while the server holds it.
|
|
340
345
|
- **The pause before the fault.** In every other run, the fault comes 250 ms after the client's first reply (`QUIET_MS`). That is more than Linux's longest delayed ACK, 200 ms. A fault that comes earlier meets the client's delayed ACK of the reply, not the request or the idle time that the cell measures. On a fast host the fault came 35 ms after the reply, and the ACK met a reset first.
|
|
@@ -30,6 +30,9 @@ import { KernelRecord } from "@mark1russell7/prober";
|
|
|
30
30
|
* target (`Target.close`): `CLIENT KILL` in Redis.
|
|
31
31
|
* - `none` injects nothing. A calibration cell measures the path itself: for example the time of a
|
|
32
32
|
* proxy between its two sockets (`Cell.calibrates`).
|
|
33
|
+
* - `hop-event` is an event at a proxy of the topology (`Cell.event`): a signal to its program, or a
|
|
34
|
+
* query on its console. Examples are a reload of the configuration (SIGHUP) and PAUSE on the
|
|
35
|
+
* console of PgBouncer.
|
|
33
36
|
*
|
|
34
37
|
* A fault at a router of the topology (`Cell.faultAt`) acts on what the router forwards toward
|
|
35
38
|
* one side. It is a black hole, a reset or an ICMP unreachable. It can also be a simplex drop, an
|
|
@@ -51,7 +54,7 @@ import { KernelRecord } from "@mark1russell7/prober";
|
|
|
51
54
|
* This fault is of a replica set (`Cell.replicaSet`):
|
|
52
55
|
* - `step-down` sends `replSetStepDown` to the primary, as a client sends a command.
|
|
53
56
|
*/
|
|
54
|
-
type Fault = "blackhole" | "freeze-process" | "rst" | "stall" | "fin" | "unreachable" | "simplex" | "mtu-blackhole" | "netem" | "middlebox-forget" | "proxy-idle" | "toxic" | "server-close" | "partition" | "planned-failover" | "app-stall" | "step-down" | "none";
|
|
57
|
+
type Fault = "blackhole" | "freeze-process" | "rst" | "stall" | "fin" | "unreachable" | "simplex" | "mtu-blackhole" | "netem" | "middlebox-forget" | "proxy-idle" | "toxic" | "server-close" | "partition" | "planned-failover" | "app-stall" | "step-down" | "hop-event" | "none";
|
|
55
58
|
/**
|
|
56
59
|
* A middlebox form tells what a router does with a flow that it forgot, when a late packet comes:
|
|
57
60
|
* - `reset-both` sends a reset to the two ends when it forgets the flow.
|
|
@@ -115,7 +118,31 @@ interface ProxyHop {
|
|
|
115
118
|
readonly sysctls?: Readonly<Record<string, string>>;
|
|
116
119
|
/** These are the settings of the proxy, for the manifest: for example its idle timeouts. */
|
|
117
120
|
readonly settings?: Readonly<Record<string, string>>;
|
|
121
|
+
/** This name is the process of the proxy, as /proc/PID/comm holds it, for a signal of a `hop-event`. */
|
|
122
|
+
readonly program?: string;
|
|
118
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* An event at a proxy hop (`hop-event`). A signal goes to each process of the program of the hop
|
|
126
|
+
* (`ProxyHop.program`). A query goes to the console of the hop from its own namespace, as a user
|
|
127
|
+
* that the console trusts. Its `undo` lifts it after the run. Without `inFlight`, the event
|
|
128
|
+
* comes first, and the request of the client meets it. With `inFlight`, the request goes first,
|
|
129
|
+
* and the event comes while it is in flight.
|
|
130
|
+
*/
|
|
131
|
+
type HopEvent = {
|
|
132
|
+
readonly hop: string;
|
|
133
|
+
readonly signal: "HUP";
|
|
134
|
+
readonly inFlight?: true;
|
|
135
|
+
} | {
|
|
136
|
+
readonly hop: string;
|
|
137
|
+
readonly console: {
|
|
138
|
+
readonly port: number;
|
|
139
|
+
readonly user: string;
|
|
140
|
+
readonly database: string;
|
|
141
|
+
readonly query: string;
|
|
142
|
+
readonly undo?: string;
|
|
143
|
+
};
|
|
144
|
+
readonly inFlight?: true;
|
|
145
|
+
};
|
|
119
146
|
/**
|
|
120
147
|
* A hop is one box between the client and the target. Each hop is on two networks: the network
|
|
121
148
|
* of the side of the client, and the network of the side of the target.
|
|
@@ -828,6 +855,8 @@ interface Cell {
|
|
|
828
855
|
readonly idleBefore?: Quantity;
|
|
829
856
|
/** This toxic is the toxic of a `toxic` fault, which the Toxiproxy hop gets. */
|
|
830
857
|
readonly toxic?: Toxic;
|
|
858
|
+
/** This event is the event of a `hop-event` fault, at a proxy of the topology. */
|
|
859
|
+
readonly event?: HopEvent;
|
|
831
860
|
/** This image, or this client of the harness, replaces the TCP client of the harness for each run. An HTTP client is one. */
|
|
832
861
|
readonly client?: ClientImage | Client;
|
|
833
862
|
/**
|
|
@@ -1022,5 +1051,5 @@ interface RunTopology {
|
|
|
1022
1051
|
}>>;
|
|
1023
1052
|
}
|
|
1024
1053
|
//#endregion
|
|
1025
|
-
export {
|
|
1026
|
-
//# sourceMappingURL=cell-
|
|
1054
|
+
export { WsProbe as $, Reconnects as A, RunGroup as B, MiddleboxForm as C, ProbeEvent as D, Prediction as E, Replies as F, Shipped as G, RunTopology as H, RequestPrediction as I, StageBand as J, ShippedImage as K, RouterHop as L, Reference as M, ReplicaSet as N, ProxyHop as O, ReplicaSetStage as P, Toxic as Q, RttRange as R, MiddleboxExpectation as S, PinnedImage as T, SentinelEvent as U, RunPart as V, SentinelStage as W, Target as X, Subject as Y, Topology as Z, HttpProbe as _, DriverBand as a, LineMatch as b, FaultPlace as c, GroupPrediction as d, isHopPlace as et, GroupRole as f, HopPlace as g, HopEvent as h, ClientImage as i, Recovers as j, ProxyTime as k, Group as l, Hop as m, Cell as n, DriverStep as o, HarnessImage as p, Stage as q, Client as r, Fault as s, BuildImage as t, latestEndOf as tt, GroupPlace as u, ImageSource as v, Observer as w, MiddleboxEvent as x, KernelRecord as y, Run as z };
|
|
1055
|
+
//# sourceMappingURL=cell-DOoU1LRf.d.ts.map
|
package/build/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as WsProbe, A as Reconnects, B as RunGroup, C as MiddleboxForm, D as ProbeEvent, E as Prediction, F as Replies, G as Shipped, H as RunTopology, I as RequestPrediction, J as StageBand, K as ShippedImage, L as RouterHop, M as Reference, N as ReplicaSet, O as ProxyHop, P as ReplicaSetStage, Q as Toxic, R as RttRange, S as MiddleboxExpectation, T as PinnedImage, U as SentinelEvent, V as RunPart, W as SentinelStage, X as Target, Y as Subject, Z as Topology, _ as HttpProbe, a as DriverBand, b as LineMatch, c as FaultPlace, d as GroupPrediction, et as isHopPlace, f as GroupRole, g as HopPlace, h as HopEvent, i as ClientImage, j as Recovers, k as ProxyTime, l as Group, m as Hop, n as Cell, o as DriverStep, p as HarnessImage, q as Stage, r as Client, s as Fault, t as BuildImage, tt as latestEndOf, u as GroupPlace, v as ImageSource, w as Observer, x as MiddleboxEvent, y as KernelRecord, z as Run } from "./cell-DOoU1LRf.js";
|
|
2
2
|
import { Quantity } from "@mark1russell7/quantity";
|
|
3
3
|
import "testcontainers";
|
|
4
4
|
//#region src/application.d.ts
|
|
@@ -417,7 +417,8 @@ interface SocketPlace {
|
|
|
417
417
|
* This function finds the socket of the observer of a cell in a run:
|
|
418
418
|
* - the client socket, by the address that it connected from, in its namespace.
|
|
419
419
|
* - the socket of the target for the same connection, in its namespace.
|
|
420
|
-
* - a socket of a proxy of the topology, toward the client or toward the target.
|
|
420
|
+
* - a socket of a proxy of the topology, toward the client or toward the target. Toward the target,
|
|
421
|
+
* it is the socket that carries the request, else the first socket that the run opens.
|
|
421
422
|
*/
|
|
422
423
|
export declare const socketOf: (cell: Cell, run: Run) => SocketPlace | undefined;
|
|
423
424
|
/** This function gives what reordering did in a run, from the records of the receiver and of the sender. */
|
|
@@ -910,6 +911,16 @@ interface Lab {
|
|
|
910
911
|
t0: number;
|
|
911
912
|
t1: number;
|
|
912
913
|
}>;
|
|
914
|
+
/**
|
|
915
|
+
* This method applies an event at a proxy hop (`HopEvent`), or its undo. A signal goes from a
|
|
916
|
+
* sidecar in the network and process namespaces of the hop. The lab starts that sidecar at the
|
|
917
|
+
* first signal of the hop. A query goes from the injector of the hop to its console on 127.0.0.1. The
|
|
918
|
+
* injector stamps each one. An event with no undo gives null for its undo.
|
|
919
|
+
*/
|
|
920
|
+
event(event: HopEvent, undo?: boolean): Promise<{
|
|
921
|
+
t0: number;
|
|
922
|
+
t1: number;
|
|
923
|
+
} | null>;
|
|
913
924
|
probe(args: readonly string[]): Promise<Probe>;
|
|
914
925
|
/**
|
|
915
926
|
* This method waits for the next clock line of the prober, which gives the count of lost
|
|
@@ -1421,5 +1432,5 @@ export declare const spread: (runs: number, environments: number) => number[];
|
|
|
1421
1432
|
*/
|
|
1422
1433
|
export declare const runCell: (cell: Cell, say?: (line: string) => void) => Promise<CellResult>;
|
|
1423
1434
|
//#endregion
|
|
1424
|
-
export { type Agent, type BuildImage, type CallReading, type Cell, type CellResult, type Client, type ClientImage, type Crossings, type Cut, type Decision, type DriverBand, type DriverSocket, type DriverStep, type DriverStepTime, type ErrorOnTheWay, type Fault, type FaultPlace, type FaultSide, type Group, type GroupClient, type GroupLab, type GroupMeasured, type GroupNetworkFault, type GroupPlace, type GroupPrediction, type GroupReading, type GroupRole, type GroupVerdict, type GroupView, type HarnessImage, type Hop, type HopPlace, type HttpProbe, type ImagePlan, type ImageSource, type Interval, type KernelRecord, type Lab, type LineMatch, type Manifest, type ManifestHop, type Measured, type MemberLine, type MiddleboxEvent, type MiddleboxExpectation, type MiddleboxForm, type NetworkFault, type Observer, type Outcome, type PinnedImage, type Prediction, type Probe, type ProbeEvent, type ProgramEvent, type ProxyHop, type ProxyTime, type ReconnectReading, type ReconnectVerdict, type Reconnects, type Recovers, type Reference, type Reordering, type ReplicaSet, type ReplicaSetLab, type ReplicaSetStage, type Replies, type RequestPrediction, type RequestReading, type RouterHop, type RttRange, type Run, type RunGroup, type RunPart, type RunTopology, type SentinelEvent, type SentinelStage, type Shipped, type ShippedImage, type SocketPlace, type Spread, type Stage, type StageBand, type StageVerdict, type Stamp, type Status, type Subject, type Target, type Topology, type Toxic, type Verdict, type Writes, type WsProbe, isHopPlace, latestEndOf };
|
|
1435
|
+
export { type Agent, type BuildImage, type CallReading, type Cell, type CellResult, type Client, type ClientImage, type Crossings, type Cut, type Decision, type DriverBand, type DriverSocket, type DriverStep, type DriverStepTime, type ErrorOnTheWay, type Fault, type FaultPlace, type FaultSide, type Group, type GroupClient, type GroupLab, type GroupMeasured, type GroupNetworkFault, type GroupPlace, type GroupPrediction, type GroupReading, type GroupRole, type GroupVerdict, type GroupView, type HarnessImage, type Hop, type HopEvent, type HopPlace, type HttpProbe, type ImagePlan, type ImageSource, type Interval, type KernelRecord, type Lab, type LineMatch, type Manifest, type ManifestHop, type Measured, type MemberLine, type MiddleboxEvent, type MiddleboxExpectation, type MiddleboxForm, type NetworkFault, type Observer, type Outcome, type PinnedImage, type Prediction, type Probe, type ProbeEvent, type ProgramEvent, type ProxyHop, type ProxyTime, type ReconnectReading, type ReconnectVerdict, type Reconnects, type Recovers, type Reference, type Reordering, type ReplicaSet, type ReplicaSetLab, type ReplicaSetStage, type Replies, type RequestPrediction, type RequestReading, type RouterHop, type RttRange, type Run, type RunGroup, type RunPart, type RunTopology, type SentinelEvent, type SentinelStage, type Shipped, type ShippedImage, type SocketPlace, type Spread, type Stage, type StageBand, type StageVerdict, type Stamp, type Status, type Subject, type Target, type Topology, type Toxic, type Verdict, type Writes, type WsProbe, isHopPlace, latestEndOf };
|
|
1425
1436
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.js
CHANGED
|
@@ -5,6 +5,11 @@ import { createHash } from "node:crypto";
|
|
|
5
5
|
/** This function tells if a fault place is a hop of a topology. */
|
|
6
6
|
const isHopPlace = (place) => typeof place === "object" && "hop" in place;
|
|
7
7
|
/**
|
|
8
|
+
* This function tells if the request of a client goes before the fault of its cell. That is so for a
|
|
9
|
+
* hop event that comes while the request is in flight (`HopEvent.inFlight`). Then a request after the fault is void.
|
|
10
|
+
*/
|
|
11
|
+
const requestFirst = (cell) => cell.fault === "hop-event" && cell.event?.inFlight === true;
|
|
12
|
+
/**
|
|
8
13
|
* This function gives the latest end of a band in seconds, from the reply of the client. It is
|
|
9
14
|
* the high bound, after the latest reference of the band (`startsBy`).
|
|
10
15
|
*/
|
|
@@ -898,7 +903,9 @@ const measureDriver = (cell, run) => {
|
|
|
898
903
|
let voided = null;
|
|
899
904
|
if (cell.probe.mode !== "idle" && cell.before !== true) {
|
|
900
905
|
if (request === void 0) voided = "the operation was never sent into the fault";
|
|
901
|
-
else if (
|
|
906
|
+
else if (requestFirst(cell)) {
|
|
907
|
+
if (run.fault !== null && run.fault.t0 < request.t) voided = "the event came before the operation";
|
|
908
|
+
} else if (run.fault !== null && run.fault.t1 > request.t) voided = "the operation went out before the fault held";
|
|
902
909
|
}
|
|
903
910
|
const keepalive = records.find((record) => record.ev === "probe" && record.what === "keepalive");
|
|
904
911
|
if (voided === null && run.fault !== null && keepalive !== void 0 && keepalive.mono < run.fault.t1) voided = "a keepalive probe went out before the fault held";
|
|
@@ -1421,6 +1428,21 @@ const opened = (run, netns, peer) => {
|
|
|
1421
1428
|
cookie: record.cookie
|
|
1422
1429
|
};
|
|
1423
1430
|
};
|
|
1431
|
+
/**
|
|
1432
|
+
* This function finds the socket of a hop that carries the request of a run toward the target. It is
|
|
1433
|
+
* the first socket of the hop that sends data to the target after the send of the request. A pooler can
|
|
1434
|
+
* reuse a server that it opened before the run, in the warm-up of the environment. Then the first
|
|
1435
|
+
* socket that it opens in the run is another one (run 39160356281).
|
|
1436
|
+
*/
|
|
1437
|
+
const carried = (run, netns, target) => {
|
|
1438
|
+
const request = run.events.filter((event) => event.ev === "sent")[1];
|
|
1439
|
+
if (request === void 0) return void 0;
|
|
1440
|
+
const record = run.kernel.find((each) => each.netns === netns && each.ev === "tx" && each.dst === target && (each.seg?.len.value ?? 0) > 0 && each.mono >= request.t);
|
|
1441
|
+
return record?.cookie === void 0 ? void 0 : {
|
|
1442
|
+
netns,
|
|
1443
|
+
cookie: record.cookie
|
|
1444
|
+
};
|
|
1445
|
+
};
|
|
1424
1446
|
/** This function finds the socket of the target for the connection of a run. In a topology, its peer is the last hop. */
|
|
1425
1447
|
const targetSocket = (run, local) => {
|
|
1426
1448
|
if (run.topology === void 0) {
|
|
@@ -1437,7 +1459,8 @@ const targetSocket = (run, local) => {
|
|
|
1437
1459
|
* This function finds the socket of the observer of a cell in a run:
|
|
1438
1460
|
* - the client socket, by the address that it connected from, in its namespace.
|
|
1439
1461
|
* - the socket of the target for the same connection, in its namespace.
|
|
1440
|
-
* - a socket of a proxy of the topology, toward the client or toward the target.
|
|
1462
|
+
* - a socket of a proxy of the topology, toward the client or toward the target. Toward the target,
|
|
1463
|
+
* it is the socket that carries the request, else the first socket that the run opens.
|
|
1441
1464
|
*/
|
|
1442
1465
|
const socketOf = (cell, run) => {
|
|
1443
1466
|
const local = run.events.find((event) => event.ev === "connected")?.["local"];
|
|
@@ -1452,7 +1475,7 @@ const socketOf = (cell, run) => {
|
|
|
1452
1475
|
if (observer === "server") return targetSocket(run, local);
|
|
1453
1476
|
const hop = run.topology?.hops[observer.hop];
|
|
1454
1477
|
if (hop === void 0) return void 0;
|
|
1455
|
-
return observer.toward === "client" ? opened(run, hop.netns, (dst) => ipOf(dst) === hop.client) : opened(run, hop.netns, (dst) => dst === hop.target);
|
|
1478
|
+
return observer.toward === "client" ? opened(run, hop.netns, (dst) => ipOf(dst) === hop.client) : carried(run, hop.netns, hop.target) ?? opened(run, hop.netns, (dst) => dst === hop.target);
|
|
1456
1479
|
};
|
|
1457
1480
|
/**
|
|
1458
1481
|
* This function finds the socket of the peer of the observer: its close is the reference of a FIN.
|
|
@@ -1689,7 +1712,7 @@ const measureSocket = (cell, run) => {
|
|
|
1689
1712
|
};
|
|
1690
1713
|
let voided = null;
|
|
1691
1714
|
if (run.fault !== null) {
|
|
1692
|
-
const replyFirst = server && inflight && cell.fault !== "mtu-blackhole";
|
|
1715
|
+
const replyFirst = server && inflight && cell.fault !== "mtu-blackhole" || requestFirst(cell);
|
|
1693
1716
|
const replied = events.some((event) => event.ev === "data" && event.t > request.t && event.t < run.fault.t1);
|
|
1694
1717
|
if (inflight && !replyFirst && run.fault.t1 > request.t) voided = "the request went out before the fault held";
|
|
1695
1718
|
else if (replyFirst && run.fault.t1 < request.t) voided = "the request went out into the fault, so the server never had it";
|
|
@@ -2413,6 +2436,7 @@ const openLab = async (target, kind = "tcp", topology) => {
|
|
|
2413
2436
|
const paused = runtime.container.getById(server.getId());
|
|
2414
2437
|
let targetAdmin;
|
|
2415
2438
|
let signaller;
|
|
2439
|
+
const hopSignallers = /* @__PURE__ */ new Map();
|
|
2416
2440
|
const hopNamed = (name) => {
|
|
2417
2441
|
const found = inChain.find((hop) => hop.hop.name === name);
|
|
2418
2442
|
if (found === void 0) throw new Error(`the topology has no hop ${name}`);
|
|
@@ -2688,6 +2712,38 @@ const openLab = async (target, kind = "tcp", topology) => {
|
|
|
2688
2712
|
...when
|
|
2689
2713
|
]);
|
|
2690
2714
|
},
|
|
2715
|
+
async event(event, undo = false) {
|
|
2716
|
+
const hop = hopNamed(event.hop);
|
|
2717
|
+
if (hop.hop.kind !== "proxy") throw new Error(`the hop ${event.hop} is a router: an event goes to a proxy`);
|
|
2718
|
+
if ("signal" in event) {
|
|
2719
|
+
if (undo) return null;
|
|
2720
|
+
const program = hop.hop.program;
|
|
2721
|
+
if (program === void 0) throw new Error(`the hop ${event.hop} names no program to signal`);
|
|
2722
|
+
let sidecar = hopSignallers.get(event.hop);
|
|
2723
|
+
if (sidecar === void 0) {
|
|
2724
|
+
sidecar = await withPid(new GenericContainer(image).withNetworkMode(`container:${hop.box.getId()}`).withAddedCapabilities("NET_ADMIN", "NET_RAW"), `container:${hop.box.getId()}`).start();
|
|
2725
|
+
hopSignallers.set(event.hop, sidecar);
|
|
2726
|
+
opened.push(sidecar);
|
|
2727
|
+
}
|
|
2728
|
+
return stamped(sidecar, [
|
|
2729
|
+
"/opt/harness/inject",
|
|
2730
|
+
"signal",
|
|
2731
|
+
program,
|
|
2732
|
+
event.signal
|
|
2733
|
+
]);
|
|
2734
|
+
}
|
|
2735
|
+
const query = undo ? event.console.undo : event.console.query;
|
|
2736
|
+
if (query === void 0) return null;
|
|
2737
|
+
const { port, user, database } = event.console;
|
|
2738
|
+
return stamped(hop.injector, [
|
|
2739
|
+
"/opt/harness/inject",
|
|
2740
|
+
"query",
|
|
2741
|
+
`127.0.0.1:${port}`,
|
|
2742
|
+
user,
|
|
2743
|
+
database,
|
|
2744
|
+
query
|
|
2745
|
+
]);
|
|
2746
|
+
},
|
|
2691
2747
|
async probe(args) {
|
|
2692
2748
|
const events = lines();
|
|
2693
2749
|
const container = await new GenericContainer(clientImage).withNetworkMode(`container:${client.getId()}`).withCommand([...command, ...args]).withLogConsumer(events.consume).start();
|
|
@@ -4380,7 +4436,7 @@ const once = async (lab, cell, environment, index, dry = false) => {
|
|
|
4380
4436
|
if (cell.probe.mode !== "idle") await go();
|
|
4381
4437
|
await sleep(500);
|
|
4382
4438
|
} else {
|
|
4383
|
-
const replyInFlight = cell.probe.mode === "inflight" && (server && cell.fault !== "mtu-blackhole" || cell.fault === "fin");
|
|
4439
|
+
const replyInFlight = cell.probe.mode === "inflight" && (server && cell.fault !== "mtu-blackhole" || cell.fault === "fin" || requestFirst(cell));
|
|
4384
4440
|
if (replyInFlight) {
|
|
4385
4441
|
await go();
|
|
4386
4442
|
await sleep(200);
|
|
@@ -4431,6 +4487,9 @@ const once = async (lab, cell, environment, index, dry = false) => {
|
|
|
4431
4487
|
case "server-close":
|
|
4432
4488
|
await lab.serverClose();
|
|
4433
4489
|
return null;
|
|
4490
|
+
case "hop-event":
|
|
4491
|
+
if (cell.event === void 0) throw new Error(`${cell.name} names no event`);
|
|
4492
|
+
return lab.event(cell.event);
|
|
4434
4493
|
default: return lab.fault("blackhole", faultAt);
|
|
4435
4494
|
}
|
|
4436
4495
|
};
|
|
@@ -4449,6 +4508,9 @@ const once = async (lab, cell, environment, index, dry = false) => {
|
|
|
4449
4508
|
case "server-close":
|
|
4450
4509
|
case "app-stall":
|
|
4451
4510
|
case "none": return;
|
|
4511
|
+
case "hop-event":
|
|
4512
|
+
if (cell.event !== void 0) await lab.event(cell.event, true);
|
|
4513
|
+
return;
|
|
4452
4514
|
case "unreachable": return lab.lift("reject", faultAt, { code: cell.icmp ?? "host" });
|
|
4453
4515
|
case "simplex": return lab.lift("blackhole", faultAt, options);
|
|
4454
4516
|
case "mtu-blackhole": return lab.lift("mtu", cell.faultAt ?? "client", { above: cell.mtu ?? 1200 });
|