/* Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ package helm import ( "context" "fmt" "time" "github.com/edgelesssys/constellation/v2/internal/retry" ) const ( // maximumRetryAttempts is the maximum number of attempts to retry a helm install. maximumRetryAttempts = 3 ) type retrieableApplier interface { apply(context.Context) error ReleaseName() string IsAtomic() bool } // retryApply retries the given retriable action. func retryApply(ctx context.Context, action retrieableApplier, retryInterval time.Duration, log debugLog) error { var retries int retriable := func(err error) bool { // abort after maximumRetryAttempts tries. if retries >= maximumRetryAttempts { return false } retries++ // only retry if atomic is set // otherwise helm doesn't uninstall the release on failure return action.IsAtomic() } doer := applyDoer{ applier: action, log: log, } retrier := retry.NewIntervalRetrier(doer, retryInterval, retriable) retryLoopStartTime := time.Now() if err := retrier.Do(ctx); err != nil { return fmt.Errorf("helm install: %w", err) } retryLoopFinishDuration := time.Since(retryLoopStartTime) log.Debugf("Helm chart %q installation finished after %s", action.ReleaseName(), retryLoopFinishDuration) return nil } // applyDoer is a helper struct to enable retrying helm actions. type applyDoer struct { applier retrieableApplier log debugLog } // Do tries to apply the action. func (i applyDoer) Do(ctx context.Context) error { i.log.Debugf("Trying to apply Helm chart %s", i.applier.ReleaseName()) if err := i.applier.apply(ctx); err != nil { i.log.Debugf("Helm chart installation %s failed: %v", i.applier.ReleaseName(), err) return err } return nil }