package build import ( "context" "io/ioutil" "net" "net/http" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/versions" dclient "github.com/docker/docker/client" "github.com/docker/docker/testutil/fakecontext" "github.com/docker/docker/testutil/request" "github.com/moby/buildkit/session" "github.com/moby/buildkit/session/filesync" "golang.org/x/sync/errgroup" "gotest.tools/assert" is "gotest.tools/assert/cmp" "gotest.tools/skip" ) func TestBuildWithSession(t *testing.T) { skip.If(t, testEnv.DaemonInfo.OSType == "windows") skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.39"), "experimental in older versions") client := testEnv.APIClient() dockerfile := ` FROM busybox COPY file / RUN cat /file ` fctx := fakecontext.New(t, "", fakecontext.WithFile("file", "some content"), ) defer fctx.Close() out := testBuildWithSession(t, client, client.DaemonHost(), fctx.Dir, dockerfile) assert.Check(t, is.Contains(out, "some content")) fctx.Add("second", "contentcontent") dockerfile += ` COPY second / RUN cat /second ` out = testBuildWithSession(t, client, client.DaemonHost(), fctx.Dir, dockerfile) assert.Check(t, is.Equal(strings.Count(out, "Using cache"), 2)) assert.Check(t, is.Contains(out, "contentcontent")) du, err := client.DiskUsage(context.TODO()) assert.Check(t, err) assert.Check(t, du.BuilderSize > 10) out = testBuildWithSession(t, client, client.DaemonHost(), fctx.Dir, dockerfile) assert.Check(t, is.Equal(strings.Count(out, "Using cache"), 4)) du2, err := client.DiskUsage(context.TODO()) assert.Check(t, err) assert.Check(t, is.Equal(du.BuilderSize, du2.BuilderSize)) // rebuild with regular tar, confirm cache still applies fctx.Add("Dockerfile", dockerfile) // FIXME(vdemeester) use sock here res, body, err := request.Do( "/build", request.Host(client.DaemonHost()), request.Method(http.MethodPost), request.RawContent(fctx.AsTarReader(t)), request.ContentType("application/x-tar")) assert.NilError(t, err) assert.Check(t, is.DeepEqual(http.StatusOK, res.StatusCode)) outBytes, err := request.ReadBody(body) assert.NilError(t, err) assert.Check(t, is.Contains(string(outBytes), "Successfully built")) assert.Check(t, is.Equal(strings.Count(string(outBytes), "Using cache"), 4)) _, err = client.BuildCachePrune(context.TODO(), types.BuildCachePruneOptions{All: true}) assert.Check(t, err) du, err = client.DiskUsage(context.TODO()) assert.Check(t, err) assert.Check(t, is.Equal(du.BuilderSize, int64(0))) } func testBuildWithSession(t *testing.T, client dclient.APIClient, daemonHost string, dir, dockerfile string) (outStr string) { ctx := context.Background() sess, err := session.NewSession(ctx, "foo1", "foo") assert.Check(t, err) fsProvider := filesync.NewFSSyncProvider([]filesync.SyncedDir{ {Dir: dir}, }) sess.Allow(fsProvider) g, ctx := errgroup.WithContext(ctx) g.Go(func() error { return sess.Run(ctx, func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) { return client.DialHijack(ctx, "/session", "h2c", meta) }) }) g.Go(func() error { // FIXME use sock here res, body, err := request.Do( "/build?remote=client-session&session="+sess.ID(), request.Host(daemonHost), request.Method(http.MethodPost), request.With(func(req *http.Request) error { req.Body = ioutil.NopCloser(strings.NewReader(dockerfile)) return nil }), ) if err != nil { return err } assert.Check(t, is.DeepEqual(res.StatusCode, http.StatusOK)) out, err := request.ReadBody(body) assert.NilError(t, err) assert.Check(t, is.Contains(string(out), "Successfully built")) sess.Close() outStr = string(out) return nil }) err = g.Wait() assert.Check(t, err) return }