@@ -185,54 +185,213 @@ func createACLTokenWithGivenPolicy(t *testing.T, consulClient *api.Client, polic
185185}
186186
187187func updateCoreDNSWithConsulDomain (t * testing.T , ctx environment.TestContext , releaseName string , enableDNSProxy bool , port string ) {
188- updateCoreDNSFile (t , ctx , releaseName , enableDNSProxy , port , "coredns-custom.yaml" )
189- updateCoreDNS (t , ctx , "coredns-custom.yaml" )
190-
191- t .Cleanup (func () {
192- updateCoreDNS (t , ctx , "coredns-original.yaml" )
193- time .Sleep (5 * time .Second )
194- })
188+ actualName := getCoreDNSConfigMapName (t , ctx )
189+
190+ // 1. BACKUP: Capture the current state
191+ logger .Log (t , "Backing up original CoreDNS config..." )
192+ originalConfig , err := k8s .RunKubectlAndGetOutputE (t , ctx .KubectlOptions (t ),
193+ "get" , "configmap" , actualName , "-n" , "kube-system" , "-o" , "yaml" )
194+ require .NoError (t , err )
195+
196+ // --- FIX: Sanitize the YAML to remove version locking ---
197+ // We remove fields that cause "Conflict" errors during restore.
198+ lines := strings .Split (originalConfig , "\n " )
199+ var sanitizedLines []string
200+ for _ , line := range lines {
201+ trimmed := strings .TrimSpace (line )
202+ // Skip metadata fields that tie the file to a specific point in time
203+ if strings .HasPrefix (trimmed , "resourceVersion:" ) ||
204+ strings .HasPrefix (trimmed , "uid:" ) ||
205+ strings .HasPrefix (trimmed , "creationTimestamp:" ) ||
206+ strings .HasPrefix (trimmed , "generation:" ) {
207+ continue
208+ }
209+ sanitizedLines = append (sanitizedLines , line )
210+ }
211+ cleanConfig := strings .Join (sanitizedLines , "\n " )
212+ // ---------------------------------------------------------
213+
214+ // Write the sanitized backup file
215+ err = os .WriteFile ("coredns-original.yaml" , []byte (cleanConfig ), 0644 )
216+ require .NoError (t , err )
217+
218+ // 2. GENERATE & APPLY Custom Config
219+ updateCoreDNSFile (t , ctx , releaseName , enableDNSProxy , port , "coredns-custom.yaml" )
220+ updateCoreDNS (t , ctx , "coredns-custom.yaml" )
221+
222+ // 3. CLEANUP: Restore the backup
223+ t .Cleanup (func () {
224+ logger .Log (t , "Restoring original CoreDNS configuration..." )
225+ updateCoreDNS (t , ctx , "coredns-original.yaml" )
226+ time .Sleep (5 * time .Second )
227+ })
195228}
196229
197230func updateCoreDNSFile (t * testing.T , ctx environment.TestContext , releaseName string ,
198- enableDNSProxy bool , port string , dnsFileName string ) {
199- dnsIP , err := getDNSServiceClusterIP (t , ctx , releaseName , enableDNSProxy )
200- require .NoError (t , err )
201-
202- dnsTarget := dnsIP
203- if enableDNSProxy {
204- dnsTarget = net .JoinHostPort (dnsIP , port )
205- }
206-
207- input , err := os .ReadFile ("coredns-template.yaml" )
208- require .NoError (t , err )
209- newContents := strings .Replace (string (input ), "{{CONSUL_DNS_IP}}" , dnsTarget , - 1 )
210- err = os .WriteFile (dnsFileName , []byte (newContents ), os .FileMode (0644 ))
211- require .NoError (t , err )
231+ enableDNSProxy bool , port string , dnsFileName string ) {
232+
233+ // Calculate target IP
234+ dnsIP , err := getDNSServiceClusterIP (t , ctx , releaseName , enableDNSProxy )
235+ require .NoError (t , err )
236+
237+ actualName := getCoreDNSConfigMapName (t , ctx )
238+
239+ dnsTarget := dnsIP
240+ if enableDNSProxy {
241+ dnsTarget = net .JoinHostPort (dnsIP , port )
242+ }
243+
244+ // --- STRATEGY A: GKE / Legacy (kube-dns) ---
245+ // GKE ignores Corefile changes or breaks if we overwrite it. We must use stubDomains.
246+ if strings .Contains (actualName , "kube-dns" ) {
247+ logger .Log (t , "Detected GKE/kube-dns. Using stubDomains strategy." , "target" , dnsTarget )
248+
249+ // stubDomains expects a JSON map: domain -> [ips]
250+ stubDomainJSON := fmt .Sprintf (`{"consul": ["%s"]}` , dnsTarget )
251+
252+ // We create a ConfigMap that ONLY updates the "stubDomains" key.
253+ // This merges safely with GKE's internal config.
254+ configMapYAML := fmt .Sprintf (`
255+ apiVersion: v1
256+ kind: ConfigMap
257+ metadata:
258+ name: %s
259+ namespace: kube-system
260+ data:
261+ stubDomains: |
262+ %s
263+ ` , actualName , stubDomainJSON )
264+
265+ err = os .WriteFile (dnsFileName , []byte (configMapYAML ), 0644 )
266+ require .NoError (t , err )
267+ return
268+ }
269+
270+ // --- STRATEGY B: EKS / AKS / Standard (coredns) ---
271+ // Standard clusters allow us to overwrite the Corefile.
272+ logger .Log (t , "Detected Standard CoreDNS. Using Corefile template strategy." , "target" , dnsTarget )
273+
274+ input , err := os .ReadFile ("coredns-template.yaml" )
275+ require .NoError (t , err )
276+
277+ newContents := strings .Replace (string (input ), "{{CONSUL_DNS_IP}}" , dnsTarget , - 1 )
278+ newContents = strings .ReplaceAll (newContents , "name: coredns" , fmt .Sprintf ("name: %s" , actualName ))
279+
280+ err = os .WriteFile (dnsFileName , []byte (newContents ), os .FileMode (0644 ))
281+ require .NoError (t , err )
212282}
213283
214284func updateCoreDNS (t * testing.T , ctx environment.TestContext , coreDNSConfigFile string ) {
215- coreDNSCommand := []string {
216- "replace" , "-n" , "kube-system" , "-f" , coreDNSConfigFile ,
217- }
218- var logs string
219-
220- timer := & retry.Timer {Timeout : 30 * time .Minute , Wait : 60 * time .Second }
221- retry .RunWith (timer , t , func (r * retry.R ) {
222- var err error
223- logs , err = k8s .RunKubectlAndGetOutputE (r , ctx .KubectlOptions (r ), coreDNSCommand ... )
224- require .NoError (r , err )
225- })
226-
227- require .Contains (t , logs , "configmap/coredns replaced" )
228- restartCoreDNSCommand := []string {"rollout" , "restart" , "deployment/coredns" , "-n" , "kube-system" }
229- _ , err := k8s .RunKubectlAndGetOutputE (t , ctx .KubectlOptions (t ), restartCoreDNSCommand ... )
230- require .NoError (t , err )
231- // Wait for restart to finish.
232- out , err := k8s .RunKubectlAndGetOutputE (t , ctx .KubectlOptions (t ), "rollout" , "status" , "--timeout" , "5m" , "--watch" , "deployment/coredns" , "-n" , "kube-system" )
233- require .NoError (t , err , out , "rollout status command errored, this likely means the rollout didn't complete in time" )
285+ actualName := getCoreDNSConfigMapName (t , ctx )
286+
287+ // --- STEP 0: PATCH THE FILE ---
288+ content , err := os .ReadFile (coreDNSConfigFile )
289+ require .NoError (t , err )
290+
291+ strContent := string (content )
292+ fileChanged := false
293+
294+ // Ensure name matches the cluster (kube-dns vs coredns)
295+ if actualName == "kube-dns" && strings .Contains (strContent , "name: coredns" ) {
296+ logger .Log (t , "Patching config file to target kube-dns instead of coredns" )
297+ strContent = strings .ReplaceAll (strContent , "name: coredns" , "name: kube-dns" )
298+ fileChanged = true
299+ } else if actualName == "coredns" && strings .Contains (strContent , "name: kube-dns" ) {
300+ logger .Log (t , "Patching config file to target coredns instead of kube-dns" )
301+ strContent = strings .ReplaceAll (strContent , "name: kube-dns" , "name: coredns" )
302+ fileChanged = true
303+ }
304+
305+ if fileChanged {
306+ err = os .WriteFile (coreDNSConfigFile , []byte (strContent ), 0644 )
307+ require .NoError (t , err )
308+ }
309+
310+ // --- STEP 1: APPLY ---
311+ coreDNSCommand := []string {
312+ "apply" , "-n" , "kube-system" , "-f" , coreDNSConfigFile ,
313+ }
314+ var logs string
315+
316+ timer := & retry.Timer {Timeout : 30 * time .Minute , Wait : 60 * time .Second }
317+ retry .RunWith (timer , t , func (r * retry.R ) {
318+ var err error
319+ logs , err = k8s .RunKubectlAndGetOutputE (r , ctx .KubectlOptions (r ), coreDNSCommand ... )
320+ require .NoError (r , err )
321+ })
322+
323+ // --- STEP 2: VALIDATE OUTPUT ---
324+ msgConfigured := fmt .Sprintf ("configmap/%s configured" , actualName )
325+ msgReplaced := fmt .Sprintf ("configmap/%s replaced" , actualName )
326+ msgUnchanged := fmt .Sprintf ("configmap/%s unchanged" , actualName )
327+
328+ require .True (t ,
329+ strings .Contains (logs , msgConfigured ) || strings .Contains (logs , msgReplaced ) || strings .Contains (logs , msgUnchanged ),
330+ "expected CoreDNS update output to contain '%s', '%s', or '%s' but got: \n %s" ,
331+ msgConfigured , msgReplaced , msgUnchanged , logs )
332+
333+ // --- STEP 3: RESTART DEPLOYMENT ---
334+ deploymentName := "deployment/coredns"
335+ if strings .Contains (actualName , "kube-dns" ) {
336+ deploymentName = "deployment/kube-dns"
337+ }
338+
339+ logger .Log (t , "Restarting DNS deployment" , "name" , deploymentName )
340+
341+ restartCoreDNSCommand := []string {"rollout" , "restart" , deploymentName , "-n" , "kube-system" }
342+ _ , err = k8s .RunKubectlAndGetOutputE (t , ctx .KubectlOptions (t ), restartCoreDNSCommand ... )
343+ require .NoError (t , err )
344+
345+ // --- STEP 4: WAIT FOR ROLLOUT ---
346+ out , err := k8s .RunKubectlAndGetOutputE (t , ctx .KubectlOptions (t ), "rollout" , "status" , "--timeout" , "5m" , "--watch" , deploymentName , "-n" , "kube-system" )
347+ require .NoError (t , err , out , "rollout status command errored, this likely means the rollout didn't complete in time" )
234348}
235349
350+ func getCoreDNSConfigMapName (t * testing.T , ctx environment.TestContext ) string {
351+ client := ctx .KubernetesClient (t ).CoreV1 ().ConfigMaps ("kube-system" )
352+ ctxBg := context .Background ()
353+
354+ // 1. Check Labels (Standard method)
355+ labelSelectors := []string {
356+ "k8s-app=coredns" ,
357+ "app.kubernetes.io/name=coredns" ,
358+ "k8s-app=kube-dns" ,
359+ }
360+
361+ for _ , label := range labelSelectors {
362+ cms , err := client .List (ctxBg , metav1.ListOptions {LabelSelector : label })
363+ if err == nil && len (cms .Items ) > 0 {
364+ for _ , cm := range cms .Items {
365+ if _ , ok := cm .Data ["Corefile" ]; ok {
366+ logger .Log (t , "found DNS configmap via label with Corefile" , "label" , label , "name" , cm .Name )
367+ return cm .Name
368+ }
369+ }
370+ }
371+ }
372+
373+ // 2. Check Known Names (Fallback for GKE)
374+ knownNames := []string {"coredns" , "kube-dns" }
375+ for _ , name := range knownNames {
376+ cm , err := client .Get (ctxBg , name , metav1.GetOptions {})
377+ if err == nil {
378+ logger .Log (t , "found DNS configmap via name" , "name" , cm .Name )
379+ return cm .Name
380+ }
381+ }
382+
383+ // Debugging failure
384+ cms , err := client .List (ctxBg , metav1.ListOptions {})
385+ var observedNames []string
386+ if err == nil {
387+ for _ , cm := range cms .Items {
388+ observedNames = append (observedNames , cm .Name )
389+ }
390+ }
391+ t .Fatalf ("Failed to find CoreDNS ConfigMap. Checked labels: %v, Checked names: %v. Available: %v" ,
392+ labelSelectors , knownNames , observedNames )
393+ return ""
394+ }
236395func verifyDNS (
237396 t * testing.T ,
238397 cfg * config.TestConfig ,
0 commit comments