Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
022a98d4a4 | ||
|
|
1d80a8d676 | ||
|
|
aa9c032176 | ||
|
|
baa8f229f7 | ||
|
|
ab1b99a4d9 | ||
|
|
b03311e99d | ||
|
|
d1712e9589 | ||
|
|
597c6c111d | ||
|
|
f3915592b2 | ||
|
|
d4367457a9 | ||
|
|
3674808244 | ||
|
|
44cc978a94 | ||
|
|
f441ec2644 | ||
|
|
0ae13a2650 | ||
|
|
ee6596e21c | ||
|
|
a63147beda | ||
|
|
46e4f047de | ||
|
|
5f031453d3 | ||
|
|
028216021f | ||
|
|
d6544302d3 | ||
|
|
354d1ddd5c | ||
|
|
fd34dda6c1 | ||
|
|
f989a82eb0 | ||
|
|
d410ecd34c | ||
|
|
48569265e2 | ||
|
|
71782a6aa5 | ||
|
|
e283b0a1ce | ||
|
|
fa15193011 | ||
|
|
01c436ae15 | ||
|
|
664ba8735f | ||
|
|
937036aa64 | ||
|
|
47cdf2b755 | ||
|
|
a6edd40de3 | ||
|
|
4219c2c2ab | ||
|
|
e7bde18f71 | ||
|
|
233a776c41 | ||
|
|
9cc98919fa | ||
|
|
e42600632f | ||
|
|
49e76d1486 | ||
|
|
965bed7c4f | ||
|
|
9b606fb823 | ||
|
|
15d91c558b | ||
|
|
ffc5a78139 | ||
|
|
f8693074a3 | ||
|
|
1629f4f3f0 | ||
|
|
10baab38d3 | ||
|
|
e458c11e66 | ||
|
|
a07e3301b1 | ||
|
|
2b4d13c5b1 | ||
|
|
ad743b0e96 | ||
|
|
0544901357 | ||
|
|
bc94633180 | ||
|
|
16415404c7 | ||
|
|
46eb01604c | ||
|
|
b03b3fccc7 | ||
|
|
9414e2979d | ||
|
|
865664425b | ||
|
|
b060cf30f4 | ||
|
|
5352ed837a | ||
|
|
d4d54a1ff5 | ||
|
|
ea82a6715f | ||
|
|
d356e32bdf | ||
|
|
6193d22d2e | ||
|
|
50c9568073 | ||
|
|
9d05ed41b2 | ||
|
|
e3f7e0a23a | ||
|
|
da2f0803ce | ||
|
|
6dde5e5ee9 | ||
|
|
494df21bdf | ||
|
|
e44bbbec63 | ||
|
|
c19f49a284 | ||
|
|
d3f2c110e4 | ||
|
|
09628178e4 | ||
|
|
5630a01a8c | ||
|
|
40b3a49c90 | ||
|
|
431b87b94a | ||
|
|
828c3d21d2 | ||
|
|
f0e75481d5 | ||
|
|
8bb7e1cf24 | ||
|
|
e117a6bd04 | ||
|
|
ac44fa63c8 | ||
|
|
bb639dbf32 | ||
|
|
64c890005a | ||
|
|
19088ba8bc | ||
|
|
ec5f7033a4 | ||
|
|
eb60f68df2 | ||
|
|
d72e671d8b | ||
|
|
3462357481 | ||
|
|
2b4da35b1c | ||
|
|
86d68c9875 | ||
|
|
cfff8ee8be | ||
|
|
af10cef5f0 | ||
|
|
a1a42736ed | ||
|
|
b533a11bee | ||
|
|
afed9e9646 | ||
|
|
74b477ac82 | ||
|
|
bf74c8f108 | ||
|
|
04c7f5283e | ||
|
|
d1d53ba45c | ||
|
|
dfadb8f189 | ||
|
|
5335c133a3 | ||
|
|
8fcb5daedf | ||
|
|
ede70a78d6 | ||
|
|
23056cbb45 | ||
|
|
29e9e70ac9 | ||
|
|
d0b711513e | ||
|
|
7dbee74e85 | ||
|
|
4e7a3b1805 | ||
|
|
3c7b244f18 | ||
|
|
e50f5c52c5 | ||
|
|
baf91658d6 | ||
|
|
fda035d8d6 | ||
|
|
a4a53e1a11 | ||
|
|
aad3e52d72 | ||
|
|
cb778a9af5 | ||
|
|
3af9732c84 | ||
|
|
00e754ed9f | ||
|
|
d893f44833 | ||
|
|
4839f41809 | ||
|
|
c801409c94 | ||
|
|
1c4a01d52c | ||
|
|
880b0a883a | ||
|
|
4b77662e3b | ||
|
|
a5ff7ab8df | ||
|
|
04a6e6d13b | ||
|
|
5c2e878c64 | ||
|
|
f9bada5af8 | ||
|
|
80d66f8f68 | ||
|
|
98b35b87a9 | ||
|
|
b1d6605e8f | ||
|
|
2cd3bde348 | ||
|
|
f2454775b9 | ||
|
|
a53d1e00ee | ||
|
|
523a964ac8 | ||
|
|
f8cf7a1054 | ||
|
|
4d8a40c0b5 | ||
|
|
9754da15d6 | ||
|
|
0458d32089 | ||
|
|
ee4e8eb994 | ||
|
|
b8d619dde7 | ||
|
|
00c86fed42 | ||
|
|
6d5af85eee | ||
|
|
1ae2d33714 | ||
|
|
71d550260f | ||
|
|
1092bae05c | ||
|
|
e611f04f41 | ||
|
|
3492cd93d0 | ||
|
|
947ae11fee | ||
|
|
cdd6ed548c | ||
|
|
fc940d0017 | ||
|
|
b38a55ca7c | ||
|
|
8e739d5240 | ||
|
|
71f8bb7e9a | ||
|
|
4a6cec3ecc | ||
|
|
e0cc7f17b2 | ||
|
|
6ac7ab162f | ||
|
|
03bd1fb91d | ||
|
|
a5873a3970 |
No files matched your search
@@ -43,6 +43,10 @@ prog/weavewait/weavewait
|
|||||||
prog/weavewait/weavewait_noop
|
prog/weavewait/weavewait_noop
|
||||||
prog/weavewait/weavewait_nomcast
|
prog/weavewait/weavewait_nomcast
|
||||||
prog/weaveutil/weaveutil
|
prog/weaveutil/weaveutil
|
||||||
|
prog/*/Dockerfile.weaveworks
|
||||||
|
prog/kube-peers/kube-peers
|
||||||
|
prog/weave-kube/kube-peers
|
||||||
|
prog/weave-npc/weave-npc
|
||||||
prog/plugin/plugin
|
prog/plugin/plugin
|
||||||
testing/cover/cover
|
testing/cover/cover
|
||||||
testing/runner/runner
|
testing/runner/runner
|
||||||
|
|||||||
+36
@@ -82,3 +82,39 @@
|
|||||||
[submodule "vendor/github.com/weaveworks/docker"]
|
[submodule "vendor/github.com/weaveworks/docker"]
|
||||||
path = vendor/github.com/weaveworks/docker
|
path = vendor/github.com/weaveworks/docker
|
||||||
url = https://github.com/weaveworks/docker
|
url = https://github.com/weaveworks/docker
|
||||||
|
[submodule "vendor/github.com/beorn7/perks"]
|
||||||
|
path = vendor/github.com/beorn7/perks
|
||||||
|
url = https://github.com/beorn7/perks
|
||||||
|
[submodule "vendor/github.com/prometheus/procfs"]
|
||||||
|
path = vendor/github.com/prometheus/procfs
|
||||||
|
url = https://github.com/prometheus/procfs
|
||||||
|
[submodule "vendor/github.com/prometheus/client_golang"]
|
||||||
|
path = vendor/github.com/prometheus/client_golang
|
||||||
|
url = https://github.com/prometheus/client_golang
|
||||||
|
[submodule "vendor/github.com/prometheus/client_model"]
|
||||||
|
path = vendor/github.com/prometheus/client_model
|
||||||
|
url = https://github.com/prometheus/client_model
|
||||||
|
[submodule "vendor/github.com/prometheus/common"]
|
||||||
|
path = vendor/github.com/prometheus/common
|
||||||
|
url = https://github.com/prometheus/common
|
||||||
|
[submodule "vendor/github.com/golang/protobuf"]
|
||||||
|
path = vendor/github.com/golang/protobuf
|
||||||
|
url = https://github.com/golang/protobuf
|
||||||
|
[submodule "vendor/github.com/matttproud/golang_protobuf_extensions"]
|
||||||
|
path = vendor/github.com/matttproud/golang_protobuf_extensions
|
||||||
|
url = https://github.com/matttproud/golang_protobuf_extensions
|
||||||
|
[submodule "vendor/k8s.io/client-go"]
|
||||||
|
path = vendor/k8s.io/client-go
|
||||||
|
url = https://github.com/kubernetes/client-go
|
||||||
|
[submodule "vendor/github.com/pkg/errors"]
|
||||||
|
path = vendor/github.com/pkg/errors
|
||||||
|
url = https://github.com/pkg/errors
|
||||||
|
[submodule "vendor/github.com/spf13/cobra"]
|
||||||
|
path = vendor/github.com/spf13/cobra
|
||||||
|
url = https://github.com/spf13/cobra
|
||||||
|
[submodule "vendor/github.com/spf13/plag"]
|
||||||
|
path = vendor/github.com/spf13/plag
|
||||||
|
url = https://github.com/spf13/pflag
|
||||||
|
[submodule "vendor/github.com/spf13/pflag"]
|
||||||
|
path = vendor/github.com/spf13/pflag
|
||||||
|
url = https://github.com/spf13/pflag
|
||||||
@@ -1,3 +1,54 @@
|
|||||||
|
## Release 1.8.2
|
||||||
|
|
||||||
|
Bug fixes and minor improvements
|
||||||
|
|
||||||
|
* Fixed a bug where looping flows were installed which caused high CPU
|
||||||
|
usage #2650, #2674
|
||||||
|
* Fixed a bug where Kubernetes master could not contact pods #2673, #2683
|
||||||
|
* Fixed a bug where weave-kube was crashing in a loop due to invalid
|
||||||
|
Weave bridge state #2657
|
||||||
|
* Fixed a bug where iptables NAT rules were not appended due to
|
||||||
|
"temporary unavailable" iptables error #2679
|
||||||
|
* Added a detection of enabling the hairpin mode on the Weave bridge port
|
||||||
|
which caused installation of looping flows #2674
|
||||||
|
* Added a detection of overlaps between Weave and the host IP address
|
||||||
|
ranges when launching weave-kube #2669, #2672
|
||||||
|
* Added logging of connections blocked by weave-npc #2546, #2573
|
||||||
|
|
||||||
|
More details in the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.8.2).
|
||||||
|
|
||||||
|
## Release 1.8.1
|
||||||
|
|
||||||
|
Bug fixes and minor improvements
|
||||||
|
|
||||||
|
* Fixed weave-npc crash from Succeeded/Failed pods #2632,#2658
|
||||||
|
* Fixed occasional failure to create Weave bridge on node reboot
|
||||||
|
#2617,#2637
|
||||||
|
* Fixed a bug where weave-kube would fail to install when run with
|
||||||
|
unrelease snapshot builds #2642
|
||||||
|
* Improved conformance to CNI spec by not releasing IP addresses when
|
||||||
|
a container dies #2643
|
||||||
|
* Improved troubleshooting of install failure by creating CNI config
|
||||||
|
after Weave Net is up #2570
|
||||||
|
* "up to date" shown even when the version check was blocked by
|
||||||
|
firewall #2537,#2565,#2645
|
||||||
|
* "Unable to claim" message on re-launching Weave after using CNI
|
||||||
|
#2548,#2577
|
||||||
|
* Eliminated spurious IP reclaim operations when IPAM was disabled
|
||||||
|
#2567,#2644
|
||||||
|
* Include `jq` tool in our build VM configuration #2656
|
||||||
|
|
||||||
|
## Release 1.8.0
|
||||||
|
|
||||||
|
Highlights:
|
||||||
|
|
||||||
|
* Exposed network policy controller Prometheus metrics
|
||||||
|
weaveworks/weave-npc#23, #2595, #2549
|
||||||
|
* Exposed router Prometheus metrics #2535, #2547, #2523, #2579, #2578,
|
||||||
|
#2568, #2560, #2561
|
||||||
|
|
||||||
|
More details in the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.8.0).
|
||||||
|
|
||||||
## Release 1.7.2
|
## Release 1.7.2
|
||||||
|
|
||||||
Bug fixes and minor improvements
|
Bug fixes and minor improvements
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
PUBLISH=publish_weave publish_weaveexec publish_plugin
|
PUBLISH=publish_weave publish_weaveexec publish_plugin publish_weave-kube publish_weave-npc
|
||||||
|
|
||||||
.DEFAULT: all
|
.DEFAULT: all
|
||||||
.PHONY: all exes testrunner update tests lint publish $(PUBLISH) clean clean-bin prerequisites build run-smoketests
|
.PHONY: all exes testrunner update tests lint publish $(PUBLISH) clean clean-bin prerequisites build run-smoketests
|
||||||
@@ -16,6 +16,8 @@ WEAVE_VERSION=git-$(shell git rev-parse --short=12 HEAD)
|
|||||||
WEAVER_EXE=prog/weaver/weaver
|
WEAVER_EXE=prog/weaver/weaver
|
||||||
WEAVEPROXY_EXE=prog/weaveproxy/weaveproxy
|
WEAVEPROXY_EXE=prog/weaveproxy/weaveproxy
|
||||||
SIGPROXY_EXE=prog/sigproxy/sigproxy
|
SIGPROXY_EXE=prog/sigproxy/sigproxy
|
||||||
|
KUBEPEERS_EXE=prog/kube-peers/kube-peers
|
||||||
|
WEAVENPC_EXE=prog/weave-npc/weave-npc
|
||||||
WEAVEWAIT_EXE=prog/weavewait/weavewait
|
WEAVEWAIT_EXE=prog/weavewait/weavewait
|
||||||
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
|
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
|
||||||
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
|
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
|
||||||
@@ -24,23 +26,27 @@ PLUGIN_EXE=prog/plugin/plugin
|
|||||||
RUNNER_EXE=tools/runner/runner
|
RUNNER_EXE=tools/runner/runner
|
||||||
TEST_TLS_EXE=test/tls/tls
|
TEST_TLS_EXE=test/tls/tls
|
||||||
|
|
||||||
EXES=$(WEAVER_EXE) $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVEUTIL_EXE) $(PLUGIN_EXE) $(TEST_TLS_EXE)
|
EXES=$(WEAVER_EXE) $(SIGPROXY_EXE) $(KUBEPEERS_EXE) $(WEAVENPC_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVEUTIL_EXE) $(PLUGIN_EXE) $(TEST_TLS_EXE)
|
||||||
|
|
||||||
BUILD_UPTODATE=.build.uptodate
|
BUILD_UPTODATE=.build.uptodate
|
||||||
WEAVER_UPTODATE=.weaver.uptodate
|
WEAVER_UPTODATE=.weaver.uptodate
|
||||||
WEAVEEXEC_UPTODATE=.weaveexec.uptodate
|
WEAVEEXEC_UPTODATE=.weaveexec.uptodate
|
||||||
PLUGIN_UPTODATE=.plugin.uptodate
|
PLUGIN_UPTODATE=.plugin.uptodate
|
||||||
|
WEAVEKUBE_UPTODATE=.weavekube.uptodate
|
||||||
|
WEAVENPC_UPTODATE=.weavenpc.uptodate
|
||||||
WEAVEDB_UPTODATE=.weavedb.uptodate
|
WEAVEDB_UPTODATE=.weavedb.uptodate
|
||||||
|
|
||||||
IMAGES_UPTODATE=$(WEAVER_UPTODATE) $(WEAVEEXEC_UPTODATE) $(PLUGIN_UPTODATE) $(WEAVEDB_UPTODATE)
|
IMAGES_UPTODATE=$(WEAVER_UPTODATE) $(WEAVEEXEC_UPTODATE) $(PLUGIN_UPTODATE) $(WEAVEKUBE_UPTODATE) $(WEAVENPC_UPTODATE) $(WEAVEDB_UPTODATE)
|
||||||
|
|
||||||
WEAVER_IMAGE=$(DOCKERHUB_USER)/weave
|
WEAVER_IMAGE=$(DOCKERHUB_USER)/weave
|
||||||
WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec
|
WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec
|
||||||
PLUGIN_IMAGE=$(DOCKERHUB_USER)/plugin
|
PLUGIN_IMAGE=$(DOCKERHUB_USER)/plugin
|
||||||
|
WEAVEKUBE_IMAGE=$(DOCKERHUB_USER)/weave-kube
|
||||||
|
WEAVENPC_IMAGE=$(DOCKERHUB_USER)/weave-npc
|
||||||
BUILD_IMAGE=$(DOCKERHUB_USER)/weavebuild
|
BUILD_IMAGE=$(DOCKERHUB_USER)/weavebuild
|
||||||
WEAVEDB_IMAGE=$(DOCKERHUB_USER)/weavedb
|
WEAVEDB_IMAGE=$(DOCKERHUB_USER)/weavedb
|
||||||
|
|
||||||
IMAGES=$(WEAVER_IMAGE) $(WEAVEEXEC_IMAGE) $(PLUGIN_IMAGE) $(WEAVEDB_IMAGE)
|
IMAGES=$(WEAVER_IMAGE) $(WEAVEEXEC_IMAGE) $(PLUGIN_IMAGE) $(WEAVEKUBE_IMAGE) $(WEAVENPC_IMAGE) $(WEAVEDB_IMAGE)
|
||||||
|
|
||||||
WEAVE_EXPORT=weave.tar.gz
|
WEAVE_EXPORT=weave.tar.gz
|
||||||
|
|
||||||
@@ -55,7 +61,7 @@ NETGO_CHECK=@strings $@ | grep cgo_stub\\\.go >/dev/null || { \
|
|||||||
echo " sudo go install -tags netgo std"; \
|
echo " sudo go install -tags netgo std"; \
|
||||||
false; \
|
false; \
|
||||||
}
|
}
|
||||||
BUILD_FLAGS=-i -ldflags "-extldflags \"-static\" -X main.version=$(WEAVE_VERSION)" -tags netgo
|
BUILD_FLAGS=-i -ldflags "-linkmode external -extldflags -static -X main.version=$(WEAVE_VERSION)" -tags netgo
|
||||||
|
|
||||||
PACKAGE_BASE=$(shell go list -e ./)
|
PACKAGE_BASE=$(shell go list -e ./)
|
||||||
|
|
||||||
@@ -67,6 +73,8 @@ $(WEAVER_EXE): router/*.go ipam/*.go ipam/*/*.go db/*.go nameserver/*.go prog/we
|
|||||||
$(WEAVEPROXY_EXE): proxy/*.go prog/weaveproxy/*.go
|
$(WEAVEPROXY_EXE): proxy/*.go prog/weaveproxy/*.go
|
||||||
$(WEAVEUTIL_EXE): prog/weaveutil/*.go net/*.go
|
$(WEAVEUTIL_EXE): prog/weaveutil/*.go net/*.go
|
||||||
$(SIGPROXY_EXE): prog/sigproxy/*.go
|
$(SIGPROXY_EXE): prog/sigproxy/*.go
|
||||||
|
$(KUBEPEERS_EXE): prog/kube-peers/*.go
|
||||||
|
$(WEAVENPC_EXE): prog/weave-npc/*.go npc/*.go npc/*/*.go
|
||||||
$(PLUGIN_EXE): prog/plugin/*.go plugin/*/*.go api/*.go common/*.go common/docker/*.go net/*.go
|
$(PLUGIN_EXE): prog/plugin/*.go plugin/*/*.go api/*.go common/*.go common/docker/*.go net/*.go
|
||||||
$(TEST_TLS_EXE): test/tls/*.go
|
$(TEST_TLS_EXE): test/tls/*.go
|
||||||
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
|
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
|
||||||
@@ -99,7 +107,7 @@ else
|
|||||||
endif
|
endif
|
||||||
$(NETGO_CHECK)
|
$(NETGO_CHECK)
|
||||||
|
|
||||||
$(WEAVEUTIL_EXE):
|
$(WEAVEUTIL_EXE) $(KUBEPEERS_EXE) $(WEAVENPC_EXE):
|
||||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||||
$(NETGO_CHECK)
|
$(NETGO_CHECK)
|
||||||
|
|
||||||
@@ -128,8 +136,11 @@ $(BUILD_UPTODATE): build/*
|
|||||||
$(SUDO) docker build -t $(BUILD_IMAGE) build/
|
$(SUDO) docker build -t $(BUILD_IMAGE) build/
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(WEAVER_UPTODATE): prog/weaver/Dockerfile $(WEAVER_EXE)
|
%/Dockerfile.$(DOCKERHUB_USER): %/Dockerfile.template
|
||||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -t $(WEAVER_IMAGE) prog/weaver
|
sed -e "s/DOCKERHUB_USER/$(DOCKERHUB_USER)/" $^ > $@
|
||||||
|
|
||||||
|
$(WEAVER_UPTODATE): prog/weaver/Dockerfile.$(DOCKERHUB_USER) $(WEAVER_EXE) $(WEAVEEXEC_UPTODATE)
|
||||||
|
$(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 prog/weaveexec/symlink $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVEUTIL_EXE)
|
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile prog/weaveexec/symlink $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVEUTIL_EXE)
|
||||||
@@ -144,8 +155,17 @@ $(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile prog/weaveexec/symlink $(DOCKER
|
|||||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -t $(WEAVEEXEC_IMAGE) prog/weaveexec
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -t $(WEAVEEXEC_IMAGE) prog/weaveexec
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(PLUGIN_UPTODATE): prog/plugin/Dockerfile $(PLUGIN_EXE) $(WEAVEEXEC_UPTODATE)
|
$(PLUGIN_UPTODATE): prog/plugin/Dockerfile.$(DOCKERHUB_USER) $(PLUGIN_EXE) $(WEAVER_UPTODATE)
|
||||||
$(SUDO) docker build -t $(PLUGIN_IMAGE) prog/plugin
|
$(SUDO) docker build -f prog/plugin/Dockerfile.$(DOCKERHUB_USER) -t $(PLUGIN_IMAGE) prog/plugin
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
$(WEAVEKUBE_UPTODATE): prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) prog/weave-kube/launch.sh $(KUBEPEERS_EXE) $(WEAVER_UPTODATE)
|
||||||
|
cp $(KUBEPEERS_EXE) prog/weave-kube/
|
||||||
|
$(SUDO) docker build -f prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVEKUBE_IMAGE) prog/weave-kube
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
$(WEAVENPC_UPTODATE): prog/weave-npc/Dockerfile $(WEAVENPC_EXE) prog/weave-npc/ulogd.conf
|
||||||
|
$(SUDO) docker build -f prog/weave-npc/Dockerfile -t $(WEAVENPC_IMAGE) prog/weave-npc
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(WEAVEDB_UPTODATE): prog/weavedb/Dockerfile
|
$(WEAVEDB_UPTODATE): prog/weavedb/Dockerfile
|
||||||
@@ -162,7 +182,7 @@ tools/.git:
|
|||||||
git submodule update --init
|
git submodule update --init
|
||||||
|
|
||||||
$(RUNNER_EXE): tools/.git
|
$(RUNNER_EXE): tools/.git
|
||||||
make -C tools/runner
|
GO15VENDOREXPERIMENT=1 make -C tools/runner
|
||||||
|
|
||||||
$(PUBLISH): publish_%: $(IMAGES_UPTODATE)
|
$(PUBLISH): publish_%: $(IMAGES_UPTODATE)
|
||||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker tag $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker tag $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||||
|
|||||||
Vendored
+5
@@ -14,6 +14,7 @@ pkgs = %w(
|
|||||||
git
|
git
|
||||||
mercurial
|
mercurial
|
||||||
bc
|
bc
|
||||||
|
jq
|
||||||
)
|
)
|
||||||
|
|
||||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||||
@@ -22,6 +23,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||||||
|
|
||||||
config.vm.network "private_network", ip: vm_ip
|
config.vm.network "private_network", ip: vm_ip
|
||||||
config.vm.provider :virtualbox do |vb|
|
config.vm.provider :virtualbox do |vb|
|
||||||
|
vb.memory = 2048
|
||||||
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "off"]
|
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "off"]
|
||||||
vb.customize ["modifyvm", :id, "--natdnsproxy1", "off"]
|
vb.customize ["modifyvm", :id, "--natdnsproxy1", "off"]
|
||||||
end
|
end
|
||||||
@@ -29,6 +31,9 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||||||
config.vm.synced_folder ".", "/vagrant", disabled: true
|
config.vm.synced_folder ".", "/vagrant", disabled: true
|
||||||
config.vm.synced_folder ".", "/home/vagrant/src/github.com/weaveworks/weave"
|
config.vm.synced_folder ".", "/home/vagrant/src/github.com/weaveworks/weave"
|
||||||
|
|
||||||
|
# Set SSH keys up to be able to run smoke tests straightaway:
|
||||||
|
config.vm.provision "file", source: "~/.vagrant.d/insecure_private_key", destination: "/home/vagrant/src/github.com/weaveworks/weave/test/insecure_private_key"
|
||||||
|
|
||||||
install_build_deps config.vm, pkgs
|
install_build_deps config.vm, pkgs
|
||||||
install_go_toochain config.vm
|
install_go_toochain config.vm
|
||||||
tweak_user_env config.vm
|
tweak_user_env config.vm
|
||||||
|
|||||||
@@ -5,6 +5,15 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Special token used in place of a container identifier when:
|
||||||
|
// - the caller does not know the container ID, or
|
||||||
|
// - the caller is unsure whether IPAM was previously told the container ID.
|
||||||
|
//
|
||||||
|
// While addresses are typically stored under their container IDs, when this
|
||||||
|
// token is used, the address will be stored under its own string, something
|
||||||
|
// which should be kept in mind if the entry needs to be removed at any point.
|
||||||
|
const NoContainerID string = "_"
|
||||||
|
|
||||||
func (client *Client) ipamOp(ID string, op string) (*net.IPNet, error) {
|
func (client *Client) ipamOp(ID string, op string) (*net.IPNet, error) {
|
||||||
ip, err := client.httpVerb(op, fmt.Sprintf("/ip/%s", ID), nil)
|
ip, err := client.httpVerb(op, fmt.Sprintf("/ip/%s", ID), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# We need jq to analyse the PR files JSON, so install that first
|
|
||||||
sudo apt-get -q update && sudo apt-get -q install bc jq
|
|
||||||
|
|
||||||
echo "TEST_AND_PUBLISH=1" > "$STATE"
|
echo "TEST_AND_PUBLISH=1" > "$STATE"
|
||||||
|
|
||||||
if [ -n "$CI_PULL_REQUEST" -a "$CI_PULL_REQUEST" = "$CI_PULL_REQUESTS" ] ; then
|
if [ -n "$CI_PULL_REQUEST" -a "$CI_PULL_REQUEST" = "$CI_PULL_REQUESTS" ] ; then
|
||||||
|
|||||||
+56
@@ -157,6 +157,28 @@ draft() {
|
|||||||
--name "weave" \
|
--name "weave" \
|
||||||
--file "./weave"
|
--file "./weave"
|
||||||
|
|
||||||
|
github-release release $RELEASE_ARGS \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave-kube \
|
||||||
|
--tag $LATEST_TAG \
|
||||||
|
--name "$RELEASE_NAME $VERSION" \
|
||||||
|
--description "$RELEASE_DESCRIPTION"
|
||||||
|
|
||||||
|
sed -i -e s/:latest/:$VERSION/ -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset.yaml
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave-kube \
|
||||||
|
--tag $LATEST_TAG \
|
||||||
|
--name "weave-daemonset.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset.yaml"
|
||||||
|
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave \
|
||||||
|
--tag $LATEST_TAG \
|
||||||
|
--name "weave-daemonset.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset.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"
|
||||||
}
|
}
|
||||||
@@ -236,6 +258,40 @@ publish() {
|
|||||||
--name "weave" \
|
--name "weave" \
|
||||||
--file "./weave"
|
--file "./weave"
|
||||||
|
|
||||||
|
github-release publish \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave-kube \
|
||||||
|
--tag $LATEST_TAG
|
||||||
|
|
||||||
|
if github-release info --user $GITHUB_USER --repo weave-kube \
|
||||||
|
--tag latest_release >/dev/null 2>&1; then
|
||||||
|
github-release delete \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave-kube \
|
||||||
|
--tag latest_release
|
||||||
|
fi
|
||||||
|
|
||||||
|
github-release release \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave-kube \
|
||||||
|
--tag latest_release \
|
||||||
|
--name "$RELEASE_NAME latest ($VERSION)" \
|
||||||
|
--description "[Release Notes](https://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG)"
|
||||||
|
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave-kube \
|
||||||
|
--tag latest_release \
|
||||||
|
--name "weave-daemonset.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset.yaml"
|
||||||
|
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave \
|
||||||
|
--tag latest_release \
|
||||||
|
--name "weave-daemonset.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset.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"
|
||||||
|
|||||||
+1
-151
@@ -1,16 +1,6 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import "strings"
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
"github.com/vishvananda/netns"
|
|
||||||
|
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Assert test is true, panic otherwise
|
// Assert test is true, panic otherwise
|
||||||
func Assert(test bool) {
|
func Assert(test bool) {
|
||||||
@@ -26,143 +16,3 @@ func ErrorMessages(errors []error) string {
|
|||||||
}
|
}
|
||||||
return strings.Join(result, "\n")
|
return strings.Join(result, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
type NetDev struct {
|
|
||||||
Name string
|
|
||||||
MAC net.HardwareAddr
|
|
||||||
CIDRs []*net.IPNet
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search the network namespace of a process for interfaces matching a predicate
|
|
||||||
// Note that the predicate is called while the goroutine is inside the process' netns
|
|
||||||
func FindNetDevs(processID int, match func(link netlink.Link) bool) ([]NetDev, error) {
|
|
||||||
var netDevs []NetDev
|
|
||||||
|
|
||||||
ns, err := netns.GetFromPid(processID)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer ns.Close()
|
|
||||||
|
|
||||||
err = weavenet.WithNetNSUnsafe(ns, func() error {
|
|
||||||
return forEachLink(func(link netlink.Link) error {
|
|
||||||
if match(link) {
|
|
||||||
netDev, err := linkToNetDev(link)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
netDevs = append(netDevs, netDev)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
return netDevs, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func forEachLink(f func(netlink.Link) error) error {
|
|
||||||
links, err := netlink.LinkList()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, link := range links {
|
|
||||||
if err := f(link); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func linkToNetDev(link netlink.Link) (NetDev, error) {
|
|
||||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
|
||||||
if err != nil {
|
|
||||||
return NetDev{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
netDev := NetDev{Name: link.Attrs().Name, MAC: link.Attrs().HardwareAddr}
|
|
||||||
for _, addr := range addrs {
|
|
||||||
netDev.CIDRs = append(netDev.CIDRs, addr.IPNet)
|
|
||||||
}
|
|
||||||
return netDev, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectedToBridgePredicate returns a function which is used to query whether
|
|
||||||
// a given link is a veth interface which one end is connected to a bridge.
|
|
||||||
// The returned function should be called from a container network namespace which
|
|
||||||
// the bridge does NOT belong to.
|
|
||||||
func ConnectedToBridgePredicate(bridgeName string) (func(link netlink.Link) bool, error) {
|
|
||||||
indexes := make(map[int]struct{})
|
|
||||||
|
|
||||||
// Scan devices in root namespace to find those attached to weave bridge
|
|
||||||
err := weavenet.WithNetNSLinkByPidUnsafe(1, bridgeName,
|
|
||||||
func(br netlink.Link) error {
|
|
||||||
return forEachLink(func(link netlink.Link) error {
|
|
||||||
if link.Attrs().MasterIndex == br.Attrs().Index {
|
|
||||||
peerIndex := link.Attrs().ParentIndex
|
|
||||||
if peerIndex == 0 {
|
|
||||||
// perhaps running on an older kernel where ParentIndex doesn't work.
|
|
||||||
// as fall-back, assume the indexes are consecutive
|
|
||||||
peerIndex = link.Attrs().Index - 1
|
|
||||||
}
|
|
||||||
indexes[peerIndex] = struct{}{}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return func(link netlink.Link) bool {
|
|
||||||
_, isveth := link.(*netlink.Veth)
|
|
||||||
_, found := indexes[link.Attrs().Index]
|
|
||||||
return isveth && found
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetNetDevsWithPredicate(processID int, predicate func(link netlink.Link) bool) ([]NetDev, error) {
|
|
||||||
// Bail out if this process is running in the root namespace
|
|
||||||
nsToplevel, err := netns.GetFromPid(1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to open root namespace: %s", err)
|
|
||||||
}
|
|
||||||
defer nsToplevel.Close()
|
|
||||||
nsContainr, err := netns.GetFromPid(processID)
|
|
||||||
if err != nil {
|
|
||||||
// Unable to find a namespace for this process - just return nothing
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("unable to open process %d namespace: %s", processID, err)
|
|
||||||
}
|
|
||||||
defer nsContainr.Close()
|
|
||||||
if nsToplevel.Equal(nsContainr) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return FindNetDevs(processID, predicate)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup the weave interface of a container
|
|
||||||
func GetWeaveNetDevs(processID int) ([]NetDev, error) {
|
|
||||||
p, err := ConnectedToBridgePredicate("weave")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetNetDevsWithPredicate(processID, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the weave bridge interface.
|
|
||||||
// NB: Should be called from the root network namespace.
|
|
||||||
func GetBridgeNetDev(bridgeName string) (NetDev, error) {
|
|
||||||
link, err := netlink.LinkByName(bridgeName)
|
|
||||||
if err != nil {
|
|
||||||
return NetDev{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return linkToNetDev(link)
|
|
||||||
}
|
|
||||||
@@ -107,6 +107,15 @@ the build script will complain otherwise.
|
|||||||
N.B. if you're testing the release process, push to your fork
|
N.B. if you're testing the release process, push to your fork
|
||||||
instead!
|
instead!
|
||||||
|
|
||||||
|
While the git.io link for weave-kube is still pointing to the
|
||||||
|
weave-kube repository, we also need to update the tag there:
|
||||||
|
|
||||||
|
cd ../weave-kube
|
||||||
|
git tag -a -m "Release $TAG" $TAG
|
||||||
|
git tag -af -m "Release $TAG" latest_release $TAG
|
||||||
|
git push -f git@github.com:weaveworks/weave latest_release
|
||||||
|
|
||||||
|
|
||||||
### Publish Release & Distributable Artefacts
|
### Publish Release & Distributable Artefacts
|
||||||
|
|
||||||
You can now publish the release and upload the remaining
|
You can now publish the release and upload the remaining
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# Overview
|
||||||
|
|
||||||
|
# ipsets
|
||||||
|
|
||||||
|
The policy controller maintains a number of ipsets which are
|
||||||
|
subsequently referred to by the iptables rules used to effect network
|
||||||
|
policy specifications. These ipsets are created, modified and
|
||||||
|
destroyed automatically in response to Pod, Namespace and
|
||||||
|
NetworkPolicy object updates from the k8s API server:
|
||||||
|
|
||||||
|
* A `hash:ip` set per namespace, containing the IP addresses of all
|
||||||
|
pods in that namespace
|
||||||
|
* A `list:set` per distinct (across all network policies in all
|
||||||
|
namespaces) namespace selector mentioned in a network policy,
|
||||||
|
containing the names of any of the above hash:ip sets whose
|
||||||
|
corresponding namespace labels match the selector
|
||||||
|
* A `hash:ip` set for each distinct (within the scope of the
|
||||||
|
containing network policy's namespace) pod selector mentioned in a
|
||||||
|
network policy, containing the IP addresses of all pods in the
|
||||||
|
namespace whose labels match that selector
|
||||||
|
|
||||||
|
ipset names are generated deterministically from a string
|
||||||
|
representation of the corresponding label selector. Because ipset
|
||||||
|
names are limited to 31 characters in length, this is done by taking a
|
||||||
|
SHA hash of the selector string and then printing that out as a base
|
||||||
|
85 string with a "weave-" prefix e.g.:
|
||||||
|
|
||||||
|
weave-k?Z;25^M}|1s7P3|H9i;*;MhG
|
||||||
|
|
||||||
|
Because pod selectors are scoped to a namespace, we need to make sure
|
||||||
|
that if the same selector definition is used in different namespaces
|
||||||
|
that we maintain distinct ipsets. Consequently, for such selectors the
|
||||||
|
namespace name is prepended to the label selector string before
|
||||||
|
hashing to avoid clashes.
|
||||||
|
|
||||||
|
# iptables chains
|
||||||
|
|
||||||
|
The policy controller maintains two iptables chains in response to
|
||||||
|
changes to pods, namespaces and network policies. One chain contains
|
||||||
|
the ingress rules that implement the network policy specifications,
|
||||||
|
and the other is used to bypass the ingress rules for namespaces which
|
||||||
|
have an ingress isolation policy of `DefaultAllow`.
|
||||||
|
|
||||||
|
## Dynamically maintained `WEAVE-NPC-DEFAULT` chain
|
||||||
|
|
||||||
|
The policy controller maintains a rule in this chain for every
|
||||||
|
namespace whose ingress isolation policy is `DefaultAllow`. The
|
||||||
|
purpose of this rule is simply to ACCEPT any traffic destined for such
|
||||||
|
namespaces before it reaches the ingress chain.
|
||||||
|
|
||||||
|
```
|
||||||
|
iptables -A WEAVE-NPC-DEFAULT -m set --match-set $NSIPSET dst -j ACCEPT
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dynamically maintained `WEAVE-NPC-INGRESS` chain
|
||||||
|
|
||||||
|
For each namespace network policy ingress rule peer/port combination:
|
||||||
|
|
||||||
|
```
|
||||||
|
iptables -A WEAVE-NPC-INGRESS -p $PROTO [-m set --match-set $SRCSET] -m set --match-set $DSTSET --dport $DPORT -j ACCEPT
|
||||||
|
```
|
||||||
|
|
||||||
|
## Static `WEAVE-NPC` chain
|
||||||
|
|
||||||
|
Static configuration:
|
||||||
|
|
||||||
|
```
|
||||||
|
iptables -A WEAVE-NPC -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||||
|
iptables -A WEAVE-NPC -m state --state NEW -j WEAVE-NPC-DEFAULT
|
||||||
|
iptables -A WEAVE-NPC -m state --state NEW -j WEAVE-NPC-INGRESS
|
||||||
|
```
|
||||||
|
|
||||||
|
# Steering traffic into the policy engine
|
||||||
|
|
||||||
|
To direct traffic into the policy engine:
|
||||||
|
|
||||||
|
iptables -A FORWARD -o weave -m physdev ! --physdev-out vethwe-bridge -j WEAVE-NPC
|
||||||
|
iptables -A FORWARD -o weave -m physdev ! --physdev-out vethwe-bridge -j DROP
|
||||||
|
|
||||||
|
Note this only affects traffic which egresses the bridge on a physical
|
||||||
|
port which is not the Weave Net router - in other words, it is
|
||||||
|
destined for an application container veth. The following traffic is
|
||||||
|
affected:
|
||||||
|
|
||||||
|
* Traffic bridged between local application containers
|
||||||
|
* Traffic bridged from the router to a local application container
|
||||||
|
* Traffic originating from the internet destined for nodeports - this
|
||||||
|
is routed via the FORWARD chain to a container pod IP after DNAT
|
||||||
|
|
||||||
|
The following traffic is NOT affected:
|
||||||
|
|
||||||
|
* Traffic bridged from a local application container to the router
|
||||||
|
* Traffic originating from processes in the host network namespace
|
||||||
|
(e.g. kubelet health checks)
|
||||||
|
* Traffic routed from an application container to the internet
|
||||||
|
|
||||||
|
See these resources for helpful context:
|
||||||
|
|
||||||
|
* http://ebtables.netfilter.org/br_fw_ia/br_fw_ia.html
|
||||||
|
* https://commons.wikimedia.org/wiki/File:Netfilter-packet-flow.svg
|
||||||
+2
-1
@@ -3,6 +3,7 @@ package ipam
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/api"
|
||||||
"github.com/weaveworks/weave/net/address"
|
"github.com/weaveworks/weave/net/address"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ func (g *allocate) Try(alloc *Allocator) bool {
|
|||||||
if ok, addr := alloc.space.Allocate(g.r.HostRange()); ok {
|
if ok, addr := alloc.space.Allocate(g.r.HostRange()); ok {
|
||||||
// If caller hasn't supplied a unique ID, file it under the IP address
|
// If caller hasn't supplied a unique ID, file it under the IP address
|
||||||
// which lets the caller then release the address using DELETE /ip/address
|
// which lets the caller then release the address using DELETE /ip/address
|
||||||
if g.ident == "_" {
|
if g.ident == api.NoContainerID {
|
||||||
g.ident = addr.String()
|
g.ident = addr.String()
|
||||||
}
|
}
|
||||||
alloc.debugln("Allocated", addr, "for", g.ident, "in", g.r)
|
alloc.debugln("Allocated", addr, "for", g.ident, "in", g.r)
|
||||||
|
|||||||
+5
-5
@@ -322,7 +322,7 @@ func (alloc *Allocator) Claim(ident string, cidr address.CIDR, isContainer, noEr
|
|||||||
// ContainerDied called from the updater interface. Async.
|
// ContainerDied called from the updater interface. Async.
|
||||||
func (alloc *Allocator) ContainerDied(ident string) {
|
func (alloc *Allocator) ContainerDied(ident string) {
|
||||||
alloc.actionChan <- func() {
|
alloc.actionChan <- func() {
|
||||||
if alloc.hasOwned(ident) {
|
if alloc.hasOwnedByContainer(ident) {
|
||||||
alloc.debugln("Container", ident, "died; noting to remove later")
|
alloc.debugln("Container", ident, "died; noting to remove later")
|
||||||
alloc.dead[ident] = alloc.now()
|
alloc.dead[ident] = alloc.now()
|
||||||
}
|
}
|
||||||
@@ -335,7 +335,7 @@ func (alloc *Allocator) ContainerDied(ident string) {
|
|||||||
// ContainerDestroyed called from the updater interface. Async.
|
// ContainerDestroyed called from the updater interface. Async.
|
||||||
func (alloc *Allocator) ContainerDestroyed(ident string) {
|
func (alloc *Allocator) ContainerDestroyed(ident string) {
|
||||||
alloc.actionChan <- func() {
|
alloc.actionChan <- func() {
|
||||||
if alloc.hasOwned(ident) {
|
if alloc.hasOwnedByContainer(ident) {
|
||||||
alloc.debugln("Container", ident, "destroyed; removing addresses")
|
alloc.debugln("Container", ident, "destroyed; removing addresses")
|
||||||
alloc.delete(ident)
|
alloc.delete(ident)
|
||||||
delete(alloc.dead, ident)
|
delete(alloc.dead, ident)
|
||||||
@@ -996,9 +996,9 @@ func (alloc *Allocator) persistOwned() {
|
|||||||
|
|
||||||
// Owned addresses
|
// Owned addresses
|
||||||
|
|
||||||
func (alloc *Allocator) hasOwned(ident string) bool {
|
func (alloc *Allocator) hasOwnedByContainer(ident string) bool {
|
||||||
_, b := alloc.owned[ident]
|
d, b := alloc.owned[ident]
|
||||||
return b
|
return b && d.IsContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: addr must not be owned by ident already
|
// NB: addr must not be owned by ident already
|
||||||
|
|||||||
+18
-9
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/weaveworks/mesh"
|
"github.com/weaveworks/mesh"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/api"
|
||||||
"github.com/weaveworks/weave/common"
|
"github.com/weaveworks/weave/common"
|
||||||
"github.com/weaveworks/weave/net/address"
|
"github.com/weaveworks/weave/net/address"
|
||||||
)
|
)
|
||||||
@@ -77,26 +78,34 @@ func (c *claim) Try(alloc *Allocator) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.ident == "_" { // Special "I don't have a unique ID" identifier
|
|
||||||
c.ident = c.cidr.Addr.String()
|
|
||||||
}
|
|
||||||
// We are the owner, check we haven't given it to another container
|
// We are the owner, check we haven't given it to another container
|
||||||
switch existingIdent := alloc.findOwner(c.cidr.Addr); existingIdent {
|
existingIdent := alloc.findOwner(c.cidr.Addr)
|
||||||
case "":
|
switch {
|
||||||
|
case existingIdent == "":
|
||||||
|
// Unused address, we try to claim it:
|
||||||
if err := alloc.space.Claim(c.cidr.Addr); err == nil {
|
if err := alloc.space.Claim(c.cidr.Addr); err == nil {
|
||||||
alloc.debugln("Claimed", c.cidr, "for", c.ident)
|
alloc.debugln("Claimed", c.cidr, "for", c.ident)
|
||||||
alloc.addOwned(c.ident, c.cidr, c.isContainer)
|
if c.ident == api.NoContainerID {
|
||||||
|
alloc.addOwned(c.cidr.Addr.String(), c.cidr, c.isContainer)
|
||||||
|
} else {
|
||||||
|
alloc.addOwned(c.ident, c.cidr, c.isContainer)
|
||||||
|
}
|
||||||
c.sendResult(nil)
|
c.sendResult(nil)
|
||||||
} else {
|
} else {
|
||||||
c.sendResult(err)
|
c.sendResult(err)
|
||||||
}
|
}
|
||||||
case c.ident:
|
case (existingIdent == c.ident) || (c.ident == api.NoContainerID && existingIdent == c.cidr.Addr.String()):
|
||||||
// same identifier is claiming same address; that's OK
|
// same identifier is claiming same address; that's OK
|
||||||
alloc.debugln("Re-Claimed", c.cidr, "for", c.ident)
|
alloc.debugln("Re-Claimed", c.cidr, "for", c.ident)
|
||||||
c.sendResult(nil)
|
c.sendResult(nil)
|
||||||
case c.cidr.Addr.String():
|
case existingIdent == c.cidr.Addr.String():
|
||||||
// Address already allocated via "_" name
|
// Address already allocated via api.NoContainerID name and current ID is a real container ID:
|
||||||
c.sendResult(fmt.Errorf("address %s already in use", c.cidr))
|
c.sendResult(fmt.Errorf("address %s already in use", c.cidr))
|
||||||
|
case c.ident == api.NoContainerID:
|
||||||
|
// We do not know whether this is the same container or another one,
|
||||||
|
// but we also cannot prove otherwise, so we let it reclaim the address:
|
||||||
|
alloc.debugln("Re-Claimed", c.cidr, "for ID", c.ident, "having existing ID as", existingIdent)
|
||||||
|
c.sendResult(nil)
|
||||||
default:
|
default:
|
||||||
// Addr already owned by container on this machine
|
// Addr already owned by container on this machine
|
||||||
c.sendResult(fmt.Errorf("address %s is already owned by %s", c.cidr.String(), existingIdent))
|
c.sendResult(fmt.Errorf("address %s is already owned by %s", c.cidr.String(), existingIdent))
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
package ring
|
|
||||||
|
|
||||||
/* Exported variables for monitoring and management */
|
|
||||||
|
|
||||||
import (
|
|
||||||
"expvar"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
expRingSize = expvar.NewMap("ipam.ringSize")
|
|
||||||
expRingEntries = expvar.NewMap("ipam.ringEntries")
|
|
||||||
)
|
|
||||||
|
|
||||||
type _int int
|
|
||||||
|
|
||||||
func (i _int) String() string {
|
|
||||||
return strconv.Itoa(int(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
type _uint32 uint32
|
|
||||||
|
|
||||||
func (i _uint32) String() string {
|
|
||||||
return strconv.FormatUint(uint64(i), 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Ring) updateExportedVariables() {
|
|
||||||
ringName := r.Start.String()
|
|
||||||
expRingSize.Set(ringName, _uint32(r.End-r.Start))
|
|
||||||
expRingEntries.Set(ringName, _int(len(r.Entries)))
|
|
||||||
}
|
|
||||||
@@ -114,7 +114,6 @@ func New(start, end address.Address, peer mesh.PeerName, f OnUpdate) *Ring {
|
|||||||
common.Assert(start < end)
|
common.Assert(start < end)
|
||||||
|
|
||||||
ring := &Ring{Start: start, End: end, Peer: peer, Entries: make([]*entry, 0), onUpdate: f}
|
ring := &Ring{Start: start, End: end, Peer: peer, Entries: make([]*entry, 0), onUpdate: f}
|
||||||
ring.updateExportedVariables()
|
|
||||||
return ring
|
return ring
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +150,6 @@ func (r *Ring) GrantRangeToHost(start, end address.Address, peer mesh.PeerName)
|
|||||||
r.assertInvariants()
|
r.assertInvariants()
|
||||||
defer r.trackUpdates()()
|
defer r.trackUpdates()()
|
||||||
defer r.assertInvariants()
|
defer r.assertInvariants()
|
||||||
defer r.updateExportedVariables()
|
|
||||||
|
|
||||||
// ----------------- Start of Checks -----------------
|
// ----------------- Start of Checks -----------------
|
||||||
|
|
||||||
@@ -218,7 +216,6 @@ func (r *Ring) GrantRangeToHost(start, end address.Address, peer mesh.PeerName)
|
|||||||
func (r *Ring) Merge(gossip Ring) (bool, error) {
|
func (r *Ring) Merge(gossip Ring) (bool, error) {
|
||||||
r.assertInvariants()
|
r.assertInvariants()
|
||||||
defer r.trackUpdates()()
|
defer r.trackUpdates()()
|
||||||
defer r.updateExportedVariables()
|
|
||||||
|
|
||||||
// Don't panic when checking the gossiped in ring.
|
// Don't panic when checking the gossiped in ring.
|
||||||
// In this case just return any error found.
|
// In this case just return any error found.
|
||||||
@@ -404,7 +401,6 @@ func (r *Ring) ClaimForPeers(peers []mesh.PeerName) {
|
|||||||
|
|
||||||
defer r.trackUpdates()()
|
defer r.trackUpdates()()
|
||||||
defer r.assertInvariants()
|
defer r.assertInvariants()
|
||||||
defer r.updateExportedVariables()
|
|
||||||
defer func() {
|
defer func() {
|
||||||
e := r.Entries[len(r.Entries)-1]
|
e := r.Entries[len(r.Entries)-1]
|
||||||
common.Assert(address.Add(e.Token, address.Offset(e.Free)) == r.End)
|
common.Assert(address.Add(e.Token, address.Offset(e.Free)) == r.End)
|
||||||
@@ -455,7 +451,6 @@ func (r *Ring) String() string {
|
|||||||
func (r *Ring) ReportFree(freespace map[address.Address]address.Count) (updated bool) {
|
func (r *Ring) ReportFree(freespace map[address.Address]address.Count) (updated bool) {
|
||||||
r.assertInvariants()
|
r.assertInvariants()
|
||||||
defer r.assertInvariants()
|
defer r.assertInvariants()
|
||||||
defer r.updateExportedVariables()
|
|
||||||
|
|
||||||
common.Assert(!r.Empty())
|
common.Assert(!r.Empty())
|
||||||
entries := r.Entries
|
entries := r.Entries
|
||||||
@@ -564,7 +559,6 @@ func (r *Ring) Transfer(from, to mesh.PeerName) []address.Range {
|
|||||||
defer r.trackUpdates()()
|
defer r.trackUpdates()()
|
||||||
defer r.trackUpdatesOfPeer(from)()
|
defer r.trackUpdatesOfPeer(from)()
|
||||||
defer r.assertInvariants()
|
defer r.assertInvariants()
|
||||||
defer r.updateExportedVariables()
|
|
||||||
|
|
||||||
var newRanges []address.Range
|
var newRanges []address.Range
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,14 @@ func (s *Space) Claim(addr address.Address) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Space) NumOwnedAddresses() address.Count {
|
||||||
|
res := address.Count(0)
|
||||||
|
for i := 0; i < len(s.ours); i += 2 {
|
||||||
|
res += address.Length(s.ours[i+1], s.ours[i])
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Space) NumFreeAddresses() address.Count {
|
func (s *Space) NumFreeAddresses() address.Count {
|
||||||
res := address.Count(0)
|
res := address.Count(0)
|
||||||
for i := 0; i < len(s.free); i += 2 {
|
for i := 0; i < len(s.free); i += 2 {
|
||||||
|
|||||||
@@ -98,16 +98,19 @@ func TestSpaceAllocate(t *testing.T) {
|
|||||||
|
|
||||||
space1 := makeSpace(start, size)
|
space1 := makeSpace(start, size)
|
||||||
require.Equal(t, address.Count(20), space1.NumFreeAddresses())
|
require.Equal(t, address.Count(20), space1.NumFreeAddresses())
|
||||||
|
require.Equal(t, address.Count(0), space1.NumOwnedAddresses())
|
||||||
space1.assertInvariants()
|
space1.assertInvariants()
|
||||||
|
|
||||||
_, addr1 := space1.Allocate(address.NewRange(start, size))
|
_, addr1 := space1.Allocate(address.NewRange(start, size))
|
||||||
require.Equal(t, testAddr1, addr1.String(), "address")
|
require.Equal(t, testAddr1, addr1.String(), "address")
|
||||||
require.Equal(t, address.Count(19), space1.NumFreeAddresses())
|
require.Equal(t, address.Count(19), space1.NumFreeAddresses())
|
||||||
|
require.Equal(t, address.Count(1), space1.NumOwnedAddresses())
|
||||||
space1.assertInvariants()
|
space1.assertInvariants()
|
||||||
|
|
||||||
_, addr2 := space1.Allocate(address.NewRange(start, size))
|
_, addr2 := space1.Allocate(address.NewRange(start, size))
|
||||||
require.False(t, addr2.String() == testAddr1, "address")
|
require.False(t, addr2.String() == testAddr1, "address")
|
||||||
require.Equal(t, address.Count(18), space1.NumFreeAddresses())
|
require.Equal(t, address.Count(18), space1.NumFreeAddresses())
|
||||||
|
require.Equal(t, address.Count(2), space1.NumOwnedAddresses())
|
||||||
require.Equal(t, address.Count(13), space1.NumFreeAddressesInRange(address.Range{Start: ip(testAddr1), End: ip(testAddrx)}))
|
require.Equal(t, address.Count(13), space1.NumFreeAddressesInRange(address.Range{Start: ip(testAddr1), End: ip(testAddrx)}))
|
||||||
require.Equal(t, address.Count(18), space1.NumFreeAddressesInRange(address.Range{Start: ip(testAddr1), End: ip(testAddry)}))
|
require.Equal(t, address.Count(18), space1.NumFreeAddressesInRange(address.Range{Start: ip(testAddr1), End: ip(testAddry)}))
|
||||||
space1.assertInvariants()
|
space1.assertInvariants()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ type Status struct {
|
|||||||
Paxos *paxos.Status
|
Paxos *paxos.Status
|
||||||
Range string
|
Range string
|
||||||
RangeNumIPs int
|
RangeNumIPs int
|
||||||
|
ActiveIPs int
|
||||||
DefaultSubnet string
|
DefaultSubnet string
|
||||||
Entries []EntryStatus
|
Entries []EntryStatus
|
||||||
PendingClaims []ClaimStatus
|
PendingClaims []ClaimStatus
|
||||||
@@ -52,6 +53,7 @@ func NewStatus(allocator *Allocator, defaultSubnet address.CIDR) *Status {
|
|||||||
paxosStatus,
|
paxosStatus,
|
||||||
allocator.universe.String(),
|
allocator.universe.String(),
|
||||||
int(allocator.universe.Size()),
|
int(allocator.universe.Size()),
|
||||||
|
int(allocator.space.NumOwnedAddresses()),
|
||||||
defaultSubnet.String(),
|
defaultSubnet.String(),
|
||||||
newEntryStatusSlice(allocator),
|
newEntryStatusSlice(allocator),
|
||||||
newClaimStatusSlice(allocator),
|
newClaimStatusSlice(allocator),
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -51,3 +54,35 @@ func isDatapath(link netlink.Link) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DetectHairpin(portIfName string, log *logrus.Logger) error {
|
||||||
|
link, err := netlink.LinkByName(portIfName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to find link %q: %s", portIfName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan netlink.LinkUpdate)
|
||||||
|
// See EnsureInterface for why done channel is not passed
|
||||||
|
if err := netlink.LinkSubscribe(ch, nil); err != nil {
|
||||||
|
return fmt.Errorf("Unable to subscribe to netlink updates: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pi, err := netlink.LinkGetProtinfo(link)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to get link protinfo %q: %s", portIfName, err)
|
||||||
|
}
|
||||||
|
if pi.Hairpin {
|
||||||
|
return fmt.Errorf("Hairpin mode enabled on %q", portIfName)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for up := range ch {
|
||||||
|
if up.Attrs().Name == portIfName && up.Attrs().Protinfo != nil &&
|
||||||
|
up.Attrs().Protinfo.Hairpin {
|
||||||
|
log.Errorf("Hairpin mode enabled on %q", portIfName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
"github.com/vishvananda/netns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Dev struct {
|
||||||
|
Name string `json:"Name,omitempty"`
|
||||||
|
MAC net.HardwareAddr `json:"MAC,omitempty"`
|
||||||
|
CIDRs []*net.IPNet `json:"CIDRs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func LinkToNetDev(link netlink.Link) (Dev, error) {
|
||||||
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
|
if err != nil {
|
||||||
|
return Dev{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
netDev := Dev{Name: link.Attrs().Name, MAC: link.Attrs().HardwareAddr}
|
||||||
|
for _, addr := range addrs {
|
||||||
|
netDev.CIDRs = append(netDev.CIDRs, addr.IPNet)
|
||||||
|
}
|
||||||
|
return netDev, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectedToBridgeVethPeerIds returns peer indexes of veth links connected to
|
||||||
|
// the given bridge. The peer index is used to query from a container netns
|
||||||
|
// whether the container is connected to the bridge.
|
||||||
|
func ConnectedToBridgeVethPeerIds(bridgeName string) ([]int, error) {
|
||||||
|
var ids []int
|
||||||
|
|
||||||
|
br, err := netlink.LinkByName(bridgeName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
links, err := netlink.LinkList()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, link := range links {
|
||||||
|
if _, isveth := link.(*netlink.Veth); isveth && link.Attrs().MasterIndex == br.Attrs().Index {
|
||||||
|
peerID := link.Attrs().ParentIndex
|
||||||
|
if peerID == 0 {
|
||||||
|
// perhaps running on an older kernel where ParentIndex doesn't work.
|
||||||
|
// as fall-back, assume the peers are consecutive
|
||||||
|
peerID = link.Attrs().Index - 1
|
||||||
|
}
|
||||||
|
ids = append(ids, peerID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup the weave interface of a container
|
||||||
|
func GetWeaveNetDevs(processID int) ([]Dev, error) {
|
||||||
|
peerIDs, err := ConnectedToBridgeVethPeerIds("weave")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetNetDevsByVethPeerIds(processID, peerIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNetDevsByVethPeerIds(processID int, peerIDs []int) ([]Dev, error) {
|
||||||
|
// Bail out if this container is running in the root namespace
|
||||||
|
netnsRoot, err := netns.GetFromPid(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to open root namespace: %s", err)
|
||||||
|
}
|
||||||
|
defer netnsRoot.Close()
|
||||||
|
netnsContainer, err := netns.GetFromPid(processID)
|
||||||
|
if err != nil {
|
||||||
|
// Unable to find a namespace for this process - just return nothing
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unable to open process %d namespace: %s", processID, err)
|
||||||
|
}
|
||||||
|
defer netnsContainer.Close()
|
||||||
|
if netnsRoot.Equal(netnsContainer) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var netdevs []Dev
|
||||||
|
peersStr := make([]string, len(peerIDs))
|
||||||
|
for i, id := range peerIDs {
|
||||||
|
peersStr[i] = strconv.Itoa(id)
|
||||||
|
}
|
||||||
|
nds, err := WithNetNSByPid(processID, "list-netdevs", peersStr...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("list-netdevs failed: %s", err)
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(nds, &netdevs)
|
||||||
|
|
||||||
|
return netdevs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the weave bridge interface.
|
||||||
|
// NB: Should be called from the root network namespace.
|
||||||
|
func GetBridgeNetDev(bridgeName string) (Dev, error) {
|
||||||
|
link, err := netlink.LinkByName(bridgeName)
|
||||||
|
if err != nil {
|
||||||
|
return Dev{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return LinkToNetDev(link)
|
||||||
|
}
|
||||||
+6
-14
@@ -57,33 +57,25 @@ func WithNetNSLinkUnsafe(ns netns.NsHandle, ifName string, work func(link netlin
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithNetNSLinkByPidUnsafe(pid int, ifName string, work func(link netlink.Link) error) error {
|
var WeaveUtilCmd = "weaveutil"
|
||||||
ns, err := netns.GetFromPid(pid)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer ns.Close()
|
|
||||||
|
|
||||||
return WithNetNSLinkUnsafe(ns, ifName, work)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A safe version of WithNetNS* which creates a process executing
|
// A safe version of WithNetNS* which creates a process executing
|
||||||
// "nsenter --net=<ns-path> weaveutil <cmd> [args]".
|
// "nsenter --net=<ns-path> weaveutil <cmd> [args]".
|
||||||
func WithNetNS(nsPath string, cmd string, args ...string) (string, error) {
|
func WithNetNS(nsPath string, cmd string, args ...string) ([]byte, error) {
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
|
|
||||||
args = append([]string{"--net=" + nsPath, "weaveutil", cmd}, args...)
|
args = append([]string{"--net=" + nsPath, WeaveUtilCmd, cmd}, args...)
|
||||||
c := exec.Command("nsenter", args...)
|
c := exec.Command("nsenter", args...)
|
||||||
c.Stdout = &stdout
|
c.Stdout = &stdout
|
||||||
c.Stderr = &stderr
|
c.Stderr = &stderr
|
||||||
if err := c.Run(); err != nil {
|
if err := c.Run(); err != nil {
|
||||||
return "", fmt.Errorf("%s: %s", string(stderr.Bytes()), err)
|
return nil, fmt.Errorf("%s: %s", string(stderr.Bytes()), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(stdout.Bytes()), nil
|
return stdout.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithNetNSByPid(pid int, cmd string, args ...string) (string, error) {
|
func WithNetNSByPid(pid int, cmd string, args ...string) ([]byte, error) {
|
||||||
return WithNetNS(NSPathByPid(pid), cmd, args...)
|
return WithNetNS(NSPathByPid(pid), cmd, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+59
-20
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
@@ -105,21 +106,26 @@ const (
|
|||||||
vethPrefix = "v" + VethName // starts with "veth" to suppress UI notifications
|
vethPrefix = "v" + VethName // starts with "veth" to suppress UI notifications
|
||||||
)
|
)
|
||||||
|
|
||||||
func interfaceExistsInNamespace(ns netns.NsHandle, ifName string) bool {
|
func interfaceExistsInNamespace(netNSPath string, ifName string) bool {
|
||||||
err := WithNetNSUnsafe(ns, func() error {
|
_, err := WithNetNS(netNSPath, "check-iface", ifName)
|
||||||
_, err := netlink.LinkByName(ifName)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int, withMulticastRoute bool, cidrs []*net.IPNet, keepTXOn bool) error {
|
// NB: This function can be used only by a process that terminates immediately
|
||||||
|
// after calling the function as it changes netns via WithNetNSLinkUnsafe.
|
||||||
|
func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMulticastRoute bool, cidrs []*net.IPNet, keepTXOn bool) error {
|
||||||
|
ns, err := netns.GetFromPath(netNSPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer ns.Close()
|
||||||
|
|
||||||
ipt, err := iptables.New()
|
ipt, err := iptables.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !interfaceExistsInNamespace(ns, ifName) {
|
if !interfaceExistsInNamespace(netNSPath, ifName) {
|
||||||
maxIDLen := IFNAMSIZ - 1 - len(vethPrefix+"pl")
|
maxIDLen := IFNAMSIZ - 1 - len(vethPrefix+"pl")
|
||||||
if len(id) > maxIDLen {
|
if len(id) > maxIDLen {
|
||||||
id = id[:maxIDLen] // trim passed ID if too long
|
id = id[:maxIDLen] // trim passed ID if too long
|
||||||
@@ -129,18 +135,7 @@ func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int,
|
|||||||
if err := netlink.LinkSetNsFd(veth, int(ns)); err != nil {
|
if err := netlink.LinkSetNsFd(veth, int(ns)); err != nil {
|
||||||
return fmt.Errorf("failed to move veth to container netns: %s", err)
|
return fmt.Errorf("failed to move veth to container netns: %s", err)
|
||||||
}
|
}
|
||||||
if err := WithNetNSUnsafe(ns, func() error {
|
if _, err := WithNetNS(netNSPath, "setup-iface", peerName, ifName); err != nil {
|
||||||
if err := netlink.LinkSetName(veth, ifName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := ConfigureARPCache(ifName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := ipt.Append("filter", "INPUT", "-i", ifName, "-d", "224.0.0.0/4", "-j", "DROP"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("error setting up interface: %s", err)
|
return fmt.Errorf("error setting up interface: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -201,7 +196,15 @@ func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DetachContainer(ns netns.NsHandle, id, ifName string, cidrs []*net.IPNet) error {
|
// NB: This function can be used only by a process that terminates immediately
|
||||||
|
// after calling the function as it changes netns via WithNetNSLinkUnsafe.
|
||||||
|
func DetachContainer(netNSPath, id, ifName string, cidrs []*net.IPNet) error {
|
||||||
|
ns, err := netns.GetFromPath(netNSPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer ns.Close()
|
||||||
|
|
||||||
ipt, err := iptables.New()
|
ipt, err := iptables.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -269,3 +272,39 @@ 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
|
||||||
|
}
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
package npc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/client-go/pkg/api"
|
||||||
|
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||||
|
"k8s.io/client-go/pkg/util/intstr"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/npc/ipset"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ns *ns) analysePolicy(policy *extnapi.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)
|
||||||
|
|
||||||
|
dstSelector, err := newSelectorSpec(&policy.Spec.PodSelector, ns.name, ipset.HashIP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
podSelectors[dstSelector.key] = dstSelector
|
||||||
|
|
||||||
|
for _, ingressRule := range policy.Spec.Ingress {
|
||||||
|
if ingressRule.Ports != nil && len(ingressRule.Ports) == 0 {
|
||||||
|
// Ports is empty, this rule matches no ports (no traffic matches).
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ingressRule.From != nil && len(ingressRule.From) == 0 {
|
||||||
|
// From is empty, this rule matches no sources (no traffic matches).
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ingressRule.From == nil {
|
||||||
|
// From is not provided, this rule matches all sources (traffic not restricted by source).
|
||||||
|
if ingressRule.Ports == nil {
|
||||||
|
// Ports is not provided, this rule matches all ports (traffic not restricted by port).
|
||||||
|
rule := newRuleSpec(nil, nil, dstSelector, nil)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
} else {
|
||||||
|
// Ports is present and contains at least one item, then this rule allows traffic
|
||||||
|
// only if the traffic matches at least one port in the ports list.
|
||||||
|
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
||||||
|
rule := newRuleSpec(&proto, nil, dstSelector, &port)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// From is present and contains at least on item, this rule allows traffic only if the
|
||||||
|
// traffic matches at least one item in the from list.
|
||||||
|
for _, peer := range ingressRule.From {
|
||||||
|
var srcSelector *selectorSpec
|
||||||
|
if peer.PodSelector != nil {
|
||||||
|
srcSelector, err = newSelectorSpec(peer.PodSelector, ns.name, ipset.HashIP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
podSelectors[srcSelector.key] = srcSelector
|
||||||
|
}
|
||||||
|
if peer.NamespaceSelector != nil {
|
||||||
|
srcSelector, err = newSelectorSpec(peer.NamespaceSelector, "", ipset.ListSet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
nsSelectors[srcSelector.key] = srcSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
if ingressRule.Ports == nil {
|
||||||
|
// Ports is not provided, this rule matches all ports (traffic not restricted by port).
|
||||||
|
rule := newRuleSpec(nil, srcSelector, dstSelector, nil)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
} else {
|
||||||
|
// Ports is present and contains at least one item, then this rule allows traffic
|
||||||
|
// only if the traffic matches at least one port in the ports list.
|
||||||
|
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
||||||
|
rule := newRuleSpec(&proto, srcSelector, dstSelector, &port)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules, nsSelectors, podSelectors, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func withNormalisedProtoAndPort(npps []extnapi.NetworkPolicyPort, f func(proto, port string)) {
|
||||||
|
for _, npp := range npps {
|
||||||
|
// If no proto is specified, default to TCP
|
||||||
|
proto := string(api.ProtocolTCP)
|
||||||
|
if npp.Protocol != nil {
|
||||||
|
proto = string(*npp.Protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no port is specified, match any port. Let iptables executable handle
|
||||||
|
// service name resolution
|
||||||
|
port := "0:65535"
|
||||||
|
if npp.Port != nil {
|
||||||
|
switch npp.Port.Type {
|
||||||
|
case intstr.Int:
|
||||||
|
port = fmt.Sprintf("%d", npp.Port.IntVal)
|
||||||
|
case intstr.String:
|
||||||
|
port = npp.Port.StrVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f(proto, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package npc
|
||||||
|
|
||||||
|
// These types are defined in https://github.com/kubernetes/kubernetes/blob/master/docs/proposals/network-policy.md
|
||||||
|
// but not in the k8s API yet. Copies are included here for decoding the `net.beta.kubernetes.io/network-policy`
|
||||||
|
// annotation specified in the above document.
|
||||||
|
|
||||||
|
type IngressIsolationPolicy string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Deny all ingress traffic to pods in this namespace. Ingress means
|
||||||
|
// any incoming traffic to pods, whether that be from other pods within this namespace
|
||||||
|
// or any source outside of this namespace.
|
||||||
|
DefaultDeny IngressIsolationPolicy = "DefaultDeny"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Standard NamespaceSpec object, modified to include a new
|
||||||
|
// NamespaceNetworkPolicy field.
|
||||||
|
type NamespaceSpec struct {
|
||||||
|
// This is a pointer so that it can be left undefined.
|
||||||
|
NetworkPolicy *NamespaceNetworkPolicy `json:"networkPolicy,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NamespaceNetworkPolicy struct {
|
||||||
|
// Ingress configuration for this namespace. This config is
|
||||||
|
// applied to all pods within this namespace. For now, only
|
||||||
|
// ingress is supported. This field is optional - if not
|
||||||
|
// defined, then the cluster default for ingress is applied.
|
||||||
|
Ingress *NamespaceIngressPolicy `json:"ingress,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configuration for ingress to pods within this namespace.
|
||||||
|
// For now, this only supports specifying an isolation policy.
|
||||||
|
type NamespaceIngressPolicy struct {
|
||||||
|
// The isolation policy to apply to pods in this namespace.
|
||||||
|
// Currently this field only supports "DefaultDeny", but could
|
||||||
|
// be extended to support other policies in the future. When set to DefaultDeny,
|
||||||
|
// pods in this namespace are denied ingress traffic by default. When not defined,
|
||||||
|
// the cluster default ingress isolation policy is applied (currently allow all).
|
||||||
|
Isolation *IngressIsolationPolicy `json:"isolation,omitempty"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package npc
|
||||||
|
|
||||||
|
const (
|
||||||
|
TableFilter = "filter"
|
||||||
|
|
||||||
|
MainChain = "WEAVE-NPC"
|
||||||
|
DefaultChain = "WEAVE-NPC-DEFAULT"
|
||||||
|
IngressChain = "WEAVE-NPC-INGRESS"
|
||||||
|
)
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
package npc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
coreapi "k8s.io/client-go/pkg/api/v1"
|
||||||
|
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/npc/ipset"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NetworkPolicyController interface {
|
||||||
|
AddNamespace(ns *coreapi.Namespace) error
|
||||||
|
UpdateNamespace(oldObj, newObj *coreapi.Namespace) error
|
||||||
|
DeleteNamespace(ns *coreapi.Namespace) error
|
||||||
|
|
||||||
|
AddPod(obj *coreapi.Pod) error
|
||||||
|
UpdatePod(oldObj, newObj *coreapi.Pod) error
|
||||||
|
DeletePod(obj *coreapi.Pod) error
|
||||||
|
|
||||||
|
AddNetworkPolicy(obj *extnapi.NetworkPolicy) error
|
||||||
|
UpdateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error
|
||||||
|
DeleteNetworkPolicy(obj *extnapi.NetworkPolicy) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type controller struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
ipt *iptables.IPTables
|
||||||
|
ips ipset.Interface
|
||||||
|
|
||||||
|
nss map[string]*ns // ns name -> ns struct
|
||||||
|
nsSelectors *selectorSet // selector string -> nsSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ipt *iptables.IPTables, ips ipset.Interface) NetworkPolicyController {
|
||||||
|
c := &controller{
|
||||||
|
ipt: ipt,
|
||||||
|
ips: ips,
|
||||||
|
nss: make(map[string]*ns)}
|
||||||
|
|
||||||
|
c.nsSelectors = newSelectorSet(ips, c.onNewNsSelector)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (npc *controller) onNewNsSelector(selector *selector) error {
|
||||||
|
for _, ns := range npc.nss {
|
||||||
|
if ns.namespace != nil {
|
||||||
|
if selector.matches(ns.namespace.ObjectMeta.Labels) {
|
||||||
|
if err := ns.ips.AddEntry(selector.spec.ipsetName, string(ns.allPods.ipsetName)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (npc *controller) withNS(name string, f func(ns *ns) error) error {
|
||||||
|
ns, found := npc.nss[name]
|
||||||
|
if !found {
|
||||||
|
newNs, err := newNS(name, npc.ipt, npc.ips, npc.nsSelectors)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
npc.nss[name] = newNs
|
||||||
|
ns = newNs
|
||||||
|
}
|
||||||
|
if err := f(ns); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ns.empty() {
|
||||||
|
if err := ns.destroy(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
delete(npc.nss, name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (npc *controller) AddPod(obj *coreapi.Pod) error {
|
||||||
|
npc.Lock()
|
||||||
|
defer npc.Unlock()
|
||||||
|
|
||||||
|
log.Infof("EVENT AddPod %#v", obj)
|
||||||
|
return npc.withNS(obj.ObjectMeta.Namespace, func(ns *ns) error {
|
||||||
|
return errors.Wrap(ns.addPod(obj), "add pod")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (npc *controller) UpdatePod(oldObj, newObj *coreapi.Pod) error {
|
||||||
|
npc.Lock()
|
||||||
|
defer npc.Unlock()
|
||||||
|
|
||||||
|
log.Infof("EVENT UpdatePod %#v %#v", oldObj, newObj)
|
||||||
|
return npc.withNS(oldObj.ObjectMeta.Namespace, func(ns *ns) error {
|
||||||
|
return errors.Wrap(ns.updatePod(oldObj, newObj), "update pod")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (npc *controller) DeletePod(obj *coreapi.Pod) error {
|
||||||
|
npc.Lock()
|
||||||
|
defer npc.Unlock()
|
||||||
|
|
||||||
|
log.Infof("EVENT DeletePod %#v", obj)
|
||||||
|
return npc.withNS(obj.ObjectMeta.Namespace, func(ns *ns) error {
|
||||||
|
return errors.Wrap(ns.deletePod(obj), "delete pod")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (npc *controller) AddNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
||||||
|
npc.Lock()
|
||||||
|
defer npc.Unlock()
|
||||||
|
|
||||||
|
log.Infof("EVENT AddNetworkPolicy %#v", obj)
|
||||||
|
return npc.withNS(obj.ObjectMeta.Namespace, func(ns *ns) error {
|
||||||
|
return errors.Wrap(ns.addNetworkPolicy(obj), "add network policy")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (npc *controller) UpdateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error {
|
||||||
|
npc.Lock()
|
||||||
|
defer npc.Unlock()
|
||||||
|
|
||||||
|
log.Infof("EVENT UpdateNetworkPolicy %#v %#v", oldObj, newObj)
|
||||||
|
return npc.withNS(oldObj.ObjectMeta.Namespace, func(ns *ns) error {
|
||||||
|
return errors.Wrap(ns.updateNetworkPolicy(oldObj, newObj), "update network policy")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (npc *controller) DeleteNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
||||||
|
npc.Lock()
|
||||||
|
defer npc.Unlock()
|
||||||
|
|
||||||
|
log.Infof("EVENT DeleteNetworkPolicy %#v", obj)
|
||||||
|
return npc.withNS(obj.ObjectMeta.Namespace, func(ns *ns) error {
|
||||||
|
return errors.Wrap(ns.deleteNetworkPolicy(obj), "delete network policy")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (npc *controller) AddNamespace(obj *coreapi.Namespace) error {
|
||||||
|
npc.Lock()
|
||||||
|
defer npc.Unlock()
|
||||||
|
|
||||||
|
log.Infof("EVENT AddNamespace %#v", obj)
|
||||||
|
return npc.withNS(obj.ObjectMeta.Name, func(ns *ns) error {
|
||||||
|
return errors.Wrap(ns.addNamespace(obj), "add namespace")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (npc *controller) UpdateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
||||||
|
npc.Lock()
|
||||||
|
defer npc.Unlock()
|
||||||
|
|
||||||
|
log.Infof("EVENT UpdateNamespace%#v %#v", oldObj, newObj)
|
||||||
|
return npc.withNS(oldObj.ObjectMeta.Name, func(ns *ns) error {
|
||||||
|
return errors.Wrap(ns.updateNamespace(oldObj, newObj), "update namespace")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (npc *controller) DeleteNamespace(obj *coreapi.Namespace) error {
|
||||||
|
npc.Lock()
|
||||||
|
defer npc.Unlock()
|
||||||
|
|
||||||
|
log.Infof("EVENT DeleteNamespace %#v", obj)
|
||||||
|
return npc.withNS(obj.ObjectMeta.Name, func(ns *ns) error {
|
||||||
|
return errors.Wrap(ns.deleteNamespace(obj), "delete namespace")
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package ipset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Name string
|
||||||
|
|
||||||
|
type Type string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ListSet = Type("list:set")
|
||||||
|
HashIP = Type("hash:ip")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Interface interface {
|
||||||
|
Create(ipsetName Name, ipsetType Type) error
|
||||||
|
AddEntry(ipsetName Name, entry string) error
|
||||||
|
DelEntry(ipsetName Name, entry string) error
|
||||||
|
Flush(ipsetName Name) error
|
||||||
|
Destroy(ipsetName Name) error
|
||||||
|
|
||||||
|
FlushAll() error
|
||||||
|
DestroyAll() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type ipset struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() Interface {
|
||||||
|
return &ipset{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) Create(ipsetName Name, ipsetType Type) error {
|
||||||
|
return doExec("create", string(ipsetName), string(ipsetType))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) AddEntry(ipsetName Name, entry string) error {
|
||||||
|
return doExec("add", string(ipsetName), entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) DelEntry(ipsetName Name, entry string) error {
|
||||||
|
return doExec("del", string(ipsetName), entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) Flush(ipsetName Name) error {
|
||||||
|
return doExec("flush", string(ipsetName))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) FlushAll() error {
|
||||||
|
return doExec("flush")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) Destroy(ipsetName Name) error {
|
||||||
|
return doExec("destroy", string(ipsetName))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) DestroyAll() error {
|
||||||
|
return doExec("destroy")
|
||||||
|
}
|
||||||
|
|
||||||
|
func doExec(args ...string) error {
|
||||||
|
if output, err := exec.Command("ipset", args...).CombinedOutput(); err != nil {
|
||||||
|
return errors.Wrapf(err, "ipset %v failed: %s", args, output)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
"github.com/google/gopacket/pcapgo"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
blockedConnections = prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "weavenpc_blocked_connections_total",
|
||||||
|
Help: "Connection attempts blocked by policy controller.",
|
||||||
|
},
|
||||||
|
[]string{"protocol", "dport"},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
func gatherMetrics() {
|
||||||
|
pipe, err := os.Open("/var/log/ulogd.pcap")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to open pcap: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reader, err := pcapgo.NewReader(pipe)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to read pcap header: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
data, _, err := reader.ReadPacketData()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to read pcap packet: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default)
|
||||||
|
|
||||||
|
if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
|
||||||
|
tcp, _ := tcpLayer.(*layers.TCP)
|
||||||
|
if tcp.SYN && !tcp.ACK { // Only plain SYN constitutes a NEW TCP connection
|
||||||
|
blockedConnections.With(prometheus.Labels{"protocol": "tcp", "dport": strconv.Itoa(int(tcp.DstPort))}).Inc()
|
||||||
|
log.Warnf("TCP connection from %v:%d to %v:%d blocked by Weave NPC.", srcIP(packet), tcp.SrcPort, dstIP(packet), tcp.DstPort)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if udpLayer := packet.Layer(layers.LayerTypeUDP); udpLayer != nil {
|
||||||
|
udp, _ := udpLayer.(*layers.UDP)
|
||||||
|
blockedConnections.With(prometheus.Labels{"protocol": "udp", "dport": strconv.Itoa(int(udp.DstPort))}).Inc()
|
||||||
|
log.Warnf("UDP connection from %v:%d to %v:%d blocked by Weave NPC.", srcIP(packet), udp.SrcPort, dstIP(packet), udp.DstPort)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const unknownIP string = "<unknown IP>"
|
||||||
|
|
||||||
|
func srcIP(packet gopacket.Packet) string {
|
||||||
|
if layer := packet.Layer(layers.LayerTypeIPv4); layer != nil {
|
||||||
|
if ip, ok := layer.(*layers.IPv4); ok {
|
||||||
|
return ip.SrcIP.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if layer := packet.Layer(layers.LayerTypeIPv6); layer != nil {
|
||||||
|
if ip, ok := layer.(*layers.IPv6); ok {
|
||||||
|
return ip.SrcIP.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unknownIP
|
||||||
|
}
|
||||||
|
|
||||||
|
func dstIP(packet gopacket.Packet) string {
|
||||||
|
if layer := packet.Layer(layers.LayerTypeIPv4); layer != nil {
|
||||||
|
if ip, ok := layer.(*layers.IPv4); ok {
|
||||||
|
return ip.DstIP.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if layer := packet.Layer(layers.LayerTypeIPv6); layer != nil {
|
||||||
|
if ip, ok := layer.(*layers.IPv6); ok {
|
||||||
|
return ip.DstIP.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unknownIP
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start(addr string) error {
|
||||||
|
if err := prometheus.Register(blockedConnections); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Handle("/metrics", promhttp.Handler())
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Infof("Serving /metrics on %s", addr)
|
||||||
|
if err := http.ListenAndServe(addr, nil); err != nil {
|
||||||
|
log.Fatalf("Failed to bind metrics server: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go gatherMetrics()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,338 @@
|
|||||||
|
package npc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
|
"k8s.io/client-go/pkg/api/unversioned"
|
||||||
|
coreapi "k8s.io/client-go/pkg/api/v1"
|
||||||
|
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||||
|
"k8s.io/client-go/pkg/types"
|
||||||
|
"k8s.io/client-go/pkg/util/uuid"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/npc/ipset"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ns struct {
|
||||||
|
ipt *iptables.IPTables // interface to iptables
|
||||||
|
ips ipset.Interface // interface to ipset
|
||||||
|
|
||||||
|
name string // k8s Namespace name
|
||||||
|
namespace *coreapi.Namespace // k8s Namespace object
|
||||||
|
pods map[types.UID]*coreapi.Pod // k8s Pod objects by UID
|
||||||
|
policies map[types.UID]*extnapi.NetworkPolicy // k8s NetworkPolicy objects by UID
|
||||||
|
|
||||||
|
uid types.UID // surrogate UID to own allPods selector
|
||||||
|
allPods *selectorSpec // hash:ip ipset of all pod IPs in this namespace
|
||||||
|
|
||||||
|
nsSelectors *selectorSet
|
||||||
|
podSelectors *selectorSet
|
||||||
|
rules *ruleSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNS(name string, ipt *iptables.IPTables, ips ipset.Interface, nsSelectors *selectorSet) (*ns, error) {
|
||||||
|
allPods, err := newSelectorSpec(&unversioned.LabelSelector{}, name, ipset.HashIP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ns := &ns{
|
||||||
|
ipt: ipt,
|
||||||
|
ips: ips,
|
||||||
|
name: name,
|
||||||
|
pods: make(map[types.UID]*coreapi.Pod),
|
||||||
|
policies: make(map[types.UID]*extnapi.NetworkPolicy),
|
||||||
|
uid: uuid.NewUUID(),
|
||||||
|
allPods: allPods,
|
||||||
|
nsSelectors: nsSelectors,
|
||||||
|
rules: newRuleSet(ipt)}
|
||||||
|
|
||||||
|
ns.podSelectors = newSelectorSet(ips, ns.onNewPodSelector)
|
||||||
|
|
||||||
|
if err := ns.podSelectors.provision(ns.uid, nil, map[string]*selectorSpec{ns.allPods.key: ns.allPods}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) empty() bool {
|
||||||
|
return len(ns.pods) == 0 && len(ns.policies) == 0 && ns.namespace == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) destroy() error {
|
||||||
|
if err := ns.podSelectors.deprovision(ns.uid, map[string]*selectorSpec{ns.allPods.key: ns.allPods}, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) onNewPodSelector(selector *selector) error {
|
||||||
|
for _, pod := range ns.pods {
|
||||||
|
if hasIP(pod) {
|
||||||
|
if selector.matches(pod.ObjectMeta.Labels) {
|
||||||
|
if err := selector.addEntry(pod.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) addPod(obj *coreapi.Pod) error {
|
||||||
|
ns.pods[obj.ObjectMeta.UID] = obj
|
||||||
|
|
||||||
|
if !hasIP(obj) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ns.podSelectors.addToMatching(obj.ObjectMeta.Labels, obj.Status.PodIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||||
|
delete(ns.pods, oldObj.ObjectMeta.UID)
|
||||||
|
ns.pods[newObj.ObjectMeta.UID] = newObj
|
||||||
|
|
||||||
|
if !hasIP(oldObj) && !hasIP(newObj) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasIP(oldObj) && !hasIP(newObj) {
|
||||||
|
return ns.podSelectors.delFromMatching(oldObj.ObjectMeta.Labels, oldObj.Status.PodIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasIP(oldObj) && hasIP(newObj) {
|
||||||
|
return ns.podSelectors.addToMatching(newObj.ObjectMeta.Labels, newObj.Status.PodIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !equals(oldObj.ObjectMeta.Labels, newObj.ObjectMeta.Labels) ||
|
||||||
|
oldObj.Status.PodIP != newObj.Status.PodIP {
|
||||||
|
|
||||||
|
for _, ps := range ns.podSelectors.entries {
|
||||||
|
oldMatch := ps.matches(oldObj.ObjectMeta.Labels)
|
||||||
|
newMatch := ps.matches(newObj.ObjectMeta.Labels)
|
||||||
|
if oldMatch == newMatch && oldObj.Status.PodIP == newObj.Status.PodIP {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if oldMatch {
|
||||||
|
if err := ps.delEntry(oldObj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newMatch {
|
||||||
|
if err := ps.addEntry(newObj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
||||||
|
delete(ns.pods, obj.ObjectMeta.UID)
|
||||||
|
|
||||||
|
if !hasIP(obj) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ns.podSelectors.delFromMatching(obj.ObjectMeta.Labels, obj.Status.PodIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) addNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
||||||
|
ns.policies[obj.ObjectMeta.UID] = obj
|
||||||
|
|
||||||
|
// Analyse policy, determine which rules and ipsets are required
|
||||||
|
rules, nsSelectors, podSelectors, err := ns.analysePolicy(obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provision required resources in dependency order
|
||||||
|
if err := ns.nsSelectors.provision(obj.ObjectMeta.UID, nil, nsSelectors); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.podSelectors.provision(obj.ObjectMeta.UID, nil, podSelectors); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.rules.provision(obj.ObjectMeta.UID, nil, rules); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) updateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) 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
|
||||||
|
oldRules, oldNsSelectors, oldPodSelectors, err := ns.analysePolicy(oldObj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newRules, newNsSelectors, newPodSelectors, err := ns.analysePolicy(newObj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprovision unused and provision newly required resources in dependency order
|
||||||
|
if err := ns.rules.deprovision(oldObj.ObjectMeta.UID, oldRules, newRules); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.nsSelectors.deprovision(oldObj.ObjectMeta.UID, oldNsSelectors, newNsSelectors); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.podSelectors.deprovision(oldObj.ObjectMeta.UID, oldPodSelectors, newPodSelectors); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.nsSelectors.provision(oldObj.ObjectMeta.UID, oldNsSelectors, newNsSelectors); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.podSelectors.provision(oldObj.ObjectMeta.UID, oldPodSelectors, newNsSelectors); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.rules.provision(oldObj.ObjectMeta.UID, oldRules, newRules); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) deleteNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
||||||
|
delete(ns.policies, obj.ObjectMeta.UID)
|
||||||
|
|
||||||
|
// Analyse network policy to free resources
|
||||||
|
rules, nsSelectors, podSelectors, err := ns.analysePolicy(obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprovision unused resources in dependency order
|
||||||
|
if err := ns.rules.deprovision(obj.ObjectMeta.UID, rules, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.nsSelectors.deprovision(obj.ObjectMeta.UID, nsSelectors, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.podSelectors.deprovision(obj.ObjectMeta.UID, podSelectors, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func bypassRule(nsIpsetName ipset.Name) []string {
|
||||||
|
return []string{"-m", "set", "--match-set", string(nsIpsetName), "dst", "-j", "ACCEPT"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) ensureBypassRule(nsIpsetName ipset.Name) error {
|
||||||
|
return ns.ipt.Append(TableFilter, DefaultChain, bypassRule(ns.allPods.ipsetName)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) deleteBypassRule(nsIpsetName ipset.Name) error {
|
||||||
|
return ns.ipt.Delete(TableFilter, DefaultChain, bypassRule(ns.allPods.ipsetName)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) addNamespace(obj *coreapi.Namespace) error {
|
||||||
|
ns.namespace = obj
|
||||||
|
|
||||||
|
// Insert a rule to bypass policies if namespace is DefaultAllow
|
||||||
|
if !isDefaultDeny(obj) {
|
||||||
|
return ns.ensureBypassRule(ns.allPods.ipsetName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add namespace ipset to matching namespace selectors
|
||||||
|
return ns.nsSelectors.addToMatching(obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
||||||
|
ns.namespace = newObj
|
||||||
|
|
||||||
|
// Update bypass rule if ingress default has changed
|
||||||
|
oldDefaultDeny := isDefaultDeny(oldObj)
|
||||||
|
newDefaultDeny := isDefaultDeny(newObj)
|
||||||
|
|
||||||
|
if oldDefaultDeny != newDefaultDeny {
|
||||||
|
if oldDefaultDeny {
|
||||||
|
return ns.ensureBypassRule(ns.allPods.ipsetName)
|
||||||
|
}
|
||||||
|
if newDefaultDeny {
|
||||||
|
return ns.deleteBypassRule(ns.allPods.ipsetName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-evaluate namespace selector membership if labels have changed
|
||||||
|
if !equals(oldObj.ObjectMeta.Labels, newObj.ObjectMeta.Labels) {
|
||||||
|
for _, selector := range ns.nsSelectors.entries {
|
||||||
|
oldMatch := selector.matches(oldObj.ObjectMeta.Labels)
|
||||||
|
newMatch := selector.matches(newObj.ObjectMeta.Labels)
|
||||||
|
if oldMatch == newMatch {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if oldMatch {
|
||||||
|
if err := selector.delEntry(string(ns.allPods.ipsetName)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newMatch {
|
||||||
|
if err := selector.addEntry(string(ns.allPods.ipsetName)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) deleteNamespace(obj *coreapi.Namespace) error {
|
||||||
|
ns.namespace = nil
|
||||||
|
|
||||||
|
// Remove bypass rule
|
||||||
|
if !isDefaultDeny(obj) {
|
||||||
|
return ns.deleteBypassRule(ns.allPods.ipsetName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove namespace ipset from any matching namespace selectors
|
||||||
|
return ns.nsSelectors.delFromMatching(obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasIP(pod *coreapi.Pod) bool {
|
||||||
|
// Ensure pod isn't dead, has an IP address and isn't sharing the host network namespace
|
||||||
|
return pod.Status.Phase != "Succeeded" && pod.Status.Phase != "Failed" &&
|
||||||
|
len(pod.Status.PodIP) > 0 && !pod.Spec.HostNetwork
|
||||||
|
}
|
||||||
|
|
||||||
|
func equals(a, b map[string]string) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for ak, av := range a {
|
||||||
|
if b[ak] != av {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
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
|
||||||
|
}
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
package npc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
|
"k8s.io/client-go/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ruleSpec struct {
|
||||||
|
key string
|
||||||
|
args []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRuleSpec(proto *string, srcHost *selectorSpec, dstHost *selectorSpec, dstPort *string) *ruleSpec {
|
||||||
|
args := []string{}
|
||||||
|
if proto != nil {
|
||||||
|
args = append(args, "-p", *proto)
|
||||||
|
}
|
||||||
|
if srcHost != nil {
|
||||||
|
args = append(args, "-m", "set", "--match-set", string(srcHost.ipsetName), "src")
|
||||||
|
}
|
||||||
|
if dstHost != nil {
|
||||||
|
args = append(args, "-m", "set", "--match-set", string(dstHost.ipsetName), "dst")
|
||||||
|
}
|
||||||
|
if dstPort != nil {
|
||||||
|
args = append(args, "--dport", *dstPort)
|
||||||
|
}
|
||||||
|
args = append(args, "-j", "ACCEPT")
|
||||||
|
key := strings.Join(args, " ")
|
||||||
|
|
||||||
|
return &ruleSpec{key, args}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ruleSet struct {
|
||||||
|
ipt *iptables.IPTables
|
||||||
|
users map[string]map[types.UID]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRuleSet(ipt *iptables.IPTables) *ruleSet {
|
||||||
|
return &ruleSet{ipt, make(map[string]map[types.UID]struct{})}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *ruleSet) deprovision(user types.UID, current, desired map[string]*ruleSpec) error {
|
||||||
|
for key, spec := range current {
|
||||||
|
if _, found := desired[key]; !found {
|
||||||
|
delete(rs.users[key], user)
|
||||||
|
if len(rs.users[key]) == 0 {
|
||||||
|
log.Infof("deleting rule: %v", spec.args)
|
||||||
|
if err := rs.ipt.Delete(TableFilter, IngressChain, spec.args...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
delete(rs.users, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *ruleSet) provision(user types.UID, current, desired map[string]*ruleSpec) error {
|
||||||
|
for key, spec := range desired {
|
||||||
|
if _, found := current[key]; !found {
|
||||||
|
if _, found := rs.users[key]; !found {
|
||||||
|
log.Infof("adding rule: %v", spec.args)
|
||||||
|
if err := rs.ipt.Append(TableFilter, IngressChain, spec.args...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rs.users[key] = make(map[types.UID]struct{})
|
||||||
|
}
|
||||||
|
rs.users[key][user] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+130
@@ -0,0 +1,130 @@
|
|||||||
|
package npc
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"k8s.io/client-go/pkg/api/unversioned"
|
||||||
|
"k8s.io/client-go/pkg/labels"
|
||||||
|
"k8s.io/client-go/pkg/types"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/npc/ipset"
|
||||||
|
)
|
||||||
|
|
||||||
|
type selectorSpec struct {
|
||||||
|
key string // string representation (for hash keying/equality comparison)
|
||||||
|
selector labels.Selector // k8s Selector object (for matching)
|
||||||
|
|
||||||
|
ipsetType ipset.Type // type of ipset to provision
|
||||||
|
ipsetName ipset.Name // generated ipset name
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSelectorSpec(json *unversioned.LabelSelector, nsName string, ipsetType ipset.Type) (*selectorSpec, error) {
|
||||||
|
selector, err := unversioned.LabelSelectorAsSelector(json)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key := selector.String()
|
||||||
|
return &selectorSpec{
|
||||||
|
key: key,
|
||||||
|
selector: selector,
|
||||||
|
// We prefix the selector string with the namespace name when generating
|
||||||
|
// the shortname because you can specify the same selector in multiple
|
||||||
|
// namespaces - we need those to map to distinct ipsets
|
||||||
|
ipsetName: ipset.Name("weave-" + shortName(nsName+":"+key)),
|
||||||
|
ipsetType: ipsetType}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type selector struct {
|
||||||
|
ips ipset.Interface
|
||||||
|
spec *selectorSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *selector) matches(labelMap map[string]string) bool {
|
||||||
|
return s.spec.selector.Matches(labels.Set(labelMap))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *selector) addEntry(entry string) error {
|
||||||
|
log.Infof("adding entry %s to %s", entry, s.spec.ipsetName)
|
||||||
|
return s.ips.AddEntry(s.spec.ipsetName, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *selector) delEntry(entry string) error {
|
||||||
|
log.Infof("deleting entry %s from %s", entry, s.spec.ipsetName)
|
||||||
|
return s.ips.DelEntry(s.spec.ipsetName, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
type selectorFn func(selector *selector) error
|
||||||
|
|
||||||
|
type selectorSet struct {
|
||||||
|
ips ipset.Interface
|
||||||
|
onNewSelector selectorFn
|
||||||
|
users map[string]map[types.UID]struct{} // list of users per selector
|
||||||
|
entries map[string]*selector
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSelectorSet(ips ipset.Interface, onNewSelector selectorFn) *selectorSet {
|
||||||
|
return &selectorSet{
|
||||||
|
ips: ips,
|
||||||
|
onNewSelector: onNewSelector,
|
||||||
|
users: make(map[string]map[types.UID]struct{}),
|
||||||
|
entries: make(map[string]*selector)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *selectorSet) addToMatching(labelMap map[string]string, entry string) error {
|
||||||
|
for _, s := range ss.entries {
|
||||||
|
if s.matches(labelMap) {
|
||||||
|
if err := s.addEntry(entry); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *selectorSet) delFromMatching(labelMap map[string]string, entry string) error {
|
||||||
|
for _, s := range ss.entries {
|
||||||
|
if s.matches(labelMap) {
|
||||||
|
if err := s.delEntry(entry); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*selectorSpec) error {
|
||||||
|
for key, spec := range current {
|
||||||
|
if _, found := desired[key]; !found {
|
||||||
|
delete(ss.users[key], user)
|
||||||
|
if len(ss.users[key]) == 0 {
|
||||||
|
log.Infof("destroying ipset: %#v", spec)
|
||||||
|
if err := ss.ips.Destroy(spec.ipsetName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
delete(ss.entries, key)
|
||||||
|
delete(ss.users, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *selectorSet) provision(user types.UID, current, desired map[string]*selectorSpec) error {
|
||||||
|
for key, spec := range desired {
|
||||||
|
if _, found := current[key]; !found {
|
||||||
|
if _, found := ss.users[key]; !found {
|
||||||
|
log.Infof("creating ipset: %#v", spec)
|
||||||
|
if err := ss.ips.Create(spec.ipsetName, spec.ipsetType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
selector := &selector{ss.ips, spec}
|
||||||
|
if err := ss.onNewSelector(selector); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ss.users[key] = make(map[types.UID]struct{})
|
||||||
|
ss.entries[key] = selector
|
||||||
|
}
|
||||||
|
ss.users[key][user] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package npc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha1"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// This array:
|
||||||
|
// * Must include only printable UTF8 characters that are represented with a single byte
|
||||||
|
// * Must be at least of length 85 (`len("weave-") + l(2^160)/l(85)` equals 31, the maximum ipset name length)
|
||||||
|
// * Must not include commas as those are treated specially by `ipset add` when adding a named set to a list:set
|
||||||
|
// * Should not include space for readability
|
||||||
|
// * Should not include single quote or backslash to be nice to shell users
|
||||||
|
ShortNameSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789(){}[]<>_$%^&*|/?.;:@#~"
|
||||||
|
)
|
||||||
|
|
||||||
|
// sha1 hash an arbitrary string and represent it using the full range of
|
||||||
|
// printable ascii characters
|
||||||
|
func shortName(arbitrary string) string {
|
||||||
|
symbols := []byte(ShortNameSymbols)
|
||||||
|
sum := sha1.Sum([]byte(arbitrary))
|
||||||
|
i := big.NewInt(0).SetBytes(sum[:])
|
||||||
|
base := big.NewInt(int64(len(symbols)))
|
||||||
|
zero := big.NewInt(0)
|
||||||
|
result := make([]byte, 0)
|
||||||
|
|
||||||
|
for i.Cmp(zero) > 0 {
|
||||||
|
remainder := new(big.Int).Mod(i, base)
|
||||||
|
i.Sub(i, remainder)
|
||||||
|
i.Div(i, base)
|
||||||
|
result = append(result, symbols[remainder.Int64()])
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package ulogd
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func waitForExit(cmd *exec.Cmd) {
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
log.Fatalf("ulogd terminated: %v", err)
|
||||||
|
}
|
||||||
|
log.Fatal("ulogd terminated normally")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start() error {
|
||||||
|
cmd := exec.Command("/usr/sbin/ulogd", "-v")
|
||||||
|
stdout, err := cmd.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go io.Copy(os.Stdout, stdout)
|
||||||
|
go waitForExit(cmd)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -72,9 +72,8 @@ func splitPoolID(poolID string) (subnet, iprange *net.IPNet, err error) {
|
|||||||
func (i *Ipam) RequestAddress(poolID string, address net.IP, options map[string]string) (ip *net.IPNet, _ map[string]string, err error) {
|
func (i *Ipam) RequestAddress(poolID string, address net.IP, options map[string]string) (ip *net.IPNet, _ map[string]string, err error) {
|
||||||
i.logReq("RequestAddress", poolID, address, options)
|
i.logReq("RequestAddress", poolID, address, options)
|
||||||
defer func() { i.logRes("RequestAddress", err, ip) }()
|
defer func() { i.logRes("RequestAddress", err, ip) }()
|
||||||
// If we pass magic string "_" to weave IPAM it stores the address under its own string
|
|
||||||
if poolID == "weavepool" { // old-style
|
if poolID == "weavepool" { // old-style
|
||||||
ip, err = i.weave.AllocateIP("_")
|
ip, err = i.weave.AllocateIP(api.NoContainerID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
subnet, iprange, err := splitPoolID(poolID)
|
subnet, iprange, err := splitPoolID(poolID)
|
||||||
@@ -83,12 +82,12 @@ func (i *Ipam) RequestAddress(poolID string, address net.IP, options map[string]
|
|||||||
}
|
}
|
||||||
if address != nil { // try to claim specific address requested
|
if address != nil { // try to claim specific address requested
|
||||||
ip = &net.IPNet{IP: address, Mask: subnet.Mask}
|
ip = &net.IPNet{IP: address, Mask: subnet.Mask}
|
||||||
if err = i.weave.ClaimIP("_", ip); err != nil {
|
if err = i.weave.ClaimIP(api.NoContainerID, ip); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// We are lying slightly to IPAM here: the range is not a subnet
|
// We are lying slightly to IPAM here: the range is not a subnet
|
||||||
if ip, err = i.weave.AllocateIPInSubnet("_", iprange); err != nil {
|
if ip, err = i.weave.AllocateIPInSubnet(api.NoContainerID, iprange); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ip.Mask = subnet.Mask // fix up the subnet we lied about
|
ip.Mask = subnet.Mask // fix up the subnet we lied about
|
||||||
|
|||||||
+14
-18
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/appc/cni/pkg/ipam"
|
"github.com/appc/cni/pkg/ipam"
|
||||||
"github.com/appc/cni/pkg/skel"
|
"github.com/appc/cni/pkg/skel"
|
||||||
@@ -12,7 +13,6 @@ import (
|
|||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"github.com/vishvananda/netns"
|
"github.com/vishvananda/netns"
|
||||||
weaveapi "github.com/weaveworks/weave/api"
|
weaveapi "github.com/weaveworks/weave/api"
|
||||||
"github.com/weaveworks/weave/common"
|
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
ipamplugin "github.com/weaveworks/weave/plugin/ipam"
|
ipamplugin "github.com/weaveworks/weave/plugin/ipam"
|
||||||
)
|
)
|
||||||
@@ -84,6 +84,9 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
|||||||
if err := assignBridgeIP(conf.BrName, bridgeIPResult.IP4.IP); err != nil {
|
if err := assignBridgeIP(conf.BrName, bridgeIPResult.IP4.IP); err != nil {
|
||||||
return fmt.Errorf("unable to assign IP address to bridge: %s", err)
|
return fmt.Errorf("unable to assign IP address to bridge: %s", err)
|
||||||
}
|
}
|
||||||
|
if err := weavenet.ExposeNAT(bridgeIPResult.IP4.IP); err != nil {
|
||||||
|
return fmt.Errorf("unable to create NAT rules: %s", err)
|
||||||
|
}
|
||||||
bridgeIP = bridgeIPResult.IP4.IP.IP
|
bridgeIP = bridgeIPResult.IP4.IP.IP
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -93,7 +96,7 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
ns, err := netns.GetFromPath(args.Netns)
|
ns, err := netns.GetFromPath(args.Netns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("error accessing namespace %q: %s", args.Netns, err)
|
||||||
}
|
}
|
||||||
defer ns.Close()
|
defer ns.Close()
|
||||||
|
|
||||||
@@ -107,7 +110,7 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
|||||||
id = fmt.Sprintf("%x", data)
|
id = fmt.Sprintf("%x", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := weavenet.AttachContainer(ns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&result.IP4.IP}, false); err != nil {
|
if err := weavenet.AttachContainer(args.Netns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&result.IP4.IP}, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := weavenet.WithNetNSLinkUnsafe(ns, args.IfName, func(link netlink.Link) error {
|
if err := weavenet.WithNetNSLinkUnsafe(ns, args.IfName, func(link netlink.Link) error {
|
||||||
@@ -151,6 +154,11 @@ func assignBridgeIP(bridgeName string, ipnet net.IPNet) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := netlink.AddrAdd(link, &netlink.Addr{IPNet: &ipnet}); err != nil {
|
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 fmt.Errorf("failed to add IP address to %q: %v", bridgeName, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -159,7 +167,7 @@ func assignBridgeIP(bridgeName string, ipnet net.IPNet) error {
|
|||||||
var errBridgeNoIP = fmt.Errorf("Bridge has no IP address")
|
var errBridgeNoIP = fmt.Errorf("Bridge has no IP address")
|
||||||
|
|
||||||
func findBridgeIP(bridgeName string, subnet net.IPNet) (net.IP, error) {
|
func findBridgeIP(bridgeName string, subnet net.IPNet) (net.IP, error) {
|
||||||
netdev, err := common.GetBridgeNetDev(bridgeName)
|
netdev, err := weavenet.GetBridgeNetDev(bridgeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed to get netdev for %q bridge: %s", bridgeName, err)
|
return nil, fmt.Errorf("Failed to get netdev for %q bridge: %s", bridgeName, err)
|
||||||
}
|
}
|
||||||
@@ -181,20 +189,8 @@ func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ns, err := netns.GetFromPath(args.Netns)
|
if _, err = weavenet.WithNetNS(args.Netns, "del-iface", args.IfName); err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("error removing interface %q: %s", args.IfName, err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer ns.Close()
|
|
||||||
err = weavenet.WithNetNSUnsafe(ns, func() error {
|
|
||||||
link, err := netlink.LinkByName(args.IfName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return netlink.LinkDel(link)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error removing interface: %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default IPAM is Weave's own
|
// Default IPAM is Weave's own
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
api "k8s.io/client-go/pkg/api/v1"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getKubePeers() ([]string, error) {
|
||||||
|
config, err := rest.InClusterConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c, err := kubernetes.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nodeList, err := c.Nodes().List(api.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
// Fallback for cases (e.g. from kube-up.sh) where kube-proxy is not running on master
|
||||||
|
config.Host = "http://localhost:8080"
|
||||||
|
log.Print("error contacting APIServer: ", err, "; trying with fallback: ", config.Host)
|
||||||
|
c, err = kubernetes.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nodeList, err = c.Nodes().List(api.ListOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
addresses := make([]string, 0, len(nodeList.Items))
|
||||||
|
for _, peer := range nodeList.Items {
|
||||||
|
for _, addr := range peer.Status.Addresses {
|
||||||
|
if addr.Type == "InternalIP" {
|
||||||
|
addresses = append(addresses, addr.Address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addresses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
peers, err := getKubePeers()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not get peers: %v", err)
|
||||||
|
}
|
||||||
|
for _, addr := range peers {
|
||||||
|
fmt.Println(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
FROM weaveworks/weaveexec
|
FROM DOCKERHUB_USER/weave
|
||||||
ADD ./plugin /home/weave/
|
ADD ./plugin /home/weave/
|
||||||
ENTRYPOINT ["/home/weave/plugin"]
|
ENTRYPOINT ["/home/weave/plugin"]
|
||||||
+1
-1
@@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/weaveworks/weave/plugin/skel"
|
"github.com/weaveworks/weave/plugin/skel"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "(unreleased version)"
|
var version = "unreleased"
|
||||||
|
|
||||||
var Log = common.Log
|
var Log = common.Log
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
FROM DOCKERHUB_USER/weave
|
||||||
|
ADD ./launch.sh ./kube-peers /home/weave/
|
||||||
|
ENTRYPOINT ["/home/weave/launch.sh"]
|
||||||
Executable
+161
@@ -0,0 +1,161 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Default if not supplied - same as weave net default
|
||||||
|
IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
|
||||||
|
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
||||||
|
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
|
||||||
|
|
||||||
|
# Check if the IP range overlaps anything existing on the host
|
||||||
|
/usr/bin/weaveutil netcheck $IPALLOC_RANGE weave
|
||||||
|
|
||||||
|
# Default for network policy
|
||||||
|
EXPECT_NPC=${EXPECT_NPC:-1}
|
||||||
|
|
||||||
|
# kube-proxy requires that bridged traffic passes through netfilter
|
||||||
|
if [ ! -f /proc/sys/net/bridge/bridge-nf-call-iptables ] ; then
|
||||||
|
echo /proc/sys/net/bridge/bridge-nf-call-iptables not found >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables
|
||||||
|
|
||||||
|
SOURCE_BINARY=/usr/bin/weaveutil
|
||||||
|
VERSION=$(/home/weave/weaver $EXTRA_ARGS --version | sed -e 's/weave router //')
|
||||||
|
PLUGIN="weave-plugin-$VERSION"
|
||||||
|
|
||||||
|
install_cni_plugin() {
|
||||||
|
mkdir -p $1 || return 1
|
||||||
|
if [ ! -f "$1/$PLUGIN" ]; then
|
||||||
|
cp "$SOURCE_BINARY" "$1/$PLUGIN"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
upgrade_cni_plugin_symlink() {
|
||||||
|
# Remove potential temporary symlink from previous failed upgrade:
|
||||||
|
rm -f $1/$2.tmp
|
||||||
|
# Atomically create a symlink to the plugin:
|
||||||
|
ln -s "$1/$PLUGIN" $1/$2.tmp && mv -f $1/$2.tmp $1/$2
|
||||||
|
}
|
||||||
|
|
||||||
|
upgrade_cni_plugin() {
|
||||||
|
# Check if weave-net and weave-ipam are (legacy) copies of the plugin, and
|
||||||
|
# if so remove these so symlinks can be used instead from now onwards.
|
||||||
|
if [ -f $1/weave-net -a ! -L $1/weave-net ]; then rm $1/weave-net; fi
|
||||||
|
if [ -f $1/weave-ipam -a ! -L $1/weave-ipam ]; then rm $1/weave-ipam; fi
|
||||||
|
|
||||||
|
# Create two symlinks to the plugin, as it has a different
|
||||||
|
# behaviour depending on its name:
|
||||||
|
if [ "$(readlink -f $1/weave-net)" != "$1/$PLUGIN" ]; then
|
||||||
|
upgrade_cni_plugin_symlink $1 weave-net
|
||||||
|
fi
|
||||||
|
if [ "$(readlink -f $1/weave-ipam)" != "$1/$PLUGIN" ]; then
|
||||||
|
upgrade_cni_plugin_symlink $1 weave-ipam
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install CNI plugin binary to typical CNI bin location
|
||||||
|
# with fall-back to CNI directory used by kube-up on GCI OS.
|
||||||
|
if install_cni_plugin /opt/cni/bin ; then
|
||||||
|
upgrade_cni_plugin /opt/cni/bin
|
||||||
|
elif install_cni_plugin /host_home/kubernetes/bin ; then
|
||||||
|
upgrade_cni_plugin /host_home/kubernetes/bin
|
||||||
|
else
|
||||||
|
echo "Failed to install the Weave CNI plugin" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Need to create bridge before running weaver so we can use the peer address
|
||||||
|
# (because of https://github.com/weaveworks/weave/issues/2480)
|
||||||
|
WEAVE_NPC_OPTS="--expect-npc"
|
||||||
|
if [ "${EXPECT_NPC}" = "0" ]; then
|
||||||
|
WEAVE_NPC_OPTS=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
/home/weave/weave --local create-bridge --force $WEAVE_NPC_OPTS
|
||||||
|
|
||||||
|
# Kubernetes sets HOSTNAME to the host's hostname
|
||||||
|
# when running a pod in host namespace.
|
||||||
|
NICKNAME_ARG=""
|
||||||
|
if [ -n "$HOSTNAME" ] ; then
|
||||||
|
NICKNAME_ARG="--nickname=$HOSTNAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BRIDGE_OPTIONS="--datapath=datapath"
|
||||||
|
if [ "$(/home/weave/weave --local bridge-type)" = "bridge" ] ; then
|
||||||
|
# TODO: Call into weave script to do this
|
||||||
|
if ! ip link show vethwe-pcap >/dev/null 2>&1 ; then
|
||||||
|
ip link add name vethwe-bridge type veth peer name vethwe-pcap
|
||||||
|
ip link set vethwe-bridge up
|
||||||
|
ip link set vethwe-pcap up
|
||||||
|
ip link set vethwe-bridge master weave
|
||||||
|
fi
|
||||||
|
BRIDGE_OPTIONS="--iface=vethwe-pcap"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$KUBE_PEERS" ]; then
|
||||||
|
if ! KUBE_PEERS=$(/home/weave/kube-peers) || [ -z "$KUBE_PEERS" ]; then
|
||||||
|
echo Failed to get peers >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
peer_count() {
|
||||||
|
echo $#
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z "$IPALLOC_INIT" ]; then
|
||||||
|
IPALLOC_INIT="consensus=$(peer_count $KUBE_PEERS)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
/home/weave/weaver $EXTRA_ARGS --port=6783 $BRIDGE_OPTIONS \
|
||||||
|
--http-addr=$HTTP_ADDR --status-addr=$STATUS_ADDR --docker-api='' --no-dns \
|
||||||
|
--ipalloc-range=$IPALLOC_RANGE $NICKNAME_ARG \
|
||||||
|
--ipalloc-init $IPALLOC_INIT \
|
||||||
|
--name=$(cat /sys/class/net/weave/address) "$@" \
|
||||||
|
$KUBE_PEERS &
|
||||||
|
WEAVE_PID=$!
|
||||||
|
|
||||||
|
# Wait for weave process to become responsive
|
||||||
|
while true ; do
|
||||||
|
curl $HTTP_ADDR/status >/dev/null 2>&1 && break
|
||||||
|
if ! kill -0 $WEAVE_PID >/dev/null 2>&1 ; then
|
||||||
|
echo Weave process has died >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
reclaim_ips() {
|
||||||
|
ID=$1
|
||||||
|
shift
|
||||||
|
for CIDR in "$@" ; do
|
||||||
|
curl -s -S -X PUT "$HTTP_ADDR/ip/$ID/$CIDR" || true
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Tell the newly-started weave about existing weave bridge IPs
|
||||||
|
/usr/bin/weaveutil container-addrs weave weave:expose | while read ID IFACE MAC IPS; do
|
||||||
|
reclaim_ips "weave:expose" $IPS
|
||||||
|
done
|
||||||
|
# Tell weave about existing weave process IPs
|
||||||
|
/usr/bin/weaveutil process-addrs weave | while read ID IFACE MAC IPS; do
|
||||||
|
reclaim_ips "_" $IPS
|
||||||
|
done
|
||||||
|
|
||||||
|
# Create CNI config, if not already there
|
||||||
|
if [ ! -f /etc/cni/net.d/10-weave.conf ] ; then
|
||||||
|
mkdir -p /etc/cni/net.d
|
||||||
|
cat > /etc/cni/net.d/10-weave.conf <<EOF
|
||||||
|
{
|
||||||
|
"name": "weave",
|
||||||
|
"type": "weave-net"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Expose the weave network so host processes can communicate with pods
|
||||||
|
/home/weave/weave --local expose $WEAVE_EXPOSE_IP
|
||||||
|
|
||||||
|
wait $WEAVE_PID
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
annotations:
|
||||||
|
scheduler.alpha.kubernetes.io/tolerations: |
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"key": "dedicated",
|
||||||
|
"operator": "Equal",
|
||||||
|
"value": "master",
|
||||||
|
"effect": "NoSchedule"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
spec:
|
||||||
|
hostNetwork: true
|
||||||
|
hostPID: true
|
||||||
|
containers:
|
||||||
|
- name: weave
|
||||||
|
image: weaveworks/weave-kube:latest
|
||||||
|
imagePullPolicy: Always
|
||||||
|
command:
|
||||||
|
- /home/weave/launch.sh
|
||||||
|
livenessProbe:
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
httpGet:
|
||||||
|
host: 127.0.0.1
|
||||||
|
path: /status
|
||||||
|
port: 6784
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: weavedb
|
||||||
|
mountPath: /weavedb
|
||||||
|
- name: cni-bin
|
||||||
|
mountPath: /opt
|
||||||
|
- name: cni-bin2
|
||||||
|
mountPath: /host_home
|
||||||
|
- name: cni-conf
|
||||||
|
mountPath: /etc
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
- name: weave-npc
|
||||||
|
image: weaveworks/weave-npc:latest
|
||||||
|
imagePullPolicy: Always
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
restartPolicy: Always
|
||||||
|
volumes:
|
||||||
|
- name: weavedb
|
||||||
|
emptyDir: {}
|
||||||
|
- name: cni-bin
|
||||||
|
hostPath:
|
||||||
|
path: /opt
|
||||||
|
- name: cni-bin2
|
||||||
|
hostPath:
|
||||||
|
path: /home
|
||||||
|
- name: cni-conf
|
||||||
|
hostPath:
|
||||||
|
path: /etc
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
FROM alpine
|
||||||
|
RUN apk add --update \
|
||||||
|
iptables \
|
||||||
|
ipset \
|
||||||
|
ulogd \
|
||||||
|
&& rm -rf /var/cache/apk/* \
|
||||||
|
&& mknod /var/log/ulogd.pcap p
|
||||||
|
COPY ./weave-npc /usr/bin/weave-npc
|
||||||
|
COPY ./ulogd.conf /etc/ulogd.conf
|
||||||
|
ENTRYPOINT ["/usr/bin/weave-npc"]
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
coreapi "k8s.io/client-go/pkg/api/v1"
|
||||||
|
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||||
|
"k8s.io/client-go/pkg/fields"
|
||||||
|
"k8s.io/client-go/pkg/runtime"
|
||||||
|
"k8s.io/client-go/pkg/util/wait"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/npc"
|
||||||
|
"github.com/weaveworks/weave/npc/ipset"
|
||||||
|
"github.com/weaveworks/weave/npc/metrics"
|
||||||
|
"github.com/weaveworks/weave/npc/ulogd"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
version = "unreleased"
|
||||||
|
metricsAddr string
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleError(err error) {
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeController(getter cache.Getter, resource string,
|
||||||
|
objType runtime.Object, handlers cache.ResourceEventHandlerFuncs) *cache.Controller {
|
||||||
|
listWatch := cache.NewListWatchFromClient(getter, resource, "", fields.Everything())
|
||||||
|
_, controller := cache.NewInformer(listWatch, objType, 0, handlers)
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetIPTables(ipt *iptables.IPTables) error {
|
||||||
|
// Flush chains first so there are no refs to extant ipsets
|
||||||
|
if err := ipt.ClearChain(npc.TableFilter, npc.IngressChain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipt.ClearChain(npc.TableFilter, npc.DefaultChain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipt.ClearChain(npc.TableFilter, npc.MainChain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure main chain static rules
|
||||||
|
if err := ipt.Append(npc.TableFilter, npc.MainChain,
|
||||||
|
"-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipt.Append(npc.TableFilter, npc.MainChain,
|
||||||
|
"-m", "state", "--state", "NEW", "-j", string(npc.DefaultChain)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipt.Append(npc.TableFilter, npc.MainChain,
|
||||||
|
"-m", "state", "--state", "NEW", "-j", string(npc.IngressChain)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetIPSets(ips ipset.Interface) error {
|
||||||
|
// TODO should restrict ipset operations to the `weave-` prefix:
|
||||||
|
|
||||||
|
if err := ips.FlushAll(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ips.DestroyAll(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func root(cmd *cobra.Command, args []string) {
|
||||||
|
log.Infof("Starting Weaveworks NPC %s", version)
|
||||||
|
|
||||||
|
if err := metrics.Start(metricsAddr); err != nil {
|
||||||
|
log.Fatalf("Failed to start metrics: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ulogd.Start(); err != nil {
|
||||||
|
log.Fatalf("Failed to start ulogd: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := rest.InClusterConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := kubernetes.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipt, err := iptables.New()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ips := ipset.New()
|
||||||
|
|
||||||
|
handleError(resetIPTables(ipt))
|
||||||
|
handleError(resetIPSets(ips))
|
||||||
|
|
||||||
|
npc := npc.New(ipt, ips)
|
||||||
|
|
||||||
|
nsController := makeController(client.Core().RESTClient(), "namespaces", &coreapi.Namespace{},
|
||||||
|
cache.ResourceEventHandlerFuncs{
|
||||||
|
AddFunc: func(obj interface{}) {
|
||||||
|
handleError(npc.AddNamespace(obj.(*coreapi.Namespace)))
|
||||||
|
},
|
||||||
|
DeleteFunc: func(obj interface{}) {
|
||||||
|
handleError(npc.DeleteNamespace(obj.(*coreapi.Namespace)))
|
||||||
|
},
|
||||||
|
UpdateFunc: func(old, new interface{}) {
|
||||||
|
handleError(npc.UpdateNamespace(old.(*coreapi.Namespace), new.(*coreapi.Namespace)))
|
||||||
|
}})
|
||||||
|
|
||||||
|
podController := makeController(client.Core().RESTClient(), "pods", &coreapi.Pod{},
|
||||||
|
cache.ResourceEventHandlerFuncs{
|
||||||
|
AddFunc: func(obj interface{}) {
|
||||||
|
handleError(npc.AddPod(obj.(*coreapi.Pod)))
|
||||||
|
},
|
||||||
|
DeleteFunc: func(obj interface{}) {
|
||||||
|
handleError(npc.DeletePod(obj.(*coreapi.Pod)))
|
||||||
|
},
|
||||||
|
UpdateFunc: func(old, new interface{}) {
|
||||||
|
handleError(npc.UpdatePod(old.(*coreapi.Pod), new.(*coreapi.Pod)))
|
||||||
|
}})
|
||||||
|
|
||||||
|
npController := makeController(client.Extensions().RESTClient(), "networkpolicies", &extnapi.NetworkPolicy{},
|
||||||
|
cache.ResourceEventHandlerFuncs{
|
||||||
|
AddFunc: func(obj interface{}) {
|
||||||
|
handleError(npc.AddNetworkPolicy(obj.(*extnapi.NetworkPolicy)))
|
||||||
|
},
|
||||||
|
DeleteFunc: func(obj interface{}) {
|
||||||
|
handleError(npc.DeleteNetworkPolicy(obj.(*extnapi.NetworkPolicy)))
|
||||||
|
},
|
||||||
|
UpdateFunc: func(old, new interface{}) {
|
||||||
|
handleError(npc.UpdateNetworkPolicy(old.(*extnapi.NetworkPolicy), new.(*extnapi.NetworkPolicy)))
|
||||||
|
}})
|
||||||
|
|
||||||
|
go nsController.Run(wait.NeverStop)
|
||||||
|
go podController.Run(wait.NeverStop)
|
||||||
|
go npController.Run(wait.NeverStop)
|
||||||
|
|
||||||
|
signals := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
log.Fatalf("Exiting: %v", <-signals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
rootCmd := &cobra.Command{
|
||||||
|
Use: "weave-npc",
|
||||||
|
Short: "Weaveworks Kubernetes Network Policy Controller",
|
||||||
|
Run: root}
|
||||||
|
|
||||||
|
rootCmd.PersistentFlags().StringVar(&metricsAddr, "metrics-addr", ":6781", "metrics server bind address")
|
||||||
|
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[global]
|
||||||
|
logfile="/dev/null"
|
||||||
|
plugin="/usr/lib/ulogd/ulogd_inppkt_NFLOG.so"
|
||||||
|
plugin="/usr/lib/ulogd/ulogd_raw2packet_BASE.so"
|
||||||
|
plugin="/usr/lib/ulogd/ulogd_output_PCAP.so"
|
||||||
|
stack=log1:NFLOG,base1:BASE,pcap1:PCAP
|
||||||
|
|
||||||
|
[log1]
|
||||||
|
group=86
|
||||||
|
|
||||||
|
[pcap1]
|
||||||
|
file="/var/log/ulogd.pcap"
|
||||||
|
sync=1
|
||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/weaveworks/weave/proxy"
|
"github.com/weaveworks/weave/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "(unreleased version)"
|
var version = "unreleased"
|
||||||
|
|
||||||
var Log = common.Log
|
var Log = common.Log
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM scratch
|
FROM DOCKERHUB_USER/weaveexec
|
||||||
MAINTAINER Weaveworks Inc <help@weave.works>
|
MAINTAINER Weaveworks Inc <help@weave.works>
|
||||||
LABEL works.weave.role=system
|
LABEL works.weave.role=system
|
||||||
WORKDIR /home/weave
|
WORKDIR /home/weave
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
var checker *checkpoint.Checker
|
var checker *checkpoint.Checker
|
||||||
var newVersion atomic.Value
|
var newVersion atomic.Value
|
||||||
|
var success atomic.Value
|
||||||
|
|
||||||
const (
|
const (
|
||||||
updateCheckPeriod = 6 * time.Hour
|
updateCheckPeriod = 6 * time.Hour
|
||||||
@@ -17,9 +18,11 @@ const (
|
|||||||
|
|
||||||
func checkForUpdates(dockerVersion string, network string) {
|
func checkForUpdates(dockerVersion string, network string) {
|
||||||
newVersion.Store("")
|
newVersion.Store("")
|
||||||
|
success.Store(true)
|
||||||
|
|
||||||
handleResponse := func(r *checkpoint.CheckResponse, err error) {
|
handleResponse := func(r *checkpoint.CheckResponse, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
success.Store(false)
|
||||||
Log.Printf("Error checking version: %v", err)
|
Log.Printf("Error checking version: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-22
@@ -19,16 +19,10 @@ import (
|
|||||||
weave "github.com/weaveworks/weave/router"
|
weave "github.com/weaveworks/weave/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var allConnectionStates = []string{"established", "pending", "retrying", "failed", "connecting"}
|
||||||
|
|
||||||
var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
||||||
"countDNSEntries": func(entries []nameserver.EntryStatus) int {
|
"countDNSEntries": countDNSEntries,
|
||||||
count := 0
|
|
||||||
for _, entry := range entries {
|
|
||||||
if entry.Tombstone == 0 {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
},
|
|
||||||
"printList": func(list []string) string {
|
"printList": func(list []string) string {
|
||||||
if len(list) == 0 {
|
if len(list) == 0 {
|
||||||
return "none"
|
return "none"
|
||||||
@@ -55,27 +49,28 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
|||||||
s.ips += entry.Size
|
s.ips += entry.Size
|
||||||
}
|
}
|
||||||
|
|
||||||
printOwned := func(name string, nickName string, reachable bool, ips uint32) {
|
printOwned := func(name string, nickName string, info string, ips uint32) {
|
||||||
reachableStr := ""
|
|
||||||
if !reachable {
|
|
||||||
reachableStr = "- unreachable!"
|
|
||||||
}
|
|
||||||
percentageRanges := float32(ips) * 100.0 / float32(status.RangeNumIPs)
|
percentageRanges := float32(ips) * 100.0 / float32(status.RangeNumIPs)
|
||||||
|
|
||||||
displayName := name + "(" + nickName + ")"
|
displayName := name + "(" + nickName + ")"
|
||||||
fmt.Fprintf(&buffer, "%-37v %8d IPs (%04.1f%% of total) %s\n",
|
fmt.Fprintf(&buffer, "%-37v %8d IPs (%04.1f%% of total) %s\n",
|
||||||
displayName, ips, percentageRanges, reachableStr)
|
displayName, ips, percentageRanges, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// print the local info first
|
// print the local info first
|
||||||
if ourStats := peerStats[router.Name]; ourStats != nil {
|
if ourStats := peerStats[router.Name]; ourStats != nil {
|
||||||
printOwned(router.Name, ourStats.nickname, true, ourStats.ips)
|
activeStr := fmt.Sprintf("(%d active)", status.ActiveIPs)
|
||||||
|
printOwned(router.Name, ourStats.nickname, activeStr, ourStats.ips)
|
||||||
}
|
}
|
||||||
|
|
||||||
// and then the rest
|
// and then the rest
|
||||||
for peer, stats := range peerStats {
|
for peer, stats := range peerStats {
|
||||||
if peer != router.Name {
|
if peer != router.Name {
|
||||||
printOwned(peer, stats.nickname, stats.reachable, stats.ips)
|
reachableStr := ""
|
||||||
|
if !stats.reachable {
|
||||||
|
reachableStr = "- unreachable!"
|
||||||
|
}
|
||||||
|
printOwned(peer, stats.nickname, reachableStr, stats.ips)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +89,7 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
|||||||
for _, conn := range conns {
|
for _, conn := range conns {
|
||||||
counts[conn.State]++
|
counts[conn.State]++
|
||||||
}
|
}
|
||||||
return printCounts(counts, []string{"established", "pending", "retrying", "failed", "connecting"})
|
return printCounts(counts, allConnectionStates)
|
||||||
},
|
},
|
||||||
"printPeerConnectionCounts": func(peers []mesh.PeerStatus) string {
|
"printPeerConnectionCounts": func(peers []mesh.PeerStatus) string {
|
||||||
counts := make(map[string]int)
|
counts := make(map[string]int)
|
||||||
@@ -118,6 +113,26 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
|||||||
"trimSuffix": strings.TrimSuffix,
|
"trimSuffix": strings.TrimSuffix,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
func countDNSEntries(entries []nameserver.EntryStatus) int {
|
||||||
|
count := 0
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.Tombstone == 0 {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func countDNSEntriesForPeer(peername string, entries []nameserver.EntryStatus) int {
|
||||||
|
count := 0
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.Tombstone == 0 && entry.Origin == peername {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
// Print counts in a specified order
|
// Print counts in a specified order
|
||||||
func printCounts(counts map[string]int, keys []string) string {
|
func printCounts(counts map[string]int, keys []string) string {
|
||||||
var stringCounts []string
|
var stringCounts []string
|
||||||
@@ -225,6 +240,7 @@ var ipamTemplate = defTemplate("ipamTemplate", `{{printIPAMRanges .Router .IPAM}
|
|||||||
|
|
||||||
type VersionCheck struct {
|
type VersionCheck struct {
|
||||||
Enabled bool
|
Enabled bool
|
||||||
|
Success bool
|
||||||
NewVersion string
|
NewVersion string
|
||||||
NextCheckAt time.Time
|
NextCheckAt time.Time
|
||||||
}
|
}
|
||||||
@@ -236,6 +252,7 @@ func versionCheck() *VersionCheck {
|
|||||||
}
|
}
|
||||||
|
|
||||||
v.Enabled = true
|
v.Enabled = true
|
||||||
|
v.Success = success.Load().(bool)
|
||||||
v.NewVersion = newVersion.Load().(string)
|
v.NewVersion = newVersion.Load().(string)
|
||||||
v.NextCheckAt = checker.NextCheckAt()
|
v.NextCheckAt = checker.NextCheckAt()
|
||||||
|
|
||||||
@@ -246,12 +263,12 @@ func (v *VersionCheck) String() string {
|
|||||||
switch {
|
switch {
|
||||||
case !v.Enabled:
|
case !v.Enabled:
|
||||||
return "version check update disabled"
|
return "version check update disabled"
|
||||||
|
case !v.Success:
|
||||||
|
return fmt.Sprintf("failed to check latest version - see logs; next check at %s", v.NextCheckAt.Format("2006/01/02 15:04:05"))
|
||||||
case v.NewVersion != "":
|
case v.NewVersion != "":
|
||||||
return fmt.Sprintf("version %s available - please upgrade!",
|
return fmt.Sprintf("version %s available - please upgrade!", v.NewVersion)
|
||||||
v.NewVersion)
|
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("up to date; next check at %s",
|
return fmt.Sprintf("up to date; next check at %s", v.NextCheckAt.Format("2006/01/02 15:04:05"))
|
||||||
v.NextCheckAt.Format("2006/01/02 15:04:05"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,6 +280,7 @@ type WeaveStatus struct {
|
|||||||
DNS *nameserver.Status `json:"DNS,omitempty"`
|
DNS *nameserver.Status `json:"DNS,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read-only functions, suitable for exposing on an unprotected socket
|
||||||
func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRouter, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) {
|
func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRouter, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) {
|
||||||
status := func() WeaveStatus {
|
status := func() WeaveStatus {
|
||||||
return WeaveStatus{
|
return WeaveStatus{
|
||||||
|
|||||||
+23
-4
@@ -27,7 +27,7 @@ import (
|
|||||||
weave "github.com/weaveworks/weave/router"
|
weave "github.com/weaveworks/weave/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "(unreleased version)"
|
var version = "unreleased"
|
||||||
|
|
||||||
var Log = common.Log
|
var Log = common.Log
|
||||||
|
|
||||||
@@ -132,6 +132,7 @@ func main() {
|
|||||||
bufSzMB int
|
bufSzMB int
|
||||||
noDiscovery bool
|
noDiscovery bool
|
||||||
httpAddr string
|
httpAddr string
|
||||||
|
statusAddr string
|
||||||
ipamConfig ipamConfig
|
ipamConfig ipamConfig
|
||||||
dockerAPI string
|
dockerAPI string
|
||||||
peers []string
|
peers []string
|
||||||
@@ -165,6 +166,7 @@ func main() {
|
|||||||
mflag.BoolVar(&noDiscovery, []string{"#nodiscovery", "#-nodiscovery", "-no-discovery"}, false, "disable peer discovery")
|
mflag.BoolVar(&noDiscovery, []string{"#nodiscovery", "#-nodiscovery", "-no-discovery"}, false, "disable peer discovery")
|
||||||
mflag.IntVar(&bufSzMB, []string{"#bufsz", "-bufsz"}, 8, "capture buffer size in MB")
|
mflag.IntVar(&bufSzMB, []string{"#bufsz", "-bufsz"}, 8, "capture buffer size in MB")
|
||||||
mflag.StringVar(&httpAddr, []string{"#httpaddr", "#-httpaddr", "-http-addr"}, "", "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
|
mflag.StringVar(&httpAddr, []string{"#httpaddr", "#-httpaddr", "-http-addr"}, "", "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||||
|
mflag.StringVar(&statusAddr, []string{"-status-addr"}, "", "address to bind status+metrics interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||||
mflag.StringVar(&ipamConfig.Mode, []string{"-ipalloc-init"}, "", "allocator initialisation strategy (consensus, seed or observer)")
|
mflag.StringVar(&ipamConfig.Mode, []string{"-ipalloc-init"}, "", "allocator initialisation strategy (consensus, seed or observer)")
|
||||||
mflag.StringVar(&ipamConfig.IPRangeCIDR, []string{"#iprange", "#-iprange", "-ipalloc-range"}, "", "IP address range reserved for automatic allocation, in CIDR notation")
|
mflag.StringVar(&ipamConfig.IPRangeCIDR, []string{"#iprange", "#-iprange", "-ipalloc-range"}, "", "IP address range reserved for automatic allocation, in CIDR notation")
|
||||||
mflag.StringVar(&ipamConfig.IPSubnetCIDR, []string{"#ipsubnet", "#-ipsubnet", "-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
|
mflag.StringVar(&ipamConfig.IPSubnetCIDR, []string{"#ipsubnet", "#-ipsubnet", "-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
|
||||||
@@ -224,6 +226,12 @@ func main() {
|
|||||||
overlay, bridge := createOverlay(datapathName, ifaceName, isAWSVPC, config.Host, config.Port, bufSzMB)
|
overlay, bridge := createOverlay(datapathName, ifaceName, isAWSVPC, config.Host, config.Port, bufSzMB)
|
||||||
networkConfig.Bridge = bridge
|
networkConfig.Bridge = bridge
|
||||||
|
|
||||||
|
if bridge != nil {
|
||||||
|
if err := weavenet.DetectHairpin("vethwe-bridge", Log); err != nil {
|
||||||
|
Log.Errorf("DetectHairpin failed: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
name := peerName(routerName, bridge.Interface())
|
name := peerName(routerName, bridge.Interface())
|
||||||
|
|
||||||
if nickName == "" {
|
if nickName == "" {
|
||||||
@@ -337,9 +345,20 @@ func main() {
|
|||||||
}
|
}
|
||||||
router.HandleHTTP(muxRouter)
|
router.HandleHTTP(muxRouter)
|
||||||
HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver)
|
HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver)
|
||||||
|
muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver))
|
||||||
http.Handle("/", common.LoggingHTTPHandler(muxRouter))
|
http.Handle("/", common.LoggingHTTPHandler(muxRouter))
|
||||||
Log.Println("Listening for HTTP control messages on", httpAddr)
|
Log.Println("Listening for HTTP control messages on", httpAddr)
|
||||||
go listenAndServeHTTP(httpAddr)
|
go listenAndServeHTTP(httpAddr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if statusAddr != "" {
|
||||||
|
muxRouter := mux.NewRouter()
|
||||||
|
HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver)
|
||||||
|
muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver))
|
||||||
|
statusMux := http.NewServeMux()
|
||||||
|
statusMux.Handle("/", muxRouter)
|
||||||
|
Log.Println("Listening for metrics requests on", statusAddr)
|
||||||
|
go listenAndServeHTTP(statusAddr, statusMux)
|
||||||
}
|
}
|
||||||
|
|
||||||
common.SignalHandlerLoop(router)
|
common.SignalHandlerLoop(router)
|
||||||
@@ -555,7 +574,7 @@ func parsePeerNames(s string) ([]mesh.PeerName, error) {
|
|||||||
return peerNames, nil
|
return peerNames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func listenAndServeHTTP(httpAddr string) {
|
func listenAndServeHTTP(httpAddr string, handler http.Handler) {
|
||||||
protocol := "tcp"
|
protocol := "tcp"
|
||||||
if strings.HasPrefix(httpAddr, "/") {
|
if strings.HasPrefix(httpAddr, "/") {
|
||||||
os.Remove(httpAddr) // in case it's there from last time
|
os.Remove(httpAddr) // in case it's there from last time
|
||||||
@@ -565,7 +584,7 @@ func listenAndServeHTTP(httpAddr string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatal("Unable to create http listener socket: ", err)
|
Log.Fatal("Unable to create http listener socket: ", err)
|
||||||
}
|
}
|
||||||
err = http.Serve(l, nil)
|
err = http.Serve(l, handler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatal("Unable to create http server", err)
|
Log.Fatal("Unable to create http server", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/ipam"
|
||||||
|
"github.com/weaveworks/weave/nameserver"
|
||||||
|
"github.com/weaveworks/weave/net/address"
|
||||||
|
weave "github.com/weaveworks/weave/router"
|
||||||
|
)
|
||||||
|
|
||||||
|
func metricsHandler(router *weave.NetworkRouter, allocator *ipam.Allocator, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) http.Handler {
|
||||||
|
reg := prometheus.NewRegistry()
|
||||||
|
reg.MustRegister(prometheus.NewProcessCollector(os.Getpid(), ""))
|
||||||
|
reg.MustRegister(newMetrics(router, allocator, ns, dnsserver))
|
||||||
|
return promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type collector struct {
|
||||||
|
router *weave.NetworkRouter
|
||||||
|
allocator *ipam.Allocator
|
||||||
|
ns *nameserver.Nameserver
|
||||||
|
dnsserver *nameserver.DNSServer
|
||||||
|
}
|
||||||
|
|
||||||
|
type metric struct {
|
||||||
|
*prometheus.Desc
|
||||||
|
Collect func(WeaveStatus, *prometheus.Desc, chan<- prometheus.Metric)
|
||||||
|
}
|
||||||
|
|
||||||
|
func desc(fqName, help string, variableLabels ...string) *prometheus.Desc {
|
||||||
|
return prometheus.NewDesc(fqName, help, variableLabels, prometheus.Labels{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func intGauge(desc *prometheus.Desc, val int, labels ...string) prometheus.Metric {
|
||||||
|
return prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(val), labels...)
|
||||||
|
}
|
||||||
|
func uint64Counter(desc *prometheus.Desc, val uint64, labels ...string) prometheus.Metric {
|
||||||
|
return prometheus.MustNewConstMetric(desc, prometheus.CounterValue, float64(val), labels...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var metrics []metric = []metric{
|
||||||
|
{desc("weave_connections", "Number of peer-to-peer connections.", "state"),
|
||||||
|
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
|
||||||
|
counts := make(map[string]int)
|
||||||
|
for _, conn := range s.Router.Connections {
|
||||||
|
counts[conn.State]++
|
||||||
|
}
|
||||||
|
for _, state := range allConnectionStates {
|
||||||
|
ch <- intGauge(desc, counts[state], state)
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{desc("weave_connection_terminations_total", "Number of peer-to-peer connections terminated."),
|
||||||
|
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
|
||||||
|
ch <- uint64Counter(desc, uint64(s.Router.TerminationCount))
|
||||||
|
}},
|
||||||
|
{desc("weave_ips", "Number of IP addresses.", "state"),
|
||||||
|
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
|
||||||
|
if s.IPAM != nil {
|
||||||
|
ch <- intGauge(desc, s.IPAM.ActiveIPs, "local-used")
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{desc("weave_max_ips", "Size of IP address space used by allocator."),
|
||||||
|
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
|
||||||
|
if s.IPAM != nil {
|
||||||
|
ch <- intGauge(desc, s.IPAM.RangeNumIPs)
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{desc("weave_dns_entries", "Number of DNS entries."),
|
||||||
|
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
|
||||||
|
if s.DNS != nil {
|
||||||
|
ch <- intGauge(desc, countDNSEntriesForPeer(s.Router.Name, s.DNS.Entries))
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{desc("weave_flows", "Number of FastDP flows."),
|
||||||
|
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
|
||||||
|
if metrics := fastDPMetrics(s); metrics != nil {
|
||||||
|
ch <- intGauge(desc, metrics.Flows)
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func fastDPMetrics(s WeaveStatus) *weave.FastDPMetrics {
|
||||||
|
if diagMap, ok := s.Router.OverlayDiagnostics.(map[string]interface{}); ok {
|
||||||
|
if diag, ok := diagMap["fastdp"]; ok {
|
||||||
|
if fastDPStats, ok := diag.(weave.FastDPStatus); ok {
|
||||||
|
return fastDPStats.Metrics().(*weave.FastDPMetrics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMetrics(router *weave.NetworkRouter, allocator *ipam.Allocator, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) *collector {
|
||||||
|
return &collector{
|
||||||
|
router: router,
|
||||||
|
allocator: allocator,
|
||||||
|
ns: ns,
|
||||||
|
dnsserver: dnsserver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *collector) Collect(ch chan<- prometheus.Metric) {
|
||||||
|
|
||||||
|
status := WeaveStatus{"", nil,
|
||||||
|
weave.NewNetworkRouterStatus(m.router),
|
||||||
|
ipam.NewStatus(m.allocator, address.CIDR{}),
|
||||||
|
nameserver.NewStatus(m.ns, m.dnsserver)}
|
||||||
|
|
||||||
|
for _, metric := range metrics {
|
||||||
|
metric.Collect(status, metric.Desc, ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *collector) Describe(ch chan<- *prometheus.Desc) {
|
||||||
|
for _, metric := range metrics {
|
||||||
|
ch <- metric.Desc
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-12
@@ -4,9 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/fsouza/go-dockerclient"
|
"github.com/fsouza/go-dockerclient"
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,7 +19,7 @@ func containerAddrs(args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pred, err := common.ConnectedToBridgePredicate(bridgeName)
|
peerIDs, err := weavenet.ConnectedToBridgeVethPeerIds(bridgeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == weavenet.ErrLinkNotFound {
|
if err == weavenet.ErrLinkNotFound {
|
||||||
return nil
|
return nil
|
||||||
@@ -34,11 +32,11 @@ func containerAddrs(args []string) error {
|
|||||||
|
|
||||||
for _, cid := range args[1:] {
|
for _, cid := range args[1:] {
|
||||||
if cid == "weave:expose" {
|
if cid == "weave:expose" {
|
||||||
netDev, err := common.GetBridgeNetDev(bridgeName)
|
netDev, err := weavenet.GetBridgeNetDev(bridgeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
printNetDevs(cid, []common.NetDev{netDev})
|
printNetDevs(cid, []weavenet.Dev{netDev})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if containers[cid], err = client.InspectContainer(cid); err != nil {
|
if containers[cid], err = client.InspectContainer(cid); err != nil {
|
||||||
@@ -51,11 +49,8 @@ func containerAddrs(args []string) error {
|
|||||||
containerIDs = append(containerIDs, cid)
|
containerIDs = append(containerIDs, cid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: Because network namespaces (netns) are changed many times inside the loop,
|
|
||||||
// it's NOT safe to exec any code depending on the root netns without
|
|
||||||
// wrapping with WithNetNS*.
|
|
||||||
for _, cid := range containerIDs {
|
for _, cid := range containerIDs {
|
||||||
netDevs, err := getNetDevs(client, containers[cid], pred)
|
netDevs, err := getNetDevs(client, containers[cid], peerIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -65,14 +60,14 @@ func containerAddrs(args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNetDevs(c *docker.Client, container *docker.Container, pred func(netlink.Link) bool) ([]common.NetDev, error) {
|
func getNetDevs(c *docker.Client, container *docker.Container, peerIDs []int) ([]weavenet.Dev, error) {
|
||||||
if container.State.Pid == 0 {
|
if container.State.Pid == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return common.GetNetDevsWithPredicate(container.State.Pid, pred)
|
return weavenet.GetNetDevsByVethPeerIds(container.State.Pid, peerIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printNetDevs(cid string, netDevs []common.NetDev) {
|
func printNetDevs(cid string, netDevs []weavenet.Dev) {
|
||||||
for _, netDev := range netDevs {
|
for _, netDev := range netDevs {
|
||||||
fmt.Printf("%12s %s %s", cid, netDev.Name, netDev.MAC.String())
|
fmt.Printf("%12s %s %s", cid, netDev.Name, netDev.MAC.String())
|
||||||
for _, cidr := range netDev.CIDRs {
|
for _, cidr := range netDev.CIDRs {
|
||||||
|
|||||||
+14
-13
@@ -32,10 +32,15 @@ func attach(args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pid, nsContainer, err := containerPidAndNs(args[0])
|
pid, err := containerPid(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
nsContainer, err := netns.GetFromPid(pid)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to open namespace for container %s: %s", args[0], err)
|
||||||
|
}
|
||||||
|
|
||||||
if nsHost, err := netns.GetFromPid(1); err != nil {
|
if nsHost, err := netns.GetFromPid(1); err != nil {
|
||||||
return fmt.Errorf("unable to open host namespace: %s", err)
|
return fmt.Errorf("unable to open host namespace: %s", err)
|
||||||
} else if nsHost.Equal(nsContainer) {
|
} else if nsHost.Equal(nsContainer) {
|
||||||
@@ -50,7 +55,7 @@ func attach(args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = weavenet.AttachContainer(nsContainer, fmt.Sprint(pid), weavenet.VethName, args[1], mtu, withMulticastRoute, cidrs, keepTXOn)
|
err = weavenet.AttachContainer(weavenet.NSPathByPid(pid), fmt.Sprint(pid), weavenet.VethName, args[1], mtu, withMulticastRoute, cidrs, keepTXOn)
|
||||||
// If we detected an error but the container has died, tell the user that instead.
|
// If we detected an error but the container has died, tell the user that instead.
|
||||||
if err != nil && !processExists(pid) {
|
if err != nil && !processExists(pid) {
|
||||||
err = fmt.Errorf("Container %s died", args[0])
|
err = fmt.Errorf("Container %s died", args[0])
|
||||||
@@ -58,23 +63,19 @@ func attach(args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func containerPidAndNs(containerID string) (int, netns.NsHandle, error) {
|
func containerPid(containerID string) (int, error) {
|
||||||
c, err := docker.NewVersionedClientFromEnv("1.18")
|
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, fmt.Errorf("unable to connect to docker: %s", err)
|
return 0, fmt.Errorf("unable to connect to docker: %s", err)
|
||||||
}
|
}
|
||||||
container, err := c.InspectContainer(containerID)
|
container, err := c.InspectContainer(containerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, fmt.Errorf("unable to inspect container %s: %s", containerID, err)
|
return 0, fmt.Errorf("unable to inspect container %s: %s", containerID, err)
|
||||||
}
|
}
|
||||||
if container.State.Pid == 0 {
|
if container.State.Pid == 0 {
|
||||||
return 0, 0, fmt.Errorf("container %s not running", containerID)
|
return 0, fmt.Errorf("container %s not running", containerID)
|
||||||
}
|
}
|
||||||
ns, err := netns.GetFromPid(container.State.Pid)
|
return container.State.Pid, nil
|
||||||
if err != nil {
|
|
||||||
return 0, 0, fmt.Errorf("unable to open namespace for container %s: %s", containerID, err)
|
|
||||||
}
|
|
||||||
return container.State.Pid, ns, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func processExists(pid int) bool {
|
func processExists(pid int) bool {
|
||||||
@@ -99,7 +100,7 @@ func detach(args []string) error {
|
|||||||
cmdUsage("detach-container", "<container-id> <cidr>...")
|
cmdUsage("detach-container", "<container-id> <cidr>...")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ns, err := containerPidAndNs(args[0])
|
pid, err := containerPid(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -107,5 +108,5 @@ func detach(args []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return weavenet.DetachContainer(ns, args[0], weavenet.VethName, cidrs)
|
return weavenet.DetachContainer(weavenet.NSPathByPid(pid), args[0], weavenet.VethName, cidrs)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
cni "github.com/appc/cni/pkg/skel"
|
||||||
|
|
||||||
|
weaveapi "github.com/weaveworks/weave/api"
|
||||||
|
"github.com/weaveworks/weave/common"
|
||||||
|
ipamplugin "github.com/weaveworks/weave/plugin/ipam"
|
||||||
|
netplugin "github.com/weaveworks/weave/plugin/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func cniNet(args []string) error {
|
||||||
|
weave := weaveapi.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), common.Log)
|
||||||
|
n := netplugin.NewCNIPlugin(weave)
|
||||||
|
cni.PluginMain(n.CmdAdd, n.CmdDel)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cniIPAM(args []string) error {
|
||||||
|
weave := weaveapi.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), common.Log)
|
||||||
|
i := ipamplugin.NewIpam(weave)
|
||||||
|
cni.PluginMain(i.CmdAdd, i.CmdDel)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -4,6 +4,10 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
var commands map[string]func([]string) error
|
var commands map[string]func([]string) error
|
||||||
@@ -24,10 +28,35 @@ func init() {
|
|||||||
"attach-container": attach,
|
"attach-container": attach,
|
||||||
"detach-container": detach,
|
"detach-container": detach,
|
||||||
"configure-arp": configureARP,
|
"configure-arp": configureARP,
|
||||||
|
"check-iface": checkIface,
|
||||||
|
"del-iface": delIface,
|
||||||
|
"setup-iface": setupIface,
|
||||||
|
"list-netdevs": listNetDevs,
|
||||||
|
"cni-net": cniNet,
|
||||||
|
"cni-ipam": cniIPAM,
|
||||||
|
"expose-nat": exposeNAT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// force re-exec of this binary
|
||||||
|
selfPath, err := filepath.EvalSymlinks("/proc/self/exe")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
weavenet.WeaveUtilCmd = selfPath
|
||||||
|
|
||||||
|
// If no args passed, act as CNI plugin based on executable name
|
||||||
|
switch {
|
||||||
|
case len(os.Args) == 1 && strings.HasSuffix(os.Args[0], "weave-ipam"):
|
||||||
|
cniIPAM(os.Args)
|
||||||
|
os.Exit(0)
|
||||||
|
case len(os.Args) == 1 && strings.HasSuffix(os.Args[0], "weave-net"):
|
||||||
|
cniNet(os.Args)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
if len(os.Args) < 2 {
|
if len(os.Args) < 2 {
|
||||||
usage()
|
usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -1,14 +1,75 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/j-keck/arping"
|
"github.com/j-keck/arping"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// checkIface returns an error if the given interface cannot be found.
|
||||||
|
func checkIface(args []string) error {
|
||||||
|
if len(args) != 1 {
|
||||||
|
cmdUsage("check-iface", "<iface-name>")
|
||||||
|
}
|
||||||
|
ifaceName := args[0]
|
||||||
|
|
||||||
|
if _, err := netlink.LinkByName(ifaceName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func delIface(args []string) error {
|
||||||
|
if len(args) != 1 {
|
||||||
|
cmdUsage("del-iface", "<iface-name>")
|
||||||
|
}
|
||||||
|
ifName := args[0]
|
||||||
|
|
||||||
|
link, err := netlink.LinkByName(ifName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return netlink.LinkDel(link)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupIface renames the given iface and configures its ARP cache settings.
|
||||||
|
func setupIface(args []string) error {
|
||||||
|
if len(args) != 2 {
|
||||||
|
cmdUsage("setup-iface", "<iface-name> <new-iface-name>")
|
||||||
|
}
|
||||||
|
ifaceName := args[0]
|
||||||
|
newIfName := args[1]
|
||||||
|
|
||||||
|
ipt, err := iptables.New()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
link, err := netlink.LinkByName(ifaceName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := netlink.LinkSetName(link, newIfName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := weavenet.ConfigureARPCache(newIfName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ipt.Append("filter", "INPUT", "-i", newIfName, "-d", "224.0.0.0/4", "-j", "DROP"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func configureARP(args []string) error {
|
func configureARP(args []string) error {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
cmdUsage("configure-arp", "<iface-name-prefix>")
|
cmdUsage("configure-arp", "<iface-name-prefix>")
|
||||||
@@ -33,3 +94,47 @@ func configureARP(args []string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// listNetDevs outputs network ifaces identified by the given indexes
|
||||||
|
// in the format of weavenet.Dev.
|
||||||
|
func listNetDevs(args []string) error {
|
||||||
|
if len(args) == 0 {
|
||||||
|
cmdUsage("list-netdevs", "<iface-index>[ <iface-index>]")
|
||||||
|
}
|
||||||
|
|
||||||
|
indexes := make(map[int]struct{})
|
||||||
|
for _, index := range args {
|
||||||
|
if index != "" {
|
||||||
|
id, err := strconv.Atoi(index)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
indexes[id] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
links, err := netlink.LinkList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var netdevs []weavenet.Dev
|
||||||
|
|
||||||
|
for _, link := range links {
|
||||||
|
if _, found := indexes[link.Attrs().Index]; found {
|
||||||
|
netdev, err := weavenet.LinkToNetDev(link)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
netdevs = append(netdevs, netdev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nds, err := json.Marshal(netdevs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(string(nds))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ func processAddrs(args []string) error {
|
|||||||
}
|
}
|
||||||
bridgeName := args[0]
|
bridgeName := args[0]
|
||||||
|
|
||||||
pred, err := common.ConnectedToBridgePredicate(bridgeName)
|
peerIDs, err := weavenet.ConnectedToBridgeVethPeerIds(bridgeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == weavenet.ErrLinkNotFound {
|
if err == weavenet.ErrLinkNotFound {
|
||||||
return nil
|
return nil
|
||||||
@@ -26,11 +26,8 @@ func processAddrs(args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: Because network namespaces (netns) are changed many times inside the loop,
|
|
||||||
// it's NOT safe to exec any code depending on the root netns without
|
|
||||||
// wrapping with WithNetNS*.
|
|
||||||
for _, pid := range pids {
|
for _, pid := range pids {
|
||||||
netDevs, err := common.GetNetDevsWithPredicate(pid, pred)
|
netDevs, err := weavenet.GetNetDevsByVethPeerIds(pid, peerIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -17,7 +17,6 @@ import (
|
|||||||
|
|
||||||
docker "github.com/fsouza/go-dockerclient"
|
docker "github.com/fsouza/go-dockerclient"
|
||||||
weaveapi "github.com/weaveworks/weave/api"
|
weaveapi "github.com/weaveworks/weave/api"
|
||||||
"github.com/weaveworks/weave/common"
|
|
||||||
weavedocker "github.com/weaveworks/weave/common/docker"
|
weavedocker "github.com/weaveworks/weave/common/docker"
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
"github.com/weaveworks/weave/net/address"
|
"github.com/weaveworks/weave/net/address"
|
||||||
@@ -149,7 +148,7 @@ func NewProxy(c Config) (*Proxy, error) {
|
|||||||
p.client = client.Client
|
p.client = client.Client
|
||||||
|
|
||||||
if !p.WithoutDNS {
|
if !p.WithoutDNS {
|
||||||
netDev, err := common.GetBridgeNetDev(c.DockerBridge)
|
netDev, err := weavenet.GetBridgeNetDev(c.DockerBridge)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -625,7 +624,7 @@ func (proxy *Proxy) updateContainerNetworkSettings(container jsonObject) error {
|
|||||||
if err := proxy.waitForStartByIdent(containerID); err != nil {
|
if err := proxy.waitForStartByIdent(containerID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
netDevs, err := common.GetWeaveNetDevs(pid)
|
netDevs, err := weavenet.GetWeaveNetDevs(pid)
|
||||||
if err != nil || len(netDevs) == 0 || len(netDevs[0].CIDRs) == 0 {
|
if err != nil || len(netDevs) == 0 || len(netDevs[0].CIDRs) == 0 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+78
-8
@@ -50,7 +50,8 @@ type FastDatapath struct {
|
|||||||
seenMACs map[MAC]struct{}
|
seenMACs map[MAC]struct{}
|
||||||
|
|
||||||
// vxlan vports associated with the given UDP ports
|
// vxlan vports associated with the given UDP ports
|
||||||
vxlanVportIDs map[int]odp.VportID
|
vxlanUDPPorts map[int]odp.VportID
|
||||||
|
vxlanVportIDs map[odp.VportID]struct{}
|
||||||
mainVxlanVportID odp.VportID
|
mainVxlanVportID odp.VportID
|
||||||
|
|
||||||
// A singleton pool for the occasions when we need to decode
|
// A singleton pool for the occasions when we need to decode
|
||||||
@@ -87,7 +88,8 @@ func NewFastDatapath(iface *net.Interface, port int) (*FastDatapath, error) {
|
|||||||
sendToPort: nil,
|
sendToPort: nil,
|
||||||
sendToMAC: make(map[MAC]bridgeSender),
|
sendToMAC: make(map[MAC]bridgeSender),
|
||||||
seenMACs: make(map[MAC]struct{}),
|
seenMACs: make(map[MAC]struct{}),
|
||||||
vxlanVportIDs: make(map[int]odp.VportID),
|
vxlanUDPPorts: make(map[int]odp.VportID),
|
||||||
|
vxlanVportIDs: make(map[odp.VportID]struct{}),
|
||||||
forwarders: make(map[mesh.PeerName]*fastDatapathForwarder),
|
forwarders: make(map[mesh.PeerName]*fastDatapathForwarder),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,6 +320,11 @@ func (fastDatapathOverlay) AddFeaturesTo(features map[string]string) {
|
|||||||
// OverlaySwitch.
|
// OverlaySwitch.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FastDPStatus struct {
|
||||||
|
Vports []VportStatus
|
||||||
|
Flows []FlowStatus
|
||||||
|
}
|
||||||
|
|
||||||
type FlowStatus odp.FlowInfo
|
type FlowStatus odp.FlowInfo
|
||||||
|
|
||||||
func (flowStatus *FlowStatus) MarshalJSON() ([]byte, error) {
|
func (flowStatus *FlowStatus) MarshalJSON() ([]byte, error) {
|
||||||
@@ -374,15 +381,28 @@ func (fastdp fastDatapathOverlay) Diagnostics() interface{} {
|
|||||||
flowStatuses = append(flowStatuses, FlowStatus(flow))
|
flowStatuses = append(flowStatuses, FlowStatus(flow))
|
||||||
}
|
}
|
||||||
|
|
||||||
return struct {
|
return FastDPStatus{
|
||||||
Vports []VportStatus
|
|
||||||
Flows []FlowStatus
|
|
||||||
}{
|
|
||||||
vportStatuses,
|
vportStatuses,
|
||||||
flowStatuses,
|
flowStatuses,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FastDPMetrics struct {
|
||||||
|
Flows int
|
||||||
|
TotalPackets uint64
|
||||||
|
TotalBytes uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s FastDPStatus) Metrics() interface{} {
|
||||||
|
var m FastDPMetrics
|
||||||
|
m.Flows = len(s.Flows)
|
||||||
|
for _, flow := range s.Flows {
|
||||||
|
m.TotalPackets += flow.Packets
|
||||||
|
m.TotalBytes += flow.Bytes
|
||||||
|
}
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
func (fastdp fastDatapathOverlay) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
|
func (fastdp fastDatapathOverlay) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
|
||||||
fastdp.lock.Lock()
|
fastdp.lock.Lock()
|
||||||
defer fastdp.lock.Unlock()
|
defer fastdp.lock.Unlock()
|
||||||
@@ -415,7 +435,7 @@ func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
|
|||||||
fastdp.lock.Lock()
|
fastdp.lock.Lock()
|
||||||
defer fastdp.lock.Unlock()
|
defer fastdp.lock.Unlock()
|
||||||
|
|
||||||
if vxlanVportID, present := fastdp.vxlanVportIDs[udpPort]; present {
|
if vxlanVportID, present := fastdp.vxlanUDPPorts[udpPort]; present {
|
||||||
return vxlanVportID, nil
|
return vxlanVportID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,7 +461,8 @@ func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fastdp.vxlanVportIDs[udpPort] = vxlanVportID
|
fastdp.vxlanUDPPorts[udpPort] = vxlanVportID
|
||||||
|
fastdp.vxlanVportIDs[vxlanVportID] = struct{}{}
|
||||||
fastdp.missHandlers[vxlanVportID] = func(fks odp.FlowKeys, lock *fastDatapathLock) FlowOp {
|
fastdp.missHandlers[vxlanVportID] = func(fks odp.FlowKeys, lock *fastDatapathLock) FlowOp {
|
||||||
log.Debug("ODP miss: ", fks, " on port ", vxlanVportID)
|
log.Debug("ODP miss: ", fks, " on port ", vxlanVportID)
|
||||||
tunnel := fks[odp.OVS_KEY_ATTR_TUNNEL].(odp.TunnelFlowKey)
|
tunnel := fks[odp.OVS_KEY_ATTR_TUNNEL].(odp.TunnelFlowKey)
|
||||||
@@ -1116,6 +1137,11 @@ func (fastdp *FastDatapath) send(fops FlowOp, frame []byte, lock *fastDatapathLo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fastdp.isHairpinFlow(&flow) {
|
||||||
|
log.Error("Vetoed installation of hairpin flow ", flow)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if dec != nil {
|
if dec != nil {
|
||||||
// put the decoder back
|
// put the decoder back
|
||||||
lock.relock()
|
lock.relock()
|
||||||
@@ -1153,6 +1179,50 @@ func (fastdp *FastDatapath) takeDecoder(lock *fastDatapathLock) *EthernetDecoder
|
|||||||
return dec
|
return dec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A isHairpinFlow checks whether the flow is created due to enabled hairpin
|
||||||
|
// mode on the weave bridge port which attaches the datapath. Such flow is
|
||||||
|
// identified by either:
|
||||||
|
//
|
||||||
|
// * in_port == out_port, where in_port is non-vxlan vport;
|
||||||
|
// * a packet is sent back to a vxlan tunnel it has been received from and
|
||||||
|
// the tunnel id is either the same or dstPeer and srcPeer are reversed.
|
||||||
|
func (fastdp *FastDatapath) isHairpinFlow(flow *odp.FlowSpec) bool {
|
||||||
|
var (
|
||||||
|
vxlanKey odp.TunnelAttrs
|
||||||
|
inVport odp.VportID
|
||||||
|
inVxlan bool
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, key := range flow.FlowKeys {
|
||||||
|
switch k := key.(type) {
|
||||||
|
case odp.InPortFlowKey:
|
||||||
|
inVport = k.VportID()
|
||||||
|
case odp.TunnelFlowKey:
|
||||||
|
inVxlan = true
|
||||||
|
vxlanKey = k.Key()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, action := range flow.Actions {
|
||||||
|
switch a := action.(type) {
|
||||||
|
case odp.SetTunnelAction:
|
||||||
|
if inVxlan && a.TunnelAttrs.TunnelId == vxlanKey.TunnelId &&
|
||||||
|
a.TunnelAttrs.Ipv4Src == vxlanKey.Ipv4Dst &&
|
||||||
|
a.TunnelAttrs.Ipv4Dst == vxlanKey.Ipv4Src {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case odp.OutputAction:
|
||||||
|
if a.VportID() == inVport {
|
||||||
|
if _, ok := fastdp.vxlanVportIDs[a.VportID()]; !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type odpActionsFlowOp struct {
|
type odpActionsFlowOp struct {
|
||||||
NonDiscardingFlowOp
|
NonDiscardingFlowOp
|
||||||
fastdp *FastDatapath
|
fastdp *FastDatapath
|
||||||
|
|||||||
@@ -124,6 +124,13 @@ func (router *NetworkRouter) handleCapturedPacket(key PacketKey) FlowOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (router *NetworkRouter) handleForwardedPacket(key ForwardPacketKey) FlowOp {
|
func (router *NetworkRouter) handleForwardedPacket(key ForwardPacketKey) FlowOp {
|
||||||
|
srcMac := net.HardwareAddr(key.SrcMAC[:])
|
||||||
|
dstMac := net.HardwareAddr(key.DstMAC[:])
|
||||||
|
|
||||||
|
if key.SrcPeer == router.Ourself.Peer {
|
||||||
|
log.Warn("Received own packet to peer ", key.DstPeer, " from MAC (", srcMac, ") to (", dstMac, ")")
|
||||||
|
}
|
||||||
|
|
||||||
if key.DstPeer != router.Ourself.Peer {
|
if key.DstPeer != router.Ourself.Peer {
|
||||||
// it's not for us, we're just relaying it
|
// it's not for us, we're just relaying it
|
||||||
router.PacketLogging.LogForwardPacket("Relaying", key)
|
router.PacketLogging.LogForwardPacket("Relaying", key)
|
||||||
@@ -134,9 +141,6 @@ func (router *NetworkRouter) handleForwardedPacket(key ForwardPacketKey) FlowOp
|
|||||||
// (because the DstPeer on a forwarded broadcast packet is
|
// (because the DstPeer on a forwarded broadcast packet is
|
||||||
// always set to the peer being forwarded to)
|
// always set to the peer being forwarded to)
|
||||||
|
|
||||||
srcMac := net.HardwareAddr(key.SrcMAC[:])
|
|
||||||
dstMac := net.HardwareAddr(key.DstMAC[:])
|
|
||||||
|
|
||||||
switch newSrcMac, conflictPeer := router.Macs.AddForced(srcMac, key.SrcPeer); {
|
switch newSrcMac, conflictPeer := router.Macs.AddForced(srcMac, key.SrcPeer); {
|
||||||
case newSrcMac:
|
case newSrcMac:
|
||||||
log.Print("Discovered remote MAC ", srcMac, " at ", key.SrcPeer)
|
log.Print("Discovered remote MAC ", srcMac, " at ", key.SrcPeer)
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ title: Integrating Kubernetes and Mesos via the CNI Plugin
|
|||||||
menu_order: 65
|
menu_order: 65
|
||||||
---
|
---
|
||||||
|
|
||||||
|
> The recommended way of using Weave with Kubernetes is via the new
|
||||||
|
> [Kubernetes Addon](/site/kube-addon.md). The instructions below
|
||||||
|
> remain valid however, and are still the recommended method for
|
||||||
|
> integrating with Mesos.
|
||||||
|
|
||||||
CNI, the [_Container Network Interface_](https://github.com/containernetworking/cni),
|
CNI, the [_Container Network Interface_](https://github.com/containernetworking/cni),
|
||||||
is a proposed standard for configuring network interfaces for Linux
|
is a proposed standard for configuring network interfaces for Linux
|
||||||
application containers. CNI is supported by
|
application containers. CNI is supported by
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ menu_order: 20
|
|||||||
* [Address Allocation (IPAM)](#addressing)
|
* [Address Allocation (IPAM)](#addressing)
|
||||||
* [Naming and Discovery](#naming-and-discovery)
|
* [Naming and Discovery](#naming-and-discovery)
|
||||||
* [Application Isolation](#application-isolation)
|
* [Application Isolation](#application-isolation)
|
||||||
|
* [Network Policy](#network-policy)
|
||||||
* [Dynamic Network Attachment](#dynamic-network-attachment)
|
* [Dynamic Network Attachment](#dynamic-network-attachment)
|
||||||
* [Security](#security)
|
* [Security](#security)
|
||||||
* [Host Network Integration](#host-network-integration)
|
* [Host Network Integration](#host-network-integration)
|
||||||
@@ -139,6 +140,11 @@ See [Isolating Applications](/site/using-weave/application-isolation.md)
|
|||||||
for information on how to use the isolation-through-subnets
|
for information on how to use the isolation-through-subnets
|
||||||
technique with Weave Net.
|
technique with Weave Net.
|
||||||
|
|
||||||
|
###<a name="network-policy"></a>Network Policy
|
||||||
|
|
||||||
|
The Weave [Kubernetes Addon](/site/kube-addon.md) includes a network
|
||||||
|
policy controller that implements [Kubernetes Network
|
||||||
|
Policies](http://kubernetes.io/docs/user-guide/networkpolicies/).
|
||||||
|
|
||||||
###<a name="dynamic-network-attachment"></a>Dynamic Network Attachment
|
###<a name="dynamic-network-attachment"></a>Dynamic Network Attachment
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
---
|
||||||
|
title: Integrating Kubernetes via the Addon
|
||||||
|
menu_order: 63
|
||||||
|
---
|
||||||
|
|
||||||
|
The following topics are discussed:
|
||||||
|
|
||||||
|
* [Installation](#install)
|
||||||
|
* [Network Policy Controller](#npc)
|
||||||
|
* [Upgrading the Daemon Sets](#daemon-sets)
|
||||||
|
* [Network Policy Controller](#npc)
|
||||||
|
* [Troubleshooting Blocked Connections](#blocked-connections)
|
||||||
|
* [Changing Configuration Options](#configuration-options)
|
||||||
|
|
||||||
|
|
||||||
|
##<a name=”install”></a> Installation
|
||||||
|
|
||||||
|
Weave Net can be installed onto your CNI-enabled Kubernetes cluster
|
||||||
|
with a single command:
|
||||||
|
|
||||||
|
```
|
||||||
|
kubectl apply -f https://git.io/weave-kube
|
||||||
|
```
|
||||||
|
|
||||||
|
After a few seconds, a Weave Net pod should be running on each
|
||||||
|
Node and any further pods you create will be automatically attached to the Weave
|
||||||
|
network.
|
||||||
|
|
||||||
|
**Note:** This command requires Kubernetes 1.4 or later.
|
||||||
|
|
||||||
|
> CNI, the [_Container Network Interface_](https://github.com/containernetworking/cni),
|
||||||
|
> is a proposed standard for configuring network interfaces for Linux
|
||||||
|
> containers.
|
||||||
|
>
|
||||||
|
> If you do not already have a CNI-enabled cluster, you can bootstrap
|
||||||
|
> one easily with
|
||||||
|
> [kubeadm](http://kubernetes.io/docs/getting-started-guides/kubeadm/).
|
||||||
|
>
|
||||||
|
> Alternatively, you can [configure CNI yourself](http://kubernetes.io/docs/admin/network-plugins/#cni)
|
||||||
|
|
||||||
|
**Note:** If using the [Weave CNI
|
||||||
|
Plugin](/site/cni-plugin.md) from a prior full install of Weave Net with your
|
||||||
|
cluster, you must first uninstall it before applying the Weave-kube addon.
|
||||||
|
Shut down Kubernetes, and _on all nodes_ perform the following:
|
||||||
|
|
||||||
|
* `weave reset`
|
||||||
|
* Remove any separate provisions you may have made to run Weave at
|
||||||
|
boot-time, e.g. `systemd` units
|
||||||
|
* `rm /opt/cni/bin/weave-*`
|
||||||
|
|
||||||
|
Then relaunch Kubernetes and install the addon as described
|
||||||
|
above.
|
||||||
|
|
||||||
|
The URL [https://git.io/weave-kube](https://git.io/weave-kube) points
|
||||||
|
to the YAML file for the latest release of the Weave Net addon.
|
||||||
|
Historic versions are archived on our [GitHub release
|
||||||
|
page](https://github.com/weaveworks/weave/releases).
|
||||||
|
|
||||||
|
###<a name=”daemon-sets”></a> Upgrading the Daemon Sets
|
||||||
|
|
||||||
|
Kubernetes does not currently support rolling upgrades of daemon sets,
|
||||||
|
and so you will need to perform the procedure manually:
|
||||||
|
|
||||||
|
* Apply the updated addon manifest `kubectl apply -f https://git.io/weave-kube`
|
||||||
|
* Kill each Weave Net pod with `kubectl delete` and then wait for it to reboot before moving on to the next pod.
|
||||||
|
|
||||||
|
##<a name="npc"></a>Network Policy Controller
|
||||||
|
|
||||||
|
The addon also supports the [Kubernetes policy
|
||||||
|
API](http://kubernetes.io/docs/user-guide/networkpolicies/) so that
|
||||||
|
you can securely isolate pods from each other based on namespaces and
|
||||||
|
labels. For more information on configuring network policies in
|
||||||
|
Kubernetes see the
|
||||||
|
[walkthrough](http://kubernetes.io/docs/getting-started-guides/network-policy/walkthrough/)
|
||||||
|
and the [NetworkPolicy API object
|
||||||
|
definition](http://kubernetes.io/docs/api-reference/extensions/v1beta1/definitions/#_v1beta1_networkpolicy).
|
||||||
|
|
||||||
|
###<a name=”blocked-connections”></a> Troubleshooting Blocked Connections
|
||||||
|
|
||||||
|
If you suspect that legitimate traffic is being blocked by the Weave Network Policy Controller, check the `weave-npc` container's logs:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ kubectl logs $(kubectl get pods --all-namespaces | grep weave-net | awk '{print $2}') -n kube-system weave-npc
|
||||||
|
```
|
||||||
|
|
||||||
|
When the Weave Network Policy Controller blocks a connection, it logs the following details about it:
|
||||||
|
|
||||||
|
* protocol used,
|
||||||
|
* source IP and port,
|
||||||
|
* destination IP and port,
|
||||||
|
|
||||||
|
as per the below example:
|
||||||
|
|
||||||
|
```
|
||||||
|
TCP connection from 10.32.0.7:56648 to 10.32.0.11:80 blocked by Weave NPC.
|
||||||
|
UDP connection from 10.32.0.7:56648 to 10.32.0.11:80 blocked by Weave NPC.
|
||||||
|
```
|
||||||
|
|
||||||
|
###<a name=”configuration-options”></a> Changing Configuration Options
|
||||||
|
|
||||||
|
The default configuration settings can be changed by saving and editing the
|
||||||
|
addon YAML before running `kubectl apply`. Additional arguments may be
|
||||||
|
supplied to the Weave router process by adding them to the `command:`
|
||||||
|
array in the YAML file.
|
||||||
|
|
||||||
|
Some parameters are changed by environment variables; these can be
|
||||||
|
inserted into the YAML file like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
containers:
|
||||||
|
- name: weave
|
||||||
|
env:
|
||||||
|
- name: IPALLOC_RANGE
|
||||||
|
value: 10.0.0.0/16
|
||||||
|
```
|
||||||
|
|
||||||
|
The list of variables you can set is:
|
||||||
|
|
||||||
|
* CHECKPOINT\_DISABLE - if set to 1, disable checking for new Weave Net
|
||||||
|
versions (default is blank, i.e. check is enabled)
|
||||||
|
* IPALLOC\_RANGE - the range of IP addresses used by Weave Net
|
||||||
|
and the subnet they are placed in (CIDR format; default 10.32.0.0/12)
|
||||||
|
* EXPECT\_NPC - set to 0 to disable Network Policy Controller (default is on)
|
||||||
|
* KUBE\_PEERS - list of addresses of peers in the Kubernetes cluster
|
||||||
|
(default is to fetch the list from the api-server)
|
||||||
|
* IPALLOC\_INIT - set the initialization mode of the [IP Address
|
||||||
|
Manager](/site/operational-guide/concepts.md#ip-address-manager)
|
||||||
|
(defaults to consensus amongst the KUBE\_PEERS)
|
||||||
|
* WEAVE\_EXPOSE\_IP - set the IP address used as a gateway from the
|
||||||
|
Weave network to the host network - this is useful if you are
|
||||||
|
configuring the addon as a static pod.
|
||||||
+148
@@ -0,0 +1,148 @@
|
|||||||
|
---
|
||||||
|
title: Monitoring with Prometheus
|
||||||
|
menu_order: 105
|
||||||
|
---
|
||||||
|
|
||||||
|
Two endpoints are exposed: one for the Weave Net router, and, when deployed as
|
||||||
|
a [Kubernetes Addon](/site/kube-addon.md), one for the [network policy
|
||||||
|
controller](/site/kube-addon.md#npc).
|
||||||
|
|
||||||
|
### Router Metrics
|
||||||
|
|
||||||
|
The endpoint address is `localhost:6782`; the following metrics are
|
||||||
|
exposed:
|
||||||
|
|
||||||
|
* `weave_connections` - Number of peer-to-peer connections.
|
||||||
|
* `weave_connection_terminations_total` - Number of peer-to-peer
|
||||||
|
connections terminated.
|
||||||
|
* `weave_ips` - Number of IP addresses.
|
||||||
|
* `weave_max_ips` - Size of IP address space used by allocator.
|
||||||
|
* `weave_dns_entries` - Number of DNS entries.
|
||||||
|
* `weave_flows` - Number of FastDP flows.
|
||||||
|
|
||||||
|
### Kubernetes Network Policy Controller Metrics
|
||||||
|
|
||||||
|
The endpoint address is `localhost:6781`; the following metric is
|
||||||
|
exposed:
|
||||||
|
|
||||||
|
* `weavenpc_blocked_connections_total` - Connection attempts blocked
|
||||||
|
by policy controller.
|
||||||
|
|
||||||
|
# Static Configuration for Weave Net
|
||||||
|
|
||||||
|
The following YAML fragment can be used in your Prometheus configuration to
|
||||||
|
scrape the router metrics endpoint:
|
||||||
|
|
||||||
|
```
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: 'weave'
|
||||||
|
scrape_interval: 15s
|
||||||
|
static_configs:
|
||||||
|
- targets: ['localhost:6782']
|
||||||
|
```
|
||||||
|
|
||||||
|
# Discovery Configuration for Kubernetes
|
||||||
|
|
||||||
|
If you're running Weave in conjunction with Kubernetes, it is possible
|
||||||
|
to discover the endpoints for all nodes automatically. The following
|
||||||
|
Kubernetes config will install and configure Prometheus 1.3 on your
|
||||||
|
cluster and configure it to discover and scrape the Weave endpoints:
|
||||||
|
|
||||||
|
```
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: prometheus
|
||||||
|
data:
|
||||||
|
prometheus.yml: |-
|
||||||
|
global:
|
||||||
|
scrape_interval: 15s
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: 'weave'
|
||||||
|
kubernetes_sd_configs:
|
||||||
|
- api_server:
|
||||||
|
role: pod
|
||||||
|
relabel_configs:
|
||||||
|
- source_labels: [__meta_kubernetes_namespace,__meta_kubernetes_pod_label_name]
|
||||||
|
action: keep
|
||||||
|
regex: ^kube-system;weave-net$
|
||||||
|
- source_labels: [__meta_kubernetes_pod_container_name,__address__]
|
||||||
|
action: replace
|
||||||
|
target_label: __address__
|
||||||
|
regex: ^weave;(.+?)(?::\d+)?$
|
||||||
|
replacement: $1:6782
|
||||||
|
- source_labels: [__meta_kubernetes_pod_container_name,__address__]
|
||||||
|
action: replace
|
||||||
|
target_label: __address__
|
||||||
|
regex: ^weave-npc;(.+?)(?::\d+)?$
|
||||||
|
replacement: $1:6781
|
||||||
|
- source_labels: [__meta_kubernetes_pod_container_name]
|
||||||
|
action: replace
|
||||||
|
target_label: job
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
name: prometheus
|
||||||
|
name: prometheus
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: prometheus
|
||||||
|
type: NodePort
|
||||||
|
ports:
|
||||||
|
- name: prometheus
|
||||||
|
protocol: TCP
|
||||||
|
port: 9090
|
||||||
|
nodePort: 30900
|
||||||
|
---
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: prometheus
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: prometheus
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
name: prometheus
|
||||||
|
labels:
|
||||||
|
app: prometheus
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: prometheus
|
||||||
|
image: prom/prometheus:v1.3.0
|
||||||
|
args:
|
||||||
|
- '-config.file=/etc/prometheus/prometheus.yml'
|
||||||
|
ports:
|
||||||
|
- name: web
|
||||||
|
containerPort: 9090
|
||||||
|
volumeMounts:
|
||||||
|
- name: config-volume
|
||||||
|
mountPath: /etc/prometheus
|
||||||
|
volumes:
|
||||||
|
- name: config-volume
|
||||||
|
configMap:
|
||||||
|
name: prometheus
|
||||||
|
```
|
||||||
|
|
||||||
|
# Weave Cloud Integration
|
||||||
|
|
||||||
|
Finally, you can configure your local Prometheus instance to push
|
||||||
|
metrics to Weave Cloud by including the following fragment in the
|
||||||
|
`prometheus.yml` above (after replacing `<WEAVE_CLOUD_TOKEN>` with
|
||||||
|
your own token):
|
||||||
|
|
||||||
|
```
|
||||||
|
remote_write:
|
||||||
|
url: http://frontend.dev.weave.works/api/prom/push
|
||||||
|
basic_auth:
|
||||||
|
password: <WEAVE_CLOUD_TOKEN>
|
||||||
|
```
|
||||||
|
|
||||||
|
For information on getting started with Weave Cloud metrics, including
|
||||||
|
on how to obtain your Weave Cloud token, please go
|
||||||
|
[here](https://github.com/weaveworks/cortex).
|
||||||
@@ -20,7 +20,7 @@ once and your system is configured to start Docker on boot, then Weave Net
|
|||||||
will start automatically on system restarts.
|
will start automatically on system restarts.
|
||||||
|
|
||||||
If you are aiming for a non-interactive installation, use
|
If you are aiming for a non-interactive installation, use
|
||||||
[systemd]((/site/installing-weave/systemd.md) or a similar init system to
|
[systemd](/site/installing-weave/systemd.md) or a similar init system to
|
||||||
launch Weave using the `--no-restart` flag after Docker has been started.
|
launch Weave using the `--no-restart` flag after Docker has been started.
|
||||||
|
|
||||||
##<a name="detect-reclaim-ipam"></a>Detecting and Reclaiming Lost IP Address Space
|
##<a name="detect-reclaim-ipam"></a>Detecting and Reclaiming Lost IP Address Space
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ latest snapshot release using:
|
|||||||
sudo chmod a+x /usr/local/bin/weave
|
sudo chmod a+x /usr/local/bin/weave
|
||||||
weave setup
|
weave setup
|
||||||
|
|
||||||
Snapshot releases report the script version as "(unreleased version)",
|
Snapshot releases report the script version as "unreleased",
|
||||||
and the container image versions as git hashes.
|
and the container image versions as git hashes.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Docker containers are started with the --net flag, for example:
|
|||||||
|
|
||||||
Note that if a Docker container is started with the --net flag, then the Weave Docker API Proxy
|
Note that if a Docker container is started with the --net flag, then the Weave Docker API Proxy
|
||||||
is automatically disabled and is not used to attach containers.
|
is automatically disabled and is not used to attach containers.
|
||||||
See [Integrating Docker via the Network Plugin](plugin.md).
|
See [Integrating Docker via the Network Plugin](/site/plugin.md).
|
||||||
|
|
||||||
**3.** Containers can also be attached to the Weave network with `weave run` commands. This method also
|
**3.** Containers can also be attached to the Weave network with `weave run` commands. This method also
|
||||||
does not use the Weave Docker API Proxy.
|
does not use the Weave Docker API Proxy.
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ UNIVERSE=10.2.0.0/16
|
|||||||
|
|
||||||
start_suite "weave status/report"
|
start_suite "weave status/report"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
weave_on $HOST1 launch --ipalloc-range $UNIVERSE --name 8a:3e:3e:3e:3e:3e --nickname nicknamington
|
weave_on $HOST1 launch --ipalloc-range $UNIVERSE --name 8a:3e:3e:3e:3e:3e --nickname nicknamington
|
||||||
|
|
||||||
check() {
|
check() {
|
||||||
@@ -29,10 +31,33 @@ assert "weave_on $HOST1 status connections | tr -s ' ' | cut -d ' ' -f 2" "10.2.
|
|||||||
assert "weave_on $HOST1 report -f '{{.VersionCheck.Enabled}}'" "false"
|
assert "weave_on $HOST1 report -f '{{.VersionCheck.Enabled}}'" "false"
|
||||||
assert_raises "weave_on $HOST1 status | grep 'version check update disabled'"
|
assert_raises "weave_on $HOST1 status | grep 'version check update disabled'"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
weave_on $HOST1 reset
|
||||||
|
# Guarantee the version check fails by feeding an unresponsive IP address into the resolver
|
||||||
|
$SSH $HOST1 tee /tmp/hosts.checkpoint-api > /dev/null <<EOF
|
||||||
|
127.0.0.1 localhost
|
||||||
|
127.1.1.1 checkpoint-api.weave.works
|
||||||
|
EOF
|
||||||
|
CHECKPOINT_DISABLE="" WEAVE_DOCKER_ARGS="-v /tmp/hosts.checkpoint-api:/etc/hosts" weave_on $HOST1 launch
|
||||||
|
|
||||||
|
wait_for_version_check_error() {
|
||||||
|
while true; do
|
||||||
|
docker_on $HOST1 logs weave 2>&1 | grep 'Error checking version' && return
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
assert_raises "timeout 30 cat <( wait_for_version_check_error )"
|
||||||
|
|
||||||
|
assert "weave_on $HOST1 report -f '{{.VersionCheck.Enabled}}'" "true"
|
||||||
|
assert "weave_on $HOST1 report -f '{{.VersionCheck.Success}}'" "false"
|
||||||
|
assert "weave_on $HOST1 report -f '{{.VersionCheck.NewVersion}}'" ""
|
||||||
|
|
||||||
weave_on $HOST1 reset
|
weave_on $HOST1 reset
|
||||||
|
|
||||||
CHECKPOINT_DISABLE="" weave_on $HOST1 launch
|
CHECKPOINT_DISABLE="" weave_on $HOST1 launch
|
||||||
assert "weave_on $HOST1 report -f '{{.VersionCheck.Enabled}}'" "true"
|
assert "weave_on $HOST1 report -f '{{.VersionCheck.Enabled}}'" "true"
|
||||||
|
assert "weave_on $HOST1 report -f '{{.VersionCheck.Success}}'" "true"
|
||||||
|
|
||||||
NEW_VSN=$(weave_on $HOST1 report -f "{{.VersionCheck.NewVersion}}")
|
NEW_VSN=$(weave_on $HOST1 report -f "{{.VersionCheck.NewVersion}}")
|
||||||
if [ -z "$NEW_VSN" ]; then
|
if [ -z "$NEW_VSN" ]; then
|
||||||
@@ -41,4 +66,6 @@ else
|
|||||||
assert_raises "weave_on $HOST1 status | grep \"version $NEW_VSN available - please upgrade!\""
|
assert_raises "weave_on $HOST1 status | grep \"version $NEW_VSN available - please upgrade!\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end_suite
|
end_suite
|
||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
. ./config.sh
|
||||||
|
|
||||||
|
start_suite "Detect hairpin mode"
|
||||||
|
|
||||||
|
weave_on $HOST1 launch
|
||||||
|
|
||||||
|
assert_raises "run_on $HOST1 sudo ip link set vethwe-bridge type bridge_slave hairpin on"
|
||||||
|
assert_raises "docker_on $HOST1 logs weave 2>&1 | grep -q 'Hairpin mode enabled on \"vethwe-bridge\"'"
|
||||||
|
|
||||||
|
end_suite
|
||||||
@@ -45,15 +45,33 @@ C2IP=$(container_ip $HOST1 c2)
|
|||||||
assert_raises "exec_on $HOST1 c1 $PING $C2IP"
|
assert_raises "exec_on $HOST1 c1 $PING $C2IP"
|
||||||
assert_raises "exec_on $HOST1 c2 $PING $C1IP"
|
assert_raises "exec_on $HOST1 c2 $PING $C1IP"
|
||||||
|
|
||||||
# Now remove and start a new container to see if IP address re-use breaks things
|
# Now remove and start a new container to see if anything breaks
|
||||||
docker_on $HOST1 rm -f c2
|
docker_on $HOST1 rm -f c2
|
||||||
|
|
||||||
docker_on $HOST1 run --net=none --name=c3 -dt $SMALL_IMAGE /bin/sh
|
C3=$(docker_on $HOST1 run --net=none --name=c3 -dt $SMALL_IMAGE /bin/sh)
|
||||||
|
|
||||||
cni_connect $HOST1 c3 <<EOF
|
cni_connect $HOST1 c3 <<EOF
|
||||||
{ "name": "weave", "type": "weave-net" }
|
{ "name": "weave", "type": "weave-net" }
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
assert_raises "exec_on $HOST1 c1 $PING $C2IP"
|
C3IP=$(container_ip $HOST1 c3)
|
||||||
|
|
||||||
|
# CNI shouldn't re-use the address until we call DEL
|
||||||
|
assert_raises "[ $C2IP != $C3IP ]"
|
||||||
|
assert_raises "exec_on $HOST1 c1 $PING $C3IP"
|
||||||
|
|
||||||
|
|
||||||
|
# Ensure existing containers can reclaim their IP addresses after CNI has been used -- see #2548
|
||||||
|
stop_weave_on $HOST1
|
||||||
|
|
||||||
|
# Ensure no warning is printed to the standard error:
|
||||||
|
ACTUAL_OUTPUT=$(weave_on $HOST1 launch-router 2>&1)
|
||||||
|
EXPECTED_OUTPUT=$(docker_on $HOST1 inspect --format="{{.Id}}" weave)
|
||||||
|
|
||||||
|
assert_raises "[ $EXPECTED_OUTPUT == $ACTUAL_OUTPUT ]"
|
||||||
|
|
||||||
|
assert "$SSH $HOST1 \"curl -s -X GET 127.0.0.1:6784/ip/$C1 | grep -o -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'\"" "$C1IP"
|
||||||
|
assert "$SSH $HOST1 \"curl -s -X GET 127.0.0.1:6784/ip/$C3 | grep -o -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'\"" "$C3IP"
|
||||||
|
|
||||||
|
|
||||||
end_suite
|
end_suite
|
||||||
Executable
+74
@@ -0,0 +1,74 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
. ./config.sh
|
||||||
|
|
||||||
|
tear_down_kubeadm() {
|
||||||
|
for host in $HOSTS; do
|
||||||
|
# If we don't stop kubelet, it will restart all the containers we're trying to kill
|
||||||
|
run_on $host "sudo systemctl stop kubelet"
|
||||||
|
rm_containers $host $(docker_on $host ps -aq)
|
||||||
|
run_on $host "test ! -d /var/lib/kubelet || sudo find /var/lib/kubelet -execdir findmnt -n -t tmpfs -o TARGET -T {} \; | uniq | xargs -r sudo umount"
|
||||||
|
run_on $host "sudo rm -r -f /etc/kubernetes /var/lib/kubelet /var/lib/etcd"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
start_suite "Test weave-kube image with Kubernetes"
|
||||||
|
|
||||||
|
TOKEN=112233.445566778899000
|
||||||
|
HOST1IP=$($SSH $HOST1 "getent hosts $HOST1 | cut -f 1 -d ' '")
|
||||||
|
SUCCESS="6 established"
|
||||||
|
|
||||||
|
tear_down_kubeadm
|
||||||
|
|
||||||
|
run_on $HOST1 "sudo systemctl start kubelet && sudo kubeadm init --token=$TOKEN"
|
||||||
|
run_on $HOST2 "sudo systemctl start kubelet && sudo kubeadm join --token=$TOKEN $HOST1IP"
|
||||||
|
run_on $HOST3 "sudo systemctl start kubelet && sudo kubeadm join --token=$TOKEN $HOST1IP"
|
||||||
|
|
||||||
|
[ -n "$COVERAGE" ] && COVERAGE_ARGS="\\n env:\\n - name: EXTRA_ARGS\\n value: \"-test.coverprofile=/home/weave/cover.prof --\""
|
||||||
|
|
||||||
|
sed -e "s%imagePullPolicy: Always%imagePullPolicy: Never$COVERAGE_ARGS%" ../prog/weave-kube/weave-daemonset.yaml | run_on $HOST1 "kubectl apply -f -"
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
wait_for_connections() {
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
if run_on $HOST1 "curl -sS http://127.0.0.1:6784/status | grep \"$SUCCESS\"" ; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "Waiting for connections"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
echo "Timed out waiting for connections to establish" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_raises wait_for_connections
|
||||||
|
|
||||||
|
# Check we can ping between the Weave bridg IPs on each host
|
||||||
|
HOST1EXPIP=$($SSH $HOST1 "weave expose")
|
||||||
|
HOST2EXPIP=$($SSH $HOST2 "weave expose")
|
||||||
|
HOST3EXPIP=$($SSH $HOST3 "weave expose")
|
||||||
|
assert_raises "run_on $HOST1 $PING $HOST2EXPIP"
|
||||||
|
assert_raises "run_on $HOST2 $PING $HOST1EXPIP"
|
||||||
|
assert_raises "run_on $HOST3 $PING $HOST2EXPIP"
|
||||||
|
|
||||||
|
# See if we can get some pods running that connect to the network
|
||||||
|
run_on $HOST1 "kubectl run hello --image=weaveworks/hello-world --replicas=3"
|
||||||
|
|
||||||
|
wait_for_pods() {
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
if run_on $HOST1 "kubectl get pods | grep 'hello.*Running'" ; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "Waiting for pods"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
echo "Timed out waiting for pods" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_raises wait_for_pods
|
||||||
|
|
||||||
|
tear_down_kubeadm
|
||||||
|
|
||||||
|
end_suite
|
||||||
+46
-11
@@ -10,8 +10,9 @@ set -e
|
|||||||
: ${KEY_FILE:=/tmp/gce_private_key.json}
|
: ${KEY_FILE:=/tmp/gce_private_key.json}
|
||||||
: ${SSH_KEY_FILE:=$HOME/.ssh/gce_ssh_key}
|
: ${SSH_KEY_FILE:=$HOME/.ssh/gce_ssh_key}
|
||||||
: ${PROJECT:=positive-cocoa-90213}
|
: ${PROJECT:=positive-cocoa-90213}
|
||||||
: ${IMAGE:=ubuntu-14-04}
|
: ${IMAGE_FAMILY:=ubuntu-1604-lts}
|
||||||
: ${TEMPLATE_NAME:=test-template-10}
|
: ${IMAGE_PROJECT:=ubuntu-os-cloud}
|
||||||
|
: ${TEMPLATE_NAME:=test-template-12}
|
||||||
: ${ZONE:=us-central1-a}
|
: ${ZONE:=us-central1-a}
|
||||||
: ${NUM_HOSTS:=5}
|
: ${NUM_HOSTS:=5}
|
||||||
SUFFIX=""
|
SUFFIX=""
|
||||||
@@ -33,8 +34,11 @@ function vm_names {
|
|||||||
|
|
||||||
# Delete all vms in this account
|
# Delete all vms in this account
|
||||||
function destroy {
|
function destroy {
|
||||||
|
if [ $(gcloud compute firewall-rules list test-allow-docker$SUFFIX 2>/dev/null | wc -l) -gt 0 ] ; then
|
||||||
|
gcloud compute firewall-rules delete test-allow-docker$SUFFIX
|
||||||
|
fi
|
||||||
names="$(vm_names)"
|
names="$(vm_names)"
|
||||||
if [ $(gcloud compute instances list --zone $ZONE -q $names | wc -l) -le 1 ] ; then
|
if [ $(gcloud compute instances list --zones $ZONE -q $names | wc -l) -le 1 ] ; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
for i in {0..10}; do
|
for i in {0..10}; do
|
||||||
@@ -72,16 +76,44 @@ function install_docker_on {
|
|||||||
name=$1
|
name=$1
|
||||||
ssh -t $name sudo bash -x -s <<EOF
|
ssh -t $name sudo bash -x -s <<EOF
|
||||||
curl -sSL https://get.docker.com/gpg | sudo apt-key add -
|
curl -sSL https://get.docker.com/gpg | sudo apt-key add -
|
||||||
curl -sSL https://get.docker.com/ | sh
|
curl -sSL https://get.docker.com/ | sed -e s/docker-engine/docker-engine=1.11.2-0~xenial/ | sh
|
||||||
apt-get update -qq;
|
apt-get update -qq;
|
||||||
apt-get install -q -y --force-yes --no-install-recommends ethtool;
|
apt-get install -q -y --force-yes --no-install-recommends ethtool;
|
||||||
|
apt-get install -q -y bc jq;
|
||||||
usermod -a -G docker vagrant;
|
usermod -a -G docker vagrant;
|
||||||
echo 'DOCKER_OPTS="-H unix:///var/run/docker.sock -H unix:///var/run/alt-docker.sock -H tcp://0.0.0.0:2375 -s overlay"' >> /etc/default/docker;
|
mkdir -p /etc/systemd/system/docker.service.d
|
||||||
service docker restart
|
cat >/etc/systemd/system/docker.service.d/override.conf <<OVERRIDE
|
||||||
|
[Service]
|
||||||
|
ExecStart=
|
||||||
|
ExecStart=/usr/bin/docker daemon -H fd:// -H unix:///var/run/alt-docker.sock -H tcp://0.0.0.0:2375 -s overlay
|
||||||
|
OVERRIDE
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl restart docker
|
||||||
|
# This installs nsenter.
|
||||||
|
docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter
|
||||||
|
docker pull alpine
|
||||||
|
docker pull aanand/docker-dnsutils
|
||||||
|
docker pull weaveworks/hello-world
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
function install_kubernetes_on {
|
||||||
|
name=$1
|
||||||
|
ssh -t $name sudo bash -x -s <<EOF
|
||||||
|
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
|
||||||
|
echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -q -y kubelet kubeadm kubectl kubernetes-cni
|
||||||
|
systemctl --now disable kubelet
|
||||||
|
# Pre-pull images required for Kubernetes
|
||||||
|
docker pull gcr.io/google_containers/etcd-amd64:2.2.5
|
||||||
|
docker pull gcr.io/google_containers/kube-apiserver-amd64:v1.4.0
|
||||||
|
docker pull gcr.io/google_containers/kube-controller-manager-amd64:v1.4.0
|
||||||
|
docker pull gcr.io/google_containers/kube-proxy-amd64:v1.4.0
|
||||||
|
docker pull gcr.io/google_containers/kube-scheduler-amd64:v1.4.0
|
||||||
|
docker pull gcr.io/google_containers/kube-discovery-amd64:1.0
|
||||||
|
docker pull gcr.io/google_containers/pause-amd64:3.0
|
||||||
EOF
|
EOF
|
||||||
# It seems we need a short delay for docker to start up, so I put this in
|
|
||||||
# a separate ssh connection. This installs nsenter.
|
|
||||||
ssh -t $name sudo docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function copy_hosts {
|
function copy_hosts {
|
||||||
@@ -95,7 +127,9 @@ function setup {
|
|||||||
destroy
|
destroy
|
||||||
|
|
||||||
names="$(vm_names)"
|
names="$(vm_names)"
|
||||||
gcloud compute instances create $names --image $TEMPLATE_NAME --zone $ZONE
|
gcloud compute instances create $names --image $TEMPLATE_NAME --zone $ZONE --tags test$SUFFIX --network=test
|
||||||
|
my_ip="$(curl -s http://ipinfo.io/ip)"
|
||||||
|
gcloud compute firewall-rules create test-allow-docker$SUFFIX --network=test --allow tcp:2375,tcp:12375 --target-tags test$SUFFIX --source-ranges $my_ip
|
||||||
gcloud compute config-ssh --ssh-key-file $SSH_KEY_FILE
|
gcloud compute config-ssh --ssh-key-file $SSH_KEY_FILE
|
||||||
sed -i '/UserKnownHostsFile=\/dev\/null/d' ~/.ssh/config
|
sed -i '/UserKnownHostsFile=\/dev\/null/d' ~/.ssh/config
|
||||||
|
|
||||||
@@ -124,11 +158,12 @@ function setup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function make_template {
|
function make_template {
|
||||||
gcloud compute instances create $TEMPLATE_NAME --image $IMAGE --zone $ZONE
|
gcloud compute instances create $TEMPLATE_NAME --image-family=$IMAGE_FAMILY --image-project=$IMAGE_PROJECT --zone $ZONE
|
||||||
gcloud compute config-ssh --ssh-key-file $SSH_KEY_FILE
|
gcloud compute config-ssh --ssh-key-file $SSH_KEY_FILE
|
||||||
name="$TEMPLATE_NAME.$ZONE.$PROJECT"
|
name="$TEMPLATE_NAME.$ZONE.$PROJECT"
|
||||||
try_connect $name
|
try_connect $name
|
||||||
install_docker_on $name
|
install_docker_on $name
|
||||||
|
install_kubernetes_on $name
|
||||||
gcloud -q compute instances delete $TEMPLATE_NAME --keep-disks boot --zone $ZONE
|
gcloud -q compute instances delete $TEMPLATE_NAME --keep-disks boot --zone $ZONE
|
||||||
gcloud compute images create $TEMPLATE_NAME --source-disk $TEMPLATE_NAME --source-disk-zone $ZONE
|
gcloud compute images create $TEMPLATE_NAME --source-disk $TEMPLATE_NAME --source-disk-zone $ZONE
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -1,4 +1,9 @@
|
|||||||
$go_version = "1.4.2"
|
$go_regexp = /FROM golang:(\S*).*?/
|
||||||
|
$dockerfile_path = File.expand_path(File.join(File.dirname(__FILE__), 'build', 'Dockerfile'))
|
||||||
|
$go_version = File.readlines($dockerfile_path).first { |line| line.match($go_regexp) }.match($go_regexp).captures.first
|
||||||
|
if $go_version.nil?
|
||||||
|
raise ArgumentError.new("Failed to read Go version from Dockerfile.")
|
||||||
|
end
|
||||||
|
|
||||||
$go_path = "/usr/local/go/bin"
|
$go_path = "/usr/local/go/bin"
|
||||||
|
|
||||||
|
|||||||
+1
Submodule vendor/github.com/beorn7/perks added at 4c0e84591b.
+1
Submodule vendor/github.com/golang/protobuf added at df1d3ca07d.
+1
Submodule vendor/github.com/matttproud/golang_protobuf_extensions added at 3247c84500.
+1
Submodule vendor/github.com/pkg/errors added at 248dadf4e9.
+1
Submodule vendor/github.com/prometheus/client_golang added at c5b7fccd20.
+1
Submodule vendor/github.com/prometheus/client_model added at fa8ad6fec3.
+1
Submodule vendor/github.com/prometheus/common added at 85637ea67b.
+1
Submodule vendor/github.com/prometheus/procfs added at abf152e5f3.
+1
Submodule vendor/github.com/spf13/cobra added at 6e91dded25.
+1
Submodule vendor/github.com/spf13/pflag added at 5ccb023bc2.
+1
Submodule vendor/github.com/spf13/plag added at 5ccb023bc2.
+1
-1
Submodule vendor/github.com/vishvananda/netlink updated: a632d6dc28...266f02d3a8.
+1
-1
Submodule vendor/github.com/weaveworks/mesh updated: 3e1ae95e23...3dd75b104a.
+1
Submodule vendor/k8s.io/client-go added at b22087a53b.
@@ -3,9 +3,9 @@ set -e
|
|||||||
|
|
||||||
[ -n "$WEAVE_DEBUG" ] && set -x
|
[ -n "$WEAVE_DEBUG" ] && set -x
|
||||||
|
|
||||||
SCRIPT_VERSION="(unreleased version)"
|
SCRIPT_VERSION="unreleased"
|
||||||
IMAGE_VERSION=latest
|
IMAGE_VERSION=latest
|
||||||
[ "$SCRIPT_VERSION" = "(unreleased version)" ] || IMAGE_VERSION=$SCRIPT_VERSION
|
[ "$SCRIPT_VERSION" = "unreleased" ] || IMAGE_VERSION=$SCRIPT_VERSION
|
||||||
IMAGE_VERSION=${WEAVE_VERSION:-$IMAGE_VERSION}
|
IMAGE_VERSION=${WEAVE_VERSION:-$IMAGE_VERSION}
|
||||||
|
|
||||||
# - Docker versions prior to 1.6 cannot pull images from DockerHub
|
# - Docker versions prior to 1.6 cannot pull images from DockerHub
|
||||||
@@ -157,6 +157,7 @@ exec_remote() {
|
|||||||
-e WEAVE_PASSWORD \
|
-e WEAVE_PASSWORD \
|
||||||
-e WEAVE_PORT \
|
-e WEAVE_PORT \
|
||||||
-e WEAVE_HTTP_ADDR \
|
-e WEAVE_HTTP_ADDR \
|
||||||
|
-e WEAVE_STATUS_ADDR \
|
||||||
-e WEAVE_CONTAINER_NAME \
|
-e WEAVE_CONTAINER_NAME \
|
||||||
-e WEAVE_MTU \
|
-e WEAVE_MTU \
|
||||||
-e WEAVE_NO_FASTDP \
|
-e WEAVE_NO_FASTDP \
|
||||||
@@ -351,6 +352,7 @@ DATAPATH_IFNAME=v${CONTAINER_IFNAME}-datapath
|
|||||||
PCAP_IFNAME=v${CONTAINER_IFNAME}-pcap
|
PCAP_IFNAME=v${CONTAINER_IFNAME}-pcap
|
||||||
PORT=${WEAVE_PORT:-6783}
|
PORT=${WEAVE_PORT:-6783}
|
||||||
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
||||||
|
STATUS_ADDR=${WEAVE_STATUS_ADDR:-127.0.0.1:6782}
|
||||||
PROXY_PORT=12375
|
PROXY_PORT=12375
|
||||||
PROXY_CONTAINER_NAME=weaveproxy
|
PROXY_CONTAINER_NAME=weaveproxy
|
||||||
COVERAGE_ARGS=""
|
COVERAGE_ARGS=""
|
||||||
@@ -396,7 +398,13 @@ add_iptables_rule() {
|
|||||||
IPTABLES_TABLE="$1"
|
IPTABLES_TABLE="$1"
|
||||||
shift 1
|
shift 1
|
||||||
if ! run_iptables -t $IPTABLES_TABLE -C "$@" >/dev/null 2>&1 ; then
|
if ! run_iptables -t $IPTABLES_TABLE -C "$@" >/dev/null 2>&1 ; then
|
||||||
run_iptables -t $IPTABLES_TABLE -A "$@" >/dev/null
|
## Loop until we get an exit code other than "temporarily unavailable"
|
||||||
|
while true ; do
|
||||||
|
run_iptables -t $IPTABLES_TABLE -A "$@" >/dev/null && return 0
|
||||||
|
if [ $? != 4 ] ; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,7 +413,13 @@ insert_iptables_rule() {
|
|||||||
IPTABLES_TABLE="$1"
|
IPTABLES_TABLE="$1"
|
||||||
shift 1
|
shift 1
|
||||||
if ! run_iptables -t $IPTABLES_TABLE -C "$@" >/dev/null 2>&1 ; then
|
if ! run_iptables -t $IPTABLES_TABLE -C "$@" >/dev/null 2>&1 ; then
|
||||||
run_iptables -t $IPTABLES_TABLE -I "$@" >/dev/null
|
## Loop until we get an exit code other than "temporarily unavailable"
|
||||||
|
while true ; do
|
||||||
|
run_iptables -t $IPTABLES_TABLE -I "$@" >/dev/null && return 0
|
||||||
|
if [ $? != 4 ] ; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,7 +571,7 @@ detect_bridge_type() {
|
|||||||
MTU=${WEAVE_MTU:-$(cat /sys/class/net/$BRIDGE/mtu)}
|
MTU=${WEAVE_MTU:-$(cat /sys/class/net/$BRIDGE/mtu)}
|
||||||
}
|
}
|
||||||
|
|
||||||
create_bridge() {
|
try_create_bridge() {
|
||||||
if ! detect_bridge_type ; then
|
if ! detect_bridge_type ; then
|
||||||
BRIDGE_TYPE=bridge
|
BRIDGE_TYPE=bridge
|
||||||
if [ -z "$WEAVE_NO_FASTDP" ] ; then
|
if [ -z "$WEAVE_NO_FASTDP" ] ; then
|
||||||
@@ -601,7 +615,7 @@ create_bridge() {
|
|||||||
# Steer traffic via the NPC
|
# Steer traffic via the NPC
|
||||||
run_iptables -N WEAVE-NPC >/dev/null 2>&1 || true
|
run_iptables -N WEAVE-NPC >/dev/null 2>&1 || true
|
||||||
add_iptables_rule filter FORWARD -o $BRIDGE -j WEAVE-NPC
|
add_iptables_rule filter FORWARD -o $BRIDGE -j WEAVE-NPC
|
||||||
add_iptables_rule filter FORWARD -o $BRIDGE -j LOG --log-prefix=WEAVE-NPC:
|
add_iptables_rule filter FORWARD -o $BRIDGE -m state --state NEW -j NFLOG --nflog-group 86
|
||||||
add_iptables_rule filter FORWARD -o $BRIDGE -j DROP
|
add_iptables_rule filter FORWARD -o $BRIDGE -j DROP
|
||||||
else
|
else
|
||||||
# Work around the situation where there are no rules allowing traffic
|
# Work around the situation where there are no rules allowing traffic
|
||||||
@@ -642,6 +656,16 @@ EOF
|
|||||||
configure_arp_cache $BRIDGE
|
configure_arp_cache $BRIDGE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create_bridge() {
|
||||||
|
if ! try_create_bridge "$@" ; then
|
||||||
|
echo "Creating bridge '$BRIDGE' failed" >&2
|
||||||
|
# reset to original value so we destroy both kinds
|
||||||
|
DATAPATH=datapath
|
||||||
|
destroy_bridge
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
@@ -659,14 +683,6 @@ expose_ip() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
expose_nat() {
|
|
||||||
for CIDR in $ALL_CIDRS ; do
|
|
||||||
add_iptables_rule nat WEAVE -s $CIDR -d 224.0.0.0/4 -j RETURN
|
|
||||||
add_iptables_rule nat WEAVE -d $CIDR ! -s $CIDR -j MASQUERADE
|
|
||||||
add_iptables_rule nat WEAVE -s $CIDR ! -d $CIDR -j MASQUERADE
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# create veth with ends $1-$2, and then invoke $3..., removing the
|
# create veth with ends $1-$2, and then invoke $3..., removing the
|
||||||
# veth on failure. No-op of veth already exists.
|
# veth on failure. No-op of veth already exists.
|
||||||
create_veth() {
|
create_veth() {
|
||||||
@@ -795,6 +811,11 @@ destroy_bridge() {
|
|||||||
run_iptables -t filter -D INPUT -i $DOCKER_BRIDGE -p udp --dst $DOCKER_BRIDGE_IP --dport $(($PORT + 1)) -j DROP >/dev/null 2>&1 || true
|
run_iptables -t filter -D INPUT -i $DOCKER_BRIDGE -p udp --dst $DOCKER_BRIDGE_IP --dport $(($PORT + 1)) -j DROP >/dev/null 2>&1 || true
|
||||||
|
|
||||||
run_iptables -t filter -D FORWARD -i $BRIDGE -o $BRIDGE -j ACCEPT 2>/dev/null || true
|
run_iptables -t filter -D FORWARD -i $BRIDGE -o $BRIDGE -j ACCEPT 2>/dev/null || true
|
||||||
|
run_iptables -F WEAVE-NPC >/dev/null 2>&1 || true
|
||||||
|
run_iptables -t filter -D FORWARD -o $BRIDGE -j WEAVE-NPC 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 -X WEAVE-NPC >/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
|
||||||
@@ -1021,6 +1042,11 @@ peer_args() {
|
|||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
create_cni_script() {
|
create_cni_script() {
|
||||||
|
# If target file is a symlink, it's probably a remnant of a weave-kube
|
||||||
|
# installation - if the user has now switched back to `weave launch`,
|
||||||
|
# we need to remove the link before rewriting the contents:
|
||||||
|
[ -L "$1" ] && rm "$1" # fail if we can't remove the link
|
||||||
|
|
||||||
cat >"$1" <<EOF
|
cat >"$1" <<EOF
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
[ -z "\$CNI_NETNS" -o \$(echo "\$CNI_NETNS" | grep "^/proc/") ] || \\
|
[ -z "\$CNI_NETNS" -o \$(echo "\$CNI_NETNS" | grep "^/proc/") ] || \\
|
||||||
@@ -1029,7 +1055,7 @@ docker run --rm $(docker_run_options) --pid=host -i \\
|
|||||||
-e CNI_VERSION -e CNI_COMMAND -e CNI_CONTAINERID -e CNI_NETNS \\
|
-e CNI_VERSION -e CNI_COMMAND -e CNI_CONTAINERID -e CNI_NETNS \\
|
||||||
-e CNI_IFNAME -e CNI_ARGS -e CNI_PATH \\
|
-e CNI_IFNAME -e CNI_ARGS -e CNI_PATH \\
|
||||||
-v /etc/cni:/etc/cni -v /opt/cni:/opt/cni \$EXTRA_VOLUMES \\
|
-v /etc/cni:/etc/cni -v /opt/cni:/opt/cni \$EXTRA_VOLUMES \\
|
||||||
$PLUGIN_IMAGE $COVERAGE_ARGS $2
|
--entrypoint=/usr/bin/weaveutil $EXEC_IMAGE $2
|
||||||
EOF
|
EOF
|
||||||
chmod a+x "$1"
|
chmod a+x "$1"
|
||||||
}
|
}
|
||||||
@@ -1045,8 +1071,8 @@ EOF
|
|||||||
|
|
||||||
setup_cni() {
|
setup_cni() {
|
||||||
if [ -d $HOST_ROOT/opt/cni/bin ] ; then
|
if [ -d $HOST_ROOT/opt/cni/bin ] ; then
|
||||||
create_cni_script $HOST_ROOT/opt/cni/bin/weave-net --cni-net
|
create_cni_script $HOST_ROOT/opt/cni/bin/weave-net cni-net
|
||||||
create_cni_script $HOST_ROOT/opt/cni/bin/weave-ipam --cni-ipam
|
create_cni_script $HOST_ROOT/opt/cni/bin/weave-ipam cni-ipam
|
||||||
fi
|
fi
|
||||||
if [ -d $HOST_ROOT/etc/cni/net.d -a ! -f $HOST_ROOT/etc/cni/net.d/10-weave.conf ] ; then
|
if [ -d $HOST_ROOT/etc/cni/net.d -a ! -f $HOST_ROOT/etc/cni/net.d/10-weave.conf ] ; then
|
||||||
create_cni_config $HOST_ROOT/etc/cni/net.d/10-weave.conf
|
create_cni_config $HOST_ROOT/etc/cni/net.d/10-weave.conf
|
||||||
@@ -1663,6 +1689,7 @@ launch_router() {
|
|||||||
$DNS_ROUTER_OPTS $NO_DNS_OPT \
|
$DNS_ROUTER_OPTS $NO_DNS_OPT \
|
||||||
$AWSVPC_ARGS \
|
$AWSVPC_ARGS \
|
||||||
--http-addr $HTTP_ADDR \
|
--http-addr $HTTP_ADDR \
|
||||||
|
--status-addr $STATUS_ADDR \
|
||||||
--resolv-conf "/var/run/weave/etc/$RESOLV_CONF_BASE" \
|
--resolv-conf "/var/run/weave/etc/$RESOLV_CONF_BASE" \
|
||||||
"$@")
|
"$@")
|
||||||
wait_for_status $CONTAINER_NAME http_call $HTTP_ADDR
|
wait_for_status $CONTAINER_NAME http_call $HTTP_ADDR
|
||||||
@@ -2209,7 +2236,7 @@ EOF
|
|||||||
fi
|
fi
|
||||||
create_bridge --without-ethtool
|
create_bridge --without-ethtool
|
||||||
expose_ip
|
expose_ip
|
||||||
expose_nat
|
util_op expose-nat $ALL_CIDRS
|
||||||
show_addrs $ALL_CIDRS
|
show_addrs $ALL_CIDRS
|
||||||
;;
|
;;
|
||||||
hide)
|
hide)
|
||||||
|
|||||||
Reference in new issue
Block a user