github.com/pdmccormick/importable-docker-buildx@v0.0.0-20240426161518-e47091289030/commands/rm.go (about) 1 package commands 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/docker/buildx/builder" 9 "github.com/docker/buildx/store" 10 "github.com/docker/buildx/store/storeutil" 11 "github.com/docker/buildx/util/cobrautil/completion" 12 "github.com/docker/cli/cli/command" 13 "github.com/pkg/errors" 14 "github.com/spf13/cobra" 15 "golang.org/x/sync/errgroup" 16 ) 17 18 type rmOptions struct { 19 builders []string 20 keepState bool 21 keepDaemon bool 22 allInactive bool 23 force bool 24 } 25 26 const ( 27 rmInactiveWarning = `WARNING! This will remove all builders that are not in running state. Are you sure you want to continue?` 28 ) 29 30 func runRm(ctx context.Context, dockerCli command.Cli, in rmOptions) error { 31 if in.allInactive && !in.force { 32 if ok, err := prompt(ctx, dockerCli.In(), dockerCli.Out(), rmInactiveWarning); err != nil { 33 return err 34 } else if !ok { 35 return nil 36 } 37 } 38 39 txn, release, err := storeutil.GetStore(dockerCli) 40 if err != nil { 41 return err 42 } 43 defer release() 44 45 if in.allInactive { 46 return rmAllInactive(ctx, txn, dockerCli, in) 47 } 48 49 eg, _ := errgroup.WithContext(ctx) 50 for _, name := range in.builders { 51 func(name string) { 52 eg.Go(func() (err error) { 53 defer func() { 54 if err == nil { 55 _, _ = fmt.Fprintf(dockerCli.Err(), "%s removed\n", name) 56 } else { 57 _, _ = fmt.Fprintf(dockerCli.Err(), "failed to remove %s: %v\n", name, err) 58 } 59 }() 60 61 b, err := builder.New(dockerCli, 62 builder.WithName(name), 63 builder.WithStore(txn), 64 builder.WithSkippedValidation(), 65 ) 66 if err != nil { 67 return err 68 } 69 70 nodes, err := b.LoadNodes(ctx) 71 if err != nil { 72 return err 73 } 74 75 if cb := b.ContextName(); cb != "" { 76 return errors.Errorf("context builder cannot be removed, run `docker context rm %s` to remove this context", cb) 77 } 78 79 err1 := rm(ctx, nodes, in) 80 if err := txn.Remove(b.Name); err != nil { 81 return err 82 } 83 if err1 != nil { 84 return err1 85 } 86 87 return nil 88 }) 89 }(name) 90 } 91 92 if err := eg.Wait(); err != nil { 93 return errors.New("failed to remove one or more builders") 94 } 95 return nil 96 } 97 98 func rmCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { 99 var options rmOptions 100 101 cmd := &cobra.Command{ 102 Use: "rm [OPTIONS] [NAME] [NAME...]", 103 Short: "Remove one or more builder instances", 104 RunE: func(cmd *cobra.Command, args []string) error { 105 options.builders = []string{rootOpts.builder} 106 if len(args) > 0 { 107 if options.allInactive { 108 return errors.New("cannot specify builder name when --all-inactive is set") 109 } 110 options.builders = args 111 } 112 return runRm(cmd.Context(), dockerCli, options) 113 }, 114 ValidArgsFunction: completion.BuilderNames(dockerCli), 115 } 116 117 flags := cmd.Flags() 118 flags.BoolVar(&options.keepState, "keep-state", false, "Keep BuildKit state") 119 flags.BoolVar(&options.keepDaemon, "keep-daemon", false, "Keep the BuildKit daemon running") 120 flags.BoolVar(&options.allInactive, "all-inactive", false, "Remove all inactive builders") 121 flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation") 122 123 return cmd 124 } 125 126 func rm(ctx context.Context, nodes []builder.Node, in rmOptions) (err error) { 127 for _, node := range nodes { 128 if node.Driver == nil { 129 continue 130 } 131 // Do not stop the buildkitd daemon when --keep-daemon is provided 132 if !in.keepDaemon { 133 if err := node.Driver.Stop(ctx, true); err != nil { 134 return err 135 } 136 } 137 if err := node.Driver.Rm(ctx, true, !in.keepState, !in.keepDaemon); err != nil { 138 return err 139 } 140 if node.Err != nil { 141 err = node.Err 142 } 143 } 144 return err 145 } 146 147 func rmAllInactive(ctx context.Context, txn *store.Txn, dockerCli command.Cli, in rmOptions) error { 148 builders, err := builder.GetBuilders(dockerCli, txn) 149 if err != nil { 150 return err 151 } 152 153 timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second) 154 defer cancel() 155 156 eg, _ := errgroup.WithContext(timeoutCtx) 157 for _, b := range builders { 158 func(b *builder.Builder) { 159 eg.Go(func() error { 160 nodes, err := b.LoadNodes(timeoutCtx, builder.WithData()) 161 if err != nil { 162 return errors.Wrapf(err, "cannot load %s", b.Name) 163 } 164 if b.Dynamic { 165 return nil 166 } 167 if b.Inactive() { 168 rmerr := rm(ctx, nodes, in) 169 if err := txn.Remove(b.Name); err != nil { 170 return err 171 } 172 _, _ = fmt.Fprintf(dockerCli.Err(), "%s removed\n", b.Name) 173 return rmerr 174 } 175 return nil 176 }) 177 }(b) 178 } 179 180 return eg.Wait() 181 }