<p>I have been working with one <a href="https://kubectl.docs.kubernetes.io/references/kustomize/builtins/#_patchesjson6902_" rel="nofollow noreferrer"><code>patchesJson6902</code></a> 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).</p>
<p>I have a <code>base/04-public-letsencrypt-ingress/kustomization.yaml</code></p>
<pre class="lang-yaml prettyprint-override"><code>…
patchesJson6902:
- target: { group: networking.k8s.io, version: v1, kind: Ingress, name: ingress-frontend }
  path: ingress-letsencrypt.patch.yaml
</code></pre>
<p>and a <code>overlay/04-public-letsencrypt-ingress/kustomization.yaml</code></p>
<pre class="lang-yaml prettyprint-override"><code>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
</code></pre>
<p>This does not work as I expected, it seems only one of the <code>patchesJson6902</code> definitions becomes effective.</p>
<p>Is this the expected behavior? Is there a good way to split patches; maybe is there an alternative to <code>patchesJson6902</code> that can also works when defined in multiple places?</p>

Christian Fuchs
<p>First: the <code>patchesJson6902</code> directive has been deprecated; you should simply be using <code>patches</code>.</p>
<p>With respect to your question, when you include a base kustomization in the <code>resources</code> section of your <code>kustomization.yaml</code>, kustomize neither knows or cares that the base used patches: the patches in your local <code>kustomization.yaml</code> are simply applied to whatever manifests are generated by your <code>resources</code> section (and config/secret generators, etc).</p>
<p>So for example if we have this layout:</p>
<pre><code>.
├── base
│   ├── ingress.yaml
│   ├── kustomization.yaml
│   └── set-ingressclass-patch.yaml
└── overlay
    ├── kustomization.yaml
    └── set-ingress-tls-patch.yaml
</code></pre>
<p>And the following files:</p>
<ul>
<li><p><code>base/ingress.yaml</code>:</p>
<pre><code>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
</code></pre>
</li>
<li><p><code>base/set-ingressclass-patch.yaml</code>:</p>
<pre><code>- path: /metadata/annotations
  op: add
  value:
    kubernetes.io/ingress.class: nginx
</code></pre>
</li>
<li><p><code>base/kustomization.yaml</code>:</p>
<pre><code>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
</code></pre>
</li>
<li><p><code>overlay/set-ingress-tls-patch.yaml</code>:</p>
<pre><code>- 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
</code></pre>
</li>
<li><p><code>overlay/kustomization.yaml</code>:</p>
<pre><code>apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../base

patches:
  - target:
      kind: Ingress
      name: example
    path: set-ingress-tls-patch.yaml
</code></pre>
</li>
</ul>
<p>Then running <code>kustomize build base</code> produces:</p>
<pre><code>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
</code></pre>
<p>And running <code>kustomize build overlay</code> produces:</p>
<pre><code>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
</code></pre>
<p>Here we can see that the output of the second <code>kustomize build</code> commands takes the manifests produced by <code>base</code> -- which include the patch that sets the ingress class -- and applies the patches in <code>overlay/set-ingress-tls-patch.yaml</code>.</p>
<hr />
<p>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.</p>
<p>The manifest <code>base/ingress.yaml</code> doesn't have an <code>metadata.annotations</code> section. Our patch...</p>
<pre><code>- path: /metadata/annotations
  op: add
  value:
    kubernetes.io/ingress.class: nginx
</code></pre>
<p>...creates the <code>annotations</code> key and content. But when we apply patches in the overlay, the Ingress resource already has an <code>annotations</code> section. If we had written <code>overlay/set-ingress-tls-patch.yaml</code> like this:</p>
<pre><code>- path: /metadata/annotations
  op: add
  value:
    cert-manager.io/cluster-issuer: my_issuer
</code></pre>
<p>That would have <em>replaced</em> the <code>annotations</code> key with a new value! The resulting manifest would look like:</p>
<pre><code>apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: my_issuer
  labels:
    app: example
  name: example
spec:
  ...
</code></pre>
<p>But we don't want to <em>replace</em> the <code>annotations</code> section; we want to add a new key. So instead our patch looks like:</p>
<pre><code>- path: /metadata/annotations/cert-manager.io~1cluster-issuer
  op: add
  value: my_issuer
</code></pre>
<p>This is adding a new key under <code>annotations</code>, rather than replacing the entire <code>annotations</code> key. The pattern <code>~1</code> is how you <a href="https://github.com/json-patch/json-patch-tests/issues/42" rel="nofollow noreferrer">escape a forward slash in a JSON pointer expression</a>.</p>
<hr />
<p>The above all works, but I using strategic merge patches would be simpler and more obvious. In that case, <code>base/set-ingressclass-patch.yaml</code> would look like:</p>
<pre><code>apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
  name: example
</code></pre>
<p>And <code>base/set-ingress-tls-patch.yaml</code> would look like:</p>
<pre><code>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
</code></pre>
<p>Using this style of patch, you don't need to set a <code>target</code> in your <code>kustomization.yaml</code> because the target is implied by the <code>kind</code> and <code>metadata.name</code> in the patch. E.g., <code>base/kustomization.yaml</code> would look like:</p>
<pre><code>apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
  app: example

resources:
- ingress.yaml

patches:
  - path: set-ingressclass-patch.yaml
</code></pre>
<p>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.</p>
