Cluster UI: Blocklists management, locked config-file items, read-only upstreams; honour allow_config_write
The console now works as the cluster's editor, without the monitoring pages. Web: Blocklists gets Add/Edit/Remove for BlockList objects (and shows ones the resolver has not compiled yet); Clients marks cluster groups and locks config-file groups and the default policy; Upstreams becomes a read-only health view when the config cannot be written; Zones and Domains lose the managed-directory and list copy; the Local zones page and its API calls are removed; nav is Status, Blocklists, Domains, Lookup, Address redirects, Zones, Clients, Upstreams, Settings. Console: a change to a group the resolver's config file defines is refused with the reason instead of being skipped silently, and /api/clients says what the console can and cannot edit. Server: isFileWritable called a read-only existing file (a mounted ConfigMap) writable, and api.allow_config_write was not reflected in the writable flag nor checked by the upstream/ACL edit path. Both fixed; tests pin them. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7d58b80583
commit
7fa48bcdf9
18 files changed
+726
-404
No files matched your search
@@ -4,6 +4,8 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -789,3 +791,44 @@ func TestConcurrentQueries(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestConfigWritesFollowAllowConfigWrite: with api.allow_config_write off, which
|
||||
// is how the cluster deployment runs, nothing may rewrite the config file, and
|
||||
// the console is told so (config.writable) instead of offering editors whose
|
||||
// every save fails. The upstream editor used to ignore the setting.
|
||||
func TestConfigWritesFollowAllowConfigWrite(t *testing.T) {
|
||||
h := newHarness(t, func(c *config.Config) { c.API.AllowConfigWrite = false })
|
||||
before := h.OriginalConfig()
|
||||
|
||||
_, body := h.get("/api/status")
|
||||
var st struct {
|
||||
Config struct {
|
||||
Writable bool `json:"writable"`
|
||||
} `json:"config"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &st); err != nil || st.Config.Writable {
|
||||
t.Errorf("status config.writable = %v (%v), want false", st.Config.Writable, err)
|
||||
}
|
||||
_, body = h.get("/api/settings")
|
||||
var settings struct {
|
||||
Writable bool `json:"writable"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &settings); err != nil || settings.Writable {
|
||||
t.Errorf("settings writable = %v (%v), want false", settings.Writable, err)
|
||||
}
|
||||
|
||||
up := fmt.Sprintf(`{"policy":"failover","servers":[{"addr":%q,"proto":"udp","weight":1}],"max_attempts":1,"breaker_threshold":5,"healthcheck_interval_s":0}`, h.up.addr)
|
||||
if code, out := h.do("PUT", "/api/upstreams", up); code != http.StatusBadRequest || !strings.Contains(string(out), "allow_config_write") {
|
||||
t.Errorf("PUT /api/upstreams = %d %s, want a 400 naming the setting", code, out)
|
||||
}
|
||||
if code, _ := h.do("PUT", "/api/clients", `{"groups":[]}`); code != http.StatusBadRequest {
|
||||
t.Errorf("PUT /api/clients = %d, want 400", code)
|
||||
}
|
||||
if code, _ := h.do("PUT", "/api/settings", `{"config_yaml":"x: 1"}`); code != http.StatusForbidden {
|
||||
t.Errorf("PUT /api/settings = %d, want 403", code)
|
||||
}
|
||||
raw, err := os.ReadFile(filepath.Join(h.dir, "config.yaml"))
|
||||
if err != nil || string(raw) != before {
|
||||
t.Errorf("the config file changed (%v)", err)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user