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