Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
ebfaf11f4a | ||
|
|
4a6cec3ecc | ||
|
|
1abce148a3 | ||
|
|
e0cc7f17b2 | ||
|
|
264e5c9979 | ||
|
|
a6a1ce5c9e | ||
|
|
6ac7ab162f | ||
|
|
5e660311d2 | ||
|
|
4a49ff3083 | ||
|
|
7f92c8c4c6 | ||
|
|
85ac8177ad | ||
|
|
03bd1fb91d | ||
|
|
eb5b16614c | ||
|
|
a5873a3970 | ||
|
|
e48ca80dee | ||
|
|
e7c9e22350 | ||
|
|
b8e7d83f19 | ||
|
|
4c304c0013 | ||
|
|
7249350757 | ||
|
|
50a8dc1383 | ||
|
|
71491138aa | ||
|
|
ca87f8ada3 | ||
|
|
835b3f3d67 | ||
|
|
263dffeedc | ||
|
|
e1063944f6 | ||
|
|
2380018457 | ||
|
|
a9db631836 | ||
|
|
b45f138ff1 | ||
|
|
b239694d34 | ||
|
|
0c2774f367 | ||
|
|
ea4647bc74 | ||
|
|
6ec21d3a58 | ||
|
|
44a4a56024 | ||
|
|
0aac614c08 | ||
|
|
2ea027d3a8 | ||
|
|
cd4cbed5a0 | ||
|
|
69b688b357 | ||
|
|
7a81bc2ddc | ||
|
|
cdeae04238 | ||
|
|
6df2782d73 | ||
|
|
3fc7efa536 | ||
|
|
a556deec78 | ||
|
|
71e43c2023 | ||
|
|
370f258c3a | ||
|
|
9b32bc2ab3 | ||
|
|
fd5e1970e1 | ||
|
|
a1de7f3eef | ||
|
|
06a63db4cf | ||
|
|
c720ebec02 | ||
|
|
6a528409ab | ||
|
|
c69e140e9e | ||
|
|
8ed0c13645 | ||
|
|
b635f9a994 | ||
|
|
ac4246e1f5 | ||
|
|
06a325a05f | ||
|
|
68e9bbe350 | ||
|
|
3bd9e369cd | ||
|
|
3d12e8088e | ||
|
|
aee35b34fe | ||
|
|
c06ccb3b0d | ||
|
|
268bb656fc | ||
|
|
57357a728c | ||
|
|
a335c419ec | ||
|
|
e263295720 | ||
|
|
0c854f2144 | ||
|
|
bdf4debdfd | ||
|
|
f557835ad0 | ||
|
|
17a7391086 | ||
|
|
e65293eb50 | ||
|
|
48c9547943 | ||
|
|
caa6c6c931 | ||
|
|
11f56031f1 | ||
|
|
2e825242cd | ||
|
|
ebe4c5b8b7 | ||
|
|
027064cbdb | ||
|
|
9fddb827f9 | ||
|
|
3162009325 | ||
|
|
060f6392c7 | ||
|
|
8c8c6db7dd | ||
|
|
cd94dd0088 | ||
|
|
afc6f49e80 | ||
|
|
c509b6e5bb | ||
|
|
a466f55d59 | ||
|
|
c79b6a8bd7 | ||
|
|
c2083cbce0 | ||
|
|
4157d9298d | ||
|
|
f686408808 | ||
|
|
432e10e19f | ||
|
|
b8c57ef10f | ||
|
|
601bda3176 | ||
|
|
d9bde790c6 | ||
|
|
5cc0826ca0 | ||
|
|
96f2eb3ecf | ||
|
|
e703f2f3f2 | ||
|
|
1c0ffbc177 | ||
|
|
f544da9e19 | ||
|
|
c0592ba1b3 | ||
|
|
0cbf5ca130 | ||
|
|
472eab2acd | ||
|
|
7a5d7fca6c | ||
|
|
081b95dfd0 | ||
|
|
faf5b14d8b | ||
|
|
38958049f4 | ||
|
|
589b45dace | ||
|
|
7ebf3d7a88 | ||
|
|
3273d13666 | ||
|
|
5fdbbf3722 | ||
|
|
14350ff463 | ||
|
|
0b49c961a7 | ||
|
|
39b80f26ed | ||
|
|
74b20265e4 | ||
|
|
358fddb958 | ||
|
|
5c8f6175b5 | ||
|
|
e8d823d66b | ||
|
|
83ce5875ee | ||
|
|
ed83efbceb | ||
|
|
223116c4bf | ||
|
|
8056a098a8 | ||
|
|
15a2cba7f8 | ||
|
|
b37bb02a8b | ||
|
|
0c14e5135a | ||
|
|
f5a30c790e | ||
|
|
b06ddeb962 | ||
|
|
34415c3c17 | ||
|
|
6387fa8bc9 | ||
|
|
f6e6926171 | ||
|
|
25e7868cd5 | ||
|
|
385e6ff9b8 | ||
|
|
3755906689 | ||
|
|
63cbb34dec | ||
|
|
8ec6b699b1 | ||
|
|
369852a3a7 | ||
|
|
c82be57639 | ||
|
|
092c3a17d1 | ||
|
|
fd0a7efa23 | ||
|
|
551f164ec3 | ||
|
|
acdd739deb | ||
|
|
45e3016c6a | ||
|
|
88bf64722f | ||
|
|
c1483dbf1f | ||
|
|
d9c998b5c4 | ||
|
|
170e19c283 | ||
|
|
bce07b7491 | ||
|
|
8b5741506d | ||
|
|
7c47a8ffb7 | ||
|
|
791ab55875 | ||
|
|
e642f366f5 | ||
|
|
bd1a9d020c | ||
|
|
d794b203f7 | ||
|
|
7ec68119a6 | ||
|
|
71fd3b6b2b | ||
|
|
93c60395f6 | ||
|
|
af27aa04f2 | ||
|
|
0861d05522 | ||
|
|
89221f117b | ||
|
|
7f5c37ef1e | ||
|
|
05baa566c6 | ||
|
|
f8ac27a6cc | ||
|
|
e5c23bf9f5 | ||
|
|
954cd0b86a | ||
|
|
0635ed1854 | ||
|
|
9dedeadf15 | ||
|
|
4424e877f0 | ||
|
|
6561272f8d | ||
|
|
aa54d332c2 | ||
|
|
881e795db5 | ||
|
|
4216231ec7 | ||
|
|
e5aeb4bd05 | ||
|
|
333d5f7511 | ||
|
|
7be7945b29 | ||
|
|
566ed64607 | ||
|
|
83c358c8d2 | ||
|
|
afe882cd94 | ||
|
|
5b8a359475 | ||
|
|
ae95248499 | ||
|
|
df0af203f8 | ||
|
|
53b8c804f2 | ||
|
|
7d0aaea6e8 | ||
|
|
45a66ac517 | ||
|
|
087d948941 | ||
|
|
5b2a4ff614 | ||
|
|
72e92db96e | ||
|
|
768414534f | ||
|
|
ceb5f8c714 | ||
|
|
cc3359dfe5 | ||
|
|
f1ca4854b8 | ||
|
|
6319860230 | ||
|
|
58caf38f07 | ||
|
|
cb4b5fd143 | ||
|
|
cda6785f1d | ||
|
|
cb030be4ad | ||
|
|
7165414fc3 | ||
|
|
b464ce86f7 | ||
|
|
d72d3af36e | ||
|
|
80b3514e24 | ||
|
|
f07ed7153b | ||
|
|
e33f56f9b0 | ||
|
|
6c46af25cc | ||
|
|
f386548890 | ||
|
|
c80d87554a |
No files matched your search
@@ -43,6 +43,10 @@ prog/weavewait/weavewait
|
||||
prog/weavewait/weavewait_noop
|
||||
prog/weavewait/weavewait_nomcast
|
||||
prog/weaveutil/weaveutil
|
||||
prog/*/Dockerfile.weaveworks
|
||||
prog/kube-peers/kube-peers
|
||||
prog/weave-kube/kube-peers
|
||||
prog/weave-npc/weave-npc
|
||||
prog/plugin/plugin
|
||||
testing/cover/cover
|
||||
testing/runner/runner
|
||||
|
||||
+42
@@ -76,3 +76,45 @@
|
||||
[submodule "vendor/github.com/aws/aws-sdk-go"]
|
||||
path = vendor/github.com/aws/aws-sdk-go
|
||||
url = https://github.com/aws/aws-sdk-go
|
||||
[submodule "vendor/golang.org/x/sys"]
|
||||
path = vendor/golang.org/x/sys
|
||||
url = https://go.googlesource.com/sys
|
||||
[submodule "vendor/github.com/weaveworks/docker"]
|
||||
path = vendor/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
|
||||
+76
-1
@@ -1,3 +1,78 @@
|
||||
## 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
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Fixed an error where the Docker plugin could fail to attach a
|
||||
container with a `bridge "weave" not present` error #2540/#2541
|
||||
* Fixed `cannot connect to itself` panic on weave launch #2527/#2543
|
||||
* Fixed inferred initial peer count when target peers includes self
|
||||
#2481/#2543
|
||||
* Fixed compilation on Raspberry Pi #2506/#2538
|
||||
|
||||
## Release 1.7.1
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Added utility to recover container addresses without asking Docker #2531
|
||||
* Improved CI #2274
|
||||
|
||||
## Release 1.7.0
|
||||
|
||||
Highlights:
|
||||
|
||||
* weave-kube - Deploy Weave Net to Kubernetes with a single command
|
||||
* weave-npc - Kubernetes network policy controller implementation
|
||||
|
||||
More details in the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.7.0).
|
||||
|
||||
## Release 1.6.2
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Fixed hang after stopping and restarting on Docker 1.12 #2469/#2502
|
||||
* Avoid an error on Google container images by checking for tx offload support #2504
|
||||
* Fixed an issue where the supplied peer list could be ignored when restarting after failure #2503/#2509
|
||||
* Check for empty peer name on launch #2495/#2501
|
||||
|
||||
More details in the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.6.2).
|
||||
|
||||
## Release 1.6.1
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* `weave ps` was occasionally failing to list allocated addresses of containers #2388/#2418/#2445
|
||||
* `weave launch[-router]` on 4.2 kernel would appear to succeed even if the fast datapath VXLAN UDP port was in use by a different process #2375/#2474
|
||||
* Launching the proxy would fail when the Docker daemon could not be detected #2457/#2424
|
||||
* The CNI plugin did not work with Apache Mesos #2394/#2442
|
||||
* Router stopped working after a restart in the AWSVPC mode #2381/#2409
|
||||
* Router crashed when the Docker API endpoint parameter was explicitly set to empty #2421/#2467
|
||||
* The CNI plugin did not work on recent versions of Docker for Mac #2434/#2442
|
||||
* The CNI plugin assigns an IP to the bridge if necessary, which avoids failures if `weave expose` has not run yet #2471
|
||||
* Distinguish peer name collisions from attempts to connect to self in logs #2460
|
||||
* Improve host clock skew detection message #2174
|
||||
* Improve the error message returned when executing `weave launch-plugin` without the router running #2293/#2416
|
||||
* The `create-bridge` subcommand was not enabled in the fast datapath mode #2464/2466
|
||||
* Allow users to omit `weave setup[-cni]` by initializing the CNI plugin on the `launch[-router]` subcommand #2435/#2442
|
||||
* Reduce verbosity of fast datapath miss event logs #1852/#2417
|
||||
* Include the `ipam` option in the help output of the `status` subcommand #2425/#2426
|
||||
* Remove a harmless duplication of the `--no-dns` parameter #2430
|
||||
* Internal refactoring
|
||||
* Improvements to testing and building
|
||||
* Improvements to the documentation
|
||||
|
||||
More details in the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.6.1).
|
||||
|
||||
## Release 1.6.0
|
||||
|
||||
## Highlights
|
||||
@@ -8,7 +83,7 @@
|
||||
overlays
|
||||
* Docker 1.12 introduced some internal changes that made it
|
||||
incompatible with previous version of Weave Net - this version
|
||||
restores compatability
|
||||
restores compatibility
|
||||
* An [operational guide](https://weave.works/docs/net/latest/operational-guide)
|
||||
detailing best practices for deploying and operating Weave Net
|
||||
* Changes to the target peer list are remembered across restarts,
|
||||
|
||||
@@ -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
|
||||
.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
|
||||
WEAVEPROXY_EXE=prog/weaveproxy/weaveproxy
|
||||
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_NOOP_EXE=prog/weavewait/weavewait_noop
|
||||
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
|
||||
@@ -24,23 +26,27 @@ PLUGIN_EXE=prog/plugin/plugin
|
||||
RUNNER_EXE=tools/runner/runner
|
||||
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
|
||||
WEAVER_UPTODATE=.weaver.uptodate
|
||||
WEAVEEXEC_UPTODATE=.weaveexec.uptodate
|
||||
PLUGIN_UPTODATE=.plugin.uptodate
|
||||
WEAVEKUBE_UPTODATE=.weavekube.uptodate
|
||||
WEAVENPC_UPTODATE=.weavenpc.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
|
||||
WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec
|
||||
PLUGIN_IMAGE=$(DOCKERHUB_USER)/plugin
|
||||
WEAVEKUBE_IMAGE=$(DOCKERHUB_USER)/weave-kube
|
||||
WEAVENPC_IMAGE=$(DOCKERHUB_USER)/weave-npc
|
||||
BUILD_IMAGE=$(DOCKERHUB_USER)/weavebuild
|
||||
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
|
||||
|
||||
@@ -55,7 +61,7 @@ NETGO_CHECK=@strings $@ | grep cgo_stub\\\.go >/dev/null || { \
|
||||
echo " sudo go install -tags netgo std"; \
|
||||
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 ./)
|
||||
|
||||
@@ -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
|
||||
$(WEAVEUTIL_EXE): prog/weaveutil/*.go net/*.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
|
||||
$(TEST_TLS_EXE): test/tls/*.go
|
||||
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
|
||||
@@ -99,7 +107,7 @@ else
|
||||
endif
|
||||
$(NETGO_CHECK)
|
||||
|
||||
$(WEAVEUTIL_EXE):
|
||||
$(WEAVEUTIL_EXE) $(KUBEPEERS_EXE) $(WEAVENPC_EXE):
|
||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||
$(NETGO_CHECK)
|
||||
|
||||
@@ -128,8 +136,11 @@ $(BUILD_UPTODATE): build/*
|
||||
$(SUDO) docker build -t $(BUILD_IMAGE) build/
|
||||
touch $@
|
||||
|
||||
$(WEAVER_UPTODATE): prog/weaver/Dockerfile $(WEAVER_EXE)
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -t $(WEAVER_IMAGE) prog/weaver
|
||||
%/Dockerfile.$(DOCKERHUB_USER): %/Dockerfile.template
|
||||
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 $@
|
||||
|
||||
$(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
|
||||
touch $@
|
||||
|
||||
$(PLUGIN_UPTODATE): prog/plugin/Dockerfile $(PLUGIN_EXE) $(WEAVEEXEC_UPTODATE)
|
||||
$(SUDO) docker build -t $(PLUGIN_IMAGE) prog/plugin
|
||||
$(PLUGIN_UPTODATE): prog/plugin/Dockerfile.$(DOCKERHUB_USER) $(PLUGIN_EXE) $(WEAVER_UPTODATE)
|
||||
$(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 $@
|
||||
|
||||
$(WEAVEDB_UPTODATE): prog/weavedb/Dockerfile
|
||||
@@ -162,13 +182,13 @@ tools/.git:
|
||||
git submodule update --init
|
||||
|
||||
$(RUNNER_EXE): tools/.git
|
||||
make -C tools/runner
|
||||
GO15VENDOREXPERIMENT=1 make -C tools/runner
|
||||
|
||||
$(PUBLISH): publish_%: $(IMAGES_UPTODATE)
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker tag -f $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker push $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker tag $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker push $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||
ifneq ($(UPDATE_LATEST),false)
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker push $(DOCKERHUB_USER)/$*:latest
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker push $(DOCKERHUB_USER)/$*:latest
|
||||
endif
|
||||
|
||||
publish: $(PUBLISH)
|
||||
|
||||
Vendored
+4
@@ -22,6 +22,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
|
||||
config.vm.network "private_network", ip: vm_ip
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
vb.memory = 2048
|
||||
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "off"]
|
||||
vb.customize ["modifyvm", :id, "--natdnsproxy1", "off"]
|
||||
end
|
||||
@@ -29,6 +30,9 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.vm.synced_folder ".", "/vagrant", disabled: true
|
||||
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_go_toochain config.vm
|
||||
tweak_user_env config.vm
|
||||
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
echo "TEST_AND_PUBLISH=1" > "$STATE"
|
||||
|
||||
if [ -n "$CI_PULL_REQUEST" -a "$CI_PULL_REQUEST" = "$CI_PULL_REQUESTS" ] ; then
|
||||
if PR=$(echo "$CI_PULL_REQUEST" | grep -oP '(?<=^https://github.com/weaveworks/weave/pull/)[0-9]+$') ; then
|
||||
echo "GH_PR=$PR" >> "$STATE"
|
||||
if FILES=$(curl -s https://api.github.com/repos/weaveworks/weave/pulls/$PR/files | jq -r '.[]|.filename') ; then
|
||||
echo "PR touches [$FILES]"
|
||||
if ! echo "$FILES" | grep -v ^site/ ; then
|
||||
echo "PR contains site changes only - skipping tests"
|
||||
echo "TEST_AND_PUBLISH=" >> "$STATE"
|
||||
else
|
||||
echo "PR contains non-site changes - running tests"
|
||||
fi
|
||||
else
|
||||
echo "Unable to get files for PR $PR" >&2
|
||||
fi
|
||||
else
|
||||
echo "Unable to extract PR number from $CI_PULL_REQUEST" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
source "$STATE"
|
||||
|
||||
mkdir -p $(dirname $SRCDIR) && cp -r $(pwd)/ $SRCDIR
|
||||
|
||||
if [ -n "$TEST_AND_PUBLISH" ] ; then
|
||||
bin/setup-circleci-secrets "$SECRET_PASSWORD"
|
||||
cd $SRCDIR; git submodule update --init
|
||||
if [ "$CIRCLE_NODE_INDEX" = "0" ] ; then
|
||||
cd $SRCDIR/test; ./gce.sh make_template
|
||||
fi
|
||||
cd $SRCDIR/build
|
||||
../tools/rebuild-image weaveworks/weavebuild . Dockerfile build.sh
|
||||
touch $SRCDIR/.build.uptodate
|
||||
sudo chown -R ubuntu /usr/local/go
|
||||
fi
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
source "$STATE"
|
||||
|
||||
site_changes() {
|
||||
curl -s https://api.github.com/repos/weaveworks/weave/pulls/$1/files |
|
||||
jq -r '.[]|.status + " " + .filename' |
|
||||
grep -oP '^(added|modified|removed) site/.*(?=\.md$)'
|
||||
}
|
||||
|
||||
format_comment() {
|
||||
if [ ! -d .git ] || ! BRANCH=$(git symbolic-ref --short HEAD) || [ -z "$BRANCH" ] ; then
|
||||
echo "Could not determine branch" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "#### Documentation changes:"
|
||||
while read ACTION PAGE; do
|
||||
echo "$ACTION [${PAGE#site/}]($WP_DEV_URL/docs/net/${BRANCH#issues/}/${PAGE#site/}/)"
|
||||
done | sort
|
||||
}
|
||||
|
||||
post_comment() {
|
||||
jq -s -R '. | tostring | { body: . }' |
|
||||
curl -s -d @- \
|
||||
-H "Authorization: Bearer $WP_GH_COMMENT_TOKEN" \
|
||||
-H 'Content-Type: application/json' \
|
||||
https://api.github.com/repos/weaveworks/weave/issues/$GH_PR/comments > /dev/null
|
||||
}
|
||||
|
||||
cd $SRCDIR
|
||||
bin/install-wordepress v1.0.0
|
||||
bin/publish-site "$WP_DEV_URL" "$WP_DEV_USER" "$WP_DEV_PASSWORD"
|
||||
|
||||
if [ -n "$WP_GH_COMMENT_TOKEN" -a -n "$GH_PR" ] ; then
|
||||
echo ">>> Adding doc changelog to GitHub PR $GH_PR"
|
||||
if CHANGES=$(site_changes $GH_PR) && [ -n "$CHANGES" ] ; then
|
||||
echo "$CHANGES" | format_comment | post_comment
|
||||
fi
|
||||
fi
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source "$STATE"
|
||||
|
||||
if [ -n "$TEST_AND_PUBLISH" ] ; then
|
||||
docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
|
||||
cd $SRCDIR
|
||||
make clean-bin
|
||||
make publish
|
||||
fi
|
||||
|
||||
cd $SRCDIR
|
||||
bin/install-wordepress v1.0.0
|
||||
bin/publish-site "$WP_DEV_URL" "$WP_DEV_USER" "$WP_DEV_PASSWORD"
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd $SRCDIR
|
||||
bin/install-wordepress v1.0.0
|
||||
bin/publish-site "$WP_LIVE_URL" "$WP_LIVE_USER" "$WP_LIVE_PASSWORD"
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source "$STATE"
|
||||
|
||||
if [ -n "$TEST_AND_PUBLISH" ] ; then
|
||||
if [ "$CIRCLE_NODE_INDEX" = "0" ] ; then
|
||||
go get github.com/mattn/goveralls
|
||||
goveralls -repotoken $COVERALLS_REPO_TOKEN -coverprofile=$SRCDIR/test/profile.cov -service=circleci
|
||||
cd $SRCDIR/test; cp coverage.* $CIRCLE_ARTIFACTS
|
||||
fi
|
||||
fi
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source "$STATE"
|
||||
|
||||
if [ -n "$TEST_AND_PUBLISH" ] ; then
|
||||
cd $SRCDIR
|
||||
make testrunner
|
||||
make COVERAGE=true RM= all
|
||||
|
||||
cd $SRCDIR/test
|
||||
./gce.sh setup && eval $(./gce.sh hosts) && ./setup.sh
|
||||
fi
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source "$STATE"
|
||||
|
||||
if [ -n "$TEST_AND_PUBLISH" ] ; then
|
||||
cd $SRCDIR/test
|
||||
eval $(./gce.sh hosts)
|
||||
export COVERAGE=true
|
||||
./run_all.sh
|
||||
fi
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source "$STATE"
|
||||
|
||||
if [ -n "$TEST_AND_PUBLISH" ] ; then
|
||||
cd $SRCDIR/test
|
||||
./gce.sh destroy
|
||||
test "$CIRCLE_NODE_INDEX" != "0" || (cd $SRCDIR/test; ./gen_coverage_reports.sh)
|
||||
fi
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source "$STATE"
|
||||
|
||||
if [ -n "$TEST_AND_PUBLISH" ] ; then
|
||||
cd $SRCDIR
|
||||
make lint
|
||||
COVERDIR=test/coverage make RM= tests
|
||||
fi
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ $# -ne 1 ] ; then
|
||||
echo USAGE: install-wordepress [COMMIT-ISH] >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
go get github.com/weaveworks/wordepress
|
||||
cd /home/ubuntu/src/github.com/weaveworks/wordepress
|
||||
git checkout $1
|
||||
cd cmd/wordepress
|
||||
go get
|
||||
+1
-1
@@ -29,7 +29,7 @@ case "$BRANCH" in
|
||||
if ! VERSION=$(echo "$DESCRIBE" | grep -oP '(?<=^v)[0-9]+\.[0-9]+\.[0-9]+') ; then
|
||||
fatal "Could not infer latest $BRANCH version from $DESCRIBE"
|
||||
fi
|
||||
TAGS="$VERSION latest"
|
||||
TAGS="latest"
|
||||
else
|
||||
VERSION="$BRANCH"
|
||||
TAGS="$VERSION"
|
||||
|
||||
+56
@@ -157,6 +157,28 @@ draft() {
|
||||
--name "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 -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
||||
}
|
||||
@@ -236,6 +258,40 @@ publish() {
|
||||
--name "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 -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
||||
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/latest_release"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
FROM golang:1.5.2
|
||||
ENV GO15VENDOREXPERIMENT 1
|
||||
RUN apt-get update && apt-get install -y libpcap-dev python-requests time file
|
||||
RUN go get github.com/golang/lint/golint github.com/fzipp/gocyclo github.com/client9/misspell/cmd/misspell
|
||||
RUN go get github.com/fzipp/gocyclo github.com/client9/misspell/cmd/misspell
|
||||
RUN go clean -i net && go install -tags netgo std
|
||||
RUN go install -race -tags netgo std
|
||||
COPY build.sh /
|
||||
|
||||
+18
-39
@@ -10,48 +10,33 @@ machine:
|
||||
environment:
|
||||
GOPATH: /home/ubuntu:$GOPATH
|
||||
SRCDIR: /home/ubuntu/src/github.com/weaveworks/weave
|
||||
PATH: $PATH:$HOME/.local/bin:$HOME/google-cloud-sdk/bin/
|
||||
PATH: $PATH:$HOME/.local/bin
|
||||
CLOUDSDK_CORE_DISABLE_PROMPTS: 1
|
||||
STATE: /home/ubuntu/state.env
|
||||
|
||||
dependencies:
|
||||
cache_directories:
|
||||
- "~/docker"
|
||||
post:
|
||||
- "sudo apt-get update && sudo apt-get install bc jq"
|
||||
- curl https://sdk.cloud.google.com | bash
|
||||
- bin/setup-circleci-secrets "$SECRET_PASSWORD"
|
||||
- "mkdir -p $(dirname $SRCDIR) && cp -r $(pwd)/ $SRCDIR"
|
||||
- "cd $SRCDIR; git submodule update --init"
|
||||
- cd $SRCDIR/test; ./gce.sh make_template:
|
||||
parallel: false
|
||||
- "cd $SRCDIR/build; ../tools/rebuild-image weaveworks/weavebuild . Dockerfile build.sh && touch $SRCDIR/.build.uptodate"
|
||||
- bin/circle-dependencies-post
|
||||
|
||||
test:
|
||||
pre:
|
||||
- "$SRCDIR/bin/circle-test-pre":
|
||||
parallel: true
|
||||
override:
|
||||
- cd $SRCDIR; make lint:
|
||||
- "$SRCDIR/bin/circle-test-unit":
|
||||
parallel: true
|
||||
- cd $SRCDIR; COVERDIR=test/coverage make RM= tests:
|
||||
parallel: true
|
||||
- cd $SRCDIR; make COVERAGE=true RM= exes all:
|
||||
parallel: true
|
||||
- cd $SRCDIR; make testrunner:
|
||||
parallel: true
|
||||
- cd $SRCDIR/test; ./gce.sh setup && eval $(./gce.sh hosts) && ./setup.sh:
|
||||
parallel: true
|
||||
- cd $SRCDIR/test; eval $(./gce.sh hosts); export COVERAGE=true; ./run_all.sh:
|
||||
- "$SRCDIR/bin/circle-test-smoke":
|
||||
parallel: true
|
||||
timeout: 300
|
||||
post:
|
||||
- cd $SRCDIR/test; ./gce.sh destroy:
|
||||
parallel: true
|
||||
- test "$CIRCLE_NODE_INDEX" != "0" || (cd $SRCDIR/test; ./gen_coverage_reports.sh):
|
||||
- "$SRCDIR/bin/circle-test-teardown":
|
||||
parallel: true
|
||||
|
||||
teardown:
|
||||
pre:
|
||||
- test "$CIRCLE_NODE_INDEX" != "0" || (go get github.com/mattn/goveralls && goveralls -repotoken $COVERALLS_REPO_TOKEN -coverprofile=$SRCDIR/test/profile.cov -service=circleci || true):
|
||||
parallel: true
|
||||
- test "$CIRCLE_NODE_INDEX" != "0" || (cd $SRCDIR/test; cp coverage.* $CIRCLE_ARTIFACTS):
|
||||
- "$SRCDIR/bin/circle-teardown-pre":
|
||||
parallel: true
|
||||
|
||||
deployment:
|
||||
@@ -59,20 +44,14 @@ deployment:
|
||||
branch: /[0-9]+\.[0-9]+/
|
||||
owner: weaveworks
|
||||
commands:
|
||||
- go get github.com/weaveworks/wordepress && cd /home/ubuntu/src/github.com/weaveworks/wordepress && git checkout v1.0.0 && cd cmd/wordepress && go get
|
||||
- cd $SRCDIR; bin/publish-site "$WP_LIVE_URL" "$WP_LIVE_USER" "$WP_LIVE_PASSWORD"
|
||||
hub:
|
||||
- "$SRCDIR/bin/circle-deploy-release"
|
||||
master:
|
||||
branch: master
|
||||
owner: weaveworks
|
||||
commands:
|
||||
- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
|
||||
- cd $SRCDIR; make clean-bin
|
||||
- cd $SRCDIR; make exes publish
|
||||
# - go get github.com/weaveworks/wordepress && cd /home/ubuntu/src/github.com/weaveworks/wordepress && git checkout v1.0.0 && cd cmd/wordepress && go get
|
||||
# - cd $SRCDIR; bin/publish-site "$WP_DEV_URL" "$WP_DEV_USER" "$WP_DEV_PASSWORD"
|
||||
# issues:
|
||||
# branch: /.*/
|
||||
# owner: weaveworks
|
||||
# commands:
|
||||
# - go get github.com/weaveworks/wordepress && cd /home/ubuntu/src/github.com/weaveworks/wordepress && git checkout v1.0.0 && cd cmd/wordepress && go get
|
||||
# - cd $SRCDIR; bin/publish-site "$WP_DEV_URL" "$WP_DEV_USER" "$WP_DEV_PASSWORD"
|
||||
- "$SRCDIR/bin/circle-deploy-master"
|
||||
issues:
|
||||
branch: /.*/
|
||||
owner: weaveworks
|
||||
commands:
|
||||
- "$SRCDIR/bin/circle-deploy-issues"
|
||||
@@ -3,7 +3,7 @@ package mflagext
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/pkg/mflag"
|
||||
"github.com/weaveworks/docker/pkg/mflag"
|
||||
)
|
||||
|
||||
type listOpts struct {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func AllPids(procRoot string) ([]int, error) {
|
||||
fh, err := os.Open(procRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fh.Close()
|
||||
dirNames, err := fh.Readdirnames(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pids := make([]int, len(dirNames))
|
||||
for _, dirName := range dirNames {
|
||||
pid, err := strconv.Atoi(dirName)
|
||||
if err != nil { // Only interested in numeric entries - skip /proc/stat, etc.
|
||||
continue
|
||||
}
|
||||
pids = append(pids, pid)
|
||||
}
|
||||
return pids, err
|
||||
}
|
||||
+1
-141
@@ -1,17 +1,6 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/j-keck/arping"
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
import "strings"
|
||||
|
||||
// Assert test is true, panic otherwise
|
||||
func Assert(test bool) {
|
||||
@@ -27,132 +16,3 @@ func ErrorMessages(errors []error) string {
|
||||
}
|
||||
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.WithNetNS(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 nil, 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
|
||||
}
|
||||
|
||||
// Lookup the weave interface of a container
|
||||
func GetWeaveNetDevs(processID int) ([]NetDev, error) {
|
||||
// Bail out if this container 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)
|
||||
}
|
||||
nsContainr, err := netns.GetFromPid(processID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open process %d namespace: %s", processID, err)
|
||||
}
|
||||
if nsToplevel.Equal(nsContainr) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
weaveBridge, err := netlink.LinkByName("weave")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Cannot find weave bridge: %s", err)
|
||||
}
|
||||
// Scan devices in root namespace to find those attached to weave bridge
|
||||
indexes := make(map[int]struct{})
|
||||
err = forEachLink(func(link netlink.Link) error {
|
||||
if link.Attrs().MasterIndex == weaveBridge.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 FindNetDevs(processID, func(link netlink.Link) bool {
|
||||
_, isveth := link.(*netlink.Veth)
|
||||
_, found := indexes[link.Attrs().Index]
|
||||
return isveth && found
|
||||
})
|
||||
}
|
||||
|
||||
// Get the weave bridge interface
|
||||
func GetBridgeNetDev(bridgeName string) ([]NetDev, error) {
|
||||
return FindNetDevs(1, func(link netlink.Link) bool {
|
||||
return link.Attrs().Name == bridgeName
|
||||
})
|
||||
}
|
||||
|
||||
// Do post-attach configuration of all veths we have created
|
||||
func ConfigureARPforVeths(processID int, prefix string) error {
|
||||
_, err := FindNetDevs(processID, func(link netlink.Link) bool {
|
||||
ifName := link.Attrs().Name
|
||||
if strings.HasPrefix(ifName, prefix) {
|
||||
weavenet.ConfigureARPCache(ifName)
|
||||
if addrs, err := netlink.AddrList(link, netlink.FAMILY_V4); err == nil {
|
||||
for _, addr := range addrs {
|
||||
arping.GratuitousArpOverIfaceByName(addr.IPNet.IP, ifName)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
return err
|
||||
}
|
||||
@@ -107,6 +107,15 @@ the build script will complain otherwise.
|
||||
N.B. if you're testing the release process, push to your fork
|
||||
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
|
||||
|
||||
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
|
||||
@@ -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)))
|
||||
}
|
||||
+2
-6
@@ -114,11 +114,12 @@ func New(start, end address.Address, peer mesh.PeerName, f OnUpdate) *Ring {
|
||||
common.Assert(start < end)
|
||||
|
||||
ring := &Ring{Start: start, End: end, Peer: peer, Entries: make([]*entry, 0), onUpdate: f}
|
||||
ring.updateExportedVariables()
|
||||
return ring
|
||||
}
|
||||
|
||||
func (r *Ring) Restore(other *Ring) {
|
||||
defer r.trackUpdates()()
|
||||
|
||||
onUpdate := r.onUpdate
|
||||
*r = *other
|
||||
r.onUpdate = onUpdate
|
||||
@@ -149,7 +150,6 @@ func (r *Ring) GrantRangeToHost(start, end address.Address, peer mesh.PeerName)
|
||||
r.assertInvariants()
|
||||
defer r.trackUpdates()()
|
||||
defer r.assertInvariants()
|
||||
defer r.updateExportedVariables()
|
||||
|
||||
// ----------------- Start of Checks -----------------
|
||||
|
||||
@@ -216,7 +216,6 @@ func (r *Ring) GrantRangeToHost(start, end address.Address, peer mesh.PeerName)
|
||||
func (r *Ring) Merge(gossip Ring) (bool, error) {
|
||||
r.assertInvariants()
|
||||
defer r.trackUpdates()()
|
||||
defer r.updateExportedVariables()
|
||||
|
||||
// Don't panic when checking the gossiped in ring.
|
||||
// In this case just return any error found.
|
||||
@@ -402,7 +401,6 @@ func (r *Ring) ClaimForPeers(peers []mesh.PeerName) {
|
||||
|
||||
defer r.trackUpdates()()
|
||||
defer r.assertInvariants()
|
||||
defer r.updateExportedVariables()
|
||||
defer func() {
|
||||
e := r.Entries[len(r.Entries)-1]
|
||||
common.Assert(address.Add(e.Token, address.Offset(e.Free)) == r.End)
|
||||
@@ -453,7 +451,6 @@ func (r *Ring) String() string {
|
||||
func (r *Ring) ReportFree(freespace map[address.Address]address.Count) (updated bool) {
|
||||
r.assertInvariants()
|
||||
defer r.assertInvariants()
|
||||
defer r.updateExportedVariables()
|
||||
|
||||
common.Assert(!r.Empty())
|
||||
entries := r.Entries
|
||||
@@ -562,7 +559,6 @@ func (r *Ring) Transfer(from, to mesh.PeerName) []address.Range {
|
||||
defer r.trackUpdates()()
|
||||
defer r.trackUpdatesOfPeer(from)()
|
||||
defer r.assertInvariants()
|
||||
defer r.updateExportedVariables()
|
||||
|
||||
var newRanges []address.Range
|
||||
|
||||
|
||||
@@ -88,6 +88,14 @@ func (s *Space) Claim(addr address.Address) error {
|
||||
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 {
|
||||
res := address.Count(0)
|
||||
for i := 0; i < len(s.free); i += 2 {
|
||||
|
||||
@@ -98,16 +98,19 @@ func TestSpaceAllocate(t *testing.T) {
|
||||
|
||||
space1 := makeSpace(start, size)
|
||||
require.Equal(t, address.Count(20), space1.NumFreeAddresses())
|
||||
require.Equal(t, address.Count(0), space1.NumOwnedAddresses())
|
||||
space1.assertInvariants()
|
||||
|
||||
_, addr1 := space1.Allocate(address.NewRange(start, size))
|
||||
require.Equal(t, testAddr1, addr1.String(), "address")
|
||||
require.Equal(t, address.Count(19), space1.NumFreeAddresses())
|
||||
require.Equal(t, address.Count(1), space1.NumOwnedAddresses())
|
||||
space1.assertInvariants()
|
||||
|
||||
_, addr2 := space1.Allocate(address.NewRange(start, size))
|
||||
require.False(t, addr2.String() == testAddr1, "address")
|
||||
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(18), space1.NumFreeAddressesInRange(address.Range{Start: ip(testAddr1), End: ip(testAddry)}))
|
||||
space1.assertInvariants()
|
||||
|
||||
@@ -10,6 +10,7 @@ type Status struct {
|
||||
Paxos *paxos.Status
|
||||
Range string
|
||||
RangeNumIPs int
|
||||
ActiveIPs int
|
||||
DefaultSubnet string
|
||||
Entries []EntryStatus
|
||||
PendingClaims []ClaimStatus
|
||||
@@ -52,6 +53,7 @@ func NewStatus(allocator *Allocator, defaultSubnet address.CIDR) *Status {
|
||||
paxosStatus,
|
||||
allocator.universe.String(),
|
||||
int(allocator.universe.Size()),
|
||||
int(allocator.space.NumOwnedAddresses()),
|
||||
defaultSubnet.String(),
|
||||
newEntryStatusSlice(allocator),
|
||||
newClaimStatusSlice(allocator),
|
||||
|
||||
@@ -21,6 +21,7 @@ package tracker
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||
@@ -94,14 +95,14 @@ func (t *AWSVPCTracker) HandleUpdate(prevRanges, currRanges []address.Range, loc
|
||||
for _, cidr := range curr {
|
||||
cidrStr := cidr.String()
|
||||
t.debugf("adding route %s to %s", cidrStr, t.instanceID)
|
||||
_, err := t.createVPCRoute(cidrStr)
|
||||
if err != nil {
|
||||
if _, err := t.createVPCRoute(cidrStr); err != nil {
|
||||
return fmt.Errorf("createVPCRoutes failed: %s", err)
|
||||
}
|
||||
if local {
|
||||
err = t.createHostRoute(cidrStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("createHostRoute failed: %s", err)
|
||||
if err := t.createHostRoute(cidrStr); err != nil {
|
||||
if errno, ok := err.(syscall.Errno); !(ok && errno == syscall.EEXIST) {
|
||||
return fmt.Errorf("createHostRoute failed: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,13 +111,11 @@ func (t *AWSVPCTracker) HandleUpdate(prevRanges, currRanges []address.Range, loc
|
||||
for _, cidr := range prev {
|
||||
cidrStr := cidr.String()
|
||||
t.debugf("removing %s route", cidrStr)
|
||||
_, err := t.deleteVPCRoute(cidrStr)
|
||||
if err != nil {
|
||||
if _, err := t.deleteVPCRoute(cidrStr); err != nil {
|
||||
return fmt.Errorf("deleteVPCRoute failed: %s", err)
|
||||
}
|
||||
if local {
|
||||
err = t.deleteHostRoute(cidrStr)
|
||||
if err != nil {
|
||||
if err := t.deleteHostRoute(cidrStr); err != nil {
|
||||
return fmt.Errorf("deleteHostRoute failed: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
+13
-6
@@ -28,7 +28,7 @@ const (
|
||||
)
|
||||
|
||||
type Upstream interface {
|
||||
Config() *dns.ClientConfig
|
||||
Config() (*dns.ClientConfig, error)
|
||||
}
|
||||
|
||||
func NewUpstream(resolvConf, filterAddress string) Upstream {
|
||||
@@ -48,27 +48,31 @@ type upstream struct {
|
||||
lastModified time.Time
|
||||
}
|
||||
|
||||
func (u *upstream) Config() *dns.ClientConfig {
|
||||
func (u *upstream) Config() (*dns.ClientConfig, error) {
|
||||
u.Lock()
|
||||
defer u.Unlock()
|
||||
|
||||
// If we checked less than five seconds ago return what we already have
|
||||
now := time.Now()
|
||||
if now.Sub(u.lastStat) < 5*time.Second {
|
||||
return u.cachedConfig
|
||||
return u.cachedConfig, nil
|
||||
}
|
||||
u.lastStat = now
|
||||
|
||||
// Reread file if it has changed
|
||||
if fi, err := os.Stat(u.resolvConf); err == nil && fi.ModTime() != u.lastModified {
|
||||
if fi, err := os.Stat(u.resolvConf); err != nil {
|
||||
return u.cachedConfig, err
|
||||
} else if fi.ModTime() != u.lastModified {
|
||||
if config, err := dns.ClientConfigFromFile(u.resolvConf); err == nil {
|
||||
config.Servers = filter(config.Servers, u.filterAddress)
|
||||
u.lastModified = fi.ModTime()
|
||||
u.cachedConfig = config
|
||||
} else {
|
||||
return u.cachedConfig, err
|
||||
}
|
||||
}
|
||||
|
||||
return u.cachedConfig
|
||||
return u.cachedConfig, nil
|
||||
}
|
||||
|
||||
func filter(ss []string, s string) []string {
|
||||
@@ -257,7 +261,10 @@ func (h *handler) handleRecursive(w dns.ResponseWriter, req *dns.Msg) {
|
||||
}
|
||||
}
|
||||
|
||||
upstreamConfig := h.upstream.Config()
|
||||
upstreamConfig, err := h.upstream.Config()
|
||||
if err != nil {
|
||||
h.ns.errorf("unable to read upstream config: %s", err)
|
||||
}
|
||||
for _, server := range upstreamConfig.Servers {
|
||||
reqCopy := req.Copy()
|
||||
reqCopy.Id = dns.Id()
|
||||
|
||||
@@ -19,8 +19,8 @@ type mockUpstream struct {
|
||||
config *dns.ClientConfig
|
||||
}
|
||||
|
||||
func (mu *mockUpstream) Config() *dns.ClientConfig {
|
||||
return mu.config
|
||||
func (mu *mockUpstream) Config() (*dns.ClientConfig, error) {
|
||||
return mu.config, nil
|
||||
}
|
||||
|
||||
func startServer(t *testing.T, upstream *dns.ClientConfig) (*DNSServer, *Nameserver, int, int) {
|
||||
|
||||
@@ -199,7 +199,7 @@ func (n *Nameserver) receiveGossip(msg []byte) (mesh.GossipData, mesh.GossipData
|
||||
return nil, nil, err
|
||||
}
|
||||
if delta := gossip.Timestamp - now(); delta > gossipWindow || delta < -gossipWindow {
|
||||
return nil, nil, fmt.Errorf("clock skew of %d detected", delta)
|
||||
return nil, nil, fmt.Errorf("host clock skew of %ds exceeds %ds limit", delta, gossipWindow)
|
||||
}
|
||||
|
||||
// Filter to remove entries from unknown peers, done before we take
|
||||
|
||||
@@ -36,9 +36,10 @@ func NewStatus(ns *Nameserver, dnsServer *DNSServer) *Status {
|
||||
entry.Tombstone})
|
||||
}
|
||||
|
||||
upstreamConfig, _ := dnsServer.upstream.Config()
|
||||
return &Status{
|
||||
dnsServer.domain,
|
||||
dnsServer.upstream.Config().Servers,
|
||||
upstreamConfig.Servers,
|
||||
dnsServer.address,
|
||||
dnsServer.ttl,
|
||||
entryStatusSlice}
|
||||
|
||||
+20
-12
@@ -6,6 +6,7 @@ import "unsafe"
|
||||
|
||||
const (
|
||||
SIOCETHTOOL = 0x8946 // linux/sockios.h
|
||||
ETHTOOL_GTXCSUM = 0x00000016 // linux/ethtool.h
|
||||
ETHTOOL_STXCSUM = 0x00000017 // linux/ethtool.h
|
||||
IFNAMSIZ = 16 // linux/if.h
|
||||
)
|
||||
@@ -22,31 +23,38 @@ type EthtoolValue struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
func ioctlEthtool(fd int, argp uintptr) error {
|
||||
_, _, errno := syscall.RawSyscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(SIOCETHTOOL), argp)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Disable TX checksum offload on specified interface
|
||||
func EthtoolTXOff(name string) error {
|
||||
if len(name)+1 > IFNAMSIZ {
|
||||
return fmt.Errorf("name too long")
|
||||
}
|
||||
|
||||
value := EthtoolValue{ETHTOOL_STXCSUM, 0}
|
||||
request := IFReqData{Data: uintptr(unsafe.Pointer(&value))}
|
||||
|
||||
copy(request.Name[:], name)
|
||||
|
||||
socket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.Close(socket)
|
||||
|
||||
_, _, errno := syscall.RawSyscall(syscall.SYS_IOCTL,
|
||||
uintptr(socket),
|
||||
uintptr(SIOCETHTOOL),
|
||||
uintptr(unsafe.Pointer(&request)))
|
||||
// Request current value
|
||||
value := EthtoolValue{Cmd: ETHTOOL_GTXCSUM}
|
||||
request := IFReqData{Data: uintptr(unsafe.Pointer(&value))}
|
||||
copy(request.Name[:], name)
|
||||
|
||||
if errno != 0 {
|
||||
return errno
|
||||
if err := ioctlEthtool(socket, uintptr(unsafe.Pointer(&request))); err != nil {
|
||||
return err
|
||||
}
|
||||
if value.Data == 0 { // if already off, don't try to change
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
value = EthtoolValue{ETHTOOL_STXCSUM, 0}
|
||||
return ioctlEthtool(socket, uintptr(unsafe.Pointer(&request)))
|
||||
}
|
||||
+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)
|
||||
}
|
||||
+50
-3
@@ -1,13 +1,31 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
func WithNetNS(ns netns.NsHandle, work func() error) error {
|
||||
var ErrLinkNotFound = errors.New("Link not found")
|
||||
|
||||
// NB: The following function is unsafe, because:
|
||||
// - It changes a network namespace (netns) of an OS thread which runs
|
||||
// the function. During execution, the Go runtime might clone a new OS thread
|
||||
// for scheduling other go-routines, thus they might end up running in
|
||||
// a "wrong" netns.
|
||||
// - runtime.LockOSThread does not guarantee that a spawned go-routine on
|
||||
// the locked thread will be run by it. Thus, the work function is
|
||||
// not allowed to spawn any go-routine which is dependent on the given netns.
|
||||
|
||||
// Please see https://github.com/weaveworks/weave/issues/2388#issuecomment-228365069
|
||||
// for more details and make sure that you understand the implications before
|
||||
// using the function!
|
||||
func WithNetNSUnsafe(ns netns.NsHandle, work func() error) error {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
@@ -26,12 +44,41 @@ func WithNetNS(ns netns.NsHandle, work func() error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func WithNetNSLink(ns netns.NsHandle, ifName string, work func(link netlink.Link) error) error {
|
||||
return WithNetNS(ns, func() error {
|
||||
func WithNetNSLinkUnsafe(ns netns.NsHandle, ifName string, work func(link netlink.Link) error) error {
|
||||
return WithNetNSUnsafe(ns, func() error {
|
||||
link, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
if err.Error() == errors.New("Link not found").Error() {
|
||||
return ErrLinkNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
return work(link)
|
||||
})
|
||||
}
|
||||
|
||||
var WeaveUtilCmd = "weaveutil"
|
||||
|
||||
// A safe version of WithNetNS* which creates a process executing
|
||||
// "nsenter --net=<ns-path> weaveutil <cmd> [args]".
|
||||
func WithNetNS(nsPath string, cmd string, args ...string) ([]byte, error) {
|
||||
var stdout, stderr bytes.Buffer
|
||||
|
||||
args = append([]string{"--net=" + nsPath, WeaveUtilCmd, cmd}, args...)
|
||||
c := exec.Command("nsenter", args...)
|
||||
c.Stdout = &stdout
|
||||
c.Stderr = &stderr
|
||||
if err := c.Run(); err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", string(stderr.Bytes()), err)
|
||||
}
|
||||
|
||||
return stdout.Bytes(), nil
|
||||
}
|
||||
|
||||
func WithNetNSByPid(pid int, cmd string, args ...string) ([]byte, error) {
|
||||
return WithNetNS(NSPathByPid(pid), cmd, args...)
|
||||
}
|
||||
|
||||
func NSPathByPid(pid int) string {
|
||||
return fmt.Sprintf("/proc/%d/ns/net", pid)
|
||||
}
|
||||
+88
-19
@@ -3,8 +3,10 @@ package net
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/j-keck/arping"
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
@@ -103,16 +105,26 @@ const (
|
||||
vethPrefix = "v" + VethName // starts with "veth" to suppress UI notifications
|
||||
)
|
||||
|
||||
func interfaceExistsInNamespace(ns netns.NsHandle, ifName string) bool {
|
||||
err := WithNetNS(ns, func() error {
|
||||
_, err := netlink.LinkByName(ifName)
|
||||
return err
|
||||
})
|
||||
func interfaceExistsInNamespace(netNSPath string, ifName string) bool {
|
||||
_, err := WithNetNS(netNSPath, "check-iface", ifName)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int, withMulticastRoute bool, cidrs []*net.IPNet, keepTXOn bool) error {
|
||||
if !interfaceExistsInNamespace(ns, ifName) {
|
||||
// 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()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !interfaceExistsInNamespace(netNSPath, ifName) {
|
||||
maxIDLen := IFNAMSIZ - 1 - len(vethPrefix+"pl")
|
||||
if len(id) > maxIDLen {
|
||||
id = id[:maxIDLen] // trim passed ID if too long
|
||||
@@ -122,15 +134,7 @@ func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int,
|
||||
if err := netlink.LinkSetNsFd(veth, int(ns)); err != nil {
|
||||
return fmt.Errorf("failed to move veth to container netns: %s", err)
|
||||
}
|
||||
if err := WithNetNS(ns, func() error {
|
||||
if err := netlink.LinkSetName(veth, ifName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ConfigureARPCache(ifName); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
if _, err := WithNetNS(netNSPath, "setup-iface", peerName, ifName); err != nil {
|
||||
return fmt.Errorf("error setting up interface: %s", err)
|
||||
}
|
||||
return nil
|
||||
@@ -140,11 +144,26 @@ func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int,
|
||||
}
|
||||
}
|
||||
|
||||
if err := WithNetNSLink(ns, ifName, func(veth netlink.Link) error {
|
||||
if err := WithNetNSLinkUnsafe(ns, ifName, func(veth netlink.Link) error {
|
||||
newAddresses, err := AddAddresses(veth, cidrs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add multicast ACCEPT rules for new subnets
|
||||
for _, ipnet := range newAddresses {
|
||||
acceptRule := []string{"-i", ifName, "-s", subnet(ipnet), "-d", "224.0.0.0/4", "-j", "ACCEPT"}
|
||||
exists, err := ipt.Exists("filter", "INPUT", acceptRule...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
if err := ipt.Insert("filter", "INPUT", 1, acceptRule...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := netlink.LinkSetUp(veth); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -176,8 +195,21 @@ func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int,
|
||||
return nil
|
||||
}
|
||||
|
||||
func DetachContainer(ns netns.NsHandle, id, ifName string, cidrs []*net.IPNet) error {
|
||||
return WithNetNSLink(ns, ifName, func(veth netlink.Link) 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()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return WithNetNSLinkUnsafe(ns, ifName, func(veth netlink.Link) error {
|
||||
existingAddrs, err := netlink.AddrList(veth, netlink.FAMILY_V4)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get IP address for %q: %v", veth.Attrs().Name, err)
|
||||
@@ -194,7 +226,31 @@ func DetachContainer(ns netns.NsHandle, id, ifName string, cidrs []*net.IPNet) e
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get IP address for %q: %v", veth.Attrs().Name, err)
|
||||
}
|
||||
|
||||
// Remove multicast ACCEPT rules for subnets we no longer have addresses in
|
||||
subnets := subnets(addrs)
|
||||
rules, err := ipt.List("filter", "INPUT")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rule := range rules {
|
||||
ps := strings.Split(rule, " ")
|
||||
if len(ps) == 10 &&
|
||||
ps[0] == "-A" && ps[2] == "-s" && ps[4] == "-d" && ps[5] == "224.0.0.0/4" &&
|
||||
ps[6] == "-i" && ps[7] == ifName && ps[8] == "-j" && ps[9] == "ACCEPT" {
|
||||
|
||||
if _, found := subnets[ps[3]]; !found {
|
||||
if err := ipt.Delete("filter", "INPUT", ps[2:]...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(addrs) == 0 { // all addresses gone: remove the interface
|
||||
if err := ipt.Delete("filter", "INPUT", "-i", ifName, "-d", "224.0.0.0/4", "-j", "DROP"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.LinkDel(veth); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -202,3 +258,16 @@ func DetachContainer(ns netns.NsHandle, id, ifName string, cidrs []*net.IPNet) e
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func subnet(ipn *net.IPNet) string {
|
||||
ones, _ := ipn.Mask.Size()
|
||||
return fmt.Sprintf("%s/%d", ipn.IP.Mask(ipn.Mask).String(), ones)
|
||||
}
|
||||
|
||||
func subnets(addrs []netlink.Addr) map[string]struct{} {
|
||||
subnets := make(map[string]struct{})
|
||||
for _, addr := range addrs {
|
||||
subnets[subnet(addr.IPNet)] = struct{}{}
|
||||
}
|
||||
return subnets
|
||||
}
|
||||
+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,78 @@
|
||||
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()
|
||||
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()
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,337 @@
|
||||
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 has an IP address and isn't sharing the host network namespace
|
||||
return 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
|
||||
}
|
||||
+49
-37
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
weaveapi "github.com/weaveworks/weave/api"
|
||||
"github.com/weaveworks/weave/common"
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
ipamplugin "github.com/weaveworks/weave/plugin/ipam"
|
||||
)
|
||||
@@ -40,6 +39,19 @@ func loadNetConf(bytes []byte) (*NetConf, error) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c *CNIPlugin) getIP(ipamType string, args *skel.CmdArgs) (result *types.Result, err error) {
|
||||
// Default IPAM is Weave's own
|
||||
if ipamType == "" {
|
||||
result, err = ipamplugin.NewIpam(c.weave).Allocate(args)
|
||||
} else {
|
||||
result, err = ipam.ExecAdd(ipamType, args.StdinData)
|
||||
}
|
||||
if err == nil && result.IP4 == nil {
|
||||
return nil, fmt.Errorf("IPAM plugin failed to allocate IP address")
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
||||
conf, err := loadNetConf(args.StdinData)
|
||||
if err != nil {
|
||||
@@ -53,24 +65,26 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
||||
return fmt.Errorf("IP Masquerading functionality not supported")
|
||||
}
|
||||
|
||||
var result *types.Result
|
||||
// Default IPAM is Weave's own
|
||||
if conf.IPAM.Type == "" {
|
||||
result, err = ipamplugin.NewIpam(c.weave).Allocate(args)
|
||||
} else {
|
||||
result, err = ipam.ExecAdd(conf.IPAM.Type, args.StdinData)
|
||||
}
|
||||
result, err := c.getIP(conf.IPAM.Type, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to allocate IP address: %s", err)
|
||||
}
|
||||
if result.IP4 == nil {
|
||||
return fmt.Errorf("IPAM plugin failed to allocate IP address")
|
||||
}
|
||||
|
||||
// If config says nothing about routes or gateway, default one will be via the bridge
|
||||
if result.IP4.Routes == nil && result.IP4.Gateway == nil {
|
||||
bridgeIP, err := findBridgeIP(conf.BrName, result.IP4.IP)
|
||||
if err != nil {
|
||||
if err == errBridgeNoIP {
|
||||
bridgeArgs := *args
|
||||
bridgeArgs.ContainerID = "weave:expose"
|
||||
bridgeIPResult, err := c.getIP(conf.IPAM.Type, &bridgeArgs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to allocate IP address for bridge: %s", err)
|
||||
}
|
||||
if err := assignBridgeIP(conf.BrName, bridgeIPResult.IP4.IP); err != nil {
|
||||
return fmt.Errorf("unable to assign IP address to bridge: %s", err)
|
||||
}
|
||||
bridgeIP = bridgeIPResult.IP4.IP.IP
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
result.IP4.Gateway = bridgeIP
|
||||
@@ -78,7 +92,7 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
||||
|
||||
ns, err := netns.GetFromPath(args.Netns)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error accessing namespace %q: %s", args.Netns, err)
|
||||
}
|
||||
defer ns.Close()
|
||||
|
||||
@@ -92,10 +106,10 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
||||
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
|
||||
}
|
||||
if err := weavenet.WithNetNSLink(ns, args.IfName, func(link netlink.Link) error {
|
||||
if err := weavenet.WithNetNSLinkUnsafe(ns, args.IfName, func(link netlink.Link) error {
|
||||
return setupRoutes(link, args.IfName, result.IP4.IP, result.IP4.Gateway, result.IP4.Routes)
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error setting up routes: %s", err)
|
||||
@@ -130,24 +144,34 @@ func setupRoutes(link netlink.Link, name string, ipnet net.IPNet, gw net.IP, rou
|
||||
return nil
|
||||
}
|
||||
|
||||
func assignBridgeIP(bridgeName string, ipnet net.IPNet) error {
|
||||
link, err := netlink.LinkByName(bridgeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.AddrAdd(link, &netlink.Addr{IPNet: &ipnet}); err != nil {
|
||||
return fmt.Errorf("failed to add IP address to %q: %v", bridgeName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var errBridgeNoIP = fmt.Errorf("Bridge has no IP address")
|
||||
|
||||
func findBridgeIP(bridgeName string, subnet net.IPNet) (net.IP, error) {
|
||||
netdevs, err := common.GetBridgeNetDev(bridgeName)
|
||||
netdev, err := weavenet.GetBridgeNetDev(bridgeName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to get netdev for %q bridge: %s", bridgeName, err)
|
||||
}
|
||||
if len(netdevs) == 0 {
|
||||
return nil, fmt.Errorf("Could not find %q bridge", bridgeName)
|
||||
if len(netdev.CIDRs) == 0 {
|
||||
return nil, errBridgeNoIP
|
||||
}
|
||||
if len(netdevs[0].CIDRs) == 0 {
|
||||
return nil, fmt.Errorf("Bridge %q has no IP addresses", bridgeName)
|
||||
}
|
||||
for _, cidr := range netdevs[0].CIDRs {
|
||||
for _, cidr := range netdev.CIDRs {
|
||||
if subnet.Contains(cidr.IP) {
|
||||
return cidr.IP, nil
|
||||
}
|
||||
}
|
||||
// None in the required subnet; just return the first one
|
||||
return netdevs[0].CIDRs[0].IP, nil
|
||||
return netdev.CIDRs[0].IP, nil
|
||||
}
|
||||
|
||||
func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
|
||||
@@ -156,20 +180,8 @@ func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ns, err := netns.GetFromPath(args.Netns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ns.Close()
|
||||
err = weavenet.WithNetNS(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)
|
||||
if _, err = weavenet.WithNetNS(args.Netns, "del-iface", args.IfName); err != nil {
|
||||
return fmt.Errorf("error removing interface %q: %s", args.IfName, err)
|
||||
}
|
||||
|
||||
// Default IPAM is Weave's own
|
||||
|
||||
+26
-13
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/docker/libnetwork/drivers/remote/api"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
weaveapi "github.com/weaveworks/weave/api"
|
||||
@@ -94,11 +95,21 @@ func (driver *driver) DeleteNetwork(delreq *api.DeleteNetworkRequest) error {
|
||||
|
||||
func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.CreateEndpointResponse, error) {
|
||||
driver.logReq("CreateEndpoint", create, create.EndpointID)
|
||||
common.Log.Debugf("interface %+v", create.Interface)
|
||||
|
||||
if create.Interface == nil {
|
||||
return nil, driver.error("CreateEndpoint", "Not supported: creating an interface from within CreateEndpoint")
|
||||
}
|
||||
resp := &api.CreateEndpointResponse{}
|
||||
|
||||
// create veths. note we assume endpoint IDs are unique in the first 9 chars
|
||||
name, peerName := vethPair(create.EndpointID)
|
||||
if _, err := weavenet.CreateAndAttachVeth(name, peerName, weavenet.WeaveBridgeName, 0, false, nil); err != nil {
|
||||
return nil, driver.error("JoinEndpoint", "%s", err)
|
||||
}
|
||||
|
||||
// Send back the MAC address
|
||||
link, _ := netlink.LinkByName(peerName)
|
||||
resp := &api.CreateEndpointResponse{Interface: &api.EndpointInterface{MacAddress: link.Attrs().HardwareAddr.String()}}
|
||||
|
||||
driver.logRes("CreateEndpoint", resp)
|
||||
return resp, nil
|
||||
@@ -106,6 +117,17 @@ func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.Cr
|
||||
|
||||
func (driver *driver) DeleteEndpoint(deleteReq *api.DeleteEndpointRequest) error {
|
||||
driver.logReq("DeleteEndpoint", deleteReq, deleteReq.EndpointID)
|
||||
name, _ := vethPair(deleteReq.EndpointID)
|
||||
veth := &netlink.Veth{LinkAttrs: netlink.LinkAttrs{Name: name}}
|
||||
if err := netlink.LinkDel(veth); err != nil {
|
||||
// Try again using the name construction from earlier plugin version,
|
||||
// in case user has upgraded with endpoints still extant
|
||||
veth.Name = "vethwl" + deleteReq.EndpointID[:5]
|
||||
if err2 := netlink.LinkDel(veth); err2 != nil {
|
||||
// Note we report the first error
|
||||
driver.warn("LeaveEndpoint", "unable to delete veth %q: %s", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -122,11 +144,7 @@ func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error
|
||||
return nil, driver.error("JoinEndpoint", "unable to find network info: %s", err)
|
||||
}
|
||||
|
||||
name, peerName := vethPair(j.EndpointID)
|
||||
if _, err := weavenet.CreateAndAttachVeth(name, peerName, weavenet.WeaveBridgeName, 0, false, nil); err != nil {
|
||||
return nil, driver.error("JoinEndpoint", "%s", err)
|
||||
}
|
||||
|
||||
_, peerName := vethPair(j.EndpointID)
|
||||
response := &api.JoinResponse{
|
||||
InterfaceName: &api.InterfaceName{
|
||||
SrcName: peerName,
|
||||
@@ -186,12 +204,6 @@ func (driver *driver) setupNetworkInfo(id string, isOurs bool, options map[strin
|
||||
|
||||
func (driver *driver) LeaveEndpoint(leave *api.LeaveRequest) error {
|
||||
driver.logReq("LeaveEndpoint", leave, fmt.Sprintf("%s:%s", leave.NetworkID, leave.EndpointID))
|
||||
|
||||
name, _ := vethPair(leave.EndpointID)
|
||||
veth := &netlink.Veth{LinkAttrs: netlink.LinkAttrs{Name: name}}
|
||||
if err := netlink.LinkDel(veth); err != nil {
|
||||
driver.warn("LeaveEndpoint", "unable to delete veth: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -206,7 +218,8 @@ func (driver *driver) DiscoverDelete(disco *api.DiscoveryNotification) error {
|
||||
}
|
||||
|
||||
func vethPair(id string) (string, string) {
|
||||
return "vethwl" + id[:5], "vethwg" + id[:5]
|
||||
// IFNAMSIZ is buffer length; subtract 6 for "vethwl" and 1 for terminating nul
|
||||
return "vethwl" + id[:unix.IFNAMSIZ-7], "vethwg" + id[:unix.IFNAMSIZ-7]
|
||||
}
|
||||
|
||||
// logging
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
weaveapi "github.com/weaveworks/weave/api"
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/common/docker"
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
@@ -46,7 +45,7 @@ func (w *watcher) ContainerStarted(id string) {
|
||||
if err := w.weave.RegisterWithDNS(id, fqdn, net.IPAddress); err != nil {
|
||||
w.driver.warn("ContainerStarted", "unable to register %s with weaveDNS: %s", id, err)
|
||||
}
|
||||
if err := common.ConfigureARPforVeths(info.State.Pid, weavenet.VethName); err != nil {
|
||||
if _, err := weavenet.WithNetNSByPid(info.State.Pid, "configure-arp", weavenet.VethName); err != nil {
|
||||
w.driver.warn("ContainerStarted", "unable to configure interfaces: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/
|
||||
ENTRYPOINT ["/home/weave/plugin"]
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM DOCKERHUB_USER/weave
|
||||
ADD ./launch.sh ./kube-peers /home/weave/
|
||||
ENTRYPOINT ["/home/weave/launch.sh"]
|
||||
Executable
+158
@@ -0,0 +1,158 @@
|
||||
#!/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}
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
SOURCE_BINARY=/usr/bin/weaveutil
|
||||
VERSION=$(/home/weave/weaver --version | sed -E 's/weave router (.*?)/\1/')
|
||||
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
|
||||
|
||||
# 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
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/mflag"
|
||||
"github.com/weaveworks/docker/pkg/mflag"
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/common/mflagext"
|
||||
"github.com/weaveworks/weave/proxy"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM scratch
|
||||
FROM DOCKERHUB_USER/weaveexec
|
||||
MAINTAINER Weaveworks Inc <help@weave.works>
|
||||
LABEL works.weave.role=system
|
||||
WORKDIR /home/weave
|
||||
+13
-13
@@ -32,9 +32,21 @@ func checkForUpdates(dockerVersion string, network string) {
|
||||
|
||||
var uts syscall.Utsname
|
||||
syscall.Uname(&uts)
|
||||
|
||||
release := uts.Release[:]
|
||||
releaseBytes := make([]byte, len(release))
|
||||
i := 0
|
||||
for ; i < len(release); i++ {
|
||||
if release[i] == 0 {
|
||||
break
|
||||
}
|
||||
releaseBytes[i] = uint8(release[i])
|
||||
}
|
||||
kernelVersion := string(releaseBytes[:i])
|
||||
|
||||
flags := map[string]string{
|
||||
"docker-version": dockerVersion,
|
||||
"kernel-version": charsToString(uts.Release[:]),
|
||||
"kernel-version": kernelVersion,
|
||||
}
|
||||
if network != "" {
|
||||
flags["network"] = network
|
||||
@@ -49,15 +61,3 @@ func checkForUpdates(dockerVersion string, network string) {
|
||||
}
|
||||
checker = checkpoint.CheckInterval(¶ms, updateCheckPeriod, handleResponse)
|
||||
}
|
||||
|
||||
func charsToString(ca []int8) string {
|
||||
s := make([]byte, len(ca))
|
||||
i := 0
|
||||
for ; i < len(ca); i++ {
|
||||
if ca[i] == 0 {
|
||||
break
|
||||
}
|
||||
s[i] = uint8(ca[i])
|
||||
}
|
||||
return string(s[:i])
|
||||
}
|
||||
+34
-18
@@ -19,16 +19,10 @@ import (
|
||||
weave "github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
var allConnectionStates = []string{"established", "pending", "retrying", "failed", "connecting"}
|
||||
|
||||
var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
||||
"countDNSEntries": func(entries []nameserver.EntryStatus) int {
|
||||
count := 0
|
||||
for _, entry := range entries {
|
||||
if entry.Tombstone == 0 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
},
|
||||
"countDNSEntries": countDNSEntries,
|
||||
"printList": func(list []string) string {
|
||||
if len(list) == 0 {
|
||||
return "none"
|
||||
@@ -55,27 +49,28 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
||||
s.ips += entry.Size
|
||||
}
|
||||
|
||||
printOwned := func(name string, nickName string, reachable bool, ips uint32) {
|
||||
reachableStr := ""
|
||||
if !reachable {
|
||||
reachableStr = "- unreachable!"
|
||||
}
|
||||
printOwned := func(name string, nickName string, info string, ips uint32) {
|
||||
percentageRanges := float32(ips) * 100.0 / float32(status.RangeNumIPs)
|
||||
|
||||
displayName := name + "(" + nickName + ")"
|
||||
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
|
||||
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
|
||||
for peer, stats := range peerStats {
|
||||
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 {
|
||||
counts[conn.State]++
|
||||
}
|
||||
return printCounts(counts, []string{"established", "pending", "retrying", "failed", "connecting"})
|
||||
return printCounts(counts, allConnectionStates)
|
||||
},
|
||||
"printPeerConnectionCounts": func(peers []mesh.PeerStatus) string {
|
||||
counts := make(map[string]int)
|
||||
@@ -118,6 +113,26 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
||||
"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
|
||||
func printCounts(counts map[string]int, keys []string) string {
|
||||
var stringCounts []string
|
||||
@@ -263,6 +278,7 @@ type WeaveStatus struct {
|
||||
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) {
|
||||
status := func() WeaveStatus {
|
||||
return WeaveStatus{
|
||||
|
||||
+28
-10
@@ -11,9 +11,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/mflag"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/profile"
|
||||
"github.com/weaveworks/docker/pkg/mflag"
|
||||
"github.com/weaveworks/mesh"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
@@ -132,6 +132,7 @@ func main() {
|
||||
bufSzMB int
|
||||
noDiscovery bool
|
||||
httpAddr string
|
||||
statusAddr string
|
||||
ipamConfig ipamConfig
|
||||
dockerAPI string
|
||||
peers []string
|
||||
@@ -165,6 +166,7 @@ func main() {
|
||||
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.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.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")
|
||||
@@ -180,7 +182,7 @@ func main() {
|
||||
mflag.StringVar(&datapathName, []string{"-datapath"}, "", "ODP datapath name")
|
||||
mflag.StringVar(&trustedSubnetStr, []string{"-trusted-subnets"}, "", "comma-separated list of trusted subnets in CIDR notation")
|
||||
mflag.StringVar(&dbPrefix, []string{"-db-prefix"}, "/weavedb/weave", "pathname/prefix of filename to store data")
|
||||
mflag.BoolVar(&isAWSVPC, []string{"#awsvpc", "-awsvpc"}, false, "use AWS VPC for routing")
|
||||
mflag.BoolVar(&isAWSVPC, []string{"-awsvpc"}, false, "use AWS VPC for routing")
|
||||
|
||||
// crude way of detecting that we probably have been started in a
|
||||
// container, with `weave launch` --> suppress misleading paths in
|
||||
@@ -194,7 +196,7 @@ func main() {
|
||||
|
||||
peers = mflag.Args()
|
||||
if resume && len(peers) > 0 {
|
||||
Log.Fatalf("You must not specify an initial peer list in conjuction with --resume")
|
||||
Log.Fatalf("You must not specify an initial peer list in conjunction with --resume")
|
||||
}
|
||||
|
||||
common.SetLogLevel(logLevel)
|
||||
@@ -252,6 +254,7 @@ func main() {
|
||||
}
|
||||
|
||||
var dockerCli *docker.Client
|
||||
dockerVersion := "none"
|
||||
if dockerAPI != "" {
|
||||
dc, err := docker.NewClient(dockerAPI)
|
||||
if err != nil {
|
||||
@@ -260,13 +263,14 @@ func main() {
|
||||
Log.Info(dc.Info())
|
||||
}
|
||||
dockerCli = dc
|
||||
dockerVersion = dockerCli.DockerVersion()
|
||||
}
|
||||
|
||||
network := ""
|
||||
if isAWSVPC {
|
||||
network = "awsvpc"
|
||||
}
|
||||
checkForUpdates(dockerCli.DockerVersion(), network)
|
||||
checkForUpdates(dockerVersion, network)
|
||||
|
||||
observeContainers := func(o docker.ContainerObserver) {
|
||||
if dockerCli != nil {
|
||||
@@ -296,9 +300,11 @@ func main() {
|
||||
}
|
||||
allocator, defaultSubnet = createAllocator(router, ipamConfig, db, t, isKnownPeer)
|
||||
observeContainers(allocator)
|
||||
ids, err := dockerCli.AllContainerIDs()
|
||||
checkFatal(err)
|
||||
allocator.PruneOwned(ids)
|
||||
if dockerCli != nil {
|
||||
ids, err := dockerCli.AllContainerIDs()
|
||||
checkFatal(err)
|
||||
allocator.PruneOwned(ids)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -318,6 +324,7 @@ func main() {
|
||||
if errors := router.InitiateConnections(peers, false); len(errors) > 0 {
|
||||
Log.Fatal(common.ErrorMessages(errors))
|
||||
}
|
||||
checkFatal(router.CreateRestartSentinel())
|
||||
|
||||
// The weave script always waits for a status call to succeed,
|
||||
// so there is no point in doing "weave launch --http-addr ''".
|
||||
@@ -332,9 +339,20 @@ func main() {
|
||||
}
|
||||
router.HandleHTTP(muxRouter)
|
||||
HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver)
|
||||
muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver))
|
||||
http.Handle("/", common.LoggingHTTPHandler(muxRouter))
|
||||
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)
|
||||
@@ -550,7 +568,7 @@ func parsePeerNames(s string) ([]mesh.PeerName, error) {
|
||||
return peerNames, nil
|
||||
}
|
||||
|
||||
func listenAndServeHTTP(httpAddr string) {
|
||||
func listenAndServeHTTP(httpAddr string, handler http.Handler) {
|
||||
protocol := "tcp"
|
||||
if strings.HasPrefix(httpAddr, "/") {
|
||||
os.Remove(httpAddr) // in case it's there from last time
|
||||
@@ -560,7 +578,7 @@ func listenAndServeHTTP(httpAddr string) {
|
||||
if err != nil {
|
||||
Log.Fatal("Unable to create http listener socket: ", err)
|
||||
}
|
||||
err = http.Serve(l, nil)
|
||||
err = http.Serve(l, handler)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
}
|
||||
+45
-23
@@ -2,8 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
"github.com/weaveworks/weave/common"
|
||||
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
|
||||
func containerAddrs(args []string) error {
|
||||
@@ -12,46 +14,66 @@ func containerAddrs(args []string) error {
|
||||
}
|
||||
bridgeName := args[0]
|
||||
|
||||
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||
client, err := docker.NewVersionedClientFromEnv("1.18")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, containerID := range args[1:] {
|
||||
netDevs, err := getNetDevs(bridgeName, c, containerID)
|
||||
if err != nil {
|
||||
peerIDs, err := weavenet.ConnectedToBridgeVethPeerIds(bridgeName)
|
||||
if err != nil {
|
||||
if err == weavenet.ErrLinkNotFound {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
var containerIDs []string
|
||||
containers := make(map[string]*docker.Container)
|
||||
|
||||
for _, cid := range args[1:] {
|
||||
if cid == "weave:expose" {
|
||||
netDev, err := weavenet.GetBridgeNetDev(bridgeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printNetDevs(cid, []weavenet.Dev{netDev})
|
||||
continue
|
||||
}
|
||||
if containers[cid], err = client.InspectContainer(cid); err != nil {
|
||||
if _, ok := err.(*docker.NoSuchContainer); ok {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
// To output in the right order, we keep the list of container IDs
|
||||
containerIDs = append(containerIDs, cid)
|
||||
}
|
||||
|
||||
for _, netDev := range netDevs {
|
||||
fmt.Printf("%12s %s %s", containerID, netDev.Name, netDev.MAC.String())
|
||||
for _, cidr := range netDev.CIDRs {
|
||||
prefixLength, _ := cidr.Mask.Size()
|
||||
fmt.Printf(" %v/%v", cidr.IP, prefixLength)
|
||||
}
|
||||
fmt.Println()
|
||||
for _, cid := range containerIDs {
|
||||
netDevs, err := getNetDevs(client, containers[cid], peerIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printNetDevs(cid, netDevs)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getNetDevs(bridgeName string, c *docker.Client, containerID string) ([]common.NetDev, error) {
|
||||
if containerID == "weave:expose" {
|
||||
return common.GetBridgeNetDev(bridgeName)
|
||||
}
|
||||
|
||||
container, err := c.InspectContainer(containerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func getNetDevs(c *docker.Client, container *docker.Container, peerIDs []int) ([]weavenet.Dev, error) {
|
||||
if container.State.Pid == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return weavenet.GetNetDevsByVethPeerIds(container.State.Pid, peerIDs)
|
||||
}
|
||||
|
||||
return common.GetWeaveNetDevs(container.State.Pid)
|
||||
func printNetDevs(cid string, netDevs []weavenet.Dev) {
|
||||
for _, netDev := range netDevs {
|
||||
fmt.Printf("%12s %s %s", cid, netDev.Name, netDev.MAC.String())
|
||||
for _, cidr := range netDev.CIDRs {
|
||||
prefixLength, _ := cidr.Mask.Size()
|
||||
fmt.Printf(" %v/%v", cidr.IP, prefixLength)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
+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 {
|
||||
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 {
|
||||
return fmt.Errorf("unable to open host namespace: %s", err)
|
||||
} else if nsHost.Equal(nsContainer) {
|
||||
@@ -50,7 +55,7 @@ func attach(args []string) error {
|
||||
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 err != nil && !processExists(pid) {
|
||||
err = fmt.Errorf("Container %s died", args[0])
|
||||
@@ -58,23 +63,19 @@ func attach(args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func containerPidAndNs(containerID string) (int, netns.NsHandle, error) {
|
||||
func containerPid(containerID string) (int, error) {
|
||||
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||
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)
|
||||
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 {
|
||||
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)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("unable to open namespace for container %s: %s", containerID, err)
|
||||
}
|
||||
return container.State.Pid, ns, nil
|
||||
return container.State.Pid, nil
|
||||
}
|
||||
|
||||
func processExists(pid int) bool {
|
||||
@@ -99,7 +100,7 @@ func detach(args []string) error {
|
||||
cmdUsage("detach-container", "<container-id> <cidr>...")
|
||||
}
|
||||
|
||||
_, ns, err := containerPidAndNs(args[0])
|
||||
pid, err := containerPid(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -107,5 +108,5 @@ func detach(args []string) error {
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
)
|
||||
|
||||
func dockerTLSArgs(args []string) error {
|
||||
@@ -15,18 +17,14 @@ func dockerTLSArgs(args []string) error {
|
||||
cmdUsage("docker-tls-args", "")
|
||||
}
|
||||
procRoot := "/proc"
|
||||
dirEntries, err := ioutil.ReadDir(procRoot)
|
||||
pids, err := common.AllPids(procRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, dirEntry := range dirEntries {
|
||||
dirName := dirEntry.Name()
|
||||
if _, err := strconv.Atoi(dirName); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, pid := range pids {
|
||||
isDaemon := false
|
||||
dirName := strconv.Itoa(pid)
|
||||
if comm, err := ioutil.ReadFile(filepath.Join(procRoot, dirName, "comm")); err != nil {
|
||||
continue
|
||||
} else if string(comm) == "dockerd\n" {
|
||||
|
||||
@@ -4,6 +4,10 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
|
||||
var commands map[string]func([]string) error
|
||||
@@ -20,12 +24,38 @@ func init() {
|
||||
"create-plugin-network": createPluginNetwork,
|
||||
"remove-plugin-network": removePluginNetwork,
|
||||
"container-addrs": containerAddrs,
|
||||
"process-addrs": processAddrs,
|
||||
"attach-container": attach,
|
||||
"detach-container": detach,
|
||||
"configure-arp": configureARP,
|
||||
"check-iface": checkIface,
|
||||
"del-iface": delIface,
|
||||
"setup-iface": setupIface,
|
||||
"list-netdevs": listNetDevs,
|
||||
"cni-net": cniNet,
|
||||
"cni-ipam": cniIPAM,
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
usage()
|
||||
os.Exit(1)
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/j-keck/arping"
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
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 {
|
||||
if len(args) != 1 {
|
||||
cmdUsage("configure-arp", "<iface-name-prefix>")
|
||||
}
|
||||
prefix := args[0]
|
||||
|
||||
links, err := netlink.LinkList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, link := range links {
|
||||
ifName := link.Attrs().Name
|
||||
if strings.HasPrefix(ifName, prefix) {
|
||||
weavenet.ConfigureARPCache(ifName)
|
||||
if addrs, err := netlink.AddrList(link, netlink.FAMILY_V4); err == nil {
|
||||
for _, addr := range addrs {
|
||||
arping.GratuitousArpOverIfaceByName(addr.IPNet.IP, ifName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -10,11 +10,12 @@ import (
|
||||
)
|
||||
|
||||
func createPluginNetwork(args []string) error {
|
||||
if len(args) != 2 {
|
||||
cmdUsage("create-plugin-network", "<network-name> <driver-name>")
|
||||
if len(args) != 3 {
|
||||
cmdUsage("create-plugin-network", "<network-name> <driver-name> <default-subnet>")
|
||||
}
|
||||
networkName := args[0]
|
||||
driverName := args[1]
|
||||
subnet := args[2]
|
||||
d, err := newDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -24,8 +25,11 @@ func createPluginNetwork(args []string) error {
|
||||
Name: networkName,
|
||||
CheckDuplicate: true,
|
||||
Driver: driverName,
|
||||
IPAM: docker.IPAMOptions{Driver: driverName},
|
||||
Options: map[string]interface{}{plugin.MulticastOption: "true"},
|
||||
IPAM: docker.IPAMOptions{
|
||||
Driver: driverName,
|
||||
Config: []docker.IPAMConfig{{Subnet: subnet}},
|
||||
},
|
||||
Options: map[string]interface{}{plugin.MulticastOption: "true"},
|
||||
})
|
||||
if err != docker.ErrNetworkAlreadyExists && err != nil {
|
||||
// Despite appearances to the contrary, CreateNetwork does
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
|
||||
func processAddrs(args []string) error {
|
||||
if len(args) < 1 {
|
||||
cmdUsage("process-addrs", "<bridgeName>")
|
||||
}
|
||||
bridgeName := args[0]
|
||||
|
||||
peerIDs, err := weavenet.ConnectedToBridgeVethPeerIds(bridgeName)
|
||||
if err != nil {
|
||||
if err == weavenet.ErrLinkNotFound {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
pids, err := common.AllPids("/proc")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pid := range pids {
|
||||
netDevs, err := weavenet.GetNetDevsByVethPeerIds(pid, peerIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printNetDevs(strconv.Itoa(pid), netDevs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+4
-5
@@ -17,7 +17,6 @@ import (
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
weaveapi "github.com/weaveworks/weave/api"
|
||||
"github.com/weaveworks/weave/common"
|
||||
weavedocker "github.com/weaveworks/weave/common/docker"
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
@@ -149,14 +148,14 @@ func NewProxy(c Config) (*Proxy, error) {
|
||||
p.client = client.Client
|
||||
|
||||
if !p.WithoutDNS {
|
||||
netDevs, err := common.GetBridgeNetDev(c.DockerBridge)
|
||||
netDev, err := weavenet.GetBridgeNetDev(c.DockerBridge)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(netDevs) != 1 || len(netDevs[0].CIDRs) != 1 {
|
||||
if len(netDev.CIDRs) != 1 {
|
||||
return nil, fmt.Errorf("Could not obtain address of %s", c.DockerBridge)
|
||||
}
|
||||
p.dockerBridgeIP = netDevs[0].CIDRs[0].IP.String()
|
||||
p.dockerBridgeIP = netDev.CIDRs[0].IP.String()
|
||||
}
|
||||
|
||||
p.hostnameMatchRegexp, err = regexp.Compile(c.HostnameMatch)
|
||||
@@ -625,7 +624,7 @@ func (proxy *Proxy) updateContainerNetworkSettings(container jsonObject) error {
|
||||
if err := proxy.waitForStartByIdent(containerID); err != nil {
|
||||
return err
|
||||
}
|
||||
netDevs, err := common.GetWeaveNetDevs(pid)
|
||||
netDevs, err := weavenet.GetWeaveNetDevs(pid)
|
||||
if err != nil || len(netDevs) == 0 || len(netDevs[0].CIDRs) == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
+44
-6
@@ -9,6 +9,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/weaveworks/go-odp/odp"
|
||||
"github.com/weaveworks/mesh"
|
||||
)
|
||||
@@ -267,9 +268,11 @@ func (fastdp *FastDatapath) bridge(ingress bridgePortID, key PacketKey, lock *fa
|
||||
// If we did, we'd need to delete the flows every time
|
||||
// we learned a new MAC address, or have a more
|
||||
// complicated selective invalidation scheme.
|
||||
log.Debug("fastdp: unknown dst", ingress, key)
|
||||
mfop.Add(vetoFlowCreationFlowOp{})
|
||||
} else {
|
||||
// A real broadcast
|
||||
log.Debug("fastdp: broadcast", ingress, key)
|
||||
mfop.Add(odpEthernetFlowKey(key))
|
||||
}
|
||||
|
||||
@@ -315,6 +318,11 @@ func (fastDatapathOverlay) AddFeaturesTo(features map[string]string) {
|
||||
// OverlaySwitch.
|
||||
}
|
||||
|
||||
type FastDPStatus struct {
|
||||
Vports []VportStatus
|
||||
Flows []FlowStatus
|
||||
}
|
||||
|
||||
type FlowStatus odp.FlowInfo
|
||||
|
||||
func (flowStatus *FlowStatus) MarshalJSON() ([]byte, error) {
|
||||
@@ -371,15 +379,28 @@ func (fastdp fastDatapathOverlay) Diagnostics() interface{} {
|
||||
flowStatuses = append(flowStatuses, FlowStatus(flow))
|
||||
}
|
||||
|
||||
return struct {
|
||||
Vports []VportStatus
|
||||
Flows []FlowStatus
|
||||
}{
|
||||
return FastDPStatus{
|
||||
vportStatuses,
|
||||
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 {
|
||||
fastdp.lock.Lock()
|
||||
defer fastdp.lock.Unlock()
|
||||
@@ -416,14 +437,31 @@ func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
|
||||
return vxlanVportID, nil
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("vxlan-%d", udpPort)
|
||||
vxlanVportID, err := fastdp.dp.CreateVport(
|
||||
odp.NewVxlanVportSpec(fmt.Sprintf("vxlan-%d", udpPort), uint16(udpPort)))
|
||||
odp.NewVxlanVportSpec(name, uint16(udpPort)))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// If a netdev for the vxlan vport exists, we need to do an extra check
|
||||
// to bypass the kernel bug which makes the vxlan creation to complete
|
||||
// successfully regardless whether there were any errors when binding
|
||||
// to the given UDP port.
|
||||
if link, err := netlink.LinkByName(name); err == nil {
|
||||
if link.Attrs().Flags&net.FlagUp == 0 {
|
||||
// The netdev interface is down, so most likely bringing it up
|
||||
// has failed due to the UDP port being in use.
|
||||
if err := fastdp.dp.DeleteVport(vxlanVportID); err != nil {
|
||||
log.Warning("Unable to remove vxlan vport %d: %s", vxlanVportID, err)
|
||||
}
|
||||
return 0, odp.NetlinkError(syscall.EADDRINUSE)
|
||||
}
|
||||
}
|
||||
|
||||
fastdp.vxlanVportIDs[udpPort] = vxlanVportID
|
||||
fastdp.missHandlers[vxlanVportID] = func(fks odp.FlowKeys, lock *fastDatapathLock) FlowOp {
|
||||
log.Debug("ODP miss: ", fks, " on port ", vxlanVportID)
|
||||
tunnel := fks[odp.OVS_KEY_ATTR_TUNNEL].(odp.TunnelFlowKey)
|
||||
tunKey := tunnel.Key()
|
||||
|
||||
@@ -939,7 +977,6 @@ func (fastdp *FastDatapath) Error(err error, stopped bool) {
|
||||
|
||||
func (fastdp *FastDatapath) Miss(packet []byte, fks odp.FlowKeys) error {
|
||||
ingress := fks[odp.OVS_KEY_ATTR_IN_PORT].(odp.InPortFlowKey).VportID()
|
||||
log.Debug("ODP miss ", fks, " on port ", ingress)
|
||||
|
||||
lock := fastdp.startLock()
|
||||
defer lock.unlock()
|
||||
@@ -948,6 +985,7 @@ func (fastdp *FastDatapath) Miss(packet []byte, fks odp.FlowKeys) error {
|
||||
|
||||
handler := fastdp.getMissHandler(ingress)
|
||||
if handler == nil {
|
||||
log.Debug("ODP miss (no handler): ", fks, " on port ", ingress)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -82,17 +82,17 @@ func (router *NetworkRouter) Start() {
|
||||
func (router *NetworkRouter) handleCapturedPacket(key PacketKey) FlowOp {
|
||||
router.PacketLogging.LogPacket("Captured", key)
|
||||
srcMac := net.HardwareAddr(key.SrcMAC[:])
|
||||
dstMac := net.HardwareAddr(key.DstMAC[:])
|
||||
|
||||
switch newSrcMac, conflictPeer := router.Macs.Add(srcMac, router.Ourself.Peer); {
|
||||
case newSrcMac:
|
||||
log.Println("Discovered local MAC", srcMac)
|
||||
|
||||
case conflictPeer != nil:
|
||||
// The MAC cache has an entry for the source MAC
|
||||
// associated with another peer. This probably means
|
||||
// we are seeing a frame we injected ourself. That
|
||||
// shouldn't happen, but discard it just in case.
|
||||
log.Error("Captured frame from MAC (", srcMac, ") associated with another peer ", conflictPeer)
|
||||
log.Error("Captured frame from MAC (", srcMac, ") to (", dstMac, ") associated with another peer ", conflictPeer)
|
||||
return DiscardingFlowOp{}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,6 @@ func (router *NetworkRouter) handleCapturedPacket(key PacketKey) FlowOp {
|
||||
return DiscardingFlowOp{}
|
||||
}
|
||||
|
||||
dstMac := net.HardwareAddr(key.DstMAC[:])
|
||||
switch dstPeer := router.Macs.Lookup(dstMac); dstPeer {
|
||||
case router.Ourself.Peer:
|
||||
// The packet is destined for a local MAC. The bridge
|
||||
@@ -240,12 +239,16 @@ func (router *NetworkRouter) InitialPeers(resume bool, peers []string) ([]string
|
||||
return storedPeers, nil
|
||||
}
|
||||
|
||||
sentinel, err := os.Create("restart.sentinel")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating sentinel: %v", err)
|
||||
}
|
||||
sentinel.Close()
|
||||
|
||||
log.Println("Launch detected - using supplied peer list:", peers)
|
||||
return peers, nil
|
||||
}
|
||||
|
||||
func (router *NetworkRouter) CreateRestartSentinel() error {
|
||||
sentinel, err := os.Create("restart.sentinel")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating sentinel: %v", err)
|
||||
}
|
||||
sentinel.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
+63
-28
@@ -1,18 +1,24 @@
|
||||
---
|
||||
title: Integrating Kubernetes via the CNI Plugin
|
||||
title: Integrating Kubernetes and Mesos via the CNI Plugin
|
||||
menu_order: 65
|
||||
---
|
||||
|
||||
CNI, the [_Container Network Interface_](https://github.com/appc/cni#cni---the-container-network-interface),
|
||||
> 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),
|
||||
is a proposed standard for configuring network interfaces for Linux
|
||||
application containers. CNI is supported by
|
||||
[Kubernetes](http://kubernetes.io/), an open source container cluster
|
||||
manager built by Google.
|
||||
[Kubernetes](http://kubernetes.io/), [Apache Mesos](mesos.apache.org)
|
||||
and
|
||||
[others](https://github.com/containernetworking/cni#who-is-using-cni).
|
||||
|
||||
###Installing the Weave Net CNI plugin
|
||||
|
||||
The Weave Net CNI plugin is installed when you run `weave setup`, if
|
||||
your machine has the directories normally used to host CNI plugins.
|
||||
If your machine has the directories normally used to host CNI plugins,
|
||||
then the Weave Net CNI plugin is installed when you run `weave setup`.
|
||||
|
||||
To create those directories, run (as root):
|
||||
|
||||
@@ -23,20 +29,44 @@ Then run:
|
||||
|
||||
weave setup
|
||||
|
||||
###Launching Weave Net
|
||||
####Launching Weave Net
|
||||
|
||||
To create a network which can span multiple hosts, the Weave peers must be connected to each other, by specifying the other hosts during `weave launch` or via
|
||||
To create a network that spans multiple hosts, the Weave peers must be connected to each other.
|
||||
This is accomplished by specifying the other hosts during `weave launch` or via
|
||||
[`weave connect`](/site/using-weave/finding-adding-hosts-dynamically.md).
|
||||
|
||||
See [Using Weave Net](/site/using-weave.md#peer-connections) for a discussion on peer connections.
|
||||
|
||||
As well as launching Weave Net, you have to run an extra command to
|
||||
perform some additional configuration of the Weave bridge:
|
||||
See [Creating Peer Connections Between Hosts](/site/using-weave.md#peer-connections)
|
||||
for a discussion on peer connections.
|
||||
|
||||
weave launch <peer hosts>
|
||||
weave expose
|
||||
|
||||
###Configuring Kubernetes to use the CNI Plugin
|
||||
####Using the CNI network configuration file
|
||||
|
||||
All CNI plugins are configured by a JSON file in the directory
|
||||
`/etc/cni/net.d/`. `weave setup` installs a minimal configuration
|
||||
file named `10-weave.conf`, which you can alter to suit your needs.
|
||||
|
||||
See the [CNI Spec](https://github.com/appc/cni/blob/master/SPEC.md#network-configuration)
|
||||
for details on the format and contents of this file.
|
||||
|
||||
By default, the Weave CNI plugin adds a default route out via an IP
|
||||
address on the Weave bridge, so your containers can access resources
|
||||
on the internet. If you do not want this, add a section to the config
|
||||
file that specifies no routes:
|
||||
|
||||
"ipam": {
|
||||
"routes": [ ]
|
||||
}
|
||||
|
||||
The following other fields in the spec are supported:
|
||||
|
||||
- `ipam / type` - default is to use Weave's own IPAM
|
||||
- `ipam / subnet` - default is to use Weave's IPAM default subnet
|
||||
- `ipam / gateway` - default is to use the Weave bridge IP address (allocated by `weave expose`)
|
||||
|
||||
###Using the Weave Net CNI plugin
|
||||
|
||||
####Configuring Kubernetes to use the CNI Plugin
|
||||
|
||||
After you've launched Weave and peered your hosts, you can configure
|
||||
Kubernetes to use Weave, by adding the following options to the
|
||||
@@ -49,26 +79,31 @@ for more details.
|
||||
|
||||
Now, whenever Kubernetes starts a pod, it will be attached to the Weave network.
|
||||
|
||||
###Using the CNI network configuration file
|
||||
####Configuring Mesos to use the CNI plugin
|
||||
|
||||
All CNI plugins are configured by a JSON file in the directory
|
||||
`/etc/cni/net.d/`. `weave setup` installs a minimal configuration
|
||||
file named `10-weave.conf`, which you can alter to suit your needs.
|
||||
To use the CNI plugin, the Mesos Agent must be started with reference
|
||||
to the CNI configuration and binary directories:
|
||||
|
||||
See the [CNI Spec](https://github.com/appc/cni/blob/master/SPEC.md#network-configuration)
|
||||
for details on the format and contents of this file.
|
||||
sudo mesos-slave
|
||||
--network_cni_config_dir=/etc/cni/net.d
|
||||
--network_cni_plugins_dir=/opt/cni/bin
|
||||
...
|
||||
|
||||
By default, the Weave CNI plugin adds a default route out via the Weave bridge, so your containers can access resources on the internet. If you do not want this, add a section to the config file that specifies no routes:
|
||||
To start a container that is connected to the Weave network via CNI,
|
||||
use the name specified in the configuration file. This example starts
|
||||
a alpine container running a `nc` server listening on port 1080 with
|
||||
the `mesos-execute` command. From the Master, run:
|
||||
|
||||
"ipam": {
|
||||
"routes": [ ]
|
||||
}
|
||||
nohup sudo mesos-execute --command="ifconfig; nc -k -l 0.0.0.0 1080" --docker_image=alpine --master=localhost:5050 --name=example --networks=weave --resources=cpus:0.1 --shell </dev/null >test.log 2>&1 &
|
||||
|
||||
The following other fields in the spec are supported:
|
||||
After this task has started, it is possible to obtain the ip address of
|
||||
the container and ping it from any of other agents (which are also
|
||||
connected to the weave network)"
|
||||
|
||||
- `ipam / type` - default is to use Weave's own IPAM
|
||||
- `ipam / subnet` - default is to use Weave's IPAM default subnet
|
||||
- `ipam / gateway` - default is to use the Weave bridge IP address (allocated by `weave expose`)
|
||||
nc -z -v <IP FROM LOGS> 1080
|
||||
|
||||
For more information, see the
|
||||
[Mesos documentation](http://mesos.apache.org/documentation/cni/).
|
||||
|
||||
###Caveats
|
||||
|
||||
|
||||
+11
-10
@@ -11,6 +11,7 @@ menu_order: 20
|
||||
* [Address Allocation (IPAM)](#addressing)
|
||||
* [Naming and Discovery](#naming-and-discovery)
|
||||
* [Application Isolation](#application-isolation)
|
||||
* [Network Policy](#network-policy)
|
||||
* [Dynamic Network Attachment](#dynamic-network-attachment)
|
||||
* [Security](#security)
|
||||
* [Host Network Integration](#host-network-integration)
|
||||
@@ -73,7 +74,7 @@ Containers started in this way that subsequently restart, either
|
||||
by an explicit `docker restart` command or by Docker restart
|
||||
policy, are re-attached to the Weave network by the `Weave Docker API Proxy`.
|
||||
|
||||
See [Using the Weave Docker API Proxy](/site/weave-docker-api/using-proxy.md).
|
||||
See [Integrating Docker via the API Proxy](/site/weave-docker-api.md).
|
||||
|
||||
|
||||
###<a name="plugin"></a>Weave Network Docker Plugin
|
||||
@@ -88,19 +89,14 @@ Using the Weave plugin enables you to take advantage of [Docker's network functi
|
||||
Also, Weave’s Docker Network plugin doesn't require an external cluster store and you can start and stop containers even
|
||||
when there are network connectivity problems.
|
||||
|
||||
|
||||
|
||||
>*Note:* The plugin is an *alternative* to the proxy, and therefore you do
|
||||
*not* need to run `eval $(weave env)` beforehand.
|
||||
|
||||
See [Using the Weave Docker Network Plugin](/site/plugin.md) for more details.
|
||||
See [Integrating Docker via the Network Plugin](/site/plugin.md) for more details.
|
||||
|
||||
|
||||
###<a name="cniplugin"></a>Weave Network CNI Plugin
|
||||
|
||||
Weave can be used as a plugin to systems that support the [Container Network Interface](https://github.com/appc/cni), such as Kubernetes.
|
||||
Weave can be used as a plugin to systems that support the [Container Network Interface](https://github.com/appc/cni), such as Kubernetes and Mesosphere.
|
||||
|
||||
See [Using the Weave CNI Plugin](/site/cni-plugin.md) for more details.
|
||||
See [Integrating Kubernetes and Mesos via the CNI Plugin](/site/cni-plugin.md) for more details.
|
||||
|
||||
|
||||
###<a name="addressing"></a>IP Address Management (IPAM)
|
||||
@@ -125,7 +121,7 @@ and are discoverable by using standard, simple name lookups:
|
||||
|
||||
WeaveDNS also supports [load balancing](/site/weavedns/load-balance-fault-weavedns.md), [fault resilience](/site/weavedns/load-balance-fault-weavedns.md) and [hot swapping](/site/weavedns/managing-entries-weavedns.md).
|
||||
|
||||
See [Naming and discovery with WeaveDNS](/site/weavedns/how-works-weavedns.md).
|
||||
See [Discovering Containers with WeaveDNS](/site/weavedns.md).
|
||||
|
||||
###<a name="application-isolation"></a>Application Isolation
|
||||
|
||||
@@ -144,6 +140,11 @@ See [Isolating Applications](/site/using-weave/application-isolation.md)
|
||||
for information on how to use the isolation-through-subnets
|
||||
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
|
||||
|
||||
|
||||
+1
-1
@@ -202,4 +202,4 @@ named `weavedb` is used to store this data.
|
||||
**See Also**
|
||||
|
||||
* [Automatic Allocation Across Multiple Subnets](/site/ipam/allocation-multi-ipam.md)
|
||||
* [Plugin Command-line Arguments](/site/plugin/plug-in-command-line.md)
|
||||
* [Integrating Docker via the Network Plugin](/site/plugin.md)
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: Integrating Kubernetes via the Addon
|
||||
menu_order: 63
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
If you're using Kubernetes 1.4 or later, and it is configured to use
|
||||
CNI, it is possible to install Weave Net on your cluster with a single
|
||||
command:
|
||||
|
||||
```
|
||||
kubectl create -f https://git.io/weave-kube
|
||||
```
|
||||
|
||||
After a few seconds, you should have a Weave Net pod running on each
|
||||
node, and any further pods you create will be attached to the Weave
|
||||
network.
|
||||
|
||||
> 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 if you are using the older cluster set-up scripts from
|
||||
> the Kubernetes repo, you can use
|
||||
>
|
||||
> ```
|
||||
> NETWORK_PROVIDER=cni cluster/kube-up.sh
|
||||
> ```
|
||||
|
||||
NB If you were previously using the [Weave CNI
|
||||
driver](/site/cni-plugin.md) from a full install of Weave Net with your
|
||||
cluster, you will need to uninstall it first. Shut down Kubernetes,
|
||||
then perform the following _on all nodes_:
|
||||
|
||||
* `weave reset`
|
||||
* Remove any separate provision you have made to run weave at
|
||||
boot-time, e.g. `systemd` units
|
||||
* `rm /opt/cni/bin/weave-*`
|
||||
|
||||
You can 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 addon YAML for the latest release of the Weave Net addon;
|
||||
historic versions will be archived on our [GitHub release
|
||||
page](https://github.com/weaveworks/weave/releases).
|
||||
|
||||
## Upgrading
|
||||
|
||||
Kubernetes does not currently support rolling upgrades of daemon sets,
|
||||
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 in turn with kubectl delete; wait for the
|
||||
replacement to begin running before moving on to the next.
|
||||
|
||||
##<a name="npc"></a>Network Policy Controller
|
||||
|
||||
The addon 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).
|
||||
|
||||
## Changing Configuration Options
|
||||
|
||||
You can change the default configuration by saving and editing the
|
||||
addon YAML before you `kubectl apply`. Additional arguments can be
|
||||
supplied to the Weave router process by adding them to the `command:`
|
||||
array in the yaml file.
|
||||
+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).
|
||||
@@ -1,5 +1,32 @@
|
||||
---
|
||||
title: Operational Guide
|
||||
menu_order: 35
|
||||
menu_order: 45
|
||||
---
|
||||
Operations manual.
|
||||
This operational guide is intended to give you an overview of how to
|
||||
operate and manage a Weave Network in production. It consists of three
|
||||
main parts:
|
||||
|
||||
* A [glossary of concepts](/site/operational-guide/concepts.md) with
|
||||
which you will need to be familiar
|
||||
* Detailed instructions for safely bootstrapping, growing and
|
||||
shrinking Weave networks in a number of different deployment
|
||||
scenarios:
|
||||
* An [interactive
|
||||
installation](/site/operational-guide/interactive.md), suitable
|
||||
for evaluation and development
|
||||
* A [uniformly configured
|
||||
cluster](/site/operational-guide/uniform-fixed-cluster.md) with
|
||||
a fixed number of initial nodes, suitable for automated
|
||||
provisioning but requiring manual intervention for resizing
|
||||
* A [heterogenous cluster](/site/operational-guide/autoscaling.md)
|
||||
comprising fixed and autoscaling components, suitable for a base
|
||||
load with automated scale-out/scale-in
|
||||
* A [uniformly configured
|
||||
cluster](/site/operational-guide/uniform-dynamic-cluster.md)
|
||||
with dynamic nodes, suitable for automated provisioning and
|
||||
resizing.
|
||||
* A list of [common administrative
|
||||
tasks](/site/operational-guide/tasks.md), such as configuring Weave
|
||||
Net to start on boot, upgrading clusters, cleaning up peers and
|
||||
reclaiming IP address space
|
||||
|
||||
@@ -6,18 +6,30 @@ This section describes some of the essential concepts with which you will
|
||||
need to be familiar before continuing to the example deployment
|
||||
scenarios.
|
||||
|
||||
## Host
|
||||
The following concepts are described:
|
||||
|
||||
* [Host](#host)
|
||||
* [Peer](#peer)
|
||||
* [Peer Discovery](#peer-discovery)
|
||||
* [Network Partition](#network-partition)
|
||||
* [IP Address Manager- IPAM](#ip-address-manager)
|
||||
* [Seeding](#seeding)
|
||||
* [Consensus](#consensus)
|
||||
* [Observers](#observers)
|
||||
* [Persistence](#persistence)
|
||||
|
||||
##<a name="host"></a>Host
|
||||
|
||||
For the purposes of this documentation a host is an
|
||||
installation of the Linux operating system that is running an
|
||||
instance of the Docker Engine. The host may be executing directly on bare
|
||||
hardware or inside a virtual machine.
|
||||
|
||||
## Peer
|
||||
##<a name="peer"></a>Peer
|
||||
|
||||
A peer is a running instance of Weave Net, typically one per host.
|
||||
|
||||
## Peer Name
|
||||
##<a name="peer-name"></a>Peer Name
|
||||
|
||||
Weave Net peers are identified by a 48-bit value formatted like an
|
||||
ethernet MAC address, for example, `01:23:45:67:89:ab`. The 'peer
|
||||
@@ -50,14 +62,14 @@ The appropriate strategy for assigning peer names depends on the type
|
||||
and method of your particular deployment and is discussed in more
|
||||
detail below.
|
||||
|
||||
## Peer Discovery
|
||||
##<a name="peer-discovery"></a>Peer Discovery
|
||||
|
||||
Peer discovery is a mechanism that allows peers to learn about new
|
||||
Weave Net hosts from existing peers without being explicitly told. Peer
|
||||
discovery is
|
||||
[enabled by default](/site/using-weave/finding-adding-hosts-dynamically.md).
|
||||
|
||||
## Network Partition
|
||||
##<a name="network-partition"></a>Network Partition
|
||||
|
||||
A network partition is a transient condition whereby some arbitrary
|
||||
subsets of peers are unable to communicate with each other for the
|
||||
@@ -66,7 +78,7 @@ optic line severed. Weave Net is designed to allow peers and their
|
||||
containers to make maximum safe progress under conditions of
|
||||
partition, healing automatically once the partition is over.
|
||||
|
||||
## IP Address Manager (IPAM)
|
||||
##<a name="ip-address-manager"></a>IP Address Manager (IPAM)
|
||||
|
||||
[IPAM](/site/ipam.md) is the subsystem responsible for dividing up a
|
||||
large contiguous block of IP addresses (known as the IP allocation
|
||||
@@ -77,7 +89,7 @@ When a new network is formed an initial division of the IP allocation
|
||||
range must be made. Two (mutually exclusive) mechanisms with different
|
||||
tradeoffs are provided to perform this task: seeding and consensus.
|
||||
|
||||
### Seeding
|
||||
### <a name="seeding"></a>Seeding
|
||||
|
||||
Seeding requires each peer to be told the list of peer names amongst
|
||||
which the address space is to be divided initially. There are some
|
||||
@@ -100,7 +112,7 @@ scenarios:
|
||||
|
||||
* [Uniform Dynamic Cluster](/site/operational-guide/uniform-dynamic-cluster.md)
|
||||
|
||||
### Consensus
|
||||
###<a name="consensus"></a>Consensus
|
||||
|
||||
Alternatively, when a new network is formed for the first time, peers
|
||||
can be configured to co-ordinate amongst themselves to automatically
|
||||
@@ -116,7 +128,7 @@ scenarios:
|
||||
* [Interactive Deployment](/site/operational-guide/interactive.md)
|
||||
* [Uniform Fixed Cluster](/site/operational-guide/uniform-fixed-cluster.md)
|
||||
|
||||
### Observers
|
||||
###<a name="observers"></a>Observers
|
||||
|
||||
Finally, an option is provided to start a peer as an _observer_. Such
|
||||
peers do not require a seed peer name list or an initial peer
|
||||
@@ -130,7 +142,7 @@ scenarios:
|
||||
|
||||
* [Autoscaling](/site/operational-guide/autoscaling.md)
|
||||
|
||||
## Persistence
|
||||
##<a name="persistence"></a>Persistence
|
||||
|
||||
Certain information is remembered between launches of Weave Net (for
|
||||
example across reboots):
|
||||
|
||||
@@ -20,7 +20,7 @@ once and your system is configured to start Docker on boot, then Weave Net
|
||||
will start automatically on system restarts.
|
||||
|
||||
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.
|
||||
|
||||
##<a name="detect-reclaim-ipam"></a>Detecting and Reclaiming Lost IP Address Space
|
||||
@@ -52,7 +52,7 @@ following command on any peer:
|
||||
weave status ipam
|
||||
|
||||
This command displays the peer names and nicknames, absolute quantity/percentage of allocation
|
||||
range managed by peer and also identifies the names of unreachable peers. If you are satisifed
|
||||
range managed by peer and also identifies the names of unreachable peers. If you are satisfied
|
||||
that the peer is truly gone, rather than temporarily unreachable due to a
|
||||
partition, you can reclaim their space manually.
|
||||
|
||||
|
||||
@@ -46,14 +46,14 @@ Where
|
||||
On each new peer, at boot, via
|
||||
[systemd](/site/installing-weave/systemd.md):
|
||||
|
||||
hostN$ weave launch --no-restart --name ::N --ipam-seed $SEED $PEERS
|
||||
hostN$ weave launch --no-restart --name ::N --ipalloc-init seed=$SEED $PEERS
|
||||
|
||||
Where,
|
||||
|
||||
* `--no-restart` disables the Docker restart policy, since this will be
|
||||
handled by systemd.
|
||||
* `--name` specifies a unique name for this new peer.
|
||||
* `--ipam-seed` specifies the names of only those peers that were
|
||||
* `--ipalloc-init seed` specifies the names of only those peers that were
|
||||
involved in the initial cluster bootstrap - even if they have been
|
||||
subsequently removed from the cluster. You can view this as a kind
|
||||
of 'cluster identity',where peers may only interoperate in the same
|
||||
|
||||
@@ -25,8 +25,6 @@ After you've launched Weave Net and peered your hosts, you can start containers
|
||||
on any of the hosts, and they can all communicate with each other
|
||||
using any protocol, even multicast.
|
||||
|
||||
>**Warning!** It is inadvisable to attach containers to the Weave network using the Weave Docker Networking Plugin and Weave Docker API Proxy simultaneously. Such containers will end up with two Weave network interfaces and two IP addresses, which is rarely desirable. To ensure that the proxy is not being used, do not run `eval $(weave env)`, or `docker $(weave config) ...`.
|
||||
|
||||
In order to use Weave Net's [Service Discovery](/site/weavedns.md) you
|
||||
must pass the additional arguments `--dns` and `-dns-search`, for
|
||||
which a helper is provided in the Weave script:
|
||||
|
||||
@@ -38,6 +38,5 @@ The plugin accepts the following options via `docker network create ... --opt`:
|
||||
|
||||
**See Also**
|
||||
|
||||
* [Using the Weave Net Docker Network Plugin](/site/plugin.md)
|
||||
* [Plugin Command-line Arguments](/site/plugin/plug-in-command-line.md)
|
||||
|
||||
* [Integrating Docker via the Network Plugin](/site/plugin.md)
|
||||
|
||||
+31
-6
@@ -29,12 +29,26 @@ Where,
|
||||
* The second line configures the Weave Net environment, so that containers launched via the Docker command line are automatically attached to the Weave network, and,
|
||||
* The third line runs the [application container](/site/faq.md#own-image) using [a Docker command](https://docs.docker.com/engine/reference/run/).
|
||||
|
||||
>**Note:** If the first command results in an error like
|
||||
`http:///var/run/Docker.sock/v1.19/containers/create: dial unix
|
||||
/var/run/Docker.sock: permission denied. Are you trying to connect to a TLS-enabled daemon without TLS?` then you likely need to be 'root' in order to connect to the Docker daemon. If so, run the above and all subsequent commands in a *single* root shell (e.g. one created with `sudo -s`). Do *not* prefix individual commands with `sudo`, since some commands modify environment entries and hence they all need to be executed from the same shell.
|
||||
|
||||
|
||||
>**Important!** if you are running the Weave Docker Network Plugin do not run `eval $(weave env)`. See [Using the Weave Net Docker Network Plugin](/site/plugin.md) for more information.
|
||||
> **Note** If the first command results in an error like
|
||||
> ```
|
||||
> Cannot connect to the Docker daemon. Is the docker daemon running on this host?
|
||||
> ```
|
||||
> or
|
||||
> ```
|
||||
> http:///var/run/docker.sock/v1.19/containers/create: dial unix/var/run/docker.sock: permission denied. Are you trying to connect to a TLS-enabled daemon without TLS?
|
||||
> ```
|
||||
> then you likely need to be 'root' in order to connect to the Docker
|
||||
> daemon. If so, run the above and all subsequent commands in a
|
||||
> *single* root shell:
|
||||
> ```
|
||||
> host1$ sudo -s
|
||||
> host1# weave launch
|
||||
> host1# eval $(weave env)
|
||||
> host1# docker run --name a1 -ti weaveworks/ubuntu
|
||||
> ```
|
||||
> Do *not* prefix individual commands with `sudo`, since some commands
|
||||
> modify environment entries and hence they all need to be executed from
|
||||
> the same shell.
|
||||
|
||||
Weave Net must be launched once per host. The relevant container images will be pulled down from Docker Hub on demand during `weave launch`.
|
||||
|
||||
@@ -69,6 +83,17 @@ For example:
|
||||
|
||||
Peers can also be dynamically added. See [Adding Hosts Dynamically](/site/using-weave/finding-adding-hosts-dynamically.md) for more information.
|
||||
|
||||
####Restricting Access
|
||||
|
||||
By default Weave Net listens on all host IPs (i.e. 0.0.0.0). This
|
||||
can be altered with the `--host` parameter to `weave launch`, for example,
|
||||
to ensure that Weave Net only listens on IPs on an internal network.
|
||||
|
||||
Standard firewall rules can be deployed to restrict access to the
|
||||
Weave Net control and data ports.
|
||||
|
||||
For communication across untrusted networks, connections can be
|
||||
[encrypted](/site/using-weave/security-untrusted-networks.md).
|
||||
|
||||
###<a name="testing"></a>Testing Container Communications
|
||||
|
||||
|
||||
@@ -101,6 +101,4 @@ In AWS-VPC mode you can run the following:
|
||||
**See Also**
|
||||
|
||||
* [Using Weave Net](/site/using-weave.md)
|
||||
* [Performance measurements](/blog/weave-docker-networking-performance-aws-vpc/)
|
||||
|
||||
(that last blog post doesn't exist yet)
|
||||
@@ -28,13 +28,23 @@ To guard against dictionary attacks, the password needs to be reasonably strong
|
||||
|
||||
The same password must be specified for all Weave Net peers, by default both control and data plane traffic will then use authenticated encryption.
|
||||
|
||||
Fast datapath does not support encryption. If you supply a
|
||||
password at `weave launch` the router falls back to a slower
|
||||
`sleeve` mode that does support encryption.
|
||||
[Fast datapath](/site/using-weave/fastdp.md) does not support
|
||||
encryption. If you supply a password at `weave launch`, Weave Net
|
||||
falls back to the slower `sleeve` mode for encrypted communication.
|
||||
|
||||
If some of your peers are co-located in a trusted network (for example within the boundary of your own data center) you can use the `--trusted-subnets` argument to `weave launch` to selectively disable data plane encryption as an optimization.
|
||||
As an optimization, you can selectively disable data plane encryption
|
||||
if some of your peers are co-located in a trusted network, for example
|
||||
within the boundary of your own data center. List these networks using
|
||||
the `--trusted-subnets` argument with `weave launch`:
|
||||
|
||||
>**Note:** Both peers must consider the other to be in a trusted subnet for this to take place - if they do not agree, Weave Net [falls back to a slower method](/site/using-weave/fastdp.md) for transporting data between peers, since fast datapath does not support encryption.
|
||||
weave launch --password wfvAwt7sj --trusted-subnets 10.0.2.0/24,192.168.48.0/24
|
||||
|
||||
If *both* peers at the end of a connection consider the other to be in
|
||||
a trusted subnet, Weave Net attempts to establish fast datapath
|
||||
connectivity, which is unencrypted. Otherwise the slower `sleeve` mode
|
||||
is used and communication is encrypted.
|
||||
|
||||
Configured trusted subnets are shown in [`weave status`](/site/troubleshooting.md#weave-status).
|
||||
|
||||
Be aware that:
|
||||
|
||||
|
||||
@@ -3,15 +3,38 @@ title: Integrating Docker via the API Proxy
|
||||
menu_order: 50
|
||||
---
|
||||
|
||||
|
||||
The Docker API proxy automatically attaches containers to the Weave
|
||||
network when they are started using the ordinary Docker
|
||||
[command-line interface](https://docs.docker.com/reference/commandline/cli/)
|
||||
or the [remote API](https://docs.docker.com/reference/api/docker_remote_api/),
|
||||
instead of `weave run`.
|
||||
|
||||
###<a name="attaching-containers"></a>Attaching Containers to a Weave Network
|
||||
|
||||
###Setting Up The Weave Net Docker API Proxy
|
||||
There are three ways to attach containers to a Weave network (which method to use is
|
||||
entirely up to you):
|
||||
|
||||
**1.** The Weave Net Docker API Proxy. See [Setting Up the Weave Net Docker API Proxy](#weave-api-proxy).
|
||||
|
||||
**2.** The Docker Network Plugin framework. The Docker Network Plugin is used when
|
||||
Docker containers are started with the --net flag, for example:
|
||||
|
||||
`docker run --net <docker-run-options>`
|
||||
|
||||
**Where,**
|
||||
|
||||
* `<docker-run-options>` are the [docker run options](https://docs.docker.com/engine/reference/run/)
|
||||
you give to your container on start
|
||||
|
||||
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.
|
||||
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
|
||||
does not use the Weave Docker API Proxy.
|
||||
See [Launching Containers With Weave Run (without the Proxy)](/site/weave-docker-api/launching-without-proxy.md).
|
||||
|
||||
###<a name="weave-api-proxy"></a>Setting Up The Weave Net Docker API Proxy
|
||||
|
||||
The proxy sits between the Docker client (command line or API) and the
|
||||
Docker daemon, and intercepts the communication between the two. You can
|
||||
|
||||
+1
-1
@@ -44,5 +44,5 @@ disable weaveDNS itself, launch weave with the `--no-dns` option.
|
||||
|
||||
**See Also**
|
||||
|
||||
* [How Weave Finds Containers](/site/weave-docker-api.md)
|
||||
* [Integrating Docker via the API Proxy](/site/weave-docker-api.md)
|
||||
* [Load Balancing and Fault Resilience with WeaveDNS](/site/weavedns/load-balance-fault-weavedns.md)
|
||||
@@ -17,18 +17,6 @@ When weaveDNS is queried for a name in the `.weave.local` domain, it
|
||||
looks up the hostname in its memory database and responds with the IPs
|
||||
of all containers for that hostname across the entire cluster.
|
||||
|
||||
###Basic Load Balancing and Fault Tolerance
|
||||
|
||||
WeaveDNS returns IP addresses in random order to facilitate basic
|
||||
load balancing and failure tolerance. Most client side resolvers sort
|
||||
the returned addresses based on reachability, and place local addresses
|
||||
at the top of the list (see [RFC 3484](https://www.ietf.org/rfc/rfc3484.txt)).
|
||||
|
||||
For example, if there is container with the desired hostname on the local
|
||||
machine, the application receives that container's IP address.
|
||||
Otherwise, the application receives the IP address of a random
|
||||
container with the desired hostname.
|
||||
|
||||
When weaveDNS is queried for a name in a domain other than
|
||||
`.weave.local`, it queries the host's configured nameserver, which is
|
||||
the standard behaviour for Docker containers.
|
||||
|
||||
@@ -33,6 +33,14 @@ PING pingme.weave.local (10.32.0.2) 56(84) bytes of data.
|
||||
|
||||
Notice how the ping reaches different addresses.
|
||||
|
||||
However, due to
|
||||
[RFC 3484 address selection](https://tools.ietf.org/html/rfc3484#section-6)
|
||||
most DNS resolver libraries prefer certain addresses over others, to
|
||||
the point where in some circumstances the same address is always
|
||||
chosen. To avoid this behaviour, applications may want to perform
|
||||
their own address selection, e.g. by choosing a random entry from the
|
||||
result of
|
||||
[`getaddrinfo()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html).
|
||||
|
||||
## <a name="fault-resilience"></a>Fault Resilience
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ assert_raises "! check_restart $HOST1 weave"
|
||||
assert_raises "! check_restart $HOST1 weaveproxy"
|
||||
assert_raises "! check_restart $HOST1 weaveplugin"
|
||||
|
||||
# Relaunch the plugin to prevent the `weave stop` in `end_suite`
|
||||
# Relaunch to prevent the `weave stop` in `end_suite`
|
||||
# timing out trying to remove the plugin network
|
||||
weave_on $HOST1 launch-plugin
|
||||
weave_on $HOST1 launch --no-restart
|
||||
|
||||
end_suite
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
. ./config.sh
|
||||
|
||||
IMAGE="joffrey/docker-py:1.8.1"
|
||||
|
||||
docker_py_test() {
|
||||
SHARD=$1
|
||||
TOTAL_SHARDS=$2
|
||||
@@ -12,9 +14,9 @@ docker_py_test() {
|
||||
# Work round https://github.com/docker/docker-py/issues/852
|
||||
docker_on $HOST1 pull busybox:buildroot-2014.02 >/dev/null
|
||||
# Get a list of the tests for use to shard
|
||||
docker_on $HOST1 pull joffrey/docker-py >/dev/null
|
||||
docker_on $HOST1 pull $IMAGE >/dev/null
|
||||
CANDIDATES=$(docker_on $HOST1 run \
|
||||
joffrey/docker-py \
|
||||
$IMAGE \
|
||||
py.test --collect-only tests/integration/ \
|
||||
| sed -En "s/\s*<Module '([[:print:]]+)'>/\1/p")
|
||||
|
||||
@@ -36,7 +38,7 @@ docker_py_test() {
|
||||
-e DOCKER_HOST=tcp://$DOCKER_BRIDGE_IP:12375 \
|
||||
-v /tmp:/tmp \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
joffrey/docker-py py.test $TESTS ; then
|
||||
$IMAGE py.test $TESTS ; then
|
||||
assert_raises "true"
|
||||
else
|
||||
assert_raises "false"
|
||||
|
||||
@@ -15,10 +15,11 @@ run_on $HOST1 sudo mkdir -p /opt/cni/bin
|
||||
# setup-cni is a subset of 'weave setup', without doing any 'docker pull's
|
||||
weave_on $HOST1 setup-cni
|
||||
weave_on $HOST1 launch
|
||||
weave_on $HOST1 expose
|
||||
|
||||
C1=$(docker_on $HOST1 run --net=none --name=c1 -dt $SMALL_IMAGE /bin/sh)
|
||||
C1=$(docker_on $HOST1 run --net=none --privileged --name=c1 -dt $SMALL_IMAGE /bin/sh)
|
||||
C2=$(docker_on $HOST1 run --net=none --name=c2 -dt $SMALL_IMAGE /bin/sh)
|
||||
# Enable unsolicited ARPs so that ping after the address reuse does not time out
|
||||
exec_on $HOST1 c1 sysctl -w net.ipv4.conf.all.arp_accept=1
|
||||
|
||||
cni_connect $HOST1 c1 <<EOF
|
||||
{
|
||||
@@ -26,6 +27,7 @@ cni_connect $HOST1 c1 <<EOF
|
||||
"type": "weave-net"
|
||||
}
|
||||
EOF
|
||||
|
||||
cni_connect $HOST1 c2 <<EOF
|
||||
{
|
||||
"name": "weave",
|
||||
@@ -45,7 +47,6 @@ assert_raises "exec_on $HOST1 c2 $PING $C1IP"
|
||||
|
||||
# Now remove and start a new container to see if IP address re-use breaks things
|
||||
docker_on $HOST1 rm -f c2
|
||||
sleep 1
|
||||
|
||||
docker_on $HOST1 run --net=none --name=c3 -dt $SMALL_IMAGE /bin/sh
|
||||
|
||||
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#! /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
|
||||
|
||||
# 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
|
||||
+40
-10
@@ -10,8 +10,9 @@ set -e
|
||||
: ${KEY_FILE:=/tmp/gce_private_key.json}
|
||||
: ${SSH_KEY_FILE:=$HOME/.ssh/gce_ssh_key}
|
||||
: ${PROJECT:=positive-cocoa-90213}
|
||||
: ${IMAGE:=ubuntu-14-04}
|
||||
: ${TEMPLATE_NAME:=test-template-10}
|
||||
: ${IMAGE_FAMILY:=ubuntu-1604-lts}
|
||||
: ${IMAGE_PROJECT:=ubuntu-os-cloud}
|
||||
: ${TEMPLATE_NAME:=test-template-12}
|
||||
: ${ZONE:=us-central1-a}
|
||||
: ${NUM_HOSTS:=5}
|
||||
SUFFIX=""
|
||||
@@ -34,7 +35,7 @@ function vm_names {
|
||||
# Delete all vms in this account
|
||||
function destroy {
|
||||
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
|
||||
fi
|
||||
for i in {0..10}; do
|
||||
@@ -72,16 +73,44 @@ function install_docker_on {
|
||||
name=$1
|
||||
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/ | 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 install -q -y --force-yes --no-install-recommends ethtool;
|
||||
apt-get install -q -y bc jq;
|
||||
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;
|
||||
service docker restart
|
||||
mkdir -p /etc/systemd/system/docker.service.d
|
||||
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
|
||||
# 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 {
|
||||
@@ -124,11 +153,12 @@ function setup {
|
||||
}
|
||||
|
||||
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
|
||||
name="$TEMPLATE_NAME.$ZONE.$PROJECT"
|
||||
try_connect $name
|
||||
install_docker_on $name
|
||||
install_kubernetes_on $name
|
||||
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
|
||||
}
|
||||
|
||||
Loaded 100 of 117 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user