Files
spinifex/cmd/installer/firstboot/firstboot_test.go
T
Julian Sommer 1d55404a73 revert: host DNS setup on resolved/resolvconf hosts (#580)
This reverts commit f9c9f0b38d.

#580 switched setup.sh to read the northstar :53 listener and zones from
northstar.toml, so the resolved stage now defers at install time (before
formation) and must be re-run afterward. Only firstboot re-runs it; the
manual setup.sh + spx admin init install and CI never do, so those hosts
get no host DNS and cluster-zone names (ELB/EKS) fail to resolve from the node.

Reverting restores the pre-#580 behaviour: systemd-resolved is configured
at install time from the WAN IP, with no post-formation re-run needed. This
reintroduces the original gap where resolvconf hosts get no host DNS. The
proper fix configures host DNS from spx admin init/join for both resolver
managers (mulga-x30yv).
2026-07-23 11:23:21 +10:00

149 lines
3.9 KiB
Go

package firstboot
import (
"os"
"path/filepath"
"strings"
"testing"
)
func makeRootDirs(t *testing.T, root string) {
t.Helper()
for _, d := range []string{
"usr/local/bin",
"etc/systemd/system",
"etc/systemd/system/multi-user.target.wants",
} {
if err := os.MkdirAll(filepath.Join(root, d), 0o755); err != nil {
t.Fatalf("mkdir %s: %v", d, err)
}
}
}
func TestWriteScriptNoCallbackWhenEmpty(t *testing.T) {
root := t.TempDir()
makeRootDirs(t, root)
cfg := Config{Hostname: "test-node", ClusterRole: "init"}
if err := Write(root, cfg); err != nil {
t.Fatalf("Write: %v", err)
}
script, err := os.ReadFile(filepath.Join(root, "usr/local/bin/spinifex-firstboot.sh"))
if err != nil {
t.Fatalf("read script: %v", err)
}
if strings.Contains(string(script), "curl") {
t.Error("script should not contain curl when InstallCallback is empty")
}
}
func TestWriteScriptEmbedsCurlWhenCallbackSet(t *testing.T) {
root := t.TempDir()
makeRootDirs(t, root)
const callbackURL = "http://192.168.1.12/boot/done?mac=aa:bb:cc:dd:ee:ff"
cfg := Config{
Hostname: "test-node",
ClusterRole: "init",
InstallCallback: callbackURL,
}
if err := Write(root, cfg); err != nil {
t.Fatalf("Write: %v", err)
}
script, err := os.ReadFile(filepath.Join(root, "usr/local/bin/spinifex-firstboot.sh"))
if err != nil {
t.Fatalf("read script: %v", err)
}
content := string(script)
if !strings.Contains(content, "curl") {
t.Error("script missing curl command")
}
if !strings.Contains(content, callbackURL) {
t.Errorf("script missing callback URL %q", callbackURL)
}
}
func TestWriteScriptRunsOVNWhenFormationOwned(t *testing.T) {
root := t.TempDir()
makeRootDirs(t, root)
cfg := Config{Hostname: "test-node", ClusterRole: "init"}
if err := Write(root, cfg); err != nil {
t.Fatalf("Write: %v", err)
}
content := readScript(t, root)
if !strings.Contains(content, "setup-ovn.sh --management") {
t.Error("script should run setup-ovn --management when firstboot owns formation")
}
if !strings.Contains(content, "systemctl start ovn-central") {
t.Error("script should pre-start ovn-central when firstboot owns formation")
}
}
func TestWriteScriptDefersOVNWhenSkipFormation(t *testing.T) {
root := t.TempDir()
makeRootDirs(t, root)
cfg := Config{Hostname: "test-node", ClusterRole: "init", SkipFormation: true}
if err := Write(root, cfg); err != nil {
t.Fatalf("Write: %v", err)
}
content := readScript(t, root)
if strings.Contains(content, "setup-ovn.sh --management") {
t.Error("script must not run setup-ovn --management when a controller owns OVN")
}
if strings.Contains(content, "systemctl start ovn-central") {
t.Error("script must not pre-start ovn-central when a controller owns OVN")
}
if !strings.Contains(content, "setup-ovn deferred") {
t.Error("script should note setup-ovn is deferred under SkipFormation")
}
}
func readScript(t *testing.T, root string) string {
t.Helper()
b, err := os.ReadFile(filepath.Join(root, "usr/local/bin/spinifex-firstboot.sh"))
if err != nil {
t.Fatalf("read script: %v", err)
}
return string(b)
}
func TestWriteScriptCallbackAfterDoneMarker(t *testing.T) {
root := t.TempDir()
makeRootDirs(t, root)
const callbackURL = "http://192.168.1.12/boot/done?mac=aa:bb:cc:dd:ee:ff"
cfg := Config{
Hostname: "node1",
ClusterRole: "init",
InstallCallback: callbackURL,
}
if err := Write(root, cfg); err != nil {
t.Fatalf("Write: %v", err)
}
script, err := os.ReadFile(filepath.Join(root, "usr/local/bin/spinifex-firstboot.sh"))
if err != nil {
t.Fatalf("read script: %v", err)
}
content := string(script)
doneIdx := strings.Index(content, "touch \"$DONE_MARKER\"")
curlIdx := strings.Index(content, "curl")
if doneIdx < 0 {
t.Fatal("done marker not found in script")
}
if curlIdx < 0 {
t.Fatal("curl not found in script")
}
if curlIdx < doneIdx {
t.Error("curl must appear after done marker write")
}
}