瀏覽代碼

Merge pull request #44365 from vvoland/c8d-contexts

imageservice: Add context to various methods
Sebastiaan van Stijn 2 年之前
父節點
當前提交
e7f034fe4e
共有 42 個文件被更改,包括 343 次插入299 次删除
  1. 4 4
      api/server/router/container/backend.go
  2. 4 4
      api/server/router/container/container_routes.go
  3. 1 1
      api/server/router/image/backend.go
  4. 1 1
      api/server/router/image/image_routes.go
  5. 1 1
      api/server/router/swarm/backend.go
  6. 1 1
      api/server/router/swarm/cluster_routes.go
  7. 3 3
      builder/builder.go
  8. 19 16
      builder/dockerfile/builder.go
  9. 3 3
      builder/dockerfile/containerbackend.go
  10. 53 48
      builder/dockerfile/dispatchers.go
  11. 71 60
      builder/dockerfile/dispatchers_test.go
  12. 19 18
      builder/dockerfile/evaluator.go
  13. 3 2
      builder/dockerfile/evaluator_test.go
  14. 5 5
      builder/dockerfile/imagecontext.go
  15. 4 2
      builder/dockerfile/imagecontext_test.go
  16. 26 10
      builder/dockerfile/imageprobe.go
  17. 10 9
      builder/dockerfile/internals.go
  18. 2 1
      builder/dockerfile/internals_linux.go
  19. 3 2
      builder/dockerfile/internals_linux_test.go
  20. 8 7
      builder/dockerfile/internals_windows.go
  21. 5 5
      builder/dockerfile/mockbackend_test.go
  22. 15 18
      cmd/dockerd/daemon.go
  23. 3 3
      daemon/cluster/executor/backend.go
  24. 2 2
      daemon/cluster/executor/container/adapter.go
  25. 4 4
      daemon/cluster/swarm.go
  26. 3 2
      daemon/commit.go
  27. 3 1
      daemon/containerd/cache.go
  28. 2 1
      daemon/containerd/image_import.go
  29. 9 11
      daemon/create.go
  30. 7 6
      daemon/daemon.go
  31. 1 1
      daemon/disk_usage.go
  32. 2 2
      daemon/image_service.go
  33. 7 4
      daemon/images/cache.go
  34. 3 2
      daemon/images/image_import.go
  35. 10 12
      daemon/list.go
  36. 6 5
      daemon/list_test.go
  37. 1 1
      daemon/monitor.go
  38. 2 2
      daemon/oci_linux.go
  39. 9 8
      daemon/oci_linux_test.go
  40. 1 2
      daemon/oci_windows.go
  41. 1 1
      daemon/restart.go
  42. 6 8
      daemon/start.go

+ 4 - 4
api/server/router/container/backend.go

@@ -32,14 +32,14 @@ type copyBackend interface {
 
 
 // stateBackend includes functions to implement to provide container state lifecycle functionality.
 // stateBackend includes functions to implement to provide container state lifecycle functionality.
 type stateBackend interface {
 type stateBackend interface {
-	ContainerCreate(config types.ContainerCreateConfig) (container.CreateResponse, error)
+	ContainerCreate(ctx context.Context, config types.ContainerCreateConfig) (container.CreateResponse, error)
 	ContainerKill(name string, signal string) error
 	ContainerKill(name string, signal string) error
 	ContainerPause(name string) error
 	ContainerPause(name string) error
 	ContainerRename(oldName, newName string) error
 	ContainerRename(oldName, newName string) error
 	ContainerResize(name string, height, width int) error
 	ContainerResize(name string, height, width int) error
 	ContainerRestart(ctx context.Context, name string, options container.StopOptions) error
 	ContainerRestart(ctx context.Context, name string, options container.StopOptions) error
 	ContainerRm(name string, config *types.ContainerRmConfig) error
 	ContainerRm(name string, config *types.ContainerRmConfig) error
-	ContainerStart(name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
+	ContainerStart(ctx context.Context, name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
 	ContainerStop(ctx context.Context, name string, options container.StopOptions) error
 	ContainerStop(ctx context.Context, name string, options container.StopOptions) error
 	ContainerUnpause(name string) error
 	ContainerUnpause(name string) error
 	ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error)
 	ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error)
@@ -54,7 +54,7 @@ type monitorBackend interface {
 	ContainerStats(ctx context.Context, name string, config *backend.ContainerStatsConfig) error
 	ContainerStats(ctx context.Context, name string, config *backend.ContainerStatsConfig) error
 	ContainerTop(name string, psArgs string) (*container.ContainerTopOKBody, error)
 	ContainerTop(name string, psArgs string) (*container.ContainerTopOKBody, error)
 
 
-	Containers(config *types.ContainerListOptions) ([]*types.Container, error)
+	Containers(ctx context.Context, config *types.ContainerListOptions) ([]*types.Container, error)
 }
 }
 
 
 // attachBackend includes function to implement to provide container attaching functionality.
 // attachBackend includes function to implement to provide container attaching functionality.
@@ -68,7 +68,7 @@ type systemBackend interface {
 }
 }
 
 
 type commitBackend interface {
 type commitBackend interface {
-	CreateImageFromContainer(name string, config *backend.CreateImageConfig) (imageID string, err error)
+	CreateImageFromContainer(ctx context.Context, name string, config *backend.CreateImageConfig) (imageID string, err error)
 }
 }
 
 
 // Backend is all the methods that need to be implemented to provide container specific functionality.
 // Backend is all the methods that need to be implemented to provide container specific functionality.

+ 4 - 4
api/server/router/container/container_routes.go

@@ -58,7 +58,7 @@ func (s *containerRouter) postCommit(ctx context.Context, w http.ResponseWriter,
 		Changes: r.Form["changes"],
 		Changes: r.Form["changes"],
 	}
 	}
 
 
-	imgID, err := s.backend.CreateImageFromContainer(r.Form.Get("container"), commitCfg)
+	imgID, err := s.backend.CreateImageFromContainer(ctx, r.Form.Get("container"), commitCfg)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -91,7 +91,7 @@ func (s *containerRouter) getContainersJSON(ctx context.Context, w http.Response
 		config.Limit = limit
 		config.Limit = limit
 	}
 	}
 
 
-	containers, err := s.backend.Containers(config)
+	containers, err := s.backend.Containers(ctx, config)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -214,7 +214,7 @@ func (s *containerRouter) postContainersStart(ctx context.Context, w http.Respon
 
 
 	checkpoint := r.Form.Get("checkpoint")
 	checkpoint := r.Form.Get("checkpoint")
 	checkpointDir := r.Form.Get("checkpoint-dir")
 	checkpointDir := r.Form.Get("checkpoint-dir")
-	if err := s.backend.ContainerStart(vars["name"], hostConfig, checkpoint, checkpointDir); err != nil {
+	if err := s.backend.ContainerStart(ctx, vars["name"], hostConfig, checkpoint, checkpointDir); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -578,7 +578,7 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
 		hostConfig.PidsLimit = nil
 		hostConfig.PidsLimit = nil
 	}
 	}
 
 
