Compare commits

...
196 Commits
Author SHA1 Message Date
Bryan Boreham db120fb9be Add release v1.4.0 2015-12-17 10:30:06 +00:00
Adam Harrison ea6e171a7f Merge pull request #1794 from weaveworks/warn-stop-plugin
Warn user if weave network is in use on stop or reset of plugin
2015-12-17 10:27:58 +00:00
Bryan Boreham 246cb76e50 Add note to docs about not removing the plugin 2015-12-17 10:26:08 +00:00
Bryan Boreham ecf4a82b99 Warn user if weave network is in use on stop or reset 2015-12-17 10:05:36 +00:00
Bryan Boreham 357f67255e Move logging of Docker API info to callers 2015-12-17 08:25:51 +00:00
Adam Harrison 7015ab8717 Merge pull request #1780 from weaveworks/1748-no-with-dns
Remove WITH_DNS variable from weave script
2015-12-16 15:59:24 +00:00
Bryan Boreham 67195bc9f3 Remove WITH_DNS variable and deprecate --with-dns option to 'weave run' 2015-12-16 14:55:35 +00:00
Adam Harrison ce18509aa7 Merge pull request #1781 from weaveworks/1754-no-with-dns
Remove --with-dns option from proxy
2015-12-16 14:02:30 +00:00
Bryan Boreham cbaa92dcb2 Revert "don't broadcast-gossip other peer when adding a connection"
while we consider how to fix it properly.

This reverts commit cbb2ad018d.
2015-12-16 13:46:37 +00:00
Bryan Boreham f09d87c112 Merge pull request #1796 from weaveworks/gossip-test-connection-mistake
fix gossip test connection peer initialisation and connection deletion mistakes
2015-12-16 13:36:50 +00:00
Adam Harrison 7a8bba4d95 Merge pull request #1776 from weaveworks/1752-plugin-multicast
Plugin optional multicast route
2015-12-16 13:28:03 +00:00
Bryan Boreham e6e414a017 Merge pull request #1795 from weaveworks/issues/1772-fix-fastdp-address-already-in-use
Introduce limited retry on vxlan vport creation. LGTM; fixes #1772.
2015-12-16 13:22:50 +00:00
Matthias Radestock 81df06b3f4 fix DeleteTestGossipConnection
This is a one-sided operation, so we shouldn't be touching the
remote's Peers.

Also fix the associated test... When both 1 and 2 have lost their
connection to 3, they no longer have a route to 3 and hence gc it.

And because they no longer have a route, gossip from them won't get
through to 3.
2015-12-16 13:18:05 +00:00
Matthias Radestock 5401037743 refactor: inline mockGossipConnection.Connect
Also change Start() order in AddTestGossipConnection to be more
pleasing to the eye; the order isn't actually important.
2015-12-16 12:45:12 +00:00
Matthias Radestock 9d9768ee6f fix incorrect peer initialisation on test gossip connections
We were creating peers with NewPeerFrom, which copies the Version (but
not connections). That's not what happens for a real connection, where
remote peers are always created with an initial version of 0.

The mistake results in brokenness: when we subsequently receive a
topology update for that peer with the same version then we ignore it,
even though the update contains connection information which is new to
us.

Related, it is conceptually wrong to call FetchWithDefault on the
remote router. That is not what happens for a real connection - the
FetchWithDefault occurs in the symmetric counterpart.
2015-12-16 12:33:07 +00:00
Bryan Boreham 3043217b2c Extend --no-multicast-route test to plugin 2015-12-16 12:17:25 +00:00
Bryan Boreham afcb84d42c Set up static route for multicast, with option to turn off 2015-12-16 12:17:25 +00:00
Adam Harrison d5aad8f8d2 Introduce limited retry on vxlan vport creation
The kernel processes vport deletion asynchronously, meaning that we can
sometimes fail to recreate vxlan vports on startup with an EADDRINUSE
error. We compensate by retrying a small number of times with a short
sleep on encountering this error, failing as before on a different error
or exhaustion of retries.
2015-12-16 11:51:28 +00:00
Bryan Boreham c46febd7df Fix Makefile so weavewait* are statically linked
- the `-tags netgo` was being ignored by an interaction with the newer tags
2015-12-16 11:45:10 +00:00
Bryan Boreham f880da4827 Merge pull request #1722 from weaveworks/fdp-high-level-overview
Add fast datapath high level overview
2015-12-16 10:49:31 +00:00
Bryan Boreham 85c9cf43f7 Merge pull request #1689 from weaveworks/1586-alternate-docker-sock
Support weave script running against non-standard DOCKER_HOST.
2015-12-16 10:35:46 +00:00
Adam Harrison ce90d2389b Merge pull request #1790 from weaveworks/1783-veth-name
Prefix interface names with 'veth' so NetworkManager ignores them
2015-12-16 10:34:32 +00:00
Bryan Boreham d8cfbc4e08 Launch the proxy via unix socket in last test
- proxy copies permissions from $DOCKER_HOST socket if available, so in
   this case it was not accessible because we were booting via a TCP
   socket
2015-12-16 10:14:44 +00:00
Paul Bellamy 88fad251aa add test for proxy against docker on non-standard unix socket
Means we need to rebuild the gce images

