117 lines
5.0 KiB
Go
117 lines
5.0 KiB
Go
package preflight
|
|
|
|
import (
|
|
"io/fs"
|
|
"strings"
|
|
"testing"
|
|
"testing/fstest"
|
|
|
|
"server-deploy/internal/inspect"
|
|
)
|
|
|
|
func hostFiles() fstest.MapFS {
|
|
return fstest.MapFS{
|
|
"etc/os-release": {Data: []byte("ID=ubuntu\nVERSION_ID=\"26.04\"\nVERSION_CODENAME=resolute\n")},
|
|
"var/lib/dpkg/status": {Data: []byte("Package: base-files\nStatus: install ok installed\nArchitecture: amd64\nVersion: 1.0\n")},
|
|
"var/lib/dpkg/updates": {Mode: fs.ModeDir | 0700},
|
|
}
|
|
}
|
|
|
|
func TestPackageStateBlocksFreshInstallCandidates(t *testing.T) {
|
|
for _, state := range []string{"install ok installed", "deinstall ok config-files", "install reinstreq half-installed"} {
|
|
files := hostFiles()
|
|
files["var/lib/dpkg/status"].Data = []byte("Package: containerd\nStatus: " + state + "\nArchitecture: amd64\nVersion: 1.2.3\n")
|
|
p := Plan(Probe(baseline(), files, 0, Disk{State: "observed", AvailableBytes: 20 << 30}))
|
|
if len(p.ProposedChanges) != 0 || !contains(p.Blockers, "existing_packages_require_review") {
|
|
t.Errorf("existing package state %s overlooked", state)
|
|
}
|
|
}
|
|
files := hostFiles()
|
|
delete(files, "var/lib/dpkg/status")
|
|
if p := Plan(Probe(baseline(), files, 0, Disk{State: "observed", AvailableBytes: 20 << 30})); len(p.ProposedChanges) != 0 {
|
|
t.Fatal("unknown inventory treated as empty")
|
|
}
|
|
}
|
|
func baseline() inspect.Report {
|
|
return inspect.Report{ProtocolVersion: 1, OS: "linux", Architecture: "amd64", SystemdRuntime: "present", DockerClient: "missing"}
|
|
}
|
|
func TestHostFactsAndNonExecutableProposal(t *testing.T) {
|
|
r := Probe(baseline(), hostFiles(), 0, Disk{State: "observed", AvailableBytes: 20 << 30})
|
|
if r.Distribution.ID != "ubuntu" || r.Distribution.Version != "26.04" || r.Distribution.State != "observed" || r.Privilege != "root" {
|
|
t.Fatalf("incorrect host facts: %+v", r)
|
|
}
|
|
p := Plan(r)
|
|
if p.Executable || len(p.ProposedChanges) == 0 || !contains(p.Blockers, "package_versions_unresolved") || !contains(p.Blockers, "host_identity_unverified") {
|
|
t.Fatalf("unsafe proposal: %+v", p)
|
|
}
|
|
}
|
|
func TestExistingResourcesNeverProposeFreshInstall(t *testing.T) {
|
|
for _, path := range []string{"var/lib/docker", "var/lib/containerd", "etc/docker", "var/lib/server-deploy", "etc/apt/sources.list.d/docker.sources", "etc/apt/sources.list.d/docker.list"} {
|
|
files := hostFiles()
|
|
files[path] = &fstest.MapFile{Mode: fs.ModeDir | 0700}
|
|
p := Plan(Probe(baseline(), files, 0, Disk{State: "observed", AvailableBytes: 20 << 30}))
|
|
if len(p.ProposedChanges) != 0 || !contains(p.Blockers, "existing_resources_require_review") {
|
|
t.Errorf("fresh install proposed over %s", path)
|
|
}
|
|
}
|
|
runtime := baseline()
|
|
runtime.DockerClient = "present"
|
|
if p := Plan(Probe(runtime, hostFiles(), 0, Disk{State: "observed", AvailableBytes: 20 << 30})); len(p.ProposedChanges) != 0 {
|
|
t.Fatal("existing Docker overlooked")
|
|
}
|
|
}
|
|
func TestUnknownAndInsufficientHostFailsClosed(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
runtime inspect.Report
|
|
uid int
|
|
disk Disk
|
|
blocker string
|
|
}{
|
|
{"nonroot", baseline(), 1000, Disk{State: "observed", AvailableBytes: 20 << 30}, "root_required"},
|
|
{"disk unknown", baseline(), 0, Disk{State: "unknown"}, "disk_unverified"},
|
|
{"disk low", baseline(), 0, Disk{State: "observed", AvailableBytes: 1}, "disk_below_bootstrap_floor"},
|
|
{"unsupported", inspect.Report{OS: "windows", Architecture: "amd64"}, 0, Disk{}, "unsupported_platform"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
p := Plan(Probe(tc.runtime, hostFiles(), tc.uid, tc.disk))
|
|
if !contains(p.Blockers, tc.blocker) || p.Executable || len(p.ProposedChanges) != 0 {
|
|
t.Fatalf("unsafe proposal: %+v", p)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
func TestOSReleaseRejectsAmbiguityAndNeverEvaluatesShell(t *testing.T) {
|
|
for _, content := range []string{"ID=ubuntu\nID=debian\nVERSION_ID=26.04\nVERSION_CODENAME=resolute", "ID=$(touch secret)\nVERSION_ID=26.04\nVERSION_CODENAME=resolute", "ID=ubuntu\nVERSION_ID=\"26.04\nVERSION_CODENAME=resolute", strings.Repeat("#", 65537)} {
|
|
files := hostFiles()
|
|
files["etc/os-release"].Data = []byte(content)
|
|
p := Plan(Probe(baseline(), files, 0, Disk{State: "observed", AvailableBytes: 20 << 30}))
|
|
if len(p.ProposedChanges) != 0 || !contains(p.Blockers, "distribution_unverified") {
|
|
t.Fatal("ambiguous distribution accepted")
|
|
}
|
|
}
|
|
files := hostFiles()
|
|
files["etc/os-release"].Data = []byte("ID=ubuntu\nVERSION_ID=26.04\nVERSION_CODENAME=noble\n")
|
|
if p := Plan(Probe(baseline(), files, 0, Disk{State: "observed", AvailableBytes: 20 << 30})); !contains(p.Blockers, "unsupported_distribution") {
|
|
t.Fatal("mismatched suite accepted")
|
|
}
|
|
}
|
|
|
|
type deniedFS struct{}
|
|
|
|
func (deniedFS) Open(string) (fs.File, error) { return nil, fs.ErrPermission }
|
|
func TestAccessFailuresAreNotAbsence(t *testing.T) {
|
|
r := Probe(baseline(), deniedFS{}, 0, Disk{State: "observed", AvailableBytes: 20 << 30})
|
|
if r.Distribution.State != "unknown" || r.Resources[0].State != "unknown" || len(Plan(r).ProposedChanges) != 0 {
|
|
t.Fatal("permission failure treated as clean host")
|
|
}
|
|
}
|
|
func contains(values []string, want string) bool {
|
|
for _, v := range values {
|
|
if v == want {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|