Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
022a98d4a4 | ||
|
|
1d80a8d676 | ||
|
|
aa9c032176 | ||
|
|
baa8f229f7 | ||
|
|
ab1b99a4d9 | ||
|
|
b03311e99d | ||
|
|
d1712e9589 | ||
|
|
597c6c111d | ||
|
|
f3915592b2 | ||
|
|
d4367457a9 | ||
|
|
3674808244 | ||
|
|
44cc978a94 | ||
|
|
f441ec2644 | ||
|
|
0ae13a2650 | ||
|
|
ee6596e21c | ||
|
|
a63147beda | ||
|
|
46e4f047de | ||
|
|
5f031453d3 | ||
|
|
028216021f | ||
|
|
d6544302d3 | ||
|
|
354d1ddd5c | ||
|
|
fd34dda6c1 | ||
|
|
f989a82eb0 | ||
|
|
d410ecd34c | ||
|
|
48569265e2 | ||
|
|
71782a6aa5 | ||
|
|
e283b0a1ce | ||
|
|
fa15193011 | ||
|
|
01c436ae15 | ||
|
|
664ba8735f | ||
|
|
937036aa64 | ||
|
|
47cdf2b755 | ||
|
|
a6edd40de3 | ||
|
|
4219c2c2ab | ||
|
|
e7bde18f71 | ||
|
|
233a776c41 | ||
|
|
9cc98919fa | ||
|
|
e42600632f | ||
|
|
49e76d1486 | ||
|
|
965bed7c4f | ||
|
|
9b606fb823 | ||
|
|
15d91c558b | ||
|
|
ffc5a78139 | ||
|
|
f8693074a3 | ||
|
|
1629f4f3f0 | ||
|
|
10baab38d3 | ||
|
|
e458c11e66 | ||
|
|
a07e3301b1 | ||
|
|
2b4d13c5b1 | ||
|
|
ad743b0e96 | ||
|
|
0544901357 | ||
|
|
bc94633180 | ||
|
|
16415404c7 | ||
|
|
46eb01604c | ||
|
|
b03b3fccc7 | ||
|
|
9414e2979d | ||
|
|
865664425b | ||
|
|
b060cf30f4 | ||
|
|
5352ed837a | ||
|
|
d4d54a1ff5 | ||
|
|
ea82a6715f | ||
|
|
d356e32bdf | ||
|
|
6193d22d2e | ||
|
|
50c9568073 | ||
|
|
9d05ed41b2 | ||
|
|
e3f7e0a23a | ||
|
|
da2f0803ce | ||
|
|
6dde5e5ee9 | ||
|
|
494df21bdf | ||
|
|
e44bbbec63 | ||
|
|
c19f49a284 | ||
|
|
d3f2c110e4 | ||
|
|
09628178e4 | ||
|
|
5630a01a8c | ||
|
|
40b3a49c90 | ||
|
|
431b87b94a | ||
|
|
828c3d21d2 | ||
|
|
f0e75481d5 | ||
|
|
8bb7e1cf24 | ||
|
|
e117a6bd04 | ||
|
|
ac44fa63c8 | ||
|
|
bb639dbf32 | ||
|
|
64c890005a | ||
|
|
19088ba8bc | ||
|
|
ec5f7033a4 | ||
|
|
eb60f68df2 | ||
|
|
d72e671d8b | ||
|
|
3462357481 | ||
|
|
2b4da35b1c | ||
|
|
86d68c9875 | ||
|
|
cfff8ee8be | ||
|
|
af10cef5f0 | ||
|
|
a1a42736ed | ||
|
|
b533a11bee | ||
|
|
afed9e9646 | ||
|
|
74b477ac82 | ||
|
|
bf74c8f108 | ||
|
|
04c7f5283e | ||
|
|
d1d53ba45c | ||
|
|
dfadb8f189 | ||
|
|
5335c133a3 | ||
|
|
8fcb5daedf | ||
|
|
ede70a78d6 | ||
|
|
23056cbb45 | ||
|
|
29e9e70ac9 | ||
|
|
d0b711513e | ||
|
|
7dbee74e85 | ||
|
|
4e7a3b1805 | ||
|
|
3c7b244f18 | ||
|
|
e50f5c52c5 | ||
|
|
baf91658d6 | ||
|
|
fda035d8d6 | ||
|
|
a4a53e1a11 | ||
|
|
aad3e52d72 | ||
|
|
cb778a9af5 | ||
|
|
3af9732c84 | ||
|
|
00e754ed9f | ||
|
|
d893f44833 | ||
|
|
4839f41809 | ||
|
|
c801409c94 | ||
|
|
1c4a01d52c | ||
|
|
880b0a883a | ||
|
|
4b77662e3b | ||
|
|
a5ff7ab8df | ||
|
|
04a6e6d13b | ||
|
|
5c2e878c64 | ||
|
|
f9bada5af8 | ||
|
|
80d66f8f68 | ||
|
|
98b35b87a9 | ||
|
|
b1d6605e8f | ||
|
|
2cd3bde348 | ||
|
|
f2454775b9 | ||
|
|
a53d1e00ee | ||
|
|
523a964ac8 | ||
|
|
f8cf7a1054 | ||
|
|
4d8a40c0b5 | ||
|
|
9754da15d6 | ||
|
|
0458d32089 | ||
|
|
ee4e8eb994 | ||
|
|
b8d619dde7 | ||
|
|
00c86fed42 | ||
|
|
6d5af85eee | ||
|
|
1ae2d33714 | ||
|
|
71d550260f | ||
|
|
1092bae05c | ||
|
|
e611f04f41 | ||
|
|
3492cd93d0 | ||
|
|
947ae11fee | ||
|
|
cdd6ed548c | ||
|
|
fc940d0017 | ||
|
|
b38a55ca7c | ||
|
|
8e739d5240 | ||
|
|
71f8bb7e9a | ||
|
|
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 | ||
|
|
2e825242cd | ||
|
|
027064cbdb | ||
|
|
060f6392c7 | ||
|
|
a466f55d59 | ||
|
|
f686408808 | ||
|
|
b8c57ef10f | ||
|
|
5cc0826ca0 | ||
|
|
e703f2f3f2 | ||
|
|
f544da9e19 | ||
|
|
472eab2acd | ||
|
|
7a5d7fca6c | ||
|
|
589b45dace | ||
|
|
3273d13666 | ||
|
|
5fdbbf3722 | ||
|
|
14350ff463 | ||
|
|
0b49c961a7 | ||
|
|
39b80f26ed | ||
|
|
74b20265e4 | ||
|
|
358fddb958 | ||
|
|
5c8f6175b5 | ||
|
|
83ce5875ee | ||
|
|
f5a30c790e | ||
|
|
34415c3c17 | ||
|
|
6387fa8bc9 | ||
|
|
8ec6b699b1 | ||
|
|
551f164ec3 | ||
|
|
88bf64722f | ||
|
|
d9c998b5c4 | ||
|
|
170e19c283 | ||
|
|
7c47a8ffb7 | ||
|
|
791ab55875 | ||
|
|
bd1a9d020c | ||
|
|
71fd3b6b2b | ||
|
|
f8ac27a6cc | ||
|
|
954cd0b86a | ||
|
|
9dedeadf15 | ||
|
|
6561272f8d | ||
|
|
881e795db5 | ||
|
|
4216231ec7 | ||
|
|
333d5f7511 | ||
|
|
7be7945b29 | ||
|
|
afe882cd94 | ||
|
|
5b8a359475 | ||
|
|
df0af203f8 | ||
|
|
5b2a4ff614 | ||
|
|
ceb5f8c714 | ||
|
|
cc3359dfe5 | ||
|
|
cda6785f1d | ||
|
|
cb030be4ad | ||
|
|
b464ce86f7 | ||
|
|
80b3514e24 | ||
|
|
e33f56f9b0 | ||
|
|
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
|
||||
+90
-2
@@ -1,14 +1,102 @@
|
||||
## Release 1.8.2
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Fixed a bug where looping flows were installed which caused high CPU
|
||||
usage #2650, #2674
|
||||
* Fixed a bug where Kubernetes master could not contact pods #2673, #2683
|
||||
* Fixed a bug where weave-kube was crashing in a loop due to invalid
|
||||
Weave bridge state #2657
|
||||
* Fixed a bug where iptables NAT rules were not appended due to
|
||||
"temporary unavailable" iptables error #2679
|
||||
* Added a detection of enabling the hairpin mode on the Weave bridge port
|
||||
which caused installation of looping flows #2674
|
||||
* Added a detection of overlaps between Weave and the host IP address
|
||||
ranges when launching weave-kube #2669, #2672
|
||||
* Added logging of connections blocked by weave-npc #2546, #2573
|
||||
|
||||
More details in the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.8.2).
|
||||
|
||||
## Release 1.8.1
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Fixed weave-npc crash from Succeeded/Failed pods #2632,#2658
|
||||
* Fixed occasional failure to create Weave bridge on node reboot
|
||||
#2617,#2637
|
||||
* Fixed a bug where weave-kube would fail to install when run with
|
||||
unrelease snapshot builds #2642
|
||||
* Improved conformance to CNI spec by not releasing IP addresses when
|
||||
a container dies #2643
|
||||
* Improved troubleshooting of install failure by creating CNI config
|
||||
after Weave Net is up #2570
|
||||
* "up to date" shown even when the version check was blocked by
|
||||
firewall #2537,#2565,#2645
|
||||
* "Unable to claim" message on re-launching Weave after using CNI
|
||||
#2548,#2577
|
||||
* Eliminated spurious IP reclaim operations when IPAM was disabled
|
||||
#2567,#2644
|
||||
* Include `jq` tool in our build VM configuration #2656
|
||||
|
||||
## Release 1.8.0
|
||||
|
||||
Highlights:
|
||||
|
||||
* Exposed network policy controller Prometheus metrics
|
||||
weaveworks/weave-npc#23, #2595, #2549
|
||||
* Exposed router Prometheus metrics #2535, #2547, #2523, #2579, #2578,
|
||||
#2568, #2560, #2561
|
||||
|
||||
More details in the [change log](https://github.com/weaveworks/weave/issues?q=milestone%3A1.8.0).
|
||||
|
||||
## Release 1.7.2
|
||||
|
||||
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
|
||||
* `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
|
||||
* `weave ps` returned no error when the router was down #2445
|
||||
* 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
|
||||
|
||||
@@ -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,7 +182,7 @@ 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 $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||
|
||||
Vendored
+5
@@ -14,6 +14,7 @@ pkgs = %w(
|
||||
git
|
||||
mercurial
|
||||
bc
|
||||
jq
|
||||
)
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
@@ -22,6 +23,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
|
||||
config.vm.network "private_network", ip: vm_ip
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
vb.memory = 2048
|
||||
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "off"]
|
||||
vb.customize ["modifyvm", :id, "--natdnsproxy1", "off"]
|
||||
end
|
||||
@@ -29,6 +31,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
|
||||
|
||||
@@ -5,6 +5,15 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// Special token used in place of a container identifier when:
|
||||
// - the caller does not know the container ID, or
|
||||
// - the caller is unsure whether IPAM was previously told the container ID.
|
||||
//
|
||||
// While addresses are typically stored under their container IDs, when this
|
||||
// token is used, the address will be stored under its own string, something
|
||||
// which should be kept in mind if the entry needs to be removed at any point.
|
||||
const NoContainerID string = "_"
|
||||
|
||||
func (client *Client) ipamOp(ID string, op string) (*net.IPNet, error) {
|
||||
ip, err := client.httpVerb(op, fmt.Sprintf("/ip/%s", ID), nil)
|
||||
if err != nil {
|
||||
|
||||
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
|
||||
+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
-163
@@ -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,154 +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.WithNetNSUnsafe(ns, func() error {
|
||||
return forEachLink(func(link netlink.Link) error {
|
||||
if match(link) {
|
||||
netDev, err := linkToNetDev(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
netDevs = append(netDevs, netDev)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
return netDevs, err
|
||||
}
|
||||
|
||||
func forEachLink(f func(netlink.Link) error) error {
|
||||
links, err := netlink.LinkList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, link := range links {
|
||||
if err := f(link); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func linkToNetDev(link netlink.Link) (NetDev, error) {
|
||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||
if err != nil {
|
||||
return NetDev{}, err
|
||||
}
|
||||
|
||||
netDev := NetDev{Name: link.Attrs().Name, MAC: link.Attrs().HardwareAddr}
|
||||
for _, addr := range addrs {
|
||||
netDev.CIDRs = append(netDev.CIDRs, addr.IPNet)
|
||||
}
|
||||
return netDev, nil
|
||||
}
|
||||
|
||||
// ConnectedToBridgePredicate returns a function which is used to query whether
|
||||
// a given link is a veth interface which one end is connected to a bridge.
|
||||
// The returned function should be called from a container network namespace which
|
||||
// the bridge does NOT belong to.
|
||||
func ConnectedToBridgePredicate(bridgeName string) (func(link netlink.Link) bool, error) {
|
||||
indexes := make(map[int]struct{})
|
||||
|
||||
// Scan devices in root namespace to find those attached to weave bridge
|
||||
err := weavenet.WithNetNSLinkByPidUnsafe(1, bridgeName,
|
||||
func(br netlink.Link) error {
|
||||
return forEachLink(func(link netlink.Link) error {
|
||||
if link.Attrs().MasterIndex == br.Attrs().Index {
|
||||
peerIndex := link.Attrs().ParentIndex
|
||||
if peerIndex == 0 {
|
||||
// perhaps running on an older kernel where ParentIndex doesn't work.
|
||||
// as fall-back, assume the indexes are consecutive
|
||||
peerIndex = link.Attrs().Index - 1
|
||||
}
|
||||
indexes[peerIndex] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func(link netlink.Link) bool {
|
||||
_, isveth := link.(*netlink.Veth)
|
||||
_, found := indexes[link.Attrs().Index]
|
||||
return isveth && found
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetNetDevsWithPredicate(processID int, predicate func(link netlink.Link) bool) ([]NetDev, error) {
|
||||
// Bail out if this 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
|
||||
}
|
||||
|
||||
return FindNetDevs(processID, predicate)
|
||||
}
|
||||
|
||||
// Lookup the weave interface of a container
|
||||
func GetWeaveNetDevs(processID int) ([]NetDev, error) {
|
||||
p, err := ConnectedToBridgePredicate("weave")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return GetNetDevsWithPredicate(processID, p)
|
||||
}
|
||||
|
||||
// Get the weave bridge interface.
|
||||
// NB: Should be called from the root network namespace.
|
||||
func GetBridgeNetDev(bridgeName string) (NetDev, error) {
|
||||
link, err := netlink.LinkByName(bridgeName)
|
||||
if err != nil {
|
||||
return NetDev{}, err
|
||||
}
|
||||
|
||||
return linkToNetDev(link)
|
||||
}
|
||||
|
||||
// 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
|
||||
+2
-1
@@ -3,6 +3,7 @@ package ipam
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/weaveworks/weave/api"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
)
|
||||
|
||||
@@ -43,7 +44,7 @@ func (g *allocate) Try(alloc *Allocator) bool {
|
||||
if ok, addr := alloc.space.Allocate(g.r.HostRange()); ok {
|
||||
// If caller hasn't supplied a unique ID, file it under the IP address
|
||||
// which lets the caller then release the address using DELETE /ip/address
|
||||
if g.ident == "_" {
|
||||
if g.ident == api.NoContainerID {
|
||||
g.ident = addr.String()
|
||||
}
|
||||
alloc.debugln("Allocated", addr, "for", g.ident, "in", g.r)
|
||||
|
||||
+5
-5
@@ -322,7 +322,7 @@ func (alloc *Allocator) Claim(ident string, cidr address.CIDR, isContainer, noEr
|
||||
// ContainerDied called from the updater interface. Async.
|
||||
func (alloc *Allocator) ContainerDied(ident string) {
|
||||
alloc.actionChan <- func() {
|
||||
if alloc.hasOwned(ident) {
|
||||
if alloc.hasOwnedByContainer(ident) {
|
||||
alloc.debugln("Container", ident, "died; noting to remove later")
|
||||
alloc.dead[ident] = alloc.now()
|
||||
}
|
||||
@@ -335,7 +335,7 @@ func (alloc *Allocator) ContainerDied(ident string) {
|
||||
// ContainerDestroyed called from the updater interface. Async.
|
||||
func (alloc *Allocator) ContainerDestroyed(ident string) {
|
||||
alloc.actionChan <- func() {
|
||||
if alloc.hasOwned(ident) {
|
||||
if alloc.hasOwnedByContainer(ident) {
|
||||
alloc.debugln("Container", ident, "destroyed; removing addresses")
|
||||
alloc.delete(ident)
|
||||
delete(alloc.dead, ident)
|
||||
@@ -996,9 +996,9 @@ func (alloc *Allocator) persistOwned() {
|
||||
|
||||
// Owned addresses
|
||||
|
||||
func (alloc *Allocator) hasOwned(ident string) bool {
|
||||
_, b := alloc.owned[ident]
|
||||
return b
|
||||
func (alloc *Allocator) hasOwnedByContainer(ident string) bool {
|
||||
d, b := alloc.owned[ident]
|
||||
return b && d.IsContainer
|
||||
}
|
||||
|
||||
// NB: addr must not be owned by ident already
|
||||
|
||||
+18
-9
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/weaveworks/mesh"
|
||||
|
||||
"github.com/weaveworks/weave/api"
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
)
|
||||
@@ -77,26 +78,34 @@ func (c *claim) Try(alloc *Allocator) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if c.ident == "_" { // Special "I don't have a unique ID" identifier
|
||||
c.ident = c.cidr.Addr.String()
|
||||
}
|
||||
// We are the owner, check we haven't given it to another container
|
||||
switch existingIdent := alloc.findOwner(c.cidr.Addr); existingIdent {
|
||||
case "":
|
||||
existingIdent := alloc.findOwner(c.cidr.Addr)
|
||||
switch {
|
||||
case existingIdent == "":
|
||||
// Unused address, we try to claim it:
|
||||
if err := alloc.space.Claim(c.cidr.Addr); err == nil {
|
||||
alloc.debugln("Claimed", c.cidr, "for", c.ident)
|
||||
alloc.addOwned(c.ident, c.cidr, c.isContainer)
|
||||
if c.ident == api.NoContainerID {
|
||||
alloc.addOwned(c.cidr.Addr.String(), c.cidr, c.isContainer)
|
||||
} else {
|
||||
alloc.addOwned(c.ident, c.cidr, c.isContainer)
|
||||
}
|
||||
c.sendResult(nil)
|
||||
} else {
|
||||
c.sendResult(err)
|
||||
}
|
||||
case c.ident:
|
||||
case (existingIdent == c.ident) || (c.ident == api.NoContainerID && existingIdent == c.cidr.Addr.String()):
|
||||
// same identifier is claiming same address; that's OK
|
||||
alloc.debugln("Re-Claimed", c.cidr, "for", c.ident)
|
||||
c.sendResult(nil)
|
||||
case c.cidr.Addr.String():
|
||||
// Address already allocated via "_" name
|
||||
case existingIdent == c.cidr.Addr.String():
|
||||
// Address already allocated via api.NoContainerID name and current ID is a real container ID:
|
||||
c.sendResult(fmt.Errorf("address %s already in use", c.cidr))
|
||||
case c.ident == api.NoContainerID:
|
||||
// We do not know whether this is the same container or another one,
|
||||
// but we also cannot prove otherwise, so we let it reclaim the address:
|
||||
alloc.debugln("Re-Claimed", c.cidr, "for ID", c.ident, "having existing ID as", existingIdent)
|
||||
c.sendResult(nil)
|
||||
default:
|
||||
// Addr already owned by container on this machine
|
||||
c.sendResult(fmt.Errorf("address %s is already owned by %s", c.cidr.String(), existingIdent))
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package ring
|
||||
|
||||
/* Exported variables for monitoring and management */
|
||||
|
||||
import (
|
||||
"expvar"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
expRingSize = expvar.NewMap("ipam.ringSize")
|
||||
expRingEntries = expvar.NewMap("ipam.ringEntries")
|
||||
)
|
||||
|
||||
type _int int
|
||||
|
||||
func (i _int) String() string {
|
||||
return strconv.Itoa(int(i))
|
||||
}
|
||||
|
||||
type _uint32 uint32
|
||||
|
||||
func (i _uint32) String() string {
|
||||
return strconv.FormatUint(uint64(i), 10)
|
||||
}
|
||||
|
||||
func (r *Ring) updateExportedVariables() {
|
||||
ringName := r.Start.String()
|
||||
expRingSize.Set(ringName, _uint32(r.End-r.Start))
|
||||
expRingEntries.Set(ringName, _int(len(r.Entries)))
|
||||
}
|
||||
@@ -114,7 +114,6 @@ func New(start, end address.Address, peer mesh.PeerName, f OnUpdate) *Ring {
|
||||
common.Assert(start < end)
|
||||
|
||||
ring := &Ring{Start: start, End: end, Peer: peer, Entries: make([]*entry, 0), onUpdate: f}
|
||||
ring.updateExportedVariables()
|
||||
return ring
|
||||
}
|
||||
|
||||
@@ -151,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 -----------------
|
||||
|
||||
@@ -218,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.
|
||||
@@ -404,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)
|
||||
@@ -455,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
|
||||
@@ -564,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),
|
||||
|
||||
+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) {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
@@ -51,3 +54,35 @@ func isDatapath(link netlink.Link) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func DetectHairpin(portIfName string, log *logrus.Logger) error {
|
||||
link, err := netlink.LinkByName(portIfName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to find link %q: %s", portIfName, err)
|
||||
}
|
||||
|
||||
ch := make(chan netlink.LinkUpdate)
|
||||
// See EnsureInterface for why done channel is not passed
|
||||
if err := netlink.LinkSubscribe(ch, nil); err != nil {
|
||||
return fmt.Errorf("Unable to subscribe to netlink updates: %s", err)
|
||||
}
|
||||
|
||||
pi, err := netlink.LinkGetProtinfo(link)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to get link protinfo %q: %s", portIfName, err)
|
||||
}
|
||||
if pi.Hairpin {
|
||||
return fmt.Errorf("Hairpin mode enabled on %q", portIfName)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for up := range ch {
|
||||
if up.Attrs().Name == portIfName && up.Attrs().Protinfo != nil &&
|
||||
up.Attrs().Protinfo.Hairpin {
|
||||
log.Errorf("Hairpin mode enabled on %q", portIfName)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
+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)
|
||||
}
|
||||
+26
-7
@@ -1,7 +1,10 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
@@ -54,12 +57,28 @@ func WithNetNSLinkUnsafe(ns netns.NsHandle, ifName string, work func(link netlin
|
||||
})
|
||||
}
|
||||
|
||||
func WithNetNSLinkByPidUnsafe(pid int, ifName string, work func(link netlink.Link) error) error {
|
||||
ns, err := netns.GetFromPid(pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ns.Close()
|
||||
var WeaveUtilCmd = "weaveutil"
|
||||
|
||||
return WithNetNSLinkUnsafe(ns, ifName, work)
|
||||
// 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)
|
||||
}
|
||||
+123
-17
@@ -3,8 +3,11 @@ package net
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/j-keck/arping"
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
@@ -103,16 +106,26 @@ const (
|
||||
vethPrefix = "v" + VethName // starts with "veth" to suppress UI notifications
|
||||
)
|
||||
|
||||
func interfaceExistsInNamespace(ns netns.NsHandle, ifName string) bool {
|
||||
err := WithNetNSUnsafe(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 +135,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 := WithNetNSUnsafe(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
|
||||
@@ -145,6 +150,21 @@ func AttachContainer(ns netns.NsHandle, id, ifName, bridgeName string, mtu int,
|
||||
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,7 +196,20 @@ 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 {
|
||||
// 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 {
|
||||
@@ -194,7 +227,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 +259,52 @@ 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
|
||||
}
|
||||
|
||||
func addNatRule(ipt *iptables.IPTables, rulespec ...string) error {
|
||||
// Loop until we get an exit code other than "temporarily unavailable"
|
||||
for {
|
||||
if err := ipt.AppendUnique("nat", "WEAVE", rulespec...); err != nil {
|
||||
if ierr, ok := err.(*iptables.Error); ok {
|
||||
if status, ok := ierr.ExitError.Sys().(syscall.WaitStatus); ok {
|
||||
// (magic exit code 4 found in iptables source code; undocumented)
|
||||
if status.ExitStatus() == 4 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ExposeNAT(ipnet net.IPNet) error {
|
||||
ipt, err := iptables.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cidr := ipnet.String()
|
||||
if err := addNatRule(ipt, "-s", cidr, "-d", "224.0.0.0/4", "-j", "RETURN"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := addNatRule(ipt, "-d", cidr, "!", "-s", cidr, "-j", "MASQUERADE"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := addNatRule(ipt, "-s", cidr, "!", "-d", cidr, "-j", "MASQUERADE"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/client-go/pkg/api"
|
||||
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||
"k8s.io/client-go/pkg/util/intstr"
|
||||
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
)
|
||||
|
||||
func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
|
||||
rules map[string]*ruleSpec,
|
||||
nsSelectors, podSelectors map[string]*selectorSpec,
|
||||
err error) {
|
||||
|
||||
nsSelectors = make(map[string]*selectorSpec)
|
||||
podSelectors = make(map[string]*selectorSpec)
|
||||
rules = make(map[string]*ruleSpec)
|
||||
|
||||
dstSelector, err := newSelectorSpec(&policy.Spec.PodSelector, ns.name, ipset.HashIP)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
podSelectors[dstSelector.key] = dstSelector
|
||||
|
||||
for _, ingressRule := range policy.Spec.Ingress {
|
||||
if ingressRule.Ports != nil && len(ingressRule.Ports) == 0 {
|
||||
// Ports is empty, this rule matches no ports (no traffic matches).
|
||||
continue
|
||||
}
|
||||
|
||||
if ingressRule.From != nil && len(ingressRule.From) == 0 {
|
||||
// From is empty, this rule matches no sources (no traffic matches).
|
||||
continue
|
||||
}
|
||||
|
||||
if ingressRule.From == nil {
|
||||
// From is not provided, this rule matches all sources (traffic not restricted by source).
|
||||
if ingressRule.Ports == nil {
|
||||
// Ports is not provided, this rule matches all ports (traffic not restricted by port).
|
||||
rule := newRuleSpec(nil, nil, dstSelector, nil)
|
||||
rules[rule.key] = rule
|
||||
} else {
|
||||
// Ports is present and contains at least one item, then this rule allows traffic
|
||||
// only if the traffic matches at least one port in the ports list.
|
||||
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
||||
rule := newRuleSpec(&proto, nil, dstSelector, &port)
|
||||
rules[rule.key] = rule
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// From is present and contains at least on item, this rule allows traffic only if the
|
||||
// traffic matches at least one item in the from list.
|
||||
for _, peer := range ingressRule.From {
|
||||
var srcSelector *selectorSpec
|
||||
if peer.PodSelector != nil {
|
||||
srcSelector, err = newSelectorSpec(peer.PodSelector, ns.name, ipset.HashIP)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
podSelectors[srcSelector.key] = srcSelector
|
||||
}
|
||||
if peer.NamespaceSelector != nil {
|
||||
srcSelector, err = newSelectorSpec(peer.NamespaceSelector, "", ipset.ListSet)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
nsSelectors[srcSelector.key] = srcSelector
|
||||
}
|
||||
|
||||
if ingressRule.Ports == nil {
|
||||
// Ports is not provided, this rule matches all ports (traffic not restricted by port).
|
||||
rule := newRuleSpec(nil, srcSelector, dstSelector, nil)
|
||||
rules[rule.key] = rule
|
||||
} else {
|
||||
// Ports is present and contains at least one item, then this rule allows traffic
|
||||
// only if the traffic matches at least one port in the ports list.
|
||||
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
||||
rule := newRuleSpec(&proto, srcSelector, dstSelector, &port)
|
||||
rules[rule.key] = rule
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nsSelectors, podSelectors, nil
|
||||
}
|
||||
|
||||
func withNormalisedProtoAndPort(npps []extnapi.NetworkPolicyPort, f func(proto, port string)) {
|
||||
for _, npp := range npps {
|
||||
// If no proto is specified, default to TCP
|
||||
proto := string(api.ProtocolTCP)
|
||||
if npp.Protocol != nil {
|
||||
proto = string(*npp.Protocol)
|
||||
}
|
||||
|
||||
// If no port is specified, match any port. Let iptables executable handle
|
||||
// service name resolution
|
||||
port := "0:65535"
|
||||
if npp.Port != nil {
|
||||
switch npp.Port.Type {
|
||||
case intstr.Int:
|
||||
port = fmt.Sprintf("%d", npp.Port.IntVal)
|
||||
case intstr.String:
|
||||
port = npp.Port.StrVal
|
||||
}
|
||||
}
|
||||
|
||||
f(proto, port)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package npc
|
||||
|
||||
// These types are defined in https://github.com/kubernetes/kubernetes/blob/master/docs/proposals/network-policy.md
|
||||
// but not in the k8s API yet. Copies are included here for decoding the `net.beta.kubernetes.io/network-policy`
|
||||
// annotation specified in the above document.
|
||||
|
||||
type IngressIsolationPolicy string
|
||||
|
||||
const (
|
||||
// Deny all ingress traffic to pods in this namespace. Ingress means
|
||||
// any incoming traffic to pods, whether that be from other pods within this namespace
|
||||
// or any source outside of this namespace.
|
||||
DefaultDeny IngressIsolationPolicy = "DefaultDeny"
|
||||
)
|
||||
|
||||
// Standard NamespaceSpec object, modified to include a new
|
||||
// NamespaceNetworkPolicy field.
|
||||
type NamespaceSpec struct {
|
||||
// This is a pointer so that it can be left undefined.
|
||||
NetworkPolicy *NamespaceNetworkPolicy `json:"networkPolicy,omitempty"`
|
||||
}
|
||||
|
||||
type NamespaceNetworkPolicy struct {
|
||||
// Ingress configuration for this namespace. This config is
|
||||
// applied to all pods within this namespace. For now, only
|
||||
// ingress is supported. This field is optional - if not
|
||||
// defined, then the cluster default for ingress is applied.
|
||||
Ingress *NamespaceIngressPolicy `json:"ingress,omitempty"`
|
||||
}
|
||||
|
||||
// Configuration for ingress to pods within this namespace.
|
||||
// For now, this only supports specifying an isolation policy.
|
||||
type NamespaceIngressPolicy struct {
|
||||
// The isolation policy to apply to pods in this namespace.
|
||||
// Currently this field only supports "DefaultDeny", but could
|
||||
// be extended to support other policies in the future. When set to DefaultDeny,
|
||||
// pods in this namespace are denied ingress traffic by default. When not defined,
|
||||
// the cluster default ingress isolation policy is applied (currently allow all).
|
||||
Isolation *IngressIsolationPolicy `json:"isolation,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package npc
|
||||
|
||||
const (
|
||||
TableFilter = "filter"
|
||||
|
||||
MainChain = "WEAVE-NPC"
|
||||
DefaultChain = "WEAVE-NPC-DEFAULT"
|
||||
IngressChain = "WEAVE-NPC-INGRESS"
|
||||
)
|
||||
@@ -0,0 +1,173 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/pkg/errors"
|
||||
coreapi "k8s.io/client-go/pkg/api/v1"
|
||||
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
)
|
||||
|
||||
type NetworkPolicyController interface {
|
||||
AddNamespace(ns *coreapi.Namespace) error
|
||||
UpdateNamespace(oldObj, newObj *coreapi.Namespace) error
|
||||
DeleteNamespace(ns *coreapi.Namespace) error
|
||||
|
||||
AddPod(obj *coreapi.Pod) error
|
||||
UpdatePod(oldObj, newObj *coreapi.Pod) error
|
||||
DeletePod(obj *coreapi.Pod) error
|
||||
|
||||
AddNetworkPolicy(obj *extnapi.NetworkPolicy) error
|
||||
UpdateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error
|
||||
DeleteNetworkPolicy(obj *extnapi.NetworkPolicy) error
|
||||
}
|
||||
|
||||
type controller struct {
|
||||
sync.Mutex
|
||||
|
||||
ipt *iptables.IPTables
|
||||
ips ipset.Interface
|
||||
|
||||
nss map[string]*ns // ns name -> ns struct
|
||||
nsSelectors *selectorSet // selector string -> nsSelector
|
||||
}
|
||||
|
||||
func New(ipt *iptables.IPTables, ips ipset.Interface) NetworkPolicyController {
|
||||
c := &controller{
|
||||
ipt: ipt,
|
||||
ips: ips,
|
||||
nss: make(map[string]*ns)}
|
||||
|
||||
c.nsSelectors = newSelectorSet(ips, c.onNewNsSelector)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (npc *controller) onNewNsSelector(selector *selector) error {
|
||||
for _, ns := range npc.nss {
|
||||
if ns.namespace != nil {
|
||||
if selector.matches(ns.namespace.ObjectMeta.Labels) {
|
||||
if err := ns.ips.AddEntry(selector.spec.ipsetName, string(ns.allPods.ipsetName)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (npc *controller) withNS(name string, f func(ns *ns) error) error {
|
||||
ns, found := npc.nss[name]
|
||||
if !found {
|
||||
newNs, err := newNS(name, npc.ipt, npc.ips, npc.nsSelectors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
npc.nss[name] = newNs
|
||||
ns = newNs
|
||||
}
|
||||
if err := f(ns); err != nil {
|
||||
return err
|
||||
}
|
||||
if ns.empty() {
|
||||
if err := ns.destroy(); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(npc.nss, name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (npc *controller) AddPod(obj *coreapi.Pod) error {
|
||||
npc.Lock()
|
||||
defer npc.Unlock()
|
||||
|
||||
log.Infof("EVENT AddPod %#v", obj)
|
||||
return npc.withNS(obj.ObjectMeta.Namespace, func(ns *ns) error {
|
||||
return errors.Wrap(ns.addPod(obj), "add pod")
|
||||
})
|
||||
}
|
||||
|
||||
func (npc *controller) UpdatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
npc.Lock()
|
||||
defer npc.Unlock()
|
||||
|
||||
log.Infof("EVENT UpdatePod %#v %#v", oldObj, newObj)
|
||||
return npc.withNS(oldObj.ObjectMeta.Namespace, func(ns *ns) error {
|
||||
return errors.Wrap(ns.updatePod(oldObj, newObj), "update pod")
|
||||
})
|
||||
}
|
||||
|
||||
func (npc *controller) DeletePod(obj *coreapi.Pod) error {
|
||||
npc.Lock()
|
||||
defer npc.Unlock()
|
||||
|
||||
log.Infof("EVENT DeletePod %#v", obj)
|
||||
return npc.withNS(obj.ObjectMeta.Namespace, func(ns *ns) error {
|
||||
return errors.Wrap(ns.deletePod(obj), "delete pod")
|
||||
})
|
||||
}
|
||||
|
||||
func (npc *controller) AddNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
||||
npc.Lock()
|
||||
defer npc.Unlock()
|
||||
|
||||
log.Infof("EVENT AddNetworkPolicy %#v", obj)
|
||||
return npc.withNS(obj.ObjectMeta.Namespace, func(ns *ns) error {
|
||||
return errors.Wrap(ns.addNetworkPolicy(obj), "add network policy")
|
||||
})
|
||||
}
|
||||
|
||||
func (npc *controller) UpdateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error {
|
||||
npc.Lock()
|
||||
defer npc.Unlock()
|
||||
|
||||
log.Infof("EVENT UpdateNetworkPolicy %#v %#v", oldObj, newObj)
|
||||
return npc.withNS(oldObj.ObjectMeta.Namespace, func(ns *ns) error {
|
||||
return errors.Wrap(ns.updateNetworkPolicy(oldObj, newObj), "update network policy")
|
||||
})
|
||||
}
|
||||
|
||||
func (npc *controller) DeleteNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
||||
npc.Lock()
|
||||
defer npc.Unlock()
|
||||
|
||||
log.Infof("EVENT DeleteNetworkPolicy %#v", obj)
|
||||
return npc.withNS(obj.ObjectMeta.Namespace, func(ns *ns) error {
|
||||
return errors.Wrap(ns.deleteNetworkPolicy(obj), "delete network policy")
|
||||
})
|
||||
}
|
||||
|
||||
func (npc *controller) AddNamespace(obj *coreapi.Namespace) error {
|
||||
npc.Lock()
|
||||
defer npc.Unlock()
|
||||
|
||||
log.Infof("EVENT AddNamespace %#v", obj)
|
||||
return npc.withNS(obj.ObjectMeta.Name, func(ns *ns) error {
|
||||
return errors.Wrap(ns.addNamespace(obj), "add namespace")
|
||||
})
|
||||
}
|
||||
|
||||
func (npc *controller) UpdateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
||||
npc.Lock()
|
||||
defer npc.Unlock()
|
||||
|
||||
log.Infof("EVENT UpdateNamespace%#v %#v", oldObj, newObj)
|
||||
return npc.withNS(oldObj.ObjectMeta.Name, func(ns *ns) error {
|
||||
return errors.Wrap(ns.updateNamespace(oldObj, newObj), "update namespace")
|
||||
})
|
||||
}
|
||||
|
||||
func (npc *controller) DeleteNamespace(obj *coreapi.Namespace) error {
|
||||
npc.Lock()
|
||||
defer npc.Unlock()
|
||||
|
||||
log.Infof("EVENT DeleteNamespace %#v", obj)
|
||||
return npc.withNS(obj.ObjectMeta.Name, func(ns *ns) error {
|
||||
return errors.Wrap(ns.deleteNamespace(obj), "delete namespace")
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package ipset
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Name string
|
||||
|
||||
type Type string
|
||||
|
||||
const (
|
||||
ListSet = Type("list:set")
|
||||
HashIP = Type("hash:ip")
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
Create(ipsetName Name, ipsetType Type) error
|
||||
AddEntry(ipsetName Name, entry string) error
|
||||
DelEntry(ipsetName Name, entry string) error
|
||||
Flush(ipsetName Name) error
|
||||
Destroy(ipsetName Name) error
|
||||
|
||||
FlushAll() error
|
||||
DestroyAll() error
|
||||
}
|
||||
|
||||
type ipset struct {
|
||||
}
|
||||
|
||||
func New() Interface {
|
||||
return &ipset{}
|
||||
}
|
||||
|
||||
func (i *ipset) Create(ipsetName Name, ipsetType Type) error {
|
||||
return doExec("create", string(ipsetName), string(ipsetType))
|
||||
}
|
||||
|
||||
func (i *ipset) AddEntry(ipsetName Name, entry string) error {
|
||||
return doExec("add", string(ipsetName), entry)
|
||||
}
|
||||
|
||||
func (i *ipset) DelEntry(ipsetName Name, entry string) error {
|
||||
return doExec("del", string(ipsetName), entry)
|
||||
}
|
||||
|
||||
func (i *ipset) Flush(ipsetName Name) error {
|
||||
return doExec("flush", string(ipsetName))
|
||||
}
|
||||
|
||||
func (i *ipset) FlushAll() error {
|
||||
return doExec("flush")
|
||||
}
|
||||
|
||||
func (i *ipset) Destroy(ipsetName Name) error {
|
||||
return doExec("destroy", string(ipsetName))
|
||||
}
|
||||
|
||||
func (i *ipset) DestroyAll() error {
|
||||
return doExec("destroy")
|
||||
}
|
||||
|
||||
func doExec(args ...string) error {
|
||||
if output, err := exec.Command("ipset", args...).CombinedOutput(); err != nil {
|
||||
return errors.Wrapf(err, "ipset %v failed: %s", args, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/google/gopacket/pcapgo"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
var (
|
||||
blockedConnections = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "weavenpc_blocked_connections_total",
|
||||
Help: "Connection attempts blocked by policy controller.",
|
||||
},
|
||||
[]string{"protocol", "dport"},
|
||||
)
|
||||
)
|
||||
|
||||
func gatherMetrics() {
|
||||
pipe, err := os.Open("/var/log/ulogd.pcap")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open pcap: %v", err)
|
||||
}
|
||||
|
||||
reader, err := pcapgo.NewReader(pipe)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read pcap header: %v", err)
|
||||
}
|
||||
|
||||
for {
|
||||
data, _, err := reader.ReadPacketData()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read pcap packet: %v", err)
|
||||
}
|
||||
|
||||
packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default)
|
||||
|
||||
if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
|
||||
tcp, _ := tcpLayer.(*layers.TCP)
|
||||
if tcp.SYN && !tcp.ACK { // Only plain SYN constitutes a NEW TCP connection
|
||||
blockedConnections.With(prometheus.Labels{"protocol": "tcp", "dport": strconv.Itoa(int(tcp.DstPort))}).Inc()
|
||||
log.Warnf("TCP connection from %v:%d to %v:%d blocked by Weave NPC.", srcIP(packet), tcp.SrcPort, dstIP(packet), tcp.DstPort)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if udpLayer := packet.Layer(layers.LayerTypeUDP); udpLayer != nil {
|
||||
udp, _ := udpLayer.(*layers.UDP)
|
||||
blockedConnections.With(prometheus.Labels{"protocol": "udp", "dport": strconv.Itoa(int(udp.DstPort))}).Inc()
|
||||
log.Warnf("UDP connection from %v:%d to %v:%d blocked by Weave NPC.", srcIP(packet), udp.SrcPort, dstIP(packet), udp.DstPort)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unknownIP string = "<unknown IP>"
|
||||
|
||||
func srcIP(packet gopacket.Packet) string {
|
||||
if layer := packet.Layer(layers.LayerTypeIPv4); layer != nil {
|
||||
if ip, ok := layer.(*layers.IPv4); ok {
|
||||
return ip.SrcIP.String()
|
||||
}
|
||||
}
|
||||
if layer := packet.Layer(layers.LayerTypeIPv6); layer != nil {
|
||||
if ip, ok := layer.(*layers.IPv6); ok {
|
||||
return ip.SrcIP.String()
|
||||
}
|
||||
}
|
||||
return unknownIP
|
||||
}
|
||||
|
||||
func dstIP(packet gopacket.Packet) string {
|
||||
if layer := packet.Layer(layers.LayerTypeIPv4); layer != nil {
|
||||
if ip, ok := layer.(*layers.IPv4); ok {
|
||||
return ip.DstIP.String()
|
||||
}
|
||||
}
|
||||
if layer := packet.Layer(layers.LayerTypeIPv6); layer != nil {
|
||||
if ip, ok := layer.(*layers.IPv6); ok {
|
||||
return ip.DstIP.String()
|
||||
}
|
||||
}
|
||||
return unknownIP
|
||||
}
|
||||
|
||||
func Start(addr string) error {
|
||||
if err := prometheus.Register(blockedConnections); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
http.Handle("/metrics", promhttp.Handler())
|
||||
|
||||
go func() {
|
||||
log.Infof("Serving /metrics on %s", addr)
|
||||
if err := http.ListenAndServe(addr, nil); err != nil {
|
||||
log.Fatalf("Failed to bind metrics server: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
go gatherMetrics()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"k8s.io/client-go/pkg/api/unversioned"
|
||||
coreapi "k8s.io/client-go/pkg/api/v1"
|
||||
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||
"k8s.io/client-go/pkg/types"
|
||||
"k8s.io/client-go/pkg/util/uuid"
|
||||
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
)
|
||||
|
||||
type ns struct {
|
||||
ipt *iptables.IPTables // interface to iptables
|
||||
ips ipset.Interface // interface to ipset
|
||||
|
||||
name string // k8s Namespace name
|
||||
namespace *coreapi.Namespace // k8s Namespace object
|
||||
pods map[types.UID]*coreapi.Pod // k8s Pod objects by UID
|
||||
policies map[types.UID]*extnapi.NetworkPolicy // k8s NetworkPolicy objects by UID
|
||||
|
||||
uid types.UID // surrogate UID to own allPods selector
|
||||
allPods *selectorSpec // hash:ip ipset of all pod IPs in this namespace
|
||||
|
||||
nsSelectors *selectorSet
|
||||
podSelectors *selectorSet
|
||||
rules *ruleSet
|
||||
}
|
||||
|
||||
func newNS(name string, ipt *iptables.IPTables, ips ipset.Interface, nsSelectors *selectorSet) (*ns, error) {
|
||||
allPods, err := newSelectorSpec(&unversioned.LabelSelector{}, name, ipset.HashIP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ns := &ns{
|
||||
ipt: ipt,
|
||||
ips: ips,
|
||||
name: name,
|
||||
pods: make(map[types.UID]*coreapi.Pod),
|
||||
policies: make(map[types.UID]*extnapi.NetworkPolicy),
|
||||
uid: uuid.NewUUID(),
|
||||
allPods: allPods,
|
||||
nsSelectors: nsSelectors,
|
||||
rules: newRuleSet(ipt)}
|
||||
|
||||
ns.podSelectors = newSelectorSet(ips, ns.onNewPodSelector)
|
||||
|
||||
if err := ns.podSelectors.provision(ns.uid, nil, map[string]*selectorSpec{ns.allPods.key: ns.allPods}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
func (ns *ns) empty() bool {
|
||||
return len(ns.pods) == 0 && len(ns.policies) == 0 && ns.namespace == nil
|
||||
}
|
||||
|
||||
func (ns *ns) destroy() error {
|
||||
if err := ns.podSelectors.deprovision(ns.uid, map[string]*selectorSpec{ns.allPods.key: ns.allPods}, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) onNewPodSelector(selector *selector) error {
|
||||
for _, pod := range ns.pods {
|
||||
if hasIP(pod) {
|
||||
if selector.matches(pod.ObjectMeta.Labels) {
|
||||
if err := selector.addEntry(pod.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) addPod(obj *coreapi.Pod) error {
|
||||
ns.pods[obj.ObjectMeta.UID] = obj
|
||||
|
||||
if !hasIP(obj) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ns.podSelectors.addToMatching(obj.ObjectMeta.Labels, obj.Status.PodIP)
|
||||
}
|
||||
|
||||
func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
delete(ns.pods, oldObj.ObjectMeta.UID)
|
||||
ns.pods[newObj.ObjectMeta.UID] = newObj
|
||||
|
||||
if !hasIP(oldObj) && !hasIP(newObj) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if hasIP(oldObj) && !hasIP(newObj) {
|
||||
return ns.podSelectors.delFromMatching(oldObj.ObjectMeta.Labels, oldObj.Status.PodIP)
|
||||
}
|
||||
|
||||
if !hasIP(oldObj) && hasIP(newObj) {
|
||||
return ns.podSelectors.addToMatching(newObj.ObjectMeta.Labels, newObj.Status.PodIP)
|
||||
}
|
||||
|
||||
if !equals(oldObj.ObjectMeta.Labels, newObj.ObjectMeta.Labels) ||
|
||||
oldObj.Status.PodIP != newObj.Status.PodIP {
|
||||
|
||||
for _, ps := range ns.podSelectors.entries {
|
||||
oldMatch := ps.matches(oldObj.ObjectMeta.Labels)
|
||||
newMatch := ps.matches(newObj.ObjectMeta.Labels)
|
||||
if oldMatch == newMatch && oldObj.Status.PodIP == newObj.Status.PodIP {
|
||||
continue
|
||||
}
|
||||
if oldMatch {
|
||||
if err := ps.delEntry(oldObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if newMatch {
|
||||
if err := ps.addEntry(newObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
||||
delete(ns.pods, obj.ObjectMeta.UID)
|
||||
|
||||
if !hasIP(obj) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ns.podSelectors.delFromMatching(obj.ObjectMeta.Labels, obj.Status.PodIP)
|
||||
}
|
||||
|
||||
func (ns *ns) addNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
||||
ns.policies[obj.ObjectMeta.UID] = obj
|
||||
|
||||
// Analyse policy, determine which rules and ipsets are required
|
||||
rules, nsSelectors, podSelectors, err := ns.analysePolicy(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Provision required resources in dependency order
|
||||
if err := ns.nsSelectors.provision(obj.ObjectMeta.UID, nil, nsSelectors); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.podSelectors.provision(obj.ObjectMeta.UID, nil, podSelectors); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.rules.provision(obj.ObjectMeta.UID, nil, rules); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) updateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error {
|
||||
delete(ns.policies, oldObj.ObjectMeta.UID)
|
||||
ns.policies[newObj.ObjectMeta.UID] = newObj
|
||||
|
||||
// Analyse the old and the new policy so we can determine differences
|
||||
oldRules, oldNsSelectors, oldPodSelectors, err := ns.analysePolicy(oldObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newRules, newNsSelectors, newPodSelectors, err := ns.analysePolicy(newObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Deprovision unused and provision newly required resources in dependency order
|
||||
if err := ns.rules.deprovision(oldObj.ObjectMeta.UID, oldRules, newRules); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.nsSelectors.deprovision(oldObj.ObjectMeta.UID, oldNsSelectors, newNsSelectors); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.podSelectors.deprovision(oldObj.ObjectMeta.UID, oldPodSelectors, newPodSelectors); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.nsSelectors.provision(oldObj.ObjectMeta.UID, oldNsSelectors, newNsSelectors); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.podSelectors.provision(oldObj.ObjectMeta.UID, oldPodSelectors, newNsSelectors); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.rules.provision(oldObj.ObjectMeta.UID, oldRules, newRules); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) deleteNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
||||
delete(ns.policies, obj.ObjectMeta.UID)
|
||||
|
||||
// Analyse network policy to free resources
|
||||
rules, nsSelectors, podSelectors, err := ns.analysePolicy(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Deprovision unused resources in dependency order
|
||||
if err := ns.rules.deprovision(obj.ObjectMeta.UID, rules, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.nsSelectors.deprovision(obj.ObjectMeta.UID, nsSelectors, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.podSelectors.deprovision(obj.ObjectMeta.UID, podSelectors, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func bypassRule(nsIpsetName ipset.Name) []string {
|
||||
return []string{"-m", "set", "--match-set", string(nsIpsetName), "dst", "-j", "ACCEPT"}
|
||||
}
|
||||
|
||||
func (ns *ns) ensureBypassRule(nsIpsetName ipset.Name) error {
|
||||
return ns.ipt.Append(TableFilter, DefaultChain, bypassRule(ns.allPods.ipsetName)...)
|
||||
}
|
||||
|
||||
func (ns *ns) deleteBypassRule(nsIpsetName ipset.Name) error {
|
||||
return ns.ipt.Delete(TableFilter, DefaultChain, bypassRule(ns.allPods.ipsetName)...)
|
||||
}
|
||||
|
||||
func (ns *ns) addNamespace(obj *coreapi.Namespace) error {
|
||||
ns.namespace = obj
|
||||
|
||||
// Insert a rule to bypass policies if namespace is DefaultAllow
|
||||
if !isDefaultDeny(obj) {
|
||||
return ns.ensureBypassRule(ns.allPods.ipsetName)
|
||||
}
|
||||
|
||||
// Add namespace ipset to matching namespace selectors
|
||||
return ns.nsSelectors.addToMatching(obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
||||
}
|
||||
|
||||
func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
||||
ns.namespace = newObj
|
||||
|
||||
// Update bypass rule if ingress default has changed
|
||||
oldDefaultDeny := isDefaultDeny(oldObj)
|
||||
newDefaultDeny := isDefaultDeny(newObj)
|
||||
|
||||
if oldDefaultDeny != newDefaultDeny {
|
||||
if oldDefaultDeny {
|
||||
return ns.ensureBypassRule(ns.allPods.ipsetName)
|
||||
}
|
||||
if newDefaultDeny {
|
||||
return ns.deleteBypassRule(ns.allPods.ipsetName)
|
||||
}
|
||||
}
|
||||
|
||||
// Re-evaluate namespace selector membership if labels have changed
|
||||
if !equals(oldObj.ObjectMeta.Labels, newObj.ObjectMeta.Labels) {
|
||||
for _, selector := range ns.nsSelectors.entries {
|
||||
oldMatch := selector.matches(oldObj.ObjectMeta.Labels)
|
||||
newMatch := selector.matches(newObj.ObjectMeta.Labels)
|
||||
if oldMatch == newMatch {
|
||||
continue
|
||||
}
|
||||
if oldMatch {
|
||||
if err := selector.delEntry(string(ns.allPods.ipsetName)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if newMatch {
|
||||
if err := selector.addEntry(string(ns.allPods.ipsetName)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) deleteNamespace(obj *coreapi.Namespace) error {
|
||||
ns.namespace = nil
|
||||
|
||||
// Remove bypass rule
|
||||
if !isDefaultDeny(obj) {
|
||||
return ns.deleteBypassRule(ns.allPods.ipsetName)
|
||||
}
|
||||
|
||||
// Remove namespace ipset from any matching namespace selectors
|
||||
return ns.nsSelectors.delFromMatching(obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
||||
}
|
||||
|
||||
func hasIP(pod *coreapi.Pod) bool {
|
||||
// Ensure pod isn't dead, has an IP address and isn't sharing the host network namespace
|
||||
return pod.Status.Phase != "Succeeded" && pod.Status.Phase != "Failed" &&
|
||||
len(pod.Status.PodIP) > 0 && !pod.Spec.HostNetwork
|
||||
}
|
||||
|
||||
func equals(a, b map[string]string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for ak, av := range a {
|
||||
if b[ak] != av {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isDefaultDeny(namespace *coreapi.Namespace) bool {
|
||||
nnpJson, found := namespace.ObjectMeta.Annotations["net.beta.kubernetes.io/network-policy"]
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
|
||||
var nnp NamespaceNetworkPolicy
|
||||
if err := json.Unmarshal([]byte(nnpJson), &nnp); err != nil {
|
||||
log.Warn("Ignoring network policy annotation: unmarshal failed:", err)
|
||||
// If we can't understand the annotation, behave as if it isn't present
|
||||
return false
|
||||
}
|
||||
|
||||
return nnp.Ingress != nil &&
|
||||
nnp.Ingress.Isolation != nil &&
|
||||
*(nnp.Ingress.Isolation) == DefaultDeny
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"k8s.io/client-go/pkg/types"
|
||||
)
|
||||
|
||||
type ruleSpec struct {
|
||||
key string
|
||||
args []string
|
||||
}
|
||||
|
||||
func newRuleSpec(proto *string, srcHost *selectorSpec, dstHost *selectorSpec, dstPort *string) *ruleSpec {
|
||||
args := []string{}
|
||||
if proto != nil {
|
||||
args = append(args, "-p", *proto)
|
||||
}
|
||||
if srcHost != nil {
|
||||
args = append(args, "-m", "set", "--match-set", string(srcHost.ipsetName), "src")
|
||||
}
|
||||
if dstHost != nil {
|
||||
args = append(args, "-m", "set", "--match-set", string(dstHost.ipsetName), "dst")
|
||||
}
|
||||
if dstPort != nil {
|
||||
args = append(args, "--dport", *dstPort)
|
||||
}
|
||||
args = append(args, "-j", "ACCEPT")
|
||||
key := strings.Join(args, " ")
|
||||
|
||||
return &ruleSpec{key, args}
|
||||
}
|
||||
|
||||
type ruleSet struct {
|
||||
ipt *iptables.IPTables
|
||||
users map[string]map[types.UID]struct{}
|
||||
}
|
||||
|
||||
func newRuleSet(ipt *iptables.IPTables) *ruleSet {
|
||||
return &ruleSet{ipt, make(map[string]map[types.UID]struct{})}
|
||||
}
|
||||
|
||||
func (rs *ruleSet) deprovision(user types.UID, current, desired map[string]*ruleSpec) error {
|
||||
for key, spec := range current {
|
||||
if _, found := desired[key]; !found {
|
||||
delete(rs.users[key], user)
|
||||
if len(rs.users[key]) == 0 {
|
||||
log.Infof("deleting rule: %v", spec.args)
|
||||
if err := rs.ipt.Delete(TableFilter, IngressChain, spec.args...); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(rs.users, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *ruleSet) provision(user types.UID, current, desired map[string]*ruleSpec) error {
|
||||
for key, spec := range desired {
|
||||
if _, found := current[key]; !found {
|
||||
if _, found := rs.users[key]; !found {
|
||||
log.Infof("adding rule: %v", spec.args)
|
||||
if err := rs.ipt.Append(TableFilter, IngressChain, spec.args...); err != nil {
|
||||
return err
|
||||
}
|
||||
rs.users[key] = make(map[types.UID]struct{})
|
||||
}
|
||||
rs.users[key][user] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"k8s.io/client-go/pkg/api/unversioned"
|
||||
"k8s.io/client-go/pkg/labels"
|
||||
"k8s.io/client-go/pkg/types"
|
||||
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
)
|
||||
|
||||
type selectorSpec struct {
|
||||
key string // string representation (for hash keying/equality comparison)
|
||||
selector labels.Selector // k8s Selector object (for matching)
|
||||
|
||||
ipsetType ipset.Type // type of ipset to provision
|
||||
ipsetName ipset.Name // generated ipset name
|
||||
}
|
||||
|
||||
func newSelectorSpec(json *unversioned.LabelSelector, nsName string, ipsetType ipset.Type) (*selectorSpec, error) {
|
||||
selector, err := unversioned.LabelSelectorAsSelector(json)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := selector.String()
|
||||
return &selectorSpec{
|
||||
key: key,
|
||||
selector: selector,
|
||||
// We prefix the selector string with the namespace name when generating
|
||||
// the shortname because you can specify the same selector in multiple
|
||||
// namespaces - we need those to map to distinct ipsets
|
||||
ipsetName: ipset.Name("weave-" + shortName(nsName+":"+key)),
|
||||
ipsetType: ipsetType}, nil
|
||||
}
|
||||
|
||||
type selector struct {
|
||||
ips ipset.Interface
|
||||
spec *selectorSpec
|
||||
}
|
||||
|
||||
func (s *selector) matches(labelMap map[string]string) bool {
|
||||
return s.spec.selector.Matches(labels.Set(labelMap))
|
||||
}
|
||||
|
||||
func (s *selector) addEntry(entry string) error {
|
||||
log.Infof("adding entry %s to %s", entry, s.spec.ipsetName)
|
||||
return s.ips.AddEntry(s.spec.ipsetName, entry)
|
||||
}
|
||||
|
||||
func (s *selector) delEntry(entry string) error {
|
||||
log.Infof("deleting entry %s from %s", entry, s.spec.ipsetName)
|
||||
return s.ips.DelEntry(s.spec.ipsetName, entry)
|
||||
}
|
||||
|
||||
type selectorFn func(selector *selector) error
|
||||
|
||||
type selectorSet struct {
|
||||
ips ipset.Interface
|
||||
onNewSelector selectorFn
|
||||
users map[string]map[types.UID]struct{} // list of users per selector
|
||||
entries map[string]*selector
|
||||
}
|
||||
|
||||
func newSelectorSet(ips ipset.Interface, onNewSelector selectorFn) *selectorSet {
|
||||
return &selectorSet{
|
||||
ips: ips,
|
||||
onNewSelector: onNewSelector,
|
||||
users: make(map[string]map[types.UID]struct{}),
|
||||
entries: make(map[string]*selector)}
|
||||
}
|
||||
|
||||
func (ss *selectorSet) addToMatching(labelMap map[string]string, entry string) error {
|
||||
for _, s := range ss.entries {
|
||||
if s.matches(labelMap) {
|
||||
if err := s.addEntry(entry); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *selectorSet) delFromMatching(labelMap map[string]string, entry string) error {
|
||||
for _, s := range ss.entries {
|
||||
if s.matches(labelMap) {
|
||||
if err := s.delEntry(entry); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*selectorSpec) error {
|
||||
for key, spec := range current {
|
||||
if _, found := desired[key]; !found {
|
||||
delete(ss.users[key], user)
|
||||
if len(ss.users[key]) == 0 {
|
||||
log.Infof("destroying ipset: %#v", spec)
|
||||
if err := ss.ips.Destroy(spec.ipsetName); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(ss.entries, key)
|
||||
delete(ss.users, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *selectorSet) provision(user types.UID, current, desired map[string]*selectorSpec) error {
|
||||
for key, spec := range desired {
|
||||
if _, found := current[key]; !found {
|
||||
if _, found := ss.users[key]; !found {
|
||||
log.Infof("creating ipset: %#v", spec)
|
||||
if err := ss.ips.Create(spec.ipsetName, spec.ipsetType); err != nil {
|
||||
return err
|
||||
}
|
||||
selector := &selector{ss.ips, spec}
|
||||
if err := ss.onNewSelector(selector); err != nil {
|
||||
return err
|
||||
}
|
||||
ss.users[key] = make(map[types.UID]struct{})
|
||||
ss.entries[key] = selector
|
||||
}
|
||||
ss.users[key][user] = struct{}{}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
const (
|
||||
// This array:
|
||||
// * Must include only printable UTF8 characters that are represented with a single byte
|
||||
// * Must be at least of length 85 (`len("weave-") + l(2^160)/l(85)` equals 31, the maximum ipset name length)
|
||||
// * Must not include commas as those are treated specially by `ipset add` when adding a named set to a list:set
|
||||
// * Should not include space for readability
|
||||
// * Should not include single quote or backslash to be nice to shell users
|
||||
ShortNameSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789(){}[]<>_$%^&*|/?.;:@#~"
|
||||
)
|
||||
|
||||
// sha1 hash an arbitrary string and represent it using the full range of
|
||||
// printable ascii characters
|
||||
func shortName(arbitrary string) string {
|
||||
symbols := []byte(ShortNameSymbols)
|
||||
sum := sha1.Sum([]byte(arbitrary))
|
||||
i := big.NewInt(0).SetBytes(sum[:])
|
||||
base := big.NewInt(int64(len(symbols)))
|
||||
zero := big.NewInt(0)
|
||||
result := make([]byte, 0)
|
||||
|
||||
for i.Cmp(zero) > 0 {
|
||||
remainder := new(big.Int).Mod(i, base)
|
||||
i.Sub(i, remainder)
|
||||
i.Div(i, base)
|
||||
result = append(result, symbols[remainder.Int64()])
|
||||
}
|
||||
|
||||
return string(result)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package ulogd
|
||||
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func waitForExit(cmd *exec.Cmd) {
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Fatalf("ulogd terminated: %v", err)
|
||||
}
|
||||
log.Fatal("ulogd terminated normally")
|
||||
}
|
||||
|
||||
func Start() error {
|
||||
cmd := exec.Command("/usr/sbin/ulogd", "-v")
|
||||
stdout, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
go io.Copy(os.Stdout, stdout)
|
||||
go waitForExit(cmd)
|
||||
return nil
|
||||
}
|
||||
@@ -72,9 +72,8 @@ func splitPoolID(poolID string) (subnet, iprange *net.IPNet, err error) {
|
||||
func (i *Ipam) RequestAddress(poolID string, address net.IP, options map[string]string) (ip *net.IPNet, _ map[string]string, err error) {
|
||||
i.logReq("RequestAddress", poolID, address, options)
|
||||
defer func() { i.logRes("RequestAddress", err, ip) }()
|
||||
// If we pass magic string "_" to weave IPAM it stores the address under its own string
|
||||
if poolID == "weavepool" { // old-style
|
||||
ip, err = i.weave.AllocateIP("_")
|
||||
ip, err = i.weave.AllocateIP(api.NoContainerID)
|
||||
return
|
||||
}
|
||||
subnet, iprange, err := splitPoolID(poolID)
|
||||
@@ -83,12 +82,12 @@ func (i *Ipam) RequestAddress(poolID string, address net.IP, options map[string]
|
||||
}
|
||||
if address != nil { // try to claim specific address requested
|
||||
ip = &net.IPNet{IP: address, Mask: subnet.Mask}
|
||||
if err = i.weave.ClaimIP("_", ip); err != nil {
|
||||
if err = i.weave.ClaimIP(api.NoContainerID, ip); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// We are lying slightly to IPAM here: the range is not a subnet
|
||||
if ip, err = i.weave.AllocateIPInSubnet("_", iprange); err != nil {
|
||||
if ip, err = i.weave.AllocateIPInSubnet(api.NoContainerID, iprange); err != nil {
|
||||
return
|
||||
}
|
||||
ip.Mask = subnet.Mask // fix up the subnet we lied about
|
||||
|
||||
+14
-18
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/appc/cni/pkg/ipam"
|
||||
"github.com/appc/cni/pkg/skel"
|
||||
@@ -12,7 +13,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"
|
||||
)
|
||||
@@ -84,6 +84,9 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
||||
if err := assignBridgeIP(conf.BrName, bridgeIPResult.IP4.IP); err != nil {
|
||||
return fmt.Errorf("unable to assign IP address to bridge: %s", err)
|
||||
}
|
||||
if err := weavenet.ExposeNAT(bridgeIPResult.IP4.IP); err != nil {
|
||||
return fmt.Errorf("unable to create NAT rules: %s", err)
|
||||
}
|
||||
bridgeIP = bridgeIPResult.IP4.IP.IP
|
||||
} else if err != nil {
|
||||
return err
|
||||
@@ -93,7 +96,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()
|
||||
|
||||
@@ -107,7 +110,7 @@ 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.WithNetNSLinkUnsafe(ns, args.IfName, func(link netlink.Link) error {
|
||||
@@ -151,6 +154,11 @@ func assignBridgeIP(bridgeName string, ipnet net.IPNet) error {
|
||||
return err
|
||||
}
|
||||
if err := netlink.AddrAdd(link, &netlink.Addr{IPNet: &ipnet}); err != nil {
|
||||
// Treat as non-error if this address is already there
|
||||
// - maybe another copy of this program just added it
|
||||
if err == syscall.Errno(syscall.EEXIST) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to add IP address to %q: %v", bridgeName, err)
|
||||
}
|
||||
return nil
|
||||
@@ -159,7 +167,7 @@ func assignBridgeIP(bridgeName string, ipnet net.IPNet) error {
|
||||
var errBridgeNoIP = fmt.Errorf("Bridge has no IP address")
|
||||
|
||||
func findBridgeIP(bridgeName string, subnet net.IPNet) (net.IP, error) {
|
||||
netdev, 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)
|
||||
}
|
||||
@@ -181,20 +189,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.WithNetNSUnsafe(ns, func() error {
|
||||
link, err := netlink.LinkByName(args.IfName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return netlink.LinkDel(link)
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error removing interface: %s", err)
|
||||
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"]
|
||||
+1
-1
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/weaveworks/weave/plugin/skel"
|
||||
)
|
||||
|
||||
var version = "(unreleased version)"
|
||||
var version = "unreleased"
|
||||
|
||||
var Log = common.Log
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM DOCKERHUB_USER/weave
|
||||
ADD ./launch.sh ./kube-peers /home/weave/
|
||||
ENTRYPOINT ["/home/weave/launch.sh"]
|
||||
Executable
+161
@@ -0,0 +1,161 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# Default if not supplied - same as weave net default
|
||||
IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
|
||||
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
||||
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
|
||||
|
||||
# Check if the IP range overlaps anything existing on the host
|
||||
/usr/bin/weaveutil netcheck $IPALLOC_RANGE weave
|
||||
|
||||
# Default for network policy
|
||||
EXPECT_NPC=${EXPECT_NPC:-1}
|
||||
|
||||
# kube-proxy requires that bridged traffic passes through netfilter
|
||||
if [ ! -f /proc/sys/net/bridge/bridge-nf-call-iptables ] ; then
|
||||
echo /proc/sys/net/bridge/bridge-nf-call-iptables not found >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables
|
||||
|
||||
SOURCE_BINARY=/usr/bin/weaveutil
|
||||
VERSION=$(/home/weave/weaver $EXTRA_ARGS --version | sed -e 's/weave router //')
|
||||
PLUGIN="weave-plugin-$VERSION"
|
||||
|
||||
install_cni_plugin() {
|
||||
mkdir -p $1 || return 1
|
||||
if [ ! -f "$1/$PLUGIN" ]; then
|
||||
cp "$SOURCE_BINARY" "$1/$PLUGIN"
|
||||
fi
|
||||
}
|
||||
|
||||
upgrade_cni_plugin_symlink() {
|
||||
# Remove potential temporary symlink from previous failed upgrade:
|
||||
rm -f $1/$2.tmp
|
||||
# Atomically create a symlink to the plugin:
|
||||
ln -s "$1/$PLUGIN" $1/$2.tmp && mv -f $1/$2.tmp $1/$2
|
||||
}
|
||||
|
||||
upgrade_cni_plugin() {
|
||||
# Check if weave-net and weave-ipam are (legacy) copies of the plugin, and
|
||||
# if so remove these so symlinks can be used instead from now onwards.
|
||||
if [ -f $1/weave-net -a ! -L $1/weave-net ]; then rm $1/weave-net; fi
|
||||
if [ -f $1/weave-ipam -a ! -L $1/weave-ipam ]; then rm $1/weave-ipam; fi
|
||||
|
||||
# Create two symlinks to the plugin, as it has a different
|
||||
# behaviour depending on its name:
|
||||
if [ "$(readlink -f $1/weave-net)" != "$1/$PLUGIN" ]; then
|
||||
upgrade_cni_plugin_symlink $1 weave-net
|
||||
fi
|
||||
if [ "$(readlink -f $1/weave-ipam)" != "$1/$PLUGIN" ]; then
|
||||
upgrade_cni_plugin_symlink $1 weave-ipam
|
||||
fi
|
||||
}
|
||||
|
||||
# Install CNI plugin binary to typical CNI bin location
|
||||
# with fall-back to CNI directory used by kube-up on GCI OS.
|
||||
if install_cni_plugin /opt/cni/bin ; then
|
||||
upgrade_cni_plugin /opt/cni/bin
|
||||
elif install_cni_plugin /host_home/kubernetes/bin ; then
|
||||
upgrade_cni_plugin /host_home/kubernetes/bin
|
||||
else
|
||||
echo "Failed to install the Weave CNI plugin" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Need to create bridge before running weaver so we can use the peer address
|
||||
# (because of https://github.com/weaveworks/weave/issues/2480)
|
||||
WEAVE_NPC_OPTS="--expect-npc"
|
||||
if [ "${EXPECT_NPC}" = "0" ]; then
|
||||
WEAVE_NPC_OPTS=""
|
||||
fi
|
||||
|
||||
/home/weave/weave --local create-bridge --force $WEAVE_NPC_OPTS
|
||||
|
||||
# Kubernetes sets HOSTNAME to the host's hostname
|
||||
# when running a pod in host namespace.
|
||||
NICKNAME_ARG=""
|
||||
if [ -n "$HOSTNAME" ] ; then
|
||||
NICKNAME_ARG="--nickname=$HOSTNAME"
|
||||
fi
|
||||
|
||||
BRIDGE_OPTIONS="--datapath=datapath"
|
||||
if [ "$(/home/weave/weave --local bridge-type)" = "bridge" ] ; then
|
||||
# TODO: Call into weave script to do this
|
||||
if ! ip link show vethwe-pcap >/dev/null 2>&1 ; then
|
||||
ip link add name vethwe-bridge type veth peer name vethwe-pcap
|
||||
ip link set vethwe-bridge up
|
||||
ip link set vethwe-pcap up
|
||||
ip link set vethwe-bridge master weave
|
||||
fi
|
||||
BRIDGE_OPTIONS="--iface=vethwe-pcap"
|
||||
fi
|
||||
|
||||
if [ -z "$KUBE_PEERS" ]; then
|
||||
if ! KUBE_PEERS=$(/home/weave/kube-peers) || [ -z "$KUBE_PEERS" ]; then
|
||||
echo Failed to get peers >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
peer_count() {
|
||||
echo $#
|
||||
}
|
||||
|
||||
if [ -z "$IPALLOC_INIT" ]; then
|
||||
IPALLOC_INIT="consensus=$(peer_count $KUBE_PEERS)"
|
||||
fi
|
||||
|
||||
/home/weave/weaver $EXTRA_ARGS --port=6783 $BRIDGE_OPTIONS \
|
||||
--http-addr=$HTTP_ADDR --status-addr=$STATUS_ADDR --docker-api='' --no-dns \
|
||||
--ipalloc-range=$IPALLOC_RANGE $NICKNAME_ARG \
|
||||
--ipalloc-init $IPALLOC_INIT \
|
||||
--name=$(cat /sys/class/net/weave/address) "$@" \
|
||||
$KUBE_PEERS &
|
||||
WEAVE_PID=$!
|
||||
|
||||
# Wait for weave process to become responsive
|
||||
while true ; do
|
||||
curl $HTTP_ADDR/status >/dev/null 2>&1 && break
|
||||
if ! kill -0 $WEAVE_PID >/dev/null 2>&1 ; then
|
||||
echo Weave process has died >&2
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
reclaim_ips() {
|
||||
ID=$1
|
||||
shift
|
||||
for CIDR in "$@" ; do
|
||||
curl -s -S -X PUT "$HTTP_ADDR/ip/$ID/$CIDR" || true
|
||||
done
|
||||
}
|
||||
|
||||
# Tell the newly-started weave about existing weave bridge IPs
|
||||
/usr/bin/weaveutil container-addrs weave weave:expose | while read ID IFACE MAC IPS; do
|
||||
reclaim_ips "weave:expose" $IPS
|
||||
done
|
||||
# Tell weave about existing weave process IPs
|
||||
/usr/bin/weaveutil process-addrs weave | while read ID IFACE MAC IPS; do
|
||||
reclaim_ips "_" $IPS
|
||||
done
|
||||
|
||||
# Create CNI config, if not already there
|
||||
if [ ! -f /etc/cni/net.d/10-weave.conf ] ; then
|
||||
mkdir -p /etc/cni/net.d
|
||||
cat > /etc/cni/net.d/10-weave.conf <<EOF
|
||||
{
|
||||
"name": "weave",
|
||||
"type": "weave-net"
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Expose the weave network so host processes can communicate with pods
|
||||
/home/weave/weave --local expose $WEAVE_EXPOSE_IP
|
||||
|
||||
wait $WEAVE_PID
|
||||
@@ -0,0 +1,70 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: weave-net
|
||||
annotations:
|
||||
scheduler.alpha.kubernetes.io/tolerations: |
|
||||
[
|
||||
{
|
||||
"key": "dedicated",
|
||||
"operator": "Equal",
|
||||
"value": "master",
|
||||
"effect": "NoSchedule"
|
||||
}
|
||||
]
|
||||
spec:
|
||||
hostNetwork: true
|
||||
hostPID: true
|
||||
containers:
|
||||
- name: weave
|
||||
image: weaveworks/weave-kube:latest
|
||||
imagePullPolicy: Always
|
||||
command:
|
||||
- /home/weave/launch.sh
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 30
|
||||
httpGet:
|
||||
host: 127.0.0.1
|
||||
path: /status
|
||||
port: 6784
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: weavedb
|
||||
mountPath: /weavedb
|
||||
- name: cni-bin
|
||||
mountPath: /opt
|
||||
- name: cni-bin2
|
||||
mountPath: /host_home
|
||||
- name: cni-conf
|
||||
mountPath: /etc
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
- name: weave-npc
|
||||
image: weaveworks/weave-npc:latest
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
securityContext:
|
||||
privileged: true
|
||||
restartPolicy: Always
|
||||
volumes:
|
||||
- name: weavedb
|
||||
emptyDir: {}
|
||||
- name: cni-bin
|
||||
hostPath:
|
||||
path: /opt
|
||||
- name: cni-bin2
|
||||
hostPath:
|
||||
path: /home
|
||||
- name: cni-conf
|
||||
hostPath:
|
||||
path: /etc
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM alpine
|
||||
RUN apk add --update \
|
||||
iptables \
|
||||
ipset \
|
||||
ulogd \
|
||||
&& rm -rf /var/cache/apk/* \
|
||||
&& mknod /var/log/ulogd.pcap p
|
||||
COPY ./weave-npc /usr/bin/weave-npc
|
||||
COPY ./ulogd.conf /etc/ulogd.conf
|
||||
ENTRYPOINT ["/usr/bin/weave-npc"]
|
||||
@@ -0,0 +1,180 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
coreapi "k8s.io/client-go/pkg/api/v1"
|
||||
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||
"k8s.io/client-go/pkg/fields"
|
||||
"k8s.io/client-go/pkg/runtime"
|
||||
"k8s.io/client-go/pkg/util/wait"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"github.com/weaveworks/weave/npc"
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
"github.com/weaveworks/weave/npc/metrics"
|
||||
"github.com/weaveworks/weave/npc/ulogd"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "unreleased"
|
||||
metricsAddr string
|
||||
)
|
||||
|
||||
func handleError(err error) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func makeController(getter cache.Getter, resource string,
|
||||
objType runtime.Object, handlers cache.ResourceEventHandlerFuncs) *cache.Controller {
|
||||
listWatch := cache.NewListWatchFromClient(getter, resource, "", fields.Everything())
|
||||
_, controller := cache.NewInformer(listWatch, objType, 0, handlers)
|
||||
return controller
|
||||
}
|
||||
|
||||
func resetIPTables(ipt *iptables.IPTables) error {
|
||||
// Flush chains first so there are no refs to extant ipsets
|
||||
if err := ipt.ClearChain(npc.TableFilter, npc.IngressChain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ipt.ClearChain(npc.TableFilter, npc.DefaultChain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ipt.ClearChain(npc.TableFilter, npc.MainChain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Configure main chain static rules
|
||||
if err := ipt.Append(npc.TableFilter, npc.MainChain,
|
||||
"-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ipt.Append(npc.TableFilter, npc.MainChain,
|
||||
"-m", "state", "--state", "NEW", "-j", string(npc.DefaultChain)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ipt.Append(npc.TableFilter, npc.MainChain,
|
||||
"-m", "state", "--state", "NEW", "-j", string(npc.IngressChain)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetIPSets(ips ipset.Interface) error {
|
||||
// TODO should restrict ipset operations to the `weave-` prefix:
|
||||
|
||||
if err := ips.FlushAll(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ips.DestroyAll(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func root(cmd *cobra.Command, args []string) {
|
||||
log.Infof("Starting Weaveworks NPC %s", version)
|
||||
|
||||
if err := metrics.Start(metricsAddr); err != nil {
|
||||
log.Fatalf("Failed to start metrics: %v", err)
|
||||
}
|
||||
|
||||
if err := ulogd.Start(); err != nil {
|
||||
log.Fatalf("Failed to start ulogd: %v", err)
|
||||
}
|
||||
|
||||
config, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ipt, err := iptables.New()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ips := ipset.New()
|
||||
|
||||
handleError(resetIPTables(ipt))
|
||||
handleError(resetIPSets(ips))
|
||||
|
||||
npc := npc.New(ipt, ips)
|
||||
|
||||
nsController := makeController(client.Core().RESTClient(), "namespaces", &coreapi.Namespace{},
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
handleError(npc.AddNamespace(obj.(*coreapi.Namespace)))
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
handleError(npc.DeleteNamespace(obj.(*coreapi.Namespace)))
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
handleError(npc.UpdateNamespace(old.(*coreapi.Namespace), new.(*coreapi.Namespace)))
|
||||
}})
|
||||
|
||||
podController := makeController(client.Core().RESTClient(), "pods", &coreapi.Pod{},
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
handleError(npc.AddPod(obj.(*coreapi.Pod)))
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
handleError(npc.DeletePod(obj.(*coreapi.Pod)))
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
handleError(npc.UpdatePod(old.(*coreapi.Pod), new.(*coreapi.Pod)))
|
||||
}})
|
||||
|
||||
npController := makeController(client.Extensions().RESTClient(), "networkpolicies", &extnapi.NetworkPolicy{},
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
handleError(npc.AddNetworkPolicy(obj.(*extnapi.NetworkPolicy)))
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
handleError(npc.DeleteNetworkPolicy(obj.(*extnapi.NetworkPolicy)))
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
handleError(npc.UpdateNetworkPolicy(old.(*extnapi.NetworkPolicy), new.(*extnapi.NetworkPolicy)))
|
||||
}})
|
||||
|
||||
go nsController.Run(wait.NeverStop)
|
||||
go podController.Run(wait.NeverStop)
|
||||
go npController.Run(wait.NeverStop)
|
||||
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
|
||||
log.Fatalf("Exiting: %v", <-signals)
|
||||
}
|
||||
|
||||
func main() {
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "weave-npc",
|
||||
Short: "Weaveworks Kubernetes Network Policy Controller",
|
||||
Run: root}
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&metricsAddr, "metrics-addr", ":6781", "metrics server bind address")
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
[global]
|
||||
logfile="/dev/null"
|
||||
plugin="/usr/lib/ulogd/ulogd_inppkt_NFLOG.so"
|
||||
plugin="/usr/lib/ulogd/ulogd_raw2packet_BASE.so"
|
||||
plugin="/usr/lib/ulogd/ulogd_output_PCAP.so"
|
||||
stack=log1:NFLOG,base1:BASE,pcap1:PCAP
|
||||
|
||||
[log1]
|
||||
group=86
|
||||
|
||||
[pcap1]
|
||||
file="/var/log/ulogd.pcap"
|
||||
sync=1
|
||||
@@ -5,13 +5,13 @@ 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"
|
||||
)
|
||||
|
||||
var version = "(unreleased version)"
|
||||
var version = "unreleased"
|
||||
|
||||
var Log = common.Log
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM scratch
|
||||
FROM DOCKERHUB_USER/weaveexec
|
||||
MAINTAINER Weaveworks Inc <help@weave.works>
|
||||
LABEL works.weave.role=system
|
||||
WORKDIR /home/weave
|
||||
+16
-13
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
var checker *checkpoint.Checker
|
||||
var newVersion atomic.Value
|
||||
var success atomic.Value
|
||||
|
||||
const (
|
||||
updateCheckPeriod = 6 * time.Hour
|
||||
@@ -17,9 +18,11 @@ const (
|
||||
|
||||
func checkForUpdates(dockerVersion string, network string) {
|
||||
newVersion.Store("")
|
||||
success.Store(true)
|
||||
|
||||
handleResponse := func(r *checkpoint.CheckResponse, err error) {
|
||||
if err != nil {
|
||||
success.Store(false)
|
||||
Log.Printf("Error checking version: %v", err)
|
||||
return
|
||||
}
|
||||
@@ -32,9 +35,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 +64,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])
|
||||
}
|
||||
+40
-22
@@ -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
|
||||
@@ -225,6 +240,7 @@ var ipamTemplate = defTemplate("ipamTemplate", `{{printIPAMRanges .Router .IPAM}
|
||||
|
||||
type VersionCheck struct {
|
||||
Enabled bool
|
||||
Success bool
|
||||
NewVersion string
|
||||
NextCheckAt time.Time
|
||||
}
|
||||
@@ -236,6 +252,7 @@ func versionCheck() *VersionCheck {
|
||||
}
|
||||
|
||||
v.Enabled = true
|
||||
v.Success = success.Load().(bool)
|
||||
v.NewVersion = newVersion.Load().(string)
|
||||
v.NextCheckAt = checker.NextCheckAt()
|
||||
|
||||
@@ -246,12 +263,12 @@ func (v *VersionCheck) String() string {
|
||||
switch {
|
||||
case !v.Enabled:
|
||||
return "version check update disabled"
|
||||
case !v.Success:
|
||||
return fmt.Sprintf("failed to check latest version - see logs; next check at %s", v.NextCheckAt.Format("2006/01/02 15:04:05"))
|
||||
case v.NewVersion != "":
|
||||
return fmt.Sprintf("version %s available - please upgrade!",
|
||||
v.NewVersion)
|
||||
return fmt.Sprintf("version %s available - please upgrade!", v.NewVersion)
|
||||
default:
|
||||
return fmt.Sprintf("up to date; next check at %s",
|
||||
v.NextCheckAt.Format("2006/01/02 15:04:05"))
|
||||
return fmt.Sprintf("up to date; next check at %s", v.NextCheckAt.Format("2006/01/02 15:04:05"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,6 +280,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{
|
||||
|
||||
+25
-5
@@ -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"
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
weave "github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
var version = "(unreleased version)"
|
||||
var version = "unreleased"
|
||||
|
||||
var Log = common.Log
|
||||
|
||||
@@ -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")
|
||||
@@ -224,6 +226,12 @@ func main() {
|
||||
overlay, bridge := createOverlay(datapathName, ifaceName, isAWSVPC, config.Host, config.Port, bufSzMB)
|
||||
networkConfig.Bridge = bridge
|
||||
|
||||
if bridge != nil {
|
||||
if err := weavenet.DetectHairpin("vethwe-bridge", Log); err != nil {
|
||||
Log.Errorf("DetectHairpin failed: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
name := peerName(routerName, bridge.Interface())
|
||||
|
||||
if nickName == "" {
|
||||
@@ -322,6 +330,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 ''".
|
||||
@@ -336,9 +345,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)
|
||||
@@ -554,7 +574,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
|
||||
@@ -564,7 +584,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
|
||||
}
|
||||
}
|
||||
+7
-12
@@ -4,9 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
|
||||
@@ -21,7 +19,7 @@ func containerAddrs(args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
pred, err := common.ConnectedToBridgePredicate(bridgeName)
|
||||
peerIDs, err := weavenet.ConnectedToBridgeVethPeerIds(bridgeName)
|
||||
if err != nil {
|
||||
if err == weavenet.ErrLinkNotFound {
|
||||
return nil
|
||||
@@ -34,11 +32,11 @@ func containerAddrs(args []string) error {
|
||||
|
||||
for _, cid := range args[1:] {
|
||||
if cid == "weave:expose" {
|
||||
netDev, err := common.GetBridgeNetDev(bridgeName)
|
||||
netDev, err := weavenet.GetBridgeNetDev(bridgeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printNetDevs(cid, []common.NetDev{netDev})
|
||||
printNetDevs(cid, []weavenet.Dev{netDev})
|
||||
continue
|
||||
}
|
||||
if containers[cid], err = client.InspectContainer(cid); err != nil {
|
||||
@@ -51,11 +49,8 @@ func containerAddrs(args []string) error {
|
||||
containerIDs = append(containerIDs, cid)
|
||||
}
|
||||
|
||||
// NB: Because network namespaces (netns) are changed many times inside the loop,
|
||||
// it's NOT safe to exec any code depending on the root netns without
|
||||
// wrapping with WithNetNS*.
|
||||
for _, cid := range containerIDs {
|
||||
netDevs, err := getNetDevs(client, containers[cid], pred)
|
||||
netDevs, err := getNetDevs(client, containers[cid], peerIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -65,14 +60,14 @@ func containerAddrs(args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getNetDevs(c *docker.Client, container *docker.Container, pred func(netlink.Link) bool) ([]common.NetDev, error) {
|
||||
func getNetDevs(c *docker.Client, container *docker.Container, peerIDs []int) ([]weavenet.Dev, error) {
|
||||
if container.State.Pid == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return common.GetNetDevsWithPredicate(container.State.Pid, pred)
|
||||
return weavenet.GetNetDevsByVethPeerIds(container.State.Pid, peerIDs)
|
||||
}
|
||||
|
||||
func printNetDevs(cid string, netDevs []common.NetDev) {
|
||||
func printNetDevs(cid string, netDevs []weavenet.Dev) {
|
||||
for _, netDev := range netDevs {
|
||||
fmt.Printf("%12s %s %s", cid, netDev.Name, netDev.MAC.String())
|
||||
for _, cidr := range netDev.CIDRs {
|
||||
|
||||
+14
-13
@@ -32,10 +32,15 @@ func attach(args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
pid, nsContainer, err := containerPidAndNs(args[0])
|
||||
pid, err := containerPid(args[0])
|
||||
if err != nil {
|
||||
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" {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
|
||||
func exposeNAT(args []string) error {
|
||||
if len(args) < 1 {
|
||||
cmdUsage("expose-nat", "<cidr>...")
|
||||
}
|
||||
|
||||
cidrs, err := parseCIDRs(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, cidr := range cidrs {
|
||||
if err := weavenet.ExposeNAT(*cidr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -4,6 +4,10 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
|
||||
var commands map[string]func([]string) error
|
||||
@@ -20,12 +24,39 @@ 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,
|
||||
"expose-nat": exposeNAT,
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
+2
-3
@@ -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,7 +148,7 @@ func NewProxy(c Config) (*Proxy, error) {
|
||||
p.client = client.Client
|
||||
|
||||
if !p.WithoutDNS {
|
||||
netDev, err := common.GetBridgeNetDev(c.DockerBridge)
|
||||
netDev, err := weavenet.GetBridgeNetDev(c.DockerBridge)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+78
-8
@@ -50,7 +50,8 @@ type FastDatapath struct {
|
||||
seenMACs map[MAC]struct{}
|
||||
|
||||
// vxlan vports associated with the given UDP ports
|
||||
vxlanVportIDs map[int]odp.VportID
|
||||
vxlanUDPPorts map[int]odp.VportID
|
||||
vxlanVportIDs map[odp.VportID]struct{}
|
||||
mainVxlanVportID odp.VportID
|
||||
|
||||
// A singleton pool for the occasions when we need to decode
|
||||
@@ -87,7 +88,8 @@ func NewFastDatapath(iface *net.Interface, port int) (*FastDatapath, error) {
|
||||
sendToPort: nil,
|
||||
sendToMAC: make(map[MAC]bridgeSender),
|
||||
seenMACs: make(map[MAC]struct{}),
|
||||
vxlanVportIDs: make(map[int]odp.VportID),
|
||||
vxlanUDPPorts: make(map[int]odp.VportID),
|
||||
vxlanVportIDs: make(map[odp.VportID]struct{}),
|
||||
forwarders: make(map[mesh.PeerName]*fastDatapathForwarder),
|
||||
}
|
||||
|
||||
@@ -318,6 +320,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) {
|
||||
@@ -374,15 +381,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()
|
||||
@@ -415,7 +435,7 @@ func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
|
||||
fastdp.lock.Lock()
|
||||
defer fastdp.lock.Unlock()
|
||||
|
||||
if vxlanVportID, present := fastdp.vxlanVportIDs[udpPort]; present {
|
||||
if vxlanVportID, present := fastdp.vxlanUDPPorts[udpPort]; present {
|
||||
return vxlanVportID, nil
|
||||
}
|
||||
|
||||
@@ -441,7 +461,8 @@ func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
|
||||
}
|
||||
}
|
||||
|
||||
fastdp.vxlanVportIDs[udpPort] = vxlanVportID
|
||||
fastdp.vxlanUDPPorts[udpPort] = vxlanVportID
|
||||
fastdp.vxlanVportIDs[vxlanVportID] = struct{}{}
|
||||
fastdp.missHandlers[vxlanVportID] = func(fks odp.FlowKeys, lock *fastDatapathLock) FlowOp {
|
||||
log.Debug("ODP miss: ", fks, " on port ", vxlanVportID)
|
||||
tunnel := fks[odp.OVS_KEY_ATTR_TUNNEL].(odp.TunnelFlowKey)
|
||||
@@ -1116,6 +1137,11 @@ func (fastdp *FastDatapath) send(fops FlowOp, frame []byte, lock *fastDatapathLo
|
||||
}
|
||||
}
|
||||
|
||||
if fastdp.isHairpinFlow(&flow) {
|
||||
log.Error("Vetoed installation of hairpin flow ", flow)
|
||||
return
|
||||
}
|
||||
|
||||
if dec != nil {
|
||||
// put the decoder back
|
||||
lock.relock()
|
||||
@@ -1153,6 +1179,50 @@ func (fastdp *FastDatapath) takeDecoder(lock *fastDatapathLock) *EthernetDecoder
|
||||
return dec
|
||||
}
|
||||
|
||||
// A isHairpinFlow checks whether the flow is created due to enabled hairpin
|
||||
// mode on the weave bridge port which attaches the datapath. Such flow is
|
||||
// identified by either:
|
||||
//
|
||||
// * in_port == out_port, where in_port is non-vxlan vport;
|
||||
// * a packet is sent back to a vxlan tunnel it has been received from and
|
||||
// the tunnel id is either the same or dstPeer and srcPeer are reversed.
|
||||
func (fastdp *FastDatapath) isHairpinFlow(flow *odp.FlowSpec) bool {
|
||||
var (
|
||||
vxlanKey odp.TunnelAttrs
|
||||
inVport odp.VportID
|
||||
inVxlan bool
|
||||
)
|
||||
|
||||
for _, key := range flow.FlowKeys {
|
||||
switch k := key.(type) {
|
||||
case odp.InPortFlowKey:
|
||||
inVport = k.VportID()
|
||||
case odp.TunnelFlowKey:
|
||||
inVxlan = true
|
||||
vxlanKey = k.Key()
|
||||
}
|
||||
}
|
||||
|
||||
for _, action := range flow.Actions {
|
||||
switch a := action.(type) {
|
||||
case odp.SetTunnelAction:
|
||||
if inVxlan && a.TunnelAttrs.TunnelId == vxlanKey.TunnelId &&
|
||||
a.TunnelAttrs.Ipv4Src == vxlanKey.Ipv4Dst &&
|
||||
a.TunnelAttrs.Ipv4Dst == vxlanKey.Ipv4Src {
|
||||
return true
|
||||
}
|
||||
case odp.OutputAction:
|
||||
if a.VportID() == inVport {
|
||||
if _, ok := fastdp.vxlanVportIDs[a.VportID()]; !ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type odpActionsFlowOp struct {
|
||||
NonDiscardingFlowOp
|
||||
fastdp *FastDatapath
|
||||
|
||||
@@ -124,6 +124,13 @@ func (router *NetworkRouter) handleCapturedPacket(key PacketKey) FlowOp {
|
||||
}
|
||||
|
||||
func (router *NetworkRouter) handleForwardedPacket(key ForwardPacketKey) FlowOp {
|
||||
srcMac := net.HardwareAddr(key.SrcMAC[:])
|
||||
dstMac := net.HardwareAddr(key.DstMAC[:])
|
||||
|
||||
if key.SrcPeer == router.Ourself.Peer {
|
||||
log.Warn("Received own packet to peer ", key.DstPeer, " from MAC (", srcMac, ") to (", dstMac, ")")
|
||||
}
|
||||
|
||||
if key.DstPeer != router.Ourself.Peer {
|
||||
// it's not for us, we're just relaying it
|
||||
router.PacketLogging.LogForwardPacket("Relaying", key)
|
||||
@@ -134,9 +141,6 @@ func (router *NetworkRouter) handleForwardedPacket(key ForwardPacketKey) FlowOp
|
||||
// (because the DstPeer on a forwarded broadcast packet is
|
||||
// always set to the peer being forwarded to)
|
||||
|
||||
srcMac := net.HardwareAddr(key.SrcMAC[:])
|
||||
dstMac := net.HardwareAddr(key.DstMAC[:])
|
||||
|
||||
switch newSrcMac, conflictPeer := router.Macs.AddForced(srcMac, key.SrcPeer); {
|
||||
case newSrcMac:
|
||||
log.Print("Discovered remote MAC ", srcMac, " at ", key.SrcPeer)
|
||||
@@ -239,12 +243,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
|
||||
}
|
||||
@@ -3,6 +3,11 @@ title: Integrating Kubernetes and Mesos via the CNI Plugin
|
||||
menu_order: 65
|
||||
---
|
||||
|
||||
> The recommended way of using Weave with Kubernetes is via the new
|
||||
> [Kubernetes Addon](/site/kube-addon.md). The instructions below
|
||||
> remain valid however, and are still the recommended method for
|
||||
> integrating with Mesos.
|
||||
|
||||
CNI, the [_Container Network Interface_](https://github.com/containernetworking/cni),
|
||||
is a proposed standard for configuring network interfaces for Linux
|
||||
application containers. CNI is supported by
|
||||
|
||||
+11
-5
@@ -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,14 +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.
|
||||
|
||||
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)
|
||||
@@ -120,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
|
||||
|
||||
@@ -139,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,131 @@
|
||||
---
|
||||
title: Integrating Kubernetes via the Addon
|
||||
menu_order: 63
|
||||
---
|
||||
|
||||
The following topics are discussed:
|
||||
|
||||
* [Installation](#install)
|
||||
* [Network Policy Controller](#npc)
|
||||
* [Upgrading the Daemon Sets](#daemon-sets)
|
||||
* [Network Policy Controller](#npc)
|
||||
* [Troubleshooting Blocked Connections](#blocked-connections)
|
||||
* [Changing Configuration Options](#configuration-options)
|
||||
|
||||
|
||||
##<a name=”install”></a> Installation
|
||||
|
||||
Weave Net can be installed onto your CNI-enabled Kubernetes cluster
|
||||
with a single command:
|
||||
|
||||
```
|
||||
kubectl apply -f https://git.io/weave-kube
|
||||
```
|
||||
|
||||
After a few seconds, a Weave Net pod should be running on each
|
||||
Node and any further pods you create will be automatically attached to the Weave
|
||||
network.
|
||||
|
||||
**Note:** This command requires Kubernetes 1.4 or later.
|
||||
|
||||
> CNI, the [_Container Network Interface_](https://github.com/containernetworking/cni),
|
||||
> is a proposed standard for configuring network interfaces for Linux
|
||||
> containers.
|
||||
>
|
||||
> If you do not already have a CNI-enabled cluster, you can bootstrap
|
||||
> one easily with
|
||||
> [kubeadm](http://kubernetes.io/docs/getting-started-guides/kubeadm/).
|
||||
>
|
||||
> Alternatively, you can [configure CNI yourself](http://kubernetes.io/docs/admin/network-plugins/#cni)
|
||||
|
||||
**Note:** If using the [Weave CNI
|
||||
Plugin](/site/cni-plugin.md) from a prior full install of Weave Net with your
|
||||
cluster, you must first uninstall it before applying the Weave-kube addon.
|
||||
Shut down Kubernetes, and _on all nodes_ perform the following:
|
||||
|
||||
* `weave reset`
|
||||
* Remove any separate provisions you may have made to run Weave at
|
||||
boot-time, e.g. `systemd` units
|
||||
* `rm /opt/cni/bin/weave-*`
|
||||
|
||||
Then relaunch Kubernetes and install the addon as described
|
||||
above.
|
||||
|
||||
The URL [https://git.io/weave-kube](https://git.io/weave-kube) points
|
||||
to the YAML file for the latest release of the Weave Net addon.
|
||||
Historic versions are archived on our [GitHub release
|
||||
page](https://github.com/weaveworks/weave/releases).
|
||||
|
||||
###<a name=”daemon-sets”></a> Upgrading the Daemon Sets
|
||||
|
||||
Kubernetes does not currently support rolling upgrades of daemon sets,
|
||||
and so you will need to perform the procedure manually:
|
||||
|
||||
* Apply the updated addon manifest `kubectl apply -f https://git.io/weave-kube`
|
||||
* Kill each Weave Net pod with `kubectl delete` and then wait for it to reboot before moving on to the next pod.
|
||||
|
||||
##<a name="npc"></a>Network Policy Controller
|
||||
|
||||
The addon also supports the [Kubernetes policy
|
||||
API](http://kubernetes.io/docs/user-guide/networkpolicies/) so that
|
||||
you can securely isolate pods from each other based on namespaces and
|
||||
labels. For more information on configuring network policies in
|
||||
Kubernetes see the
|
||||
[walkthrough](http://kubernetes.io/docs/getting-started-guides/network-policy/walkthrough/)
|
||||
and the [NetworkPolicy API object
|
||||
definition](http://kubernetes.io/docs/api-reference/extensions/v1beta1/definitions/#_v1beta1_networkpolicy).
|
||||
|
||||
###<a name=”blocked-connections”></a> Troubleshooting Blocked Connections
|
||||
|
||||
If you suspect that legitimate traffic is being blocked by the Weave Network Policy Controller, check the `weave-npc` container's logs:
|
||||
|
||||
```
|
||||
$ kubectl logs $(kubectl get pods --all-namespaces | grep weave-net | awk '{print $2}') -n kube-system weave-npc
|
||||
```
|
||||
|
||||
When the Weave Network Policy Controller blocks a connection, it logs the following details about it:
|
||||
|
||||
* protocol used,
|
||||
* source IP and port,
|
||||
* destination IP and port,
|
||||
|
||||
as per the below example:
|
||||
|
||||
```
|
||||
TCP connection from 10.32.0.7:56648 to 10.32.0.11:80 blocked by Weave NPC.
|
||||
UDP connection from 10.32.0.7:56648 to 10.32.0.11:80 blocked by Weave NPC.
|
||||
```
|
||||
|
||||
###<a name=”configuration-options”></a> Changing Configuration Options
|
||||
|
||||
The default configuration settings can be changed by saving and editing the
|
||||
addon YAML before running `kubectl apply`. Additional arguments may be
|
||||
supplied to the Weave router process by adding them to the `command:`
|
||||
array in the YAML file.
|
||||
|
||||
Some parameters are changed by environment variables; these can be
|
||||
inserted into the YAML file like this:
|
||||
|
||||
```
|
||||
containers:
|
||||
- name: weave
|
||||
env:
|
||||
- name: IPALLOC_RANGE
|
||||
value: 10.0.0.0/16
|
||||
```
|
||||
|
||||
The list of variables you can set is:
|
||||
|
||||
* CHECKPOINT\_DISABLE - if set to 1, disable checking for new Weave Net
|
||||
versions (default is blank, i.e. check is enabled)
|
||||
* IPALLOC\_RANGE - the range of IP addresses used by Weave Net
|
||||
and the subnet they are placed in (CIDR format; default 10.32.0.0/12)
|
||||
* EXPECT\_NPC - set to 0 to disable Network Policy Controller (default is on)
|
||||
* KUBE\_PEERS - list of addresses of peers in the Kubernetes cluster
|
||||
(default is to fetch the list from the api-server)
|
||||
* IPALLOC\_INIT - set the initialization mode of the [IP Address
|
||||
Manager](/site/operational-guide/concepts.md#ip-address-manager)
|
||||
(defaults to consensus amongst the KUBE\_PEERS)
|
||||
* WEAVE\_EXPOSE\_IP - set the IP address used as a gateway from the
|
||||
Weave network to the host network - this is useful if you are
|
||||
configuring the addon as a static pod.
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
---
|
||||
title: Monitoring with Prometheus
|
||||
menu_order: 105
|
||||
---
|
||||
|
||||
Two endpoints are exposed: one for the Weave Net router, and, when deployed as
|
||||
a [Kubernetes Addon](/site/kube-addon.md), one for the [network policy
|
||||
controller](/site/kube-addon.md#npc).
|
||||
|
||||
### Router Metrics
|
||||
|
||||
The endpoint address is `localhost:6782`; the following metrics are
|
||||
exposed:
|
||||
|
||||
* `weave_connections` - Number of peer-to-peer connections.
|
||||
* `weave_connection_terminations_total` - Number of peer-to-peer
|
||||
connections terminated.
|
||||
* `weave_ips` - Number of IP addresses.
|
||||
* `weave_max_ips` - Size of IP address space used by allocator.
|
||||
* `weave_dns_entries` - Number of DNS entries.
|
||||
* `weave_flows` - Number of FastDP flows.
|
||||
|
||||
### Kubernetes Network Policy Controller Metrics
|
||||
|
||||
The endpoint address is `localhost:6781`; the following metric is
|
||||
exposed:
|
||||
|
||||
* `weavenpc_blocked_connections_total` - Connection attempts blocked
|
||||
by policy controller.
|
||||
|
||||
# Static Configuration for Weave Net
|
||||
|
||||
The following YAML fragment can be used in your Prometheus configuration to
|
||||
scrape the router metrics endpoint:
|
||||
|
||||
```
|
||||
scrape_configs:
|
||||
- job_name: 'weave'
|
||||
scrape_interval: 15s
|
||||
static_configs:
|
||||
- targets: ['localhost:6782']
|
||||
```
|
||||
|
||||
# Discovery Configuration for Kubernetes
|
||||
|
||||
If you're running Weave in conjunction with Kubernetes, it is possible
|
||||
to discover the endpoints for all nodes automatically. The following
|
||||
Kubernetes config will install and configure Prometheus 1.3 on your
|
||||
cluster and configure it to discover and scrape the Weave endpoints:
|
||||
|
||||
```
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: prometheus
|
||||
data:
|
||||
prometheus.yml: |-
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
scrape_configs:
|
||||
- job_name: 'weave'
|
||||
kubernetes_sd_configs:
|
||||
- api_server:
|
||||
role: pod
|
||||
relabel_configs:
|
||||
- source_labels: [__meta_kubernetes_namespace,__meta_kubernetes_pod_label_name]
|
||||
action: keep
|
||||
regex: ^kube-system;weave-net$
|
||||
- source_labels: [__meta_kubernetes_pod_container_name,__address__]
|
||||
action: replace
|
||||
target_label: __address__
|
||||
regex: ^weave;(.+?)(?::\d+)?$
|
||||
replacement: $1:6782
|
||||
- source_labels: [__meta_kubernetes_pod_container_name,__address__]
|
||||
action: replace
|
||||
target_label: __address__
|
||||
regex: ^weave-npc;(.+?)(?::\d+)?$
|
||||
replacement: $1:6781
|
||||
- source_labels: [__meta_kubernetes_pod_container_name]
|
||||
action: replace
|
||||
target_label: job
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
name: prometheus
|
||||
name: prometheus
|
||||
spec:
|
||||
selector:
|
||||
app: prometheus
|
||||
type: NodePort
|
||||
ports:
|
||||
- name: prometheus
|
||||
protocol: TCP
|
||||
port: 9090
|
||||
nodePort: 30900
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: prometheus
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: prometheus
|
||||
template:
|
||||
metadata:
|
||||
name: prometheus
|
||||
labels:
|
||||
app: prometheus
|
||||
spec:
|
||||
containers:
|
||||
- name: prometheus
|
||||
image: prom/prometheus:v1.3.0
|
||||
args:
|
||||
- '-config.file=/etc/prometheus/prometheus.yml'
|
||||
ports:
|
||||
- name: web
|
||||
containerPort: 9090
|
||||
volumeMounts:
|
||||
- name: config-volume
|
||||
mountPath: /etc/prometheus
|
||||
volumes:
|
||||
- name: config-volume
|
||||
configMap:
|
||||
name: prometheus
|
||||
```
|
||||
|
||||
# Weave Cloud Integration
|
||||
|
||||
Finally, you can configure your local Prometheus instance to push
|
||||
metrics to Weave Cloud by including the following fragment in the
|
||||
`prometheus.yml` above (after replacing `<WEAVE_CLOUD_TOKEN>` with
|
||||
your own token):
|
||||
|
||||
```
|
||||
remote_write:
|
||||
url: http://frontend.dev.weave.works/api/prom/push
|
||||
basic_auth:
|
||||
password: <WEAVE_CLOUD_TOKEN>
|
||||
```
|
||||
|
||||
For information on getting started with Weave Cloud metrics, including
|
||||
on how to obtain your Weave Cloud token, please go
|
||||
[here](https://github.com/weaveworks/cortex).
|
||||
@@ -20,7 +20,7 @@ once and your system is configured to start Docker on boot, then Weave Net
|
||||
will start automatically on system restarts.
|
||||
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -302,7 +302,7 @@ latest snapshot release using:
|
||||
sudo chmod a+x /usr/local/bin/weave
|
||||
weave setup
|
||||
|
||||
Snapshot releases report the script version as "(unreleased version)",
|
||||
Snapshot releases report the script version as "unreleased",
|
||||
and the container image versions as git hashes.
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
|
||||
@@ -6,6 +6,8 @@ UNIVERSE=10.2.0.0/16
|
||||
|
||||
start_suite "weave status/report"
|
||||
|
||||
|
||||
|
||||
weave_on $HOST1 launch --ipalloc-range $UNIVERSE --name 8a:3e:3e:3e:3e:3e --nickname nicknamington
|
||||
|
||||
check() {
|
||||
@@ -29,10 +31,33 @@ assert "weave_on $HOST1 status connections | tr -s ' ' | cut -d ' ' -f 2" "10.2.
|
||||
assert "weave_on $HOST1 report -f '{{.VersionCheck.Enabled}}'" "false"
|
||||
assert_raises "weave_on $HOST1 status | grep 'version check update disabled'"
|
||||
|
||||
|
||||
|
||||
weave_on $HOST1 reset
|
||||
# Guarantee the version check fails by feeding an unresponsive IP address into the resolver
|
||||
$SSH $HOST1 tee /tmp/hosts.checkpoint-api > /dev/null <<EOF
|
||||
127.0.0.1 localhost
|
||||
127.1.1.1 checkpoint-api.weave.works
|
||||
EOF
|
||||
CHECKPOINT_DISABLE="" WEAVE_DOCKER_ARGS="-v /tmp/hosts.checkpoint-api:/etc/hosts" weave_on $HOST1 launch
|
||||
|
||||
wait_for_version_check_error() {
|
||||
while true; do
|
||||
docker_on $HOST1 logs weave 2>&1 | grep 'Error checking version' && return
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
assert_raises "timeout 30 cat <( wait_for_version_check_error )"
|
||||
|
||||
assert "weave_on $HOST1 report -f '{{.VersionCheck.Enabled}}'" "true"
|
||||
assert "weave_on $HOST1 report -f '{{.VersionCheck.Success}}'" "false"
|
||||
assert "weave_on $HOST1 report -f '{{.VersionCheck.NewVersion}}'" ""
|
||||
|
||||
weave_on $HOST1 reset
|
||||
|
||||
CHECKPOINT_DISABLE="" weave_on $HOST1 launch
|
||||
assert "weave_on $HOST1 report -f '{{.VersionCheck.Enabled}}'" "true"
|
||||
assert "weave_on $HOST1 report -f '{{.VersionCheck.Success}}'" "true"
|
||||
|
||||
NEW_VSN=$(weave_on $HOST1 report -f "{{.VersionCheck.NewVersion}}")
|
||||
if [ -z "$NEW_VSN" ]; then
|
||||
@@ -41,4 +66,6 @@ else
|
||||
assert_raises "weave_on $HOST1 status | grep \"version $NEW_VSN available - please upgrade!\""
|
||||
fi
|
||||
|
||||
|
||||
|
||||
end_suite
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
start_suite "Detect hairpin mode"
|
||||
|
||||
weave_on $HOST1 launch
|
||||
|
||||
assert_raises "run_on $HOST1 sudo ip link set vethwe-bridge type bridge_slave hairpin on"
|
||||
assert_raises "docker_on $HOST1 logs weave 2>&1 | grep -q 'Hairpin mode enabled on \"vethwe-bridge\"'"
|
||||
|
||||
end_suite
|
||||
@@ -16,8 +16,10 @@ run_on $HOST1 sudo mkdir -p /opt/cni/bin
|
||||
weave_on $HOST1 setup-cni
|
||||
weave_on $HOST1 launch
|
||||
|
||||
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
|
||||
{
|
||||
@@ -25,6 +27,7 @@ cni_connect $HOST1 c1 <<EOF
|
||||
"type": "weave-net"
|
||||
}
|
||||
EOF
|
||||
|
||||
cni_connect $HOST1 c2 <<EOF
|
||||
{
|
||||
"name": "weave",
|
||||
@@ -42,16 +45,33 @@ C2IP=$(container_ip $HOST1 c2)
|
||||
assert_raises "exec_on $HOST1 c1 $PING $C2IP"
|
||||
assert_raises "exec_on $HOST1 c2 $PING $C1IP"
|
||||
|
||||
# Now remove and start a new container to see if IP address re-use breaks things
|
||||
# Now remove and start a new container to see if anything breaks
|
||||
docker_on $HOST1 rm -f c2
|
||||
sleep 2
|
||||
|
||||
docker_on $HOST1 run --net=none --name=c3 -dt $SMALL_IMAGE /bin/sh
|
||||
C3=$(docker_on $HOST1 run --net=none --name=c3 -dt $SMALL_IMAGE /bin/sh)
|
||||
|
||||
cni_connect $HOST1 c3 <<EOF
|
||||
{ "name": "weave", "type": "weave-net" }
|
||||
EOF
|
||||
|
||||
assert_raises "exec_on $HOST1 c1 $PING $C2IP"
|
||||
C3IP=$(container_ip $HOST1 c3)
|
||||
|
||||
# CNI shouldn't re-use the address until we call DEL
|
||||
assert_raises "[ $C2IP != $C3IP ]"
|
||||
assert_raises "exec_on $HOST1 c1 $PING $C3IP"
|
||||
|
||||
|
||||
# Ensure existing containers can reclaim their IP addresses after CNI has been used -- see #2548
|
||||
stop_weave_on $HOST1
|
||||
|
||||
# Ensure no warning is printed to the standard error:
|
||||
ACTUAL_OUTPUT=$(weave_on $HOST1 launch-router 2>&1)
|
||||
EXPECTED_OUTPUT=$(docker_on $HOST1 inspect --format="{{.Id}}" weave)
|
||||
|
||||
assert_raises "[ $EXPECTED_OUTPUT == $ACTUAL_OUTPUT ]"
|
||||
|
||||
assert "$SSH $HOST1 \"curl -s -X GET 127.0.0.1:6784/ip/$C1 | grep -o -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'\"" "$C1IP"
|
||||
assert "$SSH $HOST1 \"curl -s -X GET 127.0.0.1:6784/ip/$C3 | grep -o -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'\"" "$C3IP"
|
||||
|
||||
|
||||
end_suite
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
tear_down_kubeadm() {
|
||||
for host in $HOSTS; do
|
||||
# If we don't stop kubelet, it will restart all the containers we're trying to kill
|
||||
run_on $host "sudo systemctl stop kubelet"
|
||||
rm_containers $host $(docker_on $host ps -aq)
|
||||
run_on $host "test ! -d /var/lib/kubelet || sudo find /var/lib/kubelet -execdir findmnt -n -t tmpfs -o TARGET -T {} \; | uniq | xargs -r sudo umount"
|
||||
run_on $host "sudo rm -r -f /etc/kubernetes /var/lib/kubelet /var/lib/etcd"
|
||||
done
|
||||
}
|
||||
|
||||
start_suite "Test weave-kube image with Kubernetes"
|
||||
|
||||
TOKEN=112233.445566778899000
|
||||
HOST1IP=$($SSH $HOST1 "getent hosts $HOST1 | cut -f 1 -d ' '")
|
||||
SUCCESS="6 established"
|
||||
|
||||
tear_down_kubeadm
|
||||
|
||||
run_on $HOST1 "sudo systemctl start kubelet && sudo kubeadm init --token=$TOKEN"
|
||||
run_on $HOST2 "sudo systemctl start kubelet && sudo kubeadm join --token=$TOKEN $HOST1IP"
|
||||
run_on $HOST3 "sudo systemctl start kubelet && sudo kubeadm join --token=$TOKEN $HOST1IP"
|
||||
|
||||
[ -n "$COVERAGE" ] && COVERAGE_ARGS="\\n env:\\n - name: EXTRA_ARGS\\n value: \"-test.coverprofile=/home/weave/cover.prof --\""
|
||||
|
||||
sed -e "s%imagePullPolicy: Always%imagePullPolicy: Never$COVERAGE_ARGS%" ../prog/weave-kube/weave-daemonset.yaml | run_on $HOST1 "kubectl apply -f -"
|
||||
|
||||
sleep 5
|
||||
|
||||
wait_for_connections() {
|
||||
for i in $(seq 1 30); do
|
||||
if run_on $HOST1 "curl -sS http://127.0.0.1:6784/status | grep \"$SUCCESS\"" ; then
|
||||
return
|
||||
fi
|
||||
echo "Waiting for connections"
|
||||
sleep 1
|
||||
done
|
||||
echo "Timed out waiting for connections to establish" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_raises wait_for_connections
|
||||
|
||||
# Check we can ping between the Weave bridg IPs on each host
|
||||
HOST1EXPIP=$($SSH $HOST1 "weave expose")
|
||||
HOST2EXPIP=$($SSH $HOST2 "weave expose")
|
||||
HOST3EXPIP=$($SSH $HOST3 "weave expose")
|
||||
assert_raises "run_on $HOST1 $PING $HOST2EXPIP"
|
||||
assert_raises "run_on $HOST2 $PING $HOST1EXPIP"
|
||||
assert_raises "run_on $HOST3 $PING $HOST2EXPIP"
|
||||
|
||||
# See if we can get some pods running that connect to the network
|
||||
run_on $HOST1 "kubectl run hello --image=weaveworks/hello-world --replicas=3"
|
||||
|
||||
wait_for_pods() {
|
||||
for i in $(seq 1 30); do
|
||||
if run_on $HOST1 "kubectl get pods | grep 'hello.*Running'" ; then
|
||||
return
|
||||
fi
|
||||
echo "Waiting for pods"
|
||||
sleep 1
|
||||
done
|
||||
echo "Timed out waiting for pods" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_raises wait_for_pods
|
||||
|
||||
tear_down_kubeadm
|
||||
|
||||
end_suite
|
||||
+46
-11
@@ -10,8 +10,9 @@ set -e
|
||||
: ${KEY_FILE:=/tmp/gce_private_key.json}
|
||||
: ${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=""
|
||||
@@ -33,8 +34,11 @@ function vm_names {
|
||||
|
||||
# Delete all vms in this account
|
||||
function destroy {
|
||||
if [ $(gcloud compute firewall-rules list test-allow-docker$SUFFIX 2>/dev/null | wc -l) -gt 0 ] ; then
|
||||
gcloud compute firewall-rules delete test-allow-docker$SUFFIX
|
||||
fi
|
||||
names="$(vm_names)"
|
||||
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 +76,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 {
|
||||
@@ -95,7 +127,9 @@ function setup {
|
||||
destroy
|
||||
|
||||
names="$(vm_names)"
|
||||
gcloud compute instances create $names --image $TEMPLATE_NAME --zone $ZONE
|
||||
gcloud compute instances create $names --image $TEMPLATE_NAME --zone $ZONE --tags test$SUFFIX --network=test
|
||||
my_ip="$(curl -s http://ipinfo.io/ip)"
|
||||
gcloud compute firewall-rules create test-allow-docker$SUFFIX --network=test --allow tcp:2375,tcp:12375 --target-tags test$SUFFIX --source-ranges $my_ip
|
||||
gcloud compute config-ssh --ssh-key-file $SSH_KEY_FILE
|
||||
sed -i '/UserKnownHostsFile=\/dev\/null/d' ~/.ssh/config
|
||||
|
||||
@@ -124,11 +158,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
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,4 +1,9 @@
|
||||
$go_version = "1.4.2"
|
||||
$go_regexp = /FROM golang:(\S*).*?/
|
||||
$dockerfile_path = File.expand_path(File.join(File.dirname(__FILE__), 'build', 'Dockerfile'))
|
||||
$go_version = File.readlines($dockerfile_path).first { |line| line.match($go_regexp) }.match($go_regexp).captures.first
|
||||
if $go_version.nil?
|
||||
raise ArgumentError.new("Failed to read Go version from Dockerfile.")
|
||||
end
|
||||
|
||||
$go_path = "/usr/local/go/bin"
|
||||
|
||||
|
||||
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