@mark1russell7/harness 0.14.4 → 0.15.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 +1 -0
- package/build/{cell-DZm8zXN4.d.ts → cell-biffSiEJ.d.ts} +178 -4
- package/build/{image-EZ8ioZ3L.js → image-BxVTFZ1o.js} +23 -2
- package/build/image-BxVTFZ1o.js.map +1 -0
- package/build/index.d.ts +293 -5
- package/build/index.js +895 -32
- package/build/index.js.map +1 -1
- package/build/schema.d.ts +5 -2
- package/build/schema.js +7 -3
- package/build/schema.js.map +1 -1
- package/image/Dockerfile +3 -3
- package/image/kafka/AdminKafka.java +44 -9
- package/image/kafka/ConsumerKafka.java +359 -0
- package/image/kafka/Dockerfile +2 -2
- package/image/native/Cargo.toml +1 -1
- package/image/native/src/bin/client.rs +164 -28
- package/image/native/src/bin/ctfull.rs +633 -0
- package/image/native/src/bin/inject.rs +16 -0
- package/image/native/src/bin/load.rs +481 -0
- package/image/native/src/bin/ports.rs +1 -8
- package/image/native/src/bin/queue.rs +1 -12
- package/image/native/src/lib.rs +51 -0
- package/package.json +5 -2
- package/build/image-EZ8ioZ3L.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-BxVTFZ1o.js","names":[],"sources":["../src/cell.ts","../src/locks.ts","../src/image.ts"],"sourcesContent":["import type { KernelRecord } from \"@mark1russell7/prober\";\nimport type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { Profile, Schedule, ScheduleOptions } from \"@mark1russell7/rigor-chaos\";\n\nexport type { KernelRecord };\n\n/**\n * A fault is a failure that the harness injects, by the name of its manifestation. Each name is\n * the last segment of the identifier of the manifestation in the corpus:\n * - `blackhole` is two DROP rules in the network namespace of the target. Thus the measurement\n * gets the kernel timers of the client.\n * - `freeze-process` is the cgroup freezer on the target, while its kernel stays up.\n * - `rst` answers the next segment of the client with a reset, from the namespace of the target.\n * It is the reference for the noise of the harness itself.\n * - `stall` is a black hole that stops by itself, `Cell.stall` after the client sends its\n * request. The VM times it. It shows what recovers, and when.\n * - `fin` stops the process of the peer with SIGKILL, while its kernel stays up. The kernel closes its sockets\n * with a FIN, or with a reset when unread data is in the queue.\n * - `unreachable` answers each segment from the observer with an ICMP unreachable (`Cell.icmp`).\n * Linux keeps it as a soft error on an established socket, and it continues to retransmit.\n * - `simplex` drops the segments in one direction only (`Cell.drops`).\n * - `mtu-blackhole` drops each segment to the observer that is longer than `Cell.mtu` bytes.\n * - `netem` is tc netem on the egress of the target (`Cell.netem`): loss, duplication,\n * reordering or corruption. Its outcome is a distribution.\n *\n * These faults are of a topology (`Cell.topology`):\n * - `middlebox-forget` injects nothing. The router of the topology forgets an idle flow at its own\n * idle timeout, and its form tells what a late packet then meets.\n * - `proxy-idle` injects nothing. The proxy of the topology closes an idle connection at its own\n * timeout.\n * - `toxic` adds a toxic of Toxiproxy (`Cell.toxic`) through its HTTP API.\n * - `server-close` makes the server close the connection of its peer, with the command of the\n * target (`Target.close`): `CLIENT KILL` in Redis.\n * - `none` injects nothing. A calibration cell measures the path itself: for example the time of a\n * proxy between its two sockets (`Cell.calibrates`).\n * - `hop-event` is an event at a proxy of the topology (`Cell.event`): a signal to its program, or a\n * query on its console. Examples are a reload of the configuration (SIGHUP), a stop of a proxy\n * (SIGTERM or SIGINT) and PAUSE on the console of PgBouncer.\n *\n * A fault at a router of the topology (`Cell.faultAt`) acts on what the router forwards toward\n * one side. It is a black hole, a reset or an ICMP unreachable. It can also be a simplex drop, an\n * MTU drop or netem.\n *\n * These faults are of a replicated group (`Cell.group` or `Cell.replicaSet`), on the part that `Cell.faultAt` names:\n * - `blackhole` drops each segment between the part and each of its peers. `rst` answers each\n * segment from a peer with a reset, thus the part refuses each connection.\n * - `partition` is a black hole between the part and some of its peers only (`GroupPlace.cuts`).\n * - `freeze-process` stops the server process of the part with SIGSTOP, and SIGCONT thaws it. Its\n * kernel stays up. `fin` stops the process with SIGKILL.\n * - `planned-failover` asks a Sentinel for a failover: `SENTINEL FAILOVER <group>`.\n *\n * This fault is of an HTTP server or of PostgreSQL:\n * - `app-stall` injects nothing. The server process holds the reply to the request, because the\n * request asks for that, while its kernel stays up. An HTTP request asks with `HttpProbe.hold`,\n * and a query of PostgreSQL with its text, for example `SELECT pg_sleep(20)`.\n *\n * This fault is of a replica set (`Cell.replicaSet`):\n * - `step-down` sends `replSetStepDown` to the primary, as a client sends a command.\n *\n * These faults are of a cluster of RabbitMQ (`Cell.cluster`):\n * - `step-down` moves the leadership of the queue to a follower: `rabbitmq-queues transfer_leadership`.\n * - `drain` puts the node of the part in maintenance mode: `rabbitmq-upgrade drain`. The node closes the\n * connections of its clients, and it moves the leadership of each of its queues to another node.\n * `rabbitmq-upgrade revive` ends it after the run.\n *\n * This fault is of a fabric (`Topology.fabric`):\n * - `silence` drops each packet of spine1 in one commit, and its links stay up. Each leaf loses its\n * neighbor only by a detector of its own, and the routes move to spine2. The measured move ends\n * the stall of the client (`Run.heal`).\n */\nexport type Fault =\n | \"blackhole\"\n | \"freeze-process\"\n | \"rst\"\n | \"stall\"\n | \"fin\"\n | \"unreachable\"\n | \"simplex\"\n | \"mtu-blackhole\"\n | \"netem\"\n | \"middlebox-forget\"\n | \"proxy-idle\"\n | \"toxic\"\n | \"server-close\"\n | \"partition\"\n | \"planned-failover\"\n | \"app-stall\"\n | \"step-down\"\n | \"drain\"\n | \"hop-event\"\n | \"silence\"\n | \"none\";\n\n/**\n * A middlebox form tells what a router does with a flow that it forgot, when a late packet comes:\n * - `reset-both` sends a reset to the two ends when it forgets the flow.\n * - `reset-next` answers the next packet from inside with a reset to its sender, and drops a late\n * packet from outside.\n * - `silent-drop` drops each late packet, in each direction.\n * - `recreate` lets a late packet from inside through, and the flow starts again. It drops a late\n * packet from outside.\n * - `none` is a plain NAT router. A late packet from inside starts the flow again, and the stack of\n * the router answers a late packet from outside with a reset.\n */\nexport type MiddleboxForm = \"reset-both\" | \"reset-next\" | \"silent-drop\" | \"recreate\" | \"none\";\n\n/**\n * A router is a hop that forwards and translates the addresses of the side of the client (NAT),\n * with conntrack. It is the router image of the harness.\n */\nexport interface RouterHop {\n readonly name: string;\n readonly kind: \"router\";\n readonly form: MiddleboxForm;\n /** This time is the idle timeout of an established flow: nf_conntrack_tcp_timeout_established, in whole seconds. */\n readonly idle: Quantity;\n /**\n * This time is the interval of the listing that evicts each expired flow. Without it, the\n * interval is 0.1 s. The DESTROY event of an expired flow waits for that listing.\n */\n readonly pump?: Quantity;\n /**\n * These are the sysctls of the namespace of the router, which the router sets itself: it is\n * privileged. An example is `net.ipv4.icmp_ratelimit` for the ICMP errors of a fault at the router.\n */\n readonly sysctls?: Readonly<Record<string, string>>;\n}\n\n/**\n * A proxy is a hop that ends each TCP connection from the side of the client and opens its own to\n * the next hop. It is a stock image, pinned by digest, or an image that the harness ships, for\n * example PgBouncer (`{ kind: \"shipped\", name: \"pgbouncer\" }`).\n */\nexport interface ProxyHop {\n readonly name: string;\n readonly kind: \"proxy\";\n readonly image: string | ImageSource;\n /** This port is the port that the proxy listens on for the side of the client. */\n readonly port: number;\n readonly command?: readonly string[];\n /** These variables are the environment of the proxy, over the environment of its image. */\n readonly environment?: Readonly<Record<string, string>>;\n /**\n * These files go into the proxy before it starts, by their path. In their content, {{next}} is\n * the address and port of the next hop, and {{next.host}} and {{next.port}} are each part.\n */\n readonly files?: Readonly<Record<string, string>>;\n /** This text is the log line that tells that the proxy serves. */\n readonly ready?: string;\n /** This record is the HTTP API of Toxiproxy: its port, and the name of the proxy that gets the toxics. */\n readonly api?: { readonly port: number; readonly proxy: string };\n /** These are the sysctls of the namespace of the proxy. A privileged sidecar sets them before each run. */\n readonly sysctls?: Readonly<Record<string, string>>;\n /** These are the settings of the proxy, for the manifest: for example its idle timeouts. */\n readonly settings?: Readonly<Record<string, string>>;\n /** This name is the process of the proxy, as /proc/PID/comm holds it, for a signal of a `hop-event`. */\n readonly program?: string;\n}\n\n/**\n * A signal of a hop event. HUP reloads the configuration of a pooler. TERM stops a proxy, as a stop of\n * its container does. INT stops docker-proxy, as Docker does when it removes a port binding.\n *\n * STOP blocks the event loop of a proxy: its processes stop, and its kernel keeps each socket. Thus\n * ACKs go on, and the data of each peer waits in its receive queue. With the hold of the cell\n * (`Cell.hold`), CONT thaws it at the end of the hold, from inside the VM.\n */\nexport type HopSignal = \"HUP\" | \"TERM\" | \"INT\" | \"STOP\" | \"CONT\";\n\n/**\n * An event at a proxy hop (`hop-event`). A signal goes to each process of the program of the hop\n * (`ProxyHop.program`). A query goes to the console of the hop from its own namespace, as a user\n * that the console trusts. Its `undo` lifts it after the run. Without `inFlight`, the event\n * comes first, and the request of the client meets it. With `inFlight`, the request goes first,\n * and the event comes while it is in flight.\n *\n * A write changes a file of the hop, as an edit of its configuration does (`HopWrite`).\n */\nexport type HopEvent =\n | { readonly hop: string; readonly signal: HopSignal; readonly inFlight?: true }\n | {\n readonly hop: string;\n readonly console: { readonly port: number; readonly user: string; readonly database: string; readonly query: string; readonly undo?: string };\n readonly inFlight?: true;\n }\n | { readonly hop: string; readonly write: HopWrite; readonly inFlight?: true };\n\n/**\n * A hop write puts `content` at `path` in the root of the program of the hop. The content goes to a\n * file beside it first, and a rename puts it in place in one step. Thus a program that watches the\n * folder reads the whole file, not half of it. Its `undo` writes the file of the start again\n * after the run, thus each run makes the same change.\n *\n * `confirm` is a line of the output of the hop that the change gives, for example the log line of\n * a reload. The event waits for a new such line, and a write that the hop does not confirm in 10 s\n * fails the run. Thus a cell that predicts no effect knows that the change took effect.\n */\nexport interface HopWrite {\n readonly path: string;\n readonly content: string;\n readonly undo?: string;\n readonly confirm?: string;\n}\n\n/**\n * A hop is one box between the client and the target. Each hop is on two networks: the network\n * of the side of the client, and the network of the side of the target.\n */\nexport type Hop = RouterHop | ProxyHop;\n\n/**\n * A fabric is a routed network on layer E3 (`fabric.ts`), by its timers. The detector of a fabric\n * is the BGP hold timer, or BFD when it has `bfd`.\n */\nexport interface Fabric {\n readonly keepalive: Quantity;\n readonly hold: Quantity;\n /** These are the receive and transmit interval and the multiplier of BFD, or null for no BFD. */\n readonly bfd: { readonly interval: Quantity; readonly multiplier: number } | null;\n}\n\n/** A fabric manifest tells what a fabric ran on: its timers, its image and containerlab. */\nexport interface FabricManifest extends Fabric {\n readonly image: string;\n readonly containerlab: string;\n}\n\n/**\n * A topology is the chain of hops between the client and the target, in sequence from the client.\n * Without hops, the client and the target are a direct pair. With `fabric`, a routed network\n * joins them, and the chain of hops is empty.\n */\nexport interface Topology {\n readonly name: string;\n readonly hops: readonly Hop[];\n readonly fabric?: Fabric;\n}\n\n/**\n * A group is a replicated group under an external failover authority, as data. It is a primary and\n * its replicas, and the Sentinels that decide which member is the primary. Each member and each\n * Sentinel is a container from the same stock image, pinned by digest. Each has its own network\n * namespace, thus a fault can act on one part. The client finds the primary through the\n * Sentinels, and the parts and the client share one network.\n */\nexport interface Group {\n /** This name is the name of the group that the Sentinels monitor, for example `mymaster`. */\n readonly name: string;\n /** This image is the stock image of each member and each Sentinel, pinned by digest. */\n readonly image: string;\n /** These are the programs of the image: the server, the Sentinel and the command line client. */\n readonly programs: { readonly server: string; readonly sentinel: string; readonly cli: string };\n readonly port: number;\n readonly sentinelPort: number;\n /** These are the names of the members. The first one is the primary when the environment starts. */\n readonly members: readonly string[];\n readonly sentinels: readonly string[];\n /** This count is how many Sentinels must agree that the primary is down. */\n readonly quorum: number;\n /** These are the settings of each member, as its configuration file spells them. The harness adds `replicaof` for each replica. */\n readonly memberSettings: Readonly<Record<string, string>>;\n /**\n * These are the settings of each Sentinel for the group, as `sentinel <setting> <group> <value>`\n * spells them: `down-after-milliseconds`, `failover-timeout` and `parallel-syncs`.\n */\n readonly sentinelSettings: Readonly<Record<string, string>>;\n /** These lines tell that a member and a Sentinel serve. */\n readonly ready: { readonly member: string; readonly sentinel: string };\n}\n\n/**\n * A replica set is a replicated group with no external authority, as data: its members elect their\n * own primary. Each member is a container from the same stock image, pinned by digest, in its own\n * network namespace. The client finds the primary by its own monitors. The harness starts the set\n * with the address of each member, and it waits for a primary.\n */\nexport interface ReplicaSet {\n /** This name is the name of the set, for example `rs0`. */\n readonly name: string;\n /** This image is the stock image of each member, pinned by digest. */\n readonly image: string;\n /** These are the programs of the image: the server and its shell. */\n readonly programs: { readonly server: string; readonly shell: string };\n /** These are the options of the server of each member, after the name of the set: for example the size of its cache. */\n readonly args?: readonly string[];\n readonly port: number;\n /** These are the names of the members. The first one starts the set. */\n readonly members: readonly string[];\n /** These are the settings of the configuration of the set, as `rs.initiate` takes them: for example `electionTimeoutMillis`. */\n readonly settings: Readonly<Record<string, number>>;\n /** This line tells that a member serves. */\n readonly ready: string;\n}\n\n/**\n * A drill is a recovery drill of a replica set cell, as data. The store gets generated data for\n * each tenant and real backups while a client writes. Then the fault removes the disk of a member,\n * or of each member, and the store recovers. A drill shortens the interval of the backups. The\n * loss of a backup depends on the age of the newest backup at the fault, and `age` sets that age.\n */\nexport interface Drill {\n /** This count is the number of tenants. The size of the store follows it. */\n readonly tenants: number;\n /** This size is the generated data of one tenant. */\n readonly perTenant: Quantity;\n /** This count is the number of slices of the oplog after the dump. */\n readonly slices: number;\n /** This time is the interval from the dump to the first slice, and between two slices. */\n readonly sliceEvery: Quantity;\n /** This time is the age of the newest backup at the fault: from its last write, or from its end in the control. */\n readonly age: Quantity;\n /** This word tells how the store recovers. An election follows the loss of the disk of the primary. A restore from the backups follows the loss of each disk. */\n readonly recovery: \"election\" | \"restore\";\n /** This flag makes the drill a control: the client stops before the dump, thus no write comes after it. */\n readonly quiet?: true;\n}\n\n/**\n * A cluster is a cluster of RabbitMQ nodes with one quorum queue, as data. The members of the queue\n * elect its leader by Raft, one member on each node. Each node is a container from the same stock\n * image, pinned by digest, in its own network namespace. The nodes form the cluster from a fixed\n * list of names. The client publishes a stream of sequence numbers to the queue, each with a\n * confirm, through one node. When its connection ends, it connects to the next node.\n */\nexport interface Cluster {\n /** This image is the stock image of each node, pinned by digest. */\n readonly image: string;\n /** These are the names of the nodes: each node is `rabbit@<name>`. The first node declares the queue, thus its member is the first leader. */\n readonly members: readonly string[];\n readonly port: number;\n /** This text is the Erlang cookie that each node shares. */\n readonly cookie: string;\n /** These are lines of rabbitmq.conf after the formation of the cluster, for example `raft.adaptive_failure_detector.poll_interval = 1000`. */\n readonly conf: readonly string[];\n /** This name is the name of the quorum queue. */\n readonly queue: string;\n /** This line tells that a node serves. */\n readonly ready: string;\n /** These are the programs of the image: the server, whose process a signal acts on, and the command that starts the node again. */\n readonly programs: { readonly server: string; readonly start: string };\n}\n\n/**\n * A Kafka group is a group of brokers of Apache Kafka in KRaft mode with one controller, as data.\n * Each node is a container from the same stock image, pinned by digest, in its own network namespace.\n * Each run makes one new topic with one partition, with a replica on each broker. The client is the\n * producer of kafka-clients, and it writes a stream of sequence numbers to that partition.\n * The one controller keeps a Raft election out of each run. A fault on a broker cuts it from the\n * controller too, and no fault acts on the controller.\n */\nexport interface Kafka {\n /** This image is the stock image of each node, pinned by digest. */\n readonly image: string;\n /** These are the names of the brokers. A broker's node id is its place in this list, from 1. The first broker is the first leader. */\n readonly brokers: readonly string[];\n /** This name is the name of the controller. Its node id comes after the ids of the brokers. */\n readonly controller: string;\n readonly port: number;\n readonly controllerPort: number;\n /** This text is the id of the cluster: 16 bytes in base64url without padding. */\n readonly clusterId: string;\n /** This text is the value of KAFKA_HEAP_OPTS of each node. */\n readonly heap: string;\n /** These are server properties of each node, by their names in Kafka: for example `broker.session.timeout.ms`. */\n readonly settings: Readonly<Record<string, string>>;\n /** This line tells that a node serves. */\n readonly ready: string;\n /** This count is the setting `min.insync.replicas` of the topic of each run. */\n readonly minInsync: number;\n /** This count is the number of writers of the client, each with one write at a time. */\n readonly writers: number;\n /** This record makes each run a run of two members of a consumer group, with one member stopped. Without it, the client is a producer. */\n readonly pause?: KafkaPause;\n}\n\n/**\n * A Kafka pause is one run of two members of a consumer group, on a topic with two partitions. The\n * fault stops the process of the first member while it holds a batch, and the hold of the cell ends\n * that stop. Each setting of a member comes from `Cell.driverOptions`.\n */\nexport interface KafkaPause {\n /** This name is the protocol of the group: the broker keeps the session under `consumer`, and the client sets it under `classic`. */\n readonly protocol: \"consumer\" | \"classic\";\n /** This time is how long the first member holds each batch before it commits. */\n readonly process: Quantity;\n /** This choice is what the first member does after the stop. It commits at once, or it polls until it has a new epoch and then commits. */\n readonly afterPause: \"commit\" | \"rejoin\";\n /** This count is the number of records in each partition before the members start. */\n readonly fill: number;\n /** This time is from the start of the processing of a batch to the stop. */\n readonly stopAfter: Quantity;\n}\n\n/** A pause band is the band of one time of a paused consumer, from the stop. */\nexport interface PauseBand {\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/**\n * A pause prediction is the law of a paused consumer. A null fence tells that the stop is too short.\n * The group then does not fence the member, and the other member does not get its partition.\n */\nexport interface PausePrediction {\n readonly fence: PauseBand | null;\n /** This band is the first report of the partition by the other member. Null tells that this cell has no band for it. */\n readonly takeover: PauseBand | null;\n /**\n * This choice is the result of the commit after the stop:\n * - `fails`: a commit before any poll fails with `CommitFailedException`.\n * - `acknowledged`: the commit succeeds, and the epoch of the member does not change.\n * - `rejoin`: the member gets a new epoch, the same commit succeeds, and the committed offset moves back.\n */\n readonly thaw: \"fails\" | \"acknowledged\" | \"rejoin\";\n /** This time bounds the new epoch of `rejoin`, from the end of the stop. */\n readonly rejoinWithin?: Quantity;\n}\n\n/** A run pause is what a run of a paused consumer records beyond the lines of the stopped member. */\nexport interface RunPause {\n /** These are the lines of the other member. */\n readonly other: readonly ProbeEvent[];\n /** These are the log lines of the brokers that tell that a member left this group. */\n readonly fences: readonly string[];\n /** This name is the group of the run. */\n readonly group: string;\n}\n\n/**\n * A cluster stage is one step of the timeline of a cluster after a fault. A line of the log of a\n * node marks it, or a line of the client. These are the stages:\n * - `monitor-down`: a follower sees the process of its leader go down.\n * - `elected`: the new leader, and `followed`: a follower that sees it.\n * - `old-leader-steps-down`: the old leader becomes a follower.\n * - `first-write`: the line of the client of the first publish that the queue confirms.\n */\nexport type ClusterStage = \"monitor-down\" | \"elected\" | \"followed\" | \"old-leader-steps-down\" | \"first-write\";\n\n/** This type is the node of the client in a cluster at the start of a run. It is the node of the leader of the queue, or of a follower. */\nexport type ClusterClientAt = \"leader\" | \"follower\";\n\n/** A group role is a part of a group by its role at the start of a run: the primary, a replica, or a Sentinel. */\nexport type GroupRole = \"primary\" | \"replica\" | \"sentinel\";\n\n/**\n * A group place is the part of a group that a fault acts on. A network fault cuts the part from\n * some of its peers:\n * - `all`: each other part of the group, and each client. This is the default.\n * - `group`: the other members and the Sentinels, but not the clients.\n * - `clients`: the clients, but not the group.\n * - `one`: one other member, in both directions.\n * - `one-way`: one other member, for the segments from the part only.\n * - `to-clients`: the clients, for the segments from the part only. Each write of a client reaches\n * the part, and no reply reaches the client.\n */\nexport interface GroupPlace {\n readonly role: GroupRole;\n readonly cuts?: \"all\" | \"group\" | \"clients\" | \"one\" | \"one-way\" | \"to-clients\";\n}\n\n/** A toxic is one toxic of Toxiproxy, as its API takes it. Its toxicity is 1. */\nexport interface Toxic {\n readonly type:\n | \"latency\"\n | \"timeout\"\n | \"reset_peer\"\n | \"slow_close\"\n | \"bandwidth\"\n | \"limit_data\"\n | \"slicer\";\n readonly stream: \"downstream\" | \"upstream\";\n readonly attributes: Readonly<Record<string, number>>;\n}\n\n/**\n * A reference is the event that the time of a detector counts from: the event that arms the\n * timer in its machine document. Keepalive counts from the last received segment. The give-up of\n * the retransmissions counts from the first retransmission of the RTO timer, not from the tail\n * loss probe one RTO before it. The request itself meets a reset, thus a reset counts from the\n * send of the client. A FIN counts from the change of state of the socket of the peer when its\n * process dies (`peer-closed`). A toxic of Toxiproxy counts from the start of its commit, as the\n * injector stamps it (`fault`).\n *\n * A client that starts into a fault counts from its `connecting` line, or from its first line\n * without one (`client-start`).\n *\n * A heartbeat of a program counts from its first ping that got no answer (`ping-sent`). The\n * program writes a `ping` line before each ping, and the kernel stamps the write (`tx`). No\n * segment with data reaches the socket after that write, up to the detection.\n *\n * A check of a pool on an idle connection counts from the first segment with data that the socket\n * sends after the fault (`first-sent`). The keepalive of HikariCP comes at a phase from the\n * creation of its connection, thus no line of the client gives its time. The kernel stamps it.\n *\n * A connect timer counts from the change of its socket to SYN_SENT (`syn-sent`). A pooler connects\n * to the server by itself, thus the kernel stamps that connect, and no line of the client does.\n *\n * A retry of a client after a refusal counts from the refusal (`last-reply`), for example a 429 with\n * `Retry-After`. That is the last segment with data that a socket of the client received before\n * the line that ends the band. The retry can go on a new connection. Each band of a driver has\n * its own, thus each retry counts from the refusal before it.\n */\nexport type Reference =\n | \"last-received\"\n | \"last-reply\"\n | \"last-data\"\n | \"first-retransmission\"\n | \"request-sent\"\n | \"peer-closed\"\n | \"fault\"\n | \"client-start\"\n | \"ping-sent\"\n | \"first-sent\"\n | \"syn-sent\";\n\n/**\n * This prediction tells when a connection moves again after a stall. That occurs at the first\n * firing of its retransmission timer after the stall stops, and its reply comes back one round\n * trip later.\n */\nexport interface Recovers {\n /** This count identifies the firing that gets through: the number of firings before it. */\n readonly firing: number;\n /** This time is the earliest return of the reply, from the send of the request. */\n readonly low: Quantity;\n /** This time is the latest return of the reply, from the send of the request. */\n readonly high: Quantity;\n /**\n * This range is where the stall must stop, from the send, so that this firing is the first\n * after it. The range starts after the latest time of the firing before, and it stops before\n * the earliest time of this firing.\n */\n readonly heal: { readonly low: Quantity; readonly high: Quantity };\n}\n\n/**\n * This prediction tells when the reply to the request comes back, from the send of the request.\n * For example, the reply comes after the latency of a toxic, or after a forgotten flow starts again.\n */\nexport interface Replies {\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/**\n * An RTT range is the range of the first round trip of the observer that a band assumes. The\n * models read a round trip, for example 1 ms. The band at that value holds within the noise for\n * each round trip in the range. A run whose first round trip is above the range is void.\n */\nexport interface RttRange {\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/**\n * A proxy time is the time of one proxy in one direction that a band includes. It is an upper\n * quantile of the times that a calibration cell of the proxy measured. Each time goes from an\n * arrival on one socket of the proxy to its write on the other. `up` goes toward the target, and\n * `down` toward the client.\n */\nexport interface ProxyTime {\n readonly hop: string;\n readonly direction: \"up\" | \"down\";\n /** This fraction is the quantile of the calibration, for example 0.99. */\n readonly quantile: number;\n readonly time: Quantity;\n}\n\n/**\n * A stage is one step of the timeline of a group after a fault, by the event that a Sentinel\n * publishes for it. The stages are the subjective down, the objective down, the start of a\n * failover and the election. Then the selection of a replica, the promotion, the switch to the new\n * primary, and the conversion of the old primary into a replica.\n */\nexport type SentinelStage =\n | \"+sdown\"\n | \"+odown\"\n | \"+try-failover\"\n | \"+elected-leader\"\n | \"+selected-slave\"\n | \"+promoted-slave\"\n | \"+switch-master\"\n | \"+convert-to-slave\";\n\n/**\n * A replica set stage is one step of the timeline of a replica set after a fault. A line of the log\n * of a member marks it, or a line of the client. The stages are a member that marks the primary\n * DOWN, the start of an election and its win, and the end of the catch-up. Then come the first\n * write of the new primary and the step-down of the old one. The client's lines give its discovery\n * of the new primary, the write-off of the old one, and the first write that a primary accepts.\n */\nexport type ReplicaSetStage =\n | \"member-down\"\n | \"election-start\"\n | \"elected\"\n | \"caught-up\"\n | \"writable\"\n | \"old-primary-steps-down\"\n | \"discovered\"\n | \"written-off\"\n | \"first-write\";\n\n/** A stage is one step of the timeline of a group under Sentinels, of a replica set, or of a cluster of RabbitMQ. */\nexport type Stage = SentinelStage | ReplicaSetStage | ClusterStage;\n\n/** A stage band is when the first part announces a stage, from the reference of the cell. */\nexport interface StageBand {\n readonly stage: Stage;\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/**\n * A group prediction tells what a group does after the fault, beside the band of its cell:\n * - `lands` is the primary that the first accepted write after the fault lands on: a new one, or the same one.\n * - `failover` tells if a Sentinel announces a switch to a new primary in the window.\n * - `stages` are the bands of the stages of the timeline. Each band includes the error of the\n * stamps of the observer (`stamps`).\n * - `errors` are the errors that the client must meet on the way, each with the band of its\n * first occurrence. An example is the error of the offline queue when it gives up.\n * - `quiet` are the stages that must not occur after the fault. Each one that a run shows is a finding.\n * - `lost` is the band of the count of accepted writes that the group did not keep, in each run. A\n * count outside it is a finding. A measurement-only cell with this band gets its decision from\n * the findings of its runs, thus the law of the count judges the cell.\n * - `duplicates` is the band of the count of extra copies that the group holds, in each run. It\n * judges a cell as `lost` does. Only a list or a log can show a copy, thus a Redis cell needs\n * `op: \"rpush\"`.\n */\nexport interface GroupPrediction {\n readonly lands: \"new\" | \"same\";\n readonly failover: boolean;\n readonly stages: readonly StageBand[];\n readonly stamps?: Quantity;\n readonly errors?: ReadonlyArray<{ readonly name: string; readonly low: Quantity; readonly high: Quantity }>;\n readonly quiet?: readonly Stage[];\n readonly lost?: { readonly low: number; readonly high: number };\n readonly duplicates?: { readonly low: number; readonly high: number };\n /** These are the bands of the commits of a pair (`Cell.pair`), each by its set. A set with no band is a measurement. */\n readonly outcomes?: Outcomes;\n}\n\n/** A count band is the least and the most of a count, both included. */\nexport interface CountBand {\n readonly low: number;\n readonly high: number;\n}\n\n/**\n * These are the bands of the sets of a pair. A commit with no reply is in one set, by the two final reads:\n * - `kept`: the new primary holds it.\n * - `stranded`: only the old primary holds it, after its crash recovery.\n * - `absent`: no server holds it.\n */\nexport interface Outcomes {\n /** This band is the count of the writes with no reply, of each set. */\n readonly unknown?: CountBand;\n readonly kept?: CountBand;\n readonly stranded?: CountBand;\n readonly absent?: CountBand;\n /** This band is the count of the acknowledged writes that the old primary does not hold after its crash recovery. */\n readonly missing?: CountBand;\n /** This band is the count of the WARNING notices that the client reads. */\n readonly warnings?: CountBand;\n}\n\n/**\n * A pair is a PostgreSQL primary, the target of a cell, and one standby on its network. The topology\n * of the cell stays in front of the primary. Nothing in the pair fails over by itself: the harness\n * does each step, in the order of `pairOnce`.\n */\nexport interface Pair {\n /** This flag names the standby in `synchronous_standby_names`. Without it, the standby is asynchronous. */\n readonly synchronous: boolean;\n /** This count is the writers of the client, each with its own connection of the pool. */\n readonly writers: number;\n /** This time is the pause of a writer after each reply. */\n readonly interval: Quantity;\n /** This place is what the black hole cuts the primary from: the standby, or each peer. */\n readonly cuts: \"standby\" | \"all\";\n /** This count is the cancels of the waits of the primary, spread across the hold. */\n readonly cancels?: number;\n}\n\n/**\n * A middlebox expectation tells what the router of a topology does with the flow in the window of\n * a silent prediction. Without it, the router keeps the flow. With `forgets`, the router forgets\n * the flow in that band after the last segment that the client got, and nothing detects it. For a\n * driver cell, the flow is the one of the socket of the driver.\n */\nexport interface MiddleboxExpectation {\n readonly forgets: { readonly low: Quantity; readonly high: Quantity };\n}\n\n/**\n * A request prediction tells what the second request of an HTTP client meets, beside the band of\n * its cell. The client writes it in its lines: the connection of each try, and the outcome.\n * - `connection` is the connection of its first try: the one that the first request used\n * (`reused`), or a new one.\n * - `retried` tells that the client sent it again on a new connection by itself.\n * - `error` is the error that reaches the caller, or null for a reply.\n * - `pending` tells that nothing ends the request in the window of the cell.\n */\nexport interface RequestPrediction {\n readonly connection: \"reused\" | \"new\";\n readonly retried: boolean;\n readonly error: string | null;\n readonly pending?: true;\n}\n\n/**\n * A line match is a line of the client that a driver band waits for. It has the `ev` of the line,\n * and the value of each field that it names. A dotted name is a field in a field, for example\n * `failure.name`. `name` is the field that names the error of the line, for its outcome.\n */\nexport interface LineMatch {\n readonly ev: string;\n readonly where?: Readonly<Record<string, string | number | boolean>>;\n readonly name?: string;\n}\n\n/**\n * A line count is an exact count of the lines of the target in one run, for a cell whose law gives a\n * count (SYS-67). A count reads no timer, thus it has no band and no noise. The judge compares it with\n * `count`, and `what` names it in the finding.\n */\nexport interface LineCount {\n readonly what: string;\n readonly match: LineMatch;\n readonly count: number;\n}\n\n/**\n * A driver step is one more band of a driver after the band of its cell. An example is the error\n * of the retry after the interruption. It has its own reference on a socket of the driver, its\n * line, and its band. A run whose step is outside its band, or absent, has a finding.\n */\nexport interface DriverStep {\n readonly what: string;\n /** This name is the name of the error of the line, or OK. */\n readonly name: string;\n /** This role is the socket of the reference. Without it, the socket is the socket of the band of the cell. */\n readonly socket?: string;\n readonly from: Reference;\n readonly ends: LineMatch;\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/**\n * A driver band is a band of a cell whose client is a driver with more than one socket, for\n * example the MongoDB driver. It names the socket of the reference, and the line that ends the\n * band:\n * - `socket` is the role of the socket, as the client names it in its `socket` lines. The socket\n * `server` is the socket of the target for the first connection of the pool.\n * - `ends` is the line of the client that marks the detection. Without it, the detection is the\n * report of the kernel on the socket: an error, or the end of the file.\n * - `reply` is the line of a reply that a silent prediction times (`replies`), with its reference.\n * - `quiet` are the lines that must not occur after the fault, in the window of the prediction.\n * - `then` are the later bands, each one a test of its own.\n *\n * A band whose line ends it is a timer of the driver. Its `last-received` is the last segment with\n * data that its socket received or sent. The idle timer of a Node socket starts again at each read\n * and each write. A band of the kernel counts from the last segment of any kind that the socket\n * received.\n */\nexport interface DriverBand {\n readonly socket: string;\n readonly ends?: LineMatch;\n readonly reply?: { readonly from: Reference; readonly ends: LineMatch };\n readonly quiet?: readonly LineMatch[];\n readonly then?: readonly DriverStep[];\n}\n\n/**\n * A reconnect prediction tells when the program of the client tries a new connection after the\n * detection. Each wait counts from the start of the reconnect timer of the program (a\n * `reconnecting` line) to the try (a `dial` line). The first wait follows the detection, and each\n * next wait follows the failure of the try before it.\n */\nexport interface Reconnects {\n readonly waits: ReadonlyArray<{ readonly low: Quantity; readonly high: Quantity }>;\n}\n\n/**\n * A prediction is what a cell predicts. Either a detection occurs in an interval [T_lo, T_hi]\n * after the reference event, or nothing detects the fault in a window. Nothing detects a stall\n * that is shorter than the give-up, and the connection recovers from it. The prediction then also\n * tells when. A silent prediction can also tell when the reply to the request comes back\n * (`replies`), and what a router does with the flow (`middlebox`).\n *\n * A band can name the first round trip that it assumes (`rtt`), and the proxy times that it\n * includes (`through`).\n *\n * A cell whose outcome is a distribution, for example under netem loss, has no band. Its\n * prediction is `detects: null` with the reason, and its runs are measurements only. A\n * measurement-only cell that names a reference (`from`) also records the time from it to the end\n * of the connection, or to the last data. It can also give the waits of the reconnect after that\n * end (`reconnects`).\n *\n * For a group cell, the band is the first write that a primary accepts after the fault, and its\n * errno is `OK`. `group` tells which primary, and the stages of the group on the way.\n *\n * For an HTTP cell, `request` tells what the second request meets. The errno `CLOSED` is a close\n * of the observer itself: its pool closes an idle connection, a FIN that it sends.\n *\n * A band of a driver (`driver`) counts from a socket of the driver to a line of the client.\n *\n * The errno `DROPPED` is a drop by the program of the observer itself. Its own timer gave up, and\n * it closed its socket or wrote a close frame. A detection can also tell when the client tries a\n * new connection (`reconnects`).\n */\nexport type Prediction =\n | {\n readonly detects: true;\n /** This name is the timer of the machine that fires first, or the event that detects the fault, with its errno. */\n readonly timer: string;\n /**\n * This name is the errno of the detection, or EOF for an end of file without an error. It is\n * OK for a write that a primary accepts, and CLOSED for a close of the observer itself. It is\n * DROPPED for a drop by the program of the observer.\n */\n readonly errno: string;\n readonly from: Reference;\n /** This time is the closed form, less the most that the timer can fire early. */\n readonly low: Quantity;\n /** This time is the closed form, plus the most that the timer can fire late, plus each proxy time. */\n readonly high: Quantity;\n /**\n * This time is the latest reference of the band after the reply of the client. It is for a\n * reference that can come late, for example the first ping without an answer of a server\n * heartbeat. The run then waits that much longer for the detection.\n */\n readonly startsBy?: Quantity;\n readonly rtt?: RttRange;\n readonly through?: readonly ProxyTime[];\n readonly group?: GroupPrediction;\n readonly request?: RequestPrediction;\n readonly driver?: DriverBand;\n readonly reconnects?: Reconnects;\n /**\n * This name tells how the program drops the connection, for the errno DROPPED. A close frame\n * leaves its socket ESTABLISHED below (`frame`). A close of its socket takes it out of\n * ESTABLISHED at once (`close`).\n */\n readonly drop?: \"frame\" | \"close\";\n /**\n * This flag tells that the program of the observer is the client process, which ends at the\n * end of the run. Its kernel then closes each socket that is still open. A close of the\n * socket at that end is not a close by the program, thus `left` stays null for it (`exited`).\n */\n readonly exits?: true;\n }\n | {\n readonly detects: false;\n readonly window: Quantity;\n readonly recovers?: Recovers;\n readonly replies?: Replies;\n readonly middlebox?: MiddleboxExpectation;\n readonly rtt?: RttRange;\n readonly through?: readonly ProxyTime[];\n readonly group?: GroupPrediction;\n readonly request?: RequestPrediction;\n readonly driver?: DriverBand;\n }\n | {\n readonly detects: null;\n readonly window: Quantity;\n readonly why: string;\n readonly from?: Reference;\n /**\n * This choice ends the measured time at the close of the socket by the observer itself, the\n * change from ESTABLISHED to FIN_WAIT1. Without it, the time ends at a report of the kernel or\n * at the last data. Then an idle pool that closes its own socket gives no end.\n */\n readonly ends?: \"own-close\";\n readonly group?: GroupPrediction;\n readonly request?: RequestPrediction;\n /**\n * These are the waits of the reconnect after the end of the connection, from the document of\n * the client. The end has no band, but each wait has one. A wait outside its band is a finding of its run.\n */\n readonly reconnects?: Reconnects;\n /**\n * These are the exact counts of the lines of the target in each run. A count that differs is a\n * finding of its run. A measurement-only cell with a reference and counts gets a decision from them.\n */\n readonly counts?: readonly LineCount[];\n /**\n * This record is the law of an accept queue: its limit, its count, its counters, and what each\n * connection sees. A value outside it is a finding of its run.\n */\n readonly queue?: QueuePrediction;\n /**\n * This record is the law of a port range: the count of connects, the ports that they use, and\n * when a held port comes back. A value outside it is a finding of its run.\n */\n readonly ports?: PortsPrediction;\n /**\n * This record is the law of a conntrack table: its flows, a connect that finds no entry, and the\n * return of a slot. A value outside it is a finding of its run.\n */\n readonly conntrack?: ConntrackPrediction;\n /** This record is the law of a paused consumer (`Kafka.pause`). A run outside it is a finding of its run. */\n readonly pause?: PausePrediction;\n /** These are the limits of an open-loop cell (`Cell.probe.plan`). Without them, its load is a measurement. */\n readonly load?: LoadPrediction;\n };\n\n/** A queue band is a time from one line of a queue cell to another line, with its two bounds. */\nexport interface QueueBand {\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/** This prediction tells how one connection of a queue cell ends. */\nexport interface QueueEnd {\n readonly errno: string;\n /** This name is the line of the connection that the time of the end counts from. */\n readonly from: \"dial\" | \"connected\" | \"sent\";\n readonly within: QueueBand;\n /**\n * This band is the time from the last retransmission of the connection to its end. With an\n * `expiry`, the retransmission before the last comes before the expiry, and the last comes after it.\n */\n readonly retransmitted?: QueueBand;\n}\n\n/** This prediction tells what one connection of a queue cell sees, by its name in the client. */\nexport interface QueueConnection {\n readonly name: string;\n /** This choice says if the connect of the connection succeeds. */\n readonly connects: boolean;\n /** These bands are the waits of the SYN: the first from the dial, each next one from the SYN before it. */\n readonly syns?: readonly QueueBand[];\n /** These bands are the waits of each SYN-ACK sent again: the first from the dial, each next one from the SYN-ACK before it. */\n readonly synAcks?: readonly QueueBand[];\n /** This prediction tells how the connection ends. It is null for a connection that lives to the end of the run. */\n readonly ends: QueueEnd | null;\n /** This count is the segments that the peer has not acknowledged at the end of the run. */\n readonly unacked?: number;\n}\n\n/**\n * This prediction is the law of an accept queue. It reads the lines of the queue client and of the\n * queue target. The counters count from the `opened` line of the run.\n */\nexport interface QueuePrediction {\n /** This count is the limit of the queue: `sk_max_ack_backlog`, as TCP_INFO of the listener gives it. */\n readonly max: number;\n /** This count is the most connections that the queue holds at one time. */\n readonly held: number;\n /** This count is the connections that connect, and the kernel of the client tells this. */\n readonly connected: number;\n /** These counts are the bounds of `ListenOverflows`. */\n readonly overflows: { readonly low: number; readonly high: number };\n /** This count is `EmbryonicRsts`: the handshakes that the target reset. */\n readonly resets: number;\n /** These predictions are the connections with a law of their own. */\n readonly connections: readonly QueueConnection[];\n /** A run where this connection did not take the last place in the queue is void: its burst did not land in its sequence. */\n readonly admitted?: string;\n /** This record tells when the kernel of the target forgets the handshakes: the `TCPTimeouts` of their last firings, from the first dial of the burst. */\n readonly expiry?: { readonly timeouts: number; readonly within: QueueBand };\n /** This record tells when the count of the queue gets back to `count`, from the first dial of the burst, after an accept. */\n readonly rise?: { readonly count: number; readonly within: QueueBand };\n /** This count is the connections that the target accepted in the run. */\n readonly drained?: number;\n}\n\n/**\n * This prediction is the law of a port range. It reads the lines of the ports client. In each round\n * the client connects to each destination until a connect fails with EADDRNOTAVAIL.\n */\nexport interface PortsPrediction {\n /** This port is the low end of the range. */\n readonly low: number;\n /** This count is the ports of the range. */\n readonly size: number;\n /** This count is the rounds of each run. */\n readonly rounds: number;\n /** This count is the destinations of each round. */\n readonly destinations: number;\n /** These counts are the only connects that one destination can get in one round. */\n readonly counts: readonly number[];\n /** These offsets from the low port are the only ports that the run uses. It uses each of them. */\n readonly seen: readonly number[];\n /** This time is the longest connect that fails. */\n readonly refusedWithin: Quantity;\n /** This record is the band of the share of the rounds that get `count` connects. */\n readonly share?: { readonly count: number; readonly low: number; readonly high: number };\n /** This choice says that the ports of the parity of the low port come first in each round. */\n readonly lowFirst?: boolean;\n /** This band is the time from the `closing` line to each `freed` line: when each held port comes back. */\n readonly freed?: QueueBand;\n /** This count is the segments that the sockets of the client send again. */\n readonly retransmissions?: number;\n}\n\n/**\n * This prediction is the law of a conntrack table. It reads the lines of the ctfull client. The client\n * holds flows on its loopback until the count of its namespace reads the limit, then it does its plan.\n */\nexport interface ConntrackPrediction {\n /** This count is the limit of the table: the flows that the client holds, and the count that it reads then. */\n readonly max: number;\n /** This count is what the namespace of the target reads while the client holds its flows. */\n readonly neighbor?: number;\n /** This record is the law of a connect that finds no entry: its end, and the rise of the two counters of the kernel. */\n readonly refused?: {\n readonly within: QueueBand;\n readonly errno: string;\n readonly drops: number;\n readonly retransFail: number;\n };\n /** This band is the time from the `closing` line to the first fall of the count, and to its zero. */\n readonly drained?: QueueBand;\n /**\n * This record is the law of a scan under pressure. It has the lowest count, the count after the\n * connect, and the times of that connect. With `highest`, the lowest count is a band from `lowest` to\n * `highest`. Then the count after the connect is one more than the lowest count of the run.\n */\n readonly pressure?: {\n readonly lowest: number;\n readonly highest?: number;\n readonly after: number;\n readonly points: readonly QueueBand[];\n };\n}\n\n/**\n * This setting is one sysctl of the namespace of the host, for the time of an environment. The harness\n * reads the value before it, reads the value that it set, and restores the first value at the end.\n */\nexport interface HostSysctl {\n readonly name: string;\n readonly value: string;\n /** The harness does not set the value when this sysctl of the host reads above `atMost`. The cell is then void. */\n readonly guard?: { readonly name: string; readonly atMost: number };\n}\n\n/** A load plan is the schedule of an open-loop client as data: the rate over time, and how the arrivals fall. */\nexport interface LoadPlan {\n readonly profile: Profile;\n readonly options: ScheduleOptions;\n}\n\n/**\n * A load stream is one connection of the client `load`: its key, its schedule and its requests.\n * Arrival i sends request i modulo the count of requests. Each stream has its own connection, thus\n * a slow stream does not delay the arrivals of another stream.\n */\nexport interface LoadStream {\n /** This text names the stream in each line of the client (`k`). Two streams of a cell do not have the same key. */\n readonly key: string;\n readonly plan: LoadPlan;\n /** These texts are whole HTTP/1.1 requests, each with its head and its body. */\n readonly requests: readonly string[];\n}\n\n/** A run stream is the schedule that one stream of the client `load` had in a run. */\nexport interface RunStream {\n readonly key: string;\n readonly schedule: Schedule;\n}\n\n/** A load band is a limit on one quantile of the latency of an open-loop cell, over the arrivals of its counted runs. */\nexport interface LoadBand {\n /** This number is the quantile, from 0 to 1. */\n readonly quantile: number;\n readonly low?: Quantity;\n readonly high: Quantity;\n}\n\n/** A load prediction tells how close to its schedule a client stays, and where the latency lands. */\nexport interface LoadPrediction {\n /** This time is the largest lag of a send behind its planned time. A later send is a finding of its run. */\n readonly lag: Quantity;\n readonly bands: readonly LoadBand[];\n /**\n * This share is the limit on the failed arrivals of the counted runs, from 0 to 1. An arrival fails\n * when the client did not send it, when it has no reply, or when its reply has the status of a\n * failure. The verdict reads the Wilson interval at 0.95, thus a limit of 0.01 needs 381 arrivals.\n */\n readonly failures?: number;\n}\n\n/** A target is the server that a cell connects to: a stock image. */\nexport interface Target {\n /** This image is a stock image, pinned by digest. */\n readonly image: string;\n readonly port: number;\n /** This text is the log line that tells that the target serves. */\n readonly ready: string;\n /**\n * These commands set and read one setting of the target, as argv in it. The name and the value\n * come after them: `redis-cli CONFIG SET` and `CONFIG GET` for Redis. Without them, the target\n * has no settings to set.\n */\n readonly settings?: { readonly set: readonly string[]; readonly get: readonly string[] };\n /**\n * This command replaces the command of the image, for example a shell that holds the server\n * process as its child. Then a `fin` fault can stop the server, and the container stays up.\n */\n readonly command?: readonly string[];\n /** This command stops the server process in the target with SIGKILL, as argv, for a `fin` fault. */\n readonly kill?: readonly string[];\n /** This command makes the server close the connection of each client, as argv, for a `server-close` fault. */\n readonly close?: readonly string[];\n /**\n * These files go into the target before it starts, by their path, for example the script of a\n * server on a stock Node image. The image stays stock.\n */\n readonly files?: Readonly<Record<string, string>>;\n /**\n * This flag tells that the target writes JSON lines with an `ev` and a `t` on its standard\n * output, on CLOCK_MONOTONIC. Each run then keeps its lines (`Run.server`).\n */\n readonly lines?: true;\n /**\n * This name is the server process, as /proc/PID/comm holds it. With it, a freeze of the target\n * stops this process with SIGSTOP from a sidecar in its process namespace, and the injector\n * stamps it. SIGCONT then thaws it. Without it, the freeze is the cgroup freezer, with no stamps.\n */\n readonly program?: string;\n /**\n * This source is the image of a subject that the target operates in place of `image`. It is an\n * image pinned by digest, a build context, or an image that the harness ships. `image` is then\n * the stock base of that image, for the record. An example is a subject that holds a client\n * and the script of its server.\n */\n readonly subject?: ImageSource;\n}\n\n/**\n * A shipped image is an image that the harness ships, by its name. Its build context is in the\n * package of the harness, and in the package of the prober for `harness`. `grpc-go` is a server of\n * grpc-go and `etcd` is etcd on Alpine: each is a subject of a target (`Target.subject`).\n * `docker-proxy` is the userland proxy of Docker Engine 29.1.5, the program of a proxy hop.\n */\nexport type Shipped =\n | \"harness\"\n | \"node\"\n | \"go-http\"\n | \"node-http\"\n | \"mongo\"\n | \"pg\"\n | \"hikari\"\n | \"amqp\"\n | \"kafka\"\n | \"grpc\"\n | \"grpc-go\"\n | \"etcd\"\n | \"router\"\n | \"pgbouncer\"\n | \"pgpool\"\n | \"docker-proxy\";\n\n/**\n * An image source tells where an image comes from, as data. The harness resolves each kind to an\n * image that it can start:\n * - `pinned`: an image of a registry, pinned by its digest. The lab pulls it.\n * - `build`: a build context that the caller gives. The harness builds it, with a tag from its content.\n * - `shipped`: an image that the harness ships, by its name. The harness builds it from its package.\n */\nexport type ImageSource = PinnedImage | BuildImage | ShippedImage;\n\n/** A pinned image is an image of a registry, pinned by its digest: `name@sha256:<64 hex digits>`. */\nexport interface PinnedImage {\n readonly kind: \"pinned\";\n readonly image: string;\n}\n\n/**\n * A build image is a build context that the caller gives. The harness copies its files into a new\n * context and builds it. Each `FROM` of its Dockerfile names a digest, and each `package.json` or\n * `Cargo.toml` has a lockfile beside it. Thus a build gives the same programs each time.\n */\nexport interface BuildImage {\n readonly kind: \"build\";\n /** This name is the repository of the tag. The tag is the name, then 12 digits of the sha256 of the context. */\n readonly name: string;\n /** This folder is the root of the context. A relative path starts at the working folder of the process. */\n readonly context: string;\n /** This path is the Dockerfile, in the context. Without it, the path is `Dockerfile`. */\n readonly dockerfile?: string | undefined;\n /** These paths are the files and the folders that the Dockerfile reads, in the context. Without them, the context is all of its folder. */\n readonly files?: readonly string[] | undefined;\n}\n\n/** A shipped image source names an image that the harness ships (`Shipped`). */\nexport interface ShippedImage {\n readonly kind: \"shipped\";\n readonly name: Shipped;\n}\n\n/**\n * A subject is a program under test, as data: the image that holds its programs, and the command\n * of each program. A catalog of cells takes a subject. Thus another repository can give its own\n * subject, with its own release in it. The procedures subject of the WS cells is a subject of the\n * client repository (CLI-33).\n */\nexport interface Subject {\n /** This name is the name of the subject in a catalog and in the records: `procedures`. */\n readonly name: string;\n /** This source is the image that holds the programs of the subject. */\n readonly image: ImageSource;\n /** This image is the stock base of the image, pinned by digest. A target of the subject names it (`Target.image`). */\n readonly base: string;\n /**\n * These are the commands of the programs of the image by their role, each as argv: for example\n * `client` and `server`. A run adds its arguments after the command.\n */\n readonly programs: Readonly<Record<string, readonly string[]>>;\n}\n\n/**\n * A client is what the runs of a cell use: the TCP client of the harness, or a driver on its\n * socket. The HTTP clients are the `http.Transport` of Go (`go-http`), the `http.Agent` of Node\n * (`http-agent`) and an HTTP/1.1 `Client` of undici (`undici`). The MongoDB driver is `mongodb`, node-postgres is `pg`, and HikariCP over pgjdbc is `hikari`.\n * The client of amqplib is `amqp`, and the client of grpc-js is `grpc`. The WebSocket client of `@procedures/client` is a program of its subject (`ClientImage`).\n */\nexport type Client = \"tcp\" | \"ioredis\" | \"go-http\" | \"http-agent\" | \"undici\" | \"mongodb\" | \"pg\" | \"hikari\" | \"amqp\" | \"grpc\" | \"load\";\n\n/**\n * A WebSocket probe is the workload and the settings of the WebSocket client of\n * `@procedures/client`, the `client` program of its subject. The cell gives each setting, thus the client takes\n * nothing from a default.\n */\nexport interface WsProbe {\n /**\n * This name is the class of the sockets of the client: the global `WebSocket` of Node (`global`),\n * or the `WebSocket` of the `ws` package (`ws`, the option `webSocket`).\n */\n readonly socket: \"global\" | \"ws\";\n /**\n * These are the settings of the heartbeat: a ping each interval, the wait for its pong, and the\n * misses in sequence that drop the connection. Null turns the heartbeat off.\n */\n readonly heartbeat: { readonly interval: Quantity; readonly timeout: Quantity; readonly misses: number } | null;\n /**\n * These are the settings of the reconnect: the first delay, the multiplier of each next delay,\n * the longest delay and the most tries. `jitter` is the random part of each delay, from 0 to 1.\n * Null turns the reconnect off.\n */\n readonly reconnect: {\n readonly initialDelay: Quantity;\n readonly multiplier: number;\n readonly maxDelay: Quantity;\n readonly attempts: number;\n readonly jitter: number;\n } | null;\n /** This time is how long a call waits for its first frame. */\n readonly requestTimeout: Quantity;\n /** This time is how long a call waits for a connection. */\n readonly connectionTimeout: Quantity;\n /** This time is how long the server holds the reply to the call in flight. Without it, the server replies at once. */\n readonly hold?: Quantity;\n}\n\n/**\n * An HTTP probe is the workload of an HTTP client. The first request is a GET of the root. The\n * second request follows the mode of the probe: after the signal (`inflight`), or after a gap\n * (`gap`).\n */\nexport interface HttpProbe {\n /** This method is the method of the second request. */\n readonly method: \"GET\" | \"POST\";\n /** This time is the gap of a `gap` workload: from the end of the first reply to the second request. */\n readonly gap?: Quantity;\n /** This time is how long the server holds the reply to the second request (`/hold?ms=`). */\n readonly hold?: Quantity;\n /** This length is the length of the body of the reply to the second request, in bytes (`/big?bytes=`). */\n readonly bytes?: number;\n /** These are settings of the client over its defaults, by the names of its flags: for example `idle-conn-timeout-ms`. */\n readonly options?: Readonly<Record<string, number>>;\n}\n\n/**\n * A client image is a process that a lab starts as the client of each run, in the network\n * namespace of the client. An example is an agent of an application on Node. Its command gets the\n * arguments of the TCP client of the harness. These are the address and the port of the target, then\n * the options of the cell. It writes JSON lines with an `ev` and a `t` (CLOCK_MONOTONIC in\n * nanoseconds) on its standard output, as the client of the harness does.\n */\nexport interface ClientImage {\n /** This image is a stock image by its name, pinned by digest, or the source of the image of a subject. */\n readonly image: string | ImageSource;\n readonly command: readonly string[];\n /**\n * These capabilities are added to the default set of the container of the client. A read of the\n * statistics of conntrack over netlink needs NET_ADMIN in the namespace of the client.\n */\n readonly capabilities?: readonly ClientCapability[] | undefined;\n}\n\n/** These are the capabilities that a client image can get. */\nexport const CLIENT_CAPABILITIES = [\"NET_ADMIN\"] as const;\nexport type ClientCapability = (typeof CLIENT_CAPABILITIES)[number];\n\n/**\n * An observer is the side whose detection a cell measures. The client is one observer. The\n * server is another observer. It is the hold of the target on the connection of the client, at\n * its server socket in its namespace. The fault is on the peer of the observer. Thus\n * freeze-process freezes the target for the client, and the client for the server.\n *\n * A proxy of a topology is the third observer: its socket toward the client, or its socket toward\n * the target.\n */\nexport type Observer =\n | \"client\"\n | \"server\"\n | { readonly hop: string; readonly toward: \"client\" | \"target\" };\n\n/** A cell is one cell of the matrix: a topology, its settings, a workload, a fault and the prediction. */\nexport interface Cell {\n readonly name: string;\n readonly target: Target;\n /** These are the sysctls of the network namespace of the client, as the kernel spells them. The harness sets them before the client connects. */\n readonly sysctls: Readonly<Record<string, string>>;\n readonly probe: {\n /**\n * This mode tells if the client is idle after its first reply, or sends a request into the\n * fault. In a stream, it sends `count` requests into it, each `interval` after the reply before.\n * An HTTP client in a `gap` sends its second request a gap after its first reply, by itself.\n * On a `schedule`, it sends each request at its time in `plan`, and a send does not wait for a reply.\n */\n readonly mode: \"idle\" | \"inflight\" | \"stream\" | \"gap\" | \"schedule\";\n /** This plan is the schedule of the requests in the mode `schedule`. */\n readonly plan?: LoadPlan;\n /** These streams are the plan of the client `load` in the mode `schedule`, in place of `plan`. */\n readonly streams?: readonly LoadStream[];\n /** This count is the number of requests of a stream. */\n readonly count?: number;\n /** This time is the pause after each reply of a stream, before the next request. */\n readonly interval?: Quantity;\n /** This flag sets SO_KEEPALIVE without overrides for the socket: the keepalive sysctls of the namespace apply. */\n readonly keepalive: boolean;\n /** This time sets TCP_USER_TIMEOUT. Without it, the default of the kernel (off) applies. */\n readonly userTimeout?: Quantity;\n /**\n * This text is the request that an in-flight run sends, as a Redis inline command. Without it,\n * the request is PING. BLPOP with a timeout makes the server answer only at the timeout. Thus\n * a cell on the server side has a reply in flight when the fault occurs.\n */\n readonly request?: string;\n /**\n * This driver replaces the TCP client of the harness, with its own options. The driver then\n * ignores `keepalive` and `userTimeout`:\n * - ioredis 6 on Node, whose keepAlive is the keepalive delay of the socket. In a group cell,\n * its write is `SET w:<n> <n>`, or `RPUSH w <n>` with `op: \"rpush\"`, thus a list keeps each\n * copy. `window` is the count of writers, each with one write in flight: 1 at the default.\n * - the MongoDB driver on Node, against the replica set `set`. Its operation is a write, or\n * the read of a long document (`read-long`). `Cell.driverOptions` are its options.\n * - node-postgres on Node, as a `pg.Pool` against one server. Its operation is `query`, or\n * `SELECT 1`. `Cell.driverOptions` are the options of its pool.\n * - HikariCP over pgjdbc on Java, as a `HikariDataSource` against one server. Its operation is\n * `query`, or `SELECT 1`. `Cell.driverOptions` are the properties of HikariConfig. A name\n * with the prefix `dataSource.` is a property of pgjdbc, and a name with the prefix `system.`\n * is a system property of the JVM.\n *\n * A pg or hikari driver can send a different text in flight (`inflight`): the second query of\n * an in-flight run. The first query then makes the pool ready at once, and only the query in\n * flight is slow, for example `SELECT pg_sleep(20)`.\n *\n * A hikari driver can send a burst in place of one query in flight. A burst is `count` queries,\n * query 2 and the next ones. Each one has its own thread, and it starts `gap` after the one before\n * it. The client writes a `burst` line at the end of the last one, with its counts.\n *\n * A grpc driver is grpc-js on Node, with one channel. Its operation is a unary call of\n * grpc.health.v1.Health/Check. `Cell.driverOptions` are its channel options (`grpc.*`) and\n * `deadline_ms`. With `hold`, the call in flight asks the server of the harness to hold its reply\n * that long. With `op: \"watch\"`, the operation in flight is a stream of Health/Watch, which stays open.\n */\n readonly driver?:\n | { readonly name: \"ioredis\"; readonly keepAlive: Quantity; readonly op?: \"set\" | \"rpush\"; readonly window?: number }\n | { readonly name: \"mongodb\"; readonly set: string; readonly op?: \"write\" | \"read-long\" }\n | { readonly name: \"pg\"; readonly query?: string; readonly inflight?: string }\n | { readonly name: \"hikari\"; readonly query?: string; readonly inflight?: string; readonly burst?: { readonly count: number; readonly gap: Quantity } }\n | { readonly name: \"amqp\" }\n | { readonly name: \"grpc\"; readonly hold?: Quantity; readonly op?: \"check\" | \"watch\" };\n /** This workload is the workload of an HTTP client (`Cell.client` is an HTTP client). The client then ignores `keepalive` and `userTimeout`. */\n readonly http?: HttpProbe;\n /** These are the settings of the WebSocket client (`Cell.client` is the client of a procedures subject). The client then ignores `keepalive` and `userTimeout`. */\n readonly ws?: WsProbe;\n };\n readonly fault: Fault;\n /** This time is the length of a stall, from the send of the request. */\n readonly stall?: Quantity;\n /** This code is the ICMP unreachable of an `unreachable` fault. */\n readonly icmp?: \"host\" | \"net\" | \"port\" | \"admin\";\n /** This direction is the one that a `simplex` fault drops: the segments to the server, or to the client. */\n readonly drops?: \"to-server\" | \"to-client\";\n /** This length is the longest segment, in bytes, that an `mtu-blackhole` fault lets through to the observer. */\n readonly mtu?: number;\n /**\n * This specification is the tc netem of a `netem` fault, for example \"loss 5%\". It is on the\n * egress of the target, or at a hop (`faultAt`) on the egress of its device toward one side.\n */\n readonly netem?: string;\n /**\n * This delay goes on the egress of the target before the client connects, with tc netem. Thus\n * each round trip is longer by the delay, and so is the RTO.\n */\n readonly latency?: Quantity;\n /** These commands run in the target before a client connects, each as argv: for example, a large value to read. */\n readonly targetSetup?: readonly (readonly string[])[];\n /** These commands run in the namespaces of the client and the target, each as argv, after the first connection of the environment. */\n readonly namespaceSetup?: readonly (readonly string[])[];\n /**\n * These are sysctls of the host, which a cell needs because only the host can write them. The value\n * applies to each container of the host, thus a shard runs them on its own runner. A host that does\n * not have one makes the cell void (rule 8 of the environments).\n */\n readonly hostSysctls?: readonly HostSysctl[];\n readonly prediction: Prediction;\n /** This side is the side whose detection the cell measures. Without it, the side is the client. */\n readonly observer?: Observer;\n /** These are the settings of the target. The harness sets them in each environment before a client connects. */\n readonly targetSettings?: Readonly<Record<string, string>>;\n /** These are the sysctls of the network namespace of the target, for a cell on the server side: its tcp_retries2. */\n readonly targetSysctls?: Readonly<Record<string, string>>;\n /** This topology puts hops between the client and the target. Without it, the client and the target are a direct pair. */\n readonly topology?: Topology;\n /** This place is where the fault goes, when it is not the peer of the observer: for example one side of a hop. */\n readonly faultAt?: FaultPlace;\n /** This time is how long an in-flight client stays idle after the fault holds, before it sends its request. */\n readonly idleBefore?: Quantity;\n /** This toxic is the toxic of a `toxic` fault, which the Toxiproxy hop gets. */\n readonly toxic?: Toxic;\n /** This event is the event of a `hop-event` fault, at a proxy of the topology. */\n readonly event?: HopEvent;\n /** This image, or this client of the harness, replaces the TCP client of the harness for each run. An HTTP client is one. */\n readonly client?: ClientImage | Client;\n /**\n * This name is the proxy hop whose own time a calibration cell measures. The time of a request\n * (`up`) starts at its arrival on the socket of the proxy toward the client. It stops at the\n * write on the socket toward the target. The time of its reply (`down`) goes the other way.\n *\n * For a group cell, `sentinels` makes the prober watch the namespace of each Sentinel too. Each\n * stamp of the observer then gets the write of its Sentinel that it read (`lags`).\n */\n readonly calibrates?: string;\n /**\n * This group replaces the target: a replicated group under its Sentinels. The client is then\n * ioredis in Sentinel mode, and it writes a stream of sequence numbers. `target` stays for the\n * record, and the group does not read it.\n */\n readonly group?: Group;\n /**\n * This replica set replaces the target: a replicated group whose members elect their own\n * primary. The client is then the MongoDB driver, and it writes a stream of sequence numbers.\n * `target` stays for the record, and the set does not read it.\n */\n readonly replicaSet?: ReplicaSet;\n /**\n * This cluster replaces the target: a cluster of RabbitMQ with one quorum queue. The client is then\n * amqplib, and it publishes a stream of sequence numbers with confirms. `target` stays for the\n * record, and the cluster does not read it.\n */\n readonly cluster?: Cluster;\n /**\n * This Kafka group replaces the target: brokers of Apache Kafka with one controller. The client is\n * then the producer of kafka-clients, with the settings of `driverOptions`. `target` stays for the\n * record, and the group does not read it.\n */\n readonly kafka?: Kafka;\n /** This pair adds a standby to the target (`Pair`). The client is then node-postgres, in a stream of K writers. */\n readonly pair?: Pair;\n /** This place is the node of the client in a cluster: the node of the leader, or of a follower. Without it, the node of a follower. */\n readonly clientAt?: ClusterClientAt;\n /**\n * This time is how long a fault on a group holds after its commit. The injector then lifts it at\n * that instant, on the clock of the VM. On a direct pair, a frozen target thaws after it.\n */\n readonly hold?: Quantity;\n /**\n * This time is how much longer a run of a replica set continues after the lift. A prediction of\n * no failover after a short hold needs it: the run must outlast the timers of an election.\n */\n readonly watch?: Quantity;\n /** This flag makes a planned failover coordinated: `SENTINEL FAILOVER <group> COORDINATED`. */\n readonly coordinated?: boolean;\n /**\n * These are options of the driver over its defaults: of ioredis for the client of a group, or of\n * the MongoDB driver for its client. A cell at the defaults has none.\n */\n readonly driverOptions?: Readonly<Record<string, unknown>>;\n /** This write concern is the w of each write of the MongoDB client. Without it, it is majority. */\n readonly writeConcern?: \"majority\" | 1;\n /** This flag makes the fault hold before the client starts. The client then meets it at its first selection. */\n readonly before?: boolean;\n /** This drill makes the cell a recovery drill. It needs `replicaSet`, and its runs have two clients when the store restores. */\n readonly drill?: Drill;\n /** This count is the number of runs of the cell (11), across new environments (two at least). */\n readonly runs: number;\n readonly environments: number;\n /**\n * This flag tells that the fault can change the environment for the runs after it, as a fin does.\n * Then the warm-up of each environment is a dry run, without the fault, and a cell with this flag\n * has an environment for each run. For example, behind a black hole Pgpool-II can detach its only\n * server, and then it refuses each new client.\n */\n readonly fresh?: true;\n /**\n * This count is the number of environments at the same time, each with its own prober. Without\n * it, the count is one. The calibration does its reference cells at 1, 4 and 8, to find how\n * many environments the machine holds before its noise increases.\n */\n readonly concurrency?: number;\n /**\n * This time is δ_abs of the environment of the cell: the noise of the pipeline. Each band includes\n * it, and a timer must be at least that late to make a run void. Stage 0 measures it in each\n * environment. Without it, the cell gets `NOISE`, the δ_abs of the first environment (4 ms).\n */\n readonly noise?: Quantity;\n}\n\n/** A hop place is a hop of a topology, with the side that the rules of a fault face. */\nexport interface HopPlace {\n readonly hop: string;\n readonly toward: \"client\" | \"target\";\n}\n\n/**\n * A fault place is the namespace that gets the rules of a fault. \"target\" and \"client\" are the two\n * ends. A hop place is a hop, with the side that the rules face. That side is the next hop toward\n * the client, or the next hop toward the target. The rules of a proxy act on its sockets. The\n * rules of a router act on what it forwards, by its device toward that side: the FORWARD chain.\n *\n * A group place is a part of a group, by its role.\n */\nexport type FaultPlace = \"target\" | \"client\" | HopPlace | GroupPlace;\n\n/** This function tells if a fault place is a hop of a topology. */\nexport const isHopPlace = (place: FaultPlace | undefined): place is HopPlace =>\n typeof place === \"object\" && \"hop\" in place;\n\n/**\n * This function tells if the request of a client goes before the fault of its cell. That is so for a\n * hop event that comes while the request is in flight (`HopEvent.inFlight`). Then a request after the fault is void.\n */\nexport const requestFirst = (cell: Cell): boolean => cell.fault === \"hop-event\" && cell.event?.inFlight === true;\n\n/**\n * This function gives the latest end of a band in seconds, from the reply of the client. It is\n * the high bound, after the latest reference of the band (`startsBy`).\n */\nexport const latestEndOf = (prediction: Extract<Prediction, { readonly detects: true }>): number =>\n valueIn(prediction.high, \"s\") + (prediction.startsBy === undefined ? 0 : valueIn(prediction.startsBy, \"s\"));\n\n/** A middlebox event is one line of the router: a change of a conntrack entry, or a reset that it sent. `t` is CLOCK_MONOTONIC in nanoseconds. */\nexport interface MiddleboxEvent {\n readonly ev: string;\n readonly t: number;\n readonly [field: string]: unknown;\n}\n\n/** A probe event is one line that the client wrote. `t` is CLOCK_MONOTONIC in nanoseconds: an instant, not an amount. */\nexport interface ProbeEvent {\n readonly ev: string;\n readonly t: number;\n readonly [field: string]: unknown;\n}\n\n/**\n * A jitter record tells how late the kernel stamped the acts of a program after their lines, in\n * one run. An act is a `write` line or a `ping` line on the connection of the run, before the\n * fault. Its delay goes from the line to the kernel's stamp of the first write of data on the\n * socket after it. That write must come before the next act and before the fault.\n */\nexport interface Jitter {\n /** This count is the number of acts that had a write in their window. */\n readonly acts: number;\n /** This time is the longest delay of those acts. It is null when the run has no such act. */\n readonly bound: Quantity | null;\n}\n\n/** A run is one run of a cell, as the harness records it. */\nexport interface Run {\n readonly environment: number;\n readonly index: number;\n /**\n * This flag marks the warm-up of an environment. It occurs before the counted runs, and the\n * verdict does not count it. On a loaded machine, a new environment is sometimes slow at first.\n */\n readonly warmup?: true;\n readonly events: readonly ProbeEvent[];\n /** This bracket is the time of the injector around the commit of the fault, on the same clock. It is null for a freeze. */\n readonly fault: { readonly t0: number; readonly t1: number } | null;\n /** This bracket is the time around the commit that stopped a stall. */\n readonly heal?: { readonly t0: number; readonly t1: number } | null;\n /** These are the records of the prober from the start to the end of the run, in the two namespaces. */\n readonly kernel: readonly KernelRecord[];\n /** This inode identifies the network namespace of the client, thus the records of the client. */\n readonly netns: number;\n /** This inode identifies the namespace of the target, where the records of its server socket are. */\n readonly targetNetns: number;\n /**\n * This count is the number of records that the prober lost in the run. One lost record makes\n * the run void. Null also makes the run void: no clock line came after the run to tell the count.\n */\n readonly lost: number | null;\n /**\n * This rate is the HZ of the kernel of the environment, from its manifest. With it, the rule of a\n * late timer is in time. A run without it keeps the rule in jiffies, as the runs before 0.9.0.\n */\n readonly hz?: number;\n /**\n * This record is the jitter of the program of the observer in the run (`jitterOf`). The pairing\n * window of the run is δ_abs of its cell, or the bound of the jitter when the bound is longer\n * (`pairingOf`). A record from before SYS-25 has no jitter. It reads as a bound of null, thus\n * its window is δ_abs of its cell: 1 ms on a calibrated runner.\n */\n readonly jitter?: Jitter;\n /**\n * This share is the CPU time from 0 to 1 that the host took from this machine in the run, from\n * `/proc/stat` (`stealShare`). A run of a plain cell writes it. A group, a replica set or a cluster\n * writes it with its first cell on layer E4.\n */\n readonly steal?: number;\n /** These are the addresses and the namespaces of a topology, which the measurement of a run reads. */\n readonly topology?: RunTopology;\n /** These are the lines of the routers of a topology from the start to the end of the run. */\n readonly middlebox?: readonly MiddleboxEvent[];\n /** This record is the group of a group cell: its parts, and what it did in the run. */\n readonly group?: RunGroup;\n /** These are the lines of a target that writes them (`Target.lines`), from the start to the end of the run. */\n readonly server?: readonly ProbeEvent[];\n /** This text tells what the environment of the run could not do for its cell. The run is void. */\n readonly missing?: string;\n /** This record is the other member and the broker logs of a run of a paused consumer. */\n readonly pause?: RunPause;\n /** This record is the fabric of a cell with `Topology.fabric`. */\n readonly fabric?: RunFabric;\n /** This schedule is the one that the client of an open-loop cell had: each planned time stays with its record. */\n readonly schedule?: Schedule;\n /** These schedules are the ones that the streams of the client `load` had. */\n readonly streams?: readonly RunStream[];\n}\n\n/** A run route is one change of a route at a leaf of a fabric, on the monotonic clock. */\nexport interface RunRoute {\n readonly leaf: string;\n readonly t: number;\n readonly deleted: boolean;\n readonly prefix: string;\n readonly via: string | null;\n readonly dev: string | null;\n}\n\n/**\n * A run fabric is what the routers did in a run. It holds each change of a route. It also holds the\n * count of established sessions of each leaf at the start and the end. A session that went down and\n * came back in the run raises its count.\n */\nexport interface RunFabric {\n readonly routes: readonly RunRoute[];\n readonly established: {\n readonly before: Readonly<Record<string, number>>;\n readonly after: Readonly<Record<string, number>>;\n };\n}\n\n/** A Sentinel event is one line of the observer of a group: an event that a Sentinel published, on the clock of the VM. */\nexport interface SentinelEvent {\n readonly ev: string;\n readonly t: number;\n /** This text is the address and port of the Sentinel. */\n readonly sentinel?: string;\n /** This name is the event, for example `+switch-master`. */\n readonly event?: string;\n /** This text is the detail of the event, as the Sentinel wrote it. */\n readonly detail?: string;\n readonly [field: string]: unknown;\n}\n\n/** A part is one member or Sentinel of a group, as a run found it: its address and its network namespace. */\nexport interface RunPart {\n readonly ip: string;\n readonly netns: number;\n}\n\n/** A run group is the group of a run: its parts, the primary before and after, the timeline, and the writes that it kept. */\nexport interface RunGroup {\n readonly members: Readonly<Record<string, RunPart>>;\n readonly sentinels: Readonly<Record<string, RunPart>>;\n /** This name is the member that was the primary at the start of the run. */\n readonly primary: string;\n /** This name is the part that got the fault, or null for a fault on the whole group. */\n readonly at: string | null;\n /**\n * These are the lines of the timeline from the start to the end of the run. For a group under\n * Sentinels, they are the lines of the observer of the Sentinels. For a replica set, they are\n * the log lines of the members (`ev` \"mongod\"), with their times on the monotonic clock.\n */\n readonly timeline: readonly SentinelEvent[];\n /** This name is the member that was the primary when the group converged after the run, or null when it did not converge. */\n readonly after: string | null;\n /** These are the sequence numbers that the group holds after the run, one entry for each copy, when it converged, or null. */\n readonly holds: readonly number[] | null;\n /** These are the sequence numbers that the old primary of a pair holds after its crash recovery. */\n readonly oldHolds?: readonly number[];\n /** This record tells what a recovery drill did. */\n readonly drill?: RunDrill;\n}\n\n/** A run backup is one backup of a drill: a dump with its oplog, or one slice of the oplog. */\nexport interface RunBackup {\n readonly kind: \"dump\" | \"slice\";\n /** These are the start and the end of the backup, on the wall clock of the runner, in ms. */\n readonly started: number;\n readonly ended: number;\n /** This write is the last one that the backups hold up to this backup, or null with no write. Its time is the time that the client sent it. */\n readonly last: { readonly n: number; readonly t: number } | null;\n /** This size is the size of the backup on disk. */\n readonly size: Quantity;\n}\n\n/** A run drill is what a recovery drill did in one run. */\nexport interface RunDrill {\n /** This text is the first line of `mongodump --version`. */\n readonly tool: string;\n /** This word tells where the data of the tenants came from. It is always generated data. */\n readonly seed: \"generated\";\n readonly backups: readonly RunBackup[];\n /** This hash is the hash of the data of the tenants before the first client. */\n readonly before: string;\n /** This hash is the hash of the data of the tenants after the run, or null when the set did not converge. */\n readonly after: string | null;\n /** These are the start and the end of the restore, on the wall clock of the runner, in ms, or null without a restore. */\n readonly restored: { readonly started: number; readonly ended: number } | null;\n /** This number is the first sequence number of the second client, or null with one client. */\n readonly second: number | null;\n}\n\n/**\n * A run topology tells where each socket of a run is, in a topology:\n * - `targetPeer` is the address of the peer of the target, as the target sees it: the address of\n * the last hop.\n * - `hops` are the network namespaces of the hops, by their names, with the address of the peer of\n * each hop on each side.\n */\nexport interface RunTopology {\n readonly targetPeer: string;\n readonly hops: Readonly<\n Record<string, { readonly netns: number; readonly client: string; readonly target: string }>\n >;\n}\n","import { createHash } from \"node:crypto\";\n\n/**\n * This module reads the lockfiles of a build context for the manifest of a run. A lockfile pins each\n * package that a build installs, thus its sha256 pins the packages that the image holds. The\n * versions of the packages that each manifest names directly are the part that a person reads.\n */\n\n/** A context file is a file of a build context: its path in the context, and its content. */\nexport interface ContextFile {\n readonly at: string;\n readonly content: Buffer | string;\n}\n\n/**\n * These are the names of the lockfiles that the harness records. A `go.mod` is the lockfile of a Go\n * module, because its `require` lines give exact versions. For them, Go selects the same packages\n * each time, and its checksum database examines each package.\n */\nexport const LOCK_NAMES: ReadonlySet<string> = new Set([\n \"package-lock.json\",\n \"npm-lock.json\",\n \"npm-shrinkwrap.json\",\n \"pnpm-lock.yaml\",\n \"yarn.lock\",\n \"bun.lock\",\n \"Cargo.lock\",\n \"go.mod\",\n \"go.sum\",\n \"jars.sha256\",\n]);\n\nconst nameOf = (at: string): string => at.split(\"/\").at(-1)!;\nconst folderOf = (at: string): string => at.split(\"/\").slice(0, -1).join(\"/\");\nconst textOf = (content: Buffer | string): string => (typeof content === \"string\" ? content : content.toString(\"utf8\"));\n\n/** This function gives each lockfile of a context by its path, with the sha256 of its content in hex. */\nexport const locksOf = (files: readonly ContextFile[]): Record<string, string> =>\n Object.fromEntries(\n files\n .filter((file) => LOCK_NAMES.has(nameOf(file.at)))\n .map((file) => [file.at, createHash(\"sha256\").update(file.content).digest(\"hex\")] as const)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)),\n );\n\n/** This function gives the version of each direct dependency of an npm lockfile (lockfileVersion 2 or 3). */\nexport const npmVersions = (lock: string): Record<string, string> => {\n const parsed = JSON.parse(lock) as { packages?: Record<string, { version?: string; dependencies?: Record<string, string>; optionalDependencies?: Record<string, string> }> };\n const packages = parsed.packages ?? {};\n const root = packages[\"\"] ?? {};\n const names = [...Object.keys(root.dependencies ?? {}), ...Object.keys(root.optionalDependencies ?? {})];\n const found: Record<string, string> = {};\n for (const name of names) {\n const version = packages[`node_modules/${name}`]?.version;\n if (version !== undefined) found[name] = version;\n }\n return found;\n};\n\n/** This function gives the packages of a Cargo.lock: the name, the version and the dependencies of each. */\nconst cargoPackages = (lock: string): Array<{ name: string; version: string; dependencies: string[] }> =>\n lock\n .split(/^\\[\\[package\\]\\]\\s*$/m)\n .slice(1)\n .flatMap((block) => {\n const name = /^name = \"([^\"]+)\"$/m.exec(block)?.[1];\n const version = /^version = \"([^\"]+)\"$/m.exec(block)?.[1];\n if (name === undefined || version === undefined) return [];\n const list = /^dependencies = \\[([^\\]]*)\\]/m.exec(block)?.[1] ?? \"\";\n const dependencies = [...list.matchAll(/\"([^\"]+)\"/g)].map((match) => match[1]!);\n return [{ name, version, dependencies }];\n });\n\n/**\n * This function gives the version of each direct dependency of the root package of a Cargo.lock: the\n * package that the Cargo.toml beside it names. An entry of a dependency list is a name, or a name\n * and a version when the lock holds two versions of that name.\n */\nexport const cargoVersions = (lock: string, manifest: string): Record<string, string> => {\n const rootName = /^\\[package\\][^[]*?^name\\s*=\\s*\"([^\"]+)\"/ms.exec(manifest)?.[1];\n const packages = cargoPackages(lock);\n const root = packages.find((each) => each.name === rootName);\n if (root === undefined) return {};\n const found: Record<string, string> = {};\n for (const entry of root.dependencies) {\n const [name, version] = entry.split(\" \");\n const pinned = version ?? packages.find((each) => each.name === name)?.version;\n if (name !== undefined && pinned !== undefined) found[name] = pinned;\n }\n return found;\n};\n\n/** This function gives the version of each direct requirement of a go.mod. A requirement that is `// indirect` is not direct. */\nexport const goVersions = (mod: string): Record<string, string> => {\n const found: Record<string, string> = {};\n const lines = mod.replace(/^require\\s*\\(([^)]*)\\)/gm, (_all, block: string) =>\n block\n .split(/\\r?\\n/)\n .map((line) => `require ${line.trim()}`)\n .join(\"\\n\"),\n );\n for (const line of lines.split(/\\r?\\n/)) {\n const match = /^require\\s+(\\S+)\\s+(v\\S+)(.*)$/.exec(line.trim());\n if (match === null || /\\/\\/\\s*indirect/.test(match[3]!)) continue;\n found[match[1]!] = match[2]!;\n }\n return found;\n};\n\n/** This function gives the version of each jar of a list of jars by their SHA-256: `<hex> HikariCP-7.1.0.jar` gives HikariCP 7.1.0. */\nexport const jarVersions = (list: string): Record<string, string> => {\n const found: Record<string, string> = {};\n for (const line of list.split(/\\r?\\n/)) {\n const match = /^[0-9a-f]{64}\\s+\\*?(.+?)-(\\d[^-]*(?:-[A-Za-z0-9.]+)?)\\.jar$/.exec(line.trim());\n if (match !== null) found[match[1]!] = match[2]!;\n }\n return found;\n};\n\n/**\n * This function gives the version of each package that the manifests of a context name directly,\n * by its name. It reads each npm lockfile, Cargo.lock, go.mod and list of jars. A package of a\n * workspace in the context is a package of the image too, with the version of its package.json.\n * Such a package has a name and a version, and it is not private. The record has the sha256 of\n * the lockfile of a pnpm, yarn or bun workspace (`locksOf`). This function does not read versions\n * from such a lockfile.\n */\nexport const versionsOf = (files: readonly ContextFile[]): Record<string, string> => {\n const byPath = new Map(files.map((file) => [file.at, textOf(file.content)] as const));\n const found: Record<string, string> = {};\n for (const [at, text] of [...byPath].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) {\n const name = nameOf(at);\n const folder = folderOf(at);\n const beside = (other: string): string | undefined => byPath.get(folder === \"\" ? other : `${folder}/${other}`);\n if (name === \"package-lock.json\" || name === \"npm-lock.json\" || name === \"npm-shrinkwrap.json\") Object.assign(found, npmVersions(text));\n else if (name === \"Cargo.lock\") Object.assign(found, cargoVersions(text, beside(\"Cargo.toml\") ?? \"\"));\n else if (name === \"go.mod\") Object.assign(found, goVersions(text));\n else if (name === \"jars.sha256\") Object.assign(found, jarVersions(text));\n else if (name === \"package.json\") {\n const manifest = JSON.parse(text) as { name?: string; version?: string; private?: boolean };\n if (manifest.private !== true && manifest.name !== undefined && manifest.version !== undefined) found[manifest.name] = manifest.version;\n }\n }\n return found;\n};\n","import { execFile } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { copyFileSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, posix, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { isDeepStrictEqual } from \"node:util\";\nimport type { BuildImage, Client, ClientImage, ImageSource, Shipped, ShippedImage, Subject, Target } from \"./cell.ts\";\nimport { LOCK_NAMES, locksOf, versionsOf, type ContextFile } from \"./locks.ts\";\n\n/** These are the manifests of packages that `versionsOf` reads beside the lockfiles. */\nconst READ_FOR_VERSIONS: ReadonlySet<string> = new Set([\"package.json\", \"Cargo.toml\"]);\n\n/**\n * This module resolves the source of each image (`ImageSource`) to an image that a lab can start.\n * A pinned image is its own name. A build context, or an image that the harness ships, becomes a\n * build with a tag from its content. Each image of the harness is a shipped source in one table.\n * Thus the image of a subject is data, as the image of a caller is.\n */\n\n/**\n * These are the roots of the two packages that the shipped images read: this package, and the\n * prober that Node resolves for it. In a workspace, they are two folders side by side. From npm,\n * the prober is a link, and a Docker build context does not follow a link. Thus each image builds\n * from a context that the harness copies from the two roots.\n */\nconst ROOTS: Readonly<Record<string, string>> = {\n harness: fileURLToPath(new URL(\"..\", import.meta.url)),\n prober: dirname(realpathSync(createRequire(import.meta.url).resolve(\"@mark1russell7/prober/package.json\"))),\n};\n\n/** This function gives the path of a part of a shipped image, which is `<package>/<path>` in the context. */\nconst pathOf = (part: string): string => {\n const [root, ...rest] = part.split(\"/\");\n const base = ROOTS[root!];\n if (base === undefined) throw new Error(`${part} is in no package that the images read`);\n return join(base, ...rest);\n};\n\n/** A shipped context is the build context of a shipped image: its Dockerfile, and each file or folder that the Dockerfile reads, as `<package>/<path>`. */\ninterface ShippedContext {\n readonly name: string;\n readonly dockerfile: string;\n readonly from: readonly string[];\n}\n\n/** These are the build contexts of the images that the harness ships, by their names. */\nconst SHIPPED: Readonly<Record<Shipped, ShippedContext>> = {\n // the prober, the TCP client, the injector and the namespace admin\n harness: {\n name: \"loom-harness\",\n dockerfile: \"harness/image/Dockerfile\",\n from: [\"harness/image/Dockerfile\", \"harness/image/native\", \"prober/bpf\", \"prober/native\", \"prober/fixtures\"],\n },\n // the Node client: ioredis 6.0.0 on Node 24.21.0\n node: { name: \"loom-harness-node\", dockerfile: \"harness/image/node/Dockerfile\", from: [\"harness/image/node\"] },\n // the Go HTTP client: the http.Transport of Go 1.27.2\n \"go-http\": { name: \"loom-harness-go-http\", dockerfile: \"harness/image/go-http/Dockerfile\", from: [\"harness/image/go-http\"] },\n // the Node HTTP clients: the http.Agent of Node 24.21.0, and undici 8.12.2\n \"node-http\": { name: \"loom-harness-node-http\", dockerfile: \"harness/image/node-http/Dockerfile\", from: [\"harness/image/node-http\"] },\n // the MongoDB client: the driver mongodb 7.7.0 on Node 24.21.0, and its step-down script\n mongo: { name: \"loom-harness-mongo\", dockerfile: \"harness/image/mongo/Dockerfile\", from: [\"harness/image/mongo\"] },\n // the PostgreSQL client: node-postgres (pg 8.23.1 with pg-pool 3.14.0) on Node 24.21.0\n pg: { name: \"loom-harness-pg\", dockerfile: \"harness/image/pg/Dockerfile\", from: [\"harness/image/pg\"] },\n // the client of HikariCP 7.1.0 over pgjdbc 42.7.13 on Temurin 21.0.12.1: its jars by their SHA-256\n hikari: { name: \"loom-harness-hikari\", dockerfile: \"harness/image/hikari/Dockerfile\", from: [\"harness/image/hikari\"] },\n // the RabbitMQ client: amqplib 2.2.0 on Node 24.21.0\n amqp: { name: \"loom-harness-amqp\", dockerfile: \"harness/image/amqp/Dockerfile\", from: [\"harness/image/amqp\"] },\n // the Kafka client: kafka-clients 4.3.2 on Temurin 21.0.12.1, with its admin: its jars by their SHA-256\n kafka: { name: \"loom-harness-kafka\", dockerfile: \"harness/image/kafka/Dockerfile\", from: [\"harness/image/kafka\"] },\n // the gRPC client: grpc-js 1.14.6 on Node 24.21.0\n grpc: { name: \"loom-harness-grpc\", dockerfile: \"harness/image/grpc/Dockerfile\", from: [\"harness/image/grpc\"] },\n // the gRPC server: a server of grpc-go v1.84.0, built with Go 1.27.2, that can hold a reply\n \"grpc-go\": { name: \"loom-harness-grpc-go\", dockerfile: \"harness/image/grpc-go/Dockerfile\", from: [\"harness/image/grpc-go\"] },\n // etcd v3.6.15 from its image, on Alpine: a shell to hold it, and its binaries unchanged\n etcd: { name: \"loom-harness-etcd\", dockerfile: \"harness/image/etcd/Dockerfile\", from: [\"harness/image/etcd\"] },\n // the router of a middlebox chain: NAT and conntrack, with a form for a forgotten flow\n router: { name: \"loom-harness-router\", dockerfile: \"harness/image/router/Dockerfile\", from: [\"harness/image/router\"] },\n // PgBouncer 1.26.1, a pooler hop: built from its release tarball, by its SHA-256\n pgbouncer: { name: \"loom-harness-pgbouncer\", dockerfile: \"harness/image/pgbouncer/Dockerfile\", from: [\"harness/image/pgbouncer\"] },\n // Pgpool-II 4.7.3, a pooler hop: built from the source of its tag, by its SHA-256\n pgpool: { name: \"loom-harness-pgpool\", dockerfile: \"harness/image/pgpool/Dockerfile\", from: [\"harness/image/pgpool\"] },\n // docker-proxy of Docker Engine 29.1.5, a proxy hop: its binary from the static bundle of Docker, by its SHA-256\n \"docker-proxy\": { name: \"loom-harness-docker-proxy\", dockerfile: \"harness/image/docker-proxy/Dockerfile\", from: [\"harness/image/docker-proxy\"] },\n};\n\n/** These are the names of the images that the harness ships. */\nexport const SHIPPED_NAMES: readonly [Shipped, ...Shipped[]] = Object.keys(SHIPPED) as [Shipped, ...Shipped[]];\n\n/** This function gives the source of a shipped image. */\nexport const shippedSource = (name: Shipped): ImageSource => ({ kind: \"shipped\", name });\nconst shipped = shippedSource;\n\n/** This function gives the version of the package of the harness, from its package.json. */\nexport const harnessVersion = (): string =>\n (JSON.parse(readFileSync(join(ROOTS[\"harness\"]!, \"package.json\"), \"utf8\")) as { version: string }).version;\n\n/**\n * These are the clients that the harness ships, by their names (`Client`): the image of each, and\n * its command. A cell that names a client gets this image and this command.\n */\nexport const SHIPPED_CLIENTS: Readonly<Record<Client, ClientImage>> = {\n tcp: { image: shipped(\"harness\"), command: [\"/opt/harness/client\"] },\n ioredis: { image: shipped(\"node\"), command: [\"node\", \"/opt/harness/node/client-ioredis.mjs\"] },\n \"go-http\": { image: shipped(\"go-http\"), command: [\"/opt/harness/client-go\"] },\n \"http-agent\": { image: shipped(\"node-http\"), command: [\"node\", \"/opt/harness/node-http/client-agent.mjs\"] },\n undici: { image: shipped(\"node-http\"), command: [\"node\", \"/opt/harness/node-http/client-undici.mjs\"] },\n mongodb: { image: shipped(\"mongo\"), command: [\"node\", \"/opt/harness/mongo/client-mongo.mjs\"] },\n pg: { image: shipped(\"pg\"), command: [\"node\", \"/opt/harness/pg/client-pg.mjs\"] },\n hikari: {\n image: shipped(\"hikari\"),\n command: [\"java\", \"-Dslf4j.internal.verbosity=ERROR\", \"-cp\", \"/opt/harness/hikari/classes:/opt/harness/hikari/lib/*\", \"ClientHikari\"],\n },\n amqp: { image: shipped(\"amqp\"), command: [\"node\", \"/opt/harness/amqp/client-amqp.mjs\"] },\n grpc: { image: shipped(\"grpc\"), command: [\"node\", \"/opt/harness/grpc/client-grpc.mjs\"] },\n load: { image: shipped(\"harness\"), command: [\"/opt/harness/load\"] },\n};\n\n/** These are the names of the clients that the harness ships. */\nexport const CLIENT_NAMES: readonly [Client, ...Client[]] = Object.keys(SHIPPED_CLIENTS) as [Client, ...Client[]];\n\n/** This function gives the command of a program of a subject by its role. It fails when the subject has no such program. */\nexport const programOf = (subject: Subject, role: string): readonly string[] => {\n const program = Object.hasOwn(subject.programs, role) ? subject.programs[role] : undefined;\n if (program === undefined) throw new Error(`the subject ${subject.name} has no program ${role}`);\n return program;\n};\n\n/**\n * This function gives the client of a subject for a cell (`Cell.client`): the image of the subject,\n * with the command of a program. A client that the harness ships goes by its name, thus its records\n * stay the same.\n */\nexport const subjectClient = (subject: Subject, role = \"client\"): Client | ClientImage => {\n const client: ClientImage = { image: subject.image, command: programOf(subject, role) };\n return CLIENT_NAMES.find((name) => isDeepStrictEqual(SHIPPED_CLIENTS[name], client)) ?? client;\n};\n\n/** This pattern matches an image pinned by digest: a name, a tag or none, then `@sha256:` and 64 hex digits. */\nexport const PINNED: RegExp = /^[a-z0-9][A-Za-z0-9._\\-/:]*@sha256:[0-9a-f]{64}$/;\n/** This pattern matches the repository of a tag that the harness builds. Its parts are lower case, with `/` between them. */\nexport const REPOSITORY: RegExp = /^[a-z0-9]+(?:[._-]+[a-z0-9]+)*(?:\\/[a-z0-9]+(?:[._-]+[a-z0-9]+)*)*$/;\n\n/** This function tells if a path stays in its context: relative, with `/` between its parts, and no `..`. */\nexport const inContext = (path: string): boolean =>\n typeof path === \"string\" &&\n path.length > 0 &&\n !path.startsWith(\"/\") &&\n !path.includes(\"\\\\\") &&\n !/^[A-Za-z]:/.test(path) &&\n !path.split(\"/\").includes(\"..\");\n\n/** This function tells if a value is a command: an array of one text or more. */\nconst isCommand = (value: unknown): boolean =>\n Array.isArray(value) && value.length > 0 && value.every((word) => typeof word === \"string\" && word.length > 0);\n\n/**\n * This function gives the problems of the source of an image, as data. A pinned image names a\n * digest. A build names a repository, a context, and paths that stay in the context. A shipped image\n * is one that the harness ships.\n */\nexport const imageProblems = (source: ImageSource): string[] => {\n const kind = (source as { readonly kind?: unknown } | null)?.kind;\n if (kind === \"pinned\") {\n const { image } = source as { readonly image?: unknown };\n return typeof image === \"string\" && PINNED.test(image) ? [] : [`${String(image)} is not pinned by digest: name@sha256:<64 hex digits>`];\n }\n if (kind === \"shipped\") {\n const { name } = source as { readonly name?: unknown };\n return typeof name === \"string\" && Object.hasOwn(SHIPPED, name) ? [] : [`the harness ships no image ${String(name)}: ${SHIPPED_NAMES.join(\", \")}`];\n }\n if (kind === \"build\") {\n const build = source as Partial<BuildImage>;\n const problems: string[] = [];\n if (typeof build.name !== \"string\" || !REPOSITORY.test(build.name)) problems.push(`${String(build.name)} is not the name of a repository`);\n if (typeof build.context !== \"string\" || build.context.length === 0) problems.push(\"the build names no context\");\n if (build.files !== undefined && (!Array.isArray(build.files) || build.files.length === 0)) problems.push(\"the build names no files\");\n for (const path of [build.dockerfile ?? \"Dockerfile\", ...(Array.isArray(build.files) ? build.files : [])])\n if (!inContext(path)) problems.push(`${String(path)} is not a path in the context`);\n return problems;\n }\n return [`${JSON.stringify(kind)} is not a kind of image source: pinned, build or shipped`];\n};\n\n/**\n * This function gives the problems of a subject, as data. A subject has a name and a base that a\n * digest pins. Its image source has no problems, and each of its programs is a command.\n */\nexport const subjectProblems = (subject: Subject): string[] => {\n const programs = (subject as { readonly programs?: unknown }).programs;\n const roles = typeof programs === \"object\" && programs !== null ? Object.entries(programs) : [];\n return [\n ...(typeof subject.name === \"string\" && subject.name.length > 0 ? [] : [\"the subject has no name\"]),\n ...(typeof subject.base === \"string\" && PINNED.test(subject.base) ? [] : [`the base ${String(subject.base)} is not pinned by digest`]),\n ...imageProblems(subject.image),\n ...(roles.length === 0 ? [\"the subject has no programs\"] : []),\n ...roles.flatMap(([role, command]) => (isCommand(command) ? [] : [`the program ${role} is not a command`])),\n ];\n};\n\n/**\n * This function gives each `FROM` of a Dockerfile that no digest pins. A `FROM` of an earlier stage\n * by its name, or of `scratch`, needs no digest. A base from an `ARG` has no digest that the harness\n * can read, thus it is in the list too.\n */\nexport const unpinnedBases = (dockerfile: string): string[] => basesOf(dockerfile).filter((image) => !PINNED.test(image));\n\n/**\n * This function gives each base of a Dockerfile, in the sequence of the file. A base is the image of\n * a `FROM` that is not `scratch` or an earlier stage by its name.\n */\nexport const basesOf = (dockerfile: string): string[] => {\n const stages = new Set<string>([\"scratch\"]);\n const found: string[] = [];\n for (const line of dockerfile.replace(/\\\\\\r?\\n/g, \" \").split(/\\r?\\n/)) {\n const from = /^\\s*FROM\\s+(.+)$/i.exec(line)?.[1];\n if (from === undefined) continue;\n const [image, as, stage] = from.split(/\\s+/).filter((word) => !word.startsWith(\"--\"));\n if (image === undefined) continue;\n if (!stages.has(image.toLowerCase())) found.push(image);\n if (as?.toLowerCase() === \"as\" && stage !== undefined) stages.add(stage.toLowerCase());\n }\n return found;\n};\n\n/** These are the lockfiles of each manifest of packages. A manifest without a lockfile installs other packages each time. */\nconst LOCKFILES: Readonly<Record<string, readonly string[]>> = {\n \"package.json\": [\"package-lock.json\", \"npm-lock.json\", \"npm-shrinkwrap.json\", \"pnpm-lock.yaml\", \"yarn.lock\", \"bun.lock\"],\n \"Cargo.toml\": [\"Cargo.lock\"],\n};\n\n/**\n * This function gives each manifest of packages in a context (`package.json`, `Cargo.toml`) that\n * has no lockfile in its folder or in a folder above it. The paths are the paths in the context.\n */\nexport const unlockedManifests = (paths: readonly string[]): string[] => {\n const present = new Set(paths);\n return paths.filter((path) => {\n const parts = path.split(\"/\");\n const locks = LOCKFILES[parts.at(-1)!];\n if (locks === undefined) return false;\n for (let depth = parts.length - 1; depth >= 0; depth -= 1) {\n const folder = parts.slice(0, depth).join(\"/\");\n if (locks.some((lock) => present.has(folder === \"\" ? lock : `${folder}/${lock}`))) return false;\n }\n return true;\n });\n};\n\n/**\n * An image plan tells how the harness gets the image of a source. It is a pull of an image by its\n * name, or a build of a context. A build has its tag, its Dockerfile, and each file with its path.\n */\nexport type ImagePlan =\n | { readonly kind: \"pull\"; readonly image: string }\n | {\n readonly kind: \"build\";\n readonly tag: string;\n readonly dockerfile: string;\n readonly files: ReadonlyArray<{ readonly file: string; readonly at: string }>;\n /** This record tells what the image holds, for the manifest of a run. */\n readonly record: BuildRecord;\n };\n\n/**\n * A build record tells what an image that the harness builds holds, for the manifest of a run. The\n * tag is a sha256 of each file of the context. The bases are pinned by digest, and each lockfile\n * has its sha256. Thus the record pins each program of the image. The versions are the part that a\n * person reads.\n */\nexport interface BuildRecord {\n readonly tag: string;\n /** These are the bases of its Dockerfile, each pinned by digest (`basesOf`). */\n readonly bases: readonly string[];\n /** These are the lockfiles of its context by their path, each with the sha256 of its content (`locksOf`). */\n readonly locks: Readonly<Record<string, string>>;\n /** These are the versions of the packages that its manifests name directly, by their names (`versionsOf`). */\n readonly versions: Readonly<Record<string, string>>;\n}\n\n/** This function gives each file under a path, in a stable sequence, but not the output of a local build. */\nconst sources = (path: string): string[] => {\n if (statSync(path).isFile()) return [path];\n return readdirSync(path, { withFileTypes: true })\n .filter((entry) => entry.name !== \"target\" && entry.name !== \"node_modules\")\n .flatMap((entry) => sources(join(path, entry.name)))\n .sort();\n};\n\n/** A part is a file or a folder of a context: its path on the machine, and its path in the context. */\ninterface Part {\n readonly path: string;\n readonly at: string;\n}\n\n/** This function gives the name, the Dockerfile and the parts of the context of a build or of a shipped image. */\nconst contextOf = (source: BuildImage | ShippedImage): { name: string; dockerfile: string; parts: Part[] } => {\n if (source.kind === \"shipped\") {\n const { name, dockerfile, from } = SHIPPED[source.name];\n return { name, dockerfile, parts: from.map((part) => ({ path: pathOf(part), at: part })) };\n }\n const root = resolve(source.context);\n return {\n name: source.name,\n dockerfile: posix.normalize(source.dockerfile ?? \"Dockerfile\"),\n parts: (source.files ?? [\".\"]).map((file) => ({ path: resolve(root, file), at: file === \".\" ? \"\" : file })),\n };\n};\n\n/** This function gives a text for a source in a message. */\nconst labelOf = (source: ImageSource): string =>\n source.kind === \"pinned\" ? source.image : `the ${source.kind === \"build\" ? \"build\" : \"shipped image\"} ${source.name}`;\n\nconst shippedPlans = new Map<Shipped, ImagePlan>();\n\n/**\n * This function gives the plan of the image of a source. A stock image by its name, or a pinned\n * image, is a pull. A build or a shipped image is a build. Its tag is its name, then 12 digits of a\n * sha256. The sha256 reads the path of the Dockerfile, and the path and the content of each file.\n * Thus a changed program cannot operate as an old one.\n *\n * A build fails before it starts on a problem of its source. It also fails on a `FROM` that no\n * digest pins, and on a manifest of packages without a lockfile.\n */\nexport const planOf = (source: string | ImageSource): ImagePlan => {\n if (typeof source === \"string\") return { kind: \"pull\", image: source };\n const problems = imageProblems(source);\n if (problems.length > 0) throw new Error(`${labelOf(source)}: ${problems.join(\", \")}`);\n if (source.kind === \"pinned\") return { kind: \"pull\", image: source.image };\n const known = source.kind === \"shipped\" ? shippedPlans.get(source.name) : undefined;\n if (known !== undefined) return known;\n const { name, dockerfile, parts } = contextOf(source);\n const files = parts.flatMap((part) =>\n sources(part.path).map((file) => ({ file, at: join(part.at, relative(part.path, file)).replaceAll(\"\\\\\", \"/\") })),\n );\n const docker = files.find((each) => each.at === dockerfile);\n if (docker === undefined) throw new Error(`${labelOf(source)}: the context has no ${dockerfile}`);\n const bases = basesOf(readFileSync(docker.file, \"utf8\"));\n const unpinned = bases.filter((image) => !PINNED.test(image));\n const unlocked = unlockedManifests(files.map((each) => each.at));\n if (unpinned.length > 0 || unlocked.length > 0)\n throw new Error(\n `${labelOf(source)}: ${[\n ...unpinned.map((image) => `the base ${image} is not pinned by digest`),\n ...unlocked.map((path) => `${path} has no lockfile`),\n ].join(\", \")}`,\n );\n const hash = createHash(\"sha256\");\n hash.update(dockerfile);\n const read: ContextFile[] = [];\n for (const { file, at } of files) {\n const content = readFileSync(file);\n hash.update(at);\n hash.update(content);\n if (LOCK_NAMES.has(posix.basename(at)) || READ_FOR_VERSIONS.has(posix.basename(at))) read.push({ at, content });\n }\n const tag = `${name}:${hash.digest(\"hex\").slice(0, 12)}`;\n const record: BuildRecord = { tag, bases, locks: locksOf(read), versions: versionsOf(read) };\n const plan: ImagePlan = { kind: \"build\", tag, dockerfile, files, record };\n if (source.kind === \"shipped\") shippedPlans.set(source.name, plan);\n return plan;\n};\n\n/** These counts are how many lines of the output of a failed build its error holds: its error lines, then its last lines. */\nconst BUILD_ERRORS = 10;\nconst BUILD_TAIL = 15;\n\n/**\n * This function gives the lines of the output of a failed build that its error holds. They are each\n * line that names an error, for example the diagnostic of a compiler, then the last lines.\n */\nexport const buildFailure = (output: string): string => {\n const lines = output.trim().split(/\\r?\\n/);\n const tail = lines.slice(-BUILD_TAIL);\n const errors = lines.slice(0, -BUILD_TAIL).filter((line) => /\\berror\\b/i.test(line)).slice(0, BUILD_ERRORS);\n return [...errors, ...(errors.length > 0 ? [\"...\"] : []), ...tail].join(\"\\n\");\n};\n\n/**\n * This function builds a staged context with the CLI of Docker, thus with BuildKit where the CLI has\n * it. Testcontainers reads nothing of the output of a build, and it fails with \"Failed to build\n * image\" only. The error of a failed build here holds its error lines and the end of its output.\n */\nconst dockerBuild = (context: string, plan: Extract<ImagePlan, { readonly kind: \"build\" }>): Promise<void> =>\n new Promise((resolve, reject) => {\n const args = [\"build\", \"--progress=plain\", \"--tag\", plan.tag, \"--file\", join(context, plan.dockerfile), context];\n execFile(\"docker\", args, { maxBuffer: 64 * 1024 * 1024 }, (error, _stdout, stderr) => {\n if (error === null) {\n resolve();\n return;\n }\n reject(new Error(`docker build of ${plan.tag} failed (${error.message.split(\"\\n\")[0]}):\\n${buildFailure(String(stderr))}`));\n });\n });\n\nconst built = new Map<string, Promise<string>>();\n\n/**\n * These are the waits before each next try of a build that fails, in milliseconds. A fault of a\n * registry or of the network often stops in seconds. In run 39278390448 of the cells, two runners\n * failed a build in the same second, and the same builds then passed.\n */\nexport const BUILD_WAITS: readonly number[] = [5_000, 20_000];\n\n/**\n * This function builds the image of a plan one time for each process, from a new context that it\n * copies the files into. Docker keeps its layers between runs. A build that fails tries again after\n * each wait (`waits`), and it writes each failure to stderr. A build that fails each try leaves the\n * cache. Thus a later cell builds again after a fault of Docker or of the network.\n */\nexport const buildOnce = (\n plan: Extract<ImagePlan, { readonly kind: \"build\" }>,\n builder: (context: string, plan: Extract<ImagePlan, { readonly kind: \"build\" }>) => Promise<void> = dockerBuild,\n waits: readonly number[] = BUILD_WAITS,\n): Promise<string> => {\n let tag = built.get(plan.tag);\n if (tag === undefined) {\n const building = (async () => {\n const context = mkdtempSync(join(tmpdir(), `${plan.tag.split(\":\")[0]!.replaceAll(\"/\", \"-\")}-`));\n try {\n for (const { file, at } of plan.files) {\n mkdirSync(dirname(join(context, at)), { recursive: true });\n copyFileSync(file, join(context, at));\n }\n for (let attempt = 0; ; attempt += 1) {\n try {\n await builder(context, plan);\n break;\n } catch (error) {\n const wait = waits[attempt];\n if (wait === undefined) throw error;\n process.stderr.write(`the build of ${plan.tag} failed (${error instanceof Error ? error.message : String(error)}): try ${attempt + 2} after ${wait} ms\\n`);\n await new Promise((resolve) => setTimeout(resolve, wait));\n }\n }\n } finally {\n rmSync(context, { recursive: true, force: true });\n }\n return plan.tag;\n })();\n // a failed build does not stay: the next caller tries again\n building.catch(() => {\n if (built.get(plan.tag) === building) built.delete(plan.tag);\n });\n built.set(plan.tag, building);\n tag = building;\n }\n return tag;\n};\n\n/**\n * This function gives an image that a lab can start, from its source. It is the name of a stock\n * image or of a pinned image, which the lab pulls, or the tag of a build. A build goes through the CLI\n * of Docker.\n */\nexport const imageOf = async (source: string | ImageSource): Promise<string> => {\n const plan = planOf(source);\n return plan.kind === \"pull\" ? plan.image : buildOnce(plan);\n};\n\n/**\n * This function gives the build record of the image of a source, for the manifest of a run. A\n * stock image by its name, or a pinned image, is a pull, and it has no build record.\n */\nexport const buildRecordOf = (source: string | ImageSource): BuildRecord | undefined => {\n const plan = planOf(source);\n return plan.kind === \"build\" ? plan.record : undefined;\n};\n\n/** This function gives the image of the harness: the prober, the TCP client, the injector and the namespace admin. */\nexport const harnessImage = (): Promise<string> => imageOf(shipped(\"harness\"));\n/** This function gives the image of the Node client. */\nexport const nodeClientImage = (): Promise<string> => imageOf(shipped(\"node\"));\n/** This function gives the image of the router. */\nexport const routerImage = (): Promise<string> => imageOf(shipped(\"router\"));\n/** This function gives the image of the Go HTTP client. */\nexport const goHttpImage = (): Promise<string> => imageOf(shipped(\"go-http\"));\n/** This function gives the image of the Node HTTP clients. */\nexport const nodeHttpImage = (): Promise<string> => imageOf(shipped(\"node-http\"));\n/** This function gives the image of the MongoDB client. */\nexport const mongoClientImage = (): Promise<string> => imageOf(shipped(\"mongo\"));\n/** This function gives the image of the PostgreSQL client. */\nexport const pgClientImage = (): Promise<string> => imageOf(shipped(\"pg\"));\n/** This function gives the image of the client of HikariCP over pgjdbc. */\nexport const hikariClientImage = (): Promise<string> => imageOf(shipped(\"hikari\"));\n/** This function gives the image of the RabbitMQ client: amqplib, with the client and the admin of a cluster cell. */\nexport const amqpClientImage = (): Promise<string> => imageOf(shipped(\"amqp\"));\n/** This function gives the image of the Kafka client: kafka-clients on Java, with the client and the admin of a Kafka cell. */\nexport const kafkaClientImage = (): Promise<string> => imageOf(shipped(\"kafka\"));\n\n/** This function gives the image of the gRPC client of the harness: grpc-js 1.14.6 on Node 24.21.0, with its client. */\nexport const grpcClientImage = (): Promise<string> => imageOf(shipped(\"grpc\"));\n\n/**\n * This function gives the source of the image that a target operates. It is the image of its\n * subject (`Target.subject`), or its stock image.\n */\nexport const targetSourceOf = (target: Target): string | ImageSource => target.subject ?? target.image;\n\n/** This function gives the image that a target operates: the image of its subject, or its stock image. */\nexport const targetImageOf = (target: Target): Promise<string> => imageOf(targetSourceOf(target));\n"],"mappings":"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"}
|