1b95590d |
// +build daemon
package main
import ( |
bfed4b7c |
"crypto/tls" |
007ef161 |
"fmt"
"io"
"os"
"path/filepath" |
96ce3a19 |
"strings" |
531f4122 |
"time" |
007ef161 |
|
6f4d8470 |
"github.com/Sirupsen/logrus" |
7841d6ab |
"github.com/docker/distribution/uuid" |
a0bf80fe |
apiserver "github.com/docker/docker/api/server" |
96ce3a19 |
"github.com/docker/docker/cli" |
daced1d3 |
"github.com/docker/docker/cliconfig" |
63503caf |
"github.com/docker/docker/daemon" |
9b782d3a |
"github.com/docker/docker/daemon/logger" |
8054a303 |
"github.com/docker/docker/dockerversion" |
96ce3a19 |
"github.com/docker/docker/opts" |
27220ecc |
"github.com/docker/docker/pkg/jsonlog" |
1b95590d |
flag "github.com/docker/docker/pkg/mflag" |
531f4122 |
"github.com/docker/docker/pkg/pidfile" |
c9f3fd3f |
"github.com/docker/docker/pkg/signal" |
52f4d09f |
"github.com/docker/docker/pkg/system" |
afade423 |
"github.com/docker/docker/registry" |
78578125 |
"github.com/docker/docker/utils" |
8e034802 |
"github.com/docker/go-connections/tlsconfig" |
1b95590d |
)
|
677a6b35 |
const (
daemonUsage = " docker daemon [ --help | ... ]\n"
daemonConfigFileFlag = "-config-file"
) |
96ce3a19 |
|
353b7c8e |
var ( |
96ce3a19 |
daemonCli cli.Handler = NewDaemonCli() |
353b7c8e |
)
|
677a6b35 |
// DaemonCli represents the daemon CLI.
type DaemonCli struct {
*daemon.Config
registryOptions *registry.Options
flags *flag.FlagSet
}
|
96ce3a19 |
func presentInHelp(usage string) string { return usage }
func absentFromHelp(string) string { return "" }
// NewDaemonCli returns a pre-configured daemon CLI
func NewDaemonCli() *DaemonCli { |
677a6b35 |
daemonFlags := cli.Subcmd("daemon", nil, "Enable daemon mode", true) |
96ce3a19 |
// TODO(tiborvass): remove InstallFlags?
daemonConfig := new(daemon.Config) |
e904cbec |
daemonConfig.LogConfig.Config = make(map[string]string) |
124792a8 |
daemonConfig.ClusterOpts = make(map[string]string) |
677a6b35 |
|
96ce3a19 |
daemonConfig.InstallFlags(daemonFlags, presentInHelp)
daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp)
registryOptions := new(registry.Options)
registryOptions.InstallFlags(daemonFlags, presentInHelp)
registryOptions.InstallFlags(flag.CommandLine, absentFromHelp)
daemonFlags.Require(flag.Exact, 0)
return &DaemonCli{
Config: daemonConfig,
registryOptions: registryOptions, |
677a6b35 |
flags: daemonFlags, |
dca9e02b |
} |
353b7c8e |
}
|
d55e977c |
func migrateKey() (err error) { |
007ef161 |
// Migrate trust key if exists at ~/.docker/key.json and owned by current user |
daced1d3 |
oldPath := filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile) |
007ef161 |
newPath := filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) |
01724c1c |
if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) { |
d55e977c |
defer func() {
// Ensure old path is removed if no error occurred
if err == nil {
err = os.Remove(oldPath)
} else { |
6f4d8470 |
logrus.Warnf("Key migration failed, key file not removed at %s", oldPath) |
07c45e49 |
os.Remove(newPath) |
d55e977c |
}
}()
|
52f4d09f |
if err := system.MkdirAll(getDaemonConfDir(), os.FileMode(0644)); err != nil { |
d55e977c |
return fmt.Errorf("Unable to create daemon configuration directory: %s", err) |
007ef161 |
}
newFile, err := os.OpenFile(newPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return fmt.Errorf("error creating key file %q: %s", newPath, err)
}
defer newFile.Close()
oldFile, err := os.Open(oldPath)
if err != nil { |
d55e977c |
return fmt.Errorf("error opening key file %q: %s", oldPath, err) |
007ef161 |
} |
d55e977c |
defer oldFile.Close() |
007ef161 |
if _, err := io.Copy(newFile, oldFile); err != nil {
return fmt.Errorf("error copying key: %s", err)
}
|
6f4d8470 |
logrus.Infof("Migrated key from %s to %s", oldPath, newPath) |
007ef161 |
}
return nil
}
|
96ce3a19 |
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 { |
7841d6ab |
// warn from uuid package when running the daemon
uuid.Loggerf = logrus.Warnf
|
79298882 |
if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() { |
96ce3a19 |
// 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` |
677a6b35 |
flag.Merge(cli.flags, commonFlags.FlagSet) |
96ce3a19 |
}
|
677a6b35 |
configFile := cli.flags.String([]string{daemonConfigFileFlag}, defaultDaemonConfigFile, "Daemon configuration file")
cli.flags.ParseFlags(args, true) |
96ce3a19 |
commonFlags.PostParse()
if commonFlags.TrustKey == "" {
commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) |
78578125 |
} |
677a6b35 |
cliConfig, err := loadDaemonCliConfig(cli.Config, cli.flags, commonFlags, *configFile)
if err != nil {
fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
cli.Config = cliConfig
if cli.Config.Debug {
utils.EnableDebug()
} |
78578125 |
|
96ce3a19 |
if utils.ExperimentalBuild() {
logrus.Warn("Running experimental build") |
1b95590d |
} |
711e5803 |
|
27220ecc |
logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: jsonlog.RFC3339NanoFixed}) |
711e5803 |
|
6578ad90 |
if err := setDefaultUmask(); err != nil {
logrus.Fatalf("Failed to set umask: %v", err)
}
|
96ce3a19 |
if len(cli.LogConfig.Config) > 0 {
if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { |
9b782d3a |
logrus.Fatalf("Failed to set log opts: %v", err)
}
}
|
18c7c673 |
var pfile *pidfile.PIDFile |
96ce3a19 |
if cli.Pidfile != "" {
pf, err := pidfile.New(cli.Pidfile) |
531f4122 |
if err != nil {
logrus.Fatalf("Error starting daemon: %v", err)
}
pfile = pf
defer func() {
if err := pfile.Remove(); err != nil {
logrus.Error(err)
}
}()
} |
afade423 |
|
351f6b8e |
serverConfig := &apiserver.Config{ |
5c630ea7 |
AuthorizationPluginNames: cli.Config.AuthorizationPlugins,
Logging: true,
Version: dockerversion.Version, |
bfed4b7c |
} |
96ce3a19 |
serverConfig = setPlatformServerConfig(serverConfig, cli.Config) |
bfed4b7c |
|
fbb01b81 |
defaultHost := opts.DefaultHost |
677a6b35 |
if cli.Config.TLS {
tlsOptions := tlsconfig.Options{ |
f999cd3d |
CAFile: cli.Config.CommonTLSOptions.CAFile,
CertFile: cli.Config.CommonTLSOptions.CertFile,
KeyFile: cli.Config.CommonTLSOptions.KeyFile, |
677a6b35 |
}
if cli.Config.TLSVerify { |
96ce3a19 |
// server requires and verifies client's certificate |
677a6b35 |
tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert |
bfed4b7c |
} |
677a6b35 |
tlsConfig, err := tlsconfig.Server(tlsOptions) |
bfed4b7c |
if err != nil { |
c7a04fda |
logrus.Fatal(err) |
bfed4b7c |
}
serverConfig.TLSConfig = tlsConfig |
fbb01b81 |
defaultHost = opts.DefaultTLSHost |
50f09060 |
}
|
677a6b35 |
if len(cli.Config.Hosts) == 0 {
cli.Config.Hosts = make([]string, 1) |
e38767e1 |
} |
677a6b35 |
for i := 0; i < len(cli.Config.Hosts); i++ { |
50f09060 |
var err error |
677a6b35 |
if cli.Config.Hosts[i], err = opts.ParseHost(defaultHost, cli.Config.Hosts[i]); err != nil {
logrus.Fatalf("error parsing -H %s : %v", cli.Config.Hosts[i], err) |
50f09060 |
} |
677a6b35 |
protoAddr := cli.Config.Hosts[i] |
5eda566f |
protoAddrParts := strings.SplitN(protoAddr, "://", 2)
if len(protoAddrParts) != 2 {
logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr)
}
serverConfig.Addrs = append(serverConfig.Addrs, apiserver.Addr{Proto: protoAddrParts[0], Addr: protoAddrParts[1]})
} |
677a6b35 |
|
5eda566f |
api, err := apiserver.New(serverConfig)
if err != nil {
logrus.Fatal(err)
} |
a0bf80fe |
|
08230703 |
if err := migrateKey(); err != nil {
logrus.Fatal(err)
} |
96ce3a19 |
cli.TrustKeyPath = commonFlags.TrustKey |
08230703 |
|
96ce3a19 |
registryService := registry.NewService(cli.registryOptions) |
b08f071e |
d, err := daemon.NewDaemon(cli.Config, registryService) |
08230703 |
if err != nil {
if pfile != nil {
if err := pfile.Remove(); err != nil {
logrus.Error(err)
}
}
logrus.Fatalf("Error starting daemon: %v", err)
}
logrus.Info("Daemon has completed initialization")
logrus.WithFields(logrus.Fields{ |
8054a303 |
"version": dockerversion.Version,
"commit": dockerversion.GitCommit, |
b08f071e |
"execdriver": d.ExecutionDriver().Name(), |
f5916b10 |
"graphdriver": d.GraphDriverName(), |
08230703 |
}).Info("Docker daemon")
|
da982cf5 |
api.InitRouters(d)
|
677a6b35 |
reload := func(config *daemon.Config) {
if err := d.Reload(config); err != nil {
logrus.Errorf("Error reconfiguring the daemon: %v", err)
return
}
api.Reload(config)
}
setupConfigReloadTrap(*configFile, cli.flags, reload)
|
a8b84cd8 |
// The serve API routine never exits unless an error occurs
// We need to start it as a goroutine and wait on it so
// daemon doesn't exit
serveAPIWait := make(chan error) |
677a6b35 |
go api.Wait(serveAPIWait) |
a8b84cd8 |
|
531f4122 |
signal.Trap(func() {
api.Close()
<-serveAPIWait |
b08f071e |
shutdownDaemon(d, 15) |
531f4122 |
if pfile != nil {
if err := pfile.Remove(); err != nil {
logrus.Error(err)
}
}
}) |
181fea24 |
|
ca5795ce |
// after the daemon is done setting up we can notify systemd api |
da982cf5 |
notifySystem() |
181fea24 |
|
459e58ff |
// Daemon is fully initialized and handling API traffic |
531f4122 |
// Wait for serve API to complete |
459e58ff |
errAPI := <-serveAPIWait |
b08f071e |
shutdownDaemon(d, 15) |
459e58ff |
if errAPI != nil { |
531f4122 |
if pfile != nil {
if err := pfile.Remove(); err != nil {
logrus.Error(err)
}
} |
6f4d8470 |
logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) |
459e58ff |
} |
96ce3a19 |
return nil |
1b95590d |
} |
01724c1c |
|
531f4122 |
// shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
// d.Shutdown() is waiting too long to kill container or worst it's
// blocked there |
b08f071e |
func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) { |
531f4122 |
ch := make(chan struct{})
go func() { |
b08f071e |
d.Shutdown() |
531f4122 |
close(ch)
}()
select {
case <-ch: |
51462327 |
logrus.Debug("Clean shutdown succeeded") |
531f4122 |
case <-time.After(timeout * time.Second):
logrus.Error("Force shutdown daemon")
}
} |
677a6b35 |
func loadDaemonCliConfig(config *daemon.Config, daemonFlags *flag.FlagSet, commonConfig *cli.CommonFlags, configFile string) (*daemon.Config, error) {
config.Debug = commonConfig.Debug
config.Hosts = commonConfig.Hosts
config.LogLevel = commonConfig.LogLevel
config.TLS = commonConfig.TLS
config.TLSVerify = commonConfig.TLSVerify |
f999cd3d |
config.CommonTLSOptions = daemon.CommonTLSOptions{} |
677a6b35 |
if commonConfig.TLSOptions != nil { |
f999cd3d |
config.CommonTLSOptions.CAFile = commonConfig.TLSOptions.CAFile
config.CommonTLSOptions.CertFile = commonConfig.TLSOptions.CertFile
config.CommonTLSOptions.KeyFile = commonConfig.TLSOptions.KeyFile |
677a6b35 |
}
if configFile != "" {
c, err := daemon.MergeDaemonConfigurations(config, daemonFlags, configFile)
if err != nil {
if daemonFlags.IsSet(daemonConfigFileFlag) || !os.IsNotExist(err) {
return nil, fmt.Errorf("unable to configure the Docker daemon with file %s: %v\n", configFile, err)
}
}
// the merged configuration can be nil if the config file didn't exist.
// leave the current configuration as it is if when that happens.
if c != nil {
config = c
}
}
|
ba01d9f9 |
// Regardless of whether the user sets it to true or false, if they
// specify TLSVerify at all then we need to turn on TLS |
231eeca2 |
if config.IsValueSet(tlsVerifyKey) { |
ba01d9f9 |
config.TLS = true
}
// ensure that the log level is the one set after merging configurations
setDaemonLogLevel(config.LogLevel)
|
677a6b35 |
return config, nil
} |