github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/configs/configload/loader.go (about) 1 package configload 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/terraform-plugin-sdk/internal/configs" 7 "github.com/hashicorp/terraform-plugin-sdk/internal/registry" 8 "github.com/hashicorp/terraform-svchost/disco" 9 "github.com/spf13/afero" 10 ) 11 12 // A Loader instance is the main entry-point for loading configurations via 13 // this package. 14 // 15 // It extends the general config-loading functionality in the parent package 16 // "configs" to support installation of modules from remote sources and 17 // loading full configurations using modules that were previously installed. 18 type Loader struct { 19 // parser is used to read configuration 20 parser *configs.Parser 21 22 // modules is used to install and locate descendent modules that are 23 // referenced (directly or indirectly) from the root module. 24 modules moduleMgr 25 } 26 27 // Config is used with NewLoader to specify configuration arguments for the 28 // loader. 29 type Config struct { 30 // ModulesDir is a path to a directory where descendent modules are 31 // (or should be) installed. (This is usually the 32 // .terraform/modules directory, in the common case where this package 33 // is being loaded from the main Terraform CLI package.) 34 ModulesDir string 35 36 // Services is the service discovery client to use when locating remote 37 // module registry endpoints. If this is nil then registry sources are 38 // not supported, which should be true only in specialized circumstances 39 // such as in tests. 40 Services *disco.Disco 41 } 42 43 // NewLoader creates and returns a loader that reads configuration from the 44 // real OS filesystem. 45 // 46 // The loader has some internal state about the modules that are currently 47 // installed, which is read from disk as part of this function. If that 48 // manifest cannot be read then an error will be returned. 49 func NewLoader(config *Config) (*Loader, error) { 50 fs := afero.NewOsFs() 51 parser := configs.NewParser(fs) 52 reg := registry.NewClient(config.Services, nil) 53 54 ret := &Loader{ 55 parser: parser, 56 modules: moduleMgr{ 57 FS: afero.Afero{Fs: fs}, 58 CanInstall: true, 59 Dir: config.ModulesDir, 60 Services: config.Services, 61 Registry: reg, 62 }, 63 } 64 65 err := ret.modules.readModuleManifestSnapshot() 66 if err != nil { 67 return nil, fmt.Errorf("failed to read module manifest: %s", err) 68 } 69 70 return ret, nil 71 } 72 73 // ModulesDir returns the path to the directory where the loader will look for 74 // the local cache of remote module packages. 75 func (l *Loader) ModulesDir() string { 76 return l.modules.Dir 77 } 78 79 // RefreshModules updates the in-memory cache of the module manifest from the 80 // module manifest file on disk. This is not necessary in normal use because 81 // module installation and configuration loading are separate steps, but it 82 // can be useful in tests where module installation is done as a part of 83 // configuration loading by a helper function. 84 // 85 // Call this function after any module installation where an existing loader 86 // is already alive and may be used again later. 87 // 88 // An error is returned if the manifest file cannot be read. 89 func (l *Loader) RefreshModules() error { 90 if l == nil { 91 // Nothing to do, then. 92 return nil 93 } 94 return l.modules.readModuleManifestSnapshot() 95 } 96 97 // Sources returns the source code cache for the underlying parser of this 98 // loader. This is a shorthand for l.Parser().Sources(). 99 func (l *Loader) Sources() map[string][]byte { 100 return l.parser.Sources() 101 }