mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
36fb5b52e7
In our new loader we are changing certain values in configuration to be naked keywords or references rather than quoted strings as before. Since many of these have been shown in books, tutorials, and our own documentation we will make the old forms generate deprecation warnings rather than errors so that newcomers starting from older documentation can be eased into the new syntax, rather than getting blocked. This will also avoid creating a hard compatibility wall for reusable modules that are already published, allowing them to still be used in spite of these warnings and then fixed when the maintainer is able.
222 lines
5.2 KiB
Go
222 lines
5.2 KiB
Go
package configs
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/hcl2/gohcl"
|
|
"github.com/hashicorp/hcl2/hcl"
|
|
"github.com/hashicorp/hcl2/hcl/hclsyntax"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
// A consistent detail message for all "not a valid identifier" diagnostics.
|
|
const badIdentifierDetail = "A name must start with a letter and may contain only letters, digits, underscores, and dashes."
|
|
|
|
// Variable represents a "variable" block in a module or file.
|
|
type Variable struct {
|
|
Name string
|
|
Description string
|
|
Default cty.Value
|
|
TypeHint VariableTypeHint
|
|
|
|
DescriptionSet bool
|
|
|
|
DeclRange hcl.Range
|
|
}
|
|
|
|
func decodeVariableBlock(block *hcl.Block) (*Variable, hcl.Diagnostics) {
|
|
v := &Variable{
|
|
Name: block.Labels[0],
|
|
DeclRange: block.DefRange,
|
|
}
|
|
|
|
content, diags := block.Body.Content(variableBlockSchema)
|
|
|
|
if !hclsyntax.ValidIdentifier(v.Name) {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid variable name",
|
|
Detail: badIdentifierDetail,
|
|
Subject: &block.LabelRanges[0],
|
|
})
|
|
}
|
|
|
|
// Don't allow declaration of variables that would conflict with the
|
|
// reserved attribute and block type names in a "module" block, since
|
|
// these won't be usable for child modules.
|
|
for _, attr := range moduleBlockSchema.Attributes {
|
|
if attr.Name == v.Name {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid variable name",
|
|
Detail: fmt.Sprintf("The variable name %q is reserved due to its special meaning inside module blocks.", attr.Name),
|
|
Subject: &block.LabelRanges[0],
|
|
})
|
|
}
|
|
}
|
|
|
|
if attr, exists := content.Attributes["description"]; exists {
|
|
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &v.Description)
|
|
diags = append(diags, valDiags...)
|
|
v.DescriptionSet = true
|
|
}
|
|
|
|
if attr, exists := content.Attributes["default"]; exists {
|
|
val, valDiags := attr.Expr.Value(nil)
|
|
diags = append(diags, valDiags...)
|
|
v.Default = val
|
|
}
|
|
|
|
if attr, exists := content.Attributes["type"]; exists {
|
|
expr, shimDiags := shimTraversalInString(attr.Expr, true)
|
|
diags = append(diags, shimDiags...)
|
|
|
|
switch hcl.ExprAsKeyword(expr) {
|
|
case "string":
|
|
v.TypeHint = TypeHintString
|
|
case "list":
|
|
v.TypeHint = TypeHintList
|
|
case "map":
|
|
v.TypeHint = TypeHintMap
|
|
default:
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid variable type hint",
|
|
Detail: "The type argument requires one of the following keywords: string, list, or map.",
|
|
Subject: expr.Range().Ptr(),
|
|
})
|
|
}
|
|
}
|
|
|
|
return v, diags
|
|
}
|
|
|
|
// Output represents an "output" block in a module or file.
|
|
type Output struct {
|
|
Name string
|
|
Description string
|
|
Expr hcl.Expression
|
|
DependsOn []hcl.Traversal
|
|
Sensitive bool
|
|
|
|
DescriptionSet bool
|
|
SensitiveSet bool
|
|
|
|
DeclRange hcl.Range
|
|
}
|
|
|
|
func decodeOutputBlock(block *hcl.Block, override bool) (*Output, hcl.Diagnostics) {
|
|
o := &Output{
|
|
Name: block.Labels[0],
|
|
DeclRange: block.DefRange,
|
|
}
|
|
|
|
schema := outputBlockSchema
|
|
if override {
|
|
schema = schemaForOverrides(schema)
|
|
}
|
|
|
|
content, diags := block.Body.Content(schema)
|
|
|
|
if !hclsyntax.ValidIdentifier(o.Name) {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid output name",
|
|
Detail: badIdentifierDetail,
|
|
Subject: &block.LabelRanges[0],
|
|
})
|
|
}
|
|
|
|
if attr, exists := content.Attributes["description"]; exists {
|
|
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &o.Description)
|
|
diags = append(diags, valDiags...)
|
|
o.DescriptionSet = true
|
|
}
|
|
|
|
if attr, exists := content.Attributes["value"]; exists {
|
|
o.Expr = attr.Expr
|
|
}
|
|
|
|
if attr, exists := content.Attributes["sensitive"]; exists {
|
|
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &o.Sensitive)
|
|
diags = append(diags, valDiags...)
|
|
o.SensitiveSet = true
|
|
}
|
|
|
|
if attr, exists := content.Attributes["depends_on"]; exists {
|
|
deps, depsDiags := decodeDependsOn(attr)
|
|
diags = append(diags, depsDiags...)
|
|
o.DependsOn = append(o.DependsOn, deps...)
|
|
}
|
|
|
|
return o, diags
|
|
}
|
|
|
|
// Local represents a single entry from a "locals" block in a module or file.
|
|
// The "locals" block itself is not represented, because it serves only to
|
|
// provide context for us to interpret its contents.
|
|
type Local struct {
|
|
Name string
|
|
Expr hcl.Expression
|
|
|
|
DeclRange hcl.Range
|
|
}
|
|
|
|
func decodeLocalsBlock(block *hcl.Block) ([]*Local, hcl.Diagnostics) {
|
|
attrs, diags := block.Body.JustAttributes()
|
|
if len(attrs) == 0 {
|
|
return nil, diags
|
|
}
|
|
|
|
locals := make([]*Local, 0, len(attrs))
|
|
for name, attr := range attrs {
|
|
if !hclsyntax.ValidIdentifier(name) {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid local value name",
|
|
Detail: badIdentifierDetail,
|
|
Subject: &attr.NameRange,
|
|
})
|
|
}
|
|
|
|
locals = append(locals, &Local{
|
|
Name: name,
|
|
Expr: attr.Expr,
|
|
DeclRange: attr.Range,
|
|
})
|
|
}
|
|
return locals, diags
|
|
}
|
|
|
|
var variableBlockSchema = &hcl.BodySchema{
|
|
Attributes: []hcl.AttributeSchema{
|
|
{
|
|
Name: "description",
|
|
},
|
|
{
|
|
Name: "default",
|
|
},
|
|
{
|
|
Name: "type",
|
|
},
|
|
},
|
|
}
|
|
|
|
var outputBlockSchema = &hcl.BodySchema{
|
|
Attributes: []hcl.AttributeSchema{
|
|
{
|
|
Name: "description",
|
|
},
|
|
{
|
|
Name: "value",
|
|
Required: true,
|
|
},
|
|
{
|
|
Name: "depends_on",
|
|
},
|
|
{
|
|
Name: "sensitive",
|
|
},
|
|
},
|
|
}
|