github.com/richardmarshall/terraform@v0.9.5-0.20170429023105-15704cc6ee35/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 e.Encode(marshalTransform{ 277 Type: typeTransform, 278 AddVertex: newMarshalVertex(v), 279 }) 280 } 281 282 // Remove records the removal of Vertex v. 283 func (e *encoder) Remove(v Vertex) { 284 e.Encode(marshalTransform{ 285 Type: typeTransform, 286 RemoveVertex: newMarshalVertex(v), 287 }) 288 } 289 290 func (e *encoder) Connect(edge Edge) { 291 e.Encode(marshalTransform{ 292 Type: typeTransform, 293 AddEdge: newMarshalEdge(edge), 294 }) 295 } 296 297 func (e *encoder) RemoveEdge(edge Edge) { 298 e.Encode(marshalTransform{ 299 Type: typeTransform, 300 RemoveEdge: newMarshalEdge(edge), 301 }) 302 } 303 304 // BeginOperation marks the start of set of graph transformations, and returns 305 // an EndDebugOperation func to be called once the opration is complete. 306 func (e *encoder) BeginOperation(op string, info string) DebugOperationEnd { 307 if e == nil { 308 return func(string) {} 309 } 310 311 e.Encode(marshalOperation{ 312 Type: typeOperation, 313 Begin: op, 314 Info: info, 315 }) 316 317 return func(info string) { 318 e.Encode(marshalOperation{ 319 Type: typeOperation, 320 End: op, 321 Info: info, 322 }) 323 } 324 } 325 326 // structure for recording graph transformations 327 type marshalTransform struct { 328 // Type: "Transform" 329 Type string 330 AddEdge *marshalEdge `json:",omitempty"` 331 RemoveEdge *marshalEdge `json:",omitempty"` 332 AddVertex *marshalVertex `json:",omitempty"` 333 RemoveVertex *marshalVertex `json:",omitempty"` 334 } 335 336 func (t marshalTransform) Transform(g *marshalGraph) { 337 switch { 338 case t.AddEdge != nil: 339 g.connect(t.AddEdge) 340 case t.RemoveEdge != nil: 341 g.removeEdge(t.RemoveEdge) 342 case t.AddVertex != nil: 343 g.add(t.AddVertex) 344 case t.RemoveVertex != nil: 345 g.remove(t.RemoveVertex) 346 } 347 } 348 349 // this structure allows us to decode any object in the json stream for 350 // inspection, then re-decode it into a proper struct if needed. 351 type streamDecode struct { 352 Type string 353 Map map[string]interface{} 354 JSON []byte 355 } 356 357 func (s *streamDecode) UnmarshalJSON(d []byte) error { 358 s.JSON = d 359 err := json.Unmarshal(d, &s.Map) 360 if err != nil { 361 return err 362 } 363 364 if t, ok := s.Map["Type"]; ok { 365 s.Type, _ = t.(string) 366 } 367 return nil 368 } 369 370 // structure for recording the beginning and end of any multi-step 371 // transformations. These are informational, and not required to reproduce the 372 // graph state. 373 type marshalOperation struct { 374 Type string 375 Begin string `json:",omitempty"` 376 End string `json:",omitempty"` 377 Info string `json:",omitempty"` 378 } 379 380 // decodeGraph decodes a marshalGraph from an encoded graph stream. 381 func decodeGraph(r io.Reader) (*marshalGraph, error) { 382 dec := json.NewDecoder(r) 383 384 // a stream should always start with a graph 385 g := &marshalGraph{} 386 387 err := dec.Decode(g) 388 if err != nil { 389 return nil, err 390 } 391 392 // now replay any operations that occurred on the original graph 393 for dec.More() { 394 s := &streamDecode{} 395 err := dec.Decode(s) 396 if err != nil { 397 return g, err 398 } 399 400 // the only Type we're concerned with here is Transform to complete the 401 // Graph 402 if s.Type != typeTransform { 403 continue 404 } 405 406 t := &marshalTransform{} 407 err = json.Unmarshal(s.JSON, t) 408 if err != nil { 409 return g, err 410 } 411 t.Transform(g) 412 } 413 return g, nil 414 } 415 416 // marshalVertexInfo allows encoding arbitrary information about the a single 417 // Vertex in the logs. These are accumulated for informational display while 418 // rebuilding the graph. 419 type marshalVertexInfo struct { 420 Type string 421 Vertex *marshalVertex 422 Info string 423 } 424 425 func newVertexInfo(infoType string, v Vertex, info string) *marshalVertexInfo { 426 return &marshalVertexInfo{ 427 Type: infoType, 428 Vertex: newMarshalVertex(v), 429 Info: info, 430 } 431 } 432 433 // marshalEdgeInfo allows encoding arbitrary information about the a single 434 // Edge in the logs. These are accumulated for informational display while 435 // rebuilding the graph. 436 type marshalEdgeInfo struct { 437 Type string 438 Edge *marshalEdge 439 Info string 440 } 441 442 func newEdgeInfo(infoType string, e Edge, info string) *marshalEdgeInfo { 443 return &marshalEdgeInfo{ 444 Type: infoType, 445 Edge: newMarshalEdge(e), 446 Info: info, 447 } 448 } 449 450 // JSON2Dot reads a Graph debug log from and io.Reader, and converts the final 451 // graph dot format. 452 // 453 // TODO: Allow returning the output at a certain point during decode. 454 // Encode extra information from the json log into the Dot. 455 func JSON2Dot(r io.Reader) ([]byte, error) { 456 g, err := decodeGraph(r) 457 if err != nil { 458 return nil, err 459 } 460 461 return g.Dot(nil), nil 462 }