From 50c21e4965951bf17bce004ce12754a2b4cc30e8 Mon Sep 17 00:00:00 2001 From: Ethan Mosbaugh Date: Wed, 29 Jan 2025 10:42:25 -0800 Subject: [PATCH] feat(v2): restore command, cleanup (#1761) * feat(v2): restore command, cleanup * f * f --- cmd/installer/cli/install2.go | 115 +++++++++++----------- cmd/installer/cli/join2.go | 42 +++----- cmd/installer/cli/restore.go | 179 ++++++---------------------------- pkg/k0s/install.go | 8 +- 4 files changed, 104 insertions(+), 240 deletions(-) diff --git a/cmd/installer/cli/install2.go b/cmd/installer/cli/install2.go index eafd81e29..5f05fa300 100644 --- a/cmd/installer/cli/install2.go +++ b/cmd/installer/cli/install2.go @@ -8,7 +8,6 @@ import ( "strings" "time" - k0sconfig "github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1" k0sv1beta1 "github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1" "github.com/replicatedhq/embedded-cluster/cmd/installer/kotscli" ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" @@ -43,6 +42,7 @@ type Install2CmdFlags struct { adminConsolePassword string adminConsolePort int airgapBundle string + isAirgap bool dataDir string licenseFile string localArtifactMirrorPort int @@ -76,7 +76,7 @@ func Install2Cmd(ctx context.Context, name string) *cobra.Command { SilenceUsage: true, SilenceErrors: true, PreRunE: func(cmd *cobra.Command, args []string) error { - if err := preRunInstall2(cmd, args, &flags); err != nil { + if err := preRunInstall2(cmd, &flags); err != nil { return err } @@ -143,15 +143,11 @@ func addInstallFlags(cmd *cobra.Command, flags *Install2CmdFlags) error { return nil } -func preRunInstall2(cmd *cobra.Command, args []string, flags *Install2CmdFlags) error { +func preRunInstall2(cmd *cobra.Command, flags *Install2CmdFlags) error { if os.Getuid() != 0 { return fmt.Errorf("install command must be run as root") } - if flags.skipHostPreflights { - logrus.Warnf("Warning: --skip-host-preflights is deprecated and will be removed in a later version. Use --ignore-host-preflights instead.") - } - p, err := parseProxyFlags(cmd) if err != nil { return err @@ -204,17 +200,21 @@ func preRunInstall2(cmd *cobra.Command, args []string, flags *Install2CmdFlags) metrics.DisableMetrics() } + flags.isAirgap = flags.airgapBundle != "" + return nil } func runInstall2(cmd *cobra.Command, args []string, name string, flags Install2CmdFlags) error { - if err := runInstallVerifyAndPrompt(cmd.Context(), name, &flags); err != nil { + ctx := cmd.Context() + + if err := runInstallVerifyAndPrompt(ctx, name, &flags); err != nil { return err } logrus.Debugf("materializing binaries") if err := materializeFiles(flags.airgapBundle); err != nil { - metrics.ReportApplyFinished(cmd.Context(), "", flags.license, err) + metrics.ReportApplyFinished(ctx, "", flags.license, err) return err } @@ -230,7 +230,7 @@ func runInstall2(cmd *cobra.Command, args []string, name string, flags Install2C } logrus.Debugf("configuring network manager") - if err := configureNetworkManager(cmd.Context()); err != nil { + if err := configureNetworkManager(ctx); err != nil { return fmt.Errorf("unable to configure network manager: %w", err) } @@ -241,7 +241,7 @@ func runInstall2(cmd *cobra.Command, args []string, name string, flags Install2C } logrus.Debugf("running host preflights") - if err := preflights.PrepareAndRun(cmd.Context(), preflights.PrepareAndRunOptions{ + if err := preflights.PrepareAndRun(ctx, preflights.PrepareAndRunOptions{ ReplicatedAPIURL: replicatedAPIURL, ProxyRegistryURL: proxyRegistryURL, Proxy: flags.proxy, @@ -249,23 +249,20 @@ func runInstall2(cmd *cobra.Command, args []string, name string, flags Install2C ServiceCIDR: flags.cidrCfg.ServiceCIDR, GlobalCIDR: flags.cidrCfg.GlobalCIDR, PrivateCAs: flags.privateCAs, - IsAirgap: flags.airgapBundle != "", + IsAirgap: flags.isAirgap, SkipHostPreflights: flags.skipHostPreflights, IgnoreHostPreflights: flags.ignoreHostPreflights, AssumeYes: flags.assumeYes, }); err != nil { if err == preflights.ErrPreflightsHaveFail { - // we exit and not return an error to prevent the error from being printed to stderr - // we already handled the output - os.Exit(1) - return nil + return ErrNothingElseToAdd } return fmt.Errorf("unable to prepare and run preflights: %w", err) } - k0sCfg, err := installAndStartCluster(cmd.Context(), flags.networkInterface, flags.airgapBundle, flags.license, flags.proxy, flags.cidrCfg, flags.overrides) + k0sCfg, err := installAndStartCluster(ctx, flags.networkInterface, flags.airgapBundle, flags.proxy, flags.cidrCfg, flags.overrides, nil) if err != nil { - metrics.ReportApplyFinished(cmd.Context(), "", flags.license, err) + metrics.ReportApplyFinished(ctx, "", flags.license, err) return err } @@ -274,16 +271,16 @@ func runInstall2(cmd *cobra.Command, args []string, name string, flags Install2C return fmt.Errorf("unable to check if disaster recovery is enabled: %w", err) } - installObject, err := recordInstallation(cmd.Context(), flags, k0sCfg, disasterRecoveryEnabled) + installObject, err := recordInstallation(ctx, flags, k0sCfg, disasterRecoveryEnabled) if err != nil { - metrics.ReportApplyFinished(cmd.Context(), "", flags.license, err) + metrics.ReportApplyFinished(ctx, "", flags.license, err) return err } // TODO (@salah): update installation status to reflect what's happening logrus.Debugf("installing addons") - if err := addons2.Install(cmd.Context(), addons2.InstallOptions{ + if err := addons2.Install(ctx, addons2.InstallOptions{ AdminConsolePwd: flags.adminConsolePassword, License: flags.license, LicenseFile: flags.licenseFile, @@ -304,26 +301,26 @@ func runInstall2(cmd *cobra.Command, args []string, name string, flags Install2C return kotscli.Install(opts, msg) }, }); err != nil { - metrics.ReportApplyFinished(cmd.Context(), "", flags.license, err) + metrics.ReportApplyFinished(ctx, "", flags.license, err) return err } logrus.Debugf("installing extensions") - if err := extensions.Install(cmd.Context(), flags.airgapBundle != ""); err != nil { - metrics.ReportApplyFinished(cmd.Context(), "", flags.license, err) + if err := extensions.Install(ctx, flags.isAirgap); err != nil { + metrics.ReportApplyFinished(ctx, "", flags.license, err) return err } logrus.Debugf("installing manager") - if err := installAndEnableManager(cmd.Context()); err != nil { - metrics.ReportApplyFinished(cmd.Context(), "", flags.license, err) + if err := installAndEnableManager(ctx); err != nil { + metrics.ReportApplyFinished(ctx, "", flags.license, err) return err } // mark that the installation is installed as everything has been applied installObject.Status.State = ecv1beta1.InstallationStateInstalled - if err := updateInstallation(cmd.Context(), installObject); err != nil { - metrics.ReportApplyFinished(cmd.Context(), "", flags.license, err) + if err := updateInstallation(ctx, installObject); err != nil { + metrics.ReportApplyFinished(ctx, "", flags.license, err) return err } @@ -332,7 +329,7 @@ func runInstall2(cmd *cobra.Command, args []string, name string, flags Install2C } if err := printSuccessMessage(flags.license, flags.networkInterface); err != nil { - metrics.ReportApplyFinished(cmd.Context(), "", flags.license, err) + metrics.ReportApplyFinished(ctx, "", flags.license, err) return err } @@ -340,20 +337,13 @@ func runInstall2(cmd *cobra.Command, args []string, name string, flags Install2C } func runInstallVerifyAndPrompt(ctx context.Context, name string, flags *Install2CmdFlags) error { - logrus.Debugf("checking if %s is already installed", name) - installed, err := k0s.IsInstalled() + logrus.Debugf("checking if k0s is already installed") + err := verifyNoInstallation(name, "reinstall") if err != nil { return err } - if installed { - logrus.Errorf("An installation has been detected on this machine.") - logrus.Infof("If you want to reinstall, you need to remove the existing installation first.") - logrus.Infof("You can do this by running the following command:") - logrus.Infof("\n sudo ./%s reset\n", name) - os.Exit(1) - } - _, err = getChannelReleaseAndVerify(ctx, flags) + err = verifyChannelRelease("installation", flags.isAirgap, flags.assumeYes) if err != nil { return err } @@ -367,18 +357,14 @@ func runInstallVerifyAndPrompt(ctx context.Context, name string, flags *Install2 metrics.ReportApplyFinished(ctx, "", flags.license, metricErr) return err // do not return the metricErr, as we want the user to see the error message without a prefix } - isAirgap := false - if flags.airgapBundle != "" { - isAirgap = true - } - if isAirgap { + if flags.isAirgap { logrus.Debugf("checking airgap bundle matches binary") if err := checkAirgapMatches(flags.airgapBundle); err != nil { return err // we want the user to see the error message without a prefix } } - if !isAirgap { + if !flags.isAirgap { if err := maybePromptForAppUpdate(ctx, prompts.New(), license, flags.assumeYes); err != nil { if errors.Is(err, ErrNothingElseToAdd) { metrics.ReportApplyFinished(ctx, "", flags.license, err) @@ -426,52 +412,63 @@ func runInstallVerifyAndPrompt(ctx context.Context, name string, flags *Install2 return nil } -func getChannelReleaseAndVerify(ctx context.Context, flags *Install2CmdFlags) (*release.ChannelRelease, error) { +func verifyChannelRelease(cmdName string, isAirgap bool, assumeYes bool) error { channelRelease, err := release.GetChannelRelease() if err != nil { - return nil, fmt.Errorf("unable to read channel release data: %w", err) + return fmt.Errorf("unable to read channel release data: %w", err) } - if channelRelease != nil && channelRelease.Airgap && flags.airgapBundle == "" && !flags.assumeYes { + if channelRelease != nil && channelRelease.Airgap && !isAirgap && !assumeYes { logrus.Warnf("You downloaded an air gap bundle but didn't provide it with --airgap-bundle.") - logrus.Warnf("If you continue, the installation will not use an air gap bundle and will connect to the internet.") - if !prompts.New().Confirm("Do you want to proceed with an online installation?", false) { - return nil, ErrNothingElseToAdd + logrus.Warnf("If you continue, the %s will not use an air gap bundle and will connect to the internet.", cmdName) + if !prompts.New().Confirm(fmt.Sprintf("Do you want to proceed with an online %s?", cmdName), false) { + return ErrNothingElseToAdd } } - return channelRelease, nil + return nil +} + +func verifyNoInstallation(name string, cmdName string) error { + installed, err := k0s.IsInstalled() + if err != nil { + return err + } + if installed { + logrus.Errorf("An installation has been detected on this machine.") + logrus.Infof("If you want to %s, you need to remove the existing installation first.", cmdName) + logrus.Infof("You can do this by running the following command:") + logrus.Infof("\n sudo ./%s reset\n", name) + return ErrNothingElseToAdd + } + return nil } -func installAndStartCluster(ctx context.Context, networkInterface string, airgapBundle string, license *kotsv1beta1.License, proxy *ecv1beta1.ProxySpec, cidrCfg *CIDRConfig, overrides string) (*k0sconfig.ClusterConfig, error) { +func installAndStartCluster(ctx context.Context, networkInterface string, airgapBundle string, proxy *ecv1beta1.ProxySpec, cidrCfg *CIDRConfig, overrides string, mutate func(*k0sv1beta1.ClusterConfig) error) (*k0sv1beta1.ClusterConfig, error) { loading := spinner.Start() defer loading.Close() loading.Infof("Installing %s node", runtimeconfig.BinaryName()) logrus.Debugf("creating k0s configuration file") - cfg, err := k0s.WriteK0sConfig(ctx, networkInterface, airgapBundle, cidrCfg.PodCIDR, cidrCfg.ServiceCIDR, overrides) + cfg, err := k0s.WriteK0sConfig(ctx, networkInterface, airgapBundle, cidrCfg.PodCIDR, cidrCfg.ServiceCIDR, overrides, mutate) if err != nil { err := fmt.Errorf("unable to create config file: %w", err) - metrics.ReportApplyFinished(ctx, "", license, err) return nil, err } logrus.Debugf("creating systemd unit files") if err := createSystemdUnitFiles(false, proxy); err != nil { err := fmt.Errorf("unable to create systemd unit files: %w", err) - metrics.ReportApplyFinished(ctx, "", license, err) return nil, err } logrus.Debugf("installing k0s") if err := k0s.Install(networkInterface); err != nil { err := fmt.Errorf("unable to install cluster: %w", err) - metrics.ReportApplyFinished(ctx, "", license, err) return nil, err } loading.Infof("Waiting for %s node to be ready", runtimeconfig.BinaryName()) logrus.Debugf("waiting for k0s to be ready") if err := waitForK0s(); err != nil { err := fmt.Errorf("unable to wait for node: %w", err) - metrics.ReportApplyFinished(ctx, "", license, err) return nil, err } @@ -533,7 +530,7 @@ func recordInstallation(ctx context.Context, flags Install2CmdFlags, k0sCfg *k0s Spec: ecv1beta1.InstallationSpec{ ClusterID: metrics.ClusterID().String(), MetricsBaseURL: metrics.BaseURL(flags.license), - AirGap: flags.airgapBundle != "", + AirGap: flags.isAirgap, Proxy: flags.proxy, Network: networkSpecFromK0sConfig(k0sCfg), Config: cfgspec, diff --git a/cmd/installer/cli/join2.go b/cmd/installer/cli/join2.go index c4c858bc8..012de2386 100644 --- a/cmd/installer/cli/join2.go +++ b/cmd/installer/cli/join2.go @@ -19,7 +19,6 @@ import ( "github.com/replicatedhq/embedded-cluster/pkg/netutils" "github.com/replicatedhq/embedded-cluster/pkg/preflights" "github.com/replicatedhq/embedded-cluster/pkg/prompts" - "github.com/replicatedhq/embedded-cluster/pkg/release" "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig" "github.com/replicatedhq/embedded-cluster/pkg/spinner" "github.com/replicatedhq/embedded-cluster/pkg/versions" @@ -32,6 +31,7 @@ import ( type Join2CmdFlags struct { airgapBundle string + isAirgap bool enableHighAvailability bool networkInterface string assumeYes bool @@ -60,6 +60,8 @@ func Join2Cmd(ctx context.Context, name string) *cobra.Command { logrus.Warnf("Warning: --skip-host-preflights is deprecated and will be removed in a later version. Use --ignore-host-preflights instead.") } + flags.isAirgap = flags.airgapBundle != "" + return nil }, PostRun: func(cmd *cobra.Command, args []string) { @@ -170,7 +172,7 @@ func runJoin2(cmd *cobra.Command, args []string, name string, flags Join2CmdFlag } if flags.enableHighAvailability { - if err := maybeEnableHA(cmd.Context(), kcli, flags.airgapBundle != "", cidrCfg.ServiceCIDR, jcmd.InstallationSpec.Proxy); err != nil { + if err := maybeEnableHA(cmd.Context(), kcli, flags.isAirgap, cidrCfg.ServiceCIDR, jcmd.InstallationSpec.Proxy); err != nil { err := fmt.Errorf("unable to enable high availability: %w", err) metrics.ReportJoinFailed(cmd.Context(), jcmd.InstallationSpec.MetricsBaseURL, jcmd.ClusterID, err) return err @@ -183,37 +185,18 @@ func runJoin2(cmd *cobra.Command, args []string, name string, flags Join2CmdFlag } func runJoinVerifyAndPrompt(name string, flags Join2CmdFlags, jcmd *kotsadm.JoinCommandResponse) error { - logrus.Debugf("checking if %s is already installed", name) - installed, err := k0s.IsInstalled() + logrus.Debugf("checking if k0s is already installed") + err := verifyNoInstallation(name, "join a node") if err != nil { return err } - if installed { - logrus.Errorf("An installation has been detected on this machine.") - logrus.Infof("If you want to join a node to an existing installation, you need to remove the existing installation first.") - logrus.Infof("You can do this by running the following command:") - logrus.Infof("\n sudo ./%s reset\n", name) - os.Exit(1) - } - channelRelease, err := release.GetChannelRelease() + err = verifyChannelRelease("join", flags.isAirgap, flags.assumeYes) if err != nil { - return fmt.Errorf("unable to read channel release data: %w", err) - } - - if channelRelease != nil && channelRelease.Airgap && flags.airgapBundle == "" && !flags.assumeYes { - logrus.Infof("You downloaded an air gap bundle but are performing an online join.") - logrus.Infof("To do an air gap join, pass the air gap bundle with --airgap-bundle.") - if !prompts.New().Confirm("Do you want to proceed with an online join?", false) { - return ErrNothingElseToAdd - } + return err } - isAirgap := false - if flags.airgapBundle != "" { - isAirgap = true - } - if isAirgap { + if flags.isAirgap { logrus.Debugf("checking airgap bundle matches binary") if err := checkAirgapMatches(flags.airgapBundle); err != nil { return err // we want the user to see the error message without a prefix @@ -279,17 +262,14 @@ func runJoinPreflights(ctx context.Context, jcmd *kotsadm.JoinCommandResponse, f Proxy: jcmd.InstallationSpec.Proxy, PodCIDR: cidrCfg.PodCIDR, ServiceCIDR: cidrCfg.ServiceCIDR, - IsAirgap: flags.airgapBundle != "", + IsAirgap: flags.isAirgap, SkipHostPreflights: flags.skipHostPreflights, IgnoreHostPreflights: flags.ignoreHostPreflights, AssumeYes: flags.assumeYes, TCPConnectionsRequired: jcmd.TCPConnectionsRequired, }); err != nil { if err == preflights.ErrPreflightsHaveFail { - // we exit and not return an error to prevent the error from being printed to stderr - // we already handled the output - os.Exit(1) - return nil + return ErrNothingElseToAdd } return fmt.Errorf("unable to prepare and run preflights: %w", err) } diff --git a/cmd/installer/cli/restore.go b/cmd/installer/cli/restore.go index d47bd2c24..763205a40 100644 --- a/cmd/installer/cli/restore.go +++ b/cmd/installer/cli/restore.go @@ -28,7 +28,6 @@ import ( "github.com/replicatedhq/embedded-cluster/pkg/constants" "github.com/replicatedhq/embedded-cluster/pkg/disasterrecovery" "github.com/replicatedhq/embedded-cluster/pkg/helpers" - "github.com/replicatedhq/embedded-cluster/pkg/k0s" "github.com/replicatedhq/embedded-cluster/pkg/kubeutils" "github.com/replicatedhq/embedded-cluster/pkg/netutils" "github.com/replicatedhq/embedded-cluster/pkg/prompts" @@ -83,7 +82,7 @@ const ( func RestoreCmd(ctx context.Context, name string) *cobra.Command { var flags Install2CmdFlags - var s3Store = &s3BackupStore{} + var s3Store s3BackupStore cmd := &cobra.Command{ Use: "restore", @@ -91,7 +90,7 @@ func RestoreCmd(ctx context.Context, name string) *cobra.Command { SilenceErrors: true, SilenceUsage: true, PreRunE: func(cmd *cobra.Command, args []string) error { - if err := preRunInstall2(cmd, args, &flags); err != nil { + if err := preRunInstall2(cmd, &flags); err != nil { return err } @@ -101,7 +100,7 @@ func RestoreCmd(ctx context.Context, name string) *cobra.Command { runtimeconfig.Cleanup() }, RunE: func(cmd *cobra.Command, args []string) error { - if err := runRestore(cmd, args, name, &flags, s3Store); err != nil { + if err := runRestore(cmd, args, name, flags, s3Store); err != nil { return err } @@ -109,7 +108,7 @@ func RestoreCmd(ctx context.Context, name string) *cobra.Command { }, } - addS3Flags(cmd, s3Store) + addS3Flags(cmd, &s3Store) cmd.Flags().Bool("skip-store-validation", false, "Skip validation of the backup storage location") if err := addInstallFlags(cmd, &flags); err != nil { @@ -119,14 +118,21 @@ func RestoreCmd(ctx context.Context, name string) *cobra.Command { return cmd } -func runRestore(cmd *cobra.Command, args []string, name string, flags *Install2CmdFlags, s3Store *s3BackupStore) error { +func runRestore(cmd *cobra.Command, args []string, name string, flags Install2CmdFlags, s3Store s3BackupStore) error { ctx := cmd.Context() - _, err := getChannelReleaseAndVerify(ctx, flags) + err := verifyChannelRelease("restore", flags.isAirgap, flags.assumeYes) if err != nil { return err } + if flags.isAirgap { + logrus.Debugf("checking airgap bundle matches binary") + if err := checkAirgapMatches(flags.airgapBundle); err != nil { + return err // we want the user to see the error message without a prefix + } + } + logrus.Debugf("configuring sysctl") if err := configutils.ConfigureSysctl(); err != nil { return fmt.Errorf("unable to configure sysctl: %w", err) @@ -144,19 +150,12 @@ func runRestore(cmd *cobra.Command, args []string, name string, flags *Install2C } } - if flags.airgapBundle != "" { - logrus.Debugf("checking airgap bundle matches binary") - if err := checkAirgapMatches(flags.airgapBundle); err != nil { - return err // we want the user to see the error message without a prefix - } - } - // if the user wants to resume, check if a backup has already been picked. var backupToRestore *disasterrecovery.ReplicatedBackup if state != ecRestoreStateNew { logrus.Debugf("getting backup from restore state") var err error - backupToRestore, err = getBackupFromRestoreState(ctx, flags.airgapBundle != "") + backupToRestore, err = getBackupFromRestoreState(ctx, flags.isAirgap) if err != nil { return fmt.Errorf("unable to resume: %w", err) } @@ -199,24 +198,17 @@ func runRestore(cmd *cobra.Command, args []string, name string, flags *Install2C switch state { case ecRestoreStateNew: - logrus.Debugf("checking if %s is already installed", name) - installed, err := k0s.IsInstalled() + logrus.Debugf("checking if k0s is already installed") + err := verifyNoInstallation(name, "restore") if err != nil { return err } - if installed { - logrus.Errorf("An installation has been detected on this machine.") - logrus.Infof("Before you can restore, you must remove the existing installation.") - logrus.Infof("You can do this by running the following command:") - logrus.Infof("\n sudo ./%s reset\n", name) - os.Exit(1) - } - if !s3BackupStoreHasData(s3Store) { + if !s3BackupStoreHasData(&s3Store) { logrus.Infof("You'll be guided through the process of restoring %s from a backup.\n", name) logrus.Info("Enter information to configure access to your backup storage location.\n") - promptForS3BackupStore(s3Store) + promptForS3BackupStore(&s3Store) } s3Store.prefix = strings.TrimPrefix(s3Store.prefix, "/") @@ -227,7 +219,7 @@ func runRestore(cmd *cobra.Command, args []string, name string, flags *Install2C if !skipStoreValidationFlag { logrus.Debugf("validating backup store configuration") - if err := validateS3BackupStore(s3Store); err != nil { + if err := validateS3BackupStore(&s3Store); err != nil { return fmt.Errorf("unable to validate backup store: %w", err) } } @@ -247,7 +239,13 @@ func runRestore(cmd *cobra.Command, args []string, name string, flags *Install2C return fmt.Errorf("unable to finish preflight checks: %w", err) } - cfg, err := installAndWaitForRestoredK0sNode(cmd, name, applier) + mutateK0sCfg := func(k0sCfg *k0sv1beta1.ClusterConfig) error { + if err := config.UpdateHelmConfigsForRestore(applier, k0sCfg); err != nil { + return fmt.Errorf("unable to update helm configs: %w", err) + } + return nil + } + k0sCfg, err := installAndStartCluster(ctx, flags.networkInterface, flags.airgapBundle, flags.proxy, flags.cidrCfg, flags.overrides, mutateK0sCfg) if err != nil { return err } @@ -266,7 +264,7 @@ func runRestore(cmd *cobra.Command, args []string, name string, flags *Install2C }() logrus.Debugf("running outro") - if err := runOutroForRestore(cmd, applier, cfg); err != nil { + if err := runOutroForRestore(cmd, applier, k0sCfg); err != nil { return fmt.Errorf("unable to run outro: %w", err) } @@ -301,7 +299,7 @@ func runRestore(cmd *cobra.Command, args []string, name string, flags *Install2C } logrus.Debugf("waiting for backups to become available") - backups, err := waitForBackups(ctx, cmd.OutOrStdout(), kcli, k0sCfg, flags.airgapBundle != "") + backups, err := waitForBackups(ctx, cmd.OutOrStdout(), kcli, k0sCfg, flags.isAirgap) if err != nil { return err } @@ -393,7 +391,7 @@ func runRestore(cmd *cobra.Command, args []string, name string, flags *Install2C return err } - if highAvailability && flags.airgapBundle != "" { + if highAvailability && flags.isAirgap { logrus.Debugf("setting restore state to %q", ecRestoreStateRestoreSeaweedFS) if err := setECRestoreState(ctx, ecRestoreStateRestoreSeaweedFS, backupToRestore.GetName()); err != nil { return fmt.Errorf("unable to set restore state: %w", err) @@ -408,7 +406,7 @@ func runRestore(cmd *cobra.Command, args []string, name string, flags *Install2C case ecRestoreStateRestoreRegistry: // only restore registry in case of airgap - if flags.airgapBundle != "" { + if flags.isAirgap { logrus.Debugf("setting restore state to %q", ecRestoreStateRestoreRegistry) if err := setECRestoreState(ctx, ecRestoreStateRestoreRegistry, backupToRestore.GetName()); err != nil { return fmt.Errorf("unable to set restore state: %w", err) @@ -720,80 +718,6 @@ func RunHostPreflightsForRestore(cmd *cobra.Command, applier *addons.Applier, pr return runHostPreflights(cmd, hpf, proxy, assumeYes, "") } -// ensureK0sConfigForRestore creates a new k0s.yaml configuration file for restore operations. -// The file is saved in the global location (as returned by runtimeconfig.PathToK0sConfig()). -// If a file already sits there, this function returns an error. -func ensureK0sConfigForRestore(cmd *cobra.Command, applier *addons.Applier) (*k0sv1beta1.ClusterConfig, error) { - cfgpath := runtimeconfig.PathToK0sConfig() - if _, err := os.Stat(cfgpath); err == nil { - return nil, fmt.Errorf("configuration file already exists") - } - - if err := os.MkdirAll(filepath.Dir(cfgpath), 0755); err != nil { - return nil, fmt.Errorf("unable to create directory: %w", err) - } - - cfg := config.RenderK0sConfig() - - networkInterfaceFlag, err := cmd.Flags().GetString("network-interface") - if err != nil { - return nil, fmt.Errorf("unable to get network-interface flag: %w", err) - } - - address, err := netutils.FirstValidAddress(networkInterfaceFlag) - if err != nil { - return nil, fmt.Errorf("unable to find first valid address: %w", err) - } - - cfg.Spec.API.Address = address - cfg.Spec.Storage.Etcd.PeerAddress = address - - cidrCfg, err := getCIDRConfig(cmd) - if err != nil { - return nil, fmt.Errorf("unable to determine pod and service CIDRs: %w", err) - } - - cfg.Spec.Network.PodCIDR = cidrCfg.PodCIDR - cfg.Spec.Network.ServiceCIDR = cidrCfg.ServiceCIDR - - if err := config.UpdateHelmConfigsForRestore(applier, cfg); err != nil { - return nil, fmt.Errorf("unable to update helm configs: %w", err) - } - - cfg, err = applyUnsupportedOverrides(cmd, cfg) - if err != nil { - return nil, fmt.Errorf("unable to apply unsupported overrides: %w", err) - } - - airgapBundleFlag, err := cmd.Flags().GetString("airgap-bundle") - if err != nil { - return nil, fmt.Errorf("unable to get airgap-bundle flag: %w", err) - } - - if airgapBundleFlag != "" { - // update the k0s config to install with airgap - airgap.RemapHelm(cfg) - airgap.SetAirgapConfig(cfg) - } - - // This is necessary to install the previous version of k0s in e2e tests - // TODO: remove this once the previous version is > 1.29 - unstructured, err := helpers.K0sClusterConfigTo129Compat(cfg) - if err != nil { - return nil, fmt.Errorf("unable to convert cluster config to 1.29 compat: %w", err) - } - - data, err := k8syaml.Marshal(unstructured) - if err != nil { - return nil, fmt.Errorf("unable to marshal config: %w", err) - } - - if err := os.WriteFile(cfgpath, data, 0600); err != nil { - return nil, fmt.Errorf("unable to write config file: %w", err) - } - return cfg, nil -} - // runOutroForRestore calls Outro() in all enabled addons for restore operations by means of Applier. func runOutroForRestore(cmd *cobra.Command, applier *addons.Applier, cfg *k0sv1beta1.ClusterConfig) error { return applier.OutroForRestore(cmd.Context(), cfg) @@ -1467,49 +1391,6 @@ func waitForAdditionalNodes(ctx context.Context, highAvailability bool, networkI return nil } -func installAndWaitForRestoredK0sNode(cmd *cobra.Command, name string, applier *addons.Applier) (*k0sv1beta1.ClusterConfig, error) { - loading := spinner.Start() - defer loading.Close() - - loading.Infof("Installing %s node", name) - logrus.Debugf("creating k0s configuration file") - - cfg, err := ensureK0sConfigForRestore(cmd, applier) - if err != nil { - return nil, fmt.Errorf("unable to create config file: %w", err) - } - - proxy, err := getProxySpecFromFlags(cmd) - if err != nil { - return nil, fmt.Errorf("unable to get proxy spec from flags: %w", err) - } - - logrus.Debugf("creating systemd unit files") - if err := createSystemdUnitFiles(false, proxy); err != nil { - return nil, fmt.Errorf("unable to create systemd unit files: %w", err) - } - - logrus.Debugf("installing k0s") - networkInterface, err := cmd.Flags().GetString("network-interface") - if err != nil { - return nil, fmt.Errorf("unable to get network-interface flag: %w", err) - } - - if err := k0s.Install(networkInterface); err != nil { - return nil, fmt.Errorf("unable to install cluster: %w", err) - } - - loading.Infof("Waiting for %s node to be ready", name) - logrus.Debugf("waiting for k0s to be ready") - if err := waitForK0s(); err != nil { - return nil, fmt.Errorf("unable to wait for node: %w", err) - } - - loading.Infof("Node installation finished!") - - return cfg, nil -} - // restoreReconcileInstallationFromRuntimeConfig will update the installation to match the runtime // config from the original installation. func restoreReconcileInstallationFromRuntimeConfig(ctx context.Context) error { diff --git a/pkg/k0s/install.go b/pkg/k0s/install.go index b33ce8f4a..7e7b3623c 100644 --- a/pkg/k0s/install.go +++ b/pkg/k0s/install.go @@ -55,7 +55,7 @@ func IsInstalled() (bool, error) { // WriteK0sConfig creates a new k0s.yaml configuration file. The file is saved in the // global location (as returned by runtimeconfig.PathToK0sConfig()). If a file already sits // there, this function returns an error. -func WriteK0sConfig(ctx context.Context, networkInterface string, airgapBundle string, podCIDR string, serviceCIDR string, overrides string) (*k0sconfig.ClusterConfig, error) { +func WriteK0sConfig(ctx context.Context, networkInterface string, airgapBundle string, podCIDR string, serviceCIDR string, overrides string, mutate func(*k0sconfig.ClusterConfig) error) (*k0sconfig.ClusterConfig, error) { cfgpath := runtimeconfig.PathToK0sConfig() if _, err := os.Stat(cfgpath); err == nil { return nil, fmt.Errorf("configuration file already exists") @@ -75,6 +75,12 @@ func WriteK0sConfig(ctx context.Context, networkInterface string, airgapBundle s cfg.Spec.Network.PodCIDR = podCIDR cfg.Spec.Network.ServiceCIDR = serviceCIDR + if mutate != nil { + if err := mutate(cfg); err != nil { + return nil, err + } + } + cfg, err = applyUnsupportedOverrides(ctx, overrides, cfg) if err != nil { return nil, fmt.Errorf("unable to apply unsupported overrides: %w", err)