177 lines
5.4 KiB
Go
177 lines
5.4 KiB
Go
package plan
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"metis/pkg/config"
|
|
"metis/pkg/inventory"
|
|
"metis/pkg/secrets"
|
|
)
|
|
|
|
func TestBuildFilesProducesK3sConfig(t *testing.T) {
|
|
cfg := &config.NodeConfig{
|
|
Hostname: "n1",
|
|
IP: "10.0.0.10",
|
|
SSHUser: "pi",
|
|
SSHKeys: []string{"ssh-rsa AAA"},
|
|
K3s: config.K3sConfig{
|
|
Role: "agent",
|
|
URL: "https://server:6443",
|
|
Token: "secret",
|
|
Version: "v1.31.5+k3s1",
|
|
},
|
|
Fstab: []config.FstabEntry{
|
|
{
|
|
UUID: "disk-uuid",
|
|
Mountpoint: "/mnt/astreae",
|
|
FS: "ext4",
|
|
Options: "defaults,nofail",
|
|
},
|
|
{
|
|
UUID: "usb-uuid",
|
|
Mountpoint: "/mnt/scratch",
|
|
FS: "ext4",
|
|
Options: "defaults,nofail",
|
|
},
|
|
{
|
|
Source: "/mnt/scratch",
|
|
Mountpoint: "/var/lib/rancher",
|
|
FS: "none",
|
|
Options: "bind,nofail",
|
|
},
|
|
},
|
|
Labels: map[string]string{"role": "worker", "zone": "a", "node-role.kubernetes.io/worker": "true"},
|
|
Taints: []string{"gpu=true:NoSchedule"},
|
|
}
|
|
files, err := buildFiles(cfg, nil)
|
|
if err != nil {
|
|
t.Fatalf("buildFiles: %v", err)
|
|
}
|
|
pathMap := map[string]string{}
|
|
for _, f := range files {
|
|
pathMap[f.Path] = string(f.Content)
|
|
}
|
|
k3s, ok := pathMap["etc/rancher/k3s/config.yaml"]
|
|
if !ok {
|
|
t.Fatalf("missing k3s config")
|
|
}
|
|
if !strings.Contains(k3s, "server: https://server:6443") || !strings.Contains(k3s, "node-name: n1") {
|
|
t.Fatalf("unexpected k3s config: %s", k3s)
|
|
}
|
|
if strings.Contains(k3s, "write-kubeconfig-mode") {
|
|
t.Fatalf("agent config should not include write-kubeconfig-mode: %s", k3s)
|
|
}
|
|
if strings.Contains(k3s, "node-role.kubernetes.io/worker") {
|
|
t.Fatalf("agent config should skip reserved node-role label: %s", k3s)
|
|
}
|
|
hostFile, ok := pathMap["etc/hostname"]
|
|
if !ok || strings.TrimSpace(hostFile) != "n1" {
|
|
t.Fatalf("hostname file missing/incorrect: %q", hostFile)
|
|
}
|
|
auth, ok := pathMap["home/pi/.ssh/authorized_keys"]
|
|
if !ok || !strings.Contains(auth, "ssh-rsa AAA") {
|
|
t.Fatalf("authorized_keys missing/incorrect: %s", auth)
|
|
}
|
|
firstboot, ok := pathMap["etc/metis/firstboot.env"]
|
|
if !ok || !strings.Contains(firstboot, "METIS_K3S_VERSION='v1.31.5+k3s1'") {
|
|
t.Fatalf("firstboot env missing/incorrect: %s", firstboot)
|
|
}
|
|
network, ok := pathMap["etc/NetworkManager/system-connections/end0-static.nmconnection"]
|
|
if !ok || !strings.Contains(network, "address1=10.0.0.10/24,10.0.0.1") {
|
|
t.Fatalf("networkmanager config missing/incorrect: %s", network)
|
|
}
|
|
networkEth0, ok := pathMap["etc/NetworkManager/system-connections/eth0-static.nmconnection"]
|
|
if !ok || !strings.Contains(networkEth0, "interface-name=eth0") {
|
|
t.Fatalf("eth0 networkmanager config missing/incorrect: %s", networkEth0)
|
|
}
|
|
networkd, ok := pathMap["etc/systemd/network/10-end0-static.network"]
|
|
if !ok || !strings.Contains(networkd, "Name=end0 eth0") || !strings.Contains(networkd, "Address=10.0.0.10/24") || !strings.Contains(networkd, "Gateway=10.0.0.1") {
|
|
t.Fatalf("systemd-networkd config missing/incorrect: %s", networkd)
|
|
}
|
|
fstab, ok := pathMap["etc/metis/fstab.append"]
|
|
if !ok || !strings.Contains(fstab, "UUID=disk-uuid /mnt/astreae ext4 defaults,nofail 0 0") || !strings.Contains(fstab, "UUID=usb-uuid /mnt/scratch ext4 defaults,nofail 0 0") || !strings.Contains(fstab, "/mnt/scratch /var/lib/rancher none bind,nofail 0 0") {
|
|
t.Fatalf("fstab append missing/incorrect: %s", fstab)
|
|
}
|
|
}
|
|
|
|
func TestOverlayFiles(t *testing.T) {
|
|
dir := t.TempDir()
|
|
bootDir := filepath.Join(dir, "boot")
|
|
rootDir := filepath.Join(dir, "root")
|
|
if err := os.MkdirAll(filepath.Join(bootDir, "over"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.MkdirAll(filepath.Join(rootDir, "etc"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(bootDir, "over", "cmdline.txt"), []byte("console=tty1"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(rootDir, "etc", "issue"), []byte("hello"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
class := &inventory.NodeClass{
|
|
BootOverlay: bootDir,
|
|
RootOverlay: rootDir,
|
|
}
|
|
files, err := collectOverlays(class)
|
|
if err != nil {
|
|
t.Fatalf("collectOverlays: %v", err)
|
|
}
|
|
if len(files) != 2 {
|
|
t.Fatalf("expected 2 files, got %d", len(files))
|
|
}
|
|
}
|
|
|
|
func TestSecretsWrite(t *testing.T) {
|
|
cfg := &config.NodeConfig{
|
|
Hostname: "n1",
|
|
IP: "10.0.0.1",
|
|
}
|
|
sec := &secrets.NodeSecrets{K3sToken: "tok", SSHPassword: "pw", Extra: map[string]string{"foo": "bar"}}
|
|
files, err := buildFiles(cfg, sec)
|
|
if err != nil {
|
|
t.Fatalf("buildFiles: %v", err)
|
|
}
|
|
found := false
|
|
for _, f := range files {
|
|
if f.Path == "etc/metis/secrets.json" && f.RootFS {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("secrets file not written")
|
|
}
|
|
}
|
|
|
|
func TestBuildFilesAddsHecateSudoersForAtlas(t *testing.T) {
|
|
cfg := &config.NodeConfig{
|
|
Hostname: "n1",
|
|
IP: "10.0.0.10",
|
|
SSHUser: "atlas",
|
|
SSHKeys: []string{"ssh-ed25519 AAA test"},
|
|
K3s: config.K3sConfig{
|
|
Role: "agent",
|
|
},
|
|
}
|
|
files, err := buildFiles(cfg, nil)
|
|
if err != nil {
|
|
t.Fatalf("buildFiles: %v", err)
|
|
}
|
|
pathMap := map[string]string{}
|
|
for _, f := range files {
|
|
pathMap[f.Path] = string(f.Content)
|
|
}
|
|
sudoers, ok := pathMap["etc/sudoers.d/90-hecate-atlas"]
|
|
if !ok || !strings.Contains(sudoers, "atlas ALL=(ALL) NOPASSWD: /usr/bin/systemctl") {
|
|
t.Fatalf("sudoers file missing/incorrect: %s", sudoers)
|
|
}
|
|
backup, ok := pathMap["etc/metis/sudoers-hecate"]
|
|
if !ok || backup != sudoers {
|
|
t.Fatalf("metis sudoers backup missing/incorrect: %s", backup)
|
|
}
|
|
}
|