2019-04-12 08:24:24 +08:00
package commands
import (
"fmt"
2019-04-13 07:39:06 +08:00
"os"
2019-04-12 08:24:24 +08:00
2019-04-25 10:29:56 +08:00
"github.com/docker/buildx/driver"
"github.com/docker/buildx/store"
2019-04-12 08:24:24 +08:00
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
2019-07-09 06:06:07 +08:00
"github.com/google/shlex"
2019-04-13 07:39:06 +08:00
"github.com/moby/buildkit/util/appcontext"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
2019-04-12 08:24:24 +08:00
"github.com/spf13/cobra"
)
type createOptions struct {
name string
driver string
nodeName string
platform [ ] string
actionAppend bool
actionLeave bool
2019-04-13 07:39:06 +08:00
use bool
2019-07-09 06:06:07 +08:00
flags string
2019-08-01 01:54:20 +08:00
configFile string
2019-04-12 08:24:24 +08:00
// upgrade bool // perform upgrade of the driver
}
2019-04-13 07:39:06 +08:00
func runCreate ( dockerCli command . Cli , in createOptions , args [ ] string ) error {
ctx := appcontext . Context ( )
if in . name == "default" {
2019-04-16 08:01:00 +08:00
return errors . Errorf ( "default is a reserved name and cannot be used to identify builder instance" )
2019-04-13 07:39:06 +08:00
}
if in . actionLeave {
if in . name == "" {
return errors . Errorf ( "leave requires instance name" )
}
if in . nodeName == "" {
return errors . Errorf ( "leave requires node name but --node not set" )
}
}
if in . actionAppend {
if in . name == "" {
logrus . Warnf ( "append used without name, creating a new instance instead" )
}
}
driverName := in . driver
if driverName == "" {
f , err := driver . GetDefaultFactory ( ctx , dockerCli . Client ( ) , true )
if err != nil {
return err
}
if f == nil {
return errors . Errorf ( "no valid drivers found" )
}
driverName = f . Name ( )
}
if driver . GetFactory ( driverName , true ) == nil {
return errors . Errorf ( "failed to find driver %q" , in . driver )
}
txn , release , err := getStore ( dockerCli )
if err != nil {
return err
}
defer release ( )
name := in . name
if name == "" {
name , err = store . GenerateName ( txn )
if err != nil {
return err
}
}
ng , err := txn . NodeGroupByName ( name )
if err != nil {
if os . IsNotExist ( errors . Cause ( err ) ) {
if in . actionAppend && in . name != "" {
logrus . Warnf ( "failed to find %q for append, creating a new instance instead" , in . name )
}
if in . actionLeave {
return errors . Errorf ( "failed to find instance %q for leave" , name )
}
} else {
return err
}
}
2019-04-16 08:01:00 +08:00
if ng != nil {
if in . nodeName == "" && ! in . actionAppend {
return errors . Errorf ( "existing instance for %s but no append mode, specify --node to make changes for existing instances" , name )
}
}
2019-04-13 07:39:06 +08:00
if ng == nil {
ng = & store . NodeGroup {
Name : name ,
}
}
if ng . Driver == "" || in . driver != "" {
ng . Driver = driverName
}
2019-07-09 06:06:07 +08:00
var flags [ ] string
if in . flags != "" {
flags , err = shlex . Split ( in . flags )
if err != nil {
return errors . Wrap ( err , "failed to parse buildkit flags" )
}
}
2019-04-13 07:39:06 +08:00
var ep string
if in . actionLeave {
if err := ng . Leave ( in . nodeName ) ; err != nil {
return err
}
} else {
if len ( args ) > 0 {
ep , err = validateEndpoint ( dockerCli , args [ 0 ] )
if err != nil {
return err
}
} else {
2019-04-23 05:26:18 +08:00
if dockerCli . CurrentContext ( ) == "default" && dockerCli . DockerEndpoint ( ) . TLSData != nil {
return errors . Errorf ( "could not create a builder instance with TLS data loaded from environment. Please use `docker context create <context-name>` to create a context for current environment and then create a builder instance with `docker buildx create <context-name>`" )
}
2019-04-13 07:39:06 +08:00
ep , err = getCurrentEndpoint ( dockerCli )
if err != nil {
return err
}
}
2019-08-01 01:54:20 +08:00
if err := ng . Update ( in . nodeName , ep , in . platform , len ( args ) > 0 , in . actionAppend , flags , in . configFile ) ; err != nil {
2019-04-13 07:39:06 +08:00
return err
}
}
if err := txn . Save ( ng ) ; err != nil {
return err
}
if in . use && ep != "" {
2019-04-16 01:21:09 +08:00
current , err := getCurrentEndpoint ( dockerCli )
if err != nil {
return err
}
if err := txn . SetCurrent ( current , ng . Name , false , false ) ; err != nil {
2019-04-13 07:39:06 +08:00
return err
}
}
fmt . Printf ( "%s\n" , ng . Name )
2019-04-12 08:24:24 +08:00
return nil
}
func createCmd ( dockerCli command . Cli ) * cobra . Command {
var options createOptions
2019-05-26 06:45:20 +08:00
var drivers [ ] string
for s := range driver . GetFactories ( ) {
drivers = append ( drivers , s )
}
2019-04-12 08:24:24 +08:00
cmd := & cobra . Command {
Use : "create [OPTIONS] [CONTEXT|ENDPOINT]" ,
Short : "Create a new builder instance" ,
Args : cli . RequiresMaxArgs ( 1 ) ,
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
2019-04-13 07:39:06 +08:00
return runCreate ( dockerCli , options , args )
2019-04-12 08:24:24 +08:00
} ,
}
flags := cmd . Flags ( )
flags . StringVar ( & options . name , "name" , "" , "Builder instance name" )
2019-05-26 06:45:20 +08:00
flags . StringVar ( & options . driver , "driver" , "" , fmt . Sprintf ( "Driver to use (available: %v)" , drivers ) )
2019-04-12 08:24:24 +08:00
flags . StringVar ( & options . nodeName , "node" , "" , "Create/modify node with given name" )
2019-07-09 06:06:07 +08:00
flags . StringVar ( & options . flags , "buildkitd-flags" , "" , "Flags for buildkitd daemon" )
2019-08-01 01:54:20 +08:00
flags . StringVar ( & options . configFile , "config" , "" , "BuildKit config file" )
2019-04-12 08:24:24 +08:00
flags . StringArrayVar ( & options . platform , "platform" , [ ] string { } , "Fixed platforms for current node" )
flags . BoolVar ( & options . actionAppend , "append" , false , "Append a node to builder instead of changing it" )
flags . BoolVar ( & options . actionLeave , "leave" , false , "Remove a node from builder instead of changing it" )
2019-04-13 07:39:06 +08:00
flags . BoolVar ( & options . use , "use" , false , "Set the current builder instance" )
2019-04-12 08:24:24 +08:00
_ = flags
return cmd
}