vendor: update buildkit to master@b49a8873179b

Signed-off-by: Justin Chadwell <me@jedevc.com>
This commit is contained in:
Justin Chadwell
2023-08-04 11:25:09 +01:00
parent 5ed8f1b7d9
commit 4e7709e54c
32 changed files with 753 additions and 657 deletions

View File

@ -1,89 +0,0 @@
package integration
import (
"fmt"
"net"
"net/http"
"os"
"os/exec"
"testing"
"time"
"github.com/pkg/errors"
)
const (
azuriteBin = "azurite-blob"
)
type AzuriteOpts struct {
AccountName string
AccountKey string
}
func NewAzuriteServer(t *testing.T, sb Sandbox, opts AzuriteOpts) (address string, cl func() error, err error) {
t.Helper()
if _, err := exec.LookPath(azuriteBin); err != nil {
return "", nil, errors.Wrapf(err, "failed to lookup %s binary", azuriteBin)
}
deferF := &multiCloser{}
cl = deferF.F()
defer func() {
if err != nil {
deferF.F()()
cl = nil
}
}()
l, err := net.Listen("tcp", "localhost:0")
if err != nil {
return "", nil, err
}
addr := l.Addr().String()
if err = l.Close(); err != nil {
return "", nil, err
}
host, port, err := net.SplitHostPort(addr)
if err != nil {
return "", nil, err
}
address = fmt.Sprintf("http://%s/%s", addr, opts.AccountName)
// start server
cmd := exec.Command(azuriteBin, "--disableProductStyleUrl", "--blobHost", host, "--blobPort", port, "--location", t.TempDir())
cmd.Env = append(os.Environ(), []string{
"AZURITE_ACCOUNTS=" + opts.AccountName + ":" + opts.AccountKey,
}...)
azuriteStop, err := startCmd(cmd, sb.Logs())
if err != nil {
return "", nil, err
}
if err = waitAzurite(address, 15*time.Second); err != nil {
azuriteStop()
return "", nil, errors.Wrapf(err, "azurite did not start up: %s", formatLogs(sb.Logs()))
}
deferF.append(azuriteStop)
return
}
func waitAzurite(address string, d time.Duration) error {
step := 1 * time.Second
i := 0
for {
if resp, err := http.Get(fmt.Sprintf("%s?comp=list", address)); err == nil {
resp.Body.Close()
break
}
i++
if time.Duration(i)*step > d {
return errors.Errorf("failed dialing: %s", address)
}
time.Sleep(step)
}
return nil
}

View File

