Merge branch 'master' into weave_1.1_readme_updates

Conflicts:
	README.md
This commit is contained in:
Matthias Radestock committed 2015-07-03 11:55:32 +01:00
commit 7554bd3913
90 files changed
+7092 -922

No files matched your search

+3 -2
View File
@@ -39,9 +39,10 @@ prog/netcheck/netcheck
tools/bin
tools/build
releases
.weaver.uptodate
.weavedns.uptodate
.weaveexec.uptodate
weave.tar
weavedns.tar
weaveexec.tar
prog/weaveexec/weave
prog/weaveexec/weaveproxy
prog/weaveexec/weavewait
+33 -25
View File
@@ -18,27 +18,29 @@ NETCHECK_EXE=prog/netcheck/netcheck
EXES=$(WEAVER_EXE) $(WEAVEDNS_EXE) $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(NETCHECK_EXE)
WEAVER_UPTODATE=.weaver.uptodate
WEAVEDNS_UPTODATE=.weavedns.uptodate
WEAVEEXEC_UPTODATE=.weaveexec.uptodate
IMAGES_UPTODATE=$(WEAVER_UPTODATE) $(WEAVEDNS_UPTODATE) $(WEAVEEXEC_UPTODATE)
WEAVER_IMAGE=$(DOCKERHUB_USER)/weave
WEAVEDNS_IMAGE=$(DOCKERHUB_USER)/weavedns
WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec
IMAGES=$(WEAVER_IMAGE) $(WEAVEDNS_IMAGE) $(WEAVEEXEC_IMAGE)
WEAVER_EXPORT=weave.tar
WEAVEDNS_EXPORT=weavedns.tar
WEAVEEXEC_EXPORT=weaveexec.tar
EXPORTS=$(WEAVER_EXPORT) $(WEAVEDNS_EXPORT) $(WEAVEEXEC_EXPORT)
WEAVE_EXPORT=weave.tar
WEAVEEXEC_DOCKER_VERSION=1.3.1
DOCKER_DISTRIB=prog/weaveexec/docker-$(WEAVEEXEC_DOCKER_VERSION).tgz
DOCKER_DISTRIB_URL=https://get.docker.com/builds/Linux/x86_64/docker-$(WEAVEEXEC_DOCKER_VERSION).tgz
all: $(EXPORTS)
all: $(WEAVE_EXPORT)
travis: $(EXES)
update: $(EXES)
update:
go get -u -f -v -tags -netgo $(addprefix ./,$(dir $(EXES)))
$(WEAVER_EXE) $(WEAVEDNS_EXE) $(WEAVEPROXY_EXE) $(NETCHECK_EXE): common/*.go common/*/*.go net/*.go
@@ -56,6 +58,7 @@ $(WEAVER_EXE) $(WEAVEDNS_EXE) $(WEAVEPROXY_EXE) $(NETCHECK_EXE): common/*.go com
$(WEAVER_EXE): router/*.go ipam/*.go ipam/*/*.go prog/weaver/main.go
$(WEAVEDNS_EXE): nameserver/*.go prog/weavedns/main.go
$(WEAVEPROXY_EXE): proxy/*.go prog/weaveproxy/main.go
$(NETCHECK_EXE): prog/netcheck/netcheck.go
# Sigproxy and weavewait need separate rules as they fail the netgo check in
# the main build stanza due to not importing net package
@@ -66,15 +69,15 @@ $(SIGPROXY_EXE): prog/sigproxy/main.go
$(WEAVEWAIT_EXE): prog/weavewait/main.go
go build -o $@ ./$(@D)
$(WEAVER_EXPORT): prog/weaver/Dockerfile $(WEAVER_EXE)
$(WEAVER_UPTODATE): prog/weaver/Dockerfile $(WEAVER_EXE)
$(SUDO) docker build -t $(WEAVER_IMAGE) prog/weaver
$(SUDO) docker save $(WEAVER_IMAGE):latest > $@
touch $@
$(WEAVEDNS_EXPORT): prog/weavedns/Dockerfile $(WEAVEDNS_EXE)
$(WEAVEDNS_UPTODATE): prog/weavedns/Dockerfile $(WEAVEDNS_EXE)
$(SUDO) docker build -t $(WEAVEDNS_IMAGE) prog/weavedns
$(SUDO) docker save $(WEAVEDNS_IMAGE):latest > $@
touch $@
$(WEAVEEXEC_EXPORT): prog/weaveexec/Dockerfile $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(NETCHECK_EXE)
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(NETCHECK_EXE)
cp weave prog/weaveexec/weave
cp $(SIGPROXY_EXE) prog/weaveexec/sigproxy
cp $(WEAVEPROXY_EXE) prog/weaveexec/weaveproxy
@@ -82,24 +85,29 @@ $(WEAVEEXEC_EXPORT): prog/weaveexec/Dockerfile $(DOCKER_DISTRIB) weave $(SIGPROX
cp $(NETCHECK_EXE) prog/weaveexec/netcheck
cp $(DOCKER_DISTRIB) prog/weaveexec/docker.tgz
$(SUDO) docker build -t $(WEAVEEXEC_IMAGE) prog/weaveexec
$(SUDO) docker save $(WEAVEEXEC_IMAGE):latest > $@
touch $@
$(WEAVE_EXPORT): $(IMAGES_UPTODATE)
$(SUDO) docker save $(addsuffix :latest,$(IMAGES)) > $@
$(DOCKER_DISTRIB):
curl -o $(DOCKER_DISTRIB) $(DOCKER_DISTRIB_URL)
tests:
echo "mode: count" > profile.cov
fail=0 ; \
for dir in $$(find . -type f -name '*_test.go' | xargs -n1 dirname | sort -u); do \
output=$$(mktemp cover.XXXXXXXXXX) ; \
if ! go test -cpu 4 -tags netgo -covermode=count -coverprofile=$$output $$dir ; then \
fail=1 ; \
fi ; \
if [ -f $$output ]; then \
tail -n +2 <$$output >>profile.cov; \
rm $$output; \
fi \
done ; \
fail=0 ; \
for dir in $$(find . -type f -name '*_test.go' | xargs -n1 dirname | sort -u); do \
go get -t -tags netgo $$dir ; \
output=$$(mktemp cover.XXXXXXXXXX) ; \
if ! go test -cpu 4 -tags netgo \
-covermode=count -coverprofile=$$output $$dir ; then \
fail=1 ; \
fi ; \
if [ -f $$output ]; then \
tail -n +2 <$$output >>profile.cov; \
rm $$output; \
fi \
done ; \
exit $$fail
go tool cover -html=profile.cov -o=coverage.html
@@ -112,7 +120,7 @@ publish: $(PUBLISH)
clean:
-$(SUDO) docker rmi $(IMAGES)
rm -f $(EXES) $(EXPORTS)
rm -f $(EXES) $(IMAGES_UPTODATE) $(WEAVE_EXPORT)
rm -f test/tls/*.pem
build:
+8 -3
View File
@@ -4,9 +4,14 @@
# About Weaveworks
[Come meet Weaveworks at Dockercon](http://weave.works/dockercon)
[Weaveworks](http://weave.works) is the company that develops Weave - the most productive way for developers to connect, observe and control Docker containers. To learn about our products, including getting started tutorials, visit our [website](http://weave.works), read about the [1.0 release of Weave](http://blog.weave.works/2015/06/18/weave-1-0-is-out-and-it-is-good/), visit our [documentation](http://docs.weave.works) or continue to read about some of the more technical aspects of Weave in this readme.
[Weaveworks](http://weave.works) is the company that develops Weave -
the most productive way for developers to connect, observe and control
Docker containers. To learn about our products, including getting
started tutorials, visit our [website](http://weave.works), read about
the
[1.0 release of Weave](http://blog.weave.works/2015/06/18/weave-1-0-is-out-and-it-is-good/),
visit our [documentation](http://docs.weave.works) or continue to read
about some of the more technical aspects of Weave in this readme.
# Weave
+4 -1
View File
@@ -1,6 +1,9 @@
FROM ubuntu
RUN apt-get -y update && apt-get -y install --no-install-recommends build-essential git ca-certificates docker.io mercurial libpcap-dev curl make pkg-config gcc bison flex
RUN apt-get -y update && apt-get -y install --no-install-recommends ca-certificates apt-transport-https
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
RUN echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list
RUN apt-get -y update && apt-get -y install --no-install-recommends build-essential git lxc-docker-1.3.1 mercurial libpcap-dev curl make pkg-config gcc bison flex
# When doing a build in a container, "apt-get update" happens twice,
# which can be a very significant overhead for incremental builds.
+4 -4
View File
@@ -36,7 +36,7 @@ func NewClient(apiPath string) (*Client, error) {
if err != nil {
return nil, err
}
Info.Printf("[docker] Using Docker API on %s: %v", apiPath, env)
Log.Infof("[docker] Using Docker API on %s: %v", apiPath, env)
return client, nil
}
@@ -44,7 +44,7 @@ func NewClient(apiPath string) (*Client, error) {
func (c *Client) AddObserver(ob ContainerObserver) error {
events := make(chan *docker.APIEvents)
if err := c.AddEventListener(events); err != nil {
Error.Printf("[docker] Unable to add listener to Docker API: %s", err)
Log.Errorf("[docker] Unable to add listener to Docker API: %s", err)
return err
}
@@ -63,7 +63,7 @@ func (c *Client) AddObserver(ob ContainerObserver) error {
// IsContainerNotRunning returns true if we have checked with Docker that the ID is not running
func (c *Client) IsContainerNotRunning(idStr string) bool {
if !IsContainerID(idStr) {
Debug.Printf("[docker] '%s' does not seem to be a container id", idStr)
Log.Debugf("[docker] '%s' does not seem to be a container id", idStr)
return false
}
@@ -75,6 +75,6 @@ func (c *Client) IsContainerNotRunning(idStr string) bool {
if _, notThere := err.(*docker.NoSuchContainer); notThere {
return true
}
Error.Printf("[docker] Could not check container status: %s", err)
Log.Errorf("[docker] Could not check container status: %s", err)
return false
}
+2 -2
View File
@@ -2,13 +2,13 @@ package common
func CheckFatal(e error) {
if e != nil {
Error.Fatal(e)
Log.Fatal(e)
}
}
func CheckWarn(e error) {
if e != nil {
Warning.Println(e)
Log.Warningln(e)
}
}
+1 -6
View File
@@ -1,10 +1,5 @@
package common
import (
"io/ioutil"
"os"
)
func init() {
InitLogging(ioutil.Discard, os.Stdout, os.Stdout, os.Stderr)
InitDefaultLogging(false)
}
+44 -28
View File
@@ -1,46 +1,62 @@
package common
import (
"io"
"io/ioutil"
"log"
"bytes"
"fmt"
"os"
"strings"
"github.com/Sirupsen/logrus"
)
const (
standardLogFlags = log.Ldate | log.Ltime | log.Lmicroseconds
)
type textFormatter struct {
}
// Largely taken from
// http://www.goinggo.net/2013/11/using-log-package-in-go.html
// Based off logrus.TextFormatter, which behaves completely
// differently when you don't want colored output
func (f *textFormatter) Format(entry *logrus.Entry) ([]byte, error) {
b := &bytes.Buffer{}
levelText := strings.ToUpper(entry.Level.String())[0:4]
timeStamp := entry.Time.Format("2006/01/02 15:04:05.000000")
if len(entry.Data) > 0 {
fmt.Fprintf(b, "%s: %s %-44s ", levelText, timeStamp, entry.Message)
for k, v := range entry.Data {
fmt.Fprintf(b, " %s=%v", k, v)
}
} else {
// No padding when there's no fields
fmt.Fprintf(b, "%s: %s %s", levelText, timeStamp, entry.Message)
}
b.WriteByte('\n')
return b.Bytes(), nil
}
var (
Debug *log.Logger
Info *log.Logger
Warning *log.Logger
Error *log.Logger
debugF bool
standardTextFormatter = &textFormatter{}
)
func InitLogging(debugHandle io.Writer,
infoHandle io.Writer,
warningHandle io.Writer,
errorHandle io.Writer) {
var (
Log *logrus.Logger
)
Debug = log.New(debugHandle, "DEBUG: ", standardLogFlags)
Info = log.New(infoHandle, "INFO: ", standardLogFlags)
Warning = log.New(warningHandle, "WARNING: ", standardLogFlags)
Error = log.New(errorHandle, "ERROR: ", standardLogFlags)
func InitLogging(level logrus.Level) {
if Log == nil {
Log = &logrus.Logger{
Out: os.Stderr,
Formatter: standardTextFormatter,
Hooks: make(logrus.LevelHooks),
Level: level,
}
}
Log.Level = level
}
func InitDefaultLogging(debug bool) {
if debug == debugF {
return
}
debugF = debug
debugOut := ioutil.Discard
level := logrus.InfoLevel
if debug {
debugOut = os.Stderr
level = logrus.DebugLevel
}
InitLogging(debugOut, os.Stdout, os.Stdout, os.Stderr)
InitLogging(level)
}
+3 -3
View File
@@ -76,7 +76,7 @@ func NewSchedQueue(clock clock.Clock) *SchedQueue {
// Start starts the scheduled queue
func (cq *SchedQueue) Start() {
go func() {
defer func() { Debug.Printf("[sched-q] Quitting (%d calls pending)", len(cq.callablesH)) }()
defer func() { Log.Debugf("[sched-q] Quitting (%d calls pending)", len(cq.callablesH)) }()
var now time.Time
timer := cq.clock.Timer(time.Duration(math.MaxInt64))
@@ -116,14 +116,14 @@ func (cq *SchedQueue) Start() {
// Stop stops the scheduled queue
func (cq *SchedQueue) Stop() {
Debug.Printf("[sched-q] Stopping...")
Log.Debugf("[sched-q] Stopping...")
cq.closeChan <- true
}
// Add schedules a call.
// The callable should not modify the scheduled queue in any way.
func (cq *SchedQueue) Add(c callable, t time.Time) {
Debug.Printf("[sched-q] Adding call at %s", t)
Log.Debugf("[sched-q] Adding call at %s", t)
ce := schedCall{c: c, t: t}
cq.schedChan <- &ce
}
+3 -3
View File
@@ -11,7 +11,7 @@ import (
// Ensure we can add new calls while forwarding the clock
func TestSchedCallsBasic(t *testing.T) {
InitDefaultLogging(testing.Verbose())
Info.Println("TestSchedCallsBasic starting")
Log.Infoln("TestSchedCallsBasic starting")
const testSecs = 1000
clk := clock.NewMock()
@@ -37,7 +37,7 @@ func TestSchedCallsBasic(t *testing.T) {
// Ensure we can create a 100 seconds gap in the middle of the time travel
func TestSchedCallsGap(t *testing.T) {
InitDefaultLogging(testing.Verbose())
Info.Println("TestSchedCallsGap starting")
Log.Infoln("TestSchedCallsGap starting")
const testSecs = 1000
clk := clock.NewMock()
@@ -65,7 +65,7 @@ func TestSchedCallsGap(t *testing.T) {
func TestSchedCallsStop(t *testing.T) {
InitDefaultLogging(testing.Verbose())
Info.Println("TestSchedCallsStop starting")
Log.Infoln("TestSchedCallsStop starting")
const testSecs = 1000
clk := clock.NewMock()
+3 -3
View File
@@ -20,17 +20,17 @@ func SignalHandlerLoop(ss ...SignalReceiver) {
for {
switch <-sigs {
case syscall.SIGINT:
Info.Printf("=== received SIGINT ===\n*** exiting\n")
Log.Infof("=== received SIGINT ===\n*** exiting\n")
for _, subsystem := range ss {
subsystem.Stop()
}
return
case syscall.SIGQUIT:
stacklen := runtime.Stack(buf, true)
Info.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf[:stacklen])
Log.Infof("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf[:stacklen])
case syscall.SIGUSR1:
for _, subsystem := range ss {
Info.Printf("=== received SIGUSR1 ===\n*** status...\n%s\n*** end\n", subsystem.Status())
Log.Infof("=== received SIGUSR1 ===\n*** status...\n%s\n*** end\n", subsystem.Status())
}
}
}
+8 -3
View File
@@ -43,9 +43,14 @@ func (g *allocate) Try(alloc *Allocator) bool {
}
// out of space
if donor, err := alloc.ring.ChoosePeerToAskForSpace(g.r.Start, g.r.End); err == nil {
alloc.debugln("Decided to ask peer", donor, "for space in range", g.r)
alloc.sendSpaceRequest(donor, g.r)
donors := alloc.ring.ChoosePeersToAskForSpace(g.r.Start, g.r.End)
for _, donor := range donors {
if err := alloc.sendSpaceRequest(donor, g.r); err != nil {
alloc.debugln("Problem asking peer", donor, "for space:", err)
} else {
alloc.debugln("Decided to ask peer", donor, "for space in range", g.r)
break
}
}
return false
+5 -5
View File
@@ -575,14 +575,14 @@ func (alloc *Allocator) propose() {
alloc.gossip.GossipBroadcast(alloc.Gossip())
}
func (alloc *Allocator) sendSpaceRequest(dest router.PeerName, r address.Range) {
func (alloc *Allocator) sendSpaceRequest(dest router.PeerName, r address.Range) error {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
if err := enc.Encode(r); err != nil {
panic(err)
}
msg := router.Concat([]byte{msgSpaceRequest}, buf.Bytes())
alloc.gossip.GossipUnicast(dest, msg)
return alloc.gossip.GossipUnicast(dest, msg)
}
func (alloc *Allocator) sendRingUpdate(dest router.PeerName) {
@@ -718,11 +718,11 @@ func (alloc *Allocator) findOwner(addr address.Address) string {
// Logging
func (alloc *Allocator) infof(fmt string, args ...interface{}) {
common.Info.Printf("[allocator %s] "+fmt, append([]interface{}{alloc.ourName}, args...)...)
common.Log.Infof("[allocator %s] "+fmt, append([]interface{}{alloc.ourName}, args...)...)
}
func (alloc *Allocator) debugln(args ...interface{}) {
common.Debug.Println(append([]interface{}{fmt.Sprintf("[allocator %s]:", alloc.ourName)}, args...)...)
common.Log.Debugln(append([]interface{}{fmt.Sprintf("[allocator %s]:", alloc.ourName)}, args...)...)
}
func (alloc *Allocator) debugf(fmt string, args ...interface{}) {
common.Debug.Printf("[allocator %s] "+fmt, append([]interface{}{alloc.ourName}, args...)...)
common.Log.Debugf("[allocator %s] "+fmt, append([]interface{}{alloc.ourName}, args...)...)
}
+13 -8
View File
@@ -168,7 +168,7 @@ func TestCancel(t *testing.T) {
CIDR = "10.0.1.7/26"
)
router := TestGossipRouter{make(map[router.PeerName]chan gossipMessage), 0.0}
router := TestGossipRouter{make(map[router.PeerName]chan interface{}), 0.0}
alloc1, subnet := makeAllocator("01:00:00:02:00:00", CIDR, 2)
alloc1.SetInterfaces(router.connect(alloc1.ourName, alloc1))
@@ -186,9 +186,9 @@ func TestCancel(t *testing.T) {
// Get some IPs, so each allocator has some space
res1, _ := alloc1.Allocate("foo", subnet, nil)
common.Debug.Printf("res1 = %s", res1.String())
common.Log.Debugf("res1 = %s", res1.String())
res2, _ := alloc2.Allocate("bar", subnet, nil)
common.Debug.Printf("res2 = %s", res2.String())
common.Log.Debugf("res2 = %s", res2.String())
if res1 == res2 {
require.FailNow(t, "Error: got same ips!")
}
@@ -256,13 +256,17 @@ func TestTransfer(t *testing.T) {
require.True(t, err == nil, "Failed to get address")
router.GossipBroadcast(alloc2.Gossip())
router.flush()
router.GossipBroadcast(alloc3.Gossip())
router.flush()
router.removePeer(alloc2.ourName)
router.removePeer(alloc3.ourName)
alloc2.Stop()
alloc3.Stop()
router.flush()
require.NoError(t, alloc1.AdminTakeoverRanges(alloc2.ourName.String()))
require.NoError(t, alloc1.AdminTakeoverRanges(alloc3.ourName.String()))
router.flush()
require.Equal(t, address.Offset(1022), alloc1.NumFreeAddresses(subnet))
@@ -282,7 +286,8 @@ func TestFakeRouterSimple(t *testing.T) {
alloc1 := allocs[0]
//alloc2 := allocs[1]
alloc1.Allocate("foo", subnet, nil)
_, err := alloc1.Allocate("foo", subnet, nil)
require.NoError(t, err, "Failed to get address")
}
func TestAllocatorFuzz(t *testing.T) {
@@ -338,14 +343,14 @@ func TestAllocatorFuzz(t *testing.T) {
allocIndex := rand.Int31n(nodes)
alloc := allocs[allocIndex]
//common.Info.Printf("Allocate: asking allocator %d", allocIndex)
//common.Log.Infof("Allocate: asking allocator %d", allocIndex)
addr, err := alloc.Allocate(name, subnet, nil)
if err != nil {
panic(fmt.Sprintf("Could not allocate addr"))
}
//common.Info.Printf("Allocate: got address %s for name %s", addr, name)
//common.Log.Infof("Allocate: got address %s for name %s", addr, name)
addrStr := addr.String()
stateLock.Lock()
@@ -382,7 +387,7 @@ func TestAllocatorFuzz(t *testing.T) {
stateLock.Unlock()
alloc := allocs[res.alloc]
//common.Info.Printf("Freeing %s (%s) on allocator %d", res.name, addr, res.alloc)
//common.Log.Infof("Freeing %s (%s) on allocator %d", res.name, addr, res.alloc)
oldAddr, err := address.ParseIP(addr)
if err != nil {
@@ -407,7 +412,7 @@ func TestAllocatorFuzz(t *testing.T) {
stateLock.Unlock()
alloc := allocs[res.alloc]
//common.Info.Printf("Asking for %s (%s) on allocator %d again", res.name, addr, res.alloc)
//common.Log.Infof("Asking for %s (%s) on allocator %d again", res.name, addr, res.alloc)
newAddr, _ := alloc.Allocate(res.name, subnet, nil)
oldAddr, _ := address.ParseIP(addr)
+3 -3
View File
@@ -13,7 +13,7 @@ import (
func badRequest(w http.ResponseWriter, err error) {
http.Error(w, err.Error(), http.StatusBadRequest)
common.Warning.Println("[allocator]:", err.Error())
common.Log.Warningln("[allocator]:", err.Error())
}
// HandleHTTP wires up ipams HTTP endpoints to the provided mux.
@@ -79,7 +79,7 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CID
return
}
if dockerCli != nil && dockerCli.IsContainerNotRunning(ident) {
common.Info.Printf("[allocator] '%s' is not running: freeing %s", ident, addr)
common.Log.Infof("[allocator] '%s' is not running: freeing %s", ident, addr)
alloc.Free(ident, addr)
return
}
@@ -96,7 +96,7 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CID
return
}
if dockerCli != nil && dockerCli.IsContainerNotRunning(ident) {
common.Info.Printf("[allocator] '%s' is not running: freeing %s", ident, newAddr)
common.Log.Infof("[allocator] '%s' is not running: freeing %s", ident, newAddr)
alloc.Free(ident, newAddr)
return
}
+2 -2
View File
@@ -48,13 +48,13 @@ func listenHTTP(alloc *Allocator, subnet address.CIDR) int {
httpListener, err := net.Listen("tcp", ":0")
if err != nil {
common.Error.Fatal("Unable to create http listener: ", err)
common.Log.Fatal("Unable to create http listener: ", err)
}
go func() {
srv := &http.Server{Handler: router}
if err := srv.Serve(httpListener); err != nil {
common.Error.Fatal("Unable to serve http: ", err)
common.Log.Fatal("Unable to serve http: ", err)
}
}()
return httpListener.Addr().(*net.TCPAddr).Port
+29 -20
View File
@@ -217,7 +217,7 @@ func (r *Ring) Merge(gossip Ring) error {
switch {
case mine.Version >= theirs.Version:
if mine.Version == theirs.Version && !mine.Equal(theirs) {
common.Debug.Printf("Error merging entries at %s - %v != %v\n", mine.Token, mine, theirs)
common.Log.Debugf("Error merging entries at %s - %v != %v\n", mine.Token, mine, theirs)
return ErrInvalidEntry
}
addToResult(*mine)
@@ -348,9 +348,9 @@ func (r *Ring) String() string {
return buffer.String()
}
// ReportFree is used by the allocator to tell the ring
// how many free ips are in a given range, so that ChoosePeerToAskForSpace
// can make more intelligent decisions.
// ReportFree is used by the allocator to tell the ring how many free
// ips are in a given range, so that ChoosePeersToAskForSpace can make
// more intelligent decisions.
func (r *Ring) ReportFree(freespace map[address.Address]address.Offset) {
r.assertInvariants()
defer r.assertInvariants()
@@ -392,9 +392,20 @@ func (r *Ring) ReportFree(freespace map[address.Address]address.Offset) {
}
}
// ChoosePeerToAskForSpace chooses a weighted-random peer to ask
// for space in the range [start, end). Assumes start<end.
func (r *Ring) ChoosePeerToAskForSpace(start, end address.Address) (result router.PeerName, err error) {
type weightedPeer struct {
weight float64
peername router.PeerName
}
type weightedPeers []weightedPeer
// Note Less is using > so that bigger weights sort earlier
func (ws weightedPeers) Less(i, j int) bool { return ws[i].weight > ws[j].weight }
func (ws weightedPeers) Len() int { return len(ws) }
func (ws weightedPeers) Swap(i, j int) { ws[i], ws[j] = ws[j], ws[i] }
// ChoosePeersToAskForSpace returns all peers we can ask for space in
// the range [start, end), in weighted-random order. Assumes start<end.
func (r *Ring) ChoosePeersToAskForSpace(start, end address.Address) []router.PeerName {
var (
sum address.Offset
totalSpacePerPeer = make(map[router.PeerName]address.Offset) // Compute total free space per peer
@@ -423,21 +434,19 @@ func (r *Ring) ChoosePeerToAskForSpace(start, end address.Address) (result route
sum += entry.Free
}
if sum == 0 {
err = ErrNoFreeSpace
return
}
// Pick random peer, weighted by total free space
rn := rand.Int63n(int64(sum))
// Compute weighted random numbers, then sort.
// This isn't perfect, e.g. an item with weight 2 will get chosen more than
// twice as often as an item with weight 1, but it's good enough for our purposes.
ws := make(weightedPeers, 0, len(totalSpacePerPeer))
for peername, space := range totalSpacePerPeer {
rn -= int64(space)
if rn < 0 {
return peername, nil
}
ws = append(ws, weightedPeer{weight: float64(space) * rand.Float64(), peername: peername})
}
panic("Should never reach this point")
sort.Sort(ws)
result := make([]router.PeerName, len(ws))
for i, wp := range ws {
result[i] = wp.peername
}
return result
}
func (r *Ring) PickPeerForTransfer() router.PeerName {
+25 -22
View File
@@ -351,42 +351,45 @@ func TestGossip(t *testing.T) {
assertRing(ring2, []*entry{{Token: start, Peer: peer1name, Free: 255}})
}
func assertPeersWithSpace(t *testing.T, ring *Ring, start, end address.Address, expected int) []router.PeerName {
peers := ring.ChoosePeersToAskForSpace(start, end)
require.Equal(t, expected, len(peers))
return peers
}
func TestFindFree(t *testing.T) {
ring1 := New(start, end, peer1name)
_, err := ring1.ChoosePeerToAskForSpace(start, end)
require.True(t, err == ErrNoFreeSpace, "Expected ErrNoFreeSpace")
assertPeersWithSpace(t, ring1, start, end, 0)
ring1.Entries = []*entry{{Token: start, Peer: peer1name}}
_, err = ring1.ChoosePeerToAskForSpace(start, end)
require.True(t, err == ErrNoFreeSpace, "Expected ErrNoFreeSpace")
assertPeersWithSpace(t, ring1, start, end, 0)
// We shouldn't return outselves
ring1.ReportFree(map[address.Address]address.Offset{start: 10})
_, err = ring1.ChoosePeerToAskForSpace(start, end)
require.True(t, err == ErrNoFreeSpace, "Expected ErrNoFreeSpace")
assertPeersWithSpace(t, ring1, start, end, 0)
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 1},
{Token: middle, Peer: peer1name, Free: 1}}
_, err = ring1.ChoosePeerToAskForSpace(start, end)
require.True(t, err == ErrNoFreeSpace, "Expected ErrNoFreeSpace")
assertPeersWithSpace(t, ring1, start, end, 0)
ring1.assertInvariants()
// We should return others
var peers []router.PeerName
ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 1}}
peer, err := ring1.ChoosePeerToAskForSpace(start, end)
require.NoError(t, err)
require.Equal(t, peer2name, peer)
peers = assertPeersWithSpace(t, ring1, start, end, 1)
require.Equal(t, peer2name, peers[0])
ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 1},
{Token: middle, Peer: peer3name, Free: 1}}
peer, err = ring1.ChoosePeerToAskForSpace(start, middle)
require.NoError(t, err)
require.Equal(t, peer2name, peer)
peers = assertPeersWithSpace(t, ring1, start, middle, 1)
require.Equal(t, peer2name, peers[0])
peer, err = ring1.ChoosePeerToAskForSpace(middle, end)
require.NoError(t, err)
require.Equal(t, peer3name, peer)
peers = assertPeersWithSpace(t, ring1, middle, end, 1)
require.Equal(t, peer3name, peers[0])
assertPeersWithSpace(t, ring1, start, end, 2)
ring1.assertInvariants()
}
@@ -605,7 +608,7 @@ func TestFuzzRingHard(t *testing.T) {
addPeer := func() {
peer, _ := router.PeerNameFromString(fmt.Sprintf("%02d:%02d:00:00:00:00", nextPeerID/10, nextPeerID%10))
common.Debug.Printf("%s: Adding peer", peer)
common.Log.Debugf("%s: Adding peer", peer)
nextPeerID++
peers = append(peers, peer)
rings = append(rings, New(start, end, peer))
@@ -655,7 +658,7 @@ func TestFuzzRingHard(t *testing.T) {
require.NoError(t, otherRing.Merge(*ring))
}
common.Debug.Printf("%s: transferring from peer %s", otherPeername, peername)
common.Log.Debugf("%s: transferring from peer %s", otherPeername, peername)
otherRing.Transfer(peername, peername)
// And now tell everyone about the transfer - rmpeer is
@@ -684,12 +687,12 @@ func TestFuzzRingHard(t *testing.T) {
size := address.Subtract(rangeToSplit.End, rangeToSplit.Start)
ipInRange := address.Add(rangeToSplit.Start, address.Offset(rand.Intn(int(size))))
_, peerToGiveTo, _ := randomPeer(-1)
common.Debug.Printf("%s: Granting [%v, %v) to %s", ring.Peer, ipInRange, rangeToSplit.End, peerToGiveTo)
common.Log.Debugf("%s: Granting [%v, %v) to %s", ring.Peer, ipInRange, rangeToSplit.End, peerToGiveTo)
ring.GrantRangeToHost(ipInRange, rangeToSplit.End, peerToGiveTo)
// Now 'gossip' this to a random host (note, note could be same host as above)
otherIndex, _, otherRing := randomPeer(-1)
common.Debug.Printf("%s: 'Gossiping' to %s", ring.Peer, otherRing.Peer)
common.Log.Debugf("%s: 'Gossiping' to %s", ring.Peer, otherRing.Peer)
require.NoError(t, otherRing.Merge(*ring))
theRanges[indexWithRanges] = ring.OwnedRanges()
@@ -708,7 +711,7 @@ func TestFuzzRingHard(t *testing.T) {
}
ring1 := ringsWithEntries[rand.Intn(len(ringsWithEntries))]
ring2index, _, ring2 := randomPeer(-1)
common.Debug.Printf("%s: 'Gossiping' to %s", ring1.Peer, ring2.Peer)
common.Log.Debugf("%s: 'Gossiping' to %s", ring1.Peer, ring2.Peer)
require.NoError(t, ring2.Merge(*ring1))
theRanges[ring2index] = ring2.OwnedRanges()
}
+59 -37
View File
@@ -195,33 +195,47 @@ func AssertNothingSentErr(t *testing.T, ch <-chan error) {
}
// Router to convey gossip from one gossiper to another, for testing
type gossipMessage struct {
isUnicast bool
sender *router.PeerName
buf []byte // for unicast
data router.GossipData // for broadcast
exitChan chan bool
type unicastMessage struct {
sender router.PeerName
buf []byte
}
type broadcastMessage struct {
data router.GossipData
}
type exitMessage struct {
exitChan chan struct{}
}
type flushMessage struct {
flushChan chan struct{}
}
type TestGossipRouter struct {
gossipChans map[router.PeerName]chan gossipMessage
gossipChans map[router.PeerName]chan interface{}
loss float32 // 0.0 means no loss
}
func (grouter *TestGossipRouter) GossipBroadcast(update router.GossipData) error {
for _, gossipChan := range grouter.gossipChans {
select {
case gossipChan <- gossipMessage{data: update}:
case gossipChan <- broadcastMessage{data: update}:
default: // drop the message if we cannot send it
}
}
return nil
}
func (grouter *TestGossipRouter) flush() {
for _, gossipChan := range grouter.gossipChans {
flushChan := make(chan struct{})
gossipChan <- flushMessage{flushChan: flushChan}
<-flushChan
}
}
func (grouter *TestGossipRouter) removePeer(peer router.PeerName) {
gossipChan := grouter.gossipChans[peer]
resultChan := make(chan bool)
gossipChan <- gossipMessage{exitChan: resultChan}
resultChan := make(chan struct{})
gossipChan <- exitMessage{exitChan: resultChan}
<-resultChan
delete(grouter.gossipChans, peer)
}
@@ -231,39 +245,47 @@ type TestGossipRouterClient struct {
sender router.PeerName
}
func (grouter *TestGossipRouter) connect(sender router.PeerName, gossiper router.Gossiper) router.Gossip {
gossipChan := make(chan gossipMessage, 100)
func (grouter *TestGossipRouter) run(gossiper router.Gossiper, gossipChan chan interface{}) {
gossipTimer := time.Tick(10 * time.Second)
for {
select {
case gossip := <-gossipChan:
switch message := gossip.(type) {
case exitMessage:
close(message.exitChan)
return
go func() {
gossipTimer := time.Tick(10 * time.Second)
for {
select {
case message := <-gossipChan:
if message.exitChan != nil {
message.exitChan <- true
return
}
case flushMessage:
close(message.flushChan)
case unicastMessage:
if rand.Float32() > (1.0 - grouter.loss) {
continue
}
if err := gossiper.OnGossipUnicast(message.sender, message.buf); err != nil {
panic(fmt.Sprintf("Error doing gossip unicast to %s: %s", message.sender, err))
}
if message.isUnicast {
if err := gossiper.OnGossipUnicast(*message.sender, message.buf); err != nil {
panic(fmt.Sprintf("Error doing gossip unicast to %s: %s", sender, err))
}
} else {
for _, msg := range message.data.Encode() {
if _, err := gossiper.OnGossipBroadcast(msg); err != nil {
panic(fmt.Sprintf("Error doing gossip broadcast to %s: %s", sender, err))
}
case broadcastMessage:
if rand.Float32() > (1.0 - grouter.loss) {
continue
}
for _, msg := range message.data.Encode() {
if _, err := gossiper.OnGossipBroadcast(msg); err != nil {
panic(fmt.Sprintf("Error doing gossip broadcast: %s", err))
}
}
case <-gossipTimer:
grouter.GossipBroadcast(gossiper.Gossip())
}
case <-gossipTimer:
grouter.GossipBroadcast(gossiper.Gossip())
}
}()
}
}
func (grouter *TestGossipRouter) connect(sender router.PeerName, gossiper router.Gossiper) router.Gossip {
gossipChan := make(chan interface{}, 100)
go grouter.run(gossiper, gossipChan)
grouter.gossipChans[sender] = gossipChan
return TestGossipRouterClient{grouter, sender}
@@ -271,9 +293,9 @@ func (grouter *TestGossipRouter) connect(sender router.PeerName, gossiper router
func (client TestGossipRouterClient) GossipUnicast(dstPeerName router.PeerName, buf []byte) error {
select {
case client.router.gossipChans[dstPeerName] <- gossipMessage{isUnicast: true, sender: &client.sender, buf: buf}:
case client.router.gossipChans[dstPeerName] <- unicastMessage{sender: client.sender, buf: buf}:
default: // drop the message if we cannot send it
common.Error.Printf("Dropping message")
common.Log.Errorf("Dropping message")
}
return nil
}
@@ -284,7 +306,7 @@ func (client TestGossipRouterClient) GossipBroadcast(update router.GossipData) e
func makeNetworkOfAllocators(size int, cidr string) ([]*Allocator, TestGossipRouter, address.Range) {
gossipRouter := TestGossipRouter{make(map[router.PeerName]chan gossipMessage), 0.0}
gossipRouter := TestGossipRouter{make(map[router.PeerName]chan interface{}), 0.0}
allocs := make([]*Allocator, size)
var subnet address.Range
@@ -298,7 +320,7 @@ func makeNetworkOfAllocators(size int, cidr string) ([]*Allocator, TestGossipRou
}
gossipRouter.GossipBroadcast(allocs[size-1].Gossip())
time.Sleep(1000 * time.Millisecond)
gossipRouter.flush()
return allocs, gossipRouter, subnet
}
+5 -3
View File
@@ -7,9 +7,11 @@ import (
)
// A zone record
// Note: we will try to keep all the zone records information is concentrated here. Maybe not all queries will
// use things like priorities or weights, but we do not want to create a hierarchy for dealing with all
// possible queries...
//
// Note: all the zone records information is concentrated here. Maybe
// not all queries will use things like priorities or weights, but we
// do not want to create a hierarchy for dealing with all possible
// queries...
type ZoneRecord interface {
Name() string // The name for this IP
IP() net.IP // The IP (v4) address
+1 -1
View File
@@ -10,7 +10,7 @@ import (
func TestAddrs(t *testing.T) {
InitDefaultLogging(testing.Verbose())
Info.Println("TestAddrs starting")
Log.Infoln("TestAddrs starting")
ip, err := addrToIPv4("10.13.12.11")
require.NoError(t, err)
+6 -6
View File
@@ -86,7 +86,7 @@ func (e *cacheEntry) getReply(request *dns.Msg, maxLen int, now time.Time) (*dns
}
if e.ReplyLen >= maxLen {
Debug.Printf("[cache msgid %d] returning truncated reponse: %d > %d", request.MsgHdr.Id, e.ReplyLen, maxLen)
Log.Debugf("[cache msgid %d] returning truncated reponse: %d > %d", request.MsgHdr.Id, e.ReplyLen, maxLen)
return makeTruncatedReply(request), nil
}
@@ -122,7 +122,7 @@ func (e *cacheEntry) setReply(reply *dns.Msg, ttl int, flags uint8, now time.Tim
var prevValidUntil time.Time
if e.Status == stResolved {
if reply != nil {
Debug.Printf("[cache msgid %d] replacing response in cache", reply.MsgHdr.Id)
Log.Debugf("[cache msgid %d] replacing response in cache", reply.MsgHdr.Id)
}
prevValidUntil = e.validUntil
}
@@ -147,7 +147,7 @@ func (e *cacheEntry) setReply(reply *dns.Msg, ttl int, flags uint8, now time.Tim
}
e.validUntil = now.Add(time.Second * time.Duration(minTTL))
} else {
Warning.Printf("[cache] no valid TTL could be calculated")
Log.Warningf("[cache] no valid TTL could be calculated")
}
e.Status = stResolved
@@ -323,7 +323,7 @@ func (c *Cache) Get(request *dns.Msg, maxLen int) (reply *dns.Msg, err error) {
if ent, found := c.entries[key]; found {
reply, err = ent.getReply(request, maxLen, now)
if ent.hasExpired(now) {
Debug.Printf("[cache msgid %d] expired: removing", request.MsgHdr.Id)
Log.Debugf("[cache msgid %d] expired: removing", request.MsgHdr.Id)
if ent.index > 0 {
heap.Remove(&c.entriesH, ent.index)
}
@@ -332,7 +332,7 @@ func (c *Cache) Get(request *dns.Msg, maxLen int) (reply *dns.Msg, err error) {
}
} else {
// we are the first asking for this name: create an entry with no reply... the caller must wait
Debug.Printf("[cache msgid %d] addind in pending state", request.MsgHdr.Id)
Log.Debugf("[cache msgid %d] addind in pending state", request.MsgHdr.Id)
c.entries[key] = newCacheEntry(&question, now)
}
return
@@ -345,7 +345,7 @@ func (c *Cache) Remove(question *dns.Question) {
key := cacheKey(*question)
if entry, found := c.entries[key]; found {
Debug.Printf("[cache] removing %s-response for '%s'", dns.TypeToString[question.Qtype], question.Name)
Log.Debugf("[cache] removing %s-response for '%s'", dns.TypeToString[question.Qtype], question.Name)
if entry.index > 0 {
heap.Remove(&c.entriesH, entry.index)
}
+4 -4
View File
@@ -15,7 +15,7 @@ import (
// Check that the cache keeps its intended capacity constant
func TestCacheLength(t *testing.T) {
InitDefaultLogging(testing.Verbose())
Info.Println("TestCacheLength starting")
Log.Infoln("TestCacheLength starting")
const cacheLen = 128
@@ -56,9 +56,9 @@ func TestCacheLength(t *testing.T) {
// Check that the cache entries are ok
func TestCacheEntries(t *testing.T) {
InitDefaultLogging(testing.Verbose())
Info.Println("TestCacheEntries starting")
Log.Infoln("TestCacheEntries starting")
Info.Println("Checking cache consistency")
Log.Infoln("Checking cache consistency")
const cacheLen = 128
clk := clock.NewMock()
@@ -213,7 +213,7 @@ func TestCacheEntries(t *testing.T) {
require.True(t, err == errNoLocalReplies, "Get() error with CacheNoLocalReplies")
clk.Add(time.Duration(DefaultLocalTTL/2) * time.Second)
t.Logf("Checking that we get an expired response after %f seconds", DefaultLocalTTL)
t.Logf("Checking that we get an expired response after %d seconds", DefaultLocalTTL)
clk.Add(time.Second * time.Duration(DefaultLocalTTL+1))
t.Logf("Checking that we get an expired response after %d seconds", DefaultLocalTTL)
resp, err = l.Get(questionMsg3, minUDPSize)
+1 -1
View File
@@ -12,7 +12,7 @@ import (
// Check that we can prune an answer
func TestPrune(t *testing.T) {
InitDefaultLogging(testing.Verbose())
Info.Println("TestPrune starting")
Log.Infoln("TestPrune starting")
questionMsg := new(dns.Msg)
questionMsg.SetQuestion("name", dns.TypeA)
+12 -13
View File
@@ -2,7 +2,6 @@ package nameserver
import (
"fmt"
"log"
"net"
"net/http"
@@ -12,10 +11,10 @@ import (
"github.com/weaveworks/weave/common/docker"
)
func httpErrorAndLog(level *log.Logger, w http.ResponseWriter, msg string,
func httpErrorAndLog(w http.ResponseWriter, msg string,
status int, logmsg string, logargs ...interface{}) {
http.Error(w, msg, status)
level.Printf("[http] "+logmsg, logargs...)
Log.Warningf("[http] "+logmsg, logargs...)
}
func extractFQDN(r *http.Request) (string, string) {
@@ -41,7 +40,7 @@ func ServeHTTP(listener net.Listener, version string, server *DNSServer, dockerC
muxRouter.Methods("PUT").Path("/name/{id:.+}/{ip:.+}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reqError := func(msg string, logmsg string, logargs ...interface{}) {
httpErrorAndLog(Warning, w, msg, http.StatusBadRequest, logmsg, logargs...)
httpErrorAndLog(w, msg, http.StatusBadRequest, logmsg, logargs...)
}
vars := mux.Vars(r)
@@ -62,21 +61,21 @@ func ServeHTTP(listener net.Listener, version string, server *DNSServer, dockerC
domain := server.Zone.Domain()
if !dns.IsSubDomain(domain, name) {
Info.Printf("[http] Ignoring name %s, not in %s", name, domain)
Log.Infof("[http] Ignoring name %s, not in %s", name, domain)
return
}
Info.Printf("[http] Adding %s -> %s", name, ipStr)
Log.Infof("[http] Adding %s -> %s", name, ipStr)
if err := server.Zone.AddRecord(idStr, name, ip); err != nil {
if _, ok := err.(DuplicateError); !ok {
httpErrorAndLog(
Error, w, "Internal error", http.StatusInternalServerError,
w, "Internal error", http.StatusInternalServerError,
"Unexpected error from DB: %s", err)
return
} // oh, I already know this. whatever.
}
if dockerCli != nil && dockerCli.IsContainerNotRunning(idStr) {
Info.Printf("[http] '%s' is not running: removing", idStr)
Log.Infof("[http] '%s' is not running: removing", idStr)
server.Zone.DeleteRecords(idStr, name, ip)
}
})
@@ -89,14 +88,14 @@ func ServeHTTP(listener net.Listener, version string, server *DNSServer, dockerC
ip := net.ParseIP(ipStr)
if ip == nil {
httpErrorAndLog(
Warning, w, "Invalid IP in request", http.StatusBadRequest,
w, "Invalid IP in request", http.StatusBadRequest,
"Invalid IP in request: %s", ipStr)
return
}
fqdnStr, fqdn := extractFQDN(r)
Info.Printf("[http] Deleting ID %s, IP %s, FQDN %s", idStr, ipStr, fqdnStr)
Log.Infof("[http] Deleting ID %s, IP %s, FQDN %s", idStr, ipStr, fqdnStr)
server.Zone.DeleteRecords(idStr, fqdn, ip)
})
@@ -106,20 +105,20 @@ func ServeHTTP(listener net.Listener, version string, server *DNSServer, dockerC
fqdnStr, fqdn := extractFQDN(r)
Info.Printf("[http] Deleting ID %s, IP *, FQDN %s", idStr, fqdnStr)
Log.Infof("[http] Deleting ID %s, IP *, FQDN %s", idStr, fqdnStr)
server.Zone.DeleteRecords(idStr, fqdn, net.IP{})
})
muxRouter.Methods("DELETE").Path("/name").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fqdnStr, fqdn := extractFQDN(r)
Info.Printf("[http] Deleting ID *, IP *, FQDN %s", fqdnStr)
Log.Infof("[http] Deleting ID *, IP *, FQDN %s", fqdnStr)
server.Zone.DeleteRecords("", fqdn, net.IP{})
})
http.Handle("/", muxRouter)
if err := http.Serve(listener, nil); err != nil {
Error.Fatal("[http] Unable to serve http: ", err)
Log.Fatal("[http] Unable to serve http: ", err)
}
}
+2 -2
View File
@@ -12,10 +12,10 @@ func mdnsLookup(client *MDNSClient, name string, qtype uint16, insistent bool) (
for resp := range channel {
if err := resp.err; err != nil {
Debug.Printf("[mdns] Error for query type %s name %s: %s", dns.TypeToString[qtype], name, err)
Log.Debugf("[mdns] Error for query type %s name %s: %s", dns.TypeToString[qtype], name, err)
return nil, err
}
Debug.Printf("[mdns] Got response name for %s-query about name '%s': name '%s', addr '%s'",
Log.Debugf("[mdns] Got response name for %s-query about name '%s': name '%s', addr '%s'",
dns.TypeToString[qtype], name, resp.name, resp.addr)
if !insistent {
+6 -6
View File
@@ -113,7 +113,7 @@ func (s *MDNSServer) makeHandler(qtype uint16, lookup LookupFunc) dns.HandlerFun
// answer.
remoteAddr := rw.RemoteAddr()
if s.addrIsLocal(remoteAddr) && !s.allowLocal {
Debug.Printf("[mdns] srv: mDNS query from local host ('%s'): ignored", remoteAddr)
Log.Debugf("[mdns] srv: mDNS query from local host ('%s'): ignored", remoteAddr)
return
}
@@ -121,16 +121,16 @@ func (s *MDNSServer) makeHandler(qtype uint16, lookup LookupFunc) dns.HandlerFun
if len(r.Answer) == 0 && len(r.Question) > 0 {
q := &r.Question[0]
if q.Qtype == qtype {
Debug.Printf("[mdns msgid %d] srv: trying to answer to mDNS query '%s'", r.MsgHdr.Id, q.Name)
Log.Debugf("[mdns msgid %d] srv: trying to answer to mDNS query '%s'", r.MsgHdr.Id, q.Name)
if m := lookup(s.zone, r, q); m != nil {
Debug.Printf("[mdns msgid %d] srv: found local answer to mDNS query '%s'", r.MsgHdr.Id, q.Name)
Log.Debugf("[mdns msgid %d] srv: found local answer to mDNS query '%s'", r.MsgHdr.Id, q.Name)
if err := s.sendResponse(m); err != nil {
Warning.Printf("[mdns msgid %d] srv: error writing mDNS response to %v", r.MsgHdr.Id, s.sendconn)
Log.Warningf("[mdns msgid %d] srv: error writing mDNS response to %v", r.MsgHdr.Id, s.sendconn)
} else {
Debug.Printf("[mdns msgid %d] srv: response sent: %d answers", r.MsgHdr.Id, len(m.Answer))
Log.Debugf("[mdns msgid %d] srv: response sent: %d answers", r.MsgHdr.Id, len(m.Answer))
}
} else {
Debug.Printf("[mdns msgid %d] srv: no local answer for answering mDNS query '%s'",
Log.Debugf("[mdns msgid %d] srv: no local answer for answering mDNS query '%s'",
r.MsgHdr.Id, q.Name)
}
}
+7 -7
View File
@@ -18,7 +18,7 @@ func TestServerSimpleQuery(t *testing.T) {
)
InitDefaultLogging(testing.Verbose())
Info.Println("TestServerSimpleQuery starting")
Log.Infoln("TestServerSimpleQuery starting")
mzone := newMockedZoneWithRecords([]ZoneRecord{testRecord1, testRecord2})
mdnsServer, err := NewMDNSServer(mzone, true, DefaultLocalTTL)
@@ -59,11 +59,11 @@ func TestServerSimpleQuery(t *testing.T) {
require.NoError(t, err)
conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
require.NoError(t, err)
Debug.Printf("Sending UDP packet to %s", ipv4Addr)
Log.Debugf("Sending UDP packet to %s", ipv4Addr)
_, err = conn.WriteTo(buf, ipv4Addr)
require.NoError(t, err)
Debug.Printf("Waiting for response")
Log.Debugf("Waiting for response")
for {
select {
case x := <-recvChan:
@@ -80,7 +80,7 @@ func TestServerSimpleQuery(t *testing.T) {
return
}
case <-time.After(100 * time.Millisecond):
Debug.Printf("Timeout while waiting for response")
Log.Debugf("Timeout while waiting for response")
return
}
}
@@ -96,7 +96,7 @@ func TestServerSimpleQuery(t *testing.T) {
time.Sleep(100 * time.Millisecond) // Allow for server to get going
Debug.Printf("Checking that we get 2 IPs fo name '%s' [A]", testRecord1.Name())
Log.Debugf("Checking that we get 2 IPs fo name '%s' [A]", testRecord1.Name())
sendQuery(testRecord1.Name(), dns.TypeA)
if receivedCount != 2 {
t.Fatalf("Unexpected result count %d for %s", receivedCount, testRecord1.Name())
@@ -108,13 +108,13 @@ func TestServerSimpleQuery(t *testing.T) {
t.Fatalf("Unexpected result %s for %s", receivedAddrs, testRecord1.Name())
}
Debug.Printf("Checking that 'testfail.weave.' [A] gets no answers")
Log.Debugf("Checking that 'testfail.weave.' [A] gets no answers")
sendQuery("testfail.weave.", dns.TypeA)
if receivedCount != 0 {
t.Fatalf("Unexpected result count %d for testfail.weave", receivedCount)
}
Debug.Printf("Checking that '%s' [PTR] gets one name", testInAddr1)
Log.Debugf("Checking that '%s' [PTR] gets one name", testInAddr1)
sendQuery(testInAddr1, dns.TypePTR)
if receivedCount != 1 {
t.Fatalf("Expected an answer to %s, got %d answers", testInAddr1, receivedCount)
+9 -9
View File
@@ -37,7 +37,7 @@ func TestClientServerSimpleQuery(t *testing.T) {
receivedAddr = nil
receivedName = ""
receivedCount = 0
Debug.Printf("Sending query...")
Log.Debugf("Sending query...")
switch querytype {
case dns.TypeA:
r, err := mdnsCli.LookupName(name)
@@ -60,7 +60,7 @@ func TestClientServerSimpleQuery(t *testing.T) {
time.Sleep(100 * time.Millisecond) // Allow for server to get going
Debug.Printf("Query: %s dns.TypeA", testRecord1.Name())
Log.Debugf("Query: %s dns.TypeA", testRecord1.Name())
sendQuery(testRecord1.Name(), dns.TypeA)
if receivedCount != 1 {
t.Fatalf("Unexpected result count %d for %s", receivedCount, testRecord1.Name())
@@ -69,13 +69,13 @@ func TestClientServerSimpleQuery(t *testing.T) {
t.Fatalf("Unexpected result %s for %s", receivedAddr, testRecord1.Name())
}
Debug.Printf("Query: testfail.weave. dns.TypeA")
Log.Debugf("Query: testfail.weave. dns.TypeA")
sendQuery("testfail.weave.", dns.TypeA)
if receivedCount != 0 {
t.Fatalf("Unexpected result count %d for testfail.weave", receivedCount)
}
Debug.Printf("Query: %s dns.TypePTR", testInAddr1)
Log.Debugf("Query: %s dns.TypePTR", testInAddr1)
sendQuery(testInAddr1, dns.TypePTR)
if receivedCount != 1 {
t.Fatalf("Expected an answer to %s, got %d answers", testInAddr1, receivedCount)
@@ -127,7 +127,7 @@ func TestClientServerInsistentQuery(t *testing.T) {
receivedAddrs = nil
receivedNames = nil
receivedCount = 0
Debug.Printf("Sending query...")
Log.Debugf("Sending query...")
switch querytype {
case dns.TypeA:
receivedAddrs, err = mdnsCli.InsistentLookupName(name)
@@ -144,25 +144,25 @@ func TestClientServerInsistentQuery(t *testing.T) {
time.Sleep(100 * time.Millisecond) // Allow for server to get going
Debug.Printf("Query: %s dns.TypeA", testRecord1.Name())
Log.Debugf("Query: %s dns.TypeA", testRecord1.Name())
sendQuery(testRecord1.Name(), dns.TypeA)
if receivedCount != 2 {
t.Fatalf("Unexpected result count %d for %s", receivedCount, testRecord1.Name())
}
Debug.Printf("Query: testfail.weave. dns.TypeA")
Log.Debugf("Query: testfail.weave. dns.TypeA")
sendQuery("testfail.weave.", dns.TypeA)
if receivedCount != 0 {
t.Fatalf("Unexpected result count %d for testfail.weave", receivedCount)
}
Debug.Printf("Query: %s dns.TypePTR", testInAddr1)
Log.Debugf("Query: %s dns.TypePTR", testInAddr1)
sendQuery(testInAddr1, dns.TypePTR)
if receivedCount != 1 {
t.Fatalf("Expected an answer to %s, got %d answers", testInAddr1, receivedCount)
}
Debug.Printf("Query: %s dns.TypePTR", testInAddr2)
Log.Debugf("Query: %s dns.TypePTR", testInAddr2)
sendQuery(testInAddr2, dns.TypePTR)
if receivedCount != 1 {
t.Fatalf("Expected an answer to %s, got %d answers", testInAddr2, receivedCount)
+13 -13
View File
@@ -19,7 +19,7 @@ const (
)
// Warn about some methods that some day should be implemented...
func notImplWarn() { Warning.Printf("Mocked method. Not implemented.") }
func notImplWarn() { Log.Warningf("Mocked method. Not implemented.") }
// A mocked Zone that always returns the same records
// * it does not send/receive any mDNS query
@@ -38,7 +38,7 @@ func newMockedZoneWithRecords(zr []ZoneRecord) *mockedZoneWithRecords {
}
func (mz *mockedZoneWithRecords) Domain() string { return DefaultLocalDomain }
func (mz *mockedZoneWithRecords) LookupName(name string) ([]ZoneRecord, error) {
Debug.Printf("[mocked zone]: LookupName: returning records %s", mz.records)
Log.Debugf("[mocked zone]: LookupName: returning records %s", mz.records)
mz.Lock()
defer mz.Unlock()
@@ -53,7 +53,7 @@ func (mz *mockedZoneWithRecords) LookupName(name string) ([]ZoneRecord, error) {
}
func (mz *mockedZoneWithRecords) LookupInaddr(inaddr string) ([]ZoneRecord, error) {
Debug.Printf("[mocked zone]: LookupInaddr: returning records %s", mz.records)
Log.Debugf("[mocked zone]: LookupInaddr: returning records %s", mz.records)
mz.Lock()
defer mz.Unlock()
@@ -243,17 +243,17 @@ func newZoneDbsWithMockedMDns(num int, config ZoneConfig) zoneDbsWithMockedMDns
for i := 0; i < num; i++ {
res[i] = new(zbmEntry)
Debug.Printf("[test] Creating mocked mDNS server #%d", i)
Log.Debugf("[test] Creating mocked mDNS server #%d", i)
res[i].Server = newMockedMDNSServer(nil)
Debug.Printf("[test] Creating mocked mDNS client #%d", i)
Log.Debugf("[test] Creating mocked mDNS client #%d", i)
res[i].Client = newMockedMDNSClient([]*mockedMDNSServer{})
cfg := config
cfg.MDNSClient = res[i].Client
cfg.MDNSServer = res[i].Server
Debug.Printf("[test] Creating ZoneDb #%d", i)
Log.Debugf("[test] Creating ZoneDb #%d", i)
res[i].Zone, _ = NewZoneDb(cfg)
// link the mDNS server to its zone
@@ -270,7 +270,7 @@ func newZoneDbsWithMockedMDns(num int, config ZoneConfig) zoneDbsWithMockedMDns
// this is not what we currently do, but can be convenient in some cases
// for finding some bugs...
if i != j {
Debug.Printf("[test] Linking mocked mDNS client #%d to server #%d", i, j)
Log.Debugf("[test] Linking mocked mDNS client #%d to server #%d", i, j)
res[i].Client.AddServer(res[j].Server)
}
}
@@ -389,7 +389,7 @@ func (mf *mockedFallback) Stop() error {
// Run a UDP fallback server
func runLocalUDPServer(laddr string, handler dns.HandlerFunc) (*dns.Server, string, error) {
Debug.Printf("[mocked fallback] Starting fallback UDP server at %s", laddr)
Log.Debugf("[mocked fallback] Starting fallback UDP server at %s", laddr)
pc, err := net.ListenPacket("udp", laddr)
if err != nil {
return nil, "", err
@@ -401,13 +401,13 @@ func runLocalUDPServer(laddr string, handler dns.HandlerFunc) (*dns.Server, stri
pc.Close()
}()
Debug.Printf("[mocked fallback] Fallback UDP server listening at %s", pc.LocalAddr())
Log.Debugf("[mocked fallback] Fallback UDP server listening at %s", pc.LocalAddr())
return server, pc.LocalAddr().String(), nil
}
// Run a TCP fallback server
func runLocalTCPServer(laddr string, handler dns.HandlerFunc) (*dns.Server, string, error) {
Debug.Printf("[mocked fallback] Starting fallback TCP server at %s", laddr)
Log.Debugf("[mocked fallback] Starting fallback TCP server at %s", laddr)
laddrTCP, err := net.ResolveTCPAddr("tcp", laddr)
if err != nil {
return nil, "", err
@@ -424,7 +424,7 @@ func runLocalTCPServer(laddr string, handler dns.HandlerFunc) (*dns.Server, stri
l.Close()
}()
Debug.Printf("[mocked fallback] Fallback TCP server listening at %s", l.Addr().String())
Log.Debugf("[mocked fallback] Fallback TCP server listening at %s", l.Addr().String())
return server, l.Addr().String(), nil
}
@@ -442,9 +442,9 @@ func newMockedClock() *mockedClock {
// be waiting in channels. So your code should not depend on the channels
// for creating new timers. Otherwise, time travelling will not be reliable...
func (clk *mockedClock) Forward(secs int) {
Debug.Printf(">>>>>>> Moving clock forward %d seconds - Time traveling >>>>>>>", secs)
Log.Debugf(">>>>>>> Moving clock forward %d seconds - Time traveling >>>>>>>", secs)
clk.Add(time.Duration(secs) * time.Second)
Debug.Printf("<<<<<<< Time travel finished! We are at %s <<<<<<<", clk.Now())
Log.Debugf("<<<<<<< Time travel finished! We are at %s <<<<<<<", clk.Now())
}
//////////////////////////////////////////////////////////////////
+23 -22
View File
@@ -74,7 +74,7 @@ func (proto dnsProtocol) String() string {
// get a new dns.Client for a protocol
func (proto dnsProtocol) GetNewClient(bufsize int, timeout time.Duration) *dns.Client {
Debug.Printf("[dns] Creating %s DNS client with %s timeout", proto, timeout)
Log.Debugf("[dns] Creating %s DNS client with %s timeout", proto, timeout)
switch proto {
case protTCP:
return &dns.Client{Net: "tcp", ReadTimeout: timeout}
@@ -188,11 +188,11 @@ func NewDNSServer(config DNSServerConfig) (s *DNSServer, err error) {
// Start the DNS server
func (s *DNSServer) Start() error {
Info.Printf("[dns] Upstream server(s): %+v", s.Upstream)
Log.Infof("[dns] Upstream server(s): %+v", s.Upstream)
if s.cacheDisabled {
Info.Printf("[dns] Cache: disabled")
Log.Infof("[dns] Cache: disabled")
} else {
Info.Printf("[dns] Cache: %d entries", s.cache.Capacity())
Log.Infof("[dns] Cache: %d entries", s.cache.Capacity())
}
pc, err := net.ListenPacket("udp", s.ListenAddr)
@@ -225,25 +225,25 @@ func (s *DNSServer) ActivateAndServe() {
go func() {
defer s.listenersWg.Done()
Info.Printf("[dns] Listening for DNS on %s (UDP)", s.ListenAddr)
Log.Infof("[dns] Listening for DNS on %s (UDP)", s.ListenAddr)
err := s.udpSrv.ActivateAndServe()
CheckFatal(err)
Debug.Printf("[dns] DNS UDP server exiting...")
Log.Debugf("[dns] DNS UDP server exiting...")
}()
go func() {
defer s.listenersWg.Done()
Info.Printf("[dns] Listening for DNS on %s (TCP)", s.ListenAddr)
Log.Infof("[dns] Listening for DNS on %s (TCP)", s.ListenAddr)
err := s.tcpSrv.ActivateAndServe()
CheckFatal(err)
Debug.Printf("[dns] DNS TCP server exiting...")
Log.Debugf("[dns] DNS TCP server exiting...")
}()
// Waiting for all goroutines to finish (otherwise they die as main routine dies)
s.listenersWg.Wait()
Info.Printf("[dns] Server exiting...")
Log.Infof("[dns] Server exiting...")
}
// Return status string
@@ -284,7 +284,7 @@ func (s *DNSServer) createMux(proto dnsProtocol) *dns.ServeMux {
}
notUsHandler := s.notUsHandler(proto)
notUsFallback := func(w dns.ResponseWriter, r *dns.Msg) {
Info.Printf("[dns msgid %d] -> sending to fallback server", r.MsgHdr.Id)
Log.Infof("[dns msgid %d] -> sending to fallback server", r.MsgHdr.Id)
notUsHandler(w, r)
}
@@ -305,7 +305,7 @@ func (s *DNSServer) localHandler(proto dnsProtocol, kind string, qtype uint16,
return func(w dns.ResponseWriter, r *dns.Msg) {
q := r.Question[0]
Debug.Printf("[dns msgid %d] %s: %+v", r.MsgHdr.Id, kind, q)
Log.Debugf("[dns msgid %d] %s: %+v", r.MsgHdr.Id, kind, q)
maxLen := getMaxReplyLen(r, proto)
// cache a response if the cache is enabled, and observe the name/IP
@@ -324,17 +324,17 @@ func (s *DNSServer) localHandler(proto dnsProtocol, kind string, qtype uint16,
reply, err := s.cache.Get(r, maxLen)
if err != nil {
if err == errNoLocalReplies {
Debug.Printf("[dns msgid %d] Cached no-local-replies", r.MsgHdr.Id)
Log.Debugf("[dns msgid %d] Cached no-local-replies", r.MsgHdr.Id)
fallback(w, r)
} else {
Debug.Printf("[dns msgid %d] Error from cache: %s", r.MsgHdr.Id, err)
Log.Debugf("[dns msgid %d] Error from cache: %s", r.MsgHdr.Id, err)
w.WriteMsg(makeDNSFailResponse(r))
}
return
}
if reply != nil {
reply.Answer = pruneAnswers(shuffleAnswers(reply.Answer), s.maxAnswers)
Debug.Printf("[dns msgid %d] Returning reply from cache: %s/%d answers",
Log.Debugf("[dns msgid %d] Returning reply from cache: %s/%d answers",
r.MsgHdr.Id, dns.RcodeToString[reply.MsgHdr.Rcode], len(reply.Answer))
w.WriteMsg(reply)
return
@@ -343,7 +343,7 @@ func (s *DNSServer) localHandler(proto dnsProtocol, kind string, qtype uint16,
// catch unsupported queries
if q.Qtype != qtype {
Debug.Printf("[dns msgid %d] Unsupported query type %s", r.MsgHdr.Id, dns.TypeToString[q.Qtype])
Log.Debugf("[dns msgid %d] Unsupported query type %s", r.MsgHdr.Id, dns.TypeToString[q.Qtype])
m := makeDNSFailResponse(r)
maybeCache(m, s.negLocalTTL, 0)
w.WriteMsg(m)
@@ -351,8 +351,9 @@ func (s *DNSServer) localHandler(proto dnsProtocol, kind string, qtype uint16,
}
if answers, err := lookup(q.Name); err != nil {
Info.Printf("[dns msgid %d] No results for type %s query for '%s' [caching no-local]",
Log.Infof("[dns msgid %d] No results for type %s query for '%s' [caching no-local]",
r.MsgHdr.Id, dns.TypeToString[q.Qtype], q.Name)
maybeCache(nil, s.negLocalTTL, CacheNoLocalReplies)
fallback(w, r)
} else {
@@ -360,7 +361,7 @@ func (s *DNSServer) localHandler(proto dnsProtocol, kind string, qtype uint16,
m.Authoritative = true
maybeCache(m, nullTTL, 0)
m.Answer = pruneAnswers(shuffleAnswers(m.Answer), s.maxAnswers)
Debug.Printf("[dns msgid %d] Sending response: %s/%d answers [code:%s]",
Log.Debugf("[dns msgid %d] Sending response: %s/%d answers [code:%s]",
m.MsgHdr.Id, dns.TypeToString[q.Qtype], len(m.Answer), dns.RcodeToString[m.Rcode])
w.WriteMsg(m)
}
@@ -380,25 +381,25 @@ func (s *DNSServer) notUsHandler(proto dnsProtocol) dns.HandlerFunc {
rcopy := r
rcopy.SetEdns0(uint16(maxLen), false)
Debug.Printf("[dns msgid %d] Fallback query: %+v [%s, max:%d bytes]", rcopy.MsgHdr.Id, q, proto, maxLen)
Log.Debugf("[dns msgid %d] Fallback query: %+v [%s, max:%d bytes]", rcopy.MsgHdr.Id, q, proto, maxLen)
for _, server := range s.Upstream.Servers {
reply, _, err := dnsClient.Exchange(rcopy, fmt.Sprintf("%s:%s", server, s.Upstream.Port))
if err != nil {
Debug.Printf("[dns msgid %d] Network error trying %s (%s)",
Log.Debugf("[dns msgid %d] Network error trying %s (%s)",
r.MsgHdr.Id, server, err)
continue
}
if reply != nil && reply.Rcode != dns.RcodeSuccess {
Debug.Printf("[dns msgid %d] Failure reported by %s for query %s",
Log.Debugf("[dns msgid %d] Failure reported by %s for query %s",
r.MsgHdr.Id, server, q.Name)
continue
}
Debug.Printf("[dns msgid %d] Given answer by %s for query %s",
Log.Debugf("[dns msgid %d] Given answer by %s for query %s",
r.MsgHdr.Id, server, q.Name)
w.WriteMsg(reply)
return
}
Warning.Printf("[dns msgid %d] Failed lookup for external name %s", r.MsgHdr.Id, q.Name)
Log.Warningf("[dns msgid %d] Failed lookup for external name %s", r.MsgHdr.Id, q.Name)
w.WriteMsg(makeDNSFailResponse(r))
}
}
+25 -25
View File
@@ -20,15 +20,15 @@ func TestServerDbCacheInvalidation(t *testing.T) {
)
InitDefaultLogging(testing.Verbose())
Info.Println("TestServerDbCacheInvalidation starting")
Log.Infoln("TestServerDbCacheInvalidation starting")
clk := newMockedClock()
Debug.Printf("Creating mocked mDNS client and server")
Log.Debugf("Creating mocked mDNS client and server")
mdnsServer1 := newMockedMDNSServerWithRecord(Record{testName1, net.ParseIP("10.2.2.9"), 0, 0, 0})
mdnsCli1 := newMockedMDNSClient([]*mockedMDNSServer{mdnsServer1})
Debug.Printf("Creating zone database with the mocked mDNS client and server")
Log.Debugf("Creating zone database with the mocked mDNS client and server")
zoneConfig := ZoneConfig{
MDNSServer: mdnsServer1,
MDNSClient: mdnsCli1,
@@ -40,7 +40,7 @@ func TestServerDbCacheInvalidation(t *testing.T) {
require.NoError(t, err)
defer zone.Stop()
Debug.Printf("Creating a cache")
Log.Debugf("Creating a cache")
cache, err := NewCache(1024, clk)
require.NoError(t, err)
@@ -59,7 +59,7 @@ func TestServerDbCacheInvalidation(t *testing.T) {
fallback.Start()
defer fallback.Stop()
Debug.Printf("Creating a real DNS server with a mocked cache")
Log.Debugf("Creating a real DNS server with a mocked cache")
srv, err := NewDNSServer(DNSServerConfig{
Zone: zone,
Cache: cache,
@@ -79,7 +79,7 @@ func TestServerDbCacheInvalidation(t *testing.T) {
require.NoError(t, err)
require.NotEqual(t, 0, testPort, "invalid listen port")
Debug.Printf("Adding two IPs to %s", testName1)
Log.Debugf("Adding two IPs to %s", testName1)
zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.1"))
zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.2"))
q, _ := assertExchange(t, testName1, dns.TypeA, testPort, 2, 2, 0)
@@ -93,7 +93,7 @@ func TestServerDbCacheInvalidation(t *testing.T) {
// we should have an entry in the cache for this query
// if we add another IP, that cache entry should be removed
Debug.Printf("Adding a new IP to %s: the cache entry should be removed", testName1)
Log.Debugf("Adding a new IP to %s: the cache entry should be removed", testName1)
zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.3"))
assertNotInCache(t, cache, q, fmt.Sprintf("after adding a new IP for %s", testName1))
@@ -101,10 +101,10 @@ func TestServerDbCacheInvalidation(t *testing.T) {
// first.weave.local = 10.2.2.1 10.2.2.2 10.2.2.3
// second.weave.local = 10.9.9.1
Debug.Printf("Querying again (so a cache entry will be created)")
Log.Debugf("Querying again (so a cache entry will be created)")
q, _ = assertExchange(t, testName1, dns.TypeA, testPort, 3, 4, 0)
assertInCache(t, cache, q, "after asking about the name")
Debug.Printf("... and removing one of the IP addresses")
Log.Debugf("... and removing one of the IP addresses")
zone.DeleteRecords(containerID, "", net.ParseIP("10.2.2.2"))
assertNotInCache(t, cache, q, "after deleting IP for 10.2.2.2")
@@ -113,7 +113,7 @@ func TestServerDbCacheInvalidation(t *testing.T) {
// second.weave.local = 10.9.9.1
// generate cache responses
Debug.Printf("Querying for a raddr")
Log.Debugf("Querying for a raddr")
qname, _ := assertExchange(t, testName1, dns.TypeA, testPort, 2, 2, 0)
qptr, _ := assertExchange(t, "1.2.2.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0)
qotherName, _ := assertExchange(t, testName2, dns.TypeA, testPort, 1, 1, 0)
@@ -126,7 +126,7 @@ func TestServerDbCacheInvalidation(t *testing.T) {
assertNotLocalInCache(t, cache, qwrongName, "after asking for a wrong name")
// now we will check if a removal affects all the responses
Debug.Printf("... and removing an IP should invalidate both the cached responses for name and raddr")
Log.Debugf("... and removing an IP should invalidate both the cached responses for name and raddr")
zone.DeleteRecords(containerID, "", net.ParseIP("10.2.2.1"))
assertNotInCache(t, cache, qptr, "after deleting record")
assertNotInCache(t, cache, qname, "after deleting record")
@@ -137,7 +137,7 @@ func TestServerDbCacheInvalidation(t *testing.T) {
// second.weave.local = 10.9.9.1
// generate cache responses
Debug.Printf("Querying for a raddr")
Log.Debugf("Querying for a raddr")
qptr, _ = assertExchange(t, "3.2.2.10.in-addr.arpa.", dns.TypePTR, testPort, 1, 1, 0)
qname, _ = assertExchange(t, testName1, dns.TypeA, testPort, 1, 1, 0)
qotherName, _ = assertExchange(t, testName2, dns.TypeA, testPort, 1, 1, 0)
@@ -148,7 +148,7 @@ func TestServerDbCacheInvalidation(t *testing.T) {
assertInCache(t, cache, qotherPtr, "after asking for second address")
// let's repeat this, but adding an IP
Debug.Printf("... and adding a new IP should invalidate both the cached responses for the name")
Log.Debugf("... and adding a new IP should invalidate both the cached responses for the name")
zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.7"))
assertNotInCache(t, cache, qname, "after adding a new IP")
assertInCache(t, cache, qotherName, "after adding a new IP")
@@ -178,11 +178,11 @@ func TestServerCacheExpiration(t *testing.T) {
)
InitDefaultLogging(testing.Verbose())
Info.Println("TestServerCacheExpiration starting")
Log.Infoln("TestServerCacheExpiration starting")
clk := newMockedClock()
Debug.Printf("Creating 2 zone databases")
Log.Debugf("Creating 2 zone databases")
zoneConfig := ZoneConfig{
RefreshInterval: 0, // no name updates
Clock: clk,
@@ -191,11 +191,11 @@ func TestServerCacheExpiration(t *testing.T) {
dbs.Start()
defer dbs.Stop()
Debug.Printf("Creating a cache")
Log.Debugf("Creating a cache")
cache, err := NewCache(1024, clk)
require.NoError(t, err)
Debug.Printf("Creating a real DNS server for the first zone database and with the cache")
Log.Debugf("Creating a real DNS server for the first zone database and with the cache")
srv, err := NewDNSServer(DNSServerConfig{
Zone: dbs[0].Zone,
Cache: cache,
@@ -225,7 +225,7 @@ func TestServerCacheExpiration(t *testing.T) {
assertNotInCache(t, cache, qName1, fmt.Sprintf("after asking for %s", testName1))
// We add the IP in the second peer right after
Debug.Printf("Adding an IPs to %s in database #2", testName1)
Log.Debugf("Adding an IPs to %s in database #2", testName1)
dbs[1].Zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.1"))
t.Logf("Zone database #2:\n%s", dbs[1].Zone)
@@ -241,7 +241,7 @@ func TestServerCacheExpiration(t *testing.T) {
assertNotInCache(t, cache, qName1, fmt.Sprintf("after asking for %s", testName1))
// We delete the IP in the second peer right after
Debug.Printf("Removing the IPs from %s in database #2", testName1)
Log.Debugf("Removing the IPs from %s in database #2", testName1)
dbs[1].Zone.DeleteRecords(containerID, "", net.ParseIP("10.2.2.1"))
// Check that we return to the initial state: a neg-local entry in the cache
@@ -266,10 +266,10 @@ func TestServerCacheRefresh(t *testing.T) {
)
InitDefaultLogging(testing.Verbose())
Info.Println("TestServerCacheRefresh starting")
Log.Infoln("TestServerCacheRefresh starting")
clk := newMockedClock()
Debug.Printf("Creating 2 zone databases")
Log.Debugf("Creating 2 zone databases")
zoneConfig := ZoneConfig{
RefreshInterval: refreshInterval,
Clock: clk,
@@ -278,11 +278,11 @@ func TestServerCacheRefresh(t *testing.T) {
dbs.Start()
defer dbs.Stop()
Debug.Printf("Creating a cache")
Log.Debugf("Creating a cache")
cache, err := NewCache(1024, clk)
require.NoError(t, err)
Debug.Printf("Creating a real DNS server for the first zone database and with the cache")
Log.Debugf("Creating a real DNS server for the first zone database and with the cache")
srv, err := NewDNSServer(DNSServerConfig{
Zone: dbs[0].Zone,
Cache: cache,
@@ -301,7 +301,7 @@ func TestServerCacheRefresh(t *testing.T) {
require.NoError(t, err)
require.NotEqual(t, 0, testPort, "listen port")
Debug.Printf("Adding an IPs to %s", testName1)
Log.Debugf("Adding an IPs to %s", testName1)
dbs[1].Zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.1"))
dbs.Flush() // ensure the immediate refresh is done
@@ -317,7 +317,7 @@ func TestServerCacheRefresh(t *testing.T) {
clk.Forward(refreshInterval / 2)
dbs.Flush()
Debug.Printf("Adding an IP to %s and to %s", testName1, testName2)
Log.Debugf("Adding an IP to %s and to %s", testName1, testName2)
dbs[1].Zone.AddRecord(containerID, testName1, net.ParseIP("10.2.2.2"))
dbs[1].Zone.AddRecord(containerID, testName2, net.ParseIP("10.9.9.2"))
+2 -2
View File
@@ -41,7 +41,7 @@ func TestUDPDNSServer(t *testing.T) {
testCIDR1 := testAddr1 + "/24"
InitDefaultLogging(testing.Verbose())
Info.Println("TestUDPDNSServer starting")
Log.Infoln("TestUDPDNSServer starting")
zone, err := NewZoneDb(ZoneConfig{})
require.NoError(t, err)
@@ -138,7 +138,7 @@ func TestTCPDNSServer(t *testing.T) {
)
InitDefaultLogging(testing.Verbose())
Info.Println("TestTCPDNSServer starting")
Log.Infoln("TestTCPDNSServer starting")
zone, err := NewZoneDb(ZoneConfig{})
require.NoError(t, err)
+9 -9
View File
@@ -111,7 +111,7 @@ func (re *recordEntry) addIPObserver(zro ZoneRecordObserver) {
func (re *recordEntry) notifyIPObservers() {
numObservers := len(re.observers)
if numObservers > 0 {
Debug.Printf("[zonedb] Notifying %d observers of '%s'", numObservers, re.ip)
Log.Debugf("[zonedb] Notifying %d observers of '%s'", numObservers, re.ip)
for _, observer := range re.observers {
observer()
}
@@ -257,7 +257,7 @@ func (n *name) addNameObserver(observer ZoneRecordObserver) {
func (n *name) notifyNameObservers() {
numObservers := len(n.observers)
if numObservers > 0 {
Debug.Printf("[zonedb] Notifying %d observers of '%s'", numObservers, n.name)
Log.Debugf("[zonedb] Notifying %d observers of '%s'", numObservers, n.name)
for _, observer := range n.observers {
observer()
}
@@ -373,7 +373,7 @@ func (ns *nameSet) getNameLastAccess(n string) time.Time {
// The access time is saved only for locally-introduced records (otherwise it could
// be lost when names are irrelevant...)
func (ns *nameSet) touchName(name string, now time.Time) {
Debug.Printf("[zonedb] Touching name %s", name)
Log.Debugf("[zonedb] Touching name %s", name)
n := ns.getName(name, false)
if n != nil {
n.lastAccessTime = now
@@ -506,9 +506,9 @@ func NewZoneDb(config ZoneConfig) (zone *ZoneDb, err error) {
// Start the zone database
func (zone *ZoneDb) Start() (err error) {
if zone.iface != nil {
Info.Printf("[zonedb] Using mDNS on %+v", zone.iface)
Log.Infof("[zonedb] Using mDNS on %+v", zone.iface)
} else {
Info.Printf("[zonedb] Using mDNS on all interfaces")
Log.Infof("[zonedb] Using mDNS on all interfaces")
}
if err = zone.mdnsCli.Start(zone.iface); err != nil {
@@ -527,11 +527,11 @@ func (zone *ZoneDb) Start() (err error) {
// Perform a graceful shutdown of the zone database
func (zone *ZoneDb) Stop() error {
if zone.refreshInterval > 0 {
Debug.Printf("[zonedb] Closing background updaters...")
Log.Debugf("[zonedb] Closing background updaters...")
zone.refreshScheds.Stop()
}
Debug.Printf("[zonedb] Exiting mDNS client and server...")
Log.Debugf("[zonedb] Exiting mDNS client and server...")
zone.mdnsCli.Stop()
zone.mdnsSrv.Stop()
return nil
@@ -555,7 +555,7 @@ func (zone *ZoneDb) Status() string {
func (zone *ZoneDb) AddRecord(ident string, name string, ip net.IP) (err error) {
zone.mx.Lock()
defer zone.mx.Unlock()
Debug.Printf("[zonedb] Adding record: '%s'/'%s'[%s]", ident, name, ip)
Log.Debugf("[zonedb] Adding record: '%s'/'%s'[%s]", ident, name, ip)
record := Record{dns.Fqdn(name), ip, 0, 0, 0}
_, err = zone.getNameSet(ident).addIPToName(record, zone.clock.Now())
return
@@ -638,7 +638,7 @@ func (zone *ZoneDb) String() string {
// Notify that a container has died
func (zone *ZoneDb) ContainerDied(ident string) error {
Info.Printf("[zonedb] Container %s down. Removing records", ident)
Log.Infof("[zonedb] Container %s down. Removing records", ident)
zone.DeleteRecords(ident, "", net.IP{})
return nil
}
+18 -18
View File
@@ -63,7 +63,7 @@ func (zone *ZoneDb) LookupName(name string) (res []ZoneRecord, err error) {
// note: LookupName() is usually called from the mDNS server, so we do not touch the name
name = dns.Fqdn(name)
Debug.Printf("[zonedb] Looking for name '%s' in local database", name)
Log.Debugf("[zonedb] Looking for name '%s' in local database", name)
return zone.lookup(name, func(ns *nameSet) []*recordEntry { return ns.getEntriesForName(name) })
}
@@ -78,23 +78,23 @@ func (zone *ZoneDb) LookupInaddr(inaddr string) (res []ZoneRecord, err error) {
if err != nil {
return nil, newParseError("lookup address", inaddr)
}
Debug.Printf("[zonedb] Looking for address in local database: '%s' (%s)", revIPv4, inaddr)
Log.Debugf("[zonedb] Looking for address in local database: '%s' (%s)", revIPv4, inaddr)
return zone.lookup(inaddr, func(ns *nameSet) []*recordEntry { return ns.getEntriesForIP(revIPv4) })
}
// Perform a domain lookup with mDNS
func (zone *ZoneDb) domainLookup(target string, lfun ZoneLookupFunc) (res []ZoneRecord, err error) {
// no local results have been obtained in the local database: try with a mDNS query
Debug.Printf("[zonedb] '%s' not in local database... trying with mDNS", target)
Log.Debugf("[zonedb] '%s' not in local database... trying with mDNS", target)
lanswers, err := lfun(target)
if err != nil {
Debug.Printf("[zonedb] mDNS lookup error for '%s': %s", target, err)
Log.Debugf("[zonedb] mDNS lookup error for '%s': %s", target, err)
return nil, err
}
// if the request has been successful, save the IP in the local database and return the corresponding ZoneRecord
// (we do not get the remote ident in the mDNS reply, so we save it in a "remote" ident)
Debug.Printf("[zonedb] adding '%s' (obtained with mDNS) to '%s'", lanswers, target)
Log.Debugf("[zonedb] adding '%s' (obtained with mDNS) to '%s'", lanswers, target)
res = make([]ZoneRecord, len(lanswers))
zone.mx.Lock()
now := zone.clock.Now()
@@ -104,7 +104,7 @@ func (zone *ZoneDb) domainLookup(target string, lfun ZoneLookupFunc) (res []Zone
r, err := remoteIdent.addIPToName(answer, now)
if err != nil {
zone.mx.Unlock()
Warning.Printf("[zonedb] '%s' insertion for %s failed: %s", answer, target, err)
Log.Warningf("[zonedb] '%s' insertion for %s failed: %s", answer, target, err)
return nil, err
}
uniq.add(r)
@@ -118,7 +118,7 @@ func (zone *ZoneDb) domainLookup(target string, lfun ZoneLookupFunc) (res []Zone
// The name can be resolved locally with the local database or with some other resolution method (eg, a mDNS query)
func (zone *ZoneDb) DomainLookupName(name string) (res []ZoneRecord, err error) {
name = dns.Fqdn(name)
Debug.Printf("[zonedb] Looking for name '%s' in local(&remote) database", name)
Log.Debugf("[zonedb] Looking for name '%s' in local(&remote) database", name)
zone.mx.Lock()
now := zone.clock.Now()
@@ -128,7 +128,7 @@ func (zone *ZoneDb) DomainLookupName(name string) (res []ZoneRecord, err error)
// filter the entries with expired TTL
// locally introduced entries are never expired: they always have TTL=0
if ze.hasExpired(now) {
Debug.Printf("[zonedb] '%s': expired entry '%s' ignored: removing", name, ze)
Log.Debugf("[zonedb] '%s': expired entry '%s' ignored: removing", name, ze)
nameset.deleteNameIP(name, net.IP{})
} else {
uniq.add(ze)
@@ -141,7 +141,7 @@ func (zone *ZoneDb) DomainLookupName(name string) (res []ZoneRecord, err error)
zone.mx.Unlock()
if len(uniq) > 0 {
Debug.Printf("[zonedb] '%s' resolved in local database", name)
Log.Debugf("[zonedb] '%s' resolved in local database", name)
res = uniq.toSlice()
} else {
res, err = zone.domainLookup(name, zone.mdnsCli.LookupName)
@@ -163,7 +163,7 @@ func (zone *ZoneDb) DomainLookupInaddr(inaddr string) (res []ZoneRecord, err err
return nil, newParseError("lookup address", inaddr)
}
Debug.Printf("[zonedb] Looking for address in local(&remote) database: '%s' (%s)", revIPv4, inaddr)
Log.Debugf("[zonedb] Looking for address in local(&remote) database: '%s' (%s)", revIPv4, inaddr)
zone.mx.Lock()
now := zone.clock.Now()
@@ -173,7 +173,7 @@ func (zone *ZoneDb) DomainLookupInaddr(inaddr string) (res []ZoneRecord, err err
// filter the entries with expired TTL
// locally introduced entries are never expired: they always have TTL=0
if ze.hasExpired(now) {
Debug.Printf("[zonedb] '%s': expired entry '%s' ignored: removing", revIPv4, ze)
Log.Debugf("[zonedb] '%s': expired entry '%s' ignored: removing", revIPv4, ze)
nameset.deleteNameIP("", revIPv4.toNetIP())
} else {
uniq.add(ze)
@@ -186,7 +186,7 @@ func (zone *ZoneDb) DomainLookupInaddr(inaddr string) (res []ZoneRecord, err err
zone.mx.Unlock()
if len(uniq) > 0 {
Debug.Printf("[zonedb] '%s' resolved in local database", inaddr)
Log.Debugf("[zonedb] '%s' resolved in local database", inaddr)
res = uniq.toSlice()
} else {
res, err = zone.domainLookup(inaddr, zone.mdnsCli.LookupInaddr)
@@ -243,7 +243,7 @@ func (zone *ZoneDb) startUpdatingName(name string) {
n.lastRefreshTime = now
zone.mx.Unlock()
Debug.Printf("[zonedb] Creating new immediate refresh request for '%s'", name)
Log.Debugf("[zonedb] Creating new immediate refresh request for '%s'", name)
zone.refreshScheds.Add(func() time.Time { return zone.updater(name) }, now)
} else {
zone.mx.Unlock()
@@ -261,7 +261,7 @@ func (zone *ZoneDb) updater(name string) (nextTime time.Time) {
// if nobody has asked for this name for long time, just forget about it...
if !zone.IsNameRelevant(name) || zone.IsNameExpired(name) {
Debug.Printf("[zonedb] '%s' seem to be irrelevant now: removing any remote information", name)
Log.Debugf("[zonedb] '%s' seem to be irrelevant now: removing any remote information", name)
deleteRemoteInfo()
return
}
@@ -269,7 +269,7 @@ func (zone *ZoneDb) updater(name string) (nextTime time.Time) {
// perform the refresh for this name
fullName := dns.Fqdn(name)
startTime := zone.clock.Now()
Debug.Printf("[zonedb] Refreshing name '%s' with mDNS...", fullName)
Log.Debugf("[zonedb] Refreshing name '%s' with mDNS...", fullName)
res, _ := zone.mdnsCli.InsistentLookupName(fullName)
if res != nil && len(res) > 0 {
numIps := len(res)
@@ -277,14 +277,14 @@ func (zone *ZoneDb) updater(name string) (nextTime time.Time) {
now := zone.clock.Now()
added, removed := zone.getNameSet(defaultRemoteIdent).getName(name, true).updateIPs(res, now)
zone.mx.Unlock()
Debug.Printf("[zonedb] Obtained %d IPs for name '%s' with mDNS: %d added, %d removed",
Log.Debugf("[zonedb] Obtained %d IPs for name '%s' with mDNS: %d added, %d removed",
numIps, name, added, removed)
// once the name has been updated, we re-schedule the update
nextTime = startTime.Add(zone.refreshInterval)
Debug.Printf("[zonedb] Rescheduling update for '%s' in %s", name, nextTime.Sub(zone.clock.Now()))
Log.Debugf("[zonedb] Rescheduling update for '%s' in %s", name, nextTime.Sub(zone.clock.Now()))
} else {
Debug.Printf("[zonedb] nobody knows about '%s'... removing", name)
Log.Debugf("[zonedb] nobody knows about '%s'... removing", name)
deleteRemoteInfo()
}
return
+22 -22
View File
@@ -24,7 +24,7 @@ func TestZoneRefresh(t *testing.T) {
)
InitDefaultLogging(testing.Verbose())
Info.Println("TestZoneRefresh starting")
Log.Infoln("TestZoneRefresh starting")
clk := newMockedClock()
@@ -40,77 +40,77 @@ func TestZoneRefresh(t *testing.T) {
time.Sleep(100 * time.Millisecond) // allow for server to get going
Debug.Printf("Adding '%s' to Db #1", name)
Log.Debugf("Adding '%s' to Db #1", name)
dbs[0].Zone.AddRecord("someident", name, net.ParseIP(addr1))
Debug.Printf("Checking that the name %s is relevant (as it has been locally inserted) and not remote", name)
Log.Debugf("Checking that the name %s is relevant (as it has been locally inserted) and not remote", name)
require.True(t, dbs[0].Zone.IsNameRelevant(name), "name relevant")
require.True(t, dbs[0].Zone.HasNameLocalInfo(name), "local name info")
require.False(t, dbs[0].Zone.HasNameRemoteInfo(name), "remote name info")
Debug.Printf("Asking for '%s' to Db #1: should get 1 IP from the local database...", name)
Log.Debugf("Asking for '%s' to Db #1: should get 1 IP from the local database...", name)
res, err := dbs[0].Zone.DomainLookupName(name)
require.NoError(t, err)
Debug.Printf("Got: %s", res)
Log.Debugf("Got: %s", res)
t.Logf("Db #1 after the lookup:\n%s", dbs[0].Zone)
require.Equal(t, 1, len(res), "lookup result")
clk.Forward(refreshInterval / 2)
dbs.Flush()
Debug.Printf("A couple of seconds later, we should still have one IP for that name")
Log.Debugf("A couple of seconds later, we should still have one IP for that name")
res, err = dbs[0].Zone.DomainLookupName(name)
require.NoError(t, err)
require.Equal(t, 1, len(res), "lookup result")
Debug.Printf("And then we add 2 IPs for that name at ZoneDb 2")
Log.Debugf("And then we add 2 IPs for that name at ZoneDb 2")
clk.Forward(1)
dbs.Flush()
Debug.Printf("Adding 2 IPs to '%s' in Db #2", name)
Log.Debugf("Adding 2 IPs to '%s' in Db #2", name)
dbs[1].Zone.AddRecord("someident", name, net.ParseIP(addr2))
dbs[1].Zone.AddRecord("someident", name, net.ParseIP(addr3))
Debug.Printf("Perform a lookup, to ensure the name will be updated in the background...")
Log.Debugf("Perform a lookup, to ensure the name will be updated in the background...")
dbs[0].Zone.DomainLookupName(name)
Debug.Printf("Wait for a while, until a refresh is performed...")
Log.Debugf("Wait for a while, until a refresh is performed...")
clk.Forward(refreshInterval + 1)
dbs.Flush()
Debug.Printf("A refresh should have been scheduled now: we should have 3 IPs:")
Debug.Printf("the first (local) IP and the others obtained from zone2 with a mDNS query")
Debug.Printf("Asking for '%s' again... we should have 3 IPs now", name)
Log.Debugf("A refresh should have been scheduled now: we should have 3 IPs:")
Log.Debugf("the first (local) IP and the others obtained from zone2 with a mDNS query")
Log.Debugf("Asking for '%s' again... we should have 3 IPs now", name)
res, err = dbs[0].Zone.DomainLookupName(name)
Debug.Printf("Got: %s", res)
Log.Debugf("Got: %s", res)
t.Logf("Db #1 after the second lookup:\n%s", dbs[0].Zone)
require.Equal(t, 3, len(res), "lookup result length")
Debug.Printf("We will not ask for `name` for a while, so it will become irrelevant and will be removed...")
Log.Debugf("We will not ask for `name` for a while, so it will become irrelevant and will be removed...")
clk.Forward(refreshInterval + relevantTime + 1)
dbs.Flush()
// the name should be irrelevant now, and all remote info should have been
// removed from the zone database
Debug.Printf("Name '%s' should not be in the remote database in ZoneDb 1", name)
Log.Debugf("Name '%s' should not be in the remote database in ZoneDb 1", name)
require.False(t, dbs[0].Zone.IsNameRelevant(name), "name still relevant after some inactivity time")
require.True(t, dbs[0].Zone.HasNameLocalInfo(name), "local name info")
require.False(t, dbs[0].Zone.HasNameRemoteInfo(name), "remote name info")
Debug.Printf("There is no remote info about this name at zone 1: a new IP appears remotely meanwhile...")
Log.Debugf("There is no remote info about this name at zone 1: a new IP appears remotely meanwhile...")
clk.Forward(1)
dbs.Flush()
Debug.Printf("Adding '%s' to Db #2", name)
Log.Debugf("Adding '%s' to Db #2", name)
dbs[1].Zone.AddRecord("someident", name, net.ParseIP(addr4))
Debug.Printf("When we ask about this name again, we get 4 IPs (1 local, 3 remote)")
Debug.Printf("Asking for '%s' again... the first lookup will return only the local results", name)
Log.Debugf("When we ask about this name again, we get 4 IPs (1 local, 3 remote)")
Log.Debugf("Asking for '%s' again... the first lookup will return only the local results", name)
res, err = dbs[0].Zone.DomainLookupName(name)
require.Equal(t, 1, len(res), "lookup result length")
Debug.Printf("... but a second lookup should return all the results in the network")
Log.Debugf("... but a second lookup should return all the results in the network")
clk.Forward(refreshInterval + 1)
dbs.Flush()
res, err = dbs[0].Zone.DomainLookupName(name)
Debug.Printf("Got: %s", res)
Log.Debugf("Got: %s", res)
require.Equal(t, 4, len(res), "lookup result length")
}
+16 -1
View File
@@ -16,7 +16,7 @@ func CheckNetworkFree(subnet *net.IPNet) error {
}
for _, route := range routes {
if route.Dst != nil && overlaps(route.Dst, subnet) {
return fmt.Errorf("network %s would overlap with route %s", subnet, route.Dst)
return fmt.Errorf("Network %s overlaps with existing route %s on host.", subnet, route.Dst)
}
}
return nil
@@ -26,3 +26,18 @@ func CheckNetworkFree(subnet *net.IPNet) error {
func overlaps(n1, n2 *net.IPNet) bool {
return n1.Contains(n2.IP) || n2.Contains(n1.IP)
}
// For a specific address, we only care if it is actually *inside* an
// existing route, because weave-local traffic never hits IP routing.
func CheckAddressOverlap(addr net.IP) error {
routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
if err != nil {
return err
}
for _, route := range routes {
if route.Dst != nil && route.Dst.Contains(addr) {
return fmt.Errorf("Address %s overlaps with existing route %s on host.", addr, route.Dst)
}
}
return nil
}
+10 -5
View File
@@ -1,4 +1,4 @@
/* netcheck: check whether a given network overlaps with any existing routes */
/* netcheck: check whether a given network or address overlaps with any existing routes */
package main
import (
@@ -10,7 +10,7 @@ import (
)
func fatal(err error) {
fmt.Println(err)
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
@@ -19,12 +19,17 @@ func main() {
os.Exit(0)
}
ipRangeStr := os.Args[1]
_, ipnet, err := net.ParseCIDR(ipRangeStr)
cidrStr := os.Args[1]
addr, ipnet, err := net.ParseCIDR(cidrStr)
if err != nil {
fatal(err)
}
if err := weavenet.CheckNetworkFree(ipnet); err != nil {
if ipnet.IP.Equal(addr) {
err = weavenet.CheckNetworkFree(ipnet)
} else {
err = weavenet.CheckAddressOverlap(addr)
}
if err != nil {
fatal(err)
}
os.Exit(0)
+20 -20
View File
@@ -71,17 +71,17 @@ func main() {
}
InitDefaultLogging(debug)
Info.Printf("[main] WeaveDNS version %s\n", version) // first thing in log: the version
Log.Infof("[main] WeaveDNS version %s\n", version) // first thing in log: the version
var iface *net.Interface
if ifaceName != "" {
var err error
Info.Println("[main] Waiting for mDNS interface", ifaceName, "to come up")
Log.Infoln("[main] Waiting for mDNS interface", ifaceName, "to come up")
iface, err = weavenet.EnsureInterface(ifaceName, wait)
if err != nil {
Error.Fatal(err)
Log.Fatal(err)
} else {
Info.Println("[main] Interface", ifaceName, "is up")
Log.Infoln("[main] Interface", ifaceName, "is up")
}
}
@@ -89,25 +89,25 @@ func main() {
if httpIfaceName == "" {
httpIP = "0.0.0.0"
} else {
Info.Println("[main] Waiting for HTTP interface", httpIfaceName, "to come up")
Log.Infoln("[main] Waiting for HTTP interface", httpIfaceName, "to come up")
httpIface, err := weavenet.EnsureInterface(httpIfaceName, wait)
if err != nil {
Error.Fatal(err)
Log.Fatal(err)
}
Info.Println("[main] Interface", httpIfaceName, "is up")
Log.Infoln("[main] Interface", httpIfaceName, "is up")
addrs, err := httpIface.Addrs()
if err != nil {
Error.Fatal(err)
Log.Fatal(err)
}
if len(addrs) == 0 {
Error.Fatal("[main] No addresses on HTTP interface")
Log.Fatal("[main] No addresses on HTTP interface")
}
ip, _, err := net.ParseCIDR(addrs[0].String())
if err != nil {
Error.Fatal(err)
Log.Fatal(err)
}
httpIP = ip.String()
@@ -124,22 +124,22 @@ func main() {
}
zone, err := weavedns.NewZoneDb(zoneConfig)
if err != nil {
Error.Fatal("[main] Unable to initialize the Zone database", err)
Log.Fatal("[main] Unable to initialize the Zone database", err)
}
if err := zone.Start(); err != nil {
Error.Fatal("[main] Unable to start the Zone database", err)
Log.Fatal("[main] Unable to start the Zone database", err)
}
defer zone.Stop()
dockerCli, err = docker.NewClient(apiPath)
if err != nil {
Error.Fatal("[main] Unable to start docker client: ", err)
Log.Fatal("[main] Unable to start docker client: ", err)
}
if watch {
err := dockerCli.AddObserver(zone)
if err != nil {
Error.Fatal("[main] Unable to start watcher", err)
Log.Fatal("[main] Unable to start watcher", err)
}
}
@@ -158,29 +158,29 @@ func main() {
if len(fallback) > 0 {
fallbackHost, fallbackPort, err := net.SplitHostPort(fallback)
if err != nil {
Error.Fatal("[main] Could not parse fallback host and port", err)
Log.Fatal("[main] Could not parse fallback host and port", err)
}
srvConfig.UpstreamCfg = &dns.ClientConfig{Servers: []string{fallbackHost}, Port: fallbackPort}
Debug.Printf("[main] DNS fallback at %s:%s", fallbackHost, fallbackPort)
Log.Debugf("[main] DNS fallback at %s:%s", fallbackHost, fallbackPort)
}
srv, err := weavedns.NewDNSServer(srvConfig)
if err != nil {
Error.Fatal("[main] Failed to initialize the WeaveDNS server", err)
Log.Fatal("[main] Failed to initialize the WeaveDNS server", err)
}
httpListener, err := net.Listen("tcp", httpAddr)
if err != nil {
Error.Fatal("[main] Unable to create http listener: ", err)
Log.Fatal("[main] Unable to create http listener: ", err)
}
Info.Println("[main] HTTP API listening on", httpAddr)
Log.Infoln("[main] HTTP API listening on", httpAddr)
go SignalHandlerLoop(srv)
go weavedns.ServeHTTP(httpListener, version, srv, dockerCli)
err = srv.Start()
if err != nil {
Error.Fatal("[main] Failed to start the WeaveDNS server: ", err)
Log.Fatal("[main] Failed to start the WeaveDNS server: ", err)
}
srv.ActivateAndServe()
}
+12 -23
View File
@@ -5,28 +5,29 @@ import (
"os"
"strings"
"code.google.com/p/getopt"
"github.com/pborman/getopt"
. "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/proxy"
)
var (
version = "(unreleased version)"
defaultListenAddr = "tcp://0.0.0.0:12375"
version = "(unreleased version)"
defaultListenAddrs = []string{"tcp://0.0.0.0:12375", "unix:///var/run/weave.sock"}
)
func main() {
var (
debug bool
justVersion bool
c = proxy.Config{ListenAddr: defaultListenAddr}
c = proxy.Config{ListenAddrs: defaultListenAddrs}
)
c.Version = version
getopt.BoolVarLong(&debug, "debug", 'd', "log debugging information")
getopt.BoolVarLong(&justVersion, "version", 0, "print version and exit")
getopt.StringVar(&c.ListenAddr, 'H', fmt.Sprintf("address on which to listen (default %s)", defaultListenAddr))
getopt.BoolVarLong(&c.NoDefaultIPAM, "no-default-ipam", 0, "do not automatically allocate addresses for containers without a WEAVE_CIDR")
getopt.ListVar(&c.ListenAddrs, 'H', fmt.Sprintf("address on which to listen (default %s)", defaultListenAddrs))
getopt.BoolVarLong(&c.NoDefaultIPAM, "no-default-ipalloc", 0, "do not automatically allocate addresses for containers without a WEAVE_CIDR")
getopt.BoolVarLong(&c.NoDefaultIPAM, "no-default-ipam", 0, "do not automatically allocate addresses for containers without a WEAVE_CIDR (deprecated; please use --no-default-ipalloc")
getopt.StringVarLong(&c.TLSConfig.CACert, "tlscacert", 0, "Trust certs signed only by this CA")
getopt.StringVarLong(&c.TLSConfig.Cert, "tlscert", 0, "Path to TLS certificate file")
getopt.BoolVarLong(&c.TLSConfig.Enabled, "tls", 0, "Use TLS; implied by --tlsverify")
@@ -42,32 +43,20 @@ func main() {
}
if c.WithDNS && c.WithoutDNS {
Error.Fatalf("Cannot use both '--with-dns' and '--without-dns' flags")
Log.Fatalf("Cannot use both '--with-dns' and '--without-dns' flags")
}
if debug {
InitDefaultLogging(true)
}
Info.Println("weave proxy", version)
Info.Println("Command line arguments:", strings.Join(os.Args[1:], " "))
protoAddrParts := strings.SplitN(c.ListenAddr, "://", 2)
if len(protoAddrParts) == 2 {
if protoAddrParts[0] != "tcp" {
Error.Fatalf("Invalid protocol format: %q", protoAddrParts[0])
}
c.ListenAddr = protoAddrParts[1]
} else {
c.ListenAddr = protoAddrParts[0]
}
Log.Infoln("weave proxy", version)
Log.Infoln("Command line arguments:", strings.Join(os.Args[1:], " "))
p, err := proxy.NewProxy(c)
if err != nil {
Error.Fatalf("Could not start proxy: %s", err)
Log.Fatalf("Could not start proxy: %s", err)
}
if err := p.ListenAndServe(); err != nil {
Error.Fatalf("Could not listen on %s: %s", p.ListenAddr, err)
}
p.ListenAndServe()
}
+31 -31
View File
@@ -4,14 +4,12 @@ import (
"crypto/sha256"
"flag"
"fmt"
"log"
"net"
"net/http"
"os"
"runtime"
"strings"
"code.google.com/p/gopacket/layers"
"github.com/davecheney/profile"
"github.com/gorilla/mux"
. "github.com/weaveworks/weave/common"
@@ -25,10 +23,6 @@ import (
var version = "(unreleased version)"
func main() {
log.SetPrefix(weave.Protocol + " ")
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
procs := runtime.NumCPU()
// packet sniffing can block an OS thread, so we need one thread
// for that plus at least one more.
@@ -85,33 +79,33 @@ func main() {
os.Exit(0)
}
log.Println("Command line options:", options())
log.Println("Command line peers:", peers)
Log.Println("Command line options:", options())
Log.Println("Command line peers:", peers)
var err error
if ifaceName != "" {
config.Iface, err = weavenet.EnsureInterface(ifaceName, wait)
if err != nil {
log.Fatal(err)
Log.Fatal(err)
}
}
if routerName == "" {
if config.Iface == nil {
log.Fatal("Either an interface must be specified with -iface or a name with -name")
Log.Fatal("Either an interface must be specified with -iface or a name with -name")
}
routerName = config.Iface.HardwareAddr.String()
}
name, err := weave.PeerNameFromUserInput(routerName)
if err != nil {
log.Fatal(err)
Log.Fatal(err)
}
if nickName == "" {
nickName, err = os.Hostname()
if err != nil {
log.Fatal(err)
Log.Fatal(err)
}
}
@@ -119,10 +113,10 @@ func main() {
password = os.Getenv("WEAVE_PASSWORD")
}
if password == "" {
log.Println("Communication between peers is unencrypted.")
Log.Println("Communication between peers is unencrypted.")
} else {
config.Password = []byte(password)
log.Println("Communication between peers is encrypted.")
Log.Println("Communication between peers is encrypted.")
}
if prof != "" {
@@ -137,7 +131,7 @@ func main() {
config.PeerDiscovery = !noDiscovery
router := weave.NewRouter(config, name, nickName)
log.Println("Our name is", router.Ourself)
Log.Println("Our name is", router.Ourself)
var allocator *ipam.Allocator
var defaultSubnet address.CIDR
@@ -146,18 +140,18 @@ func main() {
allocator, defaultSubnet = createAllocator(router, iprangeCIDR, ipsubnetCIDR, determineQuorum(peerCount, peers))
dockerCli, err = docker.NewClient(apiPath)
if err != nil {
Error.Fatal("Unable to start docker client: ", err)
Log.Fatal("Unable to start docker client: ", err)
}
if err = dockerCli.AddObserver(allocator); err != nil {
Error.Fatal("Unable to start watcher", err)
Log.Fatal("Unable to start watcher", err)
}
} else if peerCount > 0 {
log.Fatal("-initpeercount flag specified without -iprange")
Log.Fatal("-initpeercount flag specified without -iprange")
}
router.Start()
if errors := router.ConnectionMaker.InitiateConnections(peers, false); len(errors) > 0 {
log.Fatal(errorMessages(errors))
Log.Fatal(errorMessages(errors))
}
// The weave script always waits for a status call to succeed,
@@ -192,25 +186,31 @@ func options() map[string]string {
func logFrameFunc(debug bool) weave.LogFrameFunc {
if !debug {
return func(prefix string, frame []byte, eth *layers.Ethernet) {}
return func(prefix string, frame []byte, dec *weave.EthernetDecoder) {}
}
return func(prefix string, frame []byte, eth *layers.Ethernet) {
return func(prefix string, frame []byte, dec *weave.EthernetDecoder) {
h := fmt.Sprintf("%x", sha256.Sum256(frame))
if eth == nil {
log.Println(prefix, len(frame), "bytes (", h, ")")
} else {
log.Println(prefix, len(frame), "bytes (", h, "):", eth.SrcMAC, "->", eth.DstMAC)
parts := []interface{}{prefix, len(frame), "bytes (", h, ")"}
if dec != nil {
parts = append(parts, dec.Eth.SrcMAC, "->", dec.Eth.DstMAC)
if dec.DF() {
parts = append(parts, "(DF)")
}
}
Log.Println(parts...)
}
}
func parseAndCheckCIDR(cidrStr string) address.CIDR {
_, cidr, err := address.ParseCIDR(cidrStr)
if err != nil {
log.Fatal(err)
Log.Fatal(err)
}
if cidr.Size() < ipam.MinSubnetSize {
log.Fatalf("Allocation range smaller than minimum size %d: %s", ipam.MinSubnetSize, cidrStr)
Log.Fatalf("Allocation range smaller than minimum size %d: %s", ipam.MinSubnetSize, cidrStr)
}
return cidr
}
@@ -221,7 +221,7 @@ func createAllocator(router *weave.Router, ipRangeStr string, defaultSubnetStr s
if defaultSubnetStr != "" {
defaultSubnet = parseAndCheckCIDR(defaultSubnetStr)
if !ipRange.Range().Overlaps(defaultSubnet.Range()) {
log.Fatalf("Default subnet %s out of bounds: %s", defaultSubnet, ipRange)
Log.Fatalf("Default subnet %s out of bounds: %s", defaultSubnet, ipRange)
}
}
allocator := ipam.NewAllocator(router.Ourself.Peer.Name, router.Ourself.Peer.UID, router.Ourself.Peer.NickName, ipRange.Range(), quorum)
@@ -248,7 +248,7 @@ func determineQuorum(initPeerCountFlag int, peers []string) uint {
// specify it explicitly if that becomes a problem.
clusterSize := uint(len(peers) + 1)
quorum := clusterSize/2 + 1
log.Println("Assuming quorum size of", quorum)
Log.Println("Assuming quorum size of", quorum)
return quorum
}
@@ -298,11 +298,11 @@ func handleHTTP(router *weave.Router, httpAddr string, allocator *ipam.Allocator
}
l, err := net.Listen(protocol, httpAddr)
if err != nil {
log.Fatal("Unable to create http listener socket: ", err)
Log.Fatal("Unable to create http listener socket: ", err)
}
err = http.Serve(l, nil)
if err != nil {
log.Fatal("Unable to create http server", err)
Log.Fatal("Unable to create http server", err)
}
}
+3 -3
View File
@@ -20,7 +20,7 @@ var (
func callWeave(args ...string) ([]byte, error) {
args = append([]string{"--local"}, args...)
Debug.Print("Calling weave", args)
Log.Debug("Calling weave", args)
cmd := exec.Command("./weave", args...)
cmd.Env = []string{"PROCFS=/hostproc", "PATH=/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}
out, err := cmd.CombinedOutput()
@@ -41,14 +41,14 @@ func inspectContainerInPath(client *docker.Client, path string) (*docker.Contain
subs := containerIDRegexp.FindStringSubmatch(path)
if subs == nil {
err := fmt.Errorf("No container id found in request with path %s", path)
Warning.Println(err)
Log.Warningln(err)
return nil, err
}
containerID := subs[2]
container, err := client.InspectContainer(containerID)
if err != nil {
Warning.Printf("Error inspecting container %s: %v", containerID, err)
Log.Warningf("Error inspecting container %s: %v", containerID, err)
}
return container, err
}
+12 -6
View File
@@ -13,6 +13,8 @@ import (
"github.com/weaveworks/weave/nameserver"
)
const MaxDockerHostname = 64
type createContainerInterceptor struct{ proxy *Proxy }
type createContainerRequestBody struct {
@@ -45,7 +47,7 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
}
if cidrs, ok := i.proxy.weaveCIDRsFromConfig(container.Config); ok {
Info.Printf("Creating container with WEAVE_CIDR \"%s\"", strings.Join(cidrs, " "))
Log.Infof("Creating container with WEAVE_CIDR \"%s\"", strings.Join(cidrs, " "))
if container.HostConfig == nil {
container.HostConfig = &docker.HostConfig{}
}
@@ -56,7 +58,7 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
if err := i.setWeaveWaitEntrypoint(container.Config); err != nil {
return err
}
if err := i.setWeaveDNS(&container, r); err != nil {
if err := i.setWeaveDNS(&container, r.URL.Query().Get("name")); err != nil {
return err
}
}
@@ -96,7 +98,7 @@ func (i *createContainerInterceptor) setWeaveWaitEntrypoint(container *docker.Co
return nil
}
func (i *createContainerInterceptor) setWeaveDNS(container *createContainerRequestBody, r *http.Request) error {
func (i *createContainerInterceptor) setWeaveDNS(container *createContainerRequestBody, name string) error {
if i.proxy.WithoutDNS {
return nil
}
@@ -108,11 +110,15 @@ func (i *createContainerInterceptor) setWeaveDNS(container *createContainerReque
container.HostConfig.DNS = append(container.HostConfig.DNS, i.proxy.dockerBridgeIP)
name := r.URL.Query().Get("name")
if container.Hostname == "" && name != "" {
container.Hostname = name
// Strip trailing period because it's unusual to see it used on the end of a host name
container.Domainname = strings.TrimSuffix(dnsDomain, ".")
trimmedDNSDomain := strings.TrimSuffix(dnsDomain, ".")
if len(name)+1+len(trimmedDNSDomain) > MaxDockerHostname {
Log.Warningf("Container name [%s] too long to be used as hostname", name)
} else {
container.Hostname = name
container.Domainname = trimmedDNSDomain
}
}
if len(container.HostConfig.DNSSearch) == 0 {
+1 -1
View File
@@ -30,7 +30,7 @@ func (i *createExecInterceptor) InterceptRequest(r *http.Request) error {
}
if cidrs, ok := i.proxy.weaveCIDRsFromConfig(container.Config); ok {
Info.Printf("Exec in container %s with WEAVE_CIDR \"%s\"", container.ID, strings.Join(cidrs, " "))
Log.Infof("Exec in container %s with WEAVE_CIDR \"%s\"", container.ID, strings.Join(cidrs, " "))
cmd := append(weaveWaitEntrypoint, "-s")
options.Cmd = append(cmd, options.Cmd...)
}
+95 -12
View File
@@ -2,10 +2,13 @@ package proxy
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"os"
"regexp"
"strings"
"syscall"
"github.com/fsouza/go-dockerclient"
. "github.com/weaveworks/weave/common"
@@ -15,6 +18,8 @@ const (
defaultCaFile = "ca.pem"
defaultKeyFile = "key.pem"
defaultCertFile = "cert.pem"
dockerSock = "/var/run/docker.sock"
dockerSockUnix = "unix://" + dockerSock
)
var (
@@ -24,7 +29,7 @@ var (
)
type Config struct {
ListenAddr string
ListenAddrs []string
NoDefaultIPAM bool
TLSConfig TLSConfig
Version string
@@ -42,10 +47,10 @@ func NewProxy(c Config) (*Proxy, error) {
p := &Proxy{Config: c}
if err := p.TLSConfig.loadCerts(); err != nil {
Error.Fatalf("Could not configure tls for proxy: %s", err)
Log.Fatalf("Could not configure tls for proxy: %s", err)
}
client, err := docker.NewClient("unix:///var/run/docker.sock")
client, err := docker.NewClient(dockerSockUnix)
if err != nil {
return nil, err
}
@@ -63,11 +68,11 @@ func NewProxy(c Config) (*Proxy, error) {
}
func (proxy *Proxy) Dial() (net.Conn, error) {
return net.Dial("unix", "/var/run/docker.sock")
return net.Dial("unix", dockerSock)
}
func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Info.Printf("%s %s", r.Method, r.URL)
Log.Infof("%s %s", r.Method, r.URL)
path := r.URL.Path
var i interceptor
switch {
@@ -83,19 +88,97 @@ func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
proxy.Intercept(i, w, r)
}
func (proxy *Proxy) ListenAndServe() error {
listener, err := net.Listen("tcp", proxy.ListenAddr)
func (proxy *Proxy) ListenAndServe() {
listeners := []net.Listener{}
addrs := []string{}
for _, addr := range proxy.ListenAddrs {
listener, normalisedAddr, err := proxy.listen(addr)
if err != nil {
Log.Fatalf("Cannot listen on %s: %s", addr, err)
}
listeners = append(listeners, listener)
addrs = append(addrs, normalisedAddr)
}
for _, addr := range addrs {
Log.Infoln("proxy listening on", addr)
}
errs := make(chan error)
for _, listener := range listeners {
go func(listener net.Listener) {
errs <- (&http.Server{Handler: proxy}).Serve(listener)
}(listener)
}
for range listeners {
err := <-errs
if err != nil {
Log.Fatalf("Serve failed: %s", err)
}
}
}
func copyOwnerAndPermissions(from, to string) error {
stat, err := os.Stat(from)
if err != nil {
return err
}
if proxy.TLSConfig.enabled() {
listener = tls.NewListener(listener, proxy.TLSConfig.Config)
if err = os.Chmod(to, stat.Mode()); err != nil {
return err
}
Info.Println("proxy listening on", proxy.ListenAddr)
moreStat, ok := stat.Sys().(*syscall.Stat_t)
if !ok {
return nil
}
return (&http.Server{Handler: proxy}).Serve(listener)
if err = os.Chown(to, int(moreStat.Uid), int(moreStat.Gid)); err != nil {
return err
}
return nil
}
func (proxy *Proxy) listen(protoAndAddr string) (net.Listener, string, error) {
var (
listener net.Listener
err error
proto, addr string
)
if protoAddrParts := strings.SplitN(protoAndAddr, "://", 2); len(protoAddrParts) == 2 {
proto, addr = protoAddrParts[0], protoAddrParts[1]
} else if strings.HasPrefix(protoAndAddr, "/") {
proto, addr = "unix", protoAndAddr
} else {
proto, addr = "tcp", protoAndAddr
}
switch proto {
case "tcp":
listener, err = net.Listen(proto, addr)
if err != nil {
return nil, "", err
}
if proxy.TLSConfig.enabled() {
listener = tls.NewListener(listener, proxy.TLSConfig.Config)
}
case "unix":
os.Remove(addr) // remove socket from last invocation
listener, err = net.Listen(proto, addr)
if err != nil {
return nil, "", err
}
if err = copyOwnerAndPermissions(dockerSock, addr); err != nil {
return nil, "", err
}
default:
Log.Fatalf("Invalid protocol format: %q", proto)
}
return listener, fmt.Sprintf("%s://%s", proto, addr), nil
}
func (proxy *Proxy) weaveCIDRsFromConfig(config *docker.Config) ([]string, bool) {
+11 -11
View File
@@ -23,7 +23,7 @@ func (proxy *Proxy) Intercept(i interceptor, w http.ResponseWriter, r *http.Requ
http.Error(w, err.Error(), http.StatusNotFound)
default:
http.Error(w, err.Error(), http.StatusInternalServerError)
Warning.Print("Error intercepting request: ", err)
Log.Warning("Error intercepting request: ", err)
}
return
}
@@ -31,7 +31,7 @@ func (proxy *Proxy) Intercept(i interceptor, w http.ResponseWriter, r *http.Requ
conn, err := proxy.Dial()
if err != nil {
http.Error(w, "Could not connect to target", http.StatusInternalServerError)
Warning.Print(err)
Log.Warning(err)
return
}
client := httputil.NewClientConn(conn, nil)
@@ -40,13 +40,13 @@ func (proxy *Proxy) Intercept(i interceptor, w http.ResponseWriter, r *http.Requ
resp, err := client.Do(r)
if err != nil && err != httputil.ErrPersistEOF {
http.Error(w, fmt.Sprintf("Could not make request to target: %v", err), http.StatusInternalServerError)
Warning.Print("Error forwarding request: ", err)
Log.Warning("Error forwarding request: ", err)
return
}
err = i.InterceptResponse(resp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
Warning.Print("Error intercepting response: ", err)
Log.Warning("Error intercepting response: ", err)
return
}
@@ -56,7 +56,7 @@ func (proxy *Proxy) Intercept(i interceptor, w http.ResponseWriter, r *http.Requ
hdr.Add(k, v)
}
}
Debug.Printf("Response from target: %s %v", resp.Status, w.Header())
Log.Debugf("Response from target: %s %v", resp.Status, w.Header())
if resp.Header.Get("Content-Type") == "application/vnd.docker.raw-stream" {
doRawStream(w, resp, client)
@@ -65,7 +65,7 @@ func (proxy *Proxy) Intercept(i interceptor, w http.ResponseWriter, r *http.Requ
} else {
w.WriteHeader(resp.StatusCode)
if _, err := io.Copy(w, resp.Body); err != nil {
Warning.Print(err)
Log.Warning(err)
}
}
}
@@ -80,17 +80,17 @@ func doRawStream(w http.ResponseWriter, resp *http.Response, client *httputil.Cl
defer up.Close()
if _, err := down.Write([]byte("HTTP/1.1 200 OK\n")); err != nil {
Warning.Print(err)
Log.Warning(err)
return
}
if err := resp.Header.Write(down); err != nil {
Warning.Print(err)
Log.Warning(err)
return
}
if _, err := down.Write([]byte("\n")); err != nil {
Warning.Print(err)
Log.Warning(err)
return
}
@@ -105,13 +105,13 @@ func doRawStream(w http.ResponseWriter, resp *http.Response, client *httputil.Cl
func copyStream(dst io.Writer, src io.Reader, done chan struct{}) {
defer close(done)
if _, err := io.Copy(dst, src); err != nil {
Warning.Print(err)
Log.Warning(err)
}
if c, ok := dst.(interface {
CloseWrite() error
}); ok {
if err := c.CloseWrite(); err != nil {
Warning.Printf("Error closing connection: %s", err)
Log.Warningf("Error closing connection: %s", err)
}
}
}
+3 -3
View File
@@ -23,15 +23,15 @@ func (i *startContainerInterceptor) InterceptResponse(r *http.Response) error {
cidrs, ok := i.proxy.weaveCIDRsFromConfig(container.Config)
if !ok {
Debug.Print("No Weave CIDR, ignoring")
Log.Debug("No Weave CIDR, ignoring")
return nil
}
Info.Printf("Attaching container %s with WEAVE_CIDR \"%s\" to weave network", container.ID, strings.Join(cidrs, " "))
Log.Infof("Attaching container %s with WEAVE_CIDR \"%s\" to weave network", container.ID, strings.Join(cidrs, " "))
args := []string{"attach"}
args = append(args, cidrs...)
args = append(args, "--or-die", container.ID)
if output, err := callWeave(args...); err != nil {
Warning.Printf("Attaching container %s to weave network failed: %s", container.ID, string(output))
Log.Warningf("Attaching container %s to weave network failed: %s", container.ID, string(output))
return errors.New(string(output))
}
+30 -41
View File
@@ -4,10 +4,11 @@ import (
"encoding/binary"
"encoding/gob"
"fmt"
"log"
"net"
"sync"
"time"
. "github.com/weaveworks/weave/common"
)
type Connection interface {
@@ -50,7 +51,7 @@ type LocalConnection struct {
SessionKey *[32]byte
heartbeatTCP *time.Ticker
heartbeatTimeout *time.Timer
heartbeatFrame *ForwardedFrame
heartbeatFrame []byte
heartbeat *time.Ticker
fragTest *time.Ticker
forwarder *Forwarder
@@ -83,7 +84,11 @@ func (conn *RemoteConnection) BreakTie(Connection) ConnectionTieBreak { return T
func (conn *RemoteConnection) Shutdown(error) {}
func (conn *RemoteConnection) Log(args ...interface{}) {
log.Println(append(append([]interface{}{}, fmt.Sprintf("->[%s|%s]:", conn.remoteTCPAddr, conn.remote)), args...)...)
Log.Println(append(append([]interface{}{}, fmt.Sprintf("->[%s|%s]:", conn.remoteTCPAddr, conn.remote)), args...)...)
}
func (conn *RemoteConnection) ErrorLog(args ...interface{}) {
Log.Errorln(append(append([]interface{}{}, fmt.Sprintf("->[%s|%s]:", conn.remoteTCPAddr, conn.remote)), args...)...)
}
func (conn *RemoteConnection) String() string {
@@ -98,26 +103,23 @@ func (conn *RemoteConnection) String() string {
return fmt.Sprint("Connection ", from, "->", to)
}
func NewLocalConnection(connRemote *RemoteConnection, tcpConn *net.TCPConn, udpAddr *net.UDPAddr, router *Router) *LocalConnection {
// Does not return anything. If the connection is successful, it will
// end up in the local peer's connections map.
func StartLocalConnection(connRemote *RemoteConnection, tcpConn *net.TCPConn, udpAddr *net.UDPAddr, router *Router, acceptNewPeer bool) {
if connRemote.local != router.Ourself.Peer {
log.Fatal("Attempt to create local connection from a peer which is not ourself")
Log.Fatal("Attempt to create local connection from a peer which is not ourself")
}
// NB, we're taking a copy of connRemote here.
return &LocalConnection{
actionChan := make(chan ConnectionAction, ChannelSize)
finished := make(chan struct{})
conn := &LocalConnection{
RemoteConnection: *connRemote,
Router: router,
TCPConn: tcpConn,
remoteUDPAddr: udpAddr,
effectivePMTU: DefaultPMTU}
}
// Async. Does not return anything. If the connection is successful,
// it will end up in the local peer's connections map.
func (conn *LocalConnection) Start(acceptNewPeer bool) {
actionChan := make(chan ConnectionAction, ChannelSize)
conn.actionChan = actionChan
finished := make(chan struct{})
conn.finished = finished
effectivePMTU: DefaultPMTU,
actionChan: actionChan,
finished: finished}
go conn.run(actionChan, finished, acceptNewPeer)
}
@@ -159,8 +161,7 @@ func (conn *LocalConnection) setEffectivePMTU(pmtu int) {
}
// Called by the connection's actor process, and by the connection's
// TCP received process. StackFrag is read in conn.Forward (called by
// router udp listener and sniffer processes)
// TCP receiver process. StackFrag is read in conn.forward
func (conn *LocalConnection) setStackFrag(frag bool) {
conn.Lock()
defer conn.Unlock()
@@ -231,7 +232,7 @@ func (conn *LocalConnection) ReceivedHeartbeat(remoteUDPAddr *net.UDPAddr, connU
if oldRemoteUDPAddr == nil {
return conn.sendFastHeartbeats()
} else if oldRemoteUDPAddr.String() != remoteUDPAddr.String() {
log.Println("Peer", conn.remote, "moved from", old, "to", remoteUDPAddr)
Log.Println("Peer", conn.remote, "moved from", old, "to", remoteUDPAddr)
}
return nil
})
@@ -254,15 +255,11 @@ func (conn *LocalConnection) SetEstablished() {
}
// Send a large frame down the DF channel in order to prompt
// PMTU discovery to start.
conn.Forward(true, &ForwardedFrame{
srcPeer: conn.local,
dstPeer: conn.remote,
frame: PMTUDiscovery},
nil)
conn.Send(true, PMTUDiscovery)
conn.heartbeat = time.NewTicker(SlowHeartbeat)
conn.fragTest = time.NewTicker(FragTestInterval)
// avoid initial waits for timers to fire
conn.Forward(true, conn.heartbeatFrame, nil)
conn.Send(true, conn.heartbeatFrame)
conn.performFragTest()
return nil
})
@@ -287,7 +284,7 @@ func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, finished ch
conn.TCPConn.SetLinger(0)
_, dec, err := conn.handshake(acceptNewPeer)
dec, err := conn.handshake(acceptNewPeer)
if err != nil {
return
}
@@ -358,12 +355,8 @@ func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, finished ch
func (conn *LocalConnection) initHeartbeats() error {
conn.heartbeatTCP = time.NewTicker(TCPHeartbeat)
conn.heartbeatTimeout = time.NewTimer(HeartbeatTimeout)
heartbeatFrameBytes := make([]byte, EthernetOverhead+8)
binary.BigEndian.PutUint64(heartbeatFrameBytes[EthernetOverhead:], conn.uid)
conn.heartbeatFrame = &ForwardedFrame{
srcPeer: conn.local,
dstPeer: conn.remote,
frame: heartbeatFrameBytes}
conn.heartbeatFrame = make([]byte, EthernetOverhead+8)
binary.BigEndian.PutUint64(conn.heartbeatFrame[EthernetOverhead:], conn.uid)
if conn.remoteUDPAddr == nil {
return nil
}
@@ -380,7 +373,7 @@ func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction) (err
case <-conn.heartbeatTimeout.C:
err = fmt.Errorf("timed out waiting for UDP heartbeat")
case <-tickerChan(conn.heartbeat):
conn.Forward(true, conn.heartbeatFrame, nil)
conn.Send(true, conn.heartbeatFrame)
case <-tickerChan(conn.fragTest):
conn.performFragTest()
}
@@ -390,9 +383,9 @@ func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction) (err
func (conn *LocalConnection) shutdown(err error) {
if conn.remote == nil {
log.Printf("->[%s] connection shutting down due to error during handshake: %v\n", conn.remoteTCPAddr, err)
Log.Errorf("->[%s] connection shutting down due to error during handshake: %v\n", conn.remoteTCPAddr, err)
} else {
conn.Log("connection shutting down due to error:", err)
conn.ErrorLog("connection shutting down due to error:", err)
}
if conn.TCPConn != nil {
@@ -488,18 +481,14 @@ func (conn *LocalConnection) sendFastHeartbeats() error {
err := conn.ensureForwarders()
if err == nil {
conn.heartbeat = time.NewTicker(FastHeartbeat)
conn.Forward(true, conn.heartbeatFrame, nil) // avoid initial wait
conn.Send(true, conn.heartbeatFrame) // avoid initial wait
}
return err
}
func (conn *LocalConnection) performFragTest() {
conn.setStackFrag(false)
conn.Forward(false, &ForwardedFrame{
srcPeer: conn.local,
dstPeer: conn.remote,
frame: FragTest},
nil)
conn.Send(false, FragTest)
}
func tickerChan(ticker *time.Ticker) <-chan time.Time {
+9 -10
View File
@@ -3,10 +3,11 @@ package router
import (
"bytes"
"fmt"
"log"
"math/rand"
"net"
"time"
. "github.com/weaveworks/weave/common"
)
const (
@@ -37,19 +38,17 @@ type Target struct {
type ConnectionMakerAction func() bool
func NewConnectionMaker(ourself *LocalPeer, peers *Peers, port int, discovery bool) *ConnectionMaker {
return &ConnectionMaker{
actionChan := make(chan ConnectionMakerAction, ChannelSize)
cm := &ConnectionMaker{
ourself: ourself,
peers: peers,
port: port,
discovery: discovery,
directPeers: peerAddrs{},
targets: make(map[string]*Target)}
}
func (cm *ConnectionMaker) Start() {
actionChan := make(chan ConnectionMakerAction, ChannelSize)
cm.actionChan = actionChan
targets: make(map[string]*Target),
actionChan: actionChan}
go cm.queryLoop(actionChan)
return cm
}
func (cm *ConnectionMaker) InitiateConnections(peers []string, replace bool) []error {
@@ -300,9 +299,9 @@ func (cm *ConnectionMaker) connectToTargets(validTarget map[string]struct{}, dir
}
func (cm *ConnectionMaker) attemptConnection(address string, acceptNewPeer bool) {
log.Printf("->[%s] attempting connection\n", address)
Log.Printf("->[%s] attempting connection\n", address)
if err := cm.ourself.CreateConnection(address, acceptNewPeer); err != nil {
log.Printf("->[%s] error during connection attempt: %v\n", address, err)
Log.Errorf("->[%s] error during connection attempt: %v\n", address, err)
cm.ConnectionTerminated(address, err)
}
}
+3 -3
View File
@@ -8,9 +8,9 @@ import (
"fmt"
"sync"
"code.google.com/p/go-bit/bit"
"code.google.com/p/go.crypto/nacl/box"
"code.google.com/p/go.crypto/nacl/secretbox"
"github.com/andybalholm/go-bit"
"golang.org/x/crypto/nacl/box"
"golang.org/x/crypto/nacl/secretbox"
)
func GenerateKeyPair() (publicKey, privateKey *[32]byte, err error) {
+28 -20
View File
@@ -2,28 +2,32 @@ package router
import (
"bytes"
"log"
"net"
"code.google.com/p/gopacket"
"code.google.com/p/gopacket/layers"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
. "github.com/weaveworks/weave/common"
)
type EthernetDecoder struct {
eth layers.Ethernet
ip layers.IPv4
Eth layers.Ethernet
IP layers.IPv4
decoded []gopacket.LayerType
parser *gopacket.DecodingLayerParser
}
func NewEthernetDecoder() *EthernetDecoder {
dec := &EthernetDecoder{}
dec.parser = gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &dec.eth, &dec.ip)
dec.parser = gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &dec.Eth, &dec.IP)
return dec
}
func (dec *EthernetDecoder) DecodeLayers(data []byte) error {
return dec.parser.DecodeLayers(data, &dec.decoded)
func (dec *EthernetDecoder) DecodeLayers(data []byte) {
// We intentionally discard the error return here, because it
// is normal for gopacket to return an error saying that it
// cannot decode a layer beyond the ones we specified when
// setting up the parser.
dec.parser.DecodeLayers(data, &dec.decoded)
}
func (dec *EthernetDecoder) sendICMPFragNeeded(mtu int, sendFrame func([]byte) error) error {
@@ -31,23 +35,23 @@ func (dec *EthernetDecoder) sendICMPFragNeeded(mtu int, sendFrame func([]byte) e
opts := gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true}
ipHeaderSize := int(dec.ip.IHL) * 4 // IHL is the number of 32-byte words in the header
payload := gopacket.Payload(dec.ip.BaseLayer.Contents[:ipHeaderSize+8])
ipHeaderSize := int(dec.IP.IHL) * 4 // IHL is the number of 32-byte words in the header
payload := gopacket.Payload(dec.IP.BaseLayer.Contents[:ipHeaderSize+8])
err := gopacket.SerializeLayers(buf, opts,
&layers.Ethernet{
SrcMAC: dec.eth.DstMAC,
DstMAC: dec.eth.SrcMAC,
EthernetType: dec.eth.EthernetType},
SrcMAC: dec.Eth.DstMAC,
DstMAC: dec.Eth.SrcMAC,
EthernetType: dec.Eth.EthernetType},
&layers.IPv4{
Version: 4,
TOS: dec.ip.TOS,
TOS: dec.IP.TOS,
Id: 0,
Flags: 0,
FragOffset: 0,
TTL: 64,
Protocol: layers.IPProtocolICMPv4,
DstIP: dec.ip.SrcIP,
SrcIP: dec.ip.DstIP},
DstIP: dec.IP.SrcIP,
SrcIP: dec.IP.DstIP},
&layers.ICMPv4{
TypeCode: 0x304,
Id: 0,
@@ -57,7 +61,7 @@ func (dec *EthernetDecoder) sendICMPFragNeeded(mtu int, sendFrame func([]byte) e
return err
}
log.Printf("Sending ICMP 3,4 (%v -> %v): PMTU= %v\n", dec.ip.DstIP, dec.ip.SrcIP, mtu)
Log.Printf("Sending ICMP 3,4 (%v -> %v): PMTU= %v\n", dec.IP.DstIP, dec.IP.SrcIP, mtu)
return sendFrame(buf.Bytes())
}
@@ -68,10 +72,14 @@ var (
)
func (dec *EthernetDecoder) DropFrame() bool {
return bytes.Equal(stpMACPrefix, dec.eth.DstMAC[:len(stpMACPrefix)])
return bytes.Equal(stpMACPrefix, dec.Eth.DstMAC[:len(stpMACPrefix)])
}
func (dec *EthernetDecoder) IsSpecial() bool {
return dec.eth.Length == 0 && dec.eth.EthernetType == layers.EthernetTypeLLC &&
bytes.Equal(zeroMAC, dec.eth.SrcMAC) && bytes.Equal(zeroMAC, dec.eth.DstMAC)
return dec.Eth.Length == 0 && dec.Eth.EthernetType == layers.EthernetTypeLLC &&
bytes.Equal(zeroMAC, dec.Eth.SrcMAC) && bytes.Equal(zeroMAC, dec.Eth.DstMAC)
}
func (dec *EthernetDecoder) DF() bool {
return len(dec.decoded) == 2 && (dec.IP.Flags&layers.IPv4DontFragment != 0)
}
+37 -33
View File
@@ -4,8 +4,8 @@ import (
"syscall"
"time"
"code.google.com/p/gopacket"
"code.google.com/p/gopacket/layers"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
type ForwardedFrame struct {
@@ -40,16 +40,12 @@ func (conn *LocalConnection) ensureForwarders() error {
forwarder := NewForwarder(conn, encryptor, udpSender, DefaultPMTU)
forwarderDF := NewForwarderDF(conn, encryptorDF, udpSenderDF, DefaultPMTU)
effectivePMTU := forwarderDF.unverifiedPMTU
forwarder.Start()
forwarderDF.Start()
// Various fields in the conn struct are read by other processes,
// so we have to use locks.
conn.Lock()
conn.forwarder = forwarder
conn.forwarderDF = forwarderDF
conn.effectivePMTU = effectivePMTU
conn.Unlock()
return nil
@@ -63,11 +59,23 @@ func (conn *LocalConnection) stopForwarders() {
conn.forwarderDF.Shutdown()
}
// Called from peer.Relay[Broadcast] which is itself invoked from
// router (both UDP listener process and sniffer process). Also called
// from connection's heartbeat process, and from the connection's TCP
// receiver process.
func (conn *LocalConnection) Forward(df bool, frame *ForwardedFrame, dec *EthernetDecoder) error {
// Called from connection's actor process, and from the connection's
// TCP receiver process.
func (conn *LocalConnection) Send(df bool, frameBytes []byte) error {
frame := &ForwardedFrame{
srcPeer: conn.local,
dstPeer: conn.remote,
frame: frameBytes}
return conn.forward(frame, nil, df)
}
// Called from LocalPeer.Relay[Broadcast] which is itself invoked from
// router (both UDP listener process and sniffer process).
func (conn *LocalConnection) Forward(frame *ForwardedFrame, dec *EthernetDecoder) error {
return conn.forward(frame, dec, dec != nil && dec.DF())
}
func (conn *LocalConnection) forward(frame *ForwardedFrame, dec *EthernetDecoder, df bool) error {
conn.RLock()
var (
forwarder = conn.forwarder
@@ -81,6 +89,7 @@ func (conn *LocalConnection) Forward(df bool, frame *ForwardedFrame, dec *Ethern
conn.Log("Cannot forward frame yet - awaiting contact")
return nil
}
// We could use non-blocking channel sends here, i.e. drop frames
// on the floor when the forwarder is busy. This would allow our
// caller - the capturing loop in the router - to read frames more
@@ -109,11 +118,10 @@ func (conn *LocalConnection) Forward(df bool, frame *ForwardedFrame, dec *Ethern
forwarderDF.Forward(frame)
return nil
}
conn.Router.LogFrame("Fragmenting", frame.frame, &dec.eth)
// We can't trust the stack to fragment, we have IP, and we
// have a frame that's too big for the MTU, so we have to
// fragment it ourself.
return fragment(dec.eth, dec.ip, effectivePMTU, frame, func(segFrame *ForwardedFrame) {
return fragment(dec.Eth, dec.IP, effectivePMTU, frame, func(segFrame *ForwardedFrame) {
forwarderDF.Forward(segFrame)
})
}
@@ -195,20 +203,18 @@ type Forwarder struct {
}
func NewForwarder(conn *LocalConnection, enc Encryptor, udpSender UDPSender, pmtu int) *Forwarder {
return &Forwarder{
ch := make(chan *ForwardedFrame, ChannelSize)
finished := make(chan struct{})
fwd := &Forwarder{
conn: conn,
ch: ch,
finished: finished,
enc: enc,
udpSender: udpSender,
maxPayload: pmtu - UDPOverhead,
processSendError: func(err error) error { return err }}
}
func (fwd *Forwarder) Start() {
ch := make(chan *ForwardedFrame, ChannelSize)
fwd.ch = ch
finished := make(chan struct{})
fwd.finished = finished
go fwd.run(ch, finished)
return fwd
}
func (fwd *Forwarder) Shutdown() {
@@ -275,7 +281,7 @@ func (fwd *Forwarder) accumulateAndSendFrames(ch <-chan *ForwardedFrame, frame *
}
func (fwd *Forwarder) logDrop(frame *ForwardedFrame) {
fwd.conn.Log("Dropping too big frame during forwarding: frame len:", len(frame.frame), "; effective PMTU:", fwd.maxPayload+UDPOverhead-fwd.effectiveOverhead())
fwd.conn.ErrorLog("Dropping too big frame during forwarding: frame len:", len(frame.frame), "; effective PMTU:", fwd.maxPayload+UDPOverhead-fwd.effectiveOverhead())
}
func (fwd *Forwarder) appendFrame(frame *ForwardedFrame) bool {
@@ -310,25 +316,23 @@ type ForwarderDF struct {
}
func NewForwarderDF(conn *LocalConnection, enc Encryptor, udpSender UDPSender, pmtu int) *ForwarderDF {
ch := make(chan *ForwardedFrame, ChannelSize)
finished := make(chan struct{})
verifyPMTU := make(chan int, ChannelSize)
fwd := &ForwarderDF{
Forwarder: Forwarder{
conn: conn,
ch: ch,
finished: finished,
enc: enc,
udpSender: udpSender,
maxPayload: pmtu - UDPOverhead}}
maxPayload: pmtu - UDPOverhead},
verifyPMTU: verifyPMTU}
fwd.Forwarder.processSendError = fwd.processSendError
fwd.unverifiedPMTU = pmtu - fwd.effectiveOverhead()
return fwd
}
func (fwd *ForwarderDF) Start() {
ch := make(chan *ForwardedFrame, ChannelSize)
fwd.ch = ch
finished := make(chan struct{})
fwd.finished = finished
verifyPMTU := make(chan int, ChannelSize)
fwd.verifyPMTU = verifyPMTU
conn.setEffectivePMTU(fwd.unverifiedPMTU)
go fwd.run(ch, finished, verifyPMTU)
return fwd
}
func (fwd *ForwarderDF) PMTUVerified(pmtu int) {
+8 -6
View File
@@ -40,13 +40,15 @@ type GossipSender struct {
}
func NewGossipSender(send func(GossipData)) *GossipSender {
return &GossipSender{send: send}
}
func (sender *GossipSender) Start() {
sender.cell = make(chan GossipData, 1)
sender.flushch = make(chan chan bool)
cell := make(chan GossipData, 1)
flushch := make(chan chan bool)
sender := &GossipSender{
send: send,
cell: cell,
flushch: flushch,
}
go sender.run()
return sender
}
func (sender *GossipSender) run() {
+14 -9
View File
@@ -3,8 +3,10 @@ package router
import (
"bytes"
"encoding/gob"
"log"
"fmt"
"sync"
. "github.com/weaveworks/weave/common"
)
type GossipChannel struct {
@@ -58,7 +60,12 @@ func (c *GossipChannel) deliverUnicast(srcName PeerName, origPayload []byte, dec
return err
}
if c.ourself.Name != destName {
return c.relayUnicast(destName, origPayload)
if err := c.relayUnicast(destName, origPayload); err != nil {
// just log errors from relayUnicast; a problem between us and destination
// is not enough reason to break the connection from the source
c.log(err)
}
return nil
}
var payload []byte
if err := dec.Decode(&payload); err != nil {
@@ -147,7 +154,6 @@ func (c *GossipChannel) sendDown(conn Connection, data GossipData) {
}
})
c.senders[conn] = sender
sender.Start()
}
sender.Send(data)
}
@@ -160,15 +166,15 @@ func (c *GossipChannel) GossipBroadcast(update GossipData) error {
return c.relayBroadcast(c.ourself.Name, update)
}
func (c *GossipChannel) relayUnicast(dstPeerName PeerName, buf []byte) error {
func (c *GossipChannel) relayUnicast(dstPeerName PeerName, buf []byte) (err error) {
if relayPeerName, found := c.routes.UnicastAll(dstPeerName); !found {
c.log("unknown relay destination:", dstPeerName)
err = fmt.Errorf("unknown relay destination: %s", dstPeerName)
} else if conn, found := c.ourself.ConnectionTo(relayPeerName); !found {
c.log("unable to find connection to relay peer", relayPeerName)
err = fmt.Errorf("unable to find connection to relay peer %s", relayPeerName)
} else {
conn.(ProtocolSender).SendProtocolMsg(ProtocolMsg{ProtocolGossipUnicast, buf})
}
return nil
return err
}
func (c *GossipChannel) relayBroadcast(srcName PeerName, update GossipData) error {
@@ -198,7 +204,6 @@ func (c *GossipChannel) relayBroadcast(srcName PeerName, update GossipData) erro
if !found {
broadcaster = NewGossipSender(func(pending GossipData) { c.sendBroadcast(srcName, pending) })
c.broadcasters[srcName] = broadcaster
broadcaster.Start()
}
broadcaster.Send(update)
return nil
@@ -221,5 +226,5 @@ func (c *GossipChannel) sendBroadcast(srcName PeerName, update GossipData) {
}
func (c *GossipChannel) log(args ...interface{}) {
log.Println(append(append([]interface{}{}, "[gossip "+c.name+"]:"), args...)...)
Log.Println(append(append([]interface{}{}, "[gossip "+c.name+"]:"), args...)...)
}
+2 -7
View File
@@ -18,15 +18,10 @@ type mockChannelConnection struct {
dest *Router
}
// Construct a "passive" Router, i.e. without any goroutines, except
// for Routes and GossipSenders.
func NewTestRouter(name string) *Router {
peerName, _ := PeerNameFromString(name)
router := NewRouter(Config{}, peerName, "")
// need to create a dummy channel otherwise tests hang on nil
// channels when the Router invoked ConnectionMaker.Refresh
router.ConnectionMaker.actionChan = make(chan ConnectionMakerAction, ChannelSize)
router.Routes.Start()
router := NewRouter(Config{}, peerName, "nick")
router.Start()
return router
}
+13 -13
View File
@@ -45,7 +45,7 @@ func (fv *FieldValidator) Err() error {
return fv.err
}
func (conn *LocalConnection) handshake(acceptNewPeer bool) (*gob.Encoder, *gob.Decoder, error) {
func (conn *LocalConnection) handshake(acceptNewPeer bool) (*gob.Decoder, error) {
// We do not need to worry about locking in here as at this point
// the connection is not reachable by any go-routine other than
// ourself. Only when we add this connection to the conn.local
@@ -54,7 +54,7 @@ func (conn *LocalConnection) handshake(acceptNewPeer bool) (*gob.Encoder, *gob.D
conn.TCPConn.SetDeadline(time.Now().Add(HeaderTimeout))
version, err := exchangeProtocolHeader(conn.TCPConn, conn.TCPConn)
if err != nil {
return nil, nil, err
return nil, err
}
conn.version = version
conn.TCPConn.SetDeadline(time.Time{})
@@ -66,7 +66,7 @@ func (conn *LocalConnection) handshake(acceptNewPeer bool) (*gob.Encoder, *gob.D
dec := gob.NewDecoder(conn.TCPConn)
fv, private, err := conn.exchangeHandshake(localConnID, usingPassword, enc, dec)
if err != nil {
return nil, nil, err
return nil, err
}
nameStr, _ := fv.Value("Name")
@@ -74,39 +74,39 @@ func (conn *LocalConnection) handshake(acceptNewPeer bool) (*gob.Encoder, *gob.D
uidStr, _ := fv.Value("UID")
remoteConnIDStr, _ := fv.Value("ConnID")
if err := fv.Err(); err != nil {
return nil, nil, err
return nil, err
}
name, err := PeerNameFromString(nameStr)
if err != nil {
return nil, nil, err
return nil, err
}
if !acceptNewPeer {
if _, found := conn.Router.Peers.Fetch(name); !found {
return nil, nil, fmt.Errorf("Found unknown remote name: %s at %s", name, conn.remoteTCPAddr)
return nil, fmt.Errorf("Found unknown remote name: %s at %s", name, conn.remoteTCPAddr)
}
}
if existingConn, found := conn.Router.Ourself.ConnectionTo(name); found && existingConn.Established() {
return nil, nil, fmt.Errorf("Already have connection to %s at %s", existingConn.Remote(), existingConn.RemoteTCPAddr())
return nil, fmt.Errorf("Already have connection to %s at %s", existingConn.Remote(), existingConn.RemoteTCPAddr())
}
uid, err := ParsePeerUID(uidStr)
if err != nil {
return nil, nil, err
return nil, err
}
remoteConnID, err := strconv.ParseUint(remoteConnIDStr, 10, 64)
if err != nil {
return nil, nil, err
return nil, err
}
conn.uid = localConnID ^ remoteConnID
remotePublicStr, rpErr := fv.Value("PublicKey")
if usingPassword {
if rpErr != nil {
return nil, nil, rpErr
return nil, rpErr
}
remotePublicSlice, rpErr := hex.DecodeString(remotePublicStr)
if rpErr != nil {
return nil, nil, rpErr
return nil, rpErr
}
remotePublic := [32]byte{}
for idx, elem := range remotePublicSlice {
@@ -117,13 +117,13 @@ func (conn *LocalConnection) handshake(acceptNewPeer bool) (*gob.Encoder, *gob.D
conn.Decryptor = NewNaClDecryptor(conn.SessionKey, conn.outbound)
} else {
if rpErr == nil {
return nil, nil, fmt.Errorf("Remote network is encrypted. Password required.")
return nil, fmt.Errorf("Remote network is encrypted. Password required.")
}
conn.tcpSender = NewSimpleTCPSender(enc)
conn.Decryptor = NewNonDecryptor()
}
return enc, dec, conn.setRemote(NewPeer(name, nickNameStr, uid, 0))
return dec, conn.setRemote(NewPeer(name, nickNameStr, uid, 0))
}
func (conn *LocalConnection) exchangeHandshake(localConnID uint64, usingPassword bool, enc *gob.Encoder, dec *gob.Decoder) (*FieldValidator, *[32]byte, error) {
+26 -25
View File
@@ -2,10 +2,11 @@ package router
import (
"fmt"
"log"
"net"
"sync"
"time"
. "github.com/weaveworks/weave/common"
)
type LocalPeer struct {
@@ -18,60 +19,61 @@ type LocalPeer struct {
type LocalPeerAction func()
func NewLocalPeer(name PeerName, nickName string, router *Router) *LocalPeer {
return &LocalPeer{Peer: NewPeer(name, nickName, 0, 0), router: router}
}
func (peer *LocalPeer) Start() {
actionChan := make(chan LocalPeerAction, ChannelSize)
peer.actionChan = actionChan
peer := &LocalPeer{
Peer: NewPeer(name, nickName, 0, 0),
router: router,
actionChan: actionChan,
}
go peer.actorLoop(actionChan)
return peer
}
func (peer *LocalPeer) Forward(dstPeer *Peer, df bool, frame []byte, dec *EthernetDecoder) error {
return peer.Relay(peer.Peer, dstPeer, df, frame, dec)
func (peer *LocalPeer) Forward(dstPeer *Peer, frame []byte, dec *EthernetDecoder) error {
return peer.Relay(peer.Peer, dstPeer, frame, dec)
}
func (peer *LocalPeer) Broadcast(df bool, frame []byte, dec *EthernetDecoder) {
peer.RelayBroadcast(peer.Peer, df, frame, dec)
func (peer *LocalPeer) Broadcast(frame []byte, dec *EthernetDecoder) {
peer.RelayBroadcast(peer.Peer, frame, dec)
}
func (peer *LocalPeer) Relay(srcPeer, dstPeer *Peer, df bool, frame []byte, dec *EthernetDecoder) error {
func (peer *LocalPeer) Relay(srcPeer, dstPeer *Peer, frame []byte, dec *EthernetDecoder) error {
relayPeerName, found := peer.router.Routes.Unicast(dstPeer.Name)
if !found {
// Not necessarily an error as there could be a race with the
// dst disappearing whilst the frame is in flight
log.Println("Received packet for unknown destination:", dstPeer)
Log.Println("Received packet for unknown destination:", dstPeer)
return nil
}
conn, found := peer.ConnectionTo(relayPeerName)
if !found {
// Again, could just be a race, not necessarily an error
log.Println("Unable to find connection to relay peer", relayPeerName)
Log.Println("Unable to find connection to relay peer", relayPeerName)
return nil
}
return conn.(*LocalConnection).Forward(df, &ForwardedFrame{
return conn.(*LocalConnection).Forward(&ForwardedFrame{
srcPeer: srcPeer,
dstPeer: dstPeer,
frame: frame},
dec)
}
func (peer *LocalPeer) RelayBroadcast(srcPeer *Peer, df bool, frame []byte, dec *EthernetDecoder) {
func (peer *LocalPeer) RelayBroadcast(srcPeer *Peer, frame []byte, dec *EthernetDecoder) {
nextHops := peer.router.Routes.Broadcast(srcPeer.Name)
if len(nextHops) == 0 {
return
}
for _, conn := range peer.ConnectionsTo(nextHops) {
err := conn.(*LocalConnection).Forward(df, &ForwardedFrame{
err := conn.(*LocalConnection).Forward(&ForwardedFrame{
srcPeer: srcPeer,
dstPeer: conn.Remote(),
frame: frame},
dec)
if err != nil {
if ftbe, ok := err.(FrameTooBigError); ok {
log.Printf("dropping too big DF broadcast frame (%v -> %v): PMTU= %v\n", dec.ip.DstIP, dec.ip.SrcIP, ftbe.EPMTU)
Log.Warningf("dropping too big DF broadcast frame (%v -> %v): PMTU= %v\n", dec.IP.DstIP, dec.IP.SrcIP, ftbe.EPMTU)
} else {
log.Println(err)
Log.Errorln(err)
}
}
}
@@ -132,8 +134,7 @@ func (peer *LocalPeer) CreateConnection(peerAddr string, acceptNewPeer bool) err
return err
}
connRemote := NewRemoteConnection(peer.Peer, nil, tcpConn.RemoteAddr().String(), true, false)
connLocal := NewLocalConnection(connRemote, tcpConn, udpAddr, peer.router)
connLocal.Start(acceptNewPeer)
StartLocalConnection(connRemote, tcpConn, udpAddr, peer.router, acceptNewPeer)
return nil
}
@@ -181,10 +182,10 @@ func (peer *LocalPeer) actorLoop(actionChan <-chan LocalPeerAction) {
func (peer *LocalPeer) handleAddConnection(conn Connection) error {
if peer.Peer != conn.Local() {
log.Fatal("Attempt made to add connection to peer where peer is not the source of connection")
Log.Fatal("Attempt made to add connection to peer where peer is not the source of connection")
}
if conn.Remote() == nil {
log.Fatal("Attempt made to add connection to peer with unknown remote peer")
Log.Fatal("Attempt made to add connection to peer with unknown remote peer")
}
toName := conn.Remote().Name
dupErr := fmt.Errorf("Multiple connections to %s added to %s", conn.Remote(), peer.String())
@@ -223,7 +224,7 @@ func (peer *LocalPeer) handleAddConnection(conn Connection) error {
func (peer *LocalPeer) handleConnectionEstablished(conn Connection) {
if peer.Peer != conn.Local() {
log.Fatal("Peer informed of active connection where peer is not the source of connection")
Log.Fatal("Peer informed of active connection where peer is not the source of connection")
}
if dupConn, found := peer.connections[conn.Remote().Name]; !found || conn != dupConn {
conn.Shutdown(fmt.Errorf("Cannot set unknown connection active"))
@@ -236,10 +237,10 @@ func (peer *LocalPeer) handleConnectionEstablished(conn Connection) {
func (peer *LocalPeer) handleDeleteConnection(conn Connection) {
if peer.Peer != conn.Local() {
log.Fatal("Attempt made to delete connection from peer where peer is not the source of connection")
Log.Fatal("Attempt made to delete connection from peer where peer is not the source of connection")
}
if conn.Remote() == nil {
log.Fatal("Attempt made to delete connection to peer with unknown remote peer")
Log.Fatal("Attempt made to delete connection to peer with unknown remote peer")
}
toName := conn.Remote().Name
if connFound, found := peer.connections[toName]; !found || connFound != conn {
+2 -4
View File
@@ -22,14 +22,12 @@ type MacCache struct {
}
func NewMacCache(maxAge time.Duration, onExpiry func(net.HardwareAddr, *Peer)) *MacCache {
return &MacCache{
cache := &MacCache{
table: make(map[uint64]*MacCacheEntry),
maxAge: maxAge,
onExpiry: onExpiry}
}
func (cache *MacCache) Start() {
cache.setExpiryTimer()
return cache
}
func (cache *MacCache) Enter(mac net.HardwareAddr, peer *Peer) bool {
+1 -1
View File
@@ -3,7 +3,7 @@ package router
import (
"fmt"
"code.google.com/p/gopacket/pcap"
"github.com/google/gopacket/pcap"
)
type PcapIO struct {
+30 -45
View File
@@ -5,13 +5,11 @@ import (
"encoding/binary"
"fmt"
"io"
"log"
"net"
"sync"
"syscall"
"time"
"code.google.com/p/gopacket/layers"
. "github.com/weaveworks/weave/common"
)
@@ -27,7 +25,7 @@ const (
// against brute-force attacks on the password when encryption is in
// use. It is also a basic DoS defence.
type LogFrameFunc func(string, []byte, *layers.Ethernet)
type LogFrameFunc func(string, []byte, *EthernetDecoder)
type Config struct {
Port int
@@ -68,11 +66,11 @@ type PacketSourceSink interface {
func NewRouter(config Config, name PeerName, nickName string) *Router {
router := &Router{Config: config, gossipChannels: make(GossipChannels)}
onMacExpiry := func(mac net.HardwareAddr, peer *Peer) {
log.Println("Expired MAC", mac, "at", peer)
Log.Println("Expired MAC", mac, "at", peer)
}
onPeerGC := func(peer *Peer) {
router.Macs.Delete(peer)
log.Println("Removed unreachable peer", peer)
Log.Println("Removed unreachable peer", peer)
}
router.Ourself = NewLocalPeer(name, nickName, router)
router.Macs = NewMacCache(macMaxAge, onMacExpiry)
@@ -84,6 +82,9 @@ func NewRouter(config Config, name PeerName, nickName string) *Router {
return router
}
// Start listening for TCP connections, locally captured packets, and
// packets forwarded over UDP. This is separate from NewRouter so
// that gossipers can register before we start forming connections.
func (router *Router) Start() {
// we need two pcap handles since they aren't thread-safe
var pio PacketSourceSink
@@ -95,10 +96,6 @@ func (router *Router) Start() {
po, err = NewPcapO(router.Iface.Name)
checkFatal(err)
}
router.Ourself.Start()
router.Macs.Start()
router.Routes.Start()
router.ConnectionMaker.Start()
router.UDPListener = router.listenUDP(router.Port, po)
router.listenTCP(router.Port)
if pio != nil {
@@ -129,12 +126,12 @@ func (router *Router) Status() string {
}
func (router *Router) sniff(pio PacketSourceSink) {
log.Println("Sniffing traffic on", router.Iface)
Log.Println("Sniffing traffic on", router.Iface)
dec := NewEthernetDecoder()
mac := router.Iface.HardwareAddr
if router.Macs.Enter(mac, router.Ourself.Peer) {
log.Println("Discovered our MAC", mac)
Log.Println("Discovered our MAC", mac)
}
go func() {
for {
@@ -152,7 +149,7 @@ func (router *Router) handleCapturedPacket(frameData []byte, dec *EthernetDecode
if decodedLen == 0 {
return
}
srcMac := dec.eth.SrcMAC
srcMac := dec.Eth.SrcMAC
srcPeer, found := router.Macs.Lookup(srcMac)
// We need to filter out frames we injected ourselves. For such
// frames, the srcMAC will have been recorded as associated with a
@@ -161,22 +158,18 @@ func (router *Router) handleCapturedPacket(frameData []byte, dec *EthernetDecode
return
}
if router.Macs.Enter(srcMac, router.Ourself.Peer) {
log.Println("Discovered local MAC", srcMac)
Log.Println("Discovered local MAC", srcMac)
}
if dec.DropFrame() {
return
}
dstMac := dec.eth.DstMAC
dstMac := dec.Eth.DstMAC
dstPeer, found := router.Macs.Lookup(dstMac)
if found && dstPeer == router.Ourself.Peer {
return
}
df := decodedLen == 2 && (dec.ip.Flags&layers.IPv4DontFragment != 0)
if df {
router.LogFrame("Forwarding DF", frameData, &dec.eth)
} else {
router.LogFrame("Forwarding", frameData, &dec.eth)
}
router.LogFrame("Forwarding", frameData, dec)
// at this point we are handing over the frame to forwarders, so
// we need to make a copy of it in order to prevent the next
// capture from overwriting the data
@@ -187,11 +180,11 @@ func (router *Router) handleCapturedPacket(frameData []byte, dec *EthernetDecode
// If we don't know which peer corresponds to the dest MAC,
// broadcast it.
if !found {
router.Ourself.Broadcast(df, frameCopy, dec)
router.Ourself.Broadcast(frameCopy, dec)
return
}
err := router.Ourself.Forward(dstPeer, df, frameCopy, dec)
err := router.Ourself.Forward(dstPeer, frameCopy, dec)
if ftbe, ok := err.(FrameTooBigError); ok {
err = dec.sendICMPFragNeeded(ftbe.EPMTU, po.WritePacket)
}
@@ -208,7 +201,7 @@ func (router *Router) listenTCP(localPort int) {
for {
tcpConn, err := ln.AcceptTCP()
if err != nil {
log.Println(err)
Log.Errorln(err)
continue
}
router.acceptTCP(tcpConn)
@@ -222,10 +215,9 @@ func (router *Router) acceptTCP(tcpConn *net.TCPConn) {
// on router.Port and we wait for them to send us something on UDP to
// start.
remoteAddrStr := tcpConn.RemoteAddr().String()
log.Printf("->[%s] connection accepted\n", remoteAddrStr)
Log.Printf("->[%s] connection accepted\n", remoteAddrStr)
connRemote := NewRemoteConnection(router.Ourself.Peer, nil, remoteAddrStr, false, false)
connLocal := NewLocalConnection(connRemote, tcpConn, nil, router)
connLocal.Start(true)
StartLocalConnection(connRemote, tcpConn, nil, router, true)
}
func (router *Router) listenUDP(localPort int, po PacketSink) *net.UDPConn {
@@ -253,10 +245,10 @@ func (router *Router) udpReader(conn *net.UDPConn, po PacketSink) {
if err == io.EOF {
return
} else if err != nil {
log.Println("ignoring UDP read error", err)
Log.Warnln("ignoring UDP read error", err)
continue
} else if n < NameSize {
log.Println("ignoring too short UDP packet from", sender)
Log.Warnln("ignoring too short UDP packet from", sender)
continue
}
name := PeerNameFromBin(buf[:NameSize])
@@ -318,20 +310,13 @@ func (router *Router) handleUDPPacketFunc(relayConn *LocalConnection, dec *Ether
return
}
df := decodedLen == 2 && (dec.ip.Flags&layers.IPv4DontFragment != 0)
if dstPeer != router.Ourself.Peer {
// it's not for us, we're just relaying it
if df {
router.LogFrame("Relaying DF", frame, &dec.eth)
} else {
router.LogFrame("Relaying", frame, &dec.eth)
}
err := router.Ourself.Relay(srcPeer, dstPeer, df, frame, dec)
router.LogFrame("Relaying", frame, dec)
err := router.Ourself.Relay(srcPeer, dstPeer, frame, dec)
if ftbe, ok := err.(FrameTooBigError); ok {
err = dec.sendICMPFragNeeded(ftbe.EPMTU, func(icmpFrame []byte) error {
return router.Ourself.Forward(srcPeer, false, icmpFrame, nil)
return router.Ourself.Forward(srcPeer, icmpFrame, nil)
})
}
@@ -339,21 +324,21 @@ func (router *Router) handleUDPPacketFunc(relayConn *LocalConnection, dec *Ether
return
}
srcMac := dec.eth.SrcMAC
dstMac := dec.eth.DstMAC
srcMac := dec.Eth.SrcMAC
dstMac := dec.Eth.DstMAC
if router.Macs.Enter(srcMac, srcPeer) {
log.Println("Discovered remote MAC", srcMac, "at", srcPeer)
Log.Println("Discovered remote MAC", srcMac, "at", srcPeer)
}
if po != nil {
router.LogFrame("Injecting", frame, &dec.eth)
router.LogFrame("Injecting", frame, dec)
checkWarn(po.WritePacket(frame))
}
dstPeer, found = router.Macs.Lookup(dstMac)
if !found || dstPeer != router.Ourself.Peer {
router.LogFrame("Relaying broadcast", frame, &dec.eth)
router.Ourself.RelayBroadcast(srcPeer, df, frame, dec)
router.LogFrame("Relaying broadcast", frame, dec)
router.Ourself.RelayBroadcast(srcPeer, frame, dec)
}
}
}
@@ -429,7 +414,7 @@ func (router *Router) applyTopologyUpdate(update []byte) (PeerNameSet, PeerNameS
// itself included in the update, and we didn't know about
// already. We ignore this; eventually we should receive an
// update containing a complete topology.
log.Println("Topology gossip:", err)
Log.Println("Topology gossip:", err)
return nil, nil, nil
}
if err != nil {
+6 -9
View File
@@ -22,26 +22,23 @@ type Routes struct {
}
func NewRoutes(ourself *LocalPeer, peers *Peers) *Routes {
recalculate := make(chan *struct{}, 1)
wait := make(chan chan struct{})
routes := &Routes{
ourself: ourself,
peers: peers,
unicast: make(map[PeerName]PeerName),
unicastAll: make(map[PeerName]PeerName),
broadcast: make(map[PeerName][]PeerName),
broadcastAll: make(map[PeerName][]PeerName)}
broadcastAll: make(map[PeerName][]PeerName),
recalculate: recalculate,
wait: wait}
routes.unicast[ourself.Name] = UnknownPeerName
routes.unicastAll[ourself.Name] = UnknownPeerName
routes.broadcast[ourself.Name] = []PeerName{}
routes.broadcastAll[ourself.Name] = []PeerName{}
return routes
}
func (routes *Routes) Start() {
recalculate := make(chan *struct{}, 1)
wait := make(chan chan struct{})
routes.recalculate = recalculate
routes.wait = wait
go routes.run(recalculate, wait)
return routes
}
func (routes *Routes) PeerNames() PeerNameSet {
+4 -4
View File
@@ -1,12 +1,12 @@
package router
import (
"log"
"net"
"syscall"
"code.google.com/p/gopacket"
"code.google.com/p/gopacket/layers"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
. "github.com/weaveworks/weave/common"
)
type UDPSender interface {
@@ -87,7 +87,7 @@ func (sender *RawUDPSender) Send(msg []byte) error {
}
defer f.Close()
fd := int(f.Fd())
log.Println("EMSGSIZE on send, expecting PMTU update (IP packet was",
Log.Println("EMSGSIZE on send, expecting PMTU update (IP packet was",
len(packet), "bytes, payload was", len(msg), "bytes)")
pmtu, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU)
if err != nil {
+4 -3
View File
@@ -5,21 +5,22 @@ import (
"crypto/rand"
"encoding/gob"
"fmt"
"log"
"net"
. "github.com/weaveworks/weave/common"
)
var void = struct{}{}
func checkFatal(e error) {
if e != nil {
log.Fatal(e)
Log.Fatal(e)
}
}
func checkWarn(e error) {
if e != nil {
log.Println(e)
Log.Warnln(e)
}
}
+19 -8
View File
@@ -2,38 +2,49 @@
<div class="section footer">
<div class="container">
<div class="row clearfix">
<div class="col-md-3 col-md-offset-2 column">
<div class="col-sm-3 col-md-2">
<b>Company</b><br>
<a href="http://weave.works/about.html">About Weave</a><br>
<!-- <a href="#">Meet the Team</a><br> -->
<a href="http://weave.works/team.html">Meet the Team</a><br>
<!-- <a href="#">Careers</a><br> -->
<a href="http://blog.weave.works">Blog</a><br>
<br>
<a href="http://weave.works/articles/">Learn More</a><br>
</div>
<div class="col-sm-3 col-md-2">
<b>Community</b><br>
<a href="http://weave.works/help/index.html#partners">Partners</a><br>
<a href="http://github.com/weaveworks">Code</a><br>
</div>
<div class="col-md-3 column">
<div class="col-sm-3 col-md-2">
<b>Documentation</b><br>
<a href="http://docs.weave.works">Docs</a><br>
<a href="http://weave.works/guides/">Getting Started Guides</a><br>
<a href="http://weave.works/help/index.html">Help and Support</a><br>
<a href="http://blog.weave.works">Blog</a><br>
</div>
<div class="col-md-3 column">
<div class="col-sm-3 col-md-2">
<b>Get in Touch</b><br>
<a href="mailto:help@weave.works">help@weave.works</a><br>
<div class="hidden-md hidden-lg">
<a href="mailto:help@weave.works">help@weave.works</a><br>
</div>
<a href="http://twitter.com/weaveworks">Twitter</a><br>
<a href="http://github.com/weaveworks">Github</a><br>
<a href="http://slideshare.net/weaveworks">Slideshare</a><br>
<a href="https://www.linkedin.com/company/weaveworks">Linkedin</a><br>
<a href="https://plus.google.com/+WeaveWorksInc/">GooglePlus</a><br>
</div>
<div class="col-md-3 hidden-xs hidden-sm">
<b>Talk to a human</b>
<div align="center">
<br>
<a href="mailto:help@weave.works" class="btn btn-lg">Drop us a line</a>
</div>
</div>
</div>
<div class="row clearfix weavefooter">
<div class="row">
<br>
<br>
<div class="col-md-4 col-md-offset-2">
<div class="col-sm-4">
<p class="copyright">&copy; Weaveworks 2014 &dash; 2015</p>
</div>
</div>
+8 -6
View File
@@ -1,9 +1,10 @@
<div class="section header navbaronly">
<nav class="navbar navbar-inverse">
<div class="section section-header navbaronly">
<div class="navbar navbar-inverse">
<div class="container">
<div><!-- class="navbar-header"> -->
<a class="navbar-brand" href="http://weave.works/"><img src="http://weave.works/img/weave-logo.png"></a>
</div>
<div class="navbar-header">
<a class="navbar-brand" href="http://weave.works/"><img src="http://weave.works/img/weave-logo.png"></a>
</div>
<div class="nav-wrapper">
<ul class="nav navbar-nav navbar-right">
<li>
<a href="http://docs.weave.works">Docs</a>
@@ -18,6 +19,7 @@
<a href="http://weave.works/help/index.html">Help &amp; Support</a>
</li>
</ul>
</div>
</div>
</nav>
</div>
</div>
+3 -3
View File
@@ -19,7 +19,7 @@
<!-- CSS
–––––––––––––––––––––––––––––––––––––––––––––––––– -->
<link rel="stylesheet" type="text/css" href="http://weave.works/less/custom.css">
<link rel="stylesheet" type="text/css" href="assets/custom.css">
<!-- Favicon
–––––––––––––––––––––––––––––––––––––––––––––––––– -->
@@ -33,8 +33,8 @@
<div class="content">
{% include header-navbaronly.html %}
<div class="container">
<div class="col-md-10 col-md-offset-1">
<div class="container markdown-page">
<div class="col-md-8 col-md-offset-2">
{{ content }}
</div>
</div>
File diff suppressed because it is too large. Load diff
+4 -7
View File
@@ -57,8 +57,7 @@ $ make
This will build the weave components and package them into three
Docker images (`weaveworks/weave`, `weaveworks/weavedns`, and
`weaveworks/weaveexec`). These are then exported (as `weave.tar`,
`weavedns.tar` and `weaveexec.tar`).
`weaveworks/weaveexec`). These are then exported as `weave.tar`.
## Building in a Docker container
@@ -171,15 +170,13 @@ you can do so with
$ vagrant ssh -c 'make -C src/github.com/weaveworks/weave'
```
you should then find container snapshot tarballs in the top-level
directory. You can use these snapshots with `docker load` against a
different host, e.g.
you should then find a `weave.tar` container snapshot tarball in the
top-level directory. You can use that snapshot with `docker load`
against a different host, e.g.
```bash
$ export DOCKER_HOST=tcp://<HOST:PORT>
$ docker load < weave.tar
$ docker load < weavedns.tar
$ docker load < weaveexec.tar
```
You can provide extra Vagrant configuration by putting a file
+4 -4
View File
@@ -57,8 +57,10 @@ our data centre.
### <a name="docker"></a>Seamless Docker integration
Weave includes a [proxy](proxy.html) so that containers launched via
the Docker [command-line interface](https://docs.docker.com/reference/commandline/cli/) or
Weave includes a [Docker API proxy](proxy.html) so that containers
launched via the Docker
[command-line interface](https://docs.docker.com/reference/commandline/cli/)
or
[remote API](https://docs.docker.com/reference/api/docker_remote_api/)
are attached to the weave network before they begin execution.
@@ -141,10 +143,8 @@ Let's begin by configuring weave's allocator to manage multiple
subnets:
host1$ weave launch -iprange 10.2.0.0/16 -ipsubnet 10.2.1.0/24
host1$ weave launch-dns && weave launch-proxy
host1$ eval $(weave proxy-env)
host2$ weave launch -iprange 10.2.0.0/16 -ipsubnet 10.2.1.0/24 $HOST1
host2$ weave launch-dns && weave launch-proxy
host2$ eval $(weave proxy-env)
This delegates the entire 10.2.0.0/16 subnet to weave, and instructs
+3 -2
View File
@@ -29,8 +29,9 @@ encrypted, which carry encapsulated network packets. These
Weave creates a network bridge on the host. Each container is
connected to that bridge via a veth pair, the container side of which
is given the IP address & netmask supplied by the proxy. Also
connected to the bridge is the weave router container.
is given an IP address & netmask supplied either by the user or
Weave's IP address allocator. Also connected to the bridge is the
weave router container.
A weave router captures Ethernet packets from its bridge-connected
interface in promiscuous mode, using 'pcap'. This typically excludes
+27 -17
View File
@@ -1,12 +1,12 @@
---
title: Weave Proxy
title: Weave Docker API Proxy
layout: default
---
# Weave Proxy
# Weave Docker API Proxy
The proxy automatically attaches containers to the weave network when
they are started using the ordinary Docker
The Docker API proxy automatically attaches containers to the weave
network when they are started using the ordinary Docker
[command-line interface](https://docs.docker.com/reference/commandline/cli/)
or
[remote API](https://docs.docker.com/reference/api/docker_remote_api/),
@@ -23,19 +23,27 @@ instead of `weave run`.
## <a name="setup"></a>Setup
The proxy sits between the Docker client (command line or API) and the
Docker daemon, intercepting the communication between the two.
Docker daemon, intercepting the communication between the two. You can
start it simultaneously with the router and weaveDNS via `launch`:
To start the proxy, run
host1$ weave launch
host1$ weave launch-proxy
or independently via `launch-proxy`:
By default, the proxy listens on port 12375, on all network
interfaces. This can be adjusted with the `-H` argument, e.g.
host1$ weave launch-router && weave launch-dns && weave launch-proxy
The first form is more convenient, however you can only pass proxy
related configuration arguments to `launch-proxy` so if you need to
modify the default behaviour you will have to use the latter.
By default, the proxy listens on /var/run/weave.sock and port 12375, on
all network interfaces. This can be adjusted with the `-H` argument, e.g.
host1$ weave launch-proxy -H tcp://127.0.0.1:9999
If you are working with a remote docker daemon, then any firewalls
inbetween need to be configured to permit access to the proxy port.
Multiple -H arguments can be specified. If you are working with a remote
docker daemon, then any firewalls inbetween need to be configured to permit
access to the proxy port.
All docker commands can be run via the proxy, so it is safe to adjust
your `DOCKER_HOST` to point at the proxy. Weave provides a convenient
@@ -49,10 +57,12 @@ Alternatively, the proxy host can be set on a per-command basis with
host1$ docker $(weave proxy-config) ps
The proxy can be stopped with
The proxy can be stopped independently with
host1$ weave stop-proxy
or in conjunction with the router and weaveDNS via `stop`.
If you set your `DOCKER_HOST` to point at the proxy, remember to
revert to the original setting.
@@ -100,10 +110,10 @@ To start a container without connecting it to the weave network, pass
host1$ docker run -ti -e WEAVE_CIDR=none ubuntu
If you do not want IPAM to be used by default, the proxy needs to be
passed the `--no-default-ipam` flag, e.g.
If you do not want an IP to be assigned by default, the proxy needs to
be passed the `--no-default-ipalloc` flag, e.g.,
host1$ docker launch-proxy --no-default-ipam
host1$ docker launch-proxy --no-default-ipalloc
In this configuration, containers with no `WEAVE_CIDR` environment
variable will not be connected to the weave network. Containers
@@ -111,7 +121,7 @@ started with a `WEAVE_CIDR` environment variable are handled as
before. To automatically assign an address in this mode, we start the
container with a blank `WEAVE_CIDR`, e.g.
host1$ docker run -ti -e WEAVE_CIDR= ubuntu
host1$ docker run -ti -e WEAVE_CIDR="" ubuntu
## <a name="dns"></a>Automatic discovery
@@ -176,7 +186,7 @@ containers on the weave network with `weave run`:
The arguments after `run` are passed through to `docker run` so you
can freely specify whichever docker options are appropriate. Once the
container is started, `weave run` attaches it to the weave network, in
this example with an address allocated by IPAM. If you wish you can
this example with an automatically allocated IP. If you wish you can
specify addresses manually instead:
$ weave run 10.2.1.1/24 -ti ubuntu
+1 -1
View File
@@ -142,7 +142,7 @@ is waiting for a while before connecting again.
There may also be further sections for the
[IP address allocator](ipam.html#troubleshooting),
[weaveDNS](weavedns.html#troubleshooting), and
[proxy](proxy.html#troubleshooting).
[Weave Docker API Proxy](proxy.html#troubleshooting).
### <a name="list-attached-containers"></a>List attached containers
+31 -16
View File
@@ -28,13 +28,23 @@ hosts.
WeaveDNS is deployed as a set of containers that communicate with each
other over the weave network. One such container needs to be started
on every weave host, by invoking the weave script command
`launch-dns`:
on every weave host, either simultaneously with the Weave router and
Weave Docker API proxy via `launch`:
```bash
host1$ weave launch && weave launch-dns && weave launch-proxy
host1$ weave launch
host1$ eval $(weave proxy-env)
```
or independently via `launch-dns`:
```bash
host1$ weave launch-router && weave launch-dns && weave launch-proxy
host1$ eval $(weave proxy-env)
```
The first form is more convenient, however you can only pass weaveDNS
related configuration arguments to `launch-dns` so if you need to
modify the default behaviour you will have to use the latter.
Application containers will use weaveDNS automatically if it is
running at the point when they are started. They will use it for name
@@ -49,10 +59,10 @@ root@ubuntu:/# ping pingme
...
```
If both hostname and container name are specified at the same time the
hostname takes precedence; in this circumstance if the hostname is not
in the weaveDNS domain the container will not be registered, but will
still use weaveDNS for resolution.
> **Please note** if both hostname and container name are specified at
> the same time the hostname takes precedence; in this circumstance if
> the hostname is not in the weaveDNS domain the container will *not* be
> registered, but will still use weaveDNS for resolution.
It is also possible to force or forbid an application container's use
of weaveDNS with the `--with-dns` and `--without-dns` options to
@@ -62,20 +72,25 @@ detection of the weaveDNS container.
Each weaveDNS container started with `launch-dns` needs its own unique
IP address in a subnet that is common to all weaveDNS containers. In
the example above we did not specify such an address, so one was
allocated automatically by IPAM from the default subnet; you can
however specify an address in CIDR format manually. In this case you
are responsible for ensuring that the IP addresses specified are
uniquely allocated and not in use by any other container.
allocated automatically from the default subnet; you can however
specify an address in CIDR format manually. In this case you are
responsible for ensuring that the IP addresses specified are uniquely
allocated and not in use by any other container.
Finally, WeaveDNS containers can be stopped with `stop-dns`.
Finally, weaveDNS can be stopped independently with
host1$ weave stop-dns
or in conjunction with the router and proxy via `stop`.
## <a name="how-it-works"></a>How it works
The weaveDNS container running on every host acts as the nameserver
for containers on that host. It learns about hostnames for local
containers from the proxy and from the `weave run` command. If a
hostname is in the `.weave.local` domain then weaveDNS records the
association of that name with the container's weave IP address(es).
containers from the Weave Docker API proxy and from the `weave run`
command. If a hostname is in the `.weave.local` domain then weaveDNS
records the association of that name with the container's weave IP
address(es).
When weaveDNS is queried for a name in the `.weave.local` domain, it
first checks its own records. If the name is not found there, it asks
@@ -112,7 +127,7 @@ Returning to our earlier example, let us start an additional `pingme`
container, this time on the 2nd host, and then run some ping tests...
```bash
host2$ weave launch && weave launch-dns && weave launch-proxy
host2$ weave launch
host2$ eval $(weave proxy-env)
host2$ docker run -dti --name=pingme ubuntu
+4 -4
View File
@@ -4,15 +4,15 @@
start_suite "Run weave with --local"
run_on $HOST1 sudo ./weave --local reset
run_on $HOST1 sudo weave --local reset
run_on $HOST1 sudo ./weave --local launch -iprange 10.2.5.0/24
run_on $HOST1 sudo weave --local launch -iprange 10.2.5.0/24
assert_raises "docker_on $HOST1 ps | grep weave"
run_on $HOST1 sudo ./weave --local run 10.2.6.5/24 -ti --name=c1 $SMALL_IMAGE /bin/sh
run_on $HOST1 sudo weave --local run 10.2.6.5/24 -ti --name=c1 $SMALL_IMAGE /bin/sh
assert_raises "exec_on $HOST1 c1 $CHECK_ETHWE_UP"
run_on $HOST1 sudo ./weave --local run -ti --name=c2 $SMALL_IMAGE /bin/sh
run_on $HOST1 sudo weave --local run -ti --name=c2 $SMALL_IMAGE /bin/sh
assert_raises "exec_on $HOST1 c2 $CHECK_ETHWE_UP"
end_suite
+5 -5
View File
@@ -15,8 +15,8 @@ assert_peers() {
start_suite "Connecting and forgetting routers after launch"
weave_on $HOST1 launch
weave_on $HOST2 launch
weave_on $HOST1 launch-router
weave_on $HOST2 launch-router
start_container $HOST1 $C1/24 --name=c1
start_container $HOST2 $C2/24 --name=c2
@@ -31,10 +31,10 @@ assert_raises "weave_on $HOST2 connect --replace $HOST1 $HOST2"
assert_peers $HOST2 "$HOST1\n$HOST2"
assert_raises "exec_on $HOST1 c1 $PING $C2"
# Forget everyone and disonnect
# Forget everyone and disconnect
assert_raises "weave_on $HOST2 forget $HOST1 $HOST2"
assert_raises "weave_on $HOST1 stop"
assert_raises "weave_on $HOST1 launch"
assert_raises "weave_on $HOST1 stop-router"
assert_raises "weave_on $HOST1 launch-router"
assert_peers $HOST2 ""
assert_raises "exec_on $HOST1 c1 sh -c '! $PING $C2'"
+2 -2
View File
@@ -29,8 +29,8 @@ check() {
start_suite "Resolve names across hosts (with and without IPAM), and repopulate on restart"
weave_on $HOST1 launch -iprange $UNIVERSE
weave_on $HOST2 launch -iprange $UNIVERSE $HOST1
weave_on $HOST1 launch-router -iprange $UNIVERSE
weave_on $HOST2 launch-router -iprange $UNIVERSE $HOST1
start_container $HOST2 $C2/24 --name=c2 -h $NAME2
start_container_with_dns $HOST1 $C1/24 --name=c1
+4
View File
@@ -33,4 +33,8 @@ assert_expected_fqdn "$HOSTNAME" -h $HOSTNAME --name=$NAME
assert_expected_fqdn "$HOSTNAME" --hostname=$HOSTNAME --name=$NAME
assert_expected_fqdn "$HOSTNAME" --hostname $HOSTNAME --name=$NAME
# Ensure we can launch a container with a name that is too long for hostname derivation
SIXTY_FIVE_CHARS=01234567890123456789012345678901234567890123456789012345678901234
assert_raises "start_container $HOST1 --name=$SIXTY_FIVE_CHARS"
end_suite
+1 -1
View File
@@ -20,7 +20,7 @@ assert_resolution() {
start_suite "With or without DNS test"
weave_on $HOST1 launch
weave_on $HOST1 launch-router
DNS_IP=$(weave_on $HOST1 docker-bridge-ip)
+1 -1
View File
@@ -73,7 +73,7 @@ start_suite "Weave run/start/attach/detach/expose/hide with multiple cidr argume
# specific address, i.e. we are assuming that IPAM always returns the
# lowest available address in the subnet
weave_on $HOST1 launch -debug -iprange 10.2.3.0/24
weave_on $HOST1 launch-router -debug -iprange 10.2.3.0/24
launch_dns_on $HOST1 10.254.254.254/24
# Run container with three cidrs
+1 -1
View File
@@ -6,7 +6,7 @@ start_suite "Run docker-py test suite against the proxy"
docker_on $HOST1 pull joffrey/docker-py >/dev/null
weave_on $HOST1 launch-proxy --no-default-ipam
weave_on $HOST1 launch-proxy --no-default-ipalloc
if docker_on $HOST1 run \
-e NOT_ON_HOST=true \
+1 -1
View File
@@ -26,7 +26,7 @@ run_container "grep ^1$ /sys/class/net/ethwe/carrier"
build_image false '["/bin/false"]' ''
run_container "--entrypoint='grep' false ^1$ /sys/class/net/ethwe/carrier"
weave_on $HOST1 launch -iprange 10.2.2.0/24
weave_on $HOST1 launch-router -iprange 10.2.2.0/24
docker_on $HOST1 kill weaveproxy
weave_on $HOST1 launch-proxy
+1 -1
View File
@@ -13,6 +13,6 @@ assert_raises "docker $(weave_on $HOST1 proxy-config) $CMD"
# Check we can use the weave script through the proxy
assert_raises "eval '$(weave_on $HOST1 proxy-env)' ; $WEAVE version"
assert_raises "eval '$(weave_on $HOST1 proxy-env)' ; $WEAVE ps"
assert_raises "eval '$(weave_on $HOST1 proxy-env)' ; $WEAVE launch"
assert_raises "eval '$(weave_on $HOST1 proxy-env)' ; $WEAVE launch-router"
end_suite
+3 -3
View File
@@ -17,10 +17,10 @@ assert_no_ethwe() {
start_suite "Ping proxied containers over cross-host weave network (with IPAM)"
weave_on $HOST1 launch -iprange $UNIVERSE
weave_on $HOST2 launch -iprange $UNIVERSE $HOST1
weave_on $HOST1 launch-router -iprange $UNIVERSE
weave_on $HOST2 launch-router -iprange $UNIVERSE $HOST1
weave_on $HOST1 launch-proxy
weave_on $HOST2 launch-proxy --no-default-ipam
weave_on $HOST2 launch-proxy --no-default-ipalloc
start $HOST1 --name=auto
start $HOST1 --name=none -e WEAVE_CIDR=none
+1 -1
View File
@@ -6,7 +6,7 @@ start_suite "Boot the proxy with TLS-enabled Docker support"
PWD=$($SSH $HOST1 pwd)
weave_on $HOST1 launch
weave_on $HOST1 launch-router
weave_on $HOST1 launch-proxy \
--tlsverify \
--tlscacert $PWD/tls/ca.pem \
+21 -5
View File
@@ -2,6 +2,8 @@
set -e
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Protect against being sourced multiple times to prevent
# overwriting assert.sh global state
if ! [ -z "$SOURCED_CONFIG_SH" ]; then
@@ -25,9 +27,11 @@ fi
HOST1=$(echo $HOSTS | cut -f 1 -d ' ')
HOST2=$(echo $HOSTS | cut -f 2 -d ' ')
. ./assert.sh
. "$DIR/assert.sh"
SSH=${SSH:-ssh -l vagrant -i ./insecure_private_key -o UserKnownHostsFile=./.ssh_known_hosts -o CheckHostIP=no -o StrictHostKeyChecking=no}
SSH_DIR=${SSH_DIR:-$DIR}
SSH=${SSH:-ssh -l vagrant -i "$SSH_DIR/insecure_private_key" -o "UserKnownHostsFile=$SSH_DIR/.ssh_known_hosts" -o CheckHostIP=no -o StrictHostKeyChecking=no}
SMALL_IMAGE="gliderlabs/alpine"
DNS_IMAGE="aanand/docker-dnsutils"
@@ -41,6 +45,18 @@ DOCKER_PORT=2375
WEAVEDNS_ARGS="--no-cache"
[ -n "$DEBUG" ] && WEAVEDNS_ARGS="$WEAVEDNS_ARGS --debug"
upload_executable() {
host=$1
file=$2
prefix=${3:-/usr/local/bin/}
suffix=$(basename "$file")
target="$prefix$suffix"
[ -z "$DEBUG" ] || greyly echo "Uploading to $host: $file -> $target" >&2
<"$file" remote $host $SSH $host sh -c "cat | sudo tee $target >/dev/null"
run_on $host "sudo chmod a+x $target"
}
remote() {
rem=$1
shift 1
@@ -172,8 +188,8 @@ assert_dns_ptr_record() {
start_suite() {
for host in $HOST1 $HOST2; do
[ -z "$DEBUG" ] || echo "Cleaning up on $host: removing all containers and resetting weave"
rm_containers $host $(docker_on $host ps -aq 2>/dev/null)
weave_on $host reset 2>/dev/null
rm_containers $host $(docker_on $host ps -aq 2>/dev/null)
done
whitely echo "$@"
}
@@ -182,5 +198,5 @@ end_suite() {
whitely assert_end
}
WEAVE=../weave
DOCKER_NS=./bin/docker-ns
WEAVE=$DIR/../weave
DOCKER_NS=$DIR/../bin/docker-ns
+12 -6
View File
@@ -9,7 +9,7 @@ set -ex
KEY_FILE=/tmp/gce_private_key.json
SSH_KEY_FILE=$HOME/.ssh/gce_ssh_key
PROJECT=positive-cocoa-90213
PROJECT=${PROJECT:-positive-cocoa-90213}
IMAGE=ubuntu-14-04
TEMPLATE_NAME="test-template-2"
ZONE=us-central1-a
@@ -36,11 +36,17 @@ function destroy {
names="$(vm_names)"
for i in {0..10}; do
# gcloud instances delete can sometimes hang.
timeout 60s /bin/bash -c "gcloud compute instances delete --zone $ZONE -q $names || true"
# 124 means it timed out
if [ $? -ne 124 ]; then
return
fi
case $(set +e; timeout 60s /bin/bash -c "gcloud compute instances delete --zone $ZONE -q $names >/dev/null 2>&1 || true"; echo $?) in
0)
return 0
;;
124)
# 124 means it timed out
break
;;
*)
return 1
esac
done
}
+7 -6
View File
@@ -1,9 +1,10 @@
#!/bin/bash
. ./config.sh
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "$DIR/config.sh"
whitely echo Sanity checks
if ! bash ./sanity_check.sh; then
if ! bash "$DIR/sanity_check.sh"; then
whitely echo ...failed
exit 1
fi
@@ -27,8 +28,8 @@ trap check_test_status EXIT
TESTS="${@:-*_test.sh}"
# If running on circle, use the scheduler to work out what tests to run
if [ -n "$CIRCLECI" ]; then
TESTS=$(echo $TESTS | ./sched sched $CIRCLE_BUILD_NUM $CIRCLE_NODE_TOTAL $CIRCLE_NODE_INDEX)
if [ -n "$CIRCLECI" -a -z "$NO_SCHEDULER" ]; then
TESTS=$(echo $TESTS | "$DIR/sched" sched $CIRCLE_BUILD_NUM $CIRCLE_NODE_TOTAL $CIRCLE_NODE_INDEX)
fi
echo Running $TESTS
@@ -39,7 +40,7 @@ for t in $TESTS; do
. $t
# Report test runtime when running on circle, to help scheduler
if [ -n "$CIRCLECI" ]; then
./sched time $t $(bc -l <<< "$tests_time/1000000000")
if [ -n "$CIRCLECI" -a -z "$NO_SCHEDULER" ]; then
"$DIR/sched" time $t $(bc -l <<< "$tests_time/1000000000")
fi
done
+17
View File
@@ -9,5 +9,22 @@ run_on $HOST2 $PING $HOST1
run_on $HOST1 $PING $HOST2
whitely echo Check we can reach docker
echo
echo Host Version Info: $HOST1
echo =====================================
echo "# docker version"
docker_on $HOST1 version
echo "# docker info"
docker_on $HOST1 info
echo "# weave version"
weave_on $HOST1 version
echo
echo Host Version Info: $HOST2
echo =====================================
echo "# docker version"
docker_on $HOST2 version
echo "# docker info"
docker_on $HOST2 info
echo "# weave version"
weave_on $HOST2 version
+4 -5
View File
@@ -2,6 +2,8 @@
set -e
cd "$(dirname "${BASH_SOURCE[0]}")"
. ./config.sh
(cd ./tls && go get ./... && go run generate_certs.go $HOSTS)
@@ -10,12 +12,9 @@ echo "Copying weave images, scripts, and certificates to hosts, and"
echo " prefetch test images"
for HOST in $HOSTS; do
docker_on $HOST load -i ../weave.tar
docker_on $HOST load -i ../weavedns.tar
docker_on $HOST load -i ../weaveexec.tar
run_on $HOST mkdir -p bin
cat ../bin/docker-ns | run_on $HOST sh -c "cat > $DOCKER_NS"
cat ../weave | run_on $HOST sh -c "cat > ./weave"
run_on $HOST chmod a+x $DOCKER_NS ./weave
upload_executable $HOST ../bin/docker-ns
upload_executable $HOST ../weave
rsync -az -e "$SSH" ./tls/ $HOST:~/tls
for IMG in $TEST_IMAGES ; do
docker_on $HOST inspect --format=" " $IMG >/dev/null 2>&1 || docker_on $HOST pull $IMG
+179 -124
View File
@@ -26,36 +26,42 @@ CIDR_REGEXP="$IP_REGEXP/[0-9]{1,2}"
usage() {
echo "Usage:"
echo "weave setup"
echo "weave launch [-password <password>] [-nickname <nickname>] [-nodiscovery]"
echo " [-iprange <cidr> [-ipsubnet <cidr>]] <peer> ..."
echo "weave launch-dns [<addr>]"
echo "weave launch-proxy [-H <endpoint>] [--with-dns | --without-dns]"
echo " [--no-default-ipam]"
echo "weave connect [--replace] [<peer> ...]"
echo "weave forget <peer> ..."
echo "weave run [--with-dns | --without-dns] [<addr> ...] <docker run args> ..."
echo "weave start [<addr> ...] <container_id>"
echo "weave attach [<addr> ...] <container_id>"
echo "weave detach [<addr> ...] <container_id>"
echo "weave dns-add <ip_address> [<ip_address> ...] <container_id> [-h <fqdn>]"
echo "weave dns-remove <ip_address> [<ip_address> ...] <container_id> [-h <fqdn>]"
echo "weave expose [<addr> ...] [-h <fqdn>]"
echo "weave hide [<addr> ...]"
echo "weave ps [<container_id> ...]"
echo "weave launch [-password <password>] [-nickname <nickname>] [-nodiscovery]"
echo " [-iprange <cidr> [-ipsubnet <cidr>]]"
echo " [-initpeercount <count>] <peer> ..."
echo "weave launch-router [-password <password>] [-nickname <nickname>] [-nodiscovery]"
echo " [-iprange <cidr> [-ipsubnet <cidr>]]"
echo " [-initpeercount <count>] <peer> ..."
echo "weave launch-dns [<addr>]"
echo "weave launch-proxy [-H <endpoint>] [--with-dns | --without-dns]"
echo " [--no-default-ipalloc]"
echo "weave connect [--replace] [<peer> ...]"
echo "weave forget <peer> ..."
echo "weave run [--with-dns | --without-dns] [<addr> ...]"
echo " <docker run args> ..."
echo "weave start [<addr> ...] <container_id>"
echo "weave attach [<addr> ...] <container_id>"
echo "weave detach [<addr> ...] <container_id>"
echo "weave dns-add <ip_address> [<ip_address> ...] <container_id> [-h <fqdn>]"
echo "weave dns-remove <ip_address> [<ip_address> ...] <container_id> [-h <fqdn>]"
echo "weave expose [<addr> ...] [-h <fqdn>]"
echo "weave hide [<addr> ...]"
echo "weave ps [<container_id> ...]"
echo "weave status"
echo "weave proxy-env"
echo "weave proxy-config"
echo "weave version"
echo "weave stop"
echo "weave stop-router"
echo "weave stop-dns"
echo "weave stop-proxy"
echo "weave reset"
echo "weave rmpeer <peer_id>"
echo "weave rmpeer <peer_id>"
echo
echo "where <peer> = <ip_address_or_fqdn>[:<port>]"
echo " <cidr> = <ip_address>/<routing_prefix_length>"
echo " <addr> = [ip:]<cidr> | net:<cidr> | net:default"
echo " <endpoint> = [tcp://][<ip_address>]:<port>"
echo " <endpoint> = [tcp://][<ip_address>]:<port> | [unix://]/path/to/socket"
echo " <peer_id> = <nickname> or weave internal peer ID"
exit 1
}
@@ -183,6 +189,21 @@ command_exists() {
command -v $1 >/dev/null 2>&1
}
fractional_sleep() {
case $1 in
*.*)
if [ -z "$NO_FRACTIONAL_SLEEP" ] ; then
sleep $1 >/dev/null 2>&1 && return 0
NO_FRACTIONAL_SLEEP=1
fi
sleep $((${1%.*} + 1))
;;
*)
sleep $1
;;
esac
}
run_iptables() {
# -w is recent addition to iptables
if [ -z "$CHECKED_IPTABLES_W" ] ; then
@@ -223,7 +244,7 @@ configure_arp_cache() {
}
# Send out an unsolicated ARP response to update ARP cache entries
# accross the weave network. We do this in addition to
# across the weave network. We do this in addition to
# configure_arp_cache because a) with those ARP cache settings it
# still takes a few seconds to correct a stale ARP mapping, and b)
# there is a kernel bug that means that the base_reachable_time
@@ -585,7 +606,7 @@ wait_for_status() {
echo "$err_msg" >&2
return 1
fi
sleep 1
fractional_sleep 0.1
done
}
@@ -593,7 +614,7 @@ wait_for_status() {
wait_for_log() {
fifo=/tmp/tmpfifo.$$
mkfifo $fifo || exit 1
docker logs -f $1 2>/dev/null >$fifo &
docker logs -f $1 >$fifo 2>&1 &
res=0
grep -q "$2" $fifo || res=1
kill $! 2>/dev/null || true
@@ -693,8 +714,13 @@ dns_args() {
DNS_ARGS="--dns $DOCKER_BRIDGE_IP"
if [ -n "$NAME_ARG" -a -z "$HOSTNAME_SPECIFIED" ] ; then
DOMAIN=$(dns_domain)
DNS_ARGS="$DNS_ARGS --hostname=$NAME_ARG.${DOMAIN%.}"
HOSTNAME_SPECIFIED=1
HOSTNAME="$NAME_ARG.${DOMAIN%.}"
if [ ${#HOSTNAME} -gt 64 ] ; then
echo "Container name too long to be used as hostname" >&2
else
DNS_ARGS="$DNS_ARGS --hostname=$HOSTNAME"
HOSTNAME_SPECIFIED=1
fi
fi
if [ -z "$DNS_SEARCH_SPECIFIED" ] ; then
if [ -z "$HOSTNAME_SPECIFIED" ] ; then
@@ -837,6 +863,8 @@ ipam_cidrs() {
IPAM_CIDRS="$IPAM_CIDRS $CIDR"
ALL_CIDRS="$ALL_CIDRS $CIDR"
else
# This is a plain IP address; warn if it clashes but carry on
command_exists netcheck && netcheck $1 || true
ALL_CIDRS="$ALL_CIDRS $1"
fi
shift 1
@@ -906,18 +934,122 @@ proxy_args() {
}
proxy_addr() {
if addr=$(docker logs $PROXY_CONTAINER_NAME 2>/dev/null | head -n3 | grep -oE "proxy listening on .*"); then
addr=${addr##* }
host=${addr%:*}
[ "$host" = "0.0.0.0" ] && host=$PROXY_HOST
port=${addr#*:}
echo "${1}tcp://${host:-$PROXY_HOST}:${port:-$PROXY_PORT}"
if addr=$(docker logs $PROXY_CONTAINER_NAME 2>&1 | head -n3 | grep -oE "proxy listening on .*"); then
echo "${1}${addr##* }" | sed "s/0.0.0.0/$PROXY_HOST/g"
return 0
fi
echo "$PROXY_CONTAINER_NAME container is not present. Have you launched it?" >&2
return 1
}
######################################################################
# launch helpers
######################################################################
launch_router() {
create_bridge
# We set the router name to the bridge mac since that is
# stable across re-creations of the containers.
#
# TODO use the mac of one of the physical host interfaces
# (eth0, wlan0, etc) so the name becomes stable across host
# restarts.
PEERNAME=$(cat /sys/class/net/$BRIDGE/address)
# backward compatibility...
if is_cidr "$1" ; then
echo "WARNING: $1 parameter ignored; 'weave launch' no longer takes a CIDR as the first parameter" >&2
shift 1
fi
CONTAINER_PORT=$PORT
ARGS=""
IPRANGE=
IPRANGE_SPECIFIED=
while [ $# -gt 0 ] ; do
case "$1" in
-password)
[ $# -gt 1 ] || usage
WEAVE_PASSWORD="$2"
export WEAVE_PASSWORD
shift 2
;;
-port)
[ $# -gt 1 ] || usage
CONTAINER_PORT="$2"
shift 2
;;
-iprange)
[ $# -gt 1 ] || usage
IPRANGE="$2"
IPRANGE_SPECIFIED=1
shift 2
;;
*)
ARGS="$ARGS '$(echo "$1" | sed "s|'|'\"'\"'|g")'"
shift
;;
esac
done
eval "set -- $ARGS"
if [ -z "$IPRANGE_SPECIFIED" ] ; then
IPRANGE="10.128.0.0/10"
if command_exists netcheck && ! netcheck $IPRANGE ; then
echo "Default -iprange $IPRANGE overlaps with existing route on host." >&2
echo "Please pick another range and set it on all hosts." >&2
exit 1
fi
fi
# Set WEAVE_DOCKER_ARGS in the environment in order to supply
# additional parameters, such as resource limits, to docker
# when launching the weave container.
CONTAINER=$(docker run --privileged -d --name=$CONTAINER_NAME \
-p $PORT:$CONTAINER_PORT/tcp -p $PORT:$CONTAINER_PORT/udp \
-v /var/run/docker.sock:/var/run/docker.sock \
-e WEAVE_PASSWORD \
-e WEAVE_CIDR=none \
$WEAVE_DOCKER_ARGS $IMAGE -iface $CONTAINER_IFNAME -port $CONTAINER_PORT -name "$PEERNAME" -nickname "$(hostname)" -iprange "$IPRANGE" "$@")
with_container_netns_or_die $CONTAINER launch
wait_for_status $CONTAINER_NAME $HTTP_PORT
if [ -n "$IPRANGE" ] ; then
# Tell the newly-started weave IP allocator about existing weave IPs
with_container_addresses ipam_claim weave:expose $(docker ps -q --no-trunc)
fi
}
launch_dns() {
collect_cidr_args "$@"
[ $CIDR_ARG_COUNT -lt 2 ] || usage
shift $CIDR_ARG_COUNT
create_bridge
docker_bridge_ip
# Set WEAVEDNS_DOCKER_ARGS in the environment in order to supply
# additional parameters, such as resource limits, to docker
# when launching the weave container.
DNS_CONTAINER=$(docker run --privileged -d --name=$DNS_CONTAINER_NAME \
-p $DOCKER_BRIDGE_IP:53:53/udp \
-e WEAVE_CIDR=none \
-v /var/run/docker.sock:/var/run/docker.sock \
$WEAVEDNS_DOCKER_ARGS $DNS_IMAGE -iface $CONTAINER_IFNAME -httpiface "$DNS_HTTP_IFNAME" "$@")
ipam_cidrs_or_die $DNS_CONTAINER $CIDR_ARGS
with_container_netns_or_die $DNS_CONTAINER attach $ALL_CIDRS
populate_dns
}
launch_proxy() {
# Set WEAVEPROXY_DOCKER_ARGS in the environment in order to supply
# additional parameters, such as resource limits, to docker
# when launching the weaveproxy container.
proxy_args "$@"
PROXY_CONTAINER=$(docker run --privileged -d --name=$PROXY_CONTAINER_NAME --net=host \
$PROXY_VOLUMES \
-v /var/run:/var/run \
-v /proc:/hostproc \
-e PROCFS=/hostproc \
-e WEAVE_CIDR=none \
--entrypoint=/home/weave/weaveproxy \
$WEAVEPROXY_DOCKER_ARGS $EXEC_IMAGE $PROXY_ARGS)
wait_for_log $PROXY_CONTAINER_NAME "proxy listening"
}
##########################################################################################
[ $(id -u) = 0 ] || {
@@ -978,110 +1110,26 @@ case "$COMMAND" in
create_bridge --without-ethtool
;;
launch)
check_not_running $CONTAINER_NAME $BASE_IMAGE
check_not_running $DNS_CONTAINER_NAME $BASE_DNS_IMAGE
check_not_running $PROXY_CONTAINER_NAME $EXEC_IMAGE
launch_router "$@"
launch_dns
launch_proxy
;;
launch-router)
check_not_running $CONTAINER_NAME $BASE_IMAGE
create_bridge
# We set the router name to the bridge mac since that is
# stable across re-creations of the containers.
#
# TODO use the mac of one of the physical host interfaces
# (eth0, wlan0, etc) so the name becomes stable across host
# restarts.
PEERNAME=$(cat /sys/class/net/$BRIDGE/address)
# backward compatibility...
if is_cidr "$1" ; then
echo "WARNING: $1 parameter ignored; 'weave launch' no longer takes a CIDR as the first parameter" >&2
shift 1
fi
CONTAINER_PORT=$PORT
ARGS=""
IPRANGE=
IPRANGE_SPECIFIED=
while [ $# -gt 0 ] ; do
case "$1" in
-password)
[ $# -gt 1 ] || usage
WEAVE_PASSWORD="$2"
export WEAVE_PASSWORD
shift 2
;;
-port)
[ $# -gt 1 ] || usage
CONTAINER_PORT="$2"
shift 2
;;
-iprange)
[ $# -gt 1 ] || usage
IPRANGE="$2"
IPRANGE_SPECIFIED=1
shift 2
;;
*)
ARGS="$ARGS '$(echo "$1" | sed "s|'|'\"'\"'|g")'"
shift
;;
esac
done
eval "set -- $ARGS"
if [ -z "$IPRANGE_SPECIFIED" ] ; then
IPRANGE="10.128.0.0/10"
if command_exists netcheck && ! netcheck $IPRANGE ; then
echo "Default -iprange $IPRANGE overlaps with existing route." >&2
echo "Please pick another range and set it on all hosts." >&2
exit 1
fi
fi
# Set WEAVE_DOCKER_ARGS in the environment in order to supply
# additional parameters, such as resource limits, to docker
# when launching the weave container.
CONTAINER=$(docker run --privileged -d --name=$CONTAINER_NAME \
-p $PORT:$CONTAINER_PORT/tcp -p $PORT:$CONTAINER_PORT/udp \
-v /var/run/docker.sock:/var/run/docker.sock \
-e WEAVE_PASSWORD \
-e WEAVE_CIDR=none \
$WEAVE_DOCKER_ARGS $IMAGE -iface $CONTAINER_IFNAME -port $CONTAINER_PORT -name "$PEERNAME" -nickname "$(hostname)" -iprange "$IPRANGE" "$@")
with_container_netns_or_die $CONTAINER launch
wait_for_status $CONTAINER_NAME $HTTP_PORT
if [ -n "$IPRANGE" ] ; then
# Tell the newly-started weave IP allocator about existing weave IPs
with_container_addresses ipam_claim weave:expose $(docker ps -q --no-trunc)
fi
launch_router "$@"
echo $CONTAINER
;;
launch-dns)
collect_cidr_args "$@"
[ $CIDR_ARG_COUNT -lt 2 ] || usage
shift $CIDR_ARG_COUNT
check_not_running $DNS_CONTAINER_NAME $BASE_DNS_IMAGE
create_bridge
docker_bridge_ip
# Set WEAVEDNS_DOCKER_ARGS in the environment in order to supply
# additional parameters, such as resource limits, to docker
# when launching the weave container.
DNS_CONTAINER=$(docker run --privileged -d --name=$DNS_CONTAINER_NAME \
-p $DOCKER_BRIDGE_IP:53:53/udp \
-e WEAVE_CIDR=none \
-v /var/run/docker.sock:/var/run/docker.sock \
$WEAVEDNS_DOCKER_ARGS $DNS_IMAGE -iface $CONTAINER_IFNAME -httpiface "$DNS_HTTP_IFNAME" "$@")
ipam_cidrs_or_die $DNS_CONTAINER $CIDR_ARGS
with_container_netns_or_die $DNS_CONTAINER attach $ALL_CIDRS
populate_dns
launch_dns "$@"
echo $DNS_CONTAINER
;;
launch-proxy)
check_not_running $PROXY_CONTAINER_NAME $EXEC_IMAGE
# Set WEAVEPROXY_DOCKER_ARGS in the environment in order to supply
# additional parameters, such as resource limits, to docker
# when launching the weaveproxy container.
proxy_args "$@"
PROXY_CONTAINER=$(docker run --privileged -d --name=$PROXY_CONTAINER_NAME --net=host \
$PROXY_VOLUMES \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /proc:/hostproc \
-e PROCFS=/hostproc \
-e WEAVE_CIDR=none \
--entrypoint=/home/weave/weaveproxy \
$WEAVEPROXY_DOCKER_ARGS $EXEC_IMAGE $PROXY_ARGS)
wait_for_log $PROXY_CONTAINER_NAME "proxy listening"
launch_proxy "$@"
echo $PROXY_CONTAINER
;;
proxy-env)
@@ -1260,6 +1308,13 @@ case "$COMMAND" in
show_addrs $ALL_CIDRS
;;
stop)
[ $# -eq 0 ] || usage
stop $PROXY_CONTAINER_NAME "Proxy"
stop $DNS_CONTAINER_NAME "WeaveDNS"
stop $CONTAINER_NAME "Weave"
conntrack -D -p udp --dport $PORT >/dev/null 2>&1 || true
;;
stop-router)
[ $# -eq 0 ] || usage
stop $CONTAINER_NAME "Weave"
conntrack -D -p udp --dport $PORT >/dev/null 2>&1 || true