Skip to content

Commit 6d06d90

Browse files
kylecarbsjohnstcn
authored andcommitted
Parse variable validation
1 parent c404f82 commit 6d06d90

File tree

6 files changed

+144
-0
lines changed

6 files changed

+144
-0
lines changed

tfconfig/load_hcl.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,35 @@ func LoadModuleFromFile(file *hcl.File, mod *Module) hcl.Diagnostics {
185185
v.Sensitive = sensitive
186186
}
187187

188+
for _, block := range content.Blocks {
189+
switch block.Type {
190+
191+
case "validation":
192+
content, _, contentDiags := block.Body.PartialContent(variableValidationSchema)
193+
diags = append(diags, contentDiags...)
194+
var variableValidation VariableValidation
195+
196+
if attr, defined := content.Attributes["condition"]; defined {
197+
variableValidation.Condition = attr.Expr
198+
}
199+
200+
if attr, defined := content.Attributes["error_message"]; defined {
201+
var errorMessage string
202+
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &errorMessage)
203+
diags = append(diags, valDiags...)
204+
variableValidation.ErrorMessage = errorMessage
205+
}
206+
207+
v.Validations = append(v.Validations, variableValidation)
208+
209+
default:
210+
// Should never happen because our cases above should be
211+
// exhaustive for our schema.
212+
panic(fmt.Errorf("unhandled block type %q", block.Type))
213+
214+
}
215+
}
216+
188217
case "output":
189218

190219
content, _, contentDiags := block.Body.PartialContent(outputSchema)

tfconfig/schema.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,23 @@ var variableSchema = &hcl.BodySchema{
7979
Name: "sensitive",
8080
},
8181
},
82+
Blocks: []hcl.BlockHeaderSchema{
83+
{
84+
Type: "validation",
85+
LabelNames: nil,
86+
},
87+
},
88+
}
89+
90+
var variableValidationSchema = &hcl.BodySchema{
91+
Attributes: []hcl.AttributeSchema{
92+
{
93+
Name: "condition",
94+
},
95+
{
96+
Name: "error_message",
97+
},
98+
},
8299
}
83100

84101
var outputSchema = &hcl.BodySchema{
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"path": "testdata/variable-validation",
3+
"required_providers": {
4+
"null": {}
5+
},
6+
"variables": {
7+
"A": {
8+
"name": "A",
9+
"default": "A default",
10+
"required": false,
11+
"pos": {
12+
"filename": "testdata/variable-validation/variable-validation.tf",
13+
"line": 1
14+
}
15+
},
16+
"B": {
17+
"name": "B",
18+
"default": "B default",
19+
"required": false,
20+
"pos": {
21+
"filename": "testdata/variable-validation/variable-validation.tf",
22+
"line": 5
23+
},
24+
"validations": [
25+
{
26+
"error_message": "B error message"
27+
}
28+
]
29+
}
30+
},
31+
"outputs": {},
32+
"managed_resources": {
33+
"null_resource.A": {
34+
"mode": "managed",
35+
"type": "null_resource",
36+
"name": "A",
37+
"provider": {
38+
"name": "null"
39+
},
40+
"pos": {
41+
"filename": "testdata/variable-validation/variable-validation.tf",
42+
"line": 13
43+
}
44+
}
45+
},
46+
"data_resources": {},
47+
"module_calls": {}
48+
}
49+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
# Module `testdata/variable-validation`
3+
4+
Provider Requirements:
5+
* **null:** (any version)
6+
7+
## Input Variables
8+
* `A` (default `"A default"`)
9+
* `B` (default `"B default"`)
10+
11+
## Managed Resources
12+
* `null_resource.A` from `null`
13+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
variable "A" {
2+
default = "A default"
3+
}
4+
5+
variable "B" {
6+
default = "B default"
7+
validation {
8+
condition = true
9+
error_message = "B error message"
10+
}
11+
}
12+
13+
resource "null_resource" "A" {}

tfconfig/variable.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
package tfconfig
55

6+
import "github.com/hashicorp/hcl/v2"
7+
68
// Variable represents a single variable from a Terraform module.
79
type Variable struct {
810
Name string `json:"name"`
@@ -17,5 +19,26 @@ type Variable struct {
1719
Required bool `json:"required"`
1820
Sensitive bool `json:"sensitive,omitempty"`
1921

22+
Validations []VariableValidation `json:"validations,omitempty"`
23+
2024
Pos SourcePos `json:"pos"`
2125
}
26+
27+
// VariableValidation represents a configuration-defined validation rule
28+
// for a particular input variable, given as a "validation" block inside
29+
// a "variable" block.
30+
type VariableValidation struct {
31+
// Condition is an expression that refers to the variable being tested
32+
// and contains no other references. The expression must return true
33+
// to indicate that the value is valid or false to indicate that it is
34+
// invalid. If the expression produces an error, that's considered a bug
35+
// in the module defining the validation rule, not an error in the caller.
36+
Condition hcl.Expression `json:"-"`
37+
38+
// ErrorMessage is one or more full sentences, which would need to be in
39+
// English for consistency with the rest of the error message output but
40+
// can in practice be in any language as long as it ends with a period.
41+
// The message should describe what is required for the condition to return
42+
// true in a way that would make sense to a caller of the module.
43+
ErrorMessage string `json:"error_message"`
44+
}

0 commit comments

Comments
 (0)