@ -1,245 +0,0 @@
package integration
import (
"bytes"
"context"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/moby/buildkit/util/bklog"
"github.com/pkg/errors"
)
func InitContainerdWorker() {
Register(&Containerd{
ID: "containerd",
Containerd: "containerd",
})
// defined in Dockerfile
// e.g. `containerd-1.1=/opt/containerd-1.1/bin,containerd-42.0=/opt/containerd-42.0/bin`
if s := os.Getenv("BUILDKIT_INTEGRATION_CONTAINERD_EXTRA"); s != "" {
entries := strings.Split(s, ",")
for _, entry := range entries {
pair := strings.Split(strings.TrimSpace(entry), "=")
if len(pair) != 2 {
panic(errors.Errorf("unexpected BUILDKIT_INTEGRATION_CONTAINERD_EXTRA: %q", s))
}
name, bin := pair[0], pair[1]
Register(&Containerd{
ID: name,
Containerd: filepath.Join(bin, "containerd"),
// override PATH to make sure that the expected version of the shim binary is used
ExtraEnv: []string{fmt.Sprintf("PATH=%s:%s", bin, os.Getenv("PATH"))},
})
}
}
// the rootless uid is defined in Dockerfile
if s := os.Getenv("BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR"); s != "" {
var uid, gid int
if _, err := fmt.Sscanf(s, "%d:%d", &uid, &gid); err != nil {
bklog.L.Fatalf("unexpected BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR: %q", s)
}
if rootlessSupported(uid) {
Register(&Containerd{
ID: "containerd-rootless",
Containerd: "containerd",
UID: uid,
GID: gid,
Snapshotter: "native", // TODO: test with fuse-overlayfs as well, or automatically determine snapshotter
})
}
}
if s := os.Getenv("BUILDKIT_INTEGRATION_SNAPSHOTTER"); s != "" {
Register(&Containerd{
ID: fmt.Sprintf("containerd-snapshotter-%s", s),
Containerd: "containerd",
Snapshotter: s,
})
}
}
type Containerd struct {
ID string
Containerd string
Snapshotter string
UID int
GID int
ExtraEnv []string // e.g. "PATH=/opt/containerd-1.4/bin:/usr/bin:..."
}
func (c *Containerd) Name() string {
return c.ID
}
func (c *Containerd) Rootless() bool {
return c.UID != 0
}
func (c *Containerd) New(ctx context.Context, cfg *BackendConfig) (b Backend, cl func() error, err error) {
if err := lookupBinary(c.Containerd); err != nil {
return nil, nil, err
}
if err := lookupBinary("buildkitd"); err != nil {
return nil, nil, err
}
if err := requireRoot(); err != nil {
return nil, nil, err
}
deferF := &multiCloser{}
cl = deferF.F()
defer func() {
if err != nil {
deferF.F()()
cl = nil
}
}()
rootless := false
if c.UID != 0 {
if c.GID == 0 {
return nil, nil, errors.Errorf("unsupported id pair: uid=%d, gid=%d", c.UID, c.GID)
}
rootless = true
}
tmpdir, err := os.MkdirTemp("", "bktest_containerd")
if err != nil {
return nil, nil, err
}
if rootless {
if err := os.Chown(tmpdir, c.UID, c.GID); err != nil {
return nil, nil, err
}
}
deferF.append(func() error { return os.RemoveAll(tmpdir) })
address := filepath.Join(tmpdir, "containerd.sock")
config := fmt.Sprintf(`root = %q
state = %q
# CRI plugins listens on 10010/tcp for stream server.
# We disable CRI plugin so that multiple instance can run simultaneously.
disabled_plugins = ["cri"]
[grpc]
address = %q
[debug]
level = "debug"
address = %q
`, filepath.Join(tmpdir, "root"), filepath.Join(tmpdir, "state"), address, filepath.Join(tmpdir, "debug.sock"))
var snBuildkitdArgs []string
if c.Snapshotter != "" {
snBuildkitdArgs = append(snBuildkitdArgs,
fmt.Sprintf("--containerd-worker-snapshotter=%s", c.Snapshotter))
if c.Snapshotter == "stargz" {
snPath, snCl, err := runStargzSnapshotter(cfg)
if err != nil {
return nil, nil, err
}
deferF.append(snCl)
config = fmt.Sprintf(`%s
[proxy_plugins]
[proxy_plugins.stargz]
type = "snapshot"
address = %q
`, config, snPath)
}
}
configFile := filepath.Join(tmpdir, "config.toml")
if err := os.WriteFile(configFile, []byte(config), 0644); err != nil {
return nil, nil, err
}
containerdArgs := []string{c.Containerd, "--config", configFile}
rootlessKitState := filepath.Join(tmpdir, "rootlesskit-containerd")
if rootless {
containerdArgs = append(append([]string{"sudo", "-u", fmt.Sprintf("#%d", c.UID), "-i",
fmt.Sprintf("CONTAINERD_ROOTLESS_ROOTLESSKIT_STATE_DIR=%s", rootlessKitState),
// Integration test requires the access to localhost of the host network namespace.
// TODO: remove these configurations
"CONTAINERD_ROOTLESS_ROOTLESSKIT_NET=host",
"CONTAINERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=none",
"CONTAINERD_ROOTLESS_ROOTLESSKIT_FLAGS=--mtu=0",
}, c.ExtraEnv...), "containerd-rootless.sh", "-c", configFile)
}
cmd := exec.Command(containerdArgs[0], containerdArgs[1:]...) //nolint:gosec // test utility
cmd.Env = append(os.Environ(), c.ExtraEnv...)
ctdStop, err := startCmd(cmd, cfg.Logs)
if err != nil {
return nil, nil, err
}
if err := waitUnix(address, 10*time.Second, cmd); err != nil {
ctdStop()
return nil, nil, errors.Wrapf(err, "containerd did not start up: %s", formatLogs(cfg.Logs))
}
deferF.append(ctdStop)
buildkitdArgs := append([]string{"buildkitd",
"--oci-worker=false",
"--containerd-worker-gc=false",
"--containerd-worker=true",
"--containerd-worker-addr", address,
"--containerd-worker-labels=org.mobyproject.buildkit.worker.sandbox=true", // Include use of --containerd-worker-labels to trigger https://github.com/moby/buildkit/pull/603
}, snBuildkitdArgs...)
if runtime.GOOS != "windows" && c.Snapshotter != "native" {
c.ExtraEnv = append(c.ExtraEnv, "BUILDKIT_DEBUG_FORCE_OVERLAY_DIFF=true")
}
if rootless {
pidStr, err := os.ReadFile(filepath.Join(rootlessKitState, "child_pid"))
if err != nil {
return nil, nil, err
}
pid, err := strconv.ParseInt(string(pidStr), 10, 64)
if err != nil {
return nil, nil, err
}
buildkitdArgs = append([]string{"sudo", "-u", fmt.Sprintf("#%d", c.UID), "-i", "--", "exec",
"nsenter", "-U", "--preserve-credentials", "-m", "-t", fmt.Sprintf("%d", pid)},
append(buildkitdArgs, "--containerd-worker-snapshotter=native")...)
}
buildkitdSock, stop, err := runBuildkitd(ctx, cfg, buildkitdArgs, cfg.Logs, c.UID, c.GID, c.ExtraEnv)
if err != nil {
printLogs(cfg.Logs, log.Println)
return nil, nil, err
}
deferF.append(stop)
return backend{
address: buildkitdSock,
containerdAddress: address,
rootless: rootless,
snapshotter: c.Snapshotter,
}, cl, nil
}
func (c *Containerd) Close() error {
return nil
}
func formatLogs(m map[string]*bytes.Buffer) string {
var ss []string
for k, b := range m {
if b != nil {
ss = append(ss, fmt.Sprintf("%q:%q", k, b.String()))
}
}
return strings.Join(ss, ",")
}

View File

