2023-02-24 05:36:41 -05:00
/ *
Copyright ( c ) Edgeless Systems GmbH
SPDX - License - Identifier : AGPL - 3.0 - only
* /
package cmd
import (
"errors"
"fmt"
"os"
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
2023-08-08 09:42:06 -04:00
"github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix"
2023-04-14 08:15:07 -04:00
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
2023-02-24 05:36:41 -05:00
"github.com/edgelesssys/constellation/v2/internal/cloud/gcpshared"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/spf13/afero"
"github.com/spf13/cobra"
)
2023-02-28 03:52:32 -05:00
// NewIAMDestroyCmd returns a new cobra.Command for the iam destroy subcommand.
func newIAMDestroyCmd ( ) * cobra . Command {
cmd := & cobra . Command {
Use : "destroy" ,
Short : "Destroy an IAM configuration and delete local Terraform files" ,
Long : "Destroy an IAM configuration and delete local Terraform files." ,
Args : cobra . ExactArgs ( 0 ) ,
RunE : runIAMDestroy ,
}
cmd . Flags ( ) . BoolP ( "yes" , "y" , false , "destroy the IAM configuration without asking for confirmation" )
return cmd
}
2023-03-20 06:03:36 -04:00
func runIAMDestroy ( cmd * cobra . Command , _ [ ] string ) error {
2023-02-24 05:36:41 -05:00
log , err := newCLILogger ( cmd )
if err != nil {
return fmt . Errorf ( "creating logger: %w" , err )
}
defer log . Sync ( )
spinner := newSpinner ( cmd . ErrOrStderr ( ) )
2023-08-04 07:53:51 -04:00
destroyer := cloudcmd . NewIAMDestroyer ( )
2023-02-24 05:36:41 -05:00
fsHandler := file . NewHandler ( afero . NewOsFs ( ) )
c := & destroyCmd { log : log }
return c . iamDestroy ( cmd , spinner , destroyer , fsHandler )
}
type destroyCmd struct {
log debugLog
2023-08-08 09:42:06 -04:00
pf pathprefix . PathPrefixer
2023-02-24 05:36:41 -05:00
}
func ( c * destroyCmd ) iamDestroy ( cmd * cobra . Command , spinner spinnerInterf , destroyer iamDestroyer , fsHandler file . Handler ) error {
2023-04-14 08:15:07 -04:00
flags , err := c . parseDestroyFlags ( cmd )
if err != nil {
return fmt . Errorf ( "parsing flags: %w" , err )
}
2023-02-24 05:36:41 -05:00
// check if there is a possibility that the cluster is still running by looking out for specific files
2023-08-16 09:38:40 -04:00
c . log . Debugf ( "Checking if %q exists" , c . pf . PrefixPrintablePath ( constants . AdminConfFilename ) )
2023-04-14 08:15:07 -04:00
_ , err = fsHandler . Stat ( constants . AdminConfFilename )
2023-02-24 05:36:41 -05:00
if ! errors . Is ( err , os . ErrNotExist ) {
2023-08-16 09:38:40 -04:00
return fmt . Errorf ( "file %q still exists, please make sure to terminate your cluster before destroying your IAM configuration" , c . pf . PrefixPrintablePath ( constants . AdminConfFilename ) )
2023-02-24 05:36:41 -05:00
}
2023-08-16 09:38:40 -04:00
c . log . Debugf ( "Checking if %q exists" , c . pf . PrefixPrintablePath ( constants . ClusterIDsFilename ) )
2023-08-04 07:53:51 -04:00
_ , err = fsHandler . Stat ( constants . ClusterIDsFilename )
2023-02-24 05:36:41 -05:00
if ! errors . Is ( err , os . ErrNotExist ) {
2023-08-16 09:38:40 -04:00
return fmt . Errorf ( "file %q still exists, please make sure to terminate your cluster before destroying your IAM configuration" , c . pf . PrefixPrintablePath ( constants . ClusterIDsFilename ) )
2023-02-24 05:36:41 -05:00
}
gcpFileExists := false
2023-08-16 09:38:40 -04:00
c . log . Debugf ( "Checking if %q exists" , c . pf . PrefixPrintablePath ( constants . GCPServiceAccountKeyFilename ) )
2023-08-08 09:42:06 -04:00
_ , err = fsHandler . Stat ( constants . GCPServiceAccountKeyFilename )
2023-02-24 05:36:41 -05:00
if err != nil {
if ! errors . Is ( err , os . ErrNotExist ) {
return err
}
} else {
2023-08-16 09:38:40 -04:00
c . log . Debugf ( "%q exists" , c . pf . PrefixPrintablePath ( constants . GCPServiceAccountKeyFilename ) )
2023-02-24 05:36:41 -05:00
gcpFileExists = true
}
2023-04-14 08:15:07 -04:00
if ! flags . yes {
2023-02-24 05:36:41 -05:00
// Confirmation
2023-04-19 02:30:11 -04:00
confirmString := "Do you really want to destroy your IAM configuration? Note that this will remove all resources in the resource group."
2023-02-24 05:36:41 -05:00
if gcpFileExists {
2023-08-16 09:38:40 -04:00
confirmString += fmt . Sprintf ( "\nThis will also delete %q" , c . pf . PrefixPrintablePath ( constants . GCPServiceAccountKeyFilename ) )
2023-02-24 05:36:41 -05:00
}
ok , err := askToConfirm ( cmd , confirmString )
if err != nil {
return err
}
if ! ok {
cmd . Println ( "The destruction of the IAM configuration was aborted" )
return nil
}
}
if gcpFileExists {
2023-08-16 09:38:40 -04:00
c . log . Debugf ( "Starting to delete %q" , c . pf . PrefixPrintablePath ( constants . GCPServiceAccountKeyFilename ) )
2023-08-08 09:42:06 -04:00
proceed , err := c . deleteGCPServiceAccountKeyFile ( cmd , destroyer , fsHandler )
2023-02-24 05:36:41 -05:00
if err != nil {
return err
}
if ! proceed {
cmd . Println ( "Destruction was aborted" )
return nil
}
}
c . log . Debugf ( "Starting to destroy IAM configuration" )
spinner . Start ( "Destroying IAM configuration" , false )
defer spinner . Stop ( )
2023-08-04 07:53:51 -04:00
if err := destroyer . DestroyIAMConfiguration ( cmd . Context ( ) , constants . TerraformIAMWorkingDir , flags . tfLogLevel ) ; err != nil {
2023-02-24 05:36:41 -05:00
return fmt . Errorf ( "destroying IAM configuration: %w" , err )
}
spinner . Stop ( ) // stop the spinner to print a new line
fmt . Println ( "Successfully destroyed IAM configuration" )
return nil
}
2023-08-08 09:42:06 -04:00
func ( c * destroyCmd ) deleteGCPServiceAccountKeyFile ( cmd * cobra . Command , destroyer iamDestroyer , fsHandler file . Handler ) ( bool , error ) {
2023-02-24 05:36:41 -05:00
var fileSaKey gcpshared . ServiceAccountKey
2023-08-16 09:38:40 -04:00
c . log . Debugf ( "Parsing %q" , c . pf . PrefixPrintablePath ( constants . GCPServiceAccountKeyFilename ) )
2023-08-08 09:42:06 -04:00
if err := fsHandler . ReadJSON ( constants . GCPServiceAccountKeyFilename , & fileSaKey ) ; err != nil {
2023-02-24 05:36:41 -05:00
return false , err
}
c . log . Debugf ( "Getting service account key from the tfstate" )
2023-08-04 07:53:51 -04:00
tfSaKey , err := destroyer . GetTfStateServiceAccountKey ( cmd . Context ( ) , constants . TerraformIAMWorkingDir )
2023-02-24 05:36:41 -05:00
if err != nil {
return false , err
}
c . log . Debugf ( "Checking if keys are the same" )
if tfSaKey != fileSaKey {
2023-08-16 09:38:40 -04:00
cmd . Printf ( "The key in %q don't match up with your Terraform state. %q will not be deleted.\n" , c . pf . PrefixPrintablePath ( constants . GCPServiceAccountKeyFilename ) , c . pf . PrefixPrintablePath ( constants . GCPServiceAccountKeyFilename ) )
2023-02-24 05:36:41 -05:00
return true , nil
}
2023-08-08 09:42:06 -04:00
if err := fsHandler . Remove ( constants . GCPServiceAccountKeyFilename ) ; err != nil {
2023-02-24 05:36:41 -05:00
return false , err
}
2023-08-16 09:38:40 -04:00
c . log . Debugf ( "Successfully deleted %q" , c . pf . PrefixPrintablePath ( constants . GCPServiceAccountKeyFilename ) )
2023-02-24 05:36:41 -05:00
return true , nil
}
2023-04-14 08:15:07 -04:00
type destroyFlags struct {
yes bool
tfLogLevel terraform . LogLevel
}
// parseDestroyFlags parses the flags of the create command.
func ( c * destroyCmd ) parseDestroyFlags ( cmd * cobra . Command ) ( destroyFlags , error ) {
yes , err := cmd . Flags ( ) . GetBool ( "yes" )
if err != nil {
return destroyFlags { } , fmt . Errorf ( "parsing yes bool: %w" , err )
}
c . log . Debugf ( "Yes flag is %t" , yes )
2023-08-08 09:42:06 -04:00
workDir , err := cmd . Flags ( ) . GetString ( "workspace" )
2023-08-04 07:53:51 -04:00
if err != nil {
return destroyFlags { } , fmt . Errorf ( "parsing workspace string: %w" , err )
}
2023-08-08 09:42:06 -04:00
c . log . Debugf ( "Workspace set to %q" , workDir )
c . pf = pathprefix . New ( workDir )
2023-08-04 07:53:51 -04:00
2023-04-14 08:15:07 -04:00
logLevelString , err := cmd . Flags ( ) . GetString ( "tf-log" )
if err != nil {
return destroyFlags { } , fmt . Errorf ( "parsing tf-log string: %w" , err )
}
logLevel , err := terraform . ParseLogLevel ( logLevelString )
if err != nil {
return destroyFlags { } , fmt . Errorf ( "parsing Terraform log level %s: %w" , logLevelString , err )
}
2023-08-16 09:38:40 -04:00
c . log . Debugf ( "Terraform logs will be written into %s at level %s" , c . pf . PrefixPrintablePath ( constants . TerraformWorkingDir ) , logLevel . String ( ) )
2023-04-14 08:15:07 -04:00
return destroyFlags {
tfLogLevel : logLevel ,
yes : yes ,
} , nil
}