//go:build integration package main import ( "reflect" "strings" "testing" "time" "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/compose" "github.com/novox/mesh-controller/internal/link" ) // 1382: the adapter's Input names, for every node, what the inventory answers for it, each compared with the read named // beside the field in go/compose/input.go (novox/hq ADR 0453 clause 3). func TestTheAdaptersInputNamesWhatTheInventoryAnswersForEachNode(t *testing.T) { open, _ := aMeshWithSecrets(t) ctx := t.Context() inv := open.inventory if err := inv.SetPublicDomain(ctx, "anchor", "example.test"); err != nil { t.Fatal(err) } if _, err := inv.PortFor(ctx, "laptop", "files", 8080, false); err != nil { t.Fatal(err) } in, err := readComposeInput(ctx, open) if err != nil { t.Fatal(err) } shelf, err := inv.Catalogue(ctx) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(in.Catalogue, shelf) { t.Error("the Input's Catalogue is not the shelf the inventory holds") } nodes, err := inv.Nodes(ctx) if err != nil { t.Fatal(err) } if len(in.Nodes) != len(nodes) { t.Fatalf("the Input names %d node(s), the inventory has %d", len(in.Nodes), len(nodes)) } places, err := inv.Overlays(ctx) if err != nil { t.Fatal(err) } for _, record := range nodes { n, ok := nodeOf(in.Input, record.Name) if !ok { t.Fatalf("%s is not in the Input", record.Name) } for _, p := range places { if p.Name == record.Name { if want := (compose.Place{Key: p.Key, Endpoint: p.Endpoint, Site: p.Site, Hub: p.Hub, Address: p.Address}); n.Place != want { t.Errorf("%s: Place is %+v, the inventory's %+v", record.Name, n.Place, want) } } } assigned, _ := inv.Assigned(ctx, record.Name) if !reflect.DeepEqual(n.Assigned, assigned) { t.Errorf("%s: Assigned is %v, the inventory's %v", record.Name, n.Assigned, assigned) } caps, _ := inv.ProfileOf(ctx, record.Name) if !reflect.DeepEqual(n.Capabilities, caps) { t.Errorf("%s: Capabilities are %v, the inventory's %v", record.Name, n.Capabilities, caps) } domain, _ := inv.PublicDomainOf(ctx, record.Name) if n.PublicDomain != domain { t.Errorf("%s: PublicDomain is %q, the inventory's %q", record.Name, n.PublicDomain, domain) } if n.Account != record.Account || n.AccountHome != record.AccountHome || n.AgentAccount != record.AgentAccount || n.AgentAccountHome != record.AgentAccountHome || n.Adopted != record.Adopted { t.Errorf("%s: the accounts or the mode differ from the inventory's: %+v", record.Name, n) } pins, _ := inv.PinsFor(ctx, record.Name) if len(pins) != len(n.Pins) || (len(pins) > 0 && !reflect.DeepEqual(n.Pins, pins)) { t.Errorf("%s: Pins are %v, the inventory's %v", record.Name, n.Pins, pins) } bound, _ := inv.BindingsFor(ctx, record.Name) if len(bound) != len(n.Bound) || (len(bound) > 0 && !reflect.DeepEqual(n.Bound, bound)) { t.Errorf("%s: Bound is %v, the inventory's %v", record.Name, n.Bound, bound) } key, _ := inv.SealingKeyOf(ctx, record.Name) if n.SealingKey != key { t.Errorf("%s: SealingKey is %q, the inventory's %q", record.Name, n.SealingKey, key) } ports, _ := inv.PortsFor(ctx, record.Name) want := map[string]map[int]int{} for _, a := range ports { if want[a.Module] == nil { want[a.Module] = map[int]int{} } want[a.Module][a.Wanted] = a.Machine } if len(want) != len(n.Ports) || (len(want) > 0 && !reflect.DeepEqual(n.Ports, want)) { t.Errorf("%s: Ports are %v, the inventory's %v", record.Name, n.Ports, want) } // The settings of every module it resolves to, layer for layer. resolved, err := compose.Resolve(in.Input, record.Name) if err != nil { continue } for _, m := range resolved.Plan.Modules { layers, err := inv.SettingsFor(ctx, record.Name, m.Module) if err != nil { t.Fatal(err) } if len(layers) != len(n.Settings[m.Module]) || (len(layers) > 0 && !reflect.DeepEqual(n.Settings[m.Module], layers)) { t.Errorf("%s: the settings of %s are %v, the inventory's %v", record.Name, m.Module, n.Settings[m.Module], layers) } } } if domain := nodeDomain(in.Input, "anchor"); domain != "example.test" { t.Errorf("anchor's PublicDomain is %q", domain) } if got := nodePorts(in.Input, "laptop"); got["files"][8080] == 0 { t.Errorf("laptop's port for files:8080 is not in the Input: %v", got) } } func nodeDomain(in compose.Input, name string) string { n, _ := nodeOf(in, name) return n.PublicDomain } func nodePorts(in compose.Input, name string) map[string]map[int]int { n, _ := nodeOf(in, name) return n.Ports } // 1382/AC2: a node keeping a recorded module at the build it runs has that module in Kept and KeptBuilds, and the Input's // Catalogue is the current shelf, not the kept manifest (R4a review round 1, finding 1). func TestANodeKeepingARecordedBuildHasItInKeptAndTheCatalogueStaysCurrent(t *testing.T) { open := aMesh(t) ctx := t.Context() inv := open.inventory start := time.Now().Add(-time.Hour) image := "registry.invalid:5000/postgres/server@sha256:" + strings.Repeat("a", 64) for _, b := range []link.BuildResult{ aContainerBuild(t, "postgres", "c1111111", catalogue.PolicyRecord, start, map[string][2]string{"server": {image, ""}}), } { if _, _, err := takeIn(ctx, inv, asTheOperator(t, inv, b)); err != nil { t.Fatal(err) } } if _, err := inv.Assign(ctx, "anchor", "postgres"); err != nil { t.Fatal(err) } if err := inv.RecordSent(ctx, nodeID(t, open, "anchor"), "d-anchor", map[string]string{"postgres": "c1111111"}); err != nil { t.Fatal(err) } if _, _, err := takeIn(ctx, inv, aContainerBuild(t, "postgres", "c2222222", catalogue.PolicyRecord, start.Add(time.Minute), map[string][2]string{"server": {image, "runtime"}})); err != nil { t.Fatal(err) } // A send that is not a person's push keeps the build the machine runs. in, err := readComposeInput(keepingRecorded(ctx), open) if err != nil { t.Fatal(err) } anchor, _ := nodeOf(in.Input, "anchor") if anchor.KeptBuilds["postgres"] != "c1111111" { t.Fatalf("anchor keeps %v, want postgres at c1111111", anchor.KeptBuilds) } kept, found := anchor.Kept["postgres"] if !found { t.Fatalf("the kept manifest of postgres is not in anchor's Kept: %v", anchor.Kept) } current, err := inv.Catalogue(ctx) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(in.Catalogue["postgres"], current["postgres"]) { t.Error("the Input's Catalogue is not the current shelf") } if reflect.DeepEqual(kept, current["postgres"]) { t.Error("the kept manifest is the current one: nothing is kept") } if laptop, _ := nodeOf(in.Input, "laptop"); len(laptop.Kept) != 0 || len(laptop.KeptBuilds) != 0 { t.Errorf("laptop keeps what only anchor runs: %v", laptop.KeptBuilds) } // A person's push keeps nothing. pushed, err := readComposeInput(ctx, open) if err != nil { t.Fatal(err) } if a, _ := nodeOf(pushed.Input, "anchor"); len(a.Kept) != 0 || len(a.KeptBuilds) != 0 { t.Errorf("a person's push keeps %v", a.KeptBuilds) } } // 1382/AC3: the seats on record, the tunnels, the adopted tunnel, its carried peers and the nodes joining equal the // inventory's answers; the range and the suffix are read from the environment once, here. func TestTheAdaptersInputCarriesTheNetworkAsTheInventoryHoldsIt(t *testing.T) { open, _ := aMeshWithSecrets(t) ctx := t.Context() inv := open.inventory t.Setenv(OverlayCIDRVar, "10.99.0.0/16") t.Setenv("MESH_INTERNAL_SUFFIX", "mesh.test") in, err := readComposeInput(ctx, open) if err != nil { t.Fatal(err) } holdings, err := inv.Holdings(ctx) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(in.Holdings, holdings) { t.Errorf("Holdings are %v, the inventory's %v", in.Holdings, holdings) } tunnels, err := inv.Tunnels(ctx) if err != nil { t.Fatal(err) } if len(tunnels) != len(in.Tunnels) { t.Errorf("Tunnels are %v, the inventory's %v", in.Tunnels, tunnels) } if _, hub, _, err := inv.AdoptedTunnel(ctx); err != nil { t.Fatal(err) } else if (hub == "") != (in.Adopted == nil) { t.Errorf("Adopted is %v, the inventory's hub is %q", in.Adopted, hub) } carried, err := inv.CarriedPeers(ctx) if err != nil { t.Fatal(err) } if len(carried) != len(in.CarriedPeers) { t.Errorf("CarriedPeers are %v, the inventory's %v", in.CarriedPeers, carried) } joining, err := inv.NodesWithALiveToken(ctx) if err != nil { t.Fatal(err) } if len(joining) != len(in.Joining) { t.Errorf("Joining is %v, the inventory's %v", in.Joining, joining) } if in.OverlayCIDR != "10.99.0.0/16" || in.InternalSuffix != "mesh.test" { t.Errorf("the range and the suffix are %q and %q", in.OverlayCIDR, in.InternalSuffix) } // Read once: what the environment says afterwards moves nothing already read. t.Setenv(OverlayCIDRVar, "10.1.0.0/16") if in.OverlayCIDR != "10.99.0.0/16" { t.Error("the range moved with the environment") } }