2019-04-12 08:24:24 +08:00
package commands
import (
2019-04-13 07:39:06 +08:00
"context"
2022-05-15 12:10:42 +08:00
"fmt"
2022-02-03 20:39:44 +08:00
"time"
2019-04-13 07:39:06 +08:00
2022-12-06 02:57:35 +08:00
"github.com/docker/buildx/builder"
2019-04-25 10:29:56 +08:00
"github.com/docker/buildx/store"
2021-11-04 12:17:27 +08:00
"github.com/docker/buildx/store/storeutil"
2023-04-10 03:20:18 +08:00
"github.com/docker/buildx/util/cobrautil/completion"
2019-04-12 08:24:24 +08:00
"github.com/docker/cli/cli/command"
2022-02-03 20:39:44 +08:00
"github.com/pkg/errors"
2019-04-12 08:24:24 +08:00
"github.com/spf13/cobra"
2022-02-03 20:39:44 +08:00
"golang.org/x/sync/errgroup"
2019-04-12 08:24:24 +08:00
)
type rmOptions struct {
2023-11-25 07:28:50 +08:00
builders [ ] string
2022-02-03 20:39:44 +08:00
keepState bool
keepDaemon bool
allInactive bool
force bool
2019-04-12 08:24:24 +08:00
}
2022-02-03 20:39:44 +08:00
const (
rmInactiveWarning = ` WARNING! This will remove all builders that are not in running state. Are you sure you want to continue? `
)
2024-01-05 20:11:53 +08:00
func runRm ( ctx context . Context , dockerCli command . Cli , in rmOptions ) error {
2024-02-28 09:30:51 +08:00
if in . allInactive && ! in . force {
if ok , err := prompt ( ctx , dockerCli . In ( ) , dockerCli . Out ( ) , rmInactiveWarning ) ; err != nil {
return err
} else if ! ok {
return nil
}
2022-02-03 20:39:44 +08:00
}
2021-11-04 12:17:27 +08:00
txn , release , err := storeutil . GetStore ( dockerCli )
2019-04-13 07:39:06 +08:00
if err != nil {
return err
}
defer release ( )
2022-02-03 20:39:44 +08:00
if in . allInactive {
return rmAllInactive ( ctx , txn , dockerCli , in )
}
2023-11-25 07:28:50 +08:00
eg , _ := errgroup . WithContext ( ctx )
for _ , name := range in . builders {
func ( name string ) {
eg . Go ( func ( ) ( err error ) {
defer func ( ) {
if err == nil {
_ , _ = fmt . Fprintf ( dockerCli . Err ( ) , "%s removed\n" , name )
} else {
_ , _ = fmt . Fprintf ( dockerCli . Err ( ) , "failed to remove %s: %v\n" , name , err )
}
} ( )
b , err := builder . New ( dockerCli ,
builder . WithName ( name ) ,
builder . WithStore ( txn ) ,
builder . WithSkippedValidation ( ) ,
)
if err != nil {
return err
}
2019-04-13 07:39:06 +08:00
2023-11-25 07:28:50 +08:00
nodes , err := b . LoadNodes ( ctx )
if err != nil {
return err
}
2022-12-06 02:57:35 +08:00
2023-11-25 07:28:50 +08:00
if cb := b . ContextName ( ) ; cb != "" {
return errors . Errorf ( "context builder cannot be removed, run `docker context rm %s` to remove this context" , cb )
}
2019-04-13 07:39:06 +08:00
2023-11-25 07:28:50 +08:00
err1 := rm ( ctx , nodes , in )
if err := txn . Remove ( b . Name ) ; err != nil {
return err
}
if err1 != nil {
return err1
}
return nil
} )
} ( name )
2022-05-15 12:10:42 +08:00
}
2023-11-25 07:28:50 +08:00
if err := eg . Wait ( ) ; err != nil {
return errors . New ( "failed to remove one or more builders" )
}
2022-05-15 12:10:42 +08:00
return nil
2019-04-12 08:24:24 +08:00
}
2020-04-28 05:37:17 +08:00
func rmCmd ( dockerCli command . Cli , rootOpts * rootOptions ) * cobra . Command {
2019-04-12 08:24:24 +08:00
var options rmOptions
cmd := & cobra . Command {
2023-11-25 07:28:50 +08:00
Use : "rm [OPTIONS] [NAME] [NAME...]" ,
Short : "Remove one or more builder instances" ,
2024-01-20 08:44:30 +08:00
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
2023-11-25 07:28:50 +08:00
options . builders = [ ] string { rootOpts . builder }
2020-04-28 05:37:17 +08:00
if len ( args ) > 0 {
2022-02-03 20:39:44 +08:00
if options . allInactive {
return errors . New ( "cannot specify builder name when --all-inactive is set" )
}
2023-11-25 07:28:50 +08:00
options . builders = args
2020-04-28 05:37:17 +08:00
}
2024-01-05 20:11:53 +08:00
return runRm ( cmd . Context ( ) , dockerCli , options )
2024-01-20 08:44:30 +08:00
} ,
2023-04-11 17:45:59 +08:00
ValidArgsFunction : completion . BuilderNames ( dockerCli ) ,
2019-04-12 08:24:24 +08:00
}
2021-07-14 00:09:30 +08:00
flags := cmd . Flags ( )
flags . BoolVar ( & options . keepState , "keep-state" , false , "Keep BuildKit state" )
2024-02-21 22:16:21 +08:00
flags . BoolVar ( & options . keepDaemon , "keep-daemon" , false , "Keep the BuildKit daemon running" )
2022-02-03 20:39:44 +08:00
flags . BoolVar ( & options . allInactive , "all-inactive" , false , "Remove all inactive builders" )
flags . BoolVarP ( & options . force , "force" , "f" , false , "Do not prompt for confirmation" )
2021-07-14 00:09:30 +08:00
2019-04-13 07:39:06 +08:00
return cmd
}
2019-04-12 08:24:24 +08:00
2022-12-06 02:57:35 +08:00
func rm ( ctx context . Context , nodes [ ] builder . Node , in rmOptions ) ( err error ) {
for _ , node := range nodes {
if node . Driver == nil {
2021-11-17 20:56:42 +08:00
continue
}
// Do not stop the buildkitd daemon when --keep-daemon is provided
2022-02-03 20:39:44 +08:00
if ! in . keepDaemon {
2022-12-06 02:57:35 +08:00
if err := node . Driver . Stop ( ctx , true ) ; err != nil {
2019-04-13 07:39:06 +08:00
return err
}
2021-11-17 20:56:42 +08:00
}
2022-12-06 02:57:35 +08:00
if err := node . Driver . Rm ( ctx , true , ! in . keepState , ! in . keepDaemon ) ; err != nil {
2021-11-17 20:56:42 +08:00
return err
2019-04-13 07:39:06 +08:00
}
2022-12-06 02:57:35 +08:00
if node . Err != nil {
err = node . Err
2019-04-13 07:39:06 +08:00
}
}
return err
2019-04-12 08:24:24 +08:00
}
2022-02-03 20:39:44 +08:00
func rmAllInactive ( ctx context . Context , txn * store . Txn , dockerCli command . Cli , in rmOptions ) error {
2022-12-06 02:57:35 +08:00
builders , err := builder . GetBuilders ( dockerCli , txn )
2022-02-03 20:39:44 +08:00
if err != nil {
return err
}
2024-11-20 10:21:44 +08:00
timeoutCtx , cancel := context . WithCancelCause ( ctx )
2024-11-07 20:54:05 +08:00
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
2024-11-20 10:21:44 +08:00
defer func ( ) { cancel ( errors . WithStack ( context . Canceled ) ) } ( )
2022-02-03 20:39:44 +08:00
2022-12-06 02:57:35 +08:00
eg , _ := errgroup . WithContext ( timeoutCtx )
2022-02-03 20:39:44 +08:00
for _ , b := range builders {
2022-12-06 02:57:35 +08:00
func ( b * builder . Builder ) {
2022-02-03 20:39:44 +08:00
eg . Go ( func ( ) error {
2023-08-23 04:11:50 +08:00
nodes , err := b . LoadNodes ( timeoutCtx , builder . WithData ( ) )
2022-12-06 02:57:35 +08:00
if err != nil {
return errors . Wrapf ( err , "cannot load %s" , b . Name )
}
if b . Dynamic {
2022-02-03 20:39:44 +08:00
return nil
}
2022-12-06 02:57:35 +08:00
if b . Inactive ( ) {
rmerr := rm ( ctx , nodes , in )
if err := txn . Remove ( b . Name ) ; err != nil {
2022-02-03 20:39:44 +08:00
return err
}
2022-12-06 02:57:35 +08:00
_ , _ = fmt . Fprintf ( dockerCli . Err ( ) , "%s removed\n" , b . Name )
2022-02-03 20:39:44 +08:00
return rmerr
}
return nil
} )
} ( b )
}
return eg . Wait ( )
}