7 "github.com/hashicorp/terraform/configs"
8 "github.com/hashicorp/terraform/registry"
9 "github.com/hashicorp/terraform/svchost/disco"
10 "github.com/spf13/afero"
13 // A Loader instance is the main entry-point for loading configurations via
16 // It extends the general config-loading functionality in the parent package
17 // "configs" to support installation of modules from remote sources and
18 // loading full configurations using modules that were previously installed.
20 // parser is used to read configuration
21 parser *configs.Parser
23 // modules is used to install and locate descendent modules that are
24 // referenced (directly or indirectly) from the root module.
28 // Config is used with NewLoader to specify configuration arguments for the
31 // ModulesDir is a path to a directory where descendent modules are
32 // (or should be) installed. (This is usually the
33 // .terraform/modules directory, in the common case where this package
34 // is being loaded from the main Terraform CLI package.)
37 // Services is the service discovery client to use when locating remote
38 // module registry endpoints. If this is nil then registry sources are
39 // not supported, which should be true only in specialized circumstances
44 // NewLoader creates and returns a loader that reads configuration from the
45 // real OS filesystem.
47 // The loader has some internal state about the modules that are currently
48 // installed, which is read from disk as part of this function. If that
49 // manifest cannot be read then an error will be returned.
50 func NewLoader(config *Config) (*Loader, error) {
52 parser := configs.NewParser(fs)
53 reg := registry.NewClient(config.Services, nil)
58 FS: afero.Afero{Fs: fs},
60 Dir: config.ModulesDir,
61 Services: config.Services,
66 err := ret.modules.readModuleManifestSnapshot()
68 return nil, fmt.Errorf("failed to read module manifest: %s", err)
74 // ModulesDir returns the path to the directory where the loader will look for
75 // the local cache of remote module packages.
76 func (l *Loader) ModulesDir() string {
80 // RefreshModules updates the in-memory cache of the module manifest from the
81 // module manifest file on disk. This is not necessary in normal use because
82 // module installation and configuration loading are separate steps, but it
83 // can be useful in tests where module installation is done as a part of
84 // configuration loading by a helper function.
86 // Call this function after any module installation where an existing loader
87 // is already alive and may be used again later.
89 // An error is returned if the manifest file cannot be read.
90 func (l *Loader) RefreshModules() error {
92 // Nothing to do, then.
95 return l.modules.readModuleManifestSnapshot()
98 // Parser returns the underlying parser for this loader.
100 // This is useful for loading other sorts of files than the module directories
101 // that a loader deals with, since then they will share the source code cache
102 // for this loader and can thus be shown as snippets in diagnostic messages.
103 func (l *Loader) Parser() *configs.Parser {
107 // Sources returns the source code cache for the underlying parser of this
108 // loader. This is a shorthand for l.Parser().Sources().
109 func (l *Loader) Sources() map[string][]byte {
110 return l.parser.Sources()
113 // IsConfigDir returns true if and only if the given directory contains at
114 // least one Terraform configuration file. This is a wrapper around calling
115 // the same method name on the loader's parser.
116 func (l *Loader) IsConfigDir(path string) bool {
117 return l.parser.IsConfigDir(path)
120 // ImportSources writes into the receiver's source code the given source
123 // This is useful in the situation where an ancillary loader is created for
124 // some reason (e.g. loading config from a plan file) but the cached source
125 // code from that loader must be imported into the "main" loader in order
126 // to return source code snapshots in diagnostic messages.
128 // loader.ImportSources(otherLoader.Sources())
129 func (l *Loader) ImportSources(sources map[string][]byte) {
131 for name, src := range sources {
132 p.ForceFileSource(name, src)
136 // ImportSourcesFromSnapshot writes into the receiver's source code the
137 // source files from the given snapshot.
139 // This is similar to ImportSources but knows how to unpack and flatten a
140 // snapshot data structure to get the corresponding flat source file map.
141 func (l *Loader) ImportSourcesFromSnapshot(snap *Snapshot) {
143 for _, m := range snap.Modules {
145 for fn, src := range m.Files {
146 fullPath := filepath.Join(baseDir, fn)
147 p.ForceFileSource(fullPath, src)