Skip to content

Commit

Permalink
Have the app periodically register with weaveDNS, and the probe do lo…
Browse files Browse the repository at this point in the history
…okups there.
  • Loading branch information
tomwilkie committed Feb 1, 2016
1 parent dd19cac commit d4f8eb6
Show file tree
Hide file tree
Showing 75 changed files with 21,984 additions and 122 deletions.
115 changes: 115 additions & 0 deletions app/weave.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package app

import (
"fmt"
"net"
"strings"

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

"github.com/weaveworks/scope/common/backoff"
"github.com/weaveworks/scope/common/weave"
"github.com/weaveworks/scope/probe/docker"
)

// Default values for weave app integration
const (
DefaultHostname = "scope.weave.local."
DefaultWeaveURL = "http://127.0.0.1:6784"
DefaultContainerName = "weavescope"
DefaultDockerEndpoint = "unix:///var/run/docker.sock"
)

// Weave is a thing which periodically registers this app with WeaveDNS.
type Weave struct {
url string
containerName string
hostname string
dockerClient docker.Client
weaveClient weave.Client
backoff backoff.Backoff
}

// NewWeave makes a new Weave.
func NewWeave(weaveURL, hostname, containerName, dockerEndpoint string) (*Weave, error) {
client, err := docker.NewDockerClientStub(dockerEndpoint)
if err != nil {
return nil, err
}
w := &Weave{
url: weaveURL,
containerName: containerName,
hostname: hostname,
dockerClient: client,
weaveClient: weave.NewClient(weaveURL),
}
w.backoff = backoff.New(w.updateDNS)
w.backoff.Message = "updating weaveDNS"
go w.backoff.Start()
return w, nil
}

// Stop the Weave.
func (w *Weave) Stop() {
w.backoff.Stop()
}

func (w *Weave) updateDNS() (bool, error) {
// 1. work out my IP addresses
ifaces, err := net.Interfaces()
if err != nil {
return false, err
}
ips := []net.IP{}
for _, i := range ifaces {
if strings.HasPrefix(i.Name, "lo") ||
strings.HasPrefix(i.Name, "docker") ||
strings.HasPrefix(i.Name, "veth") {
continue
}

addrs, err := i.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPAddr:
ip = v.IP
case *net.IPNet:
ip = v.IP
}
if ip != nil && ip.To4() != nil {
ips = append(ips, ip)
}
}
}

// 2. work out my container name
containers, err := w.dockerClient.ListContainers(fsouza.ListContainersOptions{})
if err != nil {
return false, err
}
containerID := ""
outer:
for _, container := range containers {
for _, name := range container.Names {
if name == "/"+w.containerName {
containerID = container.ID
break outer
}
}
}
if containerID == "" {
return false, fmt.Errorf("Container %s not found", w.containerName)
}

// 3. Register these with weave dns
for _, ip := range ips {
if err := w.weaveClient.AddDNSEntry(w.hostname, containerID, ip); err != nil {
return false, err
}
}
return false, nil
}
98 changes: 98 additions & 0 deletions common/weave/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package weave

import (
"bytes"
"encoding/json"
"fmt"
"net"
"net/http"
"net/url"
"strconv"
)

// Client for Weave Net API
type Client interface {
Status() (Status, error)
AddDNSEntry(fqdn, containerid string, ip net.IP) error
}

// Status describes whats happen in the Weave Net router.
type Status struct {
Router Router
DNS DNS
}

// Router describes the status of the Weave Router
type Router struct {
Peers []struct {
Name string
NickName string
}
}

// DNS descirbes the status of Weave DNS
type DNS struct {
Entries []struct {
Hostname string
ContainerID string
Tombstone int64
}
}

type client struct {
url string
}

// NewClient makes a new Client
func NewClient(url string) Client {
return &client{
url: url,
}
}

func (c *client) Status() (Status, error) {
req, err := http.NewRequest("GET", c.url+"/report", nil)
if err != nil {
return Status{}, err
}
req.Header.Add("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return Status{}, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return Status{}, fmt.Errorf("Got %d", resp.StatusCode)
}

var status Status
if err := json.NewDecoder(resp.Body).Decode(&status); err != nil {
return Status{}, err
}
return status, nil
}

