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

Gather Weave Net plugin and proxy info from report #2719

Merged
merged 1 commit into from
Jul 18, 2017
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
12 changes: 12 additions & 0 deletions common/weave/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type Status struct {
Router Router
DNS *DNS
IPAM *IPAM
Proxy *Proxy
Plugin *Plugin
}

// Router describes the status of the Weave Router
Expand Down Expand Up @@ -95,6 +97,16 @@ type IPAM struct {
PendingAllocates []string
}

// Proxy describes the status of Weave Proxy
type Proxy struct {
Addresses []string
}

// Plugin describes the status of the Weave Plugin
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]+)`)

Expand Down
20 changes: 18 additions & 2 deletions common/weave/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const (
mockContainerIP = "10.0.0.123"
mockContainerIPWithScope = ";10.0.0.123"
mockHostname = "hostname.weave.local"
mockProxyAddress = "unix:///foo/bar/weave.sock"
mockDriverName = "weave_mock"
)

var (
Expand All @@ -41,8 +43,16 @@ var (
"Hostname": "%s.",
"Tombstone": 0
}]
}
}`, mockWeavePeerName, mockWeavePeerNickName, mockContainerID, mockHostname)
},
"Proxy": {
"Addresses": [
"%s"
]
},
"Plugin": {
"DriverName": "%s"
}
}`, mockWeavePeerName, mockWeavePeerNickName, mockContainerID, mockHostname, mockProxyAddress, mockDriverName)
mockIP = net.ParseIP("1.2.3.4")
)

Expand Down Expand Up @@ -84,6 +94,12 @@ func TestStatus(t *testing.T) {
},
},
},
Proxy: &weave.Proxy{
Addresses: []string{mockProxyAddress},
},
Plugin: &weave.Plugin{
DriverName: mockDriverName,
},
}
if !reflect.DeepEqual(status, want) {
t.Fatal(test.Diff(status, want))
Expand Down
145 changes: 19 additions & 126 deletions probe/overlay/weave.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package overlay

import (
"bytes"
"fmt"
"regexp"
"strings"
Expand All @@ -13,8 +12,6 @@ import (
"github.com/weaveworks/scope/probe/docker"
"github.com/weaveworks/scope/probe/host"
"github.com/weaveworks/scope/report"

docker_client "github.com/fsouza/go-dockerclient"
)

// Keys for use in Node
Expand Down Expand Up @@ -54,9 +51,6 @@ const (
)

var (
// NewDockerClientStub is used for testing
NewDockerClientStub = newDockerClient

containerNotRunningRE = regexp.MustCompile(`Container .* is not running\n`)

containerMetadata = report.MetadataTemplates{
Expand Down Expand Up @@ -145,52 +139,30 @@ var (
}
)

// DockerClient is used for testing
type DockerClient interface {
CreateExec(docker_client.CreateExecOptions) (*docker_client.Exec, error)
StartExec(string, docker_client.StartExecOptions) error
InspectContainer(id string) (*docker_client.Container, error)
}

func newDockerClient() (DockerClient, error) {
return docker_client.NewClientFromEnv()
}

// 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
// overlay -- though I'm not sure what that would look like in practice right
// now.
type Weave struct {
client weave.Client
dockerClient DockerClient
hostID string

mtx sync.RWMutex
statusCache weave.Status
psCache map[string]weave.PSEntry
proxyRunningCache bool
proxyAddressCache string
pluginRunningCache bool

backoff backoff.Interface
psBackoff backoff.Interface
proxyBackoff backoff.Interface
pluginBackoff backoff.Interface
client weave.Client
hostID string

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

backoff backoff.Interface
psBackoff backoff.Interface
}

// NewWeave returns a new Weave tagger based on the Weave router at
// address. The address should be an IP or FQDN, no port.
func NewWeave(hostID string, client weave.Client) (*Weave, error) {
dockerClient, err := NewDockerClientStub()
if err != nil {
return nil, err
}
w := &Weave{
client: client,
dockerClient: dockerClient,
hostID: hostID,
psCache: map[string]weave.PSEntry{},
client: client,
hostID: hostID,
psCache: map[string]weave.PSEntry{},
}

w.backoff = backoff.New(w.status, "collecting weave status")
Expand All @@ -201,14 +173,6 @@ func NewWeave(hostID string, client weave.Client) (*Weave, error) {
w.psBackoff.SetInitialBackoff(10 * time.Second)
go w.psBackoff.Start()

w.proxyBackoff = backoff.New(w.proxyStatus, "collecting weave proxy status")
w.proxyBackoff.SetInitialBackoff(10 * time.Second)
go w.proxyBackoff.Start()

w.pluginBackoff = backoff.New(w.pluginStatus, "collecting weave plugin status")
w.pluginBackoff.SetInitialBackoff(10 * time.Second)
go w.pluginBackoff.Start()

return w, nil
}

Expand All @@ -219,8 +183,6 @@ func (*Weave) Name() string { return "Weave" }
func (w *Weave) Stop() {
w.backoff.Stop()
w.psBackoff.Stop()
w.proxyBackoff.Stop()
w.pluginBackoff.Stop()
}

func (w *Weave) ps() (bool, error) {
Expand Down Expand Up @@ -251,78 +213,6 @@ func (w *Weave) status() (bool, error) {
return false, err
}

func filterContainerNotFound(err error) error {
if err == nil {
return nil
}

switch err.(type) {
case *docker_client.Error:
// This is really ugly, but this error comes from the client in some cases
// and there is no other way to distinguish it :(
dockerError := err.(*docker_client.Error)
if containerNotRunningRE.MatchString(dockerError.Message) {
return nil
}
case *docker_client.ContainerNotRunning:
return nil
case *docker_client.NoSuchContainer:
return nil
}

return err
}

func (w *Weave) proxyStatus() (bool, error) {
update := func(running bool, address string) {
w.mtx.Lock()
defer w.mtx.Unlock()
w.proxyRunningCache = running
w.proxyAddressCache = address
}

exec, err := w.dockerClient.CreateExec(docker_client.CreateExecOptions{
AttachStdout: true,
Cmd: []string{"curl", "-s", "--unix-socket", "status.sock", "http:/status"},
Container: "weaveproxy",
})
if err != nil {
update(false, "")
return false, filterContainerNotFound(err)
}
out := bytes.NewBuffer(nil)
err = w.dockerClient.StartExec(exec.ID, docker_client.StartExecOptions{
OutputStream: out,
})
if err != nil {
update(true, "")
return false, filterContainerNotFound(err)
}

update(true, out.String())

return false, nil
}

func (w *Weave) pluginStatus() (bool, error) {
update := func(running bool) {
w.mtx.Lock()
defer w.mtx.Unlock()

w.pluginRunningCache = running
}

c, err := w.dockerClient.InspectContainer("weaveplugin")
if err != nil {
update(false)
return false, filterContainerNotFound(err)
}

update(c.State.Running)

return false, nil
}

// Tag implements Tagger.
func (w *Weave) Tag(r report.Report) (report.Report, error) {
w.mtx.RLock()
Expand Down Expand Up @@ -462,14 +352,17 @@ func (w *Weave) addCurrentPeerInfo(latests map[string]string, node report.Node)
latests[WeaveDNSEntryCount] = fmt.Sprintf("%d", dnsEntryCount)
}
latests[WeaveProxyStatus] = "not running"
if w.proxyRunningCache {
if w.statusCache.Proxy != nil {
latests[WeaveProxyStatus] = "running"
latests[WeaveProxyAddress] = w.proxyAddressCache
latests[WeaveProxyAddress] = ""
if len(w.statusCache.Proxy.Addresses) > 0 {
latests[WeaveProxyAddress] = w.statusCache.Proxy.Addresses[0]
}
}
latests[WeavePluginStatus] = "not running"
if w.pluginRunningCache {
if w.statusCache.Plugin != nil {
latests[WeavePluginStatus] = "running"
latests[WeavePluginDriver] = "weave"
latests[WeavePluginDriver] = w.statusCache.Plugin.DriverName
}
node = node.AddPrefixMulticolumnTable(WeaveConnectionsMulticolumnTablePrefix, getConnectionsTable(w.statusCache.Router))
node = node.WithParents(report.MakeSets().Add(report.Host, report.MakeStringSet(w.hostID)))
Expand Down
58 changes: 5 additions & 53 deletions probe/overlay/weave_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package overlay_test

import (
"sync"
"testing"
"time"

Expand All @@ -12,63 +11,14 @@ import (
"github.com/weaveworks/scope/test"
"github.com/weaveworks/scope/test/reflect"
"github.com/weaveworks/scope/test/weave"

docker_client "github.com/fsouza/go-dockerclient"
)

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

type mockDockerClient struct {
sync.RWMutex
containers map[string]*docker_client.Container
}

func (m *mockDockerClient) InspectContainer(id string) (*docker_client.Container, error) {
m.RLock()
defer m.RUnlock()
c, ok := m.containers[id]
if !ok {
return nil, &docker_client.NoSuchContainer{}
}
return c, nil
}

func (m *mockDockerClient) CreateExec(docker_client.CreateExecOptions) (*docker_client.Exec, error) {
return &docker_client.Exec{ID: "id"}, nil
}

func (m *mockDockerClient) StartExec(_ string, options docker_client.StartExecOptions) error {
options.OutputStream.Write([]byte("exec_output"))
return nil
}

var mockWeaveContainers = map[string]*docker_client.Container{
"weaveproxy": {
ID: "foo",
State: docker_client.State{
Running: true,
},
},

"weaveplugin": {
ID: "bar",
State: docker_client.State{
Running: true,
},
},
}

func runTest(t *testing.T, f func(*overlay.Weave)) {
// Place and restore docker client
origNewDockerClientStub := overlay.NewDockerClientStub
overlay.NewDockerClientStub = func() (overlay.DockerClient, error) {
return &mockDockerClient{containers: mockWeaveContainers}, nil
}
defer func() { overlay.NewDockerClientStub = origNewDockerClientStub }()

w, err := overlay.NewWeave(mockHostID, weave.MockClient{})
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -152,14 +102,16 @@ func TestOverlayTopology(t *testing.T) {
if have, ok := node.Latest.Lookup(overlay.WeaveProxyStatus); !ok || have != "running" {
t.Errorf("Expected weave proxy status %q, got %q", "running", have)
}
// The weave proxy address should equal what Exec writes to stdout
if have, ok := node.Latest.Lookup(overlay.WeaveProxyAddress); !ok || have != "exec_output" {
t.Errorf("Expected weave proxy address %q, got %q", "exec_output", have)
if have, ok := node.Latest.Lookup(overlay.WeaveProxyAddress); !ok || have != weave.MockProxyAddress {
t.Errorf("Expected weave proxy address %q, got %q", weave.MockProxyAddress, have)
}
// The weave plugin container is running
if have, ok := node.Latest.Lookup(overlay.WeavePluginStatus); !ok || have != "running" {
t.Errorf("Expected weave plugin status %q, got %q", "running", have)
}
if have, ok := node.Latest.Lookup(overlay.WeavePluginDriver); !ok || have != weave.MockDriverName {
t.Errorf("Expected weave proxy address %q, got %q", weave.MockDriverName, have)
}
// The mock data indicates ranges are owned by unreachable peers
if have, ok := node.Latest.Lookup(overlay.WeaveIPAMStatus); !ok || have != "all ranges owned by unreachable peers" {
t.Errorf("Expected weave IPAM status %q, got %q", "all ranges owned by unreachable peers", have)
Expand Down
8 changes: 8 additions & 0 deletions test/weave/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const (
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"
)

// MockClient is a mock version of weave.Client
Expand Down Expand Up @@ -54,6 +56,12 @@ func (MockClient) Status() (weave.Status, error) {
{Size: 0, IsKnownPeer: false},
},
},
Proxy: &weave.Proxy{
Addresses: []string{MockProxyAddress},
},
Plugin: &weave.Plugin{
DriverName: MockDriverName,
},
}, nil
}

Expand Down