github.com/kim0/docker@v0.6.2-0.20161130212042-4addda3f07e7/cmd/dockerd/daemon.go (about) 1 package main 2 3 import ( 4 "crypto/tls" 5 "fmt" 6 "io" 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 "github.com/docker/docker/api/server/middleware" 17 "github.com/docker/docker/api/server/router" 18 "github.com/docker/docker/api/server/router/build" 19 "github.com/docker/docker/api/server/router/container" 20 "github.com/docker/docker/api/server/router/image" 21 "github.com/docker/docker/api/server/router/network" 22 swarmrouter "github.com/docker/docker/api/server/router/swarm" 23 systemrouter "github.com/docker/docker/api/server/router/system" 24 "github.com/docker/docker/api/server/router/volume" 25 "github.com/docker/docker/builder/dockerfile" 26 cliflags "github.com/docker/docker/cli/flags" 27 "github.com/docker/docker/cliconfig" 28 "github.com/docker/docker/daemon" 29 "github.com/docker/docker/daemon/cluster" 30 "github.com/docker/docker/daemon/logger" 31 "github.com/docker/docker/dockerversion" 32 "github.com/docker/docker/libcontainerd" 33 dopts "github.com/docker/docker/opts" 34 "github.com/docker/docker/pkg/authorization" 35 "github.com/docker/docker/pkg/jsonlog" 36 "github.com/docker/docker/pkg/listeners" 37 "github.com/docker/docker/pkg/pidfile" 38 "github.com/docker/docker/pkg/signal" 39 "github.com/docker/docker/pkg/system" 40 "github.com/docker/docker/registry" 41 "github.com/docker/docker/runconfig" 42 "github.com/docker/docker/utils" 43 "github.com/docker/go-connections/tlsconfig" 44 "github.com/spf13/pflag" 45 ) 46 47 const ( 48 flagDaemonConfigFile = "config-file" 49 ) 50 51 // DaemonCli represents the daemon CLI. 52 type DaemonCli struct { 53 *daemon.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 migrateKey() (err error) { 68 // Migrate trust key if exists at ~/.docker/key.json and owned by current user 69 oldPath := filepath.Join(cliconfig.ConfigDir(), cliflags.DefaultTrustKeyFile) 70 newPath := filepath.Join(getDaemonConfDir(), cliflags.DefaultTrustKeyFile) 71 if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) { 72 defer func() { 73 // Ensure old path is removed if no error occurred 74 if err == nil { 75 err = os.Remove(oldPath) 76 } else { 77 logrus.Warnf("Key migration failed, key file not removed at %s", oldPath) 78 os.Remove(newPath) 79 } 80 }() 81 82 if err := system.MkdirAll(getDaemonConfDir(), os.FileMode(0644)); err != nil { 83 return fmt.Errorf("Unable to create daemon configuration directory: %s", err) 84 } 85 86 newFile, err := os.OpenFile(newPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) 87 if err != nil { 88 return fmt.Errorf("error creating key file %q: %s", newPath, err) 89 } 90 defer newFile.Close() 91 92 oldFile, err := os.Open(oldPath) 93 if err != nil { 94 return fmt.Errorf("error opening key file %q: %s", oldPath, err) 95 } 96 defer oldFile.Close() 97 98 if _, err := io.Copy(newFile, oldFile); err != nil { 99 return fmt.Errorf("error copying key: %s", err) 100 } 101 102 logrus.Infof("Migrated key from %s to %s", oldPath, newPath) 103 } 104 105 return nil 106 } 107 108 func (cli *DaemonCli) start(opts daemonOptions) (err error) { 109 stopc := make(chan bool) 110 defer close(stopc) 111 112 // warn from uuid package when running the daemon 113 uuid.Loggerf = logrus.Warnf 114 115 opts.common.SetDefaultOptions(opts.flags) 116 117 if opts.common.TrustKey == "" { 118 opts.common.TrustKey = filepath.Join( 119 getDaemonConfDir(), 120 cliflags.DefaultTrustKeyFile) 121 } 122 if cli.Config, err = loadDaemonCliConfig(opts); err != nil { 123 return err 124 } 125 cli.configFile = &opts.configFile 126 cli.flags = opts.flags 127 128 if cli.Config.Debug { 129 utils.EnableDebug() 130 } 131 132 if cli.Config.Experimental { 133 logrus.Warn("Running experimental build") 134 } 135 136 logrus.SetFormatter(&logrus.TextFormatter{ 137 TimestampFormat: jsonlog.RFC3339NanoFixed, 138 DisableColors: cli.Config.RawLogs, 139 }) 140 141 if err := setDefaultUmask(); err != nil { 142 return fmt.Errorf("Failed to set umask: %v", err) 143 } 144 145 if len(cli.LogConfig.Config) > 0 { 146 if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { 147 return fmt.Errorf("Failed to set log opts: %v", err) 148 } 149 } 150 151 if cli.Pidfile != "" { 152 pf, err := pidfile.New(cli.Pidfile) 153 if err != nil { 154 return fmt.Errorf("Error starting daemon: %v", err) 155 } 156 defer func() { 157 if err := pf.Remove(); err != nil { 158 logrus.Error(err) 159 } 160 }() 161 } 162 163 serverConfig := &apiserver.Config{ 164 Logging: true, 165 SocketGroup: cli.Config.SocketGroup, 166 Version: dockerversion.Version, 167 EnableCors: cli.Config.EnableCors, 168 CorsHeaders: cli.Config.CorsHeaders, 169 } 170 171 if cli.Config.TLS { 172 tlsOptions := tlsconfig.Options{ 173 CAFile: cli.Config.CommonTLSOptions.CAFile, 174 CertFile: cli.Config.CommonTLSOptions.CertFile, 175 KeyFile: cli.Config.CommonTLSOptions.KeyFile, 176 } 177 178 if cli.Config.TLSVerify { 179 // server requires and verifies client's certificate 180 tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert 181 } 182 tlsConfig, err := tlsconfig.Server(tlsOptions) 183 if err != nil { 184 return err 185 } 186 serverConfig.TLSConfig = tlsConfig 187 } 188 189 if len(cli.Config.Hosts) == 0 { 190 cli.Config.Hosts = make([]string, 1) 191 } 192 193 api := apiserver.New(serverConfig) 194 cli.api = api 195 196 for i := 0; i < len(cli.Config.Hosts); i++ { 197 var err error 198 if cli.Config.Hosts[i], err = dopts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil { 199 return fmt.Errorf("error parsing -H %s : %v", cli.Config.Hosts[i], err) 200 } 201 202 protoAddr := cli.Config.Hosts[i] 203 protoAddrParts := strings.SplitN(protoAddr, "://", 2) 204 if len(protoAddrParts) != 2 { 205 return fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr) 206 } 207 208 proto := protoAddrParts[0] 209 addr := protoAddrParts[1] 210 211 // It's a bad idea to bind to TCP without tlsverify. 212 if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) { 213 logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]") 214 } 215 ls, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig) 216 if err != nil { 217 return err 218 } 219 ls = wrapListeners(proto, ls) 220 // If we're binding to a TCP port, make sure that a container doesn't try to use it. 221 if proto == "tcp" { 222 if err := allocateDaemonPort(addr); err != nil { 223 return err 224 } 225 } 226 logrus.Debugf("Listener created for HTTP on %s (%s)", proto, addr) 227 api.Accept(addr, ls...) 228 } 229 230 if err := migrateKey(); err != nil { 231 return err 232 } 233 // FIXME: why is this down here instead of with the other TrustKey logic above? 234 cli.TrustKeyPath = opts.common.TrustKey 235 236 registryService := registry.NewService(cli.Config.ServiceOptions) 237 containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...) 238 if err != nil { 239 return err 240 } 241 signal.Trap(func() { 242 cli.stop() 243 <-stopc // wait for daemonCli.start() to return 244 }) 245 246 d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote) 247 if err != nil { 248 return fmt.Errorf("Error starting daemon: %v", err) 249 } 250 251 if cli.Config.MetricsAddress != "" { 252 if !d.HasExperimental() { 253 return fmt.Errorf("metrics-addr is only supported when experimental is enabled") 254 } 255 if err := startMetricsServer(cli.Config.MetricsAddress); err != nil { 256 return err 257 } 258 } 259 260 name, _ := os.Hostname() 261 262 c, err := cluster.New(cluster.Config{ 263 Root: cli.Config.Root, 264 Name: name, 265 Backend: d, 266 NetworkSubnetsProvider: d, 267 DefaultAdvertiseAddr: cli.Config.SwarmDefaultAdvertiseAddr, 268 RuntimeRoot: cli.getSwarmRunRoot(), 269 }) 270 if err != nil { 271 logrus.Fatalf("Error creating cluster component: %v", err) 272 } 273 274 // Restart all autostart containers which has a swarm endpoint 275 // and is not yet running now that we have successfully 276 // initialized the cluster. 277 d.RestartSwarmContainers() 278 279 logrus.Info("Daemon has completed initialization") 280 281 logrus.WithFields(logrus.Fields{ 282 "version": dockerversion.Version, 283 "commit": dockerversion.GitCommit, 284 "graphdriver": d.GraphDriverName(), 285 }).Info("Docker daemon") 286 287 cli.d = d 288 289 // initMiddlewares needs cli.d to be populated. Dont change this init order. 290 cli.initMiddlewares(api, serverConfig) 291 d.SetCluster(c) 292 initRouter(api, d, c) 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 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 func (cli *DaemonCli) reloadConfig() { 319 reload := func(config *daemon.Config) { 320 321 // Reload the authorization plugin 322 cli.authzMiddleware.SetPlugins(config.AuthorizationPlugins) 323 324 if err := cli.d.Reload(config); err != nil { 325 logrus.Errorf("Error reconfiguring the daemon: %v", err) 326 return 327 } 328 329 if config.IsValueSet("debug") { 330 debugEnabled := utils.IsDebugEnabled() 331 switch { 332 case debugEnabled && !config.Debug: // disable debug 333 utils.DisableDebug() 334 cli.api.DisableProfiler() 335 case config.Debug && !debugEnabled: // enable debug 336 utils.EnableDebug() 337 cli.api.EnableProfiler() 338 } 339 340 } 341 } 342 343 if err := daemon.ReloadConfiguration(*cli.configFile, cli.flags, reload); err != nil { 344 logrus.Error(err) 345 } 346 } 347 348 func (cli *DaemonCli) stop() { 349 cli.api.Close() 350 } 351 352 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case 353 // d.Shutdown() is waiting too long to kill container or worst it's 354 // blocked there 355 func shutdownDaemon(d *daemon.Daemon) { 356 shutdownTimeout := d.ShutdownTimeout() 357 ch := make(chan struct{}) 358 go func() { 359 d.Shutdown() 360 close(ch) 361 }() 362 if shutdownTimeout < 0 { 363 <-ch 364 logrus.Debug("Clean shutdown succeeded") 365 return 366 } 367 select { 368 case <-ch: 369 logrus.Debug("Clean shutdown succeeded") 370 case <-time.After(time.Duration(shutdownTimeout) * time.Second): 371 logrus.Error("Force shutdown daemon") 372 } 373 } 374 375 func loadDaemonCliConfig(opts daemonOptions) (*daemon.Config, error) { 376 config := opts.daemonConfig 377 flags := opts.flags 378 config.Debug = opts.common.Debug 379 config.Hosts = opts.common.Hosts 380 config.LogLevel = opts.common.LogLevel 381 config.TLS = opts.common.TLS 382 config.TLSVerify = opts.common.TLSVerify 383 config.CommonTLSOptions = daemon.CommonTLSOptions{} 384 385 if opts.common.TLSOptions != nil { 386 config.CommonTLSOptions.CAFile = opts.common.TLSOptions.CAFile 387 config.CommonTLSOptions.CertFile = opts.common.TLSOptions.CertFile 388 config.CommonTLSOptions.KeyFile = opts.common.TLSOptions.KeyFile 389 } 390 391 if opts.configFile != "" { 392 c, err := daemon.MergeDaemonConfigurations(config, flags, opts.configFile) 393 if err != nil { 394 if flags.Changed(flagDaemonConfigFile) || !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 config = c 402 } 403 } 404 405 if err := daemon.ValidateConfiguration(config); err != nil { 406 return nil, err 407 } 408 409 // Labels of the docker engine used to allow multiple values associated with the same key. 410 // This is deprecated in 1.13, and, be removed after 3 release cycles. 411 // The following will check the conflict of labels, and report a warning for deprecation. 412 // 413 // TODO: After 3 release cycles (1.16) an error will be returned, and labels will be 414 // sanitized to consolidate duplicate key-value pairs (config.Labels = newLabels): 415 // 416 // newLabels, err := daemon.GetConflictFreeLabels(config.Labels) 417 // if err != nil { 418 // return nil, err 419 // } 420 // config.Labels = newLabels 421 // 422 if _, err := daemon.GetConflictFreeLabels(config.Labels); err != nil { 423 logrus.Warnf("Engine labels with duplicate keys and conflicting values have been deprecated: %s", err) 424 } 425 426 // Regardless of whether the user sets it to true or false, if they 427 // specify TLSVerify at all then we need to turn on TLS 428 if config.IsValueSet(cliflags.FlagTLSVerify) { 429 config.TLS = true 430 } 431 432 // ensure that the log level is the one set after merging configurations 433 cliflags.SetLogLevel(config.LogLevel) 434 435 return config, nil 436 } 437 438 func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) { 439 decoder := runconfig.ContainerDecoder{} 440 441 routers := []router.Router{} 442 443 // we need to add the checkpoint router before the container router or the DELETE gets masked 444 routers = addExperimentalRouters(routers, d, decoder) 445 446 routers = append(routers, []router.Router{ 447 container.NewRouter(d, decoder), 448 image.NewRouter(d, decoder), 449 systemrouter.NewRouter(d, c), 450 volume.NewRouter(d), 451 build.NewRouter(dockerfile.NewBuildManager(d)), 452 swarmrouter.NewRouter(c), 453 }...) 454 455 if d.NetworkControllerEnabled() { 456 routers = append(routers, network.NewRouter(d, c)) 457 } 458 459 s.InitRouter(utils.IsDebugEnabled(), routers...) 460 } 461 462 func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config) { 463 v := cfg.Version 464 465 exp := middleware.NewExperimentalMiddleware(cli.d.HasExperimental()) 466 s.UseMiddleware(exp) 467 468 vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, api.MinVersion) 469 s.UseMiddleware(vm) 470 471 if cfg.EnableCors { 472 c := middleware.NewCORSMiddleware(cfg.CorsHeaders) 473 s.UseMiddleware(c) 474 } 475 476 u := middleware.NewUserAgentMiddleware(v) 477 s.UseMiddleware(u) 478 479 cli.authzMiddleware = authorization.NewMiddleware(cli.Config.AuthorizationPlugins, cli.d.PluginStore) 480 s.UseMiddleware(cli.authzMiddleware) 481 }