mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-13 09:32:24 -06:00
d4285dd27f
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.
170 lines
4.7 KiB
Go
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
|
|
}
|