From b031e183328dff8e87e23df0949411c6daf6bc3e Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Fri, 11 May 2018 16:17:06 -0700 Subject: [PATCH] core: Pass ProviderSchema to EvalValidateSelfRef EvalValidateSelfRef needs schema in order to extract references. It was previously expecting a *configschema.Block directly, but we weren't actually passing that in from anywhere except the tests because it's not available directly in that form during the evaltree for node_resource_validate. Instead, we now pass in the whole *ProviderSchema for the associated provider and have this EvalNode find the schema itself based on the address. This breaks some of the generality of this node (now only really works for resource addresses) but that's okay since we have no other use-case right now anyway. --- terraform/context_validate_test.go | 2 +- terraform/eval_validate_selfref.go | 42 ++++++++++++++++++++----- terraform/eval_validate_selfref_test.go | 22 ++++++++----- terraform/node_resource_validate.go | 9 +++--- 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/terraform/context_validate_test.go b/terraform/context_validate_test.go index 4b3665e9a4..af512c59e0 100644 --- a/terraform/context_validate_test.go +++ b/terraform/context_validate_test.go @@ -887,7 +887,7 @@ func TestContext2Validate_selfRefMulti(t *testing.T) { ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.List(cty.String), Optional: true}, + "foo": {Type: cty.String, Optional: true}, }, }, }, diff --git a/terraform/eval_validate_selfref.go b/terraform/eval_validate_selfref.go index b4f060603a..c577fc2919 100644 --- a/terraform/eval_validate_selfref.go +++ b/terraform/eval_validate_selfref.go @@ -3,22 +3,21 @@ package terraform import ( "fmt" - "github.com/hashicorp/terraform/tfdiags" - - "github.com/hashicorp/terraform/lang" - "github.com/hashicorp/hcl2/hcl" + "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/config/configschema" + "github.com/hashicorp/terraform/lang" + "github.com/hashicorp/terraform/tfdiags" ) // EvalValidateSelfRef is an EvalNode implementation that checks to ensure that // expressions within a particular referencable block do not reference that // same block. type EvalValidateSelfRef struct { - Addr addrs.Referenceable - Config hcl.Body - Schema *configschema.Block + Addr addrs.Referenceable + Config hcl.Body + ProviderSchema **ProviderSchema } func (n *EvalValidateSelfRef) Eval(ctx EvalContext) (interface{}, error) { @@ -33,7 +32,34 @@ func (n *EvalValidateSelfRef) Eval(ctx EvalContext) (interface{}, error) { addrStrs = append(addrStrs, tAddr.ContainingResource().String()) } - refs, _ := lang.ReferencesInBlock(n.Config, n.Schema) + if n.ProviderSchema == nil || *n.ProviderSchema == nil { + return nil, fmt.Errorf("provider schema unavailable while validating %s for self-references; this is a bug in Terraform and should be reported", addr) + } + + providerSchema := *n.ProviderSchema + var schema *configschema.Block + switch tAddr := addr.(type) { + case addrs.Resource: + switch tAddr.Mode { + case addrs.ManagedResourceMode: + schema = providerSchema.ResourceTypes[tAddr.Type] + case addrs.DataResourceMode: + schema = providerSchema.DataSources[tAddr.Type] + } + case addrs.ResourceInstance: + switch tAddr.Resource.Mode { + case addrs.ManagedResourceMode: + schema = providerSchema.ResourceTypes[tAddr.Resource.Type] + case addrs.DataResourceMode: + schema = providerSchema.DataSources[tAddr.Resource.Type] + } + } + + if schema == nil { + return nil, fmt.Errorf("no schema available for %s to validate for self-references; this is a bug in Terraform and should be reported", addr) + } + + refs, _ := lang.ReferencesInBlock(n.Config, schema) for _, ref := range refs { for _, addrStr := range addrStrs { if ref.Subject.String() == addrStr { diff --git a/terraform/eval_validate_selfref_test.go b/terraform/eval_validate_selfref_test.go index f828e990b9..461b06a833 100644 --- a/terraform/eval_validate_selfref_test.go +++ b/terraform/eval_validate_selfref_test.go @@ -80,18 +80,24 @@ func TestEvalValidateSelfRef(t *testing.T) { }, }) - n := &EvalValidateSelfRef{ - Addr: test.Addr, - Config: body, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": { - Type: cty.String, - Required: true, + ps := &ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "aws_instance": &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Required: true, + }, }, }, }, } + + n := &EvalValidateSelfRef{ + Addr: test.Addr, + Config: body, + ProviderSchema: &ps, + } result, err := n.Eval(nil) if result != nil { t.Fatal("result should always be nil") diff --git a/terraform/node_resource_validate.go b/terraform/node_resource_validate.go index d20087b38b..064e0cef90 100644 --- a/terraform/node_resource_validate.go +++ b/terraform/node_resource_validate.go @@ -124,15 +124,16 @@ func (n *NodeValidatableResourceInstance) EvalTree() EvalNode { seq := &EvalSequence{ Nodes: []EvalNode{ - &EvalValidateSelfRef{ - Addr: addr.Resource, - Config: config.Config, - }, &EvalGetProvider{ Addr: n.ResolvedProvider, Output: &provider, Schema: &providerSchema, }, + &EvalValidateSelfRef{ + Addr: addr.Resource, + Config: config.Config, + ProviderSchema: &providerSchema, + }, &EvalValidateResource{ Addr: addr.Resource, Provider: &provider,