Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ContainerImage topology and use it to build the Container By Image graph. #254

Merged
merged 1 commit into from
Jun 18, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ var topologyRegistry = map[string]topologyView{
"containers-by-image": {
human: "by image",
parent: "containers",
renderer: render.LeafMap{Selector: report.SelectEndpoint, Mapper: render.ProcessContainerImage, Pseudo: render.InternetOnlyPseudoNode},
renderer: render.ContainerImageRenderer,
},
"hosts": {
human: "Hosts",
Expand Down
1 change: 1 addition & 0 deletions probe/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ func main() {

if dockerTagger != nil {
r.Container.Merge(dockerTagger.ContainerTopology(hostID))
r.ContainerImage.Merge(dockerTagger.ContainerImageTopology(hostID))
}

if weaveTagger != nil {
Expand Down
35 changes: 22 additions & 13 deletions probe/tag/docker_tagger.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,6 @@ func (t *DockerTagger) Containers() []*docker.Container {
// Tag implements Tagger.
func (t *DockerTagger) Tag(r report.Report) report.Report {
t.tag(&r.Process)
t.tag(&r.Endpoint)
return r
}

Expand Down Expand Up @@ -313,15 +312,6 @@ func (t *DockerTagger) tag(topology *report.Topology) {

md := report.NodeMetadata{
ContainerID: container.ID,
ImageID: container.Image,
}

t.RLock()
image, ok := t.images[container.Image]
t.RUnlock()

if ok && len(image.RepoTags) > 0 {
md[ImageName] = image.RepoTags[0]
}

topology.NodeMetadatas[nodeID].Merge(md)
Expand All @@ -341,14 +331,33 @@ func (t *DockerTagger) ContainerTopology(scope string) report.Topology {
ImageID: container.Image,
}

nmd.Merge(container.getStats())

nodeID := report.MakeContainerNodeID(scope, container.ID)
result.NodeMetadatas[nodeID] = nmd
}
return result
}

// ContainerImageTopology produces a Toplogy of Container Images
func (t *DockerTagger) ContainerImageTopology(scope string) report.Topology {
t.RLock()
defer t.RUnlock()

result := report.NewTopology()

// Loop over containers so we only emit images for running containers.
for _, container := range t.containers {
nmd := report.NodeMetadata{
ImageID: container.Image,
}

image, ok := t.images[container.Image]
if ok && len(image.RepoTags) > 0 {
nmd[ImageName] = image.RepoTags[0]
}

nmd.Merge(container.getStats())

nodeID := report.MakeContainerNodeID(scope, container.ID)
nodeID := report.MakeContainerNodeID(scope, container.Image)
result.NodeMetadatas[nodeID] = nmd
}
return result
Expand Down
49 changes: 33 additions & 16 deletions probe/tag/docker_tagger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

docker "github.com/fsouza/go-dockerclient"
"github.com/weaveworks/scope/report"
"github.com/weaveworks/scope/test"
)

type mockDockerClient struct {
Expand Down Expand Up @@ -67,18 +68,31 @@ func TestDockerTagger(t *testing.T) {
}

var (
pid1NodeID = report.MakeProcessNodeID("somehost.com", "1")
pid2NodeID = report.MakeProcessNodeID("somehost.com", "2")
endpointNodeMetadata = report.NodeMetadata{
pid1NodeID = report.MakeProcessNodeID("somehost.com", "1")
pid2NodeID = report.MakeProcessNodeID("somehost.com", "2")
processNodeMetadata = report.NodeMetadata{
ContainerID: "foo",
ImageID: "baz",
ImageName: "bang",
}
processNodeMetadata = report.NodeMetadata{
ContainerID: "foo",
ContainerName: "bar",
ImageID: "baz",
ImageName: "bang",
wantContainerTopology = report.Topology{
Adjacency: report.Adjacency{},
EdgeMetadatas: report.EdgeMetadatas{},
NodeMetadatas: report.NodeMetadatas{
report.MakeContainerNodeID("", "foo"): report.NodeMetadata{
ContainerID: "foo",
ContainerName: "bar",
ImageID: "baz",
},
},
}
wantContainerImageTopology = report.Topology{
Adjacency: report.Adjacency{},
EdgeMetadatas: report.EdgeMetadatas{},
NodeMetadatas: report.NodeMetadatas{
report.MakeContainerNodeID("", "baz"): report.NodeMetadata{
ImageID: "baz",
ImageName: "bang",
},
},
}
)

Expand All @@ -89,18 +103,21 @@ func TestDockerTagger(t *testing.T) {
dockerTagger, _ := NewDockerTagger("/irrelevant", 10*time.Second)
runtime.Gosched()
for _, nodeID := range []string{pid1NodeID, pid2NodeID} {
want := endpointNodeMetadata.Copy()
want := processNodeMetadata.Copy()
have := dockerTagger.Tag(r).Process.NodeMetadatas[nodeID].Copy()
delete(have, "pid")
if !reflect.DeepEqual(want, have) {
t.Errorf("%q: want %+v, have %+v", nodeID, want, have)
}
}

wantTopology := report.NewTopology()
wantTopology.NodeMetadatas[report.MakeContainerNodeID("", "foo")] = processNodeMetadata
haveTopology := dockerTagger.ContainerTopology("")
if !reflect.DeepEqual(wantTopology, haveTopology) {
t.Errorf("toplog want %+v, have %+v", wantTopology, haveTopology)
haveContainerTopology := dockerTagger.ContainerTopology("")
if !reflect.DeepEqual(wantContainerTopology, haveContainerTopology) {
t.Errorf("%s", test.Diff(wantContainerTopology, haveContainerTopology))
}

haveContainerImageTopology := dockerTagger.ContainerImageTopology("")
if !reflect.DeepEqual(wantContainerImageTopology, haveContainerImageTopology) {
t.Errorf("%s", test.Diff(wantContainerImageTopology, haveContainerImageTopology))
}
}
21 changes: 20 additions & 1 deletion render/detailed_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ func OriginTable(r report.Report, originID string) (Table, bool) {
if nmd, ok := r.Container.NodeMetadatas[originID]; ok {
return containerOriginTable(nmd)
}
if nmd, ok := r.ContainerImage.NodeMetadatas[originID]; ok {
return containerImageOriginTable(nmd)
}
if nmd, ok := r.Host.NodeMetadatas[originID]; ok {
return hostOriginTable(nmd)
}
Expand Down Expand Up @@ -155,7 +158,6 @@ func containerOriginTable(nmd report.NodeMetadata) (Table, bool) {
{"docker_container_id", "Container ID"},
{"docker_container_name", "Container name"},
{"docker_image_id", "Container image ID"},
{"docker_image_name", "Container image name"},
} {
if val, ok := nmd[tuple.key]; ok {
rows = append(rows, Row{Key: tuple.human, ValueMajor: val, ValueMinor: ""})
Expand All @@ -168,6 +170,23 @@ func containerOriginTable(nmd report.NodeMetadata) (Table, bool) {
}, len(rows) > 0
}

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"},
} {
if val, ok := nmd[tuple.key]; ok {
rows = append(rows, Row{Key: tuple.human, ValueMajor: val, ValueMinor: ""})
}
}
return Table{
Title: "Origin Container Image",
Numeric: false,
Rows: rows,
}, len(rows) > 0
}

func hostOriginTable(nmd report.NodeMetadata) (Table, bool) {
rows := []Row{}
if val, ok := nmd["host_name"]; ok {
Expand Down
6 changes: 4 additions & 2 deletions render/detailed_node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/test"
)

func TestOriginTable(t *testing.T) {
Expand Down Expand Up @@ -51,7 +52,7 @@ func TestOriginTable(t *testing.T) {
continue
}
if !reflect.DeepEqual(want, have) {
t.Errorf("%q: %s", originID, diff(want, have))
t.Errorf("%q: %s", originID, test.Diff(want, have))
}
}
}
Expand Down Expand Up @@ -95,6 +96,7 @@ func TestMakeDetailedNode(t *testing.T) {
Rows: []render.Row{
{"Container ID", "5e4d3c2b1a", ""},
{"Container name", "server", ""},
{"Container image ID", "imageid456", ""},
},
},
{
Expand All @@ -109,6 +111,6 @@ func TestMakeDetailedNode(t *testing.T) {
},
}
if !reflect.DeepEqual(want, have) {
t.Errorf("%s", diff(want, have))
t.Errorf("%s", test.Diff(want, have))

This comment was marked as abuse.

This comment was marked as abuse.

This comment was marked as abuse.

}
}
39 changes: 29 additions & 10 deletions render/mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ func MapContainerIdentity(m report.NodeMetadata) (RenderableNode, bool) {
return NewRenderableNode(id, major, minor, rank, m), true
}

// MapContainerImageIdentity maps a container image topology node to container
// image RenderableNode node. As it is only ever run on container image
// 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"]
)

return NewRenderableNode(id, major, "", rank, m), true
}

// MapEndpoint2Process maps endpoint RenderableNodes to process
// RenderableNodes.
//
Expand Down Expand Up @@ -156,18 +169,24 @@ func MapProcess2Name(n RenderableNode) (RenderableNode, bool) {
return node, true
}

// ProcessContainerImage maps topology nodes to the container images they run
// on. If no container metadata is found, nodes are grouped into the
// Uncontained node.
func ProcessContainerImage(m report.NodeMetadata) (RenderableNode, bool) {
var id, major, minor, rank string
if m["docker_image_id"] == "" {
id, major, minor, rank = UncontainedID, UncontainedMajor, "", UncontainedID
} else {
id, major, minor, rank = m["docker_image_id"], m["docker_image_name"], "", m["docker_image_id"]
// MapContainer2ContainerImage maps container RenderableNodes to container
// image RenderableNodes.
//
// If this function is given a node without a docker_image_id
// (including other pseudo nodes), it will produce an "Uncontained"
// pseudo node.
//
// Otherwise, this function will produce a node with the correct ID
// format for a container, but without any Major or Minor labels.
// It does not have enough info to do that, and the resulting graph
// must be merged with a container graph to get that info.
func MapContainer2ContainerImage(n RenderableNode) (RenderableNode, bool) {
id, ok := n.NodeMetadata["docker_image_id"]
if !ok || n.Pseudo {
return newDerivedPseudoNode(UncontainedID, UncontainedMajor, n), true
}

return NewRenderableNode(id, major, minor, rank, m), true
return newDerivedNode(id, n), true
}

// NetworkHostname takes a node NodeMetadata and returns a representation
Expand Down
14 changes: 14 additions & 0 deletions render/topologies.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,17 @@ var ContainerRenderer = MakeReduce(
Pseudo: GenericPseudoNode,
},
)

// ContainerImageRenderer is a Renderer which produces a renderable container
// image graph by merging the container graph and the container image topology.
var ContainerImageRenderer = MakeReduce(
Map{
MapFunc: MapContainer2ContainerImage,
Renderer: ContainerRenderer,
},
LeafMap{
Selector: report.SelectContainerImage,
Mapper: MapContainerImageIdentity,
Pseudo: GenericPseudoNode,
},
)
Loading