diff --git a/configs/configload/loader.go b/configs/configload/loader.go index fba244173..06ff27400 100644 --- a/configs/configload/loader.go +++ b/configs/configload/loader.go @@ -59,11 +59,12 @@ func NewLoader(config *Config) (*Loader, error) { ret := &Loader{ parser: parser, modules: moduleMgr{ - FS: afero.Afero{fs}, - Dir: config.ModulesDir, - Services: config.Services, - Creds: config.Creds, - Registry: reg, + FS: afero.Afero{Fs: fs}, + CanInstall: true, + Dir: config.ModulesDir, + Services: config.Services, + Creds: config.Creds, + Registry: reg, }, } diff --git a/configs/configload/loader_install.go b/configs/configload/loader_install.go index 41ef4224d..b3e015239 100644 --- a/configs/configload/loader_install.go +++ b/configs/configload/loader_install.go @@ -38,7 +38,16 @@ import ( // may have wholly or partially completed. Modules must be loaded in order // to find their dependencies, so this function does many of the same checks // as LoadConfig as a side-effect. +// +// This function will panic if called on a loader that cannot install modules. +// Use CanInstallModules to determine if a loader can install modules, or +// refer to the documentation for that method for situations where module +// installation capability is guaranteed. func (l *Loader) InstallModules(rootDir string, upgrade bool, hooks InstallHooks) hcl.Diagnostics { + if !l.CanInstallModules() { + panic(fmt.Errorf("InstallModules called on loader that cannot install modules")) + } + rootMod, diags := l.parser.LoadConfigDir(rootDir) if rootMod == nil { return diags @@ -183,6 +192,16 @@ func (l *Loader) InstallModules(rootDir string, upgrade bool, hooks InstallHooks return diags } +// CanInstallModules returns true if InstallModules can be used with this +// loader. +// +// Loaders created with NewLoader can always install modules. Loaders created +// from plan files (where the configuration is embedded in the plan file itself) +// cannot install modules, because the plan file is read-only. +func (l *Loader) CanInstallModules() bool { + return l.modules.CanInstall +} + func (l *Loader) installLocalModule(req *configs.ModuleRequest, key string, hooks InstallHooks) (*configs.Module, hcl.Diagnostics) { var diags hcl.Diagnostics diff --git a/configs/configload/module_mgr.go b/configs/configload/module_mgr.go index 5856c2ec0..ef17fda7a 100644 --- a/configs/configload/module_mgr.go +++ b/configs/configload/module_mgr.go @@ -10,6 +10,13 @@ import ( type moduleMgr struct { FS afero.Afero + // CanInstall is true for a module manager that can support installation. + // + // This must be set only if FS is an afero.OsFs, because the installer + // (which uses go-getter) is not aware of the virtual filesystem + // abstraction and will always write into the "real" filesystem. + CanInstall bool + // Dir is the path where descendent modules are (or will be) installed. Dir string