Clusters : port 8006 par défaut, liste des cibles jamais null

This commit is contained in:
claude Bot committed 2026-10-23 18:41:59 +02:00
1 parent 8dd2f187ee
commit ee8343c69f
5 files changed
+50 -3

No files matched your search

+12
View File
@@ -216,3 +216,15 @@ func (c *Config) HasKind(k model.Kind) bool {
}
return false
}
// NormalizeURL trims the URL of a cluster and adds the PVE port (8006) when
// none is given: https://pve1 becomes https://pve1:8006.
func NormalizeURL(raw string) string {
s := strings.TrimRight(strings.TrimSpace(raw), "/")
u, err := url.Parse(s)
if err != nil || u.Host == "" || u.Port() != "" {
return s
}
u.Host = net.JoinHostPort(u.Hostname(), "8006")
return u.String()
}
+15
View File
@@ -36,3 +36,18 @@ func TestSSHOptions(t *testing.T) {
t.Fatalf("%+v", o)
}
}
func TestNormalizeURL(t *testing.T) {
for in, want := range map[string]string{
"https://pve1": "https://pve1:8006",
" https://pve1/ ": "https://pve1:8006",
"https://pve1:443": "https://pve1:443",
"https://10.0.0.1": "https://10.0.0.1:8006",
"https://[fd00::1]": "https://[fd00::1]:8006",
"https://pve1:8006/": "https://pve1:8006",
} {
if got := NormalizeURL(in); got != want {
t.Errorf("NormalizeURL(%q) = %q, want %q", in, got, want)
}
}
}
+13
View File
@@ -239,6 +239,19 @@ func TestFromScratch(t *testing.T) {
if _, d := adm.do("GET", "/api/state", nil, false); d["setup"] != "clusters" {
t.Fatalf("state on an empty database: %v", d)
}
// a primary alone: no target yet, the state lists none (not null); no port: 8006
if code, d := adm.do("PUT", "/api/clusters/-", map[string]any{"name": "solo", "role": "primary", "url": "https://pve-solo", "password": "x", "tls": "insecure"}, true); code != http.StatusOK {
t.Fatalf("save solo: %d %v", code, d)
}
if _, d := adm.do("GET", "/api/clusters", nil, false); d["clusters"].([]any)[0].(map[string]any)["url"] != "https://pve-solo:8006" {
t.Fatalf("default port expected: %v", d)
}
if _, d := adm.do("GET", "/api/state", nil, false); d["state"] == nil || d["state"].(map[string]any)["clients"] == nil {
t.Fatalf("clients must be an empty list: %v", d)
}
if code, d := adm.do("DELETE", "/api/clusters/solo", nil, true); code != http.StatusOK {
t.Fatalf("delete solo: %d %v", code, d)
}
for _, c := range []struct {
name string
s *pvetest.Server
+1 -1
View File
@@ -97,7 +97,7 @@ type State struct {
// clients (empty: all). A cluster that cannot be read is reported in its
// Error field, it does not stop the others.
func Analyze(ctx context.Context, cfg *config.Config, only []string) *State {
st := &State{At: time.Now().UTC(), Kinds: cfg.Kinds, Prune: cfg.Prune, cfg: cfg, actuals: map[string][]model.Object{}}
st := &State{At: time.Now().UTC(), Kinds: cfg.Kinds, Prune: cfg.Prune, Clients: []Client{}, cfg: cfg, actuals: map[string][]model.Object{}}
var refSnap model.Snapshot
st.Reference, refSnap = read(ctx, cfg.Reference, cfg.Kinds, adapter.ReadRunning)
st.ref = refSnap.Objects
+9 -2
View File
@@ -283,7 +283,7 @@ func (s *Store) connection(c Cluster, blob []byte) (config.Cluster, error) {
if err != nil {
return config.Cluster{}, fmt.Errorf("cluster %s: password: %w", c.Name, err)
}
cc := config.Cluster{Name: c.Name, URL: c.URL, User: c.User, Password: string(pass), Timeout: c.Timeout,
cc := config.Cluster{Name: c.Name, URL: config.NormalizeURL(c.URL), User: c.User, Password: string(pass), Timeout: c.Timeout,
SSHHost: c.SSHHost, SSHPort: c.SSHPort, SSHHostKey: c.SSHHostKey}
switch c.TLS {
case TLSPin:
@@ -307,7 +307,7 @@ func ValidClusterRole(r string) bool {
// are forgotten when the address they belong to changes.
func (s *Store) SaveCluster(ctx context.Context, oldName string, c Cluster) error {
c.Name = strings.TrimSpace(c.Name)
c.URL = strings.TrimRight(strings.TrimSpace(c.URL), "/")
c.URL = config.NormalizeURL(c.URL)
if c.Name == "" || c.URL == "" {
return errors.New("name and url are required")
}
@@ -509,6 +509,13 @@ func (s *Store) Settings(ctx context.Context) (Settings, error) {
if len(st.Kinds) == 0 {
st.Kinds = append(st.Kinds, model.Kinds...)
}
// empty maps, not null, for the web UI
if st.Objects == nil {
st.Objects = map[model.Kind]config.Selection{}
}
if st.Policies == nil {
st.Policies = map[model.Kind]map[string]adapter.Policy{}
}
return st, nil
}