mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-01-11 15:39:33 -05:00
cli: add "--skip-helm-wait" flag (#2061)
* cli: add "--skip-helm-wait" flag This flag can be used to disable the atomic and wait flags during helm install. This is useful when debugging a failing constellation init, since the user gains access to the cluster even when one of the deployments is never in a ready state.
This commit is contained in:
parent
7e83991154
commit
1ff40533f1
@ -35,6 +35,8 @@ import (
|
|||||||
const (
|
const (
|
||||||
// timeout is the maximum time given to the helm client.
|
// timeout is the maximum time given to the helm client.
|
||||||
timeout = 5 * time.Minute
|
timeout = 5 * time.Minute
|
||||||
|
// maximumRetryAttempts is the maximum number of attempts to retry a helm install.
|
||||||
|
maximumRetryAttempts = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client is used to install microservice during cluster initialization. It is a wrapper for a helm install action.
|
// Client is used to install microservice during cluster initialization. It is a wrapper for a helm install action.
|
||||||
@ -57,8 +59,6 @@ func New(log *logger.Logger) (*Client, error) {
|
|||||||
action := action.NewInstall(actionConfig)
|
action := action.NewInstall(actionConfig)
|
||||||
action.Namespace = constants.HelmNamespace
|
action.Namespace = constants.HelmNamespace
|
||||||
action.Timeout = timeout
|
action.Timeout = timeout
|
||||||
action.Atomic = true
|
|
||||||
action.Wait = true
|
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
action,
|
action,
|
||||||
@ -69,6 +69,9 @@ func New(log *logger.Logger) (*Client, error) {
|
|||||||
// InstallConstellationServices installs the constellation-services chart. In the future this chart should bundle all microservices.
|
// InstallConstellationServices installs the constellation-services chart. In the future this chart should bundle all microservices.
|
||||||
func (h *Client) InstallConstellationServices(ctx context.Context, release helm.Release, extraVals map[string]any) error {
|
func (h *Client) InstallConstellationServices(ctx context.Context, release helm.Release, extraVals map[string]any) error {
|
||||||
h.ReleaseName = release.ReleaseName
|
h.ReleaseName = release.ReleaseName
|
||||||
|
if err := h.setWaitMode(release.WaitMode); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
mergedVals := helm.MergeMaps(release.Values, extraVals)
|
mergedVals := helm.MergeMaps(release.Values, extraVals)
|
||||||
|
|
||||||
@ -79,6 +82,9 @@ func (h *Client) InstallConstellationServices(ctx context.Context, release helm.
|
|||||||
func (h *Client) InstallCertManager(ctx context.Context, release helm.Release) error {
|
func (h *Client) InstallCertManager(ctx context.Context, release helm.Release) error {
|
||||||
h.ReleaseName = release.ReleaseName
|
h.ReleaseName = release.ReleaseName
|
||||||
h.Timeout = 10 * time.Minute
|
h.Timeout = 10 * time.Minute
|
||||||
|
if err := h.setWaitMode(release.WaitMode); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return h.install(ctx, release.Chart, release.Values)
|
return h.install(ctx, release.Chart, release.Values)
|
||||||
}
|
}
|
||||||
@ -86,6 +92,9 @@ func (h *Client) InstallCertManager(ctx context.Context, release helm.Release) e
|
|||||||
// InstallOperators installs the Constellation Operators.
|
// InstallOperators installs the Constellation Operators.
|
||||||
func (h *Client) InstallOperators(ctx context.Context, release helm.Release, extraVals map[string]any) error {
|
func (h *Client) InstallOperators(ctx context.Context, release helm.Release, extraVals map[string]any) error {
|
||||||
h.ReleaseName = release.ReleaseName
|
h.ReleaseName = release.ReleaseName
|
||||||
|
if err := h.setWaitMode(release.WaitMode); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
mergedVals := helm.MergeMaps(release.Values, extraVals)
|
mergedVals := helm.MergeMaps(release.Values, extraVals)
|
||||||
|
|
||||||
@ -95,6 +104,9 @@ func (h *Client) InstallOperators(ctx context.Context, release helm.Release, ext
|
|||||||
// InstallCilium sets up the cilium pod network.
|
// InstallCilium sets up the cilium pod network.
|
||||||
func (h *Client) InstallCilium(ctx context.Context, kubectl k8sapi.Client, release helm.Release, in k8sapi.SetupPodNetworkInput) error {
|
func (h *Client) InstallCilium(ctx context.Context, kubectl k8sapi.Client, release helm.Release, in k8sapi.SetupPodNetworkInput) error {
|
||||||
h.ReleaseName = release.ReleaseName
|
h.ReleaseName = release.ReleaseName
|
||||||
|
if err := h.setWaitMode(release.WaitMode); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
timeoutS := int64(10)
|
timeoutS := int64(10)
|
||||||
// allow coredns to run on uninitialized nodes (required by cloud-controller-manager)
|
// allow coredns to run on uninitialized nodes (required by cloud-controller-manager)
|
||||||
@ -164,9 +176,22 @@ func (h *Client) installCiliumGCP(ctx context.Context, release helm.Release, nod
|
|||||||
|
|
||||||
// install tries to install the given chart and aborts after ~5 tries.
|
// install tries to install the given chart and aborts after ~5 tries.
|
||||||
// The function will wait 30 seconds before retrying a failed installation attempt.
|
// The function will wait 30 seconds before retrying a failed installation attempt.
|
||||||
// After 10 minutes the retrier will be canceled and the function returns with an error.
|
// After 3 tries, the retrier will be canceled and the function returns with an error.
|
||||||
func (h *Client) install(ctx context.Context, chartRaw []byte, values map[string]any) error {
|
func (h *Client) install(ctx context.Context, chartRaw []byte, values map[string]any) error {
|
||||||
|
var retries int
|
||||||
retriable := func(err error) bool {
|
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
|
||||||
|
if !h.Atomic {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// check if error is retriable
|
||||||
return wait.Interrupted(err) ||
|
return wait.Interrupted(err) ||
|
||||||
strings.Contains(err.Error(), "connection refused")
|
strings.Contains(err.Error(), "connection refused")
|
||||||
}
|
}
|
||||||
@ -185,14 +210,8 @@ func (h *Client) install(ctx context.Context, chartRaw []byte, values map[string
|
|||||||
}
|
}
|
||||||
retrier := retry.NewIntervalRetrier(doer, 30*time.Second, retriable)
|
retrier := retry.NewIntervalRetrier(doer, 30*time.Second, retriable)
|
||||||
|
|
||||||
// Since we have no precise retry condition we want to stop retrying after 10 minutes.
|
|
||||||
// The helm library only reports a timeout error in the error cases we currently know.
|
|
||||||
// Other errors will not be retried.
|
|
||||||
newCtx, cancel := context.WithTimeout(ctx, 10*time.Minute)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
retryLoopStartTime := time.Now()
|
retryLoopStartTime := time.Now()
|
||||||
if err := retrier.Do(newCtx); err != nil {
|
if err := retrier.Do(ctx); err != nil {
|
||||||
return fmt.Errorf("helm install: %w", err)
|
return fmt.Errorf("helm install: %w", err)
|
||||||
}
|
}
|
||||||
retryLoopFinishDuration := time.Since(retryLoopStartTime)
|
retryLoopFinishDuration := time.Since(retryLoopStartTime)
|
||||||
@ -201,6 +220,23 @@ func (h *Client) install(ctx context.Context, chartRaw []byte, values map[string
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Client) setWaitMode(waitMode helm.WaitMode) error {
|
||||||
|
switch waitMode {
|
||||||
|
case helm.WaitModeNone:
|
||||||
|
h.Wait = false
|
||||||
|
h.Atomic = false
|
||||||
|
case helm.WaitModeWait:
|
||||||
|
h.Wait = true
|
||||||
|
h.Atomic = false
|
||||||
|
case helm.WaitModeAtomic:
|
||||||
|
h.Wait = true
|
||||||
|
h.Atomic = true
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown wait mode %q", waitMode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// installDoer is a help struct to enable retrying helm's install action.
|
// installDoer is a help struct to enable retrying helm's install action.
|
||||||
type installDoer struct {
|
type installDoer struct {
|
||||||
client *Client
|
client *Client
|
||||||
|
@ -65,6 +65,7 @@ go_library(
|
|||||||
"//internal/config/migration",
|
"//internal/config/migration",
|
||||||
"//internal/constants",
|
"//internal/constants",
|
||||||
"//internal/crypto",
|
"//internal/crypto",
|
||||||
|
"//internal/deploy/helm",
|
||||||
"//internal/file",
|
"//internal/file",
|
||||||
"//internal/grpc/dialer",
|
"//internal/grpc/dialer",
|
||||||
"//internal/grpc/grpclog",
|
"//internal/grpc/grpclog",
|
||||||
|
@ -43,6 +43,7 @@ import (
|
|||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/crypto"
|
"github.com/edgelesssys/constellation/v2/internal/crypto"
|
||||||
|
helmdeploy "github.com/edgelesssys/constellation/v2/internal/deploy/helm"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/grpc/dialer"
|
"github.com/edgelesssys/constellation/v2/internal/grpc/dialer"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/grpc/grpclog"
|
"github.com/edgelesssys/constellation/v2/internal/grpc/grpclog"
|
||||||
@ -65,6 +66,7 @@ func NewInitCmd() *cobra.Command {
|
|||||||
}
|
}
|
||||||
cmd.Flags().String("master-secret", "", "path to base64-encoded master secret")
|
cmd.Flags().String("master-secret", "", "path to base64-encoded master secret")
|
||||||
cmd.Flags().Bool("conformance", false, "enable conformance mode")
|
cmd.Flags().Bool("conformance", false, "enable conformance mode")
|
||||||
|
cmd.Flags().Bool("skip-helm-wait", false, "install helm charts without waiting for deployments to be ready")
|
||||||
cmd.Flags().Bool("merge-kubeconfig", false, "merge Constellation kubeconfig file with default kubeconfig file in $HOME/.kube/config")
|
cmd.Flags().Bool("merge-kubeconfig", false, "merge Constellation kubeconfig file with default kubeconfig file in $HOME/.kube/config")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
@ -174,7 +176,7 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator atls.V
|
|||||||
}
|
}
|
||||||
helmLoader := helm.NewLoader(provider, k8sVersion)
|
helmLoader := helm.NewLoader(provider, k8sVersion)
|
||||||
i.log.Debugf("Created new Helm loader")
|
i.log.Debugf("Created new Helm loader")
|
||||||
helmDeployments, err := helmLoader.Load(conf, flags.conformance, masterSecret.Key, masterSecret.Salt)
|
helmDeployments, err := helmLoader.Load(conf, flags.conformance, flags.helmWaitMode, masterSecret.Key, masterSecret.Salt)
|
||||||
i.log.Debugf("Loaded Helm deployments")
|
i.log.Debugf("Loaded Helm deployments")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("loading Helm charts: %w", err)
|
return fmt.Errorf("loading Helm charts: %w", err)
|
||||||
@ -409,6 +411,15 @@ func (i *initCmd) evalFlagArgs(cmd *cobra.Command) (initFlags, error) {
|
|||||||
return initFlags{}, fmt.Errorf("parsing conformance flag: %w", err)
|
return initFlags{}, fmt.Errorf("parsing conformance flag: %w", err)
|
||||||
}
|
}
|
||||||
i.log.Debugf("Conformance flag is %t", conformance)
|
i.log.Debugf("Conformance flag is %t", conformance)
|
||||||
|
skipHelmWait, err := cmd.Flags().GetBool("skip-helm-wait")
|
||||||
|
if err != nil {
|
||||||
|
return initFlags{}, fmt.Errorf("parsing skip-helm-wait flag: %w", err)
|
||||||
|
}
|
||||||
|
helmWaitMode := helmdeploy.WaitModeAtomic
|
||||||
|
if skipHelmWait {
|
||||||
|
helmWaitMode = helmdeploy.WaitModeNone
|
||||||
|
}
|
||||||
|
i.log.Debugf("Helm wait flag is %t", skipHelmWait)
|
||||||
configPath, err := cmd.Flags().GetString("config")
|
configPath, err := cmd.Flags().GetString("config")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return initFlags{}, fmt.Errorf("parsing config path flag: %w", err)
|
return initFlags{}, fmt.Errorf("parsing config path flag: %w", err)
|
||||||
@ -429,6 +440,7 @@ func (i *initCmd) evalFlagArgs(cmd *cobra.Command) (initFlags, error) {
|
|||||||
return initFlags{
|
return initFlags{
|
||||||
configPath: configPath,
|
configPath: configPath,
|
||||||
conformance: conformance,
|
conformance: conformance,
|
||||||
|
helmWaitMode: helmWaitMode,
|
||||||
masterSecretPath: masterSecretPath,
|
masterSecretPath: masterSecretPath,
|
||||||
force: force,
|
force: force,
|
||||||
mergeConfigs: mergeConfigs,
|
mergeConfigs: mergeConfigs,
|
||||||
@ -440,6 +452,7 @@ type initFlags struct {
|
|||||||
configPath string
|
configPath string
|
||||||
masterSecretPath string
|
masterSecretPath string
|
||||||
conformance bool
|
conformance bool
|
||||||
|
helmWaitMode helmdeploy.WaitMode
|
||||||
force bool
|
force bool
|
||||||
mergeConfigs bool
|
mergeConfigs bool
|
||||||
}
|
}
|
||||||
|
@ -101,24 +101,24 @@ func NewLoader(csp cloudprovider.Provider, k8sVersion versions.ValidK8sVersion)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load the embedded helm charts.
|
// Load the embedded helm charts.
|
||||||
func (i *ChartLoader) Load(config *config.Config, conformanceMode bool, masterSecret, salt []byte) ([]byte, error) {
|
func (i *ChartLoader) Load(config *config.Config, conformanceMode bool, helmWaitMode helm.WaitMode, masterSecret, salt []byte) ([]byte, error) {
|
||||||
ciliumRelease, err := i.loadRelease(ciliumInfo)
|
ciliumRelease, err := i.loadRelease(ciliumInfo, helmWaitMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("loading cilium: %w", err)
|
return nil, fmt.Errorf("loading cilium: %w", err)
|
||||||
}
|
}
|
||||||
extendCiliumValues(ciliumRelease.Values, conformanceMode)
|
extendCiliumValues(ciliumRelease.Values, conformanceMode)
|
||||||
|
|
||||||
certManagerRelease, err := i.loadRelease(certManagerInfo)
|
certManagerRelease, err := i.loadRelease(certManagerInfo, helmWaitMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("loading cert-manager: %w", err)
|
return nil, fmt.Errorf("loading cert-manager: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
operatorRelease, err := i.loadRelease(constellationOperatorsInfo)
|
operatorRelease, err := i.loadRelease(constellationOperatorsInfo, helmWaitMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("loading operators: %w", err)
|
return nil, fmt.Errorf("loading operators: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
conServicesRelease, err := i.loadRelease(constellationServicesInfo)
|
conServicesRelease, err := i.loadRelease(constellationServicesInfo, helmWaitMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("loading constellation-services: %w", err)
|
return nil, fmt.Errorf("loading constellation-services: %w", err)
|
||||||
}
|
}
|
||||||
@ -136,7 +136,7 @@ func (i *ChartLoader) Load(config *config.Config, conformanceMode bool, masterSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// loadRelease loads the embedded chart and values depending on the given info argument.
|
// loadRelease loads the embedded chart and values depending on the given info argument.
|
||||||
func (i *ChartLoader) loadRelease(info chartInfo) (helm.Release, error) {
|
func (i *ChartLoader) loadRelease(info chartInfo, helmWaitMode helm.WaitMode) (helm.Release, error) {
|
||||||
chart, err := loadChartsDir(helmFS, info.path)
|
chart, err := loadChartsDir(helmFS, info.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return helm.Release{}, fmt.Errorf("loading %s chart: %w", info.releaseName, err)
|
return helm.Release{}, fmt.Errorf("loading %s chart: %w", info.releaseName, err)
|
||||||
@ -168,7 +168,7 @@ func (i *ChartLoader) loadRelease(info chartInfo) (helm.Release, error) {
|
|||||||
return helm.Release{}, fmt.Errorf("packaging %s chart: %w", info.releaseName, err)
|
return helm.Release{}, fmt.Errorf("packaging %s chart: %w", info.releaseName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return helm.Release{Chart: chartRaw, Values: values, ReleaseName: info.releaseName}, nil
|
return helm.Release{Chart: chartRaw, Values: values, ReleaseName: info.releaseName, WaitMode: helmWaitMode}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadCiliumValues is used to separate the marshalling step from the loading step.
|
// loadCiliumValues is used to separate the marshalling step from the loading step.
|
||||||
|
@ -39,7 +39,7 @@ func TestLoad(t *testing.T) {
|
|||||||
|
|
||||||
config := &config.Config{Provider: config.ProviderConfig{GCP: &config.GCPConfig{}}}
|
config := &config.Config{Provider: config.ProviderConfig{GCP: &config.GCPConfig{}}}
|
||||||
chartLoader := ChartLoader{csp: config.GetProvider()}
|
chartLoader := ChartLoader{csp: config.GetProvider()}
|
||||||
release, err := chartLoader.Load(config, true, []byte("secret"), []byte("salt"))
|
release, err := chartLoader.Load(config, true, helm.WaitModeAtomic, []byte("secret"), []byte("salt"))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
var helmReleases helm.Releases
|
var helmReleases helm.Releases
|
||||||
|
@ -253,6 +253,7 @@ constellation init [flags]
|
|||||||
-h, --help help for init
|
-h, --help help for init
|
||||||
--master-secret string path to base64-encoded master secret
|
--master-secret string path to base64-encoded master secret
|
||||||
--merge-kubeconfig merge Constellation kubeconfig file with default kubeconfig file in $HOME/.kube/config
|
--merge-kubeconfig merge Constellation kubeconfig file with default kubeconfig file in $HOME/.kube/config
|
||||||
|
--skip-helm-wait install helm charts without waiting for deployments to be ready
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
@ -12,6 +12,7 @@ type Release struct {
|
|||||||
Chart []byte
|
Chart []byte
|
||||||
Values map[string]any
|
Values map[string]any
|
||||||
ReleaseName string
|
ReleaseName string
|
||||||
|
WaitMode WaitMode
|
||||||
}
|
}
|
||||||
|
|
||||||
// Releases bundles all helm releases to be deployed to Constellation.
|
// Releases bundles all helm releases to be deployed to Constellation.
|
||||||
@ -43,3 +44,16 @@ func MergeMaps(a, b map[string]any) map[string]any {
|
|||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WaitMode specifies the wait mode for a helm release.
|
||||||
|
type WaitMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// WaitModeNone specifies that the helm release should not wait for the resources to be ready.
|
||||||
|
WaitModeNone WaitMode = ""
|
||||||
|
// WaitModeWait specifies that the helm release should wait for the resources to be ready.
|
||||||
|
WaitModeWait WaitMode = "wait"
|
||||||
|
// WaitModeAtomic specifies that the helm release should
|
||||||
|
// wait for the resources to be ready and roll back atomically on failure.
|
||||||
|
WaitModeAtomic WaitMode = "atomic"
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user