Compare commits

...
138 Commits
Author SHA1 Message Date
Bryan Boreham 59fe76f29b Add release v2.1.2 2017-11-27 14:06:33 +00:00
Bryan Boreham 2ab01b55c9 Merge branch 'master' into 2.1 2017-11-27 11:45:15 +00:00
Bryan Boreham 461f9e736b Merge pull request #3186 from weaveworks/issues/3184-update-mesh
Update vendored mesh dep
2017-11-27 11:34:15 +00:00
Bryan Boreham 8e16b03ae8 Merge pull request #3181 from weaveworks/issues/3177-ooo
npc: Track ipset entries per user and refcount default-allow ipsets
2017-11-27 11:01:59 +00:00
Martynas Pumputis 6469809b9a Update vendored mesh dep 2017-11-22 16:07:19 +02:00
Martynas Pumputis 0a0dbe115a npc: Track ipset entries per user
There was a bug in handling "default-allow" ipsets - it could not
cope with out of order receive of Pod updates. E.g:

    1) AddPod(Pod_A, PodIP_A),
    2) AddPod(Pod_B, PodIP_A),
    3) DeletePod(Pod_A),

would result with PodIP_A being removed from "default-allow" ipset,
as such ipset entries were not refcounted. Bypassing of refcounting
was due to the fact that adding a netpol which dst-selects a Pod which
IP addr is in "default-allow" ipset has to remove the IP addr from
the ipset regardless of refcount value of the IP addr entry in the
ipset. So, refcounting in such case didn't make sense and it was
bypassed.

This commit changes the way entries are refcounted in ipsets, and
it makes "default-allow" ipset the same citizen as other ipsets, so
from now "default-allow" ipset is refcounted.
2017-11-21 14:41:29 +02:00
Bryan Boreham ebbf3d4244 Add release v2.1.1 2017-11-17 14:17:31 +00:00
Bryan Boreham ceb36d1891 Typo 2017-11-17 14:15:10 +00:00
Bryan Boreham 4caa850853 Merge pull request #3176 from weaveworks/issues/3175-fix-install
Fix 2.1.0 install issues
2017-11-17 14:14:41 +00:00
Bryan Boreham 96cecf1796 Pull images before running inside weaveexec container
Now we don't bundle the docker command inside weaveexec, we need to do
an explicit 'docker pull' to ensure the images are there.

Also set the weavedb tag to :latest to remove a warning from docker
2017-11-17 13:51:10 +00:00
Bryan Boreham 51c7af8fb4 Set IMAGE before using it 2017-11-17 13:31:46 +00:00
Bryan Boreham 347466aba2 Add release v2.1.0 2017-11-17 11:10:52 +00:00
Bryan Boreham bee1c44859 Merge pull request #3173 from weaveworks/reclaim-tweaks
Rearrange peer reclaim checks in weave-kube launch script
2017-11-16 17:05:46 +00:00
Bryan Boreham f07238c8bb Move info log to after error check 2017-11-16 14:31:40 +00:00
Bryan Boreham 3b499ce27c Rearrange peer reclaim checks in weave-kube launch script
This is to make it behave better on fresh start, and in the case where
the manifest has not been updated to give it permissions.
2017-11-16 14:14:54 +00:00
Bryan Boreham 4dda216398 Merge pull request #3172 from weaveworks/issues/3170-fix-upgrade
weave-kube reclaim: check for upgrade case
2017-11-15 17:59:54 +00:00
Bryan Boreham f0e511510a Don't print a message on flag not found 2017-11-15 12:35:19 +00:00
Bryan Boreham 570ab0b10b Avoid deleting persisted data on weave-kube upgrade
Add a feature to weaveutil to get and set flags, then use this feature
to mark the DB when this version runs
2017-11-15 11:26:43 +00:00
Bryan Boreham 214cf6cb2e Flip result code when checking for new peer
This avoids mistaking an error condition for the result we should act on
2017-11-15 10:11:02 +00:00
Bryan Boreham 07459f0cec Merge pull request #3149 from weaveworks/fix-2797-loosing-ipam-ips
Fix 2797 on Kubernetes - Cluster goes down because all IPs become unreachable
2017-11-14 15:31:06 +00:00
Brice Fernandes 90f4f7b62c Add test to ensure that weave recovers unreachable IPs on launch 2017-11-14 14:18:21 +00:00
Brice Fernandes 987ac98fbf Add up command to integration tests to make launching cluster easier 2017-11-14 14:16:01 +00:00
Brice Fernandes 49ed40a301 Add test function to recover clean output from command run on remote host 2017-11-14 14:16:01 +00:00
Bryan Boreham c67bc704e1 Temporarily raise kube-peers log level to debug 2017-11-14 14:16:01 +00:00
Bryan Boreham 3501ad45e6 Cope with a Kubernetes node being deleted and coming back
Add features to weaver and kube-peers to find the peer name and check
if it is in the list, then in launch.sh delete persisted data if not.
2017-11-14 14:15:31 +00:00
Bryan Boreham 70a13c9c12 Make kube-peers logging consistent and add -log-level flag
Also tag kube-peer log messages with "[kube-peers]": in a standard
deployment they end up interspersed with weaver log messages so this
makes it easier to pick them out.
2017-11-14 14:06:34 +00:00
Brice Fernandes 3afb8d3ddf Add log messages to detect dying peer edge case 2017-11-14 14:06:34 +00:00
Brice Fernandes fd2f91e2ee Refactor: Improve naming 2017-11-14 14:06:33 +00:00
Brice Fernandes cb0c9e5fba Document: Add file-level docstrings for kube-peers 2017-11-14 14:06:33 +00:00
Brice Fernandes 6a0e39e5ee Minor refactor for clarity 2017-11-14 14:06:33 +00:00
Bryan Boreham a552f194b4 Code to reclaim IP address space from removed peers 2017-11-14 14:06:33 +00:00
Bryan Boreham 267bd8b51d Pseudo-code to clean up dead peers 2017-11-14 14:06:33 +00:00
Brice Fernandes 497dddabc1 Improve config locking by using optimistic locking strategy
Loop using jitter until the update succeeds
2017-11-14 14:06:33 +00:00
Bryan Boreham 71b39489da Maintain a list of peers in a Kubernetes annotation
This is prework for a feature which will remove 'dead' peers
2017-11-14 14:06:33 +00:00
Bryan Boreham ed8d92cfc6 RBAC to read and write ConfigMaps 2017-11-14 14:06:33 +00:00
Bryan Boreham f431ee289b Refactor: create config in main function 2017-11-14 14:06:33 +00:00
Martynas Pumputis 7ea82204e0 Merge pull request #3089 from weaveworks/improve-flags
Improve flag handling
2017-11-13 17:31:33 +02:00
Bryan Boreham 44692b997c Merge pull request #3169 from weaveworks/issues/k8s-netpol-testing
Extend NetworkPolicy smoke tests
2017-11-13 15:30:48 +00:00
Bryan Boreham 21086a0b2a Merge branch '2.0' 2017-11-13 12:51:29 +00:00
Bryan Boreham d7119169df Add length to log message for too-big DF broadcast frame 2017-11-13 11:06:34 +00:00
Martynas Pumputis e1eb0a8617 npc: Fix typo 2017-11-13 10:41:45 +02:00
Martynas Pumputis cacc190764 npc: Extend NetworkPolicy test cases in 840
- Add a test case for default allow all ingress traffic.
- Fix misleading comment.
2017-11-13 09:40:07 +02:00
Brice Fernandes 89e5d935a6 Document test runner feature to pick number of hosts
Merge pull request #3164 from weaveworks/improve-test-docs
2017-11-09 17:00:31 +00:00
Brice Fernandes 884e1d7a6b Document test runner feature to pick number of hosts 2017-11-09 15:48:21 +00:00
Bryan Boreham 5b1ca02ecc Merge pull request #3151 from weaveworks/issues/3105-k8s-17-netpolicies-whitelist
Implement Kubernetes 1.7 NetworkPolicy semantics
2017-11-07 16:31:01 +00:00
Martynas Pumputis f51347feac npc: Address PR comments
* Add comments about presence of `analysePolicyLegacy` and `analysePolicy`.
* Use non-legacy netpol in TestRegressionPolicyNamespaceOrdering3059.
2017-11-07 15:40:58 +02:00
Martynas Pumputis b605b832ed npc: Extend k8s NetworkPolicy smoke tests
- Test that pods not matching ingress rules are not able to access
  the nettest pods.
- Test upgrading of NPC (from legacy to non-legacy).
- Test new semantics of k8s netpol.
2017-11-07 14:55:03 +02:00
Bryan Boreham e148b72284 Merge branch '2.0' 2017-11-06 11:48:29 +00:00
Bryan Boreham 16db921349 Merge pull request #3128 from weaveworks/k8s-notes
Added 'things to watch out for' section in Kubernetes instructions
2017-11-03 10:59:35 +00:00
Bryan Boreham 4c235b3648 Merge pull request #3160 from weaveworks/2017-11-02-update-gopacket
Update gopacket to latest master to benefit from memory optimization
2017-11-03 10:32:35 +00:00
Damien Lespiau 1a8d479227 Update gopacket to latest master to benefit from memory optimization
Upstream has done some work saving 133k allocated objects on startup (init()),
which translates into a saving of 30-50MB RSS memory.

Details: https://github.com/google/gopacket/pull/377#issuecomment-338984309
2017-11-02 17:07:31 +00:00
Matthias Radestock 7aad364116 Merge pull request #3158 from weaveworks/2.0
merge branch 2.0
2017-10-31 08:11:17 +00:00
Matthias Radestock 4958d9e423 Merge pull request #3156 from weaveworks/3153-v2-plugin-dockerhub
document using v2 plugin from dockerhub rather than store

Fixes #3153.
2017-10-31 07:08:50 +00:00
Matthias Radestock 474f1f3673 document using v2 plugin from dockerhub rather than store
Since the latter is hard to update and hence frequently out of date.

Fixes #3153.
2017-10-30 20:43:45 +00:00
Martynas Pumputis 9aa7044f1e npc: Add unit tests for default-allow ipsets 2017-10-22 19:42:08 +02:00
Martynas Pumputis 7c39f96d08 npc: Implement networking.k8s.io/v1/NetworkPolicy semantics (k8s 1.7)
The main difference between new and legacy (pre-1.7) netpol semantics
are the following:

    * The DenyDefault ingress isolation annotation is obsoleted.
    * Traffic is allowed to a pod if there are no netpol selecting
      the pod.

See https://kubernetes.io/docs/api-reference/v1.6/#networkpolicyspec-v1beta1-extensions
vs https://kubernetes.io/docs/api-reference/v1.7/#networkpolicyspec-v1-networking
for more details.

weave-npc can operate either in the legacy or the new mode (default). To
use the legacy, pass "--use-legacy-netpol" arg when starting the npc.

In non-legacy mode we create for each Namespace a default-allow ipset
which stores IP addrs of such Pods which are not selected by any
NetworkPolicy.The ipsets are dst's in rules of the WEAVE-NPC-DEFAULT chain.
2017-10-22 19:37:18 +02:00
Martynas Pumputis 3c3e0b0352 Enable access to "networkpolicies" of "networking.k8s.io" API group
Since 1.7, k8s netpol is no longer in beta, so NPC can start subscribing
from the non-beta API group.
2017-10-22 19:32:52 +02:00
Martynas Pumputis 8467742d6d Increase smoke test timeout from 240 to 300 sec
The 840 smoke test (k8s + npc) sometimes might take longer than 240 sec.
2017-10-22 19:29:52 +02:00
Martynas Pumputis 5cfc3f8c47 Bump k8s version in smoke tests to 1.7.8 2017-10-22 19:29:21 +02:00
Bryan Boreham 62dbe03193 Don't log nil errors in CNI DEL 2017-10-17 16:13:48 +00:00
Martynas Pumputis 8755da4b5a Add smoke tests to check FORWARDing after 'expose' 2017-10-17 12:08:43 +01:00
Martynas Pumputis 25cff16bd1 Create WEAVE-EXPOSE iptables chain
Docker 1.13 has changed a default policy of FORWARD chain to DROP
(https://github.com/moby/moby/pull/28257) which makes containers
inaccessible from a remote host when the bridge is exposed.

The change breaks e.g. the AWSVPC mode. To overcome this we install
an explicit rule for accepting forwarded ingress traffic to an
exposed subnet which is appended to the WEAVE-EXPOSE chain.

The chain is a target of the rule "-t filter -A FORWARD -o weave".
2017-10-17 12:08:43 +01:00
Martynas Pumputis ade6738789 Handle "weave expose" in weaver
The change makes "weave expose" to call the "/expose" HTTP
endpoint implemented in router which exposes the bridge.

Such architectural change required as we need to know whether NPC is
enabled when exposing the bridge and weaver knows this fact the best.

Unfortunately, this change makes impossible to "weave expose"
when weaver is not running.
2017-10-17 12:08:43 +01:00
Bryan Boreham ea0e3a14e0 Merge branch '2.0' 2017-10-17 10:17:25 +00:00
Bryan Boreham 4a1a1e83c8 Update Python 'setuptools' module to resolve issue
Symptom of issue was this message:

error in cryptography setup command: Invalid environment marker: platform_python_implementation != 'PyPy'
2017-10-17 10:17:06 +00:00
Bryan Boreham ee48700b40 Merge pull request #3141 from bjhaid/upgrade_client_go
Upgrading client go and vendoring its extra dependencies.
2017-10-11 16:08:00 +01:00
Bryan Boreham 0791ce8ab0 Merge branch '2.0' 2017-10-11 10:49:13 +00:00
Ayodele Abejide 1ac543aba7 Upgrading client go and vendoring it's extra dependencies. 2017-10-10 16:48:35 +00:00
Bryan Boreham 3d5f80630d Merge pull request #3142 from weaveworks/fixnotes
Fixnotes
2017-10-10 16:10:00 +01:00
abuehrle be2da07735 fix note pins 2017-10-10 09:46:35 -05:00
Bryan Boreham 276d0b621b Add release v2.0.5 2017-10-05 11:57:23 +00:00
Martynas Pumputis 6733dac9fc Merge remote-tracking branch 'origin/issues/3136-fix-etc-hosts' into 2.0 2017-10-05 13:18:41 +02:00
Martynas Pumputis 80b286ebf7 Merge pull request #3138 from weaveworks/issues/3136-fix-etc-hosts
Add back EXEC_IMAGE so proxy can docker-run itself

Fixes #3136
2017-10-05 13:14:16 +02:00
Bryan Boreham 2c143612e2 Add back EXEC_IMAGE so proxy can exec itself 2017-10-05 11:01:57 +00:00
Bryan Boreham be412812f9 Remove unused variable assignment 2017-10-05 10:45:51 +00:00
Bryan Boreham bfed7c24f8 Merge pull request #3129 from mkumatag/ppc64le_suuport
Add ppc64le support
2017-10-04 17:29:30 +01:00
Bryan Boreham fe859952e0 Merge branch '2.0' 2017-10-04 16:05:07 +00:00
Bryan Boreham 86361c5b60 Merge pull request #3137 from weaveworks/lint-nonsense
Remove explicit error check at end of functions to placate lint
2017-10-04 16:59:01 +01:00
Bryan Boreham 35c017a787 Remove explicit error check at end of functions to placate lint
It's sad because this changes a regular pattern into an irregular one,
which will create spurious diffs if more work is added at the end of
the function.  Still, this is easier than fixing the lint runner to
ignore the report.
2017-10-04 13:40:13 +00:00
Bryan Boreham 79d65bc98b Report errors from rewriting /etc/hosts 2017-10-04 13:32:52 +00:00
Brice Fernandes 6bc69199d3 Merge pull request #3135 from weaveworks/fix-build-osx
Fix builds on OSX
2017-10-04 14:00:18 +01:00
Brice Fernandes 292cbc484a Fix other occurences of GNU-specific sed invocation 2017-10-04 12:12:28 +01:00
Brice Fernandes 1a98cbfff9 Ignore sed backup files 2017-10-04 12:03:12 +01:00
Brice Fernandes 071f077cc5 Fix broken builds on OSX due to BSD vs GNU sed 2017-10-04 11:58:14 +01:00
Bryan Boreham c16653a396 Mount iptables lock file in weave-kube (#3134)
Mount iptables lock file in weave-kube

iptables uses the file `/run/xtables.lock` so that if changes are
under way, another instance of the iptables command can wait till it
is finished.
We need to mount the lock file from the host to the container in order
for this mechanism to work at all.
Note we rely on kubelet touching the file before running any pods -
see https://github.com/kubernetes/kubernetes/pull/47212
2017-10-03 14:52:49 +01:00
Damien Lespiau b157544394 Bump license copyright year to 2017 (#3132) 2017-10-03 14:52:03 +01:00
Matthias Radestock 18f65a1914 fix typo 2017-10-01 21:12:29 +01:00
Bryan Boreham ba16135b3a Escape a minus that is being interpreted as a bullet (#3131) 2017-09-29 14:16:13 +01:00
Bryan Boreham df688cfb53 Added weave_ipam_unreachable_percentage metric 2017-09-26 15:55:38 +00:00
Bryan Boreham 88679d66fc Improve wording 2017-09-26 15:53:14 +00:00
Carlos Alexandro Becker ab1cbcbbf8 prometheus: export count of unreachable peers (#3119)
* prometheus: export count of unreachable peers
* extracted part of printIPAMRanges
2017-09-26 16:50:10 +01:00
Bryan Boreham a02fb753df Use the configured Docker host for the proxy, not the default one 2017-09-26 09:44:46 +00:00
Bryan Boreham 98b002f7c0 Remove unnecessary 'Version' in proxy 2017-09-26 09:44:46 +00:00
Bryan Boreham e251bfe807 Move use of command-line params to after we know them 2017-09-26 09:44:46 +00:00
Bryan Boreham eb88893312 Rename configureProxy to newProxyConfig 2017-09-26 09:44:46 +00:00
Bryan Boreham a362bf68bb Merge branch '2.0' 2017-09-26 09:41:29 +00:00
Bryan Boreham 3c53aa66b7 Improved wording after review 2017-09-26 09:41:11 +00:00
Bryan Boreham c97d4731dc Merge pull request #3113 from weaveworks/plugin-v2-troubleshooting
Add instructions on troubleshooting the V2 plugin
2017-09-26 10:40:29 +01:00
Bryan Boreham a69546c9ee Reworded on review feedback 2017-09-26 09:39:04 +00:00
Manjunath A Kumatagi b0f78e97f4 Add ppc64le support 2017-09-26 13:49:35 +05:30
Bryan Boreham 49f7e38133 Removed stray repetition of anchor 2017-09-25 16:38:07 +00:00
Bryan Boreham 2e7f44feee Added 'things to watch out for' 2017-09-25 16:36:57 +00:00
Matthias Radestock 48ceb49832 refactor: inline and simplify proxy_addrs() 2017-09-18 09:16:37 +01:00
Matthias Radestock dd772f04fe cosmetic: move cidr functions to better place 2017-09-18 08:57:50 +01:00
Matthias Radestock 47597c941d refactor(ish): create volumes just before weave container
It's the most logical sequencing and reduces the chances of debris
being left behind when things go wrong.
2017-09-18 08:49:02 +01:00
Matthias Radestock c0e8af56e2 refactor: inline attach() 2017-09-18 08:24:13 +01:00
Matthias Radestock 72c0172c48 refactor: inline rewrite_etc_hosts 2017-09-18 07:37:01 +01:00
Matthias Radestock 507f5345c5 refactor: inline kill_container 2017-09-18 07:24:34 +01:00
Matthias Radestock 79d4aec1ec cosmetic: move kill_container to correct section 2017-09-18 07:18:27 +01:00
Matthias Radestock 89c7e6d337 refactor weave setup for clarity 2017-09-17 16:33:37 +01:00
Bryan Boreham b8d7af3822 Don't call 'docker pull' inside weaveexec 2017-09-16 18:51:58 +00:00
Bryan Boreham 3c9c968174 Merge pull request #3110 from dtshepherd/issues/2957-rm-docker-binary-from-weaveexec
Remove docker binary from weaveexec container
2017-09-13 13:38:07 +01:00
David Shepherd 38ca0f90c7 Simplify mac generation on attach|expose|hide
`weaveexec` no longer mounting '/' so we can't access the weavedb file
to get persistent peer ID.  If `weaver` was running, we couldn't have
read the file anyway because BoltDB does not support concurrent access
from two processes.

Consequence is that if someone really does need the bridge creation on
'weave expose' the peer ID may change if created pre-1.9.0.  We'll just
live with that (per @bboreham).
2017-09-13 08:04:04 -04:00
David Shepherd 8bf08c3cf8 Remove docker binary from weaveexec container
Fixes #2957

- replace `docker stop` with `weaveuitl stop-container`
- replace `docker kill` with `weaveutil kill-container`
- replace `docker rm` with `weaveutil remove-container`
- replace `docker run` with minimal `weaveutil container-run`
- replace `docker inspect -f {{.ID}}` with `weaveutil container-id`
- replace data-only container `docker create` with `weaveutil create-volume-container`
- remove `docker inspect` dependencies from `weave` script
  `rewrite_etc_hosts` and `with_container_fqdn`
- rework `weave` script `check_running` and `check_not_running` to use `weaveutil`
- rework `weave version` to use `weaver` /report API or `weaver --version` if not running
- rework `weave setup`'s `docker pull` to run locally rather than in `weaveexec` container
2017-09-13 08:03:58 -04:00
Martynas Pumputis 14c79d2184 Merge remote-tracking branch 'origin/2.0' 2017-09-09 13:17:29 +02:00
Martynas Pumputis b0519af573 Merge pull request #3114 from weaveworks/issues/3103-rm-pluginv2-db
Remove db file for v2 plugin

Fixes #3103
2017-09-09 12:44:51 +02:00
Bryan Boreham dc7acd6c70 Remove db file for v2 plugin 2017-09-08 13:23:26 +00:00
Bryan Boreham d63a21363e Add instructions on troubleshooting the V2 plugin 2017-09-08 11:11:44 +00:00
Bryan Boreham a330dd4970 Improve "What is Weave Cloud" link 2017-09-07 19:39:50 +00:00
Bryan Boreham c0388c0136 Improve "What is Weave Cloud" link 2017-09-07 20:32:24 +01:00
Bryan Boreham aeca987a4c Merge branch '2.0' 2017-08-25 15:25:55 +00:00
Bryan Boreham 3aff5f2467 Add release v2.0.4 2017-08-25 14:57:02 +00:00
Bryan Boreham 0c63a8d0e5 Merge pull request #3100 from weaveworks/wip-3098
fix: Test for ipset comment support
2017-08-24 17:29:44 +01:00
Mike Bryant 65b45e8dd1 fix: Test for ipset comment support
During initialisation try and create an ipset with the comment extension
If this fails, disable comment support

Fixes #3098
2017-08-24 11:14:16 +01:00
Bryan Boreham 03885d4b40 Merge pull request #3070 from weaveworks/update-kube-addon-doc
Update kube addon doc
2017-08-24 09:49:58 +01:00
Bryan Boreham 40e78fea99 Add a screen shot of Weave Net status in Weave Cloud 2017-08-24 08:38:05 +00:00
Bryan Boreham ecba88e445 Clarify the wording of weave-kube troubleshooting
and note that statement about deleting old pods applies only to pre-2.0
2017-08-24 08:37:57 +00:00
Bryan Boreham 29c9b2c064 Move 'weave status' up beside the other troubleshooting
and put it in the table of contents at the top
2017-08-24 08:35:11 +00:00
Bryan Boreham ff1cd7c008 Merge branch '2.0' 2017-08-23 11:18:52 +00:00
Bryan Boreham 6a871d13a6 Add release v2.0.3 2017-08-23 11:08:24 +00:00
Bryan Boreham c2a224b436 Merge pull request #3096 from weaveworks/wip-3095
fix: Missing nil checks
2017-08-23 11:28:16 +01:00
Bryan Boreham dd8d4b1d9b Merge pull request #3097 from weaveworks/issues/3095-better-test
Extend weave-kube smoke test: add a policy with blank 'from'
2017-08-23 11:27:43 +01:00
Bryan Boreham 527bcffe3b Extend weave-kube smoke test: add a policy with blank 'from'
Per issue #3095 this crashes the commenting code.
Also added check to see if the pods are still running.
2017-08-23 09:42:51 +00:00
Mike Bryant de1d409c6a Fix review comments 2017-08-23 09:40:44 +01:00
Mike Bryant 2555e491e1 fix: Missing nil checks
Fixes #3095
2017-08-23 01:21:15 +01:00
Bryan Boreham 4b720bffdf Merge branch '2.0' 2017-08-18 14:02:58 +00:00
Bryan Boreham 5c210c90b5 Merge branch '2.0' 2017-08-16 16:37:29 +00:00
Bryan Boreham 2ede1066e9 Remove unused WeaveDomain
(left over from a22b88ed89)
2017-08-14 13:56:53 +00:00
89 changed files with 2996 additions and 869 deletions

No files matched your search

+3
View File
@@ -33,6 +33,9 @@ coverage.html
# Vim backup files
*.swp
# Sed backup files
*.bak
# Project specific
prog/weaver/weaver
prog/weavedns/weavedns
+45
View File
@@ -133,3 +133,48 @@
[submodule "vendor/golang.org/x/net"]
path = vendor/golang.org/x/net
url = https://github.com/golang/net
[submodule "vendor/k8s.io/api"]
path = vendor/k8s.io/api
url = https://github.com/kubernetes/api
[submodule "vendor/k8s.io/apimachinery"]
path = vendor/k8s.io/apimachinery
url = https://github.com/kubernetes/apimachinery
[submodule "vendor/github.com/PuerkitoBio/purell"]
path = vendor/github.com/PuerkitoBio/purell
url = https://github.com/PuerkitoBio/purell
[submodule "vendor/github.com/PuerkitoBio/urlesc"]
path = vendor/github.com/PuerkitoBio/urlesc
url = https://github.com/PuerkitoBio/urlesc
[submodule "vendor/github.com/emicklei/go-restful"]
path = vendor/github.com/emicklei/go-restful
url = https://github.com/emicklei/go-restful
[submodule "vendor/github.com/go-openapi/jsonpointer"]
path = vendor/github.com/go-openapi/jsonpointer
url = https://github.com/go-openapi/jsonpointer
[submodule "vendor/github.com/go-openapi/jsonreference"]
path = vendor/github.com/go-openapi/jsonreference
url = https://github.com/go-openapi/jsonreference
[submodule "vendor/github.com/go-openapi/spec"]
path = vendor/github.com/go-openapi/spec
url = https://github.com/go-openapi/spec
[submodule "vendor/github.com/go-openapi/swag"]
path = vendor/github.com/go-openapi/swag
url = https://github.com/go-openapi/swag
[submodule "vendor/github.com/golang/glog"]
path = vendor/github.com/golang/glog
url = https://github.com/golang/glog
[submodule "vendor/github.com/google/gofuzz"]
path = vendor/github.com/google/gofuzz
url = https://github.com/google/gofuzz
[submodule "vendor/github.com/mailru/easyjson"]
path = vendor/github.com/mailru/easyjson
url = https://github.com/mailru/easyjson
[submodule "vendor/k8s.io/kube-openapi"]
path = vendor/k8s.io/kube-openapi
url = https://github.com/kubernetes/kube-openapi
[submodule "vendor/golang.org/x/text"]
path = vendor/golang.org/x/text
url = https://go.googlesource.com/text
[submodule "vendor/gopkg.in/yaml.v2"]
path = vendor/gopkg.in/yaml.v2
url = https://gopkg.in/yaml.v2
+101
View File
@@ -1,3 +1,104 @@
## Release 2.1.2
This release fixes a couple of bugs discovered since the release of Weave Net 2.1.0
##Bug fixes
* Fix crash seen when starting 10-15 nodes simultaneously #3184,#3186
* Fix NetworkPolicy blocking traffic if updates come out of order from Kubernetes #3177,#3181
Thanks to the following contributors:
@zignig
[Full list of changes](https://github.com/weaveworks/weave/milestone/66?closed=1)
## Release 2.1.1
As 2.1.0, but fixing a couple of installation glitches. #3175,#3176
## Release 2.1.0
##New Features
Improved Kubernetes Network Policy - Weave Net now supports the
'v1' policies introduced in Kubernetes 1.7 as well as the 'beta'
policies supported previously. See [Kubernetes 1.7 changelog](https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.7.md#network)
for differences. To use old policies, `--use-legacy-netpol` argument
should be passed to `weave-npc`. #3105,#3141,#3151,#3169
Weave Net now reclaims IP addresses owned by Kubernetes nodes which
have been deleted from the cluster - this avoids running out of IP
addresses when many nodes are added and deleted over a long period.
#2797,#3149,#3170,#3172
##Other improvements
* Export a Prometheus-style metric giving count of unreachable peers #3119
* Update 'gopacket' library to reduce memory use by approx 15MB #3160
* Replace bundling the 'docker' binary with our own code to avoid
security vulnerability alerts and save space #2957,#3110
##Bug fixes
* When `weave expose` is used, allow traffic into the Weave network -
up till version 1.12 Docker would do this for us, but in 1.13 they
stopped so now we do it. This change makes `weave expose` require
Weave Net to be running. #2758,#3122
* Arm64 build now works on non-kubernetes installs #2832,#3110
* TX offload was being disabled in 'awsvpc' mode, which slows down packet sending #3089
* Removed spurious 'nil' in logs from CNI DEL operation #3143
##Build and test
* Images are now also built for the ppc64le platform #3129
* Tweak build scripts to run on OSX as well as Linux #3135
##External Contributors
Thanks to the following contributors:
@caarlos0
@dtshepherd
[Full list of changes](https://github.com/weaveworks/weave/milestone/59?closed=1)
## Release 2.0.5
Bug fixes
* Fix /etc/hosts inside containers so the container's name resolves to
its Weave Net address #3136,#3138
* New weave-kube config for Kubernetes 1.7 and 1.8 which resolves an
intermittent conflict with kube-proxy that could break Weave Net
until reboot #2998,#3134
* Remove persistence file in `weave reset` when using Docker plugin V2 #3103,#3114
Build and test
* Some trivial changes to placate go-lint #3137
[Full list of changes](https://github.com/weaveworks/weave/milestone/63?closed=1).
## Release 2.0.4
Bug fixes
* weave-npc failed on Centos 7, due to older 'ipset' version in kernel #3099,#3100
[Full list of changes](https://github.com/weaveworks/weave/milestone/62?closed=1).
## Release 2.0.3
Bug fixes
* Weave-npc would crash on a policy with no 'from' part - regression introduced in 2.0.2 #3095,#3096,#3097
[Full list of changes](https://github.com/weaveworks/weave/milestone/61?closed=1).
## Release 2.0.2
Bug fixes and minor improvements
+1 -1
View File
@@ -1,3 +1,3 @@
DOCKER_VERSION=17.06
KUBERNETES_VERSION=1.6.2
KUBERNETES_VERSION=1.7.8
KUBERNETES_CNI_VERSION=0.5.1
+1 -1
View File
@@ -176,7 +176,7 @@
END OF TERMS AND CONDITIONS
Copyright 2014-2016 Weaveworks Ltd.
Copyright 2014-2017 Weaveworks Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+31 -20
View File
@@ -17,15 +17,15 @@ QEMU_VERSION=v2.7.0
# A list of all supported architectures here. Should be named as Go is naming platforms
# All supported architectures must have an "ifeq" block below that customizes the parameters
ALL_ARCHITECTURES=amd64 arm arm64
ML_PLATFORMS=linux/amd64,linux/arm,linux/arm64
ALL_ARCHITECTURES=amd64 arm arm64 ppc64le
ML_PLATFORMS=linux/amd64,linux/arm,linux/arm64,linux/ppc64le
ifeq ($(ARCH),amd64)
# The architecture to use when downloading the docker binary
WEAVEEXEC_DOCKER_ARCH?=x86_64
# The name of the alpine baseimage to use as the base for weave images
ALPINE_BASEIMAGE?=alpine:3.4
ALPINE_BASEIMAGE?=alpine:3.6
# The extension for the made images
# Specifying none means for example weaveworks/weave:latest
@@ -42,7 +42,7 @@ ifeq ($(ARCH),arm)
WEAVEEXEC_DOCKER_ARCH?=armel
# Using the (semi-)official alpine image
ALPINE_BASEIMAGE?=armhf/alpine:3.4
ALPINE_BASEIMAGE?=arm32v6/alpine:3.6
# arm images have the -arm suffix, for instance weaveworks/weave-arm:latest
ARCH_EXT?=-arm
@@ -62,7 +62,7 @@ ifeq ($(ARCH),arm64)
WEAVEEXEC_DOCKER_ARCH?=armel
# Using the (semi-)official alpine image
ALPINE_BASEIMAGE?=aarch64/alpine:3.5
ALPINE_BASEIMAGE?=arm64v8/alpine:3.6
# arm64 images have the -arm64 suffix, for instance weaveworks/weave-arm64:latest
ARCH_EXT?=-arm64
@@ -77,6 +77,26 @@ ifeq ($(ARCH),arm64)
# Tell the gcc linker to search for libpcap here
CGO_LDFLAGS="-L/usr/local/lib/$(CC)"
endif
ifeq ($(ARCH),ppc64le)
# The architecture to use when downloading the docker binary
WEAVEEXEC_DOCKER_ARCH?=ppc64le
# Using the (semi-)official alpine image
ALPINE_BASEIMAGE?=ppc64le/alpine:3.6
# ppc64le images have the -ppc64le suffix, for instance weaveworks/weave-ppc64le:latest
ARCH_EXT?=-ppc64le
# The name of the gcc binary
CC=powerpc64le-linux-gnu-gcc
# The architecture name to use when downloading a prebuilt QEMU binary
QEMUARCH=ppc64le
# In the weaveworks/build image; libpcap libraries for ppc64le are placed here
# Tell the gcc linker to search for libpcap here
CGO_LDFLAGS="-L/usr/local/lib/$(CC)"
endif
# The name of the user that this Makefile should produce image artifacts for. Can/should be overridden
DOCKERHUB_USER?=weaveworks
@@ -135,9 +155,6 @@ PUSH_ML=push_ml_weave push_ml_weaveexec push_ml_weave-kube push_ml_weave-npc
WEAVE_EXPORT=weave$(ARCH_EXT).tar.gz
DOCKER_VERSION=1.10.3
DOCKER_DISTRIB=prog/weaveexec/docker-$(DOCKER_VERSION).tgz
DOCKER_DISTRIB_URL=https://get.docker.com/builds/Linux/$(WEAVEEXEC_DOCKER_ARCH)/docker-$(DOCKER_VERSION).tgz
NETGO_CHECK=@strings $@ | grep cgo_stub\\\.go >/dev/null || { \
rm $@; \
echo "\nYour go standard library was built without the 'netgo' build tag."; \
@@ -244,13 +261,13 @@ $(BUILD_UPTODATE): build/*
sed -e "s|DOCKERHUB_USER|$(DOCKERHUB_USER)|g;s|ARCH_EXT|$(ARCH_EXT)|g;s|ALPINE_BASEIMAGE|$(ALPINE_BASEIMAGE)|g;s|QEMUARCH|$(QEMUARCH)|g" $^ > $@
ifeq ($(ARCH),amd64)
# When building "normally" for amd64, remove the whole line, it has no part in the amd64 image
sed -i "/CROSS_BUILD_/d" $@
sed -i.bak "/CROSS_BUILD_/d" $@
else
# When cross-building, only the placeholder "CROSS_BUILD_" should be removed
# Register /usr/bin/qemu-ARCH-static as the handler for ARM binaries in the kernel
# Register /usr/bin/qemu-ARCH-static as the handler for ARM and ppc64le binaries in the kernel
curl -sSL https://github.com/multiarch/qemu-user-static/releases/download/$(QEMU_VERSION)/x86_64_qemu-$(QEMUARCH)-static.tar.gz | tar -xz -C $(shell dirname $@)
cd $(shell dirname $@) && sha256sum -c $(shell pwd)/build/shasums/qemu-$(QEMUARCH)-static.sha256sum
sed -i "s/CROSS_BUILD_//g" $@
sed -i.bak "s/CROSS_BUILD_//g" $@
endif
# The targets below builds the weave images
@@ -260,13 +277,11 @@ $(WEAVER_UPTODATE): prog/weaver/Dockerfile.$(DOCKERHUB_USER) $(WEAVER_EXE) weave
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -f prog/weaver/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVER_IMAGE) prog/weaver
touch $@
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) prog/weaveexec/symlink $(DOCKER_DISTRIB) $(SIGPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVER_UPTODATE)
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) prog/weaveexec/symlink $(SIGPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVER_UPTODATE)
cp $(SIGPROXY_EXE) prog/weaveexec/sigproxy
cp $(WEAVEWAIT_EXE) prog/weaveexec/weavewait
cp $(WEAVEWAIT_NOOP_EXE) prog/weaveexec/weavewait_noop
cp $(WEAVEWAIT_NOMCAST_EXE) prog/weaveexec/weavewait_nomcast
tar -xf $(DOCKER_DISTRIB) usr/local/bin/docker -O > prog/weaveexec/docker
chmod +x prog/weaveexec/docker
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -f prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVEEXEC_IMAGE) prog/weaveexec
touch $@
@@ -307,10 +322,6 @@ $(NETWORKTESTER_UPTODATE): test/images/network-tester/Dockerfile $(NETWORKTESTER
$(WEAVE_EXPORT): $(IMAGES_UPTODATE) $(WEAVEDB_UPTODATE)
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker save $(addsuffix :latest,$(IMAGES)) | gzip > $@
$(DOCKER_DISTRIB):
curl -o $(DOCKER_DISTRIB) $(DOCKER_DISTRIB_URL)
cd $(shell dirname $@) && sha256sum -c $(shell pwd)/build/shasums/docker-tgz-$(WEAVEEXEC_DOCKER_ARCH).sha256sum
tools/.git $(MANIFEST_TOOL_DIR)/.git:
git submodule update --init
@@ -343,7 +354,7 @@ publish-one-arch: $(PUBLISH)
$(PUBLISH): publish_%: $(IMAGES_UPTODATE)
# Tag :latest with the real version
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker tag $(DOCKERHUB_USER)/$*$(ARCH_EXT) $(DOCKERHUB_USER)/$*$(ARCH_EXT):$(WEAVE_VERSION)
# Push the image with the arch suffix for arm and arm64, and without suffix for amd64
# Push the image with the arch suffix for arm, arm64 and ppc64le, and without suffix for amd64
$(MAKE) DOCKER_HOST=$(DOCKER_HOST) DOCKERHUB_USER=$(DOCKERHUB_USER) WEAVE_VERSION=$(WEAVE_VERSION) UPDATE_LATEST=$(UPDATE_LATEST) push_$*$(ARCH_EXT)
ifeq ($(ARCH),amd64)
@@ -383,7 +394,7 @@ clean-bin:
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(IMAGES)
find prog -type f -name "Dockerfile.*" -not -name "Dockerfile.template" -print | xargs rm -f
find prog -type f -name "*qemu-*" -print | xargs rm -f
rm -rf $(EXES) $(IMAGES_UPTODATE) $(WEAVEDB_UPTODATE) weave*.tar.gz $(DOCKER_DISTRIB) prog/weaveexec/docker .pkg
rm -rf $(EXES) $(IMAGES_UPTODATE) $(WEAVEDB_UPTODATE) weave*.tar.gz .pkg
clean: clean-bin clean-work-dir
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(BUILD_IMAGE)
+6
View File
@@ -62,6 +62,12 @@ func (client *Client) ReleaseIPsFor(ID string) error {
return err
}
// release all IP space owned by a peer
func (client *Client) RmPeer(peerName string) (string, error) {
result, err := client.httpVerb("DELETE", fmt.Sprintf("/peer/%s", peerName), nil)
return result, err
}
func (client *Client) DefaultSubnet() (*net.IPNet, error) {
cidr, err := client.httpVerb("GET", fmt.Sprintf("/ipinfo/defaultsubnet"), nil)
if err != nil {
+12
View File
@@ -0,0 +1,12 @@
package api
import (
"fmt"
"net"
)
// Expose calls the router to assign the given IP addr to the weave bridge.
func (client *Client) Expose(ipAddr *net.IPNet) error {
_, err := client.httpVerb("POST", fmt.Sprintf("/expose/%s", ipAddr), nil)
return err
}
+3 -1
View File
@@ -17,7 +17,9 @@ function install_terraform() {
function install_ansible() {
sudo apt-get update || true
sudo apt-get install -qq -y python-pip python-dev libffi-dev libssl-dev && pip install --user -U cffi && pip install --user ansible
sudo apt-get install -qq -y python-pip python-dev libffi-dev libssl-dev \
&& pip install --user -U setuptools cffi \
&& pip install --user ansible
export PATH="$PATH:$HOME/.local/bin"
}
+18 -3
View File
@@ -88,9 +88,10 @@ build() {
## Inject the version numbers and build the distributables
## (library versions?)
sed -i "/SCRIPT_VERSION=/ c\SCRIPT_VERSION=\"$VERSION\"" ./weave
sed -i -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset.yaml
sed -i -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.6.yaml
sed -i.bak "/SCRIPT_VERSION=/ c\SCRIPT_VERSION=\"$VERSION\"" ./weave
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset.yaml
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.6.yaml
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.7.yaml
make SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER
if make tests; then
@@ -173,6 +174,13 @@ draft() {
--name "weave-daemonset-k8s-1.6.yaml" \
--file "./prog/weave-kube/weave-daemonset-k8s-1.6.yaml"
github-release upload \
--user $GITHUB_USER \
--repo weave \
--tag $LATEST_TAG \
--name "weave-daemonset-k8s-1.7.yaml" \
--file "./prog/weave-kube/weave-daemonset-k8s-1.7.yaml"
echo "** Draft $TYPE $RELEASE_NAME $VERSION created at"
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
}
@@ -274,6 +282,13 @@ publish() {
--name "weave-daemonset-k8s-1.6.yaml" \
--file "./prog/weave-kube/weave-daemonset-k8s-1.6.yaml"
github-release upload \
--user $GITHUB_USER \
--repo weave \
--tag latest_release \
--name "weave-daemonset-k8s-1.7.yaml" \
--file "./prog/weave-kube/weave-daemonset-k8s-1.7.yaml"
echo "** Release $RELEASE_NAME $VERSION published at"
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/latest_release"
+6 -3
View File
@@ -6,17 +6,20 @@ ENV GOARM 7
# The names of the architectures that we're fetching cross-compilers for from the Debian packages
ENV DEB_CROSSPLATFORMS \
armhf \
arm64
arm64 \
ppc64el
# The go platforms we are supporting to cross-compile
ENV GO_CROSSPLATFORMS \
linux/arm \
linux/arm64
linux/arm64 \
linux/ppc64le
# The names of the gcc cross-compilers we have installed
ENV GCC_CROSSCOMPILERS \
arm-linux-gnueabihf \
aarch64-linux-gnu
aarch64-linux-gnu \
powerpc64le-linux-gnu
# Temporarily add the Debian repositories, because we're going to install gcc cross-compilers from there
# Install the build-essential and crossbuild-essential-ARCH packages
-1
View File
@@ -1 +0,0 @@
8f313a9df867c207ae9ceac6e0935d1d2a0360aafce780dd5d1e0687ee98e0c3 docker-1.10.3.tgz
@@ -1 +0,0 @@
7541b5490cee1183d911f909c0e91d3d9d57b2b775e40f14a1a7a6de216ca4a8 docker-1.10.3.tgz
@@ -0,0 +1 @@
926f7f265332076ea4abf6e9586288376dbed2322c155b8d0cafbee614aaa7f2 qemu-ppc64le-static
+1 -4
View File
@@ -17,10 +17,7 @@ func ConfigureARPCache(procPath, name string) error {
if err := sysctl(procPath, fmt.Sprintf("net/ipv4/neigh/%s/delay_first_probe_time", name), "2"); err != nil {
return err
}
if err := sysctl(procPath, fmt.Sprintf("net/ipv4/neigh/%s/ucast_solicit", name), "1"); err != nil {
return err
}
return nil
return sysctl(procPath, fmt.Sprintf("net/ipv4/neigh/%s/ucast_solicit", name), "1")
}
func sysctl(procPath, variable, value string) error {
+11 -43
View File
@@ -8,7 +8,6 @@ import (
"github.com/Sirupsen/logrus"
"github.com/coreos/go-iptables/iptables"
"github.com/j-keck/arping"
"github.com/pkg/errors"
"github.com/vishvananda/netlink"
@@ -383,11 +382,7 @@ func (bf bridgedFastdpImpl) init(config *BridgeConfig) error {
return errors.Wrap(err, "creating bridged fastdp veth pair")
}
if err := linkSetUpByName(bf.datapathName); err != nil {
return err
}
return nil
return linkSetUpByName(bf.datapathName)
}
func (b bridgeImpl) attach(veth *netlink.Veth) error {
@@ -460,6 +455,15 @@ func configureIPTables(config *BridgeConfig) error {
return err
}
}
if !config.NPC {
// Create a chain for allowing ingress traffic when the bridge is exposed
_ = ipt.NewChain("filter", "WEAVE-EXPOSE")
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "WEAVE-EXPOSE"); err != nil {
return err
}
}
// Forward from weave to the rest of the world
if err = ipt.AppendUnique("filter", "FORWARD", "-i", config.WeaveBridgeName, "!", "-o", config.WeaveBridgeName, "-j", "ACCEPT"); err != nil {
return err
@@ -473,11 +477,7 @@ func configureIPTables(config *BridgeConfig) error {
if err = ipt.ClearChain("nat", "WEAVE"); err != nil {
return errors.Wrap(err, "clearing WEAVE chain")
}
if err = ipt.AppendUnique("nat", "POSTROUTING", "-j", "WEAVE"); err != nil {
return err
}
return nil
return ipt.AppendUnique("nat", "POSTROUTING", "-j", "WEAVE")
}
func linkSetUpByName(linkName string) error {
@@ -487,35 +487,3 @@ func linkSetUpByName(linkName string) error {
}
return netlink.LinkSetUp(link)
}
func AddBridgeAddr(bridgeName string, addr *net.IPNet, removeDefaultRoute bool) error {
link, err := netlink.LinkByName(bridgeName)
if err != nil {
return errors.Wrapf(err, "AddBridgeAddr finding bridge %q", bridgeName)
}
newAddresses, err := AddAddresses(link, []*net.IPNet{addr})
if err != nil {
return errors.Wrapf(err, "adding address %v to %q", addr, bridgeName)
}
for _, ipnet := range newAddresses {
arping.GratuitousArpOverIfaceByName(ipnet.IP, bridgeName)
}
if removeDefaultRoute {
routeFilter := &netlink.Route{
LinkIndex: link.Attrs().Index,
Dst: &net.IPNet{IP: addr.IP.Mask(addr.Mask), Mask: addr.Mask},
Protocol: 2, // RTPROT_KERNEL
}
filterMask := netlink.RT_FILTER_OIF | netlink.RT_FILTER_DST | netlink.RT_FILTER_PROTOCOL
routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, routeFilter, filterMask)
if err != nil {
return errors.Wrapf(err, "failed to get route list for bridge %q", bridgeName)
}
for _, r := range routes {
if err = netlink.RouteDel(&r); err != nil {
return errors.Wrapf(err, "failed to delete default route %+v", r)
}
}
}
return nil
}
+115
View File
@@ -0,0 +1,115 @@
package net
import (
"net"
"syscall"
"github.com/coreos/go-iptables/iptables"
"github.com/j-keck/arping"
"github.com/pkg/errors"
"github.com/vishvananda/netlink"
)
// Expose makes the network accessible from a host by assigning a given IP address
// to the weave bridge.
//
// List of params:
// * "bridgeName" - a name of the weave bridge.
// * "ipAddr" - IP addr to be assigned to the bridge.
// * "removeDefaultRoute" - whether to remove a default route installed by the kernel (used only in the AWSVPC mode).
// * "npc" - whether is Weave NPC running.
func Expose(bridgeName string, ipAddr *net.IPNet, removeDefaultRoute, npc bool) error {
ipt, err := iptables.New()
if err != nil {
return errors.Wrap(err, "iptables.New")
}
cidr := ipAddr.String()
if err := addBridgeIPAddr(bridgeName, ipAddr, removeDefaultRoute); err != nil {
return errors.Wrap(err, "addBridgeIPAddr")
}
if err := exposeNAT(ipt, cidr); err != nil {
return errors.Wrap(err, "exposeNAT")
}
if !npc {
// Docker 1.13 has changed a default policy of FORWARD chain to DROP
// (https://github.com/moby/moby/pull/28257) which makes containers
// inaccessible from a remote host when the bridge is exposed.
//
// The change breaks e.g. the AWSVPC mode. To overcome this we install
// an explicit rule for accepting forwarded ingress traffic to an
// exposed subnet.
if err := ipt.AppendUnique("filter", "WEAVE-EXPOSE", "-d", cidr, "-j", "ACCEPT"); err != nil {
return errors.Wrap(err, "ipt.AppendUnique")
}
}
return nil
}
func addBridgeIPAddr(bridgeName string, addr *net.IPNet, removeDefaultRoute bool) error {
link, err := netlink.LinkByName(bridgeName)
if err != nil {
return errors.Wrapf(err, "addBridgeIPAddr finding bridge %q", bridgeName)
}
err = netlink.AddrAdd(link, &netlink.Addr{IPNet: addr})
// The IP addr might have been already set by a concurrent request, just ignore then
if err != nil && err != syscall.Errno(syscall.EEXIST) {
return errors.Wrapf(err, "adding address %v to %q", addr, bridgeName)
}
// Sending multiple ARP REQUESTs in the case of EEXIST above does not hurt
arping.GratuitousArpOverIfaceByName(addr.IP, bridgeName)
// Remove a default route installed by the kernel. Required by the AWSVPC mode.
if removeDefaultRoute {
routeFilter := &netlink.Route{
LinkIndex: link.Attrs().Index,
Dst: &net.IPNet{IP: addr.IP.Mask(addr.Mask), Mask: addr.Mask},
Protocol: 2, // RTPROT_KERNEL
}
filterMask := netlink.RT_FILTER_OIF | netlink.RT_FILTER_DST | netlink.RT_FILTER_PROTOCOL
routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, routeFilter, filterMask)
if err != nil {
return errors.Wrapf(err, "failed to get route list for bridge %q", bridgeName)
}
for _, r := range routes {
err = netlink.RouteDel(&r)
// Again, there might be a concurrent request for removing routes
if err != nil && err != syscall.Errno(syscall.ESRCH) {
return errors.Wrapf(err, "failed to delete default route %+v", r)
}
}
}
return nil
}
func exposeNAT(ipt *iptables.IPTables, cidr string) error {
if err := addNatRule(ipt, "-s", cidr, "-d", "224.0.0.0/4", "-j", "RETURN"); err != nil {
return err
}
if err := addNatRule(ipt, "-d", cidr, "!", "-s", cidr, "-j", "MASQUERADE"); err != nil {
return err
}
return addNatRule(ipt, "-s", cidr, "!", "-d", cidr, "-j", "MASQUERADE")
}
func addNatRule(ipt *iptables.IPTables, rulespec ...string) error {
// Loop until we get an exit code other than "temporarily unavailable"
for {
if err := ipt.AppendUnique("nat", "WEAVE", rulespec...); err != nil {
if ierr, ok := err.(*iptables.Error); ok {
if status, ok := ierr.ExitError.Sys().(syscall.WaitStatus); ok {
// (magic exit code 4 found in iptables source code; undocumented)
if status.ExitStatus() == 4 {
continue
}
}
}
return err
}
return nil
}
}
+1 -4
View File
@@ -421,10 +421,7 @@ func (ipsec *IPSec) installDropNonEncrypted(srcIP, dstIP net.IP, udpPort int, in
func (ipsec *IPSec) removeDropNonEncrypted(srcIP, dstIP net.IP, udpPort int, inSPI SPI) error {
rules := rulesDropNonEncrypted(srcIP, dstIP, udpPort, inSPI)
if err := ipsec.resetRules(rules, true); err != nil {
return err
}
return nil
return ipsec.resetRules(rules, true)
}
func (ipsec *IPSec) removeDropNonEncryptedInbound(srcIP, dstIP net.IP, inSPI SPI) error {
+1 -42
View File
@@ -4,7 +4,6 @@ import (
"fmt"
"net"
"strings"
"syscall"
"time"
"github.com/coreos/go-iptables/iptables"
@@ -218,11 +217,7 @@ func SetupIface(ifaceName, newIfName string) error {
if err := ConfigureARPCache("/proc", newIfName); err != nil {
return err
}
if err := ipt.Append("filter", "INPUT", "-i", newIfName, "-d", "224.0.0.0/4", "-j", "DROP"); err != nil {
return err
}
return nil
return ipt.Append("filter", "INPUT", "-i", newIfName, "-d", "224.0.0.0/4", "-j", "DROP")
}
// NB: This function can be used only by a process that terminates immediately
@@ -301,39 +296,3 @@ func subnets(addrs []netlink.Addr) map[string]struct{} {
}
return subnets
}
func addNatRule(ipt *iptables.IPTables, rulespec ...string) error {
// Loop until we get an exit code other than "temporarily unavailable"
for {
if err := ipt.AppendUnique("nat", "WEAVE", rulespec...); err != nil {
if ierr, ok := err.(*iptables.Error); ok {
if status, ok := ierr.ExitError.Sys().(syscall.WaitStatus); ok {
// (magic exit code 4 found in iptables source code; undocumented)
if status.ExitStatus() == 4 {
continue
}
}
}
return err
}
return nil
}
}
func ExposeNAT(ipnet net.IPNet) error {
ipt, err := iptables.New()
if err != nil {
return err
}
cidr := ipnet.String()
if err := addNatRule(ipt, "-s", cidr, "-d", "224.0.0.0/4", "-j", "RETURN"); err != nil {
return err
}
if err := addNatRule(ipt, "-d", cidr, "!", "-s", cidr, "-j", "MASQUERADE"); err != nil {
return err
}
if err := addNatRule(ipt, "-s", cidr, "!", "-d", cidr, "-j", "MASQUERADE"); err != nil {
return err
}
return nil
}
+121 -28
View File
@@ -3,14 +3,19 @@ package npc
import (
"fmt"
"k8s.io/client-go/pkg/api"
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
"k8s.io/client-go/pkg/util/intstr"
apiv1 "k8s.io/api/core/v1"
extnapi "k8s.io/api/extensions/v1beta1"
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"github.com/weaveworks/weave/npc/ipset"
)
func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
// analysePolicyLegacy is used to analyse extensions/v1beta1/NetworkPolicy (legacy) and
// implements pre-1.7 k8s netpol semantics, whilst analysePolicy - networking.k8s.io/v1/NetworkPolicy
// and 1.7 semantics.
func (ns *ns) analysePolicyLegacy(policy *extnapi.NetworkPolicy) (
rules map[string]*ruleSpec,
nsSelectors, podSelectors map[string]*selectorSpec,
err error) {
@@ -19,7 +24,7 @@ func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
podSelectors = make(map[string]*selectorSpec)
rules = make(map[string]*ruleSpec)
dstSelector, err := newSelectorSpec(&policy.Spec.PodSelector, ns.name, ipset.HashIP)
dstSelector, err := newSelectorSpec(&policy.Spec.PodSelector, true, ns.name, ipset.HashIP)
if err != nil {
return nil, nil, nil, err
}
@@ -45,7 +50,7 @@ func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
} else {
// Ports is present and contains at least one item, then this rule allows traffic
// only if the traffic matches at least one port in the ports list.
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
withNormalisedProtoAndPortLegacy(ingressRule.Ports, func(proto, port string) {
rule := newRuleSpec(&proto, nil, dstSelector, &port)
rules[rule.key] = rule
})
@@ -56,14 +61,14 @@ func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
for _, peer := range ingressRule.From {
var srcSelector *selectorSpec
if peer.PodSelector != nil {
srcSelector, err = newSelectorSpec(peer.PodSelector, ns.name, ipset.HashIP)
srcSelector, err = newSelectorSpec(peer.PodSelector, false, ns.name, ipset.HashIP)
if err != nil {
return nil, nil, nil, err
}
podSelectors[srcSelector.key] = srcSelector
}
if peer.NamespaceSelector != nil {
srcSelector, err = newSelectorSpec(peer.NamespaceSelector, "", ipset.ListSet)
srcSelector, err = newSelectorSpec(peer.NamespaceSelector, false, "", ipset.ListSet)
if err != nil {
return nil, nil, nil, err
}
@@ -77,6 +82,80 @@ func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
} else {
// Ports is present and contains at least one item, then this rule allows traffic
// only if the traffic matches at least one port in the ports list.
withNormalisedProtoAndPortLegacy(ingressRule.Ports, func(proto, port string) {
rule := newRuleSpec(&proto, srcSelector, dstSelector, &port)
rules[rule.key] = rule
})
}
}
}
}
return rules, nsSelectors, podSelectors, nil
}
func (ns *ns) analysePolicy(policy *networkingv1.NetworkPolicy) (
rules map[string]*ruleSpec,
nsSelectors, podSelectors map[string]*selectorSpec,
err error) {
nsSelectors = make(map[string]*selectorSpec)
podSelectors = make(map[string]*selectorSpec)
rules = make(map[string]*ruleSpec)
// If empty, matches all pods in a namespace
dstSelector, err := newSelectorSpec(&policy.Spec.PodSelector, true, ns.name, ipset.HashIP)
if err != nil {
return nil, nil, nil, err
}
podSelectors[dstSelector.key] = dstSelector
// If ingress is empty then this NetworkPolicy does not allow any traffic
if policy.Spec.Ingress == nil || len(policy.Spec.Ingress) == 0 {
return
}
for _, ingressRule := range policy.Spec.Ingress {
// If Ports is empty or missing, this rule matches all ports
allPorts := ingressRule.Ports == nil || len(ingressRule.Ports) == 0
// If From is empty or missing, this rule matches all sources
allSources := ingressRule.From == nil || len(ingressRule.From) == 0
if allSources {
if allPorts {
rule := newRuleSpec(nil, nil, dstSelector, nil)
rules[rule.key] = rule
} else {
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
rule := newRuleSpec(&proto, nil, dstSelector, &port)
rules[rule.key] = rule
})
}
} else {
for _, peer := range ingressRule.From {
var srcSelector *selectorSpec
// NetworkPolicyPeer describes a peer to allow traffic from.
// Exactly one of its fields must be specified.
if peer.PodSelector != nil {
srcSelector, err = newSelectorSpec(peer.PodSelector, false, ns.name, ipset.HashIP)
if err != nil {
return nil, nil, nil, err
}
podSelectors[srcSelector.key] = srcSelector
} else if peer.NamespaceSelector != nil {
srcSelector, err = newSelectorSpec(peer.NamespaceSelector, false, "", ipset.ListSet)
if err != nil {
return nil, nil, nil, err
}
nsSelectors[srcSelector.key] = srcSelector
}
if allPorts {
rule := newRuleSpec(nil, srcSelector, dstSelector, nil)
rules[rule.key] = rule
} else {
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
rule := newRuleSpec(&proto, srcSelector, dstSelector, &port)
rules[rule.key] = rule
@@ -89,26 +168,40 @@ func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
return rules, nsSelectors, podSelectors, nil
}
func withNormalisedProtoAndPort(npps []extnapi.NetworkPolicyPort, f func(proto, port string)) {
func withNormalisedProtoAndPortLegacy(npps []extnapi.NetworkPolicyPort, f func(proto, port string)) {
for _, npp := range npps {
// If no proto is specified, default to TCP
proto := string(api.ProtocolTCP)
if npp.Protocol != nil {
proto = string(*npp.Protocol)
}
// If no port is specified, match any port. Let iptables executable handle
// service name resolution
port := "0:65535"
if npp.Port != nil {
switch npp.Port.Type {
case intstr.Int:
port = fmt.Sprintf("%d", npp.Port.IntVal)
case intstr.String:
port = npp.Port.StrVal
}
}
f(proto, port)
f(proto(npp.Protocol), port(npp.Port))
}
}
func withNormalisedProtoAndPort(npps []networkingv1.NetworkPolicyPort, f func(proto, port string)) {
for _, npp := range npps {
f(proto(npp.Protocol), port(npp.Port))
}
}
func proto(p *apiv1.Protocol) string {
// If no proto is specified, default to TCP
proto := string(apiv1.ProtocolTCP)
if p != nil {
proto = string(*p)
}
return proto
}
func port(p *intstr.IntOrString) string {
// If no port is specified, match any port. Let iptables executable handle
// service name resolution
port := "0:65535"
if p != nil {
switch p.Type {
case intstr.Int:
port = fmt.Sprintf("%d", p.IntVal)
case intstr.String:
port = p.StrVal
}
}
return port
}
+46 -15
View File
@@ -4,8 +4,9 @@ import (
"sync"
"github.com/pkg/errors"
coreapi "k8s.io/client-go/pkg/api/v1"
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
coreapi "k8s.io/api/core/v1"
extnapi "k8s.io/api/extensions/v1beta1"
networkingv1 "k8s.io/api/networking/v1"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/npc/ipset"
@@ -21,9 +22,9 @@ type NetworkPolicyController interface {
UpdatePod(oldObj, newObj *coreapi.Pod) error
DeletePod(obj *coreapi.Pod) error
AddNetworkPolicy(obj *extnapi.NetworkPolicy) error
UpdateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error
DeleteNetworkPolicy(obj *extnapi.NetworkPolicy) error
AddNetworkPolicy(obj interface{}) error
UpdateNetworkPolicy(oldObj, newObj interface{}) error
DeleteNetworkPolicy(obj interface{}) error
}
type controller struct {
@@ -36,16 +37,20 @@ type controller struct {
nss map[string]*ns // ns name -> ns struct
nsSelectors *selectorSet // selector string -> nsSelector
legacy bool // use legacy network policy semantics (k8s pre-1.7)
}
func New(nodeName string, ipt iptables.Interface, ips ipset.Interface) NetworkPolicyController {
func New(nodeName string, legacy bool, ipt iptables.Interface, ips ipset.Interface) NetworkPolicyController {
c := &controller{
nodeName: nodeName,
legacy: legacy,
ipt: ipt,
ips: ips,
nss: make(map[string]*ns)}
c.nsSelectors = newSelectorSet(ips, c.onNewNsSelector)
doNothing := func(*selector) error { return nil }
c.nsSelectors = newSelectorSet(ips, c.onNewNsSelector, doNothing, doNothing)
return c
}
@@ -54,7 +59,7 @@ func (npc *controller) onNewNsSelector(selector *selector) error {
for _, ns := range npc.nss {
if ns.namespace != nil {
if selector.matches(ns.namespace.ObjectMeta.Labels) {
if err := selector.addEntry(string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
if err := selector.addEntry(ns.namespace.ObjectMeta.UID, string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
return err
}
}
@@ -66,7 +71,7 @@ func (npc *controller) onNewNsSelector(selector *selector) error {
func (npc *controller) withNS(name string, f func(ns *ns) error) error {
ns, found := npc.nss[name]
if !found {
newNs, err := newNS(name, npc.nodeName, npc.ipt, npc.ips, npc.nsSelectors)
newNs, err := newNS(name, npc.nodeName, npc.legacy, npc.ipt, npc.ips, npc.nsSelectors)
if err != nil {
return err
}
@@ -115,32 +120,47 @@ func (npc *controller) DeletePod(obj *coreapi.Pod) error {
})
}
func (npc *controller) AddNetworkPolicy(obj *extnapi.NetworkPolicy) error {
func (npc *controller) AddNetworkPolicy(obj interface{}) error {
npc.Lock()
defer npc.Unlock()
nsName, err := nsName(obj)
if err != nil {
return err
}
common.Log.Infof("EVENT AddNetworkPolicy %s", js(obj))
return npc.withNS(obj.ObjectMeta.Namespace, func(ns *ns) error {
return npc.withNS(nsName, func(ns *ns) error {
return errors.Wrap(ns.addNetworkPolicy(obj), "add network policy")
})
}
func (npc *controller) UpdateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error {
func (npc *controller) UpdateNetworkPolicy(oldObj, newObj interface{}) error {
npc.Lock()
defer npc.Unlock()
nsName, err := nsName(oldObj)
if err != nil {
return err
}
common.Log.Infof("EVENT UpdateNetworkPolicy %s %s", js(oldObj), js(newObj))
return npc.withNS(oldObj.ObjectMeta.Namespace, func(ns *ns) error {
return npc.withNS(nsName, func(ns *ns) error {
return errors.Wrap(ns.updateNetworkPolicy(oldObj, newObj), "update network policy")
})
}
func (npc *controller) DeleteNetworkPolicy(obj *extnapi.NetworkPolicy) error {
func (npc *controller) DeleteNetworkPolicy(obj interface{}) error {
npc.Lock()
defer npc.Unlock()
nsName, err := nsName(obj)
if err != nil {
return err
}
common.Log.Infof("EVENT DeleteNetworkPolicy %s", js(obj))
return npc.withNS(obj.ObjectMeta.Namespace, func(ns *ns) error {
return npc.withNS(nsName, func(ns *ns) error {
return errors.Wrap(ns.deleteNetworkPolicy(obj), "delete network policy")
})
}
@@ -174,3 +194,14 @@ func (npc *controller) DeleteNamespace(obj *coreapi.Namespace) error {
return errors.Wrap(ns.deleteNamespace(obj), "delete namespace")
})
}
func nsName(obj interface{}) (string, error) {
switch obj := obj.(type) {
case *networkingv1.NetworkPolicy:
return obj.ObjectMeta.Namespace, nil
case *extnapi.NetworkPolicy:
return obj.ObjectMeta.Namespace, nil
}
return "", errInvalidNetworkPolicyObjType
}
+232 -36
View File
@@ -7,16 +7,17 @@ import (
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"github.com/weaveworks/weave/npc/ipset"
unversionedapi "k8s.io/client-go/pkg/api/unversioned"
coreapi "k8s.io/client-go/pkg/api/v1"
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
"k8s.io/client-go/pkg/util/intstr"
coreapi "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
)
type mockSet struct {
name ipset.Name
setType ipset.Type
subSets map[string]bool
subSets map[string]map[types.UID]bool
}
type mockIPSet struct {
@@ -24,47 +25,59 @@ type mockIPSet struct {
}
func newMockIPSet() mockIPSet {
i := mockIPSet{
sets: make(map[string]mockSet),
}
return i
return mockIPSet{sets: make(map[string]mockSet)}
}
func (i *mockIPSet) Create(ipsetName ipset.Name, ipsetType ipset.Type) error {
if _, ok := i.sets[string(ipsetName)]; ok {
return errors.Errorf("ipset %s already exists", ipsetName)
}
i.sets[string(ipsetName)] = mockSet{name: ipsetName, setType: ipsetType, subSets: make(map[string]bool)}
i.sets[string(ipsetName)] = mockSet{name: ipsetName, setType: ipsetType, subSets: make(map[string]map[types.UID]bool)}
return nil
}
func (i *mockIPSet) AddEntry(ipsetName ipset.Name, entry string, comment string) error {
log.Printf("adding entry %s to %s", entry, ipsetName)
if _, ok := i.sets[entry]; !ok {
return errors.Errorf("ipset %s does not exist", entry)
func (i *mockIPSet) AddEntry(user types.UID, ipsetName ipset.Name, entry string, comment string) error {
log.Printf("adding entry %s to %s for %s", entry, ipsetName, user)
if _, ok := i.sets[string(ipsetName)]; !ok {
return errors.Errorf("%s does not exist", entry)
}
if _, ok := i.sets[string(ipsetName)].subSets[entry]; ok {
return errors.Errorf("ipset %s is already a member of %s", entry, ipsetName)
if i.sets[string(ipsetName)].subSets[entry] == nil {
i.sets[string(ipsetName)].subSets[entry] = make(map[types.UID]bool)
}
i.sets[string(ipsetName)].subSets[entry] = true
if _, ok := i.sets[string(ipsetName)].subSets[entry][user]; ok {
return errors.Errorf("user %s already owns entry %s", user, entry)
}
i.sets[string(ipsetName)].subSets[entry][user] = true
return nil
}
func (i *mockIPSet) DelEntry(ipsetName ipset.Name, entry string) error {
log.Printf("deleting entry %s from %s", entry, ipsetName)
func (i *mockIPSet) DelEntry(user types.UID, ipsetName ipset.Name, entry string) error {
log.Printf("deleting entry %s from %s for %s", entry, ipsetName, user)
if _, ok := i.sets[string(ipsetName)]; !ok {
return errors.Errorf("ipset %s does not exist", ipsetName)
}
if _, ok := i.sets[string(ipsetName)].subSets[entry]; !ok {
return errors.Errorf("ipset %s is not a member of %s", entry, ipsetName)
if _, ok := i.sets[string(ipsetName)].subSets[entry][user]; !ok {
return errors.Errorf("user %s does not own entry %s", user, entry)
}
delete(i.sets[string(ipsetName)].subSets[entry], user)
if len(i.sets[string(ipsetName)].subSets[entry]) == 0 {
delete(i.sets[string(ipsetName)].subSets, entry)
}
delete(i.sets[string(ipsetName)].subSets, entry)
return nil
}
func (i *mockIPSet) Exist(user types.UID, ipsetName ipset.Name, entry string) bool {
_, ok := i.sets[string(ipsetName)].subSets[entry][user]
return ok
}
func (i *mockIPSet) entryExists(ipsetName ipset.Name, entry string) bool {
return len(i.sets[string(ipsetName)].subSets[entry]) > 0
}
func (i *mockIPSet) Flush(ipsetName ipset.Name) error {
return errors.New("Not Implemented")
}
@@ -109,7 +122,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
// https://github.com/weaveworks/weave/issues/3059
sourceNamespace := &coreapi.Namespace{
ObjectMeta: coreapi.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: "source",
Labels: map[string]string{
"app": "source",
@@ -118,31 +131,31 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
}
destinationNamespace := &coreapi.Namespace{
ObjectMeta: coreapi.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: "destination",
},
}
port := intstr.FromInt(12345)
networkPolicy := &extnapi.NetworkPolicy{
ObjectMeta: coreapi.ObjectMeta{
networkPolicy := &networkingv1.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "network-policy",
Namespace: "destination",
},
Spec: extnapi.NetworkPolicySpec{
Ingress: []extnapi.NetworkPolicyIngressRule{
Spec: networkingv1.NetworkPolicySpec{
Ingress: []networkingv1.NetworkPolicyIngressRule{
{
From: []extnapi.NetworkPolicyPeer{
From: []networkingv1.NetworkPolicyPeer{
{
NamespaceSelector: &unversionedapi.LabelSelector{
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "source",
},
},
},
},
Ports: []extnapi.NetworkPolicyPort{
Ports: []networkingv1.NetworkPolicyPort{
{
Port: &port,
},
@@ -154,7 +167,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
// Namespaces first
m := newMockIPSet()
controller := New("foo", &mockIPTables{}, &m)
controller := New("foo", false, &mockIPTables{}, &m)
const (
selectorIPSetName = "weave-I239Zp%sCvoVt*D6u=A!2]YEk"
@@ -166,17 +179,200 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
controller.AddNetworkPolicy(networkPolicy)
require.Equal(t, true, m.sets[selectorIPSetName].subSets[sourceIPSetName])
require.Equal(t, true, len(m.sets[selectorIPSetName].subSets[sourceIPSetName]) > 0)
// NetworkPolicy first
m = newMockIPSet()
controller = New("foo", &mockIPTables{}, &m)
controller = New("foo", false, &mockIPTables{}, &m)
controller.AddNetworkPolicy(networkPolicy)
controller.AddNamespace(sourceNamespace)
controller.AddNamespace(destinationNamespace)
require.Equal(t, true, m.sets[selectorIPSetName].subSets[sourceIPSetName])
require.Equal(t, true, len(m.sets[selectorIPSetName].subSets[sourceIPSetName]) > 0)
}
// Tests default-allow ipset behavior when running in non-legacy mode.
func TestDefaultAllow(t *testing.T) {
const (
defaultAllowIPSetName = "weave-E.1.0W^NGSp]0_t5WwH/]gX@L"
fooPodIP = "10.32.0.10"
barPodIP = "10.32.0.11"
barPodNewIP = "10.32.0.12"
)
m := newMockIPSet()
controller := New("bar", false, &mockIPTables{}, &m)
defaultNamespace := &coreapi.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
},
}
controller.AddNamespace(defaultNamespace)
// Should create an ipset for default-allow
require.Contains(t, m.sets, defaultAllowIPSetName)
podFoo := &coreapi.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "foo",
Namespace: "default",
Name: "foo",
Labels: map[string]string{"run": "foo"}},
Status: coreapi.PodStatus{PodIP: fooPodIP}}
controller.AddPod(podFoo)
// Should add the foo pod to default-allow
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
podBar := &coreapi.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "bar",
Namespace: "default",
Name: "bar",
Labels: map[string]string{"run": "bar"}},
Status: coreapi.PodStatus{PodIP: barPodIP}}
podBarNoIP := &coreapi.Pod{ObjectMeta: podBar.ObjectMeta}
controller.AddPod(podBarNoIP)
controller.UpdatePod(podBarNoIP, podBar)
// Should add the bar pod to default-allow
require.True(t, m.entryExists(defaultAllowIPSetName, barPodIP))
// Allow access from the bar pod to the foo pod
netpol := &networkingv1.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "allow-from-bar-to-foo",
Namespace: "default",
},
Spec: networkingv1.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "foo"}},
Ingress: []networkingv1.NetworkPolicyIngressRule{{
From: []networkingv1.NetworkPolicyPeer{{
PodSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"run": "bar"},
},
}},
}},
},
}
controller.AddNetworkPolicy(netpol)
// Should remove the foo pod from default-allow as the netpol selects it
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
require.True(t, m.entryExists(defaultAllowIPSetName, barPodIP))
podBarWithNewIP := *podBar
podBarWithNewIP.Status.PodIP = barPodNewIP
controller.UpdatePod(podBar, &podBarWithNewIP)
// Should update IP addr of the bar pod in default-allow
require.False(t, m.entryExists(defaultAllowIPSetName, barPodIP))
require.True(t, m.entryExists(defaultAllowIPSetName, barPodNewIP))
controller.UpdatePod(&podBarWithNewIP, podBarNoIP)
// Should remove the bar pod from default-allow as it does not have any IP addr
require.False(t, m.entryExists(defaultAllowIPSetName, barPodNewIP))
podFooWithNewLabel := *podFoo
podFooWithNewLabel.ObjectMeta.Labels = map[string]string{"run": "new-foo"}
controller.UpdatePod(podFoo, &podFooWithNewLabel)
// Should bring back the foo pod to default-allow as it does not match dst of any netpol
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
controller.UpdatePod(&podFooWithNewLabel, podFoo)
// Should remove from default-allow as it matches the netpol after the update
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
controller.DeleteNetworkPolicy(netpol)
// Should bring back the foo pod to default-allow as no netpol selects it
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
controller.DeletePod(podFoo)
// Should remove foo pod from default-allow
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
}
func TestOutOfOrderPodEvents(t *testing.T) {
const (
defaultAllowIPSetName = "weave-E.1.0W^NGSp]0_t5WwH/]gX@L"
runBarIPSetName = "weave-bZ~x=yBgzH)Ht()K*Uv3z{M]Y"
podIP = "10.32.0.10"
)
m := newMockIPSet()
controller := New("qux", false, &mockIPTables{}, &m)
defaultNamespace := &coreapi.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
},
}
controller.AddNamespace(defaultNamespace)
podFoo := &coreapi.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "foo",
Namespace: "default",
Name: "foo",
Labels: map[string]string{"run": "foo"}},
Status: coreapi.PodStatus{PodIP: podIP}}
controller.AddPod(podFoo)
// Should be in default-allow as no netpol selects podFoo
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
netpol := &networkingv1.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "allow-from-bar-to-foo",
Namespace: "default",
},
Spec: networkingv1.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "foo"}},
Ingress: []networkingv1.NetworkPolicyIngressRule{{
From: []networkingv1.NetworkPolicyPeer{{
PodSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"run": "bar"},
},
}},
}},
},
}
controller.AddNetworkPolicy(netpol)
// Shouldn't be in default-allow as netpol above selects podFoo
require.False(t, m.entryExists(defaultAllowIPSetName, podIP))
podBar := &coreapi.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "bar",
Namespace: "default",
Name: "bar",
Labels: map[string]string{"run": "bar"}},
Status: coreapi.PodStatus{PodIP: podIP}}
controller.AddPod(podBar)
// Should be in default-allow as no netpol selects podBar
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
require.True(t, m.Exist(podBar.ObjectMeta.UID, defaultAllowIPSetName, podIP))
// Should be in run=bar ipset
require.True(t, m.entryExists(runBarIPSetName, podIP))
controller.DeletePod(podFoo)
// Multiple duplicate events should not affect npc state
controller.DeletePod(podFoo)
controller.DeletePod(podFoo)
// Should be in default-allow as no netpol selects podBar and podFoo removal
// should not affect podBar in default-allow
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
controller.DeletePod(podBar)
// Should remove from default-allow and run=bar ipsets
require.Equal(t, 0, len(m.sets[defaultAllowIPSetName].subSets))
require.False(t, m.entryExists(runBarIPSetName, podIP))
}
+109 -51
View File
@@ -6,6 +6,7 @@ import (
"strings"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/types"
)
type Name string
@@ -19,8 +20,9 @@ const (
type Interface interface {
Create(ipsetName Name, ipsetType Type) error
AddEntry(ipsetName Name, entry string, comment string) error
DelEntry(ipsetName Name, entry string) error
AddEntry(user types.UID, ipsetName Name, entry string, comment string) error
DelEntry(user types.UID, ipsetName Name, entry string) error
Exist(user types.UID, ipsetName Name, entry string) bool
Flush(ipsetName Name) error
Destroy(ipsetName Name) error
@@ -30,52 +32,100 @@ type Interface interface {
DestroyAll() error
}
type entryKey struct {
ipsetName Name
entry string
}
type ipset struct {
refCount
*log.Logger
enableComments bool
// List of users per ipset entry. User is either a namespace or a pod.
// There might be multiple users for the same ipset & entry pair because
// events from k8s API server might be out of order causing duplicate IPs:
// https://github.com/weaveworks/weave/issues/2792.
users map[entryKey]map[types.UID]struct{}
}
func New(logger *log.Logger) Interface {
return &ipset{refCount: newRefCount(), Logger: logger}
ips := &ipset{
Logger: logger,
enableComments: true,
users: make(map[entryKey]map[types.UID]struct{}),
}
// Check for comment support
testIpsetName := Name("weave-test-comment")
// Clear it out if it already exists
_ = ips.Destroy(testIpsetName)
// Test for comment support
if err := ips.Create(testIpsetName, HashIP); err != nil {
ips.Logger.Printf("failed to create %s; disabling comment support", testIpsetName)
ips.enableComments = false
}
// If it was created, destroy it
_ = ips.Destroy(testIpsetName)
return ips
}
func (i *ipset) Create(ipsetName Name, ipsetType Type) error {
return doExec("create", string(ipsetName), string(ipsetType), "comment")
args := []string{"create", string(ipsetName), string(ipsetType)}
if i.enableComments {
args = append(args, "comment")
}
return doExec(args...)
}
func (i *ipset) AddEntry(ipsetName Name, entry string, comment string) error {
i.Logger.Printf("adding entry %s to %s", entry, ipsetName)
if i.inc(ipsetName, entry) > 1 { // already in the set
func (i *ipset) AddEntry(user types.UID, ipsetName Name, entry string, comment string) error {
i.Logger.Printf("adding entry %s to %s of %s", entry, ipsetName, user)
if !i.addUser(user, ipsetName, entry) { // already in the set
return nil
}
return doExec("add", string(ipsetName), entry, "comment", comment)
i.Logger.Printf("added entry %s to %s of %s", entry, ipsetName, user)
args := []string{"add", string(ipsetName), entry}
if i.enableComments {
args = append(args, "comment", comment)
}
return doExec(args...)
}
func (i *ipset) DelEntry(ipsetName Name, entry string) error {
i.Logger.Printf("deleting entry %s from %s", entry, ipsetName)
if i.dec(ipsetName, entry) > 0 { // still needed
func (i *ipset) DelEntry(user types.UID, ipsetName Name, entry string) error {
i.Logger.Printf("deleting entry %s from %s of %s", entry, ipsetName, user)
if !i.delUser(user, ipsetName, entry) { // still needed
return nil
}
i.Logger.Printf("deleted entry %s from %s of %s", entry, ipsetName, user)
return doExec("del", string(ipsetName), entry)
}
func (i *ipset) Exist(user types.UID, ipsetName Name, entry string) bool {
return i.existUser(user, ipsetName, entry)
}
func (i *ipset) Flush(ipsetName Name) error {
i.removeSet(ipsetName)
i.removeSetFromUsers(ipsetName)
return doExec("flush", string(ipsetName))
}
func (i *ipset) FlushAll() error {
i.refCount = newRefCount()
i.users = make(map[entryKey]map[types.UID]struct{})
return doExec("flush")
}
func (i *ipset) Destroy(ipsetName Name) error {
i.removeSet(ipsetName)
i.removeSetFromUsers(ipsetName)
return doExec("destroy", string(ipsetName))
}
func (i *ipset) DestroyAll() error {
i.refCount = newRefCount()
i.users = make(map[entryKey]map[types.UID]struct{})
return doExec("destroy")
}
@@ -97,44 +147,52 @@ func (i *ipset) List(prefix string) ([]Name, error) {
return selected, err
}
// Returns true if entry does not exist in ipset (entry has to be inserted into ipset).
func (i *ipset) addUser(user types.UID, ipsetName Name, entry string) bool {
k := entryKey{ipsetName, entry}
add := false
if i.users[k] == nil {
i.users[k] = make(map[types.UID]struct{})
}
if len(i.users[k]) == 0 {
add = true
}
i.users[k][user] = struct{}{}
return add
}
// Returns true if user is the last owner of entry (entry has to be removed from ipset).
func (i *ipset) delUser(user types.UID, ipsetName Name, entry string) bool {
k := entryKey{ipsetName, entry}
oneLeft := len(i.users[k]) == 1
delete(i.users[k], user)
if len(i.users[k]) == 0 {
delete(i.users, k)
}
return oneLeft && (len(i.users[k]) == 0)
}
func (i *ipset) existUser(user types.UID, ipsetName Name, entry string) bool {
_, ok := i.users[entryKey{ipsetName, entry}][user]
return ok
}
func (i *ipset) removeSetFromUsers(ipsetName Name) {
for k := range i.users {
if k.ipsetName == ipsetName {
delete(i.users, k)
}
}
}
func doExec(args ...string) error {
if output, err := exec.Command("ipset", args...).CombinedOutput(); err != nil {
return errors.Wrapf(err, "ipset %v failed: %s", args, output)
}
return nil
}
// Reference-counting
type key struct {
ipsetName Name
entry string
}
// note no locking is required as all operations are serialised in the controller
type refCount struct {
ref map[key]int
}
func newRefCount() refCount {
return refCount{ref: make(map[key]int)}
}
func (rc *refCount) inc(ipsetName Name, entry string) int {
k := key{ipsetName, entry}
rc.ref[k]++
return rc.ref[k]
}
func (rc *refCount) dec(ipsetName Name, entry string) int {
k := key{ipsetName, entry}
rc.ref[k]--
return rc.ref[k]
}
func (rc *refCount) removeSet(ipsetName Name) {
for k := range rc.ref {
if k.ipsetName == ipsetName {
delete(rc.ref, k)
}
}
}
+286 -108
View File
@@ -2,39 +2,50 @@ package npc
import (
"encoding/json"
"errors"
"fmt"
"k8s.io/client-go/pkg/api/unversioned"
coreapi "k8s.io/client-go/pkg/api/v1"
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
"k8s.io/client-go/pkg/types"
"k8s.io/client-go/pkg/util/uuid"
coreapi "k8s.io/api/core/v1"
extnapi "k8s.io/api/extensions/v1beta1"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/uuid"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/npc/ipset"
"github.com/weaveworks/weave/npc/iptables"
)
var errInvalidNetworkPolicyObjType = errors.New("invalid NetworkPolicy object type")
type ns struct {
ipt iptables.Interface // interface to iptables
ips ipset.Interface // interface to ipset
name string // k8s Namespace name
nodeName string // my node name
namespace *coreapi.Namespace // k8s Namespace object
pods map[types.UID]*coreapi.Pod // k8s Pod objects by UID
policies map[types.UID]*extnapi.NetworkPolicy // k8s NetworkPolicy objects by UID
name string // k8s Namespace name
nodeName string // my node name
namespace *coreapi.Namespace // k8s Namespace object
pods map[types.UID]*coreapi.Pod // k8s Pod objects by UID
policies map[types.UID]interface{} // k8s NetworkPolicy objects by UID
uid types.UID // surrogate UID to own allPods selector
allPods *selectorSpec // hash:ip ipset of all pod IPs in this namespace
// stores IP addrs of pods which are not selected by any dst podSelector of
// any netpol; used only in non-legacy mode and is used as a dst in
// the WEAVE-NPC-DEFAULT iptables chain.
defaultAllowIPSet ipset.Name
nsSelectors *selectorSet
podSelectors *selectorSet
rules *ruleSet
legacy bool
}
func newNS(name, nodeName string, ipt iptables.Interface, ips ipset.Interface, nsSelectors *selectorSet) (*ns, error) {
allPods, err := newSelectorSpec(&unversioned.LabelSelector{}, name, ipset.HashIP)
func newNS(name, nodeName string, legacy bool, ipt iptables.Interface, ips ipset.Interface, nsSelectors *selectorSet) (*ns, error) {
allPods, err := newSelectorSpec(&metav1.LabelSelector{}, false, name, ipset.HashIP)
if err != nil {
return nil, err
}
@@ -45,13 +56,22 @@ func newNS(name, nodeName string, ipt iptables.Interface, ips ipset.Interface, n
name: name,
nodeName: nodeName,
pods: make(map[types.UID]*coreapi.Pod),
policies: make(map[types.UID]*extnapi.NetworkPolicy),
policies: make(map[types.UID]interface{}),
uid: uuid.NewUUID(),
allPods: allPods,
nsSelectors: nsSelectors,
rules: newRuleSet(ipt)}
rules: newRuleSet(ipt),
legacy: legacy}
ns.podSelectors = newSelectorSet(ips, ns.onNewPodSelector)
ns.podSelectors = newSelectorSet(ips, ns.onNewPodSelector, ns.onNewDstPodSelector, ns.onDestroyDstPodSelector)
if !legacy {
defaultAllowIPSet := ipset.Name(IpsetNamePrefix + shortName("default-allow:"+name))
if err := ips.Create(defaultAllowIPSet, ipset.HashIP); err != nil {
return nil, err
}
ns.defaultAllowIPSet = defaultAllowIPSet
}
if err := ns.podSelectors.provision(ns.uid, nil, map[string]*selectorSpec{ns.allPods.key: ns.allPods}); err != nil {
return nil, err
@@ -65,22 +85,75 @@ func (ns *ns) empty() bool {
}
func (ns *ns) destroy() error {
if err := ns.podSelectors.deprovision(ns.uid, map[string]*selectorSpec{ns.allPods.key: ns.allPods}, nil); err != nil {
return err
}
return nil
return ns.podSelectors.deprovision(ns.uid, map[string]*selectorSpec{ns.allPods.key: ns.allPods}, nil)
}
func (ns *ns) onNewPodSelector(selector *selector) error {
for _, pod := range ns.pods {
if hasIP(pod) {
if selector.matches(pod.ObjectMeta.Labels) {
if err := selector.addEntry(pod.Status.PodIP, podComment(pod)); err != nil {
if err := selector.addEntry(pod.ObjectMeta.UID, pod.Status.PodIP, podComment(pod)); err != nil {
return err
}
}
}
}
return nil
}
func (ns *ns) onNewDstPodSelector(selector *selector) error {
if ns.legacy {
return nil
}
for _, pod := range ns.pods {
if hasIP(pod) {
// Remove the pod from default-allow if dst podselector matches the pod
if selector.matches(pod.ObjectMeta.Labels) {
if err := ns.ips.DelEntry(pod.ObjectMeta.UID, ns.defaultAllowIPSet, pod.Status.PodIP); err != nil {
return err
}
}
}
}
return nil
}
func (ns *ns) onDestroyDstPodSelector(selector *selector) error {
if ns.legacy {
return nil
}
for _, pod := range ns.pods {
if hasIP(pod) {
if selector.matches(pod.ObjectMeta.Labels) {
if err := ns.addToDefaultAllowIfNoMatching(pod); err != nil {
return err
}
}
}
}
return nil
}
// Add pod IP addr to default-allow ipset if there are no matching dst selectors
func (ns *ns) addToDefaultAllowIfNoMatching(pod *coreapi.Pod) error {
found := false
// TODO(mp) optimize (avoid iterating over selectors) by ref counting IP addrs.
for _, s := range ns.podSelectors.entries {
if ns.podSelectors.dstSelectorExist(s) && s.matches(pod.ObjectMeta.Labels) {
found = true
break
}
}
if !found {
if err := ns.ips.AddEntry(pod.ObjectMeta.UID, ns.defaultAllowIPSet, pod.Status.PodIP, podComment(pod)); err != nil {
return err
}
}
return nil
}
@@ -99,9 +172,21 @@ func (ns *ns) addPod(obj *coreapi.Pod) error {
}
if ns.checkLocalPod(obj) {
ns.ips.AddEntry(LocalIpset, obj.Status.PodIP, podComment(obj))
ns.ips.AddEntry(obj.ObjectMeta.UID, LocalIpset, obj.Status.PodIP, podComment(obj))
}
return ns.podSelectors.addToMatching(obj.ObjectMeta.Labels, obj.Status.PodIP, podComment(obj))
found, err := ns.podSelectors.addToMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, obj.Status.PodIP, podComment(obj))
if err != nil {
return err
}
// If there are no matching dst selectors, add the pod to default-allow
if !ns.legacy && !found {
if err := ns.ips.AddEntry(obj.ObjectMeta.UID, ns.defaultAllowIPSet, obj.Status.PodIP, podComment(obj)); err != nil {
return err
}
}
return nil
}
func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
@@ -114,16 +199,49 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
if hasIP(oldObj) && !hasIP(newObj) {
if ns.checkLocalPod(oldObj) {
ns.ips.DelEntry(LocalIpset, oldObj.Status.PodIP)
ns.ips.DelEntry(oldObj.ObjectMeta.UID, LocalIpset, oldObj.Status.PodIP)
}
return ns.podSelectors.delFromMatching(oldObj.ObjectMeta.Labels, oldObj.Status.PodIP)
if !ns.legacy {
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
return err
}
}
return ns.podSelectors.delFromMatching(oldObj.ObjectMeta.UID, oldObj.ObjectMeta.Labels, oldObj.Status.PodIP)
}
if !hasIP(oldObj) && hasIP(newObj) {
if ns.checkLocalPod(newObj) {
ns.ips.AddEntry(LocalIpset, newObj.Status.PodIP, podComment(newObj))
ns.ips.AddEntry(newObj.ObjectMeta.UID, LocalIpset, newObj.Status.PodIP, podComment(newObj))
}
found, err := ns.podSelectors.addToMatching(newObj.ObjectMeta.UID, newObj.ObjectMeta.Labels, newObj.Status.PodIP, podComment(newObj))
if err != nil {
return err
}
if !ns.legacy && !found {
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.defaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
return err
}
}
return nil
}
if !ns.legacy &&
oldObj.Status.PodIP != newObj.Status.PodIP &&
ns.ips.Exist(oldObj.ObjectMeta.UID, ns.defaultAllowIPSet, oldObj.Status.PodIP) {
// Instead of iterating over all selectors we check whether old pod IP
// has been inserted into default-allow ipset to decide whether the IP
// in the ipset has to be updated.
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
return err
}
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.defaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
return err
}
return ns.podSelectors.addToMatching(newObj.ObjectMeta.Labels, newObj.Status.PodIP, podComment(newObj))
}
if !equals(oldObj.ObjectMeta.Labels, newObj.ObjectMeta.Labels) ||
@@ -136,15 +254,28 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
continue
}
if oldMatch {
if err := ps.delEntry(oldObj.Status.PodIP); err != nil {
if err := ps.delEntry(oldObj.ObjectMeta.UID, oldObj.Status.PodIP); err != nil {
return err
}
}
if newMatch {
if err := ps.addEntry(newObj.Status.PodIP, podComment(newObj)); err != nil {
if err := ps.addEntry(newObj.ObjectMeta.UID, newObj.Status.PodIP, podComment(newObj)); err != nil {
return err
}
}
if !ns.legacy && ns.podSelectors.dstSelectorExist(ps) {
switch {
case !oldMatch && newMatch:
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
return err
}
case oldMatch && !newMatch:
if err := ns.addToDefaultAllowIfNoMatching(newObj); err != nil {
return err
}
}
}
}
}
@@ -159,138 +290,147 @@ func (ns *ns) deletePod(obj *coreapi.Pod) error {
}
if ns.checkLocalPod(obj) {
ns.ips.DelEntry(LocalIpset, obj.Status.PodIP)
ns.ips.DelEntry(obj.ObjectMeta.UID, LocalIpset, obj.Status.PodIP)
}
return ns.podSelectors.delFromMatching(obj.ObjectMeta.Labels, obj.Status.PodIP)
if !ns.legacy {
if err := ns.ips.DelEntry(obj.ObjectMeta.UID, ns.defaultAllowIPSet, obj.Status.PodIP); err != nil {
return err
}
}
return ns.podSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, obj.Status.PodIP)
}
func (ns *ns) addNetworkPolicy(obj *extnapi.NetworkPolicy) error {
ns.policies[obj.ObjectMeta.UID] = obj
func (ns *ns) addNetworkPolicy(obj interface{}) error {
// Analyse policy, determine which rules and ipsets are required
rules, nsSelectors, podSelectors, err := ns.analysePolicy(obj)
uid, rules, nsSelectors, podSelectors, err := ns.analyse(obj)
if err != nil {
return err
}
// Provision required resources in dependency order
if err := ns.nsSelectors.provision(obj.ObjectMeta.UID, nil, nsSelectors); err != nil {
if err := ns.nsSelectors.provision(uid, nil, nsSelectors); err != nil {
return err
}
if err := ns.podSelectors.provision(obj.ObjectMeta.UID, nil, podSelectors); err != nil {
if err := ns.podSelectors.provision(uid, nil, podSelectors); err != nil {
return err
}
if err := ns.rules.provision(obj.ObjectMeta.UID, nil, rules); err != nil {
return err
}
return nil
return ns.rules.provision(uid, nil, rules)
}
func (ns *ns) updateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error {
delete(ns.policies, oldObj.ObjectMeta.UID)
ns.policies[newObj.ObjectMeta.UID] = newObj
func (ns *ns) updateNetworkPolicy(oldObj, newObj interface{}) error {
// Analyse the old and the new policy so we can determine differences
oldRules, oldNsSelectors, oldPodSelectors, err := ns.analysePolicy(oldObj)
oldUID, oldRules, oldNsSelectors, oldPodSelectors, err := ns.analyse(oldObj)
if err != nil {
return err
}
newRules, newNsSelectors, newPodSelectors, err := ns.analysePolicy(newObj)
newUID, newRules, newNsSelectors, newPodSelectors, err := ns.analyse(newObj)
if err != nil {
return err
}
delete(ns.policies, oldUID)
ns.policies[newUID] = newObj
// Deprovision unused and provision newly required resources in dependency order
if err := ns.rules.deprovision(oldObj.ObjectMeta.UID, oldRules, newRules); err != nil {
if err := ns.rules.deprovision(oldUID, oldRules, newRules); err != nil {
return err
}
if err := ns.nsSelectors.deprovision(oldObj.ObjectMeta.UID, oldNsSelectors, newNsSelectors); err != nil {
if err := ns.nsSelectors.deprovision(oldUID, oldNsSelectors, newNsSelectors); err != nil {
return err
}
if err := ns.podSelectors.deprovision(oldObj.ObjectMeta.UID, oldPodSelectors, newPodSelectors); err != nil {
if err := ns.podSelectors.deprovision(oldUID, oldPodSelectors, newPodSelectors); err != nil {
return err
}
if err := ns.nsSelectors.provision(oldObj.ObjectMeta.UID, oldNsSelectors, newNsSelectors); err != nil {
if err := ns.nsSelectors.provision(oldUID, oldNsSelectors, newNsSelectors); err != nil {
return err
}
if err := ns.podSelectors.provision(oldObj.ObjectMeta.UID, oldPodSelectors, newPodSelectors); err != nil {
if err := ns.podSelectors.provision(oldUID, oldPodSelectors, newPodSelectors); err != nil {
return err
}
if err := ns.rules.provision(oldObj.ObjectMeta.UID, oldRules, newRules); err != nil {
return err
}
return nil
return ns.rules.provision(oldUID, oldRules, newRules)
}
func (ns *ns) deleteNetworkPolicy(obj *extnapi.NetworkPolicy) error {
delete(ns.policies, obj.ObjectMeta.UID)
func (ns *ns) deleteNetworkPolicy(obj interface{}) error {
// Analyse network policy to free resources
rules, nsSelectors, podSelectors, err := ns.analysePolicy(obj)
uid, rules, nsSelectors, podSelectors, err := ns.analyse(obj)
if err != nil {
return err
}
// Deprovision unused resources in dependency order
if err := ns.rules.deprovision(obj.ObjectMeta.UID, rules, nil); err != nil {
return err
}
if err := ns.nsSelectors.deprovision(obj.ObjectMeta.UID, nsSelectors, nil); err != nil {
return err
}
if err := ns.podSelectors.deprovision(obj.ObjectMeta.UID, podSelectors, nil); err != nil {
return err
}
delete(ns.policies, uid)
return nil
// Deprovision unused resources in dependency order
if err := ns.rules.deprovision(uid, rules, nil); err != nil {
return err
}
if err := ns.nsSelectors.deprovision(uid, nsSelectors, nil); err != nil {
return err
}
return ns.podSelectors.deprovision(uid, podSelectors, nil)
}
func bypassRule(nsIpsetName ipset.Name, namespace string) []string {
return []string{"-m", "set", "--match-set", string(nsIpsetName), "dst", "-j", "ACCEPT", "-m", "comment", "--comment", "DefaultAllow isolation for namespace: " + namespace}
}
func (ns *ns) ensureBypassRule(nsIpsetName ipset.Name) error {
common.Log.Debugf("ensuring rule for DefaultAllow in namespace: %s, set %s", ns.name, nsIpsetName)
return ns.ipt.Append(TableFilter, DefaultChain, bypassRule(ns.allPods.ipsetName, ns.name)...)
func (ns *ns) ensureBypassRule() error {
var ipset ipset.Name
if ns.legacy {
ipset = ns.allPods.ipsetName
} else {
ipset = ns.defaultAllowIPSet
}
common.Log.Debugf("ensuring rule for DefaultAllow in namespace: %s, set %s", ns.name, ipset)
return ns.ipt.Append(TableFilter, DefaultChain, bypassRule(ipset, ns.name)...)
}
func (ns *ns) deleteBypassRule(nsIpsetName ipset.Name) error {
common.Log.Debugf("removing default rule in namespace: %s, set %s", ns.name, nsIpsetName)
return ns.ipt.Delete(TableFilter, DefaultChain, bypassRule(ns.allPods.ipsetName, ns.name)...)
func (ns *ns) deleteBypassRule() error {
var ipset ipset.Name
if ns.legacy {
ipset = ns.allPods.ipsetName
} else {
ipset = ns.defaultAllowIPSet
}
common.Log.Debugf("removing default rule in namespace: %s, set %s", ns.name, ipset)
return ns.ipt.Delete(TableFilter, DefaultChain, bypassRule(ipset, ns.name)...)
}
func (ns *ns) addNamespace(obj *coreapi.Namespace) error {
ns.namespace = obj
// Insert a rule to bypass policies if namespace is DefaultAllow
if !isDefaultDeny(obj) {
if err := ns.ensureBypassRule(ns.allPods.ipsetName); err != nil {
if !ns.isDefaultDeny(obj) {
if err := ns.ensureBypassRule(); err != nil {
return err
}
}
// Add namespace ipset to matching namespace selectors
return ns.nsSelectors.addToMatching(obj.ObjectMeta.Labels, string(ns.allPods.ipsetName), namespaceComment(ns))
_, err := ns.nsSelectors.addToMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, string(ns.allPods.ipsetName), namespaceComment(ns))
return err
}
func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
ns.namespace = newObj
// Update bypass rule if ingress default has changed
oldDefaultDeny := isDefaultDeny(oldObj)
newDefaultDeny := isDefaultDeny(newObj)
oldDefaultDeny := ns.isDefaultDeny(oldObj)
newDefaultDeny := ns.isDefaultDeny(newObj)
if oldDefaultDeny != newDefaultDeny {
common.Log.Infof("namespace DefaultDeny changed from %t to %t", oldDefaultDeny, newDefaultDeny)
if oldDefaultDeny {
if err := ns.ensureBypassRule(ns.allPods.ipsetName); err != nil {
if err := ns.ensureBypassRule(); err != nil {
return err
}
}
if newDefaultDeny {
if err := ns.deleteBypassRule(ns.allPods.ipsetName); err != nil {
if err := ns.deleteBypassRule(); err != nil {
return err
}
}
@@ -305,12 +445,12 @@ func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
continue
}
if oldMatch {
if err := selector.delEntry(string(ns.allPods.ipsetName)); err != nil {
if err := selector.delEntry(ns.namespace.ObjectMeta.UID, string(ns.allPods.ipsetName)); err != nil {
return err
}
}
if newMatch {
if err := selector.addEntry(string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
if err := selector.addEntry(ns.namespace.ObjectMeta.UID, string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
return err
}
}
@@ -324,14 +464,37 @@ func (ns *ns) deleteNamespace(obj *coreapi.Namespace) error {
ns.namespace = nil
// Remove bypass rule
if !isDefaultDeny(obj) {
if err := ns.deleteBypassRule(ns.allPods.ipsetName); err != nil {
if !ns.isDefaultDeny(obj) {
if err := ns.deleteBypassRule(); err != nil {
return err
}
}
// Remove namespace ipset from any matching namespace selectors
return ns.nsSelectors.delFromMatching(obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
return ns.nsSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
}
func (ns *ns) isDefaultDeny(namespace *coreapi.Namespace) bool {
nnpJSON, found := namespace.ObjectMeta.Annotations["net.beta.kubernetes.io/network-policy"]
if !found {
return false
}
if !ns.legacy {
common.Log.Warn("DefaultDeny annotation is supported only in legacy mode (--use-legacy-netpol)")
return false
}
var nnp NamespaceNetworkPolicy
if err := json.Unmarshal([]byte(nnpJSON), &nnp); err != nil {
common.Log.Warn("Ignoring network policy annotation: unmarshal failed:", err)
// If we can't understand the annotation, behave as if it isn't present
return false
}
return nnp.Ingress != nil &&
nnp.Ingress.Isolation != nil &&
*(nnp.Ingress.Isolation) == DefaultDeny
}
func hasIP(pod *coreapi.Pod) bool {
@@ -352,24 +515,6 @@ func equals(a, b map[string]string) bool {
return true
}
func isDefaultDeny(namespace *coreapi.Namespace) bool {
nnpJSON, found := namespace.ObjectMeta.Annotations["net.beta.kubernetes.io/network-policy"]
if !found {
return false
}
var nnp NamespaceNetworkPolicy
if err := json.Unmarshal([]byte(nnpJSON), &nnp); err != nil {
common.Log.Warn("Ignoring network policy annotation: unmarshal failed:", err)
// If we can't understand the annotation, behave as if it isn't present
return false
}
return nnp.Ingress != nil &&
nnp.Ingress.Isolation != nil &&
*(nnp.Ingress.Isolation) == DefaultDeny
}
func namespaceComment(namespace *ns) string {
return "namespace: " + namespace.name
}
@@ -377,3 +522,36 @@ func namespaceComment(namespace *ns) string {
func podComment(pod *coreapi.Pod) string {
return fmt.Sprintf("namespace: %s, pod: %s", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name)
}
func (ns *ns) analyse(obj interface{}) (
uid types.UID,
rules map[string]*ruleSpec,
nsSelectors, podSelectors map[string]*selectorSpec,
err error) {
switch p := obj.(type) {
case *extnapi.NetworkPolicy:
uid = p.ObjectMeta.UID
case *networkingv1.NetworkPolicy:
uid = p.ObjectMeta.UID
default:
err = errInvalidNetworkPolicyObjType
return
}
ns.policies[uid] = obj
// Analyse policy, determine which rules and ipsets are required
if ns.legacy {
rules, nsSelectors, podSelectors, err = ns.analysePolicyLegacy(obj.(*extnapi.NetworkPolicy))
if err != nil {
return
}
} else {
rules, nsSelectors, podSelectors, err = ns.analysePolicy(obj.(*networkingv1.NetworkPolicy))
if err != nil {
return
}
}
return
}
+10 -8
View File
@@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"k8s.io/client-go/pkg/types"
"k8s.io/apimachinery/pkg/types"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/npc/iptables"
@@ -20,23 +20,25 @@ func newRuleSpec(proto *string, srcHost *selectorSpec, dstHost *selectorSpec, ds
if proto != nil {
args = append(args, "-p", *proto)
}
srcComment := "anywhere"
if srcHost != nil {
args = append(args, "-m", "set", "--match-set", string(srcHost.ipsetName), "src")
if srcHost.nsName != "" {
srcComment = fmt.Sprintf("pods: namespace: %s, selector: %s", srcHost.nsName, srcHost.key)
} else {
srcComment = fmt.Sprintf("namespaces: selector: %s", srcHost.key)
}
}
dstComment := "anywhere"
if dstHost != nil {
args = append(args, "-m", "set", "--match-set", string(dstHost.ipsetName), "dst")
dstComment = fmt.Sprintf("pods: namespace: %s, selector: %s", dstHost.nsName, dstHost.key)
}
if dstPort != nil {
args = append(args, "--dport", *dstPort)
}
args = append(args, "-j", "ACCEPT")
srcComment := ""
if srcHost.nsName != "" {
srcComment = fmt.Sprintf("pods: namespace: %s, selector: %s", srcHost.nsName, srcHost.key)
} else {
srcComment = fmt.Sprintf("namespaces: selector: %s", srcHost.key)
}
args = append(args, "-m", "comment", "--comment", fmt.Sprintf("%s -> namespace: %s, key: %s", srcComment, dstHost.nsName, dstHost.key))
args = append(args, "-m", "comment", "--comment", fmt.Sprintf("%s -> %s", srcComment, dstComment))
key := strings.Join(args, " ")
return &ruleSpec{key, args}
+16
View File
@@ -0,0 +1,16 @@
package npc
import (
"github.com/stretchr/testify/require"
"testing"
)
func TestRegressionHandleNil3095(t *testing.T) {
// Test for handling nil values
// https://github.com/weaveworks/weave/issues/3095
rs := newRuleSpec(nil, nil, nil, nil)
require.NotEqual(t, rs.key, "")
}
+72 -28
View File
@@ -1,9 +1,9 @@
package npc
import (
"k8s.io/client-go/pkg/api/unversioned"
"k8s.io/client-go/pkg/labels"
"k8s.io/client-go/pkg/types"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/npc/ipset"
@@ -12,14 +12,15 @@ import (
type selectorSpec struct {
key string // string representation (for hash keying/equality comparison)
selector labels.Selector // k8s Selector object (for matching)
dst bool // destination selector
ipsetType ipset.Type // type of ipset to provision
ipsetName ipset.Name // generated ipset name
nsName string // Namespace name
}
func newSelectorSpec(json *unversioned.LabelSelector, nsName string, ipsetType ipset.Type) (*selectorSpec, error) {
selector, err := unversioned.LabelSelectorAsSelector(json)
func newSelectorSpec(json *metav1.LabelSelector, dst bool, nsName string, ipsetType ipset.Type) (*selectorSpec, error) {
selector, err := metav1.LabelSelectorAsSelector(json)
if err != nil {
return nil, err
}
@@ -27,6 +28,7 @@ func newSelectorSpec(json *unversioned.LabelSelector, nsName string, ipsetType i
return &selectorSpec{
key: key,
selector: selector,
dst: dst,
// We prefix the selector string with the namespace name when generating
// the shortname because you can specify the same selector in multiple
// namespaces - we need those to map to distinct ipsets
@@ -44,12 +46,12 @@ func (s *selector) matches(labelMap map[string]string) bool {
return s.spec.selector.Matches(labels.Set(labelMap))
}
func (s *selector) addEntry(entry string, comment string) error {
return s.ips.AddEntry(s.spec.ipsetName, entry, comment)
func (s *selector) addEntry(user types.UID, entry string, comment string) error {
return s.ips.AddEntry(user, s.spec.ipsetName, entry, comment)
}
func (s *selector) delEntry(entry string) error {
return s.ips.DelEntry(s.spec.ipsetName, entry)
func (s *selector) delEntry(user types.UID, entry string) error {
return s.ips.DelEntry(user, s.spec.ipsetName, entry)
}
type selectorFn func(selector *selector) error
@@ -57,22 +59,51 @@ type selectorFn func(selector *selector) error
type selectorSet struct {
ips ipset.Interface
onNewSelector selectorFn
users map[string]map[types.UID]struct{} // list of users per selector
entries map[string]*selector
// invoked after dst selector has been provisioned for the first time
onNewDstSelector selectorFn
// invoked after the last instance of dst selector has been deprovisioned
onDestroyDstSelector selectorFn
users map[string]map[types.UID]struct{} // list of users per selector
entries map[string]*selector
// We need to keep track of dst selector instances to be able to invoke
// onNewDstSelector and onDestroyDstSelector callbacks at the right time;
// selectorSpec.Key -> count
dstSelectorsCount map[string]int
}
func newSelectorSet(ips ipset.Interface, onNewSelector selectorFn) *selectorSet {
func newSelectorSet(ips ipset.Interface, onNewSelector, onNewDstSelector selectorFn, onDestroyDstSelector selectorFn) *selectorSet {
return &selectorSet{
ips: ips,
onNewSelector: onNewSelector,
users: make(map[string]map[types.UID]struct{}),
entries: make(map[string]*selector)}
ips: ips,
onNewSelector: onNewSelector,
onNewDstSelector: onNewDstSelector,
onDestroyDstSelector: onDestroyDstSelector,
users: make(map[string]map[types.UID]struct{}),
entries: make(map[string]*selector),
dstSelectorsCount: make(map[string]int)}
}
func (ss *selectorSet) addToMatching(labelMap map[string]string, entry string, comment string) error {
func (ss *selectorSet) addToMatching(user types.UID, labelMap map[string]string, entry string, comment string) (bool, error) {
found := false
for _, s := range ss.entries {
if s.matches(labelMap) {
if err := s.addEntry(entry, comment); err != nil {
if ss.dstSelectorExist(s) {
found = true
}
if err := s.addEntry(user, entry, comment); err != nil {
return found, err
}
}
}
return found, nil
}
func (ss *selectorSet) delFromMatching(user types.UID, labelMap map[string]string, entry string) error {
for _, s := range ss.entries {
if s.matches(labelMap) {
if err := s.delEntry(user, entry); err != nil {
return err
}
}
@@ -80,15 +111,8 @@ func (ss *selectorSet) addToMatching(labelMap map[string]string, entry string, c
return nil
}
func (ss *selectorSet) delFromMatching(labelMap map[string]string, entry string) error {
for _, s := range ss.entries {
if s.matches(labelMap) {
if err := s.delEntry(entry); err != nil {
return err
}
}
}
return nil
func (ss *selectorSet) dstSelectorExist(s *selector) bool {
return ss.dstSelectorsCount[s.spec.key] > 0
}
func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*selectorSpec) error {
@@ -100,9 +124,19 @@ func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*
if err := ss.ips.Destroy(spec.ipsetName); err != nil {
return err
}
delete(ss.entries, key)
delete(ss.users, key)
}
if spec.dst {
ss.dstSelectorsCount[key]--
if ss.dstSelectorsCount[key] == 0 {
if err := ss.onDestroyDstSelector(&selector{ss.ips, spec}); err != nil {
return err
}
}
}
}
}
return nil
@@ -111,12 +145,13 @@ func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*
func (ss *selectorSet) provision(user types.UID, current, desired map[string]*selectorSpec) error {
for key, spec := range desired {
if _, found := current[key]; !found {
selector := &selector{ss.ips, spec}
if _, found := ss.users[key]; !found {
common.Log.Infof("creating ipset: %#v", spec)
if err := ss.ips.Create(spec.ipsetName, spec.ipsetType); err != nil {
return err
}
selector := &selector{ss.ips, spec}
if err := ss.onNewSelector(selector); err != nil {
return err
}
@@ -124,6 +159,15 @@ func (ss *selectorSet) provision(user types.UID, current, desired map[string]*se
ss.entries[key] = selector
}
ss.users[key][user] = struct{}{}
if spec.dst {
ss.dstSelectorsCount[key]++
if ss.dstSelectorsCount[key] == 1 {
if err := ss.onNewDstSelector(selector); err != nil {
return err
}
}
}
}
}
return nil
+7 -23
View File
@@ -6,7 +6,6 @@ import (
"fmt"
"net"
"os"
"syscall"
"github.com/containernetworking/cni/pkg/ipam"
"github.com/containernetworking/cni/pkg/skel"
@@ -100,12 +99,11 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
return fmt.Errorf("unable to allocate IP address for bridge: %s", err)
}
bridgeCIDR := bridgeIPResult.IPs[0].Address
if err := assignBridgeIP(conf.BrName, bridgeCIDR); err != nil {
return fmt.Errorf("unable to assign IP address to bridge: %s", err)
}
if err := weavenet.ExposeNAT(bridgeCIDR); err != nil {
return fmt.Errorf("unable to create NAT rules: %s", err)
if err := c.weave.Expose(&bridgeCIDR); err != nil {
return fmt.Errorf("unable to expose bridge %q: %s", conf.BrName, err)
}
bridgeIP = bridgeCIDR.IP
} else if err != nil {
return err
@@ -167,22 +165,6 @@ func setupRoutes(link netlink.Link, name string, ipnet net.IPNet, gw net.IP, rou
return nil
}
func assignBridgeIP(bridgeName string, ipnet net.IPNet) error {
link, err := netlink.LinkByName(bridgeName)
if err != nil {
return err
}
if err := netlink.AddrAdd(link, &netlink.Addr{IPNet: &ipnet}); err != nil {
// Treat as non-error if this address is already there
// - maybe another copy of this program just added it
if err == syscall.Errno(syscall.EEXIST) {
return nil
}
return fmt.Errorf("failed to add IP address to %q: %v", bridgeName, err)
}
return nil
}
// As of CNI 0.5 spec:
// "Plugins should generally complete a DEL action without error even if some resources are missing"
// this method should therefore return nil in most, if not all, cases.
@@ -207,7 +189,9 @@ func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
} else {
err = ipam.ExecDel(conf.IPAM.Type, args.StdinData)
}
logOnStderr(fmt.Errorf("unable to release IP address: %s", err))
if err != nil {
logOnStderr(fmt.Errorf("unable to release IP address: %s", err))
}
return nil
}
-4
View File
@@ -8,10 +8,6 @@ import (
weavenet "github.com/weaveworks/weave/net"
)
const (
WeaveDomain = "weave.local"
)
type watcher struct {
client *docker.Client
weave *weaveapi.Client
+195
View File
@@ -0,0 +1,195 @@
/*
In order to keep track of active weave peers, we use annotations on the Kubernetes cluster.
Kubernetes uses etcd to distribute and synchronise these annotations so we don't have to.
This module deals with operations on the peerlist backed by Kubernetes' annotation mechanism.
*/
package main
import (
"encoding/json"
"log"
"time"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
kubeErrors "k8s.io/apimachinery/pkg/api/errors"
api "k8s.io/apimachinery/pkg/apis/meta/v1"
wait "k8s.io/apimachinery/pkg/util/wait"
kubernetes "k8s.io/client-go/kubernetes"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
)
type configMapAnnotations struct {
ConfigMapName string
Namespace string
Client corev1client.ConfigMapsGetter
cm *v1.ConfigMap
}
func newConfigMapAnnotations(ns string, configMapName string, clientset *kubernetes.Clientset) *configMapAnnotations {
return &configMapAnnotations{
Namespace: ns,
ConfigMapName: configMapName,
Client: clientset.CoreV1(),
}
}
type peerList struct {
Peers []peerInfo
}
type peerInfo struct {
PeerName string // Weave internal unique ID
NodeName string // Kubernetes node name
}
func (pl peerList) contains(peerName string) bool {
for _, peer := range pl.Peers {
if peer.PeerName == peerName {
return true
}
}
return false
}
func (pl *peerList) add(peerName string, name string) {
pl.Peers = append(pl.Peers, peerInfo{PeerName: peerName, NodeName: name})
}
func (pl *peerList) remove(peerNameToRemove string) {
for i := 0; i < len(pl.Peers); {
if pl.Peers[i].PeerName == peerNameToRemove {
pl.Peers = append(pl.Peers[:i], pl.Peers[i+1:]...)
} else {
i++
}
}
}
const (
retryPeriod = time.Second * 2
jitterFactor = 1.0
// KubePeersAnnotationKey is the default annotation key
KubePeersAnnotationKey = "kube-peers.weave.works/peers"
)
func (cml *configMapAnnotations) Init() error {
for {
// Since it's potentially racy to GET, then CREATE if not found, we wrap in a check loop
// so that if the configmap is created after our GET but before or CREATE, we'll gracefully
// re-try to get the configmap.
var err error
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Get(cml.ConfigMapName, api.GetOptions{})
if err != nil {
if !kubeErrors.IsNotFound(err) {
return errors.Wrapf(err, "Unable to fetch ConfigMap %s/%s", cml.Namespace, cml.ConfigMapName)
}
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Create(&v1.ConfigMap{
ObjectMeta: api.ObjectMeta{
Name: cml.ConfigMapName,
Namespace: cml.Namespace,
},
})
if err != nil {
if kubeErrors.IsAlreadyExists(err) {
continue
}
return errors.Wrapf(err, "Unable to create ConfigMap %s/%s", cml.Namespace, cml.ConfigMapName)
}
}
break
}
if cml.cm.Annotations == nil {
cml.cm.Annotations = make(map[string]string)
}
return nil
}
func (cml *configMapAnnotations) GetPeerList() (*peerList, error) {
var record peerList
if cml.cm == nil {
return nil, errors.New("endpoint not initialized, call Init first")
}
if recordBytes, found := cml.cm.Annotations[KubePeersAnnotationKey]; found {
if err := json.Unmarshal([]byte(recordBytes), &record); err != nil {
return nil, err
}
}
return &record, nil
}
func (cml *configMapAnnotations) UpdatePeerList(list peerList) error {
recordBytes, err := json.Marshal(list)
if err != nil {
return err
}
return cml.UpdateAnnotation(KubePeersAnnotationKey, string(recordBytes))
}
func (cml *configMapAnnotations) UpdateAnnotation(key, value string) error {
if cml.cm == nil {
return errors.New("endpoint not initialized, call Init first")
}
cm := cml.cm
cm.Annotations[key] = value
cm, err := cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
if err == nil {
cml.cm = cm
}
return err
}
func (cml *configMapAnnotations) RemoveAnnotation(key string) error {
if cml.cm == nil {
return errors.New("endpoint not initialized, call Init first")
}
cm := cml.cm
delete(cm.Annotations, key)
cm, err := cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
if err == nil {
cml.cm = cm
}
return err
}
func (cml *configMapAnnotations) RemoveAnnotationsWithValue(valueToRemove string) error {
if cml.cm == nil {
return errors.New("endpoint not initialized, call Init first")
}
cm := cml.cm
for key, value := range cm.Annotations {
if value == valueToRemove {
delete(cm.Annotations, key)
}
}
cm, err := cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
if err == nil {
cml.cm = cm
}
return err
}
// Loop with jitter, fetching the cml data and calling f() until it
// doesn't get an optimistic locking conflict.
// If it succeeds or gets any other kind of error, stop the loop.
func (cml *configMapAnnotations) LoopUpdate(f func() error) error {
stop := make(chan struct{})
var err error
wait.JitterUntil(func() {
if err = cml.Init(); err != nil {
close(stop)
return
}
err = f()
if err != nil && kubeErrors.IsConflict(err) {
log.Printf("Optimistic locking conflict: trying again: %s", err)
return
}
close(stop)
}, retryPeriod, jitterFactor, true, stop)
return err
}
+195 -21
View File
@@ -1,29 +1,36 @@
/*
Package main deals with weave-net peers on the cluster.
This involves peer management, such as getting the latest peers or removing defunct peers from the cluster
*/
package main
import (
"flag"
"fmt"
"log"
"net"
"os"
api "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
api "k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/rest"
weaveapi "github.com/weaveworks/weave/api"
"github.com/weaveworks/weave/common"
)
func getKubePeers() ([]string, error) {
config, err := rest.InClusterConfig()
type nodeInfo struct {
name string
addr string
}
// return the IP addresses of all nodes in the cluster
func getKubePeers(c *kubernetes.Clientset) ([]nodeInfo, error) {
nodeList, err := c.CoreV1().Nodes().List(api.ListOptions{})
if err != nil {
return nil, err
}
c, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
nodeList, err := c.Nodes().List(api.ListOptions{})
if err != nil {
return nil, err
}
addresses := make([]string, 0, len(nodeList.Items))
addresses := make([]nodeInfo, 0, len(nodeList.Items))
for _, peer := range nodeList.Items {
var internalIP, externalIP string
for _, addr := range peer.Status.Addresses {
@@ -41,20 +48,187 @@ func getKubePeers() ([]string, error) {
// Fallback for cases where a Node has an ExternalIP but no InternalIP
if internalIP != "" {
addresses = append(addresses, internalIP)
addresses = append(addresses, nodeInfo{name: peer.Name, addr: internalIP})
} else if externalIP != "" {
addresses = append(addresses, externalIP)
addresses = append(addresses, nodeInfo{name: peer.Name, addr: externalIP})
}
}
return addresses, nil
}
func main() {
peers, err := getKubePeers()
if err != nil {
log.Fatalf("Could not get peers: %v", err)
const (
configMapName = "weave-net"
configMapNamespace = "kube-system"
)
// update the list of all peers that have gone through this code path
func addMyselfToPeerList(cml *configMapAnnotations, c *kubernetes.Clientset, peerName, name string) (*peerList, error) {
var list *peerList
err := cml.LoopUpdate(func() error {
var err error
list, err = cml.GetPeerList()
if err != nil {
return err
}
if !list.contains(peerName) {
list.add(peerName, name)
err = cml.UpdatePeerList(*list)
if err != nil {
return err
}
}
return nil
})
return list, err
}
func checkIamInPeerList(cml *configMapAnnotations, c *kubernetes.Clientset, peerName string) (bool, error) {
if err := cml.Init(); err != nil {
return false, err
}
for _, addr := range peers {
fmt.Println(addr)
list, err := cml.GetPeerList()
if err != nil {
return false, err
}
common.Log.Debugf("[kube-peers] Checking peer %q against list %v", peerName, list)
return list.contains(peerName), nil
}
// For each of those peers that is no longer listed as a node by
// Kubernetes, remove it from Weave IPAM
func reclaimRemovedPeers(weave *weaveapi.Client, cml *configMapAnnotations, nodes []nodeInfo, myPeerName string) error {
for {
// 1. Compare peers stored in the peerList against all peers reported by k8s now.
storedPeerList, err := cml.GetPeerList()
if err != nil {
return err
}
peerMap := make(map[string]peerInfo, len(storedPeerList.Peers))
for _, peer := range storedPeerList.Peers {
peerMap[peer.NodeName] = peer
}
for _, node := range nodes {
delete(peerMap, node.name)
}
common.Log.Debugln("[kube-peers] Nodes that have disappeared:", peerMap)
if len(peerMap) == 0 {
break
}
// 2. Loop for each X in the first set and not in the second - we wish to remove X from our data structures
for _, peer := range peerMap {
common.Log.Debugln("[kube-peers] Preparing to remove disappeared peer", peer)
okToRemove := false
// 3. Check if there is an existing annotation with key X
if existingAnnotation, found := cml.cm.Annotations[peer.PeerName]; found {
common.Log.Debugln("[kube-peers] Existing annotation", existingAnnotation)
// 4. If annotation already contains my identity, ok;
if existingAnnotation == myPeerName {
okToRemove = true
}
} else {
// 5. If non-existent, write an annotation with key X and contents "my identity"
common.Log.Debugln("[kube-peers] Noting I plan to remove ", peer.PeerName)
if err := cml.UpdateAnnotation(peer.PeerName, myPeerName); err == nil {
okToRemove = true
}
}
if okToRemove {
// 6. If step 4 or 5 succeeded, rmpeer X
result, err := weave.RmPeer(peer.PeerName)
common.Log.Infoln("[kube-peers] rmpeer of", peer.PeerName, ":", result)
if err != nil {
return err
}
cml.LoopUpdate(func() error {
// 7aa. Remove any annotations Z* that have contents X
if err := cml.RemoveAnnotationsWithValue(peer.PeerName); err != nil {
return err
}
// 7a. Remove X from peerList
storedPeerList.remove(peer.PeerName)
common.Log.Infoln("[kube-peers] Removing peer ", peer.PeerName, ". Expecting to remove linked annotation next.")
if err := cml.UpdatePeerList(*storedPeerList); err != nil {
return err
}
common.Log.Infoln("[kube-peers] Removing annotation ", peer.PeerName)
// 7b. Remove annotation with key X
return cml.RemoveAnnotation(peer.PeerName)
})
common.Log.Debugln("[kube-peers] Finished removal of ", peer.PeerName)
}
// 8. If step 5 failed due to optimistic lock conflict, stop: someone else is handling X
// Step 3-5 is to protect against two simultaneous rmpeers of X
// Step 4 is to pick up again after a restart between step 5 and step 7b
// If the peer doing the reclaim disappears between steps 5 and 7a, then someone will clean it up in step 7aa
// If peer doing the reclaim disappears forever between 7a and 7b then we get a dangling annotation
// This should be sufficiently rare that we don't care.
}
// 9. Go back to step 1 until there is no difference between the two sets
}
// Question: Should we narrow step 2 by checking against Weave Net IPAM?
// i.e. If peer X owns any address space and is marked unreachable, we want to rmpeer X
return nil
}
func main() {
var (
justReclaim bool
justCheck bool
peerName string
nodeName string
logLevel string
)
flag.BoolVar(&justReclaim, "reclaim", false, "reclaim IP space from dead peers")
flag.BoolVar(&justCheck, "check-peer-new", false, "return success if peer name is not stored in annotation")
flag.StringVar(&peerName, "peer-name", "unknown", "name of this Weave Net peer")
flag.StringVar(&nodeName, "node-name", "unknown", "name of this Kubernetes node")
flag.StringVar(&logLevel, "log-level", "info", "logging level (debug, info, warning, error)")
flag.Parse()
common.SetLogLevel(logLevel)
config, err := rest.InClusterConfig()
if err != nil {
common.Log.Fatalf("[kube-peers] Could not get cluster config: %v", err)
}
c, err := kubernetes.NewForConfig(config)
if err != nil {
common.Log.Fatalf("[kube-peers] Could not make Kubernetes connection: %v", err)
}
if justCheck {
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
exists, err := checkIamInPeerList(cml, c, peerName)
if err != nil {
common.Log.Fatalf("[kube-peers] Could not check peer list: %v", err)
}
if exists {
os.Exit(9)
} else {
os.Exit(0)
}
}
peers, err := getKubePeers(c)
if err != nil {
common.Log.Fatalf("[kube-peers] Could not get peers: %v", err)
}
if justReclaim {
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
list, err := addMyselfToPeerList(cml, c, peerName, nodeName)
if err != nil {
common.Log.Fatalf("[kube-peers] Could not update peer list: %v", err)
}
common.Log.Infoln("[kube-peers] Added myself to peer list", list)
weave := weaveapi.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), common.Log)
err = reclaimRemovedPeers(weave, cml, peers, peerName)
if err != nil {
common.Log.Fatalf("[kube-peers] Error while reclaiming space: %v", err)
}
return
}
for _, node := range peers {
fmt.Println(node.addr)
}
}
+29 -1
View File
@@ -30,6 +30,7 @@ HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
HOST_ROOT=${HOST_ROOT:-/host}
CONN_LIMIT=${CONN_LIMIT:-30}
DB_PREFIX=${DB_PREFIX:-/weavedb/weave-net}
# Check if the IP range overlaps anything existing on the host
/usr/bin/weaveutil netcheck $IPALLOC_RANGE weave
@@ -87,6 +88,29 @@ if [ -z "$IPALLOC_INIT" ]; then
IPALLOC_INIT="consensus=$(peer_count $KUBE_PEERS)"
fi
# Find out what peer name we will be using
PEERNAME=$(/home/weave/weaver $EXTRA_ARGS --print-peer-name --host-root=$HOST_ROOT --db-prefix="$DB_PREFIX")
if [ -z "$PEERNAME" ]; then
echo "Unable to get peer name" >&2
exit 2
fi
# If we have persisted data and peer reclaim is in use
if [ -f ${DB_PREFIX}data.db ]; then
if /usr/bin/weaveutil get-db-flag "$DB_PREFIX" peer-reclaim >/dev/null ; then
# If this peer name is not stored in the list then we were removed by another peer.
if /home/weave/kube-peers -check-peer-new -peer-name="$PEERNAME" -log-level=debug ; then
# In order to avoid a CRDT clash, clean up
echo "Peer not in list; removing persisted data" >&2
rm -f ${DB_PREFIX}data.db
fi
fi
fi
# flag that peer reclaim is now in use - note we have to do this before
# weaver is running as the DB can only be opened by one process at a time
/usr/bin/weaveutil set-db-flag "$DB_PREFIX" peer-reclaim ok
post_start_actions() {
# Wait for weave process to become responsive
while true ; do
@@ -108,6 +132,9 @@ post_start_actions() {
export HOST_ROOT
/home/weave/weave --local setup-cni
# Attempt to run the reclaim process, but don't halt the script if it fails
/home/weave/kube-peers -reclaim -node-name="$HOSTNAME" -peer-name="$PEERNAME" -log-level=debug || true
# Expose the weave network so host processes can communicate with pods
/home/weave/weave --local expose $WEAVE_EXPOSE_IP
}
@@ -115,9 +142,10 @@ post_start_actions() {
post_start_actions &
/home/weave/weaver $EXTRA_ARGS --port=6783 $(router_bridge_opts) \
--name="$PEERNAME" \
--host-root=$HOST_ROOT \
--http-addr=$HTTP_ADDR --status-addr=$STATUS_ADDR --docker-api='' --no-dns \
--db-prefix="/weavedb/weave-net" \
--db-prefix="$DB_PREFIX" \
--ipalloc-range=$IPALLOC_RANGE $NICKNAME_ARG \
--ipalloc-init $IPALLOC_INIT \
--conn-limit=$CONN_LIMIT \
@@ -47,6 +47,44 @@ items:
- kind: ServiceAccount
name: weave-net
namespace: kube-system
- apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: weave-net-kube-peer
namespace: kube-system
labels:
name: weave-net-kube-peer
rules:
- apiGroups:
- ''
resources:
- configmaps
resourceNames:
- weave-net
verbs:
- get
- update
- apiGroups:
- ''
resources:
- configmaps
verbs:
- create
- apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: weave-net-kube-peer
namespace: kube-system
labels:
name: weave-net-kube-peer
roleRef:
kind: Role
name: weave-net-kube-peer
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: weave-net
namespace: kube-system
- apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
@@ -0,0 +1,199 @@
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: ServiceAccount
metadata:
name: weave-net
labels:
name: weave-net
namespace: kube-system
- apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: weave-net
labels:
name: weave-net
rules:
- apiGroups:
- ''
resources:
- pods
- namespaces
- nodes
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- networkpolicies
verbs:
- get
- list
- watch
- apiGroups:
- 'networking.k8s.io'
resources:
- networkpolicies
verbs:
- get
- list
- watch
- apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: weave-net
labels:
name: weave-net
roleRef:
kind: ClusterRole
name: weave-net
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: weave-net
namespace: kube-system
- apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: weave-net
namespace: kube-system
labels:
name: weave-net
rules:
- apiGroups:
- ''
resources:
- configmaps
resourceNames:
- weave-net
verbs:
- get
- update
- apiGroups:
- ''
resources:
- configmaps
verbs:
- create
- apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: weave-net
namespace: kube-system
labels:
name: weave-net
roleRef:
kind: Role
name: weave-net
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: weave-net
namespace: kube-system
- apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: weave-net
labels:
name: weave-net
namespace: kube-system
spec:
template:
metadata:
labels:
name: weave-net
spec:
containers:
- name: weave
command:
- /home/weave/launch.sh
env:
- name: HOSTNAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
image: 'weaveworks/weave-kube:latest'
imagePullPolicy: Always
livenessProbe:
httpGet:
host: 127.0.0.1
path: /status
port: 6784
initialDelaySeconds: 30
resources:
requests:
cpu: 10m
securityContext:
privileged: true
volumeMounts:
- name: weavedb
mountPath: /weavedb
- name: cni-bin
mountPath: /host/opt
- name: cni-bin2
mountPath: /host/home
- name: cni-conf
mountPath: /host/etc
- name: dbus
mountPath: /host/var/lib/dbus
- name: lib-modules
mountPath: /lib/modules
- name: xtables-lock
mountPath: /run/xtables.lock
readOnly: false
- name: weave-npc
env:
- name: HOSTNAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
image: 'weaveworks/weave-npc:latest'
imagePullPolicy: Always
#npc-args
resources:
requests:
cpu: 10m
securityContext:
privileged: true
volumeMounts:
- name: xtables-lock
mountPath: /run/xtables.lock
readOnly: false
hostNetwork: true
hostPID: true
restartPolicy: Always
securityContext:
seLinuxOptions: {}
serviceAccountName: weave-net
tolerations:
- effect: NoSchedule
operator: Exists
volumes:
- name: weavedb
hostPath:
path: /var/lib/weave
- name: cni-bin
hostPath:
path: /opt
- name: cni-bin2
hostPath:
path: /home
- name: cni-conf
hostPath:
path: /etc
- name: dbus
hostPath:
path: /var/lib/dbus
- name: lib-modules
hostPath:
path: /lib/modules
- name: xtables-lock
hostPath:
path: /run/xtables.lock
updateStrategy:
type: RollingUpdate
+43 -37
View File
@@ -7,12 +7,13 @@ import (
"github.com/coreos/go-iptables/iptables"
"github.com/spf13/cobra"
coreapi "k8s.io/api/core/v1"
extnapi "k8s.io/api/extensions/v1beta1"
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
coreapi "k8s.io/client-go/pkg/api/v1"
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
"k8s.io/client-go/pkg/fields"
"k8s.io/client-go/pkg/runtime"
"k8s.io/client-go/pkg/util/wait"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
@@ -29,12 +30,13 @@ var (
logLevel string
allowMcast bool
nodeName string
legacy bool
)
func handleError(err error) { common.CheckFatal(err) }
func makeController(getter cache.Getter, resource string,
objType runtime.Object, handlers cache.ResourceEventHandlerFuncs) *cache.Controller {
objType runtime.Object, handlers cache.ResourceEventHandlerFuncs) cache.Controller {
listWatch := cache.NewListWatchFromClient(getter, resource, "", fields.Everything())
_, controller := cache.NewInformer(listWatch, objType, 0, handlers)
return controller
@@ -50,11 +52,7 @@ func resetIPTables(ipt *iptables.IPTables) error {
return err
}
if err := ipt.ClearChain(npc.TableFilter, npc.MainChain); err != nil {
return err
}
return nil
return ipt.ClearChain(npc.TableFilter, npc.MainChain)
}
func resetIPSets(ips ipset.Interface) error {
@@ -116,15 +114,13 @@ func createBaseRules(ipt *iptables.IPTables, ips ipset.Interface) error {
if err := ips.Create(npc.LocalIpset, ipset.HashIP); err != nil {
return err
}
if err := ipt.Append(npc.TableFilter, npc.MainChain,
"-m", "set", "!", "--match-set", npc.LocalIpset, "dst", "-j", "ACCEPT"); err != nil {
return err
}
return nil
return ipt.Append(npc.TableFilter, npc.MainChain,
"-m", "set", "!", "--match-set", npc.LocalIpset, "dst", "-j", "ACCEPT")
}
func root(cmd *cobra.Command, args []string) {
var npController cache.Controller
common.SetLogLevel(logLevel)
if nodeName == "" {
// HOSTNAME is set by Kubernetes for pods in the host network namespace
@@ -135,6 +131,10 @@ func root(cmd *cobra.Command, args []string) {
}
common.Log.Infof("Starting Weaveworks NPC %s; node name %q", version, nodeName)
if legacy {
common.Log.Info("Running in legacy mode (k8s pre-1.7 network policy semantics)")
}
if err := metrics.Start(metricsAddr); err != nil {
common.Log.Fatalf("Failed to start metrics: %v", err)
}
@@ -158,7 +158,7 @@ func root(cmd *cobra.Command, args []string) {
handleError(resetIPSets(ips))
handleError(createBaseRules(ipt, ips))
npc := npc.New(nodeName, ipt, ips)
npc := npc.New(nodeName, legacy, ipt, ips)
nsController := makeController(client.Core().RESTClient(), "namespaces", &coreapi.Namespace{},
cache.ResourceEventHandlerFuncs{
@@ -200,25 +200,30 @@ func root(cmd *cobra.Command, args []string) {
handleError(npc.UpdatePod(old.(*coreapi.Pod), new.(*coreapi.Pod)))
}})
npController := makeController(client.Extensions().RESTClient(), "networkpolicies", &extnapi.NetworkPolicy{},
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
handleError(npc.AddNetworkPolicy(obj.(*extnapi.NetworkPolicy)))
},
DeleteFunc: func(obj interface{}) {
switch obj := obj.(type) {
case *extnapi.NetworkPolicy:
handleError(npc.DeleteNetworkPolicy(obj))
case cache.DeletedFinalStateUnknown:
// We know this object has gone away, but its final state is no longer
// available from the API server. Instead we use the last copy of it
// that we have, which is good enough for our cleanup.
handleError(npc.DeleteNetworkPolicy(obj.Obj.(*extnapi.NetworkPolicy)))
}
},
UpdateFunc: func(old, new interface{}) {
handleError(npc.UpdateNetworkPolicy(old.(*extnapi.NetworkPolicy), new.(*extnapi.NetworkPolicy)))
}})
npHandlers := cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
handleError(npc.AddNetworkPolicy(obj))
},
DeleteFunc: func(obj interface{}) {
switch obj := obj.(type) {
case cache.DeletedFinalStateUnknown:
// We know this object has gone away, but its final state is no longer
// available from the API server. Instead we use the last copy of it
// that we have, which is good enough for our cleanup.
handleError(npc.DeleteNetworkPolicy(obj.Obj))
default:
handleError(npc.DeleteNetworkPolicy(obj))
}
},
UpdateFunc: func(old, new interface{}) {
handleError(npc.UpdateNetworkPolicy(old, new))
},
}
if legacy {
npController = makeController(client.Extensions().RESTClient(), "networkpolicies", &extnapi.NetworkPolicy{}, npHandlers)
} else {
npController = makeController(client.NetworkingV1().RESTClient(), "networkpolicies", &networkingv1.NetworkPolicy{}, npHandlers)
}
go nsController.Run(wait.NeverStop)
go podController.Run(wait.NeverStop)
@@ -239,6 +244,7 @@ func main() {
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "debug", "logging level (debug, info, warning, error)")
rootCmd.PersistentFlags().BoolVar(&allowMcast, "allow-mcast", true, "allow all multicast traffic")
rootCmd.PersistentFlags().StringVar(&nodeName, "node-name", "", "only generate rules that apply to this node")
rootCmd.PersistentFlags().BoolVar(&legacy, "use-legacy-netpol", false, "use legacy network policies (pre k8s 1.7 vsn)")
handleError(rootCmd.Execute())
}
-1
View File
@@ -3,7 +3,6 @@ FROM DOCKERHUB_USER/weaveARCH_EXT
ENTRYPOINT ["/home/weave/sigproxy", "/home/weave/weave"]
ADD ./sigproxy ./symlink /home/weave/
ADD ./docker /usr/bin/
ADD ./weavewait /w/w
ADD ./weavewait_noop /w-noop/w
ADD ./weavewait_nomcast /w-nomcast/w
+20 -17
View File
@@ -34,23 +34,7 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
},
"printIPAMRanges": func(router weave.NetworkRouterStatus, status ipam.Status) string {
var buffer bytes.Buffer
type stats struct {
ips uint32
nickname string
reachable bool
}
peerStats := make(map[string]*stats)
for _, entry := range status.Entries {
s, found := peerStats[entry.Peer]
if !found {
s = &stats{nickname: entry.Nickname, reachable: entry.IsKnownPeer}
peerStats[entry.Peer] = s
}
s.ips += entry.Size
}
peerStats := summariseIpamStats(&status)
printOwned := func(name string, nickName string, info string, ips uint32) {
percentageRanges := float32(ips) * 100.0 / float32(status.RangeNumIPs)
@@ -116,6 +100,25 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
"trimSuffix": strings.TrimSuffix,
})
type ipamStats struct {
ips uint32
nickname string
reachable bool
}
func summariseIpamStats(status *ipam.Status) map[string]*ipamStats {
peerStats := make(map[string]*ipamStats)
for _, entry := range status.Entries {
s, found := peerStats[entry.Peer]
if !found {
s = &ipamStats{nickname: entry.Nickname, reachable: entry.IsKnownPeer}
peerStats[entry.Peer] = s
}
s.ips += entry.Size
}
return peerStats
}
func countDNSEntries(entries []nameserver.EntryStatus) int {
count := 0
for _, entry := range entries {
+19 -14
View File
@@ -131,6 +131,7 @@ func main() {
var (
justVersion bool
justPeerName bool
config mesh.Config
bridgeConfig weavenet.BridgeConfig
networkConfig weave.NetworkConfig
@@ -163,6 +164,7 @@ func main() {
)
mflag.BoolVar(&justVersion, []string{"-version"}, false, "print version and exit")
mflag.BoolVar(&justPeerName, []string{"-print-peer-name"}, false, "print peer name and exit")
mflag.StringVar(&config.Host, []string{"-host"}, "", "router host")
mflag.IntVar(&config.Port, []string{"-port"}, mesh.Port, "router port")
mflag.IntVar(&protocolMinVersion, []string{"-min-protocol-version"}, mesh.ProtocolMinVersion, "minimum weave protocol version")
@@ -210,11 +212,7 @@ func main() {
mflag.StringVar(&pluginConfig.Socket, []string{"-plugin-socket"}, "/run/docker/plugins/weave.sock", "plugin socket on which to listen")
mflag.StringVar(&pluginConfig.MeshSocket, []string{"-plugin-mesh-socket"}, "/run/docker/plugins/weavemesh.sock", "plugin socket on which to listen in mesh mode")
proxyConfig := configureProxy(version, defaultDockerHost)
if bridgeConfig.AWSVPC {
proxyConfig.NoMulticastRoute = true
proxyConfig.KeepTXOn = true
}
proxyConfig := newProxyConfig()
// crude way of detecting that we probably have been started in a
// container, with `weave launch` --> suppress misleading paths in
@@ -230,6 +228,17 @@ func main() {
fmt.Printf("weave %s\n", version)
os.Exit(0)
}
name := peerName(routerName, bridgeConfig.WeaveBridgeName, dbPrefix, hostRoot)
if justPeerName {
fmt.Printf("%s\n", name)
os.Exit(0)
}
proxyConfig.DockerHost = dockerAPI
if bridgeConfig.AWSVPC {
proxyConfig.NoMulticastRoute = true
proxyConfig.KeepTXOn = true
}
peers = mflag.Args()
if resume && len(peers) > 0 {
@@ -282,8 +291,6 @@ func main() {
}
}
name := peerName(routerName, bridgeConfig.WeaveBridgeName, dbPrefix, hostRoot)
bridgeConfig.Mac = name.String()
bridgeConfig.Port = config.Port
bridgeType, err := weavenet.EnsureBridge(procPath, &bridgeConfig, Log)
@@ -322,7 +329,7 @@ func main() {
checkFatal(err)
defer db.Close()
router, err := weave.NewNetworkRouter(config, networkConfig, name, nickName, overlay, db)
router, err := weave.NewNetworkRouter(config, networkConfig, bridgeConfig, name, nickName, overlay, db)
checkFatal(err)
Log.Println("Our name is", router.Ourself)
@@ -469,17 +476,17 @@ func main() {
// Run this on its own goroutine because the allocator can block
// We remove the default route installed by the kernel,
// because awsvpc has installed it as well
go expose(allocator, defaultSubnet, bridgeConfig.WeaveBridgeName, bridgeConfig.AWSVPC, waitReady.Add())
go exposeForAWSVPC(allocator, defaultSubnet, bridgeConfig.WeaveBridgeName, waitReady.Add())
}
signals.SignalHandlerLoop(common.Log, router)
}
func expose(alloc *ipam.Allocator, subnet address.CIDR, bridgeName string, removeDefaultRoute bool, ready func()) {
func exposeForAWSVPC(alloc *ipam.Allocator, subnet address.CIDR, bridgeName string, ready func()) {
addr, err := alloc.Allocate("weave:expose", subnet, false, func() bool { return false })
checkFatal(err)
cidr := address.MakeCIDR(subnet, addr)
err = weavenet.AddBridgeAddr(bridgeName, cidr.IPNet(), removeDefaultRoute)
err = weavenet.Expose(bridgeName, cidr.IPNet(), true, false)
checkFatal(err)
Log.Printf("Bridge %q exposed on address %v", bridgeName, cidr)
ready()
@@ -525,12 +532,10 @@ func (nopPacketLogging) LogPacket(string, weave.PacketKey) {
func (nopPacketLogging) LogForwardPacket(string, weave.ForwardPacketKey) {
}
func configureProxy(version string, defaultDockerHost string) *weaveproxy.Config {
func newProxyConfig() *weaveproxy.Config {
proxyConfig := weaveproxy.Config{
Version: version,
Image: getenvOrDefault("EXEC_IMAGE", "weaveworks/weaveexec"),
DockerBridge: getenvOrDefault("DOCKER_BRIDGE", "docker0"),
DockerHost: defaultDockerHost,
}
mflag.BoolVar(&proxyConfig.Enabled, []string{"-proxy"}, false, "instruct Weave Net to start its Docker proxy")
mflagext.ListVar(&proxyConfig.ListenAddrs, []string{"H"}, nil, "addresses on which to listen for Docker proxy")
+21
View File
@@ -82,6 +82,27 @@ var metrics = []metric{
ch <- intGauge(desc, metrics.Flows)
}
}},
{desc("weave_ipam_unreachable_count", "Number of unreachable peers."),
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
var count int
for _, entry := range summariseIpamStats(s.IPAM) {
if !entry.reachable {
count++
}
}
ch <- intGauge(desc, count)
}},
{desc("weave_ipam_unreachable_percentage", "Percentage of IP addresses owned by unreachable peers."),
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
var totalUnreachable uint32
for _, entry := range summariseIpamStats(s.IPAM) {
if !entry.reachable {
totalUnreachable += entry.ips
}
}
percentage := float64(totalUnreachable) * 100.0 / float64(s.IPAM.RangeNumIPs)
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, percentage)
}},
{desc("weave_ipam_pending_allocates", "Number of pending allocates."),
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
if s.IPAM != nil {
+17 -23
View File
@@ -8,7 +8,6 @@ import (
"strings"
"syscall"
docker "github.com/fsouza/go-dockerclient"
"github.com/vishvananda/netns"
weavenet "github.com/weaveworks/weave/net"
@@ -70,21 +69,6 @@ func attach(args []string) error {
return err
}
func containerPid(containerID string) (int, error) {
c, err := docker.NewVersionedClientFromEnv("1.18")
if err != nil {
return 0, fmt.Errorf("unable to connect to docker: %s", err)
}
container, err := c.InspectContainer(containerID)
if err != nil {
return 0, fmt.Errorf("unable to inspect container %s: %s", containerID, err)
}
if container.State.Pid == 0 {
return 0, fmt.Errorf("container %s not running", containerID)
}
return container.State.Pid, nil
}
func processExists(pid int) bool {
err := syscall.Kill(pid, syscall.Signal(0))
return err == nil || err == syscall.EPERM
@@ -119,21 +103,31 @@ func detach(args []string) error {
}
func rewriteEtcHosts(args []string) error {
if len(args) < 4 {
cmdUsage("rewrite-etc-hosts", "<hosts-path> <fqdn> <image> <cidr>... [name:addr...]")
if len(args) < 3 {
cmdUsage("rewrite-etc-hosts", "<container-id> <image> <cidr> [name:addr...]")
}
hostsPath := args[0]
fqdn := args[1]
image := args[2]
containerID := args[0]
image := args[1]
cidrs := args[2]
extraHosts := args[3:]
container, err := inspectContainer(containerID)
if err != nil {
return err
}
hostsPath := container.HostsPath
fqdn := container.Config.Hostname + "." + container.Config.Domainname
var ips []*net.IPNet
for _, cidr := range strings.Fields(args[3]) {
for _, cidr := range strings.Fields(cidrs) {
_, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
return err
}
ips = append(ips, ipnet)
}
extraHosts := args[4:]
docker := os.Getenv("DOCKER_HOST")
if docker == "" {
docker = "unix:///var/run/docker.sock"
+285
View File
@@ -0,0 +1,285 @@
package main
import (
"fmt"
"os"
"strings"
docker "github.com/fsouza/go-dockerclient"
)
func inspectContainer(containerNameOrID string) (*docker.Container, error) {
c, err := docker.NewVersionedClientFromEnv("1.18")
if err != nil {
return nil, fmt.Errorf("unable to connect to docker: %s", err)
}
container, err := c.InspectContainer(containerNameOrID)
if err != nil {
return nil, fmt.Errorf("unable to inspect container %s: %s", containerNameOrID, err)
}
return container, nil
}
func containerPid(containerID string) (int, error) {
container, err := inspectContainer(containerID)
if err != nil {
return 0, err
}
if container.State.Pid == 0 {
return 0, fmt.Errorf("container %s not running", containerID)
}
return container.State.Pid, nil
}
func containerID(args []string) error {
if len(args) < 1 {
cmdUsage("container-id", "<container-name-or-short-id>")
}
container, err := inspectContainer(args[0])
if err != nil {
return err
}
fmt.Print(container.ID)
return nil
}
func containerState(args []string) error {
if len(args) < 1 {
cmdUsage("container-state", "<container-id> [<image-prefix>]")
}
container, err := inspectContainer(args[0])
if err != nil {
return err
}
if len(args) == 1 {
fmt.Print(container.State.StateString())
} else {
imagePrefix := args[1]
if strings.HasPrefix(container.Config.Image, imagePrefix) {
fmt.Print(container.State.StateString())
} else {
if container.State.Running {
fmt.Print("running image mismatch: ", container.Config.Image)
} else {
fmt.Print("image mismatch: ", container.Config.Image)
}
}
}
return nil
}
func containerFQDN(args []string) error {
if len(args) < 1 {
cmdUsage("container-fqdn", "<container-id>")
}
container, err := inspectContainer(args[0])
if err != nil {
return err
}
fmt.Print(container.Config.Hostname, ".", container.Config.Domainname)
return nil
}
func runContainer(args []string) error {
env := []string{}
name := ""
net := ""
pid := ""
privileged := false
restart := docker.NeverRestart()
volumes := []string{}
volumesFrom := []string{}
done := false
for i := 0; i < len(args) && !done; {
switch args[i] {
case "-e", "--env":
if v := os.Getenv(args[i+1]); v != "" {
env = append(env, args[i+1]+"="+v)
} else {
env = append(env, args[i+1])
}
args = append(args[:i], args[i+2:]...)
case "--name":
name = args[i+1]
args = append(args[:i], args[i+2:]...)
case "--net":
net = args[i+1]
args = append(args[:i], args[i+2:]...)
case "--pid":
pid = args[i+1]
args = append(args[:i], args[i+2:]...)
case "--privileged":
privileged = true
args = append(args[:i], args[i+1:]...)
case "--restart":
restart = docker.RestartPolicy{Name: args[i+1]}
args = append(args[:i], args[i+2:]...)
case "-v", "--volume":
// Must dedup binds, otherwise, container fails creation
skip := false
for _, v := range volumes {
if v == args[i+1] {
skip = true
}
}
if !skip {
volumes = append(volumes, args[i+1])
}
args = append(args[:i], args[i+2:]...)
case "--volumes-from":
volumesFrom = append(volumesFrom, args[i+1])
args = append(args[:i], args[i+2:]...)
default:
done = true
}
}
if len(args) < 2 {
cmdUsage("run-container", `[options] <image> <cmd> [[<cmd-options>] <cmd-arg1> [<cmd-arg2> ...]]
-e, --env list Set environment variables
--name string Assign a name to the container
--net string Network Mode
--pid string PID namespace to use
--privileged Give extended privileges to this container
--restart string Restart policy to apply when a container exits (default "no")
-v, --volume list Bind mount a volume
--volumes-from list Mount volumes from the specified container(s)
`)
}
image := args[0]
cmds := args[1:]
c, err := docker.NewVersionedClientFromEnv("1.18")
if err != nil {
return fmt.Errorf("unable to connect to docker: %s", err)
}
config := docker.Config{Image: image, Env: env, Cmd: cmds}
hostConfig := docker.HostConfig{NetworkMode: net, PidMode: pid, Privileged: privileged, RestartPolicy: restart, Binds: volumes, VolumesFrom: volumesFrom}
container, err := c.CreateContainer(docker.CreateContainerOptions{Name: name, Config: &config, HostConfig: &hostConfig})
if err != nil {
return fmt.Errorf("unable to create container: %s", err)
}
err = c.StartContainer(container.ID, &hostConfig)
if err != nil {
return fmt.Errorf("unable to start container: %s", err)
}
fmt.Print(container.ID)
return nil
}
func listContainers(args []string) error {
if len(args) > 1 {
cmdUsage("list-containers", "[<label>]")
}
c, err := docker.NewVersionedClientFromEnv("1.18")
if err != nil {
return fmt.Errorf("unable to connect to docker: %s", err)
}
opts := docker.ListContainersOptions{All: true}
if len(args) == 1 {
label := args[0]
opts.Filters = map[string][]string{"label": {label}}
}
containers, err := c.ListContainers(opts)
if err != nil {
return fmt.Errorf("unable to list containers: %s", err)
}
for _, container := range containers {
fmt.Println(container.ID)
}
return nil
}
func stopContainer(args []string) error {
if len(args) < 1 {
cmdUsage("stop-container", "<container-id> [<container-id2> ...]")
}
c, err := docker.NewVersionedClientFromEnv("1.18")
if err != nil {
return fmt.Errorf("unable to connect to docker: %s", err)
}
for _, containerID := range args {
err = c.StopContainer(containerID, 10)
if err != nil {
return fmt.Errorf("unable to stop container %s: %s", containerID, err)
}
}
return nil
}
func killContainer(args []string) error {
if len(args) < 1 {
cmdUsage("kill-container", "<container-id> [<container-id2> ...]")
}
c, err := docker.NewVersionedClientFromEnv("1.18")
if err != nil {
return fmt.Errorf("unable to connect to docker: %s", err)
}
for _, containerID := range args {
err = c.KillContainer(docker.KillContainerOptions{ID: containerID, Signal: docker.SIGKILL})
if err != nil {
return fmt.Errorf("unable to stop container %s: %s", containerID, err)
}
}
return nil
}
func removeContainer(args []string) error {
if len(args) < 1 {
cmdUsage("remove-container", "[-f | --force] [-v | --volumes] <container-id> [<container-id2> ...]")
}
force := false
volumes := false
for i := 0; i < len(args); {
switch args[i] {
case "--force":
case "-f":
force = true
args = append(args[:i], args[i+1:]...)
case "--volumes":
case "-v":
volumes = true
args = append(args[:i], args[i+1:]...)
default:
i++
}
}
c, err := docker.NewVersionedClientFromEnv("1.18")
if err != nil {
return fmt.Errorf("unable to connect to docker: %s", err)
}
for _, containerID := range args {
err = c.RemoveContainer(docker.RemoveContainerOptions{
ID: containerID, Force: force, RemoveVolumes: volumes})
if err != nil {
return fmt.Errorf("unable to stop container %s: %s", containerID, err)
}
}
return nil
}
+54
View File
@@ -0,0 +1,54 @@
// Get/set arbitrary flags in the Weave Net persistence DB
package main
import (
"fmt"
"github.com/weaveworks/weave/db"
)
const dbFlagPrefix = "flag:"
func getDBFlag(args []string) error {
if len(args) != 2 {
cmdUsage("check-db-flag", "<db-prefix> <flag-name>")
}
dbPrefix := args[0]
flagName := args[1]
d, err := db.NewBoltDBReadOnly(dbPrefix)
if err != nil {
return err
}
defer d.Close()
var value string
nameFound, err := d.Load(dbFlagPrefix+flagName, &value)
if err != nil {
return err
}
if !nameFound {
return fmt.Errorf("")
}
fmt.Print(value)
return nil
}
func setDBFlag(args []string) error {
if len(args) != 3 {
cmdUsage("set-db-flag", "<db-prefix> <flag-name> <flag-value>")
}
dbPrefix := args[0]
flagName := args[1]
value := args[2]
d, err := db.NewBoltDB(dbPrefix)
if err != nil {
return err
}
defer d.Close()
err = d.Save(dbFlagPrefix+flagName, value)
if err != nil {
return err
}
return nil
}
-23
View File
@@ -1,23 +0,0 @@
package main
import (
weavenet "github.com/weaveworks/weave/net"
)
func exposeNAT(args []string) error {
if len(args) < 1 {
cmdUsage("expose-nat", "<cidr>...")
}
cidrs, err := parseCIDRs(args)
if err != nil {
return err
}
for _, cidr := range cidrs {
if err := weavenet.ExposeNAT(*cidr); err != nil {
return err
}
}
return nil
}
+14 -2
View File
@@ -26,6 +26,15 @@ func init() {
"remove-plugin-network": removePluginNetwork,
"container-addrs": containerAddrs,
"process-addrs": processAddrs,
"container-id": containerID,
"container-state": containerState,
"container-fqdn": containerFQDN,
"run-container": runContainer,
"list-containers": listContainers,
"stop-container": stopContainer,
"kill-container": killContainer,
"remove-container": removeContainer,
"create-volume-container": createVolumeContainer,
"attach-container": attach,
"detach-container": detach,
"configure-arp": configureARP,
@@ -36,12 +45,13 @@ func init() {
"list-netdevs": listNetDevs,
"cni-net": cniNet,
"cni-ipam": cniIPAM,
"expose-nat": exposeNAT,
"bridge-ip": bridgeIP,
"unique-id": uniqueID,
"swarm-manager-peers": swarmManagerPeers,
"is-docker-plugin-enabled": isDockerPluginEnabled,
"rewrite-etc-hosts": rewriteEtcHosts,
"get-db-flag": getDBFlag,
"set-db-flag": setDBFlag,
}
}
@@ -75,7 +85,9 @@ func main() {
os.Exit(1)
}
if err := cmd(os.Args[2:]); err != nil {
fmt.Fprintln(os.Stderr, err)
if err.Error() != "" {
fmt.Fprintln(os.Stderr, err)
}
os.Exit(1)
}
}
+42
View File
@@ -0,0 +1,42 @@
package main
import (
"fmt"
docker "github.com/fsouza/go-dockerclient"
)
func createVolumeContainer(args []string) error {
if len(args) < 3 {
cmdUsage("create-volume-container", "<name> <image-name-or-id> <label> [<bind-mount1> <bind-mount2> ...]")
}
containerName := args[0]
image := args[1]
label := args[2]
bindMounts := args[3:]
c, err := docker.NewVersionedClientFromEnv("1.18")
if err != nil {
return fmt.Errorf("unable to connect to docker: %s", err)
}
container, err := c.InspectContainer(containerName)
if err == nil {
fmt.Println("volume container already exists " + containerName + ":" + container.ID)
return nil
}
volumes := make(map[string]struct{})
for _, m := range bindMounts {
volumes[m] = struct{}{}
}
labels := map[string]string{label: ""}
config := docker.Config{Image: image, Volumes: volumes, Labels: labels, Entrypoint: []string{"data-only"}}
hostConfig := docker.HostConfig{}
_, err = c.CreateContainer(docker.CreateContainerOptions{Name: containerName, Config: &config, HostConfig: &hostConfig})
if err != nil {
return fmt.Errorf("unable to create container: %s", err)
}
return nil
}
+1 -2
View File
@@ -20,8 +20,7 @@ func (proxy *Proxy) RewriteEtcHosts(hostsPath, fqdn string, ips []*net.IPNet, ex
contents := buf.String()
cmdLine := fmt.Sprintf("echo '%s' > %s && rm -f %s && echo '%s' > %s", contents, mntHosts, mntHosts, contents, mntHosts)
mounts := []string{hostsPathDir + ":" + mnt}
proxy.runTransientContainer([]string{"sh"}, []string{"-c", cmdLine}, mounts)
return nil
return proxy.runTransientContainer([]string{"sh"}, []string{"-c", cmdLine}, mounts)
}
// we assume (for compatibility with the weave script) that fqdn has a dot
+6 -7
View File
@@ -60,7 +60,6 @@ type Config struct {
NoDefaultIPAM bool
NoRewriteHosts bool
TLSConfig TLSConfig
Version string
WithoutDNS bool
NoMulticastRoute bool
KeepTXOn bool
@@ -330,11 +329,7 @@ func copyOwnerAndPermissions(from, to string) error {
return nil
}
if err = os.Chown(to, int(moreStat.Uid), int(moreStat.Gid)); err != nil {
return err
}
return nil
return os.Chown(to, int(moreStat.Uid), int(moreStat.Gid))
}
func (proxy *Proxy) listen(protoAndAddr string) (net.Listener, string, error) {
@@ -510,7 +505,10 @@ func (proxy *Proxy) attach(containerID string) error {
if container.HostConfig != nil {
extraHosts = container.HostConfig.ExtraHosts
}
proxy.RewriteEtcHosts(container.HostsPath, fqdn, ips, extraHosts)
if err := proxy.RewriteEtcHosts(container.HostsPath, fqdn, ips, extraHosts); err != nil {
Log.Errorf("Error from rewriting container's /etc/hosts: %s", err)
// carry on since the original /etc/hosts is good enough in most cases
}
}
pid := container.State.Pid
@@ -684,6 +682,7 @@ func (proxy *Proxy) runTransientContainer(entrypoint, cmd, binds []string) (err
env = append(env, fmt.Sprintf("%s=%s", "WEAVE_DEBUG", val))
}
Log.Debugf("Running image %q; entrypoint=%q; cmd=%q; binds=%q", proxy.Image, entrypoint, fmt.Sprintf("%.72s", cmd), binds)
var container *docker.Container
container, err = proxy.client.CreateContainer(docker.CreateContainerOptions{
Config: &docker.Config{
+19
View File
@@ -5,7 +5,10 @@ import (
"net/http"
"github.com/gorilla/mux"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/net"
"github.com/weaveworks/weave/net/address"
)
func (router *NetworkRouter) HandleHTTP(muxRouter *mux.Router) {
@@ -26,4 +29,20 @@ func (router *NetworkRouter) HandleHTTP(muxRouter *mux.Router) {
router.ForgetConnections(r.Form["peer"])
})
muxRouter.Methods("POST").Path("/expose/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
cidr, err := address.ParseCIDR(vars["ip"] + "/" + vars["prefixlen"])
if err != nil {
http.Error(w, fmt.Sprint("unable to parse ip addr: ", err.Error()), http.StatusBadRequest)
return
}
if err = net.Expose(router.BridgeConfig.WeaveBridgeName, cidr.IPNet(), router.BridgeConfig.AWSVPC, router.BridgeConfig.NPC); err != nil {
http.Error(w, fmt.Sprint("unable to expose: ", err.Error()), http.StatusInternalServerError)
return
}
w.WriteHeader(204)
})
}
+4 -2
View File
@@ -11,6 +11,7 @@ import (
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/db"
weavenet "github.com/weaveworks/weave/net"
)
const (
@@ -47,11 +48,12 @@ type PacketLogging interface {
type NetworkRouter struct {
*mesh.Router
NetworkConfig
weavenet.BridgeConfig
Macs *MacCache
db db.DB
}
func NewNetworkRouter(config mesh.Config, networkConfig NetworkConfig, name mesh.PeerName, nickName string, overlay NetworkOverlay, db db.DB) (*NetworkRouter, error) {
func NewNetworkRouter(config mesh.Config, networkConfig NetworkConfig, bridgeConfig weavenet.BridgeConfig, name mesh.PeerName, nickName string, overlay NetworkOverlay, db db.DB) (*NetworkRouter, error) {
if overlay == nil {
overlay = NullNetworkOverlay{}
}
@@ -63,7 +65,7 @@ func NewNetworkRouter(config mesh.Config, networkConfig NetworkConfig, name mesh
if err != nil {
return nil, err
}
router := &NetworkRouter{Router: meshRouter, NetworkConfig: networkConfig, db: db}
router := &NetworkRouter{Router: meshRouter, NetworkConfig: networkConfig, BridgeConfig: bridgeConfig, db: db}
router.Peers.OnInvalidateShortIDs(overlay.InvalidateShortIDs)
router.Routes.OnChange(overlay.InvalidateRoutes)
router.Macs = NewMacCache(macMaxAge,
+1 -1
View File
@@ -493,7 +493,7 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder, broadcast bo
// non-broadcast frames can be broadcast, if the
// destination MAC was not in our MAC cache.
if broadcast {
log.Print(fwd.logPrefix(), "dropping too big DF broadcast frame (", dec.IP.SrcIP, " -> ", dec.IP.DstIP, "): MTU=", mtu)
log.Print(fwd.logPrefix(), "dropping too big DF broadcast frame: len ", len(frame), " (", dec.IP.SrcIP, " -> ", dec.IP.DstIP, "): MTU=", mtu)
return
}
+1 -1
View File
@@ -32,7 +32,7 @@ You can of course also manually change the IP of any given container or subnet o
### IP Address Notation in Weave
In the IP address `10.4.2.6/8`, the network prefix is the first 8 bits
- `10`. Written out in full, that network is `10.0.0.0/8`.
\- `10`. Written out in full, that network is `10.0.0.0/8`.
The most common prefix lengths are 8, 16 and 24, but there is nothing
stopping you using a /9 network or a /26. For example, `6.250.3.1/9` is on the
+1 -1
View File
@@ -4,7 +4,7 @@ menu_order: 30
search_type: Documentation
---
Install Weave Net from the command line on its own or if you are using Docker, Kubernetes or Mesosphere as a Docker or a CNI plugin. Weave Net can also be launched it as a cloud agent within Weave Cloud to visualize and troubleshoot peer connections.
Install Weave Net from the command line on its own or if you are using Docker, Kubernetes or Mesosphere as a Docker or a CNI plugin. Weave Net can also be launched as a cloud agent within Weave Cloud to visualize and troubleshoot peer connections.
## Installing Weave Net Stand-alone and in Weave Cloud
+2 -2
View File
@@ -8,7 +8,7 @@ search_type: Documentation
The Weave Net legacy plugin actually provides *two* network drivers to Docker -
one named `weavemesh` that can operate without a cluster store and another one
named `weave` that can only work with one (like Docker's overlay driver), while
the V2 plugin provides *one* - `store/weaveworks/net-plugin:latest_release`
the V2 plugin provides *one* - `weaveworks/net-plugin:latest_release`
operating only in swarm mode.
### weavemesh driver
@@ -41,7 +41,7 @@ The plugin accepts the following options via `docker network create ... --opt`:
network created when the plugin is first launched has the multicast
option turned on, but for any networks you create it defaults to off.
### store/weaveworks/net-plugin:latest_release driver
### weaveworks/net-plugin:latest_release driver
* The driver runs within the plugin V2.
* Requires Docker to run in [swarm mode](https://docs.docker.com/engine/swarm/swarm-mode/).
+8 -7
View File
@@ -9,7 +9,7 @@ search_type: Documentation
* [Running Services or Containers Using the Plugin](#usage)
Docker Engine version 1.12 introduced [a new plugin system (V2)](https://docs.docker.com/engine/extend/).
This document describes how to use [the Network Plugin V2 of Weave Net](https://store.docker.com/plugins/weave-net-plugin).
This document describes how to use the Network Plugin V2 of Weave Net.
Before using the plugin, please keep in mind the plugin works only in Swarm
mode and requires Docker version 1.13 or later.
@@ -19,7 +19,7 @@ mode and requires Docker version 1.13 or later.
To install the plugin run the following command on _each_ host already
participating in a Swarm cluster, i.e. on all master and worker nodes:
$ docker plugin install store/weaveworks/net-plugin:latest_release
$ docker plugin install weaveworks/net-plugin:latest_release
Docker will pull the plugin from Docker Store, and it will ask to grant
privileges before installing the plugin. Afterwards, it will start `weaver`
@@ -29,7 +29,7 @@ process which will try to connect to Swarm masters running Weave Net.
There are several configuration parameters which can be set with:
$ docker plugin set store/weaveworks/net-plugin:latest_release PARAM=VALUE
$ docker plugin set weaveworks/net-plugin:latest_release PARAM=VALUE
The parameters include:
@@ -49,22 +49,22 @@ The parameters include:
Before setting any parameter, the plugin has to be disabled with:
$ docker plugin disable store/weaveworks/net-plugin:latest_release
$ docker plugin disable weaveworks/net-plugin:latest_release
To re-enable the plugin run the following command:
$ docker plugin enable store/weaveworks/net-plugin:latest_release
$ docker plugin enable weaveworks/net-plugin:latest_release
### <a name="usage"></a>Running Services or Containers Using the Plugin
After you have launched the plugin, you can create a network for Docker Swarm
services by running the following command on any Docker Swarm master node:
$ docker network create --driver=store/weaveworks/net-plugin:latest_release mynetwork
$ docker network create --driver=weaveworks/net-plugin:latest_release mynetwork
Or you can create a network for any Docker container with:
$ docker network create --driver=store/weaveworks/net-plugin:latest_release --attachable mynetwork
$ docker network create --driver=weaveworks/net-plugin:latest_release --attachable mynetwork
To start a service attached to the network run, for example:
@@ -78,3 +78,4 @@ Or to start a container:
* [Integrating Docker via the Network Plugin (Legacy)](/site/install/plugin/plugin.md)
* [How the Weave Network Plugins Work](/site/install/plugin/plugin-how-it-works.md)
* [Troubleshooting the V2 plugin](/site/troubleshooting.md#v2plugin)
+89 -73
View File
@@ -12,8 +12,10 @@ The following topics are discussed:
* [CPU and Memory Requirements](#resources)
* [Pod Eviction](#eviction)
* [Network Policy Controller](#npc)
* [Troubleshooting](#troubleshooting)
* [Troubleshooting Blocked Connections](#blocked-connections)
* [Changing Configuration Options](#configuration-options)
* [Things to watch out for](#key-points)
* [Changing Configuration Options](#configuration-options)
## <a name="install"></a> Installation
@@ -83,8 +85,8 @@ and so you will need to perform the procedure manually:
* Apply the updated addon manifest `kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"`
* Kill each Weave Net pod with `kubectl delete` and then wait for it to reboot before moving on to the next pod.
**Note:** If you delete all Weave Net pods at the same time they will
lose track of IP address range ownership, possibly leading to
**Note:** In versions prior to Weave Net 2.0, deleting all Weave Net pods at the same time
will result in them losing track of IP address range ownership, possibly leading to
duplicate IP addresses if you then start a new copy of Weave Net.
## <a name="resources"></a>CPU and Memory Requirements
@@ -163,6 +165,80 @@ definition](http://kubernetes.io/docs/api-reference/extensions/v1beta1/definitio
all multicast traffic) by adding `--allow-mcast=false` as an
argument to `weave-npc` in the YAML configuration.
## <a name="troubleshooting"></a> Troubleshooting
The status of Weave Net can be checked by running its CLI commands. This can be done in various ways:
1\. [Install the `weave` script](/site/installing-weave.md) and run:
```
$ weave status
Version: 2.0.1 (up to date; next check at 2017/07/10 13:49:29)
Service: router
Protocol: weave 1..2
Name: 42:8e:e8:c4:52:1b(host-0)
Encryption: disabled
PeerDiscovery: enabled
Targets: 3
Connections: 3 (2 established, 1 failed)
Peers: 3 (with 6 established connections)
TrustedSubnets: none
Service: ipam
Status: ready
Range: 10.32.0.0/12
DefaultSubnet: 10.32.0.0/12
```
2\. If you don't want to install additional software onto your hosts, run via `kubectl` commands, which produce the exact same outcome as the previous example:
```
### Identify the Weave Net pods:
$ kubectl get pods -n kube-system -l name=weave-net -o wide
NAME READY STATUS RESTARTS AGE IP NODE
weave-net-1jkl6 2/2 Running 0 1d 10.128.0.4 host-0
weave-net-bskbv 2/2 Running 0 1d 10.128.0.5 host-1
weave-net-m4x1b 2/2 Running 0 1d 10.128.0.6 host-2
```
The above shows all Weave Net pods available in your cluster.
You can see Kubernetes has deployed one Weave Net pod per host, in order to interconnect all hosts.
You then need to:
- choose which pod you want to run your command from (in most cases it doesn't matter
which one you pick so just pick the first one, e.g. pod `weave-net-1jkl6` here)
- use `kubectl exec` to run the `weave status` command
- specify the absolute path `/home/weave/weave` and add `--local` because it's running inside a container
```
$ kubectl exec -n kube-system weave-net-1jkl6 -c weave -- /home/weave/weave --local status
Version: 2.0.1 (up to date; next check at 2017/07/10 13:49:29)
Service: router
Protocol: weave 1..2
Name: 42:8e:e8:c4:52:1b(host-0)
Encryption: disabled
PeerDiscovery: enabled
Targets: 3
Connections: 3 (2 established, 1 failed)
Peers: 3 (with 6 established connections)
TrustedSubnets: none
Service: ipam
Status: ready
Range: 10.32.0.0/12
DefaultSubnet: 10.32.0.0/12
```
3\. Finally you could also use [Weave Cloud](https://cloud.weave.works/) and monitor all your pods, including Weave Net's ones, from there.
![Weave Net status screen in Weave Cloud](weave-cloud-net-status.png)
For more information see [What is Weave Cloud?](https://www.weave.works/docs/cloud/latest/overview/)
### <a name="blocked-connections"></a> Troubleshooting Blocked Connections
If you suspect that legitimate traffic is being blocked by the Weave Network Policy Controller, the first thing to do is check the `weave-npc` container's logs.
@@ -195,7 +271,16 @@ TCP connection from 10.32.0.7:56648 to 10.32.0.11:80 blocked by Weave NPC.
UDP connection from 10.32.0.7:56648 to 10.32.0.11:80 blocked by Weave NPC.
```
### <a name="configuration-options"></a> Changing Configuration Options
### <a name="key-points"></a> Things to watch out for
- Don't turn on `--masquerade-all` on kube-proxy: this will change the
source address of every pod-to-pod conversation which will make it
impossible to correctly enforce network policies that restrict which
pods can talk.
- If you do set the `--cluster-cidr` option on kube-proxy, make sure
it matches the `IPALLOC_RANGE` given to Weave Net (see below)
## <a name="configuration-options"></a> Changing Configuration Options
#### Using `cloud.weave.works`
@@ -277,72 +362,3 @@ For example,
- name: IPALLOC_RANGE
value: 10.0.0.0/16
```
## <a name="operations"></a> Operations
When using the Kubernetes addon, Weave Net's peers are managed via Kubernetes and very little effort should be required from your end to operate your network.
However, should you need it, you can still run `weave` CLI commands to, for example, confirm Weave Net' status. This can be done in various ways:
1. You could [install the `weave` script](/site/installing-weave.md) and run the same commands you would run without Kubernetes:
```
$ weave status
Version: 2.0.1 (up to date; next check at 2017/07/10 13:49:29)
Service: router
Protocol: weave 1..2
Name: 42:8e:e8:c4:52:1b(host-0)
Encryption: disabled
PeerDiscovery: enabled
Targets: 3
Connections: 3 (2 established, 1 failed)
Peers: 3 (with 6 established connections)
TrustedSubnets: none
Service: ipam
Status: ready
Range: 10.32.0.0/12
DefaultSubnet: 10.32.0.0/12
```
2. If you do not want to install anything else on your hosts, you can use the following `kubectl` commands, which will produce the exact same outcome as the previous example:
```
### Identify the Weave Net pods:
$ kubectl get pods --all-namespaces -o wide | grep weave
kube-system weave-net-1jkl6 2/2 Running 0 1d 10.128.0.4 host-0
kube-system weave-net-bskbv 2/2 Running 0 1d 10.128.0.5 host-1
kube-system weave-net-m4x1b 2/2 Running 0 1d 10.128.0.6 host-2
### The above shows all Weave Net pods available in your cluster.
### You can see Kubernetes has deployed one Weave Net pod per host, in order to interconnect all hosts.
### You then need to:
### - choose which pod you want to run your command from (e.g.: here, pod weave-net-1jkl6, running on host-0), and
### - make sure you pass --local to weave, in addition to your command:
$ kubectl exec -n kube-system weave-net-1jkl6 -- /home/weave/weave --local status
Defaulting container name to weave.
Use 'kubectl describe pod/weave-net-1jkl6' to see all of the containers in this pod.
Version: 2.0.1 (up to date; next check at 2017/07/10 13:49:29)
Service: router
Protocol: weave 1..2
Name: 42:8e:e8:c4:52:1b(host-0)
Encryption: disabled
PeerDiscovery: enabled
Targets: 3
Connections: 3 (2 established, 1 failed)
Peers: 3 (with 6 established connections)
TrustedSubnets: none
Service: ipam
Status: ready
Range: 10.32.0.0/12
DefaultSubnet: 10.32.0.0/12
```
3. Finally you could also use [Weave Cloud](https://cloud.weave.works/) and monitor all your pods, including Weave Net's ones, from there.
Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

+1 -1
View File
@@ -39,7 +39,7 @@ 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
>**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
+1 -1
View File
@@ -99,7 +99,7 @@ To minimize downtime while the new script is pulling the new container images:
* 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
>**Note:** Always check the Release Notes for specific versions in case
there are any special caveats or deviations from the standard
procedure.
@@ -42,7 +42,7 @@ Then on _any_ peer run the following to force consensus:
weave prime
>>**Note:** You can run this safely on more than one or
>**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.
+2
View File
@@ -20,6 +20,8 @@ exposed:
* `weave_max_ips` - Size of IP address space used by allocator.
* `weave_dns_entries` - Number of DNS entries.
* `weave_flows` - Number of FastDP flows.
* `weave_ipam_unreachable_count` - Number of unreachable peers that own IPAM addresses.
* `weave_ipam_unreachable_percentage` - Percentage of all IP addresses owned by unreachable peers.
* `weave_ipam_pending_allocates` - Number of pending allocates.
* `weave_ipam_pending_claims` - Number of pending claims.
+27 -5
View File
@@ -14,6 +14,7 @@ search_type: Documentation
- [List attached containers](#list-attached-containers)
* [Stopping Weave](#stop)
* [Reboots](#reboots)
* [Troubleshooting the V2 plugin](#v2plugin)
* [Snapshot Releases](#snapshots)
## <a name="diagnostics"></a>Basic Diagnostics
@@ -284,11 +285,32 @@ on reboot. This can be disabled via:
weave launch --no-restart
Note that the Weave Net router will create the `weave` network bridge
if necessary when it restarts, and the [Weave Net Docker API
Proxy](/site/tasks/weave-docker-api/weave-docker-api.md) will re-attach any application
containers that it originally attached to the Weave network when they
restart.
Note that the Weave Net router creates the `weave` network bridge if
necessary when it restarts. The [Weave Net Docker API
Proxy](/site/tasks/weave-docker-api/weave-docker-api.md) then
re-attaches any application containers that it originally attached to
the Weave network when they restart.
## <a name="v2plugin"></a>Troubleshooting the V2 plugin
If Weave Net is installed via `docker plugin install`, download the
`weave` script to run `weave status`, `weave ps` or `weave report` as
above. Install Weave Net by following the [install
instructions](/site/install/installing-weave.md).
Docker "v2" plugins do run as containers, but at a lower level within
the Docker environment. Because of this, you cannot view them with
`docker ps`, `docker inspect`, etc.
Do not run `weave launch`, `weave stop` or similar commands when using
this plugin; use the `docker plugin` commands instead. You can run
`weave reset`, but only after disabling the plugin via `docker plugin disable`.
Diagnostic logs from the plugin go to the same place as the Docker
daemon, which will depend on your Linux install. For example, if it
uses `systemd`, then do this to view the Docker and plugin logs:
sudo journalctl -u docker
## <a name="snapshots"></a>Snapshot Releases
@@ -7,7 +7,9 @@ C2=10.2.1.37
C3=10.2.2.34
C4=10.2.2.37
EXP=10.2.2.101
EXP_CIDR=10.2.2.0/24
UNIVERSE=10.2.3.0/24
PORT=5555
weave_on1() {
assert_raises "weave_on $HOST1 $@"
@@ -17,6 +19,10 @@ run_on1() {
assert_raises "run_on $HOST1 $@"
}
run_on2() {
assert_raises "run_on $HOST2 $@"
}
exec_on1() {
assert_raises "exec_on $HOST1 $@"
}
@@ -33,13 +39,6 @@ check_container_connectivity() {
start_suite "exposing weave network to host"
# expose/hide with a CIDR work prior to launching weave
run_on1 "! $PING $EXP"
weave_on1 "expose $EXP/24"
run_on1 " $PING $EXP"
weave_on1 "hide $EXP/24"
run_on1 "! $PING $EXP"
weave_on $HOST1 launch --ipalloc-range $UNIVERSE
start_container $HOST1 $C1/24 --name=c1
@@ -78,4 +77,14 @@ weave_on1 "hide"
weave_on1 "hide"
run_on1 "! $PING $C5"
# remote host connectivity after 'expose'
docker_on $HOST1 exec -d c3 nc -l -p $PORT
weave_on1 "expose $EXP/24"
# Make c3 reachable from host2 w/o installing a route on host2 which does
# not work on GCP.
run_on1 "sudo iptables -t nat -A PREROUTING -p tcp --dport $PORT -j DNAT --to-destination $C3:$PORT"
run_on2 "sh -c \"echo hello | nc -w1 $HOST1 $PORT\""
weave_on1 "hide"
run_on1 "sudo iptables -t nat -D PREROUTING -p tcp --dport $PORT -j DNAT --to-destination $C3:$PORT"
end_suite
+78 -2
View File
@@ -19,6 +19,7 @@ SUCCESS="$(( $NUM_HOSTS * ($NUM_HOSTS-1) )) established"
KUBECTL="sudo kubectl --kubeconfig /etc/kubernetes/admin.conf"
KUBE_PORT=6443
IMAGE=weaveworks/network-tester:latest
DOMAIN=nettest.default.svc.cluster.local.
tear_down_kubeadm
@@ -47,7 +48,8 @@ fi
# Ensure Kubernetes uses locally built container images and inject code coverage environment variable (or do nothing depending on $COVERAGE):
sed -e "s%imagePullPolicy: Always%imagePullPolicy: Never%" \
-e "s%env:%$COVERAGE_ARGS%" \
"$(dirname "$0")/../prog/weave-kube/weave-daemonset-k8s-1.6.yaml" | run_on "$HOST1" "$KUBECTL apply -n kube-system -f -"
-e "s%#npc-args% args:\n - '--use-legacy-netpol'%" \
"$(dirname "$0")/../prog/weave-kube/weave-daemonset-k8s-1.7.yaml" | run_on "$HOST1" "$KUBECTL apply -n kube-system -f -"
sleep 5
@@ -94,6 +96,20 @@ spec:
run: nettest
EOF
# Another policy, this time with no 'from' section, just to check that doesn't cause a crash
run_on $HOST1 "$KUBECTL apply -f -" <<EOF
apiVersion: extensions/v1beta1
kind: NetworkPolicy
metadata:
name: test840f
spec:
ingress:
- {}
podSelector:
matchLabels:
run: norealpods
EOF
check_ready() {
run_on $HOST1 "$KUBECTL get nodes | grep -c -w Ready | grep $NUM_HOSTS"
}
@@ -101,7 +117,7 @@ check_ready() {
assert_raises 'wait_for_x check_ready "hosts to be ready"'
# See if we can get some pods running that connect to the network
run_on $HOST1 "$KUBECTL run --image-pull-policy=Never nettest --image=$IMAGE --replicas=3 -- -peers=3 -dns-name=nettest.default.svc.cluster.local."
run_on $HOST1 "$KUBECTL run --image-pull-policy=Never nettest --image=$IMAGE --replicas=3 -- -peers=3 -dns-name=$DOMAIN"
# Create a headless service so they can be found in Kubernetes DNS
run_on $HOST1 "$KUBECTL create -f -" <<EOF
apiVersion: v1
@@ -135,6 +151,66 @@ assert_raises 'wait_for_x check_all_pods_communicate pods'
# Check that a pod can contact the outside world
assert_raises "$SSH $HOST1 $KUBECTL exec $podName -- $PING 8.8.8.8"
# Check that our pods haven't crashed
assert "$SSH $HOST1 $KUBECTL get pods -n kube-system -l name=weave-net | grep -c Running" 3
# Start pod which should not have access to nettest
run_on $HOST1 "$KUBECTL run nettest-deny --labels=\"access=deny,run=nettest-deny\" --image-pull-policy=Never --image=$IMAGE --replicas=1 --command -- sleep 3600"
denyPodName=$($SSH $HOST1 "$KUBECTL get pods -l run=nettest-deny -o go-template='{{(index .items 0).metadata.name}}'")
assert_raises "! $SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$DOMAIN:8080/status >/dev/null"
# Restart weave-net with npc in non-legacy mode
$SSH $HOST1 "$KUBECTL delete ds weave-net -n=kube-system"
sed -e "s%imagePullPolicy: Always%imagePullPolicy: Never%" \
-e "s%env:%$COVERAGE_ARGS%" \
"$(dirname "$0")/../prog/weave-kube/weave-daemonset-k8s-1.7.yaml" | run_on "$HOST1" "$KUBECTL apply -n kube-system -f -"
assert_raises 'wait_for_x check_all_pods_communicate pods'
# nettest-deny should still not be able to reach nettest pods
assert_raises "! $SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$DOMAIN:8080/status >/dev/null"
# allow access for nettest-deny
run_on $HOST1 "$KUBECTL apply -f -" <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-nettest-deny
namespace: default
spec:
podSelector:
matchLabels:
run: nettest
ingress:
- from:
- podSelector:
matchLabels:
access: deny
EOF
assert_raises "$SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$DOMAIN:8080/status >/dev/null"
# remove the access for nettest-deny
run_on $HOST1 "$KUBECTL delete netpol allow-nettest-deny"
# nettest-deny should still not be able to reach nettest pods
assert_raises "! $SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$DOMAIN:8080/status >/dev/null"
# allow access for all
run_on $HOST1 "$KUBECTL apply -f -" <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-nettest-deny
namespace: default
spec:
podSelector: {}
ingress:
- {}
EOF
assert_raises "$SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$DOMAIN:8080/status >/dev/null"
tear_down_kubeadm
# Destroy our test ipset, and implicitly check it is still there
@@ -0,0 +1,117 @@
#! /bin/bash
. "$(dirname "$0")/config.sh"
function howmany { echo $#; }
#
# Test vars
#
TOKEN=112233.4455667788990000
HOST1IP=$($SSH $HOST1 "getent hosts $HOST1 | cut -f 1 -d ' '")
NUM_HOSTS=$(howmany $HOSTS)
SUCCESS="$(( $NUM_HOSTS * ($NUM_HOSTS-1) )) established"
KUBECTL="sudo kubectl --kubeconfig /etc/kubernetes/admin.conf"
KUBE_PORT=6443
IMAGE=weaveworks/network-tester:latest
if [ -n "$COVERAGE" ]; then
COVERAGE_ARGS="env:\\n - name: EXTRA_ARGS\\n value: \"-test.coverprofile=/home/weave/cover.prof --\""
else
COVERAGE_ARGS="env:"
fi
#
# Utility functions
#
function teardown_kubernetes_cluster {
greyly echo "Tearing down kubernetes cluster"
for host in $HOSTS; do
run_on $host "sudo kubeadm reset && sudo rm -r -f /opt/cni/bin/*weave*"
done
}
function setup_kubernetes_cluster {
teardown_kubernetes_cluster;
greyly echo "Setting up kubernetes cluster"
# kubeadm init upgrades to latest Kubernetes version by default, therefore we try to lock the version using the below option:
k8s_version="$(run_on $HOST1 "kubelet --version" | grep -oP "(?<=Kubernetes )v[\d\.\-beta]+")"
k8s_version_option="$([[ "$k8s_version" > "v1.6" ]] && echo "kubernetes-version" || echo "use-kubernetes-version")"
for host in $HOSTS; do
if [ $host = $HOST1 ] ; then
run_on $host "sudo systemctl start kubelet && sudo kubeadm init --$k8s_version_option=$k8s_version --token=$TOKEN"
else
run_on $host "sudo systemctl start kubelet && sudo kubeadm join --token=$TOKEN $HOST1IP:$KUBE_PORT"
fi
done
# Ensure Kubernetes uses locally built container images and inject code coverage environment variable (or do nothing depending on $COVERAGE):
sed -e "s%imagePullPolicy: Always%imagePullPolicy: Never%" \
-e "s%env:%$COVERAGE_ARGS%" \
"$(dirname "$0")/../prog/weave-kube/weave-daemonset-k8s-1.7.yaml" | run_on "$HOST1" "$KUBECTL apply -n kube-system -f -"
}
function force_drop_node {
greyly echo "Dropping node $1 with 'sudo kubectl delete node'"
local target=$(echo "$1" | awk -F"." '{print $1}')
run_on $HOST1 "$KUBECTL delete node $target"
}
function weave_connected {
run_on $HOST1 "curl -sS http://127.0.0.1:6784/status | grep \"$SUCCESS\""
}
function unreachable_ip_addresses_count {
# Note Well: This will return 0 if weave is not running at all.
local host=$1
local count=$(get_command_output_on $host "sudo weave status ipam | grep unreachable | wc -l")
echo $count
}
function relaunch_weave_pod {
local target=$(echo "$1" | awk -F"." '{print $1}')
# This is a pretty complex jq query. Is there a simpler way to
# get the weave-net pod name back using kubectl templates?
local QUERY="$KUBECTL get pods -n kube-system -o json | jq -r \".items | map(select(.spec.nodeName == \\\"$target\\\")) | map(select(.metadata.labels.name == \\\"weave-net\\\")) | map(.metadata.name) | .[]\""
local podname=$(get_command_output_on $HOST1 $QUERY) # Run on master node
run_on $HOST1 "$KUBECTL get pods -n kube-system \"$podname\" -o yaml | $KUBECTL replace --force -f -"
}
#
# Suite
#
function main {
local IPAM_RECOVER_DELAY=15
start_suite "Test weave-net deallocates from IPAM on node failure";
setup_kubernetes_cluster;
# Need to wait until all pods have come up
assert_raises "wait_for_x weave_connected 'pods to be running weave net'"
greyly echo "Checking unreachable IPs"
assert "unreachable_ip_addresses_count $HOST1" "0";
assert "unreachable_ip_addresses_count $HOST2" "0";
assert "unreachable_ip_addresses_count $HOST3" "0";
force_drop_node $HOST2;
relaunch_weave_pod $HOST3;
sleep $IPAM_RECOVER_DELAY;
greyly echo "Checking unreachable IPs"
assert "unreachable_ip_addresses_count $HOST1" "0";
assert "unreachable_ip_addresses_count $HOST3" "0";
teardown_kubernetes_cluster;
end_suite;
}
main
+1
View File
@@ -11,6 +11,7 @@ The `Vagrantfile` in this directory constructs three such VMs.
If you are [building Weave Net using Vagrant](https://www.weave.works/docs/net/latest/building/),
it is recommended to run the tests from the build VM and not the host.
> **NOTE WELL** The number of hosts required by a test must be specified in the test name. For example, `000_some_test.sh` will use one host, while `001_some_other_3_test.sh` will use three hosts. The number of hosts must be the last part of the test name before the `_test.sh` prefix. See `/tools/runner/runner.go` for details
## Running tests
+1 -1
View File
@@ -38,7 +38,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
host.vm.provision :shell, :inline => <<END
# Tests rely on hostnames, so we remove potentially incorrect entries (e.g. localhost), and add mappings of IP addresses to hostnames:
sed -i '/#{hostname}/d' /etc/hosts
sed -i.bak '/#{hostname}/d' /etc/hosts
echo "#{ETC_HOSTS_ENTRIES}" >> /etc/hosts
# Fix the resolution errors by using 8.8.8.8
+7
View File
@@ -104,6 +104,13 @@ run_on() {
remote $host $SSH $host "$@"
}
get_command_output_on() {
host=$1
shift 1
[ -z "$DEBUG" ] || greyly echo "Running on $host: $@" >&2
$SSH $host "$@"
}
docker_on() {
host=$1
shift 1
Binary file not shown.
+9 -2
View File
@@ -175,7 +175,7 @@ function use_or_create_image() {
function update_local_etc_hosts() {
echo "> Updating local /etc/hosts..."
# Remove old entries (if present):
for host in $1; do sudo sed -i "/$host/d" /etc/hosts; done
for host in $1; do sudo sed -i.bak "/$host/d" /etc/hosts; done
# Add new entries:
sudo sh -c "echo \"$2\" >> /etc/hosts"
}
@@ -183,7 +183,7 @@ function update_local_etc_hosts() {
function upload_etc_hosts() {
# Remove old entries (if present):
# shellcheck disable=SC2016,SC2086
$SSH $3 'for host in '$1'; do sudo sed -i "/$host/d" /etc/hosts; done'
$SSH $3 'for host in '$1'; do sudo sed -i.bak "/$host/d" /etc/hosts; done'
# Add new entries:
echo "$2" | $SSH "$3" "sudo -- sh -c \"cat >> /etc/hosts\""
}
@@ -354,6 +354,13 @@ function main() {
exit $status
;;
up) # Setup a test environment without actually doing any testing.
provision on "$PROVIDER"
configure "$ssh_user" "$ssh_hosts" "${ssh_port:-22}" "$ssh_id_file"
"$DIR/setup.sh"
echo_export_hosts
;;
provision)
provision on "$PROVIDER"
echo_export_hosts
+1 -1
View File
@@ -23,4 +23,4 @@ if [ -n "$CIRCLECI" -o -n "$PARALLEL" ]; then
RUNNER_ARGS="$RUNNER_ARGS -parallel"
fi
HOSTS="$HOSTS" "${DIR}/../tools/runner/runner" -timeout=240 $RUNNER_ARGS $TESTS
HOSTS="$HOSTS" "${DIR}/../tools/runner/runner" -timeout=300 $RUNNER_ARGS $TESTS
Generated Vendored Submodule
+1
Generated Vendored Submodule
+1
Generated Vendored Submodule
+1
Generated Vendored Submodule
+1
Generated Vendored Submodule
+1
Generated Vendored Submodule
+1
Submodule vendor/golang.org/x/text added at 825fc78a2f.
Generated Vendored Submodule
+1
Submodule vendor/gopkg.in/yaml.v2 added at eb3733d160.
Generated Vendored Submodule
+1
Submodule vendor/k8s.io/api added at 81aa34336d.
Generated Vendored Submodule
+1
Generated Vendored Submodule
+1
+157 -185
View File
@@ -8,19 +8,16 @@ IMAGE_VERSION=latest
[ "$SCRIPT_VERSION" = "unreleased" ] || IMAGE_VERSION=$SCRIPT_VERSION
IMAGE_VERSION=${WEAVE_VERSION:-$IMAGE_VERSION}
# - The weavexec image embeds a Docker 1.10.3 client. Docker will give
# a "client is newer than server error" if the daemon has an older
# API version, which could be confusing if the user's Docker client
# is correctly matched with that older version.
#
# We therefore check that the user's Docker *client* is >= 1.10.3
# Minimum tested version of Docker + Weave Net
MIN_DOCKER_VERSION=1.10.0
# These are needed for remote execs, hence we introduce them here
DOCKERHUB_USER=${DOCKERHUB_USER:-weaveworks}
BASE_EXEC_IMAGE=$DOCKERHUB_USER/weaveexec
EXEC_IMAGE=$BASE_EXEC_IMAGE:$IMAGE_VERSION
WEAVEDB_IMAGE=$DOCKERHUB_USER/weavedb
WEAVEDB_IMAGE=$DOCKERHUB_USER/weavedb:latest
BASE_IMAGE=$DOCKERHUB_USER/weave
IMAGE=$BASE_IMAGE:$IMAGE_VERSION
PROXY_HOST=${PROXY_HOST:-$(echo "${DOCKER_HOST#tcp://}" | cut -s -d: -f1)}
PROXY_HOST=${PROXY_HOST:-127.0.0.1}
DOCKER_CLIENT_HOST=${DOCKER_CLIENT_HOST:-$DOCKER_HOST}
@@ -105,13 +102,6 @@ usage() {
exit 1
}
handle_help_arg() {
if [ "$1" = "--help" ] ; then
usage_no_exit
exit 0
fi
}
docker_sock_options() {
# Pass through DOCKER_HOST if it is a Unix socket;
# a TCP socket may be secured by TLS, in which case we can't use it
@@ -123,7 +113,7 @@ docker_sock_options() {
}
docker_run_options() {
echo --privileged --net=host $(docker_sock_options)
echo --privileged --net host $(docker_sock_options)
}
exec_options() {
@@ -142,7 +132,7 @@ exec_options() {
exec_remote() {
docker $DOCKER_CLIENT_ARGS run --rm \
$(docker_run_options) \
--pid=host \
--pid host \
$(exec_options "$@") \
-e DOCKERHUB_USER="$DOCKERHUB_USER" \
-e WEAVE_VERSION \
@@ -198,52 +188,59 @@ check_docker_version() {
fi
}
is_cidr() {
echo "$1" | grep -E "^$CIDR_REGEXP$" >/dev/null
}
collect_cidr_args() {
CIDR_ARGS=""
CIDR_ARG_COUNT=0
while [ "$1" = "net:default" ] || is_cidr "$1" || is_cidr "${1#ip:}" || is_cidr "${1#net:}" ; do
CIDR_ARGS="$CIDR_ARGS ${1#ip:}"
CIDR_ARG_COUNT=$((CIDR_ARG_COUNT + 1))
shift 1
done
}
kill_container() {
docker kill $1 >/dev/null 2>&1 || true
}
######################################################################
# main
######################################################################
[ "$1" = "--local" ] && shift 1 && IS_LOCAL=1
# "--help|help" and "<command> --help" are special because we always want to
# process them at the client end.
handle_help_arg "$1"
handle_help_arg "--$1"
handle_help_arg "$2"
if [ "$1" = "version" -a -z "$IS_LOCAL" ] ; then
# non-local "version" is special because we want to show the
# version of the script executed by the user rather than what is
# embedded in weaveexec.
echo "weave script $SCRIPT_VERSION"
elif [ "$1" = "env" -a "$2" = "--restore" ] ; then
# "env --restore" is special because we always want to process it
# at the client end.
if [ "${ORIG_DOCKER_HOST-unset}" = "unset" ] ; then
echo "Nothing to restore. This is most likely because there was no preceding invocation of 'eval \$(weave env)' in this shell." >&2
exit 1
else
echo "DOCKER_HOST=$ORIG_DOCKER_HOST"
# Handle special case $1 commands that run locally at the client end
case "$1" in
help|--help)
usage_no_exit
exit 0
fi
fi
;;
version)
# non-local "version" is special because we want to show the
# version of the script executed by the user rather than what is
# embedded in weaveexec.
[ -z "$IS_LOCAL" ] && echo "weave script $SCRIPT_VERSION"
;;
env)
if [ "$2" = "--restore" ] ; then
# "env --restore" is special because we always want to process it
# at the client end.
if [ "${ORIG_DOCKER_HOST-unset}" = "unset" ] ; then
echo "Nothing to restore. This is most likely because there was no preceding invocation of 'eval \$(weave env)' in this shell." >&2
exit 1
else
echo "DOCKER_HOST=$ORIG_DOCKER_HOST"
exit 0
fi
fi
;;
launch)
# this is necessary because 'util_op run-container' doesn't do
# an implied pull like 'docker run' does
if [ -z "$IS_LOCAL" ] ; then
for img in $IMAGE $WEAVEDB_IMAGE ; do
docker inspect -f "_" $img >/dev/null 2>&1 || docker pull $img
done
fi
;;
setup)
# call 'docker pull' outside weaveexec so we don't need a
# docker binary inside
if [ -z "$IS_LOCAL" ] ; then
for img in $IMAGE $EXEC_IMAGE $WEAVEDB_IMAGE ; do
docker pull $img
done
fi
;;
*)
[ "$2" = "--help" ] && usage_no_exit && exit 0
;;
esac
if [ -z "$IS_LOCAL" ] ; then
check_docker_version
@@ -256,15 +253,14 @@ fi
######################################################################
# Default restart policy for router/proxy
RESTART_POLICY="--restart=always"
BASE_IMAGE=$DOCKERHUB_USER/weave
IMAGE=$BASE_IMAGE:$IMAGE_VERSION
RESTART_POLICY="--restart always"
CONTAINER_NAME=${WEAVE_CONTAINER_NAME:-weave}
PLUGIN_NAME="$DOCKERHUB_USER/net-plugin"
OLD_PLUGIN_CONTAINER_NAME=weaveplugin
CNI_PLUGIN_NAME="weave-plugin-$IMAGE_VERSION"
CNI_PLUGIN_DIR=${WEAVE_CNI_PLUGIN_DIR:-$HOST_ROOT/opt/cni/bin}
VOLUMES_LABEL=weavevolumes
# Note VOLUMES_CONTAINER which is for weavewait should change when you upgrade Weave
VOLUMES_CONTAINER_NAME=weavevolumes-$IMAGE_VERSION
# DB files should remain when you upgrade, so version number not included in name
@@ -367,7 +363,7 @@ util_op() {
if command_exists weaveutil ; then
weaveutil "$@"
else
docker run --rm --pid=host $(docker_run_options) \
docker run --rm --pid host $(docker_run_options) \
--entrypoint=/usr/bin/weaveutil $EXEC_IMAGE "$@"
fi
}
@@ -410,13 +406,9 @@ detect_bridge_type() {
}
try_create_bridge() {
# Running this from 'weave --local' when weaveexec is not on the
# path will run it as a Docker container that does not have access
# to /etc/machine-id, so will not give the full range of persistent IDs
if [ -z "$WEAVEDB_DIR_PATH" ]; then
WEAVEDB_DIR_PATH="$HOST_ROOT/$(docker inspect -f '{{with index .Mounts 0}}{{.Source}}{{end}}' $DB_CONTAINER_NAME 2>/dev/null)"
fi
MAC=$(util_op unique-id "$WEAVEDB_DIR_PATH/weave" "$HOST_ROOT")
# Pass /dev/null because we do not have access to the weavedb files when this runs; BoltDB
# cannot open the file anyway if weaver is running, which is likely for attach|expose|hide.
MAC=$(util_op unique-id "/dev/null" "$HOST_ROOT")
BRIDGE_TYPE=$(util_op create-bridge "$DOCKER_BRIDGE" "$BRIDGE" "$DATAPATH" "$WEAVE_MTU" "$PORT" "$MAC" "$WEAVE_NO_FASTDP" "$WEAVE_NO_BRIDGED_FASTDP" "$PROC_PATH" "$1")
# Set some variables that are expected by code that comes later
[ "$BRIDGE_TYPE" != "fastdp" ] || DATAPATH="$BRIDGE"
@@ -460,10 +452,8 @@ EOF
expose_ip() {
ipam_cidrs allocate_no_check_alive weave:expose $CIDR_ARGS
for CIDR in $ALL_CIDRS ; do
if ! ip addr show dev $BRIDGE | grep -qF $CIDR ; then
ip addr add dev $BRIDGE $CIDR
arp_update $BRIDGE $CIDR || true
fi
call_weave "POST" "/expose/$CIDR"
[ -z "$FQDN" ] || when_weave_running put_dns_fqdn_no_check_alive weave:expose $FQDN $CIDR
done
}
@@ -527,6 +517,11 @@ destroy_bridge() {
run_iptables -t filter -D FORWARD -o $BRIDGE -m state --state NEW -j NFLOG --nflog-group 86 2>/dev/null || true
run_iptables -t filter -D FORWARD -o $BRIDGE -j DROP 2>/dev/null || true
run_iptables -X WEAVE-NPC >/dev/null 2>&1 || true
run_iptables -F WEAVE-EXPOSE >/dev/null 2>&1 || true
run_iptables -t filter -D FORWARD -o $BRIDGE -j WEAVE-EXPOSE 2>/dev/null || true
run_iptables -X WEAVE-EXPOSE >/dev/null 2>&1 || true
run_iptables -t nat -F WEAVE >/dev/null 2>&1 || true
run_iptables -t nat -D POSTROUTING -j WEAVE >/dev/null 2>&1 || true
run_iptables -t nat -D POSTROUTING -o $BRIDGE -j ACCEPT >/dev/null 2>&1 || true
@@ -564,64 +559,56 @@ router_bridge_opts() {
[ -z $WEAVE_NO_FASTDP ] || echo --no-fastdp
}
ask_version() {
if check_running $1 2>/dev/null ; then
DOCKERIMAGE=$(docker inspect --format='{{.Image}}' $1 )
elif ! DOCKERIMAGE=$(docker inspect --format='{{.Id}}' $2 2>/dev/null) ; then
echo "Unable to find $2 image." >&2
fi
[ -n "$DOCKERIMAGE" ] && docker run --rm --net=none -e WEAVE_CIDR=none $3 $DOCKERIMAGE $COVERAGE_ARGS --version
}
attach() {
ATTACH_ARGS=""
[ -n "$NO_MULTICAST_ROUTE" ] && ATTACH_ARGS="--no-multicast-route"
# Relying on AWSVPC being set in 'ipam_cidrs allocate'
[ -n "$AWSVPC" ] && ATTACH_ARGS="--no-multicast-route --keep-tx-on"
util_op attach-container $ATTACH_ARGS $CONTAINER $BRIDGE $MTU "$@"
}
######################################################################
# functions for interacting with containers
######################################################################
# Check that a container for component $1 named $2 with image $3 is not running
# Print an error to stderr and return with an indicative exit status
# if the container $1 does not exist or isn't running.
check_running() {
if ! STATE=$(util_op container-state $1 2> /dev/null) ; then
echo "$1 container is not present. Have you launched it?" >&2
return 1
elif [ "$STATE" = "restarting" ] ; then
echo "$1 container is restarting." >&2
return 2
elif [ "$STATE" != "running" ] ; then
echo "$1 container is not running." >&2
return 2
fi
}
# Execute $@ only if the weave container is running
when_weave_running() {
! check_running $CONTAINER_NAME 2>/dev/null || "$@"
}
# Check that a container $1 with image $2 is not running
check_not_running() {
RUN_STATUS=$(docker inspect --format='{{.State.Running}} {{.State.Status}} {{.Config.Image}}' $2 2>/dev/null) || true
case ${RUN_STATUS%:*} in
"true restarting $3")
echo "$2 is restarting; you can stop it with 'weave stop'." >&2
STATE=$(util_op container-state $1 $2 2> /dev/null) || true
case ${STATE} in
"running")
echo "$1 is already running; you can stop it with 'weave stop'." >&2
return 3
;;
"true "*" $3")
echo "$2 is already running; you can stop it with 'weave stop'." >&2
"created" | "paused" | "restarting")
echo "$1 is created, paused, or restarting; you can stop it with 'weave stop'." >&2
return 1
;;
"false "*" $3")
docker rm $2 >/dev/null
"exited" | "dead")
util_op remove-container $1 >/dev/null
;;
true*)
echo "Found another running container named '$2'. Aborting." >&2
"running image mismatch"*)
echo "Found another running container named '$1'. Aborting." >&2
return 2
;;
false*)
echo "Found another container named '$2'. Aborting." >&2
"image mismatch"*)
echo "Found another container named '$1'. Aborting." >&2
return 2
;;
esac
}
# Given a container name or short ID in $1, ensure the specified
# container exists and then print its full ID to stdout. If
# it doesn't exist, print an error to stderr and
# return with an indicative non-zero exit code.
container_id() {
if ! docker inspect --format='{{.Id}}' $1 2>/dev/null ; then
echo "Error: No such container: $1" >&2
return 1
fi
}
http_call() {
addr="$1"
http_verb="$2"
@@ -668,7 +655,8 @@ wait_for_status() {
while true ; do
"$@" GET /status >/dev/null 2>&1 && return 0
if ! check_running $container >/dev/null 2>&1 ; then
kill_container $container # stop it restarting
# stop it restarting
util_op kill-container $container >/dev/null 2>&1 || true
echo $(death_msg $container) >&2
return 1
fi
@@ -824,35 +812,6 @@ dns_args() {
fi
}
rewrite_etc_hosts() {
HOSTS_PATH_AND_FQDN=$(docker inspect -f '{{.HostsPath}} {{.Config.Hostname}}.{{.Config.Domainname}}' $CONTAINER) || return 1
HOSTS=${HOSTS_PATH_AND_FQDN% *}
FQDN=${HOSTS_PATH_AND_FQDN#* }
# rewrite /etc/hosts, unlinking the file (so Docker does not modify it again) but
# leaving it with valid contents...
util_op rewrite-etc-hosts "$HOSTS" "$FQDN" "$EXEC_IMAGE" "$ALL_CIDRS" "$@"
}
# Print an error to stderr and return with an indicative exit status
# if the container $1 does not exist or isn't running.
check_running() {
if ! STATUS=$(docker inspect --format='{{.State.Running}} {{.State.Restarting}}' $1 2>/dev/null) ; then
echo "$1 container is not present. Have you launched it?" >&2
return 1
elif [ "$STATUS" = "true true" ] ; then
echo "$1 container is restarting." >&2
return 2
elif [ "$STATUS" != "true false" ] ; then
echo "$1 container is not running." >&2
return 2
fi
}
# Execute $@ only if the weave container is running
when_weave_running() {
! check_running $CONTAINER_NAME 2>/dev/null || "$@"
}
# Iff the container in $1 has an FQDN, invoke $2 as a command passing
# the container as the first argument, the FQDN as the second argument
# and $3.. as additional arguments
@@ -861,7 +820,7 @@ with_container_fqdn() {
COMMAND_WCF="$2"
shift 2
CONT_FQDN=$(docker inspect --format='{{.Config.Hostname}}.{{.Config.Domainname}}' $CONT 2>/dev/null) || return 0
CONT_FQDN=$(util_op container-fqdn $CONT 2>/dev/null) || return 0
CONT_NAME=${CONT_FQDN%%.*}
[ "$CONT_NAME" = "$CONT_FQDN" -o "$CONT_NAME." = "$CONT_FQDN" ] || $COMMAND_WCF "$CONT" "$CONT_FQDN" "$@"
}
@@ -931,7 +890,7 @@ collect_dns_add_remove_args() {
check_running $CONTAINER_NAME
if [ -n "$C" ] ; then
check_running $C
CONTAINER=$(container_id $C)
CONTAINER=$(util_op container-id $C)
[ $IP_COUNT -gt 0 ] || IP_ARGS=$(with_container_addresses echo_ips $CONTAINER)
fi
}
@@ -940,6 +899,20 @@ collect_dns_add_remove_args() {
# IP Allocation Management helpers
######################################################################
is_cidr() {
echo "$1" | grep -E "^$CIDR_REGEXP$" >/dev/null
}
collect_cidr_args() {
CIDR_ARGS=""
CIDR_ARG_COUNT=0
while [ "$1" = "net:default" ] || is_cidr "$1" || is_cidr "${1#ip:}" || is_cidr "${1#net:}" ; do
CIDR_ARGS="$CIDR_ARGS ${1#ip:}"
CIDR_ARG_COUNT=$((CIDR_ARG_COUNT + 1))
shift 1
done
}
check_overlap() {
util_op netcheck $1 $BRIDGE
}
@@ -1137,20 +1110,15 @@ proxy_args() {
fi
}
proxy_addrs() {
proxy_addr() {
if addr="$(call_weave GET /proxyaddrs)" ; then
[ -n "$addr" ] || return 1
echo "$addr" | sed "s/0.0.0.0/$PROXY_HOST/g" | tr '\n' ' '
echo "$addr" | sed "s/0.0.0.0/$PROXY_HOST/g" | cut -d $'\n' -f1
else
return 1
fi
}
proxy_addr() {
addr=$(proxy_addrs) || return 1
echo "$addr" | cut -d ' ' -f1
}
warn_if_stopping_proxy_in_env() {
if PROXY_ADDR=$(proxy_addr 2>/dev/null) ; then
[ "$PROXY_ADDR" != "$DOCKER_CLIENT_HOST" ] || echo "WARNING: It appears that your environment is configured to use the Weave Docker API proxy. Stopping it will break this and subsequent docker invocations. To restore your environment, run 'eval \$(weave env --restore)'."
@@ -1230,13 +1198,6 @@ launch() {
fi
fi
# Create a data-only container for persistence data
if ! docker inspect -f ' ' $DB_CONTAINER_NAME > /dev/null 2>&1 ; then
protect_against_docker_hang
docker create -v /weavedb --name=$DB_CONTAINER_NAME \
--label=weavevolumes $WEAVEDB_IMAGE >/dev/null
fi
# Figure out the location of the actual resolv.conf file because
# we want to bind mount its directory into the container.
if [ -L ${HOST_ROOT:-/}/etc/resolv.conf ]; then # symlink
@@ -1252,21 +1213,29 @@ launch() {
docker_client_args $DOCKER_CLIENT_ARGS
proxy_args "$@"
mkdir -p /var/run/weave
# Create a data-only container for persistence data
if ! util_op container-state $DB_CONTAINER_NAME > /dev/null 2>&1 ; then
protect_against_docker_hang
util_op create-volume-container $DB_CONTAINER_NAME $WEAVEDB_IMAGE $VOLUMES_LABEL \
/weavedb >/dev/null
fi
# Create a data-only container to mount the weavewait files from
if ! docker inspect -f ' ' $VOLUMES_CONTAINER_NAME > /dev/null 2>&1 ; then
protect_against_docker_hang
docker create -v /w -v /w-noop -v /w-nomcast --name=$VOLUMES_CONTAINER_NAME \
--label=weavevolumes --entrypoint=/bin/false $EXEC_IMAGE >/dev/null
if ! util_op container-state $VOLUMES_CONTAINER_NAME > /dev/null 2>&1 ; then
protect_against_docker_hang
util_op create-volume-container $VOLUMES_CONTAINER_NAME $EXEC_IMAGE $VOLUMES_LABEL \
/w /w-noop /w-nomcast >/dev/null
fi
# Set WEAVE_DOCKER_ARGS in the environment in order to supply
# additional parameters, such as resource limits, to docker
# when launching the weave container.
WEAVE_CONTAINER=$(docker run -d --name=$CONTAINER_NAME \
WEAVE_CONTAINER=$(util_op run-container --name $CONTAINER_NAME \
$(docker_run_options) \
$RESTART_POLICY \
--pid=host \
--pid host \
--volumes-from $DB_CONTAINER_NAME \
--volumes-from $VOLUMES_CONTAINER_NAME \
$PROXY_VOLUMES \
@@ -1278,6 +1247,7 @@ launch() {
-e WEAVE_DEBUG \
-e WEAVE_HTTP_ADDR \
-e WEAVE_PASSWORD \
-e EXEC_IMAGE=$EXEC_IMAGE \
-e CHECKPOINT_DISABLE \
$WEAVE_DOCKER_ARGS \
$IMAGE \
@@ -1299,10 +1269,10 @@ launch() {
stop() {
util_op remove-plugin-network weave || true
warn_if_stopping_proxy_in_env
docker stop $CONTAINER_NAME >/dev/null 2>&1 || echo "Weave is not running." >&2
util_op stop-container $CONTAINER_NAME >/dev/null 2>&1 || echo "Weave is not running." >&2
# In case user has upgraded from <v2.0 and left old containers running
docker stop $OLD_PLUGIN_CONTAINER_NAME >/dev/null 2>&1 || true
docker stop $OLD_PROXY_CONTAINER_NAME >/dev/null 2>&1 || true
util_op stop-container $OLD_PLUGIN_CONTAINER_NAME >/dev/null 2>&1 || true
util_op stop-container $OLD_PROXY_CONTAINER_NAME >/dev/null 2>&1 || true
conntrack -D -p udp --dport $PORT >/dev/null 2>&1 || true
}
@@ -1346,18 +1316,13 @@ COMMAND=$1
shift 1
case "$COMMAND" in
setup)
for img in $IMAGE $EXEC_IMAGE $WEAVEDB_IMAGE ; do
docker pull $img
done
setup_cni
;;
setup-cni)
setup|setup-cni)
setup_cni
;;
version)
[ $# -eq 0 ] || usage
ask_version $CONTAINER_NAME $IMAGE || true
VERSION="weave $(call_weave GET /report --get --data-urlencode "format={{ .Version }}" 2> /dev/null)" || VERSION="$(/home/weave/weaver $COVERAGE_ARGS --version)"
echo $VERSION
;;
attach-bridge)
if detect_bridge_type ; then
@@ -1372,7 +1337,7 @@ case "$COMMAND" in
detect_bridge_type && echo $BRIDGE_TYPE
;;
launch)
check_not_running router $CONTAINER_NAME $BASE_IMAGE
check_not_running $CONTAINER_NAME $BASE_IMAGE
if http_call $HTTP_ADDR GET /status >/dev/null 2>&1 ; then
echo "Weave Net is already running" >&2
exit 1
@@ -1441,7 +1406,7 @@ case "$COMMAND" in
attach)
DNS_EXTRA_HOSTS=
REWRITE_HOSTS=
NO_MULTICAST_ROUTE=
ATTACH_ARGS=
collect_cidr_args "$@"
shift $CIDR_ARG_COUNT
WITHOUT_DNS=
@@ -1461,7 +1426,7 @@ case "$COMMAND" in
DNS_EXTRA_HOSTS="${1#*=} $DNS_EXTRA_HOSTS"
;;
--no-multicast-route)
NO_MULTICAST_ROUTE=1
ATTACH_ARGS="$1"
;;
*)
break
@@ -1470,11 +1435,16 @@ case "$COMMAND" in
shift
done
[ $# -eq 1 ] || usage
CONTAINER=$(container_id $1)
CONTAINER=$(util_op container-id $1)
create_bridge
ipam_cidrs allocate $CONTAINER $CIDR_ARGS
[ -n "$REWRITE_HOSTS" ] && rewrite_etc_hosts $DNS_EXTRA_HOSTS
attach $ALL_CIDRS >/dev/null
if [ -n "$REWRITE_HOSTS" ] ; then
# rewrite /etc/hosts, unlinking the file (so Docker does
# not modify it again) but leaving it with valid contents.
util_op rewrite-etc-hosts "$CONTAINER" "$EXEC_IMAGE" "$ALL_CIDRS" $DNS_EXTRA_HOSTS
fi
[ -n "$AWSVPC" ] && ATTACH_ARGS="--no-multicast-route --keep-tx-on"
util_op attach-container $ATTACH_ARGS $CONTAINER $BRIDGE $MTU $ALL_CIDRS >/dev/null
[ -n "$WITHOUT_DNS" ] || when_weave_running with_container_fqdn $CONTAINER put_dns_fqdn $ALL_CIDRS
show_addrs $ALL_CIDRS
;;
@@ -1482,7 +1452,7 @@ case "$COMMAND" in
collect_cidr_args "$@"
shift $CIDR_ARG_COUNT
[ $# -eq 1 ] || usage
CONTAINER=$(container_id $1)
CONTAINER=$(util_op container-id $1)
ipam_cidrs lookup $CONTAINER $CIDR_ARGS
util_op detach-container $CONTAINER $ALL_CIDRS >/dev/null
when_weave_running with_container_fqdn $CONTAINER delete_dns_fqdn $ALL_CIDRS
@@ -1526,7 +1496,6 @@ case "$COMMAND" in
fi
create_bridge
expose_ip
util_op expose-nat $ALL_CIDRS
show_addrs $ALL_CIDRS
;;
hide)
@@ -1539,6 +1508,7 @@ case "$COMMAND" in
ip addr del dev $BRIDGE $CIDR
delete_iptables_rule nat WEAVE -d $CIDR ! -s $CIDR -j MASQUERADE
delete_iptables_rule nat WEAVE -s $CIDR ! -d $CIDR -j MASQUERADE
delete_iptables_rule filter WEAVE-EXPOSE -d $CIDR -j ACCEPT
when_weave_running delete_dns weave:expose $CIDR
fi
done
@@ -1575,12 +1545,14 @@ case "$COMMAND" in
esac
stop
for NAME in $CONTAINER_NAME $OLD_PROXY_CONTAINER_NAME $OLD_PLUGIN_CONTAINER_NAME ; do
docker rm -f $NAME >/dev/null 2>&1 || true
util_op remove-container -f $NAME >/dev/null 2>&1 || true
done
protect_against_docker_hang
VOLUME_CONTAINERS=$(docker ps -qa --filter label=weavevolumes)
[ -n "$VOLUME_CONTAINERS" ] && docker rm -v $VOLUME_CONTAINERS >/dev/null 2>&1 || true
VOLUME_CONTAINERS=$(util_op list-containers $VOLUMES_LABEL)
[ -n "$VOLUME_CONTAINERS" ] && util_op remove-container -v $VOLUME_CONTAINERS >/dev/null 2>&1 || true
# weave-kube and v2-plugin put this file in different places; remove either
rm -f $HOST_ROOT/var/lib/weave/weave-netdata.db >/dev/null 2>&1 || true
rm -f $HOST_ROOT/var/lib/weave/weavedata.db >/dev/null 2>&1 || true
destroy_bridge
for LOCAL_IFNAME in $(ip link show | grep v${CONTAINER_IFNAME}pl | cut -d ' ' -f 2 | tr -d ':') ; do
ip link del ${LOCAL_IFNAME%@*} >/dev/null 2>&1 || true