mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
update
This commit is contained in:
parent
7fcf7e15c6
commit
ad56360d5f
@ -111,6 +111,12 @@ func (u *upgradeApplyCmd) upgradeApply(cmd *cobra.Command, fileHandler file.Hand
|
||||
return fmt.Errorf("upgrading measurements: %w", err)
|
||||
}
|
||||
|
||||
migrateIAM := getIAMMigrateCmd(cmd, u.upgrader.GetTerraformUpgrader(), conf, flags, u.upgrader.GetUpgradeID())
|
||||
if err := u.executeMigration(cmd, fileHandler, migrateIAM, flags); err != nil {
|
||||
return fmt.Errorf("executing IAM migration: %w", err)
|
||||
}
|
||||
|
||||
//
|
||||
if err := u.migrateTerraform(cmd, fileHandler, u.imageFetcher, conf, flags); err != nil {
|
||||
return fmt.Errorf("performing Terraform migrations: %w", err)
|
||||
}
|
||||
@ -211,6 +217,60 @@ func (u *upgradeApplyCmd) migrateTerraform(cmd *cobra.Command, file file.Handler
|
||||
return nil
|
||||
}
|
||||
|
||||
func getIAMMigrateCmd(cmd *cobra.Command, tfClient *terraform.Client, conf *config.Config, flags upgradeApplyFlags, upgradeID string) *terraform.IAMMigrateCmd {
|
||||
// Check if there are any Terraform migrations to apply
|
||||
outWriter := cmd.OutOrStdout()
|
||||
|
||||
migrateCmd := terraform.NewIAMMigrateCmd(
|
||||
tfClient,
|
||||
upgradeID,
|
||||
conf.GetProvider(),
|
||||
flags.terraformLogLevel,
|
||||
outWriter,
|
||||
)
|
||||
return migrateCmd
|
||||
}
|
||||
|
||||
func (u *upgradeApplyCmd) executeMigration(cmd *cobra.Command, file file.Handler, migrateCmd terraform.MigrationCmd, flags upgradeApplyFlags) error {
|
||||
hasDiff, err := migrateCmd.Plan(cmd.Context()) // u.upgrader.PlanTerraformMigrations(cmd.Context(), opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("planning terraform migrations: %w", err)
|
||||
}
|
||||
if hasDiff {
|
||||
// If there are any Terraform migrations to apply, ask for confirmation
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "The %s upgrade requires a migration of Constellation cloud resources by applying an updated Terraform template. Please manually review the suggested changes below.\n", migrateCmd.String())
|
||||
if !flags.yes {
|
||||
ok, err := askToConfirm(cmd, fmt.Sprintf("Do you want to apply the %s?", migrateCmd.String()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("asking for confirmation: %w", err)
|
||||
}
|
||||
if !ok {
|
||||
cmd.Println("Aborting upgrade.")
|
||||
if err := u.upgrader.CleanUpTerraformMigrations(file); err != nil {
|
||||
return fmt.Errorf("cleaning up workspace: %w", err)
|
||||
}
|
||||
return fmt.Errorf("aborted by user")
|
||||
}
|
||||
}
|
||||
u.log.Debugf("Applying Terraform %s migrations", migrateCmd.String())
|
||||
// .ApplyMigration()
|
||||
err := migrateCmd.Apply(cmd.Context(), file) // u.upgrader.ApplyTerraformMigrations(cmd.Context(), file, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("applying terraform migrations: %w", err)
|
||||
}
|
||||
|
||||
// TODO write this outside of apply migration
|
||||
upgradeOutputFile := constants.TerraformMigrationOutputFile
|
||||
cmd.Printf("Terraform migrations applied successfully and output written to: %s\n"+
|
||||
"A backup of the pre-upgrade Terraform state has been written to: %s\n",
|
||||
upgradeOutputFile, filepath.Join(constants.UpgradeDir, constants.TerraformUpgradeBackupDir)) // TODO include log of file write where the action happens?
|
||||
} else {
|
||||
u.log.Debugf("No Terraform diff detected")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseTerraformUpgradeVars parses the variables required to execute the Terraform script with.
|
||||
func parseTerraformUpgradeVars(cmd *cobra.Command, conf *config.Config, fetcher imageFetcher) (terraform.Variables, error) {
|
||||
// Fetch variables to execute Terraform script with
|
||||
@ -446,6 +506,8 @@ type cloudUpgrader interface {
|
||||
CheckTerraformMigrations(fileHandler file.Handler) error
|
||||
CleanUpTerraformMigrations(fileHandler file.Handler) error
|
||||
AddManualStateMigration(migration terraform.StateMigration)
|
||||
GetTerraformUpgrader() *terraform.Client
|
||||
GetUpgradeID() string
|
||||
}
|
||||
|
||||
func toPtr[T any](v T) *T {
|
||||
|
@ -153,6 +153,14 @@ func NewUpgrader(ctx context.Context, outWriter io.Writer, log debugLog, upgrade
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func (u *Upgrader) GetTerraformUpgrader() *terraform.Client {
|
||||
return u.tfClient
|
||||
}
|
||||
|
||||
func (u *Upgrader) GetUpgradeID() string {
|
||||
return u.upgradeID
|
||||
}
|
||||
|
||||
// AddManualStateMigration adds a manual state migration to the Terraform client.
|
||||
// TODO(AB#3248): Remove this method after we can assume that all existing clusters have been migrated.
|
||||
func (u *Upgrader) AddManualStateMigration(migration terraform.StateMigration) {
|
||||
@ -175,10 +183,15 @@ func (u *Upgrader) CleanUpTerraformMigrations(fileHandler file.Handler) error {
|
||||
// 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)
|
||||
hasDiff, err := u.tfUpgrader.PlanIAMMigration(ctx, opts.CSP, opts.LogLevel, u.upgradeID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("planning terraform migrations: %w", err)
|
||||
}
|
||||
return hasDiff, nil
|
||||
// return u.tfUpgrader.PlanTerraformMigrations(ctx, opts, u.upgradeID)
|
||||
}
|
||||
|
||||
// ApplyTerraformMigrations applies the migerations planned by PlanTerraformMigrations.
|
||||
// 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.
|
||||
|
@ -4,6 +4,7 @@ load("//bazel/go:go_test.bzl", "go_test")
|
||||
go_library(
|
||||
name = "terraform",
|
||||
srcs = [
|
||||
"iammigrate.go",
|
||||
"loader.go",
|
||||
"logging.go",
|
||||
"terraform.go",
|
||||
|
98
cli/internal/terraform/iammigrate.go
Normal file
98
cli/internal/terraform/iammigrate.go
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
)
|
||||
|
||||
type tfClient interface {
|
||||
PrepareIAMUpgradeWorkspace(rootDir, workingDir, newWorkingDir, backupDir string) error
|
||||
Plan(ctx context.Context, logLevel LogLevel, planFile string) (bool, error)
|
||||
ShowPlan(ctx context.Context, logLevel LogLevel, planFile string, outWriter io.Writer) error
|
||||
CreateIAMConfig(ctx context.Context, csp cloudprovider.Provider, logLevel LogLevel) (IAMOutput, error)
|
||||
}
|
||||
|
||||
type MigrationCmd interface {
|
||||
Plan(ctx context.Context) (bool, error)
|
||||
Apply(ctx context.Context, fileHandler file.Handler) error
|
||||
String() string
|
||||
}
|
||||
|
||||
type IAMMigrateCmd struct {
|
||||
tf tfClient
|
||||
upgradeID string
|
||||
csp cloudprovider.Provider
|
||||
logLevel LogLevel
|
||||
outWriter io.Writer
|
||||
}
|
||||
|
||||
func NewIAMMigrateCmd(tf tfClient, upgradeID string, csp cloudprovider.Provider, logLevel LogLevel, outWriter io.Writer) *IAMMigrateCmd {
|
||||
return &IAMMigrateCmd{
|
||||
tf: tf,
|
||||
upgradeID: upgradeID,
|
||||
csp: csp,
|
||||
logLevel: logLevel,
|
||||
outWriter: outWriter,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *IAMMigrateCmd) String() string {
|
||||
return "iam migration"
|
||||
}
|
||||
|
||||
func (c *IAMMigrateCmd) Plan(ctx context.Context) (bool, error) {
|
||||
err := c.tf.PrepareIAMUpgradeWorkspace(
|
||||
filepath.Join("terraform", "iam", strings.ToLower(c.csp.String())),
|
||||
constants.TerraformIAMWorkingDir,
|
||||
filepath.Join(constants.UpgradeDir, c.upgradeID, constants.TerraformIAMUpgradeWorkingDir),
|
||||
filepath.Join(constants.UpgradeDir, c.upgradeID, constants.TerraformUpgradeBackupDir), // TODO: use IAM backup dir
|
||||
)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("preparing terraform workspace: %w", err)
|
||||
}
|
||||
|
||||
hasDiff, err := c.tf.Plan(ctx, c.logLevel, constants.TerraformUpgradePlanFile)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("terraform plan: %w", err)
|
||||
}
|
||||
|
||||
if hasDiff {
|
||||
if err := c.tf.ShowPlan(ctx, c.logLevel, constants.TerraformUpgradePlanFile, c.outWriter); err != nil {
|
||||
return false, fmt.Errorf("terraform show plan: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return hasDiff, nil
|
||||
}
|
||||
|
||||
func (c *IAMMigrateCmd) Apply(ctx context.Context, fileHandler file.Handler) error {
|
||||
_, err := c.tf.CreateIAMConfig(ctx, c.csp, c.logLevel)
|
||||
|
||||
// TODO: put in template?
|
||||
|
||||
if err := fileHandler.RemoveAll(constants.TerraformIAMWorkingDir); err != nil {
|
||||
return fmt.Errorf("removing old terraform directory: %w", err)
|
||||
}
|
||||
if err := fileHandler.CopyDir(filepath.Join(constants.UpgradeDir, c.upgradeID, constants.TerraformIAMUpgradeWorkingDir), constants.TerraformIAMWorkingDir); err != nil {
|
||||
return fmt.Errorf("replacing old terraform directory with new one: %w", err)
|
||||
}
|
||||
|
||||
if err := fileHandler.RemoveAll(filepath.Join(constants.UpgradeDir, c.upgradeID, constants.TerraformIAMUpgradeWorkingDir)); err != nil {
|
||||
return fmt.Errorf("removing terraform upgrade directory: %w", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
@ -102,6 +102,18 @@ func (c *Client) PrepareUpgradeWorkspace(path, oldWorkingDir, newWorkingDir, bac
|
||||
return c.writeVars(vars)
|
||||
}
|
||||
|
||||
// PrepareIAMUpgradeWorkspace prepares a Terraform workspace for a Constellation IAM upgrade.
|
||||
func (c *Client) PrepareIAMUpgradeWorkspace(path, oldWorkingDir, newWorkingDir, backupDir string) error {
|
||||
if err := prepareUpgradeWorkspace(path, c.file, oldWorkingDir, newWorkingDir, backupDir); err != nil {
|
||||
return fmt.Errorf("prepare upgrade workspace: %w", err)
|
||||
}
|
||||
// copy the vars file from the old working dir to the new working dir
|
||||
if err := c.file.CopyFile(filepath.Join(oldWorkingDir, terraformVarsFile), filepath.Join(newWorkingDir, terraformVarsFile)); err != nil {
|
||||
return fmt.Errorf("copying vars file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateCluster creates a Constellation cluster using Terraform.
|
||||
func (c *Client) CreateCluster(ctx context.Context, logLevel LogLevel) (CreateOutput, error) {
|
||||
if err := c.setLogLevel(logLevel); err != nil {
|
||||
|
20
cli/internal/upgrade/main/BUILD.bazel
Normal file
20
cli/internal/upgrade/main/BUILD.bazel
Normal file
@ -0,0 +1,20 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "main_lib",
|
||||
srcs = ["main.go"],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/cli/internal/upgrade/main",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//cli/internal/terraform",
|
||||
"//cli/internal/upgrade",
|
||||
"//internal/cloud/cloudprovider",
|
||||
"//internal/constants",
|
||||
],
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "main",
|
||||
embed = [":main_lib"],
|
||||
visibility = ["//cli:__subpackages__"],
|
||||
)
|
BIN
cli/internal/upgrade/main/main
Executable file
BIN
cli/internal/upgrade/main/main
Executable file
Binary file not shown.
35
cli/internal/upgrade/main/main.go
Normal file
35
cli/internal/upgrade/main/main.go
Normal file
@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/upgrade"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
tfClient, err := terraform.New(ctx, filepath.Join(constants.UpgradeDir, "test", constants.TerraformUpgradeWorkingDir))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("setting up terraform client: %w", err))
|
||||
}
|
||||
// give me a writer
|
||||
outWriter := bytes.NewBuffer(nil)
|
||||
tfUpgrader, err := upgrade.NewTerraformUpgrader(tfClient, outWriter)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("setting up terraform upgrader: %w", err))
|
||||
}
|
||||
diff, err := tfUpgrader.PlanIAMMigration(ctx, upgrade.TerraformUpgradeOptions{
|
||||
CSP: cloudprovider.AWS,
|
||||
LogLevel: terraform.LogLevelDebug,
|
||||
}, "test")
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("planning terraform migrations: %w", err))
|
||||
}
|
||||
fmt.Println(diff)
|
||||
}
|
@ -85,6 +85,31 @@ func checkFileExists(fileHandler file.Handler, existingFiles *[]string, filename
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *TerraformUpgrader) PlanIAMMigration(ctx context.Context, csp cloudprovider.Provider, logLevel terraform.LogLevel, upgradeID string) (bool, error) {
|
||||
err := u.tf.PrepareIAMUpgradeWorkspace(
|
||||
filepath.Join("terraform", "iam", strings.ToLower(csp.String())),
|
||||
constants.TerraformIAMWorkingDir,
|
||||
filepath.Join(constants.UpgradeDir, upgradeID, constants.TerraformUpgradeWorkingDir),
|
||||
filepath.Join(constants.UpgradeDir, upgradeID, constants.TerraformUpgradeBackupDir),
|
||||
)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("preparing terraform workspace: %w", err)
|
||||
}
|
||||
|
||||
hasDiff, err := u.tf.Plan(ctx, logLevel, constants.TerraformUpgradePlanFile)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("terraform plan: %w", err)
|
||||
}
|
||||
|
||||
if hasDiff {
|
||||
if err := u.tf.ShowPlan(ctx, logLevel, constants.TerraformUpgradePlanFile, u.outWriter); err != nil {
|
||||
return false, fmt.Errorf("terraform show plan: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return hasDiff, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
@ -130,7 +155,7 @@ func (u *TerraformUpgrader) CleanUpTerraformMigrations(fileHandler file.Handler,
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyTerraformMigrations applies the migerations planned by PlanTerraformMigrations.
|
||||
// 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.
|
||||
@ -176,6 +201,7 @@ func (u *TerraformUpgrader) ApplyTerraformMigrations(ctx context.Context, fileHa
|
||||
|
||||
// a tfClient performs the Terraform interactions in an upgrade.
|
||||
type tfClient interface {
|
||||
PrepareIAMUpgradeWorkspace(path, oldWorkingDir, newWorkingDir, backupDir string) error
|
||||
PrepareUpgradeWorkspace(path, oldWorkingDir, newWorkingDir, upgradeID string, vars terraform.Variables) error
|
||||
ShowPlan(ctx context.Context, logLevel terraform.LogLevel, planFilePath string, output io.Writer) error
|
||||
Plan(ctx context.Context, logLevel terraform.LogLevel, planFile string) (bool, error)
|
||||
|
@ -154,6 +154,8 @@ const (
|
||||
TerraformUpgradePlanFile = "plan.zip"
|
||||
// TerraformUpgradeWorkingDir is the directory name for the Terraform workspace being used in an upgrade.
|
||||
TerraformUpgradeWorkingDir = "terraform"
|
||||
// TerraformIAMUpgradeWorkingDir is the directory name for the Terraform IAM workspace being used in an upgrade.
|
||||
TerraformIAMUpgradeWorkingDir = "terraform-iam"
|
||||
// TerraformUpgradeBackupDir is the directory name being used to backup the pre-upgrade state in an upgrade.
|
||||
TerraformUpgradeBackupDir = "terraform-backup"
|
||||
// TerraformMigrationOutputFile is the file name of the output file created by a successful Terraform migration.
|
||||
|
Loading…
Reference in New Issue
Block a user