File: dialstdio.go

package info (click to toggle)
docker-buildx 0.13.1%2Bds1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,356 kB
  • sloc: sh: 299; makefile: 87
file content (124 lines) | stat: -rw-r--r-- 2,972 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package tests

import (
	"bytes"
	"context"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
	"time"

	"github.com/docker/buildx/util/progress"
	"github.com/moby/buildkit/client"
	"github.com/moby/buildkit/client/llb"
	gwclient "github.com/moby/buildkit/frontend/gateway/client"
	"github.com/moby/buildkit/util/progress/progressui"
	"github.com/moby/buildkit/util/testutil/integration"
	"github.com/pkg/errors"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

var dialstdioTests = []func(t *testing.T, sb integration.Sandbox){
	testDialStdio,
}

func testDialStdio(t *testing.T, sb integration.Sandbox) {
	do := func(t *testing.T, pipe func(t *testing.T, cmd *exec.Cmd) net.Conn) {
		errBuf := bytes.NewBuffer(nil)
		defer func() {
			if t.Failed() {
				t.Log(errBuf.String())
			}
		}()
		var cmd *exec.Cmd
		c, err := client.New(sb.Context(), "", client.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
			cmd = buildxCmd(sb, withArgs("dial-stdio", "--progress", "auto"))
			conn := pipe(t, cmd)
			cmd.Stderr = errBuf
			if err := cmd.Start(); err != nil {
				return nil, errors.Wrap(err, errBuf.String())
			}

			return conn, nil
		}))
		require.NoError(t, err)

		defer func() {
			c.Close()
			// Since the client is closed (and as such the connection shutdown), the buildx command should exit cleanly.
			chErr := make(chan error, 1)
			go func() {
				chErr <- cmd.Wait()
			}()
			select {
			case <-time.After(10 * time.Second):
				t.Error("timeout waiting for buildx command to exit")
			case <-chErr:
				assert.NoError(t, err)
			}
		}()

		_, err = c.Info(sb.Context())
		require.NoError(t, err)

		require.Contains(t, errBuf.String(), "builder: "+sb.Address())

		dir := t.TempDir()

		f, err := os.CreateTemp(dir, "log")
		require.NoError(t, err)
		defer f.Close()

		defer func() {
			if t.Failed() {
				dt, _ := os.ReadFile(f.Name())
				t.Log(string(dt))
			}
		}()

		p, err := progress.NewPrinter(sb.Context(), f, progressui.AutoMode)
		require.NoError(t, err)

		ch, chDone := progress.NewChannel(p)
		done := func() {
			select {
			case <-sb.Context().Done():
			case <-chDone:
			}
		}

		_, err = c.Build(sb.Context(), client.SolveOpt{
			Exports: []client.ExportEntry{
				{Type: "local", OutputDir: dir},
			},
		}, "", func(ctx context.Context, gwc gwclient.Client) (*gwclient.Result, error) {
			def, err := llb.Scratch().File(llb.Mkfile("hello", 0o600, []byte("world"))).Marshal(ctx)
			if err != nil {
				return nil, err
			}

			return gwc.Solve(ctx, gwclient.SolveRequest{
				Definition: def.ToPB(),
			})
		}, ch)
		done()
		require.NoError(t, err)

		dt, err := os.ReadFile(filepath.Join(dir, "hello"))
		require.NoError(t, err)
		require.Equal(t, "world", string(dt))
	}

	t.Run("conn=netpipe", func(t *testing.T) {
		t.Parallel()
		do(t, func(t *testing.T, cmd *exec.Cmd) net.Conn {
			c1, c2 := net.Pipe()
			cmd.Stdin = c1
			cmd.Stdout = c1
			return c2
		})
	})
}