Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fcd521f67 | ||
|
|
fa74e9f290 | ||
|
|
008eeb03ad | ||
|
|
41c48dc678 | ||
|
|
09be35dee0 | ||
|
|
c3ad641d94 | ||
|
|
bacb3952a3 | ||
|
|
70257a6c19 | ||
|
|
bfe9f00e55 | ||
|
|
591be2d08f | ||
|
|
c3ce052cc5 | ||
|
|
7dcd4bdf47 | ||
|
|
2d8373249a | ||
|
|
4979ce1f5f | ||
|
|
78e00f9a3a | ||
|
|
2ff0bbe150 | ||
|
|
a6e1165e71 | ||
|
|
fc211b15e9 | ||
|
|
acc24f1237 | ||
|
|
612e265123 | ||
|
|
9cf50588b5 | ||
|
|
6c2de466c0 | ||
|
|
dd5994c7cf | ||
|
|
e6ba59f029 | ||
|
|
8fb3da6fc8 | ||
|
|
1814bbd2f2 | ||
|
|
374fed4699 | ||
|
|
e669ac6ef8 | ||
|
|
4263ac1ac5 | ||
|
|
8176048268 | ||
|
|
e2d6ee4223 | ||
|
|
90a81ce736 | ||
|
|
9cf0dd1b87 | ||
|
|
e65cf32372 | ||
|
|
0ad2001ab4 | ||
|
|
70ff510e49 | ||
|
|
6377aeabdd | ||
|
|
1649080a35 | ||
|
|
c1ee358f66 | ||
|
|
2c708b57a1 | ||
|
|
c5772c814c | ||
|
|
10a5b6fb51 | ||
|
|
07d31a8558 | ||
|
|
cf3a3be8f2 | ||
|
|
49224ec9f7 | ||
|
|
dee24aec65 | ||
|
|
9a63ade1b5 | ||
|
|
18a6237ec7 | ||
|
|
be8298926a | ||
|
|
86958be6cf | ||
|
|
a2d07c85da | ||
|
|
295ddc5248 | ||
|
|
a85a19e40a | ||
|
|
cec3c8a846 | ||
|
|
7da7ce85df | ||
|
|
6055431b6f | ||
|
|
c0e2deb5a0 | ||
|
|
1f86ed55ba | ||
|
|
35f1ba17e1 | ||
|
|
b84fbf86ec | ||
|
|
a5e0b3452d | ||
|
|
0a1ca30aae | ||
|
|
2e85018c26 | ||
|
|
affdc6f5af | ||
|
|
9198bb5a68 | ||
|
|
9769c8d5b9 | ||
|
|
688328dc57 | ||
|
|
8904a4b144 | ||
|
|
8d1ee752d4 | ||
|
|
f02a11b27b | ||
|
|
5e675f26f7 | ||
|
|
497a9c7fd0 | ||
|
|
5afcd0595d | ||
|
|
f27d4d2117 | ||
|
|
79727a86fa | ||
|
|
d474c1c26d | ||
|
|
29ec43bdc8 | ||
|
|
cabc05b06c | ||
|
|
89e820d62e | ||
|
|
88616007b0 | ||
|
|
b262a1eedc | ||
|
|
973e1075b9 | ||
|
|
b8ce722a2c | ||
|
|
b8fa4af06a | ||
|
|
7f1945f06e | ||
|
|
ea8f041a20 | ||
|
|
52e8cdb011 | ||
|
|
ac9f47783c | ||
|
|
27f9aa16d6 | ||
|
|
08dc649b9a | ||
|
|
bb1a373272 | ||
|
|
e192f68891 | ||
|
|
73654791c1 | ||
|
|
8908458942 | ||
|
|
26792283ab | ||
|
|
32a92a4695 | ||
|
|
3bea2ae419 | ||
|
|
58eaf19026 | ||
|
|
d513e68fab | ||
|
|
ca8bbb27a0 | ||
|
|
287dab27ae | ||
|
|
25c26519ed | ||
|
|
cf66c8d507 | ||
|
|
318bb767a5 | ||
|
|
ad6148f0f6 | ||
|
|
8042d3f22a | ||
|
|
c7259fc424 | ||
|
|
62c949df6e | ||
|
|
8c7dc342e0 | ||
|
|
d15439aeac | ||
|
|
c19c64a990 | ||
|
|
8e374d9416 | ||
|
|
49216ce365 | ||
|
|
812c58297f | ||
|
|
2a04df1049 | ||
|
|
2eda624653 | ||
|
|
2f8aa7a351 | ||
|
|
b6115f0147 | ||
|
|
5ff22a1a77 | ||
|
|
7fdf8fee13 | ||
|
|
45a9ea666e | ||
|
|
efd5d0e410 | ||
|
|
6dbe95a0b5 | ||
|
|
a0ec06a487 | ||
|
|
ebf4b4b48a | ||
|
|
4f99efaaf4 | ||
|
|
62c30d9130 | ||
|
|
21fb953d0f | ||
|
|
27c98070e8 | ||
|
|
039750a7fa | ||
|
|
9b0d1bd054 | ||
|
|
4c1c2a5661 | ||
|
|
791d9aecfc | ||
|
|
48aaac90fb | ||
|
|
bfaa3a3a92 | ||
|
|
6dce685105 | ||
|
|
8c4e33faef | ||
|
|
81582b04f9 | ||
|
|
dbcc72a6f6 | ||
|
|
9f207f161a | ||
|
|
1ee4039681 | ||
|
|
3203814940 | ||
|
|
acc8c33260 | ||
|
|
22ecf505e3 | ||
|
|
c2c14410d2 | ||
|
|
ba578c91a7 | ||
|
|
7b46391e30 | ||
|
|
ae551cafea | ||
|
|
672c770dbe | ||
|
|
847046a225 | ||
|
|
4da998ab45 | ||
|
|
b534ea60f5 | ||
|
|
549ccff5eb | ||
|
|
9cf6749b2e | ||
|
|
d2999cf1d7 | ||
|
|
391b55ceaf | ||
|
|
e05345d3bb | ||
|
|
67bd54e55d | ||
|
|
20919fd6db | ||
|
|
ada2af04ff | ||
|
|
fcb6f762a9 | ||
|
|
5645f31934 | ||
|
|
a7295e2d03 | ||
|
|
c3e6ac2cac | ||
|
|
f1f3c8c8fe | ||
|
|
81b2f2e15a | ||
|
|
f6fdaf6492 | ||
|
|
519c5d686a | ||
|
|
f7e62ccbcc | ||
|
|
19161c0c92 | ||
|
|
c528303841 | ||
|
|
a439288765 | ||
|
|
416410912d | ||
|
|
b3f26f00e2 | ||
|
|
90e6ecdd11 | ||
|
|
842354bc5a | ||
|
|
4d3875cec2 | ||
|
|
74dfe49046 | ||
|
|
d0f23ed9e0 | ||
|
|
8a0992315e | ||
|
|
18b5cb10dc | ||
|
|
8afc6f208d | ||
|
|
d35d931179 | ||
|
|
093174e757 | ||
|
|
ac78762910 | ||
|
|
fff656f7e4 | ||
|
|
83b36e4c76 | ||
|
|
b42895b5c0 | ||
|
|
42e4b9addc | ||
|
|
91ab0e4a76 | ||
|
|
51feec3785 | ||
|
|
20eee3818e | ||
|
|
94cd6226ea | ||
|
|
5534ecf1b1 | ||
|
|
d498df3933 | ||
|
|
75bf62ca80 | ||
|
|
ad42357dd6 | ||
|
|
2de899342a | ||
|
|
8df48dc69d | ||
|
|
0f2809d2e9 | ||
|
|
aa44153af7 | ||
|
|
ce9c5162bd | ||
|
|
5c0da56ad5 | ||
|
|
08858c2a10 | ||
|
|
0268aeb25f | ||
|
|
ea3aba8971 | ||
|
|
265bfcf9d7 | ||
|
|
7a9fae5892 | ||
|
|
f66b0cc0aa | ||
|
|
e7f30106c0 | ||
|
|
86d2800b59 | ||
|
|
e65006042d | ||
|
|
65efc89b78 | ||
|
|
ae38834507 | ||
|
|
4261777613 | ||
|
|
7bc089f5c5 | ||
|
|
c46e3e8cf7 | ||
|
|
180d554205 | ||
|
|
a450d2e741 | ||
|
|
d528196033 | ||
|
|
0da2cbf84e | ||
|
|
20f981bc42 | ||
|
|
9a5514ffe0 | ||
|
|
439b4ad190 | ||
|
|
9a9c06966b | ||
|
|
b3f9baac8f | ||
|
|
bb961489b0 | ||
|
|
d6ae1f8375 | ||
|
|
5a1361167e | ||
|
|
4d5172f67e | ||
|
|
6f01c31458 | ||
|
|
6cfb208f3f | ||
|
|
3074892f1b | ||
|
|
b05d977cf9 | ||
|
|
36d183d988 | ||
|
|
b154fd56fb | ||
|
|
3419f69246 | ||
|
|
8e23301028 | ||
|
|
72724ee389 | ||
|
|
ae59a053b1 | ||
|
|
e71919226b | ||
|
|
6c5dd75216 | ||
|
|
23095728f5 | ||
|
|
498dacb4d2 | ||
|
|
b373a069e9 | ||
|
|
1d67e2f955 | ||
|
|
9a6935bf40 | ||
|
|
921ffefd90 | ||
|
|
a4fccd43eb | ||
|
|
48e1a8295b | ||
|
|
890c8c1905 | ||
|
|
38d50ce6fb | ||
|
|
14768d6947 | ||
|
|
b96399b563 | ||
|
|
d8c008dc7d | ||
|
|
b2ae5f4f4b | ||
|
|
ba871d7855 | ||
|
|
a0c8fc2ddb | ||
|
|
2a12053122 | ||
|
|
1860f21efb | ||
|
|
541adf6bb1 | ||
|
|
a71e90ddb8 | ||
|
|
60f4b3cdb8 | ||
|
|
0c249fa126 | ||
|
|
db1b57900b | ||
|
|
12b4898b3d | ||
|
|
f5f9bab77c | ||
|
|
ba7aa674d1 | ||
|
|
f670be4315 | ||
|
|
a658861fde | ||
|
|
192e5a1f79 | ||
|
|
475b15e372 | ||
|
|
d2ed91ccc8 | ||
|
|
333f4a1d3c | ||
|
|
832fe2f0b0 | ||
|
|
ea76185873 | ||
|
|
51c3ea184a | ||
|
|
692bccc4fc | ||
|
|
59fe76f29b | ||
|
|
51c2a0d732 | ||
|
|
2ab01b55c9 | ||
|
|
461f9e736b | ||
|
|
8e16b03ae8 | ||
|
|
6469809b9a | ||
|
|
0205adcedb | ||
|
|
0a0dbe115a | ||
|
|
ebbf3d4244 | ||
|
|
ceb36d1891 | ||
|
|
4caa850853 | ||
|
|
96cecf1796 | ||
|
|
51c7af8fb4 | ||
|
|
347466aba2 | ||
|
|
bee1c44859 | ||
|
|
a3fd36e2bb | ||
|
|
f07238c8bb | ||
|
|
3b499ce27c | ||
|
|
4dda216398 | ||
|
|
f0e511510a | ||
|
|
570ab0b10b | ||
|
|
214cf6cb2e | ||
|
|
07459f0cec | ||
|
|
90f4f7b62c | ||
|
|
987ac98fbf | ||
|
|
49ed40a301 | ||
|
|
c67bc704e1 | ||
|
|
3501ad45e6 | ||
|
|
70a13c9c12 | ||
|
|
3afb8d3ddf | ||
|
|
fd2f91e2ee | ||
|
|
cb0c9e5fba | ||
|
|
6a0e39e5ee | ||
|
|
a552f194b4 | ||
|
|
267bd8b51d | ||
|
|
497dddabc1 | ||
|
|
71b39489da | ||
|
|
ed8d92cfc6 | ||
|
|
f431ee289b | ||
|
|
7ea82204e0 | ||
|
|
44692b997c | ||
|
|
21086a0b2a | ||
|
|
d7119169df | ||
|
|
e1eb0a8617 | ||
|
|
cacc190764 | ||
|
|
89e5d935a6 | ||
|
|
884e1d7a6b | ||
|
|
5b1ca02ecc | ||
|
|
f51347feac | ||
|
|
b605b832ed | ||
|
|
e148b72284 | ||
|
|
16db921349 | ||
|
|
4c235b3648 | ||
|
|
1a8d479227 | ||
|
|
7aad364116 | ||
|
|
4958d9e423 | ||
|
|
474f1f3673 | ||
|
|
9aa7044f1e | ||
|
|
7c39f96d08 | ||
|
|
3c3e0b0352 | ||
|
|
8467742d6d | ||
|
|
5cfc3f8c47 | ||
|
|
62dbe03193 | ||
|
|
8755da4b5a | ||
|
|
25cff16bd1 | ||
|
|
ade6738789 | ||
|
|
ea0e3a14e0 | ||
|
|
4a1a1e83c8 | ||
|
|
ee48700b40 | ||
|
|
0791ce8ab0 | ||
|
|
1ac543aba7 | ||
|
|
3d5f80630d | ||
|
|
be2da07735 | ||
|
|
276d0b621b | ||
|
|
6733dac9fc | ||
|
|
80b286ebf7 | ||
|
|
2c143612e2 | ||
|
|
be412812f9 | ||
|
|
bfed7c24f8 | ||
|
|
fe859952e0 | ||
|
|
86361c5b60 | ||
|
|
35c017a787 | ||
|
|
79d65bc98b | ||
|
|
6bc69199d3 | ||
|
|
292cbc484a | ||
|
|
1a98cbfff9 | ||
|
|
071f077cc5 | ||
|
|
c16653a396 | ||
|
|
b157544394 | ||
|
|
18f65a1914 | ||
|
|
ba16135b3a | ||
|
|
df688cfb53 | ||
|
|
88679d66fc | ||
|
|
ab1cbcbbf8 | ||
|
|
a02fb753df | ||
|
|
98b002f7c0 | ||
|
|
e251bfe807 | ||
|
|
eb88893312 | ||
|
|
a362bf68bb | ||
|
|
3c53aa66b7 | ||
|
|
c97d4731dc | ||
|
|
a69546c9ee | ||
|
|
b0f78e97f4 | ||
|
|
49f7e38133 | ||
|
|
2e7f44feee | ||
|
|
48ceb49832 | ||
|
|
dd772f04fe | ||
|
|
47597c941d | ||
|
|
c0e8af56e2 | ||
|
|
72c0172c48 | ||
|
|
507f5345c5 | ||
|
|
79d4aec1ec | ||
|
|
89c7e6d337 | ||
|
|
b8d7af3822 | ||
|
|
3c9c968174 | ||
|
|
38ca0f90c7 | ||
|
|
8bf08c3cf8 | ||
|
|
14c79d2184 | ||
|
|
b0519af573 | ||
|
|
dc7acd6c70 | ||
|
|
d63a21363e | ||
|
|
a330dd4970 | ||
|
|
c0388c0136 | ||
|
|
aeca987a4c | ||
|
|
ff1cd7c008 | ||
|
|
4b720bffdf | ||
|
|
5c210c90b5 | ||
|
|
2ede1066e9 |
No files matched your search
+6
-2
@@ -33,6 +33,9 @@ coverage.html
|
|||||||
# Vim backup files
|
# Vim backup files
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
# Sed backup files
|
||||||
|
*.bak
|
||||||
|
|
||||||
# Project specific
|
# Project specific
|
||||||
prog/weaver/weaver
|
prog/weaver/weaver
|
||||||
prog/weavedns/weavedns
|
prog/weavedns/weavedns
|
||||||
@@ -43,8 +46,8 @@ prog/weavewait/weavewait_noop
|
|||||||
prog/weavewait/weavewait_nomcast
|
prog/weavewait/weavewait_nomcast
|
||||||
prog/weaveutil/weaveutil
|
prog/weaveutil/weaveutil
|
||||||
prog/*/Dockerfile.weaveworks
|
prog/*/Dockerfile.weaveworks
|
||||||
prog/kube-peers/kube-peers
|
prog/kube-utils/kube-utils
|
||||||
prog/weave-kube/kube-peers
|
prog/weave-kube/kube-utils
|
||||||
prog/weave-npc/weave-npc
|
prog/weave-npc/weave-npc
|
||||||
prog/plugin/plugin
|
prog/plugin/plugin
|
||||||
testing/cover/cover
|
testing/cover/cover
|
||||||
@@ -65,6 +68,7 @@ test/tls/tls
|
|||||||
test/tls/*.pem
|
test/tls/*.pem
|
||||||
test/coverage
|
test/coverage
|
||||||
test/coverage.*
|
test/coverage.*
|
||||||
|
test/images/network-tester/webserver
|
||||||
*qemu-*-static
|
*qemu-*-static
|
||||||
terraform.tfstate
|
terraform.tfstate
|
||||||
terraform.tfstate.backup
|
terraform.tfstate.backup
|
||||||
|
|||||||
-132
@@ -1,135 +1,3 @@
|
|||||||
[submodule "tools"]
|
[submodule "tools"]
|
||||||
path = tools
|
path = tools
|
||||||
url = https://github.com/weaveworks/build-tools
|
url = https://github.com/weaveworks/build-tools
|
||||||
[submodule "vendor/github.com/vishvananda/netlink"]
|
|
||||||
path = vendor/github.com/vishvananda/netlink
|
|
||||||
url = https://github.com/vishvananda/netlink.git
|
|
||||||
[submodule "vendor/github.com/gorilla/mux"]
|
|
||||||
path = vendor/github.com/gorilla/mux
|
|
||||||
url = https://github.com/gorilla/mux.git
|
|
||||||
[submodule "vendor/github.com/gorilla/context"]
|
|
||||||
path = vendor/github.com/gorilla/context
|
|
||||||
url = https://github.com/gorilla/context.git
|
|
||||||
[submodule "vendor/github.com/google/gopacket"]
|
|
||||||
path = vendor/github.com/google/gopacket
|
|
||||||
url = https://github.com/google/gopacket.git
|
|
||||||
[submodule "vendor/github.com/weaveworks/go-odp"]
|
|
||||||
path = vendor/github.com/weaveworks/go-odp
|
|
||||||
url = https://github.com/weaveworks/go-odp.git
|
|
||||||
[submodule "vendor/github.com/opencontainers/runc"]
|
|
||||||
path = vendor/github.com/opencontainers/runc
|
|
||||||
url = https://github.com/opencontainers/runc.git
|
|
||||||
[submodule "vendor/github.com/fsouza/go-dockerclient"]
|
|
||||||
path = vendor/github.com/fsouza/go-dockerclient
|
|
||||||
url = https://github.com/fsouza/go-dockerclient.git
|
|
||||||
[submodule "vendor/github.com/docker/libnetwork"]
|
|
||||||
path = vendor/github.com/docker/libnetwork
|
|
||||||
url = https://github.com/docker/libnetwork.git
|
|
||||||
[submodule "vendor/github.com/docker/docker"]
|
|
||||||
path = vendor/github.com/docker/docker
|
|
||||||
url = https://github.com/docker/docker.git
|
|
||||||
[submodule "vendor/github.com/Sirupsen/logrus"]
|
|
||||||
path = vendor/github.com/Sirupsen/logrus
|
|
||||||
url = https://github.com/Sirupsen/logrus.git
|
|
||||||
[submodule "vendor/github.com/andybalholm/go-bit"]
|
|
||||||
path = vendor/github.com/andybalholm/go-bit
|
|
||||||
url = https://github.com/andybalholm/go-bit.git
|
|
||||||
[submodule "vendor/github.com/miekg/dns"]
|
|
||||||
path = vendor/github.com/miekg/dns
|
|
||||||
url = https://github.com/miekg/dns.git
|
|
||||||
[submodule "vendor/github.com/stretchr/testify"]
|
|
||||||
path = vendor/github.com/stretchr/testify
|
|
||||||
url = https://github.com/stretchr/testify.git
|
|
||||||
[submodule "vendor/golang.org/x/crypto"]
|
|
||||||
path = vendor/golang.org/x/crypto
|
|
||||||
url = https://github.com/golang/crypto.git
|
|
||||||
[submodule "vendor/github.com/docker/machine"]
|
|
||||||
path = vendor/github.com/docker/machine
|
|
||||||
url = https://github.com/docker/machine.git
|
|
||||||
[submodule "vendor/github.com/weaveworks/mesh"]
|
|
||||||
path = vendor/github.com/weaveworks/mesh
|
|
||||||
url = https://github.com/weaveworks/mesh
|
|
||||||
[submodule "vendor/github.com/vishvananda/netns"]
|
|
||||||
path = vendor/github.com/vishvananda/netns
|
|
||||||
url = https://github.com/vishvananda/netns.git
|
|
||||||
[submodule "vendor/github.com/boltdb/bolt"]
|
|
||||||
path = vendor/github.com/boltdb/bolt
|
|
||||||
url = https://github.com/boltdb/bolt
|
|
||||||
[submodule "vendor/github.com/weaveworks/go-checkpoint"]
|
|
||||||
path = vendor/github.com/weaveworks/go-checkpoint
|
|
||||||
url = https://github.com/weaveworks/go-checkpoint
|
|
||||||
[submodule "vendor/github.com/hashicorp/go-cleanhttp"]
|
|
||||||
path = vendor/github.com/hashicorp/go-cleanhttp
|
|
||||||
url = http://github.com/hashicorp/go-cleanhttp
|
|
||||||
[submodule "vendor/github.com/coreos/go-iptables"]
|
|
||||||
path = vendor/github.com/coreos/go-iptables
|
|
||||||
url = https://github.com/coreos/go-iptables
|
|
||||||
[submodule "vendor/github.com/pkg/profile"]
|
|
||||||
path = vendor/github.com/pkg/profile
|
|
||||||
url = https://github.com/pkg/profile
|
|
||||||
[submodule "vendor/github.com/j-keck/arping"]
|
|
||||||
path = vendor/github.com/j-keck/arping
|
|
||||||
url = https://github.com/j-keck/arping
|
|
||||||
[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/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
|
|
||||||
[submodule "vendor/github.com/mgutz/ansi"]
|
|
||||||
path = vendor/github.com/mgutz/ansi
|
|
||||||
url = https://github.com/mgutz/ansi
|
|
||||||
[submodule "vendor/github.com/estesp/manifest-tool"]
|
|
||||||
path = vendor/github.com/estesp/manifest-tool
|
|
||||||
url = https://github.com/estesp/manifest-tool
|
|
||||||
[submodule "vendor/github.com/weaveworks/common"]
|
|
||||||
path = vendor/github.com/weaveworks/common
|
|
||||||
url = https://github.com/weaveworks/common
|
|
||||||
[submodule "vendor/github.com/containernetworking/cni"]
|
|
||||||
path = vendor/github.com/containernetworking/cni
|
|
||||||
url = https://github.com/containernetworking/cni
|
|
||||||
[submodule "vendor/google.golang.org/grpc"]
|
|
||||||
path = vendor/google.golang.org/grpc
|
|
||||||
url = https://github.com/grpc/grpc-go
|
|
||||||
[submodule "vendor/github.com/docker/go-units"]
|
|
||||||
path = vendor/github.com/docker/go-units
|
|
||||||
url = https://github.com/docker/go-units
|
|
||||||
[submodule "vendor/golang.org/x/net"]
|
|
||||||
path = vendor/golang.org/x/net
|
|
||||||
url = https://github.com/golang/net
|
|
||||||
+241
@@ -1,3 +1,244 @@
|
|||||||
|
## Release 2.4.1
|
||||||
|
|
||||||
|
This release fixes several bugs causing inconsistencies in IPAM for
|
||||||
|
Kubernetes users whose clusters scale up and down over time.
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* Nodes unable to connect after Kubernetes addon erroneously reclaimed
|
||||||
|
node without any IP addresses #3392, #3393
|
||||||
|
* Kubernetes addon could have run out of free IP addresses after nodes
|
||||||
|
are deleted #3384, #3400
|
||||||
|
* Kubernetes addon had reduced free IP addresses due to not reclaiming
|
||||||
|
IP addresses when node name is re-used #3397
|
||||||
|
|
||||||
|
### Other improvements
|
||||||
|
|
||||||
|
* Support `--label` in `WEAVE_DOCKER_ARGS` when starting Weave #3370,#3371
|
||||||
|
* Add missing `--token` argument in help for `weave launch` #3226, #3379
|
||||||
|
* Print defunct processes after smoke-tests #3362
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/75?closed=1)
|
||||||
|
|
||||||
|
|
||||||
|
## Release 2.4.0
|
||||||
|
|
||||||
|
This release introduces a support for Kubernetes Egress Network Policy (#2624, #3313)
|
||||||
|
and adds a mechanism for preserving the client source IP address to enable
|
||||||
|
`externalTrafficPolicy: Local` on Kubernetes (#2924, #3298).
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* Increase the ipset list size which prevents weave-npc from crashing on older
|
||||||
|
kernels when more than eight Kubernetes Namespaces are used (#3289, #3305).
|
||||||
|
* Avoid a possible livelock when reclaiming IP address space in weave-kube (#3317).
|
||||||
|
* Ensure `xtables.lock` is mounted as a file so that kube-proxy can take the lock
|
||||||
|
if it has started after Weave Net (#3351, #3353).
|
||||||
|
* Upgrade the CNI plugin symlinks only if the plugin has changed (#3337, #3345).
|
||||||
|
|
||||||
|
### Other improvements
|
||||||
|
|
||||||
|
* Manipulate the Kubernetes node status `NetworkUnavailable` so that Pods can be
|
||||||
|
scheduled on nodes when the GCE cloud provider is in use (#3249, #3307, #3332, #3334).
|
||||||
|
* Refrain from creating a subprocess for configuring a network interface in
|
||||||
|
a container network namespace (#3291).
|
||||||
|
* Protect against handling the CNI plugin request with the host namespace which
|
||||||
|
prevents Weave Net from misconfiguring the host network (#3206, #3346).
|
||||||
|
* Weave Net can be run on minikube VM (#3124).
|
||||||
|
* Add `org.opencontainers.image.*` labels to Dockerfiles to improve association
|
||||||
|
of the container images with git revisions (#3299).
|
||||||
|
* Improve the error message when running `weave reset` on Kubernetes (#3319).
|
||||||
|
|
||||||
|
### Build and Testing
|
||||||
|
|
||||||
|
* Use `dep` instead of `git submodules` for managing external packages (#3268).
|
||||||
|
* Fix usage of `manifest-tool` in Makefile (#3320).
|
||||||
|
* Update Kubernetes to 1.11 for the integration tests (#3340).
|
||||||
|
|
||||||
|
### External Contributors
|
||||||
|
|
||||||
|
Thanks to the following contributors:
|
||||||
|
|
||||||
|
* @kitt1987
|
||||||
|
* @louismunro
|
||||||
|
* @Nodraak
|
||||||
|
* @stevenjohnstone
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/73?closed=1)
|
||||||
|
|
||||||
|
## Release 2.3.0
|
||||||
|
|
||||||
|
### Security fixes
|
||||||
|
|
||||||
|
* By default, do not expose Weave "/status" and "/report" to all (0.0.0.0) when
|
||||||
|
running on Kubernetes #3271
|
||||||
|
|
||||||
|
### Other improvements
|
||||||
|
|
||||||
|
* Increase the default connection limit for Weave peers (from 30 to 100) when
|
||||||
|
running on Kubernetes, so that more peers could directly connect #3265
|
||||||
|
|
||||||
|
## Build and test
|
||||||
|
|
||||||
|
* Build Weave Net with Go 1.10.1 #3273
|
||||||
|
* Run integration tests against Kubernetes 1.10.0 #3266
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/70?closed=1)
|
||||||
|
|
||||||
|
## Release 2.2.1
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* Fix a bug in weave-npc which would allow ingress traffic to Kubernetes Pods selected
|
||||||
|
by a NetworkPolicy in which source and destination selectors were the same #3222,#3237
|
||||||
|
* Fix a bug in weave-npc which would crash if a previously deleted Kubernetes Namespace
|
||||||
|
has been created again #3247,#3250
|
||||||
|
|
||||||
|
### Other improvements
|
||||||
|
|
||||||
|
* Increase the default connection limit for Weave peers (from 30 to 100), so that
|
||||||
|
more peers could directly connect #3234
|
||||||
|
* When doing a rolling update of Weave Net on Kubernetes, allow each node five seconds
|
||||||
|
to initialize before rolling next Weave Net Pod, so that issues at startup will halt
|
||||||
|
the rollout and not spread across the whole cluster #3235
|
||||||
|
* Install common CA certificates from Alpine Linux package instead of copying
|
||||||
|
them manually #3236
|
||||||
|
|
||||||
|
### External contributors
|
||||||
|
|
||||||
|
Thanks to the following contributors:
|
||||||
|
|
||||||
|
* @alok87
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/71?closed=1)
|
||||||
|
|
||||||
|
## Release 2.2.0
|
||||||
|
|
||||||
|
This release improves the way Weave Net configures Linux network
|
||||||
|
devices and network filter rules, so that it is more robust in the face
|
||||||
|
of unexpected changes in its environment. #3204,#3224
|
||||||
|
|
||||||
|
As a consequence of these changes, the `weave attach` command will now
|
||||||
|
fail unless the Weave Net daemon is up and running - previously it was
|
||||||
|
possible to run independently as long as you managed all IP addresses
|
||||||
|
yourself.
|
||||||
|
|
||||||
|
## Other improvements
|
||||||
|
|
||||||
|
* Update library miekg/dns for CVE-2017-15133 (details under embargo) #3223,#3227
|
||||||
|
* Reduce the volume of logging from weave-npc #3183
|
||||||
|
* Add ability to set log level for Docker "v2" plugin, and change
|
||||||
|
default log level from DEBUG to INFO #3197
|
||||||
|
* Downgrade log messages about Discovery and Expiration to DEBUG level #3202,#3203
|
||||||
|
* Use command-line parameter for WeaveDNS address in Docker proxy #3196
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* Ensure that rules to block traffic for NetworkPolicy are placed
|
||||||
|
ahead of rules that Kubernetes has added to allow other traffic #3209,#3210
|
||||||
|
|
||||||
|
## Build and test
|
||||||
|
|
||||||
|
* Update CI tests to use Kubernetes 1.9.2 #3229
|
||||||
|
* Remove "daily update" from test VMs that only run for a few minutes #3224
|
||||||
|
|
||||||
|
## External Contributors
|
||||||
|
|
||||||
|
Thanks to the following contributors:
|
||||||
|
@vetal4444
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/67?closed=1)
|
||||||
|
|
||||||
|
|
||||||
|
## Release 2.1.3
|
||||||
|
|
||||||
|
This release fixes a race-condition in the IP reclaim code for weave-kube
|
||||||
|
where two nodes could end up fighting over the same space and break
|
||||||
|
connectivity #3190, #3192
|
||||||
|
|
||||||
|
## Release 2.1.2
|
||||||
|
|
||||||
|
This release fixes a couple of bugs discovered since the release of Weave Net 2.1.0
|
||||||
|
|
||||||
|
##Bug fixes
|
||||||
|
|
||||||
|
* Fix crash seen when starting 10-15 nodes simultaneously #3184,#3186
|
||||||
|
* Fix NetworkPolicy blocking traffic if updates come out of order from Kubernetes #3177,#3181
|
||||||
|
|
||||||
|
Thanks to the following contributors:
|
||||||
|
@zignig
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/66?closed=1)
|
||||||
|
|
||||||
|
|
||||||
|
## Release 2.1.1
|
||||||
|
|
||||||
|
As 2.1.0, but fixing a couple of installation glitches. #3175,#3176
|
||||||
|
|
||||||
|
## Release 2.1.0
|
||||||
|
|
||||||
|
##New Features
|
||||||
|
|
||||||
|
Improved Kubernetes Network Policy - Weave Net now supports the
|
||||||
|
'v1' policies introduced in Kubernetes 1.7 as well as the 'beta'
|
||||||
|
policies supported previously. See [Kubernetes 1.7 changelog](https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.7.md#network)
|
||||||
|
for differences. To use old policies, `--use-legacy-netpol` argument
|
||||||
|
should be passed to `weave-npc`. #3105,#3141,#3151,#3169
|
||||||
|
|
||||||
|
Weave Net now reclaims IP addresses owned by Kubernetes nodes which
|
||||||
|
have been deleted from the cluster - this avoids running out of IP
|
||||||
|
addresses when many nodes are added and deleted over a long period.
|
||||||
|
#2797,#3149,#3170,#3172
|
||||||
|
|
||||||
|
##Other improvements
|
||||||
|
|
||||||
|
* Export a Prometheus-style metric giving count of unreachable peers #3119
|
||||||
|
* Update 'gopacket' library to reduce memory use by approx 15MB #3160
|
||||||
|
* Replace bundling the 'docker' binary with our own code to avoid
|
||||||
|
security vulnerability alerts and save space #2957,#3110
|
||||||
|
|
||||||
|
##Bug fixes
|
||||||
|
|
||||||
|
* When `weave expose` is used, allow traffic into the Weave network -
|
||||||
|
up till version 1.12 Docker would do this for us, but in 1.13 they
|
||||||
|
stopped so now we do it. This change makes `weave expose` require
|
||||||
|
Weave Net to be running. #2758,#3122
|
||||||
|
* Arm64 build now works on non-kubernetes installs #2832,#3110
|
||||||
|
* TX offload was being disabled in 'awsvpc' mode, which slows down packet sending #3089
|
||||||
|
* Removed spurious 'nil' in logs from CNI DEL operation #3143
|
||||||
|
|
||||||
|
##Build and test
|
||||||
|
|
||||||
|
* Images are now also built for the ppc64le platform #3129
|
||||||
|
* Tweak build scripts to run on OSX as well as Linux #3135
|
||||||
|
|
||||||
|
##External Contributors
|
||||||
|
|
||||||
|
Thanks to the following contributors:
|
||||||
|
@caarlos0
|
||||||
|
@dtshepherd
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/59?closed=1)
|
||||||
|
|
||||||
|
|
||||||
|
## Release 2.0.5
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
|
||||||
|
* Fix /etc/hosts inside containers so the container's name resolves to
|
||||||
|
its Weave Net address #3136,#3138
|
||||||
|
* New weave-kube config for Kubernetes 1.7 and 1.8 which resolves an
|
||||||
|
intermittent conflict with kube-proxy that could break Weave Net
|
||||||
|
until reboot #2998,#3134
|
||||||
|
* Remove persistence file in `weave reset` when using Docker plugin V2 #3103,#3114
|
||||||
|
|
||||||
|
Build and test
|
||||||
|
|
||||||
|
* Some trivial changes to placate go-lint #3137
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/63?closed=1).
|
||||||
|
|
||||||
|
|
||||||
## Release 2.0.4
|
## Release 2.0.4
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
|
|||||||
@@ -24,6 +24,25 @@ taking the time to read this guide.
|
|||||||
* Keep pull requests small so core developers can review them quickly.
|
* Keep pull requests small so core developers can review them quickly.
|
||||||
* Keep each pull request focused on a specific topic. If you have two things to change, create two pull requests. This helps reviewers to understand the meat of your contribution.
|
* Keep each pull request focused on a specific topic. If you have two things to change, create two pull requests. This helps reviewers to understand the meat of your contribution.
|
||||||
|
|
||||||
|
## Managing Vendored Dependencies
|
||||||
|
|
||||||
|
We copy all Go dependencies into the `vendor` subdirectory and use
|
||||||
|
[`dep`](https://github.com/golang/dep) to manage them.
|
||||||
|
|
||||||
|
If you add a dependency in the code, run `dep ensure` to include it in
|
||||||
|
the config and vendor dir. Same if you remove a dependency from the code.
|
||||||
|
|
||||||
|
To update a single dependency run `dep ensure -update github.com/foo/bar`
|
||||||
|
|
||||||
|
To override `dep`'s choice of library version, you can add
|
||||||
|
[rules](https://golang.github.io/dep/docs/daily-dep.html#rule-changes-in-gopkgtoml).
|
||||||
|
Please add a comment saying why the override is necessary and when it can be
|
||||||
|
removed.
|
||||||
|
|
||||||
|
These operations result in staged but uncommitted changes to your
|
||||||
|
branch; you will need to commit them as normal. Execute them in the
|
||||||
|
root of your checkout.
|
||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
See [WorkingOnWeave](https://github.com/weaveworks/weave/wiki/WorkingOnWeave)
|
See [WorkingOnWeave](https://github.com/weaveworks/weave/wiki/WorkingOnWeave)
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
DOCKER_VERSION=17.06
|
DOCKER_VERSION=17.06
|
||||||
KUBERNETES_VERSION=1.6.2
|
KUBERNETES_VERSION=1.11.0
|
||||||
KUBERNETES_CNI_VERSION=0.5.1
|
KUBERNETES_CNI_VERSION=0.6.0
|
||||||
Generated
+1045
File diff suppressed because it is too large.
Load diff
+29
@@ -0,0 +1,29 @@
|
|||||||
|
# Pin to version before sctp was added, because latest libnetwork is
|
||||||
|
# incompatible with latest sctp
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/docker/libnetwork"
|
||||||
|
revision = "119badbd"
|
||||||
|
|
||||||
|
# Encourage dep not to use the docker-1.9 version
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/fsouza/go-dockerclient"
|
||||||
|
version = "1"
|
||||||
|
|
||||||
|
# Use recent version: last tagged version is years old
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/prometheus/client_golang"
|
||||||
|
|
||||||
|
# For compatibility with fsouza/go-dockerclient
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/docker/docker"
|
||||||
|
|
||||||
|
# Most recent release tag is incompatible with docker/docker; see https://github.com/docker/distribution/issues/2503
|
||||||
|
[[override]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/docker/distribution"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
||||||
@@ -176,7 +176,7 @@
|
|||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
Copyright 2014-2016 Weaveworks Ltd.
|
Copyright 2014-2017 Weaveworks Ltd.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ QEMU_VERSION=v2.7.0
|
|||||||
|
|
||||||
# A list of all supported architectures here. Should be named as Go is naming platforms
|
# A list of all supported architectures here. Should be named as Go is naming platforms
|
||||||
# All supported architectures must have an "ifeq" block below that customizes the parameters
|
# All supported architectures must have an "ifeq" block below that customizes the parameters
|
||||||
ALL_ARCHITECTURES=amd64 arm arm64
|
ALL_ARCHITECTURES=amd64 arm arm64 ppc64le
|
||||||
ML_PLATFORMS=linux/amd64,linux/arm,linux/arm64
|
ML_PLATFORMS=linux/amd64,linux/arm,linux/arm64,linux/ppc64le
|
||||||
|
|
||||||
ifeq ($(ARCH),amd64)
|
ifeq ($(ARCH),amd64)
|
||||||
# The architecture to use when downloading the docker binary
|
# The architecture to use when downloading the docker binary
|
||||||
WEAVEEXEC_DOCKER_ARCH?=x86_64
|
WEAVEEXEC_DOCKER_ARCH?=x86_64
|
||||||
|
|
||||||
# The name of the alpine baseimage to use as the base for weave images
|
# The name of the alpine baseimage to use as the base for weave images
|
||||||
ALPINE_BASEIMAGE?=alpine:3.4
|
ALPINE_BASEIMAGE?=alpine:3.6
|
||||||
|
|
||||||
# The extension for the made images
|
# The extension for the made images
|
||||||
# Specifying none means for example weaveworks/weave:latest
|
# Specifying none means for example weaveworks/weave:latest
|
||||||
@@ -42,7 +42,7 @@ ifeq ($(ARCH),arm)
|
|||||||
WEAVEEXEC_DOCKER_ARCH?=armel
|
WEAVEEXEC_DOCKER_ARCH?=armel
|
||||||
|
|
||||||
# Using the (semi-)official alpine image
|
# Using the (semi-)official alpine image
|
||||||
ALPINE_BASEIMAGE?=armhf/alpine:3.4
|
ALPINE_BASEIMAGE?=arm32v6/alpine:3.6
|
||||||
|
|
||||||
# arm images have the -arm suffix, for instance weaveworks/weave-arm:latest
|
# arm images have the -arm suffix, for instance weaveworks/weave-arm:latest
|
||||||
ARCH_EXT?=-arm
|
ARCH_EXT?=-arm
|
||||||
@@ -62,7 +62,7 @@ ifeq ($(ARCH),arm64)
|
|||||||
WEAVEEXEC_DOCKER_ARCH?=armel
|
WEAVEEXEC_DOCKER_ARCH?=armel
|
||||||
|
|
||||||
# Using the (semi-)official alpine image
|
# Using the (semi-)official alpine image
|
||||||
ALPINE_BASEIMAGE?=aarch64/alpine:3.5
|
ALPINE_BASEIMAGE?=arm64v8/alpine:3.6
|
||||||
|
|
||||||
# arm64 images have the -arm64 suffix, for instance weaveworks/weave-arm64:latest
|
# arm64 images have the -arm64 suffix, for instance weaveworks/weave-arm64:latest
|
||||||
ARCH_EXT?=-arm64
|
ARCH_EXT?=-arm64
|
||||||
@@ -77,10 +77,31 @@ ifeq ($(ARCH),arm64)
|
|||||||
# Tell the gcc linker to search for libpcap here
|
# Tell the gcc linker to search for libpcap here
|
||||||
CGO_LDFLAGS="-L/usr/local/lib/$(CC)"
|
CGO_LDFLAGS="-L/usr/local/lib/$(CC)"
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(ARCH),ppc64le)
|
||||||
|
# The architecture to use when downloading the docker binary
|
||||||
|
WEAVEEXEC_DOCKER_ARCH?=ppc64le
|
||||||
|
|
||||||
|
# Using the (semi-)official alpine image
|
||||||
|
ALPINE_BASEIMAGE?=ppc64le/alpine:3.6
|
||||||
|
|
||||||
|
# ppc64le images have the -ppc64le suffix, for instance weaveworks/weave-ppc64le:latest
|
||||||
|
ARCH_EXT?=-ppc64le
|
||||||
|
|
||||||
|
# The name of the gcc binary
|
||||||
|
CC=powerpc64le-linux-gnu-gcc
|
||||||
|
|
||||||
|
# The architecture name to use when downloading a prebuilt QEMU binary
|
||||||
|
QEMUARCH=ppc64le
|
||||||
|
|
||||||
|
# In the weaveworks/build image; libpcap libraries for ppc64le are placed here
|
||||||
|
# Tell the gcc linker to search for libpcap here
|
||||||
|
CGO_LDFLAGS="-L/usr/local/lib/$(CC)"
|
||||||
|
endif
|
||||||
|
|
||||||
# The name of the user that this Makefile should produce image artifacts for. Can/should be overridden
|
# The name of the user that this Makefile should produce image artifacts for. Can/should be overridden
|
||||||
DOCKERHUB_USER?=weaveworks
|
DOCKERHUB_USER?=weaveworks
|
||||||
# The default version that's chosen when pushing the images. Can/should be overridden
|
# The default version that's chosen when pushing the images. Can/should be overridden
|
||||||
|
GIT_REVISION=$(shell git rev-parse HEAD)
|
||||||
WEAVE_VERSION?=git-$(shell git rev-parse --short=12 HEAD)
|
WEAVE_VERSION?=git-$(shell git rev-parse --short=12 HEAD)
|
||||||
# Docker Store does not allow the "latest" tag.
|
# Docker Store does not allow the "latest" tag.
|
||||||
NET_PLUGIN_LATEST=latest_release
|
NET_PLUGIN_LATEST=latest_release
|
||||||
@@ -88,20 +109,20 @@ NET_PLUGIN_LATEST=latest_release
|
|||||||
# Paths to all relevant binaries that should be compiled
|
# Paths to all relevant binaries that should be compiled
|
||||||
WEAVER_EXE=prog/weaver/weaver
|
WEAVER_EXE=prog/weaver/weaver
|
||||||
SIGPROXY_EXE=prog/sigproxy/sigproxy
|
SIGPROXY_EXE=prog/sigproxy/sigproxy
|
||||||
KUBEPEERS_EXE=prog/kube-peers/kube-peers
|
KUBEUTILS_EXE=prog/kube-utils/kube-utils
|
||||||
WEAVENPC_EXE=prog/weave-npc/weave-npc
|
WEAVENPC_EXE=prog/weave-npc/weave-npc
|
||||||
WEAVEWAIT_EXE=prog/weavewait/weavewait
|
WEAVEWAIT_EXE=prog/weavewait/weavewait
|
||||||
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
|
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
|
||||||
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
|
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
|
||||||
WEAVEUTIL_EXE=prog/weaveutil/weaveutil
|
WEAVEUTIL_EXE=prog/weaveutil/weaveutil
|
||||||
RUNNER_EXE=tools/runner/runner
|
RUNNER_EXE=tools/runner/runner
|
||||||
MANIFEST_TOOL_DIR=vendor/github.com/estesp/manifest-tool
|
# manifest-tool needs registry credentials; we assume here that the active user has logged in
|
||||||
MANIFEST_TOOL_EXE=$(MANIFEST_TOOL_DIR)/manifest-tool
|
MANIFEST_TOOL_CMD=docker run --rm -v $(HOME)/.docker:/.docker weshigbee/manifest-tool --docker-cfg=/.docker
|
||||||
TEST_TLS_EXE=test/tls/tls
|
TEST_TLS_EXE=test/tls/tls
|
||||||
NETWORKTESTER_EXE=test/images/network-tester/webserver
|
NETWORKTESTER_EXE=test/images/network-tester/webserver
|
||||||
|
|
||||||
# All binaries together in a list
|
# All binaries together in a list
|
||||||
EXES=$(WEAVER_EXE) $(SIGPROXY_EXE) $(KUBEPEERS_EXE) $(WEAVENPC_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVEUTIL_EXE) $(RUNNER_EXE) $(TEST_TLS_EXE) $(MANIFEST_TOOL_EXE) $(NETWORKTESTER_EXE)
|
EXES=$(WEAVER_EXE) $(SIGPROXY_EXE) $(KUBEUTILS_EXE) $(WEAVENPC_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVEUTIL_EXE) $(RUNNER_EXE) $(TEST_TLS_EXE) $(NETWORKTESTER_EXE)
|
||||||
|
|
||||||
# These stamp files are used to mark the current state of the build; whether an image has been built or not
|
# These stamp files are used to mark the current state of the build; whether an image has been built or not
|
||||||
BUILD_UPTODATE=.build.uptodate
|
BUILD_UPTODATE=.build.uptodate
|
||||||
@@ -135,9 +156,6 @@ PUSH_ML=push_ml_weave push_ml_weaveexec push_ml_weave-kube push_ml_weave-npc
|
|||||||
|
|
||||||
WEAVE_EXPORT=weave$(ARCH_EXT).tar.gz
|
WEAVE_EXPORT=weave$(ARCH_EXT).tar.gz
|
||||||
|
|
||||||
DOCKER_VERSION=1.10.3
|
|
||||||
DOCKER_DISTRIB=prog/weaveexec/docker-$(DOCKER_VERSION).tgz
|
|
||||||
DOCKER_DISTRIB_URL=https://get.docker.com/builds/Linux/$(WEAVEEXEC_DOCKER_ARCH)/docker-$(DOCKER_VERSION).tgz
|
|
||||||
NETGO_CHECK=@strings $@ | grep cgo_stub\\\.go >/dev/null || { \
|
NETGO_CHECK=@strings $@ | grep cgo_stub\\\.go >/dev/null || { \
|
||||||
rm $@; \
|
rm $@; \
|
||||||
echo "\nYour go standard library was built without the 'netgo' build tag."; \
|
echo "\nYour go standard library was built without the 'netgo' build tag."; \
|
||||||
@@ -162,11 +180,10 @@ $(WEAVER_EXE): api/*.go plugin/*.go plugin/*/*
|
|||||||
$(WEAVER_EXE): proxy/*.go
|
$(WEAVER_EXE): proxy/*.go
|
||||||
$(WEAVEUTIL_EXE): prog/weaveutil/*.go net/*.go plugin/net/*.go plugin/ipam/*.go db/*.go
|
$(WEAVEUTIL_EXE): prog/weaveutil/*.go net/*.go plugin/net/*.go plugin/ipam/*.go db/*.go
|
||||||
$(SIGPROXY_EXE): prog/sigproxy/*.go
|
$(SIGPROXY_EXE): prog/sigproxy/*.go
|
||||||
$(KUBEPEERS_EXE): prog/kube-peers/*.go
|
$(KUBEUTILS_EXE): prog/kube-utils/*.go
|
||||||
$(WEAVENPC_EXE): prog/weave-npc/*.go npc/*.go npc/*/*.go
|
$(WEAVENPC_EXE): prog/weave-npc/*.go npc/*.go npc/*/*.go
|
||||||
$(TEST_TLS_EXE): test/tls/*.go
|
$(TEST_TLS_EXE): test/tls/*.go
|
||||||
$(RUNNER_EXE): tools/runner/*.go
|
$(RUNNER_EXE): tools/runner/*.go
|
||||||
$(MANIFEST_TOOL_EXE): $(MANIFEST_TOOL_DIR)/*.go
|
|
||||||
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
|
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
|
||||||
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
|
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
|
||||||
$(WEAVEWAIT_NOMCAST_EXE): prog/weavewait/*.go net/*.go
|
$(WEAVEWAIT_NOMCAST_EXE): prog/weavewait/*.go net/*.go
|
||||||
@@ -179,9 +196,6 @@ ifeq ($(BUILD_IN_CONTAINER),true)
|
|||||||
# This make target compiles all binaries inside of the weaveworks/build container
|
# This make target compiles all binaries inside of the weaveworks/build container
|
||||||
# It bind-mounts the source into the container and passes all important variables
|
# It bind-mounts the source into the container and passes all important variables
|
||||||
exes $(EXES) tests lint: $(BUILD_UPTODATE)
|
exes $(EXES) tests lint: $(BUILD_UPTODATE)
|
||||||
git submodule update --init
|
|
||||||
# Containernetworking has another copy of vishvananda/netlink which leads to duplicate definitions
|
|
||||||
-@rm -r vendor/github.com/containernetworking/cni/vendor
|
|
||||||
@mkdir -p $(shell pwd)/.pkg
|
@mkdir -p $(shell pwd)/.pkg
|
||||||
$(SUDO) docker run $(RM) $(RUN_FLAGS) \
|
$(SUDO) docker run $(RM) $(RUN_FLAGS) \
|
||||||
-v $(shell pwd):/go/src/github.com/weaveworks/weave \
|
-v $(shell pwd):/go/src/github.com/weaveworks/weave \
|
||||||
@@ -203,7 +217,7 @@ else
|
|||||||
endif
|
endif
|
||||||
$(NETGO_CHECK)
|
$(NETGO_CHECK)
|
||||||
|
|
||||||
$(WEAVEUTIL_EXE) $(KUBEPEERS_EXE) $(WEAVENPC_EXE) $(NETWORKTESTER_EXE):
|
$(WEAVEUTIL_EXE) $(KUBEUTILS_EXE) $(WEAVENPC_EXE) $(NETWORKTESTER_EXE):
|
||||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||||
$(NETGO_CHECK)
|
$(NETGO_CHECK)
|
||||||
|
|
||||||
@@ -217,7 +231,7 @@ $(WEAVEWAIT_NOMCAST_EXE):
|
|||||||
|
|
||||||
# These programs need a separate rule as they fail the netgo check in
|
# These programs need a separate rule as they fail the netgo check in
|
||||||
# the main build stanza due to not importing net package
|
# the main build stanza due to not importing net package
|
||||||
$(SIGPROXY_EXE) $(TEST_TLS_EXE) $(WEAVEWAIT_NOOP_EXE) $(RUNNER_EXE) $(MANIFEST_TOOL_EXE):
|
$(SIGPROXY_EXE) $(TEST_TLS_EXE) $(WEAVEWAIT_NOOP_EXE) $(RUNNER_EXE):
|
||||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
@@ -244,30 +258,28 @@ $(BUILD_UPTODATE): build/*
|
|||||||
sed -e "s|DOCKERHUB_USER|$(DOCKERHUB_USER)|g;s|ARCH_EXT|$(ARCH_EXT)|g;s|ALPINE_BASEIMAGE|$(ALPINE_BASEIMAGE)|g;s|QEMUARCH|$(QEMUARCH)|g" $^ > $@
|
sed -e "s|DOCKERHUB_USER|$(DOCKERHUB_USER)|g;s|ARCH_EXT|$(ARCH_EXT)|g;s|ALPINE_BASEIMAGE|$(ALPINE_BASEIMAGE)|g;s|QEMUARCH|$(QEMUARCH)|g" $^ > $@
|
||||||
ifeq ($(ARCH),amd64)
|
ifeq ($(ARCH),amd64)
|
||||||
# When building "normally" for amd64, remove the whole line, it has no part in the amd64 image
|
# When building "normally" for amd64, remove the whole line, it has no part in the amd64 image
|
||||||
sed -i "/CROSS_BUILD_/d" $@
|
sed -i.bak "/CROSS_BUILD_/d" $@
|
||||||
else
|
else
|
||||||
# When cross-building, only the placeholder "CROSS_BUILD_" should be removed
|
# When cross-building, only the placeholder "CROSS_BUILD_" should be removed
|
||||||
# Register /usr/bin/qemu-ARCH-static as the handler for ARM binaries in the kernel
|
# Register /usr/bin/qemu-ARCH-static as the handler for ARM and ppc64le binaries in the kernel
|
||||||
curl -sSL https://github.com/multiarch/qemu-user-static/releases/download/$(QEMU_VERSION)/x86_64_qemu-$(QEMUARCH)-static.tar.gz | tar -xz -C $(shell dirname $@)
|
curl -sSL https://github.com/multiarch/qemu-user-static/releases/download/$(QEMU_VERSION)/x86_64_qemu-$(QEMUARCH)-static.tar.gz | tar -xz -C $(shell dirname $@)
|
||||||
cd $(shell dirname $@) && sha256sum -c $(shell pwd)/build/shasums/qemu-$(QEMUARCH)-static.sha256sum
|
cd $(shell dirname $@) && sha256sum -c $(shell pwd)/build/shasums/qemu-$(QEMUARCH)-static.sha256sum
|
||||||
sed -i "s/CROSS_BUILD_//g" $@
|
sed -i.bak "s/CROSS_BUILD_//g" $@
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# The targets below builds the weave images
|
# The targets below builds the weave images
|
||||||
$(WEAVER_UPTODATE): prog/weaver/Dockerfile.$(DOCKERHUB_USER) $(WEAVER_EXE) weave $(WEAVEUTIL_EXE)
|
$(WEAVER_UPTODATE): prog/weaver/Dockerfile.$(DOCKERHUB_USER) $(WEAVER_EXE) weave $(WEAVEUTIL_EXE)
|
||||||
cp $(WEAVEUTIL_EXE) prog/weaver/weaveutil
|
cp $(WEAVEUTIL_EXE) prog/weaver/weaveutil
|
||||||
cp weave prog/weaver/weave
|
cp weave prog/weaver/weave
|
||||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -f prog/weaver/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVER_IMAGE) prog/weaver
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build --build-arg=revision=$(GIT_REVISION) -f prog/weaver/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVER_IMAGE) prog/weaver
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) prog/weaveexec/symlink $(DOCKER_DISTRIB) $(SIGPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVER_UPTODATE)
|
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) prog/weaveexec/symlink $(SIGPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVER_UPTODATE)
|
||||||
cp $(SIGPROXY_EXE) prog/weaveexec/sigproxy
|
cp $(SIGPROXY_EXE) prog/weaveexec/sigproxy
|
||||||
cp $(WEAVEWAIT_EXE) prog/weaveexec/weavewait
|
cp $(WEAVEWAIT_EXE) prog/weaveexec/weavewait
|
||||||
cp $(WEAVEWAIT_NOOP_EXE) prog/weaveexec/weavewait_noop
|
cp $(WEAVEWAIT_NOOP_EXE) prog/weaveexec/weavewait_noop
|
||||||
cp $(WEAVEWAIT_NOMCAST_EXE) prog/weaveexec/weavewait_nomcast
|
cp $(WEAVEWAIT_NOMCAST_EXE) prog/weaveexec/weavewait_nomcast
|
||||||
tar -xf $(DOCKER_DISTRIB) usr/local/bin/docker -O > prog/weaveexec/docker
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build --build-arg=revision=$(GIT_REVISION) -f prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVEEXEC_IMAGE) prog/weaveexec
|
||||||
chmod +x prog/weaveexec/docker
|
|
||||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -f prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVEEXEC_IMAGE) prog/weaveexec
|
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
# Builds Docker plugin.
|
# Builds Docker plugin.
|
||||||
@@ -287,17 +299,17 @@ $(PLUGIN_UPTODATE): prog/net-plugin/launch.sh prog/net-plugin/config.json $(WEAV
|
|||||||
$(SUDO) docker plugin create $(PLUGIN_IMAGE):$(NET_PLUGIN_LATEST) prog/net-plugin
|
$(SUDO) docker plugin create $(PLUGIN_IMAGE):$(NET_PLUGIN_LATEST) prog/net-plugin
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(WEAVEKUBE_UPTODATE): prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) prog/weave-kube/launch.sh $(KUBEPEERS_EXE) $(WEAVER_UPTODATE)
|
$(WEAVEKUBE_UPTODATE): prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) prog/weave-kube/launch.sh $(KUBEUTILS_EXE) $(WEAVER_UPTODATE)
|
||||||
cp $(KUBEPEERS_EXE) prog/weave-kube/
|
cp $(KUBEUTILS_EXE) prog/weave-kube/
|
||||||
$(SUDO) docker build -f prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVEKUBE_IMAGE) prog/weave-kube
|
$(SUDO) docker build --build-arg=revision=$(GIT_REVISION) -f prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVEKUBE_IMAGE) prog/weave-kube
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(WEAVENPC_UPTODATE): prog/weave-npc/Dockerfile.$(DOCKERHUB_USER) $(WEAVENPC_EXE) prog/weave-npc/ulogd.conf
|
$(WEAVENPC_UPTODATE): prog/weave-npc/Dockerfile.$(DOCKERHUB_USER) $(WEAVENPC_EXE) prog/weave-npc/ulogd.conf
|
||||||
$(SUDO) docker build -f prog/weave-npc/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVENPC_IMAGE) prog/weave-npc
|
$(SUDO) docker build --build-arg=revision=$(GIT_REVISION) -f prog/weave-npc/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVENPC_IMAGE) prog/weave-npc
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(WEAVEDB_UPTODATE): prog/weavedb/Dockerfile
|
$(WEAVEDB_UPTODATE): prog/weavedb/Dockerfile
|
||||||
$(SUDO) docker build -t $(WEAVEDB_IMAGE) prog/weavedb
|
$(SUDO) docker build --build-arg=revision=$(GIT_REVISION) -t $(WEAVEDB_IMAGE) prog/weavedb
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(NETWORKTESTER_UPTODATE): test/images/network-tester/Dockerfile $(NETWORKTESTER_EXE)
|
$(NETWORKTESTER_UPTODATE): test/images/network-tester/Dockerfile $(NETWORKTESTER_EXE)
|
||||||
@@ -307,11 +319,7 @@ $(NETWORKTESTER_UPTODATE): test/images/network-tester/Dockerfile $(NETWORKTESTER
|
|||||||
$(WEAVE_EXPORT): $(IMAGES_UPTODATE) $(WEAVEDB_UPTODATE)
|
$(WEAVE_EXPORT): $(IMAGES_UPTODATE) $(WEAVEDB_UPTODATE)
|
||||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker save $(addsuffix :latest,$(IMAGES)) | gzip > $@
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker save $(addsuffix :latest,$(IMAGES)) | gzip > $@
|
||||||
|
|
||||||
$(DOCKER_DISTRIB):
|
tools/.git:
|
||||||
curl -o $(DOCKER_DISTRIB) $(DOCKER_DISTRIB_URL)
|
|
||||||
cd $(shell dirname $@) && sha256sum -c $(shell pwd)/build/shasums/docker-tgz-$(WEAVEEXEC_DOCKER_ARCH).sha256sum
|
|
||||||
|
|
||||||
tools/.git $(MANIFEST_TOOL_DIR)/.git:
|
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
|
|
||||||
# CODE FOR PUBLISHING THE IMAGES
|
# CODE FOR PUBLISHING THE IMAGES
|
||||||
@@ -343,7 +351,7 @@ publish-one-arch: $(PUBLISH)
|
|||||||
$(PUBLISH): publish_%: $(IMAGES_UPTODATE)
|
$(PUBLISH): publish_%: $(IMAGES_UPTODATE)
|
||||||
# Tag :latest with the real version
|
# Tag :latest with the real version
|
||||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker tag $(DOCKERHUB_USER)/$*$(ARCH_EXT) $(DOCKERHUB_USER)/$*$(ARCH_EXT):$(WEAVE_VERSION)
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker tag $(DOCKERHUB_USER)/$*$(ARCH_EXT) $(DOCKERHUB_USER)/$*$(ARCH_EXT):$(WEAVE_VERSION)
|
||||||
# Push the image with the arch suffix for arm and arm64, and without suffix for amd64
|
# Push the image with the arch suffix for arm, arm64 and ppc64le, and without suffix for amd64
|
||||||
$(MAKE) DOCKER_HOST=$(DOCKER_HOST) DOCKERHUB_USER=$(DOCKERHUB_USER) WEAVE_VERSION=$(WEAVE_VERSION) UPDATE_LATEST=$(UPDATE_LATEST) push_$*$(ARCH_EXT)
|
$(MAKE) DOCKER_HOST=$(DOCKER_HOST) DOCKERHUB_USER=$(DOCKERHUB_USER) WEAVE_VERSION=$(WEAVE_VERSION) UPDATE_LATEST=$(UPDATE_LATEST) push_$*$(ARCH_EXT)
|
||||||
|
|
||||||
ifeq ($(ARCH),amd64)
|
ifeq ($(ARCH),amd64)
|
||||||
@@ -367,13 +375,13 @@ endif
|
|||||||
|
|
||||||
# This target pushes a manifest list; it takes one parameter, the image name.
|
# This target pushes a manifest list; it takes one parameter, the image name.
|
||||||
# The variable UPDATE_LATEST controls whether it updates the versioned tag and/or the latest tag
|
# The variable UPDATE_LATEST controls whether it updates the versioned tag and/or the latest tag
|
||||||
$(PUSH_ML): push_ml_%: $(MANIFEST_TOOL_EXE)
|
$(PUSH_ML): push_ml_%:
|
||||||
ifneq ($(UPDATE_LATEST),latest-only)
|
ifneq ($(UPDATE_LATEST),latest-only)
|
||||||
$(MANIFEST_TOOL_EXE) push from-args --platforms $(ML_PLATFORMS) --template $(DOCKERHUB_USER)/$*-ARCH:$(WEAVE_VERSION) --target $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
$(MANIFEST_TOOL_CMD) push from-args --platforms $(ML_PLATFORMS) --template $(DOCKERHUB_USER)/$*-ARCH:$(WEAVE_VERSION) --target $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||||
endif
|
endif
|
||||||
ifneq ($(UPDATE_LATEST),false)
|
ifneq ($(UPDATE_LATEST),false)
|
||||||
# Push the manifest list to :latest as well
|
# Push the manifest list to :latest as well
|
||||||
$(MANIFEST_TOOL_EXE) push from-args --platforms $(ML_PLATFORMS) --template $(DOCKERHUB_USER)/$*-ARCH:latest --target $(DOCKERHUB_USER)/$*:latest
|
$(MANIFEST_TOOL_CMD) push from-args --platforms $(ML_PLATFORMS) --template $(DOCKERHUB_USER)/$*-ARCH:latest --target $(DOCKERHUB_USER)/$*:latest
|
||||||
endif
|
endif
|
||||||
|
|
||||||
clean-work-dir:
|
clean-work-dir:
|
||||||
@@ -383,11 +391,11 @@ clean-bin:
|
|||||||
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(IMAGES)
|
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(IMAGES)
|
||||||
find prog -type f -name "Dockerfile.*" -not -name "Dockerfile.template" -print | xargs rm -f
|
find prog -type f -name "Dockerfile.*" -not -name "Dockerfile.template" -print | xargs rm -f
|
||||||
find prog -type f -name "*qemu-*" -print | xargs rm -f
|
find prog -type f -name "*qemu-*" -print | xargs rm -f
|
||||||
rm -rf $(EXES) $(IMAGES_UPTODATE) $(WEAVEDB_UPTODATE) weave*.tar.gz $(DOCKER_DISTRIB) prog/weaveexec/docker .pkg
|
rm -rf $(EXES) $(IMAGES_UPTODATE) $(WEAVEDB_UPTODATE) weave*.tar.gz .pkg
|
||||||
|
|
||||||
clean: clean-bin clean-work-dir
|
clean: clean-bin clean-work-dir
|
||||||
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(BUILD_IMAGE)
|
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(BUILD_IMAGE)
|
||||||
rm -rf test/tls/*.pem test/coverage.* test/coverage $(BUILD_UPTODATE) $(MANIFEST_TOOL_EXE)
|
rm -rf test/tls/*.pem test/coverage.* test/coverage $(BUILD_UPTODATE)
|
||||||
|
|
||||||
build:
|
build:
|
||||||
$(SUDO) go clean -i net
|
$(SUDO) go clean -i net
|
||||||
|
|||||||
@@ -62,6 +62,12 @@ func (client *Client) ReleaseIPsFor(ID string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// release all IP space owned by a peer
|
||||||
|
func (client *Client) RmPeer(peerName string) (string, error) {
|
||||||
|
result, err := client.httpVerb("DELETE", fmt.Sprintf("/peer/%s", peerName), nil)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
func (client *Client) DefaultSubnet() (*net.IPNet, error) {
|
func (client *Client) DefaultSubnet() (*net.IPNet, error) {
|
||||||
cidr, err := client.httpVerb("GET", fmt.Sprintf("/ipinfo/defaultsubnet"), nil)
|
cidr, err := client.httpVerb("GET", fmt.Sprintf("/ipinfo/defaultsubnet"), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Expose calls the router to assign the given IP addr to the weave bridge.
|
||||||
|
func (client *Client) Expose(ipAddr *net.IPNet) error {
|
||||||
|
_, err := client.httpVerb("POST", fmt.Sprintf("/expose/%s", ipAddr), nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -17,7 +17,9 @@ function install_terraform() {
|
|||||||
|
|
||||||
function install_ansible() {
|
function install_ansible() {
|
||||||
sudo apt-get update || true
|
sudo apt-get update || true
|
||||||
sudo apt-get install -qq -y python-pip python-dev libffi-dev libssl-dev && pip install --user -U cffi && pip install --user ansible
|
sudo apt-get install -qq -y python-pip python-dev libffi-dev libssl-dev \
|
||||||
|
&& pip install --user -U setuptools cffi \
|
||||||
|
&& pip install --user ansible
|
||||||
export PATH="$PATH:$HOME/.local/bin"
|
export PATH="$PATH:$HOME/.local/bin"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+35
-5
@@ -88,9 +88,11 @@ build() {
|
|||||||
|
|
||||||
## Inject the version numbers and build the distributables
|
## Inject the version numbers and build the distributables
|
||||||
## (library versions?)
|
## (library versions?)
|
||||||
sed -i "/SCRIPT_VERSION=/ c\SCRIPT_VERSION=\"$VERSION\"" ./weave
|
sed -i.bak "/SCRIPT_VERSION=/ c\SCRIPT_VERSION=\"$VERSION\"" ./weave
|
||||||
sed -i -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset.yaml
|
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset.yaml
|
||||||
sed -i -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.6.yaml
|
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.6.yaml
|
||||||
|
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.7.yaml
|
||||||
|
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.8.yaml
|
||||||
make SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER
|
make SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER
|
||||||
|
|
||||||
if make tests; then
|
if make tests; then
|
||||||
@@ -173,6 +175,20 @@ draft() {
|
|||||||
--name "weave-daemonset-k8s-1.6.yaml" \
|
--name "weave-daemonset-k8s-1.6.yaml" \
|
||||||
--file "./prog/weave-kube/weave-daemonset-k8s-1.6.yaml"
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.6.yaml"
|
||||||
|
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave \
|
||||||
|
--tag $LATEST_TAG \
|
||||||
|
--name "weave-daemonset-k8s-1.7.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.7.yaml"
|
||||||
|
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave \
|
||||||
|
--tag $LATEST_TAG \
|
||||||
|
--name "weave-daemonset-k8s-1.8.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.8.yaml"
|
||||||
|
|
||||||
echo "** Draft $TYPE $RELEASE_NAME $VERSION created at"
|
echo "** Draft $TYPE $RELEASE_NAME $VERSION created at"
|
||||||
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
||||||
}
|
}
|
||||||
@@ -188,7 +204,7 @@ publish() {
|
|||||||
|
|
||||||
if [ "$RELEASE_TYPE" = 'PRERELEASE' ] ; then
|
if [ "$RELEASE_TYPE" = 'PRERELEASE' ] ; then
|
||||||
echo "== Building and pushing plugin"
|
echo "== Building and pushing plugin"
|
||||||
make UPDATE_LATEST=$IS_MAINLINE SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
|
make UPDATE_LATEST=false SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
|
||||||
echo "** Plugin built and pushed"
|
echo "** Plugin built and pushed"
|
||||||
|
|
||||||
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
|
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
|
||||||
@@ -224,7 +240,7 @@ publish() {
|
|||||||
echo '** Sanity checks OK for publishing tag' $LATEST_TAG as $DOCKERHUB_USER/weave:$VERSION
|
echo '** Sanity checks OK for publishing tag' $LATEST_TAG as $DOCKERHUB_USER/weave:$VERSION
|
||||||
|
|
||||||
echo "== Building and pushing plugin"
|
echo "== Building and pushing plugin"
|
||||||
make UPDATE_LATEST=$IS_MAINLINE SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
|
make UPDATE_LATEST=true SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
|
||||||
echo "** Plugin built and pushed"
|
echo "** Plugin built and pushed"
|
||||||
|
|
||||||
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
|
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
|
||||||
@@ -274,6 +290,20 @@ publish() {
|
|||||||
--name "weave-daemonset-k8s-1.6.yaml" \
|
--name "weave-daemonset-k8s-1.6.yaml" \
|
||||||
--file "./prog/weave-kube/weave-daemonset-k8s-1.6.yaml"
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.6.yaml"
|
||||||
|
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave \
|
||||||
|
--tag latest_release \
|
||||||
|
--name "weave-daemonset-k8s-1.7.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.7.yaml"
|
||||||
|
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave \
|
||||||
|
--tag latest_release \
|
||||||
|
--name "weave-daemonset-k8s-1.8.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.8.yaml"
|
||||||
|
|
||||||
echo "** Release $RELEASE_NAME $VERSION published at"
|
echo "** Release $RELEASE_NAME $VERSION published at"
|
||||||
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
||||||
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/latest_release"
|
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/latest_release"
|
||||||
|
|||||||
+7
-4
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.8.1
|
FROM golang:1.10.1-stretch
|
||||||
|
|
||||||
# Support Raspberry Pi 2 and newer
|
# Support Raspberry Pi 2 and newer
|
||||||
ENV GOARM 7
|
ENV GOARM 7
|
||||||
@@ -6,17 +6,20 @@ ENV GOARM 7
|
|||||||
# The names of the architectures that we're fetching cross-compilers for from the Debian packages
|
# The names of the architectures that we're fetching cross-compilers for from the Debian packages
|
||||||
ENV DEB_CROSSPLATFORMS \
|
ENV DEB_CROSSPLATFORMS \
|
||||||
armhf \
|
armhf \
|
||||||
arm64
|
arm64 \
|
||||||
|
ppc64el
|
||||||
|
|
||||||
# The go platforms we are supporting to cross-compile
|
# The go platforms we are supporting to cross-compile
|
||||||
ENV GO_CROSSPLATFORMS \
|
ENV GO_CROSSPLATFORMS \
|
||||||
linux/arm \
|
linux/arm \
|
||||||
linux/arm64
|
linux/arm64 \
|
||||||
|
linux/ppc64le
|
||||||
|
|
||||||
# The names of the gcc cross-compilers we have installed
|
# The names of the gcc cross-compilers we have installed
|
||||||
ENV GCC_CROSSCOMPILERS \
|
ENV GCC_CROSSCOMPILERS \
|
||||||
arm-linux-gnueabihf \
|
arm-linux-gnueabihf \
|
||||||
aarch64-linux-gnu
|
aarch64-linux-gnu \
|
||||||
|
powerpc64le-linux-gnu
|
||||||
|
|
||||||
# Temporarily add the Debian repositories, because we're going to install gcc cross-compilers from there
|
# Temporarily add the Debian repositories, because we're going to install gcc cross-compilers from there
|
||||||
# Install the build-essential and crossbuild-essential-ARCH packages
|
# Install the build-essential and crossbuild-essential-ARCH packages
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
8f313a9df867c207ae9ceac6e0935d1d2a0360aafce780dd5d1e0687ee98e0c3 docker-1.10.3.tgz
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
7541b5490cee1183d911f909c0e91d3d9d57b2b775e40f14a1a7a6de216ca4a8 docker-1.10.3.tgz
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
926f7f265332076ea4abf6e9586288376dbed2322c155b8d0cafbee614aaa7f2 qemu-ppc64le-static
|
||||||
+1
-1
@@ -44,7 +44,7 @@ test:
|
|||||||
parallel: true
|
parallel: true
|
||||||
- "$SRCDIR/bin/circle-test-smoke":
|
- "$SRCDIR/bin/circle-test-smoke":
|
||||||
parallel: true
|
parallel: true
|
||||||
timeout: 300
|
timeout: 360
|
||||||
post:
|
post:
|
||||||
- "$SRCDIR/bin/circle-test-teardown":
|
- "$SRCDIR/bin/circle-test-teardown":
|
||||||
parallel: true
|
parallel: true
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ func (c *Client) EnsureNetwork(networkName, driver, subnet string, options map[s
|
|||||||
Name: networkName,
|
Name: networkName,
|
||||||
CheckDuplicate: true,
|
CheckDuplicate: true,
|
||||||
Driver: driver,
|
Driver: driver,
|
||||||
IPAM: docker.IPAMOptions{
|
IPAM: &docker.IPAMOptions{
|
||||||
Driver: driver,
|
Driver: driver,
|
||||||
Config: []docker.IPAMConfig{{Subnet: subnet}},
|
Config: []docker.IPAMConfig{{Subnet: subnet}},
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type textFormatter struct {
|
type textFormatter struct {
|
||||||
|
|||||||
@@ -75,6 +75,14 @@ func DeleteDatapath(dpname string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AddDatapathInterface(dpname string, ifname string) error {
|
func AddDatapathInterface(dpname string, ifname string) error {
|
||||||
|
return addDatapathInterface(dpname, ifname, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddDatapathInterfaceIfNotExist(dpname string, ifname string) error {
|
||||||
|
return addDatapathInterface(dpname, ifname, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addDatapathInterface(dpname, ifname string, ignoreIfExist bool) error {
|
||||||
dpif, err := odp.NewDpif()
|
dpif, err := odp.NewDpif()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -87,5 +95,9 @@ func AddDatapathInterface(dpname string, ifname string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err = dp.CreateVport(odp.NewNetdevVportSpec(ifname))
|
_, err = dp.CreateVport(odp.NewNetdevVportSpec(ifname))
|
||||||
|
if ignoreIfExist && err == odp.NetlinkError(syscall.EEXIST) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Source IP addr of packet sent to k8s Service
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
* Weave Net: v2.2.1.
|
||||||
|
* Kubernetes: v1.10.0.
|
||||||
|
* kube-proxy: iptables mode (default kubeadm options).
|
||||||
|
* Hosts: `h1` and `h2`.
|
||||||
|
|
||||||
|
## Source IP
|
||||||
|
|
||||||
|
\# | Src | Target | Dst | Src IP | `-j WEAVE-NPC`
|
||||||
|
---|-------------|-------------------|--------|------------------|--------------
|
||||||
|
1 | Pod_h1 | ip(Pod_h2) | Pod_h2 | ip(Pod_h1) | OK
|
||||||
|
2 | h1 | ClusterIP | Pod_h1 | ip(weave_h1) | **NOK**
|
||||||
|
3 | h1 | ClusterIP | Pod_h2 | ip(weave_h1) | OK
|
||||||
|
4 | Pod_h1 | ClusterIP | Pod_h1 | ip(weave_h1) | **NOK**
|
||||||
|
5 | Pod_h1 | ClusterIP | Pod_h2 | ip(weave_h1) | OK
|
||||||
|
6 | h1 | ip(h1):NodePort | Pod_h1 | ip(weave_h1) | **NOK**
|
||||||
|
7 | h1 | ip(h1):NodePort | Pod_h2 | ip(weave_h1) | OK
|
||||||
|
8 | h1 | ip(h2):NodePort | Pod_h1 | ip(weave_h2) | OK
|
||||||
|
9 | h1 | ip(h2):NodePort | Pod_h2 | ip(weave_h2) | ??? Can't reproduce
|
||||||
|
10 | Pod_h1 | ip(h1):NodePort | Pod_h1 | ip(weave_h1) | **NOK**
|
||||||
|
11 | Pod_h1 | ip(h1):NodePort | Pod_h2 | ip(weave_h1) | OK
|
||||||
|
12 | Pod_h1 | ip(h2):NodePort | Pod_h1 | ip(weave_h2) | OK
|
||||||
|
13 | Pod_h1 | ip(h2):NodePort | Pod_h2 | ip(weave_h2) | OK
|
||||||
|
|
||||||
|
Remarks:
|
||||||
|
|
||||||
|
* **Src IP** is of a packet which is captured on the weave bridge.
|
||||||
|
* **-j WEAVE-NPC** - whether a packet enters the `filter/WEAVE-NPC` iptables chain (OK = NetworkPolicy is enforced as required).
|
||||||
|
* **Pod_h1** - a Pod running on the `h1` host.
|
||||||
|
* **ip(weave_h1)** - IP addr of the weave bridge on the `h1` host.
|
||||||
+40
-2
@@ -127,14 +127,52 @@ Finally, for **Mainline** releases only:
|
|||||||
|
|
||||||
* Images tagged `latest` are updated on DockerHub
|
* Images tagged `latest` are updated on DockerHub
|
||||||
|
|
||||||
|
Now, you can validate whether images were published for all platforms:
|
||||||
|
|
||||||
|
# Required platforms for each image:
|
||||||
|
grep '^ML_PLATFORMS=' Makefile
|
||||||
|
|
||||||
|
# Published platforms per image:
|
||||||
|
for img in $(grep '^PUBLISH=' Makefile); do
|
||||||
|
img="weaveworks/$(echo $img | cut -d_ -f2):${TAG#v}"
|
||||||
|
platforms=$(vendor/github.com/estesp/manifest-tool/manifest-tool \
|
||||||
|
inspect --raw "$img" | \
|
||||||
|
jq '.[].Platform | .os + "/" +.architecture' | \
|
||||||
|
tr '\n' ' ')
|
||||||
|
echo "$img: $platforms"
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
### Docker Store
|
||||||
|
|
||||||
|
Weave Net is [available in Docker Store](https://store.docker.com/plugins/weave-net-plugin) and should also be released there.
|
||||||
|
|
||||||
|
* Go to https://store.docker.com/ and log in.
|
||||||
|
* Go to "[_Publish_](https://store.docker.com/publisher/center)".
|
||||||
|
* Under "_My Products_", select "_owners (weaveworks)_". Weave Net should be listed among our products.
|
||||||
|
* Click "_Actions_" > "_Edit Product_".
|
||||||
|
* Go to "_Plans & Pricing_" > "_Free Tier_".
|
||||||
|
* Under "_Source Repositories & Tags_", click on "_Add Source_", select "_net-plugin_" under "_Repository_" and the version to release under "_Tag_".
|
||||||
|
* Under "_Resources_" > "_Installation Instructions_", update the lines containing `export NET_VERSION=<version>`.
|
||||||
|
* Click "_Submit For Review_". You should see a message like: "_Your product has been submitted for approval. [...] We'll be in touch with next steps soon!_".
|
||||||
|
* You should receive an email saying:
|
||||||
|
|
||||||
|
> This email confirms that we received your submission on \<date\> of Weave Net to the Docker Store.
|
||||||
|
>
|
||||||
|
> We're reviewing your submission to ensure that it meets our [security guidelines](https://success.docker.com/Store#Security_and_Audit_Policies) and complies with our [best practices](https://success.docker.com/Store#Create_Great_Content). Don't worry! We'll let you know if there's anything you need to change before we publish your submission. You should hear back from us within the next 14 days.
|
||||||
|
>
|
||||||
|
> Thanks for submitting your content to the Docker Store!
|
||||||
|
|
||||||
|
* Hope Docker eventually performs the release or contacts us. If nothing happens within 14 days, contact their support team: publisher-support@docker.com.
|
||||||
|
|
||||||
### Finish up
|
### Finish up
|
||||||
|
|
||||||
* If not on master, merge branch into master and push to GitHub.
|
* If not on master, merge branch into master and push to GitHub.
|
||||||
* Close the [milestone](https://github.com/weaveworks/weave/milestones) in GitHub and create the next milestone
|
* Close the [milestone](https://github.com/weaveworks/weave/milestones) in GitHub and create the next milestone
|
||||||
* Update the `#weavenetwork` topic heading on freenode (requires 'chanops' permission)
|
* Update the `#weavenetwork` topic heading on freenode (requires 'chanops' permission)
|
||||||
* For a mainline release vX.Y.0, create a release branch X.Y from the
|
* For a mainline release vX.Y.0, create a release branch X.Y from the
|
||||||
tag and push to GitHub - this will result in X.Y.0 site docs being
|
tag, push to GitHub and set [WEAVE_NET_REV](https://github.com/weaveworks/website-next/blob/master/Makefile)
|
||||||
published to https://www.weave.works
|
to X.Y via a PR - this will result in X.Y.0 site docs being published to https://www.weave.works
|
||||||
* Add the new version of `weave-net` to the checkpoint system at
|
* Add the new version of `weave-net` to the checkpoint system at
|
||||||
https://checkpoint-api.weave.works/admin
|
https://checkpoint-api.weave.works/admin
|
||||||
* File a PR to update the version of the daemonset at
|
* File a PR to update the version of the daemonset at
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ type Allocator struct {
|
|||||||
isKnownPeer func(mesh.PeerName) bool
|
isKnownPeer func(mesh.PeerName) bool
|
||||||
quorum func() uint
|
quorum func() uint
|
||||||
now func() time.Time
|
now func() time.Time
|
||||||
|
tracker tracker.LocalRangeTracker
|
||||||
}
|
}
|
||||||
|
|
||||||
// PreClaims are IP addresses discovered before we could initialize IPAM
|
// PreClaims are IP addresses discovered before we could initialize IPAM
|
||||||
@@ -130,6 +131,7 @@ func NewAllocator(config Config) *Allocator {
|
|||||||
quorum: config.Quorum,
|
quorum: config.Quorum,
|
||||||
dead: make(map[string]time.Time),
|
dead: make(map[string]time.Time),
|
||||||
now: time.Now,
|
now: time.Now,
|
||||||
|
tracker: config.Tracker,
|
||||||
}
|
}
|
||||||
|
|
||||||
alloc.pendingClaims = make([]operation, len(config.PreClaims))
|
alloc.pendingClaims = make([]operation, len(config.PreClaims))
|
||||||
|
|||||||
+2
-2
@@ -88,7 +88,7 @@ func (alloc *Allocator) handleHTTPClaim(dockerCli *docker.Client, w http.Respons
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HandleHTTP wires up ipams HTTP endpoints to the provided mux.
|
// HandleHTTP wires up ipams HTTP endpoints to the provided mux.
|
||||||
func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CIDR, tracker string, dockerCli *docker.Client) {
|
func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CIDR, dockerCli *docker.Client) {
|
||||||
router.Methods("GET").Path("/ipinfo/defaultsubnet").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
router.Methods("GET").Path("/ipinfo/defaultsubnet").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Fprintf(w, "%s", defaultSubnet)
|
fmt.Fprintf(w, "%s", defaultSubnet)
|
||||||
})
|
})
|
||||||
@@ -199,6 +199,6 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CID
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.Methods("GET").Path("/ipinfo/tracker").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
router.Methods("GET").Path("/ipinfo/tracker").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Fprintf(w, tracker)
|
fmt.Fprintf(w, alloc.tracker.String())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
+2
-2
@@ -43,7 +43,7 @@ func listenHTTP(alloc *Allocator, subnet address.CIDR) int {
|
|||||||
router.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
|
router.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
|
||||||
io.WriteString(w, fmt.Sprintln(alloc))
|
io.WriteString(w, fmt.Sprintln(alloc))
|
||||||
})
|
})
|
||||||
alloc.HandleHTTP(router, subnet, "", nil)
|
alloc.HandleHTTP(router, subnet, nil)
|
||||||
|
|
||||||
httpListener, err := net.Listen("tcp", ":0")
|
httpListener, err := net.Listen("tcp", ":0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -132,7 +132,7 @@ func TestBadHttp(t *testing.T) {
|
|||||||
// Verb that's not handled
|
// Verb that's not handled
|
||||||
resp, err := doHTTP("HEAD", fmt.Sprintf("http://localhost:%d/ip/%s/%s", port, containerID, testAddr1))
|
resp, err := doHTTP("HEAD", fmt.Sprintf("http://localhost:%d/ip/%s/%s", port, containerID, testAddr1))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, http.StatusNotFound, resp.StatusCode, "http response")
|
require.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode, "http response")
|
||||||
// Mis-spelled URL
|
// Mis-spelled URL
|
||||||
resp, err = doHTTP("POST", fmt.Sprintf("http://localhost:%d/xip/%s/", port, containerID))
|
resp, err = doHTTP("POST", fmt.Sprintf("http://localhost:%d/xip/%s/", port, containerID))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ func CheckAllExpectedMessagesSent(allocs ...*Allocator) {
|
|||||||
m := alloc.gossip.(*mockGossipComms)
|
m := alloc.gossip.(*mockGossipComms)
|
||||||
m.RLock()
|
m.RLock()
|
||||||
if len(m.messages) > 0 {
|
if len(m.messages) > 0 {
|
||||||
require.FailNow(m, fmt.Sprintf("%s: Gossip message(s) not sent as expected: \n%x", m.name, m.messages))
|
require.FailNow(m, fmt.Sprintf("%s: Gossip message(s) not sent as expected: \n%v", m.name, m.messages))
|
||||||
}
|
}
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-42
@@ -84,7 +84,7 @@ func NewAWSVPCTracker(bridgeName string) (*AWSVPCTracker, error) {
|
|||||||
func (t *AWSVPCTracker) HandleUpdate(prevRanges, currRanges []address.Range, local bool) error {
|
func (t *AWSVPCTracker) HandleUpdate(prevRanges, currRanges []address.Range, local bool) error {
|
||||||
t.debugf("replacing %q by %q; local(%t)", prevRanges, currRanges, local)
|
t.debugf("replacing %q by %q; local(%t)", prevRanges, currRanges, local)
|
||||||
|
|
||||||
prev, curr := removeCommon(address.NewCIDRs(merge(prevRanges)), address.NewCIDRs(merge(currRanges)))
|
prev, curr := RemoveCommon(address.NewCIDRs(Merge(prevRanges)), address.NewCIDRs(Merge(currRanges)))
|
||||||
|
|
||||||
// It might make sense to do the removal first and then add entries
|
// It might make sense to do the removal first and then add entries
|
||||||
// because of the 50 routes limit. However, in such case a container might
|
// because of the 50 routes limit. However, in such case a container might
|
||||||
@@ -123,6 +123,10 @@ func (t *AWSVPCTracker) HandleUpdate(prevRanges, currRanges []address.Range, loc
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *AWSVPCTracker) String() string {
|
||||||
|
return "awsvpc"
|
||||||
|
}
|
||||||
|
|
||||||
func (t *AWSVPCTracker) createVPCRoute(cidr string) (*ec2.CreateRouteOutput, error) {
|
func (t *AWSVPCTracker) createVPCRoute(cidr string) (*ec2.CreateRouteOutput, error) {
|
||||||
route := &ec2.CreateRouteInput{
|
route := &ec2.CreateRouteInput{
|
||||||
RouteTableId: &t.routeTableID,
|
RouteTableId: &t.routeTableID,
|
||||||
@@ -232,47 +236,6 @@ func (t *AWSVPCTracker) infof(fmt string, args ...interface{}) {
|
|||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
// merge merges adjacent range entries.
|
|
||||||
// The given slice has to be sorted in increasing order.
|
|
||||||
func merge(r []address.Range) []address.Range {
|
|
||||||
var merged []address.Range
|
|
||||||
|
|
||||||
for i := range r {
|
|
||||||
if prev := len(merged) - 1; prev >= 0 && merged[prev].End == r[i].Start {
|
|
||||||
merged[prev].End = r[i].End
|
|
||||||
} else {
|
|
||||||
merged = append(merged, r[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeCommon filters out CIDR ranges which are contained in both a and b slices.
|
|
||||||
// Both slices have to be sorted in increasing order.
|
|
||||||
func removeCommon(a, b []address.CIDR) (newA, newB []address.CIDR) {
|
|
||||||
i, j := 0, 0
|
|
||||||
|
|
||||||
for i < len(a) && j < len(b) {
|
|
||||||
switch {
|
|
||||||
case a[i].Start() < b[j].Start() || a[i].End() < b[j].End():
|
|
||||||
newA = append(newA, a[i])
|
|
||||||
i++
|
|
||||||
case a[i].Start() > b[j].Start() || a[i].End() > b[j].End():
|
|
||||||
newB = append(newB, b[j])
|
|
||||||
j++
|
|
||||||
default:
|
|
||||||
i++
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
newA = append(newA, a[i:]...)
|
|
||||||
newB = append(newB, b[j:]...)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseCIDR(cidr string) (*net.IPNet, error) {
|
func parseCIDR(cidr string) (*net.IPNet, error) {
|
||||||
ip, ipnet, err := net.ParseCIDR(cidr)
|
ip, ipnet, err := net.ParseCIDR(cidr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package tracker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/weaveworks/weave/net/address"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Merge merges adjacent range entries.
|
||||||
|
// The given slice has to be sorted in increasing order.
|
||||||
|
func Merge(r []address.Range) []address.Range {
|
||||||
|
var merged []address.Range
|
||||||
|
|
||||||
|
for i := range r {
|
||||||
|
if prev := len(merged) - 1; prev >= 0 && merged[prev].End == r[i].Start {
|
||||||
|
merged[prev].End = r[i].End
|
||||||
|
} else {
|
||||||
|
merged = append(merged, r[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return merged
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveCommon filters out CIDR ranges which are contained in both a and b slices.
|
||||||
|
// Both slices have to be sorted in increasing order.
|
||||||
|
func RemoveCommon(a, b []address.CIDR) (newA, newB []address.CIDR) {
|
||||||
|
i, j := 0, 0
|
||||||
|
|
||||||
|
for i < len(a) && j < len(b) {
|
||||||
|
switch {
|
||||||
|
case a[i].Start() < b[j].Start() || a[i].End() < b[j].End():
|
||||||
|
newA = append(newA, a[i])
|
||||||
|
i++
|
||||||
|
case a[i].Start() > b[j].Start() || a[i].End() > b[j].End():
|
||||||
|
newB = append(newB, b[j])
|
||||||
|
j++
|
||||||
|
default:
|
||||||
|
i++
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
newA = append(newA, a[i:]...)
|
||||||
|
newB = append(newB, b[j:]...)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ var (
|
|||||||
func TestRemoveCommon(t *testing.T) {
|
func TestRemoveCommon(t *testing.T) {
|
||||||
a := []address.CIDR{r0to127, r1dot0to255}
|
a := []address.CIDR{r0to127, r1dot0to255}
|
||||||
b := []address.CIDR{r1dot0to255, r2dot0to255}
|
b := []address.CIDR{r1dot0to255, r2dot0to255}
|
||||||
newA, newB := removeCommon(a, b)
|
newA, newB := RemoveCommon(a, b)
|
||||||
require.Equal(t, []address.CIDR{r0to127}, newA)
|
require.Equal(t, []address.CIDR{r0to127}, newA)
|
||||||
require.Equal(t, []address.CIDR{r2dot0to255}, newB)
|
require.Equal(t, []address.CIDR{r2dot0to255}, newB)
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ func TestMerge(t *testing.T) {
|
|||||||
r128to255.Range(),
|
r128to255.Range(),
|
||||||
r2dot0to255.Range(),
|
r2dot0to255.Range(),
|
||||||
}
|
}
|
||||||
require.Equal(t, []address.Range{r0to255.Range(), r2dot0to255.Range()}, merge(ranges))
|
require.Equal(t, []address.Range{r0to255.Range(), r2dot0to255.Range()}, Merge(ranges))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper
|
// Helper
|
||||||
@@ -17,4 +17,7 @@ type LocalRangeTracker interface {
|
|||||||
// The local parameter indicates whether the ranges belong to the peer
|
// The local parameter indicates whether the ranges belong to the peer
|
||||||
// by which the method is called.
|
// by which the method is called.
|
||||||
HandleUpdate(prevRanges, currRanges []address.Range, local bool) error
|
HandleUpdate(prevRanges, currRanges []address.Range, local bool) error
|
||||||
|
|
||||||
|
// String returns the tracker name
|
||||||
|
String() string
|
||||||
}
|
}
|
||||||
+2
-5
@@ -10,17 +10,14 @@ import (
|
|||||||
// Configure the ARP cache parameters for the given interface. This
|
// Configure the ARP cache parameters for the given interface. This
|
||||||
// makes containers react more quickly to a change in the MAC address
|
// makes containers react more quickly to a change in the MAC address
|
||||||
// associated with an IP address.
|
// associated with an IP address.
|
||||||
func ConfigureARPCache(procPath, name string) error {
|
func configureARPCache(procPath, name string) error {
|
||||||
if err := sysctl(procPath, fmt.Sprintf("net/ipv4/neigh/%s/base_reachable_time", name), "5"); err != nil {
|
if err := sysctl(procPath, fmt.Sprintf("net/ipv4/neigh/%s/base_reachable_time", name), "5"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := sysctl(procPath, fmt.Sprintf("net/ipv4/neigh/%s/delay_first_probe_time", name), "2"); err != nil {
|
if err := sysctl(procPath, fmt.Sprintf("net/ipv4/neigh/%s/delay_first_probe_time", name), "2"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := sysctl(procPath, fmt.Sprintf("net/ipv4/neigh/%s/ucast_solicit", name), "1"); err != nil {
|
return sysctl(procPath, fmt.Sprintf("net/ipv4/neigh/%s/ucast_solicit", name), "1")
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sysctl(procPath, variable, value string) error {
|
func sysctl(procPath, variable, value string) error {
|
||||||
|
|||||||
+160
-70
@@ -6,13 +6,18 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/j-keck/arping"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/common"
|
||||||
"github.com/weaveworks/weave/common/odp"
|
"github.com/weaveworks/weave/common/odp"
|
||||||
|
"github.com/weaveworks/weave/ipam/tracker"
|
||||||
|
"github.com/weaveworks/weave/net/address"
|
||||||
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
|
"github.com/weaveworks/weave/npc"
|
||||||
)
|
)
|
||||||
|
|
||||||
/* This code implements three possible configurations to connect
|
/* This code implements three possible configurations to connect
|
||||||
@@ -48,11 +53,12 @@ datapath of old kernel versions (https://github.com/weaveworks/weave/issues/1577
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const (
|
const (
|
||||||
WeaveBridgeName = "weave"
|
WeaveBridgeName = "weave"
|
||||||
DatapathName = "datapath"
|
DatapathName = "datapath"
|
||||||
DatapathIfName = "vethwe-datapath"
|
DatapathIfName = "vethwe-datapath"
|
||||||
BridgeIfName = "vethwe-bridge"
|
BridgeIfName = "vethwe-bridge"
|
||||||
PcapIfName = "vethwe-pcap"
|
PcapIfName = "vethwe-pcap"
|
||||||
|
NoMasqLocalIpset = ipset.Name("weaver-no-masq-local")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Bridge interface {
|
type Bridge interface {
|
||||||
@@ -214,6 +220,7 @@ type BridgeConfig struct {
|
|||||||
MTU int
|
MTU int
|
||||||
Mac string
|
Mac string
|
||||||
Port int
|
Port int
|
||||||
|
NoMasqLocal bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (config *BridgeConfig) configuredBridgeType() Bridge {
|
func (config *BridgeConfig) configuredBridgeType() Bridge {
|
||||||
@@ -227,13 +234,20 @@ func (config *BridgeConfig) configuredBridgeType() Bridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger) (Bridge, error) {
|
func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger, ips ipset.Interface) (Bridge, error) {
|
||||||
bridgeType, err := ExistingBridgeType(config.WeaveBridgeName, config.DatapathName)
|
existingBridgeType, err := ExistingBridgeType(config.WeaveBridgeName, config.DatapathName)
|
||||||
if bridgeType != nil || err != nil {
|
if err != nil {
|
||||||
return bridgeType, err
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bridgeType := config.configuredBridgeType()
|
||||||
|
|
||||||
|
if existingBridgeType != nil && bridgeType.String() != existingBridgeType.String() {
|
||||||
|
return nil,
|
||||||
|
fmt.Errorf("Existing bridge type %q is different than requested %q. Please do 'weave reset' and try again",
|
||||||
|
existingBridgeType, bridgeType)
|
||||||
}
|
}
|
||||||
|
|
||||||
bridgeType = config.configuredBridgeType()
|
|
||||||
for {
|
for {
|
||||||
if err := bridgeType.init(config); err != nil {
|
if err := bridgeType.init(config); err != nil {
|
||||||
if errors.Cause(err) == errBridgeNotSupported {
|
if errors.Cause(err) == errBridgeNotSupported {
|
||||||
@@ -246,7 +260,7 @@ func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger) (Br
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := configureIPTables(config); err != nil {
|
if err := configureIPTables(config, ips); err != nil {
|
||||||
return bridgeType, errors.Wrap(err, "configuring iptables")
|
return bridgeType, errors.Wrap(err, "configuring iptables")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,10 +283,16 @@ func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger) (Br
|
|||||||
return bridgeType, err
|
return bridgeType, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ConfigureARPCache(procPath, config.WeaveBridgeName); err != nil {
|
if err := configureARPCache(procPath, config.WeaveBridgeName); err != nil {
|
||||||
return bridgeType, errors.Wrapf(err, "configuring ARP cache on bridge %q", config.WeaveBridgeName)
|
return bridgeType, errors.Wrapf(err, "configuring ARP cache on bridge %q", config.WeaveBridgeName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NB: No concurrent call to Expose is possible, as EnsureBridge is called
|
||||||
|
// before any service has been started.
|
||||||
|
if err := reexpose(config, log); err != nil {
|
||||||
|
return bridgeType, err
|
||||||
|
}
|
||||||
|
|
||||||
return bridgeType, nil
|
return bridgeType, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,7 +309,7 @@ func (b bridgeImpl) initPrep(config *BridgeConfig) error {
|
|||||||
config.MTU = 65535
|
config.MTU = 65535
|
||||||
}
|
}
|
||||||
b.bridge = &netlink.Bridge{LinkAttrs: linkAttrs}
|
b.bridge = &netlink.Bridge{LinkAttrs: linkAttrs}
|
||||||
if err = netlink.LinkAdd(b.bridge); err != nil {
|
if err := LinkAddIfNotExist(b.bridge); err != nil {
|
||||||
return errors.Wrapf(err, "creating bridge %q", config.WeaveBridgeName)
|
return errors.Wrapf(err, "creating bridge %q", config.WeaveBridgeName)
|
||||||
}
|
}
|
||||||
if err := netlink.LinkSetHardwareAddr(b.bridge, mac); err != nil {
|
if err := netlink.LinkSetHardwareAddr(b.bridge, mac); err != nil {
|
||||||
@@ -321,7 +341,7 @@ func (b bridgeImpl) init(config *BridgeConfig) error {
|
|||||||
if err := b.initPrep(config); err != nil {
|
if err := b.initPrep(config); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := CreateAndAttachVeth(BridgeIfName, PcapIfName, config.WeaveBridgeName, config.MTU, true, func(veth netlink.Link) error {
|
if _, err := CreateAndAttachVeth(BridgeIfName, PcapIfName, config.WeaveBridgeName, config.MTU, true, false, func(veth netlink.Link) error {
|
||||||
return netlink.LinkSetUp(veth)
|
return netlink.LinkSetUp(veth)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return errors.Wrap(err, "creating pcap veth pair")
|
return errors.Wrap(err, "creating pcap veth pair")
|
||||||
@@ -371,11 +391,11 @@ func (bf bridgedFastdpImpl) init(config *BridgeConfig) error {
|
|||||||
if err := bf.bridgeImpl.initPrep(config); err != nil {
|
if err := bf.bridgeImpl.initPrep(config); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := CreateAndAttachVeth(BridgeIfName, DatapathIfName, config.WeaveBridgeName, config.MTU, true, func(veth netlink.Link) error {
|
if _, err := CreateAndAttachVeth(BridgeIfName, DatapathIfName, config.WeaveBridgeName, config.MTU, true, false, func(veth netlink.Link) error {
|
||||||
if err := netlink.LinkSetUp(veth); err != nil {
|
if err := netlink.LinkSetUp(veth); err != nil {
|
||||||
return errors.Wrapf(err, "setting link up on %q", veth.Attrs().Name)
|
return errors.Wrapf(err, "setting link up on %q", veth.Attrs().Name)
|
||||||
}
|
}
|
||||||
if err := odp.AddDatapathInterface(bf.datapathName, veth.Attrs().Name); err != nil {
|
if err := odp.AddDatapathInterfaceIfNotExist(bf.datapathName, veth.Attrs().Name); err != nil {
|
||||||
return errors.Wrapf(err, "adding interface %q to datapath %q", veth.Attrs().Name, bf.datapathName)
|
return errors.Wrapf(err, "adding interface %q to datapath %q", veth.Attrs().Name, bf.datapathName)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -383,11 +403,7 @@ func (bf bridgedFastdpImpl) init(config *BridgeConfig) error {
|
|||||||
return errors.Wrap(err, "creating bridged fastdp veth pair")
|
return errors.Wrap(err, "creating bridged fastdp veth pair")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := linkSetUpByName(bf.datapathName); err != nil {
|
return linkSetUpByName(bf.datapathName)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b bridgeImpl) attach(veth *netlink.Veth) error {
|
func (b bridgeImpl) attach(veth *netlink.Veth) error {
|
||||||
@@ -399,20 +415,21 @@ func (bf bridgedFastdpImpl) attach(veth *netlink.Veth) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f fastdpImpl) attach(veth *netlink.Veth) error {
|
func (f fastdpImpl) attach(veth *netlink.Veth) error {
|
||||||
return odp.AddDatapathInterface(f.datapathName, veth.Attrs().Name)
|
return odp.AddDatapathInterfaceIfNotExist(f.datapathName, veth.Attrs().Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureIPTables(config *BridgeConfig) error {
|
func configureIPTables(config *BridgeConfig, ips ipset.Interface) error {
|
||||||
ipt, err := iptables.New()
|
ipt, err := iptables.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "creating iptables object")
|
return errors.Wrap(err, "creating iptables object")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The order among weave filter/FORWARD rules is important!
|
||||||
|
fwdRules := make([][]string, 0)
|
||||||
|
|
||||||
if config.DockerBridgeName != "" {
|
if config.DockerBridgeName != "" {
|
||||||
if config.WeaveBridgeName != config.DockerBridgeName {
|
if config.WeaveBridgeName != config.DockerBridgeName {
|
||||||
err := ipt.Insert("filter", "FORWARD", 1, "-i", config.DockerBridgeName, "-o", config.WeaveBridgeName, "-j", "DROP")
|
fwdRules = append(fwdRules, []string{"-i", config.DockerBridgeName, "-o", config.WeaveBridgeName, "-j", "DROP"})
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dockerBridgeIP, err := FindBridgeIP(config.DockerBridgeName, nil)
|
dockerBridgeIP, err := FindBridgeIP(config.DockerBridgeName, nil)
|
||||||
@@ -441,42 +458,121 @@ func configureIPTables(config *BridgeConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if config.NPC {
|
if config.NPC {
|
||||||
// Steer traffic via the NPC
|
// Steer traffic via the NPC.
|
||||||
_ = ipt.NewChain("filter", "WEAVE-NPC") // ignore error because it might already exist
|
|
||||||
// If WEAVE-NPC chain doesn't exist then next line will fail
|
if err = ensureChains(ipt, "filter", npc.MainChain, npc.EgressChain); err != nil {
|
||||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "WEAVE-NPC"); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-m", "state", "--state", "NEW", "-j", "NFLOG", "--nflog-group", "86"); err != nil {
|
|
||||||
return err
|
// Steer egress traffic destined to local node.
|
||||||
}
|
if err = ipt.AppendUnique("filter", "INPUT", "-i", config.WeaveBridgeName, "-j", npc.EgressChain); err != nil {
|
||||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "DROP"); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
fwdRules = append(fwdRules,
|
||||||
|
[][]string{
|
||||||
|
// Might include ingress traffic which is fine as long as we do not
|
||||||
|
// ACCEPT in WEAVE-NPC-EGRESS chain
|
||||||
|
{"-i", config.WeaveBridgeName,
|
||||||
|
"-m", "comment", "--comment", "NOTE: this must go before '-j KUBE-FORWARD'",
|
||||||
|
"-j", npc.EgressChain},
|
||||||
|
// The following rules are for ingress NPC processing
|
||||||
|
{"-o", config.WeaveBridgeName,
|
||||||
|
"-m", "comment", "--comment", "NOTE: this must go before '-j KUBE-FORWARD'",
|
||||||
|
"-j", npc.MainChain},
|
||||||
|
{"-o", config.WeaveBridgeName, "-m", "state", "--state", "NEW", "-j", "NFLOG", "--nflog-group", "86"},
|
||||||
|
{"-o", config.WeaveBridgeName, "-j", "DROP"},
|
||||||
|
}...)
|
||||||
} else {
|
} else {
|
||||||
// Work around the situation where there are no rules allowing traffic
|
// Work around the situation where there are no rules allowing traffic
|
||||||
// across our bridge. E.g. ufw
|
// across our bridge. E.g. ufw
|
||||||
if err = ipt.AppendUnique("filter", "FORWARD", "-i", config.WeaveBridgeName, "-o", config.WeaveBridgeName, "-j", "ACCEPT"); err != nil {
|
fwdRules = append(fwdRules, []string{"-i", config.WeaveBridgeName, "-o", config.WeaveBridgeName, "-j", "ACCEPT"})
|
||||||
return err
|
}
|
||||||
|
|
||||||
|
if !config.NPC {
|
||||||
|
// Create/Flush a chain for allowing ingress traffic when the bridge is exposed
|
||||||
|
if err := ipt.ClearChain("filter", "WEAVE-EXPOSE"); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to clear/create filter/WEAVE-EXPOSE chain")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fwdRules = append(fwdRules, []string{"-o", config.WeaveBridgeName, "-j", "WEAVE-EXPOSE"})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward from weave to the rest of the world
|
// Forward from weave to the rest of the world
|
||||||
if err = ipt.AppendUnique("filter", "FORWARD", "-i", config.WeaveBridgeName, "!", "-o", config.WeaveBridgeName, "-j", "ACCEPT"); err != nil {
|
fwdRules = append(fwdRules, []string{"-i", config.WeaveBridgeName, "!", "-o", config.WeaveBridgeName, "-j", "ACCEPT"})
|
||||||
return err
|
|
||||||
}
|
|
||||||
// and allow replies back
|
// and allow replies back
|
||||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"); err != nil {
|
fwdRules = append(fwdRules, []string{"-o", config.WeaveBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"})
|
||||||
|
|
||||||
|
if err := ensureRulesAtTop("filter", "FORWARD", fwdRules, ipt); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a chain for masquerading
|
// Create a chain for masquerading
|
||||||
if err = ipt.ClearChain("nat", "WEAVE"); err != nil {
|
if err := ipt.ClearChain("nat", "WEAVE"); err != nil {
|
||||||
return errors.Wrap(err, "clearing WEAVE chain")
|
return errors.Wrap(err, "failed to clear/create nat/WEAVE chain")
|
||||||
}
|
}
|
||||||
if err = ipt.AppendUnique("nat", "POSTROUTING", "-j", "WEAVE"); err != nil {
|
if err := ipt.AppendUnique("nat", "POSTROUTING", "-j", "WEAVE"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For the cases where the weave bridge is the default gateway for
|
||||||
|
// containers (e.g. Kubernetes): create the ipset to store CIDRs allocated
|
||||||
|
// by IPAM for local containers. In the case of Kubernetes, external traffic
|
||||||
|
// sent to these CIDRs avoids SNAT'ing so that NodePort with
|
||||||
|
// `"externalTrafficPolicy":"Local"` would receive packets with correct
|
||||||
|
// src IP addr.
|
||||||
|
if config.NoMasqLocal {
|
||||||
|
ips := ipset.New(common.LogLogger(), 0)
|
||||||
|
_ = ips.Destroy(NoMasqLocalIpset)
|
||||||
|
if err := ips.Create(NoMasqLocalIpset, ipset.HashNet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ipt.Insert("nat", "WEAVE", 1,
|
||||||
|
"-m", "set", "--match-set", string(NoMasqLocalIpset), "dst",
|
||||||
|
"-m", "comment", "--comment", "Prevent SNAT to locally running containers",
|
||||||
|
"-j", "RETURN"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NoMasqLocalTracker struct {
|
||||||
|
ips ipset.Interface
|
||||||
|
owner types.UID
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNoMasqLocalTracker(ips ipset.Interface) *NoMasqLocalTracker {
|
||||||
|
return &NoMasqLocalTracker{
|
||||||
|
ips: ips,
|
||||||
|
owner: types.UID(0), // dummy ipset owner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NoMasqLocalTracker) String() string {
|
||||||
|
return "no-masq-local"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NoMasqLocalTracker) HandleUpdate(prevRanges, currRanges []address.Range, local bool) error {
|
||||||
|
if !local {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
prev, curr := tracker.RemoveCommon(
|
||||||
|
address.NewCIDRs(tracker.Merge(prevRanges)),
|
||||||
|
address.NewCIDRs(tracker.Merge(currRanges)))
|
||||||
|
|
||||||
|
for _, cidr := range curr {
|
||||||
|
if err := t.ips.AddEntry(t.owner, NoMasqLocalIpset, cidr.String(), ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, cidr := range prev {
|
||||||
|
if err := t.ips.DelEntry(t.owner, NoMasqLocalIpset, cidr.String()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,34 +584,28 @@ func linkSetUpByName(linkName string) error {
|
|||||||
return netlink.LinkSetUp(link)
|
return netlink.LinkSetUp(link)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddBridgeAddr(bridgeName string, addr *net.IPNet, removeDefaultRoute bool) error {
|
func reexpose(config *BridgeConfig, log *logrus.Logger) error {
|
||||||
link, err := netlink.LinkByName(bridgeName)
|
// Get existing IP addrs of the weave bridge.
|
||||||
|
// If the bridge hasn't been exposed, then this functions does nothing.
|
||||||
|
//
|
||||||
|
// Ideally, we should consult IPAM for IP addrs allocated to "weave:expose",
|
||||||
|
// but we don't want to introduce dependency on IPAM, as weave should be able
|
||||||
|
// to run w/o IPAM.
|
||||||
|
link, err := netlink.LinkByName(config.WeaveBridgeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "AddBridgeAddr finding bridge %q", bridgeName)
|
return errors.Wrapf(err, "cannot find bridge %q", config.WeaveBridgeName)
|
||||||
}
|
}
|
||||||
newAddresses, err := AddAddresses(link, []*net.IPNet{addr})
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "adding address %v to %q", addr, bridgeName)
|
return errors.Wrapf(err, "cannot list IPv4 addrs of bridge %q", config.WeaveBridgeName)
|
||||||
}
|
}
|
||||||
for _, ipnet := range newAddresses {
|
|
||||||
arping.GratuitousArpOverIfaceByName(ipnet.IP, bridgeName)
|
for _, addr := range addrs {
|
||||||
}
|
log.Infof("Re-exposing %s on bridge %q", addr.IPNet, config.WeaveBridgeName)
|
||||||
if removeDefaultRoute {
|
if err := Expose(config.WeaveBridgeName, addr.IPNet, config.AWSVPC, config.NPC); err != nil {
|
||||||
routeFilter := &netlink.Route{
|
return errors.Wrapf(err, "unable to re-expose %s on bridge: %q", addr.IPNet, config.WeaveBridgeName)
|
||||||
LinkIndex: link.Attrs().Index,
|
|
||||||
Dst: &net.IPNet{IP: addr.IP.Mask(addr.Mask), Mask: addr.Mask},
|
|
||||||
Protocol: 2, // RTPROT_KERNEL
|
|
||||||
}
|
|
||||||
filterMask := netlink.RT_FILTER_OIF | netlink.RT_FILTER_DST | netlink.RT_FILTER_PROTOCOL
|
|
||||||
routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, routeFilter, filterMask)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to get route list for bridge %q", bridgeName)
|
|
||||||
}
|
|
||||||
for _, r := range routes {
|
|
||||||
if err = netlink.RouteDel(&r); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to delete default route %+v", r)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
|
"github.com/j-keck/arping"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Expose makes the network accessible from a host by assigning a given IP address
|
||||||
|
// to the weave bridge.
|
||||||
|
//
|
||||||
|
// List of params:
|
||||||
|
// * "bridgeName" - a name of the weave bridge.
|
||||||
|
// * "ipAddr" - IP addr to be assigned to the bridge.
|
||||||
|
// * "removeDefaultRoute" - whether to remove a default route installed by the kernel (used only in the AWSVPC mode).
|
||||||
|
// * "npc" - whether is Weave NPC running.
|
||||||
|
func Expose(bridgeName string, ipAddr *net.IPNet, removeDefaultRoute, npc bool) error {
|
||||||
|
ipt, err := iptables.New()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "iptables.New")
|
||||||
|
}
|
||||||
|
cidr := ipAddr.String()
|
||||||
|
|
||||||
|
if err := addBridgeIPAddr(bridgeName, ipAddr, removeDefaultRoute); err != nil {
|
||||||
|
return errors.Wrap(err, "addBridgeIPAddr")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exposeNAT(ipt, cidr); err != nil {
|
||||||
|
return errors.Wrap(err, "exposeNAT")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !npc {
|
||||||
|
// Docker 1.13 has changed a default policy of FORWARD chain to DROP
|
||||||
|
// (https://github.com/moby/moby/pull/28257) which makes containers
|
||||||
|
// inaccessible from a remote host when the bridge is exposed.
|
||||||
|
//
|
||||||
|
// The change breaks e.g. the AWSVPC mode. To overcome this we install
|
||||||
|
// an explicit rule for accepting forwarded ingress traffic to an
|
||||||
|
// exposed subnet.
|
||||||
|
if err := ipt.AppendUnique("filter", "WEAVE-EXPOSE", "-d", cidr, "-j", "ACCEPT"); err != nil {
|
||||||
|
return errors.Wrap(err, "ipt.AppendUnique")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addBridgeIPAddr(bridgeName string, addr *net.IPNet, removeDefaultRoute bool) error {
|
||||||
|
link, err := netlink.LinkByName(bridgeName)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "addBridgeIPAddr finding bridge %q", bridgeName)
|
||||||
|
}
|
||||||
|
err = netlink.AddrAdd(link, &netlink.Addr{IPNet: addr})
|
||||||
|
// The IP addr might have been already set by a concurrent request, just ignore then
|
||||||
|
if err != nil && err != syscall.Errno(syscall.EEXIST) {
|
||||||
|
return errors.Wrapf(err, "adding address %v to %q", addr, bridgeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sending multiple ARP REQUESTs in the case of EEXIST above does not hurt
|
||||||
|
arping.GratuitousArpOverIfaceByName(addr.IP, bridgeName)
|
||||||
|
|
||||||
|
// Remove a default route installed by the kernel. Required by the AWSVPC mode.
|
||||||
|
if removeDefaultRoute {
|
||||||
|
routeFilter := &netlink.Route{
|
||||||
|
LinkIndex: link.Attrs().Index,
|
||||||
|
Dst: &net.IPNet{IP: addr.IP.Mask(addr.Mask), Mask: addr.Mask},
|
||||||
|
Protocol: 2, // RTPROT_KERNEL
|
||||||
|
}
|
||||||
|
filterMask := netlink.RT_FILTER_OIF | netlink.RT_FILTER_DST | netlink.RT_FILTER_PROTOCOL
|
||||||
|
routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, routeFilter, filterMask)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to get route list for bridge %q", bridgeName)
|
||||||
|
}
|
||||||
|
for _, r := range routes {
|
||||||
|
err = netlink.RouteDel(&r)
|
||||||
|
// Again, there might be a concurrent request for removing routes
|
||||||
|
if err != nil && err != syscall.Errno(syscall.ESRCH) {
|
||||||
|
return errors.Wrapf(err, "failed to delete default route %+v", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func exposeNAT(ipt *iptables.IPTables, cidr string) error {
|
||||||
|
if err := addNatRule(ipt, "-s", cidr, "-d", "224.0.0.0/4", "-j", "RETURN"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := addNatRule(ipt, "-d", cidr, "!", "-s", cidr, "-j", "MASQUERADE"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return addNatRule(ipt, "-s", cidr, "!", "-d", cidr, "-j", "MASQUERADE")
|
||||||
|
}
|
||||||
|
|
||||||
|
func addNatRule(ipt *iptables.IPTables, rulespec ...string) error {
|
||||||
|
// Loop until we get an exit code other than "temporarily unavailable"
|
||||||
|
for {
|
||||||
|
if err := ipt.AppendUnique("nat", "WEAVE", rulespec...); err != nil {
|
||||||
|
if ierr, ok := err.(*iptables.Error); ok {
|
||||||
|
if status, ok := ierr.ExitError.Sys().(syscall.WaitStatus); ok {
|
||||||
|
// (magic exit code 4 found in iptables source code; undocumented)
|
||||||
|
if status.ExitStatus() == 4 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-5
@@ -12,9 +12,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
|
|
||||||
@@ -421,10 +421,7 @@ func (ipsec *IPSec) installDropNonEncrypted(srcIP, dstIP net.IP, udpPort int, in
|
|||||||
|
|
||||||
func (ipsec *IPSec) removeDropNonEncrypted(srcIP, dstIP net.IP, udpPort int, inSPI SPI) error {
|
func (ipsec *IPSec) removeDropNonEncrypted(srcIP, dstIP net.IP, udpPort int, inSPI SPI) error {
|
||||||
rules := rulesDropNonEncrypted(srcIP, dstIP, udpPort, inSPI)
|
rules := rulesDropNonEncrypted(srcIP, dstIP, udpPort, inSPI)
|
||||||
if err := ipsec.resetRules(rules, true); err != nil {
|
return ipsec.resetRules(rules, true)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ipsec *IPSec) removeDropNonEncryptedInbound(srcIP, dstIP net.IP, inSPI SPI) error {
|
func (ipsec *IPSec) removeDropNonEncryptedInbound(srcIP, dstIP net.IP, inSPI SPI) error {
|
||||||
|
|||||||
@@ -0,0 +1,214 @@
|
|||||||
|
package ipset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Name string
|
||||||
|
|
||||||
|
type Type string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ListSet = Type("list:set")
|
||||||
|
HashIP = Type("hash:ip")
|
||||||
|
HashNet = Type("hash:net")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Interface interface {
|
||||||
|
Create(ipsetName Name, ipsetType Type) error
|
||||||
|
AddEntry(user types.UID, ipsetName Name, entry string, comment string) error
|
||||||
|
DelEntry(user types.UID, ipsetName Name, entry string) error
|
||||||
|
Exist(user types.UID, ipsetName Name, entry string) bool
|
||||||
|
Flush(ipsetName Name) error
|
||||||
|
Destroy(ipsetName Name) error
|
||||||
|
|
||||||
|
List(prefix string) ([]Name, error)
|
||||||
|
|
||||||
|
FlushAll() error
|
||||||
|
DestroyAll() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type entryKey struct {
|
||||||
|
ipsetName Name
|
||||||
|
entry string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ipset struct {
|
||||||
|
*log.Logger
|
||||||
|
enableComments bool
|
||||||
|
maxListSize int
|
||||||
|
// List of users per ipset entry. User is either a namespace or a pod.
|
||||||
|
// There might be multiple users for the same ipset & entry pair because
|
||||||
|
// events from k8s API server might be out of order causing duplicate IPs:
|
||||||
|
// https://github.com/weaveworks/weave/issues/2792.
|
||||||
|
users map[entryKey]map[types.UID]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(logger *log.Logger, maxListSize int) Interface {
|
||||||
|
ips := &ipset{
|
||||||
|
Logger: logger,
|
||||||
|
enableComments: true,
|
||||||
|
maxListSize: maxListSize,
|
||||||
|
users: make(map[entryKey]map[types.UID]struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for comment support
|
||||||
|
|
||||||
|
// To prevent from a race when more than one process check for the support
|
||||||
|
// we append a random nonce to the test ipset name. The final name is
|
||||||
|
// shorter than 31 chars (max ipset name).
|
||||||
|
nonce := make([]byte, 4)
|
||||||
|
rand.Read(nonce)
|
||||||
|
testIpsetName := Name("weave-test-comment" + hex.EncodeToString(nonce))
|
||||||
|
|
||||||
|
// Clear it out if it already exists
|
||||||
|
_ = ips.Destroy(testIpsetName)
|
||||||
|
// Test for comment support
|
||||||
|
if err := ips.Create(testIpsetName, HashIP); err != nil {
|
||||||
|
ips.Logger.Printf("failed to create %s; disabling comment support", testIpsetName)
|
||||||
|
ips.enableComments = false
|
||||||
|
}
|
||||||
|
// If it was created, destroy it
|
||||||
|
_ = ips.Destroy(testIpsetName)
|
||||||
|
|
||||||
|
return ips
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) Create(ipsetName Name, ipsetType Type) error {
|
||||||
|
args := []string{"create", string(ipsetName), string(ipsetType)}
|
||||||
|
if ipsetType == ListSet && i.maxListSize > 0 {
|
||||||
|
args = append(args, "size", fmt.Sprintf("%d", i.maxListSize))
|
||||||
|
}
|
||||||
|
if i.enableComments {
|
||||||
|
args = append(args, "comment")
|
||||||
|
}
|
||||||
|
return doExec(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) AddEntry(user types.UID, ipsetName Name, entry string, comment string) error {
|
||||||
|
i.Logger.Printf("adding entry %s to %s of %s", entry, ipsetName, user)
|
||||||
|
|
||||||
|
if !i.addUser(user, ipsetName, entry) { // already in the set
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
i.Logger.Printf("added entry %s to %s of %s", entry, ipsetName, user)
|
||||||
|
|
||||||
|
args := []string{"add", string(ipsetName), entry}
|
||||||
|
if i.enableComments {
|
||||||
|
args = append(args, "comment", comment)
|
||||||
|
}
|
||||||
|
return doExec(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) DelEntry(user types.UID, ipsetName Name, entry string) error {
|
||||||
|
i.Logger.Printf("deleting entry %s from %s of %s", entry, ipsetName, user)
|
||||||
|
|
||||||
|
if !i.delUser(user, ipsetName, entry) { // still needed
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
i.Logger.Printf("deleted entry %s from %s of %s", entry, ipsetName, user)
|
||||||
|
|
||||||
|
return doExec("del", string(ipsetName), entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) Exist(user types.UID, ipsetName Name, entry string) bool {
|
||||||
|
return i.existUser(user, ipsetName, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) Flush(ipsetName Name) error {
|
||||||
|
i.removeSetFromUsers(ipsetName)
|
||||||
|
return doExec("flush", string(ipsetName))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) FlushAll() error {
|
||||||
|
i.users = make(map[entryKey]map[types.UID]struct{})
|
||||||
|
return doExec("flush")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) Destroy(ipsetName Name) error {
|
||||||
|
i.removeSetFromUsers(ipsetName)
|
||||||
|
return doExec("destroy", string(ipsetName))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) DestroyAll() error {
|
||||||
|
i.users = make(map[entryKey]map[types.UID]struct{})
|
||||||
|
return doExec("destroy")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch a list of all existing sets with a given prefix
|
||||||
|
func (i *ipset) List(prefix string) ([]Name, error) {
|
||||||
|
output, err := exec.Command("ipset", "list", "-name", "-output", "plain").Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var selected []Name
|
||||||
|
sets := strings.Split(string(output), "\n")
|
||||||
|
for _, v := range sets {
|
||||||
|
if strings.HasPrefix(v, prefix) {
|
||||||
|
selected = append(selected, Name(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return selected, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if entry does not exist in ipset (entry has to be inserted into ipset).
|
||||||
|
func (i *ipset) addUser(user types.UID, ipsetName Name, entry string) bool {
|
||||||
|
k := entryKey{ipsetName, entry}
|
||||||
|
add := false
|
||||||
|
|
||||||
|
if i.users[k] == nil {
|
||||||
|
i.users[k] = make(map[types.UID]struct{})
|
||||||
|
}
|
||||||
|
if len(i.users[k]) == 0 {
|
||||||
|
add = true
|
||||||
|
}
|
||||||
|
i.users[k][user] = struct{}{}
|
||||||
|
|
||||||
|
return add
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if user is the last owner of entry (entry has to be removed from ipset).
|
||||||
|
func (i *ipset) delUser(user types.UID, ipsetName Name, entry string) bool {
|
||||||
|
k := entryKey{ipsetName, entry}
|
||||||
|
|
||||||
|
oneLeft := len(i.users[k]) == 1
|
||||||
|
delete(i.users[k], user)
|
||||||
|
|
||||||
|
if len(i.users[k]) == 0 {
|
||||||
|
delete(i.users, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return oneLeft && (len(i.users[k]) == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) existUser(user types.UID, ipsetName Name, entry string) bool {
|
||||||
|
_, ok := i.users[entryKey{ipsetName, entry}][user]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ipset) removeSetFromUsers(ipsetName Name) {
|
||||||
|
for k := range i.users {
|
||||||
|
if k.ipsetName == ipsetName {
|
||||||
|
delete(i.users, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doExec(args ...string) error {
|
||||||
|
if output, err := exec.Command("ipset", args...).CombinedOutput(); err != nil {
|
||||||
|
return errors.Wrapf(err, "ipset %v failed: %s", args, output)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddChainWithRules creates a chain and appends given rules to it.
|
||||||
|
//
|
||||||
|
// If the chain exists, but its rules are not the same as the given ones, the
|
||||||
|
// function will flush the chain and then will append the rules.
|
||||||
|
func AddChainWithRules(ipt *iptables.IPTables, table, chain string, rulespecs [][]string) error {
|
||||||
|
if err := ensureChains(ipt, table, chain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
currRuleSpecs, err := ipt.List(table, chain)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "iptables -S. table: %q, chain: %q", table, chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First returned rule is "-N $(chain)", so ignore it
|
||||||
|
currRules := strings.Join(currRuleSpecs[1:], "\n")
|
||||||
|
rules := make([]string, 0)
|
||||||
|
for _, r := range rulespecs {
|
||||||
|
rules = append(rules, strings.Join(r, " "))
|
||||||
|
}
|
||||||
|
reqRules := strings.Join(rules, "\n")
|
||||||
|
|
||||||
|
if currRules == reqRules {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipt.ClearChain(table, chain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range rulespecs {
|
||||||
|
if err := ipt.Append(table, chain, r...); err != nil {
|
||||||
|
return errors.Wrapf(err, "iptables -A. table: %q, chain: %q, rule: %s", table, chain, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureChains creates given chains if they do not exist.
|
||||||
|
func ensureChains(ipt *iptables.IPTables, table string, chains ...string) error {
|
||||||
|
existingChains, err := ipt.ListChains(table)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "ipt.ListChains(%s)", table)
|
||||||
|
}
|
||||||
|
chainMap := make(map[string]struct{})
|
||||||
|
for _, c := range existingChains {
|
||||||
|
chainMap[c] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range chains {
|
||||||
|
if _, found := chainMap[c]; !found {
|
||||||
|
if err := ipt.NewChain(table, c); err != nil {
|
||||||
|
return errors.Wrapf(err, "ipt.NewChain(%s, %s)", table, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureRulesAtTop ensures the presence of given iptables rules.
|
||||||
|
//
|
||||||
|
// If any rule from the list is missing, the function deletes all given
|
||||||
|
// rules and re-inserts them at the top of the chain to ensure the order of the rules.
|
||||||
|
func ensureRulesAtTop(table, chain string, rulespecs [][]string, ipt *iptables.IPTables) error {
|
||||||
|
allFound := true
|
||||||
|
|
||||||
|
for _, rs := range rulespecs {
|
||||||
|
found, err := ipt.Exists(table, chain, rs...)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "ipt.Exists(%s, %s, %s)", table, chain, rs)
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
allFound = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All rules exist, do nothing.
|
||||||
|
if allFound {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for pos, rs := range rulespecs {
|
||||||
|
// If any is missing, then delete all, as we need to preserve the order of
|
||||||
|
// given rules. Ignore errors, as rule might not exist.
|
||||||
|
if !allFound {
|
||||||
|
ipt.Delete(table, chain, rs...)
|
||||||
|
}
|
||||||
|
if err := ipt.Insert(table, chain, pos+1, rs...); err != nil {
|
||||||
|
return errors.Wrapf(err, "ipt.Append(%s, %s, %s)", table, chain, rs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+25
-13
@@ -1,11 +1,9 @@
|
|||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"github.com/vishvananda/netns"
|
"github.com/vishvananda/netns"
|
||||||
@@ -17,7 +15,7 @@ type Dev struct {
|
|||||||
CIDRs []*net.IPNet `json:"CIDRs,omitempty"`
|
CIDRs []*net.IPNet `json:"CIDRs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LinkToNetDev(link netlink.Link) (Dev, error) {
|
func linkToNetDev(link netlink.Link) (Dev, error) {
|
||||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Dev{}, err
|
return Dev{}, err
|
||||||
@@ -90,16 +88,30 @@ func GetNetDevsByVethPeerIds(processID int, peerIDs []int) ([]Dev, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convert list of peerIDs into a map for faster lookup
|
||||||
|
indexes := make(map[int]struct{})
|
||||||
|
for _, id := range peerIDs {
|
||||||
|
indexes[id] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
var netdevs []Dev
|
var netdevs []Dev
|
||||||
peersStr := make([]string, len(peerIDs))
|
err = WithNetNS(netnsContainer, func() error {
|
||||||
for i, id := range peerIDs {
|
links, err := netlink.LinkList()
|
||||||
peersStr[i] = strconv.Itoa(id)
|
if err != nil {
|
||||||
}
|
return err
|
||||||
nds, err := WithNetNSByPid(processID, "list-netdevs", peersStr...)
|
}
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("list-netdevs failed: %s", err)
|
for _, link := range links {
|
||||||
}
|
if _, found := indexes[link.Attrs().Index]; found {
|
||||||
err = json.Unmarshal(nds, &netdevs)
|
netdev, err := linkToNetDev(link)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
netdevs = append(netdevs, netdev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
return netdevs, err
|
return netdevs, err
|
||||||
}
|
}
|
||||||
@@ -112,5 +124,5 @@ func GetBridgeNetDev(bridgeName string) (Dev, error) {
|
|||||||
return Dev{}, err
|
return Dev{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return LinkToNetDev(link)
|
return linkToNetDev(link)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
func LinkAddIfNotExist(link netlink.Link) error {
|
||||||
|
err := netlink.LinkAdd(link)
|
||||||
|
if err != nil && err == syscall.EEXIST {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.Wrapf(err, "creating link %q", link)
|
||||||
|
}
|
||||||
+10
-36
@@ -1,10 +1,10 @@
|
|||||||
|
// +build go1.10
|
||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
@@ -14,19 +14,7 @@ import (
|
|||||||
|
|
||||||
var ErrLinkNotFound = errors.New("Link not found")
|
var ErrLinkNotFound = errors.New("Link not found")
|
||||||
|
|
||||||
// NB: The following function is unsafe, because:
|
func WithNetNS(ns netns.NsHandle, work func() error) error {
|
||||||
// - It changes a network namespace (netns) of an OS thread which runs
|
|
||||||
// the function. During execution, the Go runtime might clone a new OS thread
|
|
||||||
// for scheduling other go-routines, thus they might end up running in
|
|
||||||
// a "wrong" netns.
|
|
||||||
// - runtime.LockOSThread does not guarantee that a spawned go-routine on
|
|
||||||
// the locked thread will be run by it. Thus, the work function is
|
|
||||||
// not allowed to spawn any go-routine which is dependent on the given netns.
|
|
||||||
|
|
||||||
// Please see https://github.com/weaveworks/weave/issues/2388#issuecomment-228365069
|
|
||||||
// for more details and make sure that you understand the implications before
|
|
||||||
// using the function!
|
|
||||||
func WithNetNSUnsafe(ns netns.NsHandle, work func() error) error {
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
@@ -45,8 +33,8 @@ func WithNetNSUnsafe(ns netns.NsHandle, work func() error) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithNetNSLinkUnsafe(ns netns.NsHandle, ifName string, work func(link netlink.Link) error) error {
|
func WithNetNSLink(ns netns.NsHandle, ifName string, work func(link netlink.Link) error) error {
|
||||||
return WithNetNSUnsafe(ns, func() error {
|
return WithNetNS(ns, func() error {
|
||||||
link, err := netlink.LinkByName(ifName)
|
link, err := netlink.LinkByName(ifName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == errors.New("Link not found").Error() {
|
if err.Error() == errors.New("Link not found").Error() {
|
||||||
@@ -58,26 +46,12 @@ func WithNetNSLinkUnsafe(ns netns.NsHandle, ifName string, work func(link netlin
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var WeaveUtilCmd = "weaveutil"
|
func WithNetNSByPath(path string, work func() error) error {
|
||||||
|
ns, err := netns.GetFromPath(path)
|
||||||
// A safe version of WithNetNS* which creates a process executing
|
if err != nil {
|
||||||
// "nsenter --net=<ns-path> weaveutil <cmd> [args]".
|
return err
|
||||||
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 WithNetNS(ns, work)
|
||||||
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 {
|
func NSPathByPid(pid int) string {
|
||||||
|
|||||||
+43
-60
@@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
@@ -14,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// create and attach a veth to the Weave bridge
|
// create and attach a veth to the Weave bridge
|
||||||
func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, keepTXOn bool, init func(peer netlink.Link) error) (*netlink.Veth, error) {
|
func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, keepTXOn bool, errIfLinkExist bool, init func(peer netlink.Link) error) (*netlink.Veth, error) {
|
||||||
bridge, err := netlink.LinkByName(bridgeName)
|
bridge, err := netlink.LinkByName(bridgeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(`bridge "%s" not present; did you launch weave?`, bridgeName)
|
return nil, fmt.Errorf(`bridge "%s" not present; did you launch weave?`, bridgeName)
|
||||||
@@ -29,7 +28,12 @@ func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, keepTXOn bo
|
|||||||
MTU: mtu},
|
MTU: mtu},
|
||||||
PeerName: peerName,
|
PeerName: peerName,
|
||||||
}
|
}
|
||||||
if err := netlink.LinkAdd(veth); err != nil {
|
|
||||||
|
linkAdd := LinkAddIfNotExist
|
||||||
|
if errIfLinkExist {
|
||||||
|
linkAdd = netlink.LinkAdd
|
||||||
|
}
|
||||||
|
if err := linkAdd(veth); err != nil {
|
||||||
return nil, fmt.Errorf(`could not create veth pair %s-%s: %s`, name, peerName, err)
|
return nil, fmt.Errorf(`could not create veth pair %s-%s: %s`, name, peerName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +104,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func interfaceExistsInNamespace(netNSPath string, ifName string) bool {
|
func interfaceExistsInNamespace(netNSPath string, ifName string) bool {
|
||||||
_, err := WithNetNS(netNSPath, "check-iface", ifName)
|
err := WithNetNSByPath(netNSPath, func() error {
|
||||||
|
_, err := netlink.LinkByName(ifName)
|
||||||
|
return err
|
||||||
|
})
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,11 +124,13 @@ func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMult
|
|||||||
id = id[:maxIDLen] // trim passed ID if too long
|
id = id[:maxIDLen] // trim passed ID if too long
|
||||||
}
|
}
|
||||||
name, peerName := vethPrefix+"pl"+id, vethPrefix+"pg"+id
|
name, peerName := vethPrefix+"pl"+id, vethPrefix+"pg"+id
|
||||||
veth, err := CreateAndAttachVeth(name, peerName, bridgeName, mtu, keepTXOn, func(veth netlink.Link) error {
|
veth, err := CreateAndAttachVeth(name, peerName, bridgeName, mtu, keepTXOn, true, func(veth netlink.Link) error {
|
||||||
if err := netlink.LinkSetNsFd(veth, int(ns)); err != nil {
|
if err := netlink.LinkSetNsFd(veth, int(ns)); err != nil {
|
||||||
return fmt.Errorf("failed to move veth to container netns: %s", err)
|
return fmt.Errorf("failed to move veth to container netns: %s", err)
|
||||||
}
|
}
|
||||||
if _, err := WithNetNS(netNSPath, "setup-iface", peerName, ifName); err != nil {
|
if err := WithNetNS(ns, func() error {
|
||||||
|
return setupIface(peerName, ifName)
|
||||||
|
}); err != nil {
|
||||||
return fmt.Errorf("error setting up interface: %s", err)
|
return fmt.Errorf("error setting up interface: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -135,19 +144,16 @@ func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMult
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string{ifName, fmt.Sprintf("%t", withMulticastRoute)}
|
if err := WithNetNSLink(ns, ifName, func(veth netlink.Link) error {
|
||||||
for _, cidr := range cidrs {
|
return setupIfaceAddrs(veth, withMulticastRoute, cidrs)
|
||||||
args = append(args, cidr.String())
|
}); err != nil {
|
||||||
}
|
|
||||||
if _, err := WithNetNS(netNSPath, "setup-iface-addrs", args...); err != nil {
|
|
||||||
return fmt.Errorf("error setting up interface addresses: %s", err)
|
return fmt.Errorf("error setting up interface addresses: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupIfaceAddrs is the implementation of the 'setup-iface-addrs' call above,
|
// setupIfaceAddrs expects to be called in the container's netns
|
||||||
// running in another process in the container's netns
|
func setupIfaceAddrs(veth netlink.Link, withMulticastRoute bool, cidrs []*net.IPNet) error {
|
||||||
func SetupIfaceAddrs(veth netlink.Link, withMulticastRoute bool, cidrs []*net.IPNet) error {
|
|
||||||
newAddresses, err := AddAddresses(veth, cidrs)
|
newAddresses, err := AddAddresses(veth, cidrs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -199,9 +205,8 @@ func SetupIfaceAddrs(veth netlink.Link, withMulticastRoute bool, cidrs []*net.IP
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupIface is the implementation of the 'setup-iface' call above,
|
// setupIface expects to be called in the container's netns
|
||||||
// running in another process in the container's netns
|
func setupIface(ifaceName, newIfName string) error {
|
||||||
func SetupIface(ifaceName, newIfName string) error {
|
|
||||||
ipt, err := iptables.New()
|
ipt, err := iptables.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -215,18 +220,32 @@ func SetupIface(ifaceName, newIfName string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// This is only called by AttachContainer which is only called in host pid namespace
|
// This is only called by AttachContainer which is only called in host pid namespace
|
||||||
if err := ConfigureARPCache("/proc", newIfName); err != nil {
|
if err := configureARPCache("/proc", newIfName); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := ipt.Append("filter", "INPUT", "-i", newIfName, "-d", "224.0.0.0/4", "-j", "DROP"); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return ipt.Append("filter", "INPUT", "-i", newIfName, "-d", "224.0.0.0/4", "-j", "DROP")
|
||||||
|
}
|
||||||
|
|
||||||
|
// configureARP is a helper for the Docker plugin which doesn't set the addresses itself
|
||||||
|
func ConfigureARP(prefix, rootPath string) error {
|
||||||
|
links, err := netlink.LinkList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, link := range links {
|
||||||
|
ifName := link.Attrs().Name
|
||||||
|
if strings.HasPrefix(ifName, prefix) {
|
||||||
|
configureARPCache(rootPath+"/proc", ifName)
|
||||||
|
if addrs, err := netlink.AddrList(link, netlink.FAMILY_V4); err == nil {
|
||||||
|
for _, addr := range addrs {
|
||||||
|
arping.GratuitousArpOverIfaceByName(addr.IPNet.IP, ifName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
func DetachContainer(netNSPath, id, ifName string, cidrs []*net.IPNet) error {
|
||||||
ns, err := netns.GetFromPath(netNSPath)
|
ns, err := netns.GetFromPath(netNSPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -239,7 +258,7 @@ func DetachContainer(netNSPath, id, ifName string, cidrs []*net.IPNet) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return WithNetNSLinkUnsafe(ns, ifName, func(veth netlink.Link) error {
|
return WithNetNSLink(ns, ifName, func(veth netlink.Link) error {
|
||||||
existingAddrs, err := netlink.AddrList(veth, netlink.FAMILY_V4)
|
existingAddrs, err := netlink.AddrList(veth, netlink.FAMILY_V4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get IP address for %q: %v", veth.Attrs().Name, err)
|
return fmt.Errorf("failed to get IP address for %q: %v", veth.Attrs().Name, err)
|
||||||
@@ -301,39 +320,3 @@ func subnets(addrs []netlink.Addr) map[string]struct{} {
|
|||||||
}
|
}
|
||||||
return subnets
|
return subnets
|
||||||
}
|
}
|
||||||
|
|
||||||
func addNatRule(ipt *iptables.IPTables, rulespec ...string) error {
|
|
||||||
// Loop until we get an exit code other than "temporarily unavailable"
|
|
||||||
for {
|
|
||||||
if err := ipt.AppendUnique("nat", "WEAVE", rulespec...); err != nil {
|
|
||||||
if ierr, ok := err.(*iptables.Error); ok {
|
|
||||||
if status, ok := ierr.ExitError.Sys().(syscall.WaitStatus); ok {
|
|
||||||
// (magic exit code 4 found in iptables source code; undocumented)
|
|
||||||
if status.ExitStatus() == 4 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExposeNAT(ipnet net.IPNet) error {
|
|
||||||
ipt, err := iptables.New()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cidr := ipnet.String()
|
|
||||||
if err := addNatRule(ipt, "-s", cidr, "-d", "224.0.0.0/4", "-j", "RETURN"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := addNatRule(ipt, "-d", cidr, "!", "-s", cidr, "-j", "MASQUERADE"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := addNatRule(ipt, "-s", cidr, "!", "-d", cidr, "-j", "MASQUERADE"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
+161
-78
@@ -3,112 +3,195 @@ package npc
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"k8s.io/client-go/pkg/api"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
"k8s.io/client-go/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
|
||||||
"github.com/weaveworks/weave/npc/ipset"
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
|
func (ns *ns) analysePolicy(policy *networkingv1.NetworkPolicy) (
|
||||||
rules map[string]*ruleSpec,
|
rules map[string]*ruleSpec,
|
||||||
nsSelectors, podSelectors map[string]*selectorSpec,
|
nsSelectors, podSelectors map[string]*selectorSpec,
|
||||||
|
ipBlocks map[string]*ipBlockSpec,
|
||||||
err error) {
|
err error) {
|
||||||
|
|
||||||
nsSelectors = make(map[string]*selectorSpec)
|
nsSelectors = make(map[string]*selectorSpec)
|
||||||
podSelectors = make(map[string]*selectorSpec)
|
podSelectors = make(map[string]*selectorSpec)
|
||||||
|
ipBlocks = make(map[string]*ipBlockSpec)
|
||||||
rules = make(map[string]*ruleSpec)
|
rules = make(map[string]*ruleSpec)
|
||||||
|
policyTypes := make([]policyType, 0)
|
||||||
|
|
||||||
dstSelector, err := newSelectorSpec(&policy.Spec.PodSelector, ns.name, ipset.HashIP)
|
for _, pt := range policy.Spec.PolicyTypes {
|
||||||
|
if pt == networkingv1.PolicyTypeIngress {
|
||||||
|
policyTypes = append(policyTypes, policyTypeIngress)
|
||||||
|
}
|
||||||
|
if pt == networkingv1.PolicyTypeEgress {
|
||||||
|
policyTypes = append(policyTypes, policyTypeEgress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If empty, matches all pods in a namespace
|
||||||
|
targetSelector, err := newSelectorSpec(&policy.Spec.PodSelector, policyTypes, ns.name, ipset.HashIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
podSelectors[dstSelector.key] = dstSelector
|
|
||||||
|
|
||||||
for _, ingressRule := range policy.Spec.Ingress {
|
// To prevent targetSelector being overwritten by a subsequent selector with
|
||||||
if ingressRule.Ports != nil && len(ingressRule.Ports) == 0 {
|
// the same key, addIfNotExist MUST be used when adding to podSelectors.
|
||||||
// Ports is empty, this rule matches no ports (no traffic matches).
|
// Otherwise, policyTypes of the selector might be lost resulting in
|
||||||
continue
|
// an invalid content in any "default-allow" ipset.
|
||||||
}
|
addIfNotExist(targetSelector, podSelectors)
|
||||||
|
|
||||||
if ingressRule.From != nil && len(ingressRule.From) == 0 {
|
// If ingress is empty then this NetworkPolicy does not allow any ingress traffic
|
||||||
// From is empty, this rule matches no sources (no traffic matches).
|
if policy.Spec.Ingress != nil && len(policy.Spec.Ingress) != 0 {
|
||||||
continue
|
for _, ingressRule := range policy.Spec.Ingress {
|
||||||
}
|
// If Ports is empty or missing, this rule matches all ports
|
||||||
|
allPorts := ingressRule.Ports == nil || len(ingressRule.Ports) == 0
|
||||||
|
// If From is empty or missing, this rule matches all sources
|
||||||
|
allSources := ingressRule.From == nil || len(ingressRule.From) == 0
|
||||||
|
|
||||||
if ingressRule.From == nil {
|
if allSources {
|
||||||
// From is not provided, this rule matches all sources (traffic not restricted by source).
|
if allPorts {
|
||||||
if ingressRule.Ports == nil {
|
rule := newRuleSpec(policyTypeIngress, nil, nil, targetSelector, 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
|
rules[rule.key] = rule
|
||||||
} else {
|
} 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) {
|
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
||||||
rule := newRuleSpec(&proto, srcSelector, dstSelector, &port)
|
rule := newRuleSpec(policyTypeIngress, &proto, nil, targetSelector, &port)
|
||||||
rules[rule.key] = rule
|
rules[rule.key] = rule
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for _, peer := range ingressRule.From {
|
||||||
|
var srcSelector *selectorSpec
|
||||||
|
|
||||||
|
// NetworkPolicyPeer describes a peer to allow traffic from.
|
||||||
|
// Exactly one of its fields must be specified.
|
||||||
|
if peer.PodSelector != nil {
|
||||||
|
srcSelector, err = newSelectorSpec(peer.PodSelector, nil, ns.name, ipset.HashIP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
addIfNotExist(srcSelector, podSelectors)
|
||||||
|
|
||||||
|
} else if peer.NamespaceSelector != nil {
|
||||||
|
srcSelector, err = newSelectorSpec(peer.NamespaceSelector, nil, "", ipset.ListSet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
nsSelectors[srcSelector.key] = srcSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
if allPorts {
|
||||||
|
rule := newRuleSpec(policyTypeIngress, nil, srcSelector, targetSelector, nil)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
} else {
|
||||||
|
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
||||||
|
rule := newRuleSpec(policyTypeIngress, &proto, srcSelector, targetSelector, &port)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rules, nsSelectors, podSelectors, nil
|
// If egress is empty then this NetworkPolicy does not allow any egress traffic
|
||||||
|
if policy.Spec.Egress != nil && len(policy.Spec.Egress) != 0 {
|
||||||
|
for _, egressRule := range policy.Spec.Egress {
|
||||||
|
// If Ports is empty or missing, this rule matches all ports
|
||||||
|
allPorts := egressRule.Ports == nil || len(egressRule.Ports) == 0
|
||||||
|
// If To is empty or missing, this rule matches all destinations
|
||||||
|
allDestinations := egressRule.To == nil || len(egressRule.To) == 0
|
||||||
|
|
||||||
|
if allDestinations {
|
||||||
|
if allPorts {
|
||||||
|
rule := newRuleSpec(policyTypeEgress, nil, targetSelector, nil, nil)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
} else {
|
||||||
|
withNormalisedProtoAndPort(egressRule.Ports, func(proto, port string) {
|
||||||
|
rule := newRuleSpec(policyTypeEgress, &proto, targetSelector, nil, &port)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, peer := range egressRule.To {
|
||||||
|
var dstSelector *selectorSpec
|
||||||
|
var dstRuleHost ruleHost
|
||||||
|
|
||||||
|
// NetworkPolicyPeer describes a peer to allow traffic to.
|
||||||
|
// Exactly one of its fields must be specified.
|
||||||
|
if peer.PodSelector != nil {
|
||||||
|
dstSelector, err = newSelectorSpec(peer.PodSelector, nil, ns.name, ipset.HashIP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
addIfNotExist(dstSelector, podSelectors)
|
||||||
|
dstRuleHost = dstSelector
|
||||||
|
|
||||||
|
} else if peer.NamespaceSelector != nil {
|
||||||
|
dstSelector, err = newSelectorSpec(peer.NamespaceSelector, nil, "", ipset.ListSet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
nsSelectors[dstSelector.key] = dstSelector
|
||||||
|
dstRuleHost = dstSelector
|
||||||
|
} else if peer.IPBlock != nil {
|
||||||
|
ipBlock := newIPBlockSpec(peer.IPBlock, ns.name)
|
||||||
|
ipBlocks[ipBlock.key] = ipBlock
|
||||||
|
dstRuleHost = ipBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if allPorts {
|
||||||
|
rule := newRuleSpec(policyTypeEgress, nil, targetSelector, dstRuleHost, nil)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
} else {
|
||||||
|
withNormalisedProtoAndPort(egressRule.Ports, func(proto, port string) {
|
||||||
|
rule := newRuleSpec(policyTypeEgress, &proto, targetSelector, dstRuleHost, &port)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules, nsSelectors, podSelectors, ipBlocks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func withNormalisedProtoAndPort(npps []extnapi.NetworkPolicyPort, f func(proto, port string)) {
|
func addIfNotExist(s *selectorSpec, ss map[string]*selectorSpec) {
|
||||||
|
if _, ok := ss[s.key]; !ok {
|
||||||
|
ss[s.key] = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func withNormalisedProtoAndPort(npps []networkingv1.NetworkPolicyPort, f func(proto, port string)) {
|
||||||
for _, npp := range npps {
|
for _, npp := range npps {
|
||||||
// If no proto is specified, default to TCP
|
f(proto(npp.Protocol), port(npp.Port))
|
||||||
proto := string(api.ProtocolTCP)
|
|
||||||
if npp.Protocol != nil {
|
|
||||||
proto = string(*npp.Protocol)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no port is specified, match any port. Let iptables executable handle
|
|
||||||
// service name resolution
|
|
||||||
port := "0:65535"
|
|
||||||
if npp.Port != nil {
|
|
||||||
switch npp.Port.Type {
|
|
||||||
case intstr.Int:
|
|
||||||
port = fmt.Sprintf("%d", npp.Port.IntVal)
|
|
||||||
case intstr.String:
|
|
||||||
port = npp.Port.StrVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
f(proto, port)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func proto(p *apiv1.Protocol) string {
|
||||||
|
// If no proto is specified, default to TCP
|
||||||
|
proto := string(apiv1.ProtocolTCP)
|
||||||
|
if p != nil {
|
||||||
|
proto = string(*p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return proto
|
||||||
|
}
|
||||||
|
|
||||||
|
func port(p *intstr.IntOrString) string {
|
||||||
|
// If no port is specified, match any port. Let iptables executable handle
|
||||||
|
// service name resolution
|
||||||
|
port := "0:65535"
|
||||||
|
if p != nil {
|
||||||
|
switch p.Type {
|
||||||
|
case intstr.Int:
|
||||||
|
port = fmt.Sprintf("%d", p.IntVal)
|
||||||
|
case intstr.String:
|
||||||
|
port = p.StrVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return port
|
||||||
|
}
|
||||||
@@ -7,6 +7,12 @@ const (
|
|||||||
DefaultChain = "WEAVE-NPC-DEFAULT"
|
DefaultChain = "WEAVE-NPC-DEFAULT"
|
||||||
IngressChain = "WEAVE-NPC-INGRESS"
|
IngressChain = "WEAVE-NPC-INGRESS"
|
||||||
|
|
||||||
|
EgressChain = "WEAVE-NPC-EGRESS"
|
||||||
|
EgressDefaultChain = "WEAVE-NPC-EGRESS-DEFAULT"
|
||||||
|
EgressCustomChain = "WEAVE-NPC-EGRESS-CUSTOM"
|
||||||
|
EgressMarkChain = "WEAVE-NPC-EGRESS-ACCEPT"
|
||||||
|
EgressMark = "0x40000/0x40000"
|
||||||
|
|
||||||
IpsetNamePrefix = "weave-"
|
IpsetNamePrefix = "weave-"
|
||||||
|
|
||||||
LocalIpset = IpsetNamePrefix + "local-pods"
|
LocalIpset = IpsetNamePrefix + "local-pods"
|
||||||
|
|||||||
+42
-14
@@ -4,11 +4,12 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
coreapi "k8s.io/client-go/pkg/api/v1"
|
coreapi "k8s.io/api/core/v1"
|
||||||
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
extnapi "k8s.io/api/extensions/v1beta1"
|
||||||
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
"github.com/weaveworks/weave/common"
|
||||||
"github.com/weaveworks/weave/npc/ipset"
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
"github.com/weaveworks/weave/npc/iptables"
|
"github.com/weaveworks/weave/npc/iptables"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,9 +22,9 @@ type NetworkPolicyController interface {
|
|||||||
UpdatePod(oldObj, newObj *coreapi.Pod) error
|
UpdatePod(oldObj, newObj *coreapi.Pod) error
|
||||||
DeletePod(obj *coreapi.Pod) error
|
DeletePod(obj *coreapi.Pod) error
|
||||||
|
|
||||||
AddNetworkPolicy(obj *extnapi.NetworkPolicy) error
|
AddNetworkPolicy(obj interface{}) error
|
||||||
UpdateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error
|
UpdateNetworkPolicy(oldObj, newObj interface{}) error
|
||||||
DeleteNetworkPolicy(obj *extnapi.NetworkPolicy) error
|
DeleteNetworkPolicy(obj interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type controller struct {
|
type controller struct {
|
||||||
@@ -45,7 +46,8 @@ func New(nodeName string, ipt iptables.Interface, ips ipset.Interface) NetworkPo
|
|||||||
ips: ips,
|
ips: ips,
|
||||||
nss: make(map[string]*ns)}
|
nss: make(map[string]*ns)}
|
||||||
|
|
||||||
c.nsSelectors = newSelectorSet(ips, c.onNewNsSelector)
|
doNothing := func(*selector, policyType) error { return nil }
|
||||||
|
c.nsSelectors = newSelectorSet(ips, c.onNewNsSelector, doNothing, doNothing)
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@@ -54,7 +56,7 @@ func (npc *controller) onNewNsSelector(selector *selector) error {
|
|||||||
for _, ns := range npc.nss {
|
for _, ns := range npc.nss {
|
||||||
if ns.namespace != nil {
|
if ns.namespace != nil {
|
||||||
if selector.matches(ns.namespace.ObjectMeta.Labels) {
|
if selector.matches(ns.namespace.ObjectMeta.Labels) {
|
||||||
if err := selector.addEntry(string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
|
if err := selector.addEntry(ns.namespace.ObjectMeta.UID, string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,32 +117,47 @@ func (npc *controller) DeletePod(obj *coreapi.Pod) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (npc *controller) AddNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
func (npc *controller) AddNetworkPolicy(obj interface{}) error {
|
||||||
npc.Lock()
|
npc.Lock()
|
||||||
defer npc.Unlock()
|
defer npc.Unlock()
|
||||||
|
|
||||||
|
nsName, err := nsName(obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
common.Log.Infof("EVENT AddNetworkPolicy %s", js(obj))
|
common.Log.Infof("EVENT AddNetworkPolicy %s", js(obj))
|
||||||
return npc.withNS(obj.ObjectMeta.Namespace, func(ns *ns) error {
|
return npc.withNS(nsName, func(ns *ns) error {
|
||||||
return errors.Wrap(ns.addNetworkPolicy(obj), "add network policy")
|
return errors.Wrap(ns.addNetworkPolicy(obj), "add network policy")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (npc *controller) UpdateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error {
|
func (npc *controller) UpdateNetworkPolicy(oldObj, newObj interface{}) error {
|
||||||
npc.Lock()
|
npc.Lock()
|
||||||
defer npc.Unlock()
|
defer npc.Unlock()
|
||||||
|
|
||||||
|
nsName, err := nsName(oldObj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
common.Log.Infof("EVENT UpdateNetworkPolicy %s %s", js(oldObj), js(newObj))
|
common.Log.Infof("EVENT UpdateNetworkPolicy %s %s", js(oldObj), js(newObj))
|
||||||
return npc.withNS(oldObj.ObjectMeta.Namespace, func(ns *ns) error {
|
return npc.withNS(nsName, func(ns *ns) error {
|
||||||
return errors.Wrap(ns.updateNetworkPolicy(oldObj, newObj), "update network policy")
|
return errors.Wrap(ns.updateNetworkPolicy(oldObj, newObj), "update network policy")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (npc *controller) DeleteNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
func (npc *controller) DeleteNetworkPolicy(obj interface{}) error {
|
||||||
npc.Lock()
|
npc.Lock()
|
||||||
defer npc.Unlock()
|
defer npc.Unlock()
|
||||||
|
|
||||||
|
nsName, err := nsName(obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
common.Log.Infof("EVENT DeleteNetworkPolicy %s", js(obj))
|
common.Log.Infof("EVENT DeleteNetworkPolicy %s", js(obj))
|
||||||
return npc.withNS(obj.ObjectMeta.Namespace, func(ns *ns) error {
|
return npc.withNS(nsName, func(ns *ns) error {
|
||||||
return errors.Wrap(ns.deleteNetworkPolicy(obj), "delete network policy")
|
return errors.Wrap(ns.deleteNetworkPolicy(obj), "delete network policy")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -174,3 +191,14 @@ func (npc *controller) DeleteNamespace(obj *coreapi.Namespace) error {
|
|||||||
return errors.Wrap(ns.deleteNamespace(obj), "delete namespace")
|
return errors.Wrap(ns.deleteNamespace(obj), "delete namespace")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nsName(obj interface{}) (string, error) {
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *networkingv1.NetworkPolicy:
|
||||||
|
return obj.ObjectMeta.Namespace, nil
|
||||||
|
case *extnapi.NetworkPolicy:
|
||||||
|
return obj.ObjectMeta.Namespace, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errInvalidNetworkPolicyObjType
|
||||||
|
}
|
||||||
+450
-38
@@ -1,22 +1,25 @@
|
|||||||
package npc
|
package npc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/weaveworks/weave/npc/ipset"
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
unversionedapi "k8s.io/client-go/pkg/api/unversioned"
|
coreapi "k8s.io/api/core/v1"
|
||||||
coreapi "k8s.io/client-go/pkg/api/v1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockSet struct {
|
type mockSet struct {
|
||||||
name ipset.Name
|
name ipset.Name
|
||||||
setType ipset.Type
|
setType ipset.Type
|
||||||
subSets map[string]bool
|
subSets map[string]map[types.UID]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockIPSet struct {
|
type mockIPSet struct {
|
||||||
@@ -24,47 +27,59 @@ type mockIPSet struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newMockIPSet() mockIPSet {
|
func newMockIPSet() mockIPSet {
|
||||||
i := mockIPSet{
|
return mockIPSet{sets: make(map[string]mockSet)}
|
||||||
sets: make(map[string]mockSet),
|
|
||||||
}
|
|
||||||
|
|
||||||
return i
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockIPSet) Create(ipsetName ipset.Name, ipsetType ipset.Type) error {
|
func (i *mockIPSet) Create(ipsetName ipset.Name, ipsetType ipset.Type) error {
|
||||||
if _, ok := i.sets[string(ipsetName)]; ok {
|
if _, ok := i.sets[string(ipsetName)]; ok {
|
||||||
return errors.Errorf("ipset %s already exists", ipsetName)
|
return errors.Errorf("ipset %s already exists", ipsetName)
|
||||||
}
|
}
|
||||||
i.sets[string(ipsetName)] = mockSet{name: ipsetName, setType: ipsetType, subSets: make(map[string]bool)}
|
i.sets[string(ipsetName)] = mockSet{name: ipsetName, setType: ipsetType, subSets: make(map[string]map[types.UID]bool)}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockIPSet) AddEntry(ipsetName ipset.Name, entry string, comment string) error {
|
func (i *mockIPSet) AddEntry(user types.UID, ipsetName ipset.Name, entry string, comment string) error {
|
||||||
log.Printf("adding entry %s to %s", entry, ipsetName)
|
log.Printf("adding entry %s to %s for %s", entry, ipsetName, user)
|
||||||
if _, ok := i.sets[entry]; !ok {
|
if _, ok := i.sets[string(ipsetName)]; !ok {
|
||||||
return errors.Errorf("ipset %s does not exist", entry)
|
return errors.Errorf("%s does not exist", entry)
|
||||||
}
|
}
|
||||||
if _, ok := i.sets[string(ipsetName)].subSets[entry]; ok {
|
if i.sets[string(ipsetName)].subSets[entry] == nil {
|
||||||
return errors.Errorf("ipset %s is already a member of %s", entry, ipsetName)
|
i.sets[string(ipsetName)].subSets[entry] = make(map[types.UID]bool)
|
||||||
}
|
}
|
||||||
i.sets[string(ipsetName)].subSets[entry] = true
|
if _, ok := i.sets[string(ipsetName)].subSets[entry][user]; ok {
|
||||||
|
return errors.Errorf("user %s already owns entry %s", user, entry)
|
||||||
|
}
|
||||||
|
i.sets[string(ipsetName)].subSets[entry][user] = true
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockIPSet) DelEntry(ipsetName ipset.Name, entry string) error {
|
func (i *mockIPSet) DelEntry(user types.UID, ipsetName ipset.Name, entry string) error {
|
||||||
log.Printf("deleting entry %s from %s", entry, ipsetName)
|
log.Printf("deleting entry %s from %s for %s", entry, ipsetName, user)
|
||||||
if _, ok := i.sets[string(ipsetName)]; !ok {
|
if _, ok := i.sets[string(ipsetName)]; !ok {
|
||||||
return errors.Errorf("ipset %s does not exist", ipsetName)
|
return errors.Errorf("ipset %s does not exist", ipsetName)
|
||||||
}
|
}
|
||||||
if _, ok := i.sets[string(ipsetName)].subSets[entry]; !ok {
|
if _, ok := i.sets[string(ipsetName)].subSets[entry][user]; !ok {
|
||||||
return errors.Errorf("ipset %s is not a member of %s", entry, ipsetName)
|
return errors.Errorf("user %s does not own entry %s", user, entry)
|
||||||
|
}
|
||||||
|
delete(i.sets[string(ipsetName)].subSets[entry], user)
|
||||||
|
|
||||||
|
if len(i.sets[string(ipsetName)].subSets[entry]) == 0 {
|
||||||
|
delete(i.sets[string(ipsetName)].subSets, entry)
|
||||||
}
|
}
|
||||||
delete(i.sets[string(ipsetName)].subSets, entry)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *mockIPSet) Exist(user types.UID, ipsetName ipset.Name, entry string) bool {
|
||||||
|
_, ok := i.sets[string(ipsetName)].subSets[entry][user]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *mockIPSet) entryExists(ipsetName ipset.Name, entry string) bool {
|
||||||
|
return len(i.sets[string(ipsetName)].subSets[entry]) > 0
|
||||||
|
}
|
||||||
|
|
||||||
func (i *mockIPSet) Flush(ipsetName ipset.Name) error {
|
func (i *mockIPSet) Flush(ipsetName ipset.Name) error {
|
||||||
return errors.New("Not Implemented")
|
return errors.New("Not Implemented")
|
||||||
}
|
}
|
||||||
@@ -90,18 +105,46 @@ func (i *mockIPSet) List(prefix string) ([]ipset.Name, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mockIPTables struct {
|
type mockIPTables struct {
|
||||||
|
rules map[string]map[string]struct{} // chain -> rulespec -> struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockIPTables() *mockIPTables {
|
||||||
|
return &mockIPTables{rules: make(map[string]map[string]struct{})}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ipt *mockIPTables) Append(table, chain string, rulespec ...string) error {
|
func (ipt *mockIPTables) Append(table, chain string, rulespec ...string) error {
|
||||||
|
if table != TableFilter {
|
||||||
|
return fmt.Errorf("invalid table: %q", table)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found := ipt.rules[chain]; !found {
|
||||||
|
ipt.rules[chain] = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
rule := strings.Join(rulespec, " ")
|
||||||
|
if _, found := ipt.rules[chain][rule]; found {
|
||||||
|
return fmt.Errorf("rule already exists. chain: %q, rule: %q", chain, rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipt.rules[chain][rule] = struct{}{}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ipt *mockIPTables) Delete(table, chain string, rulespec ...string) error {
|
func (ipt *mockIPTables) Delete(table, chain string, rulespec ...string) error {
|
||||||
|
rule := strings.Join(rulespec, " ")
|
||||||
|
|
||||||
|
if _, found := ipt.rules[chain][rule]; !found {
|
||||||
|
return fmt.Errorf("rule does not exist. chain: %q rule: %q", chain, rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(ipt.rules[chain], rule)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ipt *mockIPTables) Insert(table, chain string, pos int, rulespec ...string) error {
|
func (ipt *mockIPTables) Insert(table, chain string, pos int, rulespec ...string) error {
|
||||||
return nil
|
return errors.New("Not Implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
||||||
@@ -109,7 +152,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
|||||||
// https://github.com/weaveworks/weave/issues/3059
|
// https://github.com/weaveworks/weave/issues/3059
|
||||||
|
|
||||||
sourceNamespace := &coreapi.Namespace{
|
sourceNamespace := &coreapi.Namespace{
|
||||||
ObjectMeta: coreapi.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "source",
|
Name: "source",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
"app": "source",
|
"app": "source",
|
||||||
@@ -118,31 +161,32 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destinationNamespace := &coreapi.Namespace{
|
destinationNamespace := &coreapi.Namespace{
|
||||||
ObjectMeta: coreapi.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "destination",
|
Name: "destination",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
port := intstr.FromInt(12345)
|
port := intstr.FromInt(12345)
|
||||||
|
|
||||||
networkPolicy := &extnapi.NetworkPolicy{
|
networkPolicy := &networkingv1.NetworkPolicy{
|
||||||
ObjectMeta: coreapi.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "network-policy",
|
Name: "network-policy",
|
||||||
Namespace: "destination",
|
Namespace: "destination",
|
||||||
},
|
},
|
||||||
Spec: extnapi.NetworkPolicySpec{
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
Ingress: []extnapi.NetworkPolicyIngressRule{
|
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||||
|
Ingress: []networkingv1.NetworkPolicyIngressRule{
|
||||||
{
|
{
|
||||||
From: []extnapi.NetworkPolicyPeer{
|
From: []networkingv1.NetworkPolicyPeer{
|
||||||
{
|
{
|
||||||
NamespaceSelector: &unversionedapi.LabelSelector{
|
NamespaceSelector: &metav1.LabelSelector{
|
||||||
MatchLabels: map[string]string{
|
MatchLabels: map[string]string{
|
||||||
"app": "source",
|
"app": "source",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ports: []extnapi.NetworkPolicyPort{
|
Ports: []networkingv1.NetworkPolicyPort{
|
||||||
{
|
{
|
||||||
Port: &port,
|
Port: &port,
|
||||||
},
|
},
|
||||||
@@ -154,7 +198,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
|||||||
|
|
||||||
// Namespaces first
|
// Namespaces first
|
||||||
m := newMockIPSet()
|
m := newMockIPSet()
|
||||||
controller := New("foo", &mockIPTables{}, &m)
|
controller := New("foo", newMockIPTables(), &m)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
selectorIPSetName = "weave-I239Zp%sCvoVt*D6u=A!2]YEk"
|
selectorIPSetName = "weave-I239Zp%sCvoVt*D6u=A!2]YEk"
|
||||||
@@ -166,17 +210,385 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
|||||||
|
|
||||||
controller.AddNetworkPolicy(networkPolicy)
|
controller.AddNetworkPolicy(networkPolicy)
|
||||||
|
|
||||||
require.Equal(t, true, m.sets[selectorIPSetName].subSets[sourceIPSetName])
|
require.Equal(t, true, len(m.sets[selectorIPSetName].subSets[sourceIPSetName]) > 0)
|
||||||
|
|
||||||
// NetworkPolicy first
|
// NetworkPolicy first
|
||||||
m = newMockIPSet()
|
m = newMockIPSet()
|
||||||
controller = New("foo", &mockIPTables{}, &m)
|
controller = New("foo", newMockIPTables(), &m)
|
||||||
|
|
||||||
controller.AddNetworkPolicy(networkPolicy)
|
controller.AddNetworkPolicy(networkPolicy)
|
||||||
|
|
||||||
controller.AddNamespace(sourceNamespace)
|
controller.AddNamespace(sourceNamespace)
|
||||||
controller.AddNamespace(destinationNamespace)
|
controller.AddNamespace(destinationNamespace)
|
||||||
|
|
||||||
require.Equal(t, true, m.sets[selectorIPSetName].subSets[sourceIPSetName])
|
require.Equal(t, true, len(m.sets[selectorIPSetName].subSets[sourceIPSetName]) > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests default-allow ipset behavior
|
||||||
|
func TestDefaultAllow(t *testing.T) {
|
||||||
|
const (
|
||||||
|
ingressDefaultAllowIPSetName = "weave-;rGqyMIl1HN^cfDki~Z$3]6!N"
|
||||||
|
egressDefaultAllowIPSetName = "weave-s_+ChJId4Uy_$}G;WdH|~TK)I"
|
||||||
|
fooPodIP = "10.32.0.10"
|
||||||
|
barPodIP = "10.32.0.11"
|
||||||
|
barPodNewIP = "10.32.0.12"
|
||||||
|
)
|
||||||
|
|
||||||
|
m := newMockIPSet()
|
||||||
|
controller := New("bar", newMockIPTables(), &m)
|
||||||
|
|
||||||
|
defaultNamespace := &coreapi.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
controller.AddNamespace(defaultNamespace)
|
||||||
|
|
||||||
|
// Should create an ipset for default-allow
|
||||||
|
require.Contains(t, m.sets, ingressDefaultAllowIPSetName)
|
||||||
|
require.Contains(t, m.sets, egressDefaultAllowIPSetName)
|
||||||
|
|
||||||
|
podFoo := &coreapi.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "foo",
|
||||||
|
Namespace: "default",
|
||||||
|
Name: "foo",
|
||||||
|
Labels: map[string]string{"run": "foo"}},
|
||||||
|
Status: coreapi.PodStatus{PodIP: fooPodIP}}
|
||||||
|
controller.AddPod(podFoo)
|
||||||
|
|
||||||
|
// Should add the foo pod to default-allow
|
||||||
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
require.True(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
|
podBar := &coreapi.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "bar",
|
||||||
|
Namespace: "default",
|
||||||
|
Name: "bar",
|
||||||
|
Labels: map[string]string{"run": "bar"}},
|
||||||
|
Status: coreapi.PodStatus{PodIP: barPodIP}}
|
||||||
|
podBarNoIP := &coreapi.Pod{ObjectMeta: podBar.ObjectMeta}
|
||||||
|
controller.AddPod(podBarNoIP)
|
||||||
|
|
||||||
|
controller.UpdatePod(podBarNoIP, podBar)
|
||||||
|
|
||||||
|
// Should add the bar pod to default-allow
|
||||||
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, barPodIP))
|
||||||
|
require.True(t, m.entryExists(egressDefaultAllowIPSetName, barPodIP))
|
||||||
|
|
||||||
|
// Allow access from the bar pod to the foo pod
|
||||||
|
netpol := &networkingv1.NetworkPolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "allow-from-bar-to-foo",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{
|
||||||
|
networkingv1.PolicyTypeIngress,
|
||||||
|
networkingv1.PolicyTypeEgress,
|
||||||
|
},
|
||||||
|
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "foo"}},
|
||||||
|
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||||
|
From: []networkingv1.NetworkPolicyPeer{{
|
||||||
|
PodSelector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{"run": "bar"},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
Egress: []networkingv1.NetworkPolicyEgressRule{{
|
||||||
|
To: []networkingv1.NetworkPolicyPeer{{
|
||||||
|
IPBlock: &networkingv1.IPBlock{CIDR: "192.168.48.0/24"},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
controller.AddNetworkPolicy(netpol)
|
||||||
|
|
||||||
|
// Should remove the foo pod from default-allow as the netpol selects it
|
||||||
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
require.False(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, barPodIP))
|
||||||
|
require.True(t, m.entryExists(egressDefaultAllowIPSetName, barPodIP))
|
||||||
|
|
||||||
|
podBarWithNewIP := *podBar
|
||||||
|
podBarWithNewIP.Status.PodIP = barPodNewIP
|
||||||
|
controller.UpdatePod(podBar, &podBarWithNewIP)
|
||||||
|
|
||||||
|
// Should update IP addr of the bar pod in default-allow
|
||||||
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, barPodIP))
|
||||||
|
require.False(t, m.entryExists(egressDefaultAllowIPSetName, barPodIP))
|
||||||
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, barPodNewIP))
|
||||||
|
require.True(t, m.entryExists(egressDefaultAllowIPSetName, barPodNewIP))
|
||||||
|
|
||||||
|
controller.UpdatePod(&podBarWithNewIP, podBarNoIP)
|
||||||
|
// Should remove the bar pod from default-allow as it does not have any IP addr
|
||||||
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, barPodNewIP))
|
||||||
|
require.False(t, m.entryExists(egressDefaultAllowIPSetName, barPodNewIP))
|
||||||
|
|
||||||
|
podFooWithNewLabel := *podFoo
|
||||||
|
podFooWithNewLabel.ObjectMeta.Labels = map[string]string{"run": "new-foo"}
|
||||||
|
controller.UpdatePod(podFoo, &podFooWithNewLabel)
|
||||||
|
|
||||||
|
// Should bring back the foo pod to default-allow as it does not match dst of any netpol
|
||||||
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
require.True(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
|
controller.UpdatePod(&podFooWithNewLabel, podFoo)
|
||||||
|
// Should remove from default-allow as it matches the netpol after the update
|
||||||
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
require.False(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
|
controller.DeleteNetworkPolicy(netpol)
|
||||||
|
// Should bring back the foo pod to default-allow as no netpol selects it
|
||||||
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
require.True(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
|
controller.DeletePod(podFoo)
|
||||||
|
// Should remove foo pod from default-allow
|
||||||
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
require.False(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
|
controller.DeleteNamespace(defaultNamespace)
|
||||||
|
// Should remove default ipset
|
||||||
|
require.NotContains(t, m.sets, ingressDefaultAllowIPSetName)
|
||||||
|
require.NotContains(t, m.sets, egressDefaultAllowIPSetName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOutOfOrderPodEvents(t *testing.T) {
|
||||||
|
const (
|
||||||
|
ingressDefaultAllowIPSetName = "weave-;rGqyMIl1HN^cfDki~Z$3]6!N"
|
||||||
|
runBarIPSetName = "weave-bZ~x=yBgzH)Ht()K*Uv3z{M]Y"
|
||||||
|
podIP = "10.32.0.10"
|
||||||
|
)
|
||||||
|
|
||||||
|
m := newMockIPSet()
|
||||||
|
controller := New("qux", newMockIPTables(), &m)
|
||||||
|
|
||||||
|
defaultNamespace := &coreapi.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
controller.AddNamespace(defaultNamespace)
|
||||||
|
|
||||||
|
podFoo := &coreapi.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "foo",
|
||||||
|
Namespace: "default",
|
||||||
|
Name: "foo",
|
||||||
|
Labels: map[string]string{"run": "foo"}},
|
||||||
|
Status: coreapi.PodStatus{PodIP: podIP}}
|
||||||
|
controller.AddPod(podFoo)
|
||||||
|
|
||||||
|
// Should be in default-allow as no netpol selects podFoo
|
||||||
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
|
||||||
|
netpol := &networkingv1.NetworkPolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "allow-from-bar-to-foo",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||||
|
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "foo"}},
|
||||||
|
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||||
|
From: []networkingv1.NetworkPolicyPeer{{
|
||||||
|
PodSelector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{"run": "bar"},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
controller.AddNetworkPolicy(netpol)
|
||||||
|
|
||||||
|
// Shouldn't be in default-allow as netpol above selects podFoo
|
||||||
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
|
||||||
|
podBar := &coreapi.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "bar",
|
||||||
|
Namespace: "default",
|
||||||
|
Name: "bar",
|
||||||
|
Labels: map[string]string{"run": "bar"}},
|
||||||
|
Status: coreapi.PodStatus{PodIP: podIP}}
|
||||||
|
controller.AddPod(podBar)
|
||||||
|
|
||||||
|
// Should be in default-allow as no netpol selects podBar
|
||||||
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
require.True(t, m.Exist(podBar.ObjectMeta.UID, ingressDefaultAllowIPSetName, podIP))
|
||||||
|
// Should be in run=bar ipset
|
||||||
|
require.True(t, m.entryExists(runBarIPSetName, podIP))
|
||||||
|
|
||||||
|
controller.DeletePod(podFoo)
|
||||||
|
// Multiple duplicate events should not affect npc state
|
||||||
|
controller.DeletePod(podFoo)
|
||||||
|
controller.DeletePod(podFoo)
|
||||||
|
|
||||||
|
// Should be in default-allow as no netpol selects podBar and podFoo removal
|
||||||
|
// should not affect podBar in default-allow
|
||||||
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
|
||||||
|
controller.DeletePod(podBar)
|
||||||
|
|
||||||
|
// Should remove from default-allow and run=bar ipsets
|
||||||
|
require.Equal(t, 0, len(m.sets[ingressDefaultAllowIPSetName].subSets))
|
||||||
|
require.False(t, m.entryExists(runBarIPSetName, podIP))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case for https://github.com/weaveworks/weave/issues/3222
|
||||||
|
func TestNewTargetSelector(t *testing.T) {
|
||||||
|
const (
|
||||||
|
ingressDefaultAllowIPSetName = "weave-;rGqyMIl1HN^cfDki~Z$3]6!N"
|
||||||
|
podIP = "10.32.0.10"
|
||||||
|
)
|
||||||
|
|
||||||
|
m := newMockIPSet()
|
||||||
|
controller := New("baz", newMockIPTables(), &m)
|
||||||
|
|
||||||
|
defaultNamespace := &coreapi.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
controller.AddNamespace(defaultNamespace)
|
||||||
|
|
||||||
|
podFoo := &coreapi.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "foo",
|
||||||
|
Namespace: "default",
|
||||||
|
Name: "foo",
|
||||||
|
Labels: map[string]string{"run": "foo"}},
|
||||||
|
Status: coreapi.PodStatus{PodIP: podIP}}
|
||||||
|
controller.AddPod(podFoo)
|
||||||
|
|
||||||
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
|
||||||
|
netpolBar := &networkingv1.NetworkPolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "netpol-bar",
|
||||||
|
Name: "allow-from-default-1",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||||
|
PodSelector: metav1.LabelSelector{},
|
||||||
|
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||||
|
From: []networkingv1.NetworkPolicyPeer{{
|
||||||
|
PodSelector: &metav1.LabelSelector{},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
controller.AddNetworkPolicy(netpolBar)
|
||||||
|
|
||||||
|
// netpolBar target selector selects podFoo
|
||||||
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
|
||||||
|
netpolFoo := &networkingv1.NetworkPolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "netpol-foo",
|
||||||
|
Name: "allow-from-default-2",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||||
|
PodSelector: metav1.LabelSelector{},
|
||||||
|
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||||
|
From: []networkingv1.NetworkPolicyPeer{{
|
||||||
|
PodSelector: &metav1.LabelSelector{},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
controller.AddNetworkPolicy(netpolFoo)
|
||||||
|
|
||||||
|
controller.DeleteNetworkPolicy(netpolBar)
|
||||||
|
// netpolFoo target-selects podFoo
|
||||||
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
controller.DeleteNetworkPolicy(netpolFoo)
|
||||||
|
// No netpol target-selects podFoo
|
||||||
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEgressPolicyWithIPBlock(t *testing.T) {
|
||||||
|
const (
|
||||||
|
fooPodIP = "10.32.0.10"
|
||||||
|
exceptIPSetName = "weave-j:W$5om!$8JS})buYAD7q^#sX"
|
||||||
|
exceptIPSetNameInNonDefault = "weave-|*P{aL2C@#N/1IZ!IvQItv_pt"
|
||||||
|
)
|
||||||
|
|
||||||
|
m := newMockIPSet()
|
||||||
|
ipt := newMockIPTables()
|
||||||
|
controller := New("foo", ipt, &m)
|
||||||
|
|
||||||
|
netpolFoo := &networkingv1.NetworkPolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "netpol-foo",
|
||||||
|
Name: "allow-from-bar-to-foo",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{
|
||||||
|
networkingv1.PolicyTypeEgress,
|
||||||
|
},
|
||||||
|
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "bar"}},
|
||||||
|
Egress: []networkingv1.NetworkPolicyEgressRule{{
|
||||||
|
To: []networkingv1.NetworkPolicyPeer{{
|
||||||
|
IPBlock: &networkingv1.IPBlock{
|
||||||
|
CIDR: "192.168.48.0/24",
|
||||||
|
Except: []string{
|
||||||
|
"192.168.48.1/32",
|
||||||
|
"192.168.48.2/32",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := controller.AddNetworkPolicy(netpolFoo)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, 2, len(m.sets[exceptIPSetName].subSets))
|
||||||
|
require.True(t, m.entryExists(exceptIPSetName, "192.168.48.1/32"))
|
||||||
|
require.True(t, m.entryExists(exceptIPSetName, "192.168.48.2/32"))
|
||||||
|
|
||||||
|
// Each egress rule is represented as two iptables rules (-J MARK and -J RETURN).
|
||||||
|
require.Equal(t, 2, len(ipt.rules[EgressCustomChain]))
|
||||||
|
for rule := range ipt.rules[EgressCustomChain] {
|
||||||
|
require.Contains(t, rule, "-d 192.168.48.0/24 -m set ! --match-set "+exceptIPSetName+" dst")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we create a new ipset for the ipBlock bellow. An ipset with the
|
||||||
|
// same content already exists (created by netpolFoo), but we need to create
|
||||||
|
// a new one, as netpolBar is in a different namespace and weave-npc does
|
||||||
|
// object refcounting only within namespace boundaries.
|
||||||
|
netpolBar := &networkingv1.NetworkPolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "netpol-foo-bar",
|
||||||
|
Name: "allow-from-bar-to-foo-in-non-default",
|
||||||
|
Namespace: "non-default",
|
||||||
|
},
|
||||||
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{
|
||||||
|
networkingv1.PolicyTypeEgress,
|
||||||
|
},
|
||||||
|
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "bar"}},
|
||||||
|
Egress: []networkingv1.NetworkPolicyEgressRule{{
|
||||||
|
To: []networkingv1.NetworkPolicyPeer{{
|
||||||
|
IPBlock: &networkingv1.IPBlock{
|
||||||
|
CIDR: "192.168.48.0/24",
|
||||||
|
Except: []string{
|
||||||
|
"192.168.48.1/32",
|
||||||
|
"192.168.48.2/32",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err = controller.AddNetworkPolicy(netpolBar)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(m.sets[exceptIPSetNameInNonDefault].subSets))
|
||||||
}
|
}
|
||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
package npc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/common"
|
||||||
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ipBlockSpec struct {
|
||||||
|
key string
|
||||||
|
ipsetName ipset.Name // ipset for storing excepted CIDRs
|
||||||
|
ipBlock *networkingv1.IPBlock
|
||||||
|
nsName string // Namespace name
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIPBlockSpec(ipb *networkingv1.IPBlock, nsName string) *ipBlockSpec {
|
||||||
|
spec := &ipBlockSpec{ipBlock: ipb, nsName: nsName}
|
||||||
|
|
||||||
|
if len(ipb.Except) > 0 {
|
||||||
|
sort.Strings(ipb.Except)
|
||||||
|
spec.key = strings.Join(ipb.Except, " ")
|
||||||
|
spec.ipsetName = ipset.Name(IpsetNamePrefix + shortName(nsName+":"+"ipblock-except:"+spec.key))
|
||||||
|
}
|
||||||
|
|
||||||
|
return spec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spec *ipBlockSpec) getRuleSpec(src bool) ([]string, string) {
|
||||||
|
dir, dirOpt := "dst", "-d"
|
||||||
|
if src {
|
||||||
|
dir, dirOpt = "src", "-s"
|
||||||
|
}
|
||||||
|
|
||||||
|
if spec.key == "" {
|
||||||
|
rule := []string{dirOpt, spec.ipBlock.CIDR}
|
||||||
|
comment := fmt.Sprintf("cidr: %s", spec.ipBlock.CIDR)
|
||||||
|
return rule, comment
|
||||||
|
}
|
||||||
|
|
||||||
|
rule := []string{dirOpt, spec.ipBlock.CIDR,
|
||||||
|
"-m", "set", "!", "--match-set", string(spec.ipsetName), dir}
|
||||||
|
comment := fmt.Sprintf("cidr: %s except [%s]", spec.ipBlock.CIDR, spec.key)
|
||||||
|
return rule, comment
|
||||||
|
}
|
||||||
|
|
||||||
|
type ipBlockSet struct {
|
||||||
|
ips ipset.Interface
|
||||||
|
users map[string]map[types.UID]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIPBlockSet(ips ipset.Interface) *ipBlockSet {
|
||||||
|
return &ipBlockSet{
|
||||||
|
ips: ips,
|
||||||
|
users: make(map[string]map[types.UID]struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ipBlockSet) deprovision(user types.UID, current, desired map[string]*ipBlockSpec) error {
|
||||||
|
for key, spec := range current {
|
||||||
|
if key == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found := desired[key]; !found {
|
||||||
|
delete(s.users[key], user)
|
||||||
|
if len(s.users[key]) == 0 {
|
||||||
|
common.Log.Infof("destroying ipset: %#v", spec)
|
||||||
|
if err := s.ips.Destroy(spec.ipsetName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(s.users, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ipBlockSet) provision(user types.UID, current, desired map[string]*ipBlockSpec) (err error) {
|
||||||
|
for key, spec := range desired {
|
||||||
|
if key == "" {
|
||||||
|
// No need to provision an ipBlock with empty list of excepted CIDRs
|
||||||
|
// (i.e. no ipset is needed for the related iptables rule).
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found := current[key]; !found {
|
||||||
|
if _, found := s.users[key]; !found {
|
||||||
|
common.Log.Infof("creating ipset: %#v", spec)
|
||||||
|
if err := s.ips.Create(spec.ipsetName, ipset.HashNet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(brb) Pass comment to ips.Create instead.
|
||||||
|
comment := "excepted from " + spec.ipBlock.CIDR
|
||||||
|
for _, cidr := range spec.ipBlock.Except {
|
||||||
|
if err = s.ips.AddEntry(user, spec.ipsetName, cidr, comment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.users[key] = make(map[types.UID]struct{})
|
||||||
|
}
|
||||||
|
s.users[key][user] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
package ipset
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"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, comment string) error
|
|
||||||
DelEntry(ipsetName Name, entry string) error
|
|
||||||
Flush(ipsetName Name) error
|
|
||||||
Destroy(ipsetName Name) error
|
|
||||||
|
|
||||||
List(prefix string) ([]Name, error)
|
|
||||||
|
|
||||||
FlushAll() error
|
|
||||||
DestroyAll() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type ipset struct {
|
|
||||||
refCount
|
|
||||||
*log.Logger
|
|
||||||
enableComments bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(logger *log.Logger) Interface {
|
|
||||||
ips := &ipset{refCount: newRefCount(), Logger: logger, enableComments: true}
|
|
||||||
|
|
||||||
// Check for comment support
|
|
||||||
testIpsetName := Name("weave-test-comment")
|
|
||||||
// Clear it out if it already exists
|
|
||||||
_ = ips.Destroy(testIpsetName)
|
|
||||||
// Test for comment support
|
|
||||||
if err := ips.Create(testIpsetName, HashIP); err != nil {
|
|
||||||
ips.Logger.Printf("failed to create %s; disabling comment support", testIpsetName)
|
|
||||||
ips.enableComments = false
|
|
||||||
}
|
|
||||||
// If it was created, destroy it
|
|
||||||
_ = ips.Destroy(testIpsetName)
|
|
||||||
|
|
||||||
return ips
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipset) Create(ipsetName Name, ipsetType Type) error {
|
|
||||||
args := []string{"create", string(ipsetName), string(ipsetType)}
|
|
||||||
if i.enableComments {
|
|
||||||
args = append(args, "comment")
|
|
||||||
}
|
|
||||||
return doExec(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipset) AddEntry(ipsetName Name, entry string, comment string) error {
|
|
||||||
i.Logger.Printf("adding entry %s to %s", entry, ipsetName)
|
|
||||||
if i.inc(ipsetName, entry) > 1 { // already in the set
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
args := []string{"add", string(ipsetName), entry}
|
|
||||||
if i.enableComments {
|
|
||||||
args = append(args, "comment", comment)
|
|
||||||
}
|
|
||||||
return doExec(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipset) DelEntry(ipsetName Name, entry string) error {
|
|
||||||
i.Logger.Printf("deleting entry %s from %s", entry, ipsetName)
|
|
||||||
if i.dec(ipsetName, entry) > 0 { // still needed
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return doExec("del", string(ipsetName), entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipset) Flush(ipsetName Name) error {
|
|
||||||
i.removeSet(ipsetName)
|
|
||||||
return doExec("flush", string(ipsetName))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipset) FlushAll() error {
|
|
||||||
i.refCount = newRefCount()
|
|
||||||
return doExec("flush")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipset) Destroy(ipsetName Name) error {
|
|
||||||
i.removeSet(ipsetName)
|
|
||||||
return doExec("destroy", string(ipsetName))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipset) DestroyAll() error {
|
|
||||||
i.refCount = newRefCount()
|
|
||||||
return doExec("destroy")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch a list of all existing sets with a given prefix
|
|
||||||
func (i *ipset) List(prefix string) ([]Name, error) {
|
|
||||||
output, err := exec.Command("ipset", "list", "-name", "-output", "plain").Output()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var selected []Name
|
|
||||||
sets := strings.Split(string(output), "\n")
|
|
||||||
for _, v := range sets {
|
|
||||||
if strings.HasPrefix(v, prefix) {
|
|
||||||
selected = append(selected, Name(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return selected, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func doExec(args ...string) error {
|
|
||||||
if output, err := exec.Command("ipset", args...).CombinedOutput(); err != nil {
|
|
||||||
return errors.Wrapf(err, "ipset %v failed: %s", args, output)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reference-counting
|
|
||||||
type key struct {
|
|
||||||
ipsetName Name
|
|
||||||
entry string
|
|
||||||
}
|
|
||||||
|
|
||||||
// note no locking is required as all operations are serialised in the controller
|
|
||||||
type refCount struct {
|
|
||||||
ref map[key]int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRefCount() refCount {
|
|
||||||
return refCount{ref: make(map[key]int)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rc *refCount) inc(ipsetName Name, entry string) int {
|
|
||||||
k := key{ipsetName, entry}
|
|
||||||
rc.ref[k]++
|
|
||||||
return rc.ref[k]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rc *refCount) dec(ipsetName Name, entry string) int {
|
|
||||||
k := key{ipsetName, entry}
|
|
||||||
rc.ref[k]--
|
|
||||||
return rc.ref[k]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rc *refCount) removeSet(ipsetName Name) {
|
|
||||||
for k := range rc.ref {
|
|
||||||
if k.ipsetName == ipsetName {
|
|
||||||
delete(rc.ref, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+49
@@ -4,7 +4,56 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Return JSON suitable for logging an API object.
|
||||||
func js(v interface{}) string {
|
func js(v interface{}) string {
|
||||||
|
// Get the raw JSON
|
||||||
a, _ := json.Marshal(v)
|
a, _ := json.Marshal(v)
|
||||||
|
// Convert this back into a tree of key-value maps
|
||||||
|
var m map[string]interface{}
|
||||||
|
if err := json.Unmarshal(a, &m); err != nil {
|
||||||
|
// If that didn't work, just return the raw version
|
||||||
|
return string(a)
|
||||||
|
}
|
||||||
|
// Trim some bulk, and potentially sensitive areas
|
||||||
|
withMap(m["metadata"], func(status map[string]interface{}) {
|
||||||
|
delete(status, "ownerReferences")
|
||||||
|
})
|
||||||
|
withMap(m["spec"], func(spec map[string]interface{}) {
|
||||||
|
delete(spec, "tolerations")
|
||||||
|
delete(spec, "volumes")
|
||||||
|
rangeSlice(spec["containers"], func(container map[string]interface{}) {
|
||||||
|
delete(container, "args")
|
||||||
|
delete(container, "command")
|
||||||
|
delete(container, "env")
|
||||||
|
delete(container, "livenessProbe")
|
||||||
|
delete(container, "resources")
|
||||||
|
delete(container, "securityContext")
|
||||||
|
delete(container, "volumeMounts")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
withMap(m["status"], func(status map[string]interface{}) {
|
||||||
|
delete(status, "containerStatuses")
|
||||||
|
})
|
||||||
|
// Now marshall what's left to JSON
|
||||||
|
a, _ = json.Marshal(m)
|
||||||
return string(a)
|
return string(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function: operate on a map node from a tree of key-value maps
|
||||||
|
func withMap(m interface{}, f func(map[string]interface{})) {
|
||||||
|
if v, ok := m.(map[string]interface{}); ok {
|
||||||
|
f(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function: operate on all nodes under i which is a slice in a
|
||||||
|
// tree of key-value maps
|
||||||
|
func rangeSlice(i interface{}, f func(map[string]interface{})) {
|
||||||
|
if s, ok := i.([]interface{}); ok {
|
||||||
|
for _, v := range s {
|
||||||
|
if m, ok := v.(map[string]interface{}); ok {
|
||||||
|
f(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+325
-118
@@ -1,40 +1,50 @@
|
|||||||
package npc
|
package npc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"k8s.io/client-go/pkg/api/unversioned"
|
coreapi "k8s.io/api/core/v1"
|
||||||
coreapi "k8s.io/client-go/pkg/api/v1"
|
extnapi "k8s.io/api/extensions/v1beta1"
|
||||||
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
"k8s.io/client-go/pkg/types"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/pkg/util/uuid"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/uuid"
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
"github.com/weaveworks/weave/common"
|
||||||
"github.com/weaveworks/weave/npc/ipset"
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
"github.com/weaveworks/weave/npc/iptables"
|
"github.com/weaveworks/weave/npc/iptables"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var errInvalidNetworkPolicyObjType = errors.New("invalid NetworkPolicy object type")
|
||||||
|
|
||||||
type ns struct {
|
type ns struct {
|
||||||
ipt iptables.Interface // interface to iptables
|
ipt iptables.Interface // interface to iptables
|
||||||
ips ipset.Interface // interface to ipset
|
ips ipset.Interface // interface to ipset
|
||||||
|
|
||||||
name string // k8s Namespace name
|
name string // k8s Namespace name
|
||||||
nodeName string // my node name
|
nodeName string // my node name
|
||||||
namespace *coreapi.Namespace // k8s Namespace object
|
namespace *coreapi.Namespace // k8s Namespace object
|
||||||
pods map[types.UID]*coreapi.Pod // k8s Pod objects by UID
|
pods map[types.UID]*coreapi.Pod // k8s Pod objects by UID
|
||||||
policies map[types.UID]*extnapi.NetworkPolicy // k8s NetworkPolicy objects by UID
|
policies map[types.UID]interface{} // k8s NetworkPolicy objects by UID
|
||||||
|
|
||||||
uid types.UID // surrogate UID to own allPods selector
|
uid types.UID // surrogate UID to own allPods selector
|
||||||
allPods *selectorSpec // hash:ip ipset of all pod IPs in this namespace
|
allPods *selectorSpec // hash:ip ipset of all pod IPs in this namespace
|
||||||
|
|
||||||
|
// stores IP addrs of pods which are not selected by any target podSelector of
|
||||||
|
// any netpol; used as a target in the WEAVE-NPC-{INGRESS,EGRESS}-DEFAULT
|
||||||
|
// iptables chains.
|
||||||
|
ingressDefaultAllowIPSet ipset.Name
|
||||||
|
egressDefaultAllowIPSet ipset.Name
|
||||||
|
|
||||||
nsSelectors *selectorSet
|
nsSelectors *selectorSet
|
||||||
podSelectors *selectorSet
|
podSelectors *selectorSet
|
||||||
|
ipBlocks *ipBlockSet
|
||||||
rules *ruleSet
|
rules *ruleSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNS(name, nodeName string, ipt iptables.Interface, ips ipset.Interface, nsSelectors *selectorSet) (*ns, error) {
|
func newNS(name, nodeName string, ipt iptables.Interface, ips ipset.Interface, nsSelectors *selectorSet) (*ns, error) {
|
||||||
allPods, err := newSelectorSpec(&unversioned.LabelSelector{}, name, ipset.HashIP)
|
allPods, err := newSelectorSpec(&metav1.LabelSelector{}, nil, name, ipset.HashIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -45,13 +55,27 @@ func newNS(name, nodeName string, ipt iptables.Interface, ips ipset.Interface, n
|
|||||||
name: name,
|
name: name,
|
||||||
nodeName: nodeName,
|
nodeName: nodeName,
|
||||||
pods: make(map[types.UID]*coreapi.Pod),
|
pods: make(map[types.UID]*coreapi.Pod),
|
||||||
policies: make(map[types.UID]*extnapi.NetworkPolicy),
|
policies: make(map[types.UID]interface{}),
|
||||||
uid: uuid.NewUUID(),
|
uid: uuid.NewUUID(),
|
||||||
allPods: allPods,
|
allPods: allPods,
|
||||||
nsSelectors: nsSelectors,
|
nsSelectors: nsSelectors,
|
||||||
rules: newRuleSet(ipt)}
|
ipBlocks: newIPBlockSet(ips),
|
||||||
|
rules: newRuleSet(ipt),
|
||||||
|
}
|
||||||
|
|
||||||
ns.podSelectors = newSelectorSet(ips, ns.onNewPodSelector)
|
ns.podSelectors = newSelectorSet(ips, ns.onNewPodSelector, ns.onNewTargetPodSelector, ns.onDestroyTargetPodSelector)
|
||||||
|
|
||||||
|
ingressDefaultAllowIPSet := ipset.Name(IpsetNamePrefix + shortName("ingress-default-allow:"+name))
|
||||||
|
if err := ips.Create(ingressDefaultAllowIPSet, ipset.HashIP); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ns.ingressDefaultAllowIPSet = ingressDefaultAllowIPSet
|
||||||
|
|
||||||
|
egressDefaultAllowIPSet := ipset.Name(IpsetNamePrefix + shortName("egress-default-allow:"+name))
|
||||||
|
if err := ips.Create(egressDefaultAllowIPSet, ipset.HashIP); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ns.egressDefaultAllowIPSet = egressDefaultAllowIPSet
|
||||||
|
|
||||||
if err := ns.podSelectors.provision(ns.uid, nil, map[string]*selectorSpec{ns.allPods.key: ns.allPods}); err != nil {
|
if err := ns.podSelectors.provision(ns.uid, nil, map[string]*selectorSpec{ns.allPods.key: ns.allPods}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -65,22 +89,69 @@ func (ns *ns) empty() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) destroy() error {
|
func (ns *ns) destroy() error {
|
||||||
if err := ns.podSelectors.deprovision(ns.uid, map[string]*selectorSpec{ns.allPods.key: ns.allPods}, nil); err != nil {
|
return ns.podSelectors.deprovision(ns.uid, map[string]*selectorSpec{ns.allPods.key: ns.allPods}, nil)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) onNewPodSelector(selector *selector) error {
|
func (ns *ns) onNewPodSelector(selector *selector) error {
|
||||||
for _, pod := range ns.pods {
|
for _, pod := range ns.pods {
|
||||||
if hasIP(pod) {
|
if hasIP(pod) {
|
||||||
if selector.matches(pod.ObjectMeta.Labels) {
|
if selector.matches(pod.ObjectMeta.Labels) {
|
||||||
if err := selector.addEntry(pod.Status.PodIP, podComment(pod)); err != nil {
|
if err := selector.addEntry(pod.ObjectMeta.UID, pod.Status.PodIP, podComment(pod)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) onNewTargetPodSelector(selector *selector, policyType policyType) error {
|
||||||
|
for _, pod := range ns.pods {
|
||||||
|
if hasIP(pod) {
|
||||||
|
// Remove the pod from default-allow if dst podselector matches the pod
|
||||||
|
ipset := ns.defaultAllowIPSetName(policyType)
|
||||||
|
if selector.matches(pod.ObjectMeta.Labels) {
|
||||||
|
if err := ns.ips.DelEntry(pod.ObjectMeta.UID, ipset, pod.Status.PodIP); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) onDestroyTargetPodSelector(selector *selector, policyType policyType) error {
|
||||||
|
for _, pod := range ns.pods {
|
||||||
|
if hasIP(pod) {
|
||||||
|
if selector.matches(pod.ObjectMeta.Labels) {
|
||||||
|
if err := ns.addToDefaultAllowIfNoMatching(pod, policyType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add pod IP addr to default-allow ipset if there are no matching target selectors
|
||||||
|
func (ns *ns) addToDefaultAllowIfNoMatching(pod *coreapi.Pod, policyType policyType) error {
|
||||||
|
found := false
|
||||||
|
// TODO(mp) optimize (avoid iterating over selectors) by ref counting IP addrs.
|
||||||
|
for _, s := range ns.podSelectors.entries {
|
||||||
|
if ns.podSelectors.targetSelectorExist(s, policyType) && s.matches(pod.ObjectMeta.Labels) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
ipset := ns.defaultAllowIPSetName(policyType)
|
||||||
|
if err := ns.ips.AddEntry(pod.ObjectMeta.UID, ipset, pod.Status.PodIP, podComment(pod)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,9 +170,26 @@ func (ns *ns) addPod(obj *coreapi.Pod) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ns.checkLocalPod(obj) {
|
if ns.checkLocalPod(obj) {
|
||||||
ns.ips.AddEntry(LocalIpset, obj.Status.PodIP, podComment(obj))
|
ns.ips.AddEntry(obj.ObjectMeta.UID, LocalIpset, obj.Status.PodIP, podComment(obj))
|
||||||
}
|
}
|
||||||
return ns.podSelectors.addToMatching(obj.ObjectMeta.Labels, obj.Status.PodIP, podComment(obj))
|
|
||||||
|
foundIngress, foundEgress, err := ns.podSelectors.addToMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, obj.Status.PodIP, podComment(obj))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// If there are no matching target selectors, add the pod to default-allow
|
||||||
|
if !foundIngress {
|
||||||
|
if err := ns.ips.AddEntry(obj.ObjectMeta.UID, ns.ingressDefaultAllowIPSet, obj.Status.PodIP, podComment(obj)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundEgress {
|
||||||
|
if err := ns.ips.AddEntry(obj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, obj.Status.PodIP, podComment(obj)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||||
@@ -114,16 +202,49 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
|||||||
|
|
||||||
if hasIP(oldObj) && !hasIP(newObj) {
|
if hasIP(oldObj) && !hasIP(newObj) {
|
||||||
if ns.checkLocalPod(oldObj) {
|
if ns.checkLocalPod(oldObj) {
|
||||||
ns.ips.DelEntry(LocalIpset, oldObj.Status.PodIP)
|
ns.ips.DelEntry(oldObj.ObjectMeta.UID, LocalIpset, oldObj.Status.PodIP)
|
||||||
}
|
}
|
||||||
return ns.podSelectors.delFromMatching(oldObj.ObjectMeta.Labels, oldObj.Status.PodIP)
|
|
||||||
|
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.ingressDefaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ns.podSelectors.delFromMatching(oldObj.ObjectMeta.UID, oldObj.ObjectMeta.Labels, oldObj.Status.PodIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hasIP(oldObj) && hasIP(newObj) {
|
if !hasIP(oldObj) && hasIP(newObj) {
|
||||||
if ns.checkLocalPod(newObj) {
|
if ns.checkLocalPod(newObj) {
|
||||||
ns.ips.AddEntry(LocalIpset, newObj.Status.PodIP, podComment(newObj))
|
ns.ips.AddEntry(newObj.ObjectMeta.UID, LocalIpset, newObj.Status.PodIP, podComment(newObj))
|
||||||
|
}
|
||||||
|
foundIngress, foundEgress, err := ns.podSelectors.addToMatching(newObj.ObjectMeta.UID, newObj.ObjectMeta.Labels, newObj.Status.PodIP, podComment(newObj))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !foundIngress {
|
||||||
|
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.ingressDefaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundEgress {
|
||||||
|
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldObj.Status.PodIP != newObj.Status.PodIP {
|
||||||
|
if err := ns.updateDefaultAllowIPSetEntry(oldObj, newObj, ns.ingressDefaultAllowIPSet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.updateDefaultAllowIPSetEntry(oldObj, newObj, ns.egressDefaultAllowIPSet); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return ns.podSelectors.addToMatching(newObj.ObjectMeta.Labels, newObj.Status.PodIP, podComment(newObj))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !equals(oldObj.ObjectMeta.Labels, newObj.ObjectMeta.Labels) ||
|
if !equals(oldObj.ObjectMeta.Labels, newObj.ObjectMeta.Labels) ||
|
||||||
@@ -136,21 +257,45 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if oldMatch {
|
if oldMatch {
|
||||||
if err := ps.delEntry(oldObj.Status.PodIP); err != nil {
|
if err := ps.delEntry(oldObj.ObjectMeta.UID, oldObj.Status.PodIP); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if newMatch {
|
if newMatch {
|
||||||
if err := ps.addEntry(newObj.Status.PodIP, podComment(newObj)); err != nil {
|
if err := ps.addEntry(newObj.ObjectMeta.UID, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := ns.addOrRemoveToDefaultAllowIPSet(ps, oldObj, newObj, oldMatch, newMatch, policyTypeIngress); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.addOrRemoveToDefaultAllowIPSet(ps, oldObj, newObj, oldMatch, newMatch, policyTypeEgress); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ns *ns) addOrRemoveToDefaultAllowIPSet(ps *selector, oldObj, newObj *coreapi.Pod, oldMatch, newMatch bool, policyType policyType) error {
|
||||||
|
ipset := ns.defaultAllowIPSetName(policyType)
|
||||||
|
if ns.podSelectors.targetSelectorExist(ps, policyType) {
|
||||||
|
switch {
|
||||||
|
case !oldMatch && newMatch:
|
||||||
|
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ipset, oldObj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case oldMatch && !newMatch:
|
||||||
|
if err := ns.addToDefaultAllowIfNoMatching(newObj, policyType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
||||||
delete(ns.pods, obj.ObjectMeta.UID)
|
delete(ns.pods, obj.ObjectMeta.UID)
|
||||||
|
|
||||||
@@ -159,143 +304,178 @@ func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ns.checkLocalPod(obj) {
|
if ns.checkLocalPod(obj) {
|
||||||
ns.ips.DelEntry(LocalIpset, obj.Status.PodIP)
|
ns.ips.DelEntry(obj.ObjectMeta.UID, LocalIpset, obj.Status.PodIP)
|
||||||
}
|
}
|
||||||
return ns.podSelectors.delFromMatching(obj.ObjectMeta.Labels, obj.Status.PodIP)
|
|
||||||
|
if err := ns.ips.DelEntry(obj.ObjectMeta.UID, ns.ingressDefaultAllowIPSet, obj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.ips.DelEntry(obj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, obj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ns.podSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, obj.Status.PodIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) addNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
func (ns *ns) addNetworkPolicy(obj interface{}) error {
|
||||||
ns.policies[obj.ObjectMeta.UID] = obj
|
|
||||||
|
|
||||||
// Analyse policy, determine which rules and ipsets are required
|
// Analyse policy, determine which rules and ipsets are required
|
||||||
rules, nsSelectors, podSelectors, err := ns.analysePolicy(obj)
|
|
||||||
|
uid, rules, nsSelectors, podSelectors, ipBlocks, err := ns.analyse(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provision required resources in dependency order
|
// Provision required resources in dependency order
|
||||||
if err := ns.nsSelectors.provision(obj.ObjectMeta.UID, nil, nsSelectors); err != nil {
|
if err := ns.nsSelectors.provision(uid, nil, nsSelectors); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ns.podSelectors.provision(obj.ObjectMeta.UID, nil, podSelectors); err != nil {
|
if err := ns.podSelectors.provision(uid, nil, podSelectors); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ns.rules.provision(obj.ObjectMeta.UID, nil, rules); err != nil {
|
if err := ns.ipBlocks.provision(uid, nil, ipBlocks); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return ns.rules.provision(uid, nil, rules)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) updateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error {
|
func (ns *ns) updateNetworkPolicy(oldObj, newObj interface{}) error {
|
||||||
delete(ns.policies, oldObj.ObjectMeta.UID)
|
|
||||||
ns.policies[newObj.ObjectMeta.UID] = newObj
|
|
||||||
|
|
||||||
// Analyse the old and the new policy so we can determine differences
|
// Analyse the old and the new policy so we can determine differences
|
||||||
oldRules, oldNsSelectors, oldPodSelectors, err := ns.analysePolicy(oldObj)
|
oldUID, oldRules, oldNsSelectors, oldPodSelectors, oldIPBlocks, err := ns.analyse(oldObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
newRules, newNsSelectors, newPodSelectors, err := ns.analysePolicy(newObj)
|
newUID, newRules, newNsSelectors, newPodSelectors, newIPBlocks, err := ns.analyse(newObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete(ns.policies, oldUID)
|
||||||
|
ns.policies[newUID] = newObj
|
||||||
|
|
||||||
// Deprovision unused and provision newly required resources in dependency order
|
// Deprovision unused and provision newly required resources in dependency order
|
||||||
if err := ns.rules.deprovision(oldObj.ObjectMeta.UID, oldRules, newRules); err != nil {
|
if err := ns.rules.deprovision(oldUID, oldRules, newRules); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ns.nsSelectors.deprovision(oldObj.ObjectMeta.UID, oldNsSelectors, newNsSelectors); err != nil {
|
if err := ns.nsSelectors.deprovision(oldUID, oldNsSelectors, newNsSelectors); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ns.podSelectors.deprovision(oldObj.ObjectMeta.UID, oldPodSelectors, newPodSelectors); err != nil {
|
if err := ns.podSelectors.deprovision(oldUID, oldPodSelectors, newPodSelectors); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ns.nsSelectors.provision(oldObj.ObjectMeta.UID, oldNsSelectors, newNsSelectors); err != nil {
|
if err := ns.ipBlocks.deprovision(oldUID, oldIPBlocks, newIPBlocks); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ns.podSelectors.provision(oldObj.ObjectMeta.UID, oldPodSelectors, newPodSelectors); err != nil {
|
if err := ns.nsSelectors.provision(oldUID, oldNsSelectors, newNsSelectors); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ns.rules.provision(oldObj.ObjectMeta.UID, oldRules, newRules); err != nil {
|
if err := ns.podSelectors.provision(oldUID, oldPodSelectors, newPodSelectors); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := ns.ipBlocks.provision(oldUID, oldIPBlocks, newIPBlocks); err != nil {
|
||||||
return nil
|
return err
|
||||||
|
}
|
||||||
|
return ns.rules.provision(oldUID, oldRules, newRules)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) deleteNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
func (ns *ns) deleteNetworkPolicy(obj interface{}) error {
|
||||||
delete(ns.policies, obj.ObjectMeta.UID)
|
|
||||||
|
|
||||||
// Analyse network policy to free resources
|
// Analyse network policy to free resources
|
||||||
rules, nsSelectors, podSelectors, err := ns.analysePolicy(obj)
|
uid, rules, nsSelectors, podSelectors, ipBlocks, err := ns.analyse(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete(ns.policies, uid)
|
||||||
|
|
||||||
// Deprovision unused resources in dependency order
|
// Deprovision unused resources in dependency order
|
||||||
if err := ns.rules.deprovision(obj.ObjectMeta.UID, rules, nil); err != nil {
|
if err := ns.rules.deprovision(uid, rules, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ns.nsSelectors.deprovision(obj.ObjectMeta.UID, nsSelectors, nil); err != nil {
|
if err := ns.nsSelectors.deprovision(uid, nsSelectors, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ns.podSelectors.deprovision(obj.ObjectMeta.UID, podSelectors, nil); err != nil {
|
if err := ns.podSelectors.deprovision(uid, podSelectors, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.ipBlocks.deprovision(uid, ipBlocks, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func bypassRule(nsIpsetName ipset.Name, namespace string) []string {
|
func (ns *ns) updateDefaultAllowIPSetEntry(oldObj, newObj *coreapi.Pod, ipsetName ipset.Name) error {
|
||||||
return []string{"-m", "set", "--match-set", string(nsIpsetName), "dst", "-j", "ACCEPT", "-m", "comment", "--comment", "DefaultAllow isolation for namespace: " + namespace}
|
// Instead of iterating over all selectors we check whether old pod IP
|
||||||
|
// has been inserted into default-allow ipset to decide whether the IP
|
||||||
|
// in the ipset has to be updated.
|
||||||
|
if ns.ips.Exist(oldObj.ObjectMeta.UID, ipsetName, oldObj.Status.PodIP) {
|
||||||
|
|
||||||
|
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ipsetName, oldObj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ipsetName, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) ensureBypassRule(nsIpsetName ipset.Name) error {
|
func bypassRules(namespace string, ingress, egress ipset.Name) map[string][][]string {
|
||||||
common.Log.Debugf("ensuring rule for DefaultAllow in namespace: %s, set %s", ns.name, nsIpsetName)
|
return map[string][][]string{
|
||||||
return ns.ipt.Append(TableFilter, DefaultChain, bypassRule(ns.allPods.ipsetName, ns.name)...)
|
DefaultChain: {
|
||||||
|
{"-m", "set", "--match-set", string(ingress), "dst", "-j", "ACCEPT",
|
||||||
|
"-m", "comment", "--comment", "DefaultAllow ingress isolation for namespace: " + namespace},
|
||||||
|
},
|
||||||
|
EgressDefaultChain: {
|
||||||
|
{"-m", "set", "--match-set", string(egress), "src", "-j", EgressMarkChain,
|
||||||
|
"-m", "comment", "--comment", "DefaultAllow egress isolation for namespace: " + namespace},
|
||||||
|
{"-m", "set", "--match-set", string(egress), "src", "-j", "RETURN",
|
||||||
|
"-m", "comment", "--comment", "DefaultAllow egress isolation for namespace: " + namespace},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) deleteBypassRule(nsIpsetName ipset.Name) error {
|
func (ns *ns) ensureBypassRules() error {
|
||||||
common.Log.Debugf("removing default rule in namespace: %s, set %s", ns.name, nsIpsetName)
|
for chain, rules := range bypassRules(ns.name, ns.ingressDefaultAllowIPSet, ns.egressDefaultAllowIPSet) {
|
||||||
return ns.ipt.Delete(TableFilter, DefaultChain, bypassRule(ns.allPods.ipsetName, ns.name)...)
|
for _, rule := range rules {
|
||||||
|
common.Log.Debugf("adding rule for DefaultAllow in namespace: %s, chain: %s, %s", ns.name, chain, rule)
|
||||||
|
if err := ns.ipt.Append(TableFilter, chain, rule...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) deleteBypassRules() error {
|
||||||
|
for chain, rules := range bypassRules(ns.name, ns.ingressDefaultAllowIPSet, ns.egressDefaultAllowIPSet) {
|
||||||
|
for _, rule := range rules {
|
||||||
|
common.Log.Debugf("removing rule for DefaultAllow in namespace: %s, chain: %s, %s", ns.name, chain, rule)
|
||||||
|
if err := ns.ipt.Delete(TableFilter, chain, rule...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) addNamespace(obj *coreapi.Namespace) error {
|
func (ns *ns) addNamespace(obj *coreapi.Namespace) error {
|
||||||
ns.namespace = obj
|
ns.namespace = obj
|
||||||
|
|
||||||
// Insert a rule to bypass policies if namespace is DefaultAllow
|
// Insert a rule to bypass policies
|
||||||
if !isDefaultDeny(obj) {
|
if err := ns.ensureBypassRules(); err != nil {
|
||||||
if err := ns.ensureBypassRule(ns.allPods.ipsetName); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add namespace ipset to matching namespace selectors
|
// Add namespace ipset to matching namespace selectors
|
||||||
return ns.nsSelectors.addToMatching(obj.ObjectMeta.Labels, string(ns.allPods.ipsetName), namespaceComment(ns))
|
_, _, err := ns.nsSelectors.addToMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, string(ns.allPods.ipsetName), namespaceComment(ns))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
||||||
ns.namespace = newObj
|
ns.namespace = newObj
|
||||||
|
|
||||||
// Update bypass rule if ingress default has changed
|
|
||||||
oldDefaultDeny := isDefaultDeny(oldObj)
|
|
||||||
newDefaultDeny := isDefaultDeny(newObj)
|
|
||||||
|
|
||||||
if oldDefaultDeny != newDefaultDeny {
|
|
||||||
common.Log.Infof("namespace DefaultDeny changed from %t to %t", oldDefaultDeny, newDefaultDeny)
|
|
||||||
if oldDefaultDeny {
|
|
||||||
if err := ns.ensureBypassRule(ns.allPods.ipsetName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if newDefaultDeny {
|
|
||||||
if err := ns.deleteBypassRule(ns.allPods.ipsetName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-evaluate namespace selector membership if labels have changed
|
// Re-evaluate namespace selector membership if labels have changed
|
||||||
if !equals(oldObj.ObjectMeta.Labels, newObj.ObjectMeta.Labels) {
|
if !equals(oldObj.ObjectMeta.Labels, newObj.ObjectMeta.Labels) {
|
||||||
for _, selector := range ns.nsSelectors.entries {
|
for _, selector := range ns.nsSelectors.entries {
|
||||||
@@ -305,12 +485,12 @@ func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if oldMatch {
|
if oldMatch {
|
||||||
if err := selector.delEntry(string(ns.allPods.ipsetName)); err != nil {
|
if err := selector.delEntry(ns.namespace.ObjectMeta.UID, string(ns.allPods.ipsetName)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if newMatch {
|
if newMatch {
|
||||||
if err := selector.addEntry(string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
|
if err := selector.addEntry(ns.namespace.ObjectMeta.UID, string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -324,14 +504,24 @@ func (ns *ns) deleteNamespace(obj *coreapi.Namespace) error {
|
|||||||
ns.namespace = nil
|
ns.namespace = nil
|
||||||
|
|
||||||
// Remove bypass rule
|
// Remove bypass rule
|
||||||
if !isDefaultDeny(obj) {
|
if err := ns.deleteBypassRules(); err != nil {
|
||||||
if err := ns.deleteBypassRule(ns.allPods.ipsetName); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove namespace ipset from any matching namespace selectors
|
// Remove namespace ipset from any matching namespace selectors
|
||||||
return ns.nsSelectors.delFromMatching(obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
err := ns.nsSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ns.ips.Destroy(ns.ingressDefaultAllowIPSet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.ips.Destroy(ns.egressDefaultAllowIPSet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasIP(pod *coreapi.Pod) bool {
|
func hasIP(pod *coreapi.Pod) bool {
|
||||||
@@ -352,24 +542,6 @@ func equals(a, b map[string]string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDefaultDeny(namespace *coreapi.Namespace) bool {
|
|
||||||
nnpJSON, found := namespace.ObjectMeta.Annotations["net.beta.kubernetes.io/network-policy"]
|
|
||||||
if !found {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var nnp NamespaceNetworkPolicy
|
|
||||||
if err := json.Unmarshal([]byte(nnpJSON), &nnp); err != nil {
|
|
||||||
common.Log.Warn("Ignoring network policy annotation: unmarshal failed:", err)
|
|
||||||
// If we can't understand the annotation, behave as if it isn't present
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return nnp.Ingress != nil &&
|
|
||||||
nnp.Ingress.Isolation != nil &&
|
|
||||||
*(nnp.Ingress.Isolation) == DefaultDeny
|
|
||||||
}
|
|
||||||
|
|
||||||
func namespaceComment(namespace *ns) string {
|
func namespaceComment(namespace *ns) string {
|
||||||
return "namespace: " + namespace.name
|
return "namespace: " + namespace.name
|
||||||
}
|
}
|
||||||
@@ -377,3 +549,38 @@ func namespaceComment(namespace *ns) string {
|
|||||||
func podComment(pod *coreapi.Pod) string {
|
func podComment(pod *coreapi.Pod) string {
|
||||||
return fmt.Sprintf("namespace: %s, pod: %s", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name)
|
return fmt.Sprintf("namespace: %s, pod: %s", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ns *ns) defaultAllowIPSetName(pt policyType) ipset.Name {
|
||||||
|
ipset := ns.ingressDefaultAllowIPSet
|
||||||
|
if pt == policyTypeEgress {
|
||||||
|
ipset = ns.egressDefaultAllowIPSet
|
||||||
|
}
|
||||||
|
return ipset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) analyse(obj interface{}) (
|
||||||
|
uid types.UID,
|
||||||
|
rules map[string]*ruleSpec,
|
||||||
|
nsSelectors, podSelectors map[string]*selectorSpec,
|
||||||
|
ipBlocks map[string]*ipBlockSpec,
|
||||||
|
err error) {
|
||||||
|
|
||||||
|
switch p := obj.(type) {
|
||||||
|
case *extnapi.NetworkPolicy:
|
||||||
|
uid = p.ObjectMeta.UID
|
||||||
|
case *networkingv1.NetworkPolicy:
|
||||||
|
uid = p.ObjectMeta.UID
|
||||||
|
default:
|
||||||
|
err = errInvalidNetworkPolicyObjType
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ns.policies[uid] = obj
|
||||||
|
|
||||||
|
// Analyse policy, determine which rules and ipsets are required
|
||||||
|
rules, nsSelectors, podSelectors, ipBlocks, err = ns.analysePolicy(obj.(*networkingv1.NetworkPolicy))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
+65
-24
@@ -2,46 +2,80 @@ package npc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/client-go/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
"github.com/weaveworks/weave/common"
|
||||||
"github.com/weaveworks/weave/npc/iptables"
|
"github.com/weaveworks/weave/npc/iptables"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ruleSpec struct {
|
type ruleHost interface {
|
||||||
key string
|
// getRuleSpec returns source or destination specification and comment which
|
||||||
args []string
|
// are used in an iptables rule.
|
||||||
|
//
|
||||||
|
// If src=true then the rulespec is for source, otherwise - for destination.
|
||||||
|
getRuleSpec(src bool) ([]string, string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRuleSpec(proto *string, srcHost *selectorSpec, dstHost *selectorSpec, dstPort *string) *ruleSpec {
|
type ruleSpec struct {
|
||||||
|
key string
|
||||||
|
args []string
|
||||||
|
policyType policyType
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRuleSpec(policyType policyType, proto *string, srcHost, dstHost ruleHost, dstPort *string) *ruleSpec {
|
||||||
args := []string{}
|
args := []string{}
|
||||||
if proto != nil {
|
if proto != nil {
|
||||||
args = append(args, "-p", *proto)
|
args = append(args, "-p", *proto)
|
||||||
}
|
}
|
||||||
srcComment := "anywhere"
|
srcComment := "anywhere"
|
||||||
if srcHost != nil {
|
if srcHost != nil && !reflect.ValueOf(srcHost).IsNil() {
|
||||||
args = append(args, "-m", "set", "--match-set", string(srcHost.ipsetName), "src")
|
rule, comment := srcHost.getRuleSpec(true)
|
||||||
if srcHost.nsName != "" {
|
args = append(args, rule...)
|
||||||
srcComment = fmt.Sprintf("pods: namespace: %s, selector: %s", srcHost.nsName, srcHost.key)
|
srcComment = comment
|
||||||
} else {
|
|
||||||
srcComment = fmt.Sprintf("namespaces: selector: %s", srcHost.key)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dstComment := "anywhere"
|
dstComment := "anywhere"
|
||||||
if dstHost != nil {
|
if dstHost != nil && !reflect.ValueOf(dstHost).IsNil() {
|
||||||
args = append(args, "-m", "set", "--match-set", string(dstHost.ipsetName), "dst")
|
rule, comment := dstHost.getRuleSpec(false)
|
||||||
dstComment = fmt.Sprintf("pods: namespace: %s, selector: %s", dstHost.nsName, dstHost.key)
|
args = append(args, rule...)
|
||||||
|
dstComment = comment
|
||||||
}
|
}
|
||||||
if dstPort != nil {
|
if dstPort != nil {
|
||||||
args = append(args, "--dport", *dstPort)
|
args = append(args, "--dport", *dstPort)
|
||||||
}
|
}
|
||||||
args = append(args, "-j", "ACCEPT")
|
// NOTE: if you remove the comment bellow, then embed `policyType` into `key`.
|
||||||
args = append(args, "-m", "comment", "--comment", fmt.Sprintf("%s -> %s", srcComment, dstComment))
|
// Otherwise, the rule won't be provisioned if it exists for other policy type.
|
||||||
|
args = append(args, "-m", "comment", "--comment", fmt.Sprintf("%s -> %s (%s)", srcComment, dstComment, policyTypeStr(policyType)))
|
||||||
key := strings.Join(args, " ")
|
key := strings.Join(args, " ")
|
||||||
|
|
||||||
return &ruleSpec{key, args}
|
return &ruleSpec{key, args, policyType}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spec *ruleSpec) iptChain() string {
|
||||||
|
if spec.policyType == policyTypeEgress {
|
||||||
|
return EgressCustomChain
|
||||||
|
}
|
||||||
|
return IngressChain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spec *ruleSpec) iptRuleSpecs() [][]string {
|
||||||
|
if spec.policyType == policyTypeIngress {
|
||||||
|
rule := make([]string, len(spec.args))
|
||||||
|
copy(rule, spec.args)
|
||||||
|
rule = append(rule, "-j", "ACCEPT")
|
||||||
|
return [][]string{rule}
|
||||||
|
}
|
||||||
|
|
||||||
|
// policyTypeEgress
|
||||||
|
ruleMark := make([]string, len(spec.args))
|
||||||
|
copy(ruleMark, spec.args)
|
||||||
|
ruleMark = append(ruleMark, "-j", EgressMarkChain)
|
||||||
|
ruleReturn := make([]string, len(spec.args))
|
||||||
|
copy(ruleReturn, spec.args)
|
||||||
|
ruleReturn = append(ruleReturn, "-j", "RETURN")
|
||||||
|
return [][]string{ruleMark, ruleReturn}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ruleSet struct {
|
type ruleSet struct {
|
||||||
@@ -58,10 +92,14 @@ func (rs *ruleSet) deprovision(user types.UID, current, desired map[string]*rule
|
|||||||
if _, found := desired[key]; !found {
|
if _, found := desired[key]; !found {
|
||||||
delete(rs.users[key], user)
|
delete(rs.users[key], user)
|
||||||
if len(rs.users[key]) == 0 {
|
if len(rs.users[key]) == 0 {
|
||||||
common.Log.Infof("deleting rule: %v", spec.args)
|
chain := spec.iptChain()
|
||||||
if err := rs.ipt.Delete(TableFilter, IngressChain, spec.args...); err != nil {
|
for _, rule := range spec.iptRuleSpecs() {
|
||||||
return err
|
common.Log.Infof("deleting rule %v from %q chain", rule, chain)
|
||||||
|
if err := rs.ipt.Delete(TableFilter, chain, rule...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(rs.users, key)
|
delete(rs.users, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,9 +112,12 @@ func (rs *ruleSet) provision(user types.UID, current, desired map[string]*ruleSp
|
|||||||
for key, spec := range desired {
|
for key, spec := range desired {
|
||||||
if _, found := current[key]; !found {
|
if _, found := current[key]; !found {
|
||||||
if _, found := rs.users[key]; !found {
|
if _, found := rs.users[key]; !found {
|
||||||
common.Log.Infof("adding rule: %v", spec.args)
|
chain := spec.iptChain()
|
||||||
if err := rs.ipt.Append(TableFilter, IngressChain, spec.args...); err != nil {
|
for _, rule := range spec.iptRuleSpecs() {
|
||||||
return err
|
common.Log.Infof("adding rule %v to %q chain", rule, chain)
|
||||||
|
if err := rs.ipt.Append(TableFilter, chain, rule...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rs.users[key] = make(map[types.UID]struct{})
|
rs.users[key] = make(map[types.UID]struct{})
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ func TestRegressionHandleNil3095(t *testing.T) {
|
|||||||
// Test for handling nil values
|
// Test for handling nil values
|
||||||
// https://github.com/weaveworks/weave/issues/3095
|
// https://github.com/weaveworks/weave/issues/3095
|
||||||
|
|
||||||
rs := newRuleSpec(nil, nil, nil, nil)
|
rs := newRuleSpec(policyTypeIngress, nil, nil, nil, nil)
|
||||||
|
|
||||||
require.NotEqual(t, rs.key, "")
|
require.NotEqual(t, rs.key, "")
|
||||||
|
|
||||||
|
|||||||
+105
-33
@@ -1,32 +1,36 @@
|
|||||||
package npc
|
package npc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"k8s.io/client-go/pkg/api/unversioned"
|
"fmt"
|
||||||
"k8s.io/client-go/pkg/labels"
|
|
||||||
"k8s.io/client-go/pkg/types"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
"github.com/weaveworks/weave/common"
|
||||||
"github.com/weaveworks/weave/npc/ipset"
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
)
|
)
|
||||||
|
|
||||||
type selectorSpec struct {
|
type selectorSpec struct {
|
||||||
key string // string representation (for hash keying/equality comparison)
|
key string // string representation (for hash keying/equality comparison)
|
||||||
selector labels.Selector // k8s Selector object (for matching)
|
selector labels.Selector // k8s Selector object (for matching)
|
||||||
|
policyTypes []policyType // If non-empty, then selectorSpec is a target selector for given policyTypes.
|
||||||
|
|
||||||
ipsetType ipset.Type // type of ipset to provision
|
ipsetType ipset.Type // type of ipset to provision
|
||||||
ipsetName ipset.Name // generated ipset name
|
ipsetName ipset.Name // generated ipset name
|
||||||
nsName string // Namespace name
|
nsName string // Namespace name
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSelectorSpec(json *unversioned.LabelSelector, nsName string, ipsetType ipset.Type) (*selectorSpec, error) {
|
func newSelectorSpec(json *metav1.LabelSelector, policyType []policyType, nsName string, ipsetType ipset.Type) (*selectorSpec, error) {
|
||||||
selector, err := unversioned.LabelSelectorAsSelector(json)
|
selector, err := metav1.LabelSelectorAsSelector(json)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
key := selector.String()
|
key := selector.String()
|
||||||
return &selectorSpec{
|
return &selectorSpec{
|
||||||
key: key,
|
key: key,
|
||||||
selector: selector,
|
selector: selector,
|
||||||
|
policyTypes: policyType,
|
||||||
// We prefix the selector string with the namespace name when generating
|
// We prefix the selector string with the namespace name when generating
|
||||||
// the shortname because you can specify the same selector in multiple
|
// the shortname because you can specify the same selector in multiple
|
||||||
// namespaces - we need those to map to distinct ipsets
|
// namespaces - we need those to map to distinct ipsets
|
||||||
@@ -35,6 +39,23 @@ func newSelectorSpec(json *unversioned.LabelSelector, nsName string, ipsetType i
|
|||||||
nsName: nsName}, nil
|
nsName: nsName}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (spec *selectorSpec) getRuleSpec(src bool) ([]string, string) {
|
||||||
|
dir := "dst"
|
||||||
|
if src {
|
||||||
|
dir = "src"
|
||||||
|
}
|
||||||
|
rule := []string{"-m", "set", "--match-set", string(spec.ipsetName), dir}
|
||||||
|
|
||||||
|
comment := "anywhere"
|
||||||
|
if spec.nsName != "" {
|
||||||
|
comment = fmt.Sprintf("pods: namespace: %s, selector: %s", spec.nsName, spec.key)
|
||||||
|
} else {
|
||||||
|
comment = fmt.Sprintf("namespaces: selector: %s", spec.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rule, comment
|
||||||
|
}
|
||||||
|
|
||||||
type selector struct {
|
type selector struct {
|
||||||
ips ipset.Interface
|
ips ipset.Interface
|
||||||
spec *selectorSpec
|
spec *selectorSpec
|
||||||
@@ -44,35 +65,69 @@ func (s *selector) matches(labelMap map[string]string) bool {
|
|||||||
return s.spec.selector.Matches(labels.Set(labelMap))
|
return s.spec.selector.Matches(labels.Set(labelMap))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *selector) addEntry(entry string, comment string) error {
|
func (s *selector) addEntry(user types.UID, entry string, comment string) error {
|
||||||
return s.ips.AddEntry(s.spec.ipsetName, entry, comment)
|
return s.ips.AddEntry(user, s.spec.ipsetName, entry, comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *selector) delEntry(entry string) error {
|
func (s *selector) delEntry(user types.UID, entry string) error {
|
||||||
return s.ips.DelEntry(s.spec.ipsetName, entry)
|
return s.ips.DelEntry(user, s.spec.ipsetName, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
type selectorFn func(selector *selector) error
|
type selectorFn func(selector *selector) error
|
||||||
|
type selectorWithPolicyTypeFn func(selector *selector, policyType policyType) error
|
||||||
|
|
||||||
type selectorSet struct {
|
type selectorSet struct {
|
||||||
ips ipset.Interface
|
ips ipset.Interface
|
||||||
onNewSelector selectorFn
|
onNewSelector selectorFn
|
||||||
users map[string]map[types.UID]struct{} // list of users per selector
|
|
||||||
entries map[string]*selector
|
// invoked after target selector has been provisioned for the first time
|
||||||
|
onNewTargetSelector selectorWithPolicyTypeFn
|
||||||
|
// invoked after the last instance of target selector has been deprovisioned
|
||||||
|
onDestroyTargetSelector selectorWithPolicyTypeFn
|
||||||
|
|
||||||
|
users map[string]map[types.UID]struct{} // list of users per selector
|
||||||
|
entries map[string]*selector
|
||||||
|
|
||||||
|
// We need to keep track of target selector instances to be able to invoke
|
||||||
|
// onNewTargetSelector and onDestroyTargetSelector callbacks at the right time;
|
||||||
|
// selectorSpec.Key -> policyType -> count
|
||||||
|
targetSelectorsCount map[string]map[policyType]int
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSelectorSet(ips ipset.Interface, onNewSelector selectorFn) *selectorSet {
|
func newSelectorSet(ips ipset.Interface, onNewSelector selectorFn, onNewTargetSelector, onDestroyTargetSelector selectorWithPolicyTypeFn) *selectorSet {
|
||||||
return &selectorSet{
|
return &selectorSet{
|
||||||
ips: ips,
|
ips: ips,
|
||||||
onNewSelector: onNewSelector,
|
onNewSelector: onNewSelector,
|
||||||
users: make(map[string]map[types.UID]struct{}),
|
onNewTargetSelector: onNewTargetSelector,
|
||||||
entries: make(map[string]*selector)}
|
onDestroyTargetSelector: onDestroyTargetSelector,
|
||||||
|
users: make(map[string]map[types.UID]struct{}),
|
||||||
|
entries: make(map[string]*selector),
|
||||||
|
targetSelectorsCount: make(map[string]map[policyType]int)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *selectorSet) addToMatching(labelMap map[string]string, entry string, comment string) error {
|
func (ss *selectorSet) addToMatching(user types.UID, labelMap map[string]string, entry string, comment string) (bool, bool, error) {
|
||||||
|
foundIngress := false
|
||||||
|
foundEgress := false
|
||||||
for _, s := range ss.entries {
|
for _, s := range ss.entries {
|
||||||
if s.matches(labelMap) {
|
if s.matches(labelMap) {
|
||||||
if err := s.addEntry(entry, comment); err != nil {
|
if ss.targetSelectorExist(s, policyTypeIngress) {
|
||||||
|
foundIngress = true
|
||||||
|
}
|
||||||
|
if ss.targetSelectorExist(s, policyTypeEgress) {
|
||||||
|
foundEgress = true
|
||||||
|
}
|
||||||
|
if err := s.addEntry(user, entry, comment); err != nil {
|
||||||
|
return foundIngress, foundEgress, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return foundIngress, foundEgress, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *selectorSet) delFromMatching(user types.UID, labelMap map[string]string, entry string) error {
|
||||||
|
for _, s := range ss.entries {
|
||||||
|
if s.matches(labelMap) {
|
||||||
|
if err := s.delEntry(user, entry); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,15 +135,8 @@ func (ss *selectorSet) addToMatching(labelMap map[string]string, entry string, c
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *selectorSet) delFromMatching(labelMap map[string]string, entry string) error {
|
func (ss *selectorSet) targetSelectorExist(s *selector, policyType policyType) bool {
|
||||||
for _, s := range ss.entries {
|
return ss.targetSelectorsCount[s.spec.key][policyType] > 0
|
||||||
if s.matches(labelMap) {
|
|
||||||
if err := s.delEntry(entry); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*selectorSpec) error {
|
func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*selectorSpec) error {
|
||||||
@@ -100,9 +148,20 @@ func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*
|
|||||||
if err := ss.ips.Destroy(spec.ipsetName); err != nil {
|
if err := ss.ips.Destroy(spec.ipsetName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(ss.entries, key)
|
delete(ss.entries, key)
|
||||||
delete(ss.users, key)
|
delete(ss.users, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, policyType := range spec.policyTypes {
|
||||||
|
ss.targetSelectorsCount[key][policyType]--
|
||||||
|
if ss.targetSelectorsCount[key][policyType] == 0 {
|
||||||
|
if err := ss.onDestroyTargetSelector(&selector{ss.ips, spec}, policyType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO(brb) delete(...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -111,12 +170,13 @@ func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*
|
|||||||
func (ss *selectorSet) provision(user types.UID, current, desired map[string]*selectorSpec) error {
|
func (ss *selectorSet) provision(user types.UID, current, desired map[string]*selectorSpec) error {
|
||||||
for key, spec := range desired {
|
for key, spec := range desired {
|
||||||
if _, found := current[key]; !found {
|
if _, found := current[key]; !found {
|
||||||
|
selector := &selector{ss.ips, spec}
|
||||||
|
|
||||||
if _, found := ss.users[key]; !found {
|
if _, found := ss.users[key]; !found {
|
||||||
common.Log.Infof("creating ipset: %#v", spec)
|
common.Log.Infof("creating ipset: %#v", spec)
|
||||||
if err := ss.ips.Create(spec.ipsetName, spec.ipsetType); err != nil {
|
if err := ss.ips.Create(spec.ipsetName, spec.ipsetType); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
selector := &selector{ss.ips, spec}
|
|
||||||
if err := ss.onNewSelector(selector); err != nil {
|
if err := ss.onNewSelector(selector); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -124,6 +184,18 @@ func (ss *selectorSet) provision(user types.UID, current, desired map[string]*se
|
|||||||
ss.entries[key] = selector
|
ss.entries[key] = selector
|
||||||
}
|
}
|
||||||
ss.users[key][user] = struct{}{}
|
ss.users[key][user] = struct{}{}
|
||||||
|
|
||||||
|
for _, pt := range spec.policyTypes {
|
||||||
|
if _, found := ss.targetSelectorsCount[key]; !found {
|
||||||
|
ss.targetSelectorsCount[key] = make(map[policyType]int)
|
||||||
|
}
|
||||||
|
ss.targetSelectorsCount[key][pt]++
|
||||||
|
if ss.targetSelectorsCount[key][pt] == 1 {
|
||||||
|
if err := ss.onNewTargetSelector(selector, pt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package npc
|
||||||
|
|
||||||
|
type policyType byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
policyTypeIngress policyType = iota
|
||||||
|
policyTypeEgress
|
||||||
|
)
|
||||||
|
|
||||||
|
func policyTypeStr(policyType policyType) string {
|
||||||
|
if policyType == policyTypeEgress {
|
||||||
|
return "egress"
|
||||||
|
}
|
||||||
|
return "ingress"
|
||||||
|
}
|
||||||
@@ -110,6 +110,10 @@ func (i *Ipam) ReleaseAddress(poolID string, address net.IP) error {
|
|||||||
return i.weave.ReleaseIPsFor(address.String())
|
return i.weave.ReleaseIPsFor(address.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Ipam) IsBuiltIn() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Functions required by ipamapi "contract" but not actually used.
|
// Functions required by ipamapi "contract" but not actually used.
|
||||||
|
|
||||||
func (i *Ipam) DiscoverNew(discoverapi.DiscoveryType, interface{}) error {
|
func (i *Ipam) DiscoverNew(discoverapi.DiscoveryType, interface{}) error {
|
||||||
|
|||||||
+25
-25
@@ -6,7 +6,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ipam"
|
"github.com/containernetworking/cni/pkg/ipam"
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
@@ -100,12 +99,11 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
|||||||
return fmt.Errorf("unable to allocate IP address for bridge: %s", err)
|
return fmt.Errorf("unable to allocate IP address for bridge: %s", err)
|
||||||
}
|
}
|
||||||
bridgeCIDR := bridgeIPResult.IPs[0].Address
|
bridgeCIDR := bridgeIPResult.IPs[0].Address
|
||||||
if err := assignBridgeIP(conf.BrName, bridgeCIDR); err != nil {
|
|
||||||
return fmt.Errorf("unable to assign IP address to bridge: %s", err)
|
if err := c.weave.Expose(&bridgeCIDR); err != nil {
|
||||||
}
|
return fmt.Errorf("unable to expose bridge %q: %s", conf.BrName, err)
|
||||||
if err := weavenet.ExposeNAT(bridgeCIDR); err != nil {
|
|
||||||
return fmt.Errorf("unable to create NAT rules: %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bridgeIP = bridgeCIDR.IP
|
bridgeIP = bridgeCIDR.IP
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -119,6 +117,16 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
defer ns.Close()
|
defer ns.Close()
|
||||||
|
|
||||||
|
hostNs, err := netns.Get()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error accessing host network namespace: %s", err)
|
||||||
|
}
|
||||||
|
defer hostNs.Close()
|
||||||
|
|
||||||
|
if ns.Equal(hostNs) {
|
||||||
|
return fmt.Errorf("can not specify host network namespace as network namespace to which container should be added")
|
||||||
|
}
|
||||||
|
|
||||||
id := args.ContainerID
|
id := args.ContainerID
|
||||||
if len(id) < 5 {
|
if len(id) < 5 {
|
||||||
data := make([]byte, 5)
|
data := make([]byte, 5)
|
||||||
@@ -132,7 +140,7 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
|||||||
if err := weavenet.AttachContainer(args.Netns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&ip.Address}, false, conf.HairpinMode); err != nil {
|
if err := weavenet.AttachContainer(args.Netns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&ip.Address}, false, conf.HairpinMode); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := weavenet.WithNetNSLinkUnsafe(ns, args.IfName, func(link netlink.Link) error {
|
if err := weavenet.WithNetNSLink(ns, args.IfName, func(link netlink.Link) error {
|
||||||
return setupRoutes(link, args.IfName, ip.Address, ip.Gateway, result.Routes)
|
return setupRoutes(link, args.IfName, ip.Address, ip.Gateway, result.Routes)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("error setting up routes: %s", err)
|
return fmt.Errorf("error setting up routes: %s", err)
|
||||||
@@ -167,22 +175,6 @@ func setupRoutes(link netlink.Link, name string, ipnet net.IPNet, gw net.IP, rou
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func assignBridgeIP(bridgeName string, ipnet net.IPNet) error {
|
|
||||||
link, err := netlink.LinkByName(bridgeName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := netlink.AddrAdd(link, &netlink.Addr{IPNet: &ipnet}); err != nil {
|
|
||||||
// Treat as non-error if this address is already there
|
|
||||||
// - maybe another copy of this program just added it
|
|
||||||
if err == syscall.Errno(syscall.EEXIST) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("failed to add IP address to %q: %v", bridgeName, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// As of CNI 0.5 spec:
|
// As of CNI 0.5 spec:
|
||||||
// "Plugins should generally complete a DEL action without error even if some resources are missing"
|
// "Plugins should generally complete a DEL action without error even if some resources are missing"
|
||||||
// this method should therefore return nil in most, if not all, cases.
|
// this method should therefore return nil in most, if not all, cases.
|
||||||
@@ -195,7 +187,13 @@ func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
// As of CNI 0.3 spec, runtimes can send blank if they just want the address deallocated
|
// As of CNI 0.3 spec, runtimes can send blank if they just want the address deallocated
|
||||||
if args.Netns != "" {
|
if args.Netns != "" {
|
||||||
if _, err = weavenet.WithNetNS(args.Netns, "del-iface", args.IfName); err != nil {
|
if err = weavenet.WithNetNSByPath(args.Netns, func() error {
|
||||||
|
link, err := netlink.LinkByName(args.IfName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return netlink.LinkDel(link)
|
||||||
|
}); err != nil {
|
||||||
// We log the error and carry on instead of returning nil, as there may still be resources to free in IPAM.
|
// We log the error and carry on instead of returning nil, as there may still be resources to free in IPAM.
|
||||||
logOnStderr(fmt.Errorf("error removing interface %q: %s", args.IfName, err))
|
logOnStderr(fmt.Errorf("error removing interface %q: %s", args.IfName, err))
|
||||||
}
|
}
|
||||||
@@ -207,7 +205,9 @@ func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
|
|||||||
} else {
|
} else {
|
||||||
err = ipam.ExecDel(conf.IPAM.Type, args.StdinData)
|
err = ipam.ExecDel(conf.IPAM.Type, args.StdinData)
|
||||||
}
|
}
|
||||||
logOnStderr(fmt.Errorf("unable to release IP address: %s", err))
|
if err != nil {
|
||||||
|
logOnStderr(fmt.Errorf("unable to release IP address: %s", err))
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.Cr
|
|||||||
|
|
||||||
// create veths. note we assume endpoint IDs are unique in the first 9 chars
|
// create veths. note we assume endpoint IDs are unique in the first 9 chars
|
||||||
name, peerName := vethPair(create.EndpointID)
|
name, peerName := vethPair(create.EndpointID)
|
||||||
if _, err := weavenet.CreateAndAttachVeth(name, peerName, weavenet.WeaveBridgeName, 0, false, nil); err != nil {
|
if _, err := weavenet.CreateAndAttachVeth(name, peerName, weavenet.WeaveBridgeName, 0, false, true, nil); err != nil {
|
||||||
return nil, driver.error("JoinEndpoint", "%s", err)
|
return nil, driver.error("JoinEndpoint", "%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,6 @@ import (
|
|||||||
weavenet "github.com/weaveworks/weave/net"
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
WeaveDomain = "weave.local"
|
|
||||||
)
|
|
||||||
|
|
||||||
type watcher struct {
|
type watcher struct {
|
||||||
client *docker.Client
|
client *docker.Client
|
||||||
weave *weaveapi.Client
|
weave *weaveapi.Client
|
||||||
@@ -53,7 +49,9 @@ func (w *watcher) ContainerStarted(id string) {
|
|||||||
rootDir = "/host"
|
rootDir = "/host"
|
||||||
}
|
}
|
||||||
netNSPath := weavenet.NSPathByPidWithRoot(rootDir, info.State.Pid)
|
netNSPath := weavenet.NSPathByPidWithRoot(rootDir, info.State.Pid)
|
||||||
if _, err := weavenet.WithNetNS(netNSPath, "configure-arp", weavenet.VethName, rootDir); err != nil {
|
if err := weavenet.WithNetNSByPath(netNSPath, func() error {
|
||||||
|
return weavenet.ConfigureARP(weavenet.VethName, rootDir)
|
||||||
|
}); err != nil {
|
||||||
w.driver.warn("ContainerStarted", "unable to configure interfaces: %s", err)
|
w.driver.warn("ContainerStarted", "unable to configure interfaces: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"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 {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
addresses := make([]string, 0, len(nodeList.Items))
|
|
||||||
for _, peer := range nodeList.Items {
|
|
||||||
var internalIP, externalIP string
|
|
||||||
for _, addr := range peer.Status.Addresses {
|
|
||||||
// Check it's a valid ipv4 address
|
|
||||||
ip := net.ParseIP(addr.Address)
|
|
||||||
if ip == nil || ip.To4() == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if addr.Type == "InternalIP" {
|
|
||||||
internalIP = ip.To4().String()
|
|
||||||
} else if addr.Type == "ExternalIP" {
|
|
||||||
externalIP = ip.To4().String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback for cases where a Node has an ExternalIP but no InternalIP
|
|
||||||
if internalIP != "" {
|
|
||||||
addresses = append(addresses, internalIP)
|
|
||||||
} else if externalIP != "" {
|
|
||||||
addresses = append(addresses, externalIP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
In order to keep track of active weave peers, we use annotations on the Kubernetes cluster.
|
||||||
|
|
||||||
|
Kubernetes uses etcd to distribute and synchronise these annotations so we don't have to.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
kubeErrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
api "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
wait "k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
kubernetes "k8s.io/client-go/kubernetes"
|
||||||
|
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type configMapAnnotations struct {
|
||||||
|
ConfigMapName string
|
||||||
|
Namespace string
|
||||||
|
Client corev1client.ConfigMapsGetter
|
||||||
|
cm *v1.ConfigMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConfigMapAnnotations(ns string, configMapName string, c kubernetes.Interface) *configMapAnnotations {
|
||||||
|
return &configMapAnnotations{
|
||||||
|
Namespace: ns,
|
||||||
|
ConfigMapName: configMapName,
|
||||||
|
Client: c.CoreV1(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
retryPeriod = time.Second * 2
|
||||||
|
jitterFactor = 1.0
|
||||||
|
|
||||||
|
// Prefix all our annotation keys with this string so they don't clash with anyone else's
|
||||||
|
KubePeersPrefix = "kube-peers.weave.works/"
|
||||||
|
// KubePeersAnnotationKey is the default annotation key
|
||||||
|
KubePeersAnnotationKey = KubePeersPrefix + "peers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (cml *configMapAnnotations) Init() error {
|
||||||
|
for {
|
||||||
|
// Since it's potentially racy to GET, then CREATE if not found, we wrap in a check loop
|
||||||
|
// so that if the configmap is created after our GET but before or CREATE, we'll gracefully
|
||||||
|
// re-try to get the configmap.
|
||||||
|
var err error
|
||||||
|
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Get(cml.ConfigMapName, api.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
if !kubeErrors.IsNotFound(err) {
|
||||||
|
return errors.Wrapf(err, "Unable to fetch ConfigMap %s/%s", cml.Namespace, cml.ConfigMapName)
|
||||||
|
}
|
||||||
|
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Create(&v1.ConfigMap{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: cml.ConfigMapName,
|
||||||
|
Namespace: cml.Namespace,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if kubeErrors.IsAlreadyExists(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return errors.Wrapf(err, "Unable to create ConfigMap %s/%s", cml.Namespace, cml.ConfigMapName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if cml.cm.Annotations == nil {
|
||||||
|
cml.cm.Annotations = make(map[string]string)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up a string so it meets the Kubernetes requiremements for Annotation keys:
|
||||||
|
// name part must consist of alphanumeric characters, '-', '_' or '.', and must
|
||||||
|
// start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc')
|
||||||
|
func cleanKey(key string) string {
|
||||||
|
buf := []byte(key)
|
||||||
|
for i, c := range buf {
|
||||||
|
if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '/' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buf[i] = '_'
|
||||||
|
}
|
||||||
|
return string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cml *configMapAnnotations) GetAnnotation(key string) (string, bool) {
|
||||||
|
value, ok := cml.cm.Annotations[cleanKey(key)]
|
||||||
|
return value, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cml *configMapAnnotations) UpdateAnnotation(key, value string) (err error) {
|
||||||
|
if cml.cm == nil {
|
||||||
|
return errors.New("endpoint not initialized, call Init first")
|
||||||
|
}
|
||||||
|
// speculatively change the state, then replace with whatever comes back
|
||||||
|
// from Update(), which will be the latest on the server whatever happened
|
||||||
|
cml.cm.Annotations[cleanKey(key)] = value
|
||||||
|
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cml *configMapAnnotations) RemoveAnnotation(key string) (err error) {
|
||||||
|
if cml.cm == nil {
|
||||||
|
return errors.New("endpoint not initialized, call Init first")
|
||||||
|
}
|
||||||
|
// speculatively change the state, then replace with whatever comes back
|
||||||
|
// from Update(), which will be the latest on the server whatever happened
|
||||||
|
delete(cml.cm.Annotations, cleanKey(key))
|
||||||
|
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cml *configMapAnnotations) RemoveAnnotationsWithValue(valueToRemove string) (err error) {
|
||||||
|
if cml.cm == nil {
|
||||||
|
return errors.New("endpoint not initialized, call Init first")
|
||||||
|
}
|
||||||
|
// speculatively change the state, then replace with whatever comes back
|
||||||
|
// from Update(), which will be the latest on the server whatever happened
|
||||||
|
for key, value := range cml.cm.Annotations {
|
||||||
|
if value == valueToRemove {
|
||||||
|
delete(cml.cm.Annotations, key) // don't need to clean this key as it came from the map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop with jitter, fetching the cml data and calling f() until it
|
||||||
|
// doesn't get an optimistic locking conflict.
|
||||||
|
// If it succeeds or gets any other kind of error, stop the loop.
|
||||||
|
func (cml *configMapAnnotations) LoopUpdate(f func() error) error {
|
||||||
|
stop := make(chan struct{})
|
||||||
|
var err error
|
||||||
|
wait.JitterUntil(func() {
|
||||||
|
if err = cml.Init(); err != nil {
|
||||||
|
close(stop)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = f()
|
||||||
|
if err != nil && kubeErrors.IsConflict(err) {
|
||||||
|
log.Printf("Optimistic locking conflict: trying again: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
close(stop)
|
||||||
|
}, retryPeriod, jitterFactor, true, stop)
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Adapted from k8s.io/client-go/kubernetes/fake to use a modified ObjectTracker
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
|
"k8s.io/client-go/testing"
|
||||||
|
|
||||||
|
weavetracker "github.com/weaveworks/weave/testing/kubernetes/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var scheme = runtime.NewScheme()
|
||||||
|
var codecs = serializer.NewCodecFactory(scheme)
|
||||||
|
|
||||||
|
func NewSimpleClientset(objects ...runtime.Object) *fake.Clientset {
|
||||||
|
o := weavetracker.NewObjectTracker(scheme, codecs.UniversalDecoder())
|
||||||
|
for _, obj := range objects {
|
||||||
|
if err := o.Add(obj); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := fake.Clientset{}
|
||||||
|
ret.AddReactor("*", "*", weavetracker.ObjectReaction(o))
|
||||||
|
ret.AddWatchReactor("*", testing.DefaultWatchReactor(watch.NewFake(), nil))
|
||||||
|
|
||||||
|
return &ret
|
||||||
|
}
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
/*
|
||||||
|
Package main deals with weave-net peers on the cluster.
|
||||||
|
|
||||||
|
This involves peer management, such as getting the latest peers or removing defunct peers from the cluster
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
api "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
|
||||||
|
weaveapi "github.com/weaveworks/weave/api"
|
||||||
|
"github.com/weaveworks/weave/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type nodeInfo struct {
|
||||||
|
name string
|
||||||
|
addr string
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the IP addresses of all nodes in the cluster
|
||||||
|
func getKubePeers(c kubernetes.Interface, includeWithNoIPAddr bool) ([]nodeInfo, error) {
|
||||||
|
nodeList, err := c.CoreV1().Nodes().List(api.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
addresses := make([]nodeInfo, 0, len(nodeList.Items))
|
||||||
|
for _, peer := range nodeList.Items {
|
||||||
|
var internalIP, externalIP string
|
||||||
|
for _, addr := range peer.Status.Addresses {
|
||||||
|
// Check it's a valid ipv4 address
|
||||||
|
ip := net.ParseIP(addr.Address)
|
||||||
|
if ip == nil || ip.To4() == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if addr.Type == "InternalIP" {
|
||||||
|
internalIP = ip.To4().String()
|
||||||
|
} else if addr.Type == "ExternalIP" {
|
||||||
|
externalIP = ip.To4().String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback for cases where a Node has an ExternalIP but no InternalIP
|
||||||
|
if internalIP != "" {
|
||||||
|
addresses = append(addresses, nodeInfo{name: peer.Name, addr: internalIP})
|
||||||
|
} else if externalIP != "" {
|
||||||
|
addresses = append(addresses, nodeInfo{name: peer.Name, addr: externalIP})
|
||||||
|
} else if includeWithNoIPAddr {
|
||||||
|
addresses = append(addresses, nodeInfo{name: peer.Name, addr: ""})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addresses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// (minimal, incomplete) interface so weaver can be mocked for testing.
|
||||||
|
type weaveClient interface {
|
||||||
|
RmPeer(peerName string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each of those peers that is no longer listed as a node by
|
||||||
|
// Kubernetes, remove it from Weave IPAM
|
||||||
|
func reclaimRemovedPeers(kube kubernetes.Interface, weave weaveClient, cml *configMapAnnotations, myPeerName, myNodeName string) error {
|
||||||
|
for loopsWhenNothingChanged := 0; loopsWhenNothingChanged < 3; loopsWhenNothingChanged++ {
|
||||||
|
if err := cml.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 1. Compare peers stored in the peerList against all peers reported by k8s now.
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nodes, err := getKubePeers(kube, true)
|
||||||
|
nodeSet := make(map[string]struct{}, len(nodes))
|
||||||
|
for _, node := range nodes {
|
||||||
|
nodeSet[node.name] = struct{}{}
|
||||||
|
}
|
||||||
|
peerMap := make(map[string]peerInfo, len(storedPeerList.Peers))
|
||||||
|
for _, peer := range storedPeerList.Peers {
|
||||||
|
peerMap[peer.PeerName] = peer
|
||||||
|
}
|
||||||
|
// remove entries from the peer map that are current nodes
|
||||||
|
for key, peer := range peerMap {
|
||||||
|
if _, found := nodeSet[peer.NodeName]; found {
|
||||||
|
// unless they have a duplicate of my NodeName but are not me
|
||||||
|
if peer.NodeName == myNodeName && peer.PeerName != myPeerName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
delete(peerMap, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// so the remainder is everything we want to clean up
|
||||||
|
common.Log.Debugln("[kube-peers] Nodes that have disappeared:", peerMap)
|
||||||
|
if len(peerMap) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// 2. Loop for each X in the first set and not in the second - we wish to remove X from our data structures
|
||||||
|
for _, peer := range peerMap {
|
||||||
|
if peer.PeerName == myPeerName { // Don't remove myself.
|
||||||
|
common.Log.Warnln("[kube-peers] not removing myself", peer)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
changed, err := reclaimPeer(weave, cml, peer.PeerName, myPeerName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if changed {
|
||||||
|
loopsWhenNothingChanged = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. Go back to step 1 until there is no difference between the two sets
|
||||||
|
// (or we hit the counter that says we've been round the loop 3 times and nothing happened)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to reclaim the IP addresses owned by peerName, using the
|
||||||
|
// Kubernetes api-server as a point of consensus so that only one peer
|
||||||
|
// actions the reclaim.
|
||||||
|
// Return a bool to show whether we attempted to change anything,
|
||||||
|
// and an error if something went wrong.
|
||||||
|
func reclaimPeer(weave weaveClient, cml *configMapAnnotations, peerName string, myPeerName string) (changed bool, err error) {
|
||||||
|
common.Log.Debugln("[kube-peers] Preparing to remove disappeared peer", peerName)
|
||||||
|
okToRemove := false
|
||||||
|
// 3. Check if there is an existing annotation with key X
|
||||||
|
if existingAnnotation, found := cml.GetAnnotation(KubePeersPrefix + peerName); found {
|
||||||
|
common.Log.Debugln("[kube-peers] Existing annotation", existingAnnotation)
|
||||||
|
// 4. If annotation already contains my identity, ok;
|
||||||
|
if existingAnnotation == myPeerName {
|
||||||
|
okToRemove = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 5. If non-existent, write an annotation with key X and contents "my identity"
|
||||||
|
common.Log.Debugln("[kube-peers] Noting I plan to remove ", peerName)
|
||||||
|
if err := cml.UpdateAnnotation(KubePeersPrefix+peerName, myPeerName); err == nil {
|
||||||
|
okToRemove = true
|
||||||
|
} else {
|
||||||
|
common.Log.Errorln("[kube-peers] error from UpdateAnnotation: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !okToRemove {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
// 6. If step 4 or 5 succeeded, rmpeer X
|
||||||
|
result, err := weave.RmPeer(peerName)
|
||||||
|
common.Log.Infof("[kube-peers] rmpeer of %s: %s", peerName, result)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
err = cml.LoopUpdate(func() error {
|
||||||
|
// 7aa. Remove any annotations Z* that have contents X
|
||||||
|
if err := cml.RemoveAnnotationsWithValue(peerName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 7a. Remove X from peerList
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
storedPeerList.remove(peerName)
|
||||||
|
if err := cml.UpdatePeerList(*storedPeerList); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 7b. Remove annotation with key X
|
||||||
|
return cml.RemoveAnnotation(KubePeersPrefix + peerName)
|
||||||
|
})
|
||||||
|
// 8. If step 5 failed due to optimistic lock conflict, stop: someone else is handling X
|
||||||
|
|
||||||
|
// Step 3-5 is to protect against two simultaneous rmpeers of X
|
||||||
|
// Step 4 is to pick up again after a restart between step 5 and step 7b
|
||||||
|
// If the peer doing the reclaim disappears between steps 5 and 7a, then someone will clean it up in step 7aa
|
||||||
|
// If peer doing the reclaim disappears forever between 7a and 7b then we get a dangling annotation
|
||||||
|
// This should be sufficiently rare that we don't care.
|
||||||
|
|
||||||
|
// Question: Should we narrow step 2 by checking against Weave Net IPAM?
|
||||||
|
// i.e. If peer X owns any address space and is marked unreachable, we want to rmpeer X
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
justReclaim bool
|
||||||
|
justCheck bool
|
||||||
|
justSetNodeStatus bool
|
||||||
|
peerName string
|
||||||
|
nodeName string
|
||||||
|
logLevel string
|
||||||
|
)
|
||||||
|
flag.BoolVar(&justReclaim, "reclaim", false, "reclaim IP space from dead peers")
|
||||||
|
flag.BoolVar(&justCheck, "check-peer-new", false, "return success if peer name is not stored in annotation")
|
||||||
|
flag.BoolVar(&justSetNodeStatus, "set-node-status", false, "set NodeNetworkUnavailable to false")
|
||||||
|
flag.StringVar(&peerName, "peer-name", "unknown", "name of this Weave Net peer")
|
||||||
|
flag.StringVar(&nodeName, "node-name", "unknown", "name of this Kubernetes node")
|
||||||
|
flag.StringVar(&logLevel, "log-level", "info", "logging level (debug, info, warning, error)")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
common.SetLogLevel(logLevel)
|
||||||
|
config, err := rest.InClusterConfig()
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Could not get cluster config: %v", err)
|
||||||
|
}
|
||||||
|
c, err := kubernetes.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Could not make Kubernetes connection: %v", err)
|
||||||
|
}
|
||||||
|
if justCheck {
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
exists, err := checkIamInPeerList(cml, c, peerName)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Could not check peer list: %v", err)
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
os.Exit(9)
|
||||||
|
} else {
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if justSetNodeStatus {
|
||||||
|
err := setNodeNetworkUnavailableFalse(c, nodeName)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] could not set node status: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Could not get peers: %v", err)
|
||||||
|
}
|
||||||
|
if justReclaim {
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
|
||||||
|
list, err := addMyselfToPeerList(cml, c, peerName, nodeName)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Could not update peer list: %v", err)
|
||||||
|
}
|
||||||
|
common.Log.Infoln("[kube-peers] Added myself to peer list", list)
|
||||||
|
|
||||||
|
weave := weaveapi.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), common.Log)
|
||||||
|
err = reclaimRemovedPeers(c, weave, cml, peerName, nodeName)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Error while reclaiming space: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
peers, err := getKubePeers(c, false)
|
||||||
|
for _, node := range peers {
|
||||||
|
fmt.Println(node.addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// based on k8s.io/kubernetes/pkg/controller/nodeipam/ipam/adapter.go
|
||||||
|
func setNodeNetworkUnavailableFalse(c kubernetes.Interface, nodeName string) error {
|
||||||
|
condition := v1.NodeCondition{
|
||||||
|
Type: v1.NodeNetworkUnavailable,
|
||||||
|
Status: v1.ConditionFalse,
|
||||||
|
Reason: "WeaveIsUp",
|
||||||
|
Message: "Weave pod has set this",
|
||||||
|
LastTransitionTime: metav1.Now(),
|
||||||
|
LastHeartbeatTime: metav1.Now(),
|
||||||
|
}
|
||||||
|
raw, err := json.Marshal(&[]v1.NodeCondition{condition})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
patch := []byte(fmt.Sprintf(`{"status":{"conditions":%s}}`, raw))
|
||||||
|
_, err = c.CoreV1().Nodes().PatchStatus(nodeName, patch)
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
This module deals with operations on the peerlist backed by Kubernetes' annotation mechanism.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
configMapName = "weave-net"
|
||||||
|
configMapNamespace = "kube-system"
|
||||||
|
)
|
||||||
|
|
||||||
|
type peerList struct {
|
||||||
|
Peers []peerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type peerInfo struct {
|
||||||
|
PeerName string // Weave internal unique ID
|
||||||
|
NodeName string // Kubernetes node name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pl peerList) contains(peerName string) bool {
|
||||||
|
for _, peer := range pl.Peers {
|
||||||
|
if peer.PeerName == peerName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pl *peerList) add(peerName string, name string) {
|
||||||
|
pl.Peers = append(pl.Peers, peerInfo{PeerName: peerName, NodeName: name})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pl *peerList) remove(peerNameToRemove string) {
|
||||||
|
for i := 0; i < len(pl.Peers); {
|
||||||
|
if pl.Peers[i].PeerName == peerNameToRemove {
|
||||||
|
pl.Peers = append(pl.Peers[:i], pl.Peers[i+1:]...)
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cml *configMapAnnotations) GetPeerList() (*peerList, error) {
|
||||||
|
var record peerList
|
||||||
|
if cml.cm == nil {
|
||||||
|
return nil, errors.New("endpoint not initialized, call Init first")
|
||||||
|
}
|
||||||
|
if recordBytes, found := cml.cm.Annotations[KubePeersAnnotationKey]; found {
|
||||||
|
if err := json.Unmarshal([]byte(recordBytes), &record); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &record, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cml *configMapAnnotations) UpdatePeerList(list peerList) error {
|
||||||
|
recordBytes, err := json.Marshal(list)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return cml.UpdateAnnotation(KubePeersAnnotationKey, string(recordBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the list of all peers that have gone through this code path
|
||||||
|
func addMyselfToPeerList(cml *configMapAnnotations, c kubernetes.Interface, peerName, name string) (*peerList, error) {
|
||||||
|
var list *peerList
|
||||||
|
err := cml.LoopUpdate(func() error {
|
||||||
|
var err error
|
||||||
|
list, err = cml.GetPeerList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !list.contains(peerName) {
|
||||||
|
list.add(peerName, name)
|
||||||
|
err = cml.UpdatePeerList(*list)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkIamInPeerList(cml *configMapAnnotations, c kubernetes.Interface, peerName string) (bool, error) {
|
||||||
|
if err := cml.Init(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
list, err := cml.GetPeerList()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
common.Log.Debugf("[kube-peers] Checking peer %q against list %v", peerName, list)
|
||||||
|
return list.contains(peerName), nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
peerName1 = "01:00:00:00:00:00"
|
||||||
|
peerName2 = "02:00:00:00:00:00"
|
||||||
|
peerName3 = "03:00:00:00:00:00"
|
||||||
|
nodeName1 = "fake-node-1"
|
||||||
|
nodeName2 = "fake-node-2"
|
||||||
|
|
||||||
|
testIters = 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPeerListBasic(t *testing.T) {
|
||||||
|
c := NewSimpleClientset()
|
||||||
|
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
err := cml.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
list1, err := addMyselfToPeerList(cml, c, peerName1, nodeName1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(list1.Peers))
|
||||||
|
list2, err := addMyselfToPeerList(cml, c, peerName2, nodeName2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(list2.Peers))
|
||||||
|
|
||||||
|
check1, err := checkIamInPeerList(cml, c, peerName1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, true, check1)
|
||||||
|
check3, err := checkIamInPeerList(cml, c, peerName3)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, false, check3)
|
||||||
|
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
storedPeerList.remove(peerName1)
|
||||||
|
require.Equal(t, 1, len(storedPeerList.Peers))
|
||||||
|
err = cml.UpdatePeerList(*storedPeerList)
|
||||||
|
require.NoError(t, err)
|
||||||
|
check1, err = checkIamInPeerList(cml, c, peerName1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, false, check1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run function _f_ _iterations_ times, in _concurrency_
|
||||||
|
// number of goroutines
|
||||||
|
func doConcurrentIterations(iterations, concurrency int, f func(int)) {
|
||||||
|
iterationsPerThread := iterations / concurrency
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
for i := 0; i < concurrency; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(j int) {
|
||||||
|
defer wg.Done()
|
||||||
|
for k := 0; k < iterationsPerThread; k++ {
|
||||||
|
f((j * iterationsPerThread) + k)
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeerListFuzz(t *testing.T) {
|
||||||
|
const (
|
||||||
|
testIters = 1000
|
||||||
|
maxNodes = 100
|
||||||
|
concurrency = 5
|
||||||
|
)
|
||||||
|
peerName := func(i int) string { return fmt.Sprintf("%02d:00:00:02:00:00", i) }
|
||||||
|
nodeName := func(i int) string { return fmt.Sprintf("node%d", i) }
|
||||||
|
|
||||||
|
stateLock := sync.Mutex{}
|
||||||
|
nodes := []int{}
|
||||||
|
nodeMap := map[int]bool{} // true if an operation is pending
|
||||||
|
|
||||||
|
c := NewSimpleClientset()
|
||||||
|
|
||||||
|
addNode := func() {
|
||||||
|
i := rand.Intn(maxNodes)
|
||||||
|
// add to test state first, then to Kubernetes
|
||||||
|
stateLock.Lock()
|
||||||
|
pending, found := nodeMap[i]
|
||||||
|
if !found {
|
||||||
|
nodes = append(nodes, i)
|
||||||
|
}
|
||||||
|
nodeMap[i] = true
|
||||||
|
stateLock.Unlock()
|
||||||
|
if pending { // someone else is adding or deleting this node
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
err := cml.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = addMyselfToPeerList(cml, c, peerName(i), nodeName(i))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
stateLock.Lock()
|
||||||
|
nodeMap[i] = false
|
||||||
|
stateLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
removeNode := func() {
|
||||||
|
stateLock.Lock()
|
||||||
|
if len(nodes) == 0 { // can't remove if there aren't any nodes
|
||||||
|
stateLock.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pos := rand.Intn(len(nodes))
|
||||||
|
i := nodes[pos]
|
||||||
|
if pending, found := nodeMap[i]; found {
|
||||||
|
if pending {
|
||||||
|
stateLock.Unlock()
|
||||||
|
return // someone else is adding or deleting this node
|
||||||
|
}
|
||||||
|
nodeMap[i] = true
|
||||||
|
} else {
|
||||||
|
t.Errorf("missing entry from node map: %d", i)
|
||||||
|
}
|
||||||
|
stateLock.Unlock()
|
||||||
|
|
||||||
|
// remove from Kubernetes first, then from test state
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
require.NoError(t, cml.Init())
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
require.NoError(t, err)
|
||||||
|
found := storedPeerList.contains(peerName(i))
|
||||||
|
require.True(t, found, "peer %d not found in stored list", i)
|
||||||
|
|
||||||
|
_, err = reclaimPeer(mockWeave{}, cml, peerName(i), fmt.Sprintf("deleter-%d", i))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
storedPeerList, err = cml.GetPeerList()
|
||||||
|
require.NoError(t, err)
|
||||||
|
stateLock.Lock()
|
||||||
|
delete(nodeMap, i)
|
||||||
|
for pos = 0; pos < len(nodes); pos++ {
|
||||||
|
if nodes[pos] == i {
|
||||||
|
// Remove item from list by swapping it with last and reducing slice length by 1
|
||||||
|
nodes[pos] = nodes[len(nodes)-1]
|
||||||
|
nodes = nodes[:len(nodes)-1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stateLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
doConcurrentIterations(testIters, concurrency, func(iteration int) {
|
||||||
|
r := rand.Float32()
|
||||||
|
switch {
|
||||||
|
case 0.0 <= r && r < 0.4:
|
||||||
|
addNode()
|
||||||
|
|
||||||
|
case (0.4 <= r && r < 0.8):
|
||||||
|
removeNode()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
err := cml.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, len(storedPeerList.Peers), len(nodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockWeave struct{}
|
||||||
|
|
||||||
|
func (mockWeave) RmPeer(peerName string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
@@ -50,6 +50,14 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"env": [
|
"env": [
|
||||||
|
{
|
||||||
|
"description": "Log level",
|
||||||
|
"name": "LOG_LEVEL",
|
||||||
|
"settable": [
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Extra args to `weaver` and `plugin`",
|
"description": "Extra args to `weaver` and `plugin`",
|
||||||
"name": "EXTRA_ARGS",
|
"name": "EXTRA_ARGS",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
|
|||||||
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
||||||
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
|
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
|
||||||
HOST_ROOT=${HOST_ROOT:-/host}
|
HOST_ROOT=${HOST_ROOT:-/host}
|
||||||
|
LOG_LEVEL=${LOG_LEVEL:-info}
|
||||||
WEAVE_DIR="/host/var/lib/weave"
|
WEAVE_DIR="/host/var/lib/weave"
|
||||||
|
|
||||||
mkdir $WEAVE_DIR || true
|
mkdir $WEAVE_DIR || true
|
||||||
@@ -44,7 +45,7 @@ exec /home/weave/weaver $EXTRA_ARGS --port=6783 $(router_bridge_opts) \
|
|||||||
--no-dns \
|
--no-dns \
|
||||||
--ipalloc-range=$IPALLOC_RANGE \
|
--ipalloc-range=$IPALLOC_RANGE \
|
||||||
--nickname "$(hostname)" \
|
--nickname "$(hostname)" \
|
||||||
--log-level=debug \
|
--log-level=$LOG_LEVEL \
|
||||||
--db-prefix="$WEAVE_DIR/weave" \
|
--db-prefix="$WEAVE_DIR/weave" \
|
||||||
--plugin-v2 \
|
--plugin-v2 \
|
||||||
$(multicast_opt) \
|
$(multicast_opt) \
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
FROM DOCKERHUB_USER/weaveARCH_EXT
|
FROM DOCKERHUB_USER/weaveARCH_EXT
|
||||||
ADD ./launch.sh ./kube-peers /home/weave/
|
|
||||||
|
# These labels are pretty static, and can therefore be added early on:
|
||||||
|
LABEL maintainer="Weaveworks <help@weave.works>" \
|
||||||
|
org.opencontainers.image.title="weave-kube" \
|
||||||
|
org.opencontainers.image.source="https://github.com/weaveworks/weave" \
|
||||||
|
org.opencontainers.image.vendor="Weaveworks"
|
||||||
|
|
||||||
|
ADD ./launch.sh ./kube-utils /home/weave/
|
||||||
ENTRYPOINT ["/home/weave/launch.sh"]
|
ENTRYPOINT ["/home/weave/launch.sh"]
|
||||||
|
|
||||||
|
# This label will change for every build, and should therefore be the last layer of the image:
|
||||||
|
ARG revision
|
||||||
|
LABEL org.opencontainers.image.revision="${revision}"
|
||||||
@@ -27,9 +27,10 @@ xt_set_exists() {
|
|||||||
# Default if not supplied - same as weave net default
|
# Default if not supplied - same as weave net default
|
||||||
IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
|
IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
|
||||||
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
||||||
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
|
METRICS_ADDR=${WEAVE_METRICS_ADDR:-0.0.0.0:6782}
|
||||||
HOST_ROOT=${HOST_ROOT:-/host}
|
HOST_ROOT=${HOST_ROOT:-/host}
|
||||||
CONN_LIMIT=${CONN_LIMIT:-30}
|
CONN_LIMIT=${CONN_LIMIT:-100}
|
||||||
|
DB_PREFIX=${DB_PREFIX:-/weavedb/weave-net}
|
||||||
|
|
||||||
# Check if the IP range overlaps anything existing on the host
|
# Check if the IP range overlaps anything existing on the host
|
||||||
/usr/bin/weaveutil netcheck $IPALLOC_RANGE weave
|
/usr/bin/weaveutil netcheck $IPALLOC_RANGE weave
|
||||||
@@ -53,12 +54,23 @@ else
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
STATUS_OPTS="--metrics-addr=$METRICS_ADDR"
|
||||||
|
# --status-addr exposes internal information, so only turn it on if asked to.
|
||||||
|
if [ -n "${WEAVE_STATUS_ADDR}" ]; then
|
||||||
|
STATUS_OPTS="$STATUS_OPTS --status-addr=$WEAVE_STATUS_ADDR"
|
||||||
|
fi
|
||||||
|
|
||||||
# Default is that npc will be running; allow caller to override
|
# Default is that npc will be running; allow caller to override
|
||||||
WEAVE_NPC_OPTS="--expect-npc"
|
WEAVE_NPC_OPTS="--expect-npc"
|
||||||
if [ "${EXPECT_NPC}" = "0" ]; then
|
if [ "${EXPECT_NPC}" = "0" ]; then
|
||||||
WEAVE_NPC_OPTS=""
|
WEAVE_NPC_OPTS=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
NO_MASQ_LOCAL_OPT=""
|
||||||
|
if [ -n "${NO_MASQ_LOCAL}" ]; then
|
||||||
|
NO_MASQ_LOCAL_OPT="--no-masq-local"
|
||||||
|
fi
|
||||||
|
|
||||||
# Kubernetes sets HOSTNAME to the host's hostname
|
# Kubernetes sets HOSTNAME to the host's hostname
|
||||||
# when running a pod in host namespace.
|
# when running a pod in host namespace.
|
||||||
NICKNAME_ARG=""
|
NICKNAME_ARG=""
|
||||||
@@ -73,7 +85,7 @@ router_bridge_opts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if [ -z "$KUBE_PEERS" ]; then
|
if [ -z "$KUBE_PEERS" ]; then
|
||||||
if ! KUBE_PEERS=$(/home/weave/kube-peers) || [ -z "$KUBE_PEERS" ]; then
|
if ! KUBE_PEERS=$(/home/weave/kube-utils) || [ -z "$KUBE_PEERS" ]; then
|
||||||
echo Failed to get peers >&2
|
echo Failed to get peers >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -87,6 +99,29 @@ if [ -z "$IPALLOC_INIT" ]; then
|
|||||||
IPALLOC_INIT="consensus=$(peer_count $KUBE_PEERS)"
|
IPALLOC_INIT="consensus=$(peer_count $KUBE_PEERS)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Find out what peer name we will be using
|
||||||
|
PEERNAME=$(/home/weave/weaver $EXTRA_ARGS --print-peer-name --host-root=$HOST_ROOT --db-prefix="$DB_PREFIX")
|
||||||
|
if [ -z "$PEERNAME" ]; then
|
||||||
|
echo "Unable to get peer name" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If we have persisted data and peer reclaim is in use
|
||||||
|
if [ -f ${DB_PREFIX}data.db ]; then
|
||||||
|
if /usr/bin/weaveutil get-db-flag "$DB_PREFIX" peer-reclaim >/dev/null ; then
|
||||||
|
# If this peer name is not stored in the list then we were removed by another peer.
|
||||||
|
if /home/weave/kube-utils -check-peer-new -peer-name="$PEERNAME" -log-level=debug ; then
|
||||||
|
# In order to avoid a CRDT clash, clean up
|
||||||
|
echo "Peer not in list; removing persisted data" >&2
|
||||||
|
rm -f ${DB_PREFIX}data.db
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# flag that peer reclaim is now in use - note we have to do this before
|
||||||
|
# weaver is running as the DB can only be opened by one process at a time
|
||||||
|
/usr/bin/weaveutil set-db-flag "$DB_PREFIX" peer-reclaim ok
|
||||||
|
|
||||||
post_start_actions() {
|
post_start_actions() {
|
||||||
# Wait for weave process to become responsive
|
# Wait for weave process to become responsive
|
||||||
while true ; do
|
while true ; do
|
||||||
@@ -108,19 +143,27 @@ post_start_actions() {
|
|||||||
export HOST_ROOT
|
export HOST_ROOT
|
||||||
/home/weave/weave --local setup-cni
|
/home/weave/weave --local setup-cni
|
||||||
|
|
||||||
|
# Attempt to run the reclaim process, but don't halt the script if it fails
|
||||||
|
/home/weave/kube-utils -reclaim -node-name="$HOSTNAME" -peer-name="$PEERNAME" -log-level=debug || true
|
||||||
|
|
||||||
# Expose the weave network so host processes can communicate with pods
|
# Expose the weave network so host processes can communicate with pods
|
||||||
/home/weave/weave --local expose $WEAVE_EXPOSE_IP
|
/home/weave/weave --local expose $WEAVE_EXPOSE_IP
|
||||||
|
|
||||||
|
# Mark network as up
|
||||||
|
/home/weave/kube-utils -set-node-status -node-name="$HOSTNAME"
|
||||||
}
|
}
|
||||||
|
|
||||||
post_start_actions &
|
post_start_actions &
|
||||||
|
|
||||||
/home/weave/weaver $EXTRA_ARGS --port=6783 $(router_bridge_opts) \
|
/home/weave/weaver $EXTRA_ARGS --port=6783 $(router_bridge_opts) \
|
||||||
|
--name="$PEERNAME" \
|
||||||
--host-root=$HOST_ROOT \
|
--host-root=$HOST_ROOT \
|
||||||
--http-addr=$HTTP_ADDR --status-addr=$STATUS_ADDR --docker-api='' --no-dns \
|
--http-addr=$HTTP_ADDR $STATUS_OPTS --docker-api='' --no-dns \
|
||||||
--db-prefix="/weavedb/weave-net" \
|
--db-prefix="$DB_PREFIX" \
|
||||||
--ipalloc-range=$IPALLOC_RANGE $NICKNAME_ARG \
|
--ipalloc-range=$IPALLOC_RANGE $NICKNAME_ARG \
|
||||||
--ipalloc-init $IPALLOC_INIT \
|
--ipalloc-init $IPALLOC_INIT \
|
||||||
--conn-limit=$CONN_LIMIT \
|
--conn-limit=$CONN_LIMIT \
|
||||||
$WEAVE_NPC_OPTS \
|
$WEAVE_NPC_OPTS \
|
||||||
|
$NO_MASQ_LOCAL_OPT \
|
||||||
"$@" \
|
"$@" \
|
||||||
$KUBE_PEERS
|
$KUBE_PEERS
|
||||||
@@ -47,6 +47,44 @@ items:
|
|||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: weave-net
|
name: weave-net
|
||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: weave-net-kube-peer
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
name: weave-net-kube-peer
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
resourceNames:
|
||||||
|
- weave-net
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: weave-net-kube-peer
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
name: weave-net-kube-peer
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
name: weave-net-kube-peer
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
- apiVersion: extensions/v1beta1
|
- apiVersion: extensions/v1beta1
|
||||||
kind: DaemonSet
|
kind: DaemonSet
|
||||||
metadata:
|
metadata:
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: List
|
||||||
|
items:
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- pods
|
||||||
|
- namespaces
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- 'networking.k8s.io'
|
||||||
|
resources:
|
||||||
|
- networkpolicies
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- nodes/status
|
||||||
|
verbs:
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
roleRef:
|
||||||
|
kind: ClusterRole
|
||||||
|
name: weave-net
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
resourceNames:
|
||||||
|
- weave-net
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
name: weave-net
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
- apiVersion: extensions/v1beta1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
# Wait 5 seconds to let pod connect before rolling next pod
|
||||||
|
minReadySeconds: 5
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: weave
|
||||||
|
command:
|
||||||
|
- /home/weave/launch.sh
|
||||||
|
env:
|
||||||
|
- name: HOSTNAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
image: 'weaveworks/weave-kube:latest'
|
||||||
|
imagePullPolicy: Always
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
host: 127.0.0.1
|
||||||
|
path: /status
|
||||||
|
port: 6784
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: weavedb
|
||||||
|
mountPath: /weavedb
|
||||||
|
- name: cni-bin
|
||||||
|
mountPath: /host/opt
|
||||||
|
- name: cni-bin2
|
||||||
|
mountPath: /host/home
|
||||||
|
- name: cni-conf
|
||||||
|
mountPath: /host/etc
|
||||||
|
- name: dbus
|
||||||
|
mountPath: /host/var/lib/dbus
|
||||||
|
- name: lib-modules
|
||||||
|
mountPath: /lib/modules
|
||||||
|
- name: xtables-lock
|
||||||
|
mountPath: /run/xtables.lock
|
||||||
|
readOnly: false
|
||||||
|
- name: weave-npc
|
||||||
|
env:
|
||||||
|
- name: HOSTNAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
image: 'weaveworks/weave-npc:latest'
|
||||||
|
imagePullPolicy: Always
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: xtables-lock
|
||||||
|
mountPath: /run/xtables.lock
|
||||||
|
readOnly: false
|
||||||
|
hostNetwork: true
|
||||||
|
hostPID: true
|
||||||
|
restartPolicy: Always
|
||||||
|
securityContext:
|
||||||
|
seLinuxOptions: {}
|
||||||
|
serviceAccountName: weave-net
|
||||||
|
tolerations:
|
||||||
|
- effect: NoSchedule
|
||||||
|
operator: Exists
|
||||||
|
volumes:
|
||||||
|
- name: weavedb
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/weave
|
||||||
|
- name: cni-bin
|
||||||
|
hostPath:
|
||||||
|
path: /opt
|
||||||
|
- name: cni-bin2
|
||||||
|
hostPath:
|
||||||
|
path: /home
|
||||||
|
- name: cni-conf
|
||||||
|
hostPath:
|
||||||
|
path: /etc
|
||||||
|
- name: dbus
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/dbus
|
||||||
|
- name: lib-modules
|
||||||
|
hostPath:
|
||||||
|
path: /lib/modules
|
||||||
|
- name: xtables-lock
|
||||||
|
hostPath:
|
||||||
|
path: /run/xtables.lock
|
||||||
|
updateStrategy:
|
||||||
|
type: RollingUpdate
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: List
|
||||||
|
items:
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- pods
|
||||||
|
- namespaces
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- extensions
|
||||||
|
resources:
|
||||||
|
- networkpolicies
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- 'networking.k8s.io'
|
||||||
|
resources:
|
||||||
|
- networkpolicies
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- nodes/status
|
||||||
|
verbs:
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
roleRef:
|
||||||
|
kind: ClusterRole
|
||||||
|
name: weave-net
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
resourceNames:
|
||||||
|
- weave-net
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
name: weave-net
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
- apiVersion: extensions/v1beta1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
# Wait 5 seconds to let pod connect before rolling next pod
|
||||||
|
minReadySeconds: 5
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: weave
|
||||||
|
command:
|
||||||
|
- /home/weave/launch.sh
|
||||||
|
env:
|
||||||
|
- name: HOSTNAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
image: 'weaveworks/weave-kube:latest'
|
||||||
|
imagePullPolicy: Always
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
host: 127.0.0.1
|
||||||
|
path: /status
|
||||||
|
port: 6784
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: weavedb
|
||||||
|
mountPath: /weavedb
|
||||||
|
- name: cni-bin
|
||||||
|
mountPath: /host/opt
|
||||||
|
- name: cni-bin2
|
||||||
|
mountPath: /host/home
|
||||||
|
- name: cni-conf
|
||||||
|
mountPath: /host/etc
|
||||||
|
- name: dbus
|
||||||
|
mountPath: /host/var/lib/dbus
|
||||||
|
- name: lib-modules
|
||||||
|
mountPath: /lib/modules
|
||||||
|
- name: xtables-lock
|
||||||
|
mountPath: /run/xtables.lock
|
||||||
|
readOnly: false
|
||||||
|
- name: weave-npc
|
||||||
|
env:
|
||||||
|
- name: HOSTNAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
image: 'weaveworks/weave-npc:latest'
|
||||||
|
imagePullPolicy: Always
|
||||||
|
#npc-args
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: xtables-lock
|
||||||
|
mountPath: /run/xtables.lock
|
||||||
|
readOnly: false
|
||||||
|
hostNetwork: true
|
||||||
|
hostPID: true
|
||||||
|
restartPolicy: Always
|
||||||
|
securityContext:
|
||||||
|
seLinuxOptions: {}
|
||||||
|
serviceAccountName: weave-net
|
||||||
|
tolerations:
|
||||||
|
- effect: NoSchedule
|
||||||
|
operator: Exists
|
||||||
|
volumes:
|
||||||
|
- name: weavedb
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/weave
|
||||||
|
- name: cni-bin
|
||||||
|
hostPath:
|
||||||
|
path: /opt
|
||||||
|
- name: cni-bin2
|
||||||
|
hostPath:
|
||||||
|
path: /home
|
||||||
|
- name: cni-conf
|
||||||
|
hostPath:
|
||||||
|
path: /etc
|
||||||
|
- name: dbus
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/dbus
|
||||||
|
- name: lib-modules
|
||||||
|
hostPath:
|
||||||
|
path: /lib/modules
|
||||||
|
- name: xtables-lock
|
||||||
|
hostPath:
|
||||||
|
path: /run/xtables.lock
|
||||||
|
type: FileOrCreate
|
||||||
|
updateStrategy:
|
||||||
|
type: RollingUpdate
|
||||||
@@ -1,17 +1,24 @@
|
|||||||
FROM ALPINE_BASEIMAGE
|
FROM ALPINE_BASEIMAGE
|
||||||
|
|
||||||
# If we're building for another architecture than amd64, the CROSS_BUILD_ placeholder is removed so e.g. CROSS_BUILD_COPY turns into COPY
|
# These labels are pretty static, and can therefore be added early on:
|
||||||
# If we're building normally, for amd64, CROSS_BUILD lines are removed
|
LABEL works.weave.role="system" \
|
||||||
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
maintainer="Weaveworks <help@weave.works>" \
|
||||||
|
org.opencontainers.image.title="Weave Net" \
|
||||||
LABEL maintainer "Weaveworks Inc <help@weave.works>"
|
org.opencontainers.image.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||||
LABEL works.weave.role=system \
|
org.opencontainers.image.url="https://weave.works" \
|
||||||
|
org.opencontainers.image.source="https://github.com/weaveworks/weave" \
|
||||||
|
org.opencontainers.image.vendor="Weaveworks" \
|
||||||
|
org.label-schema.schema-version="1.0" \
|
||||||
org.label-schema.name="Weave Net" \
|
org.label-schema.name="Weave Net" \
|
||||||
org.label-schema.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
org.label-schema.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||||
org.label-schema.url="https://weave.works" \
|
org.label-schema.url="https://weave.works" \
|
||||||
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
||||||
org.label-schema.vendor="Weaveworks"
|
org.label-schema.vendor="Weaveworks"
|
||||||
|
|
||||||
|
# If we're building for another architecture than amd64, the CROSS_BUILD_ placeholder is removed so e.g. CROSS_BUILD_COPY turns into COPY
|
||||||
|
# If we're building normally, for amd64, CROSS_BUILD lines are removed
|
||||||
|
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
||||||
|
|
||||||
RUN apk add --update \
|
RUN apk add --update \
|
||||||
iptables \
|
iptables \
|
||||||
ipset \
|
ipset \
|
||||||
@@ -21,3 +28,8 @@ RUN apk add --update \
|
|||||||
COPY ./weave-npc /usr/bin/weave-npc
|
COPY ./weave-npc /usr/bin/weave-npc
|
||||||
COPY ./ulogd.conf /etc/ulogd.conf
|
COPY ./ulogd.conf /etc/ulogd.conf
|
||||||
ENTRYPOINT ["/usr/bin/weave-npc"]
|
ENTRYPOINT ["/usr/bin/weave-npc"]
|
||||||
|
|
||||||
|
# These labels will change for every build, and should therefore be the last layer of the image:
|
||||||
|
ARG revision
|
||||||
|
LABEL org.opencontainers.image.revision="${revision}" \
|
||||||
|
org.label-schema.vcs-ref="${revision}"
|
||||||
+122
-29
@@ -7,18 +7,19 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
coreapi "k8s.io/api/core/v1"
|
||||||
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
coreapi "k8s.io/client-go/pkg/api/v1"
|
|
||||||
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
|
||||||
"k8s.io/client-go/pkg/fields"
|
|
||||||
"k8s.io/client-go/pkg/runtime"
|
|
||||||
"k8s.io/client-go/pkg/util/wait"
|
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
"github.com/weaveworks/weave/common"
|
||||||
|
"github.com/weaveworks/weave/net"
|
||||||
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
"github.com/weaveworks/weave/npc"
|
"github.com/weaveworks/weave/npc"
|
||||||
"github.com/weaveworks/weave/npc/ipset"
|
|
||||||
"github.com/weaveworks/weave/npc/metrics"
|
"github.com/weaveworks/weave/npc/metrics"
|
||||||
"github.com/weaveworks/weave/npc/ulogd"
|
"github.com/weaveworks/weave/npc/ulogd"
|
||||||
)
|
)
|
||||||
@@ -29,12 +30,13 @@ var (
|
|||||||
logLevel string
|
logLevel string
|
||||||
allowMcast bool
|
allowMcast bool
|
||||||
nodeName string
|
nodeName string
|
||||||
|
maxList int
|
||||||
)
|
)
|
||||||
|
|
||||||
func handleError(err error) { common.CheckFatal(err) }
|
func handleError(err error) { common.CheckFatal(err) }
|
||||||
|
|
||||||
func makeController(getter cache.Getter, resource string,
|
func makeController(getter cache.Getter, resource string,
|
||||||
objType runtime.Object, handlers cache.ResourceEventHandlerFuncs) *cache.Controller {
|
objType runtime.Object, handlers cache.ResourceEventHandlerFuncs) cache.Controller {
|
||||||
listWatch := cache.NewListWatchFromClient(getter, resource, "", fields.Everything())
|
listWatch := cache.NewListWatchFromClient(getter, resource, "", fields.Everything())
|
||||||
_, controller := cache.NewInformer(listWatch, objType, 0, handlers)
|
_, controller := cache.NewInformer(listWatch, objType, 0, handlers)
|
||||||
return controller
|
return controller
|
||||||
@@ -54,11 +56,27 @@ func resetIPTables(ipt *iptables.IPTables) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := ipt.ClearChain(npc.TableFilter, npc.EgressMarkChain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipt.ClearChain(npc.TableFilter, npc.EgressCustomChain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipt.ClearChain(npc.TableFilter, npc.EgressDefaultChain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do not clear npc.EgressChain here because otherwise, in the case of restarting
|
||||||
|
// weave-npc process, all egress traffic is allowed for a short period of time.
|
||||||
|
// The chain is created in createBaseRules.
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetIPSets(ips ipset.Interface) error {
|
func resetIPSets(ips ipset.Interface) error {
|
||||||
// Remove ipsets prefixed `weave-` only
|
// Remove ipsets prefixed `weave-` only.
|
||||||
|
|
||||||
sets, err := ips.List(npc.IpsetNamePrefix)
|
sets, err := ips.List(npc.IpsetNamePrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -78,6 +96,11 @@ func resetIPSets(ips ipset.Interface) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range sets {
|
for _, s := range sets {
|
||||||
|
// LocalIPset might be used by WEAVE-NPC-EGRESS chain which we do not
|
||||||
|
// flush, so we cannot destroy it.
|
||||||
|
if s == npc.LocalIpset {
|
||||||
|
continue
|
||||||
|
}
|
||||||
common.Log.Debugf("Destroying ipset '%s'", string(s))
|
common.Log.Debugf("Destroying ipset '%s'", string(s))
|
||||||
if err := ips.Destroy(s); err != nil {
|
if err := ips.Destroy(s); err != nil {
|
||||||
common.Log.Errorf("Failed to destroy ipset '%s'", string(s))
|
common.Log.Errorf("Failed to destroy ipset '%s'", string(s))
|
||||||
@@ -113,18 +136,86 @@ func createBaseRules(ipt *iptables.IPTables, ips ipset.Interface) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the destination address is not any of the local pods, let it through
|
// If the destination address is not any of the local pods, let it through
|
||||||
if err := ips.Create(npc.LocalIpset, ipset.HashIP); err != nil {
|
found, err := ipsetExist(ips, npc.LocalIpset)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if !found {
|
||||||
|
if err := ips.Create(npc.LocalIpset, ipset.HashIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
if err := ipt.Append(npc.TableFilter, npc.MainChain,
|
if err := ipt.Append(npc.TableFilter, npc.MainChain,
|
||||||
"-m", "set", "!", "--match-set", npc.LocalIpset, "dst", "-j", "ACCEPT"); err != nil {
|
"-m", "set", "!", "--match-set", npc.LocalIpset, "dst", "-j", "ACCEPT"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := ipt.Append(npc.TableFilter, npc.EgressMarkChain,
|
||||||
|
"-j", "MARK", "--set-xmark", npc.EgressMark); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Egress rules:
|
||||||
|
//
|
||||||
|
// -A WEAVE-NPC-EGRESS -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||||
|
// -A WEAVE-NPC-EGRESS -m set ! --match-set weave-local-pods src -j RETURN
|
||||||
|
// -A WEAVE-NPC-EGRESS -d 224.0.0.0/4 -j RETURN
|
||||||
|
// -A WEAVE-NPC-EGRESS -m state --state NEW -j WEAVE-NPC-EGRESS-DEFAULT
|
||||||
|
// -A WEAVE-NPC-EGRESS -m state --state NEW -m mark ! --mark 0x40000/0x40000 -j WEAVE-NPC-EGRESS-CUSTOM
|
||||||
|
// -A WEAVE-NPC-EGRESS -m state --state NEW -m mark ! --mark 0x40000/0x40000 -j NFLOG --nflog-group 86
|
||||||
|
// -A WEAVE-NPC-EGRESS -m mark ! --mark 0x40000/0x40000 -j DROP
|
||||||
|
//
|
||||||
|
// -A WEAVE-NPC-EGRESS-CUSTOM <rulespec> -j MARK --set-xmark 0x40000/0x40000
|
||||||
|
// -A WEAVE-NPC-EGRESS-CUSTOM <rulespec> -j RETURN
|
||||||
|
//
|
||||||
|
// -A WEAVE-NPC-EGRESS-DEFAULT <rulespec> -j MARK --set-xmark 0x40000/0x40000
|
||||||
|
// -A WEAVE-NPC-EGRESS-DEFAULT <rulespec> -j RETURN
|
||||||
|
//
|
||||||
|
// For each rule we create two (mark and return). We cannot just accept
|
||||||
|
// a packet if it matches any rule, as a packet might need to traverse
|
||||||
|
// the ingress npc as well which happens later in the chain (in some cases
|
||||||
|
// we cannot detect whether packet is ingress or egress, so we need to
|
||||||
|
// check both chains).
|
||||||
|
|
||||||
|
ruleSpecs := [][]string{
|
||||||
|
{"-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"},
|
||||||
|
{"-m", "state", "--state", "NEW", "-m", "set", "!", "--match-set", npc.LocalIpset, "src", "-j", "RETURN"},
|
||||||
|
}
|
||||||
|
if allowMcast {
|
||||||
|
ruleSpecs = append(ruleSpecs, []string{"-d", "224.0.0.0/4", "-j", "RETURN"})
|
||||||
|
}
|
||||||
|
ruleSpecs = append(ruleSpecs, [][]string{
|
||||||
|
{"-m", "state", "--state", "NEW", "-j", string(npc.EgressDefaultChain)},
|
||||||
|
{"-m", "state", "--state", "NEW", "-m", "mark", "!", "--mark", npc.EgressMark, "-j", string(npc.EgressCustomChain)},
|
||||||
|
{"-m", "state", "--state", "NEW", "-m", "mark", "!", "--mark", npc.EgressMark, "-j", "NFLOG", "--nflog-group", "86"},
|
||||||
|
{"-m", "mark", "!", "--mark", npc.EgressMark, "-j", "DROP"},
|
||||||
|
}...)
|
||||||
|
if err := net.AddChainWithRules(ipt, npc.TableFilter, npc.EgressChain, ruleSpecs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dummy way to check whether a given ipset exists.
|
||||||
|
// TODO(brb) Use "ipset -exist create <..>" for our purpose instead (for some reasons
|
||||||
|
// creating an ipset with -exist fails).
|
||||||
|
func ipsetExist(ips ipset.Interface, name ipset.Name) (bool, error) {
|
||||||
|
sets, err := ips.List(string(name))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
for _, s := range sets {
|
||||||
|
if s == name {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
func root(cmd *cobra.Command, args []string) {
|
func root(cmd *cobra.Command, args []string) {
|
||||||
|
var npController cache.Controller
|
||||||
|
|
||||||
common.SetLogLevel(logLevel)
|
common.SetLogLevel(logLevel)
|
||||||
if nodeName == "" {
|
if nodeName == "" {
|
||||||
// HOSTNAME is set by Kubernetes for pods in the host network namespace
|
// HOSTNAME is set by Kubernetes for pods in the host network namespace
|
||||||
@@ -152,7 +243,7 @@ func root(cmd *cobra.Command, args []string) {
|
|||||||
ipt, err := iptables.New()
|
ipt, err := iptables.New()
|
||||||
handleError(err)
|
handleError(err)
|
||||||
|
|
||||||
ips := ipset.New(common.LogLogger())
|
ips := ipset.New(common.LogLogger(), maxList)
|
||||||
|
|
||||||
handleError(resetIPTables(ipt))
|
handleError(resetIPTables(ipt))
|
||||||
handleError(resetIPSets(ips))
|
handleError(resetIPSets(ips))
|
||||||
@@ -200,25 +291,26 @@ func root(cmd *cobra.Command, args []string) {
|
|||||||
handleError(npc.UpdatePod(old.(*coreapi.Pod), new.(*coreapi.Pod)))
|
handleError(npc.UpdatePod(old.(*coreapi.Pod), new.(*coreapi.Pod)))
|
||||||
}})
|
}})
|
||||||
|
|
||||||
npController := makeController(client.Extensions().RESTClient(), "networkpolicies", &extnapi.NetworkPolicy{},
|
npHandlers := cache.ResourceEventHandlerFuncs{
|
||||||
cache.ResourceEventHandlerFuncs{
|
AddFunc: func(obj interface{}) {
|
||||||
AddFunc: func(obj interface{}) {
|
handleError(npc.AddNetworkPolicy(obj))
|
||||||
handleError(npc.AddNetworkPolicy(obj.(*extnapi.NetworkPolicy)))
|
},
|
||||||
},
|
DeleteFunc: func(obj interface{}) {
|
||||||
DeleteFunc: func(obj interface{}) {
|
switch obj := obj.(type) {
|
||||||
switch obj := obj.(type) {
|
case cache.DeletedFinalStateUnknown:
|
||||||
case *extnapi.NetworkPolicy:
|
// We know this object has gone away, but its final state is no longer
|
||||||
handleError(npc.DeleteNetworkPolicy(obj))
|
// available from the API server. Instead we use the last copy of it
|
||||||
case cache.DeletedFinalStateUnknown:
|
// that we have, which is good enough for our cleanup.
|
||||||
// We know this object has gone away, but its final state is no longer
|
handleError(npc.DeleteNetworkPolicy(obj.Obj))
|
||||||
// available from the API server. Instead we use the last copy of it
|
default:
|
||||||
// that we have, which is good enough for our cleanup.
|
handleError(npc.DeleteNetworkPolicy(obj))
|
||||||
handleError(npc.DeleteNetworkPolicy(obj.Obj.(*extnapi.NetworkPolicy)))
|
}
|
||||||
}
|
},
|
||||||
},
|
UpdateFunc: func(old, new interface{}) {
|
||||||
UpdateFunc: func(old, new interface{}) {
|
handleError(npc.UpdateNetworkPolicy(old, new))
|
||||||
handleError(npc.UpdateNetworkPolicy(old.(*extnapi.NetworkPolicy), new.(*extnapi.NetworkPolicy)))
|
},
|
||||||
}})
|
}
|
||||||
|
npController = makeController(client.NetworkingV1().RESTClient(), "networkpolicies", &networkingv1.NetworkPolicy{}, npHandlers)
|
||||||
|
|
||||||
go nsController.Run(wait.NeverStop)
|
go nsController.Run(wait.NeverStop)
|
||||||
go podController.Run(wait.NeverStop)
|
go podController.Run(wait.NeverStop)
|
||||||
@@ -239,6 +331,7 @@ func main() {
|
|||||||
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "debug", "logging level (debug, info, warning, error)")
|
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "debug", "logging level (debug, info, warning, error)")
|
||||||
rootCmd.PersistentFlags().BoolVar(&allowMcast, "allow-mcast", true, "allow all multicast traffic")
|
rootCmd.PersistentFlags().BoolVar(&allowMcast, "allow-mcast", true, "allow all multicast traffic")
|
||||||
rootCmd.PersistentFlags().StringVar(&nodeName, "node-name", "", "only generate rules that apply to this node")
|
rootCmd.PersistentFlags().StringVar(&nodeName, "node-name", "", "only generate rules that apply to this node")
|
||||||
|
rootCmd.PersistentFlags().IntVar(&maxList, "max-list-size", 1024, "maximum size of ipset list (for namespaces)")
|
||||||
|
|
||||||
handleError(rootCmd.Execute())
|
handleError(rootCmd.Execute())
|
||||||
}
|
}
|
||||||
+12
-2
@@ -1,7 +1,17 @@
|
|||||||
# This is a nearly-empty image that we use to create a data-only container for persistence
|
# This is a nearly-empty image that we use to create a data-only container for persistence
|
||||||
FROM scratch
|
FROM scratch
|
||||||
LABEL maintainer "Weaveworks Inc <help@weave.works>"
|
|
||||||
LABEL works.weave.role=system
|
# These labels are pretty static, and can therefore be added early on:
|
||||||
|
LABEL works.weave.role="system" \
|
||||||
|
maintainer="Weaveworks <help@weave.works>" \
|
||||||
|
org.opencontainers.image.title="weavedb" \
|
||||||
|
org.opencontainers.image.source="https://github.com/weaveworks/weave" \
|
||||||
|
org.opencontainers.image.vendor="Weaveworks"
|
||||||
|
|
||||||
ENTRYPOINT ["data-only"]
|
ENTRYPOINT ["data-only"]
|
||||||
# Work round Docker refusing to save an empty image
|
# Work round Docker refusing to save an empty image
|
||||||
COPY Dockerfile /
|
COPY Dockerfile /
|
||||||
|
|
||||||
|
# This label will change for every build, and should therefore be the last layer of the image:
|
||||||
|
ARG revision
|
||||||
|
LABEL org.opencontainers.image.revision="${revision}"
|
||||||
@@ -1,10 +1,19 @@
|
|||||||
FROM DOCKERHUB_USER/weaveARCH_EXT
|
FROM DOCKERHUB_USER/weaveARCH_EXT
|
||||||
|
|
||||||
|
# These labels are pretty static, and can therefore be added early on:
|
||||||
|
LABEL maintainer="Weaveworks <help@weave.works>" \
|
||||||
|
org.opencontainers.image.title="weaveexec" \
|
||||||
|
org.opencontainers.image.source="https://github.com/weaveworks/weave" \
|
||||||
|
org.opencontainers.image.vendor="Weaveworks"
|
||||||
|
|
||||||
ENTRYPOINT ["/home/weave/sigproxy", "/home/weave/weave"]
|
ENTRYPOINT ["/home/weave/sigproxy", "/home/weave/weave"]
|
||||||
|
|
||||||
ADD ./sigproxy ./symlink /home/weave/
|
ADD ./sigproxy ./symlink /home/weave/
|
||||||
ADD ./docker /usr/bin/
|
|
||||||
ADD ./weavewait /w/w
|
ADD ./weavewait /w/w
|
||||||
ADD ./weavewait_noop /w-noop/w
|
ADD ./weavewait_noop /w-noop/w
|
||||||
ADD ./weavewait_nomcast /w-nomcast/w
|
ADD ./weavewait_nomcast /w-nomcast/w
|
||||||
WORKDIR /home/weave
|
WORKDIR /home/weave
|
||||||
|
|
||||||
|
# This label will change for every build, and should therefore be the last layer of the image:
|
||||||
|
ARG revision
|
||||||
|
LABEL org.opencontainers.image.revision="${revision}"
|
||||||
@@ -1,17 +1,24 @@
|
|||||||
FROM ALPINE_BASEIMAGE
|
FROM ALPINE_BASEIMAGE
|
||||||
|
|
||||||
# If we're building for another architecture than amd64, the CROSS_BUILD_ placeholder is removed so e.g. CROSS_BUILD_COPY turns into COPY
|
# These labels are pretty static, and can therefore be added early on:
|
||||||
# If we're building normally, for amd64, CROSS_BUILD lines are removed
|
LABEL works.weave.role="system" \
|
||||||
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
maintainer="Weaveworks <help@weave.works>" \
|
||||||
|
org.opencontainers.image.title="Weave Net" \
|
||||||
LABEL maintainer "Weaveworks Inc <help@weave.works>"
|
org.opencontainers.image.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||||
LABEL works.weave.role=system \
|
org.opencontainers.image.url="https://weave.works" \
|
||||||
|
org.opencontainers.image.source="https://github.com/weaveworks/weave" \
|
||||||
|
org.opencontainers.image.vendor="Weaveworks" \
|
||||||
|
org.label-schema.schema-version="1.0" \
|
||||||
org.label-schema.name="Weave Net" \
|
org.label-schema.name="Weave Net" \
|
||||||
org.label-schema.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
org.label-schema.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||||
org.label-schema.url="https://weave.works" \
|
org.label-schema.url="https://weave.works" \
|
||||||
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
||||||
org.label-schema.vendor="Weaveworks"
|
org.label-schema.vendor="Weaveworks"
|
||||||
|
|
||||||
|
# If we're building for another architecture than amd64, the CROSS_BUILD_ placeholder is removed so e.g. CROSS_BUILD_COPY turns into COPY
|
||||||
|
# If we're building normally, for amd64, CROSS_BUILD lines are removed
|
||||||
|
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
||||||
|
|
||||||
RUN apk add --update \
|
RUN apk add --update \
|
||||||
curl \
|
curl \
|
||||||
ethtool \
|
ethtool \
|
||||||
@@ -21,11 +28,16 @@ RUN apk add --update \
|
|||||||
util-linux \
|
util-linux \
|
||||||
conntrack-tools \
|
conntrack-tools \
|
||||||
bind-tools \
|
bind-tools \
|
||||||
|
ca-certificates \
|
||||||
&& rm -rf /var/cache/apk/*
|
&& rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
ADD ./weave ./weaver /home/weave/
|
ADD ./weave ./weaver /home/weave/
|
||||||
ADD ./weaveutil /usr/bin/
|
ADD ./weaveutil /usr/bin/
|
||||||
ADD weavedata.db /weavedb/
|
ADD weavedata.db /weavedb/
|
||||||
COPY ca-certificates.crt /etc/ssl/certs/
|
|
||||||
ENTRYPOINT ["/home/weave/weaver"]
|
ENTRYPOINT ["/home/weave/weaver"]
|
||||||
WORKDIR /home/weave
|
WORKDIR /home/weave
|
||||||
|
|
||||||
|
# These labels will change for every build, and should therefore be the last layer of the image:
|
||||||
|
ARG revision
|
||||||
|
LABEL org.opencontainers.image.revision="${revision}" \
|
||||||
|
org.label-schema.vcs-ref="${revision}"
|
||||||
File diff suppressed because it is too large.
Load diff
+27
-19
@@ -34,23 +34,7 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
|||||||
},
|
},
|
||||||
"printIPAMRanges": func(router weave.NetworkRouterStatus, status ipam.Status) string {
|
"printIPAMRanges": func(router weave.NetworkRouterStatus, status ipam.Status) string {
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
|
peerStats := summariseIpamStats(&status)
|
||||||
type stats struct {
|
|
||||||
ips uint32
|
|
||||||
nickname string
|
|
||||||
reachable bool
|
|
||||||
}
|
|
||||||
|
|
||||||
peerStats := make(map[string]*stats)
|
|
||||||
|
|
||||||
for _, entry := range status.Entries {
|
|
||||||
s, found := peerStats[entry.Peer]
|
|
||||||
if !found {
|
|
||||||
s = &stats{nickname: entry.Nickname, reachable: entry.IsKnownPeer}
|
|
||||||
peerStats[entry.Peer] = s
|
|
||||||
}
|
|
||||||
s.ips += entry.Size
|
|
||||||
}
|
|
||||||
|
|
||||||
printOwned := func(name string, nickName string, info string, ips uint32) {
|
printOwned := func(name string, nickName string, info string, ips uint32) {
|
||||||
percentageRanges := float32(ips) * 100.0 / float32(status.RangeNumIPs)
|
percentageRanges := float32(ips) * 100.0 / float32(status.RangeNumIPs)
|
||||||
@@ -116,6 +100,25 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
|||||||
"trimSuffix": strings.TrimSuffix,
|
"trimSuffix": strings.TrimSuffix,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
type ipamStats struct {
|
||||||
|
ips uint32
|
||||||
|
nickname string
|
||||||
|
reachable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func summariseIpamStats(status *ipam.Status) map[string]*ipamStats {
|
||||||
|
peerStats := make(map[string]*ipamStats)
|
||||||
|
for _, entry := range status.Entries {
|
||||||
|
s, found := peerStats[entry.Peer]
|
||||||
|
if !found {
|
||||||
|
s = &ipamStats{nickname: entry.Nickname, reachable: entry.IsKnownPeer}
|
||||||
|
peerStats[entry.Peer] = s
|
||||||
|
}
|
||||||
|
s.ips += entry.Size
|
||||||
|
}
|
||||||
|
return peerStats
|
||||||
|
}
|
||||||
|
|
||||||
func countDNSEntries(entries []nameserver.EntryStatus) int {
|
func countDNSEntries(entries []nameserver.EntryStatus) int {
|
||||||
count := 0
|
count := 0
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
@@ -326,7 +329,7 @@ func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRout
|
|||||||
w.Write(json)
|
w.Write(json)
|
||||||
})
|
})
|
||||||
|
|
||||||
muxRouter.Methods("GET").Path("/report").Queries("format", "{format}").HandlerFunc(
|
muxRouter.Methods("GET").Path("/report").HandlerFunc(
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
funcs := template.FuncMap{
|
funcs := template.FuncMap{
|
||||||
"json": func(v interface{}) string {
|
"json": func(v interface{}) string {
|
||||||
@@ -334,7 +337,12 @@ func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRout
|
|||||||
return string(a)
|
return string(a)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
formatTemplate, err := template.New("format").Funcs(funcs).Parse(mux.Vars(r)["format"])
|
format, ok := r.URL.Query()["format"]
|
||||||
|
if !ok || len(format) != 1 {
|
||||||
|
http.Error(w, "error: must supply one format parameter", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
formatTemplate, err := template.New("format").Funcs(funcs).Parse(format[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
|||||||
+62
-34
@@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/weaveworks/weave/nameserver"
|
"github.com/weaveworks/weave/nameserver"
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
"github.com/weaveworks/weave/net/address"
|
"github.com/weaveworks/weave/net/address"
|
||||||
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
"github.com/weaveworks/weave/plugin"
|
"github.com/weaveworks/weave/plugin"
|
||||||
weaveproxy "github.com/weaveworks/weave/proxy"
|
weaveproxy "github.com/weaveworks/weave/proxy"
|
||||||
weave "github.com/weaveworks/weave/router"
|
weave "github.com/weaveworks/weave/router"
|
||||||
@@ -45,12 +46,20 @@ type ipamConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type dnsConfig struct {
|
type dnsConfig struct {
|
||||||
Domain string
|
Domain string
|
||||||
ListenAddress string
|
ListenAddress string
|
||||||
TTL int
|
TTL int
|
||||||
ClientTimeout time.Duration
|
ClientTimeout time.Duration
|
||||||
EffectiveListenAddress string
|
ResolvConf string
|
||||||
ResolvConf string
|
}
|
||||||
|
|
||||||
|
// return the address part of the DNS listen address, without ":53" on the end
|
||||||
|
func (c dnsConfig) addressOnly() string {
|
||||||
|
addr, _, err := net.SplitHostPort(c.ListenAddress)
|
||||||
|
if err != nil {
|
||||||
|
Log.Fatalf("Error when parsing DNS listen address %q: %s", c.ListenAddress, err)
|
||||||
|
}
|
||||||
|
return addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ipamConfig) Enabled() bool {
|
func (c *ipamConfig) Enabled() bool {
|
||||||
@@ -131,6 +140,7 @@ func main() {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
justVersion bool
|
justVersion bool
|
||||||
|
justPeerName bool
|
||||||
config mesh.Config
|
config mesh.Config
|
||||||
bridgeConfig weavenet.BridgeConfig
|
bridgeConfig weavenet.BridgeConfig
|
||||||
networkConfig weave.NetworkConfig
|
networkConfig weave.NetworkConfig
|
||||||
@@ -146,6 +156,7 @@ func main() {
|
|||||||
noDiscovery bool
|
noDiscovery bool
|
||||||
httpAddr string
|
httpAddr string
|
||||||
statusAddr string
|
statusAddr string
|
||||||
|
metricsAddr string
|
||||||
ipamConfig ipamConfig
|
ipamConfig ipamConfig
|
||||||
dockerAPI string
|
dockerAPI string
|
||||||
peers []string
|
peers []string
|
||||||
@@ -163,6 +174,7 @@ func main() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
mflag.BoolVar(&justVersion, []string{"-version"}, false, "print version and exit")
|
mflag.BoolVar(&justVersion, []string{"-version"}, false, "print version and exit")
|
||||||
|
mflag.BoolVar(&justPeerName, []string{"-print-peer-name"}, false, "print peer name and exit")
|
||||||
mflag.StringVar(&config.Host, []string{"-host"}, "", "router host")
|
mflag.StringVar(&config.Host, []string{"-host"}, "", "router host")
|
||||||
mflag.IntVar(&config.Port, []string{"-port"}, mesh.Port, "router port")
|
mflag.IntVar(&config.Port, []string{"-port"}, mesh.Port, "router port")
|
||||||
mflag.IntVar(&protocolMinVersion, []string{"-min-protocol-version"}, mesh.ProtocolMinVersion, "minimum weave protocol version")
|
mflag.IntVar(&protocolMinVersion, []string{"-min-protocol-version"}, mesh.ProtocolMinVersion, "minimum weave protocol version")
|
||||||
@@ -176,12 +188,13 @@ func main() {
|
|||||||
mflag.StringVar(&logLevel, []string{"-log-level"}, "info", "logging level (debug, info, warning, error)")
|
mflag.StringVar(&logLevel, []string{"-log-level"}, "info", "logging level (debug, info, warning, error)")
|
||||||
mflag.BoolVar(&pktdebug, []string{"-pkt-debug"}, false, "enable per-packet debug logging")
|
mflag.BoolVar(&pktdebug, []string{"-pkt-debug"}, false, "enable per-packet debug logging")
|
||||||
mflag.StringVar(&prof, []string{"-profile"}, "", "enable profiling and write profiles to given path")
|
mflag.StringVar(&prof, []string{"-profile"}, "", "enable profiling and write profiles to given path")
|
||||||
mflag.IntVar(&config.ConnLimit, []string{"-conn-limit"}, 30, "connection limit (0 for unlimited)")
|
mflag.IntVar(&config.ConnLimit, []string{"-conn-limit"}, 100, "connection limit (0 for unlimited)")
|
||||||
mflag.BoolVar(&noDiscovery, []string{"-no-discovery"}, false, "disable peer discovery")
|
mflag.BoolVar(&noDiscovery, []string{"-no-discovery"}, false, "disable peer discovery")
|
||||||
mflag.IntVar(&bufSzMB, []string{"-bufsz"}, 8, "capture buffer size in MB")
|
mflag.IntVar(&bufSzMB, []string{"-bufsz"}, 8, "capture buffer size in MB")
|
||||||
mflag.IntVar(&bridgeConfig.MTU, []string{"-mtu"}, 0, "MTU size")
|
mflag.IntVar(&bridgeConfig.MTU, []string{"-mtu"}, 0, "MTU size")
|
||||||
mflag.StringVar(&httpAddr, []string{"-http-addr"}, "", "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
|
mflag.StringVar(&httpAddr, []string{"-http-addr"}, "", "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||||
mflag.StringVar(&statusAddr, []string{"-status-addr"}, "", "address to bind status+metrics interface to (disabled if blank, absolute path indicates unix domain socket)")
|
mflag.StringVar(&statusAddr, []string{"-status-addr"}, "", "address to bind status+metrics interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||||
|
mflag.StringVar(&metricsAddr, []string{"-metrics-addr"}, "", "address to bind metrics interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||||
mflag.StringVar(&ipamConfig.Mode, []string{"-ipalloc-init"}, "", "allocator initialisation strategy (consensus, seed or observer)")
|
mflag.StringVar(&ipamConfig.Mode, []string{"-ipalloc-init"}, "", "allocator initialisation strategy (consensus, seed or observer)")
|
||||||
mflag.StringVar(&ipamConfig.IPRangeCIDR, []string{"-ipalloc-range"}, "", "IP address range reserved for automatic allocation, in CIDR notation")
|
mflag.StringVar(&ipamConfig.IPRangeCIDR, []string{"-ipalloc-range"}, "", "IP address range reserved for automatic allocation, in CIDR notation")
|
||||||
mflag.StringVar(&ipamConfig.IPSubnetCIDR, []string{"-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
|
mflag.StringVar(&ipamConfig.IPSubnetCIDR, []string{"-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
|
||||||
@@ -191,10 +204,10 @@ func main() {
|
|||||||
mflag.StringVar(&dnsConfig.ListenAddress, []string{"-dns-listen-address"}, nameserver.DefaultListenAddress, "address to listen on for DNS requests")
|
mflag.StringVar(&dnsConfig.ListenAddress, []string{"-dns-listen-address"}, nameserver.DefaultListenAddress, "address to listen on for DNS requests")
|
||||||
mflag.IntVar(&dnsConfig.TTL, []string{"-dns-ttl"}, nameserver.DefaultTTL, "TTL for DNS request from our domain")
|
mflag.IntVar(&dnsConfig.TTL, []string{"-dns-ttl"}, nameserver.DefaultTTL, "TTL for DNS request from our domain")
|
||||||
mflag.DurationVar(&dnsConfig.ClientTimeout, []string{"-dns-fallback-timeout"}, nameserver.DefaultClientTimeout, "timeout for fallback DNS requests")
|
mflag.DurationVar(&dnsConfig.ClientTimeout, []string{"-dns-fallback-timeout"}, nameserver.DefaultClientTimeout, "timeout for fallback DNS requests")
|
||||||
mflag.StringVar(&dnsConfig.EffectiveListenAddress, []string{"-dns-effective-listen-address"}, "", "address DNS will actually be listening, after Docker port mapping")
|
|
||||||
mflag.StringVar(&dnsConfig.ResolvConf, []string{"-resolv-conf"}, "", "path to resolver configuration for fallback DNS lookups")
|
mflag.StringVar(&dnsConfig.ResolvConf, []string{"-resolv-conf"}, "", "path to resolver configuration for fallback DNS lookups")
|
||||||
mflag.StringVar(&bridgeConfig.DatapathName, []string{"-datapath"}, "", "ODP datapath name")
|
mflag.StringVar(&bridgeConfig.DatapathName, []string{"-datapath"}, "", "ODP datapath name")
|
||||||
mflag.BoolVar(&bridgeConfig.NoFastdp, []string{"-no-fastdp"}, false, "Disable Fast Datapath")
|
mflag.BoolVar(&bridgeConfig.NoFastdp, []string{"-no-fastdp"}, false, "Disable Fast Datapath")
|
||||||
|
mflag.BoolVar(&bridgeConfig.NoBridgedFastdp, []string{"-no-bridged-fastdp"}, false, "Disable Bridged Fast Datapath")
|
||||||
mflag.StringVar(&trustedSubnetStr, []string{"-trusted-subnets"}, "", "comma-separated list of trusted subnets in CIDR notation")
|
mflag.StringVar(&trustedSubnetStr, []string{"-trusted-subnets"}, "", "comma-separated list of trusted subnets in CIDR notation")
|
||||||
mflag.StringVar(&dbPrefix, []string{"-db-prefix"}, "/weavedb/weave", "pathname/prefix of filename to store data")
|
mflag.StringVar(&dbPrefix, []string{"-db-prefix"}, "/weavedb/weave", "pathname/prefix of filename to store data")
|
||||||
mflag.StringVar(&procPath, []string{"-proc-path"}, "/proc", "path to reach host /proc filesystem")
|
mflag.StringVar(&procPath, []string{"-proc-path"}, "/proc", "path to reach host /proc filesystem")
|
||||||
@@ -209,12 +222,9 @@ func main() {
|
|||||||
mflag.BoolVar(&pluginConfig.EnableV2Multicast, []string{"-plugin-v2-multicast"}, false, "enable multicast for Docker plugin (v2)")
|
mflag.BoolVar(&pluginConfig.EnableV2Multicast, []string{"-plugin-v2-multicast"}, false, "enable multicast for Docker plugin (v2)")
|
||||||
mflag.StringVar(&pluginConfig.Socket, []string{"-plugin-socket"}, "/run/docker/plugins/weave.sock", "plugin socket on which to listen")
|
mflag.StringVar(&pluginConfig.Socket, []string{"-plugin-socket"}, "/run/docker/plugins/weave.sock", "plugin socket on which to listen")
|
||||||
mflag.StringVar(&pluginConfig.MeshSocket, []string{"-plugin-mesh-socket"}, "/run/docker/plugins/weavemesh.sock", "plugin socket on which to listen in mesh mode")
|
mflag.StringVar(&pluginConfig.MeshSocket, []string{"-plugin-mesh-socket"}, "/run/docker/plugins/weavemesh.sock", "plugin socket on which to listen in mesh mode")
|
||||||
|
mflag.BoolVar(&bridgeConfig.NoMasqLocal, []string{"-no-masq-local"}, false, "do not SNAT external traffic sent to containers running on this node")
|
||||||
|
|
||||||
proxyConfig := configureProxy(version, defaultDockerHost)
|
proxyConfig := newProxyConfig()
|
||||||
if bridgeConfig.AWSVPC {
|
|
||||||
proxyConfig.NoMulticastRoute = true
|
|
||||||
proxyConfig.KeepTXOn = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// crude way of detecting that we probably have been started in a
|
// crude way of detecting that we probably have been started in a
|
||||||
// container, with `weave launch` --> suppress misleading paths in
|
// container, with `weave launch` --> suppress misleading paths in
|
||||||
@@ -230,6 +240,17 @@ func main() {
|
|||||||
fmt.Printf("weave %s\n", version)
|
fmt.Printf("weave %s\n", version)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
name := peerName(routerName, bridgeConfig.WeaveBridgeName, dbPrefix, hostRoot)
|
||||||
|
if justPeerName {
|
||||||
|
fmt.Printf("%s\n", name)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyConfig.DockerHost = dockerAPI
|
||||||
|
if bridgeConfig.AWSVPC {
|
||||||
|
proxyConfig.NoMulticastRoute = true
|
||||||
|
proxyConfig.KeepTXOn = true
|
||||||
|
}
|
||||||
|
|
||||||
peers = mflag.Args()
|
peers = mflag.Args()
|
||||||
if resume && len(peers) > 0 {
|
if resume && len(peers) > 0 {
|
||||||
@@ -254,6 +275,8 @@ func main() {
|
|||||||
var proxy *weaveproxy.Proxy
|
var proxy *weaveproxy.Proxy
|
||||||
var err error
|
var err error
|
||||||
if proxyConfig.Enabled {
|
if proxyConfig.Enabled {
|
||||||
|
proxyConfig.DNSListenAddress = dnsConfig.addressOnly()
|
||||||
|
proxyConfig.DNSDomain = dnsConfig.Domain
|
||||||
if noDNS {
|
if noDNS {
|
||||||
proxyConfig.WithoutDNS = true
|
proxyConfig.WithoutDNS = true
|
||||||
}
|
}
|
||||||
@@ -282,11 +305,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
name := peerName(routerName, bridgeConfig.WeaveBridgeName, dbPrefix, hostRoot)
|
|
||||||
|
|
||||||
bridgeConfig.Mac = name.String()
|
bridgeConfig.Mac = name.String()
|
||||||
bridgeConfig.Port = config.Port
|
bridgeConfig.Port = config.Port
|
||||||
bridgeType, err := weavenet.EnsureBridge(procPath, &bridgeConfig, Log)
|
ips := ipset.New(common.LogLogger(), 0)
|
||||||
|
bridgeType, err := weavenet.EnsureBridge(procPath, &bridgeConfig, Log, ips)
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
Log.Println("Bridge type is", bridgeType)
|
Log.Println("Bridge type is", bridgeType)
|
||||||
|
|
||||||
@@ -317,12 +339,15 @@ func main() {
|
|||||||
if bridgeConfig.AWSVPC && !ipamConfig.Enabled() {
|
if bridgeConfig.AWSVPC && !ipamConfig.Enabled() {
|
||||||
Log.Fatalf("--awsvpc mode requires IPAM enabled")
|
Log.Fatalf("--awsvpc mode requires IPAM enabled")
|
||||||
}
|
}
|
||||||
|
if bridgeConfig.AWSVPC && bridgeConfig.NoMasqLocal {
|
||||||
|
Log.Fatalf("--awsvpc mode is not compatible with the --no-masq-local option")
|
||||||
|
}
|
||||||
|
|
||||||
db, err := db.NewBoltDB(dbPrefix)
|
db, err := db.NewBoltDB(dbPrefix)
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
router, err := weave.NewNetworkRouter(config, networkConfig, name, nickName, overlay, db)
|
router, err := weave.NewNetworkRouter(config, networkConfig, bridgeConfig, name, nickName, overlay, db)
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
Log.Println("Our name is", router.Ourself)
|
Log.Println("Our name is", router.Ourself)
|
||||||
|
|
||||||
@@ -376,17 +401,19 @@ func main() {
|
|||||||
var (
|
var (
|
||||||
allocator *ipam.Allocator
|
allocator *ipam.Allocator
|
||||||
defaultSubnet address.CIDR
|
defaultSubnet address.CIDR
|
||||||
trackerName string
|
|
||||||
)
|
)
|
||||||
if ipamConfig.Enabled() {
|
if ipamConfig.Enabled() {
|
||||||
var t tracker.LocalRangeTracker
|
var t tracker.LocalRangeTracker
|
||||||
if bridgeConfig.AWSVPC {
|
if bridgeConfig.AWSVPC {
|
||||||
Log.Infoln("Creating AWSVPC LocalRangeTracker")
|
|
||||||
t, err = tracker.NewAWSVPCTracker(bridgeConfig.WeaveBridgeName)
|
t, err = tracker.NewAWSVPCTracker(bridgeConfig.WeaveBridgeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatalf("Cannot create AWSVPC LocalRangeTracker: %s", err)
|
Log.Fatalf("Cannot create AWSVPC LocalRangeTracker: %s", err)
|
||||||
}
|
}
|
||||||
trackerName = "awsvpc"
|
} else if bridgeConfig.NoMasqLocal {
|
||||||
|
t = weavenet.NewNoMasqLocalTracker(ips)
|
||||||
|
}
|
||||||
|
if t != nil {
|
||||||
|
Log.Infof("Using %q LocalRangeTracker", t)
|
||||||
}
|
}
|
||||||
|
|
||||||
preClaims, err := findExistingAddresses(dockerCli, bridgeConfig.WeaveBridgeName)
|
preClaims, err := findExistingAddresses(dockerCli, bridgeConfig.WeaveBridgeName)
|
||||||
@@ -434,7 +461,7 @@ func main() {
|
|||||||
if httpAddr != "" {
|
if httpAddr != "" {
|
||||||
muxRouter := mux.NewRouter()
|
muxRouter := mux.NewRouter()
|
||||||
if allocator != nil {
|
if allocator != nil {
|
||||||
allocator.HandleHTTP(muxRouter, defaultSubnet, trackerName, dockerCli)
|
allocator.HandleHTTP(muxRouter, defaultSubnet, dockerCli)
|
||||||
}
|
}
|
||||||
if ns != nil {
|
if ns != nil {
|
||||||
ns.HandleHTTP(muxRouter, dockerCli)
|
ns.HandleHTTP(muxRouter, dockerCli)
|
||||||
@@ -457,10 +484,17 @@ func main() {
|
|||||||
muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver))
|
muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver))
|
||||||
statusMux := http.NewServeMux()
|
statusMux := http.NewServeMux()
|
||||||
statusMux.Handle("/", muxRouter)
|
statusMux.Handle("/", muxRouter)
|
||||||
Log.Println("Listening for metrics requests on", statusAddr)
|
Log.Println("Listening for status+metrics requests on", statusAddr)
|
||||||
go listenAndServeHTTP(statusAddr, statusMux)
|
go listenAndServeHTTP(statusAddr, statusMux)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if metricsAddr != "" {
|
||||||
|
metricsMux := http.NewServeMux()
|
||||||
|
metricsMux.Handle("/metrics", metricsHandler(router, allocator, ns, dnsserver))
|
||||||
|
Log.Println("Listening for metrics requests on", metricsAddr)
|
||||||
|
go listenAndServeHTTP(metricsAddr, metricsMux)
|
||||||
|
}
|
||||||
|
|
||||||
if plugin != nil {
|
if plugin != nil {
|
||||||
go plugin.Start(httpAddr, dockerCli, waitReady.Add())
|
go plugin.Start(httpAddr, dockerCli, waitReady.Add())
|
||||||
}
|
}
|
||||||
@@ -469,17 +503,17 @@ func main() {
|
|||||||
// Run this on its own goroutine because the allocator can block
|
// Run this on its own goroutine because the allocator can block
|
||||||
// We remove the default route installed by the kernel,
|
// We remove the default route installed by the kernel,
|
||||||
// because awsvpc has installed it as well
|
// because awsvpc has installed it as well
|
||||||
go expose(allocator, defaultSubnet, bridgeConfig.WeaveBridgeName, bridgeConfig.AWSVPC, waitReady.Add())
|
go exposeForAWSVPC(allocator, defaultSubnet, bridgeConfig.WeaveBridgeName, waitReady.Add())
|
||||||
}
|
}
|
||||||
|
|
||||||
signals.SignalHandlerLoop(common.Log, router)
|
signals.SignalHandlerLoop(common.Log, router)
|
||||||
}
|
}
|
||||||
|
|
||||||
func expose(alloc *ipam.Allocator, subnet address.CIDR, bridgeName string, removeDefaultRoute bool, ready func()) {
|
func exposeForAWSVPC(alloc *ipam.Allocator, subnet address.CIDR, bridgeName string, ready func()) {
|
||||||
addr, err := alloc.Allocate("weave:expose", subnet, false, func() bool { return false })
|
addr, err := alloc.Allocate("weave:expose", subnet, false, func() bool { return false })
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
cidr := address.MakeCIDR(subnet, addr)
|
cidr := address.MakeCIDR(subnet, addr)
|
||||||
err = weavenet.AddBridgeAddr(bridgeName, cidr.IPNet(), removeDefaultRoute)
|
err = weavenet.Expose(bridgeName, cidr.IPNet(), true, false)
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
Log.Printf("Bridge %q exposed on address %v", bridgeName, cidr)
|
Log.Printf("Bridge %q exposed on address %v", bridgeName, cidr)
|
||||||
ready()
|
ready()
|
||||||
@@ -525,12 +559,10 @@ func (nopPacketLogging) LogPacket(string, weave.PacketKey) {
|
|||||||
func (nopPacketLogging) LogForwardPacket(string, weave.ForwardPacketKey) {
|
func (nopPacketLogging) LogForwardPacket(string, weave.ForwardPacketKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureProxy(version string, defaultDockerHost string) *weaveproxy.Config {
|
func newProxyConfig() *weaveproxy.Config {
|
||||||
proxyConfig := weaveproxy.Config{
|
proxyConfig := weaveproxy.Config{
|
||||||
Version: version,
|
|
||||||
Image: getenvOrDefault("EXEC_IMAGE", "weaveworks/weaveexec"),
|
Image: getenvOrDefault("EXEC_IMAGE", "weaveworks/weaveexec"),
|
||||||
DockerBridge: getenvOrDefault("DOCKER_BRIDGE", "docker0"),
|
DockerBridge: getenvOrDefault("DOCKER_BRIDGE", "docker0"),
|
||||||
DockerHost: defaultDockerHost,
|
|
||||||
}
|
}
|
||||||
mflag.BoolVar(&proxyConfig.Enabled, []string{"-proxy"}, false, "instruct Weave Net to start its Docker proxy")
|
mflag.BoolVar(&proxyConfig.Enabled, []string{"-proxy"}, false, "instruct Weave Net to start its Docker proxy")
|
||||||
mflagext.ListVar(&proxyConfig.ListenAddrs, []string{"H"}, nil, "addresses on which to listen for Docker proxy")
|
mflagext.ListVar(&proxyConfig.ListenAddrs, []string{"H"}, nil, "addresses on which to listen for Docker proxy")
|
||||||
@@ -630,17 +662,13 @@ func createDNSServer(config dnsConfig, router *mesh.Router, isKnownPeer func(mes
|
|||||||
gossip, err := router.NewGossip("nameserver", ns)
|
gossip, err := router.NewGossip("nameserver", ns)
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
ns.SetGossip(gossip)
|
ns.SetGossip(gossip)
|
||||||
upstream := nameserver.NewUpstream(config.ResolvConf, config.EffectiveListenAddress)
|
upstream := nameserver.NewUpstream(config.ResolvConf, config.addressOnly())
|
||||||
dnsserver, err := nameserver.NewDNSServer(ns, config.Domain, config.ListenAddress,
|
dnsserver, err := nameserver.NewDNSServer(ns, config.Domain, config.ListenAddress,
|
||||||
upstream, uint32(config.TTL), config.ClientTimeout)
|
upstream, uint32(config.TTL), config.ClientTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatal("Unable to start dns server: ", err)
|
Log.Fatal("Unable to start dns server: ", err)
|
||||||
}
|
}
|
||||||
listenAddr := config.ListenAddress
|
Log.Println("Listening for DNS queries on", config.ListenAddress)
|
||||||
if config.EffectiveListenAddress != "" {
|
|
||||||
listenAddr = config.EffectiveListenAddress
|
|
||||||
}
|
|
||||||
Log.Println("Listening for DNS queries on", listenAddr)
|
|
||||||
return ns, dnsserver
|
return ns, dnsserver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,27 @@ var metrics = []metric{
|
|||||||
ch <- intGauge(desc, metrics.Flows)
|
ch <- intGauge(desc, metrics.Flows)
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
|
{desc("weave_ipam_unreachable_count", "Number of unreachable peers."),
|
||||||
|
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
|
||||||
|
var count int
|
||||||
|
for _, entry := range summariseIpamStats(s.IPAM) {
|
||||||
|
if !entry.reachable {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ch <- intGauge(desc, count)
|
||||||
|
}},
|
||||||
|
{desc("weave_ipam_unreachable_percentage", "Percentage of IP addresses owned by unreachable peers."),
|
||||||
|
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
|
||||||
|
var totalUnreachable uint32
|
||||||
|
for _, entry := range summariseIpamStats(s.IPAM) {
|
||||||
|
if !entry.reachable {
|
||||||
|
totalUnreachable += entry.ips
|
||||||
|
}
|
||||||
|
}
|
||||||
|
percentage := float64(totalUnreachable) * 100.0 / float64(s.IPAM.RangeNumIPs)
|
||||||
|
ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, percentage)
|
||||||
|
}},
|
||||||
{desc("weave_ipam_pending_allocates", "Number of pending allocates."),
|
{desc("weave_ipam_pending_allocates", "Number of pending allocates."),
|
||||||
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
|
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
|
||||||
if s.IPAM != nil {
|
if s.IPAM != nil {
|
||||||
|
|||||||
+21
-32
@@ -4,11 +4,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
docker "github.com/fsouza/go-dockerclient"
|
|
||||||
"github.com/vishvananda/netns"
|
"github.com/vishvananda/netns"
|
||||||
|
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
@@ -16,8 +14,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func attach(args []string) error {
|
func attach(args []string) error {
|
||||||
if len(args) < 4 {
|
if len(args) < 3 {
|
||||||
cmdUsage("attach-container", "[--no-multicast-route] [--keep-tx-on] [--hairpin-mode=true|false] <container-id> <bridge-name> <mtu> <cidr>...")
|
cmdUsage("attach-container", "[--no-multicast-route] [--keep-tx-on] [--hairpin-mode=true|false] <container-id> <bridge-name> <cidr>...")
|
||||||
}
|
}
|
||||||
|
|
||||||
keepTXOn := false
|
keepTXOn := false
|
||||||
@@ -53,16 +51,12 @@ func attach(args []string) error {
|
|||||||
} else if nsHost.Equal(nsContainer) {
|
} else if nsHost.Equal(nsContainer) {
|
||||||
return fmt.Errorf("Container is running in the host network namespace, and therefore cannot be\nconnected to weave - perhaps the container was started with --net=host")
|
return fmt.Errorf("Container is running in the host network namespace, and therefore cannot be\nconnected to weave - perhaps the container was started with --net=host")
|
||||||
}
|
}
|
||||||
mtu, err := strconv.Atoi(args[2])
|
cidrs, err := parseCIDRs(args[2:])
|
||||||
if err != nil && args[3] != "" {
|
|
||||||
return fmt.Errorf("unable to parse mtu %q: %s", args[2], err)
|
|
||||||
}
|
|
||||||
cidrs, err := parseCIDRs(args[3:])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = weavenet.AttachContainer(weavenet.NSPathByPid(pid), fmt.Sprint(pid), weavenet.VethName, args[1], mtu, withMulticastRoute, cidrs, keepTXOn, hairpinMode)
|
err = weavenet.AttachContainer(weavenet.NSPathByPid(pid), fmt.Sprint(pid), weavenet.VethName, args[1], 0, withMulticastRoute, cidrs, keepTXOn, hairpinMode)
|
||||||
// If we detected an error but the container has died, tell the user that instead.
|
// If we detected an error but the container has died, tell the user that instead.
|
||||||
if err != nil && !processExists(pid) {
|
if err != nil && !processExists(pid) {
|
||||||
err = fmt.Errorf("Container %s died", args[0])
|
err = fmt.Errorf("Container %s died", args[0])
|
||||||
@@ -70,21 +64,6 @@ func attach(args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func containerPid(containerID string) (int, error) {
|
|
||||||
c, err := docker.NewVersionedClientFromEnv("1.18")
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("unable to connect to docker: %s", err)
|
|
||||||
}
|
|
||||||
container, err := c.InspectContainer(containerID)
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("unable to inspect container %s: %s", containerID, err)
|
|
||||||
}
|
|
||||||
if container.State.Pid == 0 {
|
|
||||||
return 0, fmt.Errorf("container %s not running", containerID)
|
|
||||||
}
|
|
||||||
return container.State.Pid, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func processExists(pid int) bool {
|
func processExists(pid int) bool {
|
||||||
err := syscall.Kill(pid, syscall.Signal(0))
|
err := syscall.Kill(pid, syscall.Signal(0))
|
||||||
return err == nil || err == syscall.EPERM
|
return err == nil || err == syscall.EPERM
|
||||||
@@ -119,21 +98,31 @@ func detach(args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func rewriteEtcHosts(args []string) error {
|
func rewriteEtcHosts(args []string) error {
|
||||||
if len(args) < 4 {
|
if len(args) < 3 {
|
||||||
cmdUsage("rewrite-etc-hosts", "<hosts-path> <fqdn> <image> <cidr>... [name:addr...]")
|
cmdUsage("rewrite-etc-hosts", "<container-id> <image> <cidr> [name:addr...]")
|
||||||
}
|
}
|
||||||
hostsPath := args[0]
|
containerID := args[0]
|
||||||
fqdn := args[1]
|
image := args[1]
|
||||||
image := args[2]
|
cidrs := args[2]
|
||||||
|
extraHosts := args[3:]
|
||||||
|
|
||||||
|
container, err := inspectContainer(containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
hostsPath := container.HostsPath
|
||||||
|
fqdn := container.Config.Hostname + "." + container.Config.Domainname
|
||||||
|
|
||||||
var ips []*net.IPNet
|
var ips []*net.IPNet
|
||||||
for _, cidr := range strings.Fields(args[3]) {
|
for _, cidr := range strings.Fields(cidrs) {
|
||||||
_, ipnet, err := net.ParseCIDR(cidr)
|
_, ipnet, err := net.ParseCIDR(cidr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ips = append(ips, ipnet)
|
ips = append(ips, ipnet)
|
||||||
}
|
}
|
||||||
extraHosts := args[4:]
|
|
||||||
docker := os.Getenv("DOCKER_HOST")
|
docker := os.Getenv("DOCKER_HOST")
|
||||||
if docker == "" {
|
if docker == "" {
|
||||||
docker = "unix:///var/run/docker.sock"
|
docker = "unix:///var/run/docker.sock"
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,36 +20,3 @@ func detectBridgeType(args []string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createBridge(args []string) error {
|
|
||||||
if len(args) != 10 {
|
|
||||||
cmdUsage("create-bridge", "<docker-bridge-name> <weave-bridge-name> <datapath-name> <mtu> <port> <mac> <no-fastdp> <no-bridged-fastdp> <proc-path> <expect-npc>")
|
|
||||||
}
|
|
||||||
|
|
||||||
mtu, err := strconv.Atoi(args[3])
|
|
||||||
if err != nil && args[3] != "" {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
port, err := strconv.Atoi(args[4])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
config := weavenet.BridgeConfig{
|
|
||||||
DockerBridgeName: args[0],
|
|
||||||
WeaveBridgeName: args[1],
|
|
||||||
DatapathName: args[2],
|
|
||||||
MTU: mtu,
|
|
||||||
Port: port,
|
|
||||||
Mac: args[5],
|
|
||||||
NoFastdp: args[6] != "",
|
|
||||||
NoBridgedFastdp: args[7] != "",
|
|
||||||
NPC: args[9] == "--expect-npc",
|
|
||||||
}
|
|
||||||
procPath := args[8]
|
|
||||||
bridgeType, err := weavenet.EnsureBridge(procPath, &config, common.Log)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println(bridgeType.String())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,304 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
docker "github.com/fsouza/go-dockerclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
func inspectContainer(containerNameOrID string) (*docker.Container, error) {
|
||||||
|
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to connect to docker: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
container, err := c.InspectContainer(containerNameOrID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to inspect container %s: %s", containerNameOrID, err)
|
||||||
|
}
|
||||||
|
return container, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func containerPid(containerID string) (int, error) {
|
||||||
|
container, err := inspectContainer(containerID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if container.State.Pid == 0 {
|
||||||
|
return 0, fmt.Errorf("container %s not running", containerID)
|
||||||
|
}
|
||||||
|
return container.State.Pid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func containerID(args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
cmdUsage("container-id", "<container-name-or-short-id>")
|
||||||
|
}
|
||||||
|
|
||||||
|
container, err := inspectContainer(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print(container.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func containerState(args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
cmdUsage("container-state", "<container-id> [<image-prefix>]")
|
||||||
|
}
|
||||||
|
|
||||||
|
container, err := inspectContainer(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) == 1 {
|
||||||
|
fmt.Print(container.State.StateString())
|
||||||
|
} else {
|
||||||
|
imagePrefix := args[1]
|
||||||
|
if strings.HasPrefix(container.Config.Image, imagePrefix) {
|
||||||
|
fmt.Print(container.State.StateString())
|
||||||
|
} else {
|
||||||
|
if container.State.Running {
|
||||||
|
fmt.Print("running image mismatch: ", container.Config.Image)
|
||||||
|
} else {
|
||||||
|
fmt.Print("image mismatch: ", container.Config.Image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func containerFQDN(args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
cmdUsage("container-fqdn", "<container-id>")
|
||||||
|
}
|
||||||
|
|
||||||
|
container, err := inspectContainer(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print(container.Config.Hostname, ".", container.Config.Domainname)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseContainerArgs(args []string) docker.CreateContainerOptions {
|
||||||
|
env := []string{}
|
||||||
|
labels := map[string]string{}
|
||||||
|
name := ""
|
||||||
|
net := ""
|
||||||
|
pid := ""
|
||||||
|
privileged := false
|
||||||
|
restart := docker.NeverRestart()
|
||||||
|
volumes := []string{}
|
||||||
|
volumesFrom := []string{}
|
||||||
|
|
||||||
|
done := false
|
||||||
|
for i := 0; i < len(args) && !done; {
|
||||||
|
switch args[i] {
|
||||||
|
case "-e", "--env":
|
||||||
|
if v := os.Getenv(args[i+1]); v != "" {
|
||||||
|
env = append(env, args[i+1]+"="+v)
|
||||||
|
} else {
|
||||||
|
env = append(env, args[i+1])
|
||||||
|
}
|
||||||
|
args = append(args[:i], args[i+2:]...)
|
||||||
|
case "-l", "--label":
|
||||||
|
key, value := parseLabel(args[i+1])
|
||||||
|
labels[key] = value
|
||||||
|
args = append(args[:i], args[i+2:]...)
|
||||||
|
case "--name":
|
||||||
|
name = args[i+1]
|
||||||
|
args = append(args[:i], args[i+2:]...)
|
||||||
|
case "--net":
|
||||||
|
net = args[i+1]
|
||||||
|
args = append(args[:i], args[i+2:]...)
|
||||||
|
case "--pid":
|
||||||
|
pid = args[i+1]
|
||||||
|
args = append(args[:i], args[i+2:]...)
|
||||||
|
case "--privileged":
|
||||||
|
privileged = true
|
||||||
|
args = append(args[:i], args[i+1:]...)
|
||||||
|
case "--restart":
|
||||||
|
restart = docker.RestartPolicy{Name: args[i+1]}
|
||||||
|
args = append(args[:i], args[i+2:]...)
|
||||||
|
case "-v", "--volume":
|
||||||
|
// Must dedup binds, otherwise, container fails creation
|
||||||
|
skip := false
|
||||||
|
for _, v := range volumes {
|
||||||
|
if v == args[i+1] {
|
||||||
|
skip = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !skip {
|
||||||
|
volumes = append(volumes, args[i+1])
|
||||||
|
}
|
||||||
|
args = append(args[:i], args[i+2:]...)
|
||||||
|
case "--volumes-from":
|
||||||
|
volumesFrom = append(volumesFrom, args[i+1])
|
||||||
|
args = append(args[:i], args[i+2:]...)
|
||||||
|
default:
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) < 2 {
|
||||||
|
cmdUsage("run-container", `[options] <image> <cmd> [[<cmd-options>] <cmd-arg1> [<cmd-arg2> ...]]
|
||||||
|
|
||||||
|
-e, --env list Set environment variables
|
||||||
|
--name string Assign a name to the container
|
||||||
|
--net string Network Mode
|
||||||
|
--pid string PID namespace to use
|
||||||
|
--privileged Give extended privileges to this container
|
||||||
|
--restart string Restart policy to apply when a container exits (default "no")
|
||||||
|
-v, --volume list Bind mount a volume
|
||||||
|
--volumes-from list Mount volumes from the specified container(s)
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
image := args[0]
|
||||||
|
cmds := args[1:]
|
||||||
|
|
||||||
|
config := docker.Config{Image: image, Env: env, Cmd: cmds, Labels: labels}
|
||||||
|
hostConfig := docker.HostConfig{NetworkMode: net, PidMode: pid, Privileged: privileged, RestartPolicy: restart, Binds: volumes, VolumesFrom: volumesFrom}
|
||||||
|
return docker.CreateContainerOptions{Name: name, Config: &config, HostConfig: &hostConfig}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runContainer(args []string) error {
|
||||||
|
containerOptions := parseContainerArgs(args)
|
||||||
|
|
||||||
|
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to connect to docker: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
container, err := c.CreateContainer(containerOptions)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create container: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.StartContainer(container.ID, containerOptions.HostConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to start container: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print(container.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLabel(s string) (key, value string) {
|
||||||
|
pos := strings.Index(s, "=")
|
||||||
|
if pos == -1 { // no value - set it to blank
|
||||||
|
return s, ""
|
||||||
|
}
|
||||||
|
return s[:pos], s[pos+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func listContainers(args []string) error {
|
||||||
|
if len(args) > 1 {
|
||||||
|
cmdUsage("list-containers", "[<label>]")
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to connect to docker: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := docker.ListContainersOptions{All: true}
|
||||||
|
if len(args) == 1 {
|
||||||
|
label := args[0]
|
||||||
|
opts.Filters = map[string][]string{"label": {label}}
|
||||||
|
}
|
||||||
|
|
||||||
|
containers, err := c.ListContainers(opts)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to list containers: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, container := range containers {
|
||||||
|
fmt.Println(container.ID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopContainer(args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
cmdUsage("stop-container", "<container-id> [<container-id2> ...]")
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to connect to docker: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, containerID := range args {
|
||||||
|
err = c.StopContainer(containerID, 10)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to stop container %s: %s", containerID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func killContainer(args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
cmdUsage("kill-container", "<container-id> [<container-id2> ...]")
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to connect to docker: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, containerID := range args {
|
||||||
|
err = c.KillContainer(docker.KillContainerOptions{ID: containerID, Signal: docker.SIGKILL})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to stop container %s: %s", containerID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeContainer(args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
cmdUsage("remove-container", "[-f | --force] [-v | --volumes] <container-id> [<container-id2> ...]")
|
||||||
|
}
|
||||||
|
|
||||||
|
force := false
|
||||||
|
volumes := false
|
||||||
|
for i := 0; i < len(args); {
|
||||||
|
switch args[i] {
|
||||||
|
case "--force":
|
||||||
|
case "-f":
|
||||||
|
force = true
|
||||||
|
args = append(args[:i], args[i+1:]...)
|
||||||
|
case "--volumes":
|
||||||
|
case "-v":
|
||||||
|
volumes = true
|
||||||
|
args = append(args[:i], args[i+1:]...)
|
||||||
|
default:
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to connect to docker: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, containerID := range args {
|
||||||
|
err = c.RemoveContainer(docker.RemoveContainerOptions{
|
||||||
|
ID: containerID, Force: force, RemoveVolumes: volumes})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to stop container %s: %s", containerID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
docker "github.com/fsouza/go-dockerclient"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseContainerArgs(t *testing.T) {
|
||||||
|
args := []string{"--name", "weave", "--privileged", "--net", "host",
|
||||||
|
"-l", "foo=bar", "-l", "foobar",
|
||||||
|
"-v", "/var/run/docker.sock:/var/run/docker.sock", "-v", "/etc:/host/etc",
|
||||||
|
"--restart", "always", "--pid", "host", "--volumes-from", "weavedb",
|
||||||
|
"-e", "WEAVE_DEBUG", "-e", "EXEC_IMAGE=weaveworks/weaveexec:latest",
|
||||||
|
"weaveworks/weave:latest", "cmd", "arg1", "arg2"}
|
||||||
|
expected := docker.CreateContainerOptions{
|
||||||
|
Name: "weave",
|
||||||
|
Config: &docker.Config{
|
||||||
|
Image: "weaveworks/weave:latest",
|
||||||
|
Env: []string{"WEAVE_DEBUG", "EXEC_IMAGE=weaveworks/weaveexec:latest"},
|
||||||
|
Cmd: []string{"cmd", "arg1", "arg2"},
|
||||||
|
Labels: map[string]string{"foo": "bar", "foobar": ""},
|
||||||
|
},
|
||||||
|
HostConfig: &docker.HostConfig{
|
||||||
|
NetworkMode: "host",
|
||||||
|
PidMode: "host",
|
||||||
|
Privileged: true,
|
||||||
|
RestartPolicy: docker.RestartPolicy{Name: "always"},
|
||||||
|
Binds: []string{"/var/run/docker.sock:/var/run/docker.sock", "/etc:/host/etc"},
|
||||||
|
VolumesFrom: []string{"weavedb"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
have := parseContainerArgs(args)
|
||||||
|
require.Equal(t, expected, have)
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// Get/set arbitrary flags in the Weave Net persistence DB
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
const dbFlagPrefix = "flag:"
|
||||||
|
|
||||||
|
func getDBFlag(args []string) error {
|
||||||
|
if len(args) != 2 {
|
||||||
|
cmdUsage("check-db-flag", "<db-prefix> <flag-name>")
|
||||||
|
}
|
||||||
|
dbPrefix := args[0]
|
||||||
|
flagName := args[1]
|
||||||
|
|
||||||
|
d, err := db.NewBoltDBReadOnly(dbPrefix)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer d.Close()
|
||||||
|
var value string
|
||||||
|
nameFound, err := d.Load(dbFlagPrefix+flagName, &value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !nameFound {
|
||||||
|
return fmt.Errorf("")
|
||||||
|
}
|
||||||
|
fmt.Print(value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDBFlag(args []string) error {
|
||||||
|
if len(args) != 3 {
|
||||||
|
cmdUsage("set-db-flag", "<db-prefix> <flag-name> <flag-value>")
|
||||||
|
}
|
||||||
|
dbPrefix := args[0]
|
||||||
|
flagName := args[1]
|
||||||
|
value := args[2]
|
||||||
|
|
||||||
|
d, err := db.NewBoltDB(dbPrefix)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer d.Close()
|
||||||
|
err = d.Save(dbFlagPrefix+flagName, value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
|
||||||
)
|
|
||||||
|
|
||||||
func exposeNAT(args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
cmdUsage("expose-nat", "<cidr>...")
|
|
||||||
}
|
|
||||||
|
|
||||||
cidrs, err := parseCIDRs(args)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, cidr := range cidrs {
|
|
||||||
if err := weavenet.ExposeNAT(*cidr); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
+14
-20
@@ -4,10 +4,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var commands map[string]func([]string) error
|
var commands map[string]func([]string) error
|
||||||
@@ -17,7 +14,6 @@ func init() {
|
|||||||
"help": help,
|
"help": help,
|
||||||
"netcheck": netcheck,
|
"netcheck": netcheck,
|
||||||
"docker-tls-args": dockerTLSArgs,
|
"docker-tls-args": dockerTLSArgs,
|
||||||
"create-bridge": createBridge,
|
|
||||||
"detect-bridge-type": detectBridgeType,
|
"detect-bridge-type": detectBridgeType,
|
||||||
"create-datapath": createDatapath,
|
"create-datapath": createDatapath,
|
||||||
"delete-datapath": deleteDatapath,
|
"delete-datapath": deleteDatapath,
|
||||||
@@ -26,34 +22,30 @@ func init() {
|
|||||||
"remove-plugin-network": removePluginNetwork,
|
"remove-plugin-network": removePluginNetwork,
|
||||||
"container-addrs": containerAddrs,
|
"container-addrs": containerAddrs,
|
||||||
"process-addrs": processAddrs,
|
"process-addrs": processAddrs,
|
||||||
|
"container-id": containerID,
|
||||||
|
"container-state": containerState,
|
||||||
|
"container-fqdn": containerFQDN,
|
||||||
|
"run-container": runContainer,
|
||||||
|
"list-containers": listContainers,
|
||||||
|
"stop-container": stopContainer,
|
||||||
|
"kill-container": killContainer,
|
||||||
|
"remove-container": removeContainer,
|
||||||
|
"create-volume-container": createVolumeContainer,
|
||||||
"attach-container": attach,
|
"attach-container": attach,
|
||||||
"detach-container": detach,
|
"detach-container": detach,
|
||||||
"configure-arp": configureARP,
|
|
||||||
"check-iface": checkIface,
|
|
||||||
"del-iface": delIface,
|
|
||||||
"setup-iface": setupIface,
|
|
||||||
"setup-iface-addrs": setupIfaceAddrs,
|
|
||||||
"list-netdevs": listNetDevs,
|
|
||||||
"cni-net": cniNet,
|
"cni-net": cniNet,
|
||||||
"cni-ipam": cniIPAM,
|
"cni-ipam": cniIPAM,
|
||||||
"expose-nat": exposeNAT,
|
|
||||||
"bridge-ip": bridgeIP,
|
"bridge-ip": bridgeIP,
|
||||||
"unique-id": uniqueID,
|
"unique-id": uniqueID,
|
||||||
"swarm-manager-peers": swarmManagerPeers,
|
"swarm-manager-peers": swarmManagerPeers,
|
||||||
"is-docker-plugin-enabled": isDockerPluginEnabled,
|
"is-docker-plugin-enabled": isDockerPluginEnabled,
|
||||||
"rewrite-etc-hosts": rewriteEtcHosts,
|
"rewrite-etc-hosts": rewriteEtcHosts,
|
||||||
|
"get-db-flag": getDBFlag,
|
||||||
|
"set-db-flag": setDBFlag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// force re-exec of this binary
|
|
||||||
selfPath, err := filepath.EvalSymlinks("/proc/self/exe")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
weavenet.WeaveUtilCmd = selfPath
|
|
||||||
|
|
||||||
// If no args passed, act as CNI plugin based on executable name
|
// If no args passed, act as CNI plugin based on executable name
|
||||||
switch {
|
switch {
|
||||||
case len(os.Args) == 1 && strings.HasSuffix(os.Args[0], "weave-ipam"):
|
case len(os.Args) == 1 && strings.HasSuffix(os.Args[0], "weave-ipam"):
|
||||||
@@ -75,7 +67,9 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if err := cmd(os.Args[2:]); err != nil {
|
if err := cmd(os.Args[2:]); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
if err.Error() != "" {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"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]
|
|
||||||
|
|
||||||
return weavenet.SetupIface(ifaceName, newIfName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupIfaceAddrs sets up addresses on an interface. It expects to be called inside the container's netns.
|
|
||||||
func setupIfaceAddrs(args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
cmdUsage("setup-iface-addrs", "<iface-name> <with-multicast> <cidr>...")
|
|
||||||
}
|
|
||||||
link, err := netlink.LinkByName(args[0])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
withMulticastRoute, err := strconv.ParseBool(args[1])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cidrs, err := parseCIDRs(args[2:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return weavenet.SetupIfaceAddrs(link, withMulticastRoute, cidrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func configureARP(args []string) error {
|
|
||||||
if len(args) != 2 {
|
|
||||||
cmdUsage("configure-arp", "<iface-name-prefix> <root-path>")
|
|
||||||
}
|
|
||||||
prefix := args[0]
|
|
||||||
rootPath := args[1]
|
|
||||||
|
|
||||||
links, err := netlink.LinkList()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, link := range links {
|
|
||||||
ifName := link.Attrs().Name
|
|
||||||
if strings.HasPrefix(ifName, prefix) {
|
|
||||||
weavenet.ConfigureARPCache(rootPath+"/proc", 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
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
docker "github.com/fsouza/go-dockerclient"
|
docker "github.com/fsouza/go-dockerclient"
|
||||||
)
|
)
|
||||||
@@ -58,7 +59,7 @@ func isDockerPluginEnabled(args []string) error {
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
plugins, err := c.PluginList(ctx)
|
plugins, err := c.PluginList(ctx, filters.Args{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
docker "github.com/fsouza/go-dockerclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createVolumeContainer(args []string) error {
|
||||||
|
if len(args) < 3 {
|
||||||
|
cmdUsage("create-volume-container", "<name> <image-name-or-id> <label> [<bind-mount1> <bind-mount2> ...]")
|
||||||
|
}
|
||||||
|
containerName := args[0]
|
||||||
|
image := args[1]
|
||||||
|
label := args[2]
|
||||||
|
bindMounts := args[3:]
|
||||||
|
|
||||||
|
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to connect to docker: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
container, err := c.InspectContainer(containerName)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Println("volume container already exists " + containerName + ":" + container.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
volumes := make(map[string]struct{})
|
||||||
|
for _, m := range bindMounts {
|
||||||
|
volumes[m] = struct{}{}
|
||||||
|
}
|
||||||
|
labels := map[string]string{label: ""}
|
||||||
|
config := docker.Config{Image: image, Volumes: volumes, Labels: labels, Entrypoint: []string{"data-only"}}
|
||||||
|
hostConfig := docker.HostConfig{}
|
||||||
|
_, err = c.CreateContainer(docker.CreateContainerOptions{Name: containerName, Config: &config, HostConfig: &hostConfig})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create container: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -68,11 +68,11 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" {
|
if !i.proxy.WithoutDNS {
|
||||||
if err := i.setHostname(container, hostname, dnsDomain); err != nil {
|
if err := i.setHostname(container, hostname, i.proxy.DNSDomain); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := i.proxy.setWeaveDNS(hostConfig, hostname, dnsDomain); err != nil {
|
if err := i.proxy.setWeaveDNS(hostConfig, hostname, i.proxy.DNSDomain); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -20,8 +20,7 @@ func (proxy *Proxy) RewriteEtcHosts(hostsPath, fqdn string, ips []*net.IPNet, ex
|
|||||||
contents := buf.String()
|
contents := buf.String()
|
||||||
cmdLine := fmt.Sprintf("echo '%s' > %s && rm -f %s && echo '%s' > %s", contents, mntHosts, mntHosts, contents, mntHosts)
|
cmdLine := fmt.Sprintf("echo '%s' > %s && rm -f %s && echo '%s' > %s", contents, mntHosts, mntHosts, contents, mntHosts)
|
||||||
mounts := []string{hostsPathDir + ":" + mnt}
|
mounts := []string{hostsPathDir + ":" + mnt}
|
||||||
proxy.runTransientContainer([]string{"sh"}, []string{"-c", cmdLine}, mounts)
|
return proxy.runTransientContainer([]string{"sh"}, []string{"-c", cmdLine}, mounts)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we assume (for compatibility with the weave script) that fqdn has a dot
|
// we assume (for compatibility with the weave script) that fqdn has a dot
|
||||||
|
|||||||
+9
-26
@@ -60,8 +60,9 @@ type Config struct {
|
|||||||
NoDefaultIPAM bool
|
NoDefaultIPAM bool
|
||||||
NoRewriteHosts bool
|
NoRewriteHosts bool
|
||||||
TLSConfig TLSConfig
|
TLSConfig TLSConfig
|
||||||
Version string
|
|
||||||
WithoutDNS bool
|
WithoutDNS bool
|
||||||
|
DNSListenAddress string
|
||||||
|
DNSDomain string
|
||||||
NoMulticastRoute bool
|
NoMulticastRoute bool
|
||||||
KeepTXOn bool
|
KeepTXOn bool
|
||||||
DockerBridge string
|
DockerBridge string
|
||||||
@@ -79,7 +80,6 @@ type Proxy struct {
|
|||||||
Config
|
Config
|
||||||
client *weavedocker.Client
|
client *weavedocker.Client
|
||||||
weave *weaveapi.Client
|
weave *weaveapi.Client
|
||||||
dockerBridgeIP string
|
|
||||||
hostnameMatchRegexp *regexp.Regexp
|
hostnameMatchRegexp *regexp.Regexp
|
||||||
weaveWaitVolume string
|
weaveWaitVolume string
|
||||||
weaveWaitNoopVolume string
|
weaveWaitNoopVolume string
|
||||||
@@ -159,15 +159,6 @@ func NewProxy(c Config) (*Proxy, error) {
|
|||||||
|
|
||||||
Log.Info(p.client.Info())
|
Log.Info(p.client.Info())
|
||||||
|
|
||||||
if !p.WithoutDNS {
|
|
||||||
ip, err := weavenet.FindBridgeIP(c.DockerBridge, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p.dockerBridgeIP = ip.String()
|
|
||||||
Log.Infof("Using docker bridge IP for DNS: %v", p.dockerBridgeIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
p.hostnameMatchRegexp, err = regexp.Compile(c.HostnameMatch)
|
p.hostnameMatchRegexp, err = regexp.Compile(c.HostnameMatch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Incorrect hostname match '%s': %s", c.HostnameMatch, err.Error())
|
err := fmt.Errorf("Incorrect hostname match '%s': %s", c.HostnameMatch, err.Error())
|
||||||
@@ -330,11 +321,7 @@ func copyOwnerAndPermissions(from, to string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = os.Chown(to, int(moreStat.Uid), int(moreStat.Gid)); err != nil {
|
return os.Chown(to, int(moreStat.Uid), int(moreStat.Gid))
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (proxy *Proxy) listen(protoAndAddr string) (net.Listener, string, error) {
|
func (proxy *Proxy) listen(protoAndAddr string) (net.Listener, string, error) {
|
||||||
@@ -510,7 +497,10 @@ func (proxy *Proxy) attach(containerID string) error {
|
|||||||
if container.HostConfig != nil {
|
if container.HostConfig != nil {
|
||||||
extraHosts = container.HostConfig.ExtraHosts
|
extraHosts = container.HostConfig.ExtraHosts
|
||||||
}
|
}
|
||||||
proxy.RewriteEtcHosts(container.HostsPath, fqdn, ips, extraHosts)
|
if err := proxy.RewriteEtcHosts(container.HostsPath, fqdn, ips, extraHosts); err != nil {
|
||||||
|
Log.Errorf("Error from rewriting container's /etc/hosts: %s", err)
|
||||||
|
// carry on since the original /etc/hosts is good enough in most cases
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pid := container.State.Pid
|
pid := container.State.Pid
|
||||||
@@ -597,7 +587,7 @@ func (proxy *Proxy) setWeaveDNS(hostConfig jsonObject, hostname, dnsDomain strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hostConfig["Dns"] = append(dns, proxy.dockerBridgeIP)
|
hostConfig["Dns"] = append(dns, proxy.DNSListenAddress)
|
||||||
|
|
||||||
dnsSearch, err := hostConfig.StringArray("DnsSearch")
|
dnsSearch, err := hostConfig.StringArray("DnsSearch")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -614,14 +604,6 @@ func (proxy *Proxy) setWeaveDNS(hostConfig jsonObject, hostname, dnsDomain strin
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (proxy *Proxy) getDNSDomain() string {
|
|
||||||
if proxy.WithoutDNS {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
domain, _ := proxy.weave.DNSDomain()
|
|
||||||
return domain
|
|
||||||
}
|
|
||||||
|
|
||||||
func (proxy *Proxy) updateContainerNetworkSettings(container jsonObject) error {
|
func (proxy *Proxy) updateContainerNetworkSettings(container jsonObject) error {
|
||||||
containerID, err := container.String("Id")
|
containerID, err := container.String("Id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -684,6 +666,7 @@ func (proxy *Proxy) runTransientContainer(entrypoint, cmd, binds []string) (err
|
|||||||
env = append(env, fmt.Sprintf("%s=%s", "WEAVE_DEBUG", val))
|
env = append(env, fmt.Sprintf("%s=%s", "WEAVE_DEBUG", val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.Debugf("Running image %q; entrypoint=%q; cmd=%q; binds=%q", proxy.Image, entrypoint, fmt.Sprintf("%.72s", cmd), binds)
|
||||||
var container *docker.Container
|
var container *docker.Container
|
||||||
container, err = proxy.client.CreateContainer(docker.CreateContainerOptions{
|
container, err = proxy.client.CreateContainer(docker.CreateContainerOptions{
|
||||||
Config: &docker.Config{
|
Config: &docker.Config{
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ func (i *startContainerInterceptor) InterceptRequest(r *http.Request) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" {
|
if !i.proxy.WithoutDNS {
|
||||||
if err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, dnsDomain); err != nil {
|
if err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, i.proxy.DNSDomain); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
"github.com/weaveworks/weave/common"
|
||||||
|
"github.com/weaveworks/weave/net"
|
||||||
|
"github.com/weaveworks/weave/net/address"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (router *NetworkRouter) HandleHTTP(muxRouter *mux.Router) {
|
func (router *NetworkRouter) HandleHTTP(muxRouter *mux.Router) {
|
||||||
@@ -26,4 +29,20 @@ func (router *NetworkRouter) HandleHTTP(muxRouter *mux.Router) {
|
|||||||
router.ForgetConnections(r.Form["peer"])
|
router.ForgetConnections(r.Form["peer"])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
muxRouter.Methods("POST").Path("/expose/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
cidr, err := address.ParseCIDR(vars["ip"] + "/" + vars["prefixlen"])
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprint("unable to parse ip addr: ", err.Error()), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = net.Expose(router.BridgeConfig.WeaveBridgeName, cidr.IPNet(), router.BridgeConfig.AWSVPC, router.BridgeConfig.NPC); err != nil {
|
||||||
|
http.Error(w, fmt.Sprint("unable to expose: ", err.Error()), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(204)
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
"github.com/weaveworks/weave/common"
|
||||||
"github.com/weaveworks/weave/db"
|
"github.com/weaveworks/weave/db"
|
||||||
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -47,11 +48,12 @@ type PacketLogging interface {
|
|||||||
type NetworkRouter struct {
|
type NetworkRouter struct {
|
||||||
*mesh.Router
|
*mesh.Router
|
||||||
NetworkConfig
|
NetworkConfig
|
||||||
|
weavenet.BridgeConfig
|
||||||
Macs *MacCache
|
Macs *MacCache
|
||||||
db db.DB
|
db db.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNetworkRouter(config mesh.Config, networkConfig NetworkConfig, name mesh.PeerName, nickName string, overlay NetworkOverlay, db db.DB) (*NetworkRouter, error) {
|
func NewNetworkRouter(config mesh.Config, networkConfig NetworkConfig, bridgeConfig weavenet.BridgeConfig, name mesh.PeerName, nickName string, overlay NetworkOverlay, db db.DB) (*NetworkRouter, error) {
|
||||||
if overlay == nil {
|
if overlay == nil {
|
||||||
overlay = NullNetworkOverlay{}
|
overlay = NullNetworkOverlay{}
|
||||||
}
|
}
|
||||||
@@ -63,12 +65,12 @@ func NewNetworkRouter(config mesh.Config, networkConfig NetworkConfig, name mesh
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
router := &NetworkRouter{Router: meshRouter, NetworkConfig: networkConfig, db: db}
|
router := &NetworkRouter{Router: meshRouter, NetworkConfig: networkConfig, BridgeConfig: bridgeConfig, db: db}
|
||||||
router.Peers.OnInvalidateShortIDs(overlay.InvalidateShortIDs)
|
router.Peers.OnInvalidateShortIDs(overlay.InvalidateShortIDs)
|
||||||
router.Routes.OnChange(overlay.InvalidateRoutes)
|
router.Routes.OnChange(overlay.InvalidateRoutes)
|
||||||
router.Macs = NewMacCache(macMaxAge,
|
router.Macs = NewMacCache(macMaxAge,
|
||||||
func(mac net.HardwareAddr, peer *mesh.Peer) {
|
func(mac net.HardwareAddr, peer *mesh.Peer) {
|
||||||
log.Println("Expired MAC", mac, "at", peer)
|
log.Debugln("Expired MAC", mac, "at", peer)
|
||||||
})
|
})
|
||||||
router.Peers.OnGC(func(peer *mesh.Peer) { router.Macs.Delete(peer) })
|
router.Peers.OnGC(func(peer *mesh.Peer) { router.Macs.Delete(peer) })
|
||||||
return router, nil
|
return router, nil
|
||||||
@@ -90,7 +92,7 @@ func (router *NetworkRouter) handleCapturedPacket(key PacketKey) FlowOp {
|
|||||||
|
|
||||||
switch newSrcMac, conflictPeer := router.Macs.Add(srcMac, router.Ourself.Peer); {
|
switch newSrcMac, conflictPeer := router.Macs.Add(srcMac, router.Ourself.Peer); {
|
||||||
case newSrcMac:
|
case newSrcMac:
|
||||||
log.Println("Discovered local MAC", srcMac)
|
log.Debugln("Discovered local MAC", srcMac)
|
||||||
case conflictPeer != nil:
|
case conflictPeer != nil:
|
||||||
// The MAC cache has an entry for the source MAC
|
// The MAC cache has an entry for the source MAC
|
||||||
// associated with another peer. This probably means
|
// associated with another peer. This probably means
|
||||||
|
|||||||
+1
-1
@@ -493,7 +493,7 @@ func (f curriedForward) Process(frame []byte, dec *EthernetDecoder, broadcast bo
|
|||||||
// non-broadcast frames can be broadcast, if the
|
// non-broadcast frames can be broadcast, if the
|
||||||
// destination MAC was not in our MAC cache.
|
// destination MAC was not in our MAC cache.
|
||||||
if broadcast {
|
if broadcast {
|
||||||
log.Print(fwd.logPrefix(), "dropping too big DF broadcast frame (", dec.IP.SrcIP, " -> ", dec.IP.DstIP, "): MTU=", mtu)
|
log.Print(fwd.logPrefix(), "dropping too big DF broadcast frame: len ", len(frame), " (", dec.IP.SrcIP, " -> ", dec.IP.DstIP, "): MTU=", mtu)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-15
@@ -6,27 +6,28 @@ search_type: Documentation
|
|||||||
You only need to build Weave Net if you want to work on the Weave Net codebase
|
You only need to build Weave Net if you want to work on the Weave Net codebase
|
||||||
(or you just enjoy building software).
|
(or you just enjoy building software).
|
||||||
|
|
||||||
Apart from the `weave` shell script, weave is delivered as a set of
|
Apart from the `weave` shell script, Weave Net is delivered as a set of
|
||||||
container images. There is no distribution-specific packaging, so in
|
container images. There is no distribution-specific packaging, so in
|
||||||
principle it shouldn't matter which Linux distribution you build
|
principle it shouldn't matter which Linux distribution you build
|
||||||
under. But naturally, Docker is a prerequisite (version 1.6.0 or
|
under. But naturally, Docker is a prerequisite (version 1.6.0 or
|
||||||
later).
|
later).
|
||||||
|
|
||||||
The only way to build is by using the the build container; the
|
The only way to build is by using the the build container; the
|
||||||
Makefile is setup to make this transparent. This method is
|
`Makefile` is setup to make this transparent. This method is
|
||||||
documented below, and can be run directly on your machine or using
|
documented below, and can be run directly on your machine or using
|
||||||
a Vagrant VM.
|
a Vagrant VM.
|
||||||
|
|
||||||
## <a name="ubuntu"></a>Building directly on your machine
|
## <a name="ubuntu"></a>Building directly on your machine
|
||||||
|
|
||||||
The weave git repository should be cloned into
|
The weave git repository should be cloned into
|
||||||
`$GOPATH/src/github.com/weaveworks/weave`, in accordance with [the go
|
`$GOPATH/src/github.com/weaveworks/weave`, in accordance with [the Go
|
||||||
workspace conventions](https://golang.org/doc/code.html#Workspaces):
|
workspace conventions](https://golang.org/doc/code.html#Workspaces):
|
||||||
|
|
||||||
```
|
```
|
||||||
$ WEAVE=github.com/weaveworks/weave
|
$ WEAVE=github.com/weaveworks/weave
|
||||||
$ git clone https://$WEAVE $GOPATH/src/$WEAVE
|
$ git clone https://$WEAVE $GOPATH/src/$WEAVE
|
||||||
$ cd $GOPATH/src/$WEAVE
|
$ cd $GOPATH/src/$WEAVE
|
||||||
|
$ git submodule update --init
|
||||||
```
|
```
|
||||||
|
|
||||||
Next install Docker if you haven't already, by following the instructions
|
Next install Docker if you haven't already, by following the instructions
|
||||||
@@ -44,7 +45,7 @@ On a fresh repository, the Makefile will do the following:
|
|||||||
- build the weave components in the build container
|
- build the weave components in the build container
|
||||||
- package them into two Docker images (`weaveworks/weave`,
|
- package them into two Docker images (`weaveworks/weave`,
|
||||||
`weaveworks/weaveexec`)
|
`weaveworks/weaveexec`)
|
||||||
- Exported these images as `weave.tar.gz`
|
- export these images as `weave.tar.gz`.
|
||||||
|
|
||||||
The first two steps may take a while - don't worry, they are
|
The first two steps may take a while - don't worry, they are
|
||||||
are cached and should not need to be redone very often.
|
are cached and should not need to be redone very often.
|
||||||
@@ -63,18 +64,19 @@ First, check out the code:
|
|||||||
```
|
```
|
||||||
$ git clone https://github.com/weaveworks/weave
|
$ git clone https://github.com/weaveworks/weave
|
||||||
$ cd weave
|
$ cd weave
|
||||||
|
$ git submodule update --init
|
||||||
```
|
```
|
||||||
|
|
||||||
The `Vagrantfile` in the top directory constructs a VM that has
|
The `Vagrantfile` in the top directory constructs a VM that has:
|
||||||
|
|
||||||
* docker installed
|
* Docker installed
|
||||||
* go tools installed
|
* Go tools installed
|
||||||
* weave dependencies installed
|
* Weave's dependencies installed
|
||||||
* $GOPATH set to ~
|
* `$GOPATH` set to `~`
|
||||||
* the local working directory mapped as a synced folder into the
|
* the local working directory mapped as a synced folder into the
|
||||||
right place in $GOPATH
|
right place in `$GOPATH`.
|
||||||
|
|
||||||
Once you are in the working directory you can issue
|
Once you are in the working directory you can issue:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ vagrant up
|
$ vagrant up
|
||||||
@@ -86,7 +88,7 @@ installation is done just once). The working directory is sync'ed with
|
|||||||
use git and so on in the regular filesystem.
|
use git and so on in the regular filesystem.
|
||||||
|
|
||||||
To build and run the code, you need to use the VM. To log in and build
|
To build and run the code, you need to use the VM. To log in and build
|
||||||
the weave image, do
|
the weave image, do:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ vagrant ssh
|
$ vagrant ssh
|
||||||
@@ -94,7 +96,7 @@ vm$ cd src/github.com/weaveworks/weave
|
|||||||
vm$ make
|
vm$ make
|
||||||
```
|
```
|
||||||
|
|
||||||
The Docker daemon is also running in this VM, so you can then do
|
The Docker daemon is also running in this VM, so you can then do:
|
||||||
|
|
||||||
```
|
```
|
||||||
vm$ sudo ./weave launch
|
vm$ sudo ./weave launch
|
||||||
@@ -104,7 +106,7 @@ vm$ sudo docker ps
|
|||||||
and so on.
|
and so on.
|
||||||
|
|
||||||
If you are looking to just do a build and not run anything on this VM,
|
If you are looking to just do a build and not run anything on this VM,
|
||||||
you can do so with
|
you can do so with:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ vagrant ssh -c 'make -C src/github.com/weaveworks/weave'
|
$ vagrant ssh -c 'make -C src/github.com/weaveworks/weave'
|
||||||
@@ -112,7 +114,7 @@ $ vagrant ssh -c 'make -C src/github.com/weaveworks/weave'
|
|||||||
|
|
||||||
you should then find a `weave.tar.gz` container snapshot tarball in the
|
you should then find a `weave.tar.gz` container snapshot tarball in the
|
||||||
top-level directory. You can use that snapshot with `docker load`
|
top-level directory. You can use that snapshot with `docker load`
|
||||||
against a different host, e.g.
|
against a different host, e.g.:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ export DOCKER_HOST=tcp://<HOST:PORT>
|
$ export DOCKER_HOST=tcp://<HOST:PORT>
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ You can of course also manually change the IP of any given container or subnet o
|
|||||||
### IP Address Notation in Weave
|
### IP Address Notation in Weave
|
||||||
|
|
||||||
In the IP address `10.4.2.6/8`, the network prefix is the first 8 bits
|
In the IP address `10.4.2.6/8`, the network prefix is the first 8 bits
|
||||||
- `10`. Written out in full, that network is `10.0.0.0/8`.
|
\- `10`. Written out in full, that network is `10.0.0.0/8`.
|
||||||
|
|
||||||
The most common prefix lengths are 8, 16 and 24, but there is nothing
|
The most common prefix lengths are 8, 16 and 24, but there is nothing
|
||||||
stopping you using a /9 network or a /26. For example, `6.250.3.1/9` is on the
|
stopping you using a /9 network or a /26. For example, `6.250.3.1/9` is on the
|
||||||
|
|||||||
+14
-1
@@ -40,6 +40,18 @@ A simple way to accomplish this would be to run Weave on the host and then run,
|
|||||||
Yet another option is to expose a port from the container on host B and then connect to it. You can read about exposing ports in [Exporting Services](/site/tasks/manage/service-management.md#exporting).
|
Yet another option is to expose a port from the container on host B and then connect to it. You can read about exposing ports in [Exporting Services](/site/tasks/manage/service-management.md#exporting).
|
||||||
|
|
||||||
|
|
||||||
|
<a name="duplicate-peer"></a>
|
||||||
|
**Q: Why am I seeing "peer names collision" and failed connections?**
|
||||||
|
|
||||||
|
This sometimes happens when machines are cloned; we require each
|
||||||
|
machine in your cluster to have a unique identity.
|
||||||
|
|
||||||
|
For more information see [Peer Names](/site/operational-guide/concepts.md#peer-name).
|
||||||
|
|
||||||
|
Depending on your Linux distribution you may need to [set up
|
||||||
|
machine-id](https://www.freedesktop.org/software/systemd/man/machine-id.html)
|
||||||
|
or [generate a dbus id](https://dbus.freedesktop.org/doc/dbus-uuidgen.1.html).
|
||||||
|
|
||||||
<a name="duplicate-ip"></a>
|
<a name="duplicate-ip"></a>
|
||||||
**Q: Why am I seeing the same IP address assigned to two different containers on different hosts?**
|
**Q: Why am I seeing the same IP address assigned to two different containers on different hosts?**
|
||||||
|
|
||||||
@@ -92,7 +104,8 @@ Yes, of course! Weave allows you to run isolated networks and still allow open
|
|||||||
You must permit traffic to flow through TCP 6783 and UDP 6783/6784,
|
You must permit traffic to flow through TCP 6783 and UDP 6783/6784,
|
||||||
which are Weave’s control and data ports.
|
which are Weave’s control and data ports.
|
||||||
|
|
||||||
The daemon also uses TCP port 6782 for [metrics](/site/tasks/manage/metrics.md), but
|
The daemon also uses TCP 6781/6782 for
|
||||||
|
[metrics](/site/tasks/manage/metrics.md#metrics-endpoint-addresses), but
|
||||||
you would only need to open up this port if you wish to collect metrics
|
you would only need to open up this port if you wish to collect metrics
|
||||||
from another host.
|
from another host.
|
||||||
|
|
||||||
|
|||||||
Loaded 100 of 3213 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user