github.com/hustcat/docker@v1.3.3-0.20160314103604-901c67a8eeab/docker/daemon.go (about) 1 // +build daemon 2 3 package main 4 5 import ( 6 "crypto/tls" 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 "runtime" 12 "strings" 13 "time" 14 15 "github.com/Sirupsen/logrus" 16 "github.com/docker/distribution/uuid" 17 apiserver "github.com/docker/docker/api/server" 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 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 "github.com/docker/docker/cli" 27 "github.com/docker/docker/cliconfig" 28 "github.com/docker/docker/daemon" 29 "github.com/docker/docker/daemon/logger" 30 "github.com/docker/docker/docker/listeners" 31 "github.com/docker/docker/dockerversion" 32 "github.com/docker/docker/opts" 33 "github.com/docker/docker/pkg/jsonlog" 34 flag "github.com/docker/docker/pkg/mflag" 35 "github.com/docker/docker/pkg/pidfile" 36 "github.com/docker/docker/pkg/signal" 37 "github.com/docker/docker/pkg/system" 38 "github.com/docker/docker/registry" 39 "github.com/docker/docker/utils" 40 "github.com/docker/go-connections/tlsconfig" 41 ) 42 43 const ( 44 daemonUsage = " docker daemon [ --help | ... ]\n" 45 daemonConfigFileFlag = "-config-file" 46 ) 47 48 var ( 49 daemonCli cli.Handler = NewDaemonCli() 50 ) 51 52 // DaemonCli represents the daemon CLI. 53 type DaemonCli struct { 54 *daemon.Config 55 flags *flag.FlagSet 56 } 57 58 func presentInHelp(usage string) string { return usage } 59 func absentFromHelp(string) string { return "" } 60 61 // NewDaemonCli returns a pre-configured daemon CLI 62 func NewDaemonCli() *DaemonCli { 63 daemonFlags := cli.Subcmd("daemon", nil, "Enable daemon mode", true) 64 65 // TODO(tiborvass): remove InstallFlags? 66 daemonConfig := new(daemon.Config) 67 daemonConfig.LogConfig.Config = make(map[string]string) 68 daemonConfig.ClusterOpts = make(map[string]string) 69 70 if runtime.GOOS != "linux" { 71 daemonConfig.V2Only = true 72 } 73 74 daemonConfig.InstallFlags(daemonFlags, presentInHelp) 75 daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp) 76 daemonFlags.Require(flag.Exact, 0) 77 78 return &DaemonCli{ 79 Config: daemonConfig, 80 flags: daemonFlags, 81 } 82 } 83 84 func migrateKey() (err error) { 85 // Migrate trust key if exists at ~/.docker/key.json and owned by current user 86 oldPath := filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile) 87 newPath := filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) 88 if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) { 89 defer func() { 90 // Ensure old path is removed if no error occurred 91 if err == nil { 92 err = os.Remove(oldPath) 93 } else { 94 logrus.Warnf("Key migration failed, key file not removed at %s", oldPath) 95 os.Remove(newPath) 96 } 97 }() 98 99 if err := system.MkdirAll(getDaemonConfDir(), os.FileMode(0644)); err != nil { 100 return fmt.Errorf("Unable to create daemon configuration directory: %s", err) 101 } 102 103 newFile, err := os.OpenFile(newPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) 104 if err != nil { 105 return fmt.Errorf("error creating key file %q: %s", newPath, err) 106 } 107 defer newFile.Close() 108 109 oldFile, err := os.Open(oldPath) 110 if err != nil { 111 return fmt.Errorf("error opening key file %q: %s", oldPath, err) 112 } 113 defer oldFile.Close() 114 115 if _, err := io.Copy(newFile, oldFile); err != nil { 116 return fmt.Errorf("error copying key: %s", err) 117 } 118 119 logrus.Infof("Migrated key from %s to %s", oldPath, newPath) 120 } 121 122 return nil 123 } 124 125 func getGlobalFlag() (globalFlag *flag.Flag) { 126 defer func() { 127 if x := recover(); x != nil { 128 switch f := x.(type) { 129 case *flag.Flag: 130 globalFlag = f 131 default: 132 panic(x) 133 } 134 } 135 }() 136 visitor := func(f *flag.Flag) { panic(f) } 137 commonFlags.FlagSet.Visit(visitor) 138 clientFlags.FlagSet.Visit(visitor) 139 return 140 } 141 142 // CmdDaemon is the daemon command, called the raw arguments after `docker daemon`. 143 func (cli *DaemonCli) CmdDaemon(args ...string) error { 144 // warn from uuid package when running the daemon 145 uuid.Loggerf = logrus.Warnf 146 147 if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() { 148 // deny `docker -D daemon` 149 illegalFlag := getGlobalFlag() 150 fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0]) 151 os.Exit(1) 152 } else { 153 // allow new form `docker daemon -D` 154 flag.Merge(cli.flags, commonFlags.FlagSet) 155 } 156 157 configFile := cli.flags.String([]string{daemonConfigFileFlag}, defaultDaemonConfigFile, "Daemon configuration file") 158 159 cli.flags.ParseFlags(args, true) 160 commonFlags.PostParse() 161 162 if commonFlags.TrustKey == "" { 163 commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) 164 } 165 cliConfig, err := loadDaemonCliConfig(cli.Config, cli.flags, commonFlags, *configFile) 166 if err != nil { 167 fmt.Fprint(os.Stderr, err) 168 os.Exit(1) 169 } 170 cli.Config = cliConfig 171 172 if cli.Config.Debug { 173 utils.EnableDebug() 174 } 175 176 if utils.ExperimentalBuild() { 177 logrus.Warn("Running experimental build") 178 } 179 180 logrus.SetFormatter(&logrus.TextFormatter{ 181 TimestampFormat: jsonlog.RFC3339NanoFixed, 182 DisableColors: cli.Config.RawLogs, 183 }) 184 185 if err := setDefaultUmask(); err != nil { 186 logrus.Fatalf("Failed to set umask: %v", err) 187 } 188 189 if len(cli.LogConfig.Config) > 0 { 190 if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { 191 logrus.Fatalf("Failed to set log opts: %v", err) 192 } 193 } 194 195 var pfile *pidfile.PIDFile 196 if cli.Pidfile != "" { 197 pf, err := pidfile.New(cli.Pidfile) 198 if err != nil { 199 logrus.Fatalf("Error starting daemon: %v", err) 200 } 201 pfile = pf 202 defer func() { 203 if err := pfile.Remove(); err != nil { 204 logrus.Error(err) 205 } 206 }() 207 } 208 209 serverConfig := &apiserver.Config{ 210 AuthorizationPluginNames: cli.Config.AuthorizationPlugins, 211 Logging: true, 212 SocketGroup: cli.Config.SocketGroup, 213 Version: dockerversion.Version, 214 } 215 serverConfig = setPlatformServerConfig(serverConfig, cli.Config) 216 217 if cli.Config.TLS { 218 tlsOptions := tlsconfig.Options{ 219 CAFile: cli.Config.CommonTLSOptions.CAFile, 220 CertFile: cli.Config.CommonTLSOptions.CertFile, 221 KeyFile: cli.Config.CommonTLSOptions.KeyFile, 222 } 223 224 if cli.Config.TLSVerify { 225 // server requires and verifies client's certificate 226 tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert 227 } 228 tlsConfig, err := tlsconfig.Server(tlsOptions) 229 if err != nil { 230 logrus.Fatal(err) 231 } 232 serverConfig.TLSConfig = tlsConfig 233 } 234 235 if len(cli.Config.Hosts) == 0 { 236 cli.Config.Hosts = make([]string, 1) 237 } 238 239 api := apiserver.New(serverConfig) 240 241 for i := 0; i < len(cli.Config.Hosts); i++ { 242 var err error 243 if cli.Config.Hosts[i], err = opts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil { 244 logrus.Fatalf("error parsing -H %s : %v", cli.Config.Hosts[i], err) 245 } 246 247 protoAddr := cli.Config.Hosts[i] 248 protoAddrParts := strings.SplitN(protoAddr, "://", 2) 249 if len(protoAddrParts) != 2 { 250 logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr) 251 } 252 l, err := listeners.Init(protoAddrParts[0], protoAddrParts[1], serverConfig.SocketGroup, serverConfig.TLSConfig) 253 if err != nil { 254 logrus.Fatal(err) 255 } 256 257 logrus.Debugf("Listener created for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) 258 api.Accept(protoAddrParts[1], l...) 259 } 260 261 if err := migrateKey(); err != nil { 262 logrus.Fatal(err) 263 } 264 cli.TrustKeyPath = commonFlags.TrustKey 265 266 registryService := registry.NewService(cli.Config.ServiceOptions) 267 d, err := daemon.NewDaemon(cli.Config, registryService) 268 if err != nil { 269 if pfile != nil { 270 if err := pfile.Remove(); err != nil { 271 logrus.Error(err) 272 } 273 } 274 logrus.Fatalf("Error starting daemon: %v", err) 275 } 276 277 logrus.Info("Daemon has completed initialization") 278 279 logrus.WithFields(logrus.Fields{ 280 "version": dockerversion.Version, 281 "commit": dockerversion.GitCommit, 282 "execdriver": d.ExecutionDriver().Name(), 283 "graphdriver": d.GraphDriverName(), 284 }).Info("Docker daemon") 285 286 initRouter(api, d) 287 288 reload := func(config *daemon.Config) { 289 if err := d.Reload(config); err != nil { 290 logrus.Errorf("Error reconfiguring the daemon: %v", err) 291 return 292 } 293 if config.IsValueSet("debug") { 294 debugEnabled := utils.IsDebugEnabled() 295 switch { 296 case debugEnabled && !config.Debug: // disable debug 297 utils.DisableDebug() 298 api.DisableProfiler() 299 case config.Debug && !debugEnabled: // enable debug 300 utils.EnableDebug() 301 api.EnableProfiler() 302 } 303 304 } 305 } 306 307 setupConfigReloadTrap(*configFile, cli.flags, reload) 308 309 // The serve API routine never exits unless an error occurs 310 // We need to start it as a goroutine and wait on it so 311 // daemon doesn't exit 312 serveAPIWait := make(chan error) 313 go api.Wait(serveAPIWait) 314 315 signal.Trap(func() { 316 api.Close() 317 <-serveAPIWait 318 shutdownDaemon(d, 15) 319 if pfile != nil { 320 if err := pfile.Remove(); err != nil { 321 logrus.Error(err) 322 } 323 } 324 }) 325 326 // after the daemon is done setting up we can notify systemd api 327 notifySystem() 328 329 // Daemon is fully initialized and handling API traffic 330 // Wait for serve API to complete 331 errAPI := <-serveAPIWait 332 shutdownDaemon(d, 15) 333 if errAPI != nil { 334 if pfile != nil { 335 if err := pfile.Remove(); err != nil { 336 logrus.Error(err) 337 } 338 } 339 logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) 340 } 341 return nil 342 } 343 344 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case 345 // d.Shutdown() is waiting too long to kill container or worst it's 346 // blocked there 347 func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) { 348 ch := make(chan struct{}) 349 go func() { 350 d.Shutdown() 351 close(ch) 352 }() 353 select { 354 case <-ch: 355 logrus.Debug("Clean shutdown succeeded") 356 case <-time.After(timeout * time.Second): 357 logrus.Error("Force shutdown daemon") 358 } 359 } 360 361 func loadDaemonCliConfig(config *daemon.Config, daemonFlags *flag.FlagSet, commonConfig *cli.CommonFlags, configFile string) (*daemon.Config, error) { 362 config.Debug = commonConfig.Debug 363 config.Hosts = commonConfig.Hosts 364 config.LogLevel = commonConfig.LogLevel 365 config.TLS = commonConfig.TLS 366 config.TLSVerify = commonConfig.TLSVerify 367 config.CommonTLSOptions = daemon.CommonTLSOptions{} 368 369 if commonConfig.TLSOptions != nil { 370 config.CommonTLSOptions.CAFile = commonConfig.TLSOptions.CAFile 371 config.CommonTLSOptions.CertFile = commonConfig.TLSOptions.CertFile 372 config.CommonTLSOptions.KeyFile = commonConfig.TLSOptions.KeyFile 373 } 374 375 if configFile != "" { 376 c, err := daemon.MergeDaemonConfigurations(config, daemonFlags, configFile) 377 if err != nil { 378 if daemonFlags.IsSet(daemonConfigFileFlag) || !os.IsNotExist(err) { 379 return nil, fmt.Errorf("unable to configure the Docker daemon with file %s: %v\n", configFile, err) 380 } 381 } 382 // the merged configuration can be nil if the config file didn't exist. 383 // leave the current configuration as it is if when that happens. 384 if c != nil { 385 config = c 386 } 387 } 388 389 // Regardless of whether the user sets it to true or false, if they 390 // specify TLSVerify at all then we need to turn on TLS 391 if config.IsValueSet(tlsVerifyKey) { 392 config.TLS = true 393 } 394 395 // ensure that the log level is the one set after merging configurations 396 setDaemonLogLevel(config.LogLevel) 397 398 return config, nil 399 } 400 401 func initRouter(s *apiserver.Server, d *daemon.Daemon) { 402 routers := []router.Router{ 403 container.NewRouter(d), 404 image.NewRouter(d), 405 systemrouter.NewRouter(d), 406 volume.NewRouter(d), 407 build.NewRouter(dockerfile.NewBuildManager(d)), 408 } 409 if d.NetworkControllerEnabled() { 410 routers = append(routers, network.NewRouter(d)) 411 } 412 413 s.InitRouter(utils.IsDebugEnabled(), routers...) 414 }