The gate judged a module by what the mesh saw from outside, so a container that crash-looped after it applied passed it. Each machine's node-engine now states the health of every long-running resource it runs; the controller keeps the newest statement per machine, raises module.<module>.<machine>.unhealthy on the second statement in a row, clears it on the first that does not say it, and the gate passes a module only when every long-running resource of it is stated healthy since the send. An engine that states nothing is judged as before.
38 lines
1.2 KiB
Go
38 lines
1.2 KiB
Go
package link
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// A health event is kept as the machine its subject names says it, whatever its body claims, and taken
|
|
// (novox/hq ADR 0240): core NATS, nothing to hold.
|
|
|
|
type keptHealths struct{ by map[string]Health }
|
|
|
|
func (k *keptHealths) Stated(_ context.Context, node string, h Health) error {
|
|
k.by[node] = h
|
|
return nil
|
|
}
|
|
|
|
func TestAHealthEventIsKeptAsTheMachineInItsSubject(t *testing.T) {
|
|
s, in := serving()
|
|
kept := &keptHealths{by: map[string]Health{}}
|
|
s.Hears(kept)
|
|
to := &settled{}
|
|
m := in.sends(t, to, KindHealth, HealthSaid{Node: "laptop", Health: Health{Contract: LivenessContract, At: time.Now(),
|
|
Resources: []ResourceHealth{{Module: "letta", Resource: "letta.server", State: StateUnhealthy}}}}).(*fakeControl)
|
|
m.subject = HealthSubject("anchor")
|
|
s.act(t.Context(), m)
|
|
if !to.acked {
|
|
t.Fatal("a health event was not taken")
|
|
}
|
|
if _, lied := kept.by["laptop"]; lied || len(kept.by["anchor"].Resources) != 1 {
|
|
t.Fatalf("kept %+v: the machine is the subject's, never the body's", kept.by)
|
|
}
|
|
if kind, ok := kindOfSubject(HealthSubject("anchor")); !ok || kind != KindHealth {
|
|
t.Fatalf("%s is not read as a health statement", HealthSubject("anchor"))
|
|
}
|
|
}
|