-
Notifications
You must be signed in to change notification settings - Fork 183
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
H4HIP: Wait with kstatus #374
base: main
Are you sure you want to change the base?
Changes from all commits
6d420e8
39a043c
c9dbf03
472f81a
dcf6c8b
c049e80
0c7da3f
467dde6
bb2b190
5275514
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
--- | ||
hip: 9999 | ||
title: "Wait With kstatus" | ||
authors: [ "@austinabro321" ] | ||
created: "2024-12-06" | ||
type: "feature" | ||
status: "draft" | ||
--- | ||
|
||
## Abstract | ||
|
||
Currently the `--wait` flag on `helm install` and `helm upgrade` does not wait for all resources to be fully reconciled, and does not wait for custom resources (CRs) at all. By replacing the wait logic with [kstatus](https://github.com/kubernetes-sigs/cli-utils/blob/master/pkg/kstatus/README.md), Helm will achieve more intuitive waits, while simplifying its code and documentation. | ||
|
||
## Motivation | ||
|
||
Certain workflows require custom resources to be ready. There is no way to tell Helm to wait for custom resources to be ready, so anyone with this requirement must write their own logic to wait for their custom resources. Kstatus solves this by waiting for custom resources to have [the ready condition](https://github.com/kubernetes-sigs/cli-utils/blob/master/pkg/kstatus/README.md#the-ready-condition) set to true. | ||
|
||
Certain workflows requires resources to be fully reconciled, which does happen in the current `--wait` logic. For example, Helm waits for all new pods in an upgraded deployment to be ready. However, Helm does not wait for the previous pods in that deployment to be removed. Since kstatus waits for all resources to be reconciled, the wait will not finish for a deployment until all of its new pods are ready and all of its old pods have been deleted. | ||
|
||
By introducing kstatus we will lower user friction with the `--wait` flag. | ||
|
||
## Rationale | ||
|
||
Leveraging a existing status management library maintained by the Kubernetes team will simplify the code and documentation that Helm needs to maintain and improve the functionality of `--wait`. | ||
|
||
## Specification | ||
|
||
From a CLI user's perspective there will be no changes in how waits are called, they will still use the `--wait` flag. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the below subject of compatibility, and the how how waits are action, we might want e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there cases where the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've not run into any issues with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given how extensively watches have been battle tested in k8s I think we can safely assume that using watches as the default solution and falling back to polling were that mechanism fails is sufficient. As mentioned in the other comment we should not expose internal information about how the wait logic works to users. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah that would definitely resolve the extra RBAC permissions. However, I do think it's worth considering the extra maintenance cost of adding both implementations. It might be worth adding both, but if we don't mind the extra 1-2 seconds between polls it may be worth just sticking with polling. Likewise, if we don't mind the additional "watch" RBAC permission required, it might make sense to only use watch. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the transition period, you'll likely end up with both, just to ensure people 1. update their RBAC and 2. update their expectations wrt additional wait time, which wasn't previously taken into account. Eventually allowing you to drop the polling entirely. At least, that's how I'd roll something like that in kubectl, for example 😉 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah I should clarify that there are two different types of polling here. The existing Helm wait implementation that has custom logic to poll resources, and the kstatus polling methods. I believe we'll keep the existing Helm implementation in the transition period, but I'm not sure we'll have both the kstatus polling and kstatus watcher. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That makes sense, I was alluding to the existing polling mechanism vs the new watch-based one, only. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be okay to have kstatus watch only. If user doesn't have RBAC for watch, they can fallback to the legacy mechanism while they sort out RBAC |
||
|
||
Kstatus does not output any logs. Helm will output a log message each second signalling that a resource is not ready. Helm will log one of the resources it's waiting on at a time as to not overwhelm users with output. This behavior will look similar to the current `--wait` logging. | ||
|
||
Kstatus can be used with either a poller or a watcher. The poller runs on a specified interval and only requires "list" RBAC permissions for polled resources. The watcher reacts to [watch events](https://github.com/kubernetes/kubernetes/blob/90a45563ae1bab5868ee888432fec9aac2f7f8b1/staging/src/k8s.io/apimachinery/pkg/watch/watch.go#L55-L61) and requires "list" and "watch" RBAC permissions. This proposal uses the watcher as it responds slightly faster when all resources are ready, and it is very likely that users applying or deleting resources will have "watch" permissions on their resources. However, if the additional RBAC permissions are deemed to cause potential issues the poller can be used instead. | ||
|
||
Any functions involving waits will be separated from the `kube.Interface` interface into the `kube.Waiter` interface. `kube.Waiter` will be embedded into `kube.Interface`. The client struct will embed the `Waiter` interface to allow calls to look like `client.Wait()` instead of `client.Waiter.Wait()`. `kube.New()` will accept a wait strategy to decide the wait implementation. Options will be either `HelmWaiter` or `statusWaiter`. `HelmWaiter` is the legacy implementation. The `statusWaiter` will not be public so that is kstatus is ever deprecated or replaced a new implementation can be used without changing the public SDK. | ||
|
||
The new client will look like: | ||
```go | ||
type Client struct { | ||
Factory Factory | ||
Log func(string, ...interface{}) | ||
Namespace string | ||
kubeClient *kubernetes.Clientset | ||
Waiter | ||
} | ||
type WaitStrategy int | ||
const ( | ||
StatusWaiter WaitStrategy = iota | ||
LegacyWaiter | ||
) | ||
func New(getter genericclioptions.RESTClientGetter, ws WaitStrategy) (*Client, error) | ||
``` | ||
|
||
The waiter interface will look like: | ||
```go | ||
type Waiter interface { | ||
Wait(resources ResourceList, timeout time.Duration) error | ||
WaitWithJobs(resources ResourceList, timeout time.Duration) error | ||
WaitForDelete(resources ResourceList, timeout time.Duration) error | ||
} | ||
``` | ||
|
||
`WaitAndGetCompletedPodPhase` is an exported function that is not called anywhere within the Helm repository. It will be removed. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't speak for helm maintainers, but it looks like this method is part of their public API, and not deprecated, so I'd be careful with removing it right away. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe since this is targeted at Helm v4 breaking changes in the public API are acceptable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made a PR to get rid of this immediately: helm/helm#13665 |
||
|
||
`WatchUntilReady` is used only for hooks. It has custom wait logic different from the Helm 3 general logic. Ideally, this could be replaced with a regular `Wait()` call. If there is any historical context as to why this logic is the way it is, please share. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again similar comment, I'd be careful breaking API. Either a deprecation or just wire the method to invoke the same underlying code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto with Helm 4 comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure why hooks have different logic. Hooks are typically jobs/pods, so perhaps the logic is very simple ie. check pod has exited?, would need to go look. That said, I have heard of folk using hooks for e.g. cluster pre-flight custom resources (@z4ce I think?) If we could analyze and/or test whether hooks can use kstatus, that would be good. Same "legacy" fallback comments apply. Worth mentioning too, the decision we make will remain in place for the lifetime of Helm 4 (ie. we won't change the default once Helm 4 is released) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Took a look at the hook logic and it waits for pods and jobs to be completed rather than just ready. I believe this can be done with kstatus, and I will verify later this week. I took a look at how Flux does it and it seems like we could implement the same logic with kstatus, will just involve custom code. Assuming my assumptions are correct, we will keep the Flux Job wait implementation - https://github.com/fluxcd/kustomize-controller/blob/main/internal/statusreaders/job.go |
||
|
||
The Helm CLI will always use the `statusWaiter` implementation, if this is found to be insufficient during Helm 4 testing a new cli flag `wait-strategy` will be introduced with options `status` and `legacy` to allow usage of the `HelmWaiter`. If the `statusWaiter` is found to be sufficient the `HelmWaiter` will be deleted from the public SDK before Helm 4 is released. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can used the existing --wait flag e.g. Up for suggestions on whether we use the name "kstatus" in the flag. Or whether we consider this an "implementation detail". I suspect we will want to refer users to kstatus docs generally for behavioral reference, so it is okay to refer to kstatus directly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only hiccup I see with structuring the Other option would be introducing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about |
||
|
||
The current Helm wait logic does not wait for paused deployments to be ready. This is because if `helm upgrade --wait` is run on a chart with paused deployments they will never be ready, see [#5789](https://github.com/helm/helm/pull/5789). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: this seems more like motivation that specification |
||
|
||
## Backwards compatibility | ||
AustinAbro321 marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any situation where There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Besides the two called our here, where kstatus will wait to return ready until reconciliation is complete, and waiting for CRDs I am not thinking of any, but I am not 100% sure. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect, to be on the safe side, we will want/need to support a fallback flag to allow users to fallback to the "legacy" wait/ready code. Ideally we would deprecate that flag e.g. Helm 5 and remove the old code in a future version. Not sure what we would do if the legacy code serves some behavior that kstatus doesn't (ideally this doesn't happen of course), but I think we should allow users the fallback when upgrading to Helm 4 just in case. |
||
|
||
Waiting for custom resources and for reconciliation to complete for every resource could lead to charts timing out that weren't previously. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering if we want an "opt-in" (or opt-out) mechanism for charts to specify they are compatible with new a new ready logic? At least initially. And/or a CLI flag for users to control the behavior? While one of the premises of Helm 4 is that we can/do want to move Helm functionality forward. We do want/need to remain compatible with existing user workflows as much as possible. So while it would certainly be okay to introduce new wait functionality, I think we would want a path for users to either fall back to the old functionality if their current situation warranted. Or for a chart to opt-in to the new functionality, if the chart author could deem the chart to be compatible with the new functionality. What we should do IMHO depends on how much we think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will leave the final call to you guys, I suspect kstatus will be a drop in replacement. I'm not sure if it will work 90%, 99%, or 99.9% of the time with existing deployments. I think it's most likely closer to the latter percentages, but I would love a way to test that out and gain additional confidence. My confidence so far comes from the fact that in Zarf, we changed the logic so kstatus is run by default for all charts without There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Helm seems to have a feature gate capability. I'd imagine we can start with dropping kstatus as an experimental feature which would allow interested users to switch to new logic, and slowly rollout the change over several releases. Eventually going to a point where kstatus will be the new default. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think best we aim to make kstatus the default for Helm 4. And include a CLI flag to allow users to fallback to the old mechanism (just in case; see comment above). Then we have a nice enough user story: user upgrade to Helm 4, finds a problem with waiting, reads the release notes / discovers the flag, disables kstatus |
||
|
||
The kstatus status watcher requires the "list" and "watch" RBAC permissions to watch a resource. The kstatus status poller and current Helm implementation only require "list" permissions for the resources they're watching. Kstatus and Helm require "list" permissions for some child resources. For instance, checking if a deployment is ready requires "list" permissions for the replicaset. There may be cases where the RBAC requirements for child resources differ between Kstatus and Helm, as an evaluation has not been conducted. | ||
|
||
Below is the minimal set needed to watch a deployment with the status watcher. This can be verified by following instructions in this repo: https://github.com/AustinAbro321/kstatus-rbac-test. | ||
```yaml | ||
rules: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is extensive enough. I suspect kstatus will need watch and list for every resource type in a chart (not just apps) In particular, Helm only considered a few resources types previously. This is fine, just want the HIP to be accurate |
||
- apiGroups: ["apps"] | ||
resources: ["deployments"] | ||
verbs: ["list", "watch"] # The status poller only requires "list" here | ||
- apiGroups: ["apps"] | ||
resources: ["replicasets"] | ||
verbs: ["list"] | ||
``` | ||
|
||
|
||
## Security implications | ||
|
||
Users will now need "watch" permissions on resources in their chart if `--wait` is used, assuming the status watcher is used over the status poller. | ||
|
||
## How to teach this | ||
|
||
Replace the [existing wait documentation](https://helm.sh/docs/intro/using_helm/) by explaining that we use kstatus and pointing to the [kstatus documentation](https://github.com/kubernetes-sigs/cli-utils/blob/master/pkg/kstatus/README.md). This comes with the added benefit of not needing to maintain Helm specific wait documentation. | ||
|
||
## Reference implementation | ||
|
||
seen here - https://github.com/helm/helm/pull/13604 | ||
|
||
## Rejected ideas | ||
|
||
TBD | ||
|
||
## Open issues | ||
|
||
[8661](https://github.com/helm/helm/issues/8661) | ||
|
||
## References | ||
|
||
existing wait documentation - https://helm.sh/docs/intro/using_helm/ | ||
kstatus documentation - https://github.com/kubernetes-sigs/cli-utils/blob/master/pkg/kstatus/README.md | ||
Zarf kstatus implementation - https://github.com/zarf-dev/zarf/blob/main/src/internal/healthchecks/healthchecks.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 would like to see kstatus behind an adapter/interface. Helm should use it but not expose it in the API. There are two reasons I would like to see this:
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.
Makes perfect sense, I'll add that to the doc.