core: Don't DynamicExpand during validate

Previously we would attempt to DynamicExpand during the validate walk and
then validate each expanded instance separately. However, this meant that
we would not be able to validate the contents of a block where count = 0
or if count is not yet known.

Here we instead do a more static validation pass against the resource
configuration itself, setting count.index to cty.UnknownVal(cty.Number) so
we can type-check everything inside the block as being correct regardless
of the final count.

This is another step towards repairing the "validate" command for our
changed assumptions in a world where we have a more sophisticated type
checker.

This doesn't yet address the remaining problem that the expression
evaluator can't, with the current state structures, distinguish between
a completed resource with count = 0 and a resource that doesn't exist
at all (during validate), and so we'll still get errors if an expression
elsewhere in configuration refers to a dynamic index of a resource with
"count" set. That's a pre-existing condition that's no longer being masked
by _this_ problem, but can't be addressed until we've introduced the new
state types (states.State, etc) and thus we _can_ distinguish these two
situations. That will therefore be addressed in a later commit.
This commit is contained in:
Martin Atkins 2018-06-22 16:54:27 -07:00
parent fd371d838d
commit cf4a5e6336
9 changed files with 340 additions and 262 deletions

View File

@ -4102,3 +4102,120 @@ func TestContext2Plan_computedAttrRefTypeMismatch(t *testing.T) {
t.Fatalf("expected:\n\n%s\n\nto contain:\n\n%s", errStr, expected)
}
}
func TestContext2Plan_selfRef(t *testing.T) {
p := testProvider("aws")
p.GetSchemaReturn = &ProviderSchema{
ResourceTypes: map[string]*configschema.Block{
"aws_instance": {
Attributes: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true},
},
},
},
}
m := testModule(t, "plan-self-ref")
c := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: ResourceProviderResolverFixed(
map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
),
})
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("unexpected validation failure: %s", diags.Err())
}
_, diags = c.Plan()
if !diags.HasErrors() {
t.Fatalf("plan succeeded; want error")
}
gotErrStr := diags.Err().Error()
wantErrStr := "Self-referential block"
if !strings.Contains(gotErrStr, wantErrStr) {
t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr)
}
}
func TestContext2Plan_selfRefMulti(t *testing.T) {
p := testProvider("aws")
p.GetSchemaReturn = &ProviderSchema{
ResourceTypes: map[string]*configschema.Block{
"aws_instance": {
Attributes: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true},
},
},
},
}
m := testModule(t, "plan-self-ref-multi")
c := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: ResourceProviderResolverFixed(
map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
),
})
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("unexpected validation failure: %s", diags.Err())
}
_, diags = c.Plan()
if !diags.HasErrors() {
t.Fatalf("plan succeeded; want error")
}
gotErrStr := diags.Err().Error()
wantErrStr := "Self-referential block"
if !strings.Contains(gotErrStr, wantErrStr) {
t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr)
}
}
func TestContext2Plan_selfRefMultiAll(t *testing.T) {
p := testProvider("aws")
p.GetSchemaReturn = &ProviderSchema{
ResourceTypes: map[string]*configschema.Block{
"aws_instance": {
Attributes: map[string]*configschema.Attribute{
"foo": {Type: cty.List(cty.String), Optional: true},
},
},
},
}
m := testModule(t, "plan-self-ref-multi-all")
c := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: ResourceProviderResolverFixed(
map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
),
})
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("unexpected validation failure: %s", diags.Err())
}
_, diags = c.Plan()
if !diags.HasErrors() {
t.Fatalf("plan succeeded; want error")
}
gotErrStr := diags.Err().Error()
wantErrStr := "Self-referential block"
if !strings.Contains(gotErrStr, wantErrStr) {
t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr)
}
}

View File

@ -853,90 +853,6 @@ func TestContext2Validate_resourceConfig_good(t *testing.T) {
}
}
func TestContext2Validate_selfRef(t *testing.T) {
p := testProvider("aws")
p.GetSchemaReturn = &ProviderSchema{
ResourceTypes: map[string]*configschema.Block{
"aws_instance": {
Attributes: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true},
},
},
},
}
m := testModule(t, "validate-self-ref")
c := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: ResourceProviderResolverFixed(
map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
),
})
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
}
func TestContext2Validate_selfRefMulti(t *testing.T) {
p := testProvider("aws")
p.GetSchemaReturn = &ProviderSchema{
ResourceTypes: map[string]*configschema.Block{
"aws_instance": {
Attributes: map[string]*configschema.Attribute{
"foo": {Type: cty.String, Optional: true},
},
},
},
}
m := testModule(t, "validate-self-ref-multi")
c := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: ResourceProviderResolverFixed(
map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
),
})
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
}
func TestContext2Validate_selfRefMultiAll(t *testing.T) {
p := testProvider("aws")
p.GetSchemaReturn = &ProviderSchema{
ResourceTypes: map[string]*configschema.Block{
"aws_instance": {
Attributes: map[string]*configschema.Attribute{
"foo": {Type: cty.List(cty.String), Optional: true},
},
},
},
}
m := testModule(t, "validate-self-ref-multi-all")
c := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: ResourceProviderResolverFixed(
map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
),
})
diags := c.Validate()
if !diags.HasErrors() {
t.Fatalf("succeeded; want error")
}
}
func TestContext2Validate_tainted(t *testing.T) {
p := testProvider("aws")
p.GetSchemaReturn = &ProviderSchema{

View File

@ -10,6 +10,7 @@ import (
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/tfdiags"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert"
"github.com/zclconf/go-cty/cty/gocty"
)
@ -122,11 +123,12 @@ func (n *EvalValidateProvider) Eval(ctx EvalContext) (interface{}, error) {
// EvalValidateProvisioner is an EvalNode implementation that validates
// the configuration of a provisioner belonging to a resource.
type EvalValidateProvisioner struct {
ResourceAddr addrs.ResourceInstance
Provisioner *ResourceProvisioner
Schema **configschema.Block
Config *configs.Provisioner
ConnConfig *configs.Connection
ResourceAddr addrs.Resource
Provisioner *ResourceProvisioner
Schema **configschema.Block
Config *configs.Provisioner
ConnConfig *configs.Connection
ResourceHasCount bool
}
func (n *EvalValidateProvisioner) Eval(ctx EvalContext) (interface{}, error) {
@ -142,9 +144,7 @@ func (n *EvalValidateProvisioner) Eval(ctx EvalContext) (interface{}, error) {
{
// Validate the provisioner's own config first
keyData := EvalDataForInstanceKey(n.ResourceAddr.Key)
configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, n.ResourceAddr, keyData)
configVal, _, configDiags := n.evaluateBlock(ctx, config.Config, schema)
diags = diags.Append(configDiags)
if configDiags.HasErrors() {
return nil, diags.Err()
@ -199,17 +199,36 @@ func (n *EvalValidateProvisioner) validateConnConfig(ctx EvalContext, config *co
return diags
}
keyData := EvalDataForInstanceKey(n.ResourceAddr.Key)
// We evaluate here just by evaluating the block and returning any
// diagnostics we get, since evaluation alone is enough to check for
// extraneous arguments and incorrectly-typed arguments.
_, _, configDiags := ctx.EvaluateBlock(config.Config, connectionBlockSupersetSchema, self, keyData)
_, _, configDiags := n.evaluateBlock(ctx, config.Config, connectionBlockSupersetSchema)
diags = diags.Append(configDiags)
return diags
}
func (n *EvalValidateProvisioner) evaluateBlock(ctx EvalContext, body hcl.Body, schema *configschema.Block) (cty.Value, hcl.Body, tfdiags.Diagnostics) {
keyData := EvalDataForNoInstanceKey
selfAddr := n.ResourceAddr.Instance(addrs.NoKey)
if n.ResourceHasCount {
// For a resource that has count, we allow count.index but don't
// know at this stage what it will return.
keyData = InstanceKeyEvalData{
CountIndex: cty.UnknownVal(cty.Number),
}
// "self" can't point to an unknown key, but we'll force it to be
// key 0 here, which should return an unknown value of the
// expected type since none of these elements are known at this
// point anyway.
selfAddr = n.ResourceAddr.Instance(addrs.IntKey(0))
}
return ctx.EvaluateBlock(body, schema, selfAddr, keyData)
}
// connectionBlockSupersetSchema is a schema representing the superset of all
// possible arguments for "connection" blocks across all supported connection
// types.
@ -318,7 +337,7 @@ var connectionBlockSupersetSchema = &configschema.Block{
// EvalValidateResource is an EvalNode implementation that validates
// the configuration of a resource.
type EvalValidateResource struct {
Addr addrs.ResourceInstance
Addr addrs.Resource
Provider *ResourceProvider
ProviderSchema **ProviderSchema
Config *configs.Resource
@ -346,6 +365,21 @@ func (n *EvalValidateResource) Eval(ctx EvalContext) (interface{}, error) {
schema := *n.ProviderSchema
mode := cfg.Mode
keyData := EvalDataForNoInstanceKey
if n.Config.Count != nil {
// If the config block has count, we'll evaluate with an unknown
// number as count.index so we can still type check even though
// we won't expand count until the plan phase.
keyData = InstanceKeyEvalData{
CountIndex: cty.UnknownVal(cty.Number),
}
// Basic type-checking of the count argument. More complete validation
// of this will happen when we DynamicExpand during the plan walk.
countDiags := n.validateCount(ctx, n.Config.Count)
diags = diags.Append(countDiags)
}
var warns []string
var errs []error
@ -365,8 +399,6 @@ func (n *EvalValidateResource) Eval(ctx EvalContext) (interface{}, error) {
return nil, diags.Err()
}
keyData := EvalDataForInstanceKey(n.Addr.Key)
configVal, _, valDiags := ctx.EvaluateBlock(cfg.Config, schema, nil, keyData)
diags = diags.Append(valDiags)
if valDiags.HasErrors() {
@ -394,8 +426,6 @@ func (n *EvalValidateResource) Eval(ctx EvalContext) (interface{}, error) {
return nil, diags.Err()
}
keyData := EvalDataForInstanceKey(n.Addr.Key)
configVal, _, valDiags := ctx.EvaluateBlock(cfg.Config, schema, nil, keyData)
diags = diags.Append(valDiags)
if valDiags.HasErrors() {
@ -434,3 +464,71 @@ func (n *EvalValidateResource) Eval(ctx EvalContext) (interface{}, error) {
return nil, diags.NonFatalErr()
}
}
func (n *EvalValidateResource) validateCount(ctx EvalContext, expr hcl.Expression) tfdiags.Diagnostics {
if expr == nil {
return nil
}
var diags tfdiags.Diagnostics
countVal, countDiags := ctx.EvaluateExpr(expr, cty.Number, nil)
diags = diags.Append(countDiags)
if diags.HasErrors() {
return diags
}
if countVal.IsNull() {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid count argument",
Detail: `The given "count" argument value is null. An integer is required.`,
Subject: expr.Range().Ptr(),
})
return diags
}
var err error
countVal, err = convert.Convert(countVal, cty.Number)
if err != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid count argument",
Detail: fmt.Sprintf(`The given "count" argument value is unsuitable: %s.`, err),
Subject: expr.Range().Ptr(),
})
return diags
}
// If the value isn't known then that's the best we can do for now, but
// we'll check more thoroughly during the plan walk.
if !countVal.IsKnown() {
return diags
}
// If we _do_ know the value, then we can do a few more checks here.
var count int
err = gocty.FromCtyValue(countVal, &count)
if err != nil {
// Isn't a whole number, etc.
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid count argument",
Detail: fmt.Sprintf(`The given "count" argument value is unsuitable: %s.`, err),
Subject: expr.Range().Ptr(),
})
return diags
}
if count < 0 {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid count argument",
Detail: `The given "count" argument value is unsuitable: count cannot be negative.`,
Subject: expr.Range().Ptr(),
})
return diags
}
return diags
}

