Compare commits

...
344 Commits
Author SHA1 Message Date
Adam Harrison 5ad227a8b3 Add release v1.6.0 2016-06-20 13:50:19 +01:00
Matthias Radestock 67fe1e5daf clarify init-peer-count deprecation warning
Fixes #2355
2016-06-20 03:54:16 +01:00
Matthias Radestock 99b475056a Merge branch '1.5' 2016-06-20 03:25:10 +01:00
Martynas Pumputis 804f4ad971 Add check-datapath to weaveutil
The utility is used to check whether a datapath can be created.
2016-06-20 03:24:30 +01:00
Martynas Pumputis 24d9a00f47 Add check-datapath to weaveutil
The utility is used to check whether a datapath can be created.
2016-06-20 03:20:46 +01:00
Matthias Radestock b00e7d088e Merge pull request #2372 from weaveworks/issues/2310-more-checkpoint
Add kernel version, Docker version and awsvpc setting to checkpoint info

Fixes #2310
2016-06-20 03:15:25 +01:00
Bryan Boreham c12e909882 Document what is sent in checkpoint request 2016-06-20 03:13:10 +01:00
Anita Buehrle 6356fbc882 Ops edits (#2371) 2016-06-20 03:04:53 +01:00
Matthias Radestock ca7220b775 Merge branch '1.5' 2016-06-19 02:51:52 +01:00
Matthias Radestock 619a02eefb fix a couple of typos 2016-06-19 02:51:11 +01:00
Martynas Pumputis 85c1a31e5c Merge pull request #2383 from weaveworks/proxy-buffer
Use buffered writer for http headers
2016-06-17 17:29:39 +01:00
Bryan Boreham f86ade6568 Squash up the code via 'defer' 2016-06-17 16:52:17 +01:00
Bryan Boreham 81fa4059df Use buffered writer for http headers 2016-06-17 16:52:17 +01:00
Matthias Radestock e77bf60d34 Merge branch '1.5' 2016-06-17 15:48:33 +01:00
Matthias Radestock 61cb5535db Merge pull request #2385 from weaveworks/2286-fix-check
Fix check before removing plugin sockets

Fixes the fix in #2292.
2016-06-17 15:47:48 +01:00
Bryan Boreham 63314beb2a Remove plugin sockets in 'weave reset' 2016-06-17 15:17:13 +01:00
Bryan Boreham 2d0be34a93 Fix check to see if plugin is running 2016-06-17 15:17:13 +01:00
Matthias Radestock 0abfdf4efb Merge branch '1.5' 2016-06-17 15:07:34 +01:00
Matthias Radestock b014de3f08 Merge pull request #2382 from weaveworks/issues/2379
Fix Docker 1.12 incompatibilities

Fixes #2379.
2016-06-17 15:06:55 +01:00
Bryan Boreham 56c275a9bf Copy Docker's response code through for raw stream.
This means when Docker says "101 UPGRADED" the proxy will say the
same, instead of "200 OK".  Docker 1.12 barfs if you do that.
2016-06-17 13:07:04 +01:00
Bryan Boreham ddd039761d Handle Docker daemon named 'dockerd' 2016-06-17 13:05:35 +01:00
Adam Harrison f4798fadf8 Merge pull request #2374 from weaveworks/issues/1726-plugin-arp
Perform arp configuration for plugin-connected containers
2016-06-17 10:19:53 +01:00
Bryan Boreham adda1c459c Fix plugin filter - truncate Docker ID to 12 chars 2016-06-16 18:03:45 +01:00
Matthias Radestock 979df93bff Merge pull request #2378 from weaveworks/issues/2364-observe-resolv-conf
Observe resolv.conf changes

Fixes #2364.
2016-06-16 17:13:10 +01:00
Adam Harrison 27e7ec08d4 Observe resolv.conf changes 2016-06-16 16:55:40 +01:00
Adam Harrison c6a777c4b0 Merge pull request #2366 from weaveworks/issues/2189-publish-weavedb
Publish weavedb container from release script
2016-06-16 11:03:27 +01:00
Bryan Boreham 7182ec0463 Merge pull request #2338 from weaveworks/awsvpc-docs
awsvpc docs
2016-06-15 16:59:54 +01:00
Bryan Boreham 425f17aab1 Mention that 'weave launch' now waits 2016-06-15 16:58:05 +01:00
Bryan Boreham 3494cc39c1 Expand description surrounding link to awsvpc mode 2016-06-15 16:58:05 +01:00
Bryan Boreham 2face0f318 Publish weavedb container from release script 2016-06-15 15:50:37 +01:00
Bryan Boreham bb203a2d18 Merge branch '1.5' 2016-06-15 13:50:47 +01:00
Bryan Boreham 95b42a532a Merge pull request #2367 from weaveworks/issues/2290-own-image
Use our own ubuntu image
2016-06-15 13:50:26 +01:00
Bryan Boreham 08e2c5c14e Fix link for 'docker run' docs 2016-06-15 13:48:25 +01:00
Bryan Boreham a4f3e763bb Use our own ubuntu image 2016-06-15 13:47:20 +01:00
Bryan Boreham 06feefe80f Merge branch '1.5' 2016-06-15 11:12:12 +01:00
Bryan Boreham 3dd7e13cc0 Merge pull request #2369 from weaveworks/issues/2335
Describe creating multiple networks using the plugin
2016-06-15 11:11:25 +01:00
Bryan Boreham fdf3afa935 Review feedback 2016-06-15 11:10:13 +01:00
Bryan Boreham 8bb8f70bb6 Perform arp configuration for plugin-connected containers 2016-06-14 17:53:34 +01:00
Bryan Boreham c71c630cf6 Use driver name to detect 'our' endpoints
so we don't have to remember all the endpoint IDs, or load them up
after a restart.
2016-06-14 17:52:45 +01:00
Matthias Radestock 179b6f7cf9 Merge pull request #2368 from weaveworks/small-doc-fixes
Small doc fixes
2016-06-14 16:26:17 +01:00
Bryan Boreham 1fe5c65fd4 Add kernel version, Docker version and awsvpc setting to checkpoint info 2016-06-14 12:24:37 +01:00
Bryan Boreham e1edce84d9 Extract checkForUpdates to its own file 2016-06-14 10:33:46 +01:00
Martynas Pumputis 73a393823a Merge pull request #2370 from weaveworks/awsvpc-clashes
Report a couple of options that are incompatible with --awsvpc
2016-06-14 10:12:37 +01:00
Bryan Boreham b32da8d69f Report a couple of options that are incompatible with --awsvpc 2016-06-13 16:53:27 +01:00
Bryan Boreham b993fd67ab Describe creating multiple networks using the plugin 2016-06-13 16:49:55 +01:00
Bryan Boreham e9770740d8 Use short name rather than long hex ID 2016-06-13 16:20:32 +01:00
Bryan Boreham 7abed164eb Clean up reference to 'above' 2016-06-13 16:11:01 +01:00
Matthias Radestock 7a1bd27a58 Merge branch '1.5' 2016-06-13 15:24:49 +01:00
Matthias Radestock aca34191da Merge pull request #2365 from weaveworks/issues/2283-validate-cidrs
Validate CIDRs in proxy before attaching a container

Fixes #2283
2016-06-13 15:15:48 +01:00
Martynas Pumputis df5508c0f3 Check CIDRs before attaching a container in proxy
If any cidr is invalid, a user will get the following error:
"docker: Error response from daemon: invalid WEAVE_CIDR: $CIDR".
2016-06-13 14:54:16 +01:00
Bryan Boreham e63fd0b38f Merge branch '1.5' 2016-06-13 12:00:46 +01:00
Bryan Boreham 47878cf5b6 Merge pull request #2362 from weaveworks/checkpoint-doc
Re-instate mention of checkpoint on install page
2016-06-13 11:13:16 +01:00
Anita Buehrle f0ea18171b tweaked the sentence a little 2016-06-13 10:57:35 +01:00
Bryan Boreham aa3f40ec8d Re-instate mention of checkpoint on install page lost in d1e004dedc 2016-06-13 10:57:19 +01:00
Bryan Boreham bf8c8833e5 Merge pull request #2356 from weaveworks/issues/2322-awsvpc-enhance
Transfer IPAM ranges to UnknownPeer if no peer is found during Shutdown
2016-06-10 14:00:36 +01:00
Martynas Pumputis 8569cbd4a3 Force transferring of IPAM ranges in a case of Shutdown
If no peer for transfer is found, we transfer them to
mesh.UnknownPeerName to notify the ring update tracker.

Makes the awsvpc tracker to remove local entries from
the VPC route table when the last peer calls "weave reset".
2016-06-10 11:51:31 +01:00
Adam Harrison adcb1018df Merge pull request #2345 from weaveworks/issues/2344-persist-more
Fix ring persistence after free space update
2016-06-09 16:37:57 +01:00
Adam Harrison f4b9a9b66c Merge pull request #2352 from weaveworks/issues/2318-moar-help
Print usage on --help argument to any command
2016-06-09 16:02:35 +01:00
Bryan Boreham 52d911c8c1 Merge branch '1.5' 2016-06-09 13:06:41 +00:00
Bryan Boreham d1314dd248 Fix broken link 2016-06-09 13:05:39 +00:00
Bryan Boreham 5db7a2bae1 Print usage on --help argument to any command 2016-06-09 12:44:37 +00:00
Bryan Boreham 71540e6c6d Clarify IAM role and policy 2016-06-09 10:41:34 +00:00
Anita Buehrle 3c45ba09f9 A few edits to improve awsvpc docs.
Fixed up the notes with spaces and bolding
Fixed some awkwardness in first paragraph
Fixed a couple of other sentences
2016-06-09 10:40:46 +00:00
Bryan Boreham cf87a84bd9 Merge pull request #2341 from weaveworks/issues/2017-awsvpc
Add AWSVPC. Fixes #2017
2016-06-08 17:24:55 +01:00
Bryan Boreham c77b2e8fb3 Tell the proxy that multicast is off in awsvpc mode.
Otherwise it will use the regular weavewait program which wait for the
multicast route to be added.
2016-06-08 16:44:54 +01:00
Bryan Boreham 37fd54f20b Perform AWSVPC checks for attach in script rather than in weaveutil 2016-06-08 16:44:54 +01:00
Martynas Pumputis fb9b4ddec0 Enable AWSVPC
This requires the following additional steps:

- Setting proxy_arp on on the bridge, because that's how we do
  the routing (check the comment at the place where we set it on).
- Disable ARP requests queueing, because it introduces unnecessary
  delay for the first ARP request resulting in a high latency of
  the first packet (on t2.micro machines I've encountered ~700ms
  latency). Also, the queue is bounded, so we might risking to drop
  the requests.
2016-06-08 16:44:54 +01:00
Martynas Pumputis 7670fed72c Add AWSVPC setup routines to the attach script
When running in the AWSVPC mode, we need to do the following:

- Keep the HW TX offloading on.
- Check that the allocated address belongs to the default subnet,
  because only one subnet is allowed and it should be the default one.

If it is not possible to determine whether AWSVPC is on, we ignore
all the steps, because the attachment routines have to work
regardless whether the router is running and the AWSVPC detection
is done by querying the /ipinfo/tracker endpoint served by
the router.
2016-06-08 16:44:54 +01:00
Martynas Pumputis de2a70861c Add GET /ipinfo/tracker
The endpoint returns the name of tracker. It is used to determine
whether the router is running a tracker.
2016-06-08 16:44:54 +01:00
Martynas Pumputis 9fc7be83c8 Create a Tracker when starting the router
The tracker's HandleUpdate method is called by the ring update routines.

In addition, we add a dummy overlay for AWSVPC to make `weave (status|
report)` to produce reasonable reports which includes AWS hosts
connection state.
2016-06-08 16:44:54 +01:00
Martynas Pumputis c260536429 Add AWSVPC Tracker
The tracker is responsible for managing the AWSVPC route tables
which is used to implement the underlay routing based on VPC.
2016-06-08 16:44:54 +01:00
Martynas Pumputis 59b3a3c8c5 Add the Tracker interface
An instance of the interface must implement the HandleUpdate method
which (in the future) will be called when any change to the IPAM ring
occurs.
2016-06-08 16:44:54 +01:00
Martynas Pumputis 49f2e208c3 Make ClaimForPeers to claim CIDR-aligned ranges 2016-06-08 16:44:54 +01:00
Martynas Pumputis 2266868648 Add the range.CIDRs functions 2016-06-08 16:44:54 +01:00
Bryan Boreham d1bba977f3 Don't stop reporting free space at the first unchanged range 2016-06-08 11:53:35 +01:00
Bryan Boreham e2bee6e8c6 Persist ring after updates caused by reportFreeSpace() 2016-06-08 11:52:38 +01:00
Bryan Boreham 6062627582 Merge branch '1.5' 2016-06-08 11:00:57 +01:00
Anita Buehrle efee8e73c6 minor edits also added link to guide 2016-06-08 11:00:33 +01:00
Bryan Boreham 37f820a168 Fail the script if any of the background tasks fails 2016-06-07 14:37:43 +01:00
Bryan Boreham c41ca708f3 Add AWS-VPC diagram 2016-06-07 13:58:22 +01:00
Bryan Boreham 73d7812dc5 Check that latest version of weave image exists
The smoke-tests are meant to be run in a context where you have just
uploaded a build. If that upload failed and we don't do this check
then we will download the 'latest' images from DockerHub.
2016-06-07 13:29:44 +01:00
Bryan Boreham 6a410a7749 Minor improvements 2016-06-07 13:01:42 +01:00
Adam Harrison 843a993600 Merge pull request #2332 from weaveworks/issues/2326-warn-rmpeer
Warn on 'weave reset' if DELETE fails
2016-06-06 16:56:38 +01:00
Bryan Boreham 0fc59a831c Initial awsvpc docs 2016-06-06 16:47:23 +01:00
Matthias Radestock 7ffbeead8d Merge pull request #2327 from weaveworks/issues/1960-mcast-option
Add an option to disable multicast route per-network

Fixes #1960.
2016-06-06 15:49:09 +01:00
Bryan Boreham 9a17439e2f Warn on 'weave reset' if weave container is not running 2016-06-06 15:46:30 +01:00
Bryan Boreham ab74d7b438 Improve plugin multicast docs 2016-06-06 15:41:21 +01:00
Adam Harrison d4de48aeee Merge branch '1.5'
Closes #2302
2016-06-06 15:21:54 +01:00
Adam Harrison eaa4bd1a6c Merge pull request #2333 from weaveworks/issues/2302-symlink-loop
Check if socket is identical to weave socket after trimming prefix.
2016-06-06 15:20:23 +01:00
Bryan Boreham c380a6f4ee Improve handling of build container
- Only mention the need for it on the one rule where it gets used.
- Don't delete it in `make clean-bin`.
2016-06-06 14:04:33 +01:00
Matthias Radestock faef371a8c Merge branch '1.5' 2016-06-06 11:51:19 +01:00
Bryan Boreham 39ba706a9b Run just one container to build all exes 2016-06-06 11:43:08 +01:00
Matthias Radestock aa4980bbda remove plugin cluster store section
it served no purpose and was misleading
2016-06-06 11:36:18 +01:00
Matthias Radestock 7f5bf67d33 cosmetic: fix some styling in plugin docs 2016-06-06 11:33:38 +01:00
Matthias Radestock e7e27796e4 eliminate some duplication in the plugin docs 2016-06-06 11:32:57 +01:00
Matthias Radestock 2491f49243 tweak plugin config docs 2016-06-06 11:32:15 +01:00
Matthias Radestock c25281e827 remove dead link 2016-06-06 11:30:10 +01:00
Matthias Radestock a4cd4d5a2f tweak plugin pitch 2016-06-06 11:22:07 +01:00
Bryan Boreham c6d1adf220 Revert "Do a build before the 'go test'"; it doesn't look like this goes faster after all
This reverts commit bd7e5d6921.
2016-06-05 14:56:05 +01:00
Bryan Boreham 5dd7590e5c Merge pull request #1978 from weaveworks/weave-1.5-doc-updates
Operational guide. Fixes #726, fixes #1102
2016-06-03 17:30:06 +01:00
Adam Harrison e092a517aa Operational guide 2016-06-03 17:12:29 +01:00
Adam Harrison 9b6154cf63 Merge pull request #2331 from weaveworks/issues/2320-plugin-warning
Augment warning on active plugin endpoints
2016-06-03 16:17:31 +01:00
Adam Harrison 1e4d86775f Merge pull request #2330 from weaveworks/issues/1988-proxy-plugin
Proxy shouldn't weave-attach containers which are using a network plugin
2016-06-03 15:48:35 +01:00
Bryan Boreham 2728bc0f9d Merge pull request #2305 from weaveworks/issues/2186-peer-list-persistence
Peer list persistence; Fixes #2186.
2016-06-03 14:31:01 +01:00
Bryan Boreham dac2124cd1 Don't weave-attach containers which are using a network plugin
Particularly intended for cases where the user has configured the
proxy and also said --net=weave, but will catch any other plugin
similarly
2016-06-03 14:11:15 +01:00
Anita Buehrle 37f5a17953 Update docs 2016-06-03 13:53:07 +01:00
Adam Harrison f9dd763774 Peer list persistence smoke test 2016-06-03 13:53:07 +01:00
Adam Harrison 4b758dd130 Implement weave launch --resume 2016-06-03 13:53:07 +01:00
Adam Harrison 5e03c0fe25 Revert #1967 (container reuse) 2016-06-03 13:53:07 +01:00
Adam Harrison 166d4afad8 Use persisted peer list on Docker restart 2016-06-03 13:53:07 +01:00
Bryan Boreham 153ed601be Check if socket is identical to weave socket after trimming prefix.
This allows us to run 'weave proxy -H /var/run/weave/weave.sock'
2016-06-03 13:34:43 +01:00
Bryan Boreham cbf4a0facd Move warning on plugin active into Go code 2016-06-02 17:19:51 +01:00
Bryan Boreham a1c3cba363 Invert multicast option in plugin
Now it's off by default, and there is an option to enable the
multicast route per-network.
We turn it on for the `weave` network created when we start the plugin.
2016-06-02 12:52:30 +01:00
Bryan Boreham 1d91c1a23b Cleanup: change ring test from 255 to 256 in length, which is more realistic 2016-06-01 17:51:21 +01:00
Martynas Pumputis f50fbc002e Change ipam Universe from Range to CIDR
There was no code to initialize it as anything else, and this lets us
bake in an assumption that it will always be a CIDR.
2016-06-01 17:06:31 +01:00
Adam Harrison dbc8b4fe1a Updated badges 2016-06-01 14:43:23 +01:00
Matthias Radestock b1fa417381 Merge branch '1.5' 2016-05-27 19:34:51 +01:00
Matthias Radestock 912e8fe119 Merge pull request #2323 from weaveworks/name-tests-properly
Name tests so they are picked up by runner
2016-05-27 19:33:34 +01:00
Bryan Boreham e2294482a6 Refactor: extract functional parts of weave export so they can be called from elsewhere 2016-05-27 16:52:53 +01:00
Adam Harrison b4fda4c98e Name tests so they are picked up by runner 2016-05-27 16:47:47 +01:00
Bryan Boreham d9da08792f Merge branch '1.5' 2016-05-27 13:40:29 +01:00
Bryan Boreham bf040105da Speed up smoke-tests by reducing timeout on 'docker stop' 2016-05-27 13:15:32 +01:00
Bryan Boreham e8ca085758 Merge branch '1.5' 2016-05-26 16:30:26 +00:00
Bryan Boreham 7300c33793 Add release v1.5.2 2016-05-26 16:10:07 +00:00
Bryan Boreham f5cd7b5073 Remove 'make update' from release process now we vendor dependencies 2016-05-26 16:10:07 +00:00
Adam Harrison f02b78ed73 Merge branch '1.5'
Closes #2282
2016-05-26 16:16:54 +01:00
Adam Harrison b3379df53d Merge pull request #2321 from weaveworks/issues/2282-check-subnets
Check validity of IP subnets
2016-05-26 16:16:39 +01:00
Adam Harrison c9525532ea Merge branch '1.5'
Closes #2300
2016-05-26 16:08:14 +01:00
Adam Harrison 654a811ef0 Merge pull request #2319 from weaveworks/issues/2300-spot-gateway
Don't release fake gateway address in plugin
2016-05-26 16:07:39 +01:00
Bryan Boreham cda1084777 Check validity of IP subnets
Be more consistent about this - check in the same way whether at startup
or during allocation.
2016-05-26 14:31:17 +00:00
Bryan Boreham fd257f43d1 Don't release fake gateway address
Watch out for Docker releasing the fake gateway address we create when
the network is set up, and don't pass it on to Weave IPAM.
2016-05-26 12:30:03 +00:00
Bryan Boreham 8ff814d976 Plugin and weaveutil depend on networking code 2016-05-26 11:35:10 +00:00
Bryan Boreham 7c819e7f5c Merge branch 'issues/1940-remove-proxy-attach-dep' 2016-05-26 11:33:37 +00:00
Bryan Boreham b417696c66 Wait 1ms before sending gratuitous arp 2016-05-26 11:17:04 +00:00
Martynas Pumputis f0109c1920 Merge pull request #2307 from weaveworks/issues/1940-remove-proxy-attach-dep
Move container attachment from sh to Go
2016-05-26 10:11:02 +01:00
Bryan Boreham 6dd109a5bb Only do ethtool tx off in bridged (pcap) mode.
Detect the bridge type in the same way the weave script does.
2016-05-25 17:34:13 +01:00
Bryan Boreham 2702b85ee5 Improve veth name construction; use a constant instead of "ethwe" 2016-05-25 17:34:13 +01:00
Bryan Boreham 8717fd15ed Use 'peer' and 'veth' instead of 'guest' and 'local', after review comments 2016-05-25 17:34:13 +01:00
Bryan Boreham 161446d55d Move error detection for attach() into Go 2016-05-25 17:34:13 +01:00
Bryan Boreham ac14b19db8 Move attach() and detach() from shell script to Go 2016-05-25 17:34:13 +01:00
Adam Harrison e3ef473cd3 Utility function to disable tx checksum offload 2016-05-25 17:29:45 +01:00
Bryan Boreham 4fc2835d92 Extract AddRoute function 2016-05-25 17:29:45 +01:00
Bryan Boreham 85023e0a76 Extract AttachContainer() function
Also move netlink and netns code from common to net

Creates interface if not already there
Adds addresses
Move the set link 'up' to after addresses are added
2016-05-25 17:29:45 +01:00
Bryan Boreham 0257274aa8 Extract basic veth setup 2016-05-25 17:23:49 +01:00
Bryan Boreham 9aaa7f19fd initialize via sub-function 2016-05-25 17:17:19 +01:00
Bryan Boreham 9f8b94645d Obtain IP address before setting up veth 2016-05-25 17:17:19 +01:00
Bryan Boreham c458deaf80 Slight cleanup 2016-05-25 17:17:19 +01:00
Bryan Boreham d59c170cd5 Remove veth on error 2016-05-25 17:17:19 +01:00
Bryan Boreham f11d91badf Extract veth creation and LinkSetUp to net package 2016-05-25 17:17:19 +01:00
Martynas Pumputis c63619bf7d Merge remote-tracking branch 'origin/1.5' 2016-05-25 17:09:48 +01:00
Martynas Pumputis d9d1956e0c Merge pull request #2313 from weaveworks/cni-gratuitous-arp
Send out arp update from CNI plugin
2016-05-25 17:06:10 +01:00
Bryan Boreham f695b3fd17 Smoke-test CNI arp update 2016-05-25 12:05:20 +01:00
Bryan Boreham ede00047db Send out arp update from CNI plugin 2016-05-25 12:05:20 +01:00
Adam Harrison 52377df340 Merge branch '1.5' 2016-05-24 17:10:52 +01:00
Adam Harrison 424d331a32 Merge pull request #2314 from weaveworks/disable-doc-previews
Temporarily disable doc previews
2016-05-24 17:10:28 +01:00
Adam Harrison fbbbb924a3 Temporarily disable doc previews 2016-05-24 16:46:41 +01:00
Matthias Radestock caeff127c9 Merge branch '1.5' 2016-05-24 11:27:42 +01:00
Bryan Boreham 56145ba518 Merge pull request #2311 from weaveworks/issues/2306-proxy-swarm-pending
Flush chunked response header immediately; fixes #2306.
2016-05-24 10:36:09 +01:00
Adam Harrison 885b6195f6 Flush chunked response header immediately 2016-05-24 08:28:24 +01:00
Bryan Boreham df3ccd362a Merge branch '1.5' 2016-05-19 15:58:55 +01:00
Bryan Boreham 5ed18994cd Send error messages about incompatible bridge settings to stderr 2016-05-19 15:58:15 +01:00
Adam Harrison 4dca07c9ff Merge pull request #2296 from weaveworks/issues/2284-die-another-way
Move kill-on-failure responsibility to caller of weave script
2016-05-18 12:19:44 +01:00
Adam Harrison b16de0c71e Merge pull request #2299 from weaveworks/issues/fix-offset-abuse
Change the type of entry.Free to address.Count
2016-05-18 12:04:54 +01:00
Adam Harrison a131703b2d Merge remote-tracking branch 'origin/1.5' 2016-05-18 10:29:55 +01:00
Adam Harrison 78672db440 Merge pull request #2292 from weaveworks/issues/2286-hang-torpedoes
Protect against Docker hanging trying to talk to a non-existent plugin
2016-05-18 10:27:13 +01:00
Adam Harrison 5b4906f822 Merge branch '1.5' 2016-05-18 09:46:01 +01:00
Adam Harrison ee9f8eec1c Merge pull request #2291 from weaveworks/issues/refactor-check-attached
Refactor wait_for_{proxy,attached}
2016-05-18 09:44:15 +01:00
Adam Harrison dc0e25a420 Add .ackrc which excludes vendor/ and .pkg/ 2016-05-17 10:37:56 +01:00
Martynas Pumputis 85cea672d3 Change the type of entry.Free to address.Count
This change is backward compatible, because the underlying types of
address.Offset (the previous type) and address.Count are the same
and the Gob encoder does not require types to exactly correspond.
2016-05-16 15:03:05 +01:00
Bryan Boreham ff81ecfd41 Move kill-on-failure responsibility to caller of weave script 2016-05-16 13:38:22 +01:00
Martynas Pumputis ef5f80a05a Merge pull request #2287 from weaveworks/donate-cidr-aligned
Donate CIDR-aligned ranges instead of just slicing at the mid-point.
2016-05-16 12:40:17 +01:00
Bryan Boreham d551b6b0a8 Review feedback 2016-05-13 16:27:28 +01:00
Bryan Boreham 1ec2c0fb3f Use a CIDR-aligned CIDR in unit tests 2016-05-13 16:27:28 +01:00
Bryan Boreham d1e2aa6d36 Use quick.Check instead of many specific tests 2016-05-13 16:27:28 +01:00
Bryan Boreham 64dc7a1f69 Donate power-of-2-aligned ranges instead of just slicing at the mid-point.
Also fix up space tests to match.
2016-05-13 16:27:28 +01:00
Bryan Boreham bc79a9187f Protect against Docker hanging trying to talk to a non-existent plugin
Remove the Unix socket from the filesystem before doing volume
operations, if the plugin is not running.
2016-05-12 15:40:04 +00:00
Martynas Pumputis 02d9c090ed Add wait_for_attached 2016-05-12 12:25:59 +01:00
Martynas Pumputis 671ed724d5 Move wait_for_proxy to config.sh 2016-05-12 12:15:20 +01:00
Bryan Boreham f37e742536 Merge branch '1.5' 2016-05-12 11:00:14 +00:00
Bryan Boreham 6c7f860418 Merge pull request #2278 from weaveworks/issues/fix-320-claim-test
Fix 320_claim_3_test.sh
2016-05-12 11:59:14 +01:00
Bryan Boreham 79d78ae97b Merge branch '1.5' 2016-05-12 09:57:48 +00:00
Bryan Boreham 4ff574744e Use ubuntu:trusty to ensure 'nc' is available 2016-05-12 09:53:29 +00:00
Martynas Pumputis 7c3e937ae8 Fix 320_claim_3_test 2016-05-11 12:18:39 +01:00
Martynas Pumputis 178438c470 Merge branch '1.5' 2016-05-10 17:36:56 +01:00
Martynas Pumputis 632694472c Merge pull request #2281 from weaveworks/issues/2275-request-order
Ignore IPAM requests for space until we have a ring

Fixes #2275
2016-05-10 17:36:26 +01:00
Martynas Pumputis 14e1145972 Merge branch '1.5' 2016-05-10 17:34:08 +01:00
Martynas Pumputis 8df1aa48a5 Merge pull request #2280 from weaveworks/sleep-perchance
Move sleep on reset from IPAM Allocator to weave script
2016-05-10 17:33:30 +01:00
Bryan Boreham 16b75f899e Move sleep on reset from IPAM Allocator to weave script 2016-05-10 16:03:08 +01:00
Bryan Boreham c3abe4f7b2 If we don't have a ring, just ignore a request for space. 2016-05-10 15:33:13 +01:00
Bryan Boreham f800813031 Test claim as first operation 2016-05-10 15:32:15 +01:00
Martynas Pumputis f7bddab4cb Merge branch '1.5' 2016-05-10 15:27:15 +01:00
Martynas Pumputis c434345960 Fix typo 2016-05-10 15:24:38 +01:00
Martynas Pumputis f9157acced Fix logging 2016-05-10 15:18:10 +01:00
Bryan Boreham e49bbe176d Make clear that we want the same universe throughout the test 2016-05-10 13:32:56 +01:00
Adam Harrison 0e694ed6b4 Merge branch '1.5' 2016-05-09 16:37:05 +01:00
Adam Harrison 9dfd7004cc Document adding new version to checkpoint API 2016-05-09 16:36:32 +01:00
Bryan Boreham cf6d0c8126 Merge branch '1.5' 2016-05-09 15:48:20 +01:00
Bryan Boreham 3963d4453a Remove mention of obsolete branch latest_release_doc_updates 2016-05-09 15:48:05 +01:00
Bryan Boreham 99b0b949e8 Merge branch '1.5' 2016-05-09 15:40:12 +01:00
Bryan Boreham ce5d2a6dd0 Add release v1.5.1 2016-05-09 15:16:21 +01:00
Adam Harrison 801283267a Merge branch '1.5'
Closes #1876.
2016-05-09 14:58:28 +01:00
Bryan Boreham a2e301c648 Merge pull request #2263 from weaveworks/issues/1876-weave-status-ipam-docs
Document `weave status ipam`. Fixes #1876.
2016-05-09 14:56:59 +01:00
Bryan Boreham 2249c7c648 Merge branch '1.5' 2016-05-09 14:46:06 +01:00
Bryan Boreham 0425d5bf6a Removed duplicated 'name' 2016-05-09 14:45:43 +01:00
Bryan Boreham de9597b6fa Merge branch '1.5' 2016-05-09 14:38:00 +01:00
Bryan Boreham 2fa576f3c0 Merge pull request #2265 from weaveworks/issues/2232-block-claim
Make claim block if the IPAM ring is not established. Fixes #2232
2016-05-09 14:37:37 +01:00
Adam Harrison fa29a4b4af Document weave status ipam 2016-05-09 13:41:42 +01:00
Martynas Pumputis d698aefbfc Make Claim to block if the IPAM ring is not established 2016-05-09 12:48:41 +01:00
Matthias Radestock db1ac2a343 Merge branch '1.5' 2016-05-09 11:50:09 +01:00
Matthias Radestock a3b6380296 Merge pull request #2264 from weaveworks/issues/2123-die-another-way
Kill process instead of container, on container restart.

Fixes #2123
2016-05-09 11:47:58 +01:00
Bryan Boreham 469d456c8b Smoke-test for container restart with proxy attach failure 2016-05-09 11:19:19 +01:00
Matthias Radestock 438efcccda Merge branch '1.5' 2016-05-07 20:19:18 +01:00
Matthias Radestock daa369b86c refactor: extract a couple of helpers in allocator tests
to reduce repetition
2016-05-07 20:18:21 +01:00
Matthias Radestock c3a2ea3dd8 Merge branch '1.5' 2016-05-07 17:57:10 +01:00
Matthias Radestock c54f2959ad Merge pull request #2266 from weaveworks/issues/2233-peer-name-docs
Document peer name

Fixes #2233.
2016-05-07 17:56:39 +01:00
Matthias Radestock ac0f284c5f Merge branch '1.5' 2016-05-07 14:43:04 +01:00
Matthias Radestock bd06553ce0 Merge pull request #2262 from weaveworks/issues/2194-reclaim-plugin-addrs
Reclaim plugin addresses without warning

Fixes #2194
2016-05-07 14:41:03 +01:00
Adam Harrison 158fa9d8e5 Reclaim plugin addresses using container ID of '_' 2016-05-07 14:35:15 +01:00
Anita Buehrle d234ca6ab8 clarified sentence, cosmetic 2016-05-06 12:41:59 -05:00
Bryan Boreham 8d71467802 Kill process instead of container, on container restart.
When Docker restarts a container and something goes wrong attaching
the network, kill the process inside the container instead of killing
the container, so that Docker will restart it again
2016-05-06 18:02:11 +01:00
Adam Harrison 8c5cbc738a Document peer name 2016-05-06 17:47:34 +01:00
Bryan Boreham 01e3d1d81b Refactor: extract kill_container function 2016-05-06 15:45:54 +01:00
Matthias Radestock 5e297c366e Merge branch '1.5' 2016-05-06 15:13:19 +01:00
Matthias Radestock 39b0d2a0cb Merge pull request #2261 from weaveworks/readme-update
remove duplicate content in README

Fixes #2217
2016-05-06 15:10:59 +01:00
Adam Harrison 260b416cd7 Include interface name in container-addrs output 2016-05-06 14:12:50 +01:00
Adam Harrison ad5845b67f Fix echo_ips 2016-05-06 14:12:19 +01:00
Bryan Boreham b426973679 Merge branch '1.5' 2016-05-06 12:46:21 +01:00
Bryan Boreham 7fcd837277 Cosmetic 2016-05-06 12:45:54 +01:00
Bryan Boreham 5993b3e846 Refactor: extract function to look up waiting channel for container 2016-05-06 12:45:03 +01:00
Matthias Radestock cf7ea357de Merge branch '1.5' 2016-05-06 11:21:57 +01:00
Matthias Radestock 4e48b956d9 Merge pull request #2250 from weaveworks/issues/2245-document-log-level
Document --log-level

Fixes #2245
2016-05-06 11:21:20 +01:00
Matthias Radestock 5b5d9d833a Merge pull request #2219 from weaveworks/export-wider
Document adding a route on non-Weave hosts
2016-05-06 11:20:27 +01:00
Bryan Boreham e16a4aa97a Document adding a route on non-Weave hosts 2016-05-06 11:13:15 +01:00
Bryan Boreham 12fa345d10 Punctuation 2016-05-06 11:10:34 +01:00
Matthias Radestock d2ea7e046d make clear that this repo is for Weave Net
...and make the links more relevant and 'active'
2016-05-06 10:55:29 +01:00
Matthias Radestock 1b50285609 add link to Weave Net product when first mentioning it
just as we do for scope and flux
2016-05-06 10:55:29 +01:00
Matthias Radestock 658c910862 fix typo 2016-05-06 10:55:29 +01:00
Anita Buehrle e1ab6a9035 removed installing heading 2016-05-06 10:55:29 +01:00
abuehrle 8c8709e527 added overview links to install 2016-05-06 10:55:29 +01:00
abuehrle d1e004dedc removed duplicate content added links to website 2016-05-06 10:55:29 +01:00
Matthias Radestock 8a827bebc8 Merge branch '1.5' 2016-05-05 17:56:57 +01:00
Matthias Radestock 4fc232be61 Merge pull request #2259 from weaveworks/issues/2214-fix-plugin-readme-links
Plugin README links refer to latest release docs

Fixes #2214.
2016-05-05 17:56:28 +01:00
Adam Harrison df2868fcdd Plugin README links refer to latest release docs 2016-05-05 16:32:53 +01:00
Matthias Radestock 32ba82efaf Merge branch '1.5' 2016-05-05 14:50:00 +01:00
Matthias Radestock b1aa52b3ed Merge pull request #2238 from weaveworks/missing-persist
Persist the IPAM ring on `weave reset` and `weave rmpeer`
2016-05-05 14:41:53 +01:00
Bryan Boreham 11c368531a Add --log-level to weave usage message, and document it and a couple of
other options on the proxy.
2016-05-05 12:44:40 +00:00
Matthias Radestock e389c8bb06 Merge branch '1.5' 2016-05-05 13:33:54 +01:00
Adam Harrison 608da90dc7 Publish doc previews for all branches, not just those named issues/* (#2258) 2016-05-05 13:33:28 +01:00
Matthias Radestock d3ddb71557 Merge branch '1.5' 2016-05-05 12:07:12 +01:00
Bryan Boreham fbb6e58b4f Add a section explaining WEAVE_MTU for fastdp (#2253)
Add a section explaining WEAVE_MTU for fastdp

Fixes #2198
2016-05-05 12:06:13 +01:00
Adam Harrison 34b95ee36f Merge branch '1.5' 2016-05-05 10:11:50 +01:00
Adam Harrison 1283ba4f61 Merge pull request #2249 from weaveworks/issues/2209-depersist-on-range-change
Better deleting of persisted IPAM data
2016-05-05 10:11:22 +01:00
Tom Wilkie a0982cb5e5 Merge pull request #2005 from weaveworks/sudo
Autodetect if we talk to docker via tcp in the Makefile
2016-05-05 10:05:56 +01:00
Tom Wilkie 0314a86e03 Autodetect if we talk to docker via tcp in the Makefile 2016-05-04 16:28:28 +01:00
Bryan Boreham d9d038ae35 Merge branch '1.5' 2016-05-04 15:38:33 +01:00
Bryan Boreham 87b61419f3 Move plugin arguments from separate doc into main plugin doc and add table of contents with sub-heading anchors 2016-05-04 14:44:10 +01:00
Adam Harrison 5fb6df5219 Merge branch '1.5' 2016-05-04 13:34:06 +01:00
Matthias Radestock b6647741b5 Merge pull request #2162 from weaveworks/issues/2034-log-rest-api
Add LogHandler for logging REST API calls at server side

Fixes #2034
2016-05-04 12:39:11 +01:00
Adam Harrison 0b4f9dbcf9 Merge pull request #2255 from weaveworks/issues/wordepress-stable-urls
Upgrade release branch doc publishing to wordepress 1.0.0
2016-05-04 11:59:22 +01:00
Martynas Pumputis 9644ed3f29 Merge pull request #2196 from weaveworks/issues/2194-claim-addr
Improve handling of `claim _`
2016-05-04 11:22:04 +01:00
Adam Harrison 1d52615452 Pin all branches to Wordepress 1.0.0 2016-05-04 11:07:12 +01:00
Adam Harrison 327a6f6d61 Upgrade to Wordepress 1.0.0
Release docs will now also be available with a stable 'latest' URL.
2016-05-04 11:01:07 +01:00
Adam Harrison 7aee9ebf84 Merge pull request #2254 from weaveworks/issues/wordepress-stable-urls
Upgrade to Wordepress 1.0.0
2016-05-04 10:56:34 +01:00
Adam Harrison 8f6087cfec Upgrade to Wordepress 1.0.0
Release docs will now also be available with a stable 'latest' URL.
2016-05-04 10:30:18 +01:00
Matthias Radestock 6022ad467a Merge branch '1.5' 2016-05-03 17:56:46 +01:00
Matthias Radestock 05652c0a3c Merge pull request #2252 from weaveworks/issues/2244-dns-noerror
Blank DNS response to AAAA queries where we have an A record

Fixes #2244.
2016-05-03 17:55:37 +01:00
Bryan Boreham 503546d2a9 Return DNS no-error with empty answer if we have a matching name
but the query is for a record type we don't support.
2016-05-03 17:33:51 +01:00
Bryan Boreham 3252a25e00 Broadcast initial ring after persisting it, in case we die between those operations 2016-05-03 17:23:00 +01:00
Bryan Boreham 6847a3551d Persist the ring in Shutdown() and AdminTakeoverRanges().
Also make AdminTakeoverRanges cause pending ops to be retried.
2016-05-03 17:23:00 +01:00
Bryan Boreham 70bd0ade45 Identifier "_" is address, not cidr 2016-05-03 15:47:45 +01:00
Martynas Pumputis 9dd1fbba39 Add LoggingHTTPHandler for logging REST requests at the server-side 2016-05-03 15:40:37 +01:00
Bryan Boreham 8405e0759a Clarify the function of the periodic ticker.
- it either retries for consensus or retries operations we would have tried previously.
There is no point retrying pending operations if we are waiting for consensus.
2016-05-03 15:13:20 +01:00
Adam Harrison bcc9a85e29 Merge remote-tracking branch 'origin/1.5' 2016-05-03 14:27:51 +01:00
Adam Harrison be3b0aa976 Merge pull request #2247 from weaveworks/pin-wordepress-version
Pin wordepress version used to publish site
2016-05-03 14:13:03 +01:00
Bryan Boreham 5ac8cc74d3 Refactor nested if-statements as switch 2016-05-03 13:52:36 +01:00
Adam Harrison 68e5ed575f Pin wordepress version
Use specific versions of wordepress for live and dev publishing
2016-05-03 13:47:37 +01:00
Bryan Boreham f6966548eb Delete persisted data when IPAM range changes 2016-05-03 13:20:04 +01:00
Bryan Boreham fb3ed9c1a6 Re-organise checking of persisted data so the failure paths bail out
of the function first.
Also bail out if there is no persisted ring, since persisted 'owned'
data isn't meaningful in that state.
2016-05-03 13:20:04 +01:00
Bryan Boreham 61fc5d56fb Refactor: move initialisation in the case that we don't use persisted data
out from loadPersistedData().
2016-05-03 13:20:04 +01:00
Bryan Boreham e96b68f0aa Don't use persisted data until we are sure it matches 2016-05-03 13:20:04 +01:00
Bryan Boreham bd23c79823 Merge branch '1.5' 2016-04-29 21:11:12 +01:00
Bryan Boreham 393fa3a333 Remove obsolete option and add --no-restart option 2016-04-29 19:49:21 +00:00
Bryan Boreham 27bdd3b4ad Revert "Log the error when something goes wrong requesting a claimed address"
This reverts commit 14bc1c987e.
2016-04-29 16:03:41 +01:00
Bryan Boreham 14bc1c987e Log the error when something goes wrong requesting a claimed address 2016-04-29 16:01:21 +01:00
Bryan Boreham fd7fcc762e Clarify the function of the periodic ticker.
- it either retries for consensus or retries operations we would have tried previously.
There is no point retrying pending operations if we are waiting for consensus.
2016-04-29 13:59:25 +01:00
Matthias Radestock 03afb54cdd remove cruft 2016-04-29 10:03:23 +01:00
Bryan Boreham 50e114863d Merge branch '1.5' 2016-04-28 09:40:04 +00:00
Bryan Boreham 9ce229ba3a Merge pull request #2202 from weaveworks/issues/1482-update-test320
Update test 320 to cope with persistence
2016-04-28 10:21:57 +01:00
Adam Harrison e6f031a3c9 Merge pull request #2218 from weaveworks/1.6-upgradeathon
Upgrade vendored libs
2016-04-28 10:13:44 +01:00
abuehrle 8b432a4d3e fixed broken links 2016-04-27 15:16:45 -05:00
Matthias Radestock 818f3e284b Merge branch '1.5' 2016-04-27 20:29:38 +01:00
Matthias Radestock 560f38330e Merge pull request #2230 from weaveworks/tweak-using-titles
Tweak titles under "Using Weave Net"
2016-04-27 20:17:04 +01:00
Matthias Radestock 5771ba15dd Merge branch '1.5' 2016-04-27 19:37:22 +01:00
abuehrle 9243bc2b0a fixed broken links 2016-04-27 13:25:32 -05:00
Matthias Radestock df5d6972e1 Tweak titles under "Using Weave Net"
- strip "Weave" and "Weave Net"
- "Integrating a Host Network with Weave" -> "Integrating with the
  Host Network"
- "Configuring Weave to Explicitly Use an IP Range" -> "Allocating IPs
  in a Specific Range"
2016-04-27 19:21:02 +01:00
Matthias Radestock 380cb9b0fc Merge pull request #2228 from weaveworks/tweak-using-sections
tweak 'Using Weave Net' section order
2016-04-27 18:50:14 +01:00
Matthias Radestock bd3b3791c6 tweak 'Using Weave Net' section order
- move "Managing Services in Weave Net" up so it comes after
  "Integrating a Host Network with Weave Net", to which it is deeply
  related
- move "Adding and Removing Hosts Dynamically" down so it is adjacent
  to the other sections which talk about peer connectivity
2016-04-27 18:37:28 +01:00
Matthias Radestock 77ced2ac55 Merge branch '1.5' 2016-04-27 18:25:23 +01:00
Matthias Radestock 7decce690a Merge pull request #2227 from weaveworks/2207-dedup-service-export-docs
dedup service export docs

Fixes #2207.
2016-04-27 17:31:34 +01:00
Matthias Radestock 65ecac6f1a cosmetic: consistency 2016-04-27 11:54:23 +01:00
Matthias Radestock 14a3898371 remove service-export doc since it duplicates content
...and update links accordingly.
2016-04-27 11:53:03 +01:00
Matthias Radestock c012650476 make service export section match whats in the separate doc 2016-04-27 11:48:56 +01:00
Adam Harrison d5fd62393f Merge branch '1.5' 2016-04-27 10:00:21 +01:00
Adam Harrison 789a6192f2 Merge pull request #2221 from weaveworks/issues/2216-fix-link
Fix broken link
2016-04-27 09:32:26 +01:00
Matthias Radestock 298fdc3d58 Merge branch '1.5' 2016-04-27 08:40:58 +01:00
Matthias Radestock 019a065d03 refactor: more logical question ordering 2016-04-27 08:40:07 +01:00
Matthias Radestock de1304d424 fix typo 2016-04-27 08:39:23 +01:00
Matthias Radestock ef915ca58f cosmetic: consistent formatting of FAQ questions 2016-04-27 08:36:25 +01:00
Matthias Radestock 72e134605c refactor: move pure code outside actor 2016-04-26 20:02:47 +01:00
Matthias Radestock 7cc2a1fe38 refactor: more descriptive function name 2016-04-26 17:33:04 +01:00
Adam Harrison 5de090999c Fix broken link 2016-04-26 17:29:34 +01:00
Adam Harrison 8ea5d999c2 Disable dry run mode 2016-04-26 17:29:26 +01:00
Adam Harrison 671dddf761 Merge pull request #2212 from weaveworks/issues/2114-wordepress-circleci
Incrementally publish 1.5 branch doc updates directly to live
2016-04-26 17:06:14 +01:00
Adam Harrison f4a117a8fc Enable dry run mode 2016-04-26 16:46:04 +01:00
Bryan Boreham 8fdbaf5745 Merge branch '1.5' 2016-04-26 15:43:12 +01:00
Bryan Boreham 0e655a2bbd Remove plugin-connected containers before end of test, to avoid delaying later cleanup.
Also use a shorter sequence to exclude the plugin when removing other containers.
2016-04-26 15:42:35 +01:00
Bryan Boreham 15054393fb Update profile package to latest version 2016-04-26 14:57:04 +01:00
Bryan Boreham f56d001703 Updated vendored libraries:
vendor/github.com/Sirupsen/logrus          to latest release 0.10.0
vendor/github.com/appc/cni                 to latest master
vendor/github.com/boltdb/bolt              to latest release 1.2.0
vendor/github.com/coreos/go-iptables       to latest release 0.1.0
vendor/github.com/fsouza/go-dockerclient   to latest master
vendor/github.com/docker/docker            to latest release 1.11.0
vendor/github.com/docker/libnetwork        to v0.7.0-dev.6 matching docker 1.11.0
vendor/github.com/docker/machine           to latest release v0.7.0
vendor/github.com/google/gopacket          to latest master
vendor/github.com/gorilla/context          to latest release 1.1
vendor/github.com/gorilla/mux              to latest release 1.1
vendor/github.com/hashicorp/go-cleanhttp   to latest master
vendor/github.com/miekg/dns                to latest master
vendor/github.com/opencontainers/runc      to latest release 0.1.0
vendor/github.com/stretchr/testify         unchanged at 1.1.3
vendor/github.com/vishvananda/netlink      to latest master
vendor/github.com/vishvananda/netns        to latest master
vendor/github.com/weaveworks/go-checkpoint unchanged; already at latest on master
vendor/github.com/weaveworks/go-odp        to latest master
vendor/github.com/weaveworks/mesh          to latest master
vendor/golang.org/x/crypto                 to latest master
2016-04-26 14:19:29 +01:00
Matthias Radestock 6ac248bfbf Merge branch '1.5' 2016-04-26 14:08:21 +01:00
Matthias Radestock a5739e8753 make plugin README link to other docs on github rather than website
until we have stable (non-version-specific) links for the latter.

Stop-gap for #2214.
2016-04-26 14:06:40 +01:00
Adam Harrison 79f2324d2b Update release process 2016-04-26 12:04:28 +01:00
Anita Buehrle c1a3ea925d Updated IPAM title and added punctuation 2016-04-26 10:10:41 +01:00
Adam Harrison 062c7a6fdc Minor doc fixes for incremental publish 2016-04-25 17:30:51 +01:00
Adam Harrison 6cc7f37dbb Publish /site from CI 2016-04-25 17:30:51 +01:00
Bryan Boreham 76601fed09 Fix up merge in unit test 2016-04-25 12:28:47 +01:00
Bryan Boreham 88a044f7f9 Merge pull request #2184 from weaveworks/2181-excessive-ring-frag
prevent excessive ring fragmentation due to repeated space requests. Fixes #2181
2016-04-25 11:54:12 +01:00
Matthias Radestock 03fcbbceb9 add CONTRIBUTING.md 2016-04-24 19:55:23 +01:00
Matthias Radestock cec88f154e Merge pull request #2205 from weaveworks/issues/2057-gc-nicknames
Add allocator.PeerGone

Fixes #2057
2016-04-22 17:42:43 +01:00
Martynas Pumputis 849edb1768 Add allocator.PeerGone
The function is registered within the mesh gc and it is responsible
for removing gone nicknames (#2057).
2016-04-22 17:29:27 +01:00
Bryan Boreham 981b78f680 Merge pull request #2203 from weaveworks/issues/2197-add-equals
Add equals sign to --dns arg, for consistency
2016-04-22 14:39:18 +01:00
Bryan Boreham ee75d91756 Merge branch '1.5' 2016-04-22 14:37:25 +01:00
Bryan Boreham d2450aae1c Merge pull request #2204 from weaveworks/issues/2193-dns-leak
Fixes #2123: Use ContainerID when restarting a container
2016-04-22 14:36:17 +01:00
Bryan Boreham b4f35f1d38 Add equals sign to --dns arg, for consistency 2016-04-22 11:16:58 +01:00
Matthias Radestock 9326775718 remove cruft 2016-04-21 18:59:28 +01:00
Bryan Boreham 76f9afb59c Removed errant spaces on bullets 2016-04-21 17:45:52 +01:00
Bryan Boreham 192fbe3a53 Update test 320 to cope with persistence 2016-04-21 16:19:59 +01:00
Martynas Pumputis 427b6d2902 Use ContainerID when restarting a container
Otherwise, we are risking to leak DNS entries which happens when
ContainerName is passed to `weave reset` and subsequent internal call
to nameserver creates a new entry instead of replacing the old one.
The ContainerID of the new entry is set to ContainerName and in such case
stopping the container will not remove the new entry.
2016-04-21 16:12:02 +01:00
Bryan Boreham 2f5a3f4513 Remove db-prefix now that persistence is in a volume 2016-04-21 14:32:45 +01:00
Bryan Boreham c984ec1e86 Fix port number in docs, and add to FAQ 2016-04-20 12:41:54 +01:00
Bryan Boreham 061d2b123f Add weavedb to the images pulled by 'weave setup' 2016-04-20 11:53:10 +01:00
Matthias Radestock c1d98ba59c refactor: extract a helper 2016-04-17 12:40:34 +01:00
Matthias Radestock a1823af745 refactor: make error returns less onerous 2016-04-17 12:40:34 +01:00
Matthias Radestock 4a01d23025 refactor: extract entry merging from ring merging 2016-04-17 12:40:34 +01:00
Matthias Radestock 27632b118f prevent excessive ring fragmentation due to repeated space requests
Merging a ring now indicates whether the ring has been updated. This
prevents excessive allocation retries, which in turn can result in
repeated space requests - i.e. if an allocation cannot proceed due to
lack of space - which in turn can result in excessive ring
fragmentation since many of the requests will result in the creation
of a new ring entries, due to the need to split ranges.

Fixes #2181.
2016-04-17 12:40:18 +01:00
155 changed files with 3885 additions and 1662 deletions

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
--ignore-dir=vendor
--ignore-dir=.pkg
+9 -3
View File
@@ -4,9 +4,6 @@
[submodule "vendor/github.com/vishvananda/netlink"] [submodule "vendor/github.com/vishvananda/netlink"]
path = vendor/github.com/vishvananda/netlink path = vendor/github.com/vishvananda/netlink
url = https://github.com/vishvananda/netlink.git url = https://github.com/vishvananda/netlink.git
[submodule "vendor/github.com/davecheney/profile"]
path = vendor/github.com/davecheney/profile
url = https://github.com/davecheney/profile.git
[submodule "vendor/github.com/gorilla/mux"] [submodule "vendor/github.com/gorilla/mux"]
path = vendor/github.com/gorilla/mux path = vendor/github.com/gorilla/mux
url = https://github.com/gorilla/mux.git url = https://github.com/gorilla/mux.git
@@ -70,3 +67,12 @@
[submodule "vendor/github.com/coreos/go-iptables"] [submodule "vendor/github.com/coreos/go-iptables"]
path = vendor/github.com/coreos/go-iptables path = vendor/github.com/coreos/go-iptables
url = https://github.com/coreos/go-iptables url = https://github.com/coreos/go-iptables
[submodule "vendor/github.com/pkg/profile"]
path = vendor/github.com/pkg/profile
url = https://github.com/pkg/profile
[submodule "vendor/github.com/j-keck/arping"]
path = vendor/github.com/j-keck/arping
url = https://github.com/j-keck/arping
[submodule "vendor/github.com/aws/aws-sdk-go"]
path = vendor/github.com/aws/aws-sdk-go
url = https://github.com/aws/aws-sdk-go
+55
View File
@@ -1,3 +1,58 @@
## Release 1.6.0
## Highlights
* A new [AWS VPC](https://weave.works/docs/net/latest/using-weave/awsvpc/) mode
that leverages Amazon Virtual Private Cloud for near-native network
performance, as an alternative to the Sleeve and Fast Datapath
overlays
* Docker 1.12 introduced some internal changes that made it
incompatible with previous version of Weave Net - this version
restores compatability
* An [operational guide](https://weave.works/docs/net/latest/operational-guide)
detailing best practices for deploying and operating Weave Net
* Changes to the target peer list are remembered across restarts,
making it much easier to deploy resilient networks
* The version checkpoint now transmits network mode (e.g. 'awsvpc')
and kernel/docker versions to us to inform and guide our development
efforts. See the [installation documentation](https://weave.works/docs/net/latest/installing-weave/)
for instructions on disabling the checkpoint feature.
More details in the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.6.0).
## Release 1.5.2
Bug fixes and minor improvements
* Weave Proxy did not flush the initial http header in the Docker event stream, which could cause Docker Swarm to show all nodes pending. #2306/#2311
* When using the CNI plugin, if a container was removed and quickly replaced by another using the same IP address, other containers might be unable to contact it. Send an address resolution protocol message to update them. #2313
* Avoid Docker hanging for 1 minute in `weave launch` if the plugin had not shut down cleanly #2286/#2292
* Print an error message when Weave bridge mode is changed without `weave reset` #2304
* Eliminate spurious warning message from IP allocator on plugin shutdown #2300/#2319
* Display error message when address requested in a subnet that is too small (/31 or /32) #2282/#2321
* Add short wait after `weave reset` to allow updates to reach peers #2280
* Weave was occasionally unable to claim existing IP address immediately after launch #2275/#2281
* Refactor some integration tests to run faster and more reliably #2291
## Release 1.5.1
Bug fixes and minor improvements
* Persisted data that was rendered invalid by changing peer name or
allocation range is detected and removed automatically, preventing
crashes and hangs #2246/#2209/#2249
* `weave rmpeer` persists the range takeover in case the peer on which
it was executed dies subsequently #2238
* Launching a container with an explicit `WEAVE_CIDR` in the
allocation range now waits instead of erroring if the allocator
hasn't finished initialising #2232/#2265
* Weave DNS now responds to AAAA queries with an empty answer section,
instead of NXDOMAIN which could be cached and block subsequent
resolution of A records #2244/#2252
* Many improvements to the documentation.
More details in the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.5.1).
## Release 1.5.0 ## Release 1.5.0
## Highlights ## Highlights
+32
View File
@@ -0,0 +1,32 @@
Thanks for your interest in contributing to this project and for
taking the time to read this guide.
# Code of conduct
**Be respectful:** disagreements between people are to be expected and are usually the sign of healthy debate and engagement. Disagreements can lead to frustration and even anger for some members. Turning to personal insults, intimidation or threatening behaviour does not improve the situation. Participants should thus take care to ensure all communications / interactions stay professional at all times.
**Be considerate:** remember that the community has members with a diverse background, many of whom have English as a second language. What might appear impolite, may simply be a result of a lack of knowledge of the English language. Bear in mind that actions will have an impact on other community members and the project as a whole, so take potential consequences into account before pursuing a course of action.
**Be forgiving:** humans are fallible and as such prone to make mistakes and inexplicably change their positions at times. Don't assume that other members are acting with malicious intent. Be prepared to forgive people who make mistakes and assist each other in learning from them. Playing a blame game doesn't help anyone.
# Issues
* Before reporting an issue make sure you search first if anybody has already reported a similar issue and whether or not it has been fixed.
* Make sure your issue report sufficiently details the problem:
* If you set up weave in some way other than that described by our [main instructions](https://github.com/zettio/weave#installation), please mention that.
* Include step-by-step instructions to reproduce the issue.
* State the OS platform you are using, i.e. `uname -a`
* `iptables` rules and firewalls can have a great impact on any Linux network; please note whether you have set up any rules and/or are running a firewall on your machine(s).
* If you want to tackle any open issue, make sure you let people know you are working on it.
## Pull Requests
* Please be generous describing your changes.
* Although it is highly suggested to include tests, they are not a hard requirement in order to get your contributions accepted.
* Keep pull requests small so core developers can review them quickly.
* Keep each pull request focused on a specific topic. If you have two things to change, create two pull requests. This helps reviewers to understand the meat of your contribution.
## Workflow
See [WorkingOnWeave](https://github.com/weaveworks/weave/wiki/WorkingOnWeave)
# Acknowledgements
Much of this taken from https://github.com/cloudescape/gowsdl/blob/master/CONTRIBUTING.md
+15 -12
View File
@@ -4,7 +4,7 @@ PUBLISH=publish_weave publish_weaveexec publish_plugin
.PHONY: all exes testrunner update tests lint publish $(PUBLISH) clean clean-bin prerequisites build run-smoketests .PHONY: all exes testrunner update tests lint publish $(PUBLISH) clean clean-bin prerequisites build run-smoketests
# If you can use docker without being root, you can do "make SUDO=" # If you can use docker without being root, you can do "make SUDO="
SUDO=sudo -E SUDO=$(shell docker info >/dev/null 2>&1 || echo "sudo -E")
BUILD_IN_CONTAINER=true BUILD_IN_CONTAINER=true
RM=--rm RM=--rm
RUN_FLAGS=-ti RUN_FLAGS=-ti
@@ -32,7 +32,7 @@ WEAVEEXEC_UPTODATE=.weaveexec.uptodate
PLUGIN_UPTODATE=.plugin.uptodate PLUGIN_UPTODATE=.plugin.uptodate
WEAVEDB_UPTODATE=.weavedb.uptodate WEAVEDB_UPTODATE=.weavedb.uptodate
IMAGES_UPTODATE=$(WEAVER_UPTODATE) $(WEAVEEXEC_UPTODATE) $(PLUGIN_UPTODATE) $(BUILD_UPTODATE) $(WEAVEDB_UPTODATE) IMAGES_UPTODATE=$(WEAVER_UPTODATE) $(WEAVEEXEC_UPTODATE) $(PLUGIN_UPTODATE) $(WEAVEDB_UPTODATE)
WEAVER_IMAGE=$(DOCKERHUB_USER)/weave WEAVER_IMAGE=$(DOCKERHUB_USER)/weave
WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec
@@ -60,26 +60,24 @@ BUILD_FLAGS=-i -ldflags "-extldflags \"-static\" -X main.version=$(WEAVE_VERSION
PACKAGE_BASE=$(shell go list -e ./) PACKAGE_BASE=$(shell go list -e ./)
all: $(WEAVE_EXPORT) all: $(WEAVE_EXPORT)
exes: $(EXES)
testrunner: $(RUNNER_EXE) $(TEST_TLS_EXE) testrunner: $(RUNNER_EXE) $(TEST_TLS_EXE)
$(EXES): $(BUILD_UPTODATE)
$(WEAVER_EXE) $(WEAVEPROXY_EXE) $(WEAVEUTIL_EXE): common/*.go common/*/*.go net/*.go net/*/*.go $(WEAVER_EXE) $(WEAVEPROXY_EXE) $(WEAVEUTIL_EXE): common/*.go common/*/*.go net/*.go net/*/*.go
$(WEAVER_EXE): router/*.go ipam/*.go ipam/*/*.go db/*.go nameserver/*.go prog/weaver/*.go $(WEAVER_EXE): router/*.go ipam/*.go ipam/*/*.go db/*.go nameserver/*.go prog/weaver/*.go
$(WEAVEPROXY_EXE): proxy/*.go prog/weaveproxy/*.go $(WEAVEPROXY_EXE): proxy/*.go prog/weaveproxy/*.go
$(WEAVEUTIL_EXE): prog/weaveutil/*.go $(WEAVEUTIL_EXE): prog/weaveutil/*.go net/*.go
$(SIGPROXY_EXE): prog/sigproxy/*.go $(SIGPROXY_EXE): prog/sigproxy/*.go
$(PLUGIN_EXE): prog/plugin/*.go plugin/*/*.go api/*.go common/*.go common/docker/*.go $(PLUGIN_EXE): prog/plugin/*.go plugin/*/*.go api/*.go common/*.go common/docker/*.go net/*.go
$(TEST_TLS_EXE): test/tls/*.go $(TEST_TLS_EXE): test/tls/*.go
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go $(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go $(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
$(WEAVEWAIT_NOMCAST_EXE): prog/weavewait/*.go net/*.go $(WEAVEWAIT_NOMCAST_EXE): prog/weavewait/*.go net/*.go
tests: $(BUILD_UPTODATE) tools/.git tests: tools/.git
lint: $(BUILD_UPTODATE) tools/.git lint: tools/.git
ifeq ($(BUILD_IN_CONTAINER),true) ifeq ($(BUILD_IN_CONTAINER),true)
$(EXES) tests lint: exes $(EXES) tests lint: $(BUILD_UPTODATE)
git submodule update --init git submodule update --init
@mkdir -p $(shell pwd)/.pkg @mkdir -p $(shell pwd)/.pkg
$(SUDO) docker run $(RM) $(RUN_FLAGS) \ $(SUDO) docker run $(RM) $(RUN_FLAGS) \
@@ -90,9 +88,10 @@ $(EXES) tests lint:
else else
exes: $(EXES)
$(WEAVER_EXE) $(WEAVEPROXY_EXE) $(PLUGIN_EXE): $(WEAVER_EXE) $(WEAVEPROXY_EXE) $(PLUGIN_EXE):
ifeq ($(COVERAGE),true) ifeq ($(COVERAGE),true)
go build -v $(BUILD_FLAGS) -o $@ ./$(@D)
$(eval COVERAGE_MODULES := $(shell (go list ./$(@D); go list -f '{{join .Deps "\n"}}' ./$(@D) | grep "^$(PACKAGE_BASE)/") | grep -v "^$(PACKAGE_BASE)/vendor/" | paste -s -d,)) $(eval COVERAGE_MODULES := $(shell (go list ./$(@D); go list -f '{{join .Deps "\n"}}' ./$(@D) | grep "^$(PACKAGE_BASE)/") | grep -v "^$(PACKAGE_BASE)/vendor/" | paste -s -d,))
go test -c -o ./$@ $(BUILD_FLAGS) -v -covermode=atomic -coverpkg $(COVERAGE_MODULES) ./$(@D)/ go test -c -o ./$@ $(BUILD_FLAGS) -v -covermode=atomic -coverpkg $(COVERAGE_MODULES) ./$(@D)/
else else
@@ -173,13 +172,17 @@ ifneq ($(UPDATE_LATEST),false)
endif endif
publish: $(PUBLISH) publish: $(PUBLISH)
ifeq ($(PUBLISH_WEAVEDB),true)
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker push $(DOCKERHUB_USER)/weavedb:latest
endif
clean-bin: clean-bin:
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(IMAGES) $(BUILD_IMAGE) -$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(IMAGES)
rm -rf $(EXES) $(IMAGES_UPTODATE) $(WEAVE_EXPORT) .pkg rm -rf $(EXES) $(IMAGES_UPTODATE) $(WEAVE_EXPORT) .pkg
clean: clean-bin clean: clean-bin
rm -rf test/tls/*.pem test/coverage.* test/coverage -$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(BUILD_IMAGE)
rm -rf test/tls/*.pem test/coverage.* test/coverage $(BUILD_UPTODATE)
build: build:
$(SUDO) go clean -i net $(SUDO) go clean -i net
+14 -170
View File
@@ -1,181 +1,25 @@
# Weave Net - Weaving Containers into Applications # Weave Net - Weaving Containers into Applications
[![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) [![Integration Tests](https://circleci.com/gh/weaveworks/weave/tree/master.svg?style=shield)](https://circleci.com/gh/weaveworks/weave)
[![Coverage Status](https://coveralls.io/repos/weaveworks/weave/badge.svg)](https://coveralls.io/r/weaveworks/weave)
[![Go Report Card](https://goreportcard.com/badge/github.com/weaveworks/weave)](https://goreportcard.com/report/github.com/weaveworks/weave)
[![Slack Status](https://slack.weave.works/badge.svg)](https://slack.weave.works)
[![Docker Pulls](https://img.shields.io/docker/pulls/weaveworks/weave.svg?maxAge=604800)](https://hub.docker.com/r/weaveworks/weave/)
# About Weaveworks # About Weaveworks
[Weaveworks](http://weave.works) is the company that delivers the most productive way for developers to connect, observe and control [Weaveworks](https://www.weave.works) is the company that delivers the most productive way for developers to connect, observe and control
Docker containers. The first product developed by Weaveworks, with nearly 5 million downloads to date, is Weave Net, enabling users to get started with Docker clusters and portable apps in a fraction of the time compared to other solutions. In addition, Weaveworks offers Weave Scope, a powerful container monitoring tool that automatically maps Docker containers and their interactions, as well as Weave Flux, a microservice router that automates the access of containers as services. Docker containers.
# Introduction This repository contains [Weave Net](https://www.weave.works/products/weave-net/), the first product developed by Weaveworks, and with nearly 5 million downloads to date, it enables you to get started with Docker clusters and portable apps in a fraction of the time compared with other solutions.
This document is intended to cover some of the more technical aspects of Weave Net. To learn about our products, including getting
started tutorials, visit our [website](http://weave.works) and Other products from Weaveworks include [Weave Scope](https://www.weave.works/products/weave-scope/), a powerful container monitoring tool that automatically maps Docker containers and their interactions, and [Weave Flux](https://www.weave.works/products/weave-flux/), a microservice router that automates the access of containers as services.
[documentation](http://docs.weave.works).
# Weave Net # Weave Net
Weave Net creates a virtual network that connects Docker containers Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery. With Weave Net, portable microservices-based applications consisting of multiple containers can run anywhere: on one host, multiple hosts or even across cloud providers and data centers. Applications use the network just as if the containers were all plugged into the same network switch, without having to configure port mappings, ambassadors or links.
deployed across multiple hosts and enables their automatic discovery.
![Weave Virtual Network](/docs/virtual-network.png?raw=true "Weave Virtual Network") Services provided by application containers on the Weave network can be exposed to the outside world, regardless of where they are running. Similarly, existing internal systems can be opened to accept connections from application containers irrespective of their location.
Applications use the network just as if the containers were all * [Install Weave Net](https://www.weave.works/docs/net/latest/installing-weave/)
plugged into the same network switch, with no need to configure port * [Find out more about Weave Net](https://www.weave.works/products/weave-net/)
mappings, links, etc. Services provided by application containers on
the weave network can be made accessible to the outside world,
regardless of where those containers are running. Similarly, existing
internal systems can be exposed to application containers irrespective
of their location.
![Weave Net Deployment](/docs/deployment.png?raw=true "Weave Net Deployment")
Weave Net can traverse firewalls and operate in partially connected
networks. Traffic can be encrypted, allowing hosts to be connected
across an untrusted network.
With Weave Net you can easily construct applications consisting of
multiple containers, running anywhere.
Weave Net works alongside Docker's existing (single host) networking
capabilities, so these can continue to be used by containers.
## Installation
To get started, you should be running Linux (kernel 3.8 or later) and have Docker
(version 1.6.0 or later) installed. Then install Weave Net with
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](https://docs.docker.com/installation/mac/#from-your-shell).
Then you can launch Weave Net directly from the OSX host.
For installing Weave Net on other platforms, follow the [integration guides](http://weave.works/product/integrations/).
Weave Net respects the environment variable `DOCKER_HOST`, so you can run
it locally to control a weave network on a remote host.
Weave Net
[periodically contacts Weaveworks servers for available versions](https://github.com/weaveworks/go-checkpoint).
New versions are announced in the log and in
[the status summary](http://docs.weave.works/weave/latest_release/troubleshooting.html#weave-status).
To disable this check, run:
```
export CHECKPOINT_DISABLE=1
```
before launching Weave Net.
## Quick Start Screencast
<a href="https://youtu.be/kihQCCT1ykE" alt="Click to watch the screencast" target="_blank">
<img src="/docs/hello-screencast.png" />
</a>
## Example
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 you would run:
host1$ weave launch
host1$ eval $(weave env)
host1$ docker run --name a1 -ti ubuntu
> 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 first line runs Weave Net. The second line configures your environment
so that containers launched via the docker command line are
automatically attached to the weave network. Finally, you run your
application container.
That's it! If your application consists of more than one container on
this host, you can simply launch them with `docker run` as appropriate.
Next you'll repeat similar steps on `$HOST2`...
host2$ weave launch $HOST1
host2$ eval $(weave env)
host2$ docker run --name a2 -ti ubuntu
The only difference, apart from the name of the application container,
is that weave is being told 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 you could instead have told the Weave Net instance on `$HOST1` to connect to
`$HOST2`, or told both about each other. Order does not matter here;
Weave Net automatically (re)connects to peers when they become
available. You can instruct Weave Net to connect to multiple peers by
supplying multiple addresses, separated by spaces; you can also
[add peers dynamically](http://docs.weave.works/weave/latest_release/features.html#dynamic-topologies).
Weave Net 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 you've got everything set up, let's confirm that your containers
can talk to each other...
In the container started on `$HOST1`...
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, in the container started on `$HOST2`...
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 you have it, two containers on separate hosts happily talking
to each other.
## Find out more
* [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? Please
[file an issue](https://github.com/weaveworks/weave/issues), or post
to the
[Weave Users Google Group](https://groups.google.com/a/weave.works/forum/#!forum/weave-users),
which you can email at weave-users@weave.works. When reporting a bug, please
include which version of Weave Net you are running, as shown by `weave
version`.
Follow us on Twitter:
[@weaveworks](https://twitter.com/weaveworks).
Check out our blog for the latest product and ecosystem news:
[Weaveworks Blog](https://www.weave.works/blog/).
IRC:
[#weavenetwork](https://botbot.me/freenode/weavenetwork/)
+46
View File
@@ -0,0 +1,46 @@
#!/bin/bash
set -e
set -o pipefail
PRODUCT=net
fatal() {
echo "$@" >&2
exit 1
}
if [ ! -d .git ] ; then
fatal "Current directory is not a git clone"
fi
if ! BRANCH=$(git symbolic-ref --short HEAD) || [ -z "$BRANCH" ] ; then
fatal "Could not determine branch"
fi
case "$BRANCH" in
issues/*)
VERSION="${BRANCH#issues/}"
TAGS="$VERSION"
;;
*)
if echo "$BRANCH" | grep -qE '^[0-9]+\.[0-9]+' ; then
DESCRIBE=$(git describe --match 'v*')
if ! VERSION=$(echo "$DESCRIBE" | grep -oP '(?<=^v)[0-9]+\.[0-9]+\.[0-9]+') ; then
fatal "Could not infer latest $BRANCH version from $DESCRIBE"
fi
TAGS="$VERSION latest"
else
VERSION="$BRANCH"
TAGS="$VERSION"
fi
;;
esac
for TAG in $TAGS ; do
echo ">>> Publishing $PRODUCT $VERSION to $1/docs/$PRODUCT/$TAG"
wordepress \
--url "$1" --user "$2" --password "$3" \
--product "$PRODUCT" --version "$VERSION" --tag "$TAG" \
publish site
done
+4 -4
View File
@@ -165,14 +165,14 @@ publish() {
setup setup
cd $PWD/$RELEASE_DIR cd $PWD/$RELEASE_DIR
UPDATE_LATEST=false IS_MAINLINE=false
if [ "$RELEASE_TYPE" = 'MAINLINE' ] ; then if [ "$RELEASE_TYPE" = 'MAINLINE' ] ; then
UPDATE_LATEST=true IS_MAINLINE=true
fi fi
if [ "$RELEASE_TYPE" = 'PRERELEASE' ] ; then if [ "$RELEASE_TYPE" = 'PRERELEASE' ] ; then
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER" 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 make UPDATE_LATEST=$IS_MAINLINE SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER publish
echo "** Docker images tagged and pushed" echo "** Docker images tagged and pushed"
echo "== Publishing pre-release on GitHub" echo "== Publishing pre-release on GitHub"
@@ -204,7 +204,7 @@ publish() {
echo '** Sanity checks OK for publishing tag' $LATEST_TAG as $DOCKERHUB_USER/weave:$VERSION 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" 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 make PUBLISH_WEAVEDB=$IS_MAINLINE UPDATE_LATEST=$IS_MAINLINE SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER publish
echo "** Docker images tagged and pushed" echo "** Docker images tagged and pushed"
echo "== Publishing release on GitHub" echo "== Publishing release on GitHub"
+16 -2
View File
@@ -32,7 +32,7 @@ test:
parallel: true parallel: true
- cd $SRCDIR; COVERDIR=test/coverage make RM= tests: - cd $SRCDIR; COVERDIR=test/coverage make RM= tests:
parallel: true parallel: true
- cd $SRCDIR; make COVERAGE=true RM= all: - cd $SRCDIR; make COVERAGE=true RM= exes all:
parallel: true parallel: true
- cd $SRCDIR; make testrunner: - cd $SRCDIR; make testrunner:
parallel: true parallel: true
@@ -55,10 +55,24 @@ teardown:
parallel: true parallel: true
deployment: deployment:
release:
branch: /[0-9]+\.[0-9]+/
owner: weaveworks
commands:
- go get github.com/weaveworks/wordepress && cd /home/ubuntu/src/github.com/weaveworks/wordepress && git checkout v1.0.0 && cd cmd/wordepress && go get
- cd $SRCDIR; bin/publish-site "$WP_LIVE_URL" "$WP_LIVE_USER" "$WP_LIVE_PASSWORD"
hub: hub:
branch: master branch: master
owner: weaveworks owner: weaveworks
commands: commands:
- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
- cd $SRCDIR; make clean-bin - cd $SRCDIR; make clean-bin
- cd $SRCDIR; make publish - cd $SRCDIR; make exes publish
# - go get github.com/weaveworks/wordepress && cd /home/ubuntu/src/github.com/weaveworks/wordepress && git checkout v1.0.0 && cd cmd/wordepress && go get
# - cd $SRCDIR; bin/publish-site "$WP_DEV_URL" "$WP_DEV_USER" "$WP_DEV_PASSWORD"
# issues:
# branch: /.*/
# owner: weaveworks
# commands:
# - go get github.com/weaveworks/wordepress && cd /home/ubuntu/src/github.com/weaveworks/wordepress && git checkout v1.0.0 && cd cmd/wordepress && go get
# - cd $SRCDIR; bin/publish-site "$WP_DEV_URL" "$WP_DEV_USER" "$WP_DEV_PASSWORD"
+9
View File
@@ -84,6 +84,15 @@ func (c *Client) Info() string {
return fmt.Sprintf("Docker API on %s: %v", c.Endpoint(), env) return fmt.Sprintf("Docker API on %s: %v", c.Endpoint(), env)
} }
func (c *Client) DockerVersion() string {
if env, err := c.Version(); err == nil {
if v, found := env.Map()["Version"]; found {
return v
}
}
return "unknown"
}
// AddObserver adds an observer for docker events // AddObserver adds an observer for docker events
func (c *Client) AddObserver(ob ContainerObserver) error { func (c *Client) AddObserver(ob ContainerObserver) error {
go func() { go func() {
+18
View File
@@ -0,0 +1,18 @@
package common
import (
"net/http"
)
type loggingHandler struct {
next http.Handler
}
func (l *loggingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Log.Debugf("[http] %s %s", r.Method, r.URL.RequestURI())
l.next.ServeHTTP(w, r)
}
func LoggingHTTPHandler(h http.Handler) http.Handler {
return &loggingHandler{next: h}
}
+27 -24
View File
@@ -2,12 +2,15 @@ package common
import ( import (
"fmt" "fmt"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
"net" "net"
"os" "os"
"runtime"
"strings" "strings"
"github.com/j-keck/arping"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
weavenet "github.com/weaveworks/weave/net"
) )
// Assert test is true, panic otherwise // Assert test is true, panic otherwise
@@ -25,31 +28,14 @@ func ErrorMessages(errors []error) string {
return strings.Join(result, "\n") return strings.Join(result, "\n")
} }
func WithNetNS(ns netns.NsHandle, work func() error) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
oldNs, err := netns.Get()
if err == nil {
defer oldNs.Close()
err = netns.Set(ns)
if err == nil {
defer netns.Set(oldNs)
err = work()
}
}
return err
}
type NetDev struct { type NetDev struct {
Name string
MAC net.HardwareAddr MAC net.HardwareAddr
CIDRs []*net.IPNet CIDRs []*net.IPNet
} }
// Search the network namespace of a process for interfaces matching a predicate // Search the network namespace of a process for interfaces matching a predicate
// Note that the predicate is called while the goroutine is inside the process' netns
func FindNetDevs(processID int, match func(link netlink.Link) bool) ([]NetDev, error) { func FindNetDevs(processID int, match func(link netlink.Link) bool) ([]NetDev, error) {
var netDevs []NetDev var netDevs []NetDev
@@ -62,7 +48,7 @@ func FindNetDevs(processID int, match func(link netlink.Link) bool) ([]NetDev, e
} }
defer ns.Close() defer ns.Close()
err = WithNetNS(ns, func() error { err = weavenet.WithNetNS(ns, func() error {
return forEachLink(func(link netlink.Link) error { return forEachLink(func(link netlink.Link) error {
if match(link) { if match(link) {
netDev, err := linkToNetDev(link) netDev, err := linkToNetDev(link)
@@ -97,7 +83,7 @@ func linkToNetDev(link netlink.Link) (*NetDev, error) {
return nil, err return nil, err
} }
netDev := &NetDev{MAC: link.Attrs().HardwareAddr} netDev := &NetDev{Name: link.Attrs().Name, MAC: link.Attrs().HardwareAddr}
for _, addr := range addrs { for _, addr := range addrs {
netDev.CIDRs = append(netDev.CIDRs, addr.IPNet) netDev.CIDRs = append(netDev.CIDRs, addr.IPNet)
} }
@@ -153,3 +139,20 @@ func GetBridgeNetDev(bridgeName string) ([]NetDev, error) {
return link.Attrs().Name == bridgeName return link.Attrs().Name == bridgeName
}) })
} }
// Do post-attach configuration of all veths we have created
func ConfigureARPforVeths(processID int, prefix string) error {
_, err := FindNetDevs(processID, func(link netlink.Link) bool {
ifName := link.Attrs().Name
if strings.HasPrefix(ifName, prefix) {
weavenet.ConfigureARPCache(ifName)
if addrs, err := netlink.AddrList(link, netlink.FAMILY_V4); err == nil {
for _, addr := range addrs {
arping.GratuitousArpOverIfaceByName(addr.IPNet.IP, ifName)
}
}
}
return false
})
return err
}
+5 -3
View File
@@ -6,8 +6,6 @@
* Create a [github token for * Create a [github token for
github-release](https://help.github.com/articles/creating-an-access-token-for-command-line-use/); github-release](https://help.github.com/articles/creating-an-access-token-for-command-line-use/);
set and export `$GITHUB_TOKEN` with this value set and export `$GITHUB_TOKEN` with this value
* Update all dependencies with `make update`
* Ensure you are still on the right branch, since Go may switch to `master` as part of `make update`
## Release Types ## Release Types
@@ -133,9 +131,13 @@ Finally, for **Mainline** releases only:
### Finish up ### Finish up
* If not on master, merge branch into master and push to GitHub. * If not on master, merge branch into master and push to GitHub.
* Merge the branch you are on into `latest_release_doc_updates` and push to GitHub.
* Close the [milestone](https://github.com/weaveworks/weave/milestones) in GitHub and create the next milestone * Close the [milestone](https://github.com/weaveworks/weave/milestones) in GitHub and create the next milestone
* Update the `#weavenetwork` topic heading on freenode (requires 'chanops' permission) * Update the `#weavenetwork` topic heading on freenode (requires 'chanops' permission)
* For a mainline release vX.Y.0, create a release branch X.Y from the
tag and push to GitHub - this will result in X.Y.0 site docs being
published to https://www.weave.works
* Add the new version of `weave-net` to the checkpoint system at
https://checkpoint-api.weave.works/admin
## Troubleshooting ## Troubleshooting
+1 -1
View File
@@ -33,7 +33,7 @@ func (g *allocate) Try(alloc *Allocator) bool {
return true return true
} }
if !alloc.universe.Overlaps(g.r.Range()) { if !alloc.universe.Range().Overlaps(g.r.Range()) {
g.resultChan <- allocateResult{err: fmt.Errorf("range %s out of bounds: %s", g.r, alloc.universe)} g.resultChan <- allocateResult{err: fmt.Errorf("range %s out of bounds: %s", g.r, alloc.universe)}
return true return true
} }
+128 -55
View File
@@ -14,6 +14,7 @@ import (
"github.com/weaveworks/weave/ipam/paxos" "github.com/weaveworks/weave/ipam/paxos"
"github.com/weaveworks/weave/ipam/ring" "github.com/weaveworks/weave/ipam/ring"
"github.com/weaveworks/weave/ipam/space" "github.com/weaveworks/weave/ipam/space"
"github.com/weaveworks/weave/ipam/tracker"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
) )
@@ -55,7 +56,7 @@ type Allocator struct {
stopChan chan<- struct{} stopChan chan<- struct{}
ourName mesh.PeerName ourName mesh.PeerName
seed []mesh.PeerName // optional user supplied ring seed seed []mesh.PeerName // optional user supplied ring seed
universe address.Range // superset of all ranges universe address.CIDR // superset of all ranges
ring *ring.Ring // information on ranges owned by all peers ring *ring.Ring // information on ranges owned by all peers
space space.Space // more detail on ranges owned by us space space.Space // more detail on ranges owned by us
owned map[string]ownedData // who owns what addresses, indexed by container-ID owned map[string]ownedData // who owns what addresses, indexed by container-ID
@@ -80,26 +81,39 @@ type Config struct {
OurUID mesh.PeerUID OurUID mesh.PeerUID
OurNickname string OurNickname string
Seed []mesh.PeerName Seed []mesh.PeerName
Universe address.Range Universe address.CIDR
IsObserver bool IsObserver bool
Quorum func() uint Quorum func() uint
Db db.DB Db db.DB
IsKnownPeer func(name mesh.PeerName) bool IsKnownPeer func(name mesh.PeerName) bool
Tracker tracker.LocalRangeTracker
} }
// NewAllocator creates and initialises a new Allocator // NewAllocator creates and initialises a new Allocator
func NewAllocator(config Config) *Allocator { func NewAllocator(config Config) *Allocator {
var participant paxos.Participant var participant paxos.Participant
var alloc *Allocator
var onUpdate ring.OnUpdate
if config.IsObserver { if config.IsObserver {
participant = paxos.NewObserver() participant = paxos.NewObserver()
} else { } else {
participant = paxos.NewNode(config.OurName, config.OurUID, 0) participant = paxos.NewNode(config.OurName, config.OurUID, 0)
} }
return &Allocator{
if config.Tracker != nil {
onUpdate = func(prev []address.Range, curr []address.Range, local bool) {
if err := config.Tracker.HandleUpdate(prev, curr, local); err != nil {
alloc.errorf("HandleUpdate failed: %s", err)
}
}
}
alloc = &Allocator{
ourName: config.OurName, ourName: config.OurName,
seed: config.Seed, seed: config.Seed,
universe: config.Universe, universe: config.Universe,
ring: ring.New(config.Universe.Start, config.Universe.End, config.OurName), ring: ring.New(config.Universe.Range().Start, config.Universe.Range().End, config.OurName, onUpdate),
owned: make(map[string]ownedData), owned: make(map[string]ownedData),
db: config.Db, db: config.Db,
paxos: participant, paxos: participant,
@@ -109,11 +123,39 @@ func NewAllocator(config Config) *Allocator {
dead: make(map[string]time.Time), dead: make(map[string]time.Time),
now: time.Now, now: time.Now,
} }
return alloc
}
func ParseCIDRSubnet(cidrStr string) (cidr address.CIDR, err error) {
cidr, err = address.ParseCIDR(cidrStr)
if err != nil {
return
}
if !cidr.IsSubnet() {
err = fmt.Errorf("invalid subnet - bits after network prefix are not all zero: %s", cidrStr)
}
if cidr.Size() < MinSubnetSize {
err = fmt.Errorf("invalid subnet - smaller than minimum size %d: %s", MinSubnetSize, cidrStr)
}
return
} }
// Start runs the allocator goroutine // Start runs the allocator goroutine
func (alloc *Allocator) Start() { func (alloc *Allocator) Start() {
alloc.loadPersistedData() loadedPersistedData := alloc.loadPersistedData()
switch {
case loadedPersistedData && len(alloc.seed) != 0:
alloc.infof("Found persisted IPAM data, ignoring supplied IPAM seed")
case loadedPersistedData:
alloc.infof("Initialising with persisted data")
case len(alloc.seed) != 0:
alloc.infof("Initialising with supplied IPAM seed")
alloc.createRing(alloc.seed)
case alloc.paxos.IsElector():
alloc.infof("Initialising via deferred consensus")
default:
alloc.infof("Initialising as observer - awaiting IPAM data from another peer")
}
actionChan := make(chan func(), mesh.ChannelSize) actionChan := make(chan func(), mesh.ChannelSize)
stopChan := make(chan struct{}) stopChan := make(chan struct{})
alloc.actionChan = actionChan alloc.actionChan = actionChan
@@ -263,7 +305,7 @@ func (alloc *Allocator) Lookup(ident string, r address.Range) ([]address.CIDR, e
} }
// Claim an address that we think we should own (Sync) // Claim an address that we think we should own (Sync)
func (alloc *Allocator) Claim(ident string, cidr address.CIDR, isContainer, noErrorOnUnknown bool) error { func (alloc *Allocator) Claim(ident string, cidr address.CIDR, isContainer, noErrorOnUnknown bool, hasBeenCancelled func() bool) error {
resultChan := make(chan error) resultChan := make(chan error)
op := &claim{ op := &claim{
resultChan: resultChan, resultChan: resultChan,
@@ -271,6 +313,7 @@ func (alloc *Allocator) Claim(ident string, cidr address.CIDR, isContainer, noEr
cidr: cidr, cidr: cidr,
isContainer: isContainer, isContainer: isContainer,
noErrorOnUnknown: noErrorOnUnknown, noErrorOnUnknown: noErrorOnUnknown,
hasBeenCancelled: hasBeenCancelled,
} }
alloc.doOperation(op, &alloc.pendingClaims) alloc.doOperation(op, &alloc.pendingClaims)
return <-resultChan return <-resultChan
@@ -318,13 +361,13 @@ func (alloc *Allocator) ContainerStarted(ident string) {
} }
} }
func (alloc *Allocator) AllContainerIDs(ids []string) { func (alloc *Allocator) PruneOwned(ids []string) {
idmap := make(map[string]struct{}, len(ids))
for _, id := range ids {
idmap[id] = struct{}{}
}
alloc.actionChan <- func() { alloc.actionChan <- func() {
idmap := make(map[string]struct{}, len(ids)) alloc.pruneOwned(idmap)
for _, id := range ids {
idmap[id] = struct{}{}
}
alloc.syncOwned(idmap)
} }
} }
@@ -400,11 +443,12 @@ func (alloc *Allocator) Shutdown() {
alloc.cancelOps(&alloc.pendingClaims) alloc.cancelOps(&alloc.pendingClaims)
alloc.cancelOps(&alloc.pendingAllocates) alloc.cancelOps(&alloc.pendingAllocates)
alloc.cancelOps(&alloc.pendingPrimes) alloc.cancelOps(&alloc.pendingPrimes)
if heir := alloc.pickPeerForTransfer(); heir != mesh.UnknownPeerName { heir := alloc.pickPeerForTransfer()
alloc.ring.Transfer(alloc.ourName, heir) alloc.ring.Transfer(alloc.ourName, heir)
alloc.space.Clear() alloc.space.Clear()
if heir != mesh.UnknownPeerName {
alloc.persistRing()
alloc.gossip.GossipBroadcast(alloc.Gossip()) alloc.gossip.GossipBroadcast(alloc.Gossip())
time.Sleep(100 * time.Millisecond)
} }
doneChan <- struct{}{} doneChan <- struct{}{}
} }
@@ -439,7 +483,7 @@ func (alloc *Allocator) AdminTakeoverRanges(peerNameOrNickname string) address.C
} }
before := alloc.space.NumFreeAddresses() before := alloc.space.NumFreeAddresses()
alloc.space.AddRanges(newRanges) alloc.ringUpdated()
after := alloc.space.NumFreeAddresses() after := alloc.space.NumFreeAddresses()
alloc.gossip.GossipBroadcast(alloc.Gossip()) alloc.gossip.GossipBroadcast(alloc.Gossip())
@@ -485,6 +529,22 @@ func (alloc *Allocator) annotatePeernames(names []mesh.PeerName) []string {
return res return res
} }
// PeerGone removes nicknames of peers which are no longer mentioned
// in the ring. Async.
//
// NB: the function is invoked by the gossip library routines and should be
// registered manually.
func (alloc *Allocator) PeerGone(peerName mesh.PeerName) {
alloc.debugf("PeerGone: peer %s", peerName)
alloc.actionChan <- func() {
ringPeers := alloc.ring.PeerNames()
if _, ok := ringPeers[peerName]; !ok {
delete(alloc.nicknames, peerName)
}
}
}
func decodeRange(msg []byte) (r address.Range, err error) { func decodeRange(msg []byte) (r address.Range, err error) {
decoder := gob.NewDecoder(bytes.NewReader(msg)) decoder := gob.NewDecoder(bytes.NewReader(msg))
return r, decoder.Decode(&r) return r, decoder.Decode(&r)
@@ -497,9 +557,11 @@ func (alloc *Allocator) OnGossipUnicast(sender mesh.PeerName, msg []byte) error
alloc.actionChan <- func() { alloc.actionChan <- func() {
switch msg[0] { switch msg[0] {
case msgSpaceRequest: case msgSpaceRequest:
// some other peer asked us for space alloc.debugln("Peer", sender, "asked me for space")
r, err := decodeRange(msg[1:]) r, err := decodeRange(msg[1:])
if err == nil { // If we don't have a ring, just ignore a request for space.
// They'll probably ask again later.
if err == nil && !alloc.ring.Empty() {
alloc.donateSpace(r, sender) alloc.donateSpace(r, sender)
} }
resultChan <- err resultChan <- err
@@ -614,11 +676,13 @@ func (alloc *Allocator) actorLoop(actionChan <-chan func(), stopChan <-chan stru
case <-stopChan: case <-stopChan:
return return
case <-alloc.ticker.C: case <-alloc.ticker.C:
// Retry things in case messages got lost between here and recipients
if alloc.awaitingConsensus { if alloc.awaitingConsensus {
alloc.propose() alloc.propose()
} else {
alloc.tryPendingOps()
} }
alloc.removeDeadContainers() alloc.removeDeadContainers()
alloc.tryPendingOps()
} }
alloc.assertInvariants() alloc.assertInvariants()
@@ -647,8 +711,8 @@ func (alloc *Allocator) establishRing() {
func (alloc *Allocator) createRing(peers []mesh.PeerName) { func (alloc *Allocator) createRing(peers []mesh.PeerName) {
alloc.debugln("Paxos consensus:", peers) alloc.debugln("Paxos consensus:", peers)
alloc.ring.ClaimForPeers(normalizeConsensus(peers)) alloc.ring.ClaimForPeers(normalizeConsensus(peers))
alloc.gossip.GossipBroadcast(alloc.Gossip())
alloc.ringUpdated() alloc.ringUpdated()
alloc.gossip.GossipBroadcast(alloc.Gossip())
} }
func (alloc *Allocator) ringUpdated() { func (alloc *Allocator) ringUpdated() {
@@ -727,7 +791,6 @@ func (alloc *Allocator) update(sender mesh.PeerName, msg []byte) error {
reader := bytes.NewReader(msg) reader := bytes.NewReader(msg)
decoder := gob.NewDecoder(reader) decoder := gob.NewDecoder(reader)
var data gossipState var data gossipState
var err error
if err := decoder.Decode(&data); err != nil { if err := decoder.Decode(&data); err != nil {
return err return err
@@ -742,9 +805,10 @@ func (alloc *Allocator) update(sender mesh.PeerName, msg []byte) error {
// If someone sent us a ring, merge it into ours. Note this will move us // If someone sent us a ring, merge it into ours. Note this will move us
// out of the awaiting-consensus state if we didn't have a ring already. // out of the awaiting-consensus state if we didn't have a ring already.
case data.Ring != nil: case data.Ring != nil:
switch err = alloc.ring.Merge(*data.Ring); err { updated, err := alloc.ring.Merge(*data.Ring)
switch err {
case nil: case nil:
if !alloc.ring.Empty() { if updated {
alloc.pruneNicknames() alloc.pruneNicknames()
alloc.ringUpdated() alloc.ringUpdated()
} }
@@ -806,7 +870,6 @@ func (alloc *Allocator) donateSpace(r address.Range, to mesh.PeerName) {
// more. // more.
defer alloc.sendRingUpdate(to) defer alloc.sendRingUpdate(to)
alloc.debugln("Peer", to, "asked me for space")
chunk, ok := alloc.space.Donate(r) chunk, ok := alloc.space.Donate(r)
if !ok { if !ok {
free := alloc.space.NumFreeAddressesInRange(r) free := alloc.space.NumFreeAddressesInRange(r)
@@ -849,7 +912,9 @@ func (alloc *Allocator) reportFreeSpace() {
for _, r := range ranges { for _, r := range ranges {
freespace[r.Start] = alloc.space.NumFreeAddressesInRange(r) freespace[r.Start] = alloc.space.NumFreeAddressesInRange(r)
} }
alloc.ring.ReportFree(freespace) if alloc.ring.ReportFree(freespace) {
alloc.persistRing()
}
} }
// Persistent data // Persistent data
@@ -870,52 +935,57 @@ func (alloc *Allocator) persistRing() {
} }
} }
func (alloc *Allocator) loadPersistedData() { // Returns true if persisted data is to be used, otherwise false
func (alloc *Allocator) loadPersistedData() bool {
var checkPeerName mesh.PeerName var checkPeerName mesh.PeerName
nameFound, err := alloc.db.Load(nameIdent, &checkPeerName) nameFound, err := alloc.db.Load(nameIdent, &checkPeerName)
if err != nil { if err != nil {
alloc.fatalf("Error loading persisted peer name: %s", err) alloc.fatalf("Error loading persisted peer name: %s", err)
} }
ringFound, err := alloc.db.Load(ringIdent, &alloc.ring) var persistedRing *ring.Ring
ringFound, err := alloc.db.Load(ringIdent, &persistedRing)
if err != nil { if err != nil {
alloc.fatalf("Error loading persisted IPAM data: %s", err) alloc.fatalf("Error loading persisted IPAM data: %s", err)
} }
ownedFound, err := alloc.db.Load(ownedIdent, &alloc.owned) var persistedOwned map[string]ownedData
ownedFound, err := alloc.db.Load(ownedIdent, &persistedOwned)
if err != nil { if err != nil {
alloc.fatalf("Error loading persisted address data: %s", err) alloc.fatalf("Error loading persisted address data: %s", err)
} }
if nameFound { overwritePersisted := func(fmt string, args ...interface{}) {
if checkPeerName == alloc.ourName { alloc.infof(fmt, args...)
if ringFound {
if len(alloc.seed) != 0 {
alloc.infof("Found persisted IPAM data, ignoring supplied IPAM seed")
}
alloc.space.UpdateRanges(alloc.ring.OwnedRanges())
}
if ownedFound {
for _, d := range alloc.owned {
for _, cidr := range d.Cidrs {
alloc.space.Claim(cidr.Addr)
}
}
}
return
}
alloc.infof("Deleting persisted data for peername %s", checkPeerName)
alloc.persistRing() alloc.persistRing()
alloc.persistOwned() alloc.persistOwned()
} }
if len(alloc.seed) != 0 { if !nameFound || !ringFound {
alloc.infof("Initialising with supplied IPAM seed") overwritePersisted("No valid persisted data")
alloc.createRing(alloc.seed) return false
} else if alloc.paxos.IsElector() {
alloc.infof("Initialising via deferred consensus")
} else {
alloc.infof("Initialising as observer - awaiting IPAM data from another peer")
} }
if checkPeerName != alloc.ourName {
overwritePersisted("Deleting persisted data for peername %s", checkPeerName)
return false
}
if persistedRing.Range() != alloc.universe.Range() {
overwritePersisted("Deleting persisted data for IPAM range %s; our range is %s", persistedRing.Range(), alloc.universe)
return false
}
alloc.ring.Restore(persistedRing)
alloc.space.UpdateRanges(alloc.ring.OwnedRanges())
if ownedFound {
alloc.owned = persistedOwned
for _, d := range alloc.owned {
for _, cidr := range d.Cidrs {
alloc.space.Claim(cidr.Addr)
}
}
}
return true
} }
func (alloc *Allocator) persistOwned() { func (alloc *Allocator) persistOwned() {
@@ -986,7 +1056,7 @@ func (alloc *Allocator) findOwner(addr address.Address) string {
} }
// For each ID in the 'owned' map, remove the entry if it isn't in the map // For each ID in the 'owned' map, remove the entry if it isn't in the map
func (alloc *Allocator) syncOwned(ids map[string]struct{}) { func (alloc *Allocator) pruneOwned(ids map[string]struct{}) {
changed := false changed := false
for ident, d := range alloc.owned { for ident, d := range alloc.owned {
if !d.IsContainer { if !d.IsContainer {
@@ -1014,6 +1084,9 @@ func (alloc *Allocator) fatalf(fmt string, args ...interface{}) {
func (alloc *Allocator) warnf(fmt string, args ...interface{}) { func (alloc *Allocator) warnf(fmt string, args ...interface{}) {
alloc.logf(common.Log.Warnf, fmt, args...) alloc.logf(common.Log.Warnf, fmt, args...)
} }
func (alloc *Allocator) errorf(fmt string, args ...interface{}) {
common.Log.Errorf("[allocator %s] "+fmt, append([]interface{}{alloc.ourName}, args...)...)
}
func (alloc *Allocator) infof(fmt string, args ...interface{}) { func (alloc *Allocator) infof(fmt string, args ...interface{}) {
alloc.logf(common.Log.Infof, fmt, args...) alloc.logf(common.Log.Infof, fmt, args...)
} }
+49 -25
View File
@@ -22,6 +22,14 @@ const (
func returnFalse() bool { return false } func returnFalse() bool { return false }
func (alloc *Allocator) SimplyAllocate(ident string, cidr address.CIDR) (address.Address, error) {
return alloc.Allocate(ident, cidr, true, returnFalse)
}
func (alloc *Allocator) SimplyClaim(ident string, cidr address.CIDR) error {
return alloc.Claim(ident, cidr, true, true, returnFalse)
}
func TestAllocFree(t *testing.T) { func TestAllocFree(t *testing.T) {
const ( const (
container1 = "abcdef" container1 = "abcdef"
@@ -41,11 +49,11 @@ func TestAllocFree(t *testing.T) {
cidr2, _ := address.ParseCIDR(subnet2) cidr2, _ := address.ParseCIDR(subnet2)
alloc.claimRingForTesting() alloc.claimRingForTesting()
addr1, err := alloc.Allocate(container1, cidr1, true, returnFalse) addr1, err := alloc.SimplyAllocate(container1, cidr1)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, testAddr1, addr1.String(), "address") require.Equal(t, testAddr1, addr1.String(), "address")
addr2, err := alloc.Allocate(container1, cidr2, true, returnFalse) addr2, err := alloc.SimplyAllocate(container1, cidr2)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, testAddr2, addr2.String(), "address") require.Equal(t, testAddr2, addr2.String(), "address")
@@ -54,28 +62,28 @@ func TestAllocFree(t *testing.T) {
require.Equal(t, []address.CIDR{address.MakeCIDR(cidr1, addr1), address.MakeCIDR(cidr2, addr2)}, addrs) require.Equal(t, []address.CIDR{address.MakeCIDR(cidr1, addr1), address.MakeCIDR(cidr2, addr2)}, addrs)
// Ask for another address for a different container and check it's different // Ask for another address for a different container and check it's different
addr1b, _ := alloc.Allocate(container2, cidr1, true, returnFalse) addr1b, _ := alloc.SimplyAllocate(container2, cidr1)
if addr1b.String() == testAddr1 { if addr1b.String() == testAddr1 {
t.Fatalf("Expected different address but got %s", addr1b.String()) t.Fatalf("Expected different address but got %s", addr1b.String())
} }
// Ask for the first container again and we should get the same addresses again // Ask for the first container again and we should get the same addresses again
addr1a, _ := alloc.Allocate(container1, cidr1, true, returnFalse) addr1a, _ := alloc.SimplyAllocate(container1, cidr1)
require.Equal(t, testAddr1, addr1a.String(), "address") require.Equal(t, testAddr1, addr1a.String(), "address")
addr2a, _ := alloc.Allocate(container1, cidr2, true, returnFalse) addr2a, _ := alloc.SimplyAllocate(container1, cidr2)
require.Equal(t, testAddr2, addr2a.String(), "address") require.Equal(t, testAddr2, addr2a.String(), "address")
// Now delete the first container, and we should get its addresses back // Now delete the first container, and we should get its addresses back
require.NoError(t, alloc.Delete(container1)) require.NoError(t, alloc.Delete(container1))
addr3, _ := alloc.Allocate(container3, cidr1, true, returnFalse) addr3, _ := alloc.SimplyAllocate(container3, cidr1)
require.Equal(t, testAddr1, addr3.String(), "address") require.Equal(t, testAddr1, addr3.String(), "address")
addr4, _ := alloc.Allocate(container3, cidr2, true, returnFalse) addr4, _ := alloc.SimplyAllocate(container3, cidr2)
require.Equal(t, testAddr2, addr4.String(), "address") require.Equal(t, testAddr2, addr4.String(), "address")
alloc.ContainerDied(container2) alloc.ContainerDied(container2)
// Resurrect // Resurrect
addr1c, err := alloc.Allocate(container2, cidr1, true, returnFalse) addr1c, err := alloc.SimplyAllocate(container2, cidr1)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, addr1b, addr1c, "address") require.Equal(t, addr1b, addr1c, "address")
@@ -170,31 +178,32 @@ func TestAllocatorClaim(t *testing.T) {
addr1, _ := address.ParseCIDR(testAddr1) addr1, _ := address.ParseCIDR(testAddr1)
// First claim should trigger "dunno, I'm going to wait" // First claim should trigger "dunno, I'm going to wait"
err := alloc.Claim(container3, addr1, true, true) err := alloc.SimplyClaim(container3, addr1)
require.NoError(t, err) require.NoError(t, err)
alloc.Prime() alloc.Prime()
// Do an allocate on the other peer, which we will try to claim later // Do an allocate on the other peer, which we will try to claim later
addrx, err := allocs[0].Allocate(container1, subnet, true, returnFalse) addrx, err := allocs[0].Allocate(container1, subnet, true, returnFalse)
router.Flush()
// Now try the claim again // Now try the claim again
err = alloc.Claim(container3, addr1, true, true) err = alloc.SimplyClaim(container3, addr1)
require.NoError(t, err) require.NoError(t, err)
// Check we get this address back if we try an allocate // Check we get this address back if we try an allocate
addr3, _ := alloc.Allocate(container3, subnet, true, returnFalse) addr3, _ := alloc.SimplyAllocate(container3, subnet)
require.Equal(t, testAddr1, address.MakeCIDR(subnet, addr3).String(), "address") require.Equal(t, testAddr1, address.MakeCIDR(subnet, addr3).String(), "address")
// one more claim should still work // one more claim should still work
err = alloc.Claim(container3, addr1, true, true) err = alloc.SimplyClaim(container3, addr1)
require.NoError(t, err) require.NoError(t, err)
// claim for a different container should fail // claim for a different container should fail
err = alloc.Claim(container1, addr1, true, true) err = alloc.SimplyClaim(container1, addr1)
require.Error(t, err) require.Error(t, err)
// claiming the address allocated on the other peer should fail // claiming the address allocated on the other peer should fail
err = alloc.Claim(container1, address.MakeCIDR(subnet, addrx), true, true) err = alloc.SimplyClaim(container1, address.MakeCIDR(subnet, addrx))
require.Error(t, err, "claiming address allocated on other peer should fail") require.Error(t, err, "claiming address allocated on other peer should fail")
// Check an address outside of our universe // Check an address outside of our universe
addr2, _ := address.ParseCIDR(testAddr2) addr2, _ := address.ParseCIDR(testAddr2)
err = alloc.Claim(container1, addr2, true, true) err = alloc.SimplyClaim(container1, addr2)
require.NoError(t, err) require.NoError(t, err)
} }
@@ -211,7 +220,7 @@ func (alloc *Allocator) pause() func() {
} }
func TestCancel(t *testing.T) { func TestCancel(t *testing.T) {
const cidr = "10.0.1.7/26" const cidr = "10.0.4.0/26"
router := gossip.NewTestRouter(0.0) router := gossip.NewTestRouter(0.0)
alloc1, subnet := makeAllocator("01:00:00:02:00:00", cidr, 2) alloc1, subnet := makeAllocator("01:00:00:02:00:00", cidr, 2)
@@ -272,7 +281,7 @@ func TestCancel(t *testing.T) {
func TestCancelOnDied(t *testing.T) { func TestCancelOnDied(t *testing.T) {
const ( const (
cidr = "10.0.1.7/26" cidr = "10.0.4.0/26"
container1 = "abcdef" container1 = "abcdef"
) )
@@ -314,18 +323,33 @@ func TestGossipShutdown(t *testing.T) {
defer alloc.Stop() defer alloc.Stop()
alloc.claimRingForTesting() alloc.claimRingForTesting()
alloc.Allocate(container1, subnet, true, returnFalse) alloc.SimplyAllocate(container1, subnet)
alloc.Shutdown() alloc.Shutdown()
_, err := alloc.Allocate(container2, subnet, true, returnFalse) // trying to allocate after shutdown should fail _, err := alloc.SimplyAllocate(container2, subnet) // trying to allocate after shutdown should fail
require.False(t, err == nil, "no address") require.False(t, err == nil, "no address")
CheckAllExpectedMessagesSent(alloc) CheckAllExpectedMessagesSent(alloc)
} }
func TestNoFrag(t *testing.T) {
const cidr = "10.0.4.0/22"
resultChan := make(chan int)
for i := 0; i < 100; i++ {
allocs, router, subnet := makeNetworkOfAllocators(3, cidr)
allocs[1].Allocate("foo", subnet, true, returnFalse)
allocs[2].Allocate("bar", subnet, true, returnFalse)
allocs[2].actionChan <- func() {
resultChan <- len(allocs[2].ring.Entries)
}
require.True(t, <-resultChan < 6, "excessive ring fragmentation")
stopNetworkOfAllocators(allocs, router)
}
}
func TestTransfer(t *testing.T) { func TestTransfer(t *testing.T) {
const cidr = "10.0.1.7/22" const cidr = "10.0.4.0/22"
allocs, router, subnet := makeNetworkOfAllocators(3, cidr) allocs, router, subnet := makeNetworkOfAllocators(3, cidr)
defer stopNetworkOfAllocators(allocs, router) defer stopNetworkOfAllocators(allocs, router)
alloc0 := allocs[0] alloc0 := allocs[0]
@@ -365,7 +389,7 @@ func TestTransfer(t *testing.T) {
} }
func TestFakeRouterSimple(t *testing.T) { func TestFakeRouterSimple(t *testing.T) {
const cidr = "10.0.1.7/22" const cidr = "10.0.4.0/22"
allocs, router, subnet := makeNetworkOfAllocators(2, cidr) allocs, router, subnet := makeNetworkOfAllocators(2, cidr)
defer stopNetworkOfAllocators(allocs, router) defer stopNetworkOfAllocators(allocs, router)
@@ -383,7 +407,7 @@ func TestAllocatorFuzz(t *testing.T) {
nodes = 5 nodes = 5
maxAddresses = 1000 maxAddresses = 1000
concurrency = 5 concurrency = 5
cidr = "10.0.1.7/22" cidr = "10.0.4.0/22"
) )
allocs, router, subnet := makeNetworkOfAllocators(nodes, cidr) allocs, router, subnet := makeNetworkOfAllocators(nodes, cidr)
defer stopNetworkOfAllocators(allocs, router) defer stopNetworkOfAllocators(allocs, router)
@@ -453,7 +477,7 @@ func TestAllocatorFuzz(t *testing.T) {
allocIndex := rand.Int31n(nodes) allocIndex := rand.Int31n(nodes)
alloc := allocs[allocIndex] alloc := allocs[allocIndex]
//common.Log.Infof("Allocate: asking allocator %d", allocIndex) //common.Log.Infof("Allocate: asking allocator %d", allocIndex)
addr, err := alloc.Allocate(name, subnet, true, returnFalse) addr, err := alloc.SimplyAllocate(name, subnet)
if err != nil { if err != nil {
panic(fmt.Sprintf("Could not allocate addr")) panic(fmt.Sprintf("Could not allocate addr"))
@@ -510,7 +534,7 @@ func TestAllocatorFuzz(t *testing.T) {
//common.Log.Infof("Asking for %s (%s) on allocator %d again", res.name, addr, res.alloc) //common.Log.Infof("Asking for %s (%s) on allocator %d again", res.name, addr, res.alloc)
newAddr, _ := alloc.Allocate(res.name, subnet, true, returnFalse) newAddr, _ := alloc.SimplyAllocate(res.name, subnet)
oldAddr, _ := address.ParseIP(addr) oldAddr, _ := address.ParseIP(addr)
if newAddr != oldAddr { if newAddr != oldAddr {
panic(fmt.Sprintf("Got different address for repeat request for %s: %s != %s", res.name, newAddr, oldAddr)) panic(fmt.Sprintf("Got different address for repeat request for %s: %s != %s", res.name, newAddr, oldAddr))
@@ -531,7 +555,7 @@ func TestAllocatorFuzz(t *testing.T) {
addressIndex := rand.Int31n(int32(subnet.Size())) addressIndex := rand.Int31n(int32(subnet.Size()))
alloc := allocs[allocIndex] alloc := allocs[allocIndex]
addr := address.Add(subnet.Addr, address.Offset(addressIndex)) addr := address.Add(subnet.Addr, address.Offset(addressIndex))
err := alloc.Claim(name, address.MakeCIDR(subnet, addr), true, true) err := alloc.SimplyClaim(name, address.MakeCIDR(subnet, addr))
if err == nil { if err == nil {
noteAllocation(allocIndex, name, addr) noteAllocation(allocIndex, name, addr)
} }
+12 -7
View File
@@ -15,6 +15,7 @@ type claim struct {
cidr address.CIDR cidr address.CIDR
isContainer bool isContainer bool
noErrorOnUnknown bool noErrorOnUnknown bool
hasBeenCancelled func() bool
} }
// Send an error (or nil for success) back to caller listening on resultChan // Send an error (or nil for success) back to caller listening on resultChan
@@ -33,6 +34,11 @@ func (c *claim) sendResult(result error) {
// Try returns true for success (or failure), false if we need to try again later // Try returns true for success (or failure), false if we need to try again later
func (c *claim) Try(alloc *Allocator) bool { func (c *claim) Try(alloc *Allocator) bool {
if c.hasBeenCancelled() {
c.Cancel()
return true
}
if !alloc.ring.Contains(c.cidr.Addr) { if !alloc.ring.Contains(c.cidr.Addr) {
// Address not within our universe; assume user knows what they are doing // Address not within our universe; assume user knows what they are doing
alloc.infof("Address %s claimed by %s - not in our range", c.cidr, c.ident) alloc.infof("Address %s claimed by %s - not in our range", c.cidr, c.ident)
@@ -51,11 +57,10 @@ func (c *claim) Try(alloc *Allocator) bool {
// success // success
case mesh.UnknownPeerName: case mesh.UnknownPeerName:
// If our ring doesn't know, it must be empty. // If our ring doesn't know, it must be empty.
alloc.infof("Claim %s for %s: is in the range %s, but the allocator is not initialized yet; will try later.",
c.cidr, c.ident, alloc.universe)
if c.noErrorOnUnknown { if c.noErrorOnUnknown {
alloc.infof("Claim %s for %s: address allocator still initializing; will try later.", c.cidr, c.ident)
c.sendResult(nil) c.sendResult(nil)
} else {
c.sendResult(fmt.Errorf("%s is in the range %s, but the allocator is not initialized yet", c.cidr, alloc.universe.AsCIDRString()))
} }
return false return false
default: default:
@@ -72,14 +77,14 @@ func (c *claim) Try(alloc *Allocator) bool {
return false return false
} }
if c.ident == "_" { // Special "I don't have a unique ID" identifier
c.ident = c.cidr.Addr.String()
}
// We are the owner, check we haven't given it to another container // We are the owner, check we haven't given it to another container
switch existingIdent := alloc.findOwner(c.cidr.Addr); existingIdent { switch existingIdent := alloc.findOwner(c.cidr.Addr); existingIdent {
case "": case "":
if err := alloc.space.Claim(c.cidr.Addr); err == nil { if err := alloc.space.Claim(c.cidr.Addr); err == nil {
alloc.debugln("Claimed", c.cidr, "for", c.ident) alloc.debugln("Claimed", c.cidr, "for", c.ident)
if c.ident == "_" { // Special "I don't have a unique ID" identifier
c.ident = c.cidr.String()
}
alloc.addOwned(c.ident, c.cidr, c.isContainer) alloc.addOwned(c.ident, c.cidr, c.isContainer)
c.sendResult(nil) c.sendResult(nil)
} else { } else {
@@ -89,7 +94,7 @@ func (c *claim) Try(alloc *Allocator) bool {
// same identifier is claiming same address; that's OK // same identifier is claiming same address; that's OK
alloc.debugln("Re-Claimed", c.cidr, "for", c.ident) alloc.debugln("Re-Claimed", c.cidr, "for", c.ident)
c.sendResult(nil) c.sendResult(nil)
case c.cidr.String(): case c.cidr.Addr.String():
// Address already allocated via "_" name // Address already allocated via "_" name
c.sendResult(fmt.Errorf("address %s already in use", c.cidr)) c.sendResult(fmt.Errorf("address %s already in use", c.cidr))
default: default:
+52 -29
View File
@@ -17,15 +17,17 @@ func badRequest(w http.ResponseWriter, err error) {
} }
func parseCIDR(w http.ResponseWriter, cidrStr string, net bool) (address.CIDR, bool) { func parseCIDR(w http.ResponseWriter, cidrStr string, net bool) (address.CIDR, bool) {
cidr, err := address.ParseCIDR(cidrStr) var cidr address.CIDR
var err error
if net {
cidr, err = ParseCIDRSubnet(cidrStr)
} else {
cidr, err = address.ParseCIDR(cidrStr)
}
if err != nil { if err != nil {
badRequest(w, err) badRequest(w, err)
return address.CIDR{}, false return address.CIDR{}, false
} }
if net && !cidr.IsSubnet() {
badRequest(w, fmt.Errorf("Invalid subnet %s - bits after network prefix are not all zero", cidrStr))
return address.CIDR{}, false
}
return cidr, true return cidr, true
} }
@@ -38,34 +40,54 @@ func writeAddresses(w http.ResponseWriter, cidrs []address.CIDR) {
} }
} }
func hasBeenCancelled(dockerCli *docker.Client, closedChan <-chan bool, ident string, checkAlive bool) func() bool {
return 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
}
}
}
func cancellationErr(w http.ResponseWriter, err error) bool {
if _, ok := err.(*errorCancelled); ok {
common.Log.Infoln("[allocator]:", err.Error())
fmt.Fprint(w, "cancelled")
return true
}
return false
}
func (alloc *Allocator) handleHTTPAllocate(dockerCli *docker.Client, w http.ResponseWriter, ident string, checkAlive bool, subnet address.CIDR) { 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, checkAlive, addr, err := alloc.Allocate(ident, subnet, checkAlive,
func() bool { hasBeenCancelled(dockerCli, w.(http.CloseNotifier).CloseNotify(), ident, checkAlive))
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 err != nil {
if _, ok := err.(*errorCancelled); ok { // cancellation is not really an error if !cancellationErr(w, err) {
common.Log.Infoln("[allocator]:", err.Error()) badRequest(w, err)
fmt.Fprint(w, "cancelled")
return
} }
badRequest(w, err)
return return
} }
fmt.Fprintf(w, "%s/%d", addr, subnet.PrefixLen) fmt.Fprintf(w, "%s/%d", addr, subnet.PrefixLen)
} }
func (alloc *Allocator) handleHTTPClaim(dockerCli *docker.Client, w http.ResponseWriter, ident string, cidr address.CIDR, checkAlive, noErrorOnUnknown bool) {
err := alloc.Claim(ident, cidr, checkAlive, noErrorOnUnknown,
hasBeenCancelled(dockerCli, w.(http.CloseNotifier).CloseNotify(), ident, checkAlive))
if err != nil {
if !cancellationErr(w, err) {
badRequest(w, fmt.Errorf("Unable to claim: %s", err))
}
return
}
w.WriteHeader(204)
}
// HandleHTTP wires up ipams HTTP endpoints to the provided mux. // HandleHTTP wires up ipams HTTP endpoints to the provided mux.
func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CIDR, dockerCli *docker.Client) { func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CIDR, tracker string, dockerCli *docker.Client) {
router.Methods("GET").Path("/ipinfo/defaultsubnet").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { router.Methods("GET").Path("/ipinfo/defaultsubnet").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s", defaultSubnet) fmt.Fprintf(w, "%s", defaultSubnet)
}) })
@@ -73,13 +95,10 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CID
router.Methods("PUT").Path("/ip/{id}/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { router.Methods("PUT").Path("/ip/{id}/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
if cidr, ok := parseCIDR(w, vars["ip"]+"/"+vars["prefixlen"], false); ok { if cidr, ok := parseCIDR(w, vars["ip"]+"/"+vars["prefixlen"], false); ok {
noErrorOnUnknown := r.FormValue("noErrorOnUnknown") == "true" ident := vars["id"]
checkAlive := r.FormValue("check-alive") == "true" checkAlive := r.FormValue("check-alive") == "true"
if err := alloc.Claim(vars["id"], cidr, checkAlive, noErrorOnUnknown); err != nil { noErrorOnUnknown := r.FormValue("noErrorOnUnknown") == "true"
badRequest(w, fmt.Errorf("Unable to claim: %s", err)) alloc.handleHTTPClaim(dockerCli, w, ident, cidr, checkAlive, noErrorOnUnknown)
return
}
w.WriteHeader(204)
} }
}) })
@@ -155,4 +174,8 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CID
transferred := alloc.AdminTakeoverRanges(ident) transferred := alloc.AdminTakeoverRanges(ident)
fmt.Fprintf(w, "%d IPs taken over from %s\n", transferred, ident) fmt.Fprintf(w, "%d IPs taken over from %s\n", transferred, ident)
}) })
router.Methods("GET").Path("/ipinfo/tracker").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, tracker)
})
} }
+1 -1
View File
@@ -43,7 +43,7 @@ func listenHTTP(alloc *Allocator, subnet address.CIDR) int {
router.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) { router.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, fmt.Sprintln(alloc)) io.WriteString(w, fmt.Sprintln(alloc))
}) })
alloc.HandleHTTP(router, subnet, nil) alloc.HandleHTTP(router, subnet, "", nil)
httpListener, err := net.Listen("tcp", ":0") httpListener, err := net.Listen("tcp", ":0")
if err != nil { if err != nil {
+2 -3
View File
@@ -14,8 +14,7 @@ type entry struct {
Token address.Address // The start of this range Token address.Address // The start of this range
Peer mesh.PeerName // Who owns this range Peer mesh.PeerName // Who owns this range
Version uint32 // Version of this range Version uint32 // Version of this range
Free address.Offset // Number of free IPs in this range Free address.Count // Number of free IPs in this range
// Note: Free should perhaps be an address.Count, but we can't change the wire protocol
} }
func (e *entry) Equal(e2 *entry) bool { func (e *entry) Equal(e2 *entry) bool {
@@ -23,7 +22,7 @@ func (e *entry) Equal(e2 *entry) bool {
e.Version == e2.Version e.Version == e2.Version
} }
func (e *entry) update(peername mesh.PeerName, free address.Offset) { func (e *entry) update(peername mesh.PeerName, free address.Count) {
e.Peer = peername e.Peer = peername
e.Version++ e.Version++
e.Free = free e.Free = free
+127 -73
View File
@@ -18,12 +18,15 @@ import (
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
) )
type OnUpdate func(prev, curr []address.Range, local bool)
// Ring represents the ring itself // Ring represents the ring itself
type Ring struct { type Ring struct {
Start, End address.Address // [min, max) tokens in this ring. Due to wrapping, min == max (effectively) 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 Peer mesh.PeerName // name of peer owning this ring instance
Entries entries // list of entries sorted by token Entries entries // list of entries sorted by token
Seeds []mesh.PeerName // peers with which the ring was seeded Seeds []mesh.PeerName // peers with which the ring was seeded
onUpdate OnUpdate
} }
func (r *Ring) assertInvariants() { func (r *Ring) assertInvariants() {
@@ -40,7 +43,6 @@ var (
ErrTokenOutOfRange = errors.New("Token is out of range") ErrTokenOutOfRange = errors.New("Token is out of range")
ErrDifferentSeeds = errors.New("Received ring was seeded differently from ours!") ErrDifferentSeeds = errors.New("Received ring was seeded differently from ours!")
ErrDifferentRange = errors.New("Received range differs from ours!") ErrDifferentRange = errors.New("Received range differs from ours!")
ErrNotFound = errors.New("No entries for peer found")
) )
func errInconsistentEntry(mine, theirs *entry) error { func errInconsistentEntry(mine, theirs *entry) error {
@@ -95,27 +97,45 @@ func (r *Ring) checkEntries(entries entries) error {
return nil return nil
} }
func (r *Ring) trackUpdates() func() {
return r.trackUpdatesOfPeer(r.Peer)
}
func (r *Ring) trackUpdatesOfPeer(peer mesh.PeerName) func() {
if r.onUpdate == nil {
return func() {}
}
ranges := r.OwnedRangesOfPeer(peer)
return func() { r.onUpdate(ranges, r.OwnedRangesOfPeer(peer), peer == r.Peer) }
}
// New creates an empty ring belonging to peer. // New creates an empty ring belonging to peer.
func New(start, end address.Address, peer mesh.PeerName) *Ring { func New(start, end address.Address, peer mesh.PeerName, f OnUpdate) *Ring {
common.Assert(start < end) common.Assert(start < end)
ring := &Ring{Start: start, End: end, Peer: peer, Entries: make([]*entry, 0)} ring := &Ring{Start: start, End: end, Peer: peer, Entries: make([]*entry, 0), onUpdate: f}
ring.updateExportedVariables() ring.updateExportedVariables()
return ring return ring
} }
func (r *Ring) Restore(other *Ring) {
onUpdate := r.onUpdate
*r = *other
r.onUpdate = onUpdate
}
func (r *Ring) Range() address.Range { func (r *Ring) Range() address.Range {
return address.Range{Start: r.Start, End: r.End} return address.Range{Start: r.Start, End: r.End}
} }
// Returns the distance between two tokens on this ring, dealing // Returns the distance between two tokens on this ring, dealing
// with ranges which cross the origin // with ranges which cross the origin
func (r *Ring) distance(start, end address.Address) address.Offset { func (r *Ring) distance(start, end address.Address) address.Count {
if end > start { if end > start {
return address.Offset(end - start) return address.Count(end - start)
} }
return address.Offset((r.End - start) + (end - r.Start)) return address.Count((r.End - start) + (end - r.Start))
} }
// GrantRangeToHost modifies the ring such that range [start, end) // GrantRangeToHost modifies the ring such that range [start, end)
@@ -127,6 +147,7 @@ func (r *Ring) GrantRangeToHost(start, end address.Address, peer mesh.PeerName)
//fmt.Printf("%s GrantRangeToHost [%v,%v) -> %s\n", r.Peer, start, end, peer) //fmt.Printf("%s GrantRangeToHost [%v,%v) -> %s\n", r.Peer, start, end, peer)
r.assertInvariants() r.assertInvariants()
defer r.trackUpdates()()
defer r.assertInvariants() defer r.assertInvariants()
defer r.updateExportedVariables() defer r.updateExportedVariables()
@@ -190,42 +211,70 @@ func (r *Ring) GrantRangeToHost(start, end address.Address, peer mesh.PeerName)
r.Entries.insert(entry{Token: end, Peer: r.Peer, Free: endFree}) r.Entries.insert(entry{Token: end, Peer: r.Peer, Free: endFree})
} }
// Merge the given ring into this ring and return any new ranges added // Merge the given ring into this ring and indicate whether this ring
func (r *Ring) Merge(gossip Ring) error { // got updated as a result.
func (r *Ring) Merge(gossip Ring) (bool, error) {
r.assertInvariants() r.assertInvariants()
defer r.trackUpdates()()
defer r.updateExportedVariables() defer r.updateExportedVariables()
// Don't panic when checking the gossiped in ring. // Don't panic when checking the gossiped in ring.
// In this case just return any error found. // In this case just return any error found.
if err := gossip.checkInvariants(); err != nil { if err := gossip.checkInvariants(); err != nil {
return err return false, err
} }
if len(gossip.Seeds) > 0 && len(r.Seeds) > 0 { if len(gossip.Seeds) > 0 && len(r.Seeds) > 0 {
if len(gossip.Seeds) != len(r.Seeds) { if len(gossip.Seeds) != len(r.Seeds) {
return ErrDifferentSeeds return false, ErrDifferentSeeds
} }
for i, seed := range gossip.Seeds { for i, seed := range gossip.Seeds {
if seed != r.Seeds[i] { if seed != r.Seeds[i] {
return ErrDifferentSeeds return false, ErrDifferentSeeds
} }
} }
} }
if r.Start != gossip.Start || r.End != gossip.End { if r.Start != gossip.Start || r.End != gossip.End {
return ErrDifferentRange return false, ErrDifferentRange
} }
// Now merge their ring with yours, in a temporary ring. result, updated, err := r.Entries.merge(gossip.Entries, r.Peer)
var result entries
addToResult := func(e entry) { result = append(result, &e) }
if err != nil {
return false, err
}
if err := r.checkEntries(result); err != nil {
return false, fmt.Errorf("Merge of incoming data causes: %s", err)
}
if len(r.Seeds) == 0 {
r.Seeds = gossip.Seeds
}
r.Entries = result
return updated, nil
}
// Merge other entries into ours, and complain when that stomps on
// entries belonging to ourPeer. Returns the merged entries and an
// indication whether the merge resulted in any changes, i.e. the
// result differs from the original.
func (es entries) merge(other entries, ourPeer mesh.PeerName) (result entries, updated bool, err error) {
var mine, theirs *entry var mine, theirs *entry
var previousOwner *mesh.PeerName var previousOwner *mesh.PeerName
// i is index into r.Entries; j is index into gossip.Entries addToResult := func(e entry) { result = append(result, &e) }
addTheirs := func(e entry) {
addToResult(e)
updated = true
previousOwner = nil
}
// i is index into es; j is index into other
var i, j int var i, j int
for i < len(r.Entries) && j < len(gossip.Entries) { for i < len(es) && j < len(other) {
mine, theirs = r.Entries[i], gossip.Entries[j] mine, theirs = es[i], other[j]
switch { switch {
case mine.Token < theirs.Token: case mine.Token < theirs.Token:
addToResult(*mine) addToResult(*mine)
@@ -233,59 +282,52 @@ func (r *Ring) Merge(gossip Ring) error {
i++ i++
case mine.Token > theirs.Token: case mine.Token > theirs.Token:
// insert, checking that a range owned by us hasn't been split // insert, checking that a range owned by us hasn't been split
if previousOwner != nil && *previousOwner == r.Peer && theirs.Peer != r.Peer { if previousOwner != nil && *previousOwner == ourPeer && theirs.Peer != ourPeer {
return errEntryInMyRange(theirs) err = errEntryInMyRange(theirs)
return
} }
addToResult(*theirs) addTheirs(*theirs)
previousOwner = nil
j++ j++
case mine.Token == theirs.Token: case mine.Token == theirs.Token:
// merge // merge
switch { switch {
case mine.Version >= theirs.Version: case mine.Version >= theirs.Version:
if mine.Version == theirs.Version && !mine.Equal(theirs) { if mine.Version == theirs.Version && !mine.Equal(theirs) {
return errInconsistentEntry(mine, theirs) err = errInconsistentEntry(mine, theirs)
return
} }
addToResult(*mine) addToResult(*mine)
previousOwner = &mine.Peer previousOwner = &mine.Peer
case mine.Version < theirs.Version: case mine.Version < theirs.Version:
if mine.Peer == r.Peer { // We shouldn't receive updates to our own tokens if mine.Peer == ourPeer { // We shouldn't receive updates to our own tokens
return errNewerVersion(mine, theirs) err = errNewerVersion(mine, theirs)
return
} }
addToResult(*theirs) addTheirs(*theirs)
previousOwner = nil
} }
i++ i++
j++ j++
} }
} }
// At this point, either i is at the end of r or j is at the end // At this point, either i is at the end of es or j is at the end
// of gossip, so copy over the remaining entries. // of other, so copy over the remaining entries.
for ; i < len(r.Entries); i++ { for ; i < len(es); i++ {
mine = r.Entries[i] mine = es[i]
addToResult(*mine) addToResult(*mine)
} }
for ; j < len(gossip.Entries); j++ { for ; j < len(other); j++ {
theirs = gossip.Entries[j] theirs = other[j]
if previousOwner != nil && *previousOwner == r.Peer && theirs.Peer != r.Peer { if previousOwner != nil && *previousOwner == ourPeer && theirs.Peer != ourPeer {
return errEntryInMyRange(theirs) err = errEntryInMyRange(theirs)
return
} }
addToResult(*theirs) addTheirs(*theirs)
previousOwner = nil
} }
if err := r.checkEntries(result); err != nil { return
return fmt.Errorf("Merge of incoming data causes: %s", err)
}
if len(r.Seeds) == 0 {
r.Seeds = gossip.Seeds
}
r.Entries = result
return nil
} }
// Empty returns true if the ring has no entries // Empty returns true if the ring has no entries
@@ -318,10 +360,14 @@ func (r *Ring) splitRangesOverZero(ranges []address.Range) []address.Range {
// ranges are owned by this peer. Will split ranges which // ranges are owned by this peer. Will split ranges which
// span 0 in the ring. // span 0 in the ring.
func (r *Ring) OwnedRanges() (result []address.Range) { func (r *Ring) OwnedRanges() (result []address.Range) {
return r.OwnedRangesOfPeer(r.Peer)
}
func (r *Ring) OwnedRangesOfPeer(peer mesh.PeerName) (result []address.Range) {
r.assertInvariants() r.assertInvariants()
for i, entry := range r.Entries { for i, entry := range r.Entries {
if entry.Peer == r.Peer { if entry.Peer == peer {
nextEntry := r.Entries.entry(i + 1) nextEntry := r.Entries.entry(i + 1)
result = append(result, address.Range{Start: entry.Token, End: nextEntry.Token}) result = append(result, address.Range{Start: entry.Token, End: nextEntry.Token})
} }
@@ -350,34 +396,38 @@ func (r *Ring) AllRangeInfo() (result []RangeInfo) {
} }
// ClaimForPeers claims the entire ring for the array of peers passed // ClaimForPeers claims the entire ring for the array of peers passed
// in. Only works for empty rings. // in. Only works for empty rings. Each claimed range is CIDR-aligned.
func (r *Ring) ClaimForPeers(peers []mesh.PeerName) { func (r *Ring) ClaimForPeers(peers []mesh.PeerName) {
common.Assert(r.Empty()) common.Assert(r.Empty())
defer r.trackUpdates()()
defer r.assertInvariants() defer r.assertInvariants()
defer r.updateExportedVariables() defer r.updateExportedVariables()
defer func() {
e := r.Entries[len(r.Entries)-1]
common.Assert(address.Add(e.Token, address.Offset(e.Free)) == r.End)
}()
totalSize := r.distance(r.Start, r.End) r.subdivide(r.Start, r.End, peers)
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
}
}
r.Entries.insert(entry{Token: pos, Peer: peer, Free: share})
pos += address.Address(share)
}
common.Assert(pos == r.End)
r.Seeds = peers r.Seeds = peers
} }
// subdivide subdivides the [from,to) CIDR for the given peers into
// CIDR-aligned subranges.
func (r *Ring) subdivide(from, to address.Address, peers []mesh.PeerName) {
share := address.Length(to, from)
if share == 0 {
return
}
if share == 1 || len(peers) == 1 {
r.Entries.insert(entry{Token: from, Peer: peers[0], Free: share})
return
}
mid := address.Add(from, address.Offset(share/2))
r.subdivide(from, mid, peers[:len(peers)/2])
r.subdivide(address.Add(mid, address.Offset(share%2)), to, peers[len(peers)/2:])
}
func (r *Ring) FprintWithNicknames(w io.Writer, m map[mesh.PeerName]string) { func (r *Ring) FprintWithNicknames(w io.Writer, m map[mesh.PeerName]string) {
for _, entry := range r.Entries { for _, entry := range r.Entries {
nickname, found := m[entry.Peer] nickname, found := m[entry.Peer]
@@ -399,8 +449,8 @@ func (r *Ring) String() string {
// ReportFree is used by the allocator to tell the ring how many free // ReportFree is used by the allocator to tell the ring how many free
// ips are in a given range, so that ChoosePeersToAskForSpace can make // ips are in a given range, so that ChoosePeersToAskForSpace can make
// more intelligent decisions. // more intelligent decisions. Returns true if any changes made.
func (r *Ring) ReportFree(freespace map[address.Address]address.Count) { func (r *Ring) ReportFree(freespace map[address.Address]address.Count) (updated bool) {
r.assertInvariants() r.assertInvariants()
defer r.assertInvariants() defer r.assertInvariants()
defer r.updateExportedVariables() defer r.updateExportedVariables()
@@ -432,13 +482,15 @@ func (r *Ring) ReportFree(freespace map[address.Address]address.Count) {
maxSize := r.distance(entry.Token, next.Token) maxSize := r.distance(entry.Token, next.Token)
common.Assert(free <= address.Count(maxSize)) common.Assert(free <= address.Count(maxSize))
if address.Count(entries[i].Free) == free { if entries[i].Free == free {
return continue
} }
entries[i].Free = address.Offset(free) entries[i].Free = free
entries[i].Version++ entries[i].Version++
updated = true
} }
return
} }
type weightedPeer struct { type weightedPeer struct {
@@ -455,7 +507,7 @@ 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 // ChoosePeersToAskForSpace returns all peers we can ask for space in
// the range [start, end), in weighted-random order. Assumes start<end. // the range [start, end), in weighted-random order. Assumes start<end.
func (r *Ring) ChoosePeersToAskForSpace(start, end address.Address) []mesh.PeerName { func (r *Ring) ChoosePeersToAskForSpace(start, end address.Address) []mesh.PeerName {
totalSpacePerPeer := make(map[mesh.PeerName]address.Offset) totalSpacePerPeer := make(map[mesh.PeerName]address.Count)
// iterate through tokens // iterate through tokens
for i, entry := range r.Entries { for i, entry := range r.Entries {
@@ -507,6 +559,8 @@ func (r *Ring) PickPeerForTransfer(isValid func(mesh.PeerName) bool) mesh.PeerNa
// and return ranges indicating the new space we picked up // and return ranges indicating the new space we picked up
func (r *Ring) Transfer(from, to mesh.PeerName) []address.Range { func (r *Ring) Transfer(from, to mesh.PeerName) []address.Range {
r.assertInvariants() r.assertInvariants()
defer r.trackUpdates()()
defer r.trackUpdatesOfPeer(from)()
defer r.assertInvariants() defer r.assertInvariants()
defer r.updateExportedVariables() defer r.updateExportedVariables()
+122 -105
View File
@@ -19,7 +19,8 @@ var (
peer2name, _ = mesh.PeerNameFromString("02:00:00:00:02:00") peer2name, _ = mesh.PeerNameFromString("02:00:00:00:02:00")
peer3name, _ = mesh.PeerNameFromString("03: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") start, end = ParseIP("10.0.0.0"), ParseIP("10.0.1.0")
dot8 = ParseIP("10.0.0.8")
dot10, dot245 = ParseIP("10.0.0.10"), ParseIP("10.0.0.245") dot10, dot245 = ParseIP("10.0.0.10"), ParseIP("10.0.0.245")
dot250 = ParseIP("10.0.0.250") dot250 = ParseIP("10.0.0.250")
middle = ParseIP("10.0.0.128") middle = ParseIP("10.0.0.128")
@@ -30,8 +31,17 @@ func ParseIP(s string) address.Address {
return addr return addr
} }
func merge(r1, r2 *Ring) error {
_, err := r1.Merge(*r2)
return err
}
func NewRing(start, end address.Address, peer mesh.PeerName) *Ring {
return New(start, end, peer, nil)
}
func TestInvariants(t *testing.T) { func TestInvariants(t *testing.T) {
ring := New(start, end, peer1name) ring := NewRing(start, end, peer1name)
// Check ring is sorted // Check ring is sorted
ring.Entries = []*entry{{Token: dot245, Peer: peer1name}, {Token: dot10, Peer: peer2name}} ring.Entries = []*entry{{Token: dot245, Peer: peer1name}, {Token: dot10, Peer: peer2name}}
@@ -42,7 +52,7 @@ func TestInvariants(t *testing.T) {
require.True(t, ring.checkInvariants() == ErrTokenRepeated, "Expected error") require.True(t, ring.checkInvariants() == ErrTokenRepeated, "Expected error")
// Check tokens are in bounds // Check tokens are in bounds
ring = New(dot10, dot245, peer1name) ring = NewRing(dot10, dot245, peer1name)
ring.Entries = []*entry{{Token: start, Peer: peer1name}} ring.Entries = []*entry{{Token: start, Peer: peer1name}}
require.True(t, ring.checkInvariants() == ErrTokenOutOfRange, "Expected error") require.True(t, ring.checkInvariants() == ErrTokenOutOfRange, "Expected error")
@@ -51,7 +61,7 @@ func TestInvariants(t *testing.T) {
} }
func TestInsert(t *testing.T) { func TestInsert(t *testing.T) {
ring := New(start, end, peer1name) ring := NewRing(start, end, peer1name)
ring.Entries = []*entry{{Token: start, Peer: peer1name, Free: 255}} ring.Entries = []*entry{{Token: start, Peer: peer1name, Free: 255}}
require.Panics(t, func() { require.Panics(t, func() {
@@ -76,7 +86,7 @@ func TestInsert(t *testing.T) {
} }
func TestBetween(t *testing.T) { func TestBetween(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 255}} ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 255}}
// First off, in a ring where everything is owned by the peer // First off, in a ring where everything is owned by the peer
@@ -118,29 +128,29 @@ func TestBetween(t *testing.T) {
} }
func TestGrantSimple(t *testing.T) { func TestGrantSimple(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring2 := New(start, end, peer2name) ring2 := NewRing(start, end, peer2name)
// Claim everything for peer1 // Claim everything for peer1
ring1.ClaimItAll() ring1.ClaimItAll()
require.Equal(t, entries{{Token: start, Peer: peer1name, Free: 255}}, ring1.Entries) require.Equal(t, entries{{Token: start, Peer: peer1name, Free: 256}}, ring1.Entries)
// Now grant everything to peer2 // Now grant everything to peer2
ring1.GrantRangeToHost(start, end, peer2name) ring1.GrantRangeToHost(start, end, peer2name)
ring2.Entries = []*entry{{Token: start, Peer: peer2name, Free: 255, Version: 1}} ring2.Entries = []*entry{{Token: start, Peer: peer2name, Free: 256, Version: 1}}
require.Equal(t, ring2.Entries, ring1.Entries) require.Equal(t, ring2.Entries, ring1.Entries)
// Now spint back to peer 1 // Now spint back to peer 1
ring2.GrantRangeToHost(dot10, end, peer1name) ring2.GrantRangeToHost(dot10, end, peer1name)
ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 10, Version: 2}, ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 10, Version: 2},
{Token: dot10, Peer: peer1name, Free: 245}} {Token: dot10, Peer: peer1name, Free: 246}}
require.Equal(t, ring2.Entries, ring1.Entries) require.Equal(t, ring2.Entries, ring1.Entries)
// And spint back to peer 2 again // And spint back to peer 2 again
ring1.GrantRangeToHost(dot245, end, peer2name) ring1.GrantRangeToHost(dot245, end, peer2name)
require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 10, Version: 2}, require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 10, Version: 2},
{Token: dot10, Peer: peer1name, Free: 235, Version: 1}, {Token: dot10, Peer: peer1name, Free: 235, Version: 1},
{Token: dot245, Peer: peer2name, Free: 10}}, ring1.Entries) {Token: dot245, Peer: peer2name, Free: 11}}, ring1.Entries)
// Grant range spanning a live token // Grant range spanning a live token
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 10, Version: 2}, ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 10, Version: 2},
@@ -153,19 +163,19 @@ func TestGrantSimple(t *testing.T) {
} }
func TestGrantSplit(t *testing.T) { func TestGrantSplit(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring2 := New(start, end, peer2name) ring2 := NewRing(start, end, peer2name)
// Claim everything for peer1 // Claim everything for peer1
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 255}} ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 256}}
ring2.Merge(*ring1) merge(ring2, ring1)
require.Equal(t, ring2.Entries, ring1.Entries) require.Equal(t, ring2.Entries, ring1.Entries)
// Now grant a split range to peer2 // Now grant a split range to peer2
ring1.GrantRangeToHost(dot10, dot245, peer2name) ring1.GrantRangeToHost(dot10, dot245, peer2name)
require.Equal(t, entries{{Token: start, Peer: peer1name, Free: 10, Version: 1}, require.Equal(t, entries{{Token: start, Peer: peer1name, Free: 10, Version: 1},
{Token: dot10, Peer: peer2name, Free: 235}, {Token: dot10, Peer: peer2name, Free: 235},
{Token: dot245, Peer: peer1name, Free: 10}}, ring1.Entries) {Token: dot245, Peer: peer1name, Free: 11}}, ring1.Entries)
ring1.assertInvariants() ring1.assertInvariants()
@@ -176,22 +186,22 @@ func TestGrantSplit(t *testing.T) {
require.Equal(t, entries{{Token: start, Peer: peer1name, Free: 10, Version: 2}, require.Equal(t, entries{{Token: start, Peer: peer1name, Free: 10, Version: 2},
{Token: dot10, Peer: peer2name, Free: 118, Version: 1}, {Token: dot10, Peer: peer2name, Free: 118, Version: 1},
{Token: middle, Peer: peer2name, Free: 117, Version: 1}, {Token: middle, Peer: peer2name, Free: 117, Version: 1},
{Token: dot245, Peer: peer1name, Free: 10, Version: 0}}, ring1.Entries) {Token: dot245, Peer: peer1name, Free: 11, Version: 0}}, ring1.Entries)
ring1.assertInvariants() ring1.assertInvariants()
} }
func TestMergeSimple(t *testing.T) { func TestMergeSimple(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring2 := New(start, end, peer2name) ring2 := NewRing(start, end, peer2name)
// Claim everything for peer1 // Claim everything for peer1
ring1.ClaimItAll() ring1.ClaimItAll()
ring1.GrantRangeToHost(middle, end, peer2name) ring1.GrantRangeToHost(middle, end, peer2name)
require.NoError(t, ring2.Merge(*ring1)) require.NoError(t, merge(ring2, ring1))
require.Equal(t, entries{{Token: start, Peer: peer1name, Free: 128, Version: 1}, require.Equal(t, entries{{Token: start, Peer: peer1name, Free: 128, Version: 1},
{Token: middle, Peer: peer2name, Free: 127}}, ring1.Entries) {Token: middle, Peer: peer2name, Free: 128}}, ring1.Entries)
require.Equal(t, ring2.Entries, ring1.Entries) require.Equal(t, ring2.Entries, ring1.Entries)
@@ -200,48 +210,48 @@ func TestMergeSimple(t *testing.T) {
ring1.GrantRangeToHost(start, middle, peer2name) ring1.GrantRangeToHost(start, middle, peer2name)
ring2.GrantRangeToHost(middle, end, peer1name) ring2.GrantRangeToHost(middle, end, peer1name)
require.NoError(t, ring2.Merge(*ring1)) require.NoError(t, merge(ring2, ring1))
require.NoError(t, ring1.Merge(*ring2)) require.NoError(t, merge(ring1, ring2))
require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 128, Version: 2}, require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 128, Version: 2},
{Token: middle, Peer: peer1name, Free: 127, Version: 1}}, ring1.Entries) {Token: middle, Peer: peer1name, Free: 128, Version: 1}}, ring1.Entries)
require.Equal(t, ring2.Entries, ring1.Entries) require.Equal(t, ring2.Entries, ring1.Entries)
} }
func TestMergeErrors(t *testing.T) { func TestMergeErrors(t *testing.T) {
// Cannot Merge in an invalid ring // Cannot Merge in an invalid ring
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring2 := New(start, end, peer2name) ring2 := NewRing(start, end, peer2name)
ring2.Entries = []*entry{{Token: middle, Peer: peer2name}, {Token: start, Peer: peer2name}} ring2.Entries = []*entry{{Token: middle, Peer: peer2name}, {Token: start, Peer: peer2name}}
require.True(t, ring1.Merge(*ring2) == ErrNotSorted, "Expected ErrNotSorted") require.True(t, merge(ring1, ring2) == ErrNotSorted, "Expected ErrNotSorted")
// Should Merge two rings for different ranges // Should Merge two rings for different ranges
ring2 = New(start, middle, peer2name) ring2 = NewRing(start, middle, peer2name)
ring2.Entries = []*entry{} ring2.Entries = []*entry{}
require.True(t, ring1.Merge(*ring2) == ErrDifferentRange, "Expected ErrDifferentRange") require.True(t, merge(ring1, ring2) == ErrDifferentRange, "Expected ErrDifferentRange")
// Cannot Merge newer version of entry I own // Cannot Merge newer version of entry I own
ring2 = New(start, end, peer2name) ring2 = NewRing(start, end, peer2name)
ring1.Entries = []*entry{{Token: start, Peer: peer1name}} ring1.Entries = []*entry{{Token: start, Peer: peer1name}}
ring2.Entries = []*entry{{Token: start, Peer: peer1name, Version: 1}} ring2.Entries = []*entry{{Token: start, Peer: peer1name, Version: 1}}
fmt.Println(ring1.Merge(*ring2)) fmt.Println(merge(ring1, ring2))
require.Error(t, ring1.Merge(*ring2), "Expected error") require.Error(t, merge(ring1, ring2), "Expected error")
// Cannot Merge two entries with same version but different hosts // Cannot Merge two entries with same version but different hosts
ring1.Entries = []*entry{{Token: start, Peer: peer1name}} ring1.Entries = []*entry{{Token: start, Peer: peer1name}}
ring2.Entries = []*entry{{Token: start, Peer: peer2name}} ring2.Entries = []*entry{{Token: start, Peer: peer2name}}
require.Error(t, ring1.Merge(*ring2), "Expected error") require.Error(t, merge(ring1, ring2), "Expected error")
// Cannot Merge an entry into a range I own // Cannot Merge an entry into a range I own
ring1.Entries = []*entry{{Token: start, Peer: peer1name}} ring1.Entries = []*entry{{Token: start, Peer: peer1name}}
ring2.Entries = []*entry{{Token: middle, Peer: peer2name}} ring2.Entries = []*entry{{Token: middle, Peer: peer2name}}
require.Error(t, ring1.Merge(*ring2), "Expected error") require.Error(t, merge(ring1, ring2), "Expected error")
} }
func TestMergeMore(t *testing.T) { func TestMergeMore(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring2 := New(start, end, peer2name) ring2 := NewRing(start, end, peer2name)
assertRing := func(ring *Ring, entries entries) { assertRing := func(ring *Ring, entries entries) {
require.Equal(t, entries, ring.Entries) require.Equal(t, entries, ring.Entries)
@@ -252,74 +262,74 @@ func TestMergeMore(t *testing.T) {
// Claim everything for peer1 // Claim everything for peer1
ring1.ClaimItAll() ring1.ClaimItAll()
assertRing(ring1, []*entry{{Token: start, Peer: peer1name, Free: 255}}) assertRing(ring1, []*entry{{Token: start, Peer: peer1name, Free: 256}})
assertRing(ring2, []*entry{}) assertRing(ring2, []*entry{})
// Check the Merge sends it to the other ring // Check the Merge sends it to the other ring
require.NoError(t, ring2.Merge(*ring1)) require.NoError(t, merge(ring2, ring1))
assertRing(ring1, []*entry{{Token: start, Peer: peer1name, Free: 255}}) assertRing(ring1, []*entry{{Token: start, Peer: peer1name, Free: 256}})
assertRing(ring2, []*entry{{Token: start, Peer: peer1name, Free: 255}}) assertRing(ring2, []*entry{{Token: start, Peer: peer1name, Free: 256}})
// Give everything to peer2 // Give everything to peer2
ring1.GrantRangeToHost(start, end, peer2name) ring1.GrantRangeToHost(start, end, peer2name)
assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 255, Version: 1}}) assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 256, Version: 1}})
assertRing(ring2, []*entry{{Token: start, Peer: peer1name, Free: 255}}) assertRing(ring2, []*entry{{Token: start, Peer: peer1name, Free: 256}})
require.NoError(t, ring2.Merge(*ring1)) require.NoError(t, merge(ring2, ring1))
assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 255, Version: 1}}) assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 256, Version: 1}})
assertRing(ring2, []*entry{{Token: start, Peer: peer2name, Free: 255, Version: 1}}) assertRing(ring2, []*entry{{Token: start, Peer: peer2name, Free: 256, Version: 1}})
// And carve off some space // And carve off some space
ring2.GrantRangeToHost(middle, end, peer1name) ring2.GrantRangeToHost(middle, end, peer1name)
assertRing(ring2, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2}, assertRing(ring2, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2},
{Token: middle, Peer: peer1name, Free: 127}}) {Token: middle, Peer: peer1name, Free: 128}})
assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 255, Version: 1}}) assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 256, Version: 1}})
// And Merge back // And Merge back
require.NoError(t, ring1.Merge(*ring2)) require.NoError(t, merge(ring1, ring2))
assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2}, assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2},
{Token: middle, Peer: peer1name, Free: 127}}) {Token: middle, Peer: peer1name, Free: 128}})
assertRing(ring2, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2}, assertRing(ring2, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2},
{Token: middle, Peer: peer1name, Free: 127}}) {Token: middle, Peer: peer1name, Free: 128}})
// This should be a no-op // This should be a no-op
require.NoError(t, ring2.Merge(*ring1)) require.NoError(t, merge(ring2, ring1))
assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2}, assertRing(ring1, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2},
{Token: middle, Peer: peer1name, Free: 127}}) {Token: middle, Peer: peer1name, Free: 128}})
assertRing(ring2, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2}, assertRing(ring2, []*entry{{Token: start, Peer: peer2name, Free: 128, Version: 2},
{Token: middle, Peer: peer1name, Free: 127}}) {Token: middle, Peer: peer1name, Free: 128}})
} }
func TestMergeSplit(t *testing.T) { func TestMergeSplit(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring2 := New(start, end, peer2name) ring2 := NewRing(start, end, peer2name)
// Claim everything for peer2 // Claim everything for peer2
ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 255}} ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 256}}
require.NoError(t, ring2.Merge(*ring1)) require.NoError(t, merge(ring2, ring1))
require.Equal(t, ring2.Entries, ring1.Entries) require.Equal(t, ring2.Entries, ring1.Entries)
// Now grant a split range to peer1 // Now grant a split range to peer1
ring2.GrantRangeToHost(dot10, dot245, peer1name) ring2.GrantRangeToHost(dot10, dot245, peer1name)
require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 10, Version: 1}, require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 10, Version: 1},
{Token: dot10, Peer: peer1name, Free: 235}, {Token: dot10, Peer: peer1name, Free: 235},
{Token: dot245, Peer: peer2name, Free: 10}}, ring2.Entries) {Token: dot245, Peer: peer2name, Free: 11}}, ring2.Entries)
require.NoError(t, ring1.Merge(*ring2)) require.NoError(t, merge(ring1, ring2))
require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 10, Version: 1}, require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 10, Version: 1},
{Token: dot10, Peer: peer1name, Free: 235}, {Token: dot10, Peer: peer1name, Free: 235},
{Token: dot245, Peer: peer2name, Free: 10}}, ring1.Entries) {Token: dot245, Peer: peer2name, Free: 11}}, ring1.Entries)
require.Equal(t, ring2.Entries, ring1.Entries) require.Equal(t, ring2.Entries, ring1.Entries)
} }
func TestMergeSplit2(t *testing.T) { func TestMergeSplit2(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring2 := New(start, end, peer2name) ring2 := NewRing(start, end, peer2name)
// Claim everything for peer2 // Claim everything for peer2
ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 250}, {Token: dot250, Peer: peer2name, Free: 5}} ring1.Entries = []*entry{{Token: start, Peer: peer2name, Free: 250}, {Token: dot250, Peer: peer2name, Free: 5}}
require.NoError(t, ring2.Merge(*ring1)) require.NoError(t, merge(ring2, ring1))
require.Equal(t, ring2.Entries, ring1.Entries) require.Equal(t, ring2.Entries, ring1.Entries)
// Now grant a split range to peer1 // Now grant a split range to peer1
@@ -328,7 +338,7 @@ func TestMergeSplit2(t *testing.T) {
{Token: dot10, Peer: peer1name, Free: 235}, {Token: dot10, Peer: peer1name, Free: 235},
{Token: dot245, Peer: peer2name, Free: 5}, {Token: dot250, Peer: peer2name, Free: 5}}, ring2.Entries) {Token: dot245, Peer: peer2name, Free: 5}, {Token: dot250, Peer: peer2name, Free: 5}}, ring2.Entries)
require.NoError(t, ring1.Merge(*ring2)) require.NoError(t, merge(ring1, ring2))
require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 10, Version: 1}, require.Equal(t, entries{{Token: start, Peer: peer2name, Free: 10, Version: 1},
{Token: dot10, Peer: peer1name, Free: 235}, {Token: dot10, Peer: peer1name, Free: 235},
{Token: dot245, Peer: peer2name, Free: 5}, {Token: dot250, Peer: peer2name, Free: 5}}, ring1.Entries) {Token: dot245, Peer: peer2name, Free: 5}, {Token: dot250, Peer: peer2name, Free: 5}}, ring1.Entries)
@@ -338,8 +348,8 @@ func TestMergeSplit2(t *testing.T) {
// A simple test, very similar to above, but using the marshalling to byte[]s // A simple test, very similar to above, but using the marshalling to byte[]s
func TestGossip(t *testing.T) { func TestGossip(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring2 := New(start, end, peer2name) ring2 := NewRing(start, end, peer2name)
assertRing := func(ring *Ring, entries entries) { assertRing := func(ring *Ring, entries entries) {
require.Equal(t, entries, ring.Entries) require.Equal(t, entries, ring.Entries)
@@ -350,13 +360,13 @@ func TestGossip(t *testing.T) {
// Claim everything for peer1 // Claim everything for peer1
ring1.ClaimItAll() ring1.ClaimItAll()
assertRing(ring1, []*entry{{Token: start, Peer: peer1name, Free: 255}}) assertRing(ring1, []*entry{{Token: start, Peer: peer1name, Free: 256}})
assertRing(ring2, []*entry{}) assertRing(ring2, []*entry{})
// Check the Merge sends it to the other ring // Check the Merge sends it to the other ring
require.NoError(t, ring2.Merge(*ring1)) require.NoError(t, merge(ring2, ring1))
assertRing(ring1, []*entry{{Token: start, Peer: peer1name, Free: 255}}) assertRing(ring1, []*entry{{Token: start, Peer: peer1name, Free: 256}})
assertRing(ring2, []*entry{{Token: start, Peer: peer1name, Free: 255}}) assertRing(ring2, []*entry{{Token: start, Peer: peer1name, Free: 256}})
} }
func assertPeersWithSpace(t *testing.T, ring *Ring, start, end address.Address, expected int) []mesh.PeerName { func assertPeersWithSpace(t *testing.T, ring *Ring, start, end address.Address, expected int) []mesh.PeerName {
@@ -366,7 +376,7 @@ func assertPeersWithSpace(t *testing.T, ring *Ring, start, end address.Address,
} }
func TestFindFree(t *testing.T) { func TestFindFree(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
assertPeersWithSpace(t, ring1, start, end, 0) assertPeersWithSpace(t, ring1, start, end, 0)
@@ -402,12 +412,12 @@ func TestFindFree(t *testing.T) {
} }
func TestReportFree(t *testing.T) { func TestReportFree(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring2 := New(start, end, peer2name) ring2 := NewRing(start, end, peer2name)
ring1.ClaimItAll() ring1.ClaimItAll()
ring1.GrantRangeToHost(middle, end, peer2name) ring1.GrantRangeToHost(middle, end, peer2name)
require.NoError(t, ring2.Merge(*ring1)) require.NoError(t, merge(ring2, ring1))
freespace := make(map[address.Address]address.Count) freespace := make(map[address.Address]address.Count)
for _, r := range ring2.OwnedRanges() { for _, r := range ring2.OwnedRanges() {
@@ -417,7 +427,7 @@ func TestReportFree(t *testing.T) {
} }
func TestMisc(t *testing.T) { func TestMisc(t *testing.T) {
ring := New(start, end, peer1name) ring := NewRing(start, end, peer1name)
require.True(t, ring.Empty(), "empty") require.True(t, ring.Empty(), "empty")
@@ -426,38 +436,38 @@ func TestMisc(t *testing.T) {
} }
func TestEmptyGossip(t *testing.T) { func TestEmptyGossip(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring2 := New(start, end, peer2name) ring2 := NewRing(start, end, peer2name)
ring1.ClaimItAll() ring1.ClaimItAll()
// This used to panic, and it shouldn't // This used to panic, and it shouldn't
require.NoError(t, ring1.Merge(*ring2)) require.NoError(t, merge(ring1, ring2))
} }
func TestMergeOldMessage(t *testing.T) { func TestMergeOldMessage(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring2 := New(start, end, peer2name) ring2 := NewRing(start, end, peer2name)
ring1.ClaimItAll() ring1.ClaimItAll()
require.NoError(t, ring2.Merge(*ring1)) require.NoError(t, merge(ring2, ring1))
ring1.GrantRangeToHost(middle, end, peer1name) ring1.GrantRangeToHost(middle, end, peer1name)
require.NoError(t, ring1.Merge(*ring2)) require.NoError(t, merge(ring1, ring2))
} }
func TestSplitRangeAtBeginning(t *testing.T) { func TestSplitRangeAtBeginning(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring2 := New(start, end, peer2name) ring2 := NewRing(start, end, peer2name)
ring1.ClaimItAll() ring1.ClaimItAll()
require.NoError(t, ring2.Merge(*ring1)) require.NoError(t, merge(ring2, ring1))
ring1.GrantRangeToHost(start, middle, peer2name) ring1.GrantRangeToHost(start, middle, peer2name)
require.NoError(t, ring2.Merge(*ring1)) require.NoError(t, merge(ring2, ring1))
} }
func TestOwnedRange(t *testing.T) { func TestOwnedRange(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
ring1.ClaimItAll() ring1.ClaimItAll()
require.Equal(t, []address.Range{{Start: start, End: end}}, ring1.OwnedRanges()) require.Equal(t, []address.Range{{Start: start, End: end}}, ring1.OwnedRanges())
@@ -465,8 +475,8 @@ func TestOwnedRange(t *testing.T) {
ring1.GrantRangeToHost(middle, end, peer2name) ring1.GrantRangeToHost(middle, end, peer2name)
require.Equal(t, []address.Range{{Start: start, End: middle}}, ring1.OwnedRanges()) require.Equal(t, []address.Range{{Start: start, End: middle}}, ring1.OwnedRanges())
ring2 := New(start, end, peer2name) ring2 := NewRing(start, end, peer2name)
ring2.Merge(*ring1) merge(ring2, ring1)
require.Equal(t, []address.Range{{Start: middle, End: end}}, ring2.OwnedRanges()) require.Equal(t, []address.Range{{Start: middle, End: end}}, ring2.OwnedRanges())
ring2.Entries = []*entry{{Token: middle, Peer: peer2name}} ring2.Entries = []*entry{{Token: middle, Peer: peer2name}}
@@ -480,7 +490,7 @@ func TestOwnedRange(t *testing.T) {
func TestTransfer(t *testing.T) { 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 // 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 := NewRing(start, end, peer1name)
ring1.ClaimItAll() ring1.ClaimItAll()
ring1.GrantRangeToHost(middle, end, peer2name) ring1.GrantRangeToHost(middle, end, peer2name)
ring1.Transfer(peer2name, peer1name) ring1.Transfer(peer2name, peer1name)
@@ -488,7 +498,7 @@ func TestTransfer(t *testing.T) {
// Second test is what happens when a token exists at the end of a range but is transferred // Second test is what happens when a token exists at the end of a range but is transferred
// - does it get resurrected correctly? // - does it get resurrected correctly?
ring1 = New(start, end, peer1name) ring1 = NewRing(start, end, peer1name)
ring1.ClaimItAll() ring1.ClaimItAll()
ring1.GrantRangeToHost(middle, end, peer2name) ring1.GrantRangeToHost(middle, end, peer2name)
ring1.Transfer(peer2name, peer1name) ring1.Transfer(peer2name, peer1name)
@@ -497,7 +507,7 @@ func TestTransfer(t *testing.T) {
} }
func TestOwner(t *testing.T) { func TestOwner(t *testing.T) {
ring1 := New(start, end, peer1name) ring1 := NewRing(start, end, peer1name)
require.True(t, ring1.Contains(start), "start should be in ring") require.True(t, ring1.Contains(start), "start should be in ring")
require.False(t, ring1.Contains(end), "end should not be in ring") require.False(t, ring1.Contains(end), "end should not be in ring")
@@ -521,17 +531,24 @@ func makePeerName(i int) mesh.PeerName {
return peer return peer
} }
func makePeers(numPeers int) []mesh.PeerName {
peers := make([]mesh.PeerName, numPeers)
for i := 0; i < numPeers; i++ {
peers[i] = makePeerName(i)
}
return peers
}
func TestClaimForPeers(t *testing.T) { func TestClaimForPeers(t *testing.T) {
const numPeers = 12 const numPeers = 12
// Different end to usual so we get a number of addresses that a) // Different end to usual so we get a number of addresses that a)
// is smaller than the max number of peers, and b) is divisible by // is smaller than the max number of peers, and b) is divisible by
// some number of peers. This maximises coverage of edge cases. // some number of peers. This maximises coverage of edge cases.
end := dot10 end := dot8
peers := make([]mesh.PeerName, numPeers) peers := makePeers(numPeers)
// Test for a range of peer counts // Test for a range of peer counts
for i := 0; i < numPeers; i++ { for i := 0; i < numPeers; i++ {
peers[i] = makePeerName(i) ring := NewRing(start, end, peers[0])
ring := New(start, end, peers[0])
ring.ClaimForPeers(peers[:i+1]) ring.ClaimForPeers(peers[:i+1])
} }
} }
@@ -569,7 +586,7 @@ func TestFuzzRing(t *testing.T) {
sort.Sort(addressSlice(tokens)) sort.Sort(addressSlice(tokens))
peer := peers[rand.Intn(len(peers))] peer := peers[rand.Intn(len(peers))]
ring := New(start, end, peer) ring := NewRing(start, end, peer)
for _, token := range tokens { for _, token := range tokens {
peer = peers[rand.Intn(len(peers))] peer = peers[rand.Intn(len(peers))]
ring.Entries = append(ring.Entries, &entry{Token: start + token, Peer: peer}) ring.Entries = append(ring.Entries, &entry{Token: start + token, Peer: peer})
@@ -586,7 +603,7 @@ func TestFuzzRing(t *testing.T) {
// Merge them - this might fail, we don't care // Merge them - this might fail, we don't care
// We just want to make sure it doesn't panic // We just want to make sure it doesn't panic
ring1.Merge(*ring2) merge(ring1, ring2)
// Check whats left still passes assertions // Check whats left still passes assertions
ring1.assertInvariants() ring1.assertInvariants()
@@ -603,7 +620,7 @@ func TestFuzzRing(t *testing.T) {
} }
peer := peers[rand.Intn(len(peers))] peer := peers[rand.Intn(len(peers))]
ring := New(start, end, peer) ring := NewRing(start, end, peer)
for _, token := range tokens { for _, token := range tokens {
peer = peers[rand.Intn(len(peers))] peer = peers[rand.Intn(len(peers))]
ring.Entries = append(ring.Entries, &entry{Token: start + token, Peer: peer}) ring.Entries = append(ring.Entries, &entry{Token: start + token, Peer: peer})
@@ -619,7 +636,7 @@ func TestFuzzRing(t *testing.T) {
// Merge them - this might fail, we don't care // Merge them - this might fail, we don't care
// We just want to make sure it doesn't panic // We just want to make sure it doesn't panic
ring1.Merge(*ring2) merge(ring1, ring2)
// Check whats left still passes assertions // Check whats left still passes assertions
ring1.assertInvariants() ring1.assertInvariants()
@@ -641,7 +658,7 @@ func TestFuzzRingHard(t *testing.T) {
common.Log.Debugf("%s: Adding peer", peer) common.Log.Debugf("%s: Adding peer", peer)
nextPeerID++ nextPeerID++
peers = append(peers, peer) peers = append(peers, peer)
rings = append(rings, New(start, end, peer)) rings = append(rings, NewRing(start, end, peer))
} }
for i := 0; i < numPeers; i++ { for i := 0; i < numPeers; i++ {
@@ -680,7 +697,7 @@ func TestFuzzRingHard(t *testing.T) {
// We need to be in a ~converged ring to rmpeer // We need to be in a ~converged ring to rmpeer
for _, ring := range rings { for _, ring := range rings {
require.NoError(t, otherRing.Merge(*ring)) require.NoError(t, merge(otherRing, ring))
} }
common.Log.Debugf("%s: transferring from peer %s", otherPeername, peername) common.Log.Debugf("%s: transferring from peer %s", otherPeername, peername)
@@ -694,7 +711,7 @@ func TestFuzzRingHard(t *testing.T) {
// And now tell everyone about the transfer - rmpeer is // And now tell everyone about the transfer - rmpeer is
// not partition safe // not partition safe
for i, ring := range rings { for i, ring := range rings {
require.NoError(t, ring.Merge(*otherRing)) require.NoError(t, merge(ring, otherRing))
theRanges[i] = ring.OwnedRanges() theRanges[i] = ring.OwnedRanges()
} }
} }
@@ -723,7 +740,7 @@ func TestFuzzRingHard(t *testing.T) {
// Now 'gossip' this to a random host (note, note could be same host as above) // Now 'gossip' this to a random host (note, note could be same host as above)
otherIndex, _, otherRing := randomPeer(-1) otherIndex, _, otherRing := randomPeer(-1)
common.Log.Debugf("%s: 'Gossiping' to %s", ring.Peer, otherRing.Peer) common.Log.Debugf("%s: 'Gossiping' to %s", ring.Peer, otherRing.Peer)
require.NoError(t, otherRing.Merge(*ring)) require.NoError(t, merge(otherRing, ring))
theRanges[indexWithRanges] = ring.OwnedRanges() theRanges[indexWithRanges] = ring.OwnedRanges()
theRanges[otherIndex] = otherRing.OwnedRanges() theRanges[otherIndex] = otherRing.OwnedRanges()
@@ -742,7 +759,7 @@ func TestFuzzRingHard(t *testing.T) {
ring1 := ringsWithEntries[rand.Intn(len(ringsWithEntries))] ring1 := ringsWithEntries[rand.Intn(len(ringsWithEntries))]
ring2index, _, ring2 := randomPeer(-1) ring2index, _, ring2 := randomPeer(-1)
common.Log.Debugf("%s: 'Gossiping' to %s", ring1.Peer, ring2.Peer) common.Log.Debugf("%s: 'Gossiping' to %s", ring1.Peer, ring2.Peer)
require.NoError(t, ring2.Merge(*ring1)) require.NoError(t, merge(ring2, ring1))
theRanges[ring2index] = ring2.OwnedRanges() theRanges[ring2index] = ring2.OwnedRanges()
} }
+11 -5
View File
@@ -122,8 +122,11 @@ func (s *Space) biggestFreeRange(r address.Range) (biggest address.Range) {
biggestSize := address.Count(0) biggestSize := address.Count(0)
s.walkFree(r, func(chunk address.Range) bool { s.walkFree(r, func(chunk address.Range) bool {
if size := chunk.Size(); size >= biggestSize { if size := chunk.Size(); size >= biggestSize {
biggest = chunk chunk = chunk.BiggestCIDRRange()
biggestSize = size if size = chunk.Size(); size >= biggestSize {
biggest = chunk
biggestSize = size
}
} }
return false return false
}) })
@@ -137,9 +140,12 @@ func (s *Space) Donate(r address.Range) (address.Range, bool) {
return address.Range{}, false return address.Range{}, false
} }
// Donate half of that biggest free range. Note size/2 rounds down, so // Donate no more than half what we have available
// the resulting donation size rounds up, and in particular can't be empty. if biggest.Size() > s.NumFreeAddressesInRange(r)/2 {
biggest.Start = address.Add(biggest.Start, address.Offset(biggest.Size()/2)) // 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, address.Offset(biggest.Size()/2))
}
s.ours = subtract(s.ours, biggest.Start, biggest.End) s.ours = subtract(s.ours, biggest.Start, biggest.End)
s.free = subtract(s.free, biggest.Start, biggest.End) s.free = subtract(s.free, biggest.Start, biggest.End)
+32 -31
View File
@@ -68,7 +68,7 @@ func TestLowlevel(t *testing.T) {
wt.AssertErrorInterface(t, (*error)(nil), s.Free(100), "double free") wt.AssertErrorInterface(t, (*error)(nil), s.Free(100), "double free")
r, ok := s.Donate(address.NewRange(0, 1000)) r, ok := s.Donate(address.NewRange(0, 1000))
require.True(t, ok && r.Start == 125 && r.Size() == 25, "donate") require.Equal(t, address.NewRange(0xa0, 0x20), r, "donate")
// test Donate when addresses are scarce // test Donate when addresses are scarce
s = New() s = New()
@@ -78,7 +78,7 @@ func TestLowlevel(t *testing.T) {
require.NoError(t, s.Claim(0)) require.NoError(t, s.Claim(0))
require.NoError(t, s.Claim(2)) require.NoError(t, s.Claim(2))
r, ok = s.Donate(address.NewRange(0, 1000)) r, ok = s.Donate(address.NewRange(0, 1000))
require.True(t, ok && r.Start == 1 && r.End == 2, "donate") require.Equal(t, address.NewRange(1, 1), r, "donate")
r, ok = s.Donate(address.NewRange(0, 1000)) r, ok = s.Donate(address.NewRange(0, 1000))
require.True(t, !ok, "donate should fail") require.True(t, !ok, "donate should fail")
} }
@@ -124,20 +124,21 @@ func TestSpaceAllocate(t *testing.T) {
func TestSpaceFree(t *testing.T) { func TestSpaceFree(t *testing.T) {
const ( const (
testAddr1 = "10.0.3.4" testAddr1 = "10.0.3.16"
testAddrx = "10.0.3.19" testAddrx = "10.0.3.19"
testAddry = "10.0.9.19" testAddry = "10.0.9.19"
containerID = "deadbeef" containerID = "deadbeef"
size = 16
) )
entireRange := address.NewRange(ip(testAddr1), 20) entireRange := address.NewRange(ip(testAddr1), size)
space := makeSpace(ip(testAddr1), 20) space := makeSpace(ip(testAddr1), size)
// Check we are prepared to give up the entire space // Check we are prepared to give up the entire space
r := space.biggestFreeRange(entireRange) r := space.biggestFreeRange(entireRange)
require.True(t, r.Start == ip(testAddr1) && r.Size() == 20, "Wrong space") require.Equal(t, address.NewRange(ip(testAddr1), size), r, "Wrong space")
for i := 0; i < 20; i++ { for i := 0; i < size; i++ {
ok, _ := space.Allocate(entireRange) ok, _ := space.Allocate(entireRange)
require.True(t, ok, "Failed to get address") require.True(t, ok, "Failed to get address")
} }
@@ -149,44 +150,44 @@ func TestSpaceFree(t *testing.T) {
require.True(t, r.Size() == 0, "Wrong space") require.True(t, r.Size() == 0, "Wrong space")
// Free in the middle // 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"))) require.NoError(t, space.Free(ip("10.0.3.23")))
r = space.biggestFreeRange(entireRange) r = space.biggestFreeRange(entireRange)
require.True(t, r.Start == ip("10.0.3.23") && r.Size() == 1, "Wrong space") require.True(t, r.Start == ip("10.0.3.23") && r.Size() == 1, "Wrong space")
// Free one at the end
require.NoError(t, space.Free(ip("10.0.3.31")))
r = space.biggestFreeRange(entireRange)
require.True(t, r.Start == ip("10.0.3.31") && r.Size() == 1, "Wrong space")
// Now free a few at the end // 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.30")))
require.NoError(t, space.Free(ip("10.0.3.21"))) require.NoError(t, space.Free(ip("10.0.3.29")))
require.Equal(t, address.Count(4), space.NumFreeAddresses()) require.Equal(t, address.Count(4), space.NumFreeAddresses())
// Now get the biggest free space; should be 3.21 // Now get the biggest free space; should be 3.30
r = space.biggestFreeRange(entireRange) r = space.biggestFreeRange(entireRange)
require.True(t, r.Start == ip("10.0.3.21") && r.Size() == 3, "Wrong space") require.Equal(t, address.NewRange(ip("10.0.3.30"), 2), r, "Wrong space")
// Now free a few in the middle // 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.24")))
require.NoError(t, space.Free(ip("10.0.3.11"))) require.NoError(t, space.Free(ip("10.0.3.22")))
require.NoError(t, space.Free(ip("10.0.3.10"))) require.NoError(t, space.Free(ip("10.0.3.21")))
require.Equal(t, address.Count(7), space.NumFreeAddresses()) require.Equal(t, address.Count(7), space.NumFreeAddresses())
// Now get the biggest free space; should be 3.21 // Now get the biggest free space; should be 3.30
r = space.biggestFreeRange(entireRange) r = space.biggestFreeRange(entireRange)
require.True(t, r.Start == ip("10.0.3.10") && r.Size() == 4, "Wrong space") require.Equal(t, address.NewRange(ip("10.0.3.30"), 2), r, "Wrong space")
require.Equal(t, []address.Range{{Start: ip("10.0.3.4"), End: ip("10.0.3.24")}}, space.OwnedRanges()) require.Equal(t, []address.Range{{Start: ip("10.0.3.16"), End: ip("10.0.3.32")}}, space.OwnedRanges())
} }
func TestDonateSimple(t *testing.T) { func TestDonateSimple(t *testing.T) {
const ( const (
testAddr1 = "10.0.1.0" testAddr1 = "10.0.1.0"
testAddr2 = "10.0.1.32" testAddr2 = "10.0.1.32"
size = 48 size = 64
) )
var ( var (
@@ -199,7 +200,7 @@ func TestDonateSimple(t *testing.T) {
r, ok := ps1.Donate(address.NewRange(ip(testAddr1), size)) r, ok := ps1.Donate(address.NewRange(ip(testAddr1), size))
numGivenUp := r.Size() numGivenUp := r.Size()
require.True(t, ok, "Donate result") require.True(t, ok, "Donate result")
require.Equal(t, "10.0.1.24", r.Start.String(), "Invalid start") require.Equal(t, "10.0.1.32", r.Start.String(), "Invalid start")
require.Equal(t, address.Count(size/2), numGivenUp) require.Equal(t, address.Count(size/2), numGivenUp)
require.Equal(t, address.Count(size/2), ps1.NumFreeAddresses()) require.Equal(t, address.Count(size/2), ps1.NumFreeAddresses())
@@ -241,13 +242,13 @@ func TestDonateHard(t *testing.T) {
// Now split // Now split
newRange, ok := spaceset.Donate(address.NewRange(start, size)) newRange, ok := spaceset.Donate(address.NewRange(start, size))
require.True(t, ok, "GiveUpSpace result") require.True(t, ok, "GiveUpSpace result")
require.Equal(t, ip("10.0.1.23"), newRange.Start) require.Equal(t, address.NewRange(ip("10.0.1.16"), 16), newRange, "Wrong space")
require.Equal(t, address.Count(24), newRange.Size()) require.Equal(t, address.Count(31), spaceset.NumFreeAddresses())
require.Equal(t, address.Count(23), spaceset.NumFreeAddresses())
//Space set should now have 2 spaces //Space set should now have 3 spaces
expected := New() expected := &Space{
expected.Add(start, 23) ours: add(nil, ip("10.0.1.47"), ip("10.0.1.48")),
expected.ours = add(nil, ip("10.0.1.47"), ip("10.0.1.48")) free: add(add(nil, ip("10.0.1.0"), ip("10.0.1.16")), ip("10.0.1.32"), ip("10.0.1.47")),
}
require.Equal(t, expected, spaceset) require.Equal(t, expected, spaceset)
} }
+1 -1
View File
@@ -143,7 +143,7 @@ func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, addres
OurName: peername, OurName: peername,
OurUID: mesh.PeerUID(rand.Int63()), OurUID: mesh.PeerUID(rand.Int63()),
OurNickname: "nick-" + name, OurNickname: "nick-" + name,
Universe: cidr.Range(), Universe: cidr,
IsObserver: quorum == 0, IsObserver: quorum == 0,
Quorum: func() uint { return quorum }, Quorum: func() uint { return quorum },
Db: new(mockDB), Db: new(mockDB),
+286
View File
@@ -0,0 +1,286 @@
package tracker
// The AWSVPC tracker tracks the IPAM ring changes and accordingly updates
// the AWS VPC route table and, if the announced change is local
// (denoted by local=true) to the host, the host route table.
//
// During the initialization, the tracker detects AWS VPC route table id which
// is associated with the host's subnet on the AWS network. If such a table does
// not exist, the default VPC route table is used.
//
// When a host A donates a range to a host B, the necessary route table
// updates (removal) happen on the host A first, and afterwards on
// the host B (installation).
//
// NB: there is a hard limit for 50 routes within any VPC route table
// (practically, it is 48, because one route is used by the AWS Internet GW and
// one by the AWS host subnet), therefore it is suggested to avoid
// an excessive fragmentation within the IPAM ring which might happen due to
// the claim operations or uneven distribution of containers across the hosts.
import (
"fmt"
"net"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/vishvananda/netlink"
"github.com/weaveworks/weave/common"
wnet "github.com/weaveworks/weave/net"
"github.com/weaveworks/weave/net/address"
)
type AWSVPCTracker struct {
ec2 *ec2.EC2
instanceID string // EC2 Instance ID
routeTableID string // VPC Route Table ID
linkIndex int // The weave bridge link index
}
// NewAWSVPCTracker creates and initialises AWS VPC based tracker.
func NewAWSVPCTracker() (*AWSVPCTracker, error) {
var (
err error
session = session.New()
t = &AWSVPCTracker{}
)
// Detect region and instance id
meta := ec2metadata.New(session)
t.instanceID, err = meta.GetMetadata("instance-id")
if err != nil {
return nil, fmt.Errorf("cannot detect instance-id: %s", err)
}
region, err := meta.Region()
if err != nil {
return nil, fmt.Errorf("cannot detect region: %s", err)
}
t.ec2 = ec2.New(session, aws.NewConfig().WithRegion(region))
routeTableID, err := t.detectRouteTableID()
if err != nil {
return nil, err
}
t.routeTableID = *routeTableID
// Detect Weave bridge link index
link, err := netlink.LinkByName(wnet.WeaveBridgeName)
if err != nil {
return nil, fmt.Errorf("cannot find \"%s\" interface: %s", wnet.WeaveBridgeName, err)
}
t.linkIndex = link.Attrs().Index
t.infof("AWSVPC has been initialized on %s instance for %s route table at %s region",
t.instanceID, t.routeTableID, region)
return t, nil
}
// HandleUpdate method updates the AWS VPC and the host route tables.
func (t *AWSVPCTracker) HandleUpdate(prevRanges, currRanges []address.Range, local bool) error {
t.debugf("replacing %q by %q; local(%t)", prevRanges, currRanges, local)
prev, curr := removeCommon(address.NewCIDRs(merge(prevRanges)), address.NewCIDRs(merge(currRanges)))
// It might make sense to do the removal first and then add entries
// because of the 50 routes limit. However, in such case a container might
// not be reachable for a short period of time which is not a desired behavior.
// Add new entries
for _, cidr := range curr {
cidrStr := cidr.String()
t.debugf("adding route %s to %s", cidrStr, t.instanceID)
_, err := t.createVPCRoute(cidrStr)
if err != nil {
return fmt.Errorf("createVPCRoutes failed: %s", err)
}
if local {
err = t.createHostRoute(cidrStr)
if err != nil {
return fmt.Errorf("createHostRoute failed: %s", err)
}
}
}
// Remove obsolete entries
for _, cidr := range prev {
cidrStr := cidr.String()
t.debugf("removing %s route", cidrStr)
_, err := t.deleteVPCRoute(cidrStr)
if err != nil {
return fmt.Errorf("deleteVPCRoute failed: %s", err)
}
if local {
err = t.deleteHostRoute(cidrStr)
if err != nil {
return fmt.Errorf("deleteHostRoute failed: %s", err)
}
}
}
return nil
}
func (t *AWSVPCTracker) createVPCRoute(cidr string) (*ec2.CreateRouteOutput, error) {
route := &ec2.CreateRouteInput{
RouteTableId: &t.routeTableID,
InstanceId: &t.instanceID,
DestinationCidrBlock: &cidr,
}
return t.ec2.CreateRoute(route)
}
func (t *AWSVPCTracker) createHostRoute(cidr string) error {
dst, err := parseCIDR(cidr)
if err != nil {
return err
}
route := &netlink.Route{
LinkIndex: t.linkIndex,
Dst: dst,
Scope: netlink.SCOPE_LINK,
}
return netlink.RouteAdd(route)
}
func (t *AWSVPCTracker) deleteVPCRoute(cidr string) (*ec2.DeleteRouteOutput, error) {
route := &ec2.DeleteRouteInput{
RouteTableId: &t.routeTableID,
DestinationCidrBlock: &cidr,
}
return t.ec2.DeleteRoute(route)
}
func (t *AWSVPCTracker) deleteHostRoute(cidr string) error {
dst, err := parseCIDR(cidr)
if err != nil {
return err
}
route := &netlink.Route{
LinkIndex: t.linkIndex,
Dst: dst,
Scope: netlink.SCOPE_LINK,
}
return netlink.RouteDel(route)
}
// detectRouteTableID detects AWS VPC Route Table ID of the given tracker instance.
func (t *AWSVPCTracker) detectRouteTableID() (*string, error) {
instancesParams := &ec2.DescribeInstancesInput{
InstanceIds: []*string{aws.String(t.instanceID)},
}
instancesResp, err := t.ec2.DescribeInstances(instancesParams)
if err != nil {
return nil, fmt.Errorf("DescribeInstances failed: %s", err)
}
if len(instancesResp.Reservations) == 0 ||
len(instancesResp.Reservations[0].Instances) == 0 {
return nil, fmt.Errorf("cannot find %s instance within reservations", t.instanceID)
}
vpcID := instancesResp.Reservations[0].Instances[0].VpcId
subnetID := instancesResp.Reservations[0].Instances[0].SubnetId
// First try to find a routing table associated with the subnet of the instance
tablesParams := &ec2.DescribeRouteTablesInput{
Filters: []*ec2.Filter{
{
Name: aws.String("association.subnet-id"),
Values: []*string{subnetID},
},
},
}
tablesResp, err := t.ec2.DescribeRouteTables(tablesParams)
if err != nil {
return nil, fmt.Errorf("DescribeRouteTables failed: %s", err)
}
if len(tablesResp.RouteTables) != 0 {
return tablesResp.RouteTables[0].RouteTableId, nil
}
// Fallback to the default routing table
tablesParams = &ec2.DescribeRouteTablesInput{
Filters: []*ec2.Filter{
{
Name: aws.String("association.main"),
Values: []*string{aws.String("true")},
},
{
Name: aws.String("vpc-id"),
Values: []*string{vpcID},
},
},
}
tablesResp, err = t.ec2.DescribeRouteTables(tablesParams)
if err != nil {
return nil, fmt.Errorf("DescribeRouteTables failed: %s", err)
}
if len(tablesResp.RouteTables) != 0 {
return tablesResp.RouteTables[0].RouteTableId, nil
}
return nil, fmt.Errorf("cannot find routetable for %s instance", t.instanceID)
}
func (t *AWSVPCTracker) debugf(fmt string, args ...interface{}) {
common.Log.Debugf("[tracker] "+fmt, args...)
}
func (t *AWSVPCTracker) infof(fmt string, args ...interface{}) {
common.Log.Infof("[tracker] "+fmt, args...)
}
// Helpers
// merge merges adjacent range entries.
// The given slice has to be sorted in increasing order.
func merge(r []address.Range) []address.Range {
var merged []address.Range
for i := range r {
if prev := len(merged) - 1; prev >= 0 && merged[prev].End == r[i].Start {
merged[prev].End = r[i].End
} else {
merged = append(merged, r[i])
}
}
return merged
}
// removeCommon filters out CIDR ranges which are contained in both a and b slices.
// Both slices have to be sorted in increasing order.
func removeCommon(a, b []address.CIDR) (newA, newB []address.CIDR) {
i, j := 0, 0
for i < len(a) && j < len(b) {
switch {
case a[i].Start() < b[j].Start() || a[i].End() < b[j].End():
newA = append(newA, a[i])
i++
case a[i].Start() > b[j].Start() || a[i].End() > b[j].End():
newB = append(newB, b[j])
j++
default:
i++
j++
}
}
newA = append(newA, a[i:]...)
newB = append(newB, b[j:]...)
return
}
func parseCIDR(cidr string) (*net.IPNet, error) {
ip, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
return nil, err
}
ipnet.IP = ip
return ipnet, nil
}
+52
View File
@@ -0,0 +1,52 @@
package tracker
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/net/address"
)
var (
r0to127 = cidr("10.0.0.0", "10.0.0.127")
r128to255 = cidr("10.0.0.128", "10.0.0.255")
r0to255 = cidr("10.0.0.0", "10.0.0.255")
r1dot0to255 = cidr("10.0.1.0", "10.0.1.255")
r2dot0to255 = cidr("10.0.2.0", "10.0.2.255")
)
func TestRemoveCommon(t *testing.T) {
a := []address.CIDR{r0to127, r1dot0to255}
b := []address.CIDR{r1dot0to255, r2dot0to255}
newA, newB := removeCommon(a, b)
require.Equal(t, []address.CIDR{r0to127}, newA)
require.Equal(t, []address.CIDR{r2dot0to255}, newB)
}
func TestMerge(t *testing.T) {
ranges := []address.Range{
r0to127.Range(),
r128to255.Range(),
r2dot0to255.Range(),
}
require.Equal(t, []address.Range{r0to255.Range(), r2dot0to255.Range()}, merge(ranges))
}
// Helper
// TODO(mp) DRY with helpers of other tests.
func ip(s string) address.Address {
addr, _ := address.ParseIP(s)
return addr
}
// [start; end]
func cidr(start, end string) address.CIDR {
c := address.Range{Start: ip(start), End: ip(end) + 1}.CIDRs()
if len(c) != 1 {
panic("invalid cidr")
}
return c[0]
}
+20
View File
@@ -0,0 +1,20 @@
package tracker
import (
"github.com/weaveworks/weave/net/address"
)
// LocalRangeTracker is an interface for tracking changes in the IPAM ring.
type LocalRangeTracker interface {
// HandleUpdate is called whenever an address ring gets updated.
//
// prevRanges corresponds to ranges which were owned by a peer before
// a change in the ring, while currRanges to the ones which are currently
// owned by the peer.
// Both slices have to be sorted in increasing order.
// Adjacent ranges within each slice might appear as separate ranges.
//
// The local parameter indicates whether the ranges belong to the peer
// by which the method is called.
HandleUpdate(prevRanges, currRanges []address.Range, local bool) error
}
+69 -23
View File
@@ -5,8 +5,10 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"net" "net"
"os"
"sort" "sort"
"strings" "strings"
"sync"
"time" "time"
"github.com/miekg/dns" "github.com/miekg/dns"
@@ -17,7 +19,6 @@ import (
const ( const (
topDomain = "." topDomain = "."
reverseDNSdomain = "in-addr.arpa." reverseDNSdomain = "in-addr.arpa."
etcResolvConf = "/etc/resolv.conf"
udpBuffSize = uint16(4096) udpBuffSize = uint16(4096)
minUDPSize = 512 minUDPSize = 512
@@ -26,16 +27,48 @@ const (
DefaultClientTimeout = 5 * time.Second DefaultClientTimeout = 5 * time.Second
) )
type DNSServer struct { type Upstream interface {
ns *Nameserver Config() *dns.ClientConfig
domain string }
ttl uint32
address string
servers []*dns.Server func NewUpstream(resolvConf, filterAddress string) Upstream {
upstream *dns.ClientConfig return &upstream{
tcpClient *dns.Client resolvConf: resolvConf,
udpClient *dns.Client filterAddress: filterAddress,
cachedConfig: &dns.ClientConfig{}}
}
type upstream struct {
sync.Mutex
resolvConf string
filterAddress string
cachedConfig *dns.ClientConfig
lastStat time.Time
lastModified time.Time
}
func (u *upstream) Config() *dns.ClientConfig {
u.Lock()
defer u.Unlock()
// If we checked less than five seconds ago return what we already have
now := time.Now()
if now.Sub(u.lastStat) < 5*time.Second {
return u.cachedConfig
}
u.lastStat = now
// Reread file if it has changed
if fi, err := os.Stat(u.resolvConf); err == nil && fi.ModTime() != u.lastModified {
if config, err := dns.ClientConfigFromFile(u.resolvConf); err == nil {
config.Servers = filter(config.Servers, u.filterAddress)
u.lastModified = fi.ModTime()
u.cachedConfig = config
}
}
return u.cachedConfig
} }
func filter(ss []string, s string) []string { func filter(ss []string, s string) []string {
@@ -49,24 +82,30 @@ func filter(ss []string, s string) []string {
return ss return ss
} }
func NewDNSServer(ns *Nameserver, domain, address, effectiveAddress string, ttl uint32, clientTimeout time.Duration) (*DNSServer, error) { type DNSServer struct {
ns *Nameserver
domain string
ttl uint32
address string
servers []*dns.Server
upstream Upstream
tcpClient *dns.Client
udpClient *dns.Client
}
func NewDNSServer(ns *Nameserver, domain, address string, upstream Upstream, ttl uint32, clientTimeout time.Duration) (*DNSServer, error) {
s := &DNSServer{ s := &DNSServer{
ns: ns, ns: ns,
domain: dns.Fqdn(domain), domain: dns.Fqdn(domain),
ttl: ttl, ttl: ttl,
address: address, address: address,
upstream: upstream,
tcpClient: &dns.Client{Net: "tcp", ReadTimeout: clientTimeout}, tcpClient: &dns.Client{Net: "tcp", ReadTimeout: clientTimeout},
udpClient: &dns.Client{Net: "udp", ReadTimeout: clientTimeout, UDPSize: udpBuffSize}, 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) err := s.listen(address)
return s, err return s, err
} }
@@ -134,7 +173,7 @@ func (d *DNSServer) createMux(client *dns.Client, defaultMaxResponseSize int) *d
func (h *handler) handleLocal(w dns.ResponseWriter, req *dns.Msg) { func (h *handler) handleLocal(w dns.ResponseWriter, req *dns.Msg) {
h.ns.debugf("local request: %+v", *req) h.ns.debugf("local request: %+v", *req)
if len(req.Question) != 1 || req.Question[0].Qtype != dns.TypeA { if len(req.Question) != 1 {
h.nameError(w, req) h.nameError(w, req)
return return
} }
@@ -149,6 +188,12 @@ func (h *handler) handleLocal(w dns.ResponseWriter, req *dns.Msg) {
h.nameError(w, req) h.nameError(w, req)
return return
} }
// Per RFC4074, if we have an A but another type was requested,
// return 'no error' with empty answer section
if req.Question[0].Qtype != dns.TypeA {
h.respond(w, h.makeResponse(req, nil))
return
}
header := dns.RR_Header{ header := dns.RR_Header{
Name: req.Question[0].Name, Name: req.Question[0].Name,
@@ -204,7 +249,7 @@ func (h *handler) handleRecursive(w dns.ResponseWriter, req *dns.Msg) {
h.ns.debugf("recursive request: %+v", *req) h.ns.debugf("recursive request: %+v", *req)
// Resolve unqualified names locally // Resolve unqualified names locally
if len(req.Question) == 1 && req.Question[0].Qtype == dns.TypeA { if len(req.Question) == 1 {
hostname := dns.Fqdn(req.Question[0].Name) hostname := dns.Fqdn(req.Question[0].Name)
if strings.Count(hostname, ".") == 1 { if strings.Count(hostname, ".") == 1 {
h.handleLocal(w, req) h.handleLocal(w, req)
@@ -212,10 +257,11 @@ func (h *handler) handleRecursive(w dns.ResponseWriter, req *dns.Msg) {
} }
} }
for _, server := range h.upstream.Servers { upstreamConfig := h.upstream.Config()
for _, server := range upstreamConfig.Servers {
reqCopy := req.Copy() reqCopy := req.Copy()
reqCopy.Id = dns.Id() reqCopy.Id = dns.Id()
response, _, err := h.client.Exchange(reqCopy, fmt.Sprintf("%s:%s", server, h.upstream.Port)) response, _, err := h.client.Exchange(reqCopy, fmt.Sprintf("%s:%s", server, upstreamConfig.Port))
if (err != nil && err != dns.ErrTruncated) || response == nil { if (err != nil && err != dns.ErrTruncated) || response == nil {
h.ns.debugf("error trying %s: %v", server, err) h.ns.debugf("error trying %s: %v", server, err)
continue continue
+9 -4
View File
@@ -15,17 +15,22 @@ import (
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
) )
type mockUpstream struct {
config *dns.ClientConfig
}
func (mu *mockUpstream) Config() *dns.ClientConfig {
return mu.config
}
func startServer(t *testing.T, upstream *dns.ClientConfig) (*DNSServer, *Nameserver, int, int) { func startServer(t *testing.T, upstream *dns.ClientConfig) (*DNSServer, *Nameserver, int, int) {
peername, err := mesh.PeerNameFromString("00:00:00:02:00:00") peername, err := mesh.PeerNameFromString("00:00:00:02:00:00")
require.Nil(t, err) require.Nil(t, err)
nameserver := New(peername, "", func(mesh.PeerName) bool { return true }) nameserver := New(peername, "", func(mesh.PeerName) bool { return true })
dnsserver, err := NewDNSServer(nameserver, "weave.local.", "0.0.0.0:0", "", 30, 5*time.Second) dnsserver, err := NewDNSServer(nameserver, "weave.local.", "0.0.0.0:0", &mockUpstream{upstream}, 30, 5*time.Second)
require.Nil(t, err) require.Nil(t, err)
udpPort := dnsserver.servers[0].PacketConn.LocalAddr().(*net.UDPAddr).Port udpPort := dnsserver.servers[0].PacketConn.LocalAddr().(*net.UDPAddr).Port
tcpPort := dnsserver.servers[1].Listener.Addr().(*net.TCPAddr).Port tcpPort := dnsserver.servers[1].Listener.Addr().(*net.TCPAddr).Port
if upstream != nil {
dnsserver.upstream = upstream
}
go dnsserver.ActivateAndServe() go dnsserver.ActivateAndServe()
return dnsserver, nameserver, udpPort, tcpPort return dnsserver, nameserver, udpPort, tcpPort
} }
+1 -1
View File
@@ -38,7 +38,7 @@ func NewStatus(ns *Nameserver, dnsServer *DNSServer) *Status {
return &Status{ return &Status{
dnsServer.domain, dnsServer.domain,
dnsServer.upstream.Servers, dnsServer.upstream.Config().Servers,
dnsServer.address, dnsServer.address,
dnsServer.ttl, dnsServer.ttl,
entryStatusSlice} entryStatusSlice}
+80 -1
View File
@@ -35,6 +35,35 @@ func (r Range) AsCIDRString() string {
return CIDR{Addr: r.Start, PrefixLen: prefixLen}.String() return CIDR{Addr: r.Start, PrefixLen: prefixLen}.String()
} }
// return the highest bit set in v
// algorithm from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
func msb(v Count) Count {
v |= v >> 1
v |= v >> 2
v |= v >> 4
v |= v >> 8
v |= v >> 16
// Cast to 64-bit to avoid overflow when original v >= 0x80000000
return Count((uint64(v) + 1) / 2)
}
func (r Range) BiggestCIDRRange() Range {
sizeMsb := Offset(msb(r.Size()))
maskedSize := Offset(r.Size()) & (sizeMsb - 1)
maskedStart := Offset(r.Start) & (sizeMsb - 1)
if maskedStart == 0 {
return NewRange(r.Start, sizeMsb)
}
if sizeMsb-maskedStart <= maskedSize {
return NewRange(Add(r.Start, sizeMsb-maskedStart), sizeMsb)
}
size := sizeMsb / 2
if size >= maskedStart {
return NewRange(Add(r.Start, size-maskedStart), size)
}
return NewRange(Add(r.Start, sizeMsb-maskedStart), size)
}
func MakeCIDR(subnet CIDR, addr Address) CIDR { func MakeCIDR(subnet CIDR, addr Address) CIDR {
return CIDR{Addr: addr, PrefixLen: subnet.PrefixLen} return CIDR{Addr: addr, PrefixLen: subnet.PrefixLen}
} }
@@ -44,6 +73,40 @@ type CIDR struct {
PrefixLen int PrefixLen int
} }
// CIDRs returns a list of CIDR-aligned ranges which cover this range.
func (r Range) CIDRs() []CIDR {
const (
fullMask = ^Address(0)
cidrMaxPrefixLen = 32
)
var cidrs []CIDR
for start, end := r.Start, r.End-1; end >= start; {
mask, prefixLen := fullMask, cidrMaxPrefixLen
// Find the smallest mask which would cover some part of [start;end].
// Once we found such, apply it by OR'ing
for mask > 0 {
tmpMask := mask << 1
// Check whether mask neither too short nor too long
if (start&tmpMask) != start || (start|^tmpMask) > end {
break
}
mask = tmpMask
prefixLen--
}
cidrs = append(cidrs, CIDR{start, prefixLen})
// Apply mask
start |= ^mask
// Check for overflow
if start+1 < start {
break
}
start++
}
return cidrs
}
func ParseIP(s string) (Address, error) { func ParseIP(s string) (Address, error) {
if ip := net.ParseIP(s); ip != nil { if ip := net.ParseIP(s); ip != nil {
return FromIP4(ip), nil return FromIP4(ip), nil
@@ -62,6 +125,22 @@ func ParseCIDR(s string) (CIDR, error) {
} }
} }
func NewCIDRs(ranges []Range) (cidrs []CIDR) {
for _, r := range ranges {
cidrs = append(cidrs, r.CIDRs()...)
}
return cidrs
}
func (cidr CIDR) Start() Address {
return cidr.Addr
}
// cidr = [Start; End)
func (cidr CIDR) End() Address {
return cidr.Range().End
}
func (cidr CIDR) IsSubnet() bool { func (cidr CIDR) IsSubnet() bool {
mask := cidr.Size() - 1 mask := cidr.Size() - 1
return Offset(cidr.Addr)&mask == 0 return Offset(cidr.Addr)&mask == 0
@@ -122,7 +201,7 @@ func Length(a, b Address) Count {
return Count(a - b) return Count(a - b)
} }
func Min(a, b Offset) Offset { func Min(a, b Count) Count {
if a > b { if a > b {
return b return b
} }
+69
View File
@@ -0,0 +1,69 @@
package address
import (
"testing"
"testing/quick"
"github.com/stretchr/testify/require"
)
func isPower2(x Count) bool {
if x == 0 {
return false
}
for ; x > 1; x /= 2 {
if x&1 != 0 {
return false
}
}
return true
}
func TestBiggestPow2AlignedRange(t *testing.T) {
require.Equal(t, NewRange(0, 1), NewRange(0, 1).BiggestCIDRRange())
require.Equal(t, NewRange(1, 1), NewRange(1, 2).BiggestCIDRRange())
require.Equal(t, NewRange(2, 2), NewRange(1, 3).BiggestCIDRRange())
require.Equal(t, NewRange(0, 0x40000000), NewRange(0, 0x7fffffff).BiggestCIDRRange())
require.Equal(t, NewRange(0xfffffffe, 1), NewRange(0xfffffffe, 1).BiggestCIDRRange())
prop := func(start Address, size Offset) bool {
if size > Offset(0xffffffff)-Offset(start) { // out of range
return true
}
r := NewRange(start, size)
result := r.BiggestCIDRRange()
return r.Contains(result.Start) &&
r.Contains(result.End-1) &&
isPower2(result.Size()) &&
result.Size() > r.Size()/4 &&
Count(result.Start)%result.Size() == 0
}
require.NoError(t, quick.Check(prop, &quick.Config{MaxCount: 1000000}))
}
func ip(s string) Address {
addr, _ := ParseIP(s)
return addr
}
func cidr(s string) CIDR {
c, err := ParseCIDR(s)
if err != nil {
panic(err)
}
return c
}
func TestCIDRs(t *testing.T) {
start := ip("10.0.0.1")
end := ip("10.0.0.9")
r := NewRange(start, Subtract(end, start))
require.Equal(t,
[]CIDR{cidr("10.0.0.1/32"), cidr("10.0.0.2/31"), cidr("10.0.0.4/30"), cidr("10.0.0.8/32")},
r.CIDRs())
}
func TestCIDRStartAndEnd(t *testing.T) {
cidr, _ := ParseCIDR("10.0.0.0/24")
require.Equal(t, ip("10.0.0.0"), cidr.Start(), "")
require.Equal(t, ip("10.0.1.0"), cidr.End(), "")
}
+1 -1
View File
@@ -1,4 +1,4 @@
package common package net
import "fmt" import "fmt"
import "io" import "io"
+53
View File
@@ -0,0 +1,53 @@
package net
import (
"github.com/vishvananda/netlink"
)
type BridgeType int
const (
WeaveBridgeName = "weave"
DatapathName = "datapath"
None BridgeType = iota
Bridge
Fastdp
BridgedFastdp
Inconsistent
)
func DetectBridgeType(weaveBridgeName, datapathName string) BridgeType {
bridge, _ := netlink.LinkByName(weaveBridgeName)
datapath, _ := netlink.LinkByName(datapathName)
switch {
case bridge == nil && datapath == nil:
return None
case isBridge(bridge) && datapath == nil:
return Bridge
case isDatapath(bridge) && datapath == nil:
return Fastdp
case isDatapath(datapath) && isBridge(bridge):
return BridgedFastdp
default:
return Inconsistent
}
}
func isBridge(link netlink.Link) bool {
_, isBridge := link.(*netlink.Bridge)
return isBridge
}
func isDatapath(link netlink.Link) bool {
switch link.(type) {
case *netlink.GenericLink:
return link.Type() == "openvswitch"
case *netlink.Device:
// Assume it's our openvswitch device, and the kernel has not been updated to report the kind.
return true
default:
return false
}
}
+52
View File
@@ -0,0 +1,52 @@
package net
import "fmt"
import "syscall"
import "unsafe"
const (
SIOCETHTOOL = 0x8946 // linux/sockios.h
ETHTOOL_STXCSUM = 0x00000017 // linux/ethtool.h
IFNAMSIZ = 16 // linux/if.h
)
// linux/if.h 'struct ifreq'
type IFReqData struct {
Name [IFNAMSIZ]byte
Data uintptr
}
// linux/ethtool.h 'struct ethtool_value'
type EthtoolValue struct {
Cmd uint32
Data uint32
}
// Disable TX checksum offload on specified interface
func EthtoolTXOff(name string) error {
if len(name)+1 > IFNAMSIZ {
return fmt.Errorf("name too long")
}
value := EthtoolValue{ETHTOOL_STXCSUM, 0}
request := IFReqData{Data: uintptr(unsafe.Pointer(&value))}
copy(request.Name[:], name)
socket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
if err != nil {
return err
}
defer syscall.Close(socket)
_, _, errno := syscall.RawSyscall(syscall.SYS_IOCTL,
uintptr(socket),
uintptr(SIOCETHTOOL),
uintptr(unsafe.Pointer(&request)))
if errno != 0 {
return errno
}
return nil
}
+37
View File
@@ -0,0 +1,37 @@
package net
import (
"runtime"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
)
func WithNetNS(ns netns.NsHandle, work func() error) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
oldNs, err := netns.Get()
if err == nil {
defer oldNs.Close()
err = netns.Set(ns)
if err == nil {
defer netns.Set(oldNs)
err = work()
}
}
return err
}
func WithNetNSLink(ns netns.NsHandle, ifName string, work func(link netlink.Link) error) error {
return WithNetNS(ns, func() error {
link, err := netlink.LinkByName(ifName)
if err != nil {
return err
}
return work(link)
})
}
+14
View File
@@ -3,6 +3,7 @@ package net
import ( import (
"fmt" "fmt"
"net" "net"
"os"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
) )
@@ -55,3 +56,16 @@ func forEachRoute(ignoreIfaceNames map[string]struct{}, check func(r netlink.Rou
} }
return nil return nil
} }
func AddRoute(link netlink.Link, scope netlink.Scope, dst *net.IPNet, gw net.IP) error {
err := netlink.RouteAdd(&netlink.Route{
LinkIndex: link.Attrs().Index,
Scope: scope,
Dst: dst,
Gw: gw,
})
if os.IsExist(err) { // squash duplicate route errors
err = nil
}
return err
}
+204
View File
@@ -0,0 +1,204 @@
package net
import (
"fmt"
"net"
"time"
"github.com/j-keck/arping"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
"github.com/weaveworks/weave/common/odp"
)
// create and attach a veth to the Weave bridge
func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, keepTXOn bool, init func(peer netlink.Link) error) (*netlink.Veth, error) {
bridge, err := netlink.LinkByName(bridgeName)
if err != nil {
return nil, fmt.Errorf(`bridge "%s" not present; did you launch weave?`, bridgeName)
}
if mtu == 0 {
mtu = bridge.Attrs().MTU
}
veth := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{
Name: name,
MTU: mtu},
PeerName: peerName,
}
if err := netlink.LinkAdd(veth); err != nil {
return nil, fmt.Errorf(`could not create veth pair %s-%s: %s`, name, peerName, err)
}
cleanup := func(format string, a ...interface{}) (*netlink.Veth, error) {
netlink.LinkDel(veth)
return nil, fmt.Errorf(format, a...)
}
switch bridgeType := DetectBridgeType(bridgeName, DatapathName); bridgeType {
case Bridge, BridgedFastdp:
if err := netlink.LinkSetMasterByIndex(veth, bridge.Attrs().Index); err != nil {
return cleanup(`unable to set master of %s: %s`, name, err)
}
if bridgeType == Bridge && !keepTXOn {
if err := EthtoolTXOff(peerName); err != nil {
return cleanup(`unable to set tx off on %q: %s`, peerName, err)
}
}
case Fastdp:
if err := odp.AddDatapathInterface(bridgeName, name); err != nil {
return cleanup(`failed to attach %s to device "%s": %s`, name, bridgeName, err)
}
default:
return cleanup(`invalid bridge configuration`)
}
if init != nil {
peer, err := netlink.LinkByName(peerName)
if err != nil {
return cleanup("unable to find peer veth %s: %s", peerName, err)
}
if err := init(peer); err != nil {
return cleanup("initializing veth: %s", err)
}
}
if err := netlink.LinkSetUp(veth); err != nil {
return cleanup("unable to bring veth up: %s", err)
}
return veth, nil
}
func AddAddresses(link netlink.Link, cidrs []*net.IPNet) (newAddrs []*net.IPNet, err error) {
existingAddrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
if err != nil {
return nil, fmt.Errorf("failed to get IP address for %q: %v", link.Attrs().Name, err)
}
for _, ipnet := range cidrs {
if contains(existingAddrs, ipnet) {
continue
}
if err := netlink.AddrAdd(link, &netlink.Addr{IPNet: ipnet}); err != nil {
return nil, fmt.Errorf("failed to add IP address to %q: %v", link.Attrs().Name, err)
}
newAddrs = append(newAddrs, ipnet)
}
return newAddrs, nil
}
func contains(addrs []netlink.Addr, addr *net.IPNet) bool {
for _, x := range addrs {
if addr.IP.Equal(x.IPNet.IP) {
return true
}
}
return false
}
const (
VethName = "ethwe" // name inside container namespace
vethPrefix = "v" + VethName // starts with "veth" to suppress UI notifications
)
func interfaceExistsInNamespace(ns netns.NsHandle, ifName string) bool {
err := WithNetNS(ns, func() error {
_, err := netlink.LinkByName(ifName)
return err
})
return err == nil
}
func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int, withMulticastRoute bool, cidrs []*net.IPNet, keepTXOn bool) error {
if !interfaceExistsInNamespace(ns, ifName) {
maxIDLen := IFNAMSIZ - 1 - len(vethPrefix+"pl")
if len(id) > maxIDLen {
id = id[:maxIDLen] // trim passed ID if too long
}
name, peerName := vethPrefix+"pl"+id, vethPrefix+"pg"+id
_, err := CreateAndAttachVeth(name, peerName, bridgeName, mtu, keepTXOn, func(veth netlink.Link) error {
if err := netlink.LinkSetNsFd(veth, int(ns)); err != nil {
return fmt.Errorf("failed to move veth to container netns: %s", err)
}
if err := WithNetNS(ns, func() error {
if err := netlink.LinkSetName(veth, ifName); err != nil {
return err
}
if err := ConfigureARPCache(ifName); err != nil {
return err
}
return nil
}); err != nil {
return fmt.Errorf("error setting up interface: %s", err)
}
return nil
})
if err != nil {
return err
}
}
if err := WithNetNSLink(ns, ifName, func(veth netlink.Link) error {
newAddresses, err := AddAddresses(veth, cidrs)
if err != nil {
return err
}
if err := netlink.LinkSetUp(veth); err != nil {
return err
}
for _, ipnet := range newAddresses {
// If we don't wait for a bit here, we see the arp fail to reach the bridge.
time.Sleep(1 * time.Millisecond)
arping.GratuitousArpOverIfaceByName(ipnet.IP, ifName)
}
if withMulticastRoute {
/* Route multicast packets across the weave network.
This must come last in 'attach'. If you change this, change weavewait to match.
TODO: Add the MTU lock to prevent PMTU discovery for multicast
destinations. Without that, the kernel sets the DF flag on
multicast packets. Since RFC1122 prohibits sending of ICMP
errors for packets with multicast destinations, that causes
packets larger than the PMTU to be dropped silently. */
_, multicast, _ := net.ParseCIDR("224.0.0.0/4")
if err := AddRoute(veth, netlink.SCOPE_LINK, multicast, nil); err != nil {
return err
}
}
return nil
}); err != nil {
return err
}
return nil
}
func DetachContainer(ns netns.NsHandle, id, ifName string, cidrs []*net.IPNet) error {
return WithNetNSLink(ns, ifName, func(veth netlink.Link) error {
existingAddrs, err := netlink.AddrList(veth, netlink.FAMILY_V4)
if err != nil {
return fmt.Errorf("failed to get IP address for %q: %v", veth.Attrs().Name, err)
}
for _, ipnet := range cidrs {
if !contains(existingAddrs, ipnet) {
continue
}
if err := netlink.AddrDel(veth, &netlink.Addr{IPNet: ipnet}); err != nil {
return fmt.Errorf("failed to remove IP address from %q: %v", veth.Attrs().Name, err)
}
}
addrs, err := netlink.AddrList(veth, netlink.FAMILY_V4)
if err != nil {
return fmt.Errorf("failed to get IP address for %q: %v", veth.Attrs().Name, err)
}
if len(addrs) == 0 { // all addresses gone: remove the interface
if err := netlink.LinkDel(veth); err != nil {
return err
}
}
return nil
})
}
+2 -2
View File
@@ -9,6 +9,6 @@ to integrate Weave Net with [Kubernetes](http://kubernetes.io/).
The Docker plugin runs automatically when you `weave launch`, provided your The Docker plugin runs automatically when you `weave launch`, provided your
Docker daemon is version 1.9 or newer. Docker daemon is version 1.9 or newer.
More detail on the Docker plugin [here](http://docs.weave.works/weave/latest_release/plugin.html). More detail on the Docker plugin [here](https://www.weave.works/docs/net/latest/plugin/).
More detail on the CNI plugin [here](https://github.com/weaveworks/weave/blob/master/site/plugin/cni-plugin-how-to.md). More detail on the CNI plugin [here](https://www.weave.works/docs/net/latest/cni-plugin/).
+22 -10
View File
@@ -54,6 +54,21 @@ func (i *Ipam) ReleasePool(poolID string) error {
return nil return nil
} }
func splitPoolID(poolID string) (subnet, iprange *net.IPNet, err error) {
parts := strings.Split(poolID, "-")
if len(parts) != 3 || parts[0] != "weave" {
err = fmt.Errorf("Unrecognized pool ID: %s", poolID)
return
}
if _, subnet, err = net.ParseCIDR(parts[1]); err != nil {
return
}
if _, iprange, err = net.ParseCIDR(parts[2]); err != nil {
return
}
return
}
func (i *Ipam) RequestAddress(poolID string, address net.IP, options map[string]string) (ip *net.IPNet, _ map[string]string, err error) { func (i *Ipam) RequestAddress(poolID string, address net.IP, options map[string]string) (ip *net.IPNet, _ map[string]string, err error) {
i.logReq("RequestAddress", poolID, address, options) i.logReq("RequestAddress", poolID, address, options)
defer func() { i.logRes("RequestAddress", err, ip) }() defer func() { i.logRes("RequestAddress", err, ip) }()
@@ -62,13 +77,8 @@ func (i *Ipam) RequestAddress(poolID string, address net.IP, options map[string]
ip, err = i.weave.AllocateIP("_") ip, err = i.weave.AllocateIP("_")
return return
} }
parts := strings.Split(poolID, "-") subnet, iprange, err := splitPoolID(poolID)
if len(parts) != 3 || parts[0] != "weave" { if err != nil {
err = fmt.Errorf("Unrecognized pool ID: %s", poolID)
return
}
var subnet, iprange *net.IPNet
if _, subnet, err = net.ParseCIDR(parts[1]); err != nil {
return return
} }
if address != nil { // try to claim specific address requested if address != nil { // try to claim specific address requested
@@ -77,9 +87,6 @@ func (i *Ipam) RequestAddress(poolID string, address net.IP, options map[string]
return return
} }
} else { } else {
if _, iprange, err = net.ParseCIDR(parts[2]); err != nil {
return
}
// We are lying slightly to IPAM here: the range is not a subnet // We are lying slightly to IPAM here: the range is not a subnet
if ip, err = i.weave.AllocateIPInSubnet("_", iprange); err != nil { if ip, err = i.weave.AllocateIPInSubnet("_", iprange); err != nil {
return return
@@ -91,6 +98,11 @@ func (i *Ipam) RequestAddress(poolID string, address net.IP, options map[string]
func (i *Ipam) ReleaseAddress(poolID string, address net.IP) error { func (i *Ipam) ReleaseAddress(poolID string, address net.IP) error {
i.logReq("ReleaseAddress", poolID, address) i.logReq("ReleaseAddress", poolID, address)
if subnet, _, err := splitPoolID(poolID); err != nil {
return err
} else if address.Equal(subnet.IP) { // is it the gateway address we faked earlier?
return nil
}
return i.weave.ReleaseIPsFor(address.String()) return i.weave.ReleaseIPsFor(address.String())
} }
+36 -83
View File
@@ -5,7 +5,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net" "net"
"os"
"github.com/appc/cni/pkg/ipam" "github.com/appc/cni/pkg/ipam"
"github.com/appc/cni/pkg/skel" "github.com/appc/cni/pkg/skel"
@@ -14,6 +13,7 @@ import (
"github.com/vishvananda/netns" "github.com/vishvananda/netns"
weaveapi "github.com/weaveworks/weave/api" weaveapi "github.com/weaveworks/weave/api"
"github.com/weaveworks/weave/common" "github.com/weaveworks/weave/common"
weavenet "github.com/weaveworks/weave/net"
ipamplugin "github.com/weaveworks/weave/plugin/ipam" ipamplugin "github.com/weaveworks/weave/plugin/ipam"
) )
@@ -32,7 +32,7 @@ func NewCNIPlugin(weave *weaveapi.Client) *CNIPlugin {
func loadNetConf(bytes []byte) (*NetConf, error) { func loadNetConf(bytes []byte) (*NetConf, error) {
n := &NetConf{ n := &NetConf{
BrName: "weave", BrName: weavenet.WeaveBridgeName,
} }
if err := json.Unmarshal(bytes, n); err != nil { if err := json.Unmarshal(bytes, n); err != nil {
return nil, fmt.Errorf("failed to load netconf: %v", err) return nil, fmt.Errorf("failed to load netconf: %v", err)
@@ -53,6 +53,29 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
return fmt.Errorf("IP Masquerading functionality not supported") return fmt.Errorf("IP Masquerading functionality not supported")
} }
var result *types.Result
// Default IPAM is Weave's own
if conf.IPAM.Type == "" {
result, err = ipamplugin.NewIpam(c.weave).Allocate(args)
} else {
result, err = ipam.ExecAdd(conf.IPAM.Type, args.StdinData)
}
if err != nil {
return fmt.Errorf("unable to allocate IP address: %s", err)
}
if result.IP4 == nil {
return fmt.Errorf("IPAM plugin failed to allocate IP address")
}
// If config says nothing about routes or gateway, default one will be via the bridge
if result.IP4.Routes == nil && result.IP4.Gateway == nil {
bridgeIP, err := findBridgeIP(conf.BrName, result.IP4.IP)
if err != nil {
return err
}
result.IP4.Gateway = bridgeIP
}
ns, err := netns.GetFromPath(args.Netns) ns, err := netns.GetFromPath(args.Netns)
if err != nil { if err != nil {
return err return err
@@ -69,93 +92,36 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
id = fmt.Sprintf("%x", data) id = fmt.Sprintf("%x", data)
} }
local, err := createAndAttach(id, conf.BrName, conf.MTU) if err := weavenet.AttachContainer(ns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&result.IP4.IP}, false); err != nil {
if err != nil {
return err return err
} }
if err := weavenet.WithNetNSLink(ns, args.IfName, func(link netlink.Link) error {
cleanup := func(err error) error { return setupRoutes(link, args.IfName, result.IP4.IP, result.IP4.Gateway, result.IP4.Routes)
netlink.LinkDel(local) }); err != nil {
return err return fmt.Errorf("error setting up routes: %s", err)
}
guest, err := netlink.LinkByName(local.PeerName)
if err != nil {
return cleanup(err)
}
if err = netlink.LinkSetNsFd(guest, int(ns)); err != nil {
return cleanup(fmt.Errorf("failed to move veth to container netns: %s", err))
}
if err := netlink.LinkSetUp(local); err != nil {
return cleanup(fmt.Errorf("unable to bring veth up: %s", err))
}
var result *types.Result
// Default IPAM is Weave's own
if conf.IPAM.Type == "" {
result, err = ipamplugin.NewIpam(c.weave).Allocate(args)
} else {
result, err = ipam.ExecAdd(conf.IPAM.Type, args.StdinData)
}
if err != nil {
return cleanup(fmt.Errorf("unable to allocate IP address: %s", err))
}
if result.IP4 == nil {
return cleanup(fmt.Errorf("IPAM plugin failed to allocate IP address"))
}
// If config says nothing about routes or gateway, default one will be via the bridge
if result.IP4.Routes == nil && result.IP4.Gateway == nil {
bridgeIP, err := findBridgeIP(conf.BrName, result.IP4.IP)
if err != nil {
return cleanup(err)
}
result.IP4.Gateway = bridgeIP
}
err = common.WithNetNS(ns, func() error {
return setupGuestIP4(local.PeerName, args.IfName, result.IP4.IP, result.IP4.Gateway, result.IP4.Routes)
})
if err != nil {
return cleanup(fmt.Errorf("error setting up interface: %s", err))
} }
result.DNS = conf.DNS result.DNS = conf.DNS
return result.Print() return result.Print()
} }
func setupGuestIP4(origName, name string, ipnet net.IPNet, gw net.IP, routes []types.Route) error { func setupRoutes(link netlink.Link, name string, ipnet net.IPNet, gw net.IP, routes []types.Route) error {
guest, err := netlink.LinkByName(origName) var err error
if err != nil {
return err
}
if err = netlink.LinkSetName(guest, name); err != nil {
return err
}
if err = netlink.LinkSetUp(guest); err != nil {
return err
}
if err = common.ConfigureARPCache(name); err != nil {
return err
}
if routes == nil { // If config says nothing about routes, add a default one if routes == nil { // If config says nothing about routes, add a default one
if !ipnet.Contains(gw) { if !ipnet.Contains(gw) {
// The bridge IP is not on the same subnet; add a specific route to it // The bridge IP is not on the same subnet; add a specific route to it
gw32 := &net.IPNet{IP: gw, Mask: mask32} gw32 := &net.IPNet{IP: gw, Mask: mask32}
if err = addRoute(guest, netlink.SCOPE_LINK, gw32, nil); err != nil { if err = weavenet.AddRoute(link, netlink.SCOPE_LINK, gw32, nil); err != nil {
return err return err
} }
} }
routes = []types.Route{{Dst: zeroNetwork}} routes = []types.Route{{Dst: zeroNetwork}}
} }
if err = netlink.AddrAdd(guest, &netlink.Addr{IPNet: &ipnet}); err != nil {
return fmt.Errorf("failed to add IP address to %q: %v", name, err)
}
for _, r := range routes { for _, r := range routes {
if r.GW != nil { if r.GW != nil {
err = addRoute(guest, netlink.SCOPE_UNIVERSE, &r.Dst, r.GW) err = weavenet.AddRoute(link, netlink.SCOPE_UNIVERSE, &r.Dst, r.GW)
} else { } else {
err = addRoute(guest, netlink.SCOPE_UNIVERSE, &r.Dst, gw) err = weavenet.AddRoute(link, netlink.SCOPE_UNIVERSE, &r.Dst, gw)
} }
if err != nil { if err != nil {
return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, name, err) return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, name, err)
@@ -164,19 +130,6 @@ func setupGuestIP4(origName, name string, ipnet net.IPNet, gw net.IP, routes []t
return nil return nil
} }
func addRoute(link netlink.Link, scope netlink.Scope, dst *net.IPNet, gw net.IP) error {
err := netlink.RouteAdd(&netlink.Route{
LinkIndex: link.Attrs().Index,
Scope: scope,
Dst: dst,
Gw: gw,
})
if os.IsExist(err) { // squash duplicate route errors
err = nil
}
return err
}
func findBridgeIP(bridgeName string, subnet net.IPNet) (net.IP, error) { func findBridgeIP(bridgeName string, subnet net.IPNet) (net.IP, error) {
netdevs, err := common.GetBridgeNetDev(bridgeName) netdevs, err := common.GetBridgeNetDev(bridgeName)
if err != nil { if err != nil {
@@ -208,7 +161,7 @@ func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
return err return err
} }
defer ns.Close() defer ns.Close()
err = common.WithNetNS(ns, func() error { err = weavenet.WithNetNS(ns, func() error {
link, err := netlink.LinkByName(args.IfName) link, err := netlink.LinkByName(args.IfName)
if err != nil { if err != nil {
return err return err
+92 -80
View File
@@ -2,35 +2,44 @@ package plugin
import ( import (
"fmt" "fmt"
"strconv"
"sync" "sync"
"github.com/docker/libnetwork/drivers/remote/api" "github.com/docker/libnetwork/drivers/remote/api"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/types" "github.com/docker/libnetwork/types"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
weaveapi "github.com/weaveworks/weave/api" weaveapi "github.com/weaveworks/weave/api"
"github.com/weaveworks/weave/common" "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/common/docker" "github.com/weaveworks/weave/common/docker"
"github.com/weaveworks/weave/common/odp" weavenet "github.com/weaveworks/weave/net"
"github.com/weaveworks/weave/plugin/skel" "github.com/weaveworks/weave/plugin/skel"
) )
const ( const (
WeaveBridge = "weave" MulticastOption = "works.weave.multicast"
) )
type driver struct { type network struct {
scope string isOurs bool
noMulticastRoute bool hasMulticastRoute bool
sync.RWMutex
endpoints map[string]struct{}
} }
func New(client *docker.Client, weave *weaveapi.Client, scope string, noMulticastRoute bool) (skel.Driver, error) { type driver struct {
name string
scope string
docker *docker.Client
sync.RWMutex
networks map[string]network
}
func New(client *docker.Client, weave *weaveapi.Client, name, scope string) (skel.Driver, error) {
driver := &driver{ driver := &driver{
noMulticastRoute: noMulticastRoute, name: name,
scope: scope, scope: scope,
endpoints: make(map[string]struct{}), docker: client,
networks: make(map[string]network),
} }
_, err := NewWatcher(client, weave, driver) _, err := NewWatcher(client, weave, driver)
@@ -53,24 +62,42 @@ func (driver *driver) GetCapabilities() (*api.GetCapabilityResponse, error) {
func (driver *driver) CreateNetwork(create *api.CreateNetworkRequest) error { func (driver *driver) CreateNetwork(create *api.CreateNetworkRequest) error {
driver.logReq("CreateNetwork", create, create.NetworkID) driver.logReq("CreateNetwork", create, create.NetworkID)
_, err := driver.setupNetworkInfo(create.NetworkID, true, stringOptions(create))
return err
}
// Deal with excessively-generic way the options get decoded from JSON
func stringOptions(create *api.CreateNetworkRequest) map[string]string {
if create.Options != nil {
if data, found := create.Options[netlabel.GenericData]; found {
if options, ok := data.(map[string]interface{}); ok {
out := make(map[string]string, len(options))
for key, value := range options {
if str, ok := value.(string); ok {
out[key] = str
}
}
return out
}
}
}
return nil return nil
} }
func (driver *driver) DeleteNetwork(delete *api.DeleteNetworkRequest) error { func (driver *driver) DeleteNetwork(delreq *api.DeleteNetworkRequest) error {
driver.logReq("DeleteNetwork", delete, delete.NetworkID) driver.logReq("DeleteNetwork", delreq, delreq.NetworkID)
driver.Lock()
delete(driver.networks, delreq.NetworkID)
driver.Unlock()
return nil return nil
} }
func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.CreateEndpointResponse, error) { func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.CreateEndpointResponse, error) {
driver.logReq("CreateEndpoint", create, create.EndpointID) driver.logReq("CreateEndpoint", create, create.EndpointID)
endID := create.EndpointID
if create.Interface == nil { if create.Interface == nil {
return nil, driver.error("CreateEndpoint", "Not supported: creating an interface from within CreateEndpoint") return nil, driver.error("CreateEndpoint", "Not supported: creating an interface from within CreateEndpoint")
} }
driver.Lock()
driver.endpoints[endID] = struct{}{}
driver.Unlock()
resp := &api.CreateEndpointResponse{} resp := &api.CreateEndpointResponse{}
driver.logRes("CreateEndpoint", resp) driver.logRes("CreateEndpoint", resp)
@@ -79,19 +106,9 @@ func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.Cr
func (driver *driver) DeleteEndpoint(deleteReq *api.DeleteEndpointRequest) error { func (driver *driver) DeleteEndpoint(deleteReq *api.DeleteEndpointRequest) error {
driver.logReq("DeleteEndpoint", deleteReq, deleteReq.EndpointID) driver.logReq("DeleteEndpoint", deleteReq, deleteReq.EndpointID)
driver.Lock()
delete(driver.endpoints, deleteReq.EndpointID)
driver.Unlock()
return nil 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) { func (driver *driver) EndpointInfo(req *api.EndpointInfoRequest) (*api.EndpointInfoResponse, error) {
driver.logReq("EndpointInfo", req, req.EndpointID) driver.logReq("EndpointInfo", req, req.EndpointID)
return &api.EndpointInfoResponse{Value: map[string]interface{}{}}, nil return &api.EndpointInfoResponse{Value: map[string]interface{}{}}, nil
@@ -100,24 +117,23 @@ func (driver *driver) EndpointInfo(req *api.EndpointInfoRequest) (*api.EndpointI
func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error) { func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error) {
driver.logReq("JoinEndpoint", j, fmt.Sprintf("%s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey)) driver.logReq("JoinEndpoint", j, fmt.Sprintf("%s:%s to %s", j.NetworkID, j.EndpointID, j.SandboxKey))
local, err := createAndAttach(j.EndpointID, WeaveBridge, 0) network, err := driver.findNetworkInfo(j.NetworkID)
if err != nil { if err != nil {
return nil, driver.error("JoinEndpoint", "unable to find network info: %s", err)
}
name, peerName := vethPair(j.EndpointID)
if _, err := weavenet.CreateAndAttachVeth(name, peerName, weavenet.WeaveBridgeName, 0, false, nil); err != nil {
return nil, driver.error("JoinEndpoint", "%s", err) return nil, driver.error("JoinEndpoint", "%s", err)
} }
if err := netlink.LinkSetUp(local); err != nil {
return nil, driver.error("JoinEndpoint", "unable to bring up veth %s-%s: %s", local.Name, local.PeerName, err)
}
ifname := &api.InterfaceName{
SrcName: local.PeerName,
DstPrefix: "ethwe",
}
response := &api.JoinResponse{ response := &api.JoinResponse{
InterfaceName: ifname, InterfaceName: &api.InterfaceName{
SrcName: peerName,
DstPrefix: weavenet.VethName,
},
} }
if !driver.noMulticastRoute { if network.hasMulticastRoute {
multicastRoute := api.StaticRoute{ multicastRoute := api.StaticRoute{
Destination: "224.0.0.0/4", Destination: "224.0.0.0/4",
RouteType: types.CONNECTED, RouteType: types.CONNECTED,
@@ -128,51 +144,52 @@ func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error
return response, nil return response, nil
} }
// create and attach local name to the Weave bridge func (driver *driver) findNetworkInfo(id string) (network, error) {
func createAndAttach(id, bridgeName string, mtu int) (*netlink.Veth, error) { driver.Lock()
maybeBridge, err := netlink.LinkByName(bridgeName) network, found := driver.networks[id]
driver.Unlock()
if found {
return network, nil
}
info, err := driver.docker.NetworkInfo(id)
if err != nil { if err != nil {
return nil, fmt.Errorf(`bridge "%s" not present; did you launch weave?`, bridgeName) return network, err
} }
return driver.setupNetworkInfo(id, info.Driver == driver.name, info.Options)
}
local := vethPair(id[:5]) func (driver *driver) setupNetworkInfo(id string, isOurs bool, options map[string]string) (network, error) {
if mtu == 0 { network := network{isOurs: isOurs}
local.Attrs().MTU = maybeBridge.Attrs().MTU if isOurs {
} else { for key, value := range options {
local.Attrs().MTU = mtu switch key {
} case MulticastOption:
if err := netlink.LinkAdd(local); err != nil { if value == "" { // interpret "--opt works.weave.multicast" as "turn it on"
return nil, fmt.Errorf(`could not create veth pair %s-%s: %s`, local.Name, local.PeerName, err) network.hasMulticastRoute = true
} } else {
var err error
if network.hasMulticastRoute, err = strconv.ParseBool(value); err != nil {
return network, fmt.Errorf("unrecognized value %q for option %s", value, key)
}
switch maybeBridge.(type) { }
case *netlink.Bridge: default:
if err := netlink.LinkSetMasterByIndex(local, maybeBridge.Attrs().Index); err != nil { driver.warn("setupNetworkInfo", "unrecognized option: %s", key)
return nil, fmt.Errorf(`unable to set master of %s: %s`, local.Name, err) }
} }
case *netlink.GenericLink:
if maybeBridge.Type() != "openvswitch" {
return nil, fmt.Errorf(`device "%s" is of type "%s"`, bridgeName, maybeBridge.Type())
}
if err := odp.AddDatapathInterface(bridgeName, local.Name); err != nil {
return nil, fmt.Errorf(`failed to attach %s to device "%s": %s`, local.Name, bridgeName, err)
}
case *netlink.Device:
// Assume it's our openvswitch device, and the kernel has not been updated to report the kind.
if err := odp.AddDatapathInterface(bridgeName, local.Name); err != nil {
return nil, fmt.Errorf(`failed to attach %s to device "%s": %s`, local.Name, bridgeName, err)
}
default:
return nil, fmt.Errorf(`device "%s" is not a bridge`, bridgeName)
} }
return local, nil driver.Lock()
driver.networks[id] = network
driver.Unlock()
return network, nil
} }
func (driver *driver) LeaveEndpoint(leave *api.LeaveRequest) error { func (driver *driver) LeaveEndpoint(leave *api.LeaveRequest) error {
driver.logReq("LeaveEndpoint", leave, fmt.Sprintf("%s:%s", leave.NetworkID, leave.EndpointID)) driver.logReq("LeaveEndpoint", leave, fmt.Sprintf("%s:%s", leave.NetworkID, leave.EndpointID))
local := vethPair(leave.EndpointID[:5]) name, _ := vethPair(leave.EndpointID)
if err := netlink.LinkDel(local); err != nil { veth := &netlink.Veth{LinkAttrs: netlink.LinkAttrs{Name: name}}
if err := netlink.LinkDel(veth); err != nil {
driver.warn("LeaveEndpoint", "unable to delete veth: %s", err) driver.warn("LeaveEndpoint", "unable to delete veth: %s", err)
} }
return nil return nil
@@ -188,13 +205,8 @@ func (driver *driver) DiscoverDelete(disco *api.DiscoveryNotification) error {
return nil return nil
} }
// === func vethPair(id string) (string, string) {
return "vethwl" + id[:5], "vethwg" + id[:5]
func vethPair(suffix string) *netlink.Veth {
return &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{Name: "vethwl" + suffix},
PeerName: "vethwg" + suffix,
}
} }
// logging // logging
+12 -2
View File
@@ -4,7 +4,9 @@ import (
"fmt" "fmt"
weaveapi "github.com/weaveworks/weave/api" weaveapi "github.com/weaveworks/weave/api"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/common/docker" "github.com/weaveworks/weave/common/docker"
weavenet "github.com/weaveworks/weave/net"
) )
const ( const (
@@ -32,13 +34,21 @@ func (w *watcher) ContainerStarted(id string) {
w.driver.warn("ContainerStarted", "error inspecting container %s: %s", id, err) w.driver.warn("ContainerStarted", "error inspecting container %s: %s", id, err)
return return
} }
// check that it's on our network, via the endpointID // check that it's on our network
for _, net := range info.NetworkSettings.Networks { for _, net := range info.NetworkSettings.Networks {
if w.driver.HasEndpoint(net.EndpointID) { network, err := w.driver.findNetworkInfo(net.NetworkID)
if err != nil {
w.driver.warn("ContainerStarted", "unable to find network %s info: %s", net.NetworkID, err)
continue
}
if network.isOurs {
fqdn := fmt.Sprintf("%s.%s", info.Config.Hostname, info.Config.Domainname) fqdn := fmt.Sprintf("%s.%s", info.Config.Hostname, info.Config.Domainname)
if err := w.weave.RegisterWithDNS(id, fqdn, net.IPAddress); err != nil { if err := w.weave.RegisterWithDNS(id, fqdn, net.IPAddress); err != nil {
w.driver.warn("ContainerStarted", "unable to register %s with weaveDNS: %s", id, err) w.driver.warn("ContainerStarted", "unable to register %s with weaveDNS: %s", id, err)
} }
if err := common.ConfigureARPforVeths(info.State.Pid, weavenet.VethName); err != nil {
w.driver.warn("ContainerStarted", "unable to configure interfaces: %s", err)
}
} }
} }
} }
+12 -7
View File
@@ -7,6 +7,7 @@ import (
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
"path"
"strings" "strings"
"syscall" "syscall"
@@ -42,7 +43,7 @@ func main() {
flag.StringVar(&logLevel, "log-level", "info", "logging level (debug, info, warning, error)") flag.StringVar(&logLevel, "log-level", "info", "logging level (debug, info, warning, error)")
flag.StringVar(&address, "socket", "/run/docker/plugins/weave.sock", "socket on which to listen") flag.StringVar(&address, "socket", "/run/docker/plugins/weave.sock", "socket on which to listen")
flag.StringVar(&meshAddress, "meshsocket", "/run/docker/plugins/weavemesh.sock", "socket on which to listen in mesh mode") flag.StringVar(&meshAddress, "meshsocket", "/run/docker/plugins/weavemesh.sock", "socket on which to listen in mesh mode")
flag.BoolVar(&noMulticastRoute, "no-multicast-route", false, "do not add a multicast route to network endpoints") flag.BoolVar(&noMulticastRoute, "no-multicast-route", false, "deprecated (this is now the default)")
flag.Parse() flag.Parse()
@@ -73,18 +74,21 @@ func main() {
} }
Log.Println("Weave plugin", version, "Command line options:", os.Args[1:]) Log.Println("Weave plugin", version, "Command line options:", os.Args[1:])
if noMulticastRoute {
Log.Warning("--no-multicast-route option has been removed; multicast is off by default")
}
Log.Info(dockerClient.Info()) Log.Info(dockerClient.Info())
err = run(dockerClient, weave, address, meshAddress, noMulticastRoute) err = run(dockerClient, weave, address, meshAddress)
if err != nil { if err != nil {
Log.Fatal(err) Log.Fatal(err)
} }
} }
func run(dockerClient *docker.Client, weave *weaveapi.Client, address, meshAddress string, noMulticastRoute bool) error { func run(dockerClient *docker.Client, weave *weaveapi.Client, address, meshAddress string) error {
endChan := make(chan error, 1) endChan := make(chan error, 1)
if address != "" { if address != "" {
globalListener, err := listenAndServe(dockerClient, weave, address, noMulticastRoute, endChan, "global", false) globalListener, err := listenAndServe(dockerClient, weave, address, endChan, "global", false)
if err != nil { if err != nil {
return err return err
} }
@@ -92,7 +96,7 @@ func run(dockerClient *docker.Client, weave *weaveapi.Client, address, meshAddre
defer globalListener.Close() defer globalListener.Close()
} }
if meshAddress != "" { if meshAddress != "" {
meshListener, err := listenAndServe(dockerClient, weave, meshAddress, noMulticastRoute, endChan, "local", true) meshListener, err := listenAndServe(dockerClient, weave, meshAddress, endChan, "local", true)
if err != nil { if err != nil {
return err return err
} }
@@ -117,8 +121,9 @@ func run(dockerClient *docker.Client, weave *weaveapi.Client, address, meshAddre
} }
} }
func listenAndServe(dockerClient *docker.Client, weave *weaveapi.Client, address string, noMulticastRoute bool, endChan chan<- error, scope string, withIpam bool) (net.Listener, error) { func listenAndServe(dockerClient *docker.Client, weave *weaveapi.Client, address string, endChan chan<- error, scope string, withIpam bool) (net.Listener, error) {
d, err := netplugin.New(dockerClient, weave, scope, noMulticastRoute) name := strings.TrimSuffix(path.Base(address), ".sock")
d, err := netplugin.New(dockerClient, weave, name, scope)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+63
View File
@@ -0,0 +1,63 @@
package main
import (
"sync/atomic"
"syscall"
"time"
"github.com/weaveworks/go-checkpoint"
)
var checker *checkpoint.Checker
var newVersion atomic.Value
const (
updateCheckPeriod = 6 * time.Hour
)
func checkForUpdates(dockerVersion string, network string) {
newVersion.Store("")
handleResponse := func(r *checkpoint.CheckResponse, err error) {
if err != nil {
Log.Printf("Error checking version: %v", err)
return
}
if r.Outdated {
newVersion.Store(r.CurrentVersion)
Log.Printf("Weave version %s is available; please update at %s",
r.CurrentVersion, r.CurrentDownloadURL)
}
}
var uts syscall.Utsname
syscall.Uname(&uts)
flags := map[string]string{
"docker-version": dockerVersion,
"kernel-version": charsToString(uts.Release[:]),
}
if network != "" {
flags["network"] = network
}
// Start background version checking
params := checkpoint.CheckParams{
Product: "weave-net",
Version: version,
SignatureFile: "",
Flags: flags,
}
checker = checkpoint.CheckInterval(&params, updateCheckPeriod, handleResponse)
}
func charsToString(ca []int8) string {
s := make([]byte, len(ca))
i := 0
for ; i < len(ca); i++ {
if ca[i] == 0 {
break
}
s[i] = uint8(ca[i])
}
return string(s[:i])
}
+66 -68
View File
@@ -9,19 +9,18 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"sync/atomic"
"time" "time"
"github.com/davecheney/profile"
"github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/mflag"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/pkg/profile"
"github.com/weaveworks/mesh" "github.com/weaveworks/mesh"
"github.com/weaveworks/go-checkpoint"
"github.com/weaveworks/weave/common" "github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/common/docker" "github.com/weaveworks/weave/common/docker"
"github.com/weaveworks/weave/db" "github.com/weaveworks/weave/db"
"github.com/weaveworks/weave/ipam" "github.com/weaveworks/weave/ipam"
"github.com/weaveworks/weave/ipam/tracker"
"github.com/weaveworks/weave/nameserver" "github.com/weaveworks/weave/nameserver"
weavenet "github.com/weaveworks/weave/net" weavenet "github.com/weaveworks/weave/net"
"github.com/weaveworks/weave/net/address" "github.com/weaveworks/weave/net/address"
@@ -29,8 +28,6 @@ import (
) )
var version = "(unreleased version)" var version = "(unreleased version)"
var checker *checkpoint.Checker
var newVersion atomic.Value
var Log = common.Log var Log = common.Log
@@ -49,34 +46,7 @@ type dnsConfig struct {
TTL int TTL int
ClientTimeout time.Duration ClientTimeout time.Duration
EffectiveListenAddress string EffectiveListenAddress string
} ResolvConf string
const (
updateCheckPeriod = 6 * time.Hour
)
func checkForUpdates() {
newVersion.Store("")
handleResponse := func(r *checkpoint.CheckResponse, err error) {
if err != nil {
Log.Printf("Error checking version: %v", err)
return
}
if r.Outdated {
newVersion.Store(r.CurrentVersion)
Log.Printf("Weave version %s is available; please update at %s",
r.CurrentVersion, r.CurrentDownloadURL)
}
}
// Start background version checking
params := checkpoint.CheckParams{
Product: "weave-net",
Version: version,
SignatureFile: "",
}
checker = checkpoint.CheckInterval(&params, updateCheckPeriod, handleResponse)
} }
func (c *ipamConfig) Enabled() bool { func (c *ipamConfig) Enabled() bool {
@@ -151,6 +121,7 @@ func main() {
config mesh.Config config mesh.Config
networkConfig weave.NetworkConfig networkConfig weave.NetworkConfig
protocolMinVersion int protocolMinVersion int
resume bool
ifaceName string ifaceName string
routerName string routerName string
nickName string nickName string
@@ -169,6 +140,7 @@ func main() {
datapathName string datapathName string
trustedSubnetStr string trustedSubnetStr string
dbPrefix string dbPrefix string
isAWSVPC bool
defaultDockerHost = "unix:///var/run/docker.sock" defaultDockerHost = "unix:///var/run/docker.sock"
) )
@@ -181,6 +153,7 @@ func main() {
mflag.StringVar(&config.Host, []string{"-host"}, "", "router host") mflag.StringVar(&config.Host, []string{"-host"}, "", "router host")
mflag.IntVar(&config.Port, []string{"#port", "-port"}, mesh.Port, "router port") mflag.IntVar(&config.Port, []string{"#port", "-port"}, mesh.Port, "router port")
mflag.IntVar(&protocolMinVersion, []string{"-min-protocol-version"}, mesh.ProtocolMinVersion, "minimum weave protocol version") mflag.IntVar(&protocolMinVersion, []string{"-min-protocol-version"}, mesh.ProtocolMinVersion, "minimum weave protocol version")
mflag.BoolVar(&resume, []string{"-resume"}, false, "resume connections to previous peers")
mflag.StringVar(&ifaceName, []string{"#iface", "-iface"}, "", "name of interface to capture/inject from (disabled if blank)") mflag.StringVar(&ifaceName, []string{"#iface", "-iface"}, "", "name of interface to capture/inject from (disabled if blank)")
mflag.StringVar(&routerName, []string{"#name", "-name"}, "", "name of router (defaults to MAC of interface)") mflag.StringVar(&routerName, []string{"#name", "-name"}, "", "name of router (defaults to MAC of interface)")
mflag.StringVar(&nickName, []string{"#nickname", "-nickname"}, "", "nickname of peer (defaults to hostname)") mflag.StringVar(&nickName, []string{"#nickname", "-nickname"}, "", "nickname of peer (defaults to hostname)")
@@ -203,9 +176,11 @@ func main() {
mflag.IntVar(&dnsConfig.TTL, []string{"-dns-ttl"}, nameserver.DefaultTTL, "TTL for DNS request from our domain") mflag.IntVar(&dnsConfig.TTL, []string{"-dns-ttl"}, nameserver.DefaultTTL, "TTL for DNS request from our domain")
mflag.DurationVar(&dnsConfig.ClientTimeout, []string{"-dns-fallback-timeout"}, nameserver.DefaultClientTimeout, "timeout for fallback DNS requests") mflag.DurationVar(&dnsConfig.ClientTimeout, []string{"-dns-fallback-timeout"}, nameserver.DefaultClientTimeout, "timeout for fallback DNS requests")
mflag.StringVar(&dnsConfig.EffectiveListenAddress, []string{"-dns-effective-listen-address"}, "", "address DNS will actually be listening, after Docker port mapping") mflag.StringVar(&dnsConfig.EffectiveListenAddress, []string{"-dns-effective-listen-address"}, "", "address DNS will actually be listening, after Docker port mapping")
mflag.StringVar(&dnsConfig.ResolvConf, []string{"-resolv-conf"}, "", "path to resolver configuration for fallback DNS lookups")
mflag.StringVar(&datapathName, []string{"-datapath"}, "", "ODP datapath name") mflag.StringVar(&datapathName, []string{"-datapath"}, "", "ODP datapath name")
mflag.StringVar(&trustedSubnetStr, []string{"-trusted-subnets"}, "", "comma-separated list of trusted subnets in CIDR notation") mflag.StringVar(&trustedSubnetStr, []string{"-trusted-subnets"}, "", "comma-separated list of trusted subnets in CIDR notation")
mflag.StringVar(&dbPrefix, []string{"-db-prefix"}, "/weavedb/weave", "pathname/prefix of filename to store data") mflag.StringVar(&dbPrefix, []string{"-db-prefix"}, "/weavedb/weave", "pathname/prefix of filename to store data")
mflag.BoolVar(&isAWSVPC, []string{"#awsvpc", "-awsvpc"}, false, "use AWS VPC for routing")
// crude way of detecting that we probably have been started in a // crude way of detecting that we probably have been started in a
// container, with `weave launch` --> suppress misleading paths in // container, with `weave launch` --> suppress misleading paths in
@@ -218,6 +193,9 @@ func main() {
mflag.Parse() mflag.Parse()
peers = mflag.Args() peers = mflag.Args()
if resume && len(peers) > 0 {
Log.Fatalf("You must not specify an initial peer list in conjuction with --resume")
}
common.SetLogLevel(logLevel) common.SetLogLevel(logLevel)
@@ -228,13 +206,8 @@ func main() {
Log.Println("Command line options:", options()) Log.Println("Command line options:", options())
checkForUpdates()
if prof != "" { if prof != "" {
p := *profile.CPUProfile defer profile.Start(profile.CPUProfile, profile.ProfilePath(prof), profile.NoShutdownHook).Stop()
p.ProfilePath = prof
p.NoShutdownHook = true
defer profile.Start(&p).Stop()
} }
if protocolMinVersion < mesh.ProtocolMinVersion || protocolMinVersion > mesh.ProtocolMaxVersion { if protocolMinVersion < mesh.ProtocolMinVersion || protocolMinVersion > mesh.ProtocolMaxVersion {
@@ -248,7 +221,7 @@ func main() {
networkConfig.PacketLogging = nopPacketLogging{} networkConfig.PacketLogging = nopPacketLogging{}
} }
overlay, bridge := createOverlay(datapathName, ifaceName, config.Host, config.Port, bufSzMB) overlay, bridge := createOverlay(datapathName, ifaceName, isAWSVPC, config.Host, config.Port, bufSzMB)
networkConfig.Bridge = bridge networkConfig.Bridge = bridge
name := peerName(routerName, bridge.Interface()) name := peerName(routerName, bridge.Interface())
@@ -263,6 +236,10 @@ func main() {
config.TrustedSubnets = parseTrustedSubnets(trustedSubnetStr) config.TrustedSubnets = parseTrustedSubnets(trustedSubnetStr)
config.PeerDiscovery = !noDiscovery config.PeerDiscovery = !noDiscovery
if isAWSVPC && len(config.Password) > 0 {
Log.Fatalf("--awsvpc mode is not compatible with the --password option")
}
db, err := db.NewBoltDB(dbPrefix + "data.db") db, err := db.NewBoltDB(dbPrefix + "data.db")
checkFatal(err) checkFatal(err)
defer db.Close() defer db.Close()
@@ -270,7 +247,7 @@ func main() {
router := weave.NewNetworkRouter(config, networkConfig, name, nickName, overlay, db) router := weave.NewNetworkRouter(config, networkConfig, name, nickName, overlay, db)
Log.Println("Our name is", router.Ourself) Log.Println("Our name is", router.Ourself)
if peers, err = router.InitialPeers(peers); err != nil { if peers, err = router.InitialPeers(resume, peers); err != nil {
Log.Fatal("Unable to get initial peer set: ", err) Log.Fatal("Unable to get initial peer set: ", err)
} }
@@ -284,6 +261,13 @@ func main() {
} }
dockerCli = dc dockerCli = dc
} }
network := ""
if isAWSVPC {
network = "awsvpc"
}
checkForUpdates(dockerCli.DockerVersion(), network)
observeContainers := func(o docker.ContainerObserver) { observeContainers := func(o docker.ContainerObserver) {
if dockerCli != nil { if dockerCli != nil {
if err := dockerCli.AddObserver(o); err != nil { if err := dockerCli.AddObserver(o); err != nil {
@@ -298,13 +282,23 @@ func main() {
var ( var (
allocator *ipam.Allocator allocator *ipam.Allocator
defaultSubnet address.CIDR defaultSubnet address.CIDR
trackerName string
) )
if ipamConfig.Enabled() { if ipamConfig.Enabled() {
allocator, defaultSubnet = createAllocator(router, ipamConfig, db, isKnownPeer) var t tracker.LocalRangeTracker
if isAWSVPC {
Log.Infoln("Creating AWSVPC LocalRangeTracker")
t, err = tracker.NewAWSVPCTracker()
if err != nil {
Log.Fatalf("Cannot create AWSVPC LocalRangeTracker: %s", err)
}
trackerName = "awsvpc"
}
allocator, defaultSubnet = createAllocator(router, ipamConfig, db, t, isKnownPeer)
observeContainers(allocator) observeContainers(allocator)
ids, err := dockerCli.AllContainerIDs() ids, err := dockerCli.AllContainerIDs()
checkFatal(err) checkFatal(err)
allocator.AllContainerIDs(ids) allocator.PruneOwned(ids)
} }
var ( var (
@@ -331,16 +325,16 @@ func main() {
if httpAddr != "" { if httpAddr != "" {
muxRouter := mux.NewRouter() muxRouter := mux.NewRouter()
if allocator != nil { if allocator != nil {
allocator.HandleHTTP(muxRouter, defaultSubnet, dockerCli) allocator.HandleHTTP(muxRouter, defaultSubnet, trackerName, dockerCli)
} }
if ns != nil { if ns != nil {
ns.HandleHTTP(muxRouter, dockerCli) ns.HandleHTTP(muxRouter, dockerCli)
} }
router.HandleHTTP(muxRouter) router.HandleHTTP(muxRouter)
HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver) HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver)
http.Handle("/", muxRouter) http.Handle("/", common.LoggingHTTPHandler(muxRouter))
Log.Println("Listening for HTTP control messages on", httpAddr) Log.Println("Listening for HTTP control messages on", httpAddr)
go listenAndServeHTTP(httpAddr, muxRouter) go listenAndServeHTTP(httpAddr)
} }
common.SignalHandlerLoop(router) common.SignalHandlerLoop(router)
@@ -386,10 +380,18 @@ func (nopPacketLogging) LogPacket(string, weave.PacketKey) {
func (nopPacketLogging) LogForwardPacket(string, weave.ForwardPacketKey) { func (nopPacketLogging) LogForwardPacket(string, weave.ForwardPacketKey) {
} }
func createOverlay(datapathName string, ifaceName string, host string, port int, bufSzMB int) (weave.NetworkOverlay, weave.Bridge) { func createOverlay(datapathName string, ifaceName string, isAWSVPC bool, host string, port int, bufSzMB int) (weave.NetworkOverlay, weave.Bridge) {
overlay := weave.NewOverlaySwitch() overlay := weave.NewOverlaySwitch()
var bridge weave.Bridge var bridge weave.Bridge
var ignoreSleeve bool
switch { switch {
case isAWSVPC:
vpc := weave.NewAWSVPC()
overlay.Add("awsvpc", vpc)
bridge = weave.NullBridge{}
// Currently, we do not support any overlay with AWSVPC
ignoreSleeve = true
case datapathName != "" && ifaceName != "": case datapathName != "" && ifaceName != "":
Log.Fatal("At most one of --datapath and --iface must be specified.") Log.Fatal("At most one of --datapath and --iface must be specified.")
case datapathName != "": case datapathName != "":
@@ -407,30 +409,23 @@ func createOverlay(datapathName string, ifaceName string, host string, port int,
default: default:
bridge = weave.NullBridge{} bridge = weave.NullBridge{}
} }
sleeve := weave.NewSleeveOverlay(host, port)
overlay.Add("sleeve", sleeve) if !ignoreSleeve {
overlay.SetCompatOverlay(sleeve) sleeve := weave.NewSleeveOverlay(host, port)
overlay.Add("sleeve", sleeve)
overlay.SetCompatOverlay(sleeve)
}
return overlay, bridge return overlay, bridge
} }
func parseAndCheckCIDR(cidrStr string) address.CIDR { func createAllocator(router *weave.NetworkRouter, config ipamConfig, db db.DB, track tracker.LocalRangeTracker, isKnownPeer func(mesh.PeerName) bool) (*ipam.Allocator, address.CIDR) {
cidr, err := address.ParseCIDR(cidrStr) ipRange, err := ipam.ParseCIDRSubnet(config.IPRangeCIDR)
checkFatal(err) checkFatal(err)
if !cidr.IsSubnet() {
Log.Fatalf("Invalid allocation range %s - bits after network prefix are not all zero", cidrStr)
}
if cidr.Size() < ipam.MinSubnetSize {
Log.Fatalf("Allocation range smaller than minimum size %d: %s", ipam.MinSubnetSize, cidrStr)
}
return cidr
}
func createAllocator(router *weave.NetworkRouter, config ipamConfig, db db.DB, isKnownPeer func(mesh.PeerName) bool) (*ipam.Allocator, address.CIDR) {
ipRange := parseAndCheckCIDR(config.IPRangeCIDR)
defaultSubnet := ipRange defaultSubnet := ipRange
if config.IPSubnetCIDR != "" { if config.IPSubnetCIDR != "" {
defaultSubnet = parseAndCheckCIDR(config.IPSubnetCIDR) defaultSubnet, err = ipam.ParseCIDRSubnet(config.IPSubnetCIDR)
checkFatal(err)
if !ipRange.Range().Overlaps(defaultSubnet.Range()) { if !ipRange.Range().Overlaps(defaultSubnet.Range()) {
Log.Fatalf("IP address allocation default subnet %s does not overlap with allocation range %s", defaultSubnet, ipRange) Log.Fatalf("IP address allocation default subnet %s does not overlap with allocation range %s", defaultSubnet, ipRange)
} }
@@ -441,17 +436,19 @@ func createAllocator(router *weave.NetworkRouter, config ipamConfig, db db.DB, i
OurUID: router.Ourself.Peer.UID, OurUID: router.Ourself.Peer.UID,
OurNickname: router.Ourself.Peer.NickName, OurNickname: router.Ourself.Peer.NickName,
Seed: config.SeedPeerNames, Seed: config.SeedPeerNames,
Universe: ipRange.Range(), Universe: ipRange,
IsObserver: config.Observer, IsObserver: config.Observer,
Quorum: func() uint { return determineQuorum(config.PeerCount, router) }, Quorum: func() uint { return determineQuorum(config.PeerCount, router) },
Db: db, Db: db,
IsKnownPeer: isKnownPeer, IsKnownPeer: isKnownPeer,
Tracker: track,
} }
allocator := ipam.NewAllocator(c) allocator := ipam.NewAllocator(c)
allocator.SetInterfaces(router.NewGossip("IPallocation", allocator)) allocator.SetInterfaces(router.NewGossip("IPallocation", allocator))
allocator.Start() allocator.Start()
router.Peers.OnGC(func(peer *mesh.Peer) { allocator.PeerGone(peer.Name) })
return allocator, defaultSubnet return allocator, defaultSubnet
} }
@@ -460,8 +457,9 @@ func createDNSServer(config dnsConfig, router *mesh.Router, isKnownPeer func(mes
ns := nameserver.New(router.Ourself.Peer.Name, config.Domain, isKnownPeer) ns := nameserver.New(router.Ourself.Peer.Name, config.Domain, isKnownPeer)
router.Peers.OnGC(func(peer *mesh.Peer) { ns.PeerGone(peer.Name) }) router.Peers.OnGC(func(peer *mesh.Peer) { ns.PeerGone(peer.Name) })
ns.SetGossip(router.NewGossip("nameserver", ns)) ns.SetGossip(router.NewGossip("nameserver", ns))
upstream := nameserver.NewUpstream(config.ResolvConf, config.EffectiveListenAddress)
dnsserver, err := nameserver.NewDNSServer(ns, config.Domain, config.ListenAddress, dnsserver, err := nameserver.NewDNSServer(ns, config.Domain, config.ListenAddress,
config.EffectiveListenAddress, uint32(config.TTL), config.ClientTimeout) upstream, uint32(config.TTL), config.ClientTimeout)
if err != nil { if err != nil {
Log.Fatal("Unable to start dns server: ", err) Log.Fatal("Unable to start dns server: ", err)
} }
@@ -552,7 +550,7 @@ func parsePeerNames(s string) ([]mesh.PeerName, error) {
return peerNames, nil return peerNames, nil
} }
func listenAndServeHTTP(httpAddr string, muxRouter *mux.Router) { func listenAndServeHTTP(httpAddr string) {
protocol := "tcp" protocol := "tcp"
if strings.HasPrefix(httpAddr, "/") { if strings.HasPrefix(httpAddr, "/") {
os.Remove(httpAddr) // in case it's there from last time os.Remove(httpAddr) // in case it's there from last time
+1 -1
View File
@@ -27,7 +27,7 @@ func containerAddrs(args []string) error {
} }
for _, netDev := range netDevs { for _, netDev := range netDevs {
fmt.Printf("%12s %s", containerID, netDev.MAC.String()) fmt.Printf("%12s %s %s", containerID, netDev.Name, netDev.MAC.String())
for _, cidr := range netDev.CIDRs { for _, cidr := range netDev.CIDRs {
prefixLength, _ := cidr.Mask.Size() prefixLength, _ := cidr.Mask.Size()
fmt.Printf(" %v/%v", cidr.IP, prefixLength) fmt.Printf(" %v/%v", cidr.IP, prefixLength)
+111
View File
@@ -0,0 +1,111 @@
package main
import (
"fmt"
"net"
"strconv"
"syscall"
docker "github.com/fsouza/go-dockerclient"
"github.com/vishvananda/netns"
weavenet "github.com/weaveworks/weave/net"
)
func attach(args []string) error {
if len(args) < 4 {
cmdUsage("attach-container", "[--no-multicast-route] [--keep-tx-on] <container-id> <bridge-name> <mtu> <cidr>...")
}
keepTXOn := false
withMulticastRoute := true
for i := 0; i < len(args); {
switch args[i] {
case "--no-multicast-route":
withMulticastRoute = false
args = append(args[:i], args[i+1:]...)
case "--keep-tx-on":
keepTXOn = true
args = append(args[:i], args[i+1:]...)
default:
i++
}
}
pid, nsContainer, err := containerPidAndNs(args[0])
if err != nil {
return err
}
if nsHost, err := netns.GetFromPid(1); err != nil {
return fmt.Errorf("unable to open host namespace: %s", err)
} else if nsHost.Equal(nsContainer) {
return fmt.Errorf("Container is running in the host network namespace, and therefore cannot be\nconnected to weave. Perhaps the container was started with --net=host.")
}
mtu, err := strconv.Atoi(args[2])
if err != nil && args[3] != "" {
return fmt.Errorf("unable to parse mtu %q: %s", args[2], err)
}
cidrs, err := parseCIDRs(args[3:])
if err != nil {
return err
}
err = weavenet.AttachContainer(nsContainer, fmt.Sprint(pid), weavenet.VethName, args[1], mtu, withMulticastRoute, cidrs, keepTXOn)
// If we detected an error but the container has died, tell the user that instead.
if err != nil && !processExists(pid) {
err = fmt.Errorf("Container %s died", args[0])
}
return err
}
func containerPidAndNs(containerID string) (int, netns.NsHandle, error) {
c, err := docker.NewVersionedClientFromEnv("1.18")
if err != nil {
return 0, 0, fmt.Errorf("unable to connect to docker: %s", err)
}
container, err := c.InspectContainer(containerID)
if err != nil {
return 0, 0, fmt.Errorf("unable to inspect container %s: %s", containerID, err)
}
if container.State.Pid == 0 {
return 0, 0, fmt.Errorf("container %s not running", containerID)
}
ns, err := netns.GetFromPid(container.State.Pid)
if err != nil {
return 0, 0, fmt.Errorf("unable to open namespace for container %s: %s", containerID, err)
}
return container.State.Pid, ns, nil
}
func processExists(pid int) bool {
err := syscall.Kill(pid, syscall.Signal(0))
return err == nil || err == syscall.EPERM
}
func parseCIDRs(args []string) (cidrs []*net.IPNet, err error) {
for _, ipstr := range args {
ip, ipnet, err := net.ParseCIDR(ipstr)
if err != nil {
return nil, err
}
ipnet.IP = ip // we want the specific IP plus the mask
cidrs = append(cidrs, ipnet)
}
return
}
func detach(args []string) error {
if len(args) < 2 {
cmdUsage("detach-container", "<container-id> <cidr>...")
}
_, ns, err := containerPidAndNs(args[0])
if err != nil {
return err
}
cidrs, err := parseCIDRs(args[1:])
if err != nil {
return err
}
return weavenet.DetachContainer(ns, args[0], weavenet.VethName, cidrs)
}
+13
View File
@@ -31,6 +31,19 @@ func deleteDatapath(args []string) error {
return odp.DeleteDatapath(args[0]) return odp.DeleteDatapath(args[0])
} }
// Checks whether a datapath can be created by actually creating and destroying it
func checkDatapath(args []string) error {
if len(args) != 1 {
cmdUsage("check-datapath", "<datapath>")
}
if err := createDatapath(args); err != nil {
return err
}
return odp.DeleteDatapath(args[0])
}
func addDatapathInterface(args []string) error { func addDatapathInterface(args []string) error {
if len(args) != 2 { if len(args) != 2 {
cmdUsage("add-datapath-interface", "<datapath> <interface>") cmdUsage("add-datapath-interface", "<datapath> <interface>")
+6 -2
View File
@@ -26,7 +26,12 @@ func dockerTLSArgs(args []string) error {
continue continue
} }
if comm, err := ioutil.ReadFile(filepath.Join(procRoot, dirName, "comm")); err != nil || string(comm) != "docker\n" { isDaemon := false
if comm, err := ioutil.ReadFile(filepath.Join(procRoot, dirName, "comm")); err != nil {
continue
} else if string(comm) == "dockerd\n" {
isDaemon = true
} else if string(comm) != "docker\n" {
continue continue
} }
@@ -35,7 +40,6 @@ func dockerTLSArgs(args []string) error {
continue continue
} }
isDaemon := false
tlsArgs := []string{} tlsArgs := []string{}
args := bytes.Split(cmdline, []byte{'\000'}) args := bytes.Split(cmdline, []byte{'\000'})
for i := 0; i < len(args); i++ { for i := 0; i < len(args); i++ {
+3
View File
@@ -15,10 +15,13 @@ func init() {
"docker-tls-args": dockerTLSArgs, "docker-tls-args": dockerTLSArgs,
"create-datapath": createDatapath, "create-datapath": createDatapath,
"delete-datapath": deleteDatapath, "delete-datapath": deleteDatapath,
"check-datapath": checkDatapath,
"add-datapath-interface": addDatapathInterface, "add-datapath-interface": addDatapathInterface,
"create-plugin-network": createPluginNetwork, "create-plugin-network": createPluginNetwork,
"remove-plugin-network": removePluginNetwork, "remove-plugin-network": removePluginNetwork,
"container-addrs": containerAddrs, "container-addrs": containerAddrs,
"attach-container": attach,
"detach-container": detach,
} }
} }
+16 -1
View File
@@ -4,7 +4,9 @@ package main
import ( import (
"fmt" "fmt"
"github.com/fsouza/go-dockerclient" docker "github.com/fsouza/go-dockerclient"
"github.com/weaveworks/weave/plugin/net"
) )
func createPluginNetwork(args []string) error { func createPluginNetwork(args []string) error {
@@ -23,6 +25,7 @@ func createPluginNetwork(args []string) error {
CheckDuplicate: true, CheckDuplicate: true,
Driver: driverName, Driver: driverName,
IPAM: docker.IPAMOptions{Driver: driverName}, IPAM: docker.IPAMOptions{Driver: driverName},
Options: map[string]interface{}{plugin.MulticastOption: "true"},
}) })
if err != docker.ErrNetworkAlreadyExists && err != nil { if err != docker.ErrNetworkAlreadyExists && err != nil {
// Despite appearances to the contrary, CreateNetwork does // Despite appearances to the contrary, CreateNetwork does
@@ -47,6 +50,18 @@ func removePluginNetwork(args []string) error {
} }
err = d.RemoveNetwork(networkName) err = d.RemoveNetwork(networkName)
if _, ok := err.(*docker.NoSuchNetwork); !ok && err != nil { if _, ok := err.(*docker.NoSuchNetwork); !ok && err != nil {
if info, err2 := d.NetworkInfo(networkName); err2 == nil {
if len(info.Containers) > 0 {
containers := ""
for container := range info.Containers {
containers += fmt.Sprintf(" %.12s ", container)
}
return fmt.Errorf(`WARNING: the following containers are still attached to network %q:
%s
Docker operations involving those containers may pause or fail
while Weave is not running`, networkName, containers)
}
}
return fmt.Errorf("unable to remove network: %s", err) return fmt.Errorf("unable to remove network: %s", err)
} }
return nil return nil
+1 -1
View File
@@ -7,6 +7,6 @@ import (
) )
func checkNetwork() error { func checkNetwork() error {
_, err := weavenet.EnsureInterface("ethwe") _, err := weavenet.EnsureInterface(weavenet.VethName)
return err return err
} }
+1 -1
View File
@@ -7,6 +7,6 @@ import (
) )
func checkNetwork() error { func checkNetwork() error {
_, err := weavenet.EnsureInterfaceAndMcastRoute("ethwe") _, err := weavenet.EnsureInterfaceAndMcastRoute(weavenet.VethName)
return err return err
} }
+59 -16
View File
@@ -20,6 +20,7 @@ import (
"github.com/weaveworks/weave/common" "github.com/weaveworks/weave/common"
weavedocker "github.com/weaveworks/weave/common/docker" weavedocker "github.com/weaveworks/weave/common/docker"
weavenet "github.com/weaveworks/weave/net" weavenet "github.com/weaveworks/weave/net"
"github.com/weaveworks/weave/net/address"
) )
const ( const (
@@ -103,7 +104,7 @@ func (proxy *Proxy) attachWithRetry(id string) {
j := &attachJob{id: id, tryInterval: initialInterval} j := &attachJob{id: id, tryInterval: initialInterval}
j.timer = time.AfterFunc(time.Duration(0), func() { j.timer = time.AfterFunc(time.Duration(0), func() {
if err := proxy.attach(id, false); err != nil { if err := proxy.attach(id); err != nil {
// The delay at the nth retry is a random value in the range // 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). // [i-i/2,i+i/2], where i = initialInterval * 1.5^(n-1).
j.timer.Reset(j.tryInterval) j.timer.Reset(j.tryInterval)
@@ -383,7 +384,27 @@ func (proxy *Proxy) listen(protoAndAddr string) (net.Listener, string, error) {
// weavedocker.ContainerObserver interface // weavedocker.ContainerObserver interface
func (proxy *Proxy) ContainerStarted(ident string) { func (proxy *Proxy) ContainerStarted(ident string) {
proxy.notifyWaiters(ident, proxy.attach(ident, true)) err := proxy.attach(ident)
if err != nil {
var e error
// attach failed: if we have a request waiting on the start, kill the container,
// otherwise assume it is a Docker-initated restart and kill the process inside.
if proxy.waitChan(ident) != nil {
e = proxy.client.KillContainer(docker.KillContainerOptions{ID: ident})
} else {
var c *docker.Container
if c, e = proxy.client.InspectContainer(ident); e == nil {
var process *os.Process
if process, e = os.FindProcess(c.State.Pid); e == nil {
e = process.Kill()
}
}
}
if e != nil {
Log.Warningf("Error killing %s: %s", ident, e)
}
}
proxy.notifyWaiters(ident, err)
} }
func containerShouldAttach(container *docker.Container) bool { func containerShouldAttach(container *docker.Container) bool {
@@ -438,18 +459,20 @@ func (proxy *Proxy) waitForStart(r *http.Request) error {
return nil return nil
} }
// If some other operation is waiting for a container to start, join in the wait func (proxy *Proxy) waitChan(ident string) chan error {
func (proxy *Proxy) waitForStartByIdent(ident string) error {
var ch chan error
proxy.Lock() proxy.Lock()
defer proxy.Unlock()
for _, wait := range proxy.waiters { for _, wait := range proxy.waiters {
if ident == wait.ident && !wait.done { if ident == wait.ident && !wait.done {
ch = wait.ch return wait.ch
break
} }
} }
proxy.Unlock() return nil
if ch != nil { }
// If some other operation is waiting for a container to start, join in the wait
func (proxy *Proxy) waitForStartByIdent(ident string) error {
if ch := proxy.waitChan(ident); ch != nil {
Log.Debugf("Wait for start of container %s", ident) Log.Debugf("Wait for start of container %s", ident)
return <-ch return <-ch
} }
@@ -461,7 +484,7 @@ func (proxy *Proxy) ContainerDestroyed(ident string) {}
// Check if this container needs to be attached, if so then attach it, // Check if this container needs to be attached, if so then attach it,
// and return nil on success or not needed. // and return nil on success or not needed.
func (proxy *Proxy) attach(containerID string, orDie bool) error { func (proxy *Proxy) attach(containerID string) error {
container, err := proxy.client.InspectContainer(containerID) container, err := proxy.client.InspectContainer(containerID)
if err != nil { if err != nil {
if _, ok := err.(*docker.NoSuchContainer); !ok { if _, ok := err.(*docker.NoSuchContainer); !ok {
@@ -483,6 +506,10 @@ func (proxy *Proxy) attach(containerID string, orDie bool) error {
return nil return nil
} }
Log.Infof("Attaching container %s with WEAVE_CIDR \"%s\" to weave network", container.ID, strings.Join(cidrs, " ")) Log.Infof("Attaching container %s with WEAVE_CIDR \"%s\" to weave network", container.ID, strings.Join(cidrs, " "))
if err := validateCIDRs(cidrs); err != nil {
return err
}
args := []string{"attach"} args := []string{"attach"}
args = append(args, cidrs...) args = append(args, cidrs...)
if !proxy.NoRewriteHosts { if !proxy.NoRewriteHosts {
@@ -497,9 +524,6 @@ func (proxy *Proxy) attach(containerID string, orDie bool) error {
if proxy.NoMulticastRoute { if proxy.NoMulticastRoute {
args = append(args, "--no-multicast-route") args = append(args, "--no-multicast-route")
} }
if orDie {
args = append(args, "--or-die")
}
args = append(args, container.ID) args = append(args, container.ID)
return callWeaveAttach(container, args) return callWeaveAttach(container, args)
} }
@@ -514,8 +538,27 @@ func callWeaveAttach(container *docker.Container, args []string) error {
return nil return nil
} }
func validateCIDRs(cidrs []string) error {
for _, cidr := range cidrs {
if cidr == "net:default" {
continue
}
for _, prefix := range []string{"ip:", "net:", ""} {
if strings.HasPrefix(cidr, prefix) {
if _, err := address.ParseCIDR(strings.TrimPrefix(cidr, prefix)); err == nil {
break
}
return fmt.Errorf("invalid WEAVE_CIDR: %s", cidr)
}
}
}
return nil
}
func (proxy *Proxy) weaveCIDRs(networkMode string, env []string) ([]string, error) { func (proxy *Proxy) weaveCIDRs(networkMode string, env []string) ([]string, error) {
if networkMode == "host" || strings.HasPrefix(networkMode, "container:") { if networkMode == "host" || strings.HasPrefix(networkMode, "container:") ||
// Anything else, other than blank/none/default/bridge, is some sort of network plugin
(networkMode != "" && networkMode != "none" && networkMode != "default" && networkMode != "bridge") {
return nil, fmt.Errorf("the container has '--net=%s'", networkMode) return nil, fmt.Errorf("the container has '--net=%s'", networkMode)
} }
for _, e := range env { for _, e := range env {
@@ -602,10 +645,10 @@ func (proxy *Proxy) symlink(unixAddrs []string) (err error) {
binds := []string{"/var/run/weave:/var/run/weave"} binds := []string{"/var/run/weave:/var/run/weave"}
froms := []string{} froms := []string{}
for _, addr := range unixAddrs { for _, addr := range unixAddrs {
if addr == weaveSockUnix { from := strings.TrimPrefix(addr, "unix://")
if from == weaveSock {
continue continue
} }
from := strings.TrimPrefix(addr, "unix://")
dir := filepath.Dir(from) dir := filepath.Dir(from)
binds = append(binds, dir+":"+filepath.Join("/host", dir)) binds = append(binds, dir+":"+filepath.Join("/host", dir))
froms = append(froms, filepath.Join("/host", from)) froms = append(froms, filepath.Join("/host", from))
+12 -8
View File
@@ -77,19 +77,22 @@ func doRawStream(w http.ResponseWriter, resp *http.Response, client *httputil.Cl
} }
defer down.Close() defer down.Close()
defer up.Close() defer up.Close()
defer func() {
if err != nil {
Log.Warning(err)
}
}()
if _, err := down.Write([]byte("HTTP/1.1 200 OK\n")); err != nil { if _, err = downBuf.Write([]byte("HTTP/1.1 " + resp.Status + "\n")); err != nil {
Log.Warning(err)
return return
} }
if err = resp.Header.Write(downBuf); err != nil {
if err := resp.Header.Write(down); err != nil {
Log.Warning(err)
return return
} }
if _, err = downBuf.Write([]byte("\n")); err != nil {
if _, err := down.Write([]byte("\n")); err != nil { return
Log.Warning(err) }
if err = downBuf.Flush(); err != nil {
return return
} }
@@ -133,6 +136,7 @@ func doChunkedResponse(w http.ResponseWriter, resp *http.Response, client *httpu
} }
w.WriteHeader(resp.StatusCode) w.WriteHeader(resp.StatusCode)
wf.Flush()
up, remaining := client.Hijack() up, remaining := client.Hijack()
defer up.Close() defer up.Close()
+75
View File
@@ -0,0 +1,75 @@
package router
// A dummy overlay for the AWSVPC underlay to make `weave status` to return
// a valid information about peer connections.
import (
"github.com/weaveworks/mesh"
)
// mesh.OverlayConnection
type AWSVPCConnection struct {
establishedChan chan struct{}
errorChan chan error
}
func (conn *AWSVPCConnection) Confirm() {
// We close the channel to notify mesh that the connection has been established.
close(conn.establishedChan)
}
func (conn *AWSVPCConnection) EstablishedChannel() <-chan struct{} {
return conn.establishedChan
}
func (conn *AWSVPCConnection) ErrorChannel() <-chan error {
return conn.errorChan
}
func (conn *AWSVPCConnection) Stop() {}
func (conn *AWSVPCConnection) ControlMessage(tag byte, msg []byte) {
}
func (conn *AWSVPCConnection) DisplayName() string {
return "awsvpc"
}
// OverlayForwarder
func (conn *AWSVPCConnection) Forward(key ForwardPacketKey) FlowOp {
return DiscardingFlowOp{}
}
type AWSVPC struct{}
func NewAWSVPC() AWSVPC {
return AWSVPC{}
}
// mesh.Overlay
func (vpc AWSVPC) AddFeaturesTo(features map[string]string) {}
func (vpc AWSVPC) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
conn := &AWSVPCConnection{
establishedChan: make(chan struct{}),
errorChan: make(chan error, 1),
}
return conn, nil
}
func (vpc AWSVPC) Diagnostics() interface{} {
return nil
}
// NetworkOverlay
func (vpc AWSVPC) InvalidateRoutes() {}
func (vpc AWSVPC) InvalidateShortIDs() {}
func (vpc AWSVPC) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
return nil
}
+1 -1
View File
@@ -36,7 +36,7 @@ func (NullBridge) Interface() *net.Interface {
} }
func (NullBridge) String() string { func (NullBridge) String() string {
return "<no bridge networking>" return "no overlay bridge"
} }
func (NullBridge) Stats() map[string]int { func (NullBridge) Stats() map[string]int {
+20 -27
View File
@@ -1,8 +1,10 @@
package router package router
import ( import (
"fmt"
"math" "math"
"net" "net"
"os"
"time" "time"
"github.com/weaveworks/mesh" "github.com/weaveworks/mesh"
@@ -211,11 +213,10 @@ func (router *NetworkRouter) relayBroadcast(srcPeer *mesh.Peer, key PacketKey) F
const peersIdent = "directPeers" const peersIdent = "directPeers"
func (router *NetworkRouter) persistPeers() { func (router *NetworkRouter) persistPeers() {
// Persistence disabled pending rationalisation of the expected behaviour if err := router.db.Save(peersIdent, router.ConnectionMaker.Targets(false)); err != nil {
/*if err := router.db.Save(peersIdent, router.ConnectionMaker.Targets(false)); err != nil {
log.Errorf("Error persisting peers: %s", err) log.Errorf("Error persisting peers: %s", err)
return return
}*/ }
} }
func (router *NetworkRouter) InitiateConnections(peers []string, replace bool) []error { func (router *NetworkRouter) InitiateConnections(peers []string, replace bool) []error {
@@ -229,30 +230,22 @@ func (router *NetworkRouter) ForgetConnections(peers []string) {
router.persistPeers() router.persistPeers()
} }
func (router *NetworkRouter) InitialPeers(peers []string) ([]string, error) { func (router *NetworkRouter) InitialPeers(resume bool, peers []string) ([]string, error) {
var storedPeers []string if _, err := os.Stat("restart.sentinel"); err == nil || resume {
// Persistence disabled pending rationalisation of the expected behaviour var storedPeers []string
/*if _, err := router.db.Load(peersIdent, &storedPeers); err != nil { if _, err := router.db.Load(peersIdent, &storedPeers); err != nil {
return nil, err return nil, err
}*/ }
log.Println("Restart/resume detected - using persisted peer list:", storedPeers)
if storedPeers != nil && !equal(peers, storedPeers) { return storedPeers, nil
log.Println("Overriding initial peer list with stored list:", storedPeers)
peers = storedPeers
} else {
log.Println("Initial set of peers:", peers)
} }
sentinel, err := os.Create("restart.sentinel")
if err != nil {
return nil, fmt.Errorf("error creating sentinel: %v", err)
}
sentinel.Close()
log.Println("Launch detected - using supplied peer list:", peers)
return peers, nil return peers, nil
} }
func equal(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
-1
View File
@@ -1,6 +1,5 @@
--- ---
title: Integrating Kubernetes via the CNI Plugin title: Integrating Kubernetes via the CNI Plugin
layout: default
menu_order: 65 menu_order: 65
--- ---
+31 -17
View File
@@ -5,19 +5,14 @@ menu_order: 100
**Q:How do I obtain the IP of a specific container when I'm using Weave?** **Q: How do I obtain the IP of a specific container when I'm using Weave?**
You can use `weave ps <container>` to see the allocated address of a container on a Weave network. You can use `weave ps <container>` to see the allocated address of a container on a Weave network.
See [Troubleshooting Weave - List attached containers](/site/troubleshooting.md#list-attached-containers). See [Troubleshooting Weave - List attached containers](/site/troubleshooting.md#list-attached-containers).
**Q: How do I expose one of my containers to the outside world?** **Q: My dockerized app needs to check the request of an application that uses a static IP. Is it possible to manually change the IP of a container?**
Exposing a container to the outside world is described in [Exposing Services to the Outside](/site/using-weave/service-export.md).
**Q: My dockerized app needs to check the request of a an application that uses a static IP. Is it possible to manually change the IP of a container?**
You can manually change the IP of a container using [Classless Inter-Domain Routing or CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing). You can manually change the IP of a container using [Classless Inter-Domain Routing or CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing).
@@ -25,15 +20,20 @@ You can manually change the IP of a container using [Classless Inter-Domain Rout
For more information, refer to [Manually Specifying the IP Address of a Container](/site/using-weave/manual-ip-address.md). For more information, refer to [Manually Specifying the IP Address of a Container](/site/using-weave/manual-ip-address.md).
**Q:Can I connect my existing 'legacy' network with a Weave container network?** **Q: How do I expose one of my containers to the outside world?**
Exposing a container to the outside world is described in [Exporting Services](/site/using-weave/service-management.md#exporting).
**Q: Can I connect my existing 'legacy' network with a Weave container network?**
Yes you can. Yes you can.
For example, you have a Weave network that runs on hosts A, B, C. and you have an additional host, that we'll call P, where neither Weave nor Docker are running. However, you need to connect from a process running on host P to a specific container running on host B on the Weave network. Since the Weave network is completely separate from any network that P is connected to, you cannot connect the container using the container's IP address. For example, you have a Weave network that runs on hosts A, B, C. and you have an additional host, that we'll call P, where neither Weave nor Docker are running. However, you need to connect from a process running on host P to a specific container running on host B on the Weave network. Since the Weave network is completely separate from any network that P is connected to, you cannot connect the container using the container's IP address.
The simplest way to accomplish this would be to run Weave on the host and then run, `weave expose` to expose the network to any running containers. See [Integrating a Network Host](/site/using-weave/host-network-integration.md) If this is not possible, you could also expose a port on the container on host B and then connect to it. A simple way to accomplish this would be to run Weave on the host and then run, `weave expose` to expose the network to any running containers. Or you set up a route from P to one of A, B or C. See [Integrating a Network Host](/site/using-weave/host-network-integration.md).
You can read about exposing ports in [Service Exporting](/site/using-weave/service-export.md) Yet another option is to expose a port from the container on host B and then connect to it. You can read about exposing ports in [Exporting Services](/site/using-weave/service-management.md#exporting).
**Q: Why am I seeing the same IP address assigned to two different containers on different hosts?** **Q: Why am I seeing the same IP address assigned to two different containers on different hosts?**
@@ -46,7 +46,7 @@ Once the peers detect the inconsistency, they log the error and drop the connect
Some peers may be able to communicate their claim to the others before they run `rmpeer` (i.e. it's a race), so what you can expect is a few cliques of peers that are still talking to each other, but repeatedly dropping attempted connections with peers in other cliques. Some peers may be able to communicate their claim to the others before they run `rmpeer` (i.e. it's a race), so what you can expect is a few cliques of peers that are still talking to each other, but repeatedly dropping attempted connections with peers in other cliques.
For more information on see [Address Allocation with IP Address Management (IPAM)](/site/ipam.md) and also, [Starting, Stopping and Removing Peers](/site/ipam/stop-remove-peers-ipam.md) For more information on see [Allocating IP Addresses](/site/ipam.md) and also, [Starting, Stopping and Removing Peers](/site/ipam/stop-remove-peers-ipam.md).
**Q: What is the best practice for resetting a node that goes out of service?** **Q: What is the best practice for resetting a node that goes out of service?**
@@ -62,14 +62,14 @@ All virtualization techniques have some overhead, and Weave's overhead is typica
Weave Net also automatically uses the fastest datapath between two hosts. When Weave Net can't use the fast datapath between two hosts, it falls back to the slower packet forwarding approach. Selecting the fastest forwarding approach is automatic, and is determined on a connection-by-connection basis. For example, a Weave network spanning two data centers might use fast datapath within the data centers, but not for the more constrained network link between them. Weave Net also automatically uses the fastest datapath between two hosts. When Weave Net can't use the fast datapath between two hosts, it falls back to the slower packet forwarding approach. Selecting the fastest forwarding approach is automatic, and is determined on a connection-by-connection basis. For example, a Weave network spanning two data centers might use fast datapath within the data centers, but not for the more constrained network link between them.
For more information about fast datapath see [How Fast Datapath Works](/site/how-it-works/fastdp-how-it-works.md) For more information about fast datapath see [How Fast Datapath Works](/site/how-it-works/fastdp-how-it-works.md).
**Q:How can I tell if Weave is using fast datapath (fastdp) or not?** **Q: How can I tell if Weave is using fast datapath (fastdp) or not?**
To view whether Weave is using fastdp or not, you can run, `weave status connections` To view whether Weave is using fastdp or not, you can run, `weave status connections`
For more information on this command, see [Using Fast Datapath](/site/using-weave/fastdp.md) For more information on this command, see [Using Fast Datapath](/site/using-weave/fastdp.md).
**Q: Does encryption work with fastdp?** **Q: Does encryption work with fastdp?**
@@ -78,16 +78,30 @@ Encryption does not work with fast datapath. If you enable encryption using the
You can however have a mixture of fast datapath connections over trusted links, as well as, encrypted connections over untrusted links. You can however have a mixture of fast datapath connections over trusted links, as well as, encrypted connections over untrusted links.
See [Using Fast Datapath](/site/using-weave/fastdp.md) for more information See [Using Fast Datapath](/site/using-weave/fastdp.md) for more information.
**Q: Can I create multiple networks where containers can communicate on one network, but are isolated from containers on other networks?** **Q: Can I create multiple networks where containers can communicate on one network, but are isolated from containers on other networks?**
Yes, of course! Weave allows you to run isolated networks and still allow open communications between individual containers from those isolated networks. You can find information on how to do this in [Application Isolation](/site/using-weave/application-isolation.md) Yes, of course! Weave allows you to run isolated networks and still allow open communications between individual containers from those isolated networks. You can find information on how to do this in [Application Isolation](/site/using-weave/application-isolation.md).
**Q: Which ports does Weave Net use (e.g. if I am configuring a firewall) ?**
You must permit traffic to flow through TCP 6783 and UDP 6783/6784,
which are Weave’s control and data ports.
**<a name=own-image></a>Q: Why do you use your own Docker image `weaveworks/ubuntu`?**
The official Ubuntu image does not contain the `ping` and `nc`
commands which are used in many of our examples throughout the
documentation. The `weaveworks/ubuntu` image is simply the official
Ubuntu image with those two commands added.
**See Also** **See Also**
* [Troubleshooting Weave](/site/troublehooting.md) * [Troubleshooting Weave](/site/troubleshooting.md)
* [Troubleshooting IPAM](/site/ipam.md) * [Troubleshooting IPAM](/site/ipam.md)
* [Troubleshooting the Proxy](/site/weave-docker-api/using-proxy.md) * [Troubleshooting the Proxy](/site/weave-docker-api/using-proxy.md)
+8 -8
View File
@@ -81,7 +81,7 @@ See [Using the Weave Docker API Proxy](/site/weave-docker-api/using-proxy.md).
Weave Net can also be used as a [Docker plugin](https://docs.docker.com/engine/extend/plugins_network/). A Docker network Weave Net can also be used as a [Docker plugin](https://docs.docker.com/engine/extend/plugins_network/). A Docker network
named `weave` is created by `weave launch`, which is used as follows: named `weave` is created by `weave launch`, which is used as follows:
$ docker run --net=weave -ti ubuntu $ docker run --net=weave -ti weaveworks/ubuntu
Using the Weave plugin enables you to take advantage of [Docker's network functionality](https://docs.docker.com/engine/extend/plugins_network/). Using the Weave plugin enables you to take advantage of [Docker's network functionality](https://docs.docker.com/engine/extend/plugins_network/).
@@ -111,7 +111,7 @@ Instead of allowing Weave to automatically allocate addresses, an IP address and
specified. See [How to Manually Specify IP Addresses and Subnets](/site/using-weave/manual-ip-address.md) for instructions. specified. See [How to Manually Specify IP Addresses and Subnets](/site/using-weave/manual-ip-address.md) for instructions.
For a discussion on how Weave Net uses IPAM, see [Automatic IP Address Management](/site/ipam.md). And also review the For a discussion on how Weave Net uses IPAM, see [Automatic IP Address Management](/site/ipam.md). And also review the
[the basics of IP addressing](/how-it-works/ip-addresses.md) for an explanation of addressing and private networks. [the basics of IP addressing](/site/how-it-works/ip-addresses.md) for an explanation of addressing and private networks.
###<a name="naming-and-discovery"></a>Naming and Discovery ###<a name="naming-and-discovery"></a>Naming and Discovery
@@ -119,8 +119,8 @@ For a discussion on how Weave Net uses IPAM, see [Automatic IP Address Managemen
Named containers are automatically registered in [weaveDNS](/site/weavedns.md), Named containers are automatically registered in [weaveDNS](/site/weavedns.md),
and are discoverable by using standard, simple name lookups: and are discoverable by using standard, simple name lookups:
host1$ docker run -dti --name=service ubuntu host1$ docker run -dti --name=service weaveworks/ubuntu
host1$ docker run -ti ubuntu host1$ docker run -ti weaveworks/ubuntu
root@7b21498fb103:/# ping service root@7b21498fb103:/# ping service
WeaveDNS also supports [load balancing](/site/weavedns/load-balance-fault-weavedns.md), [fault resilience](/site/weavedns/load-balance-fault-weavedns.md) and [hot swapping](/site/weavedns/managing-entries-weavedns.md). WeaveDNS also supports [load balancing](/site/weavedns/load-balance-fault-weavedns.md), [fault resilience](/site/weavedns/load-balance-fault-weavedns.md) and [hot swapping](/site/weavedns/managing-entries-weavedns.md).
@@ -178,7 +178,7 @@ running alongside Weave.
Weave Net implements encryption and security using the Go version of [Daniel J. Bernstein's NaCl library](http://nacl.cr.yp.to/index.html). Weave Net implements encryption and security using the Go version of [Daniel J. Bernstein's NaCl library](http://nacl.cr.yp.to/index.html).
For information on how to secure your Docker network connections, see [Securing Connections Across Untrusted Networks](/site/using-weave/security-untrusted-networks.md) and for a more technical discussion on how Weave implements encryption see, [Weave Encryption](/how-it-works/encryption.md) and [How Weave Implements Encryption](/site/how-it-works/encryption-implementation.md). For information on how to secure your Docker network connections, see [Securing Connections Across Untrusted Networks](/site/using-weave/security-untrusted-networks.md) and for a more technical discussion on how Weave implements encryption see, [Weave Encryption](/site/how-it-works/encryption.md) and [How Weave Implements Encryption](/site/how-it-works/encryption-implementation.md).
###<a name="host-network-integration"></a>Host Network Integration ###<a name="host-network-integration"></a>Host Network Integration
@@ -186,7 +186,7 @@ For information on how to secure your Docker network connections, see [Securing
Weave Net application networks can be integrated with a host's network, and establish connectivity between the host and Weave Net application networks can be integrated with a host's network, and establish connectivity between the host and
application containers anywhere. application containers anywhere.
See [Integrating a Host Network with Weave](/site/using-weave/host-network-integration.md). See [Integrating with the Host Network](/site/using-weave/host-network-integration.md).
###<a name="services"></a>Managing Services: Exporting, Importing, Binding and Routing ###<a name="services"></a>Managing Services: Exporting, Importing, Binding and Routing
@@ -195,7 +195,7 @@ See [Integrating a Host Network with Weave](/site/using-weave/host-network-integ
* **Binding Services** - A container can be bound to a particular IP and port without having to change your application code, while at the same time will maintain its original endpoint. * **Binding Services** - A container can be bound to a particular IP and port without having to change your application code, while at the same time will maintain its original endpoint.
* **Routing Services** - By combining the importing and exporting features, you can connect to disjointed networks, even when separated by firewalls and where there may be overlapping IP addresses. * **Routing Services** - By combining the importing and exporting features, you can connect to disjointed networks, even when separated by firewalls and where there may be overlapping IP addresses.
See [Managing Services in Weave: Exporting, Importing, Binding and Routing](/site/using-weave/service-management.md) for instructions on how to manage services on a Weave container network. See [Managing Services - Exporting, Importing, Binding and Routing](/site/using-weave/service-management.md) for instructions on how to manage services on a Weave container network.
###<a name="multi-cloud-networking"></a>Multi-Cloud Networking ###<a name="multi-cloud-networking"></a>Multi-Cloud Networking
@@ -232,7 +232,7 @@ reconfiguration or, in many cases, restarts of other containers.
All that is required is for the migrated container to be started All that is required is for the migrated container to be started
with the same IP address as it was given originally. with the same IP address as it was given originally.
See [Managing Services in Weave: Exporting, Importing, Binding and Routing](/site/using-weave/service-management.md), in particular, Routing Services for more information on container mobility. See [Managing Services - Exporting, Importing, Binding and Routing](/site/using-weave/service-management.md), in particular, Routing Services for more information on container mobility.
###<a name="fault-tolerance"></a>Fault Tolerance ###<a name="fault-tolerance"></a>Fault Tolerance
+1 -2
View File
@@ -1,6 +1,5 @@
--- ---
title: Understanding how Weave Net Works title: Understanding how Weave Net Works
layout: default
menu_order: 90 menu_order: 90
--- ---
@@ -48,4 +47,4 @@ first send it to peer 3:
**See Also** **See Also**
* [Weave Router Sleeve Encapsulation](/site/how-it-works/router-encapsulation.md) * [Weave Router Sleeve Encapsulation](/site/how-it-works/router-encapsulation.md)
* [How Weave Inteprets Network Topology](/site/how-it-works/network-topology.md) * [How Weave Interprets Network Topology](/site/how-it-works/network-topology.md)
@@ -1,11 +1,10 @@
--- ---
title: How Weave Net Implements Encryption title: How Weave Net Implements Encryption
layout: default
menu_order: 70 menu_order: 70
--- ---
This section describes some details of Weave Net's built-in This section describes some details of Weave Net's built-in
[encryption](/how-it-works/encryption.md): [encryption](/site/how-it-works/encryption.md):
* [Establishing the Ephemeral Session Key](#ephemeral-key) * [Establishing the Ephemeral Session Key](#ephemeral-key)
* [Key Generation](#csprng) * [Key Generation](#csprng)
+1 -1
View File
@@ -63,4 +63,4 @@ find a match, so uses the default rule.
**See Also** **See Also**
* [Configuring Weave to Explicitly Use an IP Range](/site/ip-addresses/configuring-weave.md) * [Allocating IPs in a Specific Range](/site/using-weave/configuring-weave.md)
+2 -2
View File
@@ -45,7 +45,7 @@ the same peer. And in this instance, it will fit as many frames as possible into
UDP packet. UDP packet.
The meta data for each frame contains the names of the capturing and The meta data for each frame contains the names of the capturing and
the destination peers. Since the name of the capturing peer name is the destination peers. Since the name of the capturing peer is
associated with the source MAC of the captured payload, it allows associated with the source MAC of the captured payload, it allows
receiving peers to build up their mappings of which client MAC receiving peers to build up their mappings of which client MAC
addresses are local to which peers. addresses are local to which peers.
@@ -61,4 +61,4 @@ MAC discovery.
**See Also** **See Also**
* [How Weave Net Inteprets Network Topology](/site/how-it-works/network-topology.md) * [How Weave Net Interprets Network Topology](/site/how-it-works/network-topology.md)
+32 -9
View File
@@ -12,29 +12,52 @@ Install Weave Net by running the following:
sudo curl -L git.io/weave -o /usr/local/bin/weave sudo curl -L git.io/weave -o /usr/local/bin/weave
sudo chmod a+x /usr/local/bin/weave sudo chmod a+x /usr/local/bin/weave
If you are on OSX and are using Docker Machine you need to make sure If you are on OSX and you are using Docker Machine ensure that a VM is running and configured
that a VM is running and configured before getting Weave Net. Setting up a VM is shown in [the Docker Machine before downloading Weave Net. To set up a VM see [the Docker Machine
documentation](https://docs.docker.com/installation/mac/#from-your-shell). documentation](https://docs.docker.com/installation/mac/#from-your-shell) or refer to ["Part 1: Launching Weave Net with Docker Machine"](/guides/part-1-launching-weave-net-with-docker-machine/).
After the VM is configured with Docker Machine, Weave Net can be launched directly from the OSX host.
Weave Net respects the environment variable `DOCKER_HOST`, so that you can run After your VM is setup with Docker Machine, Weave Net can be launched directly from the OSX host. Weave Net respects the environment variable `DOCKER_HOST`, so that you can run and control a Weave Network locally on a remote host. See [Using The Weave Docker API Proxy](/site/weave-docker-api/using-proxy.md).
and control a Weave Network locally on a remote host. See [Using The Weave Docker API Proxy](/site/weave-docker-api/using-proxy.md).
With Weave Net downloaded onto your VMs or hosts, you are ready to launch a Weave network and deploy apps onto it. See [Using Weave Net](/site/using-weave.md). With Weave Net downloaded onto your VMs or hosts, you are ready to launch a Weave network and deploy apps onto it. See [Using Weave Net](/site/using-weave.md).
###Quick Start Screencast ###Quick Start Screencast
<a href="https://youtu.be/kihQCCT1ykE" alt="Click to watch the screencast" target="_blank"> <a href="https://youtu.be/kihQCCT1ykE" target="_blank">
<img src="hello-screencast.png" /> <img src="hello-screencast.png" alt="Click to watch the screencast" />
</a> </a>
###Guides for specific platforms ###Checkpoint
Weave Net [periodically contacts Weaveworks servers for available
versions](https://github.com/weaveworks/go-checkpoint). New versions
are announced in the log and in [the status
summary](/site/troubleshooting.md#weave-status).
The information sent in this check is:
* Host UUID hash
* Kernel version
* Docker version
* Weave Net version
* Network mode, e.g. 'awsvpc'
To disable this check, run the following before launching Weave Net:
export CHECKPOINT_DISABLE=1
###Guides for Specific Platforms
CoreOS users see [here](/guides/networking-docker-containers-with-weave-on-coreos/) for an example of installing Weave using cloud-config. CoreOS users see [here](/guides/networking-docker-containers-with-weave-on-coreos/) for an example of installing Weave using cloud-config.
Amazon ECS users see [here](https://github.com/weaveworks/integrations/blob/master/aws/ecs/README.md) Amazon ECS users see [here](https://github.com/weaveworks/integrations/blob/master/aws/ecs/README.md)
for the latest Weave AMIs and [here](http://weave.works/guides/service-discovery-with-weave-aws-ecs.html) to get started with Weave Net on ECS. for the latest Weave AMIs and [here](http://weave.works/guides/service-discovery-with-weave-aws-ecs.html) to get started with Weave Net on ECS.
If you're on Amazon EC2, the standard installation instructions at the
top of this page, provide the simplest setup and the most flexibility.
A [special no-overlay mode for EC2](/site/using-weave/awsvpc.md) can
optionally be enabled, which allows containers to communicate at the
full speed of the underlying network.
**See Also** **See Also**
* [Using Weave Net](/site/using-weave.md) * [Using Weave Net](/site/using-weave.md)
+1 -1
View File
@@ -19,7 +19,7 @@ normally placed in `/etc/systemd/system/weave.service`.
After=docker.service After=docker.service
[Service] [Service]
EnvironmentFile=-/etc/sysconfig/weave EnvironmentFile=-/etc/sysconfig/weave
ExecStartPre=/usr/local/bin/weave launch $PEERS ExecStartPre=/usr/local/bin/weave launch --no-restart $PEERS
ExecStart=/usr/bin/docker attach weave ExecStart=/usr/bin/docker attach weave
ExecStop=/usr/local/bin/weave stop ExecStop=/usr/local/bin/weave stop
[Install] [Install]
+19 -9
View File
@@ -28,10 +28,22 @@ suits your deployment best.
#### <a name="seed"></a>Via Seed #### <a name="seed"></a>Via Seed
Configuration via seed requires you to provide a list of peer names Configuration via seed requires you to provide a list of _peer names_
(via the `--ipalloc-init seed=` parameter) amongst which the address (via the `--ipalloc-init seed=` parameter) amongst which the address
space will be shared initially - this means that you will also have to space will be shared initially. Normally weave derives a unique peer
specify peer names explicitly on launch: name automatically at launch, but since you need to know them ahead of
time in this case you will need to name each peer explicitly via the
`--name` parameter.
> Peers in the weave network are identified by a 48-bit value
> formatted like an ethernet MAC address (e.g. 01:23:45:67:89:ab) -
> you can either specify the name fully, or substitute a single run of
> zero-octets using the `::` notation, similar to
> [IPv6 address representation](https://en.wikipedia.org/wiki/IPv6_address#Recommended_representation_as_text):
>
> * `00:00:00:00:00:01` can be written `::1`
> * `01:00:00:00:00:00` can be written `1::`
> * `01:00:00:00:00:01` can be written `1::1`
host1$ weave launch --name ::1 --ipalloc-init seed=::1,::2,::3 host1$ weave launch --name ::1 --ipalloc-init seed=::1,::2,::3
host2$ weave launch --name ::2 --ipalloc-init seed=::1,::2,::3 host2$ weave launch --name ::2 --ipalloc-init seed=::1,::2,::3
@@ -43,10 +55,10 @@ outset even under conditions of partition - no consensus is required.
#### <a name="consensus"></a>Via One-off Consensus #### <a name="consensus"></a>Via One-off Consensus
Alternatively, you can let Weave Net determine the seed automatically via Alternatively, you can let Weave Net determine the seed automatically
a consensus algorithm. Since you don't need to provide it with a list via a consensus algorithm. Since you don't need to provide it with a
of peer names anymore, you can let Weave Net generate those randomly list of peer names anymore, you can let Weave Net derive those
for you as well. automatically for you as well.
However, in order for Weave Net to form a single consensus However, in order for Weave Net to form a single consensus
reliably you must now instead tell each peer how many peers there are reliably you must now instead tell each peer how many peers there are
@@ -191,5 +203,3 @@ named `weavedb` is used to store this data.
* [Automatic Allocation Across Multiple Subnets](/site/ipam/allocation-multi-ipam.md) * [Automatic Allocation Across Multiple Subnets](/site/ipam/allocation-multi-ipam.md)
* [Plugin Command-line Arguments](/site/plugin/plug-in-command-line.md) * [Plugin Command-line Arguments](/site/plugin/plug-in-command-line.md)
+2 -2
View File
@@ -15,13 +15,13 @@ allocation of an address from a particular subnet, set the
`WEAVE_CIDR` environment variable to `net:<subnet>` when creating the `WEAVE_CIDR` environment variable to `net:<subnet>` when creating the
container, for example: container, for example:
host1$ docker run -e WEAVE_CIDR=net:10.2.7.0/24 -ti ubuntu host1$ docker run -e WEAVE_CIDR=net:10.2.7.0/24 -ti weaveworks/ubuntu
You can ask for multiple addresses in different subnets and add in You can ask for multiple addresses in different subnets and add in
manually-assigned addresses (outside the automatic allocation range), manually-assigned addresses (outside the automatic allocation range),
for instance: for instance:
host1$ docker run -e WEAVE_CIDR="net:10.2.7.0/24 net:10.2.8.0/24 ip:10.3.9.1/24" -ti ubuntu host1$ docker run -e WEAVE_CIDR="net:10.2.7.0/24 net:10.2.8.0/24 ip:10.3.9.1/24" -ti weaveworks/ubuntu
>**Note:** The ".0" and ".-1" addresses in a subnet are not used, as required by >**Note:** The ".0" and ".-1" addresses in a subnet are not used, as required by
[RFC 1122](https://tools.ietf.org/html/rfc1122#page-29)). [RFC 1122](https://tools.ietf.org/html/rfc1122#page-29)).
+21
View File
@@ -43,3 +43,24 @@ the total allocation range and default subnet. Columns are as follows:
for more for more
* 'Range' - total allocation range set by `--ipalloc-range` * 'Range' - total allocation range set by `--ipalloc-range`
* 'DefaultSubnet' - default subnet set by `--ipalloc-default-subnet` * 'DefaultSubnet' - default subnet set by `--ipalloc-default-subnet`
Information regarding the division of the IP allocation range amongst
peers and their reachability can be obtained with
```
$ weave status ipam
00:00:00:00:00:01(one) 349526 IPs (33.3% of total)
00:00:00:00:00:02(two) 349525 IPs (33.3% of total)
00:00:00:00:00:03(three) 349525 IPs (33.3% of total) - unreachable!
```
Columns are as follows:
* Peer Name and Nickname
* Absolute quantity/percentage of allocation range managed by peer
* Indication of unreachability. This means that the peer is not
visible (directly or indirectly) to the peer on which `weave status
ipam` was run; whilst this could be a transient condition due to a
partition, it may be because the peer has failed and needs to be
removed administratively - see [Starting, Stopping and Removing
Peers](/site/ipam/stop-remove-peers-ipam.md) for more details.
+5
View File
@@ -0,0 +1,5 @@
---
title: Operational Guide
menu_order: 35
---
Operations manual.
+57
View File
@@ -0,0 +1,57 @@
---
title: Autoscaling
menu_order: 40
---
### Bootstrapping
An autoscaling configuration begins with a fixed cluster:
* Configured as per the [Uniform Fixed Cluster](/site/operational-guide/uniform-fixed-cluster.md)
scenario.
* Hosted on reserved or protected instances to ensure long-term
stability.
* Ideally sized at a minimum of three or five nodes (you can make your
fixed cluster bigger to accommodate base load as required; a minimum
is specified here in the interests of resilience only.)
Building on this foundation, arbitrary numbers of dynamic peers can be
added or removed concurrently as desired, without requiring any
changes to the configuration of the fixed cluster. As with the fixed
cluster, dynamically added nodes recover automatically from reboots
and partitions.
### Scaling Out
On the additional dynamic peer, at boot, via
[systemd](/site/installing-weave/systemd.md) or equivalent:
weave launch --no-restart --ipalloc-init=observer $PEERS
Where,
* `$PEERS` means all peers in the _fixed cluster_, initial
and subsequently added, which have not been explicitly removed. It
should include fixed peers which are temporarily offline or stopped.
You do not have to keep track of and specify the addresses
of other dynamic peers in `$PEERS` - they will discover and connect to
each other via the fixed cluster.
>>**Note:** The use of `--ipalloc-init=observer` prevents dynamic peers from
coming to a consensus on their own - this is important to stop a
clique forming amongst a group of dynamically added peers if they
become partitioned from the fixed cluster after having learned about
each other via discovery.
### Scaling In
On the dynamic peer to be removed:
weave reset
If for any reason you cannot arrange for `weave reset` to be run on
the peer before the underlying host is destroyed (for example when
using spot instances that can be destroyed without notice), you will
need an asynchronous process to [reclaim lost IP address
space](/site/operational-guide/tasks.md#detect-reclaim-ipam).
+144
View File
@@ -0,0 +1,144 @@
---
title: Concepts
menu_order: 10
---
This section describes some of the essential concepts with which you will
need to be familiar before continuing to the example deployment
scenarios.
## Host
For the purposes of this documentation a host is an
installation of the Linux operating system that is running an
instance of the Docker Engine. The host may be executing directly on bare
hardware or inside a virtual machine.
## Peer
A peer is a running instance of Weave Net, typically one per host.
## Peer Name
Weave Net peers are identified by a 48-bit value formatted like an
ethernet MAC address, for example, `01:23:45:67:89:ab`. The 'peer
name' is used for various purposes:
* Routing of packets between containers on the overlay network
* Recording the origin peer of DNS entries
* Recording ownership of IP address ranges
While it is desirable for the peer name to remain stable across
restarts, it is essential that it is unique. If two or more peers
share the same name chaos will ensue, which includes but is not limited to
double allocation of addresses and the inability to route packets on the
overlay network.
When the router is launched on a host, it derives its peer name in order of preference:
* From the command line, where the user is responsible for uniqueness and
stability
* From the BIOS product UUID, which is generally stable across
restarts and unique across different physical hardware and certain
cloned VMs
* From the hypervisor UUID, which is generally stable across restarts
and unique across VMs which do not provide access to a BIOS product
UUID
* From a random value, practically unique across different physical
hardware and cloned VMs but not stable across restarts
The appropriate strategy for assigning peer names depends on the type
and method of your particular deployment and is discussed in more
detail below.
## Peer Discovery
Peer discovery is a mechanism that allows peers to learn about new
Weave Net hosts from existing peers without being explicitly told. Peer
discovery is
[enabled by default](/site/using-weave/finding-adding-hosts-dynamically.md).
## Network Partition
A network partition is a transient condition whereby some arbitrary
subsets of peers are unable to communicate with each other for the
duration - perhaps because a network switch has failed, or a fibre
optic line severed. Weave Net is designed to allow peers and their
containers to make maximum safe progress under conditions of
partition, healing automatically once the partition is over.
## IP Address Manager (IPAM)
[IPAM](/site/ipam.md) is the subsystem responsible for dividing up a
large contiguous block of IP addresses (known as the IP allocation
range) amongst peers so that individual addresses may be uniquely
assigned to containers anywhere on the overlay network.
When a new network is formed an initial division of the IP allocation
range must be made. Two (mutually exclusive) mechanisms with different
tradeoffs are provided to perform this task: seeding and consensus.
### Seeding
Seeding requires each peer to be told the list of peer names amongst
which the address space is to be divided initially. There are some
constraints and consequences:
* Every peer added to the network _must_ receive the same seed list,
for all time, or they will not be able to join together to form a
single cohesive whole.
* Because the 'product UUID' and 'random value' methods of peer name
assignment are unpredictable, the end user must by necessity also
specify peer names.
* Even though every peer _must_ receive the same seed, that seed does
_not_ have to include every peer in the network, nor does it have to
be updated when new peers are added (in fact due to the first
constraint above it may not be).
Example configurations are given in the section on deployment
scenarios:
* [Uniform Dynamic Cluster](/site/operational-guide/uniform-dynamic-cluster.md)
### Consensus
Alternatively, when a new network is formed for the first time, peers
can be configured to co-ordinate amongst themselves to automatically
divide up the IP allocation range. This process is known as consensus
and it requires each peer to be told the total number of expected peers
(the 'initial peer count') in order to prevent the formation of disjoint
peer groups which would, ultimately, result in duplicate IP
addresses.
Example configurations are given in the section on deployment
scenarios:
* [Interactive Deployment](/site/operational-guide/interactive.md)
* [Uniform Fixed Cluster](/site/operational-guide/uniform-fixed-cluster.md)
### Observers
Finally, an option is provided to start a peer as an _observer_. Such
peers do not require a seed peer name list or an initial peer
count; instead they rely on the existence of other peers in the
network which have been so configured. When an observer needs address
space, it asks for it from one of the peers which partook of the
initial division, triggering consensus if necessary.
Example configurations are given in the section on deployment
scenarios:
* [Autoscaling](/site/operational-guide/autoscaling.md)
## Persistence
Certain information is remembered between launches of Weave Net (for
example across reboots):
* The division of the IP allocation range amongst peers
* Allocation of addresses to containers
The persistence of this information is managed transparently in a
volume container but can be
[destroyed explicitly](/site/operational-guide/tasks.md#reset)
if necessary.
+65
View File
@@ -0,0 +1,65 @@
---
title: Interactive Deployment
menu_order: 20
---
Weave Net can be launched interactively on the command line, and as
long as Docker is configured to start on boot, the network will survive
host reboots without the use of a systemd. However, since launching
Weave Net commands in interactive mode is not amenable to automation
and configuration management, it is recommended that deploying Weave Net
in this mode be reserved for exploration and evaluation only.
### Bootstrapping
On the initial peer:
weave launch
### Adding a Peer
On the new peer:
weave launch <extant peers>
Where,
* `<extant peers>` indicates all peers in the network, initial and
subsequently added, which have not been explicitly removed. It should
include peers that are temporarily offline or stopped.
To ensure that the new peer has joined the existing network,
execute the following:
weave prime
Before adding any new peers, you _must_ wait for this to complete.
If this command waits and does not exit, it means that there is some
issue (such as a network partition or failed peers) that is preventing
a quorum from being reached - you will need to [address
that](/site/troubleshooting.md) before moving on.
### Stopping a Peer
A peer can be stopped temporarily with the following command:
weave stop
A temporarily stopped peer will remember IP address allocation information on the
next `weave launch` but will forget any discovered peers or
modifications to the initial peer list that were made with `weave
connect` or `weave forget`. Note that if the host reboots, Docker
automatically restarts the peer.
### Removing a Peer
On the peer to be removed:
weave reset
Then optionally on each remaining peer:
weave forget <removed peer>
This step is not mandatory, but it will eliminate log noise and
spurious network traffic by stopping reconnection attempts and
preventing further connection attempts after a restart.
+119
View File
@@ -0,0 +1,119 @@
---
title: Administrative Tasks
menu_order: 60
---
The following administrative tasks are discussed:
* [Configuring Weave Net to Start Automatically on Boot](#start-on-boot)
* [Detecting and Reclaiming Lost IP Address Space](#detect-reclaim-ipam)
*[Manually Reclaiming Lost Address Space](#reclaim-address-space)
* [Upgrading a Cluster](#cluster-upgrade)
* [Resetting Persisted Data](#reset)
##<a name="start-on-boot"></a>Configuring Weave Net to Start Automatically on Boot
`weave launch` runs all of Weave Net's containers with a Docker restart
policy set to `always`. If you have launched Weave Net manually at least
once and your system is configured to start Docker on boot, then Weave Net
will start automatically on system restarts.
If you are aiming for a non-interactive installation, use
[systemd]((/site/installing-weave/systemd.md) or a similar init system to
launch Weave using the `--no-restart` flag after Docker has been started.
##<a name="detect-reclaim-ipam"></a>Detecting and Reclaiming Lost IP Address Space
The recommended method of removing a peer is to run `weave reset` on that
peer before the underlying host is decommissioned or repurposed. This
ensures that the portion of the IPAM allocation range assigned to the
peer is released for reuse.
Under certain circumstances this operation may not be successful,
or possible:
* If the peer is partitioned from the rest of the network
when `weave reset` is executed on it
* If the underlying host is no longer available to execute `weave
reset` due to a hardware failure or other unplanned termination (for
example when using autoscaling with spot-instances that can be
destroyed without notice)
In some cases you may already be aware of the problem, as you were
unable to execute `weave reset` successfully or because you know
through other channels that the host has died. In these cases you can
proceed straight to [Manually Reclaiming Lost Address Space](#reclaim-address-space).
However in some scenarios it may not be obvious that space has been
lost, in which case you can check for it periodically with the
following command on any peer:
weave status ipam
This command displays the peer names and nicknames, absolute quantity/percentage of allocation
range managed by peer and also identifies the names of unreachable peers. If you are satisifed
that the peer is truly gone, rather than temporarily unreachable due to a
partition, you can reclaim their space manually.
###<a name="manually-reclaim-address-space"></a>Manually Reclaiming Address Space
When a peer dies unexpectedly the remaining peers will consider its
address space to be unavailable even after it has remained unreachable
for prolonged periods. There is no universally applicable time limit
after which one of the remaining peers could decide unilaterally that
it is safe to appropriate the space for itself, and so an
administrative action is required to reclaim it.
The [`weave rmpeer`](/site/ipam/stop-remove-peers-ipam.md)
command is provided to perform this task, and must
be executed on _one_ of the remaining peers. That peer will then take
ownership of the freed address space.
##<a name="cluster-upgrade"></a>Upgrading a Cluster
Protocol versioning and feature negotiation are employed in Weave Net
to enable incremental rolling upgrades. Each major maintains
the ability to speak to the preceding major release at a minimum, and
connected peers only utilize features which both support.
The general upgrade procedure is as follows:
On each peer:
* Stop the old Weave Net with `weave stop` (or `systemctl stop weave` if
you're using a systemd unit file)
* Download the new Weave Net script and replace the existing one
* Start the new Weave with `weave launch <existing peer list>` (or
`systemctl start weave` if you're using a systemd unit file)
To minimize downtime while the new script is pulling the new container images:
* Download the new Weave Net script to a temporary location, for example,
`/path/to/new/weave`
* Pull the new images with `/path/to/new/weave setup`
* Stop the old Weave Net with `weave stop` (or `systemctl stop weave` if
you're using a systemd unit file)
* Replace the existing script with the new one
* Start the new Weave Net with `weave launch <existing peer list>` (or
`systemctl start weave` if you're using a systemd unit file)
>>**Note:** Always check the Release Notes for specific versions in case
there are any special caveats or deviations from the standard
procedure.
##<a name="reset"></a>Resetting Persisted Data
Weave Net persists information in a data volume container named
`weavedb`. If you wish to start from a completely clean slate (for
example to withdraw a peer from one network and join it to another)
you can issue the following command:
weave reset
**See Also**
* [Allocating IP Addresses](/site/ipam.md)
* [Troubleshooting the IP Allocator](/site/ipam/troubleshooting-ipam.md)
@@ -0,0 +1,96 @@
---
title: Uniform Dynamic Clusters
menu_order: 50
---
A uniform dynamic cluster has the following characteristics:
* Recovers automatically after reboots and partitions.
* Identical configuration for all of its peers.
* Initial cluster peers can make progress from the outset even if
bootstrapping occurs under conditions of partition.
* Once an initial cluster has been bootstrapped, arbitrary numbers of
new peers can be added in parallel without coordination. This makes
it ideally suited for use with convergent provisioning tools that
operate across multiple hosts in an asynchronous fashion.
## Bootstrapping
On each initial peer N, at boot, via
[systemd](/site/installing-weave/systemd.md):
hostN$ weave launch --no-restart --name ::N --ipalloc-init seed=$SEED $PEERS
Where,
* `$SEED` is a comma separated list of the _names_ of the initial
peers, for example, `::1,::2,::3` and,
* `$PEERS` is obtained from `/etc/sysconfig/weave` as described in the linked systemd
documentation, and includes the complete set of initial peer
_addresses_.
For example, if you have three initial peers you would specify the
following:
host1$ weave launch --no-restart --name ::1 --ipalloc-init seed=$SEED $PEERS
host2$ weave launch --no-restart --name ::2 --ipalloc-init seed=$SEED $PEERS
host3$ weave launch --no-restart --name ::3 --ipalloc-init seed=$SEED $PEERS
Where
SEED="::1,::2,::3"
PEERS="host1 host2 host3"
## Adding a Peer
On each new peer, at boot, via
[systemd](/site/installing-weave/systemd.md):
hostN$ weave launch --no-restart --name ::N --ipam-seed $SEED $PEERS
Where,
* `--no-restart` disables the Docker restart policy, since this will be
handled by systemd.
* `--name` specifies a unique name for this new peer.
* `--ipam-seed` specifies the names of only those peers that were
involved in the initial cluster bootstrap - even if they have been
subsequently removed from the cluster. You can view this as a kind
of 'cluster identity',where peers may only interoperate in the same
cluster if they share the same seed.
* `$PEERS` is obtained from `/etc/sysconfig/weave` as described in the
linked systemd documentation. For convenience, this may contain the
address of the peer that is being launched, so that you don't have
to compute separate lists of 'other' peers tailored to each peer -
just supply the same complete list of peer addresses to every peer.
Note that unlike [Interactive](/site/operational-guide/interactive.md)
and [Uniform Fixed Cluster](/site/operational-guide/uniform-fixed-cluster.md) deployments
there is no `weave prime` step. You can add as many new peers in
parallel as you like, even under conditions of partition, and they
will all (eventually) join safely. This is ideal for use in
conjunction with asynchronous provisioning systems such as puppet or
chef.
For maximum robustness, you should distribute an updated
`/etc/sysconfig/weave` file including the new peer to all existing
peers.
### Removing a Peer
On the peer to be removed:
weave reset
You may remove a seed peer, as long as there is at least one other
seed peer left in the network.
Then, distribute an updated `/etc/sysconfig/weave` to the remaining
peers, omitting the removed peer from `$PEERS`.
On each remaining peer:
weave forget <removed peer>
This step is not mandatory, but it will eliminate log noise and
spurious network traffic by stopping reconnection attempts.
@@ -0,0 +1,87 @@
---
title: Uniform Fixed Clusters
menu_order: 30
---
This scenario describes a production deployment of a fixed number of N
nodes (N=1 in the simplest case).
A uniform fixed cluster has the following characteristics:
* Recovers automatically from reboots and partitions.
* All peers have identical configuration.
* There is a controlled process for adding or removing nodes, however
the end user is responsible for ensuring that only one instance of
the process is in-flight at a time. While it is possible to
automate, the potentially-blocking `weave prime` operation and the
need for global serialization make it non-trivial. It is however
relatively straightforward for a human to provide the necessary
guarantees and exception handling manually, and so this scenario is best
suited to deployments which change size infrequently as a planned
maintenance event.
### Bootstrapping
On each initial peer, at boot, via
[systemd](/site/installing-weave/systemd.md):
weave launch --no-restart $PEERS
Where,
* `--no-restart` disables the Docker restart policy, since this will be
handled by systemd.
* `$PEERS` is obtained from `/etc/sysconfig/weave` as described in the
linked systemd documentation. For convenience, this may contain the
address of the peer which is being launched, so that you don't have
to compute separate lists of 'other' peers tailored to each peer -
just supply the same complete list of peer addresses to every peer.
Then on _any_ peer run the following to force consensus:
weave prime
>>**Note:** You can run this safely on more than one or
even all peers, but it's only strictly necessary to run it on one of
them.
Once this command completes successfully, IP address
allocations can proceed under partition and it is safe to add new
peers. If this command waits without exiting, it means that there is an issue (such
as a network partition or failed peers) that is preventing a quorum
from being reached – you will need to [address
that](/site/troubleshooting.md) before moving on.
### Adding a Peer
On the new peer, at boot, via
[systemd](/site/installing-weave/systemd.md) run:
weave launch --no-restart $PEERS
Where,
* `$PEERS` is the new peer plus all other peers in the network,
initial and subsequently added, which have not been explicitly
removed. It should include peers which are temporarily offline or
stopped.
For maximum robustness, distribute an updated
`/etc/sysconfig/weave` file including the new peer to all existing
peers.
### Removing a Peer
On the peer to be removed:
weave reset
Then distribute an updated `/etc/sysconfig/weave` to the remaining
peers, omitting the removed peer from `$PEERS`.
On each remaining peer:
weave forget <removed peer>
This final step is not mandatory, but it will eliminate log noise and
spurious network traffic by stopping any reconnection attempts.
+54 -21
View File
@@ -3,11 +3,15 @@ title: Integrating Docker via the Network Plugin
menu_order: 60 menu_order: 60
--- ---
* [Launching Weave Net and Running Containers Using the Plugin](#launching)
* [Restarting the Plugin](#restarting)
* [Configuring the Plugin](#configuring)
Docker versions 1.9 and later have a plugin mechanism for adding Docker versions 1.9 and later have a plugin mechanism for adding
different network providers. Weave Net installs itself as a network plugin different network providers. Weave Net installs itself as a network
when you start it with `weave launch`. The Weave Docker Networking plugin is fast and easy to use and plugin when you start it with `weave launch`. The Weave Docker
best of all doesn't require an external cluster store in order to use it. Networking Plugin is fast and easy to use, and, unlike other
networking plugins, does not require an external cluster store.
To create a network which can span multiple Docker hosts, Weave Net peers must be connected to each other, by specifying the other hosts during `weave launch` or via To create a network which can span multiple Docker hosts, Weave Net peers must be connected to each other, by specifying the other hosts during `weave launch` or via
[`weave connect`](/site/using-weave/finding-adding-hosts-dynamically.md). [`weave connect`](/site/using-weave/finding-adding-hosts-dynamically.md).
@@ -16,22 +20,23 @@ See [Using Weave Net](/site/using-weave.md#peer-connections) for a discussion on
After you've launched Weave Net and peered your hosts, you can start containers using the following, for example: After you've launched Weave Net and peered your hosts, you can start containers using the following, for example:
$ docker run --net=weave -ti ubuntu $ docker run --net=weave -ti weaveworks/ubuntu
on any of the hosts, and they can all communicate with each other. on any of the hosts, and they can all communicate with each other
using any protocol, even multicast.
>**Warning!** It is inadvisable to attach containers to the Weave network using the Weave Docker Networking Plugin and Weave Docker API Proxy simultaneously. Such containers will end up with two Weave network interfaces and two IP addresses, which is rarely desirable. To ensure that the proxy is not being used, do not run eval $(weave env), or docker $(weave config). >**Warning!** It is inadvisable to attach containers to the Weave network using the Weave Docker Networking Plugin and Weave Docker API Proxy simultaneously. Such containers will end up with two Weave network interfaces and two IP addresses, which is rarely desirable. To ensure that the proxy is not being used, do not run `eval $(weave env)`, or `docker $(weave config) ...`.
In order to use Weave Net's [Service Discovery](/site/weavedns.md) you In order to use Weave Net's [Service Discovery](/site/weavedns.md) you
must pass the additional arguments `--dns` and `-dns-search`, for must pass the additional arguments `--dns` and `-dns-search`, for
which a helper is provided in the Weave script: which a helper is provided in the Weave script:
$ docker run --net=weave -h foo.weave.local $(weave dns-args) -tdi ubuntu $ docker run --net=weave -h foo.weave.local $(weave dns-args) -tdi weaveworks/ubuntu
$ docker run --net=weave -h bar.weave.local $(weave dns-args) -ti ubuntu $ docker run --net=weave -h bar.weave.local $(weave dns-args) -ti weaveworks/ubuntu
# ping foo # ping foo
###Launching Weave Net and Running Containers Using the Plugin ###<a name="launching"></a>Launching Weave Net and Running Containers Using the Plugin
Just launch the Weave Net router onto each host and make a peer connection with the other hosts: Just launch the Weave Net router onto each host and make a peer connection with the other hosts:
@@ -40,11 +45,11 @@ Just launch the Weave Net router onto each host and make a peer connection with
then run your containers using the Docker command-line: then run your containers using the Docker command-line:
host1$ docker run --net=weave -ti ubuntu host1$ docker run --net=weave -ti weaveworks/ubuntu
root@1458e848cd90:/# hostname -i root@1458e848cd90:/# hostname -i
10.32.0.2 10.32.0.2
host2$ docker run --net=weave -ti ubuntu host2$ docker run --net=weave -ti weaveworks/ubuntu
root@8cc4b5dc5722:/# ping 10.32.0.2 root@8cc4b5dc5722:/# ping 10.32.0.2
PING 10.32.0.2 (10.32.0.2) 56(84) bytes of data. PING 10.32.0.2 (10.32.0.2) 56(84) bytes of data.
@@ -52,7 +57,31 @@ then run your containers using the Docker command-line:
64 bytes from 10.32.0.2: icmp_seq=2 ttl=64 time=0.052 ms 64 bytes from 10.32.0.2: icmp_seq=2 ttl=64 time=0.052 ms
### Restarting the Plugin ###<a name="multi"></a>Creating multiple Docker Networks
Docker enables you to create multiple independent networks and attach
different sets of containers to each network. However, coordinating
this between hosts, Docker requires that you configure a
["key-value store"](https://docs.docker.com/engine/userguide/networking/get-started-overlay/#step-1-set-up-a-key-value-store).
If your Docker installation has a key-value store, create a network
based on Weave Net as follows:
$ docker network create --driver=weave mynetwork
then use it to connect a container:
$ docker run --net=mynetwork ...
or
$ docker network connect mynetwork somecontainer
Containers attached to different Docker Networks are
[isolated through subnets](https://www.weave.works/docs/net/latest/using-weave/application-isolation/).
###<a name="restarting"></a>Restarting the Plugin
The plugin, like all Weave Net components, is started with a policy of `--restart=always`, so that it is always there after a restart or reboot. If you remove this container (for example, when using `weave reset`) before removing all endpoints created using `--net=weave`, Docker may hang for a long time when it subsequently tries to re-establish communications to the plugin. The plugin, like all Weave Net components, is started with a policy of `--restart=always`, so that it is always there after a restart or reboot. If you remove this container (for example, when using `weave reset`) before removing all endpoints created using `--net=weave`, Docker may hang for a long time when it subsequently tries to re-establish communications to the plugin.
@@ -62,20 +91,24 @@ If you are using `systemd` with Docker 1.9, it is advised that you modify the Do
TimeoutStartSec=0 TimeoutStartSec=0
###Bypassing the Central Cluster Store When Building Docker Apps
To run a Docker cluster without a central database, you need to ensure the following: ###<a name="configuring"></a>Configuring the Plugin
1. Run in "local" scope. This tells Docker to ignore any cross-host coordination. The plugin accepts a number of configuration parameters. To supply
2. Allow Weave to handle all the cross-host coordination and to set up all networks. This is done by using the `weave launch` command. these, instead of running `weave launch`, start the router and plugin
3. Provide an IP Address Management (IPAM) driver, which links to Weave Net's own IPAM system separately with:
All cross-host coordination is handled by Weave Net's "mesh" communication, using gossipDNS and eventual consistency to avoid the need for constant communication and dependency on a central cluster store. $ weave launch-router [other peers]
$ weave launch-plugin [plugin parameters]
The plugin configuration parameters are:
* `--log-level=debug|info|warning|error` --tells the plugin
how much information to emit for debugging.
* `--no-restart` -- remove the default policy of `--restart=always`, if
you want to control start-up of the plugin yourself
**See Also** **See Also**
* [How the Weave Network Plugin Works](/site/plugin/plugin-how-it-works.md) * [How the Weave Network Plugin Works](/site/plugin/plugin-how-it-works.md)
* [Plugin Command-line Arguments](/site/plugin/plug-in-command-line.md)
-33
View File
@@ -1,33 +0,0 @@
---
title: Plugin Command-line Arguments
layout: default
---
If you need to give additional arguments to the plugin independently, don't
use `weave launch`, but instead run:
$ weave launch-router [other peers]
$ weave launch-plugin [plugin arguments]
The plugin command-line arguments are:
* `--log-level=debug|info|warning|error` --tells the plugin
how much information to emit for debugging.
* `--mesh-network-name=<name>` -- set <name> to blank to disable creation
of a default network, or include a name of your own choice.
* `--no-multicast-route` -- stops weave from adding a static IP route for
multicast traffic onto its interface
By default, multicast traffic is routed over the weave network.
To turn this off, e.g. you want to configure your own multicast
route, add the `--no-multicast-route` flag to `weave launch-plugin`.
>Note: When using the Docker Plugin, there is no need to run eval $(weave env) to enable the Proxy. Because Weave is running as a plugin within Docker, the Weave Docker API Proxy, at present, cannot detect between networks.
**See Also**
* [Using the Weave Net Docker Network Plugin](/site/plugin.md)
* [How the Weave Network Plugin Works](/site/plugin/plugin-how-it-works.md)
+10 -1
View File
@@ -1,6 +1,6 @@
--- ---
title: How the Weave Net Docker Network Plugin Works title: How the Weave Net Docker Network Plugin Works
layout: default menu_order: 20
--- ---
@@ -26,6 +26,15 @@ store, but the first part of
>**Note:** In the case of multiple networks using the `weave` driver, all containers are on the same virtual network but Docker allocates their addresses on different subnets so they cannot talk to each other directly. >**Note:** In the case of multiple networks using the `weave` driver, all containers are on the same virtual network but Docker allocates their addresses on different subnets so they cannot talk to each other directly.
The plugin accepts the following options via `docker network create ... --opt`:
* `works.weave.multicast` -- tells weave to add a static IP
route for multicast traffic onto its interface.
>**Note:** If you connect a container to multiple Weave networks, at
most one of them can have the multicast route enabled. The `weave`
network created when the plugin is first launched has the multicast
option turned on, but for any networks you create it defaults to off.
**See Also** **See Also**
+4 -4
View File
@@ -80,11 +80,11 @@ $ weave status
The terms used here are explained further at The terms used here are explained further at
[How Weave Net Works](/site/how-it-works.md). [How Weave Net Works](/site/how-it-works.md).
* **Version** - shows the Weave Net version. If checkpoint is enabled (i.e. * **Version** - shows the Weave Net version. If checkpoint is enabled (i.e.
`CHECKPOINT_DISABLE` is not set), information about existence of a new version `CHECKPOINT_DISABLE` is not set), information about existence of a new version
will be shown. will be shown.
* **Protocol**- indicates the Weave Router inter-peer * **Protocol**- indicates the Weave Router inter-peer
communication protocol name and supported versions (min..max). communication protocol name and supported versions (min..max).
* **Name** - identifies the local Weave Router as a peer on the * **Name** - identifies the local Weave Router as a peer on the
@@ -94,7 +94,7 @@ can be overridden by using the `--nickname` argument at `weave
launch`. launch`.
* **Encryption** - indicates whether * **Encryption** - indicates whether
[encryption](/how-it-works/encryption.md) is in use for communication [encryption](/site/how-it-works/encryption.md) is in use for communication
between peers. between peers.
* **PeerDiscovery** - indicates whether * **PeerDiscovery** - indicates whether
@@ -284,7 +284,7 @@ on reboot. This can be disabled via:
weave launch --no-restart weave launch --no-restart
Note that the Note that the
[Weave Net Docker API Proxy](/site/weave-docker-api/set-up-proxy.md) [Weave Net Docker API Proxy](/site/weave-docker-api.md)
is responsible for reconfiguring the Weave router and re-attaching is responsible for reconfiguring the Weave router and re-attaching
application containers to the Weave network at startup, so if you application containers to the Weave network at startup, so if you
choose not to run it you must make arrangements for this choose not to run it you must make arrangements for this
+4 -4
View File
@@ -21,13 +21,13 @@ On `$HOST1` run:
host1$ weave launch host1$ weave launch
host1$ eval $(weave env) host1$ eval $(weave env)
host1$ docker run --name a1 -ti ubuntu host1$ docker run --name a1 -ti weaveworks/ubuntu
Where, Where,
* The first line runs Weave Net. * The first line runs Weave Net.
* The second line configures the Weave Net environment, so that containers launched via the Docker command line are automatically attached to the Weave network, and, * The second line configures the Weave Net environment, so that containers launched via the Docker command line are automatically attached to the Weave network, and,
* The third line runs the application container using [Docker commands](https://docs.Docker.com/engine/reference/commandline/daemon/). * The third line runs the [application container](/site/faq.md#own-image) using [a Docker command](https://docs.docker.com/engine/reference/run/).
>**Note:** If the first command results in an error like >**Note:** If the first command results in an error like
`http:///var/run/Docker.sock/v1.19/containers/create: dial unix `http:///var/run/Docker.sock/v1.19/containers/create: dial unix
@@ -49,13 +49,13 @@ To launch Weave Net on an additional host and create a peer connection, run the
host2$ weave launch $HOST1 host2$ weave launch $HOST1
host2$ eval $(weave env) host2$ eval $(weave env)
host2$ docker run --name a2 -ti ubuntu host2$ docker run --name a2 -ti weaveworks/ubuntu
As noted above, the same steps are repeated for `$HOST2`. The only difference, besides the application container’s name, is that `$HOST2` is told to peer with Weave Net on `$HOST1` during launch. As noted above, the same steps are repeated for `$HOST2`. The only difference, besides the application container’s name, is that `$HOST2` is told to peer with Weave Net on `$HOST1` during launch.
You can also peer with other hosts by specifying the IP address, and a `:port` by which `$HOST2` can reach `$HOST1`. You can also peer with other hosts by specifying the IP address, and a `:port` by which `$HOST2` can reach `$HOST1`.
>**Note:** If there is a firewall between `$HOST1` and `$HOST2`, you must permit traffic to flow through TCP 6783 and UDP 6783/6798, which are Weave’s control and data ports. >**Note:** If there is a firewall between `$HOST1` and `$HOST2`, you must permit traffic to flow through TCP 6783 and UDP 6783/6784, which are Weave’s control and data ports.
There are a number of different ways that you can specify peers on a Weave network. You can launch Weave Net on `$HOST1` and then peer with `$HOST2`, or you can launch on `$HOST2` and peer with `$HOST1` or you can tell both hosts about each other at launch. The order in which peers are specified is not important. Weave Net automatically (re)connects to peers when they become available. There are a number of different ways that you can specify peers on a Weave network. You can launch Weave Net on `$HOST1` and then peer with `$HOST2`, or you can launch on `$HOST2` and peer with `$HOST1` or you can tell both hosts about each other at launch. The order in which peers are specified is not important. Weave Net automatically (re)connects to peers when they become available.
+6 -7
View File
@@ -26,13 +26,13 @@ specified.
Next, launch the two netcat containers onto the default subnet: Next, launch the two netcat containers onto the default subnet:
host1$ docker run --name a1 -ti ubuntu host1$ docker run --name a1 -ti weaveworks/ubuntu
host2$ docker run --name a2 -ti ubuntu host2$ docker run --name a2 -ti weaveworks/ubuntu
And then to test the isolation, launch a few more containers onto a different subnet: And then to test the isolation, launch a few more containers onto a different subnet:
host1$ docker run -e WEAVE_CIDR=net:10.2.2.0/24 --name b1 -ti ubuntu host1$ docker run -e WEAVE_CIDR=net:10.2.2.0/24 --name b1 -ti weaveworks/ubuntu
host2$ docker run -e WEAVE_CIDR=net:10.2.2.0.24 --name b2 -ti ubuntu host2$ docker run -e WEAVE_CIDR=net:10.2.2.0.24 --name b2 -ti weaveworks/ubuntu
Ping each container to confirm that they can talk to each other, but not to the containers of our first subnet: Ping each container to confirm that they can talk to each other, but not to the containers of our first subnet:
@@ -54,7 +54,7 @@ Ping each container to confirm that they can talk to each other, but not to the
If required, a container can also be attached to multiple subnets when it is started using: If required, a container can also be attached to multiple subnets when it is started using:
host1$ docker run -e WEAVE_CIDR="net:default net:10.2.2.0/24" -ti ubuntu host1$ docker run -e WEAVE_CIDR="net:default net:10.2.2.0/24" -ti weaveworks/ubuntu
`net:default` is used to request the allocation of an address from the default subnet in addition to one from an explicitly specified range. `net:default` is used to request the allocation of an address from the default subnet in addition to one from an explicitly specified range.
@@ -66,5 +66,4 @@ isolation between application containers, that feature needs to be disabled by [
**See Also** **See Also**
* [Automatic Allocation Across Multiple Subnets](/site/ipam/allocation-multi-ipam.md) * [Automatic Allocation Across Multiple Subnets](/site/ipam/allocation-multi-ipam.md)
* [Managing Services in Weave Net: Exporting, Importing, Binding and Routing](/site/using-weave/service-management.md) * [Managing Services - Exporting, Importing, Binding and Routing](/site/using-weave/service-management.md)
* [Exposing Services to the Outside World] (/site/using-weave/service-export.md)
+106
View File
@@ -0,0 +1,106 @@
---
title: Using IP Routing on an Amazon Web Services Virtual Private Cloud
menu_order: 110
---
If your container infrastructure is running entirely within Amazon Web Services (AWS)
Elastic Compute Cloud (EC2), then you can enable AWS-VPC mode with Weave Net. In AWS-VPC mode,
containers are networked without using an overlay and allows network speeds
close to that of the underlying network.
With AWS-VPC enabled, Weave Net manages IP addresses and connects
containers to the network as usual, but instead of wrapping each packet and
sending it to its destination, Weave Net instructs the AWS network
router with the ranges of container IP addresses and the instances
on which they live.
![Weave Net AWS-VPC Mode](weave-net-awsvpc-1007x438.png)
###Configuring EC2 Instances to use Weave AWS-VPC Mode
First, your AWS instances need to be given write access to the route
table via its
[IAM Role](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html).
If you have an existing IAM Role then extend it, otherwise create a
new role. The role must have a
[policy](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-policies-for-amazon-ec2.html)
attached which allows the following
[actions](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_Operations.html):
```
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:CreateRoute",
"ec2:DeleteRoute",
"ec2:ReplaceRoute",
"ec2:DescribeRouteTables",
"ec2:DescribeInstances"
],
"Resource": [
"*"
]
}
]
}
```
Secondly, your Security Group must allow network traffic between instances.
You must open TCP port 6783 which is the port that Weave Net uses to manage the network and also
allow any ports which your own containers use.
>>**Remember:** There is no network overlay in this mode, and so, IP packets with container
addresses will flow over the AWS network unmodified.
Finally, since Weave will be operating with IP addresses outside of the
range allocated by Amazon, you must disable "Source/Destination check" on each machine.
###Using AWS-VPC Mode
Launch Weave Net with the `--awsvpc` flag:
$ weave launch --awsvpc [other hosts]
>>**Note:** You will still need to supply the names or IP addresses of other hosts in
your cluster.
###Present Limitations
- AWS-VPC mode does not inter-operate with other Weave Net modes; it
is all or nothing. In this mode, all hosts in a cluster must be AWS
instances. (We hope to ease this limitation in future.)
- The `weave launch` command waits until the
[IP address manager](/site//operational-guide/concepts.md#ip-address-manager-ipam)
is ready, i.e. until after this peer has been able to make contact
with other peers and confirm that it has joined the cluster the
bridge. Without AWS-VPC, `weave launch` returns without waiting.
- The AWS network does not support multicast.
- The number of hosts in a cluster is limited by the maximum size of
your AWS route table. This is limited to 50 entries though you
can request an increase to 100 by contacting Amazon.
- All of your containers must be on the same network, with no subnet
isolation. (We hope to ease this limitation in future.)
###Packet size (MTU)
The Maximum Transmission Unit, or MTU, is the technical term for the
limit on how big a single packet can be on the network. Weave Net
defaults to 1410 bytes. This default works across almost all networks, but for better
performance you can set it to a larger MTU size.
The AWS network supports packets of up to 9000 bytes.
In AWS-VPC mode you can run the following:
$ WEAVE_MTU=9000 weave launch --awsvpc host2 host3
**See Also**
* [Using Weave Net](/site/using-weave.md)
* [Performance measurements](/blog/weave-docker-networking-performance-aws-vpc/)
(that last blog post doesn't exist yet)
+2 -2
View File
@@ -1,5 +1,5 @@
--- ---
title: Configuring Weave to Explicitly Use an IP Range title: Allocating IPs in a Specific Range
menu_order: 15 menu_order: 15
--- ---
@@ -39,4 +39,4 @@ Networks](https://en.wikipedia.org/wiki/Private_network). For example
**See Also** **See Also**
* [IP Addresses, Routes and Networks](/how-it-works/ip-addresses.md) * [IP Addresses, Routes and Networks](/site/how-it-works/ip-addresses.md)
@@ -8,13 +8,13 @@ When containers may not know the network to which they will be attached, Weave N
To illustrate... To illustrate...
host1$ C=$(docker run -e WEAVE_CIDR=none -dti ubuntu) host1$ C=$(docker run -e WEAVE_CIDR=none -dti weaveworks/ubuntu)
host1$ weave attach $C host1$ weave attach $C
10.2.1.3 10.2.1.3
where, where,
* `C=$(docker run -e WEAVE_CIDR=none -dti ubuntu)` starts a container and assigns its ID to a variable * `C=$(docker run -e WEAVE_CIDR=none -dti weaveworks/ubuntu)` starts a container and assigns its ID to a variable
* `weave attach` – the Weave Net command to attach to the specified container * `weave attach` – the Weave Net command to attach to the specified container
* `10.2.1.3` - the allocated IP address output by `weave attach`, in this case in the default subnet * `10.2.1.3` - the allocated IP address output by `weave attach`, in this case in the default subnet
+19 -1
View File
@@ -42,8 +42,26 @@ Where fastdp indicates that fast datapath is being used on a connection. If fast
$ weave status connections $ weave status connections
<- 192.168.122.25:54782 established sleeve 8a:50:4c:23:11:ae(ubuntu1204) <- 192.168.122.25:54782 established sleeve 8a:50:4c:23:11:ae(ubuntu1204)
###Packet size (MTU)
The Maximum Transmission Unit, or MTU, is the technical term for the
limit on how big a single packet can be on the network. Weave Net
defaults to 1410 bytes, but you can set a smaller size if your
underlying network has a tighter limit, or set a larger size for
better performance.
The underlying network must be able to deliver packets of the size
specified plus overheads of around 50 bytes. This requirement applies
to _every path_ between peers.
To specify a different MTU, before launching Weave Net set the
environment variable `WEAVE_MTU`. For example, for a typical "jumbo
frame" configuration:
$ WEAVE_MTU=8950 weave launch host2 host3
**See Also** **See Also**
* [Using Weave Net](/site/using-weave.md) * [Using Weave Net](/site/using-weave.md)
* [How Fastdp Works](/site/how-it-works/fastdp-how-it-works.md) * [How Fastdp Works](/site/how-it-works/fastdp-how-it-works.md)
* [Performance measurements](/blog/weave-docker-networking-performance-fast-data-path/)
@@ -1,60 +1,71 @@
--- ---
title: Adding and Removing Hosts Dynamically title: Adding and Removing Hosts Dynamically
menu_order: 50 menu_order: 90
--- ---
To add a host to an existing Weave network, launch Weave Net on the
host, and supply the address of at least one host. Weave Net
automatically discovers any other hosts in the network and establishes
connections with them if it can (in order to avoid unnecessary
multi-hop routing).
To add a host to an existing Weave network, simply launch In some situations existing Weave Net hosts may be unreachable from
Weave Net on the host, and then supply the address of at least the new host due to firewalls, etc. However, it is still possible to
one host. Weave Net automatically discovers any other hosts in add the new host, provided that inverse connections, for example, from
the network and establishes connections with them if it existing hosts to the new hosts, are available.
can (in order to avoid unnecessary multi-hop routing).
In some situations all existing Weave Net hosts may be To accomplish this, launch Weave Net onto the new host without
unreachable from the new host due to firewalls, etc. supplying any additional addresses and then, from one of the existing
However, it is still possible to add the new host, hosts run:
provided that inverse connections, for example,
from existing hosts to the new hosts, are available.
To accomplish this, launch Weave Net onto the new host
without supplying any additional addresses. And then, from one
of the existing hosts run:
host# weave connect $NEW_HOST host# weave connect $NEW_HOST
Other hosts on the Weave network will automatically attempt Any other existing hosts on the Weave network will attempt to
to establish connections to the new host as well. establish connections to the new host as well.
Alternatively, you can also instruct a peer to forget a ###Instructing Peers to Forget a Host
particular host specified to it via `weave launch` or
`weave connect` by running: To instruct a peer to forget a particular host specified to it via
`weave launch` or `weave connect` run:
host# weave forget $DECOMMISSIONED_HOST host# weave forget $DECOMMISSIONED_HOST
This prevents the peer from trying to reconnect to that host This prevents the peer from reconnecting to that host once
once connectivity to it is lost, and therefore can be used connectivity to it is lost, and can be used to administratively remove
to administratively remove any decommissioned peers any decommissioned peers from the network.
from the network.
Hosts may also be bulk-replaced. All existing hosts ###Bulk Replacing Hosts
will be forgotten, and the new hosts will be added:
Hosts can also be bulk-replaced. All existing hosts will be forgotten,
and the new hosts added:
host# weave connect --replace $NEW_HOST1 $NEW_HOST2 host# weave connect --replace $NEW_HOST1 $NEW_HOST2
For complete control over the peer topology, automatic ###Restarting Docker and Weave Net
discovery can be disabled using the `--no-discovery`
option with `weave launch`.
If discovery if disabled, Weave Net only connects to the If Weave Net is restarted by Docker it automatically remembers any
addresses specified at launch time and with `weave connect`. previous connect and forget operations, however if you stop it
manually and launch it again, it will not remember any prior connects.
If you want to launch again and retain the results of those operations
use `--resume`:
A list of all hosts that a peer has been asked to connect host# weave launch --resume
to with `weave launch` and `weave connect`
can be obtained using: > **Note:** In this case, you cannot specify a list of addresses,
> since the previous peer list is used exclusively.
For complete control over the peer topology, disable automatic
discovery using the `--no-discovery` option with `weave launch`.
If discovery if disabled, Weave Net only connects to the addresses
specified at launch time and with `weave connect`.
To return a list of all hosts and their peer connections established
with `weave launch` and `weave connect` run:
host# weave status targets host# weave status targets
**See Also** **See Also**
* [Enabling Multi-Cloud, Multi-Hop Networking and Routing](/site/using-weave/multi-cloud-multi-hop.md) * [Enabling Multi-Cloud, Multi-Hop Networking and Routing](/site/using-weave/multi-cloud-multi-hop.md)
* [Stopping and Removing Peers](/site/ipam/stop-remove-peers-ipam.md) * [Stopping and Removing Peers](/site/ipam/stop-remove-peers-ipam.md)
+20 -2
View File
@@ -1,5 +1,5 @@
--- ---
title: Integrating a Host Network with Weave Net title: Integrating with the Host Network
menu_order: 60 menu_order: 60
--- ---
@@ -38,8 +38,26 @@ Exposed addresses can also be added to weaveDNS by supplying fully qualified dom
host2$ weave expose -h exposed.weave.local host2$ weave expose -h exposed.weave.local
10.2.1.132 10.2.1.132
###<a name="routing"></a>Routing from Another Host
After running `weave expose`, you can use Linux routing to provide
access to the Weave network from hosts that are not running Weave Net:
ip route add <network-cidr> via <exposing-host>
Where,
* `<network-cidr>` is an IP address range in use on Weave Net,
for example, `10.2.0.0/16` or `10.32.0.0/12` and,
* `<exposing-host>` is the address of the machine on which you ran `weave expose`.
>**Note:** You must ensure that the [IP subnet used by Weave
Net](/site/ipam.md#range) does not clash with anything on those other
hosts.
**See Also** **See Also**
* [Using Weave Net](/site/using-weave.md) * [Using Weave Net](/site/using-weave.md)
* [Managing Services in Weave: Exporting, Importing, Binding and Routing](/site/using-weave/service-management.md) * [General information on IP Addresses, Routes and Networks](/site/how-it-works/ip-addresses.md)
* [Managing Services - Exporting, Importing, Binding and Routing](/site/using-weave/service-management.md)
+5 -5
View File
@@ -10,7 +10,7 @@ Containers are automatically allocated an IP address that is unique across the W
Weave Net detects when a container has exited and releases its allocated addresses so they can be re-used by the network. Weave Net detects when a container has exited and releases its allocated addresses so they can be re-used by the network.
See the [Automatic IP Address Management](/site/ipam.md) and also an explanation of [the basics of IP addressing](/how-it-works/ip-addresses.md) for further details. See the [Automatic IP Address Management](/site/ipam.md) and also an explanation of [the basics of IP addressing](/site/how-it-works/ip-addresses.md) for further details.
Instead of allowing Weave Net to allocate IP addresses automatically (using IPAM), there may be instances where you need to control a particular container or a cluster by setting an IP address for it. Instead of allowing Weave Net to allocate IP addresses automatically (using IPAM), there may be instances where you need to control a particular container or a cluster by setting an IP address for it.
@@ -21,12 +21,12 @@ For example, we can launch a couple of containers on `$HOST1` and
On `$HOST1`: On `$HOST1`:
host1$ docker run -e WEAVE_CIDR=10.2.1.1/24 -ti ubuntu host1$ docker run -e WEAVE_CIDR=10.2.1.1/24 -ti weaveworks/ubuntu
root@7ca0f6ecf59f:/# root@7ca0f6ecf59f:/#
And `$HOST2`: And `$HOST2`:
host2$ docker run -e WEAVE_CIDR=10.2.1.2/24 -ti ubuntu host2$ docker run -e WEAVE_CIDR=10.2.1.2/24 -ti weaveworks/ubuntu
root@04c4831fafd3:/# root@04c4831fafd3:/#
Then test that the container on `$HOST2` can be reached from the container on `$HOST1`: Then test that the container on `$HOST2` can be reached from the container on `$HOST1`:
@@ -51,7 +51,7 @@ Individual IP addresses given to containers must, of course, be unique. If you p
**See Also** **See Also**
* [Managing Services: Exporting, Importing, Binding and Routing](/site/using-weave/service-management.md) * [Managing Services - Exporting, Importing, Binding and Routing](/site/using-weave/service-management.md)
* [Configuring Weave to Explicitly Use an IP Range](/site/using-weave/configuring-weave.md) * [Allocating IPs in a Specific Range](/site/using-weave/configuring-weave.md)
* [Automatic IP Address Management](/site/ipam.md) * [Automatic IP Address Management](/site/ipam.md)
@@ -45,5 +45,5 @@ Be aware that:
**See Also** **See Also**
* [Weave Encryption](/how-it-works/encryption.md) * [Weave Encryption](/site/how-it-works/encryption.md)
* [Using Fast Datapath](/site/using-weave/fastdp.md) * [Using Fast Datapath](/site/using-weave/fastdp.md)
-39
View File
@@ -1,39 +0,0 @@
---
title: Exposing Services to the Outside World
menu_order: 70
---
Services running in containers on a Weave network can be made
accessible to the outside world (and, more generally, to other networks)
from any Weave Net host, irrespective of where the service containers are
located.
Returning to the [netcat example service](/site/using-weave.md),
you can expose the netcat service running on `HOST1` and make it accessible to the outside world via `$HOST2`.
First, expose the application network to `$HOST2`, as explained in [Integrating a Host Network with a Weave Net](/site/using-weave/host-network-integration.md):
host2$ weave expose
10.2.1.132
Then add a NAT rule that routes the traffic from the outside world to the destination container service.
host2$ iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 2211 \
-j DNAT --to-destination $(weave dns-lookup a1):4422
In this example, it is assumed that the "outside world" is connecting to `$HOST2` via 'eth0'. The TCP traffic to port 2211 on the external IPs will be routed to the 'nc' service running on port 4422 in the container a1.
With the above in place, you can connect to the 'nc' service from anywhere using:
echo 'Hello, world.' | nc $HOST2 2211
>**Note:** Due to the way routing is handled in the Linux kernel, this won't work when run *on* `$HOST2`.
Similar NAT rules to the above can be used to expose services not just to the outside world but also to other, internal, networks.
**See Also**
* [Using Weave Net](/site/using-weave.md)
* [Managing Services in Weave Net: Exporting, Importing, Binding and Routing](/site/using-weave/service-management.md)
+16 -12
View File
@@ -1,6 +1,6 @@
--- ---
title: Managing Services in Weave Net - Exporting, Importing, Binding and Routing title: Managing Services - Exporting, Importing, Binding and Routing
menu_order: 90 menu_order: 70
--- ---
This section contains the following topics: This section contains the following topics:
@@ -13,31 +13,35 @@ This section contains the following topics:
###<a name="exporting"></a> Exporting Services ###<a name="exporting"></a>Exporting Services
Services running in containers on a Weave network can be made accessible to the outside world (and, more generally, to other networks) from any Weave host, regardless of where the service containers are located. Services running in containers on a Weave network can be made
accessible to the outside world (and, more generally, to other networks)
from any Weave Net host, irrespective of where the service containers are
located.
Turning back to the [netcat example](/site/using-weave.md), say you want the netcat service, which is running in a container on `$HOST1`, to be accessible to the outside world via `$HOST2`. Returning to the [netcat example service](/site/using-weave.md),
you can expose the netcat service running on `HOST1` and make it accessible to the outside world via `$HOST2`.
To do this, expose the application network to `$HOST2`, as explained in [Host Network Integration](/site/using-weave/host-network-integration.md) by running: First, expose the application network to `$HOST2`, as explained in [Integrating with the Host Network](/site/using-weave/host-network-integration.md):
host2$ weave expose host2$ weave expose
10.2.1.132 10.2.1.132
Then add a NAT rule to route from the outside world to the destination container service. Then add a NAT rule that routes the traffic from the outside world to the destination container service.
host2$ iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 2211 \ host2$ iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 2211 \
-j DNAT --to-destination $(weave dns-lookup a1):4422 -j DNAT --to-destination $(weave dns-lookup a1):4422
Here it is assumed that the "outside world" is connecting to `$HOST2` via 'eth0'. The TCP traffic to port 2211 on the external IPs will be routed to the 'nc' service, running in the a1 container and listening on port 4422. In this example, it is assumed that the "outside world" is connecting to `$HOST2` via 'eth0'. The TCP traffic to port 2211 on the external IPs will be routed to the 'nc' service running on port 4422 in the container a1.
With the above in place, you are ready to connect to the 'nc' service from anywhere using:
$ echo 'Hello, world.' | nc $HOST2 2211 With the above in place, you can connect to the 'nc' service from anywhere using:
echo 'Hello, world.' | nc $HOST2 2211
>**Note:** Due to the way routing is handled in the Linux kernel, this won't work when run *on* `$HOST2`. >**Note:** Due to the way routing is handled in the Linux kernel, this won't work when run *on* `$HOST2`.
Similar NAT rules to the above can used to expose services not just to the outside world but also other, internal, networks. Similar NAT rules to the above can be used to expose services not just to the outside world but also to other, internal, networks.
###<a name="importing"></a>Importing Services ###<a name="importing"></a>Importing Services
Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

+1 -1
View File
@@ -71,7 +71,7 @@ to the original settings prior to stopping the proxy.
**See Also** **See Also**
* [Using The Weave Docker API Proxy](/site/weave-docker-api/using-proxy.md) * [Using The Weave Docker API Proxy](/site/weave-docker-api/using-proxy.md)
* [Securing Docker Communications With TLS](securing-proxy.md) * [Securing Docker Communications With TLS](/site/weave-docker-api/securing-proxy.md)
* [Launching Containers With Weave Run (without the Proxy)](/site/weave-docker-api/launching-without-proxy.md) * [Launching Containers With Weave Run (without the Proxy)](/site/weave-docker-api/launching-without-proxy.md)
@@ -37,7 +37,7 @@ For example, you can launch the proxy using all three flags, as follows:
After launching the Weave Net proxy with these flags, running a container named `aws-12798186823-foo` without labels results in weaveDNS registering the hostname `my-app-foo` and not `aws-12798186823-foo`. After launching the Weave Net proxy with these flags, running a container named `aws-12798186823-foo` without labels results in weaveDNS registering the hostname `my-app-foo` and not `aws-12798186823-foo`.
host1$ docker run -ti --name=aws-12798186823-foo ubuntu ping my-app-foo host1$ docker run -ti --name=aws-12798186823-foo weaveworks/ubuntu ping my-app-foo
PING my-app-foo.weave.local (10.32.0.2) 56(84) bytes of data. PING my-app-foo.weave.local (10.32.0.2) 56(84) bytes of data.
64 bytes from my-app-foo.weave.local (10.32.0.2): icmp_seq=1 ttl=64 time=0.027 ms 64 bytes from my-app-foo.weave.local (10.32.0.2): icmp_seq=1 ttl=64 time=0.027 ms
64 bytes from my-app-foo.weave.local (10.32.0.2): icmp_seq=2 ttl=64 time=0.067 ms 64 bytes from my-app-foo.weave.local (10.32.0.2): icmp_seq=2 ttl=64 time=0.067 ms
@@ -45,7 +45,7 @@ After launching the Weave Net proxy with these flags, running a container named
Also, running a container named `foo` with the label Also, running a container named `foo` with the label
`hostname-label=aws-12798186823-foo` leads to the same hostname registration. `hostname-label=aws-12798186823-foo` leads to the same hostname registration.
host1$ docker run -ti --name=foo --label=hostname-label=aws-12798186823-foo ubuntu ping my-app-foo host1$ docker run -ti --name=foo --label=hostname-label=aws-12798186823-foo weaveworks/ubuntu ping my-app-foo
PING my-app-foo.weave.local (10.32.0.2) 56(84) bytes of data. PING my-app-foo.weave.local (10.32.0.2) 56(84) bytes of data.
64 bytes from my-app-foo.weave.local (10.32.0.2): icmp_seq=1 ttl=64 time=0.031 ms 64 bytes from my-app-foo.weave.local (10.32.0.2): icmp_seq=1 ttl=64 time=0.031 ms
64 bytes from my-app-foo.weave.local (10.32.0.2): icmp_seq=2 ttl=64 time=0.042 ms 64 bytes from my-app-foo.weave.local (10.32.0.2): icmp_seq=2 ttl=64 time=0.042 ms
+5 -5
View File
@@ -1,6 +1,6 @@
--- ---
title: Automatic IP Allocation and the Weave Proxy title: Automatic IP Allocation and the Weave Proxy
layout: default menu_order: 25
--- ---
@@ -8,16 +8,16 @@ If [automatic IP address allocation](/site/ipam.md) is enabled in Weave Net (by
then containers started via the proxy are automatically assigned an IP address, *without having to specify any then containers started via the proxy are automatically assigned an IP address, *without having to specify any
special environment variables or any other options*. special environment variables or any other options*.
host1$ docker run -ti ubuntu host1$ docker run -ti weaveworks/ubuntu
To use a specific subnet, you can pass a `WEAVE_CIDR` to the container, for example: To use a specific subnet, you can pass a `WEAVE_CIDR` to the container, for example:
host1$ docker run -ti -e WEAVE_CIDR=net:10.32.2.0/24 ubuntu host1$ docker run -ti -e WEAVE_CIDR=net:10.32.2.0/24 weaveworks/ubuntu
To start a container without connecting it to the Weave network, pass To start a container without connecting it to the Weave network, pass
`WEAVE_CIDR=none`, for example: `WEAVE_CIDR=none`, for example:
host1$ docker run -ti -e WEAVE_CIDR=none ubuntu host1$ docker run -ti -e WEAVE_CIDR=none weaveworks/ubuntu
###Disabling Automatic IP Address Allocation ###Disabling Automatic IP Address Allocation
@@ -33,7 +33,7 @@ Containers started with a `WEAVE_CIDR` environment variable are handled as befor
To automatically assign an address in this mode, start the To automatically assign an address in this mode, start the
container with a blank `WEAVE_CIDR`, for example: container with a blank `WEAVE_CIDR`, for example:
host1$ docker run -ti -e WEAVE_CIDR="" ubuntu host1$ docker run -ti -e WEAVE_CIDR="" weaveworks/ubuntu
**See Also** **See Also**
@@ -7,7 +7,7 @@ menu_order: 50
If you don't want to use the proxy, you can also launch If you don't want to use the proxy, you can also launch
containers on to the Weave network using `weave run`: containers on to the Weave network using `weave run`:
$ weave run -ti ubuntu $ weave run -ti weaveworks/ubuntu
The arguments after `run` are passed through to `docker run`. Therefore you The arguments after `run` are passed through to `docker run`. Therefore you
can freely specify whatever Docker options you need. can freely specify whatever Docker options you need.
@@ -17,14 +17,14 @@ this example, it obtains an automatically allocated IP.
You can specify IP addresses manually instead: You can specify IP addresses manually instead:
$ weave run 10.2.1.1/24 -ti ubuntu $ weave run 10.2.1.1/24 -ti weaveworks/ubuntu
`weave run` rewrites `/etc/hosts` in the same way `weave run` rewrites `/etc/hosts` in the same way
[the proxy does](/site/weave-docker-api/name-resolution-proxy.md). If you need to keep [the proxy does](/site/weave-docker-api/name-resolution-proxy.md). If you need to keep
the original file, specify `--no-rewrite-hosts` when running the original file, specify `--no-rewrite-hosts` when running
the container: the container:
$ weave run --no-rewrite-hosts 10.2.1.1/24 -ti ubuntu $ weave run --no-rewrite-hosts 10.2.1.1/24 -ti weaveworks/ubuntu
There are some limitations to starting containers using `weave run`: There are some limitations to starting containers using `weave run`:
Loaded 100 of 155 files, more files were not shown because too many files have changed in this diff. Show more