opentofu/terraform/node_resource_validate.go
Martin Atkins d4285dd27f core: Attach resource and provider config schemas during graph build
This is a little awkward since we need to instantiate the providers much
earlier than before. To avoid a lot of reshuffling here we just spin each
one up and then immediately shut it down again, letting our existing init
functionality during the graph walk still do the main initialization.
2018-10-16 18:46:46 -07:00

170 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,
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()
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
}