Field Merge Semantics

Dealing with Field Merges

Merging Fields

When are fields merged?

This page describes how Resource Config is merged with Resources or other Resource Config. This may occur when:

  • Applying Resource Config updates to the live Resources in the cluster
  • Defining Patches in the kustomization.yaml which are overlayed on resources and bases

Applying Resource Config Updates

Rather than replacing the Resource with the new Resource Config, Apply will merge the new Resource Config into the live Resource. This retains values which may be set by the control plane - such as replicas values set by auto scalers

Defining Patches

patches are sparse Resource Config which contain a subset of fields that override values defined in other Resource Config with the same Group/Version/Kind/Namespace/Name. This is used to alter values defined on Resource Config without having to fork it.

Motivation (Apply)

This page describes the semantics for merging Resource Config.

Ownership of Resource fields are shared between declarative Resource Config authored by human users, and values set by Controllers running in the cluster. Some fields, such as the status and clusterIp fields, are owned exclusively by Controllers. Fields, such as the name and namespace fields, are owned exclusively by the human user managing the Resource.

Other fields, such as replicas, may be owned by either human users, the apiserver or Controllers. For example, replicas may be explicitly set by a user, implicitly set to a default value by the apiserver, or continuously adjusted by a Controller such as and HorizontalPodAutoscaler.

Last Applied Resource Config

When Apply creates or updates a Resource, it writes the Resource Config it Applied to an annotation on the Resource. This allows it to compare the last Resource Config it Applied to the current Resource Config and identify fields that have been deleted.

# deployment.yaml (Resource Config)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
# Original Resource
Doesn't Exist
# Applied Resource
kind: Deployment
metadata:
  annotations:
    # ...
    # This is the deployment.yaml Resource Config written as an annotation on the object
    # It was written by kubectl apply when the object was created
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.7.9","name":"nginx"}]}}}}      
  # ...
spec:
  # ...
status:
  # ...

Merging Resources

Following are the merge semantics for Resources:

Adding Fields:

  • Fields present in the Resource Config that are missing from the Resource will be added to the Resource.
  • Fields will be added to the Last Applied Resource Config
# deployment.yaml (Resource Config)
apiVersion: apps/v1
kind: Deployment
metadata:
  # ...
  name: nginx-deployment
spec:
  # ...
  minReadySeconds: 3
# Original Resource
kind: Deployment
metadata:
  # ...
  name: nginx-deployment
spec:
  # ...
status:
  # ...
# Applied Resource
kind: Deployment
metadata:
  # ...
  name: nginx-deployment
spec:
  # ...
  minReadySeconds: 3
status:
  # ...

Updating Fields

  • Fields present in the Resource Config that are also present in the Resource will be merged recursively until a primitive field is updated, or a field is added / deleted.
  • Fields will be updated in the Last Applied Resource Config
# deployment.yaml (Resource Config)
apiVersion: apps/v1
kind: Deployment
metadata:
  # ...
  name: nginx-deployment
spec:
  # ...
  replicas: 2
# Original Resource
kind: Deployment
metadata:
  # ...
  name: nginx-deployment
spec:
  # ...
  # could be defaulted or set by Resource Config
  replicas: 1
status:
  # ...
# Applied Resource
kind: Deployment
metadata:
  # ...
  name: nginx-deployment
spec:
  # ...
  # updated
  replicas: 2
status:
  # ...

Deleting Fields

  • Fields present in the Last Applied Resource Config that have been removed from the Resource Config will be deleted from the Resource.
  • Fields set to null in the Resource Config that are present in the Resource Config will be deleted from the Resource.
  • Fields will be removed from the Last Applied Resource Config
# deployment.yaml (Resource Config)
apiVersion: apps/v1
kind: Deployment
metadata:
  # ...
  name: nginx-deployment
spec:
  # ...
# Original Resource
kind: Deployment
metadata:
  # ...
  name: nginx-deployment
  # Containers replicas and minReadySeconds
  kubectl.kubernetes.io/last-applied-configuration: |
            {"apiVersion":"apps/v1","kind":"Deployment", "spec":{"replicas": "2", "minReadySeconds": "3", ...}, "metadata": {...}}
spec:
  # ...
  minReadySeconds: 3
  replicas: 2
status:
  # ...
# Applied Resource
kind: Deployment
metadata:
  # ...
  name: nginx-deployment
  kubectl.kubernetes.io/last-applied-configuration: |
            {"apiVersion":"apps/v1","kind":"Deployment", "spec":{...}, "metadata": {...}}
spec:
  # ...
  # deleted and then defaulted, but not in Last Applied
  replicas: 1
  # minReadySeconds deleted
status:
  # ...

Field Merge Semantics

Merging Primitives

Primitive fields are merged by replacing the current value with the new value.

