Flow context.Context through image builds (#522)
This should help with cancel responsiveness. Right now, there are multiple places where docker does not respond to cancel for long periods of time.
This commit is contained in:
@@ -25,6 +25,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -203,7 +204,7 @@ func (c *cli) Close() error {
|
||||
// execBuild performs a build by os.Exec'ing the docker-buildx binary.
|
||||
// Credentials are communicated to docker-buildx via a temporary directory.
|
||||
// Secrets are communicated via dynamic environment variables.
|
||||
func (c *cli) execBuild(b Build) (*client.SolveResponse, error) {
|
||||
func (c *cli) execBuild(ctx context.Context, b Build) (*client.SolveResponse, error) {
|
||||
// Setup a temporary directory for auth, and clean it up when we're done.
|
||||
tmp, err := os.MkdirTemp("", "pulumi-docker-")
|
||||
if err != nil {
|
||||
@@ -213,7 +214,7 @@ func (c *cli) execBuild(b Build) (*client.SolveResponse, error) {
|
||||
|
||||
opts := b.BuildOptions()
|
||||
|
||||
builder, err := c.host.builderFor(b)
|
||||
builder, err := c.host.builderFor(ctx, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -339,7 +340,7 @@ func (c *cli) execBuild(b Build) (*client.SolveResponse, error) {
|
||||
}
|
||||
|
||||
// Invoke docker-buildx.
|
||||
err = c.exec(args, env)
|
||||
err = c.exec(ctx, args, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -378,7 +379,7 @@ func (c *cli) execBuild(b Build) (*client.SolveResponse, error) {
|
||||
|
||||
// exec invokes a Docker plugin binary. The first argument should be the name
|
||||
// of the plugin's subcommand, e.g. "buildx".
|
||||
func (c *cli) exec(args, extraEnv []string) error {
|
||||
func (c *cli) exec(ctx context.Context, args, extraEnv []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("args must be non-empty")
|
||||
}
|
||||
@@ -395,16 +396,18 @@ func (c *cli) exec(args, extraEnv []string) error {
|
||||
|
||||
defer contract.IgnoreClose(c.w)
|
||||
|
||||
cmd, err := manager.PluginRunCommand(c, name, root)
|
||||
runCmd, err := manager.PluginRunCommand(c, name, root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Args = append([]string{cmd.Args[0]}, args...)
|
||||
// Create a new command that inherits from ctx.
|
||||
cmd := exec.CommandContext(ctx, //nolint:gosec // We take the first argument and binary from runCmd.
|
||||
runCmd.Path, append([]string{runCmd.Args[1]}, args...)...,
|
||||
)
|
||||
cmd.Stderr = c.Err()
|
||||
cmd.Stdout = c.Out()
|
||||
cmd.Stdin = c.In()
|
||||
|
||||
cmd.Env = append(cmd.Env, extraEnv...)
|
||||
cmd.Env = append(runCmd.Env, extraEnv...) //nolint:gocritic // We are intentionally assigning from runCmd to cmd
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@ import (
|
||||
func TestExec(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
h, err := newHost(context.Background(), nil)
|
||||
h, err := newHost(t.Context(), nil)
|
||||
require.NoError(t, err)
|
||||
cli, err := wrap(h)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = cli.exec([]string{"buildx", "version"}, nil)
|
||||
err = cli.exec(t.Context(), []string{"buildx", "version"}, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
out, err := io.ReadAll(cli.r)
|
||||
|
||||
@@ -106,10 +106,10 @@ func (c *cli) Build(
|
||||
defer contract.IgnoreClose(c)
|
||||
|
||||
if build.ShouldExec() {
|
||||
return c.execBuild(build)
|
||||
return c.execBuild(ctx, build)
|
||||
}
|
||||
|
||||
b, err := c.host.builderFor(build)
|
||||
b, err := c.host.builderFor(ctx, build)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func newHost(ctx context.Context, config *Config) (*host, error) {
|
||||
//
|
||||
// If the build doesn't specify a builder by name, we will iterate through all
|
||||
// available builders until we find one that we can connect to.
|
||||
func (h *host) builderFor(build Build) (*cachedBuilder, error) {
|
||||
func (h *host) builderFor(ctx context.Context, build Build) (*cachedBuilder, error) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
@@ -116,7 +116,7 @@ func (h *host) builderFor(build Build) (*cachedBuilder, error) {
|
||||
if bb.Err() != nil {
|
||||
continue
|
||||
}
|
||||
nodes, err := bb.LoadNodes(context.Background())
|
||||
nodes, err := bb.LoadNodes(ctx)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -125,7 +125,7 @@ func (h *host) builderFor(build Build) (*cachedBuilder, error) {
|
||||
if n.Driver == nil {
|
||||
continue nextbuilder
|
||||
}
|
||||
if _, err := n.Driver.Dial(context.Background()); err != nil {
|
||||
if _, err := n.Driver.Dial(ctx); err != nil {
|
||||
continue nextbuilder
|
||||
}
|
||||
// TODO: Confirm the builder supports the requested platforms.
|
||||
@@ -139,7 +139,7 @@ func (h *host) builderFor(build Build) (*cachedBuilder, error) {
|
||||
|
||||
// If we STILL don't have a builder, create a docker-container instance.
|
||||
b, err = builder.Create(
|
||||
context.Background(),
|
||||
ctx,
|
||||
txn,
|
||||
h.cli,
|
||||
builder.CreateOpts{Driver: "docker-container"},
|
||||
@@ -147,7 +147,7 @@ func (h *host) builderFor(build Build) (*cachedBuilder, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating builder: %w", err)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
defer cancel()
|
||||
if _, err := b.Boot(ctx); err != nil {
|
||||
return nil, fmt.Errorf("booting builder: %w", err)
|
||||
@@ -157,7 +157,7 @@ func (h *host) builderFor(build Build) (*cachedBuilder, error) {
|
||||
// Attempt to load nodes in order to determine the builder's driver. Ignore
|
||||
// errors for "exec" builds because it's possible to request builders with
|
||||
// drivers that are unknown to us.
|
||||
nodes, err := b.LoadNodes(context.Background(), builder.WithData())
|
||||
nodes, err := b.LoadNodes(ctx, builder.WithData())
|
||||
if err != nil && !build.ShouldExec() {
|
||||
return nil, fmt.Errorf("loading nodes: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user