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