Skip to content

Commit

Permalink
Expose some more information on containers.
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom Wilkie committed Jun 19, 2015
1 parent 49dae07 commit c793e86
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 48 deletions.
39 changes: 32 additions & 7 deletions probe/docker/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,20 @@ import (
"strconv"
"strings"
"sync"
"time"

docker "github.com/fsouza/go-dockerclient"

"github.com/weaveworks/scope/report"
)

// These constants are keys used in node metadata
// TODO: use these constants in report/{mapping.go, detailed_node.go} - pending some circular references
const (
ContainerName = "docker_container_name"
ContainerCommand = "docker_container_command"
ContainerPorts = "docker_container_ports"
ContainerCreated = "docker_container_created"

NetworkRxDropped = "network_rx_dropped"
NetworkRxBytes = "network_rx_bytes"
NetworkRxErrors = "network_rx_errors"
Expand Down Expand Up @@ -59,7 +64,7 @@ type ClientConn interface {
Close() error
}

// Container represents a docker container
// Container represents a Docker container
type Container interface {
ID() string
Image() string
Expand Down Expand Up @@ -163,7 +168,6 @@ func (c *container) StartGatheringStats() error {
return nil
}

// called whilst holding t.Lock()
func (c *container) StopGatheringStats() {
c.Lock()
defer c.Unlock()
Expand All @@ -178,15 +182,36 @@ func (c *container) StopGatheringStats() {
return
}

// called whilst holding t.RLock()
func (c *container) ports() string {
if c.container.NetworkSettings == nil {
return ""
}

ports := []string{}
for port, bindings := range c.container.NetworkSettings.Ports {
if len(bindings) == 0 {
ports = append(ports, fmt.Sprintf("%s", port))
continue
}
for _, b := range bindings {
ports = append(ports, fmt.Sprintf("%s:%s->%s", b.HostIP, b.HostPort, port))
}
}

return strings.Join(ports, ", ")
}

func (c *container) GetNodeMetadata() report.NodeMetadata {
c.RLock()
defer c.RUnlock()

result := report.NodeMetadata{
ContainerID: c.ID(),
ContainerName: strings.TrimPrefix(c.container.Name, "/"),
ImageID: c.container.Image,
ContainerID: c.ID(),
ContainerName: strings.TrimPrefix(c.container.Name, "/"),
ContainerPorts: c.ports(),
ContainerCreated: c.container.Created.Format(time.RFC822),
ContainerCommand: c.container.Path + " " + strings.Join(c.container.Args, " "),
ImageID: c.container.Image,
}

if c.latestStats == nil {
Expand Down
5 changes: 2 additions & 3 deletions probe/docker/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import (

// Keys for use in NodeMetadata
const (
ContainerName = "docker_container_name"
ImageID = "docker_image_id"
ImageName = "docker_image_name"
ImageID = "docker_image_id"
ImageName = "docker_image_name"
)

// Reporter generate Reports containing Container and ContainerImage topologies
Expand Down
28 changes: 23 additions & 5 deletions render/detailed_node.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package render

import (
"fmt"
"reflect"
"strconv"

"github.com/weaveworks/scope/probe/docker"
"github.com/weaveworks/scope/report"
)

const (
mb = 1 << 20
)

// DetailedNode is the data type that's yielded to the JavaScript layer when
// we want deep information about an individual node.
type DetailedNode struct {
Expand Down Expand Up @@ -152,14 +158,26 @@ func processOriginTable(nmd report.NodeMetadata) (Table, bool) {
func containerOriginTable(nmd report.NodeMetadata) (Table, bool) {
rows := []Row{}
for _, tuple := range []struct{ key, human string }{
{"docker_container_id", "Container ID"},
{"docker_container_name", "Container name"},
{"docker_image_id", "Container image ID"},
{docker.ContainerID, "ID"},
{docker.ContainerName, "Name"},
{docker.ImageID, "Image ID"},
{docker.ContainerPorts, "Ports"},
{docker.ContainerCreated, "Created"},
{docker.ContainerCommand, "Command"},
} {
if val, ok := nmd[tuple.key]; ok {
rows = append(rows, Row{Key: tuple.human, ValueMajor: val, ValueMinor: ""})
}
}

if val, ok := nmd[docker.MemoryUsage]; ok {
memory, err := strconv.ParseFloat(val, 64)
if err == nil {
memoryStr := fmt.Sprintf("%0.2f", memory/float64(mb))
rows = append(rows, Row{Key: "Memory Usage (MB):", ValueMajor: memoryStr, ValueMinor: ""})
}
}

return Table{
Title: "Origin Container",
Numeric: false,
Expand All @@ -170,8 +188,8 @@ func containerOriginTable(nmd report.NodeMetadata) (Table, bool) {
func containerImageOriginTable(nmd report.NodeMetadata) (Table, bool) {
rows := []Row{}
for _, tuple := range []struct{ key, human string }{
{"docker_image_id", "Container image ID"},
{"docker_image_name", "Container image name"},
{docker.ImageID, "Image ID"},
{docker.ImageName, "Image name"},
} {
if val, ok := nmd[tuple.key]; ok {
rows = append(rows, Row{Key: tuple.human, ValueMajor: val, ValueMinor: ""})
Expand Down
6 changes: 3 additions & 3 deletions render/detailed_node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ func TestMakeDetailedNode(t *testing.T) {
Title: "Origin Container",
Numeric: false,
Rows: []render.Row{
{"Container ID", "5e4d3c2b1a", ""},
{"Container name", "server", ""},
{"Container image ID", "imageid456", ""},
{"ID", "5e4d3c2b1a", ""},
{"Name", "server", ""},
{"Image ID", "imageid456", ""},
},
},
{
Expand Down
17 changes: 9 additions & 8 deletions render/mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net"
"strings"

"github.com/weaveworks/scope/probe/docker"
"github.com/weaveworks/scope/report"
)

Expand Down Expand Up @@ -80,10 +81,10 @@ func MapProcessIdentity(m report.NodeMetadata) (RenderableNode, bool) {
// nodes, we can safely assume the presences of certain keys.
func MapContainerIdentity(m report.NodeMetadata) (RenderableNode, bool) {
var (
id = m["docker_container_id"]
major = m["docker_container_name"]
id = m[docker.ContainerID]
major = m[docker.ContainerName]
minor = report.ExtractHostID(m)
rank = m["docker_image_id"]
rank = m[docker.ImageID]
)

return NewRenderableNode(id, major, minor, rank, m), true
Expand All @@ -94,9 +95,9 @@ func MapContainerIdentity(m report.NodeMetadata) (RenderableNode, bool) {
// topology nodes, we can safely assume the presences of certain keys.
func MapContainerImageIdentity(m report.NodeMetadata) (RenderableNode, bool) {
var (
id = m["docker_image_id"]
major = m["docker_image_name"]
rank = m["docker_image_id"]
id = m[docker.ImageID]
major = m[docker.ImageName]
rank = m[docker.ImageID]
)

return NewRenderableNode(id, major, "", rank, m), true
Expand Down Expand Up @@ -181,7 +182,7 @@ func MapProcess2Container(n RenderableNode) (RenderableNode, bool) {

// Otherwise, if the process is not in a container, group it
// into an "Uncontained" node
id, ok := n.NodeMetadata["docker_container_id"]
id, ok := n.NodeMetadata[docker.ContainerID]
if !ok || n.Pseudo {
return newDerivedPseudoNode(UncontainedID, UncontainedMajor, n), true
}
Expand Down Expand Up @@ -231,7 +232,7 @@ func MapContainer2ContainerImage(n RenderableNode) (RenderableNode, bool) {

// Otherwise, if the process is not in a container, group it
// into an "Uncontained" node
id, ok := n.NodeMetadata["docker_image_id"]
id, ok := n.NodeMetadata[docker.ImageID]
if !ok || n.Pseudo {
return newDerivedPseudoNode(UncontainedID, UncontainedMajor, n), true
}
Expand Down
45 changes: 23 additions & 22 deletions render/topologies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"reflect"
"testing"

"github.com/weaveworks/scope/probe/docker"
"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/report"
"github.com/weaveworks/scope/test"
Expand Down Expand Up @@ -160,16 +161,16 @@ var (
Adjacency: report.Adjacency{},
NodeMetadatas: report.NodeMetadatas{
clientProcessNodeID: report.NodeMetadata{
"pid": clientPID,
"comm": "curl",
"docker_container_id": clientContainerID,
report.HostNodeID: clientHostNodeID,
"pid": clientPID,
"comm": "curl",
docker.ContainerID: clientContainerID,
report.HostNodeID: clientHostNodeID,
},
serverProcessNodeID: report.NodeMetadata{
"pid": serverPID,
"comm": "apache",
"docker_container_id": serverContainerID,
report.HostNodeID: serverHostNodeID,
"pid": serverPID,
"comm": "apache",
docker.ContainerID: serverContainerID,
report.HostNodeID: serverHostNodeID,
},
nonContainerProcessNodeID: report.NodeMetadata{
"pid": nonContainerPID,
Expand All @@ -182,30 +183,30 @@ var (
Container: report.Topology{
NodeMetadatas: report.NodeMetadatas{
clientContainerNodeID: report.NodeMetadata{
"docker_container_id": clientContainerID,
"docker_container_name": "client",
"docker_image_id": clientContainerImageID,
report.HostNodeID: clientHostNodeID,
docker.ContainerID: clientContainerID,
docker.ContainerName: "client",
docker.ImageID: clientContainerImageID,
report.HostNodeID: clientHostNodeID,
},
serverContainerNodeID: report.NodeMetadata{
"docker_container_id": serverContainerID,
"docker_container_name": "server",
"docker_image_id": serverContainerImageID,
report.HostNodeID: serverHostNodeID,
docker.ContainerID: serverContainerID,
docker.ContainerName: "server",
docker.ImageID: serverContainerImageID,
report.HostNodeID: serverHostNodeID,
},
},
},
ContainerImage: report.Topology{
NodeMetadatas: report.NodeMetadatas{
clientContainerImageNodeID: report.NodeMetadata{
"docker_image_id": clientContainerImageID,
"docker_image_name": "client_image",
report.HostNodeID: clientHostNodeID,
docker.ImageID: clientContainerImageID,
docker.ImageName: "client_image",
report.HostNodeID: clientHostNodeID,
},
serverContainerImageNodeID: report.NodeMetadata{
"docker_image_id": serverContainerImageID,
"docker_image_name": "server_image",
report.HostNodeID: serverHostNodeID,
docker.ImageID: serverContainerImageID,
docker.ImageName: "server_image",
report.HostNodeID: serverHostNodeID,
},
},
},
Expand Down

0 comments on commit c793e86

Please sign in to comment.