Repository: moby/moby (language: go)

Issue:
containerd integration: Passing a build context via tarball to the `/build` endpoint is broken
Related downstream issues:

- https://github.com/kreuzwerker/terraform-provider-docker/issues/534
- https://github.com/kreuzwerker/terraform-provider-docker/pull/665
- https://github.com/moby/buildkit/issues/5623
- https://github.com/pulumi/pulumi-docker/issues/967
- https://github.com/kurtosis-tech/kurtosis/issues/2613



### Description

Building via the `/build` endpoint with `Version=BuilderBuildkit` fails with:
```
failed to read downloaded context: failed to load cache key: invalid response status 403
```

When passing a build context via a tar without a build session.

It works with graphdrivers, because the `mobyworker` makes use of the passed `http.RoundTripper`:
https://github.com/moby/moby/blob/8d5d655db0170361d10dbd0acfcf7d171ca29815/builder/builder-next/controller.go#L355
https://github.com/moby/moby/blob/8d5d655db0170361d10dbd0acfcf7d171ca29815/builder/builder-next/controller.go#L193

which is used to respond to a fake url echoing the passed build context:
https://github.com/moby/moby/blob/8d5d655db0170361d10dbd0acfcf7d171ca29815/builder/builder-next/builder.go#L294

With the containerd integration, the ContainerdWorker doesn't use this RoundTripper so it isn't able to access the build context.

### Reproduce

```go
package main

import (
	"context"
	"io"
	"os"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
)

func main() {
	cli, err := client.NewClientWithOpts(client.FromEnv)
	if err != nil {
		panic(err)
	}

	rd, err := os.Open("context.tar")
	if err != nil {
		panic(err)
	}
	defer rd.Close()

	imageBuildResponse, err := cli.ImageBuild(context.Background(), rd, types.ImageBuildOptions{
		Version: types.BuilderBuildKit, // without this, classic builder is used
		Tags:    []string{"myimage:latest"},
	})
	if err != nil {
		panic(err)
	}
	defer imageBuildResponse.Body.Close()

	_, err = io.Copy(os.Stdout, imageBuildResponse.Body)
	if err != nil {
		panic(err)
	}
}
```


[context.tar.zip](https://github.com/moby/moby/files/14976968/context.tar.zip)
(zipping a tar, because github doesn't allow uploading tars 🙈)


### Expected behavior

_No response_

### docker version

```bash
Client:
 Cloud integration: v1.0.35+desktop.13
 Version:           26.0.0
 API version:       1.45
 Go version:        go1.21.8
 Git commit:        2ae903e
 Built:             Wed Mar 20 15:14:46 2024
 OS/Arch:           darwin/arm64
 Context:           desktop-linux

Server: Docker Desktop 4.30.0 (145546)
 Engine:
  Version:          26.0.0
  API version:      1.45 (minimum version 1.24)
  Go version:       go1.21.8
  Git commit:       8b79278
  Built:            Wed Mar 20 15:18:02 2024
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.6.28
  GitCommit:        ae07eda36dd25f8a1b98dfbf587313b99c0190bb
 runc:
  Version:          1.1.12
  GitCommit:        v1.1.12-0-g51d5e94
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
```


### docker info

```bash
N/A
```


### Additional Info

_No response_


Discussion / hints:

Fix patch:
diff --git a/builder/builder-next/controller.go b/builder/builder-next/controller.go
index cd0e234da094b..bed62eb5438cf 100644
--- a/builder/builder-next/controller.go
+++ b/builder/builder-next/controller.go
@@ -148,7 +148,7 @@ func newSnapshotterController(ctx context.Context, rt http.RoundTripper, opt Opt
 	}
 	wo.Executor = exec
 
-	w, err := mobyworker.NewContainerdWorker(ctx, wo, opt.Callbacks)
+	w, err := mobyworker.NewContainerdWorker(ctx, wo, opt.Callbacks, rt)
 	if err != nil {
 		return nil, err
 	}
diff --git a/builder/builder-next/worker/containerdworker.go b/builder/builder-next/worker/containerdworker.go
index 848932d3fa3e3..f4b98d614c101 100644
--- a/builder/builder-next/worker/containerdworker.go
+++ b/builder/builder-next/worker/containerdworker.go
@@ -2,11 +2,14 @@ package worker
 
 import (
 	"context"
+	nethttp "net/http"
 
+	"github.com/containerd/log"
 	"github.com/docker/docker/builder/builder-next/exporter"
 	"github.com/moby/buildkit/client"
 	bkexporter "github.com/moby/buildkit/exporter"
 	"github.com/moby/buildkit/session"
+	"github.com/moby/buildkit/source/http"
 	"github.com/moby/buildkit/worker/base"
 )
 
@@ -17,11 +20,21 @@ type ContainerdWorker struct {
 }
 
 // NewContainerdWorker instantiates a local worker.
-func NewContainerdWorker(ctx context.Context, wo base.WorkerOpt, callbacks exporter.BuildkitCallbacks) (*ContainerdWorker, error) {
+func NewContainerdWorker(ctx context.Context, wo base.WorkerOpt, callbacks exporter.BuildkitCallbacks, rt nethttp.RoundTripper) (*ContainerdWorker, error) {
 	bw, err := base.NewWorker(ctx, wo)
 	if err != nil {
 		return nil, err
 	}
+	hs, err := http.NewSource(http.Opt{
+		CacheAccessor: bw.CacheManager(),
+		Transport:     rt,
+	})
+	if err == nil {
+		bw.SourceManager.Register(hs)
+	} else {
+		log.G(ctx).Warnf("Could not register builder http source: %s", err)
+	}
+
 	return &ContainerdWorker{Worker: bw, callbacks: callbacks}, nil
 }
 


Test patch: