2014-05-24 14:04:43 -05:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2017-10-19 19:28:44 -05:00
|
|
|
"log"
|
2014-05-24 14:04:43 -05:00
|
|
|
"os"
|
2014-07-02 19:01:02 -05:00
|
|
|
"os/signal"
|
2014-05-24 14:04:43 -05:00
|
|
|
|
2017-10-19 19:40:20 -05:00
|
|
|
"github.com/hashicorp/terraform/command"
|
|
|
|
pluginDiscovery "github.com/hashicorp/terraform/plugin/discovery"
|
|
|
|
"github.com/hashicorp/terraform/svchost"
|
2017-10-18 10:52:13 -05:00
|
|
|
"github.com/hashicorp/terraform/svchost/auth"
|
|
|
|
"github.com/hashicorp/terraform/svchost/disco"
|
2014-05-24 14:04:43 -05:00
|
|
|
"github.com/mitchellh/cli"
|
|
|
|
)
|
|
|
|
|
2017-09-08 19:14:37 -05:00
|
|
|
// runningInAutomationEnvName gives the name of an environment variable that
|
|
|
|
// can be set to any non-empty value in order to suppress certain messages
|
|
|
|
// that assume that Terraform is being run from a command prompt.
|
|
|
|
const runningInAutomationEnvName = "TF_IN_AUTOMATION"
|
|
|
|
|
2014-05-24 14:04:43 -05:00
|
|
|
// Commands is the mapping of all the available Terraform commands.
|
|
|
|
var Commands map[string]cli.CommandFactory
|
2016-03-22 12:41:02 -05:00
|
|
|
var PlumbingCommands map[string]struct{}
|
2014-05-24 14:04:43 -05:00
|
|
|
|
2014-06-26 19:05:21 -05:00
|
|
|
// Ui is the cli.Ui used for communicating to the outside world.
|
|
|
|
var Ui cli.Ui
|
|
|
|
|
2015-02-21 18:04:32 -06:00
|
|
|
const (
|
|
|
|
ErrorPrefix = "e:"
|
|
|
|
OutputPrefix = "o:"
|
|
|
|
)
|
2014-06-10 12:28:47 -05:00
|
|
|
|
2018-07-05 14:28:29 -05:00
|
|
|
func initCommands(config *Config, services *disco.Disco) {
|
2017-09-08 19:14:37 -05:00
|
|
|
var inAutomation bool
|
|
|
|
if v := os.Getenv(runningInAutomationEnvName); v != "" {
|
|
|
|
inAutomation = true
|
|
|
|
}
|
|
|
|
|
2017-10-25 18:00:08 -05:00
|
|
|
for userHost, hostConfig := range config.Hosts {
|
|
|
|
host, err := svchost.ForComparison(userHost)
|
|
|
|
if err != nil {
|
|
|
|
// We expect the config was already validated by the time we get
|
|
|
|
// here, so we'll just ignore invalid hostnames.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
services.ForceHostServices(host, hostConfig.Services)
|
|
|
|
}
|
2017-10-18 10:52:13 -05:00
|
|
|
|
2017-11-01 18:44:03 -05:00
|
|
|
dataDir := os.Getenv("TF_DATA_DIR")
|
|
|
|
|
2014-07-12 22:38:56 -05:00
|
|
|
meta := command.Meta{
|
2017-04-13 20:05:58 -05:00
|
|
|
Color: true,
|
|
|
|
GlobalPluginDirs: globalPluginDirs(),
|
|
|
|
PluginOverrides: &PluginOverrides,
|
|
|
|
Ui: Ui,
|
2017-09-08 19:14:37 -05:00
|
|
|
|
2018-07-05 14:28:29 -05:00
|
|
|
Services: services,
|
2017-10-18 10:52:13 -05:00
|
|
|
|
2017-09-08 19:14:37 -05:00
|
|
|
RunningInAutomation: inAutomation,
|
2017-09-01 18:20:25 -05:00
|
|
|
PluginCacheDir: config.PluginCacheDir,
|
2017-11-01 18:44:03 -05:00
|
|
|
OverrideDataDir: dataDir,
|
2017-12-01 10:06:39 -06:00
|
|
|
|
|
|
|
ShutdownCh: makeShutdownCh(),
|
2014-07-12 22:38:56 -05:00
|
|
|
}
|
|
|
|
|
2016-11-24 11:22:18 -06:00
|
|
|
// The command list is included in the terraform -help
|
|
|
|
// output, which is in turn included in the docs at
|
|
|
|
// website/source/docs/commands/index.html.markdown; if you
|
|
|
|
// add, remove or reclassify commands then consider updating
|
|
|
|
// that to match.
|
|
|
|
|
2016-03-22 12:41:02 -05:00
|
|
|
PlumbingCommands = map[string]struct{}{
|
2017-04-01 15:06:28 -05:00
|
|
|
"state": struct{}{}, // includes all subcommands
|
|
|
|
"debug": struct{}{}, // includes all subcommands
|
|
|
|
"force-unlock": struct{}{},
|
2018-03-28 11:47:05 -05:00
|
|
|
"push": struct{}{},
|
2018-06-20 21:27:14 -05:00
|
|
|
"0.12upgrade": struct{}{},
|
2016-03-22 12:41:02 -05:00
|
|
|
}
|
|
|
|
|
2014-05-24 14:04:43 -05:00
|
|
|
Commands = map[string]cli.CommandFactory{
|
2014-05-24 14:27:58 -05:00
|
|
|
"apply": func() (cli.Command, error) {
|
|
|
|
return &command.ApplyCommand{
|
2017-12-01 10:06:39 -06:00
|
|
|
Meta: meta,
|
2014-05-24 14:27:58 -05:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2016-11-14 00:18:18 -06:00
|
|
|
"console": func() (cli.Command, error) {
|
|
|
|
return &command.ConsoleCommand{
|
2017-12-01 10:06:39 -06:00
|
|
|
Meta: meta,
|
2016-11-14 00:18:18 -06:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2014-09-30 23:51:45 -05:00
|
|
|
"destroy": func() (cli.Command, error) {
|
|
|
|
return &command.ApplyCommand{
|
2017-12-01 10:06:39 -06:00
|
|
|
Meta: meta,
|
|
|
|
Destroy: true,
|
2014-09-30 23:51:45 -05:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2017-02-16 17:29:19 -06:00
|
|
|
"env": func() (cli.Command, error) {
|
2017-05-30 17:06:13 -05:00
|
|
|
return &command.WorkspaceCommand{
|
|
|
|
Meta: meta,
|
|
|
|
LegacyName: true,
|
2017-02-16 17:29:19 -06:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2017-02-23 12:13:28 -06:00
|
|
|
"env list": func() (cli.Command, error) {
|
2017-05-30 17:06:13 -05:00
|
|
|
return &command.WorkspaceListCommand{
|
|
|
|
Meta: meta,
|
|
|
|
LegacyName: true,
|
2017-02-23 12:13:28 -06:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
|
|
|
"env select": func() (cli.Command, error) {
|
2017-05-30 17:06:13 -05:00
|
|
|
return &command.WorkspaceSelectCommand{
|
|
|
|
Meta: meta,
|
|
|
|
LegacyName: true,
|
2017-02-23 12:13:28 -06:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
|
|
|
"env new": func() (cli.Command, error) {
|
2017-05-30 17:06:13 -05:00
|
|
|
return &command.WorkspaceNewCommand{
|
|
|
|
Meta: meta,
|
|
|
|
LegacyName: true,
|
2017-02-23 12:13:28 -06:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
|
|
|
"env delete": func() (cli.Command, error) {
|
2017-05-30 17:06:13 -05:00
|
|
|
return &command.WorkspaceDeleteCommand{
|
|
|
|
Meta: meta,
|
|
|
|
LegacyName: true,
|
2017-02-23 12:13:28 -06:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2016-02-02 16:10:22 -06:00
|
|
|
"fmt": func() (cli.Command, error) {
|
|
|
|
return &command.FmtCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2014-09-22 12:56:50 -05:00
|
|
|
"get": func() (cli.Command, error) {
|
|
|
|
return &command.GetCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2014-07-01 12:02:13 -05:00
|
|
|
"graph": func() (cli.Command, error) {
|
|
|
|
return &command.GraphCommand{
|
2014-07-12 22:38:56 -05:00
|
|
|
Meta: meta,
|
2014-07-01 12:02:13 -05:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2016-05-04 12:06:16 -05:00
|
|
|
"import": func() (cli.Command, error) {
|
|
|
|
return &command.ImportCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2014-09-27 14:31:38 -05:00
|
|
|
"init": func() (cli.Command, error) {
|
|
|
|
return &command.InitCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2016-04-04 21:11:11 -05:00
|
|
|
"internal-plugin": func() (cli.Command, error) {
|
|
|
|
return &command.InternalPluginCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2014-07-13 12:25:42 -05:00
|
|
|
"output": func() (cli.Command, error) {
|
|
|
|
return &command.OutputCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2014-06-20 13:47:02 -05:00
|
|
|
"plan": func() (cli.Command, error) {
|
|
|
|
return &command.PlanCommand{
|
2014-07-12 22:38:56 -05:00
|
|
|
Meta: meta,
|
2014-06-09 13:53:41 -05:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2017-04-25 20:10:10 -05:00
|
|
|
"providers": func() (cli.Command, error) {
|
|
|
|
return &command.ProvidersCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2019-02-25 15:32:47 -06:00
|
|
|
"providers schema": func() (cli.Command, error) {
|
|
|
|
return &command.ProvidersSchemaCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2015-03-04 22:42:26 -06:00
|
|
|
"push": func() (cli.Command, error) {
|
|
|
|
return &command.PushCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2014-06-27 13:09:01 -05:00
|
|
|
"refresh": func() (cli.Command, error) {
|
|
|
|
return &command.RefreshCommand{
|
2014-07-12 22:38:56 -05:00
|
|
|
Meta: meta,
|
2014-06-27 13:09:01 -05:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2014-07-12 21:47:31 -05:00
|
|
|
"show": func() (cli.Command, error) {
|
|
|
|
return &command.ShowCommand{
|
2014-07-12 22:38:56 -05:00
|
|
|
Meta: meta,
|
2014-07-12 21:47:31 -05:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2015-02-26 12:29:51 -06:00
|
|
|
"taint": func() (cli.Command, error) {
|
|
|
|
return &command.TaintCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2015-11-05 08:47:08 -06:00
|
|
|
"validate": func() (cli.Command, error) {
|
|
|
|
return &command.ValidateCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2014-05-24 14:04:43 -05:00
|
|
|
"version": func() (cli.Command, error) {
|
|
|
|
return &command.VersionCommand{
|
2014-07-13 12:42:18 -05:00
|
|
|
Meta: meta,
|
2014-05-24 14:04:43 -05:00
|
|
|
Revision: GitCommit,
|
|
|
|
Version: Version,
|
|
|
|
VersionPrerelease: VersionPrerelease,
|
2014-10-13 16:05:29 -05:00
|
|
|
CheckFunc: commandVersionCheck,
|
2014-05-24 14:04:43 -05:00
|
|
|
}, nil
|
|
|
|
},
|
2016-03-08 14:37:34 -06:00
|
|
|
|
|
|
|
"untaint": func() (cli.Command, error) {
|
|
|
|
return &command.UntaintCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
2016-03-22 12:41:02 -05:00
|
|
|
|
2017-05-30 17:06:13 -05:00
|
|
|
"workspace": func() (cli.Command, error) {
|
|
|
|
return &command.WorkspaceCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
|
|
|
"workspace list": func() (cli.Command, error) {
|
|
|
|
return &command.WorkspaceListCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
|
|
|
"workspace select": func() (cli.Command, error) {
|
|
|
|
return &command.WorkspaceSelectCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2017-07-05 16:35:46 -05:00
|
|
|
"workspace show": func() (cli.Command, error) {
|
|
|
|
return &command.WorkspaceShowCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2017-05-30 17:06:13 -05:00
|
|
|
"workspace new": func() (cli.Command, error) {
|
|
|
|
return &command.WorkspaceNewCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
|
|
|
"workspace delete": func() (cli.Command, error) {
|
|
|
|
return &command.WorkspaceDeleteCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2016-03-22 12:41:02 -05:00
|
|
|
//-----------------------------------------------------------
|
|
|
|
// Plumbing
|
|
|
|
//-----------------------------------------------------------
|
|
|
|
|
2018-06-20 21:27:14 -05:00
|
|
|
"0.12upgrade": func() (cli.Command, error) {
|
|
|
|
return &command.ZeroTwelveUpgradeCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2016-11-15 14:33:40 -06:00
|
|
|
"debug": func() (cli.Command, error) {
|
|
|
|
return &command.DebugCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
|
|
|
"debug json2dot": func() (cli.Command, error) {
|
|
|
|
return &command.DebugJSON2DotCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2017-04-01 15:06:28 -05:00
|
|
|
"force-unlock": func() (cli.Command, error) {
|
|
|
|
return &command.UnlockCommand{
|
|
|
|
Meta: meta,
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2016-03-22 12:41:02 -05:00
|
|
|
"state": func() (cli.Command, error) {
|
2017-03-01 09:10:47 -06:00
|
|
|
return &command.StateCommand{}, nil
|
2016-03-22 12:41:02 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
"state list": func() (cli.Command, error) {
|
|
|
|
return &command.StateListCommand{
|
2017-03-01 09:10:47 -06:00
|
|
|
Meta: meta,
|
2016-03-22 12:41:02 -05:00
|
|
|
}, nil
|
|
|
|
},
|
2016-03-25 12:17:25 -05:00
|
|
|
|
2016-03-31 11:29:39 -05:00
|
|
|
"state rm": func() (cli.Command, error) {
|
|
|
|
return &command.StateRmCommand{
|
2017-07-27 10:54:14 -05:00
|
|
|
StateMeta: command.StateMeta{
|
|
|
|
Meta: meta,
|
|
|
|
},
|
2016-03-31 11:29:39 -05:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2016-06-22 03:46:38 -05:00
|
|
|
"state mv": func() (cli.Command, error) {
|
|
|
|
return &command.StateMvCommand{
|
2017-07-27 10:54:14 -05:00
|
|
|
StateMeta: command.StateMeta{
|
|
|
|
Meta: meta,
|
|
|
|
},
|
2016-06-22 03:46:38 -05:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2017-01-18 22:51:06 -06:00
|
|
|
"state pull": func() (cli.Command, error) {
|
|
|
|
return &command.StatePullCommand{
|
2017-03-01 09:10:47 -06:00
|
|
|
Meta: meta,
|
2017-01-18 22:51:06 -06:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
|
|
|
"state push": func() (cli.Command, error) {
|
|
|
|
return &command.StatePushCommand{
|
2017-03-01 09:10:47 -06:00
|
|
|
Meta: meta,
|
2017-01-18 22:51:06 -06:00
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
|
2016-03-25 12:17:25 -05:00
|
|
|
"state show": func() (cli.Command, error) {
|
|
|
|
return &command.StateShowCommand{
|
2017-03-01 09:10:47 -06:00
|
|
|
Meta: meta,
|
2016-03-25 12:17:25 -05:00
|
|
|
}, nil
|
|
|
|
},
|
2014-05-24 14:04:43 -05:00
|
|
|
}
|
|
|
|
}
|
2014-07-02 19:01:02 -05:00
|
|
|
|
|
|
|
// makeShutdownCh creates an interrupt listener and returns a channel.
|
|
|
|
// A message will be sent on the channel for every interrupt received.
|
|
|
|
func makeShutdownCh() <-chan struct{} {
|
|
|
|
resultCh := make(chan struct{})
|
|
|
|
|
|
|
|
signalCh := make(chan os.Signal, 4)
|
2016-12-08 11:10:52 -06:00
|
|
|
signal.Notify(signalCh, ignoreSignals...)
|
|
|
|
signal.Notify(signalCh, forwardSignals...)
|
2014-07-02 19:01:02 -05:00
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
<-signalCh
|
|
|
|
resultCh <- struct{}{}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
return resultCh
|
|
|
|
}
|
2017-10-19 19:40:20 -05:00
|
|
|
|
|
|
|
func credentialsSource(config *Config) auth.CredentialsSource {
|
|
|
|
creds := auth.NoCredentials
|
|
|
|
if len(config.Credentials) > 0 {
|
|
|
|
staticTable := map[svchost.Hostname]map[string]interface{}{}
|
|
|
|
for userHost, creds := range config.Credentials {
|
|
|
|
host, err := svchost.ForComparison(userHost)
|
|
|
|
if err != nil {
|
|
|
|
// We expect the config was already validated by the time we get
|
|
|
|
// here, so we'll just ignore invalid hostnames.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
staticTable[host] = creds
|
|
|
|
}
|
|
|
|
creds = auth.StaticCredentialsSource(staticTable)
|
|
|
|
}
|
|
|
|
|
|
|
|
for helperType, helperConfig := range config.CredentialsHelpers {
|
2017-10-19 19:28:44 -05:00
|
|
|
log.Printf("[DEBUG] Searching for credentials helper named %q", helperType)
|
2017-10-19 19:40:20 -05:00
|
|
|
available := pluginDiscovery.FindPlugins("credentials", globalPluginDirs())
|
|
|
|
available = available.WithName(helperType)
|
|
|
|
if available.Count() == 0 {
|
2017-10-19 19:28:44 -05:00
|
|
|
log.Printf("[ERROR] Unable to find credentials helper %q; ignoring", helperType)
|
2017-10-19 19:40:20 -05:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
selected := available.Newest()
|
|
|
|
|
|
|
|
helperSource := auth.HelperProgramCredentialsSource(selected.Path, helperConfig.Args...)
|
|
|
|
creds = auth.Credentials{
|
|
|
|
creds,
|
|
|
|
auth.CachingCredentialsSource(helperSource), // cached because external operation may be slow/expensive
|
|
|
|
}
|
|
|
|
|
|
|
|
// There should only be zero or one "credentials_helper" blocks. We
|
|
|
|
// assume that the config was validated earlier and so we don't check
|
|
|
|
// for extras here.
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
return creds
|
|
|
|
}
|