@mark1russell7/harness 0.12.1 → 0.12.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.
@@ -7,7 +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
+ // [--name S] [--clock ns|ms] [--fail-file PATH]
11
11
  //
12
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
13
13
  // negative --listener-keepalive turns the TCP keepalive of the accepted connections off.
@@ -16,10 +16,15 @@
16
16
  // "hold:<ms>" holds its reply for that time, or until the client ends the call. Any other service is
17
17
  // NOT_FOUND. Watch sends SERVING and then holds the stream open until the client ends it.
18
18
  //
19
+ // With --fail-file, the server also serves the service "liveness", the seam of a liveness restart.
20
+ // Check of "liveness" answers NOT_SERVING while the file at PATH exists, and SERVING when it does not.
21
+ // Check of "" still answers SERVING. Without the flag, "liveness" is NOT_FOUND, as each other service.
22
+ //
19
23
  // The lines: listening (the ready line of the target), accept (each connection, with its addresses),
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.
24
+ // hold, held and hold-ended (a held reply), watch and watch-ended, not-serving (each NOT_SERVING
25
+ // reply of "liveness"), and stopping. At SIGTERM or SIGINT, the server writes stopping and then calls
26
+ // GracefulStop: it closes its listener, sends GOAWAY on each connection and waits for each call in
27
+ // flight.
23
28
  //
24
29
  // A kind cell runs the server in a pod. --name gives the name of the pod: each line has it in the
25
30
  // field p, and each reply of Check has it in the header x-pod. --clock ms gives t in milliseconds, the
@@ -61,6 +66,16 @@ var name string
61
66
  // millis tells if t of each line is in milliseconds (--clock ms) and not in nanoseconds.
62
67
  var millis bool
63
68
 
69
+ // failFile is the path of the flag file that fails the service "liveness" (--fail-file), or "" for no
70
+ // such service.
71
+ var failFile string
72
+
73
+ // failing tells if the flag file exists. Then Check of "liveness" answers NOT_SERVING.
74
+ func failing() bool {
75
+ _, err := os.Stat(failFile)
76
+ return err == nil
77
+ }
78
+
64
79
  // stamp gives t of a line: the time on CLOCK_MONOTONIC in the unit of --clock.
65
80
  func stamp() any {
66
81
  if millis {
@@ -100,6 +115,13 @@ func (health) Check(ctx context.Context, request *healthpb.HealthCheckRequest) (
100
115
  if service == "" {
101
116
  return &healthpb.HealthCheckResponse{Status: healthpb.HealthCheckResponse_SERVING}, nil
102
117
  }
118
+ if service == "liveness" && failFile != "" {
119
+ if failing() {
120
+ emit("not-serving", map[string]any{"service": service})
121
+ return &healthpb.HealthCheckResponse{Status: healthpb.HealthCheckResponse_NOT_SERVING}, nil
122
+ }
123
+ return &healthpb.HealthCheckResponse{Status: healthpb.HealthCheckResponse_SERVING}, nil
124
+ }
103
125
  text, ok := strings.CutPrefix(service, "hold:")
104
126
  if !ok {
105
127
  return nil, status.Error(codes.NotFound, "unknown service")
@@ -160,6 +182,7 @@ func main() {
160
182
  keepCount := flag.Int("listener-keepalive-count", 0, "the TCP keepalive count (0: 9)")
161
183
  flag.StringVar(&name, "name", "", "the name of the server in each line (p) and each reply of Check (x-pod)")
162
184
  clock := flag.String("clock", "ns", "the unit of t: ns or ms")
185
+ flag.StringVar(&failFile, "fail-file", "", "the flag file that fails Check of the service liveness (\"\": no service liveness)")
163
186
  flag.Parse()
164
187
  if *clock != "ns" && *clock != "ms" {
165
188
  emit("fail", map[string]any{"why": "--clock ns|ms"})
@@ -202,6 +225,7 @@ func main() {
202
225
  "keepalive_time": keepaliveTime.String(), "keepalive_timeout": keepaliveTimeout.String(), "min_time": minTime.String(),
203
226
  "permit_without_stream": *permitWithoutStream, "listener_keepalive": keepIdle.String(),
204
227
  "listener_keepalive_interval": keepInterval.String(), "listener_keepalive_count": *keepCount,
228
+ "fail_file": failFile,
205
229
  })
206
230
  if err := server.Serve(listener{inner}); err != nil {
207
231
  emit("fail", map[string]any{"why": err.Error()})
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.12.1",
4
+ "version": "0.12.3",
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",