Images loaded into Docker's containerd image store are identified by their manifest or index digest, while the legacy graphdriver store uses the config digest. buildx prefers containerimage.config.digest when the Moby exporter returns both digests. Some exporter responses still produce a usable iidfile, but affected builds, such as those with attestations disabled, write a config digest that Docker image commands cannot resolve. Expose the Docker driver's image-store mode as a feature and set prefer-image-digest for containerd-backed Moby exports. Signed-off-by: Paweł Gronowski <git@grono.dev>
154 lines
4.2 KiB
Go
154 lines
4.2 KiB
Go
package docker
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/docker/buildx/driver"
|
|
"github.com/docker/buildx/util/progress"
|
|
"github.com/moby/buildkit/client"
|
|
dockerclient "github.com/moby/moby/client"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type Driver struct {
|
|
factory driver.Factory
|
|
driver.InitConfig
|
|
|
|
// if you add fields, remember to update docs:
|
|
// https://github.com/docker/docs/blob/main/content/build/drivers/docker.md
|
|
features features
|
|
hostGateway hostGateway
|
|
}
|
|
|
|
func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
|
|
return nil
|
|
}
|
|
|
|
func (d *Driver) Info(ctx context.Context) (*driver.Info, error) {
|
|
// TODO(thaJeztah): is "ping" enough for this?
|
|
_, err := d.DockerAPI.ServerVersion(ctx, dockerclient.ServerVersionOptions{})
|
|
if err != nil {
|
|
return nil, errors.Wrap(driver.ErrNotConnecting{}, err.Error())
|
|
}
|
|
return &driver.Info{
|
|
Status: driver.Running,
|
|
}, nil
|
|
}
|
|
|
|
func (d *Driver) Version(ctx context.Context) (string, error) {
|
|
v, err := d.DockerAPI.ServerVersion(ctx, dockerclient.ServerVersionOptions{})
|
|
if err != nil {
|
|
return "", errors.Wrap(driver.ErrNotConnecting{}, err.Error())
|
|
}
|
|
// TODO(thaJeztah): this code is only used for docker <= v23.0, which are deprecated.
|
|
if bkversion, _ := resolveBuildKitVersion(v.Version); bkversion != "" {
|
|
return bkversion, nil
|
|
}
|
|
// https://github.com/moby/moby/blob/efc7a2abc3ab6dfa7d8d5d8c1c3b99138989b0f1/builder/builder-next/worker/worker.go#L176
|
|
return strings.TrimSuffix(v.Version, "-moby"), nil
|
|
}
|
|
|
|
func (d *Driver) Stop(ctx context.Context, force bool) error {
|
|
return nil
|
|
}
|
|
|
|
func (d *Driver) Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error {
|
|
return nil
|
|
}
|
|
|
|
func (d *Driver) Dial(ctx context.Context) (net.Conn, error) {
|
|
return d.DockerAPI.DialHijack(ctx, "/grpc", "h2c", d.DialMeta)
|
|
}
|
|
|
|
func (d *Driver) Client(ctx context.Context, opts ...client.ClientOpt) (*client.Client, error) {
|
|
opts = append([]client.ClientOpt{
|
|
client.WithContextDialer(func(context.Context, string) (net.Conn, error) {
|
|
return d.Dial(ctx)
|
|
}), client.WithSessionDialer(func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) {
|
|
return d.DockerAPI.DialHijack(ctx, "/session", proto, meta)
|
|
}),
|
|
}, opts...)
|
|
return client.New(ctx, "", opts...)
|
|
}
|
|
|
|
type features struct {
|
|
once sync.Once
|
|
list map[driver.Feature]bool
|
|
}
|
|
|
|
func (d *Driver) Features(ctx context.Context) map[driver.Feature]bool {
|
|
d.features.once.Do(func() {
|
|
var useContainerdSnapshotter bool
|
|
if c, err := d.Client(ctx); err == nil {
|
|
workers, _ := c.ListWorkers(ctx)
|
|
for _, w := range workers {
|
|
if _, ok := w.Labels["org.mobyproject.buildkit.worker.snapshotter"]; ok {
|
|
useContainerdSnapshotter = true
|
|
}
|
|
}
|
|
c.Close()
|
|
}
|
|
d.features.list = map[driver.Feature]bool{
|
|
driver.OCIExporter: useContainerdSnapshotter,
|
|
driver.DockerExporter: useContainerdSnapshotter,
|
|
driver.CacheExport: useContainerdSnapshotter,
|
|
driver.MultiPlatform: useContainerdSnapshotter,
|
|
driver.DirectPush: useContainerdSnapshotter,
|
|
driver.PreferImageDigest: useContainerdSnapshotter,
|
|
driver.DefaultLoad: true,
|
|
}
|
|
})
|
|
return d.features.list
|
|
}
|
|
|
|
type hostGateway struct {
|
|
once sync.Once
|
|
ip net.IP
|
|
err error
|
|
}
|
|
|
|
func (d *Driver) HostGatewayIP(ctx context.Context) (net.IP, error) {
|
|
d.hostGateway.once.Do(func() {
|
|
c, err := d.Client(ctx)
|
|
if err != nil {
|
|
d.hostGateway.err = err
|
|
return
|
|
}
|
|
defer c.Close()
|
|
workers, err := c.ListWorkers(ctx)
|
|
if err != nil {
|
|
d.hostGateway.err = errors.Wrap(err, "listing workers")
|
|
return
|
|
}
|
|
for _, w := range workers {
|
|
// should match github.com/docker/docker/builder/builder-next/worker/label.HostGatewayIP const
|
|
if v, ok := w.Labels["org.mobyproject.buildkit.worker.moby.host-gateway-ip"]; ok && v != "" {
|
|
ip := net.ParseIP(v)
|
|
if ip == nil {
|
|
d.hostGateway.err = errors.Errorf("failed to parse host-gateway IP: %s", v)
|
|
return
|
|
}
|
|
d.hostGateway.ip = ip
|
|
return
|
|
}
|
|
}
|
|
d.hostGateway.err = errors.New("host-gateway IP not found")
|
|
})
|
|
return d.hostGateway.ip, d.hostGateway.err
|
|
}
|
|
|
|
func (d *Driver) Factory() driver.Factory {
|
|
return d.factory
|
|
}
|
|
|
|
func (d *Driver) IsMobyDriver() bool {
|
|
return true
|
|
}
|
|
|
|
func (d *Driver) Config() driver.InitConfig {
|
|
return d.InitConfig
|
|
}
|