package proxmox import ( "bytes" "errors" "io" "strings" "testing" "github.com/kdomanski/iso9660" "gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider" "gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider/nocloud" ) // seedFiles opens a built seed and returns its label and files by name. func seedFiles(t *testing.T, iso *bytes.Buffer) (string, map[string]string) { t.Helper() img, err := iso9660.OpenImage(bytes.NewReader(iso.Bytes())) if err != nil { t.Fatalf("OpenImage: %v", err) } label, err := img.Label() if err != nil { t.Fatalf("Label: %v", err) } root, err := img.RootDir() if err != nil { t.Fatalf("RootDir: %v", err) } children, err := root.GetChildren() if err != nil { t.Fatalf("GetChildren: %v", err) } files := make(map[string]string, len(children)) for _, c := range children { data, err := io.ReadAll(c.Reader()) if err != nil { t.Fatalf("reading %s: %v", c.Name(), err) } files[c.Name()] = string(data) } return label, files } func TestSeedNames_table(t *testing.T) { t.Parallel() if got, want := seedISOName("proxy-abc"), "proxy-abc-seed.iso"; got != want { t.Errorf("seedISOName = %q, want %q", got, want) } if got, want := seedVolID("proxy-seeds", "proxy-abc"), "proxy-seeds:iso/proxy-abc-seed.iso"; got != want { t.Errorf("seedVolID = %q, want %q", got, want) } } func TestBuildSeed_table(t *testing.T) { t.Parallel() off := false userData := "#cloud-config\r\npackages: [squid]\r\nwrite_files:\n - content: \"caf\xc3\xa9\"\n" configured := "#cloud-config\nntp:\n enabled: true\n" tests := []struct { name string vendorData string install *bool want func(t *testing.T, got string) }{ {name: "agent script by default", want: func(t *testing.T, got string) { if got != nocloud.GuestAgentScript { t.Errorf("vendor-data is not the guest agent script: %q", got) } }}, {name: "configured joined with the agent script", vendorData: configured, want: func(t *testing.T, got string) { if !strings.HasPrefix(got, "MIME-Version: 1.0\r\n") || !strings.Contains(got, configured) || !strings.Contains(got, nocloud.GuestAgentScript) { t.Errorf("vendor-data is not a multipart of both documents: %q", got) } }}, {name: "agent off passes configured through", vendorData: configured, install: &off, want: func(t *testing.T, got string) { if got != configured { t.Errorf("vendor-data = %q, want the configured document", got) } }}, {name: "agent off with nothing configured is empty", install: &off, want: func(t *testing.T, got string) { if got != "" { t.Errorf("vendor-data = %q, want empty", got) } }}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { t.Parallel() cfg := testConfig() cfg.VendorData = tc.vendorData cfg.InstallGuestAgent = tc.install p := newProvider("proxmox-home", cfg, nil) iso, err := p.buildSeed(provider.CreateRequest{Name: "proxy-abc", CloudInit: userData}) if err != nil { t.Fatalf("buildSeed error = %v", err) } label, files := seedFiles(t, iso) if label != nocloud.SeedLabel { t.Errorf("label = %q, want %q", label, nocloud.SeedLabel) } if files["user-data"] != userData { t.Errorf("user-data changed:\n got %q\nwant %q", files["user-data"], userData) } if want := "instance-id: proxy-abc\nlocal-hostname: proxy-abc\n"; files["meta-data"] != want { t.Errorf("meta-data = %q, want %q", files["meta-data"], want) } tc.want(t, files["vendor-data"]) }) } } func TestBuildSeed_emptyNameIsPermanentWithoutPayload(t *testing.T) { t.Parallel() const sentinel = "SENTINEL-cloud-init-payload" p := newProvider("proxmox-home", testConfig(), nil) _, err := p.buildSeed(provider.CreateRequest{CloudInit: sentinel}) if !errors.Is(err, provider.ErrPermanent) { t.Fatalf("buildSeed error = %v, want ErrPermanent", err) } if strings.Contains(err.Error(), sentinel) { t.Errorf("error carries the cloud-init payload: %v", err) } } // Not parallel: it points TMPDIR at a path that cannot exist for the // process. func TestBuildSeed_stagingFailureIsTransient(t *testing.T) { t.Setenv("TMPDIR", t.TempDir()+"/missing") p := newProvider("proxmox-home", testConfig(), nil) _, err := p.buildSeed(provider.CreateRequest{Name: "proxy-abc", CloudInit: "#cloud-config\n"}) if err == nil { t.Fatal("buildSeed with no staging dir succeeded") } if got := classify("create", err); got != provider.ErrTransient { t.Errorf("classify(buildSeed error) = %v, want ErrTransient: %v", got, err) } }