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 }