github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/cmd/dockerd/daemon.go (about)

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