mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-02 12:17:39 -06:00
0e963db2c5
Since the validation of connection blocks is delegated to the communicator selected by "type", we were not previously doing any validation of the attribute names in these blocks until running provisioners during apply. Proper validation here requires us to already have the instance state, since the final connection info is a merge of values provided in config with values assigned automatically by the resource. However, we can do some basic name validation to catch typos during the validation pass, even though semantic validation and checking for missing attributes will still wait until the provisioner is instantiated. This fixes #6582 as much as we reasonably can.
159 lines
3.5 KiB
Go
159 lines
3.5 KiB
Go
package terraform
|
|
|
|
import (
|
|
"github.com/hashicorp/terraform/dag"
|
|
)
|
|
|
|
// NodeValidatableResource represents a resource that is used for validation
|
|
// only.
|
|
type NodeValidatableResource struct {
|
|
*NodeAbstractCountResource
|
|
}
|
|
|
|
// GraphNodeEvalable
|
|
func (n *NodeValidatableResource) EvalTree() EvalNode {
|
|
// Ensure we're validating
|
|
c := n.NodeAbstractCountResource
|
|
c.Validate = true
|
|
return c.EvalTree()
|
|
}
|
|
|
|
// GraphNodeDynamicExpandable
|
|
func (n *NodeValidatableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
|
|
// Grab the state which we read
|
|
state, lock := ctx.State()
|
|
lock.RLock()
|
|
defer lock.RUnlock()
|
|
|
|
// Expand the resource count which must be available by now from EvalTree
|
|
count := 1
|
|
if n.Config.RawCount.Value() != unknownValue() {
|
|
var err error
|
|
count, err = n.Config.Count()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// The concrete resource factory we'll use
|
|
concreteResource := func(a *NodeAbstractResource) dag.Vertex {
|
|
// Add the config and state since we don't do that via transforms
|
|
a.Config = n.Config
|
|
|
|
return &NodeValidatableResourceInstance{
|
|
NodeAbstractResource: 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{ParsedTargets: 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",
|
|
}
|
|
|
|
return b.Build(ctx.Path())
|
|
}
|
|
|
|
// This represents a _single_ resource instance to validate.
|
|
type NodeValidatableResourceInstance struct {
|
|
*NodeAbstractResource
|
|
}
|
|
|
|
// GraphNodeEvalable
|
|
func (n *NodeValidatableResourceInstance) EvalTree() EvalNode {
|
|
addr := n.NodeAbstractResource.Addr
|
|
|
|
// Build the resource for eval
|
|
resource := &Resource{
|
|
Name: addr.Name,
|
|
Type: addr.Type,
|
|
CountIndex: addr.Index,
|
|
}
|
|
if resource.CountIndex < 0 {
|
|
resource.CountIndex = 0
|
|
}
|
|
|
|
// Declare a bunch of variables that are used for state during
|
|
// evaluation. Most of this are written to by-address below.
|
|
var config *ResourceConfig
|
|
var provider ResourceProvider
|
|
|
|
seq := &EvalSequence{
|
|
Nodes: []EvalNode{
|
|
&EvalValidateResourceSelfRef{
|
|
Addr: &addr,
|
|
Config: &n.Config.RawConfig,
|
|
},
|
|
&EvalGetProvider{
|
|
Name: n.ProvidedBy()[0],
|
|
Output: &provider,
|
|
},
|
|
&EvalInterpolate{
|
|
Config: n.Config.RawConfig.Copy(),
|
|
Resource: resource,
|
|
Output: &config,
|
|
},
|
|
&EvalValidateResource{
|
|
Provider: &provider,
|
|
Config: &config,
|
|
ResourceName: n.Config.Name,
|
|
ResourceType: n.Config.Type,
|
|
ResourceMode: n.Config.Mode,
|
|
},
|
|
},
|
|
}
|
|
|
|
// Validate all the provisioners
|
|
for _, p := range n.Config.Provisioners {
|
|
var provisioner ResourceProvisioner
|
|
var connConfig *ResourceConfig
|
|
seq.Nodes = append(
|
|
seq.Nodes,
|
|
&EvalGetProvisioner{
|
|
Name: p.Type,
|
|
Output: &provisioner,
|
|
},
|
|
&EvalInterpolate{
|
|
Config: p.RawConfig.Copy(),
|
|
Resource: resource,
|
|
Output: &config,
|
|
},
|
|
&EvalInterpolate{
|
|
Config: p.ConnInfo.Copy(),
|
|
Resource: resource,
|
|
Output: &connConfig,
|
|
},
|
|
&EvalValidateProvisioner{
|
|
Provisioner: &provisioner,
|
|
Config: &config,
|
|
ConnConfig: &connConfig,
|
|
},
|
|
)
|
|
}
|
|
|
|
return seq
|
|
}
|