diff --git a/api/handlers.go b/api/handlers.go index 0adcd8ed62..eef7dd9457 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -29,23 +29,45 @@ const APIVERSION = "1.21" // GET /info func getInfo(c *context, w http.ResponseWriter, r *http.Request) { - info := dockerclient.Info{ - Containers: int64(len(c.cluster.Containers())), - Images: int64(len(c.cluster.Images().Filter(cluster.ImageFilterOptions{}))), + info := apitypes.Info{ + Images: len(c.cluster.Images().Filter(cluster.ImageFilterOptions{})), DriverStatus: c.statusHandler.Status(), - NEventsListener: int64(c.eventsHandler.Size()), + NEventsListener: c.eventsHandler.Size(), Debug: c.debug, MemoryLimit: true, SwapLimit: true, + CPUCfsPeriod: true, + CPUCfsQuota: true, + CPUShares: true, + CPUSet: true, IPv4Forwarding: true, BridgeNfIptables: true, - BridgeNfIp6tables: true, - NCPU: c.cluster.TotalCpus(), + BridgeNfIP6tables: true, + OomKillDisable: true, + OperatingSystem: runtime.GOOS, + Architecture: runtime.GOARCH, + NCPU: int(c.cluster.TotalCpus()), MemTotal: c.cluster.TotalMemory(), - HttpProxy: os.Getenv("http_proxy"), - HttpsProxy: os.Getenv("https_proxy"), + HTTPProxy: os.Getenv("http_proxy"), + HTTPSProxy: os.Getenv("https_proxy"), NoProxy: os.Getenv("no_proxy"), SystemTime: time.Now().Format(time.RFC3339Nano), + ExperimentalBuild: experimental.ENABLED, + } + + if kernelVersion, err := kernel.GetKernelVersion(); err == nil { + info.KernelVersion = kernelVersion.String() + } + + for _, c := range c.cluster.Containers() { + info.Containers++ + if c.Info.State.Paused { + info.ContainersPaused++ + } else if c.Info.State.Running { + info.ContainersRunning++ + } else { + info.ContainersStopped++ + } } if hostname, err := os.Hostname(); err == nil { diff --git a/api/status.go b/api/status.go index 962e5c9ada..c2f59efe91 100644 --- a/api/status.go +++ b/api/status.go @@ -3,5 +3,5 @@ package api // StatusHandler allows the API to display extra information on docker info. type StatusHandler interface { // Info provides key/values to be added to docker info. - Status() [][]string + Status() [][2]string } diff --git a/cli/manage.go b/cli/manage.go index d530bb7fe2..5647315576 100644 --- a/cli/manage.go +++ b/cli/manage.go @@ -49,16 +49,16 @@ type statusHandler struct { follower *leadership.Follower } -func (h *statusHandler) Status() [][]string { - var status [][]string +func (h *statusHandler) Status() [][2]string { + var status [][2]string if h.candidate != nil && !h.candidate.IsLeader() { - status = [][]string{ + status = [][2]string{ {"\bRole", "replica"}, {"\bPrimary", h.follower.Leader()}, } } else { - status = [][]string{ + status = [][2]string{ {"\bRole", "primary"}, } } diff --git a/cluster/cluster.go b/cluster/cluster.go index 480b2c6eee..2c2627fcbd 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -69,7 +69,7 @@ type Cluster interface { // Return some info about the cluster, like nb or containers / images // It is pretty open, so the implementation decides what to return. - Info() [][]string + Info() [][2]string // Return the total memory of the cluster TotalMemory() int64 diff --git a/cluster/mesos/cluster.go b/cluster/mesos/cluster.go index fd08f5cb1e..201bbe67b6 100644 --- a/cluster/mesos/cluster.go +++ b/cluster/mesos/cluster.go @@ -396,9 +396,9 @@ func (c *Cluster) TotalCpus() int64 { } // Info gives minimal information about containers and resources on the mesos cluster -func (c *Cluster) Info() [][]string { +func (c *Cluster) Info() [][2]string { offers := c.listOffers() - info := [][]string{ + info := [][2]string{ {"\bStrategy", c.scheduler.Strategy()}, {"\bFilters", c.scheduler.Filters()}, {"\bOffers", fmt.Sprintf("%d", len(offers))}, @@ -407,9 +407,9 @@ func (c *Cluster) Info() [][]string { sort.Sort(offerSorter(offers)) for _, offer := range offers { - info = append(info, []string{" Offer", offer.Id.GetValue()}) + info = append(info, [2]string{" Offer", offer.Id.GetValue()}) for _, resource := range offer.Resources { - info = append(info, []string{" └ " + resource.GetName(), formatResource(resource)}) + info = append(info, [2]string{" └ " + resource.GetName(), formatResource(resource)}) } } diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index f8fad61260..9fa08b71f2 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -821,8 +821,8 @@ func (c *Cluster) TotalCpus() int64 { } // Info returns some info about the cluster, like nb or containers / images -func (c *Cluster) Info() [][]string { - info := [][]string{ +func (c *Cluster) Info() [][2]string { + info := [][2]string{ {"\bStrategy", c.scheduler.Strategy()}, {"\bFilters", c.scheduler.Filters()}, {"\bNodes", fmt.Sprintf("%d", len(c.engines)+len(c.pendingEngines))}, @@ -832,23 +832,23 @@ func (c *Cluster) Info() [][]string { sort.Sort(cluster.EngineSorter(engines)) for _, engine := range engines { - info = append(info, []string{engine.Name, engine.Addr}) - info = append(info, []string{" └ Status", engine.Status()}) - info = append(info, []string{" └ Containers", fmt.Sprintf("%d", len(engine.Containers()))}) - info = append(info, []string{" └ Reserved CPUs", fmt.Sprintf("%d / %d", engine.UsedCpus(), engine.TotalCpus())}) - info = append(info, []string{" └ Reserved Memory", fmt.Sprintf("%s / %s", units.BytesSize(float64(engine.UsedMemory())), units.BytesSize(float64(engine.TotalMemory())))}) + info = append(info, [2]string{engine.Name, engine.Addr}) + info = append(info, [2]string{" └ Status", engine.Status()}) + info = append(info, [2]string{" └ Containers", fmt.Sprintf("%d", len(engine.Containers()))}) + info = append(info, [2]string{" └ Reserved CPUs", fmt.Sprintf("%d / %d", engine.UsedCpus(), engine.TotalCpus())}) + info = append(info, [2]string{" └ Reserved Memory", fmt.Sprintf("%s / %s", units.BytesSize(float64(engine.UsedMemory())), units.BytesSize(float64(engine.TotalMemory())))}) labels := make([]string, 0, len(engine.Labels)) for k, v := range engine.Labels { labels = append(labels, k+"="+v) } sort.Strings(labels) - info = append(info, []string{" └ Labels", fmt.Sprintf("%s", strings.Join(labels, ", "))}) + info = append(info, [2]string{" └ Labels", fmt.Sprintf("%s", strings.Join(labels, ", "))}) errMsg := engine.ErrMsg() if len(errMsg) == 0 { errMsg = "(none)" } - info = append(info, []string{" └ Error", errMsg}) - info = append(info, []string{" └ UpdatedAt", engine.UpdatedAt().UTC().Format(time.RFC3339)}) + info = append(info, [2]string{" └ Error", errMsg}) + info = append(info, [2]string{" └ UpdatedAt", engine.UpdatedAt().UTC().Format(time.RFC3339)}) } return info diff --git a/test/integration/api/info.bats b/test/integration/api/info.bats index 6171b6a402..c57ba2d33f 100644 --- a/test/integration/api/info.bats +++ b/test/integration/api/info.bats @@ -14,4 +14,32 @@ function teardown() { [ "$status" -eq 0 ] [[ "${output}" == *"Nodes: 2"* ]] [[ "${output}" == *"└ Labels:"*"foo=bar"* ]] + } + +@test "docker info - details" { + # details in docker info were introduced in docker 1.10, skip older version without + run docker info + if [[ "${output}" != *"Paused:"* ]]; then + skip + fi + + start_docker_with_busybox 2 + swarm_manage + + docker_swarm run -d --name test busybox sleep 100 + run docker_swarm info + [ "$status" -eq 0 ] + [[ "${output}" == *"Running: 1"* ]] + + docker_swarm pause test + run docker_swarm info + [ "$status" -eq 0 ] + [[ "${output}" == *"Paused: 1"* ]] + + docker_swarm unpause test + docker_swarm kill test + run docker_swarm info + [ "$status" -eq 0 ] + [[ "${output}" == *"Stopped: 1"* ]] +} \ No newline at end of file