Compare commits

..
295 Commits
Author SHA1 Message Date
Adam Harrison 1228dbddb9 Add release v1.4.4 2016-02-09 11:20:51 +00:00
Adam Harrison c00b00d623 Merge pull request #1963 from weaveworks/issues/1959-dont-remove-network
Don't remove weave network on plugin shutdown because it bricks Docker 1.10
2016-02-09 10:53:13 +00:00
Bryan Boreham 7bbc9f1b82 Don't remove the weave network on shutdown because it bricks Docker 1.10 2016-02-09 10:02:06 +00:00
Matthias Radestock 15a92f9c92 Merge pull request #1961 from weaveworks/issues/1956-any-iface
Wait for multicast route on any interface

Fixes #1956.
2016-02-09 09:29:05 +00:00
Bryan Boreham 9386c01845 Wait for multicast route on any interface, to match weave script
which only adds a route if there isn't one already
2016-02-08 14:04:25 +00:00
Adam Harrison 5232a9af6b Add release v1.4.3 2016-02-02 16:35:32 +00:00
Adam Harrison c393b23b31 Merge pull request #1918 from weaveworks/issues/1906-plugin-no-weave
Return an error when weave isn't running, on RequestPool
2016-02-02 15:54:39 +00:00
Adam Harrison 955fbca44a Merge pull request #1943 from weaveworks/1942-mcast-iface-up
don't set the interface 'up' before it is fully configured
2016-02-01 15:29:46 +00:00
Matthias Radestock 18c26e9fbb don't set the interface 'up' before it is fully configured
This was causing container entrypoints to execute too early,
e.g. before the interface had been given an IP address. But only when
running with --no-multicast-route, since otherwise weavewait is
waiting for the multicast route to appear.

Fixes #1942.
2016-02-01 12:04:15 +00:00
Matthias Radestock b54efbde69 fix typo in comment 2016-01-30 11:15:57 +00:00
Matthias Radestock be9d64b2cc Merge pull request #1936 from weaveworks/issues/1934-rm-socket
Remove Unix domain sockets on shutdown

Fixes #1934.
2016-01-29 15:46:24 +00:00
Matthias Radestock c6f356a167 Merge pull request #1935 from weaveworks/issues/1757-volumes-container
Reduce docker volumes for weavewait to just one set per release

Fixes #1757.
2016-01-29 15:24:42 +00:00
Matthias Radestock eae45ebc94 Merge pull request #1921 from weaveworks/issues/1745-plugin-ps
Make weave script scan veths with prefix 'ethwe'

Fixes #1745
2016-01-29 12:45:10 +00:00
Bryan Boreham ea50b3d5cc Create a different weavevolumes container for each Weave version.
They will be named 'weavevolumes-v1.4.3', 'weavevolumes-latest', etc.
Use a label to identify these containers so they can all be removed on `weave reset`.
2016-01-29 12:22:22 +00:00
Bryan Boreham 3297b93edb Smoke-test 'weave ps' and restarting when using plugin 2016-01-29 12:13:00 +00:00
Bryan Boreham 6d0e867ad0 Capture addresses from veths with prefix 'ethwe'
This is to address the plugin case, where the veth will have a name like 'ethwe0'.
Previously we only looked at the device named exactly 'ethwe'.
Covers `weave ps` and reclaim of addresses for IPAM and WeaveDNS.
2016-01-29 12:13:00 +00:00
Bryan Boreham 1cea9c1c87 Extract function to detect container running in host namespace 2016-01-29 12:13:00 +00:00
Bryan Boreham 3320a9e7d4 Re-organise code so we can clean up using defer and return error codes to top level 2016-01-29 11:41:24 +00:00
Bryan Boreham 9b90a938f5 Remove Unix domain sockets on shutdown 2016-01-28 18:26:34 +00:00
Matthias Radestock c6d0149ba6 add missing dependency
The plugin image derives from the weaveexec image.
2016-01-28 17:50:26 +00:00
Bryan Boreham 598efe5d1a Reduce docker volumes for weavewait to just one set.
- Create an auxilliary container 'weavevolumes' to hold the '/w/w'
  files, and map into that instead of creating new volumes every time.
- Show "/bin/false" as its command in `docker ps` instead of
"/home/weave/sigproxy".
- Remove volumes when cleaning up smoketest containers.
2016-01-28 15:57:22 +00:00
Matthias Radestock 3679065258 cope with nil route.Dst
Fixes #1932.
2016-01-27 22:02:07 +00:00
Matthias Radestock 4649f1158a Merge pull request #1926 from weaveworks/issues/1905-fix-negative-fragment
Negative fragment fix

Fixes #1905.
2016-01-26 16:56:03 +00:00
Adam Harrison de17a44f35 Fix fragmentation test
The assumptions on which this test was based were violated by the
introduction of fast datapath. A new rule is required to block fast
datapath traffic, to ensure that sleeve is selected for forwarding; the
rules must also be moved from the FORWARD chain to INPUT as the router
now runs in the host netns (NB we use INPUT instead of OUTPUT as the
latter results in sleeve receiving an 'operation not permitted' udpv4
write error when it tries to send a packet that is too big, rather than
the packet just being silently dropped as required)
2016-01-25 10:58:31 +00:00
Adam Harrison 52ce4df18e Increase the lowest possible good PMTU
The current minimum PMTU we can detect is eight bytes, which, being
lower than the minimum IPv4 MTU of 68 bytes, can cause an exception in
the sleeve packet fragmentation implementation. We increase that minimum
to 552, the lowest PMTU value that Linux will cache.
2016-01-25 10:53:10 +00:00
Matthias Radestock f1ed5bf838 Merge branch 'latest_release_doc_updates' into 1.4 2016-01-22 13:35:27 +00:00
Matthias Radestock 256a801d16 typo 2016-01-22 13:34:59 +00:00
Matthias Radestock c416cf8da5 tidying of plugin docs 2016-01-22 13:27:22 +00:00
Matthias Radestock 75e812a061 Merge branch 'latest_release_doc_updates' into 1.4 2016-01-21 12:30:14 +00:00
Matthias Radestock 4cc7b01940 Merge pull request #1907 from weaveworks/systemd-timeout
Suggest disabling systemd timeout if using Weave Net plugin
2016-01-21 12:29:42 +00:00
Matthias Radestock a2ff7e5981 Merge pull request #1919 from weaveworks/issues/1900-dont-stop-stoppin
Warn, don't error, if unable to remove network in 'weave stop'

Fixes #1900.
2016-01-21 11:22:21 +00:00
Bryan Boreham cda98a6375 Merge pull request #1917 from weaveworks/issues/1911-fix-unexpected-eof
Fix unexpected EOF with Docker 1.10-rc1 and proxy --rewrite-inspect
2016-01-21 10:15:20 +00:00
Matthias Radestock 3b7a754b0a Merge branch 'latest_release_doc_updates' into 1.4 2016-01-20 18:56:26 +00:00
Anita Buehrle c01c6bcb91 fixed logo image in header 2016-01-20 12:48:31 -06:00
Bryan Boreham e47360f8dc Warn, don't error, if unable to remove network in 'weave stop' 2016-01-20 16:18:09 +00:00
Bryan Boreham 6035b54277 Send back an http error code in case of error, so Docker is clear something went wrong 2016-01-20 15:36:42 +00:00
Bryan Boreham a238130613 Give more explanation if we can't find weave to talk to 2016-01-20 15:36:34 +00:00
Bryan Boreham 998be42a52 Don't crash if no default subnet available 2016-01-20 15:36:25 +00:00
Adam Harrison 14944da396 Ignore stderr in proxy_addrs 2016-01-20 14:24:59 +00:00
Adam Harrison a3974c2aeb Abort inspect exec rewrite unless Containers node is present 2016-01-20 14:24:28 +00:00
Bryan Boreham 13ec517f94 Merge pull request #1910 from weaveworks/1909-forEachRoute-panic
cope with errors during route traversal
2016-01-20 14:05:10 +00:00
Matthias Radestock 43419815f4 bypass netlink.LinkByIndex
since we are seeing some cases where it returns "Link not found" when
the link is in fact there.
2016-01-19 16:36:16 +00:00
Matthias Radestock b5256f7b71 refactor: inline
This eliminates the unnecessary indirection via interface names,
instead identifying the interface by its index.
2016-01-19 16:36:16 +00:00
Matthias Radestock 0caa9dd652 cope with errors during route traversal
Fixes #1909.
2016-01-19 15:28:31 +00:00
Bryan Boreham 5e1e8ec543 Suggest user disables systemd timeout if using plugin 2016-01-18 11:45:17 +00:00
Adam Harrison c391092ea6 Add release v1.4.2 2016-01-14 11:40:17 +00:00
Adam Harrison b3a29bec1b Merge pull request #1886 from weaveworks/issues/1869-leave-plugin
Allow plugin to keep running on 'weave launch'
2016-01-13 11:08:54 +00:00
Bryan Boreham b055c9a4a2 Fool-proofing 2016-01-13 10:33:49 +00:00
Bryan Boreham 3daceac668 Allow pre-existing plugin on 'weave launch-plugin' 2016-01-13 10:05:02 +00:00
Matthias Radestock 4611e8825a Merge branch 'latest_release_doc_updates' into 1.4 2016-01-13 09:25:34 +00:00
Matthias Radestock 00e4729533 Merge pull request #1872 from weaveworks/issues/1871-address-docs
Documentation on IP addresses, routes and networks
2016-01-13 09:24:31 +00:00
Bryan Boreham 517a2e0e0a Fix nonzero exit code for a pre-existing plugin on launch;
do this purely for the 'plugin running' case, not 'restarting' etc.
2016-01-12 17:06:38 +00:00
Bryan Boreham bb8c7c599c Revert "don't launch anything when plugin is running"
This reverts commit 3ade796e8d.
2016-01-12 16:51:49 +00:00
Bryan Boreham 0d0fc39236 More review feedback 2016-01-12 15:35:34 +00:00
Adam Harrison 302a5a4efc Merge pull request #1884 from weaveworks/issues/1882-check-after-subscribe
Fix race condition in weavewait
2016-01-12 14:28:03 +00:00
Bryan Boreham 3b29e8e6f2 Check for existing interfaces and routes after subscribing to receive new ones 2016-01-12 12:27:55 +00:00
Bryan Boreham a1dff70ba2 Review feedback 2016-01-11 11:55:11 +00:00
Matthias Radestock 12671be260 Merge branch 'latest_release_doc_updates' into 1.4 2016-01-11 11:49:22 +00:00
Bryan Boreham 9a4adccec6 Remove inconsistent information about restart policy 2016-01-11 11:20:40 +00:00
Bryan Boreham f90390974a Add page explaining IP addresses, networks and routes 2016-01-11 10:30:37 +00:00
Matthias Radestock f598ae297b Merge branch 'latest_release_doc_updates' into 1.4 2016-01-08 18:14:23 +00:00
Bryan Boreham 4b0fe502f0 Document the --no-multicast-route flag 2016-01-08 17:12:20 +00:00
Matthias Radestock e4295a2da9 don't attach rewrite_etc_hosts helper container to weave
...since that is a) inefficent and b) can block when IPAM hasn't had
consensus.

We can only end up in this situation when DOCKER_HOST is pointing at
the weave proxy and we are running `weave run|attach` with
`--local`. So it's very much an edge case.

Fixes #1858.
2016-01-07 14:32:52 +00:00
Matthias Radestock 216a0fe4fc refactor: WEAVE_CIDR=none is redundant when --net=host 2016-01-07 14:29:51 +00:00
Matthias Radestock 80770d39e5 oops 2016-01-04 10:53:41 +00:00
Matthias Radestock beb15fa32c don't fail weave launch when plugin is disabled
Fixes #1851.
2016-01-04 10:35:44 +00:00
Matthias Radestock 0adf7626d0 Merge branch 'latest_release_doc_updates' into 1.4 2016-01-04 09:45:56 +00:00
Matthias Radestock 9c17fe259c add ToC to build instructions 2016-01-04 09:41:39 +00:00
Matthias Radestock ecab4b4716 correct image names 2016-01-04 09:35:17 +00:00
Matthias Radestock 7660ad761a oops
I broke this in 370212609.

Fixes #1848.
2016-01-03 16:42:39 +00:00
Matthias Radestock c8ff2de3b3 properly deal with unavailable docker server api version
Fixes #1845.
2015-12-31 12:51:16 +00:00
Matthias Radestock 24a2e43547 Merge pull request #1841 from weaveworks/1840-weave-stop-plugin-complaint
don't complain in `weave stop` when plugin is not enabled

