github.com/openshift/terraform@v0.11.12-beta1/commands.go (about) 1 package main 2 3 import ( 4 "log" 5 "os" 6 "os/signal" 7 8 "github.com/hashicorp/terraform/command" 9 pluginDiscovery "github.com/hashicorp/terraform/plugin/discovery" 10 "github.com/hashicorp/terraform/svchost" 11 "github.com/hashicorp/terraform/svchost/auth" 12 "github.com/hashicorp/terraform/svchost/disco" 13 "github.com/mitchellh/cli" 14 ) 15 16 // runningInAutomationEnvName gives the name of an environment variable that 17 // can be set to any non-empty value in order to suppress certain messages 18 // that assume that Terraform is being run from a command prompt. 19 const runningInAutomationEnvName = "TF_IN_AUTOMATION" 20 21 // Commands is the mapping of all the available Terraform commands. 22 var Commands map[string]cli.CommandFactory 23 var PlumbingCommands map[string]struct{} 24 25 // Ui is the cli.Ui used for communicating to the outside world. 26 var Ui cli.Ui 27 28 const ( 29 ErrorPrefix = "e:" 30 OutputPrefix = "o:" 31 ) 32 33 func initCommands(config *Config, services *disco.Disco) { 34 var inAutomation bool 35 if v := os.Getenv(runningInAutomationEnvName); v != "" { 36 inAutomation = true 37 } 38 39 for userHost, hostConfig := range config.Hosts { 40 host, err := svchost.ForComparison(userHost) 41 if err != nil { 42 // We expect the config was already validated by the time we get 43 // here, so we'll just ignore invalid hostnames. 44 continue 45 } 46 services.ForceHostServices(host, hostConfig.Services) 47 } 48 49 dataDir := os.Getenv("TF_DATA_DIR") 50 51 meta := command.Meta{ 52 Color: true, 53 GlobalPluginDirs: globalPluginDirs(), 54 PluginOverrides: &PluginOverrides, 55 Ui: Ui, 56 57 Services: services, 58 59 RunningInAutomation: inAutomation, 60 PluginCacheDir: config.PluginCacheDir, 61 OverrideDataDir: dataDir, 62 63 ShutdownCh: makeShutdownCh(), 64 } 65 66 // The command list is included in the terraform -help 67 // output, which is in turn included in the docs at 68 // website/source/docs/commands/index.html.markdown; if you 69 // add, remove or reclassify commands then consider updating 70 // that to match. 71 72 PlumbingCommands = map[string]struct{}{ 73 "state": struct{}{}, // includes all subcommands 74 "debug": struct{}{}, // includes all subcommands 75 "force-unlock": struct{}{}, 76 } 77 78 Commands = map[string]cli.CommandFactory{ 79 "apply": func() (cli.Command, error) { 80 return &command.ApplyCommand{ 81 Meta: meta, 82 }, nil 83 }, 84 85 "console": func() (cli.Command, error) { 86 return &command.ConsoleCommand{ 87 Meta: meta, 88 }, nil 89 }, 90 91 "destroy": func() (cli.Command, error) { 92 return &command.ApplyCommand{ 93 Meta: meta, 94 Destroy: true, 95 }, nil 96 }, 97 98 "env": func() (cli.Command, error) { 99 return &command.WorkspaceCommand{ 100 Meta: meta, 101 LegacyName: true, 102 }, nil 103 }, 104 105 "env list": func() (cli.Command, error) { 106 return &command.WorkspaceListCommand{ 107 Meta: meta, 108 LegacyName: true, 109 }, nil 110 }, 111 112 "env select": func() (cli.Command, error) { 113 return &command.WorkspaceSelectCommand{ 114 Meta: meta, 115 LegacyName: true, 116 }, nil 117 }, 118 119 "env new": func() (cli.Command, error) { 120 return &command.WorkspaceNewCommand{ 121 Meta: meta, 122 LegacyName: true, 123 }, nil 124 }, 125 126 "env delete": func() (cli.Command, error) { 127 return &command.WorkspaceDeleteCommand{ 128 Meta: meta, 129 LegacyName: true, 130 }, nil 131 }, 132 133 "fmt": func() (cli.Command, error) { 134 return &command.FmtCommand{ 135 Meta: meta, 136 }, nil 137 }, 138 139 "get": func() (cli.Command, error) { 140 return &command.GetCommand{ 141 Meta: meta, 142 }, nil 143 }, 144 145 "graph": func() (cli.Command, error) { 146 return &command.GraphCommand{ 147 Meta: meta, 148 }, nil 149 }, 150 151 "import": func() (cli.Command, error) { 152 return &command.ImportCommand{ 153 Meta: meta, 154 }, nil 155 }, 156 157 "init": func() (cli.Command, error) { 158 return &command.InitCommand{ 159 Meta: meta, 160 }, nil 161 }, 162 163 "internal-plugin": func() (cli.Command, error) { 164 return &command.InternalPluginCommand{ 165 Meta: meta, 166 }, nil 167 }, 168 169 "output": func() (cli.Command, error) { 170 return &command.OutputCommand{ 171 Meta: meta, 172 }, nil 173 }, 174 175 "plan": func() (cli.Command, error) { 176 return &command.PlanCommand{ 177 Meta: meta, 178 }, nil 179 }, 180 181 "providers": func() (cli.Command, error) { 182 return &command.ProvidersCommand{ 183 Meta: meta, 184 }, nil 185 }, 186 187 "push": func() (cli.Command, error) { 188 return &command.PushCommand{ 189 Meta: meta, 190 }, nil 191 }, 192 193 "refresh": func() (cli.Command, error) { 194 return &command.RefreshCommand{ 195 Meta: meta, 196 }, nil 197 }, 198 199 "show": func() (cli.Command, error) { 200 return &command.ShowCommand{ 201 Meta: meta, 202 }, nil 203 }, 204 205 "taint": func() (cli.Command, error) { 206 return &command.TaintCommand{ 207 Meta: meta, 208 }, nil 209 }, 210 211 "validate": func() (cli.Command, error) { 212 return &command.ValidateCommand{ 213 Meta: meta, 214 }, nil 215 }, 216 217 "version": func() (cli.Command, error) { 218 return &command.VersionCommand{ 219 Meta: meta, 220 Revision: GitCommit, 221 Version: Version, 222 VersionPrerelease: VersionPrerelease, 223 CheckFunc: commandVersionCheck, 224 }, nil 225 }, 226 227 "untaint": func() (cli.Command, error) { 228 return &command.UntaintCommand{ 229 Meta: meta, 230 }, nil 231 }, 232 233 "workspace": func() (cli.Command, error) { 234 return &command.WorkspaceCommand{ 235 Meta: meta, 236 }, nil 237 }, 238 239 "workspace list": func() (cli.Command, error) { 240 return &command.WorkspaceListCommand{ 241 Meta: meta, 242 }, nil 243 }, 244 245 "workspace select": func() (cli.Command, error) { 246 return &command.WorkspaceSelectCommand{ 247 Meta: meta, 248 }, nil 249 }, 250 251 "workspace show": func() (cli.Command, error) { 252 return &command.WorkspaceShowCommand{ 253 Meta: meta, 254 }, nil 255 }, 256 257 "workspace new": func() (cli.Command, error) { 258 return &command.WorkspaceNewCommand{ 259 Meta: meta, 260 }, nil 261 }, 262 263 "workspace delete": func() (cli.Command, error) { 264 return &command.WorkspaceDeleteCommand{ 265 Meta: meta, 266 }, nil 267 }, 268 269 //----------------------------------------------------------- 270 // Plumbing 271 //----------------------------------------------------------- 272 273 "debug": func() (cli.Command, error) { 274 return &command.DebugCommand{ 275 Meta: meta, 276 }, nil 277 }, 278 279 "debug json2dot": func() (cli.Command, error) { 280 return &command.DebugJSON2DotCommand{ 281 Meta: meta, 282 }, nil 283 }, 284 285 "force-unlock": func() (cli.Command, error) { 286 return &command.UnlockCommand{ 287 Meta: meta, 288 }, nil 289 }, 290 291 "state": func() (cli.Command, error) { 292 return &command.StateCommand{}, nil 293 }, 294 295 "state list": func() (cli.Command, error) { 296 return &command.StateListCommand{ 297 Meta: meta, 298 }, nil 299 }, 300 301 "state rm": func() (cli.Command, error) { 302 return &command.StateRmCommand{ 303 StateMeta: command.StateMeta{ 304 Meta: meta, 305 }, 306 }, nil 307 }, 308 309 "state mv": func() (cli.Command, error) { 310 return &command.StateMvCommand{ 311 StateMeta: command.StateMeta{ 312 Meta: meta, 313 }, 314 }, nil 315 }, 316 317 "state pull": func() (cli.Command, error) { 318 return &command.StatePullCommand{ 319 Meta: meta, 320 }, nil 321 }, 322 323 "state push": func() (cli.Command, error) { 324 return &command.StatePushCommand{ 325 Meta: meta, 326 }, nil 327 }, 328 329 "state show": func() (cli.Command, error) { 330 return &command.StateShowCommand{ 331 Meta: meta, 332 }, nil 333 }, 334 } 335 } 336 337 // makeShutdownCh creates an interrupt listener and returns a channel. 338 // A message will be sent on the channel for every interrupt received. 339 func makeShutdownCh() <-chan struct{} { 340 resultCh := make(chan struct{}) 341 342 signalCh := make(chan os.Signal, 4) 343 signal.Notify(signalCh, ignoreSignals...) 344 signal.Notify(signalCh, forwardSignals...) 345 go func() { 346 for { 347 <-signalCh 348 resultCh <- struct{}{} 349 } 350 }() 351 352 return resultCh 353 } 354 355 func credentialsSource(config *Config) auth.CredentialsSource { 356 creds := auth.NoCredentials 357 if len(config.Credentials) > 0 { 358 staticTable := map[svchost.Hostname]map[string]interface{}{} 359 for userHost, creds := range config.Credentials { 360 host, err := svchost.ForComparison(userHost) 361 if err != nil { 362 // We expect the config was already validated by the time we get 363 // here, so we'll just ignore invalid hostnames. 364 continue 365 } 366 staticTable[host] = creds 367 } 368 creds = auth.StaticCredentialsSource(staticTable) 369 } 370 371 for helperType, helperConfig := range config.CredentialsHelpers { 372 log.Printf("[DEBUG] Searching for credentials helper named %q", helperType) 373 available := pluginDiscovery.FindPlugins("credentials", globalPluginDirs()) 374 available = available.WithName(helperType) 375 if available.Count() == 0 { 376 log.Printf("[ERROR] Unable to find credentials helper %q; ignoring", helperType) 377 break 378 } 379 380 selected := available.Newest() 381 382 helperSource := auth.HelperProgramCredentialsSource(selected.Path, helperConfig.Args...) 383 creds = auth.Credentials{ 384 creds, 385 auth.CachingCredentialsSource(helperSource), // cached because external operation may be slow/expensive 386 } 387 388 // There should only be zero or one "credentials_helper" blocks. We 389 // assume that the config was validated earlier and so we don't check 390 // for extras here. 391 break 392 } 393 394 return creds 395 }