terraform/terraform/node_resource_validate.go

169 lines
4.7 KiB
Go

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"
)
// NodeValidatableResource represents a resource that is used for validation
// only.
type NodeValidatableResource struct {
*NodeAbstractResource
}
var (
_ GraphNodeSubPath = (*NodeValidatableResource)(nil)
_ GraphNodeDynamicExpandable = (*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() {
log.Printf("[TRACE] %T %s: count expression has errors", n, n.Name())
return nil, diags.Err()
}
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,
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()
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.
var provider ResourceProvider
var providerSchema *ProviderSchema
var configVal cty.Value
seq := &EvalSequence{
Nodes: []EvalNode{
&EvalValidateSelfRef{
Addr: addr.Resource,
Config: config.Config,
},
&EvalGetProvider{
Addr: n.ResolvedProvider,
Output: &provider,
Schema: &providerSchema,
},
&EvalValidateResource{
Addr: addr.Resource,
Provider: &provider,
ProviderSchema: &providerSchema,
Config: config,
ConfigVal: &configVal,
},
},
}
if managed := n.Config.Managed; managed != nil {
// Validate all the provisioners
for _, p := range managed.Provisioners {
var provisioner ResourceProvisioner
var provisionerSchema *configschema.Block
seq.Nodes = append(
seq.Nodes,
&EvalGetProvisioner{
Name: p.Type,
Output: &provisioner,
Schema: &provisionerSchema,
},
&EvalValidateProvisioner{
ResourceAddr: addr.Resource,
Provisioner: &provisioner,
Schema: &provisionerSchema,
Config: p,
},
)
}
}
return seq
}