Liveness alone could not see a web application whose port was open and whose program ran while every request hung for eleven hours (issue 145). A resource now carries the `health` its module declared: the engine makes http and tcp looks itself from the machine to the endpoint's published port, reads a unit's readiness from the show it already makes, hands an exec command or the image's own check to the runtime as the container's check with the declared timing and reads its state from the inspect it already makes, and asks a module's tool on its own node tools. Starting until the check passed, unhealthy once its looks after the grace fail the declared number of times; never more looks than the measured budget; nothing restarted. The statement says contract 2, which tells the controller this engine may be sent the field.
50 lines
2.5 KiB
Go
50 lines
2.5 KiB
Go
package declaration
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// A resource's health is held to its kind and bounds on arrival, as the controller holds it near the
|
|
// author (novox/hq ADR 0240 rule 2): an engine that took a check it could not judge would say a module ready
|
|
// that nothing looked at.
|
|
func TestAHealthIsHeldToItsKindAndBounds(t *testing.T) {
|
|
pinned := "postgres@sha256:" + strings.Repeat("a", 64)
|
|
parse := func(resource string) error {
|
|
_, err := Parse([]byte(`{"declaration":1,"resources":[` + resource + `]}`))
|
|
return err
|
|
}
|
|
container := func(health string, more string) string {
|
|
return `{"id":"m.db","type":"container","name":"db","image":"` + pinned + `"` + more + `,"health":` + health + `}`
|
|
}
|
|
timing := `"interval":"30s","timeout":"5s","looks":3,"grace":"1m0s"`
|
|
for _, ok := range []string{
|
|
container(`{"kind":"http","endpoint":"web","port":31001,"path":"/",`+timing+`}`, ""),
|
|
container(`{"kind":"exec","command":"pg_isready",`+timing+`}`, ""),
|
|
container(`{"kind":"runtime",`+timing+`}`, ""),
|
|
`{"id":"m.d","type":"service","unit":"d.service","state":"running","health":{"kind":"unit",` + timing + `}}`,
|
|
} {
|
|
if err := parse(ok); err != nil {
|
|
t.Errorf("refused %s: %v", ok, err)
|
|
}
|
|
}
|
|
for _, c := range []struct{ resource, says string }{
|
|
{container(`{"kind":"http","path":"/",`+timing+`}`, ""), "needs the port"},
|
|
{container(`{"kind":"exec",`+timing+`}`, ""), "needs a command"},
|
|
{container(`{"kind":"unit",`+timing+`}`, ""), "a service's or a process's own"},
|
|
{container(`{"kind":"ping",`+timing+`}`, ""), `of kind "ping"`},
|
|
{container(`{"kind":"tcp","port":1,"interval":"5s","timeout":"1s","looks":3,"grace":"0s"}`, ""), "no more often than every 10s"},
|
|
{container(`{"kind":"tcp","port":1,"interval":"30s","timeout":"30s","looks":3,"grace":"0s"}`, ""), "less than its interval"},
|
|
{container(`{"kind":"tcp","port":1,"interval":"30s","timeout":"5s","looks":1,"grace":"0s"}`, ""), "at least 2 failing looks"},
|
|
{container(`{"kind":"tcp","port":1,"interval":"60s","timeout":"5s","looks":3,"grace":"3m"}`, ""), "at most 5m0s"},
|
|
{container(`{"kind":"runtime",`+timing+`}`, `,"run-once":true`), "judged on what stays up"},
|
|
{`{"id":"m.d","type":"service","unit":"d.service","state":"running","health":{"kind":"runtime",` + timing + `}}`, "a container image's own"},
|
|
{container(`{"kind":"tcp","port":1,`+timing+`,"retries":2}`, ""), "retries"},
|
|
} {
|
|
err := parse(c.resource)
|
|
if err == nil || !strings.Contains(err.Error(), c.says) {
|
|
t.Errorf("%s: want a refusal saying %q, got %v", c.resource, c.says, err)
|
|
}
|
|
}
|
|
}
|