Skip to content

Commit f7803ed

Browse files
committed
dockerfile: add subrequest for dumping LLB contents for dockerfile
This subrequest is similar to `buildctl debug dump-llb` but works on a running buildkit daemon with the dockerfile frontend. The `--call=dumpllb` option can be used to dump the raw LLB along with the metadata and anything else in the definition to a JSON format. This is useful for debugging purposes. Signed-off-by: Jonathan A. Sternberg <[email protected]>
1 parent bc3666b commit f7803ed

File tree

5 files changed

+127
-3
lines changed

5 files changed

+127
-3
lines changed

frontend/dockerfile/builder/build.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/moby/buildkit/frontend/dockerui"
1717
"github.com/moby/buildkit/frontend/gateway/client"
1818
gwpb "github.com/moby/buildkit/frontend/gateway/pb"
19+
"github.com/moby/buildkit/frontend/subrequests/dumpllb"
1920
"github.com/moby/buildkit/frontend/subrequests/lint"
2021
"github.com/moby/buildkit/frontend/subrequests/outline"
2122
"github.com/moby/buildkit/frontend/subrequests/targets"
@@ -96,6 +97,9 @@ func Build(ctx context.Context, c client.Client) (_ *client.Result, err error) {
9697
Lint: func(ctx context.Context) (*lint.LintResults, error) {
9798
return dockerfile2llb.DockerfileLint(ctx, src.Data, convertOpt)
9899
},
100+
DumpLLB: func(ctx context.Context) (*dumpllb.Result, error) {
101+
return dockerfile2llb.DockerfileDumpLLB(ctx, src.Data, convertOpt)
102+
},
99103
}); err != nil {
100104
return nil, err
101105
} else if ok {

frontend/dockerfile/dockerfile2llb/convert.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/moby/buildkit/frontend/dockerfile/parser"
3131
"github.com/moby/buildkit/frontend/dockerfile/shell"
3232
"github.com/moby/buildkit/frontend/dockerui"
33+
"github.com/moby/buildkit/frontend/subrequests/dumpllb"
3334
"github.com/moby/buildkit/frontend/subrequests/lint"
3435
"github.com/moby/buildkit/frontend/subrequests/outline"
3536
"github.com/moby/buildkit/frontend/subrequests/targets"
@@ -123,6 +124,14 @@ func Dockerfile2Outline(ctx context.Context, dt []byte, opt ConvertOpt) (*outlin
123124
return &o, nil
124125
}
125126

127+
func DockerfileDumpLLB(ctx context.Context, dt []byte, opt ConvertOpt) (*dumpllb.Result, error) {
128+
ds, err := toDispatchState(ctx, dt, opt)
129+
if err != nil {
130+
return nil, err
131+
}
132+
return ds.DumpLLB(ctx)
133+
}
134+
126135
func DockerfileLint(ctx context.Context, dt []byte, opt ConvertOpt) (*lint.LintResults, error) {
127136
results := &lint.LintResults{}
128137
sourceIndex := results.AddSource(opt.SourceMap)
@@ -1223,7 +1232,7 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE
12231232
// Run command can potentially access any file. Mark the full filesystem as used.
12241233
d.paths["/"] = struct{}{}
12251234

1226-
var args = c.CmdLine
1235+
args := c.CmdLine
12271236
if len(c.Files) > 0 {
12281237
if len(args) != 1 || !c.PrependShell {
12291238
return errors.Errorf("parsing produced an invalid run command: %v", args)
@@ -1760,7 +1769,7 @@ func dispatchOnbuild(d *dispatchState, c *instructions.OnbuildCommand) error {
17601769
func dispatchCmd(d *dispatchState, c *instructions.CmdCommand, lint *linter.Linter) error {
17611770
validateUsedOnce(c, &d.cmd, lint)
17621771

1763-
var args = c.CmdLine
1772+
args := c.CmdLine
17641773
if c.PrependShell {
17651774
if len(d.image.Config.Shell) == 0 {
17661775
msg := linter.RuleJSONArgsRecommended.Format(c.Name())
@@ -1776,7 +1785,7 @@ func dispatchCmd(d *dispatchState, c *instructions.CmdCommand, lint *linter.Lint
17761785
func dispatchEntrypoint(d *dispatchState, c *instructions.EntrypointCommand, lint *linter.Linter) error {
17771786
validateUsedOnce(c, &d.entrypoint, lint)
17781787

1779-
var args = c.CmdLine
1788+
args := c.CmdLine
17801789
if c.PrependShell {
17811790
if len(d.image.Config.Shell) == 0 {
17821791
msg := linter.RuleJSONArgsRecommended.Format(c.Name())
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package dockerfile2llb
2+
3+
import (
4+
"context"
5+
6+
"github.com/moby/buildkit/frontend/subrequests/dumpllb"
7+
"github.com/moby/buildkit/solver/pb"
8+
digest "github.com/opencontainers/go-digest"
9+
)
10+
11+
func (ds *dispatchState) DumpLLB(ctx context.Context) (*dumpllb.Result, error) {
12+
def, err := ds.state.Marshal(ctx)
13+
if err != nil {
14+
return nil, err
15+
}
16+
17+
res := &dumpllb.Result{
18+
Def: make(map[digest.Digest]*pb.Op, len(def.Def)),
19+
Metadata: def.Metadata,
20+
Source: def.Source,
21+
}
22+
for _, dt := range def.Def {
23+
var op pb.Op
24+
if err := op.UnmarshalVT(dt); err != nil {
25+
return nil, err
26+
}
27+
28+
dgst := digest.FromBytes(dt)
29+
res.Def[dgst] = &op
30+
}
31+
return res, nil
32+
}

frontend/dockerui/requests.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/moby/buildkit/frontend/gateway/client"
99
"github.com/moby/buildkit/frontend/subrequests"
10+
"github.com/moby/buildkit/frontend/subrequests/dumpllb"
1011
"github.com/moby/buildkit/frontend/subrequests/lint"
1112
"github.com/moby/buildkit/frontend/subrequests/outline"
1213
"github.com/moby/buildkit/frontend/subrequests/targets"
@@ -21,6 +22,7 @@ type RequestHandler struct {
2122
Outline func(context.Context) (*outline.Outline, error)
2223
ListTargets func(context.Context) (*targets.List, error)
2324
Lint func(context.Context) (*lint.LintResults, error)
25+
DumpLLB func(context.Context) (*dumpllb.Result, error)
2426
AllowOther bool
2527
}
2628

@@ -69,6 +71,16 @@ func (bc *Client) HandleSubrequest(ctx context.Context, h RequestHandler) (*clie
6971
res, err := warnings.ToResult(nil)
7072
return res, true, err
7173
}
74+
case dumpllb.SubrequestDumpLLBDefinition.Name:
75+
if f := h.DumpLLB; f != nil {
76+
result, err := f(ctx)
77+
if err != nil {
78+
return nil, false, err
79+
}
80+
81+
res, err := result.ToResult()
82+
return res, true, err
83+
}
7284
}
7385
if h.AllowOther {
7486
return nil, false, nil
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package dumpllb
2+
3+
import (
4+
"encoding/json"
5+
6+
"github.com/moby/buildkit/client/llb"
7+
"github.com/moby/buildkit/frontend/gateway/client"
8+
"github.com/moby/buildkit/frontend/subrequests"
9+
"github.com/moby/buildkit/solver/pb"
10+
"github.com/opencontainers/go-digest"
11+
"google.golang.org/protobuf/encoding/protojson"
12+
)
13+
14+
const RequestDumpLLB = "frontend.dumpllb"
15+
16+
var SubrequestDumpLLBDefinition = subrequests.Request{
17+
Name: RequestDumpLLB,
18+
Version: "0.1.0",
19+
Type: subrequests.TypeRPC,
20+
Description: "Dump LLB output of a Dockerfile",
21+
Opts: []subrequests.Named{},
22+
Metadata: []subrequests.Named{
23+
{Name: "result.json"},
24+
},
25+
}
26+
27+
type Result struct {
28+
Def map[digest.Digest]*pb.Op `json:"def"`
29+
Metadata map[digest.Digest]llb.OpMetadata `json:"metadata"`
30+
Source *pb.Source `json:"source"`
31+
}
32+
33+
func (result *Result) ToResult() (*client.Result, error) {
34+
res := client.NewResult()
35+
dt, err := json.MarshalIndent(result, "", " ")
36+
if err != nil {
37+
return nil, err
38+
}
39+
res.AddMeta("result.json", dt)
40+
41+
res.AddMeta("version", []byte(SubrequestDumpLLBDefinition.Version))
42+
return res, nil
43+
}
44+
45+
func (result *Result) MarshalJSON() ([]byte, error) {
46+
var jsonResult struct {
47+
Def map[digest.Digest]json.RawMessage `json:"def"`
48+
Metadata map[digest.Digest]llb.OpMetadata `json:"metadata"`
49+
Source json.RawMessage `json:"source"`
50+
}
51+
jsonResult.Def = make(map[digest.Digest]json.RawMessage, len(result.Def))
52+
for dgst, op := range result.Def {
53+
dt, err := protojson.Marshal(op)
54+
if err != nil {
55+
return nil, err
56+
}
57+
jsonResult.Def[dgst] = dt
58+
}
59+
jsonResult.Metadata = result.Metadata
60+
61+
src, err := protojson.Marshal(result.Source)
62+
if err != nil {
63+
return nil, err
64+
}
65+
jsonResult.Source = src
66+
return json.Marshal(&jsonResult)
67+
}

0 commit comments

Comments
 (0)