mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-18 04:32:59 -06:00
83f0376673
When we originally stubbed ApplyMoves we didn't know yet how exactly we'd be using the result, so we made it a double-indexed map allowing looking up moves in both directions. However, in practice we only actually need to look up old addresses by new addresses, and so this commit first removes the double indexing so that each move is only represented by one element in the map. We also need to describe situations where a move was blocked, because in a future commit we'll generate some warnings in those cases. Therefore ApplyMoves now returns a MoveResults object which contains both a map of changes and a map of blocks. The map of blocks isn't used yet as of this commit, but we'll use it in a later commit to produce warnings within the "terraform" package.
114 lines
3.7 KiB
Go
114 lines
3.7 KiB
Go
package terraform
|
|
|
|
import (
|
|
"log"
|
|
|
|
"github.com/hashicorp/terraform/internal/configs"
|
|
"github.com/hashicorp/terraform/internal/instances"
|
|
"github.com/hashicorp/terraform/internal/plans"
|
|
"github.com/hashicorp/terraform/internal/refactoring"
|
|
"github.com/hashicorp/terraform/internal/states"
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
|
)
|
|
|
|
// graphWalkOpts captures some transient values we use (and possibly mutate)
|
|
// during a graph walk.
|
|
//
|
|
// The way these options get used unfortunately varies between the different
|
|
// walkOperation types. This is a historical design wart that dates back to
|
|
// us using the same graph structure for all operations; hopefully we'll
|
|
// make the necessary differences between the walk types more explicit someday.
|
|
type graphWalkOpts struct {
|
|
InputState *states.State
|
|
Changes *plans.Changes
|
|
Config *configs.Config
|
|
|
|
RootVariableValues InputValues
|
|
MoveResults refactoring.MoveResults
|
|
}
|
|
|
|
func (c *Context) walk(graph *Graph, operation walkOperation, opts *graphWalkOpts) (*ContextGraphWalker, tfdiags.Diagnostics) {
|
|
log.Printf("[DEBUG] Starting graph walk: %s", operation.String())
|
|
|
|
walker := c.graphWalker(operation, opts)
|
|
|
|
// Watch for a stop so we can call the provider Stop() API.
|
|
watchStop, watchWait := c.watchStop(walker)
|
|
|
|
// Walk the real graph, this will block until it completes
|
|
diags := graph.Walk(walker)
|
|
|
|
// Close the channel so the watcher stops, and wait for it to return.
|
|
close(watchStop)
|
|
<-watchWait
|
|
|
|
return walker, diags
|
|
}
|
|
|
|
func (c *Context) graphWalker(operation walkOperation, opts *graphWalkOpts) *ContextGraphWalker {
|
|
var state *states.SyncState
|
|
var refreshState *states.SyncState
|
|
var prevRunState *states.SyncState
|
|
|
|
// NOTE: None of the SyncState objects must directly wrap opts.InputState,
|
|
// because we use those to mutate the state object and opts.InputState
|
|
// belongs to our caller and thus we must treat it as immutable.
|
|
//
|
|
// To account for that, most of our SyncState values created below end up
|
|
// wrapping a _deep copy_ of opts.InputState instead.
|
|
inputState := opts.InputState
|
|
if inputState == nil {
|
|
// Lots of callers use nil to represent the "empty" case where we've
|
|
// not run Apply yet, so we tolerate that.
|
|
inputState = states.NewState()
|
|
}
|
|
|
|
switch operation {
|
|
case walkValidate:
|
|
// validate should not use any state
|
|
state = states.NewState().SyncWrapper()
|
|
|
|
// validate currently uses the plan graph, so we have to populate the
|
|
// refreshState and the prevRunState.
|
|
refreshState = states.NewState().SyncWrapper()
|
|
prevRunState = states.NewState().SyncWrapper()
|
|
|
|
case walkPlan, walkPlanDestroy:
|
|
state = inputState.DeepCopy().SyncWrapper()
|
|
refreshState = inputState.DeepCopy().SyncWrapper()
|
|
prevRunState = inputState.DeepCopy().SyncWrapper()
|
|
|
|
default:
|
|
state = inputState.DeepCopy().SyncWrapper()
|
|
// Only plan-like walks use refreshState and prevRunState
|
|
}
|
|
|
|
changes := opts.Changes
|
|
if changes == nil {
|
|
// Several of our non-plan walks end up sharing codepaths with the
|
|
// plan walk and thus expect to generate planned changes even though
|
|
// we don't care about them. To avoid those crashing, we'll just
|
|
// insert a placeholder changes object which'll get discarded
|
|
// afterwards.
|
|
changes = plans.NewChanges()
|
|
}
|
|
|
|
if opts.Config == nil {
|
|
panic("Context.graphWalker call without Config")
|
|
}
|
|
|
|
return &ContextGraphWalker{
|
|
Context: c,
|
|
State: state,
|
|
Config: opts.Config,
|
|
RefreshState: refreshState,
|
|
PrevRunState: prevRunState,
|
|
Changes: changes.SyncWrapper(),
|
|
InstanceExpander: instances.NewExpander(),
|
|
MoveResults: opts.MoveResults,
|
|
Operation: operation,
|
|
StopContext: c.runContext,
|
|
RootVariableValues: opts.RootVariableValues,
|
|
}
|
|
}
|