github.com/go/docker@v1.12.0-rc2/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 "github.com/docker/docker/api/server/router/container" 21 "github.com/docker/docker/api/server/router/image" 22 "github.com/docker/docker/api/server/router/network" 23 swarmrouter "github.com/docker/docker/api/server/router/swarm" 24 systemrouter "github.com/docker/docker/api/server/router/system" 25 "github.com/docker/docker/api/server/router/volume" 26 "github.com/docker/docker/builder/dockerfile" 27 cliflags "github.com/docker/docker/cli/flags" 28 "github.com/docker/docker/cliconfig" 29 "github.com/docker/docker/daemon" 30 "github.com/docker/docker/daemon/cluster" 31 "github.com/docker/docker/daemon/logger" 32 "github.com/docker/docker/dockerversion" 33 "github.com/docker/docker/libcontainerd" 34 "github.com/docker/docker/opts" 35 "github.com/docker/docker/pkg/authorization" 36 "github.com/docker/docker/pkg/jsonlog" 37 "github.com/docker/docker/pkg/listeners" 38 flag "github.com/docker/docker/pkg/mflag" 39 "github.com/docker/docker/pkg/pidfile" 40 "github.com/docker/docker/pkg/signal" 41 "github.com/docker/docker/pkg/system" 42 "github.com/docker/docker/registry" 43 "github.com/docker/docker/runconfig" 44 "github.com/docker/docker/utils" 45 "github.com/docker/go-connections/tlsconfig" 46 ) 47 48 const ( 49 daemonConfigFileFlag = "-config-file" 50 ) 51 52 // DaemonCli represents the daemon CLI. 53 type DaemonCli struct { 54 *daemon.Config 55 commonFlags *cliflags.CommonFlags 56 configFile *string 57 58 api *apiserver.Server 59 d *daemon.Daemon 60 } 61 62 func presentInHelp(usage string) string { return usage } 63 func absentFromHelp(string) string { return "" } 64 65 // NewDaemonCli returns a pre-configured daemon CLI 66 func NewDaemonCli() *DaemonCli { 67 // TODO(tiborvass): remove InstallFlags? 68 daemonConfig := new(daemon.Config) 69 daemonConfig.LogConfig.Config = make(map[string]string) 70 daemonConfig.ClusterOpts = make(map[string]string) 71 72 if runtime.GOOS != "linux" { 73 daemonConfig.V2Only = true 74 } 75 76 daemonConfig.InstallFlags(flag.CommandLine, presentInHelp) 77 configFile := flag.CommandLine.String([]string{daemonConfigFileFlag}, defaultDaemonConfigFile, "Daemon configuration file") 78 flag.CommandLine.Require(flag.Exact, 0) 79 80 return &DaemonCli{ 81 Config: daemonConfig, 82 commonFlags: cliflags.InitCommonFlags(), 83 configFile: configFile, 84 } 85 } 86 87 func migrateKey() (err error) { 88 // Migrate trust key if exists at ~/.docker/key.json and owned by current user 89 oldPath := filepath.Join(cliconfig.ConfigDir(), cliflags.DefaultTrustKeyFile) 90 newPath := filepath.Join(getDaemonConfDir(), cliflags.DefaultTrustKeyFile) 91 if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) { 92 defer func() { 93 // Ensure old path is removed if no error occurred 94 if err == nil { 95 err = os.Remove(oldPath) 96 } else { 97 logrus.Warnf("Key migration failed, key file not removed at %s", oldPath) 98 os.Remove(newPath) 99 } 100 }() 101 102 if err := system.MkdirAll(getDaemonConfDir(), os.FileMode(0644)); err != nil { 103 return fmt.Errorf("Unable to create daemon configuration directory: %s", err) 104 } 105 106 newFile, err := os.OpenFile(newPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) 107 if err != nil { 108 return fmt.Errorf("error creating key file %q: %s", newPath, err) 109 } 110 defer newFile.Close() 111 112 oldFile, err := os.Open(oldPath) 113 if err != nil { 114 return fmt.Errorf("error opening key file %q: %s", oldPath, err) 115 } 116 defer oldFile.Close() 117 118 if _, err := io.Copy(newFile, oldFile); err != nil { 119 return fmt.Errorf("error copying key: %s", err) 120 } 121 122 logrus.Infof("Migrated key from %s to %s", oldPath, newPath) 123 } 124 125 return nil 126 } 127 128 func (cli *DaemonCli) start() (err error) { 129 stopc := make(chan bool) 130 defer close(stopc) 131 132 // warn from uuid package when running the daemon 133 uuid.Loggerf = logrus.Warnf 134 135 flags := flag.CommandLine 136 cli.commonFlags.PostParse() 137 138 if cli.commonFlags.TrustKey == "" { 139 cli.commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), cliflags.DefaultTrustKeyFile) 140 } 141 cliConfig, err := loadDaemonCliConfig(cli.Config, flags, cli.commonFlags, *cli.configFile) 142 if err != nil { 143 return err 144 } 145 cli.Config = cliConfig 146 147 if cli.Config.Debug { 148 utils.EnableDebug() 149 } 150 151 if utils.ExperimentalBuild() { 152 logrus.Warn("Running experimental build") 153 } 154 155 logrus.SetFormatter(&logrus.TextFormatter{ 156 TimestampFormat: jsonlog.RFC3339NanoFixed, 157 DisableColors: cli.Config.RawLogs, 158 }) 159 160 if err := setDefaultUmask(); err != nil { 161 return fmt.Errorf("Failed to set umask: %v", err) 162 } 163 164 if len(cli.LogConfig.Config) > 0 { 165 if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { 166 return fmt.Errorf("Failed to set log opts: %v", err) 167 } 168 } 169 170 if cli.Pidfile != "" { 171 pf, err := pidfile.New(cli.Pidfile) 172 if err != nil { 173 return fmt.Errorf("Error starting daemon: %v", err) 174 } 175 defer func() { 176 if err := pf.Remove(); err != nil { 177 logrus.Error(err) 178 } 179 }() 180 } 181 182 serverConfig := &apiserver.Config{ 183 Logging: true, 184 SocketGroup: cli.Config.SocketGroup, 185 Version: dockerversion.Version, 186 EnableCors: cli.Config.EnableCors, 187 CorsHeaders: cli.Config.CorsHeaders, 188 } 189 190 if cli.Config.TLS { 191 tlsOptions := tlsconfig.Options{ 192 CAFile: cli.Config.CommonTLSOptions.CAFile, 193 CertFile: cli.Config.CommonTLSOptions.CertFile, 194 KeyFile: cli.Config.CommonTLSOptions.KeyFile, 195 } 196 197 if cli.Config.TLSVerify { 198 // server requires and verifies client's certificate 199 tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert 200 } 201 tlsConfig, err := tlsconfig.Server(tlsOptions) 202 if err != nil { 203 return err 204 } 205 serverConfig.TLSConfig = tlsConfig 206 } 207 208 if len(cli.Config.Hosts) == 0 { 209 cli.Config.Hosts = make([]string, 1) 210 } 211 212 api := apiserver.New(serverConfig) 213 cli.api = api 214 215 for i := 0; i < len(cli.Config.Hosts); i++ { 216 var err error 217 if cli.Config.Hosts[i], err = opts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil { 218 return fmt.Errorf("error parsing -H %s : %v", cli.Config.Hosts[i], err) 219 } 220 221 protoAddr := cli.Config.Hosts[i] 222 protoAddrParts := strings.SplitN(protoAddr, "://", 2) 223 if len(protoAddrParts) != 2 { 224 return fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr) 225 } 226 227 proto := protoAddrParts[0] 228 addr := protoAddrParts[1] 229 230 // It's a bad idea to bind to TCP without tlsverify. 231 if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) { 232 logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]") 233 } 234 ls, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig) 235 if err != nil { 236 return err 237 } 238 ls = wrapListeners(proto, ls) 239 // If we're binding to a TCP port, make sure that a container doesn't try to use it. 240 if proto == "tcp" { 241 if err := allocateDaemonPort(addr); err != nil { 242 return err 243 } 244 } 245 logrus.Debugf("Listener created for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) 246 api.Accept(protoAddrParts[1], ls...) 247 } 248 249 if err := migrateKey(); err != nil { 250 return err 251 } 252 cli.TrustKeyPath = cli.commonFlags.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 cli.api = api 260 signal.Trap(func() { 261 cli.stop() 262 <-stopc // wait for daemonCli.start() to return 263 }) 264 265 if err := pluginInit(cli.Config, containerdRemote, registryService); err != nil { 266 return err 267 } 268 269 d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote) 270 if err != nil { 271 return fmt.Errorf("Error starting daemon: %v", err) 272 } 273 274 name, _ := os.Hostname() 275 276 c, err := cluster.New(cluster.Config{ 277 Root: cli.Config.Root, 278 Name: name, 279 Backend: d, 280 }) 281 if err != nil { 282 logrus.Fatalf("Error creating cluster component: %v", err) 283 } 284 285 logrus.Info("Daemon has completed initialization") 286 287 logrus.WithFields(logrus.Fields{ 288 "version": dockerversion.Version, 289 "commit": dockerversion.GitCommit, 290 "graphdriver": d.GraphDriverName(), 291 }).Info("Docker daemon") 292 293 cli.initMiddlewares(api, serverConfig) 294 initRouter(api, d, c) 295 296 cli.d = d 297 cli.setupConfigReloadTrap() 298 299 // The serve API routine never exits unless an error occurs 300 // We need to start it as a goroutine and wait on it so 301 // daemon doesn't exit 302 serveAPIWait := make(chan error) 303 go api.Wait(serveAPIWait) 304 305 // after the daemon is done setting up we can notify systemd api 306 notifySystem() 307 308 // Daemon is fully initialized and handling API traffic 309 // Wait for serve API to complete 310 errAPI := <-serveAPIWait 311 c.Cleanup() 312 shutdownDaemon(d, 15) 313 containerdRemote.Cleanup() 314 if errAPI != nil { 315 return fmt.Errorf("Shutting down due to ServeAPI error: %v", errAPI) 316 } 317 318 return nil 319 } 320 321 func (cli *DaemonCli) reloadConfig() { 322 reload := func(config *daemon.Config) { 323 if err := cli.d.Reload(config); err != nil { 324 logrus.Errorf("Error reconfiguring the daemon: %v", err) 325 return 326 } 327 if config.IsValueSet("debug") { 328 debugEnabled := utils.IsDebugEnabled() 329 switch { 330 case debugEnabled && !config.Debug: // disable debug 331 utils.DisableDebug() 332 cli.api.DisableProfiler() 333 case config.Debug && !debugEnabled: // enable debug 334 utils.EnableDebug() 335 cli.api.EnableProfiler() 336 } 337 338 } 339 } 340 341 if err := daemon.ReloadConfiguration(*cli.configFile, flag.CommandLine, reload); err != nil { 342 logrus.Error(err) 343 } 344 } 345 346 func (cli *DaemonCli) stop() { 347 cli.api.Close() 348 } 349 350 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case 351 // d.Shutdown() is waiting too long to kill container or worst it's 352 // blocked there 353 func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) { 354 ch := make(chan struct{}) 355 go func() { 356 d.Shutdown() 357 close(ch) 358 }() 359 select { 360 case <-ch: 361 logrus.Debug("Clean shutdown succeeded") 362 case <-time.After(timeout * time.Second): 363 logrus.Error("Force shutdown daemon") 364 } 365 } 366 367 func loadDaemonCliConfig(config *daemon.Config, flags *flag.FlagSet, commonConfig *cliflags.CommonFlags, configFile string) (*daemon.Config, error) { 368 config.Debug = commonConfig.Debug 369 config.Hosts = commonConfig.Hosts 370 config.LogLevel = commonConfig.LogLevel 371 config.TLS = commonConfig.TLS 372 config.TLSVerify = commonConfig.TLSVerify 373 config.CommonTLSOptions = daemon.CommonTLSOptions{} 374 375 if commonConfig.TLSOptions != nil { 376 config.CommonTLSOptions.CAFile = commonConfig.TLSOptions.CAFile 377 config.CommonTLSOptions.CertFile = commonConfig.TLSOptions.CertFile 378 config.CommonTLSOptions.KeyFile = commonConfig.TLSOptions.KeyFile 379 } 380 381 if configFile != "" { 382 c, err := daemon.MergeDaemonConfigurations(config, flags, configFile) 383 if err != nil { 384 if flags.IsSet(daemonConfigFileFlag) || !os.IsNotExist(err) { 385 return nil, fmt.Errorf("unable to configure the Docker daemon with file %s: %v\n", configFile, err) 386 } 387 } 388 // the merged configuration can be nil if the config file didn't exist. 389 // leave the current configuration as it is if when that happens. 390 if c != nil { 391 config = c 392 } 393 } 394 395 if err := daemon.ValidateConfiguration(config); err != nil { 396 return nil, err 397 } 398 399 // Regardless of whether the user sets it to true or false, if they 400 // specify TLSVerify at all then we need to turn on TLS 401 if config.IsValueSet(cliflags.TLSVerifyKey) { 402 config.TLS = true 403 } 404 405 // ensure that the log level is the one set after merging configurations 406 cliflags.SetDaemonLogLevel(config.LogLevel) 407 408 return config, nil 409 } 410 411 func initRouter(s *apiserver.Server, d *daemon.Daemon, c *cluster.Cluster) { 412 decoder := runconfig.ContainerDecoder{} 413 414 routers := []router.Router{ 415 container.NewRouter(d, decoder), 416 image.NewRouter(d, decoder), 417 systemrouter.NewRouter(d, c), 418 volume.NewRouter(d), 419 build.NewRouter(dockerfile.NewBuildManager(d)), 420 swarmrouter.NewRouter(c), 421 } 422 if d.NetworkControllerEnabled() { 423 routers = append(routers, network.NewRouter(d, c)) 424 } 425 routers = addExperimentalRouters(routers) 426 427 s.InitRouter(utils.IsDebugEnabled(), routers...) 428 } 429 430 func (cli *DaemonCli) initMiddlewares(s *apiserver.Server, cfg *apiserver.Config) { 431 v := cfg.Version 432 433 vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, api.MinVersion) 434 s.UseMiddleware(vm) 435 436 if cfg.EnableCors { 437 c := middleware.NewCORSMiddleware(cfg.CorsHeaders) 438 s.UseMiddleware(c) 439 } 440 441 u := middleware.NewUserAgentMiddleware(v) 442 s.UseMiddleware(u) 443 444 if len(cli.Config.AuthorizationPlugins) > 0 { 445 authZPlugins := authorization.NewPlugins(cli.Config.AuthorizationPlugins) 446 handleAuthorization := authorization.NewMiddleware(authZPlugins) 447 s.UseMiddleware(handleAuthorization) 448 } 449 }