Skip to content

Commit

Permalink
validate deprecated attrs from static traversals
Browse files Browse the repository at this point in the history
We can't validate that data from deprecated nested attributes is used in
the configuration, but we can at least catch the simple case where a
deprecated attribute is referenced directly.
  • Loading branch information
jbardin committed Aug 8, 2022
1 parent fd76846 commit 25f5a81
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 10 deletions.
30 changes: 25 additions & 5 deletions internal/configs/configschema/validate_traversal.go
Expand Up @@ -74,14 +74,34 @@ func (b *Block) StaticValidateTraversal(traversal hcl.Traversal) tfdiags.Diagnos
}

if attrS, exists := b.Attributes[name]; exists {
// Check for Deprecated status of this attribute.
// We currently can't provide the user with any useful guidance because
// the deprecation string is not part of the schema, but we can at
// least warn them.
//
// This purposely does not attempt to recurse into nested attribute
// types. Because nested attribute values are often not accessed via a
// direct traversal to the leaf attributes, we cannot reliably detect
// if a nested, deprecated attribute value is actually used from the
// traversal alone. More precise detection of deprecated attributes
// would require adding metadata like marks to the cty value itself, to
// be caught during evaluation.
if attrS.Deprecated {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `Deprecated attribute`,
Detail: fmt.Sprintf(`The attribute %q is deprecated. Refer to the provider documentation for details.`, name),
Subject: next.SourceRange().Ptr(),
})
}

// For attribute validation we will just apply the rest of the
// traversal to an unknown value of the attribute type and pass through
// HCL's own errors, since we don't want to replicate all of HCL's type
// checking rules here.
// traversal to an unknown value of the attribute type and pass
// through HCL's own errors, since we don't want to replicate all
// of HCL's type checking rules here.
val := cty.UnknownVal(attrS.ImpliedType())
_, hclDiags := after.TraverseRel(val)
diags = diags.Append(hclDiags)
return diags
return diags.Append(hclDiags)
}

if blockS, exists := b.BlockTypes[name]; exists {
Expand Down
16 changes: 11 additions & 5 deletions internal/configs/configschema/validate_traversal_test.go
Expand Up @@ -10,9 +10,10 @@ import (

func TestStaticValidateTraversal(t *testing.T) {
attrs := map[string]*Attribute{
"str": {Type: cty.String, Optional: true},
"list": {Type: cty.List(cty.String), Optional: true},
"dyn": {Type: cty.DynamicPseudoType, Optional: true},
"str": {Type: cty.String, Optional: true},
"list": {Type: cty.List(cty.String), Optional: true},
"dyn": {Type: cty.DynamicPseudoType, Optional: true},
"deprecated": {Type: cty.String, Computed: true, Deprecated: true},
"nested_single": {
Optional: true,
NestedType: &Object{
Expand Down Expand Up @@ -220,6 +221,10 @@ func TestStaticValidateTraversal(t *testing.T) {
`obj.nested_map["key"].optional`,
``,
},
{
`obj.deprecated`,
`Deprecated attribute: The attribute "deprecated" is deprecated. Refer to the provider documentation for details.`,
},
}

for _, test := range tests {
Expand All @@ -239,8 +244,9 @@ func TestStaticValidateTraversal(t *testing.T) {
t.Errorf("unexpected error: %s", diags.Err().Error())
}
} else {
if diags.HasErrors() {
if got := diags.Err().Error(); got != test.WantError {
err := diags.ErrWithWarnings()
if err != nil {
if got := err.Error(); got != test.WantError {
t.Errorf("wrong error\ngot: %s\nwant: %s", got, test.WantError)
}
} else {
Expand Down

0 comments on commit 25f5a81

Please sign in to comment.