command: new Meta methods for accessing the new config loader
We need to share a single config loader across all callers because that allows us to maintain the source code cache we'll use for snippets in error messages. Nothing calls this yet. Callers will be gradually updated away from Module and Config in subsequent commits.
This commit is contained in:
parent
bd10b84a8e
commit
fd5b7b42b8
|
@ -0,0 +1,33 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
version "github.com/hashicorp/go-version"
|
||||
"github.com/hashicorp/terraform/configs/configload"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
type uiModuleInstallHooks struct {
|
||||
configload.InstallHooksImpl
|
||||
Ui cli.Ui
|
||||
ShowLocalPaths bool
|
||||
}
|
||||
|
||||
var _ configload.InstallHooks = uiModuleInstallHooks{}
|
||||
|
||||
func (h uiModuleInstallHooks) Download(modulePath, packageAddr string, v *version.Version) {
|
||||
if v != nil {
|
||||
h.Ui.Info(fmt.Sprintf("Downloading %s %s for %s...", packageAddr, v, modulePath))
|
||||
} else {
|
||||
h.Ui.Info(fmt.Sprintf("Downloading %s for %s...", packageAddr, modulePath))
|
||||
}
|
||||
}
|
||||
|
||||
func (h uiModuleInstallHooks) Install(modulePath string, v *version.Version, localDir string) {
|
||||
if h.ShowLocalPaths {
|
||||
h.Ui.Info(fmt.Sprintf("- %s in %s", modulePath, localDir))
|
||||
} else {
|
||||
h.Ui.Info(fmt.Sprintf("- %s", modulePath))
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/hashicorp/terraform/command/format"
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/configs/configload"
|
||||
"github.com/hashicorp/terraform/helper/experiment"
|
||||
"github.com/hashicorp/terraform/helper/variables"
|
||||
"github.com/hashicorp/terraform/helper/wrappedstreams"
|
||||
|
@ -98,6 +99,11 @@ type Meta struct {
|
|||
// Private: do not set these
|
||||
//----------------------------------------------------------
|
||||
|
||||
// configLoader is a shared configuration loader that is used by
|
||||
// LoadConfig and other commands that access configuration files.
|
||||
// It is initialized on first use.
|
||||
configLoader *configload.Loader
|
||||
|
||||
// backendState is the currently active backend state
|
||||
backendState *terraform.BackendState
|
||||
|
||||
|
@ -542,7 +548,7 @@ func (m *Meta) showDiagnostics(vals ...interface{}) {
|
|||
// For now, we don't have easy access to the writer that
|
||||
// ui.Error (etc) are writing to and thus can't interrogate
|
||||
// to see if it's a terminal and what size it is.
|
||||
msg := format.Diagnostic(diag, nil, m.Colorize(), 78)
|
||||
msg := format.Diagnostic(diag, m.configSources(), m.Colorize(), 78)
|
||||
switch diag.Severity() {
|
||||
case tfdiags.Error:
|
||||
m.Ui.Error(msg)
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
"github.com/hashicorp/terraform/configs/configload"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// normalizePath normalizes a given path so that it is, if possible, relative
|
||||
// to the current working directory. This is primarily used to prepare
|
||||
// paths used to load configuration, because we want to prefer recording
|
||||
// relative paths in source code references within the configuration.
|
||||
func (m *Meta) normalizePath(path string) string {
|
||||
var err error
|
||||
|
||||
// First we will make it absolute so that we have a consistent place
|
||||
// to start.
|
||||
path, err = filepath.Abs(path)
|
||||
if err != nil {
|
||||
// We'll just accept what we were given, then.
|
||||
return path
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil || !filepath.IsAbs(cwd) {
|
||||
return path
|
||||
}
|
||||
|
||||
ret, err := filepath.Rel(cwd, path)
|
||||
if err != nil {
|
||||
return path
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// loadConfig reads a configuration from the given directory, which should
|
||||
// contain a root module and have already have any required descendent modules
|
||||
// installed.
|
||||
func (m *Meta) loadConfig(rootDir string) (*configs.Config, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
rootDir = m.normalizePath(rootDir)
|
||||
|
||||
loader, err := m.initConfigLoader()
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
config, hclDiags := loader.LoadConfig(rootDir)
|
||||
diags = diags.Append(hclDiags)
|
||||
return config, diags
|
||||
}
|
||||
|
||||
// loadSingleModule reads configuration from the given directory and returns
|
||||
// a description of that module only, without attempting to assemble a module
|
||||
// tree for referenced child modules.
|
||||
//
|
||||
// Most callers should use loadConfig. This method exists to support early
|
||||
// initialization use-cases where the root module must be inspected in order
|
||||
// to determine what else needs to be installed before the full configuration
|
||||
// can be used.
|
||||
func (m *Meta) loadSingleModule(dir string) (*configs.Module, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
dir = m.normalizePath(dir)
|
||||
|
||||
loader, err := m.initConfigLoader()
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
module, hclDiags := loader.Parser().LoadConfigDir(dir)
|
||||
diags = diags.Append(hclDiags)
|
||||
return module, diags
|
||||
}
|
||||
|
||||
// installModules reads a root module from the given directory and attempts
|
||||
// recursively install all of its descendent modules.
|
||||
//
|
||||
// The given hooks object will be notified of installation progress, which
|
||||
// can then be relayed to the end-user. The moduleUiInstallHooks type in
|
||||
// this package has a reasonable implementation for displaying notifications
|
||||
// via a provided cli.Ui.
|
||||
func (m *Meta) installModules(rootDir string, upgrade bool, hooks configload.InstallHooks) tfdiags.Diagnostics {
|
||||
var diags tfdiags.Diagnostics
|
||||
rootDir = m.normalizePath(rootDir)
|
||||
|
||||
err := os.MkdirAll(m.modulesDir(), os.ModePerm)
|
||||
if err != nil {
|
||||
diags = diags.Append(fmt.Errorf("failed to create local modules directory: %s", err))
|
||||
return diags
|
||||
}
|
||||
|
||||
loader, err := m.initConfigLoader()
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
return diags
|
||||
}
|
||||
|
||||
hclDiags := loader.InstallModules(rootDir, upgrade, hooks)
|
||||
diags = diags.Append(hclDiags)
|
||||
return diags
|
||||
}
|
||||
|
||||
// initDirFromModule initializes the given directory (which should be
|
||||
// pre-verified as empty by the caller) by copying the source code from the
|
||||
// given module address.
|
||||
//
|
||||
// Internally this runs similar steps to installModules.
|
||||
// The given hooks object will be notified of installation progress, which
|
||||
// can then be relayed to the end-user. The moduleUiInstallHooks type in
|
||||
// this package has a reasonable implementation for displaying notifications
|
||||
// via a provided cli.Ui.
|
||||
func (m *Meta) initDirFromModule(targetDir string, addr string, hooks configload.InstallHooks) tfdiags.Diagnostics {
|
||||
var diags tfdiags.Diagnostics
|
||||
targetDir = m.normalizePath(targetDir)
|
||||
|
||||
loader, err := m.initConfigLoader()
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
return diags
|
||||
}
|
||||
|
||||
hclDiags := loader.InitDirFromModule(targetDir, addr, hooks)
|
||||
diags = diags.Append(hclDiags)
|
||||
return diags
|
||||
}
|
||||
|
||||
// loadVarsFile reads a file from the given path and interprets it as a
|
||||
// "vars file", returning the contained values as a map.
|
||||
//
|
||||
// The file is read using the parser associated with the receiver's
|
||||
// configuration loader, which means that the file's contents will be added
|
||||
// to the source cache that is used for config snippets in diagnostic messages.
|
||||
func (m *Meta) loadVarsFile(filename string) (map[string]cty.Value, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
loader, err := m.initConfigLoader()
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
parser := loader.Parser()
|
||||
ret, hclDiags := parser.LoadValuesFile(filename)
|
||||
diags = diags.Append(hclDiags)
|
||||
return ret, diags
|
||||
}
|
||||
|
||||
// configSources returns the source cache from the receiver's config loader,
|
||||
// which the caller must not modify.
|
||||
//
|
||||
// If a config loader has not yet been instantiated then no files could have
|
||||
// been loaded already, so this method returns a nil map in that case.
|
||||
func (m *Meta) configSources() map[string][]byte {
|
||||
if m.configLoader == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return m.configLoader.Sources()
|
||||
}
|
||||
|
||||
func (m *Meta) modulesDir() string {
|
||||
return filepath.Join(m.DataDir(), "modules")
|
||||
}
|
||||
|
||||
// initConfigLoader initializes the shared configuration loader if it isn't
|
||||
// already initialized.
|
||||
//
|
||||
// If the loader cannot be created for some reason then an error is returned
|
||||
// and no loader is created. Subsequent calls will presumably see the same
|
||||
// error. Loader initialization errors will tend to prevent any further use
|
||||
// of most Terraform features, so callers should report any error and safely
|
||||
// terminate.
|
||||
func (m *Meta) initConfigLoader() (*configload.Loader, error) {
|
||||
if m.configLoader == nil {
|
||||
loader, err := configload.NewLoader(&configload.Config{
|
||||
ModulesDir: m.modulesDir(),
|
||||
Services: m.Services,
|
||||
Creds: m.Credentials,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.configLoader = loader
|
||||
}
|
||||
return m.configLoader, nil
|
||||
}
|
|
@ -31,7 +31,8 @@ func (m *Meta) Input() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Module loads the module tree for the given root path.
|
||||
// Module loads the module tree for the given root path using the legacy
|
||||
// configuration loader.
|
||||
//
|
||||
// It expects the modules to already be downloaded. This will never
|
||||
// download any modules.
|
||||
|
@ -65,8 +66,11 @@ func (m *Meta) Module(path string) (*module.Tree, tfdiags.Diagnostics) {
|
|||
return mod, diags
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Config loads the root config for the path specified, using the legacy
|
||||
// configuration loader.
|
||||
//
|
||||
// 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.
|
||||
|
|
Loading…
Reference in New Issue