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