@mark1russell7/harness 0.11.25 → 0.11.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -202,6 +202,8 @@ From 0.11.20, the harness builds each image with the CLI of Docker (`docker buil
202
202
  - `go mod tidy` gets the modules of grpc-go at its tag, and the go command checks each module against the checksum database of Go. The build log prints the go.sum. The build does `gofmt` and `go vet` first.
203
203
  - The server is at the defaults of grpc-go and of Go, and each flag sets one value over them. The flags set the keepalive, the policy for PINGs, the age of a connection and the TCP keepalive of the listener.
204
204
  - It serves `grpc.health.v1.Health`. A check of the service `hold:<ms>` holds its reply for that time, and Watch holds a stream open. It writes JSON lines on the monotonic clock (`Target.lines`).
205
+ - At SIGTERM, it writes `stopping`, and GracefulStop closes its listener and sends GOAWAY on each connection.
206
+ - From 0.11.26, `--name` gives a name to each line (`p`) and to each reply of Check (the header `x-pod`), and `--clock ms` gives `t` in milliseconds. A kind cell of `@mark1russell7/kind` uses both.
205
207
  - **The etcd image** (`image/etcd`) is etcd v3.6.15 on a digest-pinned Alpine. The binaries come unchanged from the image of etcd, pinned by digest. That image has no shell, and a cell holds the server as the child of a shell. A target takes it as the image of a subject: `{ kind: "shipped", name: "etcd" }`.
206
208
  - **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:
207
209
  - 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.
@@ -7,6 +7,7 @@
7
7
  // [--permit-without-stream] [--max-connection-idle D] [--max-connection-age D]
8
8
  // [--max-connection-age-grace D] [--listener-keepalive D]
9
9
  // [--listener-keepalive-interval D] [--listener-keepalive-count N]
10
+ // [--name S] [--clock ns|ms]
10
11
  //
11
12
  // Each D is a duration of Go, for example 20s. A setting at 0 is the default of grpc-go or of Go. A
12
13
  // negative --listener-keepalive turns the TCP keepalive of the accepted connections off.
@@ -16,7 +17,13 @@
16
17
  // NOT_FOUND. Watch sends SERVING and then holds the stream open until the client ends it.
17
18
  //
18
19
  // The lines: listening (the ready line of the target), accept (each connection, with its addresses),
19
- // hold, held and hold-ended (a held reply), watch and watch-ended, and stopping.
20
+ // hold, held and hold-ended (a held reply), watch and watch-ended, and stopping. At SIGTERM or SIGINT,
21
+ // the server writes stopping and then calls GracefulStop: it closes its listener, sends GOAWAY on each
22
+ // connection and waits for each call in flight.
23
+ //
24
+ // A kind cell runs the server in a pod. --name gives the name of the pod: each line has it in the
25
+ // field p, and each reply of Check has it in the header x-pod. --clock ms gives t in milliseconds, the
26
+ // unit of the lines of a kind cell.
20
27
  package main
21
28
 
22
29
  import (
@@ -37,6 +44,7 @@ import (
37
44
  "google.golang.org/grpc/codes"
38
45
  healthpb "google.golang.org/grpc/health/grpc_health_v1"
39
46
  "google.golang.org/grpc/keepalive"
47
+ "google.golang.org/grpc/metadata"
40
48
  "google.golang.org/grpc/status"
41
49
  )
42
50
 
@@ -47,11 +55,28 @@ func now() int64 {
47
55
  return ts.Nano()
48
56
  }
49
57
 
58
+ // name is the name of the server in its lines and its replies (--name), or "" for none.
59
+ var name string
60
+
61
+ // millis tells if t of each line is in milliseconds (--clock ms) and not in nanoseconds.
62
+ var millis bool
63
+
64
+ // stamp gives t of a line: the time on CLOCK_MONOTONIC in the unit of --clock.
65
+ func stamp() any {
66
+ if millis {
67
+ return float64(now()) / 1e6
68
+ }
69
+ return now()
70
+ }
71
+
50
72
  // emit writes one JSON line with its event and its time.
51
73
  func emit(ev string, fields map[string]any) {
52
- line := map[string]any{"ev": ev, "t": now()}
53
- for name, value := range fields {
54
- line[name] = value
74
+ line := map[string]any{"ev": ev, "t": stamp()}
75
+ if name != "" {
76
+ line["p"] = name
77
+ }
78
+ for field, value := range fields {
79
+ line[field] = value
55
80
  }
56
81
  out, err := json.Marshal(line)
57
82
  if err != nil {
@@ -67,6 +92,11 @@ type health struct {
67
92
 
68
93
  func (health) Check(ctx context.Context, request *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) {
69
94
  service := request.GetService()
95
+ if name != "" {
96
+ if err := grpc.SetHeader(ctx, metadata.Pairs("x-pod", name)); err != nil {
97
+ return nil, err
98
+ }
99
+ }
70
100
  if service == "" {
71
101
  return &healthpb.HealthCheckResponse{Status: healthpb.HealthCheckResponse_SERVING}, nil
72
102
  }
@@ -128,7 +158,14 @@ func main() {
128
158
  keepIdle := flag.Duration("listener-keepalive", 0, "the TCP keepalive idle of each accepted connection (0: 15 s, negative: off)")
129
159
  keepInterval := flag.Duration("listener-keepalive-interval", 0, "the TCP keepalive interval (0: 15 s)")
130
160
  keepCount := flag.Int("listener-keepalive-count", 0, "the TCP keepalive count (0: 9)")
161
+ flag.StringVar(&name, "name", "", "the name of the server in each line (p) and each reply of Check (x-pod)")
162
+ clock := flag.String("clock", "ns", "the unit of t: ns or ms")
131
163
  flag.Parse()
164
+ if *clock != "ns" && *clock != "ms" {
165
+ emit("fail", map[string]any{"why": "--clock ns|ms"})
166
+ os.Exit(1)
167
+ }
168
+ millis = *clock == "ms"
132
169
 
133
170
  // at the defaults this is net.Listen: Go turns the keepalive on with its own defaults (15 s, 15 s, 9)
134
171
  config := net.ListenConfig{KeepAlive: *keepIdle}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@mark1russell7/harness",
4
- "version": "0.11.25",
4
+ "version": "0.11.26",
5
5
  "description": "A fault-injection harness for connection timers: a cell runs a client against a stock target in Docker, through a topology of routers and proxies, a replicated group under Sentinels or a replica set, injects a fault, measures the kernel's own records and judges a prediction. HTTP clients of Go, Node and undici race the keep-alive of a Node server, and the MongoDB driver meets the faults of a replica set. The WebSocket client of @procedures/client drops a silent connection on its own heartbeat, and the harness times that detection and the reconnect from the program's lines and the kernel's stamps.",
6
6
  "keywords": [
7
7
  "fault-injection",