func (c *client) AddDNSEntry(fqdn, containerID string, ip net.IP) error {
data := url.Values{
"fqdn": []string{fqdn},
}
url := fmt.Sprintf("%s/name/%s/%s", c.url, containerID, ip.String())
req, err := http.NewRequest("PUT", url, bytes.NewBufferString(data.Encode()))
if err != nil {
return err
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
if err := resp.Body.Close(); err != nil {
return err
}
if resp.StatusCode != http.StatusNoContent {
return fmt.Errorf("Got %d", resp.StatusCode)
}
return nil
}
88 changes: 88 additions & 0 deletions common/weave/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package weave_test

import (
"fmt"
"net/http"
"net/http/httptest"
"reflect"
"testing"

"github.com/weaveworks/scope/common/weave"
"github.com/weaveworks/scope/test"
)

const (
mockHostID = "host1"
mockWeavePeerName = "winnebago"
mockWeavePeerNickName = "winny"
mockContainerID = "83183a667c01"
mockContainerMAC = "d6:f2:5a:12:36:a8"
mockContainerIP = "10.0.0.123"
mockContainerIPWithScope = ";10.0.0.123"
mockHostname = "hostname.weave.local"
)

var (
mockResponse = fmt.Sprintf(`{
"Router": {
"Peers": [{
"Name": "%s",
"Nickname": "%s"
}]
},
"DNS": {
"Entries": [{
"ContainerID": "%s",
"Hostname": "%s.",
"Tombstone": 0
}]
}
}`, mockWeavePeerName, mockWeavePeerNickName, mockContainerID, mockHostname)
)

func mockWeaveRouter(w http.ResponseWriter, r *http.Request) {
if _, err := w.Write([]byte(mockResponse)); err != nil {
panic(err)
}
}

func TestClient(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(mockWeaveRouter))
defer s.Close()

client := weave.NewClient(s.URL)
status, err := client.Status()
if err != nil {
t.Fatal(err)
}

want := weave.Status{
Router: weave.Router{
Peers: []struct {
Name string
NickName string
}{
{
Name: mockWeavePeerName,
NickName: mockWeavePeerNickName,
},
},
},
DNS: weave.DNS{
Entries: []struct {
Hostname string
ContainerID string
Tombstone int64
}{
{
Hostname: mockHostname + ".",
ContainerID: mockContainerID,
Tombstone: 0,
},
},
},
}
if !reflect.DeepEqual(status, want) {
t.Fatal(test.Diff(status, want))
}
}
64 changes: 0 additions & 64 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,46 +14,6 @@ HOSTNAME=scope
DOMAIN=weave.local
IP_REGEXP="[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"

container_ip() {
if ! status=$(docker inspect --format='{{.State.Running}} {{.HostConfig.NetworkMode}}' $1 2>/dev/null); then
echo "Container $1 not found" >&2
return 1
fi
case "$status" in
"true host")
CONTAINER_IP="127.0.0.1"
return 0
;;
"true default" | "true bridge")
CONTAINER_IP="$(docker inspect --format='{{.NetworkSettings.IPAddress}}' $1 2>/dev/null)"
return 0
;;
*)
echo "Container $1 not running" >&2
return 1
;;
esac
}

is_running() {
status=$(docker inspect --format='{{.State.Running}}' $1 2>/dev/null) && [ "$status" = "true" ]
return $?
}

docker_bridge_ip() {
local DOCKER_BRIDGE_IP=$(ip -f inet address show dev $DOCKER_BRIDGE | grep -m1 -o 'inet \([.0-9]\)*')
echo ${DOCKER_BRIDGE_IP#inet }
}

# Run `weave` in the weave exec container
weave() {
WEAVEXEC_IMAGE=$(docker inspect --format='{{.Config.Image}}' weave | sed 's/\/weave/\/weaveexec/')
docker run -t --rm --privileged --net=host \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /proc:/hostproc \
-e PROCFS=/hostproc \
$WEAVEXEC_IMAGE --local "$@"
}

# Run `weave expose` if it's not already exposed.
weave_expose() {
Expand Down Expand Up @@ -125,30 +85,6 @@ while true; do
shift
done

if is_running $WEAVE_CONTAINER_NAME; then
container_ip $WEAVE_CONTAINER_NAME
weave_expose

DOCKER_BRIDGE_IP=$(docker_bridge_ip)
echo "Weave container detected at $CONTAINER_IP, Docker bridge at $DOCKER_BRIDGE_IP"

echo "domain $DOMAIN" >/etc/resolv.conf
echo "search $DOMAIN" >>/etc/resolv.conf
echo "nameserver $DOCKER_BRIDGE_IP" >>/etc/resolv.conf

IP_ADDRS=$(find /sys/class/net -type l | xargs -n1 basename | grep -vE 'docker|veth|lo' | \
xargs -n1 ip addr show | grep inet | awk '{ print $2 }' | grep -oE "$IP_REGEXP")
CONTAINER=$(docker inspect --format='{{.Id}}' weavescope)
if [ -z "$IP_ADDRS" ]; then
echo "Could not determine local IP address; Weave DNS integration will not work correctly."
exit 1
else
for ip in $IP_ADDRS; do
weave dns-add $ip $CONTAINER -h $HOSTNAME.$DOMAIN
done
fi
fi

echo "$APP_ARGS" >/etc/weave/scope-app.args
echo "$PROBE_ARGS" >/etc/weave/scope-probe.args

Expand Down
Loading

0 comments on commit d4f8eb6

Please sign in to comment.