Skip to content

Commit b5016e0

Browse files
handle supression of deprecation messages
1 parent 153c4e3 commit b5016e0

31 files changed

+390
-122
lines changed

internal/addrs/check.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ func (c AbsCheck) CheckRule(typ CheckRuleType, i int) CheckRule {
106106
}
107107
}
108108

109+
// ModuleInstance returns the module instance portion of the address.
110+
func (c AbsCheck) ModuleInstance() ModuleInstance {
111+
return c.Module
112+
}
113+
109114
// ConfigCheckable returns the ConfigCheck address for this absolute reference.
110115
func (c AbsCheck) ConfigCheckable() ConfigCheckable {
111116
return ConfigCheck{

internal/addrs/check_rule.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,8 @@ func (c CheckRuleType) Description() string {
115115
return "Condition"
116116
}
117117
}
118+
119+
// ModuleInstance returns the module instance address containing this check rule.
120+
func (c CheckRule) ModuleInstance() ModuleInstance {
121+
return c.Container.ModuleInstance()
122+
}

internal/addrs/checkable.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type Checkable interface {
3939

4040
CheckableKind() CheckableKind
4141
String() string
42+
ModuleInstance() ModuleInstance
4243
}
4344

4445
var (

internal/addrs/input_variable.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ func (v AbsInputVariableInstance) CheckRule(typ CheckRuleType, i int) CheckRule
8181
}
8282
}
8383