@ -1,253 +0,0 @@
package integration
import (
"context"
"encoding/json"
"io"
"net"
"os"
"path/filepath"
"strings"
"time"
"github.com/docker/docker/client"
"github.com/moby/buildkit/cmd/buildkitd/config"
"github.com/moby/buildkit/util/testutil/dockerd"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
)
// InitDockerdWorker registers a dockerd worker with the global registry.
func InitDockerdWorker() {
Register(&Moby{
ID: "dockerd",
IsRootless: false,
Unsupported: []string{
FeatureCacheExport,
FeatureCacheImport,
FeatureCacheBackendAzblob,
FeatureCacheBackendGha,
FeatureCacheBackendLocal,
FeatureCacheBackendRegistry,
FeatureCacheBackendS3,
FeatureDirectPush,
FeatureImageExporter,
FeatureMultiCacheExport,
FeatureMultiPlatform,
FeatureOCIExporter,
FeatureOCILayout,
FeatureProvenance,
FeatureSBOM,
FeatureSecurityMode,
FeatureCNINetwork,
},
})
Register(&Moby{
ID: "dockerd-containerd",
IsRootless: false,
ContainerdSnapshotter: true,
Unsupported: []string{
FeatureSecurityMode,
FeatureCNINetwork,
},
})
}
type Moby struct {
ID string
IsRootless bool
ContainerdSnapshotter bool
Unsupported []string
}
func (c Moby) Name() string {
return c.ID
}
func (c Moby) Rootless() bool {
return c.IsRootless
}
func (c Moby) New(ctx context.Context, cfg *BackendConfig) (b Backend, cl func() error, err error) {
if err := requireRoot(); err != nil {
return nil, nil, err
}
bkcfg, err := config.LoadFile(cfg.ConfigFile)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to load buildkit config file %s", cfg.ConfigFile)
}
dcfg := dockerd.Config{
Features: map[string]bool{
"containerd-snapshotter": c.ContainerdSnapshotter,
},
}
if reg, ok := bkcfg.Registries["docker.io"]; ok && len(reg.Mirrors) > 0 {
for _, m := range reg.Mirrors {
dcfg.Mirrors = append(dcfg.Mirrors, "http://"+m)
}
}
if bkcfg.Entitlements != nil {
for _, e := range bkcfg.Entitlements {
switch e {
case "network.host":
dcfg.Builder.Entitlements.NetworkHost = true
case "security.insecure":
dcfg.Builder.Entitlements.SecurityInsecure = true
}
}
}
dcfgdt, err := json.Marshal(dcfg)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to marshal dockerd config")
}
deferF := &multiCloser{}
cl = deferF.F()
defer func() {
if err != nil {
deferF.F()()
cl = nil
}
}()
var proxyGroup errgroup.Group
deferF.append(proxyGroup.Wait)
workDir, err := os.MkdirTemp("", "integration")
if err != nil {
return nil, nil, err
}
d, err := dockerd.NewDaemon(workDir)
if err != nil {
return nil, nil, errors.Errorf("new daemon error: %q, %s", err, formatLogs(cfg.Logs))
}
dockerdConfigFile := filepath.Join(workDir, "daemon.json")
if err := os.WriteFile(dockerdConfigFile, dcfgdt, 0644); err != nil {
return nil, nil, err
}
dockerdFlags := []string{
"--config-file", dockerdConfigFile,
"--userland-proxy=false",
"--tls=false",
"--debug",
}
if s := os.Getenv("BUILDKIT_INTEGRATION_DOCKERD_FLAGS"); s != "" {
dockerdFlags = append(dockerdFlags, strings.Split(strings.TrimSpace(s), "\n")...)
}
err = d.StartWithError(cfg.Logs, dockerdFlags...)
if err != nil {
return nil, nil, err
}
deferF.append(d.StopWithError)
if err := waitUnix(d.Sock(), 5*time.Second, nil); err != nil {
return nil, nil, errors.Errorf("dockerd did not start up: %q, %s", err, formatLogs(cfg.Logs))
}
dockerAPI, err := client.NewClientWithOpts(client.WithHost(d.Sock()))
if err != nil {
return nil, nil, err
}
deferF.append(dockerAPI.Close)
err = waitForAPI(ctx, dockerAPI, 5*time.Second)
if err != nil {
return nil, nil, errors.Wrapf(err, "dockerd client api timed out: %s", formatLogs(cfg.Logs))
}
// Create a file descriptor to be used as a Unix domain socket.
// Remove it immediately (the name will still be valid for the socket) so that
// we don't leave files all over the users tmp tree.
f, err := os.CreateTemp("", "buildkit-integration")
if err != nil {
return
}
localPath := f.Name()
f.Close()
os.Remove(localPath)
listener, err := net.Listen("unix", localPath)
if err != nil {
return nil, nil, errors.Wrapf(err, "dockerd listener error: %s", formatLogs(cfg.Logs))
}
deferF.append(listener.Close)
proxyGroup.Go(func() error {
for {
tmpConn, err := listener.Accept()
if err != nil {
// Ignore the error from accept which is always a system error.
return nil
}
conn, err := dockerAPI.DialHijack(ctx, "/grpc", "h2c", nil)
if err != nil {
return err
}
proxyGroup.Go(func() error {
_, err := io.Copy(conn, tmpConn)
if err != nil {
return err
}
return tmpConn.Close()
})
proxyGroup.Go(func() error {
_, err := io.Copy(tmpConn, conn)
if err != nil {
return err
}
return conn.Close()
})
}
})
return backend{
address: "unix://" + listener.Addr().String(),
dockerAddress: d.Sock(),
rootless: c.IsRootless,
isDockerd: true,
unsupportedFeatures: c.Unsupported,
}, cl, nil
}
func (c Moby) Close() error {
return nil
}
func waitForAPI(ctx context.Context, apiClient *client.Client, d time.Duration) error {
step := 50 * time.Millisecond
i := 0
for {
if _, err := apiClient.Ping(ctx); err == nil {
break
}
i++
if time.Duration(i)*step > d {
return errors.New("failed to connect to /_ping endpoint")
}
time.Sleep(step)
}
return nil
}
func IsTestDockerd() bool {
return os.Getenv("TEST_DOCKERD") == "1"
}
func IsTestDockerdMoby(sb Sandbox) bool {
b, err := getBackend(sb)
if err != nil {
return false
}
return b.isDockerd && sb.Name() == "dockerd"
}

