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