mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-05-08 01:05:16 -04:00
cli: store upgrade files in versioned folders (#1929)
* upgrade versioning * dont pass upgrade kind as boolean * whitespace * fix godot lint check * clarify upgrade check directory suffix * cli: dry-run Terraform migrations on `upgrade check` (#1942) * dry-run Terraform migrations on upgrade check * clean whole upgrade dir * clean up check workspace after planning * fix parsing * extend upgrade check test * rename unused parameters * exclude false positives in test
This commit is contained in:
parent
f3c2198a9a
commit
b25228d175
13 changed files with 300 additions and 127 deletions
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/edgelesssys/constellation/v2/internal/versions"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versions/components"
|
||||
updatev1alpha1 "github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api/v1alpha1"
|
||||
"github.com/google/uuid"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -44,6 +45,16 @@ import (
|
|||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
// 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.
|
||||
var ErrInProgress = errors.New("upgrade in progress")
|
||||
|
||||
|
@ -85,10 +96,26 @@ type Upgrader struct {
|
|||
outWriter io.Writer
|
||||
tfUpgrader *upgrade.TerraformUpgrader
|
||||
log debugLog
|
||||
upgradeID string
|
||||
}
|
||||
|
||||
// NewUpgrader returns a new Upgrader.
|
||||
func NewUpgrader(ctx context.Context, outWriter io.Writer, log debugLog) (*Upgrader, error) {
|
||||
func NewUpgrader(ctx context.Context, outWriter io.Writer, 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,
|
||||
}
|
||||
|
||||
kubeConfig, err := clientcmd.BuildConfigFromFlags("", constants.AdminConfFilename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building kubernetes config: %w", err)
|
||||
|
@ -98,19 +125,22 @@ func NewUpgrader(ctx context.Context, outWriter io.Writer, log debugLog) (*Upgra
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("setting up kubernetes client: %w", err)
|
||||
}
|
||||
u.stableInterface = &stableClient{client: kubeClient}
|
||||
|
||||
// use unstructured client to avoid importing the operator packages
|
||||
unstructuredClient, err := dynamic.NewForConfig(kubeConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setting up custom resource client: %w", err)
|
||||
}
|
||||
u.dynamicInterface = &NodeVersionClient{client: unstructuredClient}
|
||||
|
||||
helmClient, err := helm.NewClient(kubectl.New(), constants.AdminConfFilename, constants.HelmNamespace, log)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setting up helm client: %w", err)
|
||||
}
|
||||
u.helmClient = helmClient
|
||||
|
||||
tfClient, err := terraform.New(ctx, filepath.Join(constants.UpgradeDir, constants.TerraformUpgradeWorkingDir))
|
||||
tfClient, err := terraform.New(ctx, filepath.Join(constants.UpgradeDir, upgradeID, constants.TerraformUpgradeWorkingDir))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setting up terraform client: %w", err)
|
||||
}
|
||||
|
@ -119,35 +149,28 @@ func NewUpgrader(ctx context.Context, outWriter io.Writer, log debugLog) (*Upgra
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("setting up terraform upgrader: %w", err)
|
||||
}
|
||||
u.tfUpgrader = tfUpgrader
|
||||
|
||||
return &Upgrader{
|
||||
stableInterface: &stableClient{client: kubeClient},
|
||||
dynamicInterface: &NodeVersionClient{client: unstructuredClient},
|
||||
helmClient: helmClient,
|
||||
imageFetcher: imagefetcher.New(),
|
||||
outWriter: outWriter,
|
||||
tfUpgrader: tfUpgrader,
|
||||
log: log,
|
||||
}, nil
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// 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(fileHandler file.Handler) error {
|
||||
return u.tfUpgrader.CheckTerraformMigrations(fileHandler)
|
||||
return u.tfUpgrader.CheckTerraformMigrations(fileHandler, u.upgradeID)
|
||||
}
|
||||
|
||||
// CleanUpTerraformMigrations cleans up the Terraform migration workspace, for example when an upgrade is
|
||||
// aborted by the user.
|
||||
func (u *Upgrader) CleanUpTerraformMigrations(fileHandler file.Handler) error {
|
||||
return u.tfUpgrader.CleanUpTerraformMigrations(fileHandler)
|
||||
return u.tfUpgrader.CleanUpTerraformMigrations(fileHandler, 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)
|
||||
return u.tfUpgrader.PlanTerraformMigrations(ctx, opts, u.upgradeID)
|
||||
}
|
||||
|
||||
// ApplyTerraformMigrations applies the migerations planned by PlanTerraformMigrations.
|
||||
|
@ -155,12 +178,12 @@ func (u *Upgrader) PlanTerraformMigrations(ctx context.Context, opts upgrade.Ter
|
|||
// 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, fileHandler file.Handler, opts upgrade.TerraformUpgradeOptions) error {
|
||||
return u.tfUpgrader.ApplyTerraformMigrations(ctx, fileHandler, opts)
|
||||
return u.tfUpgrader.ApplyTerraformMigrations(ctx, fileHandler, opts, u.upgradeID)
|
||||
}
|
||||
|
||||
// UpgradeHelmServices upgrade helm services.
|
||||
func (u *Upgrader) UpgradeHelmServices(ctx context.Context, config *config.Config, timeout time.Duration, allowDestructive bool) error {
|
||||
return u.helmClient.Upgrade(ctx, config, timeout, allowDestructive)
|
||||
return u.helmClient.Upgrade(ctx, config, timeout, allowDestructive, u.upgradeID)
|
||||
}
|
||||
|
||||
// UpgradeNodeVersion upgrades the cluster's NodeVersion object and in turn triggers image & k8s version upgrades.
|
||||
|
@ -526,7 +549,7 @@ func joinConfigMigration(existingConf *corev1.ConfigMap, attestVariant variant.V
|
|||
}
|
||||
|
||||
type helmInterface interface {
|
||||
Upgrade(ctx context.Context, config *config.Config, timeout time.Duration, allowDestructive bool) error
|
||||
Upgrade(ctx context.Context, config *config.Config, timeout time.Duration, allowDestructive bool, upgradeID string) error
|
||||
}
|
||||
|
||||
type debugLog interface {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue