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