diff --git a/command/apply.go b/command/apply.go index 1e49739a0..9c8c4904a 100644 --- a/command/apply.go +++ b/command/apply.go @@ -132,10 +132,15 @@ func (c *ApplyCommand) Run(args []string) int { } */ + var conf *config.Config + if mod != nil { + conf = mod.Config() + } + // Load the backend b, err := c.Backend(&BackendOpts{ - ConfigPath: configPath, - Plan: plan, + Config: conf, + Plan: plan, }) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) diff --git a/command/console.go b/command/console.go index 4a6420b77..cd73ffe49 100644 --- a/command/console.go +++ b/command/console.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/helper/wrappedstreams" "github.com/hashicorp/terraform/repl" @@ -43,8 +44,16 @@ func (c *ConsoleCommand) Run(args []string) int { return 1 } + var conf *config.Config + if mod != nil { + conf = mod.Config() + } + // Load the backend - b, err := c.Backend(&BackendOpts{ConfigPath: configPath}) + b, err := c.Backend(&BackendOpts{ + Config: conf, + }) + if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) return 1 diff --git a/command/env_delete.go b/command/env_delete.go index a0fb01fea..3975f9157 100644 --- a/command/env_delete.go +++ b/command/env_delete.go @@ -43,8 +43,17 @@ func (c *EnvDeleteCommand) 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)) + return 1 + } + // Load the backend - b, err := c.Backend(&BackendOpts{ConfigPath: configPath}) + b, err := c.Backend(&BackendOpts{ + Config: cfg, + }) + if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) return 1 diff --git a/command/env_list.go b/command/env_list.go index 12d768e80..56d448d29 100644 --- a/command/env_list.go +++ b/command/env_list.go @@ -26,8 +26,17 @@ func (c *EnvListCommand) 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)) + return 1 + } + // Load the backend - b, err := c.Backend(&BackendOpts{ConfigPath: configPath}) + b, err := c.Backend(&BackendOpts{ + Config: cfg, + }) + if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) return 1 diff --git a/command/env_new.go b/command/env_new.go index 04c52a6d2..1b2c13a67 100644 --- a/command/env_new.go +++ b/command/env_new.go @@ -46,8 +46,16 @@ func (c *EnvNewCommand) 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)) + } + // Load the backend - b, err := c.Backend(&BackendOpts{ConfigPath: configPath}) + b, err := c.Backend(&BackendOpts{ + Config: conf, + }) + if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) return 1 diff --git a/command/env_select.go b/command/env_select.go index e7bc8743e..d65660fb8 100644 --- a/command/env_select.go +++ b/command/env_select.go @@ -31,8 +31,17 @@ func (c *EnvSelectCommand) 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)) + return 1 + } + // Load the backend - b, err := c.Backend(&BackendOpts{ConfigPath: configPath}) + b, err := c.Backend(&BackendOpts{ + Config: conf, + }) + if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) return 1 diff --git a/command/graph.go b/command/graph.go index 45299787a..befe5b329 100644 --- a/command/graph.go +++ b/command/graph.go @@ -6,6 +6,7 @@ import ( "strings" "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" @@ -62,10 +63,15 @@ func (c *GraphCommand) Run(args []string) int { } } + var conf *config.Config + if mod != nil { + conf = mod.Config() + } + // Load the backend b, err := c.Backend(&BackendOpts{ - ConfigPath: configPath, - Plan: plan, + Config: conf, + Plan: plan, }) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) diff --git a/command/import.go b/command/import.go index 1636ab9d9..a2f4d6c62 100644 --- a/command/import.go +++ b/command/import.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config/module" "github.com/hashicorp/terraform/terraform" ) @@ -60,8 +61,15 @@ func (c *ImportCommand) Run(args []string) int { } } + var conf *config.Config + if mod != nil { + conf = mod.Config() + } + // Load the backend - b, err := c.Backend(&BackendOpts{ConfigPath: configPath}) + b, err := c.Backend(&BackendOpts{ + Config: conf, + }) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) return 1 diff --git a/command/init.go b/command/init.go index ff0aadc9d..3023d9f14 100644 --- a/command/init.go +++ b/command/init.go @@ -106,11 +106,7 @@ 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 { - // Load the configuration in this directory so that we can know - // if we have anything to get or any backend to configure. We do - // this to improve the UX. Practically, we could call the functions - // below without checking this to the same effect. - conf, err := config.LoadDir(path) + conf, err := c.Config(path) if err != nil { c.Ui.Error(fmt.Sprintf( "Error loading configuration: %s", err)) @@ -145,7 +141,7 @@ func (c *InitCommand) Run(args []string) int { } opts := &BackendOpts{ - ConfigPath: path, + Config: conf, ConfigExtra: flagConfigExtra, Init: true, } diff --git a/command/meta_backend.go b/command/meta_backend.go index b57999cec..112d8ccc1 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -9,11 +9,9 @@ import ( "fmt" "io/ioutil" "log" - "os" "path/filepath" "strings" - "github.com/hashicorp/errwrap" "github.com/hashicorp/go-multierror" "github.com/hashicorp/hcl" "github.com/hashicorp/terraform/backend" @@ -29,9 +27,9 @@ import ( // BackendOpts are the options used to initialize a backend.Backend. type BackendOpts struct { - // ConfigPath is a path to a file or directory containing the backend - // configuration (declaration). - ConfigPath string + // Module is the root module from which we will extract the terraform and + // backend configuration. + Config *config.Config // ConfigFile is a path to a file that contains configuration that // is merged directly into the backend configuration when loaded @@ -178,71 +176,34 @@ func (m *Meta) Operation() *backend.Operation { // backendConfig returns the local configuration for the backend func (m *Meta) backendConfig(opts *BackendOpts) (*config.Backend, error) { - // If no explicit path was given then it is okay for there to be - // no backend configuration found. - emptyOk := opts.ConfigPath == "" - - // Determine the path to the configuration. - path := opts.ConfigPath - - // If we had no path set, it is an error. We can't initialize unset - if path == "" { - path = "." - } - - // Expand the path - if !filepath.IsAbs(path) { - var err error - path, err = filepath.Abs(path) + if opts.Config == nil { + // check if the config was missing, or just not required + conf, err := m.Config(".") if err != nil { - return nil, fmt.Errorf( - "Error expanding path to backend config %q: %s", path, err) + return nil, err } - } - log.Printf("[DEBUG] command: loading backend config file: %s", path) - - // We first need to determine if we're loading a file or a directory. - fi, err := os.Stat(path) - if err != nil { - if os.IsNotExist(err) && emptyOk { - log.Printf( - "[INFO] command: backend config not found, returning nil: %s", - path) + if conf == nil { + log.Println("[INFO] command: no config, returning nil") return nil, nil } - return nil, err + log.Println("[WARNING] BackendOpts.Config not set, but config found") + opts.Config = conf } - var f func(string) (*config.Config, error) = config.LoadFile - if fi.IsDir() { - f = config.LoadDir - } - - // Load the configuration - c, err := f(path) - if err != nil { - // Check for the error where we have no config files and return nil - // as the configuration type. - if errwrap.ContainsType(err, new(config.ErrNoConfigsFound)) { - log.Printf( - "[INFO] command: backend config not found, returning nil: %s", - path) - return nil, nil - } - - return nil, err - } + c := opts.Config // If there is no Terraform configuration block, no backend config if c.Terraform == nil { + log.Println("[INFO] command: empty terraform config, returning nil") return nil, nil } // Get the configuration for the backend itself. backend := c.Terraform.Backend if backend == nil { + log.Println("[INFO] command: empty backend config, returning nil") return nil, nil } diff --git a/command/meta_new.go b/command/meta_new.go index 3138cd2df..a1d376b34 100644 --- a/command/meta_new.go +++ b/command/meta_new.go @@ -2,7 +2,9 @@ package command import ( "fmt" + "log" "os" + "path/filepath" "strconv" "github.com/hashicorp/errwrap" @@ -51,6 +53,66 @@ func (m *Meta) Module(path string) (*module.Tree, error) { return mod, nil } +// Config loads the root config for the path specified. Path may be a directory +// or file. The absence of configuration is not an error and returns a nil Config. +func (m *Meta) Config(path string) (*config.Config, error) { + // If no explicit path was given then it is okay for there to be + // no backend configuration found. + emptyOk := path == "" + + // If we had no path set, it is an error. We can't initialize unset + if path == "" { + path = "." + } + + // Expand the path + if !filepath.IsAbs(path) { + var err error + path, err = filepath.Abs(path) + if err != nil { + return nil, fmt.Errorf( + "Error expanding path to backend config %q: %s", path, err) + } + } + + log.Printf("[DEBUG] command: loading backend config file: %s", path) + + // We first need to determine if we're loading a file or a directory. + fi, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) && emptyOk { + log.Printf( + "[INFO] command: backend config not found, returning nil: %s", + path) + return nil, nil + } + + return nil, err + } + + var f func(string) (*config.Config, error) = config.LoadFile + if fi.IsDir() { + f = config.LoadDir + } + + // Load the configuration + c, err := f(path) + if err != nil { + // Check for the error where we have no config files and return nil + // as the configuration type. + if errwrap.ContainsType(err, new(config.ErrNoConfigsFound)) { + log.Printf( + "[INFO] command: backend config not found, returning nil: %s", + path) + return nil, nil + } + + return nil, err + } + + return c, nil +} + // Plan returns the plan for the given path. // // This only has an effect if the path itself looks like a plan. diff --git a/command/plan.go b/command/plan.go index 0b66fdffd..a38a22872 100644 --- a/command/plan.go +++ b/command/plan.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config/module" ) @@ -68,10 +69,14 @@ func (c *PlanCommand) Run(args []string) int { } } + var conf *config.Config + if mod != nil { + conf = mod.Config() + } // Load the backend b, err := c.Backend(&BackendOpts{ - ConfigPath: configPath, - Plan: plan, + Config: conf, + Plan: plan, }) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) diff --git a/command/providers.go b/command/providers.go index 23221603f..03bf6feae 100644 --- a/command/providers.go +++ b/command/providers.go @@ -52,7 +52,9 @@ func (c *ProvidersCommand) Run(args []string) int { } // Load the backend - b, err := c.Backend(&BackendOpts{ConfigPath: configPath}) + b, err := c.Backend(&BackendOpts{ + Config: root.Config(), + }) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) return 1 diff --git a/command/push.go b/command/push.go index 3a4c2060e..564889210 100644 --- a/command/push.go +++ b/command/push.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/atlas-go/archive" "github.com/hashicorp/atlas-go/v1" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/terraform" ) @@ -98,9 +99,14 @@ func (c *PushCommand) Run(args []string) int { return 1 } + var conf *config.Config + if mod != nil { + conf = mod.Config() + } + // Load the backend b, err := c.Backend(&BackendOpts{ - ConfigPath: configPath, + Config: conf, }) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) diff --git a/command/refresh.go b/command/refresh.go index 3f1b8bf28..72623ed45 100644 --- a/command/refresh.go +++ b/command/refresh.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/terraform" ) @@ -43,8 +44,15 @@ func (c *RefreshCommand) Run(args []string) int { return 1 } + var conf *config.Config + if mod != nil { + conf = mod.Config() + } + // Load the backend - b, err := c.Backend(&BackendOpts{ConfigPath: configPath}) + b, err := c.Backend(&BackendOpts{ + Config: conf, + }) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) return 1 diff --git a/command/unlock.go b/command/unlock.go index 666e4f346..acaf3307f 100644 --- a/command/unlock.go +++ b/command/unlock.go @@ -43,9 +43,15 @@ 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)) + return 1 + } + // Load the backend b, err := c.Backend(&BackendOpts{ - ConfigPath: configPath, + Config: conf, }) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err)) diff --git a/plugin/getter/get.go b/plugin/getter/get.go new file mode 100644 index 000000000..e69de29bb