mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-26 08:51:02 -06:00
462 lines
11 KiB
Go
462 lines
11 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"os/signal"
|
|
|
|
"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/mitchellh/cli"
|
|
|
|
"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(
|
|
ctx context.Context,
|
|
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(),
|
|
CallerContext: ctx,
|
|
|
|
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
|
|
},
|
|
}
|
|
|
|
if meta.AllowExperimentalFeatures {
|
|
Commands["cloud"] = func() (cli.Command, error) {
|
|
return &command.CloudCommand{
|
|
Meta: meta,
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
PrimaryCommands = []string{
|
|
"init",
|
|
"validate",
|
|
"plan",
|
|
"apply",
|
|
"destroy",
|
|
}
|
|
|
|
HiddenCommands = map[string]struct{}{
|
|
"env": {},
|
|
"internal-plugin": {},
|
|
"push": {},
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
}
|