162 lines
4.6 KiB
Go
162 lines
4.6 KiB
Go
package dockerfile2llb
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/moby/buildkit/client/llb"
|
|
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
|
"github.com/moby/buildkit/solver/pb"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func detectRunMount(cmd *command, allDispatchStates *dispatchStates) bool {
|
|
if c, ok := cmd.Command.(*instructions.RunCommand); ok {
|
|
mounts := instructions.GetMounts(c)
|
|
sources := make([]*dispatchState, len(mounts))
|
|
for i, mount := range mounts {
|
|
var from string
|
|
if mount.From == "" {
|
|
// this might not be accurate because the type might not have a real source (tmpfs for instance),
|
|
// but since this is just for creating the sources map it should be ok (we don't want to check the value of
|
|
// mount.Type because it might be a variable)
|
|
from = emptyImageName
|
|
} else {
|
|
from = mount.From
|
|
}
|
|
stn, ok := allDispatchStates.findStateByName(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 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)
|
|
|
|
for i, mount := range mounts {
|
|
if mount.From == "" && mount.Type == instructions.MountTypeCache {
|
|
mount.From = emptyImageName
|
|
}
|
|
st := opt.buildContext
|
|
if mount.From != "" {
|
|
st = sources[i].state
|
|
}
|
|
var mountOpts []llb.MountOption
|
|
if mount.Type == instructions.MountTypeTmpfs {
|
|
st = llb.Scratch()
|
|
mountOpts = append(mountOpts, llb.Tmpfs())
|
|
}
|
|
if mount.Type == instructions.MountTypeSecret {
|
|
secret, err := dispatchSecret(mount)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, secret)
|
|
continue
|
|
}
|
|
if mount.Type == instructions.MountTypeSSH {
|
|
ssh, err := dispatchSSH(mount)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, ssh)
|
|
continue
|
|
}
|
|
if mount.ReadOnly {
|
|
mountOpts = append(mountOpts, llb.Readonly)
|
|
} else if mount.Type == instructions.MountTypeBind && opt.llbCaps.Supports(pb.CapExecMountBindReadWriteNoOuput) == nil {
|
|
mountOpts = append(mountOpts, llb.ForceNoOutput)
|
|
}
|
|
if mount.Type == instructions.MountTypeCache {
|
|
sharing := llb.CacheMountShared
|
|
if mount.CacheSharing == instructions.MountSharingPrivate {
|
|
sharing = llb.CacheMountPrivate
|
|
}
|
|
if mount.CacheSharing == instructions.MountSharingLocked {
|
|
sharing = llb.CacheMountLocked
|
|
}
|
|
if mount.CacheID == "" {
|
|
mount.CacheID = path.Clean(mount.Target)
|
|
}
|
|
mountOpts = append(mountOpts, llb.AsPersistentCacheDir(opt.cacheIDNamespace+"/"+mount.CacheID, sharing))
|
|
}
|
|
target := mount.Target
|
|
if !filepath.IsAbs(filepath.Clean(mount.Target)) {
|
|
dir, err := d.state.GetDir(context.TODO())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
target = filepath.Join("/", dir, mount.Target)
|
|
}
|
|
if target == "/" {
|
|
return nil, errors.Errorf("invalid mount target %q", target)
|
|
}
|
|
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...))
|
|
|
|
if mount.From == "" {
|
|
d.ctxPaths[path.Join("/", filepath.ToSlash(mount.Source))] = struct{}{}
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|