@mark1russell7/harness 0.11.1 → 0.11.3
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 +39 -7
- package/build/cell-BmSnJJtn.d.ts +993 -0
- package/build/image-Q_fs3rCC.js +410 -0
- package/build/image-Q_fs3rCC.js.map +1 -0
- package/build/index.d.ts +117 -938
- package/build/index.js +28 -179
- package/build/index.js.map +1 -1
- package/build/schema.d.ts +19 -0
- package/build/schema.js +56 -0
- package/build/schema.js.map +1 -0
- package/image/pg/Dockerfile +11 -0
- package/image/pg/client-pg.mjs +166 -0
- package/image/pg/npm-lock.json +160 -0
- package/image/pg/package.json +9 -0
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ The harness does not compute predictions. The author of a cell computes the pred
|
|
|
9
9
|
A **cell** (`cell.ts`) is all that one measurement needs, as data:
|
|
10
10
|
|
|
11
11
|
- **The topology.** A stock target image, pinned by digest, and the hops between the client and the target (`Cell.topology`). Without hops, the client and the target are a direct pair. A replicated group under its Sentinels can replace the target (`Cell.group`).
|
|
12
|
+
- **The subject.** The program under test, as data. Its image is an image pinned by digest, a build context, or an image that the harness ships (`Cell.client`, `Target.subject`).
|
|
12
13
|
- **The settings.** The sysctls of the client namespace, and the socket options of the client.
|
|
13
14
|
- **The workload.** A connection that stays idle, a connection with a request in flight, or a stream of requests. A stream of `GET` requests for a long value keeps many segments in flight.
|
|
14
15
|
- **The fault.** A black hole, a reset, a frozen process, or a stall. In a topology, also a fault at a router or a proxy, or a router that forgets a flow. Also a proxy that closes an idle connection, or a toxic of Toxiproxy.
|
|
@@ -101,24 +102,52 @@ A fault names a member by its role at the start of a run: `primary`, or `replica
|
|
|
101
102
|
|
|
102
103
|
## The clients and the agents
|
|
103
104
|
|
|
104
|
-
- **A client image** (`ClientImage`, `Cell.client`) replaces the TCP client of the harness in each run. It operates in the namespace of the client. Its command gets the arguments of the TCP client, and it prints the same JSON lines.
|
|
105
|
+
- **A client image** (`ClientImage`, `Cell.client`) replaces the TCP client of the harness in each run. It operates in the namespace of the client. Its command gets the arguments of the TCP client, and it prints the same JSON lines. Its image is a stock image by its name, or the source of the image of a subject (`ImageSource`).
|
|
106
|
+
- **A client by its name** (`Cell.client`) is a client that the harness ships (`SHIPPED_CLIENTS`): the source of its image and its command. Thus each client of the harness is data too.
|
|
105
107
|
- **An HTTP client** (`Cell.client`: `go-http`, `http-agent` or `undici`) is a client of the harness from its own image. It sends a GET of the root, then a second request (`probe.http`):
|
|
106
108
|
- after the signal (`inflight`), into the fault
|
|
107
109
|
- or by itself, a gap after its first reply (`gap`), for the keep-alive race.
|
|
108
110
|
- **The lines of an HTTP client.** It writes the lines of the TCP client, with the number `i` of the request on each line of a request. It also writes the connection of each try of a request (`request`, then `retry`), with its local address and a flag for a reused connection. Each connection gets a `conn` line, and each end of a connection a line (`eof`, `conn-error`, `closed`). The end of the second request is a `done` line.
|
|
109
111
|
- **A target with a script.** A target can take files before it starts (`Target.files`), for example the script of a Node server on the stock Node image. A target that writes JSON lines (`Target.lines`) gives them to each run (`Run.server`). The manifest holds the sha256 of each file and the first line of the target.
|
|
110
|
-
- **The WebSocket client** (`Cell.client` is `procedures-ws`) is the `WebSocketTransport` of `@procedures/client` 0.7.0. The cell gives each of its settings (`probe.ws`): the class of its sockets, the heartbeat, the reconnect, and the times of a call.
|
|
112
|
+
- **The WebSocket client** (`Cell.client` is `procedures-ws`) is the `WebSocketTransport` of `@procedures/client` 0.7.0: the `client` program of the `procedures` subject. The cell gives each of its settings (`probe.ws`): the class of its sockets, the heartbeat, the reconnect, and the times of a call.
|
|
111
113
|
- Its first request is `harness.echo`. In flight, its request into the fault is `harness.echo`, or `harness.hold` for a held reply.
|
|
112
114
|
- It writes the lines of the TCP client. It also writes a line for each act of the program: `ping`, `drop`, `reconnecting` and `dial`.
|
|
113
115
|
- The class of its sockets is the global `WebSocket` of Node, with the transport of the root entry. Or it is the `WebSocket` of the `ws` package, with the transport of the Node entry `@procedures/client/node`.
|
|
114
116
|
- The transport gets a subclass of that class. The subclass writes a line, then gives the work to its class. Thus the transport, undici and ws operate as they do without it.
|
|
115
117
|
- The subclass has `terminate()` only when its class has it, because the transport drops a socket with `terminate()` when it exists.
|
|
116
|
-
- **A target from
|
|
118
|
+
- **A target from the image of a subject** (`Target.subject`). The target operates the image of a subject in place of its stock image. `Target.image` is then the stock base of that image, and the manifest names both. The procedures server is a script in the image of the WebSocket client (`server-ws.mjs`). Thus the client and the server use one release of the package.
|
|
119
|
+
- **A target from an image of the harness** (`Target.harness`) is a short form. `harness: "procedures"` is `subject: { kind: "shipped", name: "procedures" }`.
|
|
117
120
|
- **An agent** (`lab.agent(image, command, { pid })`) is a container in the network namespace of the target. With `pid`, it also shares the process namespace of the target, thus it can signal the server process. Its JSON lines and its `exec` stay available to the caller.
|
|
118
121
|
|
|
122
|
+
## The subjects
|
|
123
|
+
|
|
124
|
+
A subject (`Subject`, `image.ts`) is the program under test, as data. The harness does not import the program. It starts the image of the program. Thus another repository can give its own subject, with its own release in it.
|
|
125
|
+
|
|
126
|
+
- **The source of an image** (`ImageSource`) tells where the image comes from:
|
|
127
|
+
- `pinned`: an image of a registry with its digest, `name@sha256:<64 hex digits>`. The lab pulls it.
|
|
128
|
+
- `build`: a build context that the caller gives. `context` is its folder, `dockerfile` is its Dockerfile, and `files` are the files and folders that the Dockerfile reads.
|
|
129
|
+
- `shipped`: an image that the harness ships, by its name (`SHIPPED_NAMES`).
|
|
130
|
+
- **A relative folder** of a build starts at the working folder of the process. A record holds the source of each image as the cell gives it. Thus give a relative folder, and the record names no path of the machine.
|
|
131
|
+
- **A subject** gives the source of its image and its stock base, pinned by digest. It also gives the command of each of its programs by their roles, for example `client` and `server`.
|
|
132
|
+
- **A subject in a cell.** `subjectClient(subject)` gives the client of a cell, and `programOf(subject, role)` gives the command of a program. A target operates the image of the subject (`Target.subject`), and its stock image is the base of the subject.
|
|
133
|
+
- **A client that the harness ships keeps its name.** `subjectClient` gives `procedures-ws` for the `procedures` subject. Thus the records of a cell on that subject stay the same.
|
|
134
|
+
- **The subjects of the harness** (`SUBJECTS`). `procedures` is `@procedures/client` 0.7.0 on Node: its WebSocket client and its server, from one image.
|
|
135
|
+
|
|
136
|
+
These rules keep a build the same in each run:
|
|
137
|
+
|
|
138
|
+
- A pinned image names its digest. A tag alone can move.
|
|
139
|
+
- Each `FROM` of the Dockerfile of a build names a digest, an earlier stage, or `scratch`.
|
|
140
|
+
- Each `package.json` and `Cargo.toml` of a build has a lockfile in its folder or in a folder above it.
|
|
141
|
+
- Each path of a build stays in its context.
|
|
142
|
+
- The tag of a build is its name, then 12 digits of a sha256. The sha256 reads the path of the Dockerfile, and the path and the content of each file. Thus a changed program cannot operate as an old one.
|
|
143
|
+
|
|
144
|
+
The images that the harness ships obey the same rules. A source that breaks a rule fails before Docker starts (`planOf`, `imageProblems`, `subjectProblems`). A pinned image, or a stock image by its name, does not load Testcontainers before the lab starts it.
|
|
145
|
+
|
|
146
|
+
The entry `@mark1russell7/harness/schema` has the zod schemas of a source (`imageSourceSchema`), a client (`clientSchema`) and a subject (`subjectSchema`). A runner that reads a subject as JSON examines it with them. The root entry does not import zod.
|
|
147
|
+
|
|
119
148
|
## The images
|
|
120
149
|
|
|
121
|
-
The harness
|
|
150
|
+
The harness ships eight images, each as a shipped source (`ImageSource`). The tag of each image comes from the path of its Dockerfile and the content of all the files that its Dockerfile reads. The harness copies these files into a new build context, from its own package and from the prober package. Thus the build works in a workspace and from npm.
|
|
122
151
|
|
|
123
152
|
- **The harness image** (`image/Dockerfile`) has two stages, each a digest-pinned Alpine. `Cargo.lock` pins the crates. The image holds:
|
|
124
153
|
- the kernel program of the prober, compiled against the BTF of the kernel that builds it, and its Rust collector.
|
|
@@ -129,12 +158,13 @@ The harness builds six images. The tag of each image comes from the content of a
|
|
|
129
158
|
- **The Node client image** (`image/node`) is a driver client: ioredis 6.0.0 on Node 24.21.0. The digest of the image and an npm lockfile pin them. `client-ioredis.mjs` prints the lines of the TCP client on the same clock, and it adds the status changes of ioredis as `status` lines. The same image holds the client of a group in Sentinel mode (`client-sentinel.mjs`) and the observer of its Sentinels (`observer.mjs`).
|
|
130
159
|
- **The Go HTTP image** (`image/go-http`) is the `http.Transport` of Go 1.27.2 at its defaults (`client-go`). The program uses the standard library only, thus the digest of the Go builder pins all of its code. The build does `gofmt` and `go vet` first. A wrapper on each connection writes what the read loop of the transport reads. The trace of the transport writes each try of a request.
|
|
131
160
|
- **The Node HTTP image** (`image/node-http`) holds the `http.Agent` of Node 24.21.0 (`client-agent.mjs`) and an HTTP/1.1 `Client` of undici 8.12.2 (`client-undici.mjs`). The digest of the image and an npm lockfile pin them. It is apart from the image of ioredis, thus the cells of ioredis keep their image.
|
|
161
|
+
- **The PostgreSQL client image** (`image/pg`) is node-postgres: pg 8.23.1 with pg-pool 3.14.0 on Node 24.21.0. The digest of the image and an npm lockfile pin them. `client-pg.mjs` makes one `pg.Pool` with the options of the cell (`Cell.driverOptions`), and it prints the lines of the TCP client on the same clock. The first query makes the connection, and the reply gives the client back to the pool, where it is idle. An error of the pool for that idle client is the detection of an idle cell. The probe is `{ driver: { name: "pg", query } }`, with `SELECT 1` as the query when the cell gives none.
|
|
132
162
|
- **The MongoDB client image** (`image/mongo`) is the driver mongodb 7.7.0 on Node 24.21.0. The digest of the image and an npm lockfile pin them. `client-mongo.mjs` prints the lines of the TCP client on the same clock:
|
|
133
163
|
- Each socket of the driver is a `socket` line with its role: `monitor`, `rtt` or `pool`. The stack of the call that makes the socket gives the role.
|
|
134
164
|
- The events of the driver are `sdam` lines (the heartbeats and the changes of each server), `cmap` lines (the pools) and `command` lines (each try of an operation).
|
|
135
165
|
- Each operation is a `sent` line, then an `ack` line or a `fail` line. An `ack` line has the server that accepted it. A `fail` line has the name and the labels of its error.
|
|
136
166
|
- The same image holds `stepdown-mongo.mjs`, the sender of `replSetStepDown`.
|
|
137
|
-
- **The procedures image** (`image/procedures`) holds `@procedures/client` 0.7.0 and `ws` 8.22.0 on Node 24.21.0, whose WebSocket is undici 7.29.1. The digest of the image and an npm lockfile pin them. It holds the WebSocket client (`client-ws.mjs`) and the script of the procedures server (`server-ws.mjs`). Each takes each of its settings as a flag, and it has no default. The lockfile holds one copy of `ws`, and the client stops when `@procedures/client/node` resolves another one.
|
|
167
|
+
- **The procedures image** (`image/procedures`) is the image of the `procedures` subject. It holds `@procedures/client` 0.7.0 and `ws` 8.22.0 on Node 24.21.0, whose WebSocket is undici 7.29.1. The digest of the image and an npm lockfile pin them. It holds the WebSocket client (`client-ws.mjs`) and the script of the procedures server (`server-ws.mjs`). Each takes each of its settings as a flag, and it has no default. The lockfile holds one copy of `ws`, and the client stops when `@procedures/client/node` resolves another one.
|
|
138
168
|
- **The router image** (`image/router`) is a digest-pinned Alpine with conntrack-tools, nftables, iproute2 and Python. `mbx.sh` sets the form of the router in one nftables table, `ctwatch.py` writes the conntrack events, and `resetter.py` sends the resets of the `reset-both` form. Each run records its packages in the manifest.
|
|
139
169
|
|
|
140
170
|
The build does the `cargo test` of the two crates. Thus the record decoder passes its tests before an image exists. Each amount that the native programs print is a quantity in a canonical unit. For example, an RTO is in `jiffy`, a round trip in `us`, and a segment in `B`.
|
|
@@ -300,7 +330,7 @@ The distribution test needs a simulator, which does not exist at this time.
|
|
|
300
330
|
pnpm add @mark1russell7/harness zod
|
|
301
331
|
```
|
|
302
332
|
|
|
303
|
-
A run needs Docker, and privileged containers for the prober and the sidecars. The kernel must have BTF (`/sys/kernel/btf/vmlinux`).
|
|
333
|
+
A run needs Docker, and privileged containers for the prober and the sidecars. The kernel must have BTF (`/sys/kernel/btf/vmlinux`). The schemas of the data of a caller are in the entry `@mark1russell7/harness/schema`.
|
|
304
334
|
|
|
305
335
|
## Tests
|
|
306
336
|
|
|
@@ -310,11 +340,13 @@ The tests also measure a group run from test lines. They find the first accepted
|
|
|
310
340
|
|
|
311
341
|
The tests of a replica set read the log of mongod, and they put its lines on the monotonic clock. They test the views that make a set healthy and the peers of each cut. They measure the stages of a set and the bands of a driver from test lines.
|
|
312
342
|
|
|
343
|
+
The tests of `image.ts` plan the build of each image that the harness ships, and of a build context of a caller. They test the rules of a build, the problems of a source and of a subject, and the schemas.
|
|
344
|
+
|
|
313
345
|
The tests of `application.ts` measure a heartbeat of a program from test lines and records. They find the first ping without an answer, the drop, the waits of the reconnect and the call in flight. They also test a report of the kernel before the drop, a VM that stopped, the heartbeat of a server, and an idle stall.
|
|
314
346
|
|
|
315
347
|
The cells themselves need Docker and privileged containers. CI does not start them. `HARNESS_LAB=1` starts the lab tests on Docker. They start a client image and an agent, and they put a reset and netem at a router. They also fail a group of Valkey 9.1 over with a planned failover.
|
|
316
348
|
|
|
317
|
-
In the lab tests, each HTTP client sends two requests on one connection to a Node server, from a script in the stock Node image. The procedures WebSocket client drops a silent connection on its heartbeat, and it connects again to the procedures server. With the global `WebSocket`, the drop writes a close frame. With the `WebSocket` of ws, the socket leaves ESTABLISHED at once.
|
|
349
|
+
In the lab tests, each HTTP client sends two requests on one connection to a Node server, from a script in the stock Node image. The procedures WebSocket client drops a silent connection on its heartbeat, and it connects again to the procedures server. With the global `WebSocket`, the drop writes a close frame. With the `WebSocket` of ws, the socket leaves ESTABLISHED at once. The subject of that test is a build context that the test gives, as another repository gives its own subject.
|
|
318
350
|
|
|
319
351
|
The lab tests also step a replica set of three members of MongoDB 9.0.4 down, and they write the free memory of the machine. They also reset a write of the MongoDB driver in flight to a set of one member.
|
|
320
352
|
|