156 lines
5.2 KiB
Go
156 lines
5.2 KiB
Go
package apply
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/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) {
|
|
args = oldShape(name, args)
|
|
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
|
|
|
|
func TestACapabilityReachesTheRuntimeAndTheSpec(t *testing.T) {
|
|
var ran []string
|
|
run := func(_ context.Context, name string, args ...string) (string, error) {
|
|
args = oldShape(name, args)
|
|
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) {
|
|
args = oldShape(name, args)
|
|
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)
|
|
}
|
|
}
|