(also small improvement re tmpdir)
2015-12-16 10:14:15 +00:00
Bryan Boreham 1a1d8b7c74 Prefix interface names with 'veth' so NetworkManager ignores them 2015-12-16 10:01:57 +00:00
Paul Bellamy a4ef96690e support alternate DOCKER_HOST settings when launching the plugin 2015-12-15 18:12:31 +00:00
Paul Bellamy f28ad350bd make weave and the proxy work with tcp-only dockers via DOCKER_HOST 2015-12-15 18:02:59 +00:00
Paul Bellamy 0afc3db383 First pass at passing DOCKER_HOST=unix://* through to the weave and proxy 2015-12-15 18:02:27 +00:00
Paul Bellamy ca1cce3232 Makefile should use DOCKER_HOST env var 2015-12-15 18:02:27 +00:00
Adam Harrison f9228eeb46 Merge pull request #1738 from weaveworks/1727-local-plugin
"mesh mode" plugin
2015-12-15 17:53:26 +00:00
Adam Harrison 0c077b8bb1 Merge pull request #1782 from weaveworks/1709-json-func
Add a 'json' function for 'weave report -f'
2015-12-15 17:09:02 +00:00
Bryan Boreham 0e9991f7d4 Make plugin create 'weave' network by default 2015-12-15 16:52:37 +00:00
Bryan Boreham 41482aff01 Update plugin README and documentation 2015-12-15 16:52:37 +00:00
Bryan Boreham 791fc9d6b1 Return fake 'gateway address' to avoid requiring consensus during initialization 2015-12-15 16:52:37 +00:00
Bryan Boreham 776ccac00a Pin docker API version to 1.21 2015-12-15 16:52:30 +00:00
Bryan Boreham 1539009362 Simple smoke-test for 'local' plugin network 2015-12-15 16:47:33 +00:00
Bryan Boreham 9542b77a34 Extend smoke-test framework for weaveplugin 2015-12-15 16:47:33 +00:00
Bryan Boreham b709cdeab5 Create local and global network from the same driver and create local-scope weave network by default on weave launch 2015-12-15 16:47:18 +00:00
Bryan Boreham e2f936fde3 Rework version check
- extract function so we can use the check elsewhere
- use shell variable expansion rather than exec-ing 'cut'
2015-12-15 16:40:43 +00:00
Bryan Boreham a4ca389f2e Make the plugin scope (local or global) an option 2015-12-15 16:31:07 +00:00
Bryan Boreham c973808b20 Merge pull request #1758 from weaveworks/issues/82-optional-encryption
Make encryption optional; fixes #82, #1788. LGTM.
2015-12-15 15:23:58 +00:00
Adam Harrison e12dbb4605 Disable pcap optimisation of encrypted sleeve
Prior to optional encryption support, enabling encryption disabled the
fast datapath overlay (because it doesn't support encryption) but left
the OVS datapath netdev in place for bridging (so that users could
switch encryption on and off without resetting the bridge). In this
situation the ODP miss handler is guaranteed to be invoked for every
packet, so as an optimisation the weave script configured the router to
use pcap to capture packets from the bridge as that is slightly more
efficient. The introduction of optional encryption means the guarantee
no longer holds, and so the optimisation must be removed.
2015-12-15 14:51:21 +00:00
Bryan Boreham c341399498 Document 'json' function 2015-12-15 13:52:28 +00:00
Bryan Boreham d1ba4ab7f4 Merge pull request #1755 from weaveworks/issues/1741-weave-no-multicast-route
Add options to disable multicast route - LGTM; fixes #1741
2015-12-15 10:17:09 +00:00
Adam Harrison 85d4b1f137 Smoke test trusted subnets 2015-12-14 12:13:16 +00:00
Adam Harrison b9792f8f96 Document trusted subnets
Update features and troubleshooting markdown.
2015-12-14 12:13:16 +00:00
Adam Harrison e0cb9cf24d Restore leading whitespace to weave status 2015-12-14 12:13:16 +00:00
Adam Harrison 4e41d4d451 Add trusted subnets to weave status
`weave status` now displays the list of specified trusted subnets, and
`weave staus connections` shows the encryption state of individual
connections.
2015-12-14 12:13:16 +00:00
Adam Harrison 059cc65b55 Fallback to encrypted sleeve on a per-connection basis
If overlay encryption is required between peers, the fast datapath
forwarder returns an error rather than aborting the process, allowing
the overlay switch to fall back gracefully to an encrypted sleeve
overlay connection.
2015-12-14 12:13:11 +00:00
Adam Harrison d434b50dfc Include trust level in features exchange
Add a 'Trusted' feature to the protocol exchange which indicates whether
the remote considers us to be reachable via a secure network; overlay
encryption will be disabled in the event that both peers agree. For
backwards compatibility purposes a default value of untrusted is assumed
if the feature is missing.
2015-12-14 12:12:25 +00:00
Adam Harrison 96f69583e7 Smoke test --no-multicast-route 2015-12-14 11:53:06 +00:00
Adam Harrison ce25f58639 weave launch-proxy accepts --no-multicast-route 2015-12-14 11:53:06 +00:00
Adam Harrison 367b73e6e3 Introduce nomcast weavewait 2015-12-14 11:53:06 +00:00
Adam Harrison 98a7d3fc99 weave run and weave attach accept --no-multicast-route 2015-12-14 11:53:06 +00:00
Bryan Boreham 6fd0f121a0 Add a 'json' function for 'weave report -f' 2015-12-12 14:44:54 +00:00
Bryan Boreham 68d6c262d1 Remove --with-dns option from proxy 2015-12-12 14:12:39 +00:00
Bryan Boreham b51b1adfcd Merge pull request #1767 from weaveworks/1765-test-gossip-surrogate-failure
better emulation of connections in gossip tests. Fixes #1765.
2015-12-11 17:20:23 +00:00
Matthias Radestock ab902b45aa refactor: introduce a helper to reduce duplication 2015-12-10 10:56:18 +00:00
Matthias Radestock ed14561afd refactor: rename 2015-12-10 10:54:20 +00:00
Matthias Radestock 86d2db5cc2 better emulation of connections in gossip tests
real Connections do not process gossip until after AddConnection.
mockChannelConnection wasn't behaving in that way, which resulted in
some sporadic test failures since it would cause some gossip to get
processed too early, which in turn would trigger gc of some of the
just-received peers, which we would then find missing in the test
assertions.

To fix this we introduce a latch on mockChannelConnection which closes
when the remote end is ready to receive gossip. Effectively this
emulates the entering of LocalConnection.receiveTCP after the call to
AddConnection completes.

Unfortunately an open latch will cause sendPendingGossip to block. We
get around that by abandoning checks in "half-connected" connection
states.

Fixes #1765.
2015-12-10 10:54:10 +00:00
Bryan Boreham 700851cdd5 Add a note about connecting up peers 2015-12-10 10:40:48 +00:00
Adam Harrison c3e1f5a6c6 .gitignore tls binary 2015-12-10 10:08:04 +00:00
Bryan Boreham 9bf18b1761 Merge pull request #1769 from weaveworks/ditch-run-with-timeout
Wins the prize for "best add-to-delete ratio in a PR this year"
2015-12-09 13:56:54 +00:00
Matthias Radestock 1374e4c4de ditch RunWithTimeout test helper
It breaks stack traces on failure.

We can just rely on running `go test -timeout ...` instead, which we
already do in tools/test.
2015-12-09 13:20:57 +00:00
Matthias Radestock 0ef57a102f cosmetic: remove unnecessary comment 2015-12-09 12:37:53 +00:00
Adam Harrison 1c7954a43d Merge pull request #1728 from weaveworks/1676-ipam-plugin
Docker IPAM plugin
2015-12-09 11:07:46 +00:00
Matthias Radestock be1dc8ef96 Merge branch '1.3' 2015-12-09 10:34:52 +00:00
Matthias Radestock 18fc6ccc03 remove redundant code in test 2015-12-09 10:32:14 +00:00
Matthias Radestock 7ec46983aa Merge branch '1.3' 2015-12-08 23:08:35 +00:00
Matthias Radestock eddcf93942 oops 2015-12-08 23:08:19 +00:00
Matthias Radestock 42807f7b8a Merge branch '1.3' 2015-12-08 23:07:06 +00:00
Matthias Radestock 0b521272ae prevent peer duplication in tests
and correct the test assertions accordingly - they make more sense
now.

Fixes #1766.
2015-12-08 23:06:02 +00:00
Bryan Boreham ecd93bbfe4 Resolve weave IP every time we need it, in case it changed 2015-12-08 16:59:34 +00:00
Bryan Boreham a6f07fbaac Move baseUrl usage up to httpVerb 2015-12-08 16:59:34 +00:00
Bryan Boreham f87876c1c3 Fetch the default subnet from weave to use as the ipam driver 'pool' 2015-12-08 16:59:34 +00:00
Bryan Boreham ec3de1a072 Introduce ipam plugin 2015-12-08 16:59:32 +00:00
Bryan Boreham c1d2c159f0 Expand listener skeleton to support IPAM driver 2015-12-08 16:58:55 +00:00
Bryan Boreham 9f86e2ba43 Moved net plugin to its own directory 2015-12-08 16:58:53 +00:00
Adam Harrison d0be36dc9b Merge branch '1.3'
Conflicts:
	Makefile
2015-12-08 16:53:47 +00:00
Adam Harrison af604a1477 Merge pull request #1760 from weaveworks/make-test-tls
Move test/tls program into Makefile so it builds inside the build container
2015-12-08 16:41:59 +00:00
Bryan Boreham c981cff450 Move build of test/tls program into Makefile so it builds in the build container 2015-12-08 16:14:53 +00:00
Bryan Boreham ab1033c26c Merge pull request #1736 from weaveworks/remove-unknown-peer-error
remove UnknownPeerError for quicker convergence and efficiency. LGTM.
2015-12-04 11:00:05 +00:00
Adam Harrison 01706df7c8 Merge pull request #1753 from weaveworks/1751-remove-unused-vars
remove CLIENT_TLS_ENABLED and DOCKER_CLIENT_TLS_VERIFY, as they're unused
2015-12-04 10:19:09 +00:00
Adam Harrison 827a7c1cf1 Merge branch '1.3' 2015-12-04 10:09:15 +00:00
Adam Harrison d14d9933b4 Merge pull request #1750 from weaveworks/add-some-quotes
quote $@
2015-12-04 10:08:34 +00:00
Adam Harrison af946893f0 Merge pull request #1749 from weaveworks/remove-eqeq-non-posixism
'==' is not POSIX
2015-12-04 10:08:26 +00:00
Paul Bellamy 245ae5e987 remove CLIENT_TLS_ENABLED and DOCKER_CLIENT_TLS_VERIFY, as they're unused 2015-12-02 17:17:04 +00:00
Matthias Radestock 29f3d711c6 Merge branch '1.3' 2015-12-02 15:34:07 +00:00
Matthias Radestock 3909936cc8 cosmetic(ish) use PROXY_PORT 2015-12-02 15:33:38 +00:00
Matthias Radestock 0602c91c54 quote $@ 2015-12-02 15:28:41 +00:00
Matthias Radestock a7bdb02a2b Merge remote-tracking branch 'upstream/master' 2015-12-02 14:33:28 +00:00
Matthias Radestock 2cfaf5a119 Merge branch '1.3' 2015-12-02 14:23:28 +00:00
Matthias Radestock 20a48f05b3 fix (harmless) typo 2015-12-02 14:22:23 +00:00
Matthias Radestock 4e70fb7f9d '==' is not POSIX 2015-12-02 14:20:29 +00:00
Adam Harrison 1f438dbf71 Merge pull request #1740 from weaveworks/1694-symlink-into-var-run-2
Symlink sockets to /var/run/weave/weave.sock from inside the proxy
2015-12-02 14:12:02 +00:00
Adam Harrison 619d0e2bdd Merge pull request #1730 from weaveworks/1725-plugin-dns
Fix DNS integration in Weave docker plugin
2015-12-02 09:49:21 +00:00
Bryan Boreham ec420c66d4 Review feedback 2015-12-01 16:47:47 +00:00
Paul Bellamy ac1cf633c7 clarify some variable shadowing 2015-12-01 16:16:39 +00:00
Paul Bellamy 6f25d2170d [proxy] have symlinking respond to WEAVE_DEBUG not DEBUG 2015-12-01 15:51:16 +00:00
Paul Bellamy 295a2ced32 [proxy] treat addrs prefix with / as unix addrs 2015-12-01 15:45:32 +00:00
Matthias Radestock cbb2ad018d don't broadcast-gossip other peer when adding a connection
This is more efficient since broadcasting about other peers is
strictly redundant. Any change to the peers will be broadcast by
those peers anyway. And SendAllGossipDown takes care of informing new
peers about others on first connect, and periodic gossip take care of
missed broadcasts.

The only reason we did broadcast about the remote peer is to reduce
the liklihood of UnknownPeerError and the associated throwing away of
gossip. This was always a bit of kludge, and is no longer necessary
since UnknownPeerError is gone.

Now peers only ever broadcast information about themselves. Which,
unlike information about other peers, is always authorative and most
up-to-date. Which is nice.
2015-12-01 13:10:06 +00:00
Matthias Radestock 7e15eabe65 remove UnknownPeerError
when encountering an unknown peer in received gossip, rather than
ignoring the entire gossip we create a placeholder Peer. This has
version 0 and uid 0, which means it will be replaced by any complete
peer information subsequently received via gossip.
2015-11-30 16:17:16 +00:00
Bryan Boreham eb9b5b535f Merge branch '1.3' 2015-11-30 16:16:09 +00:00
Bryan Boreham a13351a9eb Merge pull request #1712 from weaveworks/issues/1577-interpose-bridge-netdev
Optional intermediary bridge netdev. Fixes #1577.
2015-11-30 15:54:14 +00:00
Bryan Boreham 74fdf6beec Merge pull request #1732 from weaveworks/1731-merge-peer-has-short-id
update Peer.HasShortID as part of topology merge
Fixes #1731
2015-11-30 15:53:23 +00:00
Paul Bellamy cd7c88e5a5 [proxy] Symlink sockets to /var/run/weave/weave.sock from inside the proxy
We copy docker.sock owner to symlinks. Permissions are not copied,
because the permissions will inherit from the target.

use versioned exec image to create symlinks
2015-11-30 15:45:30 +00:00
Bryan Boreham a22b88ed89 Track endpoints so we can register the right address with DNS 2015-11-30 14:27:33 +00:00
Bryan Boreham 5eb99fe5ef Get container IP for various networks 2015-11-30 14:27:33 +00:00
Bryan Boreham 85e465cfa4 Eliminate 'dockerer' 2015-11-30 14:27:33 +00:00
Bryan Boreham e101dde62e Extract 'api' package 2015-11-30 14:27:33 +00:00
Adam Harrison 1980ffea30 Merge pull request #1702 from weaveworks/issues/1639-ipam-diagnostics
Show some IPAM stats with `weave status ipam`
2015-11-30 14:20:13 +00:00
Adam Harrison 0935e0a13d Merge branch '1.3' 2015-11-30 14:09:44 +00:00
Adam Harrison b2b68119fc Merge pull request #1719 from weaveworks/issues/1653-make-dir
Ensure /var/run/weave directory exists before telling Docker to use it
2015-11-30 13:57:37 +00:00
Adam Harrison 4420057d4f Merge pull request #1716 from weaveworks/issues/1714-complete-addr-1.3
Ensure `weave connect` attempts connection immediately
2015-11-30 13:52:13 +00:00
Bryan Boreham bd2b8df7c5 Refactor to use Weave's docker client and watcher 2015-11-30 13:25:20 +00:00
Matthias Radestock 068bbe0837 Merge branch '1.3' 2015-11-30 13:19:21 +00:00
Bryan Boreham 7e0e1fbaad Adjust formatting after review feedback; hostnames up to 14 chars will now align 2015-11-30 12:07:59 +00:00
Bryan Boreham 9723476392 Merge pull request #1708 from weaveworks/gce-connect-fix
Connect to gce boxes as vagrant so we can access docker unix socket
2015-11-30 11:45:30 +00:00
Matthias Radestock 9dee2f2230 update Peer.HasShortID as part of topology merge
This was missing.

There are two parts to this change:

1. updating the Peers.byName mapping when HasShortID changed (in
addition to the existing update on ShortID change). We need to be
careful about the sequencing here - both ShortID and HasShortID must
be set between the calls to deleteByShortID and addByShortID, since
the former is meant to delete the existing entry (hence use the old
values) and the latter add the new entry (hence use the new values).

2. update a peer when the received info has the same version and UID
but the current record has HasShortID=false, and the received info has
HasShortID=true. This caters for the case where we receive an update
about a weave 1.2+ peer first via a pre-1.2 peer (which does not
transmit ShortIDs, and always sets HasShortID=false) and subsequently
via a 1.2+ peer. The former update will force us to communicate with
that peer over the 'sleeve' overlay. We want to accept the latter
update, even though the version is the same, in order to update the
ShortID and HasShortID, so we can switch to fastdp.

Fixes #1731.
2015-11-29 16:32:50 +00:00
Matthias Radestock 175f38414e cosmetic 2015-11-28 20:15:57 +00:00
Bryan Boreham 8bbf8231d2 Show which range is being allocated in weave report 2015-11-26 17:24:41 +00:00
Bryan Boreham 2fbbb802bd Don't assert invariants when printing status
- if it's broken you might appreciate being able to print it out
2015-11-26 17:24:41 +00:00
Bryan Boreham cb65a80dce Reworded IPAM service description in weave status
following review in #1702
2015-11-26 17:24:41 +00:00
Bryan Boreham 3a9bcbcf6b Remove specialised knowledge of IPAM from weave status formatter
and unnecessary info from `weave report` output
2015-11-26 17:24:40 +00:00
Bryan Boreham e38018ab1e Add warning to main ipam status if all IPs owned by unreachable peers 2015-11-26 17:23:49 +00:00
Bryan Boreham 044c6474b2 Remove counting of ring tokens, since it isn't useful to end-user.
Previously the count of tokens was claimed to be a count of addresses in use,
which is incorrect.
Also rename `appendAddresses` to `appendOwned` since it doesn't involve addresses.
2015-11-26 17:23:49 +00:00
Bryan Boreham 5f42a4d95e Hold stats by peer identity not nickname
so we can print the peer name for each ownership.
Also avoid crashing if we don't own anything
2015-11-26 17:23:49 +00:00
Bryan Boreham 6d3fd6da07 Simplify ipam status format
Don't use the word "universe" in user-visible output; it is an internal term.
Removed everything but owned ranges from 'weave status ipam' results, for consistency with similar functions
2015-11-26 17:23:49 +00:00
Alvaro Saurin 34e7996f2f Show some IPAM status at /status/ipam 2015-11-26 17:23:47 +00:00
Bryan Boreham f2d7a28258 Add docker plugin README 2015-11-26 17:18:59 +00:00
Adam Harrison ba6f8da1e4 Merge pull request #1670 from weaveworks/issues/1634-block-6783-6784
Forbid traffic to the Weave port from containers
2015-11-26 16:52:34 +00:00
Adam Harrison 0a5e79891d Merge pull request #1724 from weaveworks/1674-move-plugin
Move Weave docker plugin into this repo
2015-11-26 15:34:15 +00:00
Bryan Boreham 901bf63cb8 Fix printf error caught by lint 2015-11-26 15:19:57 +00:00
Bryan Boreham 350b8efb0b Move Weave docker plugin into this repo 2015-11-26 14:40:28 +00:00
Adam Harrison 2f69b4d190 Mention WEAVE_MTU 2015-11-26 10:39:41 +00:00
Adam Harrison 57a15a4e96 Add fast datapath high level overview 2015-11-26 10:36:52 +00:00
Adam Harrison 13042002d7 Merge pull request #1663 from weaveworks/issues/1592-fdp-miss-counter
Add FDP miss counter to reports
2015-11-25 17:21:03 +00:00
Adam Harrison 8ac745b4e5 Lock read of miss counter 2015-11-25 17:05:19 +00:00
Adam Harrison 8a4f038ec4 Merge pull request #1662 from weaveworks/issues/1571-upstream-dns-report
DNS upstream servers report
2015-11-25 16:47:14 +00:00
Adam Harrison 890522f6f4 Add hidden weave bridge-type command 2015-11-25 16:31:32 +00:00
Adam Harrison c8aa032051 Eliminate function name stutter 2015-11-25 16:31:32 +00:00
Adam Harrison 9830c146d0 The bridge netdev is always named weave 2015-11-25 16:31:32 +00:00
Adam Harrison 00c9164fdb Pass WEAVE_NO_BRIDGED_FASTDP to remote for testing 2015-11-25 16:31:32 +00:00
Adam Harrison a31e7118b4 Optional intermediary bridge netdev 2015-11-25 16:31:32 +00:00
Matthias Radestock 2058ce3f3a refactor: change Routes.RandomNeighbours return for consistency
PeerNameSet -> []PeerName, which is what the other functions
return. This is also easier to deal with at the call site.
2015-11-25 14:46:41 +00:00
Matthias Radestock 61e3bf4a20 cosmetic(ish) 2015-11-25 14:32:34 +00:00
Bryan Boreham ea5bae711b Ensure /var/run/weave directory exists before telling Docker to use it 2015-11-25 13:56:09 +00:00
Bryan Boreham 44a2c5fdff Merge pull request #1718 from weaveworks/constant-spacetime-connection-shutdown
LGTM. prevent unbounded number of goroutines for connection shutdown
2015-11-25 11:22:08 +00:00
Matthias Radestock 3b43960779 prevent unbounded number of goroutines for connection shutdown
We need Connection.Shutdown to be non-blocked, and the way we achieved
this is by spawning a goroutine. However, there can be a potentially
unbounded number of Connection.Shutdown invocations. For example, if
the TCP connection has gone bad then any Connection.SendProtocolMsg
invocation will end up invoking Connection.Shutdown. There can be many
such invocations until the Connection has been removed from the
various data structures (e.g. LocalPeer.connections and
GossipSender.senders). Hence we can end up with with an unbounded
number of spawned goroutines.

To fix this, we introduce a one-element error channel on the
connection. Connection.Shutdown does a non-blocking send on that, and
the Connection.actorLoop reads from it and terminates.

This will throw away all but the first error encountered, but that is
fine and not much different from what was happening before (where we'd
throw away all but one of the errors encountered).
2015-11-25 11:06:20 +00:00
Bryan Boreham 89f27ec99a Set default port number on address before checking in InitiateConnections, to fix #1714 2015-11-24 14:55:21 +00:00
Adam Harrison 82cf4358ce Add release v1.3.1 2015-11-23 12:06:10 +00:00
Paul Bellamy 0f9b117744 Connect to gce boxes as vagrant so we can access docker unix socket 2015-11-20 16:54:41 +00:00
Adam Harrison c420c21219 Merge pull request #1707 from weaveworks/revert-1696-1694-symlink-into-var-run
Revert "Automatically symlink /var/run/*.sock sockets to /var/run/weave/*.sock"
2015-11-20 16:27:04 +00:00
Paul Bellamy 3bae4b0d93 Revert "Automatically symlink /var/run/*.sock sockets to /var/run/weave/*.sock" 2015-11-20 16:23:40 +00:00
Adam Harrison 1432bd7a81 Merge pull request #1696 from weaveworks/1694-symlink-into-var-run
Automatically symlink /var/run/*.sock sockets to /var/run/weave/*.sock
2015-11-20 16:23:12 +00:00
Adam Harrison bdcf930e25 Merge branch '1.3' 2015-11-20 11:08:49 +00:00
Adam Harrison 2e42d74c59 Merge pull request #1706 from weaveworks/1683-update-docker-1.3
Raise minimum Docker version to 1.6
2015-11-20 11:06:00 +00:00
Bryan Boreham bbf26fc3e8 Raise minimum Docker version to 1.6 to address #1683 2015-11-20 10:44:45 +00:00
Bryan Boreham 6dcc42cdeb Merge branch '1.3' 2015-11-20 10:33:38 +00:00
Bryan Boreham 385b0aa07f Fix typo in -tls info string 2015-11-20 10:33:20 +00:00
Bryan Boreham f2b1ee3398 Merge branch 'latest_release_doc_updates' into 1.3 2015-11-20 10:30:28 +00:00
Bryan Boreham 2a2c60201f Merge pull request #1699 from weaveworks/issues/1693-fix-proxy-docs-tls-args
Fix erroneously hyphenated Docker TLS arguments; Fixes #1693.
LGTM
2015-11-20 10:27:34 +00:00
Matthias Radestock 53749156a7 Merge branch '1.3' 2015-11-19 20:58:14 +00:00
Matthias Radestock ee12b34334 eliminate a gossip inefficiency and dormant bug
Fixes #1704
2015-11-19 20:57:27 +00:00
Matthias Radestock b5a4aefe8b never broadcast empty nameserver gossip 2015-11-19 18:28:37 +00:00
Bryan Boreham c2d5cab1b8 Merge remote-tracking branch 'origin/1.3' 2015-11-19 15:30:20 +00:00
Bryan Boreham f25af8f7d6 Merge pull request #1698 from weaveworks/1697-copy-on-write-gossip-data
make GossipData immutable; Fixes #1697. Fixes #1450. Fixes #1452.
2015-11-19 14:58:32 +00:00
Paul Bellamy 33648b083c SSH into test vms on gce as 'vagrant' user 2015-11-19 14:10:52 +00:00
Alvaro Saurin eb79711e0a Added flow misses count 2015-11-19 15:05:16 +01:00
Alvaro Saurin 03ce6052cc Updated docs 2015-11-19 14:54:37 +01:00
Alvaro Saurin 7992bf4177 DNS upstream servers report 2015-11-19 14:54:23 +01:00
Alvaro Saurin ddb390d5ec Forbid traffic to the Weave port from other containers
Make sure we have the Docker bridge IP before adding/removing iptables rules
2015-11-19 14:46:28 +01:00
Matthias Radestock 1500b1dc00 refactor: extract nameserver GossipData.copy
and remove a self-evident comment
2015-11-18 11:30:40 +00:00
Adam Harrison 9263d3e82c Fix erroneously hyphenated Docker TLS arguments 2015-11-18 10:24:46 +00:00
Adam Harrison 02b7fd53ed Merge tag 'v1.3.0' into latest_release_doc_updates
Resetting doc update branch for 1.3.0 release.
2015-11-18 10:14:10 +00:00
Matthias Radestock 3cd199e852 make GossipData immutable
The same piece of GossipData can be shared and subject to different
Merge operations. We either need to copy the data when we share it, or
create copies on modification, i.e. copy-on-write.

We choose the latter, since it is more efficient and there is just one
mutating operation.

Fixes #1697.
2015-11-17 20:13:46 +00:00
Paul Bellamy 77bc22baf5 [proxy] Auto-symlink /var/run/*.sock in from /var/run/weave/*.sock
Avoids the obscure warning for users
2015-11-17 17:25:21 +00:00
Paul Bellamy f57988761c Revert "Revert "Merge pull request #1580 from weaveworks/1579-launch-proxy-mount-h""
This reverts commit 7987d09438.
2015-11-17 17:15:28 +00:00
Adam Harrison 7987d09438 Revert "Merge pull request #1580 from weaveworks/1579-launch-proxy-mount-h"
This reverts commit 262352f8bb, reversing
changes made to 312691376a due to a bug
whereby `weave env` returns a unix domain socket with an incorrect path.

This change will be superceeded by #1694.
2015-11-17 16:28:31 +00:00
Adam Harrison 262352f8bb Merge pull request #1580 from weaveworks/1579-launch-proxy-mount-h
[proxy] dynamically mount -H dirs into the proxy when launching
2015-11-17 14:52:17 +00:00
Paul Bellamy e8c56a8e8e Review Feedback 2015-11-17 14:49:50 +00:00
Matthias Radestock 312691376a Merge remote-tracking branch 'upstream/master' 2015-11-17 13:57:08 +00:00
Matthias Radestock a9611c782b cosmetic: move comment 2015-11-17 13:56:18 +00:00
Adam Harrison bcc70b36fd Merge pull request #1679 from weaveworks/1539-no-detect-tls-flag
[proxy] add --no-detect-tls flag, and proxy always tries to detect TLS.
2015-11-17 13:55:43 +00:00
Matthias Radestock 9ee3b1bfbd add some timestamps to multiweave 2015-11-17 10:28:28 +00:00
Paul Bellamy e80a83b30b adding work around to the proxy /var/run warning 2015-11-17 09:40:34 +00:00
Paul Bellamy d80e72dfcd [proxy] dynamically mount -H dirs into the proxy when launching
This lets you use -H unix://*.sock outside /var/run/weave. Using a
socket directly in /var/run (i.e. /var/run/foo.sock), will reintroduce
the bug with namespaces not being GCed, so that is a warning.
2015-11-17 09:40:34 +00:00
Matthias Radestock d01c26e618 cosmetic: remove outdated comment
The deadlock described for SendProtocolMsg (if it was going via the
actor) cannot occur since all sending of broadcasts and gossip by
LocalPeer is mediated by GossipSenders, the interaction with which is
non-blocking.
2015-11-17 09:27:49 +00:00
Matthias Radestock cab2e11ca9 refactor: simplify per-connection GossipSender gc and creation
by putting it in one place and inlining some helper functions
2015-11-16 23:21:33 +00:00
Matthias Radestock b48994b1b3 cosmetic: move some functions 2015-11-16 23:01:20 +00:00
Matthias Radestock fb48acddb2 cosmetic: update comment 2015-11-16 19:27:31 +00:00
Paul Bellamy 7d627f7af4 Merge pull request #1673 from weaveworks/1440-cleanup
Move some netlink code to upstream library
2015-11-16 14:24:10 +00:00
Paul Bellamy f458491d88 [proxy] add --no-detect-tls flag, and proxy always tries to detect TLS. 2015-11-13 15:27:27 +00:00
Bryan Boreham de802f9d76 Work round docker-py assuming busybox is present (https://github.com/docker/docker-py/issues/852) 2015-11-13 11:44:08 +00:00
Bryan Boreham 804e6ba586 Cope with changes in docker-py structure.
Tests are now in a different directory,
and get sharded by module rather than individually.
2015-11-13 11:08:57 +00:00
Bryan Boreham e230eeb486 Use upstream netlink code 2015-11-12 13:08:56 +00:00
80 changed files with 2611 additions and 734 deletions

No files matched your search

+5
View File
@@ -40,25 +40,30 @@ prog/weaveproxy/weaveproxy
prog/sigproxy/sigproxy
prog/weavewait/weavewait
prog/weavewait/weavewait_noop
prog/weavewait/weavewait_nomcast
prog/netcheck/netcheck
prog/docker_tls_args/docker_tls_args
prog/plugin/plugin
testing/cover/cover
testing/runner/runner
releases
.weaver.uptodate
.weavedns.uptodate
.weaveexec.uptodate
.dockerplugin.uptodate
weave.tar.gz
prog/weaveexec/weave
prog/weaveexec/weavehosts
prog/weaveexec/weaveproxy
prog/weaveexec/weavewait
prog/weaveexec/weavewait_noop
prog/weaveexec/weavewait_nomcast
prog/weaveexec/sigproxy
prog/weaveexec/netcheck
prog/weaveexec/docker_tls_args
prog/weaveexec/docker*.tgz
Vagrantfile.local
test/tls/tls
test/tls/*.pem
test/coverage
test/coverage.*
+27
View File
@@ -1,3 +1,30 @@
## Release 1.4.0
**Highlights**
* The Docker Network plugin can now operate without a cluster store, so it is now run by default.
* You can now use the fast datapath over trusted links and Weave encryption over untrusted links, in the same network.
Plus many bug fixes and minor enhancements. More details below and in
the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.4.0).
## Release 1.3.1
**Highlights**
* The minimum Docker version has been increased to 1.6 due to the
[upcoming deprecation of Dockerhub access](https://blog.docker.com/2015/10/docker-hub-deprecation-1-5/)
for old clients. From December 7th onwards previous versions of the
`weave` script will fail to pull down images from the hub; if you
are unable to upgrade to 1.3.1 immediately you can work around this
by running `weave --local setup` in conjunction with a compatible
Docker client installation
* Docker networking plugin now works with older kernels and allows you
to configure the MTU
More details in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.3.1).
## Release 1.3.0
**Highlights**
+36 -17
View File
@@ -14,25 +14,30 @@ WEAVEPROXY_EXE=prog/weaveproxy/weaveproxy
SIGPROXY_EXE=prog/sigproxy/sigproxy
WEAVEWAIT_EXE=prog/weavewait/weavewait
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
NETCHECK_EXE=prog/netcheck/netcheck
DOCKERTLSARGS_EXE=prog/docker_tls_args/docker_tls_args
DOCKERPLUGIN_EXE=prog/plugin/plugin
RUNNER_EXE=tools/runner/runner
TEST_TLS_EXE=test/tls/tls
EXES=$(WEAVER_EXE) $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(NETCHECK_EXE) $(DOCKERTLSARGS_EXE)
EXES=$(WEAVER_EXE) $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(NETCHECK_EXE) $(DOCKERTLSARGS_EXE) $(DOCKERPLUGIN_EXE) $(TEST_TLS_EXE)
WEAVER_UPTODATE=.weaver.uptodate
WEAVEEXEC_UPTODATE=.weaveexec.uptodate
DOCKERPLUGIN_UPTODATE=.dockerplugin.uptodate
IMAGES_UPTODATE=$(WEAVER_UPTODATE) $(WEAVEEXEC_UPTODATE)
IMAGES_UPTODATE=$(WEAVER_UPTODATE) $(WEAVEEXEC_UPTODATE) $(DOCKERPLUGIN_UPTODATE)
WEAVER_IMAGE=$(DOCKERHUB_USER)/weave
WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec
DOCKERPLUGIN_IMAGE=$(DOCKERHUB_USER)/plugin
IMAGES=$(WEAVER_IMAGE) $(WEAVEEXEC_IMAGE)
IMAGES=$(WEAVER_IMAGE) $(WEAVEEXEC_IMAGE) $(DOCKERPLUGIN_IMAGE)
WEAVE_EXPORT=weave.tar.gz
WEAVEEXEC_DOCKER_VERSION=1.3.1
WEAVEEXEC_DOCKER_VERSION=1.6.2
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
NETGO_CHECK=@strings $@ | grep cgo_stub\\\.go >/dev/null || { \
@@ -47,7 +52,7 @@ BUILD_FLAGS=-ldflags "-extldflags \"-static\" -X main.version $(WEAVE_VERSION)"
PACKAGE_BASE=$(shell go list -e ./)
all: $(WEAVE_EXPORT) $(RUNNER_EXE)
all: $(WEAVE_EXPORT) $(RUNNER_EXE) $(TEST_TLS_EXE)
travis: $(EXES)
@@ -74,38 +79,52 @@ $(WEAVER_EXE): router/*.go mesh/*.go ipam/*.go ipam/*/*.go nameserver/*.go prog/
$(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
# These next programs need separate rules as they fail the netgo check in
# the main build stanza due to not importing net package
$(SIGPROXY_EXE): prog/sigproxy/main.go
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
$(DOCKERTLSARGS_EXE): prog/docker_tls_args/*.go
$(DOCKERPLUGIN_EXE): prog/plugin/*.go plugin/net/*.go plugin/ipam/*.go plugin/skel/*.go api/*.go common/docker/*.go
$(TEST_TLS_EXE): test/tls/*.go
$(WEAVEWAIT_EXE) $(SIGPROXY_EXE) $(DOCKERTLSARGS_EXE):
$(SIGPROXY_EXE) $(DOCKERTLSARGS_EXE) $(DOCKERPLUGIN_EXE) $(TEST_TLS_EXE):
go get -tags netgo ./$(@D)
go build $(BUILD_FLAGS) -o $@ ./$(@D)
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
go get -tags netgo ./$(@D)
go build $(BUILD_FLAGS) -tags "netgo iface mcast" -o $@ ./$(@D)
$(WEAVEWAIT_NOMCAST_EXE): prog/weavewait/*.go net/*.go
go get -tags netgo ./$(@D)
go build $(BUILD_FLAGS) -tags "netgo iface" -o $@ ./$(@D)
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
go get -tags netgo ./$(@D)
go build $(BUILD_FLAGS) -tags noop -o $@ ./$(@D)
go build $(BUILD_FLAGS) -o $@ ./$(@D)
$(WEAVER_UPTODATE): prog/weaver/Dockerfile $(WEAVER_EXE)
$(SUDO) docker build -t $(WEAVER_IMAGE) prog/weaver
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -t $(WEAVER_IMAGE) prog/weaver
touch $@
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(NETCHECK_EXE) $(DOCKERTLSARGS_EXE)
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile prog/weaveexec/symlink $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(NETCHECK_EXE) $(DOCKERTLSARGS_EXE)
cp weave prog/weaveexec/weave
cp $(SIGPROXY_EXE) prog/weaveexec/sigproxy
cp $(WEAVEPROXY_EXE) prog/weaveexec/weaveproxy
cp $(WEAVEWAIT_EXE) prog/weaveexec/weavewait
cp $(WEAVEWAIT_NOOP_EXE) prog/weaveexec/weavewait_noop
cp $(WEAVEWAIT_NOMCAST_EXE) prog/weaveexec/weavewait_nomcast
cp $(NETCHECK_EXE) prog/weaveexec/netcheck
cp $(DOCKERTLSARGS_EXE) prog/weaveexec/docker_tls_args
cp $(DOCKER_DISTRIB) prog/weaveexec/docker.tgz
$(SUDO) docker build -t $(WEAVEEXEC_IMAGE) prog/weaveexec
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -t $(WEAVEEXEC_IMAGE) prog/weaveexec
touch $@
$(DOCKERPLUGIN_UPTODATE): prog/plugin/Dockerfile $(DOCKERPLUGIN_EXE)
$(SUDO) docker build -t $(DOCKERPLUGIN_IMAGE) prog/plugin
touch $@
$(WEAVE_EXPORT): $(IMAGES_UPTODATE)
$(SUDO) docker save $(addsuffix :latest,$(IMAGES)) | gzip > $@
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker save $(addsuffix :latest,$(IMAGES)) | gzip > $@
$(DOCKER_DISTRIB):
curl -o $(DOCKER_DISTRIB) $(DOCKER_DISTRIB_URL)
@@ -123,16 +142,16 @@ $(RUNNER_EXE): tools/.git
make -C tools/runner
$(PUBLISH): publish_%: $(IMAGES_UPTODATE)
$(SUDO) docker tag -f $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
$(SUDO) docker push $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker tag -f $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker push $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
ifneq ($(UPDATE_LATEST),false)
$(SUDO) docker push $(DOCKERHUB_USER)/$*:latest
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker push $(DOCKERHUB_USER)/$*:latest
endif
publish: $(PUBLISH)
clean-bin:
-$(SUDO) docker rmi $(IMAGES)
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(IMAGES)
go clean -r $(addprefix ./,$(dir $(EXES)))
rm -f $(EXES) $(IMAGES_UPTODATE) $(WEAVE_EXPORT)
+1 -1
View File
@@ -41,7 +41,7 @@ capabilities, so these can continue to be used by containers.
## Installation
Ensure you are running Linux (kernel 3.8 or later) and have Docker
(version 1.3.1 or later) installed. Then install weave with
(version 1.6.0 or later) installed. Then install weave with
sudo curl -L git.io/weave -o /usr/local/bin/weave
sudo chmod a+x /usr/local/bin/weave
+18
View File
@@ -0,0 +1,18 @@
package api
import (
"fmt"
"net/url"
)
func (client *Client) RegisterWithDNS(ID string, fqdn string, ip string) error {
data := url.Values{}
data.Add("fqdn", fqdn)
_, err := client.httpVerb("PUT", fmt.Sprintf("/name/%s/%s", ID, ip), data)
return err
}
func (client *Client) DeregisterWithDNS(ID string, ip string) error {
_, err := client.httpVerb("DELETE", fmt.Sprintf("/name/%s/%s", ID, ip), nil)
return err
}
+49
View File
@@ -0,0 +1,49 @@
package api
import (
"fmt"
"net"
)
func (client *Client) ipamOp(ID string, op string) (*net.IPNet, error) {
ip, err := client.httpVerb(op, fmt.Sprintf("/ip/%s", ID), nil)
if err != nil {
return nil, err
}
return parseIP(ip)
}
// returns an IP for the ID given, allocating a fresh one if necessary
func (client *Client) AllocateIP(ID string) (*net.IPNet, error) {
return client.ipamOp(ID, "POST")
}
// returns an IP for the ID given, or nil if one has not been
// allocated
func (client *Client) LookupIP(ID string) (*net.IPNet, error) {
return client.ipamOp(ID, "GET")
}
// release an IP which is no longer needed
func (client *Client) ReleaseIP(ID string) error {
_, err := client.ipamOp(ID, "DELETE")
return err
}
func (client *Client) DefaultSubnet() (*net.IPNet, error) {
cidr, err := client.httpVerb("GET", fmt.Sprintf("/ipinfo/defaultsubnet"), nil)
if err != nil {
return nil, err
}
_, ipnet, err := net.ParseCIDR(cidr)
return ipnet, err
}
func parseIP(body string) (*net.IPNet, error) {
ip, ipnet, err := net.ParseCIDR(string(body))
if err != nil {
return nil, err
}
ipnet.IP = ip
return ipnet, nil
}
+73
View File
@@ -0,0 +1,73 @@
package api
import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
. "github.com/weaveworks/weave/common"
)
const (
WeaveHttpPort = 6784
)
type Client struct {
baseUrl string
resolve func() (string, error)
}
func (client *Client) httpVerb(verb string, url string, values url.Values) (string, error) {
baseUrl := client.baseUrl
if client.resolve != nil {
addr, err := client.resolve()
if err != nil {
return "", err
}
baseUrl = fmt.Sprintf("http://%s:%d", addr, WeaveHttpPort)
}
url = baseUrl + url
Log.Debugf("weave %s to %s with %v", verb, url, values)
var body io.Reader
if values != nil {
body = strings.NewReader(values.Encode())
}
req, err := http.NewRequest(verb, url, body)
if err != nil {
return "", err
}
if values != nil {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
if resp, err := http.DefaultClient.Do(req); err != nil {
return "", err
} else {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNoContent {
return string(body), nil
} else {
return "", errors.New(resp.Status + ": " + string(body))
}
}
}
func NewClient(addr string) *Client {
return &Client{baseUrl: fmt.Sprintf("http://%s:%d", addr, WeaveHttpPort)}
}
func NewClientWithResolver(resolver func() (string, error)) *Client {
return &Client{resolve: resolver}
}
func (client *Client) Connect(remote string) error {
_, err := client.httpVerb("POST", "/connect", url.Values{"peer": {remote}})
return err
}
+3
View File
@@ -29,6 +29,7 @@ random_mac() {
case "$1" in
launch)
shift 1
echo $(date -R) launching
for i in $(seq $START $FINISH); do
WEAVE_CONTAINER_NAME=weave$i WEAVE_PORT=$((5000+i)) $weavedir/weave launch-router --iface '' --name $(random_mac) --nickname weave$i --port $PORT --no-dns "$@"
done
@@ -36,6 +37,7 @@ case "$1" in
status=$(docker inspect --format='{{.State.Running}} {{.NetworkSettings.IPAddress}}' weave$((START-1)))
PREV="${status#true }"
fi
echo $(date -R) connecting
for i in $(seq $START $FINISH); do
if [ -n "$PREV" ] ; then
echo connecting weave$i to $PREV
@@ -44,6 +46,7 @@ case "$1" in
status=$(docker inspect --format='{{.State.Running}} {{.NetworkSettings.IPAddress}}' weave$i)
PREV="${status#true }"
done
echo $(date -R) done
;;
stop)
for i in $(seq $START $FINISH); do
+1 -1
View File
@@ -3,7 +3,7 @@ FROM ubuntu
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 python-requests
RUN apt-get -y update && apt-get -y install --no-install-recommends build-essential git lxc-docker-1.6.2 mercurial libpcap-dev curl make pkg-config gcc bison flex python-requests
# When doing a build in a container, "apt-get update" happens twice,
# which can be a very significant overhead for incremental builds.
+58 -8
View File
@@ -1,7 +1,12 @@
package docker
import (
"github.com/fsouza/go-dockerclient"
"errors"
"fmt"
"strings"
docker "github.com/fsouza/go-dockerclient"
. "github.com/weaveworks/weave/common"
)
@@ -17,32 +22,52 @@ type Client struct {
// NewClient creates a new Docker client and checks we can talk to Docker
func NewClient(apiPath string) (*Client, error) {
if apiPath != "" && !strings.Contains(apiPath, "://") {
apiPath = "tcp://" + apiPath
}
dc, err := docker.NewClient(apiPath)
if err != nil {
return nil, err
}
client := &Client{dc}
return client, client.checkWorking(apiPath)
return client, client.checkWorking()
}
func NewVersionedClient(apiPath string, apiVersionString string) (*Client, error) {
if !strings.Contains(apiPath, "://") {
apiPath = "tcp://" + apiPath
}
dc, err := docker.NewVersionedClient(apiPath, apiVersionString)
if err != nil {
return nil, err
}
client := &Client{dc}
return client, client.checkWorking(apiPath)
return client, client.checkWorking()
}
func (c *Client) checkWorking(apiPath string) error {
env, err := c.Version()
func NewVersionedClientFromEnv(apiVersionString string) (*Client, error) {
dc, err := docker.NewVersionedClientFromEnv(apiVersionString)
if err != nil {
return err
return nil, err
}
client := &Client{dc}
return client, client.checkWorking()
}
func (c *Client) checkWorking() error {
_, err := c.Version()
return err
}
func (c *Client) Info() string {
if env, err := c.Version(); err != nil {
return fmt.Sprintf("Docker API error: %s", err)
} else {
return fmt.Sprintf("Docker API on %s: %v", c.Endpoint(), env)
}
Log.Infof("[docker] Using Docker API on %s: %v", apiPath, env)
return nil
}
// AddObserver adds an observer for docker events
@@ -80,3 +105,28 @@ func (c *Client) IsContainerNotRunning(idStr string) bool {
Log.Errorf("[docker] Could not check container status: %s", err)
return false
}
// This is intended to find an IP address that we can reach the container on;
// if it is on the Docker bridge network then that address; if on the host network
// then localhost
func (c *Client) GetContainerIP(nameOrID string) (string, error) {
Log.Debugf("Getting IP for container %s", nameOrID)
info, err := c.InspectContainer(nameOrID)
if err != nil {
return "", err
}
if info.NetworkSettings.Networks != nil {
Log.Debugln("Networks: ", info.NetworkSettings.Networks)
if bridgeNetwork, ok := info.NetworkSettings.Networks["bridge"]; ok {
return bridgeNetwork.IPAddress, nil
} else if _, ok := info.NetworkSettings.Networks["host"]; ok {
return "127.0.0.1", nil
}
} else if info.HostConfig.NetworkMode == "host" {
return "127.0.0.1", nil
}
if info.NetworkSettings.IPAddress == "" {
return "", errors.New("No IP address found for container " + nameOrID)
}
return info.NetworkSettings.IPAddress, nil
}
+170
View File
@@ -0,0 +1,170 @@
# Overlay Method Selection
Weave automatically selects the best working overlay implementation
when connecting to a new peer. It does this by initiating both
forwarders in parallel, eventually settling on the most preferred
established forwarder at which point the other forwarder is shut down,
assuming it had not failed already. In the event that the remaining
forwarder fails at a later time (e.g. because of a heartbeat timeout)
the control plane TCP connection will recycle allowing the selection
algorithm to run again. By default fast datapath is preferred over
Sleeve.
# Local Bridging
Unlike the weave bridge netdev used by Sleeve, the OVS datapath has no
inherent bridging capability. Consequently fastdp implements an
ethernet bridge in addition to the vxlan overlay, maintaining its own
forwarding database by learning MAC addresses and dispatching
broadcast, unicast & multicast traffic accordingly.
# Short Peer IDs
The router relies on having access to the source and destination peer
when deciding how to forward packets. When the Sleeve overlay is in
use this information is conveyed directly within the encapsulation by
including the names of the peers in question, a solution not
accomodated directly by the vxlan wire format. Fortunately vxlan does
have a twenty four bit segment ID field in the header we can use to
encode this data - the challenge is to identify peers uniquely with a
twelve bit identifier instead of the seventeen bytes used by Sleeve.
In practice this is achieved by peers adopting a random 'short ID' and
resolving ownership collisions of same via the existing gossip
mechanism.
# Heartbeats via vxlan
Heartbeats are implemented by sending an encapsulated ethernet frame
with source and destination MAC addresses of `00:00:00:00:00:00` and a
payload consisting of the connection UID and total ethernet frame
length. The vxlan vport miss handler on the receiving side detects the
all-zero addresses and acknowledges the heartbeat via the TCP control
channel after validating the connection UID and frame length against
their expected values.
# PMTU Discovery
The length of the heartbeat ethernet frame is set to the MTU of the
datapath interface. In the event the vxlan packet is dropped or
truncated, the heartbeat will not be acknowledged; this lack of
acknowledgement will cause the peers to fall back to the Sleeve
overlay, which has a more sophisticated dynamic mechanism for coping
with low path MTUs.
To avoid triggering this fallback in typical deployments, the datapath
interface is statically configured with an MTU of 1410 bytes allowing
it to work with most underlay network provider MTUs, including GCE at
1460 bytes (the fifty byte difference accomodates the vxlan overhead).
This value can be overriden by setting `WEAVE_MTU` at launch if
necessary.
# Virtual Ports
There are three kinds of virtual port associated with the weave
datapath:
* `internal` - one of these is created automatically, named after the
datapath. It corresponds to the network interface of the same name
that appears on the host when the datapath is created; it is the
ingress/egress port for `weave expose`
* `netdev` - one for each application container, corresponding to the
host end of the veth pair
* `vxlan` - one for each UDP port on which we are listening for vxlan
encapsulated packets. Typically there is only one of these, but
there may be more in a network in which peers do not all use the same
port configuration
# Misses and Flow Creation
As mentioned above, the datapath has no inherent behaviour - any
ingressing packet which does not match a flow is passed to a userspace
miss handler. The miss handler is then responsible for a) instructing
the datapath to take actions for the packet that caused the miss (for
example by copying it to one or more ports) and optionally b)
installing flow rules which will allow the datapath to act on similar
packets in future without invoking the miss handler.
At a high level the fast datapath implementation can be viewed as a
set of miss handlers that determine what actions the OVS datapath
should take based on router topology information, together with some
additional machinery that manages the expiry of resulting flows when
that state changes.
When a miss handler is invoked, it has two pieces of context: a byte
array containing the packet that triggered the miss, and a set of
'flow keys' that have been extracted from the packet by the kernel
module. The following flow keys are of interest to weave:
* `InPort` - the identifier of the ingress virtual port
* `Ethernet` - the source and destination MAC addresses of the
ethernet frame
* `Tunnel` - tunnel identifier (see section on short peer IDs above),
and source/dest IPv4 addresses. Only present for ingress via a vxlan
vport
Crucially, the router must use this information alone to determine
which actions to take - this allows the specification of a flow which
matches these keys and instructs the kernel to take action
automatically in future without further userspace involvement.
Two actions are of interest:
* `Output` - output the packet to the specified vport
* `SetTunnel` - update the effective vxlan tunnel parameters. Only
required prior to output from a vxlan vport
It is possible to have multiple actions in a flow, so the router can
for example create a single rule that copies broadcast traffic to all
'local' vports (save the ingress vport obviously) as well as to vxlan
vports for onward peers as dictated by the routing topology.
Every flow specified by the router has the following characteristics:
* An `InPort` key matching the ingress vport
* A `Tunnel` key if the ingress vport is of type vxlan
* An `Ethernet` key matching the source and destination MAC
* A list of `Output` (for internal and netdev vports) and
`SetTunnel`+`Output` (for vxlan vports) actions
Under certain circumstances the miss handler will instruct the
datapath to execute a set of actions for a packet without creating a
corresponding flow:
* When broadcasting packets to discover the path to unknown
destination MACs. This condition is transient in the overwhelming
majority of cases and so the benefit of creating a flow is
outweighed by the need to invalidate it once the MAC is learned
* When the packet also needs to be forwarded to one or more peers via
Sleeve. In this case the router needs to handle all subsequent
matching packets as there is no OVS flow action to do it without
userspace involvement
# Flow Invalidation
Once a flow has been created for a particular combination of keys, the
miss handler will never again be invoked for matching packets. It is
therefore extremely important that we detect events which invalidate
existing flow actions:
* Addition of netdev vports to the datapath
* Route invalidation (topology change)
* Short peer ID collision
The response in each case is the same - delete all flows from the
datapath, allowing them to be recreated taking into account the
updated state.
In addition to these event based invalidations there is an expiry
process that executes every five minutes. This process enumerates all
flows in the datapath, removing any which have not been used since the
last check; this cleans up:
* Flows referring to netdev vports which have been removed
* Flows created by forwarders which have been stopped
* Flows related to MAC addresses which have not communicated recently
Flows keyed on tunnel IPv4 address do not need to be cleared when a
peer appears to change IP address due to NAT; this will cause a miss
resulting in a new flow, and the old flow will expire naturally via
the timer mechanism.
+5
View File
@@ -38,6 +38,11 @@ func (g *allocate) Try(alloc *Allocator) bool {
alloc.establishRing()
if ok, addr := alloc.space.Allocate(g.r); ok {
// If caller hasn't supplied a unique ID, file it under the IP address
// which lets the caller then release the address using DELETE /ip/address
if g.ident == "_" {
g.ident = addr.String()
}
alloc.debugln("Allocated", addr, "for", g.ident, "in", g.r)
alloc.addOwned(g.ident, addr)
g.resultChan <- allocateResult{addr, nil}
+2 -2
View File
@@ -478,8 +478,8 @@ type ipamGossipData struct {
alloc *Allocator
}
func (d *ipamGossipData) Merge(other mesh.GossipData) {
// no-op
func (d *ipamGossipData) Merge(other mesh.GossipData) mesh.GossipData {
return d // no-op
}
func (d *ipamGossipData) Encode() [][]byte {
+4
View File
@@ -57,6 +57,10 @@ func (alloc *Allocator) handleHTTPAllocate(dockerCli *docker.Client, w http.Resp
// HandleHTTP wires up ipams HTTP endpoints to the provided mux.
func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CIDR, dockerCli *docker.Client) {
router.Methods("GET").Path("/ipinfo/defaultsubnet").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s", defaultSubnet)
})
router.Methods("PUT").Path("/ip/{id}/{ip}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
ident := vars["id"]
-7
View File
@@ -14,7 +14,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/net/address"
wt "github.com/weaveworks/weave/testing"
)
func HTTPPost(t *testing.T, url string) string {
@@ -145,12 +144,6 @@ func TestBadHttp(t *testing.T) {
}
func TestHTTPCancel(t *testing.T) {
wt.RunWithTimeout(t, 2*time.Second, func() {
impTestHTTPCancel(t)
})
}
func impTestHTTPCancel(t *testing.T) {
var (
containerID = "deadbeef"
testCIDR1 = "10.0.3.0/29"
+19
View File
@@ -316,6 +316,25 @@ func (r *Ring) OwnedRanges() (result []address.Range) {
return r.splitRangesOverZero(result)
}
// For printing status
type RangeInfo struct {
Peer mesh.PeerName
address.Range
Version uint32
}
func (r *Ring) AllRangeInfo() (result []RangeInfo) {
for i, entry := range r.Entries {
nextEntry := r.Entries.entry(i + 1)
ranges := []address.Range{{Start: entry.Token, End: nextEntry.Token}}
ranges = r.splitRangesOverZero(ranges)
for _, r := range ranges {
result = append(result, RangeInfo{entry.Peer, r, entry.Version})
}
}
return
}
// ClaimForPeers claims the entire ring for the array of peers passed
// in. Only works for empty rings.
func (r *Ring) ClaimForPeers(peers []mesh.PeerName) {
+19 -6
View File
@@ -1,6 +1,7 @@
package ipam
import (
"fmt"
"github.com/weaveworks/weave/ipam/paxos"
"github.com/weaveworks/weave/net/address"
)
@@ -8,6 +9,7 @@ import (
type Status struct {
Paxos *paxos.Status
Range string
RangeNumIPs int
DefaultSubnet string
Entries []EntryStatus
PendingClaims []ClaimStatus
@@ -15,9 +17,12 @@ type Status struct {
}
type EntryStatus struct {
Token string
Peer string
Version uint32
Token string
Size uint32
Peer string
Nickname string
IsKnownPeer bool
Version uint32
}
type ClaimStatus struct {
@@ -40,6 +45,7 @@ func NewStatus(allocator *Allocator, defaultSubnet address.CIDR) *Status {
resultChan <- &Status{
paxosStatus,
allocator.universe.String(),
int(allocator.universe.Size()),
defaultSubnet.String(),
newEntryStatusSlice(allocator),
newClaimStatusSlice(allocator),
@@ -56,8 +62,15 @@ func newEntryStatusSlice(allocator *Allocator) []EntryStatus {
return slice
}
for _, entry := range allocator.ring.Entries {
slice = append(slice, EntryStatus{entry.Token.String(), entry.Peer.String(), entry.Version})
for _, r := range allocator.ring.AllRangeInfo() {
slice = append(slice, EntryStatus{
Token: r.Start.String(),
Size: uint32(r.Size()),
Peer: r.Peer.String(),
Nickname: allocator.nicknames[r.Peer],
IsKnownPeer: allocator.isKnownPeer(r.Peer),
Version: r.Version,
})
}
return slice
@@ -76,7 +89,7 @@ func newAllocateIdentSlice(allocator *Allocator) []string {
var slice []string
for _, op := range allocator.pendingAllocates {
allocate := op.(*allocate)
slice = append(slice, allocate.ident)
slice = append(slice, fmt.Sprintf("%s %s", allocate.ident, allocate.r.String()))
}
return slice
}
+50 -27
View File
@@ -40,16 +40,19 @@ type RemoteConnection struct {
type LocalConnection struct {
sync.RWMutex
RemoteConnection
TCPConn *net.TCPConn
version byte
tcpSender TCPSender
SessionKey *[32]byte
heartbeatTCP *time.Ticker
Router *Router
uid uint64
actionChan chan<- ConnectionAction
finished <-chan struct{} // closed to signal that actorLoop has finished
OverlayConn OverlayConnection
TCPConn *net.TCPConn
TrustRemote bool // is remote on a trusted subnet?
TrustedByRemote bool // does remote trust us?
version byte
tcpSender TCPSender
SessionKey *[32]byte
heartbeatTCP *time.Ticker
Router *Router
uid uint64
actionChan chan<- ConnectionAction
errorChan chan<- error
finished <-chan struct{} // closed to signal that actorLoop has finished
OverlayConn OverlayConnection
}
type ConnectionAction func() error
@@ -86,17 +89,19 @@ func StartLocalConnection(connRemote *RemoteConnection, tcpConn *net.TCPConn, ro
if connRemote.local != router.Ourself.Peer {
log.Fatal("Attempt to create local connection from a peer which is not ourself")
}
// NB, we're taking a copy of connRemote here.
actionChan := make(chan ConnectionAction, ChannelSize)
errorChan := make(chan error, 1)
finished := make(chan struct{})
conn := &LocalConnection{
RemoteConnection: *connRemote,
RemoteConnection: *connRemote, // NB, we're taking a copy of connRemote here.
Router: router,
TCPConn: tcpConn,
TrustRemote: router.Trusts(connRemote),
uid: randUint64(),
actionChan: actionChan,
errorChan: errorChan,
finished: finished}
go conn.run(actionChan, finished, acceptNewPeer)
go conn.run(actionChan, errorChan, finished, acceptNewPeer)
}
func (conn *LocalConnection) BreakTie(dupConn Connection) ConnectionTieBreak {
@@ -116,11 +121,6 @@ func (conn *LocalConnection) Established() bool {
return conn.established
}
// Send directly, not via the Actor. If it goes via the Actor we can
// get a deadlock where LocalConnection is blocked talking to
// LocalPeer and LocalPeer is blocked trying send a ProtocolMsg via
// LocalConnection, and the channels are full in both directions so
// nothing can proceed.
func (conn *LocalConnection) SendProtocolMsg(m ProtocolMsg) {
if err := conn.sendProtocolMsg(m); err != nil {
conn.Shutdown(err)
@@ -134,15 +134,17 @@ func (conn *LocalConnection) SendProtocolMsg(m ProtocolMsg) {
// do not need locks for reading, and only need write locks for fields
// read by other processes.
// Async
// Non-blocking.
func (conn *LocalConnection) Shutdown(err error) {
// err should always be a real error, even if only io.EOF
if err == nil {
panic("nil error")
}
// Run on its own goroutine in case the channel is backed up
go func() { conn.sendAction(func() error { return err }) }()
select {
case conn.errorChan <- err:
default:
}
}
// Send an actor request to the actorLoop, but don't block if
@@ -157,7 +159,7 @@ func (conn *LocalConnection) sendAction(action ConnectionAction) {
// ACTOR server
func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, finished chan<- struct{}, acceptNewPeer bool) {
func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, errorChan <-chan error, finished chan<- struct{}, acceptNewPeer bool) {
var err error // important to use this var and not create another one with 'err :='
defer func() { conn.shutdown(err) }()
defer close(finished)
@@ -190,13 +192,19 @@ func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, finished ch
conn.Log("connection ready; using protocol version", conn.version)
// only use negotiated session key for untrusted connections
var sessionKey *[32]byte
if conn.Untrusted() {
sessionKey = conn.SessionKey
}
params := OverlayConnectionParams{
RemotePeer: conn.remote,
LocalAddr: conn.TCPConn.LocalAddr().(*net.TCPAddr),
RemoteAddr: conn.TCPConn.RemoteAddr().(*net.TCPAddr),
Outbound: conn.outbound,
ConnUID: conn.uid,
SessionKey: conn.SessionKey,
SessionKey: sessionKey,
SendControlMessage: conn.sendOverlayControlMessage,
Features: intro.Features,
}
@@ -243,7 +251,7 @@ func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, finished ch
// running AddConnection in a separate goroutine, at least not
// without some synchronisation. Which in turn requires the
// launching of the receiveTCP goroutine to precede actorLoop.
err = conn.actorLoop(actionChan)
err = conn.actorLoop(actionChan, errorChan)
}
func (conn *LocalConnection) makeFeatures() map[string]string {
@@ -254,6 +262,7 @@ func (conn *LocalConnection) makeFeatures() map[string]string {
"ShortID": fmt.Sprint(conn.local.ShortID),
"UID": fmt.Sprint(conn.local.UID),
"ConnID": fmt.Sprint(conn.uid),
"Trusted": fmt.Sprint(conn.TrustRemote),
}
conn.Router.Overlay.AddFeaturesTo(features)
return features
@@ -295,13 +304,21 @@ func (conn *LocalConnection) parseFeatures(features features) (*Peer, error) {
var hasShortID bool
if shortIDStr, present := features["ShortID"]; present {
hasShortID = true
shortID, err = strconv.ParseUint(shortIDStr, 10,
PeerShortIDBits)
shortID, err = strconv.ParseUint(shortIDStr, 10, PeerShortIDBits)
if err != nil {
return nil, err
}
}
var trusted bool
if trustedStr, present := features["Trusted"]; present {
trusted, err = strconv.ParseBool(trustedStr)
if err != nil {
return nil, err
}
}
conn.TrustedByRemote = trusted
uid, err := ParsePeerUID(features.Get("UID"))
if err != nil {
return nil, err
@@ -335,7 +352,7 @@ func (conn *LocalConnection) registerRemote(remote *Peer, acceptNewPeer bool) er
return nil
}
func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction) (err error) {
func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction, errorChan <-chan error) (err error) {
fwdErrorChan := conn.OverlayConn.ErrorChannel()
fwdEstablishedChan := conn.OverlayConn.EstablishedChannel()
@@ -344,6 +361,8 @@ func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction) (err
case action := <-actionChan:
err = action()
case err = <-errorChan:
case <-conn.heartbeatTCP.C:
err = conn.sendSimpleProtocolMsg(ProtocolHeartbeat)
@@ -435,3 +454,7 @@ func (conn *LocalConnection) handleProtocolMsg(tag ProtocolTag, payload []byte)
func (conn *LocalConnection) extendReadDeadline() {
conn.TCPConn.SetReadDeadline(time.Now().Add(TCPHeartbeat * 2))
}
func (conn *LocalConnection) Untrusted() bool {
return !conn.TrustRemote || !conn.TrustedByRemote
}
+11 -6
View File
@@ -81,7 +81,7 @@ func (cm *ConnectionMaker) InitiateConnections(peers []string, replace bool) []e
for peer, addr := range addrs {
cm.directPeers[peer] = addr
// curtail any existing reconnect interval
if target, found := cm.targets[addr.String()]; found {
if target, found := cm.targets[cm.completeAddr(*addr)]; found {
target.nextTryNow()
}
}
@@ -159,6 +159,13 @@ func (cm *ConnectionMaker) queryLoop(actionChan <-chan ConnectionMakerAction) {
}
}
func (cm *ConnectionMaker) completeAddr(addr net.TCPAddr) string {
if addr.Port == 0 {
addr.Port = cm.port
}
return addr.String()
}
func (cm *ConnectionMaker) checkStateAndAttemptConnections() time.Duration {
var (
validTarget = make(map[string]struct{})
@@ -181,18 +188,16 @@ func (cm *ConnectionMaker) checkStateAndAttemptConnections() time.Duration {
// Add direct targets that are not connected
for _, addr := range cm.directPeers {
completeAddr := *addr
attempt := true
if completeAddr.Port == 0 {
completeAddr.Port = cm.port
if addr.Port == 0 {
// If a peer was specified w/o a port, then we do not
// attempt to connect to it if we have any inbound
// connections from that IP.
if _, connected := ourInboundIPs[completeAddr.IP.String()]; connected {
if _, connected := ourInboundIPs[addr.IP.String()]; connected {
attempt = false
}
}
address := completeAddr.String()
address := cm.completeAddr(*addr)
directTarget[address] = void
if attempt {
addTarget(address)
+3 -4
View File
@@ -6,7 +6,7 @@ import (
type GossipData interface {
Encode() [][]byte
Merge(GossipData)
Merge(GossipData) GossipData
}
type Gossip interface {
@@ -79,8 +79,7 @@ func (sender *GossipSender) Send(data GossipData) {
// NB: this must not be invoked concurrently
select {
case pending := <-sender.cell:
pending.Merge(data)
sender.cell <- pending
sender.cell <- pending.Merge(data)
default:
sender.cell <- data
}
@@ -142,7 +141,7 @@ func (router *Router) SendAllGossip() {
func (router *Router) SendAllGossipDown(conn Connection) {
for channel := range router.gossipChannelSet() {
if gossip := channel.gossiper.Gossip(); gossip != nil {
channel.SendDown(conn, channel.gossiper.Gossip())
channel.SendDown(conn, gossip)
}
}
}
+66 -72
View File
@@ -97,78 +97,6 @@ func (c *GossipChannel) deliver(srcName PeerName, _ []byte, dec *gob.Decoder) er
return nil
}
func (c *GossipChannel) Send(srcName PeerName, data GossipData) {
// do this outside the lock below so we avoid lock nesting
c.routes.EnsureRecalculated()
selectedConnections := make(ConnectionSet)
for name := range c.routes.RandomNeighbours(srcName) {
if conn, found := c.ourself.ConnectionTo(name); found {
selectedConnections[conn] = void
}
}
if len(selectedConnections) == 0 {
return
}
connections := c.ourself.Connections()
c.Lock()
defer c.Unlock()
c.gcSenders(connections)
for conn := range selectedConnections {
c.sendDown(conn, data)
}
}
func (c *GossipChannel) SendDown(conn Connection, data GossipData) {
connections := c.ourself.Connections()
c.Lock()
defer c.Unlock()
c.gcSenders(connections)
c.sendDown(conn, data)
}
// GC - randomly (courtesy of go's map iterator) pick some existing
// senders and stop&remove them if the associated connection is no
// longer active. We stop as soon as we encounter a valid entry; the
// idea being that when there is little or no garbage then this
// executes close to O(1)[1], whereas when there is lots of garbage we
// remove it quickly.
//
// [1] TODO Unfortunately, due to the desire to avoid nested locks,
// instead of simply invoking LocalPeer.ConnectionTo(name), we pass in
// the result of LocalPeer.Connections(). That is O(n_our_connections)
// at best.
func (c *GossipChannel) gcSenders(connections ConnectionSet) {
for conn, sender := range c.senders {
if _, found := connections[conn]; !found {
delete(c.senders, conn)
sender.Stop()
} else {
break
}
}
}
// We have seen a couple of failures which suggest a >128GB slice was encountered.
// 100MB should be enough for anyone.
const maxFeasibleMessageLen = 100 * 1024 * 1024
func (c *GossipChannel) sendDown(conn Connection, data GossipData) {
sender, found := c.senders[conn]
if !found {
sender = NewGossipSender(func(pending GossipData) {
for _, msg := range pending.Encode() {
if len(msg) > maxFeasibleMessageLen {
panic(fmt.Sprintf("Gossip message too large: len=%d bytes; on channel '%s' from %+v", len(msg), c.name, pending))
}
protocolMsg := ProtocolMsg{ProtocolGossip, GobEncode(c.name, c.ourself.Name, msg)}
conn.(ProtocolSender).SendProtocolMsg(protocolMsg)
}
})
c.senders[conn] = sender
}
sender.Send(data)
}
func (c *GossipChannel) GossipUnicast(dstPeerName PeerName, msg []byte) error {
return c.relayUnicast(dstPeerName, GobEncode(c.name, c.ourself.Name, dstPeerName, msg))
}
@@ -236,6 +164,72 @@ func (c *GossipChannel) sendBroadcast(srcName PeerName, update GossipData) {
}
}
func (c *GossipChannel) Send(srcName PeerName, data GossipData) {
c.routes.EnsureRecalculated()
destinations := c.routes.RandomNeighbours(srcName)
if len(destinations) == 0 {
return
}
c.sendDown(c.ourself.ConnectionsTo(destinations), data)
}
func (c *GossipChannel) SendDown(conn Connection, data GossipData) {
c.sendDown([]Connection{conn}, data)
}
func (c *GossipChannel) sendDown(selectedConnections []Connection, data GossipData) {
if len(selectedConnections) == 0 {
return
}
connections := c.ourself.Connections()
c.Lock()
defer c.Unlock()
// GC - randomly (courtesy of go's map iterator) pick some
// existing senders and stop&remove them if the associated
// connection is no longer active. We stop as soon as we
// encounter a valid entry; the idea being that when there is
// little or no garbage then this executes close to O(1)[1],
// whereas when there is lots of garbage we remove it quickly.
//
// [1] TODO Unfortunately, due to the desire to avoid nested
// locks, instead of simply invoking LocalPeer.ConnectionTo(name),
// we operate on LocalPeer.Connections(). That is
// O(n_our_connections) at best.
for conn, sender := range c.senders {
if _, found := connections[conn]; !found {
delete(c.senders, conn)
sender.Stop()
} else {
break
}
}
// start senders, if necessary, and send.
for _, conn := range selectedConnections {
sender, found := c.senders[conn]
if !found {
sender = c.makeSender(conn)
c.senders[conn] = sender
}
sender.Send(data)
}
}
// We have seen a couple of failures which suggest a >128GB slice was encountered.
// 100MB should be enough for anyone.
const maxFeasibleMessageLen = 100 * 1024 * 1024
func (c *GossipChannel) makeSender(conn Connection) *GossipSender {
return NewGossipSender(func(pending GossipData) {
for _, msg := range pending.Encode() {
if len(msg) > maxFeasibleMessageLen {
panic(fmt.Sprintf("Gossip message too large: len=%d bytes; on channel '%s' from %+v", len(msg), c.name, pending))
}
protocolMsg := ProtocolMsg{ProtocolGossip, GobEncode(c.name, c.ourself.Name, msg)}
conn.(ProtocolSender).SendProtocolMsg(protocolMsg)
}
})
}
func (c *GossipChannel) log(args ...interface{}) {
log.Println(append(append([]interface{}{}, "[gossip "+c.name+"]:"), args...)...)
}
+50 -79
View File
@@ -4,18 +4,17 @@ import (
"fmt"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
wt "github.com/weaveworks/weave/testing"
)
// TODO test gossip unicast; atm we only test topology gossip and
// surrogates, neither of which employ unicast.
type mockChannelConnection struct {
type mockGossipConnection struct {
RemoteConnection
dest *Router
dest *Router
start chan struct{}
}
func NewTestRouter(name string) *Router {
@@ -25,12 +24,17 @@ func NewTestRouter(name string) *Router {
return router
}
func (conn *mockChannelConnection) SendProtocolMsg(protocolMsg ProtocolMsg) {
func (conn *mockGossipConnection) SendProtocolMsg(protocolMsg ProtocolMsg) {
<-conn.start
if err := conn.dest.handleGossip(protocolMsg.tag, protocolMsg.msg); err != nil {
panic(err)
}
}
func (conn *mockGossipConnection) Start() {
close(conn.start)
}
func sendPendingGossip(routers ...*Router) {
// Loop until all routers report they didn't send anything
for sentSomething := true; sentSomething; {
@@ -41,38 +45,32 @@ func sendPendingGossip(routers ...*Router) {
}
}
func (router *Router) AddTestChannelConnection(r *Router) {
fromPeer := NewPeerFrom(router.Ourself.Peer)
toPeer := NewPeerFrom(r.Ourself.Peer)
func AddTestGossipConnection(r1, r2 *Router) {
c1 := r1.NewTestGossipConnection(r2)
c2 := r2.NewTestGossipConnection(r1)
c1.Start()
c2.Start()
}
r.Peers.FetchWithDefault(fromPeer) // Has side-effect of incrementing refcount
router.Peers.FetchWithDefault(toPeer) //
func (router *Router) NewTestGossipConnection(r *Router) *mockGossipConnection {
to := r.Ourself.Peer
toPeer := NewPeer(to.Name, to.NickName, to.UID, 0, to.ShortID)
toPeer = router.Peers.FetchWithDefault(toPeer) // Has side-effect of incrementing refcount
conn := &mockChannelConnection{RemoteConnection{router.Ourself.Peer, toPeer, "", false, true}, r}
conn := &mockGossipConnection{
RemoteConnection{router.Ourself.Peer, toPeer, "", false, true}, r, make(chan struct{})}
router.Ourself.handleAddConnection(conn)
router.Ourself.handleConnectionEstablished(conn)
return conn
}
func (router *Router) DeleteTestChannelConnection(r *Router) {
fromName := router.Ourself.Peer.Name
func (router *Router) DeleteTestGossipConnection(r *Router) {
toName := r.Ourself.Peer.Name
fromPeer := r.Peers.Fetch(fromName)
toPeer := router.Peers.Fetch(toName)
r.Peers.Dereference(fromPeer)
router.Peers.Dereference(toPeer)
conn, _ := router.Ourself.ConnectionTo(toName)
router.Peers.Dereference(conn.Remote())
router.Ourself.handleDeleteConnection(conn)
}
func TestGossipTopology(t *testing.T) {
wt.RunWithTimeout(t, 5*time.Second, func() {
implTestGossipTopology(t)
})
}
// Create a Peer representing the receiver router, with connections to
// the routers supplied as arguments, carrying across all UID and
// version information.
@@ -95,69 +93,46 @@ func checkTopology(t *testing.T, router *Router, wantedPeers ...*Peer) {
router.Peers.RUnlock()
}
func implTestGossipTopology(t *testing.T) {
func flushAndCheckTopology(t *testing.T, routers []*Router, wantedPeers ...*Peer) {
sendPendingGossip(routers...)
for _, r := range routers {
checkTopology(t, r, wantedPeers...)
}
}
func TestGossipTopology(t *testing.T) {
// Create some peers that will talk to each other
r1 := NewTestRouter("01:00:00:01:00:00")
r2 := NewTestRouter("02:00:00:02:00:00")
r3 := NewTestRouter("03:00:00:03:00:00")
routers := []*Router{r1, r2, r3}
// Check state when they have no connections
checkTopology(t, r1, r1.tp())
checkTopology(t, r2, r2.tp())
// Now try adding some connections
r1.AddTestChannelConnection(r2)
sendPendingGossip(r1, r2)
checkTopology(t, r1, r1.tp(r2), r2.tp())
checkTopology(t, r2, r1.tp(r2), r2.tp())
r2.AddTestChannelConnection(r1)
AddTestGossipConnection(r1, r2)
sendPendingGossip(r1, r2)
checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
checkTopology(t, r2, r1.tp(r2), r2.tp(r1))
// Currently, the connection from 2 to 3 is one-way only
r2.AddTestChannelConnection(r3)
sendPendingGossip(r1, r2, r3)
checkTopology(t, r1, r1.tp(r2), r2.tp(r1, r3), r3.tp())
checkTopology(t, r2, r1.tp(r2), r2.tp(r1, r3), r3.tp())
// When r2 gossiped to r3, 1 was unreachable from r3 so it got removed from the
// list of peers, but remains referenced in the connection from 1 to 3.
checkTopology(t, r3, r2.tp(r1, r3), r3.tp())
AddTestGossipConnection(r2, r3)
flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))
// Add a connection from 3 to 1 and now r1 is reachable.
r3.AddTestChannelConnection(r1)
sendPendingGossip(r1, r2, r3)
checkTopology(t, r1, r1.tp(r2), r2.tp(r1, r3), r3.tp(r1))
checkTopology(t, r2, r1.tp(r2), r2.tp(r1, r3), r3.tp(r1))
checkTopology(t, r3, r1.tp(), r2.tp(r1, r3), r3.tp(r1))
r1.AddTestChannelConnection(r3)
sendPendingGossip(r1, r2, r3)
checkTopology(t, r1, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1))
checkTopology(t, r2, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1))
checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1))
AddTestGossipConnection(r3, r1)
flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1, r2))
// Drop the connection from 2 to 3
r2.DeleteTestChannelConnection(r3)
sendPendingGossip(r1, r2, r3)
checkTopology(t, r1, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1))
checkTopology(t, r2, r1.tp(r2, r3), r2.tp(r1))
checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1))
r2.DeleteTestGossipConnection(r3)
flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))
// Drop the connection from 1 to 3
r1.DeleteTestChannelConnection(r3)
r1.DeleteTestGossipConnection(r3)
sendPendingGossip(r1, r2, r3)
checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1))
checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1))
checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
checkTopology(t, r2, r1.tp(r2), r2.tp(r1))
// r3 still thinks r1 has a connection to it
checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1))
// On a timer, r3 will gossip to r1
r3.SendAllGossip()
sendPendingGossip(r1, r2, r3)
checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1))
checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))
}
func TestGossipSurrogate(t *testing.T) {
@@ -165,14 +140,10 @@ func TestGossipSurrogate(t *testing.T) {
r1 := NewTestRouter("01:00:00:01:00:00")
r2 := NewTestRouter("02:00:00:02:00:00")
r3 := NewTestRouter("03:00:00:03:00:00")
r1.AddTestChannelConnection(r2)
r2.AddTestChannelConnection(r1)
r3.AddTestChannelConnection(r2)
r2.AddTestChannelConnection(r3)
sendPendingGossip(r1, r2, r3)
checkTopology(t, r1, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))
checkTopology(t, r2, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))
checkTopology(t, r3, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))
routers := []*Router{r1, r2, r3}
AddTestGossipConnection(r1, r2)
AddTestGossipConnection(r3, r2)
flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))
// create a gossiper at either end, but not the middle
g1 := newTestGossiper()
@@ -238,10 +209,10 @@ func (g *testGossiper) OnGossip(update []byte) (GossipData, error) {
g.state[v] = void
}
}
if len(delta) > 0 {
return NewSurrogateGossipData(delta), nil
if len(delta) == 0 {
return nil, nil
}
return nil, nil
return NewSurrogateGossipData(delta), nil
}
func (g *testGossiper) checkHas(t *testing.T, vs ...byte) {
+1 -1
View File
@@ -17,7 +17,7 @@ func (peers *Peers) AddTestConnection(p *Peer) {
summary := p.PeerSummary
summary.Version = 0
toPeer := NewPeerFromSummary(summary)
peers.FetchWithDefault(toPeer) // Has side-effect of incrementing refcount
toPeer = peers.FetchWithDefault(toPeer) // Has side-effect of incrementing refcount
conn := newMockConnection(peers.ourself.Peer, toPeer)
peers.ourself.addConnection(conn)
peers.ourself.connectionEstablished(conn)
+10 -1
View File
@@ -26,7 +26,12 @@ func randUint16() (r uint16) {
type PeerUID uint64
func randomPeerUID() PeerUID {
return PeerUID(randUint64())
for {
uid := randUint64()
if uid != 0 { // uid 0 is reserved for peer placeholder
return PeerUID(uid)
}
}
}
func ParsePeerUID(s string) (PeerUID, error) {
@@ -95,6 +100,10 @@ func NewPeer(name PeerName, nickName string, uid PeerUID, version uint64, shortI
})
}
func NewPeerPlaceholder(name PeerName) *Peer {
return NewPeerFromSummary(PeerSummary{NameByte: name.Bin()})
}
func NewPeerFrom(peer *Peer) *Peer {
return NewPeerFromSummary(peer.PeerSummary)
}
+10 -18
View File
@@ -3,7 +3,6 @@ package mesh
import (
"bytes"
"encoding/gob"
"fmt"
"io"
"math/rand"
"sync"
@@ -32,14 +31,6 @@ type ShortIDPeers struct {
others []*Peer
}
type UnknownPeerError struct {
Name PeerName
}
func (upe UnknownPeerError) Error() string {
return fmt.Sprint("Reference to unknown peer ", upe.Name)
}
type PeerNameSet map[PeerName]struct{}
type ConnectionSummary struct {
@@ -334,9 +325,7 @@ func (peers *Peers) ApplyUpdate(update []byte) (PeerNameSet, PeerNameSet, error)
return nil, nil, err
}
// By this point, we know the update doesn't refer to any peers we
// have no knowledge of. We can now apply the update. Start by
// adding in any new peers.
// Add new peers
for name, newPeer := range newPeers {
peers.byName[name] = newPeer
peers.addByShortID(newPeer, &pending)
@@ -446,10 +435,8 @@ func (peers *Peers) decodeUpdate(update []byte) (newPeers map[PeerName]*Peer, de
if _, found := peers.byName[remoteName]; found {
continue
}
// Update refers to a peer which we have no knowledge
// of. Thus we can't apply the update. Abort.
err = UnknownPeerError{remoteName}
return
// Update refers to a peer which we have no knowledge of.
newPeers[remoteName] = NewPeerPlaceholder(remoteName)
}
}
return
@@ -476,16 +463,21 @@ func (peers *Peers) applyUpdate(decodedUpdate []*Peer, decodedConns [][]Connecti
newUpdate[name] = void
default: // existing peer
if newPeer.Version < peer.Version ||
(newPeer.Version == peer.Version && newPeer.UID <= peer.UID) {
(newPeer.Version == peer.Version &&
(newPeer.UID < peer.UID ||
(newPeer.UID == peer.UID &&
(!newPeer.HasShortID || peer.HasShortID)))) {
continue
}
peer.Version = newPeer.Version
peer.UID = newPeer.UID
peer.NickName = newPeer.NickName
peer.connections = makeConnsMap(peer, connSummaries, peers.byName)
if newPeer.ShortID != peer.ShortID {
if newPeer.ShortID != peer.ShortID || newPeer.HasShortID != peer.HasShortID {
peers.deleteByShortID(peer, pending)
peer.ShortID = newPeer.ShortID
peer.HasShortID = newPeer.HasShortID
peers.addByShortID(peer, pending)
}
newUpdate[name] = void
+32 -23
View File
@@ -37,6 +37,7 @@ type Config struct {
Password []byte
ConnLimit int
PeerDiscovery bool
TrustedSubnets []*net.IPNet
}
type Router struct {
@@ -122,26 +123,28 @@ type TopologyGossipData struct {
update PeerNameSet
}
func (d *TopologyGossipData) Merge(other GossipData) GossipData {
names := make(PeerNameSet)
for name := range d.update {
names[name] = void
}
for name := range other.(*TopologyGossipData).update {
names[name] = void
}
return &TopologyGossipData{peers: d.peers, update: names}
}
func (d *TopologyGossipData) Encode() [][]byte {
return [][]byte{d.peers.EncodePeers(d.update)}
}
func (router *Router) BroadcastTopologyUpdate(update []*Peer) {
names := make(PeerNameSet)
for _, p := range update {
names[p.Name] = void
}
router.TopologyGossip.GossipBroadcast(&TopologyGossipData{
peers: router.Peers,
update: names,
})
}
func (d *TopologyGossipData) Merge(other GossipData) {
for name := range other.(*TopologyGossipData).update {
d.update[name] = void
}
}
func (d *TopologyGossipData) Encode() [][]byte {
return [][]byte{d.peers.EncodePeers(d.update)}
router.TopologyGossip.GossipBroadcast(
&TopologyGossipData{peers: router.Peers, update: names})
}
func (router *Router) OnGossipUnicast(sender PeerName, msg []byte) error {
@@ -170,14 +173,6 @@ func (router *Router) OnGossip(update []byte) (GossipData, error) {
func (router *Router) applyTopologyUpdate(update []byte) (PeerNameSet, PeerNameSet, error) {
origUpdate, newUpdate, err := router.Peers.ApplyUpdate(update)
if _, ok := err.(UnknownPeerError); err != nil && ok {
// That update contained a reference to a peer which wasn't
// 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)
return nil, nil, nil
}
if err != nil {
return nil, nil, err
}
@@ -187,3 +182,17 @@ func (router *Router) applyTopologyUpdate(update []byte) (PeerNameSet, PeerNameS
}
return origUpdate, newUpdate, nil
}
func (router *Router) Trusts(remote *RemoteConnection) bool {
if tcpAddr, err := net.ResolveTCPAddr("tcp4", remote.remoteTCPAddr); err == nil {
for _, trustedSubnet := range router.TrustedSubnets {
if trustedSubnet.Contains(tcpAddr.IP) {
return true
}
}
} else {
// Should not happen as remoteTCPAddr was obtained from TCPConn
log.Errorf("Unable to parse remote TCP addr: %s", err)
}
return false
}
+8 -4
View File
@@ -92,20 +92,24 @@ func (routes *Routes) BroadcastAll(name PeerName) []PeerName {
// returns a higher proportion of neighbours than elsewhere. In
// extremis, on peers with fewer than log2(n_peers) neighbours, all
// neighbours are returned.
func (routes *Routes) RandomNeighbours(except PeerName) PeerNameSet {
res := make(PeerNameSet)
func (routes *Routes) RandomNeighbours(except PeerName) []PeerName {
destinations := make(PeerNameSet)
routes.RLock()
defer routes.RUnlock()
count := int(math.Log2(float64(len(routes.unicastAll))))
// depends on go's random map iteration
for _, dst := range routes.unicastAll {
if dst != UnknownPeerName && dst != except {
res[dst] = void
if len(res) >= count {
destinations[dst] = void
if len(destinations) >= count {
break
}
}
}
res := make([]PeerName, 0, len(destinations))
for dst := range destinations {
res = append(res, dst)
}
return res
}
+19 -1
View File
@@ -2,6 +2,7 @@ package mesh
import (
"fmt"
"net"
)
type Status struct {
@@ -19,6 +20,7 @@ type Status struct {
Connections []LocalConnectionStatus
Targets []string
OverlayDiagnostics interface{}
TrustedSubnets []string
}
type PeerStatus struct {
@@ -69,7 +71,8 @@ func NewStatus(router *Router) *Status {
NewBroadcastRouteStatusSlice(router.Routes),
NewLocalConnectionStatusSlice(router.ConnectionMaker),
NewTargetSlice(router.ConnectionMaker),
router.Overlay.Diagnostics()}
router.Overlay.Diagnostics(),
NewTrustedSubnetsSlice(router.TrustedSubnets)}
}
func NewPeerStatusSlice(peers *Peers) []PeerStatus {
@@ -147,6 +150,13 @@ func NewLocalConnectionStatusSlice(cm *ConnectionMaker) []LocalConnectionStatus
}
lc, _ := conn.(*LocalConnection)
info := fmt.Sprintf("%-6v %v", lc.OverlayConn.DisplayName(), conn.Remote())
if lc.Router.UsingPassword() {
if lc.Untrusted() {
info = fmt.Sprintf("%-11v %v", "encrypted", info)
} else {
info = fmt.Sprintf("%-11v %v", "unencrypted", info)
}
}
slice = append(slice, LocalConnectionStatus{conn.RemoteTCPAddr(), conn.Outbound(), state, info})
}
for address, target := range cm.targets {
@@ -191,3 +201,11 @@ func NewTargetSlice(cm *ConnectionMaker) []string {
}
return <-resultChan
}
func NewTrustedSubnetsSlice(trustedSubnets []*net.IPNet) []string {
trustedSubnetStrs := []string{}
for _, trustedSubnet := range trustedSubnets {
trustedSubnetStrs = append(trustedSubnetStrs, trustedSubnet.String())
}
return trustedSubnetStrs
}
+6 -2
View File
@@ -12,8 +12,12 @@ func (d *SurrogateGossipData) Encode() [][]byte {
return d.messages
}
func (d *SurrogateGossipData) Merge(other GossipData) {
d.messages = append(d.messages, other.(*SurrogateGossipData).messages...)
func (d *SurrogateGossipData) Merge(other GossipData) GossipData {
o := other.(*SurrogateGossipData)
messages := make([][]byte, 0, len(d.messages)+len(o.messages))
messages = append(messages, d.messages...)
messages = append(messages, o.messages...)
return &SurrogateGossipData{messages: messages}
}
// SurrogateGossiper ignores unicasts and relays broadcasts and gossips.
+13 -7
View File
@@ -245,12 +245,14 @@ type GossipData struct {
Entries
}
func (g *GossipData) Merge(o mesh.GossipData) {
func (g *GossipData) Merge(o mesh.GossipData) mesh.GossipData {
other := o.(*GossipData)
g.Entries.merge(other.Entries)
if g.Timestamp < other.Timestamp {
g.Timestamp = other.Timestamp
gossip := g.copy()
gossip.Entries.merge(other.Entries)
if gossip.Timestamp < other.Timestamp {
gossip.Timestamp = other.Timestamp
}
return gossip
}
func (g *GossipData) Decode(msg []byte) error {
@@ -264,9 +266,7 @@ func (g *GossipData) Decode(msg []byte) error {
}
func (g *GossipData) Encode() [][]byte {
// Make a copy so we can sort: all outgoing data is sent in case-sensitive order
g2 := GossipData{Timestamp: g.Timestamp, Entries: make(Entries, len(g.Entries))}
copy(g2.Entries, g.Entries)
g2 := g.copy()
sort.Sort(CaseSensitive(g2.Entries))
buf := &bytes.Buffer{}
if err := gob.NewEncoder(buf).Encode(g2); err != nil {
@@ -274,3 +274,9 @@ func (g *GossipData) Encode() [][]byte {
}
return [][]byte{buf.Bytes()}
}
func (g *GossipData) copy() *GossipData {
g2 := &GossipData{Timestamp: g.Timestamp, Entries: make(Entries, len(g.Entries))}
copy(g2.Entries, g.Entries)
return g2
}
+2 -2
View File
@@ -117,7 +117,7 @@ func TestGossipDataMerge(t *testing.T) {
g1 := GossipData{Entries: makeEntries("AcDf")}
g2 := GossipData{Entries: makeEntries("BEf")}
g1.Merge(&g2)
g3 := g1.Merge(&g2).(*GossipData)
require.Equal(t, GossipData{Entries: makeEntries("ABcDEf")}, g1)
require.Equal(t, GossipData{Entries: makeEntries("ABcDEf")}, *g3)
}
+8 -10
View File
@@ -74,13 +74,13 @@ func (n *Nameserver) Stop() {
}
func (n *Nameserver) broadcastEntries(es ...Entry) error {
if n.gossip != nil {
return n.gossip.GossipBroadcast(&GossipData{
Entries: Entries(es),
Timestamp: now(),
})
if n.gossip == nil || len(es) == 0 {
return nil
}
return nil
return n.gossip.GossipBroadcast(&GossipData{
Entries: Entries(es),
Timestamp: now(),
})
}
func (n *Nameserver) AddEntry(hostname, containerid string, origin mesh.PeerName, addr address.Address) error {
@@ -133,10 +133,8 @@ func (n *Nameserver) ContainerDied(ident string) {
return false
})
n.Unlock()
if len(entries) > 0 {
if err := n.broadcastEntries(entries...); err != nil {
n.errorf("failed to broadcast container %s death: %v", ident, err)
}
if err := n.broadcastEntries(entries...); err != nil {
n.errorf("failed to broadcast container %s death: %v", ident, err)
}
}
-7
View File
@@ -13,7 +13,6 @@ import (
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
wt "github.com/weaveworks/weave/testing"
"github.com/weaveworks/weave/testing/gossip"
)
@@ -76,12 +75,6 @@ func (m mapping) Addrs() []address.Address {
}
func TestNameservers(t *testing.T) {
wt.RunWithTimeout(t, 2*time.Minute, func() {
testNameservers(t)
})
}
func testNameservers(t *testing.T) {
//common.SetLogLevel("debug")
lookupTimeout := 10 // ms
+6 -4
View File
@@ -1,10 +1,11 @@
package nameserver
type Status struct {
Domain string
Address string
TTL uint32
Entries []EntryStatus
Domain string
Upstream []string
Address string
TTL uint32
Entries []EntryStatus
}
type EntryStatus struct {
@@ -37,6 +38,7 @@ func NewStatus(ns *Nameserver, dnsServer *DNSServer) *Status {
return &Status{
dnsServer.domain,
dnsServer.upstream.Servers,
dnsServer.address,
dnsServer.ttl,
entryStatusSlice}
+28 -54
View File
@@ -5,28 +5,33 @@ import (
"net"
"syscall"
"github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netlink"
)
// Wait for an interface to come up.
func EnsureInterface(ifaceName string) (*net.Interface, error) {
s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK)
if err != nil {
return nil, err
}
defer s.Close()
iface, err := ensureInterface(s, ifaceName)
iface, err := ensureInterface(ifaceName)
if err != nil {
return nil, err
}
return iface, err
}
func ensureInterface(s *nl.NetlinkSocket, ifaceName string) (*net.Interface, error) {
func ensureInterface(ifaceName string) (*net.Interface, error) {
if iface, err := findInterface(ifaceName); err == nil {
return iface, nil
}
netlinkReceiveUntil(s, matchIfName(ifaceName))
ch := make(chan netlink.LinkUpdate)
done := make(chan struct{})
defer close(done)
if err := netlink.LinkSubscribe(ch, done); err != nil {
return nil, err
}
for update := range ch {
if ifaceName == update.Link.Attrs().Name && update.IfInfomsg.Flags&syscall.IFF_UP != 0 {
break
}
}
iface, err := findInterface(ifaceName)
return iface, err
}
@@ -35,12 +40,7 @@ func ensureInterface(s *nl.NetlinkSocket, ifaceName string) (*net.Interface, err
// This matches the behaviour in 'weave attach', which is the only context in which
// we expect this to be called. If you change one, change the other to match.
func EnsureInterfaceAndMcastRoute(ifaceName string) (*net.Interface, error) {
s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK, syscall.RTNLGRP_IPV4_ROUTE)
if err != nil {
return nil, err
}
defer s.Close()
iface, err := ensureInterface(s, ifaceName)
iface, err := ensureInterface(ifaceName)
if err != nil {
return nil, err
}
@@ -48,8 +48,19 @@ func EnsureInterfaceAndMcastRoute(ifaceName string) (*net.Interface, error) {
if CheckRouteExists(ifaceName, dest) {
return iface, err
}
netlinkReceiveUntil(s, matchRoute(ifaceName, dest))
return iface, err
ch := make(chan netlink.RouteUpdate)
done := make(chan struct{})
defer close(done)
if err := netlink.RouteSubscribe(ch, done); err != nil {
return nil, err
}
for update := range ch {
link, _ := netlink.LinkByIndex(update.Route.LinkIndex)
if link.Attrs().Name == ifaceName && update.Route.Dst.IP.Equal(dest) {
break
}
}
return iface, nil
}
func findInterface(ifaceName string) (iface *net.Interface, err error) {
@@ -61,40 +72,3 @@ func findInterface(ifaceName string) (iface *net.Interface, err error) {
}
return
}
func netlinkReceiveUntil(s *nl.NetlinkSocket, f func(syscall.NetlinkMessage) (bool, error)) error {
for {
msgs, err := s.Receive()
if err != nil {
return err
}
for _, m := range msgs {
if done, err := f(m); done {
return err
}
}
}
}
func matchIfName(ifaceName string) func(m syscall.NetlinkMessage) (bool, error) {
return func(m syscall.NetlinkMessage) (bool, error) {
switch m.Header.Type {
case syscall.RTM_NEWLINK: // receive this type for link 'up'
ifmsg := nl.DeserializeIfInfomsg(m.Data)
attrs, err := syscall.ParseNetlinkRouteAttr(&m)
if err != nil {
return true, err
}
name := ""
for _, attr := range attrs {
if attr.Attr.Type == syscall.IFA_LABEL {
name = string(attr.Value[:len(attr.Value)-1])
}
}
if ifaceName == name && ifmsg.Flags&syscall.IFF_UP != 0 {
return true, nil
}
}
return false, nil
}
}
-50
View File
@@ -3,10 +3,8 @@ package net
import (
"fmt"
"net"
"syscall"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netlink/nl"
)
// A network is considered free if it does not overlap any existing
@@ -65,51 +63,3 @@ func CheckRouteExists(ifaceName string, dest net.IP) bool {
})
return found
}
func matchRoute(ifaceName string, dest net.IP) func(m syscall.NetlinkMessage) (bool, error) {
return func(m syscall.NetlinkMessage) (bool, error) {
switch m.Header.Type {
case syscall.RTM_NEWROUTE:
route, err := deserializeRoute(m.Data)
if err != nil {
return true, err
}
link, _ := netlink.LinkByIndex(route.LinkIndex)
if link.Attrs().Name == ifaceName && route.Dst.IP.Equal(dest) {
return true, nil
}
}
return false, nil
}
}
// This code proposed for addition to the vishvananda/netlink library
// deserializeRoute decodes a binary netlink message into a Route struct
func deserializeRoute(m []byte) (netlink.Route, error) {
route := netlink.Route{}
msg := nl.DeserializeRtMsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {
return route, err
}
route.Scope = netlink.Scope(msg.Scope)
native := nl.NativeEndian()
for _, attr := range attrs {
switch attr.Attr.Type {
case syscall.RTA_GATEWAY:
route.Gw = net.IP(attr.Value)
case syscall.RTA_PREFSRC:
route.Src = net.IP(attr.Value)
case syscall.RTA_DST:
route.Dst = &net.IPNet{
IP: attr.Value,
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
}
case syscall.RTA_OIF:
routeIndex := int(native.Uint32(attr.Value[0:4]))
route.LinkIndex = routeIndex
}
}
return route, nil
}
+9
View File
@@ -0,0 +1,9 @@
# Weave network driver extension for Docker
A [plugin](http://docs.docker.com/engine/extend/plugin_api/) to
integrate [weave Net](http://weave.works/net/) with Docker.
The plugin runs automatically when you `weave launch`, provided your
Docker daemon is version 1.9 or newer.
More detail [here](http://docs.weave.works/weave/latest_release/plugin.html).
+58
View File
@@ -0,0 +1,58 @@
package ipamplugin
import (
"net"
"github.com/docker/libnetwork/ipamapi"
"github.com/docker/libnetwork/netlabel"
"github.com/weaveworks/weave/api"
. "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/common/docker"
)
const (
WeaveContainer = "weave"
)
type ipam struct {
client *docker.Client
weave *api.Client
}
func NewIpam(client *docker.Client, version string) (ipamapi.Ipam, error) {
resolver := func() (string, error) { return client.GetContainerIP(WeaveContainer) }
return &ipam{client: client, weave: api.NewClientWithResolver(resolver)}, nil
}
func (i *ipam) GetDefaultAddressSpaces() (string, string, error) {
Log.Debugln("GetDefaultAddressSpaces")
return "weavelocal", "weaveglobal", nil
}
func (i *ipam) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
Log.Debugln("RequestPool", addressSpace, pool, subPool, options)
cidr, err := i.weave.DefaultSubnet()
Log.Debugln("RequestPool returning ", cidr, err)
// Pass back a fake "gateway address"; we don't actually use it,
// so just give the network address.
data := map[string]string{netlabel.Gateway: cidr.String()}
return "weavepool", cidr, data, err
}
func (i *ipam) ReleasePool(poolID string) error {
Log.Debugln("ReleasePool", poolID)
return nil
}
func (i *ipam) RequestAddress(poolID string, address net.IP, options map[string]string) (*net.IPNet, map[string]string, error) {
Log.Debugln("RequestAddress", poolID, address, options)
// Pass magic string to weave IPAM, which then stores the address under its own string
ip, err := i.weave.AllocateIP("_")
Log.Debugln("allocateIP returned", ip, err)
return ip, nil, err
}
func (i *ipam) ReleaseAddress(poolID string, address net.IP) error {
Log.Debugln("ReleaseAddress", poolID, address)
return i.weave.ReleaseIP(address.String())
}
+206
View File
@@ -0,0 +1,206 @@
package plugin
import (
"fmt"
"sync"
"github.com/docker/libnetwork/drivers/remote/api"
"github.com/docker/libnetwork/types"
. "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/common/docker"
"github.com/weaveworks/weave/common/odp"
"github.com/weaveworks/weave/plugin/skel"
"github.com/vishvananda/netlink"
)
const (
WeaveContainer = "weave"
WeaveBridge = "weave"
)
type driver struct {
version string
nameserver string
scope string
noMulticastRoute bool
sync.RWMutex
endpoints map[string]struct{}
}
func New(client *docker.Client, version string, nameserver string, scope string, noMulticastRoute bool) (skel.Driver, error) {
driver := &driver{
nameserver: nameserver,
noMulticastRoute: noMulticastRoute,
version: version,
scope: scope,
endpoints: make(map[string]struct{}),
}
_, err := NewWatcher(client, driver)
if err != nil {
return nil, err
}
return driver, nil
}
func errorf(format string, a ...interface{}) error {
Log.Errorf(format, a...)
return fmt.Errorf(format, a...)
}
// === protocol handlers
func (driver *driver) GetCapabilities() (*api.GetCapabilityResponse, error) {
var caps = &api.GetCapabilityResponse{
Scope: driver.scope,
}
Log.Debugf("Get capabilities: responded with %+v", caps)
return caps, nil
}
func (driver *driver) CreateNetwork(create *api.CreateNetworkRequest) error {
Log.Debugf("Create network request %+v", create)
Log.Infof("Create network %s", create.NetworkID)
return nil
}
func (driver *driver) DeleteNetwork(delete *api.DeleteNetworkRequest) error {
Log.Debugf("Delete network request: %+v", delete)
Log.Infof("Destroy network %s", delete.NetworkID)
return nil
}
func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.CreateEndpointResponse, error) {
Log.Debugf("Create endpoint request %+v", create)
endID := create.EndpointID
if create.Interface == nil {
return nil, fmt.Errorf("Not supported: creating an interface from within CreateEndpoint")
}
driver.Lock()
driver.endpoints[endID] = struct{}{}
driver.Unlock()
resp := &api.CreateEndpointResponse{}
Log.Infof("Create endpoint %s %+v", endID, resp)
return resp, nil
}
func (driver *driver) DeleteEndpoint(deleteReq *api.DeleteEndpointRequest) error {
Log.Debugf("Delete endpoint request: %+v", deleteReq)
Log.Infof("Delete endpoint %s", deleteReq.EndpointID)
driver.Lock()
delete(driver.endpoints, deleteReq.EndpointID)
driver.Unlock()
return nil
}
func (driver *driver) HasEndpoint(endpointID string) bool {
driver.Lock()
_, found := driver.endpoints[endpointID]
driver.Unlock()
return found
}
func (driver *driver) EndpointInfo(req *api.EndpointInfoRequest) (*api.EndpointInfoResponse, error) {
Log.Debugf("Endpoint info request: %+v", req)
Log.Infof("Endpoint info %s", req.EndpointID)
return &api.EndpointInfoResponse{Value: map[string]interface{}{}}, nil
}
func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error) {
endID := j.EndpointID
maybeBridge, err := netlink.LinkByName(WeaveBridge)
if err != nil {
return nil, errorf(`bridge "%s" not present; did you launch weave?`, WeaveBridge)
}
// create and attach local name to the bridge
local := vethPair(endID[:5])
local.Attrs().MTU = maybeBridge.Attrs().MTU
if err := netlink.LinkAdd(local); err != nil {
return nil, errorf("could not create veth pair: %s", err)
}
switch maybeBridge.(type) {
case *netlink.Bridge:
if err := netlink.LinkSetMasterByIndex(local, maybeBridge.Attrs().Index); err != nil {
return nil, errorf(`unable to set master: %s`, err)
}
case *netlink.GenericLink:
if maybeBridge.Type() != "openvswitch" {
Log.Errorf("device %s is %+v", WeaveBridge, maybeBridge)
return nil, errorf(`device "%s" is of type "%s"`, WeaveBridge, maybeBridge.Type())
}
odp.AddDatapathInterface(WeaveBridge, local.Name)
case *netlink.Device:
Log.Warnf("kernel does not report what kind of device %s is, just %+v", WeaveBridge, maybeBridge)
// Assume it's our openvswitch device, and the kernel has not been updated to report the kind.
odp.AddDatapathInterface(WeaveBridge, local.Name)
default:
Log.Errorf("device %s is %+v", WeaveBridge, maybeBridge)
return nil, errorf(`device "%s" not a bridge`, WeaveBridge)
}
if err := netlink.LinkSetUp(local); err != nil {
return nil, errorf(`unable to bring veth up: %s`, err)
}
ifname := &api.InterfaceName{
SrcName: local.PeerName,
DstPrefix: "ethwe",
}
response := &api.JoinResponse{
InterfaceName: ifname,
}
if driver.nameserver != "" {
routeToDNS := api.StaticRoute{
Destination: driver.nameserver + "/32",
RouteType: types.CONNECTED,
NextHop: "",
}
response.StaticRoutes = []api.StaticRoute{routeToDNS}
}
if !driver.noMulticastRoute {
multicastRoute := api.StaticRoute{
Destination: "224.0.0.0/4",
RouteType: types.CONNECTED,
}
response.StaticRoutes = append(response.StaticRoutes, multicastRoute)
}
Log.Infof("Join endpoint %s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey)
return response, nil
}
func (driver *driver) LeaveEndpoint(leave *api.LeaveRequest) error {
Log.Debugf("Leave request: %+v", leave)
local := vethPair(leave.EndpointID[:5])
if err := netlink.LinkDel(local); err != nil {
Log.Warningf("unable to delete veth on leave: %s", err)
}
Log.Infof("Leave %s:%s", leave.NetworkID, leave.EndpointID)
return nil
}
func (driver *driver) DiscoverNew(disco *api.DiscoveryNotification) error {
Log.Debugf("Dicovery new notification: %+v", disco)
return nil
}
func (driver *driver) DiscoverDelete(disco *api.DiscoveryNotification) error {
Log.Debugf("Dicovery delete notification: %+v", disco)
return nil
}
// ===
func vethPair(suffix string) *netlink.Veth {
return &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{Name: "vethwl" + suffix},
PeerName: "vethwg" + suffix,
}
}
+57
View File
@@ -0,0 +1,57 @@
package plugin
import (
"fmt"
weaveapi "github.com/weaveworks/weave/api"
. "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/common/docker"
)
const (
WeaveDomain = "weave.local"
)
type watcher struct {
client *docker.Client
weave *weaveapi.Client
driver *driver
}
type Watcher interface {
}
func NewWatcher(client *docker.Client, driver *driver) (Watcher, error) {
resolver := func() (string, error) { return client.GetContainerIP(WeaveContainer) }
w := &watcher{client: client, driver: driver,
weave: weaveapi.NewClientWithResolver(resolver)}
err := client.AddObserver(w)
if err != nil {
return nil, err
}
return w, nil
}
func (w *watcher) ContainerStarted(id string) {
Log.Debugf("Container started %s", id)
info, err := w.client.InspectContainer(id)
if err != nil {
Log.Warningf("error inspecting container: %s", err)
return
}
// check that it's on our network, via the endpointID
for _, net := range info.NetworkSettings.Networks {
if w.driver.HasEndpoint(net.EndpointID) {
fqdn := fmt.Sprintf("%s.%s", info.Config.Hostname, info.Config.Domainname)
if err := w.weave.RegisterWithDNS(id, fqdn, net.IPAddress); err != nil {
Log.Warningf("unable to register with weaveDNS: %s", err)
}
}
}
}
func (w *watcher) ContainerDied(id string) {
// don't need to do this as WeaveDNS removes names on container died anyway
// (note by the time we get this event we can't see the EndpointID)
}
+298
View File
@@ -0,0 +1,298 @@
package skel
import (
"encoding/json"
"fmt"
"net"
"net/http"
"github.com/gorilla/mux"
"github.com/docker/libnetwork/drivers/remote/api"
"github.com/docker/libnetwork/ipamapi"
iapi "github.com/docker/libnetwork/ipams/remote/api"
)
const (
networkReceiver = "NetworkDriver"
ipamReceiver = ipamapi.PluginEndpointType
)
type Driver interface {
GetCapabilities() (*api.GetCapabilityResponse, error)
CreateNetwork(create *api.CreateNetworkRequest) error
DeleteNetwork(delete *api.DeleteNetworkRequest) error
CreateEndpoint(create *api.CreateEndpointRequest) (*api.CreateEndpointResponse, error)
DeleteEndpoint(delete *api.DeleteEndpointRequest) error
EndpointInfo(req *api.EndpointInfoRequest) (*api.EndpointInfoResponse, error)
JoinEndpoint(j *api.JoinRequest) (response *api.JoinResponse, error error)
LeaveEndpoint(leave *api.LeaveRequest) error
DiscoverNew(discover *api.DiscoveryNotification) error
DiscoverDelete(delete *api.DiscoveryNotification) error
}
type listener struct {
d Driver
i ipamapi.Ipam
}
func Listen(socket net.Listener, driver Driver, ipamDriver ipamapi.Ipam) error {
router := mux.NewRouter()
router.NotFoundHandler = http.HandlerFunc(notFound)
listener := &listener{driver, ipamDriver}
router.Methods("POST").Path("/Plugin.Activate").HandlerFunc(listener.handshake)
handleMethod := func(receiver, method string, h http.HandlerFunc) {
router.Methods("POST").Path(fmt.Sprintf("/%s.%s", receiver, method)).HandlerFunc(h)
}
handleMethod(networkReceiver, "GetCapabilities", listener.getCapabilities)
if driver != nil {
handleMethod(networkReceiver, "CreateNetwork", listener.createNetwork)
handleMethod(networkReceiver, "DeleteNetwork", listener.deleteNetwork)
handleMethod(networkReceiver, "CreateEndpoint", listener.createEndpoint)
handleMethod(networkReceiver, "DeleteEndpoint", listener.deleteEndpoint)
handleMethod(networkReceiver, "EndpointOperInfo", listener.infoEndpoint)
handleMethod(networkReceiver, "Join", listener.joinEndpoint)
handleMethod(networkReceiver, "Leave", listener.leaveEndpoint)
}
if ipamDriver != nil {
handleMethod(ipamReceiver, "GetDefaultAddressSpaces", listener.getDefaultAddressSpaces)
handleMethod(ipamReceiver, "RequestPool", listener.requestPool)
handleMethod(ipamReceiver, "ReleasePool", listener.releasePool)
handleMethod(ipamReceiver, "RequestAddress", listener.requestAddress)
handleMethod(ipamReceiver, "ReleaseAddress", listener.releaseAddress)
}
return http.Serve(socket, router)
}
func decode(w http.ResponseWriter, r *http.Request, v interface{}) error {
err := json.NewDecoder(r.Body).Decode(v)
if err != nil {
sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
}
return err
}
// === protocol handlers
type handshakeResp struct {
Implements []string
}
func (listener *listener) handshake(w http.ResponseWriter, r *http.Request) {
var resp handshakeResp
if listener.d != nil {
resp.Implements = append(resp.Implements, "NetworkDriver")
}
if listener.i != nil {
resp.Implements = append(resp.Implements, "IpamDriver")
}
err := json.NewEncoder(w).Encode(&resp)
if err != nil {
sendError(w, "encode error", http.StatusInternalServerError)
return
}
}
func (listener *listener) getCapabilities(w http.ResponseWriter, r *http.Request) {
caps, err := listener.d.GetCapabilities()
objectOrErrorResponse(w, caps, err)
}
func (listener *listener) createNetwork(w http.ResponseWriter, r *http.Request) {
var create api.CreateNetworkRequest
err := json.NewDecoder(r.Body).Decode(&create)
if err != nil {
sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
return
}
emptyOrErrorResponse(w, listener.d.CreateNetwork(&create))
}
func (listener *listener) deleteNetwork(w http.ResponseWriter, r *http.Request) {
var delete api.DeleteNetworkRequest
if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
sendError(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
return
}
emptyOrErrorResponse(w, listener.d.DeleteNetwork(&delete))
}
func (listener *listener) createEndpoint(w http.ResponseWriter, r *http.Request) {
var create api.CreateEndpointRequest
if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
sendError(w, "unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
return
}
res, err := listener.d.CreateEndpoint(&create)
objectOrErrorResponse(w, res, err)
}
func (listener *listener) deleteEndpoint(w http.ResponseWriter, r *http.Request) {
var delete api.DeleteEndpointRequest
if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
return
}
emptyOrErrorResponse(w, listener.d.DeleteEndpoint(&delete))
}
func (listener *listener) infoEndpoint(w http.ResponseWriter, r *http.Request) {
var req api.EndpointInfoRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
return
}
info, err := listener.d.EndpointInfo(&req)
objectOrErrorResponse(w, info, err)
}
func (listener *listener) joinEndpoint(w http.ResponseWriter, r *http.Request) {
var join api.JoinRequest
if err := json.NewDecoder(r.Body).Decode(&join); err != nil {
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
return
}
res, err := listener.d.JoinEndpoint(&join)
objectOrErrorResponse(w, res, err)
}
func (listener *listener) leaveEndpoint(w http.ResponseWriter, r *http.Request) {
var l api.LeaveRequest
if err := json.NewDecoder(r.Body).Decode(&l); err != nil {
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
return
}
emptyOrErrorResponse(w, listener.d.LeaveEndpoint(&l))
}
func (listener *listener) discoverNew(w http.ResponseWriter, r *http.Request) {
var disco api.DiscoveryNotification
if err := json.NewDecoder(r.Body).Decode(&disco); err != nil {
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
return
}
emptyOrErrorResponse(w, listener.d.DiscoverNew(&disco))
}
func (listener *listener) discoverDelete(w http.ResponseWriter, r *http.Request) {
var disco api.DiscoveryNotification
if err := json.NewDecoder(r.Body).Decode(&disco); err != nil {
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
return
}
emptyOrErrorResponse(w, listener.d.DiscoverDelete(&disco))
}
// ===
func (listener *listener) getDefaultAddressSpaces(w http.ResponseWriter, r *http.Request) {
local, global, err := listener.i.GetDefaultAddressSpaces()
response := &iapi.GetAddressSpacesResponse{
LocalDefaultAddressSpace: local,
GlobalDefaultAddressSpace: global,
}
objectOrErrorResponse(w, response, err)
}
func (listener *listener) requestPool(w http.ResponseWriter, r *http.Request) {
var rq iapi.RequestPoolRequest
if err := decode(w, r, &rq); err != nil {
return
}
poolID, pool, data, err := listener.i.RequestPool(rq.AddressSpace, rq.Pool, rq.SubPool, rq.Options, rq.V6)
if err != nil {
errorResponse(w, err.Error())
return
}
response := &iapi.RequestPoolResponse{
PoolID: poolID,
Pool: pool.String(),
Data: data,
}
objectResponse(w, response)
}
func (listener *listener) releasePool(w http.ResponseWriter, r *http.Request) {
var rq iapi.ReleasePoolRequest
if err := decode(w, r, &rq); err != nil {
return
}
err := listener.i.ReleasePool(rq.PoolID)
emptyOrErrorResponse(w, err)
}
func (listener *listener) requestAddress(w http.ResponseWriter, r *http.Request) {
var rq iapi.RequestAddressRequest
if err := decode(w, r, &rq); err != nil {
return
}
address, data, err := listener.i.RequestAddress(rq.PoolID, net.ParseIP(rq.Address), rq.Options)
if err != nil {
errorResponse(w, err.Error())
return
}
response := &iapi.RequestAddressResponse{
Address: address.String(),
Data: data,
}
objectResponse(w, response)
}
func (listener *listener) releaseAddress(w http.ResponseWriter, r *http.Request) {
var rq iapi.ReleaseAddressRequest
if err := decode(w, r, &rq); err != nil {
return
}
err := listener.i.ReleaseAddress(rq.PoolID, net.ParseIP(rq.Address))
emptyOrErrorResponse(w, err)
}
// ===
func notFound(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
}
func sendError(w http.ResponseWriter, msg string, code int) {
http.Error(w, msg, code)
}
func errorResponse(w http.ResponseWriter, fmtString string, item ...interface{}) {
json.NewEncoder(w).Encode(map[string]string{
"Err": fmt.Sprintf(fmtString, item...),
})
}
func objectResponse(w http.ResponseWriter, obj interface{}) {
if err := json.NewEncoder(w).Encode(obj); err != nil {
sendError(w, "Could not JSON encode response", http.StatusInternalServerError)
return
}
}
func emptyResponse(w http.ResponseWriter) {
json.NewEncoder(w).Encode(map[string]string{})
}
func objectOrErrorResponse(w http.ResponseWriter, obj interface{}, err error) {
if err != nil {
errorResponse(w, err.Error())
return
}
objectResponse(w, obj)
}
func emptyOrErrorResponse(w http.ResponseWriter, err error) {
if err != nil {
errorResponse(w, err.Error())
return
}
emptyResponse(w)
}
+3
View File
@@ -0,0 +1,3 @@
FROM weaveworks/weaveexec
ADD ./plugin /home/weave/
ENTRYPOINT ["/home/weave/plugin"]
+163
View File
@@ -0,0 +1,163 @@
package main
import (
"flag"
"fmt"
"net"
"os"
"os/signal"
"strings"
"syscall"
"github.com/docker/libnetwork/ipamapi"
go_docker "github.com/fsouza/go-dockerclient"
. "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/common/docker"
ipamplugin "github.com/weaveworks/weave/plugin/ipam"
netplugin "github.com/weaveworks/weave/plugin/net"
"github.com/weaveworks/weave/plugin/skel"
)
var version = "(unreleased version)"
func main() {
var (
justVersion bool
address string
nameserver string
meshAddress string
logLevel string
meshNetworkName string
noMulticastRoute bool
removeNetwork bool
)
flag.BoolVar(&justVersion, "version", false, "print version and exit")
flag.StringVar(&logLevel, "log-level", "info", "logging level (debug, info, warning, error)")
flag.StringVar(&address, "socket", "/run/docker/plugins/weave.sock", "socket on which to listen")
flag.StringVar(&nameserver, "nameserver", "", "nameserver to provide to containers")
flag.StringVar(&meshAddress, "meshsocket", "/run/docker/plugins/weavemesh.sock", "socket on which to listen in mesh mode")
flag.StringVar(&meshNetworkName, "mesh-network-name", "weave", "network name to create in mesh mode")
flag.BoolVar(&noMulticastRoute, "no-multicast-route", false, "do not add a multicast route to network endpoints")
flag.BoolVar(&removeNetwork, "remove-network", false, "remove mesh network and exit")
flag.Parse()
if justVersion {
fmt.Printf("weave plugin %s\n", version)
os.Exit(0)
}
SetLogLevel(logLevel)
// API 1.21 is the first version that supports docker network commands
dockerClient, err := docker.NewVersionedClientFromEnv("1.21")
if err != nil {
Log.Fatalf("unable to connect to docker: %s", err)
}
if removeNetwork {
if _, err = dockerClient.Client.NetworkInfo(meshNetworkName); err == nil {
err = dockerClient.Client.RemoveNetwork(meshNetworkName)
if err != nil {
Log.Fatalf("unable to remove network: %s", err)
}
}
os.Exit(0)
}
Log.Println("Weave plugin", version, "Command line options:", os.Args[1:])
Log.Info(dockerClient.Info())
var globalListener, meshListener net.Listener
endChan := make(chan error, 1)
if address != "" {
globalListener, err := listenAndServe(dockerClient, address, nameserver, noMulticastRoute, endChan, "global", false)
if err != nil {
Log.Fatalf("unable to create driver: %s", err)
}
defer globalListener.Close()
}
if meshAddress != "" {
meshListener, err := listenAndServe(dockerClient, meshAddress, nameserver, noMulticastRoute, endChan, "local", true)
if err != nil {
Log.Fatalf("unable to create driver: %s", err)
}
defer meshListener.Close()
}
if meshNetworkName != "" {
createNetwork(dockerClient, meshNetworkName, meshAddress)
}
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, os.Kill, syscall.SIGTERM)
select {
case sig := <-sigChan:
Log.Debugf("Caught signal %s; shutting down", sig)
if err := dockerClient.Client.RemoveNetwork(meshNetworkName); err != nil {
Log.Fatal(err)
}
case err := <-endChan:
if err != nil {
Log.Errorf("Error from listener: %s", err)
globalListener.Close()
meshListener.Close()
os.Exit(1)
}
}
}
func listenAndServe(dockerClient *docker.Client, address, nameserver string, noMulticastRoute bool, endChan chan<- error, scope string, withIpam bool) (net.Listener, error) {
d, err := netplugin.New(dockerClient, version, nameserver, scope, noMulticastRoute)
if err != nil {
return nil, err
}
var i ipamapi.Ipam
if withIpam {
if i, err = ipamplugin.NewIpam(dockerClient, version); err != nil {
return nil, err
}
}
var listener net.Listener
// remove sockets from last invocation
if err := os.Remove(address); err != nil && !os.IsNotExist(err) {
return nil, err
}
listener, err = net.Listen("unix", address)
if err != nil {
return nil, err
}
Log.Printf("Listening on %s for %s scope", address, scope)
go func() {
endChan <- skel.Listen(listener, d, i)
}()
return listener, nil
}
func createNetwork(dockerClient *docker.Client, networkName, address string) {
if _, err := dockerClient.Client.NetworkInfo(networkName); err == nil {
Log.Printf("Docker network '%s' already exists", networkName)
} else if _, ok := err.(*go_docker.NoSuchNetwork); ok {
driverName := strings.TrimSuffix(address, ".sock")
if i := strings.LastIndex(driverName, "/"); i >= 0 {
driverName = driverName[i+1:]
}
options := go_docker.CreateNetworkOptions{
Name: networkName,
CheckDuplicate: true,
Driver: driverName,
IPAM: go_docker.IPAMOptions{Driver: driverName},
}
_, err := dockerClient.Client.CreateNetwork(options)
if err != nil {
Log.Fatalf("Error creating network: %s", err)
}
}
}
+3 -1
View File
@@ -6,6 +6,7 @@ LABEL works.weave.role=system
WORKDIR /home/weave
VOLUME /w
VOLUME /w-noop
VOLUME /w-nomcast
ENTRYPOINT ["/home/weave/sigproxy", "/home/weave/weave"]
RUN apk add --update \
@@ -18,8 +19,9 @@ RUN apk add --update \
bind-tools \
&& rm -rf /var/cache/apk/*
ADD ./weave ./sigproxy ./weaveproxy /home/weave/
ADD ./weave ./sigproxy ./weaveproxy ./symlink /home/weave/
ADD ./netcheck ./docker_tls_args /usr/bin/
ADD ./weavewait /w/w
ADD ./weavewait_noop /w-noop/w
ADD ./weavewait_nomcast /w-nomcast/w
ADD ./docker.tgz /
+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
# This lets us force-symlink a bunch of stuff all at once, so we can minimize
# running containers.
set -e
[ -z "$WEAVE_DEBUG" ] || set -x
target="$1"
user="$(stat -c '%u:%g' "$target")"
mode="$(stat -c '%a' "$target")"
shift 1
for link in "$@" ; do
ln -sfT "$target" "$link"
done
chown -h "$user" "$@"
chmod "$mode" "$@"
+14 -8
View File
@@ -12,14 +12,15 @@ import (
)
var (
version = "(unreleased version)"
version = "(unreleased version)"
defaultDockerHost = "unix:///var/run/docker.sock"
)
func main() {
var (
justVersion bool
logLevel = "info"
c = proxy.Config{ListenAddrs: []string{}}
c = proxy.Config{Image: "weaveworks/weaveexec"}
)
c.Version = version
@@ -35,11 +36,11 @@ func main() {
mflag.BoolVar(&c.NoRewriteHosts, []string{"-no-rewrite-hosts"}, false, "do not automatically rewrite /etc/hosts. Use if you need the docker IP to remain in /etc/hosts")
mflag.StringVar(&c.TLSConfig.CACert, []string{"#tlscacert", "-tlscacert"}, "", "Trust certs signed only by this CA")
mflag.StringVar(&c.TLSConfig.Cert, []string{"#tlscert", "-tlscert"}, "", "Path to TLS certificate file")
mflag.BoolVar(&c.TLSConfig.Enabled, []string{"#tls", "-tls"}, false, "Use TLS; implied by --tls-verify")
mflag.BoolVar(&c.TLSConfig.Enabled, []string{"#tls", "-tls"}, false, "Use TLS; implied by --tlsverify")
mflag.StringVar(&c.TLSConfig.Key, []string{"#tlskey", "-tlskey"}, "", "Path to TLS key file")
mflag.BoolVar(&c.TLSConfig.Verify, []string{"#tlsverify", "-tlsverify"}, false, "Use TLS and verify the remote")
mflag.BoolVar(&c.WithDNS, []string{"-with-dns", "w"}, false, "instruct created containers to always use weaveDNS as their nameserver")
mflag.BoolVar(&c.WithoutDNS, []string{"-without-dns"}, false, "instruct created containers to never use weaveDNS as their nameserver")
mflag.BoolVar(&c.NoMulticastRoute, []string{"-no-multicast-route"}, false, "do not add a multicast route via the weave interface when attaching containers")
mflag.Parse()
if justVersion {
@@ -47,15 +48,20 @@ func main() {
os.Exit(0)
}
if c.WithDNS && c.WithoutDNS {
Log.Fatalf("Cannot use both '--with-dns' and '--without-dns' flags")
}
SetLogLevel(logLevel)
if image := os.Getenv("EXEC_IMAGE"); image != "" {
c.Image = image
}
Log.Infoln("weave proxy", version)
Log.Infoln("Command line arguments:", strings.Join(os.Args[1:], " "))
c.DockerHost = defaultDockerHost
if dockerHost := os.Getenv("DOCKER_HOST"); dockerHost != "" {
c.DockerHost = dockerHost
}
p, err := proxy.NewProxy(c)
if err != nil {
Log.Fatalf("Could not start proxy: %s", err)
+104 -25
View File
@@ -1,8 +1,13 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strings"
"text/template"
"github.com/gorilla/mux"
. "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/ipam"
@@ -10,9 +15,6 @@ import (
"github.com/weaveworks/weave/nameserver"
"github.com/weaveworks/weave/net/address"
weave "github.com/weaveworks/weave/router"
"net/http"
"strings"
"text/template"
)
var rootTemplate = template.New("root").Funcs(map[string]interface{}{
@@ -25,6 +27,66 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
}
return count
},
"printList": func(list []string) string {
if len(list) == 0 {
return "none"
}
return strings.Join(list, ", ")
},
"printIPAMRanges": func(router weave.NetworkRouterStatus, status ipam.Status) string {
var buffer bytes.Buffer
type stats struct {
ips uint32
nickname string
reachable bool
}
peerStats := make(map[string]*stats)
for _, entry := range status.Entries {
s, found := peerStats[entry.Peer]
if !found {
s = &stats{nickname: entry.Nickname, reachable: entry.IsKnownPeer}
peerStats[entry.Peer] = s
}
s.ips += entry.Size
}
printOwned := func(name string, nickName string, reachable bool, ips uint32) {
reachableStr := ""
if !reachable {
reachableStr = "- unreachable!"
}
percentageRanges := float32(ips) * 100.0 / float32(status.RangeNumIPs)
displayName := name + "(" + nickName + ")"
fmt.Fprintf(&buffer, "%-37v %8d IPs (%04.1f%% of total) %s\n",
displayName, ips, percentageRanges, reachableStr)
}
// print the local info first
if ourStats := peerStats[router.Name]; ourStats != nil {
printOwned(router.Name, ourStats.nickname, true, ourStats.ips)
}
// and then the rest
for peer, stats := range peerStats {
if peer != router.Name {
printOwned(peer, stats.nickname, stats.reachable, stats.ips)
}
}
return buffer.String()
},
"allIPAMOwnersUnreachable": func(status ipam.Status) bool {
for _, entry := range status.Entries {
if entry.Size > 0 && entry.IsKnownPeer {
return false
}
}
return true
},
"printConnectionCounts": func(conns []mesh.LocalConnectionStatus) string {
counts := make(map[string]int)
for _, conn := range conns {
@@ -76,40 +138,48 @@ func defTemplate(name string, text string) *template.Template {
}
var statusTemplate = defTemplate("status", `\
Version: {{.Version}}
Version: {{.Version}}
Service: router
Protocol: {{.Router.Protocol}} \
Service: router
Protocol: {{.Router.Protocol}} \
{{if eq .Router.ProtocolMinVersion .Router.ProtocolMaxVersion}}\
{{.Router.ProtocolMaxVersion}}\
{{else}}\
{{.Router.ProtocolMinVersion}}..{{.Router.ProtocolMaxVersion}}\
{{end}}
Name: {{.Router.Name}}({{.Router.NickName}})
Encryption: {{printState .Router.Encryption}}
PeerDiscovery: {{printState .Router.PeerDiscovery}}
Targets: {{len .Router.Targets}}
Connections: {{len .Router.Connections}}{{with printConnectionCounts .Router.Connections}} ({{.}}){{end}}
Peers: {{len .Router.Peers}}{{with printPeerConnectionCounts .Router.Peers}} (with {{.}} connections){{end}}
Name: {{.Router.Name}}({{.Router.NickName}})
Encryption: {{printState .Router.Encryption}}
PeerDiscovery: {{printState .Router.PeerDiscovery}}
Targets: {{len .Router.Targets}}
Connections: {{len .Router.Connections}}{{with printConnectionCounts .Router.Connections}} ({{.}}){{end}}
Peers: {{len .Router.Peers}}{{with printPeerConnectionCounts .Router.Peers}} (with {{.}} connections){{end}}
TrustedSubnets: {{printList .Router.TrustedSubnets}}
{{if .IPAM}}\
Service: ipam
Service: ipam
{{if .IPAM.Entries}}\
Consensus: achieved
{{else if .IPAM.Paxos}}\
Consensus: waiting (quorum: {{.IPAM.Paxos.Quorum}}, known: {{.IPAM.Paxos.KnownNodes}})
{{if allIPAMOwnersUnreachable .IPAM}}\
Status: all IP ranges owned by unreachable peers - use 'rmpeer' if they are dead
{{else if len .IPAM.PendingAllocates}}\
Status: waiting for IP range grant from peers
{{else}}\
Consensus: deferred
Status: ready
{{end}}\
Range: {{.IPAM.Range}}
DefaultSubnet: {{.IPAM.DefaultSubnet}}
{{else if .IPAM.Paxos}}\
Status: awaiting consensus (quorum: {{.IPAM.Paxos.Quorum}}, known: {{.IPAM.Paxos.KnownNodes}})
{{else}}\
Status: idle
{{end}}\
Range: {{.IPAM.Range}}
DefaultSubnet: {{.IPAM.DefaultSubnet}}
{{end}}\
{{if .DNS}}\
Service: dns
Domain: {{.DNS.Domain}}
TTL: {{.DNS.TTL}}
Entries: {{countDNSEntries .DNS.Entries}}
Service: dns
Domain: {{.DNS.Domain}}
Upstream: {{printList .DNS.Upstream}}
TTL: {{.DNS.TTL}}
Entries: {{countDNSEntries .DNS.Entries}}
{{end}}\
`)
@@ -129,7 +199,7 @@ var peersTemplate = defTemplate("peers", `\
{{.Name}}({{.NickName}})
{{range .Connections}}\
{{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} \
{{$nameNickName := printf "%v(%v)" .Name .NickName}}{{printf "%-32v" $nameNickName}} \
{{$nameNickName := printf "%v(%v)" .Name .NickName}}{{printf "%-37v" $nameNickName}} \
{{if .Established}}established{{else}}pending{{end}}
{{end}}\
{{end}}\
@@ -145,6 +215,8 @@ var dnsEntriesTemplate = defTemplate("dnsEntries", `\
{{end}}\
`)
var ipamTemplate = defTemplate("ipamTemplate", `{{printIPAMRanges .Router .IPAM}}`)
type WeaveStatus struct {
Version string
Router *weave.NetworkRouterStatus `json:"Router,omitempty"`
@@ -174,7 +246,13 @@ func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRout
muxRouter.Methods("GET").Path("/report").Queries("format", "{format}").HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
formatTemplate, err := template.New("format").Parse(mux.Vars(r)["format"])
funcs := template.FuncMap{
"json": func(v interface{}) string {
a, _ := json.Marshal(v)
return string(a)
},
}
formatTemplate, err := template.New("format").Funcs(funcs).Parse(mux.Vars(r)["format"])
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
@@ -200,5 +278,6 @@ func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRout
defHandler("/status/connections", connectionsTemplate)
defHandler("/status/peers", peersTemplate)
defHandler("/status/dns", dnsEntriesTemplate)
defHandler("/status/ipam", ipamTemplate)
}
+35 -5
View File
@@ -69,8 +69,15 @@ func main() {
dnsEffectiveListenAddress string
iface *net.Interface
datapathName string
trustedSubnetStr string
defaultDockerHost = "unix:///var/run/docker.sock"
)
if val := os.Getenv("DOCKER_HOST"); val != "" {
defaultDockerHost = val
}
mflag.BoolVar(&justVersion, []string{"#version", "-version"}, false, "print version and exit")
mflag.BoolVar(&createDatapath, []string{"-create-datapath"}, false, "create ODP datapath and exit")
mflag.BoolVar(&deleteDatapath, []string{"-delete-datapath"}, false, "delete ODP datapath and exit")
@@ -92,7 +99,7 @@ func main() {
mflag.StringVar(&iprangeCIDR, []string{"#iprange", "#-iprange", "-ipalloc-range"}, "", "IP address range reserved for automatic allocation, in CIDR notation")
mflag.StringVar(&ipsubnetCIDR, []string{"#ipsubnet", "#-ipsubnet", "-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
mflag.IntVar(&peerCount, []string{"#initpeercount", "#-initpeercount", "-init-peer-count"}, 0, "number of peers in network (for IP address allocation)")
mflag.StringVar(&dockerAPI, []string{"#api", "#-api", "-docker-api"}, "", "Docker API endpoint, e.g. unix:///var/run/docker.sock")
mflag.StringVar(&dockerAPI, []string{"#api", "#-api", "-docker-api"}, defaultDockerHost, "Docker API endpoint")
mflag.BoolVar(&noDNS, []string{"-no-dns"}, false, "disable DNS server")
mflag.StringVar(&dnsDomain, []string{"-dns-domain"}, nameserver.DefaultDomain, "local domain to server requests for")
mflag.StringVar(&dnsListenAddress, []string{"-dns-listen-address"}, nameserver.DefaultListenAddress, "address to listen on for DNS requests")
@@ -101,6 +108,8 @@ func main() {
mflag.StringVar(&dnsEffectiveListenAddress, []string{"-dns-effective-listen-address"}, "", "address DNS will actually be listening, after Docker port mapping")
mflag.StringVar(&datapathName, []string{"-datapath"}, "", "ODP datapath name")
mflag.StringVar(&trustedSubnetStr, []string{"-trusted-subnets"}, "", "Command separated list of trusted subnets in CIDR notation")
// crude way of detecting that we probably have been started in a
// container, with `weave launch` --> suppress misleading paths in
// mflags error messages.
@@ -193,10 +202,7 @@ func main() {
Log.Println("Communication between peers is unencrypted.")
} else {
config.Password = []byte(password)
Log.Println("Communication between peers is encrypted.")
// fastdp doesn't support encryption
fastDPOverlay = nil
Log.Println("Communication between peers via untrusted networks is encrypted.")
}
overlays := weave.NewOverlaySwitch()
@@ -237,6 +243,10 @@ func main() {
networkConfig.PacketLogging = nopPacketLogging{}
}
if config.TrustedSubnets, err = parseTrustedSubnets(trustedSubnetStr); err != nil {
Log.Fatal("Unable to parse trusted subnets: ", err)
}
router := weave.NewNetworkRouter(config, networkConfig, name, nickName, overlays)
Log.Println("Our name is", router.Ourself)
@@ -245,6 +255,8 @@ func main() {
dc, err := docker.NewClient(dockerAPI)
if err != nil {
Log.Fatal("Unable to start docker client: ", err)
} else {
Log.Info(dc.Info())
}
dockerCli = dc
}
@@ -405,6 +417,24 @@ func determineQuorum(initPeerCountFlag int, peers []string) uint {
return quorum
}
func parseTrustedSubnets(trustedSubnetStr string) ([]*net.IPNet, error) {
trustedSubnets := []*net.IPNet{}
if trustedSubnetStr == "" {
return trustedSubnets, nil
}
for _, subnetStr := range strings.Split(trustedSubnetStr, ",") {
_, subnet, err := net.ParseCIDR(subnetStr)
if err != nil {
return nil, err
}
trustedSubnets = append(trustedSubnets, subnet)
}
return trustedSubnets, nil
}
func listenAndServeHTTP(httpAddr string, muxRouter *mux.Router) {
protocol := "tcp"
if strings.HasPrefix(httpAddr, "/") {
+2 -7
View File
@@ -1,12 +1,7 @@
// +build !noop
// +build !iface,!mcast
package main
import (
weavenet "github.com/weaveworks/weave/net"
)
func checkNetwork() error {
_, err := weavenet.EnsureInterfaceAndMcastRoute("ethwe")
return err
return nil
}
+12
View File
@@ -0,0 +1,12 @@
// +build iface,!mcast
package main
import (
weavenet "github.com/weaveworks/weave/net"
)
func checkNetwork() error {
_, err := weavenet.EnsureInterface("ethwe")
return err
}
@@ -0,0 +1,12 @@
// +build iface,mcast
package main
import (
weavenet "github.com/weaveworks/weave/net"
)
func checkNetwork() error {
_, err := weavenet.EnsureInterfaceAndMcastRoute("ethwe")
return err
}
-7
View File
@@ -1,7 +0,0 @@
// +build noop
package main
func checkNetwork() error {
return nil
}
+1
View File
@@ -39,6 +39,7 @@ func callWeave(args ...string) ([]byte, []byte, error) {
}
propagateEnv("DOCKER_BRIDGE")
propagateEnv("DOCKER_HOST")
// Propogage WEAVE_DEBUG, to make debugging easier.
propagateEnv("WEAVE_DEBUG")
+8 -2
View File
@@ -53,8 +53,14 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
Log.Infof("Leaving container alone because %s", err)
} else {
Log.Infof("Creating container with WEAVE_CIDR \"%s\"", strings.Join(cidrs, " "))
if err := addVolume(hostConfig, i.proxy.weaveWaitVolume, "/w", "ro"); err != nil {
return err
if i.proxy.NoMulticastRoute {
if err := addVolume(hostConfig, i.proxy.weaveWaitNomcastVolume, "/w", "ro"); err != nil {
return err
}
} else {
if err := addVolume(hostConfig, i.proxy.weaveWaitVolume, "/w", "ro"); err != nil {
return err
}
}
if err := i.setWeaveWaitEntrypoint(container); err != nil {
return err
+134 -22
View File
@@ -1,6 +1,7 @@
package proxy
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
@@ -8,6 +9,7 @@ import (
"net"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"sync"
@@ -16,15 +18,15 @@ import (
docker "github.com/fsouza/go-dockerclient"
. "github.com/weaveworks/weave/common"
weavedocker "github.com/weaveworks/weave/common/docker"
"github.com/weaveworks/weave/nameserver"
)
const (
defaultCaFile = "ca.pem"
defaultKeyFile = "key.pem"
defaultCertFile = "cert.pem"
dockerSock = "/var/run/docker.sock"
dockerSockUnix = "unix://" + dockerSock
weaveSock = "/var/run/weave/weave.sock"
weaveSockUnix = "unix://" + weaveSock
)
var (
@@ -46,14 +48,16 @@ type Config struct {
HostnameFromLabel string
HostnameMatch string
HostnameReplacement string
Image string
ListenAddrs []string
RewriteInspect bool
NoDefaultIPAM bool
NoRewriteHosts bool
TLSConfig TLSConfig
Version string
WithDNS bool
WithoutDNS bool
NoMulticastRoute bool
DockerHost string
}
type wait struct {
@@ -65,13 +69,14 @@ type wait struct {
type Proxy struct {
sync.Mutex
Config
client *docker.Client
dockerBridgeIP string
hostnameMatchRegexp *regexp.Regexp
weaveWaitVolume string
weaveWaitNoopVolume string
normalisedAddrs []string
waiters map[*http.Request]*wait
client *docker.Client
dockerBridgeIP string
hostnameMatchRegexp *regexp.Regexp
weaveWaitVolume string
weaveWaitNoopVolume string
weaveWaitNomcastVolume string
normalisedAddrs []string
waiters map[*http.Request]*wait
}
func NewProxy(c Config) (*Proxy, error) {
@@ -81,14 +86,16 @@ func NewProxy(c Config) (*Proxy, error) {
Log.Fatalf("Could not configure tls for proxy: %s", err)
}
// We pin the protocol version to 1.15 (which corresponds to
// Docker 1.3.x; the earliest version supported by weave) in order
// We pin the protocol version to 1.18 (which corresponds to
// Docker 1.6.x; the earliest version supported by weave) in order
// to insulate ourselves from breaking changes to the API, as
// happened in 1.20 (Docker 1.8.0) when the presentation of
// volumes changed in `inspect`.
client, err := weavedocker.NewVersionedClient(dockerSockUnix, "1.15")
client, err := weavedocker.NewVersionedClient(c.DockerHost, "1.18")
if err != nil {
return nil, err
} else {
Log.Info(client.Info())
}
p.client = client.Client
@@ -132,7 +139,16 @@ func (proxy *Proxy) AttachExistingContainers() {
}
func (proxy *Proxy) Dial() (net.Conn, error) {
return net.Dial("unix", dockerSock)
proto := "tcp"
addr := proxy.Config.DockerHost
switch {
case strings.HasPrefix(addr, "unix://"):
proto = "unix"
addr = strings.TrimPrefix(addr, "unix://")
case strings.HasPrefix(addr, "tcp://"):
addr = strings.TrimPrefix(addr, "tcp://")
}
return net.Dial(proto, addr)
}
func (proxy *Proxy) findWeaveWaitVolumes() error {
@@ -140,7 +156,10 @@ func (proxy *Proxy) findWeaveWaitVolumes() error {
if proxy.weaveWaitVolume, err = proxy.findVolume("/w"); err != nil {
return err
}
proxy.weaveWaitNoopVolume, err = proxy.findVolume("/w-noop")
if proxy.weaveWaitNoopVolume, err = proxy.findVolume("/w-noop"); err != nil {
return err
}
proxy.weaveWaitNomcastVolume, err = proxy.findVolume("/w-nomcast")
return err
}
@@ -186,7 +205,12 @@ func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func (proxy *Proxy) Listen() []net.Listener {
listeners := []net.Listener{}
proxy.normalisedAddrs = []string{}
unixAddrs := []string{}
for _, addr := range proxy.ListenAddrs {
if strings.HasPrefix(addr, "unix://") || strings.HasPrefix(addr, "/") {
unixAddrs = append(unixAddrs, addr)
continue
}
listener, normalisedAddr, err := proxy.listen(addr)
if err != nil {
Log.Fatalf("Cannot listen on %s: %s", addr, err)
@@ -195,6 +219,20 @@ func (proxy *Proxy) Listen() []net.Listener {
proxy.normalisedAddrs = append(proxy.normalisedAddrs, normalisedAddr)
}
if len(unixAddrs) > 0 {
listener, _, err := proxy.listen(weaveSockUnix)
if err != nil {
Log.Fatalf("Cannot listen on %s: %s", weaveSockUnix, err)
}
listeners = append(listeners, listener)
if err := proxy.symlink(unixAddrs); err != nil {
Log.Fatalf("Cannot listen on unix sockets: %s", err)
}
proxy.normalisedAddrs = append(proxy.normalisedAddrs, weaveSockUnix)
}
for _, addr := range proxy.normalisedAddrs {
Log.Infoln("proxy listening on", addr)
}
@@ -291,8 +329,10 @@ func (proxy *Proxy) listen(protoAndAddr string) (net.Listener, string, error) {
if err != nil {
return nil, "", err
}
if err = copyOwnerAndPermissions(dockerSock, addr); err != nil {
return nil, "", err
if strings.HasPrefix(proxy.Config.DockerHost, "unix://") {
if err = copyOwnerAndPermissions(strings.TrimPrefix(proxy.Config.DockerHost, "unix://"), addr); err != nil {
return nil, "", err
}
}
default:
@@ -405,6 +445,9 @@ func (proxy *Proxy) attach(container *docker.Container, orDie bool) error {
}
}
}
if proxy.NoMulticastRoute {
args = append(args, "--no-multicast-route")
}
if orDie {
args = append(args, "--or-die")
}
@@ -476,10 +519,6 @@ func (proxy *Proxy) getDNSDomain() (domain string) {
return ""
}
if proxy.WithDNS {
domain = nameserver.DefaultDomain
}
weaveContainer, err := proxy.client.InspectContainer("weave")
var weaveIP string
if err == nil && weaveContainer.NetworkSettings != nil {
@@ -527,3 +566,76 @@ func (proxy *Proxy) updateContainerNetworkSettings(container jsonObject) error {
networkSettings["IPPrefixLen"], _ = nets[0].Mask.Size()
return nil
}
func (proxy *Proxy) symlink(unixAddrs []string) (err error) {
var container *docker.Container
binds := []string{"/var/run/weave:/var/run/weave"}
froms := []string{}
for _, addr := range unixAddrs {
if addr == weaveSockUnix {
continue
}
from := strings.TrimPrefix(addr, "unix://")
dir := filepath.Dir(from)
binds = append(binds, dir+":"+filepath.Join("/host", dir))
froms = append(froms, filepath.Join("/host", from))
proxy.normalisedAddrs = append(proxy.normalisedAddrs, addr)
}
if len(froms) == 0 {
return
}
env := []string{
"PATH=/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
}
if val := os.Getenv("WEAVE_DEBUG"); val != "" {
env = append(env, fmt.Sprintf("%s=%s", "WEAVE_DEBUG", val))
}
container, err = proxy.client.CreateContainer(docker.CreateContainerOptions{
Config: &docker.Config{
Image: proxy.Image,
Entrypoint: []string{"/home/weave/symlink", weaveSock},
Cmd: froms,
Env: env,
},
HostConfig: &docker.HostConfig{Binds: binds},
})
if err != nil {
return
}
defer func() {
err2 := proxy.client.RemoveContainer(docker.RemoveContainerOptions{ID: container.ID})
if err == nil {
err = err2
}
}()
err = proxy.client.StartContainer(container.ID, nil)
if err != nil {
return
}
var buf bytes.Buffer
err = proxy.client.AttachToContainer(docker.AttachToContainerOptions{
Container: container.ID,
ErrorStream: &buf,
Logs: true,
Stderr: true,
})
if err != nil {
return
}
var rc int
rc, err = proxy.client.WaitContainer(container.ID)
if err != nil {
return
}
if rc != 0 {
err = errors.New(buf.String())
}
return
}
+8 -2
View File
@@ -41,8 +41,14 @@ func (i *startContainerInterceptor) InterceptRequest(r *http.Request) error {
return err
}
} else {
if err := addVolume(hostConfig, i.proxy.weaveWaitVolume, "/w", "ro"); err != nil {
return err
if i.proxy.NoMulticastRoute {
if err := addVolume(hostConfig, i.proxy.weaveWaitNomcastVolume, "/w", "ro"); err != nil {
return err
}
} else {
if err := addVolume(hostConfig, i.proxy.weaveWaitVolume, "/w", "ro"); err != nil {
return err
}
}
if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" {
if err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, dnsDomain); err != nil {
+32 -6
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"net"
"sync"
"syscall"
"time"
"github.com/weaveworks/go-odp/odp"
@@ -40,6 +41,7 @@ type FastDatapath struct {
dpif *odp.Dpif
dp odp.DatapathHandle
deleteFlowsCount uint64
missCount uint64
missHandlers map[odp.VportID]missHandler
localPeer *mesh.Peer
peers *mesh.Peers
@@ -109,6 +111,10 @@ func NewFastDatapath(config FastDatapathConfig) (*FastDatapath, error) {
forwarders: make(map[mesh.PeerName]*fastDatapathForwarder),
}
// This delete happens asynchronously in the kernel, meaning that
// we can sometimes fail to recreate the vxlan vport with EADDRINUSE -
// consequently we retry a small number of times in
// getVxlanVportIDHarder() to compensate.
if err := fastdp.deleteVxlanVports(); err != nil {
return nil, err
}
@@ -125,7 +131,7 @@ func NewFastDatapath(config FastDatapathConfig) (*FastDatapath, error) {
// numbers to be independent, but working out how to specify
// them on the connecting side. So we can wait to find out if
// anyone wants that.
fastdp.mainVxlanVportID, err = fastdp.getVxlanVportID(config.Port + 1)
fastdp.mainVxlanVportID, err = fastdp.getVxlanVportIDHarder(config.Port+1, 5, time.Millisecond*10)
if err != nil {
return nil, err
}
@@ -216,8 +222,13 @@ func (fastdp fastDatapathBridge) String() string {
return fmt.Sprint(fastdp.dpname, " (via ODP)")
}
func (fastDatapathBridge) Stats() map[string]int {
return nil
func (fastdp fastDatapathBridge) Stats() map[string]int {
lock := fastdp.startLock()
defer lock.unlock()
return map[string]int{
"FlowMisses": int(fastdp.missCount),
}
}
var routerBridgePortID = bridgePortID{router: true}
@@ -400,6 +411,20 @@ func (fastdp fastDatapathOverlay) StartConsumingPackets(localPeer *mesh.Peer, pe
return nil
}
func (fastdp *FastDatapath) getVxlanVportIDHarder(udpPort int, retries int, duration time.Duration) (odp.VportID, error) {
var vxlanVportID odp.VportID
var err error
for try := 0; try < retries; try++ {
vxlanVportID, err = fastdp.getVxlanVportID(udpPort)
if err == nil || err != odp.NetlinkError(syscall.EADDRINUSE) {
return vxlanVportID, err
}
log.Warning("Address already in use creating vxlan vport ", udpPort, " - retrying")
time.Sleep(duration)
}
return 0, err
}
func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
fastdp.lock.Lock()
defer fastdp.lock.Unlock()
@@ -509,9 +534,8 @@ type fastDatapathForwarder struct {
func (fastdp fastDatapathOverlay) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
if params.SessionKey != nil {
// No encryption suport in fastdp. The weaver main.go
// is responsible for ensuring this doesn't happen.
log.Fatal("Attempt to use FastDatapath with encryption")
// No encryption support in fastdp
return nil, fmt.Errorf("encryption not supported")
}
vxlanVportID := fastdp.mainVxlanVportID
@@ -937,6 +961,8 @@ func (fastdp *FastDatapath) Miss(packet []byte, fks odp.FlowKeys) error {
lock := fastdp.startLock()
defer lock.unlock()
fastdp.missCount++
handler := fastdp.getMissHandler(ingress)
if handler == nil {
return nil
+10 -2
View File
@@ -204,8 +204,16 @@ func (osw *OverlaySwitch) PrepareConnection(params mesh.OverlayConnectionParams)
subConn, err := overlay.PrepareConnection(params)
if err != nil {
fwd.stopFrom(0)
return nil, err
log.Infof("Unable to use %s for connection to %s(%s): %s",
overlay.name,
params.RemotePeer.Name,
params.RemotePeer.NickName,
err)
// failed to start subforwarder - record overlay name and continue
fwd.forwarders[i] = subForwarder{
overlayName: overlay.name,
}
continue
}
subFwd := subConn.(OverlayForwarder)
+1 -1
View File
@@ -11,7 +11,7 @@ You only need to build weave if you want to work on the weave codebase
Apart from the `weave` shell script, weave is delivered as a set of
container images. There is no distribution-specific packaging, so in
principle it shouldn't matter which Linux distribution you build
under. But naturally, Docker is a prerequisite (version 1.3.1 or
under. But naturally, Docker is a prerequisite (version 1.6.0 or
later). And it is difficult to build under Fedora because [Fedora
does not include static
libraries](http://fedoraproject.org/wiki/Packaging:Guidelines#Packaging_Static_Libraries).
+16 -17
View File
@@ -63,7 +63,9 @@ Weave automatically chooses the fastest available method to transport
data between peers. The most performant of these ('fastdp') offers
near-native throughput and latency but does not support encryption;
consequently supplying a password will cause the router to fall back
to a slower mode ('sleeve') that does.
to a slower mode ('sleeve') that does, for connections that traverse
untrusted networks (see the [security](#security) section for more
details).
Even when encryption is not in use, certain adverse network conditions
will cause this fallback to occur dynamically; in these circumstances,
@@ -91,21 +93,12 @@ re-attached to the weave network by the weave Docker API proxy.
### <a name="plugin"></a>Docker network plugin
Alternatively, you can use weave as a Docker plugin. First you must
configure the Docker daemon to use a cluster store as documented
[here](https://docs.docker.com/engine/userguide/networking/dockernetworks/#an-overlay-network);
you can then launch weave and create a network:
$ weave launch
$ weave launch-plugin
$ docker network create --driver=weave weave
and then start a container:
Alternatively, you can use weave as a Docker plugin. A Docker network
named `weave` is created by `weave launch`, which you can use like this:
$ docker run --net=weave -ti ubuntu
For more details see the
[plugin README](https://github.com/weaveworks/docker-plugin).
For more details see the [plugin documentation](plugin.html).
### <a name="addressing"></a>Address allocation
@@ -321,10 +314,16 @@ way to generate a random password which satsifies this requirement is
< /dev/urandom tr -dc A-Za-z0-9 | head -c9 ; echo
The same password must be specified for all weave peers. Note that
supplying a password will [cause weave to fall back to a slower
method](#fast-data-path) for transporting data between
peers.
The same password must be specified for all weave peers; by default
both control and data plane traffic will then use authenticated
encryption. If some of your peers are colocated in a trusted network
(for example within the boundary of your own datacentre) you can use
the `--trusted-subnets` argument to `weave launch` to selectively
disable data plane encryption as an optimisation. Both peers must
consider the other to be in a trusted subnet for this to take place -
if they do not, weave will [fall back to a slower
method](#fast-data-path) for transporting data between peers as fast
datapath does not support encryption.
Be aware that:
+92
View File
@@ -0,0 +1,92 @@
---
title: Using Weave Net via Docker Networking
layout: default
---
New in Docker version 1.9 is a plugin mechanism to add different
network providers.
To install, download the `weave` script as per the [readme][].
The Weave Net plugin runs automatically when you `weave launch`. You
must still tell the weave peers to connect to one another, either via
`weave launch` or `weave connect`.
Note that the plugin container (`weaveplugin`) is run with
`--restart=always`, so that it is there after a restart or reboot. If
you remove this container (e.g. using `weave reset`) before removing
all endpoints created using `--net=weave`, Docker can
[hang](https://github.com/docker/libnetwork/issues/813).
## Starting a container:
$ docker run --net=weave -ti ubuntu
## Using WeaveDNS for Service Discovery:
You need to pass the additional arguments `--dns` and `-dns-search`,
for which we provide a helper in the weave script:
$ docker run --net=weave -h foo.weave.local $(weave dns-args) -tdi ubuntu
$ docker run --net=weave -h bar.weave.local $(weave dns-args) -ti ubuntu
# ping foo
For more details see the [Weave Service Discovery documentation][service-discovery].
## Under the hood
The Weave Net plugin actually provides *two* network drivers to Docker
- one named `weavemesh` that can operate without a [cluster
store](#cluster-store) (like Docker's overlay driver) and one named
`weave` that can only work with one.
### `weavemesh` driver
* Weave handles all co-ordination between hosts (referred to by Docker as a "local scope" driver)
* Supports only a single network (we create one named `weave` for you automatically)
* Uses Weave's partition tolerant IPAM
### `weave` driver
* This runs in what Docker call "global scope"; requires a cluster store
* Supports multiple networks (these must be created with `docker network create --driver weave ...`)
* Used with Docker's cluster store based IPAM
There's no specific documentation from Docker on using a cluster
store, but the first part of [Getting Started with Docker Multi-host Networking][docker-net]
should point the way.
Note that in the case of multiple docker networks, all containers are
on the same virtual network but Docker allocates their addresses on
different subnets so they cannot talk to each other directly.
## Plugin command-line arguments
If you want to give some arguments to the plugin independently, don't
use `weave launch`; instead run:
$ weave launch-router [other peers]
$ weave launch-plugin [plugin arguments]
The plugin command-line arguments are:
* `--log-level=debug|info|warning|error`, which tells the plugin
how much information to emit for debugging.
* `--mesh-network-name=<name>`: set it to blank to disable creation
of a default network, or a name of your own choice.
* `--no-multicast-route`: stop weave adding a static IP route for
multicast traffic on its interface
## Restarting
We start the plugin with a restart policy, because Docker attempts to
recreate networks on startup. If it isn't started automatically by
Docker with a restart policy, it's not possible to start it manually
because Docker is blocked.
Specifically, we use the policy `on-failure:3`, so Docker will give up
after three attempts if something just isn't working.
[readme]: https://github.com/weaveworks/weave/blob/master/README.md#installation
[service-discovery]: weavedns.html
[docker-net]: https://github.com/docker/docker/blob/master/docs/userguide/networking/get-started-overlay.md
+9 -9
View File
@@ -231,14 +231,14 @@ be able to launch a TLS-enabled proxy with:
host1$ weave launch-proxy
You can also manually configure the proxy's TLS. This is accomplished
by launching the proxy with the same TLS-related command-line flags as
supplied to the docker daemon. For example, if you have generated your
certificates and keys into the docker host's `/tls` directory, we can
launch the proxy with:
To disable auto-detection of TLS configuration, you can either pass
the `--no-detect-tls` flag, or manually configure the proxy's TLS with
the same TLS-related command-line flags as supplied to the docker
daemon. For example, if you have generated your certificates and keys
into the docker host's `/tls` directory, we can launch the proxy with:
host1$ weave launch-proxy --tls-verify --tls-cacert=/tls/ca.pem \
--tls-cert=/tls/server-cert.pem --tls-key=/tls/server-key.pem
host1$ weave launch-proxy --tlsverify --tlscacert=/tls/ca.pem \
--tlscert=/tls/server-cert.pem --tlskey=/tls/server-key.pem
The paths to your certificates and key must be provided as absolute
paths which exist on the docker host.
@@ -254,8 +254,8 @@ for an example.
With the proxy running over TLS, we can configure our regular docker
client to use TLS on a per-invocation basis with
$ docker --tls-verify --tls-cacert=ca.pem --tls-cert=cert.pem \
--tls-key=key.pem -H=tcp://host1:12375 version
$ docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem \
--tlskey=key.pem -H=tcp://host1:12375 version
...
or,
+36 -23
View File
@@ -68,29 +68,33 @@ A status summary can be obtained with `weave status`:
````
$ weave status
Version: 1.1.0
Version: 1.1.0
Service: router
Protocol: weave 1..2
Name: 4a:0f:f6:ec:1c:93(host1)
Encryption: disabled
PeerDiscovery: enabled
Targets: [192.168.48.14 192.168.48.15]
Connections: 5 (1 established, 1 pending, 1 retrying, 1 failed, 1 connecting)
Peers: 3 (with 5 established, 1 pending connections)
Service: router
Protocol: weave 1..2
Name: 4a:0f:f6:ec:1c:93(host1)
Encryption: disabled
PeerDiscovery: enabled
Targets: [192.168.48.14 192.168.48.15]
Connections: 5 (1 established, 1 pending, 1 retrying, 1 failed, 1 connecting)
Peers: 3 (with 5 established, 1 pending connections)
TrustedSubnets: none
Service: ipam
Consensus: achieved
Range: 10.32.0.0-10.47.255.255
DefaultSubnet: 10.32.0.0/12
Service: ipam
Consensus: achieved
Range: 10.32.0.0-10.47.255.255
DefaultSubnet: 10.32.0.0/12
Service: dns
Domain: weave.local.
TTL: 1
Entries: 9
Service: dns
Domain: weave.local.
TTL: 1
Entries: 9
Service: proxy
Address: tcp://127.0.0.1:12375
Service: proxy
Address: tcp://127.0.0.1:12375
Service: plugin
DriverName: weave
````
@@ -129,6 +133,9 @@ state. Further details are available with
number of connections peers have to other peers. Further details are
available with [`weave status peers`](#weave-status-peers).
'TrustedSubnets' shows subnets which the router trusts as specified by
the `--trusted-subnets` option to `weave launch`.
There are further sections for the [IP address
allocator](ipam.html#troubleshooting),
[weaveDNS](weavedns.html#troubleshooting), and [Weave Docker API
@@ -141,8 +148,8 @@ obtained with `weave status connections`:
````
$ weave status connections
<- 192.168.48.12:33866 established fastdp 7e:21:4a:70:2f:45(host2)
<- 192.168.48.13:60773 pending fastdp 7e:ae:cd:d5:23:8d(host3)
<- 192.168.48.12:33866 established unencrypted fastdp 7e:21:4a:70:2f:45(host2)
<- 192.168.48.13:60773 pending encrypted fastdp 7e:ae:cd:d5:23:8d(host3)
-> 192.168.48.14:6783 retrying dial tcp4 192.168.48.14:6783: no route to host
-> 192.168.48.15:6783 failed dial tcp4 192.168.48.15:6783: no route to host, retry: 2015-08-06 18:55:38.246910357 +0000 UTC
-> 192.168.48.16:6783 connecting
@@ -162,8 +169,8 @@ The columns are as follows:
heartbeat
* `established` - TCP connection and corresponding UDP path are up
* Info - the failure reason for failed and retrying connections, or
the data transport method, remote peer name and nickname for
pending and established connections
the encryption mode, data transport method, remote peer name and
nickname for pending and established connections
### <a name="weave-status-peers"></a>List peers
@@ -228,6 +235,12 @@ inspect`:
$ weave report -f {% raw %}'{{.DNS.Domain}}'{% endraw %}
weave.local.
Weave adds a template function, `json`, which can be applied to get
results in JSON format.
$ weave report -f {% raw %}'{{json .DNS}}'{% endraw %}
{% raw %}{"Domain":"weave.local.","Upstream":["8.8.8.8","8.8.4.4"],"Address":"172.17.0.1:53","TTL":1,"Entries":null}{% endraw %}
### <a name="list-attached-containers"></a>List attached containers
weave ps
+2
View File
@@ -260,6 +260,7 @@ DNS:
Service: dns
Domain: weave.local.
Upstream: 8.8.8.8, 8.8.4.4
TTL: 1
Entries: 9
@@ -272,6 +273,7 @@ guide](troubleshooting.html#weave-status) for more detail.
The 'Service: dns' section is pertinent to weaveDNS, and includes:
* The local domain suffix which is being served
* The list of upstream servers used for resolving names not in the local domain
* The response ttl
* The total number of entries
+3
View File
@@ -14,4 +14,7 @@ start_container $HOST1 $C1/24 --name=c1
start_container $HOST2 $C2/24 --name=c2
assert_raises "exec_on $HOST1 c1 $PING $C2"
assert_raises "weave_on $HOST1 status connections | grep encrypted"
assert_raises "weave_on $HOST2 status connections | grep encrypted"
end_suite
+30
View File
@@ -0,0 +1,30 @@
#!/bin/bash
. ./config.sh
start_suite "Optional encryption via trusted subnets"
# Determine subnet for hosts given either an IP or name. We need to resolve
# these entries on the remote hosts to make sure we get the private
# IP addresses in the Circle/GCE context
HOST1_IP=$($SSH $HOST1 "getent hosts $HOST1" | grep $HOST1 | cut -d ' ' -f 1)
HOST2_IP=$($SSH $HOST2 "getent hosts $HOST2" | grep $HOST2 | cut -d ' ' -f 1)
HOST1_CIDR=$($SSH $HOST1 "ip addr show" | grep -oP $HOST1_IP/[0-9]+)
HOST2_CIDR=$($SSH $HOST2 "ip addr show" | grep -oP $HOST2_IP/[0-9]+)
# Check asymmetric trust - connections should be encrypted
weave_on $HOST1 launch --password wfvAwt7sj --trusted-subnets $HOST2_CIDR
weave_on $HOST2 launch --password wfvAwt7sj $HOST1
assert_raises "weave_on $HOST1 status connections | grep encrypted"
assert_raises "weave_on $HOST2 status connections | grep encrypted"
weave_on $HOST1 stop
weave_on $HOST2 stop
# Check symmetric trust - overlay in plaintext
weave_on $HOST1 launch --password wfvAwt7sj --trusted-subnets $HOST2_CIDR
weave_on $HOST2 launch --password wfvAwt7sj --trusted-subnets $HOST1_CIDR $HOST1
assert_raises "weave_on $HOST1 status connections | grep unencrypted"
assert_raises "weave_on $HOST2 status connections | grep unencrypted"
end_suite
+33
View File
@@ -0,0 +1,33 @@
#! /bin/bash
. ./config.sh
show_multicast_route_on() {
exec_on $1 $2 ip route show 224.0.0.0/4
}
start_suite "--no-multicast-route operation"
# Ensure containers run either way have no multicast route
weave_on $HOST1 launch-router
weave_on $HOST1 launch-proxy --no-multicast-route
weave_on $HOST1 launch-plugin --no-multicast-route
start_container $HOST1 --no-multicast-route --name c1
proxy_start_container $HOST1 --name c2
start_container_local_plugin $HOST1 --name=c3
assert "show_multicast_route_on $HOST1 c1"
assert "show_multicast_route_on $HOST1 c2"
assert "show_multicast_route_on $HOST1 c3"
# Ensure current proxy options are obeyed on container start
docker_on $HOST1 stop c2
weave_on $HOST1 stop-proxy
weave_on $HOST1 launch-proxy
proxy docker_on $HOST1 start c2
assert "show_multicast_route_on $HOST1 c2" "224.0.0.0/4 dev ethwe "
end_suite
-1
View File
@@ -27,6 +27,5 @@ start_container $HOST1 $TARGET_IP/24 --name c2 -h $TARGET
check_dns assert_no_dns_record --without-dns
check_dns assert_dns_record
check_dns assert_dns_record --with-dns
end_suite
+5 -3
View File
@@ -9,18 +9,20 @@ docker_py_test() {
start_suite "Run docker-py test suite against the proxy"
# Work round https://github.com/docker/docker-py/issues/852
docker_on $HOST1 pull busybox:buildroot-2014.02 >/dev/null
# Get a list of the tests for use to shard
docker_on $HOST1 pull joffrey/docker-py >/dev/null
CANDIDATES=$(docker_on $HOST1 run \
joffrey/docker-py \
py.test --collect-only tests/integration_test.py \
| sed -En "s/\s*<UnitTestCase '([[:alpha:]]+)'>/\1/p")
py.test --collect-only tests/integration/ \
| sed -En "s/\s*<Module '([[:print:]]+)'>/\1/p")
i=0
TESTS=
for test in $CANDIDATES; do
if [ $(($i % $TOTAL_SHARDS)) -eq $SHARD ]; then
TESTS="$TESTS tests/integration_test.py::$test"
TESTS="$TESTS $test"
fi
i=$(($i + 1))
done
-5
View File
@@ -23,11 +23,6 @@ start_suite "Proxy registers containers with dns"
bridge_ip=$(weave_on $HOST1 docker-bridge-ip)
# Assert behaviour without weaveDNS running, but dns forced
weave_on $HOST1 launch-proxy --with-dns
do_assert_resolution assert_no_dns_record
weave_on $HOST1 stop-proxy
# Assert behaviour without weaveDNS
weave_on $HOST1 launch-proxy
do_assert_resolution assert_no_dns_record
+6
View File
@@ -28,4 +28,10 @@ weave_on $HOST1 stop
weave_on $HOST1 launch-proxy -H tcp://0.0.0.0:12375 -H unix:///var/run/weave/weave.sock
check
# Check we can use weave env/config with unix -Hs specified
weave_on $HOST1 stop
run_on $HOST1 "COVERAGE=$COVERAGE weave launch-proxy -H unix:///var/run/weave/weave.sock"
assert_raises "run_on $HOST1 'eval \$(weave env) ; docker $CMD'"
assert_raises "run_on $HOST1 'docker \$(weave config) $CMD'"
end_suite
@@ -2,10 +2,10 @@
. ./config.sh
start_suite "Boot the proxy should only listen on client's interface"
start_suite "Various launch-proxy configurations"
# Booting it over unix socket listens on unix socket
run_on $HOST1 COVERAGE=$COVERAGE sudo -E weave launch-proxy
run_on $HOST1 COVERAGE=$COVERAGE weave launch-proxy
assert_raises "run_on $HOST1 sudo docker -H unix:///var/run/weave/weave.sock ps"
assert_raises "proxy docker_on $HOST1 ps" 1
weave_on $HOST1 stop-proxy
@@ -17,11 +17,21 @@ assert_raises "proxy docker_on $HOST1 ps"
weave_on $HOST1 stop-proxy
# Booting it over tcp (no prefix) listens on tcp
DOCKER_CLIENT_ARGS="-H $HOST1:$DOCKER_PORT" $WEAVE launch-proxy
DOCKER_HOST=tcp://$HOST1:$DOCKER_PORT $WEAVE launch-proxy
assert_raises "run_on $HOST1 sudo docker -H unix:///var/run/weave/weave.sock ps" 1
assert_raises "proxy docker_on $HOST1 ps"
weave_on $HOST1 stop-proxy
# Booting it with -H outside /var/run/weave, still works
socket="$($SSH $HOST1 mktemp -d)/weave.sock"
weave_on $HOST1 launch-proxy -H unix://$socket
assert_raises "run_on $HOST1 sudo docker -H unix:///$socket ps" 0
weave_on $HOST1 stop-proxy
# Booting it against non-standard docker unix sock
run_on $HOST1 "DOCKER_HOST=unix:///var/run/alt-docker.sock COVERAGE=$COVERAGE weave launch-proxy -H tcp://0.0.0.0:12375"
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
+21
View File
@@ -0,0 +1,21 @@
#! /bin/bash
. ./config.sh
C1_NAME=c1.weave.local
C2_NAME=seetwo.weave.local
start_suite "Ping and DNS over cross-host weave network (via 'local' plugin)"
DNS_IP=$(weave_on $host docker-bridge-ip)
weave_on $HOST1 launch
weave_on $HOST2 launch $HOST1
start_container_local_plugin $HOST1 --name=c1 --hostname=$C1_NAME --dns=$DNS_IP
start_container_local_plugin $HOST2 --name=c2 --hostname=$C2_NAME --dns=$DNS_IP
assert_raises "exec_on $HOST1 c1 $PING $C2_NAME"
assert_raises "exec_on $HOST2 c2 $PING $C1_NAME"
end_suite
+1 -1
View File
@@ -2,7 +2,7 @@ This directory contains integration tests for weave.
## Requirements
You need two VMs with docker >=1.3.1 installed and listening on TCP
You need two VMs with docker >=1.6.0 installed and listening on TCP
port 2375 (see below). You also need to be able to ssh to these VMs,
preferably without having to input anything.
+13 -2
View File
@@ -151,7 +151,14 @@ start_container() {
start_container_with_dns() {
host=$1
shift 1
weave_on $host run --with-dns "$@" -t $DNS_IMAGE /bin/sh
weave_on $host run "$@" -t $DNS_IMAGE /bin/sh
}
start_container_local_plugin() {
host=$1
shift 1
# using ssh rather than docker -H because CircleCI docker client is older
$SSH $host docker run "$@" -dt --net=weave $SMALL_IMAGE /bin/sh
}
proxy_start_container() {
@@ -217,8 +224,12 @@ assert_dns_ptr_record() {
start_suite() {
for host in $HOSTS; do
[ -z "$DEBUG" ] || echo "Cleaning up on $host: removing all containers and resetting weave"
PLUGIN_ID=$(docker_on $host ps -aq --filter=name=weaveplugin)
PLUGIN_FILTER="cat"
[ -n "$PLUGIN_ID" ] && PLUGIN_FILTER="grep -v $PLUGIN_ID"
rm_containers $host $(docker_on $host ps -aq 2>/dev/null | $PLUGIN_FILTER)
run_on $host "docker network ls | grep -q ' weave ' && docker network rm weave" || true
weave_on $host reset 2>/dev/null
rm_containers $host $(docker_on $host ps -aq 2>/dev/null)
done
whitely echo "$@"
}
+3 -3
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-8}
: ${TEMPLATE_NAME:=test-template-9}
: ${ZONE:=us-central1-a}
: ${NUM_HOSTS:=5}
SUFFIX=""
@@ -73,7 +73,7 @@ curl -sSL https://get.docker.com/ | sh
apt-get update -qq;
apt-get install -q -y --force-yes --no-install-recommends ethtool;
usermod -a -G docker vagrant;
echo 'DOCKER_OPTS="-H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375 -s overlay"' >> /etc/default/docker;
echo 'DOCKER_OPTS="-H unix:///var/run/docker.sock -H unix:///var/run/alt-docker.sock -H tcp://0.0.0.0:2375 -s overlay"' >> /etc/default/docker;
service docker restart
EOF
# It seems we need a short delay for docker to start up, so I put this in
@@ -140,7 +140,7 @@ function hosts {
hosts="$hostname $hosts"
args="--add-host=$hostname:$(internal_ip $json $name) $args"
done
echo export SSH=ssh
echo export SSH=\"ssh -l vagrant\"
echo export HOSTS=\"$hosts\"
echo export ADD_HOST_ARGS=\"$args\"
rm $json
+2 -2
View File
@@ -6,7 +6,7 @@ cd "$(dirname "${BASH_SOURCE[0]}")"
. ./config.sh
(cd ./tls && go get -tags netgo ./... && go run generate_certs.go $HOSTS)
(cd ./tls && ./tls $HOSTS)
echo "Copying weave images, scripts, and certificates to hosts, and"
echo " prefetch test images"
@@ -19,7 +19,7 @@ setup_host() {
run_on $HOST mkdir -p bin
upload_executable $HOST ../bin/docker-ns
upload_executable $HOST ../weave
rsync -az -e "$SSH" ./tls/ $HOST:~/tls
rsync -az -e "$SSH" --exclude=tls ./tls/ $HOST:~/tls
for IMG in $TEST_IMAGES ; do
docker_on $HOST inspect --format=" " $IMG >/dev/null 2>&1 || docker_on $HOST pull $IMG
done
-17
View File
@@ -6,7 +6,6 @@ import (
"reflect"
"runtime"
"testing"
"time"
"github.com/stretchr/testify/require"
)
@@ -36,22 +35,6 @@ func stackTrace(all bool) string {
return string(buf[:stacklen])
}
// Borrowed from net/http tests:
// goTimeout runs f, failing t if f takes more than d to complete.
func RunWithTimeout(t *testing.T, d time.Duration, f func()) {
ch := make(chan bool, 2)
timer := time.AfterFunc(d, func() {
t.Errorf("Timeout expired after %v: stacks:\n%s", d, stackTrace(true))
ch <- true
})
defer timer.Stop()
go func() {
defer func() { ch <- true }()
f()
}()
<-ch
}
// TrimTestArgs finds the first -- in os.Args and trim all args before that
func TrimTestArgs() {
i, l := 0, len(os.Args)
+1 -1
View File
@@ -45,7 +45,7 @@ end
def tweak_docker_daemon(vm)
vm.provision :shell, :inline => <<SCRIPT
usermod -a -G docker vagrant
sed -i -e's%-H fd://%-H fd:// -H tcp://0.0.0.0:2375 -s overlay%' /lib/systemd/system/docker.service
sed -i -e's%-H fd://%-H fd:// -H unix:///var/run/alt-docker.sock -H tcp://0.0.0.0:2375 -s overlay%' /lib/systemd/system/docker.service
systemctl daemon-reload
systemctl restart docker
systemctl enable docker
+324 -145
View File
@@ -11,19 +11,14 @@ else
fi
IMAGE_VERSION=${WEAVE_VERSION:-$IMAGE_VERSION}
# - Docker versions prior to 1.2.0 cannot parse `--dns-search=.`
# - Docker versions prior to 1.3.0 fail to include the domain when
# setting the hostname, so lookups for non-fq names fail.
# - Docker version 1.3.0 contains the severe bug
# https://github.com/docker/docker/issues/8632
# - The weavexec image embeds a Docker 1.3.1 client. Misleading
# version mismatch errors are produced if the user is running with a
# client *and* server with a lower version.
# - Docker will complain if you run a client against a server older
# than that client.
# - Docker versions prior to 1.6 cannot pull images from DockerHub
# - The weavexec image embeds a Docker 1.6.2 client. Docker will give
# a "client is newer than server error" if the daemon has an older
# API version, which could be confusing if the user's Docker client
# is correctly matched with that older version.
#
# We therefore check that the user's Docker *client* is >= 1.3.1
MIN_DOCKER_VERSION=1.3.1
# We therefore check that the user's Docker *client* is >= 1.6
MIN_DOCKER_VERSION=1.6.0
# These are needed for remote execs, hence we introduce them here
DOCKERHUB_USER=${DOCKERHUB_USER:-weaveworks}
@@ -32,7 +27,6 @@ EXEC_IMAGE=$BASE_EXEC_IMAGE:$IMAGE_VERSION
PROXY_HOST=${PROXY_HOST:-$(echo "${DOCKER_HOST#tcp://}" | cut -s -d: -f1)}
PROXY_HOST=${PROXY_HOST:-127.0.0.1}
DOCKER_CLIENT_HOST=${DOCKER_CLIENT_HOST:-$DOCKER_HOST}
DOCKER_CLIENT_TLS_VERIFY=${DOCKER_CLIENT_TLS_VERIFY:-$DOCKER_TLS_VERIFY}
# Define some regular expressions for matching addresses.
# The regexp here is far from precise, but good enough.
@@ -48,24 +42,27 @@ weave setup
weave version
weave launch [--password <password>] [--nickname <nickname>]
[--ipalloc-range <cidr> [--ipalloc-default-subnet <cidr>]]
[--no-discovery] [--init-peer-count <count>] <peer> ...
[--no-discovery] [--init-peer-count <count>]
[--trusted-subnets <cidr>,...] <peer> ...
weave launch-router [--password <password>] [--nickname <nickname>]
[--ipalloc-range <cidr> [--ipalloc-default-subnet <cidr>]]
[--no-discovery] [--init-peer-count <count>] <peer> ...
weave launch-proxy [-H <endpoint>] [--with-dns | --without-dns]
weave launch-proxy [-H <endpoint>] [--without-dns]
[--no-default-ipalloc] [--no-rewrite-hosts]
[--no-multicast-route]
[--hostname-from-label <labelkey>]
[--hostname-match <regexp>]
[--hostname-replacement <replacement>]
[--rewrite-inspect]
weave launch-plugin
weave launch-plugin [--no-multicast-route]
weave env [--restore]
weave config
weave dns-args
weave connect [--replace] [<peer> ...]
weave forget <peer> ...
weave status [targets | connections | peers | dns]
weave report [-f <format>]
weave run [--with-dns | --without-dns] [--no-rewrite-hosts]
weave run [--without-dns] [--no-rewrite-hosts] [--no-multicast-route]
[<addr> ...] <docker run args> ...
weave start [<addr> ...] <container_id>
weave attach [<addr> ...] <container_id>
@@ -98,9 +95,25 @@ usage() {
exit 1
}
docker_sock() {
if [ -z "$DOCKER_HOST" ]; then
echo "/var/run/docker.sock"
return
fi
if echo "$DOCKER_HOST" | grep -q "^unix://" >/dev/null; then
echo "${DOCKER_HOST#unix://}"
fi
}
docker_sock_volume_mount() {
if [ -n "$(docker_sock)" ]; then
echo "-v $(docker_sock):$(docker_sock)"
fi
}
exec_remote() {
docker $DOCKER_CLIENT_ARGS run --rm --privileged --net=host \
-v /var/run/docker.sock:/var/run/docker.sock \
$(docker_sock_volume_mount) \
-v /proc:/hostproc \
-e PROCFS=/hostproc \
-e DOCKERHUB_USER="$DOCKERHUB_USER" \
@@ -113,9 +126,11 @@ exec_remote() {
-e WEAVE_CONTAINER_NAME \
-e WEAVE_MTU \
-e WEAVE_NO_FASTDP \
-e WEAVE_NO_BRIDGED_FASTDP \
-e WEAVE_NO_PLUGIN \
-e DOCKER_HOST \
-e DOCKER_BRIDGE \
-e DOCKER_CLIENT_HOST="$DOCKER_CLIENT_HOST" \
-e DOCKER_CLIENT_TLS_VERIFY="$DOCKER_CLIENT_TLS_VERIFY" \
-e DOCKER_CLIENT_ARGS \
-e PROXY_HOST="$PROXY_HOST" \
-e WEAVE_CIDR=none \
@@ -123,30 +138,54 @@ exec_remote() {
$WEAVEEXEC_DOCKER_ARGS $EXEC_IMAGE --local "$@"
}
# Given $1 and $2 as semantic version numbers like 3.1.2, return [ $1 < $2 ]
version_lt() {
VERSION_MAJOR=${1%.*.*}
REST=${1%.*} VERSION_MINOR=${REST#*.}
VERSION_PATCH=${1#*.*.}
MIN_VERSION_MAJOR=${2%.*.*}
REST=${2%.*} MIN_VERSION_MINOR=${REST#*.}
MIN_VERSION_PATCH=${2#*.*.}
if [ \( "$VERSION_MAJOR" -lt "$MIN_VERSION_MAJOR" \) -o \
\( "$VERSION_MAJOR" -eq "$MIN_VERSION_MAJOR" -a \
\( "$VERSION_MINOR" -lt "$MIN_VERSION_MINOR" -o \
\( "$VERSION_MINOR" -eq "$MIN_VERSION_MINOR" -a \
\( "$VERSION_PATCH" -lt "$MIN_VERSION_PATCH" \) \) \) \) ] ; then
return 0
fi
return 1
}
check_docker_version() {
if ! DOCKER_VERSION=$(docker -v | sed -n -e 's|^Docker version \([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*|\1|p') || [ -z "$DOCKER_VERSION" ] ; then
echo "ERROR: Unable to parse docker version" >&2
exit 1
fi
DOCKER_VERSION_MAJOR=$(echo "$DOCKER_VERSION" | cut -d. -f 1)
DOCKER_VERSION_MINOR=$(echo "$DOCKER_VERSION" | cut -d. -f 2)
DOCKER_VERSION_PATCH=$(echo "$DOCKER_VERSION" | cut -d. -f 3)
MIN_DOCKER_VERSION_MAJOR=$(echo "$MIN_DOCKER_VERSION" | cut -d. -f 1)
MIN_DOCKER_VERSION_MINOR=$(echo "$MIN_DOCKER_VERSION" | cut -d. -f 2)
MIN_DOCKER_VERSION_PATCH=$(echo "$MIN_DOCKER_VERSION" | cut -d. -f 3)
if [ \( "$DOCKER_VERSION_MAJOR" -lt "$MIN_DOCKER_VERSION_MAJOR" \) -o \
\( "$DOCKER_VERSION_MAJOR" -eq "$MIN_DOCKER_VERSION_MAJOR" -a \
\( "$DOCKER_VERSION_MINOR" -lt "$MIN_DOCKER_VERSION_MINOR" -o \
\( "$DOCKER_VERSION_MINOR" -eq "$MIN_DOCKER_VERSION_MINOR" -a \
\( "$DOCKER_VERSION_PATCH" -lt "$MIN_DOCKER_VERSION_PATCH" \) \) \) \) ] ; then
if version_lt $DOCKER_VERSION $MIN_DOCKER_VERSION ; then
echo "ERROR: weave requires Docker version $MIN_DOCKER_VERSION or later; you are running $DOCKER_VERSION" >&2
exit 1
fi
}
check_docker_server_api_version() {
# Cope with various versions of `docker version` output format
if ! DOCKER_API_VERSION=$(docker version -f '{{.Server.APIVersion}}' 2> /dev/null ) ; then
if ! DOCKER_API_VERSION=$(docker version -f '{{.Server.ApiVersion}}' 2> /dev/null ) ; then
if ! DOCKER_API_VERSION=$(docker version | sed -n -e 's|^Server API version: *\([0-9][0-9]*\.[0-9][0-9]\).*|\1|p') ; then
echo "ERROR: Unable to determine docker version" >&2
exit 1
fi
fi
fi
if version_lt ${DOCKER_API_VERSION}.0 ${1}.0 ; then
return 1
fi
}
enforce_docker_bridge_addr_assign_type() {
if ! ADDR_ASSIGN_TYPE=$(cat /sys/class/net/$DOCKER_BRIDGE/addr_assign_type 2>/dev/null) ; then
echo "Could not determine address assignment type of $DOCKER_BRIDGE" >&2
@@ -264,20 +303,42 @@ elif [ "$1" = "run" -a -z "$IS_LOCAL" ] ; then
# because we are passing arbitrary arguments on to docker run, and
# we can't rely on our baked-in docker to support those arguments.
shift 1
if [ "$1" != "--without-dns" ] ; then
DNS_ARGS=$(exec_remote dns-args "$@" || true)
fi
shift $(dns_arg_count "$@")
if [ "$1" = "--no-rewrite-hosts" ] ; then
REWRITE_HOSTS=1
NO_MULTICAST_ROUTE=
while [ $# -gt 0 ]; do
case "$1" in
--no-rewrite-hosts)
REWRITE_HOSTS=
;;
--no-multicast-route)
NO_MULTICAST_ROUTE=1
;;
*)
break
;;
esac
shift
else
done
if [ -n "$REWRITE_HOSTS" ] ; then
extra_hosts_args "$@"
DNS_EXTRA_HOSTS_ARGS="--rewrite-hosts $DNS_EXTRA_HOSTS_ARGS"
fi
if [ -n "$NO_MULTICAST_ROUTE" ] ; then
ATTACH_ARGS="--no-multicast-route"
fi
collect_cidr_args "$@"
shift $CIDR_ARG_COUNT
CONTAINER=$(docker $DOCKER_CLIENT_ARGS run -e WEAVE_CIDR=none $DNS_ARGS -d "$@")
exec_remote attach $CIDR_ARGS --or-die $DNS_EXTRA_HOSTS_ARGS $CONTAINER >/dev/null
exec_remote attach $CIDR_ARGS --or-die $DNS_EXTRA_HOSTS_ARGS $ATTACH_ARGS $CONTAINER >/dev/null
echo $CONTAINER
exit 0
fi
@@ -298,9 +359,11 @@ PROCFS=${PROCFS:-/proc}
DOCKER_BRIDGE=${DOCKER_BRIDGE:-docker0}
CONTAINER_NAME=${WEAVE_CONTAINER_NAME:-weave}
BRIDGE=weave
# This value is overridden when the datapath is used unbridged
DATAPATH=datapath
BRIDGE_IFNAME=vethwe-bridge
DATAPATH_IFNAME=vethwe-datapath
CONTAINER_IFNAME=ethwe
# ROUTER_HOSTNETNS_IFNAME is only used for fastdp with encryption
ROUTER_HOSTNETNS_IFNAME=veth-weave
PORT=${WEAVE_PORT:-6783}
HTTP_PORT=6784
PROXY_PORT=12375
@@ -402,22 +465,58 @@ random_mac() {
# weave and docker specific helpers
######################################################################
create_bridge() {
# Detect the current bridge/datapath state. When invoked, the values of
# $BRIDGE and $DATAPATH are expected to be distinct. $BRIDGE_TYPE and
# $DATAPATH are set correctly on success; failure indicates that the
# bridge/datapath devices have yet to be configured. If netdevs do exist
# but are in an inconsistent state the script aborts with an error.
detect_bridge_type() {
BRIDGE_TYPE=
if [ ! -d /sys/class/net/$BRIDGE ] ; then
if [ -n "$WEAVE_NO_FASTDP" ] ; then
BRIDGE_TYPE=bridge
elif docker run --rm --privileged --net=host $IMAGE $COVERAGE_ARGS --create-datapath --datapath=$BRIDGE ; then
BRIDGE_TYPE=fastdp
elif [ $? = 17 ] ; then
# Exit status of 17 means the kernel doesn't have ODP
if [ -d /sys/class/net/$DATAPATH ] ; then
# Unfortunately there's no simple way to positively check whether
# $DATAPATH is an ODP netdev so we have to make sure it isn't
# a bridge instead (and that $BRIDGE is).
if [ ! -d /sys/class/net/$DATAPATH/bridge -a -d /sys/class/net/$BRIDGE/bridge ] ; then
BRIDGE_TYPE=bridged_fastdp
else
echo "Inconsistent bridge state detected. Please do 'weave reset' and try again" >&2
exit 1
fi
elif [ -d /sys/class/net/$BRIDGE ] ; then
if [ -d /sys/class/net/$BRIDGE/bridge ] ; then
BRIDGE_TYPE=bridge
else
return 1
BRIDGE_TYPE=fastdp
# The datapath is the bridge when there is no intermediary
DATAPATH="$BRIDGE"
fi
else
# No bridge/datapath devices configured
return 1
fi
}
create_bridge() {
if ! detect_bridge_type ; then
BRIDGE_TYPE=bridge
if [ -z "$WEAVE_NO_FASTDP" ] ; then
BRIDGE_TYPE=bridged_fastdp
if [ -n "$WEAVE_NO_BRIDGED_FASTDP" ] ; then
BRIDGE_TYPE=fastdp
# The datapath is the bridge when there is no intermediary
DATAPATH="$BRIDGE"
fi
if docker run --rm --privileged --net=host $IMAGE $COVERAGE_ARGS --create-datapath --datapath=$DATAPATH ; then
: # ODP datapath created successfully
elif [ $? = 17 ] ; then
# Exit status of 17 means the kernel doesn't have ODP
BRIDGE_TYPE=bridge
else
return 1
fi
fi
init_bridge_$BRIDGE_TYPE
init_$BRIDGE_TYPE
# Drop traffic from Docker bridge to Weave; it can break
# subnet isolation
@@ -426,6 +525,13 @@ create_bridge() {
run_iptables -t filter -I FORWARD -i $DOCKER_BRIDGE -o $BRIDGE -j DROP
fi
[ -n "$DOCKER_BRIDGE_IP" ] || docker_bridge_ip
# forbid traffic to the Weave port from other containers
add_iptables_rule filter INPUT -i $DOCKER_BRIDGE -p tcp --dst $DOCKER_BRIDGE_IP --dport $PORT -j DROP
add_iptables_rule filter INPUT -i $DOCKER_BRIDGE -p udp --dst $DOCKER_BRIDGE_IP --dport $PORT -j DROP
add_iptables_rule filter INPUT -i $DOCKER_BRIDGE -p udp --dst $DOCKER_BRIDGE_IP --dport $(($PORT + 1)) -j DROP
# let DNS traffic to weaveDNS, since otherwise it might get blocked by the likes of UFW
add_iptables_rule filter INPUT -i $DOCKER_BRIDGE -p udp --dport 53 -j ACCEPT
add_iptables_rule filter INPUT -i $DOCKER_BRIDGE -p tcp --dport 53 -j ACCEPT
@@ -438,14 +544,8 @@ create_bridge() {
run_iptables -t nat -N WEAVE >/dev/null 2>&1 || true
add_iptables_rule nat POSTROUTING -j WEAVE
else
# Detect whether fast datapath is in use on
# $BRIDGE. Unfortunately there's no simple way to positively
# check whether $BRIDGE is a ODP netdev, so we have to check
# whether it is a bridge instead.
if [ -d /sys/class/net/$BRIDGE/bridge ] ; then
BRIDGE_TYPE=bridge
if [ -n "$LAUNCHING_ROUTER" -a -z "$WEAVE_NO_FASTDP" ] ; then
if [ -n "$LAUNCHING_ROUTER" ] ; then
if [ "$BRIDGE_TYPE" = bridge -a -z "$WEAVE_NO_FASTDP" ] ; then
cat <<EOF >&1
WEAVE_NO_FASTDP is not set, but there is already a bridge present of
the wrong type for fast datapath. Please do 'weave reset' to remove
@@ -453,10 +553,7 @@ the bridge first.
EOF
return 1
fi
else
BRIDGE_TYPE=fastdp
if [ -n "$WEAVE_NO_FASTDP" ] ; then
if [ "$BRIDGE_TYPE" != bridge -a -n "$WEAVE_NO_FASTDP" ] ; then
cat <<EOF >&1
WEAVE_NO_FASTDP is set, but there is already a weave fast datapath
bridge present. Please do 'weave reset' to remove the bridge first.
@@ -483,7 +580,7 @@ EOF
configure_arp_cache $BRIDGE
}
init_bridge_fastdp() {
init_fastdp() {
# GCE has the lowest underlay network MTU we're likely to encounter on
# a local network, at 1460 bytes. To get the overlay MTU from that we
# subtract 20 bytes for the outer IPv4 header, 8 bytes for the outer
@@ -492,11 +589,14 @@ init_bridge_fastdp() {
MTU=${WEAVE_MTU:-1410}
# create_bridge already created the datapath netdev
ip link set dev $BRIDGE mtu $MTU
ip link set dev $DATAPATH mtu $MTU
}
init_bridge_bridge() {
MTU=${WEAVE_MTU:-65535}
init_bridge() {
# Observe any MTU that is already set
if [ -z "$MTU" ] ; then
MTU=${WEAVE_MTU:-65535}
fi
ip link add name $BRIDGE type bridge
@@ -514,6 +614,36 @@ init_bridge_bridge() {
ip link del dev v${CONTAINER_IFNAME}du
}
init_bridged_fastdp() {
# Initialise the datapath as normal. NB sets MTU for use below
init_fastdp
# Create linking veth pair. We do this before initialising the bridge
# so that `ip link show` displays the datapath, linking veths and
# the bridge in natural order
ip link del $DATAPATH_IFNAME >/dev/null 2>&1 || true
ip link del $BRIDGE_IFNAME >/dev/null 2>&1 || true
ip link add name $BRIDGE_IFNAME mtu $MTU type veth peer name $DATAPATH_IFNAME mtu $MTU || return 1
# Initialise the bridge using fast datapath MTU
init_bridge
# Link intermediary bridge and datapath
if ! ip link set $DATAPATH_IFNAME up ||
! ip link set $BRIDGE_IFNAME up ||
! add_iface_fastdp $DATAPATH_IFNAME || ! ip link set $BRIDGE_IFNAME master $BRIDGE ; then
# Failed to link bridge and datapath - clean up
ip link del $BRIDGE >/dev/null 2>&1 || true
ip link del $DATAPATH_IFNAME >/dev/null 2>&1 || true
ip link del $BRIDGE_IFNAME >/dev/null 2>&1 || true
docker run --rm --privileged --net=host $IMAGE $COVERAGE_ARGS --delete-datapath --datapath=$DATAPATH >/dev/null 2>&1 || true
return 1
fi
# Finally, bring the datapath up
ip link set dev $DATAPATH up
}
ethtool_tx_off_fastdp() {
true
}
@@ -522,21 +652,42 @@ ethtool_tx_off_bridge() {
ethtool -K $1 tx off >/dev/null
}
ethtool_tx_off_bridged_fastdp() {
true
}
destroy_bridge() {
if [ -d /sys/class/net/$BRIDGE ] ; then
if [ -d /sys/class/net/$BRIDGE/bridge ] ; then
ip link del dev $BRIDGE
else
docker run --rm --privileged --net=host $IMAGE $COVERAGE_ARGS --delete-datapath --datapath=$BRIDGE
# It's important that detect_bridge_type has not been called so
# we have distinct values for $BRIDGE and $DATAPATH. Make best efforts
# to remove netdevs of any type with those names so `weave reset` can
# recover from inconsistent states.
for NETDEV in $BRIDGE $DATAPATH ; do
if [ -d /sys/class/net/$NETDEV ] ; then
if [ -d /sys/class/net/$NETDEV/bridge ] ; then
ip link del $NETDEV
else
docker run --rm --privileged --net=host $IMAGE $COVERAGE_ARGS --delete-datapath --datapath=$NETDEV
fi
fi
fi
done
# Remove any lingering bridged fastdp veth
ip link del $DATAPATH_IFNAME >/dev/null 2>&1 || true
ip link del $BRIDGE_IFNAME >/dev/null 2>&1 || true
if [ "$DOCKER_BRIDGE" != "$BRIDGE" ] ; then
run_iptables -t filter -D FORWARD -i $DOCKER_BRIDGE -o $BRIDGE -j DROP 2>/dev/null || true
fi
[ -n "$DOCKER_BRIDGE_IP" ] || docker_bridge_ip
run_iptables -t filter -D INPUT -i $DOCKER_BRIDGE -p udp --dport 53 -j ACCEPT >/dev/null 2>&1 || true
run_iptables -t filter -D INPUT -i $DOCKER_BRIDGE -p tcp --dport 53 -j ACCEPT >/dev/null 2>&1 || true
run_iptables -t filter -D INPUT -i $DOCKER_BRIDGE -p tcp --dst $DOCKER_BRIDGE_IP --dport $PORT -j DROP >/dev/null 2>&1 || true
run_iptables -t filter -D INPUT -i $DOCKER_BRIDGE -p udp --dst $DOCKER_BRIDGE_IP --dport $PORT -j DROP >/dev/null 2>&1 || true
run_iptables -t filter -D INPUT -i $DOCKER_BRIDGE -p udp --dst $DOCKER_BRIDGE_IP --dport $(($PORT + 1)) -j DROP >/dev/null 2>&1 || true
run_iptables -t filter -D FORWARD -i $BRIDGE -o $BRIDGE -j ACCEPT 2>/dev/null || true
run_iptables -t nat -F WEAVE >/dev/null 2>&1 || true
run_iptables -t nat -D POSTROUTING -j WEAVE >/dev/null 2>&1 || true
@@ -634,13 +785,17 @@ connect_container_to_bridge() {
}
add_iface_fastdp() {
docker run --rm --privileged --net=host $IMAGE $COVERAGE_ARGS --datapath=$BRIDGE --add-datapath-iface=$1
docker run --rm --privileged --net=host $IMAGE $COVERAGE_ARGS --datapath=$DATAPATH --add-datapath-iface=$1
}
add_iface_bridge() {
ip link set $1 master $BRIDGE
}
add_iface_bridged_fastdp() {
add_iface_bridge "$@"
}
ask_version() {
if ! DOCKERIMAGE=$(docker inspect --format='{{.Image}}' $1 2>/dev/null) ; then
if ! DOCKERIMAGE=$(docker inspect --format='{{.Id}}' $2 2>/dev/null) ; then
@@ -651,47 +806,23 @@ ask_version() {
}
router_opts_fastdp() {
if [ -z "$WEAVE_PASSWORD" ] ; then
echo "--datapath $BRIDGE"
else
# When using encryption, we still do bridging on the ODP
# datapath, because you can 'weave launch' without encryption
# and then later restart the router with encryption, or vice
# versa. Encryption disables the use of the fastdp Overlay,
# but the router could still use the fastdp Bridge to receive
# packets. However, pcap has better performance when sniffing
# every packet. So we pass --iface to use the pcap Bridge.
#
# Why don't we simply pass "--iface $BRIDGE". We could,
# except for the fact that NetworkManager likes to down the
# odp $BRIDGE netdev (at least under ubuntu), and you can only
# use pcap on an interface that is up. We avoid that by use
# pcap via a veth pair (NetworkManager leaves them alone).
# Having a netdev in the host netns called "ethwe" might
# surprise people, so it is called $ROUTER_HOSTNETNS_IFNAME
# instead.
echo "--datapath $BRIDGE --iface $ROUTER_HOSTNETNS_IFNAME"
fi
echo "--datapath $DATAPATH"
}
router_opts_bridge() {
echo "--iface $CONTAINER_IFNAME"
}
router_opts_bridged_fastdp() {
router_opts_fastdp "$@"
}
######################################################################
# functions invoked through with_container_netns
######################################################################
setup_router_iface_fastdp() {
if [ -n "$WEAVE_PASSWORD" ] ; then
# See router_opts_fastdp
# No-op if already attached
if ip link show $LOCAL_IFNAME >/dev/null 2>&1 ; then
return 0
fi
connect_container_to_bridge $ROUTER_HOSTNETNS_IFNAME &&
ip link set $ROUTER_HOSTNETNS_IFNAME up
fi
true
}
setup_router_iface_bridge() {
@@ -708,6 +839,10 @@ setup_router_iface_bridge() {
netnsenter ip link set $CONTAINER_IFNAME up
}
setup_router_iface_bridged_fastdp() {
setup_router_iface_fastdp "$@"
}
attach() {
if [ -h "$PROCFS/$CONTAINER_PID/ns/net" -a -h "/proc/self/ns/net" -a "$(readlink $PROCFS/$CONTAINER_PID/ns/net)" = "$(readlink /proc/self/ns/net)" ] ; then
echo "Container is running in the host network namespace, and therefore cannot be" >&2
@@ -743,8 +878,10 @@ attach() {
# multicast packets. Since RFC1122 prohibits sending of ICMP
# errors for packets with multicast destinations, that causes
# packets larger than the PMTU to be dropped silently.
if ! netnsenter ip route show | grep '^224\.0\.0\.0/4' >/dev/null ; then
netnsenter ip route add 224.0.0.0/4 dev $CONTAINER_IFNAME mtu lock $MTU
if [ -z "$NO_MULTICAST_ROUTE" ] ; then
if ! netnsenter ip route show | grep '^224\.0\.0\.0/4' >/dev/null ; then
netnsenter ip route add 224.0.0.0/4 dev $CONTAINER_IFNAME mtu lock $MTU
fi
fi
}
@@ -956,12 +1093,11 @@ dns_args() {
NAME_ARG=""
HOSTNAME_SPECIFIED=
DNS_SEARCH_SPECIFIED=
WITH_DNS=
WITHOUT_DNS=
while [ $# -gt 0 ] ; do
case "$1" in
--with-dns)
WITH_DNS=1
echo "Warning: $1 is deprecated; it is on by default" >&2
;;
--without-dns)
WITHOUT_DNS=1
@@ -1042,7 +1178,7 @@ rewrite_etc_hosts() {
CONTAINERS_PATH=$(dirname $HOSTS)
MNT=/container
MNT_HOSTS=$MNT/$(basename $HOSTS)
CONTENTS="$(etc_hosts_contents $FQDN $@)"
CONTENTS="$(etc_hosts_contents $FQDN "$@")"
# rewrite /etc/hosts, unlinking the file (so Docker does not modify it again) but
# leaving it with valid contents...
docker run --rm --net=none --privileged \
@@ -1184,7 +1320,7 @@ ipam_cidrs() {
CONTAINER_ID="$2"
shift 2
ALL_CIDRS=""
IPAM_CIDRs=""
IPAM_CIDRS=""
# If no addresses passed in, select the default subnet
[ $# -gt 0 ] || set -- net:default
for arg in "$@" ; do
@@ -1240,8 +1376,6 @@ show_addrs() {
######################################################################
docker_client_args() {
CLIENT_TLS_ENABLED=""
[ -z "$DOCKER_CLIENT_TLS_VERIFY" ] || CLIENT_TLS_ENABLED=1
while [ $# -gt 0 ]; do
case "$1" in
-H|--host)
@@ -1251,9 +1385,6 @@ docker_client_args() {
-H=*|--host=*)
DOCKER_CLIENT_HOST="${1#*=}"
;;
-tls|--tls|-tlsverify|--tlsverify)
CLIENT_TLS_ENABLED=1
;;
esac
shift
done
@@ -1266,17 +1397,33 @@ tls_arg() {
PROXY_ARGS="$PROXY_ARGS $1 /home/weave/tls/$3.pem"
}
# TODO: Handle relative paths for args
# TODO: Handle args with spaces
host_arg() {
PROXY_HOST="$1"
if [ "$PROXY_HOST" != "${PROXY_HOST#unix://}" ]; then
host=$(dirname ${PROXY_HOST#unix://})
if [ "$host" = "${host#/}" ]; then
echo "When launching the proxy, unix sockets must be specified as an absolute path." >&2
exit 1
fi
PROXY_VOLUMES="$PROXY_VOLUMES -v /var/run/weave:/var/run/weave"
fi
PROXY_ARGS="$PROXY_ARGS -H $1"
}
proxy_parse_args() {
while [ $# -gt 0 ]; do
case "$1" in
-H)
PROXY_HOST="$2"
PROXY_ARGS="$PROXY_ARGS $1 $2"
host_arg "$2"
shift
;;
-H=*)
PROXY_HOST="${1#*=}"
PROXY_ARGS="$PROXY_ARGS $1"
host_arg "${1#*=}"
;;
-no-detect-tls|--no-detect-tls)
PROXY_TLS_DETECTION_DISABLED=1
;;
-tls|--tls|-tlsverify|--tlsverify)
PROXY_TLS_ENABLED=1
@@ -1315,12 +1462,13 @@ proxy_args() {
PROXY_VOLUMES=""
PROXY_ARGS=""
PROXY_TLS_ENABLED=""
PROXY_TLS_DETECTION_DISABLED=""
PROXY_HOST=""
proxy_parse_args "$@"
if [ -z "$PROXY_HOST" -a -n "$CLIENT_TLS_ENABLED" -a -z "$PROXY_TLS_ENABLED" ] ; then
if ! proxy_parse_args $(docker_tls_args) ; then
echo "When launching the proxy via TLS, -H and/or TLS options are required." >&2
if [ -z "$PROXY_TLS_ENABLED" ] ; then
if [ -z "$PROXY_TLS_DETECTION_DISABLED" ] && ! proxy_parse_args $(docker_tls_args) ; then
echo "Unable to auto-detect TLS configuration, --no-detect-tls or TLS options are required." >&2
exit 1
fi
fi
@@ -1330,10 +1478,10 @@ proxy_args() {
PROXY_HOST="unix:///var/run/weave/weave.sock"
;;
*)
PROXY_HOST="tcp://0.0.0.0:12375"
PROXY_HOST="tcp://0.0.0.0:$PROXY_PORT"
;;
esac
PROXY_ARGS="$PROXY_ARGS -H $PROXY_HOST"
host_arg "$PROXY_HOST"
fi
}
@@ -1467,20 +1615,19 @@ launch_router() {
fi
fi
if [ "$BRIDGE_TYPE" = fastdp ] ; then
if [ "$BRIDGE_TYPE" != bridge ] ; then
NETHOST_OPT="--net=host"
HTTP_IP=127.0.0.1
# In case there is a lingering veth-weave netdev
ip link del $ROUTER_HOSTNETNS_IFNAME >/dev/null 2>&1 || true
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.
ROUTER_CONTAINER=$(docker run --privileged -d --name=$CONTAINER_NAME \
-v /var/run/docker.sock:/var/run/docker.sock \
$(docker_sock_volume_mount) \
-p $PORT:$CONTAINER_PORT/tcp -p $PORT:$CONTAINER_PORT/udp \
${NETHOST_OPT:-$DNS_PORT_MAPPING} \
-e DOCKER_HOST \
-e WEAVE_PASSWORD \
-e WEAVE_CIDR=none \
$WEAVE_DOCKER_ARGS $IMAGE $COVERAGE_ARGS \
@@ -1490,7 +1637,7 @@ launch_router() {
--dns-effective-listen-address $DOCKER_BRIDGE_IP \
${NETHOST_OPT:+$DNS_ROUTER_OPTS} $NO_DNS_OPT \
--http-addr $HTTP_IP:$HTTP_PORT \
--docker-api "unix:///var/run/docker.sock" "$@")
"$@")
with_container_netns_or_die $CONTAINER_NAME setup_router_iface_$BRIDGE_TYPE
attach_router
}
@@ -1524,9 +1671,6 @@ attach_router() {
stop_router() {
stop $CONTAINER_NAME "Weave"
conntrack -D -p udp --dport $PORT >/dev/null 2>&1 || true
# remove the veth-weave netdev in a fastdp context
[ -d /sys/class/net/$BRIDGE -a ! -d /sys/class/net/$BRIDGE/bridge ] \
&& ip link del $ROUTER_HOSTNETNS_IFNAME >/dev/null 2>&1 || true
}
launch_proxy() {
@@ -1535,16 +1679,19 @@ launch_proxy() {
# when launching the weaveproxy container.
docker_client_args $DOCKER_CLIENT_ARGS
proxy_args "$@"
mkdir -p /var/run/weave
PROXY_CONTAINER=$(docker run --privileged -d --name=$PROXY_CONTAINER_NAME --net=host \
$PROXY_VOLUMES \
-v /var/run/docker.sock:/var/run/docker.sock \
$(docker_sock_volume_mount) \
-v /var/run/weave:/var/run/weave \
-v /proc:/hostproc \
-e PROCFS=/hostproc \
-e WEAVE_CIDR=none \
-e DOCKER_HOST \
-e DOCKER_BRIDGE \
-e WEAVE_DEBUG \
-e COVERAGE \
-e EXEC_IMAGE=$EXEC_IMAGE \
--entrypoint=/home/weave/weaveproxy \
$WEAVEPROXY_DOCKER_ARGS $EXEC_IMAGE $COVERAGE_ARGS $PROXY_ARGS)
wait_for_status $PROXY_CONTAINER_NAME http_call_unix $PROXY_CONTAINER_NAME status.sock
@@ -1559,12 +1706,20 @@ launch_plugin() {
PLUGIN_CONTAINER=$(docker run --privileged -d --name=$PLUGIN_CONTAINER_NAME \
--restart=always \
--net=host \
-v /var/run/docker.sock:/var/run/docker.sock \
$(docker_sock_volume_mount) \
-v /run/docker/plugins:/run/docker/plugins \
-e DOCKER_HOST \
$PLUGIN_IMAGE "$@")
}
remove_plugin_network() {
docker run --rm --net=host \
-v /var/run/docker.sock:/var/run/docker.sock \
$PLUGIN_IMAGE --remove-network
}
stop_plugin() {
remove_plugin_network
stop $PLUGIN_CONTAINER_NAME "Plugin"
}
@@ -1616,14 +1771,14 @@ deprecation_warnings() {
case "$1" in
-password|-password=*)
deprecation_warning $1 "--password"
[ "$1" == "-password" ] && shift
[ "$1" = "-password" ] && shift
;;
--password)
shift
;;
-nickname|-nickname=*)
deprecation_warning $1 "--nickname"
[ "$1" == "-nickname" ] && shift
[ "$1" = "-nickname" ] && shift
;;
--nickname)
shift
@@ -1633,21 +1788,21 @@ deprecation_warnings() {
;;
-iprange|--iprange|-iprange=*|--iprange=*)
deprecation_warning $1 "--ipalloc-range"
[ ${1#--} == "iprange" ] && shift
[ ${1#--} = "iprange" ] && shift
;;
--ipalloc-range)
shift
;;
-ipsubnet|--ipsubnet|-ipsubnet=*|--ipsubnet=*)
deprecation_warning $1 "--ipalloc-default-subnet"
[ ${1#--} == "ipsubnet" ] && shift
[ ${1#--} = "ipsubnet" ] && shift
;;
--ipalloc-default-subnet)
shift
;;
-initpeercount|--initpeercount|-initpeercount=*|--initpeercount=*)
deprecation_warning $1 "--init-peer-count"
[ ${1#--} == "initpeercount" ] && shift
[ ${1#--} = "initpeercount" ] && shift
;;
--init-peer-count)
shift
@@ -1690,6 +1845,9 @@ EOF
fi
create_bridge --without-ethtool
;;
bridge-type)
detect_bridge_type && echo $BRIDGE_TYPE
;;
launch)
deprecation_warnings "$@"
check_not_running $CONTAINER_NAME $BASE_IMAGE
@@ -1697,6 +1855,10 @@ EOF
COMMON_ARGS=$(common_launch_args "$@")
launch_router "$@"
launch_proxy $COMMON_ARGS
if [ -z $WEAVE_NO_PLUGIN ] && check_docker_server_api_version 1.21 ; then
check_not_running $PLUGIN_CONTAINER_NAME $BASE_PLUGIN_IMAGE
launch_plugin $COMMON_ARGS
fi
;;
launch-router)
deprecation_warnings "$@"
@@ -1737,7 +1899,7 @@ EOF
;;
connect)
[ $# -gt 0 ] || usage
if [ "$1" == "--replace" ] ; then
if [ "$1" = "--replace" ] ; then
replace="-d replace=true"
shift
fi
@@ -1775,13 +1937,13 @@ EOF
fi
if [ -z "$SUB_STATUS" ] && check_running $PROXY_CONTAINER_NAME 2>/dev/null && PROXY_ADDRS=$(proxy_addrs) ; then
echo
echo " Service: proxy"
echo " Address: $PROXY_ADDRS"
echo " Service: proxy"
echo " Address: $PROXY_ADDRS"
fi
if [ -z "$SUB_STATUS" ] && check_running $PLUGIN_CONTAINER_NAME 2>/dev/null ; then
echo
echo " Service: plugin"
echo " DriverName: weave"
echo " Service: plugin"
echo " DriverName: weave"
fi
[ -n "$SUB_STATUS" ] || echo
[ $res -eq 0 ]
@@ -1798,10 +1960,21 @@ EOF
dns_args "$@"
shift $(dns_arg_count "$@")
REWRITE_HOSTS=1
if [ "$1" = "--no-rewrite-hosts" ] ; then
REWRITE_HOSTS=
NO_MULTICAST_ROUTE=
while [ $# -gt 0 ]; do
case "$1" in
--no-rewrite-hosts)
REWRITE_HOSTS=
;;
--no-multicast-route)
NO_MULTICAST_ROUTE=1
;;
*)
break
;;
esac
shift
fi
done
collect_cidr_args "$@"
shift $CIDR_ARG_COUNT
CONTAINER=$(docker run -e WEAVE_CIDR=none $DNS_ARGS -d "$@")
@@ -1838,6 +2011,7 @@ EOF
attach)
DNS_EXTRA_HOSTS=
REWRITE_HOSTS=
NO_MULTICAST_ROUTE=
collect_cidr_args "$@"
shift $CIDR_ARG_COUNT
while [ $# -gt 0 ]; do
@@ -1855,6 +2029,9 @@ EOF
--add-host=*)
DNS_EXTRA_HOSTS="${1#*=} $DNS_EXTRA_HOSTS"
;;
--no-multicast-route)
NO_MULTICAST_ROUTE=1
;;
*)
break
;;
@@ -1958,6 +2135,7 @@ EOF
;;
stop)
[ $# -eq 0 ] || usage
stop_plugin
stop_router
stop_proxy
;;
@@ -1975,9 +2153,10 @@ EOF
;;
reset)
[ $# -eq 0 ] || usage
remove_plugin_network || echo "WARNING: docker may hang in this state; re-launch weave and remove attached containers before shutting down" >&2
warn_if_stopping_proxy_in_env
call_weave DELETE /peer >/dev/null 2>&1 || true
for NAME in $CONTAINER_NAME $PROXY_CONTAINER_NAME ; do
for NAME in $PLUGIN_CONTAINER_NAME $CONTAINER_NAME $PROXY_CONTAINER_NAME ; do
docker stop $NAME >/dev/null 2>&1 || true
docker rm -f $NAME >/dev/null 2>&1 || true
done