dockerfile: allow uid/gid in cache mounts

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
docker-19.03
Tonis Tiigi 2019-05-21 22:11:44 -07:00
parent bb03b93a3d
commit aed5e98d92
4 changed files with 85 additions and 4 deletions

View File

@ -3,8 +3,12 @@
package dockerfile2llb
import (
"fmt"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/frontend/dockerfile/instructions"
@ -40,6 +44,40 @@ func detectRunMount(cmd *command, allDispatchStates *dispatchStates) bool {
return false
}
func setCacheUIDGIDFileOp(m *instructions.Mount, st llb.State) llb.State {
uid := 0
gid := 0
mode := os.FileMode(0755)
if m.UID != nil {
uid = int(*m.UID)
}
if m.GID != nil {
gid = int(*m.GID)
}
if m.Mode != nil {
mode = os.FileMode(*m.Mode)
}
return st.File(llb.Mkdir("/cache", mode, llb.WithUIDGID(uid, gid)), llb.WithCustomName("[internal] settings cache mount permissions"))
}
func setCacheUIDGID(m *instructions.Mount, st llb.State, fileop bool) llb.State {
if fileop {
return setCacheUIDGIDFileOp(m, st)
}
var b strings.Builder
if m.UID != nil {
b.WriteString(fmt.Sprintf("chown %d /mnt/cache;", *m.UID))
}
if m.GID != nil {
b.WriteString(fmt.Sprintf("chown :%d /mnt/cache;", *m.GID))
}
if m.Mode != nil {
b.WriteString(fmt.Sprintf("chmod %s /mnt/cache;", strconv.FormatUint(*m.Mode, 8)))
}
return llb.Image("busybox").Run(llb.Shlex(fmt.Sprintf("sh -c 'mkdir -p /mnt/cache;%s'", b.String())), llb.WithCustomName("[internal] settings cache mount permissions")).AddMount("/mnt", st)
}
func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) ([]llb.RunOption, error) {
var out []llb.RunOption
mounts := instructions.GetMounts(c)
@ -97,7 +135,13 @@ func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*
}
if src := path.Join("/", mount.Source); src != "/" {
mountOpts = append(mountOpts, llb.SourcePath(src))
} else {
if mount.UID != nil || mount.GID != nil || mount.Mode != nil {
st = setCacheUIDGID(mount, st, useFileOp(opt.buildArgValues, opt.llbCaps))
mountOpts = append(mountOpts, llb.SourcePath("/cache"))
}
}
out = append(out, llb.AddMount(target, st, mountOpts...))
d.ctxPaths[path.Join("/", filepath.ToSlash(mount.Source))] = struct{}{}

View File

@ -7,6 +7,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strconv"
"testing"
"github.com/containerd/continuity/fs/fstest"
@ -24,6 +25,8 @@ var mountTests = []integration.Test{
func init() {
allTests = append(allTests, mountTests...)
fileOpTests = append(fileOpTests, testCacheMountUser)
}
func testMountContext(t *testing.T, sb integration.Sandbox) {
@ -159,3 +162,34 @@ COPY --from=second /unique /unique
require.NoError(t, err)
require.Equal(t, dt1, dt2)
}
func testCacheMountUser(t *testing.T, sb integration.Sandbox) {
f := getFrontend(t, sb)
isFileOp := getFileOp(t, sb)
dockerfile := []byte(`
FROM busybox
RUN --mount=type=cache,target=/mycache,uid=1001,gid=1002,mode=0751 [ "$(stat -c "%u %g %f" /mycache)" == "1001 1002 41e9" ]
`)
dir, err := tmpdir(
fstest.CreateFile("Dockerfile", dockerfile, 0600),
)
require.NoError(t, err)
defer os.RemoveAll(dir)
c, err := client.New(context.TODO(), sb.Address())
require.NoError(t, err)
defer c.Close()
_, err = f.Solve(context.TODO(), c, client.SolveOpt{
FrontendAttrs: map[string]string{
"build-arg:BUILDKIT_DISABLE_FILEOP": strconv.FormatBool(!isFileOp),
},
LocalDirs: map[string]string{
builder.DefaultLocalNameDockerfile: dir,
builder.DefaultLocalNameContext: dir,
},
}, nil)
require.NoError(t, err)
}

View File

@ -41,6 +41,9 @@ This mount type allows the build container to cache directories for compilers an
|`sharing` | One of `shared`, `private`, or `locked`. Defaults to `shared`. A `shared` cache mount can be used concurrently by multiple writers. `private` creates a new mount if there are multiple writers. `locked` pauses the second writer until the first one releases the mount.|
|`from` | Build stage to use as a base of the cache mount. Defaults to empty directory.|
|`source` | Subpath in the `from` to mount. Defaults to the root of the `from`.|
|`mode` | File mode for new cache directory in octal. Default 0755.|
|`uid` | User ID for new cache directory. Default 0.|
|`gid` | Group ID for new cache directory. Default 0.|
#### Example: cache Go packages

View File

@ -206,18 +206,18 @@ func parseMount(value string) (*Mount, error) {
}
}
fileInfoAllowed := m.Type == MountTypeSecret || m.Type == MountTypeSSH
fileInfoAllowed := m.Type == MountTypeSecret || m.Type == MountTypeSSH || m.Type == MountTypeCache
if m.Mode != nil && !fileInfoAllowed {
return nil, errors.Errorf("mode not allowed for %q type mounts")
return nil, errors.Errorf("mode not allowed for %q type mounts", m.Type)
}
if m.UID != nil && !fileInfoAllowed {
return nil, errors.Errorf("uid not allowed for %q type mounts")
return nil, errors.Errorf("uid not allowed for %q type mounts", m.Type)
}
if m.GID != nil && !fileInfoAllowed {
return nil, errors.Errorf("gid not allowed for %q type mounts")
return nil, errors.Errorf("gid not allowed for %q type mounts", m.Type)
}
if roAuto {