162 lines
4.7 KiB
Go
162 lines
4.7 KiB
Go
package inventory
|
|
|
|
import (
|
|
"errors"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
// novox/hq ADR 0100: a node is adopted or converged, and the controller records which.
|
|
|
|
func TestANodeAddedWithoutSayingIsConverged(t *testing.T) {
|
|
inv := fresh(t)
|
|
if _, err := inv.AddNode(t.Context(), "anchor"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
n, err := inv.NodeByName(t.Context(), "anchor")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n.Adopted || !n.AdoptedSince.IsZero() {
|
|
t.Fatalf("a node nobody said anything about reads as adopted: %+v", n)
|
|
}
|
|
}
|
|
|
|
func TestAnAdoptedNodeRoundTripsThroughEveryReading(t *testing.T) {
|
|
inv := fresh(t)
|
|
made, err := inv.AddNodeAs(t.Context(), "anchor", true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !made.Adopted || made.AdoptedSince.IsZero() {
|
|
t.Fatalf("added adopted, got %+v", made)
|
|
}
|
|
byName, err := inv.NodeByName(t.Context(), "anchor")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
all, err := inv.Nodes(t.Context())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !byName.Adopted || len(all) != 1 || !all[0].Adopted {
|
|
t.Fatalf("adoption did not survive reading back: %+v %+v", byName, all)
|
|
}
|
|
|
|
// And through a token: enrolment reads the node from the token it spends.
|
|
issued, err := inv.IssueToken(t.Context(), "anchor", 60e9)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
claimed, err := inv.Claim(t.Context(), issued.Secret, "a-key", false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !claimed.Adopted {
|
|
t.Fatal("the node a token claims lost its mode")
|
|
}
|
|
}
|
|
|
|
func TestTakingIsRefusedOnAConvergedNodeAndForAnUnassignedModule(t *testing.T) {
|
|
inv := fresh(t)
|
|
if err := inv.RegisterModule(t.Context(), manifest("hello-web", nil, nil), Source{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := inv.AddNode(t.Context(), "converged"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.Assign(t.Context(), "converged", "hello-web"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.Take(t.Context(), "converged", "hello-web"); !errors.Is(err, ErrNotAdopted) {
|
|
t.Fatalf("taking on a converged node gave %v", err)
|
|
}
|
|
|
|
if _, err := inv.AddNodeAs(t.Context(), "anchor", true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.Take(t.Context(), "anchor", "hello-web"); !errors.Is(err, ErrNotAssigned) {
|
|
t.Fatalf("taking an unassigned module gave %v", err)
|
|
}
|
|
}
|
|
|
|
func TestATakenModuleOutlivesItsAssignmentAndReturningToAdopted(t *testing.T) {
|
|
inv := fresh(t)
|
|
for _, m := range []string{"hello-web", "postgres"} {
|
|
if err := inv.RegisterModule(t.Context(), manifest(m, nil, nil), Source{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if _, err := inv.AddNodeAs(t.Context(), "anchor", true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, m := range []string{"hello-web", "postgres"} {
|
|
if err := inv.Assign(t.Context(), "anchor", m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := inv.Take(t.Context(), "anchor", "postgres"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.Take(t.Context(), "anchor", "postgres"); err != nil {
|
|
t.Fatalf("taking twice is not an error: %v", err)
|
|
}
|
|
if err := inv.Unassign(t.Context(), "anchor", "postgres"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
taken, err := inv.Taken(t.Context(), "anchor")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(taken, []string{"postgres"}) {
|
|
t.Fatalf("unassigning un-took it: %v", taken)
|
|
}
|
|
|
|
took, err := inv.Converge(t.Context(), "anchor")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(took, []string{"hello-web"}) {
|
|
t.Fatalf("converging took %v; it takes every assigned module not yet taken", took)
|
|
}
|
|
n, _ := inv.NodeByName(t.Context(), "anchor")
|
|
if n.Adopted {
|
|
t.Fatal("converged node still reads adopted")
|
|
}
|
|
|
|
if err := inv.SetAdopted(t.Context(), "anchor", true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
taken, _ = inv.Taken(t.Context(), "anchor")
|
|
if !reflect.DeepEqual(taken, []string{"hello-web", "postgres"}) {
|
|
t.Fatalf("returning to adopted lost what was taken: %v", taken)
|
|
}
|
|
}
|
|
|
|
// Converging clears the whole of a node's account of itself: what it held, what was reachable, the
|
|
// firewall it found and when it said so. Keeping any of it would have `node show` report an
|
|
// adopted machine's account of a converged one.
|
|
func TestConvergingClearsTheAccountTheNodeGave(t *testing.T) {
|
|
inv := fresh(t)
|
|
ctx := t.Context()
|
|
made, err := inv.AddNodeAs(ctx, "anchor", true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.RecordAdoption(ctx, made.ID,
|
|
[]Held{{ID: "notes.conf", Module: "notes", Kind: "file", Target: "/etc/notes.conf"}},
|
|
"ufw", []Reach{{Protocol: "tcp", Address: "0.0.0.0", Port: 22, By: "sshd"}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := inv.Converge(ctx, "anchor"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
said, err := inv.AdoptionOf(ctx, "anchor")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(said.Held) != 0 || len(said.Reachable) != 0 || said.Firewall != "" || !said.At.IsZero() {
|
|
t.Fatalf("converging kept the adopted machine's account: %+v", said)
|
|
}
|
|
}
|