github.com/jzbruno/terraform@v0.10.3-0.20180104230435-18975d727047/command/workspace_new.go (about) 1 package command 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "strings" 8 9 "github.com/hashicorp/terraform/command/clistate" 10 "github.com/hashicorp/terraform/state" 11 "github.com/hashicorp/terraform/terraform" 12 "github.com/mitchellh/cli" 13 "github.com/posener/complete" 14 ) 15 16 type WorkspaceNewCommand struct { 17 Meta 18 LegacyName bool 19 } 20 21 func (c *WorkspaceNewCommand) Run(args []string) int { 22 args, err := c.Meta.process(args, true) 23 if err != nil { 24 return 1 25 } 26 27 envCommandShowWarning(c.Ui, c.LegacyName) 28 29 statePath := "" 30 31 cmdFlags := c.Meta.flagSet("workspace new") 32 cmdFlags.StringVar(&statePath, "state", "", "terraform state file") 33 cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } 34 if err := cmdFlags.Parse(args); err != nil { 35 return 1 36 } 37 args = cmdFlags.Args() 38 if len(args) == 0 { 39 c.Ui.Error("Expected a single argument: NAME.\n") 40 return cli.RunResultHelp 41 } 42 43 newEnv := args[0] 44 45 if !validWorkspaceName(newEnv) { 46 c.Ui.Error(fmt.Sprintf(envInvalidName, newEnv)) 47 return 1 48 } 49 50 // You can't ask to create a workspace when you're overriding the 51 // workspace name to be something different. 52 if current, isOverridden := c.WorkspaceOverridden(); current != newEnv && isOverridden { 53 c.Ui.Error(envIsOverriddenNewError) 54 return 1 55 } 56 57 configPath, err := ModulePath(args[1:]) 58 if err != nil { 59 c.Ui.Error(err.Error()) 60 return 1 61 } 62 63 conf, err := c.Config(configPath) 64 if err != nil { 65 c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err)) 66 } 67 68 // Load the backend 69 b, err := c.Backend(&BackendOpts{ 70 Config: conf, 71 }) 72 73 if err != nil { 74 c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) 75 return 1 76 } 77 78 states, err := b.States() 79 if err != nil { 80 c.Ui.Error(fmt.Sprintf("Failed to get configured named states: %s", err)) 81 return 1 82 } 83 for _, s := range states { 84 if newEnv == s { 85 c.Ui.Error(fmt.Sprintf(envExists, newEnv)) 86 return 1 87 } 88 } 89 90 _, err = b.State(newEnv) 91 if err != nil { 92 c.Ui.Error(err.Error()) 93 return 1 94 } 95 96 // now set the current workspace locally 97 if err := c.SetWorkspace(newEnv); err != nil { 98 c.Ui.Error(fmt.Sprintf("Error selecting new workspace: %s", err)) 99 return 1 100 } 101 102 c.Ui.Output(c.Colorize().Color(fmt.Sprintf( 103 strings.TrimSpace(envCreated), newEnv))) 104 105 if statePath == "" { 106 // if we're not loading a state, then we're done 107 return 0 108 } 109 110 // load the new Backend state 111 sMgr, err := b.State(newEnv) 112 if err != nil { 113 c.Ui.Error(err.Error()) 114 return 1 115 } 116 117 if c.stateLock { 118 lockCtx, cancel := context.WithTimeout(context.Background(), c.stateLockTimeout) 119 defer cancel() 120 121 // Lock the state if we can 122 lockInfo := state.NewLockInfo() 123 lockInfo.Operation = "workspace new" 124 lockID, err := clistate.Lock(lockCtx, sMgr, lockInfo, c.Ui, c.Colorize()) 125 if err != nil { 126 c.Ui.Error(fmt.Sprintf("Error locking state: %s", err)) 127 return 1 128 } 129 defer clistate.Unlock(sMgr, lockID, c.Ui, c.Colorize()) 130 } 131 132 // read the existing state file 133 stateFile, err := os.Open(statePath) 134 if err != nil { 135 c.Ui.Error(err.Error()) 136 return 1 137 } 138 139 s, err := terraform.ReadState(stateFile) 140 if err != nil { 141 c.Ui.Error(err.Error()) 142 return 1 143 } 144 145 // save the existing state in the new Backend. 146 err = sMgr.WriteState(s) 147 if err != nil { 148 c.Ui.Error(err.Error()) 149 return 1 150 } 151 err = sMgr.PersistState() 152 if err != nil { 153 c.Ui.Error(err.Error()) 154 return 1 155 } 156 157 return 0 158 } 159 160 func (c *WorkspaceNewCommand) AutocompleteArgs() complete.Predictor { 161 return completePredictSequence{ 162 complete.PredictNothing, // the "new" subcommand itself (already matched) 163 complete.PredictAnything, 164 complete.PredictDirs(""), 165 } 166 } 167 168 func (c *WorkspaceNewCommand) AutocompleteFlags() complete.Flags { 169 return complete.Flags{ 170 "-state": complete.PredictFiles("*.tfstate"), 171 } 172 } 173 174 func (c *WorkspaceNewCommand) Help() string { 175 helpText := ` 176 Usage: terraform workspace new [OPTIONS] NAME [DIR] 177 178 Create a new Terraform workspace. 179 180 181 Options: 182 183 -state=path Copy an existing state file into the new workspace. 184 ` 185 return strings.TrimSpace(helpText) 186 } 187 188 func (c *WorkspaceNewCommand) Synopsis() string { 189 return "Create a new workspace" 190 }