mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 18:01:01 -06:00
49e2e00231
This new command is intended to make it easy to create or update a mirror directory containing suitable providers for the current configuration, producing a layout that is appropriate both for a filesystem mirror or, if copied into the document root of an HTTP server, a network mirror. This initial version is not customizable aside from being able to select multiple platforms to install packages for. Future iterations of this could include commands to turn the JSON index generation on and off, or to instruct it to produce the unpacked directory layout instead of the packed directory layout as it currently does. Both of those options would make the generated directory unsuitable to be a network mirror, but it would still work as a filesystem mirror. In the long run this will hopefully form part of a replacement workflow to terraform-bundle as a way to put copies of providers somewhere so we don't need to re-download them every time, but some other changes will be needed outside of just this command before that'd be true, such as adding support for network and/or filesystem mirrors in Terraform Enterprise.
419 lines
9.5 KiB
Go
419 lines
9.5 KiB
Go
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/addrs"
|
|
"github.com/hashicorp/terraform/command"
|
|
"github.com/hashicorp/terraform/command/cliconfig"
|
|
"github.com/hashicorp/terraform/command/webbrowser"
|
|
"github.com/hashicorp/terraform/internal/getproviders"
|
|
pluginDiscovery "github.com/hashicorp/terraform/plugin/discovery"
|
|
)
|
|
|
|
// 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
|
|
var PlumbingCommands map[string]struct{}
|
|
|
|
// Ui is the cli.Ui used for communicating to the outside world.
|
|
var Ui cli.Ui
|
|
|
|
// PluginOverrides is set from wrappedMain during configuration processing
|
|
// and then eventually passed to the "command" package to specify alternative
|
|
// plugin locations via the legacy configuration file mechanism.
|
|
var PluginOverrides command.PluginOverrides
|
|
|
|
const (
|
|
ErrorPrefix = "e:"
|
|
OutputPrefix = "o:"
|
|
)
|
|
|
|
func initCommands(config *cliconfig.Config, services *disco.Disco, providerSrc getproviders.Source, 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)
|
|
}
|
|
|
|
dataDir := os.Getenv("TF_DATA_DIR")
|
|
|
|
meta := command.Meta{
|
|
Color: true,
|
|
GlobalPluginDirs: globalPluginDirs(),
|
|
PluginOverrides: &PluginOverrides,
|
|
Ui: Ui,
|
|
|
|
Services: services,
|
|
ProviderSource: providerSrc,
|
|
BrowserLauncher: webbrowser.NewNativeLauncher(),
|
|
|
|
RunningInAutomation: inAutomation,
|
|
CLIConfigDir: configDir,
|
|
PluginCacheDir: config.PluginCacheDir,
|
|
OverrideDataDir: dataDir,
|
|
|
|
ShutdownCh: makeShutdownCh(),
|
|
UnmanagedProviders: unmanagedProviders,
|
|
}
|
|
|
|
// 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.
|
|
|
|
PlumbingCommands = map[string]struct{}{
|
|
"state": struct{}{}, // includes all subcommands
|
|
"debug": struct{}{}, // includes all subcommands
|
|
"force-unlock": struct{}{},
|
|
"push": struct{}{},
|
|
"0.12upgrade": struct{}{},
|
|
"0.13upgrade": struct{}{},
|
|
}
|
|
|
|
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
|
|
},
|
|
|
|
"internal-plugin": func() (cli.Command, error) {
|
|
return &command.InternalPluginCommand{
|
|
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
|
|
},
|
|
|
|
"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 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
|
|
},
|
|
|
|
"validate": func() (cli.Command, error) {
|
|
return &command.ValidateCommand{
|
|
Meta: meta,
|
|
}, nil
|
|
},
|
|
|
|
"version": func() (cli.Command, error) {
|
|
return &command.VersionCommand{
|
|
Meta: meta,
|
|
Revision: GitCommit,
|
|
Version: Version,
|
|
VersionPrerelease: VersionPrerelease,
|
|
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
|
|
//-----------------------------------------------------------
|
|
|
|
"0.12upgrade": func() (cli.Command, error) {
|
|
return &command.ZeroTwelveUpgradeCommand{
|
|
Meta: meta,
|
|
}, nil
|
|
},
|
|
|
|
"0.13upgrade": func() (cli.Command, error) {
|
|
return &command.ZeroThirteenUpgradeCommand{
|
|
Meta: meta,
|
|
}, nil
|
|
},
|
|
|
|
"debug": func() (cli.Command, error) {
|
|
return &command.DebugCommand{
|
|
Meta: meta,
|
|
}, nil
|
|
},
|
|
|
|
"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
|
|
},
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
}
|