cli: move helm and terraform out of kubernetes package (#2222)

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2023-08-16 09:59:32 +02:00 committed by GitHub
parent f270e91724
commit ed0bfd9d41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 370 additions and 376 deletions

View File

@ -8,8 +8,6 @@ package cmd
import ( import (
"errors" "errors"
"fmt" "fmt"
"strings"
"time"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/cli/internal/upgrade" "github.com/edgelesssys/constellation/v2/cli/internal/upgrade"
@ -18,7 +16,6 @@ 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/file" "github.com/edgelesssys/constellation/v2/internal/file"
"github.com/google/uuid"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -70,7 +67,8 @@ func runIAMUpgradeApply(cmd *cobra.Command, _ []string) error {
if err != nil { if err != nil {
return err return err
} }
upgradeID := "iam-" + time.Now().Format("20060102150405") + "-" + strings.Split(uuid.New().String(), "-")[0]
upgradeID := generateUpgradeID(upgradeCmdKindIAM)
iamMigrateCmd, err := upgrade.NewIAMMigrateCmd(cmd.Context(), constants.TerraformIAMWorkingDir, constants.UpgradeDir, upgradeID, conf.GetProvider(), terraform.LogLevelDebug) iamMigrateCmd, err := upgrade.NewIAMMigrateCmd(cmd.Context(), constants.TerraformIAMWorkingDir, constants.UpgradeDir, upgradeID, conf.GetProvider(), terraform.LogLevelDebug)
if err != nil { if err != nil {
return fmt.Errorf("setting up IAM migration command: %w", err) return fmt.Errorf("setting up IAM migration command: %w", err)
@ -86,10 +84,10 @@ func runIAMUpgradeApply(cmd *cobra.Command, _ []string) error {
if err != nil { if err != nil {
return err return err
} }
err = migrator.applyMigration(cmd, constants.UpgradeDir, file.NewHandler(afero.NewOsFs()), iamMigrateCmd, yes) if err := migrator.applyMigration(cmd, constants.UpgradeDir, file.NewHandler(afero.NewOsFs()), iamMigrateCmd, yes); err != nil {
if err != nil {
return fmt.Errorf("applying IAM migration: %w", err) return fmt.Errorf("applying IAM migration: %w", err)
} }
cmd.Println("IAM profile successfully applied.") cmd.Println("IAM profile successfully applied.")
return nil return nil
} }

View File

@ -7,6 +7,10 @@ SPDX-License-Identifier: AGPL-3.0-only
package cmd package cmd
import ( import (
"strings"
"time"
"github.com/google/uuid"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -23,3 +27,31 @@ func NewUpgradeCmd() *cobra.Command {
cmd.AddCommand(newUpgradeApplyCmd()) cmd.AddCommand(newUpgradeApplyCmd())
return cmd return cmd
} }
// upgradeCmdKind is the kind of the upgrade command (check, apply).
type upgradeCmdKind int
const (
// upgradeCmdKindCheck corresponds to the upgrade check command.
upgradeCmdKindCheck upgradeCmdKind = iota
// upgradeCmdKindApply corresponds to the upgrade apply command.
upgradeCmdKindApply
// upgradeCmdKindIAM corresponds to the IAM upgrade command.
upgradeCmdKindIAM
)
func generateUpgradeID(kind upgradeCmdKind) string {
upgradeID := time.Now().Format("20060102150405") + "-" + strings.Split(uuid.New().String(), "-")[0]
switch kind {
case upgradeCmdKindCheck:
// When performing an upgrade check, the upgrade directory will only be used temporarily to store the
// Terraform state. The directory is deleted after the check is finished.
// Therefore, add a tmp-suffix to the upgrade ID to indicate that the directory will be cleared after the check.
upgradeID = "upgrade-" + upgradeID + "-tmp"
case upgradeCmdKindApply:
upgradeID = "upgrade-" + upgradeID
case upgradeCmdKindIAM:
upgradeID = "iam-" + upgradeID
}
return upgradeID
}

View File

@ -28,6 +28,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/kms/uri" "github.com/edgelesssys/constellation/v2/internal/kms/uri"
"github.com/edgelesssys/constellation/v2/internal/kubernetes/kubectl"
"github.com/edgelesssys/constellation/v2/internal/versions" "github.com/edgelesssys/constellation/v2/internal/versions"
"github.com/rogpeppe/go-internal/diff" "github.com/rogpeppe/go-internal/diff"
"github.com/spf13/afero" "github.com/spf13/afero"
@ -64,33 +65,54 @@ func runUpgradeApply(cmd *cobra.Command, _ []string) error {
return fmt.Errorf("creating logger: %w", err) return fmt.Errorf("creating logger: %w", err)
} }
defer log.Sync() defer log.Sync()
fileHandler := file.NewHandler(afero.NewOsFs())
upgrader, err := kubernetes.NewUpgrader( fileHandler := file.NewHandler(afero.NewOsFs())
cmd.Context(), cmd.OutOrStdout(), upgradeID := generateUpgradeID(upgradeCmdKindApply)
constants.UpgradeDir, constants.AdminConfFilename,
fileHandler, log, kubernetes.UpgradeCmdKindApply, kubeUpgrader, err := kubernetes.NewUpgrader(cmd.OutOrStdout(), constants.AdminConfFilename, log)
)
if err != nil { if err != nil {
return err return err
} }
helmUpgrader, err := helm.NewUpgradeClient(kubectl.New(), constants.UpgradeDir, constants.AdminConfFilename, constants.HelmNamespace, log)
if err != nil {
return fmt.Errorf("setting up helm client: %w", err)
}
configFetcher := attestationconfigapi.NewFetcher() configFetcher := attestationconfigapi.NewFetcher()
tfClient, err := terraform.New(cmd.Context(), constants.TerraformWorkingDir)
// Set up two Terraform clients. They need to be configured with different workspaces
// One for upgrading existing resources
tfUpgrader, err := terraform.New(cmd.Context(), filepath.Join(constants.UpgradeDir, upgradeID, constants.TerraformUpgradeWorkingDir))
if err != nil {
return fmt.Errorf("setting up terraform client: %w", err)
}
// And one for showing existing resources
tfShower, err := terraform.New(cmd.Context(), constants.TerraformWorkingDir)
if err != nil { if err != nil {
return fmt.Errorf("setting up terraform client: %w", err) return fmt.Errorf("setting up terraform client: %w", err)
} }
applyCmd := upgradeApplyCmd{upgrader: upgrader, log: log, configFetcher: configFetcher, clusterShower: tfClient, fileHandler: fileHandler} applyCmd := upgradeApplyCmd{
helmUpgrader: helmUpgrader,
kubeUpgrader: kubeUpgrader,
terraformUpgrader: upgrade.NewTerraformUpgrader(tfUpgrader, cmd.OutOrStdout(), fileHandler, upgradeID),
configFetcher: configFetcher,
clusterShower: tfShower,
fileHandler: fileHandler,
log: log,
}
return applyCmd.upgradeApply(cmd) return applyCmd.upgradeApply(cmd)
} }
type upgradeApplyCmd struct { type upgradeApplyCmd struct {
upgrader cloudUpgrader helmUpgrader helmUpgrader
configFetcher attestationconfigapi.Fetcher kubeUpgrader kubernetesUpgrader
clusterShower clusterShower terraformUpgrader terraformUpgrader
fileHandler file.Handler configFetcher attestationconfigapi.Fetcher
log debugLog clusterShower clusterShower
fileHandler file.Handler
log debugLog
} }
func (u *upgradeApplyCmd) upgradeApply(cmd *cobra.Command) error { func (u *upgradeApplyCmd) upgradeApply(cmd *cobra.Command) error {
@ -131,7 +153,7 @@ func (u *upgradeApplyCmd) upgradeApply(cmd *cobra.Command) error {
} }
if idFile.MeasurementSalt == nil { if idFile.MeasurementSalt == nil {
// TODO(elchead): remove after 2.10, since 2.9 does not yet save it in the idfile // TODO(elchead): remove after 2.10, since 2.9 does not yet save it in the idfile
measurementSalt, err := u.upgrader.GetMeasurementSalt(cmd.Context()) measurementSalt, err := u.kubeUpgrader.GetMeasurementSalt(cmd.Context())
if err != nil { if err != nil {
return fmt.Errorf("getting join-config: %w", err) return fmt.Errorf("getting join-config: %w", err)
} }
@ -161,7 +183,7 @@ func (u *upgradeApplyCmd) upgradeApply(cmd *cobra.Command) error {
// - fallback endpoint // - fallback endpoint
// - custom (user-provided) endpoint // - custom (user-provided) endpoint
sans := append([]string{idFile.IP, conf.CustomEndpoint}, idFile.APIServerCertSANs...) sans := append([]string{idFile.IP, conf.CustomEndpoint}, idFile.APIServerCertSANs...)
if err := u.upgrader.ExtendClusterConfigCertSANs(cmd.Context(), sans); err != nil { if err := u.kubeUpgrader.ExtendClusterConfigCertSANs(cmd.Context(), sans); err != nil {
return fmt.Errorf("extending cert SANs: %w", err) return fmt.Errorf("extending cert SANs: %w", err)
} }
@ -177,7 +199,7 @@ func (u *upgradeApplyCmd) upgradeApply(cmd *cobra.Command) error {
return fmt.Errorf("upgrading services: %w", err) return fmt.Errorf("upgrading services: %w", err)
} }
err = u.upgrader.UpgradeNodeVersion(cmd.Context(), conf, flags.force) err = u.kubeUpgrader.UpgradeNodeVersion(cmd.Context(), conf, flags.force)
switch { switch {
case errors.Is(err, kubernetes.ErrInProgress): case errors.Is(err, kubernetes.ErrInProgress):
cmd.PrintErrln("Skipping image and Kubernetes upgrades. Another upgrade is in progress.") cmd.PrintErrln("Skipping image and Kubernetes upgrades. Another upgrade is in progress.")
@ -214,7 +236,7 @@ func (u *upgradeApplyCmd) migrateTerraform(
) (res terraform.ApplyOutput, err error) { ) (res terraform.ApplyOutput, err error) {
u.log.Debugf("Planning Terraform migrations") u.log.Debugf("Planning Terraform migrations")
if err := u.upgrader.CheckTerraformMigrations(constants.UpgradeDir); err != nil { if err := u.terraformUpgrader.CheckTerraformMigrations(constants.UpgradeDir); err != nil {
return res, fmt.Errorf("checking workspace: %w", err) return res, fmt.Errorf("checking workspace: %w", err)
} }
@ -242,7 +264,7 @@ func (u *upgradeApplyCmd) migrateTerraform(
// u.upgrader.AddManualStateMigration(migration) // u.upgrader.AddManualStateMigration(migration)
// } // }
hasDiff, err := u.upgrader.PlanTerraformMigrations(cmd.Context(), opts) hasDiff, err := u.terraformUpgrader.PlanTerraformMigrations(cmd.Context(), opts)
if err != nil { if err != nil {
return res, fmt.Errorf("planning terraform migrations: %w", err) return res, fmt.Errorf("planning terraform migrations: %w", err)
} }
@ -257,14 +279,14 @@ func (u *upgradeApplyCmd) migrateTerraform(
} }
if !ok { if !ok {
cmd.Println("Aborting upgrade.") cmd.Println("Aborting upgrade.")
if err := u.upgrader.CleanUpTerraformMigrations(constants.UpgradeDir); err != nil { if err := u.terraformUpgrader.CleanUpTerraformMigrations(constants.UpgradeDir); err != nil {
return res, fmt.Errorf("cleaning up workspace: %w", err) return res, fmt.Errorf("cleaning up workspace: %w", err)
} }
return res, fmt.Errorf("aborted by user") return res, fmt.Errorf("aborted by user")
} }
} }
u.log.Debugf("Applying Terraform migrations") u.log.Debugf("Applying Terraform migrations")
tfOutput, err := u.upgrader.ApplyTerraformMigrations(cmd.Context(), opts) tfOutput, err := u.terraformUpgrader.ApplyTerraformMigrations(cmd.Context(), opts)
if err != nil { if err != nil {
return tfOutput, fmt.Errorf("applying terraform migrations: %w", err) return tfOutput, fmt.Errorf("applying terraform migrations: %w", err)
} }
@ -277,7 +299,9 @@ func (u *upgradeApplyCmd) migrateTerraform(
cmd.Printf("Terraform migrations applied successfully and output written to: %s\n"+ cmd.Printf("Terraform migrations applied successfully and output written to: %s\n"+
"A backup of the pre-upgrade state has been written to: %s\n", "A backup of the pre-upgrade state has been written to: %s\n",
flags.pf.PrefixPath(constants.ClusterIDsFilename), flags.pf.PrefixPath(filepath.Join(opts.UpgradeWorkspace, u.upgrader.GetUpgradeID(), constants.TerraformUpgradeBackupDir))) flags.pf.PrefixPath(constants.ClusterIDsFilename),
flags.pf.PrefixPath(filepath.Join(opts.UpgradeWorkspace, u.terraformUpgrader.UpgradeID(), constants.TerraformUpgradeBackupDir)),
)
} else { } else {
u.log.Debugf("No Terraform diff detected") u.log.Debugf("No Terraform diff detected")
} }
@ -327,7 +351,7 @@ func validK8sVersion(cmd *cobra.Command, version string, yes bool) (validVersion
// confirmIfUpgradeAttestConfigHasDiff checks if the locally configured measurements are different from the cluster's measurements. // confirmIfUpgradeAttestConfigHasDiff checks if the locally configured measurements are different from the cluster's measurements.
// If so the function will ask the user to confirm (if --yes is not set). // If so the function will ask the user to confirm (if --yes is not set).
func (u *upgradeApplyCmd) confirmIfUpgradeAttestConfigHasDiff(cmd *cobra.Command, newConfig config.AttestationCfg, flags upgradeApplyFlags) error { func (u *upgradeApplyCmd) confirmIfUpgradeAttestConfigHasDiff(cmd *cobra.Command, newConfig config.AttestationCfg, flags upgradeApplyFlags) error {
clusterAttestationConfig, err := u.upgrader.GetClusterAttestationConfig(cmd.Context(), newConfig.GetVariant()) clusterAttestationConfig, err := u.kubeUpgrader.GetClusterAttestationConfig(cmd.Context(), newConfig.GetVariant())
if err != nil { if err != nil {
return fmt.Errorf("getting cluster attestation config: %w", err) return fmt.Errorf("getting cluster attestation config: %w", err)
} }
@ -357,10 +381,10 @@ func (u *upgradeApplyCmd) confirmIfUpgradeAttestConfigHasDiff(cmd *cobra.Command
} }
} }
// TODO(elchead): move this outside this function to remove the side effect. // TODO(elchead): move this outside this function to remove the side effect.
if err := u.upgrader.BackupConfigMap(cmd.Context(), constants.JoinConfigMap); err != nil { if err := u.kubeUpgrader.BackupConfigMap(cmd.Context(), constants.JoinConfigMap); err != nil {
return fmt.Errorf("backing up join-config: %w", err) return fmt.Errorf("backing up join-config: %w", err)
} }
if err := u.upgrader.UpdateAttestationConfig(cmd.Context(), newConfig); err != nil { if err := u.kubeUpgrader.UpdateAttestationConfig(cmd.Context(), newConfig); err != nil {
return fmt.Errorf("updating attestation config: %w", err) return fmt.Errorf("updating attestation config: %w", err)
} }
return nil return nil
@ -375,7 +399,11 @@ func (u *upgradeApplyCmd) handleServiceUpgrade(cmd *cobra.Command, conf *config.
if err != nil { if err != nil {
return fmt.Errorf("getting service account URI: %w", err) return fmt.Errorf("getting service account URI: %w", err)
} }
err = u.upgrader.UpgradeHelmServices(cmd.Context(), conf, idFile, flags.upgradeTimeout, helm.DenyDestructive, flags.force, flags.conformance, flags.helmWaitMode, secret, serviceAccURI, validK8sVersion, tfOutput) err = u.helmUpgrader.Upgrade(
cmd.Context(), conf, idFile,
flags.upgradeTimeout, helm.DenyDestructive, flags.force, u.terraformUpgrader.UpgradeID(),
flags.conformance, flags.helmWaitMode, secret, serviceAccURI, validK8sVersion, tfOutput,
)
if errors.Is(err, helm.ErrConfirmationMissing) { if errors.Is(err, helm.ErrConfirmationMissing) {
if !flags.yes { if !flags.yes {
cmd.PrintErrln("WARNING: Upgrading cert-manager will destroy all custom resources you have manually created that are based on the current version of cert-manager.") cmd.PrintErrln("WARNING: Upgrading cert-manager will destroy all custom resources you have manually created that are based on the current version of cert-manager.")
@ -388,7 +416,11 @@ func (u *upgradeApplyCmd) handleServiceUpgrade(cmd *cobra.Command, conf *config.
return nil return nil
} }
} }
err = u.upgrader.UpgradeHelmServices(cmd.Context(), conf, idFile, flags.upgradeTimeout, helm.AllowDestructive, flags.force, flags.conformance, flags.helmWaitMode, secret, serviceAccURI, validK8sVersion, tfOutput) err = u.helmUpgrader.Upgrade(
cmd.Context(), conf, idFile,
flags.upgradeTimeout, helm.AllowDestructive, flags.force, u.terraformUpgrader.UpgradeID(),
flags.conformance, flags.helmWaitMode, secret, serviceAccURI, validK8sVersion, tfOutput,
)
} }
return err return err
@ -470,17 +502,27 @@ type upgradeApplyFlags struct {
helmWaitMode helm.WaitMode helmWaitMode helm.WaitMode
} }
type cloudUpgrader interface { type kubernetesUpgrader interface {
UpgradeNodeVersion(ctx context.Context, conf *config.Config, force bool) error UpgradeNodeVersion(ctx context.Context, conf *config.Config, force bool) error
UpgradeHelmServices(ctx context.Context, config *config.Config, idFile clusterid.File, timeout time.Duration, allowDestructive bool, force bool, conformance bool, helmWaitMode helm.WaitMode, masterSecret uri.MasterSecret, serviceAccURI string, validK8sVersion versions.ValidK8sVersion, tfOutput terraform.ApplyOutput) error
ExtendClusterConfigCertSANs(ctx context.Context, alternativeNames []string) error ExtendClusterConfigCertSANs(ctx context.Context, alternativeNames []string) error
GetClusterAttestationConfig(ctx context.Context, variant variant.Variant) (config.AttestationCfg, error) GetClusterAttestationConfig(ctx context.Context, variant variant.Variant) (config.AttestationCfg, error)
UpdateAttestationConfig(ctx context.Context, newAttestConfig config.AttestationCfg) error UpdateAttestationConfig(ctx context.Context, newAttestConfig config.AttestationCfg) error
GetMeasurementSalt(ctx context.Context) ([]byte, error) GetMeasurementSalt(ctx context.Context) ([]byte, error)
BackupConfigMap(ctx context.Context, name string) error
}
type helmUpgrader interface {
Upgrade(
ctx context.Context, config *config.Config, idFile clusterid.File, timeout time.Duration,
allowDestructive, force bool, upgradeID string, conformance bool, helmWaitMode helm.WaitMode,
masterSecret uri.MasterSecret, serviceAccURI string, validK8sVersion versions.ValidK8sVersion, tfOutput terraform.ApplyOutput,
) error
}
type terraformUpgrader interface {
PlanTerraformMigrations(ctx context.Context, opts upgrade.TerraformUpgradeOptions) (bool, error) PlanTerraformMigrations(ctx context.Context, opts upgrade.TerraformUpgradeOptions) (bool, error)
ApplyTerraformMigrations(ctx context.Context, opts upgrade.TerraformUpgradeOptions) (terraform.ApplyOutput, error) ApplyTerraformMigrations(ctx context.Context, opts upgrade.TerraformUpgradeOptions) (terraform.ApplyOutput, error)
CheckTerraformMigrations(upgradeWorkspace string) error CheckTerraformMigrations(upgradeWorkspace string) error
CleanUpTerraformMigrations(upgradeWorkspace string) error CleanUpTerraformMigrations(upgradeWorkspace string) error
GetUpgradeID() string UpgradeID() string
BackupConfigMap(ctx context.Context, name string) error
} }

View File

@ -9,7 +9,6 @@ package cmd
import ( import (
"bytes" "bytes"
"context" "context"
"errors"
"testing" "testing"
"time" "time"
@ -32,87 +31,106 @@ import (
) )
func TestUpgradeApply(t *testing.T) { func TestUpgradeApply(t *testing.T) {
someErr := errors.New("some error")
testCases := map[string]struct { testCases := map[string]struct {
upgrader *stubUpgrader helmUpgrader *stubHelmUpgrader
kubeUpgrader *stubKubernetesUpgrader
terraformUpgrader *stubTerraformUpgrader
wantErr bool wantErr bool
yesFlag bool yesFlag bool
dontWantJoinConfigBackup bool dontWantJoinConfigBackup bool
stdin string stdin string
}{ }{
"success": { "success": {
upgrader: &stubUpgrader{currentConfig: config.DefaultForAzureSEVSNP()}, kubeUpgrader: &stubKubernetesUpgrader{currentConfig: config.DefaultForAzureSEVSNP()},
yesFlag: true, helmUpgrader: &stubHelmUpgrader{},
terraformUpgrader: &stubTerraformUpgrader{},
yesFlag: true,
}, },
"nodeVersion some error": { "nodeVersion some error": {
upgrader: &stubUpgrader{ kubeUpgrader: &stubKubernetesUpgrader{
currentConfig: config.DefaultForAzureSEVSNP(), currentConfig: config.DefaultForAzureSEVSNP(),
nodeVersionErr: someErr, nodeVersionErr: assert.AnError,
}, },
wantErr: true, helmUpgrader: &stubHelmUpgrader{},
yesFlag: true, terraformUpgrader: &stubTerraformUpgrader{},
wantErr: true,
yesFlag: true,
}, },
"nodeVersion in progress error": { "nodeVersion in progress error": {
upgrader: &stubUpgrader{ kubeUpgrader: &stubKubernetesUpgrader{
currentConfig: config.DefaultForAzureSEVSNP(), currentConfig: config.DefaultForAzureSEVSNP(),
nodeVersionErr: kubernetes.ErrInProgress, nodeVersionErr: kubernetes.ErrInProgress,
}, },
yesFlag: true, helmUpgrader: &stubHelmUpgrader{},
terraformUpgrader: &stubTerraformUpgrader{},
yesFlag: true,
}, },
"helm other error": { "helm other error": {
upgrader: &stubUpgrader{ kubeUpgrader: &stubKubernetesUpgrader{
currentConfig: config.DefaultForAzureSEVSNP(), currentConfig: config.DefaultForAzureSEVSNP(),
helmErr: someErr,
}, },
wantErr: true, helmUpgrader: &stubHelmUpgrader{err: assert.AnError},
yesFlag: true, terraformUpgrader: &stubTerraformUpgrader{},
wantErr: true,
yesFlag: true,
}, },
"check terraform error": { "check terraform error": {
upgrader: &stubUpgrader{ kubeUpgrader: &stubKubernetesUpgrader{
currentConfig: config.DefaultForAzureSEVSNP(), currentConfig: config.DefaultForAzureSEVSNP(),
checkTerraformErr: someErr,
}, },
wantErr: true, helmUpgrader: &stubHelmUpgrader{},
yesFlag: true, terraformUpgrader: &stubTerraformUpgrader{checkTerraformErr: assert.AnError},
wantErr: true,
yesFlag: true,
}, },
"abort": { "abort": {
upgrader: &stubUpgrader{ kubeUpgrader: &stubKubernetesUpgrader{
currentConfig: config.DefaultForAzureSEVSNP(), currentConfig: config.DefaultForAzureSEVSNP(),
terraformDiff: true,
}, },
wantErr: true, helmUpgrader: &stubHelmUpgrader{},
stdin: "no\n", terraformUpgrader: &stubTerraformUpgrader{terraformDiff: true},
wantErr: true,
stdin: "no\n",
}, },
"clean terraform error": { "clean terraform error": {
upgrader: &stubUpgrader{ kubeUpgrader: &stubKubernetesUpgrader{
currentConfig: config.DefaultForAzureSEVSNP(), currentConfig: config.DefaultForAzureSEVSNP(),
cleanTerraformErr: someErr, },
helmUpgrader: &stubHelmUpgrader{},
terraformUpgrader: &stubTerraformUpgrader{
cleanTerraformErr: assert.AnError,
terraformDiff: true, terraformDiff: true,
}, },
wantErr: true, wantErr: true,
stdin: "no\n", stdin: "no\n",
}, },
"plan terraform error": { "plan terraform error": {
upgrader: &stubUpgrader{ kubeUpgrader: &stubKubernetesUpgrader{
currentConfig: config.DefaultForAzureSEVSNP(), currentConfig: config.DefaultForAzureSEVSNP(),
planTerraformErr: someErr,
}, },
wantErr: true, helmUpgrader: &stubHelmUpgrader{},
yesFlag: true, terraformUpgrader: &stubTerraformUpgrader{planTerraformErr: assert.AnError},
wantErr: true,
yesFlag: true,
}, },
"apply terraform error": { "apply terraform error": {
upgrader: &stubUpgrader{ kubeUpgrader: &stubKubernetesUpgrader{
currentConfig: config.DefaultForAzureSEVSNP(), currentConfig: config.DefaultForAzureSEVSNP(),
applyTerraformErr: someErr, },
helmUpgrader: &stubHelmUpgrader{},
terraformUpgrader: &stubTerraformUpgrader{
applyTerraformErr: assert.AnError,
terraformDiff: true, terraformDiff: true,
}, },
wantErr: true, wantErr: true,
yesFlag: true, yesFlag: true,
}, },
"do no backup join-config when remote attestation config is the same": { "do no backup join-config when remote attestation config is the same": {
upgrader: &stubUpgrader{ kubeUpgrader: &stubKubernetesUpgrader{
currentConfig: fakeAzureAttestationConfigFromCluster(context.Background(), t, cloudprovider.Azure), currentConfig: fakeAzureAttestationConfigFromCluster(context.Background(), t, cloudprovider.Azure),
}, },
helmUpgrader: &stubHelmUpgrader{},
terraformUpgrader: &stubTerraformUpgrader{},
yesFlag: true, yesFlag: true,
dontWantJoinConfigBackup: true, dontWantJoinConfigBackup: true,
}, },
@ -141,7 +159,15 @@ func TestUpgradeApply(t *testing.T) {
require.NoError(handler.WriteJSON(constants.ClusterIDsFilename, clusterid.File{})) require.NoError(handler.WriteJSON(constants.ClusterIDsFilename, clusterid.File{}))
require.NoError(handler.WriteJSON(constants.MasterSecretFilename, uri.MasterSecret{})) require.NoError(handler.WriteJSON(constants.MasterSecretFilename, uri.MasterSecret{}))
upgrader := upgradeApplyCmd{upgrader: tc.upgrader, log: logger.NewTest(t), configFetcher: stubAttestationFetcher{}, clusterShower: &stubShowCluster{}, fileHandler: handler} upgrader := upgradeApplyCmd{
kubeUpgrader: tc.kubeUpgrader,
helmUpgrader: tc.helmUpgrader,
terraformUpgrader: tc.terraformUpgrader,
log: logger.NewTest(t),
configFetcher: stubAttestationFetcher{},
clusterShower: &stubShowCluster{},
fileHandler: handler,
}
err := upgrader.upgradeApply(cmd) err := upgrader.upgradeApply(cmd)
if tc.wantErr { if tc.wantErr {
@ -149,70 +175,79 @@ func TestUpgradeApply(t *testing.T) {
return return
} }
assert.NoError(err) assert.NoError(err)
assert.Equal(!tc.dontWantJoinConfigBackup, tc.upgrader.backupWasCalled) assert.Equal(!tc.dontWantJoinConfigBackup, tc.kubeUpgrader.backupWasCalled)
}) })
} }
} }
type stubUpgrader struct { type stubHelmUpgrader struct {
currentConfig config.AttestationCfg err error
nodeVersionErr error }
helmErr error
func (u stubHelmUpgrader) Upgrade(
_ context.Context, _ *config.Config, _ clusterid.File, _ time.Duration, _, _ bool, _ string, _ bool,
_ helm.WaitMode, _ uri.MasterSecret, _ string, _ versions.ValidK8sVersion, _ terraform.ApplyOutput,
) error {
return u.err
}
type stubKubernetesUpgrader struct {
backupWasCalled bool
nodeVersionErr error
currentConfig config.AttestationCfg
}
func (u stubKubernetesUpgrader) GetMeasurementSalt(_ context.Context) ([]byte, error) {
return []byte{}, nil
}
func (u *stubKubernetesUpgrader) BackupConfigMap(_ context.Context, _ string) error {
u.backupWasCalled = true
return nil
}
func (u stubKubernetesUpgrader) UpgradeNodeVersion(_ context.Context, _ *config.Config, _ bool) error {
return u.nodeVersionErr
}
func (u stubKubernetesUpgrader) UpdateAttestationConfig(_ context.Context, _ config.AttestationCfg) error {
return nil
}
func (u stubKubernetesUpgrader) GetClusterAttestationConfig(_ context.Context, _ variant.Variant) (config.AttestationCfg, error) {
return u.currentConfig, nil
}
func (u stubKubernetesUpgrader) ExtendClusterConfigCertSANs(_ context.Context, _ []string) error {
return nil
}
type stubTerraformUpgrader struct {
terraformDiff bool terraformDiff bool
planTerraformErr error planTerraformErr error
checkTerraformErr error checkTerraformErr error
applyTerraformErr error applyTerraformErr error
cleanTerraformErr error cleanTerraformErr error
backupWasCalled bool
} }
func (u stubUpgrader) GetMeasurementSalt(_ context.Context) ([]byte, error) { func (u stubTerraformUpgrader) CheckTerraformMigrations(_ string) error {
return []byte{}, nil
}
func (u stubUpgrader) GetUpgradeID() string {
return "test-upgrade"
}
func (u *stubUpgrader) BackupConfigMap(_ context.Context, _ string) error {
u.backupWasCalled = true
return nil
}
func (u stubUpgrader) UpgradeNodeVersion(_ context.Context, _ *config.Config, _ bool) error {
return u.nodeVersionErr
}
func (u stubUpgrader) UpgradeHelmServices(_ context.Context, _ *config.Config, _ clusterid.File, _ time.Duration, _, _, _ bool, _ helm.WaitMode, _ uri.MasterSecret, _ string, _ versions.ValidK8sVersion, _ terraform.ApplyOutput) error {
return u.helmErr
}
func (u stubUpgrader) UpdateAttestationConfig(_ context.Context, _ config.AttestationCfg) error {
return nil
}
func (u stubUpgrader) GetClusterAttestationConfig(_ context.Context, _ variant.Variant) (config.AttestationCfg, error) {
return u.currentConfig, nil
}
func (u stubUpgrader) CheckTerraformMigrations(_ string) error {
return u.checkTerraformErr return u.checkTerraformErr
} }
func (u stubUpgrader) CleanUpTerraformMigrations(_ string) error { func (u stubTerraformUpgrader) CleanUpTerraformMigrations(_ string) error {
return u.cleanTerraformErr return u.cleanTerraformErr
} }
func (u stubUpgrader) PlanTerraformMigrations(context.Context, upgrade.TerraformUpgradeOptions) (bool, error) { func (u stubTerraformUpgrader) PlanTerraformMigrations(context.Context, upgrade.TerraformUpgradeOptions) (bool, error) {
return u.terraformDiff, u.planTerraformErr return u.terraformDiff, u.planTerraformErr
} }
func (u stubUpgrader) ApplyTerraformMigrations(context.Context, upgrade.TerraformUpgradeOptions) (terraform.ApplyOutput, error) { func (u stubTerraformUpgrader) ApplyTerraformMigrations(context.Context, upgrade.TerraformUpgradeOptions) (terraform.ApplyOutput, error) {
return terraform.ApplyOutput{}, u.applyTerraformErr return terraform.ApplyOutput{}, u.applyTerraformErr
} }
func (u stubUpgrader) ExtendClusterConfigCertSANs(_ context.Context, _ []string) error { func (u stubTerraformUpgrader) UpgradeID() string {
return nil return "test-upgrade"
} }
func fakeAzureAttestationConfigFromCluster(ctx context.Context, t *testing.T, provider cloudprovider.Provider) config.AttestationCfg { func fakeAzureAttestationConfigFromCluster(ctx context.Context, t *testing.T, provider cloudprovider.Provider) config.AttestationCfg {

View File

@ -12,6 +12,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"path/filepath"
"sort" "sort"
"strings" "strings"
@ -71,11 +72,14 @@ func runUpgradeCheck(cmd *cobra.Command, _ []string) error {
} }
fileHandler := file.NewHandler(afero.NewOsFs()) fileHandler := file.NewHandler(afero.NewOsFs())
checker, err := kubernetes.NewUpgrader( upgradeID := generateUpgradeID(upgradeCmdKindCheck)
cmd.Context(), cmd.OutOrStdout(),
constants.UpgradeDir, constants.AdminConfFilename, tfClient, err := terraform.New(cmd.Context(), filepath.Join(constants.UpgradeDir, upgradeID, constants.TerraformUpgradeWorkingDir))
fileHandler, log, kubernetes.UpgradeCmdKindCheck, if err != nil {
) return fmt.Errorf("setting up terraform client: %w", err)
}
kubeChecker, err := kubernetes.NewUpgrader(cmd.OutOrStdout(), constants.AdminConfFilename, log)
if err != nil { if err != nil {
return fmt.Errorf("setting up Kubernetes upgrader: %w", err) return fmt.Errorf("setting up Kubernetes upgrader: %w", err)
} }
@ -89,7 +93,7 @@ func runUpgradeCheck(cmd *cobra.Command, _ []string) error {
canUpgradeCheck: featureset.CanUpgradeCheck, canUpgradeCheck: featureset.CanUpgradeCheck,
collect: &versionCollector{ collect: &versionCollector{
writer: cmd.OutOrStderr(), writer: cmd.OutOrStderr(),
checker: checker, kubeChecker: kubeChecker,
verListFetcher: versionfetcher, verListFetcher: versionfetcher,
fileHandler: fileHandler, fileHandler: fileHandler,
client: http.DefaultClient, client: http.DefaultClient,
@ -99,8 +103,8 @@ func runUpgradeCheck(cmd *cobra.Command, _ []string) error {
log: log, log: log,
versionsapi: versionfetcher, versionsapi: versionfetcher,
}, },
checker: checker, terraformChecker: upgrade.NewTerraformUpgrader(tfClient, cmd.OutOrStdout(), fileHandler, upgradeID),
log: log, log: log,
} }
return up.upgradeCheck(cmd, fileHandler, attestationconfigapi.NewFetcher(), flags) return up.upgradeCheck(cmd, fileHandler, attestationconfigapi.NewFetcher(), flags)
@ -143,10 +147,10 @@ func parseUpgradeCheckFlags(cmd *cobra.Command) (upgradeCheckFlags, error) {
} }
type upgradeCheckCmd struct { type upgradeCheckCmd struct {
canUpgradeCheck bool canUpgradeCheck bool
collect collector collect collector
checker upgradeChecker terraformChecker terraformChecker
log debugLog log debugLog
} }
// upgradePlan plans an upgrade of a Constellation cluster. // upgradePlan plans an upgrade of a Constellation cluster.
@ -212,7 +216,7 @@ func (u *upgradeCheckCmd) upgradeCheck(cmd *cobra.Command, fileHandler file.Hand
// u.upgrader.AddManualStateMigration(migration) // u.upgrader.AddManualStateMigration(migration)
// } // }
if err := u.checker.CheckTerraformMigrations(constants.UpgradeDir); err != nil { if err := u.terraformChecker.CheckTerraformMigrations(constants.UpgradeDir); err != nil {
return fmt.Errorf("checking workspace: %w", err) return fmt.Errorf("checking workspace: %w", err)
} }
@ -233,12 +237,12 @@ func (u *upgradeCheckCmd) upgradeCheck(cmd *cobra.Command, fileHandler file.Hand
cmd.Println("The following Terraform migrations are available with this CLI:") cmd.Println("The following Terraform migrations are available with this CLI:")
// Check if there are any Terraform migrations // Check if there are any Terraform migrations
hasDiff, err := u.checker.PlanTerraformMigrations(cmd.Context(), opts) hasDiff, err := u.terraformChecker.PlanTerraformMigrations(cmd.Context(), opts)
if err != nil { if err != nil {
return fmt.Errorf("planning terraform migrations: %w", err) return fmt.Errorf("planning terraform migrations: %w", err)
} }
defer func() { defer func() {
if err := u.checker.CleanUpTerraformMigrations(constants.UpgradeDir); err != nil { if err := u.terraformChecker.CleanUpTerraformMigrations(constants.UpgradeDir); err != nil {
u.log.Debugf("Failed to clean up Terraform migrations: %v", err) u.log.Debugf("Failed to clean up Terraform migrations: %v", err)
} }
}() }()
@ -323,7 +327,7 @@ type collector interface {
type versionCollector struct { type versionCollector struct {
writer io.Writer writer io.Writer
checker upgradeChecker kubeChecker kubernetesChecker
verListFetcher versionListFetcher verListFetcher versionListFetcher
fileHandler file.Handler fileHandler file.Handler
client *http.Client client *http.Client
@ -382,12 +386,12 @@ func (v *versionCollector) currentVersions(ctx context.Context) (currentVersionI
return currentVersionInfo{}, fmt.Errorf("getting service versions: %w", err) return currentVersionInfo{}, fmt.Errorf("getting service versions: %w", err)
} }
imageVersion, err := getCurrentImageVersion(ctx, v.checker) imageVersion, err := getCurrentImageVersion(ctx, v.kubeChecker)
if err != nil { if err != nil {
return currentVersionInfo{}, fmt.Errorf("getting image version: %w", err) return currentVersionInfo{}, fmt.Errorf("getting image version: %w", err)
} }
k8sVersion, err := getCurrentKubernetesVersion(ctx, v.checker) k8sVersion, err := getCurrentKubernetesVersion(ctx, v.kubeChecker)
if err != nil { if err != nil {
return currentVersionInfo{}, fmt.Errorf("getting Kubernetes version: %w", err) return currentVersionInfo{}, fmt.Errorf("getting Kubernetes version: %w", err)
} }
@ -588,7 +592,7 @@ func (v *versionUpgrade) writeConfig(conf *config.Config, fileHandler file.Handl
// getCurrentImageVersion retrieves the semantic version of the image currently installed in the cluster. // getCurrentImageVersion retrieves the semantic version of the image currently installed in the cluster.
// If the cluster is not using a release image, an error is returned. // If the cluster is not using a release image, an error is returned.
func getCurrentImageVersion(ctx context.Context, checker upgradeChecker) (string, error) { func getCurrentImageVersion(ctx context.Context, checker kubernetesChecker) (string, error) {
imageVersion, err := checker.CurrentImage(ctx) imageVersion, err := checker.CurrentImage(ctx)
if err != nil { if err != nil {
return "", err return "", err
@ -602,7 +606,7 @@ func getCurrentImageVersion(ctx context.Context, checker upgradeChecker) (string
} }
// getCurrentKubernetesVersion retrieves the semantic version of Kubernetes currently installed in the cluster. // getCurrentKubernetesVersion retrieves the semantic version of Kubernetes currently installed in the cluster.
func getCurrentKubernetesVersion(ctx context.Context, checker upgradeChecker) (string, error) { func getCurrentKubernetesVersion(ctx context.Context, checker kubernetesChecker) (string, error) {
k8sVersion, err := checker.CurrentKubernetesVersion(ctx) k8sVersion, err := checker.CurrentKubernetesVersion(ctx)
if err != nil { if err != nil {
return "", err return "", err
@ -745,9 +749,12 @@ type upgradeCheckFlags struct {
terraformLogLevel terraform.LogLevel terraformLogLevel terraform.LogLevel
} }
type upgradeChecker interface { type kubernetesChecker interface {
CurrentImage(ctx context.Context) (string, error) CurrentImage(ctx context.Context) (string, error)
CurrentKubernetesVersion(ctx context.Context) (string, error) CurrentKubernetesVersion(ctx context.Context) (string, error)
}
type terraformChecker interface {
PlanTerraformMigrations(ctx context.Context, opts upgrade.TerraformUpgradeOptions) (bool, error) PlanTerraformMigrations(ctx context.Context, opts upgrade.TerraformUpgradeOptions) (bool, error)
CheckTerraformMigrations(upgradeWorkspace string) error CheckTerraformMigrations(upgradeWorkspace string) error
CleanUpTerraformMigrations(upgradeWorkspace string) error CleanUpTerraformMigrations(upgradeWorkspace string) error

View File

@ -110,22 +110,22 @@ func TestBuildString(t *testing.T) {
func TestGetCurrentImageVersion(t *testing.T) { func TestGetCurrentImageVersion(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
stubUpgradeChecker stubUpgradeChecker stubKubernetesChecker stubKubernetesChecker
wantErr bool wantErr bool
}{ }{
"valid version": { "valid version": {
stubUpgradeChecker: stubUpgradeChecker{ stubKubernetesChecker: stubKubernetesChecker{
image: "v1.0.0", image: "v1.0.0",
}, },
}, },
"invalid version": { "invalid version": {
stubUpgradeChecker: stubUpgradeChecker{ stubKubernetesChecker: stubKubernetesChecker{
image: "invalid", image: "invalid",
}, },
wantErr: true, wantErr: true,
}, },
"GetCurrentImage error": { "GetCurrentImage error": {
stubUpgradeChecker: stubUpgradeChecker{ stubKubernetesChecker: stubKubernetesChecker{
err: errors.New("error"), err: errors.New("error"),
}, },
wantErr: true, wantErr: true,
@ -136,7 +136,7 @@ func TestGetCurrentImageVersion(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
version, err := getCurrentImageVersion(context.Background(), tc.stubUpgradeChecker) version, err := getCurrentImageVersion(context.Background(), tc.stubKubernetesChecker)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)
return return
@ -215,19 +215,19 @@ func TestUpgradeCheck(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
collector stubVersionCollector collector stubVersionCollector
csp cloudprovider.Provider csp cloudprovider.Provider
checker stubUpgradeChecker checker stubTerraformChecker
cliVersion string cliVersion string
wantError bool wantError bool
}{ }{
"upgrades gcp": { "upgrades gcp": {
collector: collector, collector: collector,
checker: stubUpgradeChecker{}, checker: stubTerraformChecker{},
csp: cloudprovider.GCP, csp: cloudprovider.GCP,
cliVersion: "v1.0.0", cliVersion: "v1.0.0",
}, },
"terraform err": { "terraform err": {
collector: collector, collector: collector,
checker: stubUpgradeChecker{ checker: stubTerraformChecker{
err: assert.AnError, err: assert.AnError,
}, },
csp: cloudprovider.GCP, csp: cloudprovider.GCP,
@ -245,10 +245,10 @@ func TestUpgradeCheck(t *testing.T) {
require.NoError(fileHandler.WriteYAML(constants.ConfigFilename, cfg)) require.NoError(fileHandler.WriteYAML(constants.ConfigFilename, cfg))
checkCmd := upgradeCheckCmd{ checkCmd := upgradeCheckCmd{
canUpgradeCheck: true, canUpgradeCheck: true,
collect: &tc.collector, collect: &tc.collector,
checker: tc.checker, terraformChecker: tc.checker,
log: logger.NewTest(t), log: logger.NewTest(t),
} }
cmd := newUpgradeCheckCmd() cmd := newUpgradeCheckCmd()
@ -317,31 +317,35 @@ func (s *stubVersionCollector) filterCompatibleCLIVersions(_ context.Context, _
return s.newCompatibleCLIVersionsList, nil return s.newCompatibleCLIVersionsList, nil
} }
type stubUpgradeChecker struct { type stubKubernetesChecker struct {
image string image string
k8sVersion string k8sVersion string
tfDiff bool
err error err error
} }
func (u stubUpgradeChecker) CurrentImage(context.Context) (string, error) { func (s stubKubernetesChecker) CurrentImage(context.Context) (string, error) {
return u.image, u.err return s.image, s.err
} }
func (u stubUpgradeChecker) CurrentKubernetesVersion(context.Context) (string, error) { func (s stubKubernetesChecker) CurrentKubernetesVersion(context.Context) (string, error) {
return u.k8sVersion, u.err return s.k8sVersion, s.err
} }
func (u stubUpgradeChecker) PlanTerraformMigrations(context.Context, upgrade.TerraformUpgradeOptions) (bool, error) { type stubTerraformChecker struct {
return u.tfDiff, u.err tfDiff bool
err error
} }
func (u stubUpgradeChecker) CheckTerraformMigrations(_ string) error { func (s stubTerraformChecker) PlanTerraformMigrations(context.Context, upgrade.TerraformUpgradeOptions) (bool, error) {
return u.err return s.tfDiff, s.err
} }
func (u stubUpgradeChecker) CleanUpTerraformMigrations(_ string) error { func (s stubTerraformChecker) CheckTerraformMigrations(_ string) error {
return u.err return s.err
}
func (s stubTerraformChecker) CleanUpTerraformMigrations(_ string) error {
return s.err
} }
func TestNewCLIVersions(t *testing.T) { func TestNewCLIVersions(t *testing.T) {

View File

@ -11,25 +11,17 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/cli/internal/kubernetes", importpath = "github.com/edgelesssys/constellation/v2/cli/internal/kubernetes",
visibility = ["//cli:__subpackages__"], visibility = ["//cli:__subpackages__"],
deps = [ deps = [
"//cli/internal/clusterid",
"//cli/internal/helm",
"//cli/internal/terraform",
"//cli/internal/upgrade",
"//internal/api/versionsapi", "//internal/api/versionsapi",
"//internal/attestation/variant", "//internal/attestation/variant",
"//internal/cloud/cloudprovider", "//internal/cloud/cloudprovider",
"//internal/compatibility", "//internal/compatibility",
"//internal/config", "//internal/config",
"//internal/constants", "//internal/constants",
"//internal/file",
"//internal/imagefetcher", "//internal/imagefetcher",
"//internal/kms/uri",
"//internal/kubernetes", "//internal/kubernetes",
"//internal/kubernetes/kubectl",
"//internal/versions", "//internal/versions",
"//internal/versions/components", "//internal/versions/components",
"//operators/constellation-node-operator/api/v1alpha1", "//operators/constellation-node-operator/api/v1alpha1",
"@com_github_google_uuid//:uuid",
"@io_k8s_api//core/v1:core", "@io_k8s_api//core/v1:core",
"@io_k8s_apimachinery//pkg/api/errors", "@io_k8s_apimachinery//pkg/api/errors",
"@io_k8s_apimachinery//pkg/apis/meta/v1:meta", "@io_k8s_apimachinery//pkg/apis/meta/v1:meta",

View File

@ -12,30 +12,20 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"path/filepath"
"sort" "sort"
"strings" "strings"
"time"
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
"github.com/edgelesssys/constellation/v2/cli/internal/helm"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/cli/internal/upgrade"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi" "github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/compatibility" "github.com/edgelesssys/constellation/v2/internal/compatibility"
"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/file"
"github.com/edgelesssys/constellation/v2/internal/imagefetcher" "github.com/edgelesssys/constellation/v2/internal/imagefetcher"
"github.com/edgelesssys/constellation/v2/internal/kms/uri"
internalk8s "github.com/edgelesssys/constellation/v2/internal/kubernetes" internalk8s "github.com/edgelesssys/constellation/v2/internal/kubernetes"
"github.com/edgelesssys/constellation/v2/internal/kubernetes/kubectl"
"github.com/edgelesssys/constellation/v2/internal/versions" "github.com/edgelesssys/constellation/v2/internal/versions"
"github.com/edgelesssys/constellation/v2/internal/versions/components" "github.com/edgelesssys/constellation/v2/internal/versions/components"
updatev1alpha1 "github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api/v1alpha1" updatev1alpha1 "github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api/v1alpha1"
"github.com/google/uuid"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -49,16 +39,6 @@ import (
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
// UpgradeCmdKind is the kind of the upgrade command (check, apply).
type UpgradeCmdKind int
const (
// UpgradeCmdKindCheck corresponds to the upgrade check command.
UpgradeCmdKindCheck UpgradeCmdKind = iota
// UpgradeCmdKindApply corresponds to the upgrade apply command.
UpgradeCmdKindApply
)
// ErrInProgress signals that an upgrade is in progress inside the cluster. // ErrInProgress signals that an upgrade is in progress inside the cluster.
var ErrInProgress = errors.New("upgrade in progress") var ErrInProgress = errors.New("upgrade in progress")
@ -91,69 +71,36 @@ func (e *applyError) Error() string {
type Upgrader struct { type Upgrader struct {
stableInterface StableInterface stableInterface StableInterface
dynamicInterface DynamicInterface dynamicInterface DynamicInterface
helmClient helmInterface
imageFetcher imageFetcher imageFetcher imageFetcher
outWriter io.Writer outWriter io.Writer
tfUpgrader *upgrade.TerraformUpgrader
log debugLog log debugLog
upgradeID string
} }
// NewUpgrader returns a new Upgrader. // NewUpgrader returns a new Upgrader.
func NewUpgrader( func NewUpgrader(outWriter io.Writer, kubeConfigPath string, log debugLog) (*Upgrader, error) {
ctx context.Context, outWriter io.Writer, upgradeWorkspace, kubeConfigPath string,
fileHandler file.Handler, log debugLog, upgradeCmdKind UpgradeCmdKind,
) (*Upgrader, error) {
upgradeID := "upgrade-" + time.Now().Format("20060102150405") + "-" + strings.Split(uuid.New().String(), "-")[0]
if upgradeCmdKind == UpgradeCmdKindCheck {
// When performing an upgrade check, the upgrade directory will only be used temporarily to store the
// Terraform state. The directory is deleted after the check is finished.
// Therefore, add a tmp-suffix to the upgrade ID to indicate that the directory will be cleared after the check.
upgradeID += "-tmp"
}
u := &Upgrader{
imageFetcher: imagefetcher.New(),
outWriter: outWriter,
log: log,
upgradeID: upgradeID,
}
kubeClient, err := newClient(kubeConfigPath) kubeClient, err := newClient(kubeConfigPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
u.stableInterface = &stableClient{client: kubeClient}
// use unstructured client to avoid importing the operator packages // use unstructured client to avoid importing the operator packages
kubeConfig, err := clientcmd.BuildConfigFromFlags("", kubeConfigPath) kubeConfig, err := clientcmd.BuildConfigFromFlags("", kubeConfigPath)
if err != nil { if err != nil {
return nil, fmt.Errorf("building kubernetes config: %w", err) return nil, fmt.Errorf("building kubernetes config: %w", err)
} }
unstructuredClient, err := dynamic.NewForConfig(kubeConfig) unstructuredClient, err := dynamic.NewForConfig(kubeConfig)
if err != nil { if err != nil {
return nil, fmt.Errorf("setting up custom resource client: %w", err) return nil, fmt.Errorf("setting up custom resource client: %w", err)
} }
u.dynamicInterface = &NodeVersionClient{client: unstructuredClient}
helmClient, err := helm.NewUpgradeClient(kubectl.New(), upgradeWorkspace, kubeConfigPath, constants.HelmNamespace, log) return &Upgrader{
if err != nil { imageFetcher: imagefetcher.New(),
return nil, fmt.Errorf("setting up helm client: %w", err) outWriter: outWriter,
} log: log,
u.helmClient = helmClient stableInterface: &stableClient{client: kubeClient},
dynamicInterface: &NodeVersionClient{client: unstructuredClient},
tfClient, err := terraform.New(ctx, filepath.Join(upgradeWorkspace, upgradeID, constants.TerraformUpgradeWorkingDir)) }, nil
if err != nil {
return nil, fmt.Errorf("setting up terraform client: %w", err)
}
tfUpgrader, err := upgrade.NewTerraformUpgrader(tfClient, outWriter, fileHandler)
if err != nil {
return nil, fmt.Errorf("setting up terraform upgrader: %w", err)
}
u.tfUpgrader = tfUpgrader
return u, nil
} }
// GetMeasurementSalt returns the measurementSalt from the join-config. // GetMeasurementSalt returns the measurementSalt from the join-config.
@ -169,47 +116,6 @@ func (u *Upgrader) GetMeasurementSalt(ctx context.Context) ([]byte, error) {
return salt, nil return salt, nil
} }
// GetUpgradeID returns the upgrade ID.
func (u *Upgrader) GetUpgradeID() string {
return u.upgradeID
}
// CheckTerraformMigrations checks whether Terraform migrations are possible in the current workspace.
// If the files that will be written during the upgrade already exist, it returns an error.
func (u *Upgrader) CheckTerraformMigrations(upgradeWorkspace string) error {
return u.tfUpgrader.CheckTerraformMigrations(upgradeWorkspace, u.upgradeID, constants.TerraformUpgradeBackupDir)
}
// CleanUpTerraformMigrations cleans up the Terraform migration workspace, for example when an upgrade is
// aborted by the user.
func (u *Upgrader) CleanUpTerraformMigrations(upgradeWorkspace string) error {
return u.tfUpgrader.CleanUpTerraformMigrations(upgradeWorkspace, u.upgradeID)
}
// PlanTerraformMigrations prepares the upgrade workspace and plans the Terraform migrations for the Constellation upgrade.
// If a diff exists, it's being written to the upgrader's output writer. It also returns
// a bool indicating whether a diff exists.
func (u *Upgrader) PlanTerraformMigrations(ctx context.Context, opts upgrade.TerraformUpgradeOptions) (bool, error) {
return u.tfUpgrader.PlanTerraformMigrations(ctx, opts, u.upgradeID)
}
// ApplyTerraformMigrations applies the migrations planned by PlanTerraformMigrations.
// If PlanTerraformMigrations has not been executed before, it will return an error.
// In case of a successful upgrade, the output will be written to the specified file and the old Terraform directory is replaced
// By the new one.
func (u *Upgrader) ApplyTerraformMigrations(ctx context.Context, opts upgrade.TerraformUpgradeOptions) (terraform.ApplyOutput, error) {
return u.tfUpgrader.ApplyTerraformMigrations(ctx, opts, u.upgradeID)
}
// UpgradeHelmServices upgrade helm services.
func (u *Upgrader) UpgradeHelmServices(ctx context.Context, config *config.Config, idFile clusterid.File, timeout time.Duration,
allowDestructive bool, force bool, conformance bool, helmWaitMode helm.WaitMode, masterSecret uri.MasterSecret, serviceAccURI string,
validK8sVersion versions.ValidK8sVersion, output terraform.ApplyOutput,
) error {
return u.helmClient.Upgrade(ctx, config, idFile, timeout, allowDestructive, force, u.upgradeID, conformance,
helmWaitMode, masterSecret, serviceAccURI, validK8sVersion, output)
}
// UpgradeNodeVersion upgrades the cluster's NodeVersion object and in turn triggers image & k8s version upgrades. // UpgradeNodeVersion upgrades the cluster's NodeVersion object and in turn triggers image & k8s version upgrades.
// The versions set in the config are validated against the versions running in the cluster. // The versions set in the config are validated against the versions running in the cluster.
func (u *Upgrader) UpgradeNodeVersion(ctx context.Context, conf *config.Config, force bool) error { func (u *Upgrader) UpgradeNodeVersion(ctx context.Context, conf *config.Config, force bool) error {
@ -569,10 +475,6 @@ func upgradeInProgress(nodeVersion updatev1alpha1.NodeVersion) bool {
return false return false
} }
type helmInterface interface {
Upgrade(ctx context.Context, config *config.Config, idFile clusterid.File, timeout time.Duration, allowDestructive, force bool, upgradeID string, conformance bool, helmWaitMode helm.WaitMode, masterSecret uri.MasterSecret, serviceAccURI string, validK8sVersion versions.ValidK8sVersion, output terraform.ApplyOutput) error
}
type debugLog interface { type debugLog interface {
Debugf(format string, args ...any) Debugf(format string, args ...any)
Sync() Sync()

View File

@ -21,24 +21,6 @@ import (
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
) )
// NewTerraformUpgrader returns a new TerraformUpgrader.
func NewTerraformUpgrader(tfClient tfResourceClient, outWriter io.Writer, fileHandler file.Handler) (*TerraformUpgrader, error) {
return &TerraformUpgrader{
tf: tfClient,
policyPatcher: cloudcmd.NewAzurePolicyPatcher(),
outWriter: outWriter,
fileHandler: fileHandler,
}, nil
}
// TerraformUpgrader is responsible for performing Terraform migrations on cluster upgrades.
type TerraformUpgrader struct {
tf tfResourceClient
policyPatcher policyPatcher
outWriter io.Writer
fileHandler file.Handler
}
// TerraformUpgradeOptions are the options used for the Terraform upgrade. // TerraformUpgradeOptions are the options used for the Terraform upgrade.
type TerraformUpgradeOptions struct { type TerraformUpgradeOptions struct {
// LogLevel is the log level used for Terraform. // LogLevel is the log level used for Terraform.
@ -51,55 +33,43 @@ type TerraformUpgradeOptions struct {
UpgradeWorkspace string UpgradeWorkspace string
} }
// CheckTerraformMigrations checks whether Terraform migrations are possible in the current workspace. // TerraformUpgrader is responsible for performing Terraform migrations on cluster upgrades.
func checkTerraformMigrations(file file.Handler, upgradeWorkspace, upgradeID, upgradeSubDir string) error { type TerraformUpgrader struct {
var existingFiles []string tf tfResourceClient
filesToCheck := []string{ policyPatcher policyPatcher
filepath.Join(upgradeWorkspace, upgradeID, upgradeSubDir), outWriter io.Writer
} fileHandler file.Handler
upgradeID string
}
for _, f := range filesToCheck { // NewTerraformUpgrader returns a new TerraformUpgrader.
if err := checkFileExists(file, &existingFiles, f); err != nil { func NewTerraformUpgrader(tfClient tfResourceClient, outWriter io.Writer, fileHandler file.Handler, upgradeID string,
return fmt.Errorf("checking terraform migrations: %w", err) ) *TerraformUpgrader {
} return &TerraformUpgrader{
tf: tfClient,
policyPatcher: cloudcmd.NewAzurePolicyPatcher(),
outWriter: outWriter,
fileHandler: fileHandler,
upgradeID: upgradeID,
} }
if len(existingFiles) > 0 {
return fmt.Errorf("file(s) %s already exist", strings.Join(existingFiles, ", "))
}
return nil
} }
// CheckTerraformMigrations checks whether Terraform migrations are possible in the current workspace. // CheckTerraformMigrations checks whether Terraform migrations are possible in the current workspace.
// If the files that will be written during the upgrade already exist, it returns an error. // If the files that will be written during the upgrade already exist, it returns an error.
func (u *TerraformUpgrader) CheckTerraformMigrations(upgradeWorkspace, upgradeID, upgradeSubDir string) error { func (u *TerraformUpgrader) CheckTerraformMigrations(upgradeWorkspace string) error {
return checkTerraformMigrations(u.fileHandler, upgradeWorkspace, upgradeID, upgradeSubDir) return checkTerraformMigrations(u.fileHandler, upgradeWorkspace, u.upgradeID, constants.TerraformUpgradeBackupDir)
}
// checkFileExists checks whether a file exists and adds it to the existingFiles slice if it does.
func checkFileExists(fileHandler file.Handler, existingFiles *[]string, filename string) error {
_, err := fileHandler.Stat(filename)
if err != nil {
if !os.IsNotExist(err) {
return fmt.Errorf("checking %s: %w", filename, err)
}
return nil
}
*existingFiles = append(*existingFiles, filename)
return nil
} }
// PlanTerraformMigrations prepares the upgrade workspace and plans the Terraform migrations for the Constellation upgrade. // PlanTerraformMigrations prepares the upgrade workspace and plans the Terraform migrations for the Constellation upgrade.
// If a diff exists, it's being written to the upgrader's output writer. It also returns // If a diff exists, it's being written to the upgrader's output writer. It also returns
// a bool indicating whether a diff exists. // a bool indicating whether a diff exists.
func (u *TerraformUpgrader) PlanTerraformMigrations(ctx context.Context, opts TerraformUpgradeOptions, upgradeID string) (bool, error) { func (u *TerraformUpgrader) PlanTerraformMigrations(ctx context.Context, opts TerraformUpgradeOptions) (bool, error) {
// Prepare the new Terraform workspace and backup the old one // Prepare the new Terraform workspace and backup the old one
err := u.tf.PrepareUpgradeWorkspace( err := u.tf.PrepareUpgradeWorkspace(
filepath.Join("terraform", strings.ToLower(opts.CSP.String())), filepath.Join("terraform", strings.ToLower(opts.CSP.String())),
opts.TFWorkspace, opts.TFWorkspace,
filepath.Join(opts.UpgradeWorkspace, upgradeID, constants.TerraformUpgradeWorkingDir), filepath.Join(opts.UpgradeWorkspace, u.upgradeID, constants.TerraformUpgradeWorkingDir),
filepath.Join(opts.UpgradeWorkspace, upgradeID, constants.TerraformUpgradeBackupDir), filepath.Join(opts.UpgradeWorkspace, u.upgradeID, constants.TerraformUpgradeBackupDir),
opts.Vars, opts.Vars,
) )
if err != nil { if err != nil {
@ -122,24 +92,15 @@ func (u *TerraformUpgrader) PlanTerraformMigrations(ctx context.Context, opts Te
// CleanUpTerraformMigrations cleans up the Terraform migration workspace, for example when an upgrade is // CleanUpTerraformMigrations cleans up the Terraform migration workspace, for example when an upgrade is
// aborted by the user. // aborted by the user.
func (u *TerraformUpgrader) CleanUpTerraformMigrations(upgradeWorkspace, upgradeID string) error { func (u *TerraformUpgrader) CleanUpTerraformMigrations(upgradeWorkspace string) error {
return CleanUpTerraformMigrations(upgradeWorkspace, upgradeID, u.fileHandler) return CleanUpTerraformMigrations(upgradeWorkspace, u.upgradeID, u.fileHandler)
}
// CleanUpTerraformMigrations cleans up the Terraform upgrade directory.
func CleanUpTerraformMigrations(upgradeWorkspace, upgradeID string, fileHandler file.Handler) error {
upgradeDir := filepath.Join(upgradeWorkspace, upgradeID)
if err := fileHandler.RemoveAll(upgradeDir); err != nil {
return fmt.Errorf("cleaning up file %s: %w", upgradeDir, err)
}
return nil
} }
// ApplyTerraformMigrations applies the migrations planned by PlanTerraformMigrations. // ApplyTerraformMigrations applies the migrations planned by PlanTerraformMigrations.
// If PlanTerraformMigrations has not been executed before, it will return an error. // If PlanTerraformMigrations has not been executed before, it will return an error.
// In case of a successful upgrade, the output will be written to the specified file and the old Terraform directory is replaced // In case of a successful upgrade, the output will be written to the specified file and the old Terraform directory is replaced
// By the new one. // By the new one.
func (u *TerraformUpgrader) ApplyTerraformMigrations(ctx context.Context, opts TerraformUpgradeOptions, upgradeID string) (terraform.ApplyOutput, error) { func (u *TerraformUpgrader) ApplyTerraformMigrations(ctx context.Context, opts TerraformUpgradeOptions) (terraform.ApplyOutput, error) {
tfOutput, err := u.tf.CreateCluster(ctx, opts.CSP, opts.LogLevel) tfOutput, err := u.tf.CreateCluster(ctx, opts.CSP, opts.LogLevel)
if err != nil { if err != nil {
return tfOutput, fmt.Errorf("terraform apply: %w", err) return tfOutput, fmt.Errorf("terraform apply: %w", err)
@ -154,17 +115,64 @@ func (u *TerraformUpgrader) ApplyTerraformMigrations(ctx context.Context, opts T
} }
if err := u.fileHandler.CopyDir( if err := u.fileHandler.CopyDir(
filepath.Join(opts.UpgradeWorkspace, upgradeID, constants.TerraformUpgradeWorkingDir), filepath.Join(opts.UpgradeWorkspace, u.upgradeID, constants.TerraformUpgradeWorkingDir),
opts.TFWorkspace, opts.TFWorkspace,
); err != nil { ); err != nil {
return tfOutput, fmt.Errorf("replacing old terraform directory with new one: %w", err) return tfOutput, fmt.Errorf("replacing old terraform directory with new one: %w", err)
} }
if err := u.fileHandler.RemoveAll(filepath.Join(opts.UpgradeWorkspace, upgradeID, constants.TerraformUpgradeWorkingDir)); err != nil { if err := u.fileHandler.RemoveAll(filepath.Join(opts.UpgradeWorkspace, u.upgradeID, constants.TerraformUpgradeWorkingDir)); err != nil {
return tfOutput, fmt.Errorf("removing terraform upgrade directory: %w", err) return tfOutput, fmt.Errorf("removing terraform upgrade directory: %w", err)
} }
return tfOutput, nil return tfOutput, nil
} }
// UpgradeID returns the ID of the upgrade.
func (u *TerraformUpgrader) UpgradeID() string {
return u.upgradeID
}
// CleanUpTerraformMigrations cleans up the Terraform upgrade directory.
func CleanUpTerraformMigrations(upgradeWorkspace, upgradeID string, fileHandler file.Handler) error {
upgradeDir := filepath.Join(upgradeWorkspace, upgradeID)
if err := fileHandler.RemoveAll(upgradeDir); err != nil {
return fmt.Errorf("cleaning up file %s: %w", upgradeDir, err)
}
return nil
}
// CheckTerraformMigrations checks whether Terraform migrations are possible in the current workspace.
func checkTerraformMigrations(file file.Handler, upgradeWorkspace, upgradeID, upgradeSubDir string) error {
var existingFiles []string
filesToCheck := []string{
filepath.Join(upgradeWorkspace, upgradeID, upgradeSubDir),
}
for _, f := range filesToCheck {
if err := checkFileExists(file, &existingFiles, f); err != nil {
return fmt.Errorf("checking terraform migrations: %w", err)
}
}
if len(existingFiles) > 0 {
return fmt.Errorf("file(s) %s already exist", strings.Join(existingFiles, ", "))
}
return nil
}
// checkFileExists checks whether a file exists and adds it to the existingFiles slice if it does.
func checkFileExists(fileHandler file.Handler, existingFiles *[]string, filename string) error {
_, err := fileHandler.Stat(filename)
if err != nil {
if !os.IsNotExist(err) {
return fmt.Errorf("checking %s: %w", filename, err)
}
return nil
}
*existingFiles = append(*existingFiles, filename)
return nil
}
type tfClientCommon interface { type tfClientCommon interface {
ShowPlan(ctx context.Context, logLevel terraform.LogLevel, output io.Writer) error ShowPlan(ctx context.Context, logLevel terraform.LogLevel, output io.Writer) error
Plan(ctx context.Context, logLevel terraform.LogLevel) (bool, error) Plan(ctx context.Context, logLevel terraform.LogLevel) (bool, error)

View File

@ -23,13 +23,6 @@ import (
) )
func TestCheckTerraformMigrations(t *testing.T) { func TestCheckTerraformMigrations(t *testing.T) {
upgrader := func(fileHandler file.Handler) *TerraformUpgrader {
u, err := NewTerraformUpgrader(&stubTerraformClient{}, bytes.NewBuffer(nil), fileHandler)
require.NoError(t, err)
return u
}
workspace := func(existingFiles []string) file.Handler { workspace := func(existingFiles []string) file.Handler {
fs := afero.NewMemMapFs() fs := afero.NewMemMapFs()
for _, f := range existingFiles { for _, f := range existingFiles {
@ -57,8 +50,9 @@ func TestCheckTerraformMigrations(t *testing.T) {
for name, tc := range testCases { for name, tc := range testCases {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
u := upgrader(tc.workspace) u := NewTerraformUpgrader(&stubTerraformClient{}, bytes.NewBuffer(nil), tc.workspace, tc.upgradeID)
err := u.CheckTerraformMigrations(constants.UpgradeDir, tc.upgradeID, constants.TerraformUpgradeBackupDir)
err := u.CheckTerraformMigrations(constants.UpgradeDir)
if tc.wantErr { if tc.wantErr {
require.Error(t, err) require.Error(t, err)
return return
@ -70,12 +64,6 @@ func TestCheckTerraformMigrations(t *testing.T) {
} }
func TestPlanTerraformMigrations(t *testing.T) { func TestPlanTerraformMigrations(t *testing.T) {
upgrader := func(tf tfResourceClient, fileHandler file.Handler) *TerraformUpgrader {
u, err := NewTerraformUpgrader(tf, bytes.NewBuffer(nil), fileHandler)
require.NoError(t, err)
return u
}
workspace := func(existingFiles []string) file.Handler { workspace := func(existingFiles []string) file.Handler {
fs := afero.NewMemMapFs() fs := afero.NewMemMapFs()
for _, f := range existingFiles { for _, f := range existingFiles {
@ -142,7 +130,7 @@ func TestPlanTerraformMigrations(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
require := require.New(t) require := require.New(t)
u := upgrader(tc.tf, tc.workspace) u := NewTerraformUpgrader(tc.tf, bytes.NewBuffer(nil), tc.workspace, tc.upgradeID)
opts := TerraformUpgradeOptions{ opts := TerraformUpgradeOptions{
LogLevel: terraform.LogLevelDebug, LogLevel: terraform.LogLevelDebug,
@ -150,7 +138,7 @@ func TestPlanTerraformMigrations(t *testing.T) {
Vars: &terraform.QEMUVariables{}, Vars: &terraform.QEMUVariables{},
} }
diff, err := u.PlanTerraformMigrations(context.Background(), opts, tc.upgradeID) diff, err := u.PlanTerraformMigrations(context.Background(), opts)
if tc.wantErr { if tc.wantErr {
require.Error(err) require.Error(err)
} else { } else {
@ -162,13 +150,6 @@ func TestPlanTerraformMigrations(t *testing.T) {
} }
func TestApplyTerraformMigrations(t *testing.T) { func TestApplyTerraformMigrations(t *testing.T) {
upgrader := func(tf tfResourceClient, fileHandler file.Handler) *TerraformUpgrader {
u, err := NewTerraformUpgrader(tf, bytes.NewBuffer(nil), fileHandler)
require.NoError(t, err)
return u
}
fileHandler := func(upgradeID string, existingFiles ...string) file.Handler { fileHandler := func(upgradeID string, existingFiles ...string) file.Handler {
fh := file.NewHandler(afero.NewMemMapFs()) fh := file.NewHandler(afero.NewMemMapFs())
@ -211,7 +192,7 @@ func TestApplyTerraformMigrations(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
require := require.New(t) require := require.New(t)
u := upgrader(tc.tf, tc.fs) u := NewTerraformUpgrader(tc.tf, bytes.NewBuffer(nil), tc.fs, tc.upgradeID)
opts := TerraformUpgradeOptions{ opts := TerraformUpgradeOptions{
LogLevel: terraform.LogLevelDebug, LogLevel: terraform.LogLevelDebug,
@ -221,7 +202,7 @@ func TestApplyTerraformMigrations(t *testing.T) {
UpgradeWorkspace: constants.UpgradeDir, UpgradeWorkspace: constants.UpgradeDir,
} }
_, err := u.ApplyTerraformMigrations(context.Background(), opts, tc.upgradeID) _, err := u.ApplyTerraformMigrations(context.Background(), opts)
if tc.wantErr { if tc.wantErr {
require.Error(err) require.Error(err)
} else { } else {
@ -232,13 +213,6 @@ func TestApplyTerraformMigrations(t *testing.T) {
} }
func TestCleanUpTerraformMigrations(t *testing.T) { func TestCleanUpTerraformMigrations(t *testing.T) {
upgrader := func(fileHandler file.Handler) *TerraformUpgrader {
u, err := NewTerraformUpgrader(&stubTerraformClient{}, bytes.NewBuffer(nil), fileHandler)
require.NoError(t, err)
return u
}
workspace := func(existingFiles []string) file.Handler { workspace := func(existingFiles []string) file.Handler {
fs := afero.NewMemMapFs() fs := afero.NewMemMapFs()
for _, f := range existingFiles { for _, f := range existingFiles {
@ -299,9 +273,9 @@ func TestCleanUpTerraformMigrations(t *testing.T) {
require := require.New(t) require := require.New(t)
workspace := workspace(tc.workspaceFiles) workspace := workspace(tc.workspaceFiles)
u := upgrader(workspace) u := NewTerraformUpgrader(&stubTerraformClient{}, bytes.NewBuffer(nil), workspace, tc.upgradeID)
err := u.CleanUpTerraformMigrations(constants.UpgradeDir, tc.upgradeID) err := u.CleanUpTerraformMigrations(constants.UpgradeDir)
if tc.wantErr { if tc.wantErr {
require.Error(err) require.Error(err)
return return