// dotDump dumps the given image stream tree in DOT syntax func dotDump(root *Node, g *dot.Graph, graphName string) (string, error) { if root == nil { return "", nil } // Add current node rootNamespace, rootName, err := split(root.FullName) if err != nil { return "", err } attrs := make(map[string]string) for _, tag := range root.Tags { setTag(tag, attrs) } var tag string // Inject tag into root's name once.Do(func() { tag = root.Tags[0] }) setLabel(rootName, rootNamespace, attrs, tag) rootName = validDOT(rootName) g.AddNode(graphName, rootName, attrs) // Add edges between current node and its children for _, child := range root.Children { for _, edge := range root.Edges { if child.FullName == edge.To { _, childName, err := split(child.FullName) if err != nil { return "", err } childName = validDOT(childName) edgeNamespace, edgeName, err := split(edge.FullName) if err != nil { return "", err } edgeName = validDOT(edgeName) edgeAttrs := make(map[string]string) setLabel(edgeName, edgeNamespace, edgeAttrs, "") g.AddEdge(rootName, childName, true, edgeAttrs) } } // Recursively add every child and their children as nodes if _, err := dotDump(child, g, graphName); err != nil { return "", err } } dotOutput := g.String() // Parse DOT output for validation if _, err := dot.Parse([]byte(dotOutput)); err != nil { return "", fmt.Errorf("cannot parse DOT output: %v", err) } return dotOutput, nil }
func (n *Node) addToGraphViz(g *gographviz.Graph, parentGraph, parent string) { nodeName := fmt.Sprintf("\"%v\"", n.SimpleString()) g.AddNode(parentGraph, nodeName, nil) if "" != parent { g.AddEdge(parent, nodeName, true, nil) } for _, child := range n.children { child.addToGraphViz(g, parentGraph, nodeName) } }
func DrawPool(graph *ggv.Graph, name string, p Pool) { fpath := poolPath + "/" + name attr := map[string]string{} lbl := " | <f1> HealthCheckEvery : " + p.HealthCheckEvery + " | <f2> Degraded : " + p.Degraded + " | <f3> Critical : " + p.Critical + " | <f4> Healthz : " + p.Healthz + " | <f5> Request : " + p.Request attr["shape"] = "record" attr["label"] = "\"{ <f0>" + name + lbl + "} \"" attr["color"], attr["fillcolor"], attr["fontcolor"] = GetColorAttr(fpath) attr["fontname"] = font attr["style"] = "\"filled\"" graph.AddNode(graphName, "\""+fpath+"\"", attr) }
func DrawTrie(graph *ggv.Graph, name string, t Trie) { fpath := triePath + "/" + name attr := map[string]string{} lbl := " | <f1> Rules : " for _, v := range t.Rules { lbl = lbl + v + ", " } lbl = lbl[0 : len(lbl)-2] attr["shape"] = "record" attr["label"] = "\"{ <f0>" + name + lbl + "} \"" attr["color"], attr["fillcolor"], attr["fontcolor"] = GetColorAttr(fpath) attr["fontname"] = font attr["style"] = "\"filled\"" graph.AddNode(graphName, "\""+fpath+"\"", attr) }
func DrawHosts(graph *ggv.Graph, parentName string, hosts []string) { fpath := poolPath + "/" + parentName + "/hosts" attr := map[string]string{} lbl := "" i := 1 for _, v := range hosts { lbl = lbl + "| <f" + strconv.FormatInt(int64(i), 10) + "> " + v + " " } attr["shape"] = "record" attr["label"] = "\"{ <f0> Hosts " + lbl + "} \"" attr["color"], attr["fillcolor"], attr["fontcolor"] = GetColorAttr(fpath) attr["fontname"] = font attr["style"] = "\"filled\"" graph.AddNode(graphName, "\""+fpath+"\"", attr) graph.AddEdge("\""+poolPath+"/"+parentName+"\"", "\""+fpath+"\"", true, PoolEdgeAttr) }
func DrawRule(graph *ggv.Graph, name string, r Rule) { fpath := rulePath + "/" + name attr := map[string]string{} lbl := " | <f1> Type : " + r.Type + " | <f2> Value : " + r.Value if r.Pool != "" { lbl = lbl + "| <f3> " + StrikethroughString("Next : "+r.Next) + " | <f4> Pool : " + r.Pool } else { lbl = lbl + "| <f3> Next : " + r.Next + " | <f4> " + StrikethroughString("Pool : "+r.Pool) } attr["shape"] = "record" attr["label"] = "\"{ <f0>" + name + lbl + "} \"" attr["color"], attr["fillcolor"], attr["fontcolor"] = GetColorAttr(fpath) attr["fontname"] = font attr["style"] = "\"filled\"" graph.AddNode(graphName, "\""+fpath+"\"", attr) }
func (n *Node) addToGraph(g *gographviz.Graph, parent string) { id := strconv.Itoa(n.id) label := "root" if n.game != nil { label = fmt.Sprintf(`"Score: %d Weights: %v Direction: %s Unit: %+v"`, int(n.score), n.weights, n.d, n.game.currUnit) } attrs := map[string]string{ "label": label, } g.AddNode(parent, id, attrs) g.AddEdge(parent, id, true, nil) for _, c := range n.children { c.addToGraph(g, id) } }
func main() { var ( bytes []byte data map[string]service err error graph *gographviz.Graph project string ) logger = stdlog.GetFromFlags() project = "" // Load docker-compose.yml bytes, err = ioutil.ReadFile("docker-compose.yml") if err != nil { abort(err.Error()) } // Parse it as YML data = make(map[string]service, 5) yaml.Unmarshal(bytes, &data) if err != nil { abort(err.Error()) } // Create directed graph graph = gographviz.NewGraph() graph.SetName(project) graph.SetDir(true) // Add legend graph.AddSubGraph(project, "cluster_legend", map[string]string{"label": "Legend"}) graph.AddNode("cluster_legend", "legend_service", map[string]string{"label": "service"}) graph.AddNode("cluster_legend", "legend_service_with_ports", map[string]string{ "label": "\"service with exposed ports\\n80:80 443:443\\n--volume1[:host_dir1]\\n--volume2[:host_dir2]\"", "shape": "box"}) graph.AddEdge("legend_service", "legend_service_with_ports", true, map[string]string{"label": "links"}) graph.AddEdge("legend_service_with_ports", "legend_service", true, map[string]string{"label": "volumes_from", "style": "dashed"}) // Round one: populate nodes for name, service := range data { var attrs = map[string]string{"label": name} if service.Ports != nil { attrs["label"] += "\\n" + strings.Join(service.Ports, " ") attrs["shape"] = "box" } if service.Volumes != nil { attrs["label"] += "\\n--" + strings.Join(service.Volumes, "\\n--") } attrs["label"] = fmt.Sprintf("\"%s\"", attrs["label"]) graph.AddNode(project, name, attrs) } // Round two: populate connections for name, service := range data { // links if service.Links != nil { for _, linkTo := range service.Links { if strings.Contains(linkTo, ":") { linkTo = strings.Split(linkTo, ":")[0] } graph.AddEdge(name, linkTo, true, nil) } } // volumes_from if service.VolumesFrom != nil { for _, linkTo := range service.VolumesFrom { graph.AddEdge(name, linkTo, true, map[string]string{"style": "dotted"}) } } } fmt.Print(graph) }