From 9822961bfacaddac514381535ecdb4d62917eca5 Mon Sep 17 00:00:00 2001 From: avoidik Date: Wed, 10 Jul 2024 15:25:03 +0100 Subject: [PATCH] add timeout parameter to cli and driver --- builder/builder.go | 3 ++- commands/build.go | 2 ++ commands/create.go | 6 +++++- commands/inspect.go | 4 +++- commands/ls.go | 8 +++++--- commands/rm.go | 4 +++- commands/root.go | 18 ++++++++++++++++-- controller/control/controller.go | 2 ++ controller/remote/controller.go | 5 ++--- driver/docker-container/driver.go | 2 +- driver/kubernetes/driver.go | 10 ++++++---- driver/kubernetes/factory.go | 5 ++--- driver/manager.go | 5 +++++ driver/remote/driver.go | 3 +-- driver/remote/factory.go | 7 +++++++ 15 files changed, 62 insertions(+), 22 deletions(-) diff --git a/builder/builder.go b/builder/builder.go index b603120fd119..eac216cbba7d 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -343,6 +343,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) { @@ -522,7 +523,7 @@ func Create(ctx context.Context, txn *store.Txn, dockerCli command.Cli, opts Cre return nil, err } - timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second) + timeoutCtx, cancel := context.WithTimeout(ctx, opts.Timeout) defer cancel() nodes, err := b.LoadNodes(timeoutCtx, WithData()) diff --git a/commands/build.go b/commands/build.go index 12d45a08f7df..aec9722ded2d 100644 --- a/commands/build.go +++ b/commands/build.go @@ -104,6 +104,8 @@ type buildOptions struct { control.ControlOptions invokeConfig *invokeConfig + + timeout time.Duration } func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error) { 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 8dce4753a304..de70619fa69c 100644 --- a/commands/inspect.go +++ b/commands/inspect.go @@ -23,6 +23,7 @@ import ( type inspectOptions struct { bootstrap bool builder string + timeout time.Duration } func runInspect(ctx context.Context, dockerCli command.Cli, in inspectOptions) error { @@ -34,7 +35,7 @@ func runInspect(ctx context.Context, dockerCli command.Cli, in inspectOptions) e return err } - timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second) + timeoutCtx, cancel := context.WithTimeout(ctx, in.timeout) defer cancel() nodes, err := b.LoadNodes(timeoutCtx, builder.WithData()) @@ -147,6 +148,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 3b18e42cc779..0f4b83dcc553 100644 --- a/commands/ls.go +++ b/commands/ls.go @@ -35,7 +35,8 @@ const ( ) type lsOptions struct { - format string + format string + timeout time.Duration } func runLs(ctx context.Context, dockerCli command.Cli, in lsOptions) error { @@ -55,7 +56,7 @@ func runLs(ctx context.Context, dockerCli command.Cli, in lsOptions) error { return err } - timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second) + timeoutCtx, cancel := context.WithTimeout(ctx, in.timeout) defer cancel() eg, _ := errgroup.WithContext(timeoutCtx) @@ -92,7 +93,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{ @@ -100,6 +101,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 6987cc426159..e83079c7c0f3 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), @@ -150,7 +152,7 @@ func rmAllInactive(ctx context.Context, txn *store.Txn, dockerCli command.Cli, i return err } - timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second) + timeoutCtx, cancel := context.WithTimeout(ctx, in.timeout) defer cancel() eg, _ := errgroup.WithContext(timeoutCtx) diff --git a/commands/root.go b/commands/root.go index 8c0d86274272..af42399a2fa3 100644 --- a/commands/root.go +++ b/commands/root.go @@ -2,6 +2,7 @@ package commands import ( "os" + "time" debugcmd "github.com/docker/buildx/commands/debug" imagetoolscmd "github.com/docker/buildx/commands/imagetools" @@ -20,6 +21,8 @@ import ( "github.com/spf13/pflag" ) +const defaultTimeoutCli = 20 * time.Second + func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Command { cmd := &cobra.Command{ Short: "Docker Buildx", @@ -74,6 +77,7 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman type rootOptions struct { builder string + timeout time.Duration } func addCommands(cmd *cobra.Command, dockerCli command.Cli) { @@ -83,10 +87,10 @@ func addCommands(cmd *cobra.Command, 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), @@ -112,4 +116,14 @@ func addCommands(cmd *cobra.Command, dockerCli command.Cli) { func rootFlags(options *rootOptions, flags *pflag.FlagSet) { flags.StringVar(&options.builder, "builder", os.Getenv("BUILDX_BUILDER"), "Override the configured builder instance") + + 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 (20 seconds)") } diff --git a/controller/control/controller.go b/controller/control/controller.go index bdc5b7f891ef..f32990587cd5 100644 --- a/controller/control/controller.go +++ b/controller/control/controller.go @@ -3,6 +3,7 @@ package control import ( "context" "io" + "time" controllerapi "github.com/docker/buildx/controller/pb" "github.com/docker/buildx/util/progress" @@ -29,4 +30,5 @@ type ControlOptions struct { ServerConfig string Root string Detach bool + Timeout time.Duration } diff --git a/controller/remote/controller.go b/controller/remote/controller.go index 2fbe175819c0..9371c00555d7 100644 --- a/controller/remote/controller.go +++ b/controller/remote/controller.go @@ -13,7 +13,6 @@ import ( "path/filepath" "strconv" "syscall" - "time" "github.com/containerd/log" "github.com/docker/buildx/build" @@ -62,7 +61,7 @@ func NewRemoteBuildxController(ctx context.Context, dockerCli command.Cli, opts serverRoot := filepath.Join(rootDir, "shared") // connect to buildx server if it is already running - ctx2, cancel := context.WithTimeout(ctx, 1*time.Second) + ctx2, cancel := context.WithTimeout(ctx, opts.Timeout) c, err := newBuildxClientAndCheck(ctx2, filepath.Join(serverRoot, defaultSocketFilename)) cancel() if err != nil { @@ -90,7 +89,7 @@ func NewRemoteBuildxController(ctx context.Context, dockerCli command.Cli, opts go wait() // wait for buildx server to be ready - ctx2, cancel = context.WithTimeout(ctx, 10*time.Second) + ctx2, cancel = context.WithTimeout(ctx, opts.Timeout) c, err = newBuildxClientAndCheck(ctx2, filepath.Join(serverRoot, defaultSocketFilename)) cancel() if err != nil { diff --git a/driver/docker-container/driver.go b/driver/docker-container/driver.go index 2eede55bb77a..979223da388d 100644 --- a/driver/docker-container/driver.go +++ b/driver/docker-container/driver.go @@ -214,7 +214,7 @@ func (d *Driver) wait(ctx context.Context, l progress.SubLogger) error { select { case <-ctx.Done(): return ctx.Err() - case <-time.After(time.Duration(try*120) * time.Millisecond): + case <-time.After(d.Timeout / time.Microsecond): try++ continue } diff --git a/driver/kubernetes/driver.go b/driver/kubernetes/driver.go index de03c04efb21..fdae9c95f47f 100644 --- a/driver/kubernetes/driver.go +++ b/driver/kubernetes/driver.go @@ -51,7 +51,6 @@ type Driver struct { configMapClient clientcorev1.ConfigMapInterface podChooser podchooser.PodChooser defaultLoad bool - timeout time.Duration } func (d *Driver) IsMobyDriver() bool { @@ -90,7 +89,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) }) @@ -104,8 +103,11 @@ func (d *Driver) wait(ctx context.Context) error { depl *appsv1.Deployment ) - timeoutChan := time.After(d.timeout) - ticker := time.NewTicker(100 * time.Millisecond) + // 120 * 1000 * 1000 * 1000 = + // 100 * 1000 * 1000 = + + timeoutChan := time.After(d.Timeout) + ticker := time.NewTicker(d.Timeout / time.Microsecond) defer ticker.Stop() for { diff --git a/driver/kubernetes/factory.go b/driver/kubernetes/factory.go index c75e45447755..fe1708752397 100644 --- a/driver/kubernetes/factory.go +++ b/driver/kubernetes/factory.go @@ -20,7 +20,6 @@ import ( const ( prioritySupported = 40 priorityUnsupported = 80 - defaultTimeout = 120 * time.Second ) func init() { @@ -78,7 +77,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 { @@ -119,7 +118,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 746fd8f2f8dd..86a8af427ae2 100644 --- a/driver/manager.go +++ b/driver/manager.go @@ -6,6 +6,7 @@ import ( "sort" "strings" "sync" + "time" dockerclient "github.com/docker/docker/client" "github.com/moby/buildkit/client" @@ -60,8 +61,11 @@ type InitConfig struct { Platforms []specs.Platform ContextPathHash string // can be used for determining pods in the driver instance DialMeta map[string][]string + Timeout time.Duration } +const defaultTimeoutDriver = 120 * time.Second + var drivers map[string]Factory func Register(f Factory) { @@ -117,6 +121,7 @@ func GetDriver(ctx context.Context, name string, f Factory, endpointAddr string, ContextPathHash: contextPathHash, DialMeta: dialMeta, Files: files, + Timeout: defaultTimeoutDriver, } if f == nil { var err error diff --git a/driver/remote/driver.go b/driver/remote/driver.go index eaf47e850eac..1850091e9d01 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" @@ -47,7 +46,7 @@ func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error { return err } return progress.Wrap("[internal] waiting for connection", l, func(_ progress.SubLogger) error { - ctx, cancel := context.WithTimeout(ctx, 20*time.Second) + ctx, cancel := context.WithTimeout(ctx, d.Timeout) defer cancel() return c.Wait(ctx) }) diff --git a/driver/remote/factory.go b/driver/remote/factory.go index 1c30baad6163..b2c96dac08a5 100644 --- a/driver/remote/factory.go +++ b/driver/remote/factory.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strconv" "strings" + "time" // import connhelpers for special url schemes _ "github.com/moby/buildkit/client/connhelper/dockercontainer" @@ -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) }