opentofu/terraform/node_resource_plan_instance.go
James Bardin b79adeae02 save resolved providers for resources to state
Use the ResourceState.Provider field to store the full name of the
provider used during apply. This field is only used when a resource is
removed from the config, and will allow that resource to be removed by
the exact same provider with which it was created.

Modify the locations which might accept the alue of the
ResourceState.Provider field to detect that the name is resolved.
2017-11-07 13:09:36 -05:00

191 lines
4.5 KiB
Go

package terraform
import (
"fmt"
"github.com/hashicorp/terraform/config"
)
// NodePlannableResourceInstance represents a _single_ resource
// instance that is plannable. This means this represents a single
// count index, for example.
type NodePlannableResourceInstance struct {
*NodeAbstractResource
}
// GraphNodeEvalable
func (n *NodePlannableResourceInstance) EvalTree() EvalNode {
addr := n.NodeAbstractResource.Addr
// stateId is the ID to put into the state
stateId := addr.stateId()
// Build the instance info. More of this will be populated during eval
info := &InstanceInfo{
Id: stateId,
Type: addr.Type,
ModulePath: normalizeModulePath(addr.Path),
}
// Build the resource for eval
resource := &Resource{
Name: addr.Name,
Type: addr.Type,
CountIndex: addr.Index,
}
if resource.CountIndex < 0 {
resource.CountIndex = 0
}
// Determine the dependencies for the state.
stateDeps := n.StateReferences()
// Eval info is different depending on what kind of resource this is
switch n.Config.Mode {
case config.ManagedResourceMode:
return n.evalTreeManagedResource(
stateId, info, resource, stateDeps,
)
case config.DataResourceMode:
return n.evalTreeDataResource(
stateId, info, resource, stateDeps)
default:
panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode))
}
}
func (n *NodePlannableResourceInstance) evalTreeDataResource(
stateId string, info *InstanceInfo,
resource *Resource, stateDeps []string) EvalNode {
var provider ResourceProvider
var config *ResourceConfig
var diff *InstanceDiff
var state *InstanceState
return &EvalSequence{
Nodes: []EvalNode{
&EvalReadState{
Name: stateId,
Output: &state,
},
// We need to re-interpolate the config here because some
// of the attributes may have become computed during
// earlier planning, due to other resources having
// "requires new resource" diffs.
&EvalInterpolate{
Config: n.Config.RawConfig.Copy(),
Resource: resource,
Output: &config,
},
&EvalIf{
If: func(ctx EvalContext) (bool, error) {
computed := config.ComputedKeys != nil && len(config.ComputedKeys) > 0
// If the configuration is complete and we
// already have a state then we don't need to
// do any further work during apply, because we
// already populated the state during refresh.
if !computed && state != nil {
return true, EvalEarlyExitError{}
}
return true, nil
},
Then: EvalNoop{},
},
&EvalGetProvider{
Name: n.ResolvedProvider,
Output: &provider,
},
&EvalReadDataDiff{
Info: info,
Config: &config,
Provider: &provider,
Output: &diff,
OutputState: &state,
},
&EvalWriteState{
Name: stateId,
ResourceType: n.Config.Type,
Provider: n.ResolvedProvider,
Dependencies: stateDeps,
State: &state,
},
&EvalWriteDiff{
Name: stateId,
Diff: &diff,
},
},
}
}
func (n *NodePlannableResourceInstance) evalTreeManagedResource(
stateId string, info *InstanceInfo,
resource *Resource, stateDeps []string) EvalNode {
// Declare a bunch of variables that are used for state during
// evaluation. Most of this are written to by-address below.
var provider ResourceProvider
var diff *InstanceDiff
var state *InstanceState
var resourceConfig *ResourceConfig
return &EvalSequence{
Nodes: []EvalNode{
&EvalInterpolate{
Config: n.Config.RawConfig.Copy(),
Resource: resource,
Output: &resourceConfig,
},
&EvalGetProvider{
Name: n.ResolvedProvider,
Output: &provider,
},
// Re-run validation to catch any errors we missed, e.g. type
// mismatches on computed values.
&EvalValidateResource{
Provider: &provider,
Config: &resourceConfig,
ResourceName: n.Config.Name,
ResourceType: n.Config.Type,
ResourceMode: n.Config.Mode,
IgnoreWarnings: true,
},
&EvalReadState{
Name: stateId,
Output: &state,
},
&EvalDiff{
Name: stateId,
Info: info,
Config: &resourceConfig,
Resource: n.Config,
Provider: &provider,
State: &state,
OutputDiff: &diff,
OutputState: &state,
},
&EvalCheckPreventDestroy{
Resource: n.Config,
Diff: &diff,
},
&EvalWriteState{
Name: stateId,
ResourceType: n.Config.Type,
Provider: n.ResolvedProvider,
Dependencies: stateDeps,
State: &state,
},
&EvalWriteDiff{
Name: stateId,
Diff: &diff,
},
},
}
}