84+
// ModuleInstance returns the module instance portion of the address.
85+
func (v AbsInputVariableInstance) ModuleInstance() ModuleInstance {
86+
return v.Module
87+
}
88+
8489
func (v AbsInputVariableInstance) ConfigCheckable() ConfigCheckable {
8590
return ConfigInputVariable{
8691
Module: v.Module.Module(),

internal/addrs/output_value.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ func (m ModuleInstance) OutputValue(name string) AbsOutputValue {
7777
}
7878
}
7979

80+
// ModuleInstance returns the module instance portion of the address.
81+
func (v AbsOutputValue) ModuleInstance() ModuleInstance {
82+
return v.Module
83+
}
84+
8085
func (v AbsOutputValue) CheckRule(t CheckRuleType, i int) CheckRule {
8186
return CheckRule{
8287
Container: v,

internal/addrs/resource.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,11 @@ func (m ModuleInstance) ResourceInstance(mode ResourceMode, typeName string, nam
280280
}
281281
}
282282

283+
// ModuleInstance returns the module instance portion of the address.
284+
func (r AbsResourceInstance) ModuleInstance() ModuleInstance {
285+
return r.Module
286+
}
287+
283288
// ContainingResource returns the address of the resource that contains the
284289
// receving resource instance. In other words, it discards the key portion
285290
// of the address to produce an AbsResource value.

internal/configs/module_call.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/hashicorp/hcl/v2"
1010
"github.com/hashicorp/hcl/v2/gohcl"
1111
"github.com/hashicorp/hcl/v2/hclsyntax"
12+
"github.com/zclconf/go-cty/cty"
1213

1314
"github.com/hashicorp/terraform/internal/addrs"
1415
"github.com/hashicorp/terraform/internal/getmodules/moduleaddrs"
@@ -35,6 +36,8 @@ type ModuleCall struct {
3536
DependsOn []hcl.Traversal
3637

3738
DeclRange hcl.Range
39+
40+
IgnoreNestedDeprecations bool
3841
}
3942

4043
func decodeModuleBlock(block *hcl.Block, override bool) (*ModuleCall, hcl.Diagnostics) {
@@ -163,6 +166,30 @@ func decodeModuleBlock(block *hcl.Block, override bool) (*ModuleCall, hcl.Diagno
163166
mc.Providers = append(mc.Providers, providers...)
164167
}
165168

169+
if attr, exists := content.Attributes["ignore_nested_deprecations"]; exists {
170+
// We only allow static boolean values for this argument.
171+
val, evalDiags := attr.Expr.Value(&hcl.EvalContext{})
172+
if len(evalDiags.Errs()) > 0 {
173+
diags = append(diags, &hcl.Diagnostic{
174+
Severity: hcl.DiagError,
175+
Summary: "Invalid value for ignore_nested_deprecations",
176+
Detail: "The value for ignore_nested_deprecations must be a static boolean (true or false).",
177+
Subject: attr.Expr.Range().Ptr(),
178+
})
179+
}
180+
181+
if val.Type() != cty.Bool {
182+
diags = append(diags, &hcl.Diagnostic{
183+
Severity: hcl.DiagError,
184+
Summary: "Invalid type for ignore_nested_deprecations",
185+
Detail: fmt.Sprintf("The value for ignore_nested_deprecations must be a boolean (true or false), but the given value has type %s.", val.Type().FriendlyName()),
186+
Subject: attr.Expr.Range().Ptr(),
187+
})
188+
}
189+
190+
mc.IgnoreNestedDeprecations = val.True()
191+
}
192+
166193
var seenEscapeBlock *hcl.Block
167194
for _, block := range content.Blocks {
168195
switch block.Type {
@@ -278,6 +305,9 @@ var moduleBlockSchema = &hcl.BodySchema{
278305
{
279306
Name: "providers",
280307
},
308+
{
309+
Name: "ignore_nested_deprecations",
310+
},
281311
},
282312
Blocks: []hcl.BlockHeaderSchema{
283313
{Type: "_"}, // meta-argument escaping block
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: BUSL-1.1
3+
4+
package deprecation
5+
6+
import (
7+
"sync"
8+
9+
"github.com/hashicorp/hcl/v2"
10+
"github.com/hashicorp/terraform/internal/addrs"
11+
"github.com/hashicorp/terraform/internal/lang/marks"
12+
"github.com/hashicorp/terraform/internal/tfdiags"
13+
"github.com/zclconf/go-cty/cty"
14+
)
15+
16+
// Deprecations keeps track of meta-information related to deprecation, e.g. which module calls
17+
// suppress deprecation warnings.
18+
type Deprecations struct {
19+
// Must hold this lock when accessing all fields after this one.
20+
mu sync.Mutex
21+
22+
suppressedModules addrs.Set[addrs.Module]
23+
}
24+
25+
func NewDeprecations() *Deprecations {
26+
return &Deprecations{
27+
suppressedModules: addrs.MakeSet[addrs.Module](),
28+
}
29+
}
30+
31+
func (d *Deprecations) SuppressModuleCallDeprecation(addr addrs.Module) {
32+
d.mu.Lock()
33+
defer d.mu.Unlock()
34+
35+
d.suppressedModules.Add(addr)
36+
}
37+
38+
func (d *Deprecations) Validate(value cty.Value, module addrs.Module, rng *hcl.Range) (cty.Value, tfdiags.Diagnostics) {
39+
var diags tfdiags.Diagnostics
40+
deprecationMarks := marks.GetDeprecationMarks(value)
41+
if len(deprecationMarks) == 0 {
42+
return value, diags
43+
}
44+
45+
notDeprecatedValue := marks.RemoveDeprecationMarks(value)
46+
47+
// Check if we need to suppress deprecation warnings for this module call.
48+
if d.IsModuleCallDeprecationSuppressed(module) {
49+
return notDeprecatedValue, diags
50+
}
51+
52+
for _, depMark := range deprecationMarks {
53+
diags = diags.Append(&hcl.Diagnostic{
54+
Severity: hcl.DiagWarning,
55+
Summary: "Deprecated value used",
56+
Detail: depMark.Message,
57+
Subject: rng,
58+
})
59+
}
60+
61+
return notDeprecatedValue, diags
62+
}
63+
64+
func (d *Deprecations) ValidateAsConfig(value cty.Value, module addrs.Module) tfdiags.Diagnostics {
65+
var diags tfdiags.Diagnostics
66+
_, pvms := value.UnmarkDeepWithPaths()
67+
68+
if len(pvms) == 0 || d.IsModuleCallDeprecationSuppressed(module) {
69+
return diags
70+
}
71+
72+
for _, pvm := range pvms {
73+
for m := range pvm.Marks {
74+
if depMark, ok := m.(marks.DeprecationMark); ok {
75+
diags = diags.Append(
76+
tfdiags.AttributeValue(
77+
tfdiags.Warning,
78+
"Deprecated value used",
79+
depMark.Message,
80+
pvm.Path,
81+
),
82+
)
83+
}
84+
}
85+
}
86+
return diags
87+
}
88+
89+
func (d *Deprecations) IsModuleCallDeprecationSuppressed(addr addrs.Module) bool {
90+
for _, mod := range d.suppressedModules {
91+
if addr.Equal(mod) || mod.TargetContains(addr) {
92+
return true
93+
}
94+
}
95+
return false
96+
}

internal/terraform/context_validate_test.go

Lines changed: 133 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3898,7 +3898,7 @@ resource "test_resource" "test" {
38983898

38993899
tfdiags.AssertDiagnosticsMatch(t, diags, tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
39003900
Severity: hcl.DiagWarning,
3901-
Summary: "Deprecated value used as for_each argument",
3901+
Summary: "Deprecated value used",
39023902
Detail: "Please stop using this",
39033903
Subject: &hcl.Range{
39043904
Filename: filepath.Join(m.Module.SourceDir, "main.tf"),
@@ -3907,7 +3907,7 @@ resource "test_resource" "test" {
39073907
},
39083908
}).Append(&hcl.Diagnostic{
39093909
Severity: hcl.DiagWarning,
3910-
Summary: "Deprecated value used as for_each argument",
3910+
Summary: "Deprecated value used",
39113911
Detail: "Please stop using this",
39123912
Subject: &hcl.Range{
39133913
Filename: filepath.Join(m.Module.SourceDir, "main.tf"),
@@ -3968,7 +3968,7 @@ resource "test_resource" "test" {
39683968

39693969
tfdiags.AssertDiagnosticsMatch(t, diags, tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
39703970
Severity: hcl.DiagWarning,
3971-
Summary: "Deprecated value used as count argument",
3971+
Summary: "Deprecated value used",
39723972
Detail: "Please stop using this",
39733973
Subject: &hcl.Range{
39743974
Filename: filepath.Join(m.Module.SourceDir, "main.tf"),
@@ -3977,7 +3977,7 @@ resource "test_resource" "test" {
39773977
},
39783978
}).Append(&hcl.Diagnostic{
39793979
Severity: hcl.DiagWarning,
3980-
Summary: "Deprecated value used as count argument",
3980+
Summary: "Deprecated value used",
39813981
Detail: "Please stop using this",
39823982
Subject: &hcl.Range{
39833983
Filename: filepath.Join(m.Module.SourceDir, "main.tf"),
@@ -4522,3 +4522,132 @@ output "test_output" {
45224522
},
45234523
}))
45244524
}
4525+
4526+
func TestContext2Validate_ignoring_nested_module_deprecations(t *testing.T) {
4527+
m := testModuleInline(t, map[string]string{
4528+
"mod/main.tf": `
4529+
output "old" {
4530+
deprecated = "Please stop using this"
4531+
value = "old"
4532+
}
4533+
4534+
module "nested" {
4535+
source = "./nested"
4536+
}
4537+
4538+
locals {
4539+
foo = module.nested.old # WARNING (if not muted)
4540+
}
4541+
`,
4542+
"mod/nested/main.tf": `
4543+
output "old" {
4544+
deprecated = "Please stop using this nested output"
4545+
value = "old"
4546+
}
4547+
4548+
module "deeper" {
4549+
source = "./deeper"
4550+
}
4551+
4552+
locals {
4553+
bar = module.deeper.old # WARNING (if not muted)
4554+
}
4555+
`,
4556+
"mod/nested/deeper/main.tf": `
4557+
output "old" {
4558+
deprecated = "Please stop using this deeply nested output"
4559+
value = "old"
4560+
}
4561+
`,
4562+
"main.tf": `
4563+
module "normal" {
4564+
source = "./mod"
4565+
}
4566+
4567+
module "silenced" {
4568+
source = "./mod"
4569+
4570+
# We don't want deprecations within this module call
4571+
ignore_nested_deprecations = true
4572+
}
4573+
4574+
# We want to still have deprecation warnings within our control surface
4575+
locals {
4576+
using_normal_old = module.normal.old # WARNING
4577+
using_silenced_old = module.silenced.old # WARNING
4578+
}
4579+
`,
4580+
})
4581+
4582+
p := new(testing_provider.MockProvider)
4583+
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&providerSchema{
4584+
ResourceTypes: map[string]*configschema.Block{
4585+
"test_resource": {
4586+
Attributes: map[string]*configschema.Attribute{
4587+
"attr": {
4588+
Type: cty.String,
4589+
Computed: true,
4590+
},
4591+
},
4592+
},
4593+
},
4594+
Actions: map[string]*providers.ActionSchema{
4595+
"test_action": {
4596+
ConfigSchema: &configschema.Block{
4597+
Attributes: map[string]*configschema.Attribute{
4598+
"attr": {
4599+
Type: cty.String,
4600+
Required: true,
4601+
},
4602+
},
4603+
},
4604+
},
4605+
},
4606+
})
4607+
4608+
ctx := testContext2(t, &ContextOpts{
4609+
Providers: map[addrs.Provider]providers.Factory{
4610+
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
4611+
},
4612+
})
4613+
4614+
diags := ctx.Validate(m, &ValidateOpts{})
4615+
4616+
tfdiags.AssertDiagnosticsMatch(t, diags, tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
4617+
Severity: hcl.DiagWarning,
4618+
Summary: "Deprecated value used",
4619+
Detail: "Please stop using this",
4620+
Subject: &hcl.Range{
4621+
Filename: filepath.Join(m.Module.SourceDir, "main.tf"),
4622+
Start: hcl.Pos{Line: 15, Column: 22, Byte: 285},
4623+
End: hcl.Pos{Line: 15, Column: 39, Byte: 302},
4624+
},
4625+
}).Append(&hcl.Diagnostic{
4626+
Severity: hcl.DiagWarning,
4627+
Summary: "Deprecated value used",
4628+
Detail: "Please stop using this",
4629+
Subject: &hcl.Range{
4630+
Filename: filepath.Join(m.Module.SourceDir, "main.tf"),
4631+
Start: hcl.Pos{Line: 16, Column: 24, Byte: 336},
4632+
End: hcl.Pos{Line: 16, Column: 43, Byte: 355},
4633+
},
4634+
}).Append(&hcl.Diagnostic{
4635+
Severity: hcl.DiagWarning,
4636+
Summary: "Deprecated value used",
4637+
Detail: "Please stop using this nested output",
4638+
Subject: &hcl.Range{
4639+
Filename: filepath.Join(m.Module.SourceDir, "mod", "main.tf"),
4640+
Start: hcl.Pos{Line: 12, Column: 9, Byte: 141},
4641+
End: hcl.Pos{Line: 12, Column: 26, Byte: 158},
4642+
},
4643+
}).Append(&hcl.Diagnostic{
4644+
Severity: hcl.DiagWarning,
4645+
Summary: "Deprecated value used",
4646+
Detail: "Please stop using this deeply nested output",
4647+
Subject: &hcl.Range{
4648+
Filename: filepath.Join(m.Module.SourceDir, "mod", "nested", "main.tf"),
4649+
Start: hcl.Pos{Line: 12, Column: 9, Byte: 155},
4650+
End: hcl.Pos{Line: 12, Column: 26, Byte: 172},
4651+
},
4652+
}))
4653+
}

0 commit comments

Comments
 (0)