View File

@ -1,116 +0,0 @@
package integration
import (
"fmt"
"net"
"net/http"
"os"
"os/exec"
"testing"
"time"
"github.com/pkg/errors"
)
const (
minioBin = "minio"
mcBin = "mc"
)
type MinioOpts struct {
Region string
AccessKeyID string
SecretAccessKey string
}
func NewMinioServer(t *testing.T, sb Sandbox, opts MinioOpts) (address string, bucket string, cl func() error, err error) {
t.Helper()
bucket = randomString(10)
if _, err := exec.LookPath(minioBin); err != nil {
return "", "", nil, errors.Wrapf(err, "failed to lookup %s binary", minioBin)
}
if _, err := exec.LookPath(mcBin); err != nil {
return "", "", nil, errors.Wrapf(err, "failed to lookup %s binary", mcBin)
}
deferF := &multiCloser{}
cl = deferF.F()
defer func() {
if err != nil {
deferF.F()()
cl = nil
}
}()
l, err := net.Listen("tcp", "localhost:0")
if err != nil {
return "", "", nil, err
}
addr := l.Addr().String()
if err = l.Close(); err != nil {
return "", "", nil, err
}
address = "http://" + addr
// start server
cmd := exec.Command(minioBin, "server", "--json", "--address", addr, t.TempDir())
cmd.Env = append(os.Environ(), []string{
"MINIO_ROOT_USER=" + opts.AccessKeyID,
"MINIO_ROOT_PASSWORD=" + opts.SecretAccessKey,
}...)
minioStop, err := startCmd(cmd, sb.Logs())
if err != nil {
return "", "", nil, err
}
if err = waitMinio(address, 15*time.Second); err != nil {
minioStop()
return "", "", nil, errors.Wrapf(err, "minio did not start up: %s", formatLogs(sb.Logs()))
}
deferF.append(minioStop)
// create alias config
alias := randomString(10)
cmd = exec.Command(mcBin, "alias", "set", alias, address, opts.AccessKeyID, opts.SecretAccessKey)
if err := runCmd(cmd, sb.Logs()); err != nil {
return "", "", nil, err
}
deferF.append(func() error {
return exec.Command(mcBin, "alias", "rm", alias).Run()
})
// create bucket
cmd = exec.Command(mcBin, "mb", "--region", opts.Region, fmt.Sprintf("%s/%s", alias, bucket)) // #nosec G204
if err := runCmd(cmd, sb.Logs()); err != nil {
return "", "", nil, err
}
// trace
cmd = exec.Command(mcBin, "admin", "trace", "--json", alias)
traceStop, err := startCmd(cmd, sb.Logs())
if err != nil {
return "", "", nil, err
}
deferF.append(traceStop)
return
}
func waitMinio(address string, d time.Duration) error {
step := 1 * time.Second
i := 0
for {
if resp, err := http.Get(fmt.Sprintf("%s/minio/health/live", address)); err == nil {
resp.Body.Close()
break
}
i++
if time.Duration(i)*step > d {
return errors.Errorf("failed dialing: %s", address)
}
time.Sleep(step)
}
return nil
}

View File

@ -1,90 +0,0 @@
package integration
import (
"context"
"fmt"
"log"
"os"
"runtime"
"github.com/moby/buildkit/util/bklog"
"github.com/pkg/errors"
)
func InitOCIWorker() {
Register(&OCI{ID: "oci"})
// the rootless uid is defined in Dockerfile
if s := os.Getenv("BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR"); s != "" {
var uid, gid int
if _, err := fmt.Sscanf(s, "%d:%d", &uid, &gid); err != nil {
bklog.L.Fatalf("unexpected BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR: %q", s)
}
if rootlessSupported(uid) {
Register(&OCI{ID: "oci-rootless", UID: uid, GID: gid})
}
}
if s := os.Getenv("BUILDKIT_INTEGRATION_SNAPSHOTTER"); s != "" {
Register(&OCI{ID: "oci-snapshotter-" + s, Snapshotter: s})
}
}
type OCI struct {
ID string
UID int
GID int
Snapshotter string
}
func (s *OCI) Name() string {
return s.ID
}
func (s *OCI) Rootless() bool {
return s.UID != 0
}
func (s *OCI) New(ctx context.Context, cfg *BackendConfig) (Backend, func() error, error) {
if err := lookupBinary("buildkitd"); err != nil {
return nil, nil, err
}
if err := requireRoot(); err != nil {
return nil, nil, err
}
// Include use of --oci-worker-labels to trigger https://github.com/moby/buildkit/pull/603
buildkitdArgs := []string{"buildkitd", "--oci-worker=true", "--containerd-worker=false", "--oci-worker-gc=false", "--oci-worker-labels=org.mobyproject.buildkit.worker.sandbox=true"}
if s.Snapshotter != "" {
buildkitdArgs = append(buildkitdArgs,
fmt.Sprintf("--oci-worker-snapshotter=%s", s.Snapshotter))
}
if s.UID != 0 {
if s.GID == 0 {
return nil, nil, errors.Errorf("unsupported id pair: uid=%d, gid=%d", s.UID, s.GID)
}
// TODO: make sure the user exists and subuid/subgid are configured.
buildkitdArgs = append([]string{"sudo", "-u", fmt.Sprintf("#%d", s.UID), "-i", "--", "exec", "rootlesskit"}, buildkitdArgs...)
}
var extraEnv []string
if runtime.GOOS != "windows" && s.Snapshotter != "native" {
extraEnv = append(extraEnv, "BUILDKIT_DEBUG_FORCE_OVERLAY_DIFF=true")
}
buildkitdSock, stop, err := runBuildkitd(ctx, cfg, buildkitdArgs, cfg.Logs, s.UID, s.GID, extraEnv)
if err != nil {
printLogs(cfg.Logs, log.Println)
return nil, nil, err
}
return backend{
address: buildkitdSock,
rootless: s.UID != 0,
snapshotter: s.Snapshotter,
}, stop, nil
}
func (s *OCI) Close() error {
return nil
}

