2014-07-13 05:21:46 +02:00
package command
import (
2017-02-28 19:13:03 +01:00
"bytes"
2018-02-10 00:51:29 +01:00
"context"
2017-03-29 22:45:25 +02:00
"errors"
2014-07-18 20:37:27 +02:00
"flag"
2014-07-13 05:37:30 +02:00
"fmt"
2016-11-22 00:05:49 +01:00
"io/ioutil"
2016-11-02 18:30:28 +01:00
"log"
2014-07-13 05:37:30 +02:00
"os"
2014-09-22 20:15:27 +02:00
"path/filepath"
2015-04-30 16:59:14 +02:00
"strconv"
2017-01-19 05:50:45 +01:00
"strings"
2016-11-22 00:05:49 +01:00
"time"
2014-07-13 05:37:30 +02:00
command: Unmanaged providers
This adds supports for "unmanaged" providers, or providers with process
lifecycles not controlled by Terraform. These providers are assumed to
be started before Terraform is launched, and are assumed to shut
themselves down after Terraform has finished running.
To do this, we must update the go-plugin dependency to v1.3.0, which
added support for the "test mode" plugin serving that powers all this.
As a side-effect of not needing to manage the process lifecycle anymore,
Terraform also no longer needs to worry about the provider's binary, as
it won't be used for anything anymore. Because of this, we can disable
the init behavior that concerns itself with downloading that provider's
binary, checking its version, and otherwise managing the binary.
This is all managed on a per-provider basis, so managed providers that
Terraform downloads, starts, and stops can be used in the same commands
as unmanaged providers. The TF_REATTACH_PROVIDERS environment variable
is added, and is a JSON encoding of the provider's address to the
information we need to connect to it.
This change enables two benefits: first, delve and other debuggers can
now be attached to provider server processes, and Terraform can connect.
This allows for attaching debuggers to provider processes, which before
was difficult to impossible. Second, it allows the SDK test framework to
host the provider in the same process as the test driver, while running
a production Terraform binary against the provider. This allows for Go's
built-in race detector and test coverage tooling to work as expected in
provider tests.
Unmanaged providers are expected to work in the exact same way as
managed providers, with one caveat: Terraform kills provider processes
and restarts them once per graph walk, meaning multiple times during
most Terraform CLI commands. As unmanaged providers can't be killed by
Terraform, and have no visibility into graph walks, unmanaged providers
are likely to have differences in how their global mutable state behaves
when compared to managed providers. Namely, unmanaged providers are
likely to retain global state when managed providers would have reset
it. Developers relying on global state should be aware of this.
2020-05-27 02:48:57 +02:00
plugin "github.com/hashicorp/go-plugin"
2019-10-11 11:34:26 +02:00
"github.com/hashicorp/terraform-svchost/disco"
terraform: ugly huge change to weave in new HCL2-oriented types
Due to how deeply the configuration types go into Terraform Core, there
isn't a great way to switch out to HCL2 gradually. As a consequence, this
huge commit gets us from the old state to a _compilable_ new state, but
does not yet attempt to fix any tests and has a number of known missing
parts and bugs. We will continue to iterate on this in forthcoming
commits, heading back towards passing tests and making Terraform
fully-functional again.
The three main goals here are:
- Use the configuration models from the "configs" package instead of the
older models in the "config" package, which is now deprecated and
preserved only to help us write our migration tool.
- Do expression inspection and evaluation using the functionality of the
new "lang" package, instead of the Interpolator type and related
functionality in the main "terraform" package.
- Represent addresses of various objects using types in the addrs package,
rather than hand-constructed strings. This is not critical to support
the above, but was a big help during the implementation of these other
points since it made it much more explicit what kind of address is
expected in each context.
Since our new packages are built to accommodate some future planned
features that are not yet implemented (e.g. the "for_each" argument on
resources, "count"/"for_each" on modules), and since there's still a fair
amount of functionality still using old-style APIs, there is a moderate
amount of shimming here to connect new assumptions with old, hopefully in
a way that makes it easier to find and eliminate these shims later.
I apologize in advance to the person who inevitably just found this huge
commit while spelunking through the commit history.
2018-04-30 19:33:53 +02:00
"github.com/hashicorp/terraform/addrs"
2017-02-28 19:13:03 +01:00
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/backend/local"
2017-10-05 20:59:08 +02:00
"github.com/hashicorp/terraform/command/format"
2019-08-30 00:50:03 +02:00
"github.com/hashicorp/terraform/command/webbrowser"
2018-03-01 02:09:48 +01:00
"github.com/hashicorp/terraform/configs/configload"
2016-10-26 21:46:22 +02:00
"github.com/hashicorp/terraform/helper/experiment"
2016-11-14 09:32:01 +01:00
"github.com/hashicorp/terraform/helper/wrappedstreams"
2020-01-17 02:42:41 +01:00
"github.com/hashicorp/terraform/internal/getproviders"
2018-08-25 01:13:50 +02:00
"github.com/hashicorp/terraform/providers"
2018-09-29 18:34:23 +02:00
"github.com/hashicorp/terraform/provisioners"
2014-07-13 05:37:30 +02:00
"github.com/hashicorp/terraform/terraform"
2017-10-05 20:59:08 +02:00
"github.com/hashicorp/terraform/tfdiags"
2014-07-13 05:37:30 +02:00
"github.com/mitchellh/cli"
2014-07-13 05:21:46 +02:00
"github.com/mitchellh/colorstring"
)
// Meta are the meta-options that are available on all or most commands.
type Meta struct {
2017-01-19 05:50:45 +01:00
// The exported fields below should be set by anyone using a
// command with a Meta field. These are expected to be set externally
// (not from within the command itself).
2014-07-28 07:58:35 +02:00
main: new global option -chdir
This new option is intended to address the previous inconsistencies where
some older subcommands supported partially changing the target directory
(where Terraform would use the new directory inconsistently) where newer
commands did not support that override at all.
Instead, now Terraform will accept a -chdir command at the start of the
command line (before the subcommand) and will interpret it as a request
to direct all actions that would normally be taken in the current working
directory into the target directory instead. This is similar to options
offered by some other similar tools, such as the -C option in "make".
The new option is only accepted at the start of the command line (before
the subcommand) as a way to reflect that it is a global command (not
specific to a particular subcommand) and that it takes effect _before_
executing the subcommand. This also means it'll be forced to appear before
any other command-specific arguments that take file paths, which hopefully
communicates that those other arguments are interpreted relative to the
overridden path.
As a measure of pragmatism for existing uses, the path.cwd object in
the Terraform language will continue to return the _original_ working
directory (ignoring -chdir), in case that is important in some exceptional
workflows. The path.root object gives the root module directory, which
will always match the overriden working directory unless the user
simultaneously uses one of the legacy directory override arguments, which
is not a pattern we intend to support in the long run.
As a first step down the deprecation path, this commit adjusts the
documentation to de-emphasize the inconsistent old command line arguments,
including specific guidance on what to use instead for the main three
workflow commands, but all of those options remain supported in the same
way as they were before. In a later commit we'll make those arguments
produce a visible deprecation warning in Terraform's output, and then
in an even later commit we'll remove them entirely so that -chdir is the
single supported way to run Terraform from a directory other than the
one containing the root module configuration.
2020-09-02 00:45:12 +02:00
// OriginalWorkingDir, if set, is the actual working directory where
// Terraform was run from. This might not be the _actual_ current working
// directory, because users can add the -chdir=... option to the beginning
// of their command line to ask Terraform to switch.
//
// Most things should just use the current working directory in order to
// respect the user's override, but we retain this for exceptional
// situations where we need to refer back to the original working directory
// for some reason.
OriginalWorkingDir string
2017-04-14 03:05:58 +02:00
Color bool // True if output should be colored
GlobalPluginDirs [ ] string // Additional paths to search for plugins
PluginOverrides * PluginOverrides // legacy overrides from .terraformrc file
Ui cli . Ui // Ui for output
2014-07-13 05:59:16 +02:00
2017-01-19 05:50:45 +01:00
// ExtraHooks are extra hooks to add to the context.
ExtraHooks [ ] terraform . Hook
2014-07-18 00:14:26 +02:00
2017-10-18 17:52:13 +02:00
// Services provides access to remote endpoint information for
// "terraform-native' services running at a specific user-facing hostname.
Services * disco . Disco
2017-09-09 02:14:37 +02:00
// RunningInAutomation indicates that commands are being run by an
// automated system rather than directly at a command prompt.
//
// This is a hint to various command routines that it may be confusing
// to print out messages that suggest running specific follow-up
// commands, since the user consuming the output will not be
// in a position to run such commands.
//
// The intended use-case of this flag is when Terraform is running in
// some sort of workflow orchestration tool which is abstracting away
// the specific commands being run.
RunningInAutomation bool
2019-07-09 21:06:20 +02:00
// CLIConfigDir is the directory from which CLI configuration files were
// read by the caller and the directory where any changes to CLI
// configuration files by commands should be made.
//
// If this is empty then no configuration directory is available and
// commands which require one cannot proceed.
CLIConfigDir string
2017-09-02 01:05:05 +02:00
// PluginCacheDir, if non-empty, enables caching of downloaded plugins
// into the given directory.
PluginCacheDir string
2020-01-17 02:42:41 +01:00
// ProviderSource allows determining the available versions of a provider
// and determines where a distribution package for a particular
// provider version can be obtained.
ProviderSource getproviders . Source
2017-11-02 00:44:03 +01:00
// OverrideDataDir, if non-empty, overrides the return value of the
// DataDir method for situations where the local .terraform/ directory
// is not suitable, e.g. because of a read-only filesystem.
OverrideDataDir string
2019-08-30 00:50:03 +02:00
// BrowserLauncher is used by commands that need to open a URL in a
// web browser.
BrowserLauncher webbrowser . Launcher
2017-12-01 17:03:41 +01:00
// When this channel is closed, the command will be cancelled.
ShutdownCh <- chan struct { }
command: Unmanaged providers
This adds supports for "unmanaged" providers, or providers with process
lifecycles not controlled by Terraform. These providers are assumed to
be started before Terraform is launched, and are assumed to shut
themselves down after Terraform has finished running.
To do this, we must update the go-plugin dependency to v1.3.0, which
added support for the "test mode" plugin serving that powers all this.
As a side-effect of not needing to manage the process lifecycle anymore,
Terraform also no longer needs to worry about the provider's binary, as
it won't be used for anything anymore. Because of this, we can disable
the init behavior that concerns itself with downloading that provider's
binary, checking its version, and otherwise managing the binary.
This is all managed on a per-provider basis, so managed providers that
Terraform downloads, starts, and stops can be used in the same commands
as unmanaged providers. The TF_REATTACH_PROVIDERS environment variable
is added, and is a JSON encoding of the provider's address to the
information we need to connect to it.
This change enables two benefits: first, delve and other debuggers can
now be attached to provider server processes, and Terraform can connect.
This allows for attaching debuggers to provider processes, which before
was difficult to impossible. Second, it allows the SDK test framework to
host the provider in the same process as the test driver, while running
a production Terraform binary against the provider. This allows for Go's
built-in race detector and test coverage tooling to work as expected in
provider tests.
Unmanaged providers are expected to work in the exact same way as
managed providers, with one caveat: Terraform kills provider processes
and restarts them once per graph walk, meaning multiple times during
most Terraform CLI commands. As unmanaged providers can't be killed by
Terraform, and have no visibility into graph walks, unmanaged providers
are likely to have differences in how their global mutable state behaves
when compared to managed providers. Namely, unmanaged providers are
likely to retain global state when managed providers would have reset
it. Developers relying on global state should be aware of this.
2020-05-27 02:48:57 +02:00
// UnmanagedProviders are a set of providers that exist as processes predating Terraform, which Terraform should use but not worry about the lifecycle of.
UnmanagedProviders map [ addrs . Provider ] * plugin . ReattachConfig
2017-01-19 05:50:45 +01:00
//----------------------------------------------------------
// Protected: commands can set these
//----------------------------------------------------------
2017-06-14 21:14:26 +02:00
// Modify the data directory location. This should be accessed through the
// DataDir method.
2014-10-13 21:05:28 +02:00
dataDir string
2017-06-15 20:26:12 +02:00
// pluginPath is a user defined set of directories to look for plugins.
// This is set during init with the `-plugin-dir` flag, saved to a file in
// the data directory.
2017-07-03 19:59:13 +02:00
// This overrides all other search paths when discovering plugins.
2017-06-15 20:26:12 +02:00
pluginPath [ ] string
2017-07-03 19:59:13 +02:00
ignorePluginChecksum bool
2017-04-14 03:05:58 +02:00
// Override certain behavior for tests within this package
testingOverrides * testingOverrides
2017-01-19 05:50:45 +01:00
//----------------------------------------------------------
// Private: do not set these
//----------------------------------------------------------
2018-03-01 02:09:48 +01:00
// configLoader is a shared configuration loader that is used by
// LoadConfig and other commands that access configuration files.
// It is initialized on first use.
configLoader * configload . Loader
2017-01-19 05:50:45 +01:00
// backendState is the currently active backend state
backendState * terraform . BackendState
2014-07-18 20:37:27 +02:00
// Variables for the context (private)
terraform: ugly huge change to weave in new HCL2-oriented types
Due to how deeply the configuration types go into Terraform Core, there
isn't a great way to switch out to HCL2 gradually. As a consequence, this
huge commit gets us from the old state to a _compilable_ new state, but
does not yet attempt to fix any tests and has a number of known missing
parts and bugs. We will continue to iterate on this in forthcoming
commits, heading back towards passing tests and making Terraform
fully-functional again.
The three main goals here are:
- Use the configuration models from the "configs" package instead of the
older models in the "config" package, which is now deprecated and
preserved only to help us write our migration tool.
- Do expression inspection and evaluation using the functionality of the
new "lang" package, instead of the Interpolator type and related
functionality in the main "terraform" package.
- Represent addresses of various objects using types in the addrs package,
rather than hand-constructed strings. This is not critical to support
the above, but was a big help during the implementation of these other
points since it made it much more explicit what kind of address is
expected in each context.
Since our new packages are built to accommodate some future planned
features that are not yet implemented (e.g. the "for_each" argument on
resources, "count"/"for_each" on modules), and since there's still a fair
amount of functionality still using old-style APIs, there is a moderate
amount of shimming here to connect new assumptions with old, hopefully in
a way that makes it easier to find and eliminate these shims later.
I apologize in advance to the person who inevitably just found this huge
commit while spelunking through the commit history.
2018-04-30 19:33:53 +02:00
variableArgs rawFlags
input bool
2014-07-18 20:37:27 +02:00
2015-03-24 17:18:15 +01:00
// Targets for this context (private)
terraform: ugly huge change to weave in new HCL2-oriented types
Due to how deeply the configuration types go into Terraform Core, there
isn't a great way to switch out to HCL2 gradually. As a consequence, this
huge commit gets us from the old state to a _compilable_ new state, but
does not yet attempt to fix any tests and has a number of known missing
parts and bugs. We will continue to iterate on this in forthcoming
commits, heading back towards passing tests and making Terraform
fully-functional again.
The three main goals here are:
- Use the configuration models from the "configs" package instead of the
older models in the "config" package, which is now deprecated and
preserved only to help us write our migration tool.
- Do expression inspection and evaluation using the functionality of the
new "lang" package, instead of the Interpolator type and related
functionality in the main "terraform" package.
- Represent addresses of various objects using types in the addrs package,
rather than hand-constructed strings. This is not critical to support
the above, but was a big help during the implementation of these other
points since it made it much more explicit what kind of address is
expected in each context.
Since our new packages are built to accommodate some future planned
features that are not yet implemented (e.g. the "for_each" argument on
resources, "count"/"for_each" on modules), and since there's still a fair
amount of functionality still using old-style APIs, there is a moderate
amount of shimming here to connect new assumptions with old, hopefully in
a way that makes it easier to find and eliminate these shims later.
I apologize in advance to the person who inevitably just found this huge
commit while spelunking through the commit history.
2018-04-30 19:33:53 +02:00
targets [ ] addrs . Targetable
2015-03-24 17:18:15 +01:00
2017-01-19 05:50:45 +01:00
// Internal fields
2014-07-17 18:34:32 +02:00
color bool
2014-07-13 05:59:16 +02:00
oldUi cli . Ui
2014-10-12 03:05:23 +02:00
2015-02-22 03:17:40 +01:00
// The fields below are expected to be set by the command via
// command line flags. See the Apply command for an example.
//
2014-10-12 03:05:23 +02:00
// statePath is the path to the state file. If this is empty, then
// no state will be loaded. It is also okay for this to be a path to
// a file that doesn't exist; it is assumed that this means that there
// is simply no state.
2015-02-22 03:17:40 +01:00
//
2014-10-12 03:05:23 +02:00
// stateOutPath is used to override the output path for the state.
// If not provided, the StatePath is used causing the old state to
2019-03-07 21:07:13 +01:00
// be overridden.
2015-02-22 03:17:40 +01:00
//
2014-10-12 03:05:23 +02:00
// backupPath is used to backup the state file before writing a modified
2015-09-11 20:56:20 +02:00
// version. It defaults to stateOutPath + DefaultBackupExtension
2015-05-06 17:58:42 +02:00
//
// parallelism is used to control the number of concurrent operations
// allowed when walking the graph
2016-10-21 23:25:05 +02:00
//
2016-11-23 10:44:52 +01:00
// provider is to specify specific resource providers
2017-02-02 00:16:16 +01:00
//
2017-03-21 20:05:51 +01:00
// stateLock is set to false to disable state locking
//
2017-04-01 21:42:13 +02:00
// stateLockTimeout is the optional duration to retry a state locks locks
// when it is already locked by another process.
//
2017-03-21 20:05:51 +01:00
// forceInitCopy suppresses confirmation for copying state data during
// init.
2017-04-20 23:26:50 +02:00
//
// reconfigure forces init to ignore any stored configuration.
2019-12-10 20:06:06 +01:00
//
// compactWarnings (-compact-warnings) selects a more compact presentation
// of warnings in the output when they are not accompanied by errors.
2017-04-01 21:42:13 +02:00
statePath string
stateOutPath string
backupPath string
parallelism int
provider string
stateLock bool
stateLockTimeout time . Duration
forceInitCopy bool
2017-04-20 23:26:50 +02:00
reconfigure bool
2019-12-10 20:06:06 +01:00
compactWarnings bool
2017-06-12 19:30:19 +02:00
2017-09-18 20:41:30 +02:00
// Used with the import command to allow import of state when no matching config exists.
allowMissingConfig bool
2014-10-12 03:05:23 +02:00
}
2017-04-14 03:05:58 +02:00
type PluginOverrides struct {
Providers map [ string ] string
Provisioners map [ string ] string
}
type testingOverrides struct {
2020-03-31 00:30:56 +02:00
Providers map [ addrs . Provider ] providers . Factory
Provisioners map [ string ] provisioners . Factory
2017-04-14 03:05:58 +02:00
}
2014-10-12 03:05:23 +02:00
// initStatePaths is used to initialize the default values for
// statePath, stateOutPath, and backupPath
func ( m * Meta ) initStatePaths ( ) {
if m . statePath == "" {
m . statePath = DefaultStateFilename
}
if m . stateOutPath == "" {
m . stateOutPath = m . statePath
}
if m . backupPath == "" {
2015-09-11 20:56:20 +02:00
m . backupPath = m . stateOutPath + DefaultBackupExtension
2014-10-12 03:05:23 +02:00
}
2014-07-13 05:21:46 +02:00
}
2014-10-12 03:34:11 +02:00
// StateOutPath returns the true output path for the state file
func ( m * Meta ) StateOutPath ( ) string {
return m . stateOutPath
}
2014-07-13 05:21:46 +02:00
// Colorize returns the colorization structure for a command.
func ( m * Meta ) Colorize ( ) * colorstring . Colorize {
return & colorstring . Colorize {
Colors : colorstring . DefaultColors ,
2014-07-17 18:34:32 +02:00
Disable : ! m . color ,
2014-07-13 05:21:46 +02:00
Reset : true ,
}
}
2015-03-05 05:52:06 +01:00
// DataDir returns the directory where local data will be stored.
2017-11-02 00:44:03 +01:00
// Defaults to DefaultDataDir in the current working directory.
2015-03-05 05:52:06 +01:00
func ( m * Meta ) DataDir ( ) string {
2017-11-02 00:44:03 +01:00
if m . OverrideDataDir != "" {
return m . OverrideDataDir
2015-03-05 05:52:06 +01:00
}
2017-11-02 00:44:03 +01:00
return DefaultDataDir
2015-03-05 05:52:06 +01:00
}
2015-04-30 16:59:14 +02:00
const (
// InputModeEnvVar is the environment variable that, if set to "false" or
// "0", causes terraform commands to behave as if the `-input=false` flag was
// specified.
InputModeEnvVar = "TF_INPUT"
)
2014-10-08 19:29:54 +02:00
// InputMode returns the type of input we should ask for in the form of
// terraform.InputMode which is passed directly to Context.Input.
func ( m * Meta ) InputMode ( ) terraform . InputMode {
if test || ! m . input {
return 0
}
2015-04-30 16:59:14 +02:00
if envVar := os . Getenv ( InputModeEnvVar ) ; envVar != "" {
if v , err := strconv . ParseBool ( envVar ) ; err == nil {
if ! v {
return 0
}
}
}
2014-10-08 19:29:54 +02:00
var mode terraform . InputMode
mode |= terraform . InputModeProvider
return mode
2014-09-29 20:11:35 +02:00
}
2015-02-22 01:04:32 +01:00
// UIInput returns a UIInput object to be used for asking for input.
func ( m * Meta ) UIInput ( ) terraform . UIInput {
return & UIInput {
Colorize : m . Colorize ( ) ,
2014-10-12 03:21:20 +02:00
}
}
2016-11-14 07:18:18 +01:00
// StdinPiped returns true if the input is piped.
func ( m * Meta ) StdinPiped ( ) bool {
2016-11-14 09:32:01 +01:00
fi , err := wrappedstreams . Stdin ( ) . Stat ( )
2016-11-14 07:18:18 +01:00
if err != nil {
// If there is an error, let's just say its not piped
return false
}
return fi . Mode ( ) & os . ModeNamedPipe != 0
}
2018-03-28 00:31:05 +02:00
// RunOperation executes the given operation on the given backend, blocking
2019-07-15 15:32:35 +02:00
// until that operation completes or is interrupted, and then returns
2018-03-28 00:31:05 +02:00
// the RunningOperation object representing the completed or
// aborted operation that is, despite the name, no longer running.
//
// An error is returned if the operation either fails to start or is cancelled.
// If the operation runs to completion then no error is returned even if the
// operation itself is unsuccessful. Use the "Result" field of the
// returned operation object to recognize operation-level failure.
2018-02-10 00:51:29 +01:00
func ( m * Meta ) RunOperation ( b backend . Enhanced , opReq * backend . Operation ) ( * backend . RunningOperation , error ) {
2018-12-19 21:12:24 +01:00
if opReq . ConfigDir != "" {
opReq . ConfigDir = m . normalizePath ( opReq . ConfigDir )
}
2018-02-10 00:51:29 +01:00
op , err := b . Operation ( context . Background ( ) , opReq )
if err != nil {
return nil , fmt . Errorf ( "error starting operation: %s" , err )
}
// Wait for the operation to complete or an interrupt to occur
select {
case <- m . ShutdownCh :
// gracefully stop the operation
op . Stop ( )
// Notify the user
m . Ui . Output ( outputInterrupt )
// Still get the result, since there is still one
select {
case <- m . ShutdownCh :
m . Ui . Error (
"Two interrupts received. Exiting immediately. Note that data\n" +
"loss may have occurred." )
// cancel the operation completely
op . Cancel ( )
// the operation should return asap
// but timeout just in case
select {
case <- op . Done ( ) :
case <- time . After ( 5 * time . Second ) :
}
return nil , errors . New ( "operation canceled" )
case <- op . Done ( ) :
// operation completed after Stop
}
case <- op . Done ( ) :
// operation completed normally
}
return op , nil
}
2017-07-03 19:59:13 +02:00
const (
ProviderSkipVerifyEnvVar = "TF_SKIP_PROVIDER_VERIFY"
)
2014-07-13 05:37:30 +02:00
// contextOpts returns the options to use to initialize a Terraform
// context with the settings from this Meta.
2020-06-16 18:23:15 +02:00
func ( m * Meta ) contextOpts ( ) ( * terraform . ContextOpts , error ) {
workspace , err := m . Workspace ( )
if err != nil {
return nil , err
}
2017-01-19 05:50:45 +01:00
var opts terraform . ContextOpts
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 23:24:45 +02:00
opts . Hooks = [ ] terraform . Hook { m . uiHook ( ) }
2017-01-19 05:50:45 +01:00
opts . Hooks = append ( opts . Hooks , m . ExtraHooks ... )
2014-07-18 20:37:27 +02:00
2015-03-24 17:18:15 +01:00
opts . Targets = m . targets
2014-10-01 07:01:11 +02:00
opts . UIInput = m . UIInput ( )
2017-01-19 05:50:45 +01:00
opts . Parallelism = m . parallelism
2014-07-18 20:37:27 +02:00
2017-04-14 03:05:58 +02:00
// If testingOverrides are set, we'll skip the plugin discovery process
// and just work with what we've been given, thus allowing the tests
// to provide mock providers and provisioners.
if m . testingOverrides != nil {
2020-03-31 00:30:56 +02:00
opts . Providers = m . testingOverrides . Providers
2017-04-14 03:05:58 +02:00
opts . Provisioners = m . testingOverrides . Provisioners
} else {
2020-03-31 00:30:56 +02:00
providerFactories , err := m . providerFactories ( )
if err != nil {
// providerFactories can fail if the plugin selections file is
// invalid in some way, but we don't have any way to report that
// from here so we'll just behave as if no providers are available
// in that case. However, we will produce a warning in case this
// shows up unexpectedly and prompts a bug report.
// This situation shouldn't arise commonly in practice because
// the selections file is generated programmatically.
log . Printf ( "[WARN] Failed to determine selected providers: %s" , err )
providerFactories = nil
}
opts . Providers = providerFactories
2017-04-14 03:05:58 +02:00
opts . Provisioners = m . provisionerFactories ( )
}
2017-06-06 01:37:08 +02:00
opts . ProviderSHA256s = m . providerPluginsLock ( ) . Read ( )
2017-07-03 19:59:13 +02:00
if v := os . Getenv ( ProviderSkipVerifyEnvVar ) ; v != "" {
opts . SkipProviderVerify = true
}
2017-05-25 01:36:19 +02:00
2017-03-14 00:25:27 +01:00
opts . Meta = & terraform . ContextMeta {
main: new global option -chdir
This new option is intended to address the previous inconsistencies where
some older subcommands supported partially changing the target directory
(where Terraform would use the new directory inconsistently) where newer
commands did not support that override at all.
Instead, now Terraform will accept a -chdir command at the start of the
command line (before the subcommand) and will interpret it as a request
to direct all actions that would normally be taken in the current working
directory into the target directory instead. This is similar to options
offered by some other similar tools, such as the -C option in "make".
The new option is only accepted at the start of the command line (before
the subcommand) as a way to reflect that it is a global command (not
specific to a particular subcommand) and that it takes effect _before_
executing the subcommand. This also means it'll be forced to appear before
any other command-specific arguments that take file paths, which hopefully
communicates that those other arguments are interpreted relative to the
overridden path.
As a measure of pragmatism for existing uses, the path.cwd object in
the Terraform language will continue to return the _original_ working
directory (ignoring -chdir), in case that is important in some exceptional
workflows. The path.root object gives the root module directory, which
will always match the overriden working directory unless the user
simultaneously uses one of the legacy directory override arguments, which
is not a pattern we intend to support in the long run.
As a first step down the deprecation path, this commit adjusts the
documentation to de-emphasize the inconsistent old command line arguments,
including specific guidance on what to use instead for the main three
workflow commands, but all of those options remain supported in the same
way as they were before. In a later commit we'll make those arguments
produce a visible deprecation warning in Terraform's output, and then
in an even later commit we'll remove them entirely so that -chdir is the
single supported way to run Terraform from a directory other than the
one containing the root module configuration.
2020-09-02 00:45:12 +02:00
Env : workspace ,
OriginalWorkingDir : m . OriginalWorkingDir ,
2017-03-14 00:25:27 +01:00
}
2020-06-16 18:23:15 +02:00
return & opts , nil
2014-07-13 05:37:30 +02:00
}
2018-11-21 15:35:27 +01:00
// defaultFlagSet creates a default flag set for commands.
func ( m * Meta ) defaultFlagSet ( n string ) * flag . FlagSet {
2014-07-18 20:37:27 +02:00
f := flag . NewFlagSet ( n , flag . ContinueOnError )
2019-08-16 14:31:21 +02:00
f . SetOutput ( ioutil . Discard )
2014-09-09 05:56:18 +02:00
2016-03-22 18:41:02 +01:00
// Set the default Usage to empty
f . Usage = func ( ) { }
2018-11-21 15:35:27 +01:00
return f
}
// extendedFlagSet adds custom flags that are mostly used by commands
// that are used to run an operation like plan or apply.
func ( m * Meta ) extendedFlagSet ( n string ) * flag . FlagSet {
f := m . defaultFlagSet ( n )
f . BoolVar ( & m . input , "input" , true , "input" )
f . Var ( ( * FlagTargetSlice ) ( & m . targets ) , "target" , "resource to target" )
2019-12-10 20:06:06 +01:00
f . BoolVar ( & m . compactWarnings , "compact-warnings" , false , "use compact warnings" )
2018-11-21 15:35:27 +01:00
if m . variableArgs . items == nil {
m . variableArgs = newRawFlags ( "-var" )
}
varValues := m . variableArgs . Alias ( "-var" )
varFiles := m . variableArgs . Alias ( "-var-file" )
f . Var ( varValues , "var" , "variables" )
f . Var ( varFiles , "var-file" , "variable file" )
// Experimental features
experiment . Flag ( f )
// commands that bypass locking will supply their own flag on this var,
// but set the initial meta value to true as a failsafe.
2017-03-30 19:54:27 +02:00
m . stateLock = true
2014-07-18 20:37:27 +02:00
return f
}
2014-07-13 05:21:46 +02:00
// process will process the meta-parameters out of the arguments. This
// will potentially modify the args in-place. It will return the resulting
// slice.
2020-04-01 21:01:08 +02:00
func ( m * Meta ) process ( args [ ] string ) [ ] string {
2014-07-13 05:59:16 +02:00
// We do this so that we retain the ability to technically call
// process multiple times, even if we have no plans to do so
if m . oldUi != nil {
m . Ui = m . oldUi
}
2014-07-13 05:21:46 +02:00
2014-07-13 05:59:16 +02:00
// Set colorization
2014-07-17 18:34:32 +02:00
m . color = m . Color
2020-08-13 20:10:09 +02:00
i := 0 // output index
for _ , v := range args {
2014-07-13 05:21:46 +02:00
if v == "-no-color" {
2014-09-09 05:41:10 +02:00
m . color = false
2015-06-21 22:51:40 +02:00
m . Color = false
2020-08-13 20:10:09 +02:00
} else {
// copy and increment index
args [ i ] = v
i ++
2014-07-13 05:21:46 +02:00
}
}
2020-08-13 20:10:09 +02:00
args = args [ : i ]
2014-07-13 05:21:46 +02:00
2014-07-13 05:59:16 +02:00
// Set the UI
m . oldUi = m . Ui
2014-08-19 19:22:26 +02:00
m . Ui = & cli . ConcurrentUi {
Ui : & ColorizeUi {
Colorize : m . Colorize ( ) ,
ErrorColor : "[red]" ,
2015-03-05 21:22:34 +01:00
WarnColor : "[yellow]" ,
2014-08-19 19:22:26 +02:00
Ui : m . oldUi ,
} ,
2014-07-13 05:59:16 +02:00
}
2020-04-01 21:01:08 +02:00
return args
2014-07-13 05:21:46 +02:00
}
2014-07-13 05:37:30 +02:00
// uiHook returns the UiHook to use with the context.
func ( m * Meta ) uiHook ( ) * UiHook {
return & UiHook {
Colorize : m . Colorize ( ) ,
Ui : m . Ui ,
}
}
2014-09-22 19:56:50 +02:00
2017-01-19 05:50:45 +01:00
// confirm asks a yes/no confirmation.
func ( m * Meta ) confirm ( opts * terraform . InputOpts ) ( bool , error ) {
2017-06-24 01:11:30 +02:00
if ! m . Input ( ) {
return false , errors . New ( "input is disabled" )
2017-03-29 22:45:25 +02:00
}
2017-12-18 17:38:51 +01:00
for i := 0 ; i < 2 ; i ++ {
2019-03-07 21:07:13 +01:00
v , err := m . UIInput ( ) . Input ( context . Background ( ) , opts )
2017-01-19 05:50:45 +01:00
if err != nil {
return false , fmt . Errorf (
"Error asking for confirmation: %s" , err )
}
switch strings . ToLower ( v ) {
case "no" :
return false , nil
case "yes" :
return true , nil
}
}
2017-12-18 17:38:51 +01:00
return false , nil
2017-01-19 05:50:45 +01:00
}
2017-10-05 20:59:08 +02:00
// showDiagnostics displays error and warning messages in the UI.
//
// "Diagnostics" here means the Diagnostics type from the tfdiag package,
// though as a convenience this function accepts anything that could be
// passed to the "Append" method on that type, converting it to Diagnostics
// before displaying it.
//
// Internally this function uses Diagnostics.Append, and so it will panic
// if given unsupported value types, just as Append does.
func ( m * Meta ) showDiagnostics ( vals ... interface { } ) {
var diags tfdiags . Diagnostics
diags = diags . Append ( vals ... )
2018-06-21 03:57:23 +02:00
diags . Sort ( )
2017-10-05 20:59:08 +02:00
2019-12-10 20:06:06 +01:00
if len ( diags ) == 0 {
return
}
diags = diags . ConsolidateWarnings ( 1 )
2019-11-19 22:57:35 +01:00
// Since warning messages are generally competing
2019-12-10 20:06:06 +01:00
if m . compactWarnings {
// If the user selected compact warnings and all of the diagnostics are
// warnings then we'll use a more compact representation of the warnings
// that only includes their summaries.
// We show full warnings if there are also errors, because a warning
// can sometimes serve as good context for a subsequent error.
useCompact := true
for _ , diag := range diags {
if diag . Severity ( ) != tfdiags . Warning {
useCompact = false
break
}
}
if useCompact {
msg := format . DiagnosticWarningsCompact ( diags , m . Colorize ( ) )
msg = "\n" + msg + "\nTo see the full warning notes, run Terraform without -compact-warnings.\n"
m . Ui . Warn ( msg )
return
}
}
2019-11-19 22:57:35 +01:00
2017-10-05 20:59:08 +02:00
for _ , diag := range diags {
// TODO: Actually measure the terminal width and pass it here.
// For now, we don't have easy access to the writer that
// ui.Error (etc) are writing to and thus can't interrogate
// to see if it's a terminal and what size it is.
2018-03-01 02:09:48 +01:00
msg := format . Diagnostic ( diag , m . configSources ( ) , m . Colorize ( ) , 78 )
2017-10-05 20:59:08 +02:00
switch diag . Severity ( ) {
case tfdiags . Error :
m . Ui . Error ( msg )
case tfdiags . Warning :
m . Ui . Warn ( msg )
default :
m . Ui . Output ( msg )
}
}
}
2016-11-03 22:46:26 +01:00
// outputShadowError outputs the error from ctx.ShadowError. If the
// error is nil then nothing happens. If output is false then it isn't
// outputted to the user (you can define logic to guard against outputting).
func ( m * Meta ) outputShadowError ( err error , output bool ) bool {
// Do nothing if no error
if err == nil {
return false
}
// If not outputting, do nothing
if ! output {
return false
}
2016-11-22 00:05:49 +01:00
// Write the shadow error output to a file
path := fmt . Sprintf ( "terraform-error-%d.log" , time . Now ( ) . UTC ( ) . Unix ( ) )
if err := ioutil . WriteFile ( path , [ ] byte ( err . Error ( ) ) , 0644 ) ; err != nil {
// If there is an error writing it, just let it go
log . Printf ( "[ERROR] Error writing shadow error: %s" , err )
return false
}
2016-11-03 22:46:26 +01:00
// Output!
m . Ui . Output ( m . Colorize ( ) . Color ( fmt . Sprintf (
"[reset][bold][yellow]\nExperimental feature failure! Please report a bug.\n\n" +
"This is not an error. Your Terraform operation completed successfully.\n" +
"Your real infrastructure is unaffected by this message.\n\n" +
"[reset][yellow]While running, Terraform sometimes tests experimental features in the\n" +
"background. These features cannot affect real state and never touch\n" +
"real infrastructure. If the features work properly, you see nothing.\n" +
"If the features fail, this message appears.\n\n" +
"You can report an issue at: https://github.com/hashicorp/terraform/issues\n\n" +
2016-11-22 00:05:49 +01:00
"The failure was written to %q. Please\n" +
"double check this file contains no sensitive information and report\n" +
"it with your issue.\n\n" +
2016-11-03 22:46:26 +01:00
"This is not an error. Your terraform operation completed successfully\n" +
"and your real infrastructure is unaffected by this message." ,
2016-11-22 00:05:49 +01:00
path ,
2016-11-03 22:46:26 +01:00
) ) )
return true
}
2017-02-28 19:13:03 +01:00
2017-05-31 02:13:43 +02:00
// WorkspaceNameEnvVar is the name of the environment variable that can be used
// to set the name of the Terraform workspace, overriding the workspace chosen
// by `terraform workspace select`.
//
// Note that this environment variable is ignored by `terraform workspace new`
// and `terraform workspace delete`.
const WorkspaceNameEnvVar = "TF_WORKSPACE"
2017-05-12 22:53:29 +02:00
2020-06-16 18:23:15 +02:00
var invalidWorkspaceNameEnvVar = fmt . Errorf ( "Invalid workspace name set using %s" , WorkspaceNameEnvVar )
2017-05-31 02:13:43 +02:00
// Workspace returns the name of the currently configured workspace, corresponding
2017-02-28 19:13:03 +01:00
// to the desired named state.
2020-06-16 18:23:15 +02:00
func ( m * Meta ) Workspace ( ) ( string , error ) {
current , overridden := m . WorkspaceOverridden ( )
if overridden && ! validWorkspaceName ( current ) {
return "" , invalidWorkspaceNameEnvVar
}
return current , nil
2017-05-12 22:53:29 +02:00
}
2017-05-31 02:13:43 +02:00
// WorkspaceOverridden returns the name of the currently configured workspace,
2017-05-12 22:53:29 +02:00
// corresponding to the desired named state, as well as a bool saying whether
2017-05-31 00:06:13 +02:00
// this was set via the TF_WORKSPACE environment variable.
2017-05-31 02:13:43 +02:00
func ( m * Meta ) WorkspaceOverridden ( ) ( string , bool ) {
if envVar := os . Getenv ( WorkspaceNameEnvVar ) ; envVar != "" {
2017-05-12 22:53:29 +02:00
return envVar , true
}
2017-06-14 21:14:26 +02:00
envData , err := ioutil . ReadFile ( filepath . Join ( m . DataDir ( ) , local . DefaultWorkspaceFile ) )
2017-02-28 19:13:03 +01:00
current := string ( bytes . TrimSpace ( envData ) )
if current == "" {
current = backend . DefaultStateName
}
if err != nil && ! os . IsNotExist ( err ) {
2017-05-31 00:06:13 +02:00
// always return the default if we can't get a workspace name
log . Printf ( "[ERROR] failed to read current workspace: %s" , err )
2017-02-28 19:13:03 +01:00
}
2017-05-12 22:53:29 +02:00
return current , false
2017-02-28 19:13:03 +01:00
}
2017-05-31 02:13:43 +02:00
// SetWorkspace saves the given name as the current workspace in the local
2017-05-31 00:06:13 +02:00
// filesystem.
2017-05-31 02:13:43 +02:00
func ( m * Meta ) SetWorkspace ( name string ) error {
2017-06-14 21:14:26 +02:00
err := os . MkdirAll ( m . DataDir ( ) , 0755 )
2017-02-28 19:13:03 +01:00
if err != nil {
return err
}
2017-06-14 21:14:26 +02:00
err = ioutil . WriteFile ( filepath . Join ( m . DataDir ( ) , local . DefaultWorkspaceFile ) , [ ] byte ( name ) , 0644 )
2017-02-28 19:13:03 +01:00
if err != nil {
return err
}
return nil
}
2017-03-08 05:09:48 +01:00
2017-06-22 03:22:07 +02:00
// isAutoVarFile determines if the file ends with .auto.tfvars or .auto.tfvars.json
func isAutoVarFile ( path string ) bool {
return strings . HasSuffix ( path , ".auto.tfvars" ) ||
strings . HasSuffix ( path , ".auto.tfvars.json" )
2017-03-08 05:09:48 +01:00
}