diff --git a/frontend/dockerfile/dockerfile2llb/convert.go b/frontend/dockerfile/dockerfile2llb/convert.go index 756d7e16..7ac992f3 100644 --- a/frontend/dockerfile/dockerfile2llb/convert.go +++ b/frontend/dockerfile/dockerfile2llb/convert.go @@ -467,7 +467,11 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE opt = append(opt, llb.WithProxy(*proxy)) } - opt = append(opt, dispatchRunMounts(d, c, sources, dopt)...) + runMounts, err := dispatchRunMounts(d, c, sources, dopt) + if err != nil { + return err + } + opt = append(opt, runMounts...) 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 index b358ee68..a4544e79 100644 --- a/frontend/dockerfile/dockerfile2llb/convert_norunmount.go +++ b/frontend/dockerfile/dockerfile2llb/convert_norunmount.go @@ -11,6 +11,6 @@ func detectRunMount(cmd *command, dispatchStatesByName map[string]*dispatchState return false } -func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) []llb.RunOption { - return nil +func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) ([]llb.RunOption, error) { + return nil, nil } diff --git a/frontend/dockerfile/dockerfile2llb/convert_runmount.go b/frontend/dockerfile/dockerfile2llb/convert_runmount.go index 905e1558..61408e1f 100644 --- a/frontend/dockerfile/dockerfile2llb/convert_runmount.go +++ b/frontend/dockerfile/dockerfile2llb/convert_runmount.go @@ -9,6 +9,7 @@ import ( "github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/frontend/dockerfile/instructions" + "github.com/pkg/errors" ) func detectRunMount(cmd *command, dispatchStatesByName map[string]*dispatchState, allDispatchStates []*dispatchState) bool { @@ -40,7 +41,7 @@ func detectRunMount(cmd *command, dispatchStatesByName map[string]*dispatchState return false } -func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) []llb.RunOption { +func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) ([]llb.RunOption, error) { var out []llb.RunOption mounts := instructions.GetMounts(c) @@ -61,14 +62,25 @@ func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []* mountOpts = append(mountOpts, llb.Readonly) } if mount.Type == instructions.MountTypeCache { - mountOpts = append(mountOpts, llb.AsPersistentCacheDir(opt.cacheIDNamespace+"/"+mount.CacheID, llb.CacheMountShared)) + sharing := llb.CacheMountShared + if mount.CacheSharing == instructions.MountSharingPrivate { + sharing = llb.CacheMountPrivate + } + if mount.CacheSharing == instructions.MountSharingLocked { + sharing = llb.CacheMountLocked + } + mountOpts = append(mountOpts, llb.AsPersistentCacheDir(opt.cacheIDNamespace+"/"+mount.CacheID, sharing)) + } + target := path.Join("/", mount.Target) + if target == "/" { + return nil, errors.Errorf("invalid mount target %q", mount.Target) } if src := path.Join("/", mount.Source); src != "/" { mountOpts = append(mountOpts, llb.SourcePath(src)) } - out = append(out, llb.AddMount(path.Join("/", mount.Target), st, mountOpts...)) + out = append(out, llb.AddMount(target, st, mountOpts...)) d.ctxPaths[path.Join("/", filepath.ToSlash(mount.Source))] = struct{}{} } - return out + return out, nil } diff --git a/frontend/dockerfile/instructions/commands_runmount.go b/frontend/dockerfile/instructions/commands_runmount.go index 71030fa7..569c60bc 100644 --- a/frontend/dockerfile/instructions/commands_runmount.go +++ b/frontend/dockerfile/instructions/commands_runmount.go @@ -20,6 +20,16 @@ var allowedMountTypes = map[string]struct{}{ MountTypeTmpfs: {}, } +const MountSharingShared = "shared" +const MountSharingPrivate = "private" +const MountSharingLocked = "locked" + +var allowedSharingTypes = map[string]struct{}{ + MountSharingShared: {}, + MountSharingPrivate: {}, + MountSharingLocked: {}, +} + type mountsKeyT string var mountsKey = mountsKeyT("dockerfile/run/mounts") @@ -76,12 +86,13 @@ type mountState struct { } type Mount struct { - Type string - From string - Source string - Target string - ReadOnly bool - CacheID string + Type string + From string + Source string + Target string + ReadOnly bool + CacheID string + CacheSharing string } func parseMount(value string) (*Mount, error) { @@ -120,7 +131,7 @@ func parseMount(value string) (*Mount, error) { switch key { case "type": if !isValidMountType(strings.ToLower(value)) { - return nil, errors.Errorf("invalid mount type %q", value) + return nil, errors.Errorf("unsupported mount type %q", value) } m.Type = strings.ToLower(value) case "from": @@ -144,6 +155,11 @@ func parseMount(value string) (*Mount, error) { roAuto = false case "id": m.CacheID = value + case "sharing": + if _, ok := allowedSharingTypes[strings.ToLower(value)]; !ok { + return nil, errors.Errorf("unsupported sharing value %q", value) + } + m.CacheSharing = strings.ToLower(value) default: return nil, errors.Errorf("unexpected key '%s' in '%s'", key, field) } @@ -157,5 +173,9 @@ func parseMount(value string) (*Mount, error) { } } + if m.CacheSharing != "" && m.Type != MountTypeCache { + return nil, errors.Errorf("invalid cache sharing set for %v mount", m.Type) + } + return m, nil }