github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/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/cli" 18 "github.com/docker/docker/cliconfig" 19 "github.com/docker/docker/daemon" 20 "github.com/docker/docker/daemon/logger" 21 "github.com/docker/docker/dockerversion" 22 "github.com/docker/docker/opts" 23 "github.com/docker/docker/pkg/jsonlog" 24 flag "github.com/docker/docker/pkg/mflag" 25 "github.com/docker/docker/pkg/pidfile" 26 "github.com/docker/docker/pkg/signal" 27 "github.com/docker/docker/pkg/system" 28 "github.com/docker/docker/registry" 29 "github.com/docker/docker/utils" 30 "github.com/docker/go-connections/tlsconfig" 31 ) 32 33 const daemonUsage = " docker daemon [ --help | ... ]\n" 34 35 var ( 36 daemonCli cli.Handler = NewDaemonCli() 37 ) 38 39 func presentInHelp(usage string) string { return usage } 40 func absentFromHelp(string) string { return "" } 41 42 // NewDaemonCli returns a pre-configured daemon CLI 43 func NewDaemonCli() *DaemonCli { 44 daemonFlags = cli.Subcmd("daemon", nil, "Enable daemon mode", true) 45 46 // TODO(tiborvass): remove InstallFlags? 47 daemonConfig := new(daemon.Config) 48 daemonConfig.LogConfig.Config = make(map[string]string) 49 daemonConfig.ClusterOpts = make(map[string]string) 50 daemonConfig.InstallFlags(daemonFlags, presentInHelp) 51 daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp) 52 registryOptions := new(registry.Options) 53 registryOptions.InstallFlags(daemonFlags, presentInHelp) 54 registryOptions.InstallFlags(flag.CommandLine, absentFromHelp) 55 daemonFlags.Require(flag.Exact, 0) 56 57 return &DaemonCli{ 58 Config: daemonConfig, 59 registryOptions: registryOptions, 60 } 61 } 62 63 func migrateKey() (err error) { 64 // Migrate trust key if exists at ~/.docker/key.json and owned by current user 65 oldPath := filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile) 66 newPath := filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) 67 if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) { 68 defer func() { 69 // Ensure old path is removed if no error occurred 70 if err == nil { 71 err = os.Remove(oldPath) 72 } else { 73 logrus.Warnf("Key migration failed, key file not removed at %s", oldPath) 74 os.Remove(newPath) 75 } 76 }() 77 78 if err := system.MkdirAll(getDaemonConfDir(), os.FileMode(0644)); err != nil { 79 return fmt.Errorf("Unable to create daemon configuration directory: %s", err) 80 } 81 82 newFile, err := os.OpenFile(newPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) 83 if err != nil { 84 return fmt.Errorf("error creating key file %q: %s", newPath, err) 85 } 86 defer newFile.Close() 87 88 oldFile, err := os.Open(oldPath) 89 if err != nil { 90 return fmt.Errorf("error opening key file %q: %s", oldPath, err) 91 } 92 defer oldFile.Close() 93 94 if _, err := io.Copy(newFile, oldFile); err != nil { 95 return fmt.Errorf("error copying key: %s", err) 96 } 97 98 logrus.Infof("Migrated key from %s to %s", oldPath, newPath) 99 } 100 101 return nil 102 } 103 104 // DaemonCli represents the daemon CLI. 105 type DaemonCli struct { 106 *daemon.Config 107 registryOptions *registry.Options 108 } 109 110 func getGlobalFlag() (globalFlag *flag.Flag) { 111 defer func() { 112 if x := recover(); x != nil { 113 switch f := x.(type) { 114 case *flag.Flag: 115 globalFlag = f 116 default: 117 panic(x) 118 } 119 } 120 }() 121 visitor := func(f *flag.Flag) { panic(f) } 122 commonFlags.FlagSet.Visit(visitor) 123 clientFlags.FlagSet.Visit(visitor) 124 return 125 } 126 127 // CmdDaemon is the daemon command, called the raw arguments after `docker daemon`. 128 func (cli *DaemonCli) CmdDaemon(args ...string) error { 129 // warn from uuid package when running the daemon 130 uuid.Loggerf = logrus.Warnf 131 132 if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() { 133 // deny `docker -D daemon` 134 illegalFlag := getGlobalFlag() 135 fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0]) 136 os.Exit(1) 137 } else { 138 // allow new form `docker daemon -D` 139 flag.Merge(daemonFlags, commonFlags.FlagSet) 140 } 141 142 daemonFlags.ParseFlags(args, true) 143 commonFlags.PostParse() 144 145 if commonFlags.TrustKey == "" { 146 commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) 147 } 148 149 if utils.ExperimentalBuild() { 150 logrus.Warn("Running experimental build") 151 } 152 153 logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: jsonlog.RFC3339NanoFixed}) 154 155 if err := setDefaultUmask(); err != nil { 156 logrus.Fatalf("Failed to set umask: %v", err) 157 } 158 159 if len(cli.LogConfig.Config) > 0 { 160 if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { 161 logrus.Fatalf("Failed to set log opts: %v", err) 162 } 163 } 164 165 var pfile *pidfile.PIDFile 166 if cli.Pidfile != "" { 167 pf, err := pidfile.New(cli.Pidfile) 168 if err != nil { 169 logrus.Fatalf("Error starting daemon: %v", err) 170 } 171 pfile = pf 172 defer func() { 173 if err := pfile.Remove(); err != nil { 174 logrus.Error(err) 175 } 176 }() 177 } 178 179 serverConfig := &apiserver.Config{ 180 AuthZPluginNames: cli.Config.AuthZPlugins, 181 Logging: true, 182 Version: dockerversion.Version, 183 } 184 serverConfig = setPlatformServerConfig(serverConfig, cli.Config) 185 186 defaultHost := opts.DefaultHost 187 if commonFlags.TLSOptions != nil { 188 if !commonFlags.TLSOptions.InsecureSkipVerify { 189 // server requires and verifies client's certificate 190 commonFlags.TLSOptions.ClientAuth = tls.RequireAndVerifyClientCert 191 } 192 tlsConfig, err := tlsconfig.Server(*commonFlags.TLSOptions) 193 if err != nil { 194 logrus.Fatal(err) 195 } 196 serverConfig.TLSConfig = tlsConfig 197 defaultHost = opts.DefaultTLSHost 198 } 199 200 if len(commonFlags.Hosts) == 0 { 201 commonFlags.Hosts = make([]string, 1) 202 } 203 for i := 0; i < len(commonFlags.Hosts); i++ { 204 var err error 205 if commonFlags.Hosts[i], err = opts.ParseHost(defaultHost, commonFlags.Hosts[i]); err != nil { 206 logrus.Fatalf("error parsing -H %s : %v", commonFlags.Hosts[i], err) 207 } 208 } 209 for _, protoAddr := range commonFlags.Hosts { 210 protoAddrParts := strings.SplitN(protoAddr, "://", 2) 211 if len(protoAddrParts) != 2 { 212 logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr) 213 } 214 serverConfig.Addrs = append(serverConfig.Addrs, apiserver.Addr{Proto: protoAddrParts[0], Addr: protoAddrParts[1]}) 215 } 216 api, err := apiserver.New(serverConfig) 217 if err != nil { 218 logrus.Fatal(err) 219 } 220 221 if err := migrateKey(); err != nil { 222 logrus.Fatal(err) 223 } 224 cli.TrustKeyPath = commonFlags.TrustKey 225 226 registryService := registry.NewService(cli.registryOptions) 227 d, err := daemon.NewDaemon(cli.Config, registryService) 228 if err != nil { 229 if pfile != nil { 230 if err := pfile.Remove(); err != nil { 231 logrus.Error(err) 232 } 233 } 234 logrus.Fatalf("Error starting daemon: %v", err) 235 } 236 237 logrus.Info("Daemon has completed initialization") 238 239 logrus.WithFields(logrus.Fields{ 240 "version": dockerversion.Version, 241 "commit": dockerversion.GitCommit, 242 "execdriver": d.ExecutionDriver().Name(), 243 "graphdriver": d.GraphDriverName(), 244 }).Info("Docker daemon") 245 246 api.InitRouters(d) 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 serveAPIWait := make(chan error) 252 go func() { 253 if err := api.ServeAPI(); err != nil { 254 logrus.Errorf("ServeAPI error: %v", err) 255 serveAPIWait <- err 256 return 257 } 258 serveAPIWait <- nil 259 }() 260 261 signal.Trap(func() { 262 api.Close() 263 <-serveAPIWait 264 shutdownDaemon(d, 15) 265 if pfile != nil { 266 if err := pfile.Remove(); err != nil { 267 logrus.Error(err) 268 } 269 } 270 }) 271 272 // after the daemon is done setting up we can notify systemd api 273 notifySystem() 274 275 // Daemon is fully initialized and handling API traffic 276 // Wait for serve API to complete 277 errAPI := <-serveAPIWait 278 shutdownDaemon(d, 15) 279 if errAPI != nil { 280 if pfile != nil { 281 if err := pfile.Remove(); err != nil { 282 logrus.Error(err) 283 } 284 } 285 logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) 286 } 287 return nil 288 } 289 290 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case 291 // d.Shutdown() is waiting too long to kill container or worst it's 292 // blocked there 293 func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) { 294 ch := make(chan struct{}) 295 go func() { 296 d.Shutdown() 297 close(ch) 298 }() 299 select { 300 case <-ch: 301 logrus.Debug("Clean shutdown succeeded") 302 case <-time.After(timeout * time.Second): 303 logrus.Error("Force shutdown daemon") 304 } 305 }