Field Creation: Add the primitive field

Field Update: Change the primitive field value

Field Deletion: Delete the primitive field

Field in Resource Config Field in Resource Field in Last Applied Action
Yes Yes - Set live to the Resource Config value.
Yes No - Set live to the Resource Config value.
No - Yes Remove from Resource.
No - No Do nothing.

Merging Objects

Objects fields are updated by merging the sub-fields recursively (by field name) until a primitive field is found or the field is added / deleted.

Field Creation: Add the object field

Field Update: Recursively compare object sub-field values and merge them

Field Deletion: Delete the object field

Merge Table: For each field merge Resource Config and Resource values with the same name

Field in Resource Config Field in Resource Field in Last Applied Action
Yes Yes - Recursively merge the Resource Config and Resource values.
Yes No - Set live to the Resource Config value.
No - Yes Remove field from Resource.
No - No Do nothing.

Merging Maps

Map fields are updated by merging the elements (by key) until a primitive field is found or the value is added / deleted.

Field Creation: Add the map field

Field Update: Recursively compare map values by key and merge them

Field Deletion: Delete the map field

Merge Table: For each map element merge Resource Config and Resource values with the same key

Key in Resource Config Key in Resource Key in Last Applied Action
Yes Yes - Recursively merge the Resource Config and Resource values.
Yes No - Set live to the Resource Config value.
No - Yes Remove map element from Resource.
No - No Do nothing.

Merging Lists of Primitives

Lists of primitives will be merged if they have a patch strategy: merge on the field otherwise they will be replaced. Finalizer list example

Merge Strategy:

  • Merged primitive lists behave like ordered sets
  • Replace primitive lists are replaced when merged

Ordering: Uses the ordering specified in the Resource Config. Elements not specified in the Resource Config do not have ordering guarantees with respect to the elements in the Resource Config.

Merge Table: For each list element merge Resource Config and Resource element with the same value

Element in Resource Config Element in Resource Element in Last Applied Action
Yes Yes - Do nothing
Yes No - Add to list.
No - Yes Remove from list.
No - No Do nothing.

This merge strategy uses the patch merge key to identify container elements in a list and merge them. The patch merge key is defined in the Kubernetes API on the field.

# Last Applied
args: ["a", "b"]
# Resource Config (Local)
args: ["a", "c"]
# Resource (Live)
args: ["a", "b", "d"]
# Applied Resource
args: ["a", "c", "d"]

Merging Lists of Objects

Merge Strategy: Lists of primitives may be merged or replaced. Lists are merged if the list has a patch strategy of merge and a patch merge key on the list field. Container list example.

Merge Key: The patch merge key is used to identify same elements in a list. Unlike map elements (keyed by key) and object fields (keyed by field name), lists don’t have a built-in merge identity for elements (index does not define identity). Instead an object field is used as a synthetic key/value for merging elements. This fields is the patch merge key. List elements with the same patch merge key will be merged when lists are merged.

Ordering: Uses the ordering specified in the Resource Config. Elements not specified in the Resource Config do not have ordering guarantees.

Merge Table: For each list element merge Resource Config and Resource element where the elements have the same value for the patch merge key

Element in Resource Config Element in Resource Element in Last Applied Action
Yes - - Recursively merge the Resource Config and Resource values.
Yes No - Add to list.
No - Yes Remove from list.
No - No Do nothing.

This merge strategy uses the patch merge key to identify container elements in a list and merge them. The patch merge key is defined in the Kubernetes API on the field.

# Last Applied Resource Config
containers:
- name: nginx          # key: nginx
  image: nginx:1.10
- name: nginx-helper-a # key: nginx-helper-a; will be deleted in result
  image: helper:1.3
- name: nginx-helper-b # key: nginx-helper-b; will be retained
  image: helper:1.3
# Resource Config (Local)
containers:
- name: nginx
  image: nginx:1.10
- name: nginx-helper-b
  image: helper:1.3
- name: nginx-helper-c # key: nginx-helper-c; will be added in result
  image: helper:1.3
# Resource (Live)
containers:
- name: nginx
  image: nginx:1.10
- name: nginx-helper-a
  image: helper:1.3
- name: nginx-helper-b
  image: helper:1.3
  args: ["run"] # Field will be retained
- name: nginx-helper-d # key: nginx-helper-d; will be retained
  image: helper:1.3
# Applied Resource
containers:
- name: nginx
  image: nginx:1.10
  # Element nginx-helper-a was Deleted
- name: nginx-helper-b
  image: helper:1.3
  # Field was Ignored
  args: ["run"]
  # Element was Added
- name: nginx-helper-c
  image: helper:1.3
  # Element was Ignored
- name: nginx-helper-d
  image: helper:1.3

Last modified October 5, 2020: site refactor (53154e4)