constellation/cli/internal/helm/retryaction.go
Daniel Weiße 5706e69091
Retry helm apply on any error (#2322)
Signed-off-by: Daniel Weiße <dw@edgeless.systems>
2023-09-08 22:54:01 +02:00

72 lines
1.7 KiB
Go

/*
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
}