cli: fix iam rollback (#1148)

* AB#2897 rename DestroyCluster

* #AB2897 error if terraform dir exists

* AB#2897 reword DestroyResources
This commit is contained in:
Moritz Sanft 2023-02-13 08:42:54 +01:00 committed by GitHub
parent 94245416ca
commit 7410cf8038
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 92 additions and 32 deletions

View File

@ -19,7 +19,7 @@ type terraformClient interface {
PrepareWorkspace(path string, input terraform.Variables) error
CreateCluster(ctx context.Context) (terraform.CreateOutput, error)
CreateIAMConfig(ctx context.Context, provider cloudprovider.Provider) (terraform.IAMOutput, error)
DestroyCluster(ctx context.Context) error
Destroy(ctx context.Context) error
CleanUpWorkspace() error
RemoveInstaller()
}

View File

@ -32,9 +32,9 @@ type stubTerraformClient struct {
uid string
cleanUpWorkspaceCalled bool
removeInstallerCalled bool
destroyClusterCalled bool
destroyCalled bool
createClusterErr error
destroyClusterErr error
destroyErr error
prepareWorkspaceErr error
cleanUpWorkspaceErr error
iamOutputErr error
@ -56,9 +56,9 @@ func (c *stubTerraformClient) PrepareWorkspace(path string, input terraform.Vari
return c.prepareWorkspaceErr
}
func (c *stubTerraformClient) DestroyCluster(ctx context.Context) error {
c.destroyClusterCalled = true
return c.destroyClusterErr
func (c *stubTerraformClient) Destroy(ctx context.Context) error {
c.destroyCalled = true
return c.destroyErr
}
func (c *stubTerraformClient) CleanUpWorkspace() error {

View File

@ -121,7 +121,7 @@ func TestCreator(t *testing.T) {
if tc.wantRollback {
cl := tc.tfClient.(*stubTerraformClient)
if tc.wantTerraformRollback {
assert.True(cl.destroyClusterCalled)
assert.True(cl.destroyCalled)
}
assert.True(cl.cleanUpWorkspaceCalled)
if tc.provider == cloudprovider.QEMU {

View File

@ -40,7 +40,7 @@ type rollbackerTerraform struct {
func (r *rollbackerTerraform) rollback(ctx context.Context) error {
var err error
err = multierr.Append(err, r.client.DestroyCluster(ctx))
err = multierr.Append(err, r.client.Destroy(ctx))
if err == nil {
err = multierr.Append(err, r.client.CleanUpWorkspace())
}
@ -56,7 +56,7 @@ type rollbackerQEMU struct {
func (r *rollbackerQEMU) rollback(ctx context.Context) error {
var err error
if r.createdWorkspace {
err = multierr.Append(err, r.client.DestroyCluster(ctx))
err = multierr.Append(err, r.client.Destroy(ctx))
}
err = multierr.Append(err, r.libvirt.Stop(ctx))
if err == nil {

View File

@ -25,7 +25,7 @@ func TestRollbackTerraform(t *testing.T) {
tfClient: &stubTerraformClient{},
},
"destroy cluster error": {
tfClient: &stubTerraformClient{destroyClusterErr: someErr},
tfClient: &stubTerraformClient{destroyErr: someErr},
wantErr: true,
},
"clean up workspace error": {
@ -51,7 +51,7 @@ func TestRollbackTerraform(t *testing.T) {
return
}
assert.NoError(err)
assert.True(tc.tfClient.destroyClusterCalled)
assert.True(tc.tfClient.destroyCalled)
assert.True(tc.tfClient.cleanUpWorkspaceCalled)
})
}
@ -78,7 +78,7 @@ func TestRollbackQEMU(t *testing.T) {
},
"destroy cluster error": {
libvirt: &stubLibvirtRunner{stopErr: someErr},
tfClient: &stubTerraformClient{destroyClusterErr: someErr},
tfClient: &stubTerraformClient{destroyErr: someErr},
wantErr: true,
},
"clean up workspace error": {
@ -109,9 +109,9 @@ func TestRollbackQEMU(t *testing.T) {
assert.NoError(err)
assert.True(tc.libvirt.stopCalled)
if tc.createdWorkspace {
assert.True(tc.tfClient.destroyClusterCalled)
assert.True(tc.tfClient.destroyCalled)
} else {
assert.False(tc.tfClient.destroyClusterCalled)
assert.False(tc.tfClient.destroyCalled)
}
assert.True(tc.tfClient.cleanUpWorkspaceCalled)
})

View File

@ -50,7 +50,7 @@ func (t *Terminator) Terminate(ctx context.Context) (retErr error) {
}
func (t *Terminator) terminateTerraform(ctx context.Context, cl terraformClient) error {
if err := cl.DestroyCluster(ctx); err != nil {
if err := cl.Destroy(ctx); err != nil {
return err
}
return cl.CleanUpWorkspace()

View File

@ -33,7 +33,7 @@ func TestTerminator(t *testing.T) {
wantErr: true,
},
"destroy cluster error": {
tfClient: &stubTerraformClient{destroyClusterErr: someErr},
tfClient: &stubTerraformClient{destroyErr: someErr},
libvirt: &stubLibvirtRunner{},
wantErr: true,
},
@ -70,7 +70,7 @@ func TestTerminator(t *testing.T) {
}
assert.NoError(err)
cl := tc.tfClient.(*stubTerraformClient)
assert.True(cl.destroyClusterCalled)
assert.True(cl.destroyCalled)
assert.True(cl.removeInstallerCalled)
assert.True(tc.libvirt.stopCalled)
})

View File

@ -197,6 +197,10 @@ func (c *iamCreator) create(ctx context.Context) error {
}
c.log.Debugf("Using flags: %+v", flags)
if err := c.checkWorkingDir(); err != nil {
return err
}
if !flags.yesFlag {
c.cmd.Printf("The following IAM configuration will be created:\n\n")
c.providerCreator.printConfirmValues(c.cmd, flags)
@ -275,6 +279,17 @@ func (c *iamCreator) parseFlagsAndSetupConfig() (iamFlags, error) {
return flags, nil
}
// checkWorkingDir checks if the current working directory already contains a Terraform dir or a Constellation config file.
func (c *iamCreator) checkWorkingDir() error {
if _, err := c.fileHandler.Stat(constants.TerraformIAMWorkingDir); err == nil {
return fmt.Errorf("the current working directory already contains the %s directory. Please run the command in a different directory", constants.TerraformIAMWorkingDir)
}
if _, err := c.fileHandler.Stat(constants.ConfigFilename); err == nil {
return fmt.Errorf("the current working directory already contains the %s file. Please run the command in a different directory", constants.ConfigFilename)
}
return nil
}
// iamFlags contains the parsed flags of the iam create command, including the parsed flags of the selected cloud provider.
type iamFlags struct {
aws awsFlags

View File

@ -67,15 +67,18 @@ func TestParseIDFile(t *testing.T) {
}
func TestIAMCreateAWS(t *testing.T) {
defaultFs := func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string) afero.Fs {
defaultFs := func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string, existingDirs []string) afero.Fs {
fs := afero.NewMemMapFs()
fileHandler := file.NewHandler(fs)
for _, f := range existingFiles {
require.NoError(fileHandler.Write(f, []byte{1, 2, 3}, file.OptNone))
}
for _, d := range existingDirs {
require.NoError(fs.MkdirAll(d, 0o755))
}
return fs
}
readOnlyFs := func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string) afero.Fs {
readOnlyFs := func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string, existingDirs []string) afero.Fs {
fs := afero.NewReadOnlyFs(afero.NewMemMapFs())
return fs
}
@ -88,7 +91,7 @@ func TestIAMCreateAWS(t *testing.T) {
}
testCases := map[string]struct {
setupFs func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string) afero.Fs
setupFs func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string, existingDirs []string) afero.Fs
creator *stubIAMCreator
provider cloudprovider.Provider
zoneFlag string
@ -97,6 +100,7 @@ func TestIAMCreateAWS(t *testing.T) {
generateConfigFlag bool
configFlag string
existingFiles []string
existingDirs []string
stdin string
wantAbort bool
wantErr bool
@ -153,6 +157,16 @@ func TestIAMCreateAWS(t *testing.T) {
configFlag: "custom-config.yaml",
existingFiles: []string{"custom-config.yaml"},
},
"iam create aws existing terraform dir": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
provider: cloudprovider.AWS,
zoneFlag: "us-east-2a",
prefixFlag: "test",
yesFlag: true,
wantErr: true,
existingDirs: []string{constants.TerraformIAMWorkingDir},
},
"interactive": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
@ -244,7 +258,7 @@ func TestIAMCreateAWS(t *testing.T) {
require.NoError(cmd.Flags().Set("config", tc.configFlag))
}
fileHandler := file.NewHandler(tc.setupFs(require, tc.provider, tc.existingFiles))
fileHandler := file.NewHandler(tc.setupFs(require, tc.provider, tc.existingFiles, tc.existingDirs))
iamCreator := &iamCreator{
cmd: cmd,
@ -285,15 +299,18 @@ func TestIAMCreateAWS(t *testing.T) {
}
func TestIAMCreateAzure(t *testing.T) {
defaultFs := func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string) afero.Fs {
defaultFs := func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string, existingDirs []string) afero.Fs {
fs := afero.NewMemMapFs()
fileHandler := file.NewHandler(fs)
for _, f := range existingFiles {
require.NoError(fileHandler.Write(f, []byte{1, 2, 3}, file.OptNone))
}
for _, d := range existingDirs {
require.NoError(fs.MkdirAll(d, 0o755))
}
return fs
}
readOnlyFs := func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string) afero.Fs {
readOnlyFs := func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string, existingDirs []string) afero.Fs {
fs := afero.NewReadOnlyFs(afero.NewMemMapFs())
return fs
}
@ -309,7 +326,7 @@ func TestIAMCreateAzure(t *testing.T) {
}
testCases := map[string]struct {
setupFs func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string) afero.Fs
setupFs func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string, existingDirs []string) afero.Fs
creator *stubIAMCreator
provider cloudprovider.Provider
regionFlag string
@ -319,6 +336,7 @@ func TestIAMCreateAzure(t *testing.T) {
generateConfigFlag bool
configFlag string
existingFiles []string
existingDirs []string
stdin string
wantAbort bool
wantErr bool
@ -380,6 +398,17 @@ func TestIAMCreateAzure(t *testing.T) {
yesFlag: true,
wantErr: true,
},
"iam create azure existing terraform dir": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
provider: cloudprovider.Azure,
regionFlag: "westus",
servicePrincipalFlag: "constell-test-sp",
resourceGroupFlag: "constell-test-rg",
yesFlag: true,
wantErr: true,
existingDirs: []string{constants.TerraformIAMWorkingDir},
},
"interactive": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
@ -469,7 +498,7 @@ func TestIAMCreateAzure(t *testing.T) {
require.NoError(cmd.Flags().Set("config", tc.configFlag))
}
fileHandler := file.NewHandler(tc.setupFs(require, tc.provider, tc.existingFiles))
fileHandler := file.NewHandler(tc.setupFs(require, tc.provider, tc.existingFiles, tc.existingDirs))
iamCreator := &iamCreator{
cmd: cmd,
@ -513,15 +542,18 @@ func TestIAMCreateAzure(t *testing.T) {
}
func TestIAMCreateGCP(t *testing.T) {
defaultFs := func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string) afero.Fs {
defaultFs := func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string, existingDirs []string) afero.Fs {
fs := afero.NewMemMapFs()
fileHandler := file.NewHandler(fs)
for _, f := range existingFiles {
require.NoError(fileHandler.Write(f, []byte{1, 2, 3}, file.OptNone))
}
for _, d := range existingDirs {
require.NoError(fs.MkdirAll(d, 0o755))
}
return fs
}
readOnlyFs := func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string) afero.Fs {
readOnlyFs := func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string, existingDirs []string) afero.Fs {
fs := afero.NewReadOnlyFs(afero.NewMemMapFs())
return fs
}
@ -539,7 +571,7 @@ func TestIAMCreateGCP(t *testing.T) {
}
testCases := map[string]struct {
setupFs func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string) afero.Fs
setupFs func(require *require.Assertions, provider cloudprovider.Provider, existingFiles []string, existingDirs []string) afero.Fs
creator *stubIAMCreator
provider cloudprovider.Provider
zoneFlag string
@ -549,6 +581,7 @@ func TestIAMCreateGCP(t *testing.T) {
generateConfigFlag bool
configFlag string
existingFiles []string
existingDirs []string
stdin string
wantAbort bool
wantErr bool
@ -610,6 +643,18 @@ func TestIAMCreateGCP(t *testing.T) {
yesFlag: true,
wantErr: true,
},
"iam create gcp existing terraform dir": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
provider: cloudprovider.GCP,
zoneFlag: "europe-west1-a",
serviceAccountIDFlag: "constell-test",
projectIDFlag: "constell-1234",
existingDirs: []string{constants.TerraformIAMWorkingDir},
yesFlag: true,
wantErr: true,
},
"iam create gcp invalid flags": {
setupFs: defaultFs,
creator: &stubIAMCreator{id: validIAMIDFile},
@ -718,7 +763,7 @@ func TestIAMCreateGCP(t *testing.T) {
require.NoError(cmd.Flags().Set("config", tc.configFlag))
}
fileHandler := file.NewHandler(tc.setupFs(require, tc.provider, tc.existingFiles))
fileHandler := file.NewHandler(tc.setupFs(require, tc.provider, tc.existingFiles, tc.existingDirs))
iamCreator := &iamCreator{
cmd: cmd,

View File

@ -272,8 +272,8 @@ func (c *Client) CreateIAMConfig(ctx context.Context, provider cloudprovider.Pro
}
}
// DestroyCluster destroys a Constellation cluster using Terraform.
func (c *Client) DestroyCluster(ctx context.Context) error {
// Destroy destroys Terraform-created cloud resources.
func (c *Client) Destroy(ctx context.Context) error {
if err := c.tf.Init(ctx); err != nil {
return err
}

View File

@ -660,7 +660,7 @@ func TestDestroyInstances(t *testing.T) {
tf: tc.tf,
}
err := c.DestroyCluster(context.Background())
err := c.Destroy(context.Background())
if tc.wantErr {
assert.Error(err)
return