Skip to content

Commit

Permalink
Gather container IP addresses from Docker and Weave, and apply them t…
Browse files Browse the repository at this point in the history
…o nodes in the container topology.
  • Loading branch information
Tom Wilkie committed Aug 26, 2015
1 parent 7e39b92 commit b86cbaa
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 5 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ probe/probe
probe/scope-probe
docker/scope-app
docker/scope-probe
docker/docker*
docker/weave
experimental/bridge/bridge
experimental/demoprobe/demoprobe
experimental/fixprobe/fixprobe
Expand Down
13 changes: 12 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,23 @@ SCOPE_EXPORT=scope.tar
SCOPE_UI_BUILD_EXPORT=scope_ui_build.tar
SCOPE_UI_BUILD_IMAGE=$(DOCKERHUB_USER)/scope-ui-build
SCOPE_VERSION=$(shell git rev-parse --short HEAD)
DOCKER_VERSION=1.3.1
DOCKER_DISTRIB=docker/docker-$(DOCKER_VERSION).tgz
DOCKER_DISTRIB_URL=https://get.docker.com/builds/Linux/x86_64/docker-$(DOCKER_VERSION).tgz

all: $(SCOPE_EXPORT)

$(SCOPE_EXPORT): $(APP_EXE) $(PROBE_EXE) docker/*
$(DOCKER_DISTRIB):
curl -o $(DOCKER_DISTRIB) $(DOCKER_DISTRIB_URL)

docker/weave:
curl -L git.io/weave -o docker/weave
chmod u+x docker/weave

$(SCOPE_EXPORT): $(APP_EXE) $(PROBE_EXE) $(DOCKER_DISTRIB) docker/weave docker/entrypoint.sh docker/Dockerfile docker/run-app docker/run-probe
@if [ -z '$(DOCKER_SQUASH)' ]; then echo "Please install docker-squash by running 'make deps'." && exit 1; fi
cp $(APP_EXE) $(PROBE_EXE) docker/
cp $(DOCKER_DISTRIB) docker/docker.tgz
$(SUDO) docker build -t $(SCOPE_IMAGE) docker/
$(SUDO) docker save $(SCOPE_IMAGE):latest | sudo $(DOCKER_SQUASH) -t $(SCOPE_IMAGE) | tee $@ | $(SUDO) docker load

Expand Down
4 changes: 3 additions & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ FROM gliderlabs/alpine
MAINTAINER Weaveworks Inc <[email protected]>
WORKDIR /home/weave
RUN echo "http://dl-4.alpinelinux.org/alpine/edge/testing" >>/etc/apk/repositories && \
apk add --update runit conntrack-tools && \
apk add --update runit conntrack-tools iproute2 util-linux && \
rm -rf /var/cache/apk/*
ADD ./docker.tgz /
ADD ./weave /usr/bin/
COPY ./scope-app ./scope-probe ./entrypoint.sh /home/weave/
COPY ./run-app /etc/service/app/run
COPY ./run-probe /etc/service/probe/run
Expand Down
2 changes: 2 additions & 0 deletions probe/docker/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const (
ContainerCommand = "docker_container_command"
ContainerPorts = "docker_container_ports"
ContainerCreated = "docker_container_created"
ContainerIPs = "docker_container_ips"

NetworkRxDropped = "network_rx_dropped"
NetworkRxBytes = "network_rx_bytes"
Expand Down Expand Up @@ -212,6 +213,7 @@ func (c *container) GetNodeMetadata() report.NodeMetadata {
ContainerCreated: c.container.Created.Format(time.RFC822),
ContainerCommand: c.container.Path + " " + strings.Join(c.container.Args, " "),
ImageID: c.container.Image,
ContainerIPs: c.container.NetworkSettings.IPAddress,
})

if c.latestStats == nil {
Expand Down
3 changes: 3 additions & 0 deletions probe/docker/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ var (
Name: "pong",
Image: "baz",
State: client.State{Pid: 1, Running: true},
NetworkSettings: &client.NetworkSettings{
IPAddress: "1.2.3.4",
},
}
container2 = &client.Container{
ID: "wiff",
Expand Down
83 changes: 83 additions & 0 deletions probe/overlay/weave.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package overlay

import (
"bufio"
"encoding/json"
"fmt"
"io"
"log"
"net"
"net/http"
"net/url"
"os/exec"
"regexp"
"strings"

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

Expand All @@ -21,8 +27,25 @@ const (

// WeaveDNSHostname is the ket for the WeaveDNS hostname
WeaveDNSHostname = "weave_dns_hostname"

// WeaveMACAddress is the key for the mac address of the container on the
// weave network, to be found in container node metadata
WeaveMACAddress = "weave_mac_address"
)

var weavePsMatch = regexp.MustCompile(`^([0-9a-f]{12}) ((?:[0-9a-f][0-9a-f]\:){5}(?:[0-9a-f][0-9a-f]))(.*)$`)
var ipMatch = regexp.MustCompile(`([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})(/[0-9]+)`)

// Cmd is a hook for mocking
type Cmd interface {
StdoutPipe() (io.ReadCloser, error)
Start() error
Wait() error
}

// ExecCommand is a hook for mocking
var ExecCommand = func(name string, args ...string) Cmd { return exec.Command(name, args...) }

// Weave represents a single Weave router, presumably on the same host
// as the probe. It is both a Reporter and a Tagger: it produces an Overlay
// topology, and (in theory) can tag existing topologies with foreign keys to
Expand Down Expand Up @@ -83,6 +106,59 @@ func (w Weave) update() (weaveStatus, error) {
return result, json.NewDecoder(resp.Body).Decode(&result)
}

type psEntry struct {
containerIDPrefix string
macAddress string
ips []string
}

func (w Weave) ps() ([]psEntry, error) {
var result []psEntry
cmd := ExecCommand("weave", "--local", "ps")
out, err := cmd.StdoutPipe()
if err != nil {
return result, err
}
if err := cmd.Start(); err != nil {
return result, err
}
defer func() {
if err := cmd.Wait(); err != nil {
log.Printf("Weave tagger, cmd failed: %v", err)
}
}()
scanner := bufio.NewScanner(out)
for scanner.Scan() {
line := scanner.Text()
groups := weavePsMatch.FindStringSubmatch(line)
if len(groups) == 0 {
continue
}
containerIDPrefix, macAddress, ips := groups[1], groups[2], []string{}
for _, ipGroup := range ipMatch.FindAllStringSubmatch(groups[3], -1) {
ips = append(ips, ipGroup[1])
}
result = append(result, psEntry{containerIDPrefix, macAddress, ips})
}
return result, scanner.Err()
}

func (w Weave) tagContainer(r report.Report, containerIDPrefix, macAddress string, ips []string) {
for nodeid, nmd := range r.Container.NodeMetadatas {
idPrefix := nmd.Metadata[docker.ContainerID][:12]
if idPrefix != containerIDPrefix {
continue
}

existingIPs := report.MakeIDList(strings.Fields(nmd.Metadata[docker.ContainerIPs])...)
existingIPs = existingIPs.Add(ips...)
nmd.Metadata[docker.ContainerIPs] = strings.Join(existingIPs, " ")
nmd.Metadata[WeaveMACAddress] = macAddress
r.Container.NodeMetadatas[nodeid] = nmd
break
}
}

// Tag implements Tagger.
func (w Weave) Tag(r report.Report) (report.Report, error) {
status, err := w.update()
Expand All @@ -105,6 +181,13 @@ func (w Weave) Tag(r report.Report) (report.Report, error) {
r.Container.NodeMetadatas[nodeID] = node
}

psEntries, err := w.ps()
if err != nil {
return r, nil
}
for _, e := range psEntries {
w.tagContainer(r, e.containerIDPrefix, e.macAddress, e.ips)
}
return r, nil
}

Expand Down
45 changes: 43 additions & 2 deletions probe/overlay/weave_test.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,52 @@
package overlay_test

import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
"testing"

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

type mockCmd struct {
*bytes.Buffer
}

func (c *mockCmd) Start() error {
return nil
}

func (c *mockCmd) Wait() error {
return nil
}

func (c *mockCmd) StdoutPipe() (io.ReadCloser, error) {
return struct {
io.Reader
io.Closer
}{
c.Buffer,
ioutil.NopCloser(nil),
}, nil
}

func TestWeaveTaggerOverlayTopology(t *testing.T) {
oldExecCmd := overlay.ExecCommand
defer func() { overlay.ExecCommand = oldExecCmd }()
overlay.ExecCommand = func(name string, args ...string) overlay.Cmd {
return &mockCmd{
bytes.NewBufferString(fmt.Sprintf("%s %s %s/24\n", mockContainerID, mockContainerMAC, mockContainerIP)),
}
}

s := httptest.NewServer(http.HandlerFunc(mockWeaveRouter))
defer s.Close()

Expand Down Expand Up @@ -46,15 +80,20 @@ func TestWeaveTaggerOverlayTopology(t *testing.T) {
Container: report.Topology{
NodeMetadatas: report.NodeMetadatas{
nodeID: report.MakeNodeMetadataWith(map[string]string{
docker.ContainerID: mockContainerID,
overlay.WeaveDNSHostname: mockHostname,
overlay.WeaveMACAddress: mockContainerMAC,
docker.ContainerIPs: mockContainerIP,
}),
},
},
}
have, err := w.Tag(report.Report{
Container: report.Topology{
NodeMetadatas: report.NodeMetadatas{
nodeID: report.MakeNodeMetadata(),
nodeID: report.MakeNodeMetadataWith(map[string]string{
docker.ContainerID: mockContainerID,
}),
},
},
})
Expand All @@ -71,7 +110,9 @@ const (
mockHostID = "host1"
mockWeavePeerName = "winnebago"
mockWeavePeerNickName = "winny"
mockContainerID = "a1b2c3d4e5"
mockContainerID = "83183a667c01"
mockContainerMAC = "d6:f2:5a:12:36:a8"
mockContainerIP = "10.0.0.123"
mockHostname = "hostname.weave.local"
)

Expand Down
4 changes: 3 additions & 1 deletion render/detailed_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,13 @@ func containerOriginTable(nmd report.NodeMetadata, addHostTag bool) (Table, bool
rows := []Row{}
for _, tuple := range []struct{ key, human string }{
{docker.ContainerID, "ID"},
{overlay.WeaveDNSHostname, "Weave DNS Hostname"},
{docker.ImageID, "Image ID"},
{docker.ContainerPorts, "Ports"},
{docker.ContainerCreated, "Created"},
{docker.ContainerCommand, "Command"},
{docker.ContainerIPs, "IP Addresses"},
{overlay.WeaveMACAddress, "Weave MAC"},
{overlay.WeaveDNSHostname, "Weave DNS Hostname"},
} {
if val, ok := nmd.Metadata[tuple.key]; ok {
rows = append(rows, Row{Key: tuple.human, ValueMajor: val, ValueMinor: ""})
Expand Down

0 comments on commit b86cbaa

Please sign in to comment.