make sure required_version is checked before diags
We must ensure that the terraform required_version is checked as early as possible, so that new configuration constructs don't cause init to fail without indicating the version is incompatible. The loadConfig call before the earlyconfig parsing seems to be unneeded, and we can delay that to de-tangle it from installing the modules which may have their own constraints. TODO: it seems that loadConfig should be able to handle returning the version constraints in the same manner as loadSingleModule.
This commit is contained in:
parent
372814e49a
commit
625e768678
|
@ -150,19 +150,7 @@ func (c *InitCommand) Run(args []string) int {
|
||||||
// initialization functionality remains built around "earlyconfig" and
|
// initialization functionality remains built around "earlyconfig" and
|
||||||
// so we need to still load the module via that mechanism anyway until we
|
// so we need to still load the module via that mechanism anyway until we
|
||||||
// can do some more invasive refactoring here.
|
// can do some more invasive refactoring here.
|
||||||
rootMod, confDiags := c.loadSingleModule(path)
|
|
||||||
rootModEarly, earlyConfDiags := c.loadSingleModuleEarly(path)
|
rootModEarly, earlyConfDiags := c.loadSingleModuleEarly(path)
|
||||||
if confDiags.HasErrors() {
|
|
||||||
c.Ui.Error(c.Colorize().Color(strings.TrimSpace(errInitConfigError)))
|
|
||||||
// TODO: It would be nice to check the version constraints in
|
|
||||||
// rootModEarly.RequiredCore and print out a hint if the module is
|
|
||||||
// declaring that it's not compatible with this version of Terraform,
|
|
||||||
// though we're deferring that for now because we're intending to
|
|
||||||
// refactor our use of "earlyconfig" here anyway and so whatever we
|
|
||||||
// might do here right now would likely be invalidated by that.
|
|
||||||
c.showDiagnostics(confDiags)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
// If _only_ the early loader encountered errors then that's unusual
|
// If _only_ the early loader encountered errors then that's unusual
|
||||||
// (it should generally be a superset of the normal loader) but we'll
|
// (it should generally be a superset of the normal loader) but we'll
|
||||||
// return those errors anyway since otherwise we'll probably get
|
// return those errors anyway since otherwise we'll probably get
|
||||||
|
@ -172,7 +160,12 @@ func (c *InitCommand) Run(args []string) int {
|
||||||
c.Ui.Error(c.Colorize().Color(strings.TrimSpace(errInitConfigError)))
|
c.Ui.Error(c.Colorize().Color(strings.TrimSpace(errInitConfigError)))
|
||||||
// Errors from the early loader are generally not as high-quality since
|
// Errors from the early loader are generally not as high-quality since
|
||||||
// it has less context to work with.
|
// it has less context to work with.
|
||||||
diags = diags.Append(confDiags)
|
|
||||||
|
// TODO: It would be nice to check the version constraints in
|
||||||
|
// rootModEarly.RequiredCore and print out a hint if the module is
|
||||||
|
// declaring that it's not compatible with this version of Terraform,
|
||||||
|
// and that may be what caused earlyconfig to fail.
|
||||||
|
diags = diags.Append(earlyConfDiags)
|
||||||
c.showDiagnostics(diags)
|
c.showDiagnostics(diags)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
@ -189,9 +182,41 @@ func (c *InitCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Using loadSingleModule will allow us to get the sniffed required_version
|
||||||
|
// before trying to build the complete config.
|
||||||
|
rootMod, _ := c.loadSingleModule(path)
|
||||||
|
// We can ignore the error, since we are going to reload the full config
|
||||||
|
// again below once we know the root module constraints are valid.
|
||||||
|
if rootMod != nil {
|
||||||
|
rootCfg := &configs.Config{
|
||||||
|
Module: rootMod,
|
||||||
|
}
|
||||||
|
// If our module version constraints are not valid, then there is no
|
||||||
|
// need to continue processing.
|
||||||
|
versionDiags := terraform.CheckCoreVersionRequirements(rootCfg)
|
||||||
|
if versionDiags.HasErrors() {
|
||||||
|
c.showDiagnostics(versionDiags)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// With all of the modules (hopefully) installed, we can now try to load the
|
// With all of the modules (hopefully) installed, we can now try to load the
|
||||||
// whole configuration tree.
|
// whole configuration tree.
|
||||||
config, confDiags := c.loadConfig(path)
|
config, confDiags := c.loadConfig(path)
|
||||||
|
// configDiags will be handled after the version constraint check, since an
|
||||||
|
// incorrect version of terraform may be producing errors for configuration
|
||||||
|
// constructs added in later versions.
|
||||||
|
|
||||||
|
// Check again to make sure none of the modules in the configuration
|
||||||
|
// declare that they don't support this Terraform version, so we can
|
||||||
|
// produce a version-related error message rather than
|
||||||
|
// potentially-confusing downstream errors.
|
||||||
|
versionDiags := terraform.CheckCoreVersionRequirements(config)
|
||||||
|
if versionDiags.HasErrors() {
|
||||||
|
c.showDiagnostics(versionDiags)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
diags = diags.Append(confDiags)
|
diags = diags.Append(confDiags)
|
||||||
if confDiags.HasErrors() {
|
if confDiags.HasErrors() {
|
||||||
c.Ui.Error(strings.TrimSpace(errInitConfigError))
|
c.Ui.Error(strings.TrimSpace(errInitConfigError))
|
||||||
|
@ -199,21 +224,10 @@ func (c *InitCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before we go further, we'll check to make sure none of the modules in the
|
|
||||||
// configuration declare that they don't support this Terraform version, so
|
|
||||||
// we can produce a version-related error message rather than
|
|
||||||
// potentially-confusing downstream errors.
|
|
||||||
versionDiags := terraform.CheckCoreVersionRequirements(config)
|
|
||||||
diags = diags.Append(versionDiags)
|
|
||||||
if versionDiags.HasErrors() {
|
|
||||||
c.showDiagnostics(diags)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
var back backend.Backend
|
var back backend.Backend
|
||||||
if flagBackend {
|
if flagBackend {
|
||||||
|
|
||||||
be, backendOutput, backendDiags := c.initBackend(rootMod, flagConfigExtra)
|
be, backendOutput, backendDiags := c.initBackend(config.Root.Module, flagConfigExtra)
|
||||||
diags = diags.Append(backendDiags)
|
diags = diags.Append(backendDiags)
|
||||||
if backendDiags.HasErrors() {
|
if backendDiags.HasErrors() {
|
||||||
c.showDiagnostics(diags)
|
c.showDiagnostics(diags)
|
||||||
|
|
|
@ -1608,6 +1608,33 @@ func TestInit_checkRequiredVersion(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that init will error out with an invalid version constraint, even if
|
||||||
|
// there are other invalid configuration constructs.
|
||||||
|
func TestInit_checkRequiredVersionFirst(t *testing.T) {
|
||||||
|
td := t.TempDir()
|
||||||
|
testCopyDir(t, testFixturePath("init-check-required-version-first"), td)
|
||||||
|
defer testChdir(t, td)()
|
||||||
|
|
||||||
|
ui := cli.NewMockUi()
|
||||||
|
view, _ := testView(t)
|
||||||
|
c := &InitCommand{
|
||||||
|
Meta: Meta{
|
||||||
|
testingOverrides: metaOverridesForProvider(testProvider()),
|
||||||
|
Ui: ui,
|
||||||
|
View: view,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{}
|
||||||
|
if code := c.Run(args); code != 1 {
|
||||||
|
t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
|
||||||
|
}
|
||||||
|
errStr := ui.ErrorWriter.String()
|
||||||
|
if !strings.Contains(errStr, `Unsupported Terraform Core version`) {
|
||||||
|
t.Fatalf("output should point to unmet version constraint, but is:\n\n%s", errStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestInit_providerLockFile(t *testing.T) {
|
func TestInit_providerLockFile(t *testing.T) {
|
||||||
// Create a temporary working directory that is empty
|
// Create a temporary working directory that is empty
|
||||||
td := tempDir(t)
|
td := tempDir(t)
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
terraform {
|
||||||
|
required_version = ">200.0.0"
|
||||||
|
|
||||||
|
bad {
|
||||||
|
block = "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
required_providers {
|
||||||
|
bang = {
|
||||||
|
oops = "boom"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nope {
|
||||||
|
boom {}
|
||||||
|
}
|
Loading…
Reference in New Issue