handle install errs in installDescendentModules

This will allow us to use the same logic for `get` too.
This commit is contained in:
James Bardin 2023-08-07 14:54:28 -04:00
parent 09d91eb675
commit f6768de218
4 changed files with 62 additions and 46 deletions

View file

@ -166,35 +166,12 @@ func DirFromModule(ctx context.Context, loader *configload.Loader, rootDir, modu
}
fetcher := getmodules.NewPackageFetcher()
// When attempting to initialize the current directory with a module
// source, some use cases may want to ignore configuration errors from the
// building of the entire configuration structure, but we still need to
// capture installation errors. Because the actual module installation
// happens in the ModuleWalkFunc callback while building the config, we
// need to create a closure to capture the installation diagnostics
// separately.
var instDiags hcl.Diagnostics
w := inst.moduleInstallWalker(ctx, instManifest, true, wrapHooks, fetcher)
walker := configs.ModuleWalkerFunc(func(req *configs.ModuleRequest) (*configs.Module, *version.Version, hcl.Diagnostics) {
mod, version, diags := w.LoadModule(req)
instDiags = instDiags.Extend(diags)
return mod, version, diags
})
_, cDiags := inst.installDescendentModules(fakeRootModule, instManifest, walker)
// We can't continue if there was an error during installation, but return
// all diagnostics in case there happens to be anything else useful when
// debugging the problem.
if instDiags.HasErrors() {
walker := inst.moduleInstallWalker(ctx, instManifest, true, wrapHooks, fetcher)
_, cDiags := inst.installDescendentModules(fakeRootModule, instManifest, walker, true)
if cDiags.HasErrors() {
return diags.Append(cDiags)
}
// if there are errors here, they must be only from building the config
// structures. We don't want to block initialization at this point, so
// convert these into warnings. Any actual errors in the configuration will
// be raised as soon as the config is loaded again.
diags = append(diags, tfdiags.OverrideAll(cDiags, tfdiags.Warning, nil)...)
// If all of that succeeded then we'll now migrate what was installed
// into the final directory structure.
err = os.MkdirAll(modulesDir, os.ModePerm)

View file

@ -77,6 +77,12 @@ func NewModuleInstaller(modsDir string, loader *configload.Loader, reg *registry
// needs to replace a directory that is already present with a newly-extracted
// package.
//
// installErrsOnly installs modules but converts validation errors from
// building the configuration after installation to warnings. This is used by
// commands like `get` or `init -from-module` where the established behavior
// was only to install the requested module, and extra validation can break
// compatibility.
//
// If the returned diagnostics contains errors then the module installation
// 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
@ -85,7 +91,7 @@ func NewModuleInstaller(modsDir string, loader *configload.Loader, reg *registry
// If successful (the returned diagnostics contains no errors) then the
// first return value is the early configuration tree that was constructed by
// the installation process.
func (i *ModuleInstaller) InstallModules(ctx context.Context, rootDir, testsDir string, upgrade bool, hooks ModuleInstallHooks) (*configs.Config, tfdiags.Diagnostics) {
func (i *ModuleInstaller) InstallModules(ctx context.Context, rootDir, testsDir string, upgrade, installErrsOnly bool, hooks ModuleInstallHooks) (*configs.Config, tfdiags.Diagnostics) {
log.Printf("[TRACE] ModuleInstaller: installing child modules for %s into %s", rootDir, i.modsDir)
var diags tfdiags.Diagnostics
@ -129,7 +135,7 @@ func (i *ModuleInstaller) InstallModules(ctx context.Context, rootDir, testsDir
}
walker := i.moduleInstallWalker(ctx, manifest, upgrade, hooks, fetcher)
cfg, instDiags := i.installDescendentModules(rootMod, manifest, walker)
cfg, instDiags := i.installDescendentModules(rootMod, manifest, walker, installErrsOnly)
diags = append(diags, instDiags...)
return cfg, diags
@ -277,12 +283,45 @@ func (i *ModuleInstaller) moduleInstallWalker(ctx context.Context, manifest mods
)
}
func (i *ModuleInstaller) installDescendentModules(rootMod *configs.Module, manifest modsdir.Manifest, installWalker configs.ModuleWalker) (*configs.Config, tfdiags.Diagnostics) {
func (i *ModuleInstaller) installDescendentModules(rootMod *configs.Module, manifest modsdir.Manifest, installWalker configs.ModuleWalker, installErrsOnly bool) (*configs.Config, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
cfg, cDiags := configs.BuildConfig(rootMod, installWalker)
// When attempting to initialize the current directory with a module
// source, some use cases may want to ignore configuration errors from the
// building of the entire configuration structure, but we still need to
// capture installation errors. Because the actual module installation
// happens in the ModuleWalkFunc callback while building the config, we
// need to create a closure to capture the installation diagnostics
// separately.
var instDiags hcl.Diagnostics
walker := installWalker
if installErrsOnly {
walker = configs.ModuleWalkerFunc(func(req *configs.ModuleRequest) (*configs.Module, *version.Version, hcl.Diagnostics) {
mod, version, diags := installWalker.LoadModule(req)
instDiags = instDiags.Extend(diags)
return mod, version, diags
})
}
cfg, cDiags := configs.BuildConfig(rootMod, walker)
diags = diags.Append(cDiags)
if installErrsOnly {
// We can't continue if there was an error during installation, but
// return all diagnostics in case there happens to be anything else
// useful when debugging the problem. Any instDiags will be included in
// diags already.
if instDiags.HasErrors() {
return cfg, diags
}
// If there are any errors here, they must be only from building the
// config structures. We don't want to block initialization at this
// point, so convert these into warnings. Any actual errors in the
// configuration will be raised as soon as the config is loaded again.
// We continue below because writing the manifest is required to finish
// module installation.
diags = tfdiags.OverrideAll(diags, tfdiags.Warning, nil)
}
err := manifest.WriteSnapshotToDir(i.modsDir)
if err != nil {

View file

@ -46,7 +46,7 @@ func TestModuleInstaller(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, nil)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, hooks)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks)
assertNoDiagnostics(t, diags)
wantCalls := []testInstallHookCall{
@ -110,7 +110,7 @@ func TestModuleInstaller_error(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, nil)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, hooks)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks)
if !diags.HasErrors() {
t.Fatal("expected error")
@ -131,7 +131,7 @@ func TestModuleInstaller_emptyModuleName(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, nil)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, hooks)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks)
if !diags.HasErrors() {
t.Fatal("expected error")
@ -169,7 +169,7 @@ func TestModuleInstaller_packageEscapeError(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, nil)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, hooks)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks)
if !diags.HasErrors() {
t.Fatal("expected error")
@ -207,7 +207,7 @@ func TestModuleInstaller_explicitPackageBoundary(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, nil)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, hooks)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks)
if diags.HasErrors() {
t.Fatalf("unexpected errors\n%s", diags.Err().Error())
@ -230,7 +230,7 @@ func TestModuleInstaller_ExactMatchPrerelease(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(nil, nil))
cfg, diags := inst.InstallModules(context.Background(), ".", "tests", false, hooks)
cfg, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks)
if diags.HasErrors() {
t.Fatalf("found unexpected errors: %s", diags.Err())
@ -257,7 +257,7 @@ func TestModuleInstaller_PartialMatchPrerelease(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(nil, nil))
cfg, diags := inst.InstallModules(context.Background(), ".", "tests", false, hooks)
cfg, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks)
if diags.HasErrors() {
t.Fatalf("found unexpected errors: %s", diags.Err())
@ -280,7 +280,7 @@ func TestModuleInstaller_invalid_version_constraint_error(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, nil)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, hooks)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks)
if !diags.HasErrors() {
t.Fatal("expected error")
@ -306,7 +306,7 @@ func TestModuleInstaller_invalidVersionConstraintGetter(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, nil)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, hooks)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks)
if !diags.HasErrors() {
t.Fatal("expected error")
@ -332,7 +332,7 @@ func TestModuleInstaller_invalidVersionConstraintLocal(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, nil)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, hooks)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks)
if !diags.HasErrors() {
t.Fatal("expected error")
@ -358,7 +358,7 @@ func TestModuleInstaller_symlink(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, nil)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, hooks)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks)
assertNoDiagnostics(t, diags)
wantCalls := []testInstallHookCall{
@ -434,7 +434,7 @@ func TestLoaderInstallModules_registry(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(nil, nil))
_, diags := inst.InstallModules(context.Background(), dir, "tests", false, hooks)
_, diags := inst.InstallModules(context.Background(), dir, "tests", false, false, hooks)
assertNoDiagnostics(t, diags)
v := version.Must(version.NewVersion("0.0.1"))
@ -597,7 +597,7 @@ func TestLoaderInstallModules_goGetter(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(nil, nil))
_, diags := inst.InstallModules(context.Background(), dir, "tests", false, hooks)
_, diags := inst.InstallModules(context.Background(), dir, "tests", false, false, hooks)
assertNoDiagnostics(t, diags)
wantCalls := []testInstallHookCall{
@ -715,7 +715,7 @@ func TestModuleInstaller_fromTests(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, nil)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, hooks)
_, diags := inst.InstallModules(context.Background(), ".", "tests", false, false, hooks)
assertNoDiagnostics(t, diags)
wantCalls := []testInstallHookCall{
@ -772,7 +772,7 @@ func TestLoadInstallModules_registryFromTest(t *testing.T) {
loader, close := configload.NewLoaderForTests(t)
defer close()
inst := NewModuleInstaller(modulesDir, loader, registry.NewClient(nil, nil))
_, diags := inst.InstallModules(context.Background(), dir, "tests", false, hooks)
_, diags := inst.InstallModules(context.Background(), dir, "tests", false, false, hooks)
assertNoDiagnostics(t, diags)
v := version.Must(version.NewVersion("0.0.1"))

View file

@ -39,7 +39,7 @@ func LoadConfigForTests(t *testing.T, rootDir string, testsDir string) (*configs
loader, cleanup := configload.NewLoaderForTests(t)
inst := NewModuleInstaller(loader.ModulesDir(), loader, registry.NewClient(nil, nil))
_, moreDiags := inst.InstallModules(context.Background(), rootDir, testsDir, true, ModuleInstallHooksImpl{})
_, moreDiags := inst.InstallModules(context.Background(), rootDir, testsDir, true, false, ModuleInstallHooksImpl{})
diags = diags.Append(moreDiags)
if diags.HasErrors() {
cleanup()