Fixes #1840.
2015-12-31 12:32:05 +00:00
Matthias Radestock a9d212c888 don't fail on missing docker_tls_args
instead just don't auto-detect TLS args in that case.

This makes `weave --local launch` work (again).

Fixes #1844.
2015-12-31 10:43:46 +00:00
Matthias Radestock c1b1276aaf refactor: simplify 2015-12-31 10:16:37 +00:00
Matthias Radestock 370212609c Show expected error message when docker_tls_args fails.
And also make the error message more meaningful.

Fixes #1843.
2015-12-31 10:05:41 +00:00
Matthias Radestock 281937cf82 document weave launch-router --trusted-subnets
and eliminate duplication

Fixes #1842.
2015-12-31 01:09:51 +00:00
Matthias Radestock d11b6e8075 don't complain in weave stop when plugin is not enabled 2015-12-30 23:40:27 +00:00
Matthias Radestock 7709c6d0bd Merge branch 'latest_release_doc_updates' into 1.4 2015-12-30 20:26:06 +00:00
Matthias Radestock ef3e478506 Merge pull request #1804 from weaveworks/1774-trouble
Update 'reboots' info

Fixes #1774.
2015-12-30 19:51:09 +00:00
Matthias Radestock 93eef26d0b reword 2015-12-30 19:38:13 +00:00
Matthias Radestock 14d78cf08c Update 'reboots' info 2015-12-30 19:32:18 +00:00
Matthias Radestock e96a63f550 Merge branch 'latest_release_doc_updates' into 1.4 2015-12-30 19:14:33 +00:00
Matthias Radestock 7a5b69f850 cosmetic 2015-12-30 19:13:57 +00:00
Matthias Radestock a71d2a1682 move things around on troubleshooting page
- level 2 headings for top-level instead of level 3
- put all status reporting as L3s under one L2 heading
- add 'Reboots' to ToC
- move 'Stopping weave' into its own section and and move that
2015-12-30 19:06:04 +00:00
Bryan Boreham 257b1a5353 Add the plugin to the list of images to tag and push 2015-12-23 11:00:58 +00:00
Bryan Boreham b45ac75735 Merge pull request #1825 from weaveworks/1824-idempotent-env-restore
make `eval $(weave env)` idempotent; LGTM.  Fixes #1824
2015-12-23 10:48:35 +00:00
Matthias Radestock c7c669fd11 make eval $(weave env) idempotent
so that `weave $(env --restore)` does the right thing regardless of
how many times `eval $(weave env)` was invoked previously.

Fixes #1824.
2015-12-23 09:08:56 +00:00
Bryan Boreham ba266ef8d3 Add release v1.4.1 2015-12-22 17:25:47 +00:00
Bryan Boreham f434fad3be Merge pull request #1822 from weaveworks/1820-unix-only
Only pass $DOCKER_HOST if it is a Unix socket - fixes #1820
2015-12-22 17:16:40 +00:00
Bryan Boreham bab758ebf9 Revert "Launch the proxy via unix socket in last test"
- since we won't now be copying $DOCKER_HOST

This reverts commit d8cfbc4e08.
2015-12-22 17:01:04 +00:00
Bryan Boreham 800f4663b6 Only pass through $DOCKER_HOST in exec_remote() if it is a Unix socket;
a TCP socket may be secured by TLS, in which case we can't use it.
2015-12-22 17:01:04 +00:00
Matthias Radestock b1b62afa90 Merge pull request #1812 from weaveworks/1810-warn-with-dns
Issue a warning on --with-dns option to proxy, because it was removed

Fixes #1810
2015-12-22 15:12:27 +00:00
Bryan Boreham 6b90ef7078 Clarify message 2015-12-22 14:56:40 +00:00
Matthias Radestock 2ed4b45c24 Merge pull request #1817 from weaveworks/1815-docker-version
Check we have Docker 1.9 or newer before trying to remove a network

Fixes #1815.
2015-12-22 14:01:55 +00:00
Matthias Radestock 47ba9748b4 Merge pull request #1814 from weaveworks/1778-check-running
Handle weave containers in 'restarting' state

Fixes #1778.
2015-12-22 13:49:19 +00:00
Bryan Boreham 077ad4208f Merge pull request #1813 from weaveworks/1797-plugin-version
Show plugin version; Fixes #1797
2015-12-22 13:12:38 +00:00
Bryan Boreham c19edec364 Add 'weave stop-xxx' instructions for 'already running' case 2015-12-22 13:04:06 +00:00
Bryan Boreham 0c2aa494ae Reduce duplication 2015-12-22 13:03:04 +00:00
Bryan Boreham 73e0a6bb0a Inform user about weave containers in state 'restarting' 2015-12-22 13:01:00 +00:00
Bryan Boreham abf1db0045 Merge pull request #1819 from weaveworks/1818-dont-launch-when-plugin-running
don't launch anything when plugin is running; fixes #1818 . LGTM.
2015-12-22 12:55:57 +00:00
Matthias Radestock b40b78ece0 refactor docker_sock_volume_mount() 2015-12-22 12:47:36 +00:00
Matthias Radestock 3ade796e8d don't launch anything when plugin is running
...just as we don't launch anything if either the router or proxy are
running.

