Compare commits

...
21 Commits
Author SHA1 Message Date
Adam Harrison 6c0075ab82 Add release v1.4.6 2016-03-23 17:37:21 +00:00
Matthias Radestock ae0a4fef50 Merge pull request #2092 from weaveworks/issues/2083-error-not-panic
Change invariant fail on Merge() from panic to error

Fixes #2083.
2016-03-23 16:38:52 +00:00
Matthias Radestock 8360024e06 Merge pull request #2088 from weaveworks/issues/2087-leave-direct-targets
Don't delete targets that seem invalid if they are 'direct' targets

Fixes #2087.
2016-03-23 16:22:08 +00:00
Bryan Boreham 2e4d56efd6 Suspend connecting to targets that seem invalid, if they are 'direct' targets.
Introduce TargetSuspended state to reflect this.
2016-03-23 15:36:55 +00:00
Bryan Boreham 6bc199a8bc Change invariant fail on Merge() from panic to error 2016-03-23 15:26:26 +00:00
Matthias Radestock 537868008f make linter happy 2016-03-22 16:28:25 +00:00
Matthias Radestock 1664fce9d1 Merge pull request #2081 from weaveworks/issues/2023-override-stale-dns
Override stale dns entries received via gossip

Fixes #1867 and #2023.
2016-03-22 13:47:17 +00:00
Bryan Boreham c2cf825cdf Smoke test for stale DNS entries 2016-03-22 12:57:07 +00:00
Bryan Boreham 38a79b7f59 Pick entry with newer tombstone to merge 2016-03-22 12:57:07 +00:00
Bryan Boreham 010766fe9e Check entries claiming to originate from us against our current data, and override if necessary 2016-03-22 12:57:07 +00:00
Matthias Radestock 6b33f4215b Merge branch 'latest_release_doc_updates' into 1.4 2016-03-21 15:53:18 +00:00
Matthias Radestock cc43782d87 Merge pull request #2080 from weaveworks/issues/2061-correct-weave-status
Correct IPAM state docs

Fixes #2061.
2016-03-21 15:49:03 +00:00
Adam Harrison c2eb900dd3 Correct IPAM state docs 2016-03-21 15:33:37 +00:00
Bryan Boreham 001322751f Don't hold Nameserver lock over call out via isKnownPeer 2016-03-21 11:52:13 +00:00
Bryan Boreham b1ec2b788a Bump template name so it will fetch latest Docker 2016-03-19 14:12:17 +00:00
Matthias Radestock f42e275884 broadcast ipam ring update after rmpeer
So that other peers notice the change more promptly.

Fixes #2068.
2016-03-19 13:57:20 +00:00
Matthias Radestock 5dfd104728 reduce docker event subscription retry max interval 2016-03-18 19:29:21 +00:00
Bryan Boreham 9c096526b8 Merge pull request #2049 from weaveworks/issues/2016-retry-docker-event-subscription
Retry docker event subscription
2016-03-16 14:38:27 +00:00
Adam Harrison 922f465ecb Reset retry interval if we spent long enough processing events 2016-03-16 13:41:18 +00:00
Adam Harrison 08e9db900a Retry docker event subscription 2016-03-16 09:23:32 +00:00
Adam Harrison 30b9d33276 Adjust tests to accomodate docker/docker#14012 2016-03-02 11:59:10 +00:00
15 changed files with 190 additions and 47 deletions

No files matched your search

