From 2720d2418381ded0f1ad2a6778e9640737b4173f Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 18 Sep 2014 06:48:56 +0100 Subject: [PATCH 01/11] Attempt re-connection immediately when notified of a broken connection --- connection_maker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connection_maker.go b/connection_maker.go index 8c259bd5..957603ee 100644 --- a/connection_maker.go +++ b/connection_maker.go @@ -76,8 +76,8 @@ func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteractio if target, found := cm.targets[query.address]; found { target.attempting = false target.tryAfter, target.tryInterval = tryAfter(target.tryInterval) - maybeTick() } + cm.checkStateAndAttemptConnections(time.Now()) default: log.Fatal("Unexpected connection maker query:", query) } From f0191639de67a44da36b0fb7785f167c29aea070 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 18 Sep 2014 06:51:12 +0100 Subject: [PATCH 02/11] Start the tick again, in case we need it --- connection_maker.go | 1 + 1 file changed, 1 insertion(+) diff --git a/connection_maker.go b/connection_maker.go index 957603ee..4bb4c747 100644 --- a/connection_maker.go +++ b/connection_maker.go @@ -78,6 +78,7 @@ func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteractio target.tryAfter, target.tryInterval = tryAfter(target.tryInterval) } cm.checkStateAndAttemptConnections(time.Now()) + maybeTick() default: log.Fatal("Unexpected connection maker query:", query) } From 6ed2ab10a19995a421c402ff908b95ace016e0aa Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 22 Sep 2014 17:44:13 +0100 Subject: [PATCH 03/11] Only push two tags - latest and git-commit - to DockerHub --- weaver/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/weaver/Makefile b/weaver/Makefile index 96a251a4..a776a2ee 100644 --- a/weaver/Makefile +++ b/weaver/Makefile @@ -15,7 +15,8 @@ docker-image: /tmp/weave.tar publish: docker-image sudo docker tag zettio/weave zettio/weave:git-`git rev-parse --short=12 HEAD` - sudo docker push zettio/weave + sudo docker push zettio/weave:latest + sudo docker push zettio/weave:git-`git rev-parse --short=12 HEAD` clean: -sudo docker rmi zettio/weave From 1b524c4f27edf47fd7bdc773451906c63183b71a Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 22 Sep 2014 17:56:42 +0100 Subject: [PATCH 04/11] Updated to reflect version-tagging process --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 475ec6b2..09a2223a 100644 --- a/README.md +++ b/README.md @@ -395,13 +395,8 @@ quickly enough. ## Troubleshooting -Check what version of weave you are running: - - weave version - -and compare against -https://registry.hub.docker.com/u/zettio/weave/tags/manage/. If you -do not have the latest released version you can download it with: +Make sure you are running the latest version - you can download it +with docker pull zettio/weave @@ -515,6 +510,11 @@ Found a bug, want to suggest a feature, or have a question? [File an issue](https://github.com/zettio/weave/issues), or email weave@zett.io. +When reporting a bug, please include what version of weave you are +running, as shown by + + weave version + Follow weave on Twitter: [@weavenetwork](https://twitter.com/weavenetwork). From 9e8260dc63cbd17cafc8c86669818d4c7fb281e8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 22 Sep 2014 18:14:37 +0100 Subject: [PATCH 05/11] cosmetic --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 09a2223a..9188ef54 100644 --- a/README.md +++ b/README.md @@ -508,12 +508,8 @@ More details on the inner workings of weave can be found in the Found a bug, want to suggest a feature, or have a question? [File an issue](https://github.com/zettio/weave/issues), or email -weave@zett.io. - -When reporting a bug, please include what version of weave you are -running, as shown by - - weave version +weave@zett.io. When reporting a bug, please include what version of +weave you are running, as shown by `weave version`. Follow weave on Twitter: [@weavenetwork](https://twitter.com/weavenetwork). From aa1e1b1d6c22b15702eb0c6afe14952de8e2ce4b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 22 Sep 2014 18:34:50 +0100 Subject: [PATCH 06/11] cosmetic - remove comment for which we have an issue filed --- connection_maker.go | 1 - 1 file changed, 1 deletion(-) diff --git a/connection_maker.go b/connection_maker.go index 4bb4c747..e5b55975 100644 --- a/connection_maker.go +++ b/connection_maker.go @@ -54,7 +54,6 @@ func (cm *ConnectionMaker) String() string { func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteraction) { var tick <-chan time.Time maybeTick := func() { - // would be nice to optimise this to stop ticking when there is nothing worth trying if tick == nil { tick = time.After(5 * time.Second) } From f11f84fe1709ada65dc5a2c15a53b4f879a33458 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 22 Sep 2014 19:38:21 +0100 Subject: [PATCH 07/11] refactor: we don't care about the tick time --- connection_maker.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/connection_maker.go b/connection_maker.go index e5b55975..a9a53f22 100644 --- a/connection_maker.go +++ b/connection_maker.go @@ -67,7 +67,7 @@ func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteractio switch { case query.code == CMInitiate: cm.cmdLineAddress[NormalisePeerAddr(query.address)] = true - cm.checkStateAndAttemptConnections(time.Now()) + cm.checkStateAndAttemptConnections() maybeTick() case query.code == CMStatus: query.resultChan <- cm.status() @@ -76,20 +76,20 @@ func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteractio target.attempting = false target.tryAfter, target.tryInterval = tryAfter(target.tryInterval) } - cm.checkStateAndAttemptConnections(time.Now()) + cm.checkStateAndAttemptConnections() maybeTick() default: log.Fatal("Unexpected connection maker query:", query) } - case now := <-tick: - cm.checkStateAndAttemptConnections(now) + case <-tick: + cm.checkStateAndAttemptConnections() tick = nil maybeTick() } } } -func (cm *ConnectionMaker) checkStateAndAttemptConnections(now time.Time) { +func (cm *ConnectionMaker) checkStateAndAttemptConnections() { ourself := cm.router.Ourself validTarget := make(map[string]bool) @@ -129,7 +129,7 @@ func (cm *ConnectionMaker) checkStateAndAttemptConnections(now time.Time) { }) }) - now = time.Now() // make sure we catch items just added + now := time.Now() // make sure we catch items just added for address, target := range cm.targets { if our_connected_targets[address] { delete(cm.targets, address) From d5e797f695639014c2df1dc1cc651d77f9306733 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 22 Sep 2014 22:34:46 +0100 Subject: [PATCH 08/11] refactor: use better 'switch' --- connection.go | 10 +++++----- connection_maker.go | 12 ++++++------ peer.go | 10 +++++----- topology.go | 6 +++--- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/connection.go b/connection.go index 014183b3..61ced2b0 100644 --- a/connection.go +++ b/connection.go @@ -182,14 +182,14 @@ func (conn *LocalConnection) queryLoop(queryChan <-chan *ConnectionInteraction, if !ok { break } - switch { - case query.code == CShutdown: + switch query.code { + case CShutdown: terminate = true - case query.code == CSetEstablished: + case CSetEstablished: err = conn.handleSetEstablished() - case query.code == CSetRemoteUDPAddr: + case CSetRemoteUDPAddr: err = conn.handleSetRemoteUDPAddr(query.payload.(*net.UDPAddr)) - case query.code == CSendTCP: + case CSendTCP: err = conn.handleSendTCP(query.payload.([]byte)) } } diff --git a/connection_maker.go b/connection_maker.go index a9a53f22..e52c413e 100644 --- a/connection_maker.go +++ b/connection_maker.go @@ -16,8 +16,8 @@ const ( const ( CMInitiate = iota - CMStatus = iota CMTerminated = iota + CMStatus = iota ) func StartConnectionMaker(router *Router) *ConnectionMaker { @@ -64,20 +64,20 @@ func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteractio if !ok { return } - switch { - case query.code == CMInitiate: + switch query.code { + case CMInitiate: cm.cmdLineAddress[NormalisePeerAddr(query.address)] = true cm.checkStateAndAttemptConnections() maybeTick() - case query.code == CMStatus: - query.resultChan <- cm.status() - case query.code == CMTerminated: + case CMTerminated: if target, found := cm.targets[query.address]; found { target.attempting = false target.tryAfter, target.tryInterval = tryAfter(target.tryInterval) } cm.checkStateAndAttemptConnections() maybeTick() + case CMStatus: + query.resultChan <- cm.status() default: log.Fatal("Unexpected connection maker query:", query) } diff --git a/peer.go b/peer.go index b62e8b27..80dc3d2a 100644 --- a/peer.go +++ b/peer.go @@ -259,15 +259,15 @@ func (peer *Peer) queryLoop(queryChan <-chan *PeerInteraction) { if !ok { return } - switch { - case query.code == PAddConnection: + switch query.code { + case PAddConnection: peer.handleAddConnection(query.payload.(*LocalConnection)) - case query.code == PDeleteConnection: + case PDeleteConnection: peer.handleDeleteConnection(query.payload.(*LocalConnection)) query.resultChan <- nil - case query.code == PConnectionEstablished: + case PConnectionEstablished: peer.handleConnectionEstablished(query.payload.(*LocalConnection)) - case query.code == PBroadcastTCP: + case PBroadcastTCP: peer.handleBroadcastTCP(query.payload.([]byte)) } } diff --git a/topology.go b/topology.go index c0db270c..fe3a9323 100644 --- a/topology.go +++ b/topology.go @@ -81,15 +81,15 @@ func (topo *Topology) queryLoop(queryChan <-chan *Interaction) { if !ok { return } - switch { - case query.code == TRebuildRoutes: + switch query.code { + case TRebuildRoutes: unicast := topo.buildUnicastRoutes() broadcast := topo.buildBroadcastRoutes() topo.Lock() topo.unicast = unicast topo.broadcast = broadcast topo.Unlock() - case query.code == TFetchAll: + case TFetchAll: query.resultChan <- topo.router.Peers.EncodeAllPeers() default: log.Fatal("Unexpected topology query:", query) From eecaa567fc7eda327ea9ff49bbef554c3cc97e9f Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Mon, 22 Sep 2014 23:15:54 +0100 Subject: [PATCH 09/11] eliminate periodic 'tick' in connection_maker checkStateAndAttemptConnections already performs a full scan of all potential targets, from which we can easily determine the nearest future time (or, rather, its duration from now) on which an attempt is due. We also need to kick the ConnectionMaker into action when we receive a topology update containing new connections. --- connection_maker.go | 38 ++++++++++++++++++++++---------------- peer_cache.go | 4 ++++ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/connection_maker.go b/connection_maker.go index e52c413e..94064646 100644 --- a/connection_maker.go +++ b/connection_maker.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "log" + "math" "math/rand" "net" "time" @@ -17,6 +18,7 @@ const ( const ( CMInitiate = iota CMTerminated = iota + CMRefresh = iota CMStatus = iota ) @@ -43,6 +45,11 @@ func (cm *ConnectionMaker) ConnectionTerminated(address string) { address: address} } +func (cm *ConnectionMaker) Refresh() { + cm.queryChan <- &ConnectionMakerInteraction{ + Interaction: Interaction{code: CMRefresh}} +} + func (cm *ConnectionMaker) String() string { resultChan := make(chan interface{}, 0) cm.queryChan <- &ConnectionMakerInteraction{ @@ -52,12 +59,8 @@ func (cm *ConnectionMaker) String() string { } func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteraction) { - var tick <-chan time.Time - maybeTick := func() { - if tick == nil { - tick = time.After(5 * time.Second) - } - } + timer := time.NewTimer(math.MaxInt64) + run := func() { timer.Reset(cm.checkStateAndAttemptConnections()) } for { select { case query, ok := <-queryChan: @@ -67,29 +70,27 @@ func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteractio switch query.code { case CMInitiate: cm.cmdLineAddress[NormalisePeerAddr(query.address)] = true - cm.checkStateAndAttemptConnections() - maybeTick() + run() case CMTerminated: if target, found := cm.targets[query.address]; found { target.attempting = false target.tryAfter, target.tryInterval = tryAfter(target.tryInterval) } - cm.checkStateAndAttemptConnections() - maybeTick() + run() + case CMRefresh: + run() case CMStatus: query.resultChan <- cm.status() default: log.Fatal("Unexpected connection maker query:", query) } - case <-tick: - cm.checkStateAndAttemptConnections() - tick = nil - maybeTick() + case <-timer.C: + run() } } } -func (cm *ConnectionMaker) checkStateAndAttemptConnections() { +func (cm *ConnectionMaker) checkStateAndAttemptConnections() time.Duration { ourself := cm.router.Ourself validTarget := make(map[string]bool) @@ -130,6 +131,7 @@ func (cm *ConnectionMaker) checkStateAndAttemptConnections() { }) now := time.Now() // make sure we catch items just added + after := time.Duration(math.MaxInt64) for address, target := range cm.targets { if our_connected_targets[address] { delete(cm.targets, address) @@ -142,11 +144,15 @@ func (cm *ConnectionMaker) checkStateAndAttemptConnections() { delete(cm.targets, address) continue } - if now.After(target.tryAfter) { + switch duration := target.tryAfter.Sub(now); { + case duration <= 0: target.attempting = true go cm.attemptConnection(address, cm.cmdLineAddress[address]) + case duration < after: + after = duration } } + return after } func (cm *ConnectionMaker) addTarget(address string) { diff --git a/peer_cache.go b/peer_cache.go index a499d5ae..a5d9f956 100644 --- a/peer_cache.go +++ b/peer_cache.go @@ -77,6 +77,10 @@ func (cache *PeerCache) ApplyUpdate(update []byte, router *Router) ([]byte, erro // Don't need to hold cache lock any longer cache.Unlock() + if len(newUpdate) > 0 { + router.ConnectionMaker.Refresh() + } + return encodePeersMap(newUpdate), nil } From b5df59660fe83fd5a5d4d3ddd23e6b084ef56465 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 23 Sep 2014 11:06:41 +0100 Subject: [PATCH 10/11] don't set pcap timeout, to prevent cpu churn pcap timeouts are a mystery. Setting the timeout to -1 used to reduce latency, but that was before we started setting immediate mode. And it turns out that with that timeout, pcap chews through a lot of CPU polling its file descriptor, checking timers, etc. With no timeout set an idle weave is completely idle - no system calls at all. Fixes #80 --- pcap.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pcap.go b/pcap.go index 354ec818..912d0fba 100644 --- a/pcap.go +++ b/pcap.go @@ -40,9 +40,6 @@ func newPcapIO(ifName string, promisc bool, snaplen int, bufSz int) (handle *Pca if err = inactive.SetSnapLen(snaplen); err != nil { return } - if err = inactive.SetTimeout(-1); err != nil { - return - } if err = inactive.SetImmediateMode(true); err != nil { return } From ff3fbaede915de404527f74080d7313bac6cabc8 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Tue, 23 Sep 2014 15:32:53 +0100 Subject: [PATCH 11/11] tweak & move docs for 'attach' and 'detach' --- README.md | 53 +++++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 49fe82dd..22092da7 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,35 @@ each other but not the containers of our first application... This isolation-through-subnets scheme is an example of carrying over a well-known technique from the 'on metal' days to containers. +### Dynamic network attachment + +In some scenarios containers are started independently, e.g. via some +existing tool chain, or require more complex startup sequences than +provided by `weave run`. And sometimes the decision which application +network a container should be part of is made post startup. For these +situation, weave allows an existing, running container to be attached +to the weave network. To illustrate, we can achieve the same effect as +the first example with + + host1# C=$(docker run -d -t -i ubuntu) + host1# weave attach 10.0.1.1/24 $C + +There is a matching `weave detach` command: + + host1# weave detach 10.0.1.1/24 $C + +You can detach a container from one application network and attach it +to another: + + host1# weave detach 10.0.1.1/24 $C + host1# weave attach 10.0.2.1/24 $C + +or attach a container to multiple application networks, effectively +sharing it between applications: + + host1# weave attach 10.0.1.1/24 $C + host1# weave attach 10.0.2.1/24 $C + ### Security In order to connect containers across untrusted networks, weave peers @@ -221,30 +250,6 @@ will work. And, more interestingly, will work too, which is talking to a container that resides on $HOST1. -### Attach to already-running containers - -If you don't want to start containers via the `weave` script, e.g. because -you are controlling them as part of some larger orchestration scheme, you can -attach the weave network to an already-running container. To illustrate, we -can achieve the same effect as the first example like this: - - host1# C=$(docker run -d -t -i ubuntu) - host1# weave attach 10.0.1.1/24 $C - -There is a matching `weave detach` command: - - host1# weave detach 10.0.1.1/24 $C - -You can detach a container from one subnet and re-attach it to another: - - host1# weave detach 10.0.1.1/24 $C - host1# weave attach 10.0.2.1/24 $C - -or attach a container to multiple subnets: - - host1# weave attach 10.0.1.1/24 $C - host1# weave attach 10.0.2.1/24 $C - ### Service export Services running in containers on a weave network can be made