Ver Fonte

Merge pull request #20639 from dnephin/split_client

Split the binary into two: client and daemon
John Howard há 9 anos atrás
pai
commit
27f44b8af6

+ 1 - 2
Makefile

@@ -8,14 +8,13 @@ DOCKER_OSARCH := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo
 DOCKERFILE := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $${DOCKERFILE}')
 
 # env vars passed through directly to Docker's build scripts
-# to allow things like `make DOCKER_CLIENTONLY=1 binary` easily
+# to allow things like `make KEEPBUNDLE=1 binary` easily
 # `docs/sources/contributing/devenvironment.md ` and `project/PACKAGERS.md` have some limited documentation of some of these
 DOCKER_ENVS := \
 	-e BUILDFLAGS \
 	-e KEEPBUNDLE \
 	-e DOCKER_BUILD_GOGC \
 	-e DOCKER_BUILD_PKGS \
-	-e DOCKER_CLIENTONLY \
 	-e DOCKER_DEBUG \
 	-e DOCKER_EXPERIMENTAL \
 	-e DOCKER_GITCOMMIT \

+ 28 - 18
docker/common.go → cli/flags/common.go

@@ -1,4 +1,4 @@
-package main
+package flags
 
 import (
 	"fmt"
@@ -14,26 +14,32 @@ import (
 )
 
 const (
-	defaultTrustKeyFile = "key.json"
-	defaultCaFile       = "ca.pem"
-	defaultKeyFile      = "key.pem"
-	defaultCertFile     = "cert.pem"
-	tlsVerifyKey        = "tlsverify"
+	// DefaultTrustKeyFile is the default filename for the trust key
+	DefaultTrustKeyFile = "key.json"
+	// DefaultCaFile is the default filename for the CA pem file
+	DefaultCaFile = "ca.pem"
+	// DefaultKeyFile is the default filename for the key pem file
+	DefaultKeyFile = "key.pem"
+	// DefaultCertFile is the default filename for the cert pem file
+	DefaultCertFile = "cert.pem"
+	// TLSVerifyKey is the default flag name for the tls verification option
+	TLSVerifyKey = "tlsverify"
 )
 
 var (
-	commonFlags = &cli.CommonFlags{FlagSet: new(flag.FlagSet)}
-
 	dockerCertPath  = os.Getenv("DOCKER_CERT_PATH")
 	dockerTLSVerify = os.Getenv("DOCKER_TLS_VERIFY") != ""
 )
 
-func init() {
+// InitCommonFlags initializes flags common to both client and daemon
+func InitCommonFlags() *cli.CommonFlags {
+	var commonFlags = &cli.CommonFlags{FlagSet: new(flag.FlagSet)}
+
 	if dockerCertPath == "" {
 		dockerCertPath = cliconfig.ConfigDir()
 	}
 
-	commonFlags.PostParse = postParseCommon
+	commonFlags.PostParse = func() { postParseCommon(commonFlags) }
 
 	cmd := commonFlags.FlagSet
 
@@ -46,22 +52,24 @@ func init() {
 
 	var tlsOptions tlsconfig.Options
 	commonFlags.TLSOptions = &tlsOptions
-	cmd.StringVar(&tlsOptions.CAFile, []string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust certs signed only by this CA")
-	cmd.StringVar(&tlsOptions.CertFile, []string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file")
-	cmd.StringVar(&tlsOptions.KeyFile, []string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file")
+	cmd.StringVar(&tlsOptions.CAFile, []string{"-tlscacert"}, filepath.Join(dockerCertPath, DefaultCaFile), "Trust certs signed only by this CA")
+	cmd.StringVar(&tlsOptions.CertFile, []string{"-tlscert"}, filepath.Join(dockerCertPath, DefaultCertFile), "Path to TLS certificate file")
+	cmd.StringVar(&tlsOptions.KeyFile, []string{"-tlskey"}, filepath.Join(dockerCertPath, DefaultKeyFile), "Path to TLS key file")
 
 	cmd.Var(opts.NewNamedListOptsRef("hosts", &commonFlags.Hosts, opts.ValidateHost), []string{"H", "-host"}, "Daemon socket(s) to connect to")
+	return commonFlags
 }
 
-func postParseCommon() {
+func postParseCommon(commonFlags *cli.CommonFlags) {
 	cmd := commonFlags.FlagSet
 
-	setDaemonLogLevel(commonFlags.LogLevel)
+	SetDaemonLogLevel(commonFlags.LogLevel)
 
 	// Regardless of whether the user sets it to true or false, if they
 	// specify --tlsverify at all then we need to turn on tls
-	// TLSVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need to check that here as well
-	if cmd.IsSet("-"+tlsVerifyKey) || commonFlags.TLSVerify {
+	// TLSVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need
+	// to check that here as well
+	if cmd.IsSet("-"+TLSVerifyKey) || commonFlags.TLSVerify {
 		commonFlags.TLS = true
 	}
 
@@ -86,7 +94,9 @@ func postParseCommon() {
 	}
 }
 
-func setDaemonLogLevel(logLevel string) {
+// SetDaemonLogLevel sets the logrus logging level
+// TODO: this is a bad name, it applies to the client as well.
+func SetDaemonLogLevel(logLevel string) {
 	if logLevel != "" {
 		lvl, err := logrus.ParseLevel(logLevel)
 		if err != nil {

+ 7 - 2
docker/client.go → client/client.go

@@ -4,14 +4,19 @@ import (
 	"path/filepath"
 
 	"github.com/docker/docker/cli"
+	cliflags "github.com/docker/docker/cli/flags"
 	"github.com/docker/docker/cliconfig"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/utils"
 )
 
-var clientFlags = &cli.ClientFlags{FlagSet: new(flag.FlagSet), Common: commonFlags}
+var (
+	commonFlags = cliflags.InitCommonFlags()
+	clientFlags = &cli.ClientFlags{FlagSet: new(flag.FlagSet), Common: commonFlags}
+)
 
 func init() {
+
 	client := clientFlags.FlagSet
 	client.StringVar(&clientFlags.ConfigDir, []string{"-config"}, cliconfig.ConfigDir(), "Location of client config files")
 
@@ -23,7 +28,7 @@ func init() {
 		}
 
 		if clientFlags.Common.TrustKey == "" {
-			clientFlags.Common.TrustKey = filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile)
+			clientFlags.Common.TrustKey = filepath.Join(cliconfig.ConfigDir(), cliflags.DefaultTrustKeyFile)
 		}
 
 		if clientFlags.Common.Debug {

+ 0 - 0
docker/client_test.go → client/client_test.go


+ 11 - 0
client/daemon.go

@@ -0,0 +1,11 @@
+package main
+
+const daemonBinary = "dockerd"
+
+// DaemonProxy acts as a cli.Handler to proxy calls to the daemon binary
+type DaemonProxy struct{}
+
+// NewDaemonProxy returns a new handler
+func NewDaemonProxy() DaemonProxy {
+	return DaemonProxy{}
+}

+ 37 - 0
client/daemon_unix.go

@@ -0,0 +1,37 @@
+// +build !windows
+
+package main
+
+import (
+	"os"
+	"os/exec"
+	"syscall"
+)
+
+// CmdDaemon execs dockerd with the same flags
+// TODO: add a deprecation warning?
+func (p DaemonProxy) CmdDaemon(args ...string) error {
+	// Use os.Args[1:] so that "global" args are passed to dockerd
+	args = stripDaemonArg(os.Args[1:])
+
+	// TODO: check dirname args[0] first
+	binaryAbsPath, err := exec.LookPath(daemonBinary)
+	if err != nil {
+		return err
+	}
+
+	return syscall.Exec(
+		binaryAbsPath,
+		append([]string{daemonBinary}, args...),
+		os.Environ())
+}
+
+// stripDaemonArg removes the `daemon` argument from the list
+func stripDaemonArg(args []string) []string {
+	for i, arg := range args {
+		if arg == "daemon" {
+			return append(args[:i], args[i+1:]...)
+		}
+	}
+	return args
+}

+ 11 - 0
client/daemon_windows.go

@@ -0,0 +1,11 @@
+package main
+
+import (
+	"fmt"
+)
+
+// CmdDaemon reports on an error on windows, because there is no exec
+func (p DaemonProxy) CmdDaemon(args ...string) error {
+	return fmt.Errorf(
+		"`docker daemon` does not exist on windows. Please run `dockerd` directly")
+}

+ 18 - 0
client/daemon_windows_test.go

@@ -0,0 +1,18 @@
+package main
+
+import (
+	"strings"
+	"testing"
+)
+
+func TestCmdDaemon(t *testing.T) {
+	proxy := NewDaemonProxy()
+	err := proxy.CmdDaemon("--help")
+	if err == nil {
+		t.Fatal("Expected CmdDaemon to fail in Windows.")
+	}
+
+	if !strings.Contains(err.Error(), "Please run `dockerd`") {
+		t.Fatalf("Expected an error about running dockerd, got %s", err)
+	}
+}

+ 77 - 0
client/docker.go

@@ -0,0 +1,77 @@
+package main
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/api/client"
+	"github.com/docker/docker/cli"
+	"github.com/docker/docker/dockerversion"
+	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/docker/docker/pkg/term"
+	"github.com/docker/docker/utils"
+)
+
+func main() {
+	// Set terminal emulation based on platform as required.
+	stdin, stdout, stderr := term.StdStreams()
+
+	logrus.SetOutput(stderr)
+
+	flag.Merge(flag.CommandLine, clientFlags.FlagSet, commonFlags.FlagSet)
+
+	flag.Usage = func() {
+		fmt.Fprint(stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n       docker [ --help | -v | --version ]\n\n")
+		fmt.Fprint(stdout, "A self-sufficient runtime for containers.\n\nOptions:\n")
+
+		flag.CommandLine.SetOutput(stdout)
+		flag.PrintDefaults()
+
+		help := "\nCommands:\n"
+
+		for _, cmd := range dockerCommands {
+			help += fmt.Sprintf("    %-10.10s%s\n", cmd.Name, cmd.Description)
+		}
+
+		help += "\nRun 'docker COMMAND --help' for more information on a command."
+		fmt.Fprintf(stdout, "%s\n", help)
+	}
+
+	flag.Parse()
+
+	if *flVersion {
+		showVersion()
+		return
+	}
+
+	if *flHelp {
+		// if global flag --help is present, regardless of what other options and commands there are,
+		// just print the usage.
+		flag.Usage()
+		return
+	}
+
+	clientCli := client.NewDockerCli(stdin, stdout, stderr, clientFlags)
+
+	c := cli.New(clientCli, NewDaemonProxy())
+	if err := c.Run(flag.Args()...); err != nil {
+		if sterr, ok := err.(cli.StatusError); ok {
+			if sterr.Status != "" {
+				fmt.Fprintln(stderr, sterr.Status)
+				os.Exit(1)
+			}
+			os.Exit(sterr.StatusCode)
+		}
+		fmt.Fprintln(stderr, err)
+		os.Exit(1)
+	}
+}
+
+func showVersion() {
+	if utils.ExperimentalBuild() {
+		fmt.Printf("Docker version %s, build %s, experimental\n", dockerversion.Version, dockerversion.GitCommit)
+	} else {
+		fmt.Printf("Docker version %s, build %s\n", dockerversion.Version, dockerversion.GitCommit)
+	}
+}

+ 5 - 0
client/docker_windows.go

@@ -0,0 +1,5 @@
+package main
+
+import (
+	_ "github.com/docker/docker/autogen/winresources"
+)

+ 0 - 0
docker/flags.go → client/flags.go


+ 0 - 0
docker/flags_test.go → client/flags_test.go


+ 24 - 61
docker/daemon.go

@@ -1,5 +1,3 @@
-// +build daemon
-
 package main
 
 import (
@@ -26,6 +24,7 @@ import (
 	"github.com/docker/docker/api/server/router/volume"
 	"github.com/docker/docker/builder/dockerfile"
 	"github.com/docker/docker/cli"
+	cliflags "github.com/docker/docker/cli/flags"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/daemon/logger"
@@ -46,18 +45,14 @@ import (
 )
 
 const (
-	daemonUsage          = "       docker daemon [ --help | ... ]\n"
 	daemonConfigFileFlag = "-config-file"
 )
 
-var (
-	daemonCli cli.Handler = NewDaemonCli()
-)
-
 // DaemonCli represents the daemon CLI.
 type DaemonCli struct {
 	*daemon.Config
-	flags *flag.FlagSet
+	commonFlags *cli.CommonFlags
+	configFile  *string
 }
 
 func presentInHelp(usage string) string { return usage }
@@ -65,8 +60,6 @@ func absentFromHelp(string) string      { return "" }
 
 // NewDaemonCli returns a pre-configured daemon CLI
 func NewDaemonCli() *DaemonCli {
-	daemonFlags := cli.Subcmd("daemon", nil, "Enable daemon mode", true)
-
 	// TODO(tiborvass): remove InstallFlags?
 	daemonConfig := new(daemon.Config)
 	daemonConfig.LogConfig.Config = make(map[string]string)
@@ -76,20 +69,21 @@ func NewDaemonCli() *DaemonCli {
 		daemonConfig.V2Only = true
 	}
 
-	daemonConfig.InstallFlags(daemonFlags, presentInHelp)
-	daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp)
-	daemonFlags.Require(flag.Exact, 0)
+	daemonConfig.InstallFlags(flag.CommandLine, presentInHelp)
+	configFile := flag.CommandLine.String([]string{daemonConfigFileFlag}, defaultDaemonConfigFile, "Daemon configuration file")
+	flag.CommandLine.Require(flag.Exact, 0)
 
 	return &DaemonCli{
-		Config: daemonConfig,
-		flags:  daemonFlags,
+		Config:      daemonConfig,
+		commonFlags: cliflags.InitCommonFlags(),
+		configFile:  configFile,
 	}
 }
 
 func migrateKey() (err error) {
 	// Migrate trust key if exists at ~/.docker/key.json and owned by current user
-	oldPath := filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile)
-	newPath := filepath.Join(getDaemonConfDir(), defaultTrustKeyFile)
+	oldPath := filepath.Join(cliconfig.ConfigDir(), cliflags.DefaultTrustKeyFile)
+	newPath := filepath.Join(getDaemonConfDir(), cliflags.DefaultTrustKeyFile)
 	if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) {
 		defer func() {
 			// Ensure old path is removed if no error occurred
@@ -127,47 +121,17 @@ func migrateKey() (err error) {
 	return nil
 }
 
-func getGlobalFlag() (globalFlag *flag.Flag) {
-	defer func() {
-		if x := recover(); x != nil {
-			switch f := x.(type) {
-			case *flag.Flag:
-				globalFlag = f
-			default:
-				panic(x)
-			}
-		}
-	}()
-	visitor := func(f *flag.Flag) { panic(f) }
-	commonFlags.FlagSet.Visit(visitor)
-	clientFlags.FlagSet.Visit(visitor)
-	return
-}
-
-// CmdDaemon is the daemon command, called the raw arguments after `docker daemon`.
-func (cli *DaemonCli) CmdDaemon(args ...string) error {
+func (cli *DaemonCli) start() {
 	// warn from uuid package when running the daemon
 	uuid.Loggerf = logrus.Warnf
 
-	if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() {
-		// deny `docker -D daemon`
-		illegalFlag := getGlobalFlag()
-		fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0])
-		os.Exit(1)
-	} else {
-		// allow new form `docker daemon -D`
-		flag.Merge(cli.flags, commonFlags.FlagSet)
-	}
+	flags := flag.CommandLine
+	cli.commonFlags.PostParse()
 
-	configFile := cli.flags.String([]string{daemonConfigFileFlag}, defaultDaemonConfigFile, "Daemon configuration file")
-
-	cli.flags.ParseFlags(args, true)
-	commonFlags.PostParse()
-
-	if commonFlags.TrustKey == "" {
-		commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile)
+	if cli.commonFlags.TrustKey == "" {
+		cli.commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), cliflags.DefaultTrustKeyFile)
 	}
-	cliConfig, err := loadDaemonCliConfig(cli.Config, cli.flags, commonFlags, *configFile)
+	cliConfig, err := loadDaemonCliConfig(cli.Config, flags, cli.commonFlags, *cli.configFile)
 	if err != nil {
 		fmt.Fprint(os.Stderr, err)
 		os.Exit(1)
@@ -278,7 +242,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
 	if err := migrateKey(); err != nil {
 		logrus.Fatal(err)
 	}
-	cli.TrustKeyPath = commonFlags.TrustKey
+	cli.TrustKeyPath = cli.commonFlags.TrustKey
 
 	registryService := registry.NewService(cli.Config.ServiceOptions)
 	containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...)
@@ -326,7 +290,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
 		}
 	}
 
-	setupConfigReloadTrap(*configFile, cli.flags, reload)
+	setupConfigReloadTrap(*cli.configFile, flags, reload)
 
 	// The serve API routine never exits unless an error occurs
 	// We need to start it as a goroutine and wait on it so
@@ -361,7 +325,6 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
 		}
 		logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI)
 	}
-	return nil
 }
 
 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
@@ -381,7 +344,7 @@ func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) {
 	}
 }
 
-func loadDaemonCliConfig(config *daemon.Config, daemonFlags *flag.FlagSet, commonConfig *cli.CommonFlags, configFile string) (*daemon.Config, error) {
+func loadDaemonCliConfig(config *daemon.Config, flags *flag.FlagSet, commonConfig *cli.CommonFlags, configFile string) (*daemon.Config, error) {
 	config.Debug = commonConfig.Debug
 	config.Hosts = commonConfig.Hosts
 	config.LogLevel = commonConfig.LogLevel
@@ -396,9 +359,9 @@ func loadDaemonCliConfig(config *daemon.Config, daemonFlags *flag.FlagSet, commo
 	}
 
 	if configFile != "" {
-		c, err := daemon.MergeDaemonConfigurations(config, daemonFlags, configFile)
+		c, err := daemon.MergeDaemonConfigurations(config, flags, configFile)
 		if err != nil {
-			if daemonFlags.IsSet(daemonConfigFileFlag) || !os.IsNotExist(err) {
+			if flags.IsSet(daemonConfigFileFlag) || !os.IsNotExist(err) {
 				return nil, fmt.Errorf("unable to configure the Docker daemon with file %s: %v\n", configFile, err)
 			}
 		}
@@ -411,12 +374,12 @@ func loadDaemonCliConfig(config *daemon.Config, daemonFlags *flag.FlagSet, commo
 
 	// Regardless of whether the user sets it to true or false, if they
 	// specify TLSVerify at all then we need to turn on TLS
-	if config.IsValueSet(tlsVerifyKey) {
+	if config.IsValueSet(cliflags.TLSVerifyKey) {
 		config.TLS = true
 	}
 
 	// ensure that the log level is the one set after merging configurations
-	setDaemonLogLevel(config.LogLevel)
+	cliflags.SetDaemonLogLevel(config.LogLevel)
 
 	return config, nil
 }

+ 0 - 2
docker/daemon_freebsd.go

@@ -1,5 +1,3 @@
-// +build daemon
-
 package main
 
 // notifySystem sends a message to the host when the server is ready to be used

+ 0 - 2
docker/daemon_linux.go

@@ -1,5 +1,3 @@
-// +build daemon
-
 package main
 
 import (

+ 0 - 9
docker/daemon_none.go

@@ -1,9 +0,0 @@
-// +build !daemon
-
-package main
-
-import "github.com/docker/docker/cli"
-
-const daemonUsage = ""
-
-var daemonCli cli.Handler

+ 0 - 2
docker/daemon_test.go

@@ -1,5 +1,3 @@
-// +build daemon
-
 package main
 
 import (

+ 1 - 1
docker/daemon_unix.go

@@ -1,4 +1,4 @@
-// +build daemon,!windows
+// +build !windows
 
 package main
 

+ 1 - 1
docker/daemon_unix_test.go

@@ -1,4 +1,4 @@
-// +build daemon,!windows
+// +build !windows
 
 package main
 

+ 0 - 2
docker/daemon_windows.go

@@ -1,5 +1,3 @@
-// +build daemon
-
 package main
 
 import (

+ 16 - 30
docker/docker.go

@@ -5,8 +5,6 @@ import (
 	"os"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/docker/api/client"
-	"github.com/docker/docker/cli"
 	"github.com/docker/docker/dockerversion"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/reexec"
@@ -14,36 +12,38 @@ import (
 	"github.com/docker/docker/utils"
 )
 
+var (
+	daemonCli = NewDaemonCli()
+	flHelp    = flag.Bool([]string{"h", "-help"}, false, "Print usage")
+	flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
+)
+
 func main() {
 	if reexec.Init() {
 		return
 	}
 
 	// Set terminal emulation based on platform as required.
-	stdin, stdout, stderr := term.StdStreams()
+	_, stdout, stderr := term.StdStreams()
 
 	logrus.SetOutput(stderr)
 
-	flag.Merge(flag.CommandLine, clientFlags.FlagSet, commonFlags.FlagSet)
+	flag.Merge(flag.CommandLine, daemonCli.commonFlags.FlagSet)
 
 	flag.Usage = func() {
-		fmt.Fprint(stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n"+daemonUsage+"       docker [ --help | -v | --version ]\n\n")
+		fmt.Fprint(stdout, "Usage: dockerd [ --help | -v | --version ]\n\n")
 		fmt.Fprint(stdout, "A self-sufficient runtime for containers.\n\nOptions:\n")
 
 		flag.CommandLine.SetOutput(stdout)
 		flag.PrintDefaults()
-
-		help := "\nCommands:\n"
-
-		for _, cmd := range dockerCommands {
-			help += fmt.Sprintf("    %-10.10s%s\n", cmd.Name, cmd.Description)
-		}
-
-		help += "\nRun 'docker COMMAND --help' for more information on a command."
-		fmt.Fprintf(stdout, "%s\n", help)
+	}
+	flag.CommandLine.ShortUsage = func() {
+		fmt.Fprint(stderr, "\nUsage:\tdockerd [OPTIONS]\n")
 	}
 
-	flag.Parse()
+	if err := flag.CommandLine.ParseFlags(os.Args[1:], false); err != nil {
+		os.Exit(1)
+	}
 
 	if *flVersion {
 		showVersion()
@@ -56,21 +56,7 @@ func main() {
 		flag.Usage()
 		return
 	}
-
-	clientCli := client.NewDockerCli(stdin, stdout, stderr, clientFlags)
-
-	c := cli.New(clientCli, daemonCli)
-	if err := c.Run(flag.Args()...); err != nil {
-		if sterr, ok := err.(cli.StatusError); ok {
-			if sterr.Status != "" {
-				fmt.Fprintln(stderr, sterr.Status)
-				os.Exit(1)
-			}
-			os.Exit(sterr.StatusCode)
-		}
-		fmt.Fprintln(stderr, err)
-		os.Exit(1)
-	}
+	daemonCli.start()
 }
 
 func showVersion() {

+ 1 - 2
docs/Makefile

@@ -1,11 +1,10 @@
 .PHONY: all binary build cross default docs docs-build docs-shell shell test test-unit test-integration test-integration-cli test-docker-py validate
 
 # env vars passed through directly to Docker's build scripts
-# to allow things like `make DOCKER_CLIENTONLY=1 binary` easily
+# to allow things like `make BUILDFLAGS=... binary` easily
 # `docs/sources/contributing/devenvironment.md ` and `project/PACKAGERS.md` have some limited documentation of some of these
 DOCKER_ENVS := \
 	-e BUILDFLAGS \
-	-e DOCKER_CLIENTONLY \
 	-e DOCKER_GRAPHDRIVER \
 	-e TESTDIRS \
 	-e TESTFLAGS \

+ 2 - 1
hack/.vendor-helpers.sh

@@ -75,7 +75,8 @@ _dockerfile_env() {
 
 clean() {
 	local packages=(
-		"${PROJECT}/docker" # package main
+		"${PROJECT}/docker" # daemon package main
+		"${PROJECT}/client" # client package main
 		"${PROJECT}/integration-cli" # external tests
 	)
 	local dockerPlatforms=( ${DOCKER_ENGINE_OSARCH:="linux/amd64"} $(_dockerfile_env DOCKER_CROSSPLATFORMS) )

+ 8 - 8
hack/Jenkins/W2L/setup.sh

@@ -3,7 +3,7 @@
 set +xe
 SCRIPT_VER="Thu Feb 25 18:54:57 UTC 2016"
 
-# TODO to make (even) more resilient: 
+# TODO to make (even) more resilient:
 #  - Wait for daemon to be running before executing docker commands
 #  - Check if jq is installed
 #  - Make sure bash is v4.3 or later. Can't do until all Azure nodes on the latest version
@@ -78,7 +78,7 @@ if [ $ec -eq 0 ]; then
 		ping $ip
 	else
 		echo "INFO: The Linux nodes outer daemon replied to a ping. Good!"
-	fi 
+	fi
 fi
 
 # Get the version from the remote node. Note this may fail if jq is not installed.
@@ -134,16 +134,16 @@ if [ $ec -eq 0 ]; then
 
 	# Force remove the image if it exists
 	! docker rmi -f "docker-$COMMITHASH" &>/dev/null
-	
+
 	# This SHOULD never happen, but just in case, also blow away any containers
-	# that might be around. 
+	# that might be around.
 	! if [ ! `docker ps -aq | wc -l` -eq 0 ]; then
 		echo WARN: There were some leftover containers. Cleaning them up.
 		! docker rm -f $(docker ps -aq)
 	fi
 fi
 
-# Provide the docker version for debugging purposes. If these fail, game over. 
+# Provide the docker version for debugging purposes. If these fail, game over.
 # as the Linux box isn't responding for some reason.
 if [ $ec -eq 0 ]; then
 	echo INFO: Docker version and info of the outer daemon on the Linux node
@@ -188,7 +188,7 @@ if [ $ec -eq 0 ]; then
 	ec=$?
 	set +x
 	if [ 0 -ne $ec ]; then
-	    	echo "ERROR: Failed to compile and start the linux daemon"
+		echo "ERROR: Failed to compile and start the linux daemon"
 	fi
 fi
 
@@ -201,7 +201,7 @@ if [ $ec -eq 0 ]; then
 	export DOCKER_TEST_HOST="tcp://$ip:$port_inner"
 	unset DOCKER_CLIENTONLY
 	export DOCKER_REMOTE_DAEMON=1
-	hack/make.sh binary 
+	hack/make.sh binary
 	ec=$?
 	set +x
 	if [ 0 -ne $ec ]; then
@@ -267,7 +267,7 @@ fi
 
 # Tell the user how we did.
 if [ $ec -eq 0 ]; then
-	echo INFO: Completed successfully at `date`. 
+	echo INFO: Completed successfully at `date`.
 else
 	echo ERROR: Failed with exitcode $ec at `date`.
 fi

+ 6 - 8
hack/make.sh

@@ -66,7 +66,8 @@ DEFAULT_BUNDLES=(
 	validate-toml
 	validate-vet
 
-	binary
+	binary-client
+	binary-daemon
 	dynbinary
 
 	test-unit
@@ -126,13 +127,10 @@ if [ "$DOCKER_EXPERIMENTAL" ]; then
 	DOCKER_BUILDTAGS+=" experimental"
 fi
 
-if [ -z "$DOCKER_CLIENTONLY" ]; then
-	DOCKER_BUILDTAGS+=" daemon"
-	if pkg-config 'libsystemd >= 209' 2> /dev/null ; then
-		DOCKER_BUILDTAGS+=" journald"
-	elif pkg-config 'libsystemd-journal' 2> /dev/null ; then
-		DOCKER_BUILDTAGS+=" journald journald_compat"
-	fi
+if pkg-config 'libsystemd >= 209' 2> /dev/null ; then
+	DOCKER_BUILDTAGS+=" journald"
+elif pkg-config 'libsystemd-journal' 2> /dev/null ; then
+	DOCKER_BUILDTAGS+=" journald journald_compat"
 fi
 
 # test whether "btrfs/version.h" exists and apply btrfs_noversion appropriately

+ 64 - 0
hack/make/.binary

@@ -0,0 +1,64 @@
+#!/bin/bash
+set -e
+
+BINARY_NAME="$BINARY_SHORT_NAME-$VERSION"
+BINARY_EXTENSION="$(binary_extension)"
+BINARY_FULLNAME="$BINARY_NAME$BINARY_EXTENSION"
+
+source "${MAKEDIR}/.go-autogen"
+
+(
+export GOGC=${DOCKER_BUILD_GOGC:-1000}
+
+if [ "$(go env GOOS)/$(go env GOARCH)" != "$(go env GOHOSTOS)/$(go env GOHOSTARCH)" ]; then
+	# must be cross-compiling!
+	case "$(go env GOOS)/$(go env GOARCH)" in
+		windows/amd64)
+			export CC=x86_64-w64-mingw32-gcc
+			export CGO_ENABLED=1
+			;;
+	esac
+fi
+
+if [ "$(go env GOOS)" == "linux" ] ; then
+	case "$(go env GOARCH)" in
+		arm*|386)
+			# linking for Linux on arm or x86 needs external linking to avoid
+			# https://github.com/golang/go/issues/9510 until we move to Go 1.6
+			if [ "$IAMSTATIC" == "true" ] ; then
+				export EXTLDFLAGS_STATIC="$EXTLDFLAGS_STATIC -zmuldefs"
+				export LDFLAGS_STATIC_DOCKER="$LDFLAGS_STATIC -extldflags \"$EXTLDFLAGS_STATIC\""
+
+			else
+				export LDFLAGS="$LDFLAGS -extldflags -zmuldefs"
+			fi
+			;;
+	esac
+fi
+
+if [ "$IAMSTATIC" == "true" ] && [ "$(go env GOHOSTOS)" == "linux" ]; then
+	if  [ "${GOOS}/${GOARCH}" == "darwin/amd64" ]; then
+		export CGO_ENABLED=1
+		export CC=o64-clang
+		export LDFLAGS='-linkmode external -s'
+		export LDFLAGS_STATIC_DOCKER='-extld='${CC}
+	else
+		export BUILDFLAGS=( "${BUILDFLAGS[@]/pkcs11 /}" ) # we cannot dlopen in pkcs11 in a static binary
+	fi
+fi
+
+echo "Building: $DEST/$BINARY_FULLNAME"
+go build \
+	-o "$DEST/$BINARY_FULLNAME" \
+	"${BUILDFLAGS[@]}" \
+	-ldflags "
+		$LDFLAGS
+		$LDFLAGS_STATIC_DOCKER
+	" \
+	$SOURCE_PATH
+)
+
+echo "Created binary: $DEST/$BINARY_FULLNAME"
+ln -sf "$BINARY_FULLNAME" "$DEST/$BINARY_SHORT_NAME$BINARY_EXTENSION"
+
+hash_files "$DEST/$BINARY_FULLNAME"

+ 4 - 2
hack/make/.build-deb/rules

@@ -12,14 +12,16 @@ override_dh_auto_build:
 	# ./man/md2man-all.sh runs outside the build container (if at all), since we don't have go-md2man here
 
 override_dh_auto_test:
-	./bundles/$(VERSION)/dynbinary/docker -v
+	./bundles/$(VERSION)/dynbinary-daemon/dockerd -v
+	./bundles/$(VERSION)/dynbinary-client/docker -v
 
 override_dh_strip:
 	# Go has lots of problems with stripping, so just don't
 
 override_dh_auto_install:
 	mkdir -p debian/docker-engine/usr/bin
-	cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary/docker)" debian/docker-engine/usr/bin/docker
+	cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary-client/docker)" debian/docker-engine/usr/bin/docker
+	cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary-daemon/dockerd)" debian/docker-engine/usr/bin/dockerd
 	cp -aT /usr/local/bin/containerd debian/docker-engine/usr/bin/docker-containerd
 	cp -aT /usr/local/bin/containerd-shim debian/docker-engine/usr/bin/docker-containerd-shim
 	cp -aT /usr/local/bin/ctr debian/docker-engine/usr/bin/docker-containerd-ctr

+ 4 - 2
hack/make/.build-rpm/docker-engine.spec

@@ -112,12 +112,14 @@ export DOCKER_GITCOMMIT=%{_gitcommit}
 # ./man/md2man-all.sh runs outside the build container (if at all), since we don't have go-md2man here
 
 %check
-./bundles/%{_origversion}/dynbinary/docker -v
+./bundles/%{_origversion}/dynbinary-client/docker -v
+./bundles/%{_origversion}/dynbinary-daemon/dockerd -v
 
 %install
 # install binary
 install -d $RPM_BUILD_ROOT/%{_bindir}
-install -p -m 755 bundles/%{_origversion}/dynbinary/docker-%{_origversion} $RPM_BUILD_ROOT/%{_bindir}/docker
+install -p -m 755 bundles/%{_origversion}/dynbinary-client/docker-%{_origversion} $RPM_BUILD_ROOT/%{_bindir}/docker
+install -p -m 755 bundles/%{_origversion}/dynbinary-daemon/dockerd-%{_origversion} $RPM_BUILD_ROOT/%{_bindir}/dockerd
 
 # install containerd
 install -p -m 755 /usr/local/bin/containerd $RPM_BUILD_ROOT/%{_bindir}/docker-containerd

+ 15 - 3
hack/make/.integration-daemon-start

@@ -2,13 +2,20 @@
 
 # see test-integration-cli for example usage of this script
 
-export PATH="$ABS_DEST/../binary:$ABS_DEST/../dynbinary:$ABS_DEST/../gccgo:$ABS_DEST/../dyngccgo:$PATH"
+base="$ABS_DEST/.."
+export PATH="$base/binary-client:$base/binary-daemon:$base/dynbinary:$base/gccgo:$base/dyngccgo:$PATH"
 
 if ! command -v docker &> /dev/null; then
-	echo >&2 'error: binary or dynbinary must be run before .integration-daemon-start'
+	echo >&2 'error: binary-client or dynbinary-client must be run before .integration-daemon-start'
 	false
 fi
 
+# This is a temporary hack for split-binary mode. It can be removed once
+# https://github.com/docker/docker/pull/22134 is merged into docker master
+if [ "$(go env GOOS)" = 'windows' ]; then
+       return
+fi
+
 if [ -z "$DOCKER_TEST_HOST" ]; then
 	if docker version &> /dev/null; then
 		echo >&2 'skipping daemon start, since daemon appears to be already started'
@@ -16,6 +23,11 @@ if [ -z "$DOCKER_TEST_HOST" ]; then
 	fi
 fi
 
+if ! command -v dockerd &> /dev/null; then
+	echo >&2 'error: binary-daemon or dynbinary-daemon must be run before .integration-daemon-start'
+	false
+fi
+
 # intentionally open a couple bogus file descriptors to help test that they get scrubbed in containers
 exec 41>&1 42>&2
 
@@ -52,7 +64,7 @@ if [ -z "$DOCKER_TEST_HOST" ]; then
 
 	export DOCKER_HOST="unix://$(cd "$DEST" && pwd)/docker.sock" # "pwd" tricks to make sure $DEST is an absolute path, not a relative one
 	( set -x; exec \
-		docker daemon --debug \
+		dockerd --debug \
 		--host "$DOCKER_HOST" \
 		--storage-driver "$DOCKER_GRAPHDRIVER" \
 		--pidfile "$DEST/docker.pid" \

+ 9 - 60
hack/make/binary

@@ -1,65 +1,14 @@
 #!/bin/bash
 set -e
 
-BINARY_NAME="docker-$VERSION"
-BINARY_EXTENSION="$(binary_extension)"
-BINARY_FULLNAME="$BINARY_NAME$BINARY_EXTENSION"
-
-source "${MAKEDIR}/.go-autogen"
-
+# This script exists as backwards compatibility for CI
 (
-export GOGC=${DOCKER_BUILD_GOGC:-1000}
-
-if [ "$(go env GOOS)/$(go env GOARCH)" != "$(go env GOHOSTOS)/$(go env GOHOSTARCH)" ]; then
-	# must be cross-compiling!
-	case "$(go env GOOS)/$(go env GOARCH)" in
-		windows/amd64)
-			export CC=x86_64-w64-mingw32-gcc
-			export CGO_ENABLED=1
-			;;
-	esac
-fi
-
-if [ "$(go env GOOS)" == "linux" ] ; then
-	case "$(go env GOARCH)" in
-		arm*|386)
-			# linking for Linux on arm or x86 needs external linking to avoid
-			# https://github.com/golang/go/issues/9510 until we move to Go 1.6
-			if [ "$IAMSTATIC" == "true" ] ; then
-				export EXTLDFLAGS_STATIC="$EXTLDFLAGS_STATIC -zmuldefs"
-				export LDFLAGS_STATIC_DOCKER="$LDFLAGS_STATIC -extldflags \"$EXTLDFLAGS_STATIC\""
-
-			else
-				export LDFLAGS="$LDFLAGS -extldflags -zmuldefs"
-			fi
-			;;
-	esac
-fi
-
-if [ "$IAMSTATIC" == "true" ] && [ "$(go env GOHOSTOS)" == "linux" ]; then
-	if  [ "${GOOS}/${GOARCH}" == "darwin/amd64" ]; then
-		export CGO_ENABLED=1
-		export CC=o64-clang
-		export LDFLAGS='-linkmode external -s'
-		export LDFLAGS_STATIC_DOCKER='-extld='${CC}
-	else
-		export BUILDFLAGS=( "${BUILDFLAGS[@]/pkcs11 /}" ) # we cannot dlopen in pkcs11 in a static binary
-	fi
-fi
-
-echo "Building: $DEST/$BINARY_FULLNAME"
-go build \
-	-o "$DEST/$BINARY_FULLNAME" \
-	"${BUILDFLAGS[@]}" \
-	-ldflags "
-		$LDFLAGS
-		$LDFLAGS_STATIC_DOCKER
-	" \
-	./docker
+	DEST="${DEST}-client"
+	ABS_DEST="${ABS_DEST}-client"
+	. hack/make/binary-client
+)
+(
+	DEST="${DEST}-daemon"
+	ABS_DEST="${ABS_DEST}-daemon"
+	. hack/make/binary-daemon
 )
-
-echo "Created binary: $DEST/$BINARY_FULLNAME"
-ln -sf "$BINARY_FULLNAME" "$DEST/docker$BINARY_EXTENSION"
-
-copy_containerd "$DEST" "hash"
-hash_files "$DEST/$BINARY_FULLNAME"

+ 7 - 0
hack/make/binary-client

@@ -0,0 +1,7 @@
+#!/bin/bash
+set -e
+
+BINARY_SHORT_NAME="docker"
+SOURCE_PATH="./client"
+
+source "${MAKEDIR}/.binary"

+ 8 - 0
hack/make/binary-daemon

@@ -0,0 +1,8 @@
+#!/bin/bash
+set -e
+
+BINARY_SHORT_NAME="dockerd"
+SOURCE_PATH="./docker"
+
+source "${MAKEDIR}/.binary"
+copy_containerd "$DEST" "hash"

+ 12 - 7
hack/make/cross

@@ -9,11 +9,12 @@ daemonSupporting=(
 )
 
 # if we have our linux/amd64 version compiled, let's symlink it in
-if [ -x "$DEST/../binary/docker-$VERSION" ]; then
+if [ -x "$DEST/../binary-daemon/dockerd-$VERSION" ]; then
 	mkdir -p "$DEST/linux/amd64"
 	(
 		cd "$DEST/linux/amd64"
-		ln -s ../../../binary/* ./
+		ln -s ../../../binary-daemon/* ./
+		ln -s ../../../binary-client/* ./
 	)
 	echo "Created symlinks:" "$DEST/linux/amd64/"*
 fi
@@ -25,16 +26,20 @@ for platform in $DOCKER_CROSSPLATFORMS; do
 		ABS_DEST="$(cd "$DEST" && pwd -P)"
 		export GOOS=${platform%/*}
 		export GOARCH=${platform##*/}
-		if [ -z "${daemonSupporting[$platform]}" ]; then
-			export LDFLAGS_STATIC_DOCKER="" # we just need a simple client for these platforms
-			export BUILDFLAGS=( "${ORIG_BUILDFLAGS[@]/ daemon/}" ) # remove the "daemon" build tag from platforms that aren't supported
-		fi
+
 		# !!! TEMPORARY HACK !!!
 		# See Dockerfile
 		if [ "$platform" == "windows/amd64" ]; then
 			export GOROOT="/usr/local/go${HACK_GO_VERSION}"
 			export PATH=$(echo "$PATH" | sed "s,:/usr/local/go/bin:,:/usr/local/go${HACK_GO_VERSION}/bin:,")
 		fi
-		source "${MAKEDIR}/binary"
+
+		if [ -z "${daemonSupporting[$platform]}" ]; then
+			export LDFLAGS_STATIC_DOCKER="" # we just need a simple client for these platforms
+		    source "${MAKEDIR}/binary-client"
+        else
+		    source "${MAKEDIR}/binary-client"
+		    source "${MAKEDIR}/binary-daemon"
+		fi
 	)
 done

+ 10 - 5
hack/make/dynbinary

@@ -1,10 +1,15 @@
 #!/bin/bash
 set -e
 
+# This script exists as backwards compatiblity for CI
 (
-	export IAMSTATIC="false"
-	export LDFLAGS_STATIC_DOCKER=''
-	export BUILDFLAGS=( "${BUILDFLAGS[@]/netgo /}" ) # disable netgo, since we don't need it for a dynamic binary
-	export BUILDFLAGS=( "${BUILDFLAGS[@]/static_build /}" ) # we're not building a "static" binary here
-	source "${MAKEDIR}/binary"
+    DEST="${DEST}-client"
+    ABS_DEST="${ABS_DEST}-client"
+    . hack/make/dynbinary-client
+)
+(
+
+    DEST="${DEST}-daemon"
+    ABS_DEST="${ABS_DEST}-daemon"
+    . hack/make/dynbinary-daemon
 )

+ 12 - 0
hack/make/dynbinary-client

@@ -0,0 +1,12 @@
+#!/bin/bash
+set -e
+
+(
+    export BINARY_SHORT_NAME="docker-client"
+    export SOURCE_PATH="./client"
+	export IAMSTATIC="false"
+	export LDFLAGS_STATIC_DOCKER=''
+	export BUILDFLAGS=( "${BUILDFLAGS[@]/netgo /}" ) # disable netgo, since we don't need it for a dynamic binary
+	export BUILDFLAGS=( "${BUILDFLAGS[@]/static_build /}" ) # we're not building a "static" binary here
+	source "${MAKEDIR}/.binary"
+)

+ 12 - 0
hack/make/dynbinary-daemon

@@ -0,0 +1,12 @@
+#!/bin/bash
+set -e
+
+(
+    export BINARY_SHORT_NAME="dockerd"
+    export SOURCE_PATH="./docker"
+	export IAMSTATIC="false"
+	export LDFLAGS_STATIC_DOCKER=''
+	export BUILDFLAGS=( "${BUILDFLAGS[@]/netgo /}" ) # disable netgo, since we don't need it for a dynamic binary
+	export BUILDFLAGS=( "${BUILDFLAGS[@]/static_build /}" ) # we're not building a "static" binary here
+	source "${MAKEDIR}/.binary"
+)

+ 22 - 2
hack/make/gccgo

@@ -1,10 +1,13 @@
 #!/bin/bash
 set -e
 
-BINARY_NAME="docker-$VERSION"
+BINARY_NAME="dockerd-$VERSION"
 BINARY_EXTENSION="$(binary_extension)"
 BINARY_FULLNAME="$BINARY_NAME$BINARY_EXTENSION"
 
+CLIENTBIN_NAME="docker-$VERSION"
+CLIENTBIN_FULLNAME="$CLIENTBIN_NAME$BINARY_EXTENSION"
+
 source "${MAKEDIR}/.go-autogen"
 
 if [[ "${BUILDFLAGS[@]}" =~ 'netgo ' ]]; then
@@ -24,7 +27,24 @@ go build -compiler=gccgo \
 	./docker
 
 echo "Created binary: $DEST/$BINARY_FULLNAME"
-ln -sf "$BINARY_FULLNAME" "$DEST/docker$BINARY_EXTENSION"
+ln -sf "$BINARY_FULLNAME" "$DEST/dockerd$BINARY_EXTENSION"
 
 copy_containerd "$DEST" "hash"
 hash_files "$DEST/$BINARY_FULLNAME"
+
+go build -compiler=gccgo \
+	-o "$DEST/$CLIENTBIN_FULLNAME" \
+	"${BUILDFLAGS[@]}" \
+	-gccgoflags "
+		-g
+		$EXTLDFLAGS_STATIC
+		-Wl,--no-export-dynamic
+		-ldl
+		-pthread
+	" \
+	./client
+
+echo "Created binary: $DEST/$CLIENTBIN_FULLNAME"
+ln -sf "$CLIENTBIN_FULLNAME" "$DEST/docker$BINARY_EXTENSION"
+hash_files "$DEST/$CLIENTBIN_FULLNAME"
+

+ 1 - 1
integration-cli/docker_cli_daemon_test.go

@@ -1,4 +1,4 @@
-// +build daemon,!windows
+// +build !windows
 
 package main
 

+ 3 - 0
integration-cli/docker_utils.go

@@ -1290,6 +1290,9 @@ func appendBaseEnv(isTLS bool, env ...string) []string {
 		// windows: requires preserving SystemRoot, otherwise dial tcp fails
 		// with "GetAddrInfoW: A non-recoverable error occurred during a database lookup."
 		"SystemRoot",
+
+		// testing help text requires the $PATH to dockerd is set
+		"PATH",
 	}
 	if isTLS {
 		preserveList = append(preserveList, "DOCKER_TLS_VERIFY", "DOCKER_CERT_PATH")

+ 4 - 4
project/PACKAGERS.md

@@ -216,10 +216,10 @@ the file "./VERSION". This binary is usually installed somewhere like
 
 ### Dynamic Daemon / Client-only Binary
 
-If you are only interested in a Docker client binary, set `DOCKER_CLIENTONLY` to a non-empty value using something similar to the following:
+If you are only interested in a Docker client binary, you can build using:
 
 ```bash
-export DOCKER_CLIENTONLY=1
+./hack/make.sh binary-client
 ```
 
 If you need to (due to distro policy, distro library availability, or for other
@@ -228,10 +228,10 @@ interested in creating a client binary for Docker, use something similar to the
 following:
 
 ```bash
-./hack/make.sh dynbinary
+./hack/make.sh dynbinary-client
 ```
 
-This will create "./bundles/$VERSION/dynbinary/docker-$VERSION", which for
+This will create "./bundles/$VERSION/dynbinary-client/docker-$VERSION", which for
 client-only builds is the important file to grab and install as appropriate.
 
 ## System Dependencies