func showVersion() { if utils.ExperimentalBuild() { fmt.Printf("Docker version %s, build %s, experimental\n", dockerversion.VERSION, dockerversion.GITCOMMIT) } else { fmt.Printf("Docker version %s, build %s\n", dockerversion.VERSION, dockerversion.GITCOMMIT) } }
// ensure docker info succeeds func (s *DockerSuite) TestInfoEnsureSucceeds(c *check.C) { versionCmd := exec.Command(dockerBinary, "info") out, exitCode, err := runCommandWithOutput(versionCmd) if err != nil || exitCode != 0 { c.Fatalf("failed to execute docker info: %s, %v", out, err) } // always shown fields stringsToCheck := []string{ "ID:", "Containers:", "Images:", "Execution Driver:", "Logging Driver:", "Operating System:", "CPUs:", "Total Memory:", "Kernel Version:", "Storage Driver:", } if utils.ExperimentalBuild() { stringsToCheck = append(stringsToCheck, "Experimental: true") } for _, linePrefix := range stringsToCheck { if !strings.Contains(out, linePrefix) { c.Errorf("couldn't find string %v in output", linePrefix) } } }
// ensure docker info succeeds func (s *DockerSuite) TestInfoEnsureSucceeds(c *check.C) { out, _ := dockerCmd(c, "info") // always shown fields stringsToCheck := []string{ "ID:", "Containers:", "Images:", "Execution Driver:", "Logging Driver:", "Operating System:", "CPUs:", "Total Memory:", "Kernel Version:", "Storage Driver:", } if utils.ExperimentalBuild() { stringsToCheck = append(stringsToCheck, "Experimental: true") } for _, linePrefix := range stringsToCheck { if !strings.Contains(out, linePrefix) { c.Errorf("couldn't find string %v in output", linePrefix) } } }
// SystemVersion returns information of the docker client and server host. func (cli *Client) SystemVersion() (types.VersionResponse, error) { client := &types.Version{ Version: dockerversion.Version, APIVersion: api.Version, GoVersion: runtime.Version(), GitCommit: dockerversion.GitCommit, BuildTime: dockerversion.BuildTime, Os: runtime.GOOS, Arch: runtime.GOARCH, Experimental: utils.ExperimentalBuild(), } resp, err := cli.get("/version", nil, nil) if err != nil { return types.VersionResponse{Client: client}, err } defer ensureReaderClosed(resp) var server types.Version err = json.NewDecoder(resp.body).Decode(&server) if err != nil { return types.VersionResponse{Client: client}, err } return types.VersionResponse{Client: client, Server: &server}, nil }
// ensure docker info succeeds func (s *DockerSuite) TestInfoEnsureSucceeds(c *check.C) { out, _ := dockerCmd(c, "info") // always shown fields stringsToCheck := []string{ "ID:", "Containers:", " Running:", " Paused:", " Stopped:", "Images:", "Execution Driver:", "OSType:", "Architecture:", "Logging Driver:", "Operating System:", "CPUs:", "Total Memory:", "Kernel Version:", "Storage Driver:", "Volume:", "Network:", } if utils.ExperimentalBuild() { stringsToCheck = append(stringsToCheck, "Experimental: true") } for _, linePrefix := range stringsToCheck { c.Assert(out, checker.Contains, linePrefix, check.Commentf("couldn't find string %v in output", linePrefix)) } }
func showVersion() { if utils.ExperimentalBuild() { fmt.Printf("Docker version %s, build %s, experimental\n", dockerversion.Version, dockerversion.GitCommit) } else { fmt.Printf("Docker version %s, build %s\n", dockerversion.Version, dockerversion.GitCommit) } }
// CmdVersion shows Docker version information. // // Available version information is shown for: client Docker version, client API version, client Go version, client Git commit, client OS/Arch, server Docker version, server API version, server Go version, server Git commit, and server OS/Arch. // // Usage: docker version func (cli *DockerCli) CmdVersion(args ...string) (err error) { cmd := Cli.Subcmd("version", nil, Cli.DockerCommands["version"].Description, true) tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template") cmd.Require(flag.Exact, 0) cmd.ParseFlags(args, true) templateFormat := versionTemplate if *tmplStr != "" { templateFormat = *tmplStr } var tmpl *template.Template if tmpl, err = template.New("").Funcs(funcMap).Parse(templateFormat); err != nil { return Cli.StatusError{StatusCode: 64, Status: "Template parsing error: " + err.Error()} } vd := types.VersionResponse{ Client: &types.Version{ Version: dockerversion.Version, APIVersion: cli.client.ClientVersion(), GoVersion: runtime.Version(), GitCommit: dockerversion.GitCommit, BuildTime: dockerversion.BuildTime, Os: runtime.GOOS, Arch: runtime.GOARCH, Experimental: utils.ExperimentalBuild(), }, } serverVersion, err := cli.client.ServerVersion() if err == nil { vd.Server = &serverVersion } // first we need to make BuildTime more human friendly t, errTime := time.Parse(time.RFC3339Nano, vd.Client.BuildTime) if errTime == nil { vd.Client.BuildTime = t.Format(time.ANSIC) } if vd.ServerOK() { t, errTime = time.Parse(time.RFC3339Nano, vd.Server.BuildTime) if errTime == nil { vd.Server.BuildTime = t.Format(time.ANSIC) } } if err2 := tmpl.Execute(cli.out, vd); err2 != nil && err == nil { err = err2 } cli.out.Write([]byte{'\n'}) return err }
// CmdVersion shows Docker version information. // // Available version information is shown for: client Docker version, client API version, client Go version, client Git commit, client OS/Arch, server Docker version, server API version, server Go version, server Git commit, and server OS/Arch. // // Usage: docker version func (cli *DockerCli) CmdVersion(args ...string) error { cmd := cli.Subcmd("version", nil, "Show the Docker version information.", true) cmd.Require(flag.Exact, 0) cmd.ParseFlags(args, true) fmt.Println("Client:") if dockerversion.VERSION != "" { fmt.Fprintf(cli.out, " Version: %s\n", dockerversion.VERSION) } fmt.Fprintf(cli.out, " API version: %s\n", api.Version) fmt.Fprintf(cli.out, " Go version: %s\n", runtime.Version()) if dockerversion.GITCOMMIT != "" { fmt.Fprintf(cli.out, " Git commit: %s\n", dockerversion.GITCOMMIT) } if dockerversion.BUILDTIME != "" { fmt.Fprintf(cli.out, " Built: %s\n", dockerversion.BUILDTIME) } fmt.Fprintf(cli.out, " OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH) if utils.ExperimentalBuild() { fmt.Fprintf(cli.out, " Experimental: true\n") } stream, _, _, err := cli.call("GET", "/version", nil, nil) if err != nil { return err } defer stream.Close() var v types.Version if err := json.NewDecoder(stream).Decode(&v); err != nil { fmt.Fprintf(cli.err, "Error reading remote version: %s\n", err) return err } fmt.Println("\nServer:") fmt.Fprintf(cli.out, " Version: %s\n", v.Version) if v.ApiVersion != "" { fmt.Fprintf(cli.out, " API version: %s\n", v.ApiVersion) } fmt.Fprintf(cli.out, " Go version: %s\n", v.GoVersion) fmt.Fprintf(cli.out, " Git commit: %s\n", v.GitCommit) if len(v.BuildTime) > 0 { fmt.Fprintf(cli.out, " Built: %s\n", v.BuildTime) } fmt.Fprintf(cli.out, " OS/Arch: %s/%s\n", v.Os, v.Arch) if v.Experimental { fmt.Fprintf(cli.out, " Experimental: true\n") } fmt.Fprintf(cli.out, "\n") return nil }
// CmdVersion shows Docker version information. // // Available version information is shown for: client Docker version, client API version, client Go version, client Git commit, client OS/Arch, server Docker version, server API version, server Go version, server Git commit, and server OS/Arch. // // Usage: docker version func (cli *DockerCli) CmdVersion(args ...string) (err error) { cmd := Cli.Subcmd("version", nil, Cli.DockerCommands["version"].Description, true) tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template") cmd.Require(flag.Exact, 0) cmd.ParseFlags(args, true) if *tmplStr == "" { *tmplStr = versionTemplate } var tmpl *template.Template if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil { return Cli.StatusError{StatusCode: 64, Status: "Template parsing error: " + err.Error()} } vd := versionData{ Client: types.Version{ Version: dockerversion.VERSION, APIVersion: api.Version, GoVersion: runtime.Version(), GitCommit: dockerversion.GITCOMMIT, BuildTime: dockerversion.BUILDTIME, Os: runtime.GOOS, Arch: runtime.GOARCH, Experimental: utils.ExperimentalBuild(), }, } defer func() { if err2 := tmpl.Execute(cli.out, vd); err2 != nil && err == nil { err = err2 } cli.out.Write([]byte{'\n'}) }() serverResp, err := cli.call("GET", "/version", nil, nil) if err != nil { return err } defer serverResp.body.Close() if err = json.NewDecoder(serverResp.body).Decode(&vd.Server); err != nil { return Cli.StatusError{StatusCode: 1, Status: "Error reading remote version: " + err.Error()} } vd.ServerOK = true return }
func runVersion(dockerCli *client.DockerCli, opts *versionOptions) error { ctx := context.Background() templateFormat := versionTemplate if opts.format != "" { templateFormat = opts.format } tmpl, err := templates.Parse(templateFormat) if err != nil { return cli.StatusError{StatusCode: 64, Status: "Template parsing error: " + err.Error()} } vd := types.VersionResponse{ Client: &types.Version{ Version: dockerversion.Version, APIVersion: dockerCli.Client().ClientVersion(), GoVersion: runtime.Version(), GitCommit: dockerversion.GitCommit, BuildTime: dockerversion.BuildTime, Os: runtime.GOOS, Arch: runtime.GOARCH, Experimental: utils.ExperimentalBuild(), }, } serverVersion, err := dockerCli.Client().ServerVersion(ctx) if err == nil { vd.Server = &serverVersion } // first we need to make BuildTime more human friendly t, errTime := time.Parse(time.RFC3339Nano, vd.Client.BuildTime) if errTime == nil { vd.Client.BuildTime = t.Format(time.ANSIC) } if vd.ServerOK() { t, errTime = time.Parse(time.RFC3339Nano, vd.Server.BuildTime) if errTime == nil { vd.Server.BuildTime = t.Format(time.ANSIC) } } if err2 := tmpl.Execute(dockerCli.Out(), vd); err2 != nil && err == nil { err = err2 } dockerCli.Out().Write([]byte{'\n'}) return err }
// SystemVersion returns version information about the daemon. func (daemon *Daemon) SystemVersion() types.Version { v := types.Version{ Version: dockerversion.Version, GitCommit: dockerversion.GitCommit, GoVersion: runtime.Version(), Os: runtime.GOOS, Arch: runtime.GOARCH, BuildTime: dockerversion.BuildTime, Experimental: utils.ExperimentalBuild(), } if kernelVersion, err := kernel.GetKernelVersion(); err == nil { v.KernelVersion = kernelVersion.String() } return v }
// CmdVersion shows Docker version information. // // Available version information is shown for: client Docker version, client API version, client Go version, client Git commit, client OS/Arch, server Docker version, server API version, server Go version, server Git commit, and server OS/Arch. // // Usage: docker version func (cli *DockerCli) CmdVersion(args ...string) error { cmd := cli.Subcmd("version", nil, "Show the Docker version information.", true) cmd.Require(flag.Exact, 0) cmd.ParseFlags(args, true) if dockerversion.VERSION != "" { fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION) } fmt.Fprintf(cli.out, "Client API version: %s\n", api.Version) fmt.Fprintf(cli.out, "Go version (client): %s\n", runtime.Version()) if dockerversion.GITCOMMIT != "" { fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT) } fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH) if utils.ExperimentalBuild() { fmt.Fprintf(cli.out, "Experimental (client): true\n") } stream, _, _, err := cli.call("GET", "/version", nil, nil) if err != nil { return err } var v types.Version if err := json.NewDecoder(stream).Decode(&v); err != nil { fmt.Fprintf(cli.err, "Error reading remote version: %s\n", err) return err } fmt.Fprintf(cli.out, "Server version: %s\n", v.Version) if v.ApiVersion != "" { fmt.Fprintf(cli.out, "Server API version: %s\n", v.ApiVersion) } fmt.Fprintf(cli.out, "Go version (server): %s\n", v.GoVersion) fmt.Fprintf(cli.out, "Git commit (server): %s\n", v.GitCommit) fmt.Fprintf(cli.out, "OS/Arch (server): %s/%s\n", v.Os, v.Arch) if v.Experimental { fmt.Fprintf(cli.out, "Experimental (server): true\n") } return nil }
func (s *Server) getVersion(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { v := &types.Version{ Version: dockerversion.VERSION, ApiVersion: api.Version, GitCommit: dockerversion.GITCOMMIT, GoVersion: runtime.Version(), Os: runtime.GOOS, Arch: runtime.GOARCH, } if version.GreaterThanOrEqualTo("1.19") { v.Experimental = utils.ExperimentalBuild() } if kernelVersion, err := kernel.GetKernelVersion(); err == nil { v.KernelVersion = kernelVersion.String() } return writeJSON(w, http.StatusOK, v) }
// SystemVersion returns version information about the daemon. func (daemon *Daemon) SystemVersion() types.Version { v := types.Version{ Version: dockerversion.Version, GitCommit: dockerversion.GitCommit, GoVersion: runtime.Version(), Os: runtime.GOOS, Arch: runtime.GOARCH, BuildTime: dockerversion.BuildTime, Experimental: utils.ExperimentalBuild(), } kernelVersion := "<unknown>" if kv, err := kernel.GetKernelVersion(); err != nil { logrus.Warnf("Could not get kernel version: %v", err) } else { kernelVersion = kv.String() } v.KernelVersion = kernelVersion return v }
func (s *router) getVersion(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { v := &types.Version{ Version: dockerversion.VERSION, APIVersion: api.Version, GitCommit: dockerversion.GITCOMMIT, GoVersion: runtime.Version(), Os: runtime.GOOS, Arch: runtime.GOARCH, BuildTime: dockerversion.BUILDTIME, } version := httputils.VersionFromContext(ctx) if version.GreaterThanOrEqualTo("1.19") { v.Experimental = utils.ExperimentalBuild() } if kernelVersion, err := kernel.GetKernelVersion(); err == nil { v.KernelVersion = kernelVersion.String() } return httputils.WriteJSON(w, http.StatusOK, v) }
func (s *DockerSuite) TestHelpTextVerify(c *check.C) { testRequires(c, DaemonIsLinux) // Make sure main help text fits within 80 chars and that // on non-windows system we use ~ when possible (to shorten things). // Test for HOME set to its default value and set to "/" on linux // Yes on windows setting up an array and looping (right now) isn't // necessary because we just have one value, but we'll need the // array/loop on linux so we might as well set it up so that we can // test any number of home dirs later on and all we need to do is // modify the array - the rest of the testing infrastructure should work homes := []string{homedir.Get()} // Non-Windows machines need to test for this special case of $HOME if runtime.GOOS != "windows" { homes = append(homes, "/") } homeKey := homedir.Key() baseEnvs := appendBaseEnv(true) // Remove HOME env var from list so we can add a new value later. for i, env := range baseEnvs { if strings.HasPrefix(env, homeKey+"=") { baseEnvs = append(baseEnvs[:i], baseEnvs[i+1:]...) break } } for _, home := range homes { // Dup baseEnvs and add our new HOME value newEnvs := make([]string, len(baseEnvs)+1) copy(newEnvs, baseEnvs) newEnvs[len(newEnvs)-1] = homeKey + "=" + home scanForHome := runtime.GOOS != "windows" && home != "/" // Check main help text to make sure its not over 80 chars helpCmd := exec.Command(dockerBinary, "help") helpCmd.Env = newEnvs out, _, err := runCommandWithOutput(helpCmd) c.Assert(err, checker.IsNil, check.Commentf(out)) lines := strings.Split(out, "\n") for _, line := range lines { // All lines should not end with a space c.Assert(line, checker.Not(checker.HasSuffix), " ", check.Commentf("Line should not end with a space")) if scanForHome && strings.Contains(line, `=`+home) { c.Fatalf("Line should use '%q' instead of %q:\n%s", homedir.GetShortcutString(), home, line) } if runtime.GOOS != "windows" { i := strings.Index(line, homedir.GetShortcutString()) if i >= 0 && i != len(line)-1 && line[i+1] != '/' { c.Fatalf("Main help should not have used home shortcut:\n%s", line) } } } // Make sure each cmd's help text fits within 90 chars and that // on non-windows system we use ~ when possible (to shorten things). // Pull the list of commands from the "Commands:" section of docker help helpCmd = exec.Command(dockerBinary, "help") helpCmd.Env = newEnvs out, _, err = runCommandWithOutput(helpCmd) c.Assert(err, checker.IsNil, check.Commentf(out)) i := strings.Index(out, "Commands:") c.Assert(i, checker.GreaterOrEqualThan, 0, check.Commentf("Missing 'Commands:' in:\n%s", out)) cmds := []string{} // Grab all chars starting at "Commands:" helpOut := strings.Split(out[i:], "\n") // Skip first line, it is just "Commands:" helpOut = helpOut[1:] // Create the list of commands we want to test cmdsToTest := []string{} for _, cmd := range helpOut { // Stop on blank line or non-idented line if cmd == "" || !unicode.IsSpace(rune(cmd[0])) { break } // Grab just the first word of each line cmd = strings.Split(strings.TrimSpace(cmd), " ")[0] cmds = append(cmds, cmd) // Saving count for later cmdsToTest = append(cmdsToTest, cmd) } // Add some 'two word' commands - would be nice to automatically // calculate this list - somehow cmdsToTest = append(cmdsToTest, "volume create") cmdsToTest = append(cmdsToTest, "volume inspect") cmdsToTest = append(cmdsToTest, "volume ls") cmdsToTest = append(cmdsToTest, "volume rm") cmdsToTest = append(cmdsToTest, "network connect") cmdsToTest = append(cmdsToTest, "network create") cmdsToTest = append(cmdsToTest, "network disconnect") cmdsToTest = append(cmdsToTest, "network inspect") cmdsToTest = append(cmdsToTest, "network ls") cmdsToTest = append(cmdsToTest, "network rm") if utils.ExperimentalBuild() { cmdsToTest = append(cmdsToTest, "checkpoint create") cmdsToTest = append(cmdsToTest, "checkpoint ls") cmdsToTest = append(cmdsToTest, "checkpoint rm") } // Divide the list of commands into go routines and run the func testcommand on the commands in parallel // to save runtime of test errChan := make(chan error) for index := 0; index < len(cmdsToTest); index++ { go func(index int) { errChan <- testCommand(cmdsToTest[index], newEnvs, scanForHome, home) }(index) } for index := 0; index < len(cmdsToTest); index++ { err := <-errChan if err != nil { c.Fatal(err) } } } }
func mainDaemon() { if utils.ExperimentalBuild() { logrus.Warn("Running experimental build") } if flag.NArg() != 0 { flag.Usage() return } logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed}) if err := setDefaultUmask(); err != nil { logrus.Fatalf("Failed to set umask: %v", err) } var pfile *pidfile.PidFile if daemonCfg.Pidfile != "" { pf, err := pidfile.New(daemonCfg.Pidfile) if err != nil { logrus.Fatalf("Error starting daemon: %v", err) } pfile = pf defer func() { if err := pfile.Remove(); err != nil { logrus.Error(err) } }() } serverConfig := &apiserver.ServerConfig{ Logging: true, EnableCors: daemonCfg.EnableCors, CorsHeaders: daemonCfg.CorsHeaders, Version: dockerversion.VERSION, } serverConfig = setPlatformServerConfig(serverConfig, daemonCfg) if *flTls { if *flTlsVerify { tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert } tlsConfig, err := tlsconfig.Server(tlsOptions) if err != nil { logrus.Fatal(err) } serverConfig.TLSConfig = tlsConfig } api := apiserver.New(serverConfig) // The serve API routine never exits unless an error occurs // We need to start it as a goroutine and wait on it so // daemon doesn't exit serveAPIWait := make(chan error) go func() { if err := api.ServeApi(flHosts); err != nil { logrus.Errorf("ServeAPI error: %v", err) serveAPIWait <- err return } serveAPIWait <- nil }() if err := migrateKey(); err != nil { logrus.Fatal(err) } daemonCfg.TrustKeyPath = *flTrustKey registryService := registry.NewService(registryCfg) d, err := daemon.NewDaemon(daemonCfg, registryService) if err != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Error starting daemon: %v", err) } logrus.Info("Daemon has completed initialization") logrus.WithFields(logrus.Fields{ "version": dockerversion.VERSION, "commit": dockerversion.GITCOMMIT, "execdriver": d.ExecutionDriver().Name(), "graphdriver": d.GraphDriver().String(), }).Info("Docker daemon") signal.Trap(func() { api.Close() <-serveAPIWait shutdownDaemon(d, 15) if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } }) // after the daemon is done setting up we can tell the api to start // accepting connections with specified daemon api.AcceptConnections(d) // Daemon is fully initialized and handling API traffic // Wait for serve API to complete errAPI := <-serveAPIWait shutdownDaemon(d, 15) if errAPI != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) } }
// SystemInfo returns information about the host server the daemon is running on. func (daemon *Daemon) SystemInfo() (*types.Info, error) { kernelVersion := "<unknown>" if kv, err := kernel.GetKernelVersion(); err == nil { kernelVersion = kv.String() } operatingSystem := "<unknown>" if s, err := operatingsystem.GetOperatingSystem(); err == nil { operatingSystem = s } // Don't do containerized check on Windows if runtime.GOOS != "windows" { if inContainer, err := operatingsystem.IsContainerized(); err != nil { logrus.Errorf("Could not determine if daemon is containerized: %v", err) operatingSystem += " (error determining if containerized)" } else if inContainer { operatingSystem += " (containerized)" } } meminfo, err := system.ReadMemInfo() if err != nil { logrus.Errorf("Could not read system memory info: %v", err) } // if we still have the original dockerinit binary from before // we copied it locally, let's return the path to that, since // that's more intuitive (the copied path is trivial to derive // by hand given VERSION) initPath := utils.DockerInitPath("") if initPath == "" { // if that fails, we'll just return the path from the daemon initPath = daemon.systemInitPath() } sysInfo := sysinfo.New(true) v := &types.Info{ ID: daemon.ID, Containers: len(daemon.List()), Images: len(daemon.imageStore.Map()), Driver: daemon.GraphDriver().String(), DriverStatus: daemon.GraphDriver().Status(), Plugins: daemon.showPluginsInfo(), IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled, BridgeNfIptables: !sysInfo.BridgeNfCallIptablesDisabled, BridgeNfIP6tables: !sysInfo.BridgeNfCallIP6tablesDisabled, Debug: os.Getenv("DEBUG") != "", NFd: fileutils.GetTotalUsedFds(), NGoroutines: runtime.NumGoroutine(), SystemTime: time.Now().Format(time.RFC3339Nano), ExecutionDriver: daemon.ExecutionDriver().Name(), LoggingDriver: daemon.defaultLogConfig.Type, NEventsListener: daemon.EventsService.SubscribersCount(), KernelVersion: kernelVersion, OperatingSystem: operatingSystem, IndexServerAddress: registry.IndexServer, OSType: platform.OSType, Architecture: platform.Architecture, RegistryConfig: daemon.RegistryService.Config, InitSha1: dockerversion.InitSHA1, InitPath: initPath, NCPU: runtime.NumCPU(), MemTotal: meminfo.MemTotal, DockerRootDir: daemon.config().Root, Labels: daemon.config().Labels, ExperimentalBuild: utils.ExperimentalBuild(), ServerVersion: dockerversion.Version, ClusterStore: daemon.config().ClusterStore, ClusterAdvertise: daemon.config().ClusterAdvertise, HTTPProxy: getProxyEnv("http_proxy"), HTTPSProxy: getProxyEnv("https_proxy"), NoProxy: getProxyEnv("no_proxy"), } // TODO Windows. Refactor this more once sysinfo is refactored into // platform specific code. On Windows, sysinfo.cgroupMemInfo and // sysinfo.cgroupCpuInfo will be nil otherwise and cause a SIGSEGV if // an attempt is made to access through them. if runtime.GOOS != "windows" { v.MemoryLimit = sysInfo.MemoryLimit v.SwapLimit = sysInfo.SwapLimit v.OomKillDisable = sysInfo.OomKillDisable v.CPUCfsPeriod = sysInfo.CPUCfsPeriod v.CPUCfsQuota = sysInfo.CPUCfsQuota v.CPUShares = sysInfo.CPUShares v.CPUSet = sysInfo.Cpuset } if hostname, err := os.Hostname(); err == nil { v.Name = hostname } return v, nil }
// SystemInfo returns information about the host server the daemon is running on. func (daemon *Daemon) SystemInfo() (*types.Info, error) { kernelVersion := "<unknown>" if kv, err := kernel.GetKernelVersion(); err != nil { logrus.Warnf("Could not get kernel version: %v", err) } else { kernelVersion = kv.String() } operatingSystem := "<unknown>" if s, err := operatingsystem.GetOperatingSystem(); err != nil { logrus.Warnf("Could not get operating system name: %v", err) } else { operatingSystem = s } // Don't do containerized check on Windows if runtime.GOOS != "windows" { if inContainer, err := operatingsystem.IsContainerized(); err != nil { logrus.Errorf("Could not determine if daemon is containerized: %v", err) operatingSystem += " (error determining if containerized)" } else if inContainer { operatingSystem += " (containerized)" } } meminfo, err := system.ReadMemInfo() if err != nil { logrus.Errorf("Could not read system memory info: %v", err) } sysInfo := sysinfo.New(true) var cRunning, cPaused, cStopped int32 daemon.containers.ApplyAll(func(c *container.Container) { switch c.StateString() { case "paused": atomic.AddInt32(&cPaused, 1) case "running": atomic.AddInt32(&cRunning, 1) default: atomic.AddInt32(&cStopped, 1) } }) var securityOptions []string if sysInfo.AppArmor { securityOptions = append(securityOptions, "apparmor") } if sysInfo.Seccomp { securityOptions = append(securityOptions, "seccomp") } if selinuxEnabled() { securityOptions = append(securityOptions, "selinux") } v := &types.Info{ ID: daemon.ID, Containers: int(cRunning + cPaused + cStopped), ContainersRunning: int(cRunning), ContainersPaused: int(cPaused), ContainersStopped: int(cStopped), Images: len(daemon.imageStore.Map()), Driver: daemon.GraphDriverName(), DriverStatus: daemon.layerStore.DriverStatus(), Plugins: daemon.showPluginsInfo(), IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled, BridgeNfIptables: !sysInfo.BridgeNFCallIPTablesDisabled, BridgeNfIP6tables: !sysInfo.BridgeNFCallIP6TablesDisabled, Debug: utils.IsDebugEnabled(), NFd: fileutils.GetTotalUsedFds(), NGoroutines: runtime.NumGoroutine(), SystemTime: time.Now().Format(time.RFC3339Nano), LoggingDriver: daemon.defaultLogConfig.Type, CgroupDriver: daemon.getCgroupDriver(), NEventsListener: daemon.EventsService.SubscribersCount(), KernelVersion: kernelVersion, OperatingSystem: operatingSystem, IndexServerAddress: registry.IndexServer, OSType: platform.OSType, Architecture: platform.Architecture, RegistryConfig: daemon.RegistryService.ServiceConfig(), NCPU: sysinfo.NumCPU(), MemTotal: meminfo.MemTotal, DockerRootDir: daemon.configStore.Root, Labels: daemon.configStore.Labels, ExperimentalBuild: utils.ExperimentalBuild(), ServerVersion: dockerversion.Version, ClusterStore: daemon.configStore.ClusterStore, ClusterAdvertise: daemon.configStore.ClusterAdvertise, HTTPProxy: sockets.GetProxyEnv("http_proxy"), HTTPSProxy: sockets.GetProxyEnv("https_proxy"), NoProxy: sockets.GetProxyEnv("no_proxy"), SecurityOptions: securityOptions, } // TODO Windows. Refactor this more once sysinfo is refactored into // platform specific code. On Windows, sysinfo.cgroupMemInfo and // sysinfo.cgroupCpuInfo will be nil otherwise and cause a SIGSEGV if // an attempt is made to access through them. if runtime.GOOS != "windows" { v.MemoryLimit = sysInfo.MemoryLimit v.SwapLimit = sysInfo.SwapLimit v.KernelMemory = sysInfo.KernelMemory v.OomKillDisable = sysInfo.OomKillDisable v.CPUCfsPeriod = sysInfo.CPUCfsPeriod v.CPUCfsQuota = sysInfo.CPUCfsQuota v.CPUShares = sysInfo.CPUShares v.CPUSet = sysInfo.Cpuset v.Runtimes = daemon.configStore.GetAllRuntimes() v.DefaultRuntime = daemon.configStore.GetDefaultRuntimeName() } hostname := "" if hn, err := os.Hostname(); err != nil { logrus.Warnf("Could not get hostname: %v", err) } else { hostname = hn } v.Name = hostname return v, nil }
func (cli *DaemonCli) start() { // warn from uuid package when running the daemon uuid.Loggerf = logrus.Warnf flags := flag.CommandLine cli.commonFlags.PostParse() if cli.commonFlags.TrustKey == "" { cli.commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), cliflags.DefaultTrustKeyFile) } cliConfig, err := loadDaemonCliConfig(cli.Config, flags, cli.commonFlags, *cli.configFile) if err != nil { fmt.Fprint(os.Stderr, err) os.Exit(1) } cli.Config = cliConfig if cli.Config.Debug { utils.EnableDebug() } if utils.ExperimentalBuild() { logrus.Warn("Running experimental build") } logrus.SetFormatter(&logrus.TextFormatter{ TimestampFormat: jsonlog.RFC3339NanoFixed, DisableColors: cli.Config.RawLogs, }) if err := setDefaultUmask(); err != nil { logrus.Fatalf("Failed to set umask: %v", err) } if len(cli.LogConfig.Config) > 0 { if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { logrus.Fatalf("Failed to set log opts: %v", err) } } var pfile *pidfile.PIDFile if cli.Pidfile != "" { pf, err := pidfile.New(cli.Pidfile) if err != nil { logrus.Fatalf("Error starting daemon: %v", err) } pfile = pf defer func() { if err := pfile.Remove(); err != nil { logrus.Error(err) } }() } serverConfig := &apiserver.Config{ Logging: true, SocketGroup: cli.Config.SocketGroup, Version: dockerversion.Version, } serverConfig = setPlatformServerConfig(serverConfig, cli.Config) if cli.Config.TLS { tlsOptions := tlsconfig.Options{ CAFile: cli.Config.CommonTLSOptions.CAFile, CertFile: cli.Config.CommonTLSOptions.CertFile, KeyFile: cli.Config.CommonTLSOptions.KeyFile, } if cli.Config.TLSVerify { // server requires and verifies client's certificate tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert } tlsConfig, err := tlsconfig.Server(tlsOptions) if err != nil { logrus.Fatal(err) } serverConfig.TLSConfig = tlsConfig } if len(cli.Config.Hosts) == 0 { cli.Config.Hosts = make([]string, 1) } api := apiserver.New(serverConfig) for i := 0; i < len(cli.Config.Hosts); i++ { var err error if cli.Config.Hosts[i], err = opts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil { logrus.Fatalf("error parsing -H %s : %v", cli.Config.Hosts[i], err) } protoAddr := cli.Config.Hosts[i] protoAddrParts := strings.SplitN(protoAddr, "://", 2) if len(protoAddrParts) != 2 { logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr) } proto := protoAddrParts[0] addr := protoAddrParts[1] // It's a bad idea to bind to TCP without tlsverify. if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) { logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]") } l, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig) if err != nil { logrus.Fatal(err) } // If we're binding to a TCP port, make sure that a container doesn't try to use it. if proto == "tcp" { if err := allocateDaemonPort(addr); err != nil { logrus.Fatal(err) } } logrus.Debugf("Listener created for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) api.Accept(protoAddrParts[1], l...) } if err := migrateKey(); err != nil { logrus.Fatal(err) } cli.TrustKeyPath = cli.commonFlags.TrustKey registryService := registry.NewService(cli.Config.ServiceOptions) containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...) if err != nil { logrus.Fatal(err) } d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote) if err != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Error starting daemon: %v", err) } logrus.Info("Daemon has completed initialization") logrus.WithFields(logrus.Fields{ "version": dockerversion.Version, "commit": dockerversion.GitCommit, "graphdriver": d.GraphDriverName(), }).Info("Docker daemon") cli.initMiddlewares(api, serverConfig) initRouter(api, d) reload := func(config *daemon.Config) { if err := d.Reload(config); err != nil { logrus.Errorf("Error reconfiguring the daemon: %v", err) return } if config.IsValueSet("debug") { debugEnabled := utils.IsDebugEnabled() switch { case debugEnabled && !config.Debug: // disable debug utils.DisableDebug() api.DisableProfiler() case config.Debug && !debugEnabled: // enable debug utils.EnableDebug() api.EnableProfiler() } } } setupConfigReloadTrap(*cli.configFile, flags, reload) // The serve API routine never exits unless an error occurs // We need to start it as a goroutine and wait on it so // daemon doesn't exit serveAPIWait := make(chan error) go api.Wait(serveAPIWait) signal.Trap(func() { api.Close() <-serveAPIWait shutdownDaemon(d, 15) if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } }) // after the daemon is done setting up we can notify systemd api notifySystem() // Daemon is fully initialized and handling API traffic // Wait for serve API to complete errAPI := <-serveAPIWait shutdownDaemon(d, 15) containerdRemote.Cleanup() if errAPI != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) } }
func (cli *Docker) SendCmdInfo(args ...string) (*types.Info, error) { daemon := cli.daemon images := daemon.Graph().Map() var imgcount int if images == nil { imgcount = 0 } else { imgcount = len(images) } kernelVersion := "<unknown>" if kv, err := kernel.GetKernelVersion(); err == nil { kernelVersion = kv.String() } operatingSystem := "<unknown>" if s, err := operatingsystem.GetOperatingSystem(); err == nil { operatingSystem = s } // Don't do containerized check on Windows if runtime.GOOS != "windows" { if inContainer, err := operatingsystem.IsContainerized(); err != nil { glog.Errorf("Could not determine if daemon is containerized: %v", err) operatingSystem += " (error determining if containerized)" } else if inContainer { operatingSystem += " (containerized)" } } meminfo, err := system.ReadMemInfo() if err != nil { glog.Errorf("Could not read system memory info: %v", err) } // if we still have the original dockerinit binary from before we copied it locally, let's return the path to that, since that's more intuitive (the copied path is trivial to derive by hand given VERSION) initPath := utils.DockerInitPath("") if initPath == "" { // if that fails, we'll just return the path from the daemon initPath = daemon.SystemInitPath() } v := &types.Info{ ID: daemon.ID, Containers: len(daemon.List()), Images: imgcount, Driver: daemon.GraphDriver().String(), DriverStatus: daemon.GraphDriver().Status(), MemoryLimit: daemon.SystemConfig().MemoryLimit, SwapLimit: daemon.SystemConfig().SwapLimit, CPUCfsPeriod: daemon.SystemConfig().CPUCfsPeriod, CPUCfsQuota: daemon.SystemConfig().CPUCfsQuota, IPv4Forwarding: !daemon.SystemConfig().IPv4ForwardingDisabled, Debug: os.Getenv("DEBUG") != "", NFd: fileutils.GetTotalUsedFds(), OomKillDisable: daemon.SystemConfig().OomKillDisable, NGoroutines: runtime.NumGoroutine(), SystemTime: time.Now().Format(time.RFC3339Nano), NEventsListener: daemon.EventsService.SubscribersCount(), KernelVersion: kernelVersion, OperatingSystem: operatingSystem, IndexServerAddress: registry.IndexServer, RegistryConfig: daemon.RegistryService.Config, InitSha1: dockerversion.InitSHA1, InitPath: initPath, NCPU: runtime.NumCPU(), MemTotal: meminfo.MemTotal, DockerRootDir: daemon.Config().Root, Labels: daemon.Config().Labels, ExperimentalBuild: utils.ExperimentalBuild(), ServerVersion: dockerversion.Version, ClusterStore: daemon.Config().ClusterStore, ClusterAdvertise: daemon.Config().ClusterAdvertise, HTTPProxy: getProxyEnv("http_proxy"), HTTPSProxy: getProxyEnv("https_proxy"), NoProxy: getProxyEnv("no_proxy"), } if hostname, err := os.Hostname(); err == nil { v.Name = hostname } return v, nil }
func (cli *DaemonCli) start(opts daemonOptions) (err error) { stopc := make(chan bool) defer close(stopc) // warn from uuid package when running the daemon uuid.Loggerf = logrus.Warnf opts.common.SetDefaultOptions(opts.flags) if opts.common.TrustKey == "" { opts.common.TrustKey = filepath.Join( getDaemonConfDir(), cliflags.DefaultTrustKeyFile) } if cli.Config, err = loadDaemonCliConfig(opts); err != nil { return err } cli.configFile = &opts.configFile cli.flags = opts.flags if cli.Config.Debug { utils.EnableDebug() } if utils.ExperimentalBuild() { logrus.Warn("Running experimental build") } logrus.SetFormatter(&logrus.TextFormatter{ TimestampFormat: jsonlog.RFC3339NanoFixed, DisableColors: cli.Config.RawLogs, }) if err := setDefaultUmask(); err != nil { return fmt.Errorf("Failed to set umask: %v", err) } if len(cli.LogConfig.Config) > 0 { if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { return fmt.Errorf("Failed to set log opts: %v", err) } } if cli.Pidfile != "" { pf, err := pidfile.New(cli.Pidfile) if err != nil { return fmt.Errorf("Error starting daemon: %v", err) } defer func() { if err := pf.Remove(); err != nil { logrus.Error(err) } }() } serverConfig := &apiserver.Config{ Logging: true, SocketGroup: cli.Config.SocketGroup, Version: dockerversion.Version, EnableCors: cli.Config.EnableCors, CorsHeaders: cli.Config.CorsHeaders, } if cli.Config.TLS { tlsOptions := tlsconfig.Options{ CAFile: cli.Config.CommonTLSOptions.CAFile, CertFile: cli.Config.CommonTLSOptions.CertFile, KeyFile: cli.Config.CommonTLSOptions.KeyFile, } if cli.Config.TLSVerify { // server requires and verifies client's certificate tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert } tlsConfig, err := tlsconfig.Server(tlsOptions) if err != nil { return err } serverConfig.TLSConfig = tlsConfig } if len(cli.Config.Hosts) == 0 { cli.Config.Hosts = make([]string, 1) } api := apiserver.New(serverConfig) cli.api = api for i := 0; i < len(cli.Config.Hosts); i++ { var err error if cli.Config.Hosts[i], err = dopts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil { return fmt.Errorf("error parsing -H %s : %v", cli.Config.Hosts[i], err) } protoAddr := cli.Config.Hosts[i] protoAddrParts := strings.SplitN(protoAddr, "://", 2) if len(protoAddrParts) != 2 { return fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr) } proto := protoAddrParts[0] addr := protoAddrParts[1] // It's a bad idea to bind to TCP without tlsverify. if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) { logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]") } ls, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig) if err != nil { return err } ls = wrapListeners(proto, ls) // If we're binding to a TCP port, make sure that a container doesn't try to use it. if proto == "tcp" { if err := allocateDaemonPort(addr); err != nil { return err } } logrus.Debugf("Listener created for HTTP on %s (%s)", proto, addr) api.Accept(addr, ls...) } if err := migrateKey(); err != nil { return err } // FIXME: why is this down here instead of with the other TrustKey logic above? cli.TrustKeyPath = opts.common.TrustKey registryService := registry.NewService(cli.Config.ServiceOptions) containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...) if err != nil { return err } signal.Trap(func() { cli.stop() <-stopc // wait for daemonCli.start() to return }) d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote) if err != nil { return fmt.Errorf("Error starting daemon: %v", err) } name, _ := os.Hostname() c, err := cluster.New(cluster.Config{ Root: cli.Config.Root, Name: name, Backend: d, NetworkSubnetsProvider: d, DefaultAdvertiseAddr: cli.Config.SwarmDefaultAdvertiseAddr, RuntimeRoot: cli.getSwarmRunRoot(), }) if err != nil { logrus.Fatalf("Error creating cluster component: %v", err) } // Restart all autostart containers which has a swarm endpoint // and is not yet running now that we have successfully // initialized the cluster. d.RestartSwarmContainers() logrus.Info("Daemon has completed initialization") logrus.WithFields(logrus.Fields{ "version": dockerversion.Version, "commit": dockerversion.GitCommit, "graphdriver": d.GraphDriverName(), }).Info("Docker daemon") cli.d = d // initMiddlewares needs cli.d to be populated. Dont change this init order. cli.initMiddlewares(api, serverConfig) initRouter(api, d, c) cli.setupConfigReloadTrap() // The serve API routine never exits unless an error occurs // We need to start it as a goroutine and wait on it so // daemon doesn't exit serveAPIWait := make(chan error) go api.Wait(serveAPIWait) // after the daemon is done setting up we can notify systemd api notifySystem() // Daemon is fully initialized and handling API traffic // Wait for serve API to complete errAPI := <-serveAPIWait c.Cleanup() shutdownDaemon(d) containerdRemote.Cleanup() if errAPI != nil { return fmt.Errorf("Shutting down due to ServeAPI error: %v", errAPI) } return nil }
// CmdDaemon is the daemon command, called the raw arguments after `docker daemon`. func (cli *DaemonCli) CmdDaemon(args ...string) error { // warn from uuid package when running the daemon uuid.Loggerf = logrus.Warnf if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() { // deny `docker -D daemon` illegalFlag := getGlobalFlag() fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0]) os.Exit(1) } else { // allow new form `docker daemon -D` flag.Merge(cli.flags, commonFlags.FlagSet) } configFile := cli.flags.String([]string{daemonConfigFileFlag}, defaultDaemonConfigFile, "Daemon configuration file") cli.flags.ParseFlags(args, true) commonFlags.PostParse() if commonFlags.TrustKey == "" { commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) } cliConfig, err := loadDaemonCliConfig(cli.Config, cli.flags, commonFlags, *configFile) if err != nil { fmt.Fprint(os.Stderr, err) os.Exit(1) } cli.Config = cliConfig if cli.Config.Debug { utils.EnableDebug() } if utils.ExperimentalBuild() { logrus.Warn("Running experimental build") } logrus.SetFormatter(&logrus.TextFormatter{ TimestampFormat: jsonlog.RFC3339NanoFixed, DisableColors: cli.Config.RawLogs, }) if err := setDefaultUmask(); err != nil { logrus.Fatalf("Failed to set umask: %v", err) } if len(cli.LogConfig.Config) > 0 { if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { logrus.Fatalf("Failed to set log opts: %v", err) } } var pfile *pidfile.PIDFile if cli.Pidfile != "" { pf, err := pidfile.New(cli.Pidfile) if err != nil { logrus.Fatalf("Error starting daemon: %v", err) } pfile = pf defer func() { if err := pfile.Remove(); err != nil { logrus.Error(err) } }() } serverConfig := &apiserver.Config{ AuthorizationPluginNames: cli.Config.AuthorizationPlugins, Logging: true, SocketGroup: cli.Config.SocketGroup, Version: dockerversion.Version, } serverConfig = setPlatformServerConfig(serverConfig, cli.Config) if cli.Config.TLS { tlsOptions := tlsconfig.Options{ CAFile: cli.Config.CommonTLSOptions.CAFile, CertFile: cli.Config.CommonTLSOptions.CertFile, KeyFile: cli.Config.CommonTLSOptions.KeyFile, } if cli.Config.TLSVerify { // server requires and verifies client's certificate tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert } tlsConfig, err := tlsconfig.Server(tlsOptions) if err != nil { logrus.Fatal(err) } serverConfig.TLSConfig = tlsConfig } if len(cli.Config.Hosts) == 0 { cli.Config.Hosts = make([]string, 1) } api := apiserver.New(serverConfig) for i := 0; i < len(cli.Config.Hosts); i++ { var err error if cli.Config.Hosts[i], err = opts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil { logrus.Fatalf("error parsing -H %s : %v", cli.Config.Hosts[i], err) } protoAddr := cli.Config.Hosts[i] protoAddrParts := strings.SplitN(protoAddr, "://", 2) if len(protoAddrParts) != 2 { logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr) } l, err := listeners.Init(protoAddrParts[0], protoAddrParts[1], serverConfig.SocketGroup, serverConfig.TLSConfig) if err != nil { logrus.Fatal(err) } logrus.Debugf("Listener created for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) api.Accept(protoAddrParts[1], l...) } if err := migrateKey(); err != nil { logrus.Fatal(err) } cli.TrustKeyPath = commonFlags.TrustKey registryService := registry.NewService(cli.Config.ServiceOptions) d, err := daemon.NewDaemon(cli.Config, registryService) if err != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Error starting daemon: %v", err) } logrus.Info("Daemon has completed initialization") logrus.WithFields(logrus.Fields{ "version": dockerversion.Version, "commit": dockerversion.GitCommit, "execdriver": d.ExecutionDriver().Name(), "graphdriver": d.GraphDriverName(), }).Info("Docker daemon") initRouter(api, d) reload := func(config *daemon.Config) { if err := d.Reload(config); err != nil { logrus.Errorf("Error reconfiguring the daemon: %v", err) return } if config.IsValueSet("debug") { debugEnabled := utils.IsDebugEnabled() switch { case debugEnabled && !config.Debug: // disable debug utils.DisableDebug() api.DisableProfiler() case config.Debug && !debugEnabled: // enable debug utils.EnableDebug() api.EnableProfiler() } } } setupConfigReloadTrap(*configFile, cli.flags, reload) // The serve API routine never exits unless an error occurs // We need to start it as a goroutine and wait on it so // daemon doesn't exit serveAPIWait := make(chan error) go api.Wait(serveAPIWait) signal.Trap(func() { api.Close() <-serveAPIWait shutdownDaemon(d, 15) if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } }) // after the daemon is done setting up we can notify systemd api notifySystem() // Daemon is fully initialized and handling API traffic // Wait for serve API to complete errAPI := <-serveAPIWait shutdownDaemon(d, 15) if errAPI != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) } return nil }
// CmdDaemon is the daemon command, called the raw arguments after `docker daemon`. func (cli *DaemonCli) CmdDaemon(args ...string) error { // warn from uuid package when running the daemon uuid.Loggerf = logrus.Warnf //调整一下daemon的启动方式 if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() { // deny `docker -D daemon` illegalFlag := getGlobalFlag() fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0]) os.Exit(1) } else { // allow new form `docker daemon -D` flag.Merge(cli.flags, commonFlags.FlagSet) } configFile := cli.flags.String([]string{daemonConfigFileFlag}, defaultDaemonConfigFile, "Daemon configuration file") //匹配配置参数 cli.flags.ParseFlags(args, true) //配置参数生效 commonFlags.PostParse() if commonFlags.TrustKey == "" { commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) } cliConfig, err := loadDaemonCliConfig(cli.Config, cli.flags, commonFlags, *configFile) if err != nil { fmt.Fprint(os.Stderr, err) os.Exit(1) } cli.Config = cliConfig if cli.Config.Debug { utils.EnableDebug() } if utils.ExperimentalBuild() { logrus.Warn("Running experimental build") } logrus.SetFormatter(&logrus.TextFormatter{ TimestampFormat: jsonlog.RFC3339NanoFixed, DisableColors: cli.Config.RawLogs, }) if err := setDefaultUmask(); err != nil { logrus.Fatalf("Failed to set umask: %v", err) } if len(cli.LogConfig.Config) > 0 { if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { logrus.Fatalf("Failed to set log opts: %v", err) } } var pfile *pidfile.PIDFile if cli.Pidfile != "" { pf, err := pidfile.New(cli.Pidfile) if err != nil { logrus.Fatalf("Error starting daemon: %v", err) } pfile = pf defer func() { if err := pfile.Remove(); err != nil { logrus.Error(err) } }() } //定义apiserver的配置,包括认证、日志输出、版本等。 serverConfig := &apiserver.Config{ AuthorizationPluginNames: cli.Config.AuthorizationPlugins, Logging: true, SocketGroup: cli.Config.SocketGroup, Version: dockerversion.Version, } serverConfig = setPlatformServerConfig(serverConfig, cli.Config) if cli.Config.TLS { tlsOptions := tlsconfig.Options{ CAFile: cli.Config.CommonTLSOptions.CAFile, CertFile: cli.Config.CommonTLSOptions.CertFile, KeyFile: cli.Config.CommonTLSOptions.KeyFile, } if cli.Config.TLSVerify { // server requires and verifies client's certificate tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert } tlsConfig, err := tlsconfig.Server(tlsOptions) if err != nil { logrus.Fatal(err) } serverConfig.TLSConfig = tlsConfig } if len(cli.Config.Hosts) == 0 { cli.Config.Hosts = make([]string, 1) } //定义一个新的apiserver。 //apiServer是一个这样的结构(api/server/server.go): /* type Server struct { cfg *Config servers []*HTTPServer routers []router.Router authZPlugins []authorization.Plugin routerSwapper *routerSwapper } */ api := apiserver.New(serverConfig) for i := 0; i < len(cli.Config.Hosts); i++ { var err error if cli.Config.Hosts[i], err = opts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil { logrus.Fatalf("error parsin g -H %s : %v", cli.Config.Hosts[i], err) } protoAddr := cli.Config.Hosts[i] protoAddrParts := strings.SplitN(protoAddr, "://", 2) if len(protoAddrParts) != 2 { logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr) } l, err := listeners.Init(protoAddrParts[0], protoAddrParts[1], serverConfig.SocketGroup, serverConfig.TLSConfig) if err != nil { logrus.Fatal(err) } logrus.Debugf("Listener created for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) //初始化api的servers数组,里面放着的都是httpserver类型。此时也没有具体的运行什么 api.Accept(protoAddrParts[1], l...) } if err := migrateKey(); err != nil { logrus.Fatal(err) } cli.TrustKeyPath = commonFlags.TrustKey //创建镜像仓库服务 registryService := registry.NewService(cli.Config.ServiceOptions) //初始化libcontainer。比如在linux中,就会调用libcontainerd/remote_linux.go中的New方法。 containerdRemote, err := libcontainerd.New(filepath.Join(cli.Config.ExecRoot, "libcontainerd"), cli.getPlatformRemoteOptions()...) if err != nil { logrus.Fatal(err) } //初始化守护进程使得能够服务。需要输入仓库服务和libcontainerd服务的参数。 //返回的d是Daemon类型: /* type Daemon struct { ID string repository string containers container.Store execCommands *exec.Store referenceStore reference.Store downloadManager *xfer.LayerDownloadManager uploadManager *xfer.LayerUploadManager distributionMetadataStore dmetadata.Store trustKey libtrust.PrivateKey idIndex *truncindex.TruncIndex configStore *Config statsCollector *statsCollector defaultLogConfig containertypes.LogConfig RegistryService *registry.Service EventsService *events.Events netController libnetwork.NetworkController volumes *store.VolumeStore discoveryWatcher discoveryReloader root string seccompEnabled bool shutdown bool uidMaps []idtools.IDMap gidMaps []idtools.IDMap layerStore layer.Store imageStore image.Store nameIndex *registrar.Registrar linkIndex *linkIndex containerd libcontainerd.Client defaultIsolation containertypes.Isolation // Default isolation mode on Windows } */ d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote) if err != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Error starting daemon: %v", err) } logrus.Info("Daemon has completed initialization") logrus.WithFields(logrus.Fields{ "version": dockerversion.Version, "commit": dockerversion.GitCommit, "graphdriver": d.GraphDriverName(), }).Info("Docker daemon") //初始化http的路由,这个路由设计的非常易懂,所有的路由及处理函数的映射关系 //请见api/server/router/文件夹中的内容。有类似这样的内容: //router.NewPostRoute("/containers/create", r.postContainersCreate), //其中,对应的处理函数postContainersCreate在api/server/router/container/container_routes.go //但是,实际上这个函数也不做具体的事情,他交给backend去做,就是daemon去做 /* ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{ Name: name, Config: config, HostConfig: hostConfig, NetworkingConfig: networkingConfig, AdjustCPUShares: adjustCPUShares, }) */ //其中的ContainerCreate在 initRouter(api, d) reload := func(config *daemon.Config) { if err := d.Reload(config); err != nil { logrus.Errorf("Error reconfiguring the daemon: %v", err) return } if config.IsValueSet("debug") { debugEnabled := utils.IsDebugEnabled() switch { case debugEnabled && !config.Debug: // disable debug utils.DisableDebug() api.DisableProfiler() case config.Debug && !debugEnabled: // enable debug utils.EnableDebug() api.EnableProfiler() } } } setupConfigReloadTrap(*configFile, cli.flags, reload) // The serve API routine never exits unless an error occurs // We need to start it as a goroutine and wait on it so // daemon doesn't exit //设置一个传输apiServer状态的通道 serveAPIWait := make(chan error) //重新开启一个goroutine作为httpServer。 //具体的请查看api/server/server.go中的方法func (s *Server) serveAPI() error go api.Wait(serveAPIWait) signal.Trap(func() { api.Close() <-serveAPIWait shutdownDaemon(d, 15) if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } }) // after the daemon is done setting up we can notify systemd api notifySystem() // Daemon is fully initialized and handling API traffic // Wait for serve API to complete //<-表示接受通道值,只有当通道中有值的时候,才会返回。 //也就是说主线程一直在等待api.wait的goroutine启动apiServer之后的返回才会进行。 errAPI := <-serveAPIWait //当接收到返回(返回就是错误了),开始清理进程。 shutdownDaemon(d, 15) containerdRemote.Cleanup() if errAPI != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) } return nil }
Condition testCondition SkipMessage string } // List test requirements var ( DaemonIsWindows = testRequirement{ func() bool { return daemonPlatform == "windows" }, "Test requires a Windows daemon", } DaemonIsLinux = testRequirement{ func() bool { return daemonPlatform == "linux" }, "Test requires a Linux daemon", } ExperimentalDaemon = testRequirement{ func() bool { return utils.ExperimentalBuild() }, "Test requires an experimental daemon", } NotExperimentalDaemon = testRequirement{ func() bool { return !utils.ExperimentalBuild() }, "Test requires a non experimental daemon", } IsAmd64 = testRequirement{ func() bool { return os.Getenv("DOCKER_ENGINE_GOARCH") == "amd64" }, "Test requires a daemon running on amd64", } NotArm = testRequirement{ func() bool { return os.Getenv("DOCKER_ENGINE_GOARCH") != "arm" }, "Test requires a daemon not running on ARM", } NotArm64 = testRequirement{
// CmdDaemon is the daemon command, called the raw arguments after `docker daemon`. func (cli *DaemonCli) CmdDaemon(args ...string) error { // warn from uuid package when running the daemon uuid.Loggerf = logrus.Warnf if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() { // deny `docker -D daemon` illegalFlag := getGlobalFlag() fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0]) os.Exit(1) } else { // allow new form `docker daemon -D` flag.Merge(daemonFlags, commonFlags.FlagSet) } daemonFlags.ParseFlags(args, true) commonFlags.PostParse() if commonFlags.TrustKey == "" { commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) } if utils.ExperimentalBuild() { logrus.Warn("Running experimental build") } logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: jsonlog.RFC3339NanoFixed}) if err := setDefaultUmask(); err != nil { logrus.Fatalf("Failed to set umask: %v", err) } if len(cli.LogConfig.Config) > 0 { if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { logrus.Fatalf("Failed to set log opts: %v", err) } } var pfile *pidfile.PIDFile if cli.Pidfile != "" { pf, err := pidfile.New(cli.Pidfile) if err != nil { logrus.Fatalf("Error starting daemon: %v", err) } pfile = pf defer func() { if err := pfile.Remove(); err != nil { logrus.Error(err) } }() } serverConfig := &apiserver.Config{ AuthZPluginNames: cli.Config.AuthZPlugins, Logging: true, Version: dockerversion.Version, } serverConfig = setPlatformServerConfig(serverConfig, cli.Config) defaultHost := opts.DefaultHost if commonFlags.TLSOptions != nil { if !commonFlags.TLSOptions.InsecureSkipVerify { // server requires and verifies client's certificate commonFlags.TLSOptions.ClientAuth = tls.RequireAndVerifyClientCert } tlsConfig, err := tlsconfig.Server(*commonFlags.TLSOptions) if err != nil { logrus.Fatal(err) } serverConfig.TLSConfig = tlsConfig defaultHost = opts.DefaultTLSHost } if len(commonFlags.Hosts) == 0 { commonFlags.Hosts = make([]string, 1) } for i := 0; i < len(commonFlags.Hosts); i++ { var err error if commonFlags.Hosts[i], err = opts.ParseHost(defaultHost, commonFlags.Hosts[i]); err != nil { logrus.Fatalf("error parsing -H %s : %v", commonFlags.Hosts[i], err) } } for _, protoAddr := range commonFlags.Hosts { protoAddrParts := strings.SplitN(protoAddr, "://", 2) if len(protoAddrParts) != 2 { logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr) } serverConfig.Addrs = append(serverConfig.Addrs, apiserver.Addr{Proto: protoAddrParts[0], Addr: protoAddrParts[1]}) } api, err := apiserver.New(serverConfig) if err != nil { logrus.Fatal(err) } if err := migrateKey(); err != nil { logrus.Fatal(err) } cli.TrustKeyPath = commonFlags.TrustKey registryService := registry.NewService(cli.registryOptions) d, err := daemon.NewDaemon(cli.Config, registryService) if err != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Error starting daemon: %v", err) } logrus.Info("Daemon has completed initialization") logrus.WithFields(logrus.Fields{ "version": dockerversion.Version, "commit": dockerversion.GitCommit, "execdriver": d.ExecutionDriver().Name(), "graphdriver": d.GraphDriverName(), }).Info("Docker daemon") api.InitRouters(d) // The serve API routine never exits unless an error occurs // We need to start it as a goroutine and wait on it so // daemon doesn't exit serveAPIWait := make(chan error) go func() { if err := api.ServeAPI(); err != nil { logrus.Errorf("ServeAPI error: %v", err) serveAPIWait <- err return } serveAPIWait <- nil }() signal.Trap(func() { api.Close() <-serveAPIWait shutdownDaemon(d, 15) if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } }) // after the daemon is done setting up we can notify systemd api notifySystem() // Daemon is fully initialized and handling API traffic // Wait for serve API to complete errAPI := <-serveAPIWait shutdownDaemon(d, 15) if errAPI != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) } return nil }
// CmdDaemon is the daemon command, called the raw arguments after `docker daemon`. func (cli *DaemonCli) CmdDaemon(args ...string) error { if *flDaemon { // allow legacy forms `docker -D -d` and `docker -d -D` logrus.Warn("please use 'docker daemon' instead.") } else if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() { // deny `docker -D daemon` illegalFlag := getGlobalFlag() fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0]) os.Exit(1) } else { // allow new form `docker daemon -D` flag.Merge(daemonFlags, commonFlags.FlagSet) } daemonFlags.ParseFlags(args, true) commonFlags.PostParse() if len(commonFlags.Hosts) == 0 { commonFlags.Hosts = []string{opts.DefaultHost} } if commonFlags.TrustKey == "" { commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) } if utils.ExperimentalBuild() { logrus.Warn("Running experimental build") } logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed}) if err := setDefaultUmask(); err != nil { logrus.Fatalf("Failed to set umask: %v", err) } if len(cli.LogConfig.Config) > 0 { if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { logrus.Fatalf("Failed to set log opts: %v", err) } } var pfile *pidfile.PIDFile if cli.Pidfile != "" { pf, err := pidfile.New(cli.Pidfile) if err != nil { logrus.Fatalf("Error starting daemon: %v", err) } pfile = pf defer func() { if err := pfile.Remove(); err != nil { logrus.Error(err) } }() } if cli.LogConfig.Config == nil { cli.LogConfig.Config = make(map[string]string) } serverConfig := &apiserver.ServerConfig{ Logging: true, EnableCors: cli.EnableCors, CorsHeaders: cli.CorsHeaders, Version: dockerversion.VERSION, } serverConfig = setPlatformServerConfig(serverConfig, cli.Config) if commonFlags.TLSOptions != nil { if !commonFlags.TLSOptions.InsecureSkipVerify { // server requires and verifies client's certificate commonFlags.TLSOptions.ClientAuth = tls.RequireAndVerifyClientCert } tlsConfig, err := tlsconfig.Server(*commonFlags.TLSOptions) if err != nil { logrus.Fatalf("foobar: %v", err) } serverConfig.TLSConfig = tlsConfig } api := apiserver.New(serverConfig) // The serve API routine never exits unless an error occurs // We need to start it as a goroutine and wait on it so // daemon doesn't exit serveAPIWait := make(chan error) go func() { if err := api.ServeApi(commonFlags.Hosts); err != nil { logrus.Errorf("ServeAPI error: %v", err) serveAPIWait <- err return } serveAPIWait <- nil }() if err := migrateKey(); err != nil { logrus.Fatal(err) } cli.TrustKeyPath = commonFlags.TrustKey registryService := registry.NewService(cli.registryOptions) d, err := daemon.NewDaemon(cli.Config, registryService) if err != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Error starting daemon: %v", err) } logrus.Info("Daemon has completed initialization") logrus.WithFields(logrus.Fields{ "version": dockerversion.VERSION, "commit": dockerversion.GITCOMMIT, "execdriver": d.ExecutionDriver().Name(), "graphdriver": d.GraphDriver().String(), }).Info("Docker daemon") signal.Trap(func() { api.Close() <-serveAPIWait shutdownDaemon(d, 15) if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } }) // after the daemon is done setting up we can tell the api to start // accepting connections with specified daemon api.AcceptConnections(d) // Daemon is fully initialized and handling API traffic // Wait for serve API to complete errAPI := <-serveAPIWait shutdownDaemon(d, 15) if errAPI != nil { if pfile != nil { if err := pfile.Remove(); err != nil { logrus.Error(err) } } logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) } return nil }