add hook only waiter

Signed-off-by: Austin Abro <AustinAbro321@gmail.com>
pull/13604/head
Austin Abro 7 months ago
parent 2b03c527f1
commit f2dd2c9109
No known key found for this signature in database
GPG Key ID: 92EB5159E403F9D6

@ -54,7 +54,7 @@ func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) {
func AddWaitFlag(cmd *cobra.Command, wait *kube.WaitStrategy) {
cmd.Flags().Var(
newWaitValue(wait),
newWaitValue(kube.HookOnlyStrategy, wait),
"wait",
"if set, will wait until all resources are in the expected state before marking the operation as successful. It will wait for as long as --timeout. Options are (true, false, watcher, and legacy)",
)
@ -64,7 +64,8 @@ func AddWaitFlag(cmd *cobra.Command, wait *kube.WaitStrategy) {
type waitValue kube.WaitStrategy
func newWaitValue(ws *kube.WaitStrategy) *waitValue {
func newWaitValue(defaultValue kube.WaitStrategy, ws *kube.WaitStrategy) *waitValue {
*ws = defaultValue
return (*waitValue)(ws)
}
@ -77,7 +78,7 @@ func (ws *waitValue) String() string {
func (ws *waitValue) Set(s string) error {
switch s {
case string(kube.StatusWatcherStrategy), string(kube.LegacyWaiterStrategy):
case string(kube.StatusWatcherStrategy), string(kube.LegacyStrategy):
*ws = waitValue(s)
return nil
case "true":
@ -87,7 +88,7 @@ func (ws *waitValue) Set(s string) error {
*ws = ""
return nil
default:
return fmt.Errorf("invalid wait input %q. Valid inputs are true, false, %s, and %s", s, kube.StatusWatcherStrategy, kube.LegacyWaiterStrategy)
return fmt.Errorf("invalid wait input %q. Valid inputs are true, false, %s, and %s", s, kube.StatusWatcherStrategy, kube.LegacyStrategy)
}
}

@ -198,7 +198,7 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal
f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema")
f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used")
f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically to \"watcher\" if --atomic is used")
f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present")
f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation")

@ -76,10 +76,10 @@ func newUninstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during uninstallation")
f.BoolVar(&client.IgnoreNotFound, "ignore-not-found", false, `Treat "release not found" as a successful uninstall`)
f.BoolVar(&client.KeepHistory, "keep-history", false, "remove all associated resources and mark the release as deleted, but retain the release history")
f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all the resources are deleted before returning. It will wait for as long as --timeout")
f.StringVar(&client.DeletionPropagation, "cascade", "background", "Must be \"background\", \"orphan\", or \"foreground\". Selects the deletion cascading strategy for the dependents. Defaults to background.")
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.StringVar(&client.Description, "description", "", "add a custom description")
AddWaitFlag(cmd, &client.Wait)
return cmd
}

@ -279,7 +279,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.BoolVar(&client.ReuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored")
f.BoolVar(&client.ResetThenReuseValues, "reset-then-reuse-values", false, "when upgrading, reset the values to the ones built into the chart, apply the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' or '--reuse-values' is specified, this is ignored")
f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout")
f.BoolVar(&client.Atomic, "atomic", false, "if set, upgrade process rolls back changes made in case of failed upgrade. The --wait flag will be set automatically if --atomic is used")
f.BoolVar(&client.Atomic, "atomic", false, "if set, upgrade process rolls back changes made in case of failed upgrade. The --wait flag will be set automatically to \"watcher\" if --atomic is used")
f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit")
f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails")
f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")

@ -293,9 +293,9 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
// Make sure if Atomic is set, that wait is set as well. This makes it so
// the user doesn't have to specify both
if !i.shouldWait() {
if i.Wait == kube.HookOnlyStrategy {
if i.Atomic {
i.Wait = "watcher"
i.Wait = kube.StatusWatcherStrategy
}
}
@ -473,15 +473,13 @@ func (i *Install) performInstall(rel *release.Release, toBeAdopted kube.Resource
return rel, err
}
if i.shouldWait() {
if i.WaitForJobs {
err = i.cfg.KubeClient.WaitWithJobs(resources, i.Timeout)
} else {
err = i.cfg.KubeClient.Wait(resources, i.Timeout)
}
if err != nil {
return rel, err
}
if i.WaitForJobs {
err = i.cfg.KubeClient.WaitWithJobs(resources, i.Timeout)
} else {
err = i.cfg.KubeClient.Wait(resources, i.Timeout)
}
if err != nil {
return rel, err
}
if !i.DisableHooks {

@ -228,21 +228,19 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
}
}
if r.shouldWait() {
if r.WaitForJobs {
if err := r.cfg.KubeClient.WaitWithJobs(target, r.Timeout); err != nil {
targetRelease.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", targetRelease.Name, err.Error()))
r.cfg.recordRelease(currentRelease)
r.cfg.recordRelease(targetRelease)
return targetRelease, errors.Wrapf(err, "release %s failed", targetRelease.Name)
}
} else {
if err := r.cfg.KubeClient.Wait(target, r.Timeout); err != nil {
targetRelease.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", targetRelease.Name, err.Error()))
r.cfg.recordRelease(currentRelease)
r.cfg.recordRelease(targetRelease)
return targetRelease, errors.Wrapf(err, "release %s failed", targetRelease.Name)
}
if r.WaitForJobs {
if err := r.cfg.KubeClient.WaitWithJobs(target, r.Timeout); err != nil {
targetRelease.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", targetRelease.Name, err.Error()))
r.cfg.recordRelease(currentRelease)
r.cfg.recordRelease(targetRelease)
return targetRelease, errors.Wrapf(err, "release %s failed", targetRelease.Name)
}
} else {
if err := r.cfg.KubeClient.Wait(target, r.Timeout); err != nil {
targetRelease.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", targetRelease.Name, err.Error()))
r.cfg.recordRelease(currentRelease)
r.cfg.recordRelease(targetRelease)
return targetRelease, errors.Wrapf(err, "release %s failed", targetRelease.Name)
}
}

@ -41,7 +41,7 @@ type Uninstall struct {
DryRun bool
IgnoreNotFound bool
KeepHistory bool
Wait bool
Wait kube.WaitStrategy
DeletionPropagation string
Timeout time.Duration
Description string
@ -130,10 +130,8 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
}
res.Info = kept
if u.Wait {
if err := u.cfg.KubeClient.WaitForDelete(deletedResources, u.Timeout); err != nil {
errs = append(errs, err)
}
if err := u.cfg.KubeClient.WaitForDelete(deletedResources, u.Timeout); err != nil {
errs = append(errs, err)
}
if !u.DisableHooks {

@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/assert"
"helm.sh/helm/v4/pkg/kube"
kubefake "helm.sh/helm/v4/pkg/kube/fake"
"helm.sh/helm/v4/pkg/release"
)
@ -82,7 +83,7 @@ func TestUninstallRelease_Wait(t *testing.T) {
unAction := uninstallAction(t)
unAction.DisableHooks = true
unAction.DryRun = false
unAction.Wait = true
unAction.Wait = kube.StatusWatcherStrategy
rel := releaseStub()
rel.Name = "come-fail-away"
@ -113,7 +114,7 @@ func TestUninstallRelease_Cascade(t *testing.T) {
unAction := uninstallAction(t)
unAction.DisableHooks = true
unAction.DryRun = false
unAction.Wait = false
unAction.Wait = kube.HookOnlyStrategy
unAction.DeletionPropagation = "foreground"
rel := releaseStub()

@ -155,7 +155,7 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.
// Make sure if Atomic is set, that wait is set as well. This makes it so
// the user doesn't have to specify both
if !u.shouldWait() {
if u.Wait == kube.HookOnlyStrategy {
if u.Atomic {
u.Wait = kube.StatusWatcherStrategy
}
@ -190,10 +190,6 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.
return res, nil
}
func (u *Upgrade) shouldWait() bool {
return u.Wait != ""
}
// isDryRun returns true if Upgrade is set to run as a DryRun
func (u *Upgrade) isDryRun() bool {
if u.DryRun || u.DryRunOption == "client" || u.DryRunOption == "server" || u.DryRunOption == "true" {
@ -451,22 +447,17 @@ func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *rele
}
}
if u.shouldWait() {
u.cfg.Log(
"waiting for release %s resources (created: %d updated: %d deleted: %d)",
upgradedRelease.Name, len(results.Created), len(results.Updated), len(results.Deleted))
if u.WaitForJobs {
if err := u.cfg.KubeClient.WaitWithJobs(target, u.Timeout); err != nil {
u.cfg.recordRelease(originalRelease)
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
return
}
} else {
if err := u.cfg.KubeClient.Wait(target, u.Timeout); err != nil {
u.cfg.recordRelease(originalRelease)
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
return
}
if u.WaitForJobs {
if err := u.cfg.KubeClient.WaitWithJobs(target, u.Timeout); err != nil {
u.cfg.recordRelease(originalRelease)
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
return
}
} else {
if err := u.cfg.KubeClient.Wait(target, u.Timeout); err != nil {
u.cfg.recordRelease(originalRelease)
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
return
}
}
@ -534,7 +525,7 @@ func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, e
rollin := NewRollback(u.cfg)
rollin.Version = filteredHistory[0].Version
if !u.shouldWait() {
if u.Wait == kube.HookOnlyStrategy {
rollin.Wait = kube.StatusWatcherStrategy
}
rollin.WaitForJobs = u.WaitForJobs

@ -84,7 +84,8 @@ type WaitStrategy string
const (
StatusWatcherStrategy WaitStrategy = "watcher"
LegacyWaiterStrategy WaitStrategy = "legacy"
LegacyStrategy WaitStrategy = "legacy"
HookOnlyStrategy WaitStrategy = "noop"
)
func init() {
@ -98,36 +99,46 @@ func init() {
}
}
func (c *Client) newStatusWatcher() (*statusWaiter, error) {
cfg, err := c.Factory.ToRESTConfig()
if err != nil {
return nil, err
}
dynamicClient, err := c.Factory.DynamicClient()
if err != nil {
return nil, err
}
httpClient, err := rest.HTTPClientFor(cfg)
if err != nil {
return nil, err
}
restMapper, err := apiutil.NewDynamicRESTMapper(cfg, httpClient)
if err != nil {
return nil, err
}
return &statusWaiter{
restMapper: restMapper,
client: dynamicClient,
log: c.Log,
}, nil
}
func (c *Client) newWaiter(strategy WaitStrategy) (Waiter, error) {
switch strategy {
case LegacyWaiterStrategy:
case LegacyStrategy:
kc, err := c.Factory.KubernetesClientSet()
if err != nil {
return nil, err
}
return &HelmWaiter{kubeClient: kc, log: c.Log}, nil
case StatusWatcherStrategy:
cfg, err := c.Factory.ToRESTConfig()
if err != nil {
return nil, err
}
dynamicClient, err := c.Factory.DynamicClient()
if err != nil {
return nil, err
}
httpClient, err := rest.HTTPClientFor(cfg)
if err != nil {
return nil, err
}
restMapper, err := apiutil.NewDynamicRESTMapper(cfg, httpClient)
return c.newStatusWatcher()
case HookOnlyStrategy:
sw, err := c.newStatusWatcher()
if err != nil {
return nil, err
}
return &statusWaiter{
restMapper: restMapper,
client: dynamicClient,
log: c.Log,
}, nil
return &hookOnlyWaiter{sw: sw}, nil
default:
return nil, errors.New("unknown wait strategy")
}

@ -513,7 +513,7 @@ func TestWait(t *testing.T) {
}),
}
var err error
c.Waiter, err = c.newWaiter(LegacyWaiterStrategy)
c.Waiter, err = c.newWaiter(LegacyStrategy)
if err != nil {
t.Fatal(err)
}
@ -570,7 +570,7 @@ func TestWaitJob(t *testing.T) {
}),
}
var err error
c.Waiter, err = c.newWaiter(LegacyWaiterStrategy)
c.Waiter, err = c.newWaiter(LegacyStrategy)
if err != nil {
t.Fatal(err)
}
@ -629,7 +629,7 @@ func TestWaitDelete(t *testing.T) {
}),
}
var err error
c.Waiter, err = c.newWaiter(LegacyWaiterStrategy)
c.Waiter, err = c.newWaiter(LegacyStrategy)
if err != nil {
t.Fatal(err)
}

@ -209,3 +209,23 @@ func statusObserver(cancel context.CancelFunc, desired status.Status, logFn func
}
}
}
type hookOnlyWaiter struct {
sw *statusWaiter
}
func (w *hookOnlyWaiter) WatchUntilReady(resourceList ResourceList, timeout time.Duration) error {
return w.sw.WatchUntilReady(resourceList, timeout)
}
func (w *hookOnlyWaiter) Wait(resourceList ResourceList, timeout time.Duration) error {
return nil
}
func (w *hookOnlyWaiter) WaitWithJobs(resourceList ResourceList, timeout time.Duration) error {
return nil
}
func (w *hookOnlyWaiter) WaitForDelete(resourceList ResourceList, timeout time.Duration) error {
return nil
}

Loading…
Cancel
Save