-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Add transformer to apply json patch6902 #300
Add transformer to apply json patch6902 #300
Conversation
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: Liujingfang1 The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
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.
@mengqiy ? WDYT?
pkg/patch/loadpatch.go
Outdated
) | ||
|
||
// NewPatchJson6902 load a slice of PatchJson6902 | ||
func NewPatchJson6902(l loader.Loader, slice []PatchJson6902) (map[resource.ResId][]byte, error) { |
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.
LoadPatchJson6902Map
It doesn't make instances of PatchJson6902, and calling it Load a signals the need for a loader.
pkg/patch/loadpatch.go
Outdated
"", | ||
p.Target.Namespace, | ||
) | ||
content, err := l.Load(p.Path) |
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 approach is consistent with what we do for SMP, but seeing it now this way (and noticing this function has no test) makes me wonder if we'd be better off specifying the patch string directly in the kustomization file.
We didn't do this for SMP because those patches look like resources, and specifying a resource as a string field in another resource would be odd. Plus SMP's combine the path to the thing they are modifying with the thing they modify - they are different.
Seems like
patchesJson6902:
- target: blah
- op: replace
path: /spec/template/spec/containers/0/name
value: my-nginx
- op: add
path: /spec/replica
value: 3
would be easier to maintain in a kustomization file than a distinct JSON patch file.
perhaps it boils down to a question of who writes the JSON patch - human or tool?
The separate file approach is better for the tool, this inlining seems better for the human.
Of course we'd convert these fields to standard JSON {"op": "replace", "path": "/spec/template/spec/containers/0/name", "value": "my-nginx"}
, before feeding them into the library call.
If not, we need a regression test using a fake file loader for this Load function
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.
@mxey WDYT?
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 format is more straightforward to me.
From what I read from issues, human is more likely to write JSON patch, especially for changing some port, dns names.
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.
sgtm, lets make it in line .
yay, no loader!
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.
@monopole I like the inline approach in general, but I can see a need for a tool writing patches, like setting Ingress hostnames for feature branches. If it's inline, there should also be a command to add those.
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 have some concerns:
Make json patch inline means that kustomize need to understand json patches e.g. how to decoding it. There are more fields than what @Liujingfang1 already wrote, e.g. from
.
IMO making json patch inline pushes some complexity of json patch to the kustomize layer. Ideally, kustomize doesn't need to understand it. It should simply hand the byte array to the library.
// patchJson6902Transformer applies patches. | ||
type patchJson6902Transformer struct { | ||
patches map[resource.ResId][]byte | ||
} |
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.
How about just
type patchJson6902Map map[resource.ResId][]byte
and use this to hang transformer methods from. We return a transformer.Transformer anyway...
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.
So this changes to
map[resource.ResId][]WhateverWeCallTheOpPathValueTuple
:)
Changed the format of |
If there are several or many operations, the
|
@monopole After coding on this format for a few hours, I realized it increases the complexity beyond my expectation. The JSON patch need to support 5 different operations. Some has
If we load a JSON patch from a file, we get With this new format, we need to add logic validating the operations and |
A subset of 6902 - just But we have no data besides @mxey's comment as to how people want to do this. So OK with doing the simpler coding problem of reading the file. Can we keep it forward compatible with the less pedantic approach proposed above? E.g. a structure with a target and a path, and possibly later a slice of operations (user would be required to omit the path field if they wanted to specify inline operations). That might be the best of both worlds. |
@monopole @mengqiy I found one library https://github.com/krishicks/yaml-patch which can handle YAML format for JSON patch. Seems we can use it and I will test it. If it works, we can keep the inline format without handling the complexity by ourselves. |
sgtm! What we're going for here is a means to not have to write new kustomization directives for every small edit (e.g. changing the image name). But I'm still in favor of doing both (reading in a complete JSON6902 patch file or accepting inline directives). The |
I think Kustomize should support the entire JSON Patch spec, especially if it references the RFC in the property name. I personally have a potential use case for Also, it would be consistent with what kubectl supports. |
dc10ff5
to
19acd33
Compare
19acd33
to
7f0e9e3
Compare
|
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.
Looks great, a few nits.
t.target.Name, | ||
"", | ||
t.target.Namespace, | ||
) |
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.
If this is done in the factory, then both transformation types can have a ResId field instead of patch.Target, and we'd have only one copy of this code instead of two.
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 sounds good. Will update it.
decodedPatch, err := jsonpatch.DecodePatch(t.operations) | ||
if err != nil { | ||
return err | ||
} |
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.
Also, this decode could move to the factory, so this error would happen earlier.
s/operations []byte/patch jsonpatch.Patch/ in patchJson6902JSONTransformer
then patchJson6902JSONTransformer would look more like patchJson6902YAMLTransformer
"", | ||
t.target.Namespace, | ||
) | ||
|
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.
so this would go away
return fmt.Errorf("found multiple objects that the patch can apply %v", matchedIds) | ||
} | ||
|
||
obj := baseResourceMap[matchedIds[0]] |
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.
should only have one copy of L60-78.
shared package private helper func
} | ||
|
||
return transformers.NewNoOpTransformer(), nil | ||
} |
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.
could call this file factory.go
please add a test with a fake loader. Nothing too complex, but enough to capture the error conditions. i.e. don't actually look at the transformers returned
|
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.
/lgtm
I'm glad that now we have a solution that can handle both inline and from-file json patch! |
break up #298
To avoid import cycle, I add the transformer files to pkg/patch/transformer