Shares of other services and RepoISO integration
build / build (push) Successful in 1m43s

- include SMB drop-ins from /etc/samba/smb.conf.d into the generated smb.conf
- keep smbd/nfs-server running while other services (/etc/exports.d, drop-ins) share,
  instead of disabling them when nasmon has no shares of its own
- list those shares read-only on /shares
- settings: RepoISO URL; poll its /api/integration/status, dashboard card, menu link,
  alerts for portal down, stale worker, failed jobs, unmounted publications

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 15:06:17 +03:00
co-authored by Claude Opus 5.5
parent 661a862f8d
commit 30dbb00dd1
22 changed files with 656 additions and 19 deletions
+87
View File
@@ -0,0 +1,87 @@
package web
import (
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"nasmon/internal/collect"
"nasmon/internal/shares"
)
func TestBrowserURL(t *testing.T) {
cases := []struct{ cfg, host, want string }{
{"", "nas:8090", ""},
{"http://127.0.0.1", "10.10.0.129:8090", "http://10.10.0.129"},
{"http://localhost:8080/", "nas.lan:8090", "http://nas.lan:8080/"},
{"https://repo.example.com", "10.0.0.1:8090", "https://repo.example.com"},
{"http://127.0.0.1", "[fe80::1]:8090", "http://[fe80::1]"},
}
for _, c := range cases {
if got := browserURL(c.cfg, c.host); got != c.want {
t.Errorf("browserURL(%q, %q) = %q, want %q", c.cfg, c.host, got, c.want)
}
}
}
func TestRepoISOIntegrationUI(t *testing.T) {
srv, sc, _ := newTestServer(t)
// settings: save the URL, menu link appears
rec := postForm(srv, "/settings", url.Values{"smtp_port": {"587"}, "smtp_security": {"starttls"},
"smart_interval": {"30"}, "repoiso_url": {"http://127.0.0.1/"}})
if rec.Code != 200 || srv.cfg.Get().Integrations.RepoISOURL != "http://127.0.0.1" {
t.Fatalf("settings not saved: %d %q", rec.Code, srv.cfg.Get().Integrations.RepoISOURL)
}
rec = postForm(srv, "/settings", url.Values{"smtp_port": {"587"}, "smtp_security": {"starttls"},
"smart_interval": {"30"}, "repoiso_url": {"ftp://x"}})
if !strings.Contains(rec.Body.String(), "RepoISO URL must start") {
t.Errorf("bad URL accepted")
}
st := sc.Status()
st.RepoISO = &collect.RepoISO{URL: "http://127.0.0.1", Reachable: true, WorkerAlive: true, Version: "0.1.3",
Images: 61, ImagesBytes: 350 << 30, Publications: 2, JobsFailed24h: 1, Exports: 1}
sc.SetStatus(st)
rec = httptest.NewRecorder()
req := httptest.NewRequest("GET", "/", nil)
req.Host = "10.10.0.129:8090"
srv.ServeHTTP(rec, req)
body := rec.Body.String()
for _, want := range []string{`href="http://10.10.0.129"`, "RepoISO ↗", `id="repoiso"`, "v0.1.3", "1 failed in 24 h"} {
if !strings.Contains(body, want) {
t.Errorf("dashboard lacks %q", want)
}
}
st.RepoISO = &collect.RepoISO{URL: "http://127.0.0.1", Error: "connection refused"}
sc.SetStatus(st)
rec = httptest.NewRecorder()
srv.ServeHTTP(rec, httptest.NewRequest("GET", "/", nil))
if !strings.Contains(rec.Body.String(), "not responding") {
t.Errorf("unreachable RepoISO not shown")
}
}
func TestSharesPageListsExternal(t *testing.T) {
srv, _, _ := newTestServer(t)
f := &shareRunner{}
withShareRunner(srv, f)
dir := t.TempDir()
smbDir, nfsDir := filepath.Join(dir, "smb.conf.d"), filepath.Join(dir, "exports.d")
_ = os.MkdirAll(smbDir, 0o755)
_ = os.MkdirAll(nfsDir, 0o755)
_ = os.WriteFile(filepath.Join(smbDir, "repoiso.conf"), []byte("[iso-pve]\n path = /tank/iso/export/pve\n"), 0o644)
_ = os.WriteFile(filepath.Join(nfsDir, "repoiso.exports"), []byte("\"/tank/iso/export/pve\" 10.10.0.0/24(ro)\n"), 0o644)
srv.shm.IncludeDir, srv.shm.ExportsDir, srv.shm.Exports = smbDir, nfsDir, filepath.Join(nfsDir, shares.DefaultExports)
rec := httptest.NewRecorder()
srv.ServeHTTP(rec, httptest.NewRequest("GET", "/shares", nil))
body := rec.Body.String()
for _, want := range []string{`id="external"`, "iso-pve", "/tank/iso/export/pve", "10.10.0.0/24(ro)", "RepoISO"} {
if !strings.Contains(body, want) {
t.Errorf("shares page lacks %q", want)
}
}
}