Compare commits

..
212 Commits
Author SHA1 Message Date
Bryan Boreham 7a9fae5892 Add release v2.2 2018-01-30 12:10:05 +00:00
Martynas Pumputis f66b0cc0aa Merge pull request #3196 from weaveworks/issues/1770-proxy-dns-address
Make proxy use configured WeaveDNS address
2018-01-29 17:20:03 +01:00
Bryan Boreham e7f30106c0 Pass the WeaveDNS Domain into the proxy
instead of obtaining it via the http api
2018-01-29 15:55:36 +00:00
Bryan Boreham 86d2800b59 Make proxy use configured WeaveDNS address
rather than assuming it is listening on the weave bridge
2018-01-29 15:55:36 +00:00
Bryan Boreham e65006042d Remove obsolete --dns-effective-listen-address parameter
it was needed when we ran inside a container network namespace
2018-01-29 15:55:36 +00:00
Bryan Boreham 65efc89b78 Merge pull request #3227 from weaveworks/issues/3223-update-miekgdns
Update miekg/dns to 1.0.4
2018-01-29 15:49:45 +00:00
Bryan Boreham ae38834507 Merge pull request #3229 from weaveworks/k8s-1.9
Update k8s in CI to 1.9.2 and fix flaky tests
2018-01-29 15:49:06 +00:00
Martynas Pumputis 4261777613 Remove "-j KUBESERVICES" during teardown in 840 smoke test
Remove the rule to ensure that api-server is not accessible while
kube-proxy is not ready.

Otherwise, we have a race between "-j KUBE-FORWARD" and "-j WEAVE-NPC"
which makes the NodePort isolation test case to fail.
2018-01-29 16:20:22 +01:00
Martynas Pumputis 7bc089f5c5 Add iproute2 scope info to assertions
The latest alpine linux changed iproute2 of busybox which now
includes scope info.
2018-01-29 16:20:22 +01:00
Martynas Pumputis c46e3e8cf7 Skip CA verification in k8s smoke tests 2018-01-29 16:20:22 +01:00
Martynas Pumputis 180d554205 Do not pin k8s vsn in 840 smoke test 2018-01-29 16:20:22 +01:00
Martynas Pumputis a450d2e741 Bump CI k8s vsn to 1.9.2 2018-01-29 16:20:22 +01:00
Bryan Boreham d528196033 Merge pull request #3224 from weaveworks/fix-ipt-rules-order
Fix subnet isolation and bump build-tools to latest
2018-01-29 15:19:13 +00:00
Martynas Pumputis 0da2cbf84e Update build-tools to include the defunct process fix 2018-01-29 15:52:24 +01:00
Martynas Pumputis 20f981bc42 Update miekg/dns to 1.0.4
Includes the fix for CVE-2017-15133.
2018-01-29 13:55:03 +01:00
Martynas Pumputis 9a5514ffe0 Add comment about the FORWARD rule order
Should help debugging.
2018-01-20 14:44:40 +01:00
Martynas Pumputis 439b4ad190 Improve naming of ensureRulesAtTop function 2018-01-20 12:51:31 +01:00
Martynas Pumputis 9a9c06966b Bring the weave bridge isolation iptables rule to the top
Make the rule creation to use the same mechanism (via ensureRules)
as other rules from filter/FORWARD. Otherwise, the rule is preceded
by "-j WEAVE-EXPOSE" rule which can break the subnet isolation.
2018-01-20 12:47:11 +01:00
Martynas Pumputis b3f9baac8f Merge pull request #3210 from weaveworks/issues/3209-npc-rules-first
Move weave-npc checks to top of FORWARD chain

Fixes #3209
2018-01-14 12:45:18 +01:00
Bryan Boreham bb961489b0 Merge branch '2.1' 2018-01-12 10:47:29 +00:00
Bryan Boreham d6ae1f8375 Add CONN_LIMIT to kube-addon docs 2018-01-12 09:48:30 +00:00
Bryan Boreham 5a1361167e Move our rules to the top of the FORWARD chain
We want our DROP rule to be executed ahead of any ACCEPT rules which
might have been added by another program on the system.
2018-01-11 15:35:19 +00:00
Bryan Boreham 4d5172f67e Run test with Kubernetes 1.8.5 and --cluster-cidr 2018-01-11 15:24:25 +00:00
Bryan Boreham 6f01c31458 Extend kubernetes test to nodePort and virtual IP 2018-01-11 15:24:25 +00:00
Bryan Boreham 6cfb208f3f Merge pull request #3204 from weaveworks/issues/3133-idempotent-bridge
Do not skip bridge creation if bridge exists
2018-01-09 17:20:06 +00:00
Bryan Boreham 3074892f1b Merge pull request #3216 from weaveworks/update-release-process
Update docs release process
2018-01-09 17:18:58 +00:00
Brice Fernandes b05d977cf9 Merge pull request #3217 from weaveworks/improve-test-readme
Improve instructions for running tests against a remote cluster
2018-01-09 17:08:49 +00:00
Brice Fernandes 36d183d988 Improve readme 2018-01-09 14:51:25 +00:00
Brice Fernandes b154fd56fb Fix typo 2018-01-09 13:58:07 +00:00
Brice Fernandes 3419f69246 Make sure to mention Ansible in instructions 2018-01-09 13:41:02 +00:00
Bryan Boreham 8e23301028 Merge pull request #3214 from weaveworks/vagrant-more-mem
Give more memory to vagrant test machines
2018-01-08 17:36:59 +00:00
Bryan Boreham 72724ee389 Merge pull request #3215 from weaveworks/fix-k8s-use-legacy-mode-docs
Fix link to k8s legacy netpol docs
2018-01-08 17:36:29 +00:00
Brice Fernandes ae59a053b1 Improve readme formatting 2018-01-08 16:45:00 +00:00
Brice Fernandes e71919226b Add cloud build instructions to test readme 2018-01-08 16:38:22 +00:00
Martynas Pumputis 6c5dd75216 Merge pull request #3183 from weaveworks/trim-npc-json
Trim the JSON logged for Kubernetes objects by weave-npc
2018-01-06 17:51:34 +01:00
Bryan Boreham 23095728f5 Merge branch '2.1' 2018-01-06 15:41:56 +00:00
Martynas Pumputis 498dacb4d2 Update docs release process 2018-01-06 14:55:01 +01:00
Martynas Pumputis b373a069e9 Fix link to k8s legacy netpol docs 2018-01-06 14:48:20 +01:00
Martynas Pumputis 1d67e2f955 Add test case for restoring iptables rules after weaver restart 2018-01-06 13:20:02 +01:00
Martynas Pumputis 9a6935bf40 Give more memory to vagrant test machines
To be able to run k8s smoke tests.
2018-01-06 12:48:17 +01:00
Martynas Pumputis 921ffefd90 Ensure the order of filter/FORWARD iptables rules 2018-01-05 08:33:50 +01:00
Bryan Boreham a4fccd43eb Merge pull request #3185 from weaveworks/note-kube-dns
Note that WeaveDNS is disabled in Kubernetes addon
2018-01-04 14:57:14 +00:00
Bryan Boreham 48e1a8295b Update Kubernetes docs info 2018-01-04 15:01:06 +00:00
Bryan Boreham 890c8c1905 Extend description of weave-kube
and mention WeaveDNS is disabled
2018-01-04 14:59:43 +00:00
Bryan Boreham 38d50ce6fb Note that WeaveDNS is disabled in Kubernetes addon 2018-01-04 14:59:43 +00:00
Martynas Pumputis 14768d6947 Add smoke test for bridge re-creation after restart 2017-12-22 10:39:02 +01:00
Bryan Boreham b96399b563 Merge branch '2.1' 2017-12-20 16:10:58 +00:00
Bryan Boreham d8c008dc7d Merge pull request #3203 from weaveworks/mac-messages-to-debug
Demote messages about MAC discovery and expiry to DEBUG level
2017-12-20 11:29:27 +00:00
Bryan Boreham b2ae5f4f4b Demote messages about MAC discovery and expiry to DEBUG level 2017-12-18 11:36:24 +00:00
Bryan Boreham ba871d7855 Merge branch '2.1' 2017-12-18 11:25:30 +00:00
Martynas Pumputis a0c8fc2ddb Pass WEAVE_NO_BRIDGED_FASTDP to weaver 2017-12-17 19:05:12 +01:00
Martynas Pumputis 2a12053122 Do not skip bridge initialization if such exists
EnsureBridge used to skip creation and initialization of the weave
bridge and friends if such existed. This caused problems when
EnsureBridge failed before completing all init steps and so, subsequent
weaver could not work without "weave reset".