View File

@ -1,15 +1,16 @@
package integration
var pins = map[string]map[string]string{
// busybox is pinned to 1.35. Newer produces has "illegal instruction" panic on some of Github infra on sha256sum
// busybox 1.36
"busybox:latest": {
"amd64": "sha256:0d5a701f0ca53f38723108687add000e1922f812d4187dea7feaee85d2f5a6c5",
"arm64v8": "sha256:ffe38d75e44d8ffac4cd6d09777ffc31e94ea0ded6a0164e825a325dc17a3b68",
"library": "sha256:f4ed5f2163110c26d42741fdc92bd1710e118aed4edb19212548e8ca4e5fca22",
"amd64": "sha256:023917ec6a886d0e8e15f28fb543515a5fcd8d938edb091e8147db4efed388ee",
"arm64v8": "sha256:1fa89c01cd0473cedbd1a470abb8c139eeb80920edf1bc55de87851bfb63ea11",
"library": "sha256:3fbc632167424a6d997e74f52b878d7cc478225cffac6bc977eedfe51c7f4e79",
},
// alpine 3.18
"alpine:latest": {
"amd64": "sha256:c0d488a800e4127c334ad20d61d7bc21b4097540327217dfab52262adc02380c",
"arm64v8": "sha256:af06af3514c44a964d3b905b498cf6493db8f1cde7c10e078213a89c87308ba0",
"library": "sha256:8914eb54f968791faf6a8638949e480fef81e697984fba772b3976835194c6d4",
"amd64": "sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70",
"arm64v8": "sha256:e3bd82196e98898cae9fe7fbfd6e2436530485974dc4fb3b7ddb69134eda2407",
"library": "sha256:82d1e9d7ed48a7523bdebc18cf6290bdb97b82302a8a9c27d4fe885949ea94d1",
},
}

View File