+16
View File
@@ -1,3 +1,19 @@
## Release 1.4.6
Fixes some issues encountered by our users.
* Restarting a peer could leave stale entries in WeaveDNS
#1867/#2023/#2081
* Weave proxy occasionally failing to attach any new containers
#2016/#2049
Other fixes:
* Resolved a crash when a restarting peer re-connected to a peer that
had not received the latest IPAM data #2083/#2092
* Make IP address space available immediately after a dead peer is
removed #2068
## Release 1.4.5
Higher performance for multicast and broadcast traffic when using
+32 -14
View File
@@ -4,12 +4,18 @@ import (
"errors"
"fmt"
"strings"
"time"
docker "github.com/fsouza/go-dockerclient"
. "github.com/weaveworks/weave/common"
)
const (
InitialInterval = 1 * time.Second
MaxInterval = 20 * time.Second
)
// An observer for container events
type ContainerObserver interface {
ContainerStarted(ident string)
@@ -72,21 +78,33 @@ func (c *Client) Info() string {
// AddObserver adds an observer for docker events
func (c *Client) AddObserver(ob ContainerObserver) error {
events := make(chan *docker.APIEvents)
if err := c.AddEventListener(events); err != nil {
Log.Errorf("[docker] Unable to add listener to Docker API: %s", err)
return err
}
go func() {
for event := range events {
switch event.Status {
case "start":
id := event.ID
ob.ContainerStarted(id)
case "die":
id := event.ID
ob.ContainerDied(id)
retryInterval := InitialInterval
for {
events := make(chan *docker.APIEvents)
if err := c.AddEventListener(events); err != nil {
Log.Errorf("[docker] Unable to add listener to Docker API: %s - retrying in %ds", err, retryInterval/time.Second)
} else {
start := time.Now()
for event := range events {
switch event.Status {
case "start":
id := event.ID
ob.ContainerStarted(id)
case "die":
id := event.ID
ob.ContainerDied(id)
}
}
if time.Since(start) > retryInterval {
retryInterval = InitialInterval
}
Log.Errorf("[docker] Event listener channel closed - retrying subscription in %ds", retryInterval/time.Second)
}
time.Sleep(retryInterval)
retryInterval = retryInterval * 3 / 2
if retryInterval > MaxInterval {
retryInterval = MaxInterval
}
}
}()
+4 -1
View File
@@ -334,7 +334,10 @@ func (alloc *Allocator) AdminTakeoverRanges(peerNameOrNickname string) error {
}
newRanges, err := alloc.ring.Transfer(peername, alloc.ourName)
alloc.space.AddRanges(newRanges)
if err == nil && len(newRanges) > 0 {
alloc.space.AddRanges(newRanges)
alloc.gossip.GossipBroadcast(alloc.Gossip())
}
resultChan <- err
}
return <-resultChan
+16 -9
View File
@@ -46,33 +46,37 @@ var (
)
func (r *Ring) checkInvariants() error {
if !sort.IsSorted(r.Entries) {
return r.checkEntries(r.Entries)
}
func (r *Ring) checkEntries(entries entries) error {
if !sort.IsSorted(entries) {
return ErrNotSorted
}
// Check no token appears twice
// We know it's sorted...
for i := 1; i < len(r.Entries); i++ {
if r.Entries[i-1].Token == r.Entries[i].Token {
for i := 1; i < len(entries); i++ {
if entries[i-1].Token == entries[i].Token {
return ErrTokenRepeated
}
}
if len(r.Entries) == 0 {
if len(entries) == 0 {
return nil
}
// Check tokens are in range
if r.Entries.entry(0).Token < r.Start {
if entries.entry(0).Token < r.Start {
return ErrTokenOutOfRange
}
if r.Entries.entry(-1).Token >= r.End {
if entries.entry(-1).Token >= r.End {
return ErrTokenOutOfRange
}
// Check all the freespaces are in range
for i, entry := range r.Entries {
next := r.Entries.entry(i + 1)
for i, entry := range entries {
next := entries.entry(i + 1)
distance := r.distance(entry.Token, next.Token)
if entry.Free > distance {
@@ -181,7 +185,6 @@ func (r *Ring) GrantRangeToHost(start, end address.Address, peer mesh.PeerName)
// Merge the given ring into this ring and return any new ranges added
func (r *Ring) Merge(gossip Ring) error {
r.assertInvariants()
defer r.assertInvariants()
defer r.updateExportedVariables()
// Don't panic when checking the gossiped in ring.
@@ -267,6 +270,10 @@ func (r *Ring) Merge(gossip Ring) error {
previousOwner = nil
}
if err := r.checkEntries(result); err != nil {
return fmt.Errorf("Merge of incoming data causes: %s", err)
}
if len(r.Seeds) == 0 {
r.Seeds = gossip.Seeds
}
+10 -2
View File
@@ -32,6 +32,7 @@ const (
TargetWaiting TargetState = iota // we are waiting to connect there
TargetAttempting // we are attempting to connect there
TargetConnected // we are connected to there
TargetSuspended // not currently attempting to connect here
)
// Information about an address where we may find a peer
@@ -266,16 +267,23 @@ func (cm *ConnectionMaker) connectToTargets(validTarget map[string]struct{}, dir
now := time.Now() // make sure we catch items just added
after := MaxDuration
for address, target := range cm.targets {
if target.state != TargetWaiting {
if target.state != TargetWaiting && target.state != TargetSuspended {
continue
}
if _, valid := validTarget[address]; !valid {
delete(cm.targets, address)
// Not valid: suspend reconnects if direct peer,
// otherwise forget this target entirely
if _, direct := directTarget[address]; direct {
target.state = TargetSuspended
} else {
delete(cm.targets, address)
}
continue
}
if target.tryAfter.IsZero() {
continue
}
target.state = TargetWaiting
switch duration := target.tryAfter.Sub(now); {
case duration <= 0:
target.state = TargetAttempting
+1
View File
@@ -181,6 +181,7 @@ func NewLocalConnectionStatusSlice(cm *ConnectionMaker) []LocalConnectionStatus
add("retrying", target.lastError.Error())
}
case TargetConnected:
case TargetSuspended:
}
}
resultChan <- slice
+20 -2
View File
@@ -93,6 +93,9 @@ func (e1 *Entry) merge(e2 *Entry) bool {
e1.Version = e2.Version
e1.Tombstone = e2.Tombstone
return true
} else if e2.Version == e1.Version && e2.Tombstone > e1.Tombstone {
e1.Tombstone = e2.Tombstone
return true
}
return false
}
@@ -105,6 +108,11 @@ func (e1 *Entry) addLowercase() {
e1.lHostname = strings.ToLower(e1.Hostname)
}
func (e1 *Entry) tombstone() {
e1.Version++
e1.Tombstone = now()
}
func check(es SortableEntries) error {
if !sort.IsSorted(es) {
return fmt.Errorf("Not sorted!")
@@ -182,8 +190,7 @@ func (es *Entries) tombstone(ourname mesh.PeerName, f func(*Entry) bool) Entries
tombstoned := Entries{}
for i, e := range *es {
if f(&e) && e.Origin == ourname {
e.Version++
e.Tombstone = now()
e.tombstone()
(*es)[i] = e
tombstoned = append(tombstoned, e)
}
@@ -191,6 +198,7 @@ func (es *Entries) tombstone(ourname mesh.PeerName, f func(*Entry) bool) Entries
return tombstoned
}
// note f() may only modify entries such that they remain in order defined by less()
func (es *Entries) filter(f func(*Entry) bool) {
defer es.checkAndPanic().checkAndPanic()
@@ -205,6 +213,16 @@ func (es *Entries) filter(f func(*Entry) bool) {
*es = (*es)[:i]
}
func (es Entries) findEqual(e *Entry) (*Entry, bool) {
i := sort.Search(len(es), func(i int) bool {
return !es[i].insensitiveLess(e)
})
if i < len(es) && es[i].equal(*e) {
return &es[i], true
}
return nil, false
}
func (es Entries) lookup(hostname string) Entries {
es.checkAndPanic()
+15
View File
@@ -86,6 +86,21 @@ func TestTombstone(t *testing.T) {
Entry{Hostname: "B", Version: 1, Tombstone: 1234},
})
require.Equal(t, expected, es)
// Now try a merge including two entries which differ only in tombstone
e2 := make(Entries, len(es))
copy(e2, es)
e2[1].Tombstone = 5555
diff := es.merge(e2)
expected2 := l(Entries{
Entry{Hostname: "A"},
Entry{Hostname: "B", Version: 1, Tombstone: 5555},
})
require.Equal(t, expected2, es)
expectedDiff := l(Entries{Entry{Hostname: "B", Version: 1, Tombstone: 5555}})
require.Equal(t, expectedDiff, diff)
}
func TestDelete(t *testing.T) {
+21 -1
View File
@@ -203,11 +203,31 @@ func (n *Nameserver) receiveGossip(msg []byte) (mesh.GossipData, mesh.GossipData
return nil, nil, fmt.Errorf("clock skew of %d detected", delta)
}
// Filter to remove entries from unknown peers, done before we take
// the nameserver lock, so we don't have to worry what isKnownPeer locks.
gossip.Entries.filter(func(e *Entry) bool {
return n.isKnownPeer(e.Origin)
})
n.Lock()
defer n.Unlock()
// Check entries claiming to originate from us against our current data
gossip.Entries.filter(func(e *Entry) bool {
return n.isKnownPeer(e.Origin)
if e.Origin == n.ourName {
if ourEntry, ok := n.entries.findEqual(e); ok {
if ourEntry.Version < e.Version ||
(ourEntry.Version == e.Version && ourEntry.Tombstone != e.Tombstone) {
// Take our version of the data, but make the version higher than the incoming
nextVersion := e.Version + 1
*e = *ourEntry
e.Version = nextVersion
}
} else { // We have no entry matching the one that came in with us as Origin
e.tombstone()
}
}
return true
})
newEntries := n.entries.merge(gossip.Entries)
+19 -13
View File
@@ -48,17 +48,6 @@ time, you can give the number of peers like this:
host3$ weave launch --init-peer-count 3 $HOST2
The consensus mechanism used to determine a majority transitions
through three states: 'deferred', 'waiting' and 'achieved':
* 'deferred' - no allocation requests or claims have been made yet;
consensus is deferred until then
* 'waiting' - an attempt to achieve consensus is ongoing, triggered by
an allocation or claim request; allocations will block. This state
persists until a quorum of peers are able to communicate amongst
themselves successfully
* 'achieved' - consensus achieved; allocations proceed normally
### More on `--init-peer-count`
TL;DR: it isn't a problem to over-estimate by a bit, but if you supply
@@ -214,7 +203,7 @@ reports on the current status of the weave router and IP allocator:
...
Service: ipam
Consensus: waiting(quorum: 2, known: 0)
Status: awaiting consensus (quorum: 2, known: 0)
Range: 10.32.0.0-10.47.255.255
DefaultSubnet: 10.32.0.0/12
@@ -225,4 +214,21 @@ The first section covers the router; see the [troubleshooting
guide](troubleshooting.html#weave-status) for full details.
The 'Service: ipam' section displays the consensus state as well as
the total allocation range and default subnet.
the total allocation range and default subnet. Columns are as follows:
* 'Status' - allocator state
* 'idle' - no allocation requests or claims have been made yet;
consensus is deferred until then
* 'awaiting consensus' - an attempt to achieve consensus is
ongoing, triggered by an allocation or claim request;
allocations will block. This state persists until a quorum of
peers are able to communicate amongst themselves successfully
* 'ready' - consensus achieved; allocations proceed normally
* 'waiting for IP range grant from peers' - peer has exhausted its
agreed portion of the range and is waiting to be granted some
more
* 'all IP ranges owned by unreachable peers' - peer has exhausted
its agreed portion of the range but cannot reach anyone to ask
for more
* 'Range' - total allocation range set by `--ipalloc-range`
* 'DefaultSubnet' - default subnet set by `--ipalloc-default-subnet`
+1 -1
View File
@@ -62,7 +62,7 @@ $ weave status
TrustedSubnets: none
Service: ipam
Consensus: achieved
Status: ready
Range: 10.32.0.0-10.47.255.255
DefaultSubnet: 10.32.0.0/12
+31
View File
@@ -0,0 +1,31 @@
#! /bin/bash
. ./config.sh
start_suite "Check propagation of DNS entries when gossip is delayed"
weave_on $HOST1 launch-router --no-discovery
weave_on $HOST2 launch-router --no-discovery $HOST1
weave_on $HOST3 launch-router --no-discovery $HOST2
start_container $HOST1 --name=c1
C1=$(container_ip $HOST1 c1)
start_container_with_dns $HOST3 --name=c3
assert_dns_record $HOST3 c3 c1.weave.local $C1
# Stall the propagation of gossip, stop weave and kill the container
run_on $HOST3 "sudo iptables -A INPUT -s $HOST2 -j DROP"
weave_on $HOST1 stop-router
docker_on $HOST1 rm -f c1
weave_on $HOST1 launch-router --no-discovery $HOST3
# Now re-enable gossip
run_on $HOST3 "sudo iptables -D INPUT -s $HOST2 -j DROP"
# Currently failing due to Mesh #28
# assert_no_dns_record $HOST3 c3 c1.weave.local
end_suite
+2 -2
View File
@@ -34,8 +34,8 @@ assert_raises "proxy docker_on $HOST1 ps"
weave_on $HOST1 stop-proxy
# Booting it over tls errors
assert_raises "DOCKER_CLIENT_ARGS='--tls' weave_on $HOST1 launch-proxy" 1
assert_raises "DOCKER_CERT_PATH='./tls' DOCKER_TLS_VERIFY=1 weave_on $HOST1 launch-proxy" 1
assert_raises "! DOCKER_CLIENT_ARGS='--tls' weave_on $HOST1 launch-proxy"
assert_raises "! DOCKER_CERT_PATH='./tls' DOCKER_TLS_VERIFY=1 weave_on $HOST1 launch-proxy"
# Booting it with a specific -H overrides defaults
weave_on $HOST1 launch-proxy -H tcp://0.0.0.0:12345
+1 -1
View File
@@ -6,7 +6,7 @@ start_suite "Proxy failure modes"
# docker run should fail when weave router is not running
weave_on $HOST1 launch-proxy
assert_raises "proxy docker_on $HOST1 run --rm $SMALL_IMAGE true" 1
assert_raises "! proxy docker_on $HOST1 run --rm $SMALL_IMAGE true"
assert_raises "proxy docker_on $HOST1 run --rm $SMALL_IMAGE true 2>&1 1>/dev/null | grep 'Error response from daemon: weave container is not present. Have you launched it?'"
end_suite
+1 -1
View File
@@ -11,7 +11,7 @@ set -e
: ${SSH_KEY_FILE:=$HOME/.ssh/gce_ssh_key}
: ${PROJECT:=positive-cocoa-90213}
: ${IMAGE:=ubuntu-14-04}
: ${TEMPLATE_NAME:=test-template-9}
: ${TEMPLATE_NAME:=test-template-10}
: ${ZONE:=us-central1-a}
: ${NUM_HOSTS:=5}
SUFFIX=""