package shares import ( "os" "path/filepath" "strings" "testing" ) const repoisoSMB = `# Managed by repoiso — do not edit [iso-pve] path = /tank/iso/export/pve read only = yes guest ok = yes [global] ; ignored ` func writeDropIns(t *testing.T) (smbDir, nfsDir string) { t.Helper() root := t.TempDir() smbDir, nfsDir = filepath.Join(root, "smb.conf.d"), filepath.Join(root, "exports.d") for _, d := range []string{smbDir, nfsDir} { if err := os.MkdirAll(d, 0o755); err != nil { t.Fatal(err) } } must := func(p, s string) { if err := os.WriteFile(p, []byte(s), 0o644); err != nil { t.Fatal(err) } } must(filepath.Join(smbDir, "repoiso.conf"), repoisoSMB) must(filepath.Join(smbDir, "notes.txt"), "[ignored]\n") must(filepath.Join(nfsDir, "repoiso.exports"), "# Managed by repoiso\n\"/tank/iso/export/pve\" 10.10.0.0/24(ro,root_squash)\n") must(filepath.Join(nfsDir, "nasmon.exports"), "\"/data\" *(rw)\n") return } func TestParseDropIns(t *testing.T) { smb := ParseSMBDropIn(repoisoSMB, "repoiso") if len(smb) != 1 || smb[0].Name != "iso-pve" || smb[0].Path != "/tank/iso/export/pve" || smb[0].Source != "repoiso" { t.Fatalf("smb: %+v", smb) } nfs := ParseExportsFile("# c\n/plain host(ro)\n\"/with space\" 10.0.0.0/8(ro)\n\n", "x") if len(nfs) != 2 || nfs[0].Path != "/plain" || nfs[1].Path != "/with space" || nfs[1].Clients != "10.0.0.0/8(ro)" { t.Fatalf("nfs: %+v", nfs) } } func TestExternalSkipsOwnExports(t *testing.T) { smbDir, nfsDir := writeDropIns(t) m := newManager(newFake()) m.IncludeDir, m.ExportsDir, m.Exports = smbDir, nfsDir, filepath.Join(nfsDir, "nasmon.exports") ext := m.External() if countKind(ext, "smb") != 1 || countKind(ext, "nfs") != 1 { t.Fatalf("external: %+v", ext) } if got := IncludeFiles(smbDir); len(got) != 1 || !strings.HasSuffix(got[0], "repoiso.conf") { t.Fatalf("includes: %v", got) } } func TestRenderSMBIncludes(t *testing.T) { out := RenderSMB(sample(), "ansible", "/etc/samba/smb.conf.d/repoiso.conf") if !strings.HasSuffix(strings.TrimSpace(out), "include = /etc/samba/smb.conf.d/repoiso.conf") { t.Fatalf("include not at the end:\n%s", out) } if strings.Contains(RenderSMB(sample(), "ansible"), "include =") { t.Fatal("include rendered without drop-ins") } } // The bug this guards against: nasmon without own shares disabled smbd and // nfs-server and killed the shares RepoISO publishes for Proxmox. func TestApplyKeepsServicesForOtherServices(t *testing.T) { smbDir, nfsDir := writeDropIns(t) f := newFake() m := newManager(f) m.IncludeDir, m.ExportsDir, m.Exports = smbDir, nfsDir, filepath.Join(nfsDir, "nasmon.exports") if err := m.Apply(Defaults()); err != nil { t.Fatal(err) } if f.has("systemctl disable") { t.Fatalf("services disabled although other services share: %v", f.calls) } if !f.has("systemctl enable --now smbd nmbd") || !f.has("systemctl enable --now nfs-server") || !f.has("exportfs -ra") { t.Fatalf("services not kept running: %v", f.calls) } wi := f.index("sh -c") if !strings.Contains(f.stdin[f.calls[wi]], "include = "+filepath.Join(smbDir, "repoiso.conf")) { t.Errorf("smb.conf lacks the drop-in include") } } func TestApplyDisabledSMBHidesOwnShares(t *testing.T) { smbDir, _ := writeDropIns(t) f := newFake() m := newManager(f) m.IncludeDir = smbDir c := sample() c.SMB.Enabled = false if err := m.Apply(c); err != nil { t.Fatal(err) } if strings.Contains(f.stdin[f.calls[f.index("sh -c")]], "[documents]") { t.Errorf("disabled nasmon shares still rendered while smbd stays up for others") } }