Skip to content

Commit

Permalink
decoder: Enable validators to deal with unknown schema
Browse files Browse the repository at this point in the history
  • Loading branch information
radeksimko committed Aug 10, 2023
1 parent 9697577 commit ce29849
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 1 deletion.
26 changes: 26 additions & 0 deletions decoder/internal/schemahelper/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package schemahelper

import "context"

type unknownSchemaCtxKey struct{}

// WithUnknownSchema attaches a flag indicating that the schema being passed
// is not wholly known.
func WithUnknownSchema(ctx context.Context) context.Context {
return context.WithValue(ctx, unknownSchemaCtxKey{}, true)
}

// HasUnknownSchema returns true if the schema being passed
// is not wholly known. This allows each validator to decide
// whether and how to validate the AST.
func HasUnknownSchema(ctx context.Context) bool {
value := ctx.Value(unknownSchemaCtxKey{})
uSchema, ok := value.(bool)
if !ok {
return false
}
return uSchema
}
7 changes: 7 additions & 0 deletions decoder/internal/validator/attribute_unexpected.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"fmt"

"github.com/hashicorp/hcl-lang/decoder/internal/schemahelper"
"github.com/hashicorp/hcl-lang/schema"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
Expand All @@ -15,6 +16,12 @@ import (
type UnexpectedAttribute struct{}

func (v UnexpectedAttribute) Visit(ctx context.Context, node hclsyntax.Node, nodeSchema schema.Schema) (diags hcl.Diagnostics) {
if schemahelper.HasUnknownSchema(ctx) {
// Avoid checking for unexpected attributes
// if we cannot tell which ones are expected.
return
}

attr, ok := node.(*hclsyntax.Attribute)
if !ok {
return
Expand Down
7 changes: 7 additions & 0 deletions decoder/internal/validator/block_unexpected.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"fmt"

"github.com/hashicorp/hcl-lang/decoder/internal/schemahelper"
"github.com/hashicorp/hcl-lang/schema"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
Expand All @@ -15,6 +16,12 @@ import (
type UnexpectedBlock struct{}

func (v UnexpectedBlock) Visit(ctx context.Context, node hclsyntax.Node, nodeSchema schema.Schema) (diags hcl.Diagnostics) {
if schemahelper.HasUnknownSchema(ctx) {
// Avoid checking for unexpected blocks
// if we cannot tell which ones are expected.
return
}

block, ok := node.(*hclsyntax.Block)
if !ok {
return
Expand Down
2 changes: 1 addition & 1 deletion decoder/internal/walker/walker.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func Walk(ctx context.Context, node hclsyntax.Node, nodeSchema schema.Schema, w
if ok && bSchema.Body != nil {
mergedSchema, ok := schemahelper.MergeBlockBodySchemas(nodeType.AsHCLBlock(), bSchema)
if !ok {
// TODO
ctx = schemahelper.WithUnknownSchema(ctx)
}
blockBodySchema = mergedSchema
}
Expand Down
111 changes: 111 additions & 0 deletions decoder/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,117 @@ wakka = 2
}`,
map[string]hcl.Diagnostics{},
},
{
"missing dependent body",
// Eventually this should be a warning diagnostic but it's not appropriate yet
// At least in Terraform we don't yet have a way of easily addressing
// missing provider schemas (which are represented as dependent bodies).
&schema.BodySchema{
Blocks: map[string]*schema.BlockSchema{
"resource": {
Body: &schema.BodySchema{
Attributes: map[string]*schema.AttributeSchema{
"depends_on": {
IsOptional: true,
Constraint: schema.LiteralType{Type: cty.List(cty.String)},
},
},
},
Labels: []*schema.LabelSchema{
{
Name: "key",
IsDepKey: true,
},
},
DependentBody: map[schema.SchemaKey]*schema.BodySchema{
schema.NewSchemaKey(schema.DependencyKeys{
Labels: []schema.LabelDependent{
{Index: 0, Value: "aws_instance"},
},
}): {
Attributes: map[string]*schema.AttributeSchema{
"size": {
IsOptional: true,
Constraint: schema.LiteralType{Type: cty.Number},
},
},
},
},
},
},
},
`resource "google_instance" {
size = 42
moot = "moot"
depends_on = []
}
resource "aws_instance" {
size = 42
toot = "toot"
depends_on = []
}`,
map[string]hcl.Diagnostics{
"test.tf": {
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Unexpected attribute",
Detail: `An attribute named "toot" is not expected here`,
Subject: &hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 8, Column: 5, Byte: 133},
End: hcl.Pos{Line: 8, Column: 18, Byte: 146},
},
},
},
},
},
{
"missing dependent body with deprecated static attribute",
// Eventually this should be a warning diagnostic but it's not appropriate yet
// At least in Terraform we don't yet have a way of easily addressing
// missing provider schemas (which are represented as dependent bodies).
&schema.BodySchema{
Blocks: map[string]*schema.BlockSchema{
"resource": {
Body: &schema.BodySchema{
Attributes: map[string]*schema.AttributeSchema{
"depends_on": {
IsOptional: true,
IsDeprecated: true,
Constraint: schema.LiteralType{Type: cty.List(cty.String)},
},
},
},
Labels: []*schema.LabelSchema{
{
Name: "key",
IsDepKey: true,
},
},
DependentBody: map[schema.SchemaKey]*schema.BodySchema{},
},
},
},
`resource "google_instance" {
size = 42
moot = "moot"
depends_on = []
}`,
map[string]hcl.Diagnostics{
"test.tf": {
&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: `"depends_on" is deprecated`,
Detail: `Reason: ""`,
Subject: &hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 4, Column: 5, Byte: 65},
End: hcl.Pos{Line: 4, Column: 20, Byte: 80},
},
},
},
},
},
}

for i, tc := range testCases {
Expand Down

0 comments on commit ce29849

Please sign in to comment.