@ -15,11 +15,11 @@ import (
)
func NewRegistry(dir string) (url string, cl func() error, err error) {
if err := lookupBinary("registry"); err != nil {
if err := LookupBinary("registry"); err != nil {
return "", nil, err
}
deferF := &multiCloser{}
deferF := &MultiCloser{}
cl = deferF.F()
defer func() {
@ -34,7 +34,7 @@ func NewRegistry(dir string) (url string, cl func() error, err error) {
if err != nil {
return "", nil, err
}
deferF.append(func() error { return os.RemoveAll(tmpdir) })
deferF.Append(func() error { return os.RemoveAll(tmpdir) })
dir = tmpdir
}
@ -61,11 +61,11 @@ http:
if err != nil {
return "", nil, err
}
stop, err := startCmd(cmd, nil)
stop, err := StartCmd(cmd, nil)
if err != nil {
return "", nil, err
}
deferF.append(stop)
deferF.Append(stop)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

View File

@ -21,7 +21,6 @@ import (
"github.com/moby/buildkit/util/appcontext"
"github.com/moby/buildkit/util/contentutil"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"golang.org/x/sync/semaphore"
)
@ -37,8 +36,10 @@ type Backend interface {
Address() string
DockerAddress() string
ContainerdAddress() string
Rootless() bool
Snapshotter() string
Supports(feature string) bool
}
type Sandbox interface {
@ -422,44 +423,3 @@ func prepareValueMatrix(tc testConf) []matrixValue {
}
return m
}
func runStargzSnapshotter(cfg *BackendConfig) (address string, cl func() error, err error) {
binary := "containerd-stargz-grpc"
if err := lookupBinary(binary); err != nil {
return "", nil, err
}
deferF := &multiCloser{}
cl = deferF.F()
defer func() {
if err != nil {
deferF.F()()
cl = nil
}
}()
tmpStargzDir, err := os.MkdirTemp("", "bktest_containerd_stargz_grpc")
if err != nil {
return "", nil, err
}
deferF.append(func() error { return os.RemoveAll(tmpStargzDir) })
address = filepath.Join(tmpStargzDir, "containerd-stargz-grpc.sock")
stargzRootDir := filepath.Join(tmpStargzDir, "root")
cmd := exec.Command(binary,
"--log-level", "debug",
"--address", address,
"--root", stargzRootDir)
snStop, err := startCmd(cmd, cfg.Logs)
if err != nil {
return "", nil, err
}
if err = waitUnix(address, 10*time.Second, cmd); err != nil {
snStop()
return "", nil, errors.Wrapf(err, "containerd-stargz-grpc did not start up: %s", formatLogs(cfg.Logs))
}
deferF.append(snStop)
return
}

View File

@ -8,76 +8,20 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"github.com/google/shlex"
"github.com/moby/buildkit/util/bklog"
"github.com/pkg/errors"
)
const buildkitdConfigFile = "buildkitd.toml"
type backend struct {
address string
dockerAddress string
containerdAddress string
rootless bool
snapshotter string
unsupportedFeatures []string
isDockerd bool
}
func (b backend) Address() string {
return b.address
}
func (b backend) DockerAddress() string {
return b.dockerAddress
}
func (b backend) ContainerdAddress() string {
return b.containerdAddress
}
func (b backend) Rootless() bool {
return b.rootless
}
func (b backend) Snapshotter() string {
return b.snapshotter
}
func (b backend) isUnsupportedFeature(feature string) bool {
if enabledFeatures := os.Getenv("BUILDKIT_TEST_ENABLE_FEATURES"); enabledFeatures != "" {
for _, enabledFeature := range strings.Split(enabledFeatures, ",") {
if feature == enabledFeature {
return false
}
}
}
if disabledFeatures := os.Getenv("BUILDKIT_TEST_DISABLE_FEATURES"); disabledFeatures != "" {
for _, disabledFeature := range strings.Split(disabledFeatures, ",") {
if feature == disabledFeature {
return true
}
}
}
for _, unsupportedFeature := range b.unsupportedFeatures {
if feature == unsupportedFeature {
return true
}
}
return false
}
type sandbox struct {
Backend
logs map[string]*bytes.Buffer
cleanup *multiCloser
cleanup *MultiCloser
mv matrixValue
ctx context.Context
name string
@ -96,7 +40,7 @@ func (sb *sandbox) Logs() map[string]*bytes.Buffer {
}
func (sb *sandbox) PrintLogs(t *testing.T) {
printLogs(sb.logs, t.Log)
PrintLogs(sb.logs, t.Log)
}
func (sb *sandbox) ClearLogs() {
@ -108,7 +52,7 @@ func (sb *sandbox) NewRegistry() (string, error) {
if err != nil {
return "", err
}
sb.cleanup.append(cl)
sb.cleanup.Append(cl)
return url, nil
}
@ -144,7 +88,7 @@ func newSandbox(ctx context.Context, w Worker, mirror string, mv matrixValue) (s
upt = append(upt, withMirrorConfig(mirror))
}
deferF := &multiCloser{}
deferF := &MultiCloser{}
cl = deferF.F()
defer func() {
@ -159,7 +103,7 @@ func newSandbox(ctx context.Context, w Worker, mirror string, mv matrixValue) (s
if err != nil {
return nil, nil, err
}
deferF.append(func() error {
deferF.Append(func() error {
return os.RemoveAll(dir)
})
cfg.ConfigFile = filepath.Join(dir, buildkitdConfigFile)
@ -169,7 +113,7 @@ func newSandbox(ctx context.Context, w Worker, mirror string, mv matrixValue) (s
if err != nil {
return nil, nil, err
}
deferF.append(closer)
deferF.Append(closer)
return &sandbox{
Backend: b,
@ -181,95 +125,8 @@ func newSandbox(ctx context.Context, w Worker, mirror string, mv matrixValue) (s
}, cl, nil
}
func getBuildkitdAddr(tmpdir string) string {
address := "unix://" + filepath.Join(tmpdir, "buildkitd.sock")
if runtime.GOOS == "windows" {
address = "//./pipe/buildkitd-" + filepath.Base(tmpdir)
}
return address
}
func runBuildkitd(ctx context.Context, conf *BackendConfig, args []string, logs map[string]*bytes.Buffer, uid, gid int, extraEnv []string) (address string, cl func() error, err error) {
deferF := &multiCloser{}
cl = deferF.F()
defer func() {
if err != nil {
deferF.F()()
cl = nil
}
}()
if conf.ConfigFile != "" {
args = append(args, "--config="+conf.ConfigFile)
}
tmpdir, err := os.MkdirTemp("", "bktest_buildkitd")
if err != nil {
return "", nil, err
}
if err := os.Chown(tmpdir, uid, gid); err != nil {
return "", nil, err
}
if err := os.MkdirAll(filepath.Join(tmpdir, "tmp"), 0711); err != nil {
return "", nil, err
}
if err := os.Chown(filepath.Join(tmpdir, "tmp"), uid, gid); err != nil {
return "", nil, err
}
deferF.append(func() error { return os.RemoveAll(tmpdir) })
address = getBuildkitdAddr(tmpdir)
args = append(args, "--root", tmpdir, "--addr", address, "--debug")
cmd := exec.Command(args[0], args[1:]...) //nolint:gosec // test utility
cmd.Env = append(os.Environ(), "BUILDKIT_DEBUG_EXEC_OUTPUT=1", "BUILDKIT_DEBUG_PANIC_ON_ERROR=1", "TMPDIR="+filepath.Join(tmpdir, "tmp"))
cmd.Env = append(cmd.Env, extraEnv...)
cmd.SysProcAttr = getSysProcAttr()
stop, err := startCmd(cmd, logs)
if err != nil {
return "", nil, err
}
deferF.append(stop)
if err := waitUnix(address, 15*time.Second, cmd); err != nil {
return "", nil, err
}
deferF.append(func() error {
f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return errors.Wrap(err, "failed to open mountinfo")
}
defer f.Close()
s := bufio.NewScanner(f)
for s.Scan() {
if strings.Contains(s.Text(), tmpdir) {
return errors.Errorf("leaked mountpoint for %s", tmpdir)
}
}
return s.Err()
})
return address, cl, err
}
func getBackend(sb Sandbox) (*backend, error) {
sbx, ok := sb.(*sandbox)
if !ok {
return nil, errors.Errorf("invalid sandbox type %T", sb)
}
b, ok := sbx.Backend.(backend)
if !ok {
return nil, errors.Errorf("invalid backend type %T", b)
}
return &b, nil
}
func rootlessSupported(uid int) bool {
cmd := exec.Command("sudo", "-u", fmt.Sprintf("#%d", uid), "-i", "--", "exec", "unshare", "-U", "true") //nolint:gosec // test utility
func RootlessSupported(uid int) bool {
cmd := exec.Command("sudo", "-E", "-u", fmt.Sprintf("#%d", uid), "-i", "--", "exec", "unshare", "-U", "true") //nolint:gosec // test utility
b, err := cmd.CombinedOutput()
if err != nil {
bklog.L.Warnf("rootless mode is not supported on this host: %v (%s)", err, string(b))
@ -278,7 +135,7 @@ func rootlessSupported(uid int) bool {
return true
}
func printLogs(logs map[string]*bytes.Buffer, f func(args ...interface{})) {
func PrintLogs(logs map[string]*bytes.Buffer, f func(args ...interface{})) {
for name, l := range logs {
f(name)
s := bufio.NewScanner(l)
@ -288,74 +145,25 @@ func printLogs(logs map[string]*bytes.Buffer, f func(args ...interface{})) {
}
}
const (
FeatureCacheExport = "cache_export"
FeatureCacheImport = "cache_import"
FeatureCacheBackendAzblob = "cache_backend_azblob"
FeatureCacheBackendGha = "cache_backend_gha"
FeatureCacheBackendInline = "cache_backend_inline"
FeatureCacheBackendLocal = "cache_backend_local"
FeatureCacheBackendRegistry = "cache_backend_registry"
FeatureCacheBackendS3 = "cache_backend_s3"
FeatureDirectPush = "direct_push"
FeatureFrontendOutline = "frontend_outline"
FeatureFrontendTargets = "frontend_targets"
FeatureImageExporter = "image_exporter"
FeatureInfo = "info"
FeatureMergeDiff = "merge_diff"
FeatureMultiCacheExport = "multi_cache_export"
FeatureMultiPlatform = "multi_platform"
FeatureOCIExporter = "oci_exporter"
FeatureOCILayout = "oci_layout"
FeatureProvenance = "provenance"
FeatureSBOM = "sbom"
FeatureSecurityMode = "security_mode"
FeatureSourceDateEpoch = "source_date_epoch"
FeatureCNINetwork = "cni_network"
)
var features = map[string]struct{}{
FeatureCacheExport: {},
FeatureCacheImport: {},
FeatureCacheBackendAzblob: {},
FeatureCacheBackendGha: {},
FeatureCacheBackendInline: {},
FeatureCacheBackendLocal: {},
FeatureCacheBackendRegistry: {},
FeatureCacheBackendS3: {},
FeatureDirectPush: {},
FeatureFrontendOutline: {},
FeatureFrontendTargets: {},
FeatureImageExporter: {},
FeatureInfo: {},
FeatureMergeDiff: {},
FeatureMultiCacheExport: {},
FeatureMultiPlatform: {},
FeatureOCIExporter: {},
FeatureOCILayout: {},
FeatureProvenance: {},
FeatureSBOM: {},
FeatureSecurityMode: {},
FeatureSourceDateEpoch: {},
FeatureCNINetwork: {},
func FormatLogs(m map[string]*bytes.Buffer) string {
var ss []string
for k, b := range m {
if b != nil {
ss = append(ss, fmt.Sprintf("%q:%q", k, b.String()))
}
}
return strings.Join(ss, ",")
}
func CheckFeatureCompat(t *testing.T, sb Sandbox, reason ...string) {
func CheckFeatureCompat(t *testing.T, sb Sandbox, features map[string]struct{}, reason ...string) {
t.Helper()
if len(reason) == 0 {
t.Fatal("no reason provided")
}
b, err := getBackend(sb)
if err != nil {
t.Fatal(err)
}
if len(b.unsupportedFeatures) == 0 {
return
}
var ereasons []string
for _, r := range reason {
if _, ok := features[r]; ok {
if b.isUnsupportedFeature(r) {
if !sb.Supports(r) {
ereasons = append(ereasons, r)
}
} else {

View File

@ -1,12 +0,0 @@
//go:build !windows
// +build !windows
package integration
import "syscall"
func getSysProcAttr() *syscall.SysProcAttr {
return &syscall.SysProcAttr{
Setsid: true, // stretch sudo needs this for sigterm
}
}

View File

@ -1,10 +0,0 @@
//go:build windows
// +build windows
package integration
import "syscall"
func getSysProcAttr() *syscall.SysProcAttr {
return &syscall.SysProcAttr{}
}

View File

@ -3,7 +3,6 @@ package integration
import (
"bytes"
"context"
"crypto/rand"
"fmt"
"io"
"net"
@ -21,20 +20,42 @@ import (
"golang.org/x/sync/errgroup"
)
func runCmd(cmd *exec.Cmd, logs map[string]*bytes.Buffer) error {
var ErrRequirements = errors.Errorf("missing requirements")
func Tmpdir(t *testing.T, appliers ...fstest.Applier) string {
t.Helper()
// We cannot use t.TempDir() to create a temporary directory here because
// appliers might contain fstest.CreateSocket. If the test name is too long,
// t.TempDir() could return a path that is longer than 108 characters. This
// would result in "bind: invalid argument" when we listen on the socket.
tmpdir, err := os.MkdirTemp("", "buildkit")
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(tmpdir))
})
err = fstest.Apply(appliers...).Apply(tmpdir)
require.NoError(t, err)
return tmpdir
}
func RunCmd(cmd *exec.Cmd, logs map[string]*bytes.Buffer) error {
if logs != nil {
setCmdLogs(cmd, logs)
}
fmt.Fprintf(cmd.Stderr, "> runCmd %v %+v\n", time.Now(), cmd.String())
fmt.Fprintf(cmd.Stderr, "> RunCmd %v %+v\n", time.Now(), cmd.String())
return cmd.Run()
}
func startCmd(cmd *exec.Cmd, logs map[string]*bytes.Buffer) (func() error, error) {
func StartCmd(cmd *exec.Cmd, logs map[string]*bytes.Buffer) (func() error, error) {
if logs != nil {
setCmdLogs(cmd, logs)
}
fmt.Fprintf(cmd.Stderr, "> startCmd %v %+v\n", time.Now(), cmd.String())
fmt.Fprintf(cmd.Stderr, "> StartCmd %v %+v\n", time.Now(), cmd.String())
if err := cmd.Start(); err != nil {
return nil, err
@ -79,16 +100,7 @@ func startCmd(cmd *exec.Cmd, logs map[string]*bytes.Buffer) (func() error, error
}, nil
}
func setCmdLogs(cmd *exec.Cmd, logs map[string]*bytes.Buffer) {
b := new(bytes.Buffer)
logs["stdout: "+cmd.String()] = b
cmd.Stdout = &lockingWriter{Writer: b}
b = new(bytes.Buffer)
logs["stderr: "+cmd.String()] = b
cmd.Stderr = &lockingWriter{Writer: b}
}
func waitUnix(address string, d time.Duration, cmd *exec.Cmd) error {
func WaitUnix(address string, d time.Duration, cmd *exec.Cmd) error {
address = strings.TrimPrefix(address, "unix://")
addr, err := net.ResolveUnixAddr("unix", address)
if err != nil {
@ -115,11 +127,19 @@ func waitUnix(address string, d time.Duration, cmd *exec.Cmd) error {
return nil
}
type multiCloser struct {
func LookupBinary(name string) error {
_, err := exec.LookPath(name)
if err != nil {
return errors.Wrapf(ErrRequirements, "failed to lookup %s binary", name)
}
return nil
}
type MultiCloser struct {
fns []func() error
}
func (mc *multiCloser) F() func() error {
func (mc *MultiCloser) F() func() error {
return func() error {
var err error
for i := range mc.fns {
@ -132,25 +152,17 @@ func (mc *multiCloser) F() func() error {
}
}
func (mc *multiCloser) append(f func() error) {
func (mc *MultiCloser) Append(f func() error) {
mc.fns = append(mc.fns, f)
}
var ErrRequirements = errors.Errorf("missing requirements")
func lookupBinary(name string) error {
_, err := exec.LookPath(name)
if err != nil {
return errors.Wrapf(ErrRequirements, "failed to lookup %s binary", name)
}
return nil
}
func requireRoot() error {
if os.Getuid() != 0 {
return errors.Wrap(ErrRequirements, "requires root")
}
return nil
func setCmdLogs(cmd *exec.Cmd, logs map[string]*bytes.Buffer) {
b := new(bytes.Buffer)
logs["stdout: "+cmd.String()] = b
cmd.Stdout = &lockingWriter{Writer: b}
b = new(bytes.Buffer)
logs["stderr: "+cmd.String()] = b
cmd.Stderr = &lockingWriter{Writer: b}
}
type lockingWriter struct {
@ -164,33 +176,3 @@ func (w *lockingWriter) Write(dt []byte) (int, error) {
w.mu.Unlock()
return n, err
}
func Tmpdir(t *testing.T, appliers ...fstest.Applier) (string, error) {
// We cannot use t.TempDir() to create a temporary directory here because
// appliers might contain fstest.CreateSocket. If the test name is too long,
// t.TempDir() could return a path that is longer than 108 characters. This
// would result in "bind: invalid argument" when we listen on the socket.
tmpdir, err := os.MkdirTemp("", "buildkit")
if err != nil {
return "", err
}
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(tmpdir))
})
if err := fstest.Apply(appliers...).Apply(tmpdir); err != nil {
return "", err
}
return tmpdir, nil
}
func randomString(n int) string {
chars := "abcdefghijklmnopqrstuvwxyz"
var b = make([]byte, n)
_, _ = rand.Read(b)
for k, v := range b {
b[k] = chars[v%byte(len(chars))]
}
return string(b)
}