Skip to content

Support appending to arrays without merge keys (envFrom, env, volumeMounts, etc.) #6076

@rhiremani

Description

@rhiremani

Eschewed features

  • This issue is not requesting templating, unstuctured edits, build-time side-effects from args or env vars, or any other eschewed feature.

What would you like to have added?

Add support for appending to arrays that may not exist, particularly for fields like envFrom, env, volumeMounts, and other arrays without patchMergeKey annotations.

Proposed solution: Add a createParent option to JSON Patch operations:

patches:
  - target:
      kind: Deployment
    patch: |-
      - op: add
        path: "/spec/template/spec/containers/0/envFrom/-"
        createParent: true  # Create array if it doesn't exist
        value:
          configMapRef:
            name: my-config

This would:

  • Create the parent array if it doesn't exist
  • Append to the array if it already exists
  • Allow generic patches without knowing the current state

Why is this needed?

Current Problem

When using kustomize overlays, I need to add common ConfigMapRefs to multiple deployments' envFrom arrays. However:

  1. Using op: add with path ending in - (append):

    • ✅ Works IF the array exists
    • ❌ FAILS with "missing path" if the array doesn't exist
  2. Using op: add without - (replace):

    • ✅ Creates the array if it doesn't exist
    • ❌ But REPLACES existing values (loses helm chart configs, base values)
  3. Using strategic merge:

    • ❌ Doesn't work because envFrom has no patchMergeKey
    • ❌ Strategic merge replaces the entire array instead of merging

Current Workarounds (all suboptimal)

Workaround 1: Multiple targeted patches

One generic patch to create baseline, then specific patches for each deployment variant:

patches:
  # Generic for all
  - target:
      kind: (Deployment|StatefulSet)
    patch: |-
      - op: add
        path: "/spec/template/spec/containers/0/envFrom"
        value:
        - configMapRef:
            name: common-config

  # Specific for deployment with existing envFrom
  - target:
      name: my-special-deployment
    patch: |-
      - op: add
        path: "/spec/template/spec/containers/0/envFrom/0"
        value:
          configMapRef:
            name: special-config

Problem: Not scalable, requires knowing all deployment names in advance

Workaround 2: Include all configMaps everywhere

- op: add
  path: "/spec/template/spec/containers/0/envFrom"
  value:
  - configMapRef:
      name: special-deployment-config  # Only exists for 1 deployment
  - configMapRef:
      name: common-config

Problem: All deployments reference configMaps they don't use (wasteful, confusing)

Workaround 3: Post-process with yq/jq

kustomize build . | yq eval '... append logic ...' | kubectl apply -f -

Problem: Defeats the purpose of declarative kustomize, adds external dependencies

Real-world Use Case

I'm deploying microservices with:

  • Base deployments (some with envFrom, some without)
  • Helm charts that add their own configMapRefs (e.g., LaunchDarkly relay with test-ld-relay-config)
  • Overlays that need to add common environment configs (cluster config, base config)

I need a generic way to append configMapRefs without:

  • Knowing which deployments already have envFrom
  • Maintaining deployment-specific patches
  • Losing existing configMapRefs from base/helm

Example Scenario

Base (Helm Chart):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-ld-relay
spec:
  template:
    spec:
      containers:
      - name: ld-relay
        envFrom:
        - configMapRef:
            name: test-ld-relay-config  # From helm chart

Other Deployment (no envFrom):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-global-api
spec:
  template:
    spec:
      containers:
      - name: api
        # No envFrom field at all

Desired Overlay Result:

# test-ld-relay should have:
envFrom:
  - configMapRef:
      name: test-ld-relay-config  # Preserved from helm
  - configMapRef:
      name: test-base-config      # Added by overlay
  - configMapRef:
      name: test-cluster-config   # Added by overlay

# test-global-api should have:
envFrom:
  - configMapRef:
      name: test-base-config      # Added by overlay
  - configMapRef:
      name: test-cluster-config   # Added by overlay

What I want to write:

patches:
  - target:
      kind: (Deployment|StatefulSet)
    patch: |-
      - op: add
        path: "/spec/template/spec/containers/0/envFrom/-"
        createParent: true  # <-- THE FEATURE REQUEST
        value:
          configMapRef:
            name: test-base-config
      - op: add
        path: "/spec/template/spec/containers/0/envFrom/-"
        createParent: true  # <-- THE FEATURE REQUEST
        value:
          configMapRef:
            name: test-cluster-config

Can you accomplish the motivating task without this feature, and if so, how?

Files Demonstrating the Issue

Current workaround in our repository:

File: deployments/overlays/test-dev-global-kind/kustomization.yaml

patches:
  # Add common configMaps to all deployments/statefulsets
  - target:
      kind: (Deployment|StatefulSet)
    patch: |-
      - op: add
        path: "/spec/template/spec/containers/0/envFrom"
        value:
        - configMapRef:
            name: test-base-config
        - configMapRef:
            name: test-cluster-config

  # Add test-ld-relay-config specifically to test-ld-relay deployment
  - target:
      kind: Deployment
      name: test-ld-relay
    patch: |-
      - op: add
        path: "/spec/template/spec/containers/0/envFrom/0"
        value:
          configMapRef:
            name: test-ld-relay-config

This requires knowing that test-ld-relay is special and maintaining a targeted patch for it.

What other solutions have you considered?

Alternative 1: Change Kubernetes API

Add patchMergeKey to envFrom in the core Kubernetes API. This would allow strategic merge to work.

Why not ideal:

  • Requires KEP process, API changes, affects all Kubernetes users
  • Takes years to implement and rollout
  • Kustomize could solve this independently

Alternative 2: Use Helm instead of Kustomize

Helm has better merge capabilities for overlays.

Why not ideal:

  • Kustomize is preferred for our GitOps workflow
  • Shouldn't need to switch tools for this common use case

Alternative 3: Custom KRM function/transformer

Write custom code to handle the merge logic.

Why not ideal:

  • Adds complexity and maintenance burden
  • Requires Go programming knowledge
  • Should be a built-in kustomize feature

Alternative 4: Two-step patching (current approach)

First patch creates array, second patch prepends exceptions.

Why not ideal:

  • Requires maintaining list of all exception deployments
  • Doesn't scale with dynamic resources
  • Violates DRY principle

Anything else we should know?

This feature would significantly improve kustomize's usability for real-world scenarios involving:

  • Helm charts with kustomize overlays
  • Microservices with common configurations
  • Dynamic resource sets where you can't hardcode all resource names

The createParent option for JSON Patch would be a clean, backwards-compatible solution that empowers users to write generic, maintainable patches.

Feature ownership

  • I am interested in contributing this feature myself! 🎉

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions