From 46d38ad721388a8f02f45b86ac4691ae97f3084e Mon Sep 17 00:00:00 2001 From: Markus Wennrich Date: Thu, 16 Apr 2026 11:12:39 +0200 Subject: [PATCH 1/6] limit number of updates to fqdnstate configmap --- .../clusterwidenetworkpolicy_controller.go | 9 +-- main.go | 33 ++++++----- pkg/dns/dnscache.go | 59 +++++++++++++------ pkg/dns/dnscache_test.go | 20 +++++++ pkg/dns/dnsproxy.go | 4 +- 5 files changed, 87 insertions(+), 38 deletions(-) diff --git a/controllers/clusterwidenetworkpolicy_controller.go b/controllers/clusterwidenetworkpolicy_controller.go index 3fba5316..37d77161 100644 --- a/controllers/clusterwidenetworkpolicy_controller.go +++ b/controllers/clusterwidenetworkpolicy_controller.go @@ -43,9 +43,10 @@ type ClusterwideNetworkPolicyReconciler struct { Ctx context.Context Recorder record.EventRecorder - Interval time.Duration - DnsProxy *dns.DNSProxy - SkipDNS bool + Interval time.Duration + DNSCacheUpdateInterval time.Duration + DnsProxy *dns.DNSProxy + SkipDNS bool } // SetupWithManager configures this controller to run in schedule @@ -145,7 +146,7 @@ func (r *ClusterwideNetworkPolicyReconciler) manageDNSProxy( if enableDNS && r.DnsProxy == nil { r.Log.Info("DNS Proxy is initialized") - if r.DnsProxy, err = dns.NewDNSProxy(r.Ctx, f.Spec.DNSServerAddress, f.Spec.DNSPort, r.ShootClient, ctrl.Log.WithName("DNS proxy")); err != nil { + if r.DnsProxy, err = dns.NewDNSProxy(r.Ctx, f.Spec.DNSServerAddress, f.Spec.DNSPort, r.DNSCacheUpdateInterval, r.ShootClient, ctrl.Log.WithName("DNS proxy")); err != nil { return fmt.Errorf("failed to init DNS proxy: %w", err) } go r.DnsProxy.Run() diff --git a/main.go b/main.go index 6f359da8..459f596b 100644 --- a/main.go +++ b/main.go @@ -56,14 +56,15 @@ func init() { func main() { var ( - logLevel string - isVersion bool - metricsAddr string - enableIDS bool - enableSignatureCheck bool - hostsFile string - firewallName string - kubeconfigPath = os.Getenv("KUBECONFIG") + logLevel string + isVersion bool + metricsAddr string + enableIDS bool + enableSignatureCheck bool + hostsFile string + firewallName string + dnsCacheUpdateInterval time.Duration + kubeconfigPath = os.Getenv("KUBECONFIG") ) flag.StringVar(&logLevel, "log-level", "info", "the log level of the controller") @@ -73,6 +74,7 @@ func main() { flag.StringVar(&hostsFile, "hosts-file", "/etc/hosts", "The hosts file to manipulate for the droptailer.") flag.BoolVar(&enableSignatureCheck, "enable-signature-check", true, "Set this to false to ignore signature checking.") flag.StringVar(&firewallName, "firewall-name", "", "the name of the firewall resource in the seed cluster to reconcile (defaults to hostname)") + flag.DurationVar(&dnsCacheUpdateInterval, "dnscache-update-interval", 2*time.Second, "minimum interval between fqdn state configmap updates") if _, err := os.Stat(seedKubeconfigPath); err == nil || os.IsExist(err) { // controller-runtime registered this flag already, so we can use it @@ -265,13 +267,14 @@ func main() { // ClusterwideNetworkPolicy Reconciler if err = (&controllers.ClusterwideNetworkPolicyReconciler{ - SeedClient: seedMgr.GetClient(), - ShootClient: shootMgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("ClusterwideNetworkPolicy"), - Ctx: ctx, - Recorder: shootMgr.GetEventRecorderFor("FirewallController"), // nolint:staticcheck - FirewallName: firewallName, - SeedNamespace: seedNamespace, + SeedClient: seedMgr.GetClient(), + ShootClient: shootMgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("ClusterwideNetworkPolicy"), + Ctx: ctx, + Recorder: shootMgr.GetEventRecorderFor("FirewallController"), // nolint:staticcheck + FirewallName: firewallName, + SeedNamespace: seedNamespace, + DNSCacheUpdateInterval: dnsCacheUpdateInterval, }).SetupWithManager(shootMgr); err != nil { l.Error("unable to create clusterwidenetworkpolicy controller", "error", err) panic(err) diff --git a/pkg/dns/dnscache.go b/pkg/dns/dnscache.go index 48035ace..e541f81b 100644 --- a/pkg/dns/dnscache.go +++ b/pkg/dns/dnscache.go @@ -115,26 +115,29 @@ type cacheEntry struct { type DNSCache struct { sync.RWMutex - log logr.Logger - fqdnToEntry map[string]cacheEntry - setNames map[string]struct{} - dnsServerAddr string - shootClient client.Client - ctx context.Context - ipv4Enabled bool - ipv6Enabled bool + log logr.Logger + fqdnToEntry map[string]cacheEntry + setNames map[string]struct{} + dnsServerAddr string + shootClient client.Client + ctx context.Context + ipv4Enabled bool + ipv6Enabled bool + stateUpdateInterval time.Duration + lastStateUpdate time.Time } -func newDNSCache(ctx context.Context, dns string, ipv4Enabled, ipv6Enabled bool, shootClient client.Client, log logr.Logger) (*DNSCache, error) { +func newDNSCache(ctx context.Context, dns string, ipv4Enabled, ipv6Enabled bool, stateUpdateInterval time.Duration, shootClient client.Client, log logr.Logger) (*DNSCache, error) { c := DNSCache{ - log: log, - fqdnToEntry: map[string]cacheEntry{}, - setNames: map[string]struct{}{}, - dnsServerAddr: dns, - shootClient: shootClient, - ctx: ctx, - ipv4Enabled: ipv4Enabled, - ipv6Enabled: ipv6Enabled, + log: log, + fqdnToEntry: map[string]cacheEntry{}, + setNames: map[string]struct{}{}, + dnsServerAddr: dns, + shootClient: shootClient, + ctx: ctx, + ipv4Enabled: ipv4Enabled, + ipv6Enabled: ipv6Enabled, + stateUpdateInterval: stateUpdateInterval, } nn := types.NamespacedName{Name: fqdnStateConfigmapName, Namespace: fqdnStateNamespace} @@ -169,6 +172,12 @@ func newDNSCache(ctx context.Context, dns string, ipv4Enabled, ipv6Enabled bool, // writeStateToConfigmap writes the whole DNS cache to the state configmap func (c *DNSCache) writeStateToConfigmap() error { + now := time.Now() + if !c.shouldWriteStateToConfigmap(now) { + c.log.V(4).Info("DEBUG skipping fqdnstate configmap update due to update interval", "interval", c.stateUpdateInterval) + return nil + } + c.RLock() s, err := yaml.Marshal(c.fqdnToEntry) c.RUnlock() @@ -236,6 +245,22 @@ func (c *DNSCache) writeStateToConfigmap() error { return nil } +func (c *DNSCache) shouldWriteStateToConfigmap(now time.Time) bool { + c.Lock() + defer c.Unlock() + + if c.stateUpdateInterval <= 0 { + return true + } + + if !c.lastStateUpdate.IsZero() && now.Sub(c.lastStateUpdate) < c.stateUpdateInterval { + return false + } + + c.lastStateUpdate = now + return true +} + // getSetsForFQDN returns sets for FQDN selector func (c *DNSCache) getSetsForFQDN(fqdn firewallv1.FQDNSelector) (result []firewallv1.IPSet) { sets := map[string]firewallv1.IPSet{} diff --git a/pkg/dns/dnscache_test.go b/pkg/dns/dnscache_test.go index 3f692d21..794a7326 100644 --- a/pkg/dns/dnscache_test.go +++ b/pkg/dns/dnscache_test.go @@ -475,3 +475,23 @@ func TestRace_ConcurrentMultipleReaders(t *testing.T) { close(start) wg.Wait() } + +func TestDNSCache_shouldWriteStateToConfigmap(t *testing.T) { + now := time.Now() + + cache := &DNSCache{ + stateUpdateInterval: 2 * time.Second, + } + + if got := cache.shouldWriteStateToConfigmap(now); !got { + t.Fatalf("expected first write attempt to be allowed") + } + + if got := cache.shouldWriteStateToConfigmap(now.Add(1500 * time.Millisecond)); got { + t.Fatalf("expected write attempt within interval to be throttled") + } + + if got := cache.shouldWriteStateToConfigmap(now.Add(2 * time.Second)); !got { + t.Fatalf("expected write attempt at interval boundary to be allowed") + } +} diff --git a/pkg/dns/dnsproxy.go b/pkg/dns/dnsproxy.go index b9a16212..8f87f1cc 100644 --- a/pkg/dns/dnsproxy.go +++ b/pkg/dns/dnsproxy.go @@ -39,7 +39,7 @@ type DNSProxy struct { handler DNSHandler } -func NewDNSProxy(ctx context.Context, dns string, port *uint, shootClient client.Client, log logr.Logger) (*DNSProxy, error) { +func NewDNSProxy(ctx context.Context, dns string, port *uint, stateUpdateInterval time.Duration, shootClient client.Client, log logr.Logger) (*DNSProxy, error) { if dns == "" { dns = defaultDNSServerAddr } @@ -59,7 +59,7 @@ func NewDNSProxy(ctx context.Context, dns string, port *uint, shootClient client } backgroundCtx, cancel := context.WithCancel(ctx) - cache, err := newDNSCache(backgroundCtx, dns, true, false, shootClient, log.WithName("DNS cache")) + cache, err := newDNSCache(backgroundCtx, dns, true, false, stateUpdateInterval, shootClient, log.WithName("DNS cache")) if err != nil { cancel() return nil, err From c13b4cecdb15958f3544f6a95b3d618dc3acd912 Mon Sep 17 00:00:00 2001 From: Markus Wennrich Date: Thu, 16 Apr 2026 11:43:38 +0200 Subject: [PATCH 2/6] use ticker approach --- pkg/dns/dnscache.go | 76 ++++++++++++++++++++++++++-------------- pkg/dns/dnscache_test.go | 20 ----------- 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/pkg/dns/dnscache.go b/pkg/dns/dnscache.go index e541f81b..08d99180 100644 --- a/pkg/dns/dnscache.go +++ b/pkg/dns/dnscache.go @@ -124,7 +124,7 @@ type DNSCache struct { ipv4Enabled bool ipv6Enabled bool stateUpdateInterval time.Duration - lastStateUpdate time.Time + stateDirty bool } func newDNSCache(ctx context.Context, dns string, ipv4Enabled, ipv6Enabled bool, stateUpdateInterval time.Duration, shootClient client.Client, log logr.Logger) (*DNSCache, error) { @@ -167,17 +167,12 @@ func newDNSCache(ctx context.Context, dns string, ipv4Enabled, ipv6Enabled bool, if err != nil { c.log.Info("could not unmarshal state from fqdnstate configmap, ignoring content.", "error", err) } + c.startStateSyncLoop() return &c, nil } // writeStateToConfigmap writes the whole DNS cache to the state configmap func (c *DNSCache) writeStateToConfigmap() error { - now := time.Now() - if !c.shouldWriteStateToConfigmap(now) { - c.log.V(4).Info("DEBUG skipping fqdnstate configmap update due to update interval", "interval", c.stateUpdateInterval) - return nil - } - c.RLock() s, err := yaml.Marshal(c.fqdnToEntry) c.RUnlock() @@ -224,6 +219,8 @@ func (c *DNSCache) writeStateToConfigmap() error { return err } + c.markStateSynced() + return nil } @@ -236,31 +233,15 @@ func (c *DNSCache) writeStateToConfigmap() error { } debugLog.Info("DEBUG updated cm", "old data", cm.Data, "new data", data) + c.markStateSynced() return nil } debugLog.Info("DEBUG no need to update cm, already up to date") - return nil } -func (c *DNSCache) shouldWriteStateToConfigmap(now time.Time) bool { - c.Lock() - defer c.Unlock() - - if c.stateUpdateInterval <= 0 { - return true - } - - if !c.lastStateUpdate.IsZero() && now.Sub(c.lastStateUpdate) < c.stateUpdateInterval { - return false - } - - c.lastStateUpdate = now - return true -} - // getSetsForFQDN returns sets for FQDN selector func (c *DNSCache) getSetsForFQDN(fqdn firewallv1.FQDNSelector) (result []firewallv1.IPSet) { sets := map[string]firewallv1.IPSet{} @@ -486,9 +467,7 @@ func (c *DNSCache) Update(lookupTime time.Time, qname string, msg *dnsgo.Msg, fq } if ipEntriesUpdated { - if err := c.writeStateToConfigmap(); err != nil { - c.log.V(4).Info("DEBUG could not write updated DNS cache to state configmap", "configmap", fqdnStateConfigmapName, "namespace", fqdnStateNamespace, "error", err) - } + c.markStateDirty() } return found, nil @@ -621,3 +600,46 @@ func createRenderIPSetFromIPEntry(version IPVersion, entry *iPEntry) RenderIPSet Version: version, } } + +func (c *DNSCache) startStateSyncLoop() { + if c.stateUpdateInterval <= 0 { + return + } + + ticker := time.NewTicker(c.stateUpdateInterval) + go func() { + defer ticker.Stop() + for { + select { + case <-c.ctx.Done(): + return + case <-ticker.C: + if !c.isStateDirty() { + continue + } + if err := c.writeStateToConfigmap(); err != nil { + c.log.V(4).Info("DEBUG periodic sync of fqdnstate configmap failed", "configmap", fqdnStateConfigmapName, "namespace", fqdnStateNamespace, "error", err) + } + } + } + }() +} + +func (c *DNSCache) markStateDirty() { + c.Lock() + c.stateDirty = true + c.Unlock() +} + +func (c *DNSCache) markStateSynced() { + c.Lock() + c.stateDirty = false + c.Unlock() +} + +func (c *DNSCache) isStateDirty() bool { + c.RLock() + defer c.RUnlock() + + return c.stateDirty +} diff --git a/pkg/dns/dnscache_test.go b/pkg/dns/dnscache_test.go index 794a7326..3f692d21 100644 --- a/pkg/dns/dnscache_test.go +++ b/pkg/dns/dnscache_test.go @@ -475,23 +475,3 @@ func TestRace_ConcurrentMultipleReaders(t *testing.T) { close(start) wg.Wait() } - -func TestDNSCache_shouldWriteStateToConfigmap(t *testing.T) { - now := time.Now() - - cache := &DNSCache{ - stateUpdateInterval: 2 * time.Second, - } - - if got := cache.shouldWriteStateToConfigmap(now); !got { - t.Fatalf("expected first write attempt to be allowed") - } - - if got := cache.shouldWriteStateToConfigmap(now.Add(1500 * time.Millisecond)); got { - t.Fatalf("expected write attempt within interval to be throttled") - } - - if got := cache.shouldWriteStateToConfigmap(now.Add(2 * time.Second)); !got { - t.Fatalf("expected write attempt at interval boundary to be allowed") - } -} From aede6ed40059365d641f50e77173603465ba90b8 Mon Sep 17 00:00:00 2001 From: Markus Wennrich Date: Thu, 16 Apr 2026 12:43:00 +0200 Subject: [PATCH 3/6] add debug logs --- pkg/dns/dnscache.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/dns/dnscache.go b/pkg/dns/dnscache.go index 08d99180..82a290dd 100644 --- a/pkg/dns/dnscache.go +++ b/pkg/dns/dnscache.go @@ -603,8 +603,10 @@ func createRenderIPSetFromIPEntry(version IPVersion, entry *iPEntry) RenderIPSet func (c *DNSCache) startStateSyncLoop() { if c.stateUpdateInterval <= 0 { + c.log.Info("state update interval is set to 0 or negative, skipping state sync loop") return } + c.log.Info("starting fqdnstate sync loop", "interval", c.stateUpdateInterval) ticker := time.NewTicker(c.stateUpdateInterval) go func() { From 39b0ab205a7bcb6907e2b535de8d551e8cb80b36 Mon Sep 17 00:00:00 2001 From: Markus Wennrich Date: Thu, 16 Apr 2026 13:25:19 +0200 Subject: [PATCH 4/6] start ticker earlier --- pkg/dns/dnscache.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/dns/dnscache.go b/pkg/dns/dnscache.go index 82a290dd..5dff2e3c 100644 --- a/pkg/dns/dnscache.go +++ b/pkg/dns/dnscache.go @@ -148,6 +148,8 @@ func newDNSCache(ctx context.Context, dns string, ipv4Enabled, ipv6Enabled bool, cancel() }() + c.startStateSyncLoop() + err := shootClient.Get(ctxWithTimeout, nn, scm) if err != nil && !apierrors.IsNotFound(err) { c.log.Error(err, "error reading fqndstate configmap") @@ -167,7 +169,6 @@ func newDNSCache(ctx context.Context, dns string, ipv4Enabled, ipv6Enabled bool, if err != nil { c.log.Info("could not unmarshal state from fqdnstate configmap, ignoring content.", "error", err) } - c.startStateSyncLoop() return &c, nil } From 7051d0a49422363b3a5c78dcefa2525fcde266fa Mon Sep 17 00:00:00 2001 From: Markus Wennrich Date: Thu, 16 Apr 2026 14:51:53 +0200 Subject: [PATCH 5/6] rename flag, vars, func --- .../clusterwidenetworkpolicy_controller.go | 10 ++-- main.go | 36 ++++++------- pkg/dns/dnscache.go | 52 +++++++++---------- pkg/dns/dnsproxy.go | 4 +- 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/controllers/clusterwidenetworkpolicy_controller.go b/controllers/clusterwidenetworkpolicy_controller.go index 37d77161..40cb2daa 100644 --- a/controllers/clusterwidenetworkpolicy_controller.go +++ b/controllers/clusterwidenetworkpolicy_controller.go @@ -43,10 +43,10 @@ type ClusterwideNetworkPolicyReconciler struct { Ctx context.Context Recorder record.EventRecorder - Interval time.Duration - DNSCacheUpdateInterval time.Duration - DnsProxy *dns.DNSProxy - SkipDNS bool + Interval time.Duration + FQDNStateSyncInterval time.Duration + DnsProxy *dns.DNSProxy + SkipDNS bool } // SetupWithManager configures this controller to run in schedule @@ -146,7 +146,7 @@ func (r *ClusterwideNetworkPolicyReconciler) manageDNSProxy( if enableDNS && r.DnsProxy == nil { r.Log.Info("DNS Proxy is initialized") - if r.DnsProxy, err = dns.NewDNSProxy(r.Ctx, f.Spec.DNSServerAddress, f.Spec.DNSPort, r.DNSCacheUpdateInterval, r.ShootClient, ctrl.Log.WithName("DNS proxy")); err != nil { + if r.DnsProxy, err = dns.NewDNSProxy(r.Ctx, f.Spec.DNSServerAddress, f.Spec.DNSPort, r.FQDNStateSyncInterval, r.ShootClient, ctrl.Log.WithName("DNS proxy")); err != nil { return fmt.Errorf("failed to init DNS proxy: %w", err) } go r.DnsProxy.Run() diff --git a/main.go b/main.go index 459f596b..5ff0a070 100644 --- a/main.go +++ b/main.go @@ -56,15 +56,15 @@ func init() { func main() { var ( - logLevel string - isVersion bool - metricsAddr string - enableIDS bool - enableSignatureCheck bool - hostsFile string - firewallName string - dnsCacheUpdateInterval time.Duration - kubeconfigPath = os.Getenv("KUBECONFIG") + logLevel string + isVersion bool + metricsAddr string + enableIDS bool + enableSignatureCheck bool + hostsFile string + firewallName string + fqdnStateSyncInterval time.Duration + kubeconfigPath = os.Getenv("KUBECONFIG") ) flag.StringVar(&logLevel, "log-level", "info", "the log level of the controller") @@ -74,7 +74,7 @@ func main() { flag.StringVar(&hostsFile, "hosts-file", "/etc/hosts", "The hosts file to manipulate for the droptailer.") flag.BoolVar(&enableSignatureCheck, "enable-signature-check", true, "Set this to false to ignore signature checking.") flag.StringVar(&firewallName, "firewall-name", "", "the name of the firewall resource in the seed cluster to reconcile (defaults to hostname)") - flag.DurationVar(&dnsCacheUpdateInterval, "dnscache-update-interval", 2*time.Second, "minimum interval between fqdn state configmap updates") + flag.DurationVar(&fqdnStateSyncInterval, "fqdnstate-sync-interval", 2*time.Second, "minimum interval between fqdn state configmap syncs") if _, err := os.Stat(seedKubeconfigPath); err == nil || os.IsExist(err) { // controller-runtime registered this flag already, so we can use it @@ -267,14 +267,14 @@ func main() { // ClusterwideNetworkPolicy Reconciler if err = (&controllers.ClusterwideNetworkPolicyReconciler{ - SeedClient: seedMgr.GetClient(), - ShootClient: shootMgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("ClusterwideNetworkPolicy"), - Ctx: ctx, - Recorder: shootMgr.GetEventRecorderFor("FirewallController"), // nolint:staticcheck - FirewallName: firewallName, - SeedNamespace: seedNamespace, - DNSCacheUpdateInterval: dnsCacheUpdateInterval, + SeedClient: seedMgr.GetClient(), + ShootClient: shootMgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("ClusterwideNetworkPolicy"), + Ctx: ctx, + Recorder: shootMgr.GetEventRecorderFor("FirewallController"), // nolint:staticcheck + FirewallName: firewallName, + SeedNamespace: seedNamespace, + FQDNStateSyncInterval: fqdnStateSyncInterval, }).SetupWithManager(shootMgr); err != nil { l.Error("unable to create clusterwidenetworkpolicy controller", "error", err) panic(err) diff --git a/pkg/dns/dnscache.go b/pkg/dns/dnscache.go index 5dff2e3c..9709996a 100644 --- a/pkg/dns/dnscache.go +++ b/pkg/dns/dnscache.go @@ -115,29 +115,29 @@ type cacheEntry struct { type DNSCache struct { sync.RWMutex - log logr.Logger - fqdnToEntry map[string]cacheEntry - setNames map[string]struct{} - dnsServerAddr string - shootClient client.Client - ctx context.Context - ipv4Enabled bool - ipv6Enabled bool - stateUpdateInterval time.Duration - stateDirty bool + log logr.Logger + fqdnToEntry map[string]cacheEntry + setNames map[string]struct{} + dnsServerAddr string + shootClient client.Client + ctx context.Context + ipv4Enabled bool + ipv6Enabled bool + fqdnStateSyncInterval time.Duration + stateDirty bool } -func newDNSCache(ctx context.Context, dns string, ipv4Enabled, ipv6Enabled bool, stateUpdateInterval time.Duration, shootClient client.Client, log logr.Logger) (*DNSCache, error) { +func newDNSCache(ctx context.Context, dns string, ipv4Enabled, ipv6Enabled bool, fqdnStateSyncInterval time.Duration, shootClient client.Client, log logr.Logger) (*DNSCache, error) { c := DNSCache{ - log: log, - fqdnToEntry: map[string]cacheEntry{}, - setNames: map[string]struct{}{}, - dnsServerAddr: dns, - shootClient: shootClient, - ctx: ctx, - ipv4Enabled: ipv4Enabled, - ipv6Enabled: ipv6Enabled, - stateUpdateInterval: stateUpdateInterval, + log: log, + fqdnToEntry: map[string]cacheEntry{}, + setNames: map[string]struct{}{}, + dnsServerAddr: dns, + shootClient: shootClient, + ctx: ctx, + ipv4Enabled: ipv4Enabled, + ipv6Enabled: ipv6Enabled, + fqdnStateSyncInterval: fqdnStateSyncInterval, } nn := types.NamespacedName{Name: fqdnStateConfigmapName, Namespace: fqdnStateNamespace} @@ -148,7 +148,7 @@ func newDNSCache(ctx context.Context, dns string, ipv4Enabled, ipv6Enabled bool, cancel() }() - c.startStateSyncLoop() + c.startFQDNStateSyncLoop() err := shootClient.Get(ctxWithTimeout, nn, scm) if err != nil && !apierrors.IsNotFound(err) { @@ -602,14 +602,14 @@ func createRenderIPSetFromIPEntry(version IPVersion, entry *iPEntry) RenderIPSet } } -func (c *DNSCache) startStateSyncLoop() { - if c.stateUpdateInterval <= 0 { - c.log.Info("state update interval is set to 0 or negative, skipping state sync loop") +func (c *DNSCache) startFQDNStateSyncLoop() { + if c.fqdnStateSyncInterval <= 0 { + c.log.Info("fqdnstate sync interval is set to 0 or negative, skipping state sync loop") return } - c.log.Info("starting fqdnstate sync loop", "interval", c.stateUpdateInterval) + c.log.Info("starting fqdnstate sync loop", "interval", c.fqdnStateSyncInterval) - ticker := time.NewTicker(c.stateUpdateInterval) + ticker := time.NewTicker(c.fqdnStateSyncInterval) go func() { defer ticker.Stop() for { diff --git a/pkg/dns/dnsproxy.go b/pkg/dns/dnsproxy.go index 8f87f1cc..e6482dd6 100644 --- a/pkg/dns/dnsproxy.go +++ b/pkg/dns/dnsproxy.go @@ -39,7 +39,7 @@ type DNSProxy struct { handler DNSHandler } -func NewDNSProxy(ctx context.Context, dns string, port *uint, stateUpdateInterval time.Duration, shootClient client.Client, log logr.Logger) (*DNSProxy, error) { +func NewDNSProxy(ctx context.Context, dns string, port *uint, fqdnStateSyncInterval time.Duration, shootClient client.Client, log logr.Logger) (*DNSProxy, error) { if dns == "" { dns = defaultDNSServerAddr } @@ -59,7 +59,7 @@ func NewDNSProxy(ctx context.Context, dns string, port *uint, stateUpdateInterva } backgroundCtx, cancel := context.WithCancel(ctx) - cache, err := newDNSCache(backgroundCtx, dns, true, false, stateUpdateInterval, shootClient, log.WithName("DNS cache")) + cache, err := newDNSCache(backgroundCtx, dns, true, false, fqdnStateSyncInterval, shootClient, log.WithName("DNS cache")) if err != nil { cancel() return nil, err From 795d73c5ff3338a55d749386f34ca817087169fa Mon Sep 17 00:00:00 2001 From: Markus Wennrich Date: Fri, 17 Apr 2026 08:42:07 +0200 Subject: [PATCH 6/6] make sure to start syncloop only after reading an already existing configmap --- main.go | 2 +- pkg/dns/dnscache.go | 23 ++++++++++------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/main.go b/main.go index 5ff0a070..8bb61141 100644 --- a/main.go +++ b/main.go @@ -74,7 +74,7 @@ func main() { flag.StringVar(&hostsFile, "hosts-file", "/etc/hosts", "The hosts file to manipulate for the droptailer.") flag.BoolVar(&enableSignatureCheck, "enable-signature-check", true, "Set this to false to ignore signature checking.") flag.StringVar(&firewallName, "firewall-name", "", "the name of the firewall resource in the seed cluster to reconcile (defaults to hostname)") - flag.DurationVar(&fqdnStateSyncInterval, "fqdnstate-sync-interval", 2*time.Second, "minimum interval between fqdn state configmap syncs") + flag.DurationVar(&fqdnStateSyncInterval, "fqdnstate-sync-interval", 10*time.Second, "minimum interval between fqdn state configmap syncs") if _, err := os.Stat(seedKubeconfigPath); err == nil || os.IsExist(err) { // controller-runtime registered this flag already, so we can use it diff --git a/pkg/dns/dnscache.go b/pkg/dns/dnscache.go index 9709996a..ab72c3f0 100644 --- a/pkg/dns/dnscache.go +++ b/pkg/dns/dnscache.go @@ -148,27 +148,24 @@ func newDNSCache(ctx context.Context, dns string, ipv4Enabled, ipv6Enabled bool, cancel() }() - c.startFQDNStateSyncLoop() - err := shootClient.Get(ctxWithTimeout, nn, scm) if err != nil && !apierrors.IsNotFound(err) { c.log.Error(err, "error reading fqndstate configmap") return nil, err } - if scm.Data == nil { + if apierrors.IsNotFound(err) || scm.Data == nil { c.log.V(4).Info("DEBUG fqdnstate cm not found or contains no data", "cm", scm) - return &c, nil - - } - if scm.Data[fqdnStateConfigmapKey] == "" { + } else if scm.Data[fqdnStateConfigmapKey] == "" { c.log.Error(fmt.Errorf("error reading fqdnstate configmap, ignoring content"), "fqdnstate configmap does not contain the right key", "configmap", scm, "key", fqdnStateConfigmapKey) - return &c, nil - } - c.log.V(4).Info("DEBUG state stored in fqdnstate cm, trying to unmarshal", fqdnStateConfigmapKey, scm.Data[fqdnStateConfigmapKey]) - err = yaml.UnmarshalStrict([]byte(scm.Data[fqdnStateConfigmapKey]), &c.fqdnToEntry) - if err != nil { - c.log.Info("could not unmarshal state from fqdnstate configmap, ignoring content.", "error", err) + } else { + c.log.V(4).Info("DEBUG state stored in fqdnstate cm, trying to unmarshal", fqdnStateConfigmapKey, scm.Data[fqdnStateConfigmapKey]) + err = yaml.UnmarshalStrict([]byte(scm.Data[fqdnStateConfigmapKey]), &c.fqdnToEntry) + if err != nil { + c.log.Info("could not unmarshal state from fqdnstate configmap, ignoring content.", "error", err) + } } + + c.startFQDNStateSyncLoop() return &c, nil }