github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/cmd/dockerd/daemon.go (about)

     1  package main
     2  
     3  import (
     4  	"crypto/tls"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/Sirupsen/logrus"
    14  	"github.com/docker/distribution/uuid"
    15  	"github.com/docker/docker/api"
    16  	apiserver "github.com/docker/docker/api/server"
    17  	"github.com/docker/docker/api/server/middleware"
    18  	"github.com/docker/docker/api/server/router"
    19  	"github.com/docker/docker/api/server/router/build"
    20  	checkpointrouter "github.com/docker/docker/api/server/router/checkpoint"
    21  	"github.com/docker/docker/api/server/router/container"
    22  	"github.com/docker/docker/api/server/router/image"
    23  	"github.com/docker/docker/api/server/router/network"
    24  	pluginrouter "github.com/docker/docker/api/server/router/plugin"
    25  	swarmrouter "github.com/docker/docker/api/server/router/swarm"
    26  	systemrouter "github.com/docker/docker/api/server/router/system"
    27  	"github.com/docker/docker/api/server/router/volume"
    28  	"github.com/docker/docker/builder/dockerfile"
    29  	cliconfig "github.com/docker/docker/cli/config"
    30  	"github.com/docker/docker/cli/debug"
    31  	cliflags "github.com/docker/docker/cli/flags"
    32  	"github.com/docker/docker/daemon"
    33  	"github.com/docker/docker/daemon/cluster"
    34  	"github.com/docker/docker/daemon/config"
    35  	"github.com/docker/docker/daemon/logger"
    36  	"github.com/docker/docker/dockerversion"
    37  	"github.com/docker/docker/libcontainerd"
    38  	dopts "github.com/docker/docker/opts"
    39  	"github.com/docker/docker/pkg/authorization"
    40  	"github.com/docker/docker/pkg/jsonlog"
    41  	"github.com/docker/docker/pkg/listeners"
    42  	"github.com/docker/docker/pkg/pidfile"
    43  	"github.com/docker/docker/pkg/plugingetter"
    44  	"github.com/docker/docker/pkg/signal"
    45  	"github.com/docker/docker/pkg/system"
    46  	"github.com/docker/docker/registry"
    47  	"github.com/docker/docker/runconfig"
    48  	"github.com/docker/go-connections/tlsconfig"
    49  	"github.com/spf13/pflag"
    50  )
    51  
    52  const (
    53  	flagDaemonConfigFile = "config-file"
    54  )
    55  
    56  // DaemonCli represents the daemon CLI.
    57  type DaemonCli struct {
    58  	*config.Config
    59  	configFile *string
    60  	flags      *pflag.FlagSet
    61  
    62  	api             *apiserver.Server
    63  	d               *daemon.Daemon
    64  	authzMiddleware *authorization.Middleware // authzMiddleware enables to dynamically reload the authorization plugins
    65  }
    66  
    67  // NewDaemonCli returns a daemon CLI
    68  func NewDaemonCli() *DaemonCli {
    69  	return &DaemonCli{}
    70  }
    71  
    72  func migrateKey(config *config.Config) (err error) {
    73  	// No migration necessary on Windows
    74  	if runtime.GOOS == "windows" {
    75  		return nil
    76  	}
    77  
    78  	// Migrate trust key if exists at ~/.docker/key.json and owned by current user
    79  	oldPath := filepath.Join(cliconfig.Dir(), cliflags.DefaultTrustKeyFile)
    80  	newPath := filepath.Join(getDaemonConfDir(config.Root), cliflags.DefaultTrustKeyFile)
    81  	if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) {
    82  		defer func() {
    83  			// Ensure old path is removed if no error occurred
    84  			if err == nil {
    85  				err = os.Remove(oldPath)
    86  			} else {
    87  				logrus.Warnf("Key migration failed, key file not removed at %s", oldPath)
    88  				os.Remove(newPath)
    89  			}
    90  		}()
    91  
    92  		if err := system.MkdirAll(getDaemonConfDir(config.Root), os.FileMode(0644)); err != nil {
    93  			return fmt.Errorf("Unable to create daemon configuration directory: %s", err)
    94  		}
    95  
    96  		newFile, err := os.OpenFile(newPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
    97  		if err != nil {
    98  			return fmt.Errorf("error creating key file %q: %s", newPath, err)
    99  		}
   100  		defer newFile.Close()
   101  
   102  		oldFile, err := os.Open(oldPath)
   103  		if err != nil {
   104  			return fmt.Errorf("error opening key file %q: %s", oldPath, err)
   105  		}
   106  		defer oldFile.Close()
   107  
   108  		if _, err := io.Copy(newFile, oldFile); err != nil {
   109  			return fmt.Errorf("error copying key: %s", err)
   110  		}
   111  
   112  		logrus.Infof("Migrated key from %s to %s", oldPath, newPath)
   113  	}
   114  
   115  	return nil
   116  }
   117  
   118  func (cli *DaemonCli) start(opts daemonOptions) (err error) {
   119  	stopc := make(chan bool)
   120  	defer close(stopc)
   121  
   122  	// warn from uuid package when running the daemon
   123  	uuid.Loggerf = logrus.Warnf
   124  
   125  	opts.common.SetDefaultOptions(opts.flags)
   126  
   127  	if cli.Config, err = loadDaemonCliConfig(opts); err != nil {
   128  		return err
   129  	}
   130  	cli.configFile = &opts.configFile
   131  	cli.flags = opts.flags
   132  
   133  	if opts.common.TrustKey == "" {
   134  		opts.common.TrustKey = filepath.Join(
   135  			getDaemonConfDir(cli.Config.Root),
   136  			cliflags.DefaultTrustKeyFile)
   137  	}
   138  
   139  	if cli.Config.Debug {
   140  		debug.Enable()
   141  	}
   142  
   143  	if cli.Config.Experimental {
   144  		logrus.Warn("Running experimental build")
   145  	}
   146  
   147  	logrus.SetFormatter(&logrus.TextFormatter{
   148  		TimestampFormat: jsonlog.RFC3339NanoFixed,
   149  		DisableColors:   cli.Config.RawLogs,
   150  	})
   151  
   152  	if err := setDefaultUmask(); err != nil {
   153  		return fmt.Errorf("Failed to set umask: %v", err)
   154  	}
   155  
   156  	if len(cli.LogConfig.Config) > 0 {
   157  		if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil {
   158  			return fmt.Errorf("Failed to set log opts: %v", err)
   159  		}
   160  	}
   161  
   162  	// Create the daemon root before we create ANY other files (PID, or migrate keys)
   163  	// to ensure the appropriate ACL is set (particularly relevant on Windows)
   164  	if err := daemon.CreateDaemonRoot(cli.Config); err != nil {
   165  		return err
   166  	}
   167  
   168  	if cli.Pidfile != "" {
   169  		pf, err := pidfile.New(cli.Pidfile)
   170  		if err != nil {
   171  			return fmt.Errorf("Error starting daemon: %v", err)
   172  		}
   173  		defer func() {
   174  			if err := pf.Remove(); err != nil {
   175  				logrus.Error(err)
   176  			}
   177  		}()
   178  	}
   179  
   180  	serverConfig := &apiserver.Config{
   181  		Logging:     true,
   182  		SocketGroup: cli.Config.SocketGroup,
   183  		Version:     dockerversion.Version,
   184  		EnableCors:  cli.Config.EnableCors,
   185  		CorsHeaders: cli.Config.CorsHeaders,
   186  	}
   187  
   188  	if cli.Config.TLS {
   189  		tlsOptions := tlsconfig.Options{
   190  			CAFile:   cli.Config.CommonTLSOptions.CAFile,
   191  			CertFile: cli.Config.CommonTLSOptions.CertFile,
   192  			KeyFile:  cli.Config.CommonTLSOptions.KeyFile,
   193  		}
   194  
   195  		if cli.Config.TLSVerify {
   196  			// server requires and verifies client's certificate
   197  			tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert
   198  		}
   199  		tlsConfig, err := tlsconfig.Server(tlsOptions)
   200  		if err != nil {
   201  			return err
   202  		}
   203  		serverConfig.TLSConfig = tlsConfig
   204  	}
   205  
   206  	if len(cli.Config.Hosts) == 0 {
   207  		cli.Config.Hosts = make([]string, 1)
   208  	}
   209  
   210  	api := apiserver.New(serverConfig)
   211  	cli.api = api
   212  
   213  	for i := 0; i < len(cli.Config.Hosts); i++ {
   214  		var err error
   215  		if cli.Config.Hosts[i], err = dopts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil {
   216  			return fmt.Errorf("error parsing -H %s : %v", cli.Config.Hosts[i], err)
   217  		}
   218  
   219  		protoAddr := cli.Config.Hosts[i]
   220  		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
   221  		if len(protoAddrParts) != 2 {
   222  			return fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr)
   223  		}
   224  
   225  		proto := protoAddrParts[0]
   226  		addr := protoAddrParts[1]
   227  
   228  		// It's a bad idea to bind to TCP without tlsverify.
   229  		if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) {
   230  			logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting --tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]")
   231  		}
   232  		ls, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig)
   233  		if err != nil {
   234  			return err
   235  		}
   236  		ls = wrapListeners(proto, ls)
   237  		// If we're binding to a TCP port, make sure that a container doesn't try to use it.
   238  		if proto == "tcp" {
   239  			if err := allocateDaemonPort(addr); err != nil {
   240  				return err
   241  			}
   242  		}
   243  		logrus.Debugf("Listener created for HTTP on %s (%s)", proto, addr)
   244  		api.Accept(addr, ls...)
   245  	}
   246  
   247  	if err := migrateKey(cli.Config); err != nil {
   248  		return err
   249  	}
   250  
   251  	// FIXME: why is this down here instead of with the other TrustKey logic above?
   252  	cli.TrustKeyPath = opts.common.TrustKey
   253  
   254  	registryService := registry.NewService(cli.Config.ServiceOptions)
   255  	containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...)
   256  	if err != nil {
   257  		return err
   258  	}
   259  	signal.Trap(func() {
   260  		cli.stop()
   261  		<-stopc // wait for daemonCli.start() to return
   262  	})
   263  
   264  	// Notify that the API is active, but before daemon is set up.
   265  	preNotifySystem()
   266  
   267  	d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote)
   268  	if err != nil {
   269  		return fmt.Errorf("Error starting daemon: %v", err)
   270  	}
   271  
   272  	if cli.Config.MetricsAddress != "" {
   273  		if !d.HasExperimental() {
   274  			return fmt.Errorf("metrics-addr is only supported when experimental is enabled")
   275  		}
   276  		if err := startMetricsServer(cli.Config.MetricsAddress); err != nil {
   277  			return err
   278  		}
   279  	}
   280  
   281  	name, _ := os.Hostname()
   282  
   283  	c, err := cluster.New(cluster.Config{
   284  		Root:                   cli.Config.Root,
   285  		Name:                   name,
   286  		Backend:                d,
   287  		NetworkSubnetsProvider: d,
   288  		DefaultAdvertiseAddr:   cli.Config.SwarmDefaultAdvertiseAddr,
   289  		RuntimeRoot:            cli.getSwarmRunRoot(),
   290  	})
   291  	if err != nil {
   292  		logrus.Fatalf("Error creating cluster component: %v", err)
   293  	}
   294  
   295  	// Restart all autostart containers which has a swarm endpoint
   296  	// and is not yet running now that we have successfully
   297  	// initialized the cluster.
   298  	d.RestartSwarmContainers()
   299  
   300  	logrus.Info("Daemon has completed initialization")
   301  
   302  	logrus.WithFields(logrus.Fields{
   303  		"version":     dockerversion.Version,
   304  		"commit":      dockerversion.GitCommit,
   305  		"graphdriver": d.GraphDriverName(),
   306  	}).Info("Docker daemon")
   307  
   308  	cli.d = d
   309  
   310  	// initMiddlewares needs cli.d to be populated. Dont change this init order.
   311  	if err := cli.initMiddlewares(api, serverConfig); err != nil {
   312  		logrus.Fatalf("Error creating middlewares: %v", err)
   313  	}
   314  	d.SetCluster(c)
   315  	initRouter(api, d, c)
   316  
   317  	cli.setupConfigReloadTrap()
   318  
   319  	// The serve API routine never exits unless an error occurs
   320  	// We need to start it as a goroutine and wait on it so
   321  	// daemon doesn't exit
   322  	serveAPIWait := make(chan error)
   323  	go api.Wait(serveAPIWait)
   324  
   325  	// after the daemon is done setting up we can notify systemd api
   326  	notifySystem()
   327  
   328  	// Daemon is fully initialized and handling API traffic
   329  	// Wait for serve API to complete
   330  	errAPI := <-serveAPIWait
   331  	c.Cleanup()
   332  	shutdownDaemon(d)
   333  	containerdRemote.Cleanup()
   334  	if errAPI != nil {
   335  		return fmt.Errorf("Shutting down due to ServeAPI error: %v", errAPI)
   336  	}
   337  
   338  	return nil
   339  }
   340  
   341  func (cli *DaemonCli) reloadConfig() {
   342  	reload := func(config *config.Config) {
   343  
   344  		// Revalidate and reload the authorization plugins
   345  		if err := validateAuthzPlugins(config.AuthorizationPlugins, cli.d.PluginStore); err != nil {
   346  			logrus.Fatalf("Error validating authorization plugin: %v", err)
   347  			return
   348  		}
   349  		cli.authzMiddleware.SetPlugins(config.AuthorizationPlugins)
   350  
   351  		if err := cli.d.Reload(config); err != nil {
   352  			logrus.Errorf("Error reconfiguring the daemon: %v", err)
   353  			return
   354  		}
   355  
   356  		if config.IsValueSet("debug") {
   357  			debugEnabled := debug.IsEnabled()
   358  			switch {
   359  			case debugEnabled && !config.Debug: // disable debug
   360  				debug.Disable()
   361  				cli.api.DisableProfiler()
   362  			case config.Debug && !debugEnabled: // enable debug
   363  				debug.Enable()
   364  				cli.api.EnableProfiler()
   365  			}
   366  
   367  		}
   368  	}
   369  
   370  	if err := config.Reload(*cli.configFile, cli.flags, reload); err != nil {
   371  		logrus.Error(err)
   372  	}
   373  }
   374  
   375  func (cli *DaemonCli) stop() {
   376  	cli.api.Close()
   377  }
   378  
   379  // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
   380  // d.Shutdown() is waiting too long to kill container or worst it's
   381  // blocked there
   382  func shutdownDaemon(d *daemon.Daemon) {
   383  	shutdownTimeout := d.ShutdownTimeout()
   384  	ch := make(chan struct{})
   385  	go func() {
   386  		d.Shutdown()
   387  		close(ch)
   388  	}()
   389  	if shutdownTimeout < 0 {
   390  		<-ch
   391  		logrus.Debug("Clean shutdown succeeded")
   392  		return
   393  	}
   394  	select {
   395  	case <-ch:
   396  		logrus.Debug("Clean shutdown succeeded")
   397  	case <-time.After(time.Duration(shutdownTimeout) * time.Second):
   398  		logrus.Error("Force shutdown daemon")
   399  	}
   400  }
   401  
   402  func loadDaemonCliConfig(opts daemonOptions) (*config.Config, error) {
   403  	conf := opts.daemonConfig
   404  	flags := opts.flags
   405  	conf.Debug = opts.common.Debug
   406  	conf.Hosts = opts.common.Hosts
   407  	conf.LogLevel = opts.common.LogLevel
   408  	conf.TLS = opts.common.TLS
   409  	conf.TLSVerify = opts.common.TLSVerify
   410  	conf.CommonTLSOptions = config.CommonTLSOptions{}
   411  
   412  	if opts.common.TLSOptions != nil {
   413  		conf.CommonTLSOptions.CAFile = opts.common.TLSOptions.CAFile
   414  		conf.CommonTLSOptions.CertFile = opts.common.TLSOptions.CertFile
   415  		conf.CommonTLSOptions.KeyFile = opts.common.TLSOptions.KeyFile
   416  	}
   417  
   418  	if opts.configFile != "" {
   419  		c, err := config.MergeDaemonConfigurations(conf, flags, opts.configFile)
   420  		if err != nil {
   421  			if flags.Changed(flagDaemonConfigFile) || !os.IsNotExist(err) {
   422  				return nil, fmt.Errorf("unable to configure the Docker daemon with file %s: %v\n", opts.configFile, err)
   423  			}
   424  		}
   425  		// the merged configuration can be nil if the config file didn't exist.
   426  		// leave the current configuration as it is if when that happens.
   427  		if c != nil {
   428  			conf = c
   429  		}
   430  	}
   431  
   432  	if err := config.Validate(conf); err != nil {
   433  		return nil, err
   434  	}
   435  
   436  	// Labels of the docker engine used to allow multiple values associated with the same key.
   437  	// This is deprecated in 1.13, and, be removed after 3 release cycles.
   438  	// The following will check the conflict of labels, and report a warning for deprecation.
   439  	//
   440  	// TODO: After 3 release cycles (1.16) an error will be returned, and labels will be
   441  	// sanitized to consolidate duplicate key-value pairs (config.Labels = newLabels):
   442  	//
   443  	// newLabels, err := daemon.GetConflictFreeLabels(config.Labels)
   444  	// if err != nil {
   445  	//	return nil, err
   446  	// }
   447  	// config.Labels = newLabels
   448  	//
   449  	if _, err := config.GetConflictFreeLabels(conf.Labels); err != nil {
   450  		logrus.Warnf("Engine labels with duplicate keys and conflicting values have been deprecated: %s", err)
   451  	}
   452  
   453  	// Regardless of whether the user sets it to true or false, if they
   454  	// specify TLSVerify at all then we need to turn on TLS
   455  	if conf.IsValueSet(cliflags.FlagTLSVerify) {
   456  		conf.TLS = true
   457  	}
   458  
   459  	// ensure that the log level is the one set after merging configurations
   460  	cliflags.SetLogLevel(conf.LogLevel)
   461  
   462  	return conf, nil
   463  }
   464  
   465  func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) {
   466  	decoder := runconfig.ContainerDecoder{}
   467  
   468  	routers := []router.Router{
   469  		// we need to add the checkpoint router before the container router or the DELETE gets masked
   470  		checkpointrouter.NewRouter(d, decoder),
   471  		container.NewRouter(d, decoder),
   472  		image.NewRouter(d, decoder),
   473  		systemrouter.NewRouter(d, c),
   474  		volume.NewRouter(d),
   475  		build.NewRouter(dockerfile.NewBuildManager(d)),
   476  		swarmrouter.NewRouter(c),
   477  		pluginrouter.NewRouter(d.PluginManager()),
   478  	}
   479  
   480  	if d.NetworkControllerEnabled() {
   481  		routers = append(routers, network.NewRouter(d, c))
   482  	}
   483  
   484  	if d.HasExperimental() {
   485  		for _, r := range routers {
   486  			for _, route := range r.Routes() {
   487  				if experimental, ok := route.(router.ExperimentalRoute); ok {
   488  					experimental.Enable()
   489  				}
   490  			}
   491  		}
   492  	}
   493  
   494  	s.InitRouter(debug.IsEnabled(), routers...)
   495  }
   496  
   497  func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config) error {
   498  	v := cfg.Version
   499  
   500  	exp := middleware.NewExperimentalMiddleware(cli.d.HasExperimental())
   501  	s.UseMiddleware(exp)
   502  
   503  	vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, api.MinVersion)
   504  	s.UseMiddleware(vm)
   505  
   506  	if cfg.EnableCors {
   507  		c := middleware.NewCORSMiddleware(cfg.CorsHeaders)
   508  		s.UseMiddleware(c)
   509  	}
   510  
   511  	if err := validateAuthzPlugins(cli.Config.AuthorizationPlugins, cli.d.PluginStore); err != nil {
   512  		return fmt.Errorf("Error validating authorization plugin: %v", err)
   513  	}
   514  	cli.authzMiddleware = authorization.NewMiddleware(cli.Config.AuthorizationPlugins, cli.d.PluginStore)
   515  	s.UseMiddleware(cli.authzMiddleware)
   516  	return nil
   517  }
   518  
   519  // validates that the plugins requested with the --authorization-plugin flag are valid AuthzDriver
   520  // plugins present on the host and available to the daemon
   521  func validateAuthzPlugins(requestedPlugins []string, pg plugingetter.PluginGetter) error {
   522  	for _, reqPlugin := range requestedPlugins {
   523  		if _, err := pg.Get(reqPlugin, authorization.AuthZApiImplements, plugingetter.Lookup); err != nil {
   524  			return err
   525  		}
   526  	}
   527  	return nil
   528  }