Skip to content

Commit

Permalink
Merge pull request #3207 from weaveworks/fetch-namespace-ips
Browse files Browse the repository at this point in the history
Fetch container IPs directly from the namespace instead of calling 'weave ps'
  • Loading branch information
bboreham authored Jun 4, 2018
2 parents d7d3244 + 20ce708 commit d58a4df
Show file tree
Hide file tree
Showing 223 changed files with 42,648 additions and 7,092 deletions.
2 changes: 1 addition & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.9.2-stretch
FROM golang:1.10.2-stretch
ENV SCOPE_SKIP_UI_ASSETS true
RUN apt-get update && \
apt-get install -y libpcap-dev python-requests time file shellcheck git gcc-arm-linux-gnueabihf curl build-essential python-pip && \
Expand Down
57 changes: 1 addition & 56 deletions common/weave/client.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package weave

import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
Expand All @@ -24,8 +22,7 @@ const dockerAPIVersion = "1.22" // Support Docker Engine >= 1.10
type Client interface {
Status() (Status, error)
AddDNSEntry(fqdn, containerid string, ip net.IP) error
PS() (map[string]PSEntry, error) // on the interface for mocking
Expose() error // on the interface for mocking
Expose() error // on the interface for mocking
}

// Status describes whats happen in the Weave Net router.
Expand Down Expand Up @@ -107,16 +104,8 @@ type Plugin struct {
DriverName string
}

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]+)`)

// PSEntry is a row from the output of `weave ps`
type PSEntry struct {
ContainerIDPrefix string
MACAddress string
IPs []string
}

type client struct {
url string
}
Expand Down Expand Up @@ -176,50 +165,6 @@ func (c *client) AddDNSEntry(fqdn, containerID string, ip net.IP) error {
return nil
}

func (c *client) PS() (map[string]PSEntry, error) {
cmd := weaveCommand("--local", "ps")
stdOut, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
stdErr, err := cmd.StderrPipe()
if err != nil {
return nil, err
}
if err := cmd.Start(); err != nil {
return nil, err
}

psEntriesByPrefix := map[string]PSEntry{}
scanner := bufio.NewScanner(stdOut)
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])
}
psEntriesByPrefix[containerIDPrefix] = PSEntry{
ContainerIDPrefix: containerIDPrefix,
MACAddress: macAddress,
IPs: ips,
}
}
scannerErr := scanner.Err()
slurp, _ := ioutil.ReadAll(stdErr)
cmdErr := cmd.Wait()
if cmdErr != nil {
return nil, errorf("%s: %q", cmdErr, slurp)
}
if scannerErr != nil {
return nil, scannerErr
}
return psEntriesByPrefix, nil
}

func (c *client) Expose() error {
cmd := weaveCommand("--local", "ps", "weave:expose")
output, err := cmd.Output()
Expand Down
25 changes: 0 additions & 25 deletions common/weave/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,31 +139,6 @@ func TestDNSAdd(t *testing.T) {
}
}

func TestPS(t *testing.T) {
oldExecCmd := exec.Command
defer func() { exec.Command = oldExecCmd }()
exec.Command = func(name string, args ...string) exec.Cmd {
return testExec.NewMockCmdString(fmt.Sprintf("%s %s %s/24\n", mockContainerID, mockContainerMAC, mockContainerIP))
}

client := weave.NewClient("")
entries, err := client.PS()
if err != nil {
t.Fatal(err)
}

want := map[string]weave.PSEntry{
mockContainerID: {
ContainerIDPrefix: mockContainerID,
MACAddress: mockContainerMAC,
IPs: []string{mockContainerIP},
},
}
if !reflect.DeepEqual(entries, want) {
t.Fatal(test.Diff(entries, want))
}
}

func TestExpose(t *testing.T) {
oldExecCmd := exec.Command
defer func() { exec.Command = oldExecCmd }()
Expand Down
11 changes: 11 additions & 0 deletions probe/docker/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,17 @@ func (c *container) NetworkInfo(localAddrs []net.IP) report.Sets {
ips = append(ips, c.container.NetworkSettings.IPAddress)
}

// Fetch IP addresses from the container's namespace
cidrs, err := namespaceIPAddresses(c.container.State.Pid)
if err != nil {
log.Debugf("container %s: failed to get addresses: %s", c.container.ID, err)
}
for _, cidr := range cidrs {
// This address can duplicate an address fetched from Docker earlier,
// but we eventually turn the lists into sets which will remove duplicates.
ips = append(ips, cidr.IP.String())
}

// For now, for the proof-of-concept, we just add networks as a set of
// names. For the next iteration, we will probably want to create a new
// Network topology, populate the network nodes with all of the details
Expand Down
85 changes: 85 additions & 0 deletions probe/docker/network_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// withNetNS function requires a fix that first appeared in Go version 1.10
// +build go1.10

package docker

import (
"fmt"
"net"
"runtime"

"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
"golang.org/x/sys/unix"
)

// Code adapted from github.com/weaveworks/weave/net/netdev.go

// Return any non-local IP addresses for processID if in a non-root namespace
func namespaceIPAddresses(processID int) ([]*net.IPNet, error) {
// Ignore if this process is running in the root namespace
netnsRoot, err := netns.GetFromPid(1)
if err != nil {
return nil, fmt.Errorf("unable to open root namespace: %s", err)
}
defer netnsRoot.Close()
netnsContainer, err := netns.GetFromPid(processID)
if err != nil {
return nil, err
}
defer netnsContainer.Close()
if netnsRoot.Equal(netnsContainer) {
return nil, nil
}

var cidrs []*net.IPNet
err = withNetNS(netnsContainer, func() error {
cidrs, err = allNonLocalAddresses()
return err
})

return cidrs, err
}

// return all non-local IP addresses from the current namespace
func allNonLocalAddresses() ([]*net.IPNet, error) {
var cidrs []*net.IPNet
links, err := netlink.LinkList()
if err != nil {
return nil, err
}

for _, link := range links {
addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL)
if err != nil {
return nil, err
}
for _, addr := range addrs {
// Exclude link-local ipv6 addresses, localhost, etc. Hope this is the correct test.
if addr.Scope == unix.RT_SCOPE_UNIVERSE {
cidrs = append(cidrs, addr.IPNet)
}
}
}
return cidrs, nil
}

// Run the 'work' function in a different network namespace
func withNetNS(ns netns.NsHandle, work func() error) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()

oldNs, err := netns.Get()
if err == nil {
defer oldNs.Close()

err = netns.Set(ns)
if err == nil {
defer netns.Set(oldNs)

err = work()
}
}

return err
}
12 changes: 12 additions & 0 deletions probe/docker/network_others.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// +build !linux

package docker

import (
"errors"
"net"
)

func namespaceIPAddresses(processID int) ([]*net.IPNet, error) {
return nil, errors.New("namespaceIPAddresses not implemented on this platform")
}
41 changes: 3 additions & 38 deletions probe/overlay/weave.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ type Weave struct {

mtx sync.RWMutex
statusCache weave.Status
psCache map[string]weave.PSEntry

backoff backoff.Interface
psBackoff backoff.Interface
Expand All @@ -160,43 +159,23 @@ type Weave struct {
// address. The address should be an IP or FQDN, no port.
func NewWeave(hostID string, client weave.Client) (*Weave, error) {
w := &Weave{
client: client,
hostID: hostID,
psCache: map[string]weave.PSEntry{},
client: client,
hostID: hostID,
}

w.backoff = backoff.New(w.status, "collecting weave status")
w.backoff.SetInitialBackoff(5 * time.Second)
go w.backoff.Start()

w.psBackoff = backoff.New(w.ps, "collecting weave ps")
w.psBackoff.SetInitialBackoff(10 * time.Second)
go w.psBackoff.Start()

return w, nil
}

// Name of this reporter/tagger/ticker, for metrics gathering
func (*Weave) Name() string { return "Weave" }

// Stop gathering weave ps output.
// Stop gathering weave status.
func (w *Weave) Stop() {
w.backoff.Stop()
w.psBackoff.Stop()
}

func (w *Weave) ps() (bool, error) {
psEntriesByPrefix, err := w.client.PS()

w.mtx.Lock()
defer w.mtx.Unlock()

if err != nil {
w.psCache = map[string]weave.PSEntry{}
} else {
w.psCache = psEntriesByPrefix
}
return false, err
}

func (w *Weave) status() (bool, error) {
Expand Down Expand Up @@ -246,20 +225,6 @@ func (w *Weave) Tag(r report.Report) (report.Report, error) {
if len(prefix) > maxPrefixSize {
prefix = prefix[:maxPrefixSize]
}
entry, ok := w.psCache[prefix]
if !ok {
continue
}

ipsWithScope := report.MakeStringSet()
for _, ip := range entry.IPs {
ipsWithScope = ipsWithScope.Add(report.MakeAddressNodeID("", ip))
}
node = node.WithSet(docker.ContainerIPs, report.MakeStringSet(entry.IPs...))
node = node.WithSet(docker.ContainerIPsWithScopes, ipsWithScope)
node = node.WithLatests(map[string]string{
WeaveMACAddress: entry.MACAddress,
})
r.Container.Nodes[id] = node
}
return r, nil
Expand Down
15 changes: 1 addition & 14 deletions probe/overlay/weave_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import (
)

const (
mockHostID = "host1"
mockContainerIPWithScope = ";" + weave.MockContainerIP
mockHostID = "host1"
)

func runTest(t *testing.T, f func(*overlay.Weave)) {
Expand Down Expand Up @@ -59,18 +58,6 @@ func TestContainerTopologyTagging(t *testing.T) {
if have, ok := node.Latest.Lookup(overlay.WeaveDNSHostname); !ok || have != weave.MockHostname {
t.Errorf("Expected weave dns hostname %q, got %q", weave.MockHostname, have)
}
// Should have Weave MAC Address
if have, ok := node.Latest.Lookup(overlay.WeaveMACAddress); !ok || have != weave.MockContainerMAC {
t.Errorf("Expected weave mac address %q, got %q", weave.MockContainerMAC, have)
}
// Should have Weave container ip
if have, ok := node.Sets.Lookup(docker.ContainerIPs); !ok || !have.Contains(weave.MockContainerIP) {
t.Errorf("Expected container ips to include the weave IP %q, got %q", weave.MockContainerIP, have)
}
// Should have Weave container ip (with scope)
if have, ok := node.Sets.Lookup(docker.ContainerIPsWithScopes); !ok || !have.Contains(mockContainerIPWithScope) {
t.Errorf("Expected container ips to include the weave IP (with scope) %q, got %q", mockContainerIPWithScope, have)
}
}

runTest(t, test)
Expand Down
13 changes: 0 additions & 13 deletions test/weave/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ const (
MockWeavePeerNickName = "winny"
MockWeaveDefaultSubnet = "10.32.0.1/12"
MockContainerID = "83183a667c01"
MockContainerMAC = "d6:f2:5a:12:36:a8"
MockContainerIP = "10.0.0.123"
MockHostname = "hostname.weave.local"
MockProxyAddress = "unix:///foo/bar/weave.sock"
MockDriverName = "weave_mock"
Expand Down Expand Up @@ -70,17 +68,6 @@ func (MockClient) AddDNSEntry(fqdn, containerid string, ip net.IP) error {
return nil
}

// PS implements weave.Client
func (MockClient) PS() (map[string]weave.PSEntry, error) {
return map[string]weave.PSEntry{
MockContainerID: {
ContainerIDPrefix: MockContainerID,
MACAddress: MockContainerMAC,
IPs: []string{MockContainerIP},
},
}, nil
}

// Expose implements weave.Client
func (MockClient) Expose() error {
return nil
Expand Down
Loading

0 comments on commit d58a4df

Please sign in to comment.