Skip to content

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

Merged
merged 4 commits into from
Mar 14, 2023

Conversation

KnVerey
Copy link
Contributor

@KnVerey KnVerey commented Mar 3, 2023

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?

NONE

Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:

-->

- [KEP]: https://github.com/kubernetes/enhancements/tree/42a4b1c8a18780cf1d5c2460113d59b246002548/keps/sig-cli/3659-kubectl-apply-prune

/cc @justinsb @soltysh
/sig cli

@k8s-ci-robot k8s-ci-robot requested a review from justinsb March 3, 2023 23:08
@k8s-ci-robot k8s-ci-robot added the release-note-none Denotes a PR that doesn't merit a release note. label Mar 3, 2023
@k8s-ci-robot k8s-ci-robot requested a review from soltysh March 3, 2023 23:08
@k8s-ci-robot k8s-ci-robot added kind/feature Categorizes issue or PR as related to a new feature. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. sig/cli Categorizes an issue or PR as relevant to SIG CLI. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. labels Mar 3, 2023
@KnVerey
Copy link
Contributor Author

KnVerey commented Mar 3, 2023

/hold

This is based on #116243, which is pending merge

@k8s-ci-robot k8s-ci-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Mar 3, 2023
@k8s-ci-robot
Copy link
Contributor

[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 /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added area/kubectl approved Indicates a PR has been approved by an approver from all required OWNERS files. labels Mar 3, 2023
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)
Copy link
Contributor Author

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?

Copy link
Member

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!

Copy link
Contributor

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)
Copy link
Contributor Author

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.

Copy link
Member

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)
Copy link
Contributor Author

@KnVerey KnVerey Mar 3, 2023

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.

Copy link
Member

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...

Copy link
Contributor Author

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).

Copy link
Member

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?

@KnVerey KnVerey changed the title Applyset dry run Applyset dry run tests + ID value Mar 3, 2023
@KnVerey KnVerey force-pushed the applyset-dry-run branch from e4f49f3 to f18d1ba Compare March 3, 2023 23:23
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Mar 3, 2023
@KnVerey
Copy link
Contributor Author

KnVerey commented Mar 3, 2023

/unhold
Rebased and ready fo review!

/triage accepted
/priority important-longterm

@k8s-ci-robot k8s-ci-robot added triage/accepted Indicates an issue or PR is ready to be actively worked on. priority/important-longterm Important over the long term, but may not be staffed and/or may need multiple releases to complete. and removed do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. labels Mar 3, 2023
@KnVerey
Copy link
Contributor Author

KnVerey commented Mar 6, 2023

/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
Copy link
Member

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).

Copy link
Contributor Author

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.

Copy link
Member

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)

Copy link
Contributor Author

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)
Copy link
Member

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)
Copy link
Member

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)
Copy link
Member

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...

@justinsb
Copy link
Member

justinsb commented Mar 8, 2023

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:
https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#managedfieldsentry-v1-meta

Obvious answers are something like kubectl:<applysetid> or even just applyset:<applysetid> . I guess technically we can parse the managed fields and infer which FieldManager is managing the applyset annotation, but that is a lot more complicated.

Copy link
Contributor

@soltysh soltysh left a 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)
Copy link
Contributor

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 😄

@justinsb
Copy link
Member

Thanks - changes lgtm! Will apply lgtm whenever you're ready

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 13, 2023
@@ -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.")
Copy link
Contributor Author

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 🙈

@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 14, 2023
@KnVerey
Copy link
Contributor Author

KnVerey commented Mar 14, 2023

/label tide/merge-method-squash

Updated, rebased and ready to go! 🤞

@k8s-ci-robot k8s-ci-robot added the tide/merge-method-squash Denotes a PR that should be squashed by tide when it merges. label Mar 14, 2023
@justinsb
Copy link
Member

Thanks @KnVerey

/lgtm

I think we can tackle the field manager owner separately.

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Mar 14, 2023
@k8s-ci-robot
Copy link
Contributor

LGTM label has been added.

Git tree hash: 6b621b978960ba2ccf200e536024aa44fc79325c

@k8s-triage-robot
Copy link

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:

  • The PR does have any do-not-merge/* labels
  • The PR does not have the needs-ok-to-test label
  • The PR is mergeable (does not have a needs-rebase label)
  • The PR is approved (has cncf-cla: yes, lgtm, approved labels)
  • The PR is failing tests required for merge

You can:

/retest

@k8s-ci-robot k8s-ci-robot merged commit 6a31757 into kubernetes:master Mar 14, 2023
@k8s-ci-robot k8s-ci-robot added this to the v1.27 milestone Mar 14, 2023
@KnVerey KnVerey deleted the applyset-dry-run branch March 14, 2023 15:08
rayowang pushed a commit to rayowang/kubernetes that referenced this pull request Feb 9, 2024
* Test for ApplySet with --dry-run=client|server

* Use the real format for ApplySet ID

* Incorporate feedback

* Adjustments from rebase
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. area/kubectl cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/feature Categorizes issue or PR as related to a new feature. lgtm "Looks good to me", indicates that a PR is ready to be merged. priority/important-longterm Important over the long term, but may not be staffed and/or may need multiple releases to complete. release-note-none Denotes a PR that doesn't merit a release note. sig/cli Categorizes an issue or PR as relevant to SIG CLI. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. tide/merge-method-squash Denotes a PR that should be squashed by tide when it merges. triage/accepted Indicates an issue or PR is ready to be actively worked on.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants