-
Notifications
You must be signed in to change notification settings - Fork 40k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Strategic Merge Patch in apiserver #6027
Conversation
@@ -79,6 +79,14 @@ func (r *RequestConstructionError) Error() string { | |||
return fmt.Sprintf("request construction error: '%v'", r.Err) | |||
} | |||
|
|||
type PatchType string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should go in pkg/apiserver/patch/patch.go?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This enum is created and never used (except for once). I'd prefer if it was used in place of all the copy pasted string constants.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so... pkg/apiserver/patch is only one implementation. The header supports all three implementations.
The bigger question is whether it goes in the server or the client. For a proper API it probably needs to be duplicated in both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's lame, but we have a number of such constants in pkg/api/types.go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went with the pkg/api/types.go approach. @mikedanese would appreciate your eyes on the way I factored it out.
Nice! Looks good. I'm going to try to step through the kube-merge-patch logic in the next couple days. |
@@ -0,0 +1,454 @@ | |||
/* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason this couldn't go into pkg/util/patch/*
? Feels more generic than apiserver.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I'm thinking I should rename it from Kube Merge Patch to something more generic as well, since the way it's implemented and its interface doesn't actually have anything to do with Kubernetes. Maybe Strategic Patch or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not keen on contributing to code sprawl. We currently have code sprinkled all over a very flat directory layout -- #4851, and putting the code into util would make it harder to extract apiserver later #2742. I suggest leaving the code under apiserver until we figure out how we should actually be structuring our codebase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clayton's point that this is generic is correct - this is a library that could (and maybe should) live outside of kube. As a result, unless we're going to discourage apiserver from using things outside of itself, I think util is the right place. If it had any dependency or coupling at all to apiserver I'd definitely say it should stay in the apiserver package.
The apiserver installer portions look fine to me. I haven't had a chance to look more deeply at merge and probably won't in the next few days. |
I will try to at least look through the merge semantics today. |
To indicate that a map should not be merged and instead should be taken literally: | ||
|
||
```yaml | ||
__patch: strict # recursive and applies to all fields of the map it's in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The options for __patch are merge, strict, and delete? Would delete have any meaning at the top level? Can strict be overridden by merge specified in a sublist? Does an explicit __patch annotation override all struct field tags recursively?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would delete have any meaning at the top level?
Delete is not needed in a map: you just set the key to null.
Can strict be overridden by merge specified in a sublist?
Not at the moment, addressed in other comment.
Does an explicit __patch annotation override all struct field tags recursively?
"strict" does.
Very cool! Looks pretty close for a first cut. I'll try to look at the merge test in more detail, but I've made a pass over the implementation. |
original: | ||
simpleMap: | ||
name: 1 | ||
value: 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
key1, key2 would be less confusing than name, value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
I'm pretty happy with the merge test. The main thing it omits is error cases (merging lists of lists, invalid patch types, merge strategy w/o key tags, unspecified merge keys in the data, etc.), though it maybe could use a few more __patch: strict cases with nested structures. |
cc @lavalamp |
All comments responded to - ready for another pass. |
@@ -718,7 +718,7 @@ type PodSpec struct { | |||
// state of a system. | |||
type PodStatus struct { | |||
Phase PodPhase `json:"phase,omitempty" description:"current condition of the pod."` | |||
Conditions []PodCondition `json:"Condition,omitempty" description:"current service state of pod"` | |||
Conditions []PodCondition `json:"Condition,omitempty" description:"current service state of pod" patchStrategy:"merge" patchMergeKey:"type"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please tag NodeCondition and NodeAddress as well, for consistency.
Thanks for the changes. Other than the NodeStatus flags, LGTM. I'm not sure what to do about the Go library code, but apparently this isn't the only instance of that. @lavalamp Do you want to take a look? |
Test failures are known issues unrelated to this PR. Merging. We can improve more if necessary in subsequent PRs. |
Implement Strategic Merge Patch in apiserver
@ghodss I'm seeing some intermittent integration test failures. Could you take a look? For example:
|
I'm guessing the issue may be that the patches don't work with v1beta3. labels have moved to metadata.labels. |
|
For others: workaround in #6431 |
Fixes #4889.
This is an implementation of JSON merge patch custom to Kubernetes, called Kubernetes Merge Patch (update: this is now called Strategic Merge Patch). For a description of the design see #4889 (comment) and for usage see docs/api-conventions.md in this PR.
Currently the patch strategy metadata is listed as struct tags, but we can make the information accessible through swagger (and therefore accessible to clients) at a later point.
@smarterclayton, can you look at api_installer and resthandler? The issue is that I need the versioned object in the PatchResource method to lookup the
patchStrategy
struct tags figure out what lists of the versioned object should be merged, and I kind of hacked it together. Let me know if there's a better way.@bgrant0607 @mikedanese