0

I have been working with one patchesJson6902 clause in my Kubernetes kustomize configuration; now I want to use overlays (to support different instances) and I want to split the patches (some patches are generic, i.e. for enabling let's-encrypt instead of self-signed for cert-manager for some instances; other patches are specific, i.e. for distinct certificate hostname per instance).

I have a base/04-public-letsencrypt-ingress/kustomization.yaml

…
patchesJson6902:
- target: { group: networking.k8s.io, version: v1, kind: Ingress, name: ingress-frontend }
  path: ingress-letsencrypt.patch.yaml

and a overlay/04-public-letsencrypt-ingress/kustomization.yaml

bases:
- ../../base/04-public-letsencrypt-ingress/

patchesJson6902:
- target: { group: networking.k8s.io, version: v1, kind: Ingress, name: ingress-frontend }
  path: ingress-tls-hostname.patch.yaml

This does not work as I expected, it seems only one of the patchesJson6902 definitions becomes effective.

Is this the expected behavior? Is there a good way to split patches; maybe is there an alternative to patchesJson6902 that can also works when defined in multiple places?

Christian Fuchs
  • 430
  • 3
  • 9

1 Answers1

1

First: the patchesJson6902 directive has been deprecated; you should simply be using patches.

With respect to your question, when you include a base kustomization in the resources section of your kustomization.yaml, kustomize neither knows or cares that the base used patches: the patches in your local kustomization.yaml are simply applied to whatever manifests are generated by your resources section (and config/secret generators, etc).

So for example if we have this layout:

.
├── base
│   ├── ingress.yaml
│   ├── kustomization.yaml
│   └── set-ingressclass-patch.yaml
└── overlay
    ├── kustomization.yaml
    └── set-ingress-tls-patch.yaml

And the following files:

  • base/ingress.yaml:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: example
    spec:
      rules:
      - host: www.example.com
        http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: example
                port:
                  name: http
    
  • base/set-ingressclass-patch.yaml:

    - path: /metadata/annotations
      op: add
      value:
        kubernetes.io/ingress.class: nginx
    
  • base/kustomization.yaml:

    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    commonLabels:
      app: example
    
    resources:
    - ingress.yaml
    
    patches:
      - target:
          kind: Ingress
          name: example
        path: set-ingressclass-patch.yaml
    
  • overlay/set-ingress-tls-patch.yaml:

    - path: /metadata/annotations/cert-manager.io~1cluster-issuer
      op: add
      value: my_issuer
    - path: /spec/tls
      op: add
      value:
        - hosts:
          - www.example.com
          secretName: example-cert
    
  • overlay/kustomization.yaml:

    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    
    resources:
      - ../base
    
    patches:
      - target:
          kind: Ingress
          name: example
        path: set-ingress-tls-patch.yaml
    

Then running kustomize build base produces:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
  labels:
    app: example
  name: example
spec:
  rules:
  - host: www.example.com
    http:
      paths:
      - backend:
          service:
            name: example
            port:
              name: http
        path: /
        pathType: Prefix

And running kustomize build overlay produces:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: my_issuer
    kubernetes.io/ingress.class: nginx
  labels:
    app: example
  name: example
spec:
  rules:
  - host: www.example.com
    http:
      paths:
      - backend:
          service:
            name: example
            port:
              name: http
        path: /
        pathType: Prefix
  tls:
  - hosts:
    - www.example.com
    secretName: example-cert

Here we can see that the output of the second kustomize build commands takes the manifests produced by base -- which include the patch that sets the ingress class -- and applies the patches in overlay/set-ingress-tls-patch.yaml.


You didn't show examples of your patches, so it's hard to tell what's actually going on, but I have a theory. Let's take a closer look at the patches in this example.

The manifest base/ingress.yaml doesn't have an metadata.annotations section. Our patch...

- path: /metadata/annotations
  op: add
  value:
    kubernetes.io/ingress.class: nginx

...creates the annotations key and content. But when we apply patches in the overlay, the Ingress resource already has an annotations section. If we had written overlay/set-ingress-tls-patch.yaml like this:

- path: /metadata/annotations
  op: add
  value:
    cert-manager.io/cluster-issuer: my_issuer

That would have replaced the annotations key with a new value! The resulting manifest would look like:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: my_issuer
  labels:
    app: example
  name: example
spec:
  ...

But we don't want to replace the annotations section; we want to add a new key. So instead our patch looks like:

- path: /metadata/annotations/cert-manager.io~1cluster-issuer
  op: add
  value: my_issuer

This is adding a new key under annotations, rather than replacing the entire annotations key. The pattern ~1 is how you escape a forward slash in a JSON pointer expression.


The above all works, but I using strategic merge patches would be simpler and more obvious. In that case, base/set-ingressclass-patch.yaml would look like:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
  name: example

And base/set-ingress-tls-patch.yaml would look like:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: my_issuer
  name: example
spec:
  tls:
  - hosts:
    - www.example.com
    secretName: example-cert

Using this style of patch, you don't need to set a target in your kustomization.yaml because the target is implied by the kind and metadata.name in the patch. E.g., base/kustomization.yaml would look like:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
  app: example

resources:
- ingress.yaml

patches:
  - path: set-ingressclass-patch.yaml

I think that's easier to understand than the JSONPatch style patches, but that's a matter of opinion and as we've demonstrated here either will work.

larsks
  • 277,717
  • 41
  • 399
  • 399