Skip to content

Commit

Permalink
Add docker networks to the Overlay Topology
Browse files Browse the repository at this point in the history
  • Loading branch information
Alfonso Acosta committed Jun 14, 2016
1 parent f067311 commit 6f0a31d
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 8 deletions.
42 changes: 42 additions & 0 deletions probe/docker/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type Registry interface {
LockedPIDLookup(f func(func(int) Container))
WalkContainers(f func(Container))
WalkImages(f func(*docker_client.APIImages))
WalkNetworks(f func(*docker_client.Network))
WatchContainerUpdates(ContainerUpdateWatcher)
GetContainer(string) (Container, bool)
GetContainerByPrefix(string) (Container, bool)
Expand All @@ -61,13 +62,15 @@ type registry struct {
containers *radix.Tree
containersByPID map[int]Container
images map[string]*docker_client.APIImages
networks []*docker_client.Network
}

// Client interface for mocking.
type Client interface {
ListContainers(docker_client.ListContainersOptions) ([]docker_client.APIContainers, error)
InspectContainer(string) (*docker_client.Container, error)
ListImages(docker_client.ListImagesOptions) ([]docker_client.APIImages, error)
ListNetworks() ([]docker_client.Network, error)
AddEventListener(chan<- *docker_client.APIEvents) error
RemoveEventListener(chan *docker_client.APIEvents) error

Expand Down Expand Up @@ -171,6 +174,11 @@ func (r *registry) listenForEvents() bool {
return true
}

if err := r.updateNetworks(); err != nil {
log.Errorf("docker registry: %s", err)
return true
}

otherUpdates := time.Tick(r.interval)
for {
select {
Expand All @@ -186,6 +194,10 @@ func (r *registry) listenForEvents() bool {
log.Errorf("docker registry: %s", err)
return true
}
if err := r.updateNetworks(); err != nil {
log.Errorf("docker registry: %s", err)
return true
}

case ch := <-r.quit:
r.Lock()
Expand Down Expand Up @@ -217,6 +229,7 @@ func (r *registry) reset() {
r.containers = radix.New()
r.containersByPID = map[int]Container{}
r.images = map[string]*docker_client.APIImages{}
r.networks = r.networks[:0]
}

func (r *registry) updateContainers() error {
Expand Down Expand Up @@ -249,7 +262,26 @@ func (r *registry) updateImages() error {
return nil
}

func (r *registry) updateNetworks() error {
networks, err := r.client.ListNetworks()
if err != nil {
return err
}

r.Lock()
defer r.Unlock()

// reset
r.networks = r.networks[:0]
for i := range networks {
r.networks = append(r.networks, &networks[i])
}

return nil
}

func (r *registry) handleEvent(event *docker_client.APIEvents) {
// TODO: Send shortcut reports on networks being created/destroyed?
switch event.Status {
case CreateEvent, RenameEvent, StartEvent, DieEvent, DestroyEvent, PauseEvent, UnpauseEvent, NetworkConnectEvent, NetworkDisconnectEvent:
r.updateContainerState(event.ID, stateAfterEvent(event.Status))
Expand Down Expand Up @@ -406,6 +438,16 @@ func (r *registry) WalkImages(f func(*docker_client.APIImages)) {
})
}

// WalkNetworks runs f on every network the registry knows of.
func (r *registry) WalkNetworks(f func(*docker_client.Network)) {
r.RLock()
defer r.RUnlock()

for _, network := range r.networks {
f(network)
}
}

// ImageNameWithoutVersion splits the image name apart, returning the name
// without the version, if possible
func ImageNameWithoutVersion(name string) string {
Expand Down
32 changes: 32 additions & 0 deletions probe/docker/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type mockDockerClient struct {
apiContainers []client.APIContainers
containers map[string]*client.Container
apiImages []client.APIImages
networks []client.Network
events []chan<- *client.APIEvents
}

Expand All @@ -107,6 +108,12 @@ func (m *mockDockerClient) ListImages(client.ListImagesOptions) ([]client.APIIma
return m.apiImages, nil
}

func (m *mockDockerClient) ListNetworks() ([]client.Network, error) {
m.RLock()
defer m.RUnlock()
return m.networks, nil
}

func (m *mockDockerClient) AddEventListener(events chan<- *client.APIEvents) error {
m.Lock()
defer m.Unlock()
Expand Down Expand Up @@ -244,13 +251,22 @@ var (
"imgfoo2": "bar2",
},
}
network1 = client.Network{
ID: "deadbeef",
Name: "network1",
Scope: "local",
IPAM: client.IPAMOptions{
Config: []client.IPAMConfig{{Subnet: "5.6.7.8/24"}},
},
}
)

func newMockClient() *mockDockerClient {
return &mockDockerClient{
apiContainers: []client.APIContainers{apiContainer1},
containers: map[string]*client.Container{"ping": container1},
apiImages: []client.APIImages{apiImage1},
networks: []client.Network{network1},
}
}

Expand Down Expand Up @@ -292,6 +308,14 @@ func allImages(r docker.Registry) []*client.APIImages {
return result
}

func allNetworks(r docker.Registry) []*client.Network {
result := []*client.Network{}
r.WalkNetworks(func(i *client.Network) {
result = append(result, i)
})
return result
}

func TestRegistry(t *testing.T) {
mdc := newMockClient()
setupStubs(mdc, func() {
Expand All @@ -312,6 +336,14 @@ func TestRegistry(t *testing.T) {
return allImages(registry)
})
}

{
want := []*client.Network{&network1}
test.Poll(t, 100*time.Millisecond, want, func() interface{} {
return allNetworks(registry)
})
}

})
}

Expand Down
24 changes: 21 additions & 3 deletions probe/docker/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import (
docker_client "github.com/fsouza/go-dockerclient"

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

// Keys for use in Node
const (
ImageID = "docker_image_id"
ImageName = "docker_image_name"
ImageLabelPrefix = "docker_image_label_"
ImageID = "docker_image_id"
ImageName = "docker_image_name"
ImageLabelPrefix = "docker_image_label_"
OverlayPeerPrefix = "docker_peer_"
)

// Exposed for testing
Expand Down Expand Up @@ -145,6 +147,7 @@ func (r *Reporter) Report() (report.Report, error) {
result := report.MakeReport()
result.Container = result.Container.Merge(r.containerTopology(localAddrs))
result.ContainerImage = result.ContainerImage.Merge(r.containerImageTopology())
result.Overlay = result.Overlay.Merge(r.overlayTopology())
return result, nil
}

Expand Down Expand Up @@ -241,6 +244,21 @@ func (r *Reporter) containerImageTopology() report.Topology {
return result
}

func (r *Reporter) overlayTopology() report.Topology {
localSubnets := []string{}
r.registry.WalkNetworks(func(network *docker_client.Network) {
if network.Scope == "local" {
for _, config := range network.IPAM.Config {
localSubnets = append(localSubnets, config.Subnet)
}
}
})
peerID := OverlayPeerPrefix + r.hostID
node := report.MakeNode(report.MakeOverlayNodeID(peerID)).WithSets(
report.MakeSets().Add(host.LocalNetworks, report.MakeStringSet(localSubnets...)))
return report.MakeTopology().AddNode(node)
}

// Docker sometimes prefixes ids with a "type" annotation, but it renders a bit
// ugly and isn't necessary, so we should strip it off
func trimImageID(id string) string {
Expand Down
39 changes: 34 additions & 5 deletions probe/docker/reporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import (
client "github.com/fsouza/go-dockerclient"

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

type mockRegistry struct {
containersByPID map[int]docker.Container
images map[string]*client.APIImages
networks []*client.Network
}

func (r *mockRegistry) Stop() {}
Expand All @@ -34,27 +36,38 @@ func (r *mockRegistry) WalkImages(f func(*client.APIImages)) {
}
}

func (r *mockRegistry) WalkNetworks(f func(*client.Network)) {
for _, i := range r.networks {
f(i)
}
}

func (r *mockRegistry) WatchContainerUpdates(_ docker.ContainerUpdateWatcher) {}

func (r *mockRegistry) GetContainer(_ string) (docker.Container, bool) { return nil, false }

func (r *mockRegistry) GetContainerByPrefix(_ string) (docker.Container, bool) { return nil, false }

var (
imageID = "baz"
mockRegistryInstance = &mockRegistry{
containersByPID: map[int]docker.Container{
2: &mockContainer{container1},
},
images: map[string]*client.APIImages{
"baz": &apiImage1,
imageID: &apiImage1,
},
networks: []*client.Network{&network1},
}
)

func TestReporter(t *testing.T) {
var controlProbeID = "a1b2c3d4"
var (
controlProbeID = "a1b2c3d4"
hostID = "host1"
)

containerImageNodeID := report.MakeContainerImageNodeID("baz")
containerImageNodeID := report.MakeContainerImageNodeID(imageID)
rpt, err := docker.NewReporter(mockRegistryInstance, "host1", controlProbeID, nil).Report()
if err != nil {
t.Fatal(err)
Expand All @@ -71,7 +84,7 @@ func TestReporter(t *testing.T) {
for k, want := range map[string]string{
docker.ContainerID: "ping",
docker.ContainerName: "pong",
docker.ImageID: "baz",
docker.ImageID: imageID,
report.ControlProbeID: controlProbeID,
} {
if have, ok := node.Latest.Lookup(k); !ok || have != want {
Expand All @@ -98,7 +111,7 @@ func TestReporter(t *testing.T) {
}

for k, want := range map[string]string{
docker.ImageID: "baz",
docker.ImageID: imageID,
docker.ImageName: "bang",
docker.ImageLabelPrefix + "imgfoo1": "bar1",
docker.ImageLabelPrefix + "imgfoo2": "bar2",
Expand All @@ -113,4 +126,20 @@ func TestReporter(t *testing.T) {
t.Errorf("Container images should not have any controls")
}
}

// Reporter should add a container network
{
peerID := docker.OverlayPeerPrefix + hostID
overlayNodeID := report.MakeOverlayNodeID(peerID)
node, ok := rpt.Overlay.Nodes[overlayNodeID]
if !ok {
t.Fatalf("Expected report to have overlay node %q, but not found", overlayNodeID)
}

want := "5.6.7.8/24"
if have, ok := node.Sets.Lookup(host.LocalNetworks); !ok || len(have) != 1 || have[0] != want {
t.Fatalf("Expected node to have exactly local network %v but found %v", want, have)
}

}
}

0 comments on commit 6f0a31d

Please sign in to comment.