opentofu/terraform/transform_deposed.go
James Bardin a14fd0344c WIP reference providers by full name
This turned out to be a big messy commit, since the way providers are
referenced is tightly coupled throughout the code. That starts to unify
how providers are referenced, using the format output node Name method.

Add a new field to the internal resource data types called
ResolvedProvider. This is set by a new setter method SetProvider when a
resource is connected to a provider during graph creation. This allows
us to later lookup the provider instance a resource is connected to,
without requiring it to have the same module path.

The InitProvider context method now takes 2 arguments, one if the
provider type and the second is the full name of the provider. While the
provider type could still be parsed from the full name, this makes it
more explicit and, and changes to the name format won't effect this
code.
2017-11-02 15:00:06 -04:00

179 lines
4.1 KiB
Go

package terraform
import "fmt"
// DeposedTransformer is a GraphTransformer that adds deposed resources
// to the graph.
type DeposedTransformer struct {
// State is the global state. We'll automatically find the correct
// ModuleState based on the Graph.Path that is being transformed.
State *State
// View, if non-empty, is the ModuleState.View used around the state
// to find deposed resources.
View string
// The provider used by the resourced which were deposed
ResolvedProvider string
}
func (t *DeposedTransformer) Transform(g *Graph) error {
state := t.State.ModuleByPath(g.Path)
if state == nil {
// If there is no state for our module there can't be any deposed
// resources, since they live in the state.
return nil
}
// If we have a view, apply it now
if t.View != "" {
state = state.View(t.View)
}
// Go through all the resources in our state to look for deposed resources
for k, rs := range state.Resources {
// If we have no deposed resources, then move on
if len(rs.Deposed) == 0 {
continue
}
deposed := rs.Deposed
for i, _ := range deposed {
g.Add(&graphNodeDeposedResource{
Index: i,
ResourceName: k,
ResourceType: rs.Type,
ProviderName: rs.Provider,
ResolvedProvider: t.ResolvedProvider,
})
}
}
return nil
}
// graphNodeDeposedResource is the graph vertex representing a deposed resource.
type graphNodeDeposedResource struct {
Index int
ResourceName string
ResourceType string
ProviderName string
ResolvedProvider string
}
func (n *graphNodeDeposedResource) Name() string {
return fmt.Sprintf("%s (deposed #%d)", n.ResourceName, n.Index)
}
func (n *graphNodeDeposedResource) ProvidedBy() []string {
return []string{resourceProvider(n.ResourceName, n.ProviderName)}
}
func (n *graphNodeDeposedResource) SetProvider(p string) {
n.ResolvedProvider = p
}
// GraphNodeEvalable impl.
func (n *graphNodeDeposedResource) EvalTree() EvalNode {
var provider ResourceProvider
var state *InstanceState
seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)}
// Build instance info
info := &InstanceInfo{Id: n.Name(), Type: n.ResourceType}
seq.Nodes = append(seq.Nodes, &EvalInstanceInfo{Info: info})
// Refresh the resource
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
Ops: []walkOperation{walkRefresh},
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Name: n.ResolvedProvider,
Output: &provider,
},
&EvalReadStateDeposed{
Name: n.ResourceName,
Output: &state,
Index: n.Index,
},
&EvalRefresh{
Info: info,
Provider: &provider,
State: &state,
Output: &state,
},
&EvalWriteStateDeposed{
Name: n.ResourceName,
ResourceType: n.ResourceType,
Provider: n.ProviderName,
State: &state,
Index: n.Index,
},
},
},
})
// Apply
var diff *InstanceDiff
var err error
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
Ops: []walkOperation{walkApply, walkDestroy},
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Name: n.ResolvedProvider,
Output: &provider,
},
&EvalReadStateDeposed{
Name: n.ResourceName,
Output: &state,
Index: n.Index,
},
&EvalDiffDestroy{
Info: info,
State: &state,
Output: &diff,
},
// Call pre-apply hook
&EvalApplyPre{
Info: info,
State: &state,
Diff: &diff,
},
&EvalApply{
Info: info,
State: &state,
Diff: &diff,
Provider: &provider,
Output: &state,
Error: &err,
},
// Always write the resource back to the state deposed... if it
// was successfully destroyed it will be pruned. If it was not, it will
// be caught on the next run.
&EvalWriteStateDeposed{
Name: n.ResourceName,
ResourceType: n.ResourceType,
Provider: n.ProviderName,
State: &state,
Index: n.Index,
},
&EvalApplyPost{
Info: info,
State: &state,
Error: &err,
},
&EvalReturnError{
Error: &err,
},
&EvalUpdateStateHook{},
},
},
})
return seq
}