You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
249 lines
5.6 KiB
Go
249 lines
5.6 KiB
Go
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",
|
|
"--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 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"
|
|
}
|