package inventory import ( "os" "path/filepath" "testing" ) func TestLoadAndFindNodeBranches(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "inventory.yaml") if err := os.WriteFile(path, []byte(` classes: - name: ${CLASS} arch: arm64 os: armbian image: file:///tmp/base.img nodes: - name: node1 class: ${CLASS} hostname: node1 k3s_role: agent `), 0o644); err != nil { t.Fatal(err) } t.Setenv("CLASS", "rpi4") inv, err := Load(path) if err != nil { t.Fatalf("Load: %v", err) } if _, _, err := inv.FindNode("missing"); err == nil { t.Fatal("expected missing node error") } if _, cls, err := inv.FindNode("node1"); err != nil || cls == nil { t.Fatalf("expected class lookup for node1, got class=%#v err=%v", cls, err) } } func TestLoadReportsReadFailures(t *testing.T) { if _, err := Load(filepath.Join(t.TempDir(), "missing.yaml")); err == nil { t.Fatal("expected missing inventory file to fail") } } func TestLoadExpandsOptionalCollections(t *testing.T) { t.Setenv("CLASS_NAME", "rpi5") t.Setenv("CLASS_LABEL_VALUE", "worker") t.Setenv("CLASS_TAINT", "special=true:NoSchedule") t.Setenv("NODE_LABEL_VALUE", "maintenance") t.Setenv("NODE_TAINT", "flash=true:NoSchedule") t.Setenv("SSH_KEY", "ssh-ed25519 AAA") t.Setenv("EMPTY_VALUE", "") t.Setenv("DISK_MOUNT", "/var/lib/longhorn") t.Setenv("DISK_UUID", "uuid-1") t.Setenv("DISK_FS", "ext4") t.Setenv("USB_BIND", "/var/lib/rancher") path := filepath.Join(t.TempDir(), "inventory.yaml") if err := os.WriteFile(path, []byte(` classes: - name: ${CLASS_NAME} arch: arm64 os: armbian image: file:///tmp/rpi5.img default_labels: role: ${CLASS_LABEL_VALUE} default_taints: - ${CLASS_TAINT} nodes: - name: titan-18 class: ${CLASS_NAME} hostname: titan-18 ip: 192.168.22.18 k3s_role: agent labels: purpose: ${NODE_LABEL_VALUE} taints: - ${NODE_TAINT} ssh_authorized_keys: - " ${SSH_KEY} " - ${EMPTY_VALUE} longhorn_disks: - mountpoint: ${DISK_MOUNT} uuid: ${DISK_UUID} fs: ${DISK_FS} usb_scratch: mountpoint: /mnt/usb bind_targets: - ${USB_BIND} - ${EMPTY_VALUE} `), 0o644); err != nil { t.Fatal(err) } inv, err := Load(path) if err != nil { t.Fatalf("Load: %v", err) } if inv.Classes[0].DefaultLabels["role"] != "worker" || inv.Classes[0].DefaultTaints[0] != "special=true:NoSchedule" { t.Fatalf("class defaults not expanded: %#v", inv.Classes[0]) } node := inv.Nodes[0] if node.Labels["purpose"] != "maintenance" || node.Taints[0] != "flash=true:NoSchedule" { t.Fatalf("node labels/taints not expanded: %#v", node) } if len(node.SSHAuthorized) != 1 || node.SSHAuthorized[0] != "ssh-ed25519 AAA" { t.Fatalf("ssh keys not trimmed/filtered: %#v", node.SSHAuthorized) } if len(node.LonghornDisks) != 1 || node.LonghornDisks[0].Mountpoint != "/var/lib/longhorn" || node.LonghornDisks[0].UUID != "uuid-1" || node.LonghornDisks[0].FS != "ext4" { t.Fatalf("longhorn disk not expanded: %#v", node.LonghornDisks) } if node.USBScratch == nil || len(node.USBScratch.BindTargets) != 1 || node.USBScratch.BindTargets[0] != "/var/lib/rancher" { t.Fatalf("usb bind targets not filtered: %#v", node.USBScratch) } }