diff --git a/probe/docker/container.go b/probe/docker/container.go index d8f6496b47..594c30d596 100644 --- a/probe/docker/container.go +++ b/probe/docker/container.go @@ -33,6 +33,7 @@ const ( ContainerState = "docker_container_state" ContainerUptime = "docker_container_uptime" ContainerRestartCount = "docker_container_restart_count" + ContainerNetworkMode = "docker_container_network_mode" NetworkRxDropped = "network_rx_dropped" NetworkRxBytes = "network_rx_bytes" @@ -58,6 +59,8 @@ const ( StateStopped = "stopped" StatePaused = "paused" + NetworkModeHost = "host" + stopTimeout = 10 ) @@ -320,7 +323,10 @@ func (c *container) GetNode(hostID string, localAddrs []net.IP) report.Node { c.RLock() defer c.RUnlock() - ips := append(c.container.NetworkSettings.SecondaryIPAddresses, c.container.NetworkSettings.IPAddress) + ips := c.container.NetworkSettings.SecondaryIPAddresses + if c.container.NetworkSettings.IPAddress != "" { + ips = append(ips, c.container.NetworkSettings.IPAddress) + } // Treat all Docker IPs as local scoped. ipsWithScopes := []string{} for _, ip := range ips { @@ -351,9 +357,14 @@ func (c *container) GetNode(hostID string, localAddrs []net.IP) report.Node { result = result.WithControls(UnpauseContainer) } else if c.container.State.Running { uptime := (mtime.Now().Sub(c.container.State.StartedAt) / time.Second) * time.Second + networkMode := "" + if c.container.HostConfig != nil { + networkMode = c.container.HostConfig.NetworkMode + } result = result.WithLatests(map[string]string{ ContainerUptime: uptime.String(), ContainerRestartCount: strconv.Itoa(c.container.RestartCount), + ContainerNetworkMode: networkMode, }) result = result.WithControls( RestartContainer, StopContainer, PauseContainer, AttachContainer, ExecContainer, diff --git a/render/topologies.go b/render/topologies.go index beb1d226b6..496787e371 100644 --- a/render/topologies.go +++ b/render/topologies.go @@ -2,8 +2,10 @@ package render import ( "fmt" + "net" "github.com/weaveworks/scope/probe/docker" + "github.com/weaveworks/scope/probe/host" "github.com/weaveworks/scope/probe/process" "github.com/weaveworks/scope/report" ) @@ -116,6 +118,49 @@ var ContainerRenderer = MakeReduce( ), ) +type containerWithHostIPsRenderer struct { + Renderer +} + +// Render produces a process graph where the ips for host network mode are set +// to the host's IPs. +func (r containerWithHostIPsRenderer) Render(rpt report.Report) RenderableNodes { + containers := r.Renderer.Render(rpt) + hosts := MakeMap( + MapHostIdentity, + SelectHost, + ).Render(rpt) + + for id, c := range containers { + networkMode, ok := c.Node.Latest.Lookup(docker.ContainerNetworkMode) + if !ok || networkMode != docker.NetworkModeHost { + continue + } + + h, ok := hosts[MakeHostID(report.ExtractHostID(c.Node))] + if !ok { + continue + } + + newIPs := report.MakeStringSet() + hostNetworks, _ := h.Sets.Lookup(host.LocalNetworks) + for _, cidr := range hostNetworks { + if ip, _, err := net.ParseCIDR(cidr); err == nil { + newIPs = newIPs.Add(ip.String()) + } + } + + c.Sets = c.Sets.Add(docker.ContainerIPs, newIPs) + containers[id] = c + } + + return containers +} + +// ContainerWithHostIPsRenderer is a Renderer which produces a container graph +// enriched with host IPs on containers where NetworkMode is Host +var ContainerWithHostIPsRenderer = containerWithHostIPsRenderer{ContainerRenderer} + type containerWithImageNameRenderer struct { Renderer } @@ -149,7 +194,7 @@ func (r containerWithImageNameRenderer) Render(rpt report.Report) RenderableNode // ContainerWithImageNameRenderer is a Renderer which produces a container // graph where the ranks are the image names, not their IDs -var ContainerWithImageNameRenderer = containerWithImageNameRenderer{ContainerRenderer} +var ContainerWithImageNameRenderer = containerWithImageNameRenderer{ContainerWithHostIPsRenderer} // ContainerImageRenderer is a Renderer which produces a renderable container // image graph by merging the container graph and the container image topology. diff --git a/render/topologies_test.go b/render/topologies_test.go index 63ce03859e..bbbcfcdf2f 100644 --- a/render/topologies_test.go +++ b/render/topologies_test.go @@ -8,6 +8,7 @@ import ( "github.com/weaveworks/scope/probe/kubernetes" "github.com/weaveworks/scope/render" "github.com/weaveworks/scope/render/expected" + "github.com/weaveworks/scope/report" "github.com/weaveworks/scope/test" "github.com/weaveworks/scope/test/fixture" ) @@ -51,6 +52,25 @@ func TestContainerFilterRenderer(t *testing.T) { } } +func TestContainerWithHostIPsRenderer(t *testing.T) { + input := fixture.Report.Copy() + input.Container.Nodes[fixture.ClientContainerNodeID] = input.Container.Nodes[fixture.ClientContainerNodeID].WithLatests(map[string]string{ + docker.ContainerNetworkMode: "host", + }) + nodes := render.ContainerWithHostIPsRenderer.Render(input) + + // Test host network nodes get the host IPs added. + haveNode, ok := nodes[render.MakeContainerID(fixture.ClientContainerID)] + if !ok { + t.Fatal("Expected output to have the client container node") + } + have, _ := haveNode.Sets.Lookup(docker.ContainerIPs) + want := report.MakeStringSet("10.10.10.0") + if !reflect.DeepEqual(want, have) { + t.Error(test.Diff(want, have)) + } +} + func TestContainerFilterRendererImageName(t *testing.T) { // Test nodes are filtered by image name as well. input := fixture.Report.Copy()