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"
 | 
						|
}
 |