github.com/robertojrojas/docker@v1.9.1/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 "strings" 12 "time" 13 14 "github.com/Sirupsen/logrus" 15 "github.com/docker/distribution/uuid" 16 apiserver "github.com/docker/docker/api/server" 17 "github.com/docker/docker/autogen/dockerversion" 18 "github.com/docker/docker/cli" 19 "github.com/docker/docker/cliconfig" 20 "github.com/docker/docker/daemon" 21 "github.com/docker/docker/daemon/logger" 22 "github.com/docker/docker/opts" 23 flag "github.com/docker/docker/pkg/mflag" 24 "github.com/docker/docker/pkg/pidfile" 25 "github.com/docker/docker/pkg/signal" 26 "github.com/docker/docker/pkg/system" 27 "github.com/docker/docker/pkg/timeutils" 28 "github.com/docker/docker/pkg/tlsconfig" 29 "github.com/docker/docker/registry" 30 "github.com/docker/docker/utils" 31 ) 32 33 const daemonUsage = " docker daemon [ --help | ... ]\n" 34 35 var ( 36 flDaemon = flag.Bool([]string{"#d", "#-daemon"}, false, "Enable daemon mode (deprecated; use docker daemon)") 37 daemonCli cli.Handler = NewDaemonCli() 38 ) 39 40 // TODO: remove once `-d` is retired 41 func handleGlobalDaemonFlag() { 42 // This block makes sure that if the deprecated daemon flag `--daemon` is absent, 43 // then all daemon-specific flags are absent as well. 44 if !*flDaemon && daemonFlags != nil { 45 flag.CommandLine.Visit(func(fl *flag.Flag) { 46 for _, name := range fl.Names { 47 name := strings.TrimPrefix(name, "#") 48 if daemonFlags.Lookup(name) != nil { 49 // daemon flag was NOT specified, but daemon-specific flags were 50 // so let's error out 51 fmt.Fprintf(os.Stderr, "docker: the daemon flag '-%s' must follow the 'docker daemon' command.\n", name) 52 os.Exit(1) 53 } 54 } 55 }) 56 } 57 58 if *flDaemon { 59 daemonCli.(*DaemonCli).CmdDaemon(flag.Args()...) 60 os.Exit(0) 61 } 62 } 63 64 func presentInHelp(usage string) string { return usage } 65 func absentFromHelp(string) string { return "" } 66 67 // NewDaemonCli returns a pre-configured daemon CLI 68 func NewDaemonCli() *DaemonCli { 69 daemonFlags = cli.Subcmd("daemon", nil, "Enable daemon mode", true) 70 71 // TODO(tiborvass): remove InstallFlags? 72 daemonConfig := new(daemon.Config) 73 daemonConfig.LogConfig.Config = make(map[string]string) 74 daemonConfig.ClusterOpts = make(map[string]string) 75 daemonConfig.InstallFlags(daemonFlags, presentInHelp) 76 daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp) 77 registryOptions := new(registry.Options) 78 registryOptions.InstallFlags(daemonFlags, presentInHelp) 79 registryOptions.InstallFlags(flag.CommandLine, absentFromHelp) 80 daemonFlags.Require(flag.Exact, 0) 81 82 return &DaemonCli{ 83 Config: daemonConfig, 84 registryOptions: registryOptions, 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(), defaultTrustKeyFile) 91 newPath := filepath.Join(getDaemonConfDir(), 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 // DaemonCli represents the daemon CLI. 130 type DaemonCli struct { 131 *daemon.Config 132 registryOptions *registry.Options 133 } 134 135 func getGlobalFlag() (globalFlag *flag.Flag) { 136 defer func() { 137 if x := recover(); x != nil { 138 switch f := x.(type) { 139 case *flag.Flag: 140 globalFlag = f 141 default: 142 panic(x) 143 } 144 } 145 }() 146 visitor := func(f *flag.Flag) { panic(f) } 147 commonFlags.FlagSet.Visit(visitor) 148 clientFlags.FlagSet.Visit(visitor) 149 return 150 } 151 152 // CmdDaemon is the daemon command, called the raw arguments after `docker daemon`. 153 func (cli *DaemonCli) CmdDaemon(args ...string) error { 154 // warn from uuid package when running the daemon 155 uuid.Loggerf = logrus.Warnf 156 157 if *flDaemon { 158 // allow legacy forms `docker -D -d` and `docker -d -D` 159 logrus.Warn("please use 'docker daemon' instead.") 160 } else if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() { 161 // deny `docker -D daemon` 162 illegalFlag := getGlobalFlag() 163 fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0]) 164 os.Exit(1) 165 } else { 166 // allow new form `docker daemon -D` 167 flag.Merge(daemonFlags, commonFlags.FlagSet) 168 } 169 170 daemonFlags.ParseFlags(args, true) 171 commonFlags.PostParse() 172 173 if commonFlags.TrustKey == "" { 174 commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) 175 } 176 177 if utils.ExperimentalBuild() { 178 logrus.Warn("Running experimental build") 179 } 180 181 logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed}) 182 183 if err := setDefaultUmask(); err != nil { 184 logrus.Fatalf("Failed to set umask: %v", err) 185 } 186 187 if len(cli.LogConfig.Config) > 0 { 188 if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { 189 logrus.Fatalf("Failed to set log opts: %v", err) 190 } 191 } 192 193 var pfile *pidfile.PIDFile 194 if cli.Pidfile != "" { 195 pf, err := pidfile.New(cli.Pidfile) 196 if err != nil { 197 logrus.Fatalf("Error starting daemon: %v", err) 198 } 199 pfile = pf 200 defer func() { 201 if err := pfile.Remove(); err != nil { 202 logrus.Error(err) 203 } 204 }() 205 } 206 207 serverConfig := &apiserver.Config{ 208 Logging: true, 209 Version: dockerversion.VERSION, 210 } 211 serverConfig = setPlatformServerConfig(serverConfig, cli.Config) 212 213 defaultHost := opts.DefaultHost 214 if commonFlags.TLSOptions != nil { 215 if !commonFlags.TLSOptions.InsecureSkipVerify { 216 // server requires and verifies client's certificate 217 commonFlags.TLSOptions.ClientAuth = tls.RequireAndVerifyClientCert 218 } 219 tlsConfig, err := tlsconfig.Server(*commonFlags.TLSOptions) 220 if err != nil { 221 logrus.Fatal(err) 222 } 223 serverConfig.TLSConfig = tlsConfig 224 defaultHost = opts.DefaultTLSHost 225 } 226 227 if len(commonFlags.Hosts) == 0 { 228 commonFlags.Hosts = make([]string, 1) 229 } 230 for i := 0; i < len(commonFlags.Hosts); i++ { 231 var err error 232 if commonFlags.Hosts[i], err = opts.ParseHost(defaultHost, commonFlags.Hosts[i]); err != nil { 233 logrus.Fatalf("error parsing -H %s : %v", commonFlags.Hosts[i], err) 234 } 235 } 236 for _, protoAddr := range commonFlags.Hosts { 237 protoAddrParts := strings.SplitN(protoAddr, "://", 2) 238 if len(protoAddrParts) != 2 { 239 logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr) 240 } 241 serverConfig.Addrs = append(serverConfig.Addrs, apiserver.Addr{Proto: protoAddrParts[0], Addr: protoAddrParts[1]}) 242 } 243 api, err := apiserver.New(serverConfig) 244 if err != nil { 245 logrus.Fatal(err) 246 } 247 248 // The serve API routine never exits unless an error occurs 249 // We need to start it as a goroutine and wait on it so 250 // daemon doesn't exit 251 // All servers must be protected with some mechanism (systemd socket, listenbuffer) 252 // which prevents real handling of request until routes will be set. 253 serveAPIWait := make(chan error) 254 go func() { 255 if err := api.ServeAPI(); err != nil { 256 logrus.Errorf("ServeAPI error: %v", err) 257 serveAPIWait <- err 258 return 259 } 260 serveAPIWait <- nil 261 }() 262 263 if err := migrateKey(); err != nil { 264 logrus.Fatal(err) 265 } 266 cli.TrustKeyPath = commonFlags.TrustKey 267 268 registryService := registry.NewService(cli.registryOptions) 269 d, err := daemon.NewDaemon(cli.Config, registryService) 270 if err != nil { 271 if pfile != nil { 272 if err := pfile.Remove(); err != nil { 273 logrus.Error(err) 274 } 275 } 276 logrus.Fatalf("Error starting daemon: %v", err) 277 } 278 279 logrus.Info("Daemon has completed initialization") 280 281 logrus.WithFields(logrus.Fields{ 282 "version": dockerversion.VERSION, 283 "commit": dockerversion.GITCOMMIT, 284 "execdriver": d.ExecutionDriver().Name(), 285 "graphdriver": d.GraphDriver().String(), 286 }).Info("Docker daemon") 287 288 api.InitRouters(d) 289 290 signal.Trap(func() { 291 api.Close() 292 <-serveAPIWait 293 shutdownDaemon(d, 15) 294 if pfile != nil { 295 if err := pfile.Remove(); err != nil { 296 logrus.Error(err) 297 } 298 } 299 }) 300 301 // after the daemon is done setting up we can tell the api to start 302 // accepting connections with specified daemon 303 notifySystem() 304 api.AcceptConnections() 305 306 // Daemon is fully initialized and handling API traffic 307 // Wait for serve API to complete 308 errAPI := <-serveAPIWait 309 shutdownDaemon(d, 15) 310 if errAPI != nil { 311 if pfile != nil { 312 if err := pfile.Remove(); err != nil { 313 logrus.Error(err) 314 } 315 } 316 logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) 317 } 318 return nil 319 } 320 321 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case 322 // d.Shutdown() is waiting too long to kill container or worst it's 323 // blocked there 324 func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) { 325 ch := make(chan struct{}) 326 go func() { 327 d.Shutdown() 328 close(ch) 329 }() 330 select { 331 case <-ch: 332 logrus.Debug("Clean shutdown succeeded") 333 case <-time.After(timeout * time.Second): 334 logrus.Error("Force shutdown daemon") 335 } 336 }