From aeea615e85290f213dd2168fc00e0d8a4d46d1b0 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 6 Jun 2018 16:30:51 -0700 Subject: [PATCH 1/3] dockerfile: add run mount parsing Signed-off-by: Tonis Tiigi --- frontend/dockerfile/instructions/bflag.go | 27 +++- frontend/dockerfile/instructions/commands.go | 21 +++ .../instructions/commands_runmount.go | 120 ++++++++++++++++++ frontend/dockerfile/instructions/parse.go | 24 +++- 4 files changed, 183 insertions(+), 9 deletions(-) create mode 100644 frontend/dockerfile/instructions/commands_runmount.go diff --git a/frontend/dockerfile/instructions/bflag.go b/frontend/dockerfile/instructions/bflag.go index 7a81e3c13620..48b2b99779e7 100644 --- a/frontend/dockerfile/instructions/bflag.go +++ b/frontend/dockerfile/instructions/bflag.go @@ -11,6 +11,7 @@ type FlagType int const ( boolType FlagType = iota stringType + stringsType ) // BFlags contains all flags information for the builder @@ -23,10 +24,11 @@ type BFlags struct { // Flag contains all information for a flag type Flag struct { - bf *BFlags - name string - flagType FlagType - Value string + bf *BFlags + name string + flagType FlagType + Value string + StringValues []string } // NewBFlags returns the new BFlags struct @@ -70,6 +72,15 @@ func (bf *BFlags) AddString(name string, def string) *Flag { return flag } +// AddString adds a string flag to BFlags that can match multiple values +func (bf *BFlags) AddStrings(name string) *Flag { + flag := bf.addFlag(name, stringsType) + if flag == nil { + return nil + } + return flag +} + // addFlag is a generic func used by the other AddXXX() func // to add a new flag to the BFlags struct. // Note, any error will be generated when Parse() is called (see Parse). @@ -145,7 +156,7 @@ func (bf *BFlags) Parse() error { return fmt.Errorf("Unknown flag: %s", arg) } - if _, ok = bf.used[arg]; ok { + if _, ok = bf.used[arg]; ok || flag.flagType != stringsType { return fmt.Errorf("Duplicate flag specified: %s", arg) } @@ -173,6 +184,12 @@ func (bf *BFlags) Parse() error { } flag.Value = value + case stringsType: + if index < 0 { + return fmt.Errorf("Missing a value on flag: %s", arg) + } + flag.StringValues = append(flag.StringValues, value) + default: panic("No idea what kind of flag we have! Should never get here!") } diff --git a/frontend/dockerfile/instructions/commands.go b/frontend/dockerfile/instructions/commands.go index 12478463cd22..66330a95cf27 100644 --- a/frontend/dockerfile/instructions/commands.go +++ b/frontend/dockerfile/instructions/commands.go @@ -233,6 +233,7 @@ type ShellDependantCmdLine struct { // type RunCommand struct { withNameAndCode + withExternalData ShellDependantCmdLine } @@ -416,3 +417,23 @@ func HasStage(s []Stage, name string) (int, bool) { } return -1, false } + +type cmdWithExternalData interface { + getExternalValue(k interface{}) interface{} + setExternalValue(k, v interface{}) +} + +type withExternalData struct { + m map[interface{}]interface{} +} + +func (c *withExternalData) getExternalValue(k interface{}) interface{} { + return c.m[k] +} + +func (c *withExternalData) setExternalValue(k, v interface{}) { + if c.m == nil { + c.m = map[interface{}]interface{}{} + } + c.m[k] = v +} diff --git a/frontend/dockerfile/instructions/commands_runmount.go b/frontend/dockerfile/instructions/commands_runmount.go new file mode 100644 index 000000000000..6528d559a966 --- /dev/null +++ b/frontend/dockerfile/instructions/commands_runmount.go @@ -0,0 +1,120 @@ +// +build dfrunmount dfall + +package instructions + +import ( + "encoding/csv" + "strconv" + "strings" + + "github.com/pkg/errors" +) + +type mountsKeyT string + +var mountsKey = mountsKeyT("dockerfile/run/mounts") + +func init() { + parseRunPreHooks = append(parseRunPreHooks, runMountPreHook) + parseRunPostHooks = append(parseRunPostHooks, runMountPostHook) +} + +func runMountPreHook(cmd *RunCommand, req parseRequest) error { + st := &mountState{} + st.flag = req.flags.AddString("mount", "") + cmd.setExternalValue(mountsKey, st) + return nil +} + +func runMountPostHook(cmd *RunCommand, req parseRequest) error { + v := cmd.getExternalValue(mountsKey) + if v != nil { + return errors.Errorf("no mount state") + } + st := v.(*mountState) + var mounts []*Mount + for _, str := range st.flag.StringValues { + m, err := parseMount(str) + if err != nil { + return err + } + mounts = append(mounts, m) + } + return nil +} + +type mountState struct { + flag *Flag + mounts []*Mount +} + +type Mount struct { + Type string + From string + Source string + Target string + ReadOnly bool + CacheID string +} + +func parseMount(value string) (*Mount, error) { + csvReader := csv.NewReader(strings.NewReader(value)) + fields, err := csvReader.Read() + if err != nil { + return nil, errors.Wrap(err, "failed to parse csv mounts") + } + + m := &Mount{ReadOnly: true} + + for _, field := range fields { + parts := strings.SplitN(field, "=", 2) + key := strings.ToLower(parts[0]) + + if len(parts) == 1 { + switch key { + case "readonly", "ro": + m.ReadOnly = true + continue + case "readwrite", "rw": + m.ReadOnly = false + continue + } + } + + if len(parts) != 2 { + return nil, errors.Errorf("invalid field '%s' must be a key=value pair", field) + } + + value := parts[1] + switch key { + case "type": + if value != "" && strings.EqualFold(value, "cache") { + return nil, errors.Errorf("invalid mount type %q", value) + } + m.Type = strings.ToLower(value) + case "from": + m.From = value + case "source", "src": + m.Source = value + case "target", "dst", "destination": + m.Target = value + case "readonly", "ro": + m.ReadOnly, err = strconv.ParseBool(value) + if err != nil { + return nil, errors.Errorf("invalid value for %s: %s", key, value) + } + case "readwrite", "rw": + rw, err := strconv.ParseBool(value) + if err != nil { + return nil, errors.Errorf("invalid value for %s: %s", key, value) + } + m.ReadOnly = !rw + case "id": + m.CacheID = value + default: + return nil, errors.Errorf("unexpected key '%s' in '%s'", key, field) + } + } + + return nil, errors.Errorf("not-implemented") +} diff --git a/frontend/dockerfile/instructions/parse.go b/frontend/dockerfile/instructions/parse.go index 1285ac330633..8c8199e1f725 100644 --- a/frontend/dockerfile/instructions/parse.go +++ b/frontend/dockerfile/instructions/parse.go @@ -24,6 +24,9 @@ type parseRequest struct { original string } +var parseRunPreHooks []func(*RunCommand, parseRequest) error +var parseRunPostHooks []func(*RunCommand, parseRequest) error + func nodeArgs(node *parser.Node) []string { result := []string{} for ; node.Next != nil; node = node.Next { @@ -355,15 +358,28 @@ func parseShellDependentCommand(req parseRequest, emptyAsNil bool) ShellDependan } func parseRun(req parseRequest) (*RunCommand, error) { + cmd := &RunCommand{} + + for _, fn := range parseRunPreHooks { + if err := fn(cmd, req); err != nil { + return nil, err + } + } if err := req.flags.Parse(); err != nil { return nil, err } - return &RunCommand{ - ShellDependantCmdLine: parseShellDependentCommand(req, false), - withNameAndCode: newWithNameAndCode(req), - }, nil + cmd.ShellDependantCmdLine = parseShellDependentCommand(req, false) + cmd.withNameAndCode = newWithNameAndCode(req) + + for _, fn := range parseRunPostHooks { + if err := fn(cmd, req); err != nil { + return nil, err + } + } + + return cmd, nil } func parseCmd(req parseRequest) (*CmdCommand, error) { From af03a526e73c550922d573b23b987710c2a7b546 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Thu, 7 Jun 2018 13:57:35 -0700 Subject: [PATCH 2/3] =?UTF-8?q?dockerfile:=20run=20-=E2=80=94mount=20initi?= =?UTF-8?q?al=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supports binds from images and context and cache mounts. Signed-off-by: Tonis Tiigi --- .../cmd/dockerfile-frontend/Dockerfile | 3 +- frontend/dockerfile/dockerfile2llb/convert.go | 74 +++++++++++-------- .../dockerfile2llb/convert_norunmount.go | 16 ++++ .../dockerfile2llb/convert_runmount.go | 70 ++++++++++++++++++ frontend/dockerfile/instructions/bflag.go | 2 +- frontend/dockerfile/instructions/commands.go | 5 -- .../instructions/commands_runmount.go | 46 ++++++++++-- hack/test | 1 + 8 files changed, 172 insertions(+), 45 deletions(-) create mode 100644 frontend/dockerfile/dockerfile2llb/convert_norunmount.go create mode 100644 frontend/dockerfile/dockerfile2llb/convert_runmount.go diff --git a/frontend/dockerfile/cmd/dockerfile-frontend/Dockerfile b/frontend/dockerfile/cmd/dockerfile-frontend/Dockerfile index 7cbdc6ce3b7f..b95dba459bd0 100644 --- a/frontend/dockerfile/cmd/dockerfile-frontend/Dockerfile +++ b/frontend/dockerfile/cmd/dockerfile-frontend/Dockerfile @@ -1,6 +1,7 @@ FROM golang:1.10-alpine AS builder +ARG BUILDTAGS="" COPY . /go/src/github.com/moby/buildkit -RUN CGO_ENABLED=0 go build -o /dockerfile-frontend --ldflags '-extldflags "-static"' github.com/moby/buildkit/frontend/dockerfile/cmd/dockerfile-frontend +RUN CGO_ENABLED=0 go build -o /dockerfile-frontend -tags "$BUILDTAGS" --ldflags '-extldflags "-static"' github.com/moby/buildkit/frontend/dockerfile/cmd/dockerfile-frontend FROM scratch COPY --from=builder /dockerfile-frontend /bin/dockerfile-frontend diff --git a/frontend/dockerfile/dockerfile2llb/convert.go b/frontend/dockerfile/dockerfile2llb/convert.go index ec464ba75484..ab6f1a56c137 100644 --- a/frontend/dockerfile/dockerfile2llb/convert.go +++ b/frontend/dockerfile/dockerfile2llb/convert.go @@ -44,6 +44,8 @@ type ConvertOpt struct { // IgnoreCache contains names of the stages that should not use build cache. // Empty slice means ignore cache for all stages. Nil doesn't disable cache. IgnoreCache []string + // CacheIDNamespace scopes the IDs for different cache mounts + CacheIDNamespace string } func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, *Image, error) { @@ -125,15 +127,17 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, for _, d := range allDispatchStates { d.commands = make([]command, len(d.stage.Commands)) for i, cmd := range d.stage.Commands { - newCmd, created, err := toCommand(cmd, dispatchStatesByName, allDispatchStates) + newCmd, err := toCommand(cmd, dispatchStatesByName, allDispatchStates) if err != nil { return nil, nil, err } d.commands[i] = newCmd - if newCmd.copySource != nil { - d.deps[newCmd.copySource] = struct{}{} - if created { - allDispatchStates = append(allDispatchStates, newCmd.copySource) + for _, src := range newCmd.sources { + if src != nil { + d.deps[src] = struct{}{} + if src.unregistered { + allDispatchStates = append(allDispatchStates, src) + } } } } @@ -237,6 +241,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, sessionID: opt.SessionID, buildContext: llb.NewState(buildContext), proxyEnv: proxyEnv, + cacheIDNamespace: opt.CacheIDNamespace, } if err = dispatchOnBuild(d, d.image.Config.OnBuild, opt); err != nil { @@ -278,9 +283,8 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, return &target.state, &target.image, nil } -func toCommand(ic instructions.Command, dispatchStatesByName map[string]*dispatchState, allDispatchStates []*dispatchState) (command, bool, error) { +func toCommand(ic instructions.Command, dispatchStatesByName map[string]*dispatchState, allDispatchStates []*dispatchState) (command, error) { cmd := command{Command: ic} - created := false if c, ok := ic.(*instructions.CopyCommand); ok { if c.From != "" { var stn *dispatchState @@ -289,21 +293,26 @@ func toCommand(ic instructions.Command, dispatchStatesByName map[string]*dispatc stn, ok = dispatchStatesByName[strings.ToLower(c.From)] if !ok { stn = &dispatchState{ - stage: instructions.Stage{BaseName: c.From}, - deps: make(map[*dispatchState]struct{}), + stage: instructions.Stage{BaseName: c.From}, + deps: make(map[*dispatchState]struct{}), + unregistered: true, } - created = true } } else { if index < 0 || index >= len(allDispatchStates) { - return command{}, false, errors.Errorf("invalid stage index %d", index) + return command{}, errors.Errorf("invalid stage index %d", index) } stn = allDispatchStates[index] } - cmd.copySource = stn + cmd.sources = []*dispatchState{stn} } } - return cmd, created, nil + + if ok := detectRunMount(&cmd, dispatchStatesByName, allDispatchStates); ok { + return cmd, nil + } + + return cmd, nil } type dispatchOpt struct { @@ -315,6 +324,7 @@ type dispatchOpt struct { sessionID string buildContext llb.State proxyEnv *llb.ProxyEnv + cacheIDNamespace string } func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error { @@ -334,7 +344,7 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error { case *instructions.EnvCommand: err = dispatchEnv(d, c, true) case *instructions.RunCommand: - err = dispatchRun(d, c, opt.proxyEnv) + err = dispatchRun(d, c, opt.proxyEnv, cmd.sources, opt) case *instructions.WorkdirCommand: err = dispatchWorkdir(d, c, true) case *instructions.AddCommand: @@ -368,11 +378,11 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error { err = dispatchArg(d, c, opt.metaArgs, opt.buildArgValues) case *instructions.CopyCommand: l := opt.buildContext - if cmd.copySource != nil { - l = cmd.copySource.state + if len(cmd.sources) != 0 { + l = cmd.sources[0].state } err = dispatchCopy(d, c.SourcesAndDest, l, false, c, c.Chown) - if err == nil && cmd.copySource == nil { + if err == nil && len(cmd.sources) == 0 { for _, src := range c.Sources() { d.ctxPaths[path.Join("/", filepath.ToSlash(src))] = struct{}{} } @@ -383,21 +393,22 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error { } type dispatchState struct { - state llb.State - image Image - stage instructions.Stage - base *dispatchState - deps map[*dispatchState]struct{} - buildArgs []instructions.ArgCommand - commands []command - ctxPaths map[string]struct{} - ignoreCache bool - cmdSet bool + state llb.State + image Image + stage instructions.Stage + base *dispatchState + deps map[*dispatchState]struct{} + buildArgs []instructions.ArgCommand + commands []command + ctxPaths map[string]struct{} + ignoreCache bool + cmdSet bool + unregistered bool } type command struct { instructions.Command - copySource *dispatchState + sources []*dispatchState } func dispatchOnBuild(d *dispatchState, triggers []string, opt dispatchOpt) error { @@ -413,7 +424,7 @@ func dispatchOnBuild(d *dispatchState, triggers []string, opt dispatchOpt) error if err != nil { return err } - cmd, _, err := toCommand(ic, opt.dispatchStatesByName, opt.allDispatchStates) + cmd, err := toCommand(ic, opt.dispatchStatesByName, opt.allDispatchStates) if err != nil { return err } @@ -437,7 +448,7 @@ func dispatchEnv(d *dispatchState, c *instructions.EnvCommand, commit bool) erro return nil } -func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyEnv) error { +func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyEnv, sources []*dispatchState, dopt dispatchOpt) error { var args []string = c.CmdLine if c.PrependShell { args = withShell(d.image, args) @@ -455,6 +466,9 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE if proxy != nil { opt = append(opt, llb.WithProxy(*proxy)) } + + opt = append(opt, dispatchRunMounts(d, c, sources, dopt)...) + d.state = d.state.Run(opt...).Root() return commitToHistory(&d.image, "RUN "+runCommandString(args, d.buildArgs), true, &d.state) } diff --git a/frontend/dockerfile/dockerfile2llb/convert_norunmount.go b/frontend/dockerfile/dockerfile2llb/convert_norunmount.go new file mode 100644 index 000000000000..b358ee68f330 --- /dev/null +++ b/frontend/dockerfile/dockerfile2llb/convert_norunmount.go @@ -0,0 +1,16 @@ +// +build !dfrunmount,!dfextall + +package dockerfile2llb + +import ( + "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/frontend/dockerfile/instructions" +) + +func detectRunMount(cmd *command, dispatchStatesByName map[string]*dispatchState, allDispatchStates []*dispatchState) bool { + return false +} + +func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) []llb.RunOption { + return nil +} diff --git a/frontend/dockerfile/dockerfile2llb/convert_runmount.go b/frontend/dockerfile/dockerfile2llb/convert_runmount.go new file mode 100644 index 000000000000..487cea7c21d6 --- /dev/null +++ b/frontend/dockerfile/dockerfile2llb/convert_runmount.go @@ -0,0 +1,70 @@ +// +build dfrunmount dfextall + +package dockerfile2llb + +import ( + "path" + "path/filepath" + "strings" + + "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/frontend/dockerfile/instructions" +) + +func detectRunMount(cmd *command, dispatchStatesByName map[string]*dispatchState, allDispatchStates []*dispatchState) bool { + if c, ok := cmd.Command.(*instructions.RunCommand); ok { + mounts := instructions.GetMounts(c) + sources := make([]*dispatchState, len(mounts)) + for i, mount := range mounts { + if mount.From == "" && mount.Type == "cache" { + mount.From = emptyImageName + } + from := mount.From + if from == "" { + continue + } + stn, ok := dispatchStatesByName[strings.ToLower(from)] + if !ok { + stn = &dispatchState{ + stage: instructions.Stage{BaseName: from}, + deps: make(map[*dispatchState]struct{}), + unregistered: true, + } + } + sources[i] = stn + } + cmd.sources = sources + return true + } + + return false +} + +func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) []llb.RunOption { + var out []llb.RunOption + mounts := instructions.GetMounts(c) + + for i, mount := range mounts { + if mount.From == "" && mount.Type == "cache" { + mount.From = emptyImageName + } + st := opt.buildContext + if mount.From != "" { + st = sources[i].state + } + var mountOpts []llb.MountOption + if mount.ReadOnly { + mountOpts = append(mountOpts, llb.Readonly) + } + if mount.Type == "cache" { + mountOpts = append(mountOpts, llb.AsPersistentCacheDir(opt.cacheIDNamespace+"/"+mount.CacheID)) + } + if src := path.Join("/", mount.Source); src != "/" { + mountOpts = append(mountOpts, llb.SourcePath(src)) + } + out = append(out, llb.AddMount(path.Join("/", mount.Target), st, mountOpts...)) + + d.ctxPaths[path.Join("/", filepath.ToSlash(mount.Source))] = struct{}{} + } + return out +} diff --git a/frontend/dockerfile/instructions/bflag.go b/frontend/dockerfile/instructions/bflag.go index 48b2b99779e7..e299d52323a8 100644 --- a/frontend/dockerfile/instructions/bflag.go +++ b/frontend/dockerfile/instructions/bflag.go @@ -156,7 +156,7 @@ func (bf *BFlags) Parse() error { return fmt.Errorf("Unknown flag: %s", arg) } - if _, ok = bf.used[arg]; ok || flag.flagType != stringsType { + if _, ok = bf.used[arg]; ok && flag.flagType != stringsType { return fmt.Errorf("Duplicate flag specified: %s", arg) } diff --git a/frontend/dockerfile/instructions/commands.go b/frontend/dockerfile/instructions/commands.go index 66330a95cf27..3e4617ac918d 100644 --- a/frontend/dockerfile/instructions/commands.go +++ b/frontend/dockerfile/instructions/commands.go @@ -418,11 +418,6 @@ func HasStage(s []Stage, name string) (int, bool) { return -1, false } -type cmdWithExternalData interface { - getExternalValue(k interface{}) interface{} - setExternalValue(k, v interface{}) -} - type withExternalData struct { m map[interface{}]interface{} } diff --git a/frontend/dockerfile/instructions/commands_runmount.go b/frontend/dockerfile/instructions/commands_runmount.go index 6528d559a966..8cea2f69c7e5 100644 --- a/frontend/dockerfile/instructions/commands_runmount.go +++ b/frontend/dockerfile/instructions/commands_runmount.go @@ -1,4 +1,4 @@ -// +build dfrunmount dfall +// +build dfrunmount dfextall package instructions @@ -21,17 +21,16 @@ func init() { func runMountPreHook(cmd *RunCommand, req parseRequest) error { st := &mountState{} - st.flag = req.flags.AddString("mount", "") + st.flag = req.flags.AddStrings("mount") cmd.setExternalValue(mountsKey, st) return nil } func runMountPostHook(cmd *RunCommand, req parseRequest) error { - v := cmd.getExternalValue(mountsKey) - if v != nil { + st := getMountState(cmd) + if st == nil { return errors.Errorf("no mount state") } - st := v.(*mountState) var mounts []*Mount for _, str := range st.flag.StringValues { m, err := parseMount(str) @@ -40,9 +39,22 @@ func runMountPostHook(cmd *RunCommand, req parseRequest) error { } mounts = append(mounts, m) } + st.mounts = mounts return nil } +func getMountState(cmd *RunCommand) *mountState { + v := cmd.getExternalValue(mountsKey) + if v == nil { + return nil + } + return v.(*mountState) +} + +func GetMounts(cmd *RunCommand) []*Mount { + return getMountState(cmd).mounts +} + type mountState struct { flag *Flag mounts []*Mount @@ -64,7 +76,9 @@ func parseMount(value string) (*Mount, error) { return nil, errors.Wrap(err, "failed to parse csv mounts") } - m := &Mount{ReadOnly: true} + m := &Mount{Type: "bind"} + + roAuto := true for _, field := range fields { parts := strings.SplitN(field, "=", 2) @@ -74,9 +88,11 @@ func parseMount(value string) (*Mount, error) { switch key { case "readonly", "ro": m.ReadOnly = true + roAuto = false continue case "readwrite", "rw": m.ReadOnly = false + roAuto = false continue } } @@ -88,7 +104,11 @@ func parseMount(value string) (*Mount, error) { value := parts[1] switch key { case "type": - if value != "" && strings.EqualFold(value, "cache") { + allowedTypes := map[string]struct{}{ + "cache": {}, + "bind": {}, + } + if _, ok := allowedTypes[strings.ToLower(value)]; !ok { return nil, errors.Errorf("invalid mount type %q", value) } m.Type = strings.ToLower(value) @@ -103,12 +123,14 @@ func parseMount(value string) (*Mount, error) { if err != nil { return nil, errors.Errorf("invalid value for %s: %s", key, value) } + roAuto = false case "readwrite", "rw": rw, err := strconv.ParseBool(value) if err != nil { return nil, errors.Errorf("invalid value for %s: %s", key, value) } m.ReadOnly = !rw + roAuto = false case "id": m.CacheID = value default: @@ -116,5 +138,13 @@ func parseMount(value string) (*Mount, error) { } } - return nil, errors.Errorf("not-implemented") + if roAuto { + if m.Type == "cache" { + m.ReadOnly = false + } else { + m.ReadOnly = true + } + } + + return m, nil } diff --git a/hack/test b/hack/test index 4f32f7160ee6..a777e63005c5 100755 --- a/hack/test +++ b/hack/test @@ -12,5 +12,6 @@ docker run --rm -v /tmp --privileged $iid go test ${TESTFLAGS:--v} ${TESTPKGS:-. docker run --rm $iid go build ./frontend/gateway/client docker run --rm $iid go build ./frontend/dockerfile/cmd/dockerfile-frontend +docker run --rm $iid go build -tags dfrunmount ./frontend/dockerfile/cmd/dockerfile-frontend rm -f $iidfile From 479419abf234c87f00f042db0403d41522727b58 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Thu, 7 Jun 2018 13:54:04 -0700 Subject: [PATCH 3/3] =?UTF-8?q?llbsolver:=20don=E2=80=99t=20error=20on=20r?= =?UTF-8?q?eadonly=20scratch=20mount?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tonis Tiigi --- client/client_test.go | 3 +-- solver/llbsolver/ops/exec.go | 9 +++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/client/client_test.go b/client/client_test.go index 66a21283ae8b..b59ae1ba7676 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -893,8 +893,7 @@ func testMountWithNoSource(t *testing.T, sb integration.Sandbox) { require.NoError(t, err) _, err = c.Solve(context.TODO(), def, SolveOpt{}, nil) - require.Error(t, err) - require.Contains(t, err.Error(), "has no input") + require.NoError(t, err) checkAllReleasable(t, c, sb, true) } diff --git a/solver/llbsolver/ops/exec.go b/solver/llbsolver/ops/exec.go index 6ef6704f3de5..3cae012fd419 100644 --- a/solver/llbsolver/ops/exec.go +++ b/solver/llbsolver/ops/exec.go @@ -262,7 +262,16 @@ func (e *execOp) Exec(ctx context.Context, inputs []solver.Result) ([]solver.Res outputs = append(outputs, active) mountable = active } + } else if ref == nil { + // this case is empty readonly scratch without output that is not really useful for anything but don't error + active, err := makeMutable(ref) + if err != nil { + return nil, err + } + defer active.Release(context.TODO()) + mountable = active } + case pb.MountType_CACHE: if m.CacheOpt == nil { return nil, errors.Errorf("missing cache mount options")