Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
347466aba2 | ||
|
|
bee1c44859 | ||
|
|
f07238c8bb | ||
|
|
3b499ce27c | ||
|
|
4dda216398 | ||
|
|
f0e511510a | ||
|
|
570ab0b10b | ||
|
|
214cf6cb2e | ||
|
|
07459f0cec | ||
|
|
90f4f7b62c | ||
|
|
987ac98fbf | ||
|
|
49ed40a301 | ||
|
|
c67bc704e1 | ||
|
|
3501ad45e6 | ||
|
|
70a13c9c12 | ||
|
|
3afb8d3ddf | ||
|
|
fd2f91e2ee | ||
|
|
cb0c9e5fba | ||
|
|
6a0e39e5ee | ||
|
|
a552f194b4 | ||
|
|
267bd8b51d | ||
|
|
497dddabc1 | ||
|
|
71b39489da | ||
|
|
ed8d92cfc6 | ||
|
|
f431ee289b | ||
|
|
7ea82204e0 | ||
|
|
44692b997c | ||
|
|
21086a0b2a | ||
|
|
d7119169df | ||
|
|
e1eb0a8617 | ||
|
|
cacc190764 | ||
|
|
89e5d935a6 | ||
|
|
884e1d7a6b | ||
|
|
5b1ca02ecc | ||
|
|
f51347feac | ||
|
|
b605b832ed | ||
|
|
e148b72284 | ||
|
|
16db921349 | ||
|
|
4c235b3648 | ||
|
|
1a8d479227 | ||
|
|
7aad364116 | ||
|
|
4958d9e423 | ||
|
|
474f1f3673 | ||
|
|
9aa7044f1e | ||
|
|
7c39f96d08 | ||
|
|
3c3e0b0352 | ||
|
|
8467742d6d | ||
|
|
5cfc3f8c47 | ||
|
|
62dbe03193 | ||
|
|
8755da4b5a | ||
|
|
25cff16bd1 | ||
|
|
ade6738789 | ||
|
|
ea0e3a14e0 | ||
|
|
4a1a1e83c8 | ||
|
|
ee48700b40 | ||
|
|
0791ce8ab0 | ||
|
|
1ac543aba7 | ||
|
|
3d5f80630d | ||
|
|
be2da07735 | ||
|
|
276d0b621b | ||
|
|
6733dac9fc | ||
|
|
80b286ebf7 | ||
|
|
2c143612e2 | ||
|
|
be412812f9 | ||
|
|
bfed7c24f8 | ||
|
|
fe859952e0 | ||
|
|
86361c5b60 | ||
|
|
35c017a787 | ||
|
|
79d65bc98b | ||
|
|
6bc69199d3 | ||
|
|
292cbc484a | ||
|
|
1a98cbfff9 | ||
|
|
071f077cc5 | ||
|
|
c16653a396 | ||
|
|
b157544394 | ||
|
|
18f65a1914 | ||
|
|
ba16135b3a | ||
|
|
df688cfb53 | ||
|
|
88679d66fc | ||
|
|
ab1cbcbbf8 | ||
|
|
a02fb753df | ||
|
|
98b002f7c0 | ||
|
|
e251bfe807 | ||
|
|
eb88893312 | ||
|
|
a362bf68bb | ||
|
|
3c53aa66b7 | ||
|
|
c97d4731dc | ||
|
|
a69546c9ee | ||
|
|
b0f78e97f4 | ||
|
|
49f7e38133 | ||
|
|
2e7f44feee | ||
|
|
48ceb49832 | ||
|
|
dd772f04fe | ||
|
|
47597c941d | ||
|
|
c0e8af56e2 | ||
|
|
72c0172c48 | ||
|
|
507f5345c5 | ||
|
|
79d4aec1ec | ||
|
|
89c7e6d337 | ||
|
|
b8d7af3822 | ||
|
|
3c9c968174 | ||
|
|
38ca0f90c7 | ||
|
|
8bf08c3cf8 | ||
|
|
14c79d2184 | ||
|
|
b0519af573 | ||
|
|
dc7acd6c70 | ||
|
|
d63a21363e | ||
|
|
a330dd4970 | ||
|
|
c0388c0136 | ||
|
|
aeca987a4c | ||
|
|
3aff5f2467 | ||
|
|
0c63a8d0e5 | ||
|
|
65b45e8dd1 | ||
|
|
03885d4b40 | ||
|
|
40e78fea99 | ||
|
|
ecba88e445 | ||
|
|
29c9b2c064 | ||
|
|
ff1cd7c008 | ||
|
|
6a871d13a6 | ||
|
|
c2a224b436 | ||
|
|
dd8d4b1d9b | ||
|
|
527bcffe3b | ||
|
|
de1d409c6a | ||
|
|
2555e491e1 | ||
|
|
4b5299d124 | ||
|
|
294c16c186 | ||
|
|
4b720bffdf | ||
|
|
7df458fcdd | ||
|
|
608b8f0a06 | ||
|
|
089a993299 | ||
|
|
bc4c31e48b | ||
|
|
5c210c90b5 | ||
|
|
a6cd456f8b | ||
|
|
2ede1066e9 | ||
|
|
462033dc99 | ||
|
|
0521bdb3b9 | ||
|
|
1f4165c132 | ||
|
|
a1fb08216e | ||
|
|
381996b225 | ||
|
|
08bca70192 | ||
|
|
10d25056a0 | ||
|
|
f889dda9f3 | ||
|
|
b0cd84c940 | ||
|
|
411fd2d831 | ||
|
|
d18782afd7 | ||
|
|
43dfbdc8e3 | ||
|
|
ea0f156b6f | ||
|
|
98def2f5fc | ||
|
|
c089cb508c | ||
|
|
1797cee84f | ||
|
|
45c97c82e1 | ||
|
|
bccde69a33 | ||
|
|
d9aa1a5abd | ||
|
|
46e1153f92 | ||
|
|
3dc642c007 | ||
|
|
bd9bc9093e | ||
|
|
6f0680cc7b | ||
|
|
911bf289bd | ||
|
|
393b98a463 | ||
|
|
2126d2314e | ||
|
|
d4d9a8fa9a | ||
|
|
6a21e3a2ca | ||
|
|
7119348b31 | ||
|
|
bf291b1863 | ||
|
|
e6a1acddcb | ||
|
|
8a8cd70879 | ||
|
|
b744e41e9e | ||
|
|
c101957fb9 | ||
|
|
a8dc7986af | ||
|
|
d0c9763d0d | ||
|
|
5988fede53 | ||
|
|
25c8b46c70 | ||
|
|
60c3d97c7a | ||
|
|
ee0bd2d747 | ||
|
|
c0e94cabb6 | ||
|
|
867e3231d2 | ||
|
|
00bfa3ae03 | ||
|
|
7ae83d6a5b | ||
|
|
5d6f6456d3 | ||
|
|
69b6077221 | ||
|
|
dcf82f8db7 | ||
|
|
69f83b352a | ||
|
|
2899789147 | ||
|
|
d33f06634f | ||
|
|
1d3bdd6a34 | ||
|
|
5a38e04789 | ||
|
|
1f1b22cee7 | ||
|
|
16ce5eacbb | ||
|
|
69e311d021 | ||
|
|
ea48915007 | ||
|
|
165c8c1d0f | ||
|
|
b237eaf6bf | ||
|
|
4de70e31c6 | ||
|
|
dd29302d57 | ||
|
|
4f3cf40e30 | ||
|
|
9fdb40c9b8 | ||
|
|
3af2a070e3 | ||
|
|
6e5f0a8eea | ||
|
|
d0320dd0c6 | ||
|
|
5f25bba012 | ||
|
|
42e650bba8 | ||
|
|
c6f29d685c | ||
|
|
a45c5e1170 | ||
|
|
683b4c7c18 | ||
|
|
1b5d6033b0 | ||
|
|
c209761689 | ||
|
|
59112209d0 | ||
|
|
28ea18d3a6 | ||
|
|
fac669156e | ||
|
|
102f4ad4a4 | ||
|
|
e10ce2345b | ||
|
|
d06eaf14af | ||
|
|
21623118d3 | ||
|
|
4dfcaa470a | ||
|
|
9cc98b0ee8 | ||
|
|
acf2b4dc7d | ||
|
|
029c6f9caf | ||
|
|
34767f4986 | ||
|
|
fe852d8519 | ||
|
|
0eded20bdf | ||
|
|
9ec9e72ade | ||
|
|
c9559666ef | ||
|
|
81c0d44c09 | ||
|
|
2651fe2ed4 | ||
|
|
e1e40fc68c | ||
|
|
dff0f08cb5 | ||
|
|
5483e81d75 | ||
|
|
905d923880 | ||
|
|
c3d96ca448 | ||
|
|
7ea8761685 | ||
|
|
0e6e5e3a4d | ||
|
|
afbc7aaa14 | ||
|
|
8cc3179a36 | ||
|
|
324d013f88 | ||
|
|
70924f6f3a | ||
|
|
a2602bb712 | ||
|
|
5fc94ca01d | ||
|
|
ddf2c99240 | ||
|
|
f3e83dcdb9 | ||
|
|
773aeda980 | ||
|
|
bea58ef6dc | ||
|
|
35b4fca59f | ||
|
|
afc2f13841 | ||
|
|
4e71e4db86 | ||
|
|
dd1fc02f9f | ||
|
|
760a76cbd0 | ||
|
|
a81720d64c | ||
|
|
d0ad4ad022 | ||
|
|
2f9f500ecb | ||
|
|
1d09a35bcd | ||
|
|
e8310ab1b7 | ||
|
|
3b029896c9 | ||
|
|
61f83cd463 | ||
|
|
0c36b87bcc | ||
|
|
14e97185b3 | ||
|
|
fa512ee22a | ||
|
|
58dc68d112 | ||
|
|
7e265100c3 | ||
|
|
9d0a0253c9 | ||
|
|
e41b5d9a1c | ||
|
|
7af82945d9 | ||
|
|
6e90c56d03 | ||
|
|
887d72e45f | ||
|
|
3086a6821b | ||
|
|
599d5e4863 | ||
|
|
d59aab3f71 | ||
|
|
d356afd6cf | ||
|
|
474ff5815b | ||
|
|
5379826662 | ||
|
|
b551c15f5c | ||
|
|
8554f0f896 | ||
|
|
b8a5d49100 | ||
|
|
05ed63aaf4 | ||
|
|
f37f8308c8 | ||
|
|
136c4521d4 | ||
|
|
7a34d997aa | ||
|
|
6ca9b2c006 | ||
|
|
fdc01a41de | ||
|
|
fb138b54fa | ||
|
|
efde637e1c | ||
|
|
448518d500 | ||
|
|
b562cc9b98 | ||
|
|
7b6a21f612 | ||
|
|
cf1a1116f4 | ||
|
|
cbc3d5b04c | ||
|
|
219b03cb6b | ||
|
|
c33cd2657c | ||
|
|
e26b0663e3 | ||
|
|
7f93c403e9 | ||
|
|
4a1c2c5d09 | ||
|
|
8e445f349b | ||
|
|
09984001fc | ||
|
|
f598338a5f | ||
|
|
650adb47fd | ||
|
|
ad8a943eb2 | ||
|
|
1b7efab40a | ||
|
|
43c608e25c | ||
|
|
6aba9411a6 | ||
|
|
1e70992835 | ||
|
|
38a84e95ad | ||
|
|
5492d046d1 | ||
|
|
16dbd5eea0 | ||
|
|
098a3c59c7 | ||
|
|
16272f0d75 | ||
|
|
4e41e422c5 | ||
|
|
227d45a40c | ||
|
|
6bd529ef66 | ||
|
|
61e798d400 | ||
|
|
d84154326f | ||
|
|
cba793524e | ||
|
|
9ec8cb9bf4 | ||
|
|
be52e08f2d | ||
|
|
4edff2f17f | ||
|
|
ed70e6e344 | ||
|
|
920bba2ffd | ||
|
|
2113498348 | ||
|
|
8e7e1a3b26 | ||
|
|
f638fac152 | ||
|
|
d2ebaf3ab7 | ||
|
|
90575278b0 | ||
|
|
fdbffce93a | ||
|
|
afac7c4d09 | ||
|
|
3fdca717c1 | ||
|
|
3959149077 | ||
|
|
f17053fcee | ||
|
|
fcbbf89083 | ||
|
|
3138602661 | ||
|
|
562f49b3ad | ||
|
|
02328ae43f | ||
|
|
e132f6c385 | ||
|
|
2b8fb49be2 | ||
|
|
51601a3258 | ||
|
|
31267f36ed | ||
|
|
540d1cbf06 | ||
|
|
050f671c65 | ||
|
|
35e2179271 | ||
|
|
52d06513bb | ||
|
|
124ef94500 | ||
|
|
166d5bfc5b | ||
|
|
efd290a3be | ||
|
|
742ef03495 | ||
|
|
92a6694ac8 | ||
|
|
1354ad1884 | ||
|
|
15a4b189f4 | ||
|
|
53cf6c2f8c | ||
|
|
64518c3bd4 | ||
|
|
20711cb8bc | ||
|
|
2914b9d220 | ||
|
|
677c872d5a | ||
|
|
015809c83a | ||
|
|
092ec8d83d | ||
|
|
9d2237cfd4 | ||
|
|
8b615e58e8 | ||
|
|
e351d0c3c0 | ||
|
|
a943f66afd | ||
|
|
3df1a0943c | ||
|
|
b5759c5b6d | ||
|
|
6b4c6c84b4 | ||
|
|
d8222d76d9 | ||
|
|
88a3b41135 | ||
|
|
fb7aa9298d | ||
|
|
b79cc948b3 | ||
|
|
e7092612a9 | ||
|
|
15c512388d | ||
|
|
f37377264b | ||
|
|
bd44b20b4a | ||
|
|
e25da3c619 | ||
|
|
3499dbfe09 | ||
|
|
81c4a0ffce | ||
|
|
a0064f7535 | ||
|
|
79c9eff2b9 | ||
|
|
b8bcf0c87d | ||
|
|
179a560e1d | ||
|
|
2245bd25e9 | ||
|
|
c256daceb0 | ||
|
|
73b29050b9 | ||
|
|
162da63522 | ||
|
|
01194ee267 | ||
|
|
d958cff58e | ||
|
|
d0662b519f | ||
|
|
d75a96e9ba | ||
|
|
bd7ed1b41a | ||
|
|
b5e6d235ba | ||
|
|
3cc537b978 | ||
|
|
ac0c0db942 | ||
|
|
0affbad976 | ||
|
|
ced84955d3 | ||
|
|
b86fc67adf | ||
|
|
59de4c3435 | ||
|
|
8e418b8743 | ||
|
|
f2b003379d | ||
|
|
0d083077ab | ||
|
|
db1a94a02c | ||
|
|
fdbc7dfe04 | ||
|
|
70f004cc62 | ||
|
|
c02cb45c6c | ||
|
|
8dccc88613 | ||
|
|
bb5914dbce | ||
|
|
4698ec1a37 | ||
|
|
e29c98faad | ||
|
|
444d6be6c9 | ||
|
|
28aa181480 | ||
|
|
97e0fcb765 | ||
|
|
052fdb30c0 | ||
|
|
2e2b667864 | ||
|
|
666631134c | ||
|
|
de4cfe2b8b | ||
|
|
a7ebe53cfa | ||
|
|
66a569fc40 | ||
|
|
3a646ffdba | ||
|
|
7cfd71c0bd | ||
|
|
3265d60d52 | ||
|
|
640b417b39 | ||
|
|
b655a3aede | ||
|
|
819b395ae4 | ||
|
|
2584335b11 | ||
|
|
4e5a8f348a | ||
|
|
f91a9c2562 | ||
|
|
ea098ac53e | ||
|
|
a0d0b00938 | ||
|
|
31a4657467 | ||
|
|
2ddb8fcec3 | ||
|
|
21d4fff450 | ||
|
|
00036ec277 | ||
|
|
a5d7fb66f0 | ||
|
|
b01a204c1f | ||
|
|
e9310eba98 | ||
|
|
69285270e5 | ||
|
|
6f96d6d63e | ||
|
|
91f476d2c6 | ||
|
|
7871a0b7a4 | ||
|
|
6bd11a426e | ||
|
|
1abdc3afcf | ||
|
|
b8f98a5387 | ||
|
|
cb3b858df6 | ||
|
|
d7025e7dfa | ||
|
|
b2b9c47a50 | ||
|
|
211003f542 | ||
|
|
afb0d5bd3d | ||
|
|
f8227c3f47 | ||
|
|
258d764af8 | ||
|
|
2d3e070288 | ||
|
|
138141ac9b | ||
|
|
fa56ad8bc3 | ||
|
|
9d29da8d27 | ||
|
|
37526728e0 | ||
|
|
68f2cb9caa | ||
|
|
d13c7ea7ee | ||
|
|
4c7c702b6d | ||
|
|
c05955d598 | ||
|
|
ca64ab54e7 | ||
|
|
7f4ad03a5c | ||
|
|
d60ad1f40a | ||
|
|
5106317e5d | ||
|
|
a5e4a19f06 | ||
|
|
067ce6de9f | ||
|
|
9facab53b7 | ||
|
|
e64442f6de | ||
|
|
4f20f545dc | ||
|
|
4bbf84f551 | ||
|
|
987a1618e9 | ||
|
|
0ab95cdd27 | ||
|
|
c617612794 | ||
|
|
0c89bbb8df | ||
|
|
a7206bee95 | ||
|
|
0e3a749f69 | ||
|
|
b0d0c3d731 | ||
|
|
534dd8e16a | ||
|
|
535bf9b066 | ||
|
|
79e703c8ac | ||
|
|
d4a98a58f4 | ||
|
|
ceb7206117 | ||
|
|
1273157ace | ||
|
|
5a92200dc6 | ||
|
|
d021e61dfa | ||
|
|
3ae505d6c6 | ||
|
|
a1ddb336c1 | ||
|
|
646ac3c74d | ||
|
|
d383a29e00 | ||
|
|
b44e295562 | ||
|
|
8a1f8d549e | ||
|
|
a2b674e094 | ||
|
|
c4796dcdf5 | ||
|
|
992b4a65df | ||
|
|
6928929bc9 | ||
|
|
508b424234 | ||
|
|
7f68a96f75 | ||
|
|
535e22e5f8 | ||
|
|
348a926af4 | ||
|
|
aa84c4788a | ||
|
|
66293e8caa | ||
|
|
1e60cff2ef | ||
|
|
701519a15c | ||
|
|
72532324ed | ||
|
|
3cb6569d55 | ||
|
|
bebaa48520 | ||
|
|
af246225a9 | ||
|
|
582a792c71 | ||
|
|
98c4bd9ee5 | ||
|
|
933b395de4 | ||
|
|
4f485f856b | ||
|
|
280651da9c | ||
|
|
77201e949d | ||
|
|
292a50a891 | ||
|
|
a36fc867d1 | ||
|
|
def9f2d407 | ||
|
|
b0da4f6b91 | ||
|
|
cbf1230f6d | ||
|
|
71d6e881f2 | ||
|
|
da40771739 | ||
|
|
694314cc00 | ||
|
|
3025738377 | ||
|
|
cbe0f4e6e5 | ||
|
|
68106d804e | ||
|
|
e0088e0252 | ||
|
|
efff6d6e63 | ||
|
|
6bbd8d7a4c | ||
|
|
e0bb49aa23 | ||
|
|
81d79a64e6 | ||
|
|
9cbf0289d8 | ||
|
|
4f08879321 | ||
|
|
6d6394c408 | ||
|
|
bf3ecaf48a | ||
|
|
6ba2bf80b8 | ||
|
|
9f4030cdb6 | ||
|
|
cce818995a | ||
|
|
1e2dc43df6 | ||
|
|
06caf03d70 | ||
|
|
7226b26e8e | ||
|
|
7c72d69e2f | ||
|
|
bc1d6c47ba | ||
|
|
8ec0e025d3 | ||
|
|
9f40d913c9 | ||
|
|
3595c71d8b | ||
|
|
9740478151 | ||
|
|
255d087416 | ||
|
|
442a55d539 | ||
|
|
01fce0c6b1 | ||
|
|
0e3ac00ff5 | ||
|
|
2285942384 | ||
|
|
8668f6bb1e | ||
|
|
95ee99d652 | ||
|
|
0273491e6e | ||
|
|
b1917d909a | ||
|
|
edbf85655a | ||
|
|
be917eb86b | ||
|
|
0a2521e7a4 | ||
|
|
1b8d115b63 | ||
|
|
70929cec57 | ||
|
|
30583ec8f2 | ||
|
|
e370ef7007 | ||
|
|
42953faf70 | ||
|
|
7611cd963c | ||
|
|
3e0eb4ac1e | ||
|
|
f8d501ae50 | ||
|
|
cb614f01c3 | ||
|
|
9be11d9cf0 | ||
|
|
8ca83de332 | ||
|
|
89d3fedcd4 | ||
|
|
71ca91db09 | ||
|
|
5cf973f03a | ||
|
|
2c7693b2c5 | ||
|
|
43249a8d39 | ||
|
|
b654ac178c | ||
|
|
ccac18b21c | ||
|
|
8a5fef98ab | ||
|
|
c85fff8f23 | ||
|
|
fc57e72853 | ||
|
|
ce94e69fd5 | ||
|
|
6c29a1f648 | ||
|
|
b46ccc0937 | ||
|
|
fa29b18be7 | ||
|
|
e89fc8f66a | ||
|
|
ad4fe86285 | ||
|
|
40db885b3a | ||
|
|
ae0c16f375 | ||
|
|
34ec7bd1c9 | ||
|
|
909b751d6d | ||
|
|
9419ad16bf | ||
|
|
9a90898c7d | ||
|
|
8e80cd1441 | ||
|
|
2c12c4f67e | ||
|
|
d5d45de5a4 | ||
|
|
2ebd358aeb | ||
|
|
be11db45d8 | ||
|
|
30213bd71e | ||
|
|
bb66b6161a | ||
|
|
b14501f358 | ||
|
|
e93a145bab | ||
|
|
f7130b8c21 | ||
|
|
a497e1ab8a | ||
|
|
8b245a442c | ||
|
|
e1ff2197c0 | ||
|
|
f684ea563d | ||
|
|
041b6fe251 | ||
|
|
88cb871654 | ||
|
|
507e734d1a | ||
|
|
d128488339 | ||
|
|
c8c61adca4 | ||
|
|
7c8f33f525 | ||
|
|
cb8599ebd5 | ||
|
|
7714952574 | ||
|
|
15bf6eab1e | ||
|
|
10333b0f3e | ||
|
|
aaa974353a | ||
|
|
52a5ca8f88 | ||
|
|
1cca9d6d79 | ||
|
|
b19f2df6f8 | ||
|
|
b3ac56e3d2 | ||
|
|
d28b1dcd9f | ||
|
|
e4cab53a11 | ||
|
|
c342bc4188 | ||
|
|
73a0df56b2 | ||
|
|
e0303a0dda | ||
|
|
cc77ad2c65 | ||
|
|
280a0fed69 | ||
|
|
24c2743a58 | ||
|
|
264a2cd2ad | ||
|
|
907ba8f53b | ||
|
|
d2cbb2749e | ||
|
|
eab5ec206a | ||
|
|
b49f5bdb99 | ||
|
|
28a1578d20 | ||
|
|
e78be1fc35 | ||
|
|
fa8753cab3 | ||
|
|
9459d4b739 | ||
|
|
f517a09f0f | ||
|
|
34642a04a4 | ||
|
|
4bb4d943ba | ||
|
|
db855715ab | ||
|
|
c27e4d020d | ||
|
|
109e249aa6 | ||
|
|
a16ad4d6e6 | ||
|
|
33ce8c5060 | ||
|
|
f2568627af | ||
|
|
334974472d | ||
|
|
8227a3f67c | ||
|
|
4f7d476907 | ||
|
|
a731d47d97 | ||
|
|
44beed4027 | ||
|
|
9fd1a43769 | ||
|
|
1c6abbbe41 | ||
|
|
c4a715747a | ||
|
|
3bdea732fc | ||
|
|
bfe8e79653 | ||
|
|
7d812e3087 | ||
|
|
05d9cf8235 | ||
|
|
108f4784a6 | ||
|
|
a7feb41e62 | ||
|
|
fddb3ee06e | ||
|
|
c59c86c660 | ||
|
|
8f02c81f16 | ||
|
|
b3fac8ed6a | ||
|
|
3574da267b | ||
|
|
c929b2d5f9 | ||
|
|
f479e57870 | ||
|
|
ae625a18c8 | ||
|
|
41a261bdcd | ||
|
|
4ad1c22ef6 | ||
|
|
ef9f95f653 | ||
|
|
3b54372eab | ||
|
|
f05bb6c4e0 | ||
|
|
c8e0c12d34 | ||
|
|
ab380ed2c1 | ||
|
|
9494f0655d | ||
|
|
c3a41286fd | ||
|
|
8297652204 | ||
|
|
a26fc67ded | ||
|
|
abadbf883a | ||
|
|
833eb6166f | ||
|
|
e7e3608642 | ||
|
|
dd232775e6 | ||
|
|
a8eb7966b6 | ||
|
|
8051f51d46 | ||
|
|
6fd9f471ff | ||
|
|
2d037d874c | ||
|
|
8e5384ee01 | ||
|
|
7dd37d2a13 | ||
|
|
36521b04a2 | ||
|
|
f5d0a277fb | ||
|
|
986a3f8079 | ||
|
|
de4ec23809 | ||
|
|
1cc0794e75 | ||
|
|
3cca0887c7 | ||
|
|
0e1d39120e | ||
|
|
a072cf2e0a | ||
|
|
0f0f3649a4 | ||
|
|
54c95c04af | ||
|
|
7c5dda353d | ||
|
|
c42400de4f | ||
|
|
9b5cef9167 | ||
|
|
6081c400fb | ||
|
|
cb339523de | ||
|
|
538902b207 | ||
|
|
8961041469 | ||
|
|
cfe26ae346 | ||
|
|
bdfa06dbe9 | ||
|
|
1a3537a8f4 | ||
|
|
09606db9c3 | ||
|
|
4a708c2a82 | ||
|
|
735a7f9154 | ||
|
|
7478e78ab7 | ||
|
|
01bfa602ad | ||
|
|
332762201a | ||
|
|
568290afb7 | ||
|
|
3c11c8aa38 | ||
|
|
2ba360f33a | ||
|
|
7fc56e54b3 | ||
|
|
16062ea62d | ||
|
|
e852ae99ca | ||
|
|
0ff37d3492 | ||
|
|
b3866ebd94 | ||
|
|
3031372442 | ||
|
|
d3d2de5f1f | ||
|
|
d7306c66f7 | ||
|
|
1f42a9d2f9 | ||
|
|
8c917eeb17 | ||
|
|
a81a873703 | ||
|
|
da04d97fb5 | ||
|
|
4f42029228 | ||
|
|
4916badcc4 | ||
|
|
82b56848a9 | ||
|
|
889dc856af | ||
|
|
2ba8764615 | ||
|
|
eb5435d275 | ||
|
|
b048b6deb0 | ||
|
|
0c23e47124 | ||
|
|
0af2111535 | ||
|
|
5bbd886e6f | ||
|
|
402fe094f4 | ||
|
|
b753bcd982 | ||
|
|
d6a5eaec3f | ||
|
|
771f57029e | ||
|
|
c223ff1c7d | ||
|
|
481214776b | ||
|
|
98e6571059 | ||
|
|
19a31a5b82 | ||
|
|
090878c157 | ||
|
|
53cc270f47 | ||
|
|
ceb0186f46 | ||
|
|
46762a40b0 | ||
|
|
c291324d14 | ||
|
|
b307b9872a | ||
|
|
5e290aa11f | ||
|
|
5f6c5f6692 | ||
|
|
1c1b3248c0 | ||
|
|
bc47866880 | ||
|
|
60c5e11871 | ||
|
|
e6a692424b | ||
|
|
f293063538 | ||
|
|
6321512ddb | ||
|
|
0ca125ff2c | ||
|
|
57efe0145b | ||
|
|
bd53462ab2 | ||
|
|
c44d98b798 | ||
|
|
c8adcb2e79 | ||
|
|
768f89c3de | ||
|
|
e2d644b86b | ||
|
|
09977a2e04 | ||
|
|
63a0b964ab | ||
|
|
5cae747b36 | ||
|
|
1b7c386351 | ||
|
|
ef0dae4ed3 | ||
|
|
293f1990bc | ||
|
|
0bb62ae75a | ||
|
|
2686be1b93 | ||
|
|
77b193791a | ||
|
|
74a7bef472 | ||
|
|
fc78bf4d11 | ||
|
|
84cdf54079 | ||
|
|
fa08bb9df6 | ||
|
|
b845360091 | ||
|
|
953d451105 | ||
|
|
75d80c4828 | ||
|
|
3e030f7971 | ||
|
|
877658d22e | ||
|
|
1c797ca901 | ||
|
|
520a72c8e6 | ||
|
|
98fec7e5aa | ||
|
|
d0e0df9bd4 | ||
|
|
f5bdf916f5 | ||
|
|
038aac41fd | ||
|
|
ea36d232ac | ||
|
|
c54fe016b1 | ||
|
|
9c9b46479d | ||
|
|
916ff7aa39 | ||
|
|
28c290074b | ||
|
|
b687484dda | ||
|
|
e74783b987 | ||
|
|
0b4c19a369 | ||
|
|
def9d8d004 | ||
|
|
fd731beb87 | ||
|
|
35cc653737 | ||
|
|
1254d680ed | ||
|
|
23c0b6a04b | ||
|
|
ede1ec970c | ||
|
|
2301e8dbf7 | ||
|
|
fbab5a7280 | ||
|
|
ac430e4c42 | ||
|
|
a92cfc1ffe | ||
|
|
178cf85183 | ||
|
|
272161b00b | ||
|
|
c6457981ab | ||
|
|
56155d0819 | ||
|
|
c711826996 | ||
|
|
eef6965590 | ||
|
|
4926cb6f9a | ||
|
|
d9ca54e777 | ||
|
|
1f6a6349a9 | ||
|
|
8f27f8f442 | ||
|
|
681109ba71 | ||
|
|
99c691745a | ||
|
|
1af5699948 | ||
|
|
ef7571f1ef | ||
|
|
8862d4f76e | ||
|
|
d313ec935c | ||
|
|
40e759733d | ||
|
|
dee57aca37 | ||
|
|
7ac2301247 | ||
|
|
c0726923f5 | ||
|
|
4d9eadde5b | ||
|
|
1d55ee19e4 | ||
|
|
9da5cee8d9 | ||
|
|
ca22ecdf09 | ||
|
|
2e58e92d48 | ||
|
|
fd0c53b424 | ||
|
|
7b1e8ac1cf | ||
|
|
9810a84475 | ||
|
|
31b7e063e7 | ||
|
|
b5172f46c1 | ||
|
|
5a6cb8e708 | ||
|
|
c3c28ea27e | ||
|
|
b128562b41 | ||
|
|
8f44dc0986 | ||
|
|
623abeef4c | ||
|
|
5f30846092 | ||
|
|
6ddbc5cb57 | ||
|
|
f9f3b18ce4 | ||
|
|
dc2925263c | ||
|
|
56fd45f7ea | ||
|
|
21a0fc30e8 | ||
|
|
673d0af97d | ||
|
|
ca523fa335 | ||
|
|
5860332e49 | ||
|
|
c06d4d1b00 | ||
|
|
bec2470ddf | ||
|
|
402400d4cb | ||
|
|
b1a66012bd | ||
|
|
9516b1a91b | ||
|
|
8468183738 | ||
|
|
39563fc142 | ||
|
|
104d2240ec | ||
|
|
9bec84da5b | ||
|
|
85fa976a25 | ||
|
|
e3a37c8d71 | ||
|
|
fd28740108 | ||
|
|
fa74d1b9bb | ||
|
|
11d120bb5b | ||
|
|
e2c96cfa85 | ||
|
|
eee3e8d331 | ||
|
|
0c68ad4acb | ||
|
|
640b487914 | ||
|
|
7c84e24f48 | ||
|
|
26b566b541 | ||
|
|
16bbee2831 | ||
|
|
125f3cf6f6 | ||
|
|
96f7c6ebab | ||
|
|
b2ffe77029 | ||
|
|
3607cf2d7a | ||
|
|
5b06063442 | ||
|
|
a57cf4bc83 | ||
|
|
6036037817 |
No files matched your search
@@ -0,0 +1,65 @@
|
||||
<!--
|
||||
Hi, thank you for opening an issue!
|
||||
Before hitting the button...
|
||||
|
||||
** Is this a REQUEST FOR HELP? **
|
||||
If so, please have a look at:
|
||||
- our FAQ: https://www.weave.works/docs/net/latest/faq/
|
||||
- our troubleshooting page: https://www.weave.works/docs/net/latest/troubleshooting/
|
||||
- our help page, to choose the best channel (Slack, etc.) to reach out: https://www.weave.works/help/
|
||||
|
||||
** Is this a FEATURE REQUEST? **
|
||||
If so, please search existing feature requests, and if you find a similar one, up-vote it and/or add your comments to it instead.
|
||||
If you did not find a similar one, please describe in details:
|
||||
- why: your use-case, specific constraints you may have, etc.
|
||||
- what: the feature/behaviour/change you would like to see in Weave Net
|
||||
Do not hesitate, when appropriate, to share the exact commands or API you would like, and/or to share a diagram (e.g.: asciiflow.com): "a picture is worth a thousand words".
|
||||
|
||||
** Is this a BUG REPORT? **
|
||||
Please fill in as much of the template below as you can.
|
||||
|
||||
Thank you!
|
||||
-->
|
||||
|
||||
## What you expected to happen?
|
||||
|
||||
## What happened?
|
||||
<!-- Error message, actual behaviour, etc. -->
|
||||
|
||||
## How to reproduce it?
|
||||
<!-- Specific steps, as minimally and precisely as possible. -->
|
||||
|
||||
## Anything else we need to know?
|
||||
<!-- Cloud provider? Hardware? How did you configure your cluster? Kubernetes YAML, KOPS, etc. -->
|
||||
|
||||
## Versions:
|
||||
<!-- Please paste in the output of these commands; 'kubectl' only if using Kubernetes -->
|
||||
```
|
||||
$ weave version
|
||||
$ docker version
|
||||
$ uname -a
|
||||
$ kubectl version
|
||||
```
|
||||
|
||||
## Logs:
|
||||
```
|
||||
$ docker logs weave
|
||||
```
|
||||
or, if using Kubernetes:
|
||||
```
|
||||
$ kubectl logs -n kube-system <weave-net-pod> weave
|
||||
```
|
||||
<!-- (If output is long, please consider a Gist.) -->
|
||||
<!-- Anything interesting or unusual output by the below, potentially relevant, commands?
|
||||
$ journalctl -u docker.service --no-pager
|
||||
$ journalctl -u kubelet --no-pager
|
||||
$ kubectl get events
|
||||
-->
|
||||
|
||||
## Network:
|
||||
<!-- If your problem has anything to do with one network endpoint not being able to contact another, please run the following commands -->
|
||||
```
|
||||
$ ip route
|
||||
$ ip -4 -o addr
|
||||
$ sudo iptables-save
|
||||
```
|
||||
+9
-4
@@ -33,11 +33,13 @@ coverage.html
|
||||
# Vim backup files
|
||||
*.swp
|
||||
|
||||
# Sed backup files
|
||||
*.bak
|
||||
|
||||
# Project specific
|
||||
prog/weaver/weaver
|
||||
prog/weavedns/weavedns
|
||||
prog/weavehosts/weavehosts
|
||||
prog/weaveproxy/weaveproxy
|
||||
prog/sigproxy/sigproxy
|
||||
prog/weavewait/weavewait
|
||||
prog/weavewait/weavewait_noop
|
||||
@@ -53,14 +55,13 @@ testing/runner/runner
|
||||
releases
|
||||
.*.uptodate
|
||||
weave*.tar.gz
|
||||
prog/weaveexec/weave
|
||||
prog/weaver/weave
|
||||
prog/weaveexec/weavehosts
|
||||
prog/weaveexec/weaveproxy
|
||||
prog/weaveexec/weavewait
|
||||
prog/weaveexec/weavewait_noop
|
||||
prog/weaveexec/weavewait_nomcast
|
||||
prog/weaveexec/sigproxy
|
||||
prog/weaveexec/weaveutil
|
||||
prog/weaver/weaveutil
|
||||
prog/weaveexec/docker*
|
||||
Vagrantfile.local
|
||||
test/tls/tls
|
||||
@@ -70,3 +71,7 @@ test/coverage.*
|
||||
*qemu-*-static
|
||||
terraform.tfstate
|
||||
terraform.tfstate.backup
|
||||
prog/net-plugin/rootfs/
|
||||
|
||||
# Website
|
||||
site-build
|
||||
+57
-3
@@ -61,9 +61,6 @@
|
||||
[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/appc/cni"]
|
||||
path = vendor/github.com/appc/cni
|
||||
url = https://github.com/appc/cni
|
||||
[submodule "vendor/github.com/coreos/go-iptables"]
|
||||
path = vendor/github.com/coreos/go-iptables
|
||||
url = https://github.com/coreos/go-iptables
|
||||
@@ -124,3 +121,60 @@
|
||||
[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
|
||||
[submodule "vendor/k8s.io/api"]
|
||||
path = vendor/k8s.io/api
|
||||
url = https://github.com/kubernetes/api
|
||||
[submodule "vendor/k8s.io/apimachinery"]
|
||||
path = vendor/k8s.io/apimachinery
|
||||
url = https://github.com/kubernetes/apimachinery
|
||||
[submodule "vendor/github.com/PuerkitoBio/purell"]
|
||||
path = vendor/github.com/PuerkitoBio/purell
|
||||
url = https://github.com/PuerkitoBio/purell
|
||||
[submodule "vendor/github.com/PuerkitoBio/urlesc"]
|
||||
path = vendor/github.com/PuerkitoBio/urlesc
|
||||
url = https://github.com/PuerkitoBio/urlesc
|
||||
[submodule "vendor/github.com/emicklei/go-restful"]
|
||||
path = vendor/github.com/emicklei/go-restful
|
||||
url = https://github.com/emicklei/go-restful
|
||||
[submodule "vendor/github.com/go-openapi/jsonpointer"]
|
||||
path = vendor/github.com/go-openapi/jsonpointer
|
||||
url = https://github.com/go-openapi/jsonpointer
|
||||
[submodule "vendor/github.com/go-openapi/jsonreference"]
|
||||
path = vendor/github.com/go-openapi/jsonreference
|
||||
url = https://github.com/go-openapi/jsonreference
|
||||
[submodule "vendor/github.com/go-openapi/spec"]
|
||||
path = vendor/github.com/go-openapi/spec
|
||||
url = https://github.com/go-openapi/spec
|
||||
[submodule "vendor/github.com/go-openapi/swag"]
|
||||
path = vendor/github.com/go-openapi/swag
|
||||
url = https://github.com/go-openapi/swag
|
||||
[submodule "vendor/github.com/golang/glog"]
|
||||
path = vendor/github.com/golang/glog
|
||||
url = https://github.com/golang/glog
|
||||
[submodule "vendor/github.com/google/gofuzz"]
|
||||
path = vendor/github.com/google/gofuzz
|
||||
url = https://github.com/google/gofuzz
|
||||
[submodule "vendor/github.com/mailru/easyjson"]
|
||||
path = vendor/github.com/mailru/easyjson
|
||||
url = https://github.com/mailru/easyjson
|
||||
[submodule "vendor/k8s.io/kube-openapi"]
|
||||
path = vendor/k8s.io/kube-openapi
|
||||
url = https://github.com/kubernetes/kube-openapi
|
||||
[submodule "vendor/golang.org/x/text"]
|
||||
path = vendor/golang.org/x/text
|
||||
url = https://go.googlesource.com/text
|
||||
[submodule "vendor/gopkg.in/yaml.v2"]
|
||||
path = vendor/gopkg.in/yaml.v2
|
||||
url = https://gopkg.in/yaml.v2
|
||||
@@ -0,0 +1 @@
|
||||
2.3.0
|
||||
+345
-2
@@ -1,6 +1,348 @@
|
||||
## 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 Contributers
|
||||
|
||||
Thanks to the following contributors:
|
||||
@caarlos0
|
||||
@dtshepherd
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/59?closed=1)
|
||||
|
||||
|
||||
## Release 2.0.5
|
||||
|
||||
Bug fixes
|
||||
|
||||
* Fix /etc/hosts inside containers so the container's name resolves to
|
||||
its Weave Net address #3136,#3138
|
||||
* New weave-kube config for Kubernetes 1.7 and 1.8 which resolves an
|
||||
intermittent conflict with kube-proxy that could break Weave Net
|
||||
until reboot #2998,#3134
|
||||
* Remove persistence file in `weave reset` when using Docker plugin V2 #3103,#3114
|
||||
|
||||
Build and test
|
||||
|
||||
* Some trivial changes to placate go-lint #3137
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/63?closed=1).
|
||||
|
||||
|
||||
## Release 2.0.4
|
||||
|
||||
Bug fixes
|
||||
|
||||
* weave-npc failed on Centos 7, due to older 'ipset' version in kernel #3099,#3100
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/62?closed=1).
|
||||
|
||||
|
||||
## Release 2.0.3
|
||||
|
||||
Bug fixes
|
||||
|
||||
* Weave-npc would crash on a policy with no 'from' part - regression introduced in 2.0.2 #3095,#3096,#3097
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/61?closed=1).
|
||||
|
||||
|
||||
## Release 2.0.2
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Fix race condition in weave-npc which would intermittently block all traffic for a namespace #3057,#3059
|
||||
* Ensure Fast Datapath works on machines that need to mount the kernel module dynamically #3080
|
||||
* Regression: weave-npc would block everything if `kubelet --hostname-override` was used #3049,#3051
|
||||
* Fix netfilter rules to block containers from accessing the Weave Net control endpoint #3093
|
||||
* If DNS server is off then disable proxy DNS registration, to avoid spurious errors #3054,#3088
|
||||
* Add comments to each iptables rule and ipset, to help when troubleshooting #3064
|
||||
* Remove code that checked for an outdated fallback address for Kubernetes api-server #3071
|
||||
* Add a label to the weavedb image so it can be filtered out by tools #3066
|
||||
* Fix various build and continuous-integration failures #3061
|
||||
* Print 'help' text faster in the weave script #3056
|
||||
* Add an option to create continuous integration hosts in different ways #3060
|
||||
* Remove remnants of the pre-2.0 proxy and plugin from build and test #3035,#3036
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/60?closed=1).
|
||||
|
||||
|
||||
## Release 2.0.1
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Fall back to slower data path (`sleeve`), rather than crashing, when the machine lacks VXLAN support (required for “fast data path”, `fastdp`) #3043
|
||||
* Fix bug in processing of arguments when Docker has TLS enabled, rather than crashing with invalid peers list, e.g. `lookup --tlsverify: no such host` #3039
|
||||
* Add `kube-system` namespace back to `weave-kube`'s YAMLs, preventing omissions leading to errors like `error contacting APIServer: the server does not allow access to the requested resource` #3033,#3042
|
||||
* Fix release script to prevent ARM64 binaries to end up in AMD64 `net-plugin`, leading to `Error response from daemon: dial unix /run/docker/plugins/<id>/weave.sock: connect: no such file or directory` when installing `net-plugin` #3045
|
||||
* `weave reset` and `weave rmpeer` now only contact Weave Cloud when Weave Net is configured with a Weave Cloud token, preventing unnecessary requests and potentially confusing `401 Unauthorized` errors in Weave Net’s logs #3044
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/58?closed=1).
|
||||
|
||||
|
||||
## Release 2.0.0
|
||||
|
||||
New Features
|
||||
|
||||
Peer Discovery via Weave Cloud
|
||||
|
||||
You can now get all your Weave Net peers to find each other via the
|
||||
Weave Cloud service, instead of maintaining a list of peers at
|
||||
startup. #2799,#2827
|
||||
|
||||
See the [docs
|
||||
page](https://github.com/weaveworks/weave/blob/master/site/using-weave/weave-cloud.md)
|
||||
for more details
|
||||
|
||||
New Docker Plugin
|
||||
|
||||
Docker has a [new plugin
|
||||
system](https://docs.docker.com/engine/extend/) which improves the
|
||||
installation UX and solves some issues around startup. This means
|
||||
Weave Net 2.0 can now run with Docker in "swarm mode" and supports the
|
||||
`docker service` command. #2396,#2397,#2651,#2727,#2805,#2816,#2905,
|
||||
#2906,#2929,#2932,#2945,#2950,#2956,#2963,#2964,#2966,#3019
|
||||
|
||||
The previous Docker Plugin is still available and can be installed as before.
|
||||
|
||||
All of Weave Net now runs in one container
|
||||
|
||||
Previously we had three separate containers for routing, Docker API
|
||||
proxy and Docker plugin. Running everything in one simplifies start-up
|
||||
and removes the need to detect various error
|
||||
conditions. #1642,#2897,#2936,#2945,#2946,#2951,#2960
|
||||
|
||||
The individual commands ‘weave launch-router’, ‘weave launch-plugin’,
|
||||
etc., have been removed. You can turn off the plugin and proxy with
|
||||
new command-line options. In keeping with Semantic Versioning, we have
|
||||
changed the major version number for this release.
|
||||
|
||||
Other new features
|
||||
|
||||
* Kubernetes configuration now comes from our “Launch Generator” that
|
||||
allows different options to be selected via
|
||||
URL. #2754,#2903,#3000,#3001
|
||||
* `weave-kube` now stores data about IP allocation in `/var/lib/weave`
|
||||
on the host instead of in a Kubernetes volume. This means that the
|
||||
data will persist across pod deletion and re-creation, e.g. during
|
||||
an upgrade of Weave Net, which makes restarts more
|
||||
reliable. #2610,#2967
|
||||
* `weave-kube` turned on rolling updates, so careful manual handling
|
||||
of updates is no longer required. #3024
|
||||
|
||||
Bug fixes
|
||||
|
||||
* Kubernetes Network Policies which allowed a specific set of pods to
|
||||
connect would block all pods on other hosts. Revert the change in
|
||||
v1.9.6 which ignored pods on other hosts #3025,#3028
|
||||
|
||||
Features removed
|
||||
|
||||
* `weave run` has been removed. This was the original method provided
|
||||
to start containers with Weave Net, but it always required care over
|
||||
timing of start-up, and we now provide three alternative, better,
|
||||
ways. You can replicate the effect by calling `docker run` then
|
||||
`weave attach`. Similarly `weave start` and `weave restart` were
|
||||
removed. #2353,#2885
|
||||
* Everything deprecated more than one release ago has been removed, so
|
||||
if you use it now you get an error rather than a warning. This
|
||||
includes the ‘create-bridge’ command and older command-line
|
||||
arguments, e.g. `--iprange` was replaced by `--ipalloc-range`
|
||||
#2901,#2909,#2913,#2942,#2989,#2991
|
||||
|
||||
Functions moved from shell-script to Go code.
|
||||
|
||||
This enables more precise error-checking and runs a bit faster. It has
|
||||
also enabled us to shrink the size of images downloaded: `weave-kube`
|
||||
is 101MB compared to 163MB previously #2953,#2954,#2974
|
||||
|
||||
Specific items that moved from shell-script to Go:
|
||||
* Setting up the `weave` bridge #1958,#2975,#2977,#2978
|
||||
* Container attachment #2947
|
||||
* Creation of the ’weave’ default plugin network #2920
|
||||
|
||||
Minor improvements
|
||||
|
||||
* You can now restart the Weave Net router without requiring the proxy to be enabled #2112
|
||||
* Plugin (legacy version) now respects `--ipalloc-default-subnet` option #2919
|
||||
* The `weave` script now detects and issues an error message if
|
||||
`weave-kube` is running and you attempt to launch again from the script. #2709/#2966
|
||||
* It is now possible to choose the the MAC address of the `weave`
|
||||
bridge using `--name`, in case your hosts have identical unique
|
||||
IDs. #2900
|
||||
* Relaxed Kubernetes tolerations for Weave Net's daemonset in order to
|
||||
match any node (previously, only taints directed at master). #3018
|
||||
* Kubernetes' `seLinuxOptions` configuration is now empty by default,
|
||||
to reduce spurious failures on hosts not using seLinux. #3001
|
||||
* Improved reliability of namespace changes via `nsenter`. #2992
|
||||
* `weave ps` now fetches the list container IDs internally, rather
|
||||
than calling out to `docker ps` #2814,#2898
|
||||
* at startup, actively remove dead containers’ Weave Net IP addresses from IPAM #3013
|
||||
* at startup, only check live containers to see if they have an
|
||||
existing Weave Net IP address #2815,#2829
|
||||
* Weave Net CNI plugin now logs but does not raise an error if
|
||||
anything goes wrong during network interface delete, to be more
|
||||
compatible with Kubernetes 1.6. #2928
|
||||
* Stop running a shell in “privileged” mode when it’s only writing a file #2838
|
||||
* New internal REST endpoint to return all IP address mappings. #1350
|
||||
* Changed the wording where we do not log the password #2833
|
||||
* Fixed typo in plugin error messages #2894
|
||||
|
||||
Build and test
|
||||
|
||||
* Weave Net is now built with Go version 1.8, which has better code
|
||||
generation and garbage collection #2914
|
||||
* During smoke-tests, use a webserver instead of just `ping` so we get
|
||||
a more realistic test that the Weave network is working #2918
|
||||
* When installing dependencies for the build container, use a
|
||||
keyserver port that's better for firewalls #2812
|
||||
* Kubernetes test script now scales up to more hosts, and works with
|
||||
Kubernetes 1.6 #2837,#2853,#2923
|
||||
* Other minor build improvements and refactoring #2760,#2910
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/49?closed=1).
|
||||
|
||||
|
||||
## Release 1.9.8
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Fix weave-npc blocking NodePort and any other non-local access #3011,#3014
|
||||
* Fix bug where IPAM would duplicate a fixed IP address assigned via Docker plugin #3003,#3010
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/56?closed=1).
|
||||
|
||||
|
||||
## Release 1.9.7
|
||||
|
||||
This is identical to 1.9.6 with one additional bug-fix:
|
||||
|
||||
* weave-npc would block everything if `kubelet --hostname-override` was used #2995,#2996
|
||||
|
||||
## Release 1.9.6
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Ensure that Kubernetes pods can contact a service implemented within
|
||||
the same pod, by turning on "hairpin mode". This is required because
|
||||
of a quiet change between Kubernetes 1.5 and 1.6. #2993
|
||||
* Network Policy Controller (`weave-npc`) now checks local addresses
|
||||
only, so it doesn't interfere with cross-cluster traffic. It should
|
||||
be more efficient too #2622,#2973,#2979
|
||||
* Stop reporting back to Kubernetes any issues encountered when
|
||||
deleting a pod's network interface. This is required because
|
||||
of a quiet change between Kubernetes 1.5 and 1.6. #2921,#2928
|
||||
* Fixed an issue whereby `weave-npc` couldn't start because one
|
||||
`ipset` was referring to another one and could not be destroyed #2915,#2949
|
||||
* Improved the code which checks whether the kernel supports `ipset` #2934,#2935
|
||||
* `weave-npc` now creates ipsets with only valid xml characters in the
|
||||
name #2958,#2959
|
||||
|
||||
Build and Testing
|
||||
|
||||
* In build container use cross-compilers from debian package
|
||||
repository, so they match other components #2940
|
||||
* Pin the version of the linting tool `shfmt` so the set of things it
|
||||
checks is stable #2987
|
||||
* Fix lint error in script that runs smoke-tests #2962
|
||||
* Moved website publishing from Wordpress to Netlify #2986
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/55?closed=1).
|
||||
|
||||
## Release 1.9.5
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Improve log messages generated if "hairpin" conditions are detected,
|
||||
to make clear which kind is likely to cause problems #2808/#2926
|
||||
* Filter out IPv6 peer addresses from Kubernetes; Weave Net currently
|
||||
only supports IPv4 #2904/#2912
|
||||
* Fix rare crash during initialization of weave-kube #2893/#2892
|
||||
* Include overlay and encryption modes in checkpoint reports, in case
|
||||
this is relevant to a version upgrade #2771/#2907
|
||||
|
||||
Build and Testing
|
||||
|
||||
* Ensure CI build can run gcloud tools #2887
|
||||
* Prevent kubeadm from upgrading Kubernetes if we are trying to test an older version #2886
|
||||
* Upgrade build scripts to support Kubernetes 1.6 #2880
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/54?closed=1).
|
||||
|
||||
## Release 1.9.4
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Support Kubernetes 1.6 by creating a new DaemonSet #2777,#2801
|
||||
* Support Kubernetes 1.6 by allowing CNI callers to send a
|
||||
network-delete request for a container that is not running or has
|
||||
never been attached to the network #2850
|
||||
* Leave non-weave ipsets alone in Network Policy Controller (e.g. when
|
||||
running Weave Net alonside keepalived-vip) #2751,#2846
|
||||
* Fix various small issues revealed by 'staticcheck' tool #2843,#2857
|
||||
* Avoid leaving 'defunct' processes when weave-kube container restarts #2836,#2845
|
||||
* When using the CNI plugin with a non-standard network configuration
|
||||
file, the weave bridge could get the same IP as a container, if
|
||||
'weave expose' hadn't run at that point #2839,#2856
|
||||
|
||||
Build and Testing
|
||||
|
||||
* Check that no defunct processes remain after each test #2852
|
||||
* Update build and test scripts to work with Kubernetes 1.6 beta #2851
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/53?closed=1).
|
||||
|
||||
## Release 1.9.3
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Fixed a race condition in Fast Datapath encrypted connections which could lead Weave Net to crash: #2824, #2825.
|
||||
|
||||
## Release 1.9.2
|
||||
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Fix a weave-kube bug when `br_netfilter` or `xt_set` module is compiled into
|
||||
kernel #2820/#2821
|
||||
* Detect the absence of the required `xt_set` kernel module #2821
|
||||
|
||||
## Release 1.9.1
|
||||
|
||||
# Bug fixes and minor improvements
|
||||
Bug fixes and minor improvements
|
||||
|
||||
* Fix a race condition when the Weave Net container is restarted
|
||||
which could allow a new container to be allocated the same IP
|
||||
address as an existing one #2784,#2787
|
||||
@@ -14,7 +356,8 @@
|
||||
larger clusters can be created #2781
|
||||
* WeaveDNS was incorrectly case-sensitive for reverse DNS lookups #2817,#2818
|
||||
|
||||
# Build and Testing
|
||||
Build and Testing
|
||||
|
||||
* Scripts to create VMs to run automated tests were rewritten to use
|
||||
Terraform and Ansible, to make it much easier to test with different
|
||||
versions of components such as Docker and Kubernetes #2647,#2694,#2775,#2796
|
||||
|
||||
+3
-3
@@ -1,3 +1,3 @@
|
||||
DOCKER_VERSION=1.11.2
|
||||
KUBERNETES_VERSION=1.5.2
|
||||
KUBERNETES_CNI_VERSION=0.3.0.1
|
||||
DOCKER_VERSION=17.06
|
||||
KUBERNETES_VERSION=1.7.8
|
||||
KUBERNETES_CNI_VERSION=0.5.1
|
||||
@@ -176,7 +176,7 @@
|
||||
|
||||
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");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.DEFAULT: all
|
||||
.PHONY: all exes testrunner update tests lint publish-one-arch $(PUBLISH) clean clean-bin prerequisites build run-smoketests
|
||||
.PHONY: all exes testrunner update tests lint publish-one-arch $(PUBLISH) clean clean-bin clean-work-dir prerequisites build run-smoketests
|
||||
|
||||
# If you can use docker without being root, you can do "make SUDO="
|
||||
SUDO=$(shell docker info >/dev/null 2>&1 || echo "sudo -E")
|
||||
@@ -17,15 +17,15 @@ QEMU_VERSION=v2.7.0
|
||||
|
||||
# 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_ARCHITECTURES=amd64 arm arm64
|
||||
ML_PLATFORMS=linux/amd64,linux/arm,linux/arm64
|
||||
ALL_ARCHITECTURES=amd64 arm arm64 ppc64le
|
||||
ML_PLATFORMS=linux/amd64,linux/arm,linux/arm64,linux/ppc64le
|
||||
|
||||
ifeq ($(ARCH),amd64)
|
||||
# The architecture to use when downloading the docker binary
|
||||
WEAVEEXEC_DOCKER_ARCH?=x86_64
|
||||
|
||||
# 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
|
||||
# Specifying none means for example weaveworks/weave:latest
|
||||
@@ -42,7 +42,7 @@ ifeq ($(ARCH),arm)
|
||||
WEAVEEXEC_DOCKER_ARCH?=armel
|
||||
|
||||
# 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
|
||||
ARCH_EXT?=-arm
|
||||
@@ -62,7 +62,7 @@ ifeq ($(ARCH),arm64)
|
||||
WEAVEEXEC_DOCKER_ARCH?=armel
|
||||
|
||||
# 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
|
||||
ARCH_EXT?=-arm64
|
||||
@@ -77,15 +77,36 @@ ifeq ($(ARCH),arm64)
|
||||
# Tell the gcc linker to search for libpcap here
|
||||
CGO_LDFLAGS="-L/usr/local/lib/$(CC)"
|
||||
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
|
||||
DOCKERHUB_USER?=weaveworks
|
||||
# The default version that's chosen when pushing the images. Can/should be overridden
|
||||
WEAVE_VERSION?=git-$(shell git rev-parse --short=12 HEAD)
|
||||
# Docker Store does not allow the "latest" tag.
|
||||
NET_PLUGIN_LATEST=latest_release
|
||||
|
||||
# Paths to all relevant binaries that should be compiled
|
||||
WEAVER_EXE=prog/weaver/weaver
|
||||
WEAVEPROXY_EXE=prog/weaveproxy/weaveproxy
|
||||
SIGPROXY_EXE=prog/sigproxy/sigproxy
|
||||
KUBEPEERS_EXE=prog/kube-peers/kube-peers
|
||||
WEAVENPC_EXE=prog/weave-npc/weave-npc
|
||||
@@ -93,45 +114,47 @@ WEAVEWAIT_EXE=prog/weavewait/weavewait
|
||||
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
|
||||
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
|
||||
WEAVEUTIL_EXE=prog/weaveutil/weaveutil
|
||||
PLUGIN_EXE=prog/plugin/plugin
|
||||
RUNNER_EXE=tools/runner/runner
|
||||
MANIFEST_TOOL_DIR=vendor/github.com/estesp/manifest-tool
|
||||
MANIFEST_TOOL_EXE=$(MANIFEST_TOOL_DIR)/manifest-tool
|
||||
TEST_TLS_EXE=test/tls/tls
|
||||
NETWORKTESTER_EXE=test/images/network-tester/webserver
|
||||
|
||||
# All binaries together in a list
|
||||
EXES=$(WEAVER_EXE) $(SIGPROXY_EXE) $(KUBEPEERS_EXE) $(WEAVENPC_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVEUTIL_EXE) $(PLUGIN_EXE) $(RUNNER_EXE) $(TEST_TLS_EXE) $(MANIFEST_TOOL_EXE)
|
||||
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)
|
||||
|
||||
# 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
|
||||
WEAVER_UPTODATE=.weaver$(ARCH_EXT).uptodate
|
||||
WEAVEEXEC_UPTODATE=.weaveexec$(ARCH_EXT).uptodate
|
||||
PLUGIN_UPTODATE=.plugin$(ARCH_EXT).uptodate
|
||||
PLUGIN_UPTODATE=.net-plugin$(ARCH_EXT).uptodate
|
||||
WEAVEKUBE_UPTODATE=.weavekube$(ARCH_EXT).uptodate
|
||||
WEAVENPC_UPTODATE=.weavenpc$(ARCH_EXT).uptodate
|
||||
WEAVEDB_UPTODATE=.weavedb.uptodate
|
||||
NETWORKTESTER_UPTODATE=.networktester$(ARCH_EXT).uptodate
|
||||
|
||||
IMAGES_UPTODATE=$(WEAVER_UPTODATE) $(WEAVEEXEC_UPTODATE) $(PLUGIN_UPTODATE) $(WEAVEKUBE_UPTODATE) $(WEAVENPC_UPTODATE)
|
||||
IMAGES_UPTODATE=$(WEAVER_UPTODATE) $(WEAVEEXEC_UPTODATE) $(WEAVEKUBE_UPTODATE) $(WEAVENPC_UPTODATE) $(NETWORKTESTER_UPTODATE)
|
||||
|
||||
# The names of the images. Note that the images for other architectures than amd64 have a suffix in the image name.
|
||||
WEAVER_IMAGE=$(DOCKERHUB_USER)/weave$(ARCH_EXT)
|
||||
WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec$(ARCH_EXT)
|
||||
PLUGIN_IMAGE=$(DOCKERHUB_USER)/plugin$(ARCH_EXT)
|
||||
WEAVEKUBE_IMAGE=$(DOCKERHUB_USER)/weave-kube$(ARCH_EXT)
|
||||
WEAVENPC_IMAGE=$(DOCKERHUB_USER)/weave-npc$(ARCH_EXT)
|
||||
BUILD_IMAGE=weaveworks/weavebuild
|
||||
WEAVEDB_IMAGE=$(DOCKERHUB_USER)/weavedb
|
||||
PLUGIN_IMAGE=$(DOCKERHUB_USER)/net-plugin
|
||||
NETWORKTESTER_IMAGE=$(DOCKERHUB_USER)/network-tester
|
||||
|
||||
IMAGES=$(WEAVER_IMAGE) $(WEAVEEXEC_IMAGE) $(PLUGIN_IMAGE) $(WEAVEKUBE_IMAGE) $(WEAVENPC_IMAGE) $(WEAVEDB_IMAGE)
|
||||
IMAGES=$(WEAVER_IMAGE) $(WEAVEEXEC_IMAGE) $(WEAVEKUBE_IMAGE) $(WEAVENPC_IMAGE) $(WEAVEDB_IMAGE) $(NETWORKTESTER_IMAGE)
|
||||
|
||||
PUBLISH=publish_weave publish_weaveexec publish_plugin publish_weave-kube publish_weave-npc
|
||||
PUSH_ML=push_ml_weave push_ml_weaveexec push_ml_plugin push_ml_weave-kube push_ml_weave-npc
|
||||
PLUGIN_WORK_DIR="prog/net-plugin/rootfs"
|
||||
PLUGIN_BUILD_IMG="plugin-builder"
|
||||
|
||||
PUBLISH=publish_weave publish_weaveexec publish_weave-kube publish_weave-npc
|
||||
PUSH_ML=push_ml_weave push_ml_weaveexec push_ml_weave-kube push_ml_weave-npc
|
||||
|
||||
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 || { \
|
||||
rm $@; \
|
||||
echo "\nYour go standard library was built without the 'netgo' build tag."; \
|
||||
@@ -150,20 +173,21 @@ PACKAGE_BASE=$(shell go list -e ./)
|
||||
all: $(WEAVE_EXPORT)
|
||||
testrunner: $(RUNNER_EXE) $(TEST_TLS_EXE)
|
||||
|
||||
$(WEAVER_EXE) $(WEAVEPROXY_EXE) $(WEAVEUTIL_EXE): common/*.go common/*/*.go net/*.go net/*/*.go
|
||||
$(WEAVER_EXE) $(WEAVEUTIL_EXE): common/*.go common/*/*.go net/*.go net/*/*.go
|
||||
$(WEAVER_EXE): router/*.go ipam/*.go ipam/*/*.go db/*.go nameserver/*.go prog/weaver/*.go
|
||||
$(WEAVEPROXY_EXE): proxy/*.go prog/weaveproxy/*.go
|
||||
$(WEAVER_EXE): api/*.go plugin/*.go plugin/*/*
|
||||
$(WEAVER_EXE): proxy/*.go
|
||||
$(WEAVEUTIL_EXE): prog/weaveutil/*.go net/*.go plugin/net/*.go plugin/ipam/*.go db/*.go
|
||||
$(SIGPROXY_EXE): prog/sigproxy/*.go
|
||||
$(KUBEPEERS_EXE): prog/kube-peers/*.go
|
||||
$(WEAVENPC_EXE): prog/weave-npc/*.go npc/*.go npc/*/*.go
|
||||
$(PLUGIN_EXE): prog/plugin/*.go plugin/*/*.go api/*.go common/*.go common/docker/*.go net/*.go
|
||||
$(TEST_TLS_EXE): test/tls/*.go
|
||||
$(RUNNER_EXE): tools/runner/*.go
|
||||
$(MANIFEST_TOOL_EXE): $(MANIFEST_TOOL_DIR)/*.go
|
||||
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
|
||||
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
|
||||
$(WEAVEWAIT_NOMCAST_EXE): prog/weavewait/*.go net/*.go
|
||||
$(NETWORKTESTER_EXE): test/images/network-tester/*.go
|
||||
tests: tools/.git
|
||||
lint: tools/.git
|
||||
|
||||
@@ -173,18 +197,21 @@ ifeq ($(BUILD_IN_CONTAINER),true)
|
||||
# It bind-mounts the source into the container and passes all important variables
|
||||
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
|
||||
$(SUDO) docker run $(RM) $(RUN_FLAGS) \
|
||||
-v $(shell pwd):/go/src/github.com/weaveworks/weave \
|
||||
-v $(shell pwd)/.pkg:/go/pkg \
|
||||
-e GOARCH=$(ARCH) -e CGO_ENABLED=1 -e GOOS=linux -e CIRCLECI -e CIRCLE_BUILD_NUM -e CIRCLE_NODE_TOTAL -e CIRCLE_NODE_INDEX -e COVERDIR -e SLOW -e DEBUG \
|
||||
$(BUILD_IMAGE) COVERAGE=$(COVERAGE) WEAVE_VERSION=$(WEAVE_VERSION) CC=$(CC) QEMUARCH=$(QEMUARCH) CGO_LDFLAGS=$(CGO_LDFLAGS) $@
|
||||
touch $@
|
||||
|
||||
else
|
||||
|
||||
exes: $(EXES)
|
||||
|
||||
$(WEAVER_EXE) $(WEAVEPROXY_EXE) $(PLUGIN_EXE):
|
||||
$(WEAVER_EXE):
|
||||
ifeq ($(COVERAGE),true)
|
||||
$(eval COVERAGE_MODULES := $(shell (go list ./$(@D); go list -f '{{join .Deps "\n"}}' ./$(@D) | grep "^$(PACKAGE_BASE)/") | grep -v "^$(PACKAGE_BASE)/vendor/" | paste -s -d,))
|
||||
go test -c -o ./$@ $(BUILD_FLAGS) -v -covermode=atomic -coverpkg $(COVERAGE_MODULES) ./$(@D)/
|
||||
@@ -193,7 +220,7 @@ else
|
||||
endif
|
||||
$(NETGO_CHECK)
|
||||
|
||||
$(WEAVEUTIL_EXE) $(KUBEPEERS_EXE) $(WEAVENPC_EXE):
|
||||
$(WEAVEUTIL_EXE) $(KUBEPEERS_EXE) $(WEAVENPC_EXE) $(NETWORKTESTER_EXE):
|
||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||
$(NETGO_CHECK)
|
||||
|
||||
@@ -234,35 +261,45 @@ $(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" $^ > $@
|
||||
ifeq ($(ARCH),amd64)
|
||||
# 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
|
||||
# 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 $@)
|
||||
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
|
||||
|
||||
# The targets below builds the weave images
|
||||
$(WEAVER_UPTODATE): prog/weaver/Dockerfile.$(DOCKERHUB_USER) $(WEAVER_EXE) $(WEAVEEXEC_UPTODATE)
|
||||
$(WEAVER_UPTODATE): prog/weaver/Dockerfile.$(DOCKERHUB_USER) $(WEAVER_EXE) weave $(WEAVEUTIL_EXE)
|
||||
cp $(WEAVEUTIL_EXE) prog/weaver/weaveutil
|
||||
cp weave prog/weaver/weave
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -f prog/weaver/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVER_IMAGE) prog/weaver
|
||||
touch $@
|
||||
|
||||
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) prog/weaveexec/symlink $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVEUTIL_EXE)
|
||||
cp weave prog/weaveexec/weave
|
||||
$(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 $(WEAVEPROXY_EXE) prog/weaveexec/weaveproxy
|
||||
cp $(WEAVEWAIT_EXE) prog/weaveexec/weavewait
|
||||
cp $(WEAVEWAIT_NOOP_EXE) prog/weaveexec/weavewait_noop
|
||||
cp $(WEAVEWAIT_NOMCAST_EXE) prog/weaveexec/weavewait_nomcast
|
||||
cp $(WEAVEUTIL_EXE) prog/weaveexec/weaveutil
|
||||
tar -xf $(DOCKER_DISTRIB) usr/local/bin/docker -O > prog/weaveexec/docker
|
||||
chmod +x prog/weaveexec/docker
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -f prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVEEXEC_IMAGE) prog/weaveexec
|
||||
touch $@
|
||||
|
||||
$(PLUGIN_UPTODATE): prog/plugin/Dockerfile.$(DOCKERHUB_USER) $(PLUGIN_EXE) $(WEAVER_UPTODATE)
|
||||
$(SUDO) docker build -f prog/plugin/Dockerfile.$(DOCKERHUB_USER) -t $(PLUGIN_IMAGE) prog/plugin
|
||||
# Builds Docker plugin.
|
||||
$(PLUGIN_UPTODATE): prog/net-plugin/launch.sh prog/net-plugin/config.json $(WEAVER_UPTODATE)
|
||||
-$(SUDO) docker rm -f $(PLUGIN_BUILD_IMG) 2>/dev/null
|
||||
$(SUDO) docker create --name=$(PLUGIN_BUILD_IMG) $(WEAVER_IMAGE) true
|
||||
rm -rf $(PLUGIN_WORK_DIR)
|
||||
mkdir $(PLUGIN_WORK_DIR)
|
||||
$(SUDO) docker export $(PLUGIN_BUILD_IMG) | tar -x -C $(PLUGIN_WORK_DIR)
|
||||
$(SUDO) docker rm -f $(PLUGIN_BUILD_IMG)
|
||||
cp prog/net-plugin/launch.sh $(PLUGIN_WORK_DIR)/home/weave/launch.sh
|
||||
-$(SUDO) docker plugin disable $(PLUGIN_IMAGE):$(WEAVE_VERSION) 2>/dev/null
|
||||
-$(SUDO) docker plugin rm $(PLUGIN_IMAGE):$(WEAVE_VERSION) 2>/dev/null
|
||||
$(SUDO) docker plugin create $(PLUGIN_IMAGE):$(WEAVE_VERSION) prog/net-plugin
|
||||
-$(SUDO) docker plugin disable $(PLUGIN_IMAGE):$(NET_PLUGIN_LATEST) 2>/dev/null
|
||||
-$(SUDO) docker plugin rm $(PLUGIN_IMAGE):$(NET_PLUGIN_LATEST) 2>/dev/null
|
||||
$(SUDO) docker plugin create $(PLUGIN_IMAGE):$(NET_PLUGIN_LATEST) prog/net-plugin
|
||||
touch $@
|
||||
|
||||
$(WEAVEKUBE_UPTODATE): prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) prog/weave-kube/launch.sh $(KUBEPEERS_EXE) $(WEAVER_UPTODATE)
|
||||
@@ -278,18 +315,26 @@ $(WEAVEDB_UPTODATE): prog/weavedb/Dockerfile
|
||||
$(SUDO) docker build -t $(WEAVEDB_IMAGE) prog/weavedb
|
||||
touch $@
|
||||
|
||||
$(NETWORKTESTER_UPTODATE): test/images/network-tester/Dockerfile $(NETWORKTESTER_EXE)
|
||||
$(SUDO) docker build -t $(NETWORKTESTER_IMAGE) test/images/network-tester
|
||||
touch $@
|
||||
|
||||
$(WEAVE_EXPORT): $(IMAGES_UPTODATE) $(WEAVEDB_UPTODATE)
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker save $(addsuffix :latest,$(IMAGES)) | gzip > $@
|
||||
|
||||
$(DOCKER_DISTRIB):
|
||||
curl -o $(DOCKER_DISTRIB) $(DOCKER_DISTRIB_URL)
|
||||
cd $(shell dirname $@) && sha256sum -c $(shell pwd)/build/shasums/docker-tgz-$(WEAVEEXEC_DOCKER_ARCH).sha256sum
|
||||
|
||||
tools/.git $(MANIFEST_TOOL_DIR)/.git:
|
||||
git submodule update --init
|
||||
|
||||
# CODE FOR PUBLISHING THE IMAGES
|
||||
|
||||
# Push plugin
|
||||
plugin_publish: $(PLUGIN_UPTODATE)
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker plugin push $(PLUGIN_IMAGE):$(WEAVE_VERSION)
|
||||
# "$(NET_PLUGIN_LATEST)" means "stable release" here, so only push that if explicitly told to
|
||||
ifeq ($(UPDATE_LATEST),true)
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker plugin push $(PLUGIN_IMAGE):$(NET_PLUGIN_LATEST)
|
||||
endif
|
||||
|
||||
# This target first runs "make publish" for each architecture
|
||||
# Then it pushes the manifest lists
|
||||
publish: $(addprefix sub-publish-,$(ALL_ARCHITECTURES)) $(WEAVEDB_UPTODATE)
|
||||
@@ -309,7 +354,7 @@ publish-one-arch: $(PUBLISH)
|
||||
$(PUBLISH): publish_%: $(IMAGES_UPTODATE)
|
||||
# Tag :latest with the real 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)
|
||||
|
||||
ifeq ($(ARCH),amd64)
|
||||
@@ -342,13 +387,16 @@ ifneq ($(UPDATE_LATEST),false)
|
||||
$(MANIFEST_TOOL_EXE) push from-args --platforms $(ML_PLATFORMS) --template $(DOCKERHUB_USER)/$*-ARCH:latest --target $(DOCKERHUB_USER)/$*:latest
|
||||
endif
|
||||
|
||||
clean-work-dir:
|
||||
rm -rf $(PLUGIN_WORK_DIR)
|
||||
|
||||
clean-bin:
|
||||
-$(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 "*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: clean-bin clean-work-dir
|
||||
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(BUILD_IMAGE)
|
||||
rm -rf test/tls/*.pem test/coverage.* test/coverage $(BUILD_UPTODATE) $(MANIFEST_TOOL_EXE)
|
||||
|
||||
@@ -376,3 +424,14 @@ integration-tests: all testrunner
|
||||
# integration-tests
|
||||
#
|
||||
RUNNER_ARGS="-parallel" ./test/run-integration-tests.sh
|
||||
|
||||
# This target is only intended for use in Netlify CI environment for generating preview pages on feature branches and pull requests.
|
||||
# We need to obtain website repository (checked out under `site-build`) and place `site` directory into the context (`site-build/_weave_net_docs`).
|
||||
# We then run make in `site-build` and Netlify will publish the output (`site-build/_site`).
|
||||
netlify-site-preview:
|
||||
@mkdir -p site-build
|
||||
@curl --user $(WEBSITE_GITHUB_USER) --silent 'https://codeload.github.com/weaveworks/website-next/tar.gz/$(WEBSITE_BRANCH)' \
|
||||
| tar --extract --gunzip --directory site-build --strip 1
|
||||
@cp -r site site-build/_weave_net_docs
|
||||
@$(MAKE) -C site-build netlify_ensure_install
|
||||
@$(MAKE) -C site-build BUILD_ENV=netlify
|
||||
@@ -3,7 +3,6 @@
|
||||
[](https://circleci.com/gh/weaveworks/weave)
|
||||
[](https://coveralls.io/r/weaveworks/weave)
|
||||
[](https://goreportcard.com/report/github.com/weaveworks/weave)
|
||||
[](https://slack.weave.works)
|
||||
[](https://hub.docker.com/r/weaveworks/weave/)
|
||||
|
||||
# About Weaveworks
|
||||
@@ -11,11 +10,13 @@
|
||||
[Weaveworks](https://www.weave.works) is the company that delivers the most productive way for developers to connect, observe and control
|
||||
Docker containers.
|
||||
|
||||
This repository contains [Weave Net](https://www.weave.works/products/weave-net/), the first product developed by Weaveworks, and with over 8 million downloads to date, it enables you to get started with Docker clusters and portable apps in a fraction of the time compared with other solutions.
|
||||
This repository contains [Weave Net](https://www.weave.works/products/weave-net/), the first product developed by Weaveworks, and with over 8 million downloads to date, enables you to get started with Docker clusters and portable apps in a fraction of the time compared with other solutions.
|
||||
|
||||
Weaveworks' product [Weave Cloud](https://www.weave.works/solution/cloud/) takes the heavy lifting out of building, integrating and operating open source components, so you don’t have to manage databases, storage and availability.
|
||||
## Weave Cloud:
|
||||
|
||||
Weave Cloud is built using these Open Source projects: [Weave Scope](https://www.weave.works/products/weave-scope/), a powerful container monitoring tool that automatically maps Docker containers and their interactions, [Weave Cortex](https://github.com/weaveworks/cortex), a horizontally-scalable version of Prometheus, and [Weave Flux](https://www.weave.works/products/weave-flux/), a continuous deployment tool that works with Kubernetes.
|
||||
[Weave Cloud](https://www.weave.works/product/cloud/) is software-as-a-service that simplifies deployment, monitoring and management for containers and microservices. It extends and complements popular orchestrators, and enables developers and DevOps to realize faster deployments, insightful monitoring, visualization and networking.
|
||||
|
||||
Weave Cloud is built using these Open Source projects: [Weave Scope](https://www.weave.works/products/weave-scope/), a powerful container visualization tool that automatically maps Docker containers and their interactions, [Weave Cortex](https://github.com/weaveworks/cortex), a horizontally-scalable version of Prometheus, and [Weave Flux](https://www.weave.works/products/weave-flux/), a continuous deployment tool that works with Kubernetes.
|
||||
|
||||
# Weave Net
|
||||
|
||||
@@ -27,7 +28,7 @@ Services provided by application containers on the Weave network can be exposed
|
||||
|
||||
If you have any questions about, feedback for or a problem with Weave Net:
|
||||
|
||||
- Read [the Weave Net docs](https://www.weave.works/docs/net/latest/introducing-weave/).
|
||||
- Read [the Weave Net docs](https://www.weave.works/docs/net/latest/overview).
|
||||
- Invite yourself to the <a href="https://weaveworks.github.io/community-slack/" target="_blank"> #weave-community </a> slack channel.
|
||||
- Ask a question on the <a href="https://weave-community.slack.com/messages/general/"> #weave-community</a> slack channel.
|
||||
- Join the <a href="https://www.meetup.com/pro/Weave/"> Weave User Group </a> and get invited to online talks, hands-on training and meetups in your area.
|
||||
@@ -36,7 +37,7 @@ If you have any questions about, feedback for or a problem with Weave Net:
|
||||
|
||||
Your feedback is always welcome!
|
||||
|
||||
##Further Information
|
||||
## Further Information
|
||||
|
||||
* [Installing Weave Net](https://www.weave.works/docs/net/latest/installing-weave/)
|
||||
* [Weave Net Product Overview](https://www.weave.works/products/weave-net/)
|
||||
* [Installing Weave Net](https://www.weave.works/docs/net/latest/install/installing-weave)
|
||||
* [Weave Net Product Overview](https://www.weave.works/products/weave-net)
|
||||
+25
-10
@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Special token used in place of a container identifier when:
|
||||
@@ -14,21 +15,29 @@ import (
|
||||
// which should be kept in mind if the entry needs to be removed at any point.
|
||||
const NoContainerID string = "_"
|
||||
|
||||
func (client *Client) ipamOp(ID string, op string) (*net.IPNet, error) {
|
||||
ip, err := client.httpVerb(op, fmt.Sprintf("/ip/%s", ID), nil)
|
||||
func (client *Client) ipamOp(ID string, op string, values url.Values) (*net.IPNet, error) {
|
||||
ip, err := client.httpVerb(op, fmt.Sprintf("/ip/%s", ID), values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseIP(ip)
|
||||
}
|
||||
|
||||
// returns an IP for the ID given, allocating a fresh one if necessary
|
||||
func (client *Client) AllocateIP(ID string) (*net.IPNet, error) {
|
||||
return client.ipamOp(ID, "POST")
|
||||
func ipamValues(checkAlive bool) url.Values {
|
||||
values := make(url.Values)
|
||||
if checkAlive {
|
||||
values.Set("check-alive", "true")
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func (client *Client) AllocateIPInSubnet(ID string, subnet *net.IPNet) (*net.IPNet, error) {
|
||||
ip, err := client.httpVerb("POST", fmt.Sprintf("/ip/%s/%s", ID, subnet), nil)
|
||||
// returns an IP for the ID given, allocating a fresh one if necessary
|
||||
func (client *Client) AllocateIP(ID string, checkAlive bool) (*net.IPNet, error) {
|
||||
return client.ipamOp(ID, "POST", ipamValues(checkAlive))
|
||||
}
|
||||
|
||||
func (client *Client) AllocateIPInSubnet(ID string, subnet *net.IPNet, checkAlive bool) (*net.IPNet, error) {
|
||||
ip, err := client.httpVerb("POST", fmt.Sprintf("/ip/%s/%s", ID, subnet), ipamValues(checkAlive))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -38,12 +47,12 @@ func (client *Client) AllocateIPInSubnet(ID string, subnet *net.IPNet) (*net.IPN
|
||||
// returns an IP for the ID given, or nil if one has not been
|
||||
// allocated
|
||||
func (client *Client) LookupIP(ID string) (*net.IPNet, error) {
|
||||
return client.ipamOp(ID, "GET")
|
||||
return client.ipamOp(ID, "GET", nil)
|
||||
}
|
||||
|
||||
// Claim a specific IP on behalf of the ID
|
||||
func (client *Client) ClaimIP(ID string, cidr *net.IPNet) error {
|
||||
_, err := client.httpVerb("PUT", fmt.Sprintf("/ip/%s/%s", ID, cidr), nil)
|
||||
func (client *Client) ClaimIP(ID string, cidr *net.IPNet, checkAlive bool) error {
|
||||
_, err := client.httpVerb("PUT", fmt.Sprintf("/ip/%s/%s", ID, cidr), ipamValues(checkAlive))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -53,6 +62,12 @@ func (client *Client) ReleaseIPsFor(ID string) error {
|
||||
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) {
|
||||
cidr, err := client.httpVerb("GET", fmt.Sprintf("/ipinfo/defaultsubnet"), 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
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -76,6 +77,22 @@ func (client *Client) Connect(remote string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// IsReady returns true if the API server is up and running
|
||||
// (note it returns StatusServiceUnavailable until all parts are ready,
|
||||
// but callers of this function are expected to be ok with a partial service)
|
||||
func (client *Client) IsReady() bool {
|
||||
resp, err := http.Get(client.baseURL + "/status")
|
||||
return err == nil &&
|
||||
(resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusServiceUnavailable)
|
||||
}
|
||||
|
||||
// WaitAPIServer waits until the API server is ready to serve.
|
||||
func (client *Client) WaitAPIServer(n int) {
|
||||
for i := 0; i < n && !client.IsReady(); i++ {
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
type Logger interface {
|
||||
Infof(string, ...interface{})
|
||||
Debugf(string, ...interface{})
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
source "$STATE"
|
||||
|
||||
site_changes() {
|
||||
curl -s https://api.github.com/repos/weaveworks/weave/pulls/$1/files |
|
||||
jq -r '.[]|.status + " " + .filename' |
|
||||
grep -oP '^(added|modified|removed) site/.*(?=\.md$)'
|
||||
}
|
||||
|
||||
format_comment() {
|
||||
if [ ! -d .git ] || ! BRANCH=$(git symbolic-ref --short HEAD) || [ -z "$BRANCH" ] ; then
|
||||
echo "Could not determine branch" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "#### Documentation changes:"
|
||||
while read ACTION PAGE; do
|
||||
echo "$ACTION [${PAGE#site/}]($WP_DEV_URL/docs/net/${BRANCH#issues/}/${PAGE#site/}/)"
|
||||
done | sort
|
||||
}
|
||||
|
||||
post_comment() {
|
||||
jq -s -R '. | tostring | { body: . }' |
|
||||
curl -s -d @- \
|
||||
-H "Authorization: Bearer $WP_GH_COMMENT_TOKEN" \
|
||||
-H 'Content-Type: application/json' \
|
||||
https://api.github.com/repos/weaveworks/weave/issues/$GH_PR/comments > /dev/null
|
||||
}
|
||||
|
||||
cd $SRCDIR
|
||||
bin/install-wordepress v1.0.0
|
||||
bin/publish-site "$WP_DEV_URL" "$WP_DEV_USER" "$WP_DEV_PASSWORD"
|
||||
|
||||
if [ -n "$WP_GH_COMMENT_TOKEN" -a -n "$GH_PR" ] ; then
|
||||
echo ">>> Adding doc changelog to GitHub PR $GH_PR"
|
||||
if CHANGES=$(site_changes $GH_PR) && [ -n "$CHANGES" ] ; then
|
||||
echo "$CHANGES" | format_comment | post_comment
|
||||
fi
|
||||
fi
|
||||
@@ -11,7 +11,3 @@ if [ -n "$TEST_AND_PUBLISH" ] ; then
|
||||
# CircleCI doesn't let us install the qemu module to do ARM docker builds
|
||||
make publish ALL_ARCHITECTURES=amd64 UPDATE_LATEST=latest-only
|
||||
fi
|
||||
|
||||
cd $SRCDIR
|
||||
bin/install-wordepress v1.0.0
|
||||
bin/publish-site "$WP_DEV_URL" "$WP_DEV_USER" "$WP_DEV_PASSWORD"
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd $SRCDIR
|
||||
bin/install-wordepress v1.0.0
|
||||
bin/publish-site "$WP_LIVE_URL" "$WP_LIVE_USER" "$WP_LIVE_PASSWORD"
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ $# -ne 1 ] ; then
|
||||
echo USAGE: install-wordepress [COMMIT-ISH] >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
go get github.com/weaveworks/wordepress
|
||||
cd /home/ubuntu/src/github.com/weaveworks/wordepress
|
||||
git checkout $1
|
||||
cd cmd/wordepress
|
||||
go get
|
||||
+2
-2
@@ -41,7 +41,7 @@ case "$1" in
|
||||
for i in $(seq $START $FINISH); do
|
||||
IP=$SUBNET.$i
|
||||
sudo ip addr add $IP/24 dev multiweave
|
||||
weave $i launch-router --datapath '' --iface '' --no-dns --host $IP \
|
||||
weave $i launch --datapath '' --iface '' --no-dns --host $IP \
|
||||
--name $(random_mac) --nickname weave$i "$@"
|
||||
done
|
||||
echo $(date -R) connecting
|
||||
@@ -57,7 +57,7 @@ case "$1" in
|
||||
for i in $(seq $FINISH -1 $START); do
|
||||
IP=$SUBNET.$i
|
||||
echo Stopping weave$i
|
||||
weave $i stop-router
|
||||
weave $i stop
|
||||
sudo ip addr del $IP/24 dev multiweave || true
|
||||
done
|
||||
[ -z "$(ip -4 -o addr show multiweave)" ] && sudo ip link del multiweave
|
||||
|
||||
@@ -17,7 +17,9 @@ function install_terraform() {
|
||||
|
||||
function install_ansible() {
|
||||
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"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
PRODUCT=net
|
||||
|
||||
fatal() {
|
||||
echo "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ ! -d .git ] ; then
|
||||
fatal "Current directory is not a git clone"
|
||||
fi
|
||||
|
||||
if ! BRANCH=$(git symbolic-ref --short HEAD) || [ -z "$BRANCH" ] ; then
|
||||
fatal "Could not determine branch"
|
||||
fi
|
||||
|
||||
case "$BRANCH" in
|
||||
issues/*)
|
||||
VERSION="${BRANCH#issues/}"
|
||||
TAGS="$VERSION"
|
||||
;;
|
||||
*)
|
||||
if echo "$BRANCH" | grep -qE '^[0-9]+\.[0-9]+' ; then
|
||||
DESCRIBE=$(git describe --match 'v*')
|
||||
if ! VERSION=$(echo "$DESCRIBE" | grep -oP '(?<=^v)[0-9]+\.[0-9]+\.[0-9]+') ; then
|
||||
fatal "Could not infer latest $BRANCH version from $DESCRIBE"
|
||||
fi
|
||||
TAGS="latest"
|
||||
else
|
||||
VERSION="$BRANCH"
|
||||
TAGS="$VERSION"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
for TAG in $TAGS ; do
|
||||
echo ">>> Publishing $PRODUCT $VERSION to $1/docs/$PRODUCT/$TAG"
|
||||
wordepress \
|
||||
--url "$1" --user "$2" --password "$3" \
|
||||
--product "$PRODUCT" --version "$VERSION" --tag "$TAG" \
|
||||
publish site
|
||||
done
|
||||
+40
-2
@@ -88,8 +88,10 @@ build() {
|
||||
|
||||
## Inject the version numbers and build the distributables
|
||||
## (library versions?)
|
||||
sed -i "/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 "/SCRIPT_VERSION=/ c\SCRIPT_VERSION=\"$VERSION\"" ./weave
|
||||
sed -i.bak -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-k8s-1.6.yaml
|
||||
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.7.yaml
|
||||
make SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER
|
||||
|
||||
if make tests; then
|
||||
@@ -165,6 +167,20 @@ draft() {
|
||||
--name "weave-daemonset.yaml" \
|
||||
--file "./prog/weave-kube/weave-daemonset.yaml"
|
||||
|
||||
github-release upload \
|
||||
--user $GITHUB_USER \
|
||||
--repo weave \
|
||||
--tag $LATEST_TAG \
|
||||
--name "weave-daemonset-k8s-1.6.yaml" \
|
||||
--file "./prog/weave-kube/weave-daemonset-k8s-1.6.yaml"
|
||||
|
||||
github-release upload \
|
||||
--user $GITHUB_USER \
|
||||
--repo weave \
|
||||
--tag $LATEST_TAG \
|
||||
--name "weave-daemonset-k8s-1.7.yaml" \
|
||||
--file "./prog/weave-kube/weave-daemonset-k8s-1.7.yaml"
|
||||
|
||||
echo "** Draft $TYPE $RELEASE_NAME $VERSION created at"
|
||||
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
||||
}
|
||||
@@ -179,6 +195,10 @@ publish() {
|
||||
fi
|
||||
|
||||
if [ "$RELEASE_TYPE" = 'PRERELEASE' ] ; then
|
||||
echo "== Building and pushing plugin"
|
||||
make UPDATE_LATEST=$IS_MAINLINE SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
|
||||
echo "** Plugin built and pushed"
|
||||
|
||||
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
|
||||
make UPDATE_LATEST=$IS_MAINLINE SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER publish
|
||||
echo "** Docker images tagged and pushed"
|
||||
@@ -211,6 +231,10 @@ publish() {
|
||||
fi
|
||||
echo '** Sanity checks OK for publishing tag' $LATEST_TAG as $DOCKERHUB_USER/weave:$VERSION
|
||||
|
||||
echo "== Building and pushing plugin"
|
||||
make UPDATE_LATEST=$IS_MAINLINE SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
|
||||
echo "** Plugin built and pushed"
|
||||
|
||||
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
|
||||
make PUBLISH_WEAVEDB=$IS_MAINLINE UPDATE_LATEST=$IS_MAINLINE SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER publish
|
||||
echo "** Docker images tagged and pushed"
|
||||
@@ -251,6 +275,20 @@ publish() {
|
||||
--name "weave-daemonset.yaml" \
|
||||
--file "./prog/weave-kube/weave-daemonset.yaml"
|
||||
|
||||
github-release upload \
|
||||
--user $GITHUB_USER \
|
||||
--repo weave \
|
||||
--tag latest_release \
|
||||
--name "weave-daemonset-k8s-1.6.yaml" \
|
||||
--file "./prog/weave-kube/weave-daemonset-k8s-1.6.yaml"
|
||||
|
||||
github-release upload \
|
||||
--user $GITHUB_USER \
|
||||
--repo weave \
|
||||
--tag latest_release \
|
||||
--name "weave-daemonset-k8s-1.7.yaml" \
|
||||
--file "./prog/weave-kube/weave-daemonset-k8s-1.7.yaml"
|
||||
|
||||
echo "** Release $RELEASE_NAME $VERSION published at"
|
||||
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
||||
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/latest_release"
|
||||
|
||||
+17
-11
@@ -1,32 +1,34 @@
|
||||
FROM golang:1.7.4
|
||||
FROM golang:1.8.1
|
||||
|
||||
# Support Raspberry Pi 2 and newer
|
||||
ENV GOARM 7
|
||||
|
||||
# The names of the architectures that we're fetching cross-compilers for from the Ubuntu packages
|
||||
# The names of the architectures that we're fetching cross-compilers for from the Debian packages
|
||||
ENV DEB_CROSSPLATFORMS \
|
||||
armhf \
|
||||
arm64
|
||||
arm64 \
|
||||
ppc64el
|
||||
|
||||
# The go platforms we are supporting to cross-compile
|
||||
ENV GO_CROSSPLATFORMS \
|
||||
linux/arm \
|
||||
linux/arm64
|
||||
linux/arm64 \
|
||||
linux/ppc64le
|
||||
|
||||
# The names of the gcc cross-compilers we have installed
|
||||
ENV GCC_CROSSCOMPILERS \
|
||||
arm-linux-gnueabihf \
|
||||
aarch64-linux-gnu
|
||||
aarch64-linux-gnu \
|
||||
powerpc64le-linux-gnu
|
||||
|
||||
# Temporarily add the Ubuntu 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
|
||||
RUN echo "deb http://archive.ubuntu.com/ubuntu xenial main universe" > /etc/apt/sources.list.d/cgocrosscompiling.list \
|
||||
&& apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5 3B4FE6ACC0B21F32 \
|
||||
RUN echo "deb http://emdebian.org/tools/debian/ jessie main" > /etc/apt/sources.list.d/cgocrosscompiling.list \
|
||||
&& curl http://emdebian.org/tools/debian/emdebian-toolchain-archive.key | apt-key add - \
|
||||
&& for platform in ${DEB_CROSSPLATFORMS}; do dpkg --add-architecture $platform; done \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y build-essential \
|
||||
&& for platform in ${DEB_CROSSPLATFORMS}; do apt-get install -y crossbuild-essential-${platform}; done \
|
||||
&& rm /etc/apt/sources.list.d/cgocrosscompiling.list \
|
||||
&& apt-get update \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install some required packages
|
||||
@@ -41,11 +43,15 @@ RUN apt-get update \
|
||||
flex \
|
||||
bison
|
||||
|
||||
RUN curl -fsSLo shfmt https://github.com/mvdan/sh/releases/download/v1.3.0/shfmt_v1.3.0_linux_amd64 && \
|
||||
echo "b1925c2c405458811f0c227266402cf1868b4de529f114722c2e3a5af4ac7bb2 shfmt" | sha256sum -c && \
|
||||
chmod +x shfmt && \
|
||||
mv shfmt /usr/bin
|
||||
|
||||
# Install common Go tools
|
||||
RUN go get \
|
||||
github.com/golang/lint/golint \
|
||||
github.com/fzipp/gocyclo \
|
||||
gopkg.in/mvdan/sh.v1/cmd/shfmt \
|
||||
github.com/fatih/hclfmt \
|
||||
github.com/client9/misspell/cmd/misspell
|
||||
|
||||
|
||||
@@ -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
-11
@@ -11,7 +11,7 @@ machine:
|
||||
environment:
|
||||
GOPATH: /home/ubuntu:$GOPATH
|
||||
SRCDIR: /home/ubuntu/src/github.com/weaveworks/weave
|
||||
PATH: $PATH:$HOME/.local/bin
|
||||
PATH: $PATH:$HOME/.local/bin:/opt/google-cloud-sdk/bin
|
||||
CLOUDSDK_CORE_DISABLE_PROMPTS: 1
|
||||
STATE: /home/ubuntu/state.env
|
||||
# Base name of VMs for integration tests:
|
||||
@@ -59,18 +59,8 @@ teardown:
|
||||
parallel: true
|
||||
|
||||
deployment:
|
||||
release:
|
||||
branch: /[0-9]+\.[0-9]+/
|
||||
owner: weaveworks
|
||||
commands:
|
||||
- "$SRCDIR/bin/circle-deploy-release"
|
||||
master:
|
||||
branch: master
|
||||
owner: weaveworks
|
||||
commands:
|
||||
- "$SRCDIR/bin/circle-deploy-master"
|
||||
issues:
|
||||
branch: /.*/
|
||||
owner: weaveworks
|
||||
commands:
|
||||
- "$SRCDIR/bin/circle-deploy-issues"
|
||||
@@ -0,0 +1,7 @@
|
||||
## Community Code of Conduct
|
||||
|
||||
Weaveworks follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior
|
||||
may be reported by contacting a Weaveworks project maintainer, or
|
||||
Alexis Richardson alexis@weave.works.
|
||||
+38
-2
@@ -165,13 +165,25 @@ func (pending pendingStarts) finish(id string) {
|
||||
}
|
||||
}
|
||||
|
||||
// AllContainerIDs returns all the IDs of Docker containers,
|
||||
// whether they are running or not.
|
||||
func (c *Client) AllContainerIDs() ([]string, error) {
|
||||
all, err := c.ListContainers(docker.ListContainersOptions{All: true})
|
||||
return c.containerIDs(true)
|
||||
}
|
||||
|
||||
// RunningContainerIDs returns all the IDs of the running
|
||||
// Docker containers.
|
||||
func (c *Client) RunningContainerIDs() ([]string, error) {
|
||||
return c.containerIDs(false)
|
||||
}
|
||||
|
||||
func (c *Client) containerIDs(all bool) ([]string, error) {
|
||||
containers, err := c.ListContainers(docker.ListContainersOptions{All: all})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ids []string
|
||||
for _, c := range all {
|
||||
for _, c := range containers {
|
||||
ids = append(ids, c.ID)
|
||||
}
|
||||
return ids, nil
|
||||
@@ -213,6 +225,30 @@ func (c *Client) GetContainerIP(nameOrID string) (string, error) {
|
||||
return info.NetworkSettings.IPAddress, nil
|
||||
}
|
||||
|
||||
func (c *Client) EnsureNetwork(networkName, driver, subnet string, options map[string]interface{}) error {
|
||||
_, err := c.CreateNetwork(
|
||||
docker.CreateNetworkOptions{
|
||||
Name: networkName,
|
||||
CheckDuplicate: true,
|
||||
Driver: driver,
|
||||
IPAM: docker.IPAMOptions{
|
||||
Driver: driver,
|
||||
Config: []docker.IPAMConfig{{Subnet: subnet}},
|
||||
},
|
||||
Options: options,
|
||||
})
|
||||
if err != docker.ErrNetworkAlreadyExists && err != nil {
|
||||
// Despite appearances to the contrary, CreateNetwork does
|
||||
// sometimes(always?) *not* return ErrNetworkAlreadyExists
|
||||
// when the network already exists. Hence we need to check for
|
||||
// this explicitly.
|
||||
if _, err2 := c.NetworkInfo(networkName); err2 != nil {
|
||||
return fmt.Errorf("unable to create network: %s", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// logging
|
||||
|
||||
func (c *Client) errorf(fmt string, args ...interface{}) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type WaitGroup struct {
|
||||
wg sync.WaitGroup
|
||||
count int32
|
||||
}
|
||||
|
||||
// Increase the count of things waiting and return a closure which calls Done
|
||||
func (w *WaitGroup) Add() func() {
|
||||
atomic.AddInt32(&w.count, 1)
|
||||
w.wg.Add(1)
|
||||
return func() {
|
||||
w.wg.Done()
|
||||
atomic.AddInt32(&w.count, -1)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WaitGroup) IsDone() bool {
|
||||
return atomic.LoadInt32(&w.count) == 0
|
||||
}
|
||||
+9
-3
@@ -26,10 +26,15 @@ var (
|
||||
|
||||
const (
|
||||
NameIdent = "peername"
|
||||
FileName = "data.db"
|
||||
fileName = "data.db"
|
||||
)
|
||||
|
||||
func NewBoltDB(dbPathname string) (*BoltDB, error) {
|
||||
func Pathname(dbPrefix string) string {
|
||||
return dbPrefix + fileName
|
||||
}
|
||||
|
||||
func NewBoltDB(dbPrefix string) (*BoltDB, error) {
|
||||
dbPathname := Pathname(dbPrefix)
|
||||
db, err := bolt.Open(dbPathname, 0660, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[boltDB] Unable to open %s: %s", dbPathname, err)
|
||||
@@ -38,8 +43,9 @@ func NewBoltDB(dbPathname string) (*BoltDB, error) {
|
||||
return &BoltDB{db: db}, err
|
||||
}
|
||||
|
||||
func NewBoltDBReadOnly(dbPathname string) (*BoltDB, error) {
|
||||
func NewBoltDBReadOnly(dbPrefix string) (*BoltDB, error) {
|
||||
options := bolt.Options{Timeout: time.Millisecond * 50, ReadOnly: true}
|
||||
dbPathname := Pathname(dbPrefix)
|
||||
db, err := bolt.Open(dbPathname, 0660, &options)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[boltDB] Unable to open %s: %s", dbPathname, err)
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# Weave Net Benchmarks
|
||||
|
||||
## 2017-02-16: Fast Datapath Encryption
|
||||
|
||||
Results: https://www.weave.works/weave-net-performance-fast-datapath/
|
||||
|
||||
* Two `c3.8xlarge` instances running Ubuntu 16.04 LTS (`ami-d8f4deab`) in
|
||||
`eu-west-1` region of AWS.
|
||||
* 10 Gigabit [Enhanced Networking](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking.html).
|
||||
* `iperf 3.0.11`.
|
||||
|
||||
### fastdp
|
||||
|
||||
* Encrypted:
|
||||
|
||||
```
|
||||
host1$ WEAVE_MTU=8916 weave launch --password=foobar
|
||||
host1$ docker $(weave config) run --rm -ti --name server networkstatic/iperf3 -s
|
||||
|
||||
host2$ WEAVE_MTU=8916 weave launch --password=foobar host1
|
||||
host2$ docker $(weave config) run --rm -ti networkstatic/iperf3 -c server
|
||||
```
|
||||
|
||||
* Non-encrypted:
|
||||
|
||||
```
|
||||
host1$ WEAVE_MTU=8950 weave launch
|
||||
host1$ docker $(weave config) run --rm -ti --name server networkstatic/iperf3 -s
|
||||
|
||||
host2$ WEAVE_MTU=8950 weave launch --password=foobar host1
|
||||
host2$ docker $(weave config) run --rm -ti networkstatic/iperf3 -c server
|
||||
```
|
||||
|
||||
### sleeve
|
||||
|
||||
* Encrypted:
|
||||
|
||||
```
|
||||
host1$ WEAVE_NO_FASTDP=1 weave launch --password=foobar
|
||||
host1$ docker $(weave config) run --rm -ti --name server networkstatic/iperf3 -s
|
||||
|
||||
host2$ WEAVE_NO_FASTDP=1 weave launch --password=foobar host1
|
||||
host2$ docker $(weave config) run --rm -ti networkstatic/iperf3 -c server
|
||||
```
|
||||
|
||||
* Non-encrypted:
|
||||
|
||||
```
|
||||
host1$ WEAVE_NO_FASTDP=1 weave launch
|
||||
host1$ docker $(weave config) run --rm -ti --name server networkstatic/iperf3 -s
|
||||
|
||||
host2$ WEAVE_NO_FASTDP=1 weave launch --password=foobar host1
|
||||
host2$ docker $(weave config) run --rm -ti networkstatic/iperf3 -c server
|
||||
```
|
||||
|
||||
### host
|
||||
|
||||
* Non-encrypted:
|
||||
|
||||
```
|
||||
host1$ iperf -s
|
||||
host2$ iperf -c host1
|
||||
```
|
||||
|
||||
* Encrypted:
|
||||
|
||||
```
|
||||
host1$ export KEY1="0x466454f2b1a770f8f872f9afbc35ebeac57e00fc11ac86ed1f82716f010b20f0cf532274"
|
||||
host1$ export KEY2="0x3531151241a770f8f872f9afbc35ebeac57e00fc11ac86ed1f82716f010b20f0cf532274"
|
||||
host1# ip xfrm state add src ${IP1} dst ${IP2} proto esp spi 0x4c856ffc replay-window 256 flag esn reqid 0 mode transport aead 'rfc4106(gcm(aes))' ${KEY1} 128
|
||||
host1# ip xfrm state add src ${IP2} dst ${IP1} proto esp spi 0x9b0830bc replay-window 256 flag esn reqid 0 mode transport aead 'rfc4106(gcm(aes))' ${KEY2} 128
|
||||
host1# ip xfrm policy add src ${IP1}/32 dst ${IP2}/32 dir out tmpl src ${IP1} dst ${IP2} proto esp spi 0x4c856ffc reqid 0 mode transport
|
||||
host1$ iperf -s
|
||||
|
||||
host2$ export KEY1="0x466454f2b1a770f8f872f9afbc35ebeac57e00fc11ac86ed1f82716f010b20f0cf532274"
|
||||
host2$ export KEY2="0x3531151241a770f8f872f9afbc35ebeac57e00fc11ac86ed1f82716f010b20f0cf532274"
|
||||
host2# ip xfrm state add src ${IP2} dst ${IP1} proto esp spi 0x9b0830bc reqid 0 replay-window 256 flag esn mode transport aead 'rfc4106(gcm(aes))' ${KEY2} 128
|
||||
host2# ip xfrm state add src ${IP1} dst ${IP2} proto esp spi 0x4c856ffc reqid 0 replay-window 256 flag esn mode transport aead 'rfc4106(gcm(aes))' ${KEY1} 128
|
||||
host2# ip xfrm policy add src ${IP2}/32 dst ${IP1}/32 dir out tmpl src ${IP1} dst ${IP2} proto esp spi 0x9b0830bc reqid 0 mode transport
|
||||
host2$ iperf -c host1
|
||||
```
|
||||
@@ -0,0 +1,70 @@
|
||||
# Entrypoints
|
||||
|
||||
This document describes the various ways that weave as a binary gets launched, and where and how it processes options.
|
||||
|
||||
The goal is to enable maintainers to modify functionality without having to reverse engineer the code to find all of the places you can start a weave binary and what it depends upon.
|
||||
|
||||
## Components
|
||||
### weaver
|
||||
|
||||
`weaver` is the primary binary: it sets up the weave bridge to which containers are attached, manages IP address allocation, serves DNS requests and does many more things.
|
||||
|
||||
Since version 2.0, `weaver` also bundles the Docker Plugin (legacy version) and Docker API Proxy which used to be shipped as separate containers.
|
||||
|
||||
### weaveutil
|
||||
weaveutil is a binary which provides a number of functions for managing an existing weave network. It gets information about the network, attaches and detaches containers, etc.
|
||||
|
||||
For the majority of interactions with an existing weave network, you will launch `weaveutil` in some manner or another.
|
||||
|
||||
Almost all options can be passed as `--option` to `weaveutil`. These options are created in `prog/weaveutil/main.go` with a table mapping each command to a dedicated golang function.
|
||||
|
||||
In addition to operating in normal mode, `weaveutil` has several additional operating modes. Before processing any commands, `weaveutil` checks the filename with which it was called.
|
||||
|
||||
* If it was `weave-ipam` it delegates responsibility to the `cniIPAM()` function in `prog/weaveutil/cni.go`, which, in turn, calls the standard CNI plugin function `cni.PluginMain()`, passing it weave's IPAM implementation from `plugin/ipam`.
|
||||
* If it was `weave-net` it delegates responsibility to the `cniNet()` function in `prog/weaveutil/cni.go`, which, in turn, calls the standard CNI plugin function `cni.PluginMain()`, passing it weave's net plugin implementation from `plugin/net`.
|
||||
|
||||
Finally, `weaveutil` is called by `weaver` when it needs to operate in a different network namespace.
|
||||
Go programs cannot safely switch namespaces; see this [issue](http://github.com/vishvananda/netns/issues/17).
|
||||
The call chain is `weaver`->`nsenter`->`weaveutil`.
|
||||
|
||||
### weave
|
||||
Wrapping `weaver` and `weaveutil` is `weave`, a `sh` script that provides help information and calls `weaveutil` as relevant.
|
||||
|
||||
### weave-kube
|
||||
weave-kube is an image with the weave binaries and a wrapper script installed. It is responsible for:
|
||||
|
||||
1. Setting up the weave network
|
||||
2. Connecting to peers
|
||||
3. Copy the weave CNI-compatible binary plugin `weaveutil` to `/opt/cni/bin/weave-net` and weave config in `/etc/cni/net.d/10-weave.conf`
|
||||
4. Running the weave network policy controller (weave-npc) to implement kubernetes' `NetworkPolicy`
|
||||
|
||||
Once installation is complete, each network-relevant change in a container leads `kubelet` to:
|
||||
|
||||
1. Set certain environment variables to configure the CNI plugin, mostly related to the container ID
|
||||
2. Launch `/opt/cni/bin/weave-net`
|
||||
3. Pass the CNI config - the contents of `/etc/cni/net.d/10-weave.conf` to `weave-net`
|
||||
|
||||
Thus, when each container is changed, and `kubelet` calls weave as a CNI plugin, it really just is launching `weaveutil` as `weave-net`.
|
||||
|
||||
The installation and setup of all of the above - and therefore the entrypoint to the weave-kube image - is the script `prog/weave-kube/launch.sh`. `launch.sh` does the following:
|
||||
|
||||
1. Read configuration variables from the environment. When the documentation for `weave-kube` describes configuring the weave network by changing the environment variables in the daemonset in the `.yml` file, `launch.sh` reads these environment variables.
|
||||
2. Set up the config file at `/etc/cni/net.d/10-weave.conf`
|
||||
3. Run the `weave` initialization script
|
||||
4. Run `weaver` with the correct configuration passed as command-line options
|
||||
|
||||
|
||||
## Adding Options
|
||||
To add new options to how weave should run with each invocation, you would do the following:
|
||||
|
||||
1. determine to which `weave` command you want to add the option(s). `weave` normally is launched as `weave <command> <options> ...`.
|
||||
2. Add the option to `weave` script help for each `weave` command you wish to make it available. As of this writing, all of the commands and their options are listed in the function `usage_no_exist()`.
|
||||
3. Add the option to `weave` script option processing for the `weave` command, under the `case $COMMAND in` in the main `weave` script.
|
||||
4. In the function for the `weave` command, determine if the command should be passed on to `weaveutil` or `weaver`. Pass the option on to `weaveutil` or `weaver`, as appropriate, in the format `--option`, in the function for the `weave` command.
|
||||
5. Add a command-line option `--option` to `weaveutil` or `weaver` as appropriate.
|
||||
6. If the option can be configured for CNI:
|
||||
* have `prog/weave-kube/launch.sh` read it as an environment variable and set inside
|
||||
* set a default for the environment variable in `prog/weave-kube/weave-daemonset-k8s-1.6.yaml` and `weave-daemonset.yaml`
|
||||
* if it should be set via `weaver` globally on its one-time initialization invocation, pass it on in `launch.sh`
|
||||
* if it needs to be set via `weaveutil` on each invocation of `weave-net`, have `launch.sh` save it as an option in the CNI config file `/etc/cni/net.d/10-weave.conf` and then have the CNI code in `plugin/net/cni.go` in `weaveutil` read it and use where appropriate
|
||||
7. Document it!
|
||||
@@ -4,8 +4,7 @@
|
||||
* Up-to-date versions of `git`, `go` etc
|
||||
* Install GitHub release tool `go get github.com/weaveworks/github-release`
|
||||
* Create a [github token for
|
||||
github-release](https://help.github.com/articles/creating-an-access-token-for-command-line-use/);
|
||||
set and export `$GITHUB_TOKEN` with this value
|
||||
github-release](https://help.github.com/articles/creating-an-access-token-for-command-line-use/); select the `repo` OAuth scope; set and export `$GITHUB_TOKEN` with this value
|
||||
|
||||
## Release Types
|
||||
|
||||
@@ -138,6 +137,8 @@ Finally, for **Mainline** releases only:
|
||||
published to https://www.weave.works
|
||||
* Add the new version of `weave-net` to the checkpoint system at
|
||||
https://checkpoint-api.weave.works/admin
|
||||
* File a PR to update the version of the daemonset at
|
||||
https://github.com/kubernetes/kops/tree/master/upup/models/cloudup/resources/addons/networking.weave
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -77,8 +77,10 @@ iptables -A WEAVE-NPC -m state --state NEW -j WEAVE-NPC-INGRESS
|
||||
|
||||
To direct traffic into the policy engine:
|
||||
|
||||
```
|
||||
iptables -A FORWARD -o weave -m physdev ! --physdev-out vethwe-bridge -j WEAVE-NPC
|
||||
iptables -A FORWARD -o weave -m physdev ! --physdev-out vethwe-bridge -j DROP
|
||||
```
|
||||
|
||||
Note this only affects traffic which egresses the bridge on a physical
|
||||
port which is not the Weave Net router - in other words, it is
|
||||
|
||||
+12
-4
@@ -210,9 +210,9 @@ func (alloc *Allocator) doOperation(op operation, ops *[]operation) {
|
||||
// already succeeded. If it is on the queue, we call
|
||||
// cancel on it, allowing callers waiting for the resultChans
|
||||
// to unblock.
|
||||
func (alloc *Allocator) cancelOp(op operation, ops *[]operation) {
|
||||
func (alloc *Allocator) cancelOp(opToCancel operation, ops *[]operation) {
|
||||
for i, op := range *ops {
|
||||
if op == op {
|
||||
if op == opToCancel {
|
||||
*ops = append((*ops)[:i], (*ops)[i+1:]...)
|
||||
op.Cancel()
|
||||
break
|
||||
@@ -265,6 +265,10 @@ func (alloc *Allocator) tryPendingOps() {
|
||||
alloc.tryOps(&alloc.pendingAllocates)
|
||||
}
|
||||
|
||||
func (alloc *Allocator) havePendingOps() bool {
|
||||
return len(alloc.pendingPrimes)+len(alloc.pendingClaims)+len(alloc.pendingAllocates) > 0
|
||||
}
|
||||
|
||||
func (alloc *Allocator) spaceRequestDenied(sender mesh.PeerName, r address.Range) {
|
||||
for i := 0; i < len(alloc.pendingClaims); {
|
||||
claim := alloc.pendingClaims[i].(*claim)
|
||||
@@ -696,8 +700,12 @@ func (alloc *Allocator) actorLoop(actionChan <-chan func(), stopChan <-chan stru
|
||||
// Retry things in case messages got lost between here and recipients
|
||||
if alloc.awaitingConsensus {
|
||||
alloc.propose()
|
||||
} else {
|
||||
alloc.tryPendingOps()
|
||||
} else if alloc.havePendingOps() {
|
||||
if alloc.ring.Empty() {
|
||||
alloc.establishRing()
|
||||
} else {
|
||||
alloc.tryPendingOps()
|
||||
}
|
||||
}
|
||||
alloc.removeDeadContainers()
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/weaveworks/weave/api"
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/testing/gossip"
|
||||
@@ -30,6 +31,10 @@ func (alloc *Allocator) SimplyClaim(ident string, cidr address.CIDR) error {
|
||||
return alloc.Claim(ident, cidr, true, true, returnFalse)
|
||||
}
|
||||
|
||||
func (alloc *Allocator) AnonymousClaim(cidr address.CIDR) error {
|
||||
return alloc.Claim(api.NoContainerID, cidr, false, true, returnFalse)
|
||||
}
|
||||
|
||||
func TestAllocFree(t *testing.T) {
|
||||
const (
|
||||
container1 = "abcdef"
|
||||
@@ -170,6 +175,8 @@ func TestAllocatorClaim(t *testing.T) {
|
||||
universe = "10.0.3.0/24"
|
||||
testAddr1 = "10.0.3.5/24"
|
||||
testAddr2 = "10.0.4.2/24"
|
||||
testAddr4 = "10.0.2.3/23"
|
||||
subnet4 = "10.0.2.0/23"
|
||||
testPre = "10.0.3.1/24"
|
||||
)
|
||||
|
||||
@@ -186,6 +193,7 @@ func TestAllocatorClaim(t *testing.T) {
|
||||
alloc.Prime()
|
||||
// Do an allocate on the other peer, which we will try to claim later
|
||||
addrx, err := allocs[0].Allocate(container1, subnet, true, returnFalse)
|
||||
require.NoError(t, err)
|
||||
// Should not get the address we pre-claimed
|
||||
require.NotEqual(t, addrx, preAddr)
|
||||
router.Flush()
|
||||
@@ -202,6 +210,9 @@ func TestAllocatorClaim(t *testing.T) {
|
||||
// claim for a different container should fail
|
||||
err = alloc.SimplyClaim(container1, addr1)
|
||||
require.Error(t, err)
|
||||
// claim for an anonymous container, will assume it's the same one
|
||||
err = alloc.AnonymousClaim(addr1)
|
||||
require.NoError(t, err)
|
||||
// claiming the address allocated on the other peer should fail
|
||||
err = alloc.SimplyClaim(container1, address.MakeCIDR(subnet, addrx))
|
||||
require.Error(t, err, "claiming address allocated on other peer should fail")
|
||||
@@ -214,6 +225,15 @@ func TestAllocatorClaim(t *testing.T) {
|
||||
addr2, _ := address.ParseCIDR(testAddr2)
|
||||
err = alloc.SimplyClaim(container1, addr2)
|
||||
require.NoError(t, err)
|
||||
err = alloc.SimplyClaim(container3, addr2)
|
||||
require.Error(t, err)
|
||||
// Anonymous claim outside of our universe
|
||||
addr4, _ := address.ParseCIDR(testAddr4)
|
||||
subnet4cidr, _ := address.ParseCIDR(subnet4)
|
||||
require.NoError(t, alloc.AnonymousClaim(addr4))
|
||||
addr5, err := alloc.Allocate(api.NoContainerID, subnet4cidr, false, returnFalse)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, addr4.Start(), addr5)
|
||||
}
|
||||
|
||||
func (alloc *Allocator) pause() func() {
|
||||
|
||||
+22
-7
@@ -40,10 +40,29 @@ func (c *claim) Try(alloc *Allocator) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
addOwned := func() {
|
||||
if c.ident == api.NoContainerID {
|
||||
alloc.addOwned(c.cidr.Addr.String(), c.cidr, c.isContainer)
|
||||
} else {
|
||||
alloc.addOwned(c.ident, c.cidr, c.isContainer)
|
||||
}
|
||||
}
|
||||
|
||||
if !alloc.ring.Contains(c.cidr.Addr) {
|
||||
// Address not within our universe; assume user knows what they are doing
|
||||
alloc.infof("Address %s claimed by %s - not in our range", c.cidr, c.ident)
|
||||
alloc.addOwned(c.ident, c.cidr, c.isContainer)
|
||||
previousOwner := alloc.findOwner(c.cidr.Addr)
|
||||
switch {
|
||||
case previousOwner == "":
|
||||
addOwned()
|
||||
case previousOwner == c.ident: // already owned by this ID
|
||||
case c.ident == api.NoContainerID: // already owned by anonymous container
|
||||
// do nothing (no automatic fall-through in Go)
|
||||
case !alloc.dead[previousOwner].IsZero(): // already owned by dead container
|
||||
alloc.removeOwned(previousOwner, c.cidr.Addr)
|
||||
addOwned()
|
||||
default:
|
||||
c.sendResult(fmt.Errorf("address %s already in use by %s", c.cidr, previousOwner))
|
||||
}
|
||||
c.sendResult(nil)
|
||||
return true
|
||||
}
|
||||
@@ -85,11 +104,7 @@ func (c *claim) Try(alloc *Allocator) bool {
|
||||
// Unused address, we try to claim it:
|
||||
if err := alloc.space.Claim(c.cidr.Addr); err == nil {
|
||||
alloc.debugln("Claimed", c.cidr, "for", c.ident)
|
||||
if c.ident == api.NoContainerID {
|
||||
alloc.addOwned(c.cidr.Addr.String(), c.cidr, c.isContainer)
|
||||
} else {
|
||||
alloc.addOwned(c.ident, c.cidr, c.isContainer)
|
||||
}
|
||||
addOwned()
|
||||
c.sendResult(nil)
|
||||
} else {
|
||||
c.sendResult(err)
|
||||
|
||||
+34
-11
@@ -1,6 +1,7 @@
|
||||
package ipam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@@ -127,6 +128,39 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CID
|
||||
writeAddresses(w, addrs)
|
||||
})
|
||||
|
||||
router.Methods("GET").Path("/ip").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
type mapping struct {
|
||||
ContainerID string `json:"containerid"`
|
||||
Addrs []string `json:"addrs"`
|
||||
}
|
||||
|
||||
type mappings struct {
|
||||
Owned []mapping `json:"owned"`
|
||||
}
|
||||
|
||||
resultChan := make(chan mappings)
|
||||
alloc.actionChan <- func() {
|
||||
ms := mappings{}
|
||||
for containerid, d := range alloc.owned {
|
||||
m := mapping{
|
||||
ContainerID: containerid,
|
||||
Addrs: []string{},
|
||||
}
|
||||
for _, addr := range d.Cidrs {
|
||||
m.Addrs = append(m.Addrs, addr.String())
|
||||
}
|
||||
ms.Owned = append(ms.Owned, m)
|
||||
}
|
||||
resultChan <- ms
|
||||
}
|
||||
ms := <-resultChan
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(ms); err != nil {
|
||||
common.Log.Warningln("[allocator]:", err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
router.Methods("POST").Path("/ip/{id}/{ip}/{prefixlen}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
if subnet, ok := parseCIDR(w, vars["ip"]+"/"+vars["prefixlen"], true); ok {
|
||||
@@ -164,17 +198,6 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CID
|
||||
w.WriteHeader(204)
|
||||
})
|
||||
|
||||
router.Methods("DELETE").Path("/peer").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
alloc.Shutdown()
|
||||
w.WriteHeader(204)
|
||||
})
|
||||
|
||||
router.Methods("DELETE").Path("/peer/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ident := mux.Vars(r)["id"]
|
||||
transferred := alloc.AdminTakeoverRanges(ident)
|
||||
fmt.Fprintf(w, "%d IPs taken over from %s\n", transferred, ident)
|
||||
})
|
||||
|
||||
router.Methods("GET").Path("/ipinfo/tracker").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, tracker)
|
||||
})
|
||||
|
||||
@@ -52,12 +52,12 @@ func TestLowlevel(t *testing.T) {
|
||||
|
||||
s := New()
|
||||
require.Equal(t, address.Count(0), s.NumFreeAddresses())
|
||||
ok, got := s.Allocate(address.NewRange(0, 1000))
|
||||
ok, _ := s.Allocate(address.NewRange(0, 1000))
|
||||
require.False(t, ok, "allocate in empty space should fail")
|
||||
|
||||
s.Add(100, 100)
|
||||
require.Equal(t, address.Count(100), s.NumFreeAddresses())
|
||||
ok, got = s.Allocate(address.NewRange(0, 1000))
|
||||
ok, got := s.Allocate(address.NewRange(0, 1000))
|
||||
require.True(t, ok && got == 100, "allocate")
|
||||
require.Equal(t, address.Count(99), s.NumFreeAddresses())
|
||||
require.NoError(t, s.Claim(150))
|
||||
@@ -68,6 +68,7 @@ func TestLowlevel(t *testing.T) {
|
||||
wt.AssertErrorInterface(t, (*error)(nil), s.Free(100), "double free")
|
||||
|
||||
r, ok := s.Donate(address.NewRange(0, 1000))
|
||||
require.True(t, ok, "donate")
|
||||
require.Equal(t, address.NewRange(0xa0, 0x20), r, "donate")
|
||||
|
||||
// test Donate when addresses are scarce
|
||||
@@ -78,6 +79,7 @@ func TestLowlevel(t *testing.T) {
|
||||
require.NoError(t, s.Claim(0))
|
||||
require.NoError(t, s.Claim(2))
|
||||
r, ok = s.Donate(address.NewRange(0, 1000))
|
||||
require.True(t, ok, "donate")
|
||||
require.Equal(t, address.NewRange(1, 1), r, "donate")
|
||||
r, ok = s.Donate(address.NewRange(0, 1000))
|
||||
require.True(t, !ok, "donate should fail")
|
||||
|
||||
@@ -30,7 +30,6 @@ import (
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
wnet "github.com/weaveworks/weave/net"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
)
|
||||
|
||||
@@ -42,7 +41,7 @@ type AWSVPCTracker struct {
|
||||
}
|
||||
|
||||
// NewAWSVPCTracker creates and initialises AWS VPC based tracker.
|
||||
func NewAWSVPCTracker() (*AWSVPCTracker, error) {
|
||||
func NewAWSVPCTracker(bridgeName string) (*AWSVPCTracker, error) {
|
||||
var (
|
||||
err error
|
||||
session = session.New()
|
||||
@@ -69,9 +68,9 @@ func NewAWSVPCTracker() (*AWSVPCTracker, error) {
|
||||
t.routeTableID = *routeTableID
|
||||
|
||||
// Detect Weave bridge link index
|
||||
link, err := netlink.LinkByName(wnet.WeaveBridgeName)
|
||||
link, err := netlink.LinkByName(bridgeName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot find \"%s\" interface: %s", wnet.WeaveBridgeName, err)
|
||||
return nil, fmt.Errorf("cannot find \"%s\" interface: %s", bridgeName, err)
|
||||
}
|
||||
t.linkIndex = link.Attrs().Index
|
||||
|
||||
|
||||
+3
-8
@@ -27,7 +27,7 @@ func (n *Nameserver) HandleHTTP(router *mux.Router, dockerCli *docker.Client) {
|
||||
vars = mux.Vars(r)
|
||||
container = vars["container"]
|
||||
ipStr = vars["ip"]
|
||||
hostname = dns.Fqdn(r.FormValue("fqdn"))
|
||||
fqdn = r.FormValue("fqdn")
|
||||
ip, err = address.ParseIP(ipStr)
|
||||
)
|
||||
if err != nil {
|
||||
@@ -35,16 +35,11 @@ func (n *Nameserver) HandleHTTP(router *mux.Router, dockerCli *docker.Client) {
|
||||
return
|
||||
}
|
||||
|
||||
if !dns.IsSubDomain(n.domain, hostname) {
|
||||
n.infof("Ignoring registration %s %s %s (not a subdomain of %s)", hostname, ipStr, container, n.domain)
|
||||
return
|
||||
}
|
||||
|
||||
n.AddEntry(hostname, container, n.ourName, ip)
|
||||
n.AddEntryFQDN(fqdn, container, n.ourName, ip)
|
||||
|
||||
if r.FormValue("check-alive") == "true" && dockerCli != nil && dockerCli.IsContainerNotRunning(container) {
|
||||
n.infof("container '%s' is not running: removing", container)
|
||||
n.Delete(hostname, container, ipStr, ip)
|
||||
n.Delete(dns.Fqdn(fqdn), container, ipStr, ip)
|
||||
}
|
||||
|
||||
w.WriteHeader(204)
|
||||
|
||||
@@ -91,6 +91,15 @@ func (n *Nameserver) AddEntry(hostname, containerid string, origin mesh.PeerName
|
||||
n.broadcastEntries(entry)
|
||||
}
|
||||
|
||||
func (n *Nameserver) AddEntryFQDN(fqdn, containerid string, origin mesh.PeerName, addr address.Address) {
|
||||
hostname := dns.Fqdn(fqdn)
|
||||
if !dns.IsSubDomain(n.domain, hostname) {
|
||||
n.infof("Ignoring registration %s %s %s (not a subdomain of %s)", hostname, addr.String(), containerid, n.domain)
|
||||
return
|
||||
}
|
||||
n.AddEntry(hostname, containerid, origin, addr)
|
||||
}
|
||||
|
||||
func (n *Nameserver) Lookup(hostname string) []address.Address {
|
||||
n.RLock()
|
||||
defer n.RUnlock()
|
||||
|
||||
@@ -160,6 +160,11 @@ func (cidr CIDR) String() string {
|
||||
return fmt.Sprintf("%s/%d", cidr.Addr.String(), cidr.PrefixLen)
|
||||
}
|
||||
|
||||
func (cidr CIDR) IPNet() (r *net.IPNet) {
|
||||
mask := net.CIDRMask(cidr.PrefixLen, 8*net.IPv4len)
|
||||
return &net.IPNet{IP: cidr.Addr.IP4(), Mask: mask}
|
||||
}
|
||||
|
||||
// FromIP4 converts an ipv4 address to our integer address type
|
||||
func FromIP4(ip4 net.IP) (r Address) {
|
||||
for _, b := range ip4.To4() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package address
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
|
||||
@@ -67,3 +68,15 @@ func TestCIDRStartAndEnd(t *testing.T) {
|
||||
require.Equal(t, ip("10.0.0.0"), cidr.Start(), "")
|
||||
require.Equal(t, ip("10.0.1.0"), cidr.End(), "")
|
||||
}
|
||||
|
||||
func TestConversions(t *testing.T) {
|
||||
addrStr := "10.2.3.0"
|
||||
cidrStr := addrStr + "/24"
|
||||
addr := ip(addrStr)
|
||||
require.Equal(t, addr.String(), addrStr)
|
||||
require.Equal(t, addr.IP4(), net.ParseIP(addrStr).To4())
|
||||
cidr, _ := ParseCIDR(cidrStr)
|
||||
require.Equal(t, cidr.String(), cidrStr)
|
||||
_, ipnet, _ := net.ParseCIDR(cidrStr)
|
||||
require.Equal(t, cidr.IPNet(), ipnet)
|
||||
}
|
||||
+12
-12
@@ -1,27 +1,27 @@
|
||||
package net
|
||||
|
||||
import "fmt"
|
||||
import "io"
|
||||
import "os"
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Configure the ARP cache parameters for the given interface. This
|
||||
// makes containers react more quickly to a change in the MAC address
|
||||
// associated with an IP address.
|
||||
func ConfigureARPCache(name string) error {
|
||||
if err := sysctl(fmt.Sprintf("net/ipv4/neigh/%s/base_reachable_time", name), "5"); err != nil {
|
||||
func ConfigureARPCache(procPath, name string) error {
|
||||
if err := sysctl(procPath, fmt.Sprintf("net/ipv4/neigh/%s/base_reachable_time", name), "5"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sysctl(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
|
||||
}
|
||||
if err := sysctl(fmt.Sprintf("net/ipv4/neigh/%s/ucast_solicit", name), "1"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return sysctl(procPath, fmt.Sprintf("net/ipv4/neigh/%s/ucast_solicit", name), "1")
|
||||
}
|
||||
|
||||
func sysctl(variable, value string) error {
|
||||
f, err := os.OpenFile(fmt.Sprintf("/proc/sys/%s", variable), os.O_WRONLY, 0)
|
||||
func sysctl(procPath, variable, value string) error {
|
||||
f, err := os.OpenFile(filepath.Join(procPath, "/sys/", variable), os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+392
-13
@@ -2,43 +2,136 @@ package net
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
"github.com/weaveworks/weave/common/odp"
|
||||
)
|
||||
|
||||
type BridgeType int
|
||||
/* This code implements three possible configurations to connect
|
||||
containers to the Weave Net overlay:
|
||||
|
||||
1. Bridge
|
||||
+-------+
|
||||
(container-veth)-+ weave +-(vethwe-bridge)--(vethwe-pcap)
|
||||
+-------+
|
||||
|
||||
"weave" is a Linux bridge. "vethwe-pcap" (end of veth pair) is used
|
||||
to capture and inject packets, by router/pcap.go.
|
||||
|
||||
2. BridgedFastdp
|
||||
|
||||
+-------+ /----------\
|
||||
(container-veth)-+ weave +-(vethwe-bridge)--(vethwe-datapath)-+ datapath +
|
||||
+-------+ \----------/
|
||||
|
||||
"weave" is a Linux bridge and "datapath" is an Open vSwitch datapath;
|
||||
they are connected via a veth pair. Packet capture and injection use
|
||||
the "datapath" device, via "router/fastdp.go:fastDatapathBridge"
|
||||
|
||||
3. Fastdp
|
||||
|
||||
/-------\
|
||||
(container-veth)-+ weave +
|
||||
\-------/
|
||||
|
||||
"weave" is an Open vSwitch datapath, and capture/injection are as in
|
||||
BridgedFastdp. Not used by default due to missing conntrack support in
|
||||
datapath of old kernel versions (https://github.com/weaveworks/weave/issues/1577).
|
||||
*/
|
||||
|
||||
const (
|
||||
WeaveBridgeName = "weave"
|
||||
DatapathName = "datapath"
|
||||
|
||||
None BridgeType = iota
|
||||
Bridge
|
||||
Fastdp
|
||||
BridgedFastdp
|
||||
Inconsistent
|
||||
DatapathIfName = "vethwe-datapath"
|
||||
BridgeIfName = "vethwe-bridge"
|
||||
PcapIfName = "vethwe-pcap"
|
||||
)
|
||||
|
||||
func DetectBridgeType(weaveBridgeName, datapathName string) BridgeType {
|
||||
type Bridge interface {
|
||||
init(config *BridgeConfig) error // create and initialise bridge device(s)
|
||||
attach(veth *netlink.Veth) error // attach veth to bridge
|
||||
IsFastdp() bool // does this bridge use fastdp?
|
||||
String() string // human-readable type string
|
||||
}
|
||||
|
||||
// Used to indicate a fallback to the Bridge type
|
||||
var errBridgeNotSupported = errors.New("bridge not supported")
|
||||
|
||||
type bridgeImpl struct{ bridge netlink.Link }
|
||||
type fastdpImpl struct{ datapathName string }
|
||||
type bridgedFastdpImpl struct {
|
||||
bridgeImpl
|
||||
fastdpImpl
|
||||
}
|
||||
|
||||
// Returns a string that is consistent with the weave script
|
||||
func (bridgeImpl) String() string { return "bridge" }
|
||||
func (fastdpImpl) String() string { return "fastdp" }
|
||||
func (bridgedFastdpImpl) String() string { return "bridged_fastdp" }
|
||||
|
||||
// Used to decide whether to manage ODP tunnels
|
||||
func (bridgeImpl) IsFastdp() bool { return false }
|
||||
func (fastdpImpl) IsFastdp() bool { return true }
|
||||
func (bridgedFastdpImpl) IsFastdp() bool { return true }
|
||||
|
||||
func ExistingBridgeType(weaveBridgeName, datapathName string) (Bridge, error) {
|
||||
bridge, _ := netlink.LinkByName(weaveBridgeName)
|
||||
datapath, _ := netlink.LinkByName(datapathName)
|
||||
|
||||
switch {
|
||||
case bridge == nil && datapath == nil:
|
||||
return None
|
||||
return nil, nil
|
||||
case isBridge(bridge) && datapath == nil:
|
||||
return Bridge
|
||||
return bridgeImpl{bridge: bridge}, nil
|
||||
case isDatapath(bridge) && datapath == nil:
|
||||
return Fastdp
|
||||
return fastdpImpl{datapathName: datapathName}, nil
|
||||
case isDatapath(datapath) && isBridge(bridge):
|
||||
return BridgedFastdp
|
||||
return bridgedFastdpImpl{bridgeImpl{bridge: bridge}, fastdpImpl{datapathName: datapathName}}, nil
|
||||
default:
|
||||
return Inconsistent
|
||||
return nil, errors.New("Inconsistent bridge state detected. Please do 'weave reset' and try again")
|
||||
}
|
||||
}
|
||||
|
||||
func EnforceAddrAssignType(bridgeName string) (setAddr bool, err error) {
|
||||
sysctlFilename := filepath.Join("/sys/class/net/", bridgeName, "/addr_assign_type")
|
||||
addrAssignType, err := ioutil.ReadFile(sysctlFilename)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "reading %q", sysctlFilename)
|
||||
}
|
||||
|
||||
// From include/uapi/linux/netdevice.h
|
||||
// #define NET_ADDR_PERM 0 /* address is permanent (default) */
|
||||
// #define NET_ADDR_RANDOM 1 /* address is generated randomly */
|
||||
// #define NET_ADDR_STOLEN 2 /* address is stolen from other device */
|
||||
// #define NET_ADDR_SET 3 /* address is set using dev_set_mac_address() */
|
||||
// Note the file typically has a newline at the end, so we just look at the first char
|
||||
if addrAssignType[0] != '3' {
|
||||
link, err := netlink.LinkByName(bridgeName)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "EnforceAddrAssignType finding bridge %s", bridgeName)
|
||||
}
|
||||
|
||||
mac, err := RandomMAC()
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "creating random MAC")
|
||||
}
|
||||
|
||||
if err := netlink.LinkSetHardwareAddr(link, mac); err != nil {
|
||||
return false, errors.Wrapf(err, "setting bridge %s address to %v", bridgeName, mac)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func isBridge(link netlink.Link) bool {
|
||||
_, isBridge := link.(*netlink.Bridge)
|
||||
return isBridge
|
||||
@@ -108,3 +201,289 @@ func FindBridgeIP(bridgeName string, subnet *net.IPNet) (net.IP, error) {
|
||||
// No subnet, or none in the required subnet; just return the first one
|
||||
return netdev.CIDRs[0].IP, nil
|
||||
}
|
||||
|
||||
type BridgeConfig struct {
|
||||
DockerBridgeName string
|
||||
WeaveBridgeName string
|
||||
DatapathName string
|
||||
NoFastdp bool
|
||||
NoBridgedFastdp bool
|
||||
AWSVPC bool
|
||||
NPC bool
|
||||
MTU int
|
||||
Mac string
|
||||
Port int
|
||||
}
|
||||
|
||||
func (config *BridgeConfig) configuredBridgeType() Bridge {
|
||||
switch {
|
||||
case config.NoFastdp:
|
||||
return bridgeImpl{}
|
||||
case config.NoBridgedFastdp:
|
||||
return fastdpImpl{datapathName: config.WeaveBridgeName}
|
||||
default:
|
||||
return bridgedFastdpImpl{fastdpImpl: fastdpImpl{datapathName: config.DatapathName}}
|
||||
}
|
||||
}
|
||||
|
||||
func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger) (Bridge, error) {
|
||||
bridgeType, err := ExistingBridgeType(config.WeaveBridgeName, config.DatapathName)
|
||||
if bridgeType != nil || err != nil {
|
||||
return bridgeType, err
|
||||
}
|
||||
|
||||
bridgeType = config.configuredBridgeType()
|
||||
for {
|
||||
if err := bridgeType.init(config); err != nil {
|
||||
if errors.Cause(err) == errBridgeNotSupported {
|
||||
log.Warnf("Skipping bridge creation of %q due to: %s", bridgeType, err)
|
||||
bridgeType = bridgeImpl{}
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err := configureIPTables(config); err != nil {
|
||||
return bridgeType, errors.Wrap(err, "configuring iptables")
|
||||
}
|
||||
|
||||
if config.AWSVPC {
|
||||
// Set proxy_arp on the bridge, so that it could accept packets destined
|
||||
// to containers within the same subnet but running on remote hosts.
|
||||
// Without it, exact routes on each container are required.
|
||||
if err := sysctl(procPath, "net/ipv4/conf/"+config.WeaveBridgeName+"/proxy_arp", "1"); err != nil {
|
||||
return bridgeType, errors.Wrap(err, "setting proxy_arp")
|
||||
}
|
||||
// Avoid delaying the first ARP request. Also, setting it to 0 avoids
|
||||
// placing the request into a bounded queue as it can be seen:
|
||||
// https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/tree/net/ipv4/arp.c?id=refs/tags/v4.6.1#n819
|
||||
if err := sysctl(procPath, "net/ipv4/neigh/"+config.WeaveBridgeName+"/proxy_delay", "0"); err != nil {
|
||||
return bridgeType, errors.Wrap(err, "setting proxy_arp")
|
||||
}
|
||||
}
|
||||
|
||||
if err := linkSetUpByName(config.WeaveBridgeName); err != nil {
|
||||
return bridgeType, err
|
||||
}
|
||||
|
||||
if err := ConfigureARPCache(procPath, config.WeaveBridgeName); err != nil {
|
||||
return bridgeType, errors.Wrapf(err, "configuring ARP cache on bridge %q", config.WeaveBridgeName)
|
||||
}
|
||||
|
||||
return bridgeType, nil
|
||||
}
|
||||
|
||||
func (b bridgeImpl) initPrep(config *BridgeConfig) error {
|
||||
mac, err := net.ParseMAC(config.Mac)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "parsing bridge MAC %q", config.Mac)
|
||||
}
|
||||
|
||||
linkAttrs := netlink.NewLinkAttrs()
|
||||
linkAttrs.Name = config.WeaveBridgeName
|
||||
linkAttrs.HardwareAddr = mac
|
||||
if config.MTU == 0 {
|
||||
config.MTU = 65535
|
||||
}
|
||||
b.bridge = &netlink.Bridge{LinkAttrs: linkAttrs}
|
||||
if err = netlink.LinkAdd(b.bridge); err != nil {
|
||||
return errors.Wrapf(err, "creating bridge %q", config.WeaveBridgeName)
|
||||
}
|
||||
if err := netlink.LinkSetHardwareAddr(b.bridge, mac); err != nil {
|
||||
return errors.Wrapf(err, "setting bridge %q mac %v", config.WeaveBridgeName, mac)
|
||||
}
|
||||
// Attempting to set the bridge MTU to a high value directly
|
||||
// fails. Bridges take the lowest MTU of their interfaces. So
|
||||
// instead we create a temporary interface with the desired MTU,
|
||||
// attach that to the bridge, and then remove it again.
|
||||
dummy := &netlink.Dummy{LinkAttrs: netlink.NewLinkAttrs()}
|
||||
dummy.LinkAttrs.Name = "vethwedu"
|
||||
if err = netlink.LinkAdd(dummy); err != nil {
|
||||
return errors.Wrap(err, "creating dummy interface")
|
||||
}
|
||||
if err := netlink.LinkSetMTU(dummy, config.MTU); err != nil {
|
||||
return errors.Wrapf(err, "setting dummy interface mtu to %d", config.MTU)
|
||||
}
|
||||
if err := netlink.LinkSetMasterByIndex(dummy, b.bridge.Attrs().Index); err != nil {
|
||||
return errors.Wrap(err, "setting dummy interface master")
|
||||
}
|
||||
if err := netlink.LinkDel(dummy); err != nil {
|
||||
return errors.Wrap(err, "deleting dummy interface")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b bridgeImpl) init(config *BridgeConfig) error {
|
||||
if err := b.initPrep(config); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := CreateAndAttachVeth(BridgeIfName, PcapIfName, config.WeaveBridgeName, config.MTU, true, func(veth netlink.Link) error {
|
||||
return netlink.LinkSetUp(veth)
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "creating pcap veth pair")
|
||||
}
|
||||
if err := EthtoolTXOff(config.WeaveBridgeName); err != nil {
|
||||
return errors.Wrap(err, "setting tx off")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f fastdpImpl) init(config *BridgeConfig) error {
|
||||
odpSupported, err := odp.CreateDatapath(f.datapathName)
|
||||
if !odpSupported {
|
||||
msg := ""
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
}
|
||||
return errors.Wrap(errBridgeNotSupported, msg)
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "creating datapath %q", f.datapathName)
|
||||
}
|
||||
datapath, err := netlink.LinkByName(f.datapathName)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "finding datapath %q", f.datapathName)
|
||||
}
|
||||
if config.MTU == 0 {
|
||||
/* GCE has the lowest underlay network MTU we're likely to encounter on
|
||||
a local network, at 1460 bytes. To get the overlay MTU from that we
|
||||
subtract 20 bytes for the outer IPv4 header, 8 bytes for the outer
|
||||
UDP header, 8 bytes for the vxlan header, and 14 bytes for the inner
|
||||
ethernet header. In addition, we subtract 34 bytes for the ESP overhead
|
||||
which is needed for the vxlan encryption. */
|
||||
config.MTU = 1376
|
||||
}
|
||||
if err := netlink.LinkSetMTU(datapath, config.MTU); err != nil {
|
||||
return errors.Wrapf(err, "setting datapath %q mtu %d", f.datapathName, config.MTU)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bf bridgedFastdpImpl) init(config *BridgeConfig) error {
|
||||
if err := bf.fastdpImpl.init(config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := bf.bridgeImpl.initPrep(config); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := CreateAndAttachVeth(BridgeIfName, DatapathIfName, config.WeaveBridgeName, config.MTU, true, func(veth netlink.Link) error {
|
||||
if err := netlink.LinkSetUp(veth); err != nil {
|
||||
return errors.Wrapf(err, "setting link up on %q", veth.Attrs().Name)
|
||||
}
|
||||
if err := odp.AddDatapathInterface(bf.datapathName, veth.Attrs().Name); err != nil {
|
||||
return errors.Wrapf(err, "adding interface %q to datapath %q", veth.Attrs().Name, bf.datapathName)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "creating bridged fastdp veth pair")
|
||||
}
|
||||
|
||||
return linkSetUpByName(bf.datapathName)
|
||||
}
|
||||
|
||||
func (b bridgeImpl) attach(veth *netlink.Veth) error {
|
||||
return netlink.LinkSetMasterByIndex(veth, b.bridge.Attrs().Index)
|
||||
}
|
||||
|
||||
func (bf bridgedFastdpImpl) attach(veth *netlink.Veth) error {
|
||||
return bf.bridgeImpl.attach(veth)
|
||||
}
|
||||
|
||||
func (f fastdpImpl) attach(veth *netlink.Veth) error {
|
||||
return odp.AddDatapathInterface(f.datapathName, veth.Attrs().Name)
|
||||
}
|
||||
|
||||
func configureIPTables(config *BridgeConfig) error {
|
||||
ipt, err := iptables.New()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "creating iptables object")
|
||||
}
|
||||
if config.DockerBridgeName != "" {
|
||||
if config.WeaveBridgeName != config.DockerBridgeName {
|
||||
err := ipt.Insert("filter", "FORWARD", 1, "-i", config.DockerBridgeName, "-o", config.WeaveBridgeName, "-j", "DROP")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dockerBridgeIP, err := FindBridgeIP(config.DockerBridgeName, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// forbid traffic to the Weave port from other containers
|
||||
if err = ipt.AppendUnique("filter", "INPUT", "-i", config.DockerBridgeName, "-p", "tcp", "--dst", dockerBridgeIP.String(), "--dport", fmt.Sprint(config.Port), "-j", "DROP"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ipt.AppendUnique("filter", "INPUT", "-i", config.DockerBridgeName, "-p", "udp", "--dst", dockerBridgeIP.String(), "--dport", fmt.Sprint(config.Port), "-j", "DROP"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ipt.AppendUnique("filter", "INPUT", "-i", config.DockerBridgeName, "-p", "udp", "--dst", dockerBridgeIP.String(), "--dport", fmt.Sprint(config.Port+1), "-j", "DROP"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// let DNS traffic to weaveDNS, since otherwise it might get blocked by the likes of UFW
|
||||
if err = ipt.AppendUnique("filter", "INPUT", "-i", config.DockerBridgeName, "-p", "udp", "--dport", "53", "-j", "ACCEPT"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ipt.AppendUnique("filter", "INPUT", "-i", config.DockerBridgeName, "-p", "tcp", "--dport", "53", "-j", "ACCEPT"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if config.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 = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "WEAVE-NPC"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-m", "state", "--state", "NEW", "-j", "NFLOG", "--nflog-group", "86"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "DROP"); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Work around the situation where there are no rules allowing traffic
|
||||
// across our bridge. E.g. ufw
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-i", config.WeaveBridgeName, "-o", config.WeaveBridgeName, "-j", "ACCEPT"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !config.NPC {
|
||||
// Create a chain for allowing ingress traffic when the bridge is exposed
|
||||
_ = ipt.NewChain("filter", "WEAVE-EXPOSE")
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "WEAVE-EXPOSE"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Forward from weave to the rest of the world
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-i", config.WeaveBridgeName, "!", "-o", config.WeaveBridgeName, "-j", "ACCEPT"); err != nil {
|
||||
return err
|
||||
}
|
||||
// and allow replies back
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create a chain for masquerading
|
||||
if err = ipt.ClearChain("nat", "WEAVE"); err != nil {
|
||||
return errors.Wrap(err, "clearing WEAVE chain")
|
||||
}
|
||||
return ipt.AppendUnique("nat", "POSTROUTING", "-j", "WEAVE")
|
||||
}
|
||||
|
||||
func linkSetUpByName(linkName string) error {
|
||||
link, err := netlink.LinkByName(linkName)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "setting link up on %q", linkName)
|
||||
}
|
||||
return netlink.LinkSetUp(link)
|
||||
}
|
||||
+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
|
||||
}
|
||||
}
|
||||
@@ -82,3 +82,25 @@ func findInterface(ifaceName string) (iface *net.Interface, err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func LocalAddresses() ([]*net.IPNet, error) {
|
||||
links, err := netlink.LinkList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
retval := []*net.IPNet{}
|
||||
for _, link := range links {
|
||||
if _, isBridge := link.(*netlink.Bridge); isBridge || (link.Attrs().Flags&net.FlagLoopback) != 0 {
|
||||
continue
|
||||
}
|
||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
retval = append(retval, addr.IPNet)
|
||||
}
|
||||
}
|
||||
return retval, nil
|
||||
}
|
||||
+1
-4
@@ -421,10 +421,7 @@ func (ipsec *IPSec) installDropNonEncrypted(srcIP, dstIP net.IP, udpPort int, in
|
||||
|
||||
func (ipsec *IPSec) removeDropNonEncrypted(srcIP, dstIP net.IP, udpPort int, inSPI SPI) error {
|
||||
rules := rulesDropNonEncrypted(srcIP, dstIP, udpPort, inSPI)
|
||||
if err := ipsec.resetRules(rules, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return ipsec.resetRules(rules, true)
|
||||
}
|
||||
|
||||
func (ipsec *IPSec) removeDropNonEncryptedInbound(srcIP, dstIP net.IP, inSPI SPI) error {
|
||||
|
||||
+6
-1
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
@@ -80,5 +81,9 @@ func WithNetNSByPid(pid int, cmd string, args ...string) ([]byte, error) {
|
||||
}
|
||||
|
||||
func NSPathByPid(pid int) string {
|
||||
return fmt.Sprintf("/proc/%d/ns/net", pid)
|
||||
return NSPathByPidWithRoot("/", pid)
|
||||
}
|
||||
|
||||
func NSPathByPidWithRoot(root string, pid int) string {
|
||||
return filepath.Join(root, fmt.Sprintf("/proc/%d/ns/net", pid))
|
||||
}
|
||||
+4
-5
@@ -36,8 +36,8 @@ func getSystemUUID(hostRoot string) ([]byte, error) {
|
||||
return append(machineid, uuid...), nil
|
||||
}
|
||||
|
||||
func getPersistedPeerName(pathName string) (mesh.PeerName, error) {
|
||||
d, err := db.NewBoltDBReadOnly(pathName)
|
||||
func getPersistedPeerName(dbPrefix string) (mesh.PeerName, error) {
|
||||
d, err := db.NewBoltDBReadOnly(dbPrefix)
|
||||
if err != nil {
|
||||
return mesh.UnknownPeerName, err
|
||||
}
|
||||
@@ -56,9 +56,8 @@ func getPersistedPeerName(pathName string) (mesh.PeerName, error) {
|
||||
func GetSystemPeerName(dbPrefix, hostRoot string) (string, error) {
|
||||
// Check if we have a persisted name that matches the old-style ID for this host
|
||||
if oldUUID, err := getOldStyleSystemUUID(); err == nil {
|
||||
pathName := dbPrefix + db.FileName
|
||||
if _, err := os.Stat(pathName); err == nil {
|
||||
persistedPeerName, err := getPersistedPeerName(pathName)
|
||||
if _, err := os.Stat(db.Pathname(dbPrefix)); err == nil {
|
||||
persistedPeerName, err := getPersistedPeerName(dbPrefix)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
+95
-107
@@ -4,15 +4,12 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/j-keck/arping"
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
|
||||
"github.com/weaveworks/weave/common/odp"
|
||||
)
|
||||
|
||||
// create and attach a veth to the Weave bridge
|
||||
@@ -40,22 +37,17 @@ func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, keepTXOn bo
|
||||
return nil, fmt.Errorf(format, a...)
|
||||
}
|
||||
|
||||
switch bridgeType := DetectBridgeType(bridgeName, DatapathName); bridgeType {
|
||||
case Bridge, BridgedFastdp:
|
||||
if err := netlink.LinkSetMasterByIndex(veth, bridge.Attrs().Index); err != nil {
|
||||
return cleanup(`unable to set master of %s: %s`, name, err)
|
||||
bridgeType, err := ExistingBridgeType(bridgeName, DatapathName)
|
||||
if err != nil {
|
||||
return cleanup("detect bridge type: %s", err)
|
||||
}
|
||||
if err := bridgeType.attach(veth); err != nil {
|
||||
return cleanup("attaching veth %q to %q: %s", name, bridgeName, err)
|
||||
}
|
||||
if !bridgeType.IsFastdp() && !keepTXOn {
|
||||
if err := EthtoolTXOff(veth.PeerName); err != nil {
|
||||
return cleanup(`unable to set tx off on %q: %s`, peerName, err)
|
||||
}
|
||||
if bridgeType == Bridge && !keepTXOn {
|
||||
if err := EthtoolTXOff(peerName); err != nil {
|
||||
return cleanup(`unable to set tx off on %q: %s`, peerName, err)
|
||||
}
|
||||
}
|
||||
case Fastdp:
|
||||
if err := odp.AddDatapathInterface(bridgeName, name); err != nil {
|
||||
return cleanup(`failed to attach %s to device "%s": %s`, name, bridgeName, err)
|
||||
}
|
||||
default:
|
||||
return cleanup(`invalid bridge configuration`)
|
||||
}
|
||||
|
||||
if init != nil {
|
||||
@@ -111,27 +103,20 @@ func interfaceExistsInNamespace(netNSPath string, ifName string) bool {
|
||||
return err == 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 AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMulticastRoute bool, cidrs []*net.IPNet, keepTXOn bool) error {
|
||||
func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMulticastRoute bool, cidrs []*net.IPNet, keepTXOn bool, hairpinMode bool) error {
|
||||
ns, err := netns.GetFromPath(netNSPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ns.Close()
|
||||
|
||||
ipt, err := iptables.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !interfaceExistsInNamespace(netNSPath, ifName) {
|
||||
maxIDLen := IFNAMSIZ - 1 - len(vethPrefix+"pl")
|
||||
if len(id) > maxIDLen {
|
||||
id = id[:maxIDLen] // trim passed ID if too long
|
||||
}
|
||||
name, peerName := vethPrefix+"pl"+id, vethPrefix+"pg"+id
|
||||
_, err := CreateAndAttachVeth(name, peerName, bridgeName, mtu, keepTXOn, func(veth netlink.Link) error {
|
||||
veth, err := CreateAndAttachVeth(name, peerName, bridgeName, mtu, keepTXOn, func(veth netlink.Link) error {
|
||||
if err := netlink.LinkSetNsFd(veth, int(ns)); err != nil {
|
||||
return fmt.Errorf("failed to move veth to container netns: %s", err)
|
||||
}
|
||||
@@ -143,59 +128,98 @@ func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMult
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = netlink.LinkSetHairpin(veth, hairpinMode); err != nil {
|
||||
return fmt.Errorf("unable to set hairpin mode to %t for bridge side of veth %s: %s", hairpinMode, name, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err := WithNetNSLinkUnsafe(ns, ifName, func(veth netlink.Link) error {
|
||||
newAddresses, err := AddAddresses(veth, cidrs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args := []string{ifName, fmt.Sprintf("%t", withMulticastRoute)}
|
||||
for _, cidr := range cidrs {
|
||||
args = append(args, cidr.String())
|
||||
}
|
||||
if _, err := WithNetNS(netNSPath, "setup-iface-addrs", args...); err != nil {
|
||||
return fmt.Errorf("error setting up interface addresses: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add multicast ACCEPT rules for new subnets
|
||||
for _, ipnet := range newAddresses {
|
||||
acceptRule := []string{"-i", ifName, "-s", subnet(ipnet), "-d", "224.0.0.0/4", "-j", "ACCEPT"}
|
||||
exists, err := ipt.Exists("filter", "INPUT", acceptRule...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
if err := ipt.Insert("filter", "INPUT", 1, acceptRule...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := netlink.LinkSetUp(veth); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ipnet := range newAddresses {
|
||||
// If we don't wait for a bit here, we see the arp fail to reach the bridge.
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
arping.GratuitousArpOverIfaceByName(ipnet.IP, ifName)
|
||||
}
|
||||
if withMulticastRoute {
|
||||
/* Route multicast packets across the weave network.
|
||||
This must come last in 'attach'. If you change this, change weavewait to match.
|
||||
|
||||
TODO: Add the MTU lock to prevent PMTU discovery for multicast
|
||||
destinations. Without that, the kernel sets the DF flag on
|
||||
multicast packets. Since RFC1122 prohibits sending of ICMP
|
||||
errors for packets with multicast destinations, that causes
|
||||
packets larger than the PMTU to be dropped silently. */
|
||||
|
||||
_, multicast, _ := net.ParseCIDR("224.0.0.0/4")
|
||||
if err := AddRoute(veth, netlink.SCOPE_LINK, multicast, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
// SetupIfaceAddrs is the implementation of the 'setup-iface-addrs' call above,
|
||||
// running in another process in the container's netns
|
||||
func SetupIfaceAddrs(veth netlink.Link, withMulticastRoute bool, cidrs []*net.IPNet) error {
|
||||
newAddresses, err := AddAddresses(veth, cidrs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ifName := veth.Attrs().Name
|
||||
ipt, err := iptables.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add multicast ACCEPT rules for new subnets
|
||||
for _, ipnet := range newAddresses {
|
||||
acceptRule := []string{"-i", ifName, "-s", subnet(ipnet), "-d", "224.0.0.0/4", "-j", "ACCEPT"}
|
||||
exists, err := ipt.Exists("filter", "INPUT", acceptRule...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
if err := ipt.Insert("filter", "INPUT", 1, acceptRule...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := netlink.LinkSetUp(veth); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ipnet := range newAddresses {
|
||||
// If we don't wait for a bit here, we see the arp fail to reach the bridge.
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
arping.GratuitousArpOverIfaceByName(ipnet.IP, ifName)
|
||||
}
|
||||
if withMulticastRoute {
|
||||
/* Route multicast packets across the weave network.
|
||||
This must come last in 'attach'. If you change this, change weavewait to match.
|
||||
|
||||
TODO: Add the MTU lock to prevent PMTU discovery for multicast
|
||||
destinations. Without that, the kernel sets the DF flag on
|
||||
multicast packets. Since RFC1122 prohibits sending of ICMP
|
||||
errors for packets with multicast destinations, that causes
|
||||
packets larger than the PMTU to be dropped silently. */
|
||||
|
||||
_, multicast, _ := net.ParseCIDR("224.0.0.0/4")
|
||||
if err := AddRoute(veth, netlink.SCOPE_LINK, multicast, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupIface is the implementation of the 'setup-iface' call above,
|
||||
// running in another process in the container's netns
|
||||
func SetupIface(ifaceName, newIfName string) error {
|
||||
ipt, err := iptables.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
link, err := netlink.LinkByName(ifaceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.LinkSetName(link, newIfName); err != nil {
|
||||
return err
|
||||
}
|
||||
// This is only called by AttachContainer which is only called in host pid namespace
|
||||
if err := ConfigureARPCache("/proc", newIfName); err != nil {
|
||||
return err
|
||||
}
|
||||
return ipt.Append("filter", "INPUT", "-i", newIfName, "-d", "224.0.0.0/4", "-j", "DROP")
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -272,39 +296,3 @@ func subnets(addrs []netlink.Addr) map[string]struct{} {
|
||||
}
|
||||
return subnets
|
||||
}
|
||||
|
||||
func addNatRule(ipt *iptables.IPTables, rulespec ...string) error {
|
||||
// Loop until we get an exit code other than "temporarily unavailable"
|
||||
for {
|
||||
if err := ipt.AppendUnique("nat", "WEAVE", rulespec...); err != nil {
|
||||
if ierr, ok := err.(*iptables.Error); ok {
|
||||
if status, ok := ierr.ExitError.Sys().(syscall.WaitStatus); ok {
|
||||
// (magic exit code 4 found in iptables source code; undocumented)
|
||||
if status.ExitStatus() == 4 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ExposeNAT(ipnet net.IPNet) error {
|
||||
ipt, err := iptables.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cidr := ipnet.String()
|
||||
if err := addNatRule(ipt, "-s", cidr, "-d", "224.0.0.0/4", "-j", "RETURN"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := addNatRule(ipt, "-d", cidr, "!", "-s", cidr, "-j", "MASQUERADE"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := addNatRule(ipt, "-s", cidr, "!", "-d", cidr, "-j", "MASQUERADE"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+121
-28
@@ -3,14 +3,19 @@ package npc
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/client-go/pkg/api"
|
||||
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||
"k8s.io/client-go/pkg/util/intstr"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
extnapi "k8s.io/api/extensions/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
)
|
||||
|
||||
func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
|
||||
// analysePolicyLegacy is used to analyse extensions/v1beta1/NetworkPolicy (legacy) and
|
||||
// implements pre-1.7 k8s netpol semantics, whilst analysePolicy - networking.k8s.io/v1/NetworkPolicy
|
||||
// and 1.7 semantics.
|
||||
|
||||
func (ns *ns) analysePolicyLegacy(policy *extnapi.NetworkPolicy) (
|
||||
rules map[string]*ruleSpec,
|
||||
nsSelectors, podSelectors map[string]*selectorSpec,
|
||||
err error) {
|
||||
@@ -19,7 +24,7 @@ func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
|
||||
podSelectors = make(map[string]*selectorSpec)
|
||||
rules = make(map[string]*ruleSpec)
|
||||
|
||||
dstSelector, err := newSelectorSpec(&policy.Spec.PodSelector, ns.name, ipset.HashIP)
|
||||
dstSelector, err := newSelectorSpec(&policy.Spec.PodSelector, true, ns.name, ipset.HashIP)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
@@ -45,7 +50,7 @@ func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
|
||||
} 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) {
|
||||
withNormalisedProtoAndPortLegacy(ingressRule.Ports, func(proto, port string) {
|
||||
rule := newRuleSpec(&proto, nil, dstSelector, &port)
|
||||
rules[rule.key] = rule
|
||||
})
|
||||
@@ -56,14 +61,14 @@ func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
|
||||
for _, peer := range ingressRule.From {
|
||||
var srcSelector *selectorSpec
|
||||
if peer.PodSelector != nil {
|
||||
srcSelector, err = newSelectorSpec(peer.PodSelector, ns.name, ipset.HashIP)
|
||||
srcSelector, err = newSelectorSpec(peer.PodSelector, false, ns.name, ipset.HashIP)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
podSelectors[srcSelector.key] = srcSelector
|
||||
}
|
||||
if peer.NamespaceSelector != nil {
|
||||
srcSelector, err = newSelectorSpec(peer.NamespaceSelector, "", ipset.ListSet)
|
||||
srcSelector, err = newSelectorSpec(peer.NamespaceSelector, false, "", ipset.ListSet)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
@@ -77,6 +82,80 @@ func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
|
||||
} 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.
|
||||
withNormalisedProtoAndPortLegacy(ingressRule.Ports, func(proto, port string) {
|
||||
rule := newRuleSpec(&proto, srcSelector, dstSelector, &port)
|
||||
rules[rule.key] = rule
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nsSelectors, podSelectors, nil
|
||||
}
|
||||
|
||||
func (ns *ns) analysePolicy(policy *networkingv1.NetworkPolicy) (
|
||||
rules map[string]*ruleSpec,
|
||||
nsSelectors, podSelectors map[string]*selectorSpec,
|
||||
err error) {
|
||||
|
||||
nsSelectors = make(map[string]*selectorSpec)
|
||||
podSelectors = make(map[string]*selectorSpec)
|
||||
rules = make(map[string]*ruleSpec)
|
||||
|
||||
// If empty, matches all pods in a namespace
|
||||
dstSelector, err := newSelectorSpec(&policy.Spec.PodSelector, true, ns.name, ipset.HashIP)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
podSelectors[dstSelector.key] = dstSelector
|
||||
|
||||
// If ingress is empty then this NetworkPolicy does not allow any traffic
|
||||
if policy.Spec.Ingress == nil || len(policy.Spec.Ingress) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ingressRule := range policy.Spec.Ingress {
|
||||
// If Ports is empty or missing, this rule matches all ports
|
||||
allPorts := ingressRule.Ports == nil || len(ingressRule.Ports) == 0
|
||||
// If From is empty or missing, this rule matches all sources
|
||||
allSources := ingressRule.From == nil || len(ingressRule.From) == 0
|
||||
|
||||
if allSources {
|
||||
if allPorts {
|
||||
rule := newRuleSpec(nil, nil, dstSelector, nil)
|
||||
rules[rule.key] = rule
|
||||
} else {
|
||||
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
||||
rule := newRuleSpec(&proto, nil, dstSelector, &port)
|
||||
rules[rule.key] = rule
|
||||
})
|
||||
}
|
||||
} else {
|
||||
for _, peer := range ingressRule.From {
|
||||
var srcSelector *selectorSpec
|
||||
|
||||
// NetworkPolicyPeer describes a peer to allow traffic from.
|
||||
// Exactly one of its fields must be specified.
|
||||
if peer.PodSelector != nil {
|
||||
srcSelector, err = newSelectorSpec(peer.PodSelector, false, ns.name, ipset.HashIP)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
podSelectors[srcSelector.key] = srcSelector
|
||||
|
||||
} else if peer.NamespaceSelector != nil {
|
||||
srcSelector, err = newSelectorSpec(peer.NamespaceSelector, false, "", ipset.ListSet)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
nsSelectors[srcSelector.key] = srcSelector
|
||||
}
|
||||
|
||||
if allPorts {
|
||||
rule := newRuleSpec(nil, srcSelector, dstSelector, nil)
|
||||
rules[rule.key] = rule
|
||||
} else {
|
||||
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
||||
rule := newRuleSpec(&proto, srcSelector, dstSelector, &port)
|
||||
rules[rule.key] = rule
|
||||
@@ -89,26 +168,40 @@ func (ns *ns) analysePolicy(policy *extnapi.NetworkPolicy) (
|
||||
return rules, nsSelectors, podSelectors, nil
|
||||
}
|
||||
|
||||
func withNormalisedProtoAndPort(npps []extnapi.NetworkPolicyPort, f func(proto, port string)) {
|
||||
func withNormalisedProtoAndPortLegacy(npps []extnapi.NetworkPolicyPort, f func(proto, port string)) {
|
||||
for _, npp := range npps {
|
||||
// If no proto is specified, default to TCP
|
||||
proto := string(api.ProtocolTCP)
|
||||
if npp.Protocol != nil {
|
||||
proto = string(*npp.Protocol)
|
||||
}
|
||||
|
||||
// If no port is specified, match any port. Let iptables executable handle
|
||||
// service name resolution
|
||||
port := "0:65535"
|
||||
if npp.Port != nil {
|
||||
switch npp.Port.Type {
|
||||
case intstr.Int:
|
||||
port = fmt.Sprintf("%d", npp.Port.IntVal)
|
||||
case intstr.String:
|
||||
port = npp.Port.StrVal
|
||||
}
|
||||
}
|
||||
|
||||
f(proto, port)
|
||||
f(proto(npp.Protocol), port(npp.Port))
|
||||
}
|
||||
}
|
||||
|
||||
func withNormalisedProtoAndPort(npps []networkingv1.NetworkPolicyPort, f func(proto, port string)) {
|
||||
for _, npp := range npps {
|
||||
f(proto(npp.Protocol), port(npp.Port))
|
||||
}
|
||||
}
|
||||
|
||||
func proto(p *apiv1.Protocol) string {
|
||||
// If no proto is specified, default to TCP
|
||||
proto := string(apiv1.ProtocolTCP)
|
||||
if p != nil {
|
||||
proto = string(*p)
|
||||
}
|
||||
|
||||
return proto
|
||||
}
|
||||
|
||||
func port(p *intstr.IntOrString) string {
|
||||
// If no port is specified, match any port. Let iptables executable handle
|
||||
// service name resolution
|
||||
port := "0:65535"
|
||||
if p != nil {
|
||||
switch p.Type {
|
||||
case intstr.Int:
|
||||
port = fmt.Sprintf("%d", p.IntVal)
|
||||
case intstr.String:
|
||||
port = p.StrVal
|
||||
}
|
||||
}
|
||||
|
||||
return port
|
||||
}
|
||||
@@ -6,4 +6,8 @@ const (
|
||||
MainChain = "WEAVE-NPC"
|
||||
DefaultChain = "WEAVE-NPC-DEFAULT"
|
||||
IngressChain = "WEAVE-NPC-INGRESS"
|
||||
|
||||
IpsetNamePrefix = "weave-"
|
||||
|
||||
LocalIpset = IpsetNamePrefix + "local-pods"
|
||||
)
|
||||
+54
-20
@@ -3,13 +3,14 @@ package npc
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/pkg/errors"
|
||||
coreapi "k8s.io/client-go/pkg/api/v1"
|
||||
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||
coreapi "k8s.io/api/core/v1"
|
||||
extnapi "k8s.io/api/extensions/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
"github.com/weaveworks/weave/npc/iptables"
|
||||
)
|
||||
|
||||
type NetworkPolicyController interface {
|
||||
@@ -21,28 +22,35 @@ type NetworkPolicyController interface {
|
||||
UpdatePod(oldObj, newObj *coreapi.Pod) error
|
||||
DeletePod(obj *coreapi.Pod) error
|
||||
|
||||
AddNetworkPolicy(obj *extnapi.NetworkPolicy) error
|
||||
UpdateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error
|
||||
DeleteNetworkPolicy(obj *extnapi.NetworkPolicy) error
|
||||
AddNetworkPolicy(obj interface{}) error
|
||||
UpdateNetworkPolicy(oldObj, newObj interface{}) error
|
||||
DeleteNetworkPolicy(obj interface{}) error
|
||||
}
|
||||
|
||||
type controller struct {
|
||||
sync.Mutex
|
||||
|
||||
ipt *iptables.IPTables
|
||||
nodeName string // my node name
|
||||
|
||||
ipt iptables.Interface
|
||||
ips ipset.Interface
|
||||
|
||||
nss map[string]*ns // ns name -> ns struct
|
||||
nsSelectors *selectorSet // selector string -> nsSelector
|
||||
|
||||
legacy bool // use legacy network policy semantics (k8s pre-1.7)
|
||||
}
|
||||
|
||||
func New(ipt *iptables.IPTables, ips ipset.Interface) NetworkPolicyController {
|
||||
func New(nodeName string, legacy bool, ipt iptables.Interface, ips ipset.Interface) NetworkPolicyController {
|
||||
c := &controller{
|
||||
ipt: ipt,
|
||||
ips: ips,
|
||||
nss: make(map[string]*ns)}
|
||||
nodeName: nodeName,
|
||||
legacy: legacy,
|
||||
ipt: ipt,
|
||||
ips: ips,
|
||||
nss: make(map[string]*ns)}
|
||||
|
||||
c.nsSelectors = newSelectorSet(ips, c.onNewNsSelector)
|
||||
doNothing := func(*selector) error { return nil }
|
||||
c.nsSelectors = newSelectorSet(ips, c.onNewNsSelector, doNothing, doNothing)
|
||||
|
||||
return c
|
||||
}
|
||||
@@ -51,7 +59,7 @@ func (npc *controller) onNewNsSelector(selector *selector) error {
|
||||
for _, ns := range npc.nss {
|
||||
if ns.namespace != nil {
|
||||
if selector.matches(ns.namespace.ObjectMeta.Labels) {
|
||||
if err := ns.ips.AddEntry(selector.spec.ipsetName, string(ns.allPods.ipsetName)); err != nil {
|
||||
if err := selector.addEntry(string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -63,7 +71,7 @@ func (npc *controller) onNewNsSelector(selector *selector) error {
|
||||
func (npc *controller) withNS(name string, f func(ns *ns) error) error {
|
||||
ns, found := npc.nss[name]
|
||||
if !found {
|
||||
newNs, err := newNS(name, npc.ipt, npc.ips, npc.nsSelectors)
|
||||
newNs, err := newNS(name, npc.nodeName, npc.legacy, npc.ipt, npc.ips, npc.nsSelectors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -112,32 +120,47 @@ func (npc *controller) DeletePod(obj *coreapi.Pod) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (npc *controller) AddNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
||||
func (npc *controller) AddNetworkPolicy(obj interface{}) error {
|
||||
npc.Lock()
|
||||
defer npc.Unlock()
|
||||
|
||||
nsName, err := nsName(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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")
|
||||
})
|
||||
}
|
||||
|
||||
func (npc *controller) UpdateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error {
|
||||
func (npc *controller) UpdateNetworkPolicy(oldObj, newObj interface{}) error {
|
||||
npc.Lock()
|
||||
defer npc.Unlock()
|
||||
|
||||
nsName, err := nsName(oldObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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")
|
||||
})
|
||||
}
|
||||
|
||||
func (npc *controller) DeleteNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
||||
func (npc *controller) DeleteNetworkPolicy(obj interface{}) error {
|
||||
npc.Lock()
|
||||
defer npc.Unlock()
|
||||
|
||||
nsName, err := nsName(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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")
|
||||
})
|
||||
}
|
||||
@@ -171,3 +194,14 @@ func (npc *controller) DeleteNamespace(obj *coreapi.Namespace) error {
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
coreapi "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
type mockSet struct {
|
||||
name ipset.Name
|
||||
setType ipset.Type
|
||||
subSets map[string]bool
|
||||
}
|
||||
|
||||
type mockIPSet struct {
|
||||
sets map[string]mockSet
|
||||
}
|
||||
|
||||
func newMockIPSet() mockIPSet {
|
||||
i := mockIPSet{
|
||||
sets: make(map[string]mockSet),
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
func (i *mockIPSet) Create(ipsetName ipset.Name, ipsetType ipset.Type) error {
|
||||
if _, ok := i.sets[string(ipsetName)]; ok {
|
||||
return errors.Errorf("ipset %s already exists", ipsetName)
|
||||
}
|
||||
i.sets[string(ipsetName)] = mockSet{name: ipsetName, setType: ipsetType, subSets: make(map[string]bool)}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *mockIPSet) AddEntry(ipsetName ipset.Name, entry string, comment string) error {
|
||||
return i.addEntry(ipsetName, entry, comment, true)
|
||||
}
|
||||
|
||||
func (i *mockIPSet) AddEntryIfNotExist(ipsetName ipset.Name, entry string, comment string) error {
|
||||
return i.addEntry(ipsetName, entry, comment, false)
|
||||
}
|
||||
|
||||
func (i *mockIPSet) addEntry(ipsetName ipset.Name, entry string, comment string, checkIfExists bool) error {
|
||||
log.Printf("adding entry %s to %s", entry, ipsetName)
|
||||
if _, ok := i.sets[string(ipsetName)]; !ok {
|
||||
return errors.Errorf("ipset %s does not exist", entry)
|
||||
}
|
||||
if checkIfExists {
|
||||
if _, ok := i.sets[string(ipsetName)].subSets[entry]; ok {
|
||||
return errors.Errorf("ipset %s is already a member of %s", entry, ipsetName)
|
||||
}
|
||||
}
|
||||
i.sets[string(ipsetName)].subSets[entry] = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *mockIPSet) DelEntry(ipsetName ipset.Name, entry string) error {
|
||||
return i.delEntry(ipsetName, entry, true)
|
||||
}
|
||||
|
||||
func (i *mockIPSet) DelEntryIfExists(ipsetName ipset.Name, entry string) error {
|
||||
return i.delEntry(ipsetName, entry, false)
|
||||
}
|
||||
|
||||
func (i *mockIPSet) delEntry(ipsetName ipset.Name, entry string, checkIfExists bool) error {
|
||||
log.Printf("deleting entry %s from %s", entry, ipsetName)
|
||||
if _, ok := i.sets[string(ipsetName)]; !ok {
|
||||
return errors.Errorf("ipset %s does not exist", ipsetName)
|
||||
}
|
||||
if checkIfExists {
|
||||
if _, ok := i.sets[string(ipsetName)].subSets[entry]; !ok {
|
||||
return errors.Errorf("ipset %s is not a member of %s", entry, ipsetName)
|
||||
}
|
||||
}
|
||||
delete(i.sets[string(ipsetName)].subSets, entry)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *mockIPSet) Exist(ipsetName ipset.Name, entry string) bool {
|
||||
_, found := i.sets[string(ipsetName)].subSets[entry]
|
||||
return found
|
||||
}
|
||||
|
||||
func (i *mockIPSet) Flush(ipsetName ipset.Name) error {
|
||||
return errors.New("Not Implemented")
|
||||
}
|
||||
|
||||
func (i *mockIPSet) FlushAll() error {
|
||||
return errors.New("Not Implemented")
|
||||
}
|
||||
|
||||
func (i *mockIPSet) Destroy(ipsetName ipset.Name) error {
|
||||
if _, ok := i.sets[string(ipsetName)]; !ok {
|
||||
return errors.Errorf("ipset %s does not exist", ipsetName)
|
||||
}
|
||||
delete(i.sets, string(ipsetName))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *mockIPSet) DestroyAll() error {
|
||||
return errors.New("Not Implemented")
|
||||
}
|
||||
|
||||
func (i *mockIPSet) List(prefix string) ([]ipset.Name, error) {
|
||||
return []ipset.Name{}, errors.New("Not Implemented")
|
||||
}
|
||||
|
||||
type mockIPTables struct {
|
||||
}
|
||||
|
||||
func (ipt *mockIPTables) Append(table, chain string, rulespec ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ipt *mockIPTables) Delete(table, chain string, rulespec ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ipt *mockIPTables) Insert(table, chain string, pos int, rulespec ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
||||
// Test for race condition between namespace and networkpolicy events
|
||||
// https://github.com/weaveworks/weave/issues/3059
|
||||
|
||||
sourceNamespace := &coreapi.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "source",
|
||||
Labels: map[string]string{
|
||||
"app": "source",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
destinationNamespace := &coreapi.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "destination",
|
||||
},
|
||||
}
|
||||
|
||||
port := intstr.FromInt(12345)
|
||||
|
||||
networkPolicy := &networkingv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "network-policy",
|
||||
Namespace: "destination",
|
||||
},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
Ingress: []networkingv1.NetworkPolicyIngressRule{
|
||||
{
|
||||
From: []networkingv1.NetworkPolicyPeer{
|
||||
{
|
||||
NamespaceSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"app": "source",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Ports: []networkingv1.NetworkPolicyPort{
|
||||
{
|
||||
Port: &port,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Namespaces first
|
||||
m := newMockIPSet()
|
||||
controller := New("foo", false, &mockIPTables{}, &m)
|
||||
|
||||
const (
|
||||
selectorIPSetName = "weave-I239Zp%sCvoVt*D6u=A!2]YEk"
|
||||
sourceIPSetName = "weave-HboJG1fGgG]/SR%k9H#hv5e96"
|
||||
)
|
||||
|
||||
controller.AddNamespace(sourceNamespace)
|
||||
controller.AddNamespace(destinationNamespace)
|
||||
|
||||
controller.AddNetworkPolicy(networkPolicy)
|
||||
|
||||
require.Equal(t, true, m.sets[selectorIPSetName].subSets[sourceIPSetName])
|
||||
|
||||
// NetworkPolicy first
|
||||
m = newMockIPSet()
|
||||
controller = New("foo", false, &mockIPTables{}, &m)
|
||||
|
||||
controller.AddNetworkPolicy(networkPolicy)
|
||||
|
||||
controller.AddNamespace(sourceNamespace)
|
||||
controller.AddNamespace(destinationNamespace)
|
||||
|
||||
require.Equal(t, true, m.sets[selectorIPSetName].subSets[sourceIPSetName])
|
||||
|
||||
}
|
||||
|
||||
// Tests default-allow ipset behavior when running in non-legacy mode.
|
||||
func TestDefaultAllow(t *testing.T) {
|
||||
const (
|
||||
defaultAllowIPSetName = "weave-E.1.0W^NGSp]0_t5WwH/]gX@L"
|
||||
fooPodIP = "10.32.0.10"
|
||||
barPodIP = "10.32.0.11"
|
||||
barPodNewIP = "10.32.0.12"
|
||||
)
|
||||
|
||||
m := newMockIPSet()
|
||||
controller := New("bar", false, &mockIPTables{}, &m)
|
||||
|
||||
defaultNamespace := &coreapi.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default",
|
||||
},
|
||||
}
|
||||
controller.AddNamespace(defaultNamespace)
|
||||
|
||||
// Should create an ipset for default-allow
|
||||
require.Contains(t, m.sets, defaultAllowIPSetName)
|
||||
|
||||
podFoo := &coreapi.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "foo",
|
||||
Namespace: "default",
|
||||
Name: "foo",
|
||||
Labels: map[string]string{"run": "foo"}},
|
||||
Status: coreapi.PodStatus{PodIP: fooPodIP}}
|
||||
controller.AddPod(podFoo)
|
||||
|
||||
// Should add the foo pod to default-allow
|
||||
require.True(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
||||
|
||||
podBar := &coreapi.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "bar",
|
||||
Namespace: "default",
|
||||
Name: "bar",
|
||||
Labels: map[string]string{"run": "bar"}},
|
||||
Status: coreapi.PodStatus{PodIP: barPodIP}}
|
||||
podBarNoIP := &coreapi.Pod{ObjectMeta: podBar.ObjectMeta}
|
||||
controller.AddPod(podBarNoIP)
|
||||
controller.UpdatePod(podBarNoIP, podBar)
|
||||
|
||||
// Should add the bar pod to default-allow
|
||||
require.True(t, m.Exist(defaultAllowIPSetName, barPodIP))
|
||||
|
||||
// Allow access from the bar pod to the foo pod
|
||||
netpol := &networkingv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "allow-from-bar-to-foo",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "foo"}},
|
||||
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||
From: []networkingv1.NetworkPolicyPeer{{
|
||||
PodSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"run": "bar"},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
}
|
||||
controller.AddNetworkPolicy(netpol)
|
||||
|
||||
// Should remove the foo pod from default-allow as the netpol selects it
|
||||
require.False(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
||||
require.True(t, m.Exist(defaultAllowIPSetName, barPodIP))
|
||||
|
||||
podBarWithNewIP := *podBar
|
||||
podBarWithNewIP.Status.PodIP = barPodNewIP
|
||||
controller.UpdatePod(podBar, &podBarWithNewIP)
|
||||
|
||||
// Should update IP addr of the bar pod in default-allow
|
||||
require.False(t, m.Exist(defaultAllowIPSetName, barPodIP))
|
||||
require.True(t, m.Exist(defaultAllowIPSetName, barPodNewIP))
|
||||
|
||||
controller.UpdatePod(&podBarWithNewIP, podBarNoIP)
|
||||
// Should remove the bar pod from default-allow as it does not have any IP addr
|
||||
require.False(t, m.Exist(defaultAllowIPSetName, barPodNewIP))
|
||||
|
||||
podFooWithNewLabel := *podFoo
|
||||
podFooWithNewLabel.ObjectMeta.Labels = map[string]string{"run": "new-foo"}
|
||||
controller.UpdatePod(podFoo, &podFooWithNewLabel)
|
||||
|
||||
// Should bring back the foo pod to default-allow as it does not match dst of any netpol
|
||||
require.True(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
||||
|
||||
controller.UpdatePod(&podFooWithNewLabel, podFoo)
|
||||
// Should remove from default-allow as it matches the netpol after the update
|
||||
require.False(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
||||
|
||||
controller.DeleteNetworkPolicy(netpol)
|
||||
// Should bring back the foo pod to default-allow as no netpol selects it
|
||||
require.True(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
||||
|
||||
controller.DeletePod(podFoo)
|
||||
// Should remove foo pod from default-allow
|
||||
require.False(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
||||
}
|
||||
+83
-6
@@ -1,7 +1,9 @@
|
||||
package ipset
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -17,41 +19,94 @@ const (
|
||||
|
||||
type Interface interface {
|
||||
Create(ipsetName Name, ipsetType Type) error
|
||||
AddEntry(ipsetName Name, entry string) error
|
||||
AddEntry(ipsetName Name, entry string, comment string) error
|
||||
AddEntryIfNotExist(ipsetName Name, entry string, comment string) error
|
||||
DelEntry(ipsetName Name, entry string) error
|
||||
DelEntryIfExists(ipsetName Name, entry string) error
|
||||
Exist(ipsetName Name, entry string) bool
|
||||
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() Interface {
|
||||
return &ipset{refCount: newRefCount()}
|
||||
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 {
|
||||
return doExec("create", string(ipsetName), string(ipsetType))
|
||||
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) error {
|
||||
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
|
||||
}
|
||||
return doExec("add", string(ipsetName), entry)
|
||||
args := []string{"add", string(ipsetName), entry}
|
||||
if i.enableComments {
|
||||
args = append(args, "comment", comment)
|
||||
}
|
||||
return doExec(args...)
|
||||
}
|
||||
|
||||
// AddEntryIfNotExist does the same as AddEntry but bypasses the ref counting.
|
||||
// Should be used only with "default-allow" ipsets.
|
||||
func (i *ipset) AddEntryIfNotExist(ipsetName Name, entry string, comment string) error {
|
||||
if i.count(ipsetName, entry) == 1 {
|
||||
return nil
|
||||
}
|
||||
return i.AddEntry(ipsetName, entry, comment)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// DelEntryIfExists does the same as DelEntry but bypasses the ref counting.
|
||||
// Should be used only with "default-allow" ipsets.
|
||||
func (i *ipset) DelEntryIfExists(ipsetName Name, entry string) error {
|
||||
if i.count(ipsetName, entry) == 0 {
|
||||
return nil
|
||||
}
|
||||
return i.DelEntry(ipsetName, entry)
|
||||
}
|
||||
|
||||
func (i *ipset) Exist(ipsetName Name, entry string) bool {
|
||||
return i.count(ipsetName, entry) > 0
|
||||
}
|
||||
|
||||
func (i *ipset) Flush(ipsetName Name) error {
|
||||
i.removeSet(ipsetName)
|
||||
return doExec("flush", string(ipsetName))
|
||||
@@ -72,6 +127,24 @@ func (i *ipset) DestroyAll() error {
|
||||
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)
|
||||
@@ -106,6 +179,10 @@ func (rc *refCount) dec(ipsetName Name, entry string) int {
|
||||
return rc.ref[k]
|
||||
}
|
||||
|
||||
func (rc *refCount) count(ipsetName Name, entry string) int {
|
||||
return rc.ref[key{ipsetName, entry}]
|
||||
}
|
||||
|
||||
func (rc *refCount) removeSet(ipsetName Name) {
|
||||
for k := range rc.ref {
|
||||
if k.ipsetName == ipsetName {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package iptables
|
||||
|
||||
type Interface interface {
|
||||
Append(table, chain string, rulespec ...string) error
|
||||
Delete(table, chain string, rulespec ...string) error
|
||||
Insert(table, chain string, pos int, rulespec ...string) error
|
||||
}
|
||||
+307
-91
@@ -2,37 +2,50 @@ package npc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"k8s.io/client-go/pkg/api/unversioned"
|
||||
coreapi "k8s.io/client-go/pkg/api/v1"
|
||||
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||
"k8s.io/client-go/pkg/types"
|
||||
"k8s.io/client-go/pkg/util/uuid"
|
||||
coreapi "k8s.io/api/core/v1"
|
||||
extnapi "k8s.io/api/extensions/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
"github.com/weaveworks/weave/npc/iptables"
|
||||
)
|
||||
|
||||
var errInvalidNetworkPolicyObjType = errors.New("invalid NetworkPolicy object type")
|
||||
|
||||
type ns struct {
|
||||
ipt *iptables.IPTables // interface to iptables
|
||||
ipt iptables.Interface // interface to iptables
|
||||
ips ipset.Interface // interface to ipset
|
||||
|
||||
name string // k8s Namespace name
|
||||
namespace *coreapi.Namespace // k8s Namespace object
|
||||
pods map[types.UID]*coreapi.Pod // k8s Pod objects by UID
|
||||
policies map[types.UID]*extnapi.NetworkPolicy // k8s NetworkPolicy objects by UID
|
||||
name string // k8s Namespace name
|
||||
nodeName string // my node name
|
||||
namespace *coreapi.Namespace // k8s Namespace object
|
||||
pods map[types.UID]*coreapi.Pod // k8s Pod objects by UID
|
||||
policies map[types.UID]interface{} // k8s NetworkPolicy objects by UID
|
||||
|
||||
uid types.UID // surrogate UID to own allPods selector
|
||||
allPods *selectorSpec // hash:ip ipset of all pod IPs in this namespace
|
||||
|
||||
// stores IP addrs of pods which are not selected by any dst podSelector of
|
||||
// any netpol; used only in non-legacy mode and is used as a dst in
|
||||
// the WEAVE-NPC-DEFAULT iptables chain.
|
||||
defaultAllowIPSet ipset.Name
|
||||
|
||||
nsSelectors *selectorSet
|
||||
podSelectors *selectorSet
|
||||
rules *ruleSet
|
||||
|
||||
legacy bool
|
||||
}
|
||||
|
||||
func newNS(name string, ipt *iptables.IPTables, ips ipset.Interface, nsSelectors *selectorSet) (*ns, error) {
|
||||
allPods, err := newSelectorSpec(&unversioned.LabelSelector{}, name, ipset.HashIP)
|
||||
func newNS(name, nodeName string, legacy bool, ipt iptables.Interface, ips ipset.Interface, nsSelectors *selectorSet) (*ns, error) {
|
||||
allPods, err := newSelectorSpec(&metav1.LabelSelector{}, false, name, ipset.HashIP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -41,14 +54,24 @@ func newNS(name string, ipt *iptables.IPTables, ips ipset.Interface, nsSelectors
|
||||
ipt: ipt,
|
||||
ips: ips,
|
||||
name: name,
|
||||
nodeName: nodeName,
|
||||
pods: make(map[types.UID]*coreapi.Pod),
|
||||
policies: make(map[types.UID]*extnapi.NetworkPolicy),
|
||||
policies: make(map[types.UID]interface{}),
|
||||
uid: uuid.NewUUID(),
|
||||
allPods: allPods,
|
||||
nsSelectors: nsSelectors,
|
||||
rules: newRuleSet(ipt)}
|
||||
rules: newRuleSet(ipt),
|
||||
legacy: legacy}
|
||||
|
||||
ns.podSelectors = newSelectorSet(ips, ns.onNewPodSelector)
|
||||
ns.podSelectors = newSelectorSet(ips, ns.onNewPodSelector, ns.onNewDstPodSelector, ns.onDestroyDstPodSelector)
|
||||
|
||||
if !legacy {
|
||||
defaultAllowIPSet := ipset.Name(IpsetNamePrefix + shortName("default-allow:"+name))
|
||||
if err := ips.Create(defaultAllowIPSet, ipset.HashIP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ns.defaultAllowIPSet = defaultAllowIPSet
|
||||
}
|
||||
|
||||
if err := ns.podSelectors.provision(ns.uid, nil, map[string]*selectorSpec{ns.allPods.key: ns.allPods}); err != nil {
|
||||
return nil, err
|
||||
@@ -62,25 +85,85 @@ func (ns *ns) empty() bool {
|
||||
}
|
||||
|
||||
func (ns *ns) destroy() error {
|
||||
if err := ns.podSelectors.deprovision(ns.uid, map[string]*selectorSpec{ns.allPods.key: ns.allPods}, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return ns.podSelectors.deprovision(ns.uid, map[string]*selectorSpec{ns.allPods.key: ns.allPods}, nil)
|
||||
}
|
||||
|
||||
func (ns *ns) onNewPodSelector(selector *selector) error {
|
||||
for _, pod := range ns.pods {
|
||||
if hasIP(pod) {
|
||||
if selector.matches(pod.ObjectMeta.Labels) {
|
||||
if err := selector.addEntry(pod.Status.PodIP); err != nil {
|
||||
if err := selector.addEntry(pod.Status.PodIP, podComment(pod)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) onNewDstPodSelector(selector *selector) error {
|
||||
if ns.legacy {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, pod := range ns.pods {
|
||||
if hasIP(pod) {
|
||||
// Remove the pod from default-allow if dst podselector matches the pod
|
||||
if selector.matches(pod.ObjectMeta.Labels) {
|
||||
if err := ns.ips.DelEntryIfExists(ns.defaultAllowIPSet, pod.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) onDestroyDstPodSelector(selector *selector) error {
|
||||
if ns.legacy {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, pod := range ns.pods {
|
||||
if hasIP(pod) {
|
||||
if selector.matches(pod.ObjectMeta.Labels) {
|
||||
if err := ns.addToDefaultAllowIfNoMatching(pod); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add pod IP addr to default-allow ipset if there are no matching dst selectors
|
||||
func (ns *ns) addToDefaultAllowIfNoMatching(pod *coreapi.Pod) error {
|
||||
found := false
|
||||
// TODO(mp) optimize (avoid iterating over selectors) by ref counting IP addrs.
|
||||
for _, s := range ns.podSelectors.entries {
|
||||
if ns.podSelectors.dstSelectorExist(s) && s.matches(pod.ObjectMeta.Labels) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
if err := ns.ips.AddEntryIfNotExist(ns.defaultAllowIPSet, pod.Status.PodIP, podComment(pod)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) checkLocalPod(obj *coreapi.Pod) bool {
|
||||
if obj.Spec.NodeName != ns.nodeName {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (ns *ns) addPod(obj *coreapi.Pod) error {
|
||||
ns.pods[obj.ObjectMeta.UID] = obj
|
||||
|
||||
@@ -88,7 +171,22 @@ func (ns *ns) addPod(obj *coreapi.Pod) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ns.podSelectors.addToMatching(obj.ObjectMeta.Labels, obj.Status.PodIP)
|
||||
if ns.checkLocalPod(obj) {
|
||||
ns.ips.AddEntry(LocalIpset, obj.Status.PodIP, podComment(obj))
|
||||
}
|
||||
|
||||
found, err := ns.podSelectors.addToMatching(obj.ObjectMeta.Labels, obj.Status.PodIP, podComment(obj))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If there are no matching dst selectors, add the pod to default-allow
|
||||
if !ns.legacy && !found {
|
||||
if err := ns.ips.AddEntryIfNotExist(ns.defaultAllowIPSet, obj.Status.PodIP, podComment(obj)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
@@ -100,11 +198,47 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
}
|
||||
|
||||
if hasIP(oldObj) && !hasIP(newObj) {
|
||||
if ns.checkLocalPod(oldObj) {
|
||||
ns.ips.DelEntry(LocalIpset, oldObj.Status.PodIP)
|
||||
}
|
||||
|
||||
if !ns.legacy {
|
||||
if err := ns.ips.DelEntryIfExists(ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return ns.podSelectors.delFromMatching(oldObj.ObjectMeta.Labels, oldObj.Status.PodIP)
|
||||
}
|
||||
|
||||
if !hasIP(oldObj) && hasIP(newObj) {
|
||||
return ns.podSelectors.addToMatching(newObj.ObjectMeta.Labels, newObj.Status.PodIP)
|
||||
if ns.checkLocalPod(newObj) {
|
||||
ns.ips.AddEntry(LocalIpset, newObj.Status.PodIP, podComment(newObj))
|
||||
}
|
||||
found, err := ns.podSelectors.addToMatching(newObj.ObjectMeta.Labels, newObj.Status.PodIP, podComment(newObj))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ns.legacy && !found {
|
||||
if err := ns.ips.AddEntryIfNotExist(ns.defaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if !ns.legacy &&
|
||||
oldObj.Status.PodIP != newObj.Status.PodIP &&
|
||||
ns.ips.Exist(ns.defaultAllowIPSet, oldObj.Status.PodIP) {
|
||||
|
||||
if err := ns.ips.DelEntryIfExists(ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.ips.AddEntryIfNotExist(ns.defaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !equals(oldObj.ObjectMeta.Labels, newObj.ObjectMeta.Labels) ||
|
||||
@@ -122,10 +256,23 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
}
|
||||
}
|
||||
if newMatch {
|
||||
if err := ps.addEntry(newObj.Status.PodIP); err != nil {
|
||||
if err := ps.addEntry(newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !ns.legacy && ns.podSelectors.dstSelectorExist(ps) {
|
||||
switch {
|
||||
case !oldMatch && newMatch:
|
||||
if err := ns.ips.DelEntryIfExists(ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
case oldMatch && !newMatch:
|
||||
if err := ns.addToDefaultAllowIfNoMatching(newObj); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,129 +286,150 @@ func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if ns.checkLocalPod(obj) {
|
||||
ns.ips.DelEntry(LocalIpset, obj.Status.PodIP)
|
||||
}
|
||||
|
||||
if !ns.legacy {
|
||||
if err := ns.ips.DelEntryIfExists(ns.defaultAllowIPSet, obj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return ns.podSelectors.delFromMatching(obj.ObjectMeta.Labels, obj.Status.PodIP)
|
||||
}
|
||||
|
||||
func (ns *ns) addNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
||||
ns.policies[obj.ObjectMeta.UID] = obj
|
||||
|
||||
func (ns *ns) addNetworkPolicy(obj interface{}) error {
|
||||
// Analyse policy, determine which rules and ipsets are required
|
||||
rules, nsSelectors, podSelectors, err := ns.analysePolicy(obj)
|
||||
|
||||
uid, rules, nsSelectors, podSelectors, err := ns.analyse(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
if err := ns.podSelectors.provision(obj.ObjectMeta.UID, nil, podSelectors); err != nil {
|
||||
if err := ns.podSelectors.provision(uid, nil, podSelectors); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.rules.provision(obj.ObjectMeta.UID, nil, rules); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return ns.rules.provision(uid, nil, rules)
|
||||
}
|
||||
|
||||
func (ns *ns) updateNetworkPolicy(oldObj, newObj *extnapi.NetworkPolicy) error {
|
||||
delete(ns.policies, oldObj.ObjectMeta.UID)
|
||||
ns.policies[newObj.ObjectMeta.UID] = newObj
|
||||
|
||||
func (ns *ns) updateNetworkPolicy(oldObj, newObj interface{}) error {
|
||||
// Analyse the old and the new policy so we can determine differences
|
||||
oldRules, oldNsSelectors, oldPodSelectors, err := ns.analysePolicy(oldObj)
|
||||
oldUID, oldRules, oldNsSelectors, oldPodSelectors, err := ns.analyse(oldObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newRules, newNsSelectors, newPodSelectors, err := ns.analysePolicy(newObj)
|
||||
newUID, newRules, newNsSelectors, newPodSelectors, err := ns.analyse(newObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delete(ns.policies, oldUID)
|
||||
ns.policies[newUID] = newObj
|
||||
|
||||
// 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
|
||||
}
|
||||
if err := ns.nsSelectors.deprovision(oldObj.ObjectMeta.UID, oldNsSelectors, newNsSelectors); err != nil {
|
||||
if err := ns.nsSelectors.deprovision(oldUID, oldNsSelectors, newNsSelectors); err != nil {
|
||||
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
|
||||
}
|
||||
if err := ns.nsSelectors.provision(oldObj.ObjectMeta.UID, oldNsSelectors, newNsSelectors); err != nil {
|
||||
if err := ns.nsSelectors.provision(oldUID, oldNsSelectors, newNsSelectors); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.podSelectors.provision(oldObj.ObjectMeta.UID, oldPodSelectors, newPodSelectors); err != nil {
|
||||
if err := ns.podSelectors.provision(oldUID, oldPodSelectors, newPodSelectors); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.rules.provision(oldObj.ObjectMeta.UID, oldRules, newRules); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return ns.rules.provision(oldUID, oldRules, newRules)
|
||||
}
|
||||
|
||||
func (ns *ns) deleteNetworkPolicy(obj *extnapi.NetworkPolicy) error {
|
||||
delete(ns.policies, obj.ObjectMeta.UID)
|
||||
|
||||
func (ns *ns) deleteNetworkPolicy(obj interface{}) error {
|
||||
// Analyse network policy to free resources
|
||||
rules, nsSelectors, podSelectors, err := ns.analysePolicy(obj)
|
||||
uid, rules, nsSelectors, podSelectors, err := ns.analyse(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delete(ns.policies, uid)
|
||||
|
||||
// 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
|
||||
}
|
||||
if err := ns.nsSelectors.deprovision(obj.ObjectMeta.UID, nsSelectors, nil); err != nil {
|
||||
if err := ns.nsSelectors.deprovision(uid, nsSelectors, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.podSelectors.deprovision(obj.ObjectMeta.UID, podSelectors, nil); err != nil {
|
||||
return err
|
||||
return ns.podSelectors.deprovision(uid, podSelectors, nil)
|
||||
}
|
||||
|
||||
func bypassRule(nsIpsetName ipset.Name, namespace string) []string {
|
||||
return []string{"-m", "set", "--match-set", string(nsIpsetName), "dst", "-j", "ACCEPT", "-m", "comment", "--comment", "DefaultAllow isolation for namespace: " + namespace}
|
||||
}
|
||||
|
||||
func (ns *ns) ensureBypassRule() error {
|
||||
var ipset ipset.Name
|
||||
if ns.legacy {
|
||||
ipset = ns.allPods.ipsetName
|
||||
} else {
|
||||
ipset = ns.defaultAllowIPSet
|
||||
}
|
||||
|
||||
return nil
|
||||
common.Log.Debugf("ensuring rule for DefaultAllow in namespace: %s, set %s", ns.name, ipset)
|
||||
return ns.ipt.Append(TableFilter, DefaultChain, bypassRule(ipset, ns.name)...)
|
||||
}
|
||||
|
||||
func bypassRule(nsIpsetName ipset.Name) []string {
|
||||
return []string{"-m", "set", "--match-set", string(nsIpsetName), "dst", "-j", "ACCEPT"}
|
||||
}
|
||||
func (ns *ns) deleteBypassRule() error {
|
||||
var ipset ipset.Name
|
||||
if ns.legacy {
|
||||
ipset = ns.allPods.ipsetName
|
||||
} else {
|
||||
ipset = ns.defaultAllowIPSet
|
||||
}
|
||||
|
||||
func (ns *ns) ensureBypassRule(nsIpsetName ipset.Name) error {
|
||||
return ns.ipt.Append(TableFilter, DefaultChain, bypassRule(ns.allPods.ipsetName)...)
|
||||
}
|
||||
|
||||
func (ns *ns) deleteBypassRule(nsIpsetName ipset.Name) error {
|
||||
return ns.ipt.Delete(TableFilter, DefaultChain, bypassRule(ns.allPods.ipsetName)...)
|
||||
common.Log.Debugf("removing default rule in namespace: %s, set %s", ns.name, ipset)
|
||||
return ns.ipt.Delete(TableFilter, DefaultChain, bypassRule(ipset, ns.name)...)
|
||||
}
|
||||
|
||||
func (ns *ns) addNamespace(obj *coreapi.Namespace) error {
|
||||
ns.namespace = obj
|
||||
|
||||
// Insert a rule to bypass policies if namespace is DefaultAllow
|
||||
if !isDefaultDeny(obj) {
|
||||
return ns.ensureBypassRule(ns.allPods.ipsetName)
|
||||
if !ns.isDefaultDeny(obj) {
|
||||
if err := ns.ensureBypassRule(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Add namespace ipset to matching namespace selectors
|
||||
return ns.nsSelectors.addToMatching(obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
||||
_, err := ns.nsSelectors.addToMatching(obj.ObjectMeta.Labels, string(ns.allPods.ipsetName), namespaceComment(ns))
|
||||
return err
|
||||
}
|
||||
|
||||
func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
||||
ns.namespace = newObj
|
||||
|
||||
// Update bypass rule if ingress default has changed
|
||||
oldDefaultDeny := isDefaultDeny(oldObj)
|
||||
newDefaultDeny := isDefaultDeny(newObj)
|
||||
oldDefaultDeny := ns.isDefaultDeny(oldObj)
|
||||
newDefaultDeny := ns.isDefaultDeny(newObj)
|
||||
|
||||
if oldDefaultDeny != newDefaultDeny {
|
||||
common.Log.Infof("namespace DefaultDeny changed from %t to %t", oldDefaultDeny, newDefaultDeny)
|
||||
if oldDefaultDeny {
|
||||
return ns.ensureBypassRule(ns.allPods.ipsetName)
|
||||
if err := ns.ensureBypassRule(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if newDefaultDeny {
|
||||
return ns.deleteBypassRule(ns.allPods.ipsetName)
|
||||
if err := ns.deleteBypassRule(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +447,7 @@ func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
||||
}
|
||||
}
|
||||
if newMatch {
|
||||
if err := selector.addEntry(string(ns.allPods.ipsetName)); err != nil {
|
||||
if err := selector.addEntry(string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -293,14 +461,39 @@ func (ns *ns) deleteNamespace(obj *coreapi.Namespace) error {
|
||||
ns.namespace = nil
|
||||
|
||||
// Remove bypass rule
|
||||
if !isDefaultDeny(obj) {
|
||||
return ns.deleteBypassRule(ns.allPods.ipsetName)
|
||||
if !ns.isDefaultDeny(obj) {
|
||||
if err := ns.deleteBypassRule(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Remove namespace ipset from any matching namespace selectors
|
||||
return ns.nsSelectors.delFromMatching(obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
||||
}
|
||||
|
||||
func (ns *ns) isDefaultDeny(namespace *coreapi.Namespace) bool {
|
||||
nnpJSON, found := namespace.ObjectMeta.Annotations["net.beta.kubernetes.io/network-policy"]
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
|
||||
if !ns.legacy {
|
||||
common.Log.Warn("DefaultDeny annotation is supported only in legacy mode (--use-legacy-netpol)")
|
||||
return false
|
||||
}
|
||||
|
||||
var nnp NamespaceNetworkPolicy
|
||||
if err := json.Unmarshal([]byte(nnpJSON), &nnp); err != nil {
|
||||
common.Log.Warn("Ignoring network policy annotation: unmarshal failed:", err)
|
||||
// If we can't understand the annotation, behave as if it isn't present
|
||||
return false
|
||||
}
|
||||
|
||||
return nnp.Ingress != nil &&
|
||||
nnp.Ingress.Isolation != nil &&
|
||||
*(nnp.Ingress.Isolation) == DefaultDeny
|
||||
}
|
||||
|
||||
func hasIP(pod *coreapi.Pod) bool {
|
||||
// Ensure pod isn't dead, has an IP address and isn't sharing the host network namespace
|
||||
return pod.Status.Phase != "Succeeded" && pod.Status.Phase != "Failed" &&
|
||||
@@ -319,20 +512,43 @@ func equals(a, b map[string]string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func isDefaultDeny(namespace *coreapi.Namespace) bool {
|
||||
nnpJSON, found := namespace.ObjectMeta.Annotations["net.beta.kubernetes.io/network-policy"]
|
||||
if !found {
|
||||
return false
|
||||
func namespaceComment(namespace *ns) string {
|
||||
return "namespace: " + namespace.name
|
||||
}
|
||||
|
||||
func podComment(pod *coreapi.Pod) string {
|
||||
return fmt.Sprintf("namespace: %s, pod: %s", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name)
|
||||
}
|
||||
|
||||
func (ns *ns) analyse(obj interface{}) (
|
||||
uid types.UID,
|
||||
rules map[string]*ruleSpec,
|
||||
nsSelectors, podSelectors map[string]*selectorSpec,
|
||||
err error) {
|
||||
|
||||
switch p := obj.(type) {
|
||||
case *extnapi.NetworkPolicy:
|
||||
uid = p.ObjectMeta.UID
|
||||
case *networkingv1.NetworkPolicy:
|
||||
uid = p.ObjectMeta.UID
|
||||
default:
|
||||
err = errInvalidNetworkPolicyObjType
|
||||
return
|
||||
}
|
||||
ns.policies[uid] = obj
|
||||
|
||||
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
|
||||
// Analyse policy, determine which rules and ipsets are required
|
||||
if ns.legacy {
|
||||
rules, nsSelectors, podSelectors, err = ns.analysePolicyLegacy(obj.(*extnapi.NetworkPolicy))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
rules, nsSelectors, podSelectors, err = ns.analysePolicy(obj.(*networkingv1.NetworkPolicy))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nnp.Ingress != nil &&
|
||||
nnp.Ingress.Isolation != nil &&
|
||||
*(nnp.Ingress.Isolation) == DefaultDeny
|
||||
return
|
||||
}
|
||||
+14
-4
@@ -1,12 +1,13 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"k8s.io/client-go/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/npc/iptables"
|
||||
)
|
||||
|
||||
type ruleSpec struct {
|
||||
@@ -19,27 +20,36 @@ func newRuleSpec(proto *string, srcHost *selectorSpec, dstHost *selectorSpec, ds
|
||||
if proto != nil {
|
||||
args = append(args, "-p", *proto)
|
||||
}
|
||||
srcComment := "anywhere"
|
||||
if srcHost != nil {
|
||||
args = append(args, "-m", "set", "--match-set", string(srcHost.ipsetName), "src")
|
||||
if srcHost.nsName != "" {
|
||||
srcComment = fmt.Sprintf("pods: namespace: %s, selector: %s", srcHost.nsName, srcHost.key)
|
||||
} else {
|
||||
srcComment = fmt.Sprintf("namespaces: selector: %s", srcHost.key)
|
||||
}
|
||||
}
|
||||
dstComment := "anywhere"
|
||||
if dstHost != nil {
|
||||
args = append(args, "-m", "set", "--match-set", string(dstHost.ipsetName), "dst")
|
||||
dstComment = fmt.Sprintf("pods: namespace: %s, selector: %s", dstHost.nsName, dstHost.key)
|
||||
}
|
||||
if dstPort != nil {
|
||||
args = append(args, "--dport", *dstPort)
|
||||
}
|
||||
args = append(args, "-j", "ACCEPT")
|
||||
args = append(args, "-m", "comment", "--comment", fmt.Sprintf("%s -> %s", srcComment, dstComment))
|
||||
key := strings.Join(args, " ")
|
||||
|
||||
return &ruleSpec{key, args}
|
||||
}
|
||||
|
||||
type ruleSet struct {
|
||||
ipt *iptables.IPTables
|
||||
ipt iptables.Interface
|
||||
users map[string]map[types.UID]struct{}
|
||||
}
|
||||
|
||||
func newRuleSet(ipt *iptables.IPTables) *ruleSet {
|
||||
func newRuleSet(ipt iptables.Interface) *ruleSet {
|
||||
return &ruleSet{ipt, make(map[string]map[types.UID]struct{})}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRegressionHandleNil3095(t *testing.T) {
|
||||
// Test for handling nil values
|
||||
// https://github.com/weaveworks/weave/issues/3095
|
||||
|
||||
rs := newRuleSpec(nil, nil, nil, nil)
|
||||
|
||||
require.NotEqual(t, rs.key, "")
|
||||
|
||||
}
|
||||
+67
-23
@@ -1,9 +1,9 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
"k8s.io/client-go/pkg/api/unversioned"
|
||||
"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/npc/ipset"
|
||||
@@ -12,13 +12,15 @@ import (
|
||||
type selectorSpec struct {
|
||||
key string // string representation (for hash keying/equality comparison)
|
||||
selector labels.Selector // k8s Selector object (for matching)
|
||||
dst bool // destination selector
|
||||
|
||||
ipsetType ipset.Type // type of ipset to provision
|
||||
ipsetName ipset.Name // generated ipset name
|
||||
nsName string // Namespace name
|
||||
}
|
||||
|
||||
func newSelectorSpec(json *unversioned.LabelSelector, nsName string, ipsetType ipset.Type) (*selectorSpec, error) {
|
||||
selector, err := unversioned.LabelSelectorAsSelector(json)
|
||||
func newSelectorSpec(json *metav1.LabelSelector, dst bool, nsName string, ipsetType ipset.Type) (*selectorSpec, error) {
|
||||
selector, err := metav1.LabelSelectorAsSelector(json)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -26,11 +28,13 @@ func newSelectorSpec(json *unversioned.LabelSelector, nsName string, ipsetType i
|
||||
return &selectorSpec{
|
||||
key: key,
|
||||
selector: selector,
|
||||
dst: dst,
|
||||
// We prefix the selector string with the namespace name when generating
|
||||
// the shortname because you can specify the same selector in multiple
|
||||
// namespaces - we need those to map to distinct ipsets
|
||||
ipsetName: ipset.Name("weave-" + shortName(nsName+":"+key)),
|
||||
ipsetType: ipsetType}, nil
|
||||
ipsetName: ipset.Name(IpsetNamePrefix + shortName(nsName+":"+key)),
|
||||
ipsetType: ipsetType,
|
||||
nsName: nsName}, nil
|
||||
}
|
||||
|
||||
type selector struct {
|
||||
@@ -42,13 +46,11 @@ func (s *selector) matches(labelMap map[string]string) bool {
|
||||
return s.spec.selector.Matches(labels.Set(labelMap))
|
||||
}
|
||||
|
||||
func (s *selector) addEntry(entry string) error {
|
||||
common.Log.Infof("adding entry %s to %s", entry, s.spec.ipsetName)
|
||||
return s.ips.AddEntry(s.spec.ipsetName, entry)
|
||||
func (s *selector) addEntry(entry string, comment string) error {
|
||||
return s.ips.AddEntry(s.spec.ipsetName, entry, comment)
|
||||
}
|
||||
|
||||
func (s *selector) delEntry(entry string) error {
|
||||
common.Log.Infof("deleting entry %s from %s", entry, s.spec.ipsetName)
|
||||
return s.ips.DelEntry(s.spec.ipsetName, entry)
|
||||
}
|
||||
|
||||
@@ -57,27 +59,45 @@ type selectorFn func(selector *selector) error
|
||||
type selectorSet struct {
|
||||
ips ipset.Interface
|
||||
onNewSelector selectorFn
|
||||
users map[string]map[types.UID]struct{} // list of users per selector
|
||||
entries map[string]*selector
|
||||
|
||||
// invoked after dst selector has been provisioned for the first time
|
||||
onNewDstSelector selectorFn
|
||||
// invoked after the last instance of dst selector has been deprovisioned
|
||||
onDestroyDstSelector selectorFn
|
||||
|
||||
users map[string]map[types.UID]struct{} // list of users per selector
|
||||
entries map[string]*selector
|
||||
|
||||
// We need to keep track of dst selector instances to be able to invoke
|
||||
// onNewDstSelector and onDestroyDstSelector callbacks at the right time;
|
||||
// selectorSpec.Key -> count
|
||||
dstSelectorsCount map[string]int
|
||||
}
|
||||
|
||||
func newSelectorSet(ips ipset.Interface, onNewSelector selectorFn) *selectorSet {
|
||||
func newSelectorSet(ips ipset.Interface, onNewSelector, onNewDstSelector selectorFn, onDestroyDstSelector selectorFn) *selectorSet {
|
||||
return &selectorSet{
|
||||
ips: ips,
|
||||
onNewSelector: onNewSelector,
|
||||
users: make(map[string]map[types.UID]struct{}),
|
||||
entries: make(map[string]*selector)}
|
||||
ips: ips,
|
||||
onNewSelector: onNewSelector,
|
||||
onNewDstSelector: onNewDstSelector,
|
||||
onDestroyDstSelector: onDestroyDstSelector,
|
||||
users: make(map[string]map[types.UID]struct{}),
|
||||
entries: make(map[string]*selector),
|
||||
dstSelectorsCount: make(map[string]int)}
|
||||
}
|
||||
|
||||
func (ss *selectorSet) addToMatching(labelMap map[string]string, entry string) error {
|
||||
func (ss *selectorSet) addToMatching(labelMap map[string]string, entry string, comment string) (bool, error) {
|
||||
found := false
|
||||
for _, s := range ss.entries {
|
||||
if s.matches(labelMap) {
|
||||
if err := s.addEntry(entry); err != nil {
|
||||
return err
|
||||
if ss.dstSelectorExist(s) {
|
||||
found = true
|
||||
}
|
||||
if err := s.addEntry(entry, comment); err != nil {
|
||||
return found, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return found, nil
|
||||
}
|
||||
|
||||
func (ss *selectorSet) delFromMatching(labelMap map[string]string, entry string) error {
|
||||
@@ -91,6 +111,10 @@ func (ss *selectorSet) delFromMatching(labelMap map[string]string, entry string)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *selectorSet) dstSelectorExist(s *selector) bool {
|
||||
return ss.dstSelectorsCount[s.spec.key] > 0
|
||||
}
|
||||
|
||||
func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*selectorSpec) error {
|
||||
for key, spec := range current {
|
||||
if _, found := desired[key]; !found {
|
||||
@@ -100,9 +124,19 @@ func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*
|
||||
if err := ss.ips.Destroy(spec.ipsetName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delete(ss.entries, key)
|
||||
delete(ss.users, key)
|
||||
}
|
||||
|
||||
if spec.dst {
|
||||
ss.dstSelectorsCount[key]--
|
||||
if ss.dstSelectorsCount[key] == 0 {
|
||||
if err := ss.onDestroyDstSelector(&selector{ss.ips, spec}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -111,12 +145,13 @@ func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*
|
||||
func (ss *selectorSet) provision(user types.UID, current, desired map[string]*selectorSpec) error {
|
||||
for key, spec := range desired {
|
||||
if _, found := current[key]; !found {
|
||||
selector := &selector{ss.ips, spec}
|
||||
|
||||
if _, found := ss.users[key]; !found {
|
||||
common.Log.Infof("creating ipset: %#v", spec)
|
||||
if err := ss.ips.Create(spec.ipsetName, spec.ipsetType); err != nil {
|
||||
return err
|
||||
}
|
||||
selector := &selector{ss.ips, spec}
|
||||
if err := ss.onNewSelector(selector); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -124,6 +159,15 @@ func (ss *selectorSet) provision(user types.UID, current, desired map[string]*se
|
||||
ss.entries[key] = selector
|
||||
}
|
||||
ss.users[key][user] = struct{}{}
|
||||
|
||||
if spec.dst {
|
||||
ss.dstSelectorsCount[key]++
|
||||
if ss.dstSelectorsCount[key] == 1 {
|
||||
if err := ss.onNewDstSelector(selector); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
+3
-2
@@ -7,12 +7,13 @@ import (
|
||||
|
||||
const (
|
||||
// This array:
|
||||
// * Must include only printable UTF8 characters that are represented with a single byte
|
||||
// * Must include only printable UTF8 characters that are represented with a single byte (http://www.utf8-chartable.de/)
|
||||
// * Must be at least of length 85 (`len("weave-") + l(2^160)/l(85)` equals 31, the maximum ipset name length)
|
||||
// * Must not include commas as those are treated specially by `ipset add` when adding a named set to a list:set
|
||||
// * Should not include space for readability
|
||||
// * Should not include invalid chars for XML `<>&"'`
|
||||
// * Should not include single quote or backslash to be nice to shell users
|
||||
ShortNameSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789(){}[]<>_$%^&*|/?.;:@#~"
|
||||
ShortNameSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789(){}[]+=_$%^!*|/?.;:@#~"
|
||||
)
|
||||
|
||||
// sha1 hash an arbitrary string and represent it using the full range of
|
||||
|
||||
+20
-14
@@ -5,19 +5,24 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/appc/cni/pkg/skel"
|
||||
"github.com/appc/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
)
|
||||
|
||||
func (i *Ipam) CmdAdd(args *skel.CmdArgs) error {
|
||||
var conf types.NetConf
|
||||
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||
return fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
result, err := i.Allocate(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return result.Print()
|
||||
return types.PrintResult(result, conf.CNIVersion)
|
||||
}
|
||||
|
||||
func (i *Ipam) Allocate(args *skel.CmdArgs) (*types.Result, error) {
|
||||
func (i *Ipam) Allocate(args *skel.CmdArgs) (types.Result, error) {
|
||||
// extract the things we care about
|
||||
conf, err := loadIPAMConf(args.StdinData)
|
||||
if err != nil {
|
||||
@@ -33,25 +38,26 @@ func (i *Ipam) Allocate(args *skel.CmdArgs) (*types.Result, error) {
|
||||
var ipnet *net.IPNet
|
||||
|
||||
if conf.Subnet == "" {
|
||||
ipnet, err = i.weave.AllocateIP(containerID)
|
||||
ipnet, err = i.weave.AllocateIP(containerID, false)
|
||||
} else {
|
||||
var subnet *net.IPNet
|
||||
subnet, err = types.ParseCIDR(conf.Subnet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("subnet given in config, but not parseable: %s", err)
|
||||
}
|
||||
ipnet, err = i.weave.AllocateIPInSubnet(containerID, subnet)
|
||||
ipnet, err = i.weave.AllocateIPInSubnet(containerID, subnet, false)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &types.Result{
|
||||
IP4: &types.IPConfig{
|
||||
IP: *ipnet,
|
||||
result := ¤t.Result{
|
||||
IPs: []*current.IPConfig{{
|
||||
Version: "4",
|
||||
Address: *ipnet,
|
||||
Gateway: conf.Gateway,
|
||||
Routes: conf.Routes,
|
||||
},
|
||||
}},
|
||||
Routes: conf.Routes,
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -65,9 +71,9 @@ func (i *Ipam) Release(args *skel.CmdArgs) error {
|
||||
}
|
||||
|
||||
type ipamConf struct {
|
||||
Subnet string `json:"subnet,omitempty"`
|
||||
Gateway net.IP `json:"gateway,omitempty"`
|
||||
Routes []types.Route `json:"routes"`
|
||||
Subnet string `json:"subnet,omitempty"`
|
||||
Gateway net.IP `json:"gateway,omitempty"`
|
||||
Routes []*types.Route `json:"routes"`
|
||||
}
|
||||
|
||||
type netConf struct {
|
||||
|
||||
@@ -19,6 +19,11 @@ func NewIpam(weave *api.Client) *Ipam {
|
||||
return &Ipam{weave: weave}
|
||||
}
|
||||
|
||||
func (i *Ipam) GetCapabilities() (RequiresMACAddress bool, RequiresRequestReplay bool, err error) {
|
||||
i.logReq("GetCapabilities")
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
func (i *Ipam) GetDefaultAddressSpaces() (string, string, error) {
|
||||
i.logReq("GetDefaultAddressSpaces")
|
||||
return "weavelocal", "weaveglobal", nil
|
||||
@@ -73,7 +78,7 @@ func (i *Ipam) RequestAddress(poolID string, address net.IP, options map[string]
|
||||
i.logReq("RequestAddress", poolID, address, options)
|
||||
defer func() { i.logRes("RequestAddress", err, ip) }()
|
||||
if poolID == "weavepool" { // old-style
|
||||
ip, err = i.weave.AllocateIP(api.NoContainerID)
|
||||
ip, err = i.weave.AllocateIP(api.NoContainerID, false)
|
||||
return
|
||||
}
|
||||
subnet, iprange, err := splitPoolID(poolID)
|
||||
@@ -82,12 +87,12 @@ func (i *Ipam) RequestAddress(poolID string, address net.IP, options map[string]
|
||||
}
|
||||
if address != nil { // try to claim specific address requested
|
||||
ip = &net.IPNet{IP: address, Mask: subnet.Mask}
|
||||
if err = i.weave.ClaimIP(api.NoContainerID, ip); err != nil {
|
||||
if err = i.weave.ClaimIP(api.NoContainerID, ip, false); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// We are lying slightly to IPAM here: the range is not a subnet
|
||||
if ip, err = i.weave.AllocateIPInSubnet(api.NoContainerID, iprange); err != nil {
|
||||
if ip, err = i.weave.AllocateIPInSubnet(api.NoContainerID, iprange, false); err != nil {
|
||||
return
|
||||
}
|
||||
ip.Mask = subnet.Mask // fix up the subnet we lied about
|
||||
|
||||
+62
-46
@@ -5,11 +5,12 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
"os"
|
||||
|
||||
"github.com/appc/cni/pkg/ipam"
|
||||
"github.com/appc/cni/pkg/skel"
|
||||
"github.com/appc/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/ipam"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
weaveapi "github.com/weaveworks/weave/api"
|
||||
@@ -32,7 +33,8 @@ func NewCNIPlugin(weave *weaveapi.Client) *CNIPlugin {
|
||||
|
||||
func loadNetConf(bytes []byte) (*NetConf, error) {
|
||||
n := &NetConf{
|
||||
BrName: weavenet.WeaveBridgeName,
|
||||
BrName: weavenet.WeaveBridgeName,
|
||||
HairpinMode: true,
|
||||
}
|
||||
if err := json.Unmarshal(bytes, n); err != nil {
|
||||
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
||||
@@ -40,17 +42,26 @@ func loadNetConf(bytes []byte) (*NetConf, error) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c *CNIPlugin) getIP(ipamType string, args *skel.CmdArgs) (result *types.Result, err error) {
|
||||
func (c *CNIPlugin) getIP(ipamType string, args *skel.CmdArgs) (newResult *current.Result, err error) {
|
||||
var result types.Result
|
||||
// Default IPAM is Weave's own
|
||||
if ipamType == "" {
|
||||
result, err = ipamplugin.NewIpam(c.weave).Allocate(args)
|
||||
} else {
|
||||
result, err = ipam.ExecAdd(ipamType, args.StdinData)
|
||||
}
|
||||
if err == nil && result.IP4 == nil {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result == nil {
|
||||
return nil, fmt.Errorf("Received no usable result from IPAM plugin")
|
||||
}
|
||||
newResult, err = current.NewResultFromResult(result)
|
||||
// Check if ipam returned no results without error
|
||||
if err == nil && len(newResult.IPs) == 0 {
|
||||
return nil, fmt.Errorf("IPAM plugin failed to allocate IP address")
|
||||
}
|
||||
return result, err
|
||||
return newResult, err
|
||||
}
|
||||
|
||||
func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
||||
@@ -70,28 +81,34 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to allocate IP address: %s", err)
|
||||
}
|
||||
// Only expecting one address
|
||||
ip := result.IPs[0]
|
||||
|
||||
// If config says nothing about routes or gateway, default one will be via the bridge
|
||||
if result.IP4.Routes == nil && result.IP4.Gateway == nil {
|
||||
bridgeIP, err := weavenet.FindBridgeIP(conf.BrName, &result.IP4.IP)
|
||||
if len(result.Routes) == 0 && ip.Gateway == nil {
|
||||
bridgeIP, err := weavenet.FindBridgeIP(conf.BrName, &ip.Address)
|
||||
if err == weavenet.ErrBridgeNoIP {
|
||||
bridgeArgs := *args
|
||||
bridgeArgs.ContainerID = "weave:expose"
|
||||
// It would be better if libcni let us send just the desired parameters,
|
||||
// but there is a bug: https://github.com/containernetworking/cni/issues/410
|
||||
// so just blank out the one we want to change
|
||||
os.Setenv("CNI_CONTAINERID", bridgeArgs.ContainerID)
|
||||
bridgeIPResult, err := c.getIP(conf.IPAM.Type, &bridgeArgs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to allocate IP address for bridge: %s", err)
|
||||
}
|
||||
if err := assignBridgeIP(conf.BrName, bridgeIPResult.IP4.IP); err != nil {
|
||||
return fmt.Errorf("unable to assign IP address to bridge: %s", err)
|
||||
bridgeCIDR := bridgeIPResult.IPs[0].Address
|
||||
|
||||
if err := c.weave.Expose(&bridgeCIDR); err != nil {
|
||||
return fmt.Errorf("unable to expose bridge %q: %s", conf.BrName, err)
|
||||
}
|
||||
if err := weavenet.ExposeNAT(bridgeIPResult.IP4.IP); err != nil {
|
||||
return fmt.Errorf("unable to create NAT rules: %s", err)
|
||||
}
|
||||
bridgeIP = bridgeIPResult.IP4.IP.IP
|
||||
|
||||
bridgeIP = bridgeCIDR.IP
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
result.IP4.Gateway = bridgeIP
|
||||
result.IPs[0].Gateway = bridgeIP
|
||||
}
|
||||
|
||||
ns, err := netns.GetFromPath(args.Netns)
|
||||
@@ -110,20 +127,20 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
||||
id = fmt.Sprintf("%x", data)
|
||||
}
|
||||
|
||||
if err := weavenet.AttachContainer(args.Netns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&result.IP4.IP}, false); err != nil {
|
||||
if err := weavenet.AttachContainer(args.Netns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&ip.Address}, false, conf.HairpinMode); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := weavenet.WithNetNSLinkUnsafe(ns, args.IfName, func(link netlink.Link) error {
|
||||
return setupRoutes(link, args.IfName, result.IP4.IP, result.IP4.Gateway, result.IP4.Routes)
|
||||
return setupRoutes(link, args.IfName, ip.Address, ip.Gateway, result.Routes)
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error setting up routes: %s", err)
|
||||
}
|
||||
|
||||
result.DNS = conf.DNS
|
||||
return result.Print()
|
||||
return types.PrintResult(result, conf.CNIVersion)
|
||||
}
|
||||
|
||||
func setupRoutes(link netlink.Link, name string, ipnet net.IPNet, gw net.IP, routes []types.Route) error {
|
||||
func setupRoutes(link netlink.Link, name string, ipnet net.IPNet, gw net.IP, routes []*types.Route) error {
|
||||
var err error
|
||||
if routes == nil { // If config says nothing about routes, add a default one
|
||||
if !ipnet.Contains(gw) {
|
||||
@@ -133,7 +150,7 @@ func setupRoutes(link netlink.Link, name string, ipnet net.IPNet, gw net.IP, rou
|
||||
return err
|
||||
}
|
||||
}
|
||||
routes = []types.Route{{Dst: zeroNetwork}}
|
||||
routes = []*types.Route{{Dst: zeroNetwork}}
|
||||
}
|
||||
for _, r := range routes {
|
||||
if r.GW != nil {
|
||||
@@ -148,30 +165,22 @@ func setupRoutes(link netlink.Link, name string, ipnet net.IPNet, gw net.IP, rou
|
||||
return nil
|
||||
}
|
||||
|
||||
func assignBridgeIP(bridgeName string, ipnet net.IPNet) error {
|
||||
link, err := netlink.LinkByName(bridgeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.AddrAdd(link, &netlink.Addr{IPNet: &ipnet}); err != nil {
|
||||
// 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:
|
||||
// "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.
|
||||
func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
|
||||
conf, err := loadNetConf(args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
logOnStderr(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err = weavenet.WithNetNS(args.Netns, "del-iface", args.IfName); err != nil {
|
||||
return fmt.Errorf("error removing interface %q: %s", args.IfName, err)
|
||||
// As of CNI 0.3 spec, runtimes can send blank if they just want the address deallocated
|
||||
if args.Netns != "" {
|
||||
if _, err = weavenet.WithNetNS(args.Netns, "del-iface", args.IfName); err != nil {
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
|
||||
// Default IPAM is Weave's own
|
||||
@@ -181,15 +190,22 @@ func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
|
||||
err = ipam.ExecDel(conf.IPAM.Type, args.StdinData)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to release IP address: %s", err)
|
||||
logOnStderr(fmt.Errorf("unable to release IP address: %s", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Standard error is inherited from the caller (e.g. Kubelet, when running Weave Net's CNI plugin
|
||||
// under Kubernetes) hence provided errors should eventually appear in the caller's log files.
|
||||
func logOnStderr(err error) {
|
||||
fmt.Fprintln(os.Stderr, "weave-cni:", err)
|
||||
}
|
||||
|
||||
type NetConf struct {
|
||||
types.NetConf
|
||||
BrName string `json:"bridge"`
|
||||
IsGW bool `json:"isGateway"`
|
||||
IPMasq bool `json:"ipMasq"`
|
||||
MTU int `json:"mtu"`
|
||||
BrName string `json:"bridge"`
|
||||
IsGW bool `json:"isGateway"`
|
||||
IPMasq bool `json:"ipMasq"`
|
||||
MTU int `json:"mtu"`
|
||||
HairpinMode bool `json:"hairpinMode"`
|
||||
}
|
||||
+60
-12
@@ -28,25 +28,40 @@ type network struct {
|
||||
}
|
||||
|
||||
type driver struct {
|
||||
name string
|
||||
scope string
|
||||
docker *docker.Client
|
||||
sync.RWMutex
|
||||
networks map[string]network
|
||||
name string
|
||||
scope string
|
||||
// Docker API is not available for plugin-v2
|
||||
docker *docker.Client
|
||||
dns bool
|
||||
isPluginV2 bool
|
||||
// Enable multicast regardless whether multicast opt is passed to Docker;
|
||||
// used only by plugin-v2
|
||||
forceMulticast bool
|
||||
networks map[string]network
|
||||
}
|
||||
|
||||
func New(client *docker.Client, weave *weaveapi.Client, name, scope string) (skel.Driver, error) {
|
||||
func New(client *docker.Client, weave *weaveapi.Client, name, scope string, dns, isPluginV2, forceMulticast bool) (skel.Driver, error) {
|
||||
driver := &driver{
|
||||
name: name,
|
||||
scope: scope,
|
||||
docker: client,
|
||||
networks: make(map[string]network),
|
||||
name: name,
|
||||
scope: scope,
|
||||
docker: client,
|
||||
dns: dns,
|
||||
isPluginV2: isPluginV2,
|
||||
// make sure that it's used only by plugin-v2
|
||||
forceMulticast: isPluginV2 && forceMulticast,
|
||||
networks: make(map[string]network),
|
||||
}
|
||||
|
||||
_, err := NewWatcher(client, weave, driver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Do not start watcher in the case of plugin v2, which prevents us from
|
||||
// configuring arp settings of containers.
|
||||
if client != nil {
|
||||
_, err := NewWatcher(client, weave, driver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return driver, nil
|
||||
}
|
||||
|
||||
@@ -61,12 +76,27 @@ func (driver *driver) GetCapabilities() (*api.GetCapabilityResponse, error) {
|
||||
return caps, nil
|
||||
}
|
||||
|
||||
// In Swarm mode, CreateNetwork is called on each Swarm node when a new service
|
||||
// is created.
|
||||
func (driver *driver) CreateNetwork(create *api.CreateNetworkRequest) error {
|
||||
driver.logReq("CreateNetwork", create, create.NetworkID)
|
||||
_, err := driver.setupNetworkInfo(create.NetworkID, true, stringOptions(create))
|
||||
return err
|
||||
}
|
||||
|
||||
// NetworkAllocate is called on a Swarm node (master) which creates the network.
|
||||
// The returned options are passed to CreateNetwork.
|
||||
func (driver *driver) NetworkAllocate(alloc *api.AllocateNetworkRequest) (*api.AllocateNetworkResponse, error) {
|
||||
driver.logReq("NetworkAllocate", alloc, alloc.NetworkID)
|
||||
return &api.AllocateNetworkResponse{Options: alloc.Options}, nil
|
||||
}
|
||||
|
||||
// NetworkFree is called on a Swarm master node which created the network.
|
||||
func (driver *driver) NetworkFree(free *api.FreeNetworkRequest) (*api.FreeNetworkResponse, error) {
|
||||
driver.logReq("NetworkFree", free, free.NetworkID)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Deal with excessively-generic way the options get decoded from JSON
|
||||
func stringOptions(create *api.CreateNetworkRequest) map[string]string {
|
||||
if create.Options != nil {
|
||||
@@ -85,6 +115,7 @@ func stringOptions(create *api.CreateNetworkRequest) map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// In Swarm mode, DeleteNetwork is called after a service has been removed.
|
||||
func (driver *driver) DeleteNetwork(delreq *api.DeleteNetworkRequest) error {
|
||||
driver.logReq("DeleteNetwork", delreq, delreq.NetworkID)
|
||||
driver.Lock()
|
||||
@@ -163,12 +194,29 @@ func (driver *driver) JoinEndpoint(j *api.JoinRequest) (*api.JoinResponse, error
|
||||
}
|
||||
|
||||
func (driver *driver) findNetworkInfo(id string) (network, error) {
|
||||
var network network
|
||||
|
||||
// plugin-v2 does not have access to docker.sock, so we cannot call Docker
|
||||
// API for the network info.
|
||||
if driver.isPluginV2 {
|
||||
// safe to set, as isOurs used only by the watcher which is disabled for plugin-v2
|
||||
network.isOurs = true
|
||||
network.hasMulticastRoute = driver.forceMulticast
|
||||
|
||||
return network, nil
|
||||
}
|
||||
|
||||
driver.Lock()
|
||||
network, found := driver.networks[id]
|
||||
driver.Unlock()
|
||||
if found {
|
||||
return network, nil
|
||||
}
|
||||
|
||||
if driver.docker == nil {
|
||||
return network, fmt.Errorf("Docker client disabled; unable to get network info")
|
||||
}
|
||||
|
||||
info, err := driver.docker.NetworkInfo(id)
|
||||
if err != nil {
|
||||
return network, err
|
||||
|
||||
+12
-8
@@ -8,10 +8,6 @@ import (
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
|
||||
const (
|
||||
WeaveDomain = "weave.local"
|
||||
)
|
||||
|
||||
type watcher struct {
|
||||
client *docker.Client
|
||||
weave *weaveapi.Client
|
||||
@@ -41,11 +37,19 @@ func (w *watcher) ContainerStarted(id string) {
|
||||
continue
|
||||
}
|
||||
if network.isOurs {
|
||||
fqdn := fmt.Sprintf("%s.%s", info.Config.Hostname, info.Config.Domainname)
|
||||
if err := w.weave.RegisterWithDNS(id, fqdn, net.IPAddress); err != nil {
|
||||
w.driver.warn("ContainerStarted", "unable to register %s with weaveDNS: %s", id, err)
|
||||
if w.driver.dns {
|
||||
fqdn := fmt.Sprintf("%s.%s", info.Config.Hostname, info.Config.Domainname)
|
||||
if err := w.weave.RegisterWithDNS(id, fqdn, net.IPAddress); err != nil {
|
||||
w.driver.warn("ContainerStarted", "unable to register %s with weaveDNS: %s", id, err)
|
||||
}
|
||||
}
|
||||
if _, err := weavenet.WithNetNSByPid(info.State.Pid, "configure-arp", weavenet.VethName); err != nil {
|
||||
rootDir := "/"
|
||||
if w.driver.isPluginV2 {
|
||||
// We bind mount host's /proc to /host/proc for plugin-v2
|
||||
rootDir = "/host"
|
||||
}
|
||||
netNSPath := weavenet.NSPathByPidWithRoot(rootDir, info.State.Pid)
|
||||
if _, err := weavenet.WithNetNS(netNSPath, "configure-arp", weavenet.VethName, rootDir); err != nil {
|
||||
w.driver.warn("ContainerStarted", "unable to configure interfaces: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
weaveapi "github.com/weaveworks/weave/api"
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/common/docker"
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
ipamplugin "github.com/weaveworks/weave/plugin/ipam"
|
||||
netplugin "github.com/weaveworks/weave/plugin/net"
|
||||
"github.com/weaveworks/weave/plugin/skel"
|
||||
)
|
||||
|
||||
const (
|
||||
pluginV2Name = "net-plugin"
|
||||
defaultNetwork = "weave"
|
||||
MulticastOption = netplugin.MulticastOption
|
||||
)
|
||||
|
||||
var Log = common.Log
|
||||
|
||||
type Config struct {
|
||||
Socket string
|
||||
MeshSocket string
|
||||
Enable bool
|
||||
EnableV2 bool
|
||||
EnableV2Multicast bool
|
||||
DNS bool
|
||||
DefaultSubnet string
|
||||
}
|
||||
|
||||
type Plugin struct {
|
||||
Config
|
||||
}
|
||||
|
||||
func NewPlugin(config Config) *Plugin {
|
||||
if !config.Enable && !config.EnableV2 {
|
||||
return nil
|
||||
}
|
||||
plugin := &Plugin{Config: config}
|
||||
return plugin
|
||||
}
|
||||
|
||||
func (plugin *Plugin) Start(weaveAPIAddr string, dockerClient *docker.Client, ready func()) {
|
||||
weave := weaveapi.NewClient(weaveAPIAddr, Log)
|
||||
|
||||
Log.Info("Waiting for Weave API Server...")
|
||||
weave.WaitAPIServer(30)
|
||||
Log.Info("Finished waiting for Weave API Server")
|
||||
|
||||
if err := plugin.run(dockerClient, weave, ready); err != nil {
|
||||
Log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (plugin *Plugin) run(dockerClient *docker.Client, weave *weaveapi.Client, ready func()) error {
|
||||
endChan := make(chan error, 1)
|
||||
|
||||
if plugin.Socket != "" {
|
||||
globalListener, err := listenAndServe(dockerClient, weave, plugin.Socket, endChan, "global", false, plugin.DNS, plugin.EnableV2, plugin.EnableV2Multicast)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(plugin.Socket)
|
||||
defer globalListener.Close()
|
||||
}
|
||||
if plugin.MeshSocket != "" {
|
||||
meshListener, err := listenAndServe(dockerClient, weave, plugin.MeshSocket, endChan, "local", true, plugin.DNS, plugin.EnableV2, plugin.EnableV2Multicast)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(plugin.MeshSocket)
|
||||
defer meshListener.Close()
|
||||
if !plugin.EnableV2 {
|
||||
Log.Printf("Creating default %q network", defaultNetwork)
|
||||
options := map[string]interface{}{MulticastOption: "true"}
|
||||
dockerClient.EnsureNetwork(defaultNetwork, pluginNameFromAddress(plugin.MeshSocket), plugin.DefaultSubnet, options)
|
||||
}
|
||||
}
|
||||
ready()
|
||||
|
||||
return <-endChan
|
||||
}
|
||||
|
||||
func listenAndServe(dockerClient *docker.Client, weave *weaveapi.Client, address string, endChan chan<- error, scope string, withIpam, dns bool, isPluginV2, forceMulticast bool) (net.Listener, error) {
|
||||
var name string
|
||||
if isPluginV2 {
|
||||
name = pluginV2Name
|
||||
} else {
|
||||
name = pluginNameFromAddress(address)
|
||||
}
|
||||
|
||||
d, err := netplugin.New(dockerClient, weave, name, scope, dns, isPluginV2, forceMulticast)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var i ipamapi.Ipam
|
||||
if withIpam {
|
||||
i = ipamplugin.NewIpam(weave)
|
||||
}
|
||||
|
||||
listener, err := weavenet.ListenUnixSocket(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
Log.Printf("Listening on %s for %s scope", address, scope)
|
||||
|
||||
go func() {
|
||||
endChan <- skel.Listen(listener, d, i)
|
||||
}()
|
||||
|
||||
return listener, nil
|
||||
}
|
||||
|
||||
// Take a socket address like "/run/docker/plugins/weavemesh.sock" and extract the plugin name "weavemesh"
|
||||
func pluginNameFromAddress(address string) string {
|
||||
return strings.TrimSuffix(path.Base(address), ".sock")
|
||||
}
|
||||
+49
-6
@@ -26,11 +26,17 @@ type Driver interface {
|
||||
DeleteEndpoint(delete *api.DeleteEndpointRequest) error
|
||||
EndpointInfo(req *api.EndpointInfoRequest) (*api.EndpointInfoResponse, error)
|
||||
JoinEndpoint(j *api.JoinRequest) (response *api.JoinResponse, error error)
|
||||
NetworkAllocate(*api.AllocateNetworkRequest) (*api.AllocateNetworkResponse, error)
|
||||
NetworkFree(*api.FreeNetworkRequest) (*api.FreeNetworkResponse, error)
|
||||
LeaveEndpoint(leave *api.LeaveRequest) error
|
||||
DiscoverNew(discover *api.DiscoveryNotification) error
|
||||
DiscoverDelete(delete *api.DiscoveryNotification) error
|
||||
}
|
||||
|
||||
type IpamCapability interface {
|
||||
GetCapabilities() (RequiresMACAddress bool, RequiresRequestReplay bool, err error)
|
||||
}
|
||||
|
||||
type listener struct {
|
||||
d Driver
|
||||
i ipamapi.Ipam
|
||||
@@ -58,9 +64,12 @@ func Listen(socket net.Listener, driver Driver, ipamDriver ipamapi.Ipam) error {
|
||||
handleMethod(networkReceiver, "EndpointOperInfo", listener.infoEndpoint)
|
||||
handleMethod(networkReceiver, "Join", listener.joinEndpoint)
|
||||
handleMethod(networkReceiver, "Leave", listener.leaveEndpoint)
|
||||
handleMethod(networkReceiver, "AllocateNetwork", listener.networkAllocate)
|
||||
handleMethod(networkReceiver, "FreeNetwork", listener.networkFree)
|
||||
}
|
||||
|
||||
if ipamDriver != nil {
|
||||
handleMethod(ipamReceiver, "GetCapabilities", listener.getIpamCapabilities)
|
||||
handleMethod(ipamReceiver, "GetDefaultAddressSpaces", listener.getDefaultAddressSpaces)
|
||||
handleMethod(ipamReceiver, "RequestPool", listener.requestPool)
|
||||
handleMethod(ipamReceiver, "ReleasePool", listener.releasePool)
|
||||
@@ -137,7 +146,7 @@ func (listener *listener) createEndpoint(w http.ResponseWriter, r *http.Request)
|
||||
func (listener *listener) deleteEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
var delete api.DeleteEndpointRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&delete); err != nil {
|
||||
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||
sendError(w, "Could not decode JSON encoded payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
emptyOrErrorResponse(w, listener.d.DeleteEndpoint(&delete))
|
||||
@@ -146,7 +155,7 @@ func (listener *listener) deleteEndpoint(w http.ResponseWriter, r *http.Request)
|
||||
func (listener *listener) infoEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
var req api.EndpointInfoRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||
sendError(w, "Could not decode JSON encoded payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
info, err := listener.d.EndpointInfo(&req)
|
||||
@@ -156,17 +165,37 @@ func (listener *listener) infoEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
func (listener *listener) joinEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
var join api.JoinRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&join); err != nil {
|
||||
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||
sendError(w, "Could not decode JSON encoded payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
res, err := listener.d.JoinEndpoint(&join)
|
||||
objectOrErrorResponse(w, res, err)
|
||||
}
|
||||
|
||||
func (listener *listener) networkAllocate(w http.ResponseWriter, r *http.Request) {
|
||||
var alloc api.AllocateNetworkRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&alloc); err != nil {
|
||||
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
res, err := listener.d.NetworkAllocate(&alloc)
|
||||
objectOrErrorResponse(w, res, err)
|
||||
}
|
||||
|
||||
func (listener *listener) networkFree(w http.ResponseWriter, r *http.Request) {
|
||||
var free api.FreeNetworkRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&free); err != nil {
|
||||
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
res, err := listener.d.NetworkFree(&free)
|
||||
objectOrErrorResponse(w, res, err)
|
||||
}
|
||||
|
||||
func (listener *listener) leaveEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
var l api.LeaveRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&l); err != nil {
|
||||
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||
sendError(w, "Could not decode JSON encoded payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
emptyOrErrorResponse(w, listener.d.LeaveEndpoint(&l))
|
||||
@@ -175,7 +204,7 @@ func (listener *listener) leaveEndpoint(w http.ResponseWriter, r *http.Request)
|
||||
func (listener *listener) discoverNew(w http.ResponseWriter, r *http.Request) {
|
||||
var disco api.DiscoveryNotification
|
||||
if err := json.NewDecoder(r.Body).Decode(&disco); err != nil {
|
||||
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||
sendError(w, "Could not decode JSON encoded payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
emptyOrErrorResponse(w, listener.d.DiscoverNew(&disco))
|
||||
@@ -184,7 +213,7 @@ func (listener *listener) discoverNew(w http.ResponseWriter, r *http.Request) {
|
||||
func (listener *listener) discoverDelete(w http.ResponseWriter, r *http.Request) {
|
||||
var disco api.DiscoveryNotification
|
||||
if err := json.NewDecoder(r.Body).Decode(&disco); err != nil {
|
||||
sendError(w, "Could not decode JSON encode payload", http.StatusBadRequest)
|
||||
sendError(w, "Could not decode JSON encoded payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
emptyOrErrorResponse(w, listener.d.DiscoverDelete(&disco))
|
||||
@@ -192,6 +221,20 @@ func (listener *listener) discoverDelete(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
// ===
|
||||
|
||||
func (listener *listener) getIpamCapabilities(w http.ResponseWriter, r *http.Request) {
|
||||
c, ok := listener.i.(IpamCapability)
|
||||
if !ok {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
requiresMACAddress, requiresRequestReplay, err := c.GetCapabilities()
|
||||
response := &ipamapi.Capability{
|
||||
RequiresMACAddress: requiresMACAddress,
|
||||
RequiresRequestReplay: requiresRequestReplay,
|
||||
}
|
||||
objectOrErrorResponse(w, response, err)
|
||||
}
|
||||
|
||||
func (listener *listener) getDefaultAddressSpaces(w http.ResponseWriter, r *http.Request) {
|
||||
local, global, err := listener.i.GetDefaultAddressSpaces()
|
||||
response := &iapi.GetAddressSpacesResponse{
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package plugin
|
||||
|
||||
type Status struct {
|
||||
Config Config
|
||||
DriverName string
|
||||
MeshDriverName string `json:"MeshDriverName,omitempty"`
|
||||
}
|
||||
|
||||
func (plugin *Plugin) NewStatus() *Status {
|
||||
if plugin == nil {
|
||||
return nil
|
||||
}
|
||||
status := &Status{
|
||||
DriverName: pluginNameFromAddress(plugin.Socket),
|
||||
MeshDriverName: pluginNameFromAddress(plugin.MeshSocket),
|
||||
Config: plugin.Config,
|
||||
}
|
||||
return status
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
In order to keep track of active weave peers, we use annotations on the Kubernetes cluster.
|
||||
|
||||
Kubernetes uses etcd to distribute and synchronise these annotations so we don't have to.
|
||||
|
||||
This module deals with operations on the peerlist backed by Kubernetes' annotation mechanism.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
kubeErrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
api "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
wait "k8s.io/apimachinery/pkg/util/wait"
|
||||
kubernetes "k8s.io/client-go/kubernetes"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
)
|
||||
|
||||
type configMapAnnotations struct {
|
||||
ConfigMapName string
|
||||
Namespace string
|
||||
Client corev1client.ConfigMapsGetter
|
||||
cm *v1.ConfigMap
|
||||
}
|
||||
|
||||
func newConfigMapAnnotations(ns string, configMapName string, clientset *kubernetes.Clientset) *configMapAnnotations {
|
||||
return &configMapAnnotations{
|
||||
Namespace: ns,
|
||||
ConfigMapName: configMapName,
|
||||
Client: clientset.CoreV1(),
|
||||
}
|
||||
}
|
||||
|
||||
type peerList struct {
|
||||
Peers []peerInfo
|
||||
}
|
||||
|
||||
type peerInfo struct {
|
||||
PeerName string // Weave internal unique ID
|
||||
NodeName string // Kubernetes node name
|
||||
}
|
||||
|
||||
func (pl peerList) contains(peerName string) bool {
|
||||
for _, peer := range pl.Peers {
|
||||
if peer.PeerName == peerName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (pl *peerList) add(peerName string, name string) {
|
||||
pl.Peers = append(pl.Peers, peerInfo{PeerName: peerName, NodeName: name})
|
||||
}
|
||||
|
||||
func (pl *peerList) remove(peerNameToRemove string) {
|
||||
for i := 0; i < len(pl.Peers); {
|
||||
if pl.Peers[i].PeerName == peerNameToRemove {
|
||||
pl.Peers = append(pl.Peers[:i], pl.Peers[i+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
retryPeriod = time.Second * 2
|
||||
jitterFactor = 1.0
|
||||
|
||||
// KubePeersAnnotationKey is the default annotation key
|
||||
KubePeersAnnotationKey = "kube-peers.weave.works/peers"
|
||||
)
|
||||
|
||||
func (cml *configMapAnnotations) Init() error {
|
||||
for {
|
||||
// Since it's potentially racy to GET, then CREATE if not found, we wrap in a check loop
|
||||
// so that if the configmap is created after our GET but before or CREATE, we'll gracefully
|
||||
// re-try to get the configmap.
|
||||
var err error
|
||||
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Get(cml.ConfigMapName, api.GetOptions{})
|
||||
if err != nil {
|
||||
if !kubeErrors.IsNotFound(err) {
|
||||
return errors.Wrapf(err, "Unable to fetch ConfigMap %s/%s", cml.Namespace, cml.ConfigMapName)
|
||||
}
|
||||
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Create(&v1.ConfigMap{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: cml.ConfigMapName,
|
||||
Namespace: cml.Namespace,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
if kubeErrors.IsAlreadyExists(err) {
|
||||
continue
|
||||
}
|
||||
return errors.Wrapf(err, "Unable to create ConfigMap %s/%s", cml.Namespace, cml.ConfigMapName)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
if cml.cm.Annotations == nil {
|
||||
cml.cm.Annotations = make(map[string]string)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cml *configMapAnnotations) GetPeerList() (*peerList, error) {
|
||||
var record peerList
|
||||
if cml.cm == nil {
|
||||
return nil, errors.New("endpoint not initialized, call Init first")
|
||||
}
|
||||
if recordBytes, found := cml.cm.Annotations[KubePeersAnnotationKey]; found {
|
||||
if err := json.Unmarshal([]byte(recordBytes), &record); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
func (cml *configMapAnnotations) UpdatePeerList(list peerList) error {
|
||||
recordBytes, err := json.Marshal(list)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cml.UpdateAnnotation(KubePeersAnnotationKey, string(recordBytes))
|
||||
}
|
||||
|
||||
func (cml *configMapAnnotations) UpdateAnnotation(key, value string) error {
|
||||
if cml.cm == nil {
|
||||
return errors.New("endpoint not initialized, call Init first")
|
||||
}
|
||||
cm := cml.cm
|
||||
cm.Annotations[key] = value
|
||||
cm, err := cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||
if err == nil {
|
||||
cml.cm = cm
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (cml *configMapAnnotations) RemoveAnnotation(key string) error {
|
||||
if cml.cm == nil {
|
||||
return errors.New("endpoint not initialized, call Init first")
|
||||
}
|
||||
cm := cml.cm
|
||||
delete(cm.Annotations, key)
|
||||
cm, err := cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||
if err == nil {
|
||||
cml.cm = cm
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (cml *configMapAnnotations) RemoveAnnotationsWithValue(valueToRemove string) error {
|
||||
if cml.cm == nil {
|
||||
return errors.New("endpoint not initialized, call Init first")
|
||||
}
|
||||
cm := cml.cm
|
||||
for key, value := range cm.Annotations {
|
||||
if value == valueToRemove {
|
||||
delete(cm.Annotations, key)
|
||||
}
|
||||
}
|
||||
cm, err := cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||
if err == nil {
|
||||
cml.cm = cm
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Loop with jitter, fetching the cml data and calling f() until it
|
||||
// doesn't get an optimistic locking conflict.
|
||||
// If it succeeds or gets any other kind of error, stop the loop.
|
||||
func (cml *configMapAnnotations) LoopUpdate(f func() error) error {
|
||||
stop := make(chan struct{})
|
||||
var err error
|
||||
wait.JitterUntil(func() {
|
||||
if err = cml.Init(); err != nil {
|
||||
close(stop)
|
||||
return
|
||||
}
|
||||
err = f()
|
||||
if err != nil && kubeErrors.IsConflict(err) {
|
||||
log.Printf("Optimistic locking conflict: trying again: %s", err)
|
||||
return
|
||||
}
|
||||
close(stop)
|
||||
}, retryPeriod, jitterFactor, true, stop)
|
||||
return err
|
||||
}
|
||||
+210
-31
@@ -1,55 +1,234 @@
|
||||
/*
|
||||
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"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
api "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
api "k8s.io/client-go/pkg/api/v1"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
weaveapi "github.com/weaveworks/weave/api"
|
||||
"github.com/weaveworks/weave/common"
|
||||
)
|
||||
|
||||
func getKubePeers() ([]string, error) {
|
||||
config, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodeList, err := c.Nodes().List(api.ListOptions{})
|
||||
if err != nil {
|
||||
// Fallback for cases (e.g. from kube-up.sh) where kube-proxy is not running on master
|
||||
config.Host = "http://localhost:8080"
|
||||
log.Print("error contacting APIServer: ", err, "; trying with fallback: ", config.Host)
|
||||
c, err = kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodeList, err = c.Nodes().List(api.ListOptions{})
|
||||
}
|
||||
type nodeInfo struct {
|
||||
name string
|
||||
addr string
|
||||
}
|
||||
|
||||
// return the IP addresses of all nodes in the cluster
|
||||
func getKubePeers(c *kubernetes.Clientset) ([]nodeInfo, error) {
|
||||
nodeList, err := c.CoreV1().Nodes().List(api.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addresses := make([]string, 0, len(nodeList.Items))
|
||||
addresses := make([]nodeInfo, 0, len(nodeList.Items))
|
||||
for _, peer := range nodeList.Items {
|
||||
var internalIP, externalIP string
|
||||
for _, addr := range peer.Status.Addresses {
|
||||
if addr.Type == "InternalIP" {
|
||||
addresses = append(addresses, addr.Address)
|
||||
// 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})
|
||||
}
|
||||
}
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
peers, err := getKubePeers()
|
||||
if err != nil {
|
||||
log.Fatalf("Could not get peers: %v", err)
|
||||
const (
|
||||
configMapName = "weave-net"
|
||||
configMapNamespace = "kube-system"
|
||||
)
|
||||
|
||||
// update the list of all peers that have gone through this code path
|
||||
func addMyselfToPeerList(cml *configMapAnnotations, c *kubernetes.Clientset, peerName, name string) (*peerList, error) {
|
||||
var list *peerList
|
||||
err := cml.LoopUpdate(func() error {
|
||||
var err error
|
||||
list, err = cml.GetPeerList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !list.contains(peerName) {
|
||||
list.add(peerName, name)
|
||||
err = cml.UpdatePeerList(*list)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return list, err
|
||||
}
|
||||
|
||||
func checkIamInPeerList(cml *configMapAnnotations, c *kubernetes.Clientset, peerName string) (bool, error) {
|
||||
if err := cml.Init(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, addr := range peers {
|
||||
fmt.Println(addr)
|
||||
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
|
||||
}
|
||||
|
||||
// For each of those peers that is no longer listed as a node by
|
||||
// Kubernetes, remove it from Weave IPAM
|
||||
func reclaimRemovedPeers(weave *weaveapi.Client, cml *configMapAnnotations, nodes []nodeInfo, myPeerName string) error {
|
||||
for {
|
||||
// 1. Compare peers stored in the peerList against all peers reported by k8s now.
|
||||
storedPeerList, err := cml.GetPeerList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
peerMap := make(map[string]peerInfo, len(storedPeerList.Peers))
|
||||
for _, peer := range storedPeerList.Peers {
|
||||
peerMap[peer.NodeName] = peer
|
||||
}
|
||||
for _, node := range nodes {
|
||||
delete(peerMap, node.name)
|
||||
}
|
||||
common.Log.Debugln("[kube-peers] Nodes that have disappeared:", peerMap)
|
||||
if len(peerMap) == 0 {
|
||||
break
|
||||
}
|
||||
// 2. Loop for each X in the first set and not in the second - we wish to remove X from our data structures
|
||||
for _, peer := range peerMap {
|
||||
common.Log.Debugln("[kube-peers] Preparing to remove disappeared peer", peer)
|
||||
okToRemove := false
|
||||
// 3. Check if there is an existing annotation with key X
|
||||
if existingAnnotation, found := cml.cm.Annotations[peer.PeerName]; found {
|
||||
common.Log.Debugln("[kube-peers] Existing annotation", existingAnnotation)
|
||||
// 4. If annotation already contains my identity, ok;
|
||||
if existingAnnotation == myPeerName {
|
||||
okToRemove = true
|
||||
}
|
||||
} else {
|
||||
// 5. If non-existent, write an annotation with key X and contents "my identity"
|
||||
common.Log.Debugln("[kube-peers] Noting I plan to remove ", peer.PeerName)
|
||||
if err := cml.UpdateAnnotation(peer.PeerName, myPeerName); err == nil {
|
||||
okToRemove = true
|
||||
}
|
||||
}
|
||||
if okToRemove {
|
||||
// 6. If step 4 or 5 succeeded, rmpeer X
|
||||
result, err := weave.RmPeer(peer.PeerName)
|
||||
common.Log.Infoln("[kube-peers] rmpeer of", peer.PeerName, ":", result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cml.LoopUpdate(func() error {
|
||||
// 7aa. Remove any annotations Z* that have contents X
|
||||
if err := cml.RemoveAnnotationsWithValue(peer.PeerName); err != nil {
|
||||
return err
|
||||
}
|
||||
// 7a. Remove X from peerList
|
||||
storedPeerList.remove(peer.PeerName)
|
||||
common.Log.Infoln("[kube-peers] Removing peer ", peer.PeerName, ". Expecting to remove linked annotation next.")
|
||||
if err := cml.UpdatePeerList(*storedPeerList); err != nil {
|
||||
return err
|
||||
}
|
||||
common.Log.Infoln("[kube-peers] Removing annotation ", peer.PeerName)
|
||||
// 7b. Remove annotation with key X
|
||||
return cml.RemoveAnnotation(peer.PeerName)
|
||||
})
|
||||
common.Log.Debugln("[kube-peers] Finished removal of ", peer.PeerName)
|
||||
}
|
||||
// 8. If step 5 failed due to optimistic lock conflict, stop: someone else is handling X
|
||||
|
||||
// Step 3-5 is to protect against two simultaneous rmpeers of X
|
||||
// Step 4 is to pick up again after a restart between step 5 and step 7b
|
||||
// If the peer doing the reclaim disappears between steps 5 and 7a, then someone will clean it up in step 7aa
|
||||
// If peer doing the reclaim disappears forever between 7a and 7b then we get a dangling annotation
|
||||
// This should be sufficiently rare that we don't care.
|
||||
}
|
||||
|
||||
// 9. Go back to step 1 until there is no difference between the two sets
|
||||
}
|
||||
// Question: Should we narrow step 2 by checking against Weave Net IPAM?
|
||||
// i.e. If peer X owns any address space and is marked unreachable, we want to rmpeer X
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
justReclaim bool
|
||||
justCheck bool
|
||||
peerName string
|
||||
nodeName string
|
||||
logLevel string
|
||||
)
|
||||
flag.BoolVar(&justReclaim, "reclaim", false, "reclaim IP space from dead peers")
|
||||
flag.BoolVar(&justCheck, "check-peer-new", false, "return success if peer name is not stored in annotation")
|
||||
flag.StringVar(&peerName, "peer-name", "unknown", "name of this Weave Net peer")
|
||||
flag.StringVar(&nodeName, "node-name", "unknown", "name of this Kubernetes node")
|
||||
flag.StringVar(&logLevel, "log-level", "info", "logging level (debug, info, warning, error)")
|
||||
flag.Parse()
|
||||
|
||||
common.SetLogLevel(logLevel)
|
||||
config, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
common.Log.Fatalf("[kube-peers] Could not get cluster config: %v", err)
|
||||
}
|
||||
c, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
common.Log.Fatalf("[kube-peers] Could not make Kubernetes connection: %v", err)
|
||||
}
|
||||
if justCheck {
|
||||
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||
exists, err := checkIamInPeerList(cml, c, peerName)
|
||||
if err != nil {
|
||||
common.Log.Fatalf("[kube-peers] Could not check peer list: %v", err)
|
||||
}
|
||||
if exists {
|
||||
os.Exit(9)
|
||||
} else {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
peers, err := getKubePeers(c)
|
||||
if err != nil {
|
||||
common.Log.Fatalf("[kube-peers] Could not get peers: %v", err)
|
||||
}
|
||||
if justReclaim {
|
||||
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||
|
||||
list, err := addMyselfToPeerList(cml, c, peerName, nodeName)
|
||||
if err != nil {
|
||||
common.Log.Fatalf("[kube-peers] Could not update peer list: %v", err)
|
||||
}
|
||||
common.Log.Infoln("[kube-peers] Added myself to peer list", list)
|
||||
|
||||
weave := weaveapi.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), common.Log)
|
||||
err = reclaimRemovedPeers(weave, cml, peers, peerName)
|
||||
if err != nil {
|
||||
common.Log.Fatalf("[kube-peers] Error while reclaiming space: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
for _, node := range peers {
|
||||
fmt.Println(node.addr)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"description": "Weave Net plugin for Docker",
|
||||
"documentation": "https://weave.works",
|
||||
"entrypoint": [
|
||||
"/home/weave/launch.sh"
|
||||
],
|
||||
"interface": {
|
||||
"socket": "weave.sock",
|
||||
"types": [
|
||||
"docker.networkdriver/1.0"
|
||||
]
|
||||
},
|
||||
"linux": {
|
||||
"capabilities": [
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_SYS_MODULE"
|
||||
]
|
||||
},
|
||||
"mounts": [
|
||||
{
|
||||
"destination": "/host/proc/",
|
||||
"type": "bind",
|
||||
"source": "/proc/",
|
||||
"options": ["rbind", "rw"]
|
||||
},
|
||||
{
|
||||
"destination": "/var/run/docker.sock",
|
||||
"source": "/var/run/docker.sock",
|
||||
"type": "bind",
|
||||
"options": ["rbind"]
|
||||
},
|
||||
{
|
||||
"destination": "/host/var/lib/",
|
||||
"source": "/var/lib/",
|
||||
"type": "bind",
|
||||
"options": ["rbind"]
|
||||
},
|
||||
{
|
||||
"destination": "/host/etc/",
|
||||
"source": "/etc/",
|
||||
"type": "bind",
|
||||
"options": ["rbind"]
|
||||
},
|
||||
{
|
||||
"destination": "/lib/modules/",
|
||||
"source": "/lib/modules/",
|
||||
"type": "bind",
|
||||
"options": ["rbind"]
|
||||
}
|
||||
],
|
||||
"env": [
|
||||
{
|
||||
"description": "Extra args to `weaver` and `plugin`",
|
||||
"name": "EXTRA_ARGS",
|
||||
"settable": [
|
||||
"value"
|
||||
],
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"description": "Encryption password",
|
||||
"name": "WEAVE_PASSWORD",
|
||||
"settable": [
|
||||
"value"
|
||||
],
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"description": "MTU",
|
||||
"name": "WEAVE_MTU",
|
||||
"settable": [
|
||||
"value"
|
||||
],
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"description": "Enable multicast for all Weave networks",
|
||||
"name": "WEAVE_MULTICAST",
|
||||
"settable": [
|
||||
"value"
|
||||
],
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"description": "The range of IP addresses used by Weave Net",
|
||||
"name": "IPALLOC_RANGE",
|
||||
"settable": [
|
||||
"value"
|
||||
],
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"network": {
|
||||
"type": "host"
|
||||
},
|
||||
"workdir": ""
|
||||
}
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# Default if not supplied - same as weave net default
|
||||
IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
|
||||
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
||||
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
|
||||
HOST_ROOT=${HOST_ROOT:-/host}
|
||||
WEAVE_DIR="/host/var/lib/weave"
|
||||
|
||||
mkdir $WEAVE_DIR || true
|
||||
|
||||
echo "Starting launch.sh"
|
||||
|
||||
# Check if the IP range overlaps anything existing on the host
|
||||
/usr/bin/weaveutil netcheck $IPALLOC_RANGE weave
|
||||
|
||||
# We need to get a list of Swarm nodes which might run the net-plugin:
|
||||
# - In the case of missing restart.sentinel, we assume that net-plugin has started
|
||||
# for the first time via the docker-plugin cmd. So it's safe to request docker.sock.
|
||||
# - If restart.sentinel present, let weaver restore from it as docker.sock is not
|
||||
# available to any plugin in general (https://github.com/moby/moby/issues/32815).
|
||||
|
||||
PEERS=
|
||||
if [ ! -f "/restart.sentinel" ]; then
|
||||
PEERS=$(/usr/bin/weaveutil swarm-manager-peers)
|
||||
fi
|
||||
|
||||
router_bridge_opts() {
|
||||
echo --datapath=datapath
|
||||
[ -z "$WEAVE_MTU" ] || echo --mtu "$WEAVE_MTU"
|
||||
[ -z "$WEAVE_NO_FASTDP" ] || echo --no-fastdp
|
||||
}
|
||||
|
||||
multicast_opt() {
|
||||
[ -z "$WEAVE_MULTICAST" ] || echo "--plugin-v2-multicast"
|
||||
}
|
||||
|
||||
exec /home/weave/weaver $EXTRA_ARGS --port=6783 $(router_bridge_opts) \
|
||||
--host-root=/host \
|
||||
--proc-path=/host/proc \
|
||||
--http-addr=$HTTP_ADDR --status-addr=$STATUS_ADDR \
|
||||
--no-dns \
|
||||
--ipalloc-range=$IPALLOC_RANGE \
|
||||
--nickname "$(hostname)" \
|
||||
--log-level=debug \
|
||||
--db-prefix="$WEAVE_DIR/weave" \
|
||||
--plugin-v2 \
|
||||
$(multicast_opt) \
|
||||
--plugin-mesh-socket='' \
|
||||
--docker-api='' \
|
||||
$PEERS
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM DOCKERHUB_USER/weaveARCH_EXT
|
||||
ADD ./plugin /home/weave/
|
||||
ENTRYPOINT ["/home/weave/plugin"]
|
||||
@@ -1,140 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
weaveapi "github.com/weaveworks/weave/api"
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/common/docker"
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
ipamplugin "github.com/weaveworks/weave/plugin/ipam"
|
||||
netplugin "github.com/weaveworks/weave/plugin/net"
|
||||
"github.com/weaveworks/weave/plugin/skel"
|
||||
)
|
||||
|
||||
var version = "unreleased"
|
||||
|
||||
var Log = common.Log
|
||||
|
||||
func main() {
|
||||
var (
|
||||
justVersion bool
|
||||
address string
|
||||
meshAddress string
|
||||
logLevel string
|
||||
noMulticastRoute bool
|
||||
)
|
||||
|
||||
flag.BoolVar(&justVersion, "version", false, "print version and exit")
|
||||
flag.StringVar(&logLevel, "log-level", "info", "logging level (debug, info, warning, error)")
|
||||
flag.StringVar(&address, "socket", "/run/docker/plugins/weave.sock", "socket on which to listen")
|
||||
flag.StringVar(&meshAddress, "meshsocket", "/run/docker/plugins/weavemesh.sock", "socket on which to listen in mesh mode")
|
||||
flag.BoolVar(&noMulticastRoute, "no-multicast-route", false, "deprecated (this is now the default)")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if justVersion {
|
||||
fmt.Printf("weave plugin %s\n", version)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
common.SetLogLevel(logLevel)
|
||||
|
||||
weave := weaveapi.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), Log)
|
||||
|
||||
// API 1.21 is the first version that supports docker network commands
|
||||
dockerClient, err := docker.NewVersionedClientFromEnv("1.21")
|
||||
if err != nil {
|
||||
Log.Fatalf("unable to connect to docker: %s", err)
|
||||
}
|
||||
|
||||
Log.Println("Weave plugin", version, "Command line options:", os.Args[1:])
|
||||
if noMulticastRoute {
|
||||
Log.Warning("--no-multicast-route option has been removed; multicast is off by default")
|
||||
}
|
||||
Log.Info(dockerClient.Info())
|
||||
|
||||
err = run(dockerClient, weave, address, meshAddress)
|
||||
if err != nil {
|
||||
Log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run(dockerClient *docker.Client, weave *weaveapi.Client, address, meshAddress string) error {
|
||||
endChan := make(chan error, 1)
|
||||
if address != "" {
|
||||
globalListener, err := listenAndServe(dockerClient, weave, address, endChan, "global", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(address)
|
||||
defer globalListener.Close()
|
||||
}
|
||||
if meshAddress != "" {
|
||||
meshListener, err := listenAndServe(dockerClient, weave, meshAddress, endChan, "local", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(meshAddress)
|
||||
defer meshListener.Close()
|
||||
}
|
||||
|
||||
statusListener, err := weavenet.ListenUnixSocket("/home/weave/status.sock")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go serveStatus(statusListener)
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
|
||||
select {
|
||||
case sig := <-sigChan:
|
||||
Log.Debugf("Caught signal %s; shutting down", sig)
|
||||
return nil
|
||||
case err := <-endChan:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func listenAndServe(dockerClient *docker.Client, weave *weaveapi.Client, address string, endChan chan<- error, scope string, withIpam bool) (net.Listener, error) {
|
||||
name := strings.TrimSuffix(path.Base(address), ".sock")
|
||||
d, err := netplugin.New(dockerClient, weave, name, scope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var i ipamapi.Ipam
|
||||
if withIpam {
|
||||
i = ipamplugin.NewIpam(weave)
|
||||
}
|
||||
|
||||
listener, err := weavenet.ListenUnixSocket(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
Log.Printf("Listening on %s for %s scope", address, scope)
|
||||
|
||||
go func() {
|
||||
endChan <- skel.Listen(listener, d, i)
|
||||
}()
|
||||
|
||||
return listener, nil
|
||||
}
|
||||
|
||||
func serveStatus(listener net.Listener) {
|
||||
server := &http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "ok")
|
||||
})}
|
||||
if err := server.Serve(listener); err != nil {
|
||||
Log.Fatalf("ListenAndServeStatus failed: %s", err)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
weavetest "github.com/weaveworks/weave/testing"
|
||||
)
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
weavetest.TrimTestArgs()
|
||||
main()
|
||||
}
|
||||
+64
-18
@@ -2,12 +2,35 @@
|
||||
|
||||
set -e
|
||||
|
||||
modprobe_safe() {
|
||||
modprobe $1 || echo "Ignore the error if \"$1\" is built-in in the kernel" >&2
|
||||
}
|
||||
|
||||
# Check whether xt_set actually exists
|
||||
xt_set_exists() {
|
||||
# Clean everything up in advance, in case there's leftovers
|
||||
iptables -F WEAVE-KUBE-TEST 2>/dev/null || true
|
||||
iptables -X WEAVE-KUBE-TEST 2>/dev/null || true
|
||||
ipset destroy weave-kube-test 2>/dev/null || true
|
||||
|
||||
ipset create weave-kube-test hash:ip
|
||||
iptables -t filter -N WEAVE-KUBE-TEST
|
||||
if ! iptables -A WEAVE-KUBE-TEST -m set --match-set weave-kube-test src -j DROP; then
|
||||
NOT_EXIST=1
|
||||
fi
|
||||
iptables -F WEAVE-KUBE-TEST
|
||||
iptables -X WEAVE-KUBE-TEST
|
||||
ipset destroy weave-kube-test
|
||||
[ -z "$NOT_EXIST" ] || (echo "\"xt_set\" does not exist" >&2 && return 1)
|
||||
}
|
||||
|
||||
# Default if not supplied - same as weave net default
|
||||
IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
|
||||
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
||||
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
|
||||
HOST_ROOT=${HOST_ROOT:-/host}
|
||||
CONN_LIMIT=${CONN_LIMIT:-30}
|
||||
DB_PREFIX=${DB_PREFIX:-/weavedb/weave-net}
|
||||
|
||||
# Check if the IP range overlaps anything existing on the host
|
||||
/usr/bin/weaveutil netcheck $IPALLOC_RANGE weave
|
||||
@@ -17,8 +40,9 @@ EXPECT_NPC=${EXPECT_NPC:-1}
|
||||
|
||||
# Ensure we have the required modules for NPC
|
||||
if [ "${EXPECT_NPC}" != "0" ]; then
|
||||
modprobe br_netfilter
|
||||
modprobe xt_set
|
||||
modprobe_safe br_netfilter
|
||||
modprobe_safe xt_set
|
||||
xt_set_exists
|
||||
fi
|
||||
|
||||
# kube-proxy requires that bridged traffic passes through netfilter
|
||||
@@ -30,15 +54,12 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
# Explicitly create the bridge so we can pass --expect-npc
|
||||
# Default is that npc will be running; allow caller to override
|
||||
WEAVE_NPC_OPTS="--expect-npc"
|
||||
if [ "${EXPECT_NPC}" = "0" ]; then
|
||||
WEAVE_NPC_OPTS=""
|
||||
fi
|
||||
|
||||
export HOST_ROOT=/host
|
||||
/home/weave/weave --local create-bridge --force $WEAVE_NPC_OPTS
|
||||
|
||||
# Kubernetes sets HOSTNAME to the host's hostname
|
||||
# when running a pod in host namespace.
|
||||
NICKNAME_ARG=""
|
||||
@@ -46,17 +67,11 @@ if [ -n "$HOSTNAME" ] ; then
|
||||
NICKNAME_ARG="--nickname=$HOSTNAME"
|
||||
fi
|
||||
|
||||
BRIDGE_OPTIONS="--datapath=datapath"
|
||||
if [ "$(/home/weave/weave --local bridge-type)" = "bridge" ] ; then
|
||||
# TODO: Call into weave script to do this
|
||||
if ! ip link show vethwe-pcap >/dev/null 2>&1 ; then
|
||||
ip link add name vethwe-bridge type veth peer name vethwe-pcap
|
||||
ip link set vethwe-bridge up
|
||||
ip link set vethwe-pcap up
|
||||
ip link set vethwe-bridge master weave
|
||||
fi
|
||||
BRIDGE_OPTIONS="--iface=vethwe-pcap"
|
||||
fi
|
||||
router_bridge_opts() {
|
||||
echo --datapath=datapath
|
||||
[ -z "$WEAVE_MTU" ] || echo --mtu "$WEAVE_MTU"
|
||||
[ -z "$WEAVE_NO_FASTDP" ] || echo --no-fastdp
|
||||
}
|
||||
|
||||
if [ -z "$KUBE_PEERS" ]; then
|
||||
if ! KUBE_PEERS=$(/home/weave/kube-peers) || [ -z "$KUBE_PEERS" ]; then
|
||||
@@ -73,6 +88,29 @@ if [ -z "$IPALLOC_INIT" ]; then
|
||||
IPALLOC_INIT="consensus=$(peer_count $KUBE_PEERS)"
|
||||
fi
|
||||
|
||||
# Find out what peer name we will be using
|
||||
PEERNAME=$(/home/weave/weaver $EXTRA_ARGS --print-peer-name --host-root=$HOST_ROOT --db-prefix="$DB_PREFIX")
|
||||
if [ -z "$PEERNAME" ]; then
|
||||
echo "Unable to get peer name" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# If we have persisted data and peer reclaim is in use
|
||||
if [ -f ${DB_PREFIX}data.db ]; then
|
||||
if /usr/bin/weaveutil get-db-flag "$DB_PREFIX" peer-reclaim >/dev/null ; then
|
||||
# If this peer name is not stored in the list then we were removed by another peer.
|
||||
if /home/weave/kube-peers -check-peer-new -peer-name="$PEERNAME" -log-level=debug ; then
|
||||
# In order to avoid a CRDT clash, clean up
|
||||
echo "Peer not in list; removing persisted data" >&2
|
||||
rm -f ${DB_PREFIX}data.db
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# flag that peer reclaim is now in use - note we have to do this before
|
||||
# weaver is running as the DB can only be opened by one process at a time
|
||||
/usr/bin/weaveutil set-db-flag "$DB_PREFIX" peer-reclaim ok
|
||||
|
||||
post_start_actions() {
|
||||
# Wait for weave process to become responsive
|
||||
while true ; do
|
||||
@@ -91,18 +129,26 @@ post_start_actions() {
|
||||
fi
|
||||
fi
|
||||
mkdir -p $HOST_ROOT/etc/cni/net.d
|
||||
export HOST_ROOT
|
||||
/home/weave/weave --local setup-cni
|
||||
|
||||
# Attempt to run the reclaim process, but don't halt the script if it fails
|
||||
/home/weave/kube-peers -reclaim -node-name="$HOSTNAME" -peer-name="$PEERNAME" -log-level=debug || true
|
||||
|
||||
# Expose the weave network so host processes can communicate with pods
|
||||
/home/weave/weave --local expose $WEAVE_EXPOSE_IP
|
||||
}
|
||||
|
||||
post_start_actions &
|
||||
|
||||
exec /home/weave/weaver $EXTRA_ARGS --port=6783 $BRIDGE_OPTIONS \
|
||||
/home/weave/weaver $EXTRA_ARGS --port=6783 $(router_bridge_opts) \
|
||||
--name="$PEERNAME" \
|
||||
--host-root=$HOST_ROOT \
|
||||
--http-addr=$HTTP_ADDR --status-addr=$STATUS_ADDR --docker-api='' --no-dns \
|
||||
--db-prefix="$DB_PREFIX" \
|
||||
--ipalloc-range=$IPALLOC_RANGE $NICKNAME_ARG \
|
||||
--ipalloc-init $IPALLOC_INIT \
|
||||
--conn-limit=$CONN_LIMIT \
|
||||
$WEAVE_NPC_OPTS \
|
||||
"$@" \
|
||||
$KUBE_PEERS
|
||||
@@ -0,0 +1,180 @@
|
||||
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
|
||||
- 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-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
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: weave-net
|
||||
labels:
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: weave-net
|
||||
spec:
|
||||
containers:
|
||||
- name: weave
|
||||
command:
|
||||
- /home/weave/launch.sh
|
||||
env:
|
||||
- name: HOSTNAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
apiVersion: v1
|
||||
fieldPath: spec.nodeName
|
||||
image: 'weaveworks/weave-kube:latest'
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
host: 127.0.0.1
|
||||
path: /status
|
||||
port: 6784
|
||||
initialDelaySeconds: 30
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: weavedb
|
||||
mountPath: /weavedb
|
||||
- name: cni-bin
|
||||
mountPath: /host/opt
|
||||
- name: cni-bin2
|
||||
mountPath: /host/home
|
||||
- name: cni-conf
|
||||
mountPath: /host/etc
|
||||
- name: dbus
|
||||
mountPath: /host/var/lib/dbus
|
||||
- name: lib-modules
|
||||
mountPath: /lib/modules
|
||||
- name: 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
|
||||
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
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
@@ -0,0 +1,199 @@
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
- apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: weave-net
|
||||
labels:
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: weave-net
|
||||
labels:
|
||||
name: weave-net
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ''
|
||||
resources:
|
||||
- pods
|
||||
- namespaces
|
||||
- nodes
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
- networkpolicies
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- 'networking.k8s.io'
|
||||
resources:
|
||||
- networkpolicies
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: weave-net
|
||||
labels:
|
||||
name: weave-net
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: weave-net
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
labels:
|
||||
name: weave-net
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ''
|
||||
resources:
|
||||
- configmaps
|
||||
resourceNames:
|
||||
- weave-net
|
||||
verbs:
|
||||
- get
|
||||
- update
|
||||
- apiGroups:
|
||||
- ''
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- create
|
||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
labels:
|
||||
name: weave-net
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: weave-net
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
- apiVersion: extensions/v1beta1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: weave-net
|
||||
labels:
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: weave-net
|
||||
spec:
|
||||
containers:
|
||||
- name: weave
|
||||
command:
|
||||
- /home/weave/launch.sh
|
||||
env:
|
||||
- name: HOSTNAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
apiVersion: v1
|
||||
fieldPath: spec.nodeName
|
||||
image: 'weaveworks/weave-kube:latest'
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
host: 127.0.0.1
|
||||
path: /status
|
||||
port: 6784
|
||||
initialDelaySeconds: 30
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: weavedb
|
||||
mountPath: /weavedb
|
||||
- name: cni-bin
|
||||
mountPath: /host/opt
|
||||
- name: cni-bin2
|
||||
mountPath: /host/home
|
||||
- name: cni-conf
|
||||
mountPath: /host/etc
|
||||
- name: dbus
|
||||
mountPath: /host/var/lib/dbus
|
||||
- name: lib-modules
|
||||
mountPath: /lib/modules
|
||||
- name: xtables-lock
|
||||
mountPath: /run/xtables.lock
|
||||
readOnly: false
|
||||
- name: weave-npc
|
||||
env:
|
||||
- name: HOSTNAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
apiVersion: v1
|
||||
fieldPath: spec.nodeName
|
||||
image: 'weaveworks/weave-npc:latest'
|
||||
imagePullPolicy: Always
|
||||
#npc-args
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: xtables-lock
|
||||
mountPath: /run/xtables.lock
|
||||
readOnly: false
|
||||
hostNetwork: true
|
||||
hostPID: true
|
||||
restartPolicy: Always
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
serviceAccountName: weave-net
|
||||
tolerations:
|
||||
- effect: NoSchedule
|
||||
operator: Exists
|
||||
volumes:
|
||||
- name: weavedb
|
||||
hostPath:
|
||||
path: /var/lib/weave
|
||||
- name: cni-bin
|
||||
hostPath:
|
||||
path: /opt
|
||||
- name: cni-bin2
|
||||
hostPath:
|
||||
path: /home
|
||||
- name: cni-conf
|
||||
hostPath:
|
||||
path: /etc
|
||||
- name: dbus
|
||||
hostPath:
|
||||
path: /var/lib/dbus
|
||||
- name: lib-modules
|
||||
hostPath:
|
||||
path: /lib/modules
|
||||
- name: xtables-lock
|
||||
hostPath:
|
||||
path: /run/xtables.lock
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
@@ -1,80 +1,101 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
spec:
|
||||
template:
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
- apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: weave-net
|
||||
labels:
|
||||
name: weave-net
|
||||
annotations:
|
||||
scheduler.alpha.kubernetes.io/tolerations: |
|
||||
[
|
||||
{
|
||||
"key": "dedicated",
|
||||
"operator": "Equal",
|
||||
"value": "master",
|
||||
"effect": "NoSchedule"
|
||||
}
|
||||
]
|
||||
namespace: kube-system
|
||||
- apiVersion: extensions/v1beta1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: weave-net
|
||||
labels:
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
spec:
|
||||
hostNetwork: true
|
||||
hostPID: true
|
||||
containers:
|
||||
- name: weave
|
||||
image: weaveworks/weave-kube:latest
|
||||
imagePullPolicy: Always
|
||||
command:
|
||||
- /home/weave/launch.sh
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 30
|
||||
httpGet:
|
||||
host: 127.0.0.1
|
||||
path: /status
|
||||
port: 6784
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
scheduler.alpha.kubernetes.io/tolerations: >-
|
||||
[{"key":"dedicated","operator":"Equal","value":"master","effect":"NoSchedule"}]
|
||||
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: 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
|
||||
hostNetwork: true
|
||||
hostPID: true
|
||||
restartPolicy: Always
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
seLinuxOptions: {}
|
||||
serviceAccountName: weave-net
|
||||
volumes:
|
||||
- name: weavedb
|
||||
mountPath: /weavedb
|
||||
hostPath:
|
||||
path: /var/lib/weave
|
||||
- name: cni-bin
|
||||
mountPath: /host/opt
|
||||
hostPath:
|
||||
path: /opt
|
||||
- name: cni-bin2
|
||||
mountPath: /host/home
|
||||
hostPath:
|
||||
path: /home
|
||||
- name: cni-conf
|
||||
mountPath: /host/etc
|
||||
hostPath:
|
||||
path: /etc
|
||||
- name: dbus
|
||||
mountPath: /host/var/lib/dbus
|
||||
hostPath:
|
||||
path: /var/lib/dbus
|
||||
- name: lib-modules
|
||||
mountPath: /lib/modules
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
- name: weave-npc
|
||||
image: weaveworks/weave-npc:latest
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
securityContext:
|
||||
privileged: true
|
||||
restartPolicy: Always
|
||||
volumes:
|
||||
- name: weavedb
|
||||
emptyDir: {}
|
||||
- name: cni-bin
|
||||
hostPath:
|
||||
path: /opt
|
||||
- name: cni-bin2
|
||||
hostPath:
|
||||
path: /home
|
||||
- name: cni-conf
|
||||
hostPath:
|
||||
path: /etc
|
||||
- name: dbus
|
||||
hostPath:
|
||||
path: /var/lib/dbus
|
||||
- name: lib-modules
|
||||
hostPath:
|
||||
path: /lib/modules
|
||||
hostPath:
|
||||
path: /lib/modules
|
||||
+88
-42
@@ -7,12 +7,13 @@ import (
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/spf13/cobra"
|
||||
coreapi "k8s.io/api/core/v1"
|
||||
extnapi "k8s.io/api/extensions/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
coreapi "k8s.io/client-go/pkg/api/v1"
|
||||
extnapi "k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||
"k8s.io/client-go/pkg/fields"
|
||||
"k8s.io/client-go/pkg/runtime"
|
||||
"k8s.io/client-go/pkg/util/wait"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
@@ -28,12 +29,14 @@ var (
|
||||
metricsAddr string
|
||||
logLevel string
|
||||
allowMcast bool
|
||||
nodeName string
|
||||
legacy bool
|
||||
)
|
||||
|
||||
func handleError(err error) { common.CheckFatal(err) }
|
||||
|
||||
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())
|
||||
_, controller := cache.NewInformer(listWatch, objType, 0, handlers)
|
||||
return controller
|
||||
@@ -49,10 +52,41 @@ func resetIPTables(ipt *iptables.IPTables) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ipt.ClearChain(npc.TableFilter, npc.MainChain); err != nil {
|
||||
return ipt.ClearChain(npc.TableFilter, npc.MainChain)
|
||||
}
|
||||
|
||||
func resetIPSets(ips ipset.Interface) error {
|
||||
// Remove ipsets prefixed `weave-` only
|
||||
|
||||
sets, err := ips.List(npc.IpsetNamePrefix)
|
||||
if err != nil {
|
||||
common.Log.Errorf("Failed to retrieve list of ipsets")
|
||||
return err
|
||||
}
|
||||
|
||||
common.Log.Debugf("Got list of ipsets: %v", sets)
|
||||
|
||||
// Must remove references to ipsets by other ipsets before they're destroyed
|
||||
for _, s := range sets {
|
||||
common.Log.Debugf("Flushing ipset '%s'", string(s))
|
||||
if err := ips.Flush(s); err != nil {
|
||||
common.Log.Errorf("Failed to flush ipset '%s'", string(s))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, s := range sets {
|
||||
common.Log.Debugf("Destroying ipset '%s'", string(s))
|
||||
if err := ips.Destroy(s); err != nil {
|
||||
common.Log.Errorf("Failed to destroy ipset '%s'", string(s))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createBaseRules(ipt *iptables.IPTables, ips ipset.Interface) error {
|
||||
// Configure main chain static rules
|
||||
if err := ipt.Append(npc.TableFilter, npc.MainChain,
|
||||
"-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"); err != nil {
|
||||
@@ -76,26 +110,30 @@ func resetIPTables(ipt *iptables.IPTables) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetIPSets(ips ipset.Interface) error {
|
||||
// TODO should restrict ipset operations to the `weave-` prefix:
|
||||
|
||||
if err := ips.FlushAll(); err != nil {
|
||||
// If the destination address is not any of the local pods, let it through
|
||||
if err := ips.Create(npc.LocalIpset, ipset.HashIP); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ips.DestroyAll(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return ipt.Append(npc.TableFilter, npc.MainChain,
|
||||
"-m", "set", "!", "--match-set", npc.LocalIpset, "dst", "-j", "ACCEPT")
|
||||
}
|
||||
|
||||
func root(cmd *cobra.Command, args []string) {
|
||||
var npController cache.Controller
|
||||
|
||||
common.SetLogLevel(logLevel)
|
||||
common.Log.Infof("Starting Weaveworks NPC %s", version)
|
||||
if nodeName == "" {
|
||||
// HOSTNAME is set by Kubernetes for pods in the host network namespace
|
||||
nodeName = os.Getenv("HOSTNAME")
|
||||
}
|
||||
if nodeName == "" {
|
||||
common.Log.Fatalf("Must set node name via --node-name or $HOSTNAME")
|
||||
}
|
||||
common.Log.Infof("Starting Weaveworks NPC %s; node name %q", version, nodeName)
|
||||
|
||||
if legacy {
|
||||
common.Log.Info("Running in legacy mode (k8s pre-1.7 network policy semantics)")
|
||||
}
|
||||
|
||||
if err := metrics.Start(metricsAddr); err != nil {
|
||||
common.Log.Fatalf("Failed to start metrics: %v", err)
|
||||
@@ -114,12 +152,13 @@ func root(cmd *cobra.Command, args []string) {
|
||||
ipt, err := iptables.New()
|
||||
handleError(err)
|
||||
|
||||
ips := ipset.New()
|
||||
ips := ipset.New(common.LogLogger())
|
||||
|
||||
handleError(resetIPTables(ipt))
|
||||
handleError(resetIPSets(ips))
|
||||
handleError(createBaseRules(ipt, ips))
|
||||
|
||||
npc := npc.New(ipt, ips)
|
||||
npc := npc.New(nodeName, legacy, ipt, ips)
|
||||
|
||||
nsController := makeController(client.Core().RESTClient(), "namespaces", &coreapi.Namespace{},
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
@@ -161,25 +200,30 @@ func root(cmd *cobra.Command, args []string) {
|
||||
handleError(npc.UpdatePod(old.(*coreapi.Pod), new.(*coreapi.Pod)))
|
||||
}})
|
||||
|
||||
npController := makeController(client.Extensions().RESTClient(), "networkpolicies", &extnapi.NetworkPolicy{},
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
handleError(npc.AddNetworkPolicy(obj.(*extnapi.NetworkPolicy)))
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
switch obj := obj.(type) {
|
||||
case *extnapi.NetworkPolicy:
|
||||
handleError(npc.DeleteNetworkPolicy(obj))
|
||||
case cache.DeletedFinalStateUnknown:
|
||||
// We know this object has gone away, but its final state is no longer
|
||||
// available from the API server. Instead we use the last copy of it
|
||||
// that we have, which is good enough for our cleanup.
|
||||
handleError(npc.DeleteNetworkPolicy(obj.Obj.(*extnapi.NetworkPolicy)))
|
||||
}
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
handleError(npc.UpdateNetworkPolicy(old.(*extnapi.NetworkPolicy), new.(*extnapi.NetworkPolicy)))
|
||||
}})
|
||||
npHandlers := cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
handleError(npc.AddNetworkPolicy(obj))
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
switch obj := obj.(type) {
|
||||
case cache.DeletedFinalStateUnknown:
|
||||
// We know this object has gone away, but its final state is no longer
|
||||
// available from the API server. Instead we use the last copy of it
|
||||
// that we have, which is good enough for our cleanup.
|
||||
handleError(npc.DeleteNetworkPolicy(obj.Obj))
|
||||
default:
|
||||
handleError(npc.DeleteNetworkPolicy(obj))
|
||||
}
|
||||
},
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
handleError(npc.UpdateNetworkPolicy(old, new))
|
||||
},
|
||||
}
|
||||
if legacy {
|
||||
npController = makeController(client.Extensions().RESTClient(), "networkpolicies", &extnapi.NetworkPolicy{}, npHandlers)
|
||||
} else {
|
||||
npController = makeController(client.NetworkingV1().RESTClient(), "networkpolicies", &networkingv1.NetworkPolicy{}, npHandlers)
|
||||
}
|
||||
|
||||
go nsController.Run(wait.NeverStop)
|
||||
go podController.Run(wait.NeverStop)
|
||||
@@ -199,6 +243,8 @@ func main() {
|
||||
rootCmd.PersistentFlags().StringVar(&metricsAddr, "metrics-addr", ":6781", "metrics server bind address")
|
||||
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().StringVar(&nodeName, "node-name", "", "only generate rules that apply to this node")
|
||||
rootCmd.PersistentFlags().BoolVar(&legacy, "use-legacy-netpol", false, "use legacy network policies (pre k8s 1.7 vsn)")
|
||||
|
||||
handleError(rootCmd.Execute())
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
# This is a nearly-empty image that we use to create a data-only container for persistence
|
||||
FROM scratch
|
||||
LABEL maintainer "Weaveworks Inc <help@weave.works>"
|
||||
LABEL works.weave.role=system
|
||||
ENTRYPOINT ["data-only"]
|
||||
# Work round Docker refusing to save an empty image
|
||||
COPY Dockerfile /
|
||||
@@ -1,33 +1,9 @@
|
||||
FROM ALPINE_BASEIMAGE
|
||||
FROM DOCKERHUB_USER/weaveARCH_EXT
|
||||
|
||||
# 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/
|
||||
|
||||
LABEL maintainer "Weaveworks Inc <help@weave.works>"
|
||||
LABEL works.weave.role=system \
|
||||
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.url="https://weave.works" \
|
||||
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
||||
org.label-schema.vendor="Weaveworks"
|
||||
|
||||
WORKDIR /home/weave
|
||||
ENTRYPOINT ["/home/weave/sigproxy", "/home/weave/weave"]
|
||||
|
||||
RUN apk add --update \
|
||||
curl \
|
||||
ethtool \
|
||||
iptables \
|
||||
iproute2 \
|
||||
util-linux \
|
||||
conntrack-tools \
|
||||
bind-tools \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
ADD ./weave ./sigproxy ./weaveproxy ./symlink /home/weave/
|
||||
ADD ./weaveutil /usr/bin/
|
||||
ADD ./sigproxy ./symlink /home/weave/
|
||||
ADD ./weavewait /w/w
|
||||
ADD ./weavewait_noop /w-noop/w
|
||||
ADD ./weavewait_nomcast /w-nomcast/w
|
||||
ADD ./docker /usr/bin/
|
||||
WORKDIR /home/weave
|
||||
@@ -1,84 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/weaveworks/common/mflag"
|
||||
"github.com/weaveworks/common/mflagext"
|
||||
"github.com/weaveworks/common/signals"
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/proxy"
|
||||
)
|
||||
|
||||
var version = "unreleased"
|
||||
|
||||
var Log = common.Log
|
||||
|
||||
func getenv(key, defaultVal string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
justVersion bool
|
||||
logLevel = "info"
|
||||
c proxy.Config
|
||||
withDNS bool
|
||||
)
|
||||
|
||||
c.Version = version
|
||||
|
||||
mflag.BoolVar(&justVersion, []string{"#version", "-version"}, false, "print version and exit")
|
||||
mflag.StringVar(&logLevel, []string{"-log-level"}, "info", "logging level (debug, info, warning, error)")
|
||||
mflagext.ListVar(&c.ListenAddrs, []string{"H"}, nil, "addresses on which to listen")
|
||||
mflag.StringVar(&c.HostnameFromLabel, []string{"-hostname-from-label"}, "", "Key of container label from which to obtain the container's hostname")
|
||||
mflag.StringVar(&c.HostnameMatch, []string{"-hostname-match"}, "(.*)", "Regexp pattern to apply on container names (e.g. '^aws-[0-9]+-(.*)$')")
|
||||
mflag.StringVar(&c.HostnameReplacement, []string{"-hostname-replacement"}, "$1", "Expression to generate hostnames based on matches from --hostname-match (e.g. 'my-app-$1')")
|
||||
mflag.BoolVar(&c.RewriteInspect, []string{"-rewrite-inspect"}, false, "Rewrite 'inspect' calls to return the weave network settings (if attached)")
|
||||
mflag.BoolVar(&c.NoDefaultIPAM, []string{"#-no-default-ipam", "-no-default-ipalloc"}, false, "do not automatically allocate addresses for containers without a WEAVE_CIDR")
|
||||
mflag.BoolVar(&c.NoRewriteHosts, []string{"-no-rewrite-hosts"}, false, "do not automatically rewrite /etc/hosts. Use if you need the docker IP to remain in /etc/hosts")
|
||||
mflag.StringVar(&c.TLSConfig.CACert, []string{"#tlscacert", "-tlscacert"}, "", "Trust certs signed only by this CA")
|
||||
mflag.StringVar(&c.TLSConfig.Cert, []string{"#tlscert", "-tlscert"}, "", "Path to TLS certificate file")
|
||||
mflag.BoolVar(&c.TLSConfig.Enabled, []string{"#tls", "-tls"}, false, "Use TLS; implied by --tlsverify")
|
||||
mflag.StringVar(&c.TLSConfig.Key, []string{"#tlskey", "-tlskey"}, "", "Path to TLS key file")
|
||||
mflag.BoolVar(&c.TLSConfig.Verify, []string{"#tlsverify", "-tlsverify"}, false, "Use TLS and verify the remote")
|
||||
mflag.BoolVar(&withDNS, []string{"#-with-dns", "#w"}, false, "option removed")
|
||||
mflag.BoolVar(&c.WithoutDNS, []string{"-without-dns"}, false, "instruct created containers to never use weaveDNS as their nameserver")
|
||||
mflag.BoolVar(&c.NoMulticastRoute, []string{"-no-multicast-route"}, false, "do not add a multicast route via the weave interface when attaching containers")
|
||||
mflag.Parse()
|
||||
|
||||
if justVersion {
|
||||
fmt.Printf("weave proxy %s\n", version)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
common.SetLogLevel(logLevel)
|
||||
|
||||
Log.Infoln("weave proxy", version)
|
||||
Log.Infoln("Command line arguments:", strings.Join(os.Args[1:], " "))
|
||||
|
||||
if withDNS {
|
||||
Log.Warning("--with-dns option has been removed; DNS is on by default")
|
||||
}
|
||||
|
||||
c.Image = getenv("EXEC_IMAGE", "weaveworks/weaveexec")
|
||||
c.DockerBridge = getenv("DOCKER_BRIDGE", "docker0")
|
||||
c.DockerHost = getenv("DOCKER_HOST", "unix:///var/run/docker.sock")
|
||||
|
||||
p, err := proxy.NewProxy(c)
|
||||
if err != nil {
|
||||
Log.Fatalf("Could not start proxy: %s", err)
|
||||
}
|
||||
defer p.Stop()
|
||||
|
||||
listeners := p.Listen()
|
||||
p.AttachExistingContainers()
|
||||
go p.Serve(listeners)
|
||||
go p.ListenAndServeStatus("/home/weave/status.sock")
|
||||
signals.SignalHandlerLoop(common.Log)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
weavetest "github.com/weaveworks/weave/testing"
|
||||
)
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
weavetest.TrimTestArgs()
|
||||
main()
|
||||
}
|
||||
@@ -1,6 +1,31 @@
|
||||
FROM DOCKERHUB_USER/weaveexecARCH_EXT
|
||||
WORKDIR /home/weave
|
||||
ADD ./weaver /home/weave/
|
||||
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
|
||||
# If we're building normally, for amd64, CROSS_BUILD lines are removed
|
||||
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
||||
|
||||
LABEL maintainer "Weaveworks Inc <help@weave.works>"
|
||||
LABEL works.weave.role=system \
|
||||
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.url="https://weave.works" \
|
||||
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
||||
org.label-schema.vendor="Weaveworks"
|
||||
|
||||
RUN apk add --update \
|
||||
curl \
|
||||
ethtool \
|
||||
iptables \
|
||||
ipset \
|
||||
iproute2 \
|
||||
util-linux \
|
||||
conntrack-tools \
|
||||
bind-tools \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
ADD ./weave ./weaver /home/weave/
|
||||
ADD ./weaveutil /usr/bin/
|
||||
ADD weavedata.db /weavedb/
|
||||
COPY ca-certificates.crt /etc/ssl/certs/
|
||||
ENTRYPOINT ["/home/weave/weaver"]
|
||||
WORKDIR /home/weave
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/go-checkpoint"
|
||||
weave "github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
var checker *checkpoint.Checker
|
||||
@@ -16,7 +17,7 @@ const (
|
||||
updateCheckPeriod = 6 * time.Hour
|
||||
)
|
||||
|
||||
func checkForUpdates(dockerVersion string, network string) {
|
||||
func checkForUpdates(dockerVersion string, router *weave.NetworkRouter) {
|
||||
newVersion.Store("")
|
||||
success.Store(true)
|
||||
|
||||
@@ -51,9 +52,6 @@ func checkForUpdates(dockerVersion string, network string) {
|
||||
"docker-version": dockerVersion,
|
||||
"kernel-version": kernelVersion,
|
||||
}
|
||||
if network != "" {
|
||||
flags["network"] = network
|
||||
}
|
||||
|
||||
// Start background version checking
|
||||
params := checkpoint.CheckParams{
|
||||
@@ -61,6 +59,21 @@ func checkForUpdates(dockerVersion string, network string) {
|
||||
Version: version,
|
||||
SignatureFile: "",
|
||||
Flags: flags,
|
||||
ExtraFlags: func() []checkpoint.Flag { return checkpointFlags(router) },
|
||||
}
|
||||
checker = checkpoint.CheckInterval(¶ms, updateCheckPeriod, handleResponse)
|
||||
}
|
||||
|
||||
func checkpointFlags(router *weave.NetworkRouter) []checkpoint.Flag {
|
||||
flags := []checkpoint.Flag{}
|
||||
status := weave.NewNetworkRouterStatus(router)
|
||||
for _, conn := range status.Connections {
|
||||
if connectionName, ok := conn.Attrs["name"].(string); ok {
|
||||
if _, encrypted := conn.Attrs["encrypted"]; encrypted {
|
||||
connectionName = connectionName + " encrypted"
|
||||
}
|
||||
flags = append(flags, checkpoint.Flag{Key: "network", Value: connectionName})
|
||||
}
|
||||
}
|
||||
return flags
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/weaveworks/weave/ipam"
|
||||
)
|
||||
|
||||
// TODO: move these definitions somewhere more shareable
|
||||
type PeerUpdateRequest struct {
|
||||
Name string `json:"peername"`
|
||||
Nickname string `json:"nickname"` // optional
|
||||
Addresses []string `json:"addresses"` // can be empty
|
||||
}
|
||||
|
||||
type PeerUpdateResponse struct {
|
||||
Addresses []string `json:"addresses"`
|
||||
PeerCount int `json:"peercount"`
|
||||
}
|
||||
|
||||
var httpClient = &http.Client{Timeout: 30 * time.Second}
|
||||
|
||||
func do(verb string, discoveryEndpoint, token string, request interface{}, response interface{}) error {
|
||||
body := new(bytes.Buffer)
|
||||
err := json.NewEncoder(body).Encode(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := http.NewRequest(verb, discoveryEndpoint+"/peer", body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Scope-Probe token=%s", token))
|
||||
req.Header.Set("X-Weave-Net-Version", version)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
Log.Printf("Calling peer discovery %s with %s", req.URL, request)
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
rbody, _ := ioutil.ReadAll(resp.Body)
|
||||
return errors.New(resp.Status + ": " + string(rbody))
|
||||
}
|
||||
if response == nil {
|
||||
return nil
|
||||
}
|
||||
err = json.NewDecoder(resp.Body).Decode(response)
|
||||
Log.Printf("peer discovery result: (%v) %v", err, response)
|
||||
return err
|
||||
}
|
||||
|
||||
func peerDiscoveryUpdate(discoveryEndpoint, token, peername, nickname string, addresses []string) ([]string, int, error) {
|
||||
request := PeerUpdateRequest{
|
||||
Name: peername,
|
||||
Nickname: nickname,
|
||||
Addresses: addresses,
|
||||
}
|
||||
var updateResponse PeerUpdateResponse
|
||||
err := do("POST", discoveryEndpoint, token, request, &updateResponse)
|
||||
return updateResponse.Addresses, updateResponse.PeerCount, err
|
||||
}
|
||||
|
||||
func peerDiscoveryDelete(discoveryEndpoint, token, peername string) error {
|
||||
request := PeerUpdateRequest{Name: peername}
|
||||
return do("DELETE", discoveryEndpoint, token, request, nil)
|
||||
}
|
||||
|
||||
func HandleHTTPPeer(router *mux.Router, alloc *ipam.Allocator, discoveryEndpoint, token, peername string) {
|
||||
router.Methods("DELETE").Path("/peer").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if discoveryEndpoint != "" && token != "" {
|
||||
if err := peerDiscoveryDelete(discoveryEndpoint, token, peername); err != nil {
|
||||
Log.Errorf("Error while deleting self from peer discovery: %s", err)
|
||||
}
|
||||
}
|
||||
if alloc != nil {
|
||||
alloc.Shutdown()
|
||||
}
|
||||
w.WriteHeader(204)
|
||||
})
|
||||
|
||||
router.Methods("DELETE").Path("/peer/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ident := mux.Vars(r)["id"]
|
||||
if discoveryEndpoint != "" && token != "" {
|
||||
// TODO: deal with this being either a peername or a nickname
|
||||
if err := peerDiscoveryDelete(discoveryEndpoint, token, ident); err != nil {
|
||||
Log.Errorf("Error while deleting self from peer discovery: %s", err)
|
||||
}
|
||||
}
|
||||
if alloc != nil {
|
||||
transferred := alloc.AdminTakeoverRanges(ident)
|
||||
fmt.Fprintf(w, "%d IPs taken over from %s\n", transferred, ident)
|
||||
}
|
||||
})
|
||||
}
|
||||
+52
-21
@@ -13,9 +13,12 @@ import (
|
||||
"github.com/weaveworks/mesh"
|
||||
|
||||
"github.com/weaveworks/go-checkpoint"
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/ipam"
|
||||
"github.com/weaveworks/weave/nameserver"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/plugin"
|
||||
"github.com/weaveworks/weave/proxy"
|
||||
weave "github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
@@ -31,23 +34,7 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
||||
},
|
||||
"printIPAMRanges": func(router weave.NetworkRouterStatus, status ipam.Status) string {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
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
|
||||
}
|
||||
peerStats := summariseIpamStats(&status)
|
||||
|
||||
printOwned := func(name string, nickName string, info string, ips uint32) {
|
||||
percentageRanges := float32(ips) * 100.0 / float32(status.RangeNumIPs)
|
||||
@@ -113,6 +100,25 @@ var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
||||
"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 {
|
||||
count := 0
|
||||
for _, entry := range entries {
|
||||
@@ -178,7 +184,7 @@ var statusTemplate = defTemplate("status", `\
|
||||
{{if allIPAMOwnersUnreachable .IPAM}}\
|
||||
Status: all IP ranges owned by unreachable peers - use 'rmpeer' if they are dead
|
||||
{{else if len .IPAM.PendingAllocates}}\
|
||||
Status: waiting for IP range grant from peers
|
||||
Status: waiting for IP(s) to become available
|
||||
{{else}}\
|
||||
Status: ready
|
||||
{{end}}\
|
||||
@@ -202,6 +208,20 @@ var statusTemplate = defTemplate("status", `\
|
||||
TTL: {{.DNS.TTL}}
|
||||
Entries: {{countDNSEntries .DNS.Entries}}
|
||||
{{end}}\
|
||||
{{if .Proxy}}\
|
||||
|
||||
Service: proxy
|
||||
Address: {{printList .Proxy.Addresses}}
|
||||
{{end}}\
|
||||
{{if .Plugin}}\
|
||||
|
||||
{{if .Plugin.Config.EnableV2}}\
|
||||
Service: plugin (v2)
|
||||
{{else}}\
|
||||
Service: plugin (legacy)
|
||||
DriverName: {{.Plugin.DriverName}}
|
||||
{{end}}\
|
||||
{{end}}\
|
||||
`)
|
||||
|
||||
var targetsTemplate = defTemplate("targetsTemplate", `\
|
||||
@@ -273,22 +293,29 @@ func (v *VersionCheck) String() string {
|
||||
}
|
||||
|
||||
type WeaveStatus struct {
|
||||
Ready bool
|
||||
Version string
|
||||
VersionCheck *VersionCheck `json:"VersionCheck,omitempty"`
|
||||
Router *weave.NetworkRouterStatus `json:"Router,omitempty"`
|
||||
IPAM *ipam.Status `json:"IPAM,omitempty"`
|
||||
DNS *nameserver.Status `json:"DNS,omitempty"`
|
||||
Proxy *proxy.Status `json:"Proxy,omitempty"`
|
||||
Plugin *plugin.Status `json:"Plugin,omitempty"`
|
||||
}
|
||||
|
||||
// Read-only functions, suitable for exposing on an unprotected socket
|
||||
func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRouter, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) {
|
||||
func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRouter, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer, prxy *proxy.Proxy, plugin *plugin.Plugin, waitReady *common.WaitGroup) {
|
||||
status := func() WeaveStatus {
|
||||
return WeaveStatus{
|
||||
waitReady.IsDone(),
|
||||
version,
|
||||
versionCheck(),
|
||||
weave.NewNetworkRouterStatus(router),
|
||||
ipam.NewStatus(allocator, defaultSubnet),
|
||||
nameserver.NewStatus(ns, dnsserver)}
|
||||
nameserver.NewStatus(ns, dnsserver),
|
||||
proxy.NewStatus(prxy),
|
||||
plugin.NewStatus(),
|
||||
}
|
||||
}
|
||||
muxRouter.Methods("GET").Path("/report").Headers("Accept", "application/json").HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -324,7 +351,11 @@ func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRout
|
||||
defHandler := func(path string, template *template.Template) {
|
||||
muxRouter.Methods("GET").Path(path).HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := template.Execute(w, status()); err != nil {
|
||||
s := status()
|
||||
if !s.Ready {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
}
|
||||
if err := template.Execute(w, s); err != nil {
|
||||
http.Error(w, "error during template execution", http.StatusInternalServerError)
|
||||
Log.Error(err)
|
||||
}
|
||||
|
||||
+241
-91
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/profile"
|
||||
"github.com/weaveworks/common/mflag"
|
||||
"github.com/weaveworks/common/mflagext"
|
||||
"github.com/weaveworks/common/signals"
|
||||
"github.com/weaveworks/mesh"
|
||||
|
||||
@@ -25,6 +26,8 @@ import (
|
||||
"github.com/weaveworks/weave/nameserver"
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/plugin"
|
||||
weaveproxy "github.com/weaveworks/weave/proxy"
|
||||
weave "github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
@@ -53,7 +56,7 @@ type dnsConfig struct {
|
||||
func (c *ipamConfig) Enabled() bool {
|
||||
var (
|
||||
hasPeerCount = c.PeerCount > 0
|
||||
hasMode = len(c.Mode) > 0
|
||||
hasMode = c.HasMode()
|
||||
hasRange = c.IPRangeCIDR != ""
|
||||
hasSubnet = c.IPSubnetCIDR != ""
|
||||
)
|
||||
@@ -63,9 +66,7 @@ func (c *ipamConfig) Enabled() bool {
|
||||
case !hasRange && hasSubnet:
|
||||
Log.Fatal("--ipalloc-default-subnet specified without --ipalloc-range.")
|
||||
case !hasRange:
|
||||
Log.Fatal("--ipalloc-init or --init-peer-count specified without --ipalloc-range.")
|
||||
case hasMode && hasPeerCount:
|
||||
Log.Fatal("At most one of --ipalloc-init or --init-peer-count may be specified.")
|
||||
Log.Fatal("--ipalloc-init specified without --ipalloc-range.")
|
||||
}
|
||||
if hasMode {
|
||||
if err := c.parseMode(); err != nil {
|
||||
@@ -75,6 +76,10 @@ func (c *ipamConfig) Enabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c ipamConfig) HasMode() bool {
|
||||
return len(c.Mode) > 0
|
||||
}
|
||||
|
||||
func (c *ipamConfig) parseMode() error {
|
||||
modeAndParam := strings.SplitN(c.Mode, "=", 2)
|
||||
|
||||
@@ -108,6 +113,13 @@ func (c *ipamConfig) parseMode() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getenvOrDefault(key, defaultVal string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
func main() {
|
||||
procs := runtime.NumCPU()
|
||||
// packet sniffing can block an OS thread, so we need one thread
|
||||
@@ -119,16 +131,17 @@ func main() {
|
||||
|
||||
var (
|
||||
justVersion bool
|
||||
justPeerName bool
|
||||
config mesh.Config
|
||||
bridgeConfig weavenet.BridgeConfig
|
||||
networkConfig weave.NetworkConfig
|
||||
protocolMinVersion int
|
||||
resume bool
|
||||
ifaceName string
|
||||
routerName string
|
||||
nickName string
|
||||
password string
|
||||
pktdebug bool
|
||||
logLevel string
|
||||
logLevel = "info"
|
||||
prof string
|
||||
bufSzMB int
|
||||
noDiscovery bool
|
||||
@@ -139,40 +152,42 @@ func main() {
|
||||
peers []string
|
||||
noDNS bool
|
||||
dnsConfig dnsConfig
|
||||
datapathName string
|
||||
trustedSubnetStr string
|
||||
dbPrefix string
|
||||
isAWSVPC bool
|
||||
|
||||
defaultDockerHost = "unix:///var/run/docker.sock"
|
||||
hostRoot string
|
||||
procPath string
|
||||
discoveryEndpoint string
|
||||
token string
|
||||
advertiseAddress string
|
||||
pluginConfig plugin.Config
|
||||
defaultDockerHost = getenvOrDefault("DOCKER_HOST", "unix:///var/run/docker.sock")
|
||||
)
|
||||
|
||||
if val := os.Getenv("DOCKER_HOST"); val != "" {
|
||||
defaultDockerHost = val
|
||||
}
|
||||
|
||||
mflag.BoolVar(&justVersion, []string{"#version", "-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.IntVar(&config.Port, []string{"#port", "-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.BoolVar(&resume, []string{"-resume"}, false, "resume connections to previous peers")
|
||||
mflag.StringVar(&ifaceName, []string{"#iface", "-iface"}, "", "name of interface to capture/inject from (disabled if blank)")
|
||||
mflag.StringVar(&routerName, []string{"#name", "-name"}, "", "name of router (defaults to MAC of interface)")
|
||||
mflag.StringVar(&nickName, []string{"#nickname", "-nickname"}, "", "nickname of peer (defaults to hostname)")
|
||||
mflag.StringVar(&password, []string{"#password", "-password"}, "", "network password")
|
||||
mflag.StringVar(&bridgeConfig.WeaveBridgeName, []string{"-weave-bridge"}, "weave", "name of weave bridge")
|
||||
mflag.StringVar(&bridgeConfig.DockerBridgeName, []string{"-docker-bridge"}, "", "name of Docker bridge")
|
||||
mflag.BoolVar(&bridgeConfig.NPC, []string{"-expect-npc"}, false, "set up iptables rules for npc")
|
||||
mflag.StringVar(&routerName, []string{"-name"}, "", "name of router (defaults to MAC of interface)")
|
||||
mflag.StringVar(&nickName, []string{"-nickname"}, "", "nickname of peer (defaults to hostname)")
|
||||
mflag.StringVar(&password, []string{"-password"}, "", "network password")
|
||||
mflag.StringVar(&logLevel, []string{"-log-level"}, "info", "logging level (debug, info, warning, error)")
|
||||
mflag.BoolVar(&pktdebug, []string{"#pktdebug", "#-pktdebug", "-pkt-debug"}, false, "enable per-packet debug logging")
|
||||
mflag.StringVar(&prof, []string{"#profile", "-profile"}, "", "enable profiling and write profiles to given path")
|
||||
mflag.IntVar(&config.ConnLimit, []string{"#connlimit", "#-connlimit", "-conn-limit"}, 30, "connection limit (0 for unlimited)")
|
||||
mflag.BoolVar(&noDiscovery, []string{"#nodiscovery", "#-nodiscovery", "-no-discovery"}, false, "disable peer discovery")
|
||||
mflag.IntVar(&bufSzMB, []string{"#bufsz", "-bufsz"}, 8, "capture buffer size in MB")
|
||||
mflag.StringVar(&httpAddr, []string{"#httpaddr", "#-httpaddr", "-http-addr"}, "", "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||
mflag.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.IntVar(&config.ConnLimit, []string{"-conn-limit"}, 30, "connection limit (0 for unlimited)")
|
||||
mflag.BoolVar(&noDiscovery, []string{"-no-discovery"}, false, "disable peer discovery")
|
||||
mflag.IntVar(&bufSzMB, []string{"-bufsz"}, 8, "capture buffer size in MB")
|
||||
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(&statusAddr, []string{"-status-addr"}, "", "address to bind status+metrics interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||
mflag.StringVar(&ipamConfig.Mode, []string{"-ipalloc-init"}, "", "allocator initialisation strategy (consensus, seed or observer)")
|
||||
mflag.StringVar(&ipamConfig.IPRangeCIDR, []string{"#iprange", "#-iprange", "-ipalloc-range"}, "", "IP address range reserved for automatic allocation, in CIDR notation")
|
||||
mflag.StringVar(&ipamConfig.IPSubnetCIDR, []string{"#ipsubnet", "#-ipsubnet", "-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
|
||||
mflag.IntVar(&ipamConfig.PeerCount, []string{"#initpeercount", "#-initpeercount", "-init-peer-count"}, 0, "number of peers in network (for IP address allocation)")
|
||||
mflag.StringVar(&dockerAPI, []string{"#api", "#-api", "-docker-api"}, defaultDockerHost, "Docker API endpoint")
|
||||
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(&dockerAPI, []string{"-docker-api"}, defaultDockerHost, "Docker API endpoint")
|
||||
mflag.BoolVar(&noDNS, []string{"-no-dns"}, false, "disable DNS server")
|
||||
mflag.StringVar(&dnsConfig.Domain, []string{"-dns-domain"}, nameserver.DefaultDomain, "local domain to server requests for")
|
||||
mflag.StringVar(&dnsConfig.ListenAddress, []string{"-dns-listen-address"}, nameserver.DefaultListenAddress, "address to listen on for DNS requests")
|
||||
@@ -180,10 +195,24 @@ func main() {
|
||||
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(&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.StringVar(&trustedSubnetStr, []string{"-trusted-subnets"}, "", "comma-separated list of trusted subnets in CIDR notation")
|
||||
mflag.StringVar(&dbPrefix, []string{"-db-prefix"}, "/weavedb/weave", "pathname/prefix of filename to store data")
|
||||
mflag.BoolVar(&isAWSVPC, []string{"-awsvpc"}, false, "use AWS VPC for routing")
|
||||
mflag.StringVar(&procPath, []string{"-proc-path"}, "/proc", "path to reach host /proc filesystem")
|
||||
mflag.BoolVar(&bridgeConfig.AWSVPC, []string{"-awsvpc"}, false, "use AWS VPC for routing")
|
||||
mflag.StringVar(&hostRoot, []string{"-host-root"}, "", "path to reach host filesystem")
|
||||
mflag.StringVar(&discoveryEndpoint, []string{"-peer-discovery-url"}, "https://cloud.weave.works/api/net", "url for peer discovery")
|
||||
mflag.StringVar(&token, []string{"-token"}, "", "token for peer discovery")
|
||||
mflag.StringVar(&advertiseAddress, []string{"-advertise-address"}, "", "address to advertise for peer discovery")
|
||||
|
||||
mflag.BoolVar(&pluginConfig.Enable, []string{"-plugin"}, false, "enable Docker plugin (v1)")
|
||||
mflag.BoolVar(&pluginConfig.EnableV2, []string{"-plugin-v2"}, false, "enable 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.MeshSocket, []string{"-plugin-mesh-socket"}, "/run/docker/plugins/weavemesh.sock", "plugin socket on which to listen in mesh mode")
|
||||
|
||||
proxyConfig := newProxyConfig()
|
||||
|
||||
// crude way of detecting that we probably have been started in a
|
||||
// container, with `weave launch` --> suppress misleading paths in
|
||||
@@ -195,19 +224,30 @@ func main() {
|
||||
|
||||
mflag.Parse()
|
||||
|
||||
if justVersion {
|
||||
fmt.Printf("weave %s\n", version)
|
||||
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()
|
||||
if resume && len(peers) > 0 {
|
||||
Log.Fatalf("You must not specify an initial peer list in conjunction with --resume")
|
||||
}
|
||||
|
||||
common.SetLogLevel(logLevel)
|
||||
|
||||
if justVersion {
|
||||
fmt.Printf("weave router %s\n", version)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
Log.Println("Command line options:", options())
|
||||
Log.Infoln("weave ", version)
|
||||
|
||||
if prof != "" {
|
||||
defer profile.Start(profile.CPUProfile, profile.ProfilePath(prof), profile.NoShutdownHook).Stop()
|
||||
@@ -218,24 +258,56 @@ func main() {
|
||||
}
|
||||
config.ProtocolMinVersion = byte(protocolMinVersion)
|
||||
|
||||
var waitReady common.WaitGroup
|
||||
|
||||
var proxy *weaveproxy.Proxy
|
||||
var err error
|
||||
if proxyConfig.Enabled {
|
||||
if noDNS {
|
||||
proxyConfig.WithoutDNS = true
|
||||
}
|
||||
// Start Weave Proxy:
|
||||
proxy, err = weaveproxy.NewProxy(*proxyConfig)
|
||||
if err != nil {
|
||||
Log.Fatalf("Could not start proxy: %s", err)
|
||||
}
|
||||
defer proxy.Stop()
|
||||
listeners := proxy.Listen()
|
||||
proxy.AttachExistingContainers()
|
||||
go proxy.Serve(listeners, waitReady.Add())
|
||||
}
|
||||
|
||||
if pktdebug {
|
||||
networkConfig.PacketLogging = packetLogging{}
|
||||
} else {
|
||||
networkConfig.PacketLogging = nopPacketLogging{}
|
||||
}
|
||||
|
||||
config.Password = determinePassword(password)
|
||||
|
||||
overlay, bridge := createOverlay(datapathName, ifaceName, isAWSVPC, config.Host, config.Port, bufSzMB, config.Password != nil)
|
||||
networkConfig.Bridge = bridge
|
||||
|
||||
if bridge != nil {
|
||||
if err := weavenet.DetectHairpin("vethwe-bridge", Log); err != nil {
|
||||
Log.Errorf("DetectHairpin failed: %s", err)
|
||||
if bridgeConfig.DockerBridgeName != "" {
|
||||
if setAddr, err := weavenet.EnforceAddrAssignType(bridgeConfig.DockerBridgeName); err != nil {
|
||||
Log.Errorf("While checking address assignment type of %s: %s", bridgeConfig.DockerBridgeName, err)
|
||||
} else if setAddr {
|
||||
Log.Warningf("Setting %s MAC (mitigate https://github.com/docker/docker/issues/14908)", bridgeConfig.DockerBridgeName)
|
||||
}
|
||||
}
|
||||
|
||||
name := peerName(routerName)
|
||||
bridgeConfig.Mac = name.String()
|
||||
bridgeConfig.Port = config.Port
|
||||
bridgeType, err := weavenet.EnsureBridge(procPath, &bridgeConfig, Log)
|
||||
checkFatal(err)
|
||||
Log.Println("Bridge type is", bridgeType)
|
||||
|
||||
config.Password = determinePassword(password)
|
||||
|
||||
overlay, injectorConsumer := createOverlay(bridgeType, bridgeConfig, config.Host, config.Port, bufSzMB, config.Password != nil)
|
||||
networkConfig.InjectorConsumer = injectorConsumer
|
||||
|
||||
if injectorConsumer != nil {
|
||||
if err := weavenet.DetectHairpin("vethwe-bridge", Log); err != nil {
|
||||
Log.Errorf("Setting may cause connectivity issues : %s", err)
|
||||
Log.Infof("Hairpin mode may have been enabled by other software on this machine")
|
||||
}
|
||||
}
|
||||
|
||||
if nickName == "" {
|
||||
var err error
|
||||
@@ -246,18 +318,39 @@ func main() {
|
||||
config.TrustedSubnets = parseTrustedSubnets(trustedSubnetStr)
|
||||
config.PeerDiscovery = !noDiscovery
|
||||
|
||||
if isAWSVPC && len(config.Password) > 0 {
|
||||
if bridgeConfig.AWSVPC && len(config.Password) > 0 {
|
||||
Log.Fatalf("--awsvpc mode is not compatible with the --password option")
|
||||
}
|
||||
if bridgeConfig.AWSVPC && !ipamConfig.Enabled() {
|
||||
Log.Fatalf("--awsvpc mode requires IPAM enabled")
|
||||
}
|
||||
|
||||
db, err := db.NewBoltDB(dbPrefix + db.FileName)
|
||||
db, err := db.NewBoltDB(dbPrefix)
|
||||
checkFatal(err)
|
||||
defer db.Close()
|
||||
|
||||
router := weave.NewNetworkRouter(config, networkConfig, name, nickName, overlay, db)
|
||||
router, err := weave.NewNetworkRouter(config, networkConfig, bridgeConfig, name, nickName, overlay, db)
|
||||
checkFatal(err)
|
||||
Log.Println("Our name is", router.Ourself)
|
||||
|
||||
if peers, err = router.InitialPeers(resume, peers); err != nil {
|
||||
if token != "" {
|
||||
var addresses []string
|
||||
if advertiseAddress == "" {
|
||||
localAddrs, err := weavenet.LocalAddresses()
|
||||
checkFatal(err)
|
||||
for _, addr := range localAddrs {
|
||||
addresses = append(addresses, addr.IP.String())
|
||||
}
|
||||
} else {
|
||||
addresses = strings.Split(advertiseAddress, ",")
|
||||
}
|
||||
discoveredPeers, count, err := peerDiscoveryUpdate(discoveryEndpoint, token, name.String(), nickName, addresses)
|
||||
checkFatal(err)
|
||||
if !ipamConfig.HasMode() {
|
||||
ipamConfig.PeerCount = len(peers) + count
|
||||
}
|
||||
peers = append(peers, discoveredPeers...)
|
||||
} else if peers, err = router.InitialPeers(resume, peers); err != nil {
|
||||
Log.Fatal("Unable to get initial peer set: ", err)
|
||||
}
|
||||
|
||||
@@ -274,11 +367,7 @@ func main() {
|
||||
dockerVersion = dockerCli.DockerVersion()
|
||||
}
|
||||
|
||||
network := ""
|
||||
if isAWSVPC {
|
||||
network = "awsvpc"
|
||||
}
|
||||
checkForUpdates(dockerVersion, network)
|
||||
checkForUpdates(dockerVersion, router)
|
||||
|
||||
observeContainers := func(o docker.ContainerObserver) {
|
||||
if dockerCli != nil {
|
||||
@@ -298,24 +387,26 @@ func main() {
|
||||
)
|
||||
if ipamConfig.Enabled() {
|
||||
var t tracker.LocalRangeTracker
|
||||
if isAWSVPC {
|
||||
if bridgeConfig.AWSVPC {
|
||||
Log.Infoln("Creating AWSVPC LocalRangeTracker")
|
||||
t, err = tracker.NewAWSVPCTracker()
|
||||
t, err = tracker.NewAWSVPCTracker(bridgeConfig.WeaveBridgeName)
|
||||
if err != nil {
|
||||
Log.Fatalf("Cannot create AWSVPC LocalRangeTracker: %s", err)
|
||||
}
|
||||
trackerName = "awsvpc"
|
||||
}
|
||||
var allContainerIDs []string
|
||||
if dockerCli != nil {
|
||||
allContainerIDs, err = dockerCli.AllContainerIDs()
|
||||
checkFatal(err)
|
||||
}
|
||||
preClaims, err := findExistingAddresses(dockerCli, allContainerIDs, weavenet.WeaveBridgeName)
|
||||
|
||||
preClaims, err := findExistingAddresses(dockerCli, bridgeConfig.WeaveBridgeName)
|
||||
checkFatal(err)
|
||||
|
||||
allocator, defaultSubnet = createAllocator(router, ipamConfig, preClaims, db, t, isKnownPeer)
|
||||
observeContainers(allocator)
|
||||
allocator.PruneOwned(allContainerIDs)
|
||||
|
||||
if dockerCli != nil {
|
||||
allContainerIDs, err := dockerCli.RunningContainerIDs()
|
||||
checkFatal(err)
|
||||
allocator.PruneOwned(allContainerIDs)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -328,6 +419,9 @@ func main() {
|
||||
ns.Start()
|
||||
defer ns.Stop()
|
||||
dnsserver.ActivateAndServe()
|
||||
if dockerCli != nil {
|
||||
populateDNS(ns, dockerCli, name, bridgeConfig.WeaveBridgeName)
|
||||
}
|
||||
defer dnsserver.Stop()
|
||||
}
|
||||
|
||||
@@ -337,6 +431,10 @@ func main() {
|
||||
}
|
||||
checkFatal(router.CreateRestartSentinel())
|
||||
|
||||
pluginConfig.DNS = !noDNS
|
||||
pluginConfig.DefaultSubnet = defaultSubnet.String()
|
||||
plugin := plugin.NewPlugin(pluginConfig)
|
||||
|
||||
// The weave script always waits for a status call to succeed,
|
||||
// so there is no point in doing "weave launch --http-addr ''".
|
||||
// This is here to support stand-alone use of weaver.
|
||||
@@ -349,8 +447,12 @@ func main() {
|
||||
ns.HandleHTTP(muxRouter, dockerCli)
|
||||
}
|
||||
router.HandleHTTP(muxRouter)
|
||||
HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver)
|
||||
HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver, proxy, plugin, &waitReady)
|
||||
HandleHTTPPeer(muxRouter, allocator, discoveryEndpoint, token, name.String())
|
||||
muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver))
|
||||
if proxy != nil {
|
||||
muxRouter.Methods("GET").Path("/proxyaddrs").HandlerFunc(proxy.StatusHTTP)
|
||||
}
|
||||
http.Handle("/", common.LoggingHTTPHandler(muxRouter))
|
||||
Log.Println("Listening for HTTP control messages on", httpAddr)
|
||||
go listenAndServeHTTP(httpAddr, nil)
|
||||
@@ -358,7 +460,7 @@ func main() {
|
||||
|
||||
if statusAddr != "" {
|
||||
muxRouter := mux.NewRouter()
|
||||
HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver)
|
||||
HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver, proxy, plugin, &waitReady)
|
||||
muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver))
|
||||
statusMux := http.NewServeMux()
|
||||
statusMux.Handle("/", muxRouter)
|
||||
@@ -366,16 +468,37 @@ func main() {
|
||||
go listenAndServeHTTP(statusAddr, statusMux)
|
||||
}
|
||||
|
||||
if plugin != nil {
|
||||
go plugin.Start(httpAddr, dockerCli, waitReady.Add())
|
||||
}
|
||||
|
||||
if bridgeConfig.AWSVPC {
|
||||
// Run this on its own goroutine because the allocator can block
|
||||
// We remove the default route installed by the kernel,
|
||||
// because awsvpc has installed it as well
|
||||
go exposeForAWSVPC(allocator, defaultSubnet, bridgeConfig.WeaveBridgeName, waitReady.Add())
|
||||
}
|
||||
|
||||
signals.SignalHandlerLoop(common.Log, router)
|
||||
}
|
||||
|
||||
func exposeForAWSVPC(alloc *ipam.Allocator, subnet address.CIDR, bridgeName string, ready func()) {
|
||||
addr, err := alloc.Allocate("weave:expose", subnet, false, func() bool { return false })
|
||||
checkFatal(err)
|
||||
cidr := address.MakeCIDR(subnet, addr)
|
||||
err = weavenet.Expose(bridgeName, cidr.IPNet(), true, false)
|
||||
checkFatal(err)
|
||||
Log.Printf("Bridge %q exposed on address %v", bridgeName, cidr)
|
||||
ready()
|
||||
}
|
||||
|
||||
func options() map[string]string {
|
||||
options := make(map[string]string)
|
||||
mflag.Visit(func(f *mflag.Flag) {
|
||||
value := f.Value.String()
|
||||
name := canonicalName(f)
|
||||
if name == "password" {
|
||||
value = "<elided>"
|
||||
if name == "password" || name == "token" {
|
||||
value = "<redacted>"
|
||||
}
|
||||
options[name] = value
|
||||
})
|
||||
@@ -409,34 +532,55 @@ func (nopPacketLogging) LogPacket(string, weave.PacketKey) {
|
||||
func (nopPacketLogging) LogForwardPacket(string, weave.ForwardPacketKey) {
|
||||
}
|
||||
|
||||
func createOverlay(datapathName string, ifaceName string, isAWSVPC bool, host string, port int, bufSzMB int, enableEncryption bool) (weave.NetworkOverlay, weave.Bridge) {
|
||||
func newProxyConfig() *weaveproxy.Config {
|
||||
proxyConfig := weaveproxy.Config{
|
||||
Image: getenvOrDefault("EXEC_IMAGE", "weaveworks/weaveexec"),
|
||||
DockerBridge: getenvOrDefault("DOCKER_BRIDGE", "docker0"),
|
||||
}
|
||||
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")
|
||||
mflag.StringVar(&proxyConfig.HostnameFromLabel, []string{"-hostname-from-label"}, "", "Key of container label from which to obtain the container's hostname")
|
||||
mflag.StringVar(&proxyConfig.HostnameMatch, []string{"-hostname-match"}, "(.*)", "Regexp pattern to apply on container names (e.g. '^aws-[0-9]+-(.*)$')")
|
||||
mflag.StringVar(&proxyConfig.HostnameReplacement, []string{"-hostname-replacement"}, "$1", "Expression to generate hostnames based on matches from --hostname-match (e.g. 'my-app-$1')")
|
||||
mflag.BoolVar(&proxyConfig.RewriteInspect, []string{"-rewrite-inspect"}, false, "Rewrite 'inspect' calls to return the weave network settings (if attached)")
|
||||
mflag.BoolVar(&proxyConfig.NoDefaultIPAM, []string{"-no-default-ipalloc"}, false, "proxy: do not automatically allocate addresses for containers without a WEAVE_CIDR")
|
||||
mflag.BoolVar(&proxyConfig.NoRewriteHosts, []string{"-no-rewrite-hosts"}, false, "proxy: do not automatically rewrite /etc/hosts. Use if you need the docker IP to remain in /etc/hosts")
|
||||
mflag.StringVar(&proxyConfig.TLSConfig.CACert, []string{"-tlscacert"}, "", "Trust certs signed only by this CA")
|
||||
mflag.StringVar(&proxyConfig.TLSConfig.Cert, []string{"-tlscert"}, "", "Path to TLS certificate file")
|
||||
mflag.BoolVar(&proxyConfig.TLSConfig.Enabled, []string{"-tls"}, false, "Use TLS; implied by --tlsverify")
|
||||
mflag.StringVar(&proxyConfig.TLSConfig.Key, []string{"-tlskey"}, "", "Path to TLS key file")
|
||||
mflag.BoolVar(&proxyConfig.TLSConfig.Verify, []string{"-tlsverify"}, false, "Use TLS and verify the remote")
|
||||
mflag.BoolVar(&proxyConfig.WithoutDNS, []string{"-without-dns"}, false, "proxy: instruct created containers to never use weaveDNS as their nameserver")
|
||||
mflag.BoolVar(&proxyConfig.NoMulticastRoute, []string{"-no-multicast-route"}, false, "proxy: do not add a multicast route via the weave interface when attaching containers")
|
||||
return &proxyConfig
|
||||
}
|
||||
|
||||
func createOverlay(bridgeType weavenet.Bridge, config weavenet.BridgeConfig, host string, port int, bufSzMB int, enableEncryption bool) (weave.NetworkOverlay, weave.InjectorConsumer) {
|
||||
overlay := weave.NewOverlaySwitch()
|
||||
var bridge weave.Bridge
|
||||
var injectorConsumer weave.InjectorConsumer
|
||||
var ignoreSleeve bool
|
||||
|
||||
switch {
|
||||
case isAWSVPC:
|
||||
case config.AWSVPC:
|
||||
vpc := weave.NewAWSVPC()
|
||||
overlay.Add("awsvpc", vpc)
|
||||
bridge = weave.NullBridge{}
|
||||
injectorConsumer = weave.NullInjectorConsumer{}
|
||||
// Currently, we do not support any overlay with AWSVPC
|
||||
ignoreSleeve = true
|
||||
case datapathName != "" && ifaceName != "":
|
||||
Log.Fatal("At most one of --datapath and --iface must be specified.")
|
||||
case datapathName != "":
|
||||
iface, err := weavenet.EnsureInterface(datapathName)
|
||||
case bridgeType == nil:
|
||||
injectorConsumer = weave.NullInjectorConsumer{}
|
||||
case bridgeType.IsFastdp():
|
||||
iface, err := weavenet.EnsureInterface(config.DatapathName)
|
||||
checkFatal(err)
|
||||
fastdp, err := weave.NewFastDatapath(iface, port, enableEncryption)
|
||||
checkFatal(err)
|
||||
bridge = fastdp.Bridge()
|
||||
injectorConsumer = fastdp.InjectorConsumer()
|
||||
overlay.Add("fastdp", fastdp.Overlay())
|
||||
case ifaceName != "":
|
||||
iface, err := weavenet.EnsureInterface(ifaceName)
|
||||
case !bridgeType.IsFastdp():
|
||||
iface, err := weavenet.EnsureInterface(weavenet.PcapIfName)
|
||||
checkFatal(err)
|
||||
bridge, err = weave.NewPcap(iface, bufSzMB*1024*1024) // bufsz flag is in MB
|
||||
injectorConsumer, err = weave.NewPcap(iface, bufSzMB*1024*1024) // bufsz flag is in MB
|
||||
checkFatal(err)
|
||||
default:
|
||||
bridge = weave.NullBridge{}
|
||||
}
|
||||
|
||||
if !ignoreSleeve {
|
||||
@@ -445,7 +589,7 @@ func createOverlay(datapathName string, ifaceName string, isAWSVPC bool, host st
|
||||
overlay.SetCompatOverlay(sleeve)
|
||||
}
|
||||
|
||||
return overlay, bridge
|
||||
return overlay, injectorConsumer
|
||||
}
|
||||
|
||||
func createAllocator(router *weave.NetworkRouter, config ipamConfig, preClaims []ipam.PreClaim, db db.DB, track tracker.LocalRangeTracker, isKnownPeer func(mesh.PeerName) bool) (*ipam.Allocator, address.CIDR) {
|
||||
@@ -476,7 +620,9 @@ func createAllocator(router *weave.NetworkRouter, config ipamConfig, preClaims [
|
||||
|
||||
allocator := ipam.NewAllocator(c)
|
||||
|
||||
allocator.SetInterfaces(router.NewGossip("IPallocation", allocator))
|
||||
gossip, err := router.NewGossip("IPallocation", allocator)
|
||||
checkFatal(err)
|
||||
allocator.SetInterfaces(gossip)
|
||||
allocator.Start()
|
||||
router.Peers.OnGC(func(peer *mesh.Peer) { allocator.PeerGone(peer.Name) })
|
||||
|
||||
@@ -486,7 +632,9 @@ func createAllocator(router *weave.NetworkRouter, config ipamConfig, preClaims [
|
||||
func createDNSServer(config dnsConfig, router *mesh.Router, isKnownPeer func(mesh.PeerName) bool) (*nameserver.Nameserver, *nameserver.DNSServer) {
|
||||
ns := nameserver.New(router.Ourself.Peer.Name, config.Domain, isKnownPeer)
|
||||
router.Peers.OnGC(func(peer *mesh.Peer) { ns.PeerGone(peer.Name) })
|
||||
ns.SetGossip(router.NewGossip("nameserver", ns))
|
||||
gossip, err := router.NewGossip("nameserver", ns)
|
||||
checkFatal(err)
|
||||
ns.SetGossip(gossip)
|
||||
upstream := nameserver.NewUpstream(config.ResolvConf, config.EffectiveListenAddress)
|
||||
dnsserver, err := nameserver.NewDNSServer(ns, config.Domain, config.ListenAddress,
|
||||
upstream, uint32(config.TTL), config.ClientTimeout)
|
||||
@@ -534,13 +682,15 @@ func determinePassword(password string) []byte {
|
||||
return []byte(password)
|
||||
}
|
||||
|
||||
func peerName(routerName string) mesh.PeerName {
|
||||
func peerName(routerName, bridgeName, dbPrefix, hostRoot string) mesh.PeerName {
|
||||
if routerName == "" {
|
||||
iface, err := net.InterfaceByName(weavenet.WeaveBridgeName)
|
||||
if err != nil {
|
||||
Log.Fatalf("Unable to find bridge %q", weavenet.WeaveBridgeName)
|
||||
iface, err := net.InterfaceByName(bridgeName)
|
||||
if err == nil {
|
||||
routerName = iface.HardwareAddr.String()
|
||||
} else {
|
||||
routerName, err = weavenet.GetSystemPeerName(dbPrefix, hostRoot)
|
||||
checkFatal(err)
|
||||
}
|
||||
routerName = iface.HardwareAddr.String()
|
||||
}
|
||||
name, err := mesh.PeerNameFromUserInput(routerName)
|
||||
checkFatal(err)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
weavetest.TrimTestArgs()
|
||||
main()
|
||||
if weavetest.TrimTestArgs() {
|
||||
main()
|
||||
}
|
||||
}
|
||||
+25
-4
@@ -82,6 +82,27 @@ var metrics = []metric{
|
||||
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."),
|
||||
func(s WeaveStatus, desc *prometheus.Desc, ch chan<- prometheus.Metric) {
|
||||
if s.IPAM != nil {
|
||||
@@ -118,10 +139,10 @@ func newMetrics(router *weave.NetworkRouter, allocator *ipam.Allocator, ns *name
|
||||
|
||||
func (m *collector) Collect(ch chan<- prometheus.Metric) {
|
||||
|
||||
status := WeaveStatus{"", nil,
|
||||
weave.NewNetworkRouterStatus(m.router),
|
||||
ipam.NewStatus(m.allocator, address.CIDR{}),
|
||||
nameserver.NewStatus(m.ns, m.dnsserver)}
|
||||
status := WeaveStatus{
|
||||
Router: weave.NewNetworkRouterStatus(m.router),
|
||||
IPAM: ipam.NewStatus(m.allocator, address.CIDR{}),
|
||||
DNS: nameserver.NewStatus(m.ns, m.dnsserver)}
|
||||
|
||||
for _, metric := range metrics {
|
||||
metric.Collect(status, metric.Desc, ch)
|
||||
|
||||
+55
-1
@@ -2,13 +2,16 @@ package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
|
||||
"github.com/weaveworks/mesh"
|
||||
"github.com/weaveworks/weave/api"
|
||||
"github.com/weaveworks/weave/common"
|
||||
weavedocker "github.com/weaveworks/weave/common/docker"
|
||||
"github.com/weaveworks/weave/ipam"
|
||||
"github.com/weaveworks/weave/nameserver"
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
)
|
||||
@@ -20,7 +23,7 @@ func a(cidr *net.IPNet) address.CIDR {
|
||||
|
||||
// Get all the existing Weave IPs at startup, so we can stop IPAM
|
||||
// giving out any as duplicates
|
||||
func findExistingAddresses(dockerCli *weavedocker.Client, containerIDs []string, bridgeName string) (addrs []ipam.PreClaim, err error) {
|
||||
func findExistingAddresses(dockerCli *weavedocker.Client, bridgeName string) (addrs []ipam.PreClaim, err error) {
|
||||
Log.Infof("Checking for pre-existing addresses on %s bridge", bridgeName)
|
||||
// First get the address for the bridge
|
||||
bridgeNetDev, err := weavenet.GetBridgeNetDev(bridgeName)
|
||||
@@ -50,6 +53,11 @@ func findExistingAddresses(dockerCli *weavedocker.Client, containerIDs []string,
|
||||
// Now iterate over all containers to see if they have a network
|
||||
// namespace with an attached interface
|
||||
if dockerCli != nil {
|
||||
containerIDs, err := dockerCli.RunningContainerIDs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, cid := range containerIDs {
|
||||
container, err := dockerCli.InspectContainer(cid)
|
||||
if err != nil {
|
||||
@@ -82,3 +90,49 @@ func findExistingAddresses(dockerCli *weavedocker.Client, containerIDs []string,
|
||||
}
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
func populateDNS(ns *nameserver.Nameserver, dockerCli *weavedocker.Client, ourName mesh.PeerName, bridgeName string) (addrs []ipam.PreClaim, err error) {
|
||||
// Find all veths connected to the bridge
|
||||
peerIDs, err := weavenet.ConnectedToBridgeVethPeerIds(bridgeName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Now iterate over all containers to see if they have a network
|
||||
// namespace with an attached interface
|
||||
if dockerCli != nil {
|
||||
containerIDs, err := dockerCli.RunningContainerIDs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, cid := range containerIDs {
|
||||
container, err := dockerCli.InspectContainer(cid)
|
||||
if err != nil {
|
||||
if _, ok := err.(*docker.NoSuchContainer); ok {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if container.State.Pid != 0 {
|
||||
// Docker allows containers to have fqdns in two wauys:
|
||||
// where the container explicitly has a domain name, and when its
|
||||
// hostname contains the domain name, recognized by it having dots.
|
||||
if container.Config.Domainname == "" && !strings.Contains(container.Config.Hostname, ".") {
|
||||
continue
|
||||
}
|
||||
netDevs, err := weavenet.GetNetDevsByVethPeerIds(container.State.Pid, peerIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fqdn := container.Config.Hostname + "." + container.Config.Domainname
|
||||
for _, netDev := range netDevs {
|
||||
for _, cidr := range netDev.CIDRs {
|
||||
ns.AddEntryFQDN(fqdn, cid, ourName, address.FromIP4(cidr.IP))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return addrs, nil
|
||||
}
|
||||
+16
-1
@@ -27,10 +27,25 @@ func containerAddrs(args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
containerArgs := []string{}
|
||||
for _, cid := range args[1:] {
|
||||
if cid == "weave:allids" { // expand to all container IDs
|
||||
all, err := client.ListContainers(docker.ListContainersOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, c := range all {
|
||||
containerArgs = append(containerArgs, c.ID)
|
||||
}
|
||||
} else {
|
||||
containerArgs = append(containerArgs, cid)
|
||||
}
|
||||
}
|
||||
|
||||
var containerIDs []string
|
||||
containers := make(map[string]*docker.Container)
|
||||
|
||||
for _, cid := range args[1:] {
|
||||
for _, cid := range containerArgs {
|
||||
if cid == "weave:expose" {
|
||||
netDev, err := weavenet.GetBridgeNetDev(bridgeName)
|
||||
if err != nil {
|
||||
|
||||
+46
-18
@@ -3,22 +3,25 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
"github.com/vishvananda/netns"
|
||||
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
"github.com/weaveworks/weave/proxy"
|
||||
)
|
||||
|
||||
func attach(args []string) error {
|
||||
if len(args) < 4 {
|
||||
cmdUsage("attach-container", "[--no-multicast-route] [--keep-tx-on] <container-id> <bridge-name> <mtu> <cidr>...")
|
||||
cmdUsage("attach-container", "[--no-multicast-route] [--keep-tx-on] [--hairpin-mode=true|false] <container-id> <bridge-name> <mtu> <cidr>...")
|
||||
}
|
||||
|
||||
keepTXOn := false
|
||||
withMulticastRoute := true
|
||||
hairpinMode := true
|
||||
for i := 0; i < len(args); {
|
||||
switch args[i] {
|
||||
case "--no-multicast-route":
|
||||
@@ -27,6 +30,9 @@ func attach(args []string) error {
|
||||
case "--keep-tx-on":
|
||||
keepTXOn = true
|
||||
args = append(args[:i], args[i+1:]...)
|
||||
case "--hairpin-mode=false":
|
||||
hairpinMode = false
|
||||
args = append(args[:i], args[i+1:]...)
|
||||
default:
|
||||
i++
|
||||
}
|
||||
@@ -55,7 +61,7 @@ func attach(args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = weavenet.AttachContainer(weavenet.NSPathByPid(pid), fmt.Sprint(pid), weavenet.VethName, args[1], mtu, withMulticastRoute, cidrs, keepTXOn)
|
||||
err = weavenet.AttachContainer(weavenet.NSPathByPid(pid), fmt.Sprint(pid), weavenet.VethName, args[1], mtu, withMulticastRoute, cidrs, keepTXOn, hairpinMode)
|
||||
// If we detected an error but the container has died, tell the user that instead.
|
||||
if err != nil && !processExists(pid) {
|
||||
err = fmt.Errorf("Container %s died", args[0])
|
||||
@@ -63,21 +69,6 @@ func attach(args []string) error {
|
||||
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 {
|
||||
err := syscall.Kill(pid, syscall.Signal(0))
|
||||
return err == nil || err == syscall.EPERM
|
||||
@@ -110,3 +101,40 @@ func detach(args []string) error {
|
||||
}
|
||||
return weavenet.DetachContainer(weavenet.NSPathByPid(pid), args[0], weavenet.VethName, cidrs)
|
||||
}
|
||||
|
||||
func rewriteEtcHosts(args []string) error {
|
||||
if len(args) < 3 {
|
||||
cmdUsage("rewrite-etc-hosts", "<container-id> <image> <cidr> [name:addr...]")
|
||||
}
|
||||
containerID := args[0]
|
||||
image := args[1]
|
||||
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
|
||||
for _, cidr := range strings.Fields(cidrs) {
|
||||
_, ipnet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ips = append(ips, ipnet)
|
||||
}
|
||||
|
||||
docker := os.Getenv("DOCKER_HOST")
|
||||
if docker == "" {
|
||||
docker = "unix:///var/run/docker.sock"
|
||||
}
|
||||
p, err := proxy.StubProxy(proxy.Config{DockerHost: docker, Image: image})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.RewriteEtcHosts(hostsPath, fqdn, ips, extraHosts)
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
|
||||
func detectBridgeType(args []string) error {
|
||||
if len(args) != 2 {
|
||||
cmdUsage("detect-bridge-type", "<weave-bridge-name> <datapath-name>")
|
||||
}
|
||||
bridgeType, err := weavenet.ExistingBridgeType(args[0], args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
} else if bridgeType == nil {
|
||||
fmt.Println("none")
|
||||
} else {
|
||||
fmt.Println(bridgeType.String())
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -3,7 +3,8 @@ package main
|
||||
import (
|
||||
"os"
|
||||
|
||||
cni "github.com/appc/cni/pkg/skel"
|
||||
cni "github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
|
||||
weaveapi "github.com/weaveworks/weave/api"
|
||||
"github.com/weaveworks/weave/common"
|
||||
@@ -14,13 +15,13 @@ import (
|
||||
func cniNet(args []string) error {
|
||||
weave := weaveapi.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), common.Log)
|
||||
n := netplugin.NewCNIPlugin(weave)
|
||||
cni.PluginMain(n.CmdAdd, n.CmdDel)
|
||||
cni.PluginMain(n.CmdAdd, n.CmdDel, version.PluginSupports("0.1.0", "0.2.0", "0.3.0"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func cniIPAM(args []string) error {
|
||||
weave := weaveapi.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), common.Log)
|
||||
i := ipamplugin.NewIpam(weave)
|
||||
cni.PluginMain(i.CmdAdd, i.CmdDel)
|
||||
cni.PluginMain(i.CmdAdd, i.CmdDel, version.PluginSupports("0.1.0", "0.2.0", "0.3.0"))
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
func inspectContainer(containerNameOrID string) (*docker.Container, error) {
|
||||
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to connect to docker: %s", err)
|
||||
}
|
||||
|
||||
container, err := c.InspectContainer(containerNameOrID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to inspect container %s: %s", containerNameOrID, err)
|
||||
}
|
||||
return container, nil
|
||||
}
|
||||
|
||||
func containerPid(containerID string) (int, error) {
|
||||
container, err := inspectContainer(containerID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
||||
}
|
||||
|
||||
if container.State.Pid == 0 {
|
||||
return 0, fmt.Errorf("container %s not running", containerID)
|
||||
}
|
||||
return container.State.Pid, nil
|
||||
}
|
||||
|
||||
func containerID(args []string) error {
|
||||
if len(args) < 1 {
|
||||
cmdUsage("container-id", "<container-name-or-short-id>")
|
||||
}
|
||||
|
||||
container, err := inspectContainer(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(container.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func containerState(args []string) error {
|
||||
if len(args) < 1 {
|
||||
cmdUsage("container-state", "<container-id> [<image-prefix>]")
|
||||
}
|
||||
|
||||
container, err := inspectContainer(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(args) == 1 {
|
||||
fmt.Print(container.State.StateString())
|
||||
} else {
|
||||
imagePrefix := args[1]
|
||||
if strings.HasPrefix(container.Config.Image, imagePrefix) {
|
||||
fmt.Print(container.State.StateString())
|
||||
} else {
|
||||
if container.State.Running {
|
||||
fmt.Print("running image mismatch: ", container.Config.Image)
|
||||
} else {
|
||||
fmt.Print("image mismatch: ", container.Config.Image)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func containerFQDN(args []string) error {
|
||||
if len(args) < 1 {
|
||||
cmdUsage("container-fqdn", "<container-id>")
|
||||
}
|
||||
|
||||
container, err := inspectContainer(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(container.Config.Hostname, ".", container.Config.Domainname)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runContainer(args []string) error {
|
||||
env := []string{}
|
||||
name := ""
|
||||
net := ""
|
||||
pid := ""
|
||||
privileged := false
|
||||
restart := docker.NeverRestart()
|
||||
volumes := []string{}
|
||||
volumesFrom := []string{}
|
||||
|
||||
done := false
|
||||
for i := 0; i < len(args) && !done; {
|
||||
switch args[i] {
|
||||
case "-e", "--env":
|
||||
if v := os.Getenv(args[i+1]); v != "" {
|
||||
env = append(env, args[i+1]+"="+v)
|
||||
} else {
|
||||
env = append(env, args[i+1])
|
||||
}
|
||||
args = append(args[:i], args[i+2:]...)
|
||||
case "--name":
|
||||
name = args[i+1]
|
||||
args = append(args[:i], args[i+2:]...)
|
||||
case "--net":
|
||||
net = args[i+1]
|
||||
args = append(args[:i], args[i+2:]...)
|
||||
case "--pid":
|
||||
pid = args[i+1]
|
||||
args = append(args[:i], args[i+2:]...)
|
||||
case "--privileged":
|
||||
privileged = true
|
||||
args = append(args[:i], args[i+1:]...)
|
||||
case "--restart":
|
||||
restart = docker.RestartPolicy{Name: args[i+1]}
|
||||
args = append(args[:i], args[i+2:]...)
|
||||
case "-v", "--volume":
|
||||
// Must dedup binds, otherwise, container fails creation
|
||||
skip := false
|
||||
for _, v := range volumes {
|
||||
if v == args[i+1] {
|
||||
skip = true
|
||||
}
|
||||
}
|
||||
if !skip {
|
||||
volumes = append(volumes, args[i+1])
|
||||
}
|
||||
args = append(args[:i], args[i+2:]...)
|
||||
case "--volumes-from":
|
||||
volumesFrom = append(volumesFrom, args[i+1])
|
||||
args = append(args[:i], args[i+2:]...)
|
||||
default:
|
||||
done = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(args) < 2 {
|
||||
cmdUsage("run-container", `[options] <image> <cmd> [[<cmd-options>] <cmd-arg1> [<cmd-arg2> ...]]
|
||||
|
||||
-e, --env list Set environment variables
|
||||
--name string Assign a name to the container
|
||||
--net string Network Mode
|
||||
--pid string PID namespace to use
|
||||
--privileged Give extended privileges to this container
|
||||
--restart string Restart policy to apply when a container exits (default "no")
|
||||
-v, --volume list Bind mount a volume
|
||||
--volumes-from list Mount volumes from the specified container(s)
|
||||
`)
|
||||
}
|
||||
|
||||
image := args[0]
|
||||
cmds := args[1:]
|
||||
|
||||
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to connect to docker: %s", err)
|
||||
}
|
||||
|
||||
config := docker.Config{Image: image, Env: env, Cmd: cmds}
|
||||
hostConfig := docker.HostConfig{NetworkMode: net, PidMode: pid, Privileged: privileged, RestartPolicy: restart, Binds: volumes, VolumesFrom: volumesFrom}
|
||||
container, err := c.CreateContainer(docker.CreateContainerOptions{Name: name, Config: &config, HostConfig: &hostConfig})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create container: %s", err)
|
||||
}
|
||||
|
||||
err = c.StartContainer(container.ID, &hostConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to start container: %s", err)
|
||||
}
|
||||
|
||||
fmt.Print(container.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func listContainers(args []string) error {
|
||||
if len(args) > 1 {
|
||||
cmdUsage("list-containers", "[<label>]")
|
||||
}
|
||||
|
||||
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to connect to docker: %s", err)
|
||||
}
|
||||
|
||||
opts := docker.ListContainersOptions{All: true}
|
||||
if len(args) == 1 {
|
||||
label := args[0]
|
||||
opts.Filters = map[string][]string{"label": {label}}
|
||||
}
|
||||
|
||||
containers, err := c.ListContainers(opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list containers: %s", err)
|
||||
}
|
||||
|
||||
for _, container := range containers {
|
||||
fmt.Println(container.ID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stopContainer(args []string) error {
|
||||
if len(args) < 1 {
|
||||
cmdUsage("stop-container", "<container-id> [<container-id2> ...]")
|
||||
}
|
||||
|
||||
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to connect to docker: %s", err)
|
||||
}
|
||||
|
||||
for _, containerID := range args {
|
||||
err = c.StopContainer(containerID, 10)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to stop container %s: %s", containerID, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func killContainer(args []string) error {
|
||||
if len(args) < 1 {
|
||||
cmdUsage("kill-container", "<container-id> [<container-id2> ...]")
|
||||
}
|
||||
|
||||
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to connect to docker: %s", err)
|
||||
}
|
||||
|
||||
for _, containerID := range args {
|
||||
err = c.KillContainer(docker.KillContainerOptions{ID: containerID, Signal: docker.SIGKILL})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to stop container %s: %s", containerID, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeContainer(args []string) error {
|
||||
if len(args) < 1 {
|
||||
cmdUsage("remove-container", "[-f | --force] [-v | --volumes] <container-id> [<container-id2> ...]")
|
||||
}
|
||||
|
||||
force := false
|
||||
volumes := false
|
||||
for i := 0; i < len(args); {
|
||||
switch args[i] {
|
||||
case "--force":
|
||||
case "-f":
|
||||
force = true
|
||||
args = append(args[:i], args[i+1:]...)
|
||||
case "--volumes":
|
||||
case "-v":
|
||||
volumes = true
|
||||
args = append(args[:i], args[i+1:]...)
|
||||
default:
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to connect to docker: %s", err)
|
||||
}
|
||||
|
||||
for _, containerID := range args {
|
||||
err = c.RemoveContainer(docker.RemoveContainerOptions{
|
||||
ID: containerID, Force: force, RemoveVolumes: volumes})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to stop container %s: %s", containerID, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// Get/set arbitrary flags in the Weave Net persistence DB
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/weaveworks/weave/db"
|
||||
)
|
||||
|
||||
const dbFlagPrefix = "flag:"
|
||||
|
||||
func getDBFlag(args []string) error {
|
||||
if len(args) != 2 {
|
||||
cmdUsage("check-db-flag", "<db-prefix> <flag-name>")
|
||||
}
|
||||
dbPrefix := args[0]
|
||||
flagName := args[1]
|
||||
|
||||
d, err := db.NewBoltDBReadOnly(dbPrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer d.Close()
|
||||
var value string
|
||||
nameFound, err := d.Load(dbFlagPrefix+flagName, &value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !nameFound {
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
fmt.Print(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func setDBFlag(args []string) error {
|
||||
if len(args) != 3 {
|
||||
cmdUsage("set-db-flag", "<db-prefix> <flag-name> <flag-value>")
|
||||
}
|
||||
dbPrefix := args[0]
|
||||
flagName := args[1]
|
||||
value := args[2]
|
||||
|
||||
d, err := db.NewBoltDB(dbPrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer d.Close()
|
||||
err = d.Save(dbFlagPrefix+flagName, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Loaded 100 of 278 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user