Merge pull request #592 from pwittrock/patch
Write guide on how to evolve the strategic merge patch format.
This commit is contained in:
commit
3a1e6d22f8
|
@ -3,7 +3,9 @@ Strategic Merge Patch
|
||||||
|
|
||||||
# Background
|
# Background
|
||||||
|
|
||||||
TODO: @pwittrock complete this section
|
Kubernetes supports a customized version of JSON merge patch called strategic merge patch. This
|
||||||
|
patch format is used by `kubectl apply`, `kubectl edit` and `kubectl patch`, and contains
|
||||||
|
specialized directives to control how specific fields are merged.
|
||||||
|
|
||||||
In the standard JSON merge patch, JSON objects are always merged but lists are
|
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
|
always replaced. Often that isn't what we want. Let's say we start with the
|
||||||
|
@ -209,15 +211,118 @@ In an erroneous case, the set may be created with duplicates. Deleting an
|
||||||
item that has duplicates will delete all matching items.
|
item that has duplicates will delete all matching items.
|
||||||
|
|
||||||
|
|
||||||
TODO: @pwittrock
|
# Changing patch format
|
||||||
# Changing Patch Format
|
|
||||||
|
|
||||||
## Purpose
|
As issues and limitations have been discovered with the strategic merge
|
||||||
|
patch implementation, it has been necessary to change the patch format
|
||||||
|
to support additional semantics - such as merging lists of
|
||||||
|
primitives and defining order when merging lists.
|
||||||
|
|
||||||
## Requirement
|
## Requirments for any changes to the patch format
|
||||||
|
|
||||||
### Version Skew
|
**Note:** Changes to the strategic merge patch must be backwards compatible such
|
||||||
|
that patch requests valid in previous versions continue to be valid.
|
||||||
|
That is, old patch formats sent by old clients to new servers with
|
||||||
|
must continue to function correctly.
|
||||||
|
|
||||||
## Strategy
|
Previously valid patch requests do not need to keep the exact same
|
||||||
|
behavior, but do need to behave correctly.
|
||||||
|
|
||||||
|
**Example:** if a patch request previously randomized the order of elements
|
||||||
|
in a list and we want to provide a deterministic order, we must continue
|
||||||
|
to support old patch format but we can make the ordering deterministic
|
||||||
|
for the old format.
|
||||||
|
|
||||||
|
### Client version skew
|
||||||
|
|
||||||
|
Because the server does not publish which patch versions it supports,
|
||||||
|
and it silently ignores patch directives that it does not recognize,
|
||||||
|
new patches should behave correctly when sent to old servers that
|
||||||
|
may not support all of the patch directives.
|
||||||
|
|
||||||
|
While the patch API must be backwards compatible, it must also
|
||||||
|
be forward compatible for 1 version. This is needed because `kubectl` must
|
||||||
|
support talking to older and newer server versions without knowing what
|
||||||
|
parts of patch are supported on each, and generate patches that work correctly on both.
|
||||||
|
|
||||||
|
## Strategies for introducing new patch behavior
|
||||||
|
|
||||||
|
#### 1. Add optional semantic meaning to the existing patch format.
|
||||||
|
|
||||||
|
**Note:** Must not require new data or elements to be present that was not required before. Meaning must not break old interpretation of old patches.
|
||||||
|
|
||||||
|
**Good Example:**
|
||||||
|
|
||||||
|
Old format
|
||||||
|
- ordering of elements in patch had no meaning and the final ordering was arbitrary
|
||||||
|
|
||||||
|
New format
|
||||||
|
- ordering of elements in patch has meaning and the final ordering is deterministic based on the ordering in the patch
|
||||||
|
|
||||||
|
**Bad Example:**
|
||||||
|
|
||||||
|
Old format
|
||||||
|
- fields not present in a patch for Kind foo are ignored
|
||||||
|
- unmodified fields for Kind foo are optional in patch request
|
||||||
|
|
||||||
|
New format
|
||||||
|
- fields not present in a patch for Kind foo are cleared
|
||||||
|
- unmodified fields for Kind foo are required in patch request
|
||||||
|
|
||||||
|
This example won't work, because old patch formats will contain data that is now
|
||||||
|
considered required. To support this, introduce a new directive to guard the
|
||||||
|
new patch format.
|
||||||
|
|
||||||
|
#### 2. Add support for new directives in the patch format
|
||||||
|
|
||||||
|
- Optional directives may be introduced to change how the patch is applied by the server - **backwards compatible** (old patch against newer server).
|
||||||
|
- May control how the patch is applied
|
||||||
|
- May contain patch information - such as elements to delete from a list
|
||||||
|
- Must NOT impose new requirements on the old patch format
|
||||||
|
|
||||||
|
- New patch requests should be a superset of old patch requests - **forwards compatible** (newer patch against older server)
|
||||||
|
- *Old servers will ignore directives they do not recognize*
|
||||||
|
- Must include the full patch that would have been sent before the new directives were added.
|
||||||
|
- Must NOT rely on the directive being supported by the server
|
||||||
|
|
||||||
|
**Good Example:**
|
||||||
|
|
||||||
|
Old format
|
||||||
|
- fields not present in a patch for Kind foo are ignored
|
||||||
|
- unmodified fields for Kind foo are optional in patch request
|
||||||
|
|
||||||
|
New format *without* directive
|
||||||
|
- Same as old
|
||||||
|
|
||||||
|
New format *with* directive
|
||||||
|
- fields not present in a patch for Kind foo are cleared
|
||||||
|
- unmodified fields for Kind foo are required in patch request
|
||||||
|
|
||||||
|
In this example, the behavior was unchanged when the directive was missing,
|
||||||
|
retaining the old behavior for old patch requests.
|
||||||
|
|
||||||
|
**Bad Example:**
|
||||||
|
|
||||||
|
Old format
|
||||||
|
- fields not present in a patch for Kind foo are ignored
|
||||||
|
- unmodified fields for Kind foo are optional in patch request
|
||||||
|
|
||||||
|
New format *with* directive
|
||||||
|
- Same as old
|
||||||
|
|
||||||
|
New format *without* directive
|
||||||
|
- fields not present in a patch for Kind foo are cleared
|
||||||
|
- unmodified fields for Kind foo are required in patch request
|
||||||
|
|
||||||
|
In this example, the behavior was changed when the directive was missing,
|
||||||
|
breaking compatibility.
|
||||||
|
|
||||||
|
## Alternatives
|
||||||
|
|
||||||
|
The previous strategy is necessary because there is no notion of
|
||||||
|
patch versions. Having the client negotiate the patch version
|
||||||
|
with the server would allow changing the patch format, but at
|
||||||
|
the cost of supporting multiple patch formats in the server and client.
|
||||||
|
Using client provided directives to evolve how a patch is merged
|
||||||
|
provides some limited support for multiple versions.
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
Loading…
Reference in New Issue