-
Notifications
You must be signed in to change notification settings - Fork 261
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
Feature: "kn service apply" #964
Conversation
Skipping CI for Draft Pull Request. |
@rhuss what's the diff between this and |
Hey there, |
@duglin 'kn service create --force' will completely override the existing service resource. (akin to git push --force) while 'kn service apply' will do a three way merge of the configuration (aka git merge). |
@kwiesmueller Thanks for the offer ! I'm just back from PTO and will continue on this issue soon. |
All (the pull request submitter and all commit authors) CLAs are signed, but one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that by leaving a comment that contains only Note to project maintainer: There may be cases where the author cannot leave a comment, or the comment is not properly detected as consent. In those cases, you can manually confirm consent of the commit author(s), and set the ℹ️ Googlers: Go here for more info. |
CLAs look good, thanks! ℹ️ Googlers: Go here for more info. |
@kwiesmueller Finally I got around to continue on working on this PR. I am still trying to get client side patching to work, before switching to server side, but there are still some stumbling block with defaulting, serializing and also Knative backend handling. At the moment I'm a bit stuck to understand the following error message:
@kwiesmueller can you see, where the actual conflict is ? I though I fixed everything, but can't really see what is still conflicting. |
If you're using SSA then the conflict error should give you the exact fields you get a conflict with. Did I read correctly, that it's not SSA yet? |
It could be the CSA annotation, we should have a fix for that in the latest release. |
true, it's a true client side patch for now. But happy to explore SSA, too (thought client-side patch was easier to start with) |
@kwiesmueller do you have a good entrypoint/sample showing how to use SSA so that I can find out what's going on ? Currently I'm a bit lost. |
/retest |
The following is the coverage report on the affected files.
|
CLAs look good, thanks! ℹ️ Googlers: Go here for more info. |
The following is the coverage report on the affected files.
|
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.
Thanks for addressing feedback. LGTM.
@navidshaikh feel free to do your review and merge
CLAs look good, thanks! ℹ️ Googlers: Go here for more info. |
The following is the coverage report on the affected files.
|
@navidshaikh I think we are good to merge if you don't have any objections. |
CLAs look good, thanks! ℹ️ Googlers: Go here for more info. |
The following is the coverage report on the affected files.
|
This commit introduces a client-side apply with a plain JsonPatchMerge. This is more limited than a StrategicPatchMerg as it does not allow to merge lists (they are just overwritten). Also is not a real 3-way merger that would lead to a conflict when both the, the server-side and the provide update overlapp in fields that updated, compared to the shared original configuration. This is a problem of JsonThreeWayMerger itself, as pointed out in kubernetes/kubernetes#40666 (review). This limitation is shared with kubectl, which suffers from the same issue if using `kubectl apply` with a custom resource (i.e. with everything that has schema that is not registered within kubectl). Tests are missing, too, but will come soon
* More tests * Example for kn service apply * Remove commented-out code
CLAs look good, thanks! ℹ️ Googlers: Go here for more info. |
The following is the coverage report on the affected files.
|
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
thank you!
I've added a few questions and a few nits suggestions which can be addressed in a subsequent PR along with CHANGELOG.
var waitFlags commands.WaitFlags | ||
|
||
serviceApplyCommand := &cobra.Command{ | ||
Use: "apply NAME", |
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.
NAME could be derived from the file as well, not sure what could be the best way to represent multiple options apply could work with. The examples better represent the use of the command in this case.
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.
true, we should think about a common scheme here. (e.g using [ ]
to describe optional positional arguments)
// can't be merged. Ideally a strategicpatch merge should be used, which allows a more fine grained | ||
// way for performing the merge (but this is not supported for custom resources) | ||
// See issue https://github.com/knative/client/issues/1073 for more details how this method should be | ||
// improved for a better merge strategy. |
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.
license header need to be swapped with imports
return nil, err | ||
} | ||
|
||
if annotate { |
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.
will there be a case when we'd want to control whether to annotate the service?
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.
Yes, there is. IIUR when you do the diff, then you don't want to have that annotation present. I would have to look a bit closer again, but there is a good reason. I also more or less took over the way how kubectl does the client side apply.
if len(containers.([]interface{})) == 0 { | ||
return nil | ||
} | ||
return containers.([]interface{})[0].(map[string]interface{}) |
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 am not sure about the position of the user-container in the array, is it always at 0 index (thinking about multi-container)?
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.
For the moment we are assuming only single, container, that's true. Maybe we should iterate over the list and just pick the container with name user-container
?
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 this only remove the name of the user-container? or for all the containers in array? If its later, we can simply range over the array and remove the names. I need to catch up on the multi container support to understand this better.
@@ -68,6 +69,17 @@ type KnServingClient interface { | |||
// place. | |||
UpdateServiceWithRetry(name string, updateFunc ServiceUpdateFunc, nrRetries int) error | |||
|
|||
// Apply a service's definition to the cluster. The full service declaration needs to be provided, | |||
// which is different to UpdateService which can also do a partial update. If the given | |||
// service does not already exists (identified by name) then the service is create. |
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.
// service does not already exists (identified by name) then the service is create. | |
// service does not already exists (identified by name) then the service is created. |
// which is different to UpdateService which can also do a partial update. If the given | ||
// service does not already exists (identified by name) then the service is create. | ||
// If the service exists, then a three-way merge will be performed between the original | ||
// configuration given (from the last "apply" operation), the new configuration as given ] |
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.
// configuration given (from the last "apply" operation), the new configuration as given ] | |
// configuration given (from the last "apply" operation), the new configuration as given |
"knative.dev/client/pkg/util" | ||
) | ||
|
||
func TestServiceApply(t *testing.T) { |
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.
we can also add another one e2e for apply with file
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.
👍
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: maximilien, navidshaikh, rhuss 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 |
This implementation reuses the
kubectl.kubernetes.io/last-applied-configuration
for storing the original configuration so that kubectl tooling can be applied, too.Only client-side patching is implemented as it's not clear to me whether server-side patching is implemented as part of the Knative API contract.
An initial
kn service apply
for creating a service works as expected, but a subsequentkn service apply
fails with a patch conflict errorThe following challenges are still open:
KService
, when serializing this so a JSON string to be added to the annotation mentioned above, fields that are typed to a struct (not a pointer to struct) are always present. This includescreatedTimestamp
orresources
in the container spec.kubectl
which uses alwaysUnstructured
doesn't suffer from this problem.name
of a container in thePodSpec
is marked as mandatory for Kubernetes, but is used optional in Knative (it will be autonamed "user-container" if not provided. So it is always present (even as empty string) when deserializing to JSON from aService
struct, indicating the intention of the user to set it to an empty string (which actually is not the case as we never specify that name). That very likely is one of the causes of the patch conflict (as the backend set it to "user-container").The idea is that we converted a Service to an
Unstructured
object and remove all map entries from which we know that they have not been specified. This work is still on-going.I added the PR nevertheless as
Draft
as I'm now on PTO for the next two weeks and very likely can't continue on this PR until then. If someone feels fancy and might take a look, feel free to work on this PR.Also no tests have been implemented yet.