Skip to content

Commit

Permalink
test: Remove fallback for buildx dial-stdio
Browse files Browse the repository at this point in the history
Remove the code that falls back to the old behavior of trying to parse
the buildx directory.
This makes tests require buildx >= 0.13.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
  • Loading branch information
cpuguy83 committed May 13, 2024
1 parent 9e80998 commit 7310e1e
Showing 1 changed file with 9 additions and 141 deletions.
150 changes: 9 additions & 141 deletions test/testenv/buildx.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,19 @@ import (
"bufio"
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
"testing"

"github.com/cpuguy83/dockercfg"
"github.com/cpuguy83/go-docker"
"github.com/cpuguy83/go-docker/buildkitopt"
"github.com/cpuguy83/go-docker/container"
"github.com/cpuguy83/go-docker/transport"
"github.com/moby/buildkit/client"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/solver/pb"
Expand Down Expand Up @@ -104,18 +99,9 @@ func (b *BuildxEnv) supportsDialStdio(ctx context.Context) (bool, error) {
return minor >= 13, nil
}

var errDialStdioNotSupportedErr = errors.New("buildx dial-stdio not supported")

func (b *BuildxEnv) dialStdio(ctx context.Context) (bool, error) {
ok, err := b.supportsDialStdio(ctx)
if err != nil {
return false, fmt.Errorf("%w: %w", errDialStdioNotSupportedErr, err)
}

if !ok {
return false, nil
}
var errDialStdioNotSupported = errors.New("buildx dial-stdio not supported")

func (b *BuildxEnv) dialStdio(ctx context.Context) error {
c, err := client.New(ctx, "", client.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
args := []string{"buildx", "dial-stdio", "--progress=plain"}
if b.builder != "" {
Expand Down Expand Up @@ -171,15 +157,14 @@ func (b *BuildxEnv) dialStdio(ctx context.Context) (bool, error) {
}))

if err != nil {
return false, err
return err
}

b.client = c
return true, nil
return nil
}

// bootstrap is ultimately responsible for creating a buildkit client.
// It looks like the buildx config on the client (typically in $HOME/.docker/buildx) to determine how to connect to the configured buildkit.
func (b *BuildxEnv) bootstrap(ctx context.Context) (retErr error) {
if b.client != nil {
return nil
Expand Down Expand Up @@ -208,133 +193,16 @@ func (b *BuildxEnv) bootstrap(ctx context.Context) (retErr error) {
}
}()

ok, err := b.dialStdio(ctx)
if err != nil && !errors.Is(err, errDialStdioNotSupportedErr) {
return err
}

if ok {
return nil
}

// Fallback for older versions of buildx
p, err := dockercfg.ConfigPath()
if err != nil {
return pkgerrors.WithStack(err)
}

if out, err := exec.Command("docker", "buildx", "inspect", "--bootstrap", b.builder).CombinedOutput(); err != nil {
return pkgerrors.Wrapf(err, "failed to bootstrap builder: %s", out)
}

configBase := filepath.Join(filepath.Dir(p), "buildx")

// builder is empty, so we need to check what the currently configured buildx builder is.
// This is stored int he buildx config in (typically) $HOME/.docker/buildx (the `dockercfg` lib determines where this actually is).
if b.builder == "" {
dt, err := os.ReadFile(filepath.Join(configBase, "current"))
if err != nil {
return pkgerrors.Wrap(err, "failed to read current builder")
}

type ref struct {
Name string
Key string
}
var r ref
if err := json.Unmarshal(dt, &r); err != nil {
return err
}

if r.Name == "" {
// This is the "default" buildx instance, aka dockerd's built-in buildkit.
var tr transport.Doer
if r.Key != "" {
tr, err = transport.FromConnectionString(r.Key)
if err != nil {
return pkgerrors.Wrap(err, r.Key)
}
} else {
tr, err = transport.DefaultTransport()
if err != nil {
return pkgerrors.WithStack(err)
}
}

b.client, err = client.New(ctx, "", buildkitopt.FromDocker(tr)...)
return err
}

b.builder = r.Name
}

dt, err := os.ReadFile(filepath.Join(configBase, "instances", b.builder))
ok, err := b.supportsDialStdio(ctx)
if err != nil {
return pkgerrors.Wrap(err, "failed to read buildx instance config")
}

var cfg buildxConfig
if err := json.Unmarshal(dt, &cfg); err != nil {
return pkgerrors.Wrap(err, "failed to unmarshal buildx config")
}

if cfg.Driver != "docker-container" {
return pkgerrors.Errorf("unsupported buildx driver: %s", cfg.Driver)
}

if len(cfg.Nodes) == 0 {
return pkgerrors.Errorf("no buildx nodes configured")
return fmt.Errorf("%w: %w", errDialStdioNotSupported, err)
}

// On a typical client this would be a single node, but there could be multiple registered with he same builder name.
// We'll just try them all until we find one that works.
var errs []error
for _, n := range cfg.Nodes {
tr, err := transport.FromConnectionString(n.Endpoint)
if err != nil {
errs = append(errs, fmt.Errorf("%s: %w", n.Endpoint, err))
continue
}

dc := docker.NewClient(docker.WithTransport(tr))
ctr := dc.ContainerService().NewContainer(ctx, "buildx_buildkit_"+n.Name)

conn1, conn2 := net.Pipe()
ep, err := ctr.Exec(ctx, container.WithExecCmd("buildctl", "dial-stdio"), func(cfg *container.ExecConfig) {
cfg.Stdin = conn1
cfg.Stdout = conn1
cfg.Stderr = conn1
})
if err != nil {
conn1.Close()
conn2.Close()
errs = append(errs, fmt.Errorf("%s: %w", n.Endpoint, err))
continue
}

if err := ep.Start(ctx); err != nil {
conn1.Close()
conn2.Close()
errs = append(errs, fmt.Errorf("%s: %w", n.Endpoint, err))
continue
}

c, err := client.New(ctx, "", client.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
return conn2, nil
}))
if err != nil {
errs = append(errs, fmt.Errorf("%s: %w", n.Endpoint, err))
continue
}

b.client = c
b.ctr = ctr
b.docker = dc
return nil
if !ok {
return errDialStdioNotSupported
}

// Could not create a buildkit client, return all errors.
return errors.Join(errs...)
return b.dialStdio(ctx)
}

type buildxConfig struct {

Check failure on line 208 in test/testenv/buildx.go

View workflow job for this annotation

GitHub Actions / lint

type `buildxConfig` is unused (unused)
Expand Down

0 comments on commit 7310e1e

Please sign in to comment.