Merge pull request #535 from mengqiy/SMP_doc

Strategic Merge Patch doc
This commit is contained in:
Phillip Wittrock 2017-04-19 11:05:31 -07:00 committed by GitHub
commit f11408f7d6
2 changed files with 224 additions and 171 deletions

View File

@ -26,11 +26,6 @@ An introduction to using resources with kubectl can be found in [the object mana
- [Verbs on Resources](#verbs-on-resources) - [Verbs on Resources](#verbs-on-resources)
- [PATCH operations](#patch-operations) - [PATCH operations](#patch-operations)
- [Strategic Merge Patch](#strategic-merge-patch) - [Strategic Merge Patch](#strategic-merge-patch)
- [List Operations](#list-operations)
- [List of Maps](#list-of-maps)
- [List of Primitives](#list-of-primitives)
- [Unordered Set](#unordered-set)
- [Map Operations](#map-operations)
- [Idempotency](#idempotency) - [Idempotency](#idempotency)
- [Optional vs. Required](#optional-vs-required) - [Optional vs. Required](#optional-vs-required)
- [Defaulting](#defaulting) - [Defaulting](#defaulting)
@ -555,172 +550,7 @@ below.
#### Strategic Merge Patch #### Strategic Merge Patch
In the standard JSON merge patch, JSON objects are always merged but lists are Details of Strategic Merge Patch are covered [here](../strategic-merge-patch.md).
always replaced. Often that isn't what we want. Let's say we start with the
following Pod:
```yaml
spec:
containers:
- name: nginx
image: nginx-1.0
```
...and we POST that to the server (as JSON). Then let's say we want to *add* a
container to this Pod.
```yaml
PATCH /api/v1/namespaces/default/pods/pod-name
spec:
containers:
- name: log-tailer
image: log-tailer-1.0
```
If we were to use standard Merge Patch, the entire container list would be
replaced with the single log-tailer container. However, our intent is for the
container lists to merge together based on the `name` field.
To solve this problem, Strategic Merge Patch uses the go struct tag of the API
objects to determine what lists should be merged and which ones should not.
Currently the metadata is available as struct tags on the API objects
themselves, but will become available to clients as Swagger annotations in the
future. In the above example, the `patchStrategy` metadata for the `containers`
field would be `merge` and the `patchMergeKey` would be `name`.
Note: If the patch results in merging two lists of primitives, the primitives are
first deduplicated and then merged.
Strategic Merge Patch also supports special operations as listed below.
### List Operations
#### List of Maps
To override the container list to be strictly replaced, regardless of the
default:
```yaml
containers:
- name: nginx
image: nginx-1.0
- $patch: replace # any further $patch operations nested in this list will be ignored
```
To delete an element of a list that should be merged:
```yaml
containers:
- name: nginx
image: nginx-1.0
- $patch: delete
name: log-tailer # merge key and value goes here
```
Delete operation will delete the first entry in the list that match the merge key.
But there is validation to make sure no 2 entries with the same merge key will get in the list.
#### List of Primitives
We have two patch strategies for lists of primitives: replace and merge.
Replace is the default patch strategy, which will replace the whole list on update and it will preserve the order;
while merge strategy works as an unordered set. In this section, we call a primitive list with merge strategy an unordered set.
The patch strategy is defined in the go struct tag of the API objects,
e.g. `finalizers` uses `merge` as patch strategy.
```go
Finalizers []string `json:"finalizers,omitempty" patchStrategy:"merge" protobuf:"bytes,14,rep,name=finalizers"`
```
##### Unordered Set
There are 3 operations: add, delete, replace.
Suppose we have defined a `finalizers` and we call it the original finalizers:
```yaml
finalizers:
- a
- b
- c
```
1) To add items in a set, we use the list name as the key.
e.g. to add items "d" and "e" in the original finalizers, the patch will be:
```yaml
finalizers:
- d
- e
```
After applying the patch on the original finalizers, it will become:
```yaml
finalizers:
- a
- b
- c
- d
- e
```
2) To delete items in a set, we use a parallel list with key: `$deleteFromPrimitiveList/\<keyOfPrimitiveList\>`.
e.g. to delete items "b" and "c" from the original finalizers, the patch will be:
```yaml
$deleteFromPrimitiveList/finalizers:
- b
- c
```
After applying the patch on the original finalizers, it will become:
```yaml
finalizers:
- a
```
In an erroneous case, the set may be created with duplicates. Deleting an item that has duplicates will delete all matching items.
3) Replace can be fulfilled by an addition and a deletion.
e.g. to replace "a" with "x" in the original finalizers, the patch will be:
```yaml
$deleteFromPrimitiveList/finalizers:
- a
finalizers:
- x
```
After applying the patch on the original finalizers, it will become:
```yaml
finalizers:
- x
- b
- c
```
### Map Operations
To indicate that a map should not be merged and instead should be taken literally:
```yaml
$patch: replace # recursive and applies to all fields of the map it's in
containers:
- name: nginx
image: nginx-1.0
```
To delete a field of a map:
```yaml
name: nginx
image: nginx-1.0
labels:
live: null # set the value of the map key to null
```
## Idempotency ## Idempotency

View File

@ -0,0 +1,223 @@
Strategic Merge Patch
=====================
# Background
TODO: @pwittrock complete this section
In the standard JSON merge patch, JSON objects are always merged but lists are
always replaced. Often that isn't what we want. Let's say we start with the
following Pod:
```yaml
spec:
containers:
- name: nginx
image: nginx-1.0
```
and we POST that to the server (as JSON). Then let's say we want to *add* a
container to this Pod.
```yaml
PATCH /api/v1/namespaces/default/pods/pod-name
spec:
containers:
- name: log-tailer
image: log-tailer-1.0
```
If we were to use standard Merge Patch, the entire container list would be
replaced with the single log-tailer container. However, our intent is for the
container lists to merge together based on the `name` field.
To solve this problem, Strategic Merge Patch uses the go struct tag of the API
objects to determine what lists should be merged and which ones should not.
Currently the metadata is available as struct tags on the API objects
themselves, but will become available to clients as Swagger annotations in the
future. In the above example, the `patchStrategy` metadata for the `containers`
field would be `merge` and the `patchMergeKey` would be `name`.
# Basic Patch Format
Strategic Merge Patch supports special operations through directives.
There are multiple directives:
- replace
- merge
- delete
- delete from primitive list
`replace`, `merge` and `delete` are mutual exclusive.
## `replace` Directive
### Purpose
`replace` directive indicates that the element that contains it should be replaced instead of being merged.
### Syntax
`replace` directive is used in both patch with directive marker and go struct tags.
Example usage in the patch:
```
$patch: replace
```
### Example
`replace` directive can be used on both map and list.
#### Map
To indicate that a map should not be merged and instead should be taken literally:
```yaml
$patch: replace # recursive and applies to all fields of the map it's in
containers:
- name: nginx
image: nginx-1.0
```
#### List of Maps
To override the container list to be strictly replaced, regardless of the default:
```yaml
containers:
- name: nginx
image: nginx-1.0
- $patch: replace # any further $patch operations nested in this list will be ignored
```
## `delete` Directive
### Purpose
`delete` directive indicates that the element that contains it should be deleted.
### Syntax
`delete` directive is used only in the patch with directive marker.
It can be used on both map and list of maps.
```
$patch: delete
```
### Example
#### List of Maps
To delete an element of a list that should be merged:
```yaml
containers:
- name: nginx
image: nginx-1.0
- $patch: delete
name: log-tailer # merge key and value goes here
```
Note: Delete operation will delete all entries in the list that match the merge key.
#### Maps
One way to delete a map is using `delete` directive.
Applying this patch will delete the rollingUpdate map.
```yaml
rollingUpdate:
$patch: delete
```
An equivalent way to delete this map is
```yaml
rollingUpdate: null
```
## `merge` Directive
### Purpose
`merge` directive indicates that the element that contains it should be merged instead of being replaced.
### Syntax
`merge` directive is used only in the go struct tags.
## `deleteFromPrimitiveList` Directive
### Purpose
We have two patch strategies for lists of primitives: replace and merge.
Replace is the default patch strategy for list, which will replace the whole list on update and it will preserve the order;
while merge strategy works as an unordered set. We call a primitive list with merge strategy an unordered set.
The patch strategy is defined in the go struct tag of the API objects.
`deleteFromPrimitiveList` directive indicates that the elements in this list should be deleted from the original primitive list.
### Syntax
It is used only as the prefix of the key in the patch.
```
$deleteFromPrimitiveList/<keyOfPrimitiveList>: [a primitive list]
```
### Example
##### List of Primitives (Unordered Set)
`finalizers` uses `merge` as patch strategy.
```go
Finalizers []string `json:"finalizers,omitempty" patchStrategy:"merge" protobuf:"bytes,14,rep,name=finalizers"`
```
Suppose we have defined a `finalizers` and we call it the original finalizers:
```yaml
finalizers:
- a
- b
- c
```
To delete items "b" and "c" from the original finalizers, the patch will be:
```yaml
# The directive includes the prefix $deleteFromPrimitiveList and
# followed by a '/' and the name of the list.
# The values in this list will be deleted after applying the patch.
$deleteFromPrimitiveList/finalizers:
- b
- c
```
After applying the patch on the original finalizers, it will become:
```yaml
finalizers:
- a
```
Note: When merging two set, the primitives are first deduplicated and then merged.
In an erroneous case, the set may be created with duplicates. Deleting an
item that has duplicates will delete all matching items.
TODO: @pwittrock
# Changing Patch Format
## Purpose
## Requirement
### Version Skew
## Strategy
## Example