github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/cmd/dockerd/daemon.go (about)

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