Skip to content

Commit

Permalink
support deprecating variables
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielMSchmidt committed Nov 22, 2024
1 parent b4a634c commit 807ce06
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 0 deletions.
14 changes: 14 additions & 0 deletions internal/configs/named_values.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ type Variable struct {
Nullable bool
NullableSet bool

Deprecated string
DeprecatedSet bool
DeprecatedRange hcl.Range

DeclRange hcl.Range
}

Expand Down Expand Up @@ -186,6 +190,13 @@ func decodeVariableBlock(block *hcl.Block, override bool) (*Variable, hcl.Diagno
v.Default = val
}

if attr, exists := content.Attributes["deprecated"]; exists {
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &v.Deprecated)
diags = append(diags, valDiags...)
v.DeprecatedSet = true
v.DeprecatedRange = attr.Range
}

for _, block := range content.Blocks {
switch block.Type {

Expand Down Expand Up @@ -499,6 +510,9 @@ var variableBlockSchema = &hcl.BodySchema{
{
Name: "nullable",
},
{
Name: "deprecated",
},
},
Blocks: []hcl.BlockHeaderSchema{
{
Expand Down
27 changes: 27 additions & 0 deletions internal/configs/named_values_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,30 @@ func TestVariableInvalidDefault(t *testing.T) {
}
}
}

func TestVariableDeprecation(t *testing.T) {
src := `
variable "foo" {
type = string
deprecated = "This variable is deprecated, use bar instead"
}
`

hclF, diags := hclsyntax.ParseConfig([]byte(src), "test.tf", hcl.InitialPos)
if diags.HasErrors() {
t.Fatal(diags.Error())
}

b, diags := parseConfigFile(hclF.Body, nil, false, false)
if diags.HasErrors() {
t.Fatalf("unexpected error: %q", diags)
}

if !b.Variables[0].DeprecatedSet {
t.Fatalf("expected variable to be deprecated")
}

if b.Variables[0].Deprecated != "This variable is deprecated, use bar instead" {
t.Fatalf("expected variable to have deprecation message")
}
}
133 changes: 133 additions & 0 deletions internal/terraform/context_plan2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6068,3 +6068,136 @@ data "test_data_source" "foo" {
_, diags := ctx.Plan(m, states.NewState(), SimplePlanOpts(plans.NormalMode, testInputValuesUnset(m.Module.Variables)))
assertNoErrors(t, diags)
}

func TestContext2Plan_deprecated_variable(t *testing.T) {
m := testModuleInline(t, map[string]string{
"mod/main.tf": `
variable "old-and-used" {
type = string
deprecated = "module variable deprecation"
default = "optional"
}
variable "old-and-unused" {
type = string
deprecated = "module variable deprecation"
default = "optional"
}
variable "new" {
type = string
default = "optional"
}
output "use-everything" {
value = {
used = var.old-and-used
unused = var.old-and-unused
new = var.new
}
}
`,
"main.tf": `
variable "root-old-and-used" {
type = string
deprecated = "root variable deprecation"
default = "optional"
}
variable "root-old-and-unused" {
type = string
deprecated = "root variable deprecation"
default = "optional"
}
variable "new" {
type = string
default = "new"
}
module "old-mod" {
source = "./mod"
old-and-used = "old"
}
module "new-mod" {
source = "./mod"
new = "new"
}
output "use-everything" {
value = {
used = var.root-old-and-used
unused = var.root-old-and-unused
new = var.new
}
}
`,
})

p := new(testing_provider.MockProvider)
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{
ResourceTypes: map[string]*configschema.Block{
"test_resource": {
Attributes: map[string]*configschema.Attribute{
"attr": {
Type: cty.String,
Computed: true,
},
},
},
},
})

ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})

vars := InputValues{
"root-old-and-used": {
Value: cty.StringVal("root-old-and-used"),
},
"root-old-and-unused": {
Value: cty.NullVal(cty.String),
},
"new": {
Value: cty.StringVal("new"),
},
}

// We can find module variable deprecation during validation
diags := ctx.Validate(m, &ValidateOpts{})
var expectedValidateDiags tfdiags.Diagnostics
expectedValidateDiags = expectedValidateDiags.Append(
&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "Usage of deprecated variable",
Detail: "module variable deprecation",
Subject: &hcl.Range{
Filename: filepath.Join(m.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 21, Column: 20, Byte: 346},
End: hcl.Pos{Line: 21, Column: 25, Byte: 351},
},
},
)
assertDiagnosticsMatch(t, diags, expectedValidateDiags)

_, diags = ctx.Plan(m, states.NewState(), SimplePlanOpts(plans.NormalMode, vars))
var expectedPlanDiags tfdiags.Diagnostics
expectedPlanDiags = expectedPlanDiags.Append(
&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "Usage of deprecated variable",
Detail: "root variable deprecation",
Subject: &hcl.Range{
Filename: filepath.Join(m.Module.SourceDir, "main.tf"),
Start: hcl.Pos{Line: 4, Column: 2, Byte: 48},
End: hcl.Pos{Line: 4, Column: 42, Byte: 88},
},
},
)

assertDiagnosticsMatch(t, diags, expectedPlanDiags)
}
9 changes: 9 additions & 0 deletions internal/terraform/node_module_variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,15 @@ func (n *nodeModuleVariable) evalModuleVariable(ctx EvalContext, validateOnly bo
finalVal, moreDiags := prepareFinalInputVariableValue(n.Addr, rawVal, n.Config)
diags = diags.Append(moreDiags)

if n.Config.DeprecatedSet && givenVal.IsWhollyKnown() && !givenVal.IsNull() && validateOnly {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "Usage of deprecated variable",
Detail: n.Config.Deprecated,
Subject: n.Expr.Range().Ptr(),
})
}

return finalVal, diags.ErrWithWarnings()
}

Expand Down
9 changes: 9 additions & 0 deletions internal/terraform/node_root_variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ func (n *NodeRootVariable) Execute(ctx EvalContext, op walkOperation) tfdiags.Di
}
}

if n.Config.DeprecatedSet && givenVal.Value.IsWhollyKnown() && !givenVal.Value.IsNull() && op == walkPlan {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "Usage of deprecated variable",
Detail: n.Config.Deprecated,
Subject: &n.Config.DeprecatedRange,
})
}

if n.Planning {
if checkState := ctx.Checks(); checkState.ConfigHasChecks(n.Addr.InModule(addrs.RootModule)) {
ctx.Checks().ReportCheckableObjects(
Expand Down

0 comments on commit 807ce06

Please sign in to comment.