mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-15 19:41:02 +00:00
Merge pull request #1973 from weaveworks/1938-enrich-weave-details-panel
Extend metadata in details panel for Weave Net nodes
This commit is contained in:
@@ -27,29 +27,47 @@ type Client interface {
|
||||
type Status struct {
|
||||
Version string
|
||||
Router Router
|
||||
DNS DNS
|
||||
IPAM IPAM
|
||||
DNS *DNS
|
||||
IPAM *IPAM
|
||||
}
|
||||
|
||||
// Router describes the status of the Weave Router
|
||||
type Router struct {
|
||||
Name string
|
||||
Peers []struct {
|
||||
Name string
|
||||
Encryption bool
|
||||
ProtocolMinVersion int
|
||||
ProtocolMaxVersion int
|
||||
PeerDiscovery bool
|
||||
Peers []Peer
|
||||
Connections []struct {
|
||||
Address string
|
||||
Outbound bool
|
||||
State string
|
||||
Info string
|
||||
}
|
||||
Targets []string
|
||||
TrustedSubnets []string
|
||||
}
|
||||
|
||||
// Peer describes a peer in the weave network
|
||||
type Peer struct {
|
||||
Name string
|
||||
NickName string
|
||||
Connections []struct {
|
||||
Name string
|
||||
NickName string
|
||||
Connections []struct {
|
||||
Name string
|
||||
NickName string
|
||||
Address string
|
||||
Outbound bool
|
||||
Established bool
|
||||
}
|
||||
Address string
|
||||
Outbound bool
|
||||
Established bool
|
||||
}
|
||||
}
|
||||
|
||||
// DNS describes the status of Weave DNS
|
||||
type DNS struct {
|
||||
Entries []struct {
|
||||
Domain string
|
||||
Upstream []string
|
||||
TTL uint32
|
||||
Entries []struct {
|
||||
Hostname string
|
||||
ContainerID string
|
||||
Tombstone int64
|
||||
@@ -58,7 +76,18 @@ type DNS struct {
|
||||
|
||||
// IPAM describes the status of Weave IPAM
|
||||
type IPAM struct {
|
||||
Paxos *struct {
|
||||
Elector bool
|
||||
KnownNodes int
|
||||
Quorum uint
|
||||
}
|
||||
Range string
|
||||
DefaultSubnet string
|
||||
Entries []struct {
|
||||
Size uint32
|
||||
IsKnownPeer bool
|
||||
}
|
||||
PendingAllocates []string
|
||||
}
|
||||
|
||||
var weavePsMatch = regexp.MustCompile(`^([0-9a-f]{12}) ((?:[0-9a-f][0-9a-f]\:){5}(?:[0-9a-f][0-9a-f]))(.*)$`)
|
||||
|
||||
@@ -64,24 +64,14 @@ func TestStatus(t *testing.T) {
|
||||
|
||||
want := weave.Status{
|
||||
Router: weave.Router{
|
||||
Peers: []struct {
|
||||
Name string
|
||||
NickName string
|
||||
Connections []struct {
|
||||
Name string
|
||||
NickName string
|
||||
Address string
|
||||
Outbound bool
|
||||
Established bool
|
||||
}
|
||||
}{
|
||||
Peers: []weave.Peer{
|
||||
{
|
||||
Name: mockWeavePeerName,
|
||||
NickName: mockWeavePeerNickName,
|
||||
},
|
||||
},
|
||||
},
|
||||
DNS: weave.DNS{
|
||||
DNS: &weave.DNS{
|
||||
Entries: []struct {
|
||||
Hostname string
|
||||
ContainerID string
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestControls(t *testing.T) {
|
||||
mdc := newMockClient()
|
||||
setupStubs(mdc, func() {
|
||||
hr := controls.NewDefaultHandlerRegistry()
|
||||
registry, _ := docker.NewRegistry(10*time.Second, nil, false, "", hr)
|
||||
registry, _ := docker.NewRegistry(10*time.Second, nil, false, "", hr, "")
|
||||
defer registry.Stop()
|
||||
|
||||
for _, tc := range []struct{ command, result string }{
|
||||
@@ -58,7 +58,7 @@ func TestPipes(t *testing.T) {
|
||||
mdc := newMockClient()
|
||||
setupStubs(mdc, func() {
|
||||
hr := controls.NewDefaultHandlerRegistry()
|
||||
registry, _ := docker.NewRegistry(10*time.Second, nil, false, "", hr)
|
||||
registry, _ := docker.NewRegistry(10*time.Second, nil, false, "", hr, "")
|
||||
defer registry.Stop()
|
||||
|
||||
test.Poll(t, 100*time.Millisecond, true, func() interface{} {
|
||||
|
||||
@@ -25,7 +25,6 @@ const (
|
||||
UnpauseEvent = "unpause"
|
||||
NetworkConnectEvent = "network:connect"
|
||||
NetworkDisconnectEvent = "network:disconnect"
|
||||
endpoint = "unix:///var/run/docker.sock"
|
||||
)
|
||||
|
||||
// Vars exported for testing.
|
||||
@@ -95,8 +94,8 @@ func newDockerClient(endpoint string) (Client, error) {
|
||||
}
|
||||
|
||||
// NewRegistry returns a usable Registry. Don't forget to Stop it.
|
||||
func NewRegistry(interval time.Duration, pipes controls.PipeClient, collectStats bool, hostID string, handlerRegistry *controls.HandlerRegistry) (Registry, error) {
|
||||
client, err := NewDockerClientStub(endpoint)
|
||||
func NewRegistry(interval time.Duration, pipes controls.PipeClient, collectStats bool, hostID string, handlerRegistry *controls.HandlerRegistry, dockerEndpoint string) (Registry, error) {
|
||||
client, err := NewDockerClientStub(dockerEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
|
||||
func testRegistry() docker.Registry {
|
||||
hr := controls.NewDefaultHandlerRegistry()
|
||||
registry, _ := docker.NewRegistry(10*time.Second, nil, true, "", hr)
|
||||
registry, _ := docker.NewRegistry(10*time.Second, nil, true, "", hr, "")
|
||||
return registry
|
||||
}
|
||||
|
||||
|
||||
@@ -1,61 +1,153 @@
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/scope/common/backoff"
|
||||
"github.com/weaveworks/scope/common/mtime"
|
||||
"github.com/weaveworks/scope/common/weave"
|
||||
"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
|
||||
const (
|
||||
// WeavePeerName is the key for the peer name, typically a MAC address.
|
||||
WeavePeerName = "weave_peer_name"
|
||||
|
||||
// WeavePeerNickName is the key for the peer nickname, typically a
|
||||
// hostname.
|
||||
WeavePeerNickName = "weave_peer_nick_name"
|
||||
|
||||
// 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"
|
||||
|
||||
// WeaveVersion is the key for the weave version running on the peer
|
||||
WeaveVersion = "weave_version"
|
||||
WeavePeerName = "weave_peer_name"
|
||||
WeavePeerNickName = "weave_peer_nick_name"
|
||||
WeaveDNSHostname = "weave_dns_hostname"
|
||||
WeaveMACAddress = "weave_mac_address"
|
||||
WeaveVersion = "weave_version"
|
||||
WeaveEncryption = "weave_encryption"
|
||||
WeaveProtocol = "weave_protocol"
|
||||
WeavePeerDiscovery = "weave_peer_discovery"
|
||||
WeaveTargetCount = "weave_target_count"
|
||||
WeaveConnectionCount = "weave_connection_count"
|
||||
WeavePeerCount = "weave_peer_count"
|
||||
WeaveTrustedSubnets = "weave_trusted_subnet_count"
|
||||
WeaveIPAMTableID = "weave_ipam_table"
|
||||
WeaveIPAMStatus = "weave_ipam_status"
|
||||
WeaveIPAMRange = "weave_ipam_range"
|
||||
WeaveIPAMDefaultSubnet = "weave_ipam_default_subnet"
|
||||
WeaveDNSTableID = "weave_dns_table"
|
||||
WeaveDNSDomain = "weave_dns_domain"
|
||||
WeaveDNSUpstream = "weave_dns_upstream"
|
||||
WeaveDNSTTL = "weave_dns_ttl"
|
||||
WeaveDNSEntryCount = "weave_dns_entry_count"
|
||||
WeaveProxyTableID = "weave_proxy_table"
|
||||
WeaveProxyStatus = "weave_proxy_status"
|
||||
WeaveProxyAddress = "weave_proxy_address"
|
||||
WeavePluginTableID = "weave_plugin_table"
|
||||
WeavePluginStatus = "weave_plugin_status"
|
||||
WeavePluginDriver = "weave_plugin_driver"
|
||||
)
|
||||
|
||||
var (
|
||||
// NewDockerClientStub is used for testing
|
||||
NewDockerClientStub = newDockerClient
|
||||
|
||||
containerNotRunningRE = regexp.MustCompile(`Container .* is not running\n`)
|
||||
|
||||
containerMetadata = report.MetadataTemplates{
|
||||
WeaveMACAddress: {ID: WeaveMACAddress, Label: "Weave MAC", From: report.FromLatest, Priority: 17},
|
||||
WeaveDNSHostname: {ID: WeaveDNSHostname, Label: "Weave DNS Name", From: report.FromLatest, Priority: 18},
|
||||
}
|
||||
|
||||
weaveMetadata = report.MetadataTemplates{
|
||||
WeaveVersion: {ID: WeaveVersion, Label: "Version", From: report.FromLatest, Priority: 1},
|
||||
WeaveProtocol: {ID: WeaveProtocol, Label: "Protocol", From: report.FromLatest, Priority: 2},
|
||||
WeavePeerName: {ID: WeavePeerName, Label: "Name", From: report.FromLatest, Priority: 3},
|
||||
WeaveEncryption: {ID: WeaveEncryption, Label: "Encryption", From: report.FromLatest, Priority: 4},
|
||||
WeavePeerDiscovery: {ID: WeavePeerDiscovery, Label: "Peer Discovery", From: report.FromLatest, Priority: 5},
|
||||
WeaveTargetCount: {ID: WeaveTargetCount, Label: "Targets", From: report.FromLatest, Priority: 6},
|
||||
WeaveConnectionCount: {ID: WeaveConnectionCount, Label: "Connections", From: report.FromLatest, Priority: 8},
|
||||
WeavePeerCount: {ID: WeavePeerCount, Label: "Peers", From: report.FromLatest, Priority: 7},
|
||||
WeaveTrustedSubnets: {ID: WeaveTrustedSubnets, Label: "Trusted Subnets", From: report.FromSets, Priority: 9},
|
||||
}
|
||||
|
||||
weaveTableTemplates = report.TableTemplates{
|
||||
WeaveIPAMTableID: {ID: WeaveIPAMTableID, Label: "IPAM",
|
||||
FixedRows: map[string]string{
|
||||
WeaveIPAMStatus: "Status",
|
||||
WeaveIPAMRange: "Range",
|
||||
WeaveIPAMDefaultSubnet: "Default Subnet",
|
||||
},
|
||||
},
|
||||
WeaveDNSTableID: {ID: WeaveDNSTableID, Label: "DNS",
|
||||
FixedRows: map[string]string{
|
||||
WeaveDNSDomain: "Domain",
|
||||
WeaveDNSUpstream: "Upstream",
|
||||
WeaveDNSTTL: "TTL",
|
||||
WeaveDNSEntryCount: "Entries",
|
||||
},
|
||||
},
|
||||
WeaveProxyTableID: {ID: WeaveProxyTableID, Label: "Proxy",
|
||||
FixedRows: map[string]string{
|
||||
WeaveProxyStatus: "Status",
|
||||
WeaveProxyAddress: "Address",
|
||||
},
|
||||
},
|
||||
WeavePluginTableID: {ID: WeavePluginTableID, Label: "Plugin",
|
||||
FixedRows: map[string]string{
|
||||
WeavePluginStatus: "Status",
|
||||
WeavePluginDriver: "Driver Name",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// 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(endpoint string) (DockerClient, error) {
|
||||
return docker_client.NewClient(endpoint)
|
||||
}
|
||||
|
||||
// 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
|
||||
hostID string
|
||||
client weave.Client
|
||||
dockerClient DockerClient
|
||||
hostID string
|
||||
|
||||
mtx sync.RWMutex
|
||||
statusCache weave.Status
|
||||
psCache map[string]weave.PSEntry
|
||||
mtx sync.RWMutex
|
||||
statusCache weave.Status
|
||||
psCache map[string]weave.PSEntry
|
||||
proxyRunningCache bool
|
||||
proxyAddressCache string
|
||||
pluginRunningCache bool
|
||||
|
||||
backoff backoff.Interface
|
||||
psBackoff backoff.Interface
|
||||
backoff backoff.Interface
|
||||
psBackoff backoff.Interface
|
||||
proxyBackoff backoff.Interface
|
||||
pluginBackoff 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 {
|
||||
func NewWeave(hostID string, client weave.Client, dockerEndpoint string) (*Weave, error) {
|
||||
dockerClient, err := NewDockerClientStub(dockerEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w := &Weave{
|
||||
client: client,
|
||||
hostID: hostID,
|
||||
psCache: map[string]weave.PSEntry{},
|
||||
client: client,
|
||||
dockerClient: dockerClient,
|
||||
hostID: hostID,
|
||||
psCache: map[string]weave.PSEntry{},
|
||||
}
|
||||
|
||||
w.backoff = backoff.New(w.status, "collecting weave status")
|
||||
@@ -66,7 +158,15 @@ func NewWeave(hostID string, client weave.Client) *Weave {
|
||||
w.psBackoff.SetInitialBackoff(10 * time.Second)
|
||||
go w.psBackoff.Start()
|
||||
|
||||
return w
|
||||
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
|
||||
}
|
||||
|
||||
// Name of this reporter/tagger/ticker, for metrics gathering
|
||||
@@ -76,6 +176,8 @@ 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) {
|
||||
@@ -106,25 +208,99 @@ 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()
|
||||
defer w.mtx.RUnlock()
|
||||
|
||||
// Put information from weaveDNS on the container nodes
|
||||
for _, entry := range w.statusCache.DNS.Entries {
|
||||
if entry.Tombstone > 0 {
|
||||
continue
|
||||
if w.statusCache.DNS != nil {
|
||||
for _, entry := range w.statusCache.DNS.Entries {
|
||||
if entry.Tombstone > 0 {
|
||||
continue
|
||||
}
|
||||
nodeID := report.MakeContainerNodeID(entry.ContainerID)
|
||||
node, ok := r.Container.Nodes[nodeID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
w, _ := node.Latest.Lookup(WeaveDNSHostname)
|
||||
hostnames := report.IDList(strings.Fields(w))
|
||||
hostnames = hostnames.Add(strings.TrimSuffix(entry.Hostname, "."))
|
||||
r.Container.Nodes[nodeID] = node.WithLatests(map[string]string{WeaveDNSHostname: strings.Join(hostnames, " ")})
|
||||
}
|
||||
nodeID := report.MakeContainerNodeID(entry.ContainerID)
|
||||
node, ok := r.Container.Nodes[nodeID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
w, _ := node.Latest.Lookup(WeaveDNSHostname)
|
||||
hostnames := report.IDList(strings.Fields(w))
|
||||
hostnames = hostnames.Add(strings.TrimSuffix(entry.Hostname, "."))
|
||||
r.Container.Nodes[nodeID] = node.WithLatests(map[string]string{WeaveDNSHostname: strings.Join(hostnames, " ")})
|
||||
}
|
||||
|
||||
// Put information from weave ps on the container nodes
|
||||
@@ -162,43 +338,132 @@ func (w *Weave) Report() (report.Report, error) {
|
||||
defer w.mtx.RUnlock()
|
||||
|
||||
r := report.MakeReport()
|
||||
r.Container = r.Container.WithMetadataTemplates(report.MetadataTemplates{
|
||||
WeaveMACAddress: {ID: WeaveMACAddress, Label: "Weave MAC", From: report.FromLatest, Priority: 17},
|
||||
WeaveDNSHostname: {ID: WeaveDNSHostname, Label: "Weave DNS Name", From: report.FromLatest, Priority: 18},
|
||||
})
|
||||
r.Overlay = r.Overlay.WithMetadataTemplates(report.MetadataTemplates{
|
||||
WeavePeerName: {ID: WeavePeerName, Label: "Peer Name", From: report.FromLatest, Truncate: 17, Priority: 1},
|
||||
WeaveVersion: {ID: WeaveVersion, Label: "Version", From: report.FromLatest, Priority: 2},
|
||||
})
|
||||
r.Container = r.Container.WithMetadataTemplates(containerMetadata)
|
||||
r.Overlay = r.Overlay.WithMetadataTemplates(weaveMetadata).WithTableTemplates(weaveTableTemplates)
|
||||
|
||||
// We report nodes for all peers (not just the current node) to highlight peers not monitored by Scope
|
||||
// (i.e. without a running probe)
|
||||
// Note: this will cause redundant information (n^2) if all peers have a running probe
|
||||
for _, peer := range w.statusCache.Router.Peers {
|
||||
node := report.MakeNodeWith(report.MakeOverlayNodeID(report.WeaveOverlayPeerPrefix, peer.Name),
|
||||
map[string]string{
|
||||
WeavePeerName: peer.Name,
|
||||
WeavePeerNickName: peer.NickName,
|
||||
})
|
||||
// Peer corresponding to current host
|
||||
if peer.Name == w.statusCache.Router.Name {
|
||||
node = node.WithLatest(report.HostNodeID, mtime.Now(), w.hostID)
|
||||
node = node.WithLatest(WeaveVersion, mtime.Now(), w.statusCache.Version)
|
||||
node = node.WithParents(report.EmptySets.Add(report.Host, report.MakeStringSet(w.hostID)))
|
||||
}
|
||||
for _, conn := range peer.Connections {
|
||||
if conn.Outbound {
|
||||
node = node.WithAdjacent(report.MakeOverlayNodeID(report.WeaveOverlayPeerPrefix, conn.Name))
|
||||
}
|
||||
}
|
||||
node := w.getPeerNode(peer)
|
||||
r.Overlay.AddNode(node)
|
||||
}
|
||||
if w.statusCache.IPAM.DefaultSubnet != "" {
|
||||
if w.statusCache.IPAM != nil {
|
||||
r.Overlay.AddNode(
|
||||
report.MakeNode(report.MakeOverlayNodeID(report.WeaveOverlayPeerPrefix, w.statusCache.Router.Name)).WithSets(
|
||||
report.MakeSets().Add(host.LocalNetworks, report.MakeStringSet(w.statusCache.IPAM.DefaultSubnet)),
|
||||
),
|
||||
report.MakeNode(report.MakeOverlayNodeID(report.WeaveOverlayPeerPrefix, w.statusCache.Router.Name)).
|
||||
WithSet(host.LocalNetworks, report.MakeStringSet(w.statusCache.IPAM.DefaultSubnet)),
|
||||
)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// getPeerNode obtains an Overlay topology node for representing a peer in the Weave network
|
||||
func (w *Weave) getPeerNode(peer weave.Peer) report.Node {
|
||||
node := report.MakeNode(report.MakeOverlayNodeID(report.WeaveOverlayPeerPrefix, peer.Name))
|
||||
latests := map[string]string{
|
||||
WeavePeerName: peer.Name,
|
||||
WeavePeerNickName: peer.NickName,
|
||||
}
|
||||
|
||||
// Peer corresponding to current host
|
||||
if peer.Name == w.statusCache.Router.Name {
|
||||
latests, node = w.addCurrentPeerInfo(latests, node)
|
||||
}
|
||||
|
||||
for _, conn := range peer.Connections {
|
||||
if conn.Outbound {
|
||||
node = node.WithAdjacent(report.MakeOverlayNodeID(report.WeaveOverlayPeerPrefix, conn.Name))
|
||||
}
|
||||
}
|
||||
|
||||
return node.WithLatests(latests)
|
||||
}
|
||||
|
||||
// addCurrentPeerInfo adds information exclusive to the Overlay topology node representing current Weave Net peer
|
||||
// (i.e. in the same host as the reporting Scope probe)
|
||||
func (w *Weave) addCurrentPeerInfo(latests map[string]string, node report.Node) (map[string]string, report.Node) {
|
||||
latests[report.HostNodeID] = w.hostID
|
||||
latests[WeaveVersion] = w.statusCache.Version
|
||||
latests[WeaveEncryption] = "disabled"
|
||||
if w.statusCache.Router.Encryption {
|
||||
latests[WeaveEncryption] = "enabled"
|
||||
}
|
||||
latests[WeavePeerDiscovery] = "disabled"
|
||||
if w.statusCache.Router.PeerDiscovery {
|
||||
latests[WeavePeerDiscovery] = "enabled"
|
||||
}
|
||||
if w.statusCache.Router.ProtocolMinVersion == w.statusCache.Router.ProtocolMaxVersion {
|
||||
latests[WeaveProtocol] = fmt.Sprintf("%d", w.statusCache.Router.ProtocolMinVersion)
|
||||
} else {
|
||||
latests[WeaveProtocol] = fmt.Sprintf("%d..%d", w.statusCache.Router.ProtocolMinVersion, w.statusCache.Router.ProtocolMaxVersion)
|
||||
}
|
||||
latests[WeaveTargetCount] = fmt.Sprintf("%d", len(w.statusCache.Router.Targets))
|
||||
latests[WeaveConnectionCount] = fmt.Sprintf("%d", len(w.statusCache.Router.Connections))
|
||||
latests[WeavePeerCount] = fmt.Sprintf("%d", len(w.statusCache.Router.Peers))
|
||||
node = node.WithSet(WeaveTrustedSubnets, report.MakeStringSet(w.statusCache.Router.TrustedSubnets...))
|
||||
if w.statusCache.IPAM != nil {
|
||||
latests[WeaveIPAMStatus] = getIPAMStatus(*w.statusCache.IPAM)
|
||||
latests[WeaveIPAMRange] = w.statusCache.IPAM.Range
|
||||
latests[WeaveIPAMDefaultSubnet] = w.statusCache.IPAM.DefaultSubnet
|
||||
}
|
||||
if w.statusCache.DNS != nil {
|
||||
latests[WeaveDNSDomain] = w.statusCache.DNS.Domain
|
||||
latests[WeaveDNSUpstream] = strings.Join(w.statusCache.DNS.Upstream, ", ")
|
||||
latests[WeaveDNSTTL] = fmt.Sprintf("%d", w.statusCache.DNS.TTL)
|
||||
dnsEntryCount := 0
|
||||
for _, entry := range w.statusCache.DNS.Entries {
|
||||
if entry.Tombstone == 0 {
|
||||
dnsEntryCount++
|
||||
}
|
||||
}
|
||||
latests[WeaveDNSEntryCount] = fmt.Sprintf("%d", dnsEntryCount)
|
||||
}
|
||||
latests[WeaveProxyStatus] = "not running"
|
||||
if w.proxyRunningCache {
|
||||
latests[WeaveProxyStatus] = "running"
|
||||
latests[WeaveProxyAddress] = w.proxyAddressCache
|
||||
}
|
||||
latests[WeavePluginStatus] = "not running"
|
||||
if w.pluginRunningCache {
|
||||
latests[WeavePluginStatus] = "running"
|
||||
latests[WeavePluginDriver] = "weave"
|
||||
}
|
||||
node = node.WithParents(report.EmptySets.Add(report.Host, report.MakeStringSet(w.hostID)))
|
||||
|
||||
return latests, node
|
||||
}
|
||||
|
||||
func getIPAMStatus(ipam weave.IPAM) string {
|
||||
allIPAMOwnersUnreachable := func(ipam weave.IPAM) bool {
|
||||
for _, entry := range ipam.Entries {
|
||||
if entry.Size > 0 && entry.IsKnownPeer {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if len(ipam.Entries) > 0 {
|
||||
if allIPAMOwnersUnreachable(ipam) {
|
||||
return "all ranges owned by unreachable peers"
|
||||
} else if len(ipam.PendingAllocates) > 0 {
|
||||
return "waiting for grant"
|
||||
|
||||
} else {
|
||||
return "ready"
|
||||
}
|
||||
}
|
||||
|
||||
if ipam.Paxos != nil {
|
||||
if ipam.Paxos.Elector {
|
||||
return fmt.Sprintf(
|
||||
"awaiting consensus (quorum: %d, known: %d)",
|
||||
ipam.Paxos.Quorum,
|
||||
ipam.Paxos.KnownNodes,
|
||||
)
|
||||
}
|
||||
return "priming"
|
||||
}
|
||||
|
||||
return "idle"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package overlay_test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -11,6 +12,8 @@ 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 (
|
||||
@@ -18,8 +21,58 @@ const (
|
||||
mockContainerIPWithScope = ";" + weave.MockContainerIP
|
||||
)
|
||||
|
||||
func TestWeaveTaggerOverlayTopology(t *testing.T) {
|
||||
w := overlay.NewWeave(mockHostID, weave.MockClient{})
|
||||
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(string) (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)
|
||||
}
|
||||
defer w.Stop()
|
||||
|
||||
// Wait until the reporter reports some nodes
|
||||
@@ -28,30 +81,11 @@ func TestWeaveTaggerOverlayTopology(t *testing.T) {
|
||||
return len(have.Overlay.Nodes)
|
||||
})
|
||||
|
||||
{
|
||||
// Overlay node should include peer name and nickname
|
||||
have, err := w.Report()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f(w)
|
||||
}
|
||||
|
||||
nodeID := report.MakeOverlayNodeID(report.WeaveOverlayPeerPrefix, weave.MockWeavePeerName)
|
||||
node, ok := have.Overlay.Nodes[nodeID]
|
||||
if !ok {
|
||||
t.Errorf("Expected overlay node %q, but not found", nodeID)
|
||||
}
|
||||
if peerName, ok := node.Latest.Lookup(overlay.WeavePeerName); !ok || peerName != weave.MockWeavePeerName {
|
||||
t.Errorf("Expected weave peer name %q, got %q", weave.MockWeavePeerName, peerName)
|
||||
}
|
||||
if peerNick, ok := node.Latest.Lookup(overlay.WeavePeerNickName); !ok || peerNick != weave.MockWeavePeerNickName {
|
||||
t.Errorf("Expected weave peer nickname %q, got %q", weave.MockWeavePeerNickName, peerNick)
|
||||
}
|
||||
if localNetworks, ok := node.Sets.Lookup(host.LocalNetworks); !ok || !reflect.DeepEqual(localNetworks, report.MakeStringSet(weave.MockWeaveDefaultSubnet)) {
|
||||
t.Errorf("Expected weave node local_networks %q, got %q", report.MakeStringSet(weave.MockWeaveDefaultSubnet), localNetworks)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
func TestContainerTopologyTagging(t *testing.T) {
|
||||
test := func(w *overlay.Weave) {
|
||||
// Container nodes should be tagged with their overlay info
|
||||
nodeID := report.MakeContainerNodeID(weave.MockContainerID)
|
||||
have, err := w.Tag(report.Report{
|
||||
@@ -85,4 +119,49 @@ func TestWeaveTaggerOverlayTopology(t *testing.T) {
|
||||
t.Errorf("Expected container ips to include the weave IP (with scope) %q, got %q", mockContainerIPWithScope, have)
|
||||
}
|
||||
}
|
||||
|
||||
runTest(t, test)
|
||||
}
|
||||
|
||||
func TestOverlayTopology(t *testing.T) {
|
||||
test := func(w *overlay.Weave) {
|
||||
// Overlay node should include peer name and nickname
|
||||
have, err := w.Report()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
nodeID := report.MakeOverlayNodeID(report.WeaveOverlayPeerPrefix, weave.MockWeavePeerName)
|
||||
node, ok := have.Overlay.Nodes[nodeID]
|
||||
if !ok {
|
||||
t.Errorf("Expected overlay node %q, but not found", nodeID)
|
||||
}
|
||||
if peerName, ok := node.Latest.Lookup(overlay.WeavePeerName); !ok || peerName != weave.MockWeavePeerName {
|
||||
t.Errorf("Expected weave peer name %q, got %q", weave.MockWeavePeerName, peerName)
|
||||
}
|
||||
if peerNick, ok := node.Latest.Lookup(overlay.WeavePeerNickName); !ok || peerNick != weave.MockWeavePeerNickName {
|
||||
t.Errorf("Expected weave peer nickname %q, got %q", weave.MockWeavePeerNickName, peerNick)
|
||||
}
|
||||
if localNetworks, ok := node.Sets.Lookup(host.LocalNetworks); !ok || !reflect.DeepEqual(localNetworks, report.MakeStringSet(weave.MockWeaveDefaultSubnet)) {
|
||||
t.Errorf("Expected weave node local_networks %q, got %q", report.MakeStringSet(weave.MockWeaveDefaultSubnet), localNetworks)
|
||||
}
|
||||
// The weave proxy container is running
|
||||
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)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
runTest(t, test)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,10 @@ const (
|
||||
defaultServiceHost = "https://cloud.weave.works:443"
|
||||
)
|
||||
|
||||
var pluginAPIVersion = "1"
|
||||
var (
|
||||
pluginAPIVersion = "1"
|
||||
dockerEndpoint = "unix:///var/run/docker.sock"
|
||||
)
|
||||
|
||||
func check(flags map[string]string) {
|
||||
handleResponse := func(r *checkpoint.CheckResponse, err error) {
|
||||
@@ -174,7 +177,7 @@ func probeMain(flags probeFlags, targets []appclient.Target) {
|
||||
log.Errorf("Docker: problem with bridge %s: %v", flags.dockerBridge, err)
|
||||
}
|
||||
}
|
||||
if registry, err := docker.NewRegistry(flags.dockerInterval, clients, true, hostID, handlerRegistry); err == nil {
|
||||
if registry, err := docker.NewRegistry(flags.dockerInterval, clients, true, hostID, handlerRegistry, dockerEndpoint); err == nil {
|
||||
defer registry.Stop()
|
||||
if flags.procEnabled {
|
||||
p.AddTagger(docker.NewTagger(registry, processCache))
|
||||
@@ -200,29 +203,33 @@ func probeMain(flags probeFlags, targets []appclient.Target) {
|
||||
|
||||
if flags.weaveEnabled {
|
||||
client := weave.NewClient(sanitize.URL("http://", 6784, "")(flags.weaveAddr))
|
||||
weave := overlay.NewWeave(hostID, client)
|
||||
defer weave.Stop()
|
||||
p.AddTagger(weave)
|
||||
p.AddReporter(weave)
|
||||
|
||||
dockerBridgeIP, err := network.GetFirstAddressOf(flags.dockerBridge)
|
||||
weave, err := overlay.NewWeave(hostID, client, dockerEndpoint)
|
||||
if err != nil {
|
||||
log.Println("Error getting docker bridge ip:", err)
|
||||
log.Errorf("Weave: failed to start client: %v", err)
|
||||
} else {
|
||||
weaveDNSLookup := appclient.LookupUsing(dockerBridgeIP + ":53")
|
||||
weaveTargets, err := appclient.ParseTargets([]string{flags.weaveHostname})
|
||||
defer weave.Stop()
|
||||
p.AddTagger(weave)
|
||||
p.AddReporter(weave)
|
||||
|
||||
dockerBridgeIP, err := network.GetFirstAddressOf(flags.dockerBridge)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to parse weave targets: %v", err)
|
||||
log.Errorf("Error getting docker bridge ip: %v", err)
|
||||
} else {
|
||||
weaveResolver, err := appclient.NewResolver(appclient.ResolverConfig{
|
||||
Targets: weaveTargets,
|
||||
Lookup: weaveDNSLookup,
|
||||
Set: clients.Set,
|
||||
})
|
||||
weaveDNSLookup := appclient.LookupUsing(dockerBridgeIP + ":53")
|
||||
weaveTargets, err := appclient.ParseTargets([]string{flags.weaveHostname})
|
||||
if err != nil {
|
||||
log.Errorf("Failed to create weave resolver: %v", err)
|
||||
log.Errorf("Failed to parse weave targets: %v", err)
|
||||
} else {
|
||||
defer weaveResolver.Stop()
|
||||
weaveResolver, err := appclient.NewResolver(appclient.ResolverConfig{
|
||||
Targets: weaveTargets,
|
||||
Lookup: weaveDNSLookup,
|
||||
Set: clients.Set,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("Failed to create weave resolver: %v", err)
|
||||
} else {
|
||||
defer weaveResolver.Stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,24 +25,14 @@ func (MockClient) Status() (weave.Status, error) {
|
||||
return weave.Status{
|
||||
Router: weave.Router{
|
||||
Name: MockWeavePeerName,
|
||||
Peers: []struct {
|
||||
Name string
|
||||
NickName string
|
||||
Connections []struct {
|
||||
Name string
|
||||
NickName string
|
||||
Address string
|
||||
Outbound bool
|
||||
Established bool
|
||||
}
|
||||
}{
|
||||
Peers: []weave.Peer{
|
||||
{
|
||||
Name: MockWeavePeerName,
|
||||
NickName: MockWeavePeerNickName,
|
||||
},
|
||||
},
|
||||
},
|
||||
DNS: weave.DNS{
|
||||
DNS: &weave.DNS{
|
||||
Entries: []struct {
|
||||
Hostname string
|
||||
ContainerID string
|
||||
@@ -55,8 +45,14 @@ func (MockClient) Status() (weave.Status, error) {
|
||||
},
|
||||
},
|
||||
},
|
||||
IPAM: weave.IPAM{
|
||||
IPAM: &weave.IPAM{
|
||||
DefaultSubnet: MockWeaveDefaultSubnet,
|
||||
Entries: []struct {
|
||||
Size uint32
|
||||
IsKnownPeer bool
|
||||
}{
|
||||
{Size: 0, IsKnownPeer: false},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user