-
Notifications
You must be signed in to change notification settings - Fork 40.8k
Applyset dry run tests + ID value #116265
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
Conversation
/hold This is based on #116243, which is pending merge |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: KnVerey 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 |
if a.parentRef.IsNamespaced() { | ||
unencoded = strings.Join([]string{a.parentRef.Name, a.parentRef.Namespace, a.parentRef.GroupVersionKind.Kind, a.parentRef.GroupVersionKind.Group}, applySetIDPartDelimiter) | ||
} else { | ||
unencoded = strings.Join([]string{a.parentRef.Name, a.parentRef.GroupVersionKind.Kind, a.parentRef.GroupVersionKind.Group}, applySetIDPartDelimiter) |
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.
Is this what we want for non-namespaced? The omission of a segment + use of a delimiter that appears inside the last segment could make correct parsing of the value difficult... but AFAIK tooling should only ever reconstruct this for verification, not attempt to parse it. 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.
Good question. I'd argue for including the empty namespace (i.e. be consistent even if less aesthetically pleasing), because no human is going to notice the empty value once it goes through sha256/base64, and if we ever do need to parse it, then the ambiguous form is going to cause us trouble.
I think we might need to / want to parse it in our fsck
command. But otherwise, yes I agree it should not be parsed.
This is not a strongly held opinion - if you want to go the other way that works too!
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.
Yeah, I agree with Justin here, just make it consistent by concatenating all the elements, empty namespace is equally valid, and this will make the code easier to read as well 😄
encoder := base64.NewEncoder(base64.URLEncoding, &w) | ||
_, err := encoder.Write([]byte(unencoded)) | ||
if err != nil { | ||
klog.Fatalf("failed to encode parent ID %s: %w", unencoded, 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.
Is this unlikely enough that the panic is ok? Otherwise we'll need error handling everywhere we use the ID.
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.
Can we use EncodeToString
? Then you avoid the issue (or rather let the base64 library make the assumption!) Your assumption is valid here though, I believe.
It looks like the sha256 is also missing BTW, and there I would similarly use Sum256
to avoid error handling
@@ -384,7 +400,7 @@ func generateResourcesAnnotation(resources sets.Set[schema.GroupVersionResource] | |||
} | |||
|
|||
func (a ApplySet) FieldManager() string { | |||
return fmt.Sprintf("%s-applyset-%s", a.toolingID.name, a.ID()) // TODO: validate this choice | |||
return fmt.Sprintf("%s-applyset", a.toolingID.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.
I'm thinking that the inclusion of the ApplySet ID in the manager name was overly specific, particularly since this is only used to patch the parent itself (so it is a self-reference). We should make sure we all agree on what to do here, since changing it after anyone has tried this on a real object will be disruptive.
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.
Agreed that we need to think about it early. I think the more valuable one to tie back to an applyset is the "child objects" - do we get to pick the field manager there (when using SSA)? Or can we annotate in some way? If we could explicitly that field manager back to say "these are the objects managed in this applyset, and these are the specific fields managed", then I think that would really be making SSA work for us...
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.
In theory we could use a separate field manager of our choice when setting our labels on the children instead of adding them to the object prior to the main operation. However, the field manager is set per-request: PATCH https://127.0.0.1:51962/apis/company.com/v1/applysets/my-set?fieldManager=kubectl-applyset&fieldValidation=Strict&force=false
<-- note the first query param. So that would necessarily double the number of patches sent during an apply, which I suspect is unacceptable / not worth the benefit.
We could override the field manager name used for the main operation to be some value related to ApplySet, but that would have some undesired effects. First, there's CSA<->SSA migration/compatibility code (including on the server) that is triggered when the default names are used, so if we override the names, then ApplySet use makes a CSA->SSA upgrade non-rollback-safe. Second, the field manager name is exposed as a flag, so the user can override it.
Another thing I realized in writing this out is that we can't use the field manager here as in the main operation if we want to leave the door open to having the parent present in the main set. Allowing the parent to be in the main set would make ApplySet easier to use with custom resources, which may have required fields we can't guess values for. And if we used the same field manager name here, we'd effectively be requesting deletion of those extra fields.
Another option that occurred to me is using the field manager name from the main operation as the prefix, instead of the tooling name. That could work for SSA and for the case where the user overrides the name, but CSA has a different default name, and we don't want a CSA->SSA migration to have any effect on this (plus the CSA name literally has '-client-side-apply` in it, which is inaccurate for us).
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 points all. I was thinking about the "main" apply. TBH my instinct was to have one field manager name for everything we apply, though I do now see that will introduce some very tricky edge cases for the parent.
I think CSA -> SSA means that we can't really mess with this, unless we were willing to make prunev2 SSA-only.
Maybe we should think about - easy enough to relax later and might avoid opening some as-yet-unknown cans of worms?
e4f49f3
to
f18d1ba
Compare
/unhold /triage accepted |
/test pull-kubernetes-conformance-kind-ga-only-parallel |
@@ -2361,7 +2361,7 @@ metadata: | |||
applyset.k8s.io/tooling: kubectl/v0.0.0-master+$Format:%H$ | |||
creationTimestamp: null | |||
labels: | |||
applyset.k8s.io/id: placeholder-todo | |||
applyset.k8s.io/id: bXlTZXQudGVzdC5TZWNyZXQu |
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 value looks pretty reasonable (but is maybe too short?). I just checked the label conditions and spotted "unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z])," https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
So we should add a consistent prefix and suffix, or only add them when the first/last character is the one of the two "odd" base64 characters (+/ or -_)
Maybe something like "as-" or even applyset-
We could also use base32 if that's easier, but personally I prefer a constant prefix (the suffix I find less useful, but also nobody is going to notice if we put an extra a
on the end).
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 agree probably nobody would notice an extra "a" (or any other letter), but in the PR you made to my PR, you used -1
. That looks like a potential sequence... is that intentional? Maybe another option could be a version suffix like v1
or something? Because tools are supposed to validate the values of this, we're going to have to dictate this suffix/prefix too I assume.
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, I was thinking if we have to add something, we might as well add some sort of version identifier. That could be "-v1", "-v2" (as I think you did), that could be -a
, -b
etc if we wanted to make it a little less explicit. I like -v1
and as we have to dictate what to do if the last value of the base64 encoding is not alphanumeric, so I suggest we use this for a version marker (even if we never use it)
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.
Yep, v1
is what I went with!
if a.parentRef.IsNamespaced() { | ||
unencoded = strings.Join([]string{a.parentRef.Name, a.parentRef.Namespace, a.parentRef.GroupVersionKind.Kind, a.parentRef.GroupVersionKind.Group}, applySetIDPartDelimiter) | ||
} else { | ||
unencoded = strings.Join([]string{a.parentRef.Name, a.parentRef.GroupVersionKind.Kind, a.parentRef.GroupVersionKind.Group}, applySetIDPartDelimiter) |
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 question. I'd argue for including the empty namespace (i.e. be consistent even if less aesthetically pleasing), because no human is going to notice the empty value once it goes through sha256/base64, and if we ever do need to parse it, then the ambiguous form is going to cause us trouble.
I think we might need to / want to parse it in our fsck
command. But otherwise, yes I agree it should not be parsed.
This is not a strongly held opinion - if you want to go the other way that works too!
encoder := base64.NewEncoder(base64.URLEncoding, &w) | ||
_, err := encoder.Write([]byte(unencoded)) | ||
if err != nil { | ||
klog.Fatalf("failed to encode parent ID %s: %w", unencoded, 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.
Can we use EncodeToString
? Then you avoid the issue (or rather let the base64 library make the assumption!) Your assumption is valid here though, I believe.
It looks like the sha256 is also missing BTW, and there I would similarly use Sum256
to avoid error handling
@@ -384,7 +400,7 @@ func generateResourcesAnnotation(resources sets.Set[schema.GroupVersionResource] | |||
} | |||
|
|||
func (a ApplySet) FieldManager() string { | |||
return fmt.Sprintf("%s-applyset-%s", a.toolingID.name, a.ID()) // TODO: validate this choice | |||
return fmt.Sprintf("%s-applyset", a.toolingID.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.
Agreed that we need to think about it early. I think the more valuable one to tie back to an applyset is the "child objects" - do we get to pick the field manager there (when using SSA)? Or can we annotate in some way? If we could explicitly that field manager back to say "these are the objects managed in this applyset, and these are the specific fields managed", then I think that would really be making SSA work for us...
Looks good (though I think we need a sha256 and probably a prefix & suffix). The question re field manager is really interesting - it would be really powerful to be able to tie the fields back to the applyset IMO. I think previously we have used a generic field manager name ("kubectl"?), which doesn't really help. We don't have any other fields that we can (obviously) set: Obvious answers are something like |
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.
One comment, otherwise this is good as is.
if a.parentRef.IsNamespaced() { | ||
unencoded = strings.Join([]string{a.parentRef.Name, a.parentRef.Namespace, a.parentRef.GroupVersionKind.Kind, a.parentRef.GroupVersionKind.Group}, applySetIDPartDelimiter) | ||
} else { | ||
unencoded = strings.Join([]string{a.parentRef.Name, a.parentRef.GroupVersionKind.Kind, a.parentRef.GroupVersionKind.Group}, applySetIDPartDelimiter) |
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.
Yeah, I agree with Justin here, just make it consistent by concatenating all the elements, empty namespace is equally valid, and this will make the code easier to read as well 😄
Thanks - changes lgtm! Will apply lgtm whenever you're ready |
a876b29
to
1a2f4e8
Compare
@@ -419,8 +418,6 @@ func (o *ApplyOptions) Validate() error { | |||
return fmt.Errorf("--selector is incompatible with --applyset") | |||
} else if len(o.PruneResources) > 0 { | |||
return fmt.Errorf("--prune-allowlist is incompatible with --applyset") | |||
} else { | |||
klog.Warning("WARNING: --prune --applyset is not fully implemented and does not yet prune any resources.") |
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 should have removed this in the last PR 🙈
/label tide/merge-method-squash Updated, rebased and ready to go! 🤞 |
Thanks @KnVerey /lgtm I think we can tackle the field manager owner separately. |
LGTM label has been added. Git tree hash: 6b621b978960ba2ccf200e536024aa44fc79325c
|
The Kubernetes project has merge-blocking tests that are currently too flaky to consistently pass. This bot retests PRs for certain kubernetes repos according to the following rules:
You can:
/retest |
* Test for ApplySet with --dry-run=client|server * Use the real format for ApplySet ID * Incorporate feedback * Adjustments from rebase
What type of PR is this?
/kind feature
What this PR does / why we need it:
This is part of a series of PRs to implement the ApplySet KEP in kubectl: http://git.k8s.io/enhancements/keps/sig-cli/3659-kubectl-apply-prune.
This one fixes a few of the small tasks on our new project board: https://github.com/orgs/kubernetes/projects/128/views/1
Related PRs:
Does this PR introduce a user-facing change?
Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:
-->
/cc @justinsb @soltysh
/sig cli