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.
73 lines
2.7 KiB
Go
73 lines
2.7 KiB
Go
package link
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// A health statement is said on the link open now, as the machine's own word on its own subject, and
|
|
// not said — for the caller to say again — while no link is open (novox/hq ADR 0240, to-be 48 §4).
|
|
|
|
// sayingLink is a link that can say health.
|
|
type sayingLink struct {
|
|
*quietLink
|
|
said [][]byte
|
|
}
|
|
|
|
func (l *sayingLink) Health(_ context.Context, node string, body []byte) error {
|
|
l.said = append(l.said, body)
|
|
return nil
|
|
}
|
|
|
|
func TestAHealthStatementIsSaidOnTheLinkOpenNow(t *testing.T) {
|
|
q := &Queue{Membership: Membership{Node: "anchor"}}
|
|
h := Health{Contract: LivenessContract, At: time.Now().UTC(),
|
|
Resources: []ResourceHealth{{Module: "letta", Resource: "letta.server", State: StateUnhealthy, Reason: ReasonRestarting}}}
|
|
if q.SayHealth(t.Context(), h) {
|
|
t.Fatal("said with no link open")
|
|
}
|
|
l := &sayingLink{quietLink: newQuietLink()}
|
|
q.attach(t.Context(), l)
|
|
if !q.SayHealth(t.Context(), h) || len(l.said) != 1 {
|
|
t.Fatalf("not said on the open link: %d", len(l.said))
|
|
}
|
|
var got HealthSaid
|
|
if err := json.Unmarshal(l.said[0], &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got.Node != "anchor" || len(got.Health.Resources) != 1 || got.Health.Resources[0].State != StateUnhealthy {
|
|
t.Fatalf("said %+v", got)
|
|
}
|
|
// A link that cannot say it is not an error: the next report carries the statement.
|
|
q.detach(l)
|
|
q.attach(t.Context(), newQuietLink())
|
|
if q.SayHealth(t.Context(), h) {
|
|
t.Fatal("said on a link that cannot")
|
|
}
|
|
if HealthSubject("anchor") != "mesh.control.anchor.health" {
|
|
t.Fatalf("the subject %s is not inside the host's own grant", HealthSubject("anchor"))
|
|
}
|
|
}
|
|
|
|
// A declared tool check reads the node tools' reply envelope: healthy only when the tool says so (novox/hq
|
|
// ADR 0240, to-be 48 §2), and asked on this machine's own instance (to-be 48 §3).
|
|
func TestAToolsAnswerIsHealthyOnlyWhenItSaysSo(t *testing.T) {
|
|
for reply, want := range map[string]bool{
|
|
`{"result":{"healthy":true},"node":"anchor"}`: true,
|
|
`{"result":{"healthy":false,"why":"the admin refused the secret"}}`: false,
|
|
`{"result":{"status":"fine"}}`: false,
|
|
`{"error":"503 no responders"}`: false,
|
|
`{"result":"yes"}`: false,
|
|
} {
|
|
got, _, err := ReadToolAnswer([]byte(reply))
|
|
if err != nil || got != want {
|
|
t.Errorf("%s read as %v (%v)", reply, got, err)
|
|
}
|
|
}
|
|
if ToolSubject("keycloak", "keycloak_admin_health", "anchor") != "mesh.mod.keycloak.tool.keycloak_admin_health.anchor" {
|
|
t.Errorf("asked on %s", ToolSubject("keycloak", "keycloak_admin_health", "anchor"))
|
|
}
|
|
}
|