Re-introduce experimental/graphviz

This commit is contained in:
Peter Bourgon
2015-09-30 17:30:27 +02:00
parent b78be6a71b
commit a086bbe846
4 changed files with 175 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
package main
import (
"bytes"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"os/exec"
"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/report"
)
func dot(w io.Writer, t render.RenderableNodes) {
fmt.Fprintf(w, "digraph G {\n")
fmt.Fprintf(w, "\toutputorder=edgesfirst;\n")
fmt.Fprintf(w, "\toverlap=scale;\n")
fmt.Fprintf(w, "\tnode [style=filled];\n")
fmt.Fprintf(w, "\t\n")
for id, rn := range t {
label := rn.LabelMajor
if len(label) > 20 {
label = label[:20] + "..."
}
fmt.Fprintf(w, "\t%q [label=%q];\n", id, label)
for _, other := range rn.Adjacency {
fmt.Fprintf(w, "\t%q -> %q;\n", id, other)
}
fmt.Fprintf(w, "\t\n")
}
fmt.Fprintf(w, "}\n")
}
func handleHTML(rpt report.Report) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
logRequest("HTML", r)
fmt.Fprintf(w, `<html><head></head><body><img src="/svg?%s" width="95%" height="95%"/></body></html>`, r.URL.RawQuery)
}
}
func handleDot(rpt report.Report) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
logRequest("Dot", r)
r.ParseForm()
var (
engine = getDefault(r.Form, "engine", "twopi")
topology = getDefault(r.Form, "topology", "containers")
)
log.Printf("engine=%s topology=%s", engine, topology)
t, err := renderTo(rpt, topology)
if err != nil {
log.Print(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
log.Printf("render %s to %d node(s)", topology, len(t))
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
dot(w, t)
}
}
func handleSVG(rpt report.Report) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
logRequest("SVG", r)
r.ParseForm()
var (
engine = getDefault(r.Form, "engine", "twopi")
topology = getDefault(r.Form, "topology", "containers")
)
log.Printf("engine=%s topology=%s", engine, topology)
t, err := renderTo(rpt, topology)
if err != nil {
log.Print(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
log.Printf("render %s to %d node(s)", topology, len(t))
vizcmd, err := exec.LookPath(engine)
if err != nil {
log.Print(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
var buf bytes.Buffer
dot(&buf, t)
w.Header().Set("Content-Type", "image/svg+xml")
if err := (&exec.Cmd{
Path: vizcmd,
Args: []string{vizcmd, "-Tsvg"},
Stdin: &buf,
Stdout: w,
Stderr: os.Stderr,
}).Run(); err != nil {
log.Print(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
}
func logRequest(what string, r *http.Request) {
log.Printf("> %s %s %s %s", what, r.RemoteAddr, r.Method, r.URL.String())
}
func getDefault(v url.Values, key, def string) string {
if val := v.Get(key); val != "" {
return val
}
return def
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,31 @@
package main
import (
"encoding/json"
"flag"
"log"
"net/http"
"os"
"github.com/weaveworks/scope/report"
)
func main() {
var (
listen = flag.String("listen", ":8080", "HTTP listen address")
)
flag.Parse()
log.Printf("reading /api/report from stdin...")
var rpt report.Report
if err := json.NewDecoder(os.Stdin).Decode(&rpt); err != nil {
log.Fatal(err)
}
http.HandleFunc("/", handleHTML(rpt))
http.HandleFunc("/dot", handleDot(rpt))
http.HandleFunc("/svg", handleSVG(rpt))
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, _ *http.Request) { http.Error(w, "Stop it", http.StatusTeapot) })
log.Printf("listening on %s", *listen)
http.ListenAndServe(*listen, nil)
}

View File

@@ -0,0 +1,22 @@
package main
import (
"fmt"
"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/report"
)
func renderTo(rpt report.Report, topology string) (render.RenderableNodes, error) {
renderer, ok := map[string]render.Renderer{
"applications": render.FilterUnconnected(render.ProcessWithContainerNameRenderer),
"applications-by-name": render.FilterUnconnected(render.ProcessNameRenderer),
"containers": render.ContainerWithImageNameRenderer,
"containers-by-image": render.ContainerImageRenderer,
"hosts": render.HostRenderer,
}[topology]
if !ok {
return render.RenderableNodes{}, fmt.Errorf("unknown topology %v", topology)
}
return renderer.Render(rpt), nil
}