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
parent
6896b5c414
commit
42bfec5028
|
@ -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,12 +43,16 @@ func dumpLLB(clicontext *cli.Context) error {
|
|||
if 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"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue