mirror of https://github.com/hashicorp/terraform
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.pull/19086/head
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))
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
Loading…
Reference in new issue