Fixes #1818.
2015-12-22 12:30:53 +00:00
Bryan Boreham 7c2e808924 Check we have Docker 1.9 or newer before trying to remove a network 2015-12-22 12:10:48 +00:00
Bryan Boreham 7d2227c5a4 Add plugin version to 'weave version' 2015-12-22 10:50:02 +00:00
Bryan Boreham 8a517d5c7c Issue a warning on --with-dns option to proxy, because it was removed 2015-12-22 10:00:06 +00:00
Bryan Boreham 48658f011f Merge pull request #1805 from weaveworks/script-indentation
Fix indentation of weave script
2015-12-18 09:53:12 +00:00
Bryan Boreham 4ebb13fa94 Fix indentation of netcheck if-statement 2015-12-17 16:23:42 +00:00
Adam Harrison ff9a6b91a4 Merge branch 'latest_release_doc_updates' into 1.4 2015-12-17 13:07:48 +00:00
Adam Harrison 86fc37a968 Merge pull request #1799 from weaveworks/plugin-docs
Extend plugin docs
2015-12-17 12:24:12 +00:00
Bryan Boreham 37ae169323 Merge remote-tracking branch 'origin/master' into latest_release_doc_updates 2015-12-17 11:48:23 +00:00
Bryan Boreham 1ce0df4e94 Explain multiple networks 2015-12-17 11:42:41 +00:00
Bryan Boreham db5ba61915 Improve formatting 2015-12-17 11:41:44 +00:00
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 1d9ef76080 Remove mention of --with-dns option; see #1748 and #1754 2015-12-12 13:58:14 +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
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
Bryan Boreham 6dcc42cdeb Merge branch '1.3' 2015-11-20 10:33:38 +00:00
Matthias Radestock 53749156a7 Merge branch '1.3' 2015-11-19 20:58:14 +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
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
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 e230eeb486 Use upstream netlink code 2015-11-12 13:08:56 +00:00
79 changed files with 2969 additions and 823 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.*
+84
View File
@@ -1,3 +1,87 @@
## Release 1.4.4
Fixing a rather serious issue that slipped through our preparations
for Docker 1.10:
* Restarting Docker or rebooting your machine while the Weave Net
plugin is running causes Docker 1.10.0 to fail to start #1959/#1963
Also one other small fix:
* Avoid a hang when trying to use plugin and proxy at the same time
#1956/#1961
## Release 1.4.3
Preparing for Docker 1.10, plus some bug-fixes.
* Avoid hang in Docker v1.10 on `docker volume ls` after `weave stop`,
`weave stop-plugin` or `weave reset` #1934/#1936
* Fix "unexpected EOF" from Docker 1.10 on `docker exec` with Weave
proxy `--rewrite-inspect` enabled #1911/#1917
* Avoid losing DNS entries and potentially double-allocating IP
Addresses allocated via plugin, on router restart; also extend
`weave ps` to show IP addresses allocated via plugin #1745/#1921
* Stop creating lots of copies of `weavewait` program in Docker
volumes #1757/#1935
* Prevent container starting prematurely when proxy in
`--no-multicast-route` mode #1942/#1943
* Log error message from plugin rather than crashing when weave not
running #1906/#1918
* Warn, don't error, if unable to remove plugin network in 'weave
stop', to avoid breaking the usual upgrade or config change process
#1900/#1919
* Don't crash if network conditions suggest only very small packets
will get through #1905/#1926
* Cope with unexpected errors during route traversal when starting
container via proxy #1909/#1910/#1932
## Release 1.4.2
Bug-fixes and minor improvements.
* A race condition in weavewait that would occasionally hang
containers at startup #1882/#1884
* Having the plugin auto-restart prevents successful `weave launch` on
reboot #1869
* Work round weave router failure on CoreOS 4.3 caused by kernel bug
#1854
* `weave launch` would exit with error code on docker <1.9 #1851
* Running `eval $(weave env)` multiple times would break `eval $(weave
env --restore)` #1824/#1825
* Don't complain in `weave stop` about "Plugin is not running" when
plugin is not enabled #1840/#1841
* `weave --local launch` would fail if utility program
`docker_tls_args` could not be found #1844
* Improved error reporting when TLS arg detection fails #1843
* Improve error reporting when docker isn't running #1845
* Add `--trusted-subnets` usage to `weave` script #1842
* weave run can hang under rare combinations of options #1858
## Release 1.4.1
This is a bug-fix release to cover a few issues that came up since the
release of 1.4.0.
* Weave would fail to launch when `$DOCKER_HOST` was set to a TCP socket secured with TLS #1820/#1822
* Weave would fail to stop when run against Docker pre-1.9 #1815/#1817
* Issue a warning instead of failing on `--with-dns` option to proxy, which was removed #1810/#1812
* Make `weave version` show the plugin version #1797/#1813
* Make `weave launch` show when the a container is restarting #1778/#1814
* Make `weave launch` fail if the plugin is running, for consistency with router and proxy. #1818/#1819
More details in the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.4.1).
## 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**
+36 -17
View File
@@ -1,4 +1,4 @@
PUBLISH=publish_weave publish_weaveexec
PUBLISH=publish_weave publish_weaveexec publish_plugin
.DEFAULT: all
.PHONY: all update tests lint publish $(PUBLISH) clean clean-bin prerequisites build travis run-smoketests
@@ -14,21 +14,26 @@ 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
@@ -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) $(WEAVEEXEC_UPTODATE)
$(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)
+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
+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}
+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)
+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
+24 -17
View File
@@ -37,6 +37,7 @@ type Config struct {
Password []byte
ConnLimit int
PeerDiscovery bool
TrustedSubnets []*net.IPNet
}
type Router struct {
@@ -122,15 +123,6 @@ type TopologyGossipData struct {
update PeerNameSet
}
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) GossipData {
names := make(PeerNameSet)
for name := range d.update {
@@ -146,6 +138,15 @@ 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 (router *Router) OnGossipUnicast(sender PeerName, msg []byte) error {
return fmt.Errorf("unexpected topology gossip unicast: %v", msg)
}
@@ -172,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
}
@@ -189,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
}
+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}
+39 -54
View File
@@ -5,28 +5,34 @@ 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) {
ch := make(chan netlink.LinkUpdate)
done := make(chan struct{})
defer close(done)
if err := netlink.LinkSubscribe(ch, done); err != nil {
return nil, err
}
// check for currently-existing interface after subscribing, to avoid race
if iface, err := findInterface(ifaceName); err == nil {
return iface, nil
}
netlinkReceiveUntil(s, matchIfName(ifaceName))
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,21 +41,37 @@ 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)
iface, err := ensureInterface(ifaceName)
if err != nil {
return nil, err
}
defer s.Close()
iface, err := ensureInterface(s, ifaceName)
if err != nil {
ch := make(chan netlink.RouteUpdate)
done := make(chan struct{})
defer close(done)
if err := netlink.RouteSubscribe(ch, done); err != nil {
return nil, err
}
dest := net.IPv4(224, 0, 0, 0)
if CheckRouteExists(ifaceName, dest) {
return iface, err
check := func(route netlink.Route) bool {
return route.Dst != nil && route.Dst.IP.Equal(dest)
}
netlinkReceiveUntil(s, matchRoute(ifaceName, dest))
return iface, err
// check for currently-existing route after subscribing, to avoid race
routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
if err != nil {
return nil, err
}
for _, route := range routes {
if check(route) {
return iface, nil
}
}
for update := range ch {
if check(update.Route) {
return iface, nil
}
}
// should never get here
return iface, nil
}
func findInterface(ifaceName string) (iface *net.Interface, err error) {
@@ -61,40 +83,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
}
}
+12 -70
View File
@@ -3,16 +3,14 @@ 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
// routes on this host. This is the same approach taken by Docker.
func CheckNetworkFree(subnet *net.IPNet, ignoreIfaceNames map[string]struct{}) error {
return forEachRoute(ignoreIfaceNames, func(name string, route netlink.Route) error {
return forEachRoute(ignoreIfaceNames, func(route netlink.Route) error {
if route.Dst != nil && overlaps(route.Dst, subnet) {
return fmt.Errorf("Network %s overlaps with existing route %s on host.", subnet, route.Dst)
}
@@ -28,7 +26,7 @@ func overlaps(n1, n2 *net.IPNet) bool {
// For a specific address, we only care if it is actually *inside* an
// existing route, because weave-local traffic never hits IP routing.
func CheckAddressOverlap(addr net.IP, ignoreIfaceNames map[string]struct{}) error {
return forEachRoute(ignoreIfaceNames, func(name string, route netlink.Route) error {
return forEachRoute(ignoreIfaceNames, func(route netlink.Route) error {
if route.Dst != nil && route.Dst.Contains(addr) {
return fmt.Errorf("Address %s overlaps with existing route %s on host.", addr, route.Dst)
}
@@ -36,80 +34,24 @@ func CheckAddressOverlap(addr net.IP, ignoreIfaceNames map[string]struct{}) erro
})
}
func forEachRoute(ignoreIfaceNames map[string]struct{}, check func(name string, r netlink.Route) error) error {
func forEachRoute(ignoreIfaceNames map[string]struct{}, check func(r netlink.Route) error) error {
ignoreIfaceIndices := make(map[int]struct{})
for ifaceName := range ignoreIfaceNames {
if iface, err := net.InterfaceByName(ifaceName); err == nil {
ignoreIfaceIndices[iface.Index] = struct{}{}
}
}
routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
if err != nil {
return err
}
for _, route := range routes {
link, err := netlink.LinkByIndex(route.LinkIndex)
if err == nil {
if _, found := ignoreIfaceNames[link.Attrs().Name]; found {
continue
}
if _, found := ignoreIfaceIndices[route.LinkIndex]; found {
continue
}
if err := check(link.Attrs().Name, route); err != nil {
if err := check(route); err != nil {
return err
}
}
return nil
}
func CheckRouteExists(ifaceName string, dest net.IP) bool {
found := false
forEachRoute(map[string]struct{}{}, func(name string, route netlink.Route) error {
if name == ifaceName && route.Dst.IP.Equal(dest) {
found = true
}
return nil
})
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).
+69
View File
@@ -0,0 +1,69 @@
package ipamplugin
import (
"fmt"
"net"
"github.com/docker/libnetwork/ipamapi"
"github.com/docker/libnetwork/netlabel"
godocker "github.com/fsouza/go-dockerclient"
"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) {
addr, err := client.GetContainerIP(WeaveContainer)
if _, ok := err.(*godocker.NoSuchContainer); ok {
return "", fmt.Errorf("%s container is not present. Have you launched it?", WeaveContainer)
}
return addr, err
}
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)
if err != nil {
return "", nil, nil, 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)
}
+299
View File
@@ -0,0 +1,299 @@
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{}) {
w.WriteHeader(http.StatusInternalServerError)
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"]
+167
View File
@@ -0,0 +1,167 @@
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())
err = run(dockerClient, address, meshAddress, meshNetworkName, nameserver, noMulticastRoute)
if err != nil {
Log.Fatal(err)
}
}
func run(dockerClient *docker.Client, address, meshAddress, meshNetworkName, nameserver string, noMulticastRoute bool) error {
endChan := make(chan error, 1)
if address != "" {
globalListener, err := listenAndServe(dockerClient, address, nameserver, noMulticastRoute, endChan, "global", false)
if err != nil {
return err
}
defer os.Remove(address)
defer globalListener.Close()
}
if meshAddress != "" {
meshListener, err := listenAndServe(dockerClient, meshAddress, nameserver, noMulticastRoute, endChan, "local", true)
if err != nil {
return err
}
defer os.Remove(meshAddress)
defer meshListener.Close()
}
if meshNetworkName != "" {
if err := createNetwork(dockerClient, meshNetworkName, meshAddress); err != nil {
return err
}
}
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)
return nil
case err := <-endChan:
return err
}
}
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) error {
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 {
return fmt.Errorf("Error creating network: %s", err)
}
}
return nil
}
+2 -3
View File
@@ -4,8 +4,6 @@ MAINTAINER Weaveworks Inc <help@weave.works>
LABEL works.weave.role=system
WORKDIR /home/weave
VOLUME /w
VOLUME /w-noop
ENTRYPOINT ["/home/weave/sigproxy", "/home/weave/weave"]
RUN apk add --update \
@@ -18,8 +16,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" "$@"
+19 -7
View File
@@ -12,14 +12,16 @@ 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"}
withDNS bool
)
c.Version = version
@@ -38,8 +40,9 @@ func main() {
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(&withDNS, []string{"#-with-dns", "#w"}, false, "option removed")
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 +50,24 @@ 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:], " "))
if withDNS {
Log.Warning("--with-dns option has been removed; DNS is on by default")
}
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
+4
View File
@@ -22,6 +22,10 @@ func (i *inspectExecInterceptor) InterceptResponse(r *http.Response) error {
return err
}
if _, ok := exec["Container"]; !ok {
return nil
}
container, err := exec.Object("Container")
if err != nil {
return err
+132 -20
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) {
@@ -86,9 +91,11 @@ func NewProxy(c Config) (*Proxy, error) {
// 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.18")
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
@@ -917,7 +917,7 @@ func (fwd *sleeveForwarder) processSendError(err error) error {
return nil
}
fwd.mtuHighestGood = 8
fwd.mtuHighestGood = 552
fwd.mtuLowestBad = mtu + 1
fwd.mtuCandidate = mtu
fwd.mtuTestsSent = 0
+1 -1
View File
@@ -2,7 +2,7 @@
<div class="navbar navbar-inverse">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="http://weave.works/"><img src="http://weave.works/img/weave-logo.png"></a>
<a class="navbar-brand" href="http://weave.works/"><img src="http://www.weave.works/wp-content/uploads/2015/11/weave-logo-nav.png" class=" img-responsive" align="left"></a>
</div>
<div class="nav-wrapper">
<ul class="nav navbar-nav navbar-right">
+9 -5
View File
@@ -21,7 +21,11 @@ You can also build in a container under any system that supports
Docker. And you can run Ubuntu in a VM and build there. These
options are described below.
## Building directly on Ubuntu
* [Building directly on Ubuntu](#ubuntu)
* [Building in a Docker container](#docker)
* [Building using Vagrant](#vagrant)
## <a name="ubuntu"></a>Building directly on Ubuntu
The weave git repository should be cloned into
`$GOPATH/src/github.com/weaveworks/weave`, in accordance with [the go
@@ -53,10 +57,10 @@ $ make
```
This will build the weave components and package them into three
Docker images (`weaveworks/weave`, `weaveworks/weavedns`, and
`weaveworks/weaveexec`). These are then exported as `weave.tar.gz`.
Docker images (`weaveworks/weave`, `weaveworks/weaveexec`, and
`weaveworks/plugin`). These are then exported as `weave.tar.gz`.
## Building in a Docker container
## <a name="docker"></a>Building in a Docker container
As a preliminary step, we create a container image based on Ubuntu
that has all the prerequisites. This avoids the need to download and
@@ -106,7 +110,7 @@ $ sudo docker run -v /var/run/docker.sock:/var/run/docker.sock -v <host gopath>:
This will leave the intermediate build artifacts on the host, so that
you can modify the weave source code and rebuild quickly.
## Building using Vagrant
## <a name="vagrant"></a>Building using Vagrant
If you aren't running Linux, or otherwise don't want to run the docker
daemon outside a VM, you can use
+20 -18
View File
@@ -50,6 +50,8 @@ TCP/IP, e.g. a netcat UDP service would be run with
root@a2:/# echo 'Hello, world.' | nc -u a1 5533
Broadcast and multicast protocols also work over Weave Net.
We can deploy the entire arsenal of standard network tools and
applications, developed over decades, to configure, secure, monitor,
and troubleshoot our container network. To put it another way, we can
@@ -63,7 +65,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 +95,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
@@ -120,7 +115,8 @@ Weave detects when a container has exited and releases its
automatically allocated addresses so they can be re-used.
See the [Automatic IP Address Management](ipam.html) documentation for
further details.
further details. We also have an explanation of
[the basics of IP addressing](ip-addresses.html)
Instead of getting weave to allocate IP addresses automatically, it is
also possible to specify an address and network explicitly, expressed
@@ -321,10 +317,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:
+103
View File
@@ -0,0 +1,103 @@
---
title: IP Addresses, routes and networks
layout: default
---
# IP Addresses, routes and networks
Weave Net lets you run containers on a private network, and so the IP
addresses those containers use are insulated from the rest of the
Internet, and you don't have to worry about them clashing. Except, if
they actually do clash with some addresses that you'd like the
containers to talk to.
### Some definitions
- _IP_ is the Internet Protocol, the fundamental basis of network
communication between billions of connected devices.
- The _IP address_ is (for most purposes) the four numbers separated
by dots, like `192.168.48.12`. Each number is one byte in size, so can
be between 0 and 255.
- Each IP address lives on a _Network_, which is some set of those
addresses that all know how talk to each other. The network address
is some prefix of the IP address, like `192.168.48`. To show
which part of the address is the network, we append a slash
and then the number of bits in the network prefix, like
`/24`.
- A _route_ is an instruction for how to deal with traffic destined
for somewhere else - it specifies a Network, and a way to talk to
that network. Every device using IP has a table of routes, so for
any destination address it looks up that table, finds the right
route, and sends it in the direction indicated.
### Examples
In the IP address `10.4.2.6/8`, the network prefix is the first 8 bits
- `10`. Written out in full, that network is `10.0.0.0/8`.
The most common prefix lengths are 8, 16 and 24, but there is nothing
stopping you using a /9 network or a /26. E.g. `6.250.3.1/9` is on the
`6.128.0.0/9` network.
Several websites offer calculators to decode this kind of address; for
example [IP Address Guide](http://www.ipaddressguide.com/cidr).
Here is an example route table for a container attached to the Weave
network:
````
# ip route show
default via 172.17.42.1 dev eth0
10.2.2.0/24 dev ethwe proto kernel scope link src 10.2.2.1
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.170
````
It has two interfaces: one that Docker gave it called `eth0`, and one
that weave gave it called `ethwe`. They are on networks
`172.17.0.0/16` and `10.2.2.0/24` respectively, and if you want to
talk to any other address on those networks then the routing table
tells it to send directly down that interface. If you want to talk to
anything else not matching those rules, the default rule says to send
it to `172.17.42.1` down the eth0 interface.
So, suppose this container wants to talk to another container at
address `10.2.2.9`; it will send down the ethwe interface and weave
Net will take care of routing the traffic. To talk an external server
at address `74.125.133.128`, it looks in its routing table, doesn't
find a match, so uses the default rule.
### Configuring Weave
The default configurations for both weave Net and Docker use [Private
Networks](https://en.wikipedia.org/wiki/Private_network), whose
addresses are never found on the public internet, so that reduces the
chances of overlap. But it could be that you or your hosting provider
are using some of these private addresses in the same range, which would
cause a clash.
Here's an example: on `weave launch`, the following error message
can appear:
````
Network 10.32.0.0/12 overlaps with existing route 10.0.0.0/8 on host.
ERROR: Default --ipalloc-range 10.32.0.0/12 overlaps with existing route on host.
You must pick another range and set it on all hosts.
````
As the message says, the default that weave Net would like to use is
`10.32.0.0/12` - a 12-bit prefix so all addresses starting with the bit
pattern 000010100010, or in decimal everything from 10.32.0.0 through
10.47.255.255. But the user's host already has a route for `10.0.0.0/8`,
which overlaps, because the first 8 bits are the same. If we went
ahead and used the default network, then for an address like
`10.32.5.6` the kernel would never be sure whether this meant the
weave Net network of `10.32.0.0/12` or the hosting network of
`10.0.0.0/8`.
If you're sure the addresses you want are not really in use, then
explicitly setting the range with `--ipalloc-range` in the
command-line arguments to `weave launch` on all hosts will force Weave
Net to use that range, even though it overlaps. Otherwise, you can
pick a different range, preferrably another subset of the [Private
Networks](https://en.wikipedia.org/wiki/Private_network). For example
172.30.0.0/16.
+2 -1
View File
@@ -109,7 +109,8 @@ The range parameter is written in
[CIDR notation](http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) -
in this example "/16" means the first 16 bits of the address form the
network address and the allocator is to allocate container addresses
that all start 10.2.
that all start 10.2. We have [a page with more information on IP
addresses and routes](ip-addresses.html).
Weave shares the IP address range across all peers, dynamically
according to their needs. If a group of peers becomes isolated from
+98
View File
@@ -0,0 +1,98 @@
---
title: Using Weave Net via Docker Networking
layout: default
---
# Weave Plugin
Docker versions 1.9 and later have a plugin mechanism for adding
different network providers. Weave installs itself as a network plugin
when you start it with `weave launch`. To create a network that spans
multiple docker hosts, the weave peers must be connected in the usual
way, i.e. by specifying other hosts in `weave launch` or
[`weave connect`](features.html#dynamic-topologies).
Subsequently you can start containers with, e.g.
$ docker run --net=weave -ti ubuntu
on any of the hosts, and they can all communicate with each other.
In order to use Weave's [Service Discovery](weavedns.html), 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
## Under the hood
The Weave plugin actually provides *two* network drivers to Docker
- one named `weavemesh` that can operate without a cluster store and
one named `weave` that can only work with one (like Docker's overlay
driver).
### `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
If you do create additional networks using the `weavemesh` driver,
containers attached to them will be able to communicate with
containers attached to `weave`; there is no isolation between those
networks.
### `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](https://github.com/docker/docker/blob/master/docs/userguide/networking/get-started-overlay.md)
should point the way.
Note that in the case of multiple networks using the `weave` driver, 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
By default, multicast traffic will be routed over the weave network.
To turn this off, e.g. because you want to configure your own multicast
route, add the `--no-multicast-route` flag to `weave launch-plugin`.
## Restarting
We start the plugin with a policy of `--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 may hang for a long time when it subsequently
tries to talk to the plugin.
Unfortunately, [Docker 1.9 may also try to talk to the plugin before it has even started it](https://github.com/docker/libnetwork/issues/813).
If using `systemd`, we advise that you modify the Docker unit to
remove the timeout on startup, to give Docker enough time to abandon
its attempts.
E.g. in the file `/lib/systemd/system/docker.service`, add under `[Service]`:
TimeoutStartSec=0
+9 -5
View File
@@ -115,6 +115,10 @@ will only include one of them.
host1$ weave launch-router && weave launch-proxy --rewrite-inspect
By default, multicast traffic will be routed over the weave network.
To turn this off, e.g. because you want to configure your own multicast
route, add the `--no-multicast-route` flag to `weave launch-proxy`.
## <a name="ipam"></a>Automatic IP address assignment
If [automatic IP address assignment](ipam.html) is enabled in weave,
@@ -231,11 +235,11 @@ 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 --tlsverify --tlscacert=/tls/ca.pem \
--tlscert=/tls/server-cert.pem --tlskey=/tls/server-key.pem
+86 -66
View File
@@ -5,14 +5,19 @@ layout: default
# Troubleshooting Weave
* [Overall status](#weave-status)
* [List connections](#weave-status-connections)
* [List peers](#weave-status-peers)
* [List DNS entries](#weave-status-dns)
* [JSON report](#weave-report)
* [List attached containers](#list-attached-containers)
* [Basic diagnostics](#diagnostics)
* [Status reporting](#weave-status)
- [List connections](#weave-status-connections)
- [List peers](#weave-status-peers)
- [List DNS entries](#weave-status-dns)
- [JSON report](#weave-report)
- [List attached containers](#list-attached-containers)
* [Stopping weave](#stop)
* [Reboots](#reboots)
* [Snapshot releases](#snapshots)
## <a name="diagnostics"></a>Basic diagnostics
Check what version of weave you are running with
weave version
@@ -37,60 +42,40 @@ Another useful debugging technique is to attach standard packet
capture and analysis tools, such as tcpdump and wireshark, to the
`weave` network bridge on the host.
To stop weave, if you have configured your environment to use the
Weave Docker API Proxy, e.g. by running `eval $(weave env)` in your
shell, you must first restore the environment with
eval $(weave env --restore)
Then run
weave stop
Note that this leaves the local application container network intact;
containers on the local host can continue to communicate, whereas
communication with containers on different hosts, as well as service
export/import, is disrupted but resumes when weave is relaunched.
To stop weave and completely remove all traces of the weave network on
the local host, run
weave reset
Any running application containers will permanently lose connectivity
with the weave network and have to be restarted in order to
re-connect.
### <a name="weave-status"></a>Overall status
## <a name="weave-status"></a>Status reporting
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 +114,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 +129,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 +150,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 +216,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
@@ -254,22 +248,48 @@ You can also supply a list of container IDs/names to `weave ps`, like this:
able ce:15:34:a9:b5:6d 10.2.5.1/24
baker 7a:61:a2:49:4b:91 10.2.8.3/24
### Reboots
## <a name="stop"></a>Stopping weave
To stop weave, if you have configured your environment to use the
Weave Docker API Proxy, e.g. by running `eval $(weave env)` in your
shell, you must first restore the environment with
When a host reboots, docker's default behaviour is to restart any
containers that were running. Since weave relies on special network
configuration outside of the containers, the weave network will not
function in this state.
eval $(weave env --restore)
To remedy this, stop and re-launch the weave container, and re-attach
the application containers with `weave attach`.
Then run
For a more permanent solution,
[disable Docker's auto-restart feature](https://docs.docker.com/articles/host_integration/)
and create appropriate startup scripts to launch weave and run
application containers from your favourite process manager.
weave stop
### <a name="snapshots"></a>Snapshot Releases
Note that this leaves the local application container network intact;
containers on the local host can continue to communicate, whereas
communication with containers on different hosts, as well as service
export/import, is disrupted but resumes when weave is relaunched.
To stop weave and completely remove all traces of the weave network on
the local host, run
weave reset
Any running application containers will permanently lose connectivity
with the weave network and have to be restarted in order to
re-connect.
## <a name="reboots"></a>Reboots
The router and proxy containers do not have Docker restart policies
set, because the process of getting everything re-started and
re-connected via restart policies is not entirely reliable. Until that
changes, we recommend you create appropriate startup scripts to launch
weave and run application containers from
[your favourite process manager](systemd.html).
If you are shutting down or restarting a host deliberately, run `weave
reset` to clear everything down.
The Weave Docker plugin does restart automatically because it must
always start with Docker, as described in
[its documentation](plugin.html).
## <a name="snapshots"></a>Snapshot releases
We sometimes publish snapshot releases, to provide previews of new
features, assist in validation of bug fixes, etc. One can install the
+5 -4
View File
@@ -55,9 +55,8 @@ root@ubuntu:/# ping pingme
> the hostname is not in the weaveDNS domain the container will *not*
> be registered, but will still use weaveDNS for resolution.
It is also possible to force or forbid an application container's use
of weaveDNS with the `--with-dns` and `--without-dns` options to
`weave run` and `weave launch-proxy`.
To disable application containers' use of weaveDNS, add the
`--without-dns` option to `weave run` or `weave launch-proxy`.
## <a name="how-it-works"></a>How it works
@@ -100,7 +99,7 @@ In the event that weaveDNS is launched in this way, it's important that
other calls to `weave` also specify the bridge device:
```bash
$ sudo DOCKER_BRIDGE=someother weave run --with-dns ...
$ sudo DOCKER_BRIDGE=someother weave run ...
```
## <a name="load-balancing"></a>Load balancing
@@ -260,6 +259,7 @@ DNS:
Service: dns
Domain: weave.local.
Upstream: 8.8.8.8, 8.8.4.4
TTL: 1
Entries: 9
@@ -272,6 +272,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
+6 -3
View File
@@ -8,7 +8,8 @@ C2=10.2.1.71
# 700 = Linux's minimum PMTU (552) + weave framing overheads
# (currently about 60, but we leave some margin). That way the PMTUs
# seen by containers is still >= 552.
drop_large_udp_packets="FORWARD -p udp --destination-port 6783 -m length '!' --length 0:700 -j DROP"
drop_large_sleeve_packets="INPUT -p udp --destination-port 6783 -m length '!' --length 0:700 -j DROP"
drop_large_fastdp_packets="INPUT -p udp --destination-port 6784 -m length '!' --length 0:700 -j DROP"
start_suite "PMTU discovery and packet fragmentation"
@@ -18,7 +19,8 @@ weave_on $HOST2 launch
start_container $HOST1 $C1/24 --name=c1
start_container $HOST2 $C2/24 --name=c2
run_on $HOST2 "sudo iptables -I $drop_large_udp_packets"
run_on $HOST1 "sudo iptables -I $drop_large_sleeve_packets"
run_on $HOST1 "sudo iptables -I $drop_large_fastdp_packets"
assert_raises "weave_on $HOST2 connect $HOST1"
sleep 35 # give weave time to discover the PMTU
@@ -28,6 +30,7 @@ sleep 35 # give weave time to discover the PMTU
exec_on $HOST2 c2 $PING -s 10000 $C1 1>/dev/null 2>&1 || true
assert_raises "exec_on $HOST2 c2 $PING -s 10000 $C1"
run_on $HOST2 "sudo iptables -D $drop_large_udp_packets"
run_on $HOST1 "sudo iptables -D $drop_large_fastdp_packets"
run_on $HOST1 "sudo iptables -D $drop_large_sleeve_packets"
end_suite
+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
+13 -2
View File
@@ -6,15 +6,26 @@ entrypoint() {
docker_on $HOST1 inspect --format="{{.Config.Entrypoint}}" "$@"
}
check_iface_ready() {
assert_raises "proxy docker_on $HOST1 run -e 'WEAVE_CIDR=$1' $BASE_IMAGE $CHECK_ETHWE_UP"
assert_raises "proxy docker_on $HOST1 run -e 'WEAVE_CIDR=$1' $BASE_IMAGE ip -4 -o addr show ethwe | grep -q '$1'"
}
start_suite "Proxy waits for weave to be ready before running container commands"
weave_on $HOST1 launch-proxy
BASE_IMAGE=busybox
# Ensure the base image does not exist, so that it will be pulled
! docker_on $HOST1 inspect --format=" " $BASE_IMAGE >/dev/null 2>&1 || docker_on $HOST1 rmi $BASE_IMAGE
assert_raises "proxy docker_on $HOST1 run --name c1 -e 'WEAVE_CIDR=10.2.1.1/24' $BASE_IMAGE $CHECK_ETHWE_UP"
# check that interface is ready, with and without --no-multicast-route
weave_on $HOST1 launch-proxy --no-multicast-route
check_iface_ready 10.2.1.1/24
weave_on $HOST1 stop-proxy
weave_on $HOST1 launch-proxy
check_iface_ready 10.2.1.1/24
# Check committed containers only have one weavewait prepended
proxy_start_container $HOST1 --name c1 -e 'WEAVE_CIDR=10.2.1.1/24'
COMMITTED_IMAGE=$(proxy docker_on $HOST1 commit c1)
assert_raises "proxy docker_on $HOST1 run --name c2 $COMMITTED_IMAGE"
assert "entrypoint c2" "$(entrypoint $COMMITTED_IMAGE)"
-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
weave_on $HOST1 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
+24
View File
@@ -0,0 +1,24 @@
#! /bin/bash
. ./config.sh
C1_NAME=c1.weave.local
start_suite "Recovery of container details on router restart (via 'local' plugin)"
weave_on $HOST1 launch
start_container_local_plugin $HOST1 --name=c1 --hostname=$C1_NAME
c1ip=$(container_ip $HOST1 c1)
weave_on $HOST1 stop-router
weave_on $HOST1 launch-router
assert "container_ip $HOST1 c1" "$c1ip"
assert "weave_on $HOST1 dns-lookup $C1_NAME" "$c1ip"
# check that c1 IP has been reclaimed and doesn't get assigned to a
# fresh container
start_container $HOST1 --name=c2
assert_raises "container_ip $HOST1 c2 | grep -v $c1ip"
end_suite
+14 -3
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() {
@@ -169,7 +176,7 @@ proxy_start_container_with_dns() {
rm_containers() {
host=$1
shift
[ $# -eq 0 ] || docker_on $host rm -f "$@" >/dev/null
[ $# -eq 0 ] || docker_on $host rm -f -v "$@" >/dev/null
}
container_ip() {
@@ -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
+400 -187
View File
File diff suppressed because it is too large. Load diff