Answer brother_health's wait for unset settings with the SDK's health.Waiting (issue 1510)
This commit is contained in:
@@ -66,7 +66,7 @@ makes it (mode 0755) when it files the first scan.
|
||||
| `scanner.scan` | `source` (`glass`, default, or `feeder`), `duplex` (feeder only) and `apart` (glass only): asks the device process to scan, crop (glass only), keep the PDF in the bucket under its sha256 and say `scan.added`; then fetches it in parts, checks the sha256 and files it in `scan-folder`, in the background, so a scan is filed whether or not the caller still waits. A glass scan is waited for up to 180 s. Several glass pages in one PDF (a page turned over, a stapled document): `document` `start` scans page 1 into a new document and answers its id (nothing is filed yet), `document` with that id scans the next page, and `document` with the id and `finish` true closes it into one multi-page PDF, filed once with one `scan.added` (nothing is scanned). The pages are kept in the bucket under `pending/<id>/` (not in memory, so a restart of the device process loses none); a document that gets no page for 30 minutes is closed and filed so by the tool process (a restart of the tool process forgets that timer; `finish` still works). The pages in the bucket are not deleted, the bucket having no delete. A feeder scan is waited for 25 s (the mesh console gives up after 30 s); if it is not done, the answer is `started: true` with its `id`, and `scanner.scan {id}` reads the outcome (`state` running, done with the event's body and `path`, or failed with `why`). The device process ends a feeder job at 20 s a page (at least 3 min, at most 30 min). A scan whose PDF the tool could not fetch (over 100 MiB) is refused before it is kept or announced. Answers the event's body and `path` |
|
||||
| `scanner.document` | one part of a kept scan (`object`, `part` from 1): `part`, `parts`, `size`, `sha256` (of the whole), `data` (base64). Join the parts and check the sha256 |
|
||||
| `scanner.status` | whether the scanner answers, its state (Idle or Processing) and its document feeder's state; a scanner that does not answer is `answers: false` with why. |
|
||||
| `brother_health` | the module's health, asked by the node-engine every 60 s (ADR 0240): the certificates the printer serves, one for each key type (ECDSA and RSA, told apart by the cipher suites offered under TLS 1.2), read with no login and compared with the pinned `printer-certificate` list. Unhealthy when one is not pinned, `why` naming the new fingerprint, so the pin can be changed before the admin pages are needed. With `printer` or `printer-certificate` not given it answers `state: waits` with `waits: [{config: ...}]` (ADR 0283), healthy and not an error. Each handshake is bounded by 3 s and the whole check by 7 s. A printer that does not answer is healthy, with `why` saying nothing was compared (it may be switched off). The process's liveness is judged by the node-engine without a declaration |
|
||||
| `brother_health` | the module's health, asked by the node-engine every 60 s (ADR 0240): the certificates the printer serves, one for each key type (ECDSA and RSA, told apart by the cipher suites offered under TLS 1.2), read with no login and compared with the pinned `printer-certificate` list. Unhealthy when one is not pinned, `why` naming the new fingerprint, so the pin can be changed before the admin pages are needed. With `printer` or `printer-certificate` not given it answers `state: waits` with `waits` naming each setting (the SDK's health.Waiting, ADR 0283), not an error. Each handshake is bounded by 3 s and the whole check by 7 s. A printer that does not answer is healthy, with `why` saying nothing was compared (it may be switched off). The process's liveness is judged by the node-engine without a declaration |
|
||||
| `scanner.list` | the newest filed scans, newest first (`limit`, default 20). Read-only |
|
||||
|
||||
## The printer seat
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
)
|
||||
|
||||
// The module's health (issue 1510, novox/hq ADR 0240): the node-engine asks the tool brother_health, and the tool
|
||||
@@ -52,30 +54,30 @@ type Served struct {
|
||||
// CertHealth is brother_health's answer: healthy, why when it is not (or what could not be compared), and what the
|
||||
// printer served.
|
||||
type CertHealth struct {
|
||||
Healthy bool `json:"healthy"`
|
||||
State string `json:"state"` // healthy, unhealthy or waits (ADR 0283)
|
||||
Why string `json:"why"`
|
||||
// Waits names the config values the operator has yet to give, when State is waits.
|
||||
Waits []map[string]string `json:"waits,omitempty"`
|
||||
Served []Served `json:"served"`
|
||||
Healthy bool `json:"healthy"`
|
||||
State string `json:"state"` // healthy, unhealthy or waits (ADR 0283)
|
||||
Why string `json:"why"`
|
||||
Served []Served `json:"served"`
|
||||
}
|
||||
|
||||
// CertWaits is the answer while printer or printer-certificate is not given: the module waits for the operator, which
|
||||
// is not a fault (ADR 0283), and names the setting.
|
||||
func CertWaits(s Settings) (CertHealth, bool) {
|
||||
var waits []map[string]string
|
||||
// CertWaits is the answer while printer or printer-certificate is not given: the module waits for the operator,
|
||||
// which is not a fault (ADR 0283), and names each setting as the SDK's health.Wait asks.
|
||||
func CertWaits(s Settings) (health.Answer, bool) {
|
||||
var waits []health.Wait
|
||||
var names []string
|
||||
for _, c := range []struct{ name, value string }{{"printer", s.Printer}, {"printer-certificate", s.PrinterCertificate}} {
|
||||
for _, c := range []struct{ name, value, what string }{
|
||||
{"printer", s.Printer, "the printer's address"},
|
||||
{"printer-certificate", s.PrinterCertificate, "the SHA-256 fingerprints of the certificates the printer serves"},
|
||||
} {
|
||||
if c.value == "" {
|
||||
waits = append(waits, map[string]string{"config": c.name})
|
||||
waits = append(waits, health.Wait{Part: "the printer's certificate check", Setting: c.name, What: c.what})
|
||||
names = append(names, c.name)
|
||||
}
|
||||
}
|
||||
if len(waits) == 0 {
|
||||
return CertHealth{}, false
|
||||
return health.Answer{}, false
|
||||
}
|
||||
return CertHealth{Healthy: true, State: "waits", Waits: waits, Served: []Served{},
|
||||
Why: "waits for the operator to give the config value " + strings.Join(names, " and ")}, true
|
||||
return health.Waiting("waits for the operator to give the config value "+strings.Join(names, " and "), waits...), true
|
||||
}
|
||||
|
||||
// serveAddr is the host with the HTTPS port when it names none.
|
||||
|
||||
@@ -9,11 +9,14 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
)
|
||||
|
||||
// selfSigned is a self-signed certificate over the key.
|
||||
@@ -105,17 +108,24 @@ func TestBrotherHealthIsAToolThatReadsTheSettings(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 1510: with the printer not given the answer is waits, naming the setting (ADR 0283), not an error; and so is
|
||||
// the pin.
|
||||
// 1510: with the printer not given the answer is waits, read by the SDK as the engine reads it: state waits, each
|
||||
// wait naming exactly one setting, a part and what is wanted; not an error and not unhealthy.
|
||||
func TestHealthWaitsForTheSettingsTheOperatorHasYetToGive(t *testing.T) {
|
||||
got, err := tool(t, newRig(t, `{}`, "pw").tl, "brother_health").Run(map[string]any{})
|
||||
h, ok := got.(CertHealth)
|
||||
if err != nil || !ok || !h.Healthy || h.State != "waits" || len(h.Waits) != 2 || h.Waits[0]["config"] != "printer" || h.Waits[1]["config"] != "printer-certificate" {
|
||||
t.Errorf("answer = %+v, %v", got, err)
|
||||
}
|
||||
got, _ = tool(t, newRig(t, `{"printer":"192.0.2.10"}`, "pw").tl, "brother_health").Run(map[string]any{})
|
||||
if h := got.(CertHealth); h.State != "waits" || len(h.Waits) != 1 || h.Waits[0]["config"] != "printer-certificate" {
|
||||
t.Errorf("no pin: %+v", h)
|
||||
for js, want := range map[string][]string{`{}`: {"printer", "printer-certificate"}, `{"printer":"192.0.2.10"}`: {"printer-certificate"}} {
|
||||
got, err := tool(t, newRig(t, js, "pw").tl, "brother_health").Run(map[string]any{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raw, _ := json.Marshal(got)
|
||||
a, err := health.Read(raw)
|
||||
if err != nil || a.State != health.StateWaits || len(a.Waits) != len(want) {
|
||||
t.Fatalf("%s: read as %+v, %v", js, a, err)
|
||||
}
|
||||
for i, w := range a.Waits {
|
||||
if w.Setting != want[i] || w.Check() != nil {
|
||||
t.Errorf("%s: wait %d = %+v (%v)", js, i, w, w.Check())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user