ApplyMoves modifies in-place the given state object so that any existing objects that are matched by a "from" argument of one of the move statements will be moved to instead appear at the "to" argument of that statement. The result is a map from the unique key of each absolute address that was eith
(stmts []MoveStatement, state *states.State)
| 31 | // ApplyMoves expects exclusive access to the given state while it's running. |
| 32 | // Don't read or write any part of the state structure until ApplyMoves returns. |
| 33 | func ApplyMoves(stmts []MoveStatement, state *states.State) MoveResults { |
| 34 | ret := makeMoveResults() |
| 35 | |
| 36 | if len(stmts) == 0 { |
| 37 | return ret |
| 38 | } |
| 39 | |
| 40 | // The methodology here is to construct a small graph of all of the move |
| 41 | // statements where the edges represent where a particular statement |
| 42 | // is either chained from or nested inside the effect of another statement. |
| 43 | // That then means we can traverse the graph in topological sort order |
| 44 | // to gradually move objects through potentially multiple moves each. |
| 45 | |
| 46 | g := buildMoveStatementGraph(stmts) |
| 47 | |
| 48 | // If the graph is not valid the we will not take any action at all. The |
| 49 | // separate validation step should detect this and return an error. |
| 50 | if diags := validateMoveStatementGraph(g); diags.HasErrors() { |
| 51 | log.Printf("[ERROR] ApplyMoves: %s", diags.ErrWithWarnings()) |
| 52 | return ret |
| 53 | } |
| 54 | |
| 55 | // The graph must be reduced in order for ReverseDepthFirstWalk to work |
| 56 | // correctly, since it is built from following edges and can skip over |
| 57 | // dependencies if there is a direct edge to a transitive dependency. |
| 58 | g.TransitiveReduction() |
| 59 | |
| 60 | // The starting nodes are the ones that don't depend on any other nodes. |
| 61 | startNodes := make(dag.Set, len(stmts)) |
| 62 | for _, v := range g.Vertices() { |
| 63 | if len(g.DownEdges(v)) == 0 { |
| 64 | startNodes.Add(v) |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | if startNodes.Len() == 0 { |
| 69 | log.Println("[TRACE] refactoring.ApplyMoves: No 'moved' statements to consider in this configuration") |
| 70 | return ret |
| 71 | } |
| 72 | |
| 73 | log.Printf("[TRACE] refactoring.ApplyMoves: Processing 'moved' statements in the configuration\n%s", logging.Indent(g.String())) |
| 74 | |
| 75 | recordOldAddr := func(oldAddr, newAddr addrs.AbsResourceInstance) { |
| 76 | if prevMove, exists := ret.Changes.GetOk(oldAddr); exists { |
| 77 | // If the old address was _already_ the result of a move then |
| 78 | // we'll replace that entry so that our results summarize a chain |
| 79 | // of moves into a single entry. |
| 80 | ret.Changes.Remove(oldAddr) |
| 81 | oldAddr = prevMove.From |
| 82 | } |
| 83 | ret.Changes.Put(newAddr, MoveSuccess{ |
| 84 | From: oldAddr, |
| 85 | To: newAddr, |
| 86 | }) |
| 87 | } |
| 88 | recordBlockage := func(newAddr, wantedAddr addrs.AbsMoveable) { |
| 89 | ret.Blocked.Put(newAddr, MoveBlocked{ |
| 90 | Wanted: wantedAddr, |