Merge pull request #38146 from hashicorp/dbanck/variable-const-attribute

Add `const` attribute to variable blocks
This commit is contained in:
Daniel Banck 2026-02-18 11:37:30 +01:00 committed by GitHub
commit 8ab5ded5b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 263 additions and 1 deletions

View file

@ -53,6 +53,10 @@ func (v *Variable) merge(ov *Variable) hcl.Diagnostics {
v.Ephemeral = ov.Ephemeral
v.EphemeralSet = ov.EphemeralSet
}
if ov.ConstSet {
v.Const = ov.Const
v.ConstSet = ov.ConstSet
}
if ov.Default != cty.NilVal {
v.Default = ov.Default
}

View file

@ -308,7 +308,117 @@ func TestModuleOverrideSensitiveVariable(t *testing.T) {
}
if got[v].SensitiveSet != want.sensitiveSet {
t.Errorf("wrong result for sensitive set\ngot: %t want: %t", got[v].Sensitive, want.sensitive)
t.Errorf("wrong result for sensitive set\ngot: %t want: %t", got[v].SensitiveSet, want.sensitiveSet)
}
})
}
}
func TestModuleOverrideEphemeralVariable(t *testing.T) {
type testCase struct {
ephemeral bool
ephemeralSet bool
}
cases := map[string]testCase{
"false_true": {
ephemeral: true,
ephemeralSet: true,
},
"true_false": {
ephemeral: false,
ephemeralSet: true,
},
"false_false_true": {
ephemeral: true,
ephemeralSet: true,
},
"true_true_false": {
ephemeral: false,
ephemeralSet: true,
},
"false_true_false": {
ephemeral: false,
ephemeralSet: true,
},
"true_false_true": {
ephemeral: true,
ephemeralSet: true,
},
}
mod, diags := testModuleFromDir("testdata/valid-modules/override-variable-ephemeral")
assertNoDiagnostics(t, diags)
if mod == nil {
t.Fatalf("module is nil")
}
got := mod.Variables
for v, want := range cases {
t.Run(fmt.Sprintf("variable %s", v), func(t *testing.T) {
if got[v].Ephemeral != want.ephemeral {
t.Errorf("wrong result for ephemeral\ngot: %t want: %t", got[v].Ephemeral, want.ephemeral)
}
if got[v].EphemeralSet != want.ephemeralSet {
t.Errorf("wrong result for ephemeral set\ngot: %t want: %t", got[v].EphemeralSet, want.ephemeralSet)
}
})
}
}
func TestModuleOverrideConstVariable(t *testing.T) {
type testCase struct {
constV bool
constSet bool
}
cases := map[string]testCase{
"false_true": {
constV: true,
constSet: true,
},
"true_false": {
constV: false,
constSet: true,
},
"false_false_true": {
constV: true,
constSet: true,
},
"true_true_false": {
constV: false,
constSet: true,
},
"false_true_false": {
constV: false,
constSet: true,
},
"true_false_true": {
constV: true,
constSet: true,
},
}
mod, diags := testModuleFromDir("testdata/valid-modules/override-variable-const")
assertNoDiagnostics(t, diags)
if mod == nil {
t.Fatalf("module is nil")
}
got := mod.Variables
for v, want := range cases {
t.Run(fmt.Sprintf("variable %s", v), func(t *testing.T) {
if got[v].Const != want.constV {
t.Errorf("wrong result for const\ngot: %t want: %t", got[v].Const, want.constV)
}
if got[v].ConstSet != want.constSet {
t.Errorf("wrong result for const set\ngot: %t want: %t", got[v].ConstSet, want.constSet)
}
})
}

View file

@ -38,9 +38,14 @@ type Variable struct {
Sensitive bool
Ephemeral bool
// Const indicates that this variable can be used during early evaluation
// work and configuration loading, for example in module sources
Const bool
DescriptionSet bool
SensitiveSet bool
EphemeralSet bool
ConstSet bool
// Nullable indicates that null is a valid value for this variable. Setting
// Nullable to false means that the module can expect this variable to
@ -133,6 +138,12 @@ func decodeVariableBlock(block *hcl.Block, override bool) (*Variable, hcl.Diagno
v.EphemeralSet = true
}
if attr, exists := content.Attributes["const"]; exists {
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &v.Const)
diags = append(diags, valDiags...)
v.ConstSet = true
}
if attr, exists := content.Attributes["nullable"]; exists {
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &v.Nullable)
diags = append(diags, valDiags...)
@ -525,6 +536,9 @@ var variableBlockSchema = &hcl.BodySchema{
{
Name: "deprecated",
},
{
Name: "const",
},
},
Blocks: []hcl.BlockHeaderSchema{
{

View file

@ -0,0 +1,23 @@
variable "false_true" {
const = true
}
variable "true_false" {
const = false
}
variable "false_false_true" {
const = false
}
variable "true_true_false" {
const = true
}
variable "false_true_false" {
const = true
}
variable "true_false_true" {
const = false
}

View file

@ -0,0 +1,21 @@
variable "false_true" {
}
variable "true_false" {
}
variable "false_false_true" {
const = true
}
variable "true_true_false" {
const = false
}
variable "false_true_false" {
const = false
}
variable "true_false_true" {
const = true
}

View file

@ -0,0 +1,23 @@
variable "false_true" {
const = false
}
variable "true_false" {
const = true
}
variable "false_false_true" {
const = false
}
variable "true_true_false" {
const = true
}
variable "false_true_false" {
const = false
}
variable "true_false_true" {
const = true
}

View file

@ -0,0 +1,23 @@
variable "false_true" {
ephemeral = true
}
variable "true_false" {
ephemeral = false
}
variable "false_false_true" {
ephemeral = false
}
variable "true_true_false" {
ephemeral = true
}
variable "false_true_false" {
ephemeral = true
}
variable "true_false_true" {
ephemeral = false
}

View file

@ -0,0 +1,21 @@
variable "false_true" {
}
variable "true_false" {
}
variable "false_false_true" {
ephemeral = true
}
variable "true_true_false" {
ephemeral = false
}
variable "false_true_false" {
ephemeral = false
}
variable "true_false_true" {
ephemeral = true
}

View file

@ -0,0 +1,23 @@
variable "false_true" {
ephemeral = false
}
variable "true_false" {
ephemeral = true
}
variable "false_false_true" {
ephemeral = false
}
variable "true_true_false" {
ephemeral = true
}
variable "false_true_false" {
ephemeral = false
}
variable "true_false_true" {
ephemeral = true
}