diff --git a/builder/builder.go b/builder/builder.go index d82af2109d76..2bb83a44318e 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -344,6 +344,7 @@ type CreateOpts struct { Use bool Endpoint string Append bool + Timeout time.Duration } func Create(ctx context.Context, txn *store.Txn, dockerCli command.Cli, opts CreateOpts) (*Builder, error) { @@ -524,7 +525,7 @@ func Create(ctx context.Context, txn *store.Txn, dockerCli command.Cli, opts Cre } cancelCtx, cancel := context.WithCancelCause(ctx) - timeoutCtx, _ := context.WithTimeoutCause(cancelCtx, 20*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent + timeoutCtx, _ := context.WithTimeoutCause(cancelCtx, opts.Timeout, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent defer func() { cancel(errors.WithStack(context.Canceled)) }() nodes, err := b.LoadNodes(timeoutCtx, WithData()) diff --git a/builder/node.go b/builder/node.go index b582503bb889..636ea4979ed8 100644 --- a/builder/node.go +++ b/builder/node.go @@ -5,6 +5,7 @@ import ( "encoding/json" "sort" "strings" + "time" "github.com/containerd/platforms" "github.com/docker/buildx/driver" @@ -39,6 +40,8 @@ type Node struct { CDIDevices []client.CDIDevice } +const defaultDriverTimeout = 120 * time.Second + // Nodes returns nodes for this builder. func (b *Builder) Nodes() []Node { return b.nodes @@ -130,6 +133,7 @@ func (b *Builder) LoadNodes(ctx context.Context, opts ...LoadNodesOption) (_ []N Platforms: n.Platforms, ContextPathHash: b.opts.contextPathHash, DialMeta: lno.dialMeta, + Timeout: defaultDriverTimeout, }) if err != nil { node.Err = err diff --git a/commands/create.go b/commands/create.go index 5cc90e724a51..164ee4a9c880 100644 --- a/commands/create.go +++ b/commands/create.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "time" "github.com/docker/buildx/builder" "github.com/docker/buildx/driver" @@ -27,6 +28,7 @@ type createOptions struct { buildkitdFlags string buildkitdConfigFile string bootstrap bool + timeout time.Duration // upgrade bool // perform upgrade of the driver } @@ -61,6 +63,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg Use: in.use, Endpoint: ep, Append: in.actionAppend, + Timeout: in.timeout, }) if err != nil { return err @@ -80,7 +83,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg return nil } -func createCmd(dockerCli command.Cli) *cobra.Command { +func createCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { var options createOptions var drivers bytes.Buffer @@ -96,6 +99,7 @@ func createCmd(dockerCli command.Cli) *cobra.Command { Short: "Create a new builder instance", Args: cli.RequiresMaxArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + options.timeout = rootOpts.timeout return runCreate(cmd.Context(), dockerCli, options, args) }, ValidArgsFunction: completion.Disable, diff --git a/commands/inspect.go b/commands/inspect.go index eeae99ea124f..e9cb5020f84b 100644 --- a/commands/inspect.go +++ b/commands/inspect.go @@ -24,6 +24,7 @@ import ( type inspectOptions struct { bootstrap bool builder string + timeout time.Duration } func runInspect(ctx context.Context, dockerCli command.Cli, in inspectOptions) error { @@ -36,7 +37,7 @@ func runInspect(ctx context.Context, dockerCli command.Cli, in inspectOptions) e } timeoutCtx, cancel := context.WithCancelCause(ctx) - timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 20*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent + timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, in.timeout, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent defer func() { cancel(errors.WithStack(context.Canceled)) }() nodes, err := b.LoadNodes(timeoutCtx, builder.WithData()) @@ -180,6 +181,7 @@ func inspectCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { if len(args) > 0 { options.builder = args[0] } + options.timeout = rootOpts.timeout return runInspect(cmd.Context(), dockerCli, options) }, ValidArgsFunction: completion.BuilderNames(dockerCli), diff --git a/commands/ls.go b/commands/ls.go index 8c247679d05d..fe78c857abda 100644 --- a/commands/ls.go +++ b/commands/ls.go @@ -40,6 +40,7 @@ const ( type lsOptions struct { format string noTrunc bool + timeout time.Duration } func runLs(ctx context.Context, dockerCli command.Cli, in lsOptions) error { @@ -60,7 +61,7 @@ func runLs(ctx context.Context, dockerCli command.Cli, in lsOptions) error { } timeoutCtx, cancel := context.WithCancelCause(ctx) - timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 20*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent + timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, in.timeout, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent defer func() { cancel(errors.WithStack(context.Canceled)) }() eg, _ := errgroup.WithContext(timeoutCtx) @@ -97,7 +98,7 @@ func runLs(ctx context.Context, dockerCli command.Cli, in lsOptions) error { return nil } -func lsCmd(dockerCli command.Cli) *cobra.Command { +func lsCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { var options lsOptions cmd := &cobra.Command{ @@ -105,6 +106,7 @@ func lsCmd(dockerCli command.Cli) *cobra.Command { Short: "List builder instances", Args: cli.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { + options.timeout = rootOpts.timeout return runLs(cmd.Context(), dockerCli, options) }, ValidArgsFunction: completion.Disable, diff --git a/commands/rm.go b/commands/rm.go index cabbe9aa5dfd..776efe2649fd 100644 --- a/commands/rm.go +++ b/commands/rm.go @@ -21,6 +21,7 @@ type rmOptions struct { keepDaemon bool allInactive bool force bool + timeout time.Duration } const ( @@ -109,6 +110,7 @@ func rmCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { } options.builders = args } + options.timeout = rootOpts.timeout return runRm(cmd.Context(), dockerCli, options) }, ValidArgsFunction: completion.BuilderNames(dockerCli), @@ -151,7 +153,7 @@ func rmAllInactive(ctx context.Context, txn *store.Txn, dockerCli command.Cli, i } timeoutCtx, cancel := context.WithCancelCause(ctx) - timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 20*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent + timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, in.timeout, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent defer func() { cancel(errors.WithStack(context.Canceled)) }() eg, _ := errgroup.WithContext(timeoutCtx) diff --git a/commands/root.go b/commands/root.go index d42eb44b08b9..5a1dd039897d 100644 --- a/commands/root.go +++ b/commands/root.go @@ -3,6 +3,7 @@ package commands import ( "fmt" "os" + "time" debugcmd "github.com/docker/buildx/commands/debug" historycmd "github.com/docker/buildx/commands/history" @@ -22,6 +23,8 @@ import ( "github.com/spf13/pflag" ) +const defaultTimeoutCli = 20 * time.Second + func NewRootCmd(name string, isPlugin bool, dockerCli *command.DockerCli) *cobra.Command { var opt rootOptions cmd := &cobra.Command{ @@ -96,6 +99,7 @@ func NewRootCmd(name string, isPlugin bool, dockerCli *command.DockerCli) *cobra type rootOptions struct { builder string debug bool + timeout time.Duration } func addCommands(cmd *cobra.Command, opts *rootOptions, dockerCli command.Cli) { @@ -104,10 +108,10 @@ func addCommands(cmd *cobra.Command, opts *rootOptions, dockerCli command.Cli) { cmd.AddCommand( buildCmd(dockerCli, opts, nil), bakeCmd(dockerCli, opts), - createCmd(dockerCli), + createCmd(dockerCli, opts), dialStdioCmd(dockerCli, opts), rmCmd(dockerCli, opts), - lsCmd(dockerCli), + lsCmd(dockerCli, opts), useCmd(dockerCli, opts), inspectCmd(dockerCli, opts), stopCmd(dockerCli, opts), @@ -134,4 +138,14 @@ func addCommands(cmd *cobra.Command, opts *rootOptions, dockerCli command.Cli) { func rootFlags(options *rootOptions, flags *pflag.FlagSet) { flags.StringVar(&options.builder, "builder", os.Getenv("BUILDX_BUILDER"), "Override the configured builder instance") flags.BoolVarP(&options.debug, "debug", "D", debug.IsEnabled(), "Enable debug logging") + + var timeoutDuration = defaultTimeoutCli + if value, ok := os.LookupEnv("BUILDX_TIMEOUT"); ok { + var err error + timeoutDuration, err = time.ParseDuration(value) + if err != nil { + timeoutDuration = defaultTimeoutCli + } + } + flags.DurationVar(&options.timeout, "timeout", timeoutDuration, "Override the default global timeout (as duration, for example 1m20s)") } diff --git a/controller/control/controller.go b/controller/control/controller.go index cc501e9bbd0b..9a425595f25a 100644 --- a/controller/control/controller.go +++ b/controller/control/controller.go @@ -3,6 +3,7 @@ package control import ( "context" "io" + "time" "github.com/docker/buildx/build" cbuild "github.com/docker/buildx/controller/build" @@ -13,7 +14,7 @@ import ( ) type BuildxController interface { - Build(ctx context.Context, options *cbuild.Options, in io.ReadCloser, progress progress.Writer) (resp *client.SolveResponse, inputs *build.Inputs, err error) + Build(ctx context.Context, options *ControlOptions, in io.ReadCloser, progress progress.Writer) (resp *client.SolveResponse, inputs *build.Inputs, err error) // Invoke starts an IO session into the specified process. // If pid doesn't match to any running processes, it starts a new process with the specified config. // If there is no container running or InvokeConfig.Rollback is specified, the process will start in a newly created container. @@ -22,5 +23,10 @@ type BuildxController interface { Close() error ListProcesses(ctx context.Context) (infos []*processes.ProcessInfo, retErr error) DisconnectProcess(ctx context.Context, pid string) error - Inspect(ctx context.Context) *cbuild.Options + Inspect(ctx context.Context) *ControlOptions +} + +type ControlOptions struct { + cbuild.Options + Timeout time.Duration } diff --git a/controller/local/controller.go b/controller/local/controller.go index 44e85382f2a6..ebf29e7583e0 100644 --- a/controller/local/controller.go +++ b/controller/local/controller.go @@ -30,7 +30,7 @@ type buildConfig struct { // TODO: these two structs should be merged // Discussion: https://github.com/docker/buildx/pull/1640#discussion_r1113279719 resultCtx *build.ResultHandle - buildOptions *cbuild.Options + buildOptions *control.ControlOptions } type localController struct { @@ -41,13 +41,13 @@ type localController struct { buildOnGoing atomic.Bool } -func (b *localController) Build(ctx context.Context, options *cbuild.Options, in io.ReadCloser, progress progress.Writer) (*client.SolveResponse, *build.Inputs, error) { +func (b *localController) Build(ctx context.Context, options *control.ControlOptions, in io.ReadCloser, progress progress.Writer) (*client.SolveResponse, *build.Inputs, error) { if !b.buildOnGoing.CompareAndSwap(false, true) { return nil, nil, errors.New("build ongoing") } defer b.buildOnGoing.Store(false) - resp, res, dockerfileMappings, buildErr := cbuild.RunBuild(ctx, b.dockerCli, options, in, progress, true) + resp, res, dockerfileMappings, buildErr := cbuild.RunBuild(ctx, b.dockerCli, &options.Options, in, progress, true) // NOTE: RunBuild can return *build.ResultHandle even on error. if res != nil { b.buildConfig = buildConfig{ @@ -118,6 +118,6 @@ func (b *localController) Close() error { return nil } -func (b *localController) Inspect(ctx context.Context) *cbuild.Options { +func (b *localController) Inspect(ctx context.Context) *control.ControlOptions { return b.buildConfig.buildOptions } diff --git a/docs/reference/buildx.md b/docs/reference/buildx.md index 6e060ce5c631..fd00ee0f89b0 100644 --- a/docs/reference/buildx.md +++ b/docs/reference/buildx.md @@ -30,10 +30,11 @@ Extended build capabilities with BuildKit ### Options -| Name | Type | Default | Description | -|:------------------------|:---------|:--------|:-----------------------------------------| -| [`--builder`](#builder) | `string` | | Override the configured builder instance | -| `-D`, `--debug` | `bool` | | Enable debug logging | +| Name | Type | Default | Description | +|:------------------------|:-----------|:--------|:---------------------------------------------------------------------| +| [`--builder`](#builder) | `string` | | Override the configured builder instance | +| `-D`, `--debug` | `bool` | | Enable debug logging | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | diff --git a/docs/reference/buildx_bake.md b/docs/reference/buildx_bake.md index 733a3eae1231..eca0d76a718d 100644 --- a/docs/reference/buildx_bake.md +++ b/docs/reference/buildx_bake.md @@ -32,6 +32,7 @@ Build from a file | `--push` | `bool` | | Shorthand for `--set=*.output=type=registry` | | [`--sbom`](#sbom) | `string` | | Shorthand for `--set=*.attest=type=sbom` | | [`--set`](#set) | `stringArray` | | Override target value (e.g., `targetpattern.key=value`) | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | diff --git a/docs/reference/buildx_build.md b/docs/reference/buildx_build.md index e9de2927849f..9d9feb536a0e 100644 --- a/docs/reference/buildx_build.md +++ b/docs/reference/buildx_build.md @@ -49,6 +49,7 @@ Start a build | [`--ssh`](#ssh) | `stringArray` | | SSH agent socket or keys to expose to the build (format: `default\|[=\|[,]]`) | | [`-t`](#tag), [`--tag`](#tag) | `stringArray` | | Name and optionally a tag (format: `name:tag`) | | [`--target`](#target) | `string` | | Set the target build stage to build | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | | [`--ulimit`](#ulimit) | `ulimit` | | Ulimit options | diff --git a/docs/reference/buildx_create.md b/docs/reference/buildx_create.md index 96d71b29afce..0ae56eda6585 100644 --- a/docs/reference/buildx_create.md +++ b/docs/reference/buildx_create.md @@ -22,6 +22,7 @@ Create a new builder instance | [`--name`](#name) | `string` | | Builder instance name | | [`--node`](#node) | `string` | | Create/modify node with given name | | [`--platform`](#platform) | `stringArray` | | Fixed platforms for current node | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | | [`--use`](#use) | `bool` | | Set the current builder instance | diff --git a/docs/reference/buildx_debug.md b/docs/reference/buildx_debug.md index afe695674592..e2afdd5bcadc 100644 --- a/docs/reference/buildx_debug.md +++ b/docs/reference/buildx_debug.md @@ -12,13 +12,14 @@ Start debugger (EXPERIMENTAL) ### Options -| Name | Type | Default | Description | -|:----------------|:---------|:--------|:--------------------------------------------------------------------------------------------------------------------| -| `--builder` | `string` | | Override the configured builder instance | -| `-D`, `--debug` | `bool` | | Enable debug logging | -| `--invoke` | `string` | | Launch a monitor with executing specified command (EXPERIMENTAL) | -| `--on` | `string` | `error` | When to launch the monitor ([always, error]) (EXPERIMENTAL) | -| `--progress` | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`) for the monitor. Use plain to show container output | +| Name | Type | Default | Description | +|:----------------|:-----------|:--------|:--------------------------------------------------------------------------------------------------------------------| +| `--builder` | `string` | | Override the configured builder instance | +| `-D`, `--debug` | `bool` | | Enable debug logging | +| `--invoke` | `string` | | Launch a monitor with executing specified command (EXPERIMENTAL) | +| `--on` | `string` | `error` | When to launch the monitor ([always, error]) (EXPERIMENTAL) | +| `--progress` | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`) for the monitor. Use plain to show container output | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | diff --git a/docs/reference/buildx_debug_build.md b/docs/reference/buildx_debug_build.md index ee3594804c67..5b0c459c1567 100644 --- a/docs/reference/buildx_debug_build.md +++ b/docs/reference/buildx_debug_build.md @@ -45,6 +45,7 @@ Start a build | `--ssh` | `stringArray` | | SSH agent socket or keys to expose to the build (format: `default\|[=\|[,]]`) | | `-t`, `--tag` | `stringArray` | | Name and optionally a tag (format: `name:tag`) | | `--target` | `string` | | Set the target build stage to build | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | | `--ulimit` | `ulimit` | | Ulimit options | diff --git a/docs/reference/buildx_dial-stdio.md b/docs/reference/buildx_dial-stdio.md index 7fdc84ec0fe4..fb951c239df0 100644 --- a/docs/reference/buildx_dial-stdio.md +++ b/docs/reference/buildx_dial-stdio.md @@ -5,12 +5,13 @@ Proxy current stdio streams to builder instance ### Options -| Name | Type | Default | Description | -|:----------------|:---------|:--------|:----------------------------------------------------------------------------------------------------| -| `--builder` | `string` | | Override the configured builder instance | -| `-D`, `--debug` | `bool` | | Enable debug logging | -| `--platform` | `string` | | Target platform: this is used for node selection | -| `--progress` | `string` | `quiet` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`). Use plain to show container output | +| Name | Type | Default | Description | +|:----------------|:-----------|:--------|:----------------------------------------------------------------------------------------------------| +| `--builder` | `string` | | Override the configured builder instance | +| `-D`, `--debug` | `bool` | | Enable debug logging | +| `--platform` | `string` | | Target platform: this is used for node selection | +| `--progress` | `string` | `quiet` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`). Use plain to show container output | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | diff --git a/docs/reference/buildx_du.md b/docs/reference/buildx_du.md index 684f717a2e09..3922fd2169ad 100644 --- a/docs/reference/buildx_du.md +++ b/docs/reference/buildx_du.md @@ -9,12 +9,13 @@ Disk usage ### Options -| Name | Type | Default | Description | -|:------------------------|:---------|:--------|:-----------------------------------------| -| [`--builder`](#builder) | `string` | | Override the configured builder instance | -| `-D`, `--debug` | `bool` | | Enable debug logging | -| `--filter` | `filter` | | Provide filter values | -| [`--verbose`](#verbose) | `bool` | | Provide a more verbose output | +| Name | Type | Default | Description | +|:------------------------|:-----------|:--------|:---------------------------------------------------------------------| +| [`--builder`](#builder) | `string` | | Override the configured builder instance | +| `-D`, `--debug` | `bool` | | Enable debug logging | +| `--filter` | `filter` | | Provide filter values | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | +| [`--verbose`](#verbose) | `bool` | | Provide a more verbose output | diff --git a/docs/reference/buildx_imagetools.md b/docs/reference/buildx_imagetools.md index 42e191ddf219..9151533e2cee 100644 --- a/docs/reference/buildx_imagetools.md +++ b/docs/reference/buildx_imagetools.md @@ -17,10 +17,11 @@ Commands to work on images in registry ### Options -| Name | Type | Default | Description | -|:------------------------|:---------|:--------|:-----------------------------------------| -| [`--builder`](#builder) | `string` | | Override the configured builder instance | -| `-D`, `--debug` | `bool` | | Enable debug logging | +| Name | Type | Default | Description | +|:------------------------|:-----------|:--------|:---------------------------------------------------------------------| +| [`--builder`](#builder) | `string` | | Override the configured builder instance | +| `-D`, `--debug` | `bool` | | Enable debug logging | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | diff --git a/docs/reference/buildx_imagetools_create.md b/docs/reference/buildx_imagetools_create.md index d937d5d7c475..098a2d45c8c9 100644 --- a/docs/reference/buildx_imagetools_create.md +++ b/docs/reference/buildx_imagetools_create.md @@ -20,6 +20,7 @@ Create a new image based on source images | `--prefer-index` | `bool` | `true` | When only a single source is specified, prefer outputting an image index or manifest list instead of performing a carbon copy | | `--progress` | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`). Use plain to show container output | | [`-t`](#tag), [`--tag`](#tag) | `stringArray` | | Set reference for new image | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | diff --git a/docs/reference/buildx_imagetools_inspect.md b/docs/reference/buildx_imagetools_inspect.md index ed98215b143a..2dc2e2c35ded 100644 --- a/docs/reference/buildx_imagetools_inspect.md +++ b/docs/reference/buildx_imagetools_inspect.md @@ -9,12 +9,13 @@ Show details of an image in the registry ### Options -| Name | Type | Default | Description | -|:------------------------|:---------|:----------------|:----------------------------------------------| -| [`--builder`](#builder) | `string` | | Override the configured builder instance | -| `-D`, `--debug` | `bool` | | Enable debug logging | -| [`--format`](#format) | `string` | `{{.Manifest}}` | Format the output using the given Go template | -| [`--raw`](#raw) | `bool` | | Show original, unformatted JSON manifest | +| Name | Type | Default | Description | +|:------------------------|:-----------|:----------------|:---------------------------------------------------------------------| +| [`--builder`](#builder) | `string` | | Override the configured builder instance | +| `-D`, `--debug` | `bool` | | Enable debug logging | +| [`--format`](#format) | `string` | `{{.Manifest}}` | Format the output using the given Go template | +| [`--raw`](#raw) | `bool` | | Show original, unformatted JSON manifest | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | diff --git a/docs/reference/buildx_inspect.md b/docs/reference/buildx_inspect.md index 02847e8755b1..6a53cc7d3491 100644 --- a/docs/reference/buildx_inspect.md +++ b/docs/reference/buildx_inspect.md @@ -9,11 +9,12 @@ Inspect current builder instance ### Options -| Name | Type | Default | Description | -|:----------------------------|:---------|:--------|:--------------------------------------------| -| [`--bootstrap`](#bootstrap) | `bool` | | Ensure builder has booted before inspecting | -| [`--builder`](#builder) | `string` | | Override the configured builder instance | -| `-D`, `--debug` | `bool` | | Enable debug logging | +| Name | Type | Default | Description | +|:----------------------------|:-----------|:--------|:---------------------------------------------------------------------| +| [`--bootstrap`](#bootstrap) | `bool` | | Ensure builder has booted before inspecting | +| [`--builder`](#builder) | `string` | | Override the configured builder instance | +| `-D`, `--debug` | `bool` | | Enable debug logging | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | diff --git a/docs/reference/buildx_ls.md b/docs/reference/buildx_ls.md index 63f442f211ca..4547ed702965 100644 --- a/docs/reference/buildx_ls.md +++ b/docs/reference/buildx_ls.md @@ -9,11 +9,12 @@ List builder instances ### Options -| Name | Type | Default | Description | -|:----------------------|:---------|:--------|:----------------------| -| `-D`, `--debug` | `bool` | | Enable debug logging | -| [`--format`](#format) | `string` | `table` | Format the output | -| `--no-trunc` | `bool` | | Don't truncate output | +| Name | Type | Default | Description | +|:----------------------|:-----------|:--------|:---------------------------------------------------------------------| +| `-D`, `--debug` | `bool` | | Enable debug logging | +| [`--format`](#format) | `string` | `table` | Format the output | +| `--no-trunc` | `bool` | | Don't truncate output | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | diff --git a/docs/reference/buildx_prune.md b/docs/reference/buildx_prune.md index e86b3021538f..fca5bee96d29 100644 --- a/docs/reference/buildx_prune.md +++ b/docs/reference/buildx_prune.md @@ -9,17 +9,18 @@ Remove build cache ### Options -| Name | Type | Default | Description | -|:------------------------|:---------|:--------|:-------------------------------------------------------| -| `-a`, `--all` | `bool` | | Include internal/frontend images | -| [`--builder`](#builder) | `string` | | Override the configured builder instance | -| `-D`, `--debug` | `bool` | | Enable debug logging | -| `--filter` | `filter` | | Provide filter values (e.g., `until=24h`) | -| `-f`, `--force` | `bool` | | Do not prompt for confirmation | -| `--max-used-space` | `bytes` | `0` | Maximum amount of disk space allowed to keep for cache | -| `--min-free-space` | `bytes` | `0` | Target amount of free disk space after pruning | -| `--reserved-space` | `bytes` | `0` | Amount of disk space always allowed to keep for cache | -| `--verbose` | `bool` | | Provide a more verbose output | +| Name | Type | Default | Description | +|:------------------------|:-----------|:--------|:---------------------------------------------------------------------| +| `-a`, `--all` | `bool` | | Include internal/frontend images | +| [`--builder`](#builder) | `string` | | Override the configured builder instance | +| `-D`, `--debug` | `bool` | | Enable debug logging | +| `--filter` | `filter` | | Provide filter values (e.g., `until=24h`) | +| `-f`, `--force` | `bool` | | Do not prompt for confirmation | +| `--max-used-space` | `bytes` | `0` | Maximum amount of disk space allowed to keep for cache | +| `--min-free-space` | `bytes` | `0` | Target amount of free disk space after pruning | +| `--reserved-space` | `bytes` | `0` | Amount of disk space always allowed to keep for cache | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | +| `--verbose` | `bool` | | Provide a more verbose output | diff --git a/docs/reference/buildx_rm.md b/docs/reference/buildx_rm.md index bcb856e7bdf3..795f1efe41d4 100644 --- a/docs/reference/buildx_rm.md +++ b/docs/reference/buildx_rm.md @@ -9,14 +9,15 @@ Remove one or more builder instances ### Options -| Name | Type | Default | Description | -|:------------------------------------|:---------|:--------|:-----------------------------------------| -| [`--all-inactive`](#all-inactive) | `bool` | | Remove all inactive builders | -| [`--builder`](#builder) | `string` | | Override the configured builder instance | -| `-D`, `--debug` | `bool` | | Enable debug logging | -| [`-f`](#force), [`--force`](#force) | `bool` | | Do not prompt for confirmation | -| [`--keep-daemon`](#keep-daemon) | `bool` | | Keep the BuildKit daemon running | -| [`--keep-state`](#keep-state) | `bool` | | Keep BuildKit state | +| Name | Type | Default | Description | +|:------------------------------------|:-----------|:--------|:---------------------------------------------------------------------| +| [`--all-inactive`](#all-inactive) | `bool` | | Remove all inactive builders | +| [`--builder`](#builder) | `string` | | Override the configured builder instance | +| `-D`, `--debug` | `bool` | | Enable debug logging | +| [`-f`](#force), [`--force`](#force) | `bool` | | Do not prompt for confirmation | +| [`--keep-daemon`](#keep-daemon) | `bool` | | Keep the BuildKit daemon running | +| [`--keep-state`](#keep-state) | `bool` | | Keep BuildKit state | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | diff --git a/docs/reference/buildx_stop.md b/docs/reference/buildx_stop.md index dcf0430a9f3e..1cddab582b99 100644 --- a/docs/reference/buildx_stop.md +++ b/docs/reference/buildx_stop.md @@ -9,10 +9,11 @@ Stop builder instance ### Options -| Name | Type | Default | Description | -|:------------------------|:---------|:--------|:-----------------------------------------| -| [`--builder`](#builder) | `string` | | Override the configured builder instance | -| `-D`, `--debug` | `bool` | | Enable debug logging | +| Name | Type | Default | Description | +|:------------------------|:-----------|:--------|:---------------------------------------------------------------------| +| [`--builder`](#builder) | `string` | | Override the configured builder instance | +| `-D`, `--debug` | `bool` | | Enable debug logging | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | diff --git a/docs/reference/buildx_use.md b/docs/reference/buildx_use.md index 6cd1652f7049..9137aa89db84 100644 --- a/docs/reference/buildx_use.md +++ b/docs/reference/buildx_use.md @@ -9,12 +9,13 @@ Set the current builder instance ### Options -| Name | Type | Default | Description | -|:------------------------|:---------|:--------|:-------------------------------------------| -| [`--builder`](#builder) | `string` | | Override the configured builder instance | -| `-D`, `--debug` | `bool` | | Enable debug logging | -| `--default` | `bool` | | Set builder as default for current context | -| `--global` | `bool` | | Builder persists context changes | +| Name | Type | Default | Description | +|:------------------------|:-----------|:--------|:---------------------------------------------------------------------| +| [`--builder`](#builder) | `string` | | Override the configured builder instance | +| `-D`, `--debug` | `bool` | | Enable debug logging | +| `--default` | `bool` | | Set builder as default for current context | +| `--global` | `bool` | | Builder persists context changes | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | diff --git a/docs/reference/buildx_version.md b/docs/reference/buildx_version.md index e186812f12a9..13b41712e296 100644 --- a/docs/reference/buildx_version.md +++ b/docs/reference/buildx_version.md @@ -9,9 +9,10 @@ Show buildx version information ### Options -| Name | Type | Default | Description | -|:----------------|:-------|:--------|:---------------------| -| `-D`, `--debug` | `bool` | | Enable debug logging | +| Name | Type | Default | Description | +|:----------------|:-----------|:--------|:---------------------------------------------------------------------| +| `-D`, `--debug` | `bool` | | Enable debug logging | +| `--timeout` | `duration` | `20s` | Override the default global timeout (as duration, for example 1m20s) | diff --git a/driver/docker-container/driver.go b/driver/docker-container/driver.go index cfcdf67cca3b..c10f3646aa28 100644 --- a/driver/docker-container/driver.go +++ b/driver/docker-container/driver.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "io" + "math" "net" "os" "path" @@ -199,12 +200,15 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error { } func (d *Driver) wait(ctx context.Context, l progress.SubLogger) error { - try := 1 + var try uint64 = 1 + var total float64 = float64(d.Timeout.Abs().Milliseconds()) + var step uint64 = 120 + var maxTries uint64 = uint64(math.Ceil((math.Sqrt(1.0 + (8.0 * total / float64(step))) - 1.0) / 2.0)) for { bufStdout := &bytes.Buffer{} bufStderr := &bytes.Buffer{} if err := d.run(ctx, []string{"buildctl", "debug", "workers"}, bufStdout, bufStderr); err != nil { - if try > 15 { + if try > maxTries { d.copyLogs(context.TODO(), l) if bufStdout.Len() != 0 { l.Log(1, bufStdout.Bytes()) @@ -217,7 +221,7 @@ func (d *Driver) wait(ctx context.Context, l progress.SubLogger) error { select { case <-ctx.Done(): return context.Cause(ctx) - case <-time.After(time.Duration(try*120) * time.Millisecond): + case <-time.After(time.Duration(try * step) * time.Millisecond): try++ continue } diff --git a/driver/docker-container/factory.go b/driver/docker-container/factory.go index dba791b61eef..c9389f3c5d30 100644 --- a/driver/docker-container/factory.go +++ b/driver/docker-container/factory.go @@ -5,6 +5,7 @@ import ( "fmt" "strconv" "strings" + "time" "github.com/docker/buildx/driver" dockeropts "github.com/docker/cli/opts" @@ -105,6 +106,12 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver if err != nil { return nil, err } + case k == "timeout": + parsed, err := time.ParseDuration(v) + if err != nil { + return nil, err + } + d.Timeout = parsed case strings.HasPrefix(k, "env."): envName := strings.TrimPrefix(k, "env.") if envName == "" { diff --git a/driver/kubernetes/driver.go b/driver/kubernetes/driver.go index 3bfe568a5802..08dbbafeeb0a 100644 --- a/driver/kubernetes/driver.go +++ b/driver/kubernetes/driver.go @@ -52,7 +52,6 @@ type Driver struct { configMapClient clientcorev1.ConfigMapInterface podChooser podchooser.PodChooser defaultLoad bool - timeout time.Duration } func (d *Driver) IsMobyDriver() bool { @@ -91,7 +90,7 @@ func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error { } } return sub.Wrap( - fmt.Sprintf("waiting for %d pods to be ready, timeout: %s", d.minReplicas, units.HumanDuration(d.timeout)), + fmt.Sprintf("waiting for %d pods to be ready, timeout: %s", d.minReplicas, units.HumanDuration(d.Timeout)), func() error { return d.wait(ctx) }) @@ -105,8 +104,8 @@ func (d *Driver) wait(ctx context.Context) error { depl *appsv1.Deployment ) - timeoutChan := time.After(d.timeout) - ticker := time.NewTicker(100 * time.Millisecond) + timeoutChan := time.After(d.Timeout) + ticker := time.NewTicker(d.Timeout / 1000) defer ticker.Stop() for { diff --git a/driver/kubernetes/factory.go b/driver/kubernetes/factory.go index b25abb899b68..8a0b9f013a57 100644 --- a/driver/kubernetes/factory.go +++ b/driver/kubernetes/factory.go @@ -23,7 +23,6 @@ import ( const ( prioritySupported = 40 priorityUnsupported = 80 - defaultTimeout = 120 * time.Second ) type ClientConfig interface { @@ -134,7 +133,7 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver } d.defaultLoad = defaultLoad - d.timeout = timeout + d.Timeout = timeout d.deployment, d.configMaps, err = manifest.NewDeployment(deploymentOpt) if err != nil { @@ -175,7 +174,7 @@ func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg } defaultLoad := false - timeout := defaultTimeout + timeout := cfg.Timeout deploymentOpt.Qemu.Image = bkimage.QemuImage diff --git a/driver/manager.go b/driver/manager.go index 400a0cdd72c0..745b53ecfba6 100644 --- a/driver/manager.go +++ b/driver/manager.go @@ -4,6 +4,7 @@ import ( "context" "sort" "sync" + "time" "github.com/docker/cli/cli/context/store" dockerclient "github.com/docker/docker/client" @@ -38,6 +39,7 @@ type InitConfig struct { Platforms []specs.Platform ContextPathHash string DialMeta map[string][]string + Timeout time.Duration } var drivers map[string]Factory @@ -90,6 +92,9 @@ func GetDriver(ctx context.Context, f Factory, cfg InitConfig) (*DriverHandle, e return nil, err } } + if cfg.Timeout.Nanoseconds() < 1 { + return nil, errors.Errorf("InitConfig.Timeout not set, or negative") + } d, err := f.New(ctx, cfg) if err != nil { return nil, err diff --git a/driver/remote/driver.go b/driver/remote/driver.go index 22145dd62bad..80aa34dceae8 100644 --- a/driver/remote/driver.go +++ b/driver/remote/driver.go @@ -8,7 +8,6 @@ import ( "os" "strings" "sync" - "time" "github.com/docker/buildx/driver" util "github.com/docker/buildx/driver/remote/util" @@ -48,7 +47,7 @@ func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error { } return progress.Wrap("[internal] waiting for connection", l, func(_ progress.SubLogger) error { cancelCtx, cancel := context.WithCancelCause(ctx) - ctx, _ := context.WithTimeoutCause(cancelCtx, 20*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent + ctx, _ := context.WithTimeoutCause(cancelCtx, d.Timeout, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent defer func() { cancel(errors.WithStack(context.Canceled)) }() return c.Wait(ctx) }) diff --git a/driver/remote/factory.go b/driver/remote/factory.go index c07c08f2c7fa..be3c31c253ee 100644 --- a/driver/remote/factory.go +++ b/driver/remote/factory.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strconv" "strings" + "time" "github.com/docker/buildx/driver" util "github.com/docker/buildx/driver/remote/util" @@ -87,6 +88,12 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver return nil, err } d.defaultLoad = parsed + case "timeout": + parsed, err := time.ParseDuration(v) + if err != nil { + return nil, err + } + d.Timeout = parsed default: return nil, errors.Errorf("invalid driver option %s for remote driver", k) } diff --git a/monitor/monitor.go b/monitor/monitor.go index 750f44393249..5487482b9aea 100644 --- a/monitor/monitor.go +++ b/monitor/monitor.go @@ -11,7 +11,6 @@ import ( "github.com/containerd/console" "github.com/docker/buildx/build" - cbuild "github.com/docker/buildx/controller/build" "github.com/docker/buildx/controller/control" controllerapi "github.com/docker/buildx/controller/pb" "github.com/docker/buildx/monitor/commands" @@ -32,7 +31,7 @@ type MonitorBuildResult struct { } // RunMonitor provides an interactive session for running and managing containers via specified IO. -func RunMonitor(ctx context.Context, curRef string, options *cbuild.Options, invokeConfig *controllerapi.InvokeConfig, c control.BuildxController, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File, progress *progress.Printer) (*MonitorBuildResult, error) { +func RunMonitor(ctx context.Context, curRef string, options *control.ControlOptions, invokeConfig *controllerapi.InvokeConfig, c control.BuildxController, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File, progress *progress.Printer) (*MonitorBuildResult, error) { defer func() { if err := c.Close(); err != nil { logrus.Warnf("close error: %v", err) @@ -94,7 +93,7 @@ func RunMonitor(ctx context.Context, curRef string, options *cbuild.Options, inv fmt.Fprintf(stdout, "Interactive container was restarted with process %q. Press Ctrl-a-c to switch to the new container\n", id) availableCommands := []types.Command{ - commands.NewReloadCmd(m, stdout, progress, options, invokeConfig), + commands.NewReloadCmd(m, stdout, progress, &options.Options, invokeConfig), commands.NewRollbackCmd(m, invokeConfig, stdout), commands.NewAttachCmd(m, stdout), commands.NewExecCmd(m, invokeConfig, stdout), @@ -242,7 +241,7 @@ type monitor struct { lastBuildResult *MonitorBuildResult } -func (m *monitor) Build(ctx context.Context, options *cbuild.Options, in io.ReadCloser, progress progress.Writer) (resp *client.SolveResponse, input *build.Inputs, err error) { +func (m *monitor) Build(ctx context.Context, options *control.ControlOptions, in io.ReadCloser, progress progress.Writer) (resp *client.SolveResponse, input *build.Inputs, err error) { resp, _, err = m.BuildxController.Build(ctx, options, in, progress) m.lastBuildResult = &MonitorBuildResult{Resp: resp, Err: err} // Record build result return