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
@@ -2,6 +2,7 @@ package console
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -52,12 +53,27 @@ func (c *Console) health() any {
|
||||
// reads.
|
||||
type clientsView struct {
|
||||
Groups []struct {
|
||||
Name string `json:"name"`
|
||||
config.Group
|
||||
Source string `json:"source"`
|
||||
} `json:"groups"`
|
||||
DefaultPolicy string `json:"default_policy"`
|
||||
}
|
||||
|
||||
// sameGroup compares two groups by what they configure, so an empty list and a
|
||||
// missing one are the same.
|
||||
func sameGroup(a, b config.Group) bool {
|
||||
norm := func(g config.Group) string {
|
||||
for _, p := range []*[]string{&g.Match.CIDRs, &g.Match.PTRSuffixes, &g.Match.Tags, &g.AllowDomains, &g.DenyDomains, &g.Upstreams} {
|
||||
if *p == nil {
|
||||
*p = []string{}
|
||||
}
|
||||
}
|
||||
out, _ := json.Marshal(g)
|
||||
return string(out)
|
||||
}
|
||||
return norm(a) == norm(b)
|
||||
}
|
||||
|
||||
func (c *Console) putACL(ctx context.Context, groups []config.Group, def string) (int64, error) {
|
||||
var view clientsView
|
||||
if err := c.opt.Runtime.Get(ctx, "/api/clients", &view); err != nil {
|
||||
@@ -69,13 +85,21 @@ func (c *Console) putACL(ctx context.Context, groups []config.Group, def string)
|
||||
if def != "" && def != view.DefaultPolicy {
|
||||
return 0, fmt.Errorf("default_policy is resolver configuration (currently %q), not a ClientGroup; change it in the resolver's config", view.DefaultPolicy)
|
||||
}
|
||||
inFile := map[string]bool{}
|
||||
inFile := map[string]config.Group{}
|
||||
for _, g := range view.Groups {
|
||||
if g.Source == "" {
|
||||
inFile[g.Name] = true
|
||||
inFile[g.Name] = g.Group
|
||||
}
|
||||
}
|
||||
res, err := c.opt.Editor.SyncGroups(ctx, groups, func(n string) bool { return inFile[n] }, actor(ctx))
|
||||
// The console writes cluster objects. A group the resolver's config file
|
||||
// defines is left alone, and a change to it is refused, because skipping it
|
||||
// quietly would make the save look as if it worked.
|
||||
for _, g := range groups {
|
||||
if have, ok := inFile[g.Name]; ok && !sameGroup(have, g) {
|
||||
return 0, fmt.Errorf("group %q is defined in the resolver's config file; change it there", g.Name)
|
||||
}
|
||||
}
|
||||
res, err := c.opt.Editor.SyncGroups(ctx, groups, func(n string) bool { _, ok := inFile[n]; return ok }, actor(ctx))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -209,6 +233,9 @@ func (c *Console) handleClients(w http.ResponseWriter, r *http.Request) {
|
||||
merged = append(merged, groupJSON(o))
|
||||
}
|
||||
view["groups"] = merged
|
||||
// What this editor can and cannot change, so the page can say so.
|
||||
view["default_policy_editable"] = false
|
||||
view["file_groups_editable"] = false
|
||||
api.WriteJSON(w, http.StatusOK, view)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user