-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cli: remove/refactor upgrade package (#2266)
* Move IAM migration client to cloudcmd package * Move Terraform Cluster upgrade client to cloudcmd package * Use hcl for creating Terraform IAM variables files * Unify terraform upgrade code * Rename some cloudcmd files for better clarity --------- Signed-off-by: Daniel Weiße <dw@edgeless.systems>
- Loading branch information
1 parent
3d5d291
commit 0a91180
Showing
29 changed files
with
1,197 additions
and
1,194 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* | ||
Copyright (c) Edgeless Systems GmbH | ||
SPDX-License-Identifier: AGPL-3.0-only | ||
*/ | ||
|
||
package cloudcmd | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" | ||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" | ||
"github.com/edgelesssys/constellation/v2/internal/constants" | ||
"github.com/edgelesssys/constellation/v2/internal/file" | ||
) | ||
|
||
// ClusterUpgrader is responsible for performing Terraform migrations on cluster upgrades. | ||
type ClusterUpgrader struct { | ||
tf tfClusterUpgradeClient | ||
policyPatcher policyPatcher | ||
fileHandler file.Handler | ||
existingWorkspace string | ||
upgradeWorkspace string | ||
logLevel terraform.LogLevel | ||
} | ||
|
||
// NewClusterUpgrader initializes and returns a new ClusterUpgrader. | ||
// existingWorkspace is the directory holding the existing Terraform resources. | ||
// upgradeWorkspace is the directory to use for holding temporary files and resources required to apply the upgrade. | ||
func NewClusterUpgrader(ctx context.Context, existingWorkspace, upgradeWorkspace string, | ||
logLevel terraform.LogLevel, fileHandler file.Handler, | ||
) (*ClusterUpgrader, error) { | ||
tfClient, err := terraform.New(ctx, filepath.Join(upgradeWorkspace, constants.TerraformUpgradeWorkingDir)) | ||
if err != nil { | ||
return nil, fmt.Errorf("setting up terraform client: %w", err) | ||
} | ||
|
||
return &ClusterUpgrader{ | ||
tf: tfClient, | ||
policyPatcher: NewAzurePolicyPatcher(), | ||
fileHandler: fileHandler, | ||
existingWorkspace: existingWorkspace, | ||
upgradeWorkspace: upgradeWorkspace, | ||
logLevel: logLevel, | ||
}, nil | ||
} | ||
|
||
// PlanClusterUpgrade prepares the upgrade workspace and plans the possible Terraform migrations for Constellation's cluster resources (Loadbalancers, VMs, networks etc.). | ||
// In case of possible migrations, the diff is written to outWriter and this function returns true. | ||
func (u *ClusterUpgrader) PlanClusterUpgrade(ctx context.Context, outWriter io.Writer, vars terraform.Variables, csp cloudprovider.Provider, | ||
) (bool, error) { | ||
return planUpgrade( | ||
ctx, u.tf, u.fileHandler, outWriter, u.logLevel, vars, | ||
filepath.Join("terraform", strings.ToLower(csp.String())), | ||
u.existingWorkspace, | ||
filepath.Join(u.upgradeWorkspace, constants.TerraformUpgradeBackupDir), | ||
) | ||
} | ||
|
||
// ApplyClusterUpgrade applies the Terraform migrations planned by PlanClusterUpgrade. | ||
// On success, the workspace of the Upgrader replaces the existing Terraform workspace. | ||
func (u *ClusterUpgrader) ApplyClusterUpgrade(ctx context.Context, csp cloudprovider.Provider) (terraform.ApplyOutput, error) { | ||
tfOutput, err := u.tf.ApplyCluster(ctx, csp, u.logLevel) | ||
if err != nil { | ||
return tfOutput, fmt.Errorf("terraform apply: %w", err) | ||
} | ||
if tfOutput.Azure != nil { | ||
if err := u.policyPatcher.Patch(ctx, tfOutput.Azure.AttestationURL); err != nil { | ||
return tfOutput, fmt.Errorf("patching policies: %w", err) | ||
} | ||
} | ||
|
||
if err := moveUpgradeToCurrent( | ||
u.fileHandler, | ||
u.existingWorkspace, | ||
filepath.Join(u.upgradeWorkspace, constants.TerraformUpgradeWorkingDir), | ||
); err != nil { | ||
return tfOutput, fmt.Errorf("promoting upgrade workspace to current workspace: %w", err) | ||
} | ||
|
||
return tfOutput, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
/* | ||
Copyright (c) Edgeless Systems GmbH | ||
SPDX-License-Identifier: AGPL-3.0-only | ||
*/ | ||
|
||
package cloudcmd | ||
|
||
import ( | ||
"context" | ||
"io" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" | ||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" | ||
"github.com/edgelesssys/constellation/v2/internal/constants" | ||
"github.com/edgelesssys/constellation/v2/internal/file" | ||
"github.com/spf13/afero" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestPlanClusterUpgrade(t *testing.T) { | ||
setUpFilesystem := func(existingFiles []string) file.Handler { | ||
fs := afero.NewMemMapFs() | ||
for _, f := range existingFiles { | ||
require.NoError(t, afero.WriteFile(fs, f, []byte{}, 0o644)) | ||
} | ||
|
||
return file.NewHandler(fs) | ||
} | ||
|
||
testCases := map[string]struct { | ||
upgradeID string | ||
tf *tfClusterUpgradeStub | ||
fs file.Handler | ||
want bool | ||
wantErr bool | ||
}{ | ||
"success no diff": { | ||
upgradeID: "1234", | ||
tf: &tfClusterUpgradeStub{}, | ||
fs: setUpFilesystem([]string{}), | ||
}, | ||
"success diff": { | ||
upgradeID: "1234", | ||
tf: &tfClusterUpgradeStub{ | ||
planDiff: true, | ||
}, | ||
fs: setUpFilesystem([]string{}), | ||
want: true, | ||
}, | ||
"prepare workspace error": { | ||
upgradeID: "1234", | ||
tf: &tfClusterUpgradeStub{ | ||
prepareWorkspaceErr: assert.AnError, | ||
}, | ||
fs: setUpFilesystem([]string{}), | ||
wantErr: true, | ||
}, | ||
"plan error": { | ||
tf: &tfClusterUpgradeStub{ | ||
planErr: assert.AnError, | ||
}, | ||
fs: setUpFilesystem([]string{}), | ||
wantErr: true, | ||
}, | ||
"show plan error no diff": { | ||
upgradeID: "1234", | ||
tf: &tfClusterUpgradeStub{ | ||
showErr: assert.AnError, | ||
}, | ||
fs: setUpFilesystem([]string{}), | ||
}, | ||
"show plan error diff": { | ||
upgradeID: "1234", | ||
tf: &tfClusterUpgradeStub{ | ||
showErr: assert.AnError, | ||
planDiff: true, | ||
}, | ||
fs: setUpFilesystem([]string{}), | ||
wantErr: true, | ||
}, | ||
"workspace not clean": { | ||
upgradeID: "1234", | ||
tf: &tfClusterUpgradeStub{}, | ||
fs: setUpFilesystem([]string{filepath.Join(constants.UpgradeDir, "1234", constants.TerraformUpgradeBackupDir)}), | ||
wantErr: true, | ||
}, | ||
} | ||
|
||
for name, tc := range testCases { | ||
t.Run(name, func(t *testing.T) { | ||
require := require.New(t) | ||
|
||
u := &ClusterUpgrader{ | ||
tf: tc.tf, | ||
policyPatcher: stubPolicyPatcher{}, | ||
fileHandler: tc.fs, | ||
upgradeWorkspace: filepath.Join(constants.UpgradeDir, tc.upgradeID), | ||
existingWorkspace: "test", | ||
logLevel: terraform.LogLevelDebug, | ||
} | ||
|
||
diff, err := u.PlanClusterUpgrade(context.Background(), io.Discard, &terraform.QEMUVariables{}, cloudprovider.Unknown) | ||
if tc.wantErr { | ||
require.Error(err) | ||
} else { | ||
require.NoError(err) | ||
require.Equal(tc.want, diff) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestApplyClusterUpgrade(t *testing.T) { | ||
setUpFilesystem := func(upgradeID string, existingFiles ...string) file.Handler { | ||
fh := file.NewHandler(afero.NewMemMapFs()) | ||
|
||
require.NoError(t, | ||
fh.Write( | ||
filepath.Join(constants.UpgradeDir, upgradeID, constants.TerraformUpgradeWorkingDir, "someFile"), | ||
[]byte("some content"), | ||
)) | ||
for _, f := range existingFiles { | ||
require.NoError(t, fh.Write(f, []byte("some content"))) | ||
} | ||
return fh | ||
} | ||
|
||
testCases := map[string]struct { | ||
upgradeID string | ||
tf *tfClusterUpgradeStub | ||
policyPatcher stubPolicyPatcher | ||
fs file.Handler | ||
wantErr bool | ||
}{ | ||
"success": { | ||
upgradeID: "1234", | ||
tf: &tfClusterUpgradeStub{}, | ||
fs: setUpFilesystem("1234"), | ||
policyPatcher: stubPolicyPatcher{}, | ||
}, | ||
"apply error": { | ||
upgradeID: "1234", | ||
tf: &tfClusterUpgradeStub{ | ||
applyErr: assert.AnError, | ||
}, | ||
fs: setUpFilesystem("1234"), | ||
policyPatcher: stubPolicyPatcher{}, | ||
wantErr: true, | ||
}, | ||
} | ||
|
||
for name, tc := range testCases { | ||
t.Run(name, func(t *testing.T) { | ||
assert := require.New(t) | ||
|
||
tc.tf.file = tc.fs | ||
u := &ClusterUpgrader{ | ||
tf: tc.tf, | ||
policyPatcher: stubPolicyPatcher{}, | ||
fileHandler: tc.fs, | ||
upgradeWorkspace: filepath.Join(constants.UpgradeDir, tc.upgradeID), | ||
existingWorkspace: "test", | ||
logLevel: terraform.LogLevelDebug, | ||
} | ||
|
||
_, err := u.ApplyClusterUpgrade(context.Background(), cloudprovider.Unknown) | ||
if tc.wantErr { | ||
assert.Error(err) | ||
} else { | ||
assert.NoError(err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
type tfClusterUpgradeStub struct { | ||
file file.Handler | ||
applyErr error | ||
planErr error | ||
planDiff bool | ||
showErr error | ||
prepareWorkspaceErr error | ||
} | ||
|
||
func (t *tfClusterUpgradeStub) Plan(_ context.Context, _ terraform.LogLevel) (bool, error) { | ||
return t.planDiff, t.planErr | ||
} | ||
|
||
func (t *tfClusterUpgradeStub) ShowPlan(_ context.Context, _ terraform.LogLevel, _ io.Writer) error { | ||
return t.showErr | ||
} | ||
|
||
func (t *tfClusterUpgradeStub) ApplyCluster(_ context.Context, _ cloudprovider.Provider, _ terraform.LogLevel) (terraform.ApplyOutput, error) { | ||
return terraform.ApplyOutput{}, t.applyErr | ||
} | ||
|
||
func (t *tfClusterUpgradeStub) PrepareUpgradeWorkspace(_, _, _ string, _ terraform.Variables) error { | ||
return t.prepareWorkspaceErr | ||
} |
Oops, something went wrong.