Compare commits

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

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

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

This change will be superceeded by #1694.
2015-11-17 16:28:31 +00:00
Adam Harrison 262352f8bb Merge pull request #1580 from weaveworks/1579-launch-proxy-mount-h
[proxy] dynamically mount -H dirs into the proxy when launching
2015-11-17 14:52:17 +00:00
Paul Bellamy e8c56a8e8e Review Feedback 2015-11-17 14:49:50 +00:00
Matthias Radestock 312691376a Merge remote-tracking branch 'upstream/master' 2015-11-17 13:57:08 +00:00
Matthias Radestock a9611c782b cosmetic: move comment 2015-11-17 13:56:18 +00:00
Adam Harrison bcc70b36fd Merge pull request #1679 from weaveworks/1539-no-detect-tls-flag
[proxy] add --no-detect-tls flag, and proxy always tries to detect TLS.
2015-11-17 13:55:43 +00:00
Matthias Radestock 9ee3b1bfbd add some timestamps to multiweave 2015-11-17 10:28:28 +00:00
Paul Bellamy e80a83b30b adding work around to the proxy /var/run warning 2015-11-17 09:40:34 +00:00
Paul Bellamy d80e72dfcd [proxy] dynamically mount -H dirs into the proxy when launching
This lets you use -H unix://*.sock outside /var/run/weave. Using a
socket directly in /var/run (i.e. /var/run/foo.sock), will reintroduce
the bug with namespaces not being GCed, so that is a warning.
2015-11-17 09:40:34 +00:00
Matthias Radestock d01c26e618 cosmetic: remove outdated comment
The deadlock described for SendProtocolMsg (if it was going via the
actor) cannot occur since all sending of broadcasts and gossip by
LocalPeer is mediated by GossipSenders, the interaction with which is
non-blocking.
2015-11-17 09:27:49 +00:00
Matthias Radestock cab2e11ca9 refactor: simplify per-connection GossipSender gc and creation
by putting it in one place and inlining some helper functions
2015-11-16 23:21:33 +00:00
Matthias Radestock b48994b1b3 cosmetic: move some functions 2015-11-16 23:01:20 +00:00
Matthias Radestock fb48acddb2 cosmetic: update comment 2015-11-16 19:27:31 +00:00
Paul Bellamy 7d627f7af4 Merge pull request #1673 from weaveworks/1440-cleanup
Move some netlink code to upstream library
2015-11-16 14:24:10 +00:00
Paul Bellamy f458491d88 [proxy] add --no-detect-tls flag, and proxy always tries to detect TLS. 2015-11-13 15:27:27 +00:00
Bryan Boreham de802f9d76 Work round docker-py assuming busybox is present (https://github.com/docker/docker-py/issues/852) 2015-11-13 11:44:08 +00:00
Bryan Boreham 804e6ba586 Cope with changes in docker-py structure.
Tests are now in a different directory,
and get sharded by module rather than individually.
2015-11-13 11:08:57 +00:00
Adam Harrison af3fc82b77 Add release v1.3.0 2015-11-12 16:49:33 +00:00
Adam Harrison 3bb7036cfd Runner depends on tools 2015-11-12 16:48:56 +00:00
Bryan Boreham d802eb485b Merge pull request #1664 from weaveworks/weave-launch-plugin
Add plugin support to weave script. LGTM.
2015-11-12 16:04:41 +00:00
Adam Harrison b4dd9c2490 Add plugin to features doc 2015-11-12 16:01:01 +00:00
Adam Harrison 04ab198e95 Set restart policy to 'always'
Docker attempts to recreate networks on startup which means the plugin
container has to be started. If it isn't started automatically by Docker
with a restart policy, it's not possible to start it manually because
Docker is blocked.
2015-11-12 14:58:54 +00:00
Adam Harrison c345700963 Add plugin to weave status 2015-11-12 14:58:54 +00:00
Adam Harrison b21afcb78f Implement weave launch-plugin and weave stop-plugin 2015-11-12 14:56:13 +00:00
Adam Harrison 7f65530b26 weave setup fetches plugin 2015-11-12 14:56:13 +00:00
Bryan Boreham e230eeb486 Use upstream netlink code 2015-11-12 13:08:56 +00:00
Bryan Boreham b1b03f3a97 Revert "Remove code coverage as temporary work-around to Docker 1.9 issue"
This reverts commit 65fe753a58.
2015-11-12 11:27:48 +00:00
Bryan Boreham 36ef6d8da2 Merge remote-tracking branch 'origin/1.2' 2015-11-12 11:27:01 +00:00
Bryan Boreham 28bb0dd3b0 Merge pull request #1671 from weaveworks/1666-remove-cont-dir
Remove containers directory bind-mount from proxy; fixes #1647 and #1666
2015-11-12 11:25:34 +00:00
Bryan Boreham 79f8a8f909 Merge remote-tracking branch 'origin/1.2' 2015-11-12 10:43:01 +00:00
Bryan Boreham bfe2513540 Merge pull request #1669 from dpw/fdp-fix-npd
Handle the case where Forward returns nil. Fixes #1661.
2015-11-12 10:38:29 +00:00
Bryan Boreham 2536aef737 Proxy doesn't need to mount the containers dir 2015-11-12 10:28:58 +00:00
Adam Harrison 0661ccdea9 Revert "Revert "Disable restart of docker""
This reverts commit 76db732f1b.
2015-11-11 10:10:17 +00:00
Adam Harrison 29f7015e58 Merge pull request #1665 from weaveworks/move-odp-helpers
Move ODP helper functions out to their own package
2015-11-11 09:56:24 +00:00
David Wragg 7dc1b50d3b Handle the case where Forward returns nil
Forward can return nil if the remote peer has HasShortID false.  That
can happen if we learn about a 1.2 peer from a 1.1  peer and attempt to
connect to it.

This change just fixes the panic.  HasShortID will never be set, so we'll
fall back to sleeve.  But if that occurs, if can be fixed by a restart of
the affected routers.
2015-11-10 23:17:01 +00:00
Bryan Boreham 682133a56a Move ODP helper functions out to their own package 2015-11-10 17:09:17 +00:00
Adam Harrison 76db732f1b Revert "Disable restart of docker"
This reverts commit a400a3dd09.
2015-11-10 10:24:58 +00:00
Matthias Radestock 64aa2b5cc3 refactor: remove weave overlay-isms from mesh 2015-11-09 20:56:04 +00:00
Matthias Radestock 1eed5fd271 cosmetic: don't mention UDP in mesh 2015-11-09 19:47:27 +00:00
Matthias Radestock dd7ffa4501 code docs: warn against nonce re-use by overlay connection 2015-11-09 19:46:53 +00:00
Adam Harrison 5e79da0c9c Merge pull request #1658 from weaveworks/tweak-circle
Upgrade CI to Docker 1.9 and work round issues
2015-11-09 17:55:27 +00:00
Bryan Boreham 65fe753a58 Remove code coverage as temporary work-around to Docker 1.9 issue 2015-11-09 17:18:59 +00:00
Bryan Boreham 17c835e672 Compress weave.tar to save upload time 2015-11-09 17:18:58 +00:00
Bryan Boreham fa5bcd0457 Lump some commands together to reduce CircleCI waiting overhead 2015-11-09 17:18:58 +00:00
Bryan Boreham 499d6312b7 Don't need --add-host except in non-FDP case because we put the
mappings in the outer /etc/hosts
2015-11-09 17:18:58 +00:00
Bryan Boreham 20d5903696 Update test template name to pull in new Docker version 2015-11-09 17:18:57 +00:00
Adam Harrison f039221ec9 Merge remote-tracking branch 'origin/create-mesh'
Closes #1659.
2015-11-09 17:16:35 +00:00
Matthias Radestock dc9f8762ca move generic communication substrate into its own 'mesh' package
This is mostly a matter of moving some files, changing package names,
changing some imports, and changing some type references.

We duplicate the odd constant & global var, and the intmac/macint
functions, since neither 'router' or 'mesh' nor, really,' common' are
a natural home for them.
2015-11-09 16:37:39 +00:00
Adam Harrison e5c69801a7 Merge remote-tracking branch 'origin/separate-overlay'
Closes #1648.
2015-11-09 16:17:41 +00:00
Matthias Radestock ab458368de refactor: move NetworkRouterStatus into its own file 2015-11-09 15:51:11 +00:00
Matthias Radestock 8eb680fcc9 refactor: separate out NetworkRouter-specific status 2015-11-09 15:51:11 +00:00
Matthias Radestock 71b05972f4 refactor: move NetworkRouter into its own file 2015-11-09 15:50:55 +00:00
Matthias Radestock 7deeb8dba5 refactor: separate NetworkRouter from Router 2015-11-09 15:47:08 +00:00
Matthias Radestock daad190188 refactor: split overlay.go to extract network overlay stuff 2015-11-09 15:47:08 +00:00
Matthias Radestock 7cc0052451 refactor: separate NullNetworkOverlay from NullOverlay 2015-11-09 15:47:07 +00:00
Matthias Radestock 18f8e29af8 refactor: separate NetworkOverlay from Overlay
...since connections only need to know about the latter.
2015-11-09 15:47:07 +00:00
Matthias Radestock d78f6614cc refactor: separate forwarder from overlay connection
...since the main connections only need to know about the former.

We make LocalConnection.Overlay (formerly '.forwarder') public since
the router needs access to it, and it shouldn't be doing that via a
private field.
2015-11-09 15:45:06 +00:00
Bryan Boreham 53ac43202a Merge pull request #1641 from weaveworks/1554-converge-on-peer-uid-change
better convergence when peers restart; fixes #1554.
2015-11-09 15:09:59 +00:00
Matthias Radestock bfb050c9cc better convergence when peers restart
Previously when a peer restarted, information about the new
incarnation (i.e. with a different UID) was not accepted by other
peers (and connections would be dropped) unless all knowledge of the
previous incarnation had been purged. This could result in a lot of
connection churn and hence connectivity disruption, and, in some
pathological cases, very slow convergence and hence acceptance of the
new incarnation into the network.

We now no longer drop connections when encountering different
incarnations of a peer. There are two situations when that can happen:

1) on connection establishment

we simply proceed

2) on receipt of gossip

to ensure convergence we

a) treat the UID as an additional discriminator when deciding whether
we should update our information about a peer with that which was
gossiped. Specifically, we update the information we hold when a) the
gossiped version is greater, or b) is the same and the UID is
greater.

b) include the UID in the information we update

c) move our own version number beyond any we receive for ourselves, if
the received UID differs from ours.

With (a) we establishes a total order of peer information across
several incarnations of the same peer. i.e. we consider information to
be fresher if it has a higher version, or the same version and higher
UID. This may seem somewhat counter intutive, since it will generally
treat information about new incarnations as older than old
incarnations, since incarnations always start life with version 1. But
to do better we'd need to establish a total order of incarnations that
matches their temporal occurrence. Which requires some sort of durable
state.

So instead we have (c). Through that we learn the highest version
number of any old incarnation of ourselves that other peers still
hold, and then make sure that our version is greater than
that. Essentially we continue where the old incarnations left
off. It's as if instead of restarting we had simply changed UIDs. And
due to (a) and (b) the information about the new incarnation of
ourselves, now with a higher version, will supersede that of the old
incarnations.

Fixes #1554.
2015-11-09 14:58:33 +00:00
Matthias Radestock 97622b961c remove cruft 2015-11-08 13:06:29 +00:00
Matthias Radestock b7e7c11a57 refactor: make Overlay setting more explicit 2015-11-08 12:29:36 +00:00
Matthias Radestock 59e11e4e8a refactor: more flexible Routes API 2015-11-08 11:15:31 +00:00
Matthias Radestock 39b0357517 Merge branch '1.2' 2015-11-08 11:04:48 +00:00
Matthias Radestock 878a12002c set http listen address solely through the weave script
Previously it was set by the script only in fastdp mode; and in the
router's main.go otherwise. Yet every interaction of the script with
weave assumes the port is the script's HTTP_PORT=6784. By always
starting the router with options to listen on that port we ensure that
the router is in fact listening where the script expects it to.

In doing this get rid of router.HTTPPort. We do need to hard-code the
port in the proxy instead, but relying on router.HTTPPort was flawed
anyway, for the above reasons.
2015-11-07 18:01:12 +00:00
Matthias Radestock d3d2863d52 refactor: get rid of const.go 2015-11-07 16:32:23 +00:00
Matthias Radestock f3f23b43b5 refactor: crypto doesn't need to be a generic overlay feature 2015-11-07 16:14:31 +00:00
Matthias Radestock 006262ed8f refactor: Connections don't need to know about overlay crypto 2015-11-07 16:10:00 +00:00
Matthias Radestock 519aef86fa refactor: split crypto code 2015-11-07 15:59:15 +00:00
Matthias Radestock 8bd77c0d47 refactor: get rid of router/utils.go
most functions end up in other files in the router package, but a few
land in common.
2015-11-07 15:45:29 +00:00
Matthias Radestock 7a5c910cd5 tiny refactor: inline a tiny single-use fun and move another 2015-11-07 12:44:13 +00:00
Matthias Radestock 4e34f2001c cosmetic 2015-11-07 11:14:11 +00:00
Matthias Radestock 54b7e2c874 tiny refactor: use checkWarn in a few more places 2015-11-07 11:08:44 +00:00
Matthias Radestock 19f89f7122 remove duplicate test
this is already covered in dns_fallback_test
2015-11-07 07:30:53 +00:00
Matthias Radestock 9f25d72a4a cosmetic 2015-11-07 05:43:20 +00:00
Adam Harrison 3e99b71e0f Merge pull request #1643 from weaveworks/refactor-forwarder-remote-address
Refactor: remove knowledge of overlay connectivity details from Local{Peer,Connection}
2015-11-06 16:02:10 +00:00
Matthias Radestock fb3b690c5f refactor: reduce coupling between LocalConnection and Forwarder 2015-11-05 21:27:14 +00:00
Matthias Radestock 18fe553d23 LocalConnection shouldn't have to know about remote UDP address
...and generally know less about how overlays establish connectivity.
2015-11-05 20:43:52 +00:00
Matthias Radestock e7ff6a2191 LocalPeer shouldn't have to know about remote UDP address
...and LocalConnection shouldn't have to remember it
2015-11-05 19:32:49 +00:00
Matthias Radestock ad15e5a234 cosmetic 2015-11-05 19:01:34 +00:00
Matthias Radestock 094c7aa438 cosmetic 2015-11-05 17:24:38 +00:00
Adam Harrison 472ed5a7d3 Merge pull request #1640 from weaveworks/1588-disable-docker-restart
Disable restart of docker in test 640
2015-11-05 15:40:01 +00:00
Adam Harrison f284300870 Merge pull request #1636 from weaveworks/1635-notation
Drop use of interval notation when printing ranges
2015-11-05 15:38:40 +00:00
Adam Harrison ab5f4410b6 Add release v1.2.1 2015-11-05 14:34:13 +00:00
Bryan Boreham a400a3dd09 Disable restart of docker 2015-11-05 14:20:38 +00:00
Bryan Boreham c4855e8123 Merge branch '1.2' 2015-11-05 12:41:39 +00:00
Adam Harrison 3abb657777 Document port security 2015-11-05 12:23:17 +00:00
Adam Harrison 4dd63d8f72 Bind the HTTP listener to localhost when fastdp enabled
Prevent containers from accessing the weave router control port when
fast datapath is in use.
2015-11-05 12:23:17 +00:00
Bryan Boreham 6c0bdec2c9 Merge pull request #1637 from weaveworks/issues/1632-http-localhost-bind
Bind router HTTP listener to localhost when fastdp enabled
LGTM.  Fixes #1632
2015-11-05 12:20:20 +00:00
Adam Harrison d6dd1f269a Document port security 2015-11-05 12:16:57 +00:00
Bryan Boreham 2bd23f3709 Merge branch '1.2' to pull in latest_release_doc_updates 2015-11-05 12:05:25 +00:00
Bryan Boreham 79dc464b83 Merge branch 'latest_release_doc_updates' into 1.2 2015-11-05 12:04:54 +00:00
Bryan Boreham 3dbd5b11d8 Segregate discussion of range and subnet, and explain subnet a bit 2015-11-05 11:32:06 +00:00
Bryan Boreham 549fd5fe29 Merge branch '1.2' 2015-11-05 11:30:53 +00:00
Bryan Boreham 0ab5c65185 Merge pull request #1630 from weaveworks/issues/1588-improve-proxy-restart-reattach-test
Improve proxy restart reattach test reliability
2015-11-05 11:25:31 +00:00
Bryan Boreham 5f4753f881 Segregate discussion of range and subnet, and explain subnet a bit 2015-11-05 10:13:06 +00:00
Bryan Boreham e03a45f8c5 Update docs to match loss of interval notation 2015-11-05 10:11:43 +00:00
Adam Harrison 67c2d53e59 Bind the HTTP listener to localhost when fastdp enabled
Prevent containers from accessing the weave router control port when
fast datapath is in use.
2015-11-04 17:42:24 +00:00
Bryan Boreham 7b01dd9794 Drop use of interval notation 2015-11-04 16:45:16 +00:00
Bryan Boreham 04e50ff02c Merge branch '1.2' 2015-11-04 16:40:30 +00:00
Bryan Boreham e57ccdc858 Merge branch 'latest_release_doc_updates' into 1.2 2015-11-04 16:39:43 +00:00
Bryan Boreham a73a65b48a Removed obsolete text referring to mixed auto-manual allocation, and clarify Range printout 2015-11-04 16:36:44 +00:00
Adam Harrison 781c805e10 Merge branch '1.2' 2015-11-04 15:32:17 +00:00
Adam Harrison 7c4af88911 Merge pull request #1606 from weaveworks/issues/1578_ufw_ubuntu
Add an iptables rule for letting weaveDNS requests in
2015-11-04 15:30:34 +00:00
Alvaro Saurin 0aca173e96 Add an iptables rule for letting weaveDNS requests in 2015-11-04 14:00:26 +01:00
Matthias Radestock 60db57a41c refactor: remove duplicate isKnownPeer function 2015-11-03 18:11:53 +00:00
Matthias Radestock df68f7dcb9 Merge branch '1.2' 2015-11-03 17:56:36 +00:00
Matthias Radestock e95e3cc29d Merge pull request #1624 from weaveworks/1593-more-peers
Better choice of peers for IPAM to transfer ownership to on `weave reset`

Fixes #1593.
2015-11-03 17:55:40 +00:00
Bryan Boreham 67e9310cc1 Review feedback 2015-11-03 17:01:27 +00:00
Adam Harrison 94984df047 Merge branch '1.2' 2015-11-03 16:57:15 +00:00
Adam Harrison 861bf953bf Merge pull request #1629 from weaveworks/1626-expose-names
Always add 'weave expose -h' argument to DNS
2015-11-03 16:32:13 +00:00
Adam Harrison 06eaac8690 Don't assert ethwe carrier via the proxy
The test script runs commands inside containers via docker exec to
ensure the ethwe carrier is up. We must not run these commands via the
proxy because it will interpose weavewait, which will block forever if
attachment has failed (e.g. due to #1556).
2015-11-03 15:42:37 +00:00
Adam Harrison 6b9e8816d1 Wait for proxy to become available after docker is restarted 2015-11-03 15:33:37 +00:00
Bryan Boreham cf9f715a28 Merge branch '1.2' 2015-11-03 15:31:51 +00:00
Bryan Boreham 08e043fea7 Always add 'weave expose -h' argument to DNS, and add smoke-test for this also 2015-11-03 15:23:28 +00:00
Bryan Boreham 54ba1009d3 Merge pull request #1608 from weaveworks/1600-start-networkmode
LGTM.  Fixes #1600
2015-11-03 15:14:15 +00:00
Adam Harrison 4a6a46fd7b Merge pull request #1627 from tomwilkie/mv-scheduler
Move scheduler to build-tools.
2015-11-03 14:44:52 +00:00
Adam Harrison 0eb7414f72 Merge pull request #1583 from weaveworks/move-runner-to-tools
Remove test runner; it now lives in build-tools.git
2015-11-03 14:44:46 +00:00
Tom Wilkie c02691a70c Move scheduler to build-tools. 2015-11-03 14:38:42 +00:00
Bryan Boreham 8742957f75 Tweak naming to be closer to code changes in nameserver 2015-11-03 14:15:49 +00:00
Bryan Boreham ace372cfc6 More extensive choice of transferee 2015-11-03 12:03:28 +00:00
Bryan Boreham 27c5f5f49f Fix title of test 2015-11-03 12:03:00 +00:00
Paul Bellamy ec5a1a3977 When container is started in host (or container) networkMode, we mount a noop weavewait, so it can continue
review feedback
2015-11-03 10:18:05 +00:00
Adam Harrison abc653c87d Fix merge breakage 2015-11-03 10:13:43 +00:00
Matthias Radestock 30a128c00b Merge branch '1.2' 2015-11-02 20:58:15 +00:00
Matthias Radestock 77c7963bde Merge pull request #1615 from weaveworks/1603-resort-in-encode
Hold DNS entries sorted case-insensitively until just before sending

Fixes #1603 and #1610.
2015-11-02 20:57:40 +00:00
Bryan Boreham b80c1343c3 Take steps to avoid the test timing out 2015-11-02 20:40:17 +00:00
Bryan Boreham e5c5fbcea3 Hold on to the lower-case version of a hostname, rather than re-computing it every time 2015-11-02 20:40:05 +00:00
Bryan Boreham fc7d04ce73 Condense Entries unit tests with helper function 2015-11-02 20:03:10 +00:00
Bryan Boreham 35f57b3ed2 Move decoding code next to encoding code 2015-11-02 20:01:40 +00:00
Bryan Boreham ed9ade05ea Improve merge asserts 2015-11-02 20:01:40 +00:00
Bryan Boreham f485b729bc Hold all entries sorted case-insensitively until just before sending, when we re-sort case-sensitively in Encode() 2015-11-02 20:01:40 +00:00
Bryan Boreham 64f7953423 Add unit test to check outbound mixed-case merging 2015-11-02 20:01:40 +00:00
Bryan Boreham fc1d837501 Extend unit test to cover mixed-case hostnames and daisy-chained gossip;
reduce mock gossip timer to 2 seconds to trigger more often during tests.
2015-11-02 20:01:40 +00:00
Matthias Radestock 6a238818da Merge branch '1.2' 2015-11-02 19:58:37 +00:00
Matthias Radestock 9800c43bfa refactor(ish): don't use reference in method that doesn't modify 2015-11-02 19:57:17 +00:00
Bryan Boreham b3cda39b0b Let IPAM pick any peer we know about to transfer ownership to, when shutting down 2015-11-02 18:34:58 +00:00
Adam Harrison 7e1b7a2fcd Merge branch '1.2' 2015-11-02 17:28:31 +00:00
Adam Harrison ccae8d42fd Merge remote-tracking branch 'dpw/fdp-probe-vxlan' into 1.2
Fixes #1599
2015-11-02 17:24:52 +00:00
David Wragg 38f78286c7 Probe whether the kernel has ODP vxlan support
It's possible to build that kernel with openvswitch, and vxlan, but
without the vport_vxlan module that glues them together.  And at
least one distro has actually done this.
2015-11-02 15:53:16 +00:00
Adam Harrison c3a53a6ec5 Merge remote-tracking branch 'upstream/1.2' 2015-11-02 15:21:06 +00:00
Bryan Boreham 2bbe6a2ad8 Merge pull request #1622 from weaveworks/fix-miekg-dns-truncation-test
LGTM
2015-11-02 15:09:57 +00:00
Adam Harrison f64f63f675 Handle truncated responses from upstream servers 2015-11-02 15:00:02 +00:00
Adam Harrison f0fb44f6d4 Update unit test for miekg DNS API change 2015-11-02 13:24:04 +00:00
Paul Bellamy fa972615e6 Merge pull request #1616 from weaveworks/1549-inspect-wait
Make inspect-rewrite wait for start
2015-11-02 11:00:23 +00:00
Bryan Boreham f7cb2ef25d have waitForStartByIdent() return an error 2015-10-30 13:46:30 +00:00
Bryan Boreham 949f8bb2e7 Move updateContainerNetworkSettings() into proxy 2015-10-30 13:34:16 +00:00
Bryan Boreham 5eaff802d4 Make inspect-rewrite wait if the start operation is still in progress 2015-10-30 13:34:16 +00:00
Matthias Radestock b37d428726 Merge branch '1.2' 2015-10-30 01:31:16 +00:00
Matthias Radestock a6336149f8 guard against chef not running
Provisioning on ubuntu/wily64 was tripping over this.
2015-10-30 01:29:21 +00:00
Matthias Radestock 12f5ec40b5 refactor: reduce coupling of nameserver to router
The nameserver no longer knows anything about router.Peers and
router.Peer. That only leaves PeerName, Gossip and GossipData.
2015-10-29 23:21:07 +00:00
Matthias Radestock 6a672cf8bc tiny refactor: more idiomatic referal to process' /proc 2015-10-28 18:23:15 +00:00
Matthias Radestock 036b7d9f66 Merge branch '1.2' 2015-10-28 09:40:50 +00:00
Matthias Radestock ca6bce3820 Merge branch 'latest_release_doc_updates' into 1.2 2015-10-28 09:20:46 +00:00
Matthias Radestock 9b4f2297b0 drop quotes in eval "$(weave env)"
They are unnecessary and unwieldy.

Fixes #1596
2015-10-28 09:19:10 +00:00
Matthias Radestock 355c4bc58e Merge branch '1.2' 2015-10-28 08:52:01 +00:00
Matthias Radestock a31b2876d0 Merge pull request #1598 from weaveworks/issues/1584-improved-proxy-router-restart
Improved detection of router container restart

Fixes #1584
2015-10-28 08:51:06 +00:00
Matthias Radestock b697b6e57b Merge pull request #1597 from weaveworks/issues/1516-weavedns-ignore-foreign-domains
Check weaveDNS subdomain on REST PUT

Fixes #1516
2015-10-28 08:50:25 +00:00
Adam Harrison 4792fe6391 Improved detection of router container restart
Use the container name in addition to the entrypoint to avoid triggering
reattachment unnecessarily.
2015-10-27 14:41:26 +00:00
Adam Harrison 7408f72de2 Check weaveDNS subdomain on REST PUT 2015-10-27 14:37:57 +00:00
Tom Wilkie 203a3cfdd1 Remove test runner; it now live in build-tools.git 2015-10-23 13:02:00 +00:00
Ilya Dmitrichenko 3a029686e3 Add Pardot script 2015-10-22 15:07:52 -07:00
Matthias Radestock 9fa7b3956d Merge remote-tracking branch 'upstream/master' 2015-10-22 16:17:55 +01:00
Matthias Radestock b888ace508 remove superfluous code in ApplyUpdate
If the local peer's short ID was updated, we broadcastPeerUpdate at
the end (in unlockAndNotify), so there is no need to include the local
peer in the current update.

Also, reading peers.ourself.Version is bad here - we don't take a lock
and it's written by other goroutines. Doesn't actually break anything
though since it's a uint64 and hence atomic.
2015-10-22 16:12:37 +01:00
Bryan Boreham 2e54efe34d Stop the proxy last, so we can stop the router
before being unable to communicate with docker
when $DOCKER_HOST is pointing at the proxy
2015-10-22 15:45:54 +01:00
Bryan Boreham 94d7f33a03 Merge pull request #1541 from weaveworks/weave-1.2-readme-updates
Update README for 1.2 release
2015-10-22 13:22:54 +01:00
Bryan Boreham 11598295cf Add release v1.2.0 2015-10-22 12:53:57 +01:00
Bryan Boreham 383c3769aa Try en even bigger sleep 2015-10-22 12:19:57 +01:00
Adam Harrison caccba18e9 Mention fast datapath port 2015-10-22 11:58:14 +01:00
Paul Bellamy 95ce00e551 moving boot2docker instructions into the readme 2015-10-22 11:58:14 +01:00
David Wragg 85dad58cfe Pass --privileged for the temporary container in rewrite_etc_hosts
Otherwise we run into selinux issues on fedora23 (and probably other
selinux distros).
2015-10-22 11:44:17 +01:00
Bryan Boreham acdc70f72a Merge pull request #1560 from weaveworks/1542-proxy-bug-weave-not-running
LGTM. Fixes #1542
2015-10-22 10:19:32 +01:00
Paul Bellamy bc9db72212 [proxy] Add notifyWaiters to fix anticipated race condition.
* User makes /start request, and blocks on the waiter
* ContainerStarted fires with ID
* Container goes away
* We try to inspect the container to get the entrypoint, which fails
* Previously, this wouldn't call notifyWaiters and the /start would hang (I expect)
2015-10-22 09:50:12 +01:00
Adam Harrison 7905f81d7e Merge pull request #1570 from dpw/mac-invalidate-routes
Do InvalidateRoutes when a forwarded packet updates a MAC cache entry
2015-10-21 23:54:56 +01:00
Matthias Radestock 2ef9cc6a1c start temporary containers without networking when possible
This is more efficient and TRTTD

Closes #1572.
2015-10-21 16:52:19 +01:00
Matthias Radestock 2357b69a5f nicer interface name
and more likely to be ignored by NetworkManager

Closes #1567.
2015-10-21 16:22:16 +01:00
Matthias Radestock 98b47f866b Merge pull request #1569 from weaveworks/issues/1565-docker-1.9.0-workaround
Reorder inspect template to work around docker/docker#17026

Fixes #1565.
2015-10-21 15:29:59 +01:00
David Wragg d2dfb0d15d Do InvalidateRoutes when a forwarded packet updates a MAC cache entry
When we get a forwarded packet, we put its source MAC into the MAC cache,
overriding any existing entry.  But we could have a flow which says
to forward packets destined to that MAC based on the old entry.  Such
a flow could be prolonged indefinitely by attempts to send to the MAC.
This is obviously bad, so do InvalidateRoutes in such cases.
2015-10-21 13:46:18 +01:00
Paul Bellamy 63339c026d [proxy] return errors when attaching new containers
I had implemented this with a promise, but was informed that as
waitForStart is only called once, we are safe to just put the error into
the channel.
2015-10-21 13:05:05 +01:00
Adam Harrison 40922b88f7 Reorder inspect template to work around docker/docker#17026 2015-10-21 12:39:55 +01:00
Bryan Boreham 674c617c16 Merge branch '1.1' 2015-10-21 11:52:49 +01:00
Bryan Boreham 2884b6deca Add release v1.1.2 2015-10-20 17:06:14 +01:00
Adam Harrison 720862dafb Merge pull request #1562 from dpw/fdp-create-bridge
'weave --local create-bridge' is not compatible with fastdp
2015-10-20 16:37:20 +01:00
David Wragg 5120b4b468 'weave --local create-bridge' is not compatible with fastdp 2015-10-20 16:34:50 +01:00
Adam Harrison c5f5d4543e Merge pull request #1518 from weaveworks/401-reattach-weave2
Get proxy to re-attach weave router if it restarts
2015-10-20 15:31:13 +01:00
Paul Bellamy baf92da9f2 Merge pull request #1534 from weaveworks/1531-check-hostconfig
Take more care over HostConfig on start request in proxy
2015-10-20 12:18:49 +01:00
Paul Bellamy 526c9c3d73 Add test for {"HostConfig":{}} overriding previous settings, but still connecting to weave 2015-10-20 11:26:29 +01:00
Bryan Boreham 119d87b795 Extract repeated test into function 2015-10-20 11:26:29 +01:00
Bryan Boreham 17b030cca1 Cope with client sending a HostConfig named HostConfig 2015-10-20 11:26:29 +01:00
Bryan Boreham 81c9b3f327 Check that HostConfig parameters supplied on /start are passed to Docker,
also what happens when you supply a HostConfig named HostConfig
2015-10-20 11:26:29 +01:00
Bryan Boreham 50198a2159 Don't create a HostConfig accidentally if none was passed in 2015-10-20 11:26:29 +01:00
Bryan Boreham 20c8b1eaf6 Reverse the sense of a test, to simplify the next change
and remove unused struct
2015-10-20 11:26:21 +01:00
Bryan Boreham faf94b033d Extend test to check we don't overwrite HostConfig 2015-10-20 11:25:06 +01:00
Adam Harrison 342b3d3d08 Merge pull request #1557 from weaveworks/issues/1497
Add container FQDN to /etc/hosts
2015-10-20 11:22:22 +01:00
Bryan Boreham 26d43f193e Move docker restart test above the place where we stop part of weave 2015-10-20 07:07:04 +00:00
Alvaro Saurin f04ae91c82 Add container FQDN to /etc/hosts 2015-10-19 21:51:38 +02:00
Bryan Boreham bb5515113f Make attach-router work in fastdp+encryption case 2015-10-19 15:49:07 +00:00
Bryan Boreham c09d84efc6 Move setup_router_iface call so we don't kill an already-running router on attach-router 2015-10-19 15:49:07 +00:00
Bryan Boreham 2a99a2a597 Don't fail if the arg we're looking for isn't there 2015-10-19 15:47:44 +00:00
Bryan Boreham 08f6dcf08d Missing format 2015-10-19 15:47:44 +00:00
Bryan Boreham 284d78c6b2 Update fetch_router_args in line with changes elsewhere 2015-10-19 15:47:44 +00:00
Bryan Boreham b8b5fb9eae Review feedback 2015-10-19 15:47:43 +00:00
Bryan Boreham 697ee62d9a Use entrypoint to detect weave router 2015-10-19 15:47:43 +00:00
Bryan Boreham 20ac0edda8 Try a bigger sleep to get Docker restarted 2015-10-19 15:46:48 +00:00
Bryan Boreham a1bbb06e2f Don't need depreciation warnings on internal-only command 2015-10-19 15:46:47 +00:00
Bryan Boreham a87cc5c3b1 Get the proxy to re-attach the weave router if it sees it restart 2015-10-19 15:46:47 +00:00
Matthias Radestock 5ff740cddc cosmetic 2015-10-19 15:43:49 +01:00
Matthias Radestock db9229f485 Merge pull request #1551 from weaveworks/1547-proxy-restart-no-kill-containers
Proxy restart shouldn't kill containers

Fixes #1547.
2015-10-19 13:55:48 +01:00
Paul Bellamy ace6c01b00 Review feedback 2015-10-19 13:53:20 +01:00
Bryan Boreham 2f4d460c04 Merge pull request #1517 from weaveworks/1385-non-container-dns-add
LGTM. Fixes #1385
2015-10-19 12:52:09 +01:00
Matthias Radestock d5401ae3cb Merge pull request #1550 from dpw/weave2-eexist
Don't fail in launch-router if weave2 already exists

Fixes #1548.
2015-10-16 21:36:45 +01:00
David Wragg 3482a7f1f1 Don't fail in launch-router if weave2 already exists
And delete weave2 on stop-router.
2015-10-16 20:59:06 +01:00
David Wragg 23762e0639 Move repeated 'stop' code into functions 2015-10-16 20:55:44 +01:00
Bryan Boreham 50cc15af3b Merge pull request #1477 from weaveworks/weave-1374
LGTM.  Fixes #1374
2015-10-16 18:02:16 +01:00
Paul Bellamy a0f0367e60 [proxy] don't kill containers when starting the proxy 2015-10-16 15:33:43 +01:00
Adam Harrison 3f08ddefe9 Correct DisplayName implementation 2015-10-16 12:24:38 +01:00
Adam Harrison debe656f9f Merge pull request #1522 from dpw/fdp-compat
Fast datapath compatibility with 1.1
2015-10-16 11:26:20 +01:00
Alvaro Saurin 4350b6402f Updated docs 2015-10-16 11:47:07 +02:00
Alvaro Saurin 5d2892f6fc Test we respect --add-host when rewriting /etc/hosts
Simplify some tests
2015-10-16 11:47:07 +02:00
Alvaro Saurin 91f27ddd66 Removed weavehosts 2015-10-16 11:47:07 +02:00
Alvaro Saurin 6a674d0d67 Add hosts when attaching 2015-10-16 11:47:07 +02:00
Alvaro Saurin 0d165487b8 Rewrite /etc/hosts in the containers 2015-10-16 11:47:07 +02:00
Matthias Radestock bacc5836ea log listeners 2015-10-16 09:34:20 +01:00
Matthias Radestock 230931a2c9 prevent accidental var shadowing
Fixes #1544
2015-10-16 01:05:04 +01:00
Matthias Radestock 61d289f414 Merge branch '1.1' 2015-10-15 18:14:20 +01:00
Matthias Radestock 44255862a9 prevent spurious errors from showing up
silly mistake
2015-10-15 18:12:56 +01:00
Matthias Radestock ed9577150b suppress spurious errors 2015-10-15 18:01:06 +01:00
Matthias Radestock 80319e7087 Merge branch '1.1' 2015-10-15 17:41:49 +01:00
Matthias Radestock 379048d8fc make test/setup.sh work under go 1.5
Fixes #1543.
2015-10-15 17:40:51 +01:00
Matthias Radestock 9ac655d774 Merge pull request #1526 from weaveworks/fdp-docs
Document fast data path feature

Fixes #1526.
2015-10-15 17:29:29 +01:00
Adam Harrison 9b17470129 Document fast data path feature 2015-10-15 17:19:13 +01:00
Matthias Radestock 5c114fc6fe Merge pull request #1536 from weaveworks/1515-print-json
Print request and response json when debugging weaveproxy

Fixes #1515
2015-10-15 15:59:35 +01:00
Bryan Boreham efb12f41b2 Also print intercepted responses 2015-10-15 15:07:02 +01:00
Adam Harrison 813edcfe0a Merge branch '1.1' 2015-10-15 14:59:29 +01:00
Adam Harrison dea1405d71 Remove hardcoded docker bridge IP from proxy test 2015-10-15 14:39:56 +01:00
Ilya Dmitrichenko e7f91b7529 Update screencast preview image 2015-10-15 14:26:39 +01:00
Matthias Radestock 5e8a64508e Merge remote-tracking branch 'upstream/master' 2015-10-15 14:19:50 +01:00
Matthias Radestock 21ebb7a585 Merge branch '1.1'
Conflicts:
	Makefile
2015-10-15 14:19:14 +01:00
David Wragg 6edd430538 Use consistent options for all "go get"/"go build" invocations
Without this, building with go-1.5 fails with

    go get ./prog/weavewait
    go install net: open /usr/local/go/pkg/linux_amd64/net.a: permission denied

This makes the options passed too "go get"/"go build" consistent for
all executables. So it should be safe with go-1.4.

Conflicts:
	Makefile
2015-10-15 14:16:33 +01:00
Ilya Dmitrichenko 34606e0f9f Link to the new screencast (close #1402) 2015-10-15 14:07:54 +01:00
Matthias Radestock 5f62f9edc7 Merge branch '1.1' 2015-10-15 13:05:35 +01:00
Matthias Radestock 0d2ff55c07 Merge pull request #1535 from weaveworks/1523-rewrite-inspect-failure
[proxy] only attempt to rewrite successful inspects

Fixes #1523.
2015-10-15 13:04:34 +01:00
Matthias Radestock 207ee8667b Merge pull request #1538 from dpw/detect-no-fastdp-mismatch
signal mismatches between WEAVE_NO_FASTDP and the bridge type

Fixes #1533.
2015-10-14 17:17:35 +01:00
David Wragg 5984ed921e signal mismatches between WEAVE_NO_FASTDP and the bridge type 2015-10-14 16:33:23 +01:00
Matthias Radestock aac5fb8c24 Merge pull request #1537 from weaveworks/1502-flakey-test
Wait until container exits in test to remove race condition

Fixes #1502
2015-10-14 16:19:20 +01:00
Matthias Radestock 8cfc8b3132 Merge pull request #1528 from weaveworks/weave-env-bug
[proxy] Fix bug in weave env/config when passing multiple -H flags

Fixes #1527
2015-10-14 15:50:42 +01:00
Paul Bellamy 0b5ebd7b1d Wait until container exits in test to remove race condition 2015-10-14 14:53:23 +01:00
David Wragg 35141a4aef Forward a packet with sleeve if the source or dest peer lacks a short ID 2015-10-14 13:53:59 +01:00
David Wragg e702234fdc Don't use a nil FlowOp to mean "discard the packet" 2015-10-14 13:53:34 +01:00
Bryan Boreham 05dbe23563 Print request and response json when debugging 2015-10-14 13:45:51 +01:00
Paul Bellamy fd07f59d16 [proxy] Fix bug in weave env/config when passing multiple -H flags
The echo in proxy_addr was not preserving newlines, so the multiple
addrs were all getting returned
2015-10-14 11:58:10 +01:00
Paul Bellamy 36de840c91 [proxy] only attempt to rewrite successful inspects 2015-10-14 11:28:43 +01:00
David Wragg b36ffe1c9d Support peers that don't have short IDs 2015-10-14 11:11:34 +01:00
David Wragg 1f1451e666 Fall back to sleeve when "Overlays" not present in features 2015-10-14 11:11:34 +01:00
David Wragg 1000839309 Merge issues/1505-fast-datapath-weave-status' (PR #1525)
Fast datapath weave status
2015-10-14 11:08:09 +01:00
Adam Harrison ed4ed5fdcd Include short peer ID in report JSON 2015-10-13 17:04:56 +01:00
Adam Harrison eaf1f7cc13 Include overlay diagnostics in report JSON 2015-10-13 17:04:56 +01:00
Adam Harrison ddfc8c2fa4 Handle JSON report marshalling error 2015-10-13 17:04:56 +01:00
Adam Harrison 91afa7f7f4 Include selected overlay type in connection status 2015-10-13 17:04:56 +01:00
Paul Bellamy da49e71eb8 Merge pull request #1529 from weaveworks/expose-proxy-tls
Expose some tls functions from the proxy for use elsewhere.
2015-10-13 16:09:27 +01:00
Tom Wilkie 5b8ce8ebae Expose some tls functions from the proxy for use elsewhere. 2015-10-13 14:56:11 +00:00
Adam Harrison 827bcd898e Merge pull request #1438 from weaveworks/fdp
Fast datapath
2015-10-13 10:34:49 +01:00
Matthias Radestock 54a4add40f cosmetic 2015-10-13 09:02:03 +01:00
David Wragg 0093eb1220 A smoke test for WEAVE_NO_FASTDP
Partly for the side effect of restoring coverage of
router/ethernet_decoder.go
2015-10-12 21:27:11 +01:00
David Wragg b56ff7678c Make code coverage work through the proxy
Previously, the proxy would run the weave script in ways that did
not run the weaver executable.  Now they can.  This is a problem because
of the contortions involved in using golang's code coverage support
in the integration tests, which require the weaver command line args to
be slightly mangled.
2015-10-12 21:27:11 +01:00
David Wragg 431197042d Weave script changes to enable fast datapath
This means:

* The 'weave' netdev is an OVS datapath

* The weave router container runs with --net=host in order to access
  the datapath.

* The MTU is set to a smaller value, to allow vxlan encapsulation.

Setting the WEAVE_NO_FASTDP environment variable disables these
things, effectively running the router without fast datapath.  It is
an environment variable rather than an option to 'weave launch'
because it takes effect in create_bridge, which is called from many
subcommands, and it doesn't seem practical to add option processing to
all of them.
2015-10-12 21:27:11 +01:00
David Wragg 295876e1d1 Fast datapath 2015-10-12 21:27:11 +01:00
David Wragg e8f975e44f Introduce checkFatal in prog/weaver/main.go 2015-10-12 21:27:11 +01:00
David Wragg bc026ab49a Show the overlay name in sleeve log messsages 2015-10-12 21:27:11 +01:00
David Wragg f56457271d OverlaySwitch 2015-10-12 21:27:11 +01:00
David Wragg bc5250c899 Replace OverlayForwarderListener with channels
It's more robust to signal the conditions asynchronously via channels
rather than callbacks.
2015-10-12 21:27:11 +01:00
David Wragg 971511e1c5 Introduce PeerShortIDs
These are needed to support vxlan encapsulation.  In a vxlan packet,
the only place we can straightforwardly hold the source and
destination peer identifiers used by the weave router is in the
24-bit VNI (vxlan network identifier) field.  This gives us 12 bits to
indicate a peer. The resulting limit of 4096 peers on a weave network
does not seem like a problem in the near future.  But it does mean
that decentralized allocation makes collisions fairly likely even in
weave networks of sizes that are common today.  Fortunately, the
problem is much more tractable than IPAM because we can detect when
collisions occur and take action to resolve them.
2015-10-12 21:27:11 +01:00
David Wragg 51317dca70 Introduce PeersPendingNotifications 2015-10-12 21:27:11 +01:00
David Wragg 91dda08165 Rename Peers "table" field to "byName" 2015-10-12 21:27:11 +01:00
David Wragg 67bfa5bc90 applyUpdate can return a PeerNameSet 2015-10-12 21:27:11 +01:00
David Wragg 0841f4a27d Tidy broadcastPeerUpdate 2015-10-12 21:27:11 +01:00
David Wragg c6edf8421f Handle the not-found case implicitly in Broadcast and BroadcastAll 2015-10-12 21:27:11 +01:00
David Wragg 97fb90c37a Always include local peer in Peers 2015-10-12 21:27:11 +01:00
Matthias Radestock 1d00bdd2a4 a better way to restrict coverage
this better fixes #1524.
2015-10-12 18:04:07 +01:00
Matthias Radestock 0ff01871f3 Merge remote-tracking branch 'upstream/master' 2015-10-12 17:43:41 +01:00
Matthias Radestock e00a675e6e update tools to latest version 2015-10-12 17:43:06 +01:00
Paul Bellamy d5e63b875b Merge pull request #1395 from weaveworks/1371-proxy-no-dockerclient
[proxy] all request modification is done through map[string]interface{}
2015-10-12 16:42:44 +01:00
Matthias Radestock a31ccef99b only produce coverage for packages under github.com/weaveworks/weave
Fixes #1524.
2015-10-12 16:06:26 +01:00
Paul Bellamy 375fd9140b review feedback
* Opted to remove json path traversal, as it added more code than it saved.
2015-10-12 15:53:07 +01:00
Paul Bellamy fbb2c213a9 [proxy] all request modification is done through map[string]interface{}
That way we are less likely fall victim to go-dockerclient, or the API changing.
2015-10-12 15:53:07 +01:00
Bryan Boreham d323de3c21 Merge pull request #1520 from weaveworks/1519-rebind-status-sock
LGTM. Fixes #1519
2015-10-09 17:58:33 +01:00
Paul Bellamy 18872cc8ae [proxy] remove status.sock before binding status.sock 2015-10-09 17:44:16 +01:00
Tom Wilkie 33085e3798 Merge pull request #1499 from weaveworks/1463-ipam-seed-mismatch
detect when IPAM has been seeded by different peers
2015-10-09 14:46:55 +01:00
Matthias Radestock e42c811797 docs 2015-10-08 22:43:47 +01:00
Matthias Radestock 6b321e4d02 associate non-container dns entries with 'weave:extern'
...instead of the weave container. This is cleaner.

'weave:extern' because typically these entries will refer to external
(non-weave) IP addresses. And because it's the same length as
'weave:expose', which is desirable for 'weave status dns' output.
2015-10-08 22:21:38 +01:00
Matthias Radestock e6b17c701d dns-{add,remove} can add/remove non-container DNS records
...by omitting the container name/id

Fixes #1385.
2015-10-08 17:37:35 +01:00
Bryan Boreham 60bda7ca25 Merge pull request #1512 from weaveworks/1459-ipam-early-container-death
LGTM. Fixes #1459
2015-10-07 16:01:45 +01:00
Adam Harrison b37d2ec6d1 Merge pull request #1494 from weaveworks/1468-template-on-demand
Build GCE template on demand
2015-10-07 15:42:39 +01:00
Matthias Radestock 751ccd02f6 don't stall in ipam on early container death
When a container died before we got into the allocation code (inside
the allocator run loop) we would not cancel the allocation since the
container death event will have been processed by the allocator before
it had any knowledge of the container.

The fix is to move the container-aliveness check inside the allocation
code. That way we still detect the death even if the event has already
flown past.

We only need to perform the check once, since any subsequent death
will result in an event that is processed by the allocator when it has
recorded the container in its state.

Fixes #1459.
2015-10-07 15:20:22 +01:00
Matthias Radestock 431d04bd40 refactor: move all dataplane code out of LocalPeer 2015-10-06 19:50:41 +01:00
Matthias Radestock e756f07cad Merge branch '1.1' 2015-10-06 16:25:45 +01:00
Matthias Radestock 0ef548b9c9 prevent dropping of large multicast packets
Fixes #1507.
2015-10-06 16:25:02 +01:00
Ilya Dmitrichenko b9e025b50e Merge branch '1.1' 2015-10-06 15:28:54 +01:00
Ilya Dmitrichenko 7743f0e05f Merge branch 'latest_release_doc_updates' into 1.1 2015-10-06 15:26:50 +01:00
Ilya Dmitrichenko a1581ed5b5 Remove stale/invalid flag 2015-10-06 15:26:14 +01:00
Ilya Dmitrichenko 7a83a71a02 Merge branch '1.1' 2015-10-06 15:24:25 +01:00
Ilya Dmitrichenko a6c30088ab Merge branch 'latest_release_doc_updates' into 1.1 2015-10-06 15:24:08 +01:00
Ilya Dmitrichenko fdbe950483 Fix #1509 2015-10-06 15:21:24 +01:00
Matthias Radestock 2f86326405 Merge tag 'v1.1.1' into latest_release_doc_updates
Release v1.1.1
2015-10-06 15:13:50 +01:00
Matthias Radestock 8aaf4cfaf0 include nicknames in error message, if we have them 2015-10-06 14:41:41 +01:00
Ilya Dmitrichenko 8a62d13cee Merge pull request #1500 from weaveworks/1443-dont-assume-docker
allow router exe to be run w/o access to Docker
2015-10-06 14:28:26 +01:00
Matthias Radestock 5a45471c68 more readable errors on ring.Merge
There are three significant changes here:

- rewrite ring.ErrDifferentSeeds errors
- remove the pre-checking of ranges and instead rewrite
  ring.ErrDifferentRange errors
- don't pruneNicknames() and signal ringUpdated() when there was an
  error

We don't rewrite other errors since our current belief is that only
ErrDifferentSeeds and ErrDifferentRange can arise from user error.
2015-10-06 14:20:42 +01:00
Matthias Radestock 6dc21bf433 Merge pull request #1503 from weaveworks/860-detach-hide-no-ipalloc
don't allocate IPs on 'detach' and 'hide'

Fixes #860.
2015-10-06 12:53:51 +01:00
Matthias Radestock 1da24c68ca don't allocate IPs on 'detach' and 'hide'
Instead of POSTing to IPAM in order to obtain IPs, we GET existing
IPs. We ignore 404s, so detaching/hiding remains idempotent.

Fixes #860
2015-10-06 12:35:55 +01:00
Matthias Radestock 3d3b7a5672 Merge branch '1.1'
Conflicts:
	router/ethernet_decoder.go
	router/local_peer.go
2015-10-05 20:02:32 +01:00
Matthias Radestock 6c4ea7a957 unswap src and dst ip in "too big DF broadcast" warning
and some whitespace cosmetics.

Fixes #1508.
2015-10-05 19:56:59 +01:00
Matthias Radestock f221a6d953 Merge branch '1.1' 2015-10-05 12:08:54 +01:00
Matthias Radestock de95859e0d add release v1.1.1 2015-10-05 11:26:24 +01:00
David Wragg bcf71c0fee Merge branch '1.1' 2015-10-04 20:25:31 +01:00
David Wragg 8fe0228d4d Merge 1364-ip-link-kernel-4.2 (PR #1504)
strip off @... from interface names returned by `ip link show`
2015-10-04 20:23:43 +01:00
Matthias Radestock 37ea8a28ad strip off @... from interface names returned by ip link show
Apparently kernel 4.2 adds these, which breaks `weave reset` when we
pass the names to `ip link del'.

Fixes #1364.
2015-10-04 15:43:55 +01:00
Matthias Radestock a689317696 make 'no Docker API access' the default in the exe
...and instead enable it in the script, via the (better named) option.
2015-10-03 17:52:57 +01:00
Matthias Radestock 14a5c12404 allow router to be run w/o access to Docker
Fixes #1443.
2015-10-03 17:52:57 +01:00
Matthias Radestock 35b2968b60 Merge branch '1.1' 2015-10-03 17:20:21 +01:00
Matthias Radestock dac932f553 fix multi-ip attach
This got broken in f7dfe9d
2015-10-03 17:09:25 +01:00
Matthias Radestock 7e0ae6112c detect when IPAM has been seeded by different peers
This is an early detector for a multitude of other symptoms, some of
which cause panics.

When creating the ring we record the paxos-supplied normalised list of
peers used for seeding. This subsequently gets gossipped around as
part of the Ring data structure, and checked in ring.Merge that it is
the same for the recipient.

There are some subtleties due to the desire to maintain backward
compatibility and deal with empty rings

- The gossip gob decoder will ignore the Ring.Seeds field when an old
  weave receives a Ring from a new weave. So everything on the old
  weave will just continue to work as before.

- The gossip gob decoder will set the Ring.Seeds field to nil when a
  new weave receives a Ring from an old weave. We therefore omit the
  seed equivalence check when the received Seeds slice is empty.

- Our ring may not have any seeds - either because it is empty or was
  created from a ring received from an old weave. We therefore a) omit
  the seed equivalence check in that situation, and b) set our seeds
  to those received. Note that this allows us to learn about the seeds
  well after ring creation - a situation which may arise when
  receiving gossip from a mixture of old and new weaves.

Fixes #1463.
2015-10-02 16:33:28 +01:00
Bryan Boreham 9ad1ccf19f Don't listen to SIGUSR1, since we stopped handling it a few months back 2015-10-02 13:52:23 +01:00
Bryan Boreham 5b7599e1b0 Merge pull request #1496 from weaveworks/953-backoff-late-connection-failures
LGTM.  The timing choices are all very arbitrary, so we should wait for more experience in the wild before trying to tune them further.

Fixes #953
2015-10-02 12:44:52 +01:00
Matthias Radestock 19c44b83d2 refactor: better 'try' method names 2015-10-02 11:31:11 +01:00
Matthias Radestock 41691957d2 refactor: simplify target status reporting 2015-10-01 20:05:12 +01:00
Matthias Radestock e3182624fd refactor: more obviously handle all target states in reporting 2015-10-01 20:05:12 +01:00
Matthias Radestock b8b3e6f9cd refactor: better representation of 3-valued target state 2015-10-01 20:05:12 +01:00
Matthias Radestock 520c7286c4 backoff retries for late-failing connections
If a connection gets through all the handshake and subsequent checks -
i.e. it ends up getting added to the LocalPeer's connection list - but
gets terminated within one minute (of the start of the successful
connection attempt), then we apply the existing backoff logic to
schedule a retry, instead of retrying immediately.

In order to do this we retain the 'Target' data structure for such
connections, marking it as 'connected' instead of deleting it (as we
did previously).

Fixes #953.
2015-10-01 20:05:12 +01:00
Matthias Radestock d547829c5f Merge pull request #1495 from weaveworks/1492-proxy-remove-var-run-mount
Remove /var/run mount from exec_remote. Proxy uses /var/run/weave/weave.sock

Fixes #1492.
2015-10-01 16:58:37 +01:00
Matthias Radestock 49459cfc9b Merge pull request #1462 from weaveworks/1461-dns-case-sensitive
DNS lookups should not be case sensitive.

Fixes #1461.
2015-10-01 16:52:00 +01:00
Paul Bellamy 827ba446ab Remove /var/run mount from exec_remote. Proxy uses /var/run/weave/weave.sock
It was left in for backwards compatibility, in 1.1.
2015-10-01 15:52:29 +01:00
Tom Wilkie e5316221bb Review feedback 2015-10-01 14:14:15 +00:00
Tom Wilkie 357174e351 DNS lookups should not be case sensitive.
- On-the-wire gossip messages are sorted case sensitive, but in memory they are sorted case-insensitive.
- Add test for case insensitive match on multiple names.
2015-10-01 12:19:49 +00:00
Tom Wilkie 5fc2f3236e Build GCE template on demand 2015-10-01 11:49:31 +00:00
Matthias Radestock a3484cce43 Merge pull request #1488 from weaveworks/1481-proxy-null-hostconfig
[proxy] Handle null HostConfigs gracefully in start

Fixes #1481.
2015-10-01 12:06:51 +01:00
Matthias Radestock cdd107b06b Merge branch '1.1'
Conflicts:
	weave
2015-10-01 12:04:44 +01:00
Bryan Boreham 6619b584bc Merge pull request #1483 from weaveworks/1478-cm-termination-race
LGTM.  Fixes #1478
2015-10-01 12:01:55 +01:00
Matthias Radestock d798b0cabb Merge pull request #1476 from weaveworks/1455-mount-var-run
unmount /var/run after boot, so the kernel can GC veth pairs

Fixes #1455.
2015-10-01 11:55:24 +01:00
Paul Bellamy 29bfd77855 [proxy] don't mount /var/run into the proxy, so the kernel can GC veth pairs 2015-10-01 11:43:13 +01:00
Alfonso Acosta c24dc6609d [proxy] Handle null HostConfigs gracefully in start 2015-10-01 11:40:50 +01:00
Adam Harrison b5e7e9fc5c Merge pull request #1451 from dpw/sleeve-compat
Restore sleeve compatibility with 1.1
2015-10-01 11:24:31 +01:00
Tom Wilkie 58dec7334b Merge branch '1.1' 2015-10-01 08:23:15 +00:00
Tom Wilkie 0b7af28c86 Typo in Makefile. 2015-10-01 08:21:55 +00:00
Matthias Radestock 725f99ea4c Merge branch '1.1' 2015-10-01 08:23:52 +01:00
Matthias Radestock 526259ef8e Merge pull request #1487 from weaveworks/1480-dns-test-race
Fix races in TestRecursiveCompress

Fixes #1480.
2015-10-01 08:11:48 +01:00
Matthias Radestock 4b43cac093 Merge branch '1.1' 2015-09-30 23:34:57 +01:00
Matthias Radestock 3795c14931 better 2015-09-30 22:57:10 +01:00
Matthias Radestock 3e5cdd8bf2 refactor: remove pointless NXDOMAIN tests 2015-09-30 22:42:27 +01:00
Matthias Radestock 35058a1dde refactor: put negative DNS queries in one test suite 2015-09-30 22:38:48 +01:00
Matthias Radestock 0159e26631 store connections as a set instead of map-of-remote-addresses
...since it is theoretically possible that the lifecycle events from
two consecutive inbound connections from the same remote address
overlap.
2015-09-30 17:29:13 +01:00
Tom Wilkie 11d93b1883 Fix races in TestRecursiveCompress 2015-09-30 15:51:07 +00:00
Tom Wilkie f86f392641 Merge pull request #1486 from weaveworks/1482-disable-160
Disable 160_claim test.
2015-09-30 16:20:34 +01:00
Tom Wilkie 19cb1ae8e1 Merge branch '1.1' 2015-09-30 15:06:26 +00:00
Matthias Radestock cd8befe43e Merge pull request #1485 from weaveworks/1450-check-slice
Sanity-check gossip messages
2015-09-30 15:41:21 +01:00
Tom Wilkie db46204ff9 Disable 160_claim test. 2015-09-30 14:22:24 +00:00
Bryan Boreham d2290cc82c Sanity-check gossip messages 2015-09-30 14:39:27 +01:00
David Wragg f8e06f9dbc Merge 1286-tool (PR #1470): Use test script from build-tools.git 2015-09-30 14:15:20 +01:00
Matthias Radestock e68ca761a9 we no longer need to Refresh() in CM status
since CM now gets an event - ConnectionCreated - for new connections.
2015-09-30 14:00:32 +01:00
Matthias Radestock fc6f3993bd refactor(ish): obtain local connection status from CM alone 2015-09-30 14:00:32 +01:00
Matthias Radestock f8b074f696 a better way to clean up after established connections
...than 3db4e86
2015-09-30 14:00:32 +01:00
Matthias Radestock 66f14099ed prevent ConnectionMaker.ConnectionTerminated race
Get the ConnectionMaker to maintain its own knowledge of the local
connections, based on events emitted in the connection lifecycle.

Fixes #1478.
2015-09-30 14:00:32 +01:00
Tom Wilkie f3dc681f61 Only clean weave packages. 2015-09-30 10:15:50 +00:00
Tom Wilkie 62f62e1fd5 tools.git is now called build-tools.git 2015-09-30 08:21:19 +00:00
Tom Wilkie f3360e61e1 Remove cover tool, this lives in tools.git now. 2015-09-30 08:21:12 +00:00
Tom Wilkie 5208dcd67e Review feedback
- Use git submodule for tools.
- Update Makefile to use submodules.
- Put submodule at tools/
- Tidy up some obsolete references to tools in .gitignore.
2015-09-30 08:21:08 +00:00
Tom Wilkie 6f7d4a7a01 Use lint from tools.git
- Do lint first, on circle.
2015-09-30 08:20:56 +00:00
Tom Wilkie 958a3bbd99 Use test script from tools.git 2015-09-30 07:42:43 +00:00
Matthias Radestock d5c85a3d53 cosmetic: fix typo 2015-09-29 19:40:54 +01:00
Tom Wilkie 5e7b527af2 Make publish from the right place. 2015-09-29 11:02:46 +00:00
Paul Bellamy ddbd33a8e3 Merge pull request #1471 from weaveworks/1295-publish
Publish latest images from CI on successful builds.
2015-09-29 11:37:44 +01:00
Matthias Radestock a7dc368191 Merge pull request #1469 from weaveworks/1204-test-container
Run tests in a separate container.

Fixes #1204.
2015-09-29 11:37:25 +01:00
Matthias Radestock 5b1d0c55a0 Merge pull request #1474 from weaveworks/1472-weave-env-debug
use a container-local unix socket to get the address, instead of logs

Fixes #1453. Fixes #1472.
2015-09-29 11:04:41 +01:00
Matthias Radestock 58515ca36e make weave(proxy) startup death message more generic
...in order to account for `docker logs` not working due to custom
logging drivers.

Partially undoes #958. Partially fixes #1453.
2015-09-29 11:02:05 +01:00
Matthias Radestock 2d753ebf1a cosmetic: reduce confusion w.r.t. meaning of 'connection' 2015-09-29 09:21:39 +01:00
Tom Wilkie 50cd716220 Review feedback. 2015-09-29 07:46:00 +00:00
Matthias Radestock 47c97e8191 refactor: remove some code duplication 2015-09-28 16:10:43 +01:00
Paul Bellamy d704ba175f [proxy] use a container-local unix socket to get the address, instead of logs 2015-09-28 16:08:25 +01:00
Matthias Radestock c1036c5e4f Merge remote-tracking branch 'upstream/master' 2015-09-28 14:53:12 +01:00
Matthias Radestock 356d06915b refactor weave script http calls
- inline http_call into call_weave
- rename http_call_ip to http_call
- make http_call take the IP & port as one single "address" argument
  instead of separate args
2015-09-28 12:43:09 +01:00
Tom Wilkie 2b4fd738da Review feedback 2015-09-28 09:05:10 +00:00
Tom Wilkie f9de6df426 Publish latest images from CI on successful builds.
This time rebuild without insturmentation first.
2015-09-28 08:27:21 +00:00
Tom Wilkie 8ee409d9bd Run tests in a separate container. 2015-09-25 06:19:00 +00:00
Matthias Radestock 0cf3de4541 Merge pull request #1446 from weaveworks/smoke-subdomains
Add smoke-test for subdomains
2015-09-24 15:55:57 +01:00
Bryan Boreham 3a79be7426 Add smoke-test for subdomains 2015-09-24 14:48:39 +01:00
David Wragg 30589bb580 Mention the mailing list 2015-09-24 14:40:20 +01:00
Matthias Radestock e07b18bb18 Merge branch '1.1' 2015-09-24 12:08:13 +01:00
Matthias Radestock 63e3f441d8 cosmetic: link to relevant RFC section for ARP announcements 2015-09-24 12:07:12 +01:00
Tom Wilkie b31eb3db82 Merge remote-tracking branch 'upstream/1.1' 2015-09-24 03:54:09 +00:00
Tom Wilkie 99ae38c9a6 Merge pull request #1467 from weaveworks/docker-machine
Update for docker/machine PR #1729
2015-09-24 11:53:21 +08:00
Tom Wilkie 011f96c6a7 New test image using docker 1.8 2015-09-24 03:42:44 +00:00
Tom Wilkie c7b4136d31 Update for docker/machine PR #1729 2015-09-24 03:25:30 +00:00
Bryan Boreham da2f98fa8f Remove workaround for upstream, no longer required 2015-09-23 10:15:06 +01:00
Tom Wilkie 7ac532a1d0 Merge pull request #1454 from weaveworks/1433-docker-1.8
New test image using docker 1.8
2015-09-23 10:16:06 +08:00
Matthias Radestock b98ab70517 Merge branch '1.1' 2015-09-22 21:18:32 +01:00
Matthias Radestock b1d453a0c2 Merge pull request #1460 from weaveworks/simplify-ipam-cidrs
Simplify ipam_cidrs function in weave script
2015-09-22 21:17:42 +01:00
Bryan Boreham a51f63267a Merge 0-args case in ipam_cidrs() and simplify loop 2015-09-22 21:06:12 +01:00
Matthias Radestock 0bdc84d42a Merge branch '1.1' 2015-09-22 16:28:05 +01:00
Matthias Radestock 7f74ae7912 Merge pull request #1426 from weaveworks/1418-clean-cancel
Handle cancelled allocation in 'weave attach'

Fixes #1418.
2015-09-22 16:27:27 +01:00
Bryan Boreham abf13c7026 Simplify detection of issues 2015-09-22 16:14:14 +01:00
Matthias Radestock 7d7c5ce63e Merge pull request #1449 from weaveworks/1368-start-hostconfig
Override sneaky /start HostConfig params

Fixes #1368.
2015-09-22 15:11:36 +01:00
Paul Bellamy 547c9e7f5a Removing confusing variable from getDNSDomain, just return "" when disabled 2015-09-22 15:09:32 +01:00
Paul Bellamy 3e4d8a3ffe review feedback 2015-09-22 15:09:32 +01:00
Paul Bellamy 74c3b0d079 [proxy] Override sneaky /start HostConfig params 2015-09-22 15:09:32 +01:00
Paul Bellamy 846aa98a9c renaming test so it runs 2015-09-22 14:52:28 +01:00
Matthias Radestock 17b7789531 Merge pull request #1456 from weaveworks/fix-race-condition
[proxy] fix race condition when waiting for containers to start
2015-09-22 14:51:43 +01:00
Paul Bellamy b86d9bf1ab [proxy] fix race condition when waiting for containers to start
If ContainerStarted fired before the response handler, then the "waiter"
chan would be nil, and the response handler would hang.
2015-09-22 12:50:36 +01:00
Tom Wilkie 6e06ce2af1 New test image using docker 1.8 2015-09-22 05:31:26 +00:00
David Wragg dc275d7009 Restore sleeve compatibility with 1.1
Addresses #1396
2015-09-21 08:33:11 +01:00
David Wragg a67b9f566a Use consistent options for all "go get"/"go build" invocations
Without this, building with go-1.5 fails with

    go get ./prog/weavewait
    go install net: open /usr/local/go/pkg/linux_amd64/net.a: permission denied

This makes the options passed too "go get"/"go build" consistent for
all executables. So it should be safe with go-1.4.
2015-09-20 19:58:04 +01:00
Michael Bridgen 9344289262 Merge pull request #1445 from weaveworks/docs-ecs-amis
Document ECS integration
2015-09-17 14:43:38 +01:00
Alfonso Acosta 6713282a43 Link to ECS AMIs from README 2015-09-17 13:27:32 +00:00
Matthias Radestock f834626f00 Merge pull request #1414 from weaveworks/1285-autoconfig-boot2docker-tls
Proxy TLS autoconfiguration

Closes #1285.
2015-09-16 15:49:20 +01:00
Bryan Boreham 98b80c59d9 Detect cancelled allocation more cleanly
to avoid the weave script trying to parse an error message as a CIDR
2015-09-16 14:14:40 +01:00
Bryan Boreham 1beaf79e47 Refactor: extract duplicated code into helper functions 2015-09-16 14:14:30 +01:00
Paul Bellamy 0a475bd4cc docker_tls_args should look for '-d' or 'daemon' to handle docker 1.8 change 2015-09-16 13:25:29 +01:00
Paul Bellamy 0820c484d6 [proxy] Documenting TLS autoconfig 2015-09-16 10:44:18 +01:00
Paul Bellamy dbf39403d3 [proxy] Autoconfigure TLS based on daemon settings
With this on OSX you can:
```sh
$ boot2docker init
$ boot2docker up
$ eval $(boot2docker shellinit)
$ weave launch
$ eval $(weave env)
```
2015-09-16 10:44:17 +01:00
Matthias Radestock 14dde9ec32 Merge branch '1.1' 2015-09-15 16:47:05 +01:00
Matthias Radestock 8e656c1fd7 Merge pull request #1429 from weaveworks/1424-fix-cancel
Use explicit index into operations slice in cancelOpsFor()

Fixes #1424
2015-09-15 16:45:11 +01:00
Bryan Boreham d55c68c248 Review feedback 2015-09-15 16:18:37 +01:00
Matthias Radestock 3c266dce8c Merge branch '1.1' 2015-09-15 15:51:36 +01:00
Matthias Radestock 8ec3ab5e28 Merge pull request #1435 from weaveworks/update-vagrantfile-docker-repos
Update docker repo, keys and package name
2015-09-15 15:49:33 +01:00
Matthias Radestock f7be34f5bb Merge branch '1.1' 2015-09-15 15:44:53 +01:00
Matthias Radestock 58a30f7282 Merge branch 'latest_release_doc_updates' into 1.1 2015-09-15 15:44:15 +01:00
Matthias Radestock 2b5c2874ab remove 'lxc-docker' reference from build-on-ubuntu instructions
...since the Docker docs have been updated to install docker-engine.
2015-09-15 15:34:44 +01:00
Matthias Radestock 64b47ca3c7 Merge branch '1.1' 2015-09-15 13:17:06 +01:00
Matthias Radestock bed11f4214 Merge pull request #1431 from weaveworks/issues/1397-strip-version-prefix
Automatically strip 'v' prefix for mainline and branch releases

Fixes #1397.
2015-09-15 13:15:49 +01:00
Adam Harrison bbf7646751 Update docker repo, keys and package name 2015-09-15 11:33:53 +01:00
Adam Harrison 6dfefb95e8 Advise installation of weaveworks github-release fork 2015-09-15 10:47:43 +01:00
Adam Harrison 44c2861aba Update release process doc 2015-09-15 10:41:41 +01:00
Adam Harrison 3039046a6d Strip v prefix for mainline and branch releases 2015-09-15 10:41:24 +01:00
Paul Bellamy e39974eb07 Merge pull request #1210 from weaveworks/401-proxy-start-events
make proxy attach network on docker start event
2015-09-15 10:18:48 +01:00
Bryan Boreham 76895c76a1 Use Mutex instead of RWMutex, for simplicity 2015-09-14 17:11:56 +01:00
Bryan Boreham a7e0f03647 Use explicit index into operations slice in cancelOpsFor()
to avoid mis-indexing when more than one op is removed
2015-09-14 17:02:35 +01:00
Bryan Boreham 98e8ef4e11 Merge remote-tracking branch 'upstream/1.1' 2015-09-14 16:33:12 +01:00
Bryan Boreham 5635ba1fd0 Update out-dated error message 2015-09-14 16:31:37 +01:00
Adam Harrison 2ffe9b53f7 Merge branch '1.1' 2015-09-14 14:21:36 +01:00
Adam Harrison dd0eee6418 Merge pull request #1411 from weaveworks/1405-arping-dynamic-attach-1
always arp_update new addresses in 'attach'
2015-09-14 14:20:21 +01:00
Paul Bellamy 405c2889f0 Merge pull request #1415 from weaveworks/1341_report_snapshot_versions
correctly report snapshot versions
2015-09-14 10:45:29 +01:00
Matthias Radestock 40cf392504 cosmetic: vertical alignment in version output 2015-09-11 15:56:17 +01:00
Matthias Radestock 42baa03985 report the proxy version instead of weaveexec version
That way we no longer rely on the weavexec image version tag, which
may not always be present. Since the proxy is embedded in the
weaveexec image, this effectively versions the image.

Also report the *local* script version instead of the embedded script
version. There is no point in reporting the latter since it will
always match the weaveexec version.

Closes #1341.
2015-09-11 15:52:50 +01:00
Matthias Radestock 016f20b2d4 Merge branch '1.1' 2015-09-11 14:07:59 +01:00
Matthias Radestock 83ef93f3fb detect running proxy regardless of version
Fixes #1413.
2015-09-11 14:06:44 +01:00
Matthias Radestock d5faf31d43 Merge remote-tracking branch 'upstream/master' 2015-09-11 13:36:59 +01:00
David Wragg 12324e3550 Revert "Make weave build under go1.5; use our build flags for go get, and force stuff to be static."
This reverts commit 5860492a38.

Although the build works under 1.4, this change had an unfortunate
impact (see #1412).
2015-09-11 13:31:15 +01:00
Matthias Radestock ff67499562 cosmetic 2015-09-11 13:30:22 +01:00
Matthias Radestock 2ba5abc2bb rename 'VERSION' to 'WEAVE_VERSION'
to avoid potential accidental clashes with user's environment.

Closes #1389.
2015-09-11 11:02:51 +01:00
Matthias Radestock f7dfe9dd23 always arp_update new addresses in 'attach'
Previously we'd only do that when the container had no ethwe at the
start. Therefore adding a recycled IP address to an already attached
container would not send out an arp update for that IP, which would
break connectivity to that IP for containers which had previously
accessed that IP.

Fixes #1405.
2015-09-10 18:43:49 +01:00
Adam Harrison 3d4a8aaad1 Merge branch '1.1' 2015-09-10 17:24:26 +01:00
Adam Harrison ac145052d6 Correct weave stop advice
Fixes #1408.
2015-09-10 17:23:33 +01:00
Matthias Radestock 4ded1f6193 Merge branch '1.1' 2015-09-09 20:06:34 +01:00
Matthias Radestock 7a41337d17 Merge branch 'latest_release_doc_updates' into 1.1 2015-09-09 20:06:14 +01:00
Matthias Radestock 2d006dffaf add link to latest release to docs homepage
Closes #1400.
2015-09-09 20:04:37 +01:00
Matthias Radestock 940a0a67a5 Merge branch '1.1' 2015-09-09 19:54:46 +01:00
Matthias Radestock 69c935a6e8 Make latest_release description link to real release
Closes #1399.
2015-09-09 19:52:08 +01:00
Adam Harrison be304365a0 Merge branch '1.1' 2015-09-09 14:26:34 +01:00
Adam Harrison 676127893f Escape double curly braces eaten by Jekyll/Liquid 2015-09-09 14:21:31 +01:00
Adam Harrison df62d512cd Correct weave report example in troubleshooting guide 2015-09-09 14:00:53 +01:00
Adam Harrison fc00767cf8 Remove reference to 1.0 from README 2015-09-09 13:56:25 +01:00
David Wragg 2163b39268 Merge weaveworks/go-1.5 (PR #1386)
Make Weave build & work with Go 1.5
2015-09-09 12:23:57 +01:00
Matthias Radestock aa61cbd574 Merge pull request #1388 from weaveworks/alpine
gliderlabs/alpine is now just alpine
2015-09-08 14:01:40 +01:00
Adam Harrison 2a89d53d7d Merge pull request #991 from dpw/fdp-prep
Fast datapath preparations
2015-09-08 13:19:03 +01:00
David Wragg f7ce6a1fdb Injected packets shouldn't happen in handleCapturedPacket
Check for them just in case, but don't take an extra lock to do it.
2015-09-08 10:46:04 +01:00
David Wragg 20b35f3139 Add NewPeerFromSummary
To reduce the number of places that are explicit about which values
go into a new Peer.
2015-09-08 10:46:02 +01:00
David Wragg 46d454730f Notify the Overlay when routes change 2015-09-08 10:44:57 +01:00
David Wragg c2ad4c5633 Flows
For fast datapath, the router core will only be invoked upon datapath
misses.  And it needs to yield routing decisions in a form that can be
used to configure the in-kernel datapath.  So here we make the router
core yield FlowOps, which represent operations which actually do
something with a packet.  The router core obtains these in turn from
the Bridge or the Overlay, depending on what it wants to do with
a packet.

For fast datapath, it's critical that the router makes decisions based
only on the required packet header information (as the same header
information will be used when configuring the ODP flow rules).  So we
pass only that information into the router core, in the form of
PacketKeys.

The overall effect is to curry the router core: Rather than taking a
packet and doing something with it, it takes packet header information
and returns a function that does something with a packet.
2015-09-08 10:44:52 +01:00
David Wragg 6e0795253e Move ICMP frag-needed handling into sleeveForwarder 2015-09-08 10:44:52 +01:00
David Wragg 9956d422f2 Introduce Overlay / sleeve
This introduces interfaces to represent the packet forwarding
mechanism, and brings all the scattered UDP-related code together into
a single implementation.

This is not a pure refactoring, although much of the code in sleeve.go
has been copied from pre-existing code, and the UDP protocol remains
the same in spirit.  The biggest change is the organization of the
forwarder goroutines (there is now one instead of two, and that
goroutine does a lot of auxiliary work as well as forwarding).
Performance testing between VMs on my laptop suggests that packet
throughput has not gone down, and may even have improved slightly.
2015-09-08 10:44:44 +01:00
David Wragg 57e78e2bec Introduce Bridge 2015-09-08 10:44:37 +01:00
David Wragg 5aa58e6043 go 1.5 broke PMTU detection 2015-09-07 17:31:18 +01:00
Adam Harrison 64e3cf04cc Add release v1.1.0 2015-09-07 16:43:03 +01:00
Adam Harrison 4f9185e3fe Merge branch 'weave_1.1_readme_updates' 2015-09-07 16:38:28 +01:00
Adam Harrison 4dbccea5df Add missing quotes 2015-09-07 16:38:02 +01:00
Adam Harrison a412268154 Merge branch '1.0' 2015-09-07 15:59:17 +01:00
Tom Wilkie 5860492a38 Make weave build under go1.5; use our build flags for go get, and force stuff to be static. 2015-09-07 14:45:17 +00:00
Matthias Radestock d79ac3c12c Merge pull request #1387 from weaveworks/issues/1383-switch-release-behaviour-on-type
Only update DockerHub `latest` images on mainline release

Fixes #1383.
2015-09-07 12:53:07 +01:00
Matthias Radestock 85c309eee0 fix broken link 2015-09-05 16:04:21 +01:00
Matthias Radestock f782fb569f Merge pull request #1391 from weaveworks/1390-use-number
Stop marshalling numbers as floats

Fixes #1390.
2015-09-04 20:15:58 +01:00
Bryan Boreham c50968a0a4 Stop marshalling numbers as floats 2015-09-04 20:01:31 +01:00
Matthias Radestock 99b482b363 Merge pull request #1384 from weaveworks/dns-hostname-from-labels
[proxy] Add --hostname-from-label

Closes #1381.
2015-09-04 17:14:44 +01:00
Alfonso Acosta ce5dd847e9 [proxy] Add --hostname-from-label
This allows registering container hostnames based on a label provided when
creating the container.
2015-09-04 17:02:07 +01:00
Adam Harrison 5c0383059e Update documentation 2015-09-04 15:45:27 +01:00
Adam Harrison 6f672f9bc1 Infer release type
Script adjusts its behaviour based on the format of the version tag.
2015-09-04 15:44:48 +01:00
Paul Bellamy e74b6cbcaf gliderlabs/alpine is now just alpine 2015-09-04 14:33:18 +01:00
Matthias Radestock 32a82f2d0e Merge pull request #1376 from weaveworks/1199-proxy-kubernetes
[proxy] add --rewrite-inspect flag

Fixes #117. Fixes #1199.
2015-09-04 14:27:16 +01:00
Matthias Radestock 633df0cdaf refactor: extract docker API endpoint regexp creation 2015-09-04 13:40:07 +01:00
Paul Bellamy 32a12578f3 Don't use go-dockerclient to en/decode inspects in the proxy 2015-09-04 13:40:07 +01:00
Paul Bellamy 11b655154c "Clarifying" proxy --rewrite-inspect documentation 2015-09-04 13:40:07 +01:00
Paul Bellamy 19ceb424c3 [proxy] refactoring out common marshalling logic 2015-09-04 13:40:07 +01:00
Paul Bellamy e21a204eed [proxy] add --rewrite-inspect flag
To return the weave ip/mac for docker inspect calls.
2015-09-04 13:40:06 +01:00
Bryan Boreham 145a527626 Merge pull request #1379 from awh/issues/1369-weave-dns-add-remove-optional-ip
LGTM.  Fixes #1369
2015-09-04 12:13:38 +01:00
Paul Bellamy b33bbdb3f8 Merge commit '704e6a8' 2015-09-04 10:41:27 +01:00
Paul Bellamy 704e6a8676 errcheck says I should handle this case 2015-09-04 10:40:47 +01:00
Matthias Radestock 4c6a8f29c0 Merge pull request #1372 from weaveworks/issues/880-draft-releases
Support draft and pre-releases in `bin/release`

Fixes #880. Fixes #909.
2015-09-03 15:54:07 +01:00
Adam Harrison 5dfe1d28ff Add release 1.0.3 2015-09-03 15:02:57 +01:00
Adam Harrison 5744ed8994 Document release process 2015-09-03 13:01:29 +01:00
Matthias Radestock c71d9b126f Merge pull request #1380 from weaveworks/598-update-docs
Update docs for mixed manual and automatic IP allocation

Missed from #1200.
2015-09-03 12:27:24 +01:00
Bryan Boreham 5b437d2199 Update docs for mixed manual and automatic IP allocation 2015-09-03 12:17:31 +01:00
Adam Harrison 0125e67b35 Implement smoke test 2015-09-03 09:59:37 +01:00
Adam Harrison 1472e35011 Update documentation 2015-09-03 09:59:37 +01:00
Adam Harrison 58ea422ea2 Optional address argument to dns-add/dns-remove 2015-09-02 17:10:48 +01:00
Adam Harrison 3097565664 Merge branch '1.0'
Conflicts:
	site/weavedns.md
2015-09-02 17:09:39 +01:00
Adam Harrison 81134b408d Merge branch 'latest_release_doc_updates' into 1.0 2015-09-02 17:07:48 +01:00
Adam Harrison 26fdd5da03 Remove misplaced sentence 2015-09-02 17:06:23 +01:00
Adam Harrison 6a5df80bda Fix latest_release hash test
Obtain the version and latest_release tag commit hashes before changing
into the cloned repository so that all tagging and pushing happens on
the outer repository.
2015-09-02 14:13:11 +01:00
Bryan Boreham 1e2344341d Wait for container to be attached before returning from {re}start request 2015-09-01 17:48:22 +01:00
Bryan Boreham b712ba1e04 Extend test to cover containers restarting automatically 2015-09-01 17:48:22 +01:00
Bryan Boreham 9ae6435317 Attach existing weave containers at startup 2015-09-01 17:48:22 +01:00
Bryan Boreham 81e8ba223f Do network attach on docker event rather than intercepting start and restart commands.
This means it also works when the docker daemon restarts a container.
2015-09-01 17:48:12 +01:00
Adam Harrison b13baccbd2 Introduce draft step
The publish stage has been split into two pieces - a draft step that
creates a draft version release in Github and a publish step that
publishes the draft, updates `latest_release` and pushes images to
DockerHub.

It is also possible to specify `--pre-release` to draft. Once published
such a release will be marked with a prominent warning in GitHub;
additionally we do not update `latest_release` in GitHub nor push an
update to the `latest` tag in DockerHub.
2015-09-01 17:04:11 +01:00
Adam Harrison 82f2f7b2b9 Check for required tooling at top level
Pull common checks out to top level in preparation for the introduction
of an additional process step.
2015-09-01 17:04:11 +01:00
Adam Harrison f48a4373d1 Allow freeform version tags
To support arbitrarily named pre-releases we no longer impose any
restrictions on the format of the most recent annotated tag - instead we
expect to find a single annotated tag pointing at HEAD of the current
branch and assume this is the version to release.
2015-09-01 17:04:11 +01:00
Adam Harrison ab9d1777a1 Support make UPDATE_LATEST=false publish
When publishing pre-releases via the release script we do not want to
modify the `latest` tag in DockerHub - specifying UPDATE_LATEST=false
causes publish to push to $WEAVE_VERSION only. Omitting the flag
entirely retains the existing behaviour for backwards compatability.
2015-09-01 17:04:11 +01:00
Bryan Boreham 6a34995796 Merge pull request #1370 from weaveworks/docker-py-test-matcher
LGTM. Fixes #1367
2015-09-01 16:47:24 +01:00
Bryan Boreham df25f84011 Merge pull request #1375 from weaveworks/1373-document-no-rewrite-hosts
LGTM.  Fixes #1373
2015-09-01 15:01:51 +01:00
Paul Bellamy cead9579be document --no-rewrite-hosts flag 2015-09-01 11:01:45 +01:00
Paul Bellamy a23c0237c3 using --collect-only so we don't miss tests 2015-09-01 10:50:13 +01:00
Matthias Radestock 5c3efbdca0 Merge pull request #1366 from weaveworks/python-call-changed
Fix docker api proxy smoke-tests after docker-py change
2015-08-28 20:50:35 +01:00
Bryan Boreham 176ee73efe Ensure we have the busybox image 2015-08-28 20:34:43 +01:00
Bryan Boreham 54833195ed Call to docker-py changed upstream 2015-08-28 20:34:43 +01:00
Matthias Radestock 8960a3a898 Merge branch '1.0'
Conflicts:
	proxy/create_exec_interceptor.go
	test/600_proxy_docker_py_test.sh
2015-08-28 20:29:43 +01:00
Matthias Radestock 8184d29d4a Merge pull request #1367 from weaveworks/1.0-python-call-changed
Fix docker api proxy smoke-tests after docker-py change
2015-08-28 20:22:22 +01:00
Matthias Radestock fef91b5339 comment 2015-08-28 18:09:23 +01:00
Matthias Radestock 24de98e188 refactor 2015-08-28 18:08:24 +01:00
Matthias Radestock 6291a89665 Merge pull request #1314 from weaveworks/1209-wait-iface-netlink
Wait for ethwe via netlink, not via polling or signals

Fixes #1209. Fixes #1258. Fixes #1300.
2015-08-28 17:44:55 +01:00
Bryan Boreham a45e5ac152 Test calling 'start' with a HOST_CONFIG not supplied to 'create' 2015-08-28 16:20:31 +01:00
Paul Bellamy 6b5f35e574 [proxy] Only replace the request body if we've modified it.
This is a temporary fix for a failure in docker-py tests:
"test_valid_no_config_specified".
2015-08-28 15:09:33 +01:00
Matthias Radestock 244a679f90 Merge pull request #1349 from weaveworks/issues/1266-detect-adverse-firewall-config
Warn on detection of REJECT rule in FORWARD chain

Fixes #1266.
2015-08-28 12:22:23 +01:00
Matthias Radestock 1a9909e3e2 also warn on weave reset when env's DOCKER_HOST points to proxy
This was missed in #1327.
2015-08-28 09:34:18 +01:00
Matthias Radestock 6655974ef3 cosmetic: tidy up log messages 2015-08-28 09:12:42 +01:00
Matthias Radestock cdf4fb605a Merge branch '1.0' 2015-08-27 20:04:04 +01:00
Michael Bridgen bca31c7335 Merge pull request #1365 from weaveworks/1356_ignore_interface_deletion_errors_in_weave_reset
ignore interface deletion errors during `weave reset`
2015-08-27 15:38:16 +01:00
Bryan Boreham 7652b44c21 Ensure we have the busybox image 2015-08-27 15:14:06 +01:00
Bryan Boreham 6fce798164 Factor out deserializeRoute() func 2015-08-27 15:10:31 +01:00
Bryan Boreham 7801dcd849 Factor out common loop receiving from netlink 2015-08-27 15:10:31 +01:00
Bryan Boreham 2eed101a93 In weavewait, wait for the multicast route to be added before calling container entrypoint.
We have moved the 'route add' command to the end of 'attach', so this
indicates that all network interface set-up is complete.
2015-08-27 15:10:31 +01:00
Bryan Boreham 3d25620d13 In proxy, rewrite /etc/hosts from 'weave attach', via new utility weavehosts 2015-08-27 15:03:41 +01:00
Bryan Boreham 6e89b036e2 Wait for ethwe via netlink, not via polling or signals
('docker kill' breaks restart policy)
2015-08-27 15:03:41 +01:00
Michael Bridgen 8b5992e43f Merge pull request #1290 from weaveworks/labels
Add role label to weave containers so scope can filter
2015-08-27 15:00:15 +01:00
Bryan Boreham 548b4aea40 Call to docker-py changed upstream 2015-08-27 14:54:20 +01:00
Tom Wilkie 42b64f826b Merge pull request #1358 from weaveworks/1357-dns-reverse-tombstone
Make reverse dns lookups honor tombstones.
2015-08-27 14:25:23 +01:00
Tom Wilkie 95379b0c5a Add works.weave.role=system label to weave containers. 2015-08-27 12:22:34 +00:00
Matthias Radestock f5918a3c14 remove dead code
since #1248
2015-08-27 08:42:48 +01:00
Matthias Radestock 1df752cd33 ignore interface deletion errors during weave reset
These can occur when a container dies during `weave reset`.

Fixes #1356.
2015-08-27 07:54:51 +01:00
Tom Wilkie a1f5d90323 Merge pull request #1363 from weaveworks/1362-fix-quoted-params
Fix test 295
2015-08-26 13:46:49 +01:00
Bryan Boreham 5289a380f2 Use $* instead of $@ to get the desired quoting 2015-08-26 12:31:38 +01:00
Tom Wilkie 914c88454d Review feedback 2015-08-25 16:53:21 +00:00
Tom Wilkie 46363f4129 Make reverse dns lookups honor tombstones. 2015-08-25 12:01:09 +00:00
Michael Bridgen bd19b86259 Merge pull request #1352 from weaveworks/doc-format
Small improvements in features doc
2015-08-25 12:56:56 +01:00
Tom Wilkie db2e7e4ba1 Merge pull request #1355 from weaveworks/1343-docker-bridge-in-resolv-conf
Pass docker bridge ip to dns server, and filter it out of upstream dns ips.
2015-08-25 09:48:06 +01:00
Bryan Boreham 760f419e8f Merge pull request #1354 from weaveworks/1347-dns-tombstone-comment
Lgtm.  Closes #1347
2015-08-24 17:05:54 +01:00
Tom Wilkie 58ae0cd113 Pass docker bridge ip to dns server, and filter it out of upstream dns ips. 2015-08-24 16:00:26 +00:00
Tom Wilkie 4809c113b3 Update comment. 2015-08-24 15:43:31 +00:00
Ilya Dmitrichenko 6d0db90f43 Revert "Merge pull request #1337 from weaveworks/1295-push-on-green"
This reverts commit 5054805ac6, reversing
changes made to 396df6bcda.
2015-08-24 14:22:36 +01:00
Ilya Dmitrichenko 75fb8f6ebf Revert "need to be in gopath to run make publish"
This reverts commit 130cf6eb2b.
2015-08-24 14:22:29 +01:00
Bryan Boreham 26739463a4 Escape a minus character to stop it being taken as a bullet, and clarify which example we are talking about 2015-08-24 14:19:14 +01:00
Paul Bellamy 130cf6eb2b need to be in gopath to run make publish 2015-08-24 11:10:59 +01:00
Paul Bellamy 5054805ac6 Merge pull request #1337 from weaveworks/1295-push-on-green
Publish latest images from CI on successful builds.
2015-08-24 10:58:14 +01:00
Adam Harrison ca94cfffe7 Detect REJECT rule in FORWARD chain 2015-08-21 16:25:49 +01:00
Matthias Radestock 396df6bcda Merge pull request #1348 from weaveworks/1345-1332-dns-response-size
Unify logic to tell if DNS response is too big.

Fixes #1345, fixes #1332
2015-08-21 14:03:00 +01:00
Matthias Radestock 58dafbfa9d Merge pull request #1344 from weaveworks/refactor-dns-handlers
Refactor dns handlers
2015-08-21 13:44:57 +01:00
Tom Wilkie 8516ab7c69 Unify logic to tell if DNS response is too big. 2015-08-21 12:39:12 +00:00
Matthias Radestock 2284bc2120 move error response construction to handler
and implement it in terms of existing functions, plus provide a
convenience method for sending the rather common NameError.
2015-08-21 12:09:22 +01:00
Matthias Radestock 31a490bfe6 ensure that make publish publishes up-to-date images
otherwise the git revision based image tag doesn't match what is in
the image.
2015-08-21 09:34:08 +01:00
Matthias Radestock 0b5f5fd74e extract response writing 2015-08-20 20:24:12 +01:00
Matthias Radestock 9a7abe68fd optimise response construction when no truncation is required
no need to do a binary search if all the answers fit
2015-08-20 20:18:56 +01:00
Matthias Radestock 2ea27ec08f simplify response truncation 2015-08-20 19:55:19 +01:00
Matthias Radestock 322441d5c8 inline truncateResponse 2015-08-20 19:55:19 +01:00
Matthias Radestock 7c7c302920 extract response construction 2015-08-20 19:17:03 +01:00
Matthias Radestock 69986e77dd cosmetic 2015-08-20 18:46:55 +01:00
Matthias Radestock 8182022d52 refactor dns handlers
introduce a handler struct, which

- enables lifting of all the handle* closures to methods
- eliminates the passing around of defaultMaxResponseSize
- reduces the number of free-floating functions
2015-08-20 18:27:58 +01:00
Matthias Radestock b075e93319 Merge pull request #1324 from weaveworks/1321_elminate_misleading_flag_parsing_error
eliminate misleading flag parsing error

Fixes #1321.
2015-08-20 17:34:32 +01:00
Matthias Radestock fd6d15e712 eliminate misleading flag parsing error
The standard mflags error reporting produces s.t. like
```
flag provided but not defined: -p
See '/home/weave/weaver --help'.
```
on stderr. This is misleading since the program name is container
local, so not something the user actually invoked.

Fixes #1321.
2015-08-20 17:19:48 +01:00
Matthias Radestock f399c3a888 Merge branch '1.0'
Conflicts:
	ipam/ring/ring_test.go
2015-08-20 16:32:09 +01:00
Matthias Radestock 79d1c404a7 Merge pull request #1342 from weaveworks/1336-fix-ring-fuzztest
Fix ring fuzz-test

Fixes #1336.
2015-08-20 16:25:50 +01:00
Adam Harrison 1e83e7a95c Merge branch '1.0' 2015-08-20 15:33:59 +01:00
Adam Harrison 086ab9d126 Correct MaxUDPPacketSize constant 2015-08-20 15:22:10 +01:00
Tom Wilkie 8a7821a15b Publish latest images from CI on successful builds. 2015-08-20 14:20:32 +00:00
Bryan Boreham 135c34a45a Don't remove peer from table until after we've copied its entries out 2015-08-20 13:11:51 +01:00
Bryan Boreham ca6e54890c Fix Transfer() when removing a peer, so we actually target the other peer. 2015-08-20 13:11:51 +01:00
Tom Wilkie dac219b64a Merge pull request #1339 from weaveworks/revert-1338-1336-check-len
Revert "In ipam ring unit test, check if we have any peers with entri…
2015-08-20 12:23:06 +01:00
Tom Wilkie 790a1757e0 Revert "In ipam ring unit test, check if we have any peers with entries to avoid crash " 2015-08-20 12:18:34 +01:00
Tom Wilkie aafa0f6783 Merge pull request #1338 from weaveworks/1336-check-len
In ipam ring unit test, check if we have any peers with entries to avoid crash
2015-08-20 12:15:27 +01:00
Bryan Boreham df5426dd8b Check if we have any peers with entries, to avoid crash 2015-08-20 11:57:56 +01:00
Adam Harrison 3803d79835 Merge pull request #1335 from weaveworks/1328_handle_invalid_status_sub_command
better error handling for invalid `weave status` sub-command
2015-08-20 11:48:01 +01:00
Matthias Radestock ceff3bed28 better error handling for invalid weave status sub-command
instead of showing '404 page not found'

Fixes #1328.
2015-08-20 11:14:59 +01:00
Matthias Radestock eebdbe226b Merge pull request #1325 from weaveworks/1322_handle_usage_locally
deal with `weave --help|help` locally

Fixes #1322.
2015-08-20 10:13:45 +01:00
Matthias Radestock 8ab639ddaa deal with weave --help|help locally
thus making them unaffected by docker breakages

Fixes #1322.
2015-08-20 10:03:21 +01:00
Matthias Radestock 812579a139 Merge pull request #1326 from weaveworks/576_better_version_check
better docker version check

Fixes #576.
2015-08-19 21:31:43 +01:00
Matthias Radestock 08f31886d6 better docker version check
Fixes #567. Supersedes #1298.
2015-08-19 21:20:14 +01:00
Matthias Radestock f080e5aa5c Merge pull request #1331 from weaveworks/clarify-test-160
clarify test 160
2015-08-19 20:41:59 +01:00
Matthias Radestock d61c719e45 cosmetic 2015-08-19 20:39:16 +01:00
Bryan Boreham 4d49bf3ab9 Add comment to clarify the reason why there is no explicit 'assert' test at the end. 2015-08-19 16:54:24 +01:00
Matthias Radestock f34f837106 Merge branch '1.0'
Conflicts:
	weave
2015-08-19 16:38:55 +01:00
Matthias Radestock 1326994a6d Merge pull request #1319 from weaveworks/dns-tcp
Expose dns server on tcp as well as udp

Fixes #1333.
2015-08-19 16:34:09 +01:00
Matthias Radestock 99ab0d3abc Merge branch '1.0' 2015-08-19 11:29:23 +01:00
Matthias Radestock e61f9afb72 Merge pull request #1330 from weaveworks/1329_plug_gossip_sender_leak
plug GossipSender leak

Fixes #1329.
2015-08-19 11:26:25 +01:00
Matthias Radestock 29c64b1f40 document weave env --restore
leftover from #1327
2015-08-19 11:20:10 +01:00
Bryan Boreham db51b772b4 Merge pull request #1327 from weaveworks/1288_restore_env
Closes #1288
2015-08-19 10:54:43 +01:00
Matthias Radestock 0b764a2a27 plug GossipSender leak
GossipChannel.sendDown is the (only) place which creates
GossipSenders. It has two call sites, Send and SendDown, only the
former of which performed a gc of existing senders. Therefore, in
configurations with

- a lot of connection churn, and
- SendDown invocations for these connections, and
- few or no Send invocations

we can leak GossipSenders.

The most likely scenario for this to occur is a peer that, while
having no other connections, is making/accepting connections which get
torn down post handshake, e.g. because IPAM detected a clash.

Fixes #1329.
2015-08-19 10:22:11 +01:00
Matthias Radestock a93a3e98b9 warn when stopping proxy when the env is using it 2015-08-18 18:08:02 +01:00
Matthias Radestock be94dcc8a3 introduce weave env --restore
`weave env` stashes the existing DOCKER_HOST in ORIG_DOCKER_HOST, and
`weave env --restore` sets DOCKER_HOST to $ORIG_DOCKER_HOST.

Closes #1288.
2015-08-18 17:20:21 +01:00
Matthias Radestock a34b31ad12 refactor: make proxy_addr more single purpose 2015-08-18 15:40:58 +01:00
Matthias Radestock e1d07aa264 cosmetic 2015-08-18 15:34:43 +01:00
Matthias Radestock 0d79a8fc43 cosmetic 2015-08-18 15:08:59 +01:00
Matthias Radestock 5d1411830b Merge branch '1.0' 2015-08-18 13:33:36 +01:00
Matthias Radestock dc91ce9ec6 Merge branch 'latest_release_doc_updates' into 1.0 2015-08-18 13:33:15 +01:00
Matthias Radestock 36a857f343 remove "Contact information" link on docs home page
It's broken, and we have a link in the footer anyway.

Fixes #1260.
2015-08-18 13:31:28 +01:00
Matthias Radestock c1c5450caf more idiomatic logger initialisation, with standard default log level 2015-08-18 12:04:48 +01:00
Bryan Boreham 0b4e8b8754 Merge pull request #1318 from weaveworks/1305_suppress_self_reconnection
lgtm. Fixes #1305.
2015-08-18 11:14:15 +01:00
Matthias Radestock f73dcba306 eliminate spurious warning when --ipalloc-range=""
Fixes #1320.
2015-08-18 09:50:42 +01:00
Ilya Dmitrichenko 147a537b82 We are not using gcloud, but it cause a conflict
https://s3.amazonaws.com/archive.travis-ci.org/jobs/76062893/log.txt
2015-08-18 09:47:37 +01:00
Ilya Dmitrichenko a4844b8ee7 We are not using gcloud, but it cause a conflict
https://s3.amazonaws.com/archive.travis-ci.org/jobs/76062893/log.txt
2015-08-18 09:46:09 +01:00
Ilya Dmitrichenko acde6cb0ee We are not using gcloud, but it cause a conflict
https://s3.amazonaws.com/archive.travis-ci.org/jobs/76062893/log.txt
2015-08-18 09:40:03 +01:00
Paul Bellamy b4e13215c7 expose dns server on tcp as well as udp
issue was discovered when testing scope with master vs 1.0 branches.
2015-08-18 09:35:53 +01:00
Matthias Radestock 4ecaa0f508 don't put blank lines around sub-status
not what I had in mind, and it breaks the tests
2015-08-18 09:00:01 +01:00
Matthias Radestock 86b7e4a6db Merge branch '1.0' 2015-08-18 08:50:41 +01:00
Matthias Radestock aa19028583 Merge branch 'latest_release_doc_updates' into 1.0 2015-08-18 08:50:19 +01:00
Matthias Radestock a6e2296e0a Merge branch '1.0' into latest_release_doc_updates 2015-08-18 08:49:31 +01:00
Matthias Radestock 8c5248da43 Add release 1.0.2 2015-08-18 08:27:26 +01:00
Matthias Radestock 59859c4aed clarify allocation subnet overlap failure
Closes #1317.
2015-08-18 01:50:30 +01:00
Matthias Radestock 0e85783257 cosmetic: add blank lines around weave status output
Closes #1315.
2015-08-18 01:38:51 +01:00
Matthias Radestock d8e8cd6e51 don't reconnect to ourself
When we discover that a connection target address leads to ourself we
set the retry time to the zero time.Time value - the only special
time.Time value. We use that marker to a) suppress subsequent
connection attempts, and b) display the next retry as 'never' in
`weave status|report`.

Closes #1305.
2015-08-18 00:51:33 +01:00
Matthias Radestock bbc91c1273 Merge pull request #1307 from weaveworks/1306-dns-recurse-compress
Compress recursive answers if they are too big.

Fixes #1306.
2015-08-17 19:23:46 +01:00
Tom Wilkie 67f974923b Test for recursive DNS queries & compression.
Note this doesn't test the golang resolver, where the original error
was found, as I can't find a way to point the golang resolver at a
custom dns server during a test.  It does check that a compressed
response from an upstream server is correctly handled.
2015-08-17 18:25:36 +01:00
Tom Wilkie 4f00dab4c4 Compress recursive answers if they are too big. 2015-08-17 18:25:26 +01:00
Matthias Radestock 74e29c8990 Merge branch '1.0'
Conflicts:
	nameserver/server.go
	nameserver/server_test.go
2015-08-17 18:10:56 +01:00
Matthias Radestock 7fb597a8bf Merge pull request #1313 from weaveworks/issues/1306-dns-compress-1-0-2
Try to compress too long replies (when forwarding).

Fixes #1306.
2015-08-17 18:05:04 +01:00
Alvaro Saurin c50c90ea8d Try to compress too long replies (when forwarding). 2015-08-17 18:50:16 +02:00
Paul Bellamy 74883c5da8 Merge pull request #1310 from weaveworks/improve_proxy_ignore_messages
Improve log messages for when the proxy ignores a container
2015-08-17 16:27:45 +01:00
Matthias Radestock 375af0a7b9 Improve log messages for when the proxy ignores a container
- 'ignoring' could be misread as 'dropped request', so change that to
  'leaving container alone'
- more elaborate reasons
2015-08-17 16:15:45 +01:00
Matthias Radestock 8c9679b19c Merge branch '1.0'
Conflicts:
	circle.yml
2015-08-17 15:01:44 +01:00
Tom Wilkie d220542491 Add sudo apt-get update to circle.yml 2015-08-17 13:37:41 +00:00
Alvaro 0a80fdcc1d Merge pull request #1312 from weaveworks/circle
Add sudo apt-get update to circle.yml
2015-08-17 13:52:00 +02:00
Tom Wilkie 5fbcb3f905 Add sudo apt-get update to circle.yml 2015-08-17 08:49:40 +00:00
Matthias Radestock 067f96023a correct option name in log message 2015-08-17 09:37:46 +01:00
Matthias Radestock 9de7c9ed0d Merge pull request #1303 from weaveworks/1302-proxy-net-none
[proxy] attach containers with --net=none to weave

Fixes #1302.
2015-08-14 14:03:36 +01:00
Paul Bellamy 0de292c0d4 [proxy] attach containers with --net=none to weave 2015-08-14 13:48:32 +01:00
Matthias Radestock eaa5385069 refactor: use new proxy_start_container function 2015-08-13 19:54:00 +01:00
Matthias Radestock fa2aa1f791 Merge branch '1.0'
Conflicts:
	test/640_proxy_restart_reattaches_test.sh
2015-08-13 19:46:23 +01:00
Matthias Radestock 181ce4cc60 Merge pull request #1296 from weaveworks/1294_extract_proxy_container_start
extract test functions for "start container via proxy"

Fixes #1294.
2015-08-13 19:40:53 +01:00
Matthias Radestock 65578211ef extract test functions for "start container via proxy"
This also eliminates the redefinition of 'start_container' by some of
the tests, which causes failures when multiple tests are run with
`run_all.sh`.

Fixes #1294.
2015-08-13 15:01:15 +01:00
Matthias Radestock 78df99e7e7 Merge branch '1.0' 2015-08-13 14:37:31 +01:00
Matthias Radestock 90ed76853c fix test breakage
on master, and only when running via `run_all.sh`. Weird.
2015-08-13 14:36:30 +01:00
Matthias Radestock 26f4a41900 Merge branch '1.0' 2015-08-13 14:07:22 +01:00
Matthias Radestock a3b4d6cdbf consistency: use $SMALL_IMAGE instead of $BASE_IMAGE
whenever possible
2015-08-13 14:06:43 +01:00
Matthias Radestock 686500b181 Merge branch '1.0' 2015-08-13 14:05:35 +01:00
Matthias Radestock 84c5c52090 fix broken test silliness 2015-08-13 14:05:17 +01:00
Matthias Radestock d5de0b41f9 Merge branch '1.0'
Conflicts:
	proxy/proxy.go
2015-08-13 13:57:02 +01:00
Matthias Radestock bd41d114b5 Merge pull request #1291 from weaveworks/docker-1.8
Docker 1.8 compatibility.

Fixes #1287.
2015-08-13 13:53:03 +01:00
Matthias Radestock 94d2f84364 refactor: use proxy abstraction 2015-08-13 13:50:29 +01:00
Paul Bellamy bf2b456af1 Don't use .Volumes in tests, as it's gone in docker 1.8 2015-08-13 13:36:47 +01:00
Paul Bellamy 4961e83686 [proxy] support new docker 1.8 --net="default" 2015-08-13 13:36:47 +01:00
Paul Bellamy f70dc6660a [proxy] lock docker api usage to 1.15 2015-08-13 13:36:47 +01:00
Matthias Radestock 2bb84ae28b Merge branch '1.0'
Conflicts:
	weave
2015-08-13 13:18:55 +01:00
Paul Bellamy 360c0cb642 Merge commit '74eef4dedae617' into 1.0 2015-08-13 12:38:51 +01:00
Matthias Radestock e319f54d3f Merge branch '1.0'
Conflicts:
	site/proxy.md
2015-08-13 11:36:01 +01:00
Matthias Radestock 1ce9551ba6 Merge branch 'latest_release_doc_updates' into 1.0 2015-08-13 11:34:56 +01:00
Matthias Radestock 4c566c8587 correct typo 2015-08-13 11:34:16 +01:00
Paul Bellamy 74eef4deda [proxy] mount all tls volumes as readonly 2015-08-13 11:13:22 +01:00
Paul Bellamy 2576e819a7 Revert "fix intermittent test failure"
This reverts commit e2fec3d007.

Was causing a failure on circleci
2015-08-13 11:12:50 +01:00
Tom Wilkie cdab6a71b7 Merge pull request #1289 from weaveworks/move-listvar
Move ListVar into common, so it can be reused elsewhere.
2015-08-12 12:56:25 +01:00
Tom Wilkie 45293fdcac Move ListVar into common, so it can be reused elsewhere. 2015-08-12 11:35:57 +00:00
Paul Bellamy e2fec3d007 fix intermittent test failure
Tests were running in the same shell, so function definitions were
leaking between tests.
2015-08-12 11:14:51 +01:00
Matthias Radestock 7656752d87 unbreak test
caused by faulty merge
2015-08-12 07:40:20 +01:00
Matthias Radestock d0ed225184 Merge branch '1.0'
Conflicts:
	weave
2015-08-11 15:01:54 +01:00
Paul Bellamy 12ed5cc9ee Merge pull request #1284 from weaveworks/1279_proxy_weave_run
prevent proxy from interferring with `weave run`
2015-08-11 14:54:27 +01:00
Matthias Radestock f8639a57e7 prevent proxy from interferring with weave run
...which could result in containers unexpectedly getting multiple IP
addresses.

Fixes #1279.
2015-08-11 14:02:23 +01:00
Adam Harrison c2147f80b7 Merge pull request #1283 from weaveworks/1185_report_pcap_stats
add pcap stats to `weave report`
2015-08-11 13:09:34 +01:00
Matthias Radestock f1b9cf4fc0 Merge pull request #1282 from weaveworks/1281-dns-remove-dead-quick
Remove DNS entries for dead containers when adding correctly.

Fixes #1281.
2015-08-11 13:08:46 +01:00
Matthias Radestock c1d799a06c add pcap stats to weave report
Closes #1185.
2015-08-11 12:57:52 +01:00
Tom Wilkie 6a5044fc70 Make nameserver logging more consistent. 2015-08-11 11:32:19 +00:00
Tom Wilkie 7fa568872c Ensure we delete the fqdn is the container fails to start. 2015-08-11 11:32:17 +00:00
Adam Harrison f3ad81d554 Merge pull request #1248 from weaveworks/issues/1025-enhanced-weave-status
Enhanced weave status
2015-08-11 12:29:29 +01:00
Adam Harrison fc9a1c8664 new weave status, weave status ..., and weave report 2015-08-11 12:15:13 +01:00
Adam Harrison 00ecff09f7 Move status HTTP handlers into prog/weaver/http.go 2015-08-11 12:01:27 +01:00
Adam Harrison 278807a199 Move router HTTP handlers into router/http.go 2015-08-11 12:01:27 +01:00
Matthias Radestock 8bce900170 Merge pull request #1247 from weaveworks/test_frag
add test for PMTU discovery and packet fragmentation
2015-08-10 22:02:07 +01:00
Matthias Radestock cf6d6d0cd3 add test for PMTU discovery and packet fragmentation 2015-08-10 17:50:21 +01:00
Matthias Radestock 94af02fbf4 Merge branch '1.0'
Conflicts:
	weave
2015-08-10 17:37:06 +01:00
Matthias Radestock 9e65cc20cb cosmetic 2015-08-10 17:33:45 +01:00
Tom Wilkie 25c4a2961e Merge pull request #1278 from weaveworks/1276_warn_on_iprange_overlap
warn on -iprange overlap
2015-08-10 17:27:47 +01:00
Matthias Radestock 755a949f6d Merge pull request #1277 from weaveworks/1275-negative-dns
Return NXDOMAIN when we don't have entries

and drop the non-authoritative special case for recursive dns.

Fixes #1275.
2015-08-10 17:19:44 +01:00
Tom Wilkie 42debde746 Merge pull request #1259 from weaveworks/1257-proxy-chunking
Proxy chunking should handle arbitrarily large chunks
2015-08-10 15:57:19 +01:00
Tom Wilkie c2d5b35cd6 Add test for negative DNS queries 2015-08-10 13:13:12 +00:00
Matthias Radestock f4e7c85219 warn on -iprange overlap
Closes #1276.
2015-08-10 13:27:06 +01:00
Tom Wilkie 073836905a Return NXDOMAIN when we don't have entries, and drop the non-authoritative special case for recursive dns. 2015-08-10 12:19:21 +00:00
Matthias Radestock fd2a59417d cosmetic 2015-08-10 12:39:00 +01:00
Paul Bellamy 6d7e784c25 review feedback 2015-08-10 11:02:51 +01:00
Matthias Radestock b2a24d1cbe Merge branch '1.0'
Conflicts:
	nameserver/zone_lookup.go
2015-08-10 10:27:10 +01:00
Tom Wilkie 0a2701bbbc Merge pull request #1268 from weaveworks/1243_fix_claim_test
add a sleep to improve test reliability
2015-08-10 10:23:48 +01:00
Alvaro 475daa82b3 Merge pull request #1273 from weaveworks/1270_mdns_result_race
ignore DuplicateError when inserting mDNS answer in zone
2015-08-10 11:15:47 +02:00
Matthias Radestock c3b653b1b9 ignore DuplicateError when inserting mDNS answer in zone
When we issue two concurrent mDNS queries for the same name, the
answer to the second one encounters a DuplicateError when inserting
into the zone. We would return that error, which causes the query
would fail. And we'd cache that fact. All bad.

So ignore DuplicateError.

Fixes #1270.
2015-08-08 22:01:45 +01:00
Matthias Radestock 1a11b989e7 simplify test 2015-08-07 14:40:34 +01:00
Matthias Radestock 010792f714 cosmetic: more sensible order of weave commands 2015-08-07 11:44:47 +01:00
Matthias Radestock 536369bc12 Merge pull request #1265 from weaveworks/1252-proxy-non-root-hosts
[proxy] rewrite /etc/hosts from outside the container

Fixes #1252.
2015-08-06 13:30:02 +01:00
Paul Bellamy f55655216f [proxy] rewrite /etc/hosts from outside the container
Avoids problems when container user is non-root. Added a test for this
and the --no-rewrite-hosts flag.
2015-08-06 13:14:18 +01:00
Matthias Radestock 43ef3b8515 give routers some time to establish connectivity 2015-08-06 11:54:58 +01:00
Paul Bellamy 3180c67151 -no-rewrite-hosts -> --no-rewrite-hosts 2015-08-06 11:01:35 +01:00
Adam Harrison 457e162d0c Merge branch '1.0'
Conflicts:
	site/ipam.md
2015-08-06 09:56:50 +01:00
Adam Harrison ed3207afe7 Merge remote-tracking branch 'upstream/latest_release_doc_updates' into 1.0 2015-08-06 09:51:31 +01:00
Adam Harrison 8e5b0ceceb Merge pull request #1262 from weaveworks/latest_release_doc_udpates
Fix IPAM doc bug
2015-08-06 09:48:14 +01:00
Tom Wilkie f9127e90db Merge pull request #1263 from weaveworks/integration-tests-no-save-file
don't save the python test file when running test/600
2015-08-05 18:50:22 +01:00
Paul Bellamy 94db0beb25 don't save the python test file when running test/600 2015-08-05 18:04:49 +01:00
Adam Harrison 4a1bb1a33a Fix IPAM doc bug 2015-08-05 17:53:24 +01:00
Adam Harrison 77996158ef Merge branch '1.0' 2015-08-05 14:42:09 +01:00
Adam Harrison 93984e1955 Merge pull request #1261 from weaveworks/arp_all_ips
send ARP update for *all* container IPs; not just the first one
2015-08-05 14:38:16 +01:00
Matthias Radestock 67aae051d6 send ARP update for *all* container IPs; not just the first one
This was missed in #498.
2015-08-05 13:35:35 +01:00
Matthias Radestock 6cc7749cf2 cosmetic: remove debug logging from test 2015-08-05 12:31:20 +01:00
Matthias Radestock c47e57cc29 cosmetic 2015-08-05 11:55:11 +01:00
Matthias Radestock de6f5a93e8 refactor: extract build flags 2015-08-05 11:11:57 +01:00
Paul Bellamy 157910ec9a adding integration test for proxy handling large http chunks 2015-08-05 10:52:06 +01:00
Paul Bellamy b85b90ef4e Proxy chunking should handle arbitrarily large chunks 2015-08-04 17:47:51 +01:00
Paul Bellamy 1d1ebc103d Merge remote-tracking branch 'upstream/1.0' 2015-08-04 14:29:12 +01:00
Paul Bellamy eee13e7dd9 Merge pull request #1253 from weaveworks/1241-no-command-specified
[proxy] Handle case where no command is specified.
2015-08-04 13:11:35 +01:00
Paul Bellamy 7cc53c4c68 [proxy] Handle case where no command is specified.
The docker daemon can't do this because we set /w/w.
2015-08-04 12:32:42 +01:00
Matthias Radestock 9c22066bdb Merge pull request #1256 from weaveworks/1245-dns-loadbalancing
Add note to explain DNS shuffle effects.

Fixes #1245.
2015-08-04 12:22:23 +01:00
Tom Wilkie f241e15ec1 Review feedback 2015-08-04 11:00:32 +00:00
Tom Wilkie 881ece3553 Add note to explain dns shuffle effects. 2015-08-04 10:47:36 +00:00
Michael Bridgen 281bf02871 Merge branch 'pr1254' 2015-08-03 15:26:08 +01:00
Michael Bridgen 778038fdd3 Merge branch 'fix-dotted-imports-for-log' of https://github.com/dpw/weave into pr1254 2015-08-03 14:29:53 +01:00
David Wragg e7ee72836d Log -> log 2015-08-03 14:13:24 +01:00
David Wragg 31c0506b85 Eliminate dotted imports of common for the sake of Log 2015-08-03 14:07:16 +01:00
David Wragg 7d6f85424c Move OnOff into router.go 2015-08-03 14:06:23 +01:00
Tom Wilkie ca89cfee3d Merge pull request #1250 from paulbellamy/testrunner-summarize-fails
Some usability tweaks to the test runner
2015-07-31 12:36:50 +01:00
Paul Bellamy 5a37f652c2 add -v to testrunner, and less output in happy path 2015-07-31 12:25:50 +01:00
Paul Bellamy cc7b781bb3 have testrunner summarize failed tests so they are easier to find 2015-07-31 12:12:33 +01:00
Paul Bellamy 55051f0194 Merge remote-tracking branch 'upstream/1.0' 2015-07-31 11:01:19 +01:00
Paul Bellamy 78e0e79b26 Merge pull request #1238 from weaveworks/1232-weavewait-image
[proxy] only mount /w into weavewaited containers
2015-07-31 10:57:26 +01:00
Paul Bellamy b7e8938ab4 scope the test runner call so scope works 2015-07-31 10:49:21 +01:00
Matthias Radestock f8327c3c6c expand test coverage to unicast relaying
by way of an ipam space request from host3 to host1, via host2.
2015-07-30 20:38:00 +01:00
Paul Bellamy e3851e6e55 Merge pull request #1246 from weaveworks/gce-parameterize
parameterize test/gce.sh so scope can override TEMPLATE_NAME and NUM_HOSTS
2015-07-30 16:18:59 +01:00
Matthias Radestock 6a136eebb2 make parallel test running work locally
@tomwilkie assures me this change won't break CircleCI. We shall see.

This now works for me:

make clean
make COVERAGE=true
COVERDIR=test/coverage SLOW=true ./test/units.sh
cd test
./setup.sh
COVERAGE=true PARALLEL=true ./run_all.sh
./gen_coverage_reports.sh
2015-07-30 15:57:49 +01:00
Paul Bellamy 9396c046cd parameterize test/gce.sh so scope can override TEMPLATE_NAME and NUM_HOSTS 2015-07-30 15:48:22 +01:00
Paul Bellamy 4958b8d37a [proxy] Mount /w from the host for weavewaited containers
We set it at container create. Had tried also setting it at start, but
it seems to cause weird behaviour, and there is no way to do it from the
CLI.
2015-07-30 15:11:03 +01:00
Paul Bellamy e2389a4061 [proxy] Don't try to attach if container failed to start 2015-07-30 13:31:47 +01:00
Paul Bellamy dccd199369 Merge pull request #1242 from weaveworks/1239-600-docker-py
Shard 600_proxy_docker_py into 4.
2015-07-30 13:14:54 +01:00
Tom Wilkie a87be89095 Typos. Honestly 2015-07-30 11:07:17 +00:00
Tom Wilkie 5c9a13d343 Shard 600_proxy_docker_py into 4. 2015-07-30 10:38:02 +00:00
Tom Wilkie bfe07fc71a Give each shard 5 VMs 2015-07-30 10:32:16 +00:00
Tom Wilkie fa95b67c92 Add summary to bottom of test runner output.
Also, don't sort tests by #hosts when run sequentially, and fix typo in run_all.sh.

Fixes #1240
2015-07-30 10:29:05 +00:00
Adam Harrison 728203912e .gitignore Vim recovery files 2015-07-30 10:53:59 +01:00
Paul Bellamy 27289e7b7b Merge pull request #1236 from weaveworks/1169-frugal-frugal-frugal
Run integration tests in parallel when possible.
2015-07-30 10:16:39 +01:00
Tom Wilkie 669a0db528 Run integration tests in parallel when possible. 2015-07-29 17:28:58 +00:00
Paul Bellamy 4da999ba86 Merge pull request #1235 from weaveworks/gce
Improve CI run time
2015-07-29 17:38:30 +01:00
Paul Bellamy 5e9bd5424a Removed all the imports from "code.google.com" 2015-07-29 15:47:02 +01:00
Tom Wilkie 3bffd871b8 Merge pull request #1230 from weaveworks/1154-proxy-coverage
Add coverage to proxy for integration tests.
2015-07-29 13:24:21 +01:00
Adam Harrison 2d450ac39d Merge pull request #1225 from weaveworks/21-dns-listen-addr
Allow users to set dns listen address
2015-07-29 13:18:10 +01:00
Tom Wilkie 6fe69590c4 Add coverage to proxy for integration tests. 2015-07-29 12:06:29 +00:00
Tom Wilkie 4df3d7f150 Reduce the amount of parallelism in the ipam tests 2015-07-29 10:13:02 +00:00
Tom Wilkie 51128a6705 Parallelise the copying of /etc/hosts. 2015-07-29 09:58:51 +00:00
Tom Wilkie e7676979ee Parallelise the copying of imaages etc 2015-07-29 09:58:05 +00:00
Tom Wilkie da36c78942 Reduce the number of times gcs.sh ssh's into the VMs 2015-07-29 09:41:57 +00:00
Matthias Radestock 77358140d9 Merge pull request #1219 from squaremo/driveby
Remove misleading comment and lowercase deniedBy
2015-07-28 18:09:01 +01:00
Adam Harrison 1701bc3e92 Merge branch '1.0'
Conflicts:
	weave
2015-07-28 18:06:44 +01:00
Matthias Radestock ba0ffd225e Merge pull request #1231 from weaveworks/issues/1229-explicitly-set-docker-bridge-mac
Enforce docker bridge address assignment type on launch

Fixes #1229.
2015-07-28 17:59:46 +01:00
Adam Harrison a14d4d9f8e Enforce docker bridge address assignment type on launch 2015-07-28 17:51:48 +01:00
Tom Wilkie db6ee9954c Allow users to set dns listen address so we can run the weave router in the host net namespace. 2015-07-28 14:49:09 +00:00
Paul Bellamy 792c4c8de4 Merge pull request #1227 from weaveworks/gce-gc
Extend scheduler to periodically garbage collect stranded VMs
2015-07-28 15:35:28 +01:00
Matthias Radestock 820307dd4e Merge pull request #1228 from weaveworks/1030-150-connect-forget
Don't init the ring if the address is not in range.

Fixes #1030.
2015-07-28 15:29:16 +01:00
Tom Wilkie 88144ac7f8 Review feedback 2015-07-28 14:16:38 +00:00
Tom Wilkie da7ac78694 Don't try and init the ring if the IP address is not in our range. 2015-07-28 14:12:42 +00:00
Tom Wilkie 10dbd9ad4c Merge pull request #1224 from weaveworks/1203_proper_mock_gossip
proper mock periodic gossip
2015-07-28 13:29:20 +01:00
Tom Wilkie 9779e06f66 Extend scheduler to periodically garbage collect stranded VMs 2015-07-28 13:19:38 +01:00
Matthias Radestock f861d22593 do proper mock periodic gossip
instead of abusing broadcast gossip

Fixes #1203.
2015-07-28 10:37:18 +01:00
Paul Bellamy e3f0565517 Merge pull request #1221 from weaveworks/1220_include_test_dir_coverage
include directory of test code in coverage
2015-07-28 10:05:16 +01:00
Matthias Radestock 9a91b3ce9c include directory of test code in coverage
Fixes #1220.
2015-07-27 17:08:31 +01:00
Michael Bridgen c88361473c Remove misleading comment and lowercase deniedBy
* the comment "don't make the caller wait" applies to both branches
   of that if clause, and having it on one branch implies the other
   does make the caller wait
 * there's no reason for DeniedBy to start with a capital: it's a
   method on a private struct and it's not part of an interface.
2015-07-27 16:22:32 +01:00
Michael Bridgen a2e1a2d375 Merge branch 'pr1200' 2015-07-27 15:46:29 +01:00
Matthias Radestock 536ce992fc re-enable test
our switch on CircleCI from Docker 1.7.0 to 1.7.1 should make this
pass now.
2015-07-27 14:47:55 +01:00
Matthias Radestock 8d57b85a3a Merge branch 'master' into weave_1.1_readme_updates
Conflicts:
	README.md
2015-07-27 14:33:05 +01:00
Matthias Radestock ce37b622eb Merge branch '1.0' 2015-07-27 14:25:06 +01:00
Matthias Radestock d5e510f778 Merge branch 'latest_release_doc_updates' into 1.0 2015-07-27 14:24:33 +01:00
Matthias Radestock be8e414d5a Merge pull request #1217 from weaveworks/1186_document_root_docker
document how to run weave when docker access is root-only

Closes #1186.
2015-07-27 14:23:50 +01:00
Matthias Radestock 65e94f624b document how to run weave when docker access is root-only
Closes #1186.
2015-07-27 14:13:50 +01:00
Paul Bellamy ed4fb14f52 Merge pull request #1214 from weaveworks/rebuild-gce-templates
[circle] use new gce templates for docker 1.7.1
2015-07-27 13:38:44 +01:00
Matthias Radestock 32cce0c636 Merge branch 'master' into weave_1.1_readme_updates 2015-07-27 13:31:48 +01:00
Paul Bellamy 34bb7ae84a [circle] use new gce templates for docker 1.7.1 2015-07-27 12:51:12 +01:00
Michael Bridgen 5389e31190 Merge branch '687-detect-ipam-clash' of github.com:weaveworks/weave into pr1200 2015-07-27 11:54:31 +01:00
Bryan Boreham f0915221d1 Rename 'reclaim' to 'noErrorUnknown' which describes the behaviour better 2015-07-23 20:16:30 +01:00
Matthias Radestock aa4efd63ef cosmetic: uncomment test the way ordained by emacs 2015-07-22 16:55:43 +01:00
Tom Wilkie a12d63bb12 Make ipam test less stressful, so it doesn't timeout. 2015-07-22 12:53:27 +00:00
Tom Wilkie 062a25a032 Make unit test timeout 8mins. 2015-07-22 12:19:12 +00:00
Tom Wilkie 9e6d08c606 Put 2m timeout on all unit tests. 2015-07-22 11:56:39 +00:00
Alvaro 976641c3b6 Reverted: Fixed typo 2015-07-22 12:17:29 +02:00
Alvaro 2f80f12001 Fixed typo 2015-07-22 12:14:38 +02:00
Bryan Boreham 647b755b64 Check manually-assigned addresses with IPAM to see if they clash 2015-07-21 17:02:52 +01:00
David Wragg 26dc83dbc2 Merge weaveworks/519_remove_outermost_gob (PR #1189) 2015-07-21 12:01:40 +01:00
Matthias Radestock 3c107d675a Merge branch '1.0' 2015-07-21 11:42:49 +01:00
Matthias Radestock a20ca49344 Merge pull request #1206 from weaveworks/1201-weavewait-ro
[proxy] Mount weavewait readonly so it can't be tampered with

Fixes #1201.
2015-07-21 11:41:48 +01:00
Paul Bellamy aa8466f688 [proxy] Mount weavewait readonly so it can't be tampered with 2015-07-21 11:30:56 +01:00
Tom Wilkie 2980586e76 Put timeout on nameserver test to try and expose #1203 2015-07-21 09:25:04 +00:00
Paul Bellamy dba116bae1 Merge remote-tracking branch 'upstream/1.0' 2015-07-21 10:13:24 +01:00
Matthias Radestock 0b7d462adf enforce a message size limit
so we do not allocate loads of memory when receiving (accidental or
deliberate) garbage
2015-07-20 23:51:04 +01:00
Matthias Radestock 6f8622a1a0 replace outermost framing on tcp connection
switch from gob to length-prefix encoding
2015-07-20 23:51:04 +01:00
Matthias Radestock af049efb40 refactor: s/SimpleTCP{Sender,Receiver}/GobTCP{Sender,Receiver} 2015-07-20 23:51:02 +01:00
Matthias Radestock ad4e4ce6b8 refactor: make 'inner' encoder/decoder explicit 2015-07-20 23:50:10 +01:00
Tom Wilkie 4c9b0be117 Merge pull request #1198 from weaveworks/1146-coverage
When running unit tests, output coverage for imported modules.
2015-07-20 20:34:12 +01:00
Tom Wilkie a9dae46110 When running unit tests, output coverage for imported modules. 2015-07-20 15:47:07 +00:00
Paul Bellamy 53628fdeff Merge pull request #1187 from weaveworks/1182-proxy-tls-close
[proxy] Fully close TLS sockets, as they have no CloseWrite
2015-07-20 16:46:21 +01:00
Bryan Boreham e34093c328 Merge pull request #1197 from weaveworks/1158-flaky
LGTM
2015-07-20 16:02:52 +01:00
Tom Wilkie 9131b46263 Flush gossip messages after each turn, to ensure there isn't a build up of pending messages. 2015-07-20 14:36:40 +00:00
Michael Bridgen 2a9c3fc64a Merge branch 'pr1112' 2015-07-20 15:31:42 +01:00
Michael Bridgen c44c61dc83 Merge branch '1103-proxy-chunking' of github.com:weaveworks/weave into pr1112 2015-07-20 15:09:50 +01:00
Paul Bellamy 11663dd29f [proxy] rewrote chunked response handler
1) We cannot send "Connection: close", because the fsouza docker client
   expects the tcp socket to stay open between requests.

2) Because we cannot force-close the connection, we can't hijack the
   connection (because go's net/http doesn't let use un-hijack it).

3) Because we need to maintain the individual chunking of messages (for
   docker-py), we can't just copy the response body, as Go will remove and
   re-add the chunking willy-nilly.

Therefore, we have to read each chunk one-by-one, and flush the
ResponseWriter after each one.
2015-07-20 14:43:16 +01:00
Bryan Boreham 90b7c3625d Merge pull request #1176 from weaveworks/1156-gossip-timestamp
LGTM
2015-07-20 14:28:01 +01:00
Tom Wilkie 4e29dba5cd Make Gossip DNS reject gossip messages if clockskew > tombstoneTimeout / 2 2015-07-20 13:12:07 +00:00
Matthias Radestock f1b5a00cb3 Merge pull request #1195 from weaveworks/1177-dns-disappear
Don't periodically delete all non-tombstone entries.

Fixes #1177.
2015-07-20 13:07:42 +01:00
Tom Wilkie 1a4b38a10c Don't delete all the non-tombstone entries every 10mins! 2015-07-20 12:03:46 +00:00
Tom Wilkie 9ab2da8deb Add GET /name handler to return all dns entries (including tombstones) for debugging.
Also, make Address marshall into json as an IP string, not a number.
2015-07-20 12:03:33 +00:00
Matthias Radestock 66473e6021 refactor: return slice instead of pointer to slice from tombstone() 2015-07-20 12:37:54 +01:00
Paul Bellamy a9229b9197 weave env should still work with '--log-level=debug' 2015-07-20 11:57:20 +01:00
Paul Bellamy 7b9e59833e test upload_executable helper should mkdir before uploading 2015-07-20 10:58:39 +01:00
Tom Wilkie 5baddb83cb Comment out failing test until we figure out what to do. 2015-07-20 09:45:02 +00:00
Tom Wilkie 4e56247ed9 Merge pull request #1193 from weaveworks/disable_dns_in_multiweave
Disable dns in multiweave
2015-07-20 10:41:56 +01:00
Matthias Radestock ae2348875b disable dns in multiweave
Thus unbreaking it post #1065.
2015-07-19 22:52:42 +01:00
Matthias Radestock fafaeef07d introduce --no-dns option
undocumented, since it is mainly meant for testing
2015-07-19 22:51:40 +01:00
Matthias Radestock 5b62b8c1e3 refactor: inline populate_dns
and ditch the wait_for_status, since we've only just called that
2015-07-19 22:32:33 +01:00
Matthias Radestock 7c17c8aec3 refactor: simplify router arg parsing
using the same "always shift one" approach as in proxy_args
2015-07-19 21:56:04 +01:00
Matthias Radestock 64408b126e weave launch-router wasn't returning the correct container id
since $CONTAINER gets stomped on by various functions, in particular
populate_dns.

Broken by #1065.
2015-07-19 21:50:45 +01:00
Bryan Boreham 2a1f090a88 Merge pull request #1183 from weaveworks/1181-data-race
Correctly stop the test gossip router to prevent complaints of data races in subsequent tests.
2015-07-17 17:14:46 +01:00
Matthias Radestock e2a2a8f18f Merge branch '1.0' 2015-07-17 15:07:45 +01:00
Matthias Radestock dafa30725e Merge branch 'latest_release_doc_updates' into 1.0 2015-07-17 15:07:19 +01:00
Matthias Radestock 24b9a56e0a Merge pull request #1172 from weaveworks/issues/1061-improve-blockquote
Use Boostrap styles for warnings in docs

Fixes #1061.
2015-07-17 15:06:40 +01:00
Tom Wilkie a3d32068d2 Correctly stop the test gossip router to prevent complaints of data races in subsequent tests. 2015-07-17 13:48:39 +00:00
David Kaltschmidt e7f6682593 Use bootstrap alert styles for blockquote 2015-07-17 15:14:29 +02:00
Paul Bellamy 3f4b385b0f [proxy] If we can't CloseWrite a connection (tls), just Close it so clients don't hang 2015-07-17 14:09:27 +01:00
Paul Bellamy 5fc16a8267 [proxy] use a waitgroup as it's more semantically accurate 2015-07-17 14:08:51 +01:00
Bryan Boreham 68afbe31b0 Shrink image file size 2015-07-17 14:07:23 +01:00
Tom Wilkie ba3451e9ca Merge pull request #1180 from weaveworks/1171-210-failures
Don't swallow errors in the docker client.
2015-07-17 13:38:38 +01:00
Tom Wilkie 25c9180fdf Don't swallow errors in the docker client. 2015-07-17 12:35:12 +00:00
Paul Bellamy e03a4bb176 updating boot2docker docs from 1.0 to master 2015-07-17 12:55:39 +01:00
Paul Bellamy 4d594458d2 Merge branch '1.0' 2015-07-17 12:47:23 +01:00
Paul Bellamy 38176dcbb6 Merge remote-tracking branch 'upstream/latest_release_doc_updates' into 1.0 2015-07-17 12:46:48 +01:00
Paul Bellamy 854d83f294 updating boot2docker docs for proxy 2015-07-17 12:41:04 +01:00
Matthias Radestock 358ae3a936 Merge pull request #1174 from weaveworks/1128-iprange-error
More helpful error message when --ip-alloc-range mismatched

Closes #1128.
2015-07-17 12:35:20 +01:00
Paul Bellamy f3131fab57 Merge pull request #1175 from paulbellamy/975-boot2docker-documentation-update
updating boot2docker docs for proxy
2015-07-17 12:32:54 +01:00
Paul Bellamy 397f1398ee updating boot2docker docs for proxy 2015-07-17 12:30:14 +01:00
Bryan Boreham 2dbf83889b Remove unused error messages 2015-07-17 12:19:54 +01:00
Bryan Boreham 9b2c5b8466 Improve description of merge error 2015-07-17 12:19:33 +01:00
Bryan Boreham 1f4b943454 More helpful error message when --ip-alloc-range mismatched 2015-07-17 11:42:30 +01:00
Matthias Radestock 2ca0fb93c9 Merge pull request #1173 from weaveworks/1168-checkandpanic
Make nameserver.checkAndPanic look for dups, call it more often.

Fixes #1168.
2015-07-17 11:37:34 +01:00
Matthias Radestock 47273d424f Merge pull request #1170 from weaveworks/exec-dockerfile
Re-arrange weaveexec Dockerfile to improve download times
2015-07-17 11:36:15 +01:00
Bryan Boreham f4d6e028d4 Simplify 2015-07-17 11:33:29 +01:00
Tom Wilkie f10e0e4add Merge pull request #1159 from weaveworks/1150-claim-from-peer
Implement claiming addresses from other peers
2015-07-17 11:21:48 +01:00
Tom Wilkie 66e58937ca Make nameserver.checkAndPanic look for dups, call it more often. 2015-07-17 10:19:25 +00:00
Matthias Radestock 62c1cad676 Merge remote-tracking branch 'upstream/master' 2015-07-17 10:58:34 +01:00
Matthias Radestock ca523c716c cosmetic 2015-07-17 10:58:01 +01:00
Tom Wilkie e2deff09a6 Nameserver: log when peer goes away. 2015-07-17 09:36:44 +00:00
Ilya Dmitrichenko 19ddb65036 New screencast URL and image 2015-07-16 18:13:34 +01:00
Bryan Boreham 61c7c034e7 Re-arrange weaveexec Dockerfile to improve download times 2015-07-16 17:02:00 +01:00
Bryan Boreham 4c5920a50a Add fuzz-test for Claim 2015-07-16 16:12:23 +01:00
Bryan Boreham c3c40b15f1 Avoid hanging the caller when we try to claim from a dead peer 2015-07-16 16:12:23 +01:00
Bryan Boreham 2fa65f0a22 Smoke-test for claim operation 2015-07-16 16:12:23 +01:00
Bryan Boreham af7ca99dc4 Extend unit-tests for claim operation 2015-07-16 16:12:23 +01:00
Bryan Boreham 2d224d423d Add SpaceRequestDenied message 2015-07-16 16:12:23 +01:00
Bryan Boreham e7088e5daf Extract decodeRange function 2015-07-16 16:12:23 +01:00
Bryan Boreham d65409e97e Improve comment & log for deferred claim 2015-07-16 16:12:23 +01:00
Matthias Radestock 70110e483c Merge pull request #1166 from weaveworks/dns-tests
Make 210_dns_cross_hosts_test.sh excercise gossip dns tombstones.
2015-07-16 16:11:58 +01:00
Paul Bellamy a0876becbc Merge remote-tracking branch 'upstream/1.0'
Conflicts:
	proxy/create_container_interceptor.go
	proxy/create_exec_interceptor.go
	proxy/proxy.go
	proxy/start_container_interceptor.go
2015-07-16 15:29:29 +01:00
Paul Bellamy 1afdac4d2c Merge pull request #1167 from weaveworks/1157-proxy-net-host
[proxy] do not weave attach containers in '--net=host|container:<name|id>'
2015-07-16 15:24:37 +01:00
Paul Bellamy c6fa9cb17e refactor weaveCIDRsFromConfig to return an error message 2015-07-16 15:15:32 +01:00
Matthias Radestock aa3f35e38c remove stale reference to weave launch-dns 2015-07-16 14:41:57 +01:00
Tom Wilkie db77570779 Test is too long 2015-07-16 13:40:39 +00:00
Paul Bellamy 9619a27f8c [proxy] adding log message for ignored containers 2015-07-16 14:01:22 +01:00
Paul Bellamy c13154eea9 updating comment as per golint 2015-07-16 13:58:30 +01:00
Paul Bellamy 1132d26d47 [proxy] do not weave attach containers in '--net=host|container:<name|id>' 2015-07-16 13:58:27 +01:00
Tom Wilkie 6e5995964d Make 210_dns_cross_hosts_test.sh excercise gossip dns tombstones.
Also, make gce.sh less verbose, and do the env dance correctly.
2015-07-16 12:48:00 +00:00
Adam Harrison da13238368 Merge branch '1.0' 2015-07-16 13:45:36 +01:00
Paul Bellamy 0865f4c207 Merge pull request #1099 from weaveworks/1055-proxy-autoconfig
Proxy autoconfig
2015-07-16 13:33:21 +01:00
Adam Harrison 77bc56751b Merge pull request #1164 from weaveworks/1162_detect_host_netns_in_container
fix detection of host netns when weave script runs inside container
2015-07-16 13:33:19 +01:00
Matthias Radestock b32c017f33 fix detection of host netns when weave script runs inside container
$$ refers to the pid in the container's process namespace, so using it
as a reference into /hostproc is entirely wrong.

Fixes #1162.
2015-07-16 13:19:56 +01:00
Paul Bellamy a4a31e632b [proxy] Choose where to listen based on how launching client connected 2015-07-16 12:44:39 +01:00
Matthias Radestock 23ad547db4 Merge pull request #1155 from weaveworks/dns-gossip
Make DNS gossip converge correctly

Fixes #1151
2015-07-15 20:37:43 +01:00
Matthias Radestock 4d887c8a55 Merge branch '1.0'
Conflicts:
	proxy/create_exec_interceptor.go
	test/610_proxy_wait_for_weave_test.sh
2015-07-15 18:07:11 +01:00
Matthias Radestock a982146cbe Merge pull request #1153 from weaveworks/1143-non-weave-exec
[proxy] should not attempt weavewait in on weave container's execs

Fixes #1143.
2015-07-15 17:40:13 +01:00
Tom Wilkie 73210b6a70 Make sure DNS Gossip converges correctly.
- Abide by gossip interface (return nil, not empty gossip) so gossip converges!
- Make sure we don't say an old entry version is new to us; add some more tests.
- Add dns test for 3 (or N) hosts
- Extend mock gossip router to ensure reconsumption off diffs yield nil
- Don't increment version when re-adding existing entry.
2015-07-15 15:54:52 +00:00
Paul Bellamy e4422205e8 [proxy] should not attempt weavewait in on weave container's execs 2015-07-15 16:14:14 +01:00
Tom Wilkie 9df7358ded Add pprof handler to weaver. 2015-07-15 15:10:22 +00:00
Matthias Radestock 9507ea93dd rename test since we have two 250_* 2015-07-15 14:46:51 +01:00
Matthias Radestock 52d3bcfe8b Merge pull request #1149 from weaveworks/1118-respond-late-paxos
Respond to paxos messages when we already have a ring

Fixes #1118.
2015-07-15 13:24:40 +01:00
Bryan Boreham b3979c9505 Respond to paxos message if we already have a ring 2015-07-15 13:05:25 +01:00
Bryan Boreham 273a0fba47 Add sender to mock broadcast messages 2015-07-15 13:05:25 +01:00
Alvaro 7e34ec61f6 Merge pull request #1142 from weaveworks/1113-recursive-dns
Closes #1113
2015-07-15 13:42:59 +02:00
Bryan Boreham acf13dc157 Add sender to OnGossipBroadcast() 2015-07-15 12:07:56 +01:00
Tom Wilkie 91395d6728 Use Msg.Copy! 2015-07-15 11:05:36 +00:00
Paul Bellamy ef4a227ae9 Merge pull request #1144 from weaveworks/restart-docker-after-vagrant-tweaks
Restart docker after vagrant tweaking and refactor tweak-code
2015-07-15 11:34:59 +01:00
Alfonso Acosta 25589562cd Restart docker after vagrant tweaking and refactor tweak-code
Docker needs to be restarted and not started, since it already starts running after installation
2015-07-15 09:31:47 +00:00
Matthias Radestock 7063ca51aa remove dead code 2015-07-15 05:35:05 +01:00
Matthias Radestock 97498d26f7 remove dead code 2015-07-15 05:32:35 +01:00
Matthias Radestock 36292216fd rename tests so they actually run
These tests were introduced in #1065 and #1126.
2015-07-14 19:47:15 +01:00
Matthias Radestock 86448a1025 fix breakage
This wasn't spotted because #1065, which removed launch-dns, got
merged just before #1126, which introduced this test.
2015-07-14 19:42:59 +01:00
Ilya Dmitrichenko b4f53edd91 Merge pull request #1138 from weaveworks/vagrantfile-update
update default vagrant box to ubuntu 15
2015-07-14 18:21:02 +01:00
Matthias Radestock 3a22d2e6a1 Merge pull request #1135 from weaveworks/issues/1028-weave-dns-lookup
Implement `weave dns-lookup`

Closes #1028.
2015-07-14 18:19:26 +01:00
Matthias Radestock 9a4bbb55da Merge branch '1.0'
Conflicts:
	proxy/common.go
	weave
2015-07-14 18:09:25 +01:00
Matthias Radestock b476bd2afe Merge pull request #1137 from weaveworks/1109-docker-bridge-ip
[proxy] respect custom DOCKER_BRIDGE

Fixes #1109.
2015-07-14 17:52:08 +01:00
Tom Wilkie fa73240812 Copy dns requests and give them a fresh id when doing recursive queries. 2015-07-14 16:44:21 +00:00
Adam Harrison a3dc2d0482 Smoketest dns-lookup 2015-07-14 17:36:30 +01:00
Adam Harrison 41834478d7 Rework documentation to use weave dns-lookup 2015-07-14 17:36:30 +01:00
Adam Harrison 6832ae20b0 Implement weave dns-lookup 2015-07-14 17:36:30 +01:00
Matthias Radestock 7ae06ef00d Merge pull request #1126 from weaveworks/issues/1018-more-flexible-hostname-derivation
[proxy] More flexible container name -> hostname derivation

Closes #1018.
2015-07-14 17:31:53 +01:00
Alvaro a1ac0313e5 Merge pull request #1065 from weaveworks/gossipdns
Gossip-based DNS embedded in the router.
2015-07-14 18:30:16 +02:00
Paul Bellamy d9c5d2f940 update default vagrant box to ubuntu 15 2015-07-14 17:08:26 +01:00
Tom Wilkie 361ed1b4a6 Resolve unqualified domains in the weave.local domain. 2015-07-14 16:05:41 +00:00
Alfonso Acosta dfcc9678f3 Make weave usage fit into 80 columns 2015-07-14 16:02:19 +00:00
Tom Wilkie 8b061fd555 Gossip-based DNS embedded in the router.
- Nameserver holds a sorted list of (hostname, peer, container id, ip) tuples.
- Entries are gossiped using topology gossip / broadcast - only new/updated entries
  are propogated as expected.
- Entries for a given peer are removed when the connection to the peer is lost.
- Entries are tombstoned when containers die.  Tombstones are kept for 10mins,
  as they do not need to survive partition, only gossip propagation delay.
2015-07-14 16:00:38 +00:00
Tom Wilkie 01f7201a9e Remove legacy WeaveDNS code. 2015-07-14 16:00:28 +00:00
Paul Bellamy acd8bdec21 [proxy] respect custom DOCKER_BRIDGE 2015-07-14 16:33:35 +01:00
Alfonso Acosta ded3d6b40b Address PR comments 2015-07-14 15:31:44 +00:00
Tom Wilkie f7739ef04c Don't fail if docker rmi fails 2015-07-14 14:50:39 +00:00
Alfonso Acosta 4f82dc97fd [proxy] More flexible container name -> hostname derivation
Provides to new two new flags: `--hostname-match <regexp>`
and `--hostname-replacement <replacement>`. When launching a container, its name
matched against regular expression `<regexp>`. Then, based on that match,
`<replacement>` will be used to generate a hostname, which will ultimately be
handed over to weaveDNS for registration.

The substitution is implemented with
https://golang.org/pkg/regexp/#Regexp.ReplaceAllString . Thus, references to
regexp substitution groups should be prepended with a dollar sign.

For instance, if we provide `--hostname-match '^aws-[0-9]+-(.*)$'` and
`--hostname-replacement 'my-app-$1'`, running a container named
`aws-12798186823-foo` through WeaveProxy will lead to WeaveDNS registering
`my-app-foo`.

Closes #1018
2015-07-14 14:41:44 +00:00
Matthias Radestock 16471d0a28 Merge branch '1.0'
Conflicts:
	site/features.md
	test/110_encryption_test.sh
2015-07-14 15:41:10 +01:00
Matthias Radestock 6386c1bd18 Merge branch 'latest_release_doc_updates' into 1.0 2015-07-14 15:36:55 +01:00
Matthias Radestock 2e93a4c18e Merge pull request #1132 from weaveworks/issues/945-secure-router-config
Improvements to password handling recommendations

Closes #945.
2015-07-14 15:31:20 +01:00
Matthias Radestock 0abd134a0d Merge pull request #1133 from weaveworks/revert-1050-issues/987
Revert "Resolve unqualified names in the local WeaveDNS domain"
2015-07-14 15:06:19 +01:00
Matthias Radestock 18032015b9 Revert "Resolve unqualified names in the local WeaveDNS domain" 2015-07-14 16:06:05 +01:00
Adam Harrison e26e7e29f2 Improvements to password handling recommendations 2015-07-14 14:48:58 +01:00
Matthias Radestock 39a1f2163f Merge pull request #1050 from weaveworks/issues/987
Resolve unqualified names in the local WeaveDNS domain

Closes #987.
2015-07-14 14:43:29 +01:00
Alvaro Saurin 4c519fc761 Smoke tests 2015-07-14 15:12:40 +02:00
Matthias Radestock bbacaa42b8 cosmetic 2015-07-14 13:23:28 +01:00
Matthias Radestock 2f35ad1d42 Merge pull request #1098 from dpw/encrypt-features
New connection protocol version with encrypted features map

Closes #1029. Closes #912.
2015-07-14 13:20:18 +01:00
Bryan Boreham 6d17fe5df5 Need to restart docker after systemd reconfigure 2015-07-14 12:12:35 +01:00
Bryan Boreham 907de67a40 Merge pull request #1125 from weaveworks/smoke-overlay
Upgrade smoke-tests to use 'overlay' storage and Ubuntu 15.04
2015-07-14 11:51:53 +01:00
Tom Wilkie 499d65e715 Build cover tool by default, and do go get -t for tests, irregardless of $SLOW 2015-07-14 10:50:34 +00:00
Alvaro Saurin efb0ead9ad Resolve unqualified names in the local WeaveDNS domain
Unit tests
2015-07-14 10:46:41 +02:00
Matthias Radestock 85b4a05c9a Merge branch '1.0' 2015-07-13 19:07:47 +01:00
Matthias Radestock 1f3d054b09 Merge pull request #1124 from weaveworks/issues/1037-mitigate-entropy-depletion
Mitigate entropy depletion

Closes #1037.
2015-07-13 19:06:54 +01:00
Bryan Boreham 4853a1da05 Tell Docker to use 'overlay' storage driver 2015-07-13 18:28:29 +01:00
Bryan Boreham db8016112e Upgrade to Ubuntu 15.04 2015-07-13 18:28:11 +01:00
Tom Wilkie 4eb6d147b5 Merge pull request #1121 from weaveworks/test_large_packets
test that large packets can be transmitted over weave
2015-07-13 17:06:28 +01:00
Adam Harrison e2f8b6f9fd Document /dev/random blocking issue 2015-07-13 15:02:31 +01:00
Adam Harrison f3c4a9fc6d Limit router accept rate with token bucket 2015-07-13 15:02:31 +01:00
Adam Harrison 9634526a67 Add token bucket implementation 2015-07-13 15:01:04 +01:00
Adam Harrison b67c102fe5 Remove simple sleep from router accept 2015-07-13 15:01:04 +01:00
Tom Wilkie 1e39b2bdcf Merge pull request #1123 from weaveworks/remove-danglies
Remove Docker dangling images in smoke-tests
2015-07-13 14:38:04 +01:00
Adam Harrison d6443809bd Merge pull request #1120 from weaveworks/998-race
Various improvements to unit test running
2015-07-13 14:09:45 +01:00
Bryan Boreham 80e56d67db Remove Docker dangling images after copying new ones 2015-07-13 14:03:47 +01:00
Tom Wilkie 24a4d6501e Merge pull request #1117 from weaveworks/multi_hop_tests
Multi hop tests
2015-07-13 11:39:06 +01:00
Bryan Boreham 5e43f0fd4c Merge pull request #1079 from weaveworks/68-rewrite-etc-hosts
LGTM.  Closes #68, with remainder to #1122
2015-07-13 11:03:20 +01:00
Tom Wilkie 13f1239a3e Various improvements to unit test running
- units.sh should exit on error.
- Add race detector to unit test runner.
- Use local testing/cover/cover tool in gen_coverage_report.sh and units.sh
- Use the scheduler to parallelise the unit tests.
- Make test run id a string and differentiate between unit runs and integration runs in the scheduler.
- Pipe circle env vars through to build container so we can detect when to use the scheduler
- Add python requests to the weave build container.
- Quote test names in urls, make flask handle url segments with slashes in.
2015-07-13 09:33:52 +00:00
Matthias Radestock 9463ceb873 test that large packets can be transmitted over weave 2015-07-13 10:31:20 +01:00
Matthias Radestock 7aab5cc5b4 refactor: simplify NewPeers 2015-07-11 17:46:57 +01:00
Matthias Radestock 2b61bc91df cosmetic: remove blank log lines 2015-07-11 16:43:33 +01:00
Matthias Radestock 57a4371e9f attempt to cure spurious test failures 2015-07-11 16:31:16 +01:00
Matthias Radestock 1d2e6b4c56 test gossip forwarding 2015-07-11 16:17:36 +01:00
Matthias Radestock 2a0bc91f86 add test for peer discovery and multi-hop routing 2015-07-11 14:57:24 +01:00
Matthias Radestock 210b926064 increase number of test VMs from 2 to 3 2015-07-11 14:57:24 +01:00
Matthias Radestock 9a089a111f don't invoke weave stop-router at end of tests
since that removes the container, rather than just stopping it.
2015-07-11 14:56:11 +01:00
Matthias Radestock 710eabeff3 reduce test vm memory 2015-07-11 12:15:46 +01:00
Matthias Radestock 603f34e944 oops, forgot to commit this 2015-07-11 12:14:43 +01:00
Matthias Radestock a3dff7e1e9 cope with more than two test hosts 2015-07-11 11:59:34 +01:00
Matthias Radestock 25486f7b4d cosmetic: s/weave_stop_router/stop_router_on
Also run `weave stop-router` as part of this, so conntrack cleanup
happens.
2015-07-11 11:58:29 +01:00
Matthias Radestock c22d824db8 .gitignore and clean up coverage reports 2015-07-11 11:57:01 +01:00
Matthias Radestock a44a3a4158 cure "./gen_coverage_reports.sh: line 5: CIRCLECI: unbound variable" 2015-07-11 11:46:49 +01:00
Matthias Radestock 982c9d7650 remove dead code 2015-07-11 11:17:44 +01:00
Matthias Radestock 7042c1506a cosmetic: consistency with other tests 2015-07-11 03:36:21 +01:00
Matthias Radestock 76943f0b17 cosmetic 2015-07-11 02:52:30 +01:00
Matthias Radestock c4f516655d refactor: simplify ipam claim try 2015-07-11 02:28:14 +01:00
Matthias Radestock aaba681db1 remove dead code 2015-07-11 01:54:37 +01:00
Tom Wilkie 56a169b50f Merge pull request #1115 from weaveworks/unit-coverage
Run unit tests in circle and include their coverage in the report.
2015-07-10 19:34:48 +01:00
Tom Wilkie 32b2ec4038 Run unit tests in circle and include their coverage in the report. Report coveralls.io from Circle. 2015-07-10 18:19:50 +00:00
Matthias Radestock ce5052305d fix CircleCI breakage 2015-07-10 18:44:30 +01:00
Matthias Radestock 0e94e3e07b complain even less about absent weave container in coverage collection 2015-07-10 18:30:13 +01:00
Matthias Radestock 3d72670105 don't complain in coverage collection when weave container is absent 2015-07-10 18:24:19 +01:00
Tom Wilkie f8718dcc5d Remove duplication of coverage stanza in weave script. 2015-07-10 16:35:46 +00:00
Paul Bellamy fa6777004f Review feedback 2015-07-10 17:34:06 +01:00
Paul Bellamy e3212d462d Merge pull request #1105 from weaveworks/673-smoke-test-coverage
Code coverage for smoke tests.
2015-07-10 16:53:59 +01:00
Tom Wilkie f5186e74c0 Review feedback 2015-07-10 15:33:00 +00:00
Tom Wilkie bf814739ba Reduce repetition in Makefile. 2015-07-10 15:03:54 +00:00
Tom Wilkie 00a7490a1d Code coverage for smoke tests.
Allow non-circle people to build coverage reports.
2015-07-10 14:45:14 +00:00
Adam Harrison da41604229 Fix IPAM test compilation 2015-07-10 15:43:28 +01:00
Adam Harrison d5a4326b9e Merge branch '1.0'
Conflicts:
	ipam/allocator_test.go
	ipam/claim.go
2015-07-10 14:35:41 +01:00
Adam Harrison 835d99e6fe Merge pull request #1104 from weaveworks/1083-async-claim
Don't wait to claim addresses from IPAM
2015-07-10 14:03:05 +01:00
Paul Bellamy e4458372e8 [proxy] weavewait rewrites /etc/hosts so weave ip is returned first from 'hostname -i'
Adds a --no-rewrite-hosts flag to the proxy, and be default, removes the
docker IP from hosts. We only discard the docker host when it exists, so
--net=none works.
2015-07-10 13:57:20 +01:00
Matthias Radestock a05c65c73b simplify feature extraction 2015-07-09 14:38:08 +01:00
David Wragg 14d346db8b Add a --protocol-min-version option to router
To allow users to forgo compatibility with Weave Net 1.0 in order to
avoid #912, by setting --protocol-min-version=2.
Defaults to --protocol-min-version=1, i.e. compatible but vulnerable.
2015-07-09 14:38:06 +01:00
David Wragg 882f14d50c Version 2 protocol, which encrypts the features map
With V1 compatibility
2015-07-09 14:37:42 +01:00
Bryan Boreham 75646d4786 Merge remote-tracking branch 'origin/1.0'
Conflicts:
	router/connection.go
2015-07-09 12:56:37 +01:00
Bryan Boreham b93070f91a Fix log message on peer move 2015-07-09 12:51:52 +01:00
Bryan Boreham cbcd5da680 Fix log message on peer move 2015-07-09 12:33:39 +01:00
Bryan Boreham cf4ce0a07d Don't cancel address claim when caller closes socket 2015-07-09 10:47:34 +01:00
Bryan Boreham 78c4b06492 Don't wait for certainty before responding to Claim
Conflicts:
	ipam/claim.go
2015-07-09 10:47:27 +01:00
David Wragg 501f40d9c1 Consolidate remote peer checks 2015-07-08 17:21:32 +01:00
Matthias Radestock 08463ea403 Merge branch '1.0' 2015-07-08 16:54:24 +01:00
Matthias Radestock 2ced8b9469 Merge branch 'latest_release_doc_updates' into 1.0 2015-07-08 16:53:13 +01:00
Matthias Radestock 26c1bcf866 clarify role of IP allocator range
Part of #1035.
2015-07-08 16:51:25 +01:00
Tom Wilkie 89ad7e1ba2 Move test gossip router out of ipam code for use in gossip dns code. 2015-07-08 15:03:13 +00:00
Matthias Radestock e6828e1aa5 Merge pull request #1101 from weaveworks/misc-changes
Extend router/peers.go to allow multiple OnGC callbacks
2015-07-08 16:00:22 +01:00
Tom Wilkie c8718d85dc Extend router/peers.go to allow multiple OnGC callbacks, and call the callbacks outside of the peers lock. 2015-07-08 14:47:02 +00:00
Tom Wilkie 56863c3d0e Ensure bin/lint doesn't complain about removed files when used as a git precommit script. 2015-07-08 14:43:21 +00:00
David Wragg e50b1f1fe7 Remove the "found" return value from Peers Fetch method
Callers can simply check whether the returned *Peer is nil.
2015-07-08 09:06:33 +01:00
David Wragg 5e10b901a3 Drop the check for duplicate connections in the handshake
Duplicate connections are also detected and handled in AddConnection,
I have not been able to reproduce #182 without the check, so although
it may once have been necessary, it doesn't seem to be needed now.
2015-07-08 09:03:51 +01:00
David Wragg a6a74001bd Make TCPReceiver more closely mirror TCPSender 2015-07-08 08:53:53 +01:00
Bryan Boreham 0b032386c1 Merge pull request #1092 from weaveworks/1082_usage_to_stderr
Note I have rebased this and merged changes.  Closes #1082.
2015-07-07 15:43:23 +01:00
Matthias Radestock 8eb092c754 display usage on stderr
so that the likes of $(weave proxy-typo-env) don't do weird things

Closes #1082.
2015-07-07 15:39:47 +01:00
Alvaro 33921c2cfe Merge pull request #1073 from weaveworks/971_check_container_alive_in_api
make container aliveness check part of the ipam & dns API
2015-07-07 16:08:40 +02:00
Matthias Radestock 6cc566fd3e Merge pull request #1090 from weaveworks/1076-ignore-iface
Don't complain about weave bridge routes clashing

Fixes #1076.
2015-07-07 13:12:41 +01:00
Matthias Radestock 491a4369d7 Merge pull request #1096 from squaremo/issue1087_password_equals
Handle --option=value as well as --option value

Closes #1087.
2015-07-07 12:57:01 +01:00
Bryan Boreham 6666fd14ac Allow multiple --ignore-iface arguments 2015-07-07 12:52:29 +01:00
Bryan Boreham e621aca77e Added --ignore-iface option to netcheck and use it to ignore weave bridge route 2015-07-07 12:52:29 +01:00
Adam Harrison 3e7c20b4ee Merge branch '1.0' 2015-07-07 12:46:00 +01:00
Michael Bridgen 22446ffc19 Handle --option=value as well as --option value
In any place we munge options, treat the form with an equals the same
as the form without.

The exes ultimately invoked by the script handle both forms, so unless
we need to do something with the option in the script, we can just let
options fall through.
2015-07-07 12:44:37 +01:00
Matthias Radestock f39791616d Merge pull request #1095 from weaveworks/move-ipam-address
Move ipam/address to net/address

Closes #1065.
2015-07-07 11:43:47 +01:00
Paul Bellamy d39aaa86ee Merge pull request #1084 from squaremo/issue602_moar_hyphens
Consistent option naming
2015-07-07 11:37:21 +01:00
Tom Wilkie 4fdb14293b Move ipam/address to net/address 2015-07-07 10:33:20 +00:00
Michael Bridgen 4ede33468e Update docs and smoke-tests with new option names 2015-07-07 11:26:09 +01:00
Michael Bridgen ebbb5d379d Usage and deprecation warnings in script
The usage is rearranged slightly to avoid wrapping at 80 characters.

This will tend to overestimate warnings, since it doesn't try very
hard to ignore values that are not intended to be options; in
particular, arguments that are intended for `docker run`. (Using `--`
to delimit these arguments would make this easier!)

It does ignore values that follow known options though, for instance
in

        weave -nickname "-password"

(-nickname results in a deprecation warning, -password does not)

and

        weave --password "-iprange"

(no deprecation warning, since -iprange is ignored)
2015-07-07 11:26:09 +01:00
Michael Bridgen 379b8c4e6b Standardise on options --like-this
Hyphenate words, and always expect double dashes for long options. A
small number of options have entirely new names.

In general I have added aliases for the unhyphenated and single-dashed
varieties, and used mflag's deprecation syntax to make it print a
warning when it sees them. I left some abbreviated options
unhyphenated (e.g., `bufsz`), and the TLS options as-is since they
correspond with Docker options.

NB the deprecation wanrings will mostly get printed to container logs,
since mostly the executables are run in containers. The next step is
to emit warnings from the script for the options mentioned in the
script usage message.
2015-07-07 11:13:30 +01:00
Adam Harrison bde445df9b Merge pull request #1089 from weaveworks/1086_dns_search_without_equals
detect `--dns-search <arg>`
2015-07-07 10:16:21 +01:00
Matthias Radestock 41b0c2a03f Merge pull request #1070 from weaveworks/1036-change-iprange
Change default IP allocation range from 10.128/10 to 10.32/12

Closes #1036.
2015-07-07 02:24:13 +01:00
Matthias Radestock 02715f88d5 cosmetic: re-order option parsing
to match the order in which we susequently process them.
2015-07-06 17:26:04 +01:00
Matthias Radestock 706f9ee352 detect --dns-search <arg>
...and not just `--dns-search=<arg>`

Fixes #1086.
2015-07-06 17:22:42 +01:00
Bryan Boreham 646cf84b4a Merge pull request #1072 from weaveworks/1043_common_launch_args
LGTM
2015-07-06 17:15:51 +01:00
Bryan Boreham f593c2fbf2 Change default IP allocation range from 10.128/10 to 10.32/12 2015-07-06 17:09:05 +01:00
Bryan Boreham bd24307c2b Merge pull request #1069 from 2opremio/fons/fix-tests-OSX
LGTM
2015-07-06 16:43:26 +01:00
Michael Bridgen e0c7054f9b Merge pull request #1075 from weaveworks/fons/test-vagrant-creation
Explain how to create the test VMs from vagrant
2015-07-06 16:31:22 +01:00
Michael Bridgen 59550e0fcb Merge branch 'pr1077' 2015-07-06 16:27:45 +01:00
Michael Bridgen af64c8e2af Merge branch '1063_status_json_accept_header' of github.com:weaveworks/weave into pr1077 2015-07-06 16:25:58 +01:00
Matthias Radestock 03ac7a86c6 make bin/multiweave work again in 'launch launches everything' world 2015-07-06 15:13:21 +01:00
Matthias Radestock 80a232f44c copy latest random_mac() from weave script to bin/multiweave 2015-07-06 15:09:23 +01:00
Matthias Radestock d1041c17d3 Merge pull request #1081 from weaveworks/weave_1.1_readme_updates_proxy_env
Remove weave proxy-(env|config) for just (env|config). Readme changes
2015-07-06 15:05:05 +01:00
Matthias Radestock cf7fff3da2 Merge pull request #1080 from weaveworks/1014-weave-env
rename proxy-(env|config) to (env|config)

Closes #1014.
2015-07-06 15:04:40 +01:00
Alfonso Acosta 373d663b5e Fix integration tests for OSX 2015-07-06 14:48:53 +01:00
Alfonso Acosta e86d255a47 Add comment about running integration tests from the Vagrant build VM 2015-07-06 14:07:16 +01:00
Paul Bellamy 5c44f74475 rename proxy-(env|config) to (env|config)
Keep error message to stderr, as the proxy should be launched with
'weave launch'. 'proxy-(env|config)' are kept with a deprecation
warning, for a while.
2015-07-06 13:41:23 +01:00
Paul Bellamy d649bdddc7 Remove weave proxy-(env|config) for just (env|config). Readme changes 2015-07-06 12:59:07 +01:00
Matthias Radestock 5c7763b075 Merge branch '1.0' 2015-07-06 11:00:15 +01:00
Matthias Radestock e3ab396428 Merge branch 'latest_release_doc_updates' into 1.0 2015-07-06 10:59:34 +01:00
Matthias Radestock df31454c30 Merge commit 'c8032b6' into latest_release_doc_updates 2015-07-06 10:58:18 +01:00
Matthias Radestock c8bd47e948 set correct content-type header when returning json status 2015-07-05 19:25:28 +01:00
Matthias Radestock 098fc81825 output json status based on 'Accept' header
...instead if having a separate URL.

Closes #1063.
2015-07-05 19:19:02 +01:00
Alfonso Acosta cab4a7e1ee Explain how to create the test VMs from vagrant 2015-07-05 18:59:53 +01:00
Matthias Radestock 6596c8b1a7 make container aliveness check part of the ipam & dns API
...rather than conducting it implicitly when an ID "looks like" a
container ID.

Closes #971
2015-07-04 19:13:13 +01:00
Matthias Radestock 5559f1df2e cosmetic: remove some unnecessary quoting
for consistency, since we don't quote the same term anywhere else
2015-07-04 18:14:13 +01:00
Matthias Radestock 156597a965 small simplifying refactor: use shortcircuit boolean expressions 2015-07-04 18:04:25 +01:00
Matthias Radestock d0641062c0 pass common launch args to all three components
ATM that's just --loglevel=...

Closes #1043.
2015-07-04 04:38:18 +01:00
Matthias Radestock cf4fffa394 Merge pull request #1057 from weaveworks/329-log-level-choice
Give the user more choice of log levels

Closes #329
2015-07-04 03:46:59 +01:00
Matthias Radestock b30de63f18 Merge pull request #1067 from paulbellamy/proxy-stderr
print all stderr output from callWeave to the proxy logs, even if successful

Closes #1056
2015-07-04 03:44:50 +01:00
Bryan Boreham ff2d7451e2 Remove -debug option, superceded by --log-level 2015-07-04 03:27:07 +01:00
Matthias Radestock 910b57731d remove stray -debug in tests 2015-07-04 03:19:27 +01:00
Matthias Radestock b8aa1d473b remove dead code 2015-07-04 02:53:36 +01:00
Matthias Radestock ce26f88ee2 Merge branch '1.0'
Conflicts:
	router/crypto.go
	router/ethernet_decoder.go
	router/handshake_test.go
	router/mocks_test.go
	router/peers_test.go
	router/router.go
	router/udp_sender.go
2015-07-03 20:47:29 +01:00
Matthias Radestock bf73029d15 cosmetic: goimports -w . 2015-07-03 20:41:35 +01:00
Matthias Radestock 4dbd657a26 import gopacket from github instaead of google code
The latter redirects to the former, which breaks `go get`.

Closes #1071.
2015-07-03 20:32:27 +01:00
Matthias Radestock 1524796408 Merge pull request #1068 from weaveworks/1004-seed-random
Seed random-number generator from the host time

Fixes #1004.
2015-07-03 16:01:16 +01:00
Paul Bellamy 7821412bf7 [proxy] print all stderr from callWeave to the proxy logs, even if successful 2015-07-03 15:45:09 +01:00
Matthias Radestock b72b7a9ed5 Merge branch '1.0' 2015-07-03 14:28:42 +01:00
Matthias Radestock a3a7a19870 simplifying refactor 2015-07-03 14:28:18 +01:00
Matthias Radestock 8b0089565f Merge branch '1.0' 2015-07-03 14:18:46 +01:00
Matthias Radestock 251c68e85a Merge pull request #1064 from weaveworks/issues/1023-await-ipam-forever
Wait forever for IPAM

Fixes #1023.
2015-07-03 14:17:52 +01:00
Adam Harrison 227cb11f25 Router, DNS and proxy wait forever by default 2015-07-03 14:01:34 +01:00
Adam Harrison 6097dc8aaa Add wait forever capability to EnsureInterface 2015-07-03 14:01:34 +01:00
Bryan Boreham 8c6c692926 Seed the math/rand generator from the time 2015-07-03 13:39:47 +01:00
Matthias Radestock 7554bd3913 Merge branch 'master' into weave_1.1_readme_updates
Conflicts:
	README.md
2015-07-03 11:55:32 +01:00
Matthias Radestock b81fce32f6 one more s/proxy/Weave Docker API proxy 2015-07-03 11:51:12 +01:00
Matthias Radestock 3ea1448c52 cosmetic 2015-07-03 11:49:20 +01:00
Matthias Radestock f31a3c1261 Merge branch '1.0' 2015-07-03 11:48:07 +01:00
Matthias Radestock 9e32605183 Merge branch 'latest_release_doc_updates' into 1.0 2015-07-03 11:45:37 +01:00
Matthias Radestock 9009e0eb7f one more s/proxy/Weave Docker API proxy 2015-07-03 11:45:05 +01:00
Matthias Radestock 243d445d1b Merge branch 'latest_release_doc_updates' into 1.0 2015-07-03 10:59:10 +01:00
Matthias Radestock 65cd2b0e04 missed one 2015-07-03 10:58:22 +01:00
Matthias Radestock 33b230c114 Merge branch 'latest_release_doc_updates' into 1.0 2015-07-03 10:56:00 +01:00
Matthias Radestock 6389af6ea2 Merge pull request #1066 from weaveworks/1019_rename_proxy_in_docs
introduce 'Proxy' as 'Docker API Proxy'

Closes #1066.
2015-07-03 10:55:17 +01:00
Matthias Radestock fcea6fcbf8 Merge branch '1.0' 2015-07-03 10:03:43 +01:00
Matthias Radestock 8b35f5e8fe fix typo in comment 2015-07-03 10:03:07 +01:00
Matthias Radestock 7d5dcfafc9 introduce 'Proxy' as 'Docker API Proxy'
in order to reduce potential confusion about its role.

Closes #1019.
2015-07-03 03:33:23 +01:00
Matthias Radestock 07ee2df822 cosmetic(ish): use dotted import. like everywhere else 2015-07-02 21:07:51 +01:00
Paul Bellamy 5b79e274e8 fixup logging changes, so it compiles 2015-07-02 17:04:21 +01:00
Matthias Radestock e25d1b8365 Merge branch '1.0' 2015-07-02 16:35:40 +01:00
Matthias Radestock af40ebcab4 Merge pull request #1062 from weaveworks/issues/1006-fix-hostname-derivation
Disable hostname derivation when container name is too long

Fixes #1006.
2015-07-02 16:34:40 +01:00
Paul Bellamy 3fd146557a Merge pull request #1060 from weaveworks/issues/1059-remove-google-code-refs
Remove all the imports from "code.google.com"
2015-07-02 15:59:57 +01:00
Adam Harrison d427c99161 Add smoke test 2015-07-02 14:29:38 +01:00
Adam Harrison d1a887857d Enforce maximum length on weave run hostname derivation 2015-07-02 14:29:38 +01:00
Adam Harrison 3914c4c018 Enforce maximum length on proxy hostname derivation 2015-07-02 14:29:34 +01:00
Alvaro Saurin f6ffd604dd Removed all the imports from "code.google.com" 2015-07-02 13:32:52 +02:00
Matthias Radestock 18a4c14670 clarify network/address route overlap error/warning message
Closes #1034.
2015-07-02 11:51:22 +01:00
Matthias Radestock 0f67dbd8bd Merge branch '1.0' 2015-07-02 11:48:22 +01:00
Matthias Radestock 3bd5ae56f0 fix make update
Trying to build the thing for which we want to fetch the dependencies
is rather silly.
2015-07-02 11:46:48 +01:00
Matthias Radestock ecfc5d091d Merge pull request #1053 from weaveworks/437-check-address
Check addresses supplied by users for conflict with existing routes.

Fixes #437. Fixes #480.
2015-07-01 15:03:28 +01:00
Bryan Boreham 84238a0262 Amend error message to reflect usage situation 2015-07-01 14:58:42 +01:00
Matthias Radestock edf3ec9f09 Merge pull request #1044 from weaveworks/664-use-logrus
Use off-the-shelf logger logrus

Closes #664.
2015-07-01 14:47:52 +01:00
Matthias Radestock 2fee0f0184 Merge branch '1.0' 2015-07-01 14:02:57 +01:00
Bryan Boreham a5ddb63fca Remove Info.Print in favour of Log.Info 2015-07-01 14:01:45 +01:00
Matthias Radestock a05a3c4b26 Merge pull request #1054 from weaveworks/1042-reset-first-10
Call `weave reset` earlier in smoke-test set-up, to improve reliability

Fixes #1042.
2015-07-01 14:01:29 +01:00
Bryan Boreham a938608cc8 Call weave reset before deleting containers, to improve clean-down 2015-07-01 13:19:43 +01:00
Bryan Boreham 013380c7e7 Update router code to use common logger 2015-07-01 12:26:23 +01:00
Bryan Boreham 13c0b62f96 When looking at proxy logs, use both stderr and stdout 2015-07-01 12:26:23 +01:00
Bryan Boreham 7276fc1d4b Wire up selection of logging levels to match old behaviour
- note that all output is now going to stderr.
2015-07-01 12:26:23 +01:00
Bryan Boreham 67a6961b4b Adjust logrus feature of padding all lines less than 44 chars with spaces at the end 2015-07-01 12:26:23 +01:00
Bryan Boreham d82578d895 Replace logging calls with new style 2015-07-01 12:26:23 +01:00
Bryan Boreham c786e7eb5a Switch from basic logger to logrus 2015-07-01 12:26:23 +01:00
Bryan Boreham 91099694c9 Send error message to stderr 2015-07-01 12:17:02 +01:00
Bryan Boreham c9a20ae10c Warn user if they are trying to use a specific address that clashes with an existing route 2015-07-01 12:17:02 +01:00
Bryan Boreham f05225da22 Check specific addresses differently to networks
For a network, as used by IPAM, we care if *any* address in the range
could possibly conflict with an existing address routed by the host.
When given a single address, we only care if *that specific address*
conflicts with an existing route.
2015-07-01 12:15:04 +01:00
Matthias Radestock badfdee5e4 Merge branch '1.0' 2015-07-01 11:34:50 +01:00
Matthias Radestock ba1e75405f Merge branch 'latest_release_doc_updates' into 1.0 2015-07-01 11:34:24 +01:00
Matthias Radestock 0f5f671054 Merge pull request #1051 from weaveworks/issues/1046-clarify-weavedns-docs
Draw attention to --hostname precedence

Fixes #1046.
2015-07-01 11:33:24 +01:00
Adam Harrison 5b9d350002 Draw attention to --hostname precedence 2015-07-01 11:15:08 +01:00
Bryan Boreham 6403e7a11c Add missing rule to rebuild netcheck when its source changes 2015-07-01 11:13:40 +01:00
Matthias Radestock 312122e1be fix typo 2015-07-01 10:30:12 +01:00
Matthias Radestock bb1da63bfc Merge pull request #1048 from squaremo/issue1040_eliminate_ipam_mentions
Remove references to 'IPAM' in docs and UI

Closes #1040.
2015-06-29 23:15:58 +01:00
Matthias Radestock d1b2e74985 Merge pull request #1045 from squaremo/issue1041_usage_for_initpeercount
Add -initpeercount to usage

Closes #1041.
2015-06-29 19:48:52 +01:00
Michael Bridgen c0b2fe3e3b Remove references to 'IPAM' in docs and UI
The option `--no-default-ipam` is replaced with
`--no-default-ipalloc`; the former will still work (in a 'last one
wins' way), but its usage message says it's deprecated.
2015-06-29 19:20:26 +01:00
Michael Bridgen 864461918f Add -initpeercount to usage 2015-06-29 17:06:14 +01:00
Matthias Radestock 9127eb8acf cosmetic: make weave run usage fit in 80 columns 2015-06-29 16:56:57 +01:00
Paul Bellamy 8e629d9471 Merge pull request #999 from paulbellamy/scope-tests
Let Scope use these integration test helpers
2015-06-29 13:51:26 +01:00
Matthias Radestock ab943d938a refactor: don't return encoder from handshake()
We did this for symmetry/completeness, but the encoder is actually
"consumed" by handshake() in the creation of the tcp senders, hence it
is in fact dangerous to return it.
2015-06-29 12:38:21 +01:00
David Wragg d6fedc1e6e Remove error return value from DecodeLayers
It is never checked, so add a comment explaining why.
2015-06-29 10:47:56 +01:00
David Wragg 3222478449 Remove the LogFrame when fragmenting
Subsequent commits make this LogFrame call a little tricky to retain,
and it's not clear that it is really needed.
2015-06-29 10:47:26 +01:00
Paul Bellamy 26cb65c357 making test helpers more robust so scope can use them 2015-06-29 10:22:10 +01:00
Paul Bellamy 1ef3bd3a8b add docker version and weave version to test sanity checks 2015-06-29 10:20:55 +01:00
Matthias Radestock 51ecf05091 cosmetic: fix typo in comment 2015-06-27 09:35:14 +01:00
Bryan Boreham 565454d3cb fixed Logf format specifier 2015-06-26 17:22:35 +01:00
Matthias Radestock 906ff210fe Merge pull request #1026 from weaveworks/1003-proxy-unix-socket
Make proxy listen on unix:///var/run/weave.sock

Closes #1003
2015-06-26 16:44:41 +01:00
Tom Wilkie c4565f6ed3 Make proxy listen on /var/run/weave.sock 2015-06-26 15:08:21 +00:00
Matthias Radestock 8dd9020803 Merge branch '1.0'
Conflicts:
	ipam/allocator_test.go
2015-06-26 14:08:09 +01:00
Matthias Radestock ec62029db5 Merge pull request #1024 from weaveworks/982-fix-mock-gossip3
Flush messages from mock gossip in IPAM test

Fixes #982.
2015-06-26 13:44:57 +01:00
Matthias Radestock 55fbffef75 Merge pull request #1009 from awh/weave_launch_all_the_things
Weave launch all the things

Closes #962.
2015-06-26 13:21:44 +01:00
Bryan Boreham c84c5dbcdf Make TestGossipRouter goroutine easier to follow 2015-06-26 11:53:14 +01:00
Bryan Boreham 6725c8355f Check the result of Allocate in TestFakeRouterSimple() 2015-06-26 11:53:03 +01:00
Bryan Boreham c659e3448a Add a flush() function to the mock gossip router, and call it where required 2015-06-26 11:40:58 +01:00
Bryan Boreham 959e5f0082 Use different object types for mock gossip messages 2015-06-26 11:38:25 +01:00
Bryan Boreham 6488b0d11c More idiomatic exitChan 2015-06-26 11:38:07 +01:00
Matthias Radestock 039a2030db refactor: make DF flag passing logic more obvious
...and provide a better abstraction for sending special frames.
2015-06-26 11:11:17 +01:00
Matthias Radestock 33d85aaee5 Merge pull request #1016 from dpw/defer-df
Don't inspect the DF flag earlier than we need to
2015-06-26 11:10:42 +01:00
Adam Harrison d4c1766532 Fix smoke tests 2015-06-26 11:10:08 +01:00
Matthias Radestock 39a6d1c87e refactor: simplify a test 2015-06-25 20:13:03 +01:00
Matthias Radestock ee2a66323e Merge branch '1.0'
Conflicts:
	ipam/ring/ring_test.go
2015-06-25 19:57:09 +01:00
Matthias Radestock 635a3e5b26 cosmetic: remove attempt at pretty shell fragment
there is just no way to format this so it looks good everywhere, due
to different tab widths.
2015-06-25 19:51:39 +01:00
Matthias Radestock 82da45a638 go get test dependencies
The previous 'fix' wasn't working since it `go get -t`'ed the dirs
where our executables live. Which don't contain any tests.
2015-06-25 19:31:38 +01:00
David Wragg 64ac7745fe Don't inspect the DF flag earlier than we need to 2015-06-25 17:11:52 +01:00
David Wragg 5690902173 Move DF indication into LogFrame 2015-06-25 17:11:20 +01:00
David Wragg 4162dcafd3 Make the full EthernetDecoder available to LogFrame
So that LogFrame can include information about the decoded frame beyond
the ethernet layer.
2015-06-25 17:11:20 +01:00
Matthias Radestock 9253d45968 Merge pull request #1010 from bboreham/896-check-gossip
Try other peers for space if we get errors

Fixes #896
2015-06-25 17:05:40 +01:00
Matthias Radestock ea59c6c065 Merge pull request #1015 from weaveworks/go-get-tests
Extend 'go get' to tests
2015-06-25 17:01:26 +01:00
Bryan Boreham 7c741d6f7e Extend 'go get' to tests
The `-t` flag asks Go to fetch anything required for tests, which is more necessary now we have added testify.
2015-06-25 16:42:53 +01:00
Adam Harrison 99dc70ea7a Eliminate references to proxy and weaveDNS 2015-06-25 15:42:06 +01:00
Bryan Boreham 8a4b0d536e Updated after review feedback 2015-06-25 15:41:06 +01:00
Adam Harrison b78d4f05d0 Use new weave launch command introduced by #962 2015-06-25 14:59:43 +01:00
Bryan Boreham c76f640e2e Simpler implementation of weighted-random order 2015-06-25 14:54:44 +01:00
Adam Harrison a9e3320e7c Update documentation 2015-06-25 14:43:29 +01:00
Bryan Boreham b472da18f0 Improve logging of unicast gossip errors 2015-06-25 14:04:37 +01:00
Bryan Boreham c58cc449c0 More complex ChoosePeersToAskForSpace, with better complexity 2015-06-25 13:19:49 +01:00
Bryan Boreham af9668f130 Keep trying peers to ask for space until we find one that doesn't error 2015-06-25 12:47:38 +01:00
Adam Harrison 08f3d5b8e8 Implement new weave stop behaviour 2015-06-25 12:47:18 +01:00
Adam Harrison 0d5aad3e90 Rename stop to stop-router 2015-06-25 12:47:18 +01:00
Adam Harrison fcd79a78af Implement new weave launch behaviour 2015-06-25 12:47:14 +01:00
Bryan Boreham 9f784b3c31 Report errors in unicasting gossip 2015-06-25 12:22:53 +01:00
Matthias Radestock 657bbbffbc tiny refactor: raise abstraction level 2015-06-24 18:17:15 +01:00
Tom Wilkie 7780ac1078 Fix gce.sh destory timeouts. 2015-06-24 17:13:37 +00:00
Matthias Radestock 76a12edf57 Merge pull request #997 from dpw/eliminate-starts
Eliminate most Start methods in router package
2015-06-24 18:04:57 +01:00
David Wragg 7387caa2eb Eliminate forwarder Start methods 2015-06-24 17:37:07 +01:00
David Wragg 576dd69b0b Add a comment to Router's Start method 2015-06-24 17:37:02 +01:00
David Wragg 5e6b107d01 Eliminate the GossipSender Start method 2015-06-24 17:35:26 +01:00
David Wragg f444ae9224 Eliminate ConnectionMaker Start method 2015-06-24 17:35:26 +01:00
David Wragg 8f5d1eec11 Eliminate the Routes Start method 2015-06-24 17:35:17 +01:00
Matthias Radestock 9a6afcd236 Merge branch '1.0' 2015-06-24 17:26:19 +01:00
Matthias Radestock 5f95de19ce Merge branch 'latest_release_doc_updates' into 1.0 2015-06-24 17:25:51 +01:00
Fintan Ryan 8fb00ff206 Merge pull request #1002 from weaveworks/include-css
Include stylesheet in repo, update footer
2015-06-24 17:09:53 +01:00
Matthias Radestock 94a8338be5 fix typo in comment 2015-06-24 17:02:35 +01:00
David Kaltschmidt 00744fb9b4 use relative paths for stylesheet in docs 2015-06-24 17:54:50 +02:00
David Kaltschmidt 8250e05618 include stylesheet in repo, update footer
need because website's custom.css is fingerprinted
2015-06-24 17:18:54 +02:00
David Wragg 12c17d39cc Merge weaveworks/separate_image_build_and_export (PR #994)
separate the image build and export phases
2015-06-24 15:26:11 +01:00
Adam Harrison 409961fff3 Rename launch to launch-router 2015-06-24 14:30:35 +01:00
David Wragg 159d47a934 Merge rade/992_fractional_sleep (PR #993)
Sleep for 0.1s instead of 1s in the wait_for_status loop.

Closes #992.
2015-06-24 12:44:52 +01:00
Matthias Radestock 4b11874390 sleep for 0.1s instead of 1s in the wait_for_status loop
This reduces `weave launch|launch-dns` execution time.

Closes #992.
2015-06-24 12:16:52 +01:00
Matthias Radestock 3c7cff1c9c Merge pull request #996 from weaveworks/rm_dockercon_pointer
Remove Link to Dockercon from README
2015-06-24 10:47:00 +01:00
Fintan Ryan e3574f243f Remove Link to Dockercon from README 2015-06-24 10:35:12 +01:00
Matthias Radestock bdda9d03ae bump docker version in weave-build docker image
to 1.3.1, the same version we use everywhere else, e.g. in weavexec.
2015-06-24 03:24:58 +01:00
Matthias Radestock ae1805de76 separate the image build and export phases
That way the export can be just a single tar, which is more
convenient. And without introducing unnecessary image builds.
2015-06-24 02:54:45 +01:00
Matthias Radestock 0ce123e63a Merge branch '1.0' 2015-06-24 02:53:06 +01:00
Matthias Radestock 9ae45f6674 Merge branch 'latest_release_doc_updates' into 1.0 2015-06-24 02:52:37 +01:00
Matthias Radestock 6c3aa99595 bump min required docker version for build
to the same version we use everywhere else, e.g. in weaveexec
2015-06-24 02:51:24 +01:00
Matthias Radestock 215d96fdc3 Merge branch '1.0' 2015-06-23 23:18:09 +01:00
Matthias Radestock 2c73fd6dde Merge branch 'latest_release_doc_updates' into 1.0 2015-06-23 23:17:38 +01:00
Matthias Radestock 3b27413887 fix broken link 2015-06-23 23:17:05 +01:00
Matthias Radestock eb9e450cf1 cosmetic 2015-06-23 19:10:10 +01:00
David Wragg 72039bbe5a Eliminate the LocalPeer Start method 2015-06-23 16:08:43 +01:00
David Wragg d6bceb139e Eliminate MacCache Start method 2015-06-23 16:08:43 +01:00
David Wragg a311623bb9 Eliminate the Start method on LocalConnection 2015-06-23 16:08:43 +01:00
Paul Bellamy 95d72a57e8 Merge pull request #989 from rade/prefetch_test_images
prefetch test images
2015-06-23 15:14:59 +01:00
Adam Harrison aa8d9eab53 Merge branch '1.0' 2015-06-23 15:05:32 +01:00
Adam Harrison 17ee829299 Merge branch 'latest_release_doc_updates' into 1.0 2015-06-23 15:05:02 +01:00
Adam Harrison 5db39466e8 Fix broken IPAM link 2015-06-23 15:04:37 +01:00
Matthias Radestock 43b8840dd2 Merge branch '1.0' 2015-06-23 14:58:55 +01:00
Matthias Radestock cc4ab80f62 Merge branch 'latest_release_doc_updates' into 1.0 2015-06-23 14:57:51 +01:00
Matthias Radestock 0e47cee661 remove references to dns, ipam and proxy docs from ToCs
Since they are now explained and referenced prominently from the main
Features documentation.
2015-06-23 14:53:49 +01:00
Adam Harrison 59e667801d Merge GCE fix from branch '1.0' 2015-06-23 14:50:58 +01:00
Adam Harrison d6dca80dd7 Merge branch 'latest_release_doc_updates' into 1.0
Pull in GCE fix
2015-06-23 14:49:36 +01:00
Tom Wilkie 74c391f19c Update gce.sh make_template command, and build a fresh template. 2015-06-23 14:48:48 +01:00
Matthias Radestock ae8172ec97 simplify docker image removal 2015-06-23 14:39:29 +01:00
Adam Harrison d6ab78ff03 Merge documentation from branch '1.0' 2015-06-23 14:36:04 +01:00
Adam Harrison 7e36dc01e8 Merge documentation from branch 'latest_release_doc_updates' into 1.0 2015-06-23 14:35:30 +01:00
Adam Harrison 9020c84f81 Merge pull request #990 from awh/feature_docs_proxy_section
Add docker integration section to feature docs
2015-06-23 14:34:25 +01:00
Adam Harrison d0a1514ab6 Removed redundant further reading section from feature docs 2015-06-23 14:33:33 +01:00
Adam Harrison 06c3a0cf2a Add docker integration section to feature docs 2015-06-23 14:33:29 +01:00
Adam Harrison dde5782625 Merge documentation from branch '1.0' 2015-06-23 14:21:05 +01:00
Adam Harrison a1913130e7 Merge documentation from branch 'latest_release_doc_updates' into 1.0 2015-06-23 14:18:42 +01:00
Matthias Radestock 369bd0f4dc prefetch test images
This gives more consistent test execution times, and also make image
fetch failures more apparent and decoupled from the tests.

We could *always* just run pull, but it's an expensive operation, so
we check for the presence of the image first. The downside is that we
could be running old images. That is not a problem on our CI
infrastructure though since it spins up fresh VMs per for every tun.
2015-06-23 14:16:52 +01:00
Tom Wilkie 3b4a47a961 Update gce.sh make_template command, and build a fresh template. 2015-06-23 13:07:09 +00:00
Matthias Radestock f29ee3ad05 Merge pull request #986 from awh/feature_docs_weavedns_section
Add service discovery section to features docs
2015-06-23 13:55:50 +01:00
Adam Harrison 19a3fc6b9e Add naming and discovery section to features docs 2015-06-23 13:50:37 +01:00
Paul Bellamy 0a287217b6 Merge pull request #955 from paulbellamy/testing
Replacing custom testing helpers with testify, where possible
2015-06-23 12:21:24 +01:00
Adam Harrison 043d15355c Restore commit lost to force push 2015-06-23 11:53:22 +01:00
Matthias Radestock 2c4cebe9d9 Merge branch '1.0' 2015-06-23 10:31:40 +01:00
Matthias Radestock 9ec1ce3225 Merge pull request #981 from weaveworks/970_show_docker_py_test_failures
show docker-py test failures
2015-06-23 10:30:57 +01:00
Matthias Radestock cedf9bb82d Merge branch '1.0' 2015-06-23 09:54:24 +01:00
Matthias Radestock 1b4f83c067 Merge branch 'latest_release_doc_updates' into 1.0 2015-06-23 09:53:44 +01:00
Matthias Radestock c126c403e7 Merge pull request #983 from rade/improve_ipam_doc_integration
improve IPAM doc integration
2015-06-23 09:52:39 +01:00
Matthias Radestock 90b9b3c1bf 'address allocation' is the feature
covering both automatic and manual allocation
2015-06-23 09:50:39 +01:00
Matthias Radestock 1a17b41bc3 reduce test output noise 2015-06-23 09:42:46 +01:00
Matthias Radestock 2e5e0a7730 remove pointless assertion 2015-06-23 09:42:46 +01:00
Matthias Radestock b2f62cc635 show test failure output
step towards fixing #970
2015-06-23 09:42:46 +01:00
Matthias Radestock 2a9f8a3d75 Merge branch '1.0' 2015-06-22 23:00:29 +01:00
Matthias Radestock 0a553d6630 Merge branch 'latest_release_doc_updates' into 1.0 2015-06-22 22:59:16 +01:00
Matthias Radestock e51c240ea1 fix broken links 2015-06-22 22:57:48 +01:00
Paul Bellamy acd5b91eb8 Merge remote-tracking branch 'upstream/1.0' 2015-06-22 17:28:50 +01:00
Paul Bellamy f5201469a3 Merge pull request #980 from paulbellamy/only-one-entrypoint
[proxy] Only prepend weavewait if not already present
2015-06-22 17:28:20 +01:00
Paul Bellamy 34aa2e32d8 [proxy] Only prepend weavewait if not already present
This way `docker commit` will not stack up weavewaits.
2015-06-22 15:24:49 +01:00
Adam Harrison bda245bfdb Merge pull request #968 from rade/954_show_attach_expose_addresses
show IP addresses in attach/expose/detach/hide
2015-06-22 14:49:41 +01:00
Bryan Boreham d1eb2917e8 Merge documentation from branch 1.0 into master 2015-06-22 14:32:12 +01:00
Bryan Boreham 9a631d0ecd Merge documentation from branch 'latest_release_doc_updates' into 1.0 2015-06-22 14:25:56 +01:00
Bryan Boreham c8032b6b25 Merge pull request #977 from bboreham/proxy-net-syntax
Change one IPAM proxy example to use a network
2015-06-22 14:24:24 +01:00
Bryan Boreham 68ca6ebe04 Change one IPAM proxy example to use the 'net:' syntax 2015-06-22 14:15:24 +01:00
Matthias Radestock 9f0cd93859 refactor(ish): get the more fundamental setup out of the way first 2015-06-22 12:50:50 +01:00
Matthias Radestock 7cf0d59003 update docs 2015-06-22 12:34:32 +01:00
Adam Harrison 74f8c07674 Merge documentation from branch '1.0' 2015-06-22 12:05:40 +01:00
Adam Harrison 1bbec56cc7 Merge documenation from branch 'latest_release_doc_updates' into 1.0 2015-06-22 12:02:35 +01:00
Adam Harrison b86bae9c86 Fix incorrect address in service import/export examples 2015-06-22 12:00:57 +01:00
Paul Bellamy 6d89867a74 Merge pull request #978 from rade/972_remove_proxy_d_option
remove proxy -D option
2015-06-22 11:36:37 +01:00
Matthias Radestock ebf725d869 add some tests 2015-06-22 11:26:33 +01:00
Matthias Radestock 257774a093 make weave attach|expose|detach|hide show IP addresses
The output contains one IP address for each addr (i.e. [ip:]<cidr> |
net:<cidr> | net:default) supplied on the command line; or, if none
was specified, a single address from the default IPAM subnet.

We don't want `weave run` to output the addresses, since it is meant
to (only) output the container id. Hence we redirect the `attach`
output to stderr in that case.

Closes #954.
2015-06-22 11:26:33 +01:00
Matthias Radestock 6f798f723a Merge remote-tracking branch 'upstream/1.0' into 1.0 2015-06-22 11:17:40 +01:00
Matthias Radestock 5fbda6b094 Merge remote-tracking branch 'upstream/master' 2015-06-22 11:15:58 +01:00
Matthias Radestock b483be8c4b remove the -D proxy option
It doesn't do anything useful since the weave script needs to be able
to talk to docker on the standard unix socket.

Fixes #972.
2015-06-22 11:13:53 +01:00
Adam Harrison a751237164 Merge documentation from branch '1.0' 2015-06-22 11:00:25 +01:00
Matthias Radestock 0c87dcd656 cosmetic 2015-06-22 11:00:12 +01:00
Adam Harrison b1bd1d4d6a Merge pull request #964 from awh/v1.0_use_proxy_doc_updates
Update documentation to leverage proxy
2015-06-22 10:46:00 +01:00
Matthias Radestock e276f3e026 cosmetic 2015-06-22 10:38:59 +01:00
Adam Harrison 1e638be7ac Remove weave run from how it works 2015-06-22 10:36:56 +01:00
Adam Harrison acd2b9abeb Reword proxy hyperlink text in index 2015-06-22 10:36:56 +01:00
Adam Harrison 6ddecfc764 Update proxy docs 2015-06-22 10:36:56 +01:00
Adam Harrison f15ad71354 Update IPAM docs to use proxy 2015-06-22 10:36:56 +01:00
Adam Harrison 4d54af39b0 Update weaveDNS docs to use proxy 2015-06-22 10:36:56 +01:00
Adam Harrison 81d5a8f4f5 Update features docs to use proxy 2015-06-22 10:36:56 +01:00
Adam Harrison 344119f42a Update README examples to use proxy 2015-06-22 10:36:52 +01:00
Matthias Radestock 84e5659090 Merge branch '1.0' 2015-06-22 10:31:50 +01:00
Matthias Radestock 514db8efdd Merge branch 'latest_release_doc_updates' into 1.0 2015-06-22 10:31:20 +01:00
Paul Bellamy 0693ec55c6 Merge pull request #974 from rade/972_remove_proxy_d_option_from_docs
remove the `-D` proxy option from the docs
2015-06-22 10:29:16 +01:00
Matthias Radestock 56c720b078 remove the -D proxy option from the docs
It doesn't do anything useful since the weave script needs to be able
to talk to docker on the standard unix socket.

Also remove dangling reference to 'localhost', and move note about
firwalls to where it is most pertinent.

Part fix to #972.
2015-06-22 10:20:18 +01:00
Matthias Radestock bbeb7966b4 Merge remote-tracking branch 'upstream/1.0' 2015-06-21 10:42:28 +01:00
Matthias Radestock 403b9654f1 Merge remote-tracking branch 'upstream/1.0' into latest_release_doc_updates 2015-06-21 10:41:38 +01:00
Matthias Radestock 84775e414b Add release 1.0.1 2015-06-21 10:05:52 +01:00
Matthias Radestock edb4911bf6 Merge branch '1.0' 2015-06-21 09:39:45 +01:00
Matthias Radestock d913ca869c Merge branch '967_include_image_name_in_inspect_error' into 1.0 2015-06-21 09:38:42 +01:00
Michael Bridgen e3917121fa Merge remote-tracking branch 'upstream/1.0.0' 2015-06-20 16:45:34 -07:00
Michael Bridgen ede288ad38 Merge pull request #969 from rade/967_include_image_name_in_inspect_error
include image name in errors
2015-06-20 16:41:05 -07:00
Michael Bridgen 58f14551dc Merge branch 'pr969' 2015-06-20 16:28:44 -07:00
Michael Bridgen 97816d6c54 Merge branch '967_include_image_name_in_inspect_error' of https://github.com/rade/weave into pr969 2015-06-20 16:21:20 -07:00
Matthias Radestock 3732f968d0 include image name in errors
The go docker client's ErrNoSuchImage does not include the image
name. However, it is imperative that this name appears in error
messages sent to clients, since the docker client, as of version
1.7.0, only automatically pulls images when their name is present in
the error.

Fixes #967.
2015-06-20 23:36:48 +01:00
Matthias Radestock 939a095bf4 Merge pull request #966 from weaveworks/readme_website_sync
README update to point at weave.works
2015-06-19 20:38:37 +01:00
Fintan Ryan ad0d53f7fe Update proposed text 2015-06-19 19:17:43 +01:00
Fintan Ryan e748ddc20d README update to point at weave.works 2015-06-19 13:00:01 +01:00
Matthias Radestock e51ecd73b3 refactor: extract helper 2015-06-18 19:01:10 +01:00
Matthias Radestock 5479d0a54d improve test performance - only fetch status once per check 2015-06-18 18:53:06 +01:00
Matthias Radestock 6830b27317 cosmetic: correct some comments, tweak some arg parsing 2015-06-18 18:13:27 +01:00
Matthias Radestock 4399be7ad2 correct comments 2015-06-18 17:55:42 +01:00
Matthias Radestock 42d2a860eb test more things, more obviously 2015-06-18 17:48:21 +01:00
Matthias Radestock 3ec0200067 cosmetic oops 2015-06-18 16:57:54 +01:00
Matthias Radestock d1e4a8ec8a cosmetic: vertical alignment and consistency for better readability 2015-06-18 16:26:16 +01:00
Paul Bellamy 54b3061c50 Merge gofmt and goimports run 2015-06-18 14:05:51 +01:00
Bryan Boreham 19fe808dff Update links in changelog to point at latest_release 2015-06-18 11:59:44 +01:00
Bryan Boreham b7281cc08d Add release 1.0.0 2015-06-18 11:23:44 +01:00
Adam Harrison 617c3883c7 Merge pull request #948 from awh/v1.0_doc_updates
Update documentation to leverage IPAM and DNS
2015-06-18 10:38:32 +01:00
Adam Harrison 9e20a5256b Rename example containers 2015-06-18 10:32:15 +01:00
Matthias Radestock a3db33cfd9 more helpful error message when weave et al die on startup
Closes #958.
2015-06-17 22:54:11 +01:00
Adam Harrison aa8a86a0b9 Update service binding example 2015-06-17 17:34:15 +01:00
Adam Harrison 7238192150 Update service routing example 2015-06-17 17:34:15 +01:00
Adam Harrison caa0e31be2 Update service import example 2015-06-17 17:34:15 +01:00
Adam Harrison 5a6cafc3da Update service export example 2015-06-17 17:34:15 +01:00
Adam Harrison 1df52350fa Update host network integration example 2015-06-17 17:34:15 +01:00
Adam Harrison 40323202f5 Update dynamic attachment example 2015-06-17 17:34:15 +01:00
Adam Harrison 0d83f24661 Prefer -ti to -t -i in updated examples 2015-06-17 17:34:15 +01:00
Adam Harrison 8550469c9f Update subnet isolation example 2015-06-17 17:34:15 +01:00
Adam Harrison e5679a787c Update subnet isolation example 2015-06-17 17:34:15 +01:00
Adam Harrison e37a4314b3 Add manual addressing to features 2015-06-17 17:34:10 +01:00
Adam Harrison 7b5ee8fb65 Update virtual ethernet switch example 2015-06-17 17:32:40 +01:00
Adam Harrison cf5835f081 Update README examples to use IPAM and DNS 2015-06-17 17:32:40 +01:00
Paul Bellamy aea1a2b08c have travis fetch any test-only dependencies before building tests 2015-06-17 17:00:36 +01:00
Paul Bellamy cdee7a5d6f Replacing custom testing helpers with testify, where possible 2015-06-17 16:58:03 +01:00
Paul Bellamy 21e07c0db9 Running gofmt -s 2015-06-17 15:32:08 +01:00
Paul Bellamy 06101d134b Running goimports 2015-06-17 15:32:08 +01:00
Matthias Radestock 501108e3ae cosmetic: vertical alignment 2015-06-17 15:13:07 +01:00
Matthias Radestock 87fe10ab5d Merge pull request #952 from paulbellamy/950-proxy-launch-router
Add support for WEAVE_CIDR=none

Fixes #950.
2015-06-17 14:57:35 +01:00
Paul Bellamy 6c26a55b5a [proxy] refactor weaveCIDRsFromConfig slightly 2015-06-17 14:51:12 +01:00
Paul Bellamy ca681bce13 [proxy] Add support for WEAVE_CIDR=none
Tells the proxy not to do anything with this container. We set it when
running weaveexec/weave/etc containers, so that the weave script works
through the proxy.
2015-06-17 14:51:12 +01:00
Bryan Boreham ff838a14d9 Fix terminology in error message.
Also removed hint on usage, because `-iprange` is on by default.
2015-06-17 13:16:53 +01:00
Matthias Radestock c45b586a44 Merge pull request #949 from paulbellamy/939-proxy-env-bug
remove spurious output from proxy-(env|config)

Fixes #939.
2015-06-17 12:10:09 +01:00
Matthias Radestock 215312bb5f Merge pull request #946 from bboreham/758-expand-ipam-docs
Expand description of -initpeercount in docs

Closes #758.
2015-06-17 12:06:04 +01:00
Bryan Boreham 34e0e3bb24 Merge pull request #935 from bboreham/weavedns-races
All unit and smoke tests passed.
2015-06-17 12:05:43 +01:00
Paul Bellamy b1f43f0d7c [proxy] Remove spurious output from proxy-(env|config)
`docker log` outputs an error if stdout is closed before it is done, so
silence that
2015-06-17 11:53:28 +01:00
Bryan Boreham 40efc347b6 Cosmetic 2015-06-17 11:41:46 +01:00
Matthias Radestock 71f19469ab Merge pull request #947 from paulbellamy/940-proxy-default-ipam
[proxy] --with-ipam -> --no-default-ipam

Closes #940.
2015-06-17 11:37:25 +01:00
Bryan Boreham 9636675c2c Split part of DNSServer.Start() to a separate ActivateAndServe()
Specifically, move the part that blocks, and call that as necessary.
This means Start() can run on the caller's goroutine and avoid race conditions.
2015-06-17 11:35:53 +01:00
Bryan Boreham 1b57a24789 Don't re-initialise log writers unless the type changed.
This avoids race-conditions in unit tests where the initialisation
is on a different goroutine to the logging.
2015-06-17 11:35:01 +01:00
Bryan Boreham 90fb201a51 Copy the DNS entry going into the cache, to avoid races.
The shuffleAnswers() call ends up acting on the same data,
so it seems simplest to resolve ownership arguments by copying.
2015-06-17 11:35:01 +01:00
Bryan Boreham 657133db50 Move the counting of results from DNS goroutine to test goroutine to avoid race-condition 2015-06-17 11:35:01 +01:00
Bryan Boreham e68364cb3b Don't overwrite recvChan; it just creates a race condition 2015-06-17 11:35:01 +01:00
Bryan Boreham 1f92ae6ba8 Swap the order in which two goroutines are started, since one relies on the other. 2015-06-17 11:35:01 +01:00
Bryan Boreham a6bebd85ed Remove 'running' member that triggers a race warning and isn't necessary. 2015-06-17 11:35:01 +01:00
Paul Bellamy 220c80c3ed [proxy] --with-ipam -> --no-default-ipam 2015-06-17 11:31:33 +01:00
Bryan Boreham 5960ecb3d1 Expand description of -initpeercount 2015-06-17 11:12:10 +01:00
Matthias Radestock c2778d9fa3 improve -neg-ttl usage message 2015-06-17 10:29:39 +01:00
Matthias Radestock cfe14ff568 Merge pull request #943 from inercia/weave-941
make negative TTL default to same value as TTL

Fixes #941.
2015-06-17 10:23:41 +01:00
Matthias Radestock cf5d8874b6 Merge pull request #937 from inercia/weave-934
Ignore empty queries (sent when the server is being shut down)

Fixes #934.
2015-06-17 10:10:18 +01:00
Alvaro Saurin d086867c0c make negative TTL default to same value as TTL 2015-06-17 10:17:12 +02:00
Matthias Radestock d09133c010 cosmetic: less sudoing 2015-06-17 08:30:35 +01:00
Matthias Radestock 6ac2b34a6e Merge pull request #936 from weaveworks/readme_update_2
Update wget to curl
2015-06-17 08:27:46 +01:00
Matthias Radestock 9276df62c2 document weave stop-proxy 2015-06-17 07:09:47 +01:00
Matthias Radestock d4c383b4b7 cosmetic: add weaveDNS TTL customisation to ToC 2015-06-17 07:05:09 +01:00
Matthias Radestock b10a4a2976 cosmetic 2015-06-17 06:59:20 +01:00
Matthias Radestock cfda9ffb8f add load balancing and faul resilience to weaveDNS docs ToC
This was missed in #775
2015-06-17 06:48:12 +01:00
Matthias Radestock ae5c2dab86 take advantage of recent features in proxy example 2015-06-16 23:01:14 +01:00
Matthias Radestock 0452076ebc remove outdated note 2015-06-16 22:58:30 +01:00
Matthias Radestock 7831460eff rely on ipam for launch-dns in proxy example 2015-06-16 22:51:57 +01:00
Matthias Radestock 20d5790358 drop -iprange from proxy docs, since ipam is enabled by default now 2015-06-16 22:51:10 +01:00
Matthias Radestock a52702f76e fix typo 2015-06-16 22:46:41 +01:00
Matthias Radestock 351573c122 simplify proxy launch examples
No need to specify --with-dns if DNS is running.

This should have been changed as part of #898.
2015-06-16 22:38:53 +01:00
Matthias Radestock 809b048abd fix sporadic 650_*.sh test failure caused by undefined variable
This is quite hilarious...

$BASE_IMAGE is not defined in config.sh. So running this test would
actually attempt to run something like

docker -H=... run -e WEAVE_CIDR=10.2.1.4/24  grep ^1$ /sys/class/net/ethwe/carrier

i.e. it would try to run the 'grep' image.

So why does this not fail all the time?

1) 610_*.sh defines $BASE_IMAGE.
2) 620_*.sh builds a 'grep' image.

If either of these tests are included in the same test run before
650_*.sh, then the latter will succeed.

Throw in our fabulous CircleCI scheduler, which distributes tests
according to their execution time, and you end up with sporadic
failures.

Fixes #938.
2015-06-16 21:13:35 +01:00
Alvaro Saurin c697d2a8f9 Ignore empty queries (sent when the server is being shut down) 2015-06-16 20:54:51 +02:00
Adam Harrison b8afefa55f Merge pull request #931 from paulbellamy/weavedns_default_search_domain
[dns] resolve names in `weave.local` by default unless hostname or search path is specified
2015-06-16 18:13:17 +01:00
Fintan Ryan 84bf3c139c Update wget to curl 2015-06-16 17:55:48 +01:00
Paul Bellamy 1bd343f9f0 Merge pull request #932 from bboreham/921-ipam-or-die
Kill the container we just started if we fail to allocate an IP address
2015-06-16 16:18:23 +01:00
Bryan Boreham fd5ac633d3 Use weave attach --or-die in proxy instead of killing it in the proxy. 2015-06-16 16:05:41 +01:00
Paul Bellamy a3e836bbaf Merge pull request #933 from inercia/weave-930
Use read/write-locks instead of read-locks in the Domain*Lookup()
2015-06-16 15:41:44 +01:00
Bryan Boreham ee4daf02b5 Introduce ipam_cidrs_or_die, and call it where we don't want the
container to survive an ipam failure.
2015-06-16 15:27:49 +01:00
Paul Bellamy a8c1832469 updating docs for new dns-search domain behaviour 2015-06-16 15:13:19 +01:00
Paul Bellamy 2c3baa9119 use weavedns search-domain by default
iff no hostname/dns-search specified
2015-06-16 15:13:19 +01:00
Alvaro Saurin f4507e7252 Change read-locks for read/write-locks 2015-06-16 16:02:49 +02:00
Bryan Boreham 32d3909023 Make ipam_cidrs() return failure when call_weave fails 2015-06-16 14:47:47 +01:00
Alvaro Saurin 8a935e3cb2 Launch WeaveDNS with "launch_dns_on" 2015-06-16 15:29:36 +02:00
Matthias Radestock 8b5d0e3fbd Merge pull request #898 from paulbellamy/weavedns_default_resolver
Weavedns default resolver

Fixes #383.
2015-06-16 14:17:14 +01:00
Paul Bellamy af38f07d26 refactoring proxy dns test 2015-06-16 14:08:35 +01:00
Adam Harrison b33d4497fa Fix test broken by new default behaviour 2015-06-16 14:07:42 +01:00
Adam Harrison 03b83811fa Update documentation 2015-06-16 14:07:42 +01:00
Adam Harrison e5b1cc21a9 Smoke test for new weave script DNS behaviour 2015-06-16 14:07:42 +01:00
Adam Harrison fdce84d9d9 New --with-dns/--without-dns behaviour in weave script 2015-06-16 14:07:42 +01:00
Paul Bellamy 8873d69b77 [proxy] Add --without-dns flag, and change meaning of --with-dns flag 2015-06-16 14:07:40 +01:00
Alvaro Saurin a1cb5b16a6 Run the unit tests with GOMAXPROCS=4 2015-06-16 14:58:53 +02:00
Alvaro Saurin 8621aa7cbf Launch WeaveDNS with --debug when DEBUG is set 2015-06-16 14:58:53 +02:00
Bryan Boreham 9a99a51e14 Merge pull request #925 from bboreham/922-remove-dot
Remove trailing dot from hostnames. Fixes #922.
2015-06-16 13:58:12 +01:00
Paul Bellamy bd0aff6a0f Merge pull request #929 from weaveworks/923-fix-deadlock
Fix deadlock in weavedns
2015-06-16 13:31:39 +01:00
Matthias Radestock 25dd60a149 Merge pull request #928 from awh/offset_tcp_nonce
Set high bit in TCP nonce to differentiate from UDP

Fixes #927.
2015-06-16 13:21:09 +01:00
Paul Bellamy 048261c921 Hold lock while accessing mutable variable, but release before sending on chan 2015-06-16 13:06:14 +01:00
Adam Harrison 9149366919 Set high bit in TCP nonce to differentiate from UDP 2015-06-16 12:21:18 +01:00
David Wragg 47d7c22073 Merge rade/871_version_header (PR #920): protocol versioning
Closes #871.
2015-06-16 12:20:40 +01:00
Bryan Boreham 7a106aeb5f Release lock before sending on a channel 2015-06-16 11:44:25 +01:00
Matthias Radestock 3124ffede5 more forward/backward compatible protocol versioning
We now first exchange a very minimal header, consisting of just the
name of the protocol ("weave") and the min and max version (encoded as
a byte each) we support. This leaves all other details of the
protocol, including the contents of of the handshake, open.

Peers agree to communicate using the maximum mutually supported
version. We record this in the LocalConnection, though atm nothing is
making use of that information.

We set a shorter than normal deadline (10s instead of 60s) for the
header exchange, so we detect version mismatches, garbage, or
non-responsive remotes quickly.

We reset the protocol version to '1' since this is a fresh start; old
weaves will not be able to parse the header.

Closes #871.
2015-06-16 11:00:41 +01:00
Bryan Boreham 79db00a90e Strip trailing period from hostname generated from weavedns domain.
We strip at the calling point rather than in the server, because DNS
servers do see the domain as ending with a '.'  representing the root.
2015-06-16 10:26:11 +01:00
Bryan Boreham dd9adf6a65 Don't add a trailing newline to the domain 2015-06-16 10:25:54 +01:00
Matthias Radestock 0206434f62 fix 130_expose_test breakage
introduced by 8ac98f9f6b
2015-06-15 22:25:22 +01:00
Matthias Radestock 657cbaefb9 refactor: simplify dns_args 2015-06-15 21:54:51 +01:00
Matthias Radestock ee61f07d9b refactor: extract rm_containers helper
cherry-picked from #898
2015-06-15 21:41:43 +01:00
Matthias Radestock cba1827d2c Merge pull request #918 from bboreham/916-check-call-weave
Output error message if http call to weave or weavedns fails

Fixes #916.
2015-06-15 21:16:40 +01:00
Matthias Radestock 3d7c18ee70 cosmetic: remove some unnecessary quoting 2015-06-15 21:02:23 +01:00
Matthias Radestock 82c1a58e81 refactor: remove unused weave dns-domain command
and inline dns_domain() function.

Both were introduced in #890.
2015-06-15 19:23:52 +01:00
Matthias Radestock 8ac98f9f6b tiny refactor: replace some 'if's with short-circuit expressions 2015-06-15 18:33:29 +01:00
Bryan Boreham 56e0636b14 Output error message if http call to weave or weavedns fails 2015-06-15 17:58:59 +01:00
Bryan Boreham 6534cc6ae3 Merge pull request #919 from bboreham/915-not-again-again
Fixes #915
2015-06-15 17:06:30 +01:00
Bryan Boreham d25d0810af Extend the 'block' flag to block allocateAgain()
otherwise we can re-insert the entry into the map after a parallel
allocateAgain() has cleared the flag. And it /that/ happens in
parallel with a free() then the data-structure can get out of sync.
2015-06-15 16:51:27 +01:00
Alvaro Saurin ba2f6671a6 Removed a couple of Debug.Printf()s 2015-06-15 17:11:27 +02:00
Alvaro 9284366a3d Merge pull request #917 from bboreham/inercia-flush-sched-queue
Return of the Fix for the zone refresh unit test
2015-06-15 15:45:57 +02:00
Bryan Boreham 7e6a478283 Merge pull request #914 from rade/911_different_gossip_channels
LGTM. Fixes #911
2015-06-15 14:42:22 +01:00
Matthias Radestock e15ebd9118 add unit test for SurrogateGossiper 2015-06-15 14:22:25 +01:00
Bryan Boreham 0ff311356d Flush the background updater goroutines to make tests reliable 2015-06-15 13:48:54 +01:00
Bryan Boreham e49a29197b Add a Flush() routine to synchronize callers 2015-06-15 13:48:24 +01:00
Bryan Boreham 2b7fb699af Clean up a couple of compilation errors 2015-06-15 13:47:01 +01:00
Bryan Boreham f3ad8bd52f Merge branch 'weave-fix-unittest' of https://github.com/inercia/weave into inercia-weave-fix-unittest 2015-06-15 11:51:14 +01:00
Matthias Radestock 36ba838f54 remove ipam.DummyAllocator and rely on new SurrogateGossiper
As a result it is possible to mix nodes with and without IPAM. Note
that the quorum only involves the former, and hence such mixed
environments will typically require a custom `-initpeercount`.
2015-06-15 11:24:07 +01:00
Matthias Radestock dea5bd0639 introduce SurrogateGossiper for dealing with unknown gossip channels
Instead of erroring when we receive gossip for an unknown channel, we
create a GossipChannel on the fly, with a SurrogateGossiper singleton
as the gossiper.

The SurrogateGossiper ignores unicasts and relays broadcasts and
gossip.
2015-06-15 11:23:32 +01:00
Matthias Radestock 0b3584a355 permit encoding of GossipData into multiple messages 2015-06-15 11:14:24 +01:00
Matthias Radestock 94220f22b2 allow dynamic addition of gossip channels 2015-06-15 11:14:24 +01:00
Matthias Radestock ec85da914a simplify dns-add/remove arg checking 2015-06-15 10:34:59 +01:00
Matthias Radestock bfebfecf6c refactor: simplify test router creation 2015-06-15 08:12:35 +01:00
Matthias Radestock e48ec6a35f clean up erroneous comment 2015-06-15 08:03:02 +01:00
Matthias Radestock a844797d67 cosmetic: reduce test noise 2015-06-14 17:48:21 +01:00
Matthias Radestock 72640f1828 refactor: extract helpers for http calls into weave and weavedns 2015-06-14 17:46:56 +01:00
Matthias Radestock 4ce6a76d1f remove outdated piece of docs 2015-06-14 16:36:37 +01:00
Matthias Radestock bf4c75b1cd don't validate -iprange value
We generally don't validate arguments in the script unless we process
them there.
2015-06-14 16:12:53 +01:00
Matthias Radestock 313a95a467 let -iprange "" start the router w/o IPAM 2015-06-14 16:06:20 +01:00
Matthias Radestock 926c85c6ba more idiomatic 'boolean' in shell 2015-06-14 15:48:08 +01:00
Matthias Radestock afb31d3beb cosmetic: remove unused method 2015-06-14 11:49:03 +01:00
Matthias Radestock c4c5ec1e92 refactor: add a little helper to improve abstraction 2015-06-14 11:09:55 +01:00
Matthias Radestock cea85eb05b cosmetic: move GossipChannel code into separate file 2015-06-14 10:35:51 +01:00
Matthias Radestock 3e0d0a1993 refactor: introduce NewGossipChannel 2015-06-14 10:09:36 +01:00
Matthias Radestock 882ed85d77 tiny refactor 2015-06-14 09:37:13 +01:00
Matthias Radestock eae9928e98 don't complain when weave --local run is invoked without args
leave that to docker, same as we do for `weave run`

Fixes #910.
2015-06-13 00:48:01 +01:00
Matthias Radestock 779d1589c5 Merge pull request #907 from bboreham/819-check-its-alive
Free allocated address if container is not running

Fixes #819.
2015-06-12 17:22:16 +01:00
Bryan Boreham 4c01af4e1d Check if container is still running right after allocating 2015-06-12 17:09:11 +01:00
Bryan Boreham 13944f3811 Refactor is-running check so we can re-use more of the code 2015-06-12 17:09:11 +01:00
Matthias Radestock 0f9f1feb87 Merge pull request #906 from awh/weavedns_restrict_http_listen
DNS HTTP API listens on eth0 only

Fixes #899.
2015-06-12 16:56:22 +01:00
Adam Harrison b6889ec8c3 DNS HTTP API listens on eth0 only 2015-06-12 16:55:27 +01:00
Bryan Boreham 5aafbbeff0 Fix compile error introduced in merge of #904 2015-06-12 16:20:35 +01:00
Matthias Radestock 7462fe91d5 Merge pull request #904 from bboreham/inercia-weave-821
Remove stale records for short-lived containers (bis)

Fixes #821. Replaces and hence closes #865.
2015-06-12 15:36:12 +01:00
Matthias Radestock 79c5343ca5 Merge pull request #905 from bboreham/slashten
Use /10 instead of /9 for default iprange

Patching up #893.
2015-06-12 15:21:38 +01:00
Matthias Radestock d5ebd6a0c4 Merge branch 'latest_release_doc_updates'
Conflicts:
	CHANGELOG.md
	nameserver/cache_test.go
	site/features.md
	site/proxy.md
	weave
2015-06-12 15:16:50 +01:00
Bryan Boreham 12de13c678 Use /10 instead of /9 for default iprange, so it doesn't clash with CircleCI address 2015-06-12 15:15:36 +01:00
Matthias Radestock 97d692df80 don't restart docker in test setup
due to that leaving container debris behind that docker takes ages to
clean up.
2015-06-12 15:01:35 +01:00
Bryan Boreham 7941612dad remove one level of indentation 2015-06-12 15:01:06 +01:00
Matthias Radestock c25706f6a5 Merge pull request #902 from bboreham/885-gossip-race
Keep flushing gossip until it's all gone

A better (i.e. working?) fix for #885.
2015-06-12 15:00:02 +01:00
Bryan Boreham 799e202d6d Make error message more readable 2015-06-12 14:52:20 +01:00
Bryan Boreham 658fbac7aa Keep flushing gossip until it's all gone
A better (i.e. working?) fix for #885.
2015-06-12 14:50:37 +01:00
Bryan Boreham 086daca3d0 Fold communication-checking code into docker.Client.NewClient() 2015-06-12 14:45:29 +01:00
Matthias Radestock f48f9ba475 relax documented weavedns subnet requirements somewhat
Following #763.
2015-06-12 14:22:57 +01:00
Bryan Boreham 29c988d42f Merge pull request #900 from rade/762_dns_ipam
LGTM.  Closes #762.
2015-06-12 14:21:07 +01:00
Matthias Radestock 06183bea1a extensive documentation 2015-06-12 14:01:34 +01:00
Matthias Radestock a2146ca5b0 add tests for launching DNS with various ways of obtaining an IP
As a side effect this also tests repopulation on restart, so we can
remove the separate test for that.
2015-06-12 13:50:22 +01:00
Matthias Radestock 727a213ba1 permit weavedns IP addresses to be choosen by IPAM
Same as for ordinary containers.

Closes #762.
2015-06-12 13:50:22 +01:00
Matthias Radestock 38266f2918 fix layout 2015-06-12 13:35:12 +01:00
Matthias Radestock 6d4c1e49fe fix layout 2015-06-12 13:33:32 +01:00
Matthias Radestock fa00b9faf3 add ToC to ipam docs
also shorten some of the headings, and use H2 instead of H3
2015-06-12 12:24:32 +01:00
Matthias Radestock 685d7d2559 Merge pull request #893 from bboreham/743-default-iprange-in-script
Default value for -iprange

Closes #743. Closes #870.
2015-06-12 11:17:12 +01:00
Matthias Radestock df918ab3c4 revamp ipam docs 2015-06-11 22:24:07 +01:00
Bryan Boreham 404ae010e4 Improve flow of IPAM doc 2015-06-11 18:35:18 +01:00
Bryan Boreham 43d42e6f15 Update docs for default -iprange 2015-06-11 18:01:28 +01:00
Bryan Boreham 328e63828e Check whether netcheck exists before relying on it 2015-06-11 17:55:27 +01:00
Bryan Boreham 465b16e668 Give -iprange a default, and check if it is free 2015-06-11 17:34:43 +01:00
Bryan Boreham 0e6569d78c New program netcheck to check if network overlaps routes 2015-06-11 17:27:20 +01:00
Bryan Boreham 8285046962 Function to check if a network overlaps with existing routes 2015-06-11 17:27:20 +01:00
Matthias Radestock 8e04b135bb check that nsenter is present
for `weave --local ...`

Closes #895.
2015-06-11 16:37:28 +01:00
Matthias Radestock 2b7c785386 don't output container id when we kill it
This was missed from #831.
2015-06-11 16:34:46 +01:00
Paul Bellamy 09f491821b making test executable 2015-06-11 13:51:41 +01:00
Adam Harrison c0cfacdb28 Repatriate itinerant smoke test 2015-06-11 12:54:07 +01:00
Matthias Radestock e96cde1872 Merge pull request #890 from awh/use_container_name_as_hostname
Use container name as hostname

Closes #750.
2015-06-11 12:35:46 +01:00
Adam Harrison 12fc760e4a Refocus documentation around --name 2015-06-11 12:17:05 +01:00
Paul Bellamy d2b17690f9 use dnsutils image in one test instead of awk on all the tests. 2015-06-11 12:13:24 +01:00
Adam Harrison 8d7fe2b9e7 Renamed smoke test to avoid number clash 2015-06-11 11:55:21 +01:00
Adam Harrison 1fa7c24a2a Prefer --name to -h in examples 2015-06-11 11:54:05 +01:00
Adam Harrison 6b80dc035a Simplify smoke test 2015-06-11 11:53:24 +01:00
Adam Harrison 35cfafc319 Defer evaluation of $(dns_domain) until necessary 2015-06-11 11:52:33 +01:00
Matthias Radestock e1c07dce3f permit overriding of DOCKERHUB_USER
which in turn enables operation from private registries

Closes #887.
2015-06-11 11:50:55 +01:00
Paul Bellamy 96bda1639e define weavedns http port as a constant 2015-06-11 10:27:35 +01:00
Adam Harrison a424bfe715 Correctly interpret long form arguments that lack an equals symbol 2015-06-11 10:27:34 +01:00
Adam Harrison 4f6ace8b10 Permit optional trailing dot on hostname 2015-06-11 10:27:34 +01:00
Adam Harrison bccb5065db Update docs with new weave run behaviour 2015-06-11 10:27:34 +01:00
Adam Harrison bed6a130d6 Smoke test for new weave run behaviour 2015-06-11 10:27:34 +01:00
Adam Harrison 108eea8b79 Modify weave run to use container name as fqdn
When run is invoked with `--with-dns` and a container name but no
hostname, synthesise a fully qualified hostname from the container name
and the weaveDNS domain and supply that as a hostname for the container.
2015-06-11 10:27:34 +01:00
Paul Bellamy 678063efec updating proxy docs on hostname/domainname dns 2015-06-11 10:27:34 +01:00
Paul Bellamy 99d9ad7042 [proxy] query weavedns for domain 2015-06-11 10:27:34 +01:00
Paul Bellamy b9850ac200 [proxy] Set hostname and domainname based on container name, when dns
Had to make the assert_dns_record test helper more robust against
'getent hosts' returning duplicates
2015-06-11 10:27:29 +01:00
Adam Harrison 9fddad02ad Add dns_domain helper to weave script 2015-06-11 10:26:07 +01:00
Adam Harrison a1007411ad Add /domain resource to DNS REST API 2015-06-11 10:26:07 +01:00
Matthias Radestock 9955de3e9b cosmetic: shorten usage info 2015-06-11 09:23:58 +01:00
Alvaro Saurin 2eb39d962a Use the scheduled queue for refreshing names
Some minor time tweaks...
2015-06-10 22:56:40 +02:00
Alvaro Saurin d6a9fc57cc New scheduled queue, for scheduling calls to callables 2015-06-10 22:56:38 +02:00
Alvaro Saurin 698d352322 Check with Docker whether the container is really running 2015-06-10 22:27:49 +02:00
Alvaro Saurin 1889a4a87f Simplify the arguments passed to nameserver/http.go:ServeHTTP() (by using information found in some other objects we are already passing, like the server) 2015-06-10 22:27:49 +02:00
Alvaro Saurin 5cb2347f77 Use our own Docker client 2015-06-10 22:27:47 +02:00
David Wragg 5ce4b25f38 Merge rade/746_simplify_frag_test (PR #888): simplify fragmentation test
Closes #746.
2015-06-10 18:28:26 +01:00
Matthias Radestock 267716fed5 Merge pull request #827 from bboreham/675-ipam-subnets
multi-subnet ipam

Closes #675. Fixes #861. Closes #862.
2015-06-10 17:44:17 +01:00
Bryan Boreham a89b549c40 Change command-line syntax to net: prefix instead of --subnet 2015-06-10 17:28:06 +01:00
Bryan Boreham 91ad3ef684 Illustrate mix-and-match of ipam and non-ipam CIDRs on command-line 2015-06-10 17:27:07 +01:00
Bryan Boreham 36f793c1f4 Delete entry from addresses map when slice is empty 2015-06-10 17:27:07 +01:00
Bryan Boreham c27cd5b4ab Extract lookup function for owned addresses 2015-06-10 17:27:07 +01:00
Bryan Boreham 7d881a0f0c Check that weave ps returns exactly the CIDRs we are looking for, and comment assumption 2015-06-10 17:27:07 +01:00
Bryan Boreham 487bd2eaf7 Use zero-cidr-args form of 'weave detach' to test #862 2015-06-10 17:24:36 +01:00
Bryan Boreham e9d194327a Reduce level of noise in debug logs from containers dying 2015-06-10 17:24:36 +01:00
Bryan Boreham c301e6a380 Expand test to use IPAM and check addresses are freed 2015-06-10 17:24:36 +01:00
Bryan Boreham a3d73be8b8 General tidying
tidy Address helper functions,
make ordering of members consistent in allocate and claim,
make linter happier,
remove some unused items, and
update some comments
2015-06-10 17:24:36 +01:00
Bryan Boreham fa0b0042b2 Eradicate the last vestiges of CIDR in Allocator 2015-06-10 17:24:36 +01:00
Bryan Boreham e21f66db0a Use address.Range instead of (start,end) pairs 2015-06-10 17:24:36 +01:00
Bryan Boreham 1e71d2dacb Introduce new '<address_spec>' type in usage text 2015-06-10 17:24:36 +01:00
Bryan Boreham a80e5cb028 Corrected space.Donate() to restrict the space donated to be in-range, and associated unit test changes 2015-06-10 17:24:36 +01:00
Bryan Boreham 40c9adc8d3 Extract common freelist-walking code 2015-06-10 17:24:36 +01:00
Bryan Boreham 147962d101 Clarify some unit test operations 2015-06-10 17:24:36 +01:00
Bryan Boreham 6536034ab6 Move default subnet outside of Allocator 2015-06-10 17:24:36 +01:00
Bryan Boreham 7e7a2227a0 Document multi-subnet IPAM 2015-06-10 17:24:36 +01:00
Bryan Boreham e5113889f4 Move NumFreeAddresses() into test 2015-06-10 17:24:36 +01:00
Bryan Boreham 3cdc3522be Don't overwrite CIDR_ARGS; write new variables ALL_CIDRS and IPAM_CIDRS
Handle manual CIDRs and IPAM CIDRs separately, to avoid inconsistencies and misleading messages
2015-06-10 17:24:35 +01:00
Bryan Boreham 2b4eec39cf Support '--subnet default' 2015-06-10 17:21:16 +01:00
Bryan Boreham 3a9af4413d Rationalize ipam arguments
- rename -defaultsubnet to -ipsubnet
- default subnet defaults to whole -iprange
- parse and check inside ipam package
- removes some error cases
2015-06-10 17:21:16 +01:00
Bryan Boreham f956a76455 Tighten error checking in weave script
- --subnet is used but IPAM is off
 - arg that comes after --subnet is not a cidr
2015-06-10 17:21:16 +01:00
Bryan Boreham ecbd514a83 Tighten error checking 2015-06-10 17:21:16 +01:00
Bryan Boreham 50eaccc60c Allocate() and Lookup() now return a CIDR, to simplify output 2015-06-10 17:21:16 +01:00
Bryan Boreham 2a00b0092d Extend 'Lookup' to multi-subnet 2015-06-10 17:21:16 +01:00
Bryan Boreham d18ccac43b Free IPAM addresses individually in 'weave hide', because the user now has the ability to free some subnets and not others 2015-06-10 17:21:16 +01:00
Bryan Boreham a617e7918c Introduce single-address Free() and rename the previous one to Delete() 2015-06-10 17:21:16 +01:00
Bryan Boreham d20d2e9142 Update smoke tests that use IPAM with default subnet 2015-06-10 17:21:16 +01:00
Bryan Boreham 551394a831 Add subnet info to space requests from one peer to another 2015-06-10 17:21:16 +01:00
Bryan Boreham 0613a06653 IPAM: handle multiple CIDRs in weave script 2015-06-10 17:21:16 +01:00
Bryan Boreham ff545feae3 Add some unit tests for multi-subnet 2015-06-10 17:21:16 +01:00
Bryan Boreham c7a9dfbb3f Remember multiple addresses owned by each container, for multi-subnet use 2015-06-10 17:21:16 +01:00
Bryan Boreham 2553850ed4 Initial implementation of multi-subnet IPAM 2015-06-10 17:21:16 +01:00
Bryan Boreham e6d230042d Introduce address.CIDR type 2015-06-10 17:21:16 +01:00
Bryan Boreham ae8985deb5 Merge pull request #884 from rade/881_get_container_id_on_start
LGTM. Fixes #881
2015-06-10 17:20:25 +01:00
Matthias Radestock 18bc3c2b99 "online dictionary attacks" is better terminology 2015-06-10 16:40:25 +01:00
Matthias Radestock afd0275042 Merge pull request #848 from paulbellamy/proxy-env
add proxy-env and proxy-config

Closes #753.
2015-06-10 15:34:32 +01:00
Paul Bellamy 2d5e362115 [proxy] use logs for getting proxy-env and proxy-config
It's ugly, but beats arg-parsing, inspecting the container and piecing
the address together from exposed ports.

We could just use the host from DOCKER_HOST, but that will fail in the
case of a host with multiple IPs, where the proxy only listens on one.
2015-06-10 15:24:53 +01:00
Matthias Radestock 7a2b5c43e7 feed full container id on weave start to DNS/IPAM
We erroneously assumed that `docker start` returned the full container
id. It doesn't and as a result we populated DNS with records with ids
other than full container ids. And that's also what we told IPAM. As a
result, DNS and IPAM didn't clean up after the container's death,
resulting in stale DNS entries and leaked IPs.

Fixes #881.
2015-06-10 14:53:43 +01:00
Matthias Radestock 0ab7c31bbe Merge pull request #886 from bboreham/885-gossip-race
Avoid gossip test race conditions

Fixes #885.
2015-06-10 13:50:40 +01:00
Bryan Boreham 0cd4127087 Factor out GossipSender.flush() 2015-06-10 12:24:19 +01:00
Bryan Boreham 5ebfc9027b Flush gossip inside the channel lock so we know sender hasn't been Stop()-ed 2015-06-10 12:22:38 +01:00
Matthias Radestock fe6d05fccc Merge pull request #875 from paulbellamy/proxy-port
Add support for custom proxy bind address

Closes #805. Closes #810.
2015-06-10 12:19:41 +01:00
Bryan Boreham 35b3a2d6a6 Inline GossipSender.flush() and don't try to send nil 2015-06-10 12:18:09 +01:00
Paul Bellamy 6bb087cecd [proxy] Renaming flags: -H -> -D, -L -> -H
As per discussion in issue 875
2015-06-10 12:13:52 +01:00
Paul Bellamy 447a00ba7e [proxy] Add support for tcp:// prefix on listen addresses 2015-06-10 12:13:52 +01:00
Paul Bellamy 0da9669e5c [proxy] removing unneeded port expose 2015-06-10 12:13:51 +01:00
Paul Bellamy 4a7d4d3555 [proxy] documenting -L option 2015-06-10 12:13:51 +01:00
Paul Bellamy 1ac5d4fe98 renaming dns_restart_test so it is picked up by test runner 2015-06-10 11:52:58 +01:00
Bryan Boreham bd5353024f Helpful suggestions from the linter 2015-06-10 11:05:04 +01:00
Bryan Boreham 569fd784fa Make flushing of GossipSender wait till done in tests,
and take extra locks to avoid race conditions.
Also removed the sendPendingGossip() from mockChannelConnection.SendProtocolMsg(),
because waiting in flush now creates a deadlock.  Tests will have to
call sendPendingGossip() explicitly if they need it.
2015-06-10 10:59:54 +01:00
Matthias Radestock fbf2208e80 simplify fragmentation test
This is how fragmentation tests used to happen:

    A: conn.stackFrag = false
    A->B: StartFragmentationTest
    B->A: send large non-DF UDP packet
    A->B: FragmentationReceived
    B: conn.stackFrag = true

conn.stackFrag = true means "UDP packets sent to the other peer get
correctly fragmented and re-assembled".

Note how A clears its own stackFrag flag, but the test establishes
whether fragmentation works for B->A, and ends up setting stackFrag on
B. Not only is this weird, it also means that if A previously had
stackFrag=true, it will now start fragmenting packets until B
initiates a fragmentation test.

The new sequence is:

    A: conn.stackFrag = false
    A->B: send large non-DF UDP packet
    B->A: FragmentationReceived
    A: conn.stackFrag = true

Closes #746.
2015-06-10 07:41:37 +01:00
Matthias Radestock bd364f787e fix silent failure in assert_dns_record test
If the name cannot be resolved, getent fails, which in turn causes
assert_dns_record to just return with a non-zero status at that point,
which causes the whole test to just terminate with a non-zero status,
but no indication whatsoever what had gone wrong.
2015-06-09 19:08:59 +01:00
Matthias Radestock ca133d6d9b Call LocalPeer.Encode, not Peer.Encode!
We need the former's locking.

Fixes #883.
2015-06-09 17:19:36 +01:00
Matthias Radestock 94f18f7004 remove outdated comment 2015-06-09 15:52:15 +01:00
Paul Bellamy e324c15b04 Merge pull request #879 from rade/proxy_interceptor_refactor
Proxy interceptor refactor
2015-06-09 14:50:54 +01:00
Matthias Radestock 5c70c7c71a simplify interceptor structs by referencing proxy 2015-06-09 14:45:50 +01:00
Matthias Radestock 020a9ea396 rename file 2015-06-09 14:45:50 +01:00
Matthias Radestock 3f9c7a5c00 remove pointless indirection 2015-06-09 14:45:50 +01:00
Matthias Radestock f025b6ea36 refactor interceptor invocation 2015-06-09 14:45:42 +01:00
Paul Bellamy 03fa95fcb5 Merge pull request #878 from rade/877_remove_proxy_status
remove most of proxy status
2015-06-09 14:24:53 +01:00
Matthias Radestock fee2898c05 remove detailed proxy status
There are two problems with obtaining the proxy status via the normal
proxy endpoint...

1. when TLS is enabled, this requires using TLS at the client end

2. the status URL is mixed in with the docker remote API, which is
rather unclean

There is limited value to having some status output from the proxy,
since all the output is constant.

So instead we log the version and command line args, and in `weave
status` only report whether the proxy is running.

We still want a way to wait for the proxy to start up, and log a nice
error if it dies. We do this by polling the docker logs for a startup
message.

Fixes #877. Fixes #876.

Undoes most of #756, #800, #854.
2015-06-09 14:03:52 +01:00
Matthias Radestock 2d70197221 cosmetic: more formal command grammar 2015-06-08 23:57:53 +01:00
Matthias Radestock f9c325c793 refactor: more sensible field order 2015-06-08 21:57:49 +01:00
Matthias Radestock a4b323c5df mention docker logs weavedns for debugging 2015-06-08 18:39:26 +01:00
Fintan Ryan 365edac5d5 Update strapline 2015-06-08 18:37:51 +01:00
Adam Harrison 89cf5939b8 Merge branch '0.11.2' into latest_release_doc_updates 2015-06-08 17:59:55 +01:00
Adam Harrison 93c1e755e6 Add release 0.11.2 to CHANGELOG 2015-06-08 16:31:24 +01:00
David Kaltschmidt b7f69c77f1 [site] header markup needs change after refactor
due to change in stylesheet in website repo

this change does not affect current deployment, but has to be deployed
when the website is deployed again
2015-06-08 15:09:04 +01:00
Ilya Dmitrichenko 235acc16f6 Merge pull request #874 from weaveworks/header-markup-refactor
[site] header markup needs change after refactor
2015-06-08 15:07:08 +01:00
Bryan Boreham 011c86fdca Merge pull request #852 from rade/816_make_launch_args_order_insensitive
LGTM. Fixes #816
2015-06-08 14:34:46 +01:00
Alvaro Saurin 5f4756aa80 Do not overwrite negLocal entries in the cache
Unit tests
2015-06-08 13:53:24 +01:00
Ilya Dmitrichenko 106f1c4434 Respect DOCKER_CLIENT_ARGS in weave run (close #855) 2015-06-08 13:53:24 +01:00
Alvaro Saurin bb3dd55a10 Merge branch '0.11'
Conflicts:
	nameserver/cache_test.go
2015-06-08 13:41:38 +02:00
Matthias Radestock 3f274d02ce make weave launch args insensitive to order
Fixes #816.
2015-06-08 12:41:26 +01:00
David Kaltschmidt f65e02477e [site] header markup needs change after refactor
due to change in stylesheet in website repo

this change does not affect current deployment, but has to be deployed
when the website is deployed again
2015-06-08 13:19:58 +02:00
Matthias Radestock 8a5725fa74 correct docs for proxy's -H 2015-06-08 10:26:21 +01:00
Adam Harrison 0df28f58df Merge pull request #849 from inercia/weave-fix-neg-ttl
Do not overwrite negLocal entries in the cache
2015-06-08 09:59:32 +01:00
Matthias Radestock 9d707b9670 add proxy troubleshooting docs
Closes #854.
2015-06-07 13:32:02 +01:00
Matthias Radestock ad92bbc8c8 get rid of proxy 'Advantages' section
instead point out the main purpose right at the top, and list further
benefits in the 'Usage'.

Also, expand docs on configuring the listener.
2015-06-06 23:57:20 +01:00
Matthias Radestock e1ba892827 go back to using the tcp:// prefix in DOCKER_HOST
seems to be the recommended way
2015-06-06 23:54:46 +01:00
Matthias Radestock 4012b3c011 raise profile of proxy 2015-06-06 23:20:56 +01:00
Matthias Radestock 897f625db9 substantial reworking of the proxy TLS docs 2015-06-06 23:08:08 +01:00
Matthias Radestock 33c03e00e4 rewrite proxy dns docs, and add a complete multi-host example 2015-06-06 21:34:26 +01:00
Matthias Radestock 03ede29e7e ditch proxy 'Limitations' section 2015-06-06 21:22:09 +01:00
Matthias Radestock 4ea451378e cosmetic 2015-06-06 21:18:57 +01:00
Matthias Radestock 542f966dcd typo 2015-06-06 21:14:44 +01:00
Matthias Radestock 198b7db463 simplify examples - don't specify a command on container start 2015-06-06 21:13:46 +01:00
Matthias Radestock b22fbd3734 expand proxy setup documentation 2015-06-06 21:11:39 +01:00
Matthias Radestock 06716ba132 rewrite proxy ipam docs 2015-06-06 16:57:50 +01:00
Matthias Radestock 52509087a7 cosmetic 2015-06-06 16:19:44 +01:00
Matthias Radestock 7e88302d01 mention docker logs weavedns for debugging 2015-06-06 16:17:24 +01:00
David Kaltschmidt 6bf0feeffc Wrapping content area to allow for sticky footer 2015-06-05 16:46:29 +01:00
Ilya Dmitrichenko e7f02c954c Merge pull request #866 from weaveworks/fix-doc-layout
Fix doc layout
2015-06-05 16:44:18 +01:00
David Kaltschmidt cdcd37e15a Wrapping content area to allow for sticky footer 2015-06-05 17:36:30 +02:00
Alvaro Saurin 8b5df952c9 Do not overwrite negLocal entries in the cache
Unit tests
2015-06-05 16:31:57 +02:00
Paul Bellamy 525b7c8eeb Merge pull request #853 from rade/847_option_to_disable_peer_discovery
introduce '-nodiscovery' option to disable automatic peer discovery
2015-06-05 15:31:51 +01:00
Matthias Radestock 8f85a07ecc introduce '-nodiscovery' option to disable automatic peer discovery
Closes #847.
2015-06-05 15:13:48 +01:00
Paul Bellamy 5f5c0079d3 Merge pull request #863 from rade/onoff
introduce OnOff helper, for consistency in status output
2015-06-05 15:05:24 +01:00
Matthias Radestock c10f1dacad introduce OnOff helper, for consistency in status output 2015-06-05 15:00:02 +01:00
Michael Bridgen 1c14660ed5 Merge pull request #859 from weaveworks/website_39_again
Update strapline
2015-06-05 14:51:30 +01:00
Matthias Radestock 87e30d8382 Merge pull request #815 from paulbellamy/proxy-ssl
Proxy TLS Support

Closes #506.
2015-06-05 14:48:54 +01:00
Paul Bellamy edeb531940 [docs] rewording proxy TLS docs 2015-06-05 14:35:00 +01:00
Paul Bellamy 14cc33c512 reordering args to launch-proxy docker command 2015-06-05 14:10:38 +01:00
Paul Bellamy bddf79855d [proxy] refactor tls arg parsing 2015-06-05 13:59:30 +01:00
Fintan Ryan 124d98246b Update strapline 2015-06-05 13:51:03 +01:00
Paul Bellamy 127cffba6f [proxy] add TLS to /status 2015-06-05 13:38:55 +01:00
Matthias Radestock 3e8813e559 Merge pull request #858 from weaveworks/0.11
Respect `DOCKER_CLIENT_ARGS` in `weave run`
2015-06-05 13:36:27 +01:00
Matthias Radestock 4fc0671d0d Merge pull request #857 from weaveworks/weave-run-client-args-855
Respect `DOCKER_CLIENT_ARGS` in `weave run` (close #855)
2015-06-05 13:28:20 +01:00
Ilya Dmitrichenko e1537000c2 Respect DOCKER_CLIENT_ARGS in weave run (close #855) 2015-06-05 13:24:05 +01:00
Paul Bellamy b8595b9062 [proxy] cleaner default args 2015-06-05 13:23:15 +01:00
Paul Bellamy fa6707effa [docs] adding paragraph about docker listening on unix socket for proxy 2015-06-05 13:23:15 +01:00
Paul Bellamy a1ffbb528c adding documentation on using the proxy with tls 2015-06-05 13:23:14 +01:00
Paul Bellamy c0cf9e509c [proxy] Added proxy.Config struct for initialization 2015-06-05 13:23:14 +01:00
Paul Bellamy c31358259f [proxy] Add tls support
Caveats:
* TLS files must be present on the target server (and readable)
* TLS file paths cannot contain spaces
* TLS file paths must be absolute (i.e. cannot be relative)
* Bash argument parsing is terrible
2015-06-05 13:23:14 +01:00
Matthias Radestock 8514172e57 explain meaning of 'Encryption' line in weave status output 2015-06-05 11:27:38 +01:00
Matthias Radestock 3a0a8ba939 cosmetic 2015-06-05 10:31:34 +01:00
Matthias Radestock 16410d6014 refactor: make encryption status part of router status 2015-06-05 10:05:17 +01:00
Matthias Radestock 34e3f0ba3f refactor: simplify topology gossip gob decoder initialisation
Should be more efficient too due to less copying.
2015-06-05 07:39:34 +01:00
Matthias Radestock 62ee70f388 recommend strong password
Closes #641.
2015-06-04 15:19:47 +01:00
Matthias Radestock 441e58e422 excise reference to crypto implementation from main docs
It's too scary for the uninitiated.
2015-06-04 15:19:37 +01:00
Paul Bellamy c917b261f7 Capitalizing and standardizing titles of pages in the docs.
Also re-wording to make them match the page title.
2015-06-04 15:19:26 +01:00
Paul Bellamy d3daaf32f9 [docs] re-ordering proxy doc sections 2015-06-04 15:19:13 +01:00
Bryan Boreham 62bbaf57a2 Merge pull request #839 from rade/837_tcp_accept_delay
LGTM. Closes #837
2015-06-04 14:29:58 +01:00
Bryan Boreham 7acb4be13f Merge pull request #844 from rade/641_recommend_strong_password
LGTM. Closes #641
2015-06-04 14:28:22 +01:00
Matthias Radestock 044992eea0 limit acceptance rate on peer connections
The main purpose of this is to set an upper bound on the rate at which
an attacker might execute a brute force attack on the weave password.

No need to get clever or precise here.

Closes #837.
2015-06-04 14:21:35 +01:00
Bryan Boreham 2a244e5699 Simplify paxos.Consensus() call 2015-06-04 13:30:15 +01:00
Fintan Ryan bee5c300db Docs look and feel update 2015-06-04 12:36:13 +01:00
Matthias Radestock e31233c473 recommend strong password
Closes #641.
2015-06-03 18:15:32 +01:00
Matthias Radestock 720fdf714c excise reference to crypto implementation from main docs
It's too scary for the uninitiated.
2015-06-03 18:10:38 +01:00
Paul Bellamy 6f9c0d6cde Merge pull request #834 from weaveworks/docs_lookandfeel
Docs look and feel update
2015-06-03 17:29:29 +01:00
Paul Bellamy a7782359b1 Merge pull request #838 from paulbellamy/docs-capitals
Capitalizing and standardizing titles of pages in the docs.
2015-06-03 16:29:37 +01:00
Fintan Ryan e96a22bd6d Docs look and feel update 2015-06-03 16:04:01 +01:00
Paul Bellamy a957ed253c Capitalizing and standardizing titles of pages in the docs.
Also re-wording to make them match the page title.
2015-06-03 15:59:40 +01:00
Matthias Radestock dc8a82e913 cosmetic: consistency in launchers 2015-06-03 14:22:20 +01:00
Bryan Boreham c1f8b62e11 Merge pull request #832 from rade/412_min_backoff
LGTM. Fixes #412
2015-06-03 12:47:07 +01:00
Paul Bellamy c91e699a27 Merge pull request #831 from rade/192_kill_container_on_network_config_error
kill container on network config error
2015-06-03 12:45:23 +01:00
Adam Harrison 4a8850afb3 Fix comment 2015-06-03 12:08:13 +01:00
Matthias Radestock d1c41a97b1 encapsulate retry delay calculation 2015-06-03 12:04:33 +01:00
Matthias Radestock fe5102580d fix connection retry delay calculation logic
The delay d(n) for the nth retry was
d(1) = rand(initial_interval)
d(n+1) = d(n) + rand(d(n))

There are two problem with this:

1) The entire sequence is dominated by the random value for d(1). If
this is a small value, lots of retries at short interval ensue. In
extremis, if d(1) is 0, indefinite immediate retries would ensue.

2) The delay is not a true exponential backoff; the upper bound only
increases exponentially *probabilistically*.

The new algorithm calculates

d(n) = random_value in range [i-i/2,i+i/2],
       where i = initial_interval * 1.5^(n-1).

Thus d(1) is at least initial_interval/2, and successive values are
independent of previous values, forming a true exponential sequence of
intervals bounds between which the retry delay is chosen randomly.

Due to the change in algorithm we reduce...

- the initial interval from 5s to 2s. Recall that previously d(1) was
  in the range [0,initial_interval=5s]. It is now [2-2/2=1s,2+2/2=3s].

- the max interval from 10 minutes to 6. Previously, once that was
  reached, it would stay exactly there. It is now randomly chosen in
  the range [6-6/2=3m,6+6/2=9min].

Thus previously delays would range from 0 to 10 minutes, now they
range from 1s to 9 minutes.

Fixes #412.
2015-06-03 11:29:55 +01:00
Matthias Radestock d909a86fb0 Merge pull request #828 from rade/666_no_timeout_on_wait_for_status
no timeout on wait for status

Fixes #666.
2015-06-03 11:20:37 +01:00
Matthias Radestock 4e68bde7b5 don't time out waiting for weave infrastructure containers to start
instead keep trying, until we succeed or the container dies

Fixes #666.
2015-06-03 11:19:51 +01:00
Matthias Radestock 52f31f0596 Merge pull request #830 from rade/829_fix_container_death_check
kill -0 $CONTAINER_PID does not work inside weaveexec container

Fixes #829.
2015-06-03 11:17:23 +01:00
Matthias Radestock fa8b77b060 kill -0 $CONTAINER_PID does not work inside weaveexec container
since $CONTAINER_PID is in a different process namespace.

Fixes #829.
2015-06-03 11:16:08 +01:00
Matthias Radestock 83fe4e59f6 kill container on network config error
but only if we have just started it; killing other containers, e.g. on
`weave attach`, would be rather impolite.

Closes #192.
2015-06-02 20:26:20 +01:00
Matthias Radestock 8de1ddd255 cosmetic 2015-06-02 20:21:56 +01:00
Matthias Radestock 395aea28c6 cosmetic 2015-06-02 20:16:25 +01:00
Matthias Radestock 5e7d1cd0eb Merge remote-tracking branch 'upstream/master' 2015-06-02 18:46:30 +01:00
Michael Bridgen 3344e45a9f Merge pull request #825 from squaremo/discover_ipam_ip
Allow lookup of IPAM-allocated addresses
2015-06-02 18:04:11 +01:00
Matthias Radestock d020999e82 cosmetic: more sensible function order 2015-06-02 17:52:39 +01:00
Matthias Radestock d105d255ca Merge pull request #824 from paulbellamy/weavewait-race-condition
[proxy] We can only rely on receiving a signal when we are a container

This got broken in #820.
2015-06-02 17:39:28 +01:00
Michael Bridgen 791584d9f3 Allow lookup of IPAM-allocated addresses 2015-06-02 17:31:25 +01:00
Matthias Radestock 48b54ef722 cosmetic: consistency 2015-06-02 17:25:56 +01:00
Paul Bellamy e2447721eb [proxy] We can only rely on receiving a signal when we are a container
Due to https://github.com/docker/docker/issues/9098, there is no way to
signal an exec. Instead we'll have to just poll the interface and hope.

Adds `weavewait -s` which is the way to tell weavewait to skip waiting
for a signal, which we use on execs
2015-06-02 17:21:41 +01:00
Bryan Boreham 89074715bc Merge pull request #823 from bboreham/818-claim-no-trunc
Fixes #818
2015-06-02 16:12:47 +01:00
Bryan Boreham 4968f0f3c4 Merge pull request #822 from bboreham/802-test-race
Fix race condition in allocator fuzz-test - fixes #802
2015-06-02 16:12:22 +01:00
Bryan Boreham aa7c7a9a9d Add --no-trunc flag to docker ps for ipam_claim 2015-06-02 16:07:48 +01:00
Bryan Boreham 6a1b658f59 Fix race condition in allocator fuzz-test
It was possible to free an address between deciding to re-test it
and actually re-testing it.  So introduced a flag 'block' to prevent
an address from being freed during the re-test.
2015-06-02 16:04:35 +01:00
Matthias Radestock e2805c01f4 cosmetic: remove stray trailing whitespace 2015-06-02 15:57:50 +01:00
Matthias Radestock 04c10233f7 Merge pull request #820 from paulbellamy/weavewait-race-condition
Use USR2 signal to tell weavewait when to continue

Fixes #812.
2015-06-02 15:52:22 +01:00
Paul Bellamy 92444a75eb [proxy] Use USR2 signal to tell weavewait when to continue
There was a race condition with containers running and exiting before
`weave attach` was finished, which would cause an error.
2015-06-02 15:48:49 +01:00
Paul Bellamy cb92e6b0d2 Merge pull request #817 from rade/420_move_exe_image_to_subdir
move exe and docker image producing dirs to subdir
2015-06-02 14:18:49 +01:00
Matthias Radestock 5b04efac7c move weaver 2015-06-02 14:04:02 +01:00
Matthias Radestock 485db6e48b move weavedns 2015-06-02 13:51:19 +01:00
Matthias Radestock ee28cf91e1 move weaveexec 2015-06-02 13:46:02 +01:00
Matthias Radestock 8d4a97fed5 move sigproxy, weaveproxy, weavewait 2015-06-02 13:19:16 +01:00
Matthias Radestock 46ef954057 cosmetic: rename: trains station 2015-06-02 12:58:09 +01:00
Paul Bellamy f28fe80861 Merge pull request #814 from weaveworks/revert-813-proxy-ssl
Revert "First pass at Proxy SSL Support"
2015-06-02 12:49:08 +01:00
Paul Bellamy 91375a1095 Revert "First pass at Proxy SSL Support" 2015-06-02 12:48:38 +01:00
Paul Bellamy 3586ddcd63 Merge pull request #813 from paulbellamy/proxy-ssl
First pass at Proxy SSL Support
2015-06-02 12:48:31 +01:00
Matthias Radestock fca02eb489 Merge pull request #706 from awh/dns_delete_individual_records
delete individual dns records

Closes #631. Closes #654.
2015-06-02 12:35:50 +01:00
Adam Harrison 56160e0798 Merge pull request #728 from paulbellamy/test-runner
allow test/run_all.sh to run specific test files
2015-06-02 12:30:15 +01:00
Matthias Radestock 0b72338400 Merge remote-tracking branch 'upstream/master' 2015-06-02 11:58:38 +01:00
Matthias Radestock cc1a5548af reduce smoke test noise 2015-06-02 11:57:49 +01:00
Matthias Radestock a8b8a0948f cosmetic(ish): banish double bracket bashism 2015-06-02 11:56:27 +01:00
Paul Bellamy e7f73900cb allow test/run_all.sh to run specific test files 2015-06-02 11:45:43 +01:00
Paul Bellamy 927d40f47f Correctly building the client cert subjectAltNames
Still haven't managed to try it due to:
https://github.com/docker/docker/issues/8943
2015-06-02 11:34:54 +01:00
Matthias Radestock 294aafde14 cosmetic: s/when_has_fqdn/with_container_fqdn 2015-06-02 10:52:58 +01:00
Matthias Radestock dcedf241bb simplify dns-add/remove 2015-06-02 10:52:58 +01:00
Matthias Radestock bf8e82ba16 refactor: get rid of with_full_container_id 2015-06-02 10:52:58 +01:00
Paul Bellamy 79cc2d6d25 add note about not handling args with spaces for proxy tls files 2015-06-02 10:20:06 +01:00
Adam Harrison a4582fb529 Optimise DeleteRecords when explicit values are passed 2015-06-02 10:09:26 +01:00
Adam Harrison 30447ada52 Remove obsolete methods 2015-06-02 10:09:22 +01:00
Adam Harrison 66de361fd6 Smoketest dns-remove -h 2015-06-02 09:57:28 +01:00
Adam Harrison 9da5511484 Support -h argument to dns-remove 2015-06-02 09:57:28 +01:00
Adam Harrison e02bd8bb30 Add HTTP routes for wildcard deletion 2015-06-02 09:57:28 +01:00
Adam Harrison e21592d74f Add DeleteRecords method 2015-06-02 09:57:28 +01:00
Adam Harrison 5ead7eb020 Fix TTL breakage + cosmetic fixes 2015-06-02 09:42:59 +01:00
Adam Harrison 2a5f3f5b2d Merge pull request #737 from inercia/weave-cache-tests
WeaveDNS cache tests
2015-06-02 09:30:36 +01:00
Paul Bellamy d80ffa8ad2 working on bash argument parsing 2015-06-01 16:50:52 +01:00
Paul Bellamy 74afbc5505 Proof-of-Concept for proxy TLS support
The code is quite messy, but particularly the argument parsing and
volume-mounting. Also, it doesn't support args with spaces, or
non-absolute file-paths.
2015-06-01 16:50:52 +01:00
Paul Bellamy 4774043295 Add release 0.11.1 to CHANGELOG 2015-06-01 16:23:40 +01:00
Matthias Radestock 1237bfe840 cosmetic 2015-06-01 14:40:39 +01:00
Paul Bellamy 4f86a20824 Add release 0.11.1 to CHANGELOG 2015-06-01 14:26:33 +01:00
Paul Bellamy c4fc2ecf09 Merge remote-tracking branch 'upstream/0.11' 2015-06-01 13:35:37 +01:00
Matthias Radestock 49ad336a35 Merge pull request #809 from paulbellamy/0.11
Rewording and clarifying proxy docs for 0.11.1 release
2015-06-01 13:32:28 +01:00
Paul Bellamy 6feaa72abd [docs] elaborating on proxy IPAM and DNS interaction 2015-06-01 13:24:55 +01:00
Paul Bellamy 71d6f9cdfb [docs] re-ordering proxy doc sections 2015-06-01 13:24:52 +01:00
Adam Harrison 59f919bda3 Merge pull request #795 from inercia/weave-711
Make the TTL configurable
2015-06-01 12:51:45 +01:00
Matthias Radestock 84c0daa000 remove out-of-date info from ipam docs 2015-06-01 12:41:39 +01:00
Matthias Radestock 415b9ead52 cosmetic 2015-06-01 12:41:32 +01:00
Matthias Radestock 7ccf650e48 cosmetic 2015-06-01 12:41:14 +01:00
Matthias Radestock a29831b7fa expand weavedns example to cover multi-host 2015-06-01 12:41:08 +01:00
Alex Chan 70dc8b027d Minor spelling corrections in docs 2015-06-01 12:41:00 +01:00
Paul Bellamy 4e1d388017 weave status should look for proxy at 127.0.0.1 (since PROXY_HOST is gone) 2015-06-01 12:21:58 +01:00
Matthias Radestock 341d5ecbb6 remove out-of-date info from ipam docs 2015-06-01 12:15:14 +01:00
Matthias Radestock 2e208b4921 cosmetic 2015-06-01 12:15:05 +01:00
Matthias Radestock c983d9f39f cosmetic 2015-06-01 12:14:55 +01:00
Matthias Radestock adc5784fc9 expand weavedns example to cover multi-host 2015-06-01 12:14:35 +01:00
Alex Chan a8c3551040 Minor spelling corrections in docs 2015-06-01 12:14:20 +01:00
Matthias Radestock ac90086fb1 Merge pull request #808 from paulbellamy/proxy-short-docker-host
weave PROXY_HOST is always localhost, so just hardcode it as that

Closes #807.
2015-06-01 11:55:58 +01:00
Paul Bellamy bd94c00e96 weave PROXY_HOST is always localhost, so just hardcode it as that
Because launch-proxy runs in weaveexec container, DOCKER_HOST is never
set.
2015-06-01 11:51:32 +01:00
Bryan Boreham 2698f3f376 Merge remote-tracking branch 'upstream/0.11' 2015-06-01 11:42:30 +01:00
Matthias Radestock 3332f94a00 Merge pull request #801 from bboreham/787-better-status
Improve ipam status message before and after pending consensus

Fixes #787.
2015-06-01 11:17:19 +01:00
Matthias Radestock fd0aa3f6ae remove out-of-date info from ipam docs 2015-06-01 11:13:16 +01:00
Bryan Boreham cd1cca3df2 Reformat output of pending ipam requests 2015-06-01 10:55:01 +01:00
Matthias Radestock b86ead2737 fix tests post 9a1e8c07ac 2015-05-31 13:11:28 +01:00
Matthias Radestock 21ee0fdcf7 Merge branch '0.11' 2015-05-31 11:54:54 +01:00
Matthias Radestock 158fb36c8c Merge remote-tracking branch 'upstream/0.11' into 0.11 2015-05-31 11:54:08 +01:00
Matthias Radestock 5f385a297c remove bashism
This was introduced in #780.
2015-05-31 11:51:34 +01:00
Matthias Radestock 8d0adb9359 refactor(ish): use full container id when we have it 2015-05-31 11:25:13 +01:00
Matthias Radestock 79eb4c298c refactor: extract helper for and simplify get_ipam_cidr 2015-05-31 09:59:32 +01:00
Matthias Radestock 6f57bcb477 tiny refactor - use full container id, since we happen to have it 2015-05-31 09:35:53 +01:00
Matthias Radestock 54dbc2f582 cosmetic 2015-05-31 09:03:45 +01:00
Matthias Radestock 1a8f2117b1 explain Direct Peers status 2015-05-29 17:35:21 +01:00
Bryan Boreham 1d6f1f7561 Improve ipam status message before and after pending consensus 2015-05-29 17:34:17 +01:00
Matthias Radestock 59fd53ee99 Merge pull request #800 from paulbellamy/proxy-status
Add proxy info to 'weave status' output

Closes #756.
2015-05-29 17:13:33 +01:00
Matthias Radestock 8c27d469a1 add Direct Peers to status output example
This was missed in #764.
2015-05-29 17:05:34 +01:00
Paul Bellamy 7f62ba6ebe Add proxy info to './weave status' output 2015-05-29 17:05:25 +01:00
Matthias Radestock 9a1e8c07ac cosmetic: report direct peers on one line in status 2015-05-29 17:03:33 +01:00
Matthias Radestock b7df116491 make weave reset stop the proxy
Missed this in #790.
2015-05-29 16:43:18 +01:00
Paul Bellamy ece1fa1b1b Merge remote-tracking branch 'upstream/0.11' 2015-05-29 16:35:59 +01:00
Matthias Radestock 1769cdad7c Merge pull request #799 from paulbellamy/proxy-attach
Proxy kills container and returns error when attach fails

Fixes #788.
2015-05-29 16:35:25 +01:00
Matthias Radestock 99b1e00a75 Merge pull request #794 from paulbellamy/connect-replace
add 'weave connect --replace ...'

Closes #658.
2015-05-29 15:52:32 +01:00
Matthias Radestock e0f4da4641 Merge pull request #792 from rade/720_auto_assigned_dns_http_test_port
use an automatically assigned port for http tests

Fixes #720.
2015-05-29 15:51:38 +01:00
Paul Bellamy 2be69634df replace proxy /weave endpoint with /status
which is more standard with others, and doesn't conflict with docker api
2015-05-29 15:48:33 +01:00
Paul Bellamy 858356de22 add 'weave connect --replace ...' 2015-05-29 15:44:45 +01:00
Paul Bellamy 4f73ef73bb weavewait should exit if it times out
To match behaviour of proxy when interface fails to attach
2015-05-29 14:37:58 +01:00
Paul Bellamy 0cb2e80e09 Proxy kills container and returns error when attach fails 2015-05-29 14:37:33 +01:00
Matthias Radestock 08a17ec376 no need for sleeps to wait for http server
Since we start the tcp listener in the main thread now, connections
can be made immediately; they will just queue up until there is a
handler.
2015-05-29 14:17:03 +01:00
Matthias Radestock 3ad08c0f2a don't print the test port
it's of no interest whatsoever
2015-05-29 14:05:24 +01:00
Tom Wilkie 6e2bb075a6 Merge pull request #797 from weaveworks/timeout
Various fixes to circleci integration tests
2015-05-29 13:57:42 +01:00
Paul Bellamy 587a56cf31 Merge remote-tracking branch 'upstream/0.11'
Conflicts:
	weave
2015-05-29 13:37:21 +01:00
Tom Wilkie 0a945a04f3 Add smarter scheduler to decide which tests to run on which shard on circle. 2015-05-29 12:35:43 +00:00
Matthias Radestock 10a9f58d13 Merge pull request #780 from paulbellamy/proxy-launch-2
simplifying and standardizing `launch-proxy`

Fixes #767. Fixes #755. Closes #757.
2015-05-29 13:30:30 +01:00
Tom Wilkie 332a1bf58d Checkin the assert.sh script, so we can expose test run time. 2015-05-29 11:48:49 +00:00
Matthias Radestock fb721558af auto-assign port in ipam tests 2015-05-29 12:35:23 +01:00
Bryan Boreham 0ab2263a41 Merge pull request #793 from bboreham/ipam-avoid-unittest-races
[ipam] Fix race conditions in unit tests
2015-05-29 12:30:39 +01:00
Tom Wilkie 3642dc5514 Add some timeouts to the circle/gce tests. 2015-05-29 11:29:43 +00:00
Tom Wilkie 28781ef08c Don't pull weavebuild container from docker hub if you don't have a cached version, build it. 2015-05-29 11:28:58 +00:00
Alvaro Saurin 8a188e23d9 Documented the '--ttl' argument 2015-05-29 12:56:45 +02:00
Alvaro Saurin 343e9b1f5a Provide the '--neg-ttl' argument too... 2015-05-29 12:56:45 +02:00
Alvaro Saurin 858a6dd253 Make the TTL returned for local answers configurable. 2015-05-29 12:50:18 +02:00
Bryan Boreham adeb0d3934 Don't bypass the actor mechanism in unit tests, to avoid race conditions 2015-05-29 10:43:19 +01:00
Bryan Boreham 0ee872def4 Lock round mockGossipComms to avoid race conditions 2015-05-29 10:27:17 +01:00
Matthias Radestock c93e789b6b use an automatically assigned port for http tests
instead of random port, which can clash

Fixes #720.
2015-05-29 10:21:41 +01:00
Paul Bellamy db217cd56f Merge pull request #785 from rade/710_reduce_interface_check_interval
reduce interface availability check interval
2015-05-29 10:17:13 +01:00
Bryan Boreham 2befd9d473 Merge remote-tracking branch 'origin/0.11' 2015-05-29 09:56:53 +01:00
Paul Bellamy 22aa1f8fe3 Merge pull request #790 from rade/754_stop_proxy
add `weave stop_proxy`
2015-05-29 09:53:32 +01:00
Paul Bellamy f919ef12d0 fix bug with WEAVEPROXY_DOCKER_ARGS not being passed through 2015-05-29 09:52:43 +01:00
Paul Bellamy 20c9bc04a7 simplifying and standardizing launch-proxy 2015-05-29 09:52:42 +01:00
Bryan Boreham e37f7de0ad Improve error reporting of too much free space 2015-05-29 09:44:24 +01:00
Bryan Boreham 72b30bc699 Fix incorrect calculation of free space
when granting a range that spans a token and inserting a new token, the
free space on the last token that we are handing over has to change.
Add unit test for this too.
2015-05-29 09:44:14 +01:00
Bryan Boreham 10c3f47bd1 Merge pull request #783 from bboreham/782-space-panic
Fixes #782
2015-05-29 09:42:44 +01:00
Matthias Radestock e31f21a6d2 add weave stop_proxy
Closes #754.
2015-05-29 09:20:56 +01:00
Matthias Radestock c87da5e873 Merge pull request #789 from rade/784_status_fail
make `weave status` fail when weave isn't running

Closes #784.
2015-05-28 23:08:43 +01:00
Matthias Radestock b0633281a1 make weave status fail when weave isn't running
but still always show weavedns status, if present

Closes #784.
2015-05-28 21:41:56 +01:00
Matthias Radestock 8dcdf8d314 reduce interface availability check interval
in order to reduce container startup times

Closes #710.
2015-05-28 19:05:09 +01:00
Bryan Boreham 915bf392e9 Improve error reporting of too much free space 2015-05-28 18:15:04 +01:00
Bryan Boreham af5531cbf6 Fix incorrect calculation of free space
when granting a range that spans a token and inserting a new token, the
free space on the last token that we are handing over has to change.
Add unit test for this too.
2015-05-28 18:12:30 +01:00
Paul Bellamy e3e5934c51 Merge remote-tracking branch 'upstream/0.11'
Conflicts:
	proxy/common.go
2015-05-28 17:43:30 +01:00
Matthias Radestock 32c35da402 Merge pull request #777 from paulbellamy/proxy-versionless
allow proxy to intercept unversioned (i.e. 1.0) urls

Closes #770. Closes #778.
2015-05-28 17:19:46 +01:00
Matthias Radestock 8dc5ae662a simplify dependencies
This isn't as precise, but these common dependencies rarely change.
2015-05-28 17:16:22 +01:00
Matthias Radestock ec6a251b7c fix dependencies 2015-05-28 17:10:42 +01:00
Paul Bellamy 147c7f503c add curl test for proxy v1.0 api support 2015-05-28 16:39:32 +01:00
Paul Bellamy c8b95a2527 fix bug in proxy handling of minimal request bodies 2015-05-28 16:24:04 +01:00
Paul Bellamy 169eb19523 allow proxy to intercept unversioned (i.e. 1.0) urls 2015-05-28 16:24:02 +01:00
Adam Harrison ed133dfe0d Merge pull request #773 from inercia/weave-restore-fallback
Restore the "--fallback" argument in WeaveDNS
2015-05-28 16:12:54 +01:00
Matthias Radestock 374b4c523c cosmetic: remove some more dead code 2015-05-28 15:24:51 +01:00
Matthias Radestock 9c71f7f1e9 cosmetic: remove some more dead code 2015-05-28 15:23:34 +01:00
Matthias Radestock ba064a906e cosmetic: remove dead code 2015-05-28 15:17:20 +01:00
Alvaro 5c1297d560 Merge pull request #775 from rade/736_load_balancing_example
add load balancing example
2015-05-28 16:01:55 +02:00
Matthias Radestock 323229dacf add load balancing example
Closes #736.
2015-05-28 14:58:04 +01:00
Matthias Radestock 0b48ea01d7 cosmetic 2015-05-28 14:48:08 +01:00
Matthias Radestock 54d34ea796 expand weavedns example to cover multi-host 2015-05-28 13:36:32 +01:00
Matthias Radestock ccd7153b9d refactor: remove excessive escaping 2015-05-28 12:29:58 +01:00
Paul Bellamy 4f89ad2342 Merge pull request #769 from rade/768_shorten_proxy_entrypoint
shorten proxy entrypoint
2015-05-28 12:16:24 +01:00
Matthias Radestock 0ed1ef1600 Merge pull request #764 from paulbellamy/status-connection-maker
Add "Direct Peers" to weave status

Closes #659. Closes #660.
2015-05-28 12:02:47 +01:00
Alvaro Saurin 7202f7e1d6 Restored "fallback" argument for forcing a fallback server (used for testing/debugging) 2015-05-28 12:57:24 +02:00
Paul Bellamy 998130fa9e Reformatting connection maker target JSON output
So `LastError` is forced to a string, not an object.
2015-05-28 11:49:46 +01:00
Paul Bellamy 637215fcff add status of connection-maker to status-json 2015-05-28 11:49:46 +01:00
Paul Bellamy d16395b8b9 weave status should include 'Direct Peers' set with 'weave connect' 2015-05-28 11:49:46 +01:00
Adam Harrison e110a8ab9c Fix erroneous use of nil value 2015-05-28 10:23:02 +01:00
Bryan Boreham cb03d1d763 Merge pull request #772 from alexwlchan/master
Thanks!
2015-05-28 10:11:58 +01:00
Alex Chan 2a1150b74e Minor spelling corrections in docs 2015-05-28 10:10:16 +01:00
Alvaro Saurin 91cbd75f92 Unit test for cache expirations 2015-05-28 11:09:07 +02:00
Alvaro Saurin 156f5be4d4 Unit test for cache invalidations as a result of AddRecord()/DeleteRecord()/etc... 2015-05-28 11:09:07 +02:00
Adam Harrison 037b6df385 Merge pull request #761 from inercia/weave-583-2
Background updates for names in the Zone database. Fixes #226.
2015-05-28 10:00:32 +01:00
Alvaro Saurin 0a803c61ff Smoke tests 2015-05-28 10:05:22 +02:00
Alvaro Saurin b8d4068a26 Force 8.8.8.8 as the DNS server in VirtualBoxes 2015-05-28 10:05:22 +02:00
Alvaro Saurin 6d2317d60e Silence "golint" errors
Silence "go vet" errors
Run goimports
2015-05-28 10:05:22 +02:00
Alvaro Saurin 40d7b2a87d Dump the zone database status on "weave status"
Fix the smoke test by updating the regexps used for parsing "weave status" output
2015-05-28 09:12:53 +02:00
Alvaro Saurin 32627eccbf Fix the timeout for the fallback server client connection 2015-05-28 09:12:53 +02:00
Matthias Radestock fc9475592d shorten proxy entrypoint
...so that the default output of the likes of `docker ps` shows some
of the real entrypoint/command.

Closes #768.
2015-05-28 06:35:10 +01:00
Matthias Radestock e950d37ed2 cosmetic: reduce noise 2015-05-28 06:31:05 +01:00
Matthias Radestock 5e0d1e6036 refactor: add some lists
in order to reduce likelihood of adding an item in one place and
forgetting to do so in another.
2015-05-27 19:04:39 +01:00
Matthias Radestock 7fc51a508a cosmetic: consistency 2015-05-27 18:28:16 +01:00
Matthias Radestock cd6ba31cfa add weavewait to 'make update' target 2015-05-27 18:27:56 +01:00
Paul Bellamy ce2e80d861 fixing build on master (broken by 68ef0e) 2015-05-27 17:37:13 +01:00
Matthias Radestock a27516b0bf Merge pull request #765 from rade/run_linter_on_travis
Run linter on travis
2015-05-27 17:31:12 +01:00
Matthias Radestock 4dc2ee034c run linter on travis 2015-05-27 17:27:22 +01:00
Matthias Radestock d68dd39839 build all exes 2015-05-27 17:22:56 +01:00
Bryan Boreham b10c2e554c Remove redundant variable 2015-05-27 16:59:12 +01:00
Matthias Radestock ac8d7a5c55 cosmetic: this is somewhat gross, but the last barrier to linter bliss 2015-05-27 16:58:12 +01:00
Matthias Radestock 68ef0e845a cosmetic: yet more linter happiness 2015-05-27 16:56:38 +01:00
Matthias Radestock c6604a6442 cosmetic: more linter happiness 2015-05-27 16:52:08 +01:00
Matthias Radestock 5920927a52 make the linter (almost) happy with ipam 2015-05-27 16:43:18 +01:00
David Wragg e30ab8abba Reduce obfuscation 2015-05-27 14:50:06 +01:00
Matthias Radestock cee6ea6762 cosmetic: it has been decreed that this was "just silly" 2015-05-27 14:18:22 +01:00
Matthias Radestock 84874b9676 cosmetic: more consistency - then on same line as if 2015-05-27 14:14:51 +01:00
Matthias Radestock f281eeb02a cosmetic: missed one 2015-05-27 13:59:09 +01:00
Alvaro Saurin 6f7e385c74 Unit tests for multiple IPs through the HTTP API 2015-05-27 14:54:30 +02:00
Alvaro Saurin 954c5cb394 Limit the number of answers we provide to users. 2015-05-27 14:54:29 +02:00
Paul Bellamy b9d666d4a5 Merge branch '0.11' 2015-05-27 13:52:13 +01:00
Matthias Radestock b26e2128e4 Merge pull request #752 from paulbellamy/proxy-ipam
Fix bug of --with-ipam not setting entrypoints

Fixes #744.
2015-05-27 13:51:06 +01:00
Matthias Radestock 09feb0c53a Merge pull request #697 from paulbellamy/peer-replacement
weave connect/forget take multiple peers

Closes #759.
2015-05-27 13:39:50 +01:00
Paul Bellamy cb73b73862 Fix bug of --with-ipam not setting entrypoints 2015-05-27 13:38:28 +01:00
Matthias Radestock de27c465ca cosmetic: remove stray semicolon 2015-05-27 13:24:53 +01:00
Matthias Radestock dd33c94188 cosmetic: semicolon shall always be preceded by a space 2015-05-27 13:23:34 +01:00
Matthias Radestock 901f0bf04e cosmetic: replace all implict $@s 2015-05-27 13:20:51 +01:00
Matthias Radestock cfbd9dc310 Merge pull request #748 from dpw/gossip-channel-names
Make gossip use channel names on the wire, not channel name hashes

Closes #745.
2015-05-27 13:03:05 +01:00
David Wragg 530a39350b Make gossip use channel names on the wire, not channel name hashes 2015-05-27 12:51:14 +01:00
Ilya Dmitrichenko 84aa629010 Must quoute WEAVE_CIDR when it is a list 2015-05-27 07:47:57 +01:00
Ilya Dmitrichenko 536d55a226 Cosmetic markdown fix in proxy docs 2015-05-27 07:13:06 +01:00
Alvaro Saurin 2d919d0a5f Remove duplicates in answers 2015-05-26 21:27:53 +02:00
Alvaro Saurin c4b07fd44e Shuffle all the replies, not only the ones from the cache... 2015-05-26 21:24:28 +02:00
Alvaro Saurin b87b942363 Check we can use our mDNS client with our mDNS server 2015-05-26 21:24:26 +02:00
Alvaro Saurin 5fe0a7fbb2 Make Stop() idempotent 2015-05-26 21:20:44 +02:00
Alvaro Saurin a159ac44d9 Unit tests for cache invalidations as a result of names updates 2015-05-26 21:20:44 +02:00
Alvaro Saurin 836d012459 Command line arg for changing the number of background updaters
Default number of background updaters raised to 4
2015-05-26 21:20:44 +02:00
Alvaro Saurin 07d15092aa Do not invoke the mDNS lookups from the server: they are used from the Zone database now...
Improved DNS server:
- simplify the handlers code, removing duplicated code
- more configuration parameters (eg, we can now disable the cache)
- we can now bind to port 0 and let the server get a free port (so we can get rid of the mess we had in the unit tests...)
2015-05-26 21:20:42 +02:00
Alvaro Saurin a23462e830 Command line option for disabling the cache 2015-05-26 21:17:24 +02:00
Alvaro Saurin 6fc6afa6a7 Unit tests for the zone lookups and names updates 2015-05-26 21:17:24 +02:00
Alvaro Saurin 64090d0b95 Improved mocks 2015-05-26 21:17:24 +02:00
Alvaro Saurin dbce56ed30 Background updates for names 2015-05-26 21:17:21 +02:00
Alvaro Saurin ea808bd08b Domain lookups, using mDNS
Added DomainLookup* to the Zone interface
2015-05-26 21:14:09 +02:00
Alvaro Saurin da2d66a6df New zone database 2015-05-26 21:14:06 +02:00
Alvaro Saurin c1d124df4c Interfaces for the mDNS client and server 2015-05-26 21:11:34 +02:00
Paul Bellamy dafe2bbc83 adding basic connect/forget test
The test for multiple connect/forget options is not amazing, because we
only have 2 test VMs to connect to each other
2015-05-26 14:37:22 +01:00
Paul Bellamy b392a7d24d weave connect should be best-effort, and return errors for any fails 2015-05-26 14:37:22 +01:00
Paul Bellamy d18640fc8c use @dpw's shell join, and remove non-posix 'local' 2015-05-26 14:37:22 +01:00
Paul Bellamy 781686ee4a weave connect/forget take multiple peers 2015-05-26 14:37:22 +01:00
Bryan Boreham 69ac0950fe Add release 0.11 to CHANGELOG 2015-05-26 13:50:26 +01:00
Matthias Radestock a77ffc4ca5 Merge pull request #740 from awh/revise_weavedns_docs_further
Address weaveDNS doc review comments
2015-05-26 12:46:18 +01:00
Adam Harrison 3d8c8012cf Address review comments 2015-05-26 12:41:14 +01:00
Matthias Radestock 6c5075b9a1 Merge pull request #738 from bboreham/proxy-docs
Reword weave proxy intro
2015-05-26 12:14:01 +01:00
Alvaro de46758eba Merge pull request #739 from weaveworks/revert-615-weave-583-2
Revert "Background updates for names in the Zone database"
2015-05-26 13:07:24 +02:00
Alvaro 21a1cd0469 Revert "Background updates for names in the Zone database" 2015-05-26 12:56:44 +02:00
Bryan Boreham 1e128c142e Links to Docker docs 2015-05-26 11:42:48 +01:00
Bryan Boreham aafc1a56f9 Reword weave proxy intro 2015-05-26 11:40:39 +01:00
Adam Harrison 5382037c4e Merge pull request #615 from inercia/weave-583-2
Background updates for names in the Zone database. Fixes #226.
2015-05-26 10:37:32 +01:00
Alvaro Saurin ee6bbe7925 Smoke tests 2015-05-26 11:30:40 +02:00
Alvaro Saurin cfcc08a914 Limit the number of answers we provide to users. 2015-05-26 11:30:35 +02:00
Alvaro Saurin 0cb6a6269d Unit tests for multiple IPs through the HTTP API 2015-05-26 10:53:13 +02:00
Alvaro Saurin db0ef5cf5c Remove duplicates in answers 2015-05-26 10:53:13 +02:00
Alvaro Saurin 174b47d1db Shuffle all the replies, not only the ones from the cache... 2015-05-26 10:53:13 +02:00
Alvaro Saurin 87a2886539 Refactored the mocked clock.
Refactored some common code in the Zone lookups
2015-05-26 10:53:12 +02:00
Alvaro Saurin 04e4ef214c Check we can use our mDNS client with our mDNS server 2015-05-26 10:53:12 +02:00
Alvaro Saurin 2783f02790 Make Stop() idempotent 2015-05-26 10:53:12 +02:00
Alvaro Saurin 761eb787ff Unit tests for cache invalidations as a result of names updates 2015-05-26 10:53:12 +02:00
Alvaro Saurin 6fa2f2142f Command line arg for changing the number of background updaters
Default number of background updaters raised to 4
2015-05-26 10:53:12 +02:00
Alvaro Saurin 72b55c82ec Do not invoke the mDNS lookups from the server: they are used from the Zone database now...
Improved DNS server:
- simplify the handlers code, removing duplicated code
- more configuration parameters (eg, we can now disable the cache)
- we can now bind to port 0 and let the server get a free port (so we can get rid of the mess we had in the unit tests...)
2015-05-26 10:53:12 +02:00
Alvaro Saurin 44cdf3bca3 Command line option for disabling the cache 2015-05-26 10:53:12 +02:00
Alvaro Saurin fe672eae63 Unit tests for the zone lookups and names updates 2015-05-26 10:53:12 +02:00
Alvaro Saurin 267fddc474 Improved mocks 2015-05-26 10:53:12 +02:00
Alvaro Saurin 2b6388359b Background updates for names 2015-05-26 10:53:12 +02:00
Alvaro Saurin a2dad42ef6 Domain lookups, using mDNS
Added DomainLookup* to the Zone interface
2015-05-26 10:53:12 +02:00
Alvaro Saurin 8a509168ee New zone database 2015-05-26 10:53:12 +02:00
Alvaro Saurin 9c48c83701 Interfaces for the mDNS client and server 2015-05-26 10:53:12 +02:00
Matthias Radestock 6197f98c26 reword proxy intro a bit 2015-05-24 16:27:34 +01:00
Matthias Radestock 213208bf12 Merge branch 'bboreham-723-check-bridge'
Fixes #723.
2015-05-24 11:30:47 +01:00
Matthias Radestock 5148ed7936 symmetry with create_bridge 2015-05-24 11:30:04 +01:00
Matthias Radestock add68afe0a simplify 2015-05-24 11:29:47 +01:00
Matthias Radestock ec59d5abee Merge branch '723-check-bridge' of https://github.com/bboreham/weave into bboreham-723-check-bridge 2015-05-24 11:20:43 +01:00
Matthias Radestock a7fccf053f Merge pull request #719 from bboreham/705-hide-404
Don't talk to IPAM unless 0 addresses supplied to 'hide'

Fixes #705.
2015-05-24 11:15:00 +01:00
Matthias Radestock b1af1f752d refactor: extract a helper 2015-05-23 12:28:43 +01:00
Matthias Radestock 01bc994822 refactor: extract image creation 2015-05-23 12:14:58 +01:00
Matthias Radestock 2b8a1fe177 rename 2015-05-23 11:59:47 +01:00
Matthias Radestock 1c4beb5ef0 improve entrypoint/command selection tests 2015-05-23 11:58:00 +01:00
Matthias Radestock b42b045c08 cosmetic: vertical alignment 2015-05-23 10:30:45 +01:00
Matthias Radestock c904a650b6 cosmetic: reflow 2015-05-23 10:27:38 +01:00
Matthias Radestock 0612f27f1f fix typo 2015-05-23 10:27:07 +01:00
Matthias Radestock 5387eb77ab Merge pull request #721 from paulbellamy/proxy-ipam
Proxy support for IPAM

Closes #657.
2015-05-23 10:24:53 +01:00
Matthias Radestock 0c01bb758e Merge pull request #722 from errordeveloper/docs-branch-heads
Create `weave/<branch>/head` for latest branch docs

Closes #668.
2015-05-22 18:48:14 +01:00
Paul Bellamy 11931f3ea2 [tests] extract gliderlabs/alpine and aanand/docker-dnsutils into var in
config.sh

@bboreham made me do it.
2015-05-22 15:52:36 +01:00
Paul Bellamy cc82213147 add 'weave launch-proxy --with-ipam' flag 2015-05-22 15:52:36 +01:00
Matthias Radestock f36af211c0 cosmetic 2015-05-22 15:41:29 +01:00
Matthias Radestock b20b4a8675 Merge pull request #731 from dpw/connection-select-break
Fix use of "break" within "select" in LocalConnection actorLoop
2015-05-22 15:40:00 +01:00
David Wragg dfa93436c2 Fix use of "break" within "select" in LocalConnection actorLoop
From the golang spec: 'A "break" statement terminates execution of the
innermost "for", "switch", or "select" statement within the same
function.'  But the LocalConnection actorLoop seems to assume it
skips the "select" and terminates the enclosing "for".

As a result, if something did conn.Shutdown(nil), it would not actually
shutdown the connection. It turns out this is never a problem, because
Shutdown is always called with a non-nil error, even though there is code
in a few places to handle the nil case. By cleaning that up, the dubious
"break" is eliminated.
2015-05-22 11:26:30 +01:00
Paul Bellamy d0320b43f9 realigning the weave help output columns 2015-05-22 10:04:10 +01:00
Paul Bellamy 73071f2c62 cleanup proxy test helpers 2015-05-22 10:04:09 +01:00
Paul Bellamy c9409c5342 [proxy] Adding basic docs about IPAM integration 2015-05-21 15:37:37 +01:00
Paul Bellamy 46bdd8c67d Proxy support for IPAM
Pass `-e WEAVE_CIDR=` to your container to use IPAM. If IPAM is not
enabled on the weave router, the container will be booted without weave
networking.

I'm not crazy about adding *another* undocumented weave cli command
(ipam-cidr), but it saves the proxy messing around finding container
IPs.
2015-05-21 15:37:37 +01:00
Bryan Boreham 22b8f482ea Fix a couple of mistakes in get_ipam_cidr calls
(first one only caused a slight slow-down; second one only mattered if
your 'weave' container had a non-standard name)
2015-05-21 12:05:48 +01:00
Adam Harrison 387ec3fdd5 Add run-smoketests target to Makefile 2015-05-21 11:40:12 +01:00
Tom Wilkie 449bf45c2b Try and ignore pull request pseudo-branches for circle build. 2015-05-21 09:50:38 +00:00
Bryan Boreham b771528942 Check if Docker using weave bridge before inserting DROP rule
and comment the use of -I
2015-05-21 10:14:15 +01:00
Bryan Boreham f727e56bad Revert "consistency"
This reverts commit c5d5dd5fdc.
2015-05-21 09:54:46 +01:00
Matthias Radestock c5d5dd5fdc consistency 2015-05-21 05:46:07 +01:00
Matthias Radestock 6f68eb50aa get rid of a silly redirect 2015-05-20 16:54:07 +01:00
Paul Bellamy c63d2e531a Merge pull request #712 from errordeveloper/vagrantfile-more-cleanup
Improve readability of Vagrantfile and cleanup this official image
2015-05-20 16:47:28 +01:00
Ilya Dmitrichenko eb6f9faae7 Create weave/<branch>/head for latest branch docs (close #668) 2015-05-20 16:44:02 +01:00
Matthias Radestock 23dd02a05a remove redundant check 2015-05-20 15:58:22 +01:00
Paul Bellamy d24e75f76b extract vagrantfile shared bits 2015-05-20 15:42:08 +01:00
Matthias Radestock b119872b1b Merge pull request #718 from paulbellamy/proxy-check-up
use grep in /sys to verify ethwe is up in tests

Closes #707.
2015-05-20 13:47:48 +01:00
Paul Bellamy eeaa6c9e2c use grep in /sys to verify ethwe is up in tests
Closes #707. `ip link show` seems to "lag" the real state. In tests
checking the physical link state is good enough.
2015-05-20 13:36:36 +01:00
Matthias Radestock 0a7211988a Merge pull request #714 from paulbellamy/proxy-cmd-merge
refactor proxy entrypoint rewriting to match docker daemon

Fixes #709.
2015-05-20 13:19:03 +01:00
Bryan Boreham 9e79d7dcca Don't talk to IPAM unless 0 addresses supplied to 'hide' 2015-05-20 12:59:04 +01:00
Paul Bellamy 369466d3d4 refactor proxy entrypoint rewriting to match docker daemon
Closes #709
2015-05-20 11:41:15 +01:00
Matthias Radestock f642db0462 Merge pull request #704 from bboreham/620-drop-docker
Drop traffic from docker0 bridge to weave bridge

Fixes #620.
2015-05-20 11:24:41 +01:00
Bryan Boreham 906be31483 Regularise the ping tests across different subnets 2015-05-20 11:08:39 +01:00
Matthias Radestock 0220f9aaca Merge pull request #713 from paulbellamy/proxy-exec
use exec instead of run, to replace weavewait with subprocess

Closes #708.
2015-05-20 10:48:01 +01:00
Ilya Dmitrichenko a48777e482 Clean test VMs as well 2015-05-20 10:47:15 +01:00
Bryan Boreham 4ee32cc12d Extend smoke test for new drop rule
Note that we have to wrap the '!' in 'sh -c' to get the negation to work as expected.
2015-05-20 10:45:18 +01:00
Paul Bellamy 818e162fff [proxy] use exec instead of run, to replace weavewait with subprocess
Closes #708
2015-05-20 10:35:52 +01:00
Bryan Boreham 6739e6c166 iptables rule to drop traffic from docker0 bridge to weave bridge 2015-05-20 10:27:38 +01:00
Ilya Dmitrichenko 3a0b341770 Improve readability of Vagrantfile and cleanup this official image 2015-05-20 08:24:18 +01:00
Matthias Radestock 7396f61222 move some tests 2015-05-19 21:38:50 +01:00
Matthias Radestock f7f0369bff improve logging in proxy
- always info-log when we encounter an 'interesting' container
- s/CIDR/WEAVE_CIDR
- some other minor tweaks
2015-05-19 19:46:53 +01:00
Matthias Radestock fe36da2041 Merge pull request #703 from paulbellamy/proxy-minimal
proxy needs to add weavewait to execs as well
2015-05-19 18:54:15 +01:00
Paul Bellamy 610bc46970 proxy should provide reasonable error when execing on a container which doesn't exist 2015-05-19 17:37:23 +01:00
Paul Bellamy e6b587ea8d proxy needs to add weavewait to execs as well
Bug came up in a test where we are restarting and doing an exec checking
a network interface right away. weavewait is only added if the container
has a WEAVE_CIDR var set, the same as at container creation.
2015-05-19 17:36:53 +01:00
Bryan Boreham 70b595c4a2 Clarify status message when allocator is awaiting consensus 2015-05-19 14:12:08 +01:00
Matthias Radestock f59facf5a6 Merge pull request #701 from bboreham/684-improve-errors
Improve ipam error messages

Closes #684.
2015-05-19 13:34:22 +01:00
Bryan Boreham 0e55f75d86 Oops 2015-05-19 13:16:36 +01:00
Bryan Boreham ee7dac9e28 Improve error messages
Make clear that they come from the IP allocator, and reduce repetition in the text.
Also include the nickname for other peer, if known.
2015-05-19 12:37:57 +01:00
Matthias Radestock 9f85cbc07d Merge pull request #696 from bboreham/692-ipam-status
Document ipam additions to `weave status`

Closes #692.
2015-05-19 12:05:53 +01:00
Bryan Boreham 454bbdbc68 Make reference into a link 2015-05-19 11:48:11 +01:00
Paul Bellamy 62242cb640 fixup bad golint refactor 2015-05-19 11:46:03 +01:00
Bryan Boreham 9363193fe8 Review feedback 2015-05-19 11:43:59 +01:00
Matthias Radestock 29601610c0 Merge pull request #698 from paulbellamy/proxy-minimal
remove usage of backticks in sh script
2015-05-19 11:33:00 +01:00
Paul Bellamy fdd0bd3b35 remove usage of backticks in bash script 2015-05-19 11:26:55 +01:00
Bryan Boreham 4d3234aa36 Merge pull request #695 from rade/merge_ipam_tests_with_rest
LGTM
2015-05-19 11:25:50 +01:00
Matthias Radestock 8b66b8ae47 Merge pull request #655 from paulbellamy/proxy-minimal
Minimal implementation of the proxy

closes #47, closes #230, closes #251, closes #400.
2015-05-19 10:59:00 +01:00
Paul Bellamy ee07d645b4 golint cleanup of else block 2015-05-19 10:47:57 +01:00
Bryan Boreham bffe9f811a Document 'weave status' section for IPAM 2015-05-19 10:41:45 +01:00
Bryan Boreham b6a8c48097 Tidy up IPAM parts of 'weave status'
- removed detailed "free" and "owned" printout of 'space'
 - removed "free" counts from ring lines - limited usefulness and often out of date.
 - removed trailing newline
 - changed "Ring" to "Owned Ranges"
 - print subnet in CIDR format
2015-05-19 10:38:26 +01:00
Paul Bellamy b71a428369 proxy should be sending error code to client, instead of exiting 2015-05-19 10:34:46 +01:00
Paul Bellamy 6911dc8d38 updating proxy with-dns description to match docs 2015-05-19 10:34:46 +01:00
Paul Bellamy 1e7c82bb58 use busybox instead of ubuntu for proxy image pulling test
busybox is smaller. gliderlabs/alpine is even smaller, but is used by
weaveexec, so we can't remove it.
2015-05-19 10:34:46 +01:00
Paul Bellamy 2f8c5d7bd9 proxy interceptors implicitly modify arguments, instead of returning them 2015-05-19 10:34:46 +01:00
Matthias Radestock 126d4f4079 some refactoring and tidying
- some inlining
- compile regexps once
- consistent error messages
- always mention container id in startContainerInterceptor errors
2015-05-19 10:34:46 +01:00
Paul Bellamy ca4b8e6f8e add proxy support for 'docker restart' 2015-05-19 10:34:46 +01:00
Paul Bellamy 2a120e5565 refactor proxy rawStream handler 2015-05-19 10:34:46 +01:00
Paul Bellamy 6fa0a9d70c moving weaveproxy docs off 'experimental-features' 2015-05-19 10:34:46 +01:00
Paul Bellamy 55d17ba3bf separate proxy package into weaveproxy and proxy 2015-05-19 10:34:46 +01:00
Paul Bellamy bedc20c7c1 extract docker-bridge-ip fetching from proxy into bash script 2015-05-19 10:34:46 +01:00
Paul Bellamy 96a69d2303 Add dns support for the proxy
In the proxy, I swapped the 'flag' library for
'code.google.com/p/getopt', which uses double-dash flags.

proxy lets 'weave attach' do dns registration on container start

proxy needs to set DNSSearch if not specified
2015-05-19 10:34:45 +01:00
Paul Bellamy 444b1482fa add weavewait
binocarlos/wait-for-weave is Apache2 licensed, so attribution/etc applies. Thus, we
re-invent the wheel here.
2015-05-19 10:34:45 +01:00
Paul Bellamy 9389001a1d [docs] Initial version of proxy docs 2015-05-19 10:34:45 +01:00
Paul Bellamy 5a6a67f632 Add integration test for the proxy, using docker-py client 2015-05-19 10:34:45 +01:00
Michael Bridgen 6c4271f15d Add launch-proxy command to cli
Use curl /weave to wait for the proxy to boot before returning.
2015-05-19 09:48:12 +01:00
Michael Bridgen 6d7de3ddff Proxy implementation
This adds a docker remote API proxy to the weaveexec image. The proxy
has hooks for (post-) container start and (pre-) container create; the
former attaches a weave interface to a container given a WEAVE_CIDR
environment entry, and the latter adjusts the entrypoint of a container
to wait for the ethwe interface.

Notes:

None of the hooks need to inspect/change large bodies. In order to avoid
a crash when handling images larger than memory we need to ensure that
the entire request body is never loaded into memory (i.e. No
ioutils.ReadAll).

When proxying a raw-stream response, we can't use http.WriteHeader. It
sets the Transfer-Encoding to chunked, which will throw off the clients.
Instead we have to hijack the socket, and write out http status code
manually.

For chunked transfer-encodings, clients (docker-py) depend on each http
chunk containing exactly one JSON object. Therefore, we cannot re-chunk
the body, and have to pass it across verbatim.

Proxy needs to pass multi-cidrs separately when calling weave. Otherwise
they get smashed together on the command line, and the call fails.

When creating the image, if no entrypoint is specified on the container,
it should use the entrypoint of the image. Code for this is based on
powerstrip-weave.

We handle WEAVE_CIDR='' by acting like it is not set. The reason being,
that because WEAVE_CIDR is inherited from images (along with env vars),
setting it to blank is equivalent to unsetting it.
2015-05-19 09:48:12 +01:00
Paul Bellamy d8e1d0798e replace test usage of ifconfig with ip link show 2015-05-19 09:48:12 +01:00
Matthias Radestock e9963945d6 cosmetic 2015-05-18 23:38:37 +01:00
Matthias Radestock 9017888b9c merge IPAM tests with rest
keeping them separate leads to too much duplication
2015-05-18 23:28:35 +01:00
Matthias Radestock 8e1ac2b300 Merge pull request #693 from bboreham/677-document-rmpeer
Illustrate the usage of 'weave rmpeer'

Closes #677.
2015-05-18 17:37:19 +01:00
Bryan Boreham 25c70cfad6 Illustrate the usage of 'weave rmpeer' 2015-05-18 17:26:24 +01:00
Matthias Radestock cf34335ec7 Merge pull request #691 from bboreham/681-ipam-docs
Expand documentation of 'weave launch' parameters for IPAM

Closes #669 and #681.
2015-05-18 17:09:26 +01:00
Bryan Boreham 4ea9cfca48 Expanded description of 'weave launch' parameters for IPAM 2015-05-18 16:49:45 +01:00
Matthias Radestock 94525f0497 Merge pull request #690 from bboreham/and_true
Fix typo '&& true' in weave script, that could cause partial population of DNS
2015-05-18 16:47:28 +01:00
Bryan Boreham 0227f7e727 '&& true' was a typo - replaced with '|| true' 2015-05-18 16:06:38 +01:00
Bryan Boreham bb4dd716c8 Document -iprange a bit more, and warn not to use its addresses elsewhere 2015-05-18 16:03:34 +01:00
Matthias Radestock d53ec858a2 Merge pull request #689 from bboreham/671-hide-errors
Silence complaints from http_call when at end of 'weave hide'

Fixes #671.
2015-05-18 15:59:45 +01:00
Bryan Boreham 286c46653c Add smoke tests for 'weave expose' and 'weave hide' when weave isn't running 2015-05-18 15:43:44 +01:00
Bryan Boreham 7cf8234d9f Silence complaints from http_call when at end of 'weave hide' 2015-05-18 14:54:46 +01:00
Matthias Radestock fd0282ee05 Merge pull request #686 from bboreham/672-ipam-run-local
Fetch an address from IPAM for 'weave --local run'

Fixes #672.
2015-05-18 14:44:46 +01:00
Bryan Boreham 5cc3036498 Extend smoke test for 'weave run --local' to include IPAM 2015-05-18 14:38:12 +01:00
Matthias Radestock 957c8a1954 Merge pull request #665 from pradeepchhetri/dockerfile
Merged two run commands into one in weavexec Dockerfile
2015-05-18 12:44:17 +01:00
Matthias Radestock 94c7e7f646 Merge pull request #567 from bboreham/pktdebug
Rename per-packet debugging flag to `-pktdebug`
2015-05-18 12:21:40 +01:00
Matthias Radestock 5f16854e1f Merge pull request #685 from bboreham/679-ipam-docs
Give 'weave ps' example with IPAM and link to docs

Closes #679.
2015-05-18 12:20:03 +01:00
Bryan Boreham 48e9c53dda Fetch an address from IPAM for 'weave --local run' 2015-05-18 12:06:06 +01:00
Bryan Boreham 0ec4417992 Rename per-packet debugging flag to -pktdebug
to allow `-debug` to mean "more verbose logging"
2015-05-18 11:52:50 +01:00
Adam Harrison eecb540141 Merge pull request #683 from rade/refactor_smoke_tests
Refactor smoke tests
2015-05-18 11:37:40 +01:00
Bryan Boreham 14c690d9f5 Give 'weave ps' example with IPAM and link to docs 2015-05-18 11:37:38 +01:00
Matthias Radestock c08bfd2205 always quote $@ 2015-05-18 11:16:12 +01:00
Adam Harrison d9eea28f17 Merge pull request #667 from rade/revise_weavedns_docs
revise weaveDNS docs
2015-05-18 10:46:09 +01:00
Matthias Radestock 06abd62af0 always spell weaveDNS lower-case, except in titles/headings 2015-05-18 10:43:07 +01:00
Matthias Radestock f732252f22 eliminate ambiguity 2015-05-18 10:36:13 +01:00
Matthias Radestock a01d33452e revise weaveDNS docs
- add ToC and anchors for all headings
- move docker bridge settings into "How it works" - it's a rarely used
  feature
- highlight a bunch of features, extracting them from obscurely titled
  sub-sections of "manual usage"
  - basic rendundancy
  - adding/removing extra DNS entries
  - service hot-swapping
  - ability to retain entries for stopped containers
  - using a different local domain
- rename remainder of "manual usage" section to "Using weaveDNS
  without `weave run`"
- remove "Unregistering" section - it was out of date and is not
  covered by the "add/remove" section
2015-05-18 10:36:13 +01:00
Matthias Radestock 6dd6d9d6f0 one ping to rule them all 2015-05-16 21:53:50 +01:00
Matthias Radestock ccd468f80c drop -debug from launch-dns
It wasn't being applied consistently in any case.
2015-05-16 21:06:08 +01:00
Matthias Radestock 4e7ddedf75 extract startup of container with dns
There is now just one mention of the aanand/docker-dnsutils image; in
config.sh.
2015-05-16 21:00:27 +01:00
Matthias Radestock 8261c7f782 extract hostname as a constant 2015-05-16 21:00:27 +01:00
Matthias Radestock c3c5b76007 use already-defined constants 2015-05-16 21:00:27 +01:00
Matthias Radestock 1c1bd817e1 extract startup of simple container
There is now just one mention of the gliderlabs/alpine image outside config.sh.
2015-05-16 21:00:07 +01:00
Matthias Radestock ab244f76f6 consistency 2015-05-16 20:37:16 +01:00
Matthias Radestock 535a6ae199 consistent docker run parameter order 2015-05-16 20:37:16 +01:00
Matthias Radestock 822cab7750 use assert_dns_record in ipam test
instead of homegrown version that uses dig
2015-05-16 19:22:31 +01:00
Matthias Radestock 2647fd4c92 extract helper for obtaining container ip address
thus keeping the ghastly regexp in one place instead of three.
2015-05-16 19:14:23 +01:00
Matthias Radestock bb8a745431 cosmetic: helper functions come first 2015-05-16 19:05:19 +01:00
Matthias Radestock 352e31c88b remove all the weave_on hacks
that existed because it was echoing a message to stdout, which it no
longer does.

As a result there is now no mention of the docker tcp host/port
outside config.sh.
2015-05-16 19:00:35 +01:00
Matthias Radestock a94d2ef277 remove pointless quoting 2015-05-16 18:22:55 +01:00
Matthias Radestock ad34a56e56 simplify NXDOMAIN test
- remove premature abstraction
- only do one check
2015-05-16 18:22:55 +01:00
Matthias Radestock 970af56765 less noise, and use lower-case var names 2015-05-16 18:22:02 +01:00
Paul Bellamy defcfa5d26 test *_on helpers should send non-result output to &2, so that asserts work
Previously, the result output would include:
Docker on: <command>
result output text

etc...
But this would cause: assert "docker_on ..." to fail
2015-05-16 18:03:21 +01:00
Matthias Radestock 11d241eccd cosmetic: better error message 2015-05-16 03:17:07 +01:00
Matthias Radestock dbcdace361 tiny refactor: outdent 2015-05-16 02:50:02 +01:00
Matthias Radestock 47727e23f1 fix quoting, unbreaking multi-cidr weave start/attach/hide
Fixes #670.
2015-05-16 02:42:10 +01:00
Matthias Radestock 2bd197e12c cosmetic 2015-05-16 01:31:47 +01:00
Matthias Radestock 2934e89e14 fetch dependencies of sigproxy
There aren't any right now, but might be in the future.
2015-05-16 01:15:47 +01:00
Matthias Radestock dc96fc17a9 correct protocol version
let us not get ahead of ourselves
2015-05-16 01:09:53 +01:00
Matthias Radestock 35211f65c9 pull IPAM docs onto separate page
and link to them from main docs page
2015-05-15 17:08:24 +01:00
David Wragg 977666c2c9 Fix test when run on circleci
'docker exec -i' hangs there.
2015-05-15 15:48:55 +01:00
Matthias Radestock 7737c7f34f move weavedns link 2015-05-15 14:02:57 +01:00
Matthias Radestock dcac8d8316 cosmetic: move weavedns-readme.md to weavedns.md 2015-05-15 13:59:40 +01:00
David Wragg f95b3971ab Merge bboreham/22_ipam (PR #563): IP Allocation Management
Fixes #22.
2015-05-15 13:34:57 +01:00
Pradeep Chhetri 91bb28b47b Merged two run commands into one in Dockerfile 2015-05-15 18:04:20 +05:30
David Wragg 4b942e8ed5 populate_1_ipam -> ipam_claim 2015-05-15 13:12:36 +01:00
Matthias Radestock fbbc2bbf79 fix typo 2015-05-15 12:56:04 +01:00
Matthias Radestock 483c565fac better titles for WeaveDNS links 2015-05-15 11:59:27 +01:00
Matthias Radestock 210101932c fix broken link 2015-05-15 11:56:46 +01:00
David Wragg 0eee999d7c Unify common call to wait_for_status upon "weave launch" 2015-05-15 11:49:36 +01:00
David Wragg a027337be2 Tidy up IPAM setup 2015-05-15 11:44:50 +01:00
Bryan Boreham f6ac069bc5 Remove some less-necessary test helper functions 2015-05-14 17:57:13 +01:00
Bryan Boreham 3cd1a17c23 IP Allocation Management
Using the Ring CRDT and Weave gossip, provides an interface
for the weave script to allocate IP addresses in `weave run`,
`weave start` and `weave expose`.

See docs/ipam.md for implementation details, and site/features.md
for user-visible changes.
2015-05-14 17:44:06 +01:00
David Wragg 72112d7aef Implementation of Basic Paxos 2015-05-14 17:33:20 +01:00
Tom Wilkie 4f3dcf8dc9 Ring CRDT for IP Allocation Management
The basic data structure used to share information about
IP allocations between peers
2015-05-14 17:33:20 +01:00
Matthias Radestock c158b86dc6 refactor: shorten a method name 2015-05-14 15:13:10 +01:00
Matthias Radestock 79d3a2821f simplify test
less clever, but more readable
2015-05-14 14:52:35 +01:00
Matthias Radestock c7dd48d769 introduce PeerUID type 2015-05-14 12:35:01 +01:00
Matthias Radestock ace56da7b3 allow Gossiper.Gossip to return nil
...to indicate 'no data'
2015-05-14 12:02:56 +01:00
Ilya Dmitrichenko af21f5eaf6 Markdown cosmetic 2015-05-13 16:53:49 +01:00
Ilya Dmitrichenko 0881fa0c69 Add instructions for building on a Vargant VM without logging in 2015-05-13 16:45:58 +01:00
Ilya Dmitrichenko db28286a4b Vagrnant should not do everything as root 2015-05-13 16:41:25 +01:00
Ilya Dmitrichenko ccd111d512 Cleanup Vagrantfile and fix path in non-interactive shell 2015-05-13 16:24:36 +01:00
Fintan Ryan d106c56c00 Merge pull request #651 from weaveworks/move-weavedns-docs
Update docs index: move WeaveDNS docs and add Getting Started Guides
2015-05-13 14:44:54 +01:00
Paul Bellamy f0578f5f66 forgot to give Overview section an anchor 2015-05-13 14:17:56 +01:00
Ilya Dmitrichenko d63a9785bd Point to new location for WeaveDNS docs 2015-05-13 13:59:41 +01:00
rade 8f38a48a1f Merge pull request #653 from paulbellamy/docs-how-it-works-toc
Add ToC to how-it-works docs page
2015-05-13 13:53:37 +01:00
Paul Bellamy 39f87a27e5 Add ToC to how-it-works docs page 2015-05-13 13:45:06 +01:00
Ilya Dmitrichenko ca4d813b71 Cleanup documentation index, add getting started guides 2015-05-13 12:47:43 +01:00
Ilya Dmitrichenko 2199c5c273 Add Jekyll "frontmatter", it does not build it otherwise 2015-05-13 10:36:22 +01:00
Ilya Dmitrichenko 366adfb8f9 Move WeaveDNS docs to docs.weave.works (close #471) 2015-05-13 10:21:51 +01:00
Ilya Dmitrichenko 17925a0c0b gsutil rsync does not imply recursion 2015-05-13 10:21:32 +01:00
Matthias Radestock 55077d5059 cosmetic: it's lower-case 'weave' everywhere except in titles 2015-05-12 17:26:28 +01:00
David Wragg 3e4f7358a7 Mention "CIDR notation" rather than "CIDR" in error message
It's more web-searchable.
2015-05-12 15:48:33 +01:00
Matthias Radestock 9195894d2a Show error message when DNS population fails part way through.
Closes #393.
2015-05-12 11:49:48 +01:00
Matthias Radestock 953449c215 refactor: extract Peer.ForEachConnectedPeer helper 2015-05-11 21:06:07 +01:00
Matthias Radestock c5c93d3a77 refactor: increase similarity betweeen parts of route calculation 2015-05-11 20:14:24 +01:00
Matthias Radestock 039446f756 cosmetic: remove outdated comment 2015-05-11 20:00:04 +01:00
Matthias Radestock 03c1f49850 make profiling work again
The weave SIGINT signal handler was racing the profiler SIGINT signal
handler. So now we a) make the former return instead of exit, and b)
disable the latter, as a result of which the deferred profile.Stop in
weaver/main gets called.
2015-05-11 19:38:06 +01:00
Matthias Radestock 43133b5851 increase tolerance for missed udp heartbeats
3 are quite easy to miss when there is contention. 6 less so.
2015-05-11 18:35:06 +01:00
David Wragg df40c5b97e Merge rade/643_eliminate_route_calculcation_deadlock (PR #644)
eliminate route calculcation deadlock

Fixes #643.
2015-05-11 17:57:53 +01:00
Matthias Radestock e246e6acc6 coarser lock granularity
- get rid of individual Peer locks
- hold Peers read (or write) lock and LocalPeer read lock while
  performing route calculations

This eliminates the deadlock resulting from the inconsistent lock
acquisition order in route calculcation.

Fixes #643.
2015-05-11 17:19:54 +01:00
Matthias Radestock 5bd8730353 make Peer.Encode lock-free 2015-05-11 17:19:51 +01:00
Matthias Radestock c210f0e3c1 make Peer.Info lock-free 2015-05-11 17:19:46 +01:00
Matthias Radestock 426d8edb31 remove Peer.SetVersionAndConnections
further shrinking Peer API
2015-05-11 17:19:43 +01:00
Matthias Radestock 86ba3be4f7 move Connections from Peer to LocalPeer
further shrinking the former's API
2015-05-11 17:19:38 +01:00
Matthias Radestock 5bf6889250 remove Peer.Version
further shrinking the Peer API
2015-05-11 17:19:35 +01:00
Matthias Radestock 0229185ffe move ConnectionTo from Peer to LocalPeer
further shrinking the former's API
2015-05-11 17:19:31 +01:00
Matthias Radestock 505ced5cc8 move localRefCount accounting responsibility from Peer to Peers
This makes for a more symmetric API - Peers.FetchWithDefault does the
increment and Peers.Dereference does the decrement, whereas previously
the latter was done by Peer.DecrementLocalRefCount.

This does increase lock granularity, i.e. modifying any ref count now
requires a write lock on Peers.
2015-05-11 17:19:23 +01:00
Matthias Radestock 3e6eef5ebd refactor(ish): move a bunch of methods from Peer to LocalPeer
in order to reduce the former's API surface
2015-05-11 17:19:15 +01:00
Paul Bellamy 8f1f6741e3 Merge pull request #642 from errordeveloper/post-docs-move
Fixes post #612
2015-05-11 17:02:35 +01:00
Matthias Radestock 52a89a318a cosmetic: resolve yet more linter complaints 2015-05-11 02:44:15 +01:00
Matthias Radestock b43a3e9ab2 cosmetic: resolve some more linter complaints 2015-05-11 02:37:59 +01:00
Matthias Radestock 4bef9d90fb cosmetic: resolve some linter complaints 2015-05-11 02:33:56 +01:00
Matthias Radestock e5a87d1ab1 cosmetic: resolve some linter complaints 2015-05-11 02:31:34 +01:00
Matthias Radestock ef2c55141b keep linter happy 2015-05-11 02:25:01 +01:00
Matthias Radestock 0cbbe9bac3 make Peer.String() return the common case
When we want a string representation of a Peer, we nearly always just
want the full name. So make String() return that and introduce Info()
to return expanded info.
2015-05-10 23:58:23 +01:00
Matthias Radestock db198652a5 refactor: get rid of test-launch 2015-05-09 17:29:13 +01:00
Matthias Radestock 7b0f432339 cosmetic: fix typo 2015-05-09 17:11:52 +01:00
Matthias Radestock 5472d868aa refactor
we don't need to extract the -nickname option since when the same
option is specified more than once, the last value wins
2015-05-08 18:28:22 +01:00
Matthias Radestock 5a3ff111ef cosmetic 2015-05-08 18:24:43 +01:00
Ilya Dmitrichenko 35ca82dc0c Use gsutil rsync 2015-05-08 16:44:40 +01:00
Ilya Dmitrichenko 86fdfef6f1 No redirect required for latest_release_doc_updates 2015-05-08 15:41:49 +01:00
Paul Bellamy 9c5323ee4f Merge pull request #612 from errordeveloper/post-docs-move
Post docs move
2015-05-08 13:40:35 +01:00
Ilya Dmitrichenko 2daf805e95 Point to released docs for WeaveDNS 2015-05-08 11:16:35 +01:00
Paul Bellamy 5e5b3ba65e Merge pull request #638 from weaveworks/circle
More CircleCI Improvements
2015-05-08 10:19:18 +01:00
Tom Wilkie 28a2c0d8a9 Parallelise integration tests. 2015-05-07 19:03:59 +00:00
Tom Wilkie 8d1b161d4f Use circleci build number instead of git hash, as we can have multiple builds for a given hash. 2015-05-07 18:33:02 +00:00
Tom Wilkie 5094e84af3 Use gliderlabs/alpine over ubuntu in attempt to speed up tests. 2015-05-07 17:40:44 +00:00
Tom Wilkie a5750dda2a Tidy up try_connect function 2015-05-07 17:15:14 +00:00
Tom Wilkie f9c43fecf4 Wait for VMs to come up before connecting 2015-05-07 17:00:36 +00:00
Paul Bellamy 93012a1cfd Merge pull request #637 from weaveworks/circle
Improvements to circle ci integration
2015-05-07 17:32:53 +01:00
Tom Wilkie 5ed8ad46ee Don't need gsutil on circle; use a prebuild weave-build image and cache it locally. 2015-05-07 16:23:08 +00:00
rade 5e5e932584 Merge pull request #627 from grkvlt/feature/add-dns-entry
add `weave dns-add/remove` commands

Closes #364.
2015-05-07 13:05:45 +01:00
Tom Wilkie 26b6b743f5 Use a template with everything installed for GCE tests. 2015-05-07 11:15:13 +00:00
Andrew Kennedy 633d539b60 New commands to add and remove DNS entries
- Commands dns-add and dns-remove added to weave script
- Updated documentation for WeaveDNS
- Added test script for dns-add and dns-remove
- Added IP address functions matching existing CIDR ones
2015-05-07 12:13:59 +01:00
Tom Wilkie 432662bc86 Create multiple GCE VMs at a time. 2015-05-07 10:20:26 +00:00
Matthias Radestock 04ceb5620b allow user to supply args to docker for remote execution
DOCKER_CLIENT_ARGS - for args to docker
WEAVEEXEC_DOCKER_ARGS - for extra args to `docker run`

Closes #626.
2015-05-07 09:27:22 +01:00
Matthias Radestock 63829a07c5 fix typo 2015-05-06 23:35:32 +01:00
Matthias Radestock 2028861a58 refactor
0 is the default
2015-05-06 22:13:02 +01:00
rade efd704c798 Merge pull request #589 from inercia/weave-588
return NXDOMAIN instead of NOTIMP

Fixes #588.
2015-05-06 19:50:51 +01:00
Matthias Radestock d6f4ebec50 Merge branch 'inercia-weave-common-signals' 2015-05-06 19:40:24 +01:00
Matthias Radestock 14b99fac2a minor refactors 2015-05-06 19:39:44 +01:00
Matthias Radestock 8caa58af26 Merge branch 'weave-common-signals' of https://github.com/inercia/weave into inercia-weave-common-signals 2015-05-06 19:33:06 +01:00
Ilya Dmitrichenko c9bff8b5a7 Publishing updates to the docs on latest release
This uses a special branch `latest_release_doc_updates`, as don't
require docs to be updated in retrospect for releases other then
the latest.
2015-05-06 16:43:28 +01:00
Ilya Dmitrichenko b16d4dff5e Update documentation URLs 2015-05-06 16:42:12 +01:00
Ilya Dmitrichenko a3164db1bf Remove scripts for publishing to gh-pages 2015-05-06 16:42:12 +01:00
Tom Wilkie 213337c210 Fix circle badge 2015-05-06 15:34:01 +00:00
Tom Wilkie f8ce0354ae Merge pull request #634 from weaveworks/circle
Add circle ci badge to README
2015-05-06 16:28:06 +01:00
Tom Wilkie 3c813f71f6 Add circle ci badge to README 2015-05-06 15:15:14 +00:00
Tom Wilkie 9f75e4eba5 Merge pull request #624 from weaveworks/circle
Spin up test VMs on GCE during a circle ci run and use them to run the integration tests.
2015-05-06 15:28:26 +01:00
Tom Wilkie e5ae053b4f Spin up test VMs on GCE during a circle ci run and use them to run the integration tests. 2015-05-06 14:01:41 +00:00
Alvaro Saurin f723d5afd8 Move the router to the new signals handler 2015-05-06 15:07:36 +02:00
Alvaro Saurin dbc4b68301 Use a general signals handler 2015-05-06 15:06:54 +02:00
Matthias Radestock dfac7ea564 Merge branch 'awh-reduced_gob' 2015-05-05 22:10:38 +01:00
Matthias Radestock e656d74633 cosmetic 2015-05-05 22:08:03 +01:00
Matthias Radestock 22e612a738 Merge branch 'reduced_gob' of https://github.com/awh/weave into awh-reduced_gob 2015-05-05 21:36:09 +01:00
Alvaro Saurin ba98135e03 Return NOTIMP for some unimplemented query types, NXDOMAIN otherwise 2015-05-05 21:31:11 +02:00
Matthias Radestock e23c15dab2 hostname->fqdn 2015-05-05 18:58:44 +01:00
Adam Harrison 1703250e87 Merge pull request #628 from rade/605_expose_with_multiple_subnets
prevent interference of expose in other subnets
2015-05-05 17:48:57 +01:00
Matthias Radestock d8f1a04467 only NAT when crossing between subnets
otherwise we can end up NATing traffic inside a subnet, placing it
outside the subnet and thus breaking connectivity.
2015-05-05 16:00:37 +01:00
Matthias Radestock 12ce7d4c18 add some tests of weave expose/hide functionality 2015-05-05 16:00:37 +01:00
Adam Harrison ab68a1c325 Introduce PeerSummary 2015-05-05 10:59:09 +01:00
Matthias Radestock 56038674b1 enable weave script debugging with WEAVE_DEBUG
Closes #618
2015-05-04 10:28:24 +01:00
Matthias Radestock 72e8b3dcc0 fix comment 2015-05-03 21:42:26 +01:00
Matthias Radestock 5aa912c6e9 Merge branch 'dpw-check-libpcap-version'
Fixes #623.
2015-05-02 17:49:00 +01:00
Matthias Radestock e40f1fe9a7 fix typo and reformat 2015-05-02 17:46:21 +01:00
David Wragg be983931fc Produce a better error message when there is no real SetImmediateMode
If gopacket is compiled against an older pcap.h that doesn't have
pcap_set_immediate_mode, it supplies a dummy definition that
always returns PCAP_ERROR.  That becomes "Generic error", which
is not very helpful.  The real pcap_set_set_immediate never
returns PCAP_ERROR, so this turns it into a more informative
message.
2015-05-01 21:40:53 +01:00
Matthias Radestock 64f7e04e14 Merge remote-tracking branch 'upstream/master' 2015-05-01 10:10:11 +01:00
Matthias Radestock 221c07067e Accurately describe WeaveDNS status and link to correct docs 2015-05-01 10:08:45 +01:00
David Wragg 17be43ff9e Merge rade/counter_nonce (PR #617)
replace random nonces with message sequence numbers

Fixes #564.
2015-04-30 14:54:10 +01:00
Matthias Radestock cdf7542c25 update docs 2015-04-30 14:47:16 +01:00
Matthias Radestock 137776bcb2 remove decryptor shutdown
we only needed it because of the nonce channels
2015-04-30 14:47:16 +01:00
Matthias Radestock e223899982 remove nonce transmission from protocol 2015-04-30 14:47:16 +01:00
Matthias Radestock b365c3806b replace random nonces with message sequence numbers in udp crypto 2015-04-30 14:47:16 +01:00
Matthias Radestock 696bfe464c replace random nonces with message sequence numbers in tcp crypto
Since TCP is reliable, we do not need to transmit the nonce itself;
the recipient can simply construct the nonce from its own message
counter. Hence there is no message envelope and we can thus remove a
whole layer of encoding.
2015-04-30 14:47:16 +01:00
rade 87f47a60ae Merge pull request #621 from errordeveloper/fix-binding-hostproc-within-container-where-is-already-bound
Should not pass value of `$PROCFS`

Fixes #622.
2015-04-30 14:06:19 +01:00
Ilya Dmitrichenko 9d0c9f1264 Move definition of $PROCFS (cosmetic)
It's not required to be set at the top, it's only looked at down below.
2015-04-30 13:49:39 +01:00
Michael Bridgen d710684f8e Should not pass value of $PROCFS (close #622)
This is picked from squaremo/weave@d3cc606d5b

The environment variable `PROCFS` was originally added so that the script
could be run in a container. The host's /proc is bind-mounted as /hostproc,
and the script must be told this.

The remote weaving changes got this a bit wrong, by using the value of
`$PROCFS` in a "volume" mount argument (`-v`) to docker -- but when
invoking a container, the left-hand-side of the volume mount should
*always* be `/proc` (and the right-hand-side always `/hostproc`),
since it will always be binding the host's `/proc` to the container's
`/hostproc`.

The gist is that docker looks at host's root filesystem and if you
pass `-v /hostporc:/hostproc` when running from a container where
you have already bind-mounted it, it will end-up creating empty
directory in the target container as there is no `/hostproc` from
docker's perspective.
2015-04-30 13:49:29 +01:00
Adam Harrison b48a93c483 Remove obsolete connsIterator 2015-04-30 13:06:57 +01:00
Adam Harrison 047614b540 Encode/decode []ConnectionSummary instead of []byte 2015-04-30 13:03:18 +01:00
Matthias Radestock 0fc0b9a671 cosmetic 2015-04-30 08:54:42 +01:00
Adam Harrison 26dfc42034 Bump protocol version due to gob restructuring 2015-04-29 16:39:14 +01:00
rade 153731c1f3 Merge pull request #614 from awh/fix_weave_version
`weave version` continues after missing image

Fixes #597.
2015-04-28 18:23:09 +01:00
rade df9e188867 Merge pull request #613 from awh/fix_weave_detach
Detach of unattached container succeeds silently
2015-04-28 18:20:38 +01:00
rade cc62f92988 Merge pull request #616 from awh/require_kernel_3.8.0
Require Linux kernel 3.8.0 or above

Closes #585.
2015-04-28 18:16:40 +01:00
Matthias Radestock 61a5640a95 do not connect to weave port of outbound connection endpoints
There is no point.
2015-04-28 16:07:34 +01:00
Adam Harrison c0d6eb4a22 Require Linux kernel 3.8.0 or above 2015-04-28 15:05:47 +01:00
Adam Harrison 5835838202 Merge pull request #610 from inercia/weave-583-1
External clock provider for the cache
2015-04-28 11:58:35 +01:00
Adam Harrison bafea4bc0e weave version continues after missing image 2015-04-28 11:47:32 +01:00
Adam Harrison 19fce2aa8c Only output colour escapes when attached to a tty 2015-04-28 11:35:08 +01:00
Adam Harrison cf299f59de Detach of unattached container succeeds silently 2015-04-28 11:26:31 +01:00
Alvaro Saurin 3bdfa9b050 Make lint happy 2015-04-28 12:05:24 +02:00
Alvaro Saurin 42fa953053 Minor: logs messages... 2015-04-28 12:05:24 +02:00
Alvaro Saurin 18b7110c31 Stringer'ify some structs 2015-04-28 12:05:24 +02:00
Alvaro Saurin 4aa5e0a97e Minor: logging 2015-04-28 12:05:24 +02:00
Alvaro Saurin ce12b36433 Unit test for multiple answers from a mDNS server 2015-04-28 12:05:21 +02:00
Alvaro Saurin 9e5fe255a5 Improved mocks 2015-04-28 12:02:51 +02:00
Alvaro Saurin d7c154ea41 Removed the "now" parameters and use an external clock provider 2015-04-28 12:02:49 +02:00
Alvaro Saurin 7de2080b9e Added a TTL to the zone records 2015-04-28 11:59:21 +02:00
Alvaro Saurin 22a229c305 Utility function raddrToIP() 2015-04-28 11:52:08 +02:00
Tom Wilkie 840a493e62 Merge pull request #604 from weaveworks/build-and-pubslish-docs-site
Basic automation for publishing documentation
2015-04-28 09:48:26 +01:00
Alvaro c2f55fc836 Merge pull request #609 from rade/combine_nameserver_query_handlers
refactor: eliminate duplication in nameserver query handlers
2015-04-27 19:50:14 +02:00
Ilya Dmitrichenko cfb8e75411 Use shorter URLs for tags 2015-04-27 17:47:59 +01:00
Ilya Dmitrichenko 81ff18565f Generate pointer to latest revision 2015-04-27 17:47:28 +01:00
Ilya Dmitrichenko 83c2bcd418 Build and publish docs on push, not on pull-requests 2015-04-27 17:47:25 +01:00
Ilya Dmitrichenko 0b4b849583 Refactor how sectrets are setup 2015-04-27 17:46:34 +01:00
Ilya Dmitrichenko 6bc601bbfc Basic automation for publishing documentation 2015-04-27 17:46:34 +01:00
Matthias Radestock e45ea65e45 tiny simplifying refactor 2015-04-27 15:51:25 +01:00
Matthias Radestock b74348e59d d'oh 2015-04-27 08:58:02 +01:00
Matthias Radestock ce03f27044 refactor: use 'copy' instead of for loop for slice->array copy 2015-04-27 07:42:52 +01:00
Matthias Radestock 1de86f4312 cosmetic 2015-04-27 07:30:17 +01:00
Matthias Radestock 02a7875eb8 refactor: remove last dependency of crypto on LocalConnection 2015-04-27 07:27:31 +01:00
Matthias Radestock ea4e84cdd3 refactor: (almost) remove dependency of crypto on LocalConnection 2015-04-27 07:14:30 +01:00
Matthias Radestock 29c02d2724 refactor: lift error handling out of Encryptor.Bytes()
in order to reduce dependency on LocalConnection
2015-04-27 07:12:21 +01:00
Matthias Radestock fcd44c37e1 refactor: reduce coupling between connections and crypto 2015-04-27 06:13:41 +01:00
Matthias Radestock ce4c79ed93 refactor: extract commonQueryHandler
to remove duplication between queryHandler and rdnsHandler
2015-04-26 12:40:34 +01:00
Matthias Radestock cb42ec358e even more alignment between queryHandler and rdnsHandler
in preparation of extracting common helper function
2015-04-26 11:40:28 +01:00
Matthias Radestock 1a35b5aa2b align queryHandler and rdnsHandler
increasing their similarity in preparation of refactor
2015-04-26 11:20:44 +01:00
Matthias Radestock 4cc0cf795d explain relationship to TLS 2015-04-26 09:04:09 +01:00
Matthias Radestock ff49bb5125 document why we carry the DF flag in payload 2015-04-25 12:36:05 +01:00
Matthias Radestock d82f60f18b cosmetic 2015-04-25 12:06:20 +01:00
Matthias Radestock d15575212a refactor: no need to record 'df' in two places in state 2015-04-25 12:05:17 +01:00
Matthias Radestock e414f6d059 cosmetic: reformat 2015-04-25 09:17:38 +01:00
rade f217d7badf Merge pull request #606 from awh/592_smoke_test_teardown
Factor out test fixture teardown

Fixes #592.
2015-04-24 12:10:15 +01:00
Adam Harrison d0d8144ef9 Factor out test fixture teardown 2015-04-24 12:03:57 +01:00
Adam Harrison 443b11646d Merge pull request #559 from inercia/weave-338
Support for multiple responses in mDNS
2015-04-24 12:01:43 +01:00
Matthias Radestock 252dacb278 update topology docs to reflect recent changes 2015-04-23 20:46:27 +01:00
rade 2b50a183bb Merge pull request #603 from rade/600_early_death_in_wait_for_status
guard against early container death in wait_for_status

Fixes #600.
2015-04-23 17:31:43 +01:00
Matthias Radestock ed894d52c9 guard against early container death in wait_for_status 2015-04-23 17:28:03 +01:00
Matthias Radestock 4a8fe43036 refactor: simplify "return 1 on error" code 2015-04-23 17:21:56 +01:00
Matthias Radestock af4b7daa77 weave no longer needs to be run as root, so don't say that 2015-04-23 16:40:09 +01:00
Matthias Radestock edf92ef958 add tests for resolving unqualified names 2015-04-23 15:21:26 +01:00
Adam Harrison dd21cfad7f Merge pull request #595 from rade/shrink_dns_tests
shrink dns tests and prevent exit on failure
2015-04-23 15:04:00 +01:00
rade d3128808b9 Merge pull request #601 from fintanr/readme_update
Highlight DNS in Readme
2015-04-23 12:28:23 +01:00
Fintan Ryan ae80d08935 Highlight DNS in Readme 2015-04-23 12:27:07 +01:00
Alvaro Saurin 41ae6e0b7a Make "go vet" happy 2015-04-23 13:01:29 +02:00
Alvaro Saurin 56859a9c30 Run gofmt 2015-04-23 13:01:29 +02:00
Alvaro Saurin 58c8945b32 Support "insistent" queries in the mDNS client, used for obtaining all possible responses from mDNS servers.
Support for multiple responses from Lookup*() methods
Support for multiple records when building packets with make*() methods
Use a common "ZoneRecord" interface for structs that contain names and IPs
2015-04-23 13:01:29 +02:00
Matthias Radestock ec29c2c776 cosmetic 2015-04-23 08:04:12 +01:00
Matthias Radestock 22c04bfc24 don't use $(exec_on ...)
since that will cause the test to exit on failure
2015-04-22 20:09:34 +01:00
Matthias Radestock eebb674232 simplify dns tests
- use 'host' to lookup MX record. It's shorter
- use getent everywhere else for dns lookups, instead of dig; this
  tests more of the o/s name resolution machinery and is generally
  shorter.
- introduce assert_dns_record helper that performs forward and reverse
- lookup introduce some vars for holding constants, so we don't have
  to repeat them
2015-04-22 19:38:04 +01:00
rade 5749d19fee Merge pull request #594 from awh/591_smoke_test_robustness
Improved smoke test robustness

Fixes #591.
2015-04-22 16:52:37 +01:00
Adam Harrison 6a06d0f95f Simplify ping tests to avoid set -e precipitated exit in failure case 2015-04-22 16:46:35 +01:00
Adam Harrison d850ea41a2 Report overall status after running all suites 2015-04-22 16:46:22 +01:00
Adam Harrison 5e01021cba Avoid use of subshell in whitely/greyly 2015-04-22 16:43:38 +01:00
Adam Harrison f3619367a2 Make sourcing of config.sh idempotent 2015-04-22 14:54:33 +01:00
Adam Harrison 849bd52096 Merge pull request #590 from rade/simplify_test_docker_exec
add exec_on test helper
2015-04-22 10:31:37 +01:00
Matthias Radestock a7c9fd272c add exec_on helper
so we don't repeat the remote docker invocation command (complete with
hard-coded port) over and over again.
2015-04-22 09:25:42 +01:00
Matthias Radestock 3ad186d727 make vagrant provision idempotent
Closes #586.
2015-04-21 23:27:46 +01:00
Matthias Radestock 7135508d66 bump docker version requirement for --with-dns
and explain why

Fixes #587.
2015-04-21 22:46:13 +01:00
Matthias Radestock 7221744d9a Merge remote-tracking branch 'upstream/master' 2015-04-21 19:47:20 +01:00
rade 5178e1a66c Merge pull request #580 from awh/use_nsenter
Replace `ip netns exec` with `nsenter`

Closes #458.
2015-04-21 18:23:34 +01:00
Adam Harrison 8326768b9f Upgrade Vagrantfiles to Ubuntu 14.10 2015-04-21 17:28:17 +01:00
Adam Harrison 89e0a6098c Replace ip netns exec calls with nsenter 2015-04-21 17:28:06 +01:00
Adam Harrison 8b9a2142c9 Include util-linux package in weaveexec container 2015-04-21 15:19:56 +01:00
272 changed files with 32749 additions and 9282 deletions

No files matched your search

+35 -11
View File
@@ -29,17 +29,41 @@ coverage.html
# Emacs backup files
*~
# Vim backup files
*.swp
# Project specific
weaver/weaver
weavedns/weavedns
sigproxy/sigproxy
tools/bin
tools/build
prog/weaver/weaver
prog/weavedns/weavedns
prog/weavehosts/weavehosts
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
weave.tar
weavedns.tar
weaveexec.tar
weaveexec/weave
weaveexec/sigproxy
weaveexec/docker*.tgz
.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.*
+3
View File
@@ -0,0 +1,3 @@
[submodule "tools"]
path = tools
url = https://github.com/weaveworks/build-tools
+8 -2
View File
@@ -3,6 +3,9 @@ sudo: false
go:
- 1.4
env:
- PATH="${PATH}:${HOME}/.local/bin"
addons:
apt:
packages:
@@ -11,12 +14,15 @@ addons:
install:
- go clean -i net
- go install -tags netgo std
- go get github.com/golang/lint/golint
- pip install --user --upgrade gsutil
- bin/setup-ci-secrets $encrypted_5ba036e89377_key $encrypted_5ba036e89377_iv
- make travis
- go get ./...
script:
- bin/lint .
- make tests
after_success:
- go get github.com/mattn/goveralls
- $HOME/gopath/bin/goveralls -coverprofile=profile.cov -service=travis-ci
- test "${TRAVIS_PULL_REQUEST}" = "false" && site/build-and-publish.sh
+324
View File
@@ -1,3 +1,327 @@
## 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**
* The minimum Docker version has been increased to 1.6 due to the
[upcoming deprecation of Dockerhub access](https://blog.docker.com/2015/10/docker-hub-deprecation-1-5/)
for old clients. From December 7th onwards previous versions of the
`weave` script will fail to pull down images from the hub; if you
are unable to upgrade to 1.3.1 immediately you can work around this
by running `weave --local setup` in conjunction with a compatible
Docker client installation
* Docker networking plugin now works with older kernels and allows you
to configure the MTU
More details in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.3.1).
## Release 1.3.0
**Highlights**
This release includes a Docker Plugin, so you have the option to use
Weave Net that way. More details in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.3.0).
## Release 1.2.1
This release contains a number of bug fixes and minor
enhancements. More details in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.2.1).
The release is fully compatible with 1.2.0 versions, so existing
clusters can be upgraded incrementally.
## Release 1.2.0
**Highlights**
This release introduces the
[Fast Data Path](http://docs.weave.works/weave/master/head/features.html#fast-data-path),
which allows Weave networks to operate at near wire-level speeds. This
new feature is enabled by default.
Other highlights:
- auto-configuration of TLS for the Weave Docker API proxy, making it
easier to run Weave on Macs and in conjunction with tools like
Docker Swarm
- support for restart policies on application containers and weave
infrastructure containers
- better compatibility with recent and future Docker versions
More details in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.2.0).
## Release 1.1.2
This release contains a small number of bug fixes. More details in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.1.2).
The release is fully compatible with 1.1.0 versions, so existing
clusters can be upgraded incrementally. When upgrading from 1.0.x,
note the compatibility information in the
[Installation & Upgrading instructions for Weave 1.1.0](https://github.com/weaveworks/weave/releases/tag/v1.1.0).
## Release 1.1.1
This release contains a number of bug fixes and minor
enhancements. More details in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.1.1).
The release is fully compatible with 1.1.0 versions, so existing
clusters can be upgraded incrementally. When upgrading from 1.0.x,
note the compatibility information in the
[Installation & Upgrading instructions for Weave 1.1.0](https://github.com/weaveworks/weave/releases/tag/v1.1.0).
## Release v1.1.0
**Highlights**:
- `weave launch` now launches all weave components, simplifying
startup.
- `weave status` has been completely revamped, with a much improved
presentation of the information, and the option to select and output
data in JSON.
- weaveDNS has been rewritten and embedded in the router. The new
implementation simplifies configuration, improves performance, and
provides fault resilience for services.
- the weave Docker API proxy now provides an even more seamless user
experience, and enables easier integration of weave with other
systems such as kubernetes.
- many usability improvements
- a few minor bug fixes, including a couple of security
vulnerabilities
More details in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.1.0).
## Release 1.0.3
This release contains a weaveDNS feature enhancement as well as minor fixes for
improved stability and robustness.
More details in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.0.3).
The release is fully compatible with other 1.0.x versions, so existing
clusters can be upgraded incrementally.
## Release 1.0.2
This release fixes a number of bugs, including some security
vulnerabilities in the Weave Docker API proxy, hangs and failures in
address allocation, and sporadic failures in name resolution.
More details in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.0.2).
The release is fully compatible with other 1.0.x versions, so existing
clusters can be upgraded incrementally.
## Release 1.0.1
This is a bug fix release, addressing the following issue:
- executing `docker run` from a Docker 1.7 client against the weave
proxy would report a `no such image` error when the requested image
wasn't present on the Docker host, instead of downloading it.
## Release 1.0.0
**Highlights**:
- It is now easier than ever to start containers and for them to
communicate, across multiple hosts. Automatic IP address allocation,
and name resolution via weaveDNS are now enabled by default, and the
proxy has become more fully-featured. In short, once weave has been
launched the following is possible:
````
host1$ docker run --name=pingme -dti ubuntu
host2$ docker run -ti ubuntu
root@d11e9287f65b:/# ping pingme
````
- Containers can now be
[load-balanced](http://docs.weave.works/weave/latest_release/weavedns.html#load-balancing)
easily.
- IP address allocation is now available across [multiple
subnets](http://docs.weave.works/weave/latest_release/ipam.html#range),
and hence can be employed when running multiple, isolated
applications.
- The proxy now supports [TLS
connections](http://docs.weave.works/weave/latest_release/proxy.html#tls),
enabling its deployment when the communication between docker
clients and the server must be secured.
There are many other new features, plus the usual assortment of bug
fixes and improvements under the hood. More detail below and in the
[change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.0.0).
*NB: This release changes the weave protocol version. Therefore, when
upgrading an existing installation, all hosts need to be upgraded in
order to for them to be able to communicate and form a network.*
## Release 0.11.2
This is a bug fix release, addressing the following issues:
- `weave run` did not respect DOCKER_CLIENT_ARGS. #855
- Negative result cache did not expire if requeried within TTL. #845
More details in the [change
log](https://github.com/weaveworks/weave/issues?q=milestone%3A0.11.2).
*NB: This release does not change the weave protocol version.
Therefore, when upgrading an existing 0.11 installation incrementally,
connectivity between peers will be retained.*
## Release 0.11.1
This is a bug fix release, addressing the following issues:
- The IP Allocator could crash in some relatively rare
circumstances. #782/#783.
- When the proxy failed to attach a container to the weave network,
there was no failure indication and descriptive error anywhere, and
the application process would still start. Now an error is reported
to the client (i.e. typically the Docker CLI), recorded in the proxy
logs, and the container is terminated. #788/#799.
- `weave launch-proxy --with-ipam` failed to configure the entrypoint
and DNS unless a (possibly blank) `WEAVE_CIDR` was
specified. Affected containers could start the application process
w/o the weave network interface being available, and without
functioning name resolution for the weaveDNS domain.
#744/#747/#751/#752
- The `weave status` output for the IP Allocator was misleadingly
conveying a sense of brokenness when no IP allocation requests had
been made yet. #787/#801
- When invoking `weave launch-proxy` twice, the second invocation
would output a blank line and terminate with a success exit
status. Now it reports that the proxy is already running and exits
with a non-zero status. #767/#780
- `weave launch-proxy` was not respecting WEAVEPROXY_DOCKER_ARGS, so
any user-supplied custom configuration for the weaveproxy container
was ignored. #755/#780
- The proxy was not intercepting API calls to the unversioned (1.0)
Docker Remote API. Hence none of the weave functionality was
available when Docker clients were using that version of the
API. #770/#774/#777,#809
- The proxy would crash when certain elements of the
`/containers/create` JSON were missing. We are not aware of any
Docker clients doing this, but it's been fixed regardless. #778/#777
More details in the [change
log](https://github.com/weaveworks/weave/issues?q=milestone%3A0.11.1).
*NB: This release does not change the weave protocol version.
Therefore, when upgrading an existing 0.11 installation incrementally,
connectivity between peers will be retained.*
## Release 0.11.0
**Highlights**:
- **automatic IP Address Management (IPAM)**, which allows application
containers to be started and attached to the weave network without
needing to supply an IP address.
- **proxy** for automatically attaching containers started with
ordinary `docker run`, or the Docker remote API, to the weave
network.
- ability to **add/remove extra DNS records**.
- performance and scalability improvements
- fixes for a small number of bugs discovered during testing
More detail in the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A0.11.0)
*NB: This release changes the weave protocol version. Therefore, when
upgrading an existing installation, all hosts need to be upgraded in
order to for them to be able to communicate and form a network.*
## Release 0.10.0
**Highlights**:
+131 -64
View File
@@ -1,100 +1,167 @@
PUBLISH=publish_weave publish_weavedns publish_weaveexec
PUBLISH=publish_weave publish_weaveexec publish_plugin
.DEFAULT: all
.PHONY: all update tests publish $(PUBLISH) clean prerequisites build travis
.PHONY: all update tests lint publish $(PUBLISH) clean clean-bin prerequisites build travis run-smoketests
# If you can use docker without being root, you can do "make SUDO="
SUDO=sudo
DOCKERHUB_USER=weaveworks
WEAVE_VERSION=git-$(shell git rev-parse --short=12 HEAD)
WEAVER_EXE=weaver/weaver
WEAVEDNS_EXE=weavedns/weavedns
SIGPROXY_EXE=sigproxy/sigproxy
WEAVER_EXE=prog/weaver/weaver
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) $(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) $(DOCKERPLUGIN_UPTODATE)
WEAVER_IMAGE=$(DOCKERHUB_USER)/weave
WEAVEDNS_IMAGE=$(DOCKERHUB_USER)/weavedns
WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec
WEAVER_EXPORT=weave.tar
WEAVEDNS_EXPORT=weavedns.tar
WEAVEEXEC_EXPORT=weaveexec.tar
DOCKERPLUGIN_IMAGE=$(DOCKERHUB_USER)/plugin
WEAVEEXEC_DOCKER_VERSION=1.3.1
DOCKER_DISTRIB=weaveexec/docker-$(WEAVEEXEC_DOCKER_VERSION).tgz
IMAGES=$(WEAVER_IMAGE) $(WEAVEEXEC_IMAGE) $(DOCKERPLUGIN_IMAGE)
WEAVE_EXPORT=weave.tar.gz
WEAVEEXEC_DOCKER_VERSION=1.6.2
DOCKER_DISTRIB=prog/weaveexec/docker-$(WEAVEEXEC_DOCKER_VERSION).tgz
DOCKER_DISTRIB_URL=https://get.docker.com/builds/Linux/x86_64/docker-$(WEAVEEXEC_DOCKER_VERSION).tgz
NETGO_CHECK=@strings $@ | grep cgo_stub\\\.go >/dev/null || { \
rm $@; \
echo "\nYour go standard library was built without the 'netgo' build tag."; \
echo "To fix that, run"; \
echo " sudo go clean -i net"; \
echo " sudo go install -tags netgo std"; \
false; \
}
BUILD_FLAGS=-ldflags "-extldflags \"-static\" -X main.version $(WEAVE_VERSION)" -tags netgo
all: $(WEAVER_EXPORT) $(WEAVEDNS_EXPORT) $(WEAVEEXEC_EXPORT)
PACKAGE_BASE=$(shell go list -e ./)
travis: $(WEAVER_EXE) $(WEAVEDNS_EXE)
all: $(WEAVE_EXPORT) $(RUNNER_EXE) $(TEST_TLS_EXE)
travis: $(EXES)
update:
go get -u -f -v -tags -netgo ./$(dir $(WEAVER_EXE)) ./$(dir $(WEAVEDNS_EXE))
go get -u -f -v -tags netgo $(addprefix ./,$(dir $(EXES)))
$(WEAVER_EXE) $(WEAVEDNS_EXE): common/*.go
$(WEAVER_EXE) $(WEAVEPROXY_EXE): common/*.go common/*/*.go net/*.go
ifeq ($(COVERAGE),true)
$(eval COVERAGE_MODULES := $(shell (go list ./$(@D); go list -f '{{join .Deps "\n"}}' ./$(@D) | grep "^$(PACKAGE_BASE)/") | paste -s -d,))
go get -t -tags netgo ./$(@D)
go test -c -o ./$@ $(BUILD_FLAGS) -v -covermode=atomic -coverpkg $(COVERAGE_MODULES) ./$(@D)/
else
go get -tags netgo ./$(@D)
go build -ldflags "-extldflags \"-static\" -X main.version $(WEAVE_VERSION)" -tags netgo -o $@ ./$(@D)
@strings $@ | grep cgo_stub\\\.go >/dev/null || { \
rm $@; \
echo "\nYour go standard library was built without the 'netgo' build tag."; \
echo "To fix that, run"; \
echo " sudo go clean -i net"; \
echo " sudo go install -tags netgo std"; \
false; \
}
go build $(BUILD_FLAGS) -o $@ ./$(@D)
endif
$(NETGO_CHECK)
$(WEAVER_EXE): router/*.go weaver/main.go
$(WEAVEDNS_EXE): nameserver/*.go weavedns/main.go
$(NETCHECK_EXE): common/*.go common/*/*.go net/*.go
go get -tags netgo ./$(@D)
go build $(BUILD_FLAGS) -o $@ ./$(@D)
$(NETGO_CHECK)
# Sigproxy needs separate rule as it fails the netgo check in the main
# build stanza due to not importing net package
$(SIGPROXY_EXE): sigproxy/main.go
go build -o $@ ./$(@D)
$(WEAVER_EXE): router/*.go mesh/*.go ipam/*.go ipam/*/*.go nameserver/*.go prog/weaver/*.go
$(WEAVEPROXY_EXE): proxy/*.go prog/weaveproxy/main.go
$(NETCHECK_EXE): prog/netcheck/netcheck.go
$(WEAVER_EXPORT): weaver/Dockerfile $(WEAVER_EXE)
$(SUDO) docker build -t $(WEAVER_IMAGE) weaver
$(SUDO) docker save $(WEAVER_IMAGE):latest > $@
# 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
$(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
$(WEAVEDNS_EXPORT): weavedns/Dockerfile $(WEAVEDNS_EXE)
$(SUDO) docker build -t $(WEAVEDNS_IMAGE) weavedns
$(SUDO) docker save $(WEAVEDNS_IMAGE):latest > $@
$(SIGPROXY_EXE) $(DOCKERTLSARGS_EXE) $(DOCKERPLUGIN_EXE) $(TEST_TLS_EXE):
go get -tags netgo ./$(@D)
go build $(BUILD_FLAGS) -o $@ ./$(@D)
$(WEAVEEXEC_EXPORT): weaveexec/Dockerfile $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE)
cp weave weaveexec/weave
cp sigproxy/sigproxy weaveexec/sigproxy
cp $(DOCKER_DISTRIB) weaveexec/docker.tgz
$(SUDO) docker build -t $(WEAVEEXEC_IMAGE) weaveexec
$(SUDO) docker save $(WEAVEEXEC_IMAGE):latest > $@
$(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) -o $@ ./$(@D)
$(WEAVER_UPTODATE): prog/weaver/Dockerfile $(WEAVER_EXE)
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -t $(WEAVER_IMAGE) prog/weaver
touch $@
$(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_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_HOST=$(DOCKER_HOST) docker save $(addsuffix :latest,$(IMAGES)) | gzip > $@
$(DOCKER_DISTRIB):
curl -o $(DOCKER_DISTRIB) $(DOCKER_DISTRIB_URL)
tests:
echo "mode: count" > profile.cov
fail=0 ; \
for dir in $$(find . -type f -name '*_test.go' | xargs -n1 dirname | sort -u); do \
output=$$(mktemp cover.XXXXXXXXXX) ; \
if ! go test -tags netgo -covermode=count -coverprofile=$$output $$dir ; then \
fail=1 ; \
fi ; \
if [ -f $$output ]; then \
tail -n +2 <$$output >>profile.cov; \
rm $$output; \
fi \
done ; \
exit $$fail
go tool cover -html=profile.cov -o=coverage.html
tests: tools/.git
tools/test
$(PUBLISH): publish_%:
$(SUDO) docker tag -f $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
$(SUDO) docker push $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
$(SUDO) docker push $(DOCKERHUB_USER)/$*:latest
lint: tools/.git
tools/lint -nocomment -notestpackage .
tools/.git:
git submodule update --init
$(RUNNER_EXE): tools/.git
make -C tools/runner
$(PUBLISH): publish_%: $(IMAGES_UPTODATE)
$(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_HOST=$(DOCKER_HOST) docker push $(DOCKERHUB_USER)/$*:latest
endif
publish: $(PUBLISH)
clean:
-$(SUDO) docker rmi $(WEAVER_IMAGE) $(WEAVEDNS_IMAGE) $(WEAVEEXEC_IMAGE)
rm -f $(WEAVER_EXE) $(WEAVEDNS_EXE) $(SIGPROXY_EXE) $(WEAVER_EXPORT) $(WEAVEDNS_EXPORT) $(WEAVEEXEC_EXPORT)
clean-bin:
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(IMAGES)
go clean -r $(addprefix ./,$(dir $(EXES)))
rm -f $(EXES) $(IMAGES_UPTODATE) $(WEAVE_EXPORT)
clean: clean-bin
rm -rf test/tls/*.pem test/coverage.* test/coverage
build:
$(SUDO) go clean -i net
$(SUDO) go install -tags netgo std
$(MAKE)
run-smoketests: all
cd test && ./setup.sh && ./run_all.sh
+90 -71
View File
@@ -1,9 +1,20 @@
# Weave - the Docker network
# Weave - weaving containers into applications
[![Build Status](https://travis-ci.org/weaveworks/weave.svg?branch=master)](https://travis-ci.org/weaveworks/weave) [![Coverage Status](https://coveralls.io/repos/weaveworks/weave/badge.svg)](https://coveralls.io/r/weaveworks/weave)
[![Build Status](https://travis-ci.org/weaveworks/weave.svg?branch=master)](https://travis-ci.org/weaveworks/weave) [![Integration Tests](https://circleci.com/gh/weaveworks/weave/tree/master.svg?style=shield&circle-token=4933c7dabb3d0383e62117565cb9d16df7b1a811)](https://circleci.com/gh/weaveworks/weave) [![Coverage Status](https://coveralls.io/repos/weaveworks/weave/badge.svg)](https://coveralls.io/r/weaveworks/weave)
# About Weaveworks
[Weaveworks](http://weave.works) is the company that develops Weave -
the most productive way for developers to connect, observe and control
Docker containers. To learn about our products, including getting
started tutorials, visit our [website](http://weave.works) and
[documentation](http://docs.weave.works) or continue to read about
some of the more technical aspects of Weave in this readme.
# Weave
Weave creates a virtual network that connects Docker containers
deployed across multiple hosts.
deployed across multiple hosts and enables their automatic discovery.
![Weave Virtual Network](/docs/virtual-network.png?raw=true "Weave Virtual Network")
@@ -29,21 +40,30 @@ capabilities, so these can continue to be used by containers.
## Installation
Ensure you are running Linux (kernel 3.5 or later) and have Docker
(version 1.3.1 or later) installed. Then install weave with
Ensure you are running Linux (kernel 3.8 or later) and have Docker
(version 1.6.0 or later) installed. Then install weave with
sudo wget -O /usr/local/bin/weave \
https://github.com/weaveworks/weave/releases/download/latest_release/weave
sudo curl -L git.io/weave -o /usr/local/bin/weave
sudo chmod a+x /usr/local/bin/weave
For usage on OSX (with Docker Machine) you first need to make sure
that a VM is running and configured, as shown in [the Docker Machine
documentation](https://docs.docker.com/installation/mac/#from-your-shell).
Then you can launch weave directly from the OSX host.
CoreOS users see [here](https://github.com/fintanr/weave-gs/blob/master/coreos-simple/user-data) for an example of installing weave using cloud-config.
Amazon ECS users see
[here](https://github.com/weaveworks/guides/blob/master/aws-ecs/LATESTAMIs.md)
for the latest Weave AMIs and
[here](http://weave.works/guides/service-discovery-with-weave-aws-ecs.html) to get started with Weave on ECS.
Weave respects the environment variable `DOCKER_HOST`, so you can run
it locally to control a weave network on a remote host.
## Quick Start Screencast
<a href="http://youtu.be/k6r7yuSr0hE" alt="Click to watch the screencast" target="_blank">
<a href="https://youtu.be/kihQCCT1ykE" alt="Click to watch the screencast" target="_blank">
<img src="/docs/hello-screencast.png" />
</a>
@@ -53,99 +73,98 @@ Say you have docker running on two hosts, accessible to each other as
`$HOST1` and `$HOST2`, and want to deploy an application consisting of
two containers, one on each host.
On `$HOST1` run (as root)
On $HOST1 we run:
host1# weave launch
host1# C=$(weave run 10.2.1.1/24 -t -i ubuntu)
host1$ weave launch
host1$ eval $(weave env)
host1$ docker run --name a1 -ti ubuntu
The first line starts the weave router, in a container. This needs to
be done once on each host. The required docker image for the weave
router container is downloaded automatically. There is also a `weave
setup` command for downloading this and other images required for
weave operation; this is a strictly optional step which is especially
useful for automated installation of weave and ensures that any
subsequent weave commands do not encounter delays due to image
downloading.
> NB: If the first command results in an error like
> `http:///var/run/docker.sock/v1.19/containers/create: dial unix
> /var/run/docker.sock: permission denied. Are you trying to connect
> to a TLS-enabled daemon without TLS?` then you likely need to be
> 'root' in order to connect to the Docker daemon. If so, run the
> above and all subsequent commands in a *single* root shell (e.g. one
> created with `sudo -s`). Do *not* prefix individual commands with
> `sudo`, since some commands modify environment entries and hence
> they all need to be executed from the same shell.
The second line runs our application container. We give it an IP
address and network, in
[CIDR notation](http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation).
`weave run` invokes `docker run -d` with all the parameters following
the IP address and network. So we could be launching any container
this way; here we just take a stock ubuntu container and launch a
shell in it. There's also a `weave start` command, which invokes
`docker start` for starting existing containers.
The first line runs weave. The second line configures our environment
so that containers launched via the docker command line are
automatically attached to the weave network. Finally, we run our
application container.
If our application consists of more than one container on this host we
simply launch them with a variation on that second line.
That's it! If our application consists of more than one container on
this host we simply launch them with `docker run` as appropriate.
The IP addresses and netmasks can be anything you like, but make sure
they don't conflict with any IP ranges in use on the hosts or IP
addresses of external services the hosts or containers need to connect
to. The same IP range must be used everywhere, and the individual IP
addresses must, of course, be unique.
Next we repeat similar steps on `$HOST2`...
We repeat similar steps on `$HOST2`...
host2$ weave launch $HOST1
host2$ eval $(weave env)
host2$ docker run --name a2 -ti ubuntu
host2# weave launch $HOST1
host2# C=$(weave run 10.2.1.2/24 -t -i ubuntu)
The only difference, apart from the choice of IP address for the
application container, is that we tell our weave that it should peer
with the weave on `$HOST1` (specified as the IP address or hostname,
and optional `:port`, by which `$HOST2` can reach it). NB: if there is
a firewall between `$HOST1` and `$HOST2`, you must open the weave port
(6783 by default; this can be overriden by setting `WEAVE_PORT`) for
TCP and UDP.
The only difference, apart from the name of the application container,
is that we tell our weave that it should peer with the weave on
`$HOST1` (specified as the IP address or hostname, and optional
`:port`, by which `$HOST2` can reach it). NB: if there is a firewall
between `$HOST1` and `$HOST2`, you must permit traffic to the weave
control port (TCP 6783) and data ports (UDP 6783/6784).
Note that we could instead have told the weave on `$HOST1` to connect to
`$HOST2`, or told both about each other. Order does not matter here;
weave automatically (re)connects to peers when they become
available. Also, we can tell weave to connect to multiple peers by
supplying multiple addresses, separated by spaces. And we can
[add peers dynamically](http://weaveworks.github.io/weave/features.html#dynamic-topologies).
[add peers dynamically](http://docs.weave.works/weave/latest_release/features.html#dynamic-topologies).
Weave must be started once per host. The relevant container images are
pulled down on demand, but if you wish you can preload them by running
`weave setup` - this is particularly useful for automated deployments,
and ensures that there are no delays during later operations.
Now that we've got everything set up, let's see whether our containers
can talk to each other...
On `$HOST1`...
In the container started on `$HOST1`...
host1# docker attach $C
root@28841bd02eff:/# ping -c 1 -q 10.2.1.2
PING 10.2.1.2 (10.2.1.2): 48 data bytes
--- 10.2.1.2 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max/stddev = 1.048/1.048/1.048/0.000 ms
root@a1:/# ping -c 1 -q a2
PING a2.weave.local (10.40.0.2) 56(84) bytes of data.
--- a2.weave.local ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.341/0.341/0.341/0.000 ms
Similarly, on `$HOST2`...
Similarly, in the container started on `$HOST2`...
host2# docker attach $C
root@f76829496120:/# ping -c 1 -q 10.2.1.1
PING 10.2.1.1 (10.2.1.1): 48 data bytes
--- 10.2.1.1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max/stddev = 1.034/1.034/1.034/0.000 ms
root@a2:/# ping -c 1 -q a1
PING a1.weave.local (10.32.0.2) 56(84) bytes of data.
--- a1.weave.local ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.366/0.366/0.366/0.000 ms
So there we have it, two containers on separate hosts happily talking
to each other.
## Find out more
* [Documentation homepage](http://weaveworks.github.io/weave/)
* [Features](http://weaveworks.github.io/weave/features.html)
* [Troubleshooting](http://weaveworks.github.io/weave/troubleshooting.html)
* [Building](http://weaveworks.github.io/weave/building.html)
* [How it works](http://weaveworks.github.io/weave/how-it-works.html)
* [WeaveDNS README](https://github.com/weaveworks/weave/tree/master/weavedns#readme)
* [Documentation homepage](http://docs.weave.works/weave/latest_release/)
* [Getting Started Guides](http://weave.works/guides/)
* [Features](http://docs.weave.works/weave/latest_release/features.html)
* [Troubleshooting](http://docs.weave.works/weave/latest_release/troubleshooting.html)
* [Building](http://docs.weave.works/weave/latest_release/building.html)
* [How it works](http://docs.weave.works/weave/latest_release/how-it-works.html)
## Contact Us
Found a bug, want to suggest a feature, or have a question?
[File an issue](https://github.com/weaveworks/weave/issues), or email
help@weave.works. When reporting a bug, please include which version of
weave you are running, as shown by `weave version`.
Found a bug, want to suggest a feature, or have a question? You can
[File an issue](https://github.com/weaveworks/weave/issues). And
there is a [Weave Users Google
Group](https://groups.google.com/a/weave.works/forum/#!forum/weave-users),
which you can post to by email at weave-users@weave.works. Or you can
contact Weaveworks directly at help@weave.works. When reporting a
bug, please include which version of weave you are running, as shown
by `weave version`.
Follow weave on Twitter:
Follow on Twitter:
[@weaveworks](https://twitter.com/weaveworks).
Read the Weave blog:
Vendored
+21 -23
View File
@@ -1,12 +1,25 @@
VAGRANTFILE_API_VERSION = "2"
require './vagrant-common.rb'
vm_ip = "172.16.0.3" # arbitrary private IP
pkgs = "lxc-docker aufs-tools build-essential ethtool iputils-arping libpcap-dev git mercurial bc"
pkgs = %w(
docker-engine
aufs-tools
build-essential
ethtool
iputils-arping
libpcap-dev
git
mercurial
bc
)
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "phusion/ubuntu-14.04-amd64"
config.vm.box_url = "https://oss-binaries.phusionpassenger.com/vagrant/boxes/latest/ubuntu-14.04-amd64-vbox.box"
config.vm.box = "ubuntu/ubuntu-15.04-amd64"
config.vm.box_url = "https://cloud-images.ubuntu.com/vagrant/vivid/current/vivid-server-cloudimg-amd64-vagrant-disk1.box"
config.vm.network "private_network", ip: vm_ip
config.vm.provider :virtualbox do |vb|
@@ -16,27 +29,12 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.synced_folder ".", "/vagrant", disabled: true
config.vm.synced_folder ".", "/home/vagrant/src/github.com/weaveworks/weave"
config.vm.provision :shell, :inline => "ln -s /home/vagrant/src/github.com/weaveworks/weave /home/vagrant/weave"
config.vm.provision :shell, :inline => "sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9"
config.vm.provision :shell, :inline => "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
pkg_cmd = "export DEBIAN_FRONTEND=noninteractive; " \
"apt-get update -qq; " \
"apt-get install -qq --no-install-recommends #{pkgs}"
install_go_toolchain = "curl -s https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz | tar xz -C /usr/local"
config.vm.provision :shell, :inline => pkg_cmd
config.vm.provision :shell, :inline => install_go_toolchain
config.vm.provision :shell, :inline => "usermod -a -G docker vagrant; "
config.vm.provision :shell, :inline => "echo export GOPATH=/home/vagrant >> /home/vagrant/.bashrc"
config.vm.provision :shell, :inline => "echo export PATH=/usr/local/go/bin:${PATH} >> /home/vagrant/.bashrc"
config.vm.provision :shell, :inline => "chown -R vagrant:vagrant /home/vagrant/src"
config.vm.provision :shell, :inline => "/usr/local/go/bin/go clean -i net; /usr/local/go/bin/go install -tags netgo std"
config.vm.provision :shell, :inline => "echo 'DOCKER_OPTS=\"-H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375\"' >> /etc/default/docker"
config.vm.provision :shell, :inline => "service docker restart"
install_build_deps config.vm, pkgs
install_go_toochain config.vm
tweak_user_env config.vm
tweak_docker_daemon config.vm
cleanup config.vm
end
+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
}
-81
View File
@@ -1,81 +0,0 @@
#!/bin/bash
# This scipt lints go files for common errors.
#
# Its runs gofmt and go vet, and optionally golint and
# gocyclo, if they are installed.
#
# With no arguments, it lints the current files staged
# for git commit. Or you can pass it explicit filenames
# (or directories) and it will lint them.
#
# To use this script automatically, run:
# ln -s ../../bin/lint .git/hooks/pre-commit
set -eu
function lint_go {
filename="$1"
result=0
if [ -n "$(gofmt -l "${filename}")" ]; then
result=1
echo "${filename}: run gofmt -w ${filename}!"
fi
go tool vet "${filename}" || result=$?
# golint is completely optional. If you don't like it
# don't have it installed.
if type golint >/dev/null 2>&1; then
# golint doesn't set an exit code it seems
lintoutput=$(golint "${filename}")
if [ "$lintoutput" != "" ]; then
result=1
echo "$lintoutput"
fi
fi
# gocyclo is completely optional. If you don't like it
# don't have it installed. Also never blocks a commit,
# it just warns.
if type gocyclo >/dev/null 2>&1; then
gocyclo -over 25 "${filename}" | while read line; do
echo "${filename}": higher than 25 cyclomatic complexity - "${line}"
done
fi
return $result
}
function lint {
filename="$1"
ext="${filename##*\.}"
# Don't lint this script
if [ "${filename}" = "bin/lint" ]; then
return
fi
case "$ext" in
go) lint_go "${filename}"
;;
esac
}
function lint_files {
lint_result=0
while read filename; do
lint "${filename}" || lint_result=1
done
exit $lint_result
}
function list_files {
if [ $# -gt 0 ]; then
find "$@" -type f
else
git diff --cached --name-only
fi
}
list_files "$@" | lint_files
-20
View File
@@ -1,20 +0,0 @@
#! /bin/bash
set -e
# For info on why/how to use this script, see
# https://github.com/weaveworks/weave/wiki/WorkingOnWeave#working-on-docs
MERGE_ARGS="-X subtree=site"
merge_dir=$(mktemp -d -t weave-merge.XXXXXX)/weave
git clone -b gh-pages . $merge_dir
pushd $merge_dir
git pull --ff-only git@github.com:weaveworks/weave gh-pages
git fetch origin master
git merge $MERGE_ARGS -e -m "Merge master into gh-pages" origin/master
popd
git fetch $merge_dir gh-pages:gh-pages
# rm -rf $merge_dir
+14 -2
View File
@@ -14,19 +14,30 @@ set -e
START=${FIRST_WEAVE:-1}
COUNT=${NUM_WEAVES:-28}
FINISH=$((START+COUNT-1))
PORT=6783
weavedir=$(dirname $0)/..
# Generate a random MAC value - copied from 'weave' script
random_mac() {
# In the first byte of the MAC, the 'multicast' bit should be
# clear and 'locally administered' bit should be set. All other
# bits should be random.
od -txC -An -N6 /dev/urandom | ( read a b c d e f && printf "%02x:$b:$c:$d:$e:$f" $((0x$a & ~1 | 2)) )
}
case "$1" in
launch)
shift 1
echo $(date -R) launching
for i in $(seq $START $FINISH); do
WEAVE_CONTAINER_NAME=weave$i $weavedir/weave test-launch "$@"
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
if [ $START -ne 1 ] ; then
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
@@ -35,11 +46,12 @@ 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
echo Stopping weave$i
WEAVE_CONTAINER_NAME=weave$i $weavedir/weave stop
WEAVE_CONTAINER_NAME=weave$i $weavedir/weave stop-router
done
;;
esac
-3
View File
@@ -1,3 +0,0 @@
#! /bin/sh
set -e
git push git@github.com:weaveworks/weave gh-pages:gh-pages
+156 -65
View File
@@ -10,15 +10,53 @@ RELEASE_DESCRIPTION=${RELEASE_DESCRIPTION:-"Weaving Containers into Applications
PWD=`pwd`
setup() {
## Get the new version number from the most recent tag
if ! LATEST_TAG=$(git describe --abbrev=0 --match='v*' 2>/dev/null) ; then
echo "Could not find an annotated 'v*' version tag." >&2
exit 1
infer_release_type() {
if echo $1 | grep -qE '^v[0-9]+\.[0-9]+\.0+$' ; then
echo MAINLINE
elif echo $1 | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$' ; then
echo BRANCH
else
echo PRERELEASE
fi
}
setup() {
# Ensure we have exactly one annotated tag pointing at HEAD
HEAD_TAGS=$(git tag --points-at HEAD)
case $(echo $HEAD_TAGS | wc -w) in
1)
if [ $HEAD_TAGS != "latest_release" ] ; then
LATEST_TAG=$HEAD_TAGS
else
echo "Cannot determine version - latest_release points at HEAD" >&2
exit 1
fi
;;
0)
echo "Cannot determine version - no tags point at HEAD" >&2
exit 1
;;
*)
echo "Cannot determine version - multiple tags point at HEAD:" >&2
for TAG in $HEAD_TAGS ; do
echo -e "\t$TAG" >&2
done
exit 1
;;
esac
RELEASE_TYPE=$(infer_release_type $LATEST_TAG)
echo "== Inferred release type $RELEASE_TYPE from tag $LATEST_TAG"
LATEST_TAG_SHA=$(git rev-parse $LATEST_TAG)
LATEST_TAG_COMMIT_SHA=$(git rev-list -1 $LATEST_TAG)
LATEST_RELEASE_SHA=$(git rev-parse latest_release)
VERSION=${LATEST_TAG#v}
LATEST_RELEASE_COMMIT_SHA=$(git rev-list -1 latest_release)
if [ "$RELEASE_TYPE" != 'PRERELEASE' ] ; then
VERSION=${LATEST_TAG#v}
else
VERSION=${LATEST_TAG}
fi
# NB does not check that this tag is on master
RELEASE_DIR=./releases/$LATEST_TAG
}
@@ -39,8 +77,8 @@ build() {
cd $RELEASE_DIR
## Check that the top changelog entry is this version
if ! latest_changelog=$(grep -o "## Release [0-9].*" -m1 ./CHANGELOG.md) || \
! [ `echo "$latest_changelog" | grep -o '[0-9][^ ]*'` == "$VERSION" ]; then
if ! latest_changelog=$(grep -oP '(?<=^## Release ).*' -m1 ./CHANGELOG.md) || \
! [ `echo "$latest_changelog" = "$VERSION"` ]; then
echo -e "\u2757 Latest changelog entry \"$latest_changelog\" does not match the release version $VERSION" >&2
exit 1
fi
@@ -61,7 +99,7 @@ build() {
fi
## Run tests with the distributables, including version check
v=$(./weaver/weaver --version | grep -o '[0-9].*')
v=$(./prog/weaver/weaver --version | grep -oP '(?<=^weave router ).*')
if ! [ "$v" == "$VERSION" ]; then
echo "Version of distributable "$v" does not match release version $VERSION" >&2
exit 1
@@ -71,24 +109,13 @@ build() {
echo '** Release artefacts in' $RELEASE_DIR
}
publish() {
draft() {
setup
cd $PWD/$RELEASE_DIR
echo "== Sanity checks"
if ! which github-release >/dev/null; then
echo "Please install git-release:" >&2
echo -e "\tgo get github.com/aktau/github-release" >&2
echo "and create a git token per https://github.com/aktau/github-release" >&2
exit 1
fi
if ! [ $(git rev-list -1 $LATEST_TAG) == $(git rev-list -1 latest_release) ]; then
echo -e "\u2757 The tag latest_release does not point to the same commit as $LATEST_TAG"
exit 1
fi
## Check that the tag exists by looking at github
if ! curl -sSf https://api.github.com/repos/$GITHUB_USER/weave/git/tags/$LATEST_TAG_SHA >/dev/null 2>&1; then
echo -e "\u2757 Tag $LATEST_TAG is not on GitHub, or is not the same as the local tag" >&2
@@ -98,19 +125,9 @@ publish() {
exit 1
fi
## Check that the 'latest_release' tag exists by looking at github
if ! curl -sSf https://api.github.com/repos/$GITHUB_USER/weave/git/tags/$LATEST_RELEASE_SHA >/dev/null 2>&1; then
echo -e "\u2757 Tag latest_release is not on GitHub, or is not the same as the local tag" >&2
echo -e "\thttps://github.com/$GITHUB_USER/weave/tags" >&2
echo "You may need to" >&2
echo -e "\tgit tag -af latest_release"
echo -e "\tgit push -f git@github.com:$GITHUB_USER/weave latest_release"
exit 1
fi
echo -e "\u2713 Tag $LATEST_TAG exists in GitHub repo $GITHUB_USER/weave"
## Check that the version doesn't already exist by looking at github
## Check that the version does not already exist by looking at github
## releases
if github-release info --user $GITHUB_USER --repo weave --tag $LATEST_TAG >/dev/null 2>&1; then
echo -e "\u2757 Release $LATEST_TAG already exists on GitHub" >&2
@@ -120,13 +137,13 @@ publish() {
echo '** Sanity checks OK for publishing tag' $LATEST_TAG as $DOCKERHUB_USER/weave:$VERSION
echo "== Tagging and publishing images on docker hub as user $DOCKERHUB_USER"
make SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER publish
RELEASE_ARGS="--draft"
if [ "$RELEASE_TYPE" = 'PRERELEASE' ] ; then
RELEASE_ARGS="$RELEASE_ARGS --pre-release"
fi
echo "== Creating GitHub release $RELEASE_NAME $VERSION"
# This cannot be done as a draft because of a bug in
# github-release: https://github.com/aktau/github-release/issues/7
github-release release \
echo "== Creating GitHub release $RELEASE_ARGS $RELEASE_NAME $VERSION"
github-release release $RELEASE_ARGS \
--user $GITHUB_USER \
--repo weave \
--tag $LATEST_TAG \
@@ -140,52 +157,126 @@ publish() {
--name "weave" \
--file "./weave"
echo "** Published release $RELEASE_NAME $VERSION"
echo "** Draft $TYPE $RELEASE_NAME $VERSION created at"
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
}
if github-release info --user $GITHUB_USER --repo weave \
--tag latest_release >/dev/null 2>&1; then
github-release delete \
--user $GITHUB_USER \
--repo weave \
--tag latest_release
publish() {
setup
cd $PWD/$RELEASE_DIR
UPDATE_LATEST=false
if [ "$RELEASE_TYPE" = 'MAINLINE' ] ; then
UPDATE_LATEST=true
fi
github-release release \
--user $GITHUB_USER \
--repo weave \
--tag latest_release \
--name "$RELEASE_NAME latest ($VERSION)" \
--description "$RELEASE_DESCRIPTION"
if [ "$RELEASE_TYPE" = 'PRERELEASE' ] ; then
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
make UPDATE_LATEST=$UPDATE_LATEST SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER publish
echo "** Docker images tagged and pushed"
github-release upload \
--user $GITHUB_USER \
--repo weave \
--tag latest_release \
--name "weave" \
--file "./weave"
echo "== Publishing pre-release on GitHub"
echo "** Published $RELEASE_NAME as latest_release at"
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/latest_release"
github-release publish \
--user $GITHUB_USER \
--repo weave \
--tag $LATEST_TAG
echo "** Pre-release $RELEASE_NAME $VERSION published at"
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
else
echo "== Sanity checks"
if ! [ "$LATEST_TAG_COMMIT_SHA" == "$LATEST_RELEASE_COMMIT_SHA" ] ; then
echo -e "\u2757 The tag latest_release does not point to the same commit as $LATEST_TAG" >&2
echo "You may need to" >&2
echo -e "\tgit tag -af latest_release $LATEST_TAG" >&2
exit 1
fi
## Check that the 'latest_release' tag exists by looking at github
if ! curl -sSf https://api.github.com/repos/$GITHUB_USER/weave/git/tags/$LATEST_RELEASE_SHA >/dev/null 2>&1; then
echo -e "\u2757 Tag latest_release is not on GitHub, or is not the same as the local tag" >&2
echo -e "\thttps://github.com/$GITHUB_USER/weave/tags" >&2
echo "You may need to" >&2
echo -e "\tgit push -f git@github.com:$GITHUB_USER/weave latest_release" >&2
exit 1
fi
echo '** Sanity checks OK for publishing tag' $LATEST_TAG as $DOCKERHUB_USER/weave:$VERSION
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
make UPDATE_LATEST=$UPDATE_LATEST SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER publish
echo "** Docker images tagged and pushed"
echo "== Publishing release on GitHub"
github-release publish \
--user $GITHUB_USER \
--repo weave \
--tag $LATEST_TAG
if github-release info --user $GITHUB_USER --repo weave \
--tag latest_release >/dev/null 2>&1; then
github-release delete \
--user $GITHUB_USER \
--repo weave \
--tag latest_release
fi
github-release release \
--user $GITHUB_USER \
--repo weave \
--tag latest_release \
--name "$RELEASE_NAME latest ($VERSION)" \
--description "[Release Notes](https://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG)"
github-release upload \
--user $GITHUB_USER \
--repo weave \
--tag latest_release \
--name "weave" \
--file "./weave"
echo "** Release $RELEASE_NAME $VERSION published at"
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/latest_release"
fi
}
usage() {
echo "Usage:"
echo -e "\t./bin/release build"
echo "-- Build artefacts for the latest version tag"
echo -e "\t./bin/release draft"
echo "-- Create draft release with artefacts in GitHub"
echo -e "\t./bin/release publish"
echo "-- Publish the artefacts for the latest tag to GitHub and DockerHub"
echo "-- Publish the GitHub release and update DockerHub"
exit 1
}
case "$1" in
# Ensure required tooling is installed
if ! which github-release >/dev/null; then
echo "Please install git-release:" >&2
echo -e "\tgo get github.com/weaveworks/github-release" >&2
echo "and create a git token per https://github.com/weaveworks/github-release" >&2
exit 1
fi
[ $# -eq 0 ] && usage
COMMAND=$1
shift
case "$COMMAND" in
build)
build
build "$@"
;;
draft)
draft "$@"
;;
publish)
publish
publish "$@"
;;
*)
echo "Unknown command \"$1\""
echo "Unknown command \"$COMMAND\""
usage
;;
esac
+57
View File
@@ -0,0 +1,57 @@
#!/bin/sh
## This is the shape of unencrypted `do-setup-ci-secrets`
##
## cat > /tmp/boto_gsutil_cert.pem << EOF
## -----BEGIN PRIVATE KEY-----
## <...>
## -----END PRIVATE KEY-----
## EOF
##
## cat > ~/.boto << EOF
## [Credentials]
## gs_service_client_id = <...>
## gs_service_key_file = /tmp/boto_gsutil_cert.pem
##
## [Boto]
## https_validate_certificates = True
##
## [GSUtil]
## content_language = en
## default_api_version = 2
## default_project_id = <...>
## EOF
openssl base64 -d << EOF \
| openssl aes-256-cbc -d -K $1 -iv $2 \
-out bin/do-setup-ci-secrets
mOYNzNsAdwva0LbB2YFMY9VgL0SITFu1bPrbOujyLtOrBNjmOYbzR5d/sG+ARMFr
7NZ5OKcvMbItllz0n7TtNkzkCOMztMC870Wcx3nGJzbBephdcYwos4tBlp5bihTU
AXgxLUiDT1broYAFKnbE1aZC/XyNxuLTN+nXJWIWeijaXTgvhkEEm3tab+z7ioCb
tlg71W1BrUuocctAJAuVwH/uTZAaXaJGJj1s2S7F2DrFnG6sFlrp8rnCucPLWKnC
4IwJFC5saR5gxK6wECffwmwbpeXVOoQ58OgzxoZTEyAr9nhVbaBt5UZz3tfHlx8W
Rn5jHehACw5kg5eMtGPqMEksJ63LX4h3mfSTLJXJHEmOrzCRpFrigVrsbA408U2u
8jJTHJmKrGfwBh5ZFzBg6NuV4VplOp+ZquJLgegv2WH/iu/ciopXqB2YDCdx5ZBi
nHV2Puth7kjS4EMCKUGfeqpesjL9Djb8cygzv/J3ZBmLjNH9dqlSXWy2DcFZA1Iy
pFTTHkVU3w5sNz3nYOuT2UPQvULBW0FUndDKX6olyVa0mlxPwGBP9N3yGg918/rw
7rNV/r54lA334F5QMhDrVxar6sl9wfLA+Z+8qemBbNFlLog1uQGhuwWK2nFu1tRM
9uyvQIq0oq5NHKpogWc7NEuk7CPe1JyfRIcbMVj8+ilmKjUPB2HPC43IDmKt9/56
rY3IXAcxZ/ClGfGpeNHXqop9T/WhfaowponOZFcdGSUddG0OswqkTE4KIv71mgTF
jXooMkTQBOG+gBnCe+8bCRdRCG3anxHX74za4dGD58cOJtl6DYWnjWuR91NWnxdT
1Zr3iUe41txg9hCNY9LHo2QHGLPxe6pmNnSClQ1mcFhuY6NF8RmCX3HAGKJQkxJb
vpLGbXTXjAIFgIYgLNgKP2RLvCif/LmSWgkvE3ZU9GXtCUbm6xgJodwOqXiI9uuO
4H4+/vpGHnSFENiclkgMFrLG4yiMu8ArgmnCBzg9E2AoOX5zCrh/2ZRTU8Dk4mt7
jUd5SKQLsjB9QtB+y3tRO7zY9RDvR6iBOPRsjx6t8EJMCfPb06KchfMJw+VGxvMM
48jtmepREFnhd/8XDRGnw5bznjRuwohGik8BToltjIviMkkpifKWaLwlaOiDXaHh
VBFNXnUKvOBwRMU+PZqv4+DP2qff1gSXnGKqA5GIURYrK9P7TqHHNTnmjBdfJLzc
0x+W1DTiYhLT2vrLrkEpZ99o6qrg+lwvjat6IsJF8xoQEo4pLD/ZRPJ0WzoDyctT
oWZ5rMsN1TzTvJrBrTAWIJyhIzZcuNwChMrU6qogMr0fd6TSxWhWn0sOJ5y8AaTR
ej4JHx0qroS/9JPmgFYU61pX3c0ypdef/sSHWG23mcFzLTgwe2d4ty9VakypZy3T
sXCl6EHVVXlUFWhY4zQCjdZpYJv/rtoKL1BL4fPE2NV5WTjCL2GNvaO5SfHR7pA0
yQJR/iU+cS2n8UXsWNjyV+/qEn6+QXuyVHGLbxyH8EeqlliAbyHsc2rzVI8Xctzo
W5DuIqxwHAkNNurhmx27ktIIQ6HhaCp028SWwvLttkbBBgF4v1mmd4u/2bKbKjUd
dOaetaIUx1hU9OAeaePvVkeTDcc3mlFtPARCS9Fqj16hgJQ43onZyC0H1K2WAxmU
GsmzjCHlTNquzFTuuQihYGZKfsWIh9z6HEOkvNJ3VEQ=
EOF
exec sh bin/do-setup-ci-secrets
+115
View File
@@ -0,0 +1,115 @@
#!/bin/bash
set -eu
# openssl enc -in do-setup-circleci-secrets.orig -out setup-circleci-secrets.orig -e -aes256 -pass stdin
# openssl base64 < setup-circleci-secrets.orig
openssl base64 -d << EOF \
| openssl enc \
-out bin/do-setup-circleci-secrets \
-d -aes256 -pass pass:$1
U2FsdGVkX1/3QHnDgpZn0srp+GiEQBTlag0CBdsw6Bp6iaIKy6Jy8gFshrsmvl3+
c+7hn1nqsBVpzVH0S5QA3Ex5nx9rl7/dcGoogsySFcwMmBFcauT0UTMK1bvX0Hqx
QrshYSndUkww72KA7EsSnjR1BBJWm3AhCDOTIcnO6nQhig1Fc+SBEj5FGS5xTQ5Z
/8hDsKf54Ms49vOO6QeE5w5ndZ+k7IvVJfq9Evhb7htD20RntDhR86foAwMnLV7R
Wh9qAqI3gnRq0GmbeRKaHmB7pcLq9f0OdScqM/Tn1D+q0556fqJjBziXpZiGbxVs
DxEp8zDJ8yNDAi0Wb2NXjoA79SXJ8Q7iTieaST4y2OmXQpY+Vw8RVDXxi6ERyVnH
41OeMzyLm0qvCoMl32pe1sSG5DRCUiAm7wOtDTVvNDRqmGJYUgwwexyayecJI1wQ
LlhYLJavUvw3UGDZOld8BaMQhfOKBVCdFrjmA4HuVxmOf3029qkgzOCSZiQVNqs+
fvM6J05zBuk+GPy8YInvRkzfxum/x00vmUyGjMs/lHDFfrmYDFK3ux41EoVqvWLG
S5uYxdZJFdR3Xsrs+sMr51XNBQGMmz6KoEEzFYyHs5fZgtdpRdPAuQS1m6FqgZyi
wSz6+EKsALtmGorEp+CF8JN5YzKJCrKzNkg0fILBqsqmJZGP64lmX69950lRLNGM
/l3KVR2zH31b4AnnzTcvldVSCKDXjaMw3JA2Ce/MK6XhOWOcpEv4oDt/62lbhUKP
QUQTPbfpQRvVfBcoIrfAm3ghi/CZc2TDVEuD7DhasLTZD3r7LoK261BWC9O5Wubi
vlXuFIF+uyrpTDf6EJKE/+XywtArppffMP6RkHu1Iqnd8JtsqPaIZsmMmZbjgb6j
BX4ZLb5U9R7jYoSgIke4+u6geFcrMQ+Lny4r7MxXaS0Y1UJ6Jo1bwpYw1TBIobZc
eQ30+xhbSeRC9vEw1NXI6qM2Ai2gIHtAZvEiBkBBXTG5Ld7N/mBAmDIJEzVRxh6F
jRkB3dzESF6eeTBm61OQepwV3aW6lslMaf26drPGv2W2q+K/WOpQOejfggtHUgVs
9SWqtn7hHYSpIASe1Ihi14HAWv3kPhQien0zY1DLKJWf4ilbRaICn1QSJjNuKwtl
A2ZwEw7FFddFcsF4okHPkxEoCre3shq0DEw9UA1gnwEaEF1Q+i9cvLtQg56zK3+R
MfTzFUv2K1z3j1s5tC6Z6LzL8j682EFOll7MkihqiIWh+po1zcNs6Ln0SkjB3v9T
OFRYVJis9GqXYO9PUW1mI69udR4nXbKlBl5tWNyAxn9hufJHLSAmbGMwActKLRM5
kROapKrN1Ra/sHUmhoSSDUK9AXeYKkqPXVMbhly9xC1Kya4hFaFokjRQIMLPmXdQ
lP46RL3HOq0v1BkXg05poSwDm+BtitZkcIhOzcmeucUqn1whsz39y3VoOhJqQu2j
0dUcj19Fue1CDZfwzACb9mLZr643h0yCIHQaVT7is/yiN2vEt0S0zynvOA1D+Xgy
4bFdjqed9DgBShLwF/lh4/hur5iZPpVr9A8rt3Vq0k5A1tdjsZpXP3qv5jLc7lJv
cAQxdVLEuv8WPwwTV/EQsoH7lpoEdjktw/gu0v5PkUgR/WBxEqJq4liTekViQ+8X
BAmdm/FGWeIxSSEm0a8I+muiBpIuKjuQoJit1f0k+lLUohoVDqpDDfeYHh1TxXHE
xSKAKNmFSc2XOr5Zg2mOInxeH7WoxH6iylXKADBgEgAQ4C55DoAPRjFNtGGz2iJl
WkzhQOfgsDss1VxpPZ4SWDQO+xxuuEnYcCYdJ6Tyi5hAwZ0+RZmXx0g9BCK+5qzc
Ffei066kCItBcayekwCs0i86r8JvDtsMUUkzEKDQTSWHpP8ypIZoz3P+Skdqcs6p
IcZHy2fODIsClctRAsHOjZ9BnWSY2rqf1lysqvRfOkNTViTOUVx2hf+LuL+8rT6g
K4GvS39Q+c9FYSMjCNxv1m37NfHJv2W7AlqVV95gMBEFB97M3388T7PfvmSLsyPv
qAKDE7genTOycQvEX9oeEACxPo08ai2Mgs/YXCig0g0btZoScoKPelQKtD0HLpZ+
j7ruaKFy4/vySDMostB3AKSkCE/SkGu/W9oI37n1guSYC/9yJjtTcwZULXAClfwt
LyHvLAw4+7BdGJqDe9Tf8z4zqVLax+BcBHkW7pbP+pvxC6fhIA4PO8k5HwwAVlRw
68lG6e3z9+Vst4zrFLewHJvkDRSOTSecs9h9SPOjReTK0ovlDzMHVakfUvqpk9mv
nCj8SWaMPF70z138AuRuKN7X4exNXPBFnOcseKp/NpT1DyvgoHm/oIO65mq9vvo+
42uSbyyegYiTdVK4SaXBc2hixkhaTteqdEy6GxfWqKCvHtYuRD2LyUSjZn/0Y6CG
3wZapylKL1sxfH+b1gogv2wAA8KtZAfiTlMISuymwQr5RbOFma7UVMVecCa7XrNG
fZwO8R+6++ru0efDzeC/gtLOItjn+yVfsbk2gfu0FikHIV1sogOjnsv3dl6IpH/s
w6WbuK/yZhyrrmhIIueHF30ES512Peogf/Gxp8U/ZAmqBa7p4qsv93XWDR52IPYz
yU1MtboLD02J39daBNrYZuO4Yfo/xAvU7Ijg2KIR9HS0bSWNuSFAZaZXlJ2bSag3
8l0dZMx2fZa0FALxWvY2+nd1tWcH+Wo99RSxX+4TH3guCpe1x/58yKOKL4d1xRpu
02nGaqerV9bHU6WjCUYTrrZurTp6yqivpSFCALDMhAU5Ihd/Qjz69zg4uZ7iqbNd
WqIYJ3SAOei2tSfoOJbdh8mohotnMFFbupck3Fv+lXAXYDp0V/sSuK32MSeJxan1
q3t0eenAh6zoBP0Tj53N8B9YmrgZ1i1AljQqYsAdb8N5Z3vecuzQa946U8H4axEc
BL5fgmqSfsbj1EjJVUrw3cE9hgqeqAYBBvZsyW5zSAsLq/RJKYtBxDGADqr0TOOu
TjVqfPObb1u9EAQj15ALBhhPORa2gBAK5gnhJWW5deNgA0UOYoWARFYfATuQvr1T
9xTVbqzZ0VJR4n7X5JyxJo2tZ44QYVzA0fqX0lt+uRkarrkbZMgplKp4MJTFCp2N
tLciWS0YSHPbKTm7svw5mROazUvEoj1N3SYrJT+Ai9UgSL1cA4FmQ5Jsi+GOeQbH
7E4pzjeST4b1aATY9ZZSy4TzK+UNYBcCVqY1dN5Xnk0e5yPMnrkkcYI41s/6oOaW
lQkp/yrHkvpzQWZa7vF2yWg54j3eX8YWx/TsLvmuJmSQqiIHfu9TXcAIPDispCpF
gp4ZbYPZ8RDQLw3ROc+bZ9E0J8e+FHwgXEELsMLW9n4d3PTj9pLFej1vmBKa03o3
g+JViNxb1gUPNmC6ADk4o7TQzBrjV2pmmuCza16kOmq6oAbJPOeCLtVWA5HJTX8d
AuOzBLO37tWP457dNqoNDaCMik3LCy8cslZjAz02D6VG/QGOAXP9x8bJ3zg9N7vW
EOn1BMtIeMI4yIxpC6HgotNWDNbUBWremAFWYQRgYQqo9L1SdW4hz6X8FqdlBFvK
ml7sdUwjEy2tNW+aiZI4cgAlI/VvMWYr5kPfhvaUu1wGYeS1TD1B+DPcjcXpOGWo
vB1f/uvGc61zZWD0ph9f3S9OnvEko5qV3VhxOnxYUZTrqlM7LVgy/sHrSaPdR7Eo
nb4QGdi5wEGMxVgQlDGwOmEmS5nCaA0RcfHbkQV4SebRleocLB9qcSwj5JzPICAB
sn1qeSv45LnGdTzUnCQErw1EzgrK1FXOknujjZ3GX5dM7a6Gag0E6cAxoQeC8Mh0
aUWolyBo9aSXltn7Vnywoe4X3v96eVH6tUQtN3wt51LBlfSDTWOQDeXLFyW57oYL
/ErbmJLaSHpANBcC9InI+rxNYePTTba7K8rdPDppmMEmdOXjaL+5aPay2Bay9K8T
FUyZdoyrbzxFZYT9/YyVJV/ukVcT9ru3HpzLTg9Vgc/CyRfIvCZY/Gzh+pPUbRsR
pHYDT09BFS+mjszTkcdbceweYXJvknXpajMiISrPUUkb9rwCTmLS4QR4y7I9zAgJ
9U72mCZB9yhhzS17kyKAANihXvOFrMXCO7QTWZbtSkobMBS2SybDxYP3rM5b6824
HQaY4MZW5qCndydfz4fa908XbPTDka9Nu3CZfI3fhGiiGDFWViotGEYE4i9BuGZJ
vmtzII7zeIoHwM4t5fb5mYDd9H+Rkct1yATGEIFEx7cu1fj4Ht2177EzhdkF3Q6I
Fccn43yGDW06nFvEjnjb+Lz4EvtGp6dDefUhNBohdzd4Rm2o+etKobm0lZ1xOX74
2gOGBLfXS6PF+msNkWP76mqKE6hG0+R1iPrN06Arti+ZRBhNGJDp1pnOERl7IyB0
hiLUqae7xVSMVRDeq4O04EUpMXh5ixs3RJXjthYILt7vX+6qskJC3rMuyThrsvMi
Kk0wErDpzR5U7KFnM2fk4jY5by/0b/jHZ/PWFGnaFrXNOiwK/mV1RIJtg5FCrvMv
xJN5Qo7o2ZYzUL+IBf5mP5O49FCnz3e++9r4ySY/K57ybhQft1l+cQPWS7xuvVAg
wZGM9DPl9AhBus0I3V3XzfkcWWHkMynMRcfTnkBHrmKjMopIOggsq05JPalM9b0V
NfwZngVvTFbQejbGjgQQQ+G7spav/icCIA8Iu8JPHaKZt2IX77L1xMcQOouqMrx8
JCXnJ7zsuU+8W3GQGm70H2kcen4bf4beA8TSkFmbVsaqDNtGaG+ZopWO8ctZX3CP
jFgIYgEGi137IWvs1KD5QNFHZI6l0JMzglmvcfnViRPDHLi+EQXAoL/98j/YzA9t
a9L4xMO0oucovKkqYOS2cdFaQKVIUM9L5ibcbW7Tol4ugMo5ibIsTgjZoyFohG6t
acg+h2wTTyxtrIapvOOBeAo9V/iQowTAPQf38LKQZ5tApksiCG1qvJerKALTMp1J
yUHjTCPCPsaiXVVO0mfa9OPtzVvi/icLmlz7umk82mew13djedgMS3/ghq5aEHbg
e7rtI5snFlm/GQnV6IaCDytb+jYxqQ0DZ9Mb0CHcMpnLyiBO6nzHfadpZYlrxrpx
dPx9MVqfLavj1UVjKuVj4Mgec9J38IBBRGMJZ4MGhde2hrw/s2jVzG1BzaIzcvFa
bWiJOkKKRC4BY9DBDYfae5c/obwzkxLmvkbWAVzoFXc+46K1IK6bbC2a4EerOAxc
C7B3X8BucD8OtiPORwXgjctd9AJ7cXq0EEVGoZZ8jkILnklsK6RXsxrT4gSOGFpx
cBU7TF4kHpH+o4KGQmjlFA2VYP1tXirjEep57LOHfL+zy+HiSXdsbexf6odkqLYA
1RR0TzSkmqJYvP0GfHPMvp2xredx1kP934fhTIUcWvErrG3NPd/Dod4BzQye1nNK
8Y8aFYF6fACcD4SFZ11ecK9DJmB+WXGlsxtO3w1LeEysny2lVkkH6HuMRIzPyXAM
/sEpwh3hwPJosAh6vGM6JxuL7CXP8hC1oUvK91VR0hxeDcO/LgdcHtDnlNMqt1wC
jvYt9Hg+3aY5GehBnQPRppnIMaGhiBrRC6HjBg8Umt6UTUr5PXNY2aia5wRcx4M6
Tj0O0dAt8WWZ9ySgzOoq4SRoaAp6m4E7CpRNMgXH031OWEeHqtt8rsYqAcvcQLuW
vIwts5hiXZOfgXyR6SBY3ANE1SjDY46na2G4oiG8FI7eAqYgehjHrXqCnGrzjefE
V1eVcIr7DBYuM8wq7C9x+qdMir6RNV4MrVD+7rO/drivnd3MaqDHlG0LfMZo6GIK
QZGIy0z1W5lOXm9x3RAz7/Ea11ynkZyPDDxBE5NRcV6JfukdjDrvsaQA/Qk2Wo7h
PdUZjv17H4syMdap6tUDTc8Cohut6n1UxnDnhaAYEDGR3TgPqI5vEaJ+yoaM9itv
b2I/K+0uK6GfWwca7PeTtYn92/5etO4czG99xEBFzw7tiEmBTA8KfseZIUe/i3fr
9LO+rKEGalFB4mVTYyCKPYYx4hOIvjdCox/jEBTFyYUEhgGnZvArgw9XabvII7eM
c1FmwnN2CM/MHUzM0Kw7cS/oaoRFcG/PqioO/krL2Bd+xThwSH4Zn+Y1UVO7aXNy
90MOehHSc2nhN984Y2re3ffVsjPhC3KdWxr8jbho/Az65rUkYg64roiO2ZqdqRvf
aYtX9fCe8N8SzgLTlWZt+l8sjY8HP2k9NoajphV1nG4iC/pX0ZJiVQ+VBRNaGhSl
6IhM9gM1ZsI86cULJpOTAhNu1fZBv+Mq8IXYD8SqlNVwVtjvshtlzBN4InSGEKiO
HM7coo7aDuu+Go5AEiIVMbR09jAa0VINsCVScfSbTFhlGRTrZr2DSO/s8WxbV+Mb
EQAfZmyrLF71Z3MfIukg+ps5iuqFhZ+gJSXXSFwumpg=
EOF
exec sh bin/do-setup-circleci-secrets
+4 -1
View File
@@ -1,6 +1,9 @@
FROM ubuntu
RUN apt-get -y update && apt-get -y install --no-install-recommends build-essential git ca-certificates docker.io mercurial libpcap-dev curl make pkg-config gcc bison flex
RUN apt-get -y update && apt-get -y install --no-install-recommends ca-certificates apt-transport-https
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
RUN echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list
RUN apt-get -y update && apt-get -y install --no-install-recommends build-essential git lxc-docker-1.6.2 mercurial libpcap-dev curl make pkg-config gcc bison flex python-requests
# When doing a build in a container, "apt-get update" happens twice,
# which can be a very significant overhead for incremental builds.
+2 -1
View File
@@ -6,7 +6,7 @@ export GOPATH
WEAVE_SRC=$GOPATH/src/github.com/weaveworks/weave
if [ $# -eq 0 ] ; then
if [ $# -eq 0 -o "$1" = "tests" ] ; then
# No arguments. Expect that the weave repo will be bind-mounted
# into $GOPATH
if ! [ -e $WEAVE_SRC ] ; then
@@ -35,6 +35,7 @@ EOF
echo "weave:x:$uid:$gid::$WEAVE_SRC:/bin/sh" >>/etc/passwd
echo "weave:*:::::::" >>/etc/shadow
echo "weave ALL=(ALL) NOPASSWD: ALL" >>/etc/sudoers
su weave -c "PATH=$PATH make -C $WEAVE_SRC build"
else
# There are arguments to pass to git-clone
+73
View File
@@ -0,0 +1,73 @@
general:
branches:
ignore:
- gh\-pages
- /pull\/.*/
machine:
services:
- docker
environment:
GOPATH: /home/ubuntu:$GOPATH
SRCDIR: /home/ubuntu/src/github.com/weaveworks/weave
PATH: $PATH:$HOME/.local/bin:$HOME/google-cloud-sdk/bin/
CLOUDSDK_CORE_DISABLE_PROMPTS: 1
WEAVE_TEST: $HOME/docker/weave-test.tar
dependencies:
cache_directories:
- "~/docker"
override:
- if [[ -e "$WEAVE_TEST" ]]; then
docker load -i $WEAVE_TEST;
else
docker build -t weaveworks/weave-build build;
docker build -t weaveworks/weave-test test/image;
mkdir -p $(dirname "$WEAVE_TEST");
docker save weaveworks/weave-test >$WEAVE_TEST;
fi
post:
- sudo apt-get update && sudo apt-get install bc jq && pip install requests
- curl https://sdk.cloud.google.com | bash
- bin/setup-circleci-secrets "$SECRET_PASSWORD"
- mkdir -p $(dirname $SRCDIR) && cp -r $(pwd)/ $SRCDIR
- cd $SRCDIR/test; ./gce.sh make_template:
parallel: false
test:
override:
- cd $SRCDIR; make lint:
parallel: true
- docker run -v /home/ubuntu:/home/go -e COVERDIR=test/coverage -e SLOW=true -e CIRCLECI -e CIRCLE_BUILD_NUM -e CIRCLE_NODE_TOTAL -e CIRCLE_NODE_INDEX weaveworks/weave-test:
parallel: true
- docker run -v /var/run/docker.sock:/run/docker.sock -v /home/ubuntu:/home/go -e COVERAGE=true weaveworks/weave-build:
parallel: true
- cd $SRCDIR/test; ./gce.sh setup && eval $(./gce.sh hosts) && ./setup.sh:
parallel: true
- cd $SRCDIR/test; eval $(./gce.sh hosts); export COVERAGE=true; ./run_all.sh:
parallel: true
timeout: 300
- cd $SRCDIR; make clean-bin:
parallel: true
- docker run -v /var/run/docker.sock:/run/docker.sock -v /home/ubuntu:/home/go weaveworks/weave-build:
parallel: true
post:
- cd $SRCDIR/test; ./gce.sh destroy:
parallel: true
teardown:
pre:
- test "$CIRCLE_NODE_INDEX" != "0" || (cd $SRCDIR/test; ./gen_coverage_reports.sh):
parallel: true
- test "$CIRCLE_NODE_INDEX" != "0" || (go get github.com/mattn/goveralls && goveralls -repotoken $COVERALLS_REPO_TOKEN -coverprofile=$SRCDIR/test/profile.cov -service=circleci || true):
parallel: true
- test "$CIRCLE_NODE_INDEX" != "0" || (cd $SRCDIR/test; cp coverage.* $CIRCLE_ARTIFACTS):
parallel: true
deployment:
hub:
branch: master
owner: weaveworks
commands:
- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
- cd $SRCDIR; make publish
+132
View File
@@ -0,0 +1,132 @@
package docker
import (
"errors"
"fmt"
"strings"
docker "github.com/fsouza/go-dockerclient"
. "github.com/weaveworks/weave/common"
)
// An observer for container events
type ContainerObserver interface {
ContainerStarted(ident string)
ContainerDied(ident string)
}
type Client struct {
*docker.Client
}
// 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()
}
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()
}
func NewVersionedClientFromEnv(apiVersionString string) (*Client, error) {
dc, err := docker.NewVersionedClientFromEnv(apiVersionString)
if err != nil {
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)
}
}
// AddObserver adds an observer for docker events
func (c *Client) AddObserver(ob ContainerObserver) error {
events := make(chan *docker.APIEvents)
if err := c.AddEventListener(events); err != nil {
Log.Errorf("[docker] Unable to add listener to Docker API: %s", err)
return err
}
go func() {
for event := range events {
switch event.Status {
case "start":
id := event.ID
ob.ContainerStarted(id)
case "die":
id := event.ID
ob.ContainerDied(id)
}
}
}()
return nil
}
// IsContainerNotRunning returns true if we have checked with Docker that the ID is not running
func (c *Client) IsContainerNotRunning(idStr string) bool {
container, err := c.InspectContainer(idStr)
if err == nil {
return !container.State.Running
}
if _, notThere := err.(*docker.NoSuchContainer); notThere {
return true
}
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
}
-13
View File
@@ -1,13 +0,0 @@
package common
func CheckFatal(e error) {
if e != nil {
Error.Fatal(e)
}
}
func CheckWarn(e error) {
if e != nil {
Warning.Println(e)
}
}
-10
View File
@@ -1,10 +0,0 @@
package common
import (
"io/ioutil"
"os"
)
func init() {
InitLogging(ioutil.Discard, os.Stdout, os.Stdout, os.Stderr)
}
+51 -26
View File
@@ -1,41 +1,66 @@
package common
import (
"io"
"io/ioutil"
"log"
"os"
"bytes"
"fmt"
"strings"
"github.com/Sirupsen/logrus"
)
const (
standardLogFlags = log.Ldate | log.Ltime | log.Lmicroseconds
)
type textFormatter struct {
}
// Largely taken from
// http://www.goinggo.net/2013/11/using-log-package-in-go.html
// Based off logrus.TextFormatter, which behaves completely
// differently when you don't want colored output
func (f *textFormatter) Format(entry *logrus.Entry) ([]byte, error) {
b := &bytes.Buffer{}
levelText := strings.ToUpper(entry.Level.String())[0:4]
timeStamp := entry.Time.Format("2006/01/02 15:04:05.000000")
if len(entry.Data) > 0 {
fmt.Fprintf(b, "%s: %s %-44s ", levelText, timeStamp, entry.Message)
for k, v := range entry.Data {
fmt.Fprintf(b, " %s=%v", k, v)
}
} else {
// No padding when there's no fields
fmt.Fprintf(b, "%s: %s %s", levelText, timeStamp, entry.Message)
}
b.WriteByte('\n')
return b.Bytes(), nil
}
var (
Debug *log.Logger
Info *log.Logger
Warning *log.Logger
Error *log.Logger
standardTextFormatter = &textFormatter{}
)
func InitLogging(debugHandle io.Writer,
infoHandle io.Writer,
warningHandle io.Writer,
errorHandle io.Writer) {
var (
Log *logrus.Logger
)
Debug = log.New(debugHandle, "DEBUG: ", standardLogFlags)
Info = log.New(infoHandle, "INFO: ", standardLogFlags)
Warning = log.New(warningHandle, "WARNING: ", standardLogFlags)
Error = log.New(errorHandle, "ERROR: ", standardLogFlags)
func init() {
Log = logrus.New()
Log.Formatter = standardTextFormatter
}
func InitDefaultLogging(debug bool) {
debugOut := ioutil.Discard
if debug {
debugOut = os.Stderr
func SetLogLevel(levelname string) {
level, err := logrus.ParseLevel(levelname)
if err != nil {
Log.Fatal(err)
}
Log.Level = level
}
func CheckFatal(e error) {
if e != nil {
Log.Fatal(e)
}
}
func CheckWarn(e error) {
if e != nil {
Log.Warnln(e)
}
InitLogging(debugOut, os.Stdout, os.Stdout, os.Stderr)
}
+31
View File
@@ -0,0 +1,31 @@
package mflagext
import (
"fmt"
"github.com/docker/docker/pkg/mflag"
)
type listOpts struct {
value *[]string
hasBeenSet bool
}
func ListVar(p *[]string, names []string, value []string, usage string) {
*p = value
mflag.Var(&listOpts{p, false}, names, usage)
}
func (opts *listOpts) Set(value string) error {
if opts.hasBeenSet {
(*opts.value) = append((*opts.value), value)
} else {
(*opts.value) = []string{value}
opts.hasBeenSet = true
}
return nil
}
func (opts *listOpts) String() string {
return fmt.Sprintf("%v", []string(*opts.value))
}
+93
View File
@@ -0,0 +1,93 @@
package odp
import (
"fmt"
"net"
"syscall"
"github.com/weaveworks/go-odp/odp"
)
// ODP admin functionality
func CreateDatapath(dpname string) (err error, supported bool) {
dpif, err := odp.NewDpif()
if err != nil {
if odp.IsKernelLacksODPError(err) {
return nil, false
}
return err, true
}
defer dpif.Close()
dp, err := dpif.CreateDatapath(dpname)
if err != nil && !odp.IsDatapathNameAlreadyExistsError(err) {
return err, true
}
// Pick an ephemeral port number to use in probing for vxlan
// support.
udpconn, err := net.ListenUDP("udp4", nil)
if err != nil {
return err, true
}
// we leave the UDP socket open, so creating a vxlan vport on
// the same port number should fail. But that's fine: It's
// still sufficient to probe for support.
portno := uint16(udpconn.LocalAddr().(*net.UDPAddr).Port)
vpid, err := dp.CreateVport(odp.NewVxlanVportSpec(
fmt.Sprintf("vxlan-%d", portno), portno))
if nlerr, ok := err.(odp.NetlinkError); ok {
if syscall.Errno(nlerr) == syscall.EAFNOSUPPORT {
dp.Delete()
return fmt.Errorf("kernel does not have Open vSwitch VXLAN support"), false
}
}
if err == nil {
dp.DeleteVport(vpid)
}
udpconn.Close()
return nil, true
}
func DeleteDatapath(dpname string) error {
dpif, err := odp.NewDpif()
if err != nil {
return err
}
defer dpif.Close()
dp, err := dpif.LookupDatapath(dpname)
if err != nil {
if odp.IsNoSuchDatapathError(err) {
return nil
}
return err
}
return dp.Delete()
}
func AddDatapathInterface(dpname string, ifname string) error {
dpif, err := odp.NewDpif()
if err != nil {
return err
}
defer dpif.Close()
dp, err := dpif.LookupDatapath(dpname)
if err != nil {
return err
}
_, err = dp.CreateVport(odp.NewNetdevVportSpec(ifname))
return err
}
+32
View File
@@ -0,0 +1,32 @@
package common
import (
"os"
"os/signal"
"runtime"
"syscall"
)
// A subsystem/server/... that can be stopped or queried about the status with a signal
type SignalReceiver interface {
Stop() error
}
func SignalHandlerLoop(ss ...SignalReceiver) {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
buf := make([]byte, 1<<20)
for {
switch <-sigs {
case syscall.SIGINT, syscall.SIGTERM:
Log.Infof("=== received SIGINT/SIGTERM ===\n*** exiting")
for _, subsystem := range ss {
subsystem.Stop()
}
return
case syscall.SIGQUIT:
stacklen := runtime.Stack(buf, true)
Log.Infof("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end", buf[:stacklen])
}
}
}
-47
View File
@@ -1,47 +0,0 @@
package updater
import (
"github.com/fsouza/go-dockerclient"
. "github.com/weaveworks/weave/common"
)
type ContainerObserver interface {
ContainerDied(ident string) error
}
func checkError(err error, apiPath string) {
if err != nil {
Error.Fatalf("[updater] Unable to connect to Docker API on %s: %s",
apiPath, err)
}
}
func Start(apiPath string, ob ContainerObserver) error {
client, err := docker.NewClient(apiPath)
checkError(err, apiPath)
env, err := client.Version()
checkError(err, apiPath)
events := make(chan *docker.APIEvents)
err = client.AddEventListener(events)
checkError(err, apiPath)
Info.Printf("[updater] Using Docker API on %s: %v", apiPath, env)
go func() {
for event := range events {
handleEvent(ob, event, client)
}
}()
return nil
}
func handleEvent(ob ContainerObserver, event *docker.APIEvents, client *docker.Client) error {
switch event.Status {
case "die":
id := event.ID
ob.ContainerDied(id)
}
return nil
}
+20
View File
@@ -0,0 +1,20 @@
package common
import (
"strings"
)
// Assert test is true, panic otherwise
func Assert(test bool) {
if !test {
panic("Assertion failure")
}
}
func ErrorMessages(errors []error) string {
var result []string
for _, err := range errors {
result = append(result, err.Error())
}
return strings.Join(result, "\n")
}
+19 -19
View File
@@ -34,7 +34,7 @@ Router:
the known locations of peers as they're discovered and is informed
when connections we have made die. It proactively tries to create
connections to remote peers we're not connected to using a random
binary exponential backoff to regulate the period between connection
exponential backoff to regulate the period between connection
attempts.
- Local Peer
@@ -66,23 +66,23 @@ Router:
simply responds to traffic received from the remote peer via TCP
5. We register this connection with the local peer
6a. If we initiated this connection then we now start sending fast
heartbeats to the remote peer so that the remote peer can determine what
address/port it should use to send UDP back to us. To do this, we
spawn off two further threads which are the forwarder
loops. These receive frames which are to be sent to the remote
peer. One of them sends frames without the DF flag set. To do this
it just sends the packets out of the UDP Listener socket. The
other needs to send frames with the DF flag set and needs its own
socket so that it can do PMTU discovery easily. To do this, it
uses a Raw IP socket (IP has no ports, so there's no collision
issue with the UDP Listener socket) and so it must add UDP headers
itself.
6b. If we did not initiate this connection then the UDP Listener
heartbeats to the remote peer so that the remote peer can
determine what address/port it should use to send UDP back to
us. To do this, we spawn off a "forwarder" thread to send
heartbeats, monitor incoming heartbeats, and some other auxiliary
duties. It also consumes frames to be encapsulated and send via
UDP from two channels, for DF and non-DF cases. In the non-DF
case, it can just send the packets out of the UDP Listener
socket. In the DF case, it needs its own socket so that it can do
PMTU discovery easily. To do this, it uses a Raw IP socket (IP has
no ports, so there's no collision issue with the UDP Listener
socket) and so it must add UDP headers itself.
6b. If we did not initiate this connection then the UDP Listener
should start receiving fast heartbeats from the remote peer. From
those it should be able to identify the local connection via the
local peer. It will tell the local connection (communicating to
the actor thread) about the UDP address of the remote peer. The
local connection will then start its forwarder threads as
local connection will then start its forwarder thread as
described in 6a, and start sending fast heartbeats. We send to the
remote peer via TCP a ConnectionEstablished message. The remote
peer receives this (on the TCP receiver process), tells the
@@ -160,7 +160,7 @@ if a node receives a packet that is bigger than the next MTU, it is
required to drop the packet and send back an ICMP 3,4 packet which
informs the sending node of the next hop MTU (RFC 1191). In theory,
these ICMP packets should have all the reverse NAT and so forth
applied, to make it all the way back to the sender. In practise this
applied, to make it all the way back to the sender. In practice, this
does often work, but in some cases all ICMP are sometimes blocked,
often by idiots who don't understand that ICMP is really the error
channel of all network traffic.
@@ -187,16 +187,16 @@ If weave determines that fragmentation is reliable then when weave
sniffs large packets (i.e. packets larger than the MTU and thus don't
have DF set) then weave will encapsulate these packets as necessary
and similarly send them out as-is, without DF set, trusting to the
network to do all necessary fragementation and reassembly.
network to do all necessary fragmentation and reassembly.
However, if weave determines that fragementation is not reliable
However, if weave determines that fragmentation is not reliable
between any two peers then it will manually fragment larger packets
correctly according to the IP spec, and will then send them between
weave peers with the DF flag set (i.e. the fragmentation will ensure
that the encapsulatated traffic will not be greater than the PMTU
that the encapsulated traffic will not be greater than the PMTU
between weave peers). Because the fragmentation is done according to
the IP spec, we don't need to do reassembly ourselves - on the
receiving weave, we jus inject all the fragments and rely on the
receiving weave, we just inject all the fragments and rely on the
stack to do reassembly.
Sometimes PMTU discovery doesn't work as ICMP packets sometimes get
+2 -2
View File
@@ -125,8 +125,8 @@ VM2> nc 192.168.122.2 6783
If all is well, you should now be able to launch weave on VM1
then on VM2:
VM1> weaver -iface veth6 -name vm1
VM2> weaver -iface veth6 -name vm2 192.168.122.2
VM1> weaver --iface veth6 --name vm1
VM2> weaver --iface veth6 --name vm2 192.168.122.2
Once the connection is up and running, you should be able to connect
from VM1/ns2 to VM2/ns2 and vice versa:
+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.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

After

Width:  |  Height:  |  Size: 43 KiB

+220
View File
@@ -0,0 +1,220 @@
See the [requirements](https://github.com/zettio/weave/wiki/IP-allocation-requirements).
At its highest level, the idea is that we start with a certain IP
address space, known to all peers, and divide it up between the
peers. This allows peers to allocate and free individual IPs locally
until they run out.
We use a CRDT to represent shared knowledge about the space,
transmitted over the Weave Gossip mechanism, together with
point-to-point messages for one peer to request more space from
another.
The allocator running at each peer also has an http interface which
the container infrastructure (e.g. the Weave script, or a Docker
plugin) uses to request IP addresses.
![Schematic of IP allocation in operation](https://docs.google.com/drawings/d/1-EUIRKYxwfKTpBJ7v_LMcdvSpodIMSz4lT3wgEfWKl4/pub?w=701&h=310)
## Commands
The commands supported by the allocator via the http interface are:
- Allocate: request one IP address for a container in a subnet
- Lookup: fetch the previously-allocated IP address for a container in a subnet
- Free: return an IP address that is currently allocated
- Claim: request a specific IP address for a container (e.g. because
it is already using that address)
Each http request either specifies a subnet, or if no subnet is
specified this is taken as a request to allocate in a pre-defined
default subnet.
The allocator also watches via the Docker event mechanism: if a
container dies then all IP addresses allocated to that container are
freed.
## Definitions
1. Allocations. We use the word 'allocation' to refer to a specific
IP address being assigned, e.g. so it can be given to a container.
2. Range. Most of the time, instead of dealing with individual IP
addresses, we operate on them in contiguous groups, for which we
use the word "range".
3. Ring. We consider the address space as a ring, so ranges wrap
around from the highest address to the lowest address.
4. Peer. A Peer is a node on the Weave network. It can own zero or
more ranges.
### The Allocation Process
When a peer owns some range(s), it can allocate freely from within
those ranges to containers on the same machine. If it runs out of
space in a subnet (all owned ranges are full), it will ask another
peer for space:
- it picks a peer to ask at random, weighted by the amount of space
owned by each peer in the subnet
- if the target peer decides to give up space, it unicasts a message
back to the asker with the newly-updated ring.
- if the target peer has no space, it unicasts a message back to the
asker with its current copy of the ring, on the basis that the
requestor must have acted on out-of-date information.
- it will continue to ask for space until it receives some, or its
copy of the ring tells it all peers are full in that subnet.
### Claiming an address
If a Weave process is restarted, in most cases it will hear from
another peer which ranges it used to own, but it needs to know which
individual IP addresses are assigned to containers in order to avoid
giving the same address out in subsequent allocation requests. The
weave script invokes the `claim` command in `populate_ipam` to do
this.
When the Allocator is told to claim an address, there are four
possibilities:
- the address is outside of the space managed by this Allocator, in
which case we ignore the request.
- the address is owned by this peer, in which case we record the
address as being assigned to a particular container and return
success.
- the address is owned by a different peer, in which case we return
failure.
- we have not yet heard of any address ranges being owned by anyone,
in which case we wait until we do hear.
This approach fails if the peer does not hear from another peer about
the ranges it used to own, e.g. if all peers in a network partition
are restarted at once.
### The Ring CRDT
We use a Convergent Replicated Data Type - a CRDT - so that peers can
make changes concurrently and communicate them without central
coordination. To achieve this, we arrange that peers only make changes
to the data structure in ranges that they own (except under
administrator command - see later).
The data structure is a set of tokens, each containing the name of an
owning peer. A peer can own many ranges. Each token is placed at the
start address of a range, and the set is kept ordered so each range
goes from one token to the next. Each range on the ring includes the
start address but does not include the end address (which is the start
of the next range). Ranges wrap, so the 'next' token after the last
one is the first token.
![Tokens on the Ring](https://docs.google.com/drawings/d/1hp--q2vmxbBAnPjhza4Kqjr1ugrw2iS1M1GerhH-IKY/pub?w=960&h=288)
In more detail:
- Each token is a tuple {peer name, version}, placed
at an IP address.
- Peer names are taken from Weave: they are unique and survive across restarts.
- The contents of a token can only be updated by the owning peer, and
when this is done the version is incremented
- The ring data structure is always gossiped in its entirety
- The merge operation when a peer receives a ring via gossip is:
- Tokens with unique addresses are just copied into the combined ring
- For tokens at the same address, pick the one with the highest
version number
- The data gossiped about the ring also includes the amount of free
space in each range: this is not essential but it improves the
selection of which peer to ask for space.
- When a peer is asked for space, there are four scenarios:
1. It has an empty range; it can change the peer associated with
the token at the beginning of the range and increment the version.
2. It has a range which can be subdivided by a single token to form
a free range. It inserts said token, owned by the peer requesting
space.
3. It has a 'hole' in the middle of a range; an empty range can be
created by inserting two tokens, one at the beginning of the hole
owned by the peer requesting the space, and one at the end of the
hole owned by the requestee.
4. It has no space.
## Initialisation
The previous sections describe how peers coordinate changes to the
ring. But how is the initial state of the ring established? If a new
peer joins a long-standing cluster, it can learn about the ring state
from other peers. But in a freshly started cluster, the initial ring
state must be established from a clean slate. And it must be
consistent across all peers. This is a distributed consensus problem,
and we solve it using the Paxos algorithm.
Although Paxos has a reputation for being hard to understand and to
implement, the implementation used for ring initialization is
relatively straightforward:
- We only need to establish consensus for a single value, rather than
a succession of values (as in a replicated transaction log). Thus
we implement basic Paxos, rather than multi-Paxos, and the
implementation closely follows the outline of basic Paxos described
[on
wikipedia](http://en.wikipedia.org/wiki/Paxos_%28computer_science%29#Basic_Paxos).
- All peers play all the roles described in basic Paxos: Proposer,
acceptor and listener.
- Paxos is usually described in terms of unicast messages from one
agent to another, although three of the four messages are fanned out
to all agents in a certain role ("prepare" and "accept request" from
a proposer to all acceptors; "accepted" from an acceptor to all
learners). So most implementations involve a communications layer
to implement the required communication patterns. But our
implementation is built on top of the weave gossip layer, which
gives us a broadcast medium. Using broadcast for the one truly
unicast message ("promise") may seem wasteful, but as we only
establish consensus for a single value, it is not a major concern.
The value the peers obtain consensus on is the set of peers that will
be represented in the initial ring (with each peer getting an equal
share of the address space). When a proposing peer gets to choose the
value, it includes all the peers it has heard from during the Paxos
phase, which is at least a quorum.
Once a consensus is reached, it is used to initialize the ring. If a
peer hears about an initialized ring via gossip, that implies that a
consensus was reached, so it will stop participating in Paxos.
Paxos requires that we know the quorum size for the cluster. As weave
clusters are a dynamic entity, there is no fixed quorum size. But the
Paxos phase used to initialize the ring should normally be
short-lived, so we just need to know the initial cluster size. A
heuristic is used to derive this from the list of addresses passed to
`weave launch`, but the user can set it explicitly.
## Peer shutdown
When a peer leaves (a `weave reset` command), it grants all its own
tokens to another peer, then broadcasts the updated ring.
After sending the message, the peer terminates - it does not wait for
any response.
Failures:
- message lost
- the space will be unusable by other peers because it will still be
seen as owned.
To cope with the situation where a peer has left or died without
managing to tell its peers, an administrator may go to any other peer
and command that it take over the dead peer's tokens (with `weave
rmpeer`). This information will then be gossipped out to the network.
(This operation is not guaranteed to be safe - if the dead peer made
some transfers which have are known to other peers but have not
reached the peer that does the `rmpeer`, then we get an inconsistent
ring. We could make it safe, up to partitions, by inquiring of every
live peer what its view of the dead peer is, and making sure we use
the latest info.)
## Limitations
Nothing is persisted. If one peer restarts it should receive gossip
from other peers showing what it used to own, and in that case it can
recover quite well. If, however, there are no peers left alive, or
this peer cannot establish network communication with those that are,
then it cannot recover.
+145
View File
@@ -0,0 +1,145 @@
# Release Process
## Prerequisites
* Up-to-date versions of `git`, `go` etc
* Install GitHub release tool `go get github.com/weaveworks/github-release`
* Create a [github token for
github-release](https://help.github.com/articles/creating-an-access-token-for-command-line-use/);
set and export `$GITHUB_TOKEN` with this value
* Update all dependencies with `make update`
## Release Types
The release script behaves differently depending on the kind of
release you are doing. There are three types:
* **Mainline** - a release (typically from master) with version tag
`vX.Y.Z` where Z is zero (e.g. `v2.1.0`)
* **Branch** - a bugfix release (typically from a branch) with version tag
`vX.Y.Z` where Z is non-zero (e.g `v2.1.1`)
* **Prerelease** - a release from an arbitrary branch with an arbitrary
version tag (e.g. `feature-preview-20150904`)
N.B. the release script _infers the release type from the format of the
version tag_. Ensure your tag is in the correct format and the desired
behaviours for that type of release will be obtained from the script.
## Build Phase
### Update CHANGELOG.md
* Checkout the branch from which you wish to release
* Choose a version tag (see above) henceforth referred to as `$TAG`.
* Add a changelog entry for the new tag at the top of `CHANGELOG.md`.
The first line must be a markdown header of the form `## Release
$TAG` for **Prerelease** builds, `## Release ${TAG#v}` otherwise.
Commit the changelog update:
git commit -m "Add release $TAG" CHANGELOG.md
### Create Version Tag
Next you must tag the changelog commit with `$TAG`
git tag -a -m "Release $TAG" $TAG
### Execute Build
You are now ready to perform the build. If you have skipped the
previous steps (e.g. because you're doing a rebuild), you must ensure
that `HEAD` points to the tagged commit. You may then execute
bin/release build
This has the following effects:
* `git tag --points-at HEAD` is used to determine `$TAG` (hence the
`HEAD` requirement)
* Your *local* repository is cloned into `releases/$TAG`
* `CHANGELOG.md` is checked to ensure it has an entry for `$TAG`
* Distributables injected with `$TAG` are built
* Tests are executed
## Draft Phase
### Push Version Tag Upstream
First you must push your version tag upstream, so that an associated
GitHub release may be created:
git push git@github.com:weaveworks/weave $TAG
N.B. if you're testing the release process, push to your fork
instead!
### Create Draft Release
You're now ready to draft your release notes:
bin/release draft
This has the following effects:
* A [release](https://help.github.com/articles/about-releases) is
created in GitHub for `$TAG`. This release is in the draft state, so
it is only visible to contributors; for **Prerelease** builds the
pre-release attribute will also be set
* The `weave` script is uploaded as an attachment to the release
Navigate to https://github.com/weaveworks/weave/releases, 'Edit' the
draft and input the release notes. When you are done make sure you
'Save draft' (and not 'Publish release'!).
Once the release notes have passed review, proceed to the publish
phase.
## Publish Phase
### Move/Force Push `latest_release` Tag
This step must only be performed for **Mainline** and **Branch**
releases:
git tag -af -m "Release $TAG" latest_release $TAG
git push -f git@github.com:weaveworks/weave latest_release
The `latest_release` tag *must* point at `$TAG`, *not* at `HEAD` -
the build script will complain otherwise.
N.B. if you're testing the release process, push to your fork
instead!
### Publish Release & Distributable Artefacts
You can now publish the release and upload the remaining
distributables to DockerHub:
bin/release publish
The effects of this step depend on the inferred release type. The
following occurs for all types:
* Docker images are tagged `$TAG` and pushed to DockerHub
* GitHub release moves from draft to published state
Additionally, for **Mainline** and **Branch** types:
* Release named `latest_release` is updated on GitHub
Finally, for **Mainline** releases only:
* Images tagged `latest` are updated on DockerHub
## Troubleshooting
There's a few things that can go wrong.
* If the build is wonky, e.g., the tests don't pass, you can delete
the directory in `./releases/`, fix whatever it is, move the
version tag (which should still be only local) and have another go.
* If the DockerHub pushes fail (which sadly seems to happen a lot),
you can just run `./bin/release publish` again.
* If you need to overwrite a release you can do so by manually
deleting the GitHub version release and re-running the process.
Please note that the DockerHub `latest` images, GitHub
`latest_release` and download links may be in an inconsistent state
until the overwrite is completed.
+72
View File
@@ -0,0 +1,72 @@
package ipam
import (
"fmt"
"github.com/weaveworks/weave/net/address"
)
type allocateResult struct {
addr address.Address
err error
}
type allocate struct {
resultChan chan<- allocateResult
ident string
r address.Range // Range we are trying to allocate within
hasBeenCancelled func() bool
}
// Try returns true if the request is completed, false if pending
func (g *allocate) Try(alloc *Allocator) bool {
if g.hasBeenCancelled() {
g.Cancel()
return true
}
if addr, found := alloc.lookupOwned(g.ident, g.r); found {
g.resultChan <- allocateResult{addr, nil}
return true
}
if !alloc.universe.Overlaps(g.r) {
g.resultChan <- allocateResult{0, fmt.Errorf("range %s out of bounds: %s", g.r, alloc.universe)}
return true
}
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}
return true
}
// out of space
donors := alloc.ring.ChoosePeersToAskForSpace(g.r.Start, g.r.End)
for _, donor := range donors {
if err := alloc.sendSpaceRequest(donor, g.r); err != nil {
alloc.debugln("Problem asking peer", donor, "for space:", err)
} else {
alloc.debugln("Decided to ask peer", donor, "for space in range", g.r)
break
}
}
return false
}
func (g *allocate) Cancel() {
g.resultChan <- allocateResult{0, &errorCancelled{"Allocate", g.ident}}
}
func (g *allocate) ForContainer(ident string) bool {
return g.ident == ident
}
+778
View File
@@ -0,0 +1,778 @@
package ipam
import (
"bytes"
"encoding/gob"
"fmt"
"sort"
"time"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/ipam/paxos"
"github.com/weaveworks/weave/ipam/ring"
"github.com/weaveworks/weave/ipam/space"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
)
// Kinds of message we can unicast to other peers
const (
msgSpaceRequest = iota
msgRingUpdate
msgSpaceRequestDenied
paxosInterval = time.Second * 5
MinSubnetSize = 4 // first and last addresses are excluded, so 2 would be too small
)
// operation represents something which Allocator wants to do, but
// which may need to wait until some other message arrives.
type operation interface {
// Try attempts this operations and returns false if needs to be tried again.
Try(alloc *Allocator) bool
Cancel()
// Does this operation pertain to the given container id?
// Used for tidying up pending operations when containers die.
ForContainer(ident string) bool
}
// Allocator brings together Ring and space.Set, and does the
// necessary plumbing. Runs as a single-threaded Actor, so no locks
// are used around data structures.
type Allocator struct {
actionChan chan<- func()
ourName mesh.PeerName
universe address.Range // superset of all ranges
ring *ring.Ring // information on ranges owned by all peers
space space.Space // more detail on ranges owned by us
owned map[string][]address.Address // who owns what addresses, indexed by container-ID
nicknames map[mesh.PeerName]string // so we can map nicknames for rmpeer
pendingAllocates []operation // held until we get some free space
pendingClaims []operation // held until we know who owns the space
gossip mesh.Gossip // our link to the outside world for sending messages
paxos *paxos.Node
paxosTicker *time.Ticker
shuttingDown bool // to avoid doing any requests while trying to shut down
isKnownPeer func(mesh.PeerName) bool
now func() time.Time
}
// NewAllocator creates and initialises a new Allocator
func NewAllocator(ourName mesh.PeerName, ourUID mesh.PeerUID, ourNickname string, universe address.Range, quorum uint, isKnownPeer func(name mesh.PeerName) bool) *Allocator {
return &Allocator{
ourName: ourName,
universe: universe,
ring: ring.New(universe.Start, universe.End, ourName),
owned: make(map[string][]address.Address),
paxos: paxos.NewNode(ourName, ourUID, quorum),
nicknames: map[mesh.PeerName]string{ourName: ourNickname},
isKnownPeer: isKnownPeer,
now: time.Now,
}
}
// Start runs the allocator goroutine
func (alloc *Allocator) Start() {
actionChan := make(chan func(), mesh.ChannelSize)
alloc.actionChan = actionChan
go alloc.actorLoop(actionChan)
}
// Stop makes the actor routine exit, for test purposes ONLY because any
// calls after this is processed will hang. Async.
func (alloc *Allocator) Stop() {
alloc.actionChan <- nil
}
// Operation life cycle
// Given an operation, try it, and add it to the pending queue if it didn't succeed
func (alloc *Allocator) doOperation(op operation, ops *[]operation) {
alloc.actionChan <- func() {
if alloc.shuttingDown {
op.Cancel()
return
}
if !op.Try(alloc) {
*ops = append(*ops, op)
}
}
}
// Given an operation, remove it from the pending queue
// Note the op may not be on the queue; it may have
// already succeeded. If it is on the queue, we call
// cancel on it, allowing callers waiting for the resultChans
// to unblock.
func (alloc *Allocator) cancelOp(op operation, ops *[]operation) {
for i, op := range *ops {
if op == op {
*ops = append((*ops)[:i], (*ops)[i+1:]...)
op.Cancel()
break
}
}
}
// Cancel all operations in a queue
func (alloc *Allocator) cancelOps(ops *[]operation) {
for _, op := range *ops {
op.Cancel()
}
*ops = []operation{}
}
// Cancel all operations for a given container id, returns true
// if we found any.
func (alloc *Allocator) cancelOpsFor(ops *[]operation, ident string) bool {
var found bool
for i := 0; i < len(*ops); {
if op := (*ops)[i]; op.ForContainer(ident) {
found = true
op.Cancel()
*ops = append((*ops)[:i], (*ops)[i+1:]...)
} else {
i++
}
}
return found
}
// Try all pending operations
func (alloc *Allocator) tryPendingOps() {
// The slightly different semantics requires us to operate on 'claims' and
// 'allocates' separately:
// Claims must be tried before Allocates
for i := 0; i < len(alloc.pendingClaims); {
op := alloc.pendingClaims[i]
if !op.Try(alloc) {
i++
continue
}
alloc.pendingClaims = append(alloc.pendingClaims[:i], alloc.pendingClaims[i+1:]...)
}
// When the first Allocate fails, bail - no need to
// send too many begs for space.
for i := 0; i < len(alloc.pendingAllocates); {
op := alloc.pendingAllocates[i]
if !op.Try(alloc) {
break
}
alloc.pendingAllocates = append(alloc.pendingAllocates[:i], alloc.pendingAllocates[i+1:]...)
}
}
func (alloc *Allocator) spaceRequestDenied(sender mesh.PeerName, r address.Range) {
for i := 0; i < len(alloc.pendingClaims); {
claim := alloc.pendingClaims[i].(*claim)
if r.Contains(claim.addr) {
claim.deniedBy(alloc, sender)
alloc.pendingClaims = append(alloc.pendingClaims[:i], alloc.pendingClaims[i+1:]...)
continue
}
i++
}
}
type errorCancelled struct {
kind string
ident string
}
func (e *errorCancelled) Error() string {
return fmt.Sprintf("%s request for %s cancelled", e.kind, e.ident)
}
// Actor client API
// Allocate (Sync) - get new IP address for container with given name in range
// if there isn't any space in that range we block indefinitely
func (alloc *Allocator) Allocate(ident string, r address.Range, hasBeenCancelled func() bool) (address.Address, error) {
resultChan := make(chan allocateResult)
op := &allocate{resultChan: resultChan, ident: ident, r: r, hasBeenCancelled: hasBeenCancelled}
alloc.doOperation(op, &alloc.pendingAllocates)
result := <-resultChan
return result.addr, result.err
}
// Lookup (Sync) - get existing IP address for container with given name in range
func (alloc *Allocator) Lookup(ident string, r address.Range) (address.Address, error) {
resultChan := make(chan allocateResult)
alloc.actionChan <- func() {
if addr, found := alloc.lookupOwned(ident, r); found {
resultChan <- allocateResult{addr: addr}
return
}
resultChan <- allocateResult{err: fmt.Errorf("lookup: no address found for %s in range %s", ident, r)}
}
result := <-resultChan
return result.addr, result.err
}
// Claim an address that we think we should own (Sync)
func (alloc *Allocator) Claim(ident string, addr address.Address, noErrorOnUnknown bool) error {
resultChan := make(chan error)
op := &claim{resultChan: resultChan, ident: ident, addr: addr, noErrorOnUnknown: noErrorOnUnknown}
alloc.doOperation(op, &alloc.pendingClaims)
return <-resultChan
}
// ContainerDied is provided to satisfy the updater interface; does a 'Delete' underneath. Sync.
func (alloc *Allocator) ContainerDied(ident string) {
if err := alloc.Delete(ident); err == nil {
alloc.debugln("Container", ident, "died; released addresses")
}
}
func (alloc *Allocator) ContainerStarted(ident string) {}
// Delete (Sync) - release all IP addresses for container with given name
func (alloc *Allocator) Delete(ident string) error {
errChan := make(chan error)
alloc.actionChan <- func() {
addrs, found := alloc.owned[ident]
for _, addr := range addrs {
alloc.space.Free(addr)
}
delete(alloc.owned, ident)
// Also remove any pending ops
found = alloc.cancelOpsFor(&alloc.pendingAllocates, ident) || found
found = alloc.cancelOpsFor(&alloc.pendingClaims, ident) || found
if !found {
errChan <- fmt.Errorf("Delete: no addresses for %s", ident)
return
}
errChan <- nil
}
return <-errChan
}
// Free (Sync) - release single IP address for container
func (alloc *Allocator) Free(ident string, addrToFree address.Address) error {
errChan := make(chan error)
alloc.actionChan <- func() {
addrs := alloc.owned[ident]
for i, ownedAddr := range addrs {
if ownedAddr == addrToFree {
alloc.debugln("Freed", addrToFree, "for", ident)
if len(addrs) == 1 {
delete(alloc.owned, ident)
} else {
alloc.owned[ident] = append(addrs[:i], addrs[i+1:]...)
}
alloc.space.Free(addrToFree)
errChan <- nil
return
}
}
errChan <- fmt.Errorf("Free: address %s not found for %s", addrToFree, ident)
}
return <-errChan
}
func (alloc *Allocator) pickPeerFromNicknames(isValid func(mesh.PeerName) bool) mesh.PeerName {
for name := range alloc.nicknames {
if name != alloc.ourName && isValid(name) {
return name
}
}
return mesh.UnknownPeerName
}
func (alloc *Allocator) pickPeerForTransfer() mesh.PeerName {
// first try alive peers that actively participate in IPAM (i.e. have entries)
if heir := alloc.ring.PickPeerForTransfer(alloc.isKnownPeer); heir != mesh.UnknownPeerName {
return heir
}
// next try alive peers that have IPAM enabled but have no entries
if heir := alloc.pickPeerFromNicknames(alloc.isKnownPeer); heir != mesh.UnknownPeerName {
return heir
}
// next try disappeared peers that still have entries
t := func(mesh.PeerName) bool { return true }
if heir := alloc.ring.PickPeerForTransfer(t); heir != mesh.UnknownPeerName {
return heir
}
// finally, disappeared peers that that passively participated in IPAM
return alloc.pickPeerFromNicknames(t)
}
// Shutdown (Sync)
func (alloc *Allocator) Shutdown() {
alloc.infof("Shutdown")
doneChan := make(chan struct{})
alloc.actionChan <- func() {
alloc.shuttingDown = true
alloc.cancelOps(&alloc.pendingClaims)
alloc.cancelOps(&alloc.pendingAllocates)
if heir := alloc.pickPeerForTransfer(); heir != mesh.UnknownPeerName {
alloc.ring.Transfer(alloc.ourName, heir)
alloc.space.Clear()
alloc.gossip.GossipBroadcast(alloc.Gossip())
time.Sleep(100 * time.Millisecond)
}
doneChan <- struct{}{}
}
<-doneChan
}
// AdminTakeoverRanges (Sync) - take over the ranges owned by a given peer.
// Only done on adminstrator command.
func (alloc *Allocator) AdminTakeoverRanges(peerNameOrNickname string) error {
resultChan := make(chan error)
alloc.actionChan <- func() {
peername, err := alloc.lookupPeername(peerNameOrNickname)
if err != nil {
resultChan <- fmt.Errorf("Cannot find peer '%s'", peerNameOrNickname)
return
}
alloc.debugln("AdminTakeoverRanges:", peername)
if peername == alloc.ourName {
resultChan <- fmt.Errorf("Cannot take over ranges from yourself!")
return
}
newRanges, err := alloc.ring.Transfer(peername, alloc.ourName)
alloc.space.AddRanges(newRanges)
resultChan <- err
}
return <-resultChan
}
// Lookup a PeerName by nickname or stringified PeerName. We can't
// call into the router for this because we are interested in peers
// that have gone away but are still in the ring, which is why we
// maintain our own nicknames map.
func (alloc *Allocator) lookupPeername(name string) (mesh.PeerName, error) {
for peername, nickname := range alloc.nicknames {
if nickname == name {
return peername, nil
}
}
return mesh.PeerNameFromString(name)
}
// Restrict the peers in "nicknames" to those in the ring plus peers known to the router
func (alloc *Allocator) pruneNicknames() {
ringPeers := alloc.ring.PeerNames()
for name := range alloc.nicknames {
if _, ok := ringPeers[name]; !ok && !alloc.isKnownPeer(name) {
delete(alloc.nicknames, name)
}
}
}
func (alloc *Allocator) annotatePeernames(names []mesh.PeerName) []string {
var res []string
for _, name := range names {
if nickname, found := alloc.nicknames[name]; found {
res = append(res, fmt.Sprint(name, "(", nickname, ")"))
} else {
res = append(res, name.String())
}
}
return res
}
func decodeRange(msg []byte) (r address.Range, err error) {
decoder := gob.NewDecoder(bytes.NewReader(msg))
return r, decoder.Decode(&r)
}
// OnGossipUnicast (Sync)
func (alloc *Allocator) OnGossipUnicast(sender mesh.PeerName, msg []byte) error {
alloc.debugln("OnGossipUnicast from", sender, ": ", len(msg), "bytes")
resultChan := make(chan error)
alloc.actionChan <- func() {
switch msg[0] {
case msgSpaceRequest:
// some other peer asked us for space
r, err := decodeRange(msg[1:])
if err == nil {
alloc.donateSpace(r, sender)
}
resultChan <- err
case msgSpaceRequestDenied:
r, err := decodeRange(msg[1:])
if err == nil {
alloc.spaceRequestDenied(sender, r)
}
resultChan <- err
case msgRingUpdate:
resultChan <- alloc.update(sender, msg[1:])
}
}
return <-resultChan
}
// OnGossipBroadcast (Sync)
func (alloc *Allocator) OnGossipBroadcast(sender mesh.PeerName, msg []byte) (mesh.GossipData, error) {
alloc.debugln("OnGossipBroadcast from", sender, ":", len(msg), "bytes")
resultChan := make(chan error)
alloc.actionChan <- func() {
resultChan <- alloc.update(sender, msg)
}
return alloc.Gossip(), <-resultChan
}
type gossipState struct {
// We send a timstamp along with the information to be
// gossipped in order to detect skewed clocks
Now int64
Nicknames map[mesh.PeerName]string
Paxos paxos.GossipState
Ring *ring.Ring
}
func (alloc *Allocator) encode() []byte {
data := gossipState{
Now: alloc.now().Unix(),
Nicknames: alloc.nicknames,
}
// We're only interested in Paxos until we have a Ring.
if alloc.ring.Empty() {
data.Paxos = alloc.paxos.GossipState()
} else {
data.Ring = alloc.ring
}
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
if err := enc.Encode(data); err != nil {
panic(err)
}
return buf.Bytes()
}
// Encode (Sync)
func (alloc *Allocator) Encode() []byte {
resultChan := make(chan []byte)
alloc.actionChan <- func() {
resultChan <- alloc.encode()
}
return <-resultChan
}
// OnGossip (Sync)
func (alloc *Allocator) OnGossip(msg []byte) (mesh.GossipData, error) {
alloc.debugln("Allocator.OnGossip:", len(msg), "bytes")
resultChan := make(chan error)
alloc.actionChan <- func() {
resultChan <- alloc.update(mesh.UnknownPeerName, msg)
}
return nil, <-resultChan // for now, we never propagate updates. TBD
}
// GossipData implementation is trivial - we always gossip the latest
// data we have at time of sending
type ipamGossipData struct {
alloc *Allocator
}
func (d *ipamGossipData) Merge(other mesh.GossipData) mesh.GossipData {
return d // no-op
}
func (d *ipamGossipData) Encode() [][]byte {
return [][]byte{d.alloc.Encode()}
}
// Gossip returns a GossipData implementation, which in this case always
// returns the latest ring state (and does nothing on merge)
func (alloc *Allocator) Gossip() mesh.GossipData {
return &ipamGossipData{alloc}
}
// SetInterfaces gives the allocator two interfaces for talking to the outside world
func (alloc *Allocator) SetInterfaces(gossip mesh.Gossip) {
alloc.gossip = gossip
}
// ACTOR server
func (alloc *Allocator) actorLoop(actionChan <-chan func()) {
for {
var tickChan <-chan time.Time
if alloc.paxosTicker != nil {
tickChan = alloc.paxosTicker.C
}
select {
case action := <-actionChan:
if action == nil {
return
}
action()
case <-tickChan:
alloc.propose()
}
alloc.assertInvariants()
alloc.reportFreeSpace()
}
}
// Helper functions
// Ensure we are making progress towards an established ring
func (alloc *Allocator) establishRing() {
if !alloc.ring.Empty() || alloc.paxosTicker != nil {
return
}
alloc.propose()
if ok, cons := alloc.paxos.Consensus(); ok {
// If the quorum was 1, then proposing immediately
// leads to consensus
alloc.createRing(cons.Value)
} else {
// re-try until we get consensus
alloc.paxosTicker = time.NewTicker(paxosInterval)
}
}
func (alloc *Allocator) createRing(peers []mesh.PeerName) {
alloc.debugln("Paxos consensus:", peers)
alloc.ring.ClaimForPeers(normalizeConsensus(peers))
alloc.gossip.GossipBroadcast(alloc.Gossip())
alloc.ringUpdated()
}
func (alloc *Allocator) ringUpdated() {
// When we have a ring, we don't need paxos any more
if alloc.paxos != nil {
alloc.paxos = nil
if alloc.paxosTicker != nil {
alloc.paxosTicker.Stop()
alloc.paxosTicker = nil
}
}
alloc.space.UpdateRanges(alloc.ring.OwnedRanges())
alloc.tryPendingOps()
}
// For compatibility with sort.Interface
type peerNames []mesh.PeerName
func (a peerNames) Len() int { return len(a) }
func (a peerNames) Less(i, j int) bool { return a[i] < a[j] }
func (a peerNames) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// When we get a consensus from Paxos, the peer names are not in a
// defined order and may contain duplicates. This function sorts them
// and de-dupes.
func normalizeConsensus(consensus []mesh.PeerName) []mesh.PeerName {
if len(consensus) == 0 {
return nil
}
peers := make(peerNames, len(consensus))
copy(peers, consensus)
sort.Sort(peers)
dst := 0
for src := 1; src < len(peers); src++ {
if peers[dst] != peers[src] {
dst++
peers[dst] = peers[src]
}
}
return peers[:dst+1]
}
func (alloc *Allocator) propose() {
alloc.debugf("Paxos proposing")
alloc.paxos.Propose()
alloc.gossip.GossipBroadcast(alloc.Gossip())
}
func encodeRange(r address.Range) []byte {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
if err := enc.Encode(r); err != nil {
panic(err)
}
return buf.Bytes()
}
func (alloc *Allocator) sendSpaceRequest(dest mesh.PeerName, r address.Range) error {
msg := append([]byte{msgSpaceRequest}, encodeRange(r)...)
return alloc.gossip.GossipUnicast(dest, msg)
}
func (alloc *Allocator) sendSpaceRequestDenied(dest mesh.PeerName, r address.Range) error {
msg := append([]byte{msgSpaceRequestDenied}, encodeRange(r)...)
return alloc.gossip.GossipUnicast(dest, msg)
}
func (alloc *Allocator) sendRingUpdate(dest mesh.PeerName) {
msg := append([]byte{msgRingUpdate}, alloc.encode()...)
alloc.gossip.GossipUnicast(dest, msg)
}
func (alloc *Allocator) update(sender mesh.PeerName, msg []byte) error {
reader := bytes.NewReader(msg)
decoder := gob.NewDecoder(reader)
var data gossipState
var err error
if err := decoder.Decode(&data); err != nil {
return err
}
deltat := time.Unix(data.Now, 0).Sub(alloc.now())
if deltat > time.Hour || -deltat > time.Hour {
return fmt.Errorf("clock skew of %v detected, ignoring update", deltat)
}
// Merge nicknames
for peer, nickname := range data.Nicknames {
alloc.nicknames[peer] = nickname
}
// only one of Ring and Paxos should be present. And we
// shouldn't get updates for a empty Ring. But tolerate
// them just in case.
if data.Ring != nil {
switch err = alloc.ring.Merge(*data.Ring); err {
case ring.ErrDifferentSeeds:
return fmt.Errorf("IP allocation was seeded by different peers (received: %v, ours: %v)",
alloc.annotatePeernames(data.Ring.Seeds), alloc.annotatePeernames(alloc.ring.Seeds))
case ring.ErrDifferentRange:
return fmt.Errorf("Incompatible IP allocation ranges (received: %s, ours: %s)",
data.Ring.Range().AsCIDRString(), alloc.ring.Range().AsCIDRString())
default:
if err == nil && !alloc.ring.Empty() {
alloc.pruneNicknames()
alloc.ringUpdated()
}
return err
}
}
if data.Paxos != nil {
if alloc.ring.Empty() {
if alloc.paxos.Update(data.Paxos) {
if alloc.paxos.Think() {
// If something important changed, broadcast
alloc.gossip.GossipBroadcast(alloc.Gossip())
}
if ok, cons := alloc.paxos.Consensus(); ok {
alloc.createRing(cons.Value)
}
}
} else if sender != mesh.UnknownPeerName {
// Sender is trying to initialize a ring, but we have one
// already - send it straight back
alloc.sendRingUpdate(sender)
}
}
return nil
}
func (alloc *Allocator) donateSpace(r address.Range, to mesh.PeerName) {
// No matter what we do, we'll send a unicast gossip
// of our ring back to tha chap who asked for space.
// This serves to both tell him of any space we might
// have given him, or tell him where he might find some
// more.
defer alloc.sendRingUpdate(to)
alloc.debugln("Peer", to, "asked me for space")
chunk, ok := alloc.space.Donate(r)
if !ok {
free := alloc.space.NumFreeAddressesInRange(r)
common.Assert(free == 0)
alloc.debugln("No space to give to peer", to)
// separate message maintains backwards-compatibility:
// down-level peers will ignore this and still get the ring update.
alloc.sendSpaceRequestDenied(to, r)
return
}
alloc.debugln("Giving range", chunk, "to", to)
alloc.ring.GrantRangeToHost(chunk.Start, chunk.End, to)
alloc.sendRingUpdate(to)
}
func (alloc *Allocator) assertInvariants() {
// We need to ensure all ranges the ring thinks we own have
// a corresponding space in the space set, and vice versa
checkSpace := space.New()
checkSpace.AddRanges(alloc.ring.OwnedRanges())
ranges := checkSpace.OwnedRanges()
spaces := alloc.space.OwnedRanges()
common.Assert(len(ranges) == len(spaces))
for i := 0; i < len(ranges); i++ {
r := ranges[i]
s := spaces[i]
common.Assert(s.Start == r.Start && s.End == r.End)
}
}
func (alloc *Allocator) reportFreeSpace() {
ranges := alloc.ring.OwnedRanges()
if len(ranges) == 0 {
return
}
freespace := make(map[address.Address]address.Offset)
for _, r := range ranges {
freespace[r.Start] = alloc.space.NumFreeAddressesInRange(r)
}
alloc.ring.ReportFree(freespace)
}
// Owned addresses
// NB: addr must not be owned by ident already
func (alloc *Allocator) addOwned(ident string, addr address.Address) {
alloc.owned[ident] = append(alloc.owned[ident], addr)
}
func (alloc *Allocator) lookupOwned(ident string, r address.Range) (address.Address, bool) {
for _, addr := range alloc.owned[ident] {
if r.Contains(addr) {
return addr, true
}
}
return 0, false
}
func (alloc *Allocator) findOwner(addr address.Address) string {
for ident, addrs := range alloc.owned {
for _, candidate := range addrs {
if candidate == addr {
return ident
}
}
}
return ""
}
// Logging
func (alloc *Allocator) infof(fmt string, args ...interface{}) {
common.Log.Infof("[allocator %s] "+fmt, append([]interface{}{alloc.ourName}, args...)...)
}
func (alloc *Allocator) debugln(args ...interface{}) {
common.Log.Debugln(append([]interface{}{fmt.Sprintf("[allocator %s]:", alloc.ourName)}, args...)...)
}
func (alloc *Allocator) debugf(fmt string, args ...interface{}) {
common.Log.Debugf("[allocator %s] "+fmt, append([]interface{}{alloc.ourName}, args...)...)
}
+576
View File
@@ -0,0 +1,576 @@
package ipam
import (
"fmt"
"math/rand"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/testing/gossip"
)
const (
testStart1 = "10.0.1.0"
testStart2 = "10.0.2.0"
testStart3 = "10.0.3.0"
)
func returnFalse() bool { return false }
func TestAllocFree(t *testing.T) {
const (
container1 = "abcdef"
container2 = "baddf00d"
container3 = "b01df00d"
universe = "10.0.3.0/26"
subnet1 = "10.0.3.0/28"
subnet2 = "10.0.3.32/28"
testAddr1 = "10.0.3.1"
testAddr2 = "10.0.3.33"
spaceSize = 62 // 64 IP addresses in /26, minus .0 and .63
)
alloc, subnet := makeAllocatorWithMockGossip(t, "01:00:00:01:00:00", universe, 1)
defer alloc.Stop()
_, cidr1, _ := address.ParseCIDR(subnet1)
_, cidr2, _ := address.ParseCIDR(subnet2)
alloc.claimRingForTesting()
addr1, err := alloc.Allocate(container1, cidr1.HostRange(), returnFalse)
require.NoError(t, err)
require.Equal(t, testAddr1, addr1.String(), "address")
addr2, err := alloc.Allocate(container1, cidr2.HostRange(), returnFalse)
require.NoError(t, err)
require.Equal(t, testAddr2, addr2.String(), "address")
// Ask for another address for a different container and check it's different
addr1b, _ := alloc.Allocate(container2, cidr1.HostRange(), returnFalse)
if addr1b.String() == testAddr1 {
t.Fatalf("Expected different address but got %s", addr1b.String())
}
// Ask for the first container again and we should get the same addresses again
addr1a, _ := alloc.Allocate(container1, cidr1.HostRange(), returnFalse)
require.Equal(t, testAddr1, addr1a.String(), "address")
addr2a, _ := alloc.Allocate(container1, cidr2.HostRange(), returnFalse)
require.Equal(t, testAddr2, addr2a.String(), "address")
// Now delete the first container, and we should get its addresses back
require.NoError(t, alloc.Delete(container1))
addr3, _ := alloc.Allocate(container3, cidr1.HostRange(), returnFalse)
require.Equal(t, testAddr1, addr3.String(), "address")
addr4, _ := alloc.Allocate(container3, cidr2.HostRange(), returnFalse)
require.Equal(t, testAddr2, addr4.String(), "address")
alloc.ContainerDied(container2)
alloc.ContainerDied(container3)
require.Equal(t, address.Offset(spaceSize), alloc.NumFreeAddresses(subnet))
}
func TestBootstrap(t *testing.T) {
const (
donateSize = 5
donateStart = "10.0.1.7"
ourNameString = "01:00:00:01:00:00"
peerNameString = "02:00:00:02:00:00"
)
alloc1, subnet := makeAllocatorWithMockGossip(t, ourNameString, testStart1+"/22", 2)
defer alloc1.Stop()
// Simulate another peer on the gossip network
alloc2, _ := makeAllocatorWithMockGossip(t, peerNameString, testStart1+"/22", 2)
defer alloc2.Stop()
alloc1.OnGossipBroadcast(alloc2.ourName, alloc2.Encode())
alloc1.actionChan <- func() { alloc1.tryPendingOps() }
ExpectBroadcastMessage(alloc1, nil) // alloc1 will try to form consensus
done := make(chan bool)
go func() {
alloc1.Allocate("somecontainer", subnet, returnFalse)
done <- true
}()
time.Sleep(100 * time.Millisecond)
AssertNothingSent(t, done)
CheckAllExpectedMessagesSent(alloc1, alloc2)
alloc1.actionChan <- func() { alloc1.tryPendingOps() }
AssertNothingSent(t, done)
CheckAllExpectedMessagesSent(alloc1, alloc2)
// alloc2 receives paxos update and broadcasts its reply
ExpectBroadcastMessage(alloc2, nil)
alloc2.OnGossipBroadcast(alloc1.ourName, alloc1.Encode())
ExpectBroadcastMessage(alloc1, nil)
alloc1.OnGossipBroadcast(alloc2.ourName, alloc2.Encode())
// both nodes will get consensus now so initialize the ring
ExpectBroadcastMessage(alloc2, nil)
ExpectBroadcastMessage(alloc2, nil)
alloc2.OnGossipBroadcast(alloc1.ourName, alloc1.Encode())
CheckAllExpectedMessagesSent(alloc1, alloc2)
alloc1.OnGossipBroadcast(alloc2.ourName, alloc2.Encode())
// now alloc1 should have space
AssertSent(t, done)
CheckAllExpectedMessagesSent(alloc1, alloc2)
}
func TestAllocatorClaim(t *testing.T) {
const (
container1 = "abcdef"
container3 = "b01df00d"
universe = "10.0.3.0/24"
testAddr1 = "10.0.3.2"
testAddr2 = "10.0.4.2"
)
allocs, _, subnet := makeNetworkOfAllocators(2, universe)
alloc := allocs[1]
defer alloc.Stop()
addr1, _ := address.ParseIP(testAddr1)
// First claim should trigger "dunno, I'm going to wait"
err := alloc.Claim(container3, addr1, true)
require.NoError(t, err)
// Do one allocate to ensure paxos is all done
alloc.Allocate("unused", subnet, returnFalse)
// Do an allocate on the other peer, which we will try to claim later
addrx, err := allocs[0].Allocate(container1, subnet, returnFalse)
// Now try the claim again
err = alloc.Claim(container3, addr1, true)
require.NoError(t, err)
// Check we get this address back if we try an allocate
addr3, _ := alloc.Allocate(container3, subnet, returnFalse)
require.Equal(t, testAddr1, addr3.String(), "address")
// one more claim should still work
err = alloc.Claim(container3, addr1, true)
require.NoError(t, err)
// claim for a different container should fail
err = alloc.Claim(container1, addr1, true)
require.Error(t, err)
// claiming the address allocated on the other peer should fail
err = alloc.Claim(container1, addrx, true)
require.Error(t, err, "claiming address allocated on other peer should fail")
// Check an address outside of our universe
addr2, _ := address.ParseIP(testAddr2)
err = alloc.Claim(container1, addr2, true)
require.NoError(t, err)
}
func (alloc *Allocator) pause() func() {
paused := make(chan struct{})
alloc.actionChan <- func() {
paused <- struct{}{}
<-paused
}
<-paused
return func() {
close(paused)
}
}
func TestCancel(t *testing.T) {
const (
CIDR = "10.0.1.7/26"
)
router := gossip.NewTestRouter(0.0)
alloc1, subnet := makeAllocator("01:00:00:02:00:00", CIDR, 2)
alloc1.SetInterfaces(router.Connect(alloc1.ourName, alloc1))
alloc2, _ := makeAllocator("02:00:00:02:00:00", CIDR, 2)
alloc2.SetInterfaces(router.Connect(alloc2.ourName, alloc2))
alloc1.claimRingForTesting(alloc1, alloc2)
alloc2.claimRingForTesting(alloc1, alloc2)
alloc1.Start()
alloc2.Start()
// tell peers about each other
alloc1.OnGossipBroadcast(alloc2.ourName, alloc2.Encode())
// Get some IPs, so each allocator has some space
res1, _ := alloc1.Allocate("foo", subnet, returnFalse)
common.Log.Debugf("res1 = %s", res1.String())
res2, _ := alloc2.Allocate("bar", subnet, returnFalse)
common.Log.Debugf("res2 = %s", res2.String())
if res1 == res2 {
require.FailNow(t, "Error: got same ips!")
}
// Now we're going to pause alloc2 and ask alloc1
// for an allocation
unpause := alloc2.pause()
// Use up all the IPs that alloc1 owns, so the allocation after this will prompt a request to alloc2
for i := 0; alloc1.NumFreeAddresses(subnet) > 0; i++ {
alloc1.Allocate(fmt.Sprintf("tmp%d", i), subnet, returnFalse)
}
cancelChan := make(chan bool, 1)
doneChan := make(chan bool)
go func() {
_, ok := alloc1.Allocate("baz", subnet,
func() bool {
select {
case <-cancelChan:
return true
default:
return false
}
})
doneChan <- ok == nil
}()
time.Sleep(100 * time.Millisecond)
AssertNothingSent(t, doneChan)
cancelChan <- true
unpause()
if <-doneChan {
require.FailNow(t, "Error: got result from Allocate")
}
}
func TestCancelOnDied(t *testing.T) {
const (
CIDR = "10.0.1.7/26"
container1 = "abcdef"
)
router := gossip.NewTestRouter(0.0)
alloc1, subnet := makeAllocator("01:00:00:02:00:00", CIDR, 2)
alloc1.SetInterfaces(router.Connect(alloc1.ourName, alloc1))
alloc1.Start()
doneChan := make(chan bool)
f := func() {
_, ok := alloc1.Allocate(container1, subnet, returnFalse)
doneChan <- ok == nil
}
// Attempt two allocations in parallel, to check that this is handled correctly
go f()
go f()
// Nothing should happen, because we declared the quorum as 2
time.Sleep(100 * time.Millisecond)
AssertNothingSent(t, doneChan)
alloc1.ContainerDied(container1)
// Check that the two allocations both exit with an error
if <-doneChan || <-doneChan {
require.FailNow(t, "Error: got result from Allocate")
}
}
func TestGossipShutdown(t *testing.T) {
const (
container1 = "abcdef"
container2 = "baddf00d"
universe = "10.0.3.0/30"
)
alloc, subnet := makeAllocatorWithMockGossip(t, "01:00:00:01:00:00", universe, 1)
defer alloc.Stop()
alloc.claimRingForTesting()
alloc.Allocate(container1, subnet, returnFalse)
alloc.Shutdown()
_, err := alloc.Allocate(container2, subnet, returnFalse) // trying to allocate after shutdown should fail
require.False(t, err == nil, "no address")
CheckAllExpectedMessagesSent(alloc)
}
func TestTransfer(t *testing.T) {
const (
cidr = "10.0.1.7/22"
)
allocs, router, subnet := makeNetworkOfAllocators(3, cidr)
alloc1 := allocs[0]
alloc2 := allocs[1]
alloc3 := allocs[2] // This will be 'master' and get the first range
_, err := alloc2.Allocate("foo", subnet, returnFalse)
require.True(t, err == nil, "Failed to get address")
_, err = alloc3.Allocate("bar", subnet, returnFalse)
require.True(t, err == nil, "Failed to get address")
alloc2.gossip.GossipBroadcast(alloc2.Gossip())
router.Flush()
alloc2.gossip.GossipBroadcast(alloc3.Gossip())
router.Flush()
router.RemovePeer(alloc2.ourName)
router.RemovePeer(alloc3.ourName)
alloc2.Stop()
alloc3.Stop()
router.Flush()
require.NoError(t, alloc1.AdminTakeoverRanges(alloc2.ourName.String()))
require.NoError(t, alloc1.AdminTakeoverRanges(alloc3.ourName.String()))
router.Flush()
require.Equal(t, address.Offset(1022), alloc1.NumFreeAddresses(subnet))
_, err = alloc1.Allocate("foo", subnet, returnFalse)
require.True(t, err == nil, "Failed to get address")
alloc1.Stop()
}
func TestFakeRouterSimple(t *testing.T) {
const (
cidr = "10.0.1.7/22"
)
allocs, _, subnet := makeNetworkOfAllocators(2, cidr)
defer stopNetworkOfAllocators(allocs)
alloc1 := allocs[0]
//alloc2 := allocs[1]
_, err := alloc1.Allocate("foo", subnet, returnFalse)
require.NoError(t, err, "Failed to get address")
}
func TestAllocatorFuzz(t *testing.T) {
const (
firstpass = 1000
secondpass = 10000
nodes = 5
maxAddresses = 1000
concurrency = 5
cidr = "10.0.1.7/22"
)
allocs, _, subnet := makeNetworkOfAllocators(nodes, cidr)
defer stopNetworkOfAllocators(allocs)
// Test state
// For each IP issued we store the allocator
// that issued it and the name of the container
// it was issued to.
type result struct {
name string
alloc int32
block bool
}
stateLock := sync.Mutex{}
state := make(map[string]result)
// Keep a list of addresses issued, so we
// Can pick random ones
var addrs []string
numPending := 0
rand.Seed(0)
// Remove item from list by swapping it with last
// and reducing slice length by 1
rm := func(xs []string, i int32) []string {
ls := len(xs) - 1
xs[i] = xs[ls]
return xs[:ls]
}
bumpPending := func() bool {
stateLock.Lock()
if len(addrs)+numPending >= maxAddresses {
stateLock.Unlock()
return false
}
numPending++
stateLock.Unlock()
return true
}
noteAllocation := func(allocIndex int32, name string, addr address.Address) {
//common.Log.Infof("Allocate: got address %s for name %s", addr, name)
addrStr := addr.String()
stateLock.Lock()
defer stateLock.Unlock()
if res, existing := state[addrStr]; existing {
panic(fmt.Sprintf("Dup found for address %s - %s and %s", addrStr,
name, res.name))
}
state[addrStr] = result{name, allocIndex, false}
addrs = append(addrs, addrStr)
numPending--
}
// Do a Allocate and check the address
// is unique. Needs a unique container
// name.
allocate := func(name string) {
if !bumpPending() {
return
}
allocIndex := rand.Int31n(nodes)
alloc := allocs[allocIndex]
//common.Log.Infof("Allocate: asking allocator %d", allocIndex)
addr, err := alloc.Allocate(name, subnet, returnFalse)
if err != nil {
panic(fmt.Sprintf("Could not allocate addr"))
}
noteAllocation(allocIndex, name, addr)
}
// Free a random address.
free := func() {
stateLock.Lock()
if len(addrs) == 0 {
stateLock.Unlock()
return
}
// Delete an existing allocation
// Pick random addr
addrIndex := rand.Int31n(int32(len(addrs)))
addr := addrs[addrIndex]
res := state[addr]
if res.block {
stateLock.Unlock()
return
}
addrs = rm(addrs, addrIndex)
delete(state, addr)
stateLock.Unlock()
alloc := allocs[res.alloc]
//common.Log.Infof("Freeing %s (%s) on allocator %d", res.name, addr, res.alloc)
oldAddr, err := address.ParseIP(addr)
if err != nil {
panic(err)
}
require.NoError(t, alloc.Free(res.name, oldAddr))
}
// Do a Allocate on an existing container & allocator
// and check we get the right answer.
allocateAgain := func() {
stateLock.Lock()
addrIndex := rand.Int31n(int32(len(addrs)))
addr := addrs[addrIndex]
res := state[addr]
if res.block {
stateLock.Unlock()
return
}
res.block = true
state[addr] = res
stateLock.Unlock()
alloc := allocs[res.alloc]
//common.Log.Infof("Asking for %s (%s) on allocator %d again", res.name, addr, res.alloc)
newAddr, _ := alloc.Allocate(res.name, subnet, returnFalse)
oldAddr, _ := address.ParseIP(addr)
if newAddr != oldAddr {
panic(fmt.Sprintf("Got different address for repeat request for %s: %s != %s", res.name, newAddr, oldAddr))
}
stateLock.Lock()
res.block = false
state[addr] = res
stateLock.Unlock()
}
// Claim a random address for a unique container name - may not succeed
claim := func(name string) {
if !bumpPending() {
return
}
allocIndex := rand.Int31n(nodes)
addressIndex := rand.Int31n(int32(subnet.Size()))
alloc := allocs[allocIndex]
addr := address.Add(subnet.Start, address.Offset(addressIndex))
err := alloc.Claim(name, addr, true)
if err == nil {
noteAllocation(allocIndex, name, addr)
}
}
// Run function _f_ _iterations_ times, in _concurrency_
// number of goroutines
doConcurrentIterations := func(iterations int, f func(int)) {
iterationsPerThread := iterations / concurrency
wg := sync.WaitGroup{}
for i := 0; i < concurrency; i++ {
wg.Add(1)
go func(j int) {
defer wg.Done()
for k := 0; k < iterationsPerThread; k++ {
f((j * iterationsPerThread) + k)
}
}(i)
}
wg.Wait()
}
// First pass, just allocate a bunch of ips
doConcurrentIterations(firstpass, func(iteration int) {
name := fmt.Sprintf("first%d", iteration)
allocate(name)
})
// Second pass, random ask for more allocations,
// or remove existing ones, or ask for allocation
// again.
doConcurrentIterations(secondpass, func(iteration int) {
r := rand.Float32()
switch {
case 0.0 <= r && r < 0.4:
// Ask for a new allocation
name := fmt.Sprintf("second%d", iteration)
allocate(name)
case (0.4 <= r && r < 0.8):
// free a random addr
free()
case 0.8 <= r && r < 0.95:
// ask for an existing name again, check we get same ip
allocateAgain()
case 0.95 <= r && r < 1.0:
name := fmt.Sprintf("second%d", iteration)
claim(name)
}
})
}
func TestGossipSkew(t *testing.T) {
alloc1, _ := makeAllocatorWithMockGossip(t, "01:00:00:01:00:00", "10.0.1.0/22", 2)
defer alloc1.Stop()
alloc2, _ := makeAllocatorWithMockGossip(t, "02:00:00:02:00:00", "10.0.1.0/22", 2)
alloc2.now = func() time.Time { return time.Now().Add(time.Hour * 2) }
defer alloc2.Stop()
if _, err := alloc1.OnGossipBroadcast(alloc2.ourName, alloc2.Encode()); err == nil {
t.Fail()
}
}
+103
View File
@@ -0,0 +1,103 @@
package ipam
import (
"fmt"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
)
type claim struct {
resultChan chan<- error
ident string
addr address.Address
noErrorOnUnknown bool
}
// Send an error (or nil for success) back to caller listening on resultChan
func (c *claim) sendResult(result error) {
// Make sure we only send a result once, since listener stops listening after that
if c.resultChan != nil {
c.resultChan <- result
close(c.resultChan)
c.resultChan = nil
return
}
if result != nil {
common.Log.Errorln("[allocator] " + result.Error())
}
}
// Try returns true for success (or failure), false if we need to try again later
func (c *claim) Try(alloc *Allocator) bool {
if !alloc.ring.Contains(c.addr) {
// Address not within our universe; assume user knows what they are doing
alloc.infof("Ignored address %s claimed by %s - not in our universe", c.addr, c.ident)
c.sendResult(nil)
return true
}
alloc.establishRing()
switch owner := alloc.ring.Owner(c.addr); owner {
case alloc.ourName:
// success
case mesh.UnknownPeerName:
// If our ring doesn't know, it must be empty.
if c.noErrorOnUnknown {
alloc.infof("Claim %s for %s: address allocator still initializing; will try later.", c.addr, c.ident)
c.sendResult(nil)
} else {
c.sendResult(fmt.Errorf("%s is in the range %s, but the allocator is not initialized yet", c.addr, alloc.universe.AsCIDRString()))
}
return false
default:
alloc.debugf("requesting address %s from other peer %s", c.addr, owner)
err := alloc.sendSpaceRequest(owner, address.NewRange(c.addr, 1))
if err != nil { // can't speak to owner right now
if c.noErrorOnUnknown {
alloc.infof("Claim %s for %s: %s; will try later.", c.addr, c.ident, err)
c.sendResult(nil)
} else { // just tell the user they can't do this.
c.deniedBy(alloc, owner)
}
}
return false
}
// We are the owner, check we haven't given it to another container
switch existingIdent := alloc.findOwner(c.addr); existingIdent {
case "":
if err := alloc.space.Claim(c.addr); err == nil {
alloc.debugln("Claimed", c.addr, "for", c.ident)
alloc.addOwned(c.ident, c.addr)
c.sendResult(nil)
} else {
c.sendResult(err)
}
case c.ident:
// same identifier is claiming same address; that's OK
c.sendResult(nil)
default:
// Addr already owned by container on this machine
c.sendResult(fmt.Errorf("address %s is already owned by %s", c.addr.String(), existingIdent))
}
return true
}
func (c *claim) deniedBy(alloc *Allocator, owner mesh.PeerName) {
name, found := alloc.nicknames[owner]
if found {
name = " (" + name + ")"
}
c.sendResult(fmt.Errorf("address %s is owned by other peer %s%s", c.addr.String(), owner, name))
}
func (c *claim) Cancel() {
c.sendResult(&errorCancelled{"Claim", c.ident})
}
func (c *claim) ForContainer(ident string) bool {
return c.ident == ident
}
+152
View File
@@ -0,0 +1,152 @@
package ipam
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/common/docker"
"github.com/weaveworks/weave/net/address"
)
func badRequest(w http.ResponseWriter, err error) {
http.Error(w, err.Error(), http.StatusBadRequest)
common.Log.Warningln("[allocator]:", err.Error())
}
func parseCIDR(w http.ResponseWriter, cidrStr string) (address.CIDR, bool) {
subnetAddr, cidr, err := address.ParseCIDR(cidrStr)
if err != nil {
badRequest(w, err)
return address.CIDR{}, false
}
if cidr.Start != subnetAddr {
badRequest(w, fmt.Errorf("Invalid subnet %s - bits after network prefix are not all zero", cidrStr))
return address.CIDR{}, false
}
return cidr, true
}
func (alloc *Allocator) handleHTTPAllocate(dockerCli *docker.Client, w http.ResponseWriter, ident string, checkAlive bool, subnet address.CIDR) {
closedChan := w.(http.CloseNotifier).CloseNotify()
addr, err := alloc.Allocate(ident, subnet.HostRange(),
func() bool {
select {
case <-closedChan:
return true
default:
res := checkAlive && dockerCli != nil && dockerCli.IsContainerNotRunning(ident)
checkAlive = false // we check only once; if the container dies later we learn about that through events
return res
}
})
if err != nil {
if _, ok := err.(*errorCancelled); ok { // cancellation is not really an error
common.Log.Infoln("[allocator]:", err.Error())
fmt.Fprint(w, "cancelled")
return
}
badRequest(w, err)
return
}
fmt.Fprintf(w, "%s/%d", addr, subnet.PrefixLen)
}
// 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"]
ipStr := vars["ip"]
noErrorOnUnknown := r.FormValue("noErrorOnUnknown") == "true"
if ip, err := address.ParseIP(ipStr); err != nil {
badRequest(w, err)
return
} else if err := alloc.Claim(ident, ip, noErrorOnUnknown); err != nil {
badRequest(w, fmt.Errorf("Unable to claim: %s", err))
return
}
w.WriteHeader(204)
})
router.Methods("GET").Path("/ip/{id}/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
if subnet, ok := parseCIDR(w, vars["ip"]+"/"+vars["prefixlen"]); ok {
addr, err := alloc.Lookup(vars["id"], subnet.HostRange())
if err != nil {
http.NotFound(w, r)
return
}
fmt.Fprintf(w, "%s/%d", addr, subnet.PrefixLen)
}
})
router.Methods("GET").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
addr, err := alloc.Lookup(mux.Vars(r)["id"], defaultSubnet.HostRange())
if err != nil {
http.NotFound(w, r)
return
}
fmt.Fprintf(w, "%s/%d", addr, defaultSubnet.PrefixLen)
})
router.Methods("POST").Path("/ip/{id}/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
if subnet, ok := parseCIDR(w, vars["ip"]+"/"+vars["prefixlen"]); ok {
alloc.handleHTTPAllocate(dockerCli, w, vars["id"], r.FormValue("check-alive") == "true", subnet)
}
})
router.Methods("POST").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
alloc.handleHTTPAllocate(dockerCli, w, vars["id"], r.FormValue("check-alive") == "true", defaultSubnet)
})
router.Methods("DELETE").Path("/ip/{id}/{ip}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
ident := vars["id"]
ipStr := vars["ip"]
if ip, err := address.ParseIP(ipStr); err != nil {
badRequest(w, err)
return
} else if err := alloc.Free(ident, ip); err != nil {
badRequest(w, fmt.Errorf("Unable to free: %s", err))
return
}
w.WriteHeader(204)
})
router.Methods("DELETE").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ident := mux.Vars(r)["id"]
if err := alloc.Delete(ident); err != nil {
badRequest(w, err)
return
}
w.WriteHeader(204)
})
router.Methods("DELETE").Path("/peer").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
alloc.Shutdown()
w.WriteHeader(204)
})
router.Methods("DELETE").Path("/peer/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ident := mux.Vars(r)["id"]
if err := alloc.AdminTakeoverRanges(ident); err != nil {
badRequest(w, err)
return
}
w.WriteHeader(204)
})
}
+177
View File
@@ -0,0 +1,177 @@
package ipam
import (
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"strings"
"testing"
"time"
"github.com/gorilla/mux"
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/net/address"
)
func HTTPPost(t *testing.T, url string) string {
resp, err := http.Post(url, "", nil)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode, "http response")
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
return string(body)
}
func HTTPGet(t *testing.T, url string) string {
resp, err := http.Get(url)
require.NoError(t, err)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
return string(body)
}
func doHTTP(method string, url string) (resp *http.Response, err error) {
req, _ := http.NewRequest(method, url, nil)
return http.DefaultClient.Do(req)
}
func listenHTTP(alloc *Allocator, subnet address.CIDR) int {
router := mux.NewRouter()
router.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, fmt.Sprintln(alloc))
})
alloc.HandleHTTP(router, subnet, nil)
httpListener, err := net.Listen("tcp", ":0")
if err != nil {
common.Log.Fatal("Unable to create http listener: ", err)
}
go func() {
srv := &http.Server{Handler: router}
if err := srv.Serve(httpListener); err != nil {
common.Log.Fatal("Unable to serve http: ", err)
}
}()
return httpListener.Addr().(*net.TCPAddr).Port
}
func identURL(port int, containerID string) string {
return fmt.Sprintf("http://localhost:%d/ip/%s", port, containerID)
}
func allocURL(port int, cidr string, containerID string) string {
return fmt.Sprintf("http://localhost:%d/ip/%s/%s", port, containerID, cidr)
}
func TestHttp(t *testing.T) {
var (
containerID = "deadbeef"
container2 = "baddf00d"
container3 = "b01df00d"
universe = "10.0.0.0/8"
testCIDR1 = "10.0.3.8/29"
testCIDR2 = "10.2.0.0/16"
testAddr1 = "10.0.3.9/29"
testAddr2 = "10.2.0.1/16"
)
alloc, _ := makeAllocatorWithMockGossip(t, "08:00:27:01:c3:9a", universe, 1)
_, cidr, _ := address.ParseCIDR(universe)
port := listenHTTP(alloc, cidr)
alloc.claimRingForTesting()
// Allocate an address in each subnet, and check we got what we expected
cidr1a := HTTPPost(t, allocURL(port, testCIDR1, containerID))
require.Equal(t, testAddr1, cidr1a, "address")
cidr2a := HTTPPost(t, allocURL(port, testCIDR2, containerID))
require.Equal(t, testAddr2, cidr2a, "address")
// Now, make the same requests again to check the operation is idempotent
check := HTTPGet(t, allocURL(port, testCIDR1, containerID))
require.Equal(t, cidr1a, check, "address")
check = HTTPGet(t, allocURL(port, testCIDR2, containerID))
require.Equal(t, cidr2a, check, "address")
// Ask the http server for a pair of addresses for another container and check they're different
cidr1b := HTTPPost(t, allocURL(port, testCIDR1, container2))
require.False(t, cidr1b == testAddr1, "address")
cidr2b := HTTPPost(t, allocURL(port, testCIDR2, container2))
require.False(t, cidr2b == testAddr2, "address")
// Now free the first container, and we should get its addresses back when we ask
doHTTP("DELETE", identURL(port, containerID))
cidr1c := HTTPPost(t, allocURL(port, testCIDR1, container3))
require.Equal(t, testAddr1, cidr1c, "address")
cidr2c := HTTPPost(t, allocURL(port, testCIDR2, container3))
require.Equal(t, testAddr2, cidr2c, "address")
// Would like to shut down the http server at the end of this test
// but it's complicated.
// See https://groups.google.com/forum/#!topic/golang-nuts/vLHWa5sHnCE
}
func TestBadHttp(t *testing.T) {
var (
containerID = "deadbeef"
testCIDR1 = "10.0.0.0/8"
)
alloc, _ := makeAllocatorWithMockGossip(t, "08:00:27:01:c3:9a", testCIDR1, 1)
defer alloc.Stop()
_, cidr, _ := address.ParseCIDR(testCIDR1)
port := listenHTTP(alloc, cidr)
alloc.claimRingForTesting()
cidr1 := HTTPPost(t, allocURL(port, testCIDR1, containerID))
parts := strings.Split(cidr1, "/")
testAddr1 := parts[0]
// Verb that's not handled
resp, err := doHTTP("HEAD", fmt.Sprintf("http://localhost:%d/ip/%s/%s", port, containerID, testAddr1))
require.NoError(t, err)
require.Equal(t, http.StatusNotFound, resp.StatusCode, "http response")
// Mis-spelled URL
resp, err = doHTTP("POST", fmt.Sprintf("http://localhost:%d/xip/%s/", port, containerID))
require.NoError(t, err)
require.Equal(t, http.StatusNotFound, resp.StatusCode, "http response")
// Malformed URL
resp, err = doHTTP("POST", fmt.Sprintf("http://localhost:%d/ip/%s/foo/bar/baz", port, containerID))
require.NoError(t, err)
require.Equal(t, http.StatusNotFound, resp.StatusCode, "http response")
}
func TestHTTPCancel(t *testing.T) {
var (
containerID = "deadbeef"
testCIDR1 = "10.0.3.0/29"
)
alloc, _ := makeAllocatorWithMockGossip(t, "08:00:27:01:c3:9a", testCIDR1, 1)
defer alloc.Stop()
alloc.claimRingForTesting()
_, cidr, _ := address.ParseCIDR(testCIDR1)
port := listenHTTP(alloc, cidr)
// Stop the alloc so nothing actually works
unpause := alloc.pause()
// Ask the http server for a new address
done := make(chan *http.Response)
req, _ := http.NewRequest("POST", allocURL(port, testCIDR1, containerID), nil)
go func() {
res, _ := http.DefaultClient.Do(req)
done <- res
}()
time.Sleep(100 * time.Millisecond)
fmt.Println("Cancelling allocate")
http.DefaultTransport.(*http.Transport).CancelRequest(req)
unpause()
res := <-done
if res != nil {
require.FailNow(t, "Error: Allocate returned non-nil")
}
}
+263
View File
@@ -0,0 +1,263 @@
package paxos
import (
"github.com/weaveworks/weave/mesh"
)
// The node identifier. The use of the UID here is important: Paxos
// acceptors must not forget their promises, so it's important that a
// node does not restart and lose its Paxos state but claim to have
// the same ID.
type NodeID struct {
Name mesh.PeerName
UID mesh.PeerUID
}
// note all fields exported in structs so we can Gob them
type ProposalID struct {
// round numbers begin at 1. round 0 indicates an
// uninitialized ProposalID, and precedes all other ProposalIDs
Round uint
Proposer NodeID
}
func (a ProposalID) precedes(b ProposalID) bool {
switch {
case a.Round != b.Round:
return a.Round < b.Round
case a.Proposer.Name != b.Proposer.Name:
return a.Proposer.Name < b.Proposer.Name
case a.Proposer.UID != b.Proposer.UID:
return a.Proposer.UID < b.Proposer.UID
default:
return false
}
}
func (a ProposalID) valid() bool {
return a.Round > 0
}
// For seeding IPAM, the value we want consensus on is a set of peer names
type Value []mesh.PeerName
// An AcceptedValue is a Value plus the proposal which originated that
// Value. The origin is not essential, but makes comparing
// AcceptedValues easy even if comparing Values is not.
type AcceptedValue struct {
Value Value
Origin ProposalID
}
type NodeClaims struct {
// The node promises not to accept a proposal with id less
// than this.
Promise ProposalID
// The accepted proposal, if valid
Accepted ProposalID
AcceptedVal AcceptedValue
}
func (a NodeClaims) equals(b NodeClaims) bool {
return a.Promise == b.Promise && a.Accepted == b.Accepted &&
a.AcceptedVal.Origin == b.AcceptedVal.Origin
}
type GossipState map[NodeID]NodeClaims
type Node struct {
id NodeID
quorum uint
knows GossipState
}
func NewNode(name mesh.PeerName, uid mesh.PeerUID, quorum uint) *Node {
return &Node{
id: NodeID{name, uid},
quorum: quorum,
knows: map[NodeID]NodeClaims{},
}
}
func (node *Node) GossipState() GossipState {
return node.knows
}
// Update this node's information about what other nodes know.
// Returns true if we learned something new.
func (node *Node) Update(from GossipState) bool {
changed := false
for i, fromClaims := range from {
claims, ok := node.knows[i]
if ok {
if claims.Promise.precedes(fromClaims.Promise) {
claims.Promise = fromClaims.Promise
changed = true
}
if claims.Accepted.precedes(fromClaims.Accepted) {
claims.Accepted = fromClaims.Accepted
claims.AcceptedVal = fromClaims.AcceptedVal
changed = true
}
} else {
claims = fromClaims
changed = true
}
node.knows[i] = claims
}
return changed
}
func max(a uint, b uint) uint {
if a > b {
return a
}
return b
}
// Initiate a new proposal, i.e. the Paxos "Prepare" step. This is
// simply a matter of gossipping a new proposal that supersedes all
// others.
func (node *Node) Propose() {
// Find the highest round number around
round := uint(0)
for _, claims := range node.knows {
round = max(round, claims.Promise.Round)
round = max(round, claims.Accepted.Round)
}
ourClaims := node.knows[node.id]
ourClaims.Promise = ProposalID{
Round: round + 1,
Proposer: node.id,
}
node.knows[node.id] = ourClaims
// With a quorum of 1, we can immediately accept our proposal
if node.quorum == 1 {
node.Think()
}
}
// The heart of the consensus algorithm. Return true if we have
// changed our claims.
func (node *Node) Think() bool {
ourClaims := node.knows[node.id]
// The "Promise" step of Paxos: Copy the highest known
// promise.
for _, claims := range node.knows {
if ourClaims.Promise.precedes(claims.Promise) {
ourClaims.Promise = claims.Promise
}
}
// The "Accept Request" step of Paxos: Acting as a proposer,
// do we have a proposal that has been promised by a quorum?
//
// In Paxos, the "proposer" and "acceptor" roles are distinct,
// so in principle a node acting as a proposer could continue
// trying to get its proposal acccepted even after the same
// node as an acceptor has superseded that proposal. But
// that's pointless in a gossip context: If our promise
// supersedes our own proposal, then anyone who hears about
// that promise will not accept that proposal. So our
// proposal is only in the running if it is also our promise.
if ourClaims.Promise.Proposer == node.id {
// Determine whether a quorum has promised, and the
// best previously-accepted value if there is one.
count := uint(0)
var accepted ProposalID
var acceptedVal AcceptedValue
for _, claims := range node.knows {
if claims.Promise == ourClaims.Promise {
count++
if accepted.precedes(claims.Accepted) {
accepted = claims.Accepted
acceptedVal = claims.AcceptedVal
}
}
}
if count >= node.quorum {
if !accepted.valid() {
acceptedVal.Value = node.pickValue()
acceptedVal.Origin = ourClaims.Promise
}
// We automatically accept our own proposal,
// and that's how we communicate the "accept
// request" to other nodes.
ourClaims.Accepted = ourClaims.Promise
ourClaims.AcceptedVal = acceptedVal
}
}
// The "Accepted" step of Paxos: If the proposal we promised
// on got accepted by some other node, we accept it too.
for _, claims := range node.knows {
if claims.Accepted == ourClaims.Promise {
ourClaims.Accepted = claims.Accepted
ourClaims.AcceptedVal = claims.AcceptedVal
break
}
}
if ourClaims.equals(node.knows[node.id]) {
return false
}
node.knows[node.id] = ourClaims
return true
}
// When we get to pick a value, we use the set of peer names we know
// about. This is not necessarily all peer names, but it is at least
// a quorum, and so good enough for seeding the ring.
func (node *Node) pickValue() Value {
val := make([]mesh.PeerName, len(node.knows))
i := 0
for id := range node.knows {
val[i] = id.Name
i++
}
return val
}
// Has a consensus been reached, based on the known claims of other nodes?
func (node *Node) Consensus() (bool, AcceptedValue) {
counts := map[ProposalID]uint{}
for _, claims := range node.knows {
if claims.Accepted.valid() {
origin := claims.AcceptedVal.Origin
count := counts[origin] + 1
counts[origin] = count
if count >= node.quorum {
return true, claims.AcceptedVal
}
}
}
return false, AcceptedValue{}
}
type Status struct {
KnownNodes int
Quorum uint
}
func NewStatus(node *Node) *Status {
if node == nil {
return nil
}
return &Status{len(node.knows), node.quorum}
}
+373
View File
@@ -0,0 +1,373 @@
package paxos
import (
"fmt"
"math/rand"
"testing"
"time"
"github.com/weaveworks/weave/mesh"
)
type TestNode struct {
*Node
// Topology
links []*Link
isolated bool
// The first consensus the Node observed
firstConsensus AcceptedValue
}
type Link struct {
from *TestNode
to *TestNode
converse *Link
// A link is considered ready if it is worthwhile gossipping
// along it (i.e. unless we already gossipped along it and the
// "from" node didn't change since then). If this is <0, then
// the link is not ready. Otherwise, this gives the index in
// the Model's readyLinks slice.
ready int
}
type Model struct {
t *testing.T
r *rand.Rand
quorum uint
nodes []TestNode
readyLinks []*Link
nextID uint
}
func (m *Model) addLink(a, b *TestNode) {
if a == b {
// Never link a node to itself
return
}
ab := Link{from: a, to: b, ready: len(m.readyLinks)}
ba := Link{from: b, to: a, converse: &ab, ready: len(m.readyLinks) + 1}
ab.converse = &ba
a.links = append(a.links, &ab)
b.links = append(b.links, &ba)
m.readyLinks = append(m.readyLinks, &ab, &ba)
}
func (m *Model) linkExists(a, b *TestNode) bool {
for _, l := range a.links {
if l.to == b {
return true
}
}
return false
}
type TestParams struct {
// Number of nodes
nodeCount uint
// Probability that two nodes are connected.
connectedProb float32
// Probability that some node will re-propose at each
// step. Setting this too high makes it likely that we'll fail
// to converge.
reproposeProb float32
// Probability that some node will be isolated at each step.
isolateProb float32
// Probability that some node willbe restarted at each step.
restartProb float32
}
// Make a network of nodes with random topology
func makeRandomModel(params *TestParams, r *rand.Rand, t *testing.T) *Model {
m := Model{
t: t,
r: r,
quorum: params.nodeCount/2 + 1,
nodes: make([]TestNode, params.nodeCount),
readyLinks: []*Link{},
nextID: params.nodeCount + 1,
}
for i := range m.nodes {
m.nodes[i].Node = NewNode(mesh.PeerName(i/2+1),
mesh.PeerUID(r.Int63()), m.quorum)
m.nodes[i].Propose()
}
for i := 1; i < len(m.nodes); i++ {
// was node i connected to the other nodes yet?
connected := false
for j := 0; j < i; j++ {
if r.Float32() < params.connectedProb {
connected = true
m.addLink(&m.nodes[i], &m.nodes[j])
}
}
if !connected {
// node i must be connected into the graph
// somewhere. So if we didn't connect it
// already, this is a last resort.
m.addLink(&m.nodes[i], &m.nodes[r.Intn(i)])
}
}
return &m
}
// Mark a link as unready
func (m *Model) unreadyLink(link *Link) {
i := link.ready
if i >= 0 {
m.readyLinks[i] = m.readyLinks[len(m.readyLinks)-1]
m.readyLinks[i].ready = i
m.readyLinks = m.readyLinks[:len(m.readyLinks)-1]
link.ready = -1
}
}
// Mark a link as ready
func (m *Model) readyLink(link *Link) {
if link.ready < 0 {
link.ready = len(m.readyLinks)
m.readyLinks = append(m.readyLinks, link)
}
}
// Mark all the outgoing links from a node as ready
func (m *Model) nodeChanged(node *TestNode) {
for _, l := range node.links {
m.readyLink(l)
}
}
// Isolate a node
func (m *Model) isolateNode(node *TestNode) {
node.isolated = true
for _, l := range node.links {
if l.ready >= 0 {
m.unreadyLink(l)
m.unreadyLink(l.converse)
}
}
// Isolating a node could partition the network. We don't
// want to test such a case (because it could prevent
// consensus). Checking for partitions would be more code
// than its worth, so just add some links to prevent the
// possibility of partitions.
for i := 1; i < len(node.links); i++ {
m.addLink(node.links[i-1].to, node.links[i].to)
}
}
// Restart a node
func (m *Model) restart(node *TestNode) {
node.Node = NewNode(mesh.PeerName(m.nextID),
mesh.PeerUID(m.r.Int63()), m.quorum)
m.nextID++
node.Propose()
// The node is now ignorant, so we need to mark the links into
// the node as ready.
for _, l := range node.links {
if !l.to.isolated {
m.readyLink(l.converse)
}
}
// If a consensus was just accepted due to this node accepting
// it, without other nodes hearing of it, and we then restart
// this node, then a different consensus can occur later on.
// If a tree falls with no one to hear it, does it make a
// sound?
node.firstConsensus = AcceptedValue{}
}
func (m *Model) pickNode() *TestNode {
for {
node := &m.nodes[m.r.Intn(len(m.nodes))]
if !node.isolated {
return node
}
}
}
func (m *Model) simulate(params *TestParams) bool {
nodesLeft := uint(len(m.nodes))
restarts := uint(0)
for step := 0; step < 1000000; step++ {
if len(m.readyLinks) == 0 {
// Everything has converged. This can be
// because consensus was reached, or because
// consensus became impossible, e.g. everyone
// promised on a particular proposal, but then
// the proposer restarted or was isolated. So
// we detect the latter cases and force a new
// proposal
for i := range m.nodes {
ok, _ := m.nodes[i].Consensus()
if ok {
return true
}
}
node := m.pickNode()
node.Propose()
m.nodeChanged(node)
}
// Pick a ready link at random
i := m.r.Intn(len(m.readyLinks))
link := m.readyLinks[i]
if link.ready != i {
m.t.Fatal("Link in readyLinks was not ready")
}
// gossip across link
node := link.to
if node.Update(link.from.GossipState()) {
node.Think()
if !node.firstConsensus.Origin.valid() {
ok, val := node.Consensus()
if ok {
node.firstConsensus = val
}
}
m.nodeChanged(node)
}
m.unreadyLink(link)
// Re-propose?
if m.r.Float32() < params.reproposeProb {
node := m.pickNode()
node.Propose()
m.nodeChanged(node)
}
// Isolate?
if nodesLeft > m.quorum && m.r.Float32() < params.isolateProb {
m.isolateNode(m.pickNode())
nodesLeft--
// We isolated a node, so get another node to
// re-propose. In reality the lack of
// consensus would be detected via a timeout
node := m.pickNode()
node.Propose()
m.nodeChanged(node)
}
// Restart?
if restarts < m.quorum && m.r.Float32() < params.restartProb {
restarts++
node := m.pickNode()
m.restart(node)
m.nodeChanged(node)
}
}
return false
}
func (m *Model) dump() {
for i := range m.nodes {
node := &m.nodes[i]
fmt.Println(node.id)
for n, claims := range node.knows {
fmt.Printf("\t%d %v\n", n, claims)
}
}
}
// Validate the final model state
func (m *Model) validate() {
var origin ProposalID
for i := range m.nodes {
ok, val := m.nodes[i].Consensus()
if !ok {
//m.dump()
m.t.Fatal("Node doesn't know about consensus")
}
firstConsensus := m.nodes[i].firstConsensus
if firstConsensus.Origin.valid() &&
firstConsensus.Origin != val.Origin {
//m.dump()
m.t.Fatal("Consensus mismatch")
}
if i == 0 {
origin = val.Origin
} else if val.Origin != origin {
m.t.Fatal("Node disagrees about consensus")
}
}
}
func TestSingleNode(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
// Test the single node case
params := &TestParams{
nodeCount: 1,
connectedProb: 0,
reproposeProb: 0,
isolateProb: 0,
restartProb: 0,
}
m := makeRandomModel(params, r, t)
if !m.simulate(params) {
m.t.Fatal("Failed to converge")
}
m.validate()
}
func TestPaxos(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
params := &TestParams{
nodeCount: 10,
connectedProb: 0.5,
reproposeProb: 0.01,
isolateProb: 0.01,
// Restarts cause failures due to the case where 1)
// the gossip ordering is such that there is an
// effective partition from the start, with a quorum
// on one side and one node less than a quorum on the
// other; 2) the quorum reaches a consensus; 3) a node
// is restarted, and at the same time jumps the
// partition, so the side that was now just less than
// a quorum becomes a quorum; 4) the new quorum
// reaches a contradictory consensus.
restartProb: 0,
}
for i := 0; i < 1000; i++ {
m := makeRandomModel(params, r, t)
if !m.simulate(params) {
m.t.Fatal("Failed to converge")
}
m.validate()
}
}
+98
View File
@@ -0,0 +1,98 @@
package ring
import (
"sort"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
)
// Entry represents entries around the ring
type entry struct {
Token address.Address // The start of this range
Peer mesh.PeerName // Who owns this range
Version uint32 // Version of this range
Free address.Offset // Number of free IPs in this range
}
func (e *entry) Equal(e2 *entry) bool {
return e.Token == e2.Token && e.Peer == e2.Peer &&
e.Version == e2.Version
}
func (e *entry) update(peername mesh.PeerName, free address.Offset) {
e.Peer = peername
e.Version++
e.Free = free
}
// For compatibility with sort.Interface
type entries []*entry
func (es entries) Len() int { return len(es) }
func (es entries) Less(i, j int) bool { return es[i].Token < es[j].Token }
func (es entries) Swap(i, j int) { panic("Should never be swapping entries!") }
func (es entries) entry(i int) *entry {
i = i % len(es)
if i < 0 {
i += len(es)
}
return es[i]
}
func (es *entries) insert(e entry) {
i := sort.Search(len(*es), func(j int) bool {
return (*es)[j].Token >= e.Token
})
if i < len(*es) && (*es)[i].Token == e.Token {
panic("Trying to insert an existing token!")
}
*es = append(*es, &entry{})
copy((*es)[i+1:], (*es)[i:])
(*es)[i] = &e
}
func (es entries) get(token address.Address) (*entry, bool) {
i := sort.Search(len(es), func(j int) bool {
return es[j].Token >= token
})
if i < len(es) && es[i].Token == token {
return es[i], true
}
return nil, false
}
// Is token between entries at i and j?
// NB i and j can overflow and will wrap
// NBB this function does not work very well if there is only one
// token on the ring; luckily an accurate answer is not needed
// by the call sites in this case.
func (es entries) between(token address.Address, i, j int) bool {
common.Assert(i < j)
first := es.entry(i)
second := es.entry(j)
switch {
case first.Token == second.Token:
// This implies there is only one token
// on the ring (i < j and i.token == j.token)
// In which case everything is between, expect
// this one token
return token != first.Token
case first.Token < second.Token:
return first.Token <= token && token < second.Token
case second.Token < first.Token:
return first.Token <= token || token < second.Token
}
panic("Should never get here - switch covers all possibilities.")
}
+31
View File
@@ -0,0 +1,31 @@
package ring
/* Exported variables for monitoring and management */
import (
"expvar"
"strconv"
)
var (
expRingSize = expvar.NewMap("ipam.ringSize")
expRingEntries = expvar.NewMap("ipam.ringEntries")
)
type _int int
func (i _int) String() string {
return strconv.Itoa(int(i))
}
type _uint32 uint32
func (i _uint32) String() string {
return strconv.FormatUint(uint64(i), 10)
}
func (r *Ring) updateExportedVariables() {
ringName := r.Start.String()
expRingSize.Set(ringName, _uint32(r.End-r.Start))
expRingEntries.Set(ringName, _int(len(r.Entries)))
}
+564
View File
@@ -0,0 +1,564 @@
/*
Package ring implements a simple ring CRDT.
*/
package ring
import (
"bytes"
"errors"
"fmt"
"io"
"math/rand"
"sort"
"time"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
)
// Ring represents the ring itself
type Ring struct {
Start, End address.Address // [min, max) tokens in this ring. Due to wrapping, min == max (effectively)
Peer mesh.PeerName // name of peer owning this ring instance
Entries entries // list of entries sorted by token
Seeds []mesh.PeerName // peers with which the ring was seeded
}
func (r *Ring) assertInvariants() {
err := r.checkInvariants()
if err != nil {
panic(err.Error())
}
}
// Errors returned by Merge
var (
ErrNotSorted = errors.New("Ring not sorted")
ErrTokenRepeated = errors.New("Token appears twice in ring")
ErrTokenOutOfRange = errors.New("Token is out of range")
ErrDifferentSeeds = errors.New("Received ring was seeded differently from ours!")
ErrDifferentRange = errors.New("Received range differs from ours!")
ErrNewerVersion = errors.New("Received new version for entry I own!")
ErrInvalidEntry = errors.New("Received invalid state update!")
ErrEntryInMyRange = errors.New("Received new entry in my range!")
ErrNotFound = errors.New("No entries for peer found")
)
func (r *Ring) checkInvariants() error {
if !sort.IsSorted(r.Entries) {
return ErrNotSorted
}
// Check no token appears twice
// We know it's sorted...
for i := 1; i < len(r.Entries); i++ {
if r.Entries[i-1].Token == r.Entries[i].Token {
return ErrTokenRepeated
}
}
if len(r.Entries) == 0 {
return nil
}
// Check tokens are in range
if r.Entries.entry(0).Token < r.Start {
return ErrTokenOutOfRange
}
if r.Entries.entry(-1).Token >= r.End {
return ErrTokenOutOfRange
}
// Check all the freespaces are in range
for i, entry := range r.Entries {
next := r.Entries.entry(i + 1)
distance := r.distance(entry.Token, next.Token)
if entry.Free > distance {
return fmt.Errorf("Entry %s-%s reporting too much free space: %d > %d", entry.Token, next.Token, entry.Free, distance)
}
}
return nil
}
// New creates an empty ring belonging to peer.
func New(start, end address.Address, peer mesh.PeerName) *Ring {
common.Assert(start < end)
ring := &Ring{Start: start, End: end, Peer: peer, Entries: make([]*entry, 0)}
ring.updateExportedVariables()
return ring
}
func (r *Ring) Range() address.Range {
return address.Range{Start: r.Start, End: r.End}
}
// Returns the distance between two tokens on this ring, dealing
// with ranges which cross the origin
func (r *Ring) distance(start, end address.Address) address.Offset {
if end > start {
return address.Offset(end - start)
}
return address.Offset((r.End - start) + (end - r.Start))
}
// GrantRangeToHost modifies the ring such that range [start, end)
// is assigned to peer. This may insert up to two new tokens.
// Preconditions:
// - start < end
// - [start, end) must be owned by the calling peer
func (r *Ring) GrantRangeToHost(start, end address.Address, peer mesh.PeerName) {
//fmt.Printf("%s GrantRangeToHost [%v,%v) -> %s\n", r.Peer, start, end, peer)
r.assertInvariants()
defer r.assertInvariants()
defer r.updateExportedVariables()
// ----------------- Start of Checks -----------------
common.Assert(start < end)
common.Assert(r.Start <= start && start < r.End)
common.Assert(r.Start < end && end <= r.End)
common.Assert(len(r.Entries) > 0)
// Look for the left-most entry greater than start, then go one previous
// to get the right-most entry less than or equal to start
preceedingPos := sort.Search(len(r.Entries), func(j int) bool {
return r.Entries[j].Token > start
})
preceedingPos--
// Check all tokens up to end are owned by us
for pos := preceedingPos; pos < len(r.Entries) && r.Entries.entry(pos).Token < end; pos++ {
common.Assert(r.Entries.entry(pos).Peer == r.Peer)
}
// ----------------- End of Checks -----------------
// Free space at start is max(length of range, distance to next token)
startFree := r.distance(start, r.Entries.entry(preceedingPos+1).Token)
if length := r.distance(start, end); startFree > length {
startFree = length
}
// Is there already a token at start, update it
if previousEntry := r.Entries.entry(preceedingPos); previousEntry.Token == start {
previousEntry.update(peer, startFree)
} else {
// Otherwise, these isn't a token here, insert a new one.
r.Entries.insert(entry{Token: start, Peer: peer, Free: startFree})
preceedingPos++
// Reset free space on previous entry, which we own.
previousEntry.update(r.Peer, r.distance(previousEntry.Token, start))
}
// Give all intervening tokens to the other peer
pos := preceedingPos + 1
for ; pos < len(r.Entries) && r.Entries.entry(pos).Token < end; pos++ {
entry := r.Entries.entry(pos)
entry.update(peer, address.Min(entry.Free, r.distance(entry.Token, end)))
}
// There is never an entry with a token of r.End, as the end of
// the ring is exclusive.
if end == r.End {
end = r.Start
}
// If there is a token equal to the end of the range, we don't need to do anything further
if _, found := r.Entries.get(end); found {
return
}
// If not, we need to insert a token such that we claim this bit on the end.
endFree := r.distance(end, r.Entries.entry(pos).Token)
r.Entries.insert(entry{Token: end, Peer: r.Peer, Free: endFree})
}
// Merge the given ring into this ring and return any new ranges added
func (r *Ring) Merge(gossip Ring) error {
r.assertInvariants()
defer r.assertInvariants()
defer r.updateExportedVariables()
// Don't panic when checking the gossiped in ring.
// In this case just return any error found.
if err := gossip.checkInvariants(); err != nil {
return err
}
if len(gossip.Seeds) > 0 && len(r.Seeds) > 0 {
if len(gossip.Seeds) != len(r.Seeds) {
return ErrDifferentSeeds
}
for i, seed := range gossip.Seeds {
if seed != r.Seeds[i] {
return ErrDifferentSeeds
}
}
}
if r.Start != gossip.Start || r.End != gossip.End {
return ErrDifferentRange
}
// Now merge their ring with yours, in a temporary ring.
var result entries
addToResult := func(e entry) { result = append(result, &e) }
var mine, theirs *entry
var previousOwner *mesh.PeerName
// i is index into r.Entries; j is index into gossip.Entries
var i, j int
for i < len(r.Entries) && j < len(gossip.Entries) {
mine, theirs = r.Entries[i], gossip.Entries[j]
switch {
case mine.Token < theirs.Token:
addToResult(*mine)
previousOwner = &mine.Peer
i++
case mine.Token > theirs.Token:
// insert, checking that a range owned by us hasn't been split
if previousOwner != nil && *previousOwner == r.Peer && theirs.Peer != r.Peer {
return ErrEntryInMyRange
}
addToResult(*theirs)
previousOwner = nil
j++
case mine.Token == theirs.Token:
// merge
switch {
case mine.Version >= theirs.Version:
if mine.Version == theirs.Version && !mine.Equal(theirs) {
common.Log.Debugf("Error merging entries at %s - %v != %v", mine.Token, mine, theirs)
return ErrInvalidEntry
}
addToResult(*mine)
previousOwner = &mine.Peer
case mine.Version < theirs.Version:
if mine.Peer == r.Peer { // We shouldn't receive updates to our own tokens
return ErrNewerVersion
}
addToResult(*theirs)
previousOwner = nil
}
i++
j++
}
}
// At this point, either i is at the end of r or j is at the end
// of gossip, so copy over the remaining entries.
for ; i < len(r.Entries); i++ {
mine = r.Entries[i]
addToResult(*mine)
}
for ; j < len(gossip.Entries); j++ {
theirs = gossip.Entries[j]
if previousOwner != nil && *previousOwner == r.Peer && theirs.Peer != r.Peer {
return ErrEntryInMyRange
}
addToResult(*theirs)
previousOwner = nil
}
if len(r.Seeds) == 0 {
r.Seeds = gossip.Seeds
}
r.Entries = result
return nil
}
// Empty returns true if the ring has no entries
func (r *Ring) Empty() bool {
return len(r.Entries) == 0
}
// Given a slice of ranges which are all in the right order except
// possibly the last one spans zero, fix that up and return the slice
func (r *Ring) splitRangesOverZero(ranges []address.Range) []address.Range {
if len(ranges) == 0 {
return ranges
}
lastRange := ranges[len(ranges)-1]
// if end token == start (ie last) entry on ring, we want to actually use r.End
if lastRange.End == r.Start {
ranges[len(ranges)-1].End = r.End
} else if lastRange.End <= lastRange.Start {
// We wrapped; want to split around 0
// First shuffle everything up as we want results to be sorted
ranges = append(ranges, address.Range{})
copy(ranges[1:], ranges[:len(ranges)-1])
ranges[0] = address.Range{Start: r.Start, End: lastRange.End}
ranges[len(ranges)-1].End = r.End
}
return ranges
}
// OwnedRanges returns slice of Ranges, ordered by IP, indicating which
// ranges are owned by this peer. Will split ranges which
// span 0 in the ring.
func (r *Ring) OwnedRanges() (result []address.Range) {
r.assertInvariants()
for i, entry := range r.Entries {
if entry.Peer == r.Peer {
nextEntry := r.Entries.entry(i + 1)
result = append(result, address.Range{Start: entry.Token, End: nextEntry.Token})
}
}
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) {
common.Assert(r.Empty())
defer r.assertInvariants()
defer r.updateExportedVariables()
totalSize := r.distance(r.Start, r.End)
share := totalSize/address.Offset(len(peers)) + 1
remainder := totalSize % address.Offset(len(peers))
pos := r.Start
for i, peer := range peers {
if address.Offset(i) == remainder {
share--
if share == 0 {
break
}
}
if e, found := r.Entries.get(pos); found {
e.update(peer, share)
} else {
r.Entries.insert(entry{Token: pos, Peer: peer, Free: share})
}
pos += address.Address(share)
}
common.Assert(pos == r.End)
r.Seeds = peers
}
func (r *Ring) FprintWithNicknames(w io.Writer, m map[mesh.PeerName]string) {
for _, entry := range r.Entries {
nickname, found := m[entry.Peer]
if found {
nickname = fmt.Sprintf(" (%s)", nickname)
}
fmt.Fprintf(w, "\n %s -> %s%s (v%d)", entry.Token,
entry.Peer, nickname, entry.Version)
}
}
func (r *Ring) String() string {
var buffer bytes.Buffer
fmt.Fprintf(&buffer, "Ring [%s, %s)", r.Start, r.End)
r.FprintWithNicknames(&buffer, make(map[mesh.PeerName]string))
return buffer.String()
}
// ReportFree is used by the allocator to tell the ring how many free
// ips are in a given range, so that ChoosePeersToAskForSpace can make
// more intelligent decisions.
func (r *Ring) ReportFree(freespace map[address.Address]address.Offset) {
r.assertInvariants()
defer r.assertInvariants()
defer r.updateExportedVariables()
common.Assert(!r.Empty())
entries := r.Entries
// As OwnedRanges splits around the origin, we need to
// detect that here and fix up freespace
if free, found := freespace[r.Start]; found && entries.entry(0).Token != r.Start {
lastToken := entries.entry(-1).Token
prevFree, found := freespace[lastToken]
common.Assert(found)
freespace[lastToken] = prevFree + free
delete(freespace, r.Start)
}
for start, free := range freespace {
// Look for entry
i := sort.Search(len(entries), func(j int) bool {
return entries[j].Token >= start
})
// Are you trying to report free on space I don't own?
common.Assert(i < len(entries) && entries[i].Token == start && entries[i].Peer == r.Peer)
// Check we're not reporting more space than the range
entry, next := entries.entry(i), entries.entry(i+1)
maxSize := r.distance(entry.Token, next.Token)
common.Assert(free <= maxSize)
if entries[i].Free == free {
return
}
entries[i].Free = free
entries[i].Version++
}
}
type weightedPeer struct {
weight float64
peername mesh.PeerName
}
type weightedPeers []weightedPeer
// Note Less is using > so that bigger weights sort earlier
func (ws weightedPeers) Less(i, j int) bool { return ws[i].weight > ws[j].weight }
func (ws weightedPeers) Len() int { return len(ws) }
func (ws weightedPeers) Swap(i, j int) { ws[i], ws[j] = ws[j], ws[i] }
// ChoosePeersToAskForSpace returns all peers we can ask for space in
// the range [start, end), in weighted-random order. Assumes start<end.
func (r *Ring) ChoosePeersToAskForSpace(start, end address.Address) []mesh.PeerName {
var (
sum address.Offset
totalSpacePerPeer = make(map[mesh.PeerName]address.Offset) // Compute total free space per peer
)
// iterate through tokens
for i, entry := range r.Entries {
// Ignore entries that don't span the range we want
if i+1 < len(r.Entries) && r.Entries.entry(i+1).Token-1 < start {
continue
}
if entry.Token >= end {
break
}
// Ignore ranges with no free space
if entry.Free <= 0 {
continue
}
// Don't talk to yourself
if entry.Peer == r.Peer {
continue
}
totalSpacePerPeer[entry.Peer] += entry.Free
sum += entry.Free
}
// Compute weighted random numbers, then sort.
// This isn't perfect, e.g. an item with weight 2 will get chosen more than
// twice as often as an item with weight 1, but it's good enough for our purposes.
ws := make(weightedPeers, 0, len(totalSpacePerPeer))
for peername, space := range totalSpacePerPeer {
ws = append(ws, weightedPeer{weight: float64(space) * rand.Float64(), peername: peername})
}
sort.Sort(ws)
result := make([]mesh.PeerName, len(ws))
for i, wp := range ws {
result[i] = wp.peername
}
return result
}
func (r *Ring) PickPeerForTransfer(isValid func(mesh.PeerName) bool) mesh.PeerName {
for _, entry := range r.Entries {
if entry.Peer != r.Peer && isValid(entry.Peer) {
return entry.Peer
}
}
return mesh.UnknownPeerName
}
// Transfer will mark all entries associated with 'from' peer as owned by 'to' peer
// and return ranges indicating the new space we picked up
func (r *Ring) Transfer(from, to mesh.PeerName) ([]address.Range, error) {
r.assertInvariants()
defer r.assertInvariants()
defer r.updateExportedVariables()
var newRanges []address.Range
found := false
for i, entry := range r.Entries {
if entry.Peer == from {
found = true
entry.Peer = to
entry.Version++
newRanges = append(newRanges, address.Range{Start: entry.Token, End: r.Entries.entry(i + 1).Token})
}
}
if !found {
return nil, ErrNotFound
}
return r.splitRangesOverZero(newRanges), nil
}
// Contains returns true if addr is in this ring
func (r *Ring) Contains(addr address.Address) bool {
return addr >= r.Start && addr < r.End
}
// Owner returns the peername which owns the range containing addr
func (r *Ring) Owner(token address.Address) mesh.PeerName {
common.Assert(r.Start <= token && token < r.End)
r.assertInvariants()
// There can be no owners on an empty ring
if r.Empty() {
return mesh.UnknownPeerName
}
// Look for the right-most entry, less than or equal to token
preceedingEntry := sort.Search(len(r.Entries), func(j int) bool {
return r.Entries[j].Token > token
})
preceedingEntry--
entry := r.Entries.entry(preceedingEntry)
return entry.Peer
}
// Get the set of PeerNames mentioned in the ring
func (r *Ring) PeerNames() map[mesh.PeerName]struct{} {
res := make(map[mesh.PeerName]struct{})
for _, entry := range r.Entries {
res[entry.Peer] = struct{}{}
}
return res
}
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
+746
View File
@@ -0,0 +1,746 @@
package ring
import (
"bytes"
"fmt"
"math/rand"
"sort"
"testing"
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
)
var (
peer1name, _ = mesh.PeerNameFromString("01:00:00:00:02:00")
peer2name, _ = mesh.PeerNameFromString("02:00:00:00:02:00")
peer3name, _ = mesh.PeerNameFromString("03:00:00:00:02:00")
start, end = ParseIP("10.0.0.0"), ParseIP("10.0.0.255")
dot10, dot245 = ParseIP("10.0.0.10"), ParseIP("10.0.0.245")
dot250 = ParseIP("10.0.0.250")
middle = ParseIP("10.0.0.128")
)
func ParseIP(s string) address.Address {
addr, _ := address.ParseIP(s)
return addr
}
func TestInvariants(t *testing.T) {
ring := New(start, end, peer1name)
// Check ring is sorted
ring.Entries = []*entry{{Token: dot245, Peer: peer1name}, {Token: dot10, Peer: peer2name}}
require.True(t, ring.checkInvariants() == ErrNotSorted, "Expected error")
// Check tokens don't appear twice
ring.Entries = []*entry{{Token: dot245, Peer: peer1name}, {Token: dot245, Peer: peer2name}}
require.True(t, ring.checkInvariants() == ErrTokenRepeated, "Expected error")
// Check tokens are in bounds
ring = New(dot10, dot245, peer1name)
ring.Entries = []*entry{{Token: start, Peer: peer1name}}
require.True(t, ring.checkInvariants() == ErrTokenOutOfRange, "Expected error")
ring.Entries = []*entry{{Token: end, Peer: peer1name}}
require.True(t, ring.checkInvariants() == ErrTokenOutOfRange, "Expected error")
}
func TestInsert(t *testing.T) {
ring := New(start, end, peer1name)
ring.Entries = []*entry{{Token: start, Peer: peer1name, Free: 255}}
require.Panics(t, func() {
ring.Entries.insert(entry{Token: start, Peer: peer1name})
})
ring.Entries.entry(0).Free = 0
ring.Entries.insert(entry{Token: dot245, Peer: peer1name})
ring2 := New(start, end, peer1name)
ring2.Entries = []*entry{{Token: start, Peer: peer1name, Free: 0}, {Token: dot245, Peer: peer1name}}
require.Equal(t, ring2, ring)
ring.Entries.insert(entry{Token: dot10, Peer: peer1name})
ring2.Entries = []*entry{{Token: start, Peer: peer1name, Free: 0}, {Token: dot10, Peer: peer1name}, {Token: dot245, Peer: peer1name}}
require.Equal(t, ring2, ring)
}
func TestBetween(t *testing.T) {
ring1 := New(start, end, peer1name)
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 255}}
// First off, in a ring where everything is owned by the peer
// between should return true for everything
for i := 1; i <= 255; i++ {
ip := ParseIP(fmt.Sprintf("10.0.0.%d", i))
require.True(t, ring1.Entries.between(ip, 0, 1), "between should be true!")
}
// Now, construct a ring with entries at +10 and -10
// And check the correct behaviour
ring1.Entries = []*entry{{Token: dot10, Peer: peer1name}, {Token: dot245, Peer: peer2name}}
ring1.assertInvariants()
for i := 10; i <= 244; i++ {
ipStr := fmt.Sprintf("10.0.0.%d", i)
ip := ParseIP(ipStr)
require.True(t, ring1.Entries.between(ip, 0, 1),
fmt.Sprintf("Between should be true for %s!", ipStr))
require.False(t, ring1.Entries.between(ip, 1, 2),
fmt.Sprintf("Between should be false for %s!", ipStr))
}
for i := 0; i <= 9; i++ {
ipStr := fmt.Sprintf("10.0.0.%d", i)
ip := ParseIP(ipStr)
require.False(t, ring1.Entries.between(ip, 0, 1),
fmt.Sprintf("Between should be false for %s!", ipStr))
require.True(t, ring1.Entries.between(ip, 1, 2),
fmt.Sprintf("Between should be true for %s!", ipStr))
}
for i := 245; i <= 255; i++ {
ipStr := fmt.Sprintf("10.0.0.%d", i)
ip := ParseIP(ipStr)
require.False(t, ring1.Entries.between(ip, 0, 1),
fmt.Sprintf("Between should be false for %s!", ipStr))
require.True(t, ring1.Entries.between(ip, 1, 2),
fmt.Sprintf("Between should be true for %s!", ipStr))
}
}
func TestGrantSimple(t *testing.T) {
ring1 := New(start, end, peer1name)
ring2 := New(start, end, peer2name)
// Claim everything for peer1
ring1.ClaimItAll()
require.Equal(t, entries{{Token: start, Peer: peer1name, Free: 255}}, ring1.Entries)
// Now grant everything to peer2
ring1.GrantRangeToHost(start, end, peer2name)
ring2.Entries = []*entry{{Token: start, Peer: peer2name, Free: 255, Version: 1}}
require.Equal(t, ring2.Entries, ring1.Entries)
// Now spint back to peer 1
ring2.GrantRangeToHost(dot10, end, peer1name)
ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 10, Version: 2},
{Token: dot10, Peer: peer1name, Free: 245}}
require.Equal(t, ring2.Entries, ring1.Entries)
// And spint back to peer 2 again
ring1.GrantRangeToHost(dot245, end, peer2name)
require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 10, Version: 2},
{Token: dot10, Peer: peer1name, Free: 235, Version: 1},
{Token: dot245, Peer: peer2name, Free: 10}}, ring1.Entries)
// Grant range spanning a live token
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 10, Version: 2},
{Token: dot10, Peer: peer1name, Free: 235}, {Token: dot245, Peer: peer1name, Free: 10}}
ring1.GrantRangeToHost(dot10, end, peer2name)
require.Equal(t, entries{{Token: start, Peer: peer1name, Free: 10, Version: 2},
{Token: dot10, Peer: peer2name, Free: 235, Version: 1},
{Token: dot245, Peer: peer2name, Free: 10, Version: 1}}, ring1.Entries)
}
func TestGrantSplit(t *testing.T) {
ring1 := New(start, end, peer1name)
ring2 := New(start, end, peer2name)
// Claim everything for peer1
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 255}}
ring2.Merge(*ring1)
require.Equal(t, ring2.Entries, ring1.Entries)
// Now grant a split range to peer2
ring1.GrantRangeToHost(dot10, dot245, peer2name)
require.Equal(t, entries{{Token: start, Peer: peer1name, Free: 10, Version: 1},
{Token: dot10, Peer: peer2name, Free: 235},
{Token: dot245, Peer: peer1name, Free: 10}}, ring1.Entries)
ring1.assertInvariants()
// Grant range spanning a live token, and inserting a new token
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 10, Version: 2},
{Token: dot10, Peer: peer1name, Free: 118}, {Token: middle, Peer: peer1name, Free: 127}}
ring1.GrantRangeToHost(dot10, dot245, peer2name)
require.Equal(t, entries{{Token: start, Peer: peer1name, Free: 10, Version: 2},
{Token: dot10, Peer: peer2name, Free: 118, Version: 1},
{Token: middle, Peer: peer2name, Free: 117, Version: 1},
{Token: dot245, Peer: peer1name, Free: 10, Version: 0}}, ring1.Entries)
ring1.assertInvariants()
}
func TestMergeSimple(t *testing.T) {
ring1 := New(start, end, peer1name)
ring2 := New(start, end, peer2name)
// Claim everything for peer1
ring1.ClaimItAll()
ring1.GrantRangeToHost(middle, end, peer2name)
require.NoError(t, ring2.Merge(*ring1))
require.Equal(t, entries{{Token: start, Peer: peer1name, Free: 128, Version: 1},
{Token: middle, Peer: peer2name, Free: 127}}, ring1.Entries)
require.Equal(t, ring2.Entries, ring1.Entries)
// Now to two different operations on either side,
// check we can Merge again
ring1.GrantRangeToHost(start, middle, peer2name)
ring2.GrantRangeToHost(middle, end, peer1name)
require.NoError(t, ring2.Merge(*ring1))
require.NoError(t, ring1.Merge(*ring2))
require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 128, Version: 2},
{Token: middle, Peer: peer1name, Free: 127, Version: 1}}, ring1.Entries)
require.Equal(t, ring2.Entries, ring1.Entries)
}
func TestMergeErrors(t *testing.T) {
// Cannot Merge in an invalid ring
ring1 := New(start, end, peer1name)
ring2 := New(start, end, peer2name)
ring2.Entries = []*entry{{Token: middle, Peer: peer2name}, {Token: start, Peer: peer2name}}
require.True(t, ring1.Merge(*ring2) == ErrNotSorted, "Expected ErrNotSorted")
// Should Merge two rings for different ranges
ring2 = New(start, middle, peer2name)
ring2.Entries = []*entry{}
require.True(t, ring1.Merge(*ring2) == ErrDifferentRange, "Expected ErrDifferentRange")
// Cannot Merge newer version of entry I own
ring2 = New(start, end, peer2name)
ring1.Entries = []*entry{{Token: start, Peer: peer1name}}
ring2.Entries = []*entry{{Token: start, Peer: peer1name, Version: 1}}
require.True(t, ring1.Merge(*ring2) == ErrNewerVersion, "Expected ErrNewerVersion")
// Cannot Merge two entries with same version but different hosts
ring1.Entries = []*entry{{Token: start, Peer: peer1name}}
ring2.Entries = []*entry{{Token: start, Peer: peer2name}}
require.True(t, ring1.Merge(*ring2) == ErrInvalidEntry, "Expected ErrInvalidEntry")
// Cannot Merge an entry into a range I own
ring1.Entries = []*entry{{Token: start, Peer: peer1name}}
ring2.Entries = []*entry{{Token: middle, Peer: peer2name}}
require.True(t, ring1.Merge(*ring2) == ErrEntryInMyRange, "Expected ErrEntryInMyRange")
}
func TestMergeMore(t *testing.T) {
ring1 := New(start, end, peer1name)
ring2 := New(start, end, peer2name)
assertRing := func(ring *Ring, entries entries) {
require.Equal(t, entries, ring.Entries)
}
assertRing(ring1, []*entry{})
assertRing(ring2, []*entry{})
// Claim everything for peer1
ring1.ClaimItAll()
assertRing(ring1, []*entry{{Token: start, Peer: peer1name, Free: 255}})
assertRing(ring2, []*entry{})
// Check the Merge sends it to the other ring
require.NoError(t, ring2.Merge(*ring1))
assertRing(ring1, []*entry{{Token: start, Peer: peer1name, Free: 255}})
assertRing(ring2, []*entry{{Token: start, Peer: peer1name, Free: 255}})
// Give everything to peer2
ring1.GrantRangeToHost(start, end, peer2name)
assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 255, Version: 1}})
assertRing(ring2, []*entry{{Token: start, Peer: peer1name, Free: 255}})
require.NoError(t, ring2.Merge(*ring1))
assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 255, Version: 1}})
assertRing(ring2, []*entry{{Token: start, Peer: peer2name, Free: 255, Version: 1}})
// And carve off some space
ring2.GrantRangeToHost(middle, end, peer1name)
assertRing(ring2, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2},
{Token: middle, Peer: peer1name, Free: 127}})
assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 255, Version: 1}})
// And Merge back
require.NoError(t, ring1.Merge(*ring2))
assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2},
{Token: middle, Peer: peer1name, Free: 127}})
assertRing(ring2, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2},
{Token: middle, Peer: peer1name, Free: 127}})
// This should be a no-op
require.NoError(t, ring2.Merge(*ring1))
assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2},
{Token: middle, Peer: peer1name, Free: 127}})
assertRing(ring2, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2},
{Token: middle, Peer: peer1name, Free: 127}})
}
func TestMergeSplit(t *testing.T) {
ring1 := New(start, end, peer1name)
ring2 := New(start, end, peer2name)
// Claim everything for peer2
ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 255}}
require.NoError(t, ring2.Merge(*ring1))
require.Equal(t, ring2.Entries, ring1.Entries)
// Now grant a split range to peer1
ring2.GrantRangeToHost(dot10, dot245, peer1name)
require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 10, Version: 1},
{Token: dot10, Peer: peer1name, Free: 235},
{Token: dot245, Peer: peer2name, Free: 10}}, ring2.Entries)
require.NoError(t, ring1.Merge(*ring2))
require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 10, Version: 1},
{Token: dot10, Peer: peer1name, Free: 235},
{Token: dot245, Peer: peer2name, Free: 10}}, ring1.Entries)
require.Equal(t, ring2.Entries, ring1.Entries)
}
func TestMergeSplit2(t *testing.T) {
ring1 := New(start, end, peer1name)
ring2 := New(start, end, peer2name)
// Claim everything for peer2
ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 250}, {Token: dot250, Peer: peer2name, Free: 5}}
require.NoError(t, ring2.Merge(*ring1))
require.Equal(t, ring2.Entries, ring1.Entries)
// Now grant a split range to peer1
ring2.GrantRangeToHost(dot10, dot245, peer1name)
require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 10, Version: 1},
{Token: dot10, Peer: peer1name, Free: 235},
{Token: dot245, Peer: peer2name, Free: 5}, {Token: dot250, Peer: peer2name, Free: 5}}, ring2.Entries)
require.NoError(t, ring1.Merge(*ring2))
require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 10, Version: 1},
{Token: dot10, Peer: peer1name, Free: 235},
{Token: dot245, Peer: peer2name, Free: 5}, {Token: dot250, Peer: peer2name, Free: 5}}, ring1.Entries)
require.Equal(t, ring2.Entries, ring1.Entries)
}
// A simple test, very similar to above, but using the marshalling to byte[]s
func TestGossip(t *testing.T) {
ring1 := New(start, end, peer1name)
ring2 := New(start, end, peer2name)
assertRing := func(ring *Ring, entries entries) {
require.Equal(t, entries, ring.Entries)
}
assertRing(ring1, []*entry{})
assertRing(ring2, []*entry{})
// Claim everything for peer1
ring1.ClaimItAll()
assertRing(ring1, []*entry{{Token: start, Peer: peer1name, Free: 255}})
assertRing(ring2, []*entry{})
// Check the Merge sends it to the other ring
require.NoError(t, ring2.Merge(*ring1))
assertRing(ring1, []*entry{{Token: start, Peer: peer1name, Free: 255}})
assertRing(ring2, []*entry{{Token: start, Peer: peer1name, Free: 255}})
}
func assertPeersWithSpace(t *testing.T, ring *Ring, start, end address.Address, expected int) []mesh.PeerName {
peers := ring.ChoosePeersToAskForSpace(start, end)
require.Equal(t, expected, len(peers))
return peers
}
func TestFindFree(t *testing.T) {
ring1 := New(start, end, peer1name)
assertPeersWithSpace(t, ring1, start, end, 0)
ring1.Entries = []*entry{{Token: start, Peer: peer1name}}
assertPeersWithSpace(t, ring1, start, end, 0)
// We shouldn't return outselves
ring1.ReportFree(map[address.Address]address.Offset{start: 10})
assertPeersWithSpace(t, ring1, start, end, 0)
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 1},
{Token: middle, Peer: peer1name, Free: 1}}
assertPeersWithSpace(t, ring1, start, end, 0)
ring1.assertInvariants()
// We should return others
var peers []mesh.PeerName
ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 1}}
peers = assertPeersWithSpace(t, ring1, start, end, 1)
require.Equal(t, peer2name, peers[0])
ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 1},
{Token: middle, Peer: peer3name, Free: 1}}
peers = assertPeersWithSpace(t, ring1, start, middle, 1)
require.Equal(t, peer2name, peers[0])
peers = assertPeersWithSpace(t, ring1, middle, end, 1)
require.Equal(t, peer3name, peers[0])
assertPeersWithSpace(t, ring1, start, end, 2)
ring1.assertInvariants()
}
func TestReportFree(t *testing.T) {
ring1 := New(start, end, peer1name)
ring2 := New(start, end, peer2name)
ring1.ClaimItAll()
ring1.GrantRangeToHost(middle, end, peer2name)
require.NoError(t, ring2.Merge(*ring1))
freespace := make(map[address.Address]address.Offset)
for _, r := range ring2.OwnedRanges() {
freespace[r.Start] = 0
}
ring2.ReportFree(freespace)
}
func TestMisc(t *testing.T) {
ring := New(start, end, peer1name)
require.True(t, ring.Empty(), "empty")
ring.ClaimItAll()
println(ring.String())
}
func TestEmptyGossip(t *testing.T) {
ring1 := New(start, end, peer1name)
ring2 := New(start, end, peer2name)
ring1.ClaimItAll()
// This used to panic, and it shouldn't
require.NoError(t, ring1.Merge(*ring2))
}
func TestMergeOldMessage(t *testing.T) {
ring1 := New(start, end, peer1name)
ring2 := New(start, end, peer2name)
ring1.ClaimItAll()
require.NoError(t, ring2.Merge(*ring1))
ring1.GrantRangeToHost(middle, end, peer1name)
require.NoError(t, ring1.Merge(*ring2))
}
func TestSplitRangeAtBeginning(t *testing.T) {
ring1 := New(start, end, peer1name)
ring2 := New(start, end, peer2name)
ring1.ClaimItAll()
require.NoError(t, ring2.Merge(*ring1))
ring1.GrantRangeToHost(start, middle, peer2name)
require.NoError(t, ring2.Merge(*ring1))
}
func TestOwnedRange(t *testing.T) {
ring1 := New(start, end, peer1name)
ring1.ClaimItAll()
require.Equal(t, []address.Range{{Start: start, End: end}}, ring1.OwnedRanges())
ring1.GrantRangeToHost(middle, end, peer2name)
require.Equal(t, []address.Range{{Start: start, End: middle}}, ring1.OwnedRanges())
ring2 := New(start, end, peer2name)
ring2.Merge(*ring1)
require.Equal(t, []address.Range{{Start: middle, End: end}}, ring2.OwnedRanges())
ring2.Entries = []*entry{{Token: middle, Peer: peer2name}}
require.Equal(t, []address.Range{{Start: start, End: middle}, {Start: middle, End: end}}, ring2.OwnedRanges())
ring2.Entries = []*entry{{Token: dot10, Peer: peer2name}, {Token: middle, Peer: peer2name}}
require.Equal(t, []address.Range{{Start: start, End: dot10}, {Start: dot10, End: middle},
{Start: middle, End: end}}, ring2.OwnedRanges())
}
func TestTransfer(t *testing.T) {
// First test just checks if we can grant some range to a host, when we transfer it, we get it back
ring1 := New(start, end, peer1name)
ring1.ClaimItAll()
ring1.GrantRangeToHost(middle, end, peer2name)
ring1.Transfer(peer2name, peer1name)
require.Equal(t, []address.Range{{start, middle}, {middle, end}}, ring1.OwnedRanges())
// Second test is what happens when a token exists at the end of a range but is transferred
// - does it get resurrected correctly?
ring1 = New(start, end, peer1name)
ring1.ClaimItAll()
ring1.GrantRangeToHost(middle, end, peer2name)
ring1.Transfer(peer2name, peer1name)
ring1.GrantRangeToHost(dot10, middle, peer2name)
require.Equal(t, []address.Range{{start, dot10}, {middle, end}}, ring1.OwnedRanges())
}
func TestOwner(t *testing.T) {
ring1 := New(start, end, peer1name)
require.True(t, ring1.Contains(start), "start should be in ring")
require.False(t, ring1.Contains(end), "end should not be in ring")
require.Equal(t, mesh.UnknownPeerName, ring1.Owner(start))
ring1.ClaimItAll()
ring1.GrantRangeToHost(middle, end, peer2name)
require.Equal(t, peer1name, ring1.Owner(start))
require.Equal(t, peer2name, ring1.Owner(middle))
require.Panics(t, func() {
ring1.Owner(end)
})
}
type addressSlice []address.Address
func (s addressSlice) Len() int { return len(s) }
func (s addressSlice) Less(i, j int) bool { return s[i] < s[j] }
func (s addressSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func TestFuzzRing(t *testing.T) {
var (
numPeers = 25
iterations = 1000
)
peers := make([]mesh.PeerName, numPeers)
for i := 0; i < numPeers; i++ {
peer, _ := mesh.PeerNameFromString(fmt.Sprintf("%02d:00:00:00:02:00", i))
peers[i] = peer
}
// Make a valid, random ring
makeGoodRandomRing := func() *Ring {
addressSpace := end - start
numTokens := rand.Intn(int(addressSpace))
tokenMap := make(map[address.Address]bool)
for i := 0; i < numTokens; i++ {
tokenMap[address.Address(rand.Intn(int(addressSpace)))] = true
}
var tokens []address.Address
for token := range tokenMap {
tokens = append(tokens, token)
}
sort.Sort(addressSlice(tokens))
peer := peers[rand.Intn(len(peers))]
ring := New(start, end, peer)
for _, token := range tokens {
peer = peers[rand.Intn(len(peers))]
ring.Entries = append(ring.Entries, &entry{Token: start + token, Peer: peer})
}
ring.assertInvariants()
return ring
}
for i := 0; i < iterations; i++ {
// make 2 random rings
ring1 := makeGoodRandomRing()
ring2 := makeGoodRandomRing()
// Merge them - this might fail, we don't care
// We just want to make sure it doesn't panic
ring1.Merge(*ring2)
// Check whats left still passes assertions
ring1.assertInvariants()
ring2.assertInvariants()
}
// Make an invalid, random ring
makeBadRandomRing := func() *Ring {
addressSpace := end - start
numTokens := rand.Intn(int(addressSpace))
tokens := make([]address.Address, numTokens)
for i := 0; i < numTokens; i++ {
tokens[i] = address.Address(rand.Intn(int(addressSpace)))
}
peer := peers[rand.Intn(len(peers))]
ring := New(start, end, peer)
for _, token := range tokens {
peer = peers[rand.Intn(len(peers))]
ring.Entries = append(ring.Entries, &entry{Token: start + token, Peer: peer})
}
return ring
}
for i := 0; i < iterations; i++ {
// make 2 random rings
ring1 := makeGoodRandomRing()
ring2 := makeBadRandomRing()
// Merge them - this might fail, we don't care
// We just want to make sure it doesn't panic
ring1.Merge(*ring2)
// Check whats left still passes assertions
ring1.assertInvariants()
}
}
func TestFuzzRingHard(t *testing.T) {
//common.SetLogLevel("debug")
var (
numPeers = 100
iterations = 3000
peers []mesh.PeerName
rings []*Ring
nextPeerID = 0
)
addPeer := func() {
peer, _ := mesh.PeerNameFromString(fmt.Sprintf("%02d:%02d:00:00:00:00", nextPeerID/10, nextPeerID%10))
common.Log.Debugf("%s: Adding peer", peer)
nextPeerID++
peers = append(peers, peer)
rings = append(rings, New(start, end, peer))
}
for i := 0; i < numPeers; i++ {
addPeer()
}
rings[0].ClaimItAll()
randomPeer := func(exclude int) (int, mesh.PeerName, *Ring) {
var peerIndex int
if exclude >= 0 {
peerIndex = rand.Intn(len(peers) - 1)
if peerIndex == exclude {
peerIndex++
}
} else {
peerIndex = rand.Intn(len(peers))
}
return peerIndex, peers[peerIndex], rings[peerIndex]
}
// Keep a map of index -> ranges, as these are a little expensive to
// calculate for every ring on every iteration.
var theRanges = make(map[int][]address.Range)
theRanges[0] = rings[0].OwnedRanges()
addOrRmPeer := func() {
if len(peers) < numPeers {
addPeer()
return
}
// Pick one peer to remove, and a different one to transfer to
peerIndex, peername, _ := randomPeer(-1)
_, otherPeername, otherRing := randomPeer(peerIndex)
// We need to be in a ~converged ring to rmpeer
for _, ring := range rings {
require.NoError(t, otherRing.Merge(*ring))
}
common.Log.Debugf("%s: transferring from peer %s", otherPeername, peername)
otherRing.Transfer(peername, otherPeername)
// Remove peer from our state
peers = append(peers[:peerIndex], peers[peerIndex+1:]...)
rings = append(rings[:peerIndex], rings[peerIndex+1:]...)
theRanges = make(map[int][]address.Range)
// And now tell everyone about the transfer - rmpeer is
// not partition safe
for i, ring := range rings {
require.NoError(t, ring.Merge(*otherRing))
theRanges[i] = ring.OwnedRanges()
}
}
doGrantOrGossip := func() {
var ringsWithRanges = make([]int, 0, len(rings))
for index, ranges := range theRanges {
if len(ranges) > 0 {
ringsWithRanges = append(ringsWithRanges, index)
}
}
if len(ringsWithRanges) > 0 {
// Produce a random split in a random owned range, given to a random peer
indexWithRanges := ringsWithRanges[rand.Intn(len(ringsWithRanges))]
ownedRanges := theRanges[indexWithRanges]
ring := rings[indexWithRanges]
rangeToSplit := ownedRanges[rand.Intn(len(ownedRanges))]
size := address.Subtract(rangeToSplit.End, rangeToSplit.Start)
ipInRange := address.Add(rangeToSplit.Start, address.Offset(rand.Intn(int(size))))
_, peerToGiveTo, _ := randomPeer(-1)
common.Log.Debugf("%s: Granting [%v, %v) to %s", ring.Peer, ipInRange, rangeToSplit.End, peerToGiveTo)
ring.GrantRangeToHost(ipInRange, rangeToSplit.End, peerToGiveTo)
// Now 'gossip' this to a random host (note, note could be same host as above)
otherIndex, _, otherRing := randomPeer(-1)
common.Log.Debugf("%s: 'Gossiping' to %s", ring.Peer, otherRing.Peer)
require.NoError(t, otherRing.Merge(*ring))
theRanges[indexWithRanges] = ring.OwnedRanges()
theRanges[otherIndex] = otherRing.OwnedRanges()
return
}
// No rings think they own anything (as gossip might be behind)
// We're going to pick a random host (which has entries) and gossip
// it to a random host (which may or may not have entries).
var ringsWithEntries = make([]*Ring, 0, len(rings))
for _, ring := range rings {
if len(ring.Entries) > 0 {
ringsWithEntries = append(ringsWithEntries, ring)
}
}
ring1 := ringsWithEntries[rand.Intn(len(ringsWithEntries))]
ring2index, _, ring2 := randomPeer(-1)
common.Log.Debugf("%s: 'Gossiping' to %s", ring1.Peer, ring2.Peer)
require.NoError(t, ring2.Merge(*ring1))
theRanges[ring2index] = ring2.OwnedRanges()
}
for i := 0; i < iterations; i++ {
// about 1 in 10 times, rmpeer or add host
n := rand.Intn(10)
switch {
case n < 1:
addOrRmPeer()
default:
doGrantOrGossip()
}
}
}
func (r *Ring) ClaimItAll() {
r.ClaimForPeers([]mesh.PeerName{r.Peer})
}
func (es entries) String() string {
var buffer bytes.Buffer
fmt.Fprintf(&buffer, "[")
for i, entry := range es {
fmt.Fprintf(&buffer, "%+v", *entry)
if i+1 < len(es) {
fmt.Fprintf(&buffer, " ")
}
}
fmt.Fprintf(&buffer, "]")
return buffer.String()
}
+258
View File
@@ -0,0 +1,258 @@
package space
import (
"bytes"
"fmt"
"sort"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/net/address"
)
type Space struct {
// ours and free represent a set of addresses as a sorted
// sequences of ranges. Even elements give the inclusive
// starting points of ranges, and odd elements give the
// exclusive ending points. Ranges in an array do not
// overlap, and neighbouring ranges are always coalesced if
// possible, so the arrays consist of sorted Addrs without
// repetition.
ours []address.Address
free []address.Address
}
func New() *Space {
return &Space{}
}
func (s *Space) Add(start address.Address, size address.Offset) {
s.free = add(s.free, start, address.Add(start, size))
}
// Clear removes all spaces from this space set. Used during node shutdown.
func (s *Space) Clear() {
s.free = s.free[:0]
s.ours = s.ours[:0]
}
// Walk down the free list calling f() on the in-range portions, until
// f() returns true or we run out of free space. Return true iff f() returned true
func (s *Space) walkFree(r address.Range, f func(address.Range) bool) bool {
if r.Start >= r.End { // degenerate case
return false
}
for i := 0; i < len(s.free); i += 2 {
chunk := address.Range{Start: s.free[i], End: s.free[i+1]}
if chunk.End <= r.Start { // this chunk comes before the range
continue
}
if chunk.Start >= r.End {
// all remaining free space is completely after range
break
}
// at this point we know chunk.End>chunk.Start &&
// chunk.End>r.Start && r.End>chunk.Start && r.End>r.Start
// therefore max(start, r.Start) < min(end, r.End)
// Restrict this block of free space to be in range
if chunk.Start < r.Start {
chunk.Start = r.Start
}
if chunk.End > r.End {
chunk.End = r.End
}
// at this point we know start<end
if f(chunk) {
return true
}
}
return false
}
func (s *Space) Allocate(r address.Range) (bool, address.Address) {
var result address.Address
return s.walkFree(r, func(chunk address.Range) bool {
result = chunk.Start
s.ours = add(s.ours, result, result+1)
s.free = subtract(s.free, result, result+1)
return true
}), result
}
func (s *Space) Claim(addr address.Address) error {
if !contains(s.free, addr) {
return fmt.Errorf("Address %v is not free to claim", addr)
}
s.ours = add(s.ours, addr, addr+1)
s.free = subtract(s.free, addr, addr+1)
return nil
}
func (s *Space) NumFreeAddressesInRange(r address.Range) address.Offset {
res := address.Offset(0)
s.walkFree(r, func(chunk address.Range) bool {
res += chunk.Size()
return false
})
return res
}
func (s *Space) Free(addr address.Address) error {
if !contains(s.ours, addr) {
return fmt.Errorf("Address %v is not ours", addr)
}
if contains(s.free, addr) {
return fmt.Errorf("Address %v is already free", addr)
}
s.ours = subtract(s.ours, addr, addr+1)
s.free = add(s.free, addr, addr+1)
return nil
}
func (s *Space) biggestFreeRange(r address.Range) (biggest address.Range) {
biggestSize := address.Offset(0)
s.walkFree(r, func(chunk address.Range) bool {
if size := chunk.Size(); size >= biggestSize {
biggest = chunk
biggestSize = size
}
return false
})
return
}
func (s *Space) Donate(r address.Range) (address.Range, bool) {
biggest := s.biggestFreeRange(r)
if biggest.Size() == 0 {
return address.Range{}, false
}
// Donate half of that biggest free range. Note size/2 rounds down, so
// the resulting donation size rounds up, and in particular can't be empty.
biggest.Start = address.Add(biggest.Start, biggest.Size()/2)
s.ours = subtract(s.ours, biggest.Start, biggest.End)
s.free = subtract(s.free, biggest.Start, biggest.End)
return biggest, true
}
func firstGreater(a []address.Address, x address.Address) int {
return sort.Search(len(a), func(i int) bool { return a[i] > x })
}
func firstGreaterOrEq(a []address.Address, x address.Address) int {
return sort.Search(len(a), func(i int) bool { return a[i] >= x })
}
// Do the ranges contain the given address?
func contains(addrs []address.Address, addr address.Address) bool {
return firstGreater(addrs, addr)&1 != 0
}
// Take the union of the range [start, end) with the ranges in the array
func add(addrs []address.Address, start address.Address, end address.Address) []address.Address {
return addSub(addrs, start, end, 0)
}
// Subtract the range [start, end) from the ranges in the array
func subtract(addrs []address.Address, start address.Address, end address.Address) []address.Address {
return addSub(addrs, start, end, 1)
}
func addSub(addrs []address.Address, start address.Address, end address.Address, sense int) []address.Address {
startPos := firstGreaterOrEq(addrs, start)
endPos := firstGreater(addrs[startPos:], end) + startPos
// Boundaries up to startPos are unaffected
res := make([]address.Address, startPos, len(addrs)+2)
copy(res, addrs)
// Include start and end as new boundaries if they lie
// outside/inside existing ranges (according to sense).
if startPos&1 == sense {
res = append(res, start)
}
if endPos&1 == sense {
res = append(res, end)
}
// Boundaries after endPos are unaffected
return append(res, addrs[endPos:]...)
}
func (s *Space) String() string {
var buf bytes.Buffer
if len(s.ours) > 0 {
fmt.Fprint(&buf, "owned:")
for i := 0; i < len(s.ours); i += 2 {
fmt.Fprintf(&buf, " %s+%d ", s.ours[i], s.ours[i+1]-s.ours[i])
}
}
if len(s.free) > 0 {
fmt.Fprintf(&buf, "free:")
for i := 0; i < len(s.free); i += 2 {
fmt.Fprintf(&buf, " %s+%d ", s.free[i], s.free[i+1]-s.free[i])
}
}
if len(s.ours) == 0 && len(s.free) == 0 {
fmt.Fprintf(&buf, "No address ranges owned")
}
return buf.String()
}
type addressSlice []address.Address
func (p addressSlice) Len() int { return len(p) }
func (p addressSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p addressSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (s *Space) assertInvariants() {
common.Assert(sort.IsSorted(addressSlice(s.ours)))
common.Assert(sort.IsSorted(addressSlice(s.free)))
}
// Return a slice representing everything we own, whether it is free or not
func (s *Space) everything() []address.Address {
a := make([]address.Address, len(s.ours))
copy(a, s.ours)
for i := 0; i < len(s.free); i += 2 {
a = add(a, s.free[i], s.free[i+1])
}
return a
}
// OwnedRanges returns slice of Ranges, ordered by IP, gluing together
// contiguous sequences of owned and free addresses
func (s *Space) OwnedRanges() []address.Range {
everything := s.everything()
result := make([]address.Range, len(everything)/2)
for i := 0; i < len(everything); i += 2 {
result[i/2] = address.Range{Start: everything[i], End: everything[i+1]}
}
return result
}
// Create a Space that has free space in all the supplied Ranges.
func (s *Space) AddRanges(ranges []address.Range) {
for _, r := range ranges {
s.free = add(s.free, r.Start, r.End)
}
}
// Taking ranges to be a set of all space we should own, add in any excess as free space
func (s *Space) UpdateRanges(ranges []address.Range) {
new := []address.Address{}
for _, r := range ranges {
new = add(new, r.Start, r.End)
}
current := s.everything()
for i := 0; i < len(current); i += 2 {
new = subtract(new, current[i], current[i+1])
}
for i := 0; i < len(new); i += 2 {
s.free = add(s.free, new[i], new[i+1])
}
}
+263
View File
@@ -0,0 +1,263 @@
package space
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/net/address"
wt "github.com/weaveworks/weave/testing"
)
func makeSpace(start address.Address, size address.Offset) *Space {
s := New()
s.Add(start, size)
return s
}
func ip(s string) address.Address {
addr, _ := address.ParseIP(s)
return addr
}
// Helper function to avoid 'NumFreeAddressesInRange(start, end)'
// dozens of times in tests
func (s *Space) NumFreeAddresses() address.Offset {
res := address.Offset(0)
for i := 0; i < len(s.free); i += 2 {
res += address.Subtract(s.free[i+1], s.free[i])
}
return res
}
func TestLowlevel(t *testing.T) {
a := []address.Address{}
a = add(a, 100, 200)
require.Equal(t, []address.Address{100, 200}, a)
require.True(t, !contains(a, 99), "")
require.True(t, contains(a, 100), "")
require.True(t, contains(a, 199), "")
require.True(t, !contains(a, 200), "")
a = add(a, 700, 800)
require.Equal(t, []address.Address{100, 200, 700, 800}, a)
a = add(a, 300, 400)
require.Equal(t, []address.Address{100, 200, 300, 400, 700, 800}, a)
a = add(a, 400, 500)
require.Equal(t, []address.Address{100, 200, 300, 500, 700, 800}, a)
a = add(a, 600, 700)
require.Equal(t, []address.Address{100, 200, 300, 500, 600, 800}, a)
a = add(a, 500, 600)
require.Equal(t, []address.Address{100, 200, 300, 800}, a)
a = subtract(a, 500, 600)
require.Equal(t, []address.Address{100, 200, 300, 500, 600, 800}, a)
a = subtract(a, 600, 700)
require.Equal(t, []address.Address{100, 200, 300, 500, 700, 800}, a)
a = subtract(a, 400, 500)
require.Equal(t, []address.Address{100, 200, 300, 400, 700, 800}, a)
a = subtract(a, 300, 400)
require.Equal(t, []address.Address{100, 200, 700, 800}, a)
a = subtract(a, 700, 800)
require.Equal(t, []address.Address{100, 200}, a)
a = subtract(a, 100, 200)
require.Equal(t, []address.Address{}, a)
s := New()
require.Equal(t, address.Offset(0), s.NumFreeAddresses())
ok, got := s.Allocate(address.NewRange(0, 1000))
require.False(t, ok, "allocate in empty space should fail")
s.Add(100, 100)
require.Equal(t, address.Offset(100), s.NumFreeAddresses())
ok, got = s.Allocate(address.NewRange(0, 1000))
require.True(t, ok && got == 100, "allocate")
require.Equal(t, address.Offset(99), s.NumFreeAddresses())
require.NoError(t, s.Claim(150))
require.Equal(t, address.Offset(98), s.NumFreeAddresses())
require.NoError(t, s.Free(100))
require.Equal(t, address.Offset(99), s.NumFreeAddresses())
wt.AssertErrorInterface(t, (*error)(nil), s.Free(0), "free not allocated")
wt.AssertErrorInterface(t, (*error)(nil), s.Free(100), "double free")
r, ok := s.Donate(address.NewRange(0, 1000))
require.True(t, ok && r.Start == 125 && r.Size() == 25, "donate")
// test Donate when addresses are scarce
s = New()
r, ok = s.Donate(address.NewRange(0, 1000))
require.True(t, !ok, "donate on empty space should fail")
s.Add(0, 3)
require.NoError(t, s.Claim(0))
require.NoError(t, s.Claim(2))
r, ok = s.Donate(address.NewRange(0, 1000))
require.True(t, ok && r.Start == 1 && r.End == 2, "donate")
r, ok = s.Donate(address.NewRange(0, 1000))
require.True(t, !ok, "donate should fail")
}
func TestSpaceAllocate(t *testing.T) {
const (
testAddr1 = "10.0.3.4"
testAddr2 = "10.0.3.5"
testAddrx = "10.0.3.19"
testAddry = "10.0.9.19"
containerID = "deadbeef"
size = 20
)
var (
start = ip(testAddr1)
)
space1 := makeSpace(start, size)
require.Equal(t, address.Offset(20), space1.NumFreeAddresses())
space1.assertInvariants()
_, addr1 := space1.Allocate(address.NewRange(start, size))
require.Equal(t, testAddr1, addr1.String(), "address")
require.Equal(t, address.Offset(19), space1.NumFreeAddresses())
space1.assertInvariants()
_, addr2 := space1.Allocate(address.NewRange(start, size))
require.False(t, addr2.String() == testAddr1, "address")
require.Equal(t, address.Offset(18), space1.NumFreeAddresses())
require.Equal(t, address.Offset(13), space1.NumFreeAddressesInRange(address.Range{Start: ip(testAddr1), End: ip(testAddrx)}))
require.Equal(t, address.Offset(18), space1.NumFreeAddressesInRange(address.Range{Start: ip(testAddr1), End: ip(testAddry)}))
space1.assertInvariants()
space1.Free(addr2)
space1.assertInvariants()
wt.AssertErrorInterface(t, (*error)(nil), space1.Free(addr2), "double free")
wt.AssertErrorInterface(t, (*error)(nil), space1.Free(ip(testAddrx)), "address not allocated")
wt.AssertErrorInterface(t, (*error)(nil), space1.Free(ip(testAddry)), "wrong out of range")
space1.assertInvariants()
}
func TestSpaceFree(t *testing.T) {
const (
testAddr1 = "10.0.3.4"
testAddrx = "10.0.3.19"
testAddry = "10.0.9.19"
containerID = "deadbeef"
)
entireRange := address.NewRange(ip(testAddr1), 20)
space := makeSpace(ip(testAddr1), 20)
// Check we are prepared to give up the entire space
r := space.biggestFreeRange(entireRange)
require.True(t, r.Start == ip(testAddr1) && r.Size() == 20, "Wrong space")
for i := 0; i < 20; i++ {
ok, _ := space.Allocate(entireRange)
require.True(t, ok, "Failed to get address")
}
// Check we are full
ok, _ := space.Allocate(entireRange)
require.True(t, !ok, "Should have failed to get address")
r, ok = space.Donate(entireRange)
require.True(t, r.Size() == 0, "Wrong space")
// Free in the middle
require.NoError(t, space.Free(ip("10.0.3.13")))
r = space.biggestFreeRange(entireRange)
require.True(t, r.Start == ip("10.0.3.13") && r.Size() == 1, "Wrong space")
// Free one at the end
require.NoError(t, space.Free(ip("10.0.3.23")))
r = space.biggestFreeRange(entireRange)
require.True(t, r.Start == ip("10.0.3.23") && r.Size() == 1, "Wrong space")
// Now free a few at the end
require.NoError(t, space.Free(ip("10.0.3.22")))
require.NoError(t, space.Free(ip("10.0.3.21")))
require.Equal(t, address.Offset(4), space.NumFreeAddresses())
// Now get the biggest free space; should be 3.21
r = space.biggestFreeRange(entireRange)
require.True(t, r.Start == ip("10.0.3.21") && r.Size() == 3, "Wrong space")
// Now free a few in the middle
require.NoError(t, space.Free(ip("10.0.3.12")))
require.NoError(t, space.Free(ip("10.0.3.11")))
require.NoError(t, space.Free(ip("10.0.3.10")))
require.Equal(t, address.Offset(7), space.NumFreeAddresses())
// Now get the biggest free space; should be 3.21
r = space.biggestFreeRange(entireRange)
require.True(t, r.Start == ip("10.0.3.10") && r.Size() == 4, "Wrong space")
require.Equal(t, []address.Range{{Start: ip("10.0.3.4"), End: ip("10.0.3.24")}}, space.OwnedRanges())
}
func TestDonateSimple(t *testing.T) {
const (
testAddr1 = "10.0.1.0"
testAddr2 = "10.0.1.32"
size = 48
)
var (
ipAddr1 = ip(testAddr1)
)
ps1 := makeSpace(ipAddr1, size)
// Empty space set should split in two and give me the second half
r, ok := ps1.Donate(address.NewRange(ip(testAddr1), size))
numGivenUp := r.Size()
require.True(t, ok, "Donate result")
require.Equal(t, "10.0.1.24", r.Start.String(), "Invalid start")
require.Equal(t, address.Offset(size/2), numGivenUp)
require.Equal(t, address.Offset(size/2), ps1.NumFreeAddresses())
// Now check we can give the rest up.
count := 0 // count to avoid infinite loop
for ; count < 1000; count++ {
r, ok := ps1.Donate(address.NewRange(ip(testAddr1), size))
if !ok {
break
}
numGivenUp += r.Size()
}
require.Equal(t, address.Offset(0), ps1.NumFreeAddresses())
require.Equal(t, address.Offset(size), numGivenUp)
}
func TestDonateHard(t *testing.T) {
//common.InitDefaultLogging(true)
var (
start = ip("10.0.1.0")
size address.Offset = 48
)
// Fill a fresh space
spaceset := makeSpace(start, size)
for i := address.Offset(0); i < size; i++ {
ok, _ := spaceset.Allocate(address.NewRange(start, size))
require.True(t, ok, "Failed to get IP!")
}
require.Equal(t, address.Offset(0), spaceset.NumFreeAddresses())
// Now free all but the last address
// this will force us to split the free list
for i := address.Offset(0); i < size-1; i++ {
require.NoError(t, spaceset.Free(address.Add(start, i)))
}
// Now split
newRange, ok := spaceset.Donate(address.NewRange(start, size))
require.True(t, ok, "GiveUpSpace result")
require.Equal(t, ip("10.0.1.23"), newRange.Start)
require.Equal(t, address.Offset(24), newRange.Size())
require.Equal(t, address.Offset(23), spaceset.NumFreeAddresses())
//Space set should now have 2 spaces
expected := New()
expected.Add(start, 23)
expected.ours = add(nil, ip("10.0.1.47"), ip("10.0.1.48"))
require.Equal(t, expected, spaceset)
}
+95
View File
@@ -0,0 +1,95 @@
package ipam
import (
"fmt"
"github.com/weaveworks/weave/ipam/paxos"
"github.com/weaveworks/weave/net/address"
)
type Status struct {
Paxos *paxos.Status
Range string
RangeNumIPs int
DefaultSubnet string
Entries []EntryStatus
PendingClaims []ClaimStatus
PendingAllocates []string
}
type EntryStatus struct {
Token string
Size uint32
Peer string
Nickname string
IsKnownPeer bool
Version uint32
}
type ClaimStatus struct {
Ident string
Address address.Address
}
func NewStatus(allocator *Allocator, defaultSubnet address.CIDR) *Status {
if allocator == nil {
return nil
}
var paxosStatus *paxos.Status
if allocator.paxosTicker != nil {
paxosStatus = paxos.NewStatus(allocator.paxos)
}
resultChan := make(chan *Status)
allocator.actionChan <- func() {
resultChan <- &Status{
paxosStatus,
allocator.universe.String(),
int(allocator.universe.Size()),
defaultSubnet.String(),
newEntryStatusSlice(allocator),
newClaimStatusSlice(allocator),
newAllocateIdentSlice(allocator)}
}
return <-resultChan
}
func newEntryStatusSlice(allocator *Allocator) []EntryStatus {
var slice []EntryStatus
if allocator.ring.Empty() {
return slice
}
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
}
func newClaimStatusSlice(allocator *Allocator) []ClaimStatus {
var slice []ClaimStatus
for _, op := range allocator.pendingClaims {
claim := op.(*claim)
slice = append(slice, ClaimStatus{claim.ident, claim.addr})
}
return slice
}
func newAllocateIdentSlice(allocator *Allocator) []string {
var slice []string
for _, op := range allocator.pendingAllocates {
allocate := op.(*allocate)
slice = append(slice, fmt.Sprintf("%s %s", allocate.ident, allocate.r.String()))
}
return slice
}
+220
View File
@@ -0,0 +1,220 @@
package ipam
import (
"fmt"
"math/rand"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/testing/gossip"
)
type mockMessage struct {
dst mesh.PeerName
msgType byte
buf []byte
}
func (m *mockMessage) String() string {
return fmt.Sprintf("-> %s [%x]", m.dst, m.buf)
}
func toStringArray(messages []mockMessage) []string {
out := make([]string, len(messages))
for i := range out {
out[i] = messages[i].String()
}
return out
}
type mockGossipComms struct {
sync.RWMutex
*testing.T
name string
messages []mockMessage
}
func (m *mockGossipComms) String() string {
m.RLock()
defer m.RUnlock()
return fmt.Sprintf("[mockGossipComms %s]", m.name)
}
// Note: this style of verification, using equalByteBuffer, requires
// that the contents of messages are never re-ordered. Which, for instance,
// requires they are not based off iterating through a map.
func (m *mockGossipComms) GossipBroadcast(update mesh.GossipData) error {
m.Lock()
defer m.Unlock()
buf := []byte{}
if len(m.messages) == 0 {
require.FailNow(m, fmt.Sprintf("%s: Gossip broadcast message unexpected: \n%x", m.name, buf))
} else if msg := m.messages[0]; msg.dst != mesh.UnknownPeerName {
require.FailNow(m, fmt.Sprintf("%s: Expected Gossip message to %s but got broadcast", m.name, msg.dst))
} else if msg.buf != nil && !equalByteBuffer(msg.buf, buf) {
require.FailNow(m, fmt.Sprintf("%s: Gossip message not sent as expected: \nwant: %x\ngot : %x", m.name, msg.buf, buf))
} else {
// Swallow this message
m.messages = m.messages[1:]
}
return nil
}
func equalByteBuffer(a, b []byte) bool {
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return false
}
}
return true
}
func (m *mockGossipComms) GossipUnicast(dstPeerName mesh.PeerName, buf []byte) error {
m.Lock()
defer m.Unlock()
if len(m.messages) == 0 {
require.FailNow(m, fmt.Sprintf("%s: Gossip message to %s unexpected: \n%s", m.name, dstPeerName, buf))
} else if msg := m.messages[0]; msg.dst == mesh.UnknownPeerName {
require.FailNow(m, fmt.Sprintf("%s: Expected Gossip broadcast message but got dest %s", m.name, dstPeerName))
} else if msg.dst != dstPeerName {
require.FailNow(m, fmt.Sprintf("%s: Expected Gossip message to %s but got dest %s", m.name, msg.dst, dstPeerName))
} else if buf[0] != msg.msgType {
require.FailNow(m, fmt.Sprintf("%s: Expected Gossip message of type %d but got type %d", m.name, msg.msgType, buf[0]))
} else if msg.buf != nil && !equalByteBuffer(msg.buf, buf[1:]) {
require.FailNow(m, fmt.Sprintf("%s: Gossip message not sent as expected: \nwant: %x\ngot : %x", m.name, msg.buf, buf[1:]))
} else {
// Swallow this message
m.messages = m.messages[1:]
}
return nil
}
func ExpectMessage(alloc *Allocator, dst string, msgType byte, buf []byte) {
m := alloc.gossip.(*mockGossipComms)
dstPeerName, _ := mesh.PeerNameFromString(dst)
m.Lock()
m.messages = append(m.messages, mockMessage{dstPeerName, msgType, buf})
m.Unlock()
}
func ExpectBroadcastMessage(alloc *Allocator, buf []byte) {
m := alloc.gossip.(*mockGossipComms)
m.Lock()
m.messages = append(m.messages, mockMessage{mesh.UnknownPeerName, 0, buf})
m.Unlock()
}
func CheckAllExpectedMessagesSent(allocs ...*Allocator) {
for _, alloc := range allocs {
m := alloc.gossip.(*mockGossipComms)
m.RLock()
if len(m.messages) > 0 {
require.FailNow(m, fmt.Sprintf("%s: Gossip message(s) not sent as expected: \n%x", m.name, m.messages))
}
m.RUnlock()
}
}
func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, address.Range) {
peername, err := mesh.PeerNameFromString(name)
if err != nil {
panic(err)
}
_, cidr, err := address.ParseCIDR(cidrStr)
if err != nil {
panic(err)
}
alloc := NewAllocator(peername, mesh.PeerUID(rand.Int63()),
"nick-"+name, cidr.Range(), quorum, func(mesh.PeerName) bool { return true })
return alloc, cidr.HostRange()
}
func makeAllocatorWithMockGossip(t *testing.T, name string, universeCIDR string, quorum uint) (*Allocator, address.Range) {
alloc, subnet := makeAllocator(name, universeCIDR, quorum)
gossip := &mockGossipComms{T: t, name: name}
alloc.SetInterfaces(gossip)
alloc.Start()
return alloc, subnet
}
func (alloc *Allocator) claimRingForTesting(allocs ...*Allocator) {
peers := []mesh.PeerName{alloc.ourName}
for _, alloc2 := range allocs {
peers = append(peers, alloc2.ourName)
}
alloc.ring.ClaimForPeers(normalizeConsensus(peers))
alloc.space.AddRanges(alloc.ring.OwnedRanges())
}
func (alloc *Allocator) NumFreeAddresses(r address.Range) address.Offset {
resultChan := make(chan address.Offset)
alloc.actionChan <- func() {
resultChan <- alloc.space.NumFreeAddressesInRange(r)
}
return <-resultChan
}
// Check whether or not something was sent on a channel
func AssertSent(t *testing.T, ch <-chan bool) {
timeout := time.After(10 * time.Second)
select {
case <-ch:
// This case is ok
case <-timeout:
require.FailNow(t, "Nothing sent on channel")
}
}
func AssertNothingSent(t *testing.T, ch <-chan bool) {
select {
case val := <-ch:
require.FailNow(t, fmt.Sprintf("Unexpected value on channel: %v", val))
default:
// no message received
}
}
func AssertNothingSentErr(t *testing.T, ch <-chan error) {
select {
case val := <-ch:
require.FailNow(t, fmt.Sprintf("Unexpected value on channel: %v", val))
default:
// no message received
}
}
func makeNetworkOfAllocators(size int, cidr string) ([]*Allocator, *gossip.TestRouter, address.Range) {
gossipRouter := gossip.NewTestRouter(0.0)
allocs := make([]*Allocator, size)
var subnet address.Range
for i := 0; i < size; i++ {
var alloc *Allocator
alloc, subnet = makeAllocator(fmt.Sprintf("%02d:00:00:02:00:00", i),
cidr, uint(size/2+1))
alloc.SetInterfaces(gossipRouter.Connect(alloc.ourName, alloc))
alloc.Start()
allocs[i] = alloc
}
allocs[size-1].gossip.GossipBroadcast(allocs[size-1].Gossip())
gossipRouter.Flush()
return allocs, gossipRouter, subnet
}
func stopNetworkOfAllocators(allocs []*Allocator) {
for _, alloc := range allocs {
alloc.Stop()
}
}
+460
View File
@@ -0,0 +1,460 @@
package mesh
import (
"fmt"
"net"
"strconv"
"sync"
"time"
)
type Connection interface {
Local() *Peer
Remote() *Peer
RemoteTCPAddr() string
Outbound() bool
Established() bool
BreakTie(Connection) ConnectionTieBreak
Shutdown(error)
Log(args ...interface{})
}
type ConnectionTieBreak int
const (
TieBreakWon ConnectionTieBreak = iota
TieBreakLost
TieBreakTied
)
var ErrConnectToSelf = fmt.Errorf("Cannot connect to ourself")
type RemoteConnection struct {
local *Peer
remote *Peer
remoteTCPAddr string
outbound bool
established bool
}
type LocalConnection struct {
sync.RWMutex
RemoteConnection
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
func NewRemoteConnection(from, to *Peer, tcpAddr string, outbound bool, established bool) *RemoteConnection {
return &RemoteConnection{
local: from,
remote: to,
remoteTCPAddr: tcpAddr,
outbound: outbound,
established: established,
}
}
func (conn *RemoteConnection) Local() *Peer { return conn.local }
func (conn *RemoteConnection) Remote() *Peer { return conn.remote }
func (conn *RemoteConnection) RemoteTCPAddr() string { return conn.remoteTCPAddr }
func (conn *RemoteConnection) Outbound() bool { return conn.outbound }
func (conn *RemoteConnection) Established() bool { return conn.established }
func (conn *RemoteConnection) BreakTie(Connection) ConnectionTieBreak { return TieBreakTied }
func (conn *RemoteConnection) Shutdown(error) {}
func (conn *RemoteConnection) Log(args ...interface{}) {
log.Println(append(append([]interface{}{}, fmt.Sprintf("->[%s|%s]:", conn.remoteTCPAddr, conn.remote)), args...)...)
}
func (conn *RemoteConnection) ErrorLog(args ...interface{}) {
log.Errorln(append(append([]interface{}{}, fmt.Sprintf("->[%s|%s]:", conn.remoteTCPAddr, conn.remote)), args...)...)
}
// Does not return anything. If the connection is successful, it will
// end up in the local peer's connections map.
func StartLocalConnection(connRemote *RemoteConnection, tcpConn *net.TCPConn, router *Router, acceptNewPeer bool) {
if connRemote.local != router.Ourself.Peer {
log.Fatal("Attempt to create local connection from a peer which is not ourself")
}
actionChan := make(chan ConnectionAction, ChannelSize)
errorChan := make(chan error, 1)
finished := make(chan struct{})
conn := &LocalConnection{
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, errorChan, finished, acceptNewPeer)
}
func (conn *LocalConnection) BreakTie(dupConn Connection) ConnectionTieBreak {
dupConnLocal := dupConn.(*LocalConnection)
// conn.uid is used as the tie breaker here, in the knowledge that
// both sides will make the same decision.
if conn.uid < dupConnLocal.uid {
return TieBreakWon
} else if dupConnLocal.uid < conn.uid {
return TieBreakLost
} else {
return TieBreakTied
}
}
func (conn *LocalConnection) Established() bool {
return conn.established
}
func (conn *LocalConnection) SendProtocolMsg(m ProtocolMsg) {
if err := conn.sendProtocolMsg(m); err != nil {
conn.Shutdown(err)
}
}
// ACTOR methods
// NB: The conn.* fields are only written by the connection actor
// process, which is the caller of the ConnectionAction funs. Hence we
// do not need locks for reading, and only need write locks for fields
// read by other processes.
// 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")
}
select {
case conn.errorChan <- err:
default:
}
}
// Send an actor request to the actorLoop, but don't block if
// actorLoop has exited - see http://blog.golang.org/pipelines for
// pattern
func (conn *LocalConnection) sendAction(action ConnectionAction) {
select {
case conn.actionChan <- action:
case <-conn.finished:
}
}
// ACTOR server
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)
conn.TCPConn.SetLinger(0)
intro, err := ProtocolIntroParams{
MinVersion: conn.Router.ProtocolMinVersion,
MaxVersion: ProtocolMaxVersion,
Features: conn.makeFeatures(),
Conn: conn.TCPConn,
Password: conn.Router.Password,
Outbound: conn.outbound,
}.DoIntro()
if err != nil {
return
}
conn.SessionKey = intro.SessionKey
conn.tcpSender = intro.Sender
conn.version = intro.Version
remote, err := conn.parseFeatures(intro.Features)
if err != nil {
return
}
if err = conn.registerRemote(remote, acceptNewPeer); err != nil {
return
}
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: sessionKey,
SendControlMessage: conn.sendOverlayControlMessage,
Features: intro.Features,
}
if conn.OverlayConn, err = conn.Router.Overlay.PrepareConnection(params); err != nil {
return
}
// As soon as we do AddConnection, the new connection becomes
// visible to the packet routing logic. So AddConnection must
// come after PrepareConnection
if err = conn.Router.Ourself.AddConnection(conn); err != nil {
return
}
conn.Router.ConnectionMaker.ConnectionCreated(conn)
// OverlayConnection confirmation comes after AddConnection,
// because only after that completes do we know the connection is
// valid: in particular that it is not a duplicate connection to
// the same peer. Overlay communication on a duplicate connection
// can cause problems such as tripping up overlay crypto at the
// other end due to data being decoded by the other connection. It
// is also generally wasteful to engage in any interaction with
// the remote on a connection that turns out to be invalid.
conn.OverlayConn.Confirm()
// receiveTCP must follow also AddConnection. In the absence
// of any indirect connectivity to the remote peer, the first
// we hear about it (and any peers reachable from it) is
// through topology gossip it sends us on the connection. We
// must ensure that the connection has been added to Ourself
// prior to processing any such gossip, otherwise we risk
// immediately gc'ing part of that newly received portion of
// the topology (though not the remote peer itself, since that
// will have a positive ref count), leaving behind dangling
// references to peers. Hence we must invoke AddConnection,
// which is *synchronous*, first.
conn.heartbeatTCP = time.NewTicker(TCPHeartbeat)
go conn.receiveTCP(intro.Receiver)
// AddConnection must precede actorLoop. More precisely, it
// must precede shutdown, since that invokes DeleteConnection
// and is invoked on termination of this entire
// function. Essentially this boils down to a prohibition on
// 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, errorChan)
}
func (conn *LocalConnection) makeFeatures() map[string]string {
features := map[string]string{
"PeerNameFlavour": PeerNameFlavour,
"Name": conn.local.Name.String(),
"NickName": conn.local.NickName,
"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
}
type features map[string]string
func (features features) MustHave(keys []string) error {
for _, key := range keys {
if _, ok := features[key]; !ok {
return fmt.Errorf("Field %s is missing", key)
}
}
return nil
}
func (features features) Get(key string) string {
return features[key]
}
func (conn *LocalConnection) parseFeatures(features features) (*Peer, error) {
if err := features.MustHave([]string{"PeerNameFlavour", "Name", "NickName", "UID", "ConnID"}); err != nil {
return nil, err
}
remotePeerNameFlavour := features.Get("PeerNameFlavour")
if remotePeerNameFlavour != PeerNameFlavour {
return nil, fmt.Errorf("Peer name flavour mismatch (ours: '%s', theirs: '%s')", PeerNameFlavour, remotePeerNameFlavour)
}
name, err := PeerNameFromString(features.Get("Name"))
if err != nil {
return nil, err
}
nickName := features.Get("NickName")
var shortID uint64
var hasShortID bool
if shortIDStr, present := features["ShortID"]; present {
hasShortID = true
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
}
remoteConnID, err := strconv.ParseUint(features.Get("ConnID"), 10, 64)
if err != nil {
return nil, err
}
conn.uid ^= remoteConnID
peer := NewPeer(name, nickName, uid, 0, PeerShortID(shortID))
peer.HasShortID = hasShortID
return peer, nil
}
func (conn *LocalConnection) registerRemote(remote *Peer, acceptNewPeer bool) error {
if acceptNewPeer {
conn.remote = conn.Router.Peers.FetchWithDefault(remote)
} else {
conn.remote = conn.Router.Peers.FetchAndAddRef(remote.Name)
if conn.remote == nil {
return fmt.Errorf("Found unknown remote name: %s at %s", remote.Name, conn.remoteTCPAddr)
}
}
if conn.remote == conn.local {
return ErrConnectToSelf
}
return nil
}
func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction, errorChan <-chan error) (err error) {
fwdErrorChan := conn.OverlayConn.ErrorChannel()
fwdEstablishedChan := conn.OverlayConn.EstablishedChannel()
for err == nil {
select {
case action := <-actionChan:
err = action()
case err = <-errorChan:
case <-conn.heartbeatTCP.C:
err = conn.sendSimpleProtocolMsg(ProtocolHeartbeat)
case <-fwdEstablishedChan:
conn.established = true
fwdEstablishedChan = nil
conn.Router.Ourself.ConnectionEstablished(conn)
case err = <-fwdErrorChan:
}
}
return
}
func (conn *LocalConnection) shutdown(err error) {
if conn.remote == nil {
log.Errorf("->[%s] connection shutting down due to error during handshake: %v", conn.remoteTCPAddr, err)
} else {
conn.ErrorLog("connection shutting down due to error:", err)
}
if conn.TCPConn != nil {
checkWarn(conn.TCPConn.Close())
}
if conn.remote != nil {
conn.Router.Peers.Dereference(conn.remote)
conn.Router.Ourself.DeleteConnection(conn)
}
if conn.heartbeatTCP != nil {
conn.heartbeatTCP.Stop()
}
if conn.OverlayConn != nil {
conn.OverlayConn.Stop()
}
conn.Router.ConnectionMaker.ConnectionTerminated(conn, err)
}
func (conn *LocalConnection) sendOverlayControlMessage(tag byte, msg []byte) error {
return conn.sendProtocolMsg(ProtocolMsg{ProtocolTag(tag), msg})
}
// Helpers
func (conn *LocalConnection) sendSimpleProtocolMsg(tag ProtocolTag) error {
return conn.sendProtocolMsg(ProtocolMsg{tag: tag})
}
func (conn *LocalConnection) sendProtocolMsg(m ProtocolMsg) error {
return conn.tcpSender.Send(append([]byte{byte(m.tag)}, m.msg...))
}
func (conn *LocalConnection) receiveTCP(receiver TCPReceiver) {
var err error
for {
conn.extendReadDeadline()
var msg []byte
if msg, err = receiver.Receive(); err != nil {
break
}
if len(msg) < 1 {
conn.Log("ignoring blank msg")
continue
}
if err = conn.handleProtocolMsg(ProtocolTag(msg[0]), msg[1:]); err != nil {
break
}
}
conn.Shutdown(err)
}
func (conn *LocalConnection) handleProtocolMsg(tag ProtocolTag, payload []byte) error {
switch tag {
case ProtocolHeartbeat:
case ProtocolReserved1, ProtocolReserved2, ProtocolReserved3, ProtocolOverlayControlMsg:
conn.OverlayConn.ControlMessage(byte(tag), payload)
case ProtocolGossipUnicast, ProtocolGossipBroadcast, ProtocolGossip:
return conn.Router.handleGossip(tag, payload)
default:
conn.Log("ignoring unknown protocol tag:", tag)
}
return nil
}
func (conn *LocalConnection) extendReadDeadline() {
conn.TCPConn.SetReadDeadline(time.Now().Add(TCPHeartbeat * 2))
}
func (conn *LocalConnection) Untrusted() bool {
return !conn.TrustRemote || !conn.TrustedByRemote
}
+317
View File
@@ -0,0 +1,317 @@
package mesh
import (
"fmt"
"math/rand"
"net"
"time"
)
const (
InitialInterval = 2 * time.Second
MaxInterval = 6 * time.Minute
ResetAfter = 1 * time.Minute
)
type peerAddrs map[string]*net.TCPAddr
type ConnectionMaker struct {
ourself *LocalPeer
peers *Peers
port int
discovery bool
targets map[string]*Target
connections map[Connection]struct{}
directPeers peerAddrs
actionChan chan<- ConnectionMakerAction
}
type TargetState int
const (
TargetWaiting TargetState = iota // we are waiting to connect there
TargetAttempting // we are attempting to connect there
TargetConnected // we are connected to there
)
// Information about an address where we may find a peer
type Target struct {
state TargetState
lastError error // reason for disconnection last time
tryAfter time.Time // next time to try this address
tryInterval time.Duration // retry delay on next failure
}
type ConnectionMakerAction func() bool
func NewConnectionMaker(ourself *LocalPeer, peers *Peers, port int, discovery bool) *ConnectionMaker {
actionChan := make(chan ConnectionMakerAction, ChannelSize)
cm := &ConnectionMaker{
ourself: ourself,
peers: peers,
port: port,
discovery: discovery,
directPeers: peerAddrs{},
targets: make(map[string]*Target),
connections: make(map[Connection]struct{}),
actionChan: actionChan}
go cm.queryLoop(actionChan)
return cm
}
func (cm *ConnectionMaker) InitiateConnections(peers []string, replace bool) []error {
errors := []error{}
addrs := peerAddrs{}
for _, peer := range peers {
host, port, err := net.SplitHostPort(peer)
if err != nil {
host = peer
port = "0" // we use that as an indication that "no port was supplied"
}
if addr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%s", host, port)); err != nil {
errors = append(errors, err)
} else {
addrs[peer] = addr
}
}
cm.actionChan <- func() bool {
if replace {
cm.directPeers = peerAddrs{}
}
for peer, addr := range addrs {
cm.directPeers[peer] = addr
// curtail any existing reconnect interval
if target, found := cm.targets[cm.completeAddr(*addr)]; found {
target.nextTryNow()
}
}
return true
}
return errors
}
func (cm *ConnectionMaker) ForgetConnections(peers []string) {
cm.actionChan <- func() bool {
for _, peer := range peers {
delete(cm.directPeers, peer)
}
return false
}
}
func (cm *ConnectionMaker) ConnectionAborted(address string, err error) {
cm.actionChan <- func() bool {
target := cm.targets[address]
target.state = TargetWaiting
target.lastError = err
target.nextTryLater()
return true
}
}
func (cm *ConnectionMaker) ConnectionCreated(conn Connection) {
cm.actionChan <- func() bool {
cm.connections[conn] = void
if conn.Outbound() {
target := cm.targets[conn.RemoteTCPAddr()]
target.state = TargetConnected
}
return false
}
}
func (cm *ConnectionMaker) ConnectionTerminated(conn Connection, err error) {
cm.actionChan <- func() bool {
delete(cm.connections, conn)
if conn.Outbound() {
target := cm.targets[conn.RemoteTCPAddr()]
target.state = TargetWaiting
target.lastError = err
switch {
case err == ErrConnectToSelf:
target.nextTryNever()
case time.Now().After(target.tryAfter.Add(ResetAfter)):
target.nextTryNow()
default:
target.nextTryLater()
}
}
return true
}
}
func (cm *ConnectionMaker) Refresh() {
cm.actionChan <- func() bool { return true }
}
func (cm *ConnectionMaker) queryLoop(actionChan <-chan ConnectionMakerAction) {
timer := time.NewTimer(MaxDuration)
run := func() { timer.Reset(cm.checkStateAndAttemptConnections()) }
for {
select {
case action := <-actionChan:
if action() {
run()
}
case <-timer.C:
run()
}
}
}
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{})
directTarget = make(map[string]struct{})
)
ourConnectedPeers, ourConnectedTargets, ourInboundIPs := cm.ourConnections()
addTarget := func(address string) {
if _, connected := ourConnectedTargets[address]; connected {
return
}
validTarget[address] = void
if _, found := cm.targets[address]; found {
return
}
target := &Target{state: TargetWaiting}
target.nextTryNow()
cm.targets[address] = target
}
// Add direct targets that are not connected
for _, addr := range cm.directPeers {
attempt := true
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[addr.IP.String()]; connected {
attempt = false
}
}
address := cm.completeAddr(*addr)
directTarget[address] = void
if attempt {
addTarget(address)
}
}
// Add targets for peers that someone else is connected to, but we
// aren't
if cm.discovery {
cm.addPeerTargets(ourConnectedPeers, addTarget)
}
return cm.connectToTargets(validTarget, directTarget)
}
func (cm *ConnectionMaker) ourConnections() (PeerNameSet, map[string]struct{}, map[string]struct{}) {
var (
ourConnectedPeers = make(PeerNameSet)
ourConnectedTargets = make(map[string]struct{})
ourInboundIPs = make(map[string]struct{})
)
for conn := range cm.connections {
address := conn.RemoteTCPAddr()
ourConnectedPeers[conn.Remote().Name] = void
ourConnectedTargets[address] = void
if conn.Outbound() {
continue
}
if ip, _, err := net.SplitHostPort(address); err == nil { // should always succeed
ourInboundIPs[ip] = void
}
}
return ourConnectedPeers, ourConnectedTargets, ourInboundIPs
}
func (cm *ConnectionMaker) addPeerTargets(ourConnectedPeers PeerNameSet, addTarget func(string)) {
cm.peers.ForEach(func(peer *Peer) {
if peer == cm.ourself.Peer {
return
}
// Modifying peer.connections requires a write lock on Peers,
// and since we are holding a read lock (due to the ForEach),
// access without locking the peer is safe.
for otherPeer, conn := range peer.connections {
if otherPeer == cm.ourself.Name {
continue
}
if _, connected := ourConnectedPeers[otherPeer]; connected {
continue
}
address := conn.RemoteTCPAddr()
if conn.Outbound() {
addTarget(address)
} else if ip, _, err := net.SplitHostPort(address); err == nil {
// There is no point connecting to the (likely
// ephemeral) remote port of an inbound connection
// that some peer has. Let's try to connect on the
// weave port instead.
addTarget(fmt.Sprintf("%s:%d", ip, cm.port))
}
}
})
}
func (cm *ConnectionMaker) connectToTargets(validTarget map[string]struct{}, directTarget map[string]struct{}) time.Duration {
now := time.Now() // make sure we catch items just added
after := MaxDuration
for address, target := range cm.targets {
if target.state != TargetWaiting {
continue
}
if _, valid := validTarget[address]; !valid {
delete(cm.targets, address)
continue
}
if target.tryAfter.IsZero() {
continue
}
switch duration := target.tryAfter.Sub(now); {
case duration <= 0:
target.state = TargetAttempting
_, isCmdLineTarget := directTarget[address]
go cm.attemptConnection(address, isCmdLineTarget)
case duration < after:
after = duration
}
}
return after
}
func (cm *ConnectionMaker) attemptConnection(address string, acceptNewPeer bool) {
log.Printf("->[%s] attempting connection", address)
if err := cm.ourself.CreateConnection(address, acceptNewPeer); err != nil {
log.Errorf("->[%s] error during connection attempt: %v", address, err)
cm.ConnectionAborted(address, err)
}
}
func (t *Target) nextTryNever() {
t.tryAfter = time.Time{}
t.tryInterval = MaxInterval
}
func (t *Target) nextTryNow() {
t.tryAfter = time.Now()
t.tryInterval = InitialInterval
}
// The delay at the nth retry is a random value in the range
// [i-i/2,i+i/2], where i = InitialInterval * 1.5^(n-1).
func (t *Target) nextTryLater() {
t.tryAfter = time.Now().Add(t.tryInterval/2 + time.Duration(rand.Int63n(int64(t.tryInterval))))
t.tryInterval = t.tryInterval * 3 / 2
if t.tryInterval > MaxInterval {
t.tryInterval = MaxInterval
}
}
+170
View File
@@ -0,0 +1,170 @@
package mesh
import (
"fmt"
)
type GossipData interface {
Encode() [][]byte
Merge(GossipData) GossipData
}
type Gossip interface {
// specific message from one peer to another
// intermediate peers relay it using unicast topology.
GossipUnicast(dstPeerName PeerName, msg []byte) error
// send gossip to every peer, relayed using broadcast topology.
GossipBroadcast(update GossipData) error
}
type Gossiper interface {
OnGossipUnicast(sender PeerName, msg []byte) error
// merge received data into state and return a representation of
// the received data, for further propagation
OnGossipBroadcast(sender PeerName, update []byte) (GossipData, error)
// return state of everything we know; gets called periodically
Gossip() GossipData
// merge received data into state and return "everything new I've
// just learnt", or nil if nothing in the received data was new
OnGossip(update []byte) (GossipData, error)
}
// Accumulates GossipData that needs to be sent to one destination,
// and sends it when possible.
type GossipSender struct {
send func(GossipData)
cell chan GossipData
// for testing
sent bool
flushch chan chan bool
}
func NewGossipSender(send func(GossipData)) *GossipSender {
cell := make(chan GossipData, 1)
flushch := make(chan chan bool)
sender := &GossipSender{
send: send,
cell: cell,
flushch: flushch,
}
go sender.run()
return sender
}
func (sender *GossipSender) run() {
for {
select {
case pending := <-sender.cell:
if pending == nil { // receive zero value when chan is closed
return
}
sender.send(pending)
sender.sent = true
case ch := <-sender.flushch:
// send anything pending, then reply back whether we sent
// anything since previous flush
select {
case pending := <-sender.cell:
sender.send(pending)
sender.sent = true
default:
}
ch <- sender.sent
sender.sent = false
}
}
}
func (sender *GossipSender) Send(data GossipData) {
// NB: this must not be invoked concurrently
select {
case pending := <-sender.cell:
sender.cell <- pending.Merge(data)
default:
sender.cell <- data
}
}
func (sender *GossipSender) Stop() {
close(sender.cell)
}
type GossipChannels map[string]*GossipChannel
func (router *Router) NewGossip(channelName string, g Gossiper) Gossip {
channel := NewGossipChannel(channelName, router.Ourself, router.Routes, g)
router.gossipLock.Lock()
defer router.gossipLock.Unlock()
if _, found := router.gossipChannels[channelName]; found {
checkFatal(fmt.Errorf("[gossip] duplicate channel %s", channelName))
}
router.gossipChannels[channelName] = channel
return channel
}
func (router *Router) gossipChannel(channelName string) *GossipChannel {
router.gossipLock.RLock()
channel, found := router.gossipChannels[channelName]
router.gossipLock.RUnlock()
if found {
return channel
}
router.gossipLock.Lock()
defer router.gossipLock.Unlock()
if channel, found = router.gossipChannels[channelName]; found {
return channel
}
channel = NewGossipChannel(channelName, router.Ourself, router.Routes, &surrogateGossiper)
channel.log("created surrogate channel")
router.gossipChannels[channelName] = channel
return channel
}
func (router *Router) gossipChannelSet() map[*GossipChannel]struct{} {
channels := make(map[*GossipChannel]struct{})
router.gossipLock.RLock()
defer router.gossipLock.RUnlock()
for _, channel := range router.gossipChannels {
channels[channel] = void
}
return channels
}
func (router *Router) SendAllGossip() {
for channel := range router.gossipChannelSet() {
if gossip := channel.gossiper.Gossip(); gossip != nil {
channel.Send(router.Ourself.Name, gossip)
}
}
}
func (router *Router) SendAllGossipDown(conn Connection) {
for channel := range router.gossipChannelSet() {
if gossip := channel.gossiper.Gossip(); gossip != nil {
channel.SendDown(conn, gossip)
}
}
}
// for testing
func (router *Router) sendPendingGossip() bool {
sentSomething := false
for channel := range router.gossipChannelSet() {
channel.Lock()
for _, sender := range channel.senders {
sentSomething = sender.flush() || sentSomething
}
for _, sender := range channel.broadcasters {
sentSomething = sender.flush() || sentSomething
}
channel.Unlock()
}
return sentSomething
}
func (sender *GossipSender) flush() bool {
ch := make(chan bool)
sender.flushch <- ch
return <-ch
}
+103 -196
View File
@@ -1,132 +1,42 @@
package router
package mesh
import (
"bytes"
"encoding/gob"
"fmt"
"log"
"sync"
"time"
)
const GossipInterval = 30 * time.Second
type GossipData interface {
Encode() []byte
Merge(GossipData)
}
type Gossip interface {
// specific message from one peer to another
// intermediate peers relay it using unicast topology.
GossipUnicast(dstPeerName PeerName, msg []byte) error
// send gossip to every peer, relayed using broadcast topology.
GossipBroadcast(update GossipData) error
}
type Gossiper interface {
OnGossipUnicast(sender PeerName, msg []byte) error
// merge received data into state and return a representation of
// the received data, for further propagation
OnGossipBroadcast(update []byte) (GossipData, error)
// return state of everything we know; gets called periodically
Gossip() GossipData
// merge received data into state and return "everything new I've
// just learnt", or nil if nothing in the received data was new
OnGossip(update []byte) (GossipData, error)
}
// Accumulates GossipData that needs to be sent to one destination,
// and sends it when possible.
type GossipSender struct {
send func(GossipData)
cell chan GossipData
}
func NewGossipSender(send func(GossipData)) *GossipSender {
return &GossipSender{send: send}
}
func (sender *GossipSender) Start() {
sender.cell = make(chan GossipData, 1)
go sender.run()
}
func (sender *GossipSender) run() {
for {
if pending := <-sender.cell; pending == nil { // receive zero value when chan is closed
break
} else {
sender.send(pending)
}
}
}
func (sender *GossipSender) Send(data GossipData) {
// NB: this must not be invoked concurrently
select {
case pending := <-sender.cell:
pending.Merge(data)
sender.cell <- pending
default:
sender.cell <- data
}
}
func (sender *GossipSender) Stop() {
close(sender.cell)
}
type connectionSenders map[Connection]*GossipSender
type peerSenders map[PeerName]*GossipSender
type GossipChannel struct {
sync.Mutex
name string
ourself *LocalPeer
routes *Routes
name string
hash uint32
gossiper Gossiper
senders connectionSenders
broadcasters peerSenders
}
func (router *Router) NewGossip(channelName string, g Gossiper) Gossip {
channelHash := hash(channelName)
channel := &GossipChannel{
ourself: router.Ourself,
routes: router.Routes,
type connectionSenders map[Connection]*GossipSender
type peerSenders map[PeerName]*GossipSender
func NewGossipChannel(channelName string, ourself *LocalPeer, routes *Routes, g Gossiper) *GossipChannel {
return &GossipChannel{
name: channelName,
hash: channelHash,
ourself: ourself,
routes: routes,
gossiper: g,
senders: make(connectionSenders),
broadcasters: make(peerSenders)}
router.GossipChannels[channelHash] = channel
return channel
}
func (router *Router) SendAllGossip() {
for _, channel := range router.GossipChannels {
channel.Send(router.Ourself.Name, channel.gossiper.Gossip())
}
}
func (router *Router) SendAllGossipDown(conn Connection) {
for _, channel := range router.GossipChannels {
channel.SendDown(conn, channel.gossiper.Gossip())
}
}
func (router *Router) handleGossip(tag ProtocolTag, payload []byte) error {
decoder := gob.NewDecoder(bytes.NewReader(payload))
var channelHash uint32
if err := decoder.Decode(&channelHash); err != nil {
var channelName string
if err := decoder.Decode(&channelName); err != nil {
return err
}
channel, found := router.GossipChannels[channelHash]
if !found {
return fmt.Errorf("[gossip] received unknown channel with hash %v", channelHash)
}
channel := router.gossipChannel(channelName)
var srcName PeerName
if err := decoder.Decode(&srcName); err != nil {
return err
@@ -148,7 +58,12 @@ func (c *GossipChannel) deliverUnicast(srcName PeerName, origPayload []byte, dec
return err
}
if c.ourself.Name != destName {
return c.relayUnicast(destName, origPayload)
if err := c.relayUnicast(destName, origPayload); err != nil {
// just log errors from relayUnicast; a problem between us and destination
// is not enough reason to break the connection from the source
c.log(err)
}
return nil
}
var payload []byte
if err := dec.Decode(&payload); err != nil {
@@ -162,7 +77,7 @@ func (c *GossipChannel) deliverBroadcast(srcName PeerName, _ []byte, dec *gob.De
if err := dec.Decode(&payload); err != nil {
return err
}
data, err := c.gossiper.OnGossipBroadcast(payload)
data, err := c.gossiper.OnGossipBroadcast(srcName, payload)
if err != nil || data == nil {
return err
}
@@ -182,81 +97,23 @@ 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()
// GC - randomly (courtesy of go's map iterator) pick some
// existing entries 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 Peer.ConnectionTo(name)
// below, we have that Peer.Connections() invocation above. 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
}
}
for conn := range selectedConnections {
c.sendDown(conn, data)
}
}
func (c *GossipChannel) SendDown(conn Connection, data GossipData) {
c.Lock()
c.sendDown(conn, data)
c.Unlock()
}
func (c *GossipChannel) sendDown(conn Connection, data GossipData) {
sender, found := c.senders[conn]
if !found {
sender = NewGossipSender(func(pending GossipData) {
protocolMsg := ProtocolMsg{ProtocolGossip, GobEncode(c.hash, c.ourself.Name, pending.Encode())}
conn.(ProtocolSender).SendProtocolMsg(protocolMsg)
})
c.senders[conn] = sender
sender.Start()
}
sender.Send(data)
}
func (c *GossipChannel) GossipUnicast(dstPeerName PeerName, msg []byte) error {
return c.relayUnicast(dstPeerName, GobEncode(c.hash, c.ourself.Name, dstPeerName, msg))
return c.relayUnicast(dstPeerName, GobEncode(c.name, c.ourself.Name, dstPeerName, msg))
}
func (c *GossipChannel) GossipBroadcast(update GossipData) error {
return c.relayBroadcast(c.ourself.Name, update)
}
func (c *GossipChannel) relayUnicast(dstPeerName PeerName, buf []byte) error {
func (c *GossipChannel) relayUnicast(dstPeerName PeerName, buf []byte) (err error) {
if relayPeerName, found := c.routes.UnicastAll(dstPeerName); !found {
c.log("unknown relay destination:", dstPeerName)
err = fmt.Errorf("unknown relay destination: %s", dstPeerName)
} else if conn, found := c.ourself.ConnectionTo(relayPeerName); !found {
c.log("unable to find connection to relay peer", relayPeerName)
err = fmt.Errorf("unable to find connection to relay peer %s", relayPeerName)
} else {
conn.(ProtocolSender).SendProtocolMsg(ProtocolMsg{ProtocolGossipUnicast, buf})
}
return nil
return err
}
func (c *GossipChannel) relayBroadcast(srcName PeerName, update GossipData) error {
@@ -286,7 +143,6 @@ func (c *GossipChannel) relayBroadcast(srcName PeerName, update GossipData) erro
if !found {
broadcaster = NewGossipSender(func(pending GossipData) { c.sendBroadcast(srcName, pending) })
c.broadcasters[srcName] = broadcaster
broadcaster.Start()
}
broadcaster.Send(update)
return nil
@@ -298,40 +154,91 @@ func (c *GossipChannel) sendBroadcast(srcName PeerName, update GossipData) {
if len(nextHops) == 0 {
return
}
protocolMsg := ProtocolMsg{ProtocolGossipBroadcast, GobEncode(c.hash, srcName, update.Encode())}
// FIXME a single blocked connection can stall us
for _, conn := range c.ourself.ConnectionsTo(nextHops) {
conn.(ProtocolSender).SendProtocolMsg(protocolMsg)
connections := c.ourself.ConnectionsTo(nextHops)
for _, msg := range update.Encode() {
protocolMsg := ProtocolMsg{ProtocolGossipBroadcast, GobEncode(c.name, srcName, msg)}
// FIXME a single blocked connection can stall us
for _, conn := range connections {
conn.(ProtocolSender).SendProtocolMsg(protocolMsg)
}
}
}
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...)...)
}
// for testing
// FIXME this doesn't actually guarantee everything has been sent
// since a GossipSender may be in the process of sending and there is
// no easy way for us to know when that has completed.
func (router *Router) sendPendingGossip() {
for _, channel := range router.GossipChannels {
for _, sender := range channel.senders {
sender.flush()
}
for _, sender := range channel.broadcasters {
sender.flush()
}
}
}
func (sender *GossipSender) flush() {
for {
select {
case pending := <-sender.cell:
sender.send(pending)
default:
return
}
func GobEncode(items ...interface{}) []byte {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
for _, i := range items {
checkFatal(enc.Encode(i))
}
return buf.Bytes()
}
+230
View File
@@ -0,0 +1,230 @@
package mesh
import (
"fmt"
"sync"
"testing"
"github.com/stretchr/testify/require"
)
// TODO test gossip unicast; atm we only test topology gossip and
// surrogates, neither of which employ unicast.
type mockGossipConnection struct {
RemoteConnection
dest *Router
start chan struct{}
}
func NewTestRouter(name string) *Router {
peerName, _ := PeerNameFromString(name)
router := NewRouter(Config{}, peerName, "nick", nil)
router.Start()
return router
}
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; {
sentSomething = false
for _, router := range routers {
sentSomething = router.sendPendingGossip() || sentSomething
}
}
}
func AddTestGossipConnection(r1, r2 *Router) {
c1 := r1.NewTestGossipConnection(r2)
c2 := r2.NewTestGossipConnection(r1)
c1.Start()
c2.Start()
}
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 := &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) DeleteTestGossipConnection(r *Router) {
toName := r.Ourself.Peer.Name
conn, _ := router.Ourself.ConnectionTo(toName)
router.Peers.Dereference(conn.Remote())
router.Ourself.handleDeleteConnection(conn)
}
// Create a Peer representing the receiver router, with connections to
// the routers supplied as arguments, carrying across all UID and
// version information.
func (router *Router) tp(routers ...*Router) *Peer {
peer := NewPeerFrom(router.Ourself.Peer)
connections := make(map[PeerName]Connection)
for _, r := range routers {
p := NewPeerFrom(r.Ourself.Peer)
connections[r.Ourself.Peer.Name] = newMockConnection(peer, p)
}
peer.Version = router.Ourself.Peer.Version
peer.connections = connections
return peer
}
// Check that the topology of router matches the peers and all of their connections
func checkTopology(t *testing.T, router *Router, wantedPeers ...*Peer) {
router.Peers.RLock()
checkTopologyPeers(t, true, router.Peers.allPeers(), wantedPeers...)
router.Peers.RUnlock()
}
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
AddTestGossipConnection(r1, r2)
sendPendingGossip(r1, r2)
checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
checkTopology(t, r2, r1.tp(r2), r2.tp(r1))
AddTestGossipConnection(r2, r3)
flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))
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.DeleteTestGossipConnection(r3)
flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))
// Drop the connection from 1 to 3
r1.DeleteTestGossipConnection(r3)
sendPendingGossip(r1, r2, r3)
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, r2))
}
func TestGossipSurrogate(t *testing.T) {
// create the topology r1 <-> r2 <-> r3
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}
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()
g3 := newTestGossiper()
s1 := r1.NewGossip("Test", g1)
s3 := r3.NewGossip("Test", g3)
// broadcast a message from each end, check it reaches the other
broadcast(s1, 1)
broadcast(s3, 2)
sendPendingGossip(r1, r2, r3)
g1.checkHas(t, 2)
g3.checkHas(t, 1)
// check that each end gets their message back through periodic
// gossip
r1.SendAllGossip()
r3.SendAllGossip()
sendPendingGossip(r1, r2, r3)
g1.checkHas(t, 1, 2)
g3.checkHas(t, 1, 2)
}
type testGossiper struct {
sync.RWMutex
state map[byte]struct{}
}
func newTestGossiper() *testGossiper {
return &testGossiper{state: make(map[byte]struct{})}
}
func (g *testGossiper) OnGossipUnicast(sender PeerName, msg []byte) error {
return nil
}
func (g *testGossiper) OnGossipBroadcast(_ PeerName, update []byte) (GossipData, error) {
g.Lock()
defer g.Unlock()
for _, v := range update {
g.state[v] = void
}
return NewSurrogateGossipData(update), nil
}
func (g *testGossiper) Gossip() GossipData {
g.RLock()
defer g.RUnlock()
state := make([]byte, len(g.state))
for v := range g.state {
state = append(state, v)
}
return NewSurrogateGossipData(state)
}
func (g *testGossiper) OnGossip(update []byte) (GossipData, error) {
g.Lock()
defer g.Unlock()
var delta []byte
for _, v := range update {
if _, found := g.state[v]; !found {
delta = append(delta, v)
g.state[v] = void
}
}
if len(delta) == 0 {
return nil, nil
}
return NewSurrogateGossipData(delta), nil
}
func (g *testGossiper) checkHas(t *testing.T, vs ...byte) {
g.RLock()
defer g.RUnlock()
for _, v := range vs {
if _, found := g.state[v]; !found {
require.FailNow(t, fmt.Sprintf("%d is missing", v))
}
}
}
func broadcast(s Gossip, v byte) {
s.GossipBroadcast(NewSurrogateGossipData([]byte{v}))
}
+86 -67
View File
@@ -1,13 +1,14 @@
package router
package mesh
import (
"fmt"
"log"
"net"
"sync"
"time"
)
type LocalPeer struct {
sync.RWMutex
*Peer
router *Router
actionChan chan<- LocalPeerAction
@@ -16,63 +17,31 @@ type LocalPeer struct {
type LocalPeerAction func()
func NewLocalPeer(name PeerName, nickName string, router *Router) *LocalPeer {
return &LocalPeer{Peer: NewPeer(name, nickName, 0, 0), router: router}
}
func (peer *LocalPeer) Start() {
actionChan := make(chan LocalPeerAction, ChannelSize)
peer.actionChan = actionChan
peer := &LocalPeer{
Peer: NewPeer(name, nickName, randomPeerUID(), 0, randomPeerShortID()),
router: router,
actionChan: actionChan,
}
go peer.actorLoop(actionChan)
return peer
}
func (peer *LocalPeer) Forward(dstPeer *Peer, df bool, frame []byte, dec *EthernetDecoder) error {
return peer.Relay(peer.Peer, dstPeer, df, frame, dec)
func (peer *LocalPeer) Connections() ConnectionSet {
connections := make(ConnectionSet)
peer.RLock()
defer peer.RUnlock()
for _, conn := range peer.connections {
connections[conn] = void
}
return connections
}
func (peer *LocalPeer) Broadcast(df bool, frame []byte, dec *EthernetDecoder) {
peer.RelayBroadcast(peer.Peer, df, frame, dec)
}
func (peer *LocalPeer) Relay(srcPeer, dstPeer *Peer, df bool, frame []byte, dec *EthernetDecoder) error {
relayPeerName, found := peer.router.Routes.Unicast(dstPeer.Name)
if !found {
// Not necessarily an error as there could be a race with the
// dst disappearing whilst the frame is in flight
log.Println("Received packet for unknown destination:", dstPeer.FullName())
return nil
}
conn, found := peer.ConnectionTo(relayPeerName)
if !found {
// Again, could just be a race, not necessarily an error
log.Println("Unable to find connection to relay peer", relayPeerName)
return nil
}
return conn.(*LocalConnection).Forward(df, &ForwardedFrame{
srcPeer: srcPeer,
dstPeer: dstPeer,
frame: frame},
dec)
}
func (peer *LocalPeer) RelayBroadcast(srcPeer *Peer, df bool, frame []byte, dec *EthernetDecoder) {
nextHops := peer.router.Routes.Broadcast(srcPeer.Name)
if len(nextHops) == 0 {
return
}
for _, conn := range peer.ConnectionsTo(nextHops) {
err := conn.(*LocalConnection).Forward(df, &ForwardedFrame{
srcPeer: srcPeer,
dstPeer: conn.Remote(),
frame: frame},
dec)
if err != nil {
if ftbe, ok := err.(FrameTooBigError); ok {
log.Printf("dropping too big DF broadcast frame (%v -> %v): PMTU= %v\n", dec.ip.DstIP, dec.ip.SrcIP, ftbe.EPMTU)
} else {
log.Println(err)
}
}
}
func (peer *LocalPeer) ConnectionTo(name PeerName) (Connection, bool) {
peer.RLock()
defer peer.RUnlock()
conn, found := peer.connections[name]
return conn, found // yes, you really can't inline that. FFS.
}
func (peer *LocalPeer) ConnectionsTo(names []PeerName) []Connection {
@@ -93,22 +62,16 @@ func (peer *LocalPeer) CreateConnection(peerAddr string, acceptNewPeer bool) err
if err := peer.checkConnectionLimit(); err != nil {
return err
}
tcpAddr, tcpErr := net.ResolveTCPAddr("tcp4", peerAddr)
udpAddr, udpErr := net.ResolveUDPAddr("udp4", peerAddr)
if tcpErr != nil || udpErr != nil {
// they really should have the same value, but just in case...
if tcpErr == nil {
return udpErr
}
return tcpErr
tcpAddr, err := net.ResolveTCPAddr("tcp4", peerAddr)
if err != nil {
return err
}
tcpConn, err := net.DialTCP("tcp4", nil, tcpAddr)
if err != nil {
return err
}
connRemote := NewRemoteConnection(peer.Peer, nil, tcpConn.RemoteAddr().String(), true, false)
connLocal := NewLocalConnection(connRemote, tcpConn, udpAddr, peer.router)
connLocal.Start(acceptNewPeer)
StartLocalConnection(connRemote, tcpConn, peer.router, acceptNewPeer)
return nil
}
@@ -162,7 +125,7 @@ func (peer *LocalPeer) handleAddConnection(conn Connection) error {
log.Fatal("Attempt made to add connection to peer with unknown remote peer")
}
toName := conn.Remote().Name
dupErr := fmt.Errorf("Multiple connections to %s added to %s", conn.Remote().FullName(), peer.FullName())
dupErr := fmt.Errorf("Multiple connections to %s added to %s", conn.Remote(), peer.String())
// deliberately non symmetrical
if dupConn, found := peer.connections[toName]; found {
if dupConn == conn {
@@ -192,7 +155,10 @@ func (peer *LocalPeer) handleAddConnection(conn Connection) error {
conn.Log("connection added (new peer)")
peer.router.SendAllGossipDown(conn)
}
peer.router.Routes.Recalculate()
peer.broadcastPeerUpdate(conn.Remote())
return nil
}
@@ -206,6 +172,8 @@ func (peer *LocalPeer) handleConnectionEstablished(conn Connection) {
}
peer.connectionEstablished(conn)
conn.Log("connection fully established")
peer.router.Routes.Recalculate()
peer.broadcastPeerUpdate()
}
@@ -225,18 +193,69 @@ func (peer *LocalPeer) handleDeleteConnection(conn Connection) {
// Must do garbage collection first to ensure we don't send out an
// update with unreachable peers (can cause looping)
peer.router.Peers.GarbageCollect()
peer.router.Routes.Recalculate()
peer.broadcastPeerUpdate()
}
// helpers
func (peer *LocalPeer) broadcastPeerUpdate(peers ...*Peer) {
peer.router.Routes.Recalculate()
peer.router.TopologyGossip.GossipBroadcast(NewTopologyGossipData(peer.router.Peers, append(peers, peer.Peer)...))
// Some tests run without a router. This should be fixed so
// that the relevant part of Router can be easily run in the
// context of a test, but that will involve significant
// reworking of tests.
if peer.router != nil {
peer.router.BroadcastTopologyUpdate(append(peers, peer.Peer))
}
}
func (peer *LocalPeer) checkConnectionLimit() error {
limit := peer.router.ConnLimit
if 0 != limit && peer.ConnectionCount() >= limit {
if 0 != limit && peer.connectionCount() >= limit {
return fmt.Errorf("Connection limit reached (%v)", limit)
}
return nil
}
func (peer *LocalPeer) addConnection(conn Connection) {
peer.Lock()
defer peer.Unlock()
peer.connections[conn.Remote().Name] = conn
peer.Version++
}
func (peer *LocalPeer) deleteConnection(conn Connection) {
peer.Lock()
defer peer.Unlock()
delete(peer.connections, conn.Remote().Name)
peer.Version++
}
func (peer *LocalPeer) connectionEstablished(conn Connection) {
peer.Lock()
defer peer.Unlock()
peer.Version++
}
func (peer *LocalPeer) connectionCount() int {
peer.RLock()
defer peer.RUnlock()
return len(peer.connections)
}
func (peer *LocalPeer) setShortID(shortID PeerShortID) {
peer.Lock()
defer peer.Unlock()
peer.ShortID = shortID
peer.Version++
}
func (peer *LocalPeer) setVersionBeyond(version uint64) bool {
peer.Lock()
defer peer.Unlock()
if version >= peer.Version {
peer.Version = version + 1
return true
}
return false
}
+18 -16
View File
@@ -3,37 +3,39 @@
// It supplies some mock implementations to other unit tests, and is
// named "...test.go" so it is only compiled under `go test`.
package router
package mesh
import (
wt "github.com/weaveworks/weave/testing"
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
// Add to peers a connection from peers.ourself to p
func (peers *Peers) AddTestConnection(p *Peer) {
toPeer := NewPeer(p.Name, "", p.UID, 0)
peers.FetchWithDefault(toPeer) // Has side-effect of incrementing refcount
conn := newMockConnection(peers.ourself, toPeer)
summary := p.PeerSummary
summary.Version = 0
toPeer := NewPeerFromSummary(summary)
toPeer = peers.FetchWithDefault(toPeer) // Has side-effect of incrementing refcount
conn := newMockConnection(peers.ourself.Peer, toPeer)
peers.ourself.addConnection(conn)
peers.ourself.connectionEstablished(conn)
}
// Add to peers a connection from p1 to p2
func (peers *Peers) AddTestRemoteConnection(p1, p2 *Peer) {
fromName := p1.Name
fromPeer := NewPeer(fromName, "", p1.UID, 0)
fromPeer := NewPeerFrom(p1)
fromPeer = peers.FetchWithDefault(fromPeer)
toName := p2.Name
toPeer := NewPeer(toName, "", p2.UID, 0)
toPeer := NewPeerFrom(p2)
toPeer = peers.FetchWithDefault(toPeer)
peers.ourself.addConnection(&RemoteConnection{fromPeer, toPeer, "", false, false})
}
func (peers *Peers) DeleteTestConnection(p *Peer) {
toName := p.Name
toPeer, _ := peers.Fetch(toName)
toPeer.DecrementLocalRefCount()
toPeer := peers.Fetch(toName)
peers.Dereference(toPeer)
conn, _ := peers.ourself.ConnectionTo(toName)
peers.ourself.deleteConnection(conn)
}
@@ -57,18 +59,18 @@ func checkEqualConns(t *testing.T, ourName PeerName, got, wanted map[PeerName]Co
if _, found := checkConns[remoteName]; found {
delete(checkConns, remoteName)
} else {
wt.Fatalf(t, "Unexpected connection from %s to %s", ourName, remoteName)
require.FailNow(t, fmt.Sprintf("Unexpected connection from %s to %s", ourName, remoteName))
}
}
if len(checkConns) > 0 {
wt.Fatalf(t, "Expected connections not found: from %s to %v", ourName, checkConns)
require.FailNow(t, fmt.Sprintf("Expected connections not found: from %s to %v", ourName, checkConns))
}
}
// Get all the peers from a Peers in a slice
func (peers *Peers) allPeers() []*Peer {
var res []*Peer
for _, peer := range peers.table {
for _, peer := range peers.byName {
res = append(res, peer)
}
return res
@@ -104,10 +106,10 @@ func checkTopologyPeers(t *testing.T, checkConns bool, peers []*Peer, wantedPeer
}
delete(check, name)
} else {
wt.Fatalf(t, "Unexpected peer: %s", name)
require.FailNow(t, fmt.Sprintf("Unexpected peer: %s", name))
}
}
if len(check) > 0 {
wt.Fatalf(t, "Expected peers not found: %v", check)
require.FailNow(t, fmt.Sprintf("Expected peers not found: %v", check))
}
}
+116
View File
@@ -0,0 +1,116 @@
package mesh
import (
"net"
)
type Overlay interface {
// Enhance a features map with overlay-related features
AddFeaturesTo(map[string]string)
// Prepare on overlay connection. The connection should remain
// passive until it has been Confirm()ed.
PrepareConnection(OverlayConnectionParams) (OverlayConnection, error)
// Obtain diagnostic information specific to the overlay
Diagnostics() interface{}
}
type OverlayConnectionParams struct {
RemotePeer *Peer
// The local address of the corresponding TCP connection. Used to
// derive the local IP address for sending. May differ for
// different overlay connections.
LocalAddr *net.TCPAddr
// The remote address of the corresponding TCP connection. Used to
// determine the address to send to, but only if the TCP
// connection is outbound. Otherwise the Overlay needs to discover
// it (e.g. from incoming datagrams).
RemoteAddr *net.TCPAddr
// Is the corresponding TCP connection outbound?
Outbound bool
// Unique identifier for this connection
ConnUID uint64
// Session key, if connection is encrypted; nil otherwise.
//
// NB: overlay connections must take care not to use nonces which
// may collide with those of the main connection. These nonces are
// 192 bits, with the top most bit unspecified, the next bit set
// to 1, followed by 126 zero bits, and a message sequence number
// in the lowest 64 bits.
SessionKey *[32]byte
// Function to send a control message to the counterpart
// overlay connection.
SendControlMessage func(tag byte, msg []byte) error
// Features passed at connection initiation
Features map[string]string
}
// All of the machinery to manage overlay connectivity to a particular
// peer
type OverlayConnection interface {
// Confirm that the connection is really wanted, and so the
// Overlay should begin heartbeats etc. to verify the operation of
// the overlay connection.
Confirm()
// A channel indicating that the overlay connection is
// established, i.e. its operation has been confirmed.
EstablishedChannel() <-chan struct{}
// A channel indicating an error from the overlay connection. The
// overlay connection is not expected to be operational after the
// first error, so the channel only needs to buffer a single
// error.
ErrorChannel() <-chan error
Stop()
// Handle a message from the peer. 'tag' exists for
// compatibility, and should always be
// ProtocolOverlayControlMessage for non-sleeve overlays.
ControlMessage(tag byte, msg []byte)
// User facing overlay name
DisplayName() string
}
type NullOverlay struct{}
func (NullOverlay) AddFeaturesTo(map[string]string) {
}
func (NullOverlay) PrepareConnection(OverlayConnectionParams) (OverlayConnection, error) {
return NullOverlay{}, nil
}
func (NullOverlay) Diagnostics() interface{} {
return nil
}
func (NullOverlay) Confirm() {
}
func (NullOverlay) EstablishedChannel() <-chan struct{} {
return nil
}
func (NullOverlay) ErrorChannel() <-chan error {
return nil
}
func (NullOverlay) Stop() {
}
func (NullOverlay) ControlMessage(byte, []byte) {
}
func (NullOverlay) DisplayName() string {
return "null"
}
+182
View File
@@ -0,0 +1,182 @@
package mesh
import (
"crypto/rand"
"encoding/binary"
"fmt"
"sort"
"strconv"
)
func randBytes(n int) []byte {
buf := make([]byte, n)
_, err := rand.Read(buf)
checkFatal(err)
return buf
}
func randUint64() (r uint64) {
return binary.LittleEndian.Uint64(randBytes(8))
}
func randUint16() (r uint16) {
return binary.LittleEndian.Uint16(randBytes(2))
}
type PeerUID uint64
func randomPeerUID() PeerUID {
for {
uid := randUint64()
if uid != 0 { // uid 0 is reserved for peer placeholder
return PeerUID(uid)
}
}
}
func ParsePeerUID(s string) (PeerUID, error) {
uid, err := strconv.ParseUint(s, 10, 64)
return PeerUID(uid), err
}
// Short IDs exist for the sake of fast datapath. They are 12 bits,
// randomly assigned, but we detect and recover from collisions. This
// does limit us to 4096 peers, but that should be sufficient for a
// while.
type PeerShortID uint16
const PeerShortIDBits = 12
func randomPeerShortID() PeerShortID {
return PeerShortID(randUint16() & (1<<PeerShortIDBits - 1))
}
type PeerSummary struct {
NameByte []byte
NickName string
UID PeerUID
Version uint64
ShortID PeerShortID
HasShortID bool
}
type Peer struct {
Name PeerName
PeerSummary
localRefCount uint64 // maintained by Peers
connections map[PeerName]Connection
}
type ListOfPeers []*Peer
func (lop ListOfPeers) Len() int {
return len(lop)
}
func (lop ListOfPeers) Swap(i, j int) {
lop[i], lop[j] = lop[j], lop[i]
}
func (lop ListOfPeers) Less(i, j int) bool {
return lop[i].Name < lop[j].Name
}
type ConnectionSet map[Connection]struct{}
func NewPeerFromSummary(summary PeerSummary) *Peer {
return &Peer{
Name: PeerNameFromBin(summary.NameByte),
PeerSummary: summary,
connections: make(map[PeerName]Connection),
}
}
func NewPeer(name PeerName, nickName string, uid PeerUID, version uint64, shortID PeerShortID) *Peer {
return NewPeerFromSummary(PeerSummary{
NameByte: name.Bin(),
NickName: nickName,
UID: uid,
Version: version,
ShortID: shortID,
HasShortID: true,
})
}
func NewPeerPlaceholder(name PeerName) *Peer {
return NewPeerFromSummary(PeerSummary{NameByte: name.Bin()})
}
func NewPeerFrom(peer *Peer) *Peer {
return NewPeerFromSummary(peer.PeerSummary)
}
func (peer *Peer) String() string {
return fmt.Sprint(peer.Name, "(", peer.NickName, ")")
}
// Calculate the routing table from this peer to all peers reachable
// from it, returning a "next hop" map of PeerNameX -> PeerNameY,
// which says "in order to send a message to X, the peer should send
// the message to its neighbour Y".
//
// Because currently we do not have weightings on the connections
// between peers, there is no need to use a minimum spanning tree
// algorithm. Instead we employ the simpler and cheaper breadth-first
// widening. The computation is deterministic, which ensures that when
// it is performed on the same data by different peers, they get the
// same result. This is important since otherwise we risk message loss
// or routing cycles.
//
// When the 'establishedAndSymmetric' flag is set, only connections
// that are marked as 'established' and are symmetric (i.e. where both
// sides indicate they have a connection to the other) are considered.
//
// When a non-nil stopAt peer is supplied, the widening stops when it
// reaches that peer. The boolean return indicates whether that has
// happened.
//
// NB: This function should generally be invoked while holding a read
// lock on Peers and LocalPeer.
func (peer *Peer) Routes(stopAt *Peer, establishedAndSymmetric bool) (bool, map[PeerName]PeerName) {
routes := make(unicastRoutes)
routes[peer.Name] = UnknownPeerName
nextWorklist := []*Peer{peer}
for len(nextWorklist) > 0 {
worklist := nextWorklist
sort.Sort(ListOfPeers(worklist))
nextWorklist = []*Peer{}
for _, curPeer := range worklist {
if curPeer == stopAt {
return true, routes
}
curPeer.ForEachConnectedPeer(establishedAndSymmetric, routes,
func(remotePeer *Peer) {
nextWorklist = append(nextWorklist, remotePeer)
remoteName := remotePeer.Name
// We now know how to get to remoteName: the same
// way we get to curPeer. Except, if curPeer is
// the starting peer in which case we know we can
// reach remoteName directly.
if curPeer == peer {
routes[remoteName] = remoteName
} else {
routes[remoteName] = routes[curPeer.Name]
}
})
}
}
return false, routes
}
func (peer *Peer) ForEachConnectedPeer(establishedAndSymmetric bool, exclude map[PeerName]PeerName, f func(*Peer)) {
for remoteName, conn := range peer.connections {
if establishedAndSymmetric && !conn.Established() {
continue
}
if _, found := exclude[remoteName]; found {
continue
}
remotePeer := conn.Remote()
if remoteConn, found := remotePeer.connections[peer.Name]; !establishedAndSymmetric || (found && remoteConn.Established()) {
f(remotePeer)
}
}
}
@@ -2,7 +2,8 @@
// Let peer names be sha256 hashes...of anything (as long as it's
// unique).
package router
package mesh
import (
"crypto/sha256"
@@ -14,7 +14,8 @@
// Note that we do not mandate *what* MAC should be used as the peer
// name. In particular it doesn't actually have to be the MAC of, say,
// the network interface the peer is sniffing on.
package router
package mesh
import (
"net"
@@ -55,3 +56,20 @@ func (name PeerName) Bin() []byte {
func (name PeerName) String() string {
return intmac(uint64(name)).String()
}
func macint(mac net.HardwareAddr) (r uint64) {
for _, b := range mac {
r <<= 8
r |= uint64(b)
}
return
}
func intmac(key uint64) (r net.HardwareAddr) {
r = make([]byte, 6)
for i := 5; i >= 0; i-- {
r[i] = byte(key)
key >>= 8
}
return
}
+530
View File
@@ -0,0 +1,530 @@
package mesh
import (
"bytes"
"encoding/gob"
"io"
"math/rand"
"sync"
)
var void = struct{}{}
type Peers struct {
sync.RWMutex
ourself *LocalPeer
byName map[PeerName]*Peer
byShortID map[PeerShortID]ShortIDPeers
onGC []func(*Peer)
// Called when the mapping from short ids to peers changes
onInvalidateShortIDs []func()
}
type ShortIDPeers struct {
// If we know about a single peer with the short id, this is
// that peer. If there is a collision, this is the peer with
// the lowest Name.
peer *Peer
// In case of a collision, this holds the other peers.
others []*Peer
}
type PeerNameSet map[PeerName]struct{}
type ConnectionSummary struct {
NameByte []byte
RemoteTCPAddr string
Outbound bool
Established bool
}
// Pending notifications due to changes to Peers that need to be sent
// out once the Peers is unlocked.
type PeersPendingNotifications struct {
// Peers that have been GCed
removed []*Peer
// The mapping from shorts ids to peers changed
invalidateShortIDs bool
// The local short ID needs reassigning due to a collision
reassignLocalShortID bool
// The local peer was modified
localPeerModified bool
}
func NewPeers(ourself *LocalPeer) *Peers {
peers := &Peers{
ourself: ourself,
byName: make(map[PeerName]*Peer),
byShortID: make(map[PeerShortID]ShortIDPeers),
}
peers.FetchWithDefault(ourself.Peer)
return peers
}
func (peers *Peers) OnGC(callback func(*Peer)) {
peers.Lock()
defer peers.Unlock()
// Although the array underlying peers.onGC might be accessed
// without holding the lock in unlockAndNotify, we don't
// support removing callbacks, so a simple append here is
// safe.
peers.onGC = append(peers.onGC, callback)
}
func (peers *Peers) OnInvalidateShortIDs(callback func()) {
peers.Lock()
defer peers.Unlock()
// Safe, as in OnGC
peers.onInvalidateShortIDs = append(peers.onInvalidateShortIDs, callback)
}
func (peers *Peers) unlockAndNotify(pending *PeersPendingNotifications) {
broadcastLocalPeer := (pending.reassignLocalShortID && peers.reassignLocalShortID(pending)) ||
pending.localPeerModified
onGC := peers.onGC
onInvalidateShortIDs := peers.onInvalidateShortIDs
peers.Unlock()
if pending.removed != nil {
for _, callback := range onGC {
for _, peer := range pending.removed {
callback(peer)
}
}
}
if pending.invalidateShortIDs {
for _, callback := range onInvalidateShortIDs {
callback()
}
}
if broadcastLocalPeer {
peers.ourself.broadcastPeerUpdate()
}
}
func (peers *Peers) addByShortID(peer *Peer, pending *PeersPendingNotifications) {
if !peer.HasShortID {
return
}
entry, ok := peers.byShortID[peer.ShortID]
if !ok {
entry = ShortIDPeers{peer: peer}
} else if entry.peer == nil {
// This short ID is free, but was used in the past.
// Because we are reusing it, it's an invalidation
// event.
entry.peer = peer
pending.invalidateShortIDs = true
} else if peer.Name < entry.peer.Name {
// Short ID collision, this peer becomes the principal
// peer for the short ID, bumping the previous one
// into others.
if entry.peer == peers.ourself.Peer {
// The bumped peer is peers.ourself, so we
// need to look for a new short id
pending.reassignLocalShortID = true
}
entry.others = append(entry.others, entry.peer)
entry.peer = peer
pending.invalidateShortIDs = true
} else {
// Short ID collision, this peer is secondary
entry.others = append(entry.others, peer)
}
peers.byShortID[peer.ShortID] = entry
}
func (peers *Peers) deleteByShortID(peer *Peer, pending *PeersPendingNotifications) {
if !peer.HasShortID {
return
}
entry := peers.byShortID[peer.ShortID]
var otherIndex int
if peer != entry.peer {
// peer is secondary, find its index in others
otherIndex = -1
for i, other := range entry.others {
if peer == other {
otherIndex = i
break
}
}
if otherIndex < 0 {
return
}
} else if len(entry.others) != 0 {
// need to find the peer with the lowest name to
// become the new principal one
otherIndex = 0
minName := entry.others[0].Name
for i := 1; i < len(entry.others); i++ {
otherName := entry.others[i].Name
if otherName < minName {
minName = otherName
otherIndex = i
}
}
entry.peer = entry.others[otherIndex]
pending.invalidateShortIDs = true
} else {
// This is the last peer with the short id. We clear
// the entry, don't delete it, in order to detect when
// it gets re-used.
peers.byShortID[peer.ShortID] = ShortIDPeers{}
return
}
entry.others[otherIndex] = entry.others[len(entry.others)-1]
entry.others = entry.others[:len(entry.others)-1]
peers.byShortID[peer.ShortID] = entry
}
func (peers *Peers) reassignLocalShortID(pending *PeersPendingNotifications) bool {
newShortID, ok := peers.chooseShortID()
if ok {
peers.setLocalShortID(newShortID, pending)
return true
}
// Otherwise we'll try again later on in garbageColleect
return false
}
func (peers *Peers) setLocalShortID(newShortID PeerShortID, pending *PeersPendingNotifications) {
peers.deleteByShortID(peers.ourself.Peer, pending)
peers.ourself.setShortID(newShortID)
peers.addByShortID(peers.ourself.Peer, pending)
}
// Choose an available short id at random
func (peers *Peers) chooseShortID() (PeerShortID, bool) {
rng := rand.New(rand.NewSource(int64(randUint64())))
// First, just try picking some short ids at random, and
// seeing if they are available:
for i := 0; i < 10; i++ {
shortID := PeerShortID(rng.Intn(1 << PeerShortIDBits))
if peers.byShortID[shortID].peer == nil {
return shortID, true
}
}
// Looks like most short ids are used. So count the number of
// unused ones, and pick one at random.
available := int(1 << PeerShortIDBits)
for _, entry := range peers.byShortID {
if entry.peer != nil {
available--
}
}
if available == 0 {
// All short ids are used.
return 0, false
}
n := rng.Intn(available)
var i PeerShortID
for {
if peers.byShortID[i].peer == nil {
if n == 0 {
return PeerShortID(i), true
}
n--
}
i++
}
}
func (peers *Peers) FetchWithDefault(peer *Peer) *Peer {
peers.Lock()
var pending PeersPendingNotifications
defer peers.unlockAndNotify(&pending)
if existingPeer, found := peers.byName[peer.Name]; found {
existingPeer.localRefCount++
return existingPeer
}
peers.byName[peer.Name] = peer
peers.addByShortID(peer, &pending)
peer.localRefCount++
return peer
}
func (peers *Peers) Fetch(name PeerName) *Peer {
peers.RLock()
defer peers.RUnlock()
return peers.byName[name]
}
func (peers *Peers) FetchAndAddRef(name PeerName) *Peer {
peers.Lock()
defer peers.Unlock()
peer := peers.byName[name]
if peer != nil {
peer.localRefCount++
}
return peer
}
func (peers *Peers) FetchByShortID(shortID PeerShortID) *Peer {
peers.RLock()
defer peers.RUnlock()
return peers.byShortID[shortID].peer
}
func (peers *Peers) Dereference(peer *Peer) {
peers.Lock()
defer peers.Unlock()
peer.localRefCount--
}
func (peers *Peers) ForEach(fun func(*Peer)) {
peers.RLock()
defer peers.RUnlock()
for _, peer := range peers.byName {
fun(peer)
}
}
// Merge an incoming update with our own topology.
//
// We add peers hitherto unknown to us, and update peers for which the
// update contains a more recent version than known to us. The return
// value is a) a representation of the received update, and b) an
// "improved" update containing just these new/updated elements.
func (peers *Peers) ApplyUpdate(update []byte) (PeerNameSet, PeerNameSet, error) {
peers.Lock()
var pending PeersPendingNotifications
defer peers.unlockAndNotify(&pending)
newPeers, decodedUpdate, decodedConns, err := peers.decodeUpdate(update)
if err != nil {
return nil, nil, err
}
// Add new peers
for name, newPeer := range newPeers {
peers.byName[name] = newPeer
peers.addByShortID(newPeer, &pending)
}
// Now apply the updates
newUpdate := peers.applyUpdate(decodedUpdate, decodedConns, &pending)
peers.garbageCollect(&pending)
for _, peerRemoved := range pending.removed {
delete(newUpdate, peerRemoved.Name)
}
updateNames := make(PeerNameSet)
for _, peer := range decodedUpdate {
updateNames[peer.Name] = void
}
return updateNames, newUpdate, nil
}
func (peers *Peers) Names() PeerNameSet {
peers.RLock()
defer peers.RUnlock()
names := make(PeerNameSet)
for name := range peers.byName {
names[name] = void
}
return names
}
func (peers *Peers) EncodePeers(names PeerNameSet) []byte {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
peers.RLock()
defer peers.RUnlock()
for name := range names {
if peer, found := peers.byName[name]; found {
if peer == peers.ourself.Peer {
peers.ourself.Encode(enc)
} else {
peer.Encode(enc)
}
}
}
return buf.Bytes()
}
func (peers *Peers) GarbageCollect() {
peers.Lock()
var pending PeersPendingNotifications
defer peers.unlockAndNotify(&pending)
peers.garbageCollect(&pending)
}
func (peers *Peers) garbageCollect(pending *PeersPendingNotifications) {
peers.ourself.RLock()
_, reached := peers.ourself.Routes(nil, false)
peers.ourself.RUnlock()
for name, peer := range peers.byName {
if _, found := reached[peer.Name]; !found && peer.localRefCount == 0 {
delete(peers.byName, name)
peers.deleteByShortID(peer, pending)
pending.removed = append(pending.removed, peer)
}
}
if peers.byShortID[peers.ourself.ShortID].peer != peers.ourself.Peer {
// The local peer doesn't own its short id. Garbage
// collection might have freed some up, so try to
// reassign.
pending.reassignLocalShortID = true
}
}
func (peers *Peers) decodeUpdate(update []byte) (newPeers map[PeerName]*Peer, decodedUpdate []*Peer, decodedConns [][]ConnectionSummary, err error) {
newPeers = make(map[PeerName]*Peer)
decodedUpdate = []*Peer{}
decodedConns = [][]ConnectionSummary{}
decoder := gob.NewDecoder(bytes.NewReader(update))
for {
peerSummary, connSummaries, decErr := decodePeer(decoder)
if decErr == io.EOF {
break
} else if decErr != nil {
err = decErr
return
}
newPeer := NewPeerFromSummary(peerSummary)
decodedUpdate = append(decodedUpdate, newPeer)
decodedConns = append(decodedConns, connSummaries)
if _, found := peers.byName[newPeer.Name]; !found {
newPeers[newPeer.Name] = newPeer
}
}
for _, connSummaries := range decodedConns {
for _, connSummary := range connSummaries {
remoteName := PeerNameFromBin(connSummary.NameByte)
if _, found := newPeers[remoteName]; found {
continue
}
if _, found := peers.byName[remoteName]; found {
continue
}
// Update refers to a peer which we have no knowledge of.
newPeers[remoteName] = NewPeerPlaceholder(remoteName)
}
}
return
}
func (peers *Peers) applyUpdate(decodedUpdate []*Peer, decodedConns [][]ConnectionSummary, pending *PeersPendingNotifications) PeerNameSet {
newUpdate := make(PeerNameSet)
for idx, newPeer := range decodedUpdate {
connSummaries := decodedConns[idx]
name := newPeer.Name
// guaranteed to find peer in the peers.byName
switch peer := peers.byName[name]; peer {
case peers.ourself.Peer:
if newPeer.UID != peer.UID {
// The update contains information about an old
// incarnation of ourselves. We increase our version
// number beyond that which we received, so our
// information supersedes the old one when it is
// received by other peers.
pending.localPeerModified = peers.ourself.setVersionBeyond(newPeer.Version)
}
case newPeer:
peer.connections = makeConnsMap(peer, connSummaries, peers.byName)
newUpdate[name] = void
default: // existing peer
if newPeer.Version < peer.Version ||
(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 || newPeer.HasShortID != peer.HasShortID {
peers.deleteByShortID(peer, pending)
peer.ShortID = newPeer.ShortID
peer.HasShortID = newPeer.HasShortID
peers.addByShortID(peer, pending)
}
newUpdate[name] = void
}
}
return newUpdate
}
func (peer *Peer) Encode(enc *gob.Encoder) {
checkFatal(enc.Encode(peer.PeerSummary))
connSummaries := []ConnectionSummary{}
for _, conn := range peer.connections {
connSummaries = append(connSummaries, ConnectionSummary{
conn.Remote().NameByte,
conn.RemoteTCPAddr(),
conn.Outbound(),
conn.Established(),
})
}
checkFatal(enc.Encode(connSummaries))
}
func (peer *LocalPeer) Encode(enc *gob.Encoder) {
peer.RLock()
defer peer.RUnlock()
peer.Peer.Encode(enc)
}
func decodePeer(dec *gob.Decoder) (peerSummary PeerSummary, connSummaries []ConnectionSummary, err error) {
if err = dec.Decode(&peerSummary); err != nil {
return
}
if err = dec.Decode(&connSummaries); err != nil {
return
}
return
}
func makeConnsMap(peer *Peer, connSummaries []ConnectionSummary, byName map[PeerName]*Peer) map[PeerName]Connection {
conns := make(map[PeerName]Connection)
for _, connSummary := range connSummaries {
name := PeerNameFromBin(connSummary.NameByte)
remotePeer := byName[name]
conn := NewRemoteConnection(peer, remotePeer, connSummary.RemoteTCPAddr, connSummary.Outbound, connSummary.Established)
conns[name] = conn
}
return conns
}
+376
View File
@@ -0,0 +1,376 @@
package mesh
import (
"fmt"
"math/rand"
"testing"
"time"
"github.com/stretchr/testify/require"
)
// TODO we should also test:
//
// - applying an incremental update, including the case where that
// leads to an UnknownPeerError
//
// - the "improved update" calculation
//
// - non-gc of peers that are only referenced locally
func newNode(name PeerName) (*Peer, *Peers) {
peer := NewLocalPeer(name, "", nil)
peers := NewPeers(peer)
return peer.Peer, peers
}
// Check that ApplyUpdate copies the whole topology from peers
func checkApplyUpdate(t *testing.T, peers *Peers) {
dummyName, _ := PeerNameFromString("99:00:00:01:00:00")
// We need a new node outside of the network, with a connection
// into it.
_, testBedPeers := newNode(dummyName)
testBedPeers.AddTestConnection(peers.ourself.Peer)
testBedPeers.ApplyUpdate(peers.EncodePeers(peers.Names()))
checkTopologyPeers(t, true, testBedPeers.allPeersExcept(dummyName), peers.allPeers()...)
}
func TestPeersEncoding(t *testing.T) {
const numNodes = 20
const numIters = 1000
var peer [numNodes]*Peer
var ps [numNodes]*Peers
for i := 0; i < numNodes; i++ {
name, _ := PeerNameFromString(fmt.Sprintf("%02d:00:00:01:00:00", i))
peer[i], ps[i] = newNode(name)
}
var conns []struct{ from, to int }
for i := 0; i < numIters; i++ {
oper := rand.Intn(2)
switch oper {
case 0:
from, to := rand.Intn(numNodes), rand.Intn(numNodes)
if from != to {
if _, found := peer[from].connections[peer[to].Name]; !found {
ps[from].AddTestConnection(peer[to])
conns = append(conns, struct{ from, to int }{from, to})
checkApplyUpdate(t, ps[from])
}
}
case 1:
if len(conns) > 0 {
n := rand.Intn(len(conns))
c := conns[n]
ps[c.from].DeleteTestConnection(peer[c.to])
ps[c.from].GarbageCollect()
checkApplyUpdate(t, ps[c.from])
conns = append(conns[:n], conns[n+1:]...)
}
}
}
}
func garbageCollect(peers *Peers) []*Peer {
var removed []*Peer
peers.OnGC(func(peer *Peer) { removed = append(removed, peer) })
peers.GarbageCollect()
return removed
}
func TestPeersGarbageCollection(t *testing.T) {
const (
peer1NameString = "01:00:00:01:00:00"
peer2NameString = "02:00:00:02:00:00"
peer3NameString = "03:00:00:03:00:00"
)
var (
peer1Name, _ = PeerNameFromString(peer1NameString)
peer2Name, _ = PeerNameFromString(peer2NameString)
peer3Name, _ = PeerNameFromString(peer3NameString)
)
// Create some peers with some connections to each other
p1, ps1 := newNode(peer1Name)
p2, ps2 := newNode(peer2Name)
p3, ps3 := newNode(peer3Name)
ps1.AddTestConnection(p2)
ps2.AddTestRemoteConnection(p1, p2)
ps2.AddTestConnection(p1)
ps2.AddTestConnection(p3)
ps3.AddTestConnection(p1)
ps1.AddTestConnection(p3)
ps2.AddTestRemoteConnection(p1, p3)
ps2.AddTestRemoteConnection(p3, p1)
// Every peer is referenced, so nothing should be dropped
require.Empty(t, garbageCollect(ps1), "peers removed")
require.Empty(t, garbageCollect(ps2), "peers removed")
require.Empty(t, garbageCollect(ps3), "peers removed")
// Drop the connection from 2 to 3, and 3 isn't garbage-collected
// because 1 has a connection to 3
ps2.DeleteTestConnection(p3)
require.Empty(t, garbageCollect(ps2), "peers removed")
// Drop the connection from 1 to 3, and 3 will get removed by
// garbage-collection
ps1.DeleteTestConnection(p3)
checkPeerArray(t, garbageCollect(ps1), p3)
}
func TestShortIDCollisions(t *testing.T) {
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
_, peers := newNode(PeerName(1 << PeerShortIDBits))
// Make enough peers that short id collisions are
// overwhelmingly likely
ps := make([]*Peer, 1<<PeerShortIDBits)
for i := 0; i < 1<<PeerShortIDBits; i++ {
ps[i] = NewPeer(PeerName(i), "", PeerUID(i), 0,
PeerShortID(rng.Intn(1<<PeerShortIDBits)))
}
shuffle := func() {
for i := range ps {
j := rng.Intn(i + 1)
ps[i], ps[j] = ps[j], ps[i]
}
}
// Fill peers
shuffle()
var pending PeersPendingNotifications
for _, p := range ps {
peers.addByShortID(p, &pending)
}
// Check invariants
counts := make([]int, 1<<PeerShortIDBits)
saw := func(p *Peer) {
if p != peers.ourself.Peer {
counts[p.UID]++
}
}
for shortID, entry := range peers.byShortID {
if entry.peer == nil {
// no principal peer for this short id, so
// others must be empty
require.Empty(t, entry.others)
continue
}
require.Equal(t, shortID, entry.peer.ShortID)
saw(entry.peer)
for _, p := range entry.others {
saw(p)
require.Equal(t, shortID, p.ShortID)
// the principal peer should have the lowest name
require.True(t, p.Name > entry.peer.Name)
}
}
// Check that every peer was seen
for _, n := range counts {
require.Equal(t, 1, n)
}
// Delete all the peers
shuffle()
for _, p := range ps {
peers.deleteByShortID(p, &pending)
}
for _, entry := range peers.byShortID {
if entry.peer != peers.ourself.Peer {
require.Nil(t, entry.peer)
}
require.Empty(t, entry.others)
}
}
// Test the easy case of short id reassignment, when few short ids are taken
func TestShortIDReassignmentEasy(t *testing.T) {
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
_, peers := newNode(PeerName(0))
for i := 1; i <= 10; i++ {
peers.FetchWithDefault(NewPeer(PeerName(i), "", PeerUID(i), 0,
PeerShortID(rng.Intn(1<<PeerShortIDBits))))
}
checkShortIDReassignment(t, peers)
}
// Test the hard case of short id reassignment, when most short ids are taken
func TestShortIDReassignmentHard(t *testing.T) {
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
_, peers := newNode(PeerName(1 << PeerShortIDBits))
// Take all short ids
ps := make([]*Peer, 1<<PeerShortIDBits)
var pending PeersPendingNotifications
for i := 0; i < 1<<PeerShortIDBits; i++ {
ps[i] = NewPeer(PeerName(i), "", PeerUID(i), 0,
PeerShortID(i))
peers.addByShortID(ps[i], &pending)
}
// As all short ids are taken, an attempted reassigment won't
// do anything
oldShortID := peers.ourself.ShortID
require.False(t, peers.reassignLocalShortID(&pending))
require.Equal(t, oldShortID, peers.ourself.ShortID)
// Free up a few ids
for i := 0; i < 10; i++ {
x := rng.Intn(len(ps))
if ps[x] != nil {
peers.deleteByShortID(ps[x], &pending)
ps[x] = nil
}
}
checkShortIDReassignment(t, peers)
}
func checkShortIDReassignment(t *testing.T, peers *Peers) {
oldShortID := peers.ourself.ShortID
peers.reassignLocalShortID(&PeersPendingNotifications{})
require.NotEqual(t, oldShortID, peers.ourself.ShortID)
require.Equal(t, peers.ourself.Peer, peers.byShortID[peers.ourself.ShortID].peer)
}
func TestShortIDInvalidation(t *testing.T) {
_, peers := newNode(PeerName(1 << PeerShortIDBits))
// need to use a short id that is not the local peer's
shortID := peers.ourself.ShortID + 1
var pending PeersPendingNotifications
requireInvalidateShortIDs := func(expect bool) {
require.Equal(t, expect, pending.invalidateShortIDs)
pending.invalidateShortIDs = false
}
// The use of a fresh short id does not cause invalidation
a := NewPeer(PeerName(1), "", PeerUID(1), 0, shortID)
peers.addByShortID(a, &pending)
requireInvalidateShortIDs(false)
// An addition which does not change the mapping
b := NewPeer(PeerName(2), "", PeerUID(2), 0, shortID)
peers.addByShortID(b, &pending)
requireInvalidateShortIDs(false)
// An addition which does change the mapping
c := NewPeer(PeerName(0), "", PeerUID(0), 0, shortID)
peers.addByShortID(c, &pending)
requireInvalidateShortIDs(true)
// A deletion which does not change the mapping
peers.deleteByShortID(b, &pending)
requireInvalidateShortIDs(false)
// A deletion which does change the mapping
peers.deleteByShortID(c, &pending)
requireInvalidateShortIDs(true)
// Deleting the last peer with a short id does not cause invalidation
peers.deleteByShortID(a, &pending)
requireInvalidateShortIDs(false)
// .. but subsequent reuse of that short id does cause invalidation
peers.addByShortID(a, &pending)
requireInvalidateShortIDs(true)
}
func TestShortIDPropagation(t *testing.T) {
_, peers1 := newNode(PeerName(1))
_, peers2 := newNode(PeerName(2))
peers1.AddTestConnection(peers2.ourself.Peer)
peers1.ApplyUpdate(peers2.EncodePeers(peers2.Names()))
peers12 := peers1.Fetch(PeerName(2))
old := peers12.PeerSummary
require.True(t,
peers2.reassignLocalShortID(&PeersPendingNotifications{}))
peers1.ApplyUpdate(peers2.EncodePeers(peers2.Names()))
require.NotEqual(t, old.Version, peers12.Version)
require.NotEqual(t, old.ShortID, peers12.ShortID)
}
func TestShortIDCollision(t *testing.T) {
// Create 3 peers
_, peers1 := newNode(PeerName(1))
_, peers2 := newNode(PeerName(2))
_, peers3 := newNode(PeerName(3))
var pending PeersPendingNotifications
peers1.setLocalShortID(1, &pending)
peers2.setLocalShortID(2, &pending)
peers3.setLocalShortID(3, &pending)
peers2.AddTestConnection(peers1.ourself.Peer)
peers3.AddTestConnection(peers2.ourself.Peer)
// Propogate from 1 to 2 to 3
peers2.ApplyUpdate(peers1.EncodePeers(peers1.Names()))
peers3.ApplyUpdate(peers2.EncodePeers(peers2.Names()))
// Force the short id of peer 1 to collide with peer 2. Peer
// 1 has the lowest name, so it gets to keep the short id
peers1.setLocalShortID(2, &pending)
oldShortID := peers2.ourself.ShortID
_, updated, _ := peers2.ApplyUpdate(peers1.EncodePeers(peers1.Names()))
// peer 2 should have noticed the collision and resolved it
require.NotEqual(t, oldShortID, peers2.ourself.ShortID)
// The Peers do not have a Router, so broadcastPeerUpdate does
// nothing in the context of this test. So we fake what it
// would do.
updated[PeerName(2)] = void
// the update from peer 2 should include its short id change
peers3.ApplyUpdate(peers2.EncodePeers(updated))
require.Equal(t, peers2.ourself.ShortID,
peers3.Fetch(PeerName(2)).ShortID)
}
// Test the case where all short ids are taken, but then some peers go
// away, so the local peer reassigns
func TestDeferredShortIDReassignment(t *testing.T) {
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
_, us := newNode(PeerName(1 << PeerShortIDBits))
// Connect us to other peers occupying all short ids
others := make([]*Peers, 1<<PeerShortIDBits)
var pending PeersPendingNotifications
for i := range others {
_, others[i] = newNode(PeerName(i))
others[i].setLocalShortID(PeerShortID(i), &pending)
us.AddTestConnection(others[i].ourself.Peer)
}
// Check that, as expected, the local peer does not own its
// short id
require.NotEqual(t, us.ourself.Peer,
us.byShortID[us.ourself.ShortID].peer)
// Disconnect one peer, and we should now be able to claim its
// short id
other := others[rng.Intn(1<<PeerShortIDBits)]
us.DeleteTestConnection(other.ourself.Peer)
us.GarbageCollect()
require.Equal(t, us.ourself.Peer, us.byShortID[us.ourself.ShortID].peer)
}
+348
View File
@@ -0,0 +1,348 @@
package mesh
import (
"bytes"
"encoding/gob"
"encoding/hex"
"fmt"
"io"
"time"
)
const (
Protocol = "weave"
ProtocolMinVersion = 1
ProtocolMaxVersion = 2
)
var (
ProtocolBytes = []byte(Protocol)
HeaderTimeout = 10 * time.Second
ProtocolV1Features = []string{
"ConnID",
"Name",
"NickName",
"PeerNameFlavour",
"UID",
}
ErrExpectedCrypto = fmt.Errorf("Password specified, but peer requested an unencrypted connection")
ErrExpectedNoCrypto = fmt.Errorf("No password specificed, but peer requested an encrypted connection")
)
// We don't need the full net.TCPConn to do the protocol intro. This
// interface contains just the parts we do need, to support testing
type ProtocolIntroConn interface {
// io.Reader
Read(b []byte) (n int, err error)
// io.Writer
Write(b []byte) (n int, err error)
// net.Conn's deadline methods
SetDeadline(t time.Time) error
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error
}
type ProtocolIntroParams struct {
MinVersion byte
MaxVersion byte
Features map[string]string
Conn ProtocolIntroConn
Password []byte
Outbound bool
}
type ProtocolIntroResults struct {
Features map[string]string
Receiver TCPReceiver
Sender TCPSender
SessionKey *[32]byte
Version byte
}
func (params ProtocolIntroParams) DoIntro() (res ProtocolIntroResults, err error) {
if err = params.Conn.SetDeadline(time.Now().Add(HeaderTimeout)); err != nil {
return
}
if res.Version, err = params.exchangeProtocolHeader(); err != nil {
return
}
var pubKey, privKey *[32]byte
if params.Password != nil {
if pubKey, privKey, err = GenerateKeyPair(); err != nil {
return
}
}
if err = params.Conn.SetWriteDeadline(time.Time{}); err != nil {
return
}
if err = params.Conn.SetReadDeadline(time.Now().Add(TCPHeartbeat * 2)); err != nil {
return
}
switch res.Version {
case 1:
err = res.doIntroV1(params, pubKey, privKey)
case 2:
err = res.doIntroV2(params, pubKey, privKey)
default:
panic("unhandled protocol version")
}
return
}
func (params ProtocolIntroParams) exchangeProtocolHeader() (byte, error) {
// Write in a separate goroutine to avoid the possibility of
// deadlock. The result channel is of size 1 so that the
// goroutine does not linger even if we encounter an error on
// the read side.
sendHeader := append(ProtocolBytes, params.MinVersion, params.MaxVersion)
writeDone := make(chan error, 1)
go func() {
_, err := params.Conn.Write(sendHeader)
writeDone <- err
}()
header := make([]byte, len(ProtocolBytes)+2)
if n, err := io.ReadFull(params.Conn, header); err != nil && n == 0 {
return 0, fmt.Errorf("failed to receive remote protocol header: %s", err)
} else if err != nil {
return 0, fmt.Errorf("received incomplete remote protocol header (%d octets instead of %d): %v; error: %s",
n, len(header), header[:n], err)
}
if !bytes.Equal(ProtocolBytes, header[:len(ProtocolBytes)]) {
return 0, fmt.Errorf("remote protocol header not recognised: %v", header[:len(ProtocolBytes)])
}
theirMinVersion := header[len(ProtocolBytes)]
minVersion := theirMinVersion
if params.MinVersion > minVersion {
minVersion = params.MinVersion
}
theirMaxVersion := header[len(ProtocolBytes)+1]
maxVersion := theirMaxVersion
if maxVersion > params.MaxVersion {
maxVersion = params.MaxVersion
}
if minVersion > maxVersion {
return 0, fmt.Errorf("remote version range [%d,%d] is incompatible with ours [%d,%d]",
theirMinVersion, theirMaxVersion,
params.MinVersion, params.MaxVersion)
}
if err := <-writeDone; err != nil {
return 0, err
}
return maxVersion, nil
}
// The V1 procotol consists of the protocol identification/version
// header, followed by a stream of gobified values. The first value
// is the encoded features map (never encrypted). The subsequent
// values are the messages on the connection (encrypted for an
// encrypted connection). For an encrypted connection, the public key
// is passed in the "PublicKey" feature as a string of hex digits.
func (res *ProtocolIntroResults) doIntroV1(params ProtocolIntroParams, pubKey, privKey *[32]byte) error {
features := filterV1Features(params.Features)
if pubKey != nil {
features["PublicKey"] = hex.EncodeToString(pubKey[:])
}
enc := gob.NewEncoder(params.Conn)
dec := gob.NewDecoder(params.Conn)
// Encode in a separate goroutine to avoid the possibility of
// deadlock. The result channel is of size 1 so that the
// goroutine does not linger even if we encounter an error on
// the read side.
encodeDone := make(chan error, 1)
go func() {
encodeDone <- enc.Encode(features)
}()
if err := dec.Decode(&res.Features); err != nil {
return err
}
if err := <-encodeDone; err != nil {
return err
}
res.Sender = NewGobTCPSender(enc)
res.Receiver = NewGobTCPReceiver(dec)
if pubKey == nil {
if _, present := res.Features["PublicKey"]; present {
return ErrExpectedNoCrypto
}
} else {
remotePubKeyStr, ok := res.Features["PublicKey"]
if !ok {
return ErrExpectedCrypto
}
remotePubKey, err := hex.DecodeString(remotePubKeyStr)
if err != nil {
return err
}
res.setupCrypto(params, remotePubKey, privKey)
}
res.Features = filterV1Features(res.Features)
return nil
}
// In the V1 protocol, the intro fields are sent unencrypted. So we
// restrict them to an established subset of fields that are assumed
// to be safe.
func filterV1Features(intro map[string]string) map[string]string {
safe := make(map[string]string)
for _, k := range ProtocolV1Features {
if val, ok := intro[k]; ok {
safe[k] = val
}
}
return safe
}
// The V2 procotol consists of the protocol identification/version
// header, followed by:
//
// - A single "encryption flag" byte: 0 for no encryption, 1 for
// encryption.
//
// - When the connection is encrypted, 32 bytes follow containing the
// public key.
//
// - Then a stream of length-prefixed messages, which are encrypted
// for an encrypted connection.
//
// The first message contains the encoded features map (so in contrast
// to V1, it will be encrypted on an encrypted connection).
func (res *ProtocolIntroResults) doIntroV2(params ProtocolIntroParams, pubKey, privKey *[32]byte) error {
// Public key exchange
var wbuf []byte
if pubKey == nil {
wbuf = []byte{0}
} else {
wbuf = make([]byte, 1+len(*pubKey))
wbuf[0] = 1
copy(wbuf[1:], (*pubKey)[:])
}
// Write in a separate goroutine to avoid the possibility of
// deadlock. The result channel is of size 1 so that the
// goroutine does not linger even if we encounter an error on
// the read side.
writeDone := make(chan error, 1)
go func() {
_, err := params.Conn.Write(wbuf)
writeDone <- err
}()
rbuf := make([]byte, 1)
if _, err := io.ReadFull(params.Conn, rbuf); err != nil {
return err
}
switch rbuf[0] {
case 0:
if pubKey != nil {
return ErrExpectedCrypto
}
res.Sender = NewLengthPrefixTCPSender(params.Conn)
res.Receiver = NewLengthPrefixTCPReceiver(params.Conn)
case 1:
if pubKey == nil {
return ErrExpectedNoCrypto
}
rbuf = make([]byte, len(pubKey))
if _, err := io.ReadFull(params.Conn, rbuf); err != nil {
return err
}
res.Sender = NewLengthPrefixTCPSender(params.Conn)
res.Receiver = NewLengthPrefixTCPReceiver(params.Conn)
res.setupCrypto(params, rbuf, privKey)
default:
return fmt.Errorf("Bad encryption flag %d", rbuf[0])
}
if err := <-writeDone; err != nil {
return err
}
// Features exchange
go func() {
buf := new(bytes.Buffer)
if err := gob.NewEncoder(buf).Encode(&params.Features); err != nil {
writeDone <- err
return
}
writeDone <- res.Sender.Send(buf.Bytes())
}()
rbuf, err := res.Receiver.Receive()
if err != nil {
return err
}
if err := gob.NewDecoder(bytes.NewReader(rbuf)).Decode(&res.Features); err != nil {
return err
}
if err := <-writeDone; err != nil {
return err
}
return nil
}
func (res *ProtocolIntroResults) setupCrypto(params ProtocolIntroParams, remotePubKey []byte, privKey *[32]byte) {
var remotePubKeyArr [32]byte
copy(remotePubKeyArr[:], remotePubKey)
res.SessionKey = FormSessionKey(&remotePubKeyArr, privKey, params.Password)
res.Sender = NewEncryptedTCPSender(res.Sender, res.SessionKey, params.Outbound)
res.Receiver = NewEncryptedTCPReceiver(res.Receiver, res.SessionKey, params.Outbound)
}
type ProtocolTag byte
const (
ProtocolHeartbeat = iota
ProtocolReserved1 // legacy overlay control messages
ProtocolReserved2 //
ProtocolReserved3 //
ProtocolGossip
ProtocolGossipUnicast
ProtocolGossipBroadcast
ProtocolOverlayControlMsg
)
type ProtocolMsg struct {
tag ProtocolTag
msg []byte
}
type ProtocolSender interface {
SendProtocolMsg(m ProtocolMsg)
}
+180
View File
@@ -0,0 +1,180 @@
package mesh
import (
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"encoding/gob"
"fmt"
"io"
"sync"
"golang.org/x/crypto/nacl/box"
"golang.org/x/crypto/nacl/secretbox"
)
const MaxTCPMsgSize = 10 * 1024 * 1024
func GenerateKeyPair() (publicKey, privateKey *[32]byte, err error) {
return box.GenerateKey(rand.Reader)
}
func FormSessionKey(remotePublicKey, localPrivateKey *[32]byte, secretKey []byte) *[32]byte {
var sharedKey [32]byte
box.Precompute(&sharedKey, remotePublicKey, localPrivateKey)
sharedKeySlice := sharedKey[:]
sharedKeySlice = append(sharedKeySlice, secretKey...)
sessionKey := sha256.Sum256(sharedKeySlice)
return &sessionKey
}
// TCP Senders/Receivers
// The lowest 64 bits of the nonce contain the message sequence
// number. The top most bit indicates the connection polarity at the
// sender - '1' for outbound; the next indicates protocol type - '1'
// for TCP. The remaining 126 bits are zero. The polarity is needed so
// that the two ends of a connection do not use the same nonces; the
// protocol type so that the TCP connection nonces are distinct from
// nonces used by overlay connections, if they share the session key.
// This is a requirement of the NaCl Security Model; see
// http://nacl.cr.yp.to/box.html.
type TCPCryptoState struct {
sessionKey *[32]byte
nonce [24]byte
seqNo uint64
}
func NewTCPCryptoState(sessionKey *[32]byte, outbound bool) *TCPCryptoState {
s := &TCPCryptoState{sessionKey: sessionKey}
if outbound {
s.nonce[0] |= (1 << 7)
}
s.nonce[0] |= (1 << 6)
return s
}
func (s *TCPCryptoState) advance() {
s.seqNo++
binary.BigEndian.PutUint64(s.nonce[16:24], s.seqNo)
}
type TCPSender interface {
Send([]byte) error
}
type GobTCPSender struct {
encoder *gob.Encoder
}
type LengthPrefixTCPSender struct {
writer io.Writer
}
type EncryptedTCPSender struct {
sync.RWMutex
sender TCPSender
state *TCPCryptoState
}
func NewGobTCPSender(encoder *gob.Encoder) *GobTCPSender {
return &GobTCPSender{encoder: encoder}
}
func (sender *GobTCPSender) Send(msg []byte) error {
return sender.encoder.Encode(msg)
}
func NewLengthPrefixTCPSender(writer io.Writer) *LengthPrefixTCPSender {
return &LengthPrefixTCPSender{writer: writer}
}
func (sender *LengthPrefixTCPSender) Send(msg []byte) error {
l := len(msg)
if l > MaxTCPMsgSize {
return fmt.Errorf("outgoing message exceeds maximum size: %d > %d", l, MaxTCPMsgSize)
}
// We copy the message so we can send it in a single Write
// operation, thus making this thread-safe without locking.
prefixedMsg := make([]byte, 4+l)
binary.BigEndian.PutUint32(prefixedMsg, uint32(l))
copy(prefixedMsg[4:], msg)
_, err := sender.writer.Write(prefixedMsg)
return err
}
func NewEncryptedTCPSender(sender TCPSender, sessionKey *[32]byte, outbound bool) *EncryptedTCPSender {
return &EncryptedTCPSender{sender: sender, state: NewTCPCryptoState(sessionKey, outbound)}
}
func (sender *EncryptedTCPSender) Send(msg []byte) error {
sender.Lock()
defer sender.Unlock()
encodedMsg := secretbox.Seal(nil, msg, &sender.state.nonce, sender.state.sessionKey)
sender.state.advance()
return sender.sender.Send(encodedMsg)
}
type TCPReceiver interface {
Receive() ([]byte, error)
}
type GobTCPReceiver struct {
decoder *gob.Decoder
}
type LengthPrefixTCPReceiver struct {
reader io.Reader
}
type EncryptedTCPReceiver struct {
receiver TCPReceiver
state *TCPCryptoState
}
func NewGobTCPReceiver(decoder *gob.Decoder) *GobTCPReceiver {
return &GobTCPReceiver{decoder: decoder}
}
func (receiver *GobTCPReceiver) Receive() ([]byte, error) {
var msg []byte
err := receiver.decoder.Decode(&msg)
return msg, err
}
func NewLengthPrefixTCPReceiver(reader io.Reader) *LengthPrefixTCPReceiver {
return &LengthPrefixTCPReceiver{reader: reader}
}
func (receiver *LengthPrefixTCPReceiver) Receive() ([]byte, error) {
lenPrefix := make([]byte, 4)
if _, err := io.ReadFull(receiver.reader, lenPrefix); err != nil {
return nil, err
}
l := binary.BigEndian.Uint32(lenPrefix)
if l > MaxTCPMsgSize {
return nil, fmt.Errorf("incoming message exceeds maximum size: %d > %d", l, MaxTCPMsgSize)
}
msg := make([]byte, l)
_, err := io.ReadFull(receiver.reader, msg)
return msg, err
}
func NewEncryptedTCPReceiver(receiver TCPReceiver, sessionKey *[32]byte, outbound bool) *EncryptedTCPReceiver {
return &EncryptedTCPReceiver{receiver: receiver, state: NewTCPCryptoState(sessionKey, !outbound)}
}
func (receiver *EncryptedTCPReceiver) Receive() ([]byte, error) {
msg, err := receiver.receiver.Receive()
if err != nil {
return nil, err
}
decodedMsg, success := secretbox.Open(nil, msg, &receiver.state.nonce, receiver.state.sessionKey)
if !success {
return nil, fmt.Errorf("Unable to decrypt TCP msg")
}
receiver.state.advance()
return decodedMsg, nil
}
+96
View File
@@ -0,0 +1,96 @@
package mesh
import (
"io"
"testing"
"time"
"github.com/stretchr/testify/require"
)
type testConn struct {
io.Writer
io.Reader
}
func (testConn) SetDeadline(t time.Time) error {
return nil
}
func (testConn) SetReadDeadline(t time.Time) error {
return nil
}
func (testConn) SetWriteDeadline(t time.Time) error {
return nil
}
func connPair() (ProtocolIntroConn, ProtocolIntroConn) {
a := testConn{}
b := testConn{}
a.Reader, b.Writer = io.Pipe()
b.Reader, a.Writer = io.Pipe()
return &a, &b
}
func doIntro(t *testing.T, params ProtocolIntroParams) <-chan ProtocolIntroResults {
ch := make(chan ProtocolIntroResults, 1)
go func() {
res, err := params.DoIntro()
require.Nil(t, err)
ch <- res
}()
return ch
}
func doProtocolIntro(t *testing.T, aver, bver byte, password []byte) byte {
aconn, bconn := connPair()
aresch := doIntro(t, ProtocolIntroParams{
MinVersion: ProtocolMinVersion,
MaxVersion: aver,
Features: map[string]string{"Name": "A"},
Conn: aconn,
Outbound: true,
Password: password,
})
bresch := doIntro(t, ProtocolIntroParams{
MinVersion: ProtocolMinVersion,
MaxVersion: bver,
Features: map[string]string{"Name": "B"},
Conn: bconn,
Outbound: false,
Password: password,
})
ares := <-aresch
bres := <-bresch
// Check that features were conveyed
require.Equal(t, "B", ares.Features["Name"])
require.Equal(t, "A", bres.Features["Name"])
// Check that Senders and Receivers work
go func() {
require.Nil(t, ares.Sender.Send([]byte("Hello from A")))
require.Nil(t, bres.Sender.Send([]byte("Hello from B")))
}()
data, err := bres.Receiver.Receive()
require.Nil(t, err)
require.Equal(t, "Hello from A", string(data))
data, err = ares.Receiver.Receive()
require.Nil(t, err)
require.Equal(t, "Hello from B", string(data))
require.Equal(t, ares.Version, bres.Version)
return ares.Version
}
func TestProtocolIntro(t *testing.T) {
require.Equal(t, 2, int(doProtocolIntro(t, 2, 2, nil)))
require.Equal(t, 2, int(doProtocolIntro(t, 2, 2, []byte("sekr1t"))))
require.Equal(t, 1, int(doProtocolIntro(t, 1, 2, nil)))
require.Equal(t, 1, int(doProtocolIntro(t, 1, 2, []byte("pa55"))))
require.Equal(t, 1, int(doProtocolIntro(t, 2, 1, nil)))
require.Equal(t, 1, int(doProtocolIntro(t, 2, 1, []byte("w0rd"))))
}
+198
View File
@@ -0,0 +1,198 @@
package mesh
import (
"fmt"
"math"
"net"
"sync"
"time"
"github.com/weaveworks/weave/common"
)
const (
Port = 6783
ChannelSize = 16
TCPHeartbeat = 30 * time.Second
GossipInterval = 30 * time.Second
MaxDuration = time.Duration(math.MaxInt64)
acceptMaxTokens = 100 // [1]
acceptTokenDelay = 100 * time.Millisecond // [2]
)
// [1] capacity of token bucket for rate limiting accepts
// [2] control rate at which new tokens are added to the bucket
var (
log = common.Log
checkFatal = common.CheckFatal
checkWarn = common.CheckWarn
)
type Config struct {
Port int
ProtocolMinVersion byte
Password []byte
ConnLimit int
PeerDiscovery bool
TrustedSubnets []*net.IPNet
}
type Router struct {
Config
Overlay Overlay
Ourself *LocalPeer
Peers *Peers
Routes *Routes
ConnectionMaker *ConnectionMaker
gossipLock sync.RWMutex
gossipChannels GossipChannels
TopologyGossip Gossip
acceptLimiter *TokenBucket
}
func NewRouter(config Config, name PeerName, nickName string, overlay Overlay) *Router {
router := &Router{Config: config, gossipChannels: make(GossipChannels)}
if overlay == nil {
overlay = NullOverlay{}
}
router.Overlay = overlay
router.Ourself = NewLocalPeer(name, nickName, router)
router.Peers = NewPeers(router.Ourself)
router.Peers.OnGC(func(peer *Peer) {
log.Println("Removed unreachable peer", peer)
})
router.Routes = NewRoutes(router.Ourself, router.Peers)
router.ConnectionMaker = NewConnectionMaker(router.Ourself, router.Peers, router.Port, router.PeerDiscovery)
router.TopologyGossip = router.NewGossip("topology", router)
router.acceptLimiter = NewTokenBucket(acceptMaxTokens, acceptTokenDelay)
return router
}
// Start listening for TCP connections. This is separate from
// NewRouter so that gossipers can register before we start forming
// connections.
func (router *Router) Start() {
router.listenTCP(router.Port)
}
func (router *Router) Stop() error {
// TODO: perform graceful shutdown...
return nil
}
func (router *Router) UsingPassword() bool {
return router.Password != nil
}
func (router *Router) listenTCP(localPort int) {
localAddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprint(":", localPort))
checkFatal(err)
ln, err := net.ListenTCP("tcp4", localAddr)
checkFatal(err)
go func() {
defer ln.Close()
for {
tcpConn, err := ln.AcceptTCP()
if err != nil {
log.Errorln(err)
continue
}
router.acceptTCP(tcpConn)
router.acceptLimiter.Wait()
}
}()
}
func (router *Router) acceptTCP(tcpConn *net.TCPConn) {
remoteAddrStr := tcpConn.RemoteAddr().String()
log.Printf("->[%s] connection accepted", remoteAddrStr)
connRemote := NewRemoteConnection(router.Ourself.Peer, nil, remoteAddrStr, false, false)
StartLocalConnection(connRemote, tcpConn, router, true)
}
// Gossiper methods - the Router is the topology Gossiper
type TopologyGossipData struct {
peers *Peers
update PeerNameSet
}
func (d *TopologyGossipData) Merge(other GossipData) GossipData {
names := make(PeerNameSet)
for name := range d.update {
names[name] = void
}
for name := range other.(*TopologyGossipData).update {
names[name] = void
}
return &TopologyGossipData{peers: d.peers, update: names}
}
func (d *TopologyGossipData) Encode() [][]byte {
return [][]byte{d.peers.EncodePeers(d.update)}
}
func (router *Router) BroadcastTopologyUpdate(update []*Peer) {
names := make(PeerNameSet)
for _, p := range update {
names[p.Name] = void
}
router.TopologyGossip.GossipBroadcast(
&TopologyGossipData{peers: router.Peers, update: names})
}
func (router *Router) OnGossipUnicast(sender PeerName, msg []byte) error {
return fmt.Errorf("unexpected topology gossip unicast: %v", msg)
}
func (router *Router) OnGossipBroadcast(_ PeerName, update []byte) (GossipData, error) {
origUpdate, _, err := router.applyTopologyUpdate(update)
if err != nil || len(origUpdate) == 0 {
return nil, err
}
return &TopologyGossipData{peers: router.Peers, update: origUpdate}, nil
}
func (router *Router) Gossip() GossipData {
return &TopologyGossipData{peers: router.Peers, update: router.Peers.Names()}
}
func (router *Router) OnGossip(update []byte) (GossipData, error) {
_, newUpdate, err := router.applyTopologyUpdate(update)
if err != nil || len(newUpdate) == 0 {
return nil, err
}
return &TopologyGossipData{peers: router.Peers, update: newUpdate}, nil
}
func (router *Router) applyTopologyUpdate(update []byte) (PeerNameSet, PeerNameSet, error) {
origUpdate, newUpdate, err := router.Peers.ApplyUpdate(update)
if err != nil {
return nil, nil, err
}
if len(newUpdate) > 0 {
router.ConnectionMaker.Refresh()
router.Routes.Recalculate()
}
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
}
+108 -74
View File
@@ -1,47 +1,52 @@
package router
package mesh
import (
"bytes"
"fmt"
"math"
"sync"
)
type unicastRoutes map[PeerName]PeerName
type broadcastRoutes map[PeerName][]PeerName
type Routes struct {
sync.RWMutex
ourself *Peer
ourself *LocalPeer
peers *Peers
unicast map[PeerName]PeerName
unicastAll map[PeerName]PeerName // [1]
broadcast map[PeerName][]PeerName
broadcastAll map[PeerName][]PeerName // [1]
onChange []func()
unicast unicastRoutes
unicastAll unicastRoutes // [1]
broadcast broadcastRoutes
broadcastAll broadcastRoutes // [1]
recalculate chan<- *struct{}
wait chan<- chan struct{}
// [1] based on *all* connections, not just established &
// symmetric ones
}
func NewRoutes(ourself *Peer, peers *Peers) *Routes {
func NewRoutes(ourself *LocalPeer, peers *Peers) *Routes {
recalculate := make(chan *struct{}, 1)
wait := make(chan chan struct{})
routes := &Routes{
ourself: ourself,
peers: peers,
unicast: make(map[PeerName]PeerName),
unicastAll: make(map[PeerName]PeerName),
broadcast: make(map[PeerName][]PeerName),
broadcastAll: make(map[PeerName][]PeerName)}
unicast: make(unicastRoutes),
unicastAll: make(unicastRoutes),
broadcast: make(broadcastRoutes),
broadcastAll: make(broadcastRoutes),
recalculate: recalculate,
wait: wait}
routes.unicast[ourself.Name] = UnknownPeerName
routes.unicastAll[ourself.Name] = UnknownPeerName
routes.broadcast[ourself.Name] = []PeerName{}
routes.broadcastAll[ourself.Name] = []PeerName{}
go routes.run(recalculate, wait)
return routes
}
func (routes *Routes) Start() {
recalculate := make(chan *struct{}, 1)
wait := make(chan chan struct{})
routes.recalculate = recalculate
routes.wait = wait
go routes.run(recalculate, wait)
func (routes *Routes) OnChange(callback func()) {
routes.Lock()
defer routes.Unlock()
routes.onChange = append(routes.onChange, callback)
}
func (routes *Routes) PeerNames() PeerNameSet {
@@ -65,21 +70,13 @@ func (routes *Routes) UnicastAll(name PeerName) (PeerName, bool) {
func (routes *Routes) Broadcast(name PeerName) []PeerName {
routes.RLock()
defer routes.RUnlock()
hops, found := routes.broadcast[name]
if !found {
return []PeerName{}
}
return hops
return routes.broadcast[name]
}
func (routes *Routes) BroadcastAll(name PeerName) []PeerName {
routes.RLock()
defer routes.RUnlock()
hops, found := routes.broadcastAll[name]
if !found {
return []PeerName{}
}
return hops
return routes.broadcastAll[name]
}
// Choose min(log2(n_peers), n_neighbouring_peers) neighbours, with a
@@ -95,40 +92,27 @@ 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
}
func (routes *Routes) String() string {
var buf bytes.Buffer
routes.RLock()
defer routes.RUnlock()
fmt.Fprintln(&buf, "unicast:")
for name, hop := range routes.unicast {
fmt.Fprintf(&buf, "%s -> %s\n", name, hop)
}
fmt.Fprintln(&buf, "broadcast:")
for name, hops := range routes.broadcast {
fmt.Fprintf(&buf, "%s -> %v\n", name, hops)
}
// We don't include the 'all' routes here since they are of
// limited utility in troubleshooting
return buf.String()
}
// Request recalculation of the routing table. This is async but can
// effectively be made synchronous with a subsequent call to
// EnsureRecalculated.
@@ -166,18 +150,32 @@ func (routes *Routes) run(recalculate <-chan *struct{}, wait <-chan chan struct{
}
func (routes *Routes) calculate() {
routes.peers.RLock()
routes.ourself.RLock()
var (
oldUnicast = routes.unicast
oldBroadcast = routes.broadcast
unicast = routes.calculateUnicast(true)
unicastAll = routes.calculateUnicast(false)
broadcast = routes.calculateBroadcast(true)
broadcastAll = routes.calculateBroadcast(false)
)
routes.ourself.RUnlock()
routes.peers.RUnlock()
routes.Lock()
routes.unicast = unicast
routes.unicastAll = unicastAll
routes.broadcast = broadcast
routes.broadcastAll = broadcastAll
onChange := routes.onChange
routes.Unlock()
if !unicast.equals(oldUnicast) || !broadcast.equals(oldBroadcast) {
for _, callback := range onChange {
callback()
}
}
}
// Calculate all the routes for the question: if *we* want to send a
@@ -189,7 +187,7 @@ func (routes *Routes) calculate() {
// any knowledge of the MAC address at all. Thus there's no need
// to exchange knowledge of MAC addresses, nor any constraints on
// the routes that we construct.
func (routes *Routes) calculateUnicast(establishedAndSymmetric bool) map[PeerName]PeerName {
func (routes *Routes) calculateUnicast(establishedAndSymmetric bool) unicastRoutes {
_, unicast := routes.ourself.Routes(nil, establishedAndSymmetric)
return unicast
}
@@ -212,31 +210,67 @@ func (routes *Routes) calculateUnicast(establishedAndSymmetric bool) map[PeerNam
// Y =/= Z /\ X.Routes(Y) <= X.Routes(Z) =>
// X.Routes(Y) u [P | Y.HasSymmetricConnectionTo(P)] <= X.Routes(Z)
// where <= is the subset relationship on keys of the returned map.
func (routes *Routes) calculateBroadcast(establishedAndSymmetric bool) map[PeerName][]PeerName {
broadcast := make(map[PeerName][]PeerName)
ourself := routes.ourself
ourConnections := ourself.Connections()
routes.peers.ForEach(func(peer *Peer) {
func (routes *Routes) calculateBroadcast(establishedAndSymmetric bool) broadcastRoutes {
broadcast := make(broadcastRoutes)
for _, peer := range routes.peers.byName {
hops := []PeerName{}
if found, reached := peer.Routes(ourself, establishedAndSymmetric); found {
// This is rather similar to the inner loop on
// peer.Routes(...); the main difference is in the
// locking.
for conn := range ourConnections {
if establishedAndSymmetric && !conn.Established() {
continue
}
remoteName := conn.Remote().Name
if _, found := reached[remoteName]; found {
continue
}
if remoteConn, found := conn.Remote().ConnectionTo(ourself.Name); !establishedAndSymmetric || (found && remoteConn.Established()) {
hops = append(hops, remoteName)
}
}
if found, reached := peer.Routes(routes.ourself.Peer, establishedAndSymmetric); found {
routes.ourself.ForEachConnectedPeer(establishedAndSymmetric, reached,
func(remotePeer *Peer) { hops = append(hops, remotePeer.Name) })
}
broadcast[peer.Name] = hops
})
}
return broadcast
}
func (a unicastRoutes) equals(b unicastRoutes) bool {
for key, aval := range a {
if bval, ok := b[key]; !ok || bval != aval {
return false
}
}
for key := range b {
if _, ok := a[key]; !ok {
return false
}
}
return true
}
func (a broadcastRoutes) equals(b broadcastRoutes) bool {
set := make(map[PeerName]struct{})
for key, aval := range a {
bval, ok := b[key]
if !ok {
return false
}
for _, peer := range aval {
set[peer] = struct{}{}
}
for _, peer := range bval {
if _, ok := set[peer]; !ok {
return false
}
delete(set, peer)
}
if len(set) != 0 {
return false
}
}
for key := range b {
if _, ok := a[key]; !ok {
return false
}
}
return true
}
+211
View File
@@ -0,0 +1,211 @@
package mesh
import (
"fmt"
"net"
)
type Status struct {
Protocol string
ProtocolMinVersion int
ProtocolMaxVersion int
Encryption bool
PeerDiscovery bool
Name string
NickName string
Port int
Peers []PeerStatus
UnicastRoutes []UnicastRouteStatus
BroadcastRoutes []BroadcastRouteStatus
Connections []LocalConnectionStatus
Targets []string
OverlayDiagnostics interface{}
TrustedSubnets []string
}
type PeerStatus struct {
Name string
NickName string
UID PeerUID
ShortID PeerShortID
Version uint64
Connections []ConnectionStatus
}
type ConnectionStatus struct {
Name string
NickName string
Address string
Outbound bool
Established bool
}
type UnicastRouteStatus struct {
Dest, Via string
}
type BroadcastRouteStatus struct {
Source string
Via []string
}
type LocalConnectionStatus struct {
Address string
Outbound bool
State string
Info string
}
func NewStatus(router *Router) *Status {
return &Status{
Protocol,
ProtocolMinVersion,
ProtocolMaxVersion,
router.UsingPassword(),
router.PeerDiscovery,
router.Ourself.Name.String(),
router.Ourself.NickName,
router.Port,
NewPeerStatusSlice(router.Peers),
NewUnicastRouteStatusSlice(router.Routes),
NewBroadcastRouteStatusSlice(router.Routes),
NewLocalConnectionStatusSlice(router.ConnectionMaker),
NewTargetSlice(router.ConnectionMaker),
router.Overlay.Diagnostics(),
NewTrustedSubnetsSlice(router.TrustedSubnets)}
}
func NewPeerStatusSlice(peers *Peers) []PeerStatus {
var slice []PeerStatus
peers.ForEach(func(peer *Peer) {
var connections []ConnectionStatus
if peer == peers.ourself.Peer {
for conn := range peers.ourself.Connections() {
connections = append(connections, newConnectionStatus(conn))
}
} else {
// Modifying peer.connections requires a write lock on
// Peers, and since we are holding a read lock (due to the
// ForEach), access without locking the peer is safe.
for _, conn := range peer.connections {
connections = append(connections, newConnectionStatus(conn))
}
}
slice = append(slice, PeerStatus{
peer.Name.String(),
peer.NickName,
peer.UID,
peer.ShortID,
peer.Version,
connections})
})
return slice
}
func newConnectionStatus(c Connection) ConnectionStatus {
return ConnectionStatus{
c.Remote().Name.String(),
c.Remote().NickName,
c.RemoteTCPAddr(),
c.Outbound(),
c.Established()}
}
func NewUnicastRouteStatusSlice(routes *Routes) []UnicastRouteStatus {
routes.RLock()
defer routes.RUnlock()
var slice []UnicastRouteStatus
for dest, via := range routes.unicast {
slice = append(slice, UnicastRouteStatus{dest.String(), via.String()})
}
return slice
}
func NewBroadcastRouteStatusSlice(routes *Routes) []BroadcastRouteStatus {
routes.RLock()
defer routes.RUnlock()
var slice []BroadcastRouteStatus
for source, via := range routes.broadcast {
var hops []string
for _, hop := range via {
hops = append(hops, hop.String())
}
slice = append(slice, BroadcastRouteStatus{source.String(), hops})
}
return slice
}
func NewLocalConnectionStatusSlice(cm *ConnectionMaker) []LocalConnectionStatus {
resultChan := make(chan []LocalConnectionStatus, 0)
cm.actionChan <- func() bool {
var slice []LocalConnectionStatus
for conn := range cm.connections {
state := "pending"
if conn.Established() {
state = "established"
}
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 {
add := func(state, info string) {
slice = append(slice, LocalConnectionStatus{address, true, state, info})
}
switch target.state {
case TargetWaiting:
until := "never"
if !target.tryAfter.IsZero() {
until = target.tryAfter.String()
}
if target.lastError == nil { // shouldn't happen
add("waiting", "until: "+until)
} else {
add("failed", target.lastError.Error()+", retry: "+until)
}
case TargetAttempting:
if target.lastError == nil {
add("connecting", "")
} else {
add("retrying", target.lastError.Error())
}
case TargetConnected:
}
}
resultChan <- slice
return false
}
return <-resultChan
}
func NewTargetSlice(cm *ConnectionMaker) []string {
resultChan := make(chan []string, 0)
cm.actionChan <- func() bool {
var slice []string
for peer := range cm.directPeers {
slice = append(slice, peer)
}
resultChan <- slice
return false
}
return <-resultChan
}
func NewTrustedSubnetsSlice(trustedSubnets []*net.IPNet) []string {
trustedSubnetStrs := []string{}
for _, trustedSubnet := range trustedSubnets {
trustedSubnetStrs = append(trustedSubnetStrs, trustedSubnet.String())
}
return trustedSubnetStrs
}
+45
View File
@@ -0,0 +1,45 @@
package mesh
type SurrogateGossipData struct {
messages [][]byte
}
func NewSurrogateGossipData(msg []byte) *SurrogateGossipData {
return &SurrogateGossipData{messages: [][]byte{msg}}
}
func (d *SurrogateGossipData) Encode() [][]byte {
return d.messages
}
func (d *SurrogateGossipData) Merge(other GossipData) GossipData {
o := other.(*SurrogateGossipData)
messages := make([][]byte, 0, len(d.messages)+len(o.messages))
messages = append(messages, d.messages...)
messages = append(messages, o.messages...)
return &SurrogateGossipData{messages: messages}
}
// SurrogateGossiper ignores unicasts and relays broadcasts and gossips.
type SurrogateGossiper struct{}
func (*SurrogateGossiper) OnGossipUnicast(sender PeerName, msg []byte) error {
return nil
}
func (*SurrogateGossiper) OnGossipBroadcast(_ PeerName, update []byte) (GossipData, error) {
return NewSurrogateGossipData(update), nil
}
func (*SurrogateGossiper) Gossip() GossipData {
return nil
}
func (*SurrogateGossiper) OnGossip(update []byte) (GossipData, error) {
return NewSurrogateGossipData(update), nil
}
var (
surrogateGossiper SurrogateGossiper
)
+42
View File
@@ -0,0 +1,42 @@
package mesh
import (
"time"
)
type TokenBucket struct {
capacity int64 // Maximum capacity of bucket
tokenInterval time.Duration // Token replenishment rate
refillDuration time.Duration // Time to refill from empty
earliestUnspentToken time.Time
}
func NewTokenBucket(capacity int64, tokenInterval time.Duration) *TokenBucket {
tb := TokenBucket{
capacity: capacity,
tokenInterval: tokenInterval,
refillDuration: tokenInterval * time.Duration(capacity)}
tb.earliestUnspentToken = tb.capacityToken()
return &tb
}
func (tb *TokenBucket) Wait() {
// If earliest unspent token is in the future, sleep until then
time.Sleep(tb.earliestUnspentToken.Sub(time.Now()))
// Alternatively, enforce bucket capacity if necessary
capacityToken := tb.capacityToken()
if tb.earliestUnspentToken.Before(capacityToken) {
tb.earliestUnspentToken = capacityToken
}
// 'Remove' a token from the bucket
tb.earliestUnspentToken = tb.earliestUnspentToken.Add(tb.tokenInterval)
}
// Determine the historic token timestamp representing a full bucket
func (tb *TokenBucket) capacityToken() time.Time {
return time.Now().Add(-tb.refillDuration).Truncate(tb.tokenInterval)
}
-323
View File
@@ -1,323 +0,0 @@
package nameserver
import (
"container/heap"
"errors"
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
"math"
"math/rand"
"sync"
"time"
)
var (
errInvalidCapacity = errors.New("Invalid cache capacity")
errCouldNotResolve = errors.New("Could not resolve")
errTimeout = errors.New("Timeout while waiting for resolution")
errNoLocalReplies = errors.New("No local replies")
)
const (
defPendingTimeout int = 5 // timeout for a resolution
)
const nullTTL = 0 // a null TTL
type entryStatus uint8
const (
stPending entryStatus = iota // someone is waiting for the resolution
stResolved entryStatus = iota // resolved
)
const (
CacheNoLocalReplies uint8 = 1 << iota // not found in local network (stored in the cache so we skip another local lookup or some time)
)
// shuffleAnswers reorders answers for very basic load balancing
func shuffleAnswers(answers []dns.RR) []dns.RR {
n := len(answers)
if n > 1 {
rand.Seed(time.Now().UTC().UnixNano())
for i := 0; i < n; i++ {
r := i + rand.Intn(n-i)
answers[r], answers[i] = answers[i], answers[r]
}
}
return answers
}
// a cache entry
type cacheEntry struct {
Status entryStatus // status of the entry
Flags uint8 // some extra flags
ReplyLen int
question dns.Question
protocol dnsProtocol
reply dns.Msg
validUntil time.Time // obtained from the reply and stored here for convenience/speed
putTime time.Time
index int // for fast lookups in the heap
}
func newCacheEntry(question *dns.Question, now time.Time) *cacheEntry {
e := &cacheEntry{
Status: stPending,
validUntil: now.Add(time.Second * time.Duration(defPendingTimeout)),
question: *question,
index: -1,
}
return e
}
// Get a copy of the reply stored in the entry, but with some values adjusted like the TTL
func (e *cacheEntry) getReply(request *dns.Msg, maxLen int, now time.Time) (*dns.Msg, error) {
if e.Status != stResolved {
return nil, nil
}
// if the reply has expired or is invalid, force the caller to start a new resolution
if e.hasExpired(now) {
return nil, nil
}
if e.Flags&CacheNoLocalReplies != 0 {
return nil, errNoLocalReplies
}
if e.ReplyLen >= maxLen {
Debug.Printf("[cache msgid %d] returning truncated reponse: %d > %d", request.MsgHdr.Id, e.ReplyLen, maxLen)
return makeTruncatedReply(request), nil
}
// create a copy of the reply, with values for this particular query
reply := e.reply.Copy()
reply.SetReply(request)
// adjust the TTLs
passedSecs := uint32(now.Sub(e.putTime).Seconds())
for _, rr := range reply.Answer {
hdr := rr.Header()
ttl := hdr.Ttl
if passedSecs < ttl {
hdr.Ttl = ttl - passedSecs
} else {
return nil, nil // it is expired: do not spend more time and return nil...
}
}
reply.Rcode = e.reply.Rcode
reply.Authoritative = true
// shuffle the values, etc...
reply.Answer = shuffleAnswers(reply.Answer)
return reply, nil
}
func (e cacheEntry) hasExpired(now time.Time) bool {
return e.validUntil.Before(now) || e.validUntil == now
}
// set the reply for the entry
// returns True if the entry has changed the validUntil time
func (e *cacheEntry) setReply(reply *dns.Msg, ttl int, flags uint8, now time.Time) bool {
var prevValidUntil time.Time
if e.Status == stResolved {
if reply != nil {
Debug.Printf("[cache msgid %d] replacing response in cache", reply.MsgHdr.Id)
}
prevValidUntil = e.validUntil
}
e.Status = stResolved
e.Flags = flags
e.putTime = now
if ttl != nullTTL {
e.validUntil = now.Add(time.Second * time.Duration(ttl))
} else if reply != nil {
// calculate the validUntil from the reply TTL
var minTTL uint32 = math.MaxUint32
for _, rr := range reply.Answer {
ttl := rr.Header().Ttl
if ttl < minTTL {
minTTL = ttl // TODO: improve the minTTL calculation (maybe we should skip some RRs)
}
}
e.validUntil = now.Add(time.Second * time.Duration(minTTL))
}
if reply != nil {
e.reply = *reply
e.ReplyLen = reply.Len()
}
return (prevValidUntil != e.validUntil)
}
//////////////////////////////////////////////////////////////////////////////////////
// An entriesPtrHeap is a min-heap of cache entries.
type entriesPtrsHeap []*cacheEntry
func (h entriesPtrsHeap) Len() int { return len(h) }
func (h entriesPtrsHeap) Less(i, j int) bool { return h[i].validUntil.Before(h[j].validUntil) }
func (h entriesPtrsHeap) Swap(i, j int) {
h[i], h[j] = h[j], h[i]
h[i].index = i
h[j].index = j
}
func (h *entriesPtrsHeap) Push(x interface{}) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
n := len(*h)
entry := x.(*cacheEntry)
entry.index = n
*h = append(*h, entry)
}
func (h *entriesPtrsHeap) Pop() interface{} {
old := *h
n := len(old)
item := old[n-1]
item.index = -1 // for safety
*h = old[0 : n-1]
return item
}
//////////////////////////////////////////////////////////////////////////////////////
type cacheKey dns.Question
type entries map[cacheKey]*cacheEntry
// Cache is a thread-safe fixed capacity LRU cache.
type Cache struct {
Capacity int
entries entries
entriesH entriesPtrsHeap // len(entriesH) <= len(entries), as pending entries can be in entries but not in entriesH
lock sync.RWMutex
}
// NewCache creates a cache of the given capacity
func NewCache(capacity int) (*Cache, error) {
if capacity <= 0 {
return nil, errInvalidCapacity
}
c := &Cache{
Capacity: capacity,
entries: make(entries, capacity),
}
heap.Init(&c.entriesH)
return c, nil
}
// Clear removes all the entries in the cache
func (c *Cache) Clear() {
c.lock.Lock()
defer c.lock.Unlock()
c.entries = make(entries, c.Capacity)
heap.Init(&c.entriesH)
}
// Purge removes the old elements in the cache
func (c *Cache) Purge(now time.Time) {
c.lock.Lock()
defer c.lock.Unlock()
for i, entry := range c.entriesH {
if entry.hasExpired(now) {
heap.Remove(&c.entriesH, i)
delete(c.entries, cacheKey(entry.question))
} else {
return // all remaining entries must be still valid...
}
}
}
// Add adds a reply to the cache.
func (c *Cache) Put(request *dns.Msg, reply *dns.Msg, ttl int, flags uint8, now time.Time) int {
c.lock.Lock()
defer c.lock.Unlock()
question := request.Question[0]
key := cacheKey(question)
ent, found := c.entries[key]
if found {
updated := ent.setReply(reply, ttl, flags, now)
if updated {
heap.Fix(&c.entriesH, ent.index)
}
} else {
// If we will add a new item and the capacity has been exceeded, make some room...
if len(c.entriesH) >= c.Capacity {
lowestEntry := heap.Pop(&c.entriesH).(*cacheEntry)
delete(c.entries, cacheKey(lowestEntry.question))
}
ent = newCacheEntry(&question, now)
ent.setReply(reply, ttl, flags, now)
heap.Push(&c.entriesH, ent)
c.entries[key] = ent
}
return ent.ReplyLen
}
// Look up for a question's reply from the cache.
// If no reply is stored in the cache, it returns a `nil` reply and no error. The caller can then `Wait()`
// for another goroutine `Put`ing a reply in the cache.
func (c *Cache) Get(request *dns.Msg, maxLen int, now time.Time) (reply *dns.Msg, err error) {
c.lock.Lock()
defer c.lock.Unlock()
question := request.Question[0]
key := cacheKey(question)
if ent, found := c.entries[key]; found {
reply, err = ent.getReply(request, maxLen, now)
if ent.hasExpired(now) {
Debug.Printf("[cache msgid %d] expired: removing", request.MsgHdr.Id)
if ent.index > 0 {
heap.Remove(&c.entriesH, ent.index)
}
delete(c.entries, key)
reply = nil
}
} else {
// we are the first asking for this name: create an entry with no reply... the caller must wait
Debug.Printf("[cache msgid %d] addind in pending state", request.MsgHdr.Id)
c.entries[key] = newCacheEntry(&question, now)
}
return
}
// Remove removes the provided question from the cache.
func (c *Cache) Remove(question *dns.Question) {
c.lock.Lock()
defer c.lock.Unlock()
key := cacheKey(*question)
if entry, found := c.entries[key]; found {
if entry.index > 0 {
heap.Remove(&c.entriesH, entry.index)
}
delete(c.entries, key)
}
}
// Len returns the number of entries in the cache.
func (c *Cache) Len() int {
c.lock.RLock()
defer c.lock.RUnlock()
return len(c.entries)
}
-206
View File
@@ -1,206 +0,0 @@
package nameserver
import (
"fmt"
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
wt "github.com/weaveworks/weave/testing"
"net"
"testing"
"time"
)
// Check that the cache keeps its intended capacity constant
func TestCacheLength(t *testing.T) {
InitDefaultLogging(true)
const cacheLen = 128
l, err := NewCache(cacheLen)
wt.AssertNoErr(t, err)
insTime := time.Now()
t.Logf("Inserting 256 questions in the cache at '%s', with TTL from 0 to 255", insTime)
for i := 0; i < cacheLen*2; i++ {
questionMsg := new(dns.Msg)
questionMsg.SetQuestion(fmt.Sprintf("name%d", i), dns.TypeA)
questionMsg.RecursionDesired = true
question := &questionMsg.Question[0]
ip := net.ParseIP(fmt.Sprintf("10.0.1.%d", i))
ips := []net.IP{ip}
reply := makeAddressReply(questionMsg, question, ips)
reply.Answer[0].Header().Ttl = uint32(i)
l.Put(questionMsg, reply, 0, 0, insTime)
}
wt.AssertEqualInt(t, l.Len(), cacheLen, "cache length")
minExpectedTime := insTime.Add(time.Duration(cacheLen) * time.Second)
t.Logf("Checking all remaining entries expire after insert_time + %d secs='%s'", cacheLen, minExpectedTime)
for _, entry := range l.entries {
if entry.validUntil.Before(minExpectedTime) {
t.Fatalf("Entry valid until %s", entry.validUntil)
}
}
}
// Check that the cache entries are ok
func TestCacheEntries(t *testing.T) {
InitDefaultLogging(true)
Info.Println("Checking cache consistency")
const cacheLen = 128
l, err := NewCache(cacheLen)
wt.AssertNoErr(t, err)
questionMsg := new(dns.Msg)
questionMsg.SetQuestion("some.name", dns.TypeA)
questionMsg.RecursionDesired = true
question := &questionMsg.Question[0]
t.Logf("Trying to get a name")
resp, err := l.Get(questionMsg, minUDPSize, time.Now())
wt.AssertNoErr(t, err)
if resp != nil {
t.Logf("Got\n%s", resp)
t.Fatalf("ERROR: Did not expect a reponse from Get() yet")
}
t.Logf("Trying to get it again")
resp, err = l.Get(questionMsg, minUDPSize, time.Now())
wt.AssertNoErr(t, err)
if resp != nil {
t.Logf("Got\n%s", resp)
t.Fatalf("ERROR: Did not expect a reponse from Get() yet")
}
t.Logf("Inserting the reply")
reply1 := makeAddressReply(questionMsg, question, []net.IP{net.ParseIP("10.0.1.1")})
l.Put(questionMsg, reply1, nullTTL, 0, time.Now())
timeGet1 := time.Now()
t.Logf("Checking we can Get() the reply now")
resp, err = l.Get(questionMsg, minUDPSize, timeGet1)
wt.AssertNoErr(t, err)
wt.AssertTrue(t, resp != nil, "reponse from Get()")
t.Logf("Received '%s'", resp.Answer[0])
wt.AssertType(t, resp.Answer[0], (*dns.A)(nil), "DNS record")
ttlGet1 := resp.Answer[0].Header().Ttl
timeGet2 := timeGet1.Add(time.Duration(1) * time.Second)
t.Logf("Checking that a second Get(), after 1 second, gets the same result, but with reduced TTL")
resp, err = l.Get(questionMsg, minUDPSize, timeGet2)
wt.AssertNoErr(t, err)
wt.AssertTrue(t, resp != nil, "reponse from a second Get()")
t.Logf("Received '%s'", resp.Answer[0])
wt.AssertType(t, resp.Answer[0], (*dns.A)(nil), "DNS record")
ttlGet2 := resp.Answer[0].Header().Ttl
wt.AssertEqualInt(t, int(ttlGet1-ttlGet2), 1, "TTL difference")
timeGet3 := timeGet1.Add(time.Duration(localTTL) * time.Second)
t.Logf("Checking that a third Get(), after %d second, gets no result", localTTL)
resp, err = l.Get(questionMsg, minUDPSize, timeGet3)
wt.AssertNoErr(t, err)
if resp != nil {
t.Logf("Got\n%s", resp)
t.Fatalf("ERROR: Did NOT expect a reponse from the second Get()")
}
t.Logf("Checking that an Remove() results in Get() returning nothing")
replyTemp := makeAddressReply(questionMsg, question, []net.IP{net.ParseIP("10.0.9.9")})
l.Put(questionMsg, replyTemp, nullTTL, 0, time.Now())
lenBefore := l.Len()
l.Remove(question)
wt.AssertEqualInt(t, l.Len(), lenBefore-1, "cache length")
l.Remove(question) // do it again: should have no effect...
wt.AssertEqualInt(t, l.Len(), lenBefore-1, "cache length")
resp, err = l.Get(questionMsg, minUDPSize, timeGet1)
wt.AssertNoErr(t, err)
wt.AssertTrue(t, resp == nil, "reponse from the Get() after a Remove()")
t.Logf("Inserting a two replies for the same query")
timePut2 := time.Now()
reply2 := makeAddressReply(questionMsg, question, []net.IP{net.ParseIP("10.0.1.2")})
l.Put(questionMsg, reply2, nullTTL, 0, timePut2)
timePut3 := timePut2.Add(time.Duration(1) * time.Second)
reply3 := makeAddressReply(questionMsg, question, []net.IP{net.ParseIP("10.0.1.3")})
l.Put(questionMsg, reply3, nullTTL, 0, timePut3)
t.Logf("Checking we get the last one...")
resp, err = l.Get(questionMsg, minUDPSize, timePut3)
wt.AssertNoErr(t, err)
wt.AssertTrue(t, resp != nil, "reponse from the Get()")
t.Logf("Received '%s'", resp.Answer[0])
wt.AssertType(t, resp.Answer[0], (*dns.A)(nil), "DNS record")
wt.AssertEqualString(t, resp.Answer[0].(*dns.A).A.String(), "10.0.1.3", "IP address")
wt.AssertEqualInt(t, int(resp.Answer[0].Header().Ttl), int(localTTL), "TTL")
resp, err = l.Get(questionMsg, minUDPSize, timePut3.Add(time.Duration(localTTL-1)*time.Second))
wt.AssertNoErr(t, err)
wt.AssertTrue(t, resp != nil, "reponse from the Get()")
t.Logf("Received '%s'", resp.Answer[0])
wt.AssertType(t, resp.Answer[0], (*dns.A)(nil), "DNS record")
wt.AssertEqualString(t, resp.Answer[0].(*dns.A).A.String(), "10.0.1.3", "IP address")
wt.AssertEqualInt(t, int(resp.Answer[0].Header().Ttl), 1, "TTL")
t.Logf("Checking we get empty replies when they are expired...")
lenBefore = l.Len()
resp, err = l.Get(questionMsg, minUDPSize, timePut3.Add(time.Duration(localTTL)*time.Second))
wt.AssertNoErr(t, err)
if resp != nil {
t.Logf("Got\n%s", resp.Answer[0])
t.Fatalf("ERROR: Did NOT expect a reponse from the Get()")
}
wt.AssertEqualInt(t, l.Len(), lenBefore-1, "cache length (after getting an expired entry)")
questionMsg2 := new(dns.Msg)
questionMsg2.SetQuestion("some.other.name", dns.TypeA)
questionMsg2.RecursionDesired = true
question2 := &questionMsg2.Question[0]
t.Logf("Trying to Get() a name")
resp, err = l.Get(questionMsg2, minUDPSize, time.Now())
wt.AssertNoErr(t, err)
wt.AssertNil(t, resp, "reponse from Get() yet")
t.Logf("Checking that an Remove() between Get() and Put() does not break things")
replyTemp2 := makeAddressReply(questionMsg2, question2, []net.IP{net.ParseIP("10.0.9.9")})
l.Remove(question2)
l.Put(questionMsg2, replyTemp2, nullTTL, 0, time.Now())
resp, err = l.Get(questionMsg2, minUDPSize, time.Now())
wt.AssertNoErr(t, err)
wt.AssertNotNil(t, resp, "reponse from Get()")
questionMsg3 := new(dns.Msg)
questionMsg3.SetQuestion("some.other.name", dns.TypeA)
questionMsg3.RecursionDesired = true
question3 := &questionMsg3.Question[0]
t.Logf("Checking that a entry with CacheNoLocalReplies return an error")
timePut3 = time.Now()
l.Put(questionMsg3, nil, nullTTL, CacheNoLocalReplies, timePut3)
resp, err = l.Get(questionMsg3, minUDPSize, timePut3)
wt.AssertNil(t, resp, "Get() response with CacheNoLocalReplies")
wt.AssertNotNil(t, err, "Get() error with CacheNoLocalReplies")
timeExpiredGet3 := timePut3.Add(time.Second * time.Duration(negLocalTTL+1))
t.Logf("Checking that we get an expired response after %f seconds", timeExpiredGet3.Sub(timePut3).Seconds())
resp, err = l.Get(questionMsg3, minUDPSize, timeExpiredGet3)
wt.AssertNil(t, resp, "expired Get() response with CacheNoLocalReplies")
wt.AssertNil(t, err, "expired Get() error with CacheNoLocalReplies")
l.Remove(question3)
t.Logf("Checking that Put&Get with CacheNoLocalReplies with a Remove in the middle returns nothing")
l.Put(questionMsg3, nil, nullTTL, CacheNoLocalReplies, time.Now())
l.Remove(question3)
resp, err = l.Get(questionMsg3, minUDPSize, time.Now())
wt.AssertNil(t, resp, "Get() reponse with CacheNoLocalReplies")
wt.AssertNil(t, err, "Get() error with CacheNoLocalReplies")
}
+271 -65
View File
@@ -1,92 +1,298 @@
package nameserver
import (
"github.com/miekg/dns"
"bytes"
"fmt"
"math/rand"
"net"
"sort"
"strings"
"time"
"github.com/miekg/dns"
"github.com/weaveworks/weave/net/address"
)
const (
localTTL uint32 = 30 // somewhat arbitrary; we don't expect anyone downstream to cache results
negLocalTTL = 30 // TTL for negative local resolutions
minUDPSize = 512
maxUDPSize = 65535
topDomain = "."
reverseDNSdomain = "in-addr.arpa."
etcResolvConf = "/etc/resolv.conf"
udpBuffSize = uint16(4096)
minUDPSize = 512
DefaultListenAddress = "0.0.0.0:53"
DefaultTTL = 1
DefaultClientTimeout = 5 * time.Second
)
func makeHeader(r *dns.Msg, q *dns.Question) *dns.RR_Header {
return &dns.RR_Header{
Name: q.Name, Rrtype: q.Qtype,
Class: dns.ClassINET, Ttl: localTTL}
type DNSServer struct {
ns *Nameserver
domain string
ttl uint32
address string
servers []*dns.Server
upstream *dns.ClientConfig
tcpClient *dns.Client
udpClient *dns.Client
}
func makeReply(r *dns.Msg, as []dns.RR) *dns.Msg {
m := new(dns.Msg)
m.SetReply(r)
m.RecursionAvailable = true
m.Answer = as
return m
func filter(ss []string, s string) []string {
for i := 0; i < len(ss); {
if ss[i] == s {
ss = append(ss[:i], ss[i+1:]...)
continue
}
i++
}
return ss
}
func makeTruncatedReply(r *dns.Msg) *dns.Msg {
// for truncated response, we create a minimal reply with the Truncated bit set
reply := new(dns.Msg)
reply.SetReply(r)
reply.Truncated = true
return reply
func NewDNSServer(ns *Nameserver, domain, address, effectiveAddress string, ttl uint32, clientTimeout time.Duration) (*DNSServer, error) {
s := &DNSServer{
ns: ns,
domain: dns.Fqdn(domain),
ttl: ttl,
address: address,
tcpClient: &dns.Client{Net: "tcp", ReadTimeout: clientTimeout},
udpClient: &dns.Client{Net: "udp", ReadTimeout: clientTimeout, UDPSize: udpBuffSize},
}
var err error
if s.upstream, err = dns.ClientConfigFromFile(etcResolvConf); err != nil {
return nil, err
}
if s.upstream != nil {
s.upstream.Servers = filter(s.upstream.Servers, effectiveAddress)
}
err = s.listen(address)
return s, err
}
func makeAddressReply(r *dns.Msg, q *dns.Question, addrs []net.IP) *dns.Msg {
answers := make([]dns.RR, len(addrs))
header := makeHeader(r, q)
count := 0
for _, addr := range addrs {
switch q.Qtype {
case dns.TypeA:
if ip4 := addr.To4(); ip4 != nil {
answers[count] = &dns.A{Hdr: *header, A: addr}
count++
}
case dns.TypeAAAA:
if ip4 := addr.To4(); ip4 == nil {
answers[count] = &dns.AAAA{Hdr: *header, AAAA: addr}
count++
}
func (d *DNSServer) String() string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "WeaveDNS (%s)\n", d.ns.ourName)
fmt.Fprintf(&buf, " listening on %s, for domain %s\n", d.address, d.domain)
fmt.Fprintf(&buf, " response ttl %d\n", d.ttl)
return buf.String()
}
func (d *DNSServer) listen(address string) error {
udpListener, err := net.ListenPacket("udp", address)
if err != nil {
return err
}
udpServer := &dns.Server{PacketConn: udpListener, Handler: d.createMux(d.udpClient, minUDPSize)}
tcpListener, err := net.Listen("tcp", address)
if err != nil {
udpServer.Shutdown()
return err
}
tcpServer := &dns.Server{Listener: tcpListener, Handler: d.createMux(d.tcpClient, -1)}
d.servers = []*dns.Server{udpServer, tcpServer}
return nil
}
func (d *DNSServer) ActivateAndServe() {
for _, server := range d.servers {
go func(server *dns.Server) {
server.ActivateAndServe()
}(server)
}
}
func (d *DNSServer) Stop() error {
for _, server := range d.servers {
if err := server.Shutdown(); err != nil {
return err
}
}
return makeReply(r, answers[:count])
return nil
}
func makePTRReply(r *dns.Msg, q *dns.Question, names []string) *dns.Msg {
answers := make([]dns.RR, len(names))
header := makeHeader(r, q)
for i, name := range names {
answers[i] = &dns.PTR{Hdr: *header, Ptr: name}
type handler struct {
*DNSServer
maxResponseSize int
client *dns.Client
}
func (d *DNSServer) createMux(client *dns.Client, defaultMaxResponseSize int) *dns.ServeMux {
m := dns.NewServeMux()
h := &handler{
DNSServer: d,
maxResponseSize: defaultMaxResponseSize,
client: client,
}
return makeReply(r, answers)
}
func makeDNSFailResponse(r *dns.Msg) *dns.Msg {
m := new(dns.Msg)
m.SetReply(r)
m.RecursionAvailable = true
m.Rcode = dns.RcodeNameError
m.HandleFunc(d.domain, h.handleLocal)
m.HandleFunc(reverseDNSdomain, h.handleReverse)
m.HandleFunc(topDomain, h.handleRecursive)
return m
}
func makeDNSNotImplResponse(r *dns.Msg) *dns.Msg {
m := new(dns.Msg)
m.SetReply(r)
m.RecursionAvailable = true
m.Rcode = dns.RcodeNotImplemented
return m
func (h *handler) handleLocal(w dns.ResponseWriter, req *dns.Msg) {
h.ns.debugf("local request: %+v", *req)
if len(req.Question) != 1 || req.Question[0].Qtype != dns.TypeA {
h.nameError(w, req)
return
}
hostname := dns.Fqdn(req.Question[0].Name)
if strings.Count(hostname, ".") == 1 {
hostname = hostname + h.domain
}
addrs := h.ns.Lookup(hostname)
if len(addrs) == 0 {
h.nameError(w, req)
return
}
header := dns.RR_Header{
Name: req.Question[0].Name,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: h.ttl,
}
answers := make([]dns.RR, len(addrs))
for i, addr := range addrs {
ip := addr.IP4()
answers[i] = &dns.A{Hdr: header, A: ip}
}
shuffleAnswers(&answers)
h.respond(w, h.makeResponse(req, answers))
}
// get the maximum UDP-reply length
func getMaxReplyLen(r *dns.Msg, proto dnsProtocol) int {
maxLen := minUDPSize
if proto == protTCP {
maxLen = maxUDPSize
} else if opt := r.IsEdns0(); opt != nil {
maxLen = int(opt.UDPSize())
func (h *handler) handleReverse(w dns.ResponseWriter, req *dns.Msg) {
h.ns.debugf("reverse request: %+v", *req)
if len(req.Question) != 1 || req.Question[0].Qtype != dns.TypePTR {
h.nameError(w, req)
return
}
ipStr := strings.TrimSuffix(req.Question[0].Name, "."+reverseDNSdomain)
ip, err := address.ParseIP(ipStr)
if err != nil {
h.nameError(w, req)
return
}
hostname, err := h.ns.ReverseLookup(ip.Reverse())
if err != nil {
h.handleRecursive(w, req)
return
}
header := dns.RR_Header{
Name: req.Question[0].Name,
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: h.ttl,
}
answers := []dns.RR{&dns.PTR{
Hdr: header,
Ptr: hostname,
}}
h.respond(w, h.makeResponse(req, answers))
}
func (h *handler) handleRecursive(w dns.ResponseWriter, req *dns.Msg) {
h.ns.debugf("recursive request: %+v", *req)
// Resolve unqualified names locally
if len(req.Question) == 1 && req.Question[0].Qtype == dns.TypeA {
hostname := dns.Fqdn(req.Question[0].Name)
if strings.Count(hostname, ".") == 1 {
h.handleLocal(w, req)
return
}
}
for _, server := range h.upstream.Servers {
reqCopy := req.Copy()
reqCopy.Id = dns.Id()
response, _, err := h.client.Exchange(reqCopy, fmt.Sprintf("%s:%s", server, h.upstream.Port))
if (err != nil && err != dns.ErrTruncated) || response == nil {
h.ns.debugf("error trying %s: %v", server, err)
continue
}
response.Id = req.Id
if h.responseTooBig(req, response) {
response.Compress = true
}
h.respond(w, response)
return
}
h.respond(w, h.makeErrorResponse(req, dns.RcodeServerFailure))
}
func (h *handler) makeResponse(req *dns.Msg, answers []dns.RR) *dns.Msg {
response := &dns.Msg{}
response.SetReply(req)
response.RecursionAvailable = true
response.Authoritative = true
response.Answer = answers
if !h.responseTooBig(req, response) {
return response
}
// search for smallest i that is too big
maxSize := h.getMaxResponseSize(req)
i := sort.Search(len(answers), func(i int) bool {
// return true if too big
response.Answer = answers[:i+1]
return response.Len() > maxSize
})
response.Answer = answers[:i]
if i < len(answers) {
response.Truncated = true
}
return response
}
func (h *handler) makeErrorResponse(req *dns.Msg, code int) *dns.Msg {
response := &dns.Msg{}
response.SetReply(req)
response.RecursionAvailable = true
response.Rcode = code
return response
}
func (h *handler) responseTooBig(req, response *dns.Msg) bool {
return len(response.Answer) > 1 && h.maxResponseSize > 0 && response.Len() > h.getMaxResponseSize(req)
}
func (h *handler) respond(w dns.ResponseWriter, response *dns.Msg) {
h.ns.debugf("response: %+v", response)
if err := w.WriteMsg(response); err != nil {
h.ns.infof("error responding: %v", err)
}
}
func (h *handler) nameError(w dns.ResponseWriter, req *dns.Msg) {
h.respond(w, h.makeErrorResponse(req, dns.RcodeNameError))
}
func (h *handler) getMaxResponseSize(req *dns.Msg) int {
if opt := req.IsEdns0(); opt != nil {
return int(opt.UDPSize())
}
return h.maxResponseSize
}
func shuffleAnswers(answers *[]dns.RR) {
if len(*answers) <= 1 {
return
}
for i := range *answers {
j := rand.Intn(i + 1)
(*answers)[i], (*answers)[j] = (*answers)[j], (*answers)[i]
}
return maxLen
}
+172
View File
@@ -0,0 +1,172 @@
package nameserver
import (
"fmt"
"math/rand"
"net"
"strconv"
"testing"
"time"
"github.com/miekg/dns"
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
)
func startServer(t *testing.T, upstream *dns.ClientConfig) (*DNSServer, *Nameserver, int, int) {
peername, err := mesh.PeerNameFromString("00:00:00:02:00:00")
require.Nil(t, err)
nameserver := New(peername, "", func(mesh.PeerName) bool { return true })
dnsserver, err := NewDNSServer(nameserver, "weave.local.", "0.0.0.0:0", "", 30, 5*time.Second)
require.Nil(t, err)
udpPort := dnsserver.servers[0].PacketConn.LocalAddr().(*net.UDPAddr).Port
tcpPort := dnsserver.servers[1].Listener.Addr().(*net.TCPAddr).Port
if upstream != nil {
dnsserver.upstream = upstream
}
go dnsserver.ActivateAndServe()
return dnsserver, nameserver, udpPort, tcpPort
}
func TestTruncation(t *testing.T) {
//common.SetLogLevel("debug")
dnsserver, nameserver, udpPort, tcpPort := startServer(t, nil)
defer dnsserver.Stop()
// Add 100 mappings to nameserver
addrs := []address.Address{}
for i := address.Address(0); i < 100; i++ {
addrs = append(addrs, i)
nameserver.AddEntry("foo.weave.local.", "", mesh.UnknownPeerName, i)
}
doRequest := func(client *dns.Client, request *dns.Msg, port int, expectedErr error) *dns.Msg {
request.SetQuestion("foo.weave.local.", dns.TypeA)
response, _, err := client.Exchange(request, fmt.Sprintf("127.0.0.1:%d", port))
require.Equal(t, expectedErr, err)
return response
}
// do a udp query, ensure we get a truncated response
{
udpClient := dns.Client{Net: "udp", UDPSize: minUDPSize}
response := doRequest(&udpClient, &dns.Msg{}, udpPort, dns.ErrTruncated)
require.True(t, response.MsgHdr.Truncated)
require.True(t, len(response.Answer) < 100)
}
// do a udp query with big size, ensure we don't get a truncated response
{
udpClient := dns.Client{Net: "udp", UDPSize: 65535}
request := &dns.Msg{}
request.SetEdns0(65535, false)
response := doRequest(&udpClient, request, udpPort, nil)
require.False(t, response.MsgHdr.Truncated)
require.Equal(t, len(response.Answer), 100)
}
// do a tcp query, ensure we don't get a truncated response
{
tcpClient := dns.Client{Net: "tcp"}
response := doRequest(&tcpClient, &dns.Msg{}, tcpPort, nil)
require.False(t, response.MsgHdr.Truncated)
require.Equal(t, len(response.Answer), 100)
}
}
func TestTruncateResponse(t *testing.T) {
header := dns.RR_Header{
Name: "host.domain.com",
Rrtype: dns.TypePTR,
Class: dns.ClassINET,
Ttl: 30,
}
for i := 0; i < 10000; i++ {
// generate a random answer set
numAnswers := 40 + rand.Intn(200)
answers := make([]dns.RR, numAnswers)
for j := 0; j < numAnswers; j++ {
answers[j] = &dns.A{Hdr: header, A: address.Address(j).IP4()}
}
// pick a random max size, truncate response to that, check it
maxSize := 512 + rand.Intn(2*512)
h := handler{maxResponseSize: maxSize}
response := h.makeResponse(&dns.Msg{}, answers)
require.True(t, response.Len() <= maxSize)
}
}
func TestRecursiveCompress(t *testing.T) {
const (
hostname = "foo.example."
maxSize = 512
)
// Construct a response that is >512 when uncompressed, <512 when compressed
response := dns.Msg{}
response.Authoritative = true
response.Answer = []dns.RR{}
header := dns.RR_Header{
Name: hostname,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 10,
}
for response.Len() <= maxSize {
ip := address.Address(rand.Uint32()).IP4()
response.Answer = append(response.Answer, &dns.A{Hdr: header, A: ip})
}
response.Compress = true
require.True(t, response.Len() <= maxSize)
// A dns server that returns the above response
var gotRequest = make(chan struct{}, 1)
handleRecursive := func(w dns.ResponseWriter, req *dns.Msg) {
gotRequest <- struct{}{}
require.Equal(t, req.Question[0].Name, hostname)
response.SetReply(req)
err := w.WriteMsg(&response)
require.Nil(t, err)
}
mux := dns.NewServeMux()
mux.HandleFunc(topDomain, handleRecursive)
udpListener, err := net.ListenPacket("udp", "0.0.0.0:0")
require.Nil(t, err)
udpServer := &dns.Server{PacketConn: udpListener, Handler: mux}
udpServerPort := udpListener.LocalAddr().(*net.UDPAddr).Port
go udpServer.ActivateAndServe()
defer udpServer.Shutdown()
// The weavedns server, pointed at the above server
dnsserver, _, udpPort, _ := startServer(t, &dns.ClientConfig{
Servers: []string{"127.0.0.1"},
Port: strconv.Itoa(udpServerPort),
Ndots: 1,
Timeout: 5,
Attempts: 2,
})
defer dnsserver.Stop()
// Now do lookup, check its what we expected.
// NB this doesn't really test golang's resolver behaves correctly, as I can't see
// a way to point golangs resolver at a specific hosts.
req := new(dns.Msg)
req.Id = dns.Id()
req.RecursionDesired = true
req.Question = make([]dns.Question, 1)
req.Question[0] = dns.Question{
Name: hostname,
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
}
c := new(dns.Client)
res, _, err := c.Exchange(req, fmt.Sprintf("127.0.0.1:%d", udpPort))
require.Nil(t, err)
require.True(t, len(gotRequest) > 0)
require.True(t, res.Len() > maxSize)
}
+282
View File
@@ -0,0 +1,282 @@
package nameserver
import (
"bytes"
"encoding/gob"
"fmt"
"sort"
"strings"
"time"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
)
var now = func() int64 { return time.Now().Unix() }
type Entry struct {
ContainerID string
Origin mesh.PeerName
Addr address.Address
Hostname string // as supplied
lHostname string // lowercased (not exported, so not encoded by gob)
Version int
Tombstone int64 // timestamp of when it was deleted
}
type Entries []Entry
type CaseSensitive Entries
type CaseInsensitive Entries
type SortableEntries interface {
sort.Interface
Get(i int) Entry
}
// Gossip messages are sorted in a case sensitive order...
func (es CaseSensitive) Len() int { return len(es) }
func (es CaseSensitive) Swap(i, j int) { es[i], es[j] = es[j], es[i] }
func (es CaseSensitive) Get(i int) Entry { return es[i] }
func (es CaseSensitive) Less(i, j int) bool { return es[i].less(&es[j]) }
// ... but we store entries in a case insensitive order.
func (es CaseInsensitive) Len() int { return len(es) }
func (es CaseInsensitive) Swap(i, j int) { es[i], es[j] = es[j], es[i] }
func (es CaseInsensitive) Get(i int) Entry { return es[i] }
func (es CaseInsensitive) Less(i, j int) bool { return es[i].insensitiveLess(&es[j]) }
func (e1 Entry) equal(e2 Entry) bool {
return e1.ContainerID == e2.ContainerID &&
e1.Origin == e2.Origin &&
e1.Addr == e2.Addr &&
e1.Hostname == e2.Hostname
}
func (e1 *Entry) less(e2 *Entry) bool {
// Entries are kept sorted by Hostname, Origin, ContainerID then address
switch {
case e1.Hostname != e2.Hostname:
return e1.Hostname < e2.Hostname
case e1.Origin != e2.Origin:
return e1.Origin < e2.Origin
case e1.ContainerID != e2.ContainerID:
return e1.ContainerID < e2.ContainerID
default:
return e1.Addr < e2.Addr
}
}
func (e1 *Entry) insensitiveLess(e2 *Entry) bool {
// Entries are kept sorted by Hostname, Origin, ContainerID then address
e1Hostname, e2Hostname := e1.lHostname, e2.lHostname
switch {
case e1Hostname != e2Hostname:
return e1Hostname < e2Hostname
case e1.Origin != e2.Origin:
return e1.Origin < e2.Origin
case e1.ContainerID != e2.ContainerID:
return e1.ContainerID < e2.ContainerID
default:
return e1.Addr < e2.Addr
}
}
// returns true to indicate a change
func (e1 *Entry) merge(e2 *Entry) bool {
// we know container id, origin, add and hostname are equal
if e2.Version > e1.Version {
e1.Version = e2.Version
e1.Tombstone = e2.Tombstone
return true
}
return false
}
func (e1 *Entry) String() string {
return fmt.Sprintf("%s -> %s", e1.Hostname, e1.Addr.String())
}
func (e1 *Entry) addLowercase() {
e1.lHostname = strings.ToLower(e1.Hostname)
}
func check(es SortableEntries) error {
if !sort.IsSorted(es) {
return fmt.Errorf("Not sorted!")
}
for i := 1; i < es.Len(); i++ {
if es.Get(i).equal(es.Get(i - 1)) {
return fmt.Errorf("Duplicate entry: %d:%v and %d:%v", i-1, es.Get(i-1), i, es.Get(i))
}
}
return nil
}
func checkAndPanic(es SortableEntries) {
if err := check(es); err != nil {
panic(err)
}
}
func (es *Entries) checkAndPanic() *Entries {
checkAndPanic(CaseInsensitive(*es))
return es
}
func (es *Entries) add(hostname, containerid string, origin mesh.PeerName, addr address.Address) Entry {
defer es.checkAndPanic().checkAndPanic()
entry := Entry{Hostname: hostname, lHostname: strings.ToLower(hostname),
Origin: origin, ContainerID: containerid, Addr: addr}
i := sort.Search(len(*es), func(i int) bool {
return !(*es)[i].insensitiveLess(&entry)
})
if i < len(*es) && (*es)[i].equal(entry) {
if (*es)[i].Tombstone > 0 {
(*es)[i].Tombstone = 0
(*es)[i].Version++
}
} else {
*es = append(*es, Entry{})
copy((*es)[i+1:], (*es)[i:])
(*es)[i] = entry
}
return (*es)[i]
}
func (es *Entries) merge(incoming Entries) Entries {
defer es.checkAndPanic().checkAndPanic()
incoming.checkAndPanic()
newEntries := Entries{}
i := 0
for _, entry := range incoming {
for i < len(*es) && (*es)[i].insensitiveLess(&entry) {
i++
}
if i < len(*es) && (*es)[i].equal(entry) {
if (*es)[i].merge(&entry) {
newEntries = append(newEntries, entry)
}
} else {
*es = append(*es, Entry{})
copy((*es)[i+1:], (*es)[i:])
(*es)[i] = entry
newEntries = append(newEntries, entry)
}
}
return newEntries
}
// f returning true means keep the entry.
func (es *Entries) tombstone(ourname mesh.PeerName, f func(*Entry) bool) Entries {
defer es.checkAndPanic().checkAndPanic()
tombstoned := Entries{}
for i, e := range *es {
if f(&e) && e.Origin == ourname {
e.Version++
e.Tombstone = now()
(*es)[i] = e
tombstoned = append(tombstoned, e)
}
}
return tombstoned
}
func (es *Entries) filter(f func(*Entry) bool) {
defer es.checkAndPanic().checkAndPanic()
i := 0
for _, e := range *es {
if !f(&e) {
continue
}
(*es)[i] = e
i++
}
*es = (*es)[:i]
}
func (es Entries) lookup(hostname string) Entries {
es.checkAndPanic()
lowerHostname := strings.ToLower(hostname)
i := sort.Search(len(es), func(i int) bool {
return es[i].lHostname >= lowerHostname
})
if i >= len(es) || es[i].lHostname != lowerHostname {
return Entries{}
}
j := sort.Search(len(es)-i, func(j int) bool {
return es[i+j].lHostname > lowerHostname
})
return es[i : i+j]
}
func (es Entries) first(f func(*Entry) bool) (*Entry, error) {
es.checkAndPanic()
for _, e := range es {
if f(&e) {
return &e, nil
}
}
return nil, fmt.Errorf("Not found")
}
func (es Entries) addLowercase() {
for i := range es {
es[i].addLowercase()
}
}
type GossipData struct {
Timestamp int64
Entries
}
func (g *GossipData) Merge(o mesh.GossipData) mesh.GossipData {
other := o.(*GossipData)
gossip := g.copy()
gossip.Entries.merge(other.Entries)
if gossip.Timestamp < other.Timestamp {
gossip.Timestamp = other.Timestamp
}
return gossip
}
func (g *GossipData) Decode(msg []byte) error {
if err := gob.NewDecoder(bytes.NewReader(msg)).Decode(g); err != nil {
return err
}
g.Entries.addLowercase() // lowercase strings not sent on the wire
sort.Sort(CaseInsensitive(g.Entries))
return nil
}
func (g *GossipData) Encode() [][]byte {
g2 := g.copy()
sort.Sort(CaseSensitive(g2.Entries))
buf := &bytes.Buffer{}
if err := gob.NewEncoder(buf).Encode(g2); err != nil {
panic(err)
}
return [][]byte{buf.Bytes()}
}
func (g *GossipData) copy() *GossipData {
g2 := &GossipData{Timestamp: g.Timestamp, Entries: make(Entries, len(g.Entries))}
copy(g2.Entries, g.Entries)
return g2
}
+123
View File
@@ -0,0 +1,123 @@
package nameserver
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
)
func l(es Entries) Entries {
es.addLowercase()
return es
}
func makeEntries(values string) Entries {
entries := make(Entries, len(values))
for i, c := range values {
entries[i] = Entry{Hostname: string(c)}
}
return l(entries)
}
func TestAdd(t *testing.T) {
oldNow := now
defer func() { now = oldNow }()
now = func() int64 { return 1234 }
entries := Entries{}
entries.add("A", "", mesh.UnknownPeerName, address.Address(0))
expected := l(Entries{
Entry{Hostname: "A", Origin: mesh.UnknownPeerName, Addr: address.Address(0)},
})
require.Equal(t, entries, expected)
entries.tombstone(mesh.UnknownPeerName, func(e *Entry) bool { return e.Hostname == "A" })
expected = l(Entries{
Entry{Hostname: "A", Origin: mesh.UnknownPeerName, Addr: address.Address(0), Version: 1, Tombstone: 1234},
})
require.Equal(t, entries, expected)
entries.add("A", "", mesh.UnknownPeerName, address.Address(0))
expected = l(Entries{
Entry{Hostname: "A", Origin: mesh.UnknownPeerName, Addr: address.Address(0), Version: 2},
})
require.Equal(t, entries, expected)
}
func TestMerge(t *testing.T) {
e1 := makeEntries("ACDF")
e2 := makeEntries("BEF")
diff := e1.merge(e2)
require.Equal(t, makeEntries("BE"), diff)
require.Equal(t, makeEntries("ABCDEF"), e1)
diff = e1.merge(e1)
require.Equal(t, Entries{}, diff)
}
func TestOldMerge(t *testing.T) {
e1 := l(Entries{Entry{Hostname: "A", Version: 0}})
diff := e1.merge(l(Entries{Entry{Hostname: "A", Version: 1}}))
require.Equal(t, l(Entries{Entry{Hostname: "A", Version: 1}}), diff)
require.Equal(t, l(Entries{Entry{Hostname: "A", Version: 1}}), e1)
diff = e1.merge(l(Entries{Entry{Hostname: "A", Version: 0}}))
require.Equal(t, Entries{}, diff)
require.Equal(t, l(Entries{Entry{Hostname: "A", Version: 1}}), e1)
}
func TestTombstone(t *testing.T) {
oldNow := now
defer func() { now = oldNow }()
now = func() int64 { return 1234 }
es := makeEntries("AB")
es.tombstone(mesh.UnknownPeerName, func(e *Entry) bool {
return e.Hostname == "B"
})
expected := l(Entries{
Entry{Hostname: "A"},
Entry{Hostname: "B", Version: 1, Tombstone: 1234},
})
require.Equal(t, expected, es)
}
func TestDelete(t *testing.T) {
es := makeEntries("AB")
es.filter(func(e *Entry) bool {
return e.Hostname != "A"
})
require.Equal(t, makeEntries("B"), es)
}
func TestLookup(t *testing.T) {
es := l(Entries{
Entry{Hostname: "A"},
Entry{Hostname: "B", ContainerID: "bar"},
Entry{Hostname: "B", ContainerID: "foo"},
Entry{Hostname: "C"},
})
have := es.lookup("B")
want := l(Entries{
Entry{Hostname: "B", ContainerID: "bar"},
Entry{Hostname: "B", ContainerID: "foo"},
})
require.Equal(t, have, want)
}
func TestGossipDataMerge(t *testing.T) {
g1 := GossipData{Entries: makeEntries("AcDf")}
g2 := GossipData{Entries: makeEntries("BEf")}
g3 := g1.Merge(&g2).(*GossipData)
require.Equal(t, GossipData{Entries: makeEntries("ABcDEf")}, *g3)
}
+71 -67
View File
@@ -1,95 +1,99 @@
package nameserver
import (
"encoding/json"
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
"log"
"net"
"net/http"
"github.com/weaveworks/weave/common/docker"
"github.com/weaveworks/weave/net/address"
)
func httpErrorAndLog(level *log.Logger, w http.ResponseWriter, msg string,
status int, logmsg string, logargs ...interface{}) {
http.Error(w, msg, status)
level.Printf("[http] "+logmsg, logargs...)
func (n *Nameserver) badRequest(w http.ResponseWriter, err error) {
http.Error(w, err.Error(), http.StatusBadRequest)
n.infof("%v", err)
}
func ListenHTTP(version string, server *DNSServer, domain string, db Zone, port int) {
muxRouter := mux.NewRouter()
muxRouter.Methods("GET").Path("/status").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "weave DNS", version)
fmt.Fprintln(w, server.Status())
func (n *Nameserver) HandleHTTP(router *mux.Router, dockerCli *docker.Client) {
router.Methods("GET").Path("/domain").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, n.domain)
})
muxRouter.Methods("PUT").Path("/name/{id:.+}/{ip:.+}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reqError := func(msg string, logmsg string, logargs ...interface{}) {
httpErrorAndLog(Warning, w, msg, http.StatusBadRequest, logmsg, logargs...)
}
vars := mux.Vars(r)
idStr := vars["id"]
ipStr := vars["ip"]
name := r.FormValue("fqdn")
if name == "" {
reqError("Invalid FQDN", "Invalid FQDN in request: %s, %s", r.URL, r.Form)
router.Methods("PUT").Path("/name/{container}/{ip}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var (
vars = mux.Vars(r)
container = vars["container"]
ipStr = vars["ip"]
hostname = dns.Fqdn(r.FormValue("fqdn"))
ip, err = address.ParseIP(ipStr)
)
if err != nil {
n.badRequest(w, err)
return
}
ip := net.ParseIP(ipStr)
if ip == nil {
reqError("Invalid IP", "Invalid IP in request: %s", ipStr)
if !dns.IsSubDomain(n.domain, hostname) {
n.infof("Ignoring registration %s %s %s (not a subdomain of %s)", hostname, ipStr, container, n.domain)
return
}
if dns.IsSubDomain(domain, name) {
Info.Printf("[http] Adding %s -> %s", name, ipStr)
if err := db.AddRecord(idStr, name, ip); err != nil {
if _, ok := err.(DuplicateError); !ok {
httpErrorAndLog(
Error, w, "Internal error", http.StatusInternalServerError,
"Unexpected error from DB: %s", err)
return
} // oh, I already know this. whatever.
if err := n.AddEntry(hostname, container, n.ourName, ip); err != nil {
n.badRequest(w, fmt.Errorf("Unable to add entry: %v", err))
return
}
if r.FormValue("check-alive") == "true" && dockerCli != nil && dockerCli.IsContainerNotRunning(container) {
n.infof("container '%s' is not running: removing", container)
if err := n.Delete(hostname, container, ipStr, ip); err != nil {
n.infof("failed to remove: %v", err)
}
}
w.WriteHeader(204)
})
deleteHandler := func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
hostname := r.FormValue("fqdn")
if hostname == "" {
hostname = "*"
} else {
Info.Printf("[http] Ignoring name %s, not in %s", name, domain)
}
})
muxRouter.Methods("DELETE").Path("/name/{id:.+}/{ip:.+}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reqError := func(msg string, logmsg string, logargs ...interface{}) {
httpErrorAndLog(Warning, w, msg, http.StatusBadRequest, logmsg, logargs...)
hostname = dns.Fqdn(hostname)
}
vars := mux.Vars(r)
idStr := vars["id"]
ipStr := vars["ip"]
container, ok := vars["container"]
if !ok {
container = "*"
}
ip := net.ParseIP(ipStr)
if ip == nil {
reqError("Invalid IP in request", "Invalid IP in request: %s", ipStr)
ipStr, ok := vars["ip"]
ip, err := address.ParseIP(ipStr)
if ok && err != nil {
n.badRequest(w, err)
return
} else if !ok {
ipStr = "*"
}
if err := n.Delete(hostname, container, ipStr, ip); err != nil {
n.badRequest(w, fmt.Errorf("Unable to delete entries: %v", err))
return
}
Info.Printf("[http] Deleting %s (%s)", idStr, ipStr)
if err := db.DeleteRecord(idStr, ip); err != nil {
if _, ok := err.(LookupError); !ok {
httpErrorAndLog(
Error, w, "Internal error", http.StatusInternalServerError,
"Unexpected error from DB: %s", err)
return
}
w.WriteHeader(204)
}
router.Methods("DELETE").Path("/name/{container}/{ip}").HandlerFunc(deleteHandler)
router.Methods("DELETE").Path("/name/{container}").HandlerFunc(deleteHandler)
router.Methods("DELETE").Path("/name").HandlerFunc(deleteHandler)
router.Methods("GET").Path("/name").Headers("Accept", "application/json").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
n.RLock()
defer n.RUnlock()
if err := json.NewEncoder(w).Encode(n.entries); err != nil {
n.badRequest(w, fmt.Errorf("Error marshalling response: %v", err))
}
})
http.Handle("/", muxRouter)
address := fmt.Sprintf(":%d", port)
if err := http.ListenAndServe(address, nil); err != nil {
Error.Fatal("[http] Unable to create http listener: ", err)
}
}
-88
View File
@@ -1,88 +0,0 @@
package nameserver
import (
"fmt"
wt "github.com/weaveworks/weave/testing"
"math/rand"
"net"
"net/http"
"net/url"
"strings"
"testing"
"time"
)
func genForm(method string, url string, data url.Values) (resp *http.Response, err error) {
req, err := http.NewRequest(method, url, strings.NewReader(data.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
return http.DefaultClient.Do(req)
}
func TestHttp(t *testing.T) {
var (
containerID = "deadbeef"
testDomain = "weave.local."
successTestName = "test1." + testDomain
testAddr1 = "10.2.2.1/24"
dockerIP = "9.8.7.6"
)
var zone = new(ZoneDb)
port := rand.Intn(10000) + 32768
fmt.Println("Http test on port", port)
go ListenHTTP("", nil, testDomain, zone, port)
time.Sleep(100 * time.Millisecond) // Allow for http server to get going
// Ask the http server to add our test address into the database
addrParts := strings.Split(testAddr1, "/")
addrURL := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, containerID, addrParts[0])
resp, err := genForm("PUT", addrURL,
url.Values{"fqdn": {successTestName}, "local_ip": {dockerIP}, "routing_prefix": {addrParts[1]}})
wt.AssertNoErr(t, err)
wt.AssertStatus(t, resp.StatusCode, http.StatusOK, "http response")
// Check that the address is now there.
foundIP, err := zone.LookupName(successTestName)
wt.AssertNoErr(t, err)
ip, _, _ := net.ParseCIDR(testAddr1)
if !foundIP.Equal(ip) {
t.Fatal("Unexpected result for", successTestName, foundIP)
}
// Adding exactly the same address should be OK
resp, err = genForm("PUT", addrURL,
url.Values{"fqdn": {successTestName}, "local_ip": {dockerIP}, "routing_prefix": {addrParts[1]}})
wt.AssertNoErr(t, err)
wt.AssertStatus(t, resp.StatusCode, http.StatusOK, "http success response for duplicate add")
// Now try adding the same address again with a different ident --
// again should be fine
otherURL := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, "other", addrParts[0])
resp, err = genForm("PUT", otherURL,
url.Values{"fqdn": {successTestName}, "local_ip": {dockerIP}, "routing_prefix": {addrParts[1]}})
wt.AssertNoErr(t, err)
wt.AssertStatus(t, resp.StatusCode, http.StatusOK, "http response")
// Delete the address
resp, err = genForm("DELETE", addrURL, nil)
wt.AssertNoErr(t, err)
wt.AssertStatus(t, resp.StatusCode, http.StatusOK, "http response")
// Check that the address is still resolvable.
_, err = zone.LookupName(successTestName)
wt.AssertNoErr(t, err)
// Delete the address record mentioning the other container
resp, err = genForm("DELETE", otherURL, nil)
wt.AssertNoErr(t, err)
wt.AssertStatus(t, resp.StatusCode, http.StatusOK, "http response")
// Check that the address is gone
_, err = zone.LookupName(successTestName)
wt.AssertErrorType(t, err, (*LookupError)(nil), "fully-removed address")
// Would like to shut down the http server at the end of this test
// but it's complicated.
// See https://groups.google.com/forum/#!topic/golang-nuts/vLHWa5sHnCE
}
-232
View File
@@ -1,232 +0,0 @@
package nameserver
import (
"github.com/miekg/dns"
"math"
"net"
"time"
)
// Portions of this code taken from github.com/armon/mdns
const (
ipv4mdns = "224.0.0.251" // link-local multicast address
mdnsPort = 5353 // mDNS assigned port
// We wait this long to hear a response from other mDNS servers on
// the network.
mDNSTimeout = 500 * time.Millisecond
MaxDuration = time.Duration(math.MaxInt64)
MailboxSize = 16
)
var (
ipv4Addr = &net.UDPAddr{
IP: net.ParseIP(ipv4mdns),
Port: mdnsPort,
}
)
type Response struct {
Name string
Addr net.IP
Err error
}
type responseInfo struct {
timeout time.Time // if no answer by this time, give up
ch chan<- *Response
}
// Represents one query that we have sent for one name.
// If we, internally, get several requests for the same name while we have
// a query in flight, then we don't want to send more queries out.
// Invariant on responseInfos: they are in ascending order of timeout.
type inflightQuery struct {
name string
id uint16 // the DNS message ID
responseInfos []*responseInfo
}
type MDNSClient struct {
// note unorthodox use of 'Server' class in client logic - using it to
// listen on a multicast socket and call callback whenever a message comes in.
listener *dns.Server
conn *net.UDPConn
addr *net.UDPAddr
inflight map[string]*inflightQuery
actionChan chan<- MDNSAction
}
type mDNSQueryInfo struct {
name string
querytype uint16
responseCh chan<- *Response
}
func NewMDNSClient() (*MDNSClient, error) {
if conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}); err != nil {
return nil, err
} else {
return &MDNSClient{
conn: conn,
addr: ipv4Addr,
inflight: make(map[string]*inflightQuery)}, nil
}
}
func (c *MDNSClient) Start(ifi *net.Interface) error {
multicast, err := LinkLocalMulticastListener(ifi)
if err != nil {
return err
}
handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) {
// Don't want to handle queries here, so filter anything out that isn't a response
if len(r.Answer) > 0 {
c.ResponseCallback(r)
}
}
c.listener = &dns.Server{Unsafe: true, PacketConn: multicast, Handler: dns.HandlerFunc(handleMDNS)}
go c.listener.ActivateAndServe()
actionChan := make(chan MDNSAction, MailboxSize)
c.actionChan = actionChan
go c.actorLoop(actionChan)
return nil
}
func LinkLocalMulticastListener(ifi *net.Interface) (net.PacketConn, error) {
return net.ListenMulticastUDP("udp", ifi, ipv4Addr)
}
// ACTOR client API
type MDNSAction func()
// Async
func (c *MDNSClient) SendQuery(name string, querytype uint16, responseCh chan<- *Response) {
c.actionChan <- func() {
query, found := c.inflight[name]
if !found {
m := new(dns.Msg)
m.SetQuestion(name, querytype)
m.RecursionDesired = false
buf, err := m.Pack()
if err != nil {
responseCh <- &Response{Err: err}
close(responseCh)
return
}
query = &inflightQuery{
name: name,
id: m.Id,
}
if _, err = c.conn.WriteTo(buf, c.addr); err != nil {
responseCh <- &Response{Err: err}
close(responseCh)
return
}
c.inflight[name] = query
}
info := &responseInfo{
ch: responseCh,
timeout: time.Now().Add(mDNSTimeout),
}
// Invariant on responseInfos: they are in ascending order of
// timeout. Since we use a fixed interval from Now(), this
// must be after all existing timeouts.
query.responseInfos = append(query.responseInfos, info)
}
}
// Async - called from dns library multiplexer
func (c *MDNSClient) ResponseCallback(r *dns.Msg) {
c.actionChan <- func() {
for _, answer := range r.Answer {
var name string
var res *Response
switch rr := answer.(type) {
case *dns.A:
name = rr.Hdr.Name
res = &Response{Addr: rr.A}
case *dns.PTR:
name = rr.Hdr.Name
res = &Response{Name: rr.Ptr}
default:
return
}
if query, found := c.inflight[name]; found {
for _, resp := range query.responseInfos {
resp.ch <- res
close(resp.ch)
}
delete(c.inflight, name)
} else {
// We've received a response that didn't match a query
// Do we want to cache it?
}
}
}
}
// ACTOR server
// Check all in-flight queries, close all that have already timed out,
// and return the duration until the next timeout
func (c *MDNSClient) checkInFlightQueries() time.Duration {
now := time.Now()
after := MaxDuration
for name, query := range c.inflight {
// Invariant on responseInfos: they are in ascending order of timeout.
numClosed := 0
for _, item := range query.responseInfos {
duration := item.timeout.Sub(now)
if duration <= 0 { // timed out
close(item.ch)
numClosed++
} else {
if duration < after {
after = duration
}
break // don't need to look at any more for this query
}
}
// Remove timed-out items from the slice
query.responseInfos = query.responseInfos[numClosed:]
if len(query.responseInfos) == 0 {
delete(c.inflight, name)
}
}
return after
}
func (c *MDNSClient) actorLoop(actionChan <-chan MDNSAction) {
timer := time.NewTimer(MaxDuration)
run := func() { timer.Reset(c.checkInFlightQueries()) }
terminate := false
for !terminate {
select {
case action := <-actionChan:
if action == nil {
c.listener.Shutdown()
terminate = true
} else {
action()
run()
}
case <-timer.C:
run()
}
}
// Close all response channels at termination
for _, query := range c.inflight {
for _, item := range query.responseInfos {
close(item.ch)
}
}
}
-195
View File
@@ -1,195 +0,0 @@
package nameserver
import (
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
wt "github.com/weaveworks/weave/testing"
"log"
"net"
"testing"
"time"
)
var (
successTestName = "test1.weave."
failTestName = "test2.weave."
testAddr = net.ParseIP("9.8.7.6")
testInAddr = "6.7.8.9.in-addr.arpa."
)
func minimalServer(w dns.ResponseWriter, req *dns.Msg) {
sendAnswer := func(m *dns.Msg) {
buf, err := m.Pack()
CheckFatal(err)
if buf == nil {
log.Fatal("Nil buffer")
}
//log.Println("minimalServer sending:", buf)
// This is a bit of a kludge - per the RFC we should send responses from 5353, but that doesn't seem to work
sendconn, err := net.DialUDP("udp", nil, ipv4Addr)
CheckFatal(err)
_, err = sendconn.Write(buf)
sendconn.Close()
CheckFatal(err)
}
//log.Println("minimalServer received:", req)
if len(req.Answer) > 0 {
return // Only interested in questions.
}
if len(req.Question) != 1 {
return // We only handle single-question messages
}
if req.Question[0].Qtype == dns.TypeA && req.Question[0].Name == successTestName {
m := new(dns.Msg)
m.SetReply(req)
hdr := dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeA,
Class: dns.ClassINET, Ttl: 3600}
a := &dns.A{Hdr: hdr, A: testAddr}
m.Answer = append(m.Answer, a)
sendAnswer(m)
} else if req.Question[0].Qtype == dns.TypePTR && req.Question[0].Name == testInAddr {
m := new(dns.Msg)
m.SetReply(req)
hdr := dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypePTR,
Class: dns.ClassINET, Ttl: 3600}
a := &dns.PTR{Hdr: hdr, Ptr: successTestName}
m.Answer = append(m.Answer, a)
sendAnswer(m)
}
}
func RunLocalMulticastServer() (*dns.Server, error) {
multicast, err := LinkLocalMulticastListener(nil)
if err != nil {
return nil, err
}
server := &dns.Server{
PacketConn: multicast,
Handler: dns.HandlerFunc(minimalServer),
ReadTimeout: 100 * time.Millisecond}
go server.ActivateAndServe()
return server, nil
}
func setup(t *testing.T) (*MDNSClient, *dns.Server, error) {
server, err := RunLocalMulticastServer()
if err != nil {
t.Fatalf("Unable to run test server: %s. No default multicast interface?", err)
}
mdnsClient, err := NewMDNSClient()
wt.AssertNoErr(t, err)
err = mdnsClient.Start(nil)
wt.AssertNoErr(t, err)
return mdnsClient, server, err
}
func (c *MDNSClient) Shutdown() {
c.actionChan <- nil
}
type testContext struct {
receivedAddr net.IP
receivedCount int
channel chan *Response
}
func newTestContext() *testContext {
return &testContext{channel: make(chan *Response, 4)}
}
func (c *testContext) checkResponse(t *testing.T, channelOk bool, resp *Response) {
if !channelOk {
c.channel = nil
return
}
wt.AssertNoErr(t, resp.Err)
log.Printf("Got address response %s addr %s", resp.Name, resp.Addr)
c.receivedAddr = resp.Addr
c.receivedCount++
}
func TestSimpleQuery(t *testing.T) {
log.Println("TestSimpleQuery starting")
mdnsClient, server, _ := setup(t)
defer mdnsClient.Shutdown()
defer server.Shutdown()
context := newTestContext()
// First, a test we expect to succeed
mdnsClient.SendQuery(successTestName, dns.TypeA, context.channel)
for resp := range context.channel {
context.checkResponse(t, true, resp)
}
if !context.receivedAddr.Equal(testAddr) || context.receivedCount != 1 {
t.Fatal("Unexpected result for", successTestName, context.receivedAddr, context.receivedCount)
}
// Now, a test we expect to time out with no responses
context = newTestContext()
mdnsClient.SendQuery("test2.weave.", dns.TypeA, context.channel)
for resp := range context.channel {
context.checkResponse(t, true, resp)
}
if context.receivedCount > 0 {
t.Fatal("Unexpected result for test2.weave", context.receivedAddr)
}
}
func TestParallelQuery(t *testing.T) {
log.Println("TestParallelQuery starting")
mdnsClient, server, _ := setup(t)
defer mdnsClient.Shutdown()
defer server.Shutdown()
context1 := newTestContext()
context2 := newTestContext()
go mdnsClient.SendQuery(successTestName, dns.TypeA, context1.channel)
go mdnsClient.SendQuery(successTestName, dns.TypeA, context2.channel)
timeout := time.After(2 * time.Second)
outerloop:
for context1.channel != nil || context2.channel != nil {
select {
case resp, ok := <-context1.channel:
context1.checkResponse(t, ok, resp)
case resp, ok := <-context2.channel:
context2.checkResponse(t, ok, resp)
case <-timeout:
break outerloop
}
}
if !context1.receivedAddr.Equal(testAddr) || !context2.receivedAddr.Equal(testAddr) || context1.receivedCount != 1 || context2.receivedCount != 1 {
t.Fatal("Unexpected result for", successTestName, context1.receivedAddr, context2.receivedAddr, context1.receivedCount, context2.receivedCount)
}
}
func TestAsLookup(t *testing.T) {
mdnsClient, server, _ := setup(t)
defer mdnsClient.Shutdown()
defer server.Shutdown()
ip, err := mdnsClient.LookupName(successTestName)
wt.AssertNoErr(t, err)
if !testAddr.Equal(ip) {
t.Fatalf("Returned address incorrect %s", ip)
}
ip, err = mdnsClient.LookupName("foo.example.com.")
wt.AssertErrorType(t, err, (*LookupError)(nil), "unknown hostname")
name, err := mdnsClient.LookupInaddr(testInAddr)
wt.AssertNoErr(t, err)
if !(successTestName == name) {
t.Fatalf("Expected name %s, got %s", successTestName, name)
}
}
-39
View File
@@ -1,39 +0,0 @@
package nameserver
import (
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
"net"
)
func mdnsLookup(client *MDNSClient, name string, qtype uint16) (*Response, error) {
channel := make(chan *Response)
client.SendQuery(name, qtype, channel)
for resp := range channel {
if err := resp.Err; err != nil {
Debug.Printf("[mdns] Error for query type %s name %s: %s",
dns.TypeToString[qtype], name, err)
return nil, err
}
Debug.Printf("[mdns] Got response name for query type %s name %s: name %s, addr %s",
dns.TypeToString[qtype], name, resp.Name, resp.Addr)
return resp, nil
}
return nil, LookupError(name)
}
func (client *MDNSClient) LookupName(name string) (net.IP, error) {
if r, e := mdnsLookup(client, name, dns.TypeA); r != nil {
return r.Addr, nil
} else {
return nil, e
}
}
func (client *MDNSClient) LookupInaddr(inaddr string) (string, error) {
if r, e := mdnsLookup(client, inaddr, dns.TypePTR); r != nil {
return r.Name, nil
} else {
return "", e
}
}
-124
View File
@@ -1,124 +0,0 @@
package nameserver
import (
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
"net"
)
type MDNSServer struct {
localAddrs []net.Addr
sendconn *net.UDPConn
srv *dns.Server
zone Zone
}
func NewMDNSServer(zone Zone) (*MDNSServer, error) {
// This is a bit of a kludge - per the RFC we should send responses from 5353, but that doesn't seem to work
sendconn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
if err != nil {
return nil, err
}
return &MDNSServer{sendconn: sendconn, zone: zone}, nil
}
// Return true if testaddr is a UDP address with IP matching my local i/f
func (s *MDNSServer) addrIsLocal(testaddr net.Addr) bool {
if udpaddr, ok := testaddr.(*net.UDPAddr); ok {
for _, localaddr := range s.localAddrs {
if ipnetlocal, ok := localaddr.(*net.IPNet); ok {
if ipnetlocal.IP.Equal(udpaddr.IP) {
return true
}
}
}
}
return false
}
func (s *MDNSServer) Start(ifi *net.Interface, localDomain string) error {
conn, err := LinkLocalMulticastListener(ifi)
if err != nil {
return err
}
if ifi == nil {
s.localAddrs, err = net.InterfaceAddrs()
} else {
s.localAddrs, err = ifi.Addrs()
}
if err != nil {
return err
}
handleLocal := s.makeHandler(dns.TypeA,
func(zone Lookup, r *dns.Msg, q *dns.Question) *dns.Msg {
if ip, err := zone.LookupName(q.Name); err == nil {
return makeAddressReply(r, q, []net.IP{ip})
}
return nil
})
handleReverse := s.makeHandler(dns.TypePTR,
func(zone Lookup, r *dns.Msg, q *dns.Question) *dns.Msg {
if name, err := zone.LookupInaddr(q.Name); err == nil {
return makePTRReply(r, q, []string{name})
}
return nil
})
mux := dns.NewServeMux()
mux.HandleFunc(localDomain, handleLocal)
mux.HandleFunc(RDNSDomain, handleReverse)
s.srv = &dns.Server{
Listener: nil,
PacketConn: conn,
Handler: mux,
}
go s.srv.ActivateAndServe()
return err
}
func (s *MDNSServer) Stop() error {
return s.srv.Shutdown()
}
type LookupFunc func(Lookup, *dns.Msg, *dns.Question) *dns.Msg
func (s *MDNSServer) makeHandler(qtype uint16, lookup LookupFunc) dns.HandlerFunc {
return func(_ dns.ResponseWriter, r *dns.Msg) {
// Handle only questions, ignore answers. We might also ignore
// questions that arise locally (i.e., that come from an IP we
// think is local), but in the interest of avoiding
// complication, and easier testing, this is elided on the
// assumption that the client wouldn't ask if it already knew
// the answer, and if it does ask, it'll be happy to get an
// answer.
if len(r.Answer) == 0 && len(r.Question) > 0 {
q := &r.Question[0]
if q.Qtype == qtype {
if m := lookup(s.zone, r, q); m != nil {
Debug.Printf("[mdns msgid %d] Found local answer to mDNS query %s",
r.MsgHdr.Id, q.Name)
if err := s.sendResponse(m); err != nil {
Warning.Printf("[mdns msgid %d] Error writing to %v",
r.MsgHdr.Id, s.sendconn)
}
} else {
Debug.Printf("[mdns msgid %d] No local answer for mDNS query %s",
r.MsgHdr.Id, q.Name)
}
}
}
}
}
func (s *MDNSServer) sendResponse(m *dns.Msg) error {
buf, err := m.Pack()
if err != nil {
return err
}
_, err = s.sendconn.WriteTo(buf, ipv4Addr)
return err
}
-132
View File
@@ -1,132 +0,0 @@
package nameserver
import (
"github.com/miekg/dns"
"github.com/weaveworks/weave/common"
wt "github.com/weaveworks/weave/testing"
"log"
"net"
"testing"
"time"
)
var (
containerID = "deadbeef"
testName = "test.weave.local."
testAddr1 = "10.2.2.1/24"
testInAddr1 = "1.2.2.10.in-addr.arpa."
)
func sendQuery(name string, querytype uint16) error {
m := new(dns.Msg)
m.SetQuestion(name, querytype)
m.RecursionDesired = false
buf, err := m.Pack()
if err != nil {
return err
}
conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
if err != nil {
return err
}
_, err = conn.WriteTo(buf, ipv4Addr)
return err
}
func TestServerSimpleQuery(t *testing.T) {
// The ff can be handy for debugging (obvs)
common.InitDefaultLogging(true)
log.Println("TestServerSimpleQuery starting")
var zone = new(ZoneDb)
ip, _, _ := net.ParseCIDR(testAddr1)
zone.AddRecord(containerID, testName, ip)
var receivedAddr net.IP
var receivedName string
var recvChan chan interface{}
receivedCount := 0
reset := func() {
receivedAddr = nil
receivedName = ""
receivedCount = 0
recvChan = make(chan interface{})
}
wait := func() {
select {
case <-recvChan:
return
case <-time.After(100 * time.Millisecond):
return
}
}
// Implement a minimal listener for responses
multicast, err := LinkLocalMulticastListener(nil)
if err != nil {
t.Fatalf("Unable to run test server: %s. No default multicast interface?", err)
}
handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) {
// Only handle responses here
if len(r.Answer) > 0 {
for _, answer := range r.Answer {
switch rr := answer.(type) {
case *dns.A:
receivedAddr = rr.A
receivedCount++
case *dns.PTR:
receivedName = rr.Ptr
receivedCount++
}
}
recvChan <- "ok"
}
}
listener := &dns.Server{
Unsafe: true,
PacketConn: multicast,
Handler: dns.HandlerFunc(handleMDNS),
ReadTimeout: 100 * time.Millisecond}
go listener.ActivateAndServe()
defer listener.Shutdown()
mdnsServer, err := NewMDNSServer(zone)
wt.AssertNoErr(t, err)
err = mdnsServer.Start(nil, DefaultLocalDomain)
wt.AssertNoErr(t, err)
time.Sleep(100 * time.Millisecond) // Allow for server to get going
reset()
sendQuery(testName, dns.TypeA)
wait()
if receivedCount != 1 {
t.Fatalf("Unexpected result count %d for %s", receivedCount, testName)
}
if !receivedAddr.Equal(ip) {
t.Fatalf("Unexpected result %s for %s", receivedAddr, testName)
}
reset()
sendQuery("testfail.weave.", dns.TypeA)
wait()
if receivedCount != 0 {
t.Fatalf("Unexpected result count %d for testfail.weave", receivedCount)
}
reset()
sendQuery(testInAddr1, dns.TypePTR)
wait()
if receivedCount != 1 {
t.Fatalf("Expected an answer to %s, got %d answers", testInAddr1, receivedCount)
} else if !(testName == receivedName) {
t.Fatalf("Expected answer %s to query for %s, got %s", testName, testInAddr1, receivedName)
}
}
+242
View File
@@ -0,0 +1,242 @@
package nameserver
import (
"fmt"
"sync"
"time"
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
)
const (
// Tombstones do not need to survive long periods of peer disconnection, as
// we delete entries for disconnected peers. Therefore they just need to hang
// around to account for propagation delay through gossip. We do need them
// to hang around longer than the allowed clock skew (defined next). 30mins
// tombstone timeout allows for 15mins max clock skew.
tombstoneTimeout = time.Minute * 30
// Maximum age of acceptable gossip messages (to account for clock skew)
gossipWindow = int64(tombstoneTimeout/time.Second) / 2
// Used by prog/weaver/main.go and proxy/create_container_interceptor.go
DefaultDomain = "weave.local."
)
// Nameserver: gossip-based, in memory nameserver.
// - Holds a sorted list of (hostname, peer, container id, ip) tuples for the whole cluster.
// - This list is gossiped & merged around the cluser.
// - Lookup-by-hostname are O(nlogn), and return a (copy of a) slice of the entries
// - Update is O(n) for now
type Nameserver struct {
sync.RWMutex
ourName mesh.PeerName
domain string
gossip mesh.Gossip
entries Entries
isKnownPeer func(mesh.PeerName) bool
quit chan struct{}
}
func New(ourName mesh.PeerName, domain string, isKnownPeer func(mesh.PeerName) bool) *Nameserver {
return &Nameserver{
ourName: ourName,
domain: dns.Fqdn(domain),
isKnownPeer: isKnownPeer,
quit: make(chan struct{}),
}
}
func (n *Nameserver) SetGossip(gossip mesh.Gossip) {
n.gossip = gossip
}
func (n *Nameserver) Start() {
go func() {
ticker := time.Tick(tombstoneTimeout)
for {
select {
case <-n.quit:
return
case <-ticker:
n.deleteTombstones()
}
}
}()
}
func (n *Nameserver) Stop() {
n.quit <- struct{}{}
}
func (n *Nameserver) broadcastEntries(es ...Entry) error {
if n.gossip == nil || len(es) == 0 {
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 {
n.infof("adding entry %s -> %s", hostname, addr.String())
n.Lock()
entry := n.entries.add(hostname, containerid, origin, addr)
n.Unlock()
return n.broadcastEntries(entry)
}
func (n *Nameserver) Lookup(hostname string) []address.Address {
n.RLock()
defer n.RUnlock()
entries := n.entries.lookup(hostname)
result := []address.Address{}
for _, e := range entries {
if e.Tombstone > 0 {
continue
}
result = append(result, e.Addr)
}
n.debugf("lookup %s -> %s", hostname, &result)
return result
}
func (n *Nameserver) ReverseLookup(ip address.Address) (string, error) {
n.RLock()
defer n.RUnlock()
match, err := n.entries.first(func(e *Entry) bool {
return e.Tombstone == 0 && e.Addr == ip
})
if err != nil {
return "", err
}
n.debugf("reverse lookup %s -> %s", ip, match.Hostname)
return match.Hostname, nil
}
func (n *Nameserver) ContainerStarted(ident string) {}
func (n *Nameserver) ContainerDied(ident string) {
n.Lock()
entries := n.entries.tombstone(n.ourName, func(e *Entry) bool {
if e.ContainerID == ident {
n.infof("container %s died; tombstoning entry %s", ident, e.String())
return true
}
return false
})
n.Unlock()
if err := n.broadcastEntries(entries...); err != nil {
n.errorf("failed to broadcast container %s death: %v", ident, err)
}
}
func (n *Nameserver) PeerGone(peer mesh.PeerName) {
n.infof("peer %s gone", peer.String())
n.Lock()
defer n.Unlock()
n.entries.filter(func(e *Entry) bool {
return e.Origin != peer
})
}
func (n *Nameserver) Delete(hostname, containerid, ipStr string, ip address.Address) error {
n.Lock()
n.infof("tombstoning hostname=%s, container=%s, ip=%s", hostname, containerid, ipStr)
entries := n.entries.tombstone(n.ourName, func(e *Entry) bool {
if hostname != "*" && e.Hostname != hostname {
return false
}
if containerid != "*" && e.ContainerID != containerid {
return false
}
if ipStr != "*" && e.Addr != ip {
return false
}
n.infof("tombstoning entry %v", e)
return true
})
n.Unlock()
return n.broadcastEntries(entries...)
}
func (n *Nameserver) deleteTombstones() {
n.Lock()
defer n.Unlock()
now := time.Now().Unix()
n.entries.filter(func(e *Entry) bool {
return e.Tombstone == 0 || now-e.Tombstone <= int64(tombstoneTimeout/time.Second)
})
}
func (n *Nameserver) Gossip() mesh.GossipData {
n.RLock()
defer n.RUnlock()
gossip := &GossipData{
Entries: make(Entries, len(n.entries)),
Timestamp: now(),
}
copy(gossip.Entries, n.entries)
return gossip
}
func (n *Nameserver) OnGossipUnicast(sender mesh.PeerName, msg []byte) error {
return nil
}
func (n *Nameserver) receiveGossip(msg []byte) (mesh.GossipData, mesh.GossipData, error) {
var gossip GossipData
if err := gossip.Decode(msg); err != nil {
return nil, nil, err
}
if delta := gossip.Timestamp - now(); delta > gossipWindow || delta < -gossipWindow {
return nil, nil, fmt.Errorf("clock skew of %d detected", delta)
}
n.Lock()
defer n.Unlock()
gossip.Entries.filter(func(e *Entry) bool {
return n.isKnownPeer(e.Origin)
})
newEntries := n.entries.merge(gossip.Entries)
if len(newEntries) > 0 {
return &GossipData{Entries: newEntries, Timestamp: now()}, &gossip, nil
}
return nil, &gossip, nil
}
// merge received data into state and return "everything new I've
// just learnt", or nil if nothing in the received data was new
func (n *Nameserver) OnGossip(msg []byte) (mesh.GossipData, error) {
newEntries, _, err := n.receiveGossip(msg)
return newEntries, err
}
// merge received data into state and return a representation of
// the received data, for further propagation
func (n *Nameserver) OnGossipBroadcast(_ mesh.PeerName, msg []byte) (mesh.GossipData, error) {
_, entries, err := n.receiveGossip(msg)
return entries, err
}
func (n *Nameserver) infof(fmt string, args ...interface{}) {
Log.Infof("[nameserver %s] "+fmt, append([]interface{}{n.ourName}, args...)...)
}
func (n *Nameserver) debugf(fmt string, args ...interface{}) {
Log.Debugf("[nameserver %s] "+fmt, append([]interface{}{n.ourName}, args...)...)
}
func (n *Nameserver) errorf(fmt string, args ...interface{}) {
Log.Errorf("[nameserver %s] "+fmt, append([]interface{}{n.ourName}, args...)...)
}
+272
View File
@@ -0,0 +1,272 @@
package nameserver
import (
"fmt"
"math/rand"
"reflect"
"sort"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/mesh"
"github.com/weaveworks/weave/net/address"
"github.com/weaveworks/weave/testing/gossip"
)
func makeNameserver(name mesh.PeerName) *Nameserver {
return New(name, "", func(mesh.PeerName) bool { return true })
}
func makeNetwork(size int) ([]*Nameserver, *gossip.TestRouter) {
gossipRouter := gossip.NewTestRouter(0.0)
nameservers := make([]*Nameserver, size)
for i := 0; i < size; i++ {
name, _ := mesh.PeerNameFromString(fmt.Sprintf("%02d:00:00:02:00:00", i))
nameserver := makeNameserver(name)
nameserver.SetGossip(gossipRouter.Connect(nameserver.ourName, nameserver))
nameserver.Start()
nameservers[i] = nameserver
}
return nameservers, gossipRouter
}
func stopNetwork(nameservers []*Nameserver, grouter *gossip.TestRouter) {
for _, nameserver := range nameservers {
nameserver.Stop()
}
grouter.Stop()
}
type pair struct {
origin mesh.PeerName
addr address.Address
}
type mapping struct {
hostname string
addrs []pair
}
type addrs []address.Address
func (a addrs) Len() int { return len(a) }
func (a addrs) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a addrs) Less(i, j int) bool { return a[i] < a[j] }
func (a addrs) String() string {
ss := []string{}
for _, addr := range a {
ss = append(ss, addr.String())
}
return strings.Join(ss, " ")
}
func (m mapping) Addrs() []address.Address {
want := addrs{}
for _, p := range m.addrs {
want = append(want, p.addr)
}
sort.Sort(want)
return want
}
func TestNameservers(t *testing.T) {
//common.SetLogLevel("debug")
lookupTimeout := 10 // ms
nameservers, grouter := makeNetwork(30)
defer stopNetwork(nameservers, grouter)
// This subset will sometimes lose touch with some of the others
badNameservers := nameservers[25:]
// This subset will remain well-connected, and we will deal mainly with them
nameservers = nameservers[:25]
nameserversByName := map[mesh.PeerName]*Nameserver{}
for _, n := range nameservers {
nameserversByName[n.ourName] = n
}
mappings := []mapping{}
check := func(nameserver *Nameserver, expected mapping) {
have := []address.Address{}
for i := 0; i < lookupTimeout; i++ {
have = nameserver.Lookup(expected.hostname)
sort.Sort(addrs(have))
if reflect.DeepEqual(have, expected.Addrs()) {
return
}
time.Sleep(1 * time.Millisecond)
}
want := expected.Addrs()
require.Equal(t, addrs(want).String(), addrs(have).String())
}
addMapping := func() {
nameserver := nameservers[rand.Intn(len(nameservers))]
addr := address.Address(rand.Int31())
// Create a hostname which has some upper and lowercase letters,
// and a unique number so we don't have to check if we allocated it already
randomBits := rand.Int63()
firstLetter := 'H' + (randomBits&1)*32
secondLetter := 'O' + (randomBits&2)*16
randomBits = randomBits >> 2
hostname := fmt.Sprintf("%c%cstname%d", firstLetter, secondLetter, randomBits)
mapping := mapping{hostname, []pair{{nameserver.ourName, addr}}}
mappings = append(mappings, mapping)
require.Nil(t, nameserver.AddEntry(hostname, "", nameserver.ourName, addr))
check(nameserver, mapping)
}
addExtraMapping := func() {
if len(mappings) <= 0 {
return
}
nameserver := nameservers[rand.Intn(len(nameservers))]
i := rand.Intn(len(mappings))
mapping := mappings[i]
addr := address.Address(rand.Int31())
mapping.addrs = append(mapping.addrs, pair{nameserver.ourName, addr})
mappings[i] = mapping
require.Nil(t, nameserver.AddEntry(mapping.hostname, "", nameserver.ourName, addr))
check(nameserver, mapping)
}
loseConnection := func() {
nameserver1 := badNameservers[rand.Intn(len(badNameservers))]
nameserver2 := nameservers[rand.Intn(len(nameservers))]
nameserver1.PeerGone(nameserver2.ourName)
}
deleteMapping := func() {
if len(mappings) <= 0 {
return
}
i := rand.Intn(len(mappings))
mapping := mappings[i]
if len(mapping.addrs) <= 0 {
return
}
j := rand.Intn(len(mapping.addrs))
pair := mapping.addrs[j]
mapping.addrs = append(mapping.addrs[:j], mapping.addrs[j+1:]...)
mappings[i] = mapping
nameserver := nameserversByName[pair.origin]
require.Nil(t, nameserver.Delete(mapping.hostname, "*", pair.addr.String(), pair.addr))
check(nameserver, mapping)
}
doLookup := func() {
if len(mappings) <= 0 {
return
}
mapping := mappings[rand.Intn(len(mappings))]
nameserver := nameservers[rand.Intn(len(nameservers))]
check(nameserver, mapping)
}
doReverseLookup := func() {
if len(mappings) <= 0 {
return
}
mapping := mappings[rand.Intn(len(mappings))]
if len(mapping.addrs) <= 0 {
return
}
nameserver := nameservers[rand.Intn(len(nameservers))]
hostname := ""
var err error
for i := 0; i < lookupTimeout; i++ {
hostname, err = nameserver.ReverseLookup(mapping.addrs[0].addr)
if err != nil && mapping.hostname == hostname {
return
}
time.Sleep(1 * time.Millisecond)
}
require.Nil(t, err)
require.Equal(t, mapping.hostname, hostname)
}
for i := 0; i < 800; i++ {
r := rand.Float32()
switch {
case r < 0.1:
addMapping()
case 0.1 <= r && r < 0.2:
addExtraMapping()
case 0.2 <= r && r < 0.3:
deleteMapping()
case 0.3 <= r && r < 0.35:
loseConnection()
case 0.35 <= r && r < 0.9:
doLookup()
case 0.9 <= r:
doReverseLookup()
}
grouter.Flush()
}
}
func TestContainerAndPeerDeath(t *testing.T) {
peername, err := mesh.PeerNameFromString("00:00:00:02:00:00")
require.Nil(t, err)
nameserver := makeNameserver(peername)
err = nameserver.AddEntry("hostname", "containerid", peername, address.Address(0))
require.Nil(t, err)
require.Equal(t, []address.Address{0}, nameserver.Lookup("hostname"))
nameserver.ContainerDied("containerid")
require.Equal(t, []address.Address{}, nameserver.Lookup("hostname"))
err = nameserver.AddEntry("hostname", "containerid", peername, address.Address(0))
require.Nil(t, err)
require.Equal(t, []address.Address{0}, nameserver.Lookup("hostname"))
nameserver.PeerGone(peername)
require.Equal(t, []address.Address{}, nameserver.Lookup("hostname"))
}
func TestTombstoneDeletion(t *testing.T) {
oldNow := now
defer func() { now = oldNow }()
now = func() int64 { return 1234 }
peername, err := mesh.PeerNameFromString("00:00:00:02:00:00")
require.Nil(t, err)
nameserver := makeNameserver(peername)
err = nameserver.AddEntry("hostname", "containerid", peername, address.Address(0))
require.Nil(t, err)
require.Equal(t, []address.Address{0}, nameserver.Lookup("hostname"))
nameserver.deleteTombstones()
require.Equal(t, []address.Address{0}, nameserver.Lookup("hostname"))
err = nameserver.Delete("hostname", "containerid", "", address.Address(0))
require.Nil(t, err)
require.Equal(t, []address.Address{}, nameserver.Lookup("hostname"))
require.Equal(t, l(Entries{Entry{
ContainerID: "containerid",
Origin: peername,
Addr: address.Address(0),
Hostname: "hostname",
Version: 1,
Tombstone: 1234,
}}), nameserver.entries)
now = func() int64 { return 1234 + int64(tombstoneTimeout/time.Second) + 1 }
nameserver.deleteTombstones()
require.Equal(t, Entries{}, nameserver.entries)
}
-365
View File
@@ -1,365 +0,0 @@
package nameserver
import (
"bytes"
"fmt"
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
"net"
"sync"
"time"
)
const (
DefaultLocalDomain = "weave.local." // The default name used for the local domain
DefaultServerPort = 53 // The default server port
DefaultCLICfgFile = "/etc/resolv.conf" // default "resolv.conf" file to try to load
DefaultUDPBuflen = 4096 // bigger than the default 512
DefaultCacheLen = 8192 // default cache capacity
DefaultResolvWorkers = 8 // default number of resolution workers
DefaultTimeout = 5 // default timeout for DNS resolutions
)
type DNSServerConfig struct {
// (Optional) client config file for resolving upstream servers
UpstreamCfgFile string
// (Optional) DNS client config for the fallback server(s)
UpstreamCfg *dns.ClientConfig
// (Optional) port number (for TCP and UDP)
Port int
// (Optional) local domain (ie, "weave.local.")
LocalDomain string
// (Optional) cache size
CacheLen int
// (Optional) timeout for DNS queries
Timeout int
// (Optional) UDP buffer length
UDPBufLen int
}
type dnsProtocol uint8
const (
protUDP dnsProtocol = iota // UDP protocol
protTCP dnsProtocol = iota // TCP protocol
)
func (proto dnsProtocol) String() string {
switch proto {
case protUDP:
return "UDP"
case protTCP:
return "TCP"
}
return "unknown"
}
// get a new dns.Client for a protocol
func (proto dnsProtocol) GetNewClient(bufsize int) *dns.Client {
switch proto {
case protTCP:
return &dns.Client{Net: "tcp"}
case protUDP:
return &dns.Client{Net: "udp", UDPSize: uint16(bufsize)}
}
return nil
}
type DNSServer struct {
Zone Zone
Iface *net.Interface
Upstream *dns.ClientConfig
udpSrv *dns.Server
tcpSrv *dns.Server
mdnsCli *MDNSClient
mdnsSrv *MDNSServer
cache *Cache
timeout int
udpBuf int
listenersWg *sync.WaitGroup
Domain string // the local domain
ListenAddr string // the address the server is listening at
}
// Creates a new DNS server
func NewDNSServer(config DNSServerConfig, zone Zone, iface *net.Interface) (s *DNSServer, err error) {
s = &DNSServer{
Zone: zone,
Iface: iface,
listenersWg: new(sync.WaitGroup),
Domain: DefaultLocalDomain,
ListenAddr: fmt.Sprintf(":%d", DefaultServerPort),
}
// fill empty parameters with defaults...
if config.Port != 0 {
s.ListenAddr = fmt.Sprintf(":%d", config.Port)
}
if len(config.LocalDomain) > 0 {
s.Domain = config.LocalDomain
}
if config.UpstreamCfg != nil {
s.Upstream = config.UpstreamCfg
} else {
cfgFile := DefaultCLICfgFile
if len(config.UpstreamCfgFile) > 0 {
cfgFile = config.UpstreamCfgFile
}
if s.Upstream, err = dns.ClientConfigFromFile(cfgFile); err != nil {
return nil, err
}
}
if config.Timeout > 0 {
s.timeout = config.Timeout
}
if config.UDPBufLen > 0 {
s.udpBuf = config.UDPBufLen
}
s.mdnsCli, err = NewMDNSClient()
if err != nil {
return
}
s.mdnsSrv, err = NewMDNSServer(s.Zone)
if err != nil {
return
}
cacheLen := DefaultCacheLen
if config.CacheLen > 0 {
cacheLen = config.CacheLen
}
Debug.Printf("[dns] Initializing cache: %d entries", config.CacheLen)
s.cache, err = NewCache(cacheLen)
if err != nil {
return
}
// create two DNS request multiplexerers, depending on the protocol used by clients
// (we use the same protocol for asking upstream servers)
mux := func(proto dnsProtocol) *dns.ServeMux {
m := dns.NewServeMux()
m.HandleFunc(s.Domain, s.queryHandler(proto))
m.HandleFunc(RDNSDomain, s.rdnsHandler(proto))
m.HandleFunc(".", s.notUsHandler(proto))
return m
}
s.udpSrv = &dns.Server{Addr: s.ListenAddr, Net: "udp", Handler: mux(protUDP)}
s.tcpSrv = &dns.Server{Addr: s.ListenAddr, Net: "tcp", Handler: mux(protTCP)}
return
}
// Start the DNS server
func (s *DNSServer) Start() error {
Info.Printf("Using mDNS on %v", s.Iface)
err := s.mdnsCli.Start(s.Iface)
CheckFatal(err)
err = s.mdnsSrv.Start(s.Iface, s.Domain)
CheckFatal(err)
s.listenersWg.Add(2)
go func() {
defer s.listenersWg.Done()
Debug.Printf("Listening for DNS on %s (UDP)", s.ListenAddr)
err = s.udpSrv.ListenAndServe()
CheckFatal(err)
Debug.Printf("DNS UDP server exiting...")
}()
go func() {
defer s.listenersWg.Done()
Debug.Printf("Listening for DNS on %s (TCP)", s.ListenAddr)
err = s.tcpSrv.ListenAndServe()
CheckFatal(err)
Debug.Printf("DNS TCP server exiting...")
}()
// Waiting for all goroutines to finish (otherwise they die as main routine dies)
s.listenersWg.Wait()
Info.Printf("WeaveDNS server exiting...")
return nil
}
// Return status string
func (s *DNSServer) Status() string {
var buf bytes.Buffer
fmt.Fprintln(&buf, "Local domain", s.Domain)
fmt.Fprintln(&buf, "Listen address", s.ListenAddr)
fmt.Fprintln(&buf, "mDNS interface", s.Iface)
fmt.Fprintln(&buf, "Fallback DNS config", s.Upstream)
fmt.Fprintf(&buf, "Zone database:\n%s", s.Zone)
return buf.String()
}
// Perform a graceful shutdown
func (s *DNSServer) Stop() error {
// Stop the listeners/handlers
if err := s.tcpSrv.Shutdown(); err != nil {
return err
}
if err := s.udpSrv.Shutdown(); err != nil {
return err
}
s.listenersWg.Wait()
// shutdown the mDNS server
s.mdnsSrv.Stop()
return nil
}
func (s *DNSServer) queryHandler(proto dnsProtocol) dns.HandlerFunc {
return func(w dns.ResponseWriter, r *dns.Msg) {
now := time.Now()
q := r.Question[0]
maxLen := getMaxReplyLen(r, proto)
lookups := []Lookup{s.Zone, s.mdnsCli}
Debug.Printf("Query: %+v", q)
reply, err := s.cache.Get(r, maxLen, time.Now())
if err != nil {
if err == errNoLocalReplies {
Debug.Printf("[dns msgid %d] Cached 'no local replies' - skipping local lookup", r.MsgHdr.Id)
lookups = []Lookup{s.Zone}
} else {
Debug.Printf("[dns msgid %d] Error from cache: %s", r.MsgHdr.Id, err)
w.WriteMsg(makeDNSFailResponse(r))
return
}
}
if reply != nil {
Debug.Printf("[dns msgid %d] Returning reply from cache: %s/%d answers",
r.MsgHdr.Id, dns.RcodeToString[reply.MsgHdr.Rcode], len(reply.Answer))
w.WriteMsg(reply)
return
}
// catch unsupported queries
if q.Qtype != dns.TypeA {
Debug.Printf("[dns msgid %d] Unsuported query type %s", r.MsgHdr.Id, dns.TypeToString[q.Qtype])
m := makeDNSNotImplResponse(r)
s.cache.Put(r, m, negLocalTTL, 0, now)
w.WriteMsg(m)
return
}
for _, lookup := range lookups {
if ip, err := lookup.LookupName(q.Name); err == nil {
m := makeAddressReply(r, &q, []net.IP{ip})
m.Authoritative = true
Debug.Printf("[dns msgid %d] Caching response for %s-query for \"%s\": %s [code:%s]",
m.MsgHdr.Id, dns.TypeToString[q.Qtype], q.Name, ip, dns.RcodeToString[m.Rcode])
s.cache.Put(r, m, nullTTL, 0, now)
w.WriteMsg(m)
return
}
now = time.Now()
}
Info.Printf("[dns msgid %d] No results for type %s query %s [caching no-local]",
r.MsgHdr.Id, dns.TypeToString[q.Qtype], q.Name)
s.cache.Put(r, nil, negLocalTTL, CacheNoLocalReplies, now)
w.WriteMsg(makeDNSFailResponse(r))
}
}
func (s *DNSServer) rdnsHandler(proto dnsProtocol) dns.HandlerFunc {
fallback := s.notUsHandler(proto)
return func(w dns.ResponseWriter, r *dns.Msg) {
now := time.Now()
q := r.Question[0]
lookups := []Lookup{s.Zone, s.mdnsCli}
maxLen := getMaxReplyLen(r, proto)
Debug.Printf("Reverse query: %+v", q)
reply, err := s.cache.Get(r, maxLen, time.Now())
if err != nil {
if err == errNoLocalReplies {
Debug.Printf("[dns msgid %d] Cached 'no local replies' - skipping local lookup", r.MsgHdr.Id)
lookups = []Lookup{s.Zone}
} else {
Debug.Printf("[dns msgid %d] Error from cache: %s", r.MsgHdr.Id, err)
w.WriteMsg(makeDNSFailResponse(r))
return
}
}
if reply != nil {
Debug.Printf("[dns msgid %d] Returning reply from cache: %s/%d answers",
r.MsgHdr.Id, dns.RcodeToString[reply.MsgHdr.Rcode], len(reply.Answer))
w.WriteMsg(reply)
return
}
// catch unsupported queries
if q.Qtype != dns.TypePTR {
Warning.Printf("[dns msgid %d] Unexpected reverse query type %s: %+v",
r.MsgHdr.Id, dns.TypeToString[q.Qtype], q)
m := makeDNSNotImplResponse(r)
s.cache.Put(r, m, negLocalTTL, 0, now)
w.WriteMsg(m)
return
}
for _, lookup := range lookups {
if name, err := lookup.LookupInaddr(q.Name); err == nil {
m := makePTRReply(r, &q, []string{name})
m.Authoritative = true
Debug.Printf("[dns msgid %d] Caching response for %s-query for \"%s\": %s [code:%s]",
m.MsgHdr.Id, dns.TypeToString[q.Qtype], q.Name, name, dns.RcodeToString[m.Rcode])
s.cache.Put(r, m, nullTTL, 0, now)
w.WriteMsg(m)
return
}
now = time.Now()
}
Info.Printf("[dns msgid %d] No results for %s-query about '%s' [caching no-local] -> sending to fallback server",
r.MsgHdr.Id, dns.TypeToString[q.Qtype], q.Name)
s.cache.Put(r, nil, negLocalTTL, CacheNoLocalReplies, now)
fallback(w, r)
}
}
// When we receive a request for a name outside of our '.weave.local.'
// domain, ask the configured DNS server as a fallback.
func (s *DNSServer) notUsHandler(proto dnsProtocol) dns.HandlerFunc {
dnsClient := proto.GetNewClient(DefaultUDPBuflen)
return func(w dns.ResponseWriter, r *dns.Msg) {
maxLen := getMaxReplyLen(r, proto)
q := r.Question[0]
// create a request where we announce our max payload size
rcopy := r
rcopy.SetEdns0(uint16(maxLen), false)
Debug.Printf("[dns msgid %d] Fallback query: %+v [%s, max:%d bytes]", rcopy.MsgHdr.Id, q, proto, maxLen)
for _, server := range s.Upstream.Servers {
reply, _, err := dnsClient.Exchange(rcopy, fmt.Sprintf("%s:%s", server, s.Upstream.Port))
if err != nil {
Debug.Printf("[dns msgid %d] Network error trying %s (%s)",
r.MsgHdr.Id, server, err)
continue
}
if reply != nil && reply.Rcode != dns.RcodeSuccess {
Debug.Printf("[dns msgid %d] Failure reported by %s for query %s",
r.MsgHdr.Id, server, q.Name)
continue
}
Debug.Printf("[dns msgid %d] Given answer by %s for query %s",
r.MsgHdr.Id, server, q.Name)
w.WriteMsg(reply)
return
}
Warning.Printf("[dns msgid %d] Failed lookup for external name %s", r.MsgHdr.Id, q.Name)
w.WriteMsg(makeDNSFailResponse(r))
}
}
-300
View File
@@ -1,300 +0,0 @@
package nameserver
import (
"encoding/binary"
"fmt"
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
wt "github.com/weaveworks/weave/testing"
"net"
"testing"
"time"
)
const (
testRDNSsuccess = "1.2.2.10.in-addr.arpa."
testRDNSfail = "4.3.2.1.in-addr.arpa."
testRDNSnonlocal = "8.8.8.8.in-addr.arpa."
testUDPBufSize = 16384
)
var (
testPort = 17625
)
func setupForTest(t *testing.T) {
// fail early if we cannot find a default multicast interface
multicast, err := LinkLocalMulticastListener(nil)
if err != nil {
t.Fatalf("Unable to create multicast listener: %s. No default multicast interface?", err)
}
multicast.Close()
testPort++
}
func TestUDPDNSServer(t *testing.T) {
setupForTest(t)
const (
successTestName = "test1.weave.local."
failTestName = "test2.weave.local."
nonLocalName = "weave.works."
testAddr1 = "10.2.2.1"
)
testCIDR1 := testAddr1 + "/24"
InitDefaultLogging(true)
var zone = new(ZoneDb)
ip, _, _ := net.ParseCIDR(testCIDR1)
zone.AddRecord(containerID, successTestName, ip)
fallbackHandler := func(w dns.ResponseWriter, req *dns.Msg) {
m := new(dns.Msg)
m.SetReply(req)
if len(req.Question) == 1 {
q := req.Question[0]
if q.Name == nonLocalName && q.Qtype == dns.TypeMX {
m.Answer = make([]dns.RR, 1)
m.Answer[0] = &dns.MX{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 0}, Mx: "mail." + nonLocalName}
} else if q.Name == nonLocalName && q.Qtype == dns.TypeANY {
m.Answer = make([]dns.RR, 512/len("mailn."+nonLocalName)+1)
for i := range m.Answer {
m.Answer[i] = &dns.MX{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 0}, Mx: fmt.Sprintf("mail%d.%s", i, nonLocalName)}
}
} else if q.Name == testRDNSnonlocal && q.Qtype == dns.TypePTR {
m.Answer = make([]dns.RR, 1)
m.Answer[0] = &dns.PTR{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: 0}, Ptr: "ns1.google.com."}
} else if q.Name == testRDNSfail && q.Qtype == dns.TypePTR {
m.Rcode = dns.RcodeNameError
}
}
w.WriteMsg(m)
}
// Run another DNS server for fallback
s, fallbackAddr, err := runLocalUDPServer(t, "127.0.0.1:0", fallbackHandler)
wt.AssertNoErr(t, err)
defer s.Shutdown()
_, fallbackPort, err := net.SplitHostPort(fallbackAddr)
wt.AssertNoErr(t, err)
config := &dns.ClientConfig{Servers: []string{"127.0.0.1"}, Port: fallbackPort}
srv, err := NewDNSServer(DNSServerConfig{UpstreamCfg: config, Port: testPort}, zone, nil)
wt.AssertNoErr(t, err)
defer srv.Stop()
go srv.Start()
time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start
var r *dns.Msg
r = assertExchange(t, successTestName, dns.TypeA, 1, 1, 0)
wt.AssertType(t, r.Answer[0], (*dns.A)(nil), "DNS record")
wt.AssertEqualString(t, r.Answer[0].(*dns.A).A.String(), testAddr1, "IP address")
assertExchange(t, failTestName, dns.TypeA, 0, 0, dns.RcodeNameError)
r = assertExchange(t, testRDNSsuccess, dns.TypePTR, 1, 1, 0)
wt.AssertType(t, r.Answer[0], (*dns.PTR)(nil), "DNS record")
wt.AssertEqualString(t, r.Answer[0].(*dns.PTR).Ptr, successTestName, "IP address")
assertExchange(t, testRDNSfail, dns.TypePTR, 0, 0, dns.RcodeNameError)
// This should fail because we don't handle MX records
assertExchange(t, successTestName, dns.TypeMX, 0, 0, dns.RcodeNotImplemented)
// This non-local query for an MX record should succeed by being
// passed on to the fallback server
assertExchange(t, nonLocalName, dns.TypeMX, 1, -1, 0)
// Now ask a query that we expect to return a lot of data.
assertExchange(t, nonLocalName, dns.TypeANY, 5, -1, 0)
assertExchange(t, testRDNSnonlocal, dns.TypePTR, 1, -1, 0)
// Not testing MDNS functionality of server here (yet), since it
// needs two servers, each listening on its own address
}
func TestTCPDNSServer(t *testing.T) {
setupForTest(t)
const (
numAnswers = 512
nonLocalName = "weave.works."
)
dnsAddr := fmt.Sprintf("localhost:%d", testPort)
InitDefaultLogging(true)
var zone = new(ZoneDb)
// generate a list of `numAnswers` IP addresses
var addrs []net.IP
bs := make([]byte, 4)
for i := 0; i < numAnswers; i++ {
binary.LittleEndian.PutUint32(bs, uint32(i))
addrs = append(addrs, net.IPv4(bs[0], bs[1], bs[2], bs[3]))
}
// handler for the fallback server: it will just return a very long response
fallbackUDPHandler := func(w dns.ResponseWriter, req *dns.Msg) {
maxLen := getMaxReplyLen(req, protUDP)
t.Logf("Fallback UDP server got asked: returning %d answers", numAnswers)
q := req.Question[0]
m := makeAddressReply(req, &q, addrs)
mLen := m.Len()
m.SetEdns0(uint16(maxLen), false)
if mLen > maxLen {
t.Logf("... truncated response (%d > %d)", mLen, maxLen)
m.Truncated = true
}
w.WriteMsg(m)
}
fallbackTCPHandler := func(w dns.ResponseWriter, req *dns.Msg) {
t.Logf("Fallback TCP server got asked: returning %d answers", numAnswers)
q := req.Question[0]
m := makeAddressReply(req, &q, addrs)
w.WriteMsg(m)
}
t.Logf("Running a DNS fallback server with UDP")
us, fallbackUDPAddr, err := runLocalUDPServer(t, "127.0.0.1:0", fallbackUDPHandler)
wt.AssertNoErr(t, err)
defer us.Shutdown()
_, fallbackPort, err := net.SplitHostPort(fallbackUDPAddr)
wt.AssertNoErr(t, err)
t.Logf("Starting another fallback server, with TCP, on the same port as the UDP server")
fallbackTCPAddr := fmt.Sprintf("127.0.0.1:%s", fallbackPort)
ts, fallbackTCPAddr, err := runLocalTCPServer(t, fallbackTCPAddr, fallbackTCPHandler)
wt.AssertNoErr(t, err)
defer ts.Shutdown()
t.Logf("Creating a WeaveDNS server instance, falling back to 127.0.0.1:%s", fallbackPort)
config := &dns.ClientConfig{Servers: []string{"127.0.0.1"}, Port: fallbackPort}
srv, err := NewDNSServer(DNSServerConfig{UpstreamCfg: config, Port: testPort}, zone, nil)
wt.AssertNoErr(t, err)
defer srv.Stop()
go srv.Start()
time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start
t.Logf("Creating a UDP and a TCP client")
uc := new(dns.Client)
uc.UDPSize = minUDPSize
tc := new(dns.Client)
tc.Net = "tcp"
t.Logf("Creating DNS query message")
m := new(dns.Msg)
m.RecursionDesired = true
m.SetQuestion(nonLocalName, dns.TypeA)
t.Logf("Checking the fallback server at %s returns a truncated response with UDP", fallbackUDPAddr)
r, _, err := uc.Exchange(m, fallbackUDPAddr)
t.Logf("Got response from fallback server (UDP) with %d answers", len(r.Answer))
t.Logf("Response:\n%+v\n", r)
wt.AssertNoErr(t, err)
wt.AssertTrue(t, r.MsgHdr.Truncated, "DNS truncated reponse flag")
wt.AssertNotEqualInt(t, len(r.Answer), numAnswers, "number of answers (UDP)")
t.Logf("Checking the WeaveDNS server at %s returns a truncated reponse with UDP", dnsAddr)
r, _, err = uc.Exchange(m, dnsAddr)
t.Logf("UDP Response:\n%+v\n", r)
wt.AssertNoErr(t, err)
wt.AssertNotNil(t, r, "response")
t.Logf("%d answers", len(r.Answer))
wt.AssertTrue(t, r.MsgHdr.Truncated, "DNS truncated reponse flag")
wt.AssertNotEqualInt(t, len(r.Answer), numAnswers, "number of answers (UDP)")
t.Logf("Checking the WeaveDNS server at %s does not return a truncated reponse with TCP", dnsAddr)
r, _, err = tc.Exchange(m, dnsAddr)
t.Logf("TCP Response:\n%+v\n", r)
wt.AssertNoErr(t, err)
wt.AssertNotNil(t, r, "response")
t.Logf("%d answers", len(r.Answer))
wt.AssertFalse(t, r.MsgHdr.Truncated, "DNS truncated response flag")
wt.AssertEqualInt(t, len(r.Answer), numAnswers, "number of answers (TCP)")
t.Logf("Checking the WeaveDNS server at %s does not return a truncated reponse with UDP with a bigger buffer", dnsAddr)
m.SetEdns0(testUDPBufSize, false)
r, _, err = uc.Exchange(m, dnsAddr)
t.Logf("UDP-large Response:\n%+v\n", r)
wt.AssertNoErr(t, err)
wt.AssertNotNil(t, r, "response")
t.Logf("%d answers", len(r.Answer))
wt.AssertNoErr(t, err)
wt.AssertFalse(t, r.MsgHdr.Truncated, "DNS truncated response flag")
wt.AssertEqualInt(t, len(r.Answer), numAnswers, "number of answers (UDP-long)")
}
//////////////////////////////////////////////////////////////////////////////
// perform a DNS query and assert the reply code, number or answers, etc
func assertExchange(t *testing.T, z string, ty uint16, minAnswers int, maxAnswers int, expErr int) *dns.Msg {
c := new(dns.Client)
c.UDPSize = testUDPBufSize
m := new(dns.Msg)
m.RecursionDesired = true
m.SetQuestion(z, ty)
m.SetEdns0(testUDPBufSize, false) // we don't want to play with truncation here...
r, _, err := c.Exchange(m, fmt.Sprintf("127.0.0.1:%d", testPort))
t.Logf("Response:\n%+v\n", r)
wt.AssertNoErr(t, err)
if minAnswers == 0 && maxAnswers == 0 {
wt.AssertStatus(t, r.Rcode, expErr, "DNS response code")
} else {
wt.AssertStatus(t, r.Rcode, dns.RcodeSuccess, "DNS response code")
}
answers := len(r.Answer)
if minAnswers >= 0 && answers < minAnswers {
wt.Fatalf(t, "Number of answers >= %d", minAnswers)
}
if maxAnswers >= 0 && answers > maxAnswers {
wt.Fatalf(t, "Number of answers <= %d", maxAnswers)
}
return r
}
// run a UDP fallback server
func runLocalUDPServer(t *testing.T, laddr string, handler dns.HandlerFunc) (*dns.Server, string, error) {
t.Logf("Starting fallback UDP server at %s", laddr)
pc, err := net.ListenPacket("udp", laddr)
if err != nil {
return nil, "", err
}
server := &dns.Server{PacketConn: pc, Handler: handler, ReadTimeout: 100 * time.Millisecond}
go func() {
server.ActivateAndServe()
pc.Close()
}()
t.Logf("Fallback UDP server listening at %s", pc.LocalAddr())
return server, pc.LocalAddr().String(), nil
}
// run a TCP fallback server
func runLocalTCPServer(t *testing.T, laddr string, handler dns.HandlerFunc) (*dns.Server, string, error) {
t.Logf("Starting fallback TCP server at %s", laddr)
laddrTCP, err := net.ResolveTCPAddr("tcp", laddr)
if err != nil {
return nil, "", err
}
l, err := net.ListenTCP("tcp", laddrTCP)
if err != nil {
return nil, "", err
}
server := &dns.Server{Listener: l, Handler: handler, ReadTimeout: 100 * time.Millisecond}
go func() {
server.ActivateAndServe()
l.Close()
}()
t.Logf("Fallback TCP server listening at %s", l.Addr().String())
return server, l.Addr().String(), nil
}
+45
View File
@@ -0,0 +1,45 @@
package nameserver
type Status struct {
Domain string
Upstream []string
Address string
TTL uint32
Entries []EntryStatus
}
type EntryStatus struct {
Hostname string
Origin string
ContainerID string
Address string
Version int
Tombstone int64
}
func NewStatus(ns *Nameserver, dnsServer *DNSServer) *Status {
if dnsServer == nil {
return nil
}
ns.RLock()
defer ns.RUnlock()
var entryStatusSlice []EntryStatus
for _, entry := range ns.entries {
entryStatusSlice = append(entryStatusSlice, EntryStatus{
entry.Hostname,
entry.Origin.String(),
entry.ContainerID,
entry.Addr.String(),
entry.Version,
entry.Tombstone})
}
return &Status{
dnsServer.domain,
dnsServer.upstream.Servers,
dnsServer.address,
dnsServer.ttl,
entryStatusSlice}
}
-146
View File
@@ -1,146 +0,0 @@
package nameserver
import (
"bytes"
"fmt"
"github.com/miekg/dns"
. "github.com/weaveworks/weave/common"
"net"
"sync"
)
const (
RDNSDomain = "in-addr.arpa."
)
// +1 to also exclude a dot
var rdnsDomainLen = len(RDNSDomain) + 1
type Lookup interface {
LookupName(name string) (net.IP, error)
LookupInaddr(inaddr string) (string, error)
}
type Zone interface {
AddRecord(ident string, name string, ip net.IP) error
DeleteRecord(ident string, ip net.IP) error
DeleteRecordsFor(ident string) error
Lookup
}
type Record struct {
Ident string
Name string
IP net.IP
}
// Very simple data structure for now, with linear searching.
// TODO: make more sophisticated to improve performance.
type ZoneDb struct {
mx sync.RWMutex
recs []Record
}
type LookupError string
func (ops LookupError) Error() string {
return "Unable to find " + string(ops)
}
type DuplicateError struct {
}
func (dup DuplicateError) Error() string {
return "Tried to add a duplicate entry"
}
func (zone *ZoneDb) indexOf(match func(Record) bool) int {
for i, r := range zone.recs {
if match(r) {
return i
}
}
return -1
}
func (zone *ZoneDb) String() string {
zone.mx.RLock()
defer zone.mx.RUnlock()
var buf bytes.Buffer
for _, r := range zone.recs {
fmt.Fprintf(&buf, "%.12s %s %v\n", r.Ident, r.IP, r.Name)
}
return buf.String()
}
func (zone *ZoneDb) LookupName(name string) (net.IP, error) {
zone.mx.RLock()
defer zone.mx.RUnlock()
for _, r := range zone.recs {
if r.Name == name {
return r.IP, nil
}
}
return nil, LookupError(name)
}
func (zone *ZoneDb) LookupInaddr(inaddr string) (string, error) {
if revIP := net.ParseIP(inaddr[:len(inaddr)-rdnsDomainLen]); revIP != nil {
revIP4 := revIP.To4()
ip := []byte{revIP4[3], revIP4[2], revIP4[1], revIP4[0]}
Debug.Printf("[zonedb] Looking for address: %+v", ip)
zone.mx.RLock()
defer zone.mx.RUnlock()
for _, r := range zone.recs {
if r.IP.Equal(ip) {
return r.Name, nil
}
}
return "", LookupError(inaddr)
}
Warning.Printf("[zonedb] Asked to reverse lookup %s", inaddr)
return "", LookupError(inaddr)
}
func (zone *ZoneDb) AddRecord(ident string, name string, ip net.IP) error {
zone.mx.Lock()
defer zone.mx.Unlock()
fqdn := dns.Fqdn(name)
pred := func(r Record) bool { return r.Name == fqdn && r.IP.Equal(ip) && r.Ident == ident }
if index := zone.indexOf(pred); index != -1 {
return DuplicateError{}
}
zone.recs = append(zone.recs, Record{ident, fqdn, ip})
return nil
}
func (zone *ZoneDb) DeleteRecord(ident string, ip net.IP) error {
zone.mx.Lock()
defer zone.mx.Unlock()
pred := func(r Record) bool { return r.Ident == ident && r.IP.Equal(ip) }
if index := zone.indexOf(pred); index != -1 {
zone.recs = append(zone.recs[:index], zone.recs[index+1:]...)
return nil
}
return LookupError(ident)
}
func (zone *ZoneDb) DeleteRecordsFor(ident string) error {
zone.mx.Lock()
defer zone.mx.Unlock()
w := 0 // write index
for _, r := range zone.recs {
if r.Ident != ident {
zone.recs[w] = r
w++
}
}
zone.recs = zone.recs[:w]
return nil
}
func (zone *ZoneDb) ContainerDied(ident string) error {
Info.Printf("[zonedb] Container %s down. Removing records", ident)
return zone.DeleteRecordsFor(ident)
}
-88
View File
@@ -1,88 +0,0 @@
package nameserver
import (
wt "github.com/weaveworks/weave/testing"
"net"
"testing"
)
func TestZone(t *testing.T) {
var (
containerID = "deadbeef"
otherContainerID = "cowjuice"
successTestName = "test1.weave."
testAddr1 = "10.2.2.1/24"
)
var zone = new(ZoneDb)
ip, _, _ := net.ParseCIDR(testAddr1)
err := zone.AddRecord(containerID, successTestName, ip)
wt.AssertNoErr(t, err)
// Add a few more records to make the job harder
err = zone.AddRecord("abcdef0123", "adummy.weave.", net.ParseIP("10.2.0.1"))
wt.AssertNoErr(t, err)
err = zone.AddRecord("0123abcdef", "zdummy.weave.", net.ParseIP("10.2.0.2"))
wt.AssertNoErr(t, err)
// Check that the address is now there.
foundIP, err := zone.LookupName(successTestName)
wt.AssertNoErr(t, err)
if !foundIP.Equal(ip) {
t.Fatal("Unexpected result for", successTestName, foundIP)
}
// See if we can find the address by IP.
foundName, err := zone.LookupInaddr("1.2.2.10.in-addr.arpa.")
wt.AssertNoErr(t, err)
if foundName != successTestName {
t.Fatal("Unexpected result for", ip, foundName)
}
err = zone.AddRecord(containerID, successTestName, ip)
wt.AssertErrorType(t, err, (*DuplicateError)(nil), "duplicate add")
err = zone.AddRecord(otherContainerID, successTestName, ip)
// Delete the record for the original container
err = zone.DeleteRecord(containerID, ip)
wt.AssertNoErr(t, err)
_, err = zone.LookupName(successTestName)
wt.AssertNoErr(t, err)
err = zone.DeleteRecord(otherContainerID, ip)
wt.AssertNoErr(t, err)
// Check that the address is not there now.
_, err = zone.LookupName(successTestName)
wt.AssertErrorType(t, err, (*LookupError)(nil), "after deleting record")
// Delete a record that isn't there
err = zone.DeleteRecord(containerID, net.ParseIP("0.0.0.0"))
wt.AssertErrorType(t, err, (*LookupError)(nil), "when deleting record that doesn't exist")
}
func TestDeleteFor(t *testing.T) {
var (
id = "foobar"
name = "foo.weave."
addr1 = "10.2.2.3/24"
addr2 = "10.2.7.8/24"
)
zone := new(ZoneDb)
for _, addr := range []string{addr1, addr2} {
ip, _, _ := net.ParseCIDR(addr)
err := zone.AddRecord(id, name, ip)
wt.AssertNoErr(t, err)
}
_, err := zone.LookupName(name)
wt.AssertNoErr(t, err)
err = zone.DeleteRecordsFor(id)
_, err = zone.LookupName(name)
wt.AssertErrorType(t, err, (*LookupError)(nil), "after deleting records for ident")
}
+122
View File
@@ -0,0 +1,122 @@
package address
import (
"fmt"
"net"
"github.com/weaveworks/weave/common"
)
// Using 32-bit integer to represent IPv4 address
type Address uint32
type Offset uint32
type Range struct {
Start, End Address // [Start, End); Start <= End
}
func NewRange(start Address, size Offset) Range {
return Range{Start: start, End: Add(start, size)}
}
func (r Range) Size() Offset { return Subtract(r.End, r.Start) }
func (r Range) String() string { return fmt.Sprintf("%s-%s", r.Start, r.End-1) }
func (r Range) Overlaps(or Range) bool { return !(r.Start >= or.End || r.End <= or.Start) }
func (r Range) Contains(addr Address) bool { return addr >= r.Start && addr < r.End }
func (r Range) AsCIDRString() string {
prefixLen := 32
for size := r.Size(); size > 1; size = size / 2 {
if size%2 != 0 { // Size not a power of two; cannot be expressed as a CIDR.
return r.String()
}
prefixLen--
}
return CIDR{Start: r.Start, PrefixLen: prefixLen}.String()
}
type CIDR struct {
Start Address
PrefixLen int
}
func ParseIP(s string) (Address, error) {
if ip := net.ParseIP(s); ip != nil {
return FromIP4(ip), nil
}
return 0, &net.ParseError{Type: "IP Address", Text: s}
}
func ParseCIDR(s string) (Address, CIDR, error) {
if ip, ipnet, err := net.ParseCIDR(s); err != nil {
return 0, CIDR{}, err
} else if ipnet.IP.To4() == nil {
return 0, CIDR{}, &net.ParseError{Type: "Non-IPv4 address not supported", Text: s}
} else {
prefixLen, _ := ipnet.Mask.Size()
return FromIP4(ip), CIDR{Start: FromIP4(ipnet.IP), PrefixLen: prefixLen}, nil
}
}
func (cidr CIDR) Size() Offset { return 1 << uint(32-cidr.PrefixLen) }
func (cidr CIDR) Range() Range {
return NewRange(cidr.Start, cidr.Size())
}
func (cidr CIDR) HostRange() Range {
// Respect RFC1122 exclusions of first and last addresses
return NewRange(cidr.Start+1, cidr.Size()-2)
}
func (cidr CIDR) String() string {
return fmt.Sprintf("%s/%d", cidr.Start.String(), cidr.PrefixLen)
}
// FromIP4 converts an ipv4 address to our integer address type
func FromIP4(ip4 net.IP) (r Address) {
for _, b := range ip4.To4() {
r <<= 8
r |= Address(b)
}
return
}
// IP4 converts our integer address type to an ipv4 address
func (addr Address) IP4() (r net.IP) {
r = make([]byte, net.IPv4len)
for i := 3; i >= 0; i-- {
r[i] = byte(addr)
addr >>= 8
}
return
}
func (addr Address) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("%q", addr.String())), nil
}
func (addr Address) String() string {
return addr.IP4().String()
}
func Add(addr Address, i Offset) Address {
return addr + Address(i)
}
func Subtract(a, b Address) Offset {
common.Assert(a >= b)
return Offset(a - b)
}
func Min(a, b Offset) Offset {
if a > b {
return b
}
return a
}
func (addr Address) Reverse() Address {
return ((addr >> 24) & 0xff) | // move byte 3 to byte 0
((addr << 8) & 0xff0000) | // move byte 1 to byte 2
((addr >> 8) & 0xff00) | // move byte 2 to byte 1
((addr << 24) & 0xff000000) // byte 0 to byte 3
}
+65 -8
View File
@@ -3,18 +3,75 @@ package net
import (
"fmt"
"net"
"time"
"syscall"
"github.com/vishvananda/netlink"
)
func EnsureInterface(ifaceName string, wait int) (iface *net.Interface, err error) {
if iface, err = findInterface(ifaceName); err == nil || wait == 0 {
return
// Wait for an interface to come up.
func EnsureInterface(ifaceName string) (*net.Interface, error) {
iface, err := ensureInterface(ifaceName)
if err != nil {
return nil, err
}
for ; err != nil && wait > 0; wait-- {
time.Sleep(1 * time.Second)
iface, err = findInterface(ifaceName)
return iface, err
}
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
}
return
// check for currently-existing interface after subscribing, to avoid race
if iface, err := findInterface(ifaceName); err == nil {
return iface, nil
}
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
}
// Wait for an interface to come up and have a route added to the multicast subnet.
// 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) {
iface, err := ensureInterface(ifaceName)
if err != nil {
return nil, err
}
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)
check := func(route netlink.Route) bool {
return route.Dst != nil && route.Dst.IP.Equal(dest)
}
// 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) {
+57
View File
@@ -0,0 +1,57 @@
package net
import (
"fmt"
"net"
"github.com/vishvananda/netlink"
)
// 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(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)
}
return nil
})
}
// Two networks overlap if the start-point of one is inside the other.
func overlaps(n1, n2 *net.IPNet) bool {
return n1.Contains(n2.IP) || n2.Contains(n1.IP)
}
// 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(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)
}
return nil
})
}
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 {
if _, found := ignoreIfaceIndices[route.LinkIndex]; found {
continue
}
if err := check(route); err != nil {
return err
}
}
return nil
}
+25
View File
@@ -0,0 +1,25 @@
package net
import (
"net"
"testing"
"github.com/stretchr/testify/require"
)
func TestOverlap(t *testing.T) {
_, subnet1, _ := net.ParseCIDR("10.0.3.0/24")
_, subnet2, _ := net.ParseCIDR("10.0.4.0/24")
_, subnet3, _ := net.ParseCIDR("10.0.3.128/25")
_, subnet4, _ := net.ParseCIDR("10.0.3.192/25")
_, universe, _ := net.ParseCIDR("10.0.0.0/8")
require.False(t, overlaps(subnet1, subnet2))
require.False(t, overlaps(subnet2, subnet1))
require.True(t, overlaps(subnet1, subnet1))
require.True(t, overlaps(subnet1, subnet3))
require.True(t, overlaps(subnet1, subnet4))
require.False(t, overlaps(subnet2, subnet4))
require.False(t, overlaps(subnet4, subnet2))
require.True(t, overlaps(universe, subnet1))
require.True(t, overlaps(subnet1, universe))
}
+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)
}
Loaded 100 of 272 files, more files were not shown because too many files have changed in this diff. Show more