{"record":{"id":"69d2c3632d5ffa8d","repo":"kubernetes/kops","slug":"expected-exactly-one-container-in-dns-controller-d","errorCode":null,"errorMessage":"expected exactly one container in dns-controller Deployment, found %d","messagePattern":"expected exactly one container in dns-controller Deployment, found (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/model/components/addonmanifests/dnscontroller/remap.go","lineNumber":57,"sourceCode":"\t\t}\n\t\tif object.APIVersion() != \"apps/v1\" {\n\t\t\tcontinue\n\t\t}\n\t\tdeployments = append(deployments, object)\n\t}\n\n\tif len(deployments) != 1 {\n\t\treturn fmt.Errorf(\"expected exactly one Deployment in dns-controller manifest, found %d\", len(deployments))\n\t}\n\n\tpodSpec := &corev1.PodSpec{}\n\tif err := deployments[0].Reparse(podSpec, \"spec\", \"template\", \"spec\"); err != nil {\n\t\treturn fmt.Errorf(\"failed to parse spec.template.spec from Deployment: %v\", err)\n\t}\n\n\tcontainers := podSpec.Containers\n\tif len(containers) != 1 {\n\t\treturn fmt.Errorf(\"expected exactly one container in dns-controller Deployment, found %d\", len(containers))\n\t}\n\n\tif err := iam.AddServiceAccountRole(&context.IAMModelContext, podSpec, &ServiceAccount{}); err != nil {\n\t\treturn err\n\t}\n\n\tif err := deployments[0].Set(podSpec, \"spec\", \"template\", \"spec\"); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":39,"sourceCodeEnd":70,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/model/components/addonmanifests/dnscontroller/remap.go#L39-L70","documentation":"The dns-controller addon remapper (dnscontroller.Remap) locates the single apps/v1 Deployment in the dns-controller manifest, parses its pod spec, and injects the IAM service-account role. It hard-codes the assumption that the Deployment runs exactly one container; if the parsed podSpec.Containers slice has any other length, the function aborts because per-container patches (volume mounts, env, annotation) would otherwise be ambiguous.","triggerScenarios":"Calling RemapAddonManifest for the dns-controller.addons.k8s.io addon (with UseServiceAccountExternalPermissions enabled) where the manifest's spec.template.spec.containers list has 0 or >=2 entries — e.g. a hand-edited or newer upstream manifest added a sidecar container.","commonSituations":"Users overriding the dns-controller manifest via an older/newer addon channel version whose Deployment gained a sidecar (e.g. proxy or metrics container); custom manifests that omitted the main container; typo causing the container list to fail parsing into the expected single container.","solutions":["Restore the stock dns-controller manifest so its Deployment pod template has exactly one container","If a sidecar is required, restructure it (e.g. separate Deployment) or update kops so the remapper tolerates extra containers","Check the addon channel version matches the kops version; downgrade/upgrade the addon channel to a compatible release","Verify the Deployment is apps/v1 and the containers live at spec.template.spec.containers"],"exampleFix":"# before (dns-controller.yaml, pod template)\ncontainers:\n  - name: dns-controller\n    image: ...\n  - name: sidecar\n    image: ...\n# after\ncontainers:\n  - name: dns-controller\n    image: ...","handlingStrategy":"validation","validationCode":"// Before applying a custom dns-controller manifest, check container count:\nvar podSpec corev1.PodSpec\nif err := obj.Reparse(&podSpec, \"spec\", \"template\", \"spec\"); err != nil { return err }\nif len(podSpec.Containers) != 1 {\n    return fmt.Errorf(\"dns-controller Deployment must have exactly 1 container, found %d\", len(podSpec.Containers))\n}","typeGuard":"func hasExactlyOneContainer(podSpec *corev1.PodSpec) bool {\n    return podSpec != nil && len(podSpec.Containers) == 1\n}","tryCatchPattern":"if err := dnscontroller.Remap(ctx, addon, objects); err != nil {\n    if strings.Contains(err.Error(), \"expected exactly one container\") {\n        // fall back to stock manifest or strip sidecar containers\n    }\n    return err\n}","preventionTips":["Never add sidecar containers to the dns-controller Deployment when using IRSA in kops","Pin the addon channel version to one matching your kops version","Diff custom manifests against the stock channel manifest before applying"],"tags":["kops","addon","dns-controller","deployment","manifest"],"backgroundTag":"addon-manifest-structure-mismatch","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}