-	ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
+	ccr, err := s.backend.ContainerCreate(ctx, types.ContainerCreateConfig{
 		Name:             name,
 		Name:             name,
 		Config:           config,
 		Config:           config,
 		HostConfig:       hostConfig,
 		HostConfig:       hostConfig,

+ 1 - 1
api/server/router/image/backend.go

@@ -31,7 +31,7 @@ type imageBackend interface {
 
 
 type importExportBackend interface {
 type importExportBackend interface {
 	LoadImage(ctx context.Context, inTar io.ReadCloser, outStream io.Writer, quiet bool) error
 	LoadImage(ctx context.Context, inTar io.ReadCloser, outStream io.Writer, quiet bool) error
-	ImportImage(src string, repository string, platform *specs.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error
+	ImportImage(ctx context.Context, src string, repository string, platform *specs.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error
 	ExportImage(ctx context.Context, names []string, outStream io.Writer) error
 	ExportImage(ctx context.Context, names []string, outStream io.Writer) error
 }
 }
 
 

+ 1 - 1
api/server/router/image/image_routes.go

@@ -68,7 +68,7 @@ func (ir *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrit
 		progressErr = ir.backend.PullImage(ctx, img, tag, platform, metaHeaders, authConfig, output)
 		progressErr = ir.backend.PullImage(ctx, img, tag, platform, metaHeaders, authConfig, output)
 	} else { // import
 	} else { // import
 		src := r.Form.Get("fromSrc")
 		src := r.Form.Get("fromSrc")
-		progressErr = ir.backend.ImportImage(src, repo, platform, tag, message, r.Body, output, r.Form["changes"])
+		progressErr = ir.backend.ImportImage(ctx, src, repo, platform, tag, message, r.Body, output, r.Form["changes"])
 	}
 	}
 	if progressErr != nil {
 	if progressErr != nil {
 		if !output.Flushed() {
 		if !output.Flushed() {

+ 1 - 1
api/server/router/swarm/backend.go

@@ -12,7 +12,7 @@ import (
 type Backend interface {
 type Backend interface {
 	Init(req types.InitRequest) (string, error)
 	Init(req types.InitRequest) (string, error)
 	Join(req types.JoinRequest) error
 	Join(req types.JoinRequest) error
-	Leave(force bool) error
+	Leave(ctx context.Context, force bool) error
 	Inspect() (types.Swarm, error)
 	Inspect() (types.Swarm, error)
 	Update(uint64, types.Spec, types.UpdateFlags) error
 	Update(uint64, types.Spec, types.UpdateFlags) error
 	GetUnlockKey() (string, error)
 	GetUnlockKey() (string, error)

+ 1 - 1
api/server/router/swarm/cluster_routes.go

@@ -56,7 +56,7 @@ func (sr *swarmRouter) leaveCluster(ctx context.Context, w http.ResponseWriter,
 	}
 	}
 
 
 	force := httputils.BoolValue(r, "force")
 	force := httputils.BoolValue(r, "force")
-	return sr.backend.Leave(force)
+	return sr.backend.Leave(ctx, force)
 }
 }
 
 
 func (sr *swarmRouter) inspectCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func (sr *swarmRouter) inspectCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {

+ 3 - 3
builder/builder.go

@@ -60,13 +60,13 @@ type ExecBackend interface {
 	// ContainerAttachRaw attaches to container.
 	// ContainerAttachRaw attaches to container.
 	ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool, attached chan struct{}) error
 	ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool, attached chan struct{}) error
 	// ContainerCreateIgnoreImagesArgsEscaped creates a new Docker container and returns potential warnings
 	// ContainerCreateIgnoreImagesArgsEscaped creates a new Docker container and returns potential warnings
-	ContainerCreateIgnoreImagesArgsEscaped(config types.ContainerCreateConfig) (container.CreateResponse, error)
+	ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context, config types.ContainerCreateConfig) (container.CreateResponse, error)
 	// ContainerRm removes a container specified by `id`.
 	// ContainerRm removes a container specified by `id`.
 	ContainerRm(name string, config *types.ContainerRmConfig) error
 	ContainerRm(name string, config *types.ContainerRmConfig) error
 	// ContainerKill stops the container execution abruptly.
 	// ContainerKill stops the container execution abruptly.
 	ContainerKill(containerID string, sig string) error
 	ContainerKill(containerID string, sig string) error
 	// ContainerStart starts a new container
 	// ContainerStart starts a new container
-	ContainerStart(containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
+	ContainerStart(ctx context.Context, containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
 	// ContainerWait stops processing until the given container is stopped.
 	// ContainerWait stops processing until the given container is stopped.
 	ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
 	ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
 }
 }
@@ -80,7 +80,7 @@ type Result struct {
 // ImageCacheBuilder represents a generator for stateful image cache.
 // ImageCacheBuilder represents a generator for stateful image cache.
 type ImageCacheBuilder interface {
 type ImageCacheBuilder interface {
 	// MakeImageCache creates a stateful image cache.
 	// MakeImageCache creates a stateful image cache.
-	MakeImageCache(cacheFrom []string) ImageCache
+	MakeImageCache(ctx context.Context, cacheFrom []string) (ImageCache, error)
 }
 }
 
 
 // ImageCache abstracts an image cache.
 // ImageCache abstracts an image cache.

+ 19 - 16
builder/dockerfile/builder.go

@@ -95,7 +95,7 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	return b.build(source, dockerfile)
+	return b.build(ctx, source, dockerfile)
 }
 }
 
 
 // builderOptions are the dependencies required by the builder
 // builderOptions are the dependencies required by the builder
@@ -117,8 +117,7 @@ type Builder struct {
 	Aux    *streamformatter.AuxFormatter
 	Aux    *streamformatter.AuxFormatter
 	Output io.Writer
 	Output io.Writer
 
 
-	docker    builder.Backend
-	clientCtx context.Context
+	docker builder.Backend
 
 
 	idMapping        idtools.IdentityMapping
 	idMapping        idtools.IdentityMapping
 	disableCommit    bool
 	disableCommit    bool
@@ -130,14 +129,18 @@ type Builder struct {
 }
 }
 
 
 // newBuilder creates a new Dockerfile builder from an optional dockerfile and a Options.
 // newBuilder creates a new Dockerfile builder from an optional dockerfile and a Options.
-func newBuilder(clientCtx context.Context, options builderOptions) (*Builder, error) {
+func newBuilder(ctx context.Context, options builderOptions) (*Builder, error) {
 	config := options.Options
 	config := options.Options
 	if config == nil {
 	if config == nil {
 		config = new(types.ImageBuildOptions)
 		config = new(types.ImageBuildOptions)
 	}
 	}
 
 
+	imageProber, err := newImageProber(ctx, options.Backend, config.CacheFrom, config.NoCache)
+	if err != nil {
+		return nil, err
+	}
+
 	b := &Builder{
 	b := &Builder{
-		clientCtx:        clientCtx,
 		options:          config,
 		options:          config,
 		Stdout:           options.ProgressWriter.StdoutFormatter,
 		Stdout:           options.ProgressWriter.StdoutFormatter,
 		Stderr:           options.ProgressWriter.StderrFormatter,
 		Stderr:           options.ProgressWriter.StderrFormatter,
@@ -145,9 +148,9 @@ func newBuilder(clientCtx context.Context, options builderOptions) (*Builder, er
 		Output:           options.ProgressWriter.Output,
 		Output:           options.ProgressWriter.Output,
 		docker:           options.Backend,
 		docker:           options.Backend,
 		idMapping:        options.IDMapping,
 		idMapping:        options.IDMapping,
-		imageSources:     newImageSources(clientCtx, options),
+		imageSources:     newImageSources(options),
 		pathCache:        options.PathCache,
 		pathCache:        options.PathCache,
-		imageProber:      newImageProber(options.Backend, config.CacheFrom, config.NoCache),
+		imageProber:      imageProber,
 		containerManager: newContainerManager(options.Backend),
 		containerManager: newContainerManager(options.Backend),
 	}
 	}
 
 
@@ -181,7 +184,7 @@ func buildLabelOptions(labels map[string]string, stages []instructions.Stage) {
 
 
 // Build runs the Dockerfile builder by parsing the Dockerfile and executing
 // Build runs the Dockerfile builder by parsing the Dockerfile and executing
 // the instructions from the file.
 // the instructions from the file.
-func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*builder.Result, error) {
+func (b *Builder) build(ctx context.Context, source builder.Source, dockerfile *parser.Result) (*builder.Result, error) {
 	defer b.imageSources.Unmount()
 	defer b.imageSources.Unmount()
 
 
 	stages, metaArgs, err := instructions.Parse(dockerfile.AST)
 	stages, metaArgs, err := instructions.Parse(dockerfile.AST)
@@ -205,7 +208,7 @@ func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*buil
 	buildLabelOptions(b.options.Labels, stages)
 	buildLabelOptions(b.options.Labels, stages)
 
 
 	dockerfile.PrintWarnings(b.Stderr)
 	dockerfile.PrintWarnings(b.Stderr)
-	dispatchState, err := b.dispatchDockerfileWithCancellation(stages, metaArgs, dockerfile.EscapeToken, source)
+	dispatchState, err := b.dispatchDockerfileWithCancellation(ctx, stages, metaArgs, dockerfile.EscapeToken, source)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -244,7 +247,7 @@ func printCommand(out io.Writer, currentCommandIndex int, totalCommands int, cmd
 	return currentCommandIndex + 1
 	return currentCommandIndex + 1
 }
 }
 
 
-func (b *Builder) dispatchDockerfileWithCancellation(parseResult []instructions.Stage, metaArgs []instructions.ArgCommand, escapeToken rune, source builder.Source) (*dispatchState, error) {
+func (b *Builder) dispatchDockerfileWithCancellation(ctx context.Context, parseResult []instructions.Stage, metaArgs []instructions.ArgCommand, escapeToken rune, source builder.Source) (*dispatchState, error) {
 	dispatchRequest := dispatchRequest{}
 	dispatchRequest := dispatchRequest{}
 	buildArgs := NewBuildArgs(b.options.BuildArgs)
 	buildArgs := NewBuildArgs(b.options.BuildArgs)
 	totalCommands := len(metaArgs) + len(parseResult)
 	totalCommands := len(metaArgs) + len(parseResult)
@@ -272,14 +275,14 @@ func (b *Builder) dispatchDockerfileWithCancellation(parseResult []instructions.
 		dispatchRequest = newDispatchRequest(b, escapeToken, source, buildArgs, stagesResults)
 		dispatchRequest = newDispatchRequest(b, escapeToken, source, buildArgs, stagesResults)
 
 
 		currentCommandIndex = printCommand(b.Stdout, currentCommandIndex, totalCommands, stage.SourceCode)
 		currentCommandIndex = printCommand(b.Stdout, currentCommandIndex, totalCommands, stage.SourceCode)
-		if err := initializeStage(dispatchRequest, &stage); err != nil {
+		if err := initializeStage(ctx, dispatchRequest, &stage); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 		dispatchRequest.state.updateRunConfig()
 		dispatchRequest.state.updateRunConfig()
 		fmt.Fprintf(b.Stdout, " ---> %s\n", stringid.TruncateID(dispatchRequest.state.imageID))
 		fmt.Fprintf(b.Stdout, " ---> %s\n", stringid.TruncateID(dispatchRequest.state.imageID))
 		for _, cmd := range stage.Commands {
 		for _, cmd := range stage.Commands {
 			select {
 			select {
-			case <-b.clientCtx.Done():
+			case <-ctx.Done():
 				logrus.Debug("Builder: build cancelled!")
 				logrus.Debug("Builder: build cancelled!")
 				fmt.Fprint(b.Stdout, "Build cancelled\n")
 				fmt.Fprint(b.Stdout, "Build cancelled\n")
 				buildsFailed.WithValues(metricsBuildCanceled).Inc()
 				buildsFailed.WithValues(metricsBuildCanceled).Inc()
@@ -290,7 +293,7 @@ func (b *Builder) dispatchDockerfileWithCancellation(parseResult []instructions.
 
 
 			currentCommandIndex = printCommand(b.Stdout, currentCommandIndex, totalCommands, cmd)
 			currentCommandIndex = printCommand(b.Stdout, currentCommandIndex, totalCommands, cmd)
 
 
-			if err := dispatch(dispatchRequest, cmd); err != nil {
+			if err := dispatch(ctx, dispatchRequest, cmd); err != nil {
 				return nil, err
 				return nil, err
 			}
 			}
 			dispatchRequest.state.updateRunConfig()
 			dispatchRequest.state.updateRunConfig()
@@ -317,7 +320,7 @@ func (b *Builder) dispatchDockerfileWithCancellation(parseResult []instructions.
 // coming from the query parameter of the same name.
 // coming from the query parameter of the same name.
 //
 //
 // TODO: Remove?
 // TODO: Remove?
-func BuildFromConfig(config *container.Config, changes []string, os string) (*container.Config, error) {
+func BuildFromConfig(ctx context.Context, config *container.Config, changes []string, os string) (*container.Config, error) {
 	if len(changes) == 0 {
 	if len(changes) == 0 {
 		return config, nil
 		return config, nil
 	}
 	}
@@ -327,7 +330,7 @@ func BuildFromConfig(config *container.Config, changes []string, os string) (*co
 		return nil, errdefs.InvalidParameter(err)
 		return nil, errdefs.InvalidParameter(err)
 	}
 	}
 
 
-	b, err := newBuilder(context.Background(), builderOptions{
+	b, err := newBuilder(ctx, builderOptions{
 		Options: &types.ImageBuildOptions{NoCache: true},
 		Options: &types.ImageBuildOptions{NoCache: true},
 	})
 	})
 	if err != nil {
 	if err != nil {
@@ -360,7 +363,7 @@ func BuildFromConfig(config *container.Config, changes []string, os string) (*co
 	dispatchRequest.state.imageID = config.Image
 	dispatchRequest.state.imageID = config.Image
 	dispatchRequest.state.operatingSystem = os
 	dispatchRequest.state.operatingSystem = os
 	for _, cmd := range commands {
 	for _, cmd := range commands {
-		err := dispatch(dispatchRequest, cmd)
+		err := dispatch(ctx, dispatchRequest, cmd)
 		if err != nil {
 		if err != nil {
 			return nil, errdefs.InvalidParameter(err)
 			return nil, errdefs.InvalidParameter(err)
 		}
 		}

+ 3 - 3
builder/dockerfile/containerbackend.go

@@ -28,8 +28,8 @@ func newContainerManager(docker builder.ExecBackend) *containerManager {
 }
 }
 
 
 // Create a container
 // Create a container
-func (c *containerManager) Create(runConfig *container.Config, hostConfig *container.HostConfig) (container.CreateResponse, error) {
-	container, err := c.backend.ContainerCreateIgnoreImagesArgsEscaped(types.ContainerCreateConfig{
+func (c *containerManager) Create(ctx context.Context, runConfig *container.Config, hostConfig *container.HostConfig) (container.CreateResponse, error) {
+	container, err := c.backend.ContainerCreateIgnoreImagesArgsEscaped(ctx, types.ContainerCreateConfig{
 		Config:     runConfig,
 		Config:     runConfig,
 		HostConfig: hostConfig,
 		HostConfig: hostConfig,
 	})
 	})
@@ -69,7 +69,7 @@ func (c *containerManager) Run(ctx context.Context, cID string, stdout, stderr i
 		}
 		}
 	}()
 	}()
 
 
-	if err := c.backend.ContainerStart(cID, nil, "", ""); err != nil {
+	if err := c.backend.ContainerStart(ctx, cID, nil, "", ""); err != nil {
 		close(finished)
 		close(finished)
 		logCancellationError(cancelErrCh, "error from ContainerStart: "+err.Error())
 		logCancellationError(cancelErrCh, "error from ContainerStart: "+err.Error())
 		return err
 		return err

+ 53 - 48
builder/dockerfile/dispatchers.go

@@ -9,6 +9,7 @@ package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"context"
 	"fmt"
 	"fmt"
 	"runtime"
 	"runtime"
 	"sort"
 	"sort"
@@ -35,7 +36,7 @@ import (
 //
 //
 // Sets the environment variable foo to bar, also makes interpolation
 // Sets the environment variable foo to bar, also makes interpolation
 // in the dockerfile available from the next statement on via ${foo}.
 // in the dockerfile available from the next statement on via ${foo}.
-func dispatchEnv(d dispatchRequest, c *instructions.EnvCommand) error {
+func dispatchEnv(ctx context.Context, d dispatchRequest, c *instructions.EnvCommand) error {
 	runConfig := d.state.runConfig
 	runConfig := d.state.runConfig
 	commitMessage := bytes.NewBufferString("ENV")
 	commitMessage := bytes.NewBufferString("ENV")
 	for _, e := range c.Env {
 	for _, e := range c.Env {
@@ -57,21 +58,21 @@ func dispatchEnv(d dispatchRequest, c *instructions.EnvCommand) error {
 			runConfig.Env = append(runConfig.Env, newVar)
 			runConfig.Env = append(runConfig.Env, newVar)
 		}
 		}
 	}
 	}
-	return d.builder.commit(d.state, commitMessage.String())
+	return d.builder.commit(ctx, d.state, commitMessage.String())
 }
 }
 
 
 // MAINTAINER some text <maybe@an.email.address>
 // MAINTAINER some text <maybe@an.email.address>
 //
 //
 // Sets the maintainer metadata.
 // Sets the maintainer metadata.
-func dispatchMaintainer(d dispatchRequest, c *instructions.MaintainerCommand) error {
+func dispatchMaintainer(ctx context.Context, d dispatchRequest, c *instructions.MaintainerCommand) error {
 	d.state.maintainer = c.Maintainer
 	d.state.maintainer = c.Maintainer
-	return d.builder.commit(d.state, "MAINTAINER "+c.Maintainer)
+	return d.builder.commit(ctx, d.state, "MAINTAINER "+c.Maintainer)
 }
 }
 
 
 // LABEL some json data describing the image
 // LABEL some json data describing the image
 //
 //
 // Sets the Label variable foo to bar,
 // Sets the Label variable foo to bar,
-func dispatchLabel(d dispatchRequest, c *instructions.LabelCommand) error {
+func dispatchLabel(ctx context.Context, d dispatchRequest, c *instructions.LabelCommand) error {
 	if d.state.runConfig.Labels == nil {
 	if d.state.runConfig.Labels == nil {
 		d.state.runConfig.Labels = make(map[string]string)
 		d.state.runConfig.Labels = make(map[string]string)
 	}
 	}
@@ -80,14 +81,14 @@ func dispatchLabel(d dispatchRequest, c *instructions.LabelCommand) error {
 		d.state.runConfig.Labels[v.Key] = v.Value
 		d.state.runConfig.Labels[v.Key] = v.Value
 		commitStr += " " + v.String()
 		commitStr += " " + v.String()
 	}
 	}
-	return d.builder.commit(d.state, commitStr)
+	return d.builder.commit(ctx, d.state, commitStr)
 }
 }
 
 
 // ADD foo /path
 // ADD foo /path
 //
 //
 // Add the file 'foo' to '/path'. Tarball and Remote URL (http, https) handling
 // Add the file 'foo' to '/path'. Tarball and Remote URL (http, https) handling
 // exist here. If you do not wish to have this automatic handling, use COPY.
 // exist here. If you do not wish to have this automatic handling, use COPY.
-func dispatchAdd(d dispatchRequest, c *instructions.AddCommand) error {
+func dispatchAdd(ctx context.Context, d dispatchRequest, c *instructions.AddCommand) error {
 	if c.Chmod != "" {
 	if c.Chmod != "" {
 		return errors.New("the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled")
 		return errors.New("the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled")
 	}
 	}
@@ -102,20 +103,20 @@ func dispatchAdd(d dispatchRequest, c *instructions.AddCommand) error {
 	copyInstruction.chownStr = c.Chown
 	copyInstruction.chownStr = c.Chown
 	copyInstruction.allowLocalDecompression = true
 	copyInstruction.allowLocalDecompression = true
 
 
-	return d.builder.performCopy(d, copyInstruction)
+	return d.builder.performCopy(ctx, d, copyInstruction)
 }
 }
 
 
 // COPY foo /path
 // COPY foo /path
 //
 //
 // Same as 'ADD' but without the tar and remote url handling.
 // Same as 'ADD' but without the tar and remote url handling.
-func dispatchCopy(d dispatchRequest, c *instructions.CopyCommand) error {
+func dispatchCopy(ctx context.Context, d dispatchRequest, c *instructions.CopyCommand) error {
 	if c.Chmod != "" {
 	if c.Chmod != "" {
 		return errors.New("the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled")
 		return errors.New("the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled")
 	}
 	}
 	var im *imageMount
 	var im *imageMount
 	var err error
 	var err error
 	if c.From != "" {
 	if c.From != "" {
-		im, err = d.getImageMount(c.From)
+		im, err = d.getImageMount(ctx, c.From)
 		if err != nil {
 		if err != nil {
 			return errors.Wrapf(err, "invalid from flag value %s", c.From)
 			return errors.Wrapf(err, "invalid from flag value %s", c.From)
 		}
 		}
@@ -130,10 +131,10 @@ func dispatchCopy(d dispatchRequest, c *instructions.CopyCommand) error {
 	if c.From != "" && copyInstruction.chownStr == "" {
 	if c.From != "" && copyInstruction.chownStr == "" {
 		copyInstruction.preserveOwnership = true
 		copyInstruction.preserveOwnership = true
 	}
 	}
-	return d.builder.performCopy(d, copyInstruction)
+	return d.builder.performCopy(ctx, d, copyInstruction)
 }
 }
 
 
-func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error) {
+func (d *dispatchRequest) getImageMount(ctx context.Context, imageRefOrID string) (*imageMount, error) {
 	if imageRefOrID == "" {
 	if imageRefOrID == "" {
 		// TODO: this could return the source in the default case as well?
 		// TODO: this could return the source in the default case as well?
 		return nil, nil
 		return nil, nil
@@ -148,12 +149,15 @@ func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error
 		imageRefOrID = stage.Image
 		imageRefOrID = stage.Image
 		localOnly = true
 		localOnly = true
 	}
 	}
-	return d.builder.imageSources.Get(imageRefOrID, localOnly, d.builder.platform)
+	return d.builder.imageSources.Get(ctx, imageRefOrID, localOnly, d.builder.platform)
 }
 }
 
 
 // FROM [--platform=platform] imagename[:tag | @digest] [AS build-stage-name]
 // FROM [--platform=platform] imagename[:tag | @digest] [AS build-stage-name]
-func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
-	d.builder.imageProber.Reset()
+func initializeStage(ctx context.Context, d dispatchRequest, cmd *instructions.Stage) error {
+	err := d.builder.imageProber.Reset(ctx)
+	if err != nil {
+		return err
+	}
 
 
 	var platform *specs.Platform
 	var platform *specs.Platform
 	if v := cmd.Platform; v != "" {
 	if v := cmd.Platform; v != "" {
@@ -169,7 +173,7 @@ func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
 		platform = &p
 		platform = &p
 	}
 	}
 
 
-	image, err := d.getFromImage(d.shlex, cmd.BaseName, platform)
+	image, err := d.getFromImage(ctx, d.shlex, cmd.BaseName, platform)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -180,12 +184,12 @@ func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
 	if len(state.runConfig.OnBuild) > 0 {
 	if len(state.runConfig.OnBuild) > 0 {
 		triggers := state.runConfig.OnBuild
 		triggers := state.runConfig.OnBuild
 		state.runConfig.OnBuild = nil
 		state.runConfig.OnBuild = nil
-		return dispatchTriggeredOnBuild(d, triggers)
+		return dispatchTriggeredOnBuild(ctx, d, triggers)
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-func dispatchTriggeredOnBuild(d dispatchRequest, triggers []string) error {
+func dispatchTriggeredOnBuild(ctx context.Context, d dispatchRequest, triggers []string) error {
 	fmt.Fprintf(d.builder.Stdout, "# Executing %d build trigger", len(triggers))
 	fmt.Fprintf(d.builder.Stdout, "# Executing %d build trigger", len(triggers))
 	if len(triggers) > 1 {
 	if len(triggers) > 1 {
 		fmt.Fprint(d.builder.Stdout, "s")
 		fmt.Fprint(d.builder.Stdout, "s")
@@ -208,7 +212,7 @@ func dispatchTriggeredOnBuild(d dispatchRequest, triggers []string) error {
 			}
 			}
 			return err
 			return err
 		}
 		}
-		err = dispatch(d, cmd)
+		err = dispatch(ctx, d, cmd)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -229,7 +233,7 @@ func (d *dispatchRequest) getExpandedString(shlex *shell.Lex, str string) (strin
 	return name, nil
 	return name, nil
 }
 }
 
 
-func (d *dispatchRequest) getImageOrStage(name string, platform *specs.Platform) (builder.Image, error) {
+func (d *dispatchRequest) getImageOrStage(ctx context.Context, name string, platform *specs.Platform) (builder.Image, error) {
 	var localOnly bool
 	var localOnly bool
 	if im, ok := d.stages.getByName(name); ok {
 	if im, ok := d.stages.getByName(name); ok {
 		name = im.Image
 		name = im.Image
@@ -256,13 +260,14 @@ func (d *dispatchRequest) getImageOrStage(name string, platform *specs.Platform)
 		}
 		}
 		return builder.Image(imageImage), nil
 		return builder.Image(imageImage), nil
 	}
 	}
-	imageMount, err := d.builder.imageSources.Get(name, localOnly, platform)
+	imageMount, err := d.builder.imageSources.Get(ctx, name, localOnly, platform)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	return imageMount.Image(), nil
 	return imageMount.Image(), nil
 }
 }
-func (d *dispatchRequest) getFromImage(shlex *shell.Lex, basename string, platform *specs.Platform) (builder.Image, error) {
+
+func (d *dispatchRequest) getFromImage(ctx context.Context, shlex *shell.Lex, basename string, platform *specs.Platform) (builder.Image, error) {
 	name, err := d.getExpandedString(shlex, basename)
 	name, err := d.getExpandedString(shlex, basename)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -273,18 +278,18 @@ func (d *dispatchRequest) getFromImage(shlex *shell.Lex, basename string, platfo
 		return nil, errors.Errorf("base name (%s) should not be blank", basename)
 		return nil, errors.Errorf("base name (%s) should not be blank", basename)
 	}
 	}
 
 
-	return d.getImageOrStage(name, platform)
+	return d.getImageOrStage(ctx, name, platform)
 }
 }
 
 
-func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error {
+func dispatchOnbuild(ctx context.Context, d dispatchRequest, c *instructions.OnbuildCommand) error {
 	d.state.runConfig.OnBuild = append(d.state.runConfig.OnBuild, c.Expression)
 	d.state.runConfig.OnBuild = append(d.state.runConfig.OnBuild, c.Expression)
-	return d.builder.commit(d.state, "ONBUILD "+c.Expression)
+	return d.builder.commit(ctx, d.state, "ONBUILD "+c.Expression)
 }
 }
 
 
 // WORKDIR /tmp
 // WORKDIR /tmp
 //
 //
 // Set the working directory for future RUN/CMD/etc statements.
 // Set the working directory for future RUN/CMD/etc statements.
-func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
+func dispatchWorkdir(ctx context.Context, d dispatchRequest, c *instructions.WorkdirCommand) error {
 	runConfig := d.state.runConfig
 	runConfig := d.state.runConfig
 	var err error
 	var err error
 	runConfig.WorkingDir, err = normalizeWorkdir(d.state.operatingSystem, runConfig.WorkingDir, c.Path)
 	runConfig.WorkingDir, err = normalizeWorkdir(d.state.operatingSystem, runConfig.WorkingDir, c.Path)
@@ -305,7 +310,7 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
 	comment := "WORKDIR " + runConfig.WorkingDir
 	comment := "WORKDIR " + runConfig.WorkingDir
 	runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, d.state.operatingSystem))
 	runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, d.state.operatingSystem))
 
 
-	containerID, err := d.builder.probeAndCreate(d.state, runConfigWithCommentCmd)
+	containerID, err := d.builder.probeAndCreate(ctx, d.state, runConfigWithCommentCmd)
 	if err != nil || containerID == "" {
 	if err != nil || containerID == "" {
 		return err
 		return err
 	}
 	}
@@ -326,7 +331,7 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
 // RUN echo hi          # sh -c echo hi       (Linux and LCOW)
 // RUN echo hi          # sh -c echo hi       (Linux and LCOW)
 // RUN echo hi          # cmd /S /C echo hi   (Windows)
 // RUN echo hi          # cmd /S /C echo hi   (Windows)
 // RUN [ "echo", "hi" ] # echo hi
 // RUN [ "echo", "hi" ] # echo hi
-func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error {
+func dispatchRun(ctx context.Context, d dispatchRequest, c *instructions.RunCommand) error {
 	if !system.IsOSSupported(d.state.operatingSystem) {
 	if !system.IsOSSupported(d.state.operatingSystem) {
 		return system.ErrNotSupportedOperatingSystem
 		return system.ErrNotSupportedOperatingSystem
 	}
 	}
@@ -360,12 +365,12 @@ func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error {
 		withEntrypointOverride(saveCmd, strslice.StrSlice{""}),
 		withEntrypointOverride(saveCmd, strslice.StrSlice{""}),
 		withoutHealthcheck())
 		withoutHealthcheck())
 
 
-	cID, err := d.builder.create(runConfig)
+	cID, err := d.builder.create(ctx, runConfig)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	if err := d.builder.containerManager.Run(d.builder.clientCtx, cID, d.builder.Stdout, d.builder.Stderr); err != nil {
+	if err := d.builder.containerManager.Run(ctx, cID, d.builder.Stdout, d.builder.Stderr); err != nil {
 		if err, ok := err.(*statusCodeError); ok {
 		if err, ok := err.(*statusCodeError); ok {
 			// TODO: change error type, because jsonmessage.JSONError assumes HTTP
 			// TODO: change error type, because jsonmessage.JSONError assumes HTTP
 			msg := fmt.Sprintf(
 			msg := fmt.Sprintf(
@@ -420,7 +425,7 @@ func prependEnvOnCmd(buildArgs *BuildArgs, buildArgVars []string, cmd strslice.S
 //
 //
 // Set the default command to run in the container (which may be empty).
 // Set the default command to run in the container (which may be empty).
 // Argument handling is the same as RUN.
 // Argument handling is the same as RUN.
-func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error {
+func dispatchCmd(ctx context.Context, d dispatchRequest, c *instructions.CmdCommand) error {
 	runConfig := d.state.runConfig
 	runConfig := d.state.runConfig
 	cmd, argsEscaped := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.operatingSystem, c.Name(), c.String())
 	cmd, argsEscaped := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.operatingSystem, c.Name(), c.String())
 
 
@@ -436,7 +441,7 @@ func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error {
 	runConfig.Cmd = cmd
 	runConfig.Cmd = cmd
 	runConfig.ArgsEscaped = argsEscaped
 	runConfig.ArgsEscaped = argsEscaped
 
 
-	if err := d.builder.commit(d.state, fmt.Sprintf("CMD %q", cmd)); err != nil {
+	if err := d.builder.commit(ctx, d.state, fmt.Sprintf("CMD %q", cmd)); err != nil {
 		return err
 		return err
 	}
 	}
 	if len(c.ShellDependantCmdLine.CmdLine) != 0 {
 	if len(c.ShellDependantCmdLine.CmdLine) != 0 {
@@ -450,7 +455,7 @@ func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error {
 //
 //
 // Set the default healthcheck command to run in the container (which may be empty).
 // Set the default healthcheck command to run in the container (which may be empty).
 // Argument handling is the same as RUN.
 // Argument handling is the same as RUN.
-func dispatchHealthcheck(d dispatchRequest, c *instructions.HealthCheckCommand) error {
+func dispatchHealthcheck(ctx context.Context, d dispatchRequest, c *instructions.HealthCheckCommand) error {
 	runConfig := d.state.runConfig
 	runConfig := d.state.runConfig
 	if runConfig.Healthcheck != nil {
 	if runConfig.Healthcheck != nil {
 		oldCmd := runConfig.Healthcheck.Test
 		oldCmd := runConfig.Healthcheck.Test
@@ -459,7 +464,7 @@ func dispatchHealthcheck(d dispatchRequest, c *instructions.HealthCheckCommand)
 		}
 		}
 	}
 	}
 	runConfig.Healthcheck = c.Health
 	runConfig.Healthcheck = c.Health
-	return d.builder.commit(d.state, fmt.Sprintf("HEALTHCHECK %q", runConfig.Healthcheck))
+	return d.builder.commit(ctx, d.state, fmt.Sprintf("HEALTHCHECK %q", runConfig.Healthcheck))
 }
 }
 
 
 // ENTRYPOINT /usr/sbin/nginx
 // ENTRYPOINT /usr/sbin/nginx
@@ -469,7 +474,7 @@ func dispatchHealthcheck(d dispatchRequest, c *instructions.HealthCheckCommand)
 //
 //
 // Handles command processing similar to CMD and RUN, only req.runConfig.Entrypoint
 // Handles command processing similar to CMD and RUN, only req.runConfig.Entrypoint
 // is initialized at newBuilder time instead of through argument parsing.
 // is initialized at newBuilder time instead of through argument parsing.
-func dispatchEntrypoint(d dispatchRequest, c *instructions.EntrypointCommand) error {
+func dispatchEntrypoint(ctx context.Context, d dispatchRequest, c *instructions.EntrypointCommand) error {
 	runConfig := d.state.runConfig
 	runConfig := d.state.runConfig
 	cmd, argsEscaped := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.operatingSystem, c.Name(), c.String())
 	cmd, argsEscaped := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.operatingSystem, c.Name(), c.String())
 
 
@@ -491,14 +496,14 @@ func dispatchEntrypoint(d dispatchRequest, c *instructions.EntrypointCommand) er
 		runConfig.Cmd = nil
 		runConfig.Cmd = nil
 	}
 	}
 
 
-	return d.builder.commit(d.state, fmt.Sprintf("ENTRYPOINT %q", runConfig.Entrypoint))
+	return d.builder.commit(ctx, d.state, fmt.Sprintf("ENTRYPOINT %q", runConfig.Entrypoint))
 }
 }
 
 
 // EXPOSE 6667/tcp 7000/tcp
 // EXPOSE 6667/tcp 7000/tcp
 //
 //
 // Expose ports for links and port mappings. This all ends up in
 // Expose ports for links and port mappings. This all ends up in
 // req.runConfig.ExposedPorts for runconfig.
 // req.runConfig.ExposedPorts for runconfig.
-func dispatchExpose(d dispatchRequest, c *instructions.ExposeCommand, envs []string) error {
+func dispatchExpose(ctx context.Context, d dispatchRequest, c *instructions.ExposeCommand, envs []string) error {
 	// custom multi word expansion
 	// custom multi word expansion
 	// expose $FOO with FOO="80 443" is expanded as EXPOSE [80,443]. This is the only command supporting word to words expansion
 	// expose $FOO with FOO="80 443" is expanded as EXPOSE [80,443]. This is the only command supporting word to words expansion
 	// so the word processing has been de-generalized
 	// so the word processing has been de-generalized
@@ -524,22 +529,22 @@ func dispatchExpose(d dispatchRequest, c *instructions.ExposeCommand, envs []str
 		d.state.runConfig.ExposedPorts[p] = struct{}{}
 		d.state.runConfig.ExposedPorts[p] = struct{}{}
 	}
 	}
 
 
-	return d.builder.commit(d.state, "EXPOSE "+strings.Join(c.Ports, " "))
+	return d.builder.commit(ctx, d.state, "EXPOSE "+strings.Join(c.Ports, " "))
 }
 }
 
 
 // USER foo
 // USER foo
 //
 //
 // Set the user to 'foo' for future commands and when running the
 // Set the user to 'foo' for future commands and when running the
 // ENTRYPOINT/CMD at container run time.
 // ENTRYPOINT/CMD at container run time.
-func dispatchUser(d dispatchRequest, c *instructions.UserCommand) error {
+func dispatchUser(ctx context.Context, d dispatchRequest, c *instructions.UserCommand) error {
 	d.state.runConfig.User = c.User
 	d.state.runConfig.User = c.User
-	return d.builder.commit(d.state, fmt.Sprintf("USER %v", c.User))
+	return d.builder.commit(ctx, d.state, fmt.Sprintf("USER %v", c.User))
 }
 }
 
 
 // VOLUME /foo
 // VOLUME /foo
 //
 //
 // Expose the volume /foo for use. Will also accept the JSON array form.
 // Expose the volume /foo for use. Will also accept the JSON array form.
-func dispatchVolume(d dispatchRequest, c *instructions.VolumeCommand) error {
+func dispatchVolume(ctx context.Context, d dispatchRequest, c *instructions.VolumeCommand) error {
 	if d.state.runConfig.Volumes == nil {
 	if d.state.runConfig.Volumes == nil {
 		d.state.runConfig.Volumes = map[string]struct{}{}
 		d.state.runConfig.Volumes = map[string]struct{}{}
 	}
 	}
@@ -549,19 +554,19 @@ func dispatchVolume(d dispatchRequest, c *instructions.VolumeCommand) error {
 		}
 		}
 		d.state.runConfig.Volumes[v] = struct{}{}
 		d.state.runConfig.Volumes[v] = struct{}{}
 	}
 	}
-	return d.builder.commit(d.state, fmt.Sprintf("VOLUME %v", c.Volumes))
+	return d.builder.commit(ctx, d.state, fmt.Sprintf("VOLUME %v", c.Volumes))
 }
 }
 
 
 // STOPSIGNAL signal
 // STOPSIGNAL signal
 //
 //
 // Set the signal that will be used to kill the container.
 // Set the signal that will be used to kill the container.
-func dispatchStopSignal(d dispatchRequest, c *instructions.StopSignalCommand) error {
+func dispatchStopSignal(ctx context.Context, d dispatchRequest, c *instructions.StopSignalCommand) error {
 	_, err := signal.ParseSignal(c.Signal)
 	_, err := signal.ParseSignal(c.Signal)
 	if err != nil {
 	if err != nil {
 		return errdefs.InvalidParameter(err)
 		return errdefs.InvalidParameter(err)
 	}
 	}
 	d.state.runConfig.StopSignal = c.Signal
 	d.state.runConfig.StopSignal = c.Signal
-	return d.builder.commit(d.state, fmt.Sprintf("STOPSIGNAL %v", c.Signal))
+	return d.builder.commit(ctx, d.state, fmt.Sprintf("STOPSIGNAL %v", c.Signal))
 }
 }
 
 
 // ARG name[=value]
 // ARG name[=value]
@@ -569,7 +574,7 @@ func dispatchStopSignal(d dispatchRequest, c *instructions.StopSignalCommand) er
 // Adds the variable foo to the trusted list of variables that can be passed
 // Adds the variable foo to the trusted list of variables that can be passed
 // to builder using the --build-arg flag for expansion/substitution or passing to 'run'.
 // to builder using the --build-arg flag for expansion/substitution or passing to 'run'.
 // Dockerfile author may optionally set a default value of this variable.
 // Dockerfile author may optionally set a default value of this variable.
-func dispatchArg(d dispatchRequest, c *instructions.ArgCommand) error {
+func dispatchArg(ctx context.Context, d dispatchRequest, c *instructions.ArgCommand) error {
 	var commitStr strings.Builder
 	var commitStr strings.Builder
 	commitStr.WriteString("ARG ")
 	commitStr.WriteString("ARG ")
 	for i, arg := range c.Args {
 	for i, arg := range c.Args {
@@ -584,13 +589,13 @@ func dispatchArg(d dispatchRequest, c *instructions.ArgCommand) error {
 		d.state.buildArgs.AddArg(arg.Key, arg.Value)
 		d.state.buildArgs.AddArg(arg.Key, arg.Value)
 	}
 	}
 
 
-	return d.builder.commit(d.state, commitStr.String())
+	return d.builder.commit(ctx, d.state, commitStr.String())
 }
 }
 
 
 // SHELL powershell -command
 // SHELL powershell -command
 //
 //
 // Set the non-default shell to use.
 // Set the non-default shell to use.
-func dispatchShell(d dispatchRequest, c *instructions.ShellCommand) error {
+func dispatchShell(ctx context.Context, d dispatchRequest, c *instructions.ShellCommand) error {
 	d.state.runConfig.Shell = c.Shell
 	d.state.runConfig.Shell = c.Shell
-	return d.builder.commit(d.state, fmt.Sprintf("SHELL %v", d.state.runConfig.Shell))
+	return d.builder.commit(ctx, d.state, fmt.Sprintf("SHELL %v", d.state.runConfig.Shell))
 }
 }

+ 71 - 60
builder/dockerfile/dispatchers_test.go

@@ -23,28 +23,32 @@ import (
 	is "gotest.tools/v3/assert/cmp"
 	is "gotest.tools/v3/assert/cmp"
 )
 )
 
 
-func newBuilderWithMockBackend() *Builder {
+func newBuilderWithMockBackend(t *testing.T) *Builder {
+	t.Helper()
 	mockBackend := &MockBackend{}
 	mockBackend := &MockBackend{}
 	opts := &types.ImageBuildOptions{}
 	opts := &types.ImageBuildOptions{}
 	ctx := context.Background()
 	ctx := context.Background()
+
+	imageProber, err := newImageProber(ctx, mockBackend, nil, false)
+	assert.NilError(t, err, "Could not create image prober")
+
 	b := &Builder{
 	b := &Builder{
 		options:       opts,
 		options:       opts,
 		docker:        mockBackend,
 		docker:        mockBackend,
 		Stdout:        new(bytes.Buffer),
 		Stdout:        new(bytes.Buffer),
-		clientCtx:     ctx,
 		disableCommit: true,
 		disableCommit: true,
-		imageSources: newImageSources(ctx, builderOptions{
+		imageSources: newImageSources(builderOptions{
 			Options: opts,
 			Options: opts,
 			Backend: mockBackend,
 			Backend: mockBackend,
 		}),
 		}),
-		imageProber:      newImageProber(mockBackend, nil, false),
+		imageProber:      imageProber,
 		containerManager: newContainerManager(mockBackend),
 		containerManager: newContainerManager(mockBackend),
 	}
 	}
 	return b
 	return b
 }
 }
 
 
 func TestEnv2Variables(t *testing.T) {
 func TestEnv2Variables(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	envCommand := &instructions.EnvCommand{
 	envCommand := &instructions.EnvCommand{
 		Env: instructions.KeyValuePairs{
 		Env: instructions.KeyValuePairs{
@@ -52,7 +56,7 @@ func TestEnv2Variables(t *testing.T) {
 			instructions.KeyValuePair{Key: "var2", Value: "val2"},
 			instructions.KeyValuePair{Key: "var2", Value: "val2"},
 		},
 		},
 	}
 	}
-	err := dispatch(sb, envCommand)
+	err := dispatch(context.TODO(), sb, envCommand)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	expected := []string{
 	expected := []string{
@@ -63,7 +67,7 @@ func TestEnv2Variables(t *testing.T) {
 }
 }
 
 
 func TestEnvValueWithExistingRunConfigEnv(t *testing.T) {
 func TestEnvValueWithExistingRunConfigEnv(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb.state.runConfig.Env = []string{"var1=old", "var2=fromenv"}
 	sb.state.runConfig.Env = []string{"var1=old", "var2=fromenv"}
 	envCommand := &instructions.EnvCommand{
 	envCommand := &instructions.EnvCommand{
@@ -71,7 +75,7 @@ func TestEnvValueWithExistingRunConfigEnv(t *testing.T) {
 			instructions.KeyValuePair{Key: "var1", Value: "val1"},
 			instructions.KeyValuePair{Key: "var1", Value: "val1"},
 		},
 		},
 	}
 	}
-	err := dispatch(sb, envCommand)
+	err := dispatch(context.TODO(), sb, envCommand)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	expected := []string{
 	expected := []string{
 		"var1=val1",
 		"var1=val1",
@@ -82,10 +86,10 @@ func TestEnvValueWithExistingRunConfigEnv(t *testing.T) {
 
 
 func TestMaintainer(t *testing.T) {
 func TestMaintainer(t *testing.T) {
 	maintainerEntry := "Some Maintainer <maintainer@example.com>"
 	maintainerEntry := "Some Maintainer <maintainer@example.com>"
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	cmd := &instructions.MaintainerCommand{Maintainer: maintainerEntry}
 	cmd := &instructions.MaintainerCommand{Maintainer: maintainerEntry}
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(maintainerEntry, sb.state.maintainer))
 	assert.Check(t, is.Equal(maintainerEntry, sb.state.maintainer))
 }
 }
@@ -94,14 +98,14 @@ func TestLabel(t *testing.T) {
 	labelName := "label"
 	labelName := "label"
 	labelValue := "value"
 	labelValue := "value"
 
 
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	cmd := &instructions.LabelCommand{
 	cmd := &instructions.LabelCommand{
 		Labels: instructions.KeyValuePairs{
 		Labels: instructions.KeyValuePairs{
 			instructions.KeyValuePair{Key: labelName, Value: labelValue},
 			instructions.KeyValuePair{Key: labelName, Value: labelValue},
 		},
 		},
 	}
 	}
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	assert.Assert(t, is.Contains(sb.state.runConfig.Labels, labelName))
 	assert.Assert(t, is.Contains(sb.state.runConfig.Labels, labelName))
@@ -109,12 +113,12 @@ func TestLabel(t *testing.T) {
 }
 }
 
 
 func TestFromScratch(t *testing.T) {
 func TestFromScratch(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	cmd := &instructions.Stage{
 	cmd := &instructions.Stage{
 		BaseName: "scratch",
 		BaseName: "scratch",
 	}
 	}
-	err := initializeStage(sb, cmd)
+	err := initializeStage(context.TODO(), sb, cmd)
 
 
 	if runtime.GOOS == "windows" {
 	if runtime.GOOS == "windows" {
 		assert.Check(t, is.Error(err, "Windows does not support FROM scratch"))
 		assert.Check(t, is.Error(err, "Windows does not support FROM scratch"))
@@ -135,7 +139,7 @@ func TestFromWithArg(t *testing.T) {
 		assert.Check(t, is.Equal("alpine"+tag, name))
 		assert.Check(t, is.Equal("alpine"+tag, name))
 		return &mockImage{id: "expectedthisid"}, nil, nil
 		return &mockImage{id: "expectedthisid"}, nil, nil
 	}
 	}
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	b.docker.(*MockBackend).getImageFunc = getImage
 	b.docker.(*MockBackend).getImageFunc = getImage
 	args := NewBuildArgs(make(map[string]*string))
 	args := NewBuildArgs(make(map[string]*string))
 
 
@@ -151,7 +155,7 @@ func TestFromWithArg(t *testing.T) {
 
 
 	sb := newDispatchRequest(b, '\\', nil, args, newStagesBuildResults())
 	sb := newDispatchRequest(b, '\\', nil, args, newStagesBuildResults())
 	assert.NilError(t, err)
 	assert.NilError(t, err)
-	err = initializeStage(sb, cmd)
+	err = initializeStage(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	assert.Check(t, is.Equal(expected, sb.state.imageID))
 	assert.Check(t, is.Equal(expected, sb.state.imageID))
@@ -161,7 +165,7 @@ func TestFromWithArg(t *testing.T) {
 }
 }
 
 
 func TestFromWithArgButBuildArgsNotGiven(t *testing.T) {
 func TestFromWithArgButBuildArgsNotGiven(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	args := NewBuildArgs(make(map[string]*string))
 	args := NewBuildArgs(make(map[string]*string))
 
 
 	metaArg := instructions.ArgCommand{}
 	metaArg := instructions.ArgCommand{}
@@ -172,7 +176,7 @@ func TestFromWithArgButBuildArgsNotGiven(t *testing.T) {
 
 
 	sb := newDispatchRequest(b, '\\', nil, args, newStagesBuildResults())
 	sb := newDispatchRequest(b, '\\', nil, args, newStagesBuildResults())
 	assert.NilError(t, err)
 	assert.NilError(t, err)
-	err = initializeStage(sb, cmd)
+	err = initializeStage(context.TODO(), sb, cmd)
 	assert.Error(t, err, "base name (${THETAG}) should not be blank")
 	assert.Error(t, err, "base name (${THETAG}) should not be blank")
 }
 }
 
 
@@ -183,7 +187,7 @@ func TestFromWithUndefinedArg(t *testing.T) {
 		assert.Check(t, is.Equal("alpine", name))
 		assert.Check(t, is.Equal("alpine", name))
 		return &mockImage{id: "expectedthisid"}, nil, nil
 		return &mockImage{id: "expectedthisid"}, nil, nil
 	}
 	}
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	b.docker.(*MockBackend).getImageFunc = getImage
 	b.docker.(*MockBackend).getImageFunc = getImage
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 
 
@@ -192,41 +196,41 @@ func TestFromWithUndefinedArg(t *testing.T) {
 	cmd := &instructions.Stage{
 	cmd := &instructions.Stage{
 		BaseName: "alpine${THETAG}",
 		BaseName: "alpine${THETAG}",
 	}
 	}
-	err := initializeStage(sb, cmd)
+	err := initializeStage(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(expected, sb.state.imageID))
 	assert.Check(t, is.Equal(expected, sb.state.imageID))
 }
 }
 
 
 func TestFromMultiStageWithNamedStage(t *testing.T) {
 func TestFromMultiStageWithNamedStage(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	firstFrom := &instructions.Stage{BaseName: "someimg", Name: "base"}
 	firstFrom := &instructions.Stage{BaseName: "someimg", Name: "base"}
 	secondFrom := &instructions.Stage{BaseName: "base"}
 	secondFrom := &instructions.Stage{BaseName: "base"}
 	previousResults := newStagesBuildResults()
 	previousResults := newStagesBuildResults()
 	firstSB := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), previousResults)
 	firstSB := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), previousResults)
 	secondSB := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), previousResults)
 	secondSB := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), previousResults)
-	err := initializeStage(firstSB, firstFrom)
+	err := initializeStage(context.TODO(), firstSB, firstFrom)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Check(t, firstSB.state.hasFromImage())
 	assert.Check(t, firstSB.state.hasFromImage())
 	previousResults.indexed["base"] = firstSB.state.runConfig
 	previousResults.indexed["base"] = firstSB.state.runConfig
 	previousResults.flat = append(previousResults.flat, firstSB.state.runConfig)
 	previousResults.flat = append(previousResults.flat, firstSB.state.runConfig)
-	err = initializeStage(secondSB, secondFrom)
+	err = initializeStage(context.TODO(), secondSB, secondFrom)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Check(t, secondSB.state.hasFromImage())
 	assert.Check(t, secondSB.state.hasFromImage())
 }
 }
 
 
 func TestOnbuild(t *testing.T) {
 func TestOnbuild(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	cmd := &instructions.OnbuildCommand{
 	cmd := &instructions.OnbuildCommand{
 		Expression: "ADD . /app/src",
 		Expression: "ADD . /app/src",
 	}
 	}
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal("ADD . /app/src", sb.state.runConfig.OnBuild[0]))
 	assert.Check(t, is.Equal("ADD . /app/src", sb.state.runConfig.OnBuild[0]))
 }
 }
 
 
 func TestWorkdir(t *testing.T) {
 func TestWorkdir(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb.state.baseImage = &mockImage{}
 	sb.state.baseImage = &mockImage{}
 	workingDir := "/app"
 	workingDir := "/app"
@@ -237,13 +241,13 @@ func TestWorkdir(t *testing.T) {
 		Path: workingDir,
 		Path: workingDir,
 	}
 	}
 
 
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(workingDir, sb.state.runConfig.WorkingDir))
 	assert.Check(t, is.Equal(workingDir, sb.state.runConfig.WorkingDir))
 }
 }
 
 
 func TestCmd(t *testing.T) {
 func TestCmd(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb.state.baseImage = &mockImage{}
 	sb.state.baseImage = &mockImage{}
 	command := "./executable"
 	command := "./executable"
@@ -254,7 +258,7 @@ func TestCmd(t *testing.T) {
 			PrependShell: true,
 			PrependShell: true,
 		},
 		},
 	}
 	}
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	var expectedCommand strslice.StrSlice
 	var expectedCommand strslice.StrSlice
@@ -269,14 +273,14 @@ func TestCmd(t *testing.T) {
 }
 }
 
 
 func TestHealthcheckNone(t *testing.T) {
 func TestHealthcheckNone(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	cmd := &instructions.HealthCheckCommand{
 	cmd := &instructions.HealthCheckCommand{
 		Health: &container.HealthConfig{
 		Health: &container.HealthConfig{
 			Test: []string{"NONE"},
 			Test: []string{"NONE"},
 		},
 		},
 	}
 	}
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	assert.Assert(t, sb.state.runConfig.Healthcheck != nil)
 	assert.Assert(t, sb.state.runConfig.Healthcheck != nil)
@@ -284,7 +288,7 @@ func TestHealthcheckNone(t *testing.T) {
 }
 }
 
 
 func TestHealthcheckCmd(t *testing.T) {
 func TestHealthcheckCmd(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	expectedTest := []string{"CMD-SHELL", "curl -f http://localhost/ || exit 1"}
 	expectedTest := []string{"CMD-SHELL", "curl -f http://localhost/ || exit 1"}
 	cmd := &instructions.HealthCheckCommand{
 	cmd := &instructions.HealthCheckCommand{
@@ -292,7 +296,7 @@ func TestHealthcheckCmd(t *testing.T) {
 			Test: expectedTest,
 			Test: expectedTest,
 		},
 		},
 	}
 	}
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	assert.Assert(t, sb.state.runConfig.Healthcheck != nil)
 	assert.Assert(t, sb.state.runConfig.Healthcheck != nil)
@@ -300,7 +304,7 @@ func TestHealthcheckCmd(t *testing.T) {
 }
 }
 
 
 func TestEntrypoint(t *testing.T) {
 func TestEntrypoint(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb.state.baseImage = &mockImage{}
 	sb.state.baseImage = &mockImage{}
 	entrypointCmd := "/usr/sbin/nginx"
 	entrypointCmd := "/usr/sbin/nginx"
@@ -311,7 +315,7 @@ func TestEntrypoint(t *testing.T) {
 			PrependShell: true,
 			PrependShell: true,
 		},
 		},
 	}
 	}
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Assert(t, sb.state.runConfig.Entrypoint != nil)
 	assert.Assert(t, sb.state.runConfig.Entrypoint != nil)
 
 
@@ -325,14 +329,14 @@ func TestEntrypoint(t *testing.T) {
 }
 }
 
 
 func TestExpose(t *testing.T) {
 func TestExpose(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 
 
 	exposedPort := "80"
 	exposedPort := "80"
 	cmd := &instructions.ExposeCommand{
 	cmd := &instructions.ExposeCommand{
 		Ports: []string{exposedPort},
 		Ports: []string{exposedPort},
 	}
 	}
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	assert.Assert(t, sb.state.runConfig.ExposedPorts != nil)
 	assert.Assert(t, sb.state.runConfig.ExposedPorts != nil)
@@ -344,19 +348,19 @@ func TestExpose(t *testing.T) {
 }
 }
 
 
 func TestUser(t *testing.T) {
 func TestUser(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 
 
 	cmd := &instructions.UserCommand{
 	cmd := &instructions.UserCommand{
 		User: "test",
 		User: "test",
 	}
 	}
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal("test", sb.state.runConfig.User))
 	assert.Check(t, is.Equal("test", sb.state.runConfig.User))
 }
 }
 
 
 func TestVolume(t *testing.T) {
 func TestVolume(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 
 
 	exposedVolume := "/foo"
 	exposedVolume := "/foo"
@@ -364,7 +368,7 @@ func TestVolume(t *testing.T) {
 	cmd := &instructions.VolumeCommand{
 	cmd := &instructions.VolumeCommand{
 		Volumes: []string{exposedVolume},
 		Volumes: []string{exposedVolume},
 	}
 	}
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Assert(t, sb.state.runConfig.Volumes != nil)
 	assert.Assert(t, sb.state.runConfig.Volumes != nil)
 	assert.Check(t, is.Len(sb.state.runConfig.Volumes, 1))
 	assert.Check(t, is.Len(sb.state.runConfig.Volumes, 1))
@@ -376,7 +380,7 @@ func TestStopSignal(t *testing.T) {
 		t.Skip("Windows does not support stopsignal")
 		t.Skip("Windows does not support stopsignal")
 		return
 		return
 	}
 	}
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb.state.baseImage = &mockImage{}
 	sb.state.baseImage = &mockImage{}
 	signal := "SIGKILL"
 	signal := "SIGKILL"
@@ -384,19 +388,19 @@ func TestStopSignal(t *testing.T) {
 	cmd := &instructions.StopSignalCommand{
 	cmd := &instructions.StopSignalCommand{
 		Signal: signal,
 		Signal: signal,
 	}
 	}
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(signal, sb.state.runConfig.StopSignal))
 	assert.Check(t, is.Equal(signal, sb.state.runConfig.StopSignal))
 }
 }
 
 
 func TestArg(t *testing.T) {
 func TestArg(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 
 
 	argName := "foo"
 	argName := "foo"
 	argVal := "bar"
 	argVal := "bar"
 	cmd := &instructions.ArgCommand{Args: []instructions.KeyValuePairOptional{{Key: argName, Value: &argVal}}}
 	cmd := &instructions.ArgCommand{Args: []instructions.KeyValuePairOptional{{Key: argName, Value: &argVal}}}
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	expected := map[string]string{argName: argVal}
 	expected := map[string]string{argName: argVal}
@@ -404,13 +408,13 @@ func TestArg(t *testing.T) {
 }
 }
 
 
 func TestShell(t *testing.T) {
 func TestShell(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 
 
 	shellCmd := "powershell"
 	shellCmd := "powershell"
 	cmd := &instructions.ShellCommand{Shell: strslice.StrSlice{shellCmd}}
 	cmd := &instructions.ShellCommand{Shell: strslice.StrSlice{shellCmd}}
 
 
-	err := dispatch(sb, cmd)
+	err := dispatch(context.TODO(), sb, cmd)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	expectedShell := strslice.StrSlice([]string{shellCmd})
 	expectedShell := strslice.StrSlice([]string{shellCmd})
@@ -430,7 +434,7 @@ func TestPrependEnvOnCmd(t *testing.T) {
 }
 }
 
 
 func TestRunWithBuildArgs(t *testing.T) {
 func TestRunWithBuildArgs(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	args := NewBuildArgs(make(map[string]*string))
 	args := NewBuildArgs(make(map[string]*string))
 	args.argsFromOptions["HTTP_PROXY"] = strPtr("FOO")
 	args.argsFromOptions["HTTP_PROXY"] = strPtr("FOO")
 	b.disableCommit = false
 	b.disableCommit = false
@@ -462,7 +466,11 @@ func TestRunWithBuildArgs(t *testing.T) {
 	mockBackend.makeImageCacheFunc = func(_ []string) builder.ImageCache {
 	mockBackend.makeImageCacheFunc = func(_ []string) builder.ImageCache {
 		return imageCache
 		return imageCache
 	}
 	}
-	b.imageProber = newImageProber(mockBackend, nil, false)
+
+	imageProber, err := newImageProber(context.TODO(), mockBackend, nil, false)
+	assert.NilError(t, err, "Could not create image prober")
+	b.imageProber = imageProber
+
 	mockBackend.getImageFunc = func(_ string) (builder.Image, builder.ROLayer, error) {
 	mockBackend.getImageFunc = func(_ string) (builder.Image, builder.ROLayer, error) {
 		return &mockImage{
 		return &mockImage{
 			id:     "abcdef",
 			id:     "abcdef",
@@ -484,7 +492,7 @@ func TestRunWithBuildArgs(t *testing.T) {
 		return "", nil
 		return "", nil
 	}
 	}
 	from := &instructions.Stage{BaseName: "abcdef"}
 	from := &instructions.Stage{BaseName: "abcdef"}
-	err := initializeStage(sb, from)
+	err = initializeStage(context.TODO(), sb, from)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	sb.state.buildArgs.AddArg("one", strPtr("two"))
 	sb.state.buildArgs.AddArg("one", strPtr("two"))
 
 
@@ -504,14 +512,14 @@ func TestRunWithBuildArgs(t *testing.T) {
 	runinst.CmdLine = strslice.StrSlice{"echo foo"}
 	runinst.CmdLine = strslice.StrSlice{"echo foo"}
 	runinst.PrependShell = true
 	runinst.PrependShell = true
 
 
-	assert.NilError(t, dispatch(sb, runinst))
+	assert.NilError(t, dispatch(context.TODO(), sb, runinst))
 
 
 	// Check that runConfig.Cmd has not been modified by run
 	// Check that runConfig.Cmd has not been modified by run
 	assert.Check(t, is.DeepEqual(origCmd, sb.state.runConfig.Cmd))
 	assert.Check(t, is.DeepEqual(origCmd, sb.state.runConfig.Cmd))
 }
 }
 
 
 func TestRunIgnoresHealthcheck(t *testing.T) {
 func TestRunIgnoresHealthcheck(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	args := NewBuildArgs(make(map[string]*string))
 	args := NewBuildArgs(make(map[string]*string))
 	sb := newDispatchRequest(b, '`', nil, args, newStagesBuildResults())
 	sb := newDispatchRequest(b, '`', nil, args, newStagesBuildResults())
 	b.disableCommit = false
 	b.disableCommit = false
@@ -528,7 +536,10 @@ func TestRunIgnoresHealthcheck(t *testing.T) {
 	mockBackend.makeImageCacheFunc = func(_ []string) builder.ImageCache {
 	mockBackend.makeImageCacheFunc = func(_ []string) builder.ImageCache {
 		return imageCache
 		return imageCache
 	}
 	}
-	b.imageProber = newImageProber(mockBackend, nil, false)
+	imageProber, err := newImageProber(context.TODO(), mockBackend, nil, false)
+	assert.NilError(t, err, "Could not create image prober")
+
+	b.imageProber = imageProber
 	mockBackend.getImageFunc = func(_ string) (builder.Image, builder.ROLayer, error) {
 	mockBackend.getImageFunc = func(_ string) (builder.Image, builder.ROLayer, error) {
 		return &mockImage{
 		return &mockImage{
 			id:     "abcdef",
 			id:     "abcdef",
@@ -542,7 +553,7 @@ func TestRunIgnoresHealthcheck(t *testing.T) {
 		return "", nil
 		return "", nil
 	}
 	}
 	from := &instructions.Stage{BaseName: "abcdef"}
 	from := &instructions.Stage{BaseName: "abcdef"}
-	err := initializeStage(sb, from)
+	err = initializeStage(context.TODO(), sb, from)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	expectedTest := []string{"CMD-SHELL", "curl -f http://localhost/ || exit 1"}
 	expectedTest := []string{"CMD-SHELL", "curl -f http://localhost/ || exit 1"}
@@ -559,7 +570,7 @@ func TestRunIgnoresHealthcheck(t *testing.T) {
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	cmd := healthint.(*instructions.HealthCheckCommand)
 	cmd := healthint.(*instructions.HealthCheckCommand)
 
 
-	assert.NilError(t, dispatch(sb, cmd))
+	assert.NilError(t, dispatch(context.TODO(), sb, cmd))
 	assert.Assert(t, sb.state.runConfig.Healthcheck != nil)
 	assert.Assert(t, sb.state.runConfig.Healthcheck != nil)
 
 
 	mockBackend.containerCreateFunc = func(config types.ContainerCreateConfig) (container.CreateResponse, error) {
 	mockBackend.containerCreateFunc = func(config types.ContainerCreateConfig) (container.CreateResponse, error) {
@@ -574,12 +585,12 @@ func TestRunIgnoresHealthcheck(t *testing.T) {
 	run := runint.(*instructions.RunCommand)
 	run := runint.(*instructions.RunCommand)
 	run.PrependShell = true
 	run.PrependShell = true
 
 
-	assert.NilError(t, dispatch(sb, run))
+	assert.NilError(t, dispatch(context.TODO(), sb, run))
 	assert.Check(t, is.DeepEqual(expectedTest, sb.state.runConfig.Healthcheck.Test))
 	assert.Check(t, is.DeepEqual(expectedTest, sb.state.runConfig.Healthcheck.Test))
 }
 }
 
 
 func TestDispatchUnsupportedOptions(t *testing.T) {
 func TestDispatchUnsupportedOptions(t *testing.T) {
-	b := newBuilderWithMockBackend()
+	b := newBuilderWithMockBackend(t)
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 	sb.state.baseImage = &mockImage{}
 	sb.state.baseImage = &mockImage{}
 	sb.state.operatingSystem = runtime.GOOS
 	sb.state.operatingSystem = runtime.GOOS
@@ -592,7 +603,7 @@ func TestDispatchUnsupportedOptions(t *testing.T) {
 			},
 			},
 			Chmod: "0655",
 			Chmod: "0655",
 		}
 		}
-		err := dispatch(sb, cmd)
+		err := dispatch(context.TODO(), sb, cmd)
 		assert.Error(t, err, "the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled")
 		assert.Error(t, err, "the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled")
 	})
 	})
 
 
@@ -604,7 +615,7 @@ func TestDispatchUnsupportedOptions(t *testing.T) {
 			},
 			},
 			Chmod: "0655",
 			Chmod: "0655",
 		}
 		}
-		err := dispatch(sb, cmd)
+		err := dispatch(context.TODO(), sb, cmd)
 		assert.Error(t, err, "the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled")
 		assert.Error(t, err, "the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled")
 	})
 	})
 
 
@@ -618,7 +629,7 @@ func TestDispatchUnsupportedOptions(t *testing.T) {
 		// one or more of these flags will be supported in future
 		// one or more of these flags will be supported in future
 		for _, f := range []string{"mount", "network", "security", "any-flag"} {
 		for _, f := range []string{"mount", "network", "security", "any-flag"} {
 			cmd.FlagsUsed = []string{f}
 			cmd.FlagsUsed = []string{f}
-			err := dispatch(sb, cmd)
+			err := dispatch(context.TODO(), sb, cmd)
 			assert.Error(t, err, fmt.Sprintf("the --%s option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled", f))
 			assert.Error(t, err, fmt.Sprintf("the --%s option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled", f))
 		}
 		}
 	})
 	})

+ 19 - 18
builder/dockerfile/evaluator.go

@@ -20,6 +20,7 @@
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 
 
 import (
 import (
+	"context"
 	"reflect"
 	"reflect"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
@@ -34,7 +35,7 @@ import (
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
-func dispatch(d dispatchRequest, cmd instructions.Command) (err error) {
+func dispatch(ctx context.Context, d dispatchRequest, cmd instructions.Command) (err error) {
 	if c, ok := cmd.(instructions.PlatformSpecific); ok {
 	if c, ok := cmd.(instructions.PlatformSpecific); ok {
 		err := c.CheckPlatform(d.state.operatingSystem)
 		err := c.CheckPlatform(d.state.operatingSystem)
 		if err != nil {
 		if err != nil {
@@ -65,39 +66,39 @@ func dispatch(d dispatchRequest, cmd instructions.Command) (err error) {
 	}()
 	}()
 	switch c := cmd.(type) {
 	switch c := cmd.(type) {
 	case *instructions.EnvCommand:
 	case *instructions.EnvCommand:
-		return dispatchEnv(d, c)
+		return dispatchEnv(ctx, d, c)
 	case *instructions.MaintainerCommand:
 	case *instructions.MaintainerCommand:
-		return dispatchMaintainer(d, c)
+		return dispatchMaintainer(ctx, d, c)
 	case *instructions.LabelCommand:
 	case *instructions.LabelCommand:
-		return dispatchLabel(d, c)
+		return dispatchLabel(ctx, d, c)
 	case *instructions.AddCommand:
 	case *instructions.AddCommand:
-		return dispatchAdd(d, c)
+		return dispatchAdd(ctx, d, c)
 	case *instructions.CopyCommand:
 	case *instructions.CopyCommand:
-		return dispatchCopy(d, c)
+		return dispatchCopy(ctx, d, c)
 	case *instructions.OnbuildCommand:
 	case *instructions.OnbuildCommand:
-		return dispatchOnbuild(d, c)
+		return dispatchOnbuild(ctx, d, c)
 	case *instructions.WorkdirCommand:
 	case *instructions.WorkdirCommand:
-		return dispatchWorkdir(d, c)
+		return dispatchWorkdir(ctx, d, c)
 	case *instructions.RunCommand:
 	case *instructions.RunCommand:
-		return dispatchRun(d, c)
+		return dispatchRun(ctx, d, c)
 	case *instructions.CmdCommand:
 	case *instructions.CmdCommand:
-		return dispatchCmd(d, c)
+		return dispatchCmd(ctx, d, c)
 	case *instructions.HealthCheckCommand:
 	case *instructions.HealthCheckCommand:
-		return dispatchHealthcheck(d, c)
+		return dispatchHealthcheck(ctx, d, c)
 	case *instructions.EntrypointCommand:
 	case *instructions.EntrypointCommand:
-		return dispatchEntrypoint(d, c)
+		return dispatchEntrypoint(ctx, d, c)
 	case *instructions.ExposeCommand:
 	case *instructions.ExposeCommand:
-		return dispatchExpose(d, c, envs)
+		return dispatchExpose(ctx, d, c, envs)
 	case *instructions.UserCommand:
 	case *instructions.UserCommand:
-		return dispatchUser(d, c)
+		return dispatchUser(ctx, d, c)
 	case *instructions.VolumeCommand:
 	case *instructions.VolumeCommand:
-		return dispatchVolume(d, c)
+		return dispatchVolume(ctx, d, c)
 	case *instructions.StopSignalCommand:
 	case *instructions.StopSignalCommand:
-		return dispatchStopSignal(d, c)
+		return dispatchStopSignal(ctx, d, c)
 	case *instructions.ArgCommand:
 	case *instructions.ArgCommand:
-		return dispatchArg(d, c)
+		return dispatchArg(ctx, d, c)
 	case *instructions.ShellCommand:
 	case *instructions.ShellCommand:
-		return dispatchShell(d, c)
+		return dispatchShell(ctx, d, c)
 	}
 	}
 	return errors.Errorf("unsupported command type: %v", reflect.TypeOf(cmd))
 	return errors.Errorf("unsupported command type: %v", reflect.TypeOf(cmd))
 }
 }

+ 3 - 2
builder/dockerfile/evaluator_test.go

@@ -1,6 +1,7 @@
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 
 
 import (
 import (
+	"context"
 	"os"
 	"os"
 	"runtime"
 	"runtime"
 	"testing"
 	"testing"
@@ -127,9 +128,9 @@ func TestDispatch(t *testing.T) {
 				}
 				}
 			}()
 			}()
 
 
-			b := newBuilderWithMockBackend()
+			b := newBuilderWithMockBackend(t)
 			sb := newDispatchRequest(b, '`', buildContext, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
 			sb := newDispatchRequest(b, '`', buildContext, NewBuildArgs(make(map[string]*string)), newStagesBuildResults())
-			err = dispatch(sb, tc.cmd)
+			err = dispatch(context.TODO(), sb, tc.cmd)
 			assert.Check(t, is.ErrorContains(err, tc.expectedError))
 			assert.Check(t, is.ErrorContains(err, tc.expectedError))
 		})
 		})
 	}
 	}

+ 5 - 5
builder/dockerfile/imagecontext.go

@@ -13,7 +13,7 @@ import (
 	"github.com/sirupsen/logrus"
 	"github.com/sirupsen/logrus"
 )
 )
 
 
-type getAndMountFunc func(string, bool, *specs.Platform) (builder.Image, builder.ROLayer, error)
+type getAndMountFunc func(context.Context, string, bool, *specs.Platform) (builder.Image, builder.ROLayer, error)
 
 
 // imageSources mounts images and provides a cache for mounted images. It tracks
 // imageSources mounts images and provides a cache for mounted images. It tracks
 // all images so they can be unmounted at the end of the build.
 // all images so they can be unmounted at the end of the build.
@@ -23,8 +23,8 @@ type imageSources struct {
 	getImage  getAndMountFunc
 	getImage  getAndMountFunc
 }
 }
 
 
-func newImageSources(ctx context.Context, options builderOptions) *imageSources {
-	getAndMount := func(idOrRef string, localOnly bool, platform *specs.Platform) (builder.Image, builder.ROLayer, error) {
+func newImageSources(options builderOptions) *imageSources {
+	getAndMount := func(ctx context.Context, idOrRef string, localOnly bool, platform *specs.Platform) (builder.Image, builder.ROLayer, error) {
 		pullOption := backend.PullOptionNoPull
 		pullOption := backend.PullOptionNoPull
 		if !localOnly {
 		if !localOnly {
 			if options.Options.PullParent {
 			if options.Options.PullParent {
@@ -47,12 +47,12 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources
 	}
 	}
 }
 }
 
 
-func (m *imageSources) Get(idOrRef string, localOnly bool, platform *specs.Platform) (*imageMount, error) {
+func (m *imageSources) Get(ctx context.Context, idOrRef string, localOnly bool, platform *specs.Platform) (*imageMount, error) {
 	if im, ok := m.byImageID[idOrRef]; ok {
 	if im, ok := m.byImageID[idOrRef]; ok {
 		return im, nil
 		return im, nil
 	}
 	}
 
 
-	image, layer, err := m.getImage(idOrRef, localOnly, platform)
+	image, layer, err := m.getImage(ctx, idOrRef, localOnly, platform)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}

+ 4 - 2
builder/dockerfile/imagecontext_test.go

@@ -1,6 +1,7 @@
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
 	"runtime"
 	"runtime"
 	"testing"
 	"testing"
@@ -16,7 +17,7 @@ func getMockImageSource(getImageImage builder.Image, getImageLayer builder.ROLay
 	return &imageSources{
 	return &imageSources{
 		byImageID: make(map[string]*imageMount),
 		byImageID: make(map[string]*imageMount),
 		mounts:    []*imageMount{},
 		mounts:    []*imageMount{},
-		getImage: func(name string, localOnly bool, platform *ocispec.Platform) (builder.Image, builder.ROLayer, error) {
+		getImage: func(_ context.Context, name string, localOnly bool, platform *ocispec.Platform) (builder.Image, builder.ROLayer, error) {
 			return getImageImage, getImageLayer, getImageError
 			return getImageImage, getImageLayer, getImageError
 		},
 		},
 	}
 	}
@@ -100,7 +101,8 @@ func TestAddFromScratchPopulatesPlatformIfNil(t *testing.T) {
 
 
 func TestImageSourceGetAddsToMounts(t *testing.T) {
 func TestImageSourceGetAddsToMounts(t *testing.T) {
 	is := getMockImageSource(nil, nil, nil)
 	is := getMockImageSource(nil, nil, nil)
-	_, err := is.Get("test", false, nil)
+	ctx := context.Background()
+	_, err := is.Get(ctx, "test", false, nil)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Equal(t, len(is.mounts), 1)
 	assert.Equal(t, len(is.mounts), 1)
 }
 }

+ 26 - 10
builder/dockerfile/imageprobe.go

@@ -1,6 +1,8 @@
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 
 
 import (
 import (
+	"context"
+
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder"
 	"github.com/sirupsen/logrus"
 	"github.com/sirupsen/logrus"
@@ -9,30 +11,42 @@ import (
 // ImageProber exposes an Image cache to the Builder. It supports resetting a
 // ImageProber exposes an Image cache to the Builder. It supports resetting a
 // cache.
 // cache.
 type ImageProber interface {
 type ImageProber interface {
-	Reset()
+	Reset(ctx context.Context) error
 	Probe(parentID string, runConfig *container.Config) (string, error)
 	Probe(parentID string, runConfig *container.Config) (string, error)
 }
 }
 
 
+type resetFunc func(context.Context) (builder.ImageCache, error)
+
 type imageProber struct {
 type imageProber struct {
 	cache       builder.ImageCache
 	cache       builder.ImageCache
-	reset       func() builder.ImageCache
+	reset       resetFunc
 	cacheBusted bool
 	cacheBusted bool
 }
 }
 
 
-func newImageProber(cacheBuilder builder.ImageCacheBuilder, cacheFrom []string, noCache bool) ImageProber {
+func newImageProber(ctx context.Context, cacheBuilder builder.ImageCacheBuilder, cacheFrom []string, noCache bool) (ImageProber, error) {
 	if noCache {
 	if noCache {
-		return &nopProber{}
+		return &nopProber{}, nil
+	}
+
+	reset := func(ctx context.Context) (builder.ImageCache, error) {
+		return cacheBuilder.MakeImageCache(ctx, cacheFrom)
 	}
 	}
 
 
-	reset := func() builder.ImageCache {
-		return cacheBuilder.MakeImageCache(cacheFrom)
+	cache, err := reset(ctx)
+	if err != nil {
+		return nil, err
 	}
 	}
-	return &imageProber{cache: reset(), reset: reset}
+	return &imageProber{cache: cache, reset: reset}, nil
 }
 }
 
 
-func (c *imageProber) Reset() {
-	c.cache = c.reset()
+func (c *imageProber) Reset(ctx context.Context) error {
+	newCache, err := c.reset(ctx)
+	if err != nil {
+		return err
+	}
+	c.cache = newCache
 	c.cacheBusted = false
 	c.cacheBusted = false
+	return nil
 }
 }
 
 
 // Probe checks if cache match can be found for current build instruction.
 // Probe checks if cache match can be found for current build instruction.
@@ -56,7 +70,9 @@ func (c *imageProber) Probe(parentID string, runConfig *container.Config) (strin
 
 
 type nopProber struct{}
 type nopProber struct{}
 
 
-func (c *nopProber) Reset() {}
+func (c *nopProber) Reset(ctx context.Context) error {
+	return nil
+}
 
 
 func (c *nopProber) Probe(_ string, _ *container.Config) (string, error) {
 func (c *nopProber) Probe(_ string, _ *container.Config) (string, error) {
 	return "", nil
 	return "", nil

+ 10 - 9
builder/dockerfile/internals.go

@@ -4,6 +4,7 @@ package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 // non-contiguous functionality. Please read the comments.
 // non-contiguous functionality. Please read the comments.
 
 
 import (
 import (
+	"context"
 	"crypto/sha256"
 	"crypto/sha256"
 	"encoding/hex"
 	"encoding/hex"
 	"fmt"
 	"fmt"
@@ -27,7 +28,7 @@ func (b *Builder) getArchiver() *archive.Archiver {
 	return chrootarchive.NewArchiver(b.idMapping)
 	return chrootarchive.NewArchiver(b.idMapping)
 }
 }
 
 
-func (b *Builder) commit(dispatchState *dispatchState, comment string) error {
+func (b *Builder) commit(ctx context.Context, dispatchState *dispatchState, comment string) error {
 	if b.disableCommit {
 	if b.disableCommit {
 		return nil
 		return nil
 	}
 	}
@@ -36,7 +37,7 @@ func (b *Builder) commit(dispatchState *dispatchState, comment string) error {
 	}
 	}
 
 
 	runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, dispatchState.operatingSystem))
 	runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, dispatchState.operatingSystem))
-	id, err := b.probeAndCreate(dispatchState, runConfigWithCommentCmd)
+	id, err := b.probeAndCreate(ctx, dispatchState, runConfigWithCommentCmd)
 	if err != nil || id == "" {
 	if err != nil || id == "" {
 		return err
 		return err
 	}
 	}
@@ -107,7 +108,7 @@ func (b *Builder) exportImage(state *dispatchState, layer builder.RWLayer, paren
 	return nil
 	return nil
 }
 }
 
 
-func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
+func (b *Builder) performCopy(ctx context.Context, req dispatchRequest, inst copyInstruction) error {
 	state := req.state
 	state := req.state
 	srcHash := getSourceHashFromInfos(inst.infos)
 	srcHash := getSourceHashFromInfos(inst.infos)
 
 
@@ -126,7 +127,7 @@ func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
 		return err
 		return err
 	}
 	}
 
 
-	imageMount, err := b.imageSources.Get(state.imageID, true, req.builder.platform)
+	imageMount, err := b.imageSources.Get(ctx, state.imageID, true, req.builder.platform)
 	if err != nil {
 	if err != nil {
 		return errors.Wrapf(err, "failed to get destination image %q", state.imageID)
 		return errors.Wrapf(err, "failed to get destination image %q", state.imageID)
 	}
 	}
@@ -147,7 +148,7 @@ func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
 	// translated (if necessary because of user namespaces), and replace
 	// translated (if necessary because of user namespaces), and replace
 	// the root pair with the chown pair for copy operations
 	// the root pair with the chown pair for copy operations
 	if inst.chownStr != "" {
 	if inst.chownStr != "" {
-		identity, err = parseChownFlag(b, state, inst.chownStr, destInfo.root, b.idMapping)
+		identity, err = parseChownFlag(ctx, b, state, inst.chownStr, destInfo.root, b.idMapping)
 		if err != nil {
 		if err != nil {
 			if b.options.Platform != "windows" {
 			if b.options.Platform != "windows" {
 				return errors.Wrapf(err, "unable to convert uid/gid chown string to host mapping")
 				return errors.Wrapf(err, "unable to convert uid/gid chown string to host mapping")
@@ -331,18 +332,18 @@ func (b *Builder) probeCache(dispatchState *dispatchState, runConfig *container.
 
 
 var defaultLogConfig = container.LogConfig{Type: "none"}
 var defaultLogConfig = container.LogConfig{Type: "none"}
 
 
-func (b *Builder) probeAndCreate(dispatchState *dispatchState, runConfig *container.Config) (string, error) {
+func (b *Builder) probeAndCreate(ctx context.Context, dispatchState *dispatchState, runConfig *container.Config) (string, error) {
 	if hit, err := b.probeCache(dispatchState, runConfig); err != nil || hit {
 	if hit, err := b.probeCache(dispatchState, runConfig); err != nil || hit {
 		return "", err
 		return "", err
 	}
 	}
-	return b.create(runConfig)
+	return b.create(ctx, runConfig)
 }
 }
 
 
-func (b *Builder) create(runConfig *container.Config) (string, error) {
+func (b *Builder) create(ctx context.Context, runConfig *container.Config) (string, error) {
 	logrus.Debugf("[BUILDER] Command to be executed: %v", runConfig.Cmd)
 	logrus.Debugf("[BUILDER] Command to be executed: %v", runConfig.Cmd)
 
 
 	hostConfig := hostConfigFromOptions(b.options)
 	hostConfig := hostConfigFromOptions(b.options)
-	container, err := b.containerManager.Create(runConfig, hostConfig)
+	container, err := b.containerManager.Create(ctx, runConfig, hostConfig)
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}

+ 2 - 1
builder/dockerfile/internals_linux.go

@@ -1,6 +1,7 @@
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 
 
 import (
 import (
+	"context"
 	"path/filepath"
 	"path/filepath"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
@@ -11,7 +12,7 @@ import (
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
-func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping idtools.IdentityMapping) (idtools.Identity, error) {
+func parseChownFlag(ctx context.Context, builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping idtools.IdentityMapping) (idtools.Identity, error) {
 	var userStr, grpStr string
 	var userStr, grpStr string
 	parts := strings.Split(chown, ":")
 	parts := strings.Split(chown, ":")
 	if len(parts) > 2 {
 	if len(parts) > 2 {

+ 3 - 2
builder/dockerfile/internals_linux_test.go

@@ -1,6 +1,7 @@
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 
 
 import (
 import (
+	"context"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	"testing"
 	"testing"
@@ -115,7 +116,7 @@ othergrp:x:6666:
 		},
 		},
 	} {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 		t.Run(testcase.name, func(t *testing.T) {
-			idPair, err := parseChownFlag(testcase.builder, testcase.state, testcase.chownStr, contextDir, testcase.idMapping)
+			idPair, err := parseChownFlag(context.TODO(), testcase.builder, testcase.state, testcase.chownStr, contextDir, testcase.idMapping)
 			assert.NilError(t, err, "Failed to parse chown flag: %q", testcase.chownStr)
 			assert.NilError(t, err, "Failed to parse chown flag: %q", testcase.chownStr)
 			assert.Check(t, is.DeepEqual(testcase.expected, idPair), "chown flag mapping failure")
 			assert.Check(t, is.DeepEqual(testcase.expected, idPair), "chown flag mapping failure")
 		})
 		})
@@ -156,7 +157,7 @@ othergrp:x:6666:
 		},
 		},
 	} {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 		t.Run(testcase.name, func(t *testing.T) {
-			_, err := parseChownFlag(testcase.builder, testcase.state, testcase.chownStr, contextDir, testcase.idMapping)
+			_, err := parseChownFlag(context.TODO(), testcase.builder, testcase.state, testcase.chownStr, contextDir, testcase.idMapping)
 			assert.Check(t, is.Error(err, testcase.descr), "Expected error string doesn't match")
 			assert.Check(t, is.Error(err, testcase.descr), "Expected error string doesn't match")
 		})
 		})
 	}
 	}

+ 8 - 7
builder/dockerfile/internals_windows.go

@@ -2,6 +2,7 @@ package dockerfile // import "github.com/docker/docker/builder/dockerfile"
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"context"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	"strings"
 	"strings"
@@ -14,15 +15,15 @@ import (
 	"golang.org/x/sys/windows"
 	"golang.org/x/sys/windows"
 )
 )
 
 
-func parseChownFlag(builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping idtools.IdentityMapping) (idtools.Identity, error) {
+func parseChownFlag(ctx context.Context, builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping idtools.IdentityMapping) (idtools.Identity, error) {
 	if builder.options.Platform == "windows" {
 	if builder.options.Platform == "windows" {
-		return getAccountIdentity(builder, chown, ctrRootPath, state)
+		return getAccountIdentity(ctx, builder, chown, ctrRootPath, state)
 	}
 	}
 
 
 	return identityMapping.RootPair(), nil
 	return identityMapping.RootPair(), nil
 }
 }
 
 
-func getAccountIdentity(builder *Builder, accountName string, ctrRootPath string, state *dispatchState) (idtools.Identity, error) {
+func getAccountIdentity(ctx context.Context, builder *Builder, accountName string, ctrRootPath string, state *dispatchState) (idtools.Identity, error) {
 	// If this is potentially a string SID then attempt to convert it to verify
 	// If this is potentially a string SID then attempt to convert it to verify
 	// this, otherwise continue looking for the account.
 	// this, otherwise continue looking for the account.
 	if strings.HasPrefix(accountName, "S-") || strings.HasPrefix(accountName, "s-") {
 	if strings.HasPrefix(accountName, "S-") || strings.HasPrefix(accountName, "s-") {
@@ -51,10 +52,10 @@ func getAccountIdentity(builder *Builder, accountName string, ctrRootPath string
 
 
 	// All other lookups failed, so therefore determine if the account in
 	// All other lookups failed, so therefore determine if the account in
 	// question exists in the container and if so, obtain its SID.
 	// question exists in the container and if so, obtain its SID.
-	return lookupNTAccount(builder, accountName, state)
+	return lookupNTAccount(ctx, builder, accountName, state)
 }
 }
 
 
-func lookupNTAccount(builder *Builder, accountName string, state *dispatchState) (idtools.Identity, error) {
+func lookupNTAccount(ctx context.Context, builder *Builder, accountName string, state *dispatchState) (idtools.Identity, error) {
 
 
 	source, _ := filepath.Split(os.Args[0])
 	source, _ := filepath.Split(os.Args[0])
 
 
@@ -81,7 +82,7 @@ func lookupNTAccount(builder *Builder, accountName string, state *dispatchState)
 	},
 	},
 	}
 	}
 
 
-	container, err := builder.containerManager.Create(runConfig, hostConfig)
+	container, err := builder.containerManager.Create(ctx, runConfig, hostConfig)
 	if err != nil {
 	if err != nil {
 		return idtools.Identity{}, err
 		return idtools.Identity{}, err
 	}
 	}
@@ -89,7 +90,7 @@ func lookupNTAccount(builder *Builder, accountName string, state *dispatchState)
 	stdout := new(bytes.Buffer)
 	stdout := new(bytes.Buffer)
 	stderr := new(bytes.Buffer)
 	stderr := new(bytes.Buffer)
 
 
-	if err := builder.containerManager.Run(builder.clientCtx, container.ID, stdout, stderr); err != nil {
+	if err := builder.containerManager.Run(ctx, container.ID, stdout, stderr); err != nil {
 		if err, ok := err.(*statusCodeError); ok {
 		if err, ok := err.(*statusCodeError); ok {
 			return idtools.Identity{}, &jsonmessage.JSONError{
 			return idtools.Identity{}, &jsonmessage.JSONError{
 				Message: stderr.String(),
 				Message: stderr.String(),

+ 5 - 5
builder/dockerfile/mockbackend_test.go

@@ -27,7 +27,7 @@ func (m *MockBackend) ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout
 	return nil
 	return nil
 }
 }
 
 
-func (m *MockBackend) ContainerCreateIgnoreImagesArgsEscaped(config types.ContainerCreateConfig) (container.CreateResponse, error) {
+func (m *MockBackend) ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context, config types.ContainerCreateConfig) (container.CreateResponse, error) {
 	if m.containerCreateFunc != nil {
 	if m.containerCreateFunc != nil {
 		return m.containerCreateFunc(config)
 		return m.containerCreateFunc(config)
 	}
 	}
@@ -49,7 +49,7 @@ func (m *MockBackend) ContainerKill(containerID string, sig string) error {
 	return nil
 	return nil
 }
 }
 
 
-func (m *MockBackend) ContainerStart(containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error {
+func (m *MockBackend) ContainerStart(ctx context.Context, containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error {
 	return nil
 	return nil
 }
 }
 
 
@@ -73,11 +73,11 @@ func (m *MockBackend) GetImageAndReleasableLayer(ctx context.Context, refOrID st
 	return &mockImage{id: "theid"}, &mockLayer{}, nil
 	return &mockImage{id: "theid"}, &mockLayer{}, nil
 }
 }
 
 
-func (m *MockBackend) MakeImageCache(cacheFrom []string) builder.ImageCache {
+func (m *MockBackend) MakeImageCache(ctx context.Context, cacheFrom []string) (builder.ImageCache, error) {
 	if m.makeImageCacheFunc != nil {
 	if m.makeImageCacheFunc != nil {
-		return m.makeImageCacheFunc(cacheFrom)
+		return m.makeImageCacheFunc(cacheFrom), nil
 	}
 	}
-	return nil
+	return nil, nil
 }
 }
 
 
 func (m *MockBackend) CreateImage(config []byte, parent string) (builder.Image, error) {
 func (m *MockBackend) CreateImage(config []byte, parent string) (builder.Image, error) {

+ 15 - 18
cmd/dockerd/daemon.go

@@ -252,7 +252,7 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
 
 
 	// notify systemd that we're shutting down
 	// notify systemd that we're shutting down
 	notifyStopping()
 	notifyStopping()
-	shutdownDaemon(d)
+	shutdownDaemon(ctx, d)
 
 
 	// Stop notification processing and any background processes
 	// Stop notification processing and any background processes
 	cancel()
 	cancel()
@@ -359,27 +359,24 @@ func (cli *DaemonCli) stop() {
 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
 // d.Shutdown() is waiting too long to kill container or worst it's
 // d.Shutdown() is waiting too long to kill container or worst it's
 // blocked there
 // blocked there
-func shutdownDaemon(d *daemon.Daemon) {
-	shutdownTimeout := d.ShutdownTimeout()
-	ch := make(chan struct{})
-	go func() {
-		d.Shutdown()
-		close(ch)
-	}()
-	if shutdownTimeout < 0 {
-		<-ch
-		logrus.Debug("Clean shutdown succeeded")
-		return
+func shutdownDaemon(ctx context.Context, d *daemon.Daemon) {
+	var cancel context.CancelFunc
+	if timeout := d.ShutdownTimeout(); timeout >= 0 {
+		ctx, cancel = context.WithTimeout(ctx, time.Duration(timeout)*time.Second)
+	} else {
+		ctx, cancel = context.WithCancel(ctx)
 	}
 	}
 
 
-	timeout := time.NewTimer(time.Duration(shutdownTimeout) * time.Second)
-	defer timeout.Stop()
+	go func() {
+		defer cancel()
+		d.Shutdown(ctx)
+	}()
 
 
-	select {
-	case <-ch:
-		logrus.Debug("Clean shutdown succeeded")
-	case <-timeout.C:
+	<-ctx.Done()
+	if errors.Is(ctx.Err(), context.DeadlineExceeded) {
 		logrus.Error("Force shutdown daemon")
 		logrus.Error("Force shutdown daemon")
+	} else {
+		logrus.Debug("Clean shutdown succeeded")
 	}
 	}
 }
 }
 
 

+ 3 - 3
daemon/cluster/executor/backend.go

@@ -37,8 +37,8 @@ type Backend interface {
 	FindNetwork(idName string) (libnetwork.Network, error)
 	FindNetwork(idName string) (libnetwork.Network, error)
 	SetupIngress(clustertypes.NetworkCreateRequest, string) (<-chan struct{}, error)
 	SetupIngress(clustertypes.NetworkCreateRequest, string) (<-chan struct{}, error)
 	ReleaseIngress() (<-chan struct{}, error)
 	ReleaseIngress() (<-chan struct{}, error)
-	CreateManagedContainer(config types.ContainerCreateConfig) (container.CreateResponse, error)
-	ContainerStart(name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
+	CreateManagedContainer(ctx context.Context, config types.ContainerCreateConfig) (container.CreateResponse, error)
+	ContainerStart(ctx context.Context, name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
 	ContainerStop(ctx context.Context, name string, config container.StopOptions) error
 	ContainerStop(ctx context.Context, name string, config container.StopOptions) error
 	ContainerLogs(ctx context.Context, name string, config *types.ContainerLogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error)
 	ContainerLogs(ctx context.Context, name string, config *types.ContainerLogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error)
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
@@ -53,7 +53,7 @@ type Backend interface {
 	SetContainerSecretReferences(name string, refs []*swarm.SecretReference) error
 	SetContainerSecretReferences(name string, refs []*swarm.SecretReference) error
 	SetContainerConfigReferences(name string, refs []*swarm.ConfigReference) error
 	SetContainerConfigReferences(name string, refs []*swarm.ConfigReference) error
 	SystemInfo() *types.Info
 	SystemInfo() *types.Info
-	Containers(config *types.ContainerListOptions) ([]*types.Container, error)
+	Containers(ctx context.Context, config *types.ContainerListOptions) ([]*types.Container, error)
 	SetNetworkBootstrapKeys([]*networktypes.EncryptionKey) error
 	SetNetworkBootstrapKeys([]*networktypes.EncryptionKey) error
 	DaemonJoinsCluster(provider cluster.Provider)
 	DaemonJoinsCluster(provider cluster.Provider)
 	DaemonLeavesCluster()
 	DaemonLeavesCluster()

+ 2 - 2
daemon/cluster/executor/container/adapter.go

@@ -290,7 +290,7 @@ func (c *containerAdapter) waitForDetach(ctx context.Context) error {
 func (c *containerAdapter) create(ctx context.Context) error {
 func (c *containerAdapter) create(ctx context.Context) error {
 	var cr containertypes.CreateResponse
 	var cr containertypes.CreateResponse
 	var err error
 	var err error
-	if cr, err = c.backend.CreateManagedContainer(types.ContainerCreateConfig{
+	if cr, err = c.backend.CreateManagedContainer(ctx, types.ContainerCreateConfig{
 		Name:       c.container.name(),
 		Name:       c.container.name(),
 		Config:     c.container.config(),
 		Config:     c.container.config(),
 		HostConfig: c.container.hostConfig(c.dependencies.Volumes()),
 		HostConfig: c.container.hostConfig(c.dependencies.Volumes()),
@@ -357,7 +357,7 @@ func (c *containerAdapter) start(ctx context.Context) error {
 		return err
 		return err
 	}
 	}
 
 
-	return c.backend.ContainerStart(c.container.name(), nil, "", "")
+	return c.backend.ContainerStart(ctx, c.container.name(), nil, "", "")
 }
 }
 
 
 func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, error) {
 func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, error) {

+ 4 - 4
daemon/cluster/swarm.go

@@ -356,7 +356,7 @@ func (c *Cluster) UnlockSwarm(req types.UnlockRequest) error {
 }
 }
 
 
 // Leave shuts down Cluster and removes current state.
 // Leave shuts down Cluster and removes current state.
-func (c *Cluster) Leave(force bool) error {
+func (c *Cluster) Leave(ctx context.Context, force bool) error {
 	c.controlMutex.Lock()
 	c.controlMutex.Lock()
 	defer c.controlMutex.Unlock()
 	defer c.controlMutex.Unlock()
 
 
@@ -408,7 +408,7 @@ func (c *Cluster) Leave(force bool) error {
 	c.mu.Unlock()
 	c.mu.Unlock()
 
 
 	if nodeID := state.NodeID(); nodeID != "" {
 	if nodeID := state.NodeID(); nodeID != "" {
-		nodeContainers, err := c.listContainerForNode(nodeID)
+		nodeContainers, err := c.listContainerForNode(ctx, nodeID)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -604,11 +604,11 @@ func initClusterSpec(node *swarmnode.Node, spec types.Spec) error {
 	return ctx.Err()
 	return ctx.Err()
 }
 }
 
 
-func (c *Cluster) listContainerForNode(nodeID string) ([]string, error) {
+func (c *Cluster) listContainerForNode(ctx context.Context, nodeID string) ([]string, error) {
 	var ids []string
 	var ids []string
 	filters := filters.NewArgs()
 	filters := filters.NewArgs()
 	filters.Add("label", fmt.Sprintf("com.docker.swarm.node.id=%s", nodeID))
 	filters.Add("label", fmt.Sprintf("com.docker.swarm.node.id=%s", nodeID))
-	containers, err := c.config.Backend.Containers(&apitypes.ContainerListOptions{
+	containers, err := c.config.Backend.Containers(ctx, &apitypes.ContainerListOptions{
 		Filters: filters,
 		Filters: filters,
 	})
 	})
 	if err != nil {
 	if err != nil {

+ 3 - 2
daemon/commit.go

@@ -1,6 +1,7 @@
 package daemon // import "github.com/docker/docker/daemon"
 package daemon // import "github.com/docker/docker/daemon"
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
 	"runtime"
 	"runtime"
 	"strings"
 	"strings"
@@ -116,7 +117,7 @@ func merge(userConf, imageConf *containertypes.Config) error {
 // CreateImageFromContainer creates a new image from a container. The container
 // CreateImageFromContainer creates a new image from a container. The container
 // config will be updated by applying the change set to the custom config, then
 // config will be updated by applying the change set to the custom config, then
 // applying that config over the existing container config.
 // applying that config over the existing container config.
-func (daemon *Daemon) CreateImageFromContainer(name string, c *backend.CreateImageConfig) (string, error) {
+func (daemon *Daemon) CreateImageFromContainer(ctx context.Context, name string, c *backend.CreateImageConfig) (string, error) {
 	start := time.Now()
 	start := time.Now()
 	container, err := daemon.GetContainer(name)
 	container, err := daemon.GetContainer(name)
 	if err != nil {
 	if err != nil {
@@ -146,7 +147,7 @@ func (daemon *Daemon) CreateImageFromContainer(name string, c *backend.CreateIma
 	if c.Config == nil {
 	if c.Config == nil {
 		c.Config = container.Config
 		c.Config = container.Config
 	}
 	}
-	newConfig, err := dockerfile.BuildFromConfig(c.Config, c.Changes, container.OS)
+	newConfig, err := dockerfile.BuildFromConfig(ctx, c.Config, c.Changes, container.OS)
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}

+ 3 - 1
daemon/containerd/cache.go

@@ -1,10 +1,12 @@
 package containerd
 package containerd
 
 
 import (
 import (
+	"context"
+
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder"
 )
 )
 
 
 // MakeImageCache creates a stateful image cache.
 // MakeImageCache creates a stateful image cache.
-func (i *ImageService) MakeImageCache(cacheFrom []string) builder.ImageCache {
+func (i *ImageService) MakeImageCache(ctx context.Context, cacheFrom []string) (builder.ImageCache, error) {
 	panic("not implemented")
 	panic("not implemented")
 }
 }

+ 2 - 1
daemon/containerd/image_import.go

@@ -1,6 +1,7 @@
 package containerd
 package containerd
 
 
 import (
 import (
+	"context"
 	"errors"
 	"errors"
 	"io"
 	"io"
 
 
@@ -12,6 +13,6 @@ import (
 // inConfig (if src is "-"), or from a URI specified in src. Progress output is
 // inConfig (if src is "-"), or from a URI specified in src. Progress output is
 // written to outStream. Repository and tag names can optionally be given in
 // written to outStream. Repository and tag names can optionally be given in
 // the repo and tag arguments, respectively.
 // the repo and tag arguments, respectively.
-func (i *ImageService) ImportImage(src string, repository string, platform *specs.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error {
+func (i *ImageService) ImportImage(ctx context.Context, src string, repository string, platform *specs.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error {
 	return errdefs.NotImplemented(errors.New("not implemented"))
 	return errdefs.NotImplemented(errors.New("not implemented"))
 }
 }

+ 9 - 11
daemon/create.go

@@ -33,31 +33,30 @@ type createOpts struct {
 }
 }
 
 
 // CreateManagedContainer creates a container that is managed by a Service
 // CreateManagedContainer creates a container that is managed by a Service
-func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
-	return daemon.containerCreate(createOpts{
+func (daemon *Daemon) CreateManagedContainer(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
+	return daemon.containerCreate(ctx, createOpts{
 		params:  params,
 		params:  params,
 		managed: true,
 		managed: true,
 	})
 	})
 }
 }
 
 
 // ContainerCreate creates a regular container
 // ContainerCreate creates a regular container
-func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
-	return daemon.containerCreate(createOpts{
+func (daemon *Daemon) ContainerCreate(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
+	return daemon.containerCreate(ctx, createOpts{
 		params: params,
 		params: params,
 	})
 	})
 }
 }
 
 
 // ContainerCreateIgnoreImagesArgsEscaped creates a regular container. This is called from the builder RUN case
 // ContainerCreateIgnoreImagesArgsEscaped creates a regular container. This is called from the builder RUN case
 // and ensures that we do not take the images ArgsEscaped
 // and ensures that we do not take the images ArgsEscaped
-func (daemon *Daemon) ContainerCreateIgnoreImagesArgsEscaped(params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
-	return daemon.containerCreate(createOpts{
+func (daemon *Daemon) ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context, params types.ContainerCreateConfig) (containertypes.CreateResponse, error) {
+	return daemon.containerCreate(ctx, createOpts{
 		params:                  params,
 		params:                  params,
 		ignoreImagesArgsEscaped: true,
 		ignoreImagesArgsEscaped: true,
 	})
 	})
 }
 }
 
 
-func (daemon *Daemon) containerCreate(opts createOpts) (containertypes.CreateResponse, error) {
-	ctx := context.TODO()
+func (daemon *Daemon) containerCreate(ctx context.Context, opts createOpts) (containertypes.CreateResponse, error) {
 	start := time.Now()
 	start := time.Now()
 	if opts.params.Config == nil {
 	if opts.params.Config == nil {
 		return containertypes.CreateResponse{}, errdefs.InvalidParameter(errors.New("Config cannot be empty in order to create a container"))
 		return containertypes.CreateResponse{}, errdefs.InvalidParameter(errors.New("Config cannot be empty in order to create a container"))
@@ -100,7 +99,7 @@ func (daemon *Daemon) containerCreate(opts createOpts) (containertypes.CreateRes
 		return containertypes.CreateResponse{Warnings: warnings}, errdefs.InvalidParameter(err)
 		return containertypes.CreateResponse{Warnings: warnings}, errdefs.InvalidParameter(err)
 	}
 	}
 
 
-	ctr, err := daemon.create(opts)
+	ctr, err := daemon.create(ctx, opts)
 	if err != nil {
 	if err != nil {
 		return containertypes.CreateResponse{Warnings: warnings}, err
 		return containertypes.CreateResponse{Warnings: warnings}, err
 	}
 	}
@@ -114,8 +113,7 @@ func (daemon *Daemon) containerCreate(opts createOpts) (containertypes.CreateRes
 }
 }
 
 
 // Create creates a new container from the given configuration with a given name.
 // Create creates a new container from the given configuration with a given name.
-func (daemon *Daemon) create(opts createOpts) (retC *container.Container, retErr error) {
-	ctx := context.TODO()
+func (daemon *Daemon) create(ctx context.Context, opts createOpts) (retC *container.Container, retErr error) {
 	var (
 	var (
 		ctr   *container.Container
 		ctr   *container.Container
 		img   *image.Image
 		img   *image.Image

+ 7 - 6
daemon/daemon.go

@@ -526,7 +526,7 @@ func (daemon *Daemon) restore() error {
 			if err := daemon.prepareMountPoints(c); err != nil {
 			if err := daemon.prepareMountPoints(c); err != nil {
 				log.WithError(err).Error("failed to prepare mount points for container")
 				log.WithError(err).Error("failed to prepare mount points for container")
 			}
 			}
-			if err := daemon.containerStart(c, "", "", true); err != nil {
+			if err := daemon.containerStart(context.Background(), c, "", "", true); err != nil {
 				log.WithError(err).Error("failed to start container")
 				log.WithError(err).Error("failed to start container")
 			}
 			}
 			close(chNotify)
 			close(chNotify)
@@ -617,7 +617,7 @@ func (daemon *Daemon) RestartSwarmContainers() {
 						return
 						return
 					}
 					}
 
 
-					if err := daemon.containerStart(c, "", "", true); err != nil {
+					if err := daemon.containerStart(ctx, c, "", "", true); err != nil {
 						logrus.WithField("container", c.ID).WithError(err).Error("failed to start swarm container")
 						logrus.WithField("container", c.ID).WithError(err).Error("failed to start swarm container")
 					}
 					}
 
 
@@ -775,7 +775,8 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 	// initialization
 	// initialization
 	defer func() {
 	defer func() {
 		if err != nil {
 		if err != nil {
-			if err := d.Shutdown(); err != nil {
+			// Use a fresh context here. Passed context could be cancelled.
+			if err := d.Shutdown(context.Background()); err != nil {
 				logrus.Error(err)
 				logrus.Error(err)
 			}
 			}
 		}
 		}
@@ -1193,17 +1194,17 @@ func (daemon *Daemon) ShutdownTimeout() int {
 }
 }
 
 
 // Shutdown stops the daemon.
 // Shutdown stops the daemon.
-func (daemon *Daemon) Shutdown() error {
+func (daemon *Daemon) Shutdown(ctx context.Context) error {
 	daemon.shutdown = true
 	daemon.shutdown = true
 	// Keep mounts and networking running on daemon shutdown if
 	// Keep mounts and networking running on daemon shutdown if
 	// we are to keep containers running and restore them.
 	// we are to keep containers running and restore them.
 
 
 	if daemon.configStore.LiveRestoreEnabled && daemon.containers != nil {
 	if daemon.configStore.LiveRestoreEnabled && daemon.containers != nil {
 		// check if there are any running containers, if none we should do some cleanup
 		// check if there are any running containers, if none we should do some cleanup
-		if ls, err := daemon.Containers(&types.ContainerListOptions{}); len(ls) != 0 || err != nil {
+		if ls, err := daemon.Containers(ctx, &types.ContainerListOptions{}); len(ls) != 0 || err != nil {
 			// metrics plugins still need some cleanup
 			// metrics plugins still need some cleanup
 			daemon.cleanupMetricsPlugins()
 			daemon.cleanupMetricsPlugins()
-			return nil
+			return err
 		}
 		}
 	}
 	}
 
 

+ 1 - 1
daemon/disk_usage.go

@@ -14,7 +14,7 @@ import (
 func (daemon *Daemon) ContainerDiskUsage(ctx context.Context) ([]*types.Container, error) {
 func (daemon *Daemon) ContainerDiskUsage(ctx context.Context) ([]*types.Container, error) {
 	ch := daemon.usage.DoChan("ContainerDiskUsage", func() (interface{}, error) {
 	ch := daemon.usage.DoChan("ContainerDiskUsage", func() (interface{}, error) {
 		// Retrieve container list
 		// Retrieve container list
-		containers, err := daemon.Containers(&types.ContainerListOptions{
+		containers, err := daemon.Containers(context.TODO(), &types.ContainerListOptions{
 			Size: true,
 			Size: true,
 			All:  true,
 			All:  true,
 		})
 		})

+ 2 - 2
daemon/image_service.go

@@ -37,7 +37,7 @@ type ImageService interface {
 	CountImages() int
 	CountImages() int
 	ImageDiskUsage(ctx context.Context) ([]*types.ImageSummary, error)
 	ImageDiskUsage(ctx context.Context) ([]*types.ImageSummary, error)
 	ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error)
 	ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error)
-	ImportImage(src string, repository string, platform *v1.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error
+	ImportImage(ctx context.Context, src string, repository string, platform *v1.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error
 	TagImage(imageName, repository, tag string) (string, error)
 	TagImage(imageName, repository, tag string) (string, error)
 	TagImageWithReference(imageID image.ID, newTag reference.Named) error
 	TagImageWithReference(imageID image.ID, newTag reference.Named) error
 	GetImage(ctx context.Context, refOrID string, options imagetype.GetImageOpts) (*image.Image, error)
 	GetImage(ctx context.Context, refOrID string, options imagetype.GetImageOpts) (*image.Image, error)
@@ -62,7 +62,7 @@ type ImageService interface {
 
 
 	// Build
 	// Build
 
 
-	MakeImageCache(sourceRefs []string) builder.ImageCache
+	MakeImageCache(ctx context.Context, cacheFrom []string) (builder.ImageCache, error)
 	CommitBuildStep(c backend.CommitConfig) (image.ID, error)
 	CommitBuildStep(c backend.CommitConfig) (image.ID, error)
 
 
 	// Other
 	// Other

+ 7 - 4
daemon/images/cache.go

@@ -6,14 +6,14 @@ import (
 	imagetypes "github.com/docker/docker/api/types/image"
 	imagetypes "github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/image/cache"
 	"github.com/docker/docker/image/cache"
+	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 	"github.com/sirupsen/logrus"
 )
 )
 
 
 // MakeImageCache creates a stateful image cache.
 // MakeImageCache creates a stateful image cache.
-func (i *ImageService) MakeImageCache(sourceRefs []string) builder.ImageCache {
-	ctx := context.TODO()
+func (i *ImageService) MakeImageCache(ctx context.Context, sourceRefs []string) (builder.ImageCache, error) {
 	if len(sourceRefs) == 0 {
 	if len(sourceRefs) == 0 {
-		return cache.NewLocal(i.imageStore)
+		return cache.NewLocal(i.imageStore), nil
 	}
 	}
 
 
 	cache := cache.New(i.imageStore)
 	cache := cache.New(i.imageStore)
@@ -21,11 +21,14 @@ func (i *ImageService) MakeImageCache(sourceRefs []string) builder.ImageCache {
 	for _, ref := range sourceRefs {
 	for _, ref := range sourceRefs {
 		img, err := i.GetImage(ctx, ref, imagetypes.GetImageOpts{})
 		img, err := i.GetImage(ctx, ref, imagetypes.GetImageOpts{})
 		if err != nil {
 		if err != nil {
+			if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
+				return nil, err
+			}
 			logrus.Warnf("Could not look up %s for cache resolution, skipping: %+v", ref, err)
 			logrus.Warnf("Could not look up %s for cache resolution, skipping: %+v", ref, err)
 			continue
 			continue
 		}
 		}
 		cache.Populate(img)
 		cache.Populate(img)
 	}
 	}
 
 
-	return cache
+	return cache, nil
 }
 }

+ 3 - 2
daemon/images/image_import.go

@@ -1,6 +1,7 @@
 package images // import "github.com/docker/docker/daemon/images"
 package images // import "github.com/docker/docker/daemon/images"
 
 
 import (
 import (
+	"context"
 	"encoding/json"
 	"encoding/json"
 	"io"
 	"io"
 	"net/http"
 	"net/http"
@@ -29,7 +30,7 @@ import (
 // inConfig (if src is "-"), or from a URI specified in src. Progress output is
 // inConfig (if src is "-"), or from a URI specified in src. Progress output is
 // written to outStream. Repository and tag names can optionally be given in
 // written to outStream. Repository and tag names can optionally be given in
 // the repo and tag arguments, respectively.
 // the repo and tag arguments, respectively.
-func (i *ImageService) ImportImage(src string, repository string, platform *specs.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error {
+func (i *ImageService) ImportImage(ctx context.Context, src string, repository string, platform *specs.Platform, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error {
 	var (
 	var (
 		rc     io.ReadCloser
 		rc     io.ReadCloser
 		resp   *http.Response
 		resp   *http.Response
@@ -62,7 +63,7 @@ func (i *ImageService) ImportImage(src string, repository string, platform *spec
 	if !system.IsOSSupported(platform.OS) {
 	if !system.IsOSSupported(platform.OS) {
 		return errdefs.InvalidParameter(system.ErrNotSupportedOperatingSystem)
 		return errdefs.InvalidParameter(system.ErrNotSupportedOperatingSystem)
 	}
 	}
-	config, err := dockerfile.BuildFromConfig(&container.Config{}, changes, platform.OS)
+	config, err := dockerfile.BuildFromConfig(ctx, &container.Config{}, changes, platform.OS)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 10 - 12
daemon/list.go

@@ -42,7 +42,7 @@ type iterationAction int
 
 
 // containerReducer represents a reducer for a container.
 // containerReducer represents a reducer for a container.
 // Returns the object to serialize by the api.
 // Returns the object to serialize by the api.
-type containerReducer func(*container.Snapshot, *listContext) (*types.Container, error)
+type containerReducer func(context.Context, *container.Snapshot, *listContext) (*types.Container, error)
 
 
 const (
 const (
 	// includeContainer is the action to include a container in the reducer.
 	// includeContainer is the action to include a container in the reducer.
@@ -106,8 +106,8 @@ func (r byCreatedDescending) Less(i, j int) bool {
 }
 }
 
 
 // Containers returns the list of containers to show given the user's filtering.
 // Containers returns the list of containers to show given the user's filtering.
-func (daemon *Daemon) Containers(config *types.ContainerListOptions) ([]*types.Container, error) {
-	return daemon.reduceContainers(config, daemon.refreshImage)
+func (daemon *Daemon) Containers(ctx context.Context, config *types.ContainerListOptions) ([]*types.Container, error) {
+	return daemon.reduceContainers(ctx, config, daemon.refreshImage)
 }
 }
 
 
 func (daemon *Daemon) filterByNameIDMatches(view *container.View, filter *listContext) ([]container.Snapshot, error) {
 func (daemon *Daemon) filterByNameIDMatches(view *container.View, filter *listContext) ([]container.Snapshot, error) {
@@ -177,7 +177,7 @@ func (daemon *Daemon) filterByNameIDMatches(view *container.View, filter *listCo
 }
 }
 
 
 // reduceContainers parses the user's filtering options and generates the list of containers to return based on a reducer.
 // reduceContainers parses the user's filtering options and generates the list of containers to return based on a reducer.
-func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, reducer containerReducer) ([]*types.Container, error) {
+func (daemon *Daemon) reduceContainers(ctx context.Context, config *types.ContainerListOptions, reducer containerReducer) ([]*types.Container, error) {
 	if err := config.Filters.Validate(acceptedPsFilterTags); err != nil {
 	if err := config.Filters.Validate(acceptedPsFilterTags); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -187,7 +187,7 @@ func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, reduc
 		containers = []*types.Container{}
 		containers = []*types.Container{}
 	)
 	)
 
 
-	filter, err := daemon.foldFilter(view, config)
+	filter, err := daemon.foldFilter(ctx, view, config)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -201,7 +201,7 @@ func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, reduc
 	}
 	}
 
 
 	for i := range containerList {
 	for i := range containerList {
-		t, err := daemon.reducePsContainer(&containerList[i], filter, reducer)
+		t, err := daemon.reducePsContainer(ctx, &containerList[i], filter, reducer)
 		if err != nil {
 		if err != nil {
 			if err != errStopIteration {
 			if err != errStopIteration {
 				return nil, err
 				return nil, err
@@ -218,7 +218,7 @@ func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, reduc
 }
 }
 
 
 // reducePsContainer is the basic representation for a container as expected by the ps command.
 // reducePsContainer is the basic representation for a container as expected by the ps command.
-func (daemon *Daemon) reducePsContainer(container *container.Snapshot, filter *listContext, reducer containerReducer) (*types.Container, error) {
+func (daemon *Daemon) reducePsContainer(ctx context.Context, container *container.Snapshot, filter *listContext, reducer containerReducer) (*types.Container, error) {
 	// filter containers to return
 	// filter containers to return
 	switch includeContainerInList(container, filter) {
 	switch includeContainerInList(container, filter) {
 	case excludeContainer:
 	case excludeContainer:
@@ -228,7 +228,7 @@ func (daemon *Daemon) reducePsContainer(container *container.Snapshot, filter *l
 	}
 	}
 
 
 	// transform internal container struct into api structs
 	// transform internal container struct into api structs
-	newC, err := reducer(container, filter)
+	newC, err := reducer(ctx, container, filter)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -243,8 +243,7 @@ func (daemon *Daemon) reducePsContainer(container *container.Snapshot, filter *l
 }
 }
 
 
 // foldFilter generates the container filter based on the user's filtering options.
 // foldFilter generates the container filter based on the user's filtering options.
-func (daemon *Daemon) foldFilter(view *container.View, config *types.ContainerListOptions) (*listContext, error) {
-	ctx := context.TODO()
+func (daemon *Daemon) foldFilter(ctx context.Context, view *container.View, config *types.ContainerListOptions) (*listContext, error) {
 	psFilters := config.Filters
 	psFilters := config.Filters
 
 
 	var filtExited []int
 	var filtExited []int
@@ -580,8 +579,7 @@ func includeContainerInList(container *container.Snapshot, filter *listContext)
 }
 }
 
 
 // refreshImage checks if the Image ref still points to the correct ID, and updates the ref to the actual ID when it doesn't
 // refreshImage checks if the Image ref still points to the correct ID, and updates the ref to the actual ID when it doesn't
-func (daemon *Daemon) refreshImage(s *container.Snapshot, filter *listContext) (*types.Container, error) {
-	ctx := context.TODO()
+func (daemon *Daemon) refreshImage(ctx context.Context, s *container.Snapshot, filter *listContext) (*types.Container, error) {
 	c := s.Container
 	c := s.Container
 	tmpImage := s.Image // keep the original ref if still valid (hasn't changed)
 	tmpImage := s.Image // keep the original ref if still valid (hasn't changed)
 	if tmpImage != s.ImageID {
 	if tmpImage != s.ImageID {

+ 6 - 5
daemon/list_test.go

@@ -1,6 +1,7 @@
 package daemon
 package daemon
 
 
 import (
 import (
+	"context"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	"testing"
 	"testing"
@@ -88,7 +89,7 @@ func TestListInvalidFilter(t *testing.T) {
 
 
 	f := filters.NewArgs(filters.Arg("invalid", "foo"))
 	f := filters.NewArgs(filters.Arg("invalid", "foo"))
 
 
-	_, err = d.Containers(&types.ContainerListOptions{
+	_, err = d.Containers(context.Background(), &types.ContainerListOptions{
 		Filters: f,
 		Filters: f,
 	})
 	})
 	assert.Assert(t, is.Error(err, "invalid filter 'invalid'"))
 	assert.Assert(t, is.Error(err, "invalid filter 'invalid'"))
@@ -109,7 +110,7 @@ func TestNameFilter(t *testing.T) {
 
 
 	// moby/moby #37453 - ^ regex not working due to prefix slash
 	// moby/moby #37453 - ^ regex not working due to prefix slash
 	// not being stripped
 	// not being stripped
-	containerList, err := d.Containers(&types.ContainerListOptions{
+	containerList, err := d.Containers(context.Background(), &types.ContainerListOptions{
 		Filters: filters.NewArgs(filters.Arg("name", "^a")),
 		Filters: filters.NewArgs(filters.Arg("name", "^a")),
 	})
 	})
 	assert.NilError(t, err)
 	assert.NilError(t, err)
@@ -118,7 +119,7 @@ func TestNameFilter(t *testing.T) {
 	assert.Assert(t, containerListContainsName(containerList, two.Name))
 	assert.Assert(t, containerListContainsName(containerList, two.Name))
 
 
 	// Same as above but with slash prefix should produce the same result
 	// Same as above but with slash prefix should produce the same result
-	containerListWithPrefix, err := d.Containers(&types.ContainerListOptions{
+	containerListWithPrefix, err := d.Containers(context.Background(), &types.ContainerListOptions{
 		Filters: filters.NewArgs(filters.Arg("name", "^/a")),
 		Filters: filters.NewArgs(filters.Arg("name", "^/a")),
 	})
 	})
 	assert.NilError(t, err)
 	assert.NilError(t, err)
@@ -127,7 +128,7 @@ func TestNameFilter(t *testing.T) {
 	assert.Assert(t, containerListContainsName(containerListWithPrefix, two.Name))
 	assert.Assert(t, containerListContainsName(containerListWithPrefix, two.Name))
 
 
 	// Same as above but make sure it works for exact names
 	// Same as above but make sure it works for exact names
-	containerList, err = d.Containers(&types.ContainerListOptions{
+	containerList, err = d.Containers(context.Background(), &types.ContainerListOptions{
 		Filters: filters.NewArgs(filters.Arg("name", "b1")),
 		Filters: filters.NewArgs(filters.Arg("name", "b1")),
 	})
 	})
 	assert.NilError(t, err)
 	assert.NilError(t, err)
@@ -135,7 +136,7 @@ func TestNameFilter(t *testing.T) {
 	assert.Assert(t, containerListContainsName(containerList, three.Name))
 	assert.Assert(t, containerListContainsName(containerList, three.Name))
 
 
 	// Same as above but with slash prefix should produce the same result
 	// Same as above but with slash prefix should produce the same result
-	containerListWithPrefix, err = d.Containers(&types.ContainerListOptions{
+	containerListWithPrefix, err = d.Containers(context.Background(), &types.ContainerListOptions{
 		Filters: filters.NewArgs(filters.Arg("name", "/b1")),
 		Filters: filters.NewArgs(filters.Arg("name", "/b1")),
 	})
 	})
 	assert.NilError(t, err)
 	assert.NilError(t, err)

+ 1 - 1
daemon/monitor.go

@@ -111,7 +111,7 @@ func (daemon *Daemon) handleContainerExit(c *container.Container, e *libcontaine
 				// But containerStart will use daemon.netController segment.
 				// But containerStart will use daemon.netController segment.
 				// So to avoid panic at startup process, here must wait util daemon restore done.
 				// So to avoid panic at startup process, here must wait util daemon restore done.
 				daemon.waitForStartupDone()
 				daemon.waitForStartupDone()
-				if err = daemon.containerStart(c, "", "", false); err != nil {
+				if err = daemon.containerStart(context.Background(), c, "", "", false); err != nil {
 					logrus.Debugf("failed to restart container: %+v", err)
 					logrus.Debugf("failed to restart container: %+v", err)
 				}
 				}
 			}
 			}

+ 2 - 2
daemon/oci_linux.go

@@ -1007,7 +1007,7 @@ func WithUser(c *container.Container) coci.SpecOpts {
 	}
 	}
 }
 }
 
 
-func (daemon *Daemon) createSpec(c *container.Container) (retSpec *specs.Spec, err error) {
+func (daemon *Daemon) createSpec(ctx context.Context, c *container.Container) (retSpec *specs.Spec, err error) {
 	var (
 	var (
 		opts []coci.SpecOpts
 		opts []coci.SpecOpts
 		s    = oci.DefaultSpec()
 		s    = oci.DefaultSpec()
@@ -1052,7 +1052,7 @@ func (daemon *Daemon) createSpec(c *container.Container) (retSpec *specs.Spec, e
 		snapshotKey = c.ID
 		snapshotKey = c.ID
 	}
 	}
 
 
-	return &s, coci.ApplyOpts(context.Background(), nil, &containers.Container{
+	return &s, coci.ApplyOpts(ctx, nil, &containers.Container{
 		ID:          c.ID,
 		ID:          c.ID,
 		Snapshotter: snapshotter,
 		Snapshotter: snapshotter,
 		SnapshotKey: snapshotKey,
 		SnapshotKey: snapshotKey,

+ 9 - 8
daemon/oci_linux_test.go

@@ -1,6 +1,7 @@
 package daemon // import "github.com/docker/docker/daemon"
 package daemon // import "github.com/docker/docker/daemon"
 
 
 import (
 import (
+	"context"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	"testing"
 	"testing"
@@ -73,7 +74,7 @@ func TestTmpfsDevShmNoDupMount(t *testing.T) {
 	d := setupFakeDaemon(t, c)
 	d := setupFakeDaemon(t, c)
 	defer cleanupFakeContainer(c)
 	defer cleanupFakeContainer(c)
 
 
-	_, err := d.createSpec(c)
+	_, err := d.createSpec(context.TODO(), c)
 	assert.Check(t, err)
 	assert.Check(t, err)
 }
 }
 
 
@@ -92,7 +93,7 @@ func TestIpcPrivateVsReadonly(t *testing.T) {
 	d := setupFakeDaemon(t, c)
 	d := setupFakeDaemon(t, c)
 	defer cleanupFakeContainer(c)
 	defer cleanupFakeContainer(c)
 
 
-	s, err := d.createSpec(c)
+	s, err := d.createSpec(context.TODO(), c)
 	assert.Check(t, err)
 	assert.Check(t, err)
 
 
 	// Find the /dev/shm mount in ms, check it does not have ro
 	// Find the /dev/shm mount in ms, check it does not have ro
@@ -122,7 +123,7 @@ func TestSysctlOverride(t *testing.T) {
 	defer cleanupFakeContainer(c)
 	defer cleanupFakeContainer(c)
 
 
 	// Ensure that the implicit sysctl is set correctly.
 	// Ensure that the implicit sysctl is set correctly.
-	s, err := d.createSpec(c)
+	s, err := d.createSpec(context.TODO(), c)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Equal(t, s.Hostname, "foobar")
 	assert.Equal(t, s.Hostname, "foobar")
 	assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.Config.Domainname)
 	assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.Config.Domainname)
@@ -138,7 +139,7 @@ func TestSysctlOverride(t *testing.T) {
 	assert.Assert(t, c.HostConfig.Sysctls["kernel.domainname"] != c.Config.Domainname)
 	assert.Assert(t, c.HostConfig.Sysctls["kernel.domainname"] != c.Config.Domainname)
 	c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
 	c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
 
 
-	s, err = d.createSpec(c)
+	s, err = d.createSpec(context.TODO(), c)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Equal(t, s.Hostname, "foobar")
 	assert.Equal(t, s.Hostname, "foobar")
 	assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.HostConfig.Sysctls["kernel.domainname"])
 	assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.HostConfig.Sysctls["kernel.domainname"])
@@ -146,7 +147,7 @@ func TestSysctlOverride(t *testing.T) {
 
 
 	// Ensure the ping_group_range is not set on a daemon with user-namespaces enabled
 	// Ensure the ping_group_range is not set on a daemon with user-namespaces enabled
 	d.configStore.RemappedRoot = "dummy:dummy"
 	d.configStore.RemappedRoot = "dummy:dummy"
-	s, err = d.createSpec(c)
+	s, err = d.createSpec(context.TODO(), c)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	_, ok := s.Linux.Sysctl["net.ipv4.ping_group_range"]
 	_, ok := s.Linux.Sysctl["net.ipv4.ping_group_range"]
 	assert.Assert(t, !ok)
 	assert.Assert(t, !ok)
@@ -154,7 +155,7 @@ func TestSysctlOverride(t *testing.T) {
 	// Ensure the ping_group_range is set on a container in "host" userns mode
 	// Ensure the ping_group_range is set on a container in "host" userns mode
 	// on a daemon with user-namespaces enabled
 	// on a daemon with user-namespaces enabled
 	c.HostConfig.UsernsMode = "host"
 	c.HostConfig.UsernsMode = "host"
-	s, err = d.createSpec(c)
+	s, err = d.createSpec(context.TODO(), c)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647")
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647")
 }
 }
@@ -174,7 +175,7 @@ func TestSysctlOverrideHost(t *testing.T) {
 	defer cleanupFakeContainer(c)
 	defer cleanupFakeContainer(c)
 
 
 	// Ensure that the implicit sysctl is not set
 	// Ensure that the implicit sysctl is not set
-	s, err := d.createSpec(c)
+	s, err := d.createSpec(context.TODO(), c)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "")
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "")
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "")
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "")
@@ -182,7 +183,7 @@ func TestSysctlOverrideHost(t *testing.T) {
 	// Set an explicit sysctl.
 	// Set an explicit sysctl.
 	c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
 	c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
 
 
-	s, err = d.createSpec(c)
+	s, err = d.createSpec(context.TODO(), c)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"])
 	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"])
 }
 }

+ 1 - 2
daemon/oci_windows.go

@@ -26,8 +26,7 @@ const (
 	credentialSpecFileLocation     = "CredentialSpecs"
 	credentialSpecFileLocation     = "CredentialSpecs"
 )
 )
 
 
-func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
-	ctx := context.TODO()
+func (daemon *Daemon) createSpec(ctx context.Context, c *container.Container) (*specs.Spec, error) {
 	img, err := daemon.imageService.GetImage(ctx, string(c.ImageID), imagetypes.GetImageOpts{})
 	img, err := daemon.imageService.GetImage(ctx, string(c.ImageID), imagetypes.GetImageOpts{})
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err

+ 1 - 1
daemon/restart.go

@@ -61,7 +61,7 @@ func (daemon *Daemon) containerRestart(ctx context.Context, container *container
 		}
 		}
 	}
 	}
 
 
-	if err := daemon.containerStart(container, "", "", true); err != nil {
+	if err := daemon.containerStart(ctx, container, "", "", true); err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 6 - 8
daemon/start.go

@@ -15,7 +15,7 @@ import (
 )
 )
 
 
 // ContainerStart starts a container.
 // ContainerStart starts a container.
-func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
+func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
 	if checkpoint != "" && !daemon.HasExperimental() {
 	if checkpoint != "" && !daemon.HasExperimental() {
 		return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode"))
 		return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode"))
 	}
 	}
@@ -92,14 +92,14 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos
 			return errdefs.InvalidParameter(err)
 			return errdefs.InvalidParameter(err)
 		}
 		}
 	}
 	}
-	return daemon.containerStart(ctr, checkpoint, checkpointDir, true)
+	return daemon.containerStart(ctx, ctr, checkpoint, checkpointDir, true)
 }
 }
 
 
 // containerStart prepares the container to run by setting up everything the
 // containerStart prepares the container to run by setting up everything the
 // container needs, such as storage and networking, as well as links
 // container needs, such as storage and networking, as well as links
 // between containers. The container is left waiting for a signal to
 // between containers. The container is left waiting for a signal to
 // begin running.
 // begin running.
-func (daemon *Daemon) containerStart(container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (err error) {
+func (daemon *Daemon) containerStart(ctx context.Context, container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (err error) {
 	start := time.Now()
 	start := time.Now()
 	container.Lock()
 	container.Lock()
 	defer container.Unlock()
 	defer container.Unlock()
@@ -151,7 +151,7 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint
 		return err
 		return err
 	}
 	}
 
 
-	spec, err := daemon.createSpec(container)
+	spec, err := daemon.createSpec(ctx, container)
 	if err != nil {
 	if err != nil {
 		return errdefs.System(err)
 		return errdefs.System(err)
 	}
 	}
@@ -177,16 +177,14 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint
 		return err
 		return err
 	}
 	}
 
 
-	ctx := context.TODO()
-
 	ctr, err := libcontainerd.ReplaceContainer(ctx, daemon.containerd, container.ID, spec, shim, createOptions)
 	ctr, err := libcontainerd.ReplaceContainer(ctx, daemon.containerd, container.ID, spec, shim, createOptions)
 	if err != nil {
 	if err != nil {
 		return translateContainerdStartErr(container.Path, container.SetExitCode, err)
 		return translateContainerdStartErr(container.Path, container.SetExitCode, err)
 	}
 	}
 
 
 	// TODO(mlaventure): we need to specify checkpoint options here
 	// TODO(mlaventure): we need to specify checkpoint options here
-	tsk, err := ctr.Start(ctx, checkpointDir,
-		container.StreamConfig.Stdin() != nil || container.Config.Tty,
+	tsk, err := ctr.Start(context.TODO(), // Passing ctx to ctr.Start caused integration tests to be stuck in the cleanup phase
+		checkpointDir, container.StreamConfig.Stdin() != nil || container.Config.Tty,
 		container.InitializeStdio)
 		container.InitializeStdio)
 	if err != nil {
 	if err != nil {
 		if err := ctr.Delete(context.Background()); err != nil {
 		if err := ctr.Delete(context.Background()); err != nil {