Files
buildkit/client/client_exec_test.go
Tonis Tiigi 38f8536ee3 client: split client integration tests by area
Move client integration test declarations out of the large harness files into
focused files for cache, exporters, sources, mounts, gateway behavior, and
related client features.

Keep registration lists grouped by source file, move non-gateway tests out of
the gateway harness, and remove the now-empty build_test.go. This is intended
as a mechanical split with no test logic changes.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2026-07-06 22:19:20 -07:00

744 lines
22 KiB
Go

package client
import (
"context"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/entitlements"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/moby/buildkit/util/testutil/workers"
digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
)
func testCgroupParent(t *testing.T, sb integration.Sandbox) {
if sb.Rootless() {
t.SkipNow()
}
if _, err := os.Lstat("/sys/fs/cgroup/cgroup.subtree_control"); os.IsNotExist(err) {
t.Skipf("test requires cgroup v2")
}
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()
img := llb.Image("alpine:latest")
st := llb.Scratch()
run := func(cmd string, ro ...llb.RunOption) {
st = img.Run(append(ro, llb.Shlex(cmd), llb.Dir("/wd"))...).AddMount("/wd", st)
}
cgroupName := "test." + identity.NewID()
err = os.MkdirAll(filepath.Join("/sys/fs/cgroup", cgroupName), 0755)
require.NoError(t, err)
defer func() {
err := os.RemoveAll(filepath.Join("/sys/fs/cgroup", cgroupName))
require.NoError(t, err)
}()
err = os.WriteFile(filepath.Join("/sys/fs/cgroup", cgroupName, "pids.max"), []byte("10"), 0644)
require.NoError(t, err)
run(`sh -c "(for i in $(seq 1 10); do sleep 1 & done 2>first.error); cat /proc/self/cgroup >> first"`, llb.WithCgroupParent(cgroupName))
run(`sh -c "(for i in $(seq 1 10); do sleep 1 & done 2>second.error); cat /proc/self/cgroup >> second"`)
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
destDir := t.TempDir()
_, err = c.Solve(sb.Context(), def, SolveOpt{
Exports: []ExportEntry{
{
Type: ExporterLocal,
OutputDir: destDir,
},
},
}, nil)
require.NoError(t, err)
// neither process leaks parent cgroup name inside container
dt, err := os.ReadFile(filepath.Join(destDir, "first"))
require.NoError(t, err)
require.NotContains(t, strings.TrimSpace(string(dt)), cgroupName)
dt2, err := os.ReadFile(filepath.Join(destDir, "second"))
require.NoError(t, err)
require.NotContains(t, strings.TrimSpace(string(dt2)), cgroupName)
dt, err = os.ReadFile(filepath.Join(destDir, "first.error"))
require.NoError(t, err)
require.Contains(t, strings.TrimSpace(string(dt)), "Resource temporarily unavailable")
dt, err = os.ReadFile(filepath.Join(destDir, "second.error"))
require.NoError(t, err)
require.Equal(t, "", strings.TrimSpace(string(dt)))
}
func testLinuxResources(t *testing.T, sb integration.Sandbox) {
integration.SkipOnPlatform(t, "windows")
if sb.Rootless() {
t.SkipNow()
}
if _, err := os.Lstat("/sys/fs/cgroup/cgroup.subtree_control"); os.IsNotExist(err) {
t.Skipf("test requires cgroup v2")
}
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()
img := llb.Image("alpine:latest")
st := llb.Scratch()
run := func(cmd string, ro ...llb.RunOption) {
st = img.Run(append(ro, llb.Shlex(cmd), llb.Dir("/wd"))...).AddMount("/wd", st)
}
// Test memory limit: set 64MiB and verify via cgroup
run(`sh -c "cat /sys/fs/cgroup/memory.max > mem_limited"`, llb.MemoryLimit(64*1024*1024))
run(`sh -c "cat /sys/fs/cgroup/memory.max > mem_default"`)
// Test CPU quota: set quota=50000 period=100000 (50% CPU) and verify
run(`sh -c "cat /sys/fs/cgroup/cpu.max > cpu_limited"`, llb.CPUQuota(50000), llb.CPUPeriod(100000))
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
destDir := t.TempDir()
_, err = c.Solve(sb.Context(), def, SolveOpt{
Exports: []ExportEntry{
{
Type: ExporterLocal,
OutputDir: destDir,
},
},
}, nil)
require.NoError(t, err)
dt, err := os.ReadFile(filepath.Join(destDir, "mem_limited"))
require.NoError(t, err)
require.Equal(t, "67108864", strings.TrimSpace(string(dt)))
dt2, err := os.ReadFile(filepath.Join(destDir, "mem_default"))
require.NoError(t, err)
require.Equal(t, "max", strings.TrimSpace(string(dt2)))
dt3, err := os.ReadFile(filepath.Join(destDir, "cpu_limited"))
require.NoError(t, err)
require.Equal(t, "50000 100000", strings.TrimSpace(string(dt3)))
}
func testPassthroughOp(t *testing.T, sb integration.Sandbox) {
requiresLinux(t)
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()
ctx := sb.Context()
busybox := llb.Image("busybox:latest")
base := llb.Scratch().File(llb.Mkfile("/base", 0644, []byte("base")))
t.Run("requires returns receiver and builds non-output dependency", func(t *testing.T) {
cacheID := "passthrough-requires-" + identity.NewID()
nonOutputDep := busybox.Run(llb.Shlex(`sh -e -c "echo requires-dep-built > /cache/marker; echo should-not-export > /not-output"`))
nonOutputDep.AddMount("/cache", llb.Scratch(), llb.AsPersistentCacheDir(cacheID, llb.CacheMountShared))
outDir := solveStateToLocalDir(ctx, t, c, base.Requires("test.passthrough.requires", nonOutputDep.Root()))
requireLocalFile(t, outDir, "base", "base")
require.NoFileExists(t, filepath.Join(outDir, "not-output"))
markerDir := solveStateToLocalDir(ctx, t, c, cacheMountFile(busybox, cacheID, "marker"))
requireLocalFile(t, markerDir, "marker", "requires-dep-built\n")
})
t.Run("direct passthrough returns selected outputs and builds non-output input", func(t *testing.T) {
cacheID := "passthrough-direct-" + identity.NewID()
nonOutputInput := busybox.Run(llb.Shlex(`sh -e -c "echo direct-input-built > /cache/marker; echo should-not-export > /not-output"`))
nonOutputInput.AddMount("/cache", llb.Scratch(), llb.AsPersistentCacheDir(cacheID, llb.CacheMountShared))
pass := llb.NewPassthroughOp("test.passthrough.direct", []llb.PassthroughInput{
{State: llb.Scratch().File(llb.Mkfile("/a", 0644, []byte("a"))), Output: true},
{State: nonOutputInput.Root()},
{State: llb.Scratch().File(llb.Mkfile("/c", 0644, []byte("c"))), Output: true},
})
outA := llb.NewState(pass.Output())
outC := llb.NewState(pass.OutputAt(1))
out := llb.Scratch().
File(llb.Copy(outA, "/a", "/a")).
File(llb.Copy(outC, "/c", "/c"))
outDir := solveStateToLocalDir(ctx, t, c, out)
requireLocalFile(t, outDir, "a", "a")
requireLocalFile(t, outDir, "c", "c")
require.NoFileExists(t, filepath.Join(outDir, "not-output"))
markerDir := solveStateToLocalDir(ctx, t, c, cacheMountFile(busybox, cacheID, "marker"))
requireLocalFile(t, markerDir, "marker", "direct-input-built\n")
})
t.Run("failing non-output dependency fails solve", func(t *testing.T) {
fail := busybox.Run(llb.Shlex(`sh -e -c "exit 42"`)).Root()
def, err := base.Requires("test.passthrough.fail", fail).Marshal(ctx)
require.NoError(t, err)
_, err = c.Solve(ctx, def, SolveOpt{
Exports: []ExportEntry{{
Type: ExporterLocal,
OutputDir: t.TempDir(),
}},
}, nil)
require.Error(t, err)
})
}
// testRelativeMountpoint is a test that relative paths for mountpoints don't
// fail when runc is upgraded to at least rc95, which introduces an error when
// mountpoints are not absolute. Relative paths should be transformed to
// absolute points based on the llb.State's current working directory.
func testRelativeMountpoint(t *testing.T, sb integration.Sandbox) {
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()
id := identity.NewID()
imgName := integration.UnixOrWindows("busybox:latest", "nanoserver:latest")
scratch := integration.UnixOrWindows(llb.Scratch(), llb.Image(imgName))
cmdStr := integration.UnixOrWindows(
"sh -c 'echo -n %s > /root/relpath/data'",
`cmd /C echo %s > \\root\\relpath\\data`,
)
st := llb.Image(imgName).Dir("/root").Run(
llb.Shlexf(cmdStr, id),
).AddMount("relpath", scratch)
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
destDir := t.TempDir()
_, err = c.Solve(sb.Context(), def, SolveOpt{
Exports: []ExportEntry{
{
Type: ExporterLocal,
OutputDir: destDir,
},
},
}, nil)
require.NoError(t, err)
dt, err := os.ReadFile(filepath.Join(destDir, "data"))
require.NoError(t, err)
newLine := integration.UnixOrWindows("", " \r\n")
require.Equal(t, dt, []byte(id+newLine))
}
func testRelativeWorkDir(t *testing.T, sb integration.Sandbox) {
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()
imgName := integration.UnixOrWindows(
"docker.io/library/busybox:latest",
"mcr.microsoft.com/windows/nanoserver:ltsc2022",
)
cmdStr := integration.UnixOrWindows(
`sh -c "pwd > /out/pwd"`,
`cmd /C "cd > /out/pwd"`,
)
pwd := llb.Image(imgName).
Dir("test1").
Dir("test2").
Run(llb.Shlex(cmdStr)).
AddMount("/out", llb.Scratch())
def, err := pwd.Marshal(sb.Context())
require.NoError(t, err)
destDir := t.TempDir()
_, err = c.Solve(sb.Context(), def, SolveOpt{
Exports: []ExportEntry{
{
Type: ExporterLocal,
OutputDir: destDir,
},
},
}, nil)
require.NoError(t, err)
dt, err := os.ReadFile(filepath.Join(destDir, "pwd"))
require.NoError(t, err)
pathStr := integration.UnixOrWindows(
"/test1/test2\n",
"C:\\test1\\test2\r\n",
)
require.Equal(t, []byte(pathStr), dt)
}
func testRunValidExitCodes(t *testing.T, sb integration.Sandbox) {
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()
// no exit codes specified, equivalent to [0]
imgName := integration.UnixOrWindows("busybox:latest", "nanoserver:latest")
out := llb.Image(imgName)
shellPrefix := integration.UnixOrWindows("sh -c", "cmd /C")
out = out.Run(llb.Shlexf(`%s "exit 0"`, shellPrefix)).Root()
out = out.Run(llb.Shlexf(`%s "exit 1"`, shellPrefix)).Root()
def, err := out.Marshal(sb.Context())
require.NoError(t, err)
_, err = c.Solve(sb.Context(), def, SolveOpt{}, nil)
require.Error(t, err)
require.ErrorContains(t, err, "exit code: 1")
// empty exit codes, equivalent to [0]
out = llb.Image(imgName)
out = out.Run(llb.Shlexf(`%s "exit 0"`, shellPrefix), llb.ValidExitCodes()).Root()
def, err = out.Marshal(sb.Context())
require.NoError(t, err)
_, err = c.Solve(sb.Context(), def, SolveOpt{}, nil)
require.NoError(t, err)
// if we expect non-zero, those non-zero codes should succeed
out = llb.Image(imgName)
out = out.Run(llb.Shlexf(`%s "exit 1"`, shellPrefix), llb.ValidExitCodes(1)).Root()
out = out.Run(llb.Shlexf(`%s "exit 2"`, shellPrefix), llb.ValidExitCodes(2, 3)).Root()
out = out.Run(llb.Shlexf(`%s "exit 3"`, shellPrefix), llb.ValidExitCodes(2, 3)).Root()
def, err = out.Marshal(sb.Context())
require.NoError(t, err)
_, err = c.Solve(sb.Context(), def, SolveOpt{}, nil)
require.NoError(t, err)
// if we expect non-zero, returning zero should fail
out = llb.Image(imgName)
out = out.Run(llb.Shlexf(`%s "exit 0"`, shellPrefix), llb.ValidExitCodes(1)).Root()
def, err = out.Marshal(sb.Context())
require.NoError(t, err)
_, err = c.Solve(sb.Context(), def, SolveOpt{}, nil)
require.Error(t, err)
require.ErrorContains(t, err, "exit code: 0")
}
func testSecurityMode(t *testing.T, sb integration.Sandbox) {
integration.SkipOnPlatform(t, "windows")
workers.CheckFeatureCompat(t, sb, workers.FeatureSecurityMode)
command := `sh -c 'cat /proc/self/status | grep CapEff | cut -f 2 > /out'`
mode := llb.SecurityModeSandbox
var allowedEntitlements []string
var assertCaps func(caps uint64)
secMode := sb.Value("secmode")
if secMode == securitySandbox {
assertCaps = func(caps uint64) {
/*
$ capsh --decode=00000000a80425fb
0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,
cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
*/
require.Equal(t, uint64(0xa80425fb), caps)
}
allowedEntitlements = []string{}
} else {
assertCaps = func(caps uint64) {
/*
$ capsh --decode=0000003fffffffff
0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,
cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,
cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,
cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,
cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
*/
// require that _at least_ minimum capabilities are granted
require.Equal(t, uint64(0x3fffffffff), caps&0x3fffffffff)
}
mode = llb.SecurityModeInsecure
allowedEntitlements = []string{entitlements.EntitlementSecurityInsecure.String()}
}
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()
st := llb.Image("busybox:latest").
Run(llb.Shlex(command),
llb.Security(mode))
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
destDir := t.TempDir()
_, err = c.Solve(sb.Context(), def, SolveOpt{
Exports: []ExportEntry{
{
Type: ExporterLocal,
OutputDir: destDir,
},
},
AllowedEntitlements: allowedEntitlements,
}, nil)
require.NoError(t, err)
contents, err := os.ReadFile(filepath.Join(destDir, "out"))
require.NoError(t, err)
caps, err := strconv.ParseUint(strings.TrimSpace(string(contents)), 16, 64)
require.NoError(t, err)
t.Logf("Caps: %x", caps)
assertCaps(caps)
}
func testSecurityModeErrors(t *testing.T, sb integration.Sandbox) {
integration.SkipOnPlatform(t, "windows")
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()
secMode := sb.Value("secmode")
if secMode == securitySandbox {
st := llb.Image("busybox:latest").
Run(llb.Shlex(`sh -c 'echo sandbox'`))
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
_, err = c.Solve(sb.Context(), def, SolveOpt{
AllowedEntitlements: []string{entitlements.EntitlementSecurityInsecure.String()},
}, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "security.insecure is not allowed")
}
if secMode == securityInsecure {
st := llb.Image("busybox:latest").
Run(llb.Shlex(`sh -c 'echo insecure'`), llb.Security(llb.SecurityModeInsecure))
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
_, err = c.Solve(sb.Context(), def, SolveOpt{}, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "security.insecure is not allowed")
}
st := llb.Image("busybox:latest").
Run(llb.Shlex("echo invalid"))
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
var foundExec bool
digestMap := map[digest.Digest]digest.Digest{}
for i, dt := range def.Def {
oldDigest := digest.FromBytes(dt)
var op pb.Op
require.NoError(t, op.UnmarshalVT(dt))
changed := false
for j, input := range op.Inputs {
if newDigest, ok := digestMap[digest.Digest(input.Digest)]; ok {
op.Inputs[j].Digest = string(newDigest)
changed = true
}
}
if execOp := op.GetExec(); execOp != nil {
execOp.Security = pb.SecurityMode(2)
changed = true
foundExec = true
}
if changed {
newDt, err := op.MarshalVT()
require.NoError(t, err)
def.Def[i] = newDt
newDigest := digest.FromBytes(newDt)
digestMap[oldDigest] = newDigest
if meta, ok := def.Metadata[oldDigest]; ok {
delete(def.Metadata, oldDigest)
def.Metadata[newDigest] = meta
}
}
}
require.True(t, foundExec)
_, err = c.Solve(sb.Context(), def, SolveOpt{}, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "invalid security mode")
}
func testSecurityModeSysfs(t *testing.T, sb integration.Sandbox) {
integration.SkipOnPlatform(t, "windows")
workers.CheckFeatureCompat(t, sb, workers.FeatureSecurityMode)
if sb.Rootless() {
t.SkipNow()
}
mode := llb.SecurityModeSandbox
var allowedEntitlements []string
secMode := sb.Value("secmode")
if secMode == securitySandbox {
allowedEntitlements = []string{}
} else {
mode = llb.SecurityModeInsecure
allowedEntitlements = []string{entitlements.EntitlementSecurityInsecure.String()}
}
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()
cg := "/sys/fs/cgroup/cpuset/securitytest" // cgroup v1
if _, err := os.Stat("/sys/fs/cgroup/cpuset"); errors.Is(err, os.ErrNotExist) {
cg = "/sys/fs/cgroup/securitytest" // cgroup v2
}
command := "mkdir " + cg
st := llb.Image("busybox:latest").
Run(llb.Shlex(command),
llb.Security(mode))
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
_, err = c.Solve(sb.Context(), def, SolveOpt{
AllowedEntitlements: allowedEntitlements,
}, nil)
if secMode == securitySandbox {
require.Error(t, err)
require.Contains(t, err.Error(), "did not complete successfully")
require.Contains(t, err.Error(), "mkdir "+cg)
} else {
require.NoError(t, err)
}
}
func testShmSize(t *testing.T, sb integration.Sandbox) {
integration.SkipOnPlatform(t, "windows")
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()
st := llb.Image("busybox:latest").Run(
llb.AddMount("/dev/shm", llb.Scratch(), llb.Tmpfs(llb.TmpfsSize(128*1024*1024))),
llb.Shlex(`sh -c 'mount | grep /dev/shm > /out/out'`),
)
out := st.AddMount("/out", llb.Scratch())
def, err := out.Marshal(sb.Context())
require.NoError(t, err)
destDir := t.TempDir()
_, err = c.Solve(sb.Context(), def, SolveOpt{
Exports: []ExportEntry{
{
Type: ExporterLocal,
OutputDir: destDir,
},
},
}, nil)
require.NoError(t, err)
dt, err := os.ReadFile(filepath.Join(destDir, "out"))
require.NoError(t, err)
require.Contains(t, string(dt), `size=131072k`)
}
// moby/buildkit#614
func testStdinClosed(t *testing.T, sb integration.Sandbox) {
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()
imgName := integration.UnixOrWindows("busybox:latest", "nanoserver:latest")
cmdStr := integration.UnixOrWindows("cat", "cmd /C more")
st := llb.Image(imgName).Run(llb.Shlex(cmdStr))
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
_, err = c.Solve(sb.Context(), def, SolveOpt{}, nil)
require.NoError(t, err)
}
func testUlimit(t *testing.T, sb integration.Sandbox) {
integration.SkipOnPlatform(t, "windows")
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()
busybox := llb.Image("busybox:latest")
st := llb.Scratch()
run := func(cmd string, ro ...llb.RunOption) {
st = busybox.Run(append(ro, llb.Shlex(cmd), llb.Dir("/wd"))...).AddMount("/wd", st)
}
run(`sh -c "ulimit -n > first"`, llb.AddUlimit(llb.UlimitNofile, 1062, 1062))
run(`sh -c "ulimit -n > second"`)
def, err := st.Marshal(sb.Context())
require.NoError(t, err)
destDir := t.TempDir()
_, err = c.Solve(sb.Context(), def, SolveOpt{
Exports: []ExportEntry{
{
Type: ExporterLocal,
OutputDir: destDir,
},
},
}, nil)
require.NoError(t, err)
dt, err := os.ReadFile(filepath.Join(destDir, "first"))
require.NoError(t, err)
require.Equal(t, `1062`, strings.TrimSpace(string(dt)))
dt2, err := os.ReadFile(filepath.Join(destDir, "second"))
require.NoError(t, err)
require.NotEqual(t, `1062`, strings.TrimSpace(string(dt2)))
}
func testUser(t *testing.T, sb integration.Sandbox) {
requiresLinux(t)
c, err := New(sb.Context(), sb.Address())
require.NoError(t, err)
defer c.Close()
st := llb.Image("busybox:latest").Run(llb.Shlex(`sh -c "mkdir -m 0777 /wd"`))
run := func(user, cmd string) {
if user != "" {
st = st.Run(llb.Shlex(cmd), llb.Dir("/wd"), llb.User(user))
} else {
st = st.Run(llb.Shlex(cmd), llb.Dir("/wd"))
}
}
run("daemon", `sh -c "id -nu > user"`)
run("daemon:daemon", `sh -c "id -ng > group"`)
run("daemon:nobody", `sh -c "id -ng > nobody"`)
run("1:1", `sh -c "id -g > userone"`)
run("root", `sh -c "id -Gn > root_supplementary"`)
run("", `sh -c "id -Gn > default_supplementary"`)
run("", `rm /etc/passwd /etc/group`) // test that default user still works
run("", `sh -c "id -u > default_uid"`)
st = st.Run(llb.Shlex("cp -a /wd/. /out/"))
out := st.AddMount("/out", llb.Scratch())
def, err := out.Marshal(sb.Context())
require.NoError(t, err)
destDir := t.TempDir()
_, err = c.Solve(sb.Context(), def, SolveOpt{
Exports: []ExportEntry{
{
Type: ExporterLocal,
OutputDir: destDir,
},
},
}, nil)
require.NoError(t, err)
dt, err := os.ReadFile(filepath.Join(destDir, "user"))
require.NoError(t, err)
require.Equal(t, "daemon", strings.TrimSpace(string(dt)))
dt, err = os.ReadFile(filepath.Join(destDir, "group"))
require.NoError(t, err)
require.Equal(t, "daemon", strings.TrimSpace(string(dt)))
dt, err = os.ReadFile(filepath.Join(destDir, "nobody"))
require.NoError(t, err)
require.Equal(t, "nobody", strings.TrimSpace(string(dt)))
dt, err = os.ReadFile(filepath.Join(destDir, "userone"))
require.NoError(t, err)
require.Equal(t, "1", strings.TrimSpace(string(dt)))
dt, err = os.ReadFile(filepath.Join(destDir, "root_supplementary"))
require.NoError(t, err)
require.True(t, strings.HasPrefix(string(dt), "root "))
require.Contains(t, string(dt), "wheel")
dt2, err := os.ReadFile(filepath.Join(destDir, "default_supplementary"))
require.NoError(t, err)
require.Equal(t, string(dt), string(dt2))
dt, err = os.ReadFile(filepath.Join(destDir, "default_uid"))
require.NoError(t, err)
require.Equal(t, "0", strings.TrimSpace(string(dt)))
checkAllReleasable(t, c, sb, true)
}
func requireLocalFile(t *testing.T, dir, name, expected string) {
t.Helper()
dt, err := os.ReadFile(filepath.Join(dir, name))
require.NoError(t, err)
require.Equal(t, expected, string(dt))
}
func solveStateToLocalDir(ctx context.Context, t *testing.T, c *Client, st llb.State) string {
t.Helper()
def, err := st.Marshal(ctx)
require.NoError(t, err)
outDir := t.TempDir()
_, err = c.Solve(ctx, def, SolveOpt{
Exports: []ExportEntry{{
Type: ExporterLocal,
OutputDir: outDir,
}},
}, nil)
require.NoError(t, err)
return outDir
}
type secModeSandbox struct{}
func (*secModeSandbox) UpdateConfigFile(in string) (string, func() error) {
return in, nil
}
type secModeInsecure struct{}
func (*secModeInsecure) UpdateConfigFile(in string) (string, func() error) {
return in + "\n\ninsecure-entitlements = [\"security.insecure\"]\n", nil
}
var (
securitySandbox integration.ConfigUpdater = &secModeSandbox{}
securityInsecure integration.ConfigUpdater = &secModeInsecure{}
)