command: Various updates for the new backend package API
This is a rather-messy, complex change to get the "command" package building again against the new backend API that was updated for the new configuration loader. A lot of this is mechanical rewriting to the new API, but meta_config.go and meta_backend.go in particular saw some major changes to interface with the new loader APIs and to deal with the change in order of steps in the backend API.
This commit is contained in:
parent
5782357c28
commit
ebafa51723
|
@ -275,3 +275,7 @@ const (
|
|||
// performed at all.
|
||||
OperationFailure OperationResult = 1
|
||||
)
|
||||
|
||||
func (r OperationResult) ExitStatus() int {
|
||||
return int(r)
|
||||
}
|
||||
|
|
|
@ -7,12 +7,13 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
|
||||
"github.com/hashicorp/go-getter"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
|
@ -118,66 +119,75 @@ func (c *ApplyCommand) Run(args []string) int {
|
|||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
// Load the module if we don't have one yet (not running from plan)
|
||||
var mod *module.Tree
|
||||
var backendConfig *configs.Backend
|
||||
if plan == nil {
|
||||
var modDiags tfdiags.Diagnostics
|
||||
mod, modDiags = c.Module(configPath)
|
||||
diags = diags.Append(modDiags)
|
||||
if modDiags.HasErrors() {
|
||||
var configDiags tfdiags.Diagnostics
|
||||
backendConfig, configDiags = c.loadBackendConfig(configPath)
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
var conf *config.Config
|
||||
if mod != nil {
|
||||
conf = mod.Config()
|
||||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(&BackendOpts{
|
||||
Config: conf,
|
||||
b, beDiags := c.Backend(&BackendOpts{
|
||||
Config: backendConfig,
|
||||
Plan: plan,
|
||||
})
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
diags = diags.Append(beDiags)
|
||||
if beDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Before we delegate to the backend, we'll print any warning diagnostics
|
||||
// we've accumulated here, since the backend will start fresh with its own
|
||||
// diagnostics.
|
||||
c.showDiagnostics(diags)
|
||||
diags = nil
|
||||
|
||||
// Build the operation
|
||||
opReq := c.Operation()
|
||||
opReq.AutoApprove = autoApprove
|
||||
opReq.Destroy = c.Destroy
|
||||
opReq.DestroyForce = destroyForce
|
||||
opReq.Module = mod
|
||||
opReq.ConfigDir = configPath
|
||||
opReq.Plan = plan
|
||||
opReq.PlanRefresh = refresh
|
||||
opReq.Type = backend.OperationTypeApply
|
||||
|
||||
op, err := c.RunOperation(b, opReq)
|
||||
opReq.AutoApprove = autoApprove
|
||||
opReq.DestroyForce = destroyForce
|
||||
opReq.ConfigLoader, err = c.initConfigLoader()
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
}
|
||||
|
||||
c.showDiagnostics(diags)
|
||||
if diags.HasErrors() {
|
||||
c.showDiagnostics(err)
|
||||
return 1
|
||||
}
|
||||
|
||||
if !c.Destroy {
|
||||
// Get the right module that we used. If we ran a plan, then use
|
||||
// that module.
|
||||
if plan != nil {
|
||||
mod = plan.Module
|
||||
}
|
||||
|
||||
if outputs := outputsAsString(op.State, terraform.RootModulePath, mod.Config().Outputs, true); outputs != "" {
|
||||
c.Ui.Output(c.Colorize().Color(outputs))
|
||||
}
|
||||
op, err := c.RunOperation(b, opReq)
|
||||
if err != nil {
|
||||
c.showDiagnostics(err)
|
||||
return 1
|
||||
}
|
||||
if op.Result != backend.OperationSuccess {
|
||||
return op.Result.ExitStatus()
|
||||
}
|
||||
|
||||
return op.ExitCode
|
||||
if !c.Destroy {
|
||||
// TODO: Print outputs, once this is updated to use new config types.
|
||||
/*
|
||||
// Get the right module that we used. If we ran a plan, then use
|
||||
// that module.
|
||||
if plan != nil {
|
||||
mod = plan.Module
|
||||
}
|
||||
|
||||
if outputs := outputsAsString(op.State, terraform.RootModulePath, mod.Config().Outputs, true); outputs != "" {
|
||||
c.Ui.Output(c.Colorize().Color(outputs))
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return op.Result.ExitStatus()
|
||||
}
|
||||
|
||||
func (c *ApplyCommand) Help() string {
|
||||
|
|
|
@ -49,15 +49,15 @@ func (m *Meta) completePredictWorkspaceName() complete.Predictor {
|
|||
return nil
|
||||
}
|
||||
|
||||
cfg, err := m.Config(configPath)
|
||||
if err != nil {
|
||||
backendConfig, diags := m.loadBackendConfig(configPath)
|
||||
if diags.HasErrors() {
|
||||
return nil
|
||||
}
|
||||
|
||||
b, err := m.Backend(&BackendOpts{
|
||||
Config: cfg,
|
||||
b, diags := m.Backend(&BackendOpts{
|
||||
Config: backendConfig,
|
||||
})
|
||||
if err != nil {
|
||||
if diags.HasErrors() {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -552,9 +552,9 @@ func testBackendState(t *testing.T, s *terraform.State, c int) (*terraform.State
|
|||
|
||||
state := terraform.NewState()
|
||||
state.Backend = &terraform.BackendState{
|
||||
Type: "http",
|
||||
Config: map[string]interface{}{"address": srv.URL},
|
||||
Hash: 2529831861221416334,
|
||||
Type: "http",
|
||||
ConfigRaw: json.RawMessage(fmt.Sprintf(`{"address":%q}`, srv.URL)),
|
||||
Hash: 2529831861221416334,
|
||||
}
|
||||
|
||||
return state, srv
|
||||
|
|
|
@ -2,11 +2,9 @@ package command
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/helper/wrappedstreams"
|
||||
"github.com/hashicorp/terraform/repl"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
|
@ -41,43 +39,46 @@ func (c *ConsoleCommand) Run(args []string) int {
|
|||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
// Load the module
|
||||
mod, diags := c.Module(configPath)
|
||||
backendConfig, backendDiags := c.loadBackendConfig(configPath)
|
||||
diags = diags.Append(backendDiags)
|
||||
if diags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
var conf *config.Config
|
||||
if mod != nil {
|
||||
conf = mod.Config()
|
||||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(&BackendOpts{
|
||||
Config: conf,
|
||||
b, backendDiags := c.Backend(&BackendOpts{
|
||||
Config: backendConfig,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// We require a local backend
|
||||
local, ok := b.(backend.Local)
|
||||
if !ok {
|
||||
c.showDiagnostics(diags) // in case of any warnings in here
|
||||
c.Ui.Error(ErrUnsupportedLocalOp)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Build the operation
|
||||
opReq := c.Operation()
|
||||
opReq.Module = mod
|
||||
opReq.ConfigDir = configPath
|
||||
opReq.ConfigLoader, err = c.initConfigLoader()
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Get the context
|
||||
ctx, _, err := local.Context(opReq)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
ctx, _, ctxDiags := local.Context(opReq)
|
||||
diags = diags.Append(ctxDiags)
|
||||
if ctxDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@ import (
|
|||
"github.com/hashicorp/terraform/tfdiags"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/dag"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
@ -60,55 +58,47 @@ func (c *GraphCommand) Run(args []string) int {
|
|||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
// Load the module
|
||||
var mod *module.Tree
|
||||
if plan == nil {
|
||||
var modDiags tfdiags.Diagnostics
|
||||
mod, modDiags = c.Module(configPath)
|
||||
diags = diags.Append(modDiags)
|
||||
if modDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
var conf *config.Config
|
||||
if mod != nil {
|
||||
conf = mod.Config()
|
||||
backendConfig, backendDiags := c.loadBackendConfig(configPath)
|
||||
diags = diags.Append(backendDiags)
|
||||
if diags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(&BackendOpts{
|
||||
Config: conf,
|
||||
Plan: plan,
|
||||
b, backendDiags := c.Backend(&BackendOpts{
|
||||
Config: backendConfig,
|
||||
})
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// We require a local backend
|
||||
local, ok := b.(backend.Local)
|
||||
if !ok {
|
||||
c.showDiagnostics(diags) // in case of any warnings in here
|
||||
c.Ui.Error(ErrUnsupportedLocalOp)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Building a graph may require config module to be present, even if it's
|
||||
// empty.
|
||||
if mod == nil && plan == nil {
|
||||
mod = module.NewEmptyTree()
|
||||
}
|
||||
|
||||
// Build the operation
|
||||
opReq := c.Operation()
|
||||
opReq.Module = mod
|
||||
opReq.ConfigDir = configPath
|
||||
opReq.ConfigLoader, err = c.initConfigLoader()
|
||||
opReq.Plan = plan
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Get the context
|
||||
ctx, _, err := local.Context(opReq)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
ctx, _, ctxDiags := local.Context(opReq)
|
||||
diags = diags.Append(ctxDiags)
|
||||
if ctxDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
@ -76,37 +76,34 @@ func (c *ImportCommand) Run(args []string) int {
|
|||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
// Load the module
|
||||
var mod *module.Tree
|
||||
if configPath != "" {
|
||||
if empty, _ := config.IsEmptyDir(configPath); empty {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "No Terraform configuration files",
|
||||
Detail: fmt.Sprintf(
|
||||
"The directory %s does not contain any Terraform configuration files (.tf or .tf.json). To specify a different configuration directory, use the -config=\"...\" command line option.",
|
||||
configPath,
|
||||
),
|
||||
})
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
if !c.dirIsConfigPath(configPath) {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "No Terraform configuration files",
|
||||
Detail: fmt.Sprintf(
|
||||
"The directory %s does not contain any Terraform configuration files (.tf or .tf.json). To specify a different configuration directory, use the -config=\"...\" command line option.",
|
||||
configPath,
|
||||
),
|
||||
})
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
var modDiags tfdiags.Diagnostics
|
||||
mod, modDiags = c.Module(configPath)
|
||||
diags = diags.Append(modDiags)
|
||||
if modDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
// Load the full config, so we can verify that the target resource is
|
||||
// already configured.
|
||||
config, configDiags := c.loadConfig(configPath)
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Verify that the given address points to something that exists in config.
|
||||
// This is to reduce the risk that a typo in the resource address will
|
||||
// import something that Terraform will want to immediately destroy on
|
||||
// the next plan, and generally acts as a reassurance of user intent.
|
||||
targetMod := mod.Child(addr.Path)
|
||||
if targetMod == nil {
|
||||
targetConfig := config.Descendent(addr.Path)
|
||||
if targetConfig == nil {
|
||||
modulePath := addr.WholeModuleAddress().String()
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
|
@ -119,10 +116,11 @@ func (c *ImportCommand) Run(args []string) int {
|
|||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
rcs := targetMod.Config().Resources
|
||||
var rc *config.Resource
|
||||
targetMod := targetConfig.Module
|
||||
rcs := targetMod.ManagedResources
|
||||
var rc *configs.ManagedResource
|
||||
for _, thisRc := range rcs {
|
||||
if addr.MatchesConfig(targetMod, thisRc) {
|
||||
if addr.MatchesManagedResourceConfig(addr.Path, thisRc) {
|
||||
rc = thisRc
|
||||
break
|
||||
}
|
||||
|
@ -154,11 +152,12 @@ func (c *ImportCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(&BackendOpts{
|
||||
Config: mod.Config(),
|
||||
b, backendDiags := c.Backend(&BackendOpts{
|
||||
Config: config.Module.Backend,
|
||||
})
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
@ -175,12 +174,19 @@ func (c *ImportCommand) Run(args []string) int {
|
|||
|
||||
// Build the operation
|
||||
opReq := c.Operation()
|
||||
opReq.Module = mod
|
||||
opReq.ConfigDir = configPath
|
||||
opReq.ConfigLoader, err = c.initConfigLoader()
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Get the context
|
||||
ctx, state, err := local.Context(opReq)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
ctx, state, ctxDiags := local.Context(opReq)
|
||||
diags = diags.Append(ctxDiags)
|
||||
if ctxDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
183
command/init.go
183
command/init.go
|
@ -7,16 +7,19 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/posener/complete"
|
||||
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
backendinit "github.com/hashicorp/terraform/backend/init"
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/helper/variables"
|
||||
"github.com/hashicorp/terraform/config/configschema"
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
"github.com/hashicorp/terraform/plugin"
|
||||
"github.com/hashicorp/terraform/plugin/discovery"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/posener/complete"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// InitCommand is a Command implementation that takes a Terraform
|
||||
|
@ -37,9 +40,9 @@ type InitCommand struct {
|
|||
func (c *InitCommand) Run(args []string) int {
|
||||
var flagFromModule string
|
||||
var flagBackend, flagGet, flagUpgrade bool
|
||||
var flagConfigExtra map[string]interface{}
|
||||
var flagPluginPath FlagStringSlice
|
||||
var flagVerifyPlugins bool
|
||||
flagConfigExtra := newRawFlags("-backend-config")
|
||||
|
||||
args, err := c.Meta.process(args, false)
|
||||
if err != nil {
|
||||
|
@ -47,7 +50,7 @@ func (c *InitCommand) Run(args []string) int {
|
|||
}
|
||||
cmdFlags := c.flagSet("init")
|
||||
cmdFlags.BoolVar(&flagBackend, "backend", true, "")
|
||||
cmdFlags.Var((*variables.FlagAny)(&flagConfigExtra), "backend-config", "")
|
||||
cmdFlags.Var(flagConfigExtra, "backend-config", "")
|
||||
cmdFlags.StringVar(&flagFromModule, "from-module", "", "copy the source of the given module into the directory before init")
|
||||
cmdFlags.BoolVar(&flagGet, "get", true, "")
|
||||
cmdFlags.BoolVar(&c.getPlugins, "get-plugins", true, "")
|
||||
|
@ -64,6 +67,8 @@ func (c *InitCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
if len(flagPluginPath) > 0 {
|
||||
c.pluginPath = flagPluginPath
|
||||
c.getPlugins = false
|
||||
|
@ -129,18 +134,23 @@ func (c *InitCommand) Run(args []string) int {
|
|||
)))
|
||||
header = true
|
||||
|
||||
s := module.NewStorage("", c.Services)
|
||||
if err := s.GetModule(path, src); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error copying source module: %s", err))
|
||||
hooks := uiModuleInstallHooks{
|
||||
Ui: c.Ui,
|
||||
ShowLocalPaths: false, // since they are in a weird location for init
|
||||
}
|
||||
|
||||
initDiags := c.initDirFromModule(path, src, hooks)
|
||||
diags = diags.Append(initDiags)
|
||||
if initDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// If our directory is empty, then we're done. We can't get or setup
|
||||
// the backend with an empty directory.
|
||||
empty, err := config.IsEmptyDir(path)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error checking configuration: %s", err))
|
||||
if empty, err := config.IsEmptyDir(path); err != nil {
|
||||
diags = diags.Append(fmt.Errorf("Error checking configuration: %s", err))
|
||||
return 1
|
||||
}
|
||||
if empty {
|
||||
|
@ -153,32 +163,34 @@ func (c *InitCommand) Run(args []string) int {
|
|||
// If we're performing a get or loading the backend, then we perform
|
||||
// some extra tasks.
|
||||
if flagGet || flagBackend {
|
||||
conf, err := c.Config(path)
|
||||
if err != nil {
|
||||
config, confDiags := c.loadSingleModule(path)
|
||||
diags = diags.Append(confDiags)
|
||||
if confDiags.HasErrors() {
|
||||
// Since this may be the user's first ever interaction with Terraform,
|
||||
// we'll provide some additional context in this case.
|
||||
c.Ui.Error(strings.TrimSpace(errInitConfigError))
|
||||
c.showDiagnostics(err)
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// If we requested downloading modules and have modules in the config
|
||||
if flagGet && len(conf.Modules) > 0 {
|
||||
if flagGet && len(config.ModuleCalls) > 0 {
|
||||
header = true
|
||||
|
||||
getMode := module.GetModeGet
|
||||
if flagUpgrade {
|
||||
getMode = module.GetModeUpdate
|
||||
c.Ui.Output(c.Colorize().Color(fmt.Sprintf(
|
||||
"[reset][bold]Upgrading modules...")))
|
||||
c.Ui.Output(c.Colorize().Color(fmt.Sprintf("[reset][bold]Upgrading modules...")))
|
||||
} else {
|
||||
c.Ui.Output(c.Colorize().Color(fmt.Sprintf(
|
||||
"[reset][bold]Initializing modules...")))
|
||||
c.Ui.Output(c.Colorize().Color(fmt.Sprintf("[reset][bold]Initializing modules...")))
|
||||
}
|
||||
|
||||
if err := getModules(&c.Meta, path, getMode); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Error downloading modules: %s", err))
|
||||
hooks := uiModuleInstallHooks{
|
||||
Ui: c.Ui,
|
||||
ShowLocalPaths: true,
|
||||
}
|
||||
instDiags := c.installModules(path, flagUpgrade, hooks)
|
||||
diags = diags.Append(instDiags)
|
||||
if instDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
@ -188,21 +200,52 @@ func (c *InitCommand) Run(args []string) int {
|
|||
if flagBackend {
|
||||
header = true
|
||||
|
||||
var backendSchema *configschema.Block
|
||||
|
||||
// Only output that we're initializing a backend if we have
|
||||
// something in the config. We can be UNSETTING a backend as well
|
||||
// in which case we choose not to show this.
|
||||
if conf.Terraform != nil && conf.Terraform.Backend != nil {
|
||||
c.Ui.Output(c.Colorize().Color(fmt.Sprintf(
|
||||
"\n[reset][bold]Initializing the backend...")))
|
||||
if config.Backend != nil {
|
||||
c.Ui.Output(c.Colorize().Color(fmt.Sprintf("\n[reset][bold]Initializing the backend...")))
|
||||
|
||||
backendType := config.Backend.Type
|
||||
bf := backendinit.Backend(backendType)
|
||||
if bf == nil {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Unsupported backend type",
|
||||
Detail: fmt.Sprintf("There is no backend type named %q.", backendType),
|
||||
Subject: &config.Backend.TypeRange,
|
||||
})
|
||||
c.showDiagnostics()
|
||||
return 1
|
||||
}
|
||||
|
||||
b := bf()
|
||||
backendSchema = b.ConfigSchema()
|
||||
}
|
||||
|
||||
var backendConfigOverride hcl.Body
|
||||
if backendSchema != nil {
|
||||
var overrideDiags tfdiags.Diagnostics
|
||||
backendConfigOverride, overrideDiags = c.backendConfigOverrideBody(flagConfigExtra, backendSchema)
|
||||
diags = diags.Append(overrideDiags)
|
||||
if overrideDiags.HasErrors() {
|
||||
c.showDiagnostics()
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
opts := &BackendOpts{
|
||||
Config: conf,
|
||||
ConfigExtra: flagConfigExtra,
|
||||
Init: true,
|
||||
Config: config.Backend,
|
||||
ConfigOverride: backendConfigOverride,
|
||||
Init: true,
|
||||
}
|
||||
if back, err = c.Backend(opts); err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
var backDiags tfdiags.Diagnostics
|
||||
back, backDiags = c.Backend(opts)
|
||||
diags = diags.Append(backDiags)
|
||||
if backDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
@ -213,8 +256,9 @@ func (c *InitCommand) Run(args []string) int {
|
|||
// instantiate one. This might fail if it wasn't already initalized
|
||||
// by a previous run, so we must still expect that "back" may be nil
|
||||
// in code that follows.
|
||||
back, err = c.Backend(nil)
|
||||
if err != nil {
|
||||
var backDiags tfdiags.Diagnostics
|
||||
back, backDiags = c.Backend(nil)
|
||||
if backDiags.HasErrors() {
|
||||
// This is fine. We'll proceed with no backend, then.
|
||||
back = nil
|
||||
}
|
||||
|
@ -269,6 +313,75 @@ func (c *InitCommand) Run(args []string) int {
|
|||
return 0
|
||||
}
|
||||
|
||||
// backendConfigOverrideBody interprets the raw values of -backend-config
|
||||
// arguments into a hcl Body that should override the backend settings given
|
||||
// in the configuration.
|
||||
//
|
||||
// If the result is nil then no override needs to be provided.
|
||||
//
|
||||
// If the returned diagnostics contains errors then the returned body may be
|
||||
// incomplete or invalid.
|
||||
func (c *InitCommand) backendConfigOverrideBody(flags rawFlags, schema *configschema.Block) (hcl.Body, tfdiags.Diagnostics) {
|
||||
items := flags.AllItems()
|
||||
if len(items) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var ret hcl.Body
|
||||
var diags tfdiags.Diagnostics
|
||||
synthVals := make(map[string]cty.Value)
|
||||
|
||||
mergeBody := func(newBody hcl.Body) {
|
||||
if ret == nil {
|
||||
ret = newBody
|
||||
} else {
|
||||
ret = configs.MergeBodies(ret, newBody)
|
||||
}
|
||||
}
|
||||
flushVals := func() {
|
||||
if len(synthVals) == 0 {
|
||||
return
|
||||
}
|
||||
newBody := configs.SynthBody("-backend-config=...", synthVals)
|
||||
mergeBody(newBody)
|
||||
synthVals = make(map[string]cty.Value)
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
eq := strings.Index(item.Value, "=")
|
||||
|
||||
if eq == -1 {
|
||||
// The value is interpreted as a filename.
|
||||
newBody, fileDiags := c.loadHCLFile(item.Value)
|
||||
diags = diags.Append(fileDiags)
|
||||
flushVals() // deal with any accumulated individual values first
|
||||
mergeBody(newBody)
|
||||
} else {
|
||||
name := item.Value[:eq]
|
||||
rawValue := item.Value[eq+1:]
|
||||
attrS := schema.Attributes[name]
|
||||
if attrS == nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Invalid backend configuration argument",
|
||||
fmt.Sprintf("The backend configuration argument %q given on the command line is not expected for the selected backend type.", name),
|
||||
))
|
||||
continue
|
||||
}
|
||||
value, valueDiags := configValueFromCLI(item.String(), rawValue, attrS.Type)
|
||||
diags = diags.Append(valueDiags)
|
||||
if valueDiags.HasErrors() {
|
||||
continue
|
||||
}
|
||||
synthVals[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
flushVals()
|
||||
|
||||
return ret, diags
|
||||
}
|
||||
|
||||
// Load the complete module tree, and fetch any missing providers.
|
||||
// This method outputs its own Ui.
|
||||
func (c *InitCommand) getProviders(path string, state *terraform.State, upgrade bool) error {
|
||||
|
|
|
@ -11,6 +11,9 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/backend/local"
|
||||
"github.com/hashicorp/terraform/helper/copy"
|
||||
"github.com/hashicorp/terraform/plugin/discovery"
|
||||
|
@ -312,8 +315,8 @@ func TestInit_backendConfigFile(t *testing.T) {
|
|||
|
||||
// Read our saved backend config and verify we have our settings
|
||||
state := testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
|
||||
if v := state.Backend.Config["path"]; v != "hello" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
if got, want := string(state.Backend.ConfigRaw), `{"path":"hello"}`; got != want {
|
||||
t.Errorf("wrong config\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,8 +347,8 @@ func TestInit_backendConfigFileChange(t *testing.T) {
|
|||
|
||||
// Read our saved backend config and verify we have our settings
|
||||
state := testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
|
||||
if v := state.Backend.Config["path"]; v != "hello" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
if got, want := string(state.Backend.ConfigRaw), `{"path":"hello"}`; got != want {
|
||||
t.Errorf("wrong config\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -371,8 +374,8 @@ func TestInit_backendConfigKV(t *testing.T) {
|
|||
|
||||
// Read our saved backend config and verify we have our settings
|
||||
state := testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
|
||||
if v := state.Backend.Config["path"]; v != "hello" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
if got, want := string(state.Backend.ConfigRaw), `{"path":"hello"}`; got != want {
|
||||
t.Errorf("wrong config\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -419,11 +422,13 @@ func TestInit_backendReinitWithExtra(t *testing.T) {
|
|||
|
||||
m := testMetaBackend(t, nil)
|
||||
opts := &BackendOpts{
|
||||
ConfigExtra: map[string]interface{}{"path": "hello"},
|
||||
Init: true,
|
||||
ConfigOverride: configs.SynthBody("synth", map[string]cty.Value{
|
||||
"path": cty.StringVal("hello"),
|
||||
}),
|
||||
Init: true,
|
||||
}
|
||||
|
||||
b, err := m.backendConfig(opts)
|
||||
_, cHash, err := m.backendConfig(opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -443,28 +448,23 @@ func TestInit_backendReinitWithExtra(t *testing.T) {
|
|||
|
||||
// Read our saved backend config and verify we have our settings
|
||||
state := testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
|
||||
if v := state.Backend.Config["path"]; v != "hello" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
if got, want := string(state.Backend.ConfigRaw), `{"path":"hello"}`; got != want {
|
||||
t.Errorf("wrong config\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
|
||||
if state.Backend.Hash != b.Hash {
|
||||
if state.Backend.Hash != cHash {
|
||||
t.Fatal("mismatched state and config backend hashes")
|
||||
}
|
||||
|
||||
if state.Backend.Rehash() != b.Rehash() {
|
||||
t.Fatal("mismatched state and config re-hashes")
|
||||
}
|
||||
|
||||
// init again and make sure nothing changes
|
||||
if code := c.Run(args); code != 0 {
|
||||
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
|
||||
}
|
||||
state = testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
|
||||
if v := state.Backend.Config["path"]; v != "hello" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
if got, want := string(state.Backend.ConfigRaw), `{"path":"hello"}`; got != want {
|
||||
t.Errorf("wrong config\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
|
||||
if state.Backend.Hash != b.Hash {
|
||||
if state.Backend.Hash != cHash {
|
||||
t.Fatal("mismatched state and config backend hashes")
|
||||
}
|
||||
}
|
||||
|
@ -490,8 +490,8 @@ func TestInit_backendReinitConfigToExtra(t *testing.T) {
|
|||
|
||||
// Read our saved backend config and verify we have our settings
|
||||
state := testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
|
||||
if v := state.Backend.Config["path"]; v != "foo" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
if got, want := string(state.Backend.ConfigRaw), `{"path":"foo"}`; got != want {
|
||||
t.Errorf("wrong config\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
|
||||
backendHash := state.Backend.Hash
|
||||
|
|
|
@ -261,6 +261,15 @@ func (m *Meta) StdinPiped() bool {
|
|||
return fi.Mode()&os.ModeNamedPipe != 0
|
||||
}
|
||||
|
||||
// RunOperation executes the given operation on the given backend, blocking
|
||||
// until that operation completes or is inteerrupted, and then returns
|
||||
// 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.
|
||||
func (m *Meta) RunOperation(b backend.Enhanced, opReq *backend.Operation) (*backend.RunningOperation, error) {
|
||||
op, err := b.Operation(context.Background(), opReq)
|
||||
if err != nil {
|
||||
|
@ -302,10 +311,6 @@ func (m *Meta) RunOperation(b backend.Enhanced, opReq *backend.Operation) (*back
|
|||
// operation completed normally
|
||||
}
|
||||
|
||||
if op.Err != nil {
|
||||
return op, op.Err
|
||||
}
|
||||
|
||||
return op, nil
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -9,6 +9,9 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
backendInit "github.com/hashicorp/terraform/backend/init"
|
||||
backendLocal "github.com/hashicorp/terraform/backend/local"
|
||||
|
@ -28,9 +31,9 @@ func TestMetaBackend_emptyDir(t *testing.T) {
|
|||
|
||||
// Get the backend
|
||||
m := testMetaBackend(t, nil)
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Write some state
|
||||
|
@ -98,9 +101,9 @@ func TestMetaBackend_emptyWithDefaultState(t *testing.T) {
|
|||
|
||||
// Get the backend
|
||||
m := testMetaBackend(t, nil)
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -171,9 +174,9 @@ func TestMetaBackend_emptyWithExplicitState(t *testing.T) {
|
|||
m.statePath = statePath
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -230,9 +233,9 @@ func TestMetaBackend_emptyLegacyRemote(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -297,9 +300,9 @@ func TestMetaBackend_configureNew(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -366,9 +369,9 @@ func TestMetaBackend_configureNewWithState(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -444,9 +447,9 @@ func TestMetaBackend_configureNewWithoutCopy(t *testing.T) {
|
|||
m.input = false
|
||||
|
||||
// init the backend
|
||||
_, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
_, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Verify the state is where we expect
|
||||
|
@ -493,9 +496,9 @@ func TestMetaBackend_configureNewWithStateNoMigrate(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -537,9 +540,9 @@ func TestMetaBackend_configureNewWithStateExisting(t *testing.T) {
|
|||
m.forceInitCopy = true
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -611,9 +614,9 @@ func TestMetaBackend_configureNewWithStateExistingNoMigrate(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -685,9 +688,9 @@ func TestMetaBackend_configureNewLegacy(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -779,9 +782,9 @@ func TestMetaBackend_configureNewLegacyCopy(t *testing.T) {
|
|||
m.forceInitCopy = true
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -872,9 +875,9 @@ func TestMetaBackend_configuredUnchanged(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -919,9 +922,9 @@ func TestMetaBackend_configuredChange(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -1007,9 +1010,9 @@ func TestMetaBackend_reconfigureChange(t *testing.T) {
|
|||
m.reconfigure = true
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -1051,9 +1054,9 @@ func TestMetaBackend_configuredChangeCopy(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -1105,9 +1108,9 @@ func TestMetaBackend_configuredChangeCopy_singleState(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -1160,9 +1163,9 @@ func TestMetaBackend_configuredChangeCopy_multiToSingleDefault(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -1215,9 +1218,9 @@ func TestMetaBackend_configuredChangeCopy_multiToSingle(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -1286,9 +1289,9 @@ func TestMetaBackend_configuredChangeCopy_multiToSingleCurrentEnv(t *testing.T)
|
|||
}
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -1342,9 +1345,9 @@ func TestMetaBackend_configuredChangeCopy_multiToMulti(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check resulting states
|
||||
|
@ -1584,9 +1587,9 @@ func TestMetaBackend_configuredUnset(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -1668,9 +1671,9 @@ func TestMetaBackend_configuredUnsetCopy(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -1747,9 +1750,9 @@ func TestMetaBackend_configuredUnchangedLegacy(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -1848,9 +1851,9 @@ func TestMetaBackend_configuredUnchangedLegacyCopy(t *testing.T) {
|
|||
m.forceInitCopy = true
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -1951,9 +1954,9 @@ func TestMetaBackend_configuredChangedLegacy(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -2051,9 +2054,9 @@ func TestMetaBackend_configuredChangedLegacyCopyBackend(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -2154,9 +2157,9 @@ func TestMetaBackend_configuredChangedLegacyCopyLegacy(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -2257,9 +2260,9 @@ func TestMetaBackend_configuredChangedLegacyCopyBoth(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -2360,9 +2363,9 @@ func TestMetaBackend_configuredUnsetWithLegacyNoCopy(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -2450,9 +2453,9 @@ func TestMetaBackend_configuredUnsetWithLegacyCopyBackend(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -2548,9 +2551,9 @@ func TestMetaBackend_configuredUnsetWithLegacyCopyLegacy(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -2646,9 +2649,9 @@ func TestMetaBackend_configuredUnsetWithLegacyCopyBoth(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Init: true})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -2747,9 +2750,9 @@ func TestMetaBackend_planLocal(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Plan: plan})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Plan: plan})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -2844,9 +2847,9 @@ func TestMetaBackend_planLocalStatePath(t *testing.T) {
|
|||
m.stateOutPath = statePath
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Plan: plan})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Plan: plan})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -2930,9 +2933,9 @@ func TestMetaBackend_planLocalMatch(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Plan: plan})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Plan: plan})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -3023,12 +3026,12 @@ func TestMetaBackend_planLocalMismatchLineage(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
_, err := m.Backend(&BackendOpts{Plan: plan})
|
||||
if err == nil {
|
||||
_, diags := m.Backend(&BackendOpts{Plan: plan})
|
||||
if !diags.HasErrors() {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "lineage") {
|
||||
t.Fatalf("bad: %s", err)
|
||||
if !strings.Contains(diags[0].Description().Summary, "lineage") {
|
||||
t.Fatalf("wrong diagnostic message %q; want something containing \"lineage\"", diags[0].Description().Summary)
|
||||
}
|
||||
|
||||
// Verify our local state didn't change
|
||||
|
@ -3075,12 +3078,12 @@ func TestMetaBackend_planLocalNewer(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
_, err := m.Backend(&BackendOpts{Plan: plan})
|
||||
if err == nil {
|
||||
_, diags := m.Backend(&BackendOpts{Plan: plan})
|
||||
if !diags.HasErrors() {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "older") {
|
||||
t.Fatalf("bad: %s", err)
|
||||
if !strings.Contains(diags[0].Description().Summary, "older") {
|
||||
t.Fatalf("wrong diagnostic message %q; want something containing \"older\"", diags[0].Description().Summary)
|
||||
}
|
||||
|
||||
// Verify our local state didn't change
|
||||
|
@ -3130,9 +3133,9 @@ func TestMetaBackend_planBackendEmptyDir(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Plan: plan})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Plan: plan})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -3232,9 +3235,9 @@ func TestMetaBackend_planBackendMatch(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Plan: plan})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Plan: plan})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -3337,12 +3340,12 @@ func TestMetaBackend_planBackendMismatchLineage(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
_, err := m.Backend(&BackendOpts{Plan: plan})
|
||||
if err == nil {
|
||||
_, diags := m.Backend(&BackendOpts{Plan: plan})
|
||||
if !diags.HasErrors() {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "lineage") {
|
||||
t.Fatalf("bad: %s", err)
|
||||
if !strings.Contains(diags[0].Description().Summary, "lineage") {
|
||||
t.Fatalf("wrong diagnostic message %q; want something containing \"lineage\"", diags[0].Description().Summary)
|
||||
}
|
||||
|
||||
// Verify our local state didn't change
|
||||
|
@ -3395,9 +3398,9 @@ func TestMetaBackend_planLegacy(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
|
||||
// Get the backend
|
||||
b, err := m.Backend(&BackendOpts{Plan: plan})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
b, diags := m.Backend(&BackendOpts{Plan: plan})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
|
@ -3476,52 +3479,46 @@ func TestMetaBackend_configureWithExtra(t *testing.T) {
|
|||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
extras := map[string]interface{}{"path": "hello"}
|
||||
extras := map[string]cty.Value{"path": cty.StringVal("hello")}
|
||||
m := testMetaBackend(t, nil)
|
||||
opts := &BackendOpts{
|
||||
ConfigExtra: extras,
|
||||
Init: true,
|
||||
ConfigOverride: configs.SynthBody("synth", extras),
|
||||
Init: true,
|
||||
}
|
||||
|
||||
backendCfg, err := m.backendConfig(opts)
|
||||
_, cHash, err := m.backendConfig(opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// init the backend
|
||||
_, err = m.Backend(&BackendOpts{
|
||||
ConfigExtra: extras,
|
||||
Init: true,
|
||||
_, diags := m.Backend(&BackendOpts{
|
||||
ConfigOverride: configs.SynthBody("synth", extras),
|
||||
Init: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// Check the state
|
||||
s := testStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename))
|
||||
if s.Backend.Hash != backendCfg.Hash {
|
||||
s := testStateRead(t, filepath.Join(DefaultDataDir, backendlocal.DefaultStateFilename))
|
||||
if s.Backend.Hash != cHash {
|
||||
t.Fatal("mismatched state and config backend hashes")
|
||||
}
|
||||
if s.Backend.Rehash() == s.Backend.Hash {
|
||||
t.Fatal("saved hash should not match actual hash")
|
||||
}
|
||||
if s.Backend.Rehash() != backendCfg.Rehash() {
|
||||
t.Fatal("mismatched state and config re-hashes")
|
||||
}
|
||||
|
||||
// init the backend again with the same options
|
||||
m = testMetaBackend(t, nil)
|
||||
_, err = m.Backend(&BackendOpts{
|
||||
ConfigExtra: extras,
|
||||
Init: true,
|
||||
ConfigOverride: configs.SynthBody("synth", extras),
|
||||
Init: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
|
||||
// Check the state
|
||||
s = testStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename))
|
||||
if s.Backend.Hash != backendCfg.Hash {
|
||||
s = testStateRead(t, filepath.Join(DefaultDataDir, backendlocal.DefaultStateFilename))
|
||||
if s.Backend.Hash != cHash {
|
||||
t.Fatal("mismatched state and config backend hashes")
|
||||
}
|
||||
}
|
||||
|
@ -3557,11 +3554,9 @@ func TestMetaBackend_localDoesNotDeleteLocal(t *testing.T) {
|
|||
m := testMetaBackend(t, nil)
|
||||
m.forceInitCopy = true
|
||||
// init the backend
|
||||
_, err = m.Backend(&BackendOpts{
|
||||
Init: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
_, diags := m.Backend(&BackendOpts{Init: true})
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
// check that we can read the state
|
||||
|
@ -3599,15 +3594,15 @@ func TestMetaBackend_configToExtra(t *testing.T) {
|
|||
}
|
||||
|
||||
// init the backend again with the options
|
||||
extras := map[string]interface{}{"path": "hello"}
|
||||
extras := map[string]cty.Value{"path": cty.StringVal("hello")}
|
||||
m = testMetaBackend(t, nil)
|
||||
m.forceInitCopy = true
|
||||
_, err = m.Backend(&BackendOpts{
|
||||
ConfigExtra: extras,
|
||||
Init: true,
|
||||
_, diags := m.Backend(&BackendOpts{
|
||||
ConfigOverride: configs.SynthBody("synth", extras),
|
||||
Init: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
s = testStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename))
|
||||
|
|
|
@ -4,11 +4,18 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/hcl2/hcl/hclsyntax"
|
||||
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/hashicorp/terraform/config/configschema"
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
"github.com/hashicorp/terraform/configs/configload"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
)
|
||||
|
||||
// normalizePath normalizes a given path so that it is, if possible, relative
|
||||
|
@ -80,6 +87,23 @@ func (m *Meta) loadSingleModule(dir string) (*configs.Module, tfdiags.Diagnostic
|
|||
return module, diags
|
||||
}
|
||||
|
||||
// dirIsConfigPath checks if the given path is a directory that contains at
|
||||
// least one Terraform configuration file (.tf or .tf.json), returning true
|
||||
// if so.
|
||||
//
|
||||
// In the unlikely event that the underlying config loader cannot be initalized,
|
||||
// this function optimistically returns true, assuming that the caller will
|
||||
// then do some other operation that requires the config loader and get an
|
||||
// error at that point.
|
||||
func (m *Meta) dirIsConfigPath(dir string) bool {
|
||||
loader, err := m.initConfigLoader()
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return loader.IsConfigDir(dir)
|
||||
}
|
||||
|
||||
// loadBackendConfig reads configuration from the given directory and returns
|
||||
// the backend configuration defined by that module, if any. Nil is returned
|
||||
// if the specified module does not have an explicit backend configuration.
|
||||
|
@ -99,6 +123,41 @@ func (m *Meta) loadBackendConfig(rootDir string) (*configs.Backend, tfdiags.Diag
|
|||
return mod.Backend, diags
|
||||
}
|
||||
|
||||
// loadValuesFile loads a file that defines a single map of key/value pairs.
|
||||
// This is the format used for "tfvars" files.
|
||||
func (m *Meta) loadValuesFile(filename string) (map[string]cty.Value, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
filename = m.normalizePath(filename)
|
||||
|
||||
loader, err := m.initConfigLoader()
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
vals, hclDiags := loader.Parser().LoadValuesFile(filename)
|
||||
diags = diags.Append(hclDiags)
|
||||
return vals, diags
|
||||
}
|
||||
|
||||
// loadHCLFile reads an arbitrary HCL file and returns the unprocessed body
|
||||
// representing its toplevel. Most callers should use one of the more
|
||||
// specialized "load..." methods to get a higher-level representation.
|
||||
func (m *Meta) loadHCLFile(filename string) (hcl.Body, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
filename = m.normalizePath(filename)
|
||||
|
||||
loader, err := m.initConfigLoader()
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
body, hclDiags := loader.Parser().LoadHCLFile(filename)
|
||||
diags = diags.Append(hclDiags)
|
||||
return body, diags
|
||||
}
|
||||
|
||||
// installModules reads a root module from the given directory and attempts
|
||||
// recursively install all of its descendent modules.
|
||||
//
|
||||
|
@ -172,6 +231,67 @@ func (m *Meta) loadVarsFile(filename string) (map[string]cty.Value, tfdiags.Diag
|
|||
return ret, diags
|
||||
}
|
||||
|
||||
// inputForSchema uses interactive prompts to try to populate any
|
||||
// not-yet-populated required attributes in the given object value to
|
||||
// comply with the given schema.
|
||||
//
|
||||
// An error will be returned if input is disabled for this meta or if
|
||||
// values cannot be obtained for some other operational reason. Errors are
|
||||
// not returned for invalid input since the input loop itself will report
|
||||
// that interactively.
|
||||
//
|
||||
// It is not guaranteed that the result will be valid, since certain attribute
|
||||
// types and nested blocks are not supported for input.
|
||||
//
|
||||
// The given value must conform to the given schema. If not, this method will
|
||||
// panic.
|
||||
func (m *Meta) inputForSchema(given cty.Value, schema *configschema.Block) (cty.Value, error) {
|
||||
if given.IsNull() || !given.IsKnown() {
|
||||
// This is not reasonable input, but we'll tolerate it anyway and
|
||||
// just pass it through for the caller to handle downstream.
|
||||
return given, nil
|
||||
}
|
||||
|
||||
givenVals := given.AsValueMap()
|
||||
retVals := make(map[string]cty.Value, len(givenVals))
|
||||
names := make([]string, 0, len(schema.Attributes))
|
||||
for name, attrS := range schema.Attributes {
|
||||
retVals[name] = givenVals[name]
|
||||
if givenVal := givenVals[name]; attrS.Required && givenVal.IsNull() && attrS.Type.IsPrimitiveType() {
|
||||
names = append(names, name)
|
||||
}
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
input := m.UIInput()
|
||||
for _, name := range names {
|
||||
attrS := schema.Attributes[name]
|
||||
|
||||
for {
|
||||
strVal, err := input.Input(&terraform.InputOpts{
|
||||
Id: name,
|
||||
Query: name,
|
||||
Description: attrS.Description,
|
||||
})
|
||||
if err != nil {
|
||||
return cty.UnknownVal(schema.ImpliedType()), fmt.Errorf("%s: %s", name, err)
|
||||
}
|
||||
|
||||
val := cty.StringVal(strVal)
|
||||
val, err = convert.Convert(val, attrS.Type)
|
||||
if err != nil {
|
||||
m.showDiagnostics(fmt.Errorf("Invalid value: %s", err))
|
||||
continue
|
||||
}
|
||||
|
||||
retVals[name] = val
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return cty.ObjectVal(retVals), nil
|
||||
}
|
||||
|
||||
// configSources returns the source cache from the receiver's config loader,
|
||||
// which the caller must not modify.
|
||||
//
|
||||
|
@ -211,3 +331,87 @@ func (m *Meta) initConfigLoader() (*configload.Loader, error) {
|
|||
}
|
||||
return m.configLoader, nil
|
||||
}
|
||||
|
||||
// configValueFromCLI parses a configuration value that was provided in a
|
||||
// context in the CLI where only strings can be provided, such as on the
|
||||
// command line or in an environment variable, and returns the resulting
|
||||
// value.
|
||||
func configValueFromCLI(synthFilename, rawValue string, wantType cty.Type) (cty.Value, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
switch {
|
||||
case wantType.IsPrimitiveType():
|
||||
// Primitive types are handled as conversions from string.
|
||||
val := cty.StringVal(rawValue)
|
||||
var err error
|
||||
val, err = convert.Convert(val, wantType)
|
||||
if err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Invalid backend configuration value",
|
||||
fmt.Sprintf("Invalid backend configuration argument %s: %s", synthFilename, err),
|
||||
))
|
||||
val = cty.DynamicVal // just so we return something valid-ish
|
||||
}
|
||||
return val, diags
|
||||
default:
|
||||
// Non-primitives are parsed as HCL expressions
|
||||
src := []byte(rawValue)
|
||||
expr, hclDiags := hclsyntax.ParseExpression(src, synthFilename, hcl.Pos{Line: 1, Column: 1})
|
||||
diags = diags.Append(hclDiags)
|
||||
if hclDiags.HasErrors() {
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
val, hclDiags := expr.Value(nil)
|
||||
diags = diags.Append(hclDiags)
|
||||
if hclDiags.HasErrors() {
|
||||
val = cty.DynamicVal
|
||||
}
|
||||
return val, diags
|
||||
}
|
||||
}
|
||||
|
||||
// rawFlags is a flag.Value implementation that just appends raw flag
|
||||
// names and values to a slice.
|
||||
type rawFlags struct {
|
||||
flagName string
|
||||
items *[]rawFlag
|
||||
}
|
||||
|
||||
func newRawFlags(flagName string) rawFlags {
|
||||
return rawFlags{
|
||||
flagName: flagName,
|
||||
}
|
||||
}
|
||||
|
||||
func (f rawFlags) AllItems() []rawFlag {
|
||||
return *f.items
|
||||
}
|
||||
|
||||
func (f rawFlags) Alias(flagName string) rawFlags {
|
||||
return rawFlags{
|
||||
flagName: flagName,
|
||||
items: f.items,
|
||||
}
|
||||
}
|
||||
|
||||
func (f rawFlags) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f rawFlags) Set(str string) error {
|
||||
*f.items = append(*f.items, rawFlag{
|
||||
Name: f.flagName,
|
||||
Value: str,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
type rawFlag struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
func (f rawFlag) String() string {
|
||||
return fmt.Sprintf("%s=%q", f.Name, f.Value)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
||||
// OutputCommand is a Command implementation that reads an output
|
||||
|
@ -46,10 +48,13 @@ func (c *OutputCommand) Run(args []string) int {
|
|||
name = args[0]
|
||||
}
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
b, backendDiags := c.Backend(nil)
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
||||
|
@ -70,58 +69,62 @@ func (c *PlanCommand) Run(args []string) int {
|
|||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
// Load the module if we don't have one yet (not running from plan)
|
||||
var mod *module.Tree
|
||||
var backendConfig *configs.Backend
|
||||
if plan == nil {
|
||||
var modDiags tfdiags.Diagnostics
|
||||
mod, modDiags = c.Module(configPath)
|
||||
diags = diags.Append(modDiags)
|
||||
if modDiags.HasErrors() {
|
||||
var configDiags tfdiags.Diagnostics
|
||||
backendConfig, configDiags = c.loadBackendConfig(configPath)
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
var conf *config.Config
|
||||
if mod != nil {
|
||||
conf = mod.Config()
|
||||
}
|
||||
// Load the backend
|
||||
b, err := c.Backend(&BackendOpts{
|
||||
Config: conf,
|
||||
b, backendDiags := c.Backend(&BackendOpts{
|
||||
Config: backendConfig,
|
||||
Plan: plan,
|
||||
})
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Emit any diagnostics we've accumulated before we delegate to the
|
||||
// backend, since the backend will handle its own diagnostics internally.
|
||||
c.showDiagnostics(diags)
|
||||
diags = nil
|
||||
|
||||
// Build the operation
|
||||
opReq := c.Operation()
|
||||
opReq.Destroy = destroy
|
||||
opReq.Module = mod
|
||||
opReq.ModuleDepth = moduleDepth
|
||||
opReq.ConfigDir = configPath
|
||||
opReq.Plan = plan
|
||||
opReq.PlanOutPath = outPath
|
||||
opReq.PlanRefresh = refresh
|
||||
opReq.Type = backend.OperationTypePlan
|
||||
opReq.ConfigLoader, err = c.initConfigLoader()
|
||||
if err != nil {
|
||||
c.showDiagnostics(err)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Perform the operation
|
||||
op, err := c.RunOperation(b, opReq)
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
}
|
||||
|
||||
c.showDiagnostics(diags)
|
||||
if diags.HasErrors() {
|
||||
c.showDiagnostics(err)
|
||||
return 1
|
||||
}
|
||||
|
||||
if op.Result != backend.OperationSuccess {
|
||||
return op.Result.ExitStatus()
|
||||
}
|
||||
if detailed && !op.PlanEmpty {
|
||||
return 2
|
||||
}
|
||||
|
||||
return op.ExitCode
|
||||
return op.Result.ExitStatus()
|
||||
}
|
||||
|
||||
func (c *PlanCommand) Help() string {
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"sort"
|
||||
|
||||
"github.com/hashicorp/terraform/moduledeps"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/xlab/treeprint"
|
||||
)
|
||||
|
||||
|
@ -38,26 +38,22 @@ func (c *ProvidersCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
// Load the config
|
||||
root, diags := c.Module(configPath)
|
||||
if diags.HasErrors() {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
config, configDiags := c.loadConfig(configPath)
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
if root == nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"No configuration files found in the directory: %s\n\n"+
|
||||
"This command requires configuration to run.",
|
||||
configPath))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(&BackendOpts{
|
||||
Config: root.Config(),
|
||||
b, backendDiags := c.Backend(&BackendOpts{
|
||||
Config: config.Module.Backend,
|
||||
})
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
@ -73,9 +69,11 @@ func (c *ProvidersCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
s := state.State()
|
||||
|
||||
depTree := terraform.ModuleTreeDependencies(root, s)
|
||||
// FIXME: Restore this once the "terraform" package is updated to deal
|
||||
// with HCL2 config types.
|
||||
//s := state.State()
|
||||
//depTree := terraform.ModuleTreeDependencies(config, s)
|
||||
var depTree *moduledeps.Module
|
||||
depTree.SortDescendents()
|
||||
|
||||
printRoot := treeprint.New()
|
||||
|
|
|
@ -5,8 +5,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
||||
|
@ -42,54 +40,62 @@ func (c *RefreshCommand) Run(args []string) int {
|
|||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
// Load the module
|
||||
mod, diags := c.Module(configPath)
|
||||
if diags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Check for user-supplied plugin path
|
||||
if c.pluginPath, err = c.loadPluginPath(); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error loading plugin path: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
var conf *config.Config
|
||||
if mod != nil {
|
||||
conf = mod.Config()
|
||||
backendConfig, configDiags := c.loadBackendConfig(configPath)
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(&BackendOpts{
|
||||
Config: conf,
|
||||
b, backendDiags := c.Backend(&BackendOpts{
|
||||
Config: backendConfig,
|
||||
})
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Before we delegate to the backend, we'll print any warning diagnostics
|
||||
// we've accumulated here, since the backend will start fresh with its own
|
||||
// diagnostics.
|
||||
c.showDiagnostics(diags)
|
||||
diags = nil
|
||||
|
||||
// Build the operation
|
||||
opReq := c.Operation()
|
||||
opReq.Type = backend.OperationTypeRefresh
|
||||
opReq.Module = mod
|
||||
|
||||
op, err := c.RunOperation(b, opReq)
|
||||
opReq.ConfigDir = configPath
|
||||
opReq.ConfigLoader, err = c.initConfigLoader()
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
}
|
||||
|
||||
c.showDiagnostics(diags)
|
||||
if diags.HasErrors() {
|
||||
c.showDiagnostics(err)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Output the outputs
|
||||
if outputs := outputsAsString(op.State, terraform.RootModulePath, nil, true); outputs != "" {
|
||||
c.Ui.Output(c.Colorize().Color(outputs))
|
||||
op, err := c.RunOperation(b, opReq)
|
||||
if err != nil {
|
||||
c.showDiagnostics(err)
|
||||
return 1
|
||||
}
|
||||
if op.Result != backend.OperationSuccess {
|
||||
return op.Result.ExitStatus()
|
||||
}
|
||||
|
||||
return 0
|
||||
// TODO: Print outputs, once this is updated to use new config types.
|
||||
/*
|
||||
if outputs := outputsAsString(op.State, terraform.RootModulePath, nil, true); outputs != "" {
|
||||
c.Ui.Output(c.Colorize().Color(outputs))
|
||||
}
|
||||
*/
|
||||
|
||||
return op.Result.ExitStatus()
|
||||
}
|
||||
|
||||
func (c *RefreshCommand) Help() string {
|
||||
|
|
|
@ -71,9 +71,9 @@ func (c *ShowCommand) Run(args []string) int {
|
|||
}
|
||||
} else {
|
||||
// Load the backend
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
b, backendDiags := c.Backend(nil)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(backendDiags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@ func (c *StateListCommand) Run(args []string) int {
|
|||
args = cmdFlags.Args()
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
b, backendDiags := c.Backend(nil)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(backendDiags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -31,9 +31,9 @@ func (c *StateMeta) State() (state.State, error) {
|
|||
}
|
||||
} else {
|
||||
// Load the backend
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
b, backendDiags := c.Backend(nil)
|
||||
if backendDiags.HasErrors() {
|
||||
return nil, backendDiags.Err()
|
||||
}
|
||||
|
||||
env := c.Workspace()
|
||||
|
@ -44,10 +44,10 @@ func (c *StateMeta) State() (state.State, error) {
|
|||
}
|
||||
|
||||
// Get a local backend
|
||||
localRaw, err := c.Backend(&BackendOpts{ForceLocal: true})
|
||||
if err != nil {
|
||||
localRaw, backendDiags := c.Backend(&BackendOpts{ForceLocal: true})
|
||||
if backendDiags.HasErrors() {
|
||||
// This should never fail
|
||||
panic(err)
|
||||
panic(backendDiags.Err())
|
||||
}
|
||||
localB := localRaw.(*backendLocal.Local)
|
||||
_, stateOutPath, _ = localB.StatePaths(env)
|
||||
|
|
|
@ -28,9 +28,9 @@ func (c *StatePullCommand) Run(args []string) int {
|
|||
args = cmdFlags.Args()
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
b, backendDiags := c.Backend(nil)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(backendDiags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -63,9 +63,9 @@ func (c *StatePushCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
b, backendDiags := c.Backend(nil)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(backendDiags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@ func (c *StateShowCommand) Run(args []string) int {
|
|||
args = cmdFlags.Args()
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
b, backendDiags := c.Backend(nil)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(backendDiags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -64,9 +64,9 @@ func (c *TaintCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
b, backendDiags := c.Backend(nil)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(backendDiags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
|
@ -46,18 +47,22 @@ func (c *UnlockCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
conf, err := c.Config(configPath)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err))
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
backendConfig, backendDiags := c.loadBackendConfig(configPath)
|
||||
diags = diags.Append(backendDiags)
|
||||
if diags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(&BackendOpts{
|
||||
Config: conf,
|
||||
b, backendDiags := c.Backend(&BackendOpts{
|
||||
Config: backendConfig,
|
||||
})
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -52,9 +52,9 @@ func (c *UntaintCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
b, backendDiags := c.Backend(nil)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(backendDiags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/command/clistate"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
@ -49,19 +50,22 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
cfg, err := c.Config(configPath)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err))
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
backendConfig, backendDiags := c.loadBackendConfig(configPath)
|
||||
diags = diags.Append(backendDiags)
|
||||
if diags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(&BackendOpts{
|
||||
Config: cfg,
|
||||
b, backendDiags := c.Backend(&BackendOpts{
|
||||
Config: backendConfig,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@ package command
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
|
@ -34,19 +34,22 @@ func (c *WorkspaceListCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
cfg, err := c.Config(configPath)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err))
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
backendConfig, backendDiags := c.loadBackendConfig(configPath)
|
||||
diags = diags.Append(backendDiags)
|
||||
if diags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(&BackendOpts{
|
||||
Config: cfg,
|
||||
b, backendDiags := c.Backend(&BackendOpts{
|
||||
Config: backendConfig,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/command/clistate"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
@ -59,18 +60,22 @@ func (c *WorkspaceNewCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
conf, err := c.Config(configPath)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err))
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
backendConfig, backendDiags := c.loadBackendConfig(configPath)
|
||||
diags = diags.Append(backendDiags)
|
||||
if diags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(&BackendOpts{
|
||||
Config: conf,
|
||||
b, backendDiags := c.Backend(&BackendOpts{
|
||||
Config: backendConfig,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
@ -38,9 +39,13 @@ func (c *WorkspaceSelectCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
conf, err := c.Config(configPath)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err))
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
backendConfig, backendDiags := c.loadBackendConfig(configPath)
|
||||
diags = diags.Append(backendDiags)
|
||||
if diags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
current, isOverridden := c.WorkspaceOverridden()
|
||||
|
@ -50,9 +55,14 @@ func (c *WorkspaceSelectCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
// Load the backend
|
||||
b, err := c.Backend(&BackendOpts{
|
||||
Config: conf,
|
||||
b, backendDiags := c.Backend(&BackendOpts{
|
||||
Config: backendConfig,
|
||||
})
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package configschema
|
||||
|
||||
// NoneRequired returns a deep copy of the receiver with any required
|
||||
// attributes translated to optional.
|
||||
func (b *Block) NoneRequired() *Block {
|
||||
ret := &Block{}
|
||||
|
||||
if b.Attributes != nil {
|
||||
ret.Attributes = make(map[string]*Attribute, len(b.Attributes))
|
||||
}
|
||||
for name, attrS := range b.Attributes {
|
||||
ret.Attributes[name] = attrS.forceOptional()
|
||||
}
|
||||
|
||||
if b.BlockTypes != nil {
|
||||
ret.BlockTypes = make(map[string]*NestedBlock, len(b.BlockTypes))
|
||||
}
|
||||
for name, blockS := range b.BlockTypes {
|
||||
ret.BlockTypes[name] = blockS.noneRequired()
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *NestedBlock) noneRequired() *NestedBlock {
|
||||
ret := *b
|
||||
ret.Block = *(ret.Block.NoneRequired())
|
||||
ret.MinItems = 0
|
||||
ret.MaxItems = 0
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (a *Attribute) forceOptional() *Attribute {
|
||||
ret := *a
|
||||
ret.Optional = true
|
||||
ret.Required = false
|
||||
return &ret
|
||||
}
|
|
@ -2,6 +2,9 @@ package configs
|
|||
|
||||
import (
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/hashicorp/hcl2/hcldec"
|
||||
"github.com/hashicorp/terraform/config/configschema"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// Backend represents a "backend" block inside a "terraform" block in a module
|
||||
|
@ -22,3 +25,31 @@ func decodeBackendBlock(block *hcl.Block) (*Backend, hcl.Diagnostics) {
|
|||
DeclRange: block.DefRange,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Hash produces a hash value for the reciever that covers the type and the
|
||||
// portions of the config that conform to the given schema.
|
||||
//
|
||||
// If the config does not conform to the schema then the result is not
|
||||
// meaningful for comparison since it will be based on an incomplete result.
|
||||
//
|
||||
// As an exception, required attributes in the schema are treated as optional
|
||||
// for the purpose of hashing, so that an incomplete configuration can still
|
||||
// be hashed. Other errors, such as extraneous attributes, have no such special
|
||||
// case.
|
||||
func (b *Backend) Hash(schema *configschema.Block) int {
|
||||
// Don't fail if required attributes are not set. Instead, we'll just
|
||||
// hash them as nulls.
|
||||
schema = schema.NoneRequired()
|
||||
spec := schema.DecoderSpec()
|
||||
val, _ := hcldec.Decode(b.Config, spec, nil)
|
||||
if val == cty.NilVal {
|
||||
val = cty.UnknownVal(schema.ImpliedType())
|
||||
}
|
||||
|
||||
toHash := cty.TupleVal([]cty.Value{
|
||||
cty.StringVal(b.Type),
|
||||
val,
|
||||
})
|
||||
|
||||
return toHash.Hash()
|
||||
}
|
||||
|
|
|
@ -113,3 +113,21 @@ func (c *Config) AllModules() []*Config {
|
|||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
// Descendent returns the descendent config that has the given path beneath
|
||||
// the receiver, or nil if there is no such module.
|
||||
//
|
||||
// The path traverses the static module tree, prior to any expansion to handle
|
||||
// count and for_each arguments.
|
||||
//
|
||||
// An empty path will just return the receiver, and is therefore pointless.
|
||||
func (c *Config) Descendent(path []string) *Config {
|
||||
current := c
|
||||
for _, name := range path {
|
||||
current = current.Children[name]
|
||||
if current == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
)
|
||||
|
||||
// ResourceAddress is a way of identifying an individual resource (or,
|
||||
|
@ -109,30 +109,35 @@ func (r *ResourceAddress) WholeModuleAddress() *ResourceAddress {
|
|||
}
|
||||
}
|
||||
|
||||
// MatchesConfig returns true if the receiver matches the given
|
||||
// configuration resource within the given configuration module.
|
||||
// MatchesManagedResourceConfig returns true if the receiver matches the given
|
||||
// configuration resource within the given _static_ module path. Note that
|
||||
// the module path in a resource address is a _dynamic_ module path, and
|
||||
// multiple dynamic resource paths may map to a single static path if
|
||||
// count and for_each are in use on module calls.
|
||||
//
|
||||
// Since resource configuration blocks represent all of the instances of
|
||||
// a multi-instance resource, the index of the address (if any) is not
|
||||
// considered.
|
||||
func (r *ResourceAddress) MatchesConfig(mod *module.Tree, rc *config.Resource) bool {
|
||||
func (r *ResourceAddress) MatchesManagedResourceConfig(path []string, rc *configs.ManagedResource) bool {
|
||||
if r.HasResourceSpec() {
|
||||
if r.Mode != rc.Mode || r.Type != rc.Type || r.Name != rc.Name {
|
||||
if r.Mode != config.ManagedResourceMode {
|
||||
return false
|
||||
}
|
||||
if r.Type != rc.Type || r.Name != rc.Name {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
addrPath := r.Path
|
||||
cfgPath := mod.Path()
|
||||
|
||||
// normalize
|
||||
if len(addrPath) == 0 {
|
||||
addrPath = nil
|
||||
}
|
||||
if len(cfgPath) == 0 {
|
||||
cfgPath = nil
|
||||
if len(path) == 0 {
|
||||
path = nil
|
||||
}
|
||||
return reflect.DeepEqual(addrPath, cfgPath)
|
||||
return reflect.DeepEqual(addrPath, path)
|
||||
}
|
||||
|
||||
// stateId returns the ID that this resource should be entered with
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
)
|
||||
|
||||
func TestParseResourceAddressInternal(t *testing.T) {
|
||||
|
@ -1046,16 +1046,17 @@ func TestResourceAddressWholeModuleAddress(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestResourceAddressMatchesConfig(t *testing.T) {
|
||||
root := testModule(t, "empty-with-child-module")
|
||||
child := root.Child([]string{"child"})
|
||||
grandchild := root.Child([]string{"child", "grandchild"})
|
||||
func TestResourceAddressMatchesManagedResourceConfig(t *testing.T) {
|
||||
root := []string(nil)
|
||||
child := []string{"child"}
|
||||
grandchild := []string{"child", "grandchild"}
|
||||
irrelevant := []string{"irrelevant"}
|
||||
|
||||
tests := []struct {
|
||||
Addr *ResourceAddress
|
||||
Module *module.Tree
|
||||
Resource *config.Resource
|
||||
Want bool
|
||||
Addr *ResourceAddress
|
||||
ModulePath []string
|
||||
Resource *configs.ManagedResource
|
||||
Want bool
|
||||
}{
|
||||
{
|
||||
&ResourceAddress{
|
||||
|
@ -1065,8 +1066,7 @@ func TestResourceAddressMatchesConfig(t *testing.T) {
|
|||
Index: -1,
|
||||
},
|
||||
root,
|
||||
&config.Resource{
|
||||
Mode: config.ManagedResourceMode,
|
||||
&configs.ManagedResource{
|
||||
Type: "null_resource",
|
||||
Name: "baz",
|
||||
},
|
||||
|
@ -1081,8 +1081,7 @@ func TestResourceAddressMatchesConfig(t *testing.T) {
|
|||
Index: -1,
|
||||
},
|
||||
child,
|
||||
&config.Resource{
|
||||
Mode: config.ManagedResourceMode,
|
||||
&configs.ManagedResource{
|
||||
Type: "null_resource",
|
||||
Name: "baz",
|
||||
},
|
||||
|
@ -1097,8 +1096,7 @@ func TestResourceAddressMatchesConfig(t *testing.T) {
|
|||
Index: -1,
|
||||
},
|
||||
grandchild,
|
||||
&config.Resource{
|
||||
Mode: config.ManagedResourceMode,
|
||||
&configs.ManagedResource{
|
||||
Type: "null_resource",
|
||||
Name: "baz",
|
||||
},
|
||||
|
@ -1110,8 +1108,7 @@ func TestResourceAddressMatchesConfig(t *testing.T) {
|
|||
Index: -1,
|
||||
},
|
||||
child,
|
||||
&config.Resource{
|
||||
Mode: config.ManagedResourceMode,
|
||||
&configs.ManagedResource{
|
||||
Type: "null_resource",
|
||||
Name: "baz",
|
||||
},
|
||||
|
@ -1123,8 +1120,7 @@ func TestResourceAddressMatchesConfig(t *testing.T) {
|
|||
Index: -1,
|
||||
},
|
||||
grandchild,
|
||||
&config.Resource{
|
||||
Mode: config.ManagedResourceMode,
|
||||
&configs.ManagedResource{
|
||||
Type: "null_resource",
|
||||
Name: "baz",
|
||||
},
|
||||
|
@ -1137,9 +1133,8 @@ func TestResourceAddressMatchesConfig(t *testing.T) {
|
|||
Name: "baz",
|
||||
Index: -1,
|
||||
},
|
||||
module.NewEmptyTree(),
|
||||
&config.Resource{
|
||||
Mode: config.ManagedResourceMode,
|
||||
irrelevant,
|
||||
&configs.ManagedResource{
|
||||
Type: "null_resource",
|
||||
Name: "baz",
|
||||
},
|
||||
|
@ -1152,9 +1147,8 @@ func TestResourceAddressMatchesConfig(t *testing.T) {
|
|||
Name: "baz",
|
||||
Index: -1,
|
||||
},
|
||||
module.NewEmptyTree(),
|
||||
&config.Resource{
|
||||
Mode: config.ManagedResourceMode,
|
||||
irrelevant,
|
||||
&configs.ManagedResource{
|
||||
Type: "null_resource",
|
||||
Name: "pizza",
|
||||
},
|
||||
|
@ -1167,9 +1161,8 @@ func TestResourceAddressMatchesConfig(t *testing.T) {
|
|||
Name: "baz",
|
||||
Index: -1,
|
||||
},
|
||||
module.NewEmptyTree(),
|
||||
&config.Resource{
|
||||
Mode: config.ManagedResourceMode,
|
||||
irrelevant,
|
||||
&configs.ManagedResource{
|
||||
Type: "aws_instance",
|
||||
Name: "baz",
|
||||
},
|
||||
|
@ -1184,8 +1177,7 @@ func TestResourceAddressMatchesConfig(t *testing.T) {
|
|||
Index: -1,
|
||||
},
|
||||
child,
|
||||
&config.Resource{
|
||||
Mode: config.ManagedResourceMode,
|
||||
&configs.ManagedResource{
|
||||
Type: "null_resource",
|
||||
Name: "baz",
|
||||
},
|
||||
|
@ -1200,8 +1192,7 @@ func TestResourceAddressMatchesConfig(t *testing.T) {
|
|||
Index: -1,
|
||||
},
|
||||
grandchild,
|
||||
&config.Resource{
|
||||
Mode: config.ManagedResourceMode,
|
||||
&configs.ManagedResource{
|
||||
Type: "null_resource",
|
||||
Name: "baz",
|
||||
},
|
||||
|
@ -1211,11 +1202,11 @@ func TestResourceAddressMatchesConfig(t *testing.T) {
|
|||
|
||||
for i, test := range tests {
|
||||
t.Run(fmt.Sprintf("%02d-%s", i, test.Addr), func(t *testing.T) {
|
||||
got := test.Addr.MatchesConfig(test.Module, test.Resource)
|
||||
got := test.Addr.MatchesManagedResourceConfig(test.ModulePath, test.Resource)
|
||||
if got != test.Want {
|
||||
t.Errorf(
|
||||
"wrong result\naddr: %s\nmod: %#v\nrsrc: %#v\ngot: %#v\nwant: %#v",
|
||||
test.Addr, test.Module.Path(), test.Resource, got, test.Want,
|
||||
test.Addr, test.ModulePath, test.Resource, got, test.Want,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -20,9 +20,12 @@ import (
|
|||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/mitchellh/copystructure"
|
||||
|
||||
"github.com/hashicorp/terraform/config/configschema"
|
||||
tfversion "github.com/hashicorp/terraform/version"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
|
||||
"github.com/mitchellh/copystructure"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -811,13 +814,9 @@ func (s *State) String() string {
|
|||
|
||||
// BackendState stores the configuration to connect to a remote backend.
|
||||
type BackendState struct {
|
||||
Type string `json:"type"` // Backend type
|
||||
Config map[string]interface{} `json:"config"` // Backend raw config
|
||||
|
||||
// Hash is the hash code to uniquely identify the original source
|
||||
// configuration. We use this to detect when there is a change in
|
||||
// configuration even when "type" isn't changed.
|
||||
Hash uint64 `json:"hash"`
|
||||
Type string `json:"type"` // Backend type
|
||||
ConfigRaw json.RawMessage `json:"config"` // Backend raw config
|
||||
Hash int `json:"hash"` // Hash of portion of configuration from config files
|
||||
}
|
||||
|
||||
// Empty returns true if BackendState has no state.
|
||||
|
@ -825,25 +824,29 @@ func (s *BackendState) Empty() bool {
|
|||
return s == nil || s.Type == ""
|
||||
}
|
||||
|
||||
// Rehash returns a unique content hash for this backend's configuration
|
||||
// as a uint64 value.
|
||||
// The Hash stored in the backend state needs to match the config itself, but
|
||||
// we need to compare the backend config after it has been combined with all
|
||||
// options.
|
||||
// This function must match the implementation used by config.Backend.
|
||||
func (s *BackendState) Rehash() uint64 {
|
||||
if s == nil {
|
||||
return 0
|
||||
}
|
||||
// Config decodes the type-specific configuration object using the provided
|
||||
// schema and returns the result as a cty.Value.
|
||||
//
|
||||
// An error is returned if the stored configuration does not conform to the
|
||||
// given schema.
|
||||
func (s *BackendState) Config(schema *configschema.Block) (cty.Value, error) {
|
||||
ty := schema.ImpliedType()
|
||||
return ctyjson.Unmarshal(s.ConfigRaw, ty)
|
||||
}
|
||||
|
||||
cfg := config.Backend{
|
||||
Type: s.Type,
|
||||
RawConfig: &config.RawConfig{
|
||||
Raw: s.Config,
|
||||
},
|
||||
// SetConfig replaces (in-place) the type-specific configuration object using
|
||||
// the provided value and associated schema.
|
||||
//
|
||||
// An error is returned if the given value does not conform to the implied
|
||||
// type of the schema.
|
||||
func (s *BackendState) SetConfig(val cty.Value, schema *configschema.Block) error {
|
||||
ty := schema.ImpliedType()
|
||||
buf, err := ctyjson.Marshal(val, ty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cfg.Rehash()
|
||||
s.ConfigRaw = buf
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoteState is used to track the information about a remote
|
||||
|
|
Loading…
Reference in New Issue