From 378f0b45c60577a6ccea2ef9a4e6d725b3046fa3 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Thu, 3 Feb 2022 13:39:44 +0100 Subject: [PATCH] cli: add --all-inactive and --force flags for rm command Signed-off-by: CrazyMax --- commands/rm.go | 77 +++++++++++++++++++++++++++++++++---- commands/util.go | 11 ++++++ docs/reference/buildx_rm.md | 25 ++++++++++-- 3 files changed, 102 insertions(+), 11 deletions(-) diff --git a/commands/rm.go b/commands/rm.go index ab19bbef..73f33be9 100644 --- a/commands/rm.go +++ b/commands/rm.go @@ -2,36 +2,53 @@ package commands import ( "context" + "time" "github.com/docker/buildx/store" "github.com/docker/buildx/store/storeutil" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" "github.com/moby/buildkit/util/appcontext" + "github.com/pkg/errors" "github.com/spf13/cobra" + "golang.org/x/sync/errgroup" ) type rmOptions struct { - builder string - keepState bool - keepDaemon bool + builder string + keepState bool + keepDaemon bool + allInactive bool + force bool } +const ( + rmInactiveWarning = `WARNING! This will remove all builders that are not in running state. Are you sure you want to continue?` +) + func runRm(dockerCli command.Cli, in rmOptions) error { ctx := appcontext.Context() + if in.allInactive && !in.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), rmInactiveWarning) { + return nil + } + txn, release, err := storeutil.GetStore(dockerCli) if err != nil { return err } defer release() + if in.allInactive { + return rmAllInactive(ctx, txn, dockerCli, in) + } + if in.builder != "" { ng, err := storeutil.GetNodeGroup(txn, dockerCli, in.builder) if err != nil { return err } - err1 := rm(ctx, dockerCli, ng, in.keepState, in.keepDaemon) + err1 := rm(ctx, dockerCli, in, ng) if err := txn.Remove(ng.Name); err != nil { return err } @@ -43,7 +60,7 @@ func runRm(dockerCli command.Cli, in rmOptions) error { return err } if ng != nil { - err1 := rm(ctx, dockerCli, ng, in.keepState, in.keepDaemon) + err1 := rm(ctx, dockerCli, in, ng) if err := txn.Remove(ng.Name); err != nil { return err } @@ -63,6 +80,9 @@ func rmCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { options.builder = rootOpts.builder if len(args) > 0 { + if options.allInactive { + return errors.New("cannot specify builder name when --all-inactive is set") + } options.builder = args[0] } return runRm(dockerCli, options) @@ -72,11 +92,13 @@ func rmCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { flags := cmd.Flags() flags.BoolVar(&options.keepState, "keep-state", false, "Keep BuildKit state") flags.BoolVar(&options.keepDaemon, "keep-daemon", false, "Keep the buildkitd daemon running") + flags.BoolVar(&options.allInactive, "all-inactive", false, "Remove all inactive builders") + flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation") return cmd } -func rm(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup, keepState, keepDaemon bool) error { +func rm(ctx context.Context, dockerCli command.Cli, in rmOptions, ng *store.NodeGroup) error { dis, err := driversForNodeGroup(ctx, dockerCli, ng, "") if err != nil { return err @@ -86,12 +108,12 @@ func rm(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup, keepSta continue } // Do not stop the buildkitd daemon when --keep-daemon is provided - if !keepDaemon { + if !in.keepDaemon { if err := di.Driver.Stop(ctx, true); err != nil { return err } } - if err := di.Driver.Rm(ctx, true, !keepState, !keepDaemon); err != nil { + if err := di.Driver.Rm(ctx, true, !in.keepState, !in.keepDaemon); err != nil { return err } if di.Err != nil { @@ -100,3 +122,42 @@ func rm(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup, keepSta } return err } + +func rmAllInactive(ctx context.Context, txn *store.Txn, dockerCli command.Cli, in rmOptions) error { + ctx, cancel := context.WithTimeout(ctx, 20*time.Second) + defer cancel() + + ll, err := txn.List() + if err != nil { + return err + } + + builders := make([]*nginfo, len(ll)) + for i, ng := range ll { + builders[i] = &nginfo{ng: ng} + } + + eg, _ := errgroup.WithContext(ctx) + for _, b := range builders { + func(b *nginfo) { + eg.Go(func() error { + if err := loadNodeGroupData(ctx, dockerCli, b); err != nil { + return errors.Wrapf(err, "cannot load %s", b.ng.Name) + } + if b.ng.Dynamic { + return nil + } + if b.inactive() { + rmerr := rm(ctx, dockerCli, in, b.ng) + if err := txn.Remove(b.ng.Name); err != nil { + return err + } + return rmerr + } + return nil + }) + }(b) + } + + return eg.Wait() +} diff --git a/commands/util.go b/commands/util.go index d4ee40ad..03c28684 100644 --- a/commands/util.go +++ b/commands/util.go @@ -389,6 +389,17 @@ type nginfo struct { err error } +// inactive checks if all nodes are inactive for this builder +func (n *nginfo) inactive() bool { + for idx := range n.ng.Nodes { + d := n.drivers[idx] + if d.info != nil && d.info.Status == driver.Running { + return false + } + } + return true +} + func boot(ctx context.Context, ngi *nginfo) (bool, error) { toBoot := make([]int, 0, len(ngi.drivers)) for i, d := range ngi.drivers { diff --git a/docs/reference/buildx_rm.md b/docs/reference/buildx_rm.md index 28ac60cb..dba7a8f2 100644 --- a/docs/reference/buildx_rm.md +++ b/docs/reference/buildx_rm.md @@ -11,7 +11,9 @@ Remove a builder instance | Name | Description | | --- | --- | +| [`--all-inactive`](#all-inactive) | Remove all inactive builders | | [`--builder string`](#builder) | Override the configured builder instance | +| [`-f`](#force), [`--force`](#force) | Do not prompt for confirmation | | [`--keep-daemon`](#keep-daemon) | Keep the buildkitd daemon running | | [`--keep-state`](#keep-state) | Keep BuildKit state | @@ -25,16 +27,33 @@ default builder. ## Examples +### Remove all inactive builders (--all-inactive) + +Remove builders that are not in running state. + +```console +$ docker buildx rm --all-inactive +WARNING! This will remove all builders that are not in running state. Are you sure you want to continue? [y/N] y +``` + ### Override the configured builder instance (--builder) Same as [`buildx --builder`](buildx.md#builder). -### Keep BuildKit state (--keep-state) +### Do not prompt for confirmation (--force) -Keep BuildKit state, so it can be reused by a new builder with the same name. -Currently, only supported by the [`docker-container` driver](buildx_create.md#driver). +Do not prompt for confirmation before removing inactive builders. + +```console +$ docker buildx rm --all-inactive --force +``` ### Keep the buildkitd daemon running (--keep-daemon) Keep the buildkitd daemon running after the buildx context is removed. This is useful when you manage buildkitd daemons and buildx contexts independently. Currently, only supported by the [`docker-container` and `kubernetes` drivers](buildx_create.md#driver). + +### Keep BuildKit state (--keep-state) + +Keep BuildKit state, so it can be reused by a new builder with the same name. +Currently, only supported by the [`docker-container` driver](buildx_create.md#driver).