Merge branch '0.11.2' into latest_release_doc_updates

This commit is contained in:
Adam Harrison committed 2015-06-08 17:59:55 +01:00
commit 89cf5939b8
17 files changed
+238 -107

No files matched your search

+56
View File
@@ -1,3 +1,59 @@
## Release 0.11.2
This is a bug fix release, addressing the following issues:
- `weave run` did not respect DOCKER_CLIENT_ARGS. #855
- Negative result cache did not expire if requeried within TTL. #845
More details in the [change
log](https://github.com/weaveworks/weave/issues?q=milestone%3A0.11.2).
*NB: This release does not change the weave protocol version.
Therefore, when upgrading an existing 0.11 installation incrementally,
connectivity between peers will be retained.*
## Release 0.11.1
This is a bug fix release, addressing the following issues:
- The IP Allocator could crash in some relatively rare
circumstances. #782/#783.
- When the proxy failed to attach a container to the weave network,
there was no failure indication and descriptive error anywhere, and
the application process would still start. Now an error is reported
to the client (i.e. typically the Docker CLI), recorded in the proxy
logs, and the container is terminated. #788/#799.
- `weave launch-proxy --with-ipam` failed to configure the entrypoint
and DNS unless a (possibly blank) `WEAVE_CIDR` was
specified. Affected containers could start the application process
w/o the weave network interface being available, and without
functioning name resolution for the weaveDNS domain.
#744/#747/#751/#752
- The `weave status` output for the IP Allocator was misleadingly
conveying a sense of brokenness when no IP allocation requests had
been made yet. #787/#801
- When invoking `weave launch-proxy` twice, the second invocation
would output a blank line and terminate with a success exit
status. Now it reports that the proxy is already running and exits
with a non-zero status. #767/#780
- `weave launch-proxy` was not respecting WEAVEPROXY_DOCKER_ARGS, so
any user-supplied custom configuration for the weaveproxy container
was ignored. #755/#780
- The proxy was not intercepting API calls to the unversioned (1.0)
Docker Remote API. Hence none of the weave functionality was
available when Docker clients were using that version of the
API. #770/#774/#777,#809
- The proxy would crash when certain elements of the
`/containers/create` JSON were missing. We are not aware of any
Docker clients doing this, but it's been fixed regardless. #778/#777
More details in the [change
log](https://github.com/weaveworks/weave/issues?q=milestone%3A0.11.1).
*NB: This release does not change the weave protocol version.
Therefore, when upgrading an existing 0.11 installation incrementally,
connectivity between peers will be retained.*
## Release 0.11.0
**Highlights**:
+7
View File
@@ -52,3 +52,10 @@ func Subtract(a, b Address) Offset {
common.Assert(a >= b)
return Offset(a - b)
}
func Min(a, b Offset) Offset {
if a > b {
return b
}
return a
}
+12 -10
View File
@@ -456,10 +456,12 @@ func (alloc *Allocator) actorLoop(actionChan <-chan func()) {
func (alloc *Allocator) string() string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "Allocator subnet %s/%d\n", alloc.subnetStart.String(), alloc.prefixLen)
fmt.Fprintf(&buf, "Allocator subnet %s/%d", alloc.subnetStart.String(), alloc.prefixLen)
if alloc.ring.Empty() {
fmt.Fprintf(&buf, "Awaiting consensus: %s", alloc.paxos.String())
if alloc.paxosTicker != nil {
fmt.Fprintf(&buf, " awaiting consensus: %s", alloc.paxos.String())
}
} else {
localFreeSpace := alloc.space.NumFreeAddresses()
remoteFreeSpace := alloc.ring.TotalRemoteFree()
@@ -469,14 +471,14 @@ func (alloc *Allocator) string() string {
fmt.Fprint(&buf, "Owned Ranges:")
alloc.ring.FprintWithNicknames(&buf, alloc.nicknames)
if len(alloc.pendingAllocates)+len(alloc.pendingClaims) > 0 {
fmt.Fprintf(&buf, "\nPending requests for ")
for _, op := range alloc.pendingAllocates {
fmt.Fprintf(&buf, "%s, ", op.String())
}
for _, op := range alloc.pendingClaims {
fmt.Fprintf(&buf, "%s, ", op.String())
}
}
if len(alloc.pendingAllocates)+len(alloc.pendingClaims) > 0 {
fmt.Fprintf(&buf, "\nPending requests:")
for _, op := range alloc.pendingAllocates {
fmt.Fprintf(&buf, "\n %s", op.String())
}
for _, op := range alloc.pendingClaims {
fmt.Fprintf(&buf, "\n %s", op.String())
}
}
return buf.String()
+2 -3
View File
@@ -40,7 +40,6 @@ var (
ErrInvalidEntry = errors.New("Received invalid state update!")
ErrEntryInMyRange = errors.New("Received new entry in my range!")
ErrNoFreeSpace = errors.New("No free space found!")
ErrTooMuchFreeSpace = errors.New("Entry reporting too much free space!")
ErrInvalidTimeout = errors.New("dt must be greater than 0")
ErrNotFound = errors.New("No entries for peer found")
)
@@ -76,7 +75,7 @@ func (r *Ring) checkInvariants() error {
distance := r.distance(entry.Token, next.Token)
if entry.Free > distance {
return ErrTooMuchFreeSpace
return fmt.Errorf("Entry %s-%s reporting too much free space: %d > %d", entry.Token, next.Token, entry.Free, distance)
}
}
@@ -167,7 +166,7 @@ func (r *Ring) GrantRangeToHost(start, end address.Address, peer router.PeerName
pos := preceedingPos + 1
for ; pos < len(r.Entries) && r.Entries.entry(pos).Token < end; pos++ {
entry := r.Entries.entry(pos)
entry.update(peer, entry.Free)
entry.update(peer, address.Min(entry.Free, r.distance(entry.Token, end)))
}
// There is never an entry with a token of r.End, as the end of
+11
View File
@@ -158,6 +158,17 @@ func TestGrantSplit(t *testing.T) {
wt.AssertEquals(t, ring1.Entries, entries{{Token: start, Peer: peer1name, Free: 10, Version: 1},
{Token: dot10, Peer: peer2name, Free: 235},
{Token: dot245, Peer: peer1name, Free: 10}})
ring1.assertInvariants()
// Grant range spanning a live token, and inserting a new token
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 10, Version: 2},
{Token: dot10, Peer: peer1name, Free: 118}, {Token: middle, Peer: peer1name, Free: 127}}
ring1.GrantRangeToHost(dot10, dot245, peer2name)
wt.AssertEquals(t, ring1.Entries, entries{{Token: start, Peer: peer1name, Free: 10, Version: 2},
{Token: dot10, Peer: peer2name, Free: 118, Version: 1},
{Token: middle, Peer: peer2name, Free: 117, Version: 1},
{Token: dot245, Peer: peer1name, Free: 10, Version: 0}})
ring1.assertInvariants()
}
func TestMergeSimple(t *testing.T) {
+16 -6
View File
@@ -5,13 +5,14 @@ import (
"container/heap"
"errors"
"fmt"
"github.com/benbjohnson/clock"
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
"math"
"math/rand"
"sync"
"time"
"github.com/benbjohnson/clock"
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
)
var (
@@ -145,9 +146,12 @@ func (e *cacheEntry) setReply(reply *dns.Msg, ttl int, flags uint8, now time.Tim
prevValidUntil = e.validUntil
}
e.Status = stResolved
e.Flags = flags
e.putTime = now
// make sure we do not overwrite noLocalReplies entries
if flags&CacheNoLocalReplies != 0 {
if e.Flags&CacheNoLocalReplies != 0 {
return false
}
}
if ttl != nullTTL {
e.validUntil = now.Add(time.Second * time.Duration(ttl))
@@ -161,8 +165,14 @@ 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")
}
e.Status = stResolved
e.Flags = flags
e.putTime = now
if reply != nil {
e.reply = *reply
e.ReplyLen = reply.Len()
+18 -6
View File
@@ -2,13 +2,14 @@ package nameserver
import (
"fmt"
"net"
"testing"
"time"
"github.com/benbjohnson/clock"
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
wt "github.com/weaveworks/weave/testing"
"net"
"testing"
"time"
)
// Check that the cache keeps its intended capacity constant
@@ -195,12 +196,23 @@ func TestCacheEntries(t *testing.T) {
question3 := &questionMsg3.Question[0]
t.Logf("Checking that a entry with CacheNoLocalReplies return an error")
l.Put(questionMsg3, nil, nullTTL, CacheNoLocalReplies)
l.Put(questionMsg3, nil, negLocalTTL, CacheNoLocalReplies)
resp, err = l.Get(questionMsg3, minUDPSize)
wt.AssertNil(t, resp, "Get() response with CacheNoLocalReplies")
wt.AssertNotNil(t, err, "Get() error with CacheNoLocalReplies")
wt.AssertTrue(t, err == errNoLocalReplies, "Get() error with CacheNoLocalReplies")
clk.Add(time.Second * time.Duration(negLocalTTL+1))
t.Logf("Checking that more Put's do not make the negative TTL longer")
clk.Add(time.Duration(negLocalTTL/2) * time.Second)
l.Put(questionMsg3, nil, negLocalTTL, CacheNoLocalReplies)
clk.Add(time.Duration(1) * time.Second)
l.Put(questionMsg3, nil, negLocalTTL, CacheNoLocalReplies)
clk.Add(time.Duration(1) * time.Second)
l.Put(questionMsg3, nil, negLocalTTL, CacheNoLocalReplies)
resp, err = l.Get(questionMsg3, minUDPSize)
wt.AssertNil(t, resp, "Get() response with CacheNoLocalReplies")
wt.AssertTrue(t, err == errNoLocalReplies, "Get() error with CacheNoLocalReplies")
clk.Add(time.Duration(negLocalTTL/2) * time.Second)
t.Logf("Checking that we get an expired response after %f seconds", negLocalTTL)
resp, err = l.Get(questionMsg3, minUDPSize)
wt.AssertNil(t, resp, "expired Get() response with CacheNoLocalReplies")
+2 -2
View File
@@ -35,7 +35,7 @@ func (c *client) ServeHTTP(w http.ResponseWriter, r *http.Request) {
case err == docker.ErrNoSuchImage:
http.Error(w, err.Error(), http.StatusNotFound)
default:
http.Error(w, "Unable to intercept request", http.StatusInternalServerError)
http.Error(w, err.Error(), http.StatusInternalServerError)
Warning.Print("Error intercepting request: ", err)
}
return
@@ -58,7 +58,7 @@ func (c *client) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
err = c.InterceptResponse(resp)
if err != nil {
http.Error(w, "Unable to intercept response", http.StatusInternalServerError)
http.Error(w, err.Error(), http.StatusInternalServerError)
Warning.Print("Error intercepting response: ", err)
return
}
+2 -2
View File
@@ -15,7 +15,7 @@ import (
)
var (
containerIDRegexp = regexp.MustCompile("^/v[0-9\\.]*/containers/([^/]*)/.*")
containerIDRegexp = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/([^/]*)/.*")
weaveWaitEntrypoint = []string{"/home/weavewait/weavewait"}
)
@@ -54,7 +54,7 @@ func inspectContainerInPath(client *docker.Client, path string) (*docker.Contain
Warning.Println(err)
return nil, err
}
containerID := subs[1]
containerID := subs[2]
container, err := client.InspectContainer(containerID)
if err != nil {
+8 -1
View File
@@ -15,6 +15,7 @@ type createContainerInterceptor struct {
client *docker.Client
withDNS bool
dockerBridgeIP string
withIPAM bool
}
type createContainerRequestBody struct {
@@ -35,9 +36,15 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
return err
}
if cidrs, ok := weaveCIDRsFromConfig(container.Config); ok {
if cidrs, ok := weaveCIDRsFromConfig(container.Config); ok || i.withIPAM {
Info.Printf("Creating container with WEAVE_CIDR \"%s\"", strings.Join(cidrs, " "))
if container.HostConfig == nil {
container.HostConfig = &docker.HostConfig{}
}
container.HostConfig.VolumesFrom = append(container.HostConfig.VolumesFrom, "weaveproxy")
if container.Config == nil {
container.Config = &docker.Config{}
}
if err := i.setWeaveWaitEntrypoint(container.Config); err != nil {
return err
}
+3 -2
View File
@@ -11,7 +11,8 @@ import (
)
type createExecInterceptor struct {
client *docker.Client
client *docker.Client
withIPAM bool
}
type createExecRequestBody struct {
@@ -37,7 +38,7 @@ func (i *createExecInterceptor) InterceptRequest(r *http.Request) error {
return err
}
if cidrs, ok := weaveCIDRsFromConfig(container.Config); ok {
if cidrs, ok := weaveCIDRsFromConfig(container.Config); ok || i.withIPAM {
Info.Printf("Exec in container %s with WEAVE_CIDR \"%s\"", container.ID, strings.Join(cidrs, " "))
options.Cmd = append(weaveWaitEntrypoint, options.Cmd...)
}
+5 -5
View File
@@ -12,9 +12,9 @@ import (
)
var (
containerCreateRegexp = regexp.MustCompile("/v[0-9\\.]*/containers/create")
containerStartRegexp = regexp.MustCompile("^/v[0-9\\.]*/containers/[^/]*/(re)?start$")
execCreateRegexp = regexp.MustCompile("^/v[0-9\\.]*/containers/[^/]*/exec$")
containerCreateRegexp = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/create$")
containerStartRegexp = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/[^/]*/(re)?start$")
execCreateRegexp = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/[^/]*/exec$")
)
type Proxy struct {
@@ -68,11 +68,11 @@ func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
switch {
case containerCreateRegexp.MatchString(path):
proxy.serveWithInterceptor(&createContainerInterceptor{proxy.client, proxy.withDNS, proxy.dockerBridgeIP}, w, r)
proxy.serveWithInterceptor(&createContainerInterceptor{proxy.client, proxy.withDNS, proxy.dockerBridgeIP, proxy.withIPAM}, w, r)
case containerStartRegexp.MatchString(path):
proxy.serveWithInterceptor(&startContainerInterceptor{proxy.client, proxy.withDNS, proxy.withIPAM}, w, r)
case execCreateRegexp.MatchString(path):
proxy.serveWithInterceptor(&createExecInterceptor{proxy.client}, w, r)
proxy.serveWithInterceptor(&createExecInterceptor{proxy.client, proxy.withIPAM}, w, r)
case strings.HasPrefix(path, "/weave"):
w.WriteHeader(http.StatusOK)
default:
+5 -2
View File
@@ -1,6 +1,7 @@
package proxy
import (
"errors"
"net/http"
"strings"
@@ -33,8 +34,10 @@ func (i *startContainerInterceptor) InterceptResponse(r *http.Response) error {
args := []string{"attach"}
args = append(args, cidrs...)
args = append(args, container.ID)
if _, err := callWeave(args...); err != nil {
Warning.Printf("Attaching container %s to weave network failed: %v", container.ID, err)
if output, err := callWeave(args...); err != nil {
i.client.KillContainer(docker.KillContainerOptions{ID: container.ID})
Warning.Printf("Attaching container %s to weave network failed: %s", container.ID, string(output))
return errors.New(string(output))
}
return nil
}
+24 -1
View File
@@ -76,8 +76,31 @@ without a `WEAVE_CIDR`, we can launch the proxy with the `--with-ipam`
option. For example:
host1$ weave launch-proxy --with-ipam
host1$ export DOCKER_HOST=tcp://host1:12375
More details on IPAM can be found in the [IPAM documentation](ipam.html).
Now, containers will always be automatically assigned an IP address
and attached to the weave network when we start them. For example:
host1$ docker run -ti ubuntu /bin/sh
More details on IPAM can be found in the [IPAM
documentation](ipam.html).
## Usage with WeaveDNS
Containers started via the proxy can be automatically configured to
use WeaveDNS for name resolution. To accomplish this we need to launch
the proxy with the `--with-dns` option
host1$ weave launch
host1$ weave launch-dns 10.2.254.1/24
host1$ weave launch-proxy --with-dns
With this done, any containers connected via IPAM or `WEAVE_CIDR=...`,
which are launched through the proxy will use weaveDNS for name
resolution. WeaveDNS is used in addition to any dns servers specified
via the `--dns` option. More details on weaveDNS can be found in the
[weaveDNS documentation](weavedns.html).
## Usage with WeaveDNS
+24
View File
@@ -26,4 +26,28 @@ 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
docker_on $HOST1 kill weaveproxy
weave_on $HOST1 launch-proxy --with-ipam
assert_raises "proxy docker_on $HOST1 run check-ethwe-up"
create_container_json=$(cat <<-EOF
{
"Hostname":"c1",
"AttachStdin":false,
"AttachStdout":true,
"AttachStderr":true,
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Image":"check-ethwe-up"
}
EOF
)
# Test a minimal request to the unversioned (v1.0) docker api
CONTAINER_ID=$(curl -s -X POST --header "Content-Type: application/json" http://$HOST1:12375/containers/create -d "$create_container_json" | sed -e 's/.*"Id":"\([^"]*\)".*/\1/')
assert_raises "proxy docker_on $HOST1 start -ai $CONTAINER_ID"
end_suite
+35 -59
View File
@@ -61,6 +61,7 @@ exec_remote() {
-e WEAVE_DEBUG \
-e WEAVE_DOCKER_ARGS \
-e WEAVEDNS_DOCKER_ARGS \
-e WEAVEPROXY_DOCKER_ARGS \
-e WEAVE_PASSWORD \
-e WEAVE_PORT \
-e WEAVE_CONTAINER_NAME \
@@ -141,7 +142,7 @@ if [ "$1" = "run" ]; then
fi
collect_cidr_args "$@"
shift $CIDR_COUNT
CONTAINER=$(docker run $DNS_ARGS -d "$@")
CONTAINER=$(docker $DOCKER_CLIENT_ARGS run $DNS_ARGS -d "$@")
exec_remote attach $CIDR_ARGS $CONTAINER
echo $CONTAINER
exit 0
@@ -167,6 +168,13 @@ MTU=65535
PORT=${WEAVE_PORT:-6783}
HTTP_PORT=6784
DNS_HTTP_PORT=6785
PROXY_PORT=12375
PROXY_CONTAINER_NAME=weaveproxy
PROXY_HOST="localhost"
if [ "$DOCKER_HOST" != "${DOCKER_HOST#tcp://}" ] ; then
PROXY_HOST=$(echo "$DOCKER_HOST" | sed -e "s|tcp://\([^:]*\):[0-9]*|\1|")
fi
######################################################################
# general helpers; independent of docker and weave
@@ -540,6 +548,18 @@ wait_for_status() {
return 1
}
# Just like wait_for_status, but takes the IP instead of a container ID
wait_for_status_ip() {
WAIT_TIME=0
while [ $WAIT_TIME -lt 3 ]; do
sleep $WAIT_TIME
http_call_ip $1 $2 GET ${3:-/status} >/dev/null && return 0
WAIT_TIME=$((WAIT_TIME+1))
done
echo "Timed out waiting for container to start." >&2
return 1
}
# Call $1 for all containers, passing container ID, all MACs and all IPs
with_container_addresses() {
COMMAND=$1
@@ -688,63 +708,6 @@ get_ipam_cidr() {
fi
}
######################################################################
# weave proxy helpers
######################################################################
PROXY_PORT=12375
PROXY_CONTAINER_NAME=weaveproxy
proxy_host() {
case $DOCKER_HOST in
tcp://*)
PROXY_HOST=$(echo $DOCKER_HOST | sed -e "s|tcp://\([^:]*\):[0-9]*|\1|")
;;
*)
PROXY_HOST="localhost"
;;
esac
}
wait_for_proxy_to_boot() {
proxy_host
for n in $(seq 1 10); do
if curl --max-time 1 --connect-timeout 1 -s -X GET http://$PROXY_HOST:$PROXY_PORT/weave ; then
return 0
fi
sleep 1
done
echo "Proxy did not start within 10 seconds" >&2
return 1
}
ensure_proxy_running() {
case $(docker inspect --format '{{.State.Running}} {{.Config.Image}}' $PROXY_CONTAINER_NAME 2>/dev/null) in
"true $EXEC_IMAGE")
return 0
;;
true*)
echo "Proxy already running with unknown image" >&2
exit 1
;;
false*)
docker rm -f $PROXY_CONTAINER_NAME >/dev/null
;;
esac
# Set WEAVEPROXY_DOCKER_ARGS in the environment in order to supply
# additional parameters, such as resource limits, to docker
# when launching the weave proxy container.
PROXY_CONTAINER=$(docker run --privileged --net=host -d \
--name=$PROXY_CONTAINER_NAME \
-p $PROXY_PORT:$PROXY_PORT/tcp -v /var/run/docker.sock:/var/run/docker.sock \
-v /proc:/hostproc -e PROCFS=/hostproc \
--entrypoint=/home/weave/weaveproxy \
$WEAVE_DOCKER_ARGS $WEAVEPROXY_DOCKER_ARGS \
$EXEC_IMAGE "$@")
wait_for_proxy_to_boot
}
##########################################################################################
[ $(id -u) = 0 ] || {
@@ -874,7 +837,20 @@ case "$COMMAND" in
echo $DNS_CONTAINER
;;
launch-proxy)
ensure_proxy_running "$@"
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_CONTAINER=$(docker run --privileged --net=host -d \
--name=$PROXY_CONTAINER_NAME \
-p $PROXY_PORT:$PROXY_PORT/tcp \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /proc:/hostproc -e PROCFS=/hostproc \
--entrypoint=/home/weave/weaveproxy \
$WEAVEPROXY_DOCKER_ARGS \
$EXEC_IMAGE "$@")
wait_for_status_ip $PROXY_HOST $PROXY_PORT /weave
echo $PROXY_CONTAINER
;;
connect)
+8 -8
View File
@@ -10,22 +10,22 @@ import (
)
func main() {
if _, err := net.EnsureInterface("ethwe", 20); err != nil {
fmt.Fprintln(os.Stderr, err)
}
_, err := net.EnsureInterface("ethwe", 20)
checkErr(err)
if len(os.Args) <= 1 {
os.Exit(0)
}
binary, err := exec.LookPath(os.Args[1])
checkErr(err)
checkErr(syscall.Exec(binary, os.Args[1:], os.Environ()))
}
func checkErr(err error) {
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
if err := syscall.Exec(binary, os.Args[1:], os.Environ()); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}