View File

@ -6,6 +6,7 @@ import (
"testing"
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/hcl2/hcltest"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/addrs"
@ -39,12 +40,59 @@ func TestEvalValidateResource_managedResource(t *testing.T) {
}),
}
node := &EvalValidateResource{
Addr: addrs.ResourceInstance{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "foo",
},
Addr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "foo",
},
Provider: &p,
Config: rc,
ProviderSchema: &mp.GetSchemaReturn,
}
ctx := &MockEvalContext{}
ctx.installSimpleEval()
_, err := node.Eval(ctx)
if err != nil {
t.Fatalf("err: %s", err)
}
if !mp.ValidateResourceCalled {
t.Fatal("Expected ValidateResource to be called, but it was not!")
}
}
func TestEvalValidateResource_managedResourceCount(t *testing.T) {
mp := simpleMockProvider()
mp.ValidateResourceFn = func(rt string, c *ResourceConfig) (ws []string, es []error) {
expected := "test_object"
if rt != expected {
t.Fatalf("wrong resource type\ngot: %#v\nwant: %#v", rt, expected)
}
expected = "bar"
val, _ := c.Get("test_string")
if val != expected {
t.Fatalf("wrong value for test_string\ngot: %#v\nwant: %#v", val, expected)
}
return
}
p := ResourceProvider(mp)
rc := &configs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_object",
Name: "foo",
Count: hcltest.MockExprLiteral(cty.NumberIntVal(2)),
Config: configs.SynthBody("", map[string]cty.Value{
"test_string": cty.StringVal("bar"),
}),
}
node := &EvalValidateResource{
Addr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "foo",
},
Provider: &p,
Config: rc,
@ -90,12 +138,10 @@ func TestEvalValidateResource_dataSource(t *testing.T) {
}
node := &EvalValidateResource{
Addr: addrs.ResourceInstance{
Resource: addrs.Resource{
Mode: addrs.DataResourceMode,
Type: "aws_ami",
Name: "foo",
},
Addr: addrs.Resource{
Mode: addrs.DataResourceMode,
Type: "aws_ami",
Name: "foo",
},
Provider: &p,
Config: rc,
@ -129,12 +175,10 @@ func TestEvalValidateResource_validReturnsNilError(t *testing.T) {
Config: configs.SynthBody("", map[string]cty.Value{}),
}
node := &EvalValidateResource{
Addr: addrs.ResourceInstance{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_object",
Name: "foo",
},
Addr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_object",
Name: "foo",
},
Provider: &p,
Config: rc,
@ -166,12 +210,10 @@ func TestEvalValidateResource_warningsAndErrorsPassedThrough(t *testing.T) {
Config: configs.SynthBody("", map[string]cty.Value{}),
}
node := &EvalValidateResource{
Addr: addrs.ResourceInstance{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_object",
Name: "foo",
},
Addr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_object",
Name: "foo",
},
Provider: &p,
Config: rc,
@ -215,12 +257,10 @@ func TestEvalValidateResource_ignoreWarnings(t *testing.T) {
Config: configs.SynthBody("", map[string]cty.Value{}),
}
node := &EvalValidateResource{
Addr: addrs.ResourceInstance{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test-object",
Name: "foo",
},
Addr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test-object",
Name: "foo",
},
Provider: &p,
Config: rc,
@ -247,12 +287,10 @@ func TestEvalValidateProvisioner_valid(t *testing.T) {
schema := &configschema.Block{}
node := &EvalValidateProvisioner{
ResourceAddr: addrs.ResourceInstance{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "foo",
Name: "bar",
},
ResourceAddr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "foo",
Name: "bar",
},
Provisioner: &p,
Schema: &schema,
@ -295,12 +333,10 @@ func TestEvalValidateProvisioner_warning(t *testing.T) {
}
node := &EvalValidateProvisioner{
ResourceAddr: addrs.ResourceInstance{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "foo",
Name: "bar",
},
ResourceAddr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "foo",
Name: "bar",
},
Provisioner: &p,
Schema: &schema,
@ -348,12 +384,10 @@ func TestEvalValidateProvisioner_connectionInvalid(t *testing.T) {
}
node := &EvalValidateProvisioner{
ResourceAddr: addrs.ResourceInstance{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "foo",
Name: "bar",
},
ResourceAddr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "foo",
Name: "bar",
},
Provisioner: &p,
Schema: &schema,

View File

@ -48,6 +48,7 @@ func (n *NodePlannableResourceInstance) EvalTree() EvalNode {
}
func (n *NodePlannableResourceInstance) evalTreeDataResource(addr addrs.AbsResourceInstance, stateId string, stateDeps []string) EvalNode {
config := n.Config
var provider ResourceProvider
var providerSchema *ProviderSchema
var diff *InstanceDiff
@ -67,6 +68,12 @@ func (n *NodePlannableResourceInstance) evalTreeDataResource(addr addrs.AbsResou
Schema: &providerSchema,
},
&EvalValidateSelfRef{
Addr: addr.Resource,
Config: config.Config,
ProviderSchema: &providerSchema,
},
&EvalReadDataDiff{
Addr: addr.Resource,
Config: n.Config,
@ -94,6 +101,7 @@ func (n *NodePlannableResourceInstance) evalTreeDataResource(addr addrs.AbsResou
}
func (n *NodePlannableResourceInstance) evalTreeManagedResource(addr addrs.AbsResourceInstance, stateId string, stateDeps []string) EvalNode {
config := n.Config
var provider ResourceProvider
var providerSchema *ProviderSchema
var diff *InstanceDiff
@ -112,6 +120,12 @@ func (n *NodePlannableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
Schema: &providerSchema,
},
&EvalValidateSelfRef{
Addr: addr.Resource,
Config: config.Config,
ProviderSchema: &providerSchema,
},
&EvalDiff{
Addr: addr.Resource,
Config: n.Config,

View File

@ -1,11 +1,7 @@
package terraform
import (
"log"
"github.com/hashicorp/terraform/config/configschema"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/tfdiags"
"github.com/zclconf/go-cty/cty"
)
@ -17,116 +13,21 @@ type NodeValidatableResource struct {
var (
_ GraphNodeSubPath = (*NodeValidatableResource)(nil)
_ GraphNodeDynamicExpandable = (*NodeValidatableResource)(nil)
_ GraphNodeEvalable = (*NodeValidatableResource)(nil)
_ GraphNodeReferenceable = (*NodeValidatableResource)(nil)
_ GraphNodeReferencer = (*NodeValidatableResource)(nil)
_ GraphNodeResource = (*NodeValidatableResource)(nil)
_ GraphNodeAttachResourceConfig = (*NodeValidatableResource)(nil)
)
// GraphNodeDynamicExpandable
func (n *NodeValidatableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
var diags tfdiags.Diagnostics
count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx)
diags = diags.Append(countDiags)
if countDiags.HasErrors() {
if count != 0 {
log.Printf("[TRACE] %T %s: count expression has errors", n, n.Name())
return nil, diags.Err()
}
// evaluateResourceCountExpression returns zero+errors only in the
// case where the count value successfully evaluated to an unknown
// number. We don't treat this as an error here because counts that
// are computed during validate can become known during the plan
// walk, if they refer to data resources, and so we'll just defer
// our validation steps to the plan phase in that case.
log.Printf("[TRACE] %T %s: count expression value not yet known, so deferring validation until the plan walk", n, n.Name())
} else if count >= 0 {
log.Printf("[TRACE] %T %s: count expression evaluates to %d", n, n.Name(), count)
} else {
log.Printf("[TRACE] %T %s: no count argument present", n, n.Name())
}
// Next we need to potentially rename an instance address in the state
// if we're transitioning whether "count" is set at all.
fixResourceCountSetTransition(ctx, n.ResourceAddr().Resource, count != -1)
// Grab the state which we read
state, lock := ctx.State()
lock.RLock()
defer lock.RUnlock()
// The concrete resource factory we'll use
concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex {
// Add the config and state since we don't do that via transforms
a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider
return &NodeValidatableResourceInstance{
NodeAbstractResourceInstance: a,
}
}
// Start creating the steps
steps := []GraphTransformer{
// Expand the count.
&ResourceCountTransformer{
Concrete: concreteResource,
Schema: n.Schema,
Count: count,
Addr: n.ResourceAddr(),
},
// Attach the state
&AttachStateTransformer{State: state},
// Targeting
&TargetsTransformer{Targets: n.Targets},
// Connect references so ordering is correct
&ReferenceTransformer{},
// Make sure there is a single root
&RootTransformer{},
}
// Build the graph
b := &BasicGraphBuilder{
Steps: steps,
Validate: true,
Name: "NodeValidatableResource",
}
graph, diags := b.Build(ctx.Path())
return graph, diags.ErrWithWarnings()
}
// This represents a _single_ resource instance to validate.
type NodeValidatableResourceInstance struct {
*NodeAbstractResourceInstance
}
var (
_ GraphNodeSubPath = (*NodeValidatableResourceInstance)(nil)
_ GraphNodeReferenceable = (*NodeValidatableResourceInstance)(nil)
_ GraphNodeReferencer = (*NodeValidatableResourceInstance)(nil)
_ GraphNodeResource = (*NodeValidatableResourceInstance)(nil)
_ GraphNodeResourceInstance = (*NodeValidatableResourceInstance)(nil)
_ GraphNodeAttachResourceConfig = (*NodeValidatableResourceInstance)(nil)
_ GraphNodeAttachResourceState = (*NodeValidatableResourceInstance)(nil)
_ GraphNodeEvalable = (*NodeValidatableResourceInstance)(nil)
)
// GraphNodeEvalable
func (n *NodeValidatableResourceInstance) EvalTree() EvalNode {
addr := n.ResourceInstanceAddr()
func (n *NodeValidatableResource) EvalTree() EvalNode {
addr := n.ResourceAddr()
config := n.Config
// Declare a bunch of variables that are used for state during
// evaluation. These are written to via pointers passed to the EvalNodes
// below.
// Declare the variables will be used are used to pass values along
// the evaluation sequence below. These are written to via pointers
// passed to the EvalNodes.
var provider ResourceProvider
var providerSchema *ProviderSchema
var configVal cty.Value
@ -138,11 +39,6 @@ func (n *NodeValidatableResourceInstance) EvalTree() EvalNode {
Output: &provider,
Schema: &providerSchema,
},
&EvalValidateSelfRef{
Addr: addr.Resource,
Config: config.Config,
ProviderSchema: &providerSchema,
},
&EvalValidateResource{
Addr: addr.Resource,
Provider: &provider,
@ -154,6 +50,8 @@ func (n *NodeValidatableResourceInstance) EvalTree() EvalNode {
}
if managed := n.Config.Managed; managed != nil {
hasCount := n.Config.Count != nil
// Validate all the provisioners
for _, p := range managed.Provisioners {
var provisioner ResourceProvisioner
@ -166,10 +64,11 @@ func (n *NodeValidatableResourceInstance) EvalTree() EvalNode {
Schema: &provisionerSchema,
},
&EvalValidateProvisioner{
ResourceAddr: addr.Resource,
Provisioner: &provisioner,
Schema: &provisionerSchema,
Config: p,
ResourceAddr: addr.Resource,
Provisioner: &provisioner,
Schema: &provisionerSchema,
Config: p,
ResourceHasCount: hasCount,
},
)
}