diff --git a/pkg/inventory/coverage_more_test.go b/pkg/inventory/coverage_more_test.go index 08dcdf6..78d0412 100644 --- a/pkg/inventory/coverage_more_test.go +++ b/pkg/inventory/coverage_more_test.go @@ -35,3 +35,81 @@ nodes: 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) + } +} diff --git a/pkg/secrets/coverage_more_test.go b/pkg/secrets/coverage_more_test.go index eeae6e5..d8fc806 100644 --- a/pkg/secrets/coverage_more_test.go +++ b/pkg/secrets/coverage_more_test.go @@ -50,3 +50,66 @@ func TestClientLoginAndFetchBranches(t *testing.T) { t.Fatal("httpClient returned nil") } } + +func TestClientLoginAdditionalErrorBranches(t *testing.T) { + cli := &Client{Addr: "://bad-vault", RoleID: "role", SecretID: "secret"} + if err := cli.LoginIfNeeded(context.Background()); err == nil { + t.Fatal("expected invalid login URL to fail") + } + + cli = &Client{Addr: "http://127.0.0.1:1", RoleID: "role", SecretID: "secret"} + if err := cli.LoginIfNeeded(context.Background()); err == nil { + t.Fatal("expected login connection failure") + } + + badJSON := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && strings.HasSuffix(r.URL.Path, "/auth/approle/login") { + _, _ = w.Write([]byte(`{bad-json`)) + return + } + http.NotFound(w, r) + })) + defer badJSON.Close() + cli = &Client{Addr: badJSON.URL, RoleID: "role", SecretID: "secret", Client: badJSON.Client()} + if err := cli.LoginIfNeeded(context.Background()); err == nil { + t.Fatal("expected malformed login response to fail") + } + + emptyToken := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && strings.HasSuffix(r.URL.Path, "/auth/approle/login") { + _ = json.NewEncoder(w).Encode(map[string]any{"auth": map[string]any{}}) + return + } + http.NotFound(w, r) + })) + defer emptyToken.Close() + cli = &Client{Addr: emptyToken.URL, RoleID: "role", SecretID: "secret", Client: emptyToken.Client()} + if err := cli.LoginIfNeeded(context.Background()); err == nil { + t.Fatal("expected empty login token to fail") + } +} + +func TestClientFetchAdditionalErrorBranches(t *testing.T) { + cli := &Client{Addr: "://bad-vault", Token: "token"} + if _, err := cli.FetchNode(context.Background(), "node1"); err == nil { + t.Fatal("expected invalid fetch URL to fail") + } + + cli = &Client{Addr: "http://127.0.0.1:1", Token: "token"} + if _, err := cli.FetchNode(context.Background(), "node1"); err == nil { + t.Fatal("expected fetch connection failure") + } + + badJSON := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/secret/data/nodes/node1") { + _, _ = w.Write([]byte(`{bad-json`)) + return + } + http.NotFound(w, r) + })) + defer badJSON.Close() + cli = &Client{Addr: badJSON.URL, Token: "token", Client: badJSON.Client()} + if _, err := cli.FetchNode(context.Background(), "node1"); err == nil { + t.Fatal("expected malformed fetch response to fail") + } +}