configs: Accept and minimally validate a "language" argument
We expect that in order to continue to evolve the language without breaking existing modules we will at some point need to have a way to mark when a particular module is expecting a newer interpretation of the language. Although it's too early to do any deep preparation for that, this commit aims to proactively reserve an argument named "language" inside "terraform" blocks, which currently only accepts the keyword TF2021 that is intended to represent "the edition of the Terraform language as defined in 2021". That argument also defaults to TF2021 if not set, so in practice there's no real reason to set this today, but this minimal validation today is intended to give better feedback to users of older Terraform versions in the event that we introduce a new language edition later and they try to use an module incompatible with their Terraform version.
This commit is contained in:
parent
54cc4dadf6
commit
b5adc33075
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2"
|
"github.com/hashicorp/hcl/v2"
|
||||||
"github.com/hashicorp/terraform/experiments"
|
"github.com/hashicorp/terraform/experiments"
|
||||||
|
"github.com/hashicorp/terraform/version"
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,6 +26,49 @@ func sniffActiveExperiments(body hcl.Body) (experiments.Set, hcl.Diagnostics) {
|
||||||
content, _, blockDiags := block.Body.PartialContent(configFileExperimentsSniffBlockSchema)
|
content, _, blockDiags := block.Body.PartialContent(configFileExperimentsSniffBlockSchema)
|
||||||
diags = append(diags, blockDiags...)
|
diags = append(diags, blockDiags...)
|
||||||
|
|
||||||
|
if attr, exists := content.Attributes["language"]; exists {
|
||||||
|
// We don't yet have a sense of selecting an edition of the
|
||||||
|
// language, but we're reserving this syntax for now so that
|
||||||
|
// if and when we do this later older versions of Terraform
|
||||||
|
// will emit a more helpful error message than just saying
|
||||||
|
// this attribute doesn't exist. Handling this as part of
|
||||||
|
// experiments is a bit odd for now but justified by the
|
||||||
|
// fact that a future fuller implementation of switchable
|
||||||
|
// languages would be likely use a similar implementation
|
||||||
|
// strategy as experiments, and thus would lead to this
|
||||||
|
// function being refactored to deal with both concerns at
|
||||||
|
// once. We'll see, though!
|
||||||
|
kw := hcl.ExprAsKeyword(attr.Expr)
|
||||||
|
currentVersion := version.SemVer.String()
|
||||||
|
const firstEdition = "TF2021"
|
||||||
|
switch {
|
||||||
|
case kw == "": // (the expression wasn't a keyword at all)
|
||||||
|
diags = diags.Append(&hcl.Diagnostic{
|
||||||
|
Severity: hcl.DiagError,
|
||||||
|
Summary: "Invalid language edition",
|
||||||
|
Detail: fmt.Sprintf(
|
||||||
|
"The language argument expects a bare language edition keyword. Terraform %s supports only language edition %s, which is the default.",
|
||||||
|
currentVersion, firstEdition,
|
||||||
|
),
|
||||||
|
Subject: attr.Expr.Range().Ptr(),
|
||||||
|
})
|
||||||
|
case kw != firstEdition:
|
||||||
|
rel := "different"
|
||||||
|
if kw > firstEdition { // would be weird for this not to be true, but it's user input so anything goes
|
||||||
|
rel = "newer"
|
||||||
|
}
|
||||||
|
diags = diags.Append(&hcl.Diagnostic{
|
||||||
|
Severity: hcl.DiagError,
|
||||||
|
Summary: "Unsupported language edition",
|
||||||
|
Detail: fmt.Sprintf(
|
||||||
|
"Terraform v%s only supports language edition %s. This module requires a %s version of Terraform CLI.",
|
||||||
|
currentVersion, firstEdition, rel,
|
||||||
|
),
|
||||||
|
Subject: attr.Expr.Range().Ptr(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
attr, exists := content.Attributes["experiments"]
|
attr, exists := content.Attributes["experiments"]
|
||||||
if !exists {
|
if !exists {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -58,8 +58,8 @@ func (p *Parser) loadConfigFile(path string, override bool) (*File, hcl.Diagnost
|
||||||
content, contentDiags := block.Body.Content(terraformBlockSchema)
|
content, contentDiags := block.Body.Content(terraformBlockSchema)
|
||||||
diags = append(diags, contentDiags...)
|
diags = append(diags, contentDiags...)
|
||||||
|
|
||||||
// We ignore the "terraform_version" and "experiments" attributes
|
// We ignore the "terraform_version", "language" and "experiments"
|
||||||
// here because sniffCoreVersionRequirements and
|
// attributes here because sniffCoreVersionRequirements and
|
||||||
// sniffActiveExperiments already dealt with those above.
|
// sniffActiveExperiments already dealt with those above.
|
||||||
|
|
||||||
for _, innerBlock := range content.Blocks {
|
for _, innerBlock := range content.Blocks {
|
||||||
|
@ -244,6 +244,7 @@ var terraformBlockSchema = &hcl.BodySchema{
|
||||||
Attributes: []hcl.AttributeSchema{
|
Attributes: []hcl.AttributeSchema{
|
||||||
{Name: "required_version"},
|
{Name: "required_version"},
|
||||||
{Name: "experiments"},
|
{Name: "experiments"},
|
||||||
|
{Name: "language"},
|
||||||
},
|
},
|
||||||
Blocks: []hcl.BlockHeaderSchema{
|
Blocks: []hcl.BlockHeaderSchema{
|
||||||
{
|
{
|
||||||
|
@ -283,8 +284,7 @@ var configFileVersionSniffBlockSchema = &hcl.BodySchema{
|
||||||
// to decode a single attribute from inside a "terraform" block.
|
// to decode a single attribute from inside a "terraform" block.
|
||||||
var configFileExperimentsSniffBlockSchema = &hcl.BodySchema{
|
var configFileExperimentsSniffBlockSchema = &hcl.BodySchema{
|
||||||
Attributes: []hcl.AttributeSchema{
|
Attributes: []hcl.AttributeSchema{
|
||||||
{
|
{Name: "experiments"},
|
||||||
Name: "experiments",
|
{Name: "language"},
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
terraform {
|
||||||
|
# The language argument expects a bare keyword, not a string.
|
||||||
|
language = "TF2021" # ERROR: Invalid language edition
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
terraform {
|
||||||
|
# If a future change in this repository happens to make TF2038 a valid
|
||||||
|
# edition then this will start failing; in that case, change this file to
|
||||||
|
# select a different edition that isn't supported.
|
||||||
|
language = TF2038 # ERROR: Unsupported language edition
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
terraform {
|
||||||
|
# If we drop support for TF2021 in a future Terraform release then this
|
||||||
|
# test will fail. In that case, update this to a newer edition that is
|
||||||
|
# still supported, because the purpose of this test is to verify that
|
||||||
|
# we can successfully decode the language argument, not specifically
|
||||||
|
# that we support TF2021.
|
||||||
|
language = TF2021
|
||||||
|
}
|
Loading…
Reference in New Issue