Skip to content

Kubebuilder generates subtly incorrect kustomize scaffolding for webhooks #5244

@mdbooth

Description

@mdbooth

What broke? What's expected?

The scaffolding produces the following kustomizeconfig.yaml:

# the following config is for teaching kustomize where to look at when substituting nameReference.
# It requires kustomize v2.1.0 or newer to work properly.
nameReference:
- kind: Service
  version: v1
  fieldSpecs:
  - kind: MutatingWebhookConfiguration
    group: admissionregistration.k8s.io
    path: webhooks/clientConfig/service/name
  - kind: ValidatingWebhookConfiguration
    group: admissionregistration.k8s.io
    path: webhooks/clientConfig/service/name

namespace:
- kind: MutatingWebhookConfiguration
  group: admissionregistration.k8s.io
  path: webhooks/clientConfig/service/namespace
  create: true
- kind: ValidatingWebhookConfiguration
  group: admissionregistration.k8s.io
  path: webhooks/clientConfig/service/namespace
  create: true

In this configuration, nameReference is redundant, and namespace is incorrect. This configuration can be safely removed entirely. This will:

  • Have no effect when building config/default directly, because it is redundant
  • Fix the bug described below when using this kustomize as a base.

The nameReference is redundant because it's already present in kustomize's default configuration:

https://github.com/kubernetes-sigs/kustomize/blob/77b3446b360e9033c7b94cf86e3e079fbdf3e0a9/api/internal/konfig/builtinpluginconsts/namereference.go#L297-L320

- kind: Service
  version: v1
  fieldSpecs:
  - path: spec/serviceName
    kind: StatefulSet
    group: apps
  - path: spec/rules/http/paths/backend/serviceName
    kind: Ingress
  - path: spec/backend/serviceName
    kind: Ingress
  - path: spec/rules/http/paths/backend/service/name
    kind: Ingress
  - path: spec/defaultBackend/service/name
    kind: Ingress
  - path: spec/service/name
    kind: APIService
    group: apiregistration.k8s.io
  - path: webhooks/clientConfig/service
    kind: ValidatingWebhookConfiguration
    group: admissionregistration.k8s.io
  - path: webhooks/clientConfig/service
    kind: MutatingWebhookConfiguration
    group: admissionregistration.k8s.io

This is doing nothing and can just be removed.

With the caveat that I am not a kustomize developer, just some rando who spent way too many hours trying to work out what was going on in a complex unfamiliar codebase:

The namespace directive is subtly wrong. The issue here is that the purpose of the namespace transformer is to directly set the namespace of objects, not to update the namespace in references to those objects. References are defined by the nameReference configuration. One of the last things kustomize will do in its pipeline is FixBackReferences(), which fixes up references to objects which have been modified by a transformer. The problem is that because we configured the namespace transformer to write directly to the reference, the internal state of this tracking is now wrong. This means (IIUC) that webhooks/clientConfig/service/namespace is updated by the namespace transformer, not by FixBackReferences which would have updated it anyway because we modified the Service object it

But the end result is the same, so why does this matter?

If you use this kustomization as a base for an overlay that performs a second transformation on the Service object, because the internal state is now incorrect the reference will not be updated.

I have submitted the following PRs related to this issue in projects which are based on kubebuilder scaffolding from a variety of versions over many years:

Reproducing this issue

Build kubebuilder from source. I used commit c169977, but given the number of CAPI providers I just posted PRs for this issue has been around for years.

$ ../bin/kubebuilder init --domain example.com --repo github.com/example/test
$ ../bin/kubebuilder create api --group foo --version v1 --kind Foo
$ ../bin/kubebuilder create webhook --group foo --version v1 --kind Foo --defaulting

Note that config/webhook contains the offending kustomiseconfig.yaml.

An overlay which produces incorrect output due to this:

Create the following kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- /path/to/any/kubebuilder/config/default

transformers:
- |
  apiVersion: builtin
  kind: NamespaceTransformer
  metadata:
    name: set-namespaces
    namespace: example-new-namespace

This is invoking the namespace transformer as an external transformer for... reasons. This is sometimes useful. Incidentally this does not manifest when invoking the namespace transformer via the builtin namespace directive (although it is the same transformer in both cases). I didn't get all the way to the bottom of this, but it appears to relate to a difference on ordering of how they are applied.

Inspect the output of kustomize build. Note that while the Service object's namespace has been correctly updated, the reference to it in MutatingWebhookConfiguration has not: it is still whatever it was directly transformed to by the builtin namespace directive.

Now remove the offending kustomizeconfig.yaml. You can just comment it out in config/webhook/kustomization.yaml. Note that the output is now correct. It is correct:

  • When building config/default directly - i.e. the configuration was redundant in any case
  • When transforming the Service object a second time in an overlay

KubeBuilder (CLI) Version

Version: cmd.version{KubeBuilderVersion:"v4.10.1-20-gc16997712", KubernetesVendor:"1.34.1", GitCommit:"c1699771232235a0890c270957e1a866b033aabe", BuildDate:"2025-11-21T13:56:26Z", GoOs:"linux", GoArch:"amd64"}

PROJECT version

3, 2

Plugin versions

Other versions

No response

Extra Labels

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugCategorizes issue or PR as related to a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions