The legacy reader required every path into a chain of refusals to come from a built-in whose policy accepts. The home server's ban chain hangs off the container runtime's user chain, whose forward policy the runtime set to DROP, so the machine reported the mesh's own intrusion prevention as a rule set the mesh did not write. A chain is a ban when every refusal names its sources and the chain accepts nothing — the rule the nftables side already used. A chain that accepts anything is still not a ban. Fixture captured from the machine. The citations for the uninstalled front end move to ADR 0180, which another session's renumber had left pointing at an unrelated record.
215 lines
7.3 KiB
Go
215 lines
7.3 KiB
Go
package apply
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-host/internal/declaration"
|
|
"github.com/novox/mesh-host/internal/store"
|
|
)
|
|
|
|
// A taken container keeps a found network by a per-machine setting (novox/hq ADR 0163, rule 4):
|
|
// joined once it runs, part of its spec, and refused when it cannot be joined.
|
|
func TestAContainerJoinsTheNetworksItKeeps(t *testing.T) {
|
|
var ran []string
|
|
connectFails := false
|
|
run := func(_ context.Context, name string, args ...string) (string, error) {
|
|
if name != "docker" {
|
|
return "", errors.New("not installed")
|
|
}
|
|
ran = append(ran, strings.Join(args, " "))
|
|
switch args[0] {
|
|
case "info":
|
|
return "29.0.0\n", nil
|
|
case "container":
|
|
if len(ran) > 2 {
|
|
return "true\t" + specOfLast, nil
|
|
}
|
|
return "false\t\n", errors.New("no such container")
|
|
case "run":
|
|
return "deadbeef\n", nil
|
|
case "network":
|
|
if connectFails {
|
|
return "", errors.New("network predecessor_default not found")
|
|
}
|
|
}
|
|
return "", nil
|
|
}
|
|
d := parseTrusted(t, `{"declaration":1,"resources":[
|
|
{"id":"app","type":"container","name":"app","image":"`+pinned+`",
|
|
"networks":["predecessor_default"]}
|
|
]}`)
|
|
specOfLast = containerSpec(d.Resources[0].(*declaration.Container), inputs{})
|
|
alone := *d.Resources[0].(*declaration.Container)
|
|
alone.Networks = nil
|
|
if specOfLast == containerSpec(&alone, inputs{}) {
|
|
t.Fatal("the kept network is not part of the container's spec: kept or let go, the container would be left alone")
|
|
}
|
|
report, _, err := Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, run, nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
joined := false
|
|
for i, line := range ran {
|
|
if line == "network connect predecessor_default app" {
|
|
joined = true
|
|
if ran[i-1] != "container inspect --format {{.State.Running}}\t{{index .Config.Labels \""+specLabel+"\"}} app" &&
|
|
!strings.HasPrefix(ran[i-1], "container inspect") {
|
|
t.Errorf("joined before the container was read back as running: %v", ran)
|
|
}
|
|
}
|
|
}
|
|
if !joined || report.Outcomes[0].Action != "created" {
|
|
t.Fatalf("the container did not join the kept network: %v\n%+v", ran, report.Outcomes)
|
|
}
|
|
|
|
connectFails, ran = true, nil
|
|
if _, _, err := Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, run, nil, nil); err == nil ||
|
|
!strings.Contains(err.Error(), "could not join the kept network predecessor_default") {
|
|
t.Fatalf("a network that cannot be joined was passed over: %v", err)
|
|
}
|
|
}
|
|
|
|
var specOfLast string
|
|
|
|
// A module the mesh left out of a declaration is not a module the mesh removed (novox/hq ADR 0163,
|
|
// rule 6): what the host wrote for it stays, recorded and said; what it holds for it stays held.
|
|
// A module simply absent is removed as it always was.
|
|
func TestALeftOutModuleIsNeitherRemovedNorForgotten(t *testing.T) {
|
|
var removed []string
|
|
gone := map[string]bool{}
|
|
run := func(_ context.Context, name string, args ...string) (string, error) {
|
|
if name != "docker" {
|
|
return "", nil
|
|
}
|
|
switch args[0] {
|
|
case "info":
|
|
return "29.0.0\n", nil
|
|
case "rm":
|
|
removed = append(removed, args[len(args)-1])
|
|
gone[args[len(args)-1]] = true
|
|
case "container":
|
|
if gone[args[len(args)-1]] {
|
|
return "", errors.New("no such container")
|
|
}
|
|
return "true\tspec", nil
|
|
}
|
|
return "", nil
|
|
}
|
|
known := store.State{
|
|
Resources: []store.Applied{
|
|
{ID: "web.server", Type: "container", Target: "web", Origin: store.OriginDeclared},
|
|
{ID: "old.server", Type: "container", Target: "old", Origin: store.OriginDeclared},
|
|
},
|
|
Held: []store.Held{{ID: "web.page", Module: "web", Kind: "file", Target: "/srv/web/index.html"}},
|
|
}
|
|
d := parse(t, `{"declaration":1,"left_out":["web"],"resources":[
|
|
{"id":"notes.conf","type":"file","path":"`+t.TempDir()+`/notes.conf","content":"x"}
|
|
]}`)
|
|
report, state, err := Apply(context.Background(), archHost(t), d, known, store.OriginDeclared, run, nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(removed) != 1 || removed[0] != "old" {
|
|
t.Fatalf("removed %v; only the module that is absent goes", removed)
|
|
}
|
|
if _, kept := state.At("container", "web"); !kept {
|
|
t.Fatal("the left-out module's record was forgotten")
|
|
}
|
|
if _, held := state.HeldAt("web.page"); !held {
|
|
t.Fatal("the left-out module's hold was released")
|
|
}
|
|
said := false
|
|
for _, o := range report.Outcomes {
|
|
if o.ID == "web.server" && o.Action == "unchanged" && strings.Contains(o.Detail, "web was left out of this declaration by the mesh") {
|
|
said = true
|
|
}
|
|
if o.ID == "web.server" && o.Action != "unchanged" {
|
|
t.Errorf("the left-out module's container was %s", o.Action)
|
|
}
|
|
}
|
|
if !said {
|
|
t.Fatalf("keeping the left-out module's container was not said: %+v", report.Outcomes)
|
|
}
|
|
}
|
|
|
|
// A container's capabilities reach the runtime and are part of its spec (novox/hq ADR 0170).
|
|
func TestACapabilityReachesTheRuntimeAndTheSpec(t *testing.T) {
|
|
var ran []string
|
|
run := func(_ context.Context, name string, args ...string) (string, error) {
|
|
if name != "docker" {
|
|
return "", errors.New("not installed")
|
|
}
|
|
switch args[0] {
|
|
case "info":
|
|
return "29.0.0\n", nil
|
|
case "container":
|
|
return "false\t\n", errors.New("no such container")
|
|
case "run":
|
|
ran = args
|
|
return "deadbeef\n", nil
|
|
}
|
|
return "", nil
|
|
}
|
|
d := parseTrusted(t, `{"declaration":1,"resources":[
|
|
{"id":"fw","type":"container","name":"fw","image":"`+pinned+`","network":"host","capabilities":["NET_ADMIN"]}
|
|
]}`)
|
|
_, _, _ = Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, run, nil, nil)
|
|
granted := false
|
|
for i, a := range ran {
|
|
if a == "--cap-add" && i+1 < len(ran) && ran[i+1] == "NET_ADMIN" {
|
|
granted = true
|
|
}
|
|
}
|
|
if !granted {
|
|
t.Fatalf("the capability was not granted: %v", ran)
|
|
}
|
|
with := d.Resources[0].(*declaration.Container)
|
|
without := *with
|
|
without.Capabilities = nil
|
|
if containerSpec(with, inputs{}) == containerSpec(&without, inputs{}) {
|
|
t.Fatal("a capability is not part of the container's spec")
|
|
}
|
|
}
|
|
|
|
// A package may be declared absent (novox/hq ADR 0180): removed when it is installed, read back,
|
|
// left alone when it is not.
|
|
func TestAPackageDeclaredAbsentIsRemovedWhenPresentAndLeftWhenNot(t *testing.T) {
|
|
installed := true
|
|
var ran []string
|
|
run := func(_ context.Context, name string, args ...string) (string, error) {
|
|
ran = append(ran, name+" "+strings.Join(args, " "))
|
|
if name != "pacman" {
|
|
return "", nil
|
|
}
|
|
switch args[0] {
|
|
case "-Q":
|
|
if args[1] == "pacman" || installed {
|
|
return args[1] + " 1.0\n", nil
|
|
}
|
|
return "", errors.New("package not found")
|
|
case "-R":
|
|
installed = false
|
|
}
|
|
return "", nil
|
|
}
|
|
d := parseTrusted(t, `{"declaration":1,"resources":[{"id":"front-end","type":"package","package":"ufw","absent":true}]}`)
|
|
report, _, err := Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, run, nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if report.Outcomes[0].Action != "removed" || !strings.Contains(strings.Join(ran, "\n"), "pacman -R --noconfirm ufw") {
|
|
t.Fatalf("an installed package declared absent was not removed: %+v\n%v", report.Outcomes[0], ran)
|
|
}
|
|
ran = nil
|
|
report, _, err = Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, run, nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if report.Outcomes[0].Action != "unchanged" || strings.Contains(strings.Join(ran, "\n"), "-R") {
|
|
t.Fatalf("a package already absent was touched: %+v\n%v", report.Outcomes[0], ran)
|
|
}
|
|
}
|