github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/dag/dag.go (about) 1 package dag 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 8 "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" 9 10 "github.com/hashicorp/go-multierror" 11 ) 12 13 // AcyclicGraph is a specialization of Graph that cannot have cycles. With 14 // this property, we get the property of sane graph traversal. 15 type AcyclicGraph struct { 16 Graph 17 } 18 19 // WalkFunc is the callback used for walking the graph. 20 type WalkFunc func(Vertex) tfdiags.Diagnostics 21 22 // DepthWalkFunc is a walk function that also receives the current depth of the 23 // walk as an argument 24 type DepthWalkFunc func(Vertex, int) error 25 26 func (g *AcyclicGraph) DirectedGraph() Grapher { 27 return g 28 } 29 30 // Returns a Set that includes every Vertex yielded by walking down from the 31 // provided starting Vertex v. 32 func (g *AcyclicGraph) Ancestors(v Vertex) (*Set, error) { 33 s := new(Set) 34 start := AsVertexList(g.DownEdges(v)) 35 memoFunc := func(v Vertex, d int) error { 36 s.Add(v) 37 return nil 38 } 39 40 if err := g.DepthFirstWalk(start, memoFunc); err != nil { 41 return nil, err 42 } 43 44 return s, nil 45 } 46 47 // Returns a Set that includes every Vertex yielded by walking up from the 48 // provided starting Vertex v. 49 func (g *AcyclicGraph) Descendents(v Vertex) (*Set, error) { 50 s := new(Set) 51 start := AsVertexList(g.UpEdges(v)) 52 memoFunc := func(v Vertex, d int) error { 53 s.Add(v) 54 return nil 55 } 56 57 if err := g.ReverseDepthFirstWalk(start, memoFunc); err != nil { 58 return nil, err 59 } 60 61 return s, nil 62 } 63 64 // Root returns the root of the DAG, or an error. 65 // 66 // Complexity: O(V) 67 func (g *AcyclicGraph) Root() (Vertex, error) { 68 roots := make([]Vertex, 0, 1) 69 for _, v := range g.Vertices() { 70 if g.UpEdges(v).Len() == 0 { 71 roots = append(roots, v) 72 } 73 } 74 75 if len(roots) > 1 { 76 // TODO(mitchellh): make this error message a lot better 77 return nil, fmt.Errorf("multiple roots: %#v", roots) 78 } 79 80 if len(roots) == 0 { 81 return nil, fmt.Errorf("no roots found") 82 } 83 84 return roots[0], nil 85 } 86 87 // TransitiveReduction performs the transitive reduction of graph g in place. 88 // The transitive reduction of a graph is a graph with as few edges as 89 // possible with the same reachability as the original graph. This means 90 // that if there are three nodes A => B => C, and A connects to both 91 // B and C, and B connects to C, then the transitive reduction is the 92 // same graph with only a single edge between A and B, and a single edge 93 // between B and C. 94 // 95 // The graph must be valid for this operation to behave properly. If 96 // Validate() returns an error, the behavior is undefined and the results 97 // will likely be unexpected. 98 // 99 // Complexity: O(V(V+E)), or asymptotically O(VE) 100 func (g *AcyclicGraph) TransitiveReduction() { 101 // For each vertex u in graph g, do a DFS starting from each vertex 102 // v such that the edge (u,v) exists (v is a direct descendant of u). 103 // 104 // For each v-prime reachable from v, remove the edge (u, v-prime). 105 defer g.debug.BeginOperation("TransitiveReduction", "").End("") 106 107 for _, u := range g.Vertices() { 108 uTargets := g.DownEdges(u) 109 vs := AsVertexList(g.DownEdges(u)) 110 111 g.depthFirstWalk(vs, false, func(v Vertex, d int) error { 112 shared := uTargets.Intersection(g.DownEdges(v)) 113 for _, vPrime := range AsVertexList(shared) { 114 g.RemoveEdge(BasicEdge(u, vPrime)) 115 } 116 117 return nil 118 }) 119 } 120 } 121 122 // Validate validates the DAG. A DAG is valid if it has a single root 123 // with no cycles. 124 func (g *AcyclicGraph) Validate() error { 125 if _, err := g.Root(); err != nil { 126 return err 127 } 128 129 // Look for cycles of more than 1 component 130 var err error 131 cycles := g.Cycles() 132 if len(cycles) > 0 { 133 for _, cycle := range cycles { 134 cycleStr := make([]string, len(cycle)) 135 for j, vertex := range cycle { 136 cycleStr[j] = VertexName(vertex) 137 } 138 139 err = multierror.Append(err, fmt.Errorf( 140 "Cycle: %s", strings.Join(cycleStr, ", "))) 141 } 142 } 143 144 // Look for cycles to self 145 for _, e := range g.Edges() { 146 if e.Source() == e.Target() { 147 err = multierror.Append(err, fmt.Errorf( 148 "Self reference: %s", VertexName(e.Source()))) 149 } 150 } 151 152 return err 153 } 154 155 func (g *AcyclicGraph) Cycles() [][]Vertex { 156 var cycles [][]Vertex 157 for _, cycle := range StronglyConnected(&g.Graph) { 158 if len(cycle) > 1 { 159 cycles = append(cycles, cycle) 160 } 161 } 162 return cycles 163 } 164 165 // Walk walks the graph, calling your callback as each node is visited. 166 // This will walk nodes in parallel if it can. The resulting diagnostics 167 // contains problems from all graphs visited, in no particular order. 168 func (g *AcyclicGraph) Walk(cb WalkFunc) tfdiags.Diagnostics { 169 defer g.debug.BeginOperation(typeWalk, "").End("") 170 171 w := &Walker{Callback: cb, Reverse: true} 172 w.Update(g) 173 return w.Wait() 174 } 175 176 // simple convenience helper for converting a dag.Set to a []Vertex 177 func AsVertexList(s *Set) []Vertex { 178 rawList := s.List() 179 vertexList := make([]Vertex, len(rawList)) 180 for i, raw := range rawList { 181 vertexList[i] = raw.(Vertex) 182 } 183 return vertexList 184 } 185 186 type vertexAtDepth struct { 187 Vertex Vertex 188 Depth int 189 } 190 191 // depthFirstWalk does a depth-first walk of the graph starting from 192 // the vertices in start. 193 func (g *AcyclicGraph) DepthFirstWalk(start []Vertex, f DepthWalkFunc) error { 194 return g.depthFirstWalk(start, true, f) 195 } 196 197 // This internal method provides the option of not sorting the vertices during 198 // the walk, which we use for the Transitive reduction. 199 // Some configurations can lead to fully-connected subgraphs, which makes our 200 // transitive reduction algorithm O(n^3). This is still passable for the size 201 // of our graphs, but the additional n^2 sort operations would make this 202 // uncomputable in a reasonable amount of time. 203 func (g *AcyclicGraph) depthFirstWalk(start []Vertex, sorted bool, f DepthWalkFunc) error { 204 defer g.debug.BeginOperation(typeDepthFirstWalk, "").End("") 205 206 seen := make(map[Vertex]struct{}) 207 frontier := make([]*vertexAtDepth, len(start)) 208 for i, v := range start { 209 frontier[i] = &vertexAtDepth{ 210 Vertex: v, 211 Depth: 0, 212 } 213 } 214 for len(frontier) > 0 { 215 // Pop the current vertex 216 n := len(frontier) 217 current := frontier[n-1] 218 frontier = frontier[:n-1] 219 220 // Check if we've seen this already and return... 221 if _, ok := seen[current.Vertex]; ok { 222 continue 223 } 224 seen[current.Vertex] = struct{}{} 225 226 // Visit the current node 227 if err := f(current.Vertex, current.Depth); err != nil { 228 return err 229 } 230 231 // Visit targets of this in a consistent order. 232 targets := AsVertexList(g.DownEdges(current.Vertex)) 233 234 if sorted { 235 sort.Sort(byVertexName(targets)) 236 } 237 238 for _, t := range targets { 239 frontier = append(frontier, &vertexAtDepth{ 240 Vertex: t, 241 Depth: current.Depth + 1, 242 }) 243 } 244 } 245 246 return nil 247 } 248 249 // reverseDepthFirstWalk does a depth-first walk _up_ the graph starting from 250 // the vertices in start. 251 func (g *AcyclicGraph) ReverseDepthFirstWalk(start []Vertex, f DepthWalkFunc) error { 252 defer g.debug.BeginOperation(typeReverseDepthFirstWalk, "").End("") 253 254 seen := make(map[Vertex]struct{}) 255 frontier := make([]*vertexAtDepth, len(start)) 256 for i, v := range start { 257 frontier[i] = &vertexAtDepth{ 258 Vertex: v, 259 Depth: 0, 260 } 261 } 262 for len(frontier) > 0 { 263 // Pop the current vertex 264 n := len(frontier) 265 current := frontier[n-1] 266 frontier = frontier[:n-1] 267 268 // Check if we've seen this already and return... 269 if _, ok := seen[current.Vertex]; ok { 270 continue 271 } 272 seen[current.Vertex] = struct{}{} 273 274 // Add next set of targets in a consistent order. 275 targets := AsVertexList(g.UpEdges(current.Vertex)) 276 sort.Sort(byVertexName(targets)) 277 for _, t := range targets { 278 frontier = append(frontier, &vertexAtDepth{ 279 Vertex: t, 280 Depth: current.Depth + 1, 281 }) 282 } 283 284 // Visit the current node 285 if err := f(current.Vertex, current.Depth); err != nil { 286 return err 287 } 288 } 289 290 return nil 291 } 292 293 // byVertexName implements sort.Interface so a list of Vertices can be sorted 294 // consistently by their VertexName 295 type byVertexName []Vertex 296 297 func (b byVertexName) Len() int { return len(b) } 298 func (b byVertexName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 299 func (b byVertexName) Less(i, j int) bool { 300 return VertexName(b[i]) < VertexName(b[j]) 301 }