buildctl: add dot output for visualizing llb

usage: buildctl debug dump-llb --dot | dot -Tsvg

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
docker-18.09
Tonis Tiigi 2017-07-17 14:24:19 -07:00
parent 6896b5c414
commit 42bfec5028
1 changed files with 49 additions and 4 deletions

View File

@ -2,8 +2,10 @@ package debug
import (
"encoding/json"
"fmt"
"io"
"os"
"strings"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/solver/pb"
@ -17,6 +19,12 @@ var DumpLLBCommand = cli.Command{
Usage: "dump LLB in human-readable format. LLB can be also passed via stdin. This command does not require the daemon to be running.",
ArgsUsage: "<llbfile>",
Action: dumpLLB,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "dot",
Usage: "Output dot format",
},
},
}
func dumpLLB(clicontext *cli.Context) error {
@ -35,10 +43,14 @@ func dumpLLB(clicontext *cli.Context) error {
if err != nil {
return err
}
enc := json.NewEncoder(os.Stdout)
for _, op := range ops {
if err := enc.Encode(op); err != nil {
return err
if clicontext.Bool("dot") {
writeDot(ops, os.Stdout)
} else {
enc := json.NewEncoder(os.Stdout)
for _, op := range ops {
if err := enc.Encode(op); err != nil {
return err
}
}
}
return nil
@ -65,3 +77,36 @@ func loadLLB(r io.Reader) ([]llbOp, error) {
}
return ops, nil
}
func writeDot(ops []llbOp, w io.Writer) {
fmt.Fprintln(w, "digraph {")
defer fmt.Fprintln(w, "}")
for _, op := range ops {
name, shape := attr(op.Digest, op.Op)
fmt.Fprintf(w, " \"%s\" [label=\"%s\" shape=\"%s\"];\n", op.Digest, name, shape)
}
for _, op := range ops {
for i, inp := range op.Op.Inputs {
label := ""
if eo, ok := op.Op.Op.(*pb.Op_Exec); ok {
for _, m := range eo.Exec.Mounts {
if int(m.Input) == i && m.Dest != "/" {
label = m.Dest
}
}
}
fmt.Fprintf(w, " \"%s\" -> \"%s\" [label=\"%s\"];\n", inp.Digest, op.Digest, label)
}
}
}
func attr(dgst digest.Digest, op pb.Op) (string, string) {
switch op := op.Op.(type) {
case *pb.Op_Source:
return op.Source.Identifier, "ellipse"
case *pb.Op_Exec:
return strings.Join(op.Exec.Meta.Args, " "), "box"
default:
return dgst.String(), "plaintext"
}
}