This change makes EnsureBridge idempotent.
2017-12-17 18:51:05 +01:00
Martynas Pumputis 1860f21efb Remove "create_bridge" routines
No longer needed in the weave script and thus, in weaveutil.
2017-12-16 21:19:25 +01:00
Martynas Pumputis 541adf6bb1 Make "weave attach" to depend on running weaver
"weave attach $CIDR $CONTAINER_ID" is no longer possible w/o
running weaver. This feature was undocumented and complicated
the weave script, as "weave attach" required to call "create_bridge".
Removing the feature allows us to eliminate the bridge creation
from the script.

Running "weave attach" w/o weaver results in the following err:

    bridge "weave" not present; did you launch weave?

Also, this commit simplifies the "attach-container" cmd from
weaveutil by removing "mtu" as a param. The cmd is used
only by "weave attach", and a mtu is value for an attached
container interface is exactly the same as one of the weave bridge.
2017-12-16 21:17:45 +01:00
Martynas Pumputis a71e90ddb8 Do not create bridge upon "weave expose" or "weave hide"
Both cmds depend on running weaver which creates the bridge
during its initialization. So, calling "create_bridge"
during execution of the cmds is redundant.
2017-12-16 20:52:24 +01:00
Bryan Boreham 60f4b3cdb8 Merge pull request #3198 from weaveworks/explain-active
Add meaning of 'active' in 'weave status ipam'
2017-12-15 11:53:43 +00:00
Martynas Pumputis 0c249fa126 Update netlink dependency
To include LinkNotFoundError.
2017-12-14 16:56:59 +01:00
Bryan Boreham db1b57900b Merge pull request #3197 from vetal4444/log_level_setting
Add ability to set log level for net-plugin, and default to INFO
2017-12-14 12:26:01 +00:00
Shevchenko Vitaliy 12b4898b3d Add ability to set log level for net-plugin 2017-12-14 14:19:33 +02:00
Bryan Boreham f5f9bab77c Add meaning of 'active' in 'weave status ipam' 2017-12-14 10:18:53 +00:00
Bryan Boreham ba7aa674d1 Merge branch '2.1' 2017-12-04 15:03:02 +00:00
Bryan Boreham f670be4315 Add release v2.1.3 2017-12-04 14:38:05 +00:00
Bryan Boreham a658861fde Merge pull request #3189 from weaveworks/release-latest-plugin
Fix release script for plugin latest_release
2017-12-04 06:33:02 -08:00
Bryan Boreham 192e5a1f79 Merge branch '2.1' 2017-12-04 13:51:30 +00:00
Bryan Boreham 475b15e372 Merge pull request #3192 from weaveworks/issues/3190-deepcopy-configmaps
Fix ipam reclaim
2017-12-01 12:33:48 -08:00
Bryan Boreham d2ed91ccc8 Clean up log messages a bit 2017-12-01 14:20:39 +00:00
Bryan Boreham 333f4a1d3c Make sure Annotation names conform to Kubernetes requirements.
E.g. replace "26:c6:0b:ba:fd:31" with "26_c6_0b_ba_fd_31"
2017-12-01 14:20:39 +00:00
Bryan Boreham 832fe2f0b0 Update local copy of annotations even after error
e.g. if we get a clash because someone else has updated the data, it
returns the new data so we should work off that next time.

Also re-fetch the Annotations every time round the main loop for safety.
2017-12-01 14:20:39 +00:00
Martynas Pumputis ea76185873 Update k8s netpol docs
- Mention `--use-legacy-netpol` flag.
- Link to NetworkPolicy from 1.7.
2017-12-01 14:20:21 +00:00
Bryan Boreham 51c3ea184a Fix release script for plugin latest_release
We should never update the latest_release plugin on a PRERELEASE.
We should always update it on a MAINLINE or BRANCH release.
2017-11-27 15:04:39 +00:00
Bryan Boreham 692bccc4fc Merge branch '2.1' 2017-11-27 15:03:48 +00:00
Bryan Boreham 59fe76f29b Add release v2.1.2 2017-11-27 14:06:33 +00:00
Bryan Boreham 51c2a0d732 Merge pull request #3174 from weaveworks/issues/docs-use-legacy-netpol
Update k8s netpol docs
2017-11-27 12:45:59 +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
Bryan Boreham 0205adcedb Trim the JSON logged for Kubernetes objects by weave-npc
Remove things like "args" and "env" which bulk the logs, aren't
relevant to the function of weave-npc, and may include sensitive
information.

Use a black-list rather than a white-list because we can't be sure
what information will be necessary in future.
2017-11-22 11:17:50 +00: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
Martynas Pumputis a3fd36e2bb Update k8s netpol docs
- Mention `--use-legacy-netpol` flag.
- Link to NetworkPolicy from 1.7.
2017-11-16 17:00:27 +02: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
106 changed files with 3479 additions and 1031 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
+145
View File
@@ -1,3 +1,148 @@
## Release 2.2.0
This release improves the way Weave Net configures Linux network
devices and network filter rules, so that it is more robust in the face
of unexpected changes in its environment. #3204,#3224
As a consequence of these changes, the `weave attach` command will now
fail unless the Weave Net daemon is up and running - previously it was
possible to run independently as long as you managed all IP addresses
yourself.
## Other improvements
* Update library miekg/dns for CVE-2017-15133 (details under embargo) #3223,#3227
* Reduce the volume of logging from weave-npc #3183
* Add ability to set log level for Docker "v2" plugin, and change
default log level from DEBUG to INFO #3197
* Downgrade log messages about Discovery and Expiration to DEBUG level #3202,#3203
* Use command-line parameter for WeaveDNS address in Docker proxy #3196
## Bug fixes
* Ensure that rules to block traffic for NetworkPolicy are placed
ahead of rules that Kubernetes has added to allow other traffic #3209,#3210
## Build and test
* Update CI tests to use Kubernetes 1.9.2 #3229
* Remove "daily update" from test VMs that only run for a few minutes #3224
## External Contributors
Thanks to the following contributors:
@vetal4444
[Full list of changes](https://github.com/weaveworks/weave/milestone/67?closed=1)
## Release 2.1.3
This release fixes a race-condition in the IP reclaim code for weave-kube
where two nodes could end up fighting over the same space and break
connectivity #3190, #3192
## 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
+2 -2
View File
@@ -1,3 +1,3 @@
DOCKER_VERSION=17.06
KUBERNETES_VERSION=1.6.2
KUBERNETES_CNI_VERSION=0.5.1
KUBERNETES_VERSION=1.9.2
KUBERNETES_CNI_VERSION=0.6.0
+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"
}
+20 -5
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"
}
@@ -188,7 +196,7 @@ publish() {
if [ "$RELEASE_TYPE" = 'PRERELEASE' ] ; then
echo "== Building and pushing plugin"
make UPDATE_LATEST=$IS_MAINLINE SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
make UPDATE_LATEST=false SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
echo "** Plugin built and pushed"
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
@@ -224,7 +232,7 @@ publish() {
echo '** Sanity checks OK for publishing tag' $LATEST_TAG as $DOCKERHUB_USER/weave:$VERSION
echo "== Building and pushing plugin"
make UPDATE_LATEST=$IS_MAINLINE SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
make UPDATE_LATEST=true SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
echo "** Plugin built and pushed"
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
@@ -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
+12
View File
@@ -75,6 +75,14 @@ func DeleteDatapath(dpname string) error {
}
func AddDatapathInterface(dpname string, ifname string) error {
return addDatapathInterface(dpname, ifname, false)
}
func AddDatapathInterfaceIfNotExist(dpname string, ifname string) error {
return addDatapathInterface(dpname, ifname, true)
}
func addDatapathInterface(dpname, ifname string, ignoreIfExist bool) error {
dpif, err := odp.NewDpif()
if err != nil {
return err
@@ -87,5 +95,9 @@ func AddDatapathInterface(dpname string, ifname string) error {
}
_, err = dp.CreateVport(odp.NewNetdevVportSpec(ifname))
if ignoreIfExist && err == odp.NetlinkError(syscall.EEXIST) {
return nil
}
return err
}
+2 -2
View File
@@ -133,8 +133,8 @@ Finally, for **Mainline** releases only:
* Close the [milestone](https://github.com/weaveworks/weave/milestones) in GitHub and create the next milestone
* Update the `#weavenetwork` topic heading on freenode (requires 'chanops' permission)
* For a mainline release vX.Y.0, create a release branch X.Y from the
tag and push to GitHub - this will result in X.Y.0 site docs being
published to https://www.weave.works
tag, push to GitHub and set [WEAVE_NET_REV](https://github.com/weaveworks/website-next/blob/master/Makefile)
to X.Y via a PR - this will result in X.Y.0 site docs being published to https://www.weave.works
* Add the new version of `weave-net` to the checkpoint system at
https://checkpoint-api.weave.works/admin
* File a PR to update the version of the daemonset at
+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 {
+78 -68
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"
@@ -228,12 +227,19 @@ func (config *BridgeConfig) configuredBridgeType() Bridge {
}
func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger) (Bridge, error) {
bridgeType, err := ExistingBridgeType(config.WeaveBridgeName, config.DatapathName)
if bridgeType != nil || err != nil {
return bridgeType, err
existingBridgeType, err := ExistingBridgeType(config.WeaveBridgeName, config.DatapathName)
if err != nil {
return nil, err
}
bridgeType := config.configuredBridgeType()
if existingBridgeType != nil && bridgeType.String() != existingBridgeType.String() {
return nil,
fmt.Errorf("Existing bridge type %q is different than requested %q. Please do 'weave reset' and try again",
existingBridgeType, bridgeType)
}
bridgeType = config.configuredBridgeType()
for {
if err := bridgeType.init(config); err != nil {
if errors.Cause(err) == errBridgeNotSupported {
@@ -289,7 +295,7 @@ func (b bridgeImpl) initPrep(config *BridgeConfig) error {
config.MTU = 65535
}
b.bridge = &netlink.Bridge{LinkAttrs: linkAttrs}
if err = netlink.LinkAdd(b.bridge); err != nil {
if err := LinkAddIfNotExist(b.bridge); err != nil {
return errors.Wrapf(err, "creating bridge %q", config.WeaveBridgeName)
}
if err := netlink.LinkSetHardwareAddr(b.bridge, mac); err != nil {
@@ -321,7 +327,7 @@ func (b bridgeImpl) init(config *BridgeConfig) error {
if err := b.initPrep(config); err != nil {
return err
}
if _, err := CreateAndAttachVeth(BridgeIfName, PcapIfName, config.WeaveBridgeName, config.MTU, true, func(veth netlink.Link) error {
if _, err := CreateAndAttachVeth(BridgeIfName, PcapIfName, config.WeaveBridgeName, config.MTU, true, false, func(veth netlink.Link) error {
return netlink.LinkSetUp(veth)
}); err != nil {
return errors.Wrap(err, "creating pcap veth pair")
@@ -371,11 +377,11 @@ func (bf bridgedFastdpImpl) init(config *BridgeConfig) error {
if err := bf.bridgeImpl.initPrep(config); err != nil {
return err
}
if _, err := CreateAndAttachVeth(BridgeIfName, DatapathIfName, config.WeaveBridgeName, config.MTU, true, func(veth netlink.Link) error {
if _, err := CreateAndAttachVeth(BridgeIfName, DatapathIfName, config.WeaveBridgeName, config.MTU, true, false, func(veth netlink.Link) error {
if err := netlink.LinkSetUp(veth); err != nil {
return errors.Wrapf(err, "setting link up on %q", veth.Attrs().Name)
}
if err := odp.AddDatapathInterface(bf.datapathName, veth.Attrs().Name); err != nil {
if err := odp.AddDatapathInterfaceIfNotExist(bf.datapathName, veth.Attrs().Name); err != nil {
return errors.Wrapf(err, "adding interface %q to datapath %q", veth.Attrs().Name, bf.datapathName)
}
return nil
@@ -383,11 +389,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 {
@@ -399,7 +401,7 @@ func (bf bridgedFastdpImpl) attach(veth *netlink.Veth) error {
}
func (f fastdpImpl) attach(veth *netlink.Veth) error {
return odp.AddDatapathInterface(f.datapathName, veth.Attrs().Name)
return odp.AddDatapathInterfaceIfNotExist(f.datapathName, veth.Attrs().Name)
}
func configureIPTables(config *BridgeConfig) error {
@@ -407,12 +409,13 @@ func configureIPTables(config *BridgeConfig) error {
if err != nil {
return errors.Wrap(err, "creating iptables object")
}
// The order among weave filter/FORWARD rules is important!
fwdRules := make([][]string, 0)
if config.DockerBridgeName != "" {
if config.WeaveBridgeName != config.DockerBridgeName {
err := ipt.Insert("filter", "FORWARD", 1, "-i", config.DockerBridgeName, "-o", config.WeaveBridgeName, "-j", "DROP")
if err != nil {
return err
}
fwdRules = append(fwdRules, []string{"-i", config.DockerBridgeName, "-o", config.WeaveBridgeName, "-j", "DROP"})
}
dockerBridgeIP, err := FindBridgeIP(config.DockerBridgeName, nil)
@@ -443,41 +446,43 @@ func configureIPTables(config *BridgeConfig) error {
if config.NPC {
// Steer traffic via the NPC
_ = ipt.NewChain("filter", "WEAVE-NPC") // ignore error because it might already exist
// If WEAVE-NPC chain doesn't exist then next line will fail
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "WEAVE-NPC"); err != nil {
return err
}
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-m", "state", "--state", "NEW", "-j", "NFLOG", "--nflog-group", "86"); err != nil {
return err
}
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "DROP"); err != nil {
return err
}
// If WEAVE-NPC chain doesn't exist then creating a rule in the chain will fail
fwdRules = append(fwdRules,
[][]string{
{"-o", config.WeaveBridgeName,
"-m", "comment", "--comment", "NOTE: this must go before '-j KUBE-FORWARD'",
"-j", "WEAVE-NPC"},
{"-o", config.WeaveBridgeName, "-m", "state", "--state", "NEW", "-j", "NFLOG", "--nflog-group", "86"},
{"-o", config.WeaveBridgeName, "-j", "DROP"},
}...)
} else {
// Work around the situation where there are no rules allowing traffic
// across our bridge. E.g. ufw
if err = ipt.AppendUnique("filter", "FORWARD", "-i", config.WeaveBridgeName, "-o", config.WeaveBridgeName, "-j", "ACCEPT"); err != nil {
return err
}
fwdRules = append(fwdRules, []string{"-i", config.WeaveBridgeName, "-o", config.WeaveBridgeName, "-j", "ACCEPT"})
}
if !config.NPC {
// Create a chain for allowing ingress traffic when the bridge is exposed
_ = ipt.NewChain("filter", "WEAVE-EXPOSE")
fwdRules = append(fwdRules, []string{"-o", config.WeaveBridgeName, "-j", "WEAVE-EXPOSE"})
}
// 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
}
fwdRules = append(fwdRules, []string{"-i", config.WeaveBridgeName, "!", "-o", config.WeaveBridgeName, "-j", "ACCEPT"})
// and allow replies back
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"); err != nil {
fwdRules = append(fwdRules, []string{"-o", config.WeaveBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"})
if err := ensureRulesAtTop("filter", "FORWARD", fwdRules, ipt); err != nil {
return err
}
// create a chain for masquerading
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
}
//
// NB: we do not clear the chain to preserve existing rules
// inserted by "weave expose".
_ = ipt.NewChain("nat", "WEAVE")
return nil
return ipt.AppendUnique("nat", "POSTROUTING", "-j", "WEAVE")
}
func linkSetUpByName(linkName string) error {
@@ -488,34 +493,39 @@ 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)
// ensureRulesAtTop ensures the presence of given iptables rules.
//
// If any rule from the list is missing, the function deletes all given
// rules and re-inserts them at the top of the chain to ensure the order of the rules.
func ensureRulesAtTop(table, chain string, rulespecs [][]string, ipt *iptables.IPTables) error {
allFound := true
for _, rs := range rulespecs {
found, err := ipt.Exists(table, chain, rs...)
if err != nil {
return errors.Wrapf(err, "failed to get route list for bridge %q", bridgeName)
return errors.Wrapf(err, "ipt.Exists(%s, %s, %s)", table, chain, rs)
}
for _, r := range routes {
if err = netlink.RouteDel(&r); err != nil {
return errors.Wrapf(err, "failed to delete default route %+v", r)
}
if !found {
allFound = false
break
}
}
// All rules exist, do nothing.
if allFound {
return nil
}
for pos, rs := range rulespecs {
// If any is missing, then delete all, as we need to preserve the order of
// given rules. Ignore errors, as rule might not exist.
if !allFound {
ipt.Delete(table, chain, rs...)
}
if err := ipt.Insert(table, chain, pos+1, rs...); err != nil {
return errors.Wrapf(err, "ipt.Append(%s, %s, %s)", table, chain, rs)
}
}
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 {
+16
View File
@@ -0,0 +1,16 @@
package net
import (
"syscall"
"github.com/pkg/errors"
"github.com/vishvananda/netlink"
)
func LinkAddIfNotExist(link netlink.Link) error {
err := netlink.LinkAdd(link)
if err != nil && err == syscall.EEXIST {
return nil
}
return errors.Wrapf(err, "creating link %q", link)
}
+9 -45
View File
@@ -4,7 +4,6 @@ import (
"fmt"
"net"
"strings"
"syscall"
"time"
"github.com/coreos/go-iptables/iptables"
@@ -14,7 +13,7 @@ import (
)
// create and attach a veth to the Weave bridge
func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, keepTXOn bool, init func(peer netlink.Link) error) (*netlink.Veth, error) {
func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, keepTXOn bool, errIfLinkExist bool, init func(peer netlink.Link) error) (*netlink.Veth, error) {
bridge, err := netlink.LinkByName(bridgeName)
if err != nil {
return nil, fmt.Errorf(`bridge "%s" not present; did you launch weave?`, bridgeName)
@@ -29,7 +28,12 @@ func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, keepTXOn bo
MTU: mtu},
PeerName: peerName,
}
if err := netlink.LinkAdd(veth); err != nil {
linkAdd := LinkAddIfNotExist
if errIfLinkExist {
linkAdd = netlink.LinkAdd
}
if err := linkAdd(veth); err != nil {
return nil, fmt.Errorf(`could not create veth pair %s-%s: %s`, name, peerName, err)
}
@@ -117,7 +121,7 @@ func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMult
id = id[:maxIDLen] // trim passed ID if too long
}
name, peerName := vethPrefix+"pl"+id, vethPrefix+"pg"+id
veth, err := CreateAndAttachVeth(name, peerName, bridgeName, mtu, keepTXOn, func(veth netlink.Link) error {
veth, err := CreateAndAttachVeth(name, peerName, bridgeName, mtu, keepTXOn, true, func(veth netlink.Link) error {
if err := netlink.LinkSetNsFd(veth, int(ns)); err != nil {
return fmt.Errorf("failed to move veth to container netns: %s", err)
}
@@ -218,11 +222,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 +301,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)
}
}
}
+49
View File
@@ -4,7 +4,56 @@ import (
"encoding/json"
)
// Return JSON suitable for logging an API object.
func js(v interface{}) string {
// Get the raw JSON
a, _ := json.Marshal(v)
// Convert this back into a tree of key-value maps
var m map[string]interface{}
if err := json.Unmarshal(a, &m); err != nil {
// If that didn't work, just return the raw version
return string(a)
}
// Trim some bulk, and potentially sensitive areas
withMap(m["metadata"], func(status map[string]interface{}) {
delete(status, "ownerReferences")
})
withMap(m["spec"], func(spec map[string]interface{}) {
delete(spec, "tolerations")
delete(spec, "volumes")
rangeSlice(spec["containers"], func(container map[string]interface{}) {
delete(container, "args")
delete(container, "command")
delete(container, "env")
delete(container, "livenessProbe")
delete(container, "resources")
delete(container, "securityContext")
delete(container, "volumeMounts")
})
})
withMap(m["status"], func(status map[string]interface{}) {
delete(status, "containerStatuses")
})
// Now marshall what's left to JSON
a, _ = json.Marshal(m)
return string(a)
}
// Helper function: operate on a map node from a tree of key-value maps
func withMap(m interface{}, f func(map[string]interface{})) {
if v, ok := m.(map[string]interface{}); ok {
f(v)
}
}
// Helper function: operate on all nodes under i which is a slice in a
// tree of key-value maps
func rangeSlice(i interface{}, f func(map[string]interface{})) {
if s, ok := i.([]interface{}); ok {
for _, v := range s {
if m, ok := v.(map[string]interface{}); ok {
f(m)
}
}
}
}
+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
}
+1 -1
View File
@@ -134,7 +134,7 @@ func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.Cr
// create veths. note we assume endpoint IDs are unique in the first 9 chars
name, peerName := vethPair(create.EndpointID)
if _, err := weavenet.CreateAndAttachVeth(name, peerName, weavenet.WeaveBridgeName, 0, false, nil); err != nil {
if _, err := weavenet.CreateAndAttachVeth(name, peerName, weavenet.WeaveBridgeName, 0, false, true, nil); err != nil {
return nil, driver.error("JoinEndpoint", "%s", err)
}
-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
+210
View File
@@ -0,0 +1,210 @@
/*
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
// Prefix all our annotation keys with this string so they don't clash with anyone else's
KubePeersPrefix = "kube-peers.weave.works/"
// KubePeersAnnotationKey is the default annotation key
KubePeersAnnotationKey = KubePeersPrefix + "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))
}
// Clean up a string so it meets the Kubernetes requiremements for Annotation keys:
// name part must consist of alphanumeric characters, '-', '_' or '.', and must
// start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc')
func cleanKey(key string) string {
buf := []byte(key)
for i, c := range buf {
if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '/' {
continue
}
buf[i] = '_'
}
return string(buf)
}
func (cml *configMapAnnotations) GetAnnotation(key string) (string, bool) {
value, ok := cml.cm.Annotations[cleanKey(key)]
return value, ok
}
func (cml *configMapAnnotations) UpdateAnnotation(key, value string) (err error) {
if cml.cm == nil {
return errors.New("endpoint not initialized, call Init first")
}
// speculatively change the state, then replace with whatever comes back
// from Update(), which will be the latest on the server whatever happened
cml.cm.Annotations[cleanKey(key)] = value
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
return err
}
func (cml *configMapAnnotations) RemoveAnnotation(key string) (err error) {
if cml.cm == nil {
return errors.New("endpoint not initialized, call Init first")
}
// speculatively change the state, then replace with whatever comes back
// from Update(), which will be the latest on the server whatever happened
delete(cml.cm.Annotations, cleanKey(key))
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
return err
}
func (cml *configMapAnnotations) RemoveAnnotationsWithValue(valueToRemove string) (err error) {
if cml.cm == nil {
return errors.New("endpoint not initialized, call Init first")
}
// speculatively change the state, then replace with whatever comes back
// from Update(), which will be the latest on the server whatever happened
for key, value := range cml.cm.Annotations {
if value == valueToRemove {
delete(cml.cm.Annotations, key) // don't need to clean this key as it came from the map
}
}
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.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
}
+197 -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,189 @@ 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 {
if err := cml.Init(); err != nil {
return err
}
// 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.GetAnnotation(KubePeersPrefix + 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(KubePeersPrefix+peer.PeerName, myPeerName); err == nil {
okToRemove = true
} else {
common.Log.Debugln("[kube-peers] error from UpdateAnnotation: ", err)
}
}
if okToRemove {
// 6. If step 4 or 5 succeeded, rmpeer X
result, err := weave.RmPeer(peer.PeerName)
common.Log.Infof("[kube-peers] rmpeer of %s: %s", 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)
if err := cml.UpdatePeerList(*storedPeerList); err != nil {
return err
}
// 7b. Remove annotation with key X
return cml.RemoveAnnotation(KubePeersPrefix + 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)
}
}
+8
View File
@@ -50,6 +50,14 @@
}
],
"env": [
{
"description": "Log level",
"name": "LOG_LEVEL",
"settable": [
"value"
],
"value": ""
},
{
"description": "Extra args to `weaver` and `plugin`",
"name": "EXTRA_ARGS",
+2 -1
View File
@@ -7,6 +7,7 @@ IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
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}
LOG_LEVEL=${LOG_LEVEL:-info}
WEAVE_DIR="/host/var/lib/weave"
mkdir $WEAVE_DIR || true
@@ -44,7 +45,7 @@ exec /home/weave/weaver $EXTRA_ARGS --port=6783 $(router_bridge_opts) \
--no-dns \
--ipalloc-range=$IPALLOC_RANGE \
--nickname "$(hostname)" \
--log-level=debug \
--log-level=$LOG_LEVEL \
--db-prefix="$WEAVE_DIR/weave" \
--plugin-v2 \
$(multicast_opt) \
+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 {
+38 -27
View File
@@ -45,12 +45,20 @@ type ipamConfig struct {
}
type dnsConfig struct {
Domain string
ListenAddress string
TTL int
ClientTimeout time.Duration
EffectiveListenAddress string
ResolvConf string
Domain string
ListenAddress string
TTL int
ClientTimeout time.Duration
ResolvConf string
}
// return the address part of the DNS listen address, without ":53" on the end
func (c dnsConfig) addressOnly() string {
addr, _, err := net.SplitHostPort(c.ListenAddress)
if err != nil {
Log.Fatalf("Error when parsing DNS listen address %q: %s", c.ListenAddress, err)
}
return addr
}
func (c *ipamConfig) Enabled() bool {
@@ -131,6 +139,7 @@ func main() {
var (
justVersion bool
justPeerName bool
config mesh.Config
bridgeConfig weavenet.BridgeConfig
networkConfig weave.NetworkConfig
@@ -163,6 +172,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")
@@ -191,10 +201,10 @@ func main() {
mflag.StringVar(&dnsConfig.ListenAddress, []string{"-dns-listen-address"}, nameserver.DefaultListenAddress, "address to listen on for DNS requests")
mflag.IntVar(&dnsConfig.TTL, []string{"-dns-ttl"}, nameserver.DefaultTTL, "TTL for DNS request from our domain")
mflag.DurationVar(&dnsConfig.ClientTimeout, []string{"-dns-fallback-timeout"}, nameserver.DefaultClientTimeout, "timeout for fallback DNS requests")
mflag.StringVar(&dnsConfig.EffectiveListenAddress, []string{"-dns-effective-listen-address"}, "", "address DNS will actually be listening, after Docker port mapping")
mflag.StringVar(&dnsConfig.ResolvConf, []string{"-resolv-conf"}, "", "path to resolver configuration for fallback DNS lookups")
mflag.StringVar(&bridgeConfig.DatapathName, []string{"-datapath"}, "", "ODP datapath name")
mflag.BoolVar(&bridgeConfig.NoFastdp, []string{"-no-fastdp"}, false, "Disable Fast Datapath")
mflag.BoolVar(&bridgeConfig.NoBridgedFastdp, []string{"-no-bridged-fastdp"}, false, "Disable Bridged Fast Datapath")
mflag.StringVar(&trustedSubnetStr, []string{"-trusted-subnets"}, "", "comma-separated list of trusted subnets in CIDR notation")
mflag.StringVar(&dbPrefix, []string{"-db-prefix"}, "/weavedb/weave", "pathname/prefix of filename to store data")
mflag.StringVar(&procPath, []string{"-proc-path"}, "/proc", "path to reach host /proc filesystem")
@@ -210,11 +220,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 +236,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 {
@@ -254,6 +271,8 @@ func main() {
var proxy *weaveproxy.Proxy
var err error
if proxyConfig.Enabled {
proxyConfig.DNSListenAddress = dnsConfig.addressOnly()
proxyConfig.DNSDomain = dnsConfig.Domain
if noDNS {
proxyConfig.WithoutDNS = true
}
@@ -282,8 +301,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 +339,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 +486,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 +542,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")
@@ -630,17 +645,13 @@ func createDNSServer(config dnsConfig, router *mesh.Router, isKnownPeer func(mes
gossip, err := router.NewGossip("nameserver", ns)
checkFatal(err)
ns.SetGossip(gossip)
upstream := nameserver.NewUpstream(config.ResolvConf, config.EffectiveListenAddress)
upstream := nameserver.NewUpstream(config.ResolvConf, config.addressOnly())
dnsserver, err := nameserver.NewDNSServer(ns, config.Domain, config.ListenAddress,
upstream, uint32(config.TTL), config.ClientTimeout)
if err != nil {
Log.Fatal("Unable to start dns server: ", err)
}
listenAddr := config.ListenAddress
if config.EffectiveListenAddress != "" {
listenAddr = config.EffectiveListenAddress
}
Log.Println("Listening for DNS queries on", listenAddr)
Log.Println("Listening for DNS queries on", config.ListenAddress)
return ns, dnsserver
}
+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 {
+21 -32
View File
@@ -4,11 +4,9 @@ import (
"fmt"
"net"
"os"
"strconv"
"strings"
"syscall"
docker "github.com/fsouza/go-dockerclient"
"github.com/vishvananda/netns"
weavenet "github.com/weaveworks/weave/net"
@@ -16,8 +14,8 @@ import (
)
func attach(args []string) error {
if len(args) < 4 {
cmdUsage("attach-container", "[--no-multicast-route] [--keep-tx-on] [--hairpin-mode=true|false] <container-id> <bridge-name> <mtu> <cidr>...")
if len(args) < 3 {
cmdUsage("attach-container", "[--no-multicast-route] [--keep-tx-on] [--hairpin-mode=true|false] <container-id> <bridge-name> <cidr>...")
}
keepTXOn := false
@@ -53,16 +51,12 @@ func attach(args []string) error {
} else if nsHost.Equal(nsContainer) {
return fmt.Errorf("Container is running in the host network namespace, and therefore cannot be\nconnected to weave - perhaps the container was started with --net=host")
}
mtu, err := strconv.Atoi(args[2])
if err != nil && args[3] != "" {
return fmt.Errorf("unable to parse mtu %q: %s", args[2], err)
}
cidrs, err := parseCIDRs(args[3:])
cidrs, err := parseCIDRs(args[2:])
if err != nil {
return err
}
err = weavenet.AttachContainer(weavenet.NSPathByPid(pid), fmt.Sprint(pid), weavenet.VethName, args[1], mtu, withMulticastRoute, cidrs, keepTXOn, hairpinMode)
err = weavenet.AttachContainer(weavenet.NSPathByPid(pid), fmt.Sprint(pid), weavenet.VethName, args[1], 0, withMulticastRoute, cidrs, keepTXOn, hairpinMode)
// If we detected an error but the container has died, tell the user that instead.
if err != nil && !processExists(pid) {
err = fmt.Errorf("Container %s died", args[0])
@@ -70,21 +64,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 +98,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"
-35
View File
@@ -2,9 +2,7 @@ package main
import (
"fmt"
"strconv"
"github.com/weaveworks/weave/common"
weavenet "github.com/weaveworks/weave/net"
)
@@ -22,36 +20,3 @@ func detectBridgeType(args []string) error {
}
return nil
}
func createBridge(args []string) error {
if len(args) != 10 {
cmdUsage("create-bridge", "<docker-bridge-name> <weave-bridge-name> <datapath-name> <mtu> <port> <mac> <no-fastdp> <no-bridged-fastdp> <proc-path> <expect-npc>")
}
mtu, err := strconv.Atoi(args[3])
if err != nil && args[3] != "" {
return err
}
port, err := strconv.Atoi(args[4])
if err != nil {
return err
}
config := weavenet.BridgeConfig{
DockerBridgeName: args[0],
WeaveBridgeName: args[1],
DatapathName: args[2],
MTU: mtu,
Port: port,
Mac: args[5],
NoFastdp: args[6] != "",
NoBridgedFastdp: args[7] != "",
NPC: args[9] == "--expect-npc",
}
procPath := args[8]
bridgeType, err := weavenet.EnsureBridge(procPath, &config, common.Log)
if err != nil {
return err
}
fmt.Println(bridgeType.String())
return nil
}
+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 -3
View File
@@ -17,7 +17,6 @@ func init() {
"help": help,
"netcheck": netcheck,
"docker-tls-args": dockerTLSArgs,
"create-bridge": createBridge,
"detect-bridge-type": detectBridgeType,
"create-datapath": createDatapath,
"delete-datapath": deleteDatapath,
@@ -26,6 +25,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 +44,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 +84,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
}
+3 -3
View File
@@ -68,11 +68,11 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
if err != nil {
return err
}
if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" {
if err := i.setHostname(container, hostname, dnsDomain); err != nil {
if !i.proxy.WithoutDNS {
if err := i.setHostname(container, hostname, i.proxy.DNSDomain); err != nil {
return err
}
if err := i.proxy.setWeaveDNS(hostConfig, hostname, dnsDomain); err != nil {
if err := i.proxy.setWeaveDNS(hostConfig, hostname, i.proxy.DNSDomain); err != nil {
return err
}
}
+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
+9 -26
View File
@@ -60,8 +60,9 @@ type Config struct {
NoDefaultIPAM bool
NoRewriteHosts bool
TLSConfig TLSConfig
Version string
WithoutDNS bool
DNSListenAddress string
DNSDomain string
NoMulticastRoute bool
KeepTXOn bool
DockerBridge string
@@ -79,7 +80,6 @@ type Proxy struct {
Config
client *weavedocker.Client
weave *weaveapi.Client
dockerBridgeIP string
hostnameMatchRegexp *regexp.Regexp
weaveWaitVolume string
weaveWaitNoopVolume string
@@ -159,15 +159,6 @@ func NewProxy(c Config) (*Proxy, error) {
Log.Info(p.client.Info())
if !p.WithoutDNS {
ip, err := weavenet.FindBridgeIP(c.DockerBridge, nil)
if err != nil {
return nil, err
}
p.dockerBridgeIP = ip.String()
Log.Infof("Using docker bridge IP for DNS: %v", p.dockerBridgeIP)
}
p.hostnameMatchRegexp, err = regexp.Compile(c.HostnameMatch)
if err != nil {
err := fmt.Errorf("Incorrect hostname match '%s': %s", c.HostnameMatch, err.Error())
@@ -330,11 +321,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 +497,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
@@ -597,7 +587,7 @@ func (proxy *Proxy) setWeaveDNS(hostConfig jsonObject, hostname, dnsDomain strin
if err != nil {
return err
}
hostConfig["Dns"] = append(dns, proxy.dockerBridgeIP)
hostConfig["Dns"] = append(dns, proxy.DNSListenAddress)
dnsSearch, err := hostConfig.StringArray("DnsSearch")
if err != nil {
@@ -614,14 +604,6 @@ func (proxy *Proxy) setWeaveDNS(hostConfig jsonObject, hostname, dnsDomain strin
return nil
}
func (proxy *Proxy) getDNSDomain() string {
if proxy.WithoutDNS {
return ""
}
domain, _ := proxy.weave.DNSDomain()
return domain
}
func (proxy *Proxy) updateContainerNetworkSettings(container jsonObject) error {
containerID, err := container.String("Id")
if err != nil {
@@ -684,6 +666,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{
+2 -2
View File
@@ -50,8 +50,8 @@ func (i *startContainerInterceptor) InterceptRequest(r *http.Request) error {
return err
}
}
if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" {
if err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, dnsDomain); err != nil {
if !i.proxy.WithoutDNS {
if err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, i.proxy.DNSDomain); err != nil {
return err
}
}
+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)
})
}
+6 -4
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,12 +65,12 @@ 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,
func(mac net.HardwareAddr, peer *mesh.Peer) {
log.Println("Expired MAC", mac, "at", peer)
log.Debugln("Expired MAC", mac, "at", peer)
})
router.Peers.OnGC(func(peer *mesh.Peer) { router.Macs.Delete(peer) })
return router, nil
@@ -90,7 +92,7 @@ func (router *NetworkRouter) handleCapturedPacket(key PacketKey) FlowOp {
switch newSrcMac, conflictPeer := router.Macs.Add(srcMac, router.Ourself.Peer); {
case newSrcMac:
log.Println("Discovered local MAC", srcMac)
log.Debugln("Discovered local MAC", srcMac)
case conflictPeer != nil:
// The MAC cache has an entry for the source MAC
// associated with another peer. This probably means
+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)
+123 -80
View File
@@ -11,9 +11,13 @@ The following topics are discussed:
* [Upgrading the Daemon Sets](#daemon-sets)
* [CPU and Memory Requirements](#resources)
* [Pod Eviction](#eviction)
* [Network Policy Controller](#npc)
* [Features](#features)
* [Pod Network](#pod-network)
* [Network Policy](#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 +87,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
@@ -145,16 +149,33 @@ mypod-09vkd 0/1 Evicted 0 1h <none> node-1
If you see this in your cluster, consider some of the above steps to
reduce disruption.
## <a name="npc"></a>Network Policy Controller
## <a name="features"></a>Features
The addon also supports the [Kubernetes policy
API](http://kubernetes.io/docs/user-guide/networkpolicies/) so that
you can securely isolate pods from each other based on namespaces and
### <a name="pod-network"></a>Pod Network
Weave Net provides a network to connect all pods together,
implementing the [Kubernetes
model](https://kubernetes.io/docs/concepts/cluster-administration/networking/#kubernetes-model).
Kubernetes uses the _Container Network Interface_
([CNI](https://github.com/containernetworking/cni)) to join pods onto Weave Net.
Kubernetes implements many network features itself on top of the pod
network. This includes
[Services](https://kubernetes.io/docs/concepts/services-networking/service/),
[Service Discovery via DNS](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/)
and [Ingress into the cluster](https://kubernetes.io/docs/concepts/services-networking/ingress/).
WeaveDNS is disabled when using the Kubernetes addon.
### <a name="npc"></a>Network Policy
[Kubernetes Network Policies](https://kubernetes.io/docs/concepts/services-networking/network-policies/) let
you securely isolate pods from each other based on namespaces and
labels. For more information on configuring network policies in
Kubernetes see the
[walkthrough](http://kubernetes.io/docs/getting-started-guides/network-policy/walkthrough/)
[walkthrough](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy/)
and the [NetworkPolicy API object
definition](http://kubernetes.io/docs/api-reference/extensions/v1beta1/definitions/#_v1beta1_networkpolicy).
definition](https://v1-7.docs.kubernetes.io/docs/api-reference/v1.7/#networkpolicy-v1-networking)
**Note:** as of version 1.9 of Weave Net, the Network Policy
Controller allows all multicast traffic. Since a single multicast
@@ -163,6 +184,85 @@ 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
Many Kubernetes network issues occur at a higher level than Weave Net.
The [Kubernetes Service Debugging Guide]
(https://kubernetes.io/docs/tasks/debug-application-cluster/debug-service/)
has a detailed step-by-step guide.
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 +295,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`
@@ -214,11 +323,14 @@ You can customise the YAML you get from `cloud.weave.works` by passing some of W
- `disable-npc`: boolean (`true|false`). Default: `false`.
- `env.NAME=VALUE`: add environment variable `NAME` and set it to `VALUE`.
- `seLinuxOptions.NAME=VALUE`: add SELinux option `NAME` and set it to `VALUE`, e.g. `seLinuxOptions.type=spc_t`
- `use-legacy-netpol`: use [legacy NetworkPolicy semantics](https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#networkpolicy-v1beta1-extensions), boolean (`true|false`). Default: `true` for Kubernetes version <= 1.6, `false` for > 1.6.
The list of variables you can set is:
* `CHECKPOINT_DISABLE` - if set to 1, disable checking for new Weave Net
versions (default is blank, i.e. check is enabled)
* `CONN_LIMIT` - soft limit on the number of connections between
peers. Defaults to 30.
* `HAIRPIN_MODE` - Weave Net defaults to enabling hairpin on the bridge side of
the `veth` pair for containers attached. If you need to disable hairpin, e.g. your
kernel is one of those that can panic if hairpin is enabled, then you can disable it
@@ -277,72 +389,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.
+3 -1
View File
@@ -50,7 +50,7 @@ peers and their reachability can be obtained with
```
$ weave status ipam
00:00:00:00:00:01(one) 349526 IPs (33.3% of total)
00:00:00:00:00:01(one) 349526 IPs (33.3% of total) (2 active)
00:00:00:00:00:02(two) 349525 IPs (33.3% of total)
00:00:00:00:00:03(three) 349525 IPs (33.3% of total) - unreachable!
```
@@ -59,6 +59,8 @@ Columns are as follows:
* Peer Name and Nickname
* Absolute quantity/percentage of allocation range managed by peer
* Active - number of IP addresses in use on that peer - this is only
available for the peer where you run the status command.
* Indication of unreachability. This means that the peer is not
visible (directly or indirectly) to the peer on which `weave status
ipam` was run; whilst this could be a transient condition due to a
+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.
+4
View File
@@ -43,6 +43,10 @@ To disable an application container's use of weaveDNS, add the
`--without-dns` option to `weave launch`. To
disable weaveDNS itself, launch weave with the `--no-dns` option.
> **Note** WeaveDNS is not part of the Weave Net Kubernetes add-on.
Kubernetes has its own DNS service, integrated with Kubernetes
Services, and WeaveDNS does not duplicate that functionality.
**See Also**
* [Integrating Docker via the API Proxy](/site/tasks/weave-docker-api/weave-docker-api.md)
+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
+59
View File
@@ -0,0 +1,59 @@
#! /bin/bash
. "$(dirname "$0")/config.sh"
C1=10.32.0.2
C2=10.32.0.3
kill_weaver() {
run_on $HOST1 sudo ip link set weave down
WEAVER_PID=$(container_pid $HOST1 weave)
run_on $HOST1 sudo kill -9 $WEAVER_PID
sleep 3
}
start_suite "Re-create bridge after restart"
# Should create a bridge of the "bridge" type.
WEAVE_NO_FASTDP=1 weave_on $HOST1 launch
WEAVE_NO_FASTDP=1 weave_on $HOST2 launch $HOST1
start_container $HOST1 $C1/24 --name=c1
start_container $HOST2 $C2/24 --name=c2
assert_raises "exec_on $HOST1 c1 $PING $C2"
kill_weaver # should re-create the bridge
assert_raises "exec_on $HOST1 c1 $PING $C2"
# Should create a bridge of the "bridged_fastdp" type
weave_on $HOST1 reset
weave_on $HOST1 launch $HOST2
weave_on $HOST1 attach $C1/24 c1
assert_raises "exec_on $HOST1 c1 $PING $C2"
kill_weaver # should re-create the bridge
assert_raises "exec_on $HOST1 c1 $PING $C2"
# test restore of iptables
weave_on $HOST1 reset
# `--expect-npc` to trigger creation of WEAVE-NPC iptables chain.
weave_on $HOST1 launch --expect-npc
# To create POSTROUTING rules.
weave_on $HOST1 expose
IPT_BEFORE=$(mktemp)
IPT_AFTER=$(mktemp)
run_on $HOST1 "sudo iptables-save | grep -i weave | grep -v '\[.*:.*\]' > $IPT_BEFORE"
run_on $HOST1 "sudo iptables -t filter -D FORWARD -o weave -m comment --comment \"NOTE: this must go before '-j KUBE-FORWARD'\" -j WEAVE-NPC"
run_on $HOST1 sudo iptables -t nat -D POSTROUTING -j WEAVE
kill_weaver # should re-create the bridge and iptables friends
# Rudimentary check that weave related iptables rules has been restored
run_on $HOST1 "sudo iptables-save | grep -i weave | grep -v '\[.*:.*\]' > $IPT_AFTER"
assert_raises "run_on $HOST1 diff $IPT_BEFORE $IPT_AFTER"
end_suite
+3 -3
View File
@@ -21,7 +21,7 @@ start_container_local_plugin $HOST1 --name=c3
assert "show_multicast_route_on $HOST1 c1"
assert "show_multicast_route_on $HOST1 c2"
assert "show_multicast_route_on $HOST1 c3" "224.0.0.0/4 dev ethwe0 "
assert "show_multicast_route_on $HOST1 c3" "224.0.0.0/4 dev ethwe0 scope link "
# Now try via docker network options
# using ssh rather than docker -H because CircleCI docker client is older
@@ -30,7 +30,7 @@ $SSH $HOST1 docker run --net=testmcastfalse --name c4 -di $SMALL_IMAGE /bin/sh
assert "show_multicast_route_on $HOST1 c4"
$SSH $HOST1 docker network create --driver weavemesh --ipam-driver weavemesh --opt works.weave.multicast testmcasttrue
$SSH $HOST1 docker run --net=testmcasttrue --name c5 -di $SMALL_IMAGE /bin/sh
assert "show_multicast_route_on $HOST1 c5" "224.0.0.0/4 dev ethwe0 "
assert "show_multicast_route_on $HOST1 c5" "224.0.0.0/4 dev ethwe0 scope link "
# Ensure current proxy options are obeyed on container start
docker_on $HOST1 stop -t 1 c2
@@ -39,7 +39,7 @@ weave_on $HOST1 launch
proxy docker_on $HOST1 start c2
assert "show_multicast_route_on $HOST1 c2" "224.0.0.0/4 dev ethwe "
assert "show_multicast_route_on $HOST1 c2" "224.0.0.0/4 dev ethwe scope link "
docker_on $HOST1 rm -f c3 c4 c5
$SSH $HOST1 docker network rm testmcasttrue testmcastfalse
+114 -4
View File
@@ -5,6 +5,10 @@
tear_down_kubeadm() {
for host in $HOSTS; do
run_on $host "sudo kubeadm reset && sudo rm -r -f /opt/cni/bin/*weave*"
# Remove the rule to ensure that api-server is not accessible while kube-proxy
# is not ready. Otherwise, we have a race between "-j KUBE-FORWARD" and "-j WEAVE-NPC"
# which makes the NodePort isolation test case to fail.
run_on $host "sudo iptables -t nat -D PREROUTING -m comment --comment \"kubernetes service portals\" -j KUBE-SERVICES" || true
done
}
@@ -18,7 +22,9 @@ NUM_HOSTS=$(howmany $HOSTS)
SUCCESS="$(( $NUM_HOSTS * ($NUM_HOSTS-1) )) established"
KUBECTL="sudo kubectl --kubeconfig /etc/kubernetes/admin.conf"
KUBE_PORT=6443
WEAVE_NETWORK=10.32.0.0/12
IMAGE=weaveworks/network-tester:latest
DOMAIN=nettest.default.svc.cluster.local.
tear_down_kubeadm
@@ -32,9 +38,9 @@ k8s_version_option="$([[ "$k8s_version" > "v1.6" ]] && echo "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"
run_on $host "sudo systemctl start kubelet && sudo kubeadm init --$k8s_version_option=$k8s_version --token=$TOKEN --pod-network-cidr=$WEAVE_NETWORK"
else
run_on $host "sudo systemctl start kubelet && sudo kubeadm join --token=$TOKEN $HOST1IP:$KUBE_PORT"
run_on $host "sudo systemctl start kubelet && sudo kubeadm join --token=$TOKEN --discovery-token-unsafe-skip-ca-verification $HOST1IP:$KUBE_PORT"
fi
done
@@ -47,7 +53,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 +101,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 +122,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
@@ -118,6 +139,22 @@ spec:
run: nettest
EOF
# And a NodePort service so we can test virtual IP access
run_on $HOST1 "$KUBECTL create -f -" <<EOF
apiVersion: v1
kind: Service
metadata:
name: netvirt
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 31138
selector:
run: nettest
EOF
podName=$($SSH $HOST1 "$KUBECTL get pods -l run=nettest -o go-template='{{(index .items 0).metadata.name}}'")
check_all_pods_communicate() {
@@ -135,6 +172,79 @@ 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"
# check access via virtual IP
VIRTUAL_IP=$($SSH $HOST1 $KUBECTL get service netvirt -o template --template={{.spec.clusterIP}})
assert_raises "$SSH $HOST1 $KUBECTL exec $podName -- curl -s -S -f -m 2 http://$VIRTUAL_IP/status >/dev/null"
assert_raises "! $SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$VIRTUAL_IP/status >/dev/null"
# host should not be able to reach pods via service virtual IP or NodePort
assert_raises "! $SSH $HOST1 curl -s -S -f -m 2 http://$VIRTUAL_IP/status >/dev/null"
assert_raises "! $SSH $HOST1 curl -s -S -f -m 2 http://$HOST2:31138/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"
# Virtual IP and NodePort should now work
assert_raises "$SSH $HOST1 curl -s -S -f -m 2 http://$VIRTUAL_IP/status >/dev/null"
assert_raises "$SSH $HOST1 curl -s -S -f -m 2 http://$HOST2:31138/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 --discovery-token-unsafe-skip-ca-verification $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
+80 -1
View File
@@ -11,8 +11,9 @@ 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
## Running tests locally using VMs
**TL;DR**: You can run the steps 2. to 7. below with one command:
@@ -22,46 +23,62 @@ it is recommended to run the tests from the build VM and not the host.
1. Start the build virtual machine (see above article for more details):
```
vagrant up
```
2. Start the three testing VMs:
```
cd test
vagrant up
```
3. SSH into the build VM and go to Weave Net's sources:
```
cd ..
vagrant ssh
# you are now on the build VM:
cd ~/weave
```
4. Compile all code and dependencies:
```
make
make testrunner
cd test
```
5. Upload the weave images from where the `Makefile` puts them (`weave.tar.gz`) to
the three docker hosts, `docker load` these, and copies the `weave` script over:
```
./setup.sh
```
6. Run individual tests, e.g.:
```
./200_dns_test.sh
```
or run all tests (everything named `*_test.sh`):
```
./run_all.sh
```
7. Stop all VMs:
```
exit
# you are now on your host machine
vagrant destroy -f
cd test
vagrant destroy -f
```
## Using other VMs
@@ -91,3 +108,65 @@ When a new version of Docker is released, you willneed to update the GCE test im
To do this, change the Docker version in `run-integration-tests.sh` and push the change.
Next build in CircleCI will detect that there is no template for this version of Docker and will first create the template before running tests.
Subsequent builds will then simply re-use the template.
## Running the tests on a cloud provider
Running the tests against a remote provider instead of local vagrant VMs is a little more involved, and reproduces the environment used for CI. It is recommended that you use the build VM to coordinate the build phase. Make sure you have a recent version of [ansible](https://github.com/ansible/ansible) installed, terraform will fail to provision the VMs.
1. Create the build VM in the repository root and log in
```
# On your development machine
$ vagrant up
$ vagrant ssh
```
2. Once in the build vm, install the `gcloud` tools as [per the documentation](https://cloud.google.com/sdk/downloads). You need to do this manually as the circleCI VMs are already provisioned with `gcloud`.
```
# On the build VM
$ curl https://sdk.cloud.google.com | bash
$ exec -l $SHELL
```
You don't need to initialise the `gcloud` tools by running `gcloud init`.
3. Export the secret key needed to decrypt the cloud credentials - This assumes you're using the build tools
```
# On the build VM
$ export SECRET_KEY="XXXXXXXXXX"
```
4. Navigate to the test directory and provision the VMs
```
# On the build VM
$ cd weave/test
/weave/test$ ./run-integration-tests.sh up
```
5. Run the tests you need
```
# On the build VM
/weave/test$ export TESTS="./090_docker_restart_policy_2_test.sh"
/weave/test$ ./run-integration-tests.sh test
```
6. Don't forget to close down your remote VMs when you're done
```
# On the build VM
/weave/test$ ./run-integration-tests.sh destroy
```
7. And switch off your build VM
```
# On the build VM
/weave/test$ exit
# On your development machine
$ vagrant destroy -f
# or "vagrant suspend" if you want to preserve the machine state
```
+2 -2
View File
@@ -25,7 +25,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.define hostname do |host|
host.vm.provider :virtualbox do |vb|
vb.name = hostname
vb.memory = 384
vb.memory = 2048
configure_nat_dns(vb)
end
@@ -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
+1 -1
Submodule tools updated: 74dc626b6d...ca69607f79.
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.
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff. Show more