| package main |
| |
| import ( |
| "os" |
| "os/signal" |
| |
| "github.com/mitchellh/cli" |
| |
| "github.com/hashicorp/go-plugin" |
| svchost "github.com/hashicorp/terraform-svchost" |
| "github.com/hashicorp/terraform-svchost/auth" |
| "github.com/hashicorp/terraform-svchost/disco" |
| "github.com/hashicorp/terraform/internal/addrs" |
| "github.com/hashicorp/terraform/internal/command" |
| "github.com/hashicorp/terraform/internal/command/cliconfig" |
| "github.com/hashicorp/terraform/internal/command/views" |
| "github.com/hashicorp/terraform/internal/command/webbrowser" |
| "github.com/hashicorp/terraform/internal/getproviders" |
| pluginDiscovery "github.com/hashicorp/terraform/internal/plugin/discovery" |
| "github.com/hashicorp/terraform/internal/terminal" |
| ) |
| |
| // 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" |
| |
| // Commands is the mapping of all the available Terraform commands. |
| var Commands map[string]cli.CommandFactory |
| |
| // PrimaryCommands is an ordered sequence of the top-level commands (not |
| // subcommands) that we emphasize at the top of our help output. This is |
| // ordered so that we can show them in the typical workflow order, rather |
| // than in alphabetical order. Anything not in this sequence or in the |
| // HiddenCommands set appears under "all other commands". |
| var PrimaryCommands []string |
| |
| // HiddenCommands is a set of top-level commands (not subcommands) that are |
| // not advertised in the top-level help at all. This is typically because |
| // they are either just stubs that return an error message about something |
| // no longer being supported or backward-compatibility aliases for other |
| // commands. |
| // |
| // No commands in the PrimaryCommands sequence should also appear in the |
| // HiddenCommands set, because that would be rather silly. |
| var HiddenCommands map[string]struct{} |
| |
| // Ui is the cli.Ui used for communicating to the outside world. |
| var Ui cli.Ui |
| |
| func initCommands( |
| originalWorkingDir string, |
| streams *terminal.Streams, |
| config *cliconfig.Config, |
| services *disco.Disco, |
| providerSrc getproviders.Source, |
| providerDevOverrides map[addrs.Provider]getproviders.PackageLocalDir, |
| unmanagedProviders map[addrs.Provider]*plugin.ReattachConfig, |
| ) { |
| var inAutomation bool |
| if v := os.Getenv(runningInAutomationEnvName); v != "" { |
| inAutomation = true |
| } |
| |
| 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) |
| } |
| |
| configDir, err := cliconfig.ConfigDir() |
| if err != nil { |
| configDir = "" // No config dir available (e.g. looking up a home directory failed) |
| } |
| |
| wd := WorkingDir(originalWorkingDir, os.Getenv("TF_DATA_DIR")) |
| |
| meta := command.Meta{ |
| WorkingDir: wd, |
| Streams: streams, |
| View: views.NewView(streams).SetRunningInAutomation(inAutomation), |
| |
| Color: true, |
| GlobalPluginDirs: globalPluginDirs(), |
| Ui: Ui, |
| |
| Services: services, |
| BrowserLauncher: webbrowser.NewNativeLauncher(), |
| |
| RunningInAutomation: inAutomation, |
| CLIConfigDir: configDir, |
| PluginCacheDir: config.PluginCacheDir, |
| |
| PluginCacheMayBreakDependencyLockFile: config.PluginCacheMayBreakDependencyLockFile, |
| |
| ShutdownCh: makeShutdownCh(), |
| |
| ProviderSource: providerSrc, |
| ProviderDevOverrides: providerDevOverrides, |
| UnmanagedProviders: unmanagedProviders, |
| |
| AllowExperimentalFeatures: ExperimentsAllowed(), |
| } |
| |
| // The command list is included in the terraform -help |
| // output, which is in turn included in the docs at |
| // website/docs/cli/commands/index.html.markdown; if you |
| // add, remove or reclassify commands then consider updating |
| // that to match. |
| |
| Commands = map[string]cli.CommandFactory{ |
| "apply": func() (cli.Command, error) { |
| return &command.ApplyCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "console": func() (cli.Command, error) { |
| return &command.ConsoleCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "destroy": func() (cli.Command, error) { |
| return &command.ApplyCommand{ |
| Meta: meta, |
| Destroy: true, |
| }, nil |
| }, |
| |
| "env": func() (cli.Command, error) { |
| return &command.WorkspaceCommand{ |
| Meta: meta, |
| LegacyName: true, |
| }, nil |
| }, |
| |
| "env list": func() (cli.Command, error) { |
| return &command.WorkspaceListCommand{ |
| Meta: meta, |
| LegacyName: true, |
| }, nil |
| }, |
| |
| "env select": func() (cli.Command, error) { |
| return &command.WorkspaceSelectCommand{ |
| Meta: meta, |
| LegacyName: true, |
| }, nil |
| }, |
| |
| "env new": func() (cli.Command, error) { |
| return &command.WorkspaceNewCommand{ |
| Meta: meta, |
| LegacyName: true, |
| }, nil |
| }, |
| |
| "env delete": func() (cli.Command, error) { |
| return &command.WorkspaceDeleteCommand{ |
| Meta: meta, |
| LegacyName: true, |
| }, nil |
| }, |
| |
| "fmt": func() (cli.Command, error) { |
| return &command.FmtCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "get": func() (cli.Command, error) { |
| return &command.GetCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "graph": func() (cli.Command, error) { |
| return &command.GraphCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "import": func() (cli.Command, error) { |
| return &command.ImportCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "init": func() (cli.Command, error) { |
| return &command.InitCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "login": func() (cli.Command, error) { |
| return &command.LoginCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "logout": func() (cli.Command, error) { |
| return &command.LogoutCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "metadata": func() (cli.Command, error) { |
| return &command.MetadataCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "metadata functions": func() (cli.Command, error) { |
| return &command.MetadataFunctionsCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "output": func() (cli.Command, error) { |
| return &command.OutputCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "plan": func() (cli.Command, error) { |
| return &command.PlanCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "providers": func() (cli.Command, error) { |
| return &command.ProvidersCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "providers lock": func() (cli.Command, error) { |
| return &command.ProvidersLockCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "providers mirror": func() (cli.Command, error) { |
| return &command.ProvidersMirrorCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "providers schema": func() (cli.Command, error) { |
| return &command.ProvidersSchemaCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "push": func() (cli.Command, error) { |
| return &command.PushCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "refresh": func() (cli.Command, error) { |
| return &command.RefreshCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "show": func() (cli.Command, error) { |
| return &command.ShowCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "taint": func() (cli.Command, error) { |
| return &command.TaintCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "test": func() (cli.Command, error) { |
| return &command.TestCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "validate": func() (cli.Command, error) { |
| return &command.ValidateCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "version": func() (cli.Command, error) { |
| return &command.VersionCommand{ |
| Meta: meta, |
| Version: Version, |
| VersionPrerelease: VersionPrerelease, |
| Platform: getproviders.CurrentPlatform, |
| CheckFunc: commandVersionCheck, |
| }, nil |
| }, |
| |
| "untaint": func() (cli.Command, error) { |
| return &command.UntaintCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "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 |
| }, |
| |
| "workspace show": func() (cli.Command, error) { |
| return &command.WorkspaceShowCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "workspace new": func() (cli.Command, error) { |
| return &command.WorkspaceNewCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "workspace delete": func() (cli.Command, error) { |
| return &command.WorkspaceDeleteCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| //----------------------------------------------------------- |
| // Plumbing |
| //----------------------------------------------------------- |
| |
| "force-unlock": func() (cli.Command, error) { |
| return &command.UnlockCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "state": func() (cli.Command, error) { |
| return &command.StateCommand{}, nil |
| }, |
| |
| "state list": func() (cli.Command, error) { |
| return &command.StateListCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "state rm": func() (cli.Command, error) { |
| return &command.StateRmCommand{ |
| StateMeta: command.StateMeta{ |
| Meta: meta, |
| }, |
| }, nil |
| }, |
| |
| "state mv": func() (cli.Command, error) { |
| return &command.StateMvCommand{ |
| StateMeta: command.StateMeta{ |
| Meta: meta, |
| }, |
| }, nil |
| }, |
| |
| "state pull": func() (cli.Command, error) { |
| return &command.StatePullCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "state push": func() (cli.Command, error) { |
| return &command.StatePushCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "state show": func() (cli.Command, error) { |
| return &command.StateShowCommand{ |
| Meta: meta, |
| }, nil |
| }, |
| |
| "state replace-provider": func() (cli.Command, error) { |
| return &command.StateReplaceProviderCommand{ |
| StateMeta: command.StateMeta{ |
| Meta: meta, |
| }, |
| }, nil |
| }, |
| } |
| |
| PrimaryCommands = []string{ |
| "init", |
| "validate", |
| "plan", |
| "apply", |
| "destroy", |
| } |
| |
| HiddenCommands = map[string]struct{}{ |
| "env": struct{}{}, |
| "internal-plugin": struct{}{}, |
| "push": struct{}{}, |
| } |
| |
| } |
| |
| // 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) |
| signal.Notify(signalCh, ignoreSignals...) |
| signal.Notify(signalCh, forwardSignals...) |
| go func() { |
| for { |
| <-signalCh |
| resultCh <- struct{}{} |
| } |
| }() |
| |
| return resultCh |
| } |
| |
| func credentialsSource(config *cliconfig.Config) (auth.CredentialsSource, error) { |
| helperPlugins := pluginDiscovery.FindPlugins("credentials", globalPluginDirs()) |
| return config.CredentialsSource(helperPlugins) |
| } |