github.com/jae-cisco/terraform@v0.11.12-beta1/command/workspace_delete.go (about)

     1  package command
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/terraform/command/clistate"
     9  	"github.com/mitchellh/cli"
    10  	"github.com/posener/complete"
    11  )
    12  
    13  type WorkspaceDeleteCommand struct {
    14  	Meta
    15  	LegacyName bool
    16  }
    17  
    18  func (c *WorkspaceDeleteCommand) Run(args []string) int {
    19  	args, err := c.Meta.process(args, true)
    20  	if err != nil {
    21  		return 1
    22  	}
    23  
    24  	envCommandShowWarning(c.Ui, c.LegacyName)
    25  
    26  	force := false
    27  	cmdFlags := c.Meta.flagSet("workspace")
    28  	cmdFlags.BoolVar(&force, "force", false, "force removal of a non-empty workspace")
    29  	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
    30  	if err := cmdFlags.Parse(args); err != nil {
    31  		return 1
    32  	}
    33  	args = cmdFlags.Args()
    34  	if len(args) == 0 {
    35  		c.Ui.Error("expected NAME.\n")
    36  		return cli.RunResultHelp
    37  	}
    38  
    39  	delEnv := args[0]
    40  
    41  	if !validWorkspaceName(delEnv) {
    42  		c.Ui.Error(fmt.Sprintf(envInvalidName, delEnv))
    43  		return 1
    44  	}
    45  
    46  	configPath, err := ModulePath(args[1:])
    47  	if err != nil {
    48  		c.Ui.Error(err.Error())
    49  		return 1
    50  	}
    51  
    52  	cfg, err := c.Config(configPath)
    53  	if err != nil {
    54  		c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err))
    55  		return 1
    56  	}
    57  
    58  	// Load the backend
    59  	b, err := c.Backend(&BackendOpts{
    60  		Config: cfg,
    61  	})
    62  
    63  	if err != nil {
    64  		c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
    65  		return 1
    66  	}
    67  
    68  	states, err := b.States()
    69  	if err != nil {
    70  		c.Ui.Error(err.Error())
    71  		return 1
    72  	}
    73  
    74  	exists := false
    75  	for _, s := range states {
    76  		if delEnv == s {
    77  			exists = true
    78  			break
    79  		}
    80  	}
    81  
    82  	if !exists {
    83  		c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envDoesNotExist), delEnv))
    84  		return 1
    85  	}
    86  
    87  	if delEnv == c.Workspace() {
    88  		c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envDelCurrent), delEnv))
    89  		return 1
    90  	}
    91  
    92  	// we need the actual state to see if it's empty
    93  	sMgr, err := b.State(delEnv)
    94  	if err != nil {
    95  		c.Ui.Error(err.Error())
    96  		return 1
    97  	}
    98  
    99  	var stateLocker clistate.Locker
   100  	if c.stateLock {
   101  		stateLocker = clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize())
   102  		if err := stateLocker.Lock(sMgr, "workspace_delete"); err != nil {
   103  			c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
   104  			return 1
   105  		}
   106  	} else {
   107  		stateLocker = clistate.NewNoopLocker()
   108  	}
   109  
   110  	if err := sMgr.RefreshState(); err != nil {
   111  		c.Ui.Error(err.Error())
   112  		return 1
   113  	}
   114  
   115  	hasResources := sMgr.State().HasResources()
   116  
   117  	if hasResources && !force {
   118  		c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envNotEmpty), delEnv))
   119  		return 1
   120  	}
   121  
   122  	// We need to release the lock just before deleting the state, in case
   123  	// the backend can't remove the resource while holding the lock. This
   124  	// is currently true for Windows local files.
   125  	//
   126  	// TODO: While there is little safety in locking while deleting the
   127  	// state, it might be nice to be able to coordinate processes around
   128  	// state deletion, i.e. in a CI environment. Adding Delete() as a
   129  	// required method of States would allow the removal of the resource to
   130  	// be delegated from the Backend to the State itself.
   131  	stateLocker.Unlock(nil)
   132  
   133  	err = b.DeleteState(delEnv)
   134  	if err != nil {
   135  		c.Ui.Error(err.Error())
   136  		return 1
   137  	}
   138  
   139  	c.Ui.Output(
   140  		c.Colorize().Color(
   141  			fmt.Sprintf(envDeleted, delEnv),
   142  		),
   143  	)
   144  
   145  	if hasResources {
   146  		c.Ui.Output(
   147  			c.Colorize().Color(
   148  				fmt.Sprintf(envWarnNotEmpty, delEnv),
   149  			),
   150  		)
   151  	}
   152  
   153  	return 0
   154  }
   155  
   156  func (c *WorkspaceDeleteCommand) AutocompleteArgs() complete.Predictor {
   157  	return completePredictSequence{
   158  		complete.PredictNothing, // the "select" subcommand itself (already matched)
   159  		c.completePredictWorkspaceName(),
   160  		complete.PredictDirs(""),
   161  	}
   162  }
   163  
   164  func (c *WorkspaceDeleteCommand) AutocompleteFlags() complete.Flags {
   165  	return complete.Flags{
   166  		"-force": complete.PredictNothing,
   167  	}
   168  }
   169  
   170  func (c *WorkspaceDeleteCommand) Help() string {
   171  	helpText := `
   172  Usage: terraform workspace delete [OPTIONS] NAME [DIR]
   173  
   174    Delete a Terraform workspace
   175  
   176  
   177  Options:
   178  
   179      -force    remove a non-empty workspace.
   180  `
   181  	return strings.TrimSpace(helpText)
   182  }
   183  
   184  func (c *WorkspaceDeleteCommand) Synopsis() string {
   185  	return "Delete a workspace"
   186  }