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

Tag containers with Weave and Docker IPs #396

Merged
merged 2 commits into from
Aug 26, 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: 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
8 changes: 8 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,8 @@ 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: strings.Join(append(c.container.NetworkSettings.SecondaryIPAddresses,
c.container.NetworkSettings.IPAddress), " "),
})

if c.latestStats == nil {
Expand Down Expand Up @@ -241,3 +244,8 @@ func (c *container) GetNodeMetadata() report.NodeMetadata {
}))
return result
}

// ExtractContainerIPs returns the list of container IPs given a NodeMetadata from the Container topology.
func ExtractContainerIPs(nmd report.NodeMetadata) []string {
return strings.Fields(nmd.Metadata[ContainerIPs])
}
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]))(.*)$`)

This comment was marked as abuse.

This comment was marked as abuse.

This comment was marked as abuse.

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(docker.ExtractContainerIPs(nmd)...)
existingIPs = existingIPs.Add(ips...)

This comment was marked as abuse.

This comment was marked as abuse.

This comment was marked as abuse.

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
9 changes: 7 additions & 2 deletions render/detailed_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,17 +297,22 @@ 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"},
{overlay.WeaveMACAddress, "Weave MAC"},
{overlay.WeaveDNSHostname, "Weave DNS Hostname"},
} {
if val, ok := nmd.Metadata[tuple.key]; ok {
if val, ok := nmd.Metadata[tuple.key]; ok && val != "" {
rows = append(rows, Row{Key: tuple.human, ValueMajor: val, ValueMinor: ""})
}
}

for _, ip := range docker.ExtractContainerIPs(nmd) {
rows = append(rows, Row{Key: "IP Address", ValueMajor: ip, ValueMinor: ""})
}

if val, ok := nmd.Metadata[docker.MemoryUsage]; ok {
memory, err := strconv.ParseFloat(val, 64)
if err == nil {
Expand Down