more stuff
build / build-push (push) Successful in 1m11s

This commit is contained in:
mats committed 2026-10-16 14:49:50 +02:00
1 parent 289a8cc2d8
commit 66f36cfac6
19 files changed
+1905 -817

No files matched your search

+51 -13
View File
@@ -46,14 +46,44 @@ func convertGroup(u *unstructured.Unstructured) (group, error) {
return groupFromSpec(u.GetName(), s)
}
// FieldError is a validation failure tied to one field of a ClientGroup, so an
// editor can show the message next to the input that caused it. Field is the
// JSON path inside the group ("policy", "match.cidrs", "rate_limit"); "name"
// and "match" are used for the whole-object cases.
type FieldError struct {
Field string
Msg string
}
func (e FieldError) Error() string { return e.Msg }
// CheckGroup validates a ClientGroup spec the way the runtime will, so an
// editor can refuse a bad one before it is written.
// editor can refuse a bad one before it is written. It reports the first
// problem; CheckGroupAll reports every one.
func CheckGroup(name string, s ClientGroupSpec) (config.Group, error) {
g, err := groupFromSpec(name, s)
return g.cfg, err
g, errs := checkGroup(name, s)
if len(errs) > 0 {
return g.cfg, errs[0]
}
return g.cfg, nil
}
// CheckGroupAll is CheckGroup for an editor that wants to show every problem
// at once instead of one per save.
func CheckGroupAll(name string, s ClientGroupSpec) []FieldError {
_, errs := checkGroup(name, s)
return errs
}
func groupFromSpec(name string, s ClientGroupSpec) (group, error) {
g, errs := checkGroup(name, s)
if len(errs) > 0 {
return group{}, errs[0]
}
return g, nil
}
func checkGroup(name string, s ClientGroupSpec) (group, []FieldError) {
g := config.Group{
Name: name,
Comment: s.Comment,
@@ -68,14 +98,18 @@ func groupFromSpec(name string, s ClientGroupSpec) (group, error) {
LogQueries: s.LogQueries,
Upstreams: s.Upstreams,
}
var errs []FieldError
bad := func(field, format string, args ...any) {
errs = append(errs, FieldError{Field: field, Msg: fmt.Sprintf(format, args...)})
}
switch g.Policy {
case "allow", "deny", "block_only":
default:
return group{}, fmt.Errorf("policy must be allow, deny or block_only (got %q)", g.Policy)
bad("policy", "policy must be allow, deny or block_only (got %q)", g.Policy)
}
for _, cidr := range g.Match.CIDRs {
if _, _, err := net.ParseCIDR(cidr); err != nil {
return group{}, fmt.Errorf("bad CIDR %q", cidr)
bad("match.cidrs", "bad CIDR %q", cidr)
}
}
// Copy before normalising: the slice belongs to the decoded object.
@@ -85,18 +119,22 @@ func groupFromSpec(name string, s ClientGroupSpec) (group, error) {
g.Match.PTRSuffixes[i] = s + "."
}
}
if !g.Match.Any && len(g.Match.CIDRs) == 0 && len(g.Match.PTRSuffixes) == 0 && len(g.Match.Tags) == 0 {
return group{}, fmt.Errorf("match selects no clients: set cidrs, ptr_suffixes, tags or any")
}
if g.Match.Any && len(g.Match.CIDRs) == 0 && len(g.Match.PTRSuffixes) == 0 && len(g.Match.Tags) == 0 {
selectsSomething := len(g.Match.CIDRs) > 0 || len(g.Match.PTRSuffixes) > 0 || len(g.Match.Tags) > 0
switch {
case !g.Match.Any && !selectsSomething:
bad("match", "match selects no clients: set cidrs, ptr_suffixes, tags or any")
case g.Match.Any && !selectsSomething:
// config.Validate refuses this shape for every group. Refusing it here
// rejects one object instead of the whole merged configuration.
return group{}, fmt.Errorf("match.any alone is not accepted; use a catch-all in the YAML acl.groups")
bad("match.any", "match.any alone is not accepted; use a catch-all in the YAML acl.groups")
}
if g.RateLimit < 0 || g.MaxConcurrent < 0 {
return group{}, fmt.Errorf("rate_limit and max_concurrent must not be negative")
if g.RateLimit < 0 {
bad("rate_limit", "rate_limit must not be negative")
}
return group{cfg: g, priority: s.Priority}, nil
if g.MaxConcurrent < 0 {
bad("max_concurrent", "max_concurrent must not be negative")
}
return group{cfg: g, priority: s.Priority}, errs
}
func convertList(u *unstructured.Unstructured) (config.ListSource, error) {