Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20919fd6db | ||
|
|
ada2af04ff | ||
|
|
fcb6f762a9 | ||
|
|
5645f31934 | ||
|
|
a7295e2d03 | ||
|
|
c3e6ac2cac | ||
|
|
f1f3c8c8fe | ||
|
|
81b2f2e15a | ||
|
|
f6fdaf6492 | ||
|
|
519c5d686a | ||
|
|
f7e62ccbcc | ||
|
|
19161c0c92 | ||
|
|
c528303841 | ||
|
|
a439288765 | ||
|
|
416410912d | ||
|
|
b3f26f00e2 | ||
|
|
90e6ecdd11 | ||
|
|
842354bc5a | ||
|
|
4d3875cec2 | ||
|
|
74dfe49046 | ||
|
|
d0f23ed9e0 | ||
|
|
8a0992315e | ||
|
|
18b5cb10dc | ||
|
|
8afc6f208d | ||
|
|
d35d931179 | ||
|
|
093174e757 | ||
|
|
ac78762910 | ||
|
|
fff656f7e4 | ||
|
|
83b36e4c76 | ||
|
|
b42895b5c0 | ||
|
|
42e4b9addc | ||
|
|
91ab0e4a76 | ||
|
|
51feec3785 | ||
|
|
20eee3818e | ||
|
|
94cd6226ea | ||
|
|
5534ecf1b1 | ||
|
|
d498df3933 | ||
|
|
75bf62ca80 | ||
|
|
ad42357dd6 | ||
|
|
2de899342a | ||
|
|
8df48dc69d | ||
|
|
0f2809d2e9 | ||
|
|
aa44153af7 | ||
|
|
ce9c5162bd | ||
|
|
5c0da56ad5 | ||
|
|
08858c2a10 | ||
|
|
0268aeb25f | ||
|
|
ea3aba8971 | ||
|
|
265bfcf9d7 | ||
|
|
7a9fae5892 | ||
|
|
f66b0cc0aa | ||
|
|
e7f30106c0 | ||
|
|
86d2800b59 | ||
|
|
e65006042d | ||
|
|
65efc89b78 | ||
|
|
ae38834507 | ||
|
|
4261777613 | ||
|
|
7bc089f5c5 | ||
|
|
c46e3e8cf7 | ||
|
|
180d554205 | ||
|
|
a450d2e741 | ||
|
|
d528196033 | ||
|
|
0da2cbf84e | ||
|
|
20f981bc42 | ||
|
|
9a5514ffe0 | ||
|
|
439b4ad190 | ||
|
|
9a9c06966b | ||
|
|
b3f9baac8f | ||
|
|
bb961489b0 | ||
|
|
d6ae1f8375 | ||
|
|
5a1361167e | ||
|
|
4d5172f67e | ||
|
|
6f01c31458 | ||
|
|
6cfb208f3f | ||
|
|
3074892f1b | ||
|
|
b05d977cf9 | ||
|
|
36d183d988 | ||
|
|
b154fd56fb | ||
|
|
3419f69246 | ||
|
|
8e23301028 | ||
|
|
72724ee389 | ||
|
|
ae59a053b1 | ||
|
|
e71919226b | ||
|
|
6c5dd75216 | ||
|
|
23095728f5 | ||
|
|
498dacb4d2 | ||
|
|
b373a069e9 | ||
|
|
1d67e2f955 | ||
|
|
9a6935bf40 | ||
|
|
921ffefd90 | ||
|
|
a4fccd43eb | ||
|
|
48e1a8295b | ||
|
|
890c8c1905 | ||
|
|
38d50ce6fb | ||
|
|
14768d6947 | ||
|
|
b96399b563 | ||
|
|
d8c008dc7d | ||
|
|
b2ae5f4f4b | ||
|
|
ba871d7855 | ||
|
|
a0c8fc2ddb | ||
|
|
2a12053122 | ||
|
|
1860f21efb | ||
|
|
541adf6bb1 | ||
|
|
a71e90ddb8 | ||
|
|
60f4b3cdb8 | ||
|
|
0c249fa126 | ||
|
|
db1b57900b | ||
|
|
12b4898b3d | ||
|
|
f5f9bab77c | ||
|
|
ba7aa674d1 | ||
|
|
f670be4315 | ||
|
|
a658861fde | ||
|
|
192e5a1f79 | ||
|
|
475b15e372 | ||
|
|
d2ed91ccc8 | ||
|
|
333f4a1d3c | ||
|
|
832fe2f0b0 | ||
|
|
ea76185873 | ||
|
|
51c3ea184a | ||
|
|
692bccc4fc | ||
|
|
59fe76f29b | ||
|
|
51c2a0d732 | ||
|
|
2ab01b55c9 | ||
|
|
461f9e736b | ||
|
|
8e16b03ae8 | ||
|
|
6469809b9a | ||
|
|
0205adcedb | ||
|
|
0a0dbe115a | ||
|
|
ebbf3d4244 | ||
|
|
ceb36d1891 | ||
|
|
4caa850853 | ||
|
|
96cecf1796 | ||
|
|
51c7af8fb4 | ||
|
|
347466aba2 | ||
|
|
bee1c44859 | ||
|
|
a3fd36e2bb | ||
|
|
f07238c8bb | ||
|
|
3b499ce27c | ||
|
|
4dda216398 | ||
|
|
f0e511510a | ||
|
|
570ab0b10b | ||
|
|
214cf6cb2e | ||
|
|
07459f0cec | ||
|
|
90f4f7b62c | ||
|
|
987ac98fbf | ||
|
|
49ed40a301 | ||
|
|
c67bc704e1 | ||
|
|
3501ad45e6 | ||
|
|
70a13c9c12 | ||
|
|
3afb8d3ddf | ||
|
|
fd2f91e2ee | ||
|
|
cb0c9e5fba | ||
|
|
6a0e39e5ee | ||
|
|
a552f194b4 | ||
|
|
267bd8b51d | ||
|
|
497dddabc1 | ||
|
|
71b39489da | ||
|
|
ed8d92cfc6 | ||
|
|
f431ee289b | ||
|
|
7ea82204e0 | ||
|
|
44692b997c | ||
|
|
21086a0b2a | ||
|
|
d7119169df | ||
|
|
e1eb0a8617 | ||
|
|
cacc190764 | ||
|
|
89e5d935a6 | ||
|
|
884e1d7a6b | ||
|
|
5b1ca02ecc | ||
|
|
f51347feac | ||
|
|
b605b832ed | ||
|
|
e148b72284 | ||
|
|
16db921349 | ||
|
|
4c235b3648 | ||
|
|
1a8d479227 | ||
|
|
7aad364116 | ||
|
|
4958d9e423 | ||
|
|
474f1f3673 | ||
|
|
9aa7044f1e | ||
|
|
7c39f96d08 | ||
|
|
3c3e0b0352 | ||
|
|
8467742d6d | ||
|
|
5cfc3f8c47 | ||
|
|
62dbe03193 | ||
|
|
8755da4b5a | ||
|
|
25cff16bd1 | ||
|
|
ade6738789 | ||
|
|
ea0e3a14e0 | ||
|
|
4a1a1e83c8 | ||
|
|
ee48700b40 | ||
|
|
0791ce8ab0 | ||
|
|
1ac543aba7 | ||
|
|
3d5f80630d | ||
|
|
be2da07735 | ||
|
|
276d0b621b | ||
|
|
6733dac9fc | ||
|
|
80b286ebf7 | ||
|
|
2c143612e2 | ||
|
|
be412812f9 | ||
|
|
bfed7c24f8 | ||
|
|
fe859952e0 | ||
|
|
86361c5b60 | ||
|
|
35c017a787 | ||
|
|
79d65bc98b | ||
|
|
6bc69199d3 | ||
|
|
292cbc484a | ||
|
|
1a98cbfff9 | ||
|
|
071f077cc5 | ||
|
|
c16653a396 | ||
|
|
b157544394 | ||
|
|
18f65a1914 | ||
|
|
ba16135b3a | ||
|
|
df688cfb53 | ||
|
|
88679d66fc | ||
|
|
ab1cbcbbf8 | ||
|
|
a02fb753df | ||
|
|
98b002f7c0 | ||
|
|
e251bfe807 | ||
|
|
eb88893312 | ||
|
|
a362bf68bb | ||
|
|
3c53aa66b7 | ||
|
|
c97d4731dc | ||
|
|
a69546c9ee | ||
|
|
b0f78e97f4 | ||
|
|
49f7e38133 | ||
|
|
2e7f44feee | ||
|
|
48ceb49832 | ||
|
|
dd772f04fe | ||
|
|
47597c941d | ||
|
|
c0e8af56e2 | ||
|
|
72c0172c48 | ||
|
|
507f5345c5 | ||
|
|
79d4aec1ec | ||
|
|
89c7e6d337 | ||
|
|
b8d7af3822 | ||
|
|
3c9c968174 | ||
|
|
38ca0f90c7 | ||
|
|
8bf08c3cf8 | ||
|
|
14c79d2184 | ||
|
|
b0519af573 | ||
|
|
dc7acd6c70 | ||
|
|
d63a21363e | ||
|
|
a330dd4970 | ||
|
|
c0388c0136 | ||
|
|
aeca987a4c | ||
|
|
3aff5f2467 | ||
|
|
0c63a8d0e5 | ||
|
|
65b45e8dd1 | ||
|
|
03885d4b40 | ||
|
|
40e78fea99 | ||
|
|
ecba88e445 | ||
|
|
29c9b2c064 | ||
|
|
ff1cd7c008 | ||
|
|
4b720bffdf | ||
|
|
5c210c90b5 | ||
|
|
2ede1066e9 |
No files matched your search
@@ -33,6 +33,9 @@ coverage.html
|
||||
# Vim backup files
|
||||
*.swp
|
||||
|
||||
# Sed backup files
|
||||
*.bak
|
||||
|
||||
# Project specific
|
||||
prog/weaver/weaver
|
||||
prog/weavedns/weavedns
|
||||
|
||||
+45
@@ -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
|
||||
+182
@@ -1,3 +1,185 @@
|
||||
## Release 2.3.0
|
||||
|
||||
### Security fixes
|
||||
|
||||
* By default, do not expose Weave "/status" and "/report" to all (0.0.0.0) when
|
||||
running on Kubernetes #3271
|
||||
|
||||
### Other improvements
|
||||
|
||||
* Increase the default connection limit for Weave peers (from 30 to 100) when
|
||||
running on Kubernetes, so that more peers could directly connect #3265
|
||||
|
||||
## Build and test
|
||||
|
||||
* Build Weave Net with Go 1.10.1 #3273
|
||||
* Run integration tests against Kubernetes 1.10.0 #3266
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/70?closed=1)
|
||||
|
||||
## Release 2.2.1
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fix a bug in weave-npc which would allow ingress traffic to Kubernetes Pods selected
|
||||
by a NetworkPolicy in which source and destination selectors were the same #3222,#3237
|
||||
* Fix a bug in weave-npc which would crash if a previously deleted Kubernetes Namespace
|
||||
has been created again #3247,#3250
|
||||
|
||||
### Other improvements
|
||||
|
||||
* Increase the default connection limit for Weave peers (from 30 to 100), so that
|
||||
more peers could directly connect #3234
|
||||
* When doing a rolling update of Weave Net on Kubernetes, allow each node five seconds
|
||||
to initialize before rolling next Weave Net Pod, so that issues at startup will halt
|
||||
the rollout and not spread across the whole cluster #3235
|
||||
* Install common CA certificates from Alpine Linux package instead of copying
|
||||
them manually #3236
|
||||
|
||||
### External contributors
|
||||
|
||||
Thanks to the following contributors:
|
||||
|
||||
* @alok87
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/71?closed=1)
|
||||
|
||||
## 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
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
DOCKER_VERSION=17.06
|
||||
KUBERNETES_VERSION=1.6.2
|
||||
KUBERNETES_CNI_VERSION=0.5.1
|
||||
KUBERNETES_VERSION=1.10.0
|
||||
KUBERNETES_CNI_VERSION=0.6.0
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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"
|
||||
|
||||
+7
-4
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.8.1
|
||||
FROM golang:1.10.1-stretch
|
||||
|
||||
# Support Raspberry Pi 2 and newer
|
||||
ENV GOARM 7
|
||||
@@ -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 +0,0 @@
|
||||
8f313a9df867c207ae9ceac6e0935d1d2a0360aafce780dd5d1e0687ee98e0c3 docker-1.10.3.tgz
|
||||
@@ -1 +0,0 @@
|
||||
7541b5490cee1183d911f909c0e91d3d9d57b2b775e40f14a1a7a6de216ca4a8 docker-1.10.3.tgz
|
||||
@@ -0,0 +1 @@
|
||||
926f7f265332076ea4abf6e9586288376dbed2322c155b8d0cafbee614aaa7f2 qemu-ppc64le-static
|
||||
@@ -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
|
||||
}
|
||||
+24
-2
@@ -127,14 +127,36 @@ Finally, for **Mainline** releases only:
|
||||
|
||||
* Images tagged `latest` are updated on DockerHub
|
||||
|
||||
### Docker Store
|
||||
|
||||
Weave Net is [available in Docker Store](https://store.docker.com/plugins/weave-net-plugin) and should also be released there.
|
||||
|
||||
* Go to https://store.docker.com/ and log in.
|
||||
* Go to "[_Publish_](https://store.docker.com/publisher/center)".
|
||||
* Under "_My Products_", select "_owners (weaveworks)_". Weave Net should be listed among our products.
|
||||
* Click "_Actions_" > "_Edit Product_".
|
||||
* Go to "_Plans & Pricing_" > "_Free Tier_".
|
||||
* Under "_Source Repositories & Tags_", click on "_Add Source_", select "_net-plugin_" under "_Repository_" and the version to release under "_Tag_".
|
||||
* Under "_Resources_" > "_Installation Instructions_", update the lines containing `export NET_VERSION=<version>`.
|
||||
* Click "_Submit For Review_". You should see a message like: "_Your product has been submitted for approval. [...] We'll be in touch with next steps soon!_".
|
||||
* You should receive an email saying:
|
||||
|
||||
> This email confirms that we received your submission on \<date\> of Weave Net to the Docker Store.
|
||||
>
|
||||
> We're reviewing your submission to ensure that it meets our [security guidelines](https://success.docker.com/Store#Security_and_Audit_Policies) and complies with our [best practices](https://success.docker.com/Store#Create_Great_Content). Don't worry! We'll let you know if there's anything you need to change before we publish your submission. You should hear back from us within the next 14 days.
|
||||
>
|
||||
> Thanks for submitting your content to the Docker Store!
|
||||
|
||||
* Hope Docker eventually performs the release or contacts us. If nothing happens within 14 days, contact their support team: publisher-support@docker.com.
|
||||
|
||||
### Finish up
|
||||
|
||||
* If not on master, merge branch into master and push to GitHub.
|
||||
* 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
|
||||
|
||||
@@ -117,7 +117,7 @@ func CheckAllExpectedMessagesSent(allocs ...*Allocator) {
|
||||
m := alloc.gossip.(*mockGossipComms)
|
||||
m.RLock()
|
||||
if len(m.messages) > 0 {
|
||||
require.FailNow(m, fmt.Sprintf("%s: Gossip message(s) not sent as expected: \n%x", m.name, m.messages))
|
||||
require.FailNow(m, fmt.Sprintf("%s: Gossip message(s) not sent as expected: \n%v", m.name, m.messages))
|
||||
}
|
||||
m.RUnlock()
|
||||
}
|
||||
|
||||
+1
-4
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
+133
-29
@@ -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,85 @@ 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
|
||||
}
|
||||
|
||||
// To prevent dstSelector being overwritten by a subsequent selector with
|
||||
// the same key, addIfNotExist MUST be used when adding to podSelectors.
|
||||
// Otherwise, dstSelector' "dst" property might be lost resulting in
|
||||
// an invalid content of the "default-allow" ipset.
|
||||
addIfNotExist(dstSelector, podSelectors)
|
||||
|
||||
// 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
|
||||
}
|
||||
addIfNotExist(srcSelector, podSelectors)
|
||||
|
||||
} 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 +173,46 @@ func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
|
||||
return rules, nsSelectors, podSelectors, nil
|
||||
}
|
||||
|
||||
func withNormalisedProtoAndPort(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)
|
||||
func addIfNotExist(s *selectorSpec, ss map[string]*selectorSpec) {
|
||||
if _, ok := ss[s.key]; !ok {
|
||||
ss[s.key] = s
|
||||
}
|
||||
}
|
||||
|
||||
func withNormalisedProtoAndPortLegacy(npps []extnapi.NetworkPolicyPort, f func(proto, port string)) {
|
||||
for _, npp := range npps {
|
||||
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
@@ -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
|
||||
}
|
||||
+310
-36
@@ -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,278 @@ 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))
|
||||
|
||||
controller.DeleteNamespace(defaultNamespace)
|
||||
// Should remove default ipset
|
||||
require.NotContains(t, m.sets, defaultAllowIPSetName)
|
||||
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
// Test case for https://github.com/weaveworks/weave/issues/3222
|
||||
func TestNewDstSelector(t *testing.T) {
|
||||
const (
|
||||
defaultAllowIPSetName = "weave-E.1.0W^NGSp]0_t5WwH/]gX@L"
|
||||
podIP = "10.32.0.10"
|
||||
)
|
||||
|
||||
m := newMockIPSet()
|
||||
controller := New("baz", 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)
|
||||
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
|
||||
|
||||
netpolBar := &networkingv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "netpol-bar",
|
||||
Name: "allow-from-default-1",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
PodSelector: metav1.LabelSelector{},
|
||||
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||
From: []networkingv1.NetworkPolicyPeer{{
|
||||
PodSelector: &metav1.LabelSelector{},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
}
|
||||
controller.AddNetworkPolicy(netpolBar)
|
||||
|
||||
// netpolBar dst selector selects podFoo
|
||||
require.False(t, m.entryExists(defaultAllowIPSetName, podIP))
|
||||
|
||||
netpolFoo := &networkingv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "netpol-foo",
|
||||
Name: "allow-from-default-2",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
PodSelector: metav1.LabelSelector{},
|
||||
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||
From: []networkingv1.NetworkPolicyPeer{{
|
||||
PodSelector: &metav1.LabelSelector{},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
}
|
||||
controller.AddNetworkPolicy(netpolFoo)
|
||||
|
||||
controller.DeleteNetworkPolicy(netpolBar)
|
||||
// netpolFoo dst-selects podFoo
|
||||
require.False(t, m.entryExists(defaultAllowIPSetName, podIP))
|
||||
controller.DeleteNetworkPolicy(netpolFoo)
|
||||
// No netpol dst-selects podFoo
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
|
||||
}
|
||||
+109
-51
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+295
-108
@@ -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,46 @@ 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))
|
||||
err := ns.nsSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ns.legacy {
|
||||
return ns.ips.Destroy(ns.defaultAllowIPSet)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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 +524,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 +531,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
|
||||
}
|
||||
+1
-1
@@ -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"
|
||||
|
||||
+72
-28
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,6 @@ import (
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
|
||||
const (
|
||||
WeaveDomain = "weave.local"
|
||||
)
|
||||
|
||||
type watcher struct {
|
||||
client *docker.Client
|
||||
weave *weaveapi.Client
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,14 @@
|
||||
}
|
||||
],
|
||||
"env": [
|
||||
{
|
||||
"description": "Log level",
|
||||
"name": "LOG_LEVEL",
|
||||
"settable": [
|
||||
"value"
|
||||
],
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"description": "Extra args to `weaver` and `plugin`",
|
||||
"name": "EXTRA_ARGS",
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -27,9 +27,10 @@ xt_set_exists() {
|
||||
# Default if not supplied - same as weave net default
|
||||
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}
|
||||
METRICS_ADDR=${WEAVE_METRICS_ADDR:-0.0.0.0:6782}
|
||||
HOST_ROOT=${HOST_ROOT:-/host}
|
||||
CONN_LIMIT=${CONN_LIMIT:-30}
|
||||
CONN_LIMIT=${CONN_LIMIT:-100}
|
||||
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
|
||||
@@ -53,6 +54,12 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
STATUS_OPTS="--metrics-addr=$METRICS_ADDR"
|
||||
# --status-addr exposes internal information, so only turn it on if asked to.
|
||||
if [ -n "${WEAVE_STATUS_ADDR}" ]; then
|
||||
STATUS_OPTS="$STATUS_OPTS --status-addr=$WEAVE_STATUS_ADDR"
|
||||
fi
|
||||
|
||||
# Default is that npc will be running; allow caller to override
|
||||
WEAVE_NPC_OPTS="--expect-npc"
|
||||
if [ "${EXPECT_NPC}" = "0" ]; then
|
||||
@@ -87,6 +94,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 +138,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 +148,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" \
|
||||
--http-addr=$HTTP_ADDR $STATUS_OPTS --docker-api='' --no-dns \
|
||||
--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,201 @@
|
||||
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:
|
||||
# Wait 5 seconds to let pod connect before rolling next pod
|
||||
minReadySeconds: 5
|
||||
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
@@ -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())
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -21,11 +21,11 @@ RUN apk add --update \
|
||||
util-linux \
|
||||
conntrack-tools \
|
||||
bind-tools \
|
||||
ca-certificates \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
ADD ./weave ./weaver /home/weave/
|
||||
ADD ./weaveutil /usr/bin/
|
||||
ADD weavedata.db /weavedb/
|
||||
COPY ca-certificates.crt /etc/ssl/certs/
|
||||
ENTRYPOINT ["/home/weave/weaver"]
|
||||
WORKDIR /home/weave
|
||||
File diff suppressed because it is too large.
Load diff
+20
-17
@@ -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 {
|
||||
|
||||
+49
-29
@@ -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
|
||||
@@ -146,6 +155,7 @@ func main() {
|
||||
noDiscovery bool
|
||||
httpAddr string
|
||||
statusAddr string
|
||||
metricsAddr string
|
||||
ipamConfig ipamConfig
|
||||
dockerAPI string
|
||||
peers []string
|
||||
@@ -163,6 +173,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")
|
||||
@@ -176,12 +187,13 @@ func main() {
|
||||
mflag.StringVar(&logLevel, []string{"-log-level"}, "info", "logging level (debug, info, warning, error)")
|
||||
mflag.BoolVar(&pktdebug, []string{"-pkt-debug"}, false, "enable per-packet debug logging")
|
||||
mflag.StringVar(&prof, []string{"-profile"}, "", "enable profiling and write profiles to given path")
|
||||
mflag.IntVar(&config.ConnLimit, []string{"-conn-limit"}, 30, "connection limit (0 for unlimited)")
|
||||
mflag.IntVar(&config.ConnLimit, []string{"-conn-limit"}, 100, "connection limit (0 for unlimited)")
|
||||
mflag.BoolVar(&noDiscovery, []string{"-no-discovery"}, false, "disable peer discovery")
|
||||
mflag.IntVar(&bufSzMB, []string{"-bufsz"}, 8, "capture buffer size in MB")
|
||||
mflag.IntVar(&bridgeConfig.MTU, []string{"-mtu"}, 0, "MTU size")
|
||||
mflag.StringVar(&httpAddr, []string{"-http-addr"}, "", "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||
mflag.StringVar(&statusAddr, []string{"-status-addr"}, "", "address to bind status+metrics interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||
mflag.StringVar(&metricsAddr, []string{"-metrics-addr"}, "", "address to bind metrics interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||
mflag.StringVar(&ipamConfig.Mode, []string{"-ipalloc-init"}, "", "allocator initialisation strategy (consensus, seed or observer)")
|
||||
mflag.StringVar(&ipamConfig.IPRangeCIDR, []string{"-ipalloc-range"}, "", "IP address range reserved for automatic allocation, in CIDR notation")
|
||||
mflag.StringVar(&ipamConfig.IPSubnetCIDR, []string{"-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
|
||||
@@ -191,10 +203,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 +222,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 +238,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 +273,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 +303,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 +341,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)
|
||||
|
||||
@@ -457,10 +476,17 @@ func main() {
|
||||
muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver))
|
||||
statusMux := http.NewServeMux()
|
||||
statusMux.Handle("/", muxRouter)
|
||||
Log.Println("Listening for metrics requests on", statusAddr)
|
||||
Log.Println("Listening for status+metrics requests on", statusAddr)
|
||||
go listenAndServeHTTP(statusAddr, statusMux)
|
||||
}
|
||||
|
||||
if metricsAddr != "" {
|
||||
metricsMux := http.NewServeMux()
|
||||
metricsMux.Handle("/metrics", metricsHandler(router, allocator, ns, dnsserver))
|
||||
Log.Println("Listening for metrics requests on", metricsAddr)
|
||||
go listenAndServeHTTP(metricsAddr, metricsMux)
|
||||
}
|
||||
|
||||
if plugin != nil {
|
||||
go plugin.Start(httpAddr, dockerCli, waitReady.Add())
|
||||
}
|
||||
@@ -469,17 +495,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 +551,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 +654,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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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{
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+17
-15
@@ -6,27 +6,28 @@ search_type: Documentation
|
||||
You only need to build Weave Net if you want to work on the Weave Net codebase
|
||||
(or you just enjoy building software).
|
||||
|
||||
Apart from the `weave` shell script, weave is delivered as a set of
|
||||
Apart from the `weave` shell script, Weave Net is delivered as a set of
|
||||
container images. There is no distribution-specific packaging, so in
|
||||
principle it shouldn't matter which Linux distribution you build
|
||||
under. But naturally, Docker is a prerequisite (version 1.6.0 or
|
||||
later).
|
||||
|
||||
The only way to build is by using the the build container; the
|
||||
Makefile is setup to make this transparent. This method is
|
||||
`Makefile` is setup to make this transparent. This method is
|
||||
documented below, and can be run directly on your machine or using
|
||||
a Vagrant VM.
|
||||
|
||||
## <a name="ubuntu"></a>Building directly on your machine
|
||||
|
||||
The weave git repository should be cloned into
|
||||
`$GOPATH/src/github.com/weaveworks/weave`, in accordance with [the go
|
||||
`$GOPATH/src/github.com/weaveworks/weave`, in accordance with [the Go
|
||||
workspace conventions](https://golang.org/doc/code.html#Workspaces):
|
||||
|
||||
```
|
||||
$ WEAVE=github.com/weaveworks/weave
|
||||
$ git clone https://$WEAVE $GOPATH/src/$WEAVE
|
||||
$ cd $GOPATH/src/$WEAVE
|
||||
$ git submodule update --init
|
||||
```
|
||||
|
||||
Next install Docker if you haven't already, by following the instructions
|
||||
@@ -44,7 +45,7 @@ On a fresh repository, the Makefile will do the following:
|
||||
- build the weave components in the build container
|
||||
- package them into two Docker images (`weaveworks/weave`,
|
||||
`weaveworks/weaveexec`)
|
||||
- Exported these images as `weave.tar.gz`
|
||||
- export these images as `weave.tar.gz`.
|
||||
|
||||
The first two steps may take a while - don't worry, they are
|
||||
are cached and should not need to be redone very often.
|
||||
@@ -63,18 +64,19 @@ First, check out the code:
|
||||
```
|
||||
$ git clone https://github.com/weaveworks/weave
|
||||
$ cd weave
|
||||
$ git submodule update --init
|
||||
```
|
||||
|
||||
The `Vagrantfile` in the top directory constructs a VM that has
|
||||
The `Vagrantfile` in the top directory constructs a VM that has:
|
||||
|
||||
* docker installed
|
||||
* go tools installed
|
||||
* weave dependencies installed
|
||||
* $GOPATH set to ~
|
||||
* Docker installed
|
||||
* Go tools installed
|
||||
* Weave's dependencies installed
|
||||
* `$GOPATH` set to `~`
|
||||
* the local working directory mapped as a synced folder into the
|
||||
right place in $GOPATH
|
||||
right place in `$GOPATH`.
|
||||
|
||||
Once you are in the working directory you can issue
|
||||
Once you are in the working directory you can issue:
|
||||
|
||||
```
|
||||
$ vagrant up
|
||||
@@ -86,7 +88,7 @@ installation is done just once). The working directory is sync'ed with
|
||||
use git and so on in the regular filesystem.
|
||||
|
||||
To build and run the code, you need to use the VM. To log in and build
|
||||
the weave image, do
|
||||
the weave image, do:
|
||||
|
||||
```
|
||||
$ vagrant ssh
|
||||
@@ -94,7 +96,7 @@ vm$ cd src/github.com/weaveworks/weave
|
||||
vm$ make
|
||||
```
|
||||
|
||||
The Docker daemon is also running in this VM, so you can then do
|
||||
The Docker daemon is also running in this VM, so you can then do:
|
||||
|
||||
```
|
||||
vm$ sudo ./weave launch
|
||||
@@ -104,7 +106,7 @@ vm$ sudo docker ps
|
||||
and so on.
|
||||
|
||||
If you are looking to just do a build and not run anything on this VM,
|
||||
you can do so with
|
||||
you can do so with:
|
||||
|
||||
```
|
||||
$ vagrant ssh -c 'make -C src/github.com/weaveworks/weave'
|
||||
@@ -112,7 +114,7 @@ $ vagrant ssh -c 'make -C src/github.com/weaveworks/weave'
|
||||
|
||||
you should then find a `weave.tar.gz` container snapshot tarball in the
|
||||
top-level directory. You can use that snapshot with `docker load`
|
||||
against a different host, e.g.
|
||||
against a different host, e.g.:
|
||||
|
||||
```
|
||||
$ export DOCKER_HOST=tcp://<HOST:PORT>
|
||||
|
||||
@@ -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
|
||||
|
||||
+12
@@ -40,6 +40,18 @@ A simple way to accomplish this would be to run Weave on the host and then run,
|
||||
Yet another option is to expose a port from the container on host B and then connect to it. You can read about exposing ports in [Exporting Services](/site/tasks/manage/service-management.md#exporting).
|
||||
|
||||
|
||||
<a name="duplicate-peer"></a>
|
||||
**Q: Why am I seeing "peer names collision" and failed connections?**
|
||||
|
||||
This sometimes happens when machines are cloned; we require each
|
||||
machine in your cluster to have a unique identity.
|
||||
|
||||
For more information see [Peer Names](/site/operational-guide/concepts.md#peer-name).
|
||||
|
||||
Depending on your Linux distribution you may need to [set up
|
||||
machine-id](https://www.freedesktop.org/software/systemd/man/machine-id.html)
|
||||
or [generate a dbus id](https://dbus.freedesktop.org/doc/dbus-uuidgen.1.html).
|
||||
|
||||
<a name="duplicate-ip"></a>
|
||||
**Q: Why am I seeing the same IP address assigned to two different containers on different hosts?**
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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)
|
||||
+130
-83
@@ -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.
|
||||
|
||||

|
||||
|
||||
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,18 +295,27 @@ 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`
|
||||
|
||||
You can customise the YAML you get from `cloud.weave.works` by passing some of Weave Net's options, arguments and environment variables as query parameters:
|
||||
|
||||
- `version`: Weave Net's version. Default: `latest`, i.e. latest release. *N.B.*: This only changes the specified version inside the generated YAML file, it does not ensure that the rest of the YAML is compatible with that version. To freeze the YAML version save a copy of the YAML file from the [release page](https://github.com/weaveworks/weave/releases) and use that copy instead of downloading it each time from `cloud.weave.works`.
|
||||
- `password-secret`: name of the Kubernetes secret containing your password.
|
||||
- `password-secret`: name of the Kubernetes secret containing your password. *N.B*: The Kubernetes secret name must correspond to a name of a file containing your password.
|
||||
Example:
|
||||
|
||||
$ echo "s3cr3tp4ssw0rd" > /var/lib/weave/weave-passwd.txt
|
||||
$ kubectl create secret -n kube-system generic weave-passwd --from-file=/var/lib/weave/weave-passwd.txt
|
||||
$ echo "s3cr3tp4ssw0rd" > /var/lib/weave/weave-passwd
|
||||
$ kubectl create secret -n kube-system generic weave-passwd --from-file=/var/lib/weave/weave-passwd
|
||||
$ kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')&password-secret=weave-passwd"
|
||||
|
||||
- `known-peers`: comma-separated list of hosts. Default: empty.
|
||||
@@ -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
|
||||
@@ -234,6 +346,10 @@ The list of variables you can set is:
|
||||
* `WEAVE_EXPOSE_IP` - set the IP address used as a gateway from the
|
||||
Weave network to the host network - this is useful if you are
|
||||
configuring the addon as a static pod.
|
||||
* `WEAVE_METRICS_ADDR` - address and port that the Weave Net
|
||||
daemon will serve Prometheus-style metrics on (defaults to 0.0.0.0:6782)
|
||||
* `WEAVE_STATUS_ADDR` - address and port that the Weave Net
|
||||
daemon will serve status requests on (defaults to disabled)
|
||||
* `WEAVE_MTU` - Weave Net defaults to 1376 bytes, but you can set a
|
||||
smaller size if your underlying network has a tighter limit, or set
|
||||
a larger size for better performance if your network supports jumbo
|
||||
@@ -277,72 +393,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 |
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,9 +10,6 @@ controller](/site/kubernetes/kube-addon.md#npc).
|
||||
|
||||
### Router Metrics
|
||||
|
||||
The endpoint address is `localhost:6782`; the following metrics are
|
||||
exposed:
|
||||
|
||||
* `weave_connections` - Number of peer-to-peer connections.
|
||||
* `weave_connection_terminations_total` - Number of peer-to-peer
|
||||
connections terminated.
|
||||
@@ -20,30 +17,40 @@ 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.
|
||||
|
||||
#### Publish Router Metrics Endpoint
|
||||
|
||||
By default, when started via `weave launch`, weave listens on its local
|
||||
interface to serve metrics. To publish your metrics throughout your cluster,
|
||||
e.g. if your prometheus server is installed on a different host machine,
|
||||
you need to set `WEAVE_STATUS_ADDR` to your corresponding IP and port.
|
||||
Default port is 6782.
|
||||
|
||||
`WEAVE_STATUS_ADDR=X.X.X.X:PORT`
|
||||
|
||||
You can set `WEAVE_STATUS_ADDR=0.0.0.0:6782` to listen on all interfaces,
|
||||
but be aware, this may expose your metrics to the public internet.
|
||||
|
||||
### Kubernetes Network Policy Controller Metrics
|
||||
|
||||
The endpoint address is `localhost:6781`; the following metric is
|
||||
The following metric is
|
||||
exposed:
|
||||
|
||||
* `weavenpc_blocked_connections_total` - Connection attempts blocked
|
||||
by policy controller.
|
||||
|
||||
### Metrics Endpoint Addresses
|
||||
|
||||
When installed as a Kubernetes Addon, the router listens for metrics
|
||||
requests on 0.0.0.0:6782 and the Network Policy Controller listens on
|
||||
0.0.0.0:6781. No other requests are served on these endpoints.
|
||||
|
||||
>Note: If your Kubernetes hosts are exposed to the public internet
|
||||
then these metrics endpoints will also be exposed.
|
||||
|
||||
When started via `weave launch`, by default weave listens on its local
|
||||
interface to serve metrics and other read-only status requests. To
|
||||
publish your metrics throughout your cluster, you can set
|
||||
`WEAVE_STATUS_ADDR`:
|
||||
|
||||
`WEAVE_STATUS_ADDR=X.X.X.X:PORT`
|
||||
|
||||
Set it to an empty string to disable.
|
||||
|
||||
You can also pass the parameter `--metrics-addr=X.X.X.X:PORT` to
|
||||
`weave launch` to specify an address to listen for metrics only.
|
||||
|
||||
# Static Configuration for Weave Net
|
||||
|
||||
The following YAML fragment can be used in your Prometheus configuration to
|
||||
|
||||
@@ -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
@@ -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
|
||||
Executable
+59
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -28,13 +34,12 @@ docker_on $HOST1 run --rm --privileged --net=host --entrypoint=/usr/sbin/ipset w
|
||||
|
||||
# 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"
|
||||
run_on $host "sudo systemctl start kubelet && sudo kubeadm init --kubernetes-version=$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 +52,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
|
||||
|
||||
@@ -115,7 +121,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
|
||||
@@ -132,6 +138,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() {
|
||||
@@ -152,6 +174,76 @@ 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,116 @@
|
||||
#! /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]+")"
|
||||
|
||||
for host in $HOSTS; do
|
||||
if [ $host = $HOST1 ] ; then
|
||||
run_on $host "sudo systemctl start kubelet && sudo kubeadm init --kubernetes-version=$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
@@ -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
|
||||
```
|
||||
Vendored
+2
-2
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -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
@@ -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.
+1
-1
@@ -16,7 +16,7 @@ def ansibleize(h)
|
||||
end
|
||||
|
||||
def get_go_version_from_build_dockerfile()
|
||||
go_regexp = /FROM golang:(\S*).*?/
|
||||
go_regexp = /FROM golang:([\d\.]*).*?/
|
||||
dockerfile_path = File.expand_path(File.join(File.dirname(__FILE__), 'build', 'Dockerfile'))
|
||||
go_version = File.readlines(dockerfile_path).select { |line| line.match(go_regexp) }.first.match(go_regexp).captures.first
|
||||
if go_version.nil?
|
||||
|
||||
+1
Submodule vendor/github.com/PuerkitoBio/purell added at fd18e053af.
+1
Submodule vendor/github.com/PuerkitoBio/urlesc added at de5bf2ad45.
+1
Submodule vendor/github.com/emicklei/go-restful added at dc0f94ee75.
+1
Submodule vendor/github.com/go-openapi/jsonpointer added at 779f45308c.
+1
Submodule vendor/github.com/go-openapi/jsonreference added at 36d33bfe51.
+1
Submodule vendor/github.com/go-openapi/spec added at 48c2a71855.
+1
Submodule vendor/github.com/go-openapi/swag added at f3f9494671.
+1
Submodule vendor/github.com/golang/glog added at 23def4e6c1.
+1
Submodule vendor/github.com/google/gofuzz added at 24818f796f.
+1
-1
Submodule vendor/github.com/google/gopacket updated: e732efe9f4...92f75c8d3f.
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user