github.com/cmalfait/terraform@v0.11.12-beta1/dag/marshal.go (about) 1 package dag 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "log" 8 "reflect" 9 "sort" 10 "strconv" 11 "sync" 12 ) 13 14 const ( 15 typeOperation = "Operation" 16 typeTransform = "Transform" 17 typeWalk = "Walk" 18 typeDepthFirstWalk = "DepthFirstWalk" 19 typeReverseDepthFirstWalk = "ReverseDepthFirstWalk" 20 typeTransitiveReduction = "TransitiveReduction" 21 typeEdgeInfo = "EdgeInfo" 22 typeVertexInfo = "VertexInfo" 23 typeVisitInfo = "VisitInfo" 24 ) 25 26 // the marshal* structs are for serialization of the graph data. 27 type marshalGraph struct { 28 // Type is always "Graph", for identification as a top level object in the 29 // JSON stream. 30 Type string 31 32 // Each marshal structure requires a unique ID so that it can be referenced 33 // by other structures. 34 ID string `json:",omitempty"` 35 36 // Human readable name for this graph. 37 Name string `json:",omitempty"` 38 39 // Arbitrary attributes that can be added to the output. 40 Attrs map[string]string `json:",omitempty"` 41 42 // List of graph vertices, sorted by ID. 43 Vertices []*marshalVertex `json:",omitempty"` 44 45 // List of edges, sorted by Source ID. 46 Edges []*marshalEdge `json:",omitempty"` 47 48 // Any number of subgraphs. A subgraph itself is considered a vertex, and 49 // may be referenced by either end of an edge. 50 Subgraphs []*marshalGraph `json:",omitempty"` 51 52 // Any lists of vertices that are included in cycles. 53 Cycles [][]*marshalVertex `json:",omitempty"` 54 } 55 56 // The add, remove, connect, removeEdge methods mirror the basic Graph 57 // manipulations to reconstruct a marshalGraph from a debug log. 58 func (g *marshalGraph) add(v *marshalVertex) { 59 g.Vertices = append(g.Vertices, v) 60 sort.Sort(vertices(g.Vertices)) 61 } 62 63 func (g *marshalGraph) remove(v *marshalVertex) { 64 for i, existing := range g.Vertices { 65 if v.ID == existing.ID { 66 g.Vertices = append(g.Vertices[:i], g.Vertices[i+1:]...) 67 return 68 } 69 } 70 } 71 72 func (g *marshalGraph) connect(e *marshalEdge) { 73 g.Edges = append(g.Edges, e) 74 sort.Sort(edges(g.Edges)) 75 } 76 77 func (g *marshalGraph) removeEdge(e *marshalEdge) { 78 for i, existing := range g.Edges { 79 if e.Source == existing.Source && e.Target == existing.Target { 80 g.Edges = append(g.Edges[:i], g.Edges[i+1:]...) 81 return 82 } 83 } 84 } 85 86 func (g *marshalGraph) vertexByID(id string) *marshalVertex { 87 for _, v := range g.Vertices { 88 if id == v.ID { 89 return v 90 } 91 } 92 return nil 93 } 94 95 type marshalVertex struct { 96 // Unique ID, used to reference this vertex from other structures. 97 ID string 98 99 // Human readable name 100 Name string `json:",omitempty"` 101 102 Attrs map[string]string `json:",omitempty"` 103 104 // This is to help transition from the old Dot interfaces. We record if the 105 // node was a GraphNodeDotter here, so we can call it to get attributes. 106 graphNodeDotter GraphNodeDotter 107 } 108 109 func newMarshalVertex(v Vertex) *marshalVertex { 110 dn, ok := v.(GraphNodeDotter) 111 if !ok { 112 dn = nil 113 } 114 115 return &marshalVertex{ 116 ID: marshalVertexID(v), 117 Name: VertexName(v), 118 Attrs: make(map[string]string), 119 graphNodeDotter: dn, 120 } 121 } 122 123 // vertices is a sort.Interface implementation for sorting vertices by ID 124 type vertices []*marshalVertex 125 126 func (v vertices) Less(i, j int) bool { return v[i].Name < v[j].Name } 127 func (v vertices) Len() int { return len(v) } 128 func (v vertices) Swap(i, j int) { v[i], v[j] = v[j], v[i] } 129 130 type marshalEdge struct { 131 // Human readable name 132 Name string 133 134 // Source and Target Vertices by ID 135 Source string 136 Target string 137 138 Attrs map[string]string `json:",omitempty"` 139 } 140 141 func newMarshalEdge(e Edge) *marshalEdge { 142 return &marshalEdge{ 143 Name: fmt.Sprintf("%s|%s", VertexName(e.Source()), VertexName(e.Target())), 144 Source: marshalVertexID(e.Source()), 145 Target: marshalVertexID(e.Target()), 146 Attrs: make(map[string]string), 147 } 148 } 149 150 // edges is a sort.Interface implementation for sorting edges by Source ID 151 type edges []*marshalEdge 152 153 func (e edges) Less(i, j int) bool { return e[i].Name < e[j].Name } 154 func (e edges) Len() int { return len(e) } 155 func (e edges) Swap(i, j int) { e[i], e[j] = e[j], e[i] } 156 157 // build a marshalGraph structure from a *Graph 158 func newMarshalGraph(name string, g *Graph) *marshalGraph { 159 mg := &marshalGraph{ 160 Type: "Graph", 161 Name: name, 162 Attrs: make(map[string]string), 163 } 164 165 for _, v := range g.Vertices() { 166 id := marshalVertexID(v) 167 if sg, ok := marshalSubgrapher(v); ok { 168 smg := newMarshalGraph(VertexName(v), sg) 169 smg.ID = id 170 mg.Subgraphs = append(mg.Subgraphs, smg) 171 } 172 173 mv := newMarshalVertex(v) 174 mg.Vertices = append(mg.Vertices, mv) 175 } 176 177 sort.Sort(vertices(mg.Vertices)) 178 179 for _, e := range g.Edges() { 180 mg.Edges = append(mg.Edges, newMarshalEdge(e)) 181 } 182 183 sort.Sort(edges(mg.Edges)) 184 185 for _, c := range (&AcyclicGraph{*g}).Cycles() { 186 var cycle []*marshalVertex 187 for _, v := range c { 188 mv := newMarshalVertex(v) 189 cycle = append(cycle, mv) 190 } 191 mg.Cycles = append(mg.Cycles, cycle) 192 } 193 194 return mg 195 } 196 197 // Attempt to return a unique ID for any vertex. 198 func marshalVertexID(v Vertex) string { 199 val := reflect.ValueOf(v) 200 switch val.Kind() { 201 case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: 202 return strconv.Itoa(int(val.Pointer())) 203 case reflect.Interface: 204 return strconv.Itoa(int(val.InterfaceData()[1])) 205 } 206 207 if v, ok := v.(Hashable); ok { 208 h := v.Hashcode() 209 if h, ok := h.(string); ok { 210 return h 211 } 212 } 213 214 // fallback to a name, which we hope is unique. 215 return VertexName(v) 216 217 // we could try harder by attempting to read the arbitrary value from the 218 // interface, but we shouldn't get here from terraform right now. 219 } 220 221 // check for a Subgrapher, and return the underlying *Graph. 222 func marshalSubgrapher(v Vertex) (*Graph, bool) { 223 sg, ok := v.(Subgrapher) 224 if !ok { 225 return nil, false 226 } 227 228 switch g := sg.Subgraph().DirectedGraph().(type) { 229 case *Graph: 230 return g, true 231 case *AcyclicGraph: 232 return &g.Graph, true 233 } 234 235 return nil, false 236 } 237 238 // The DebugOperationEnd func type provides a way to call an End function via a 239 // method call, allowing for the chaining of methods in a defer statement. 240 type DebugOperationEnd func(string) 241 242 // End calls function e with the info parameter, marking the end of this 243 // operation in the logs. 244 func (e DebugOperationEnd) End(info string) { e(info) } 245 246 // encoder provides methods to write debug data to an io.Writer, and is a noop 247 // when no writer is present 248 type encoder struct { 249 sync.Mutex 250 w io.Writer 251 } 252 253 // Encode is analogous to json.Encoder.Encode 254 func (e *encoder) Encode(i interface{}) { 255 if e == nil || e.w == nil { 256 return 257 } 258 e.Lock() 259 defer e.Unlock() 260 261 js, err := json.Marshal(i) 262 if err != nil { 263 log.Println("[ERROR] dag:", err) 264 return 265 } 266 js = append(js, '\n') 267 268 _, err = e.w.Write(js) 269 if err != nil { 270 log.Println("[ERROR] dag:", err) 271 return 272 } 273 } 274 275 func (e *encoder) Add(v Vertex) { 276 if e == nil { 277 return 278 } 279 e.Encode(marshalTransform{ 280 Type: typeTransform, 281 AddVertex: newMarshalVertex(v), 282 }) 283 } 284 285 // Remove records the removal of Vertex v. 286 func (e *encoder) Remove(v Vertex) { 287 if e == nil { 288 return 289 } 290 e.Encode(marshalTransform{ 291 Type: typeTransform, 292 RemoveVertex: newMarshalVertex(v), 293 }) 294 } 295 296 func (e *encoder) Connect(edge Edge) { 297 if e == nil { 298 return 299 } 300 e.Encode(marshalTransform{ 301 Type: typeTransform, 302 AddEdge: newMarshalEdge(edge), 303 }) 304 } 305 306 func (e *encoder) RemoveEdge(edge Edge) { 307 if e == nil { 308 return 309 } 310 e.Encode(marshalTransform{ 311 Type: typeTransform, 312 RemoveEdge: newMarshalEdge(edge), 313 }) 314 } 315 316 // BeginOperation marks the start of set of graph transformations, and returns 317 // an EndDebugOperation func to be called once the opration is complete. 318 func (e *encoder) BeginOperation(op string, info string) DebugOperationEnd { 319 if e == nil { 320 return func(string) {} 321 } 322 323 e.Encode(marshalOperation{ 324 Type: typeOperation, 325 Begin: op, 326 Info: info, 327 }) 328 329 return func(info string) { 330 e.Encode(marshalOperation{ 331 Type: typeOperation, 332 End: op, 333 Info: info, 334 }) 335 } 336 } 337 338 // structure for recording graph transformations 339 type marshalTransform struct { 340 // Type: "Transform" 341 Type string 342 AddEdge *marshalEdge `json:",omitempty"` 343 RemoveEdge *marshalEdge `json:",omitempty"` 344 AddVertex *marshalVertex `json:",omitempty"` 345 RemoveVertex *marshalVertex `json:",omitempty"` 346 } 347 348 func (t marshalTransform) Transform(g *marshalGraph) { 349 switch { 350 case t.AddEdge != nil: 351 g.connect(t.AddEdge) 352 case t.RemoveEdge != nil: 353 g.removeEdge(t.RemoveEdge) 354 case t.AddVertex != nil: 355 g.add(t.AddVertex) 356 case t.RemoveVertex != nil: 357 g.remove(t.RemoveVertex) 358 } 359 } 360 361 // this structure allows us to decode any object in the json stream for 362 // inspection, then re-decode it into a proper struct if needed. 363 type streamDecode struct { 364 Type string 365 Map map[string]interface{} 366 JSON []byte 367 } 368 369 func (s *streamDecode) UnmarshalJSON(d []byte) error { 370 s.JSON = d 371 err := json.Unmarshal(d, &s.Map) 372 if err != nil { 373 return err 374 } 375 376 if t, ok := s.Map["Type"]; ok { 377 s.Type, _ = t.(string) 378 } 379 return nil 380 } 381 382 // structure for recording the beginning and end of any multi-step 383 // transformations. These are informational, and not required to reproduce the 384 // graph state. 385 type marshalOperation struct { 386 Type string 387 Begin string `json:",omitempty"` 388 End string `json:",omitempty"` 389 Info string `json:",omitempty"` 390 } 391 392 // decodeGraph decodes a marshalGraph from an encoded graph stream. 393 func decodeGraph(r io.Reader) (*marshalGraph, error) { 394 dec := json.NewDecoder(r) 395 396 // a stream should always start with a graph 397 g := &marshalGraph{} 398 399 err := dec.Decode(g) 400 if err != nil { 401 return nil, err 402 } 403 404 // now replay any operations that occurred on the original graph 405 for dec.More() { 406 s := &streamDecode{} 407 err := dec.Decode(s) 408 if err != nil { 409 return g, err 410 } 411 412 // the only Type we're concerned with here is Transform to complete the 413 // Graph 414 if s.Type != typeTransform { 415 continue 416 } 417 418 t := &marshalTransform{} 419 err = json.Unmarshal(s.JSON, t) 420 if err != nil { 421 return g, err 422 } 423 t.Transform(g) 424 } 425 return g, nil 426 } 427 428 // marshalVertexInfo allows encoding arbitrary information about the a single 429 // Vertex in the logs. These are accumulated for informational display while 430 // rebuilding the graph. 431 type marshalVertexInfo struct { 432 Type string 433 Vertex *marshalVertex 434 Info string 435 } 436 437 func newVertexInfo(infoType string, v Vertex, info string) *marshalVertexInfo { 438 return &marshalVertexInfo{ 439 Type: infoType, 440 Vertex: newMarshalVertex(v), 441 Info: info, 442 } 443 } 444 445 // marshalEdgeInfo allows encoding arbitrary information about the a single 446 // Edge in the logs. These are accumulated for informational display while 447 // rebuilding the graph. 448 type marshalEdgeInfo struct { 449 Type string 450 Edge *marshalEdge 451 Info string 452 } 453 454 func newEdgeInfo(infoType string, e Edge, info string) *marshalEdgeInfo { 455 return &marshalEdgeInfo{ 456 Type: infoType, 457 Edge: newMarshalEdge(e), 458 Info: info, 459 } 460 } 461 462 // JSON2Dot reads a Graph debug log from and io.Reader, and converts the final 463 // graph dot format. 464 // 465 // TODO: Allow returning the output at a certain point during decode. 466 // Encode extra information from the json log into the Dot. 467 func JSON2Dot(r io.Reader) ([]byte, error) { 468 g, err := decodeGraph(r) 469 if err != nil { 470 return nil, err 471 } 472 473 return g.Dot(nil), nil 474 }