github.com/gerbenjacobs/terraform@v0.9.5-0.20170630130047-e6ddd62583d8/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 ) 12 13 type WorkspaceDeleteCommand struct { 14 Meta 15 LegacyName bool 16 } 17 18 func (c *WorkspaceDeleteCommand) Run(args []string) int { 19 args = c.Meta.process(args, true) 20 21 envCommandShowWarning(c.Ui, c.LegacyName) 22 23 force := false 24 cmdFlags := c.Meta.flagSet("workspace") 25 cmdFlags.BoolVar(&force, "force", false, "force removal of a non-empty workspace") 26 cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } 27 if err := cmdFlags.Parse(args); err != nil { 28 return 1 29 } 30 args = cmdFlags.Args() 31 if len(args) == 0 { 32 c.Ui.Error("expected NAME.\n") 33 return cli.RunResultHelp 34 } 35 36 delEnv := args[0] 37 38 if !validWorkspaceName(delEnv) { 39 c.Ui.Error(fmt.Sprintf(envInvalidName, delEnv)) 40 return 1 41 } 42 43 configPath, err := ModulePath(args[1:]) 44 if err != nil { 45 c.Ui.Error(err.Error()) 46 return 1 47 } 48 49 cfg, err := c.Config(configPath) 50 if err != nil { 51 c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err)) 52 return 1 53 } 54 55 // Load the backend 56 b, err := c.Backend(&BackendOpts{ 57 Config: cfg, 58 }) 59 60 if err != nil { 61 c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) 62 return 1 63 } 64 65 states, err := b.States() 66 if err != nil { 67 c.Ui.Error(err.Error()) 68 return 1 69 } 70 71 exists := false 72 for _, s := range states { 73 if delEnv == s { 74 exists = true 75 break 76 } 77 } 78 79 if !exists { 80 c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envDoesNotExist), delEnv)) 81 return 1 82 } 83 84 if delEnv == c.Workspace() { 85 c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envDelCurrent), delEnv)) 86 return 1 87 } 88 89 // we need the actual state to see if it's empty 90 sMgr, err := b.State(delEnv) 91 if err != nil { 92 c.Ui.Error(err.Error()) 93 return 1 94 } 95 96 if err := sMgr.RefreshState(); err != nil { 97 c.Ui.Error(err.Error()) 98 return 1 99 } 100 101 hasResources := sMgr.State().HasResources() 102 103 if hasResources && !force { 104 c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envNotEmpty), delEnv)) 105 return 1 106 } 107 108 // Honor the lock request, for consistency and one final safety check. 109 if c.stateLock { 110 lockCtx, cancel := context.WithTimeout(context.Background(), c.stateLockTimeout) 111 defer cancel() 112 113 // Lock the state if we can 114 lockInfo := state.NewLockInfo() 115 lockInfo.Operation = "workspace delete" 116 lockID, err := clistate.Lock(lockCtx, sMgr, lockInfo, c.Ui, c.Colorize()) 117 if err != nil { 118 c.Ui.Error(fmt.Sprintf("Error locking state: %s", err)) 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 clistate.Unlock(sMgr, lockID, c.Ui, c.Colorize()) 132 } 133 134 err = b.DeleteState(delEnv) 135 if err != nil { 136 c.Ui.Error(err.Error()) 137 return 1 138 } 139 140 c.Ui.Output( 141 c.Colorize().Color( 142 fmt.Sprintf(envDeleted, delEnv), 143 ), 144 ) 145 146 if hasResources { 147 c.Ui.Output( 148 c.Colorize().Color( 149 fmt.Sprintf(envWarnNotEmpty, delEnv), 150 ), 151 ) 152 } 153 154 return 0 155 } 156 func (c *WorkspaceDeleteCommand) Help() string { 157 helpText := ` 158 Usage: terraform workspace delete [OPTIONS] NAME [DIR] 159 160 Delete a Terraform workspace 161 162 163 Options: 164 165 -force remove a non-empty workspace. 166 ` 167 return strings.TrimSpace(helpText) 168 } 169 170 func (c *WorkspaceDeleteCommand) Synopsis() string { 171 return "Delete a workspace" 172 }