Skip to content

Commit b87d4d1

Browse files
author
Phillip Wittrock
authored
Merge pull request kubernetes#620 from mengqiy/update_replaceKeys_proposal
improve replaceKeys proposal
2 parents b52e814 + 5225a36 commit b87d4d1

File tree

1 file changed

+87
-22
lines changed

1 file changed

+87
-22
lines changed

add-new-patchStrategy-to-clear-fields-not-present-in-patch.md

Lines changed: 87 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
Add new patchStrategy to clear fields not present in the patch
22
=============
33

4-
Add tags `patchStrategy:"replaceKeys"`. For a given type that has the tag, all keys/fields missing
5-
from the request will be cleared when patching the object.
6-
For a field presents in the request, it will be merged with the live config.
4+
We introduce a new struct tag `patchStrategy:"retainKeys"` and
5+
a new optional directive `$retainKeys: <list of fields>` in the patch.
76

87
The proposal of Full Union is in [kubernetes/community#388](https://github.com/kubernetes/community/pull/388).
98

@@ -66,11 +65,53 @@ It indicates how to generate and merge a patch for lists. It could be `merge` or
6665

6766
new tags:
6867

69-
`patchStrategy: replaceKeys`:
68+
`patchStrategy: "retainKeys"`:
69+
70+
We introduce a new optional directive `$retainKeys` to support the new patch strategy.
71+
72+
`$retainKeys` directive has the following properties:
73+
- It contains a list of strings.
74+
- All fields needing to be preserved must be present in the `$retainKeys` list.
75+
- The fields that are present will be merged with live object.
76+
- All of the missing fields will be cleared when patching.
77+
- All fields in the `$retainKeys` list must be a superset or the same as the fields present in the patch.
78+
79+
A new patch will have the same content as the old patch and an additional new directive.
80+
It will be backward compatible.
81+
82+
#### When the patch doesn't have `$retainKeys` directive
83+
84+
When the patch doesn't have `$retainKeys` directive, even for a type with `patchStrategy: "retainKeys"`,
85+
the server won't treat the patch with the retainKeys logic.
86+
87+
This will guarantee the backward compatibility: old patch behaves the same as before on the new server.
88+
89+
#### When the patch has fields that not present in the `$retainKeys` list
90+
91+
The server will reject the patch in this case.
92+
93+
This is an invalid patch:
94+
95+
```yaml
96+
union:
97+
$retainKeys:
98+
- foo
99+
foo: a
100+
bar: x
101+
```
102+
103+
#### When the `$retainKeys` list has fields that are not present in the patch
104+
105+
The server will merge the change and clear the fields not present in the `$retainKeys` list
70106

71-
We introduce a new value `replaceKeys` for `patchStrategy`.
72-
It indicates that all fields needing to be preserved must be present in the patch.
73-
And the fields that are present will be merged with live object. All the missing fields will be cleared when patching.
107+
This is a valid patch:
108+
```yaml
109+
union:
110+
$retainKeys:
111+
- foo
112+
- bar
113+
foo: a
114+
```
74115

75116
#### Examples
76117

@@ -80,8 +121,8 @@ Type definition:
80121
```go
81122
type ContainerStatus struct {
82123
...
83-
// Add patchStrategy:"replaceKeys"
84-
State ContainerState `json:"state,omitempty" protobuf:"bytes,2,opt,name=state" patchStrategy:"replaceKeys"``
124+
// Add patchStrategy:"retainKeys"
125+
State ContainerState `json:"state,omitempty" protobuf:"bytes,2,opt,name=state" patchStrategy:"retainKeys"``
85126
...
86127
}
87128
```
@@ -101,7 +142,8 @@ state:
101142
Patch:
102143
```yaml
103144
state:
104-
$patch: replaceKeys
145+
$retainKeys:
146+
- terminated
105147
terminated:
106148
exitCode: 0
107149
finishedAt: ...
@@ -120,8 +162,8 @@ Type definition:
120162
```go
121163
type DeploymentSpec struct {
122164
...
123-
// Add patchStrategy:"replaceKeys"
124-
Strategy DeploymentStrategy `json:"strategy,omitempty" protobuf:"bytes,4,opt,name=strategy" patchStrategy:"replaceKeys"`
165+
// Add patchStrategy:"retainKeys"
166+
Strategy DeploymentStrategy `json:"strategy,omitempty" protobuf:"bytes,4,opt,name=strategy" patchStrategy:"retainKeys"`
125167
...
126168
}
127169
```
@@ -144,7 +186,9 @@ unionName:
144186
Patch:
145187
```yaml
146188
unionName:
147-
$patch: replaceKeys
189+
$retainKeys:
190+
- discriminatorName
191+
- barField
148192
discriminatorName: bar
149193
barField:
150194
barSubfield: val2
@@ -159,14 +203,14 @@ unionName:
159203
160204
3) Inlined union with `patchMergeKey` only.
161205
This case is special, because `Volumes` already has a tag `patchStrategy:"merge"`.
162-
We change the tag to `patchStrategy:"merge|replaceKeys"`
206+
We change the tag to `patchStrategy:"merge|retainKeys"`
163207

164208
Type definition:
165209
```go
166210
type PodSpec struct {
167211
...
168-
// Add another value "replaceKeys" to patchStrategy
169-
Volumes []Volume `json:"volumes,omitempty" patchStrategy:"merge|replaceKeys" patchMergeKey:"name" protobuf:"bytes,1,rep,name=volumes"`
212+
// Add another value "retainKeys" to patchStrategy
213+
Volumes []Volume `json:"volumes,omitempty" patchStrategy:"merge|retainKeys" patchMergeKey:"name" protobuf:"bytes,1,rep,name=volumes"`
170214
...
171215
}
172216
```
@@ -191,8 +235,10 @@ Patch:
191235
```yaml
192236
spec:
193237
volumes:
194-
- name: foo
195-
$patch: replaceKeys
238+
- $retainKeys:
239+
- name
240+
- hostPath
241+
name: foo
196242
hostPath:
197243
path: ...
198244
```
@@ -224,8 +270,8 @@ Changes about how to generate the patch rely on package Strategic Merge Patch.
224270
Strategic Merge Patch is a package used by both client and server. A typical usage is that a client
225271
calls the function to calculate the patch and the API server calls another function to merge the patch.
226272
227-
We need to make sure the client always sends a patch that includes all of the fields that it wants to keep.
228-
When merging, auto clear missing fields of a patch if the patch has a directive `$patch: replaceKeys`
273+
We need to make sure the new client always sends its patches with the `$retainKeys` directive.
274+
When merging, auto clear missing fields of a patch if the patch has a directive `$retainKeys`
229275

230276
### Open API
231277

@@ -238,13 +284,32 @@ The changes are all backward compatible.
238284
Old kubectl vs New server: All behave the same as before, since no new directive in the patch.
239285

240286
New kubectl vs Old server: All behave the same as before, since new directive will not be recognized
241-
by the old server and it will be dropped in conversion; Unchanged fields will not affect the merged result.
287+
by the old server and it will be dropped in conversion.
242288

243289
# Alternatives Considered
244290

291+
# 1. Use directive `$patch: retainKeys` in the patch
292+
293+
Add tags `patchStrategy:"retainKeys"`.
294+
For a given type that has the tag, all keys/fields missing
295+
from the request will be cleared when patching the object.
296+
Each field present in the request will be merged with the live config.
297+
298+
## Analysis
299+
300+
There are 2 reasons of avoiding this logic:
301+
- Using `$patch` as directive key will break backward compatibility.
302+
But can easily fixed by using a different key, e.g. `retainKeys: true`.
303+
Reason is that `$patch` has been used in earlier releases.
304+
If we add new value to this directive,
305+
the old server will reject the new patch due to not knowing the new value.
306+
- The patch has to include the entire struct to hold the place in a list with `replace` patch strategy,
307+
even though there may be no changes at all.
308+
This is less efficient compared to the approach above.
309+
245310
The proposals below are not mutually exclusive with the proposal above, and maybe can be added at some point in the future.
246311

247-
# 1. Add Discriminators in All Unions/OneOf APIs
312+
# 2. Add Discriminators in All Unions/OneOf APIs
248313

249314
Original issue is described in kubernetes/kubernetes#35345
250315

0 commit comments

Comments
 (0)