From d4d57dd559246774724a29eeae927463964d504a Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Mon, 14 May 2018 15:29:16 -0700 Subject: [PATCH] dockerfile: add syntax directive for introducing new features Signed-off-by: Tonis Tiigi --- frontend/dockerfile/builder/build.go | 21 ++++++++ .../dockerfile/dockerfile2llb/directives.go | 38 ++++++++++++++ .../dockerfile2llb/directives_test.go | 52 +++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 frontend/dockerfile/dockerfile2llb/directives.go create mode 100644 frontend/dockerfile/dockerfile2llb/directives_test.go diff --git a/frontend/dockerfile/builder/build.go b/frontend/dockerfile/builder/build.go index b9d421d3250b7..f6174506997ec 100644 --- a/frontend/dockerfile/builder/build.go +++ b/frontend/dockerfile/builder/build.go @@ -116,6 +116,13 @@ func Build(ctx context.Context, c client.Client) error { return err } + if _, ok := c.Opts()["cmdline"]; !ok { + ref, argv, ok := dockerfile2llb.DetectSyntax(bytes.NewBuffer(dtDockerfile)) + if ok { + return forwardGateway(ctx, c, ref, argv) + } + } + st, img, err := dockerfile2llb.Dockerfile2LLB(ctx, dtDockerfile, dockerfile2llb.ConvertOpt{ Target: opts[keyTarget], MetaResolver: c, @@ -158,6 +165,20 @@ func Build(ctx context.Context, c client.Client) error { return nil } +func forwardGateway(ctx context.Context, c client.Client, ref string, argv string) error { + opts := c.Opts() + if opts == nil { + opts = map[string]string{} + } + opts["cmdline"] = argv + opts["source"] = ref + _, err := c.Solve(ctx, client.SolveRequest{ + Frontend: "gateway.v0", + FrontendOpt: opts, + }, nil, true) + return err +} + func filter(opt map[string]string, key string) map[string]string { m := map[string]string{} for k, v := range opt { diff --git a/frontend/dockerfile/dockerfile2llb/directives.go b/frontend/dockerfile/dockerfile2llb/directives.go new file mode 100644 index 0000000000000..bed7547e51562 --- /dev/null +++ b/frontend/dockerfile/dockerfile2llb/directives.go @@ -0,0 +1,38 @@ +package dockerfile2llb + +import ( + "bufio" + "io" + "regexp" + "strings" +) + +const keySyntax = "syntax" + +var reDirective = regexp.MustCompile(`^#\s*([a-z][a-z0-9]*)\s*=\s*(.+?)\s*$`) + +func DetectSyntax(r io.Reader) (string, string, bool) { + directives := ParseDirectives(r) + if len(directives) == 0 { + return "", "", false + } + v, ok := directives[keySyntax] + if !ok { + return "", "", false + } + p := strings.SplitN(v, " ", 2) + return p[0], v, true +} + +func ParseDirectives(r io.Reader) map[string]string { + m := map[string]string{} + s := bufio.NewScanner(r) + for s.Scan() { + match := reDirective.FindStringSubmatch(s.Text()) + if len(match) == 0 { + return m + } + m[match[1]] = match[2] + } + return m +} diff --git a/frontend/dockerfile/dockerfile2llb/directives_test.go b/frontend/dockerfile/dockerfile2llb/directives_test.go new file mode 100644 index 0000000000000..89838baa3c4c0 --- /dev/null +++ b/frontend/dockerfile/dockerfile2llb/directives_test.go @@ -0,0 +1,52 @@ +package dockerfile2llb + +import ( + "bytes" + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestDirectives(t *testing.T) { + t.Parallel() + + dt := `#escape=\ +# key = FOO bar + +# smth +` + + d := ParseDirectives(bytes.NewBuffer([]byte(dt))) + require.Equal(t, len(d), 2, fmt.Sprintf("%+v", d)) + + v, ok := d["escape"] + require.True(t, ok) + require.Equal(t, v, "\\") + + v, ok = d["key"] + require.True(t, ok) + require.Equal(t, v, "FOO bar") +} + +func TestSyntaxDirective(t *testing.T) { + t.Parallel() + + dt := `# syntax = dockerfile:experimental // opts +FROM busybox +` + + ref, argv, ok := DetectSyntax(bytes.NewBuffer([]byte(dt))) + require.True(t, ok) + require.Equal(t, ref, "dockerfile:experimental") + require.Equal(t, argv, "dockerfile:experimental // opts") + + dt = `FROM busybox +RUN ls +` + ref, argv, ok = DetectSyntax(bytes.NewBuffer([]byte(dt))) + require.False(t, ok) + require.Equal(t, ref, "") + require.Equal(t, argv, "") + +}