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

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