Sort render inputs so config is deterministic (stop restart loop)
client.List returns cache-ordered (non-deterministic) results, so the forward zones (and DNSSEC policies) reshuffled between reconciles. Before the config-hash change this was harmless, but now a reshuffled render rewrites the ConfigMap, flips the pod-template config hash, and the StatefulSet rolls forever (observed: resolver forward zones churn every reconcile, pod endlessly recreated). Sort every list rendered into named.conf (ACLs, views, forward zones, policies, DNSSEC policies) before rendering, so identical inputs always produce byte-identical config and the hash is stable.
This commit is contained in:
@@ -28,9 +28,28 @@ type RenderInput struct {
|
||||
// cluster. Both variants are shipped in the ConfigMap; the entrypoint selects
|
||||
// one based on the pod ordinal.
|
||||
func RenderNamedConf(in RenderInput) (primary string, secondary string) {
|
||||
// client.List returns cache-ordered (non-deterministic) results, so sort
|
||||
// every input slice before rendering. Otherwise the rendered config
|
||||
// reshuffles between reconciles, churning the ConfigMap — and with the
|
||||
// pod-template config hash that means an endless rolling restart.
|
||||
sortInput(&in)
|
||||
return render(in, true), render(in, false)
|
||||
}
|
||||
|
||||
// sortInput orders every list rendered into named.conf deterministically.
|
||||
func sortInput(in *RenderInput) {
|
||||
sort.Slice(in.ACLs, func(i, j int) bool { return in.ACLs[i].Name < in.ACLs[j].Name })
|
||||
sort.Slice(in.Views, func(i, j int) bool {
|
||||
if in.Views[i].Spec.Order != in.Views[j].Spec.Order {
|
||||
return in.Views[i].Spec.Order < in.Views[j].Spec.Order
|
||||
}
|
||||
return in.Views[i].Name < in.Views[j].Name
|
||||
})
|
||||
sort.Slice(in.Forwards, func(i, j int) bool { return in.Forwards[i].Spec.ZoneName < in.Forwards[j].Spec.ZoneName })
|
||||
sort.Slice(in.Policies, func(i, j int) bool { return in.Policies[i].Spec.ZoneName < in.Policies[j].Spec.ZoneName })
|
||||
sort.Slice(in.DNSSECPolicies, func(i, j int) bool { return in.DNSSECPolicies[i].Name < in.DNSSECPolicies[j].Name })
|
||||
}
|
||||
|
||||
func render(in RenderInput, isPrimary bool) string {
|
||||
c := in.Cluster
|
||||
var b strings.Builder
|
||||
|
||||
Reference in New Issue
Block a user