The keeper process runs under its own account with a bus account of its own, keeps the six states and emits the nine events of ADR 0507 and ADR 0508; its config (upgrade-order, record-days, round-start-minutes) lands in its config file and restarts it. shapes.go fixes every key's value and event body as the interface the keeper and the verbs share. The weekly timer and its service are dropped, which the node-engine removes.
104 lines
4.0 KiB
Go
104 lines
4.0 KiB
Go
package main
|
|
|
|
// The module's shape (novox/hq to-be 42 Phase 1, ADR 0207, research 027): it holds the
|
|
// node-package-manager seat and declares the package manager's own package; it owns pacman.conf
|
|
// whole — proven by pacman-conf on the rendered file, because a pacman.conf pacman cannot read is a
|
|
// machine that can neither install nor upgrade — and reflector's configuration, with the refresher
|
|
// and the cache cleaner on their timers.
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestItHoldsThePackageManagerSeatAndDeclaresItsPackage(t *testing.T) {
|
|
m := manifest(t)
|
|
if len(m.Claims) != 1 || m.Claims[0]["name"] != "node-package-manager" || m.Claims[0]["scope"] != "node" || m.Claims[0]["serves"] != nil {
|
|
t.Fatalf("claims: %v", m.Claims)
|
|
}
|
|
for id, pkg := range map[string]string{"package": "pacman", "contrib": "pacman-contrib", "reflector": "reflector"} {
|
|
if r := m.resource(t, id); r["package"] != pkg || r["absent"] != nil {
|
|
t.Errorf("%s: %v", id, r)
|
|
}
|
|
}
|
|
for id, unit := range map[string]string{"mirror-refresh": "reflector.timer", "cache-cleaning": "paccache.timer"} {
|
|
r := m.resource(t, id)
|
|
if r["unit"] != unit || r["state"] != "running" || r["boot"] != "enabled" {
|
|
t.Errorf("%s: %v", id, r)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPacmanConfIsWholeTheUnionOfRepositoriesAndTheImprovedOptions(t *testing.T) {
|
|
f := manifest(t).resource(t, "config")
|
|
content := templateText(t, f)
|
|
if f["path"] != "/etc/pacman.conf" || f["into"] != nil || !strings.HasPrefix(content, MeshHeader) {
|
|
t.Fatalf("%v", f)
|
|
}
|
|
var sections []string
|
|
for _, l := range strings.Split(content, "\n") {
|
|
if strings.HasPrefix(l, "[") {
|
|
sections = append(sections, l)
|
|
}
|
|
}
|
|
if strings.Join(sections, " ") != "[options] [core] [extra] [multilib]" {
|
|
t.Fatalf("sections: %v", sections)
|
|
}
|
|
for _, want := range []string{"\nColor\n", "\nCheckSpace\n", "\nVerbosePkgLists\n", "\nParallelDownloads = 5\n", "\nDownloadUser = alpm\n", "\nSigLevel = Required DatabaseOptional\n"} {
|
|
if !strings.Contains(content, want) {
|
|
t.Errorf("missing %q", strings.TrimSpace(want))
|
|
}
|
|
}
|
|
conf, err := exec.LookPath("pacman-conf")
|
|
if err != nil {
|
|
t.Skip("pacman-conf is not installed here; the rendered file is not proven")
|
|
}
|
|
file := filepath.Join(t.TempDir(), "pacman.conf")
|
|
if err := os.WriteFile(file, []byte(content), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
repos, err := exec.Command(conf, "--config", file, "--repo-list").CombinedOutput()
|
|
if err != nil || strings.Join(strings.Fields(string(repos)), " ") != "core extra multilib" {
|
|
t.Fatalf("pacman-conf --repo-list: %v\n%s", err, repos)
|
|
}
|
|
out, err := exec.Command(conf, "--config", file).CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("pacman-conf refuses the file: %v\n%s", err, out)
|
|
}
|
|
c := ParseConf(string(out))
|
|
if c.Options["ParallelDownloads"][0] != "5" || c.Options["DownloadUser"] == nil || c.Options["Color"] == nil || c.Options["VerbosePkgLists"] == nil {
|
|
t.Fatalf("pacman-conf does not read the options as written: %v", c.Options)
|
|
}
|
|
}
|
|
|
|
func TestReflectorWritesTheListPacmanReads(t *testing.T) {
|
|
content := templateText(t, manifest(t).resource(t, "mirrors"))
|
|
opts := map[string]string{}
|
|
for _, l := range strings.Split(content, "\n") {
|
|
if l == "" || strings.HasPrefix(l, "#") {
|
|
continue
|
|
}
|
|
k, v, _ := strings.Cut(l, " ")
|
|
opts[k] = v
|
|
}
|
|
if opts["--save"] != Mirrorlist || opts["--protocol"] != "https" || opts["--latest"] != "20" || opts["--sort"] != "rate" || opts["--country"] == "" {
|
|
t.Fatalf("%v", opts)
|
|
}
|
|
if manifest(t).resource(t, "mirrors")["path"] != ReflectorConf {
|
|
t.Fatal("reflector reads its options from " + ReflectorConf)
|
|
}
|
|
}
|
|
|
|
func TestTheReflectorPackageIsDeclaredBeforeTheFileItShips(t *testing.T) {
|
|
order := map[string]int{}
|
|
for i, r := range manifest(t).Resources {
|
|
order[r["id"].(string)] = i
|
|
}
|
|
if order["reflector"] > order["mirrors"] || order["contrib"] > order["cache-cleaning"] || order["reflector"] > order["mirror-refresh"] {
|
|
t.Fatalf("a package's file and timer come after the package: %v", order)
|
|
}
|
|
}
|