github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/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 }