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 }