Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14730f8572 | ||
|
|
e7a24eaf0a | ||
|
|
15ad210bec | ||
|
|
6bc0e5e71c | ||
|
|
0ca831f50b | ||
|
|
57bca8e955 | ||
|
|
709f820ab6 | ||
|
|
599b99a6c4 | ||
|
|
bb51a4f36d | ||
|
|
5d0e44bb6f | ||
|
|
6e7a469a62 | ||
|
|
ca0498b860 | ||
|
|
bbfa682083 | ||
|
|
f763c40c10 | ||
|
|
67037b43b0 | ||
|
|
861395011c | ||
|
|
8bdfcd3042 | ||
|
|
83024b946f | ||
|
|
4f48670345 | ||
|
|
4b30d97d4e | ||
|
|
c9e813cfcb | ||
|
|
c97c50b76e | ||
|
|
f919d06e2c | ||
|
|
526538d9d1 | ||
|
|
f43f3fc2e8 | ||
|
|
8b220f6383 | ||
|
|
d44e34584a | ||
|
|
6d2366ca4c | ||
|
|
7c1b615c98 | ||
|
|
390a4099d0 | ||
|
|
01237446eb | ||
|
|
18deb42df1 | ||
|
|
5c508f837e | ||
|
|
e000472af7 | ||
|
|
6d70284c21 | ||
|
|
4529d1587c | ||
|
|
b0d05f5e37 | ||
|
|
4a5d203e58 | ||
|
|
640c5c346c | ||
|
|
3780ce4dfb | ||
|
|
b871c1eecb | ||
|
|
02ecea4576 | ||
|
|
6876507641 | ||
|
|
d2c78d3196 | ||
|
|
5b8365687e | ||
|
|
a43bd88ddd | ||
|
|
e8fcd21baa | ||
|
|
6a48da60ec | ||
|
|
886dea9b10 | ||
|
|
80e9dee9b2 | ||
|
|
5110a604f8 | ||
|
|
404f1bbef3 | ||
|
|
56491b35f1 | ||
|
|
dd70668b89 | ||
|
|
e5441b44e9 | ||
|
|
4da2501d7a | ||
|
|
04680fc52c | ||
|
|
cb4a8769a8 | ||
|
|
62c1d28524 | ||
|
|
92b21ccd44 | ||
|
|
88cd86a5f2 | ||
|
|
b18c88524d | ||
|
|
90e0c43bf2 | ||
|
|
4215618177 | ||
|
|
cb5798f94d | ||
|
|
79e032c984 | ||
|
|
821b787774 | ||
|
|
3ed7894e39 | ||
|
|
30ed34bd2c | ||
|
|
f5c2f51964 | ||
|
|
4d7f0f5014 | ||
|
|
d1ed6c5747 | ||
|
|
44f91950cb | ||
|
|
d2f0287fa3 | ||
|
|
9c09e36135 | ||
|
|
cb8e06c994 | ||
|
|
753c7cce1e | ||
|
|
30b2bada28 | ||
|
|
eeec3833a3 | ||
|
|
ee47f0c3ee | ||
|
|
36a2146ed8 | ||
|
|
6afedb881c | ||
|
|
05ab1139db | ||
|
|
e90cbf290b | ||
|
|
1e0431f6b2 | ||
|
|
9971fc3dac | ||
|
|
7aa7e3c00d | ||
|
|
a4dd7fa198 | ||
|
|
e2157a5d04 | ||
|
|
fc199d0df5 | ||
|
|
cc6d10c9b1 | ||
|
|
ace2c12608 | ||
|
|
57c464d60e | ||
|
|
65b3894a8e | ||
|
|
77bfa6a7e4 | ||
|
|
9d485d0f21 | ||
|
|
991945081a | ||
|
|
123762ec8d | ||
|
|
b07db6479a | ||
|
|
d3f62905b7 | ||
|
|
22b3393cfd | ||
|
|
c8e7dbd600 | ||
|
|
ef26b306e4 | ||
|
|
1588a7259d | ||
|
|
3a92050ebb | ||
|
|
04c63879b8 | ||
|
|
96291d1de4 | ||
|
|
9090fddd9f | ||
|
|
5bcd33c930 | ||
|
|
959d0bf2e2 | ||
|
|
a547d4571c | ||
|
|
b05c5f63c8 | ||
|
|
309a85b29c | ||
|
|
90ccc4a014 | ||
|
|
a6f2ee5ccc | ||
|
|
dcadc0f8cc | ||
|
|
332808c524 | ||
|
|
db006060e6 | ||
|
|
7a6ba40982 | ||
|
|
75e106b667 | ||
|
|
b28e2ee384 | ||
|
|
785b6b4584 | ||
|
|
391613d015 | ||
|
|
7334869134 | ||
|
|
bb4f51499d | ||
|
|
cc88aa2145 | ||
|
|
4b5d827d94 | ||
|
|
802ede1cd3 | ||
|
|
5e69e8a7b4 | ||
|
|
2d912263b1 | ||
|
|
6e78a34a00 | ||
|
|
ac9d72875d | ||
|
|
51d7f455c9 | ||
|
|
89db74338e | ||
|
|
4c6879679d | ||
|
|
d35f2236e1 | ||
|
|
27848992b1 | ||
|
|
690b6ff2cd | ||
|
|
ea2084d871 | ||
|
|
8a4fc9c02a | ||
|
|
760bc10074 | ||
|
|
20d55fe47a | ||
|
|
4a1fbc7c38 | ||
|
|
11c55a3a07 | ||
|
|
a400d43df7 | ||
|
|
189cfa995c | ||
|
|
47d8f24e56 | ||
|
|
4f8fe2c13f | ||
|
|
873781ab58 | ||
|
|
06e0e6ace0 | ||
|
|
eb7ba691ee | ||
|
|
15b9b7416a | ||
|
|
5024270232 | ||
|
|
bd24dd7dae | ||
|
|
2c2a074a83 | ||
|
|
698e1b6d87 | ||
|
|
b248e021ec | ||
|
|
0d481ef0e9 | ||
|
|
0c2e046c86 | ||
|
|
21bc724632 | ||
|
|
16051a89b6 | ||
|
|
6130699ae5 | ||
|
|
e23e364476 | ||
|
|
8a5aa1a6ae | ||
|
|
9654df4d50 | ||
|
|
6f39a15b53 | ||
|
|
e8f35a848e | ||
|
|
43d3339b39 | ||
|
|
13644ee1b0 | ||
|
|
54d24e65ef | ||
|
|
b8bce1c28f | ||
|
|
e1e4a7887a | ||
|
|
9f0ac3dbe7 | ||
|
|
17b45d9efa | ||
|
|
27178b1c79 | ||
|
|
1bf07d86aa | ||
|
|
bba7b44248 | ||
|
|
d4d9ba1e35 | ||
|
|
f6c22ef9b5 | ||
|
|
040cb19fc5 | ||
|
|
928d08fb19 | ||
|
|
f82440f792 | ||
|
|
ad2dc562af | ||
|
|
ab1953e8ca | ||
|
|
a332af3146 | ||
|
|
5e271363cf | ||
|
|
d2a9a51296 | ||
|
|
99e360ce3b | ||
|
|
1c855f9bc1 | ||
|
|
6b6580413d | ||
|
|
583eea1ec5 | ||
|
|
81b17469f4 | ||
|
|
06669325a7 | ||
|
|
1c81fcb693 | ||
|
|
4a22dda13f | ||
|
|
6e3f8a1c7a | ||
|
|
eb77970534 | ||
|
|
f53dfdea61 | ||
|
|
6c58bd7815 | ||
|
|
83d9292a5b | ||
|
|
37bc04f851 | ||
|
|
a5161b28f5 | ||
|
|
c6cd5f53db | ||
|
|
29bbd321b4 | ||
|
|
ba469bd641 | ||
|
|
bc9939fcdc | ||
|
|
c48c746791 | ||
|
|
d3c5241f12 | ||
|
|
4d4584db2b | ||
|
|
427d694e1b | ||
|
|
f16d4561e0 | ||
|
|
f334575e69 | ||
|
|
ea423755cc | ||
|
|
db697fdaa5 | ||
|
|
c3aa03229c | ||
|
|
637bdf66b1 | ||
|
|
4a40380d0f | ||
|
|
df467be26e | ||
|
|
edba711283 | ||
|
|
1b01da45f5 | ||
|
|
bf9985b0c8 | ||
|
|
408e5f7bdd | ||
|
|
46177657b2 | ||
|
|
d351b9f020 | ||
|
|
727fe46b2a | ||
|
|
b10af5a6cb | ||
|
|
965b5c627e | ||
|
|
d00e7335bb | ||
|
|
e3eb16a1f9 | ||
|
|
078b76f3f1 | ||
|
|
6b2631e378 | ||
|
|
59c1abe9b9 | ||
|
|
4f9f0dfc3e | ||
|
|
8d44f49edd | ||
|
|
7a959df648 | ||
|
|
53fd33044a | ||
|
|
1806ca5665 | ||
|
|
7141ccc271 | ||
|
|
a73330f752 | ||
|
|
427e7a6868 | ||
|
|
b3cb38eda4 | ||
|
|
62758ca337 | ||
|
|
3c67c3c5f1 | ||
|
|
79f77f94f4 | ||
|
|
fc759e2df9 | ||
|
|
1fcd521f67 | ||
|
|
66bd443183 | ||
|
|
fa74e9f290 | ||
|
|
008eeb03ad | ||
|
|
41c48dc678 | ||
|
|
09be35dee0 | ||
|
|
c3ad641d94 | ||
|
|
bacb3952a3 | ||
|
|
70257a6c19 | ||
|
|
bfe9f00e55 | ||
|
|
591be2d08f | ||
|
|
c3ce052cc5 | ||
|
|
7dcd4bdf47 | ||
|
|
2d8373249a | ||
|
|
4979ce1f5f | ||
|
|
79d812a9c2 | ||
|
|
78e00f9a3a | ||
|
|
2ff0bbe150 | ||
|
|
a182bd4514 | ||
|
|
231d0c633e | ||
|
|
a6e1165e71 | ||
|
|
0e26b4621d | ||
|
|
fc211b15e9 | ||
|
|
acc24f1237 | ||
|
|
612e265123 | ||
|
|
9cf50588b5 | ||
|
|
6c2de466c0 | ||
|
|
6f26c732f1 | ||
|
|
ddfec13f17 | ||
|
|
b8b47787be | ||
|
|
4d1f968eb6 | ||
|
|
16a8988e41 | ||
|
|
c9f1d30f49 | ||
|
|
4b9c533b94 | ||
|
|
90907a7647 | ||
|
|
ad4e4238a5 | ||
|
|
40980aeb90 | ||
|
|
f68579654b | ||
|
|
9899780f35 | ||
|
|
dd5994c7cf | ||
|
|
e6ba59f029 | ||
|
|
e0359a32f2 | ||
|
|
a19d025677 | ||
|
|
528baedd99 | ||
|
|
e1579ecc1e | ||
|
|
c72e8bbd36 | ||
|
|
6d921802e2 | ||
|
|
7c78cdd45d | ||
|
|
6625e29071 | ||
|
|
d1d618ccb3 | ||
|
|
80e2ff0353 | ||
|
|
d5e864736b | ||
|
|
5b4040704b | ||
|
|
c85bc059eb | ||
|
|
8fb3da6fc8 | ||
|
|
55354900da | ||
|
|
c654640bde | ||
|
|
1814bbd2f2 | ||
|
|
374fed4699 | ||
|
|
cd48ec5959 | ||
|
|
aec8756151 | ||
|
|
c742ec4993 | ||
|
|
b8bb7d7696 | ||
|
|
8288da2240 | ||
|
|
dcaf77a900 | ||
|
|
e64656d16f | ||
|
|
e3231bb373 | ||
|
|
44e0c6add7 | ||
|
|
1a8794e90f | ||
|
|
e3576e13a7 | ||
|
|
b3e77e81ea | ||
|
|
b13eb5ada5 | ||
|
|
6337972c8a | ||
|
|
8620d0ae4f | ||
|
|
fc77e33d1d | ||
|
|
0164d24583 | ||
|
|
76ee9874d6 | ||
|
|
3699f89b98 | ||
|
|
911751d0c1 | ||
|
|
e669ac6ef8 | ||
|
|
4263ac1ac5 | ||
|
|
8176048268 | ||
|
|
e2d6ee4223 | ||
|
|
90a81ce736 | ||
|
|
9cf0dd1b87 | ||
|
|
e65cf32372 | ||
|
|
0ad2001ab4 | ||
|
|
70ff510e49 | ||
|
|
6377aeabdd | ||
|
|
1649080a35 | ||
|
|
c1ee358f66 | ||
|
|
2c708b57a1 | ||
|
|
c5772c814c | ||
|
|
10a5b6fb51 | ||
|
|
07d31a8558 | ||
|
|
cf3a3be8f2 | ||
|
|
49224ec9f7 | ||
|
|
dee24aec65 | ||
|
|
9a63ade1b5 | ||
|
|
18a6237ec7 | ||
|
|
be8298926a | ||
|
|
86958be6cf | ||
|
|
a2d07c85da | ||
|
|
295ddc5248 | ||
|
|
a85a19e40a | ||
|
|
cec3c8a846 | ||
|
|
7da7ce85df | ||
|
|
6055431b6f | ||
|
|
c0e2deb5a0 | ||
|
|
1f86ed55ba | ||
|
|
35f1ba17e1 | ||
|
|
b84fbf86ec | ||
|
|
a5e0b3452d | ||
|
|
0a1ca30aae | ||
|
|
2e85018c26 | ||
|
|
affdc6f5af | ||
|
|
9198bb5a68 | ||
|
|
9769c8d5b9 | ||
|
|
688328dc57 | ||
|
|
8904a4b144 | ||
|
|
8d1ee752d4 | ||
|
|
f02a11b27b | ||
|
|
5e675f26f7 | ||
|
|
497a9c7fd0 | ||
|
|
5afcd0595d | ||
|
|
f27d4d2117 | ||
|
|
79727a86fa | ||
|
|
d474c1c26d | ||
|
|
29ec43bdc8 | ||
|
|
cabc05b06c | ||
|
|
89e820d62e | ||
|
|
88616007b0 | ||
|
|
b262a1eedc | ||
|
|
973e1075b9 | ||
|
|
b8ce722a2c | ||
|
|
b8fa4af06a | ||
|
|
7f1945f06e | ||
|
|
ea8f041a20 | ||
|
|
52e8cdb011 | ||
|
|
ac9f47783c | ||
|
|
27f9aa16d6 | ||
|
|
08dc649b9a | ||
|
|
bb1a373272 | ||
|
|
e192f68891 | ||
|
|
73654791c1 | ||
|
|
8908458942 | ||
|
|
26792283ab | ||
|
|
32a92a4695 | ||
|
|
3bea2ae419 | ||
|
|
58eaf19026 | ||
|
|
d513e68fab | ||
|
|
ca8bbb27a0 | ||
|
|
287dab27ae | ||
|
|
25c26519ed | ||
|
|
cf66c8d507 | ||
|
|
318bb767a5 | ||
|
|
ad6148f0f6 | ||
|
|
8042d3f22a | ||
|
|
c7259fc424 | ||
|
|
62c949df6e | ||
|
|
8c7dc342e0 | ||
|
|
d15439aeac | ||
|
|
c19c64a990 | ||
|
|
8e374d9416 | ||
|
|
49216ce365 | ||
|
|
812c58297f | ||
|
|
2a04df1049 | ||
|
|
2eda624653 | ||
|
|
2f8aa7a351 | ||
|
|
b6115f0147 | ||
|
|
5ff22a1a77 | ||
|
|
7fdf8fee13 | ||
|
|
45a9ea666e | ||
|
|
efd5d0e410 | ||
|
|
6dbe95a0b5 | ||
|
|
a0ec06a487 | ||
|
|
ebf4b4b48a | ||
|
|
4f99efaaf4 | ||
|
|
62c30d9130 | ||
|
|
21fb953d0f | ||
|
|
27c98070e8 | ||
|
|
039750a7fa | ||
|
|
9b0d1bd054 | ||
|
|
4c1c2a5661 | ||
|
|
791d9aecfc | ||
|
|
48aaac90fb | ||
|
|
bfaa3a3a92 | ||
|
|
6dce685105 | ||
|
|
8c4e33faef | ||
|
|
81582b04f9 | ||
|
|
dbcc72a6f6 | ||
|
|
9f207f161a | ||
|
|
1ee4039681 | ||
|
|
3203814940 | ||
|
|
acc8c33260 | ||
|
|
22ecf505e3 | ||
|
|
c2c14410d2 | ||
|
|
ba578c91a7 | ||
|
|
7b46391e30 | ||
|
|
ae551cafea | ||
|
|
672c770dbe | ||
|
|
847046a225 | ||
|
|
4da998ab45 | ||
|
|
b534ea60f5 | ||
|
|
549ccff5eb | ||
|
|
9cf6749b2e | ||
|
|
d2999cf1d7 | ||
|
|
391b55ceaf | ||
|
|
e05345d3bb | ||
|
|
67bd54e55d | ||
|
|
20919fd6db | ||
|
|
ada2af04ff | ||
|
|
fcb6f762a9 | ||
|
|
5645f31934 | ||
|
|
a7295e2d03 | ||
|
|
c3e6ac2cac | ||
|
|
f1f3c8c8fe | ||
|
|
81b2f2e15a | ||
|
|
f6fdaf6492 | ||
|
|
519c5d686a | ||
|
|
f7e62ccbcc | ||
|
|
19161c0c92 | ||
|
|
c528303841 | ||
|
|
a439288765 | ||
|
|
416410912d | ||
|
|
b3f26f00e2 | ||
|
|
90e6ecdd11 | ||
|
|
842354bc5a | ||
|
|
4d3875cec2 | ||
|
|
74dfe49046 | ||
|
|
d0f23ed9e0 | ||
|
|
8a0992315e | ||
|
|
8afc6f208d | ||
|
|
d35d931179 | ||
|
|
093174e757 | ||
|
|
ac78762910 | ||
|
|
83b36e4c76 | ||
|
|
42e4b9addc | ||
|
|
51feec3785 | ||
|
|
94cd6226ea | ||
|
|
d498df3933 | ||
|
|
ad42357dd6 | ||
|
|
2de899342a | ||
|
|
8df48dc69d | ||
|
|
0f2809d2e9 | ||
|
|
aa44153af7 |
No files matched your search
@@ -0,0 +1,123 @@
|
|||||||
|
version: 2
|
||||||
|
defaults: &defaults
|
||||||
|
docker:
|
||||||
|
- image: weaveworks/weavebuild:go-1-13-1-5110a604
|
||||||
|
working_directory: /go/src/github.com/weaveworks/weave
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run: git submodule update --init
|
||||||
|
- run: make DEBUG=1 BUILD_IN_CONTAINER=false lint
|
||||||
|
|
||||||
|
unit-test:
|
||||||
|
<<: *defaults
|
||||||
|
parallelism: 2
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run: COVERDIR=test/coverage make BUILD_IN_CONTAINER=false tests
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: .
|
||||||
|
paths:
|
||||||
|
- test/coverage
|
||||||
|
|
||||||
|
build:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- setup_remote_docker
|
||||||
|
- run: git submodule update --init
|
||||||
|
- run: make COVERAGE=true BUILD_IN_CONTAINER=false SUDO= exes all
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: .
|
||||||
|
paths:
|
||||||
|
- weave.tar.gz
|
||||||
|
- tools/runner/runner
|
||||||
|
- test/tls/tls
|
||||||
|
|
||||||
|
smoke-tests:
|
||||||
|
machine:
|
||||||
|
image: circleci/classic:201709-01
|
||||||
|
working_directory: /home/circleci/src/github.com/weaveworks/weave
|
||||||
|
environment:
|
||||||
|
SRCDIR: /home/circleci/src/github.com/weaveworks/weave
|
||||||
|
CIRCLE_ARTIFACTS: /tmp/artifacts
|
||||||
|
parallelism: 2
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run: mkdir $CIRCLE_ARTIFACTS
|
||||||
|
- run: git submodule update --init
|
||||||
|
- attach_workspace:
|
||||||
|
at: .
|
||||||
|
# kick off creation of test VMs
|
||||||
|
- run: bin/provision_test_vms.sh
|
||||||
|
# when VMs are ready, copy built software to them
|
||||||
|
- run: bin/circle-test-pre
|
||||||
|
- run:
|
||||||
|
command: bin/circle-test-smoke
|
||||||
|
no_output_timeout: 360s
|
||||||
|
# Destroy testing VMs:
|
||||||
|
- run:
|
||||||
|
command: bin/circle-destroy-vms
|
||||||
|
background: true
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: .
|
||||||
|
paths:
|
||||||
|
- test/coverage
|
||||||
|
- store_artifacts:
|
||||||
|
path: /tmp/artifacts
|
||||||
|
|
||||||
|
gen-coverage:
|
||||||
|
<<: *defaults
|
||||||
|
environment:
|
||||||
|
CIRCLE_ARTIFACTS: /tmp/artifacts
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run: mkdir $CIRCLE_ARTIFACTS
|
||||||
|
- attach_workspace:
|
||||||
|
at: .
|
||||||
|
- run: cd test; ./gen_coverage_reports.sh
|
||||||
|
- run: goveralls -repotoken $COVERALLS_REPO_TOKEN -coverprofile=test/profile.cov -service=circleci
|
||||||
|
- run: cp test/coverage.* $CIRCLE_ARTIFACTS
|
||||||
|
- store_artifacts:
|
||||||
|
path: /tmp/artifacts
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- setup_remote_docker
|
||||||
|
# Register qemu hooks, rebuild without coverage and push to dockerhub
|
||||||
|
- deploy:
|
||||||
|
command: |
|
||||||
|
docker login -u $DOCKER_USER -p $DOCKER_PASS
|
||||||
|
docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||||
|
wget https://github.com/estesp/manifest-tool/releases/download/v0.8.0/manifest-tool-linux-amd64
|
||||||
|
chmod +x manifest-tool-linux-amd64
|
||||||
|
make clean-bin
|
||||||
|
make publish MANIFEST_TOOL_CMD=./manifest-tool-linux-amd64 UPDATE_LATEST=latest-only SUDO= BUILD_IN_CONTAINER=false
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
version: 2
|
||||||
|
build_test:
|
||||||
|
jobs:
|
||||||
|
- lint
|
||||||
|
- unit-test
|
||||||
|
- build
|
||||||
|
- smoke-tests:
|
||||||
|
requires:
|
||||||
|
- lint
|
||||||
|
- unit-test
|
||||||
|
- build
|
||||||
|
- gen-coverage:
|
||||||
|
requires:
|
||||||
|
- unit-test
|
||||||
|
- smoke-tests
|
||||||
|
- deploy:
|
||||||
|
requires:
|
||||||
|
- smoke-tests
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only: master
|
||||||
+3
-2
@@ -46,8 +46,8 @@ prog/weavewait/weavewait_noop
|
|||||||
prog/weavewait/weavewait_nomcast
|
prog/weavewait/weavewait_nomcast
|
||||||
prog/weaveutil/weaveutil
|
prog/weaveutil/weaveutil
|
||||||
prog/*/Dockerfile.weaveworks
|
prog/*/Dockerfile.weaveworks
|
||||||
prog/kube-peers/kube-peers
|
prog/kube-utils/kube-utils
|
||||||
prog/weave-kube/kube-peers
|
prog/weave-kube/kube-utils
|
||||||
prog/weave-npc/weave-npc
|
prog/weave-npc/weave-npc
|
||||||
prog/plugin/plugin
|
prog/plugin/plugin
|
||||||
testing/cover/cover
|
testing/cover/cover
|
||||||
@@ -68,6 +68,7 @@ test/tls/tls
|
|||||||
test/tls/*.pem
|
test/tls/*.pem
|
||||||
test/coverage
|
test/coverage
|
||||||
test/coverage.*
|
test/coverage.*
|
||||||
|
test/images/network-tester/webserver
|
||||||
*qemu-*-static
|
*qemu-*-static
|
||||||
terraform.tfstate
|
terraform.tfstate
|
||||||
terraform.tfstate.backup
|
terraform.tfstate.backup
|
||||||
|
|||||||
-177
@@ -1,180 +1,3 @@
|
|||||||
[submodule "tools"]
|
[submodule "tools"]
|
||||||
path = tools
|
path = tools
|
||||||
url = https://github.com/weaveworks/build-tools
|
url = https://github.com/weaveworks/build-tools
|
||||||
[submodule "vendor/github.com/vishvananda/netlink"]
|
|
||||||
path = vendor/github.com/vishvananda/netlink
|
|
||||||
url = https://github.com/vishvananda/netlink.git
|
|
||||||
[submodule "vendor/github.com/gorilla/mux"]
|
|
||||||
path = vendor/github.com/gorilla/mux
|
|
||||||
url = https://github.com/gorilla/mux.git
|
|
||||||
[submodule "vendor/github.com/gorilla/context"]
|
|
||||||
path = vendor/github.com/gorilla/context
|
|
||||||
url = https://github.com/gorilla/context.git
|
|
||||||
[submodule "vendor/github.com/google/gopacket"]
|
|
||||||
path = vendor/github.com/google/gopacket
|
|
||||||
url = https://github.com/google/gopacket.git
|
|
||||||
[submodule "vendor/github.com/weaveworks/go-odp"]
|
|
||||||
path = vendor/github.com/weaveworks/go-odp
|
|
||||||
url = https://github.com/weaveworks/go-odp.git
|
|
||||||
[submodule "vendor/github.com/opencontainers/runc"]
|
|
||||||
path = vendor/github.com/opencontainers/runc
|
|
||||||
url = https://github.com/opencontainers/runc.git
|
|
||||||
[submodule "vendor/github.com/fsouza/go-dockerclient"]
|
|
||||||
path = vendor/github.com/fsouza/go-dockerclient
|
|
||||||
url = https://github.com/fsouza/go-dockerclient.git
|
|
||||||
[submodule "vendor/github.com/docker/libnetwork"]
|
|
||||||
path = vendor/github.com/docker/libnetwork
|
|
||||||
url = https://github.com/docker/libnetwork.git
|
|
||||||
[submodule "vendor/github.com/docker/docker"]
|
|
||||||
path = vendor/github.com/docker/docker
|
|
||||||
url = https://github.com/docker/docker.git
|
|
||||||
[submodule "vendor/github.com/Sirupsen/logrus"]
|
|
||||||
path = vendor/github.com/Sirupsen/logrus
|
|
||||||
url = https://github.com/Sirupsen/logrus.git
|
|
||||||
[submodule "vendor/github.com/andybalholm/go-bit"]
|
|
||||||
path = vendor/github.com/andybalholm/go-bit
|
|
||||||
url = https://github.com/andybalholm/go-bit.git
|
|
||||||
[submodule "vendor/github.com/miekg/dns"]
|
|
||||||
path = vendor/github.com/miekg/dns
|
|
||||||
url = https://github.com/miekg/dns.git
|
|
||||||
[submodule "vendor/github.com/stretchr/testify"]
|
|
||||||
path = vendor/github.com/stretchr/testify
|
|
||||||
url = https://github.com/stretchr/testify.git
|
|
||||||
[submodule "vendor/golang.org/x/crypto"]
|
|
||||||
path = vendor/golang.org/x/crypto
|
|
||||||
url = https://github.com/golang/crypto.git
|
|
||||||
[submodule "vendor/github.com/docker/machine"]
|
|
||||||
path = vendor/github.com/docker/machine
|
|
||||||
url = https://github.com/docker/machine.git
|
|
||||||
[submodule "vendor/github.com/weaveworks/mesh"]
|
|
||||||
path = vendor/github.com/weaveworks/mesh
|
|
||||||
url = https://github.com/weaveworks/mesh
|
|
||||||
[submodule "vendor/github.com/vishvananda/netns"]
|
|
||||||
path = vendor/github.com/vishvananda/netns
|
|
||||||
url = https://github.com/vishvananda/netns.git
|
|
||||||
[submodule "vendor/github.com/boltdb/bolt"]
|
|
||||||
path = vendor/github.com/boltdb/bolt
|
|
||||||
url = https://github.com/boltdb/bolt
|
|
||||||
[submodule "vendor/github.com/weaveworks/go-checkpoint"]
|
|
||||||
path = vendor/github.com/weaveworks/go-checkpoint
|
|
||||||
url = https://github.com/weaveworks/go-checkpoint
|
|
||||||
[submodule "vendor/github.com/hashicorp/go-cleanhttp"]
|
|
||||||
path = vendor/github.com/hashicorp/go-cleanhttp
|
|
||||||
url = http://github.com/hashicorp/go-cleanhttp
|
|
||||||
[submodule "vendor/github.com/coreos/go-iptables"]
|
|
||||||
path = vendor/github.com/coreos/go-iptables
|
|
||||||
url = https://github.com/coreos/go-iptables
|
|
||||||
[submodule "vendor/github.com/pkg/profile"]
|
|
||||||
path = vendor/github.com/pkg/profile
|
|
||||||
url = https://github.com/pkg/profile
|
|
||||||
[submodule "vendor/github.com/j-keck/arping"]
|
|
||||||
path = vendor/github.com/j-keck/arping
|
|
||||||
url = https://github.com/j-keck/arping
|
|
||||||
[submodule "vendor/github.com/aws/aws-sdk-go"]
|
|
||||||
path = vendor/github.com/aws/aws-sdk-go
|
|
||||||
url = https://github.com/aws/aws-sdk-go
|
|
||||||
[submodule "vendor/golang.org/x/sys"]
|
|
||||||
path = vendor/golang.org/x/sys
|
|
||||||
url = https://go.googlesource.com/sys
|
|
||||||
[submodule "vendor/github.com/beorn7/perks"]
|
|
||||||
path = vendor/github.com/beorn7/perks
|
|
||||||
url = https://github.com/beorn7/perks
|
|
||||||
[submodule "vendor/github.com/prometheus/procfs"]
|
|
||||||
path = vendor/github.com/prometheus/procfs
|
|
||||||
url = https://github.com/prometheus/procfs
|
|
||||||
[submodule "vendor/github.com/prometheus/client_golang"]
|
|
||||||
path = vendor/github.com/prometheus/client_golang
|
|
||||||
url = https://github.com/prometheus/client_golang
|
|
||||||
[submodule "vendor/github.com/prometheus/client_model"]
|
|
||||||
path = vendor/github.com/prometheus/client_model
|
|
||||||
url = https://github.com/prometheus/client_model
|
|
||||||
[submodule "vendor/github.com/prometheus/common"]
|
|
||||||
path = vendor/github.com/prometheus/common
|
|
||||||
url = https://github.com/prometheus/common
|
|
||||||
[submodule "vendor/github.com/golang/protobuf"]
|
|
||||||
path = vendor/github.com/golang/protobuf
|
|
||||||
url = https://github.com/golang/protobuf
|
|
||||||
[submodule "vendor/github.com/matttproud/golang_protobuf_extensions"]
|
|
||||||
path = vendor/github.com/matttproud/golang_protobuf_extensions
|
|
||||||
url = https://github.com/matttproud/golang_protobuf_extensions
|
|
||||||
[submodule "vendor/k8s.io/client-go"]
|
|
||||||
path = vendor/k8s.io/client-go
|
|
||||||
url = https://github.com/kubernetes/client-go
|
|
||||||
[submodule "vendor/github.com/pkg/errors"]
|
|
||||||
path = vendor/github.com/pkg/errors
|
|
||||||
url = https://github.com/pkg/errors
|
|
||||||
[submodule "vendor/github.com/spf13/cobra"]
|
|
||||||
path = vendor/github.com/spf13/cobra
|
|
||||||
url = https://github.com/spf13/cobra
|
|
||||||
[submodule "vendor/github.com/spf13/plag"]
|
|
||||||
path = vendor/github.com/spf13/plag
|
|
||||||
url = https://github.com/spf13/pflag
|
|
||||||
[submodule "vendor/github.com/spf13/pflag"]
|
|
||||||
path = vendor/github.com/spf13/pflag
|
|
||||||
url = https://github.com/spf13/pflag
|
|
||||||
[submodule "vendor/github.com/mgutz/ansi"]
|
|
||||||
path = vendor/github.com/mgutz/ansi
|
|
||||||
url = https://github.com/mgutz/ansi
|
|
||||||
[submodule "vendor/github.com/estesp/manifest-tool"]
|
|
||||||
path = vendor/github.com/estesp/manifest-tool
|
|
||||||
url = https://github.com/estesp/manifest-tool
|
|
||||||
[submodule "vendor/github.com/weaveworks/common"]
|
|
||||||
path = vendor/github.com/weaveworks/common
|
|
||||||
url = https://github.com/weaveworks/common
|
|
||||||
[submodule "vendor/github.com/containernetworking/cni"]
|
|
||||||
path = vendor/github.com/containernetworking/cni
|
|
||||||
url = https://github.com/containernetworking/cni
|
|
||||||
[submodule "vendor/google.golang.org/grpc"]
|
|
||||||
path = vendor/google.golang.org/grpc
|
|
||||||
url = https://github.com/grpc/grpc-go
|
|
||||||
[submodule "vendor/github.com/docker/go-units"]
|
|
||||||
path = vendor/github.com/docker/go-units
|
|
||||||
url = https://github.com/docker/go-units
|
|
||||||
[submodule "vendor/golang.org/x/net"]
|
|
||||||
path = vendor/golang.org/x/net
|
|
||||||
url = https://github.com/golang/net
|
|
||||||
[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
|
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
# avoid breaking the build on make lint:
|
# avoid breaking the build on make lint:
|
||||||
bin/circle-deploy-issues
|
bin/circle-deploy-issues
|
||||||
bin/circle-deploy-master
|
bin/circle-deploy-master
|
||||||
bin/circle-teardown-pre
|
|
||||||
bin/circle-test-teardown
|
bin/circle-test-teardown
|
||||||
bin/circle-test-unit
|
bin/circle-test-unit
|
||||||
bin/install-wordepress
|
bin/install-wordepress
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
2.3.0
|
2.6.2
|
||||||
+257
@@ -1,3 +1,260 @@
|
|||||||
|
## Release 2.6.2
|
||||||
|
|
||||||
|
This release fixes a regression found in 2.6.1 release and removes a possible case
|
||||||
|
of CPU spinning when pcap is used in sleeve mode
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* Weave Net can not be used in fastdp mode and always falls back to sleeve mode #3781, #3783
|
||||||
|
* Restrict timeout value passed to pcap library to a value less than 2^31 microseconds to
|
||||||
|
prevent CPU spinning #3782
|
||||||
|
|
||||||
|
## Release 2.6.1
|
||||||
|
|
||||||
|
This release fixes a bug discovered since the release of Weave Net 2.6.0 and
|
||||||
|
an improvement to support iptables 1.8
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* Weave Net on node restart unable to form connections to peers and connections get stuck in
|
||||||
|
pending state and stops responding to API endpoint #3762 #3763
|
||||||
|
|
||||||
|
### Other improvements
|
||||||
|
|
||||||
|
* Weave Net Kubernetes images by default uses iptables 1.8 with legacy (netfilter) backend with
|
||||||
|
option to choose netfilter as backend #3465, #3747
|
||||||
|
|
||||||
|
|
||||||
|
## Release 2.6.0
|
||||||
|
|
||||||
|
This release reduces CPU and memory usage in larger clusters, by
|
||||||
|
sending notifications to a smaller set of peers and coalescing updates
|
||||||
|
to reduce topology recalculation. #3715, #3732
|
||||||
|
|
||||||
|
The default soft limit on connections has been raised from 100 to 200.
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* Fix a race condition in Kubernetes addon when reclaiming IP
|
||||||
|
addresses after node deletion #3724, #3716
|
||||||
|
* Buffer events so Docker won't drop them, and Weave Net can clean up
|
||||||
|
after dead containers #3432, #3705
|
||||||
|
* Weave reconnect occasionally fails after network interface
|
||||||
|
disconnect #3666, #3669, #3676
|
||||||
|
* Ingress NetworkPolicy would accepts all traffic when specifying both
|
||||||
|
IPBlock and port #3653, #3654
|
||||||
|
|
||||||
|
### Kubernetes improvements
|
||||||
|
|
||||||
|
* Support both podSelector and namespaceSelector in NetworkPolicy #3312, #3647
|
||||||
|
* Only add default-drop egress rule if network policies are in use #3639
|
||||||
|
* Manifests use 'apps/v1' rather than deprecated 'apps/v1beta1' #3660
|
||||||
|
* Avoid Weave Net pods being evicted by setting priorityClassName: system-node-critical #3697
|
||||||
|
* Manifests use recommended DNS policy ClusterFirstWithHostNet #3692
|
||||||
|
* Weave Net now tolerates 'NoExecute' taint #3655
|
||||||
|
* Allow extra arguments to NetworkPolicy controller to be set in an environment variable #3683
|
||||||
|
* Stop reporting a failure to connect to self #3454, #3585
|
||||||
|
* Minor reduction in log noise when reclaiming IPs #3710
|
||||||
|
|
||||||
|
### Other improvements
|
||||||
|
|
||||||
|
* Avoid isolating nodes which have restarted by automatically
|
||||||
|
repairing inconsistencies in IP allocation data #1962, #3637, #3708
|
||||||
|
* Build Weave Net for the s390x architecture #3685
|
||||||
|
* When a IP address is requested that may be in use, make several
|
||||||
|
attempts to claim it before returning an error #3725
|
||||||
|
* Improve logging for IP allocation updates #3627, #3630
|
||||||
|
* Improve 'expecting PMTU update' log message on initial connection #3603
|
||||||
|
|
||||||
|
### Build and test
|
||||||
|
|
||||||
|
* Shut down Kubernetes on node when testing node deletion #3716
|
||||||
|
* Update the base 'Alpine' container image to version 3.8 #3701
|
||||||
|
* Update Go to version 1.13.3 #3712
|
||||||
|
* Update gopacket library #3590
|
||||||
|
* Pin busybox version to 1.28 to avoid CI failure in Python test #3689
|
||||||
|
* Remove obsolete weave-daemonset.yaml file #3674
|
||||||
|
|
||||||
|
Thanks to contributors @christian-2, @hpdvanwyk, @guirish, @kitt1987,
|
||||||
|
@mmerrill3, @Pensu, @scritchley, @sidharthsurana, @tanishq-dubey
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/78?closed=1)
|
||||||
|
|
||||||
|
|
||||||
|
## Release 2.5.2
|
||||||
|
|
||||||
|
This release fixes several bugs causing inconsistencies in IPAM and fixes a
|
||||||
|
panic in daemon that reclaims and forgets deleted nodes in Kubernetes clusters.
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* leaking IP addresses in IPAM when weave-net hangs or restarting #3587, #3638
|
||||||
|
* Panic in kube-utils after config map update error resulting in reclaim daemon
|
||||||
|
not performing weave forget on deleted node #3613, #3623
|
||||||
|
* preventing inconsistent IPAM entries and resolving the conflict in case of
|
||||||
|
conflict during IPAM ring merge #3629, #3635, #3632, #3444
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/79?closed=1)
|
||||||
|
|
||||||
|
|
||||||
|
## Release 2.5.1
|
||||||
|
|
||||||
|
This release fixes bugs reported for 2.5 release and small improvements.
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* prevent warnings in kernal logs due to use of physdev model for
|
||||||
|
non-bridged traffic #3449, #3453
|
||||||
|
* Check and report the error occurred while getting the list of Kubernetes
|
||||||
|
peers #3581, #3582
|
||||||
|
|
||||||
|
### Other improvements
|
||||||
|
|
||||||
|
* Update Ruby version to match Weaveworks website #3458
|
||||||
|
* Fix the link to "Troubleshooting the Kubernetes Addon" section in the docs #3461
|
||||||
|
* Document concept of connection limit in Weave #3395, #3413
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/77?closed=1)
|
||||||
|
|
||||||
|
|
||||||
|
## Release 2.5.0
|
||||||
|
|
||||||
|
This release adds support for Kubernetes `hostPort` mapping (#3016,#3356)
|
||||||
|
and the `ipBlock` NetworkPolicy feature (#3168,#3367)
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* Fix a crash at start-up on Docker for Mac #3405, #3408
|
||||||
|
* Network policy: block ingress traffic when no namespaceSelector or
|
||||||
|
podSelector is specified #3347
|
||||||
|
* Reclaim IP addresses which are locked by a non-existent peer #3386, #3416
|
||||||
|
* Fix a crash when blank IP data was loaded #3067, #3415
|
||||||
|
|
||||||
|
### Other improvements
|
||||||
|
|
||||||
|
* If a connection is downgraded to the slower "sleeve" mode, Weave Net
|
||||||
|
will now periodically try to upgrade it to "fast datapath" again. #1737, #3385
|
||||||
|
* Reclaim removed Kubernetes nodes' IP space when they are deleted,
|
||||||
|
rather than on next restart #3372, #3399
|
||||||
|
* Replace Kubernetes livenessProbe with readinessProbe, so the pod is
|
||||||
|
not killed if it runs slowly #3471, #3421
|
||||||
|
* In Kubernetes NetworkPolicy controller, remove the need to maintain
|
||||||
|
a set of local pod IP addresses #3344, #3423
|
||||||
|
* Don't crash on Kubernetes named port in NetworkPolicy, just report as unsupported #3375
|
||||||
|
* Ensure the `weave` network bridge is accessible on Linux kernels
|
||||||
|
older than 3.14 #3442, #3297, #3239
|
||||||
|
* Better reporting in the logs if the `weave` network device is in the Down state #3133, #3381
|
||||||
|
* Change log-level to debug of calls through the Docker proxy, to reduce noise #3439
|
||||||
|
* Add `--without-masquerade` option to `weave expose`, so external
|
||||||
|
services can see the original container IP address #3388
|
||||||
|
* Include Kubernetes cluster information in checkpoint call #3324,#3431
|
||||||
|
* Bump go-odp dependency, so that `fastdp` works on the 4.19 kernel #3430
|
||||||
|
|
||||||
|
### Build and Testing
|
||||||
|
|
||||||
|
* CI builds on master branch now publish images for all platforms
|
||||||
|
* Fix golint path and use https for download of libpcap #3435
|
||||||
|
* Update Kubernetes client-go to v8.0.0, removing code licenced under LGPL3 #3358,#3366
|
||||||
|
* Migrate CircleCI to V2, which is much faster #3255,#3270
|
||||||
|
|
||||||
|
### External Contributors
|
||||||
|
|
||||||
|
Thanks to the following contributors:
|
||||||
|
|
||||||
|
* @Ashiroq
|
||||||
|
* @leprechau
|
||||||
|
* @lkpdn
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/74?closed=1)
|
||||||
|
|
||||||
|
|
||||||
|
## Release 2.4.1
|
||||||
|
|
||||||
|
This release fixes several bugs causing inconsistencies in IPAM for
|
||||||
|
Kubernetes users whose clusters scale up and down over time.
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* Nodes unable to connect after Kubernetes addon erroneously reclaimed
|
||||||
|
node without any IP addresses #3392, #3393
|
||||||
|
* Kubernetes addon could have run out of free IP addresses after nodes
|
||||||
|
are deleted #3384, #3400
|
||||||
|
* Kubernetes addon had reduced free IP addresses due to not reclaiming
|
||||||
|
IP addresses when node name is re-used #3397
|
||||||
|
|
||||||
|
### Other improvements
|
||||||
|
|
||||||
|
* Support `--label` in `WEAVE_DOCKER_ARGS` when starting Weave #3370,#3371
|
||||||
|
* Add missing `--token` argument in help for `weave launch` #3226, #3379
|
||||||
|
* Print defunct processes after smoke-tests #3362
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/75?closed=1)
|
||||||
|
|
||||||
|
|
||||||
|
## Release 2.4.0
|
||||||
|
|
||||||
|
This release introduces a support for Kubernetes Egress Network Policy (#2624, #3313)
|
||||||
|
and adds a mechanism for preserving the client source IP address to enable
|
||||||
|
`externalTrafficPolicy: Local` on Kubernetes (#2924, #3298).
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* Increase the ipset list size which prevents weave-npc from crashing on older
|
||||||
|
kernels when more than eight Kubernetes Namespaces are used (#3289, #3305).
|
||||||
|
* Avoid a possible livelock when reclaiming IP address space in weave-kube (#3317).
|
||||||
|
* Ensure `xtables.lock` is mounted as a file so that kube-proxy can take the lock
|
||||||
|
if it has started after Weave Net (#3351, #3353).
|
||||||
|
* Upgrade the CNI plugin symlinks only if the plugin has changed (#3337, #3345).
|
||||||
|
|
||||||
|
### Other improvements
|
||||||
|
|
||||||
|
* Manipulate the Kubernetes node status `NetworkUnavailable` so that Pods can be
|
||||||
|
scheduled on nodes when the GCE cloud provider is in use (#3249, #3307, #3332, #3334).
|
||||||
|
* Refrain from creating a subprocess for configuring a network interface in
|
||||||
|
a container network namespace (#3291).
|
||||||
|
* Protect against handling the CNI plugin request with the host namespace which
|
||||||
|
prevents Weave Net from misconfiguring the host network (#3206, #3346).
|
||||||
|
* Weave Net can be run on minikube VM (#3124).
|
||||||
|
* Add `org.opencontainers.image.*` labels to Dockerfiles to improve association
|
||||||
|
of the container images with git revisions (#3299).
|
||||||
|
* Improve the error message when running `weave reset` on Kubernetes (#3319).
|
||||||
|
|
||||||
|
### Build and Testing
|
||||||
|
|
||||||
|
* Use `dep` instead of `git submodules` for managing external packages (#3268).
|
||||||
|
* Fix usage of `manifest-tool` in Makefile (#3320).
|
||||||
|
* Update Kubernetes to 1.11 for the integration tests (#3340).
|
||||||
|
|
||||||
|
### External Contributors
|
||||||
|
|
||||||
|
Thanks to the following contributors:
|
||||||
|
|
||||||
|
* @kitt1987
|
||||||
|
* @louismunro
|
||||||
|
* @Nodraak
|
||||||
|
* @stevenjohnstone
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/73?closed=1)
|
||||||
|
|
||||||
|
## Release 2.3.0
|
||||||
|
|
||||||
|
### Security fixes
|
||||||
|
|
||||||
|
* By default, do not expose Weave "/status" and "/report" to all (0.0.0.0) when
|
||||||
|
running on Kubernetes #3271
|
||||||
|
|
||||||
|
### Other improvements
|
||||||
|
|
||||||
|
* Increase the default connection limit for Weave peers (from 30 to 100) when
|
||||||
|
running on Kubernetes, so that more peers could directly connect #3265
|
||||||
|
|
||||||
|
## Build and test
|
||||||
|
|
||||||
|
* Build Weave Net with Go 1.10.1 #3273
|
||||||
|
* Run integration tests against Kubernetes 1.10.0 #3266
|
||||||
|
|
||||||
|
[Full list of changes](https://github.com/weaveworks/weave/milestone/70?closed=1)
|
||||||
|
|
||||||
## Release 2.2.1
|
## Release 2.2.1
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|||||||
@@ -24,6 +24,25 @@ taking the time to read this guide.
|
|||||||
* Keep pull requests small so core developers can review them quickly.
|
* Keep pull requests small so core developers can review them quickly.
|
||||||
* Keep each pull request focused on a specific topic. If you have two things to change, create two pull requests. This helps reviewers to understand the meat of your contribution.
|
* Keep each pull request focused on a specific topic. If you have two things to change, create two pull requests. This helps reviewers to understand the meat of your contribution.
|
||||||
|
|
||||||
|
## Managing Vendored Dependencies
|
||||||
|
|
||||||
|
We copy all Go dependencies into the `vendor` subdirectory and use
|
||||||
|
[`dep`](https://github.com/golang/dep) to manage them.
|
||||||
|
|
||||||
|
If you add a dependency in the code, run `dep ensure` to include it in
|
||||||
|
the config and vendor dir. Same if you remove a dependency from the code.
|
||||||
|
|
||||||
|
To update a single dependency run `dep ensure -update github.com/foo/bar`
|
||||||
|
|
||||||
|
To override `dep`'s choice of library version, you can add
|
||||||
|
[rules](https://golang.github.io/dep/docs/daily-dep.html#rule-changes-in-gopkgtoml).
|
||||||
|
Please add a comment saying why the override is necessary and when it can be
|
||||||
|
removed.
|
||||||
|
|
||||||
|
These operations result in staged but uncommitted changes to your
|
||||||
|
branch; you will need to commit them as normal. Execute them in the
|
||||||
|
root of your checkout.
|
||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
See [WorkingOnWeave](https://github.com/weaveworks/weave/wiki/WorkingOnWeave)
|
See [WorkingOnWeave](https://github.com/weaveworks/weave/wiki/WorkingOnWeave)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
This file lists all individuals having contributed content to the
|
|
||||||
repository.
|
|
||||||
|
|
||||||
Matthew Sackman <matthew@wellquite.org>
|
|
||||||
Matthias Radestock <matthias.radestock@gmail.com>
|
|
||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
./test/assert.sh is a copy of
|
||||||
|
|
||||||
|
https://github.com/lehmannro/assert.sh/blob/master/assert.sh
|
||||||
|
|
||||||
|
Since it was imported from its original source, it has only received
|
||||||
|
cosmetic modifications. As it is licensed under the LGPL-3, here's the
|
||||||
|
license text in its entirety:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based
|
||||||
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
DOCKER_VERSION=17.06
|
DOCKER_VERSION=17.06
|
||||||
KUBERNETES_VERSION=1.9.2
|
KUBERNETES_VERSION=1.11.0
|
||||||
KUBERNETES_CNI_VERSION=0.6.0
|
KUBERNETES_CNI_VERSION=0.6.0
|
||||||
Generated
+1138
File diff suppressed because it is too large.
Load diff
+34
@@ -0,0 +1,34 @@
|
|||||||
|
# Pin to version before sctp was added, because latest libnetwork is
|
||||||
|
# incompatible with latest sctp
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/docker/libnetwork"
|
||||||
|
revision = "119badbd"
|
||||||
|
|
||||||
|
# Encourage dep not to use the docker-1.9 version
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/fsouza/go-dockerclient"
|
||||||
|
version = "1"
|
||||||
|
|
||||||
|
# Use recent version: last tagged version is years old
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/prometheus/client_golang"
|
||||||
|
|
||||||
|
# For compatibility with fsouza/go-dockerclient
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/docker/docker"
|
||||||
|
|
||||||
|
# Most recent release tag is incompatible with docker/docker; see https://github.com/docker/distribution/issues/2503
|
||||||
|
[[override]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/docker/distribution"
|
||||||
|
|
||||||
|
# v8.0.0 removes juju/ratelimit licensed under LGPL3
|
||||||
|
[[constraint]]
|
||||||
|
name = "k8s.io/client-go"
|
||||||
|
revision = "v8.0.0"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
||||||
@@ -17,15 +17,15 @@ QEMU_VERSION=v2.7.0
|
|||||||
|
|
||||||
# A list of all supported architectures here. Should be named as Go is naming platforms
|
# A list of all supported architectures here. Should be named as Go is naming platforms
|
||||||
# All supported architectures must have an "ifeq" block below that customizes the parameters
|
# All supported architectures must have an "ifeq" block below that customizes the parameters
|
||||||
ALL_ARCHITECTURES=amd64 arm arm64 ppc64le
|
ALL_ARCHITECTURES=amd64 arm arm64 ppc64le s390x
|
||||||
ML_PLATFORMS=linux/amd64,linux/arm,linux/arm64,linux/ppc64le
|
ML_PLATFORMS=linux/amd64,linux/arm,linux/arm64,linux/ppc64le,linux/s390x
|
||||||
|
|
||||||
ifeq ($(ARCH),amd64)
|
ifeq ($(ARCH),amd64)
|
||||||
# The architecture to use when downloading the docker binary
|
# The architecture to use when downloading the docker binary
|
||||||
WEAVEEXEC_DOCKER_ARCH?=x86_64
|
WEAVEEXEC_DOCKER_ARCH?=x86_64
|
||||||
|
|
||||||
# The name of the alpine baseimage to use as the base for weave images
|
# The name of the alpine baseimage to use as the base for weave images
|
||||||
ALPINE_BASEIMAGE?=alpine:3.6
|
ALPINE_BASEIMAGE?=alpine:3.10
|
||||||
|
|
||||||
# The extension for the made images
|
# The extension for the made images
|
||||||
# Specifying none means for example weaveworks/weave:latest
|
# Specifying none means for example weaveworks/weave:latest
|
||||||
@@ -42,7 +42,7 @@ ifeq ($(ARCH),arm)
|
|||||||
WEAVEEXEC_DOCKER_ARCH?=armel
|
WEAVEEXEC_DOCKER_ARCH?=armel
|
||||||
|
|
||||||
# Using the (semi-)official alpine image
|
# Using the (semi-)official alpine image
|
||||||
ALPINE_BASEIMAGE?=arm32v6/alpine:3.6
|
ALPINE_BASEIMAGE?=arm32v6/alpine:3.8
|
||||||
|
|
||||||
# arm images have the -arm suffix, for instance weaveworks/weave-arm:latest
|
# arm images have the -arm suffix, for instance weaveworks/weave-arm:latest
|
||||||
ARCH_EXT?=-arm
|
ARCH_EXT?=-arm
|
||||||
@@ -62,7 +62,7 @@ ifeq ($(ARCH),arm64)
|
|||||||
WEAVEEXEC_DOCKER_ARCH?=armel
|
WEAVEEXEC_DOCKER_ARCH?=armel
|
||||||
|
|
||||||
# Using the (semi-)official alpine image
|
# Using the (semi-)official alpine image
|
||||||
ALPINE_BASEIMAGE?=arm64v8/alpine:3.6
|
ALPINE_BASEIMAGE?=arm64v8/alpine:3.8
|
||||||
|
|
||||||
# arm64 images have the -arm64 suffix, for instance weaveworks/weave-arm64:latest
|
# arm64 images have the -arm64 suffix, for instance weaveworks/weave-arm64:latest
|
||||||
ARCH_EXT?=-arm64
|
ARCH_EXT?=-arm64
|
||||||
@@ -82,7 +82,7 @@ ifeq ($(ARCH),ppc64le)
|
|||||||
WEAVEEXEC_DOCKER_ARCH?=ppc64le
|
WEAVEEXEC_DOCKER_ARCH?=ppc64le
|
||||||
|
|
||||||
# Using the (semi-)official alpine image
|
# Using the (semi-)official alpine image
|
||||||
ALPINE_BASEIMAGE?=ppc64le/alpine:3.6
|
ALPINE_BASEIMAGE?=ppc64le/alpine:3.8
|
||||||
|
|
||||||
# ppc64le images have the -ppc64le suffix, for instance weaveworks/weave-ppc64le:latest
|
# ppc64le images have the -ppc64le suffix, for instance weaveworks/weave-ppc64le:latest
|
||||||
ARCH_EXT?=-ppc64le
|
ARCH_EXT?=-ppc64le
|
||||||
@@ -97,10 +97,31 @@ ifeq ($(ARCH),ppc64le)
|
|||||||
# Tell the gcc linker to search for libpcap here
|
# Tell the gcc linker to search for libpcap here
|
||||||
CGO_LDFLAGS="-L/usr/local/lib/$(CC)"
|
CGO_LDFLAGS="-L/usr/local/lib/$(CC)"
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(ARCH),s390x)
|
||||||
|
# The architecture to use when downloading the docker binary
|
||||||
|
WEAVEEXEC_DOCKER_ARCH?=s390x
|
||||||
|
|
||||||
|
# Using the (semi-)official alpine image
|
||||||
|
ALPINE_BASEIMAGE?=s390x/alpine:3.6
|
||||||
|
|
||||||
|
# s390x images have the -s390x suffix, for instance weaveworks/weave-s390x:latest
|
||||||
|
ARCH_EXT?=-s390x
|
||||||
|
|
||||||
|
# The name of the gcc binary
|
||||||
|
CC=s390x-linux-gnu-gcc
|
||||||
|
|
||||||
|
# The architecture name to use when downloading a prebuilt QEMU binary
|
||||||
|
QEMUARCH=s390x
|
||||||
|
|
||||||
|
# In the weaveworks/build image; libpcap libraries for s390x are placed here
|
||||||
|
# Tell the gcc linker to search for libpcap here
|
||||||
|
CGO_LDFLAGS="-L/usr/local/lib/$(CC)"
|
||||||
|
endif
|
||||||
|
|
||||||
# The name of the user that this Makefile should produce image artifacts for. Can/should be overridden
|
# The name of the user that this Makefile should produce image artifacts for. Can/should be overridden
|
||||||
DOCKERHUB_USER?=weaveworks
|
DOCKERHUB_USER?=weaveworks
|
||||||
# The default version that's chosen when pushing the images. Can/should be overridden
|
# The default version that's chosen when pushing the images. Can/should be overridden
|
||||||
|
GIT_REVISION=$(shell git rev-parse HEAD)
|
||||||
WEAVE_VERSION?=git-$(shell git rev-parse --short=12 HEAD)
|
WEAVE_VERSION?=git-$(shell git rev-parse --short=12 HEAD)
|
||||||
# Docker Store does not allow the "latest" tag.
|
# Docker Store does not allow the "latest" tag.
|
||||||
NET_PLUGIN_LATEST=latest_release
|
NET_PLUGIN_LATEST=latest_release
|
||||||
@@ -108,20 +129,20 @@ NET_PLUGIN_LATEST=latest_release
|
|||||||
# Paths to all relevant binaries that should be compiled
|
# Paths to all relevant binaries that should be compiled
|
||||||
WEAVER_EXE=prog/weaver/weaver
|
WEAVER_EXE=prog/weaver/weaver
|
||||||
SIGPROXY_EXE=prog/sigproxy/sigproxy
|
SIGPROXY_EXE=prog/sigproxy/sigproxy
|
||||||
KUBEPEERS_EXE=prog/kube-peers/kube-peers
|
KUBEUTILS_EXE=prog/kube-utils/kube-utils
|
||||||
WEAVENPC_EXE=prog/weave-npc/weave-npc
|
WEAVENPC_EXE=prog/weave-npc/weave-npc
|
||||||
WEAVEWAIT_EXE=prog/weavewait/weavewait
|
WEAVEWAIT_EXE=prog/weavewait/weavewait
|
||||||
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
|
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
|
||||||
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
|
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
|
||||||
WEAVEUTIL_EXE=prog/weaveutil/weaveutil
|
WEAVEUTIL_EXE=prog/weaveutil/weaveutil
|
||||||
RUNNER_EXE=tools/runner/runner
|
RUNNER_EXE=tools/runner/runner
|
||||||
MANIFEST_TOOL_DIR=vendor/github.com/estesp/manifest-tool
|
# Get this from https://github.com/estesp/manifest-tool/releases
|
||||||
MANIFEST_TOOL_EXE=$(MANIFEST_TOOL_DIR)/manifest-tool
|
MANIFEST_TOOL_CMD=manifest-tool
|
||||||
TEST_TLS_EXE=test/tls/tls
|
TEST_TLS_EXE=test/tls/tls
|
||||||
NETWORKTESTER_EXE=test/images/network-tester/webserver
|
NETWORKTESTER_EXE=test/images/network-tester/webserver
|
||||||
|
|
||||||
# All binaries together in a list
|
# All binaries together in a list
|
||||||
EXES=$(WEAVER_EXE) $(SIGPROXY_EXE) $(KUBEPEERS_EXE) $(WEAVENPC_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVEUTIL_EXE) $(RUNNER_EXE) $(TEST_TLS_EXE) $(MANIFEST_TOOL_EXE) $(NETWORKTESTER_EXE)
|
EXES=$(WEAVER_EXE) $(SIGPROXY_EXE) $(KUBEUTILS_EXE) $(WEAVENPC_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVEUTIL_EXE) $(RUNNER_EXE) $(TEST_TLS_EXE) $(NETWORKTESTER_EXE)
|
||||||
|
|
||||||
# These stamp files are used to mark the current state of the build; whether an image has been built or not
|
# These stamp files are used to mark the current state of the build; whether an image has been built or not
|
||||||
BUILD_UPTODATE=.build.uptodate
|
BUILD_UPTODATE=.build.uptodate
|
||||||
@@ -155,18 +176,11 @@ PUSH_ML=push_ml_weave push_ml_weaveexec push_ml_weave-kube push_ml_weave-npc
|
|||||||
|
|
||||||
WEAVE_EXPORT=weave$(ARCH_EXT).tar.gz
|
WEAVE_EXPORT=weave$(ARCH_EXT).tar.gz
|
||||||
|
|
||||||
NETGO_CHECK=@strings $@ | grep cgo_stub\\\.go >/dev/null || { \
|
|
||||||
rm $@; \
|
|
||||||
echo "\nYour go standard library was built without the 'netgo' build tag."; \
|
|
||||||
echo "To fix that, run"; \
|
|
||||||
echo " sudo go clean -i net"; \
|
|
||||||
echo " sudo go install -tags netgo std"; \
|
|
||||||
false; \
|
|
||||||
}
|
|
||||||
# The flags we are passing to go build. -extldflags -static for making a static binary,
|
# The flags we are passing to go build. -extldflags -static for making a static binary,
|
||||||
# -linkmode external for linking external C libraries into the binary, -X main.version for telling the
|
# -X main.version for telling the Go binary which version it is,
|
||||||
# Go binary which version it is, -tags netgo for enforcing the native Go DNS resolver
|
# -s -w drop symbol tables used by debuggers (not Go's internal symbol info)
|
||||||
BUILD_FLAGS=-i -ldflags "-linkmode external -extldflags -static -X main.version=$(WEAVE_VERSION)" -tags netgo
|
# -tags "osusergo netgo" to use native Go UID and DNS implementations
|
||||||
|
BUILD_FLAGS=-ldflags "-extldflags -static -X main.version=$(WEAVE_VERSION) -s -w" -tags "osusergo netgo"
|
||||||
|
|
||||||
PACKAGE_BASE=$(shell go list -e ./)
|
PACKAGE_BASE=$(shell go list -e ./)
|
||||||
|
|
||||||
@@ -179,11 +193,10 @@ $(WEAVER_EXE): api/*.go plugin/*.go plugin/*/*
|
|||||||
$(WEAVER_EXE): proxy/*.go
|
$(WEAVER_EXE): proxy/*.go
|
||||||
$(WEAVEUTIL_EXE): prog/weaveutil/*.go net/*.go plugin/net/*.go plugin/ipam/*.go db/*.go
|
$(WEAVEUTIL_EXE): prog/weaveutil/*.go net/*.go plugin/net/*.go plugin/ipam/*.go db/*.go
|
||||||
$(SIGPROXY_EXE): prog/sigproxy/*.go
|
$(SIGPROXY_EXE): prog/sigproxy/*.go
|
||||||
$(KUBEPEERS_EXE): prog/kube-peers/*.go
|
$(KUBEUTILS_EXE): prog/kube-utils/*.go
|
||||||
$(WEAVENPC_EXE): prog/weave-npc/*.go npc/*.go npc/*/*.go
|
$(WEAVENPC_EXE): prog/weave-npc/*.go npc/*.go npc/*/*.go
|
||||||
$(TEST_TLS_EXE): test/tls/*.go
|
$(TEST_TLS_EXE): test/tls/*.go
|
||||||
$(RUNNER_EXE): tools/runner/*.go
|
$(RUNNER_EXE): tools/runner/*.go
|
||||||
$(MANIFEST_TOOL_EXE): $(MANIFEST_TOOL_DIR)/*.go
|
|
||||||
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
|
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
|
||||||
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
|
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
|
||||||
$(WEAVEWAIT_NOMCAST_EXE): prog/weavewait/*.go net/*.go
|
$(WEAVEWAIT_NOMCAST_EXE): prog/weavewait/*.go net/*.go
|
||||||
@@ -196,13 +209,8 @@ ifeq ($(BUILD_IN_CONTAINER),true)
|
|||||||
# This make target compiles all binaries inside of the weaveworks/build container
|
# This make target compiles all binaries inside of the weaveworks/build container
|
||||||
# It bind-mounts the source into the container and passes all important variables
|
# It bind-mounts the source into the container and passes all important variables
|
||||||
exes $(EXES) tests lint: $(BUILD_UPTODATE)
|
exes $(EXES) tests lint: $(BUILD_UPTODATE)
|
||||||
git submodule update --init
|
|
||||||
# Containernetworking has another copy of vishvananda/netlink which leads to duplicate definitions
|
|
||||||
-@rm -r vendor/github.com/containernetworking/cni/vendor
|
|
||||||
@mkdir -p $(shell pwd)/.pkg
|
|
||||||
$(SUDO) docker run $(RM) $(RUN_FLAGS) \
|
$(SUDO) docker run $(RM) $(RUN_FLAGS) \
|
||||||
-v $(shell pwd):/go/src/github.com/weaveworks/weave \
|
-v $(shell pwd):/go/src/github.com/weaveworks/weave \
|
||||||
-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 \
|
-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) $@
|
$(BUILD_IMAGE) COVERAGE=$(COVERAGE) WEAVE_VERSION=$(WEAVE_VERSION) CC=$(CC) QEMUARCH=$(QEMUARCH) CGO_LDFLAGS=$(CGO_LDFLAGS) $@
|
||||||
touch $@
|
touch $@
|
||||||
@@ -218,23 +226,19 @@ ifeq ($(COVERAGE),true)
|
|||||||
else
|
else
|
||||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||||
endif
|
endif
|
||||||
$(NETGO_CHECK)
|
|
||||||
|
|
||||||
$(WEAVEUTIL_EXE) $(KUBEPEERS_EXE) $(WEAVENPC_EXE) $(NETWORKTESTER_EXE):
|
$(WEAVEUTIL_EXE) $(KUBEUTILS_EXE) $(WEAVENPC_EXE) $(NETWORKTESTER_EXE):
|
||||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||||
$(NETGO_CHECK)
|
|
||||||
|
|
||||||
$(WEAVEWAIT_EXE):
|
$(WEAVEWAIT_EXE):
|
||||||
go build $(BUILD_FLAGS) -tags "netgo iface mcast" -o $@ ./$(@D)
|
go build $(BUILD_FLAGS) -tags "osusergo netgo iface mcast" -o $@ ./$(@D)
|
||||||
$(NETGO_CHECK)
|
|
||||||
|
|
||||||
$(WEAVEWAIT_NOMCAST_EXE):
|
$(WEAVEWAIT_NOMCAST_EXE):
|
||||||
go build $(BUILD_FLAGS) -tags "netgo iface" -o $@ ./$(@D)
|
go build $(BUILD_FLAGS) -tags "osusergo netgo iface" -o $@ ./$(@D)
|
||||||
$(NETGO_CHECK)
|
|
||||||
|
|
||||||
# These programs need a separate rule as they fail the netgo check in
|
# These programs need a separate rule as they fail the netgo check in
|
||||||
# the main build stanza due to not importing net package
|
# the main build stanza due to not importing net package
|
||||||
$(SIGPROXY_EXE) $(TEST_TLS_EXE) $(WEAVEWAIT_NOOP_EXE) $(RUNNER_EXE) $(MANIFEST_TOOL_EXE):
|
$(SIGPROXY_EXE) $(TEST_TLS_EXE) $(WEAVEWAIT_NOOP_EXE) $(RUNNER_EXE):
|
||||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
@@ -247,8 +251,9 @@ endif
|
|||||||
|
|
||||||
# This rule makes sure the build image is up-to-date.
|
# This rule makes sure the build image is up-to-date.
|
||||||
# It also makes sure the multiarch hooks are reqistered in the kernel so the QEMU emulation works
|
# It also makes sure the multiarch hooks are reqistered in the kernel so the QEMU emulation works
|
||||||
$(BUILD_UPTODATE): build/*
|
$(BUILD_UPTODATE): build/* tools/.git
|
||||||
$(SUDO) docker build -t $(BUILD_IMAGE) build/
|
$(SUDO) docker build -t $(BUILD_IMAGE) build/
|
||||||
|
$(SUDO) docker tag $(BUILD_IMAGE) $(BUILD_IMAGE):$(shell tools/image-tag)
|
||||||
$(SUDO) docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
$(SUDO) docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
@@ -274,7 +279,7 @@ endif
|
|||||||
$(WEAVER_UPTODATE): prog/weaver/Dockerfile.$(DOCKERHUB_USER) $(WEAVER_EXE) weave $(WEAVEUTIL_EXE)
|
$(WEAVER_UPTODATE): prog/weaver/Dockerfile.$(DOCKERHUB_USER) $(WEAVER_EXE) weave $(WEAVEUTIL_EXE)
|
||||||
cp $(WEAVEUTIL_EXE) prog/weaver/weaveutil
|
cp $(WEAVEUTIL_EXE) prog/weaver/weaveutil
|
||||||
cp weave prog/weaver/weave
|
cp weave prog/weaver/weave
|
||||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -f prog/weaver/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVER_IMAGE) prog/weaver
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build --build-arg=revision=$(GIT_REVISION) -f prog/weaver/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVER_IMAGE) prog/weaver
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) prog/weaveexec/symlink $(SIGPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVER_UPTODATE)
|
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) prog/weaveexec/symlink $(SIGPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVER_UPTODATE)
|
||||||
@@ -282,7 +287,7 @@ $(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) prog/weaveexe
|
|||||||
cp $(WEAVEWAIT_EXE) prog/weaveexec/weavewait
|
cp $(WEAVEWAIT_EXE) prog/weaveexec/weavewait
|
||||||
cp $(WEAVEWAIT_NOOP_EXE) prog/weaveexec/weavewait_noop
|
cp $(WEAVEWAIT_NOOP_EXE) prog/weaveexec/weavewait_noop
|
||||||
cp $(WEAVEWAIT_NOMCAST_EXE) prog/weaveexec/weavewait_nomcast
|
cp $(WEAVEWAIT_NOMCAST_EXE) prog/weaveexec/weavewait_nomcast
|
||||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -f prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVEEXEC_IMAGE) prog/weaveexec
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build --build-arg=revision=$(GIT_REVISION) -f prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVEEXEC_IMAGE) prog/weaveexec
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
# Builds Docker plugin.
|
# Builds Docker plugin.
|
||||||
@@ -302,17 +307,17 @@ $(PLUGIN_UPTODATE): prog/net-plugin/launch.sh prog/net-plugin/config.json $(WEAV
|
|||||||
$(SUDO) docker plugin create $(PLUGIN_IMAGE):$(NET_PLUGIN_LATEST) prog/net-plugin
|
$(SUDO) docker plugin create $(PLUGIN_IMAGE):$(NET_PLUGIN_LATEST) prog/net-plugin
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(WEAVEKUBE_UPTODATE): prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) prog/weave-kube/launch.sh $(KUBEPEERS_EXE) $(WEAVER_UPTODATE)
|
$(WEAVEKUBE_UPTODATE): prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) prog/weave-kube/launch.sh $(KUBEUTILS_EXE) $(WEAVER_UPTODATE)
|
||||||
cp $(KUBEPEERS_EXE) prog/weave-kube/
|
cp $(KUBEUTILS_EXE) prog/weave-kube/
|
||||||
$(SUDO) docker build -f prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVEKUBE_IMAGE) prog/weave-kube
|
$(SUDO) docker build --build-arg=revision=$(GIT_REVISION) -f prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVEKUBE_IMAGE) prog/weave-kube
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(WEAVENPC_UPTODATE): prog/weave-npc/Dockerfile.$(DOCKERHUB_USER) $(WEAVENPC_EXE) prog/weave-npc/ulogd.conf
|
$(WEAVENPC_UPTODATE): prog/weave-npc/Dockerfile.$(DOCKERHUB_USER) $(WEAVENPC_EXE) prog/weave-npc/ulogd.conf
|
||||||
$(SUDO) docker build -f prog/weave-npc/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVENPC_IMAGE) prog/weave-npc
|
$(SUDO) docker build --build-arg=revision=$(GIT_REVISION) -f prog/weave-npc/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVENPC_IMAGE) prog/weave-npc
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(WEAVEDB_UPTODATE): prog/weavedb/Dockerfile
|
$(WEAVEDB_UPTODATE): prog/weavedb/Dockerfile
|
||||||
$(SUDO) docker build -t $(WEAVEDB_IMAGE) prog/weavedb
|
$(SUDO) docker build --build-arg=revision=$(GIT_REVISION) -t $(WEAVEDB_IMAGE) prog/weavedb
|
||||||
touch $@
|
touch $@
|
||||||
|
|
||||||
$(NETWORKTESTER_UPTODATE): test/images/network-tester/Dockerfile $(NETWORKTESTER_EXE)
|
$(NETWORKTESTER_UPTODATE): test/images/network-tester/Dockerfile $(NETWORKTESTER_EXE)
|
||||||
@@ -322,7 +327,7 @@ $(NETWORKTESTER_UPTODATE): test/images/network-tester/Dockerfile $(NETWORKTESTER
|
|||||||
$(WEAVE_EXPORT): $(IMAGES_UPTODATE) $(WEAVEDB_UPTODATE)
|
$(WEAVE_EXPORT): $(IMAGES_UPTODATE) $(WEAVEDB_UPTODATE)
|
||||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker save $(addsuffix :latest,$(IMAGES)) | gzip > $@
|
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker save $(addsuffix :latest,$(IMAGES)) | gzip > $@
|
||||||
|
|
||||||
tools/.git $(MANIFEST_TOOL_DIR)/.git:
|
tools/.git:
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
|
|
||||||
# CODE FOR PUBLISHING THE IMAGES
|
# CODE FOR PUBLISHING THE IMAGES
|
||||||
@@ -378,13 +383,13 @@ endif
|
|||||||
|
|
||||||
# This target pushes a manifest list; it takes one parameter, the image name.
|
# This target pushes a manifest list; it takes one parameter, the image name.
|
||||||
# The variable UPDATE_LATEST controls whether it updates the versioned tag and/or the latest tag
|
# The variable UPDATE_LATEST controls whether it updates the versioned tag and/or the latest tag
|
||||||
$(PUSH_ML): push_ml_%: $(MANIFEST_TOOL_EXE)
|
$(PUSH_ML): push_ml_%:
|
||||||
ifneq ($(UPDATE_LATEST),latest-only)
|
ifneq ($(UPDATE_LATEST),latest-only)
|
||||||
$(MANIFEST_TOOL_EXE) push from-args --platforms $(ML_PLATFORMS) --template $(DOCKERHUB_USER)/$*-ARCH:$(WEAVE_VERSION) --target $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
$(MANIFEST_TOOL_CMD) push from-args --platforms $(ML_PLATFORMS) --template $(DOCKERHUB_USER)/$*-ARCH:$(WEAVE_VERSION) --target $(DOCKERHUB_USER)/$*:$(WEAVE_VERSION)
|
||||||
endif
|
endif
|
||||||
ifneq ($(UPDATE_LATEST),false)
|
ifneq ($(UPDATE_LATEST),false)
|
||||||
# Push the manifest list to :latest as well
|
# Push the manifest list to :latest as well
|
||||||
$(MANIFEST_TOOL_EXE) push from-args --platforms $(ML_PLATFORMS) --template $(DOCKERHUB_USER)/$*-ARCH:latest --target $(DOCKERHUB_USER)/$*:latest
|
$(MANIFEST_TOOL_CMD) push from-args --platforms $(ML_PLATFORMS) --template $(DOCKERHUB_USER)/$*-ARCH:latest --target $(DOCKERHUB_USER)/$*:latest
|
||||||
endif
|
endif
|
||||||
|
|
||||||
clean-work-dir:
|
clean-work-dir:
|
||||||
@@ -394,15 +399,14 @@ clean-bin:
|
|||||||
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(IMAGES)
|
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(IMAGES)
|
||||||
find prog -type f -name "Dockerfile.*" -not -name "Dockerfile.template" -print | xargs rm -f
|
find prog -type f -name "Dockerfile.*" -not -name "Dockerfile.template" -print | xargs rm -f
|
||||||
find prog -type f -name "*qemu-*" -print | xargs rm -f
|
find prog -type f -name "*qemu-*" -print | xargs rm -f
|
||||||
rm -rf $(EXES) $(IMAGES_UPTODATE) $(WEAVEDB_UPTODATE) weave*.tar.gz .pkg
|
go clean ./...
|
||||||
|
rm -rf $(EXES) $(IMAGES_UPTODATE) $(WEAVEDB_UPTODATE) weave*.tar.gz .cache
|
||||||
|
|
||||||
clean: clean-bin clean-work-dir
|
clean: clean-bin clean-work-dir
|
||||||
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(BUILD_IMAGE)
|
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(BUILD_IMAGE)
|
||||||
rm -rf test/tls/*.pem test/coverage.* test/coverage $(BUILD_UPTODATE) $(MANIFEST_TOOL_EXE)
|
rm -rf test/tls/*.pem test/coverage.* test/coverage $(BUILD_UPTODATE)
|
||||||
|
|
||||||
build:
|
build:
|
||||||
$(SUDO) go clean -i net
|
|
||||||
$(SUDO) go install -tags netgo std
|
|
||||||
$(MAKE)
|
$(MAKE)
|
||||||
|
|
||||||
run-smoketests: all testrunner
|
run-smoketests: all testrunner
|
||||||
|
|||||||
@@ -7,10 +7,9 @@
|
|||||||
|
|
||||||
# About Weaveworks
|
# About Weaveworks
|
||||||
|
|
||||||
[Weaveworks](https://www.weave.works) is the company that delivers the most productive way for developers to connect, observe and control
|
[Weaveworks](https://www.weave.works) is the company that delivers the most productive way for developers to connect, observe and control Docker containers.
|
||||||
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, 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, with over 8 million downloads to date. Weave Net enables you to get started with Docker clusters and portable apps in a fraction of the time required by other solutions.
|
||||||
|
|
||||||
## Weave Cloud:
|
## Weave Cloud:
|
||||||
|
|
||||||
@@ -29,11 +28,10 @@ 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:
|
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/overview).
|
- 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.
|
- Invite yourself to the <a href="https://slack.weave.works/" target="_blank">Weave Users Slack</a>.
|
||||||
- Ask a question on the <a href="https://weave-community.slack.com/messages/general/"> #weave-community</a> slack channel.
|
- Ask a question on the [#net-dev](https://weave-community.slack.com/messages/net-dev/) 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.
|
- 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.
|
||||||
- Send an email to <a href="mailto:weave-users@weave.works">weave-users@weave.works</a>
|
- [File an issue](https://github.com/weaveworks/weave/issues/new).
|
||||||
- <a href="https://github.com/weaveworks/weave/issues/new">File an issue.</a>
|
|
||||||
|
|
||||||
Your feedback is always welcome!
|
Your feedback is always welcome!
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Use of vendored code in Weave Net
|
||||||
|
|
||||||
|
Weave Net is licensed under the [Apache 2.0 license](LICENSE).
|
||||||
|
|
||||||
|
Some vendored code is under different licenses though, all of them ship the
|
||||||
|
entire license text they are under.
|
||||||
|
|
||||||
|
- https://github.com/weaveworks/go-checkpoint
|
||||||
|
can be found in the ./vendor/ directory, is under MPL-2.0.
|
||||||
|
|
||||||
|
- Pulled in by dependencies are
|
||||||
|
https://github.com/hashicorp/golang-lru (MPL-2.0)
|
||||||
|
https://github.com/hashicorp/go-cleanhttp (MPL-2.0)
|
||||||
|
https://github.com/opencontainers/go-digest (docs are under CC by-sa 4.0)
|
||||||
|
|
||||||
|
[One file used in tests](COPYING.LGPL-3) is under LGPL-3, that's why we ship
|
||||||
|
the license text in this repository.
|
||||||
@@ -3,6 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Expose calls the router to assign the given IP addr to the weave bridge.
|
// Expose calls the router to assign the given IP addr to the weave bridge.
|
||||||
@@ -10,3 +11,9 @@ func (client *Client) Expose(ipAddr *net.IPNet) error {
|
|||||||
_, err := client.httpVerb("POST", fmt.Sprintf("/expose/%s", ipAddr), nil)
|
_, err := client.httpVerb("POST", fmt.Sprintf("/expose/%s", ipAddr), nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReplacePeers replace the current set of peers
|
||||||
|
func (client *Client) ReplacePeers(peers []string) error {
|
||||||
|
_, err := client.httpVerb("POST", "/connect", url.Values{"replace": {"true"}, "peer": peers})
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
source "$STATE"
|
|
||||||
|
|
||||||
if [ -n "$TEST_AND_PUBLISH" ]; then
|
|
||||||
cd "$SRCDIR"
|
|
||||||
git submodule update --init
|
|
||||||
cd "$SRCDIR/build"
|
|
||||||
../tools/rebuild-image weaveworks/weavebuild . Dockerfile build.sh
|
|
||||||
touch "$SRCDIR/.build.uptodate"
|
|
||||||
sudo chown -R ubuntu /usr/local/go
|
|
||||||
cd "$SRCDIR"
|
|
||||||
make testrunner
|
|
||||||
make COVERAGE=true RM= exes all
|
|
||||||
fi
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "TEST_AND_PUBLISH=1" >"$STATE"
|
|
||||||
|
|
||||||
# Avoid doing a full build if we can. Note if the PR doesn't exist
|
|
||||||
# when the build is kicked off, we can't run this check so we do a
|
|
||||||
# full build. Subsequent pushes to the same branch will have the desired behaviour
|
|
||||||
if [ -n "$CI_PULL_REQUEST" -a "$CI_PULL_REQUEST" = "$CI_PULL_REQUESTS" ]; then
|
|
||||||
if PR=$(echo "$CI_PULL_REQUEST" | grep -oP '(?<=^https://github.com/weaveworks/weave/pull/)[0-9]+$'); then
|
|
||||||
echo "GH_PR=$PR" >>"$STATE"
|
|
||||||
if FILES=$(curl -s "https://api.github.com/repos/weaveworks/weave/pulls/$PR/files" | jq -r '.[]|.filename'); then
|
|
||||||
echo "PR touches [$FILES]"
|
|
||||||
if ! echo "$FILES" | grep -v ^site/; then
|
|
||||||
echo "PR contains site changes only - skipping tests"
|
|
||||||
echo "TEST_AND_PUBLISH=" >>"$STATE"
|
|
||||||
else
|
|
||||||
echo "PR contains non-site changes - running tests"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Unable to get files for PR $PR" >&2
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Unable to extract PR number from $CI_PULL_REQUEST" >&2
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
source "$STATE"
|
|
||||||
|
|
||||||
mkdir -p "$(dirname "$SRCDIR")" && cp -r "$(pwd)/" "$SRCDIR"
|
|
||||||
|
|
||||||
if [ -n "$TEST_AND_PUBLISH" ]; then
|
|
||||||
cd "$SRCDIR"
|
|
||||||
git submodule update --init
|
|
||||||
fi
|
|
||||||
@@ -2,12 +2,7 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
source "$STATE"
|
docker login -u $DOCKER_USER -p $DOCKER_PASS
|
||||||
|
make clean-bin
|
||||||
if [ -n "$TEST_AND_PUBLISH" ] ; then
|
# CircleCI doesn't let us install the qemu module to do ARM docker builds
|
||||||
docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
|
make publish ALL_ARCHITECTURES=amd64 UPDATE_LATEST=latest-only SUDO= BUILD_IN_CONTAINER=false
|
||||||
cd $SRCDIR
|
|
||||||
make clean-bin
|
|
||||||
# CircleCI doesn't let us install the qemu module to do ARM docker builds
|
|
||||||
make publish ALL_ARCHITECTURES=amd64 UPDATE_LATEST=latest-only
|
|
||||||
fi
|
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source "$SRCDIR/bin/circle-env"
|
||||||
|
cd "$SRCDIR/test"
|
||||||
|
./run-integration-tests.sh destroy
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Set environment variables here because CircleCI can't expand $FOO in 'env' section
|
||||||
|
|
||||||
|
export PATH="$PATH:$HOME/.local/bin:/opt/google-cloud-sdk/bin"
|
||||||
|
export CLOUDSDK_CORE_DISABLE_PROMPTS=1
|
||||||
|
# Base name of VMs for integration tests:
|
||||||
|
export NAME=test-$CIRCLE_BUILD_NUM-$CIRCLE_NODE_INDEX
|
||||||
|
export TEST_VMS_PROV_AND_CONF_LOCK_FILE=$CIRCLE_ARTIFACTS/test_vms_provisioned_and_configured.txt
|
||||||
|
export TEST_VMS_READY_LOCK_FILE=$CIRCLE_ARTIFACTS/test_vms_ready.txt
|
||||||
|
# Google Cloud Platorm images for integration tests:
|
||||||
|
export USE_IMAGE=1
|
||||||
|
export DISK_NAME_PREFIX=test-$CIRCLE_BUILD_NUM-0
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
source "$STATE"
|
|
||||||
|
|
||||||
if [ -n "$TEST_AND_PUBLISH" ] ; then
|
|
||||||
if [ "$CIRCLE_NODE_INDEX" = "0" ] ; then
|
|
||||||
go get github.com/mattn/goveralls
|
|
||||||
goveralls -repotoken $COVERALLS_REPO_TOKEN -coverprofile=$SRCDIR/test/profile.cov -service=circleci
|
|
||||||
cd $SRCDIR/test; cp coverage.* $CIRCLE_ARTIFACTS
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
+14
-15
@@ -9,22 +9,21 @@ function signal_failure() {
|
|||||||
}
|
}
|
||||||
trap signal_failure ERR
|
trap signal_failure ERR
|
||||||
|
|
||||||
source "$STATE"
|
|
||||||
source "$(dirname "$0")/wait_for.sh"
|
source "$(dirname "$0")/wait_for.sh"
|
||||||
|
|
||||||
if [ -n "$TEST_AND_PUBLISH" ]; then
|
[ -n "$SECRET_KEY" ] || {
|
||||||
[ -n "$SECRET_KEY" ] || {
|
echo "Cannot run smoke tests: no secret key"
|
||||||
echo "Cannot run smoke tests: no secret key"
|
exit 1
|
||||||
exit 1
|
}
|
||||||
}
|
|
||||||
|
|
||||||
# Provisioning from image typically take 90 seconds,
|
source "$SRCDIR/bin/circle-env"
|
||||||
# but if we are creating a GCP image, it can take more than 10 minutes:
|
|
||||||
wait_for 900 "$TEST_VMS_PROV_AND_CONF_LOCK_FILE"
|
|
||||||
|
|
||||||
# Upload built binaries to testing VMs:
|
# Provisioning from image typically take 90 seconds,
|
||||||
cd "$SRCDIR/test" # Ensures we generate Terraform state files in the right folder, for later use by integration tests.
|
# but if we are creating a GCP image, it can take more than 10 minutes:
|
||||||
./run-integration-tests.sh setup >>"$TEST_VMS_SETUP_OUTPUT_FILE" 2>&1
|
wait_for 900 "$TEST_VMS_PROV_AND_CONF_LOCK_FILE"
|
||||||
echo "OK" >"$TEST_VMS_READY_LOCK_FILE"
|
|
||||||
echo "Test VMs now ready. $(date)." >>"$TEST_VMS_SETUP_OUTPUT_FILE"
|
# Upload built binaries to testing VMs:
|
||||||
fi
|
cd "$SRCDIR/test" # Ensures we generate Terraform state files in the right folder, for later use by integration tests.
|
||||||
|
./run-integration-tests.sh setup
|
||||||
|
echo "OK" >"$TEST_VMS_READY_LOCK_FILE"
|
||||||
|
echo "Test VMs now ready. $(date)."
|
||||||
+14
-15
@@ -2,22 +2,21 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
source "$STATE"
|
|
||||||
source "$(dirname "$0")/wait_for.sh"
|
source "$(dirname "$0")/wait_for.sh"
|
||||||
|
|
||||||
if [ -n "$TEST_AND_PUBLISH" ]; then
|
[ -n "$SECRET_KEY" ] || {
|
||||||
[ -n "$SECRET_KEY" ] || {
|
echo "Cannot run smoke tests: no secret key"
|
||||||
echo "Cannot run smoke tests: no secret key"
|
exit 1
|
||||||
exit 1
|
}
|
||||||
}
|
|
||||||
|
|
||||||
# Provisioning from image typically take 90 seconds,
|
source "$SRCDIR/bin/circle-env"
|
||||||
# but if we are creating a GCP image, it can take more than 10 minutes:
|
|
||||||
wait_for 900 "$TEST_VMS_READY_LOCK_FILE"
|
|
||||||
|
|
||||||
# Run integration tests:
|
# Provisioning from image typically take 90 seconds,
|
||||||
export COVERAGE=true
|
# but if we are creating a GCP image, it can take more than 10 minutes:
|
||||||
export WEAVE_NET_SANITY_CHECKS_FILES="$CIRCLE_ARTIFACTS/weave_net_sanity_check_*.log"
|
wait_for 900 "$TEST_VMS_READY_LOCK_FILE"
|
||||||
cd "$SRCDIR/test" # Ensures we generate code coverage files in the right folder, and use the Terraform state of previously provisionned VMs.
|
|
||||||
./run-integration-tests.sh test
|
# Run integration tests:
|
||||||
fi
|
export COVERAGE=true
|
||||||
|
export WEAVE_NET_SANITY_CHECKS_FILES="$CIRCLE_ARTIFACTS/weave_net_sanity_check_*.log"
|
||||||
|
cd "$SRCDIR/test" # Ensures we generate code coverage files in the right folder, and use the Terraform state of previously provisionned VMs.
|
||||||
|
./run-integration-tests.sh test
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
source "$STATE"
|
|
||||||
|
|
||||||
if [ -n "$TEST_AND_PUBLISH" ] ; then
|
|
||||||
cd $SRCDIR/test
|
|
||||||
test "$CIRCLE_NODE_INDEX" != "0" || (cd $SRCDIR/test; ./gen_coverage_reports.sh)
|
|
||||||
fi
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
source "$STATE"
|
|
||||||
|
|
||||||
if [ -n "$TEST_AND_PUBLISH" ] ; then
|
|
||||||
cd $SRCDIR
|
|
||||||
make DEBUG=1 lint
|
|
||||||
COVERDIR=test/coverage make RM= tests
|
|
||||||
fi
|
|
||||||
+16
-19
@@ -9,10 +9,8 @@ function signal_failure() {
|
|||||||
}
|
}
|
||||||
trap signal_failure ERR
|
trap signal_failure ERR
|
||||||
|
|
||||||
source "$STATE"
|
|
||||||
|
|
||||||
function install_terraform() {
|
function install_terraform() {
|
||||||
curl -fsS https://releases.hashicorp.com/terraform/0.8.5/terraform_0.8.5_linux_amd64.zip | gunzip >terraform && chmod +x terraform && sudo mv terraform /usr/bin
|
curl -fsS https://releases.hashicorp.com/terraform/0.11.14/terraform_0.11.14_linux_amd64.zip | gunzip >terraform && chmod +x terraform && sudo mv terraform /usr/bin
|
||||||
}
|
}
|
||||||
|
|
||||||
function install_ansible() {
|
function install_ansible() {
|
||||||
@@ -20,24 +18,23 @@ function install_ansible() {
|
|||||||
sudo apt-get install -qq -y python-pip python-dev libffi-dev libssl-dev \
|
sudo apt-get install -qq -y python-pip python-dev libffi-dev libssl-dev \
|
||||||
&& pip install --user -U setuptools cffi \
|
&& pip install --user -U setuptools cffi \
|
||||||
&& pip install --user ansible
|
&& pip install --user ansible
|
||||||
export PATH="$PATH:$HOME/.local/bin"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ -n "$TEST_AND_PUBLISH" ]; then
|
[ -n "$SECRET_KEY" ] || {
|
||||||
[ -n "$SECRET_KEY" ] || {
|
echo "Cannot run smoke tests: no secret key"
|
||||||
echo "Cannot run smoke tests: no secret key"
|
exit 1
|
||||||
exit 1
|
}
|
||||||
}
|
|
||||||
|
|
||||||
install_terraform >>"$TEST_VMS_SETUP_OUTPUT_FILE" 2>&1
|
source "$SRCDIR/bin/circle-env"
|
||||||
install_ansible >>"$TEST_VMS_SETUP_OUTPUT_FILE" 2>&1
|
|
||||||
|
|
||||||
# Only attempt to create GCP image in first container, wait for it to be created otherwise:
|
install_terraform
|
||||||
[ "$CIRCLE_NODE_INDEX" != "0" ] && export CREATE_IMAGE=0
|
install_ansible
|
||||||
|
|
||||||
# Provision and configure testing VMs:
|
# Only attempt to create GCP image in first container, wait for it to be created otherwise:
|
||||||
cd "$SRCDIR/test" # Ensures we generate Terraform state files in the right folder, for later use by integration tests.
|
[ "$CIRCLE_NODE_INDEX" != "0" ] && export CREATE_IMAGE=0
|
||||||
./run-integration-tests.sh configure >>"$TEST_VMS_SETUP_OUTPUT_FILE" 2>&1
|
|
||||||
echo "OK" >"$TEST_VMS_PROV_AND_CONF_LOCK_FILE"
|
# Provision and configure testing VMs:
|
||||||
echo "Test VMs now provisioned and configured. $(date)." >>"$TEST_VMS_SETUP_OUTPUT_FILE"
|
cd "$SRCDIR/test" # Ensures we generate Terraform state files in the right folder, for later use by integration tests.
|
||||||
fi
|
./run-integration-tests.sh configure
|
||||||
|
echo "OK" >"$TEST_VMS_PROV_AND_CONF_LOCK_FILE"
|
||||||
|
echo "Test VMs now provisioned and configured. $(date)."
|
||||||
+45
-15
@@ -89,9 +89,11 @@ build() {
|
|||||||
## Inject the version numbers and build the distributables
|
## Inject the version numbers and build the distributables
|
||||||
## (library versions?)
|
## (library versions?)
|
||||||
sed -i.bak "/SCRIPT_VERSION=/ c\SCRIPT_VERSION=\"$VERSION\"" ./weave
|
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.6.yaml
|
||||||
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.7.yaml
|
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.7.yaml
|
||||||
|
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.8.yaml
|
||||||
|
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.9.yaml
|
||||||
|
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.11.yaml
|
||||||
make SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER
|
make SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER
|
||||||
|
|
||||||
if make tests; then
|
if make tests; then
|
||||||
@@ -160,13 +162,6 @@ draft() {
|
|||||||
--name "weave" \
|
--name "weave" \
|
||||||
--file "./weave"
|
--file "./weave"
|
||||||
|
|
||||||
github-release upload \
|
|
||||||
--user $GITHUB_USER \
|
|
||||||
--repo weave \
|
|
||||||
--tag $LATEST_TAG \
|
|
||||||
--name "weave-daemonset.yaml" \
|
|
||||||
--file "./prog/weave-kube/weave-daemonset.yaml"
|
|
||||||
|
|
||||||
github-release upload \
|
github-release upload \
|
||||||
--user $GITHUB_USER \
|
--user $GITHUB_USER \
|
||||||
--repo weave \
|
--repo weave \
|
||||||
@@ -181,6 +176,27 @@ draft() {
|
|||||||
--name "weave-daemonset-k8s-1.7.yaml" \
|
--name "weave-daemonset-k8s-1.7.yaml" \
|
||||||
--file "./prog/weave-kube/weave-daemonset-k8s-1.7.yaml"
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.7.yaml"
|
||||||
|
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave \
|
||||||
|
--tag $LATEST_TAG \
|
||||||
|
--name "weave-daemonset-k8s-1.8.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.8.yaml"
|
||||||
|
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave \
|
||||||
|
--tag $LATEST_TAG \
|
||||||
|
--name "weave-daemonset-k8s-1.9.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.9.yaml"
|
||||||
|
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave \
|
||||||
|
--tag $LATEST_TAG \
|
||||||
|
--name "weave-daemonset-k8s-1.11.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.11.yaml"
|
||||||
|
|
||||||
echo "** Draft $TYPE $RELEASE_NAME $VERSION created at"
|
echo "** Draft $TYPE $RELEASE_NAME $VERSION created at"
|
||||||
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
||||||
}
|
}
|
||||||
@@ -268,13 +284,6 @@ publish() {
|
|||||||
--name "weave" \
|
--name "weave" \
|
||||||
--file "./weave"
|
--file "./weave"
|
||||||
|
|
||||||
github-release upload \
|
|
||||||
--user $GITHUB_USER \
|
|
||||||
--repo weave \
|
|
||||||
--tag latest_release \
|
|
||||||
--name "weave-daemonset.yaml" \
|
|
||||||
--file "./prog/weave-kube/weave-daemonset.yaml"
|
|
||||||
|
|
||||||
github-release upload \
|
github-release upload \
|
||||||
--user $GITHUB_USER \
|
--user $GITHUB_USER \
|
||||||
--repo weave \
|
--repo weave \
|
||||||
@@ -289,6 +298,27 @@ publish() {
|
|||||||
--name "weave-daemonset-k8s-1.7.yaml" \
|
--name "weave-daemonset-k8s-1.7.yaml" \
|
||||||
--file "./prog/weave-kube/weave-daemonset-k8s-1.7.yaml"
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.7.yaml"
|
||||||
|
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave \
|
||||||
|
--tag latest_release \
|
||||||
|
--name "weave-daemonset-k8s-1.8.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.8.yaml"
|
||||||
|
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave \
|
||||||
|
--tag latest_release \
|
||||||
|
--name "weave-daemonset-k8s-1.9.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.9.yaml"
|
||||||
|
|
||||||
|
github-release upload \
|
||||||
|
--user $GITHUB_USER \
|
||||||
|
--repo weave \
|
||||||
|
--tag latest_release \
|
||||||
|
--name "weave-daemonset-k8s-1.11.yaml" \
|
||||||
|
--file "./prog/weave-kube/weave-daemonset-k8s-1.11.yaml"
|
||||||
|
|
||||||
echo "** Release $RELEASE_NAME $VERSION published at"
|
echo "** Release $RELEASE_NAME $VERSION published at"
|
||||||
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
||||||
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/latest_release"
|
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/latest_release"
|
||||||
|
|||||||
+1
-1
@@ -16,6 +16,6 @@ wait_for() {
|
|||||||
if ! ((i % 10)); then echo "[$i seconds]: Waiting for $lock_file to be created..."; fi
|
if ! ((i % 10)); then echo "[$i seconds]: Waiting for $lock_file to be created..."; fi
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
echo "Timed out waiting for test VMs to be ready. See details in: $TEST_VMS_SETUP_OUTPUT_FILE" >&2
|
echo "Timed out waiting for test VMs to be ready." >&2
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
+15
-23
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.8.1
|
FROM golang:1.13.3-buster
|
||||||
|
|
||||||
# Support Raspberry Pi 2 and newer
|
# Support Raspberry Pi 2 and newer
|
||||||
ENV GOARM 7
|
ENV GOARM 7
|
||||||
@@ -7,25 +7,25 @@ ENV GOARM 7
|
|||||||
ENV DEB_CROSSPLATFORMS \
|
ENV DEB_CROSSPLATFORMS \
|
||||||
armhf \
|
armhf \
|
||||||
arm64 \
|
arm64 \
|
||||||
ppc64el
|
ppc64el \
|
||||||
|
s390x
|
||||||
|
|
||||||
# The go platforms we are supporting to cross-compile
|
# The go platforms we are supporting to cross-compile
|
||||||
ENV GO_CROSSPLATFORMS \
|
ENV GO_CROSSPLATFORMS \
|
||||||
linux/arm \
|
linux/arm \
|
||||||
linux/arm64 \
|
linux/arm64 \
|
||||||
linux/ppc64le
|
linux/ppc64le \
|
||||||
|
linux/s390x
|
||||||
|
|
||||||
# The names of the gcc cross-compilers we have installed
|
# The names of the gcc cross-compilers we have installed
|
||||||
ENV GCC_CROSSCOMPILERS \
|
ENV GCC_CROSSCOMPILERS \
|
||||||
arm-linux-gnueabihf \
|
arm-linux-gnueabihf \
|
||||||
aarch64-linux-gnu \
|
aarch64-linux-gnu \
|
||||||
powerpc64le-linux-gnu
|
powerpc64le-linux-gnu \
|
||||||
|
s390x-linux-gnu
|
||||||
|
|
||||||
# Temporarily add the Debian repositories, because we're going to install gcc cross-compilers from there
|
|
||||||
# Install the build-essential and crossbuild-essential-ARCH packages
|
# Install the build-essential and crossbuild-essential-ARCH packages
|
||||||
RUN echo "deb http://emdebian.org/tools/debian/ jessie main" > /etc/apt/sources.list.d/cgocrosscompiling.list \
|
RUN for platform in ${DEB_CROSSPLATFORMS}; do dpkg --add-architecture $platform; done \
|
||||||
&& 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 update \
|
||||||
&& apt-get install -y build-essential \
|
&& apt-get install -y build-essential \
|
||||||
&& for platform in ${DEB_CROSSPLATFORMS}; do apt-get install -y crossbuild-essential-${platform}; done \
|
&& for platform in ${DEB_CROSSPLATFORMS}; do apt-get install -y crossbuild-essential-${platform}; done \
|
||||||
@@ -50,31 +50,20 @@ RUN curl -fsSLo shfmt https://github.com/mvdan/sh/releases/download/v1.3.0/shfmt
|
|||||||
|
|
||||||
# Install common Go tools
|
# Install common Go tools
|
||||||
RUN go get \
|
RUN go get \
|
||||||
github.com/golang/lint/golint \
|
github.com/weaveworks/build-tools/cover \
|
||||||
|
github.com/mattn/goveralls \
|
||||||
|
golang.org/x/lint/golint \
|
||||||
github.com/fzipp/gocyclo \
|
github.com/fzipp/gocyclo \
|
||||||
github.com/fatih/hclfmt \
|
github.com/fatih/hclfmt \
|
||||||
github.com/client9/misspell/cmd/misspell
|
github.com/client9/misspell/cmd/misspell
|
||||||
|
|
||||||
# First clean, because we're gonna rebuild the std both with -race enabled and disabled
|
|
||||||
# Running go install std twice is intentional and required, as content is written in two different directories
|
|
||||||
# (/usr/local/go/pkg/linux_amd64 vs /usr/local/go/pkg/linux_amd64_race)
|
|
||||||
# The other architectures do not have support for -race
|
|
||||||
RUN go clean -i net \
|
|
||||||
&& go install -tags netgo std \
|
|
||||||
&& go install -race -tags netgo std
|
|
||||||
|
|
||||||
# Prebuild std for the other architectures
|
|
||||||
RUN for platform in ${GO_CROSSPLATFORMS}; do \
|
|
||||||
GOOS=${platform%/*} GOARCH=${platform##*/} \
|
|
||||||
go install -tags netgo std; done
|
|
||||||
|
|
||||||
# Allow full write access to the Go folders for anyone
|
# Allow full write access to the Go folders for anyone
|
||||||
RUN chmod -R a+w /usr/local/go
|
RUN chmod -R a+w /usr/local/go
|
||||||
|
|
||||||
# The libpcap version from Debian packages is 1.6.2, matching that version here although newer versions of libpcap have been released
|
# The libpcap version from Debian packages is 1.6.2, matching that version here although newer versions of libpcap have been released
|
||||||
# We have to cross-compile the libpcap library for the new architectures, that's what we're doing here
|
# We have to cross-compile the libpcap library for the new architectures, that's what we're doing here
|
||||||
ENV LIBPCAP_CROSS_VERSION=1.6.2
|
ENV LIBPCAP_CROSS_VERSION=1.6.2
|
||||||
RUN curl -sSL http://www.tcpdump.org/release/libpcap-${LIBPCAP_CROSS_VERSION}.tar.gz | tar -xz \
|
RUN curl -sSL https://www.tcpdump.org/release/libpcap-${LIBPCAP_CROSS_VERSION}.tar.gz | tar -xz \
|
||||||
&& cd libpcap-${LIBPCAP_CROSS_VERSION} \
|
&& cd libpcap-${LIBPCAP_CROSS_VERSION} \
|
||||||
&& for crosscompiler in ${GCC_CROSSCOMPILERS}; do \
|
&& for crosscompiler in ${GCC_CROSSCOMPILERS}; do \
|
||||||
CC=${crosscompiler}-gcc ac_cv_linux_vers=2 ./configure --host=${crosscompiler} --with-pcap=linux \
|
CC=${crosscompiler}-gcc ac_cv_linux_vers=2 ./configure --host=${crosscompiler} --with-pcap=linux \
|
||||||
@@ -86,5 +75,8 @@ RUN curl -sSL http://www.tcpdump.org/release/libpcap-${LIBPCAP_CROSS_VERSION}.ta
|
|||||||
&& ln -sf libpcap.so.${MAJOR_VER} /usr/local/lib/libpcap.so \
|
&& ln -sf libpcap.so.${MAJOR_VER} /usr/local/lib/libpcap.so \
|
||||||
&& make clean; done
|
&& make clean; done
|
||||||
|
|
||||||
|
# Install Docker
|
||||||
|
RUN curl -fsSL https://get.docker.com | VERSION=18.06.0-ce /bin/sh
|
||||||
|
|
||||||
COPY build.sh /
|
COPY build.sh /
|
||||||
ENTRYPOINT ["sh", "/build.sh"]
|
ENTRYPOINT ["sh", "/build.sh"]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
bab3b4d04263479b3d9f85cf59bd0cad1f2b434f29856b2204f8288f2f3598e4 qemu-s390x-static
|
||||||
-66
@@ -1,66 +0,0 @@
|
|||||||
general:
|
|
||||||
branches:
|
|
||||||
ignore:
|
|
||||||
- gh\-pages
|
|
||||||
|
|
||||||
machine:
|
|
||||||
pre:
|
|
||||||
- curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.0
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
environment:
|
|
||||||
GOPATH: /home/ubuntu:$GOPATH
|
|
||||||
SRCDIR: /home/ubuntu/src/github.com/weaveworks/weave
|
|
||||||
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:
|
|
||||||
NAME: test-$CIRCLE_BUILD_NUM-$CIRCLE_NODE_INDEX
|
|
||||||
TEST_VMS_SETUP_OUTPUT_FILE: $CIRCLE_ARTIFACTS/test_vms_setup_output.txt
|
|
||||||
TEST_VMS_PROV_AND_CONF_LOCK_FILE: $CIRCLE_ARTIFACTS/test_vms_provisioned_and_configured.txt
|
|
||||||
TEST_VMS_READY_LOCK_FILE: $CIRCLE_ARTIFACTS/test_vms_ready.txt
|
|
||||||
# Google Cloud Platorm images for integration tests:
|
|
||||||
USE_IMAGE: 1
|
|
||||||
DISK_NAME_PREFIX: test-$CIRCLE_BUILD_NUM-0
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
cache_directories:
|
|
||||||
- "~/docker"
|
|
||||||
post:
|
|
||||||
- bin/circle-dependencies-post-sources
|
|
||||||
# Provision & configure testing VMs:
|
|
||||||
- "while [ ! -d $SRCDIR ]; do sleep 1; done; $SRCDIR/bin/provision_test_vms.sh":
|
|
||||||
parallel: true
|
|
||||||
background: true
|
|
||||||
- bin/circle-dependencies-post
|
|
||||||
|
|
||||||
test:
|
|
||||||
pre:
|
|
||||||
- "$SRCDIR/bin/circle-test-pre":
|
|
||||||
parallel: true
|
|
||||||
background: true
|
|
||||||
override:
|
|
||||||
- "$SRCDIR/bin/circle-test-unit":
|
|
||||||
parallel: true
|
|
||||||
- "$SRCDIR/bin/circle-test-smoke":
|
|
||||||
parallel: true
|
|
||||||
timeout: 300
|
|
||||||
post:
|
|
||||||
- "$SRCDIR/bin/circle-test-teardown":
|
|
||||||
parallel: true
|
|
||||||
|
|
||||||
teardown:
|
|
||||||
pre:
|
|
||||||
# Destroy testing VMs:
|
|
||||||
- "cd $SRCDIR/test && ./run-integration-tests.sh destroy":
|
|
||||||
parallel: true
|
|
||||||
background: true
|
|
||||||
- "$SRCDIR/bin/circle-teardown-pre":
|
|
||||||
parallel: true
|
|
||||||
|
|
||||||
deployment:
|
|
||||||
master:
|
|
||||||
branch: master
|
|
||||||
owner: weaveworks
|
|
||||||
commands:
|
|
||||||
- "$SRCDIR/bin/circle-deploy-master"
|
|
||||||
@@ -99,7 +99,9 @@ func (c *Client) AddObserver(ob ContainerObserver) error {
|
|||||||
pending := make(pendingStarts)
|
pending := make(pendingStarts)
|
||||||
retryInterval := InitialInterval
|
retryInterval := InitialInterval
|
||||||
for {
|
for {
|
||||||
events := make(chan *docker.APIEvents)
|
// Use a buffered chan so the client library can run ahead of the listener
|
||||||
|
// - Docker will drop an event if it is not collected quickly enough.
|
||||||
|
events := make(chan *docker.APIEvents, 1024)
|
||||||
if err := c.AddEventListener(events); err != nil {
|
if err := c.AddEventListener(events); err != nil {
|
||||||
c.errorf("Unable to add listener to Docker API: %s - retrying in %ds", err, retryInterval/time.Second)
|
c.errorf("Unable to add listener to Docker API: %s - retrying in %ds", err, retryInterval/time.Second)
|
||||||
} else {
|
} else {
|
||||||
@@ -231,7 +233,7 @@ func (c *Client) EnsureNetwork(networkName, driver, subnet string, options map[s
|
|||||||
Name: networkName,
|
Name: networkName,
|
||||||
CheckDuplicate: true,
|
CheckDuplicate: true,
|
||||||
Driver: driver,
|
Driver: driver,
|
||||||
IPAM: docker.IPAMOptions{
|
IPAM: &docker.IPAMOptions{
|
||||||
Driver: driver,
|
Driver: driver,
|
||||||
Config: []docker.IPAMConfig{{Subnet: subnet}},
|
Config: []docker.IPAMConfig{{Subnet: subnet}},
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type textFormatter struct {
|
type textFormatter struct {
|
||||||
|
|||||||
+5
-5
@@ -35,21 +35,21 @@ weave-kube is an image with the weave binaries and a wrapper script installed. I
|
|||||||
|
|
||||||
1. Setting up the weave network
|
1. Setting up the weave network
|
||||||
2. Connecting to peers
|
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`
|
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.conflist`
|
||||||
4. Running the weave network policy controller (weave-npc) to implement kubernetes' `NetworkPolicy`
|
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:
|
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
|
1. Set certain environment variables to configure the CNI plugin, mostly related to the container ID
|
||||||
2. Launch `/opt/cni/bin/weave-net`
|
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`
|
3. Pass the CNI config - the contents of `/etc/cni/net.d/10-weave.conflist` 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`.
|
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:
|
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.
|
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`
|
2. Set up the config file at `/etc/cni/net.d/10-weave.conflist`
|
||||||
3. Run the `weave` initialization script
|
3. Run the `weave` initialization script
|
||||||
4. Run `weaver` with the correct configuration passed as command-line options
|
4. Run `weaver` with the correct configuration passed as command-line options
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ To add new options to how weave should run with each invocation, you would do th
|
|||||||
5. Add a command-line option `--option` to `weaveutil` or `weaver` as appropriate.
|
5. Add a command-line option `--option` to `weaveutil` or `weaver` as appropriate.
|
||||||
6. If the option can be configured for CNI:
|
6. If the option can be configured for CNI:
|
||||||
* have `prog/weave-kube/launch.sh` read it as an environment variable and set inside
|
* 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`
|
* set a default for the environment variable in `prog/weave-kube/weave-daemonset-k8s-N.N.yaml`
|
||||||
* if it should be set via `weaver` globally on its one-time initialization invocation, pass it on in `launch.sh`
|
* 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
|
* 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.conflist` and then have the CNI code in `plugin/net/cni.go` in `weaveutil` read it and use where appropriate
|
||||||
7. Document it!
|
7. Document it!
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Source IP addr of packet sent to k8s Service
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
* Weave Net: v2.2.1.
|
||||||
|
* Kubernetes: v1.10.0.
|
||||||
|
* kube-proxy: iptables/ipvs mode with `--masquerade-all=false` and `--cluster-cidr` unspecified(default kubeadm options).
|
||||||
|
* Hosts: `h1` and `h2`.
|
||||||
|
|
||||||
|
## Source IP
|
||||||
|
|
||||||
|
\# | Src | Target | Dst | Src IP | `-j WEAVE-NPC`
|
||||||
|
---|-------------|-------------------|--------|------------------|--------------
|
||||||
|
1 | Pod_h1 | ip(Pod_h2) | Pod_h2 | ip(Pod_h1) | OK
|
||||||
|
2 | h1 | ClusterIP | Pod_h1 | ip(weave_h1) | **NOK**
|
||||||
|
3 | h1 | ClusterIP | Pod_h2 | ip(weave_h1) | OK
|
||||||
|
4 | Pod_h1 | ClusterIP | Pod_h1 | ip(Pod_h1) | OK
|
||||||
|
5 | Pod_h1 | ClusterIP | Pod_h2 | ip(weave_h1) | OK
|
||||||
|
6 | h1 | ip(h1):NodePort | Pod_h1 | ip(weave_h1) | **NOK**
|
||||||
|
7 | h1 | ip(h1):NodePort | Pod_h2 | ip(weave_h1) | OK
|
||||||
|
8 | h1 | ip(h2):NodePort | Pod_h1 | ip(weave_h2) | OK
|
||||||
|
9 | h1 | ip(h2):NodePort | Pod_h2 | ip(weave_h2) | ??? Can't reproduce
|
||||||
|
10 | Pod_h1 | ip(h1):NodePort | Pod_h1 | ip(weave_h1) | **NOK**
|
||||||
|
11 | Pod_h1 | ip(h1):NodePort | Pod_h2 | ip(weave_h1) | OK
|
||||||
|
12 | Pod_h1 | ip(h2):NodePort | Pod_h1 | ip(weave_h2) | OK
|
||||||
|
13 | Pod_h1 | ip(h2):NodePort | Pod_h2 | ip(weave_h2) | OK
|
||||||
|
|
||||||
|
Remarks:
|
||||||
|
|
||||||
|
* **Src IP** is of a packet which is captured on the weave bridge.
|
||||||
|
* **-j WEAVE-NPC** - whether a packet enters the `filter/WEAVE-NPC` iptables chain (OK = NetworkPolicy is enforced as required).
|
||||||
|
* **Pod_h1** - a Pod running on the `h1` host.
|
||||||
|
* **ip(weave_h1)** - IP addr of the weave bridge on the `h1` host.
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
* Up-to-date versions of `git`, `go` etc
|
* Up-to-date versions of `git`, `go` etc
|
||||||
* Install GitHub release tool `go get github.com/weaveworks/github-release`
|
* Install GitHub release tool `go get github.com/weaveworks/github-release`
|
||||||
|
* Install manifest tool `go get github.com/estesp/manifest-tool`
|
||||||
* Create a [github token for
|
* Create a [github token for
|
||||||
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
|
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
|
||||||
|
|
||||||
@@ -127,6 +128,45 @@ Finally, for **Mainline** releases only:
|
|||||||
|
|
||||||
* Images tagged `latest` are updated on DockerHub
|
* Images tagged `latest` are updated on DockerHub
|
||||||
|
|
||||||
|
Now, you can validate whether images were published for all platforms:
|
||||||
|
|
||||||
|
# Required platforms for each image:
|
||||||
|
grep '^ML_PLATFORMS=' Makefile
|
||||||
|
|
||||||
|
# Published platforms per image:
|
||||||
|
for img in $(grep '^PUBLISH=' Makefile); do
|
||||||
|
img="weaveworks/$(echo $img | cut -d_ -f2):${TAG#v}"
|
||||||
|
platforms=$(manifest-tool \
|
||||||
|
inspect --raw "$img" | \
|
||||||
|
jq '.[].Platform | .os + "/" +.architecture' | \
|
||||||
|
tr '\n' ' ')
|
||||||
|
echo "$img: $platforms"
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
### Docker Store
|
||||||
|
|
||||||
|
Weave Net is [available in Docker Store](https://store.docker.com/plugins/weave-net-plugin) and should also be released there.
|
||||||
|
|
||||||
|
* Go to https://store.docker.com/ and log in.
|
||||||
|
* Go to "[_Publish_](https://store.docker.com/publisher/center)".
|
||||||
|
* Under "_My Products_", select "_owners (weaveworks)_". Weave Net should be listed among our products.
|
||||||
|
* Click "_Actions_" > "_Edit Product_".
|
||||||
|
* Go to "_Plans & Pricing_" > "_Free Tier_".
|
||||||
|
* Under "_Source Repositories & Tags_", click on "_Add Source_", select "_net-plugin_" under "_Repository_" and the version to release under "_Tag_".
|
||||||
|
* Under "_Resources_" > "_Installation Instructions_", update the lines containing `export NET_VERSION=<version>`.
|
||||||
|
* Click "_Save_"
|
||||||
|
* Click "_Submit For Review_". You should see a message like: "_Your product has been submitted for approval. [...] We'll be in touch with next steps soon!_".
|
||||||
|
* You should receive an email saying:
|
||||||
|
|
||||||
|
> This email confirms that we received your submission on \<date\> of Weave Net to the Docker Store.
|
||||||
|
>
|
||||||
|
> We're reviewing your submission to ensure that it meets our [security guidelines](https://success.docker.com/Store#Security_and_Audit_Policies) and complies with our [best practices](https://success.docker.com/Store#Create_Great_Content). Don't worry! We'll let you know if there's anything you need to change before we publish your submission. You should hear back from us within the next 14 days.
|
||||||
|
>
|
||||||
|
> Thanks for submitting your content to the Docker Store!
|
||||||
|
|
||||||
|
* Hope Docker eventually performs the release or contacts us. If nothing happens within 14 days, contact their support team: publisher-support@docker.com.
|
||||||
|
|
||||||
### Finish up
|
### Finish up
|
||||||
|
|
||||||
* If not on master, merge branch into master and push to GitHub.
|
* If not on master, merge branch into master and push to GitHub.
|
||||||
@@ -139,6 +179,8 @@ Finally, for **Mainline** releases only:
|
|||||||
https://checkpoint-api.weave.works/admin
|
https://checkpoint-api.weave.works/admin
|
||||||
* File a PR to update the version of the daemonset at
|
* File a PR to update the version of the daemonset at
|
||||||
https://github.com/kubernetes/kops/tree/master/upup/models/cloudup/resources/addons/networking.weave
|
https://github.com/kubernetes/kops/tree/master/upup/models/cloudup/resources/addons/networking.weave
|
||||||
|
and kops/upup/pkg/fi/cloudup/bootstrapchannelbuilder.go
|
||||||
|
and kops/upup/pkg/fi/cloudup/tests/bootstrapchannelbuilder/weave/manifest.yaml
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
|||||||
+62
-12
@@ -9,7 +9,9 @@ destroyed automatically in response to Pod, Namespace and
|
|||||||
NetworkPolicy object updates from the k8s API server:
|
NetworkPolicy object updates from the k8s API server:
|
||||||
|
|
||||||
* A `hash:ip` set per namespace, containing the IP addresses of all
|
* A `hash:ip` set per namespace, containing the IP addresses of all
|
||||||
pods in that namespace
|
pods in that namespace for which default ingress is allowed
|
||||||
|
* A `hash:ip` set per namespace, containing the IP addresses of all
|
||||||
|
pods in that namespace for which default egress is allowed
|
||||||
* A `list:set` per distinct (across all network policies in all
|
* A `list:set` per distinct (across all network policies in all
|
||||||
namespaces) namespace selector mentioned in a network policy,
|
namespaces) namespace selector mentioned in a network policy,
|
||||||
containing the names of any of the above hash:ip sets whose
|
containing the names of any of the above hash:ip sets whose
|
||||||
@@ -18,6 +20,9 @@ NetworkPolicy object updates from the k8s API server:
|
|||||||
containing network policy's namespace) pod selector mentioned in a
|
containing network policy's namespace) pod selector mentioned in a
|
||||||
network policy, containing the IP addresses of all pods in the
|
network policy, containing the IP addresses of all pods in the
|
||||||
namespace whose labels match that selector
|
namespace whose labels match that selector
|
||||||
|
* A `hash:net` set for each distinct (within the scope of the
|
||||||
|
containing network policy's namespace) `except` list of CIDR's mentioned in
|
||||||
|
the network policies
|
||||||
|
|
||||||
IPsets are implemented by the kernel module `xt_set`, without which
|
IPsets are implemented by the kernel module `xt_set`, without which
|
||||||
weave-npc will not work.
|
weave-npc will not work.
|
||||||
@@ -38,11 +43,21 @@ hashing to avoid clashes.
|
|||||||
|
|
||||||
# iptables chains
|
# iptables chains
|
||||||
|
|
||||||
The policy controller maintains two iptables chains in response to
|
The policy controller maintains several iptables chains in response to
|
||||||
changes to pods, namespaces and network policies. One chain contains
|
changes to pods, namespaces and network policies.
|
||||||
the ingress rules that implement the network policy specifications,
|
|
||||||
and the other is used to bypass the ingress rules for namespaces which
|
## Static `WEAVE-NPC` chain
|
||||||
have an ingress isolation policy of `DefaultAllow`.
|
|
||||||
|
`WEAVE-NPC` chain contains static rules to ACCEPT traffic that is `RELATED,ESTABLISHED`
|
||||||
|
and run `NEW` traffic through `WEAVE-NPC-DEFAULT` followed by `WEAVE-NPC-INGRESS` chains
|
||||||
|
|
||||||
|
Static configuration:
|
||||||
|
|
||||||
|
```
|
||||||
|
iptables -A WEAVE-NPC -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||||
|
iptables -A WEAVE-NPC -m state --state NEW -j WEAVE-NPC-DEFAULT
|
||||||
|
iptables -A WEAVE-NPC -m state --state NEW -j WEAVE-NPC-INGRESS
|
||||||
|
```
|
||||||
|
|
||||||
## Dynamically maintained `WEAVE-NPC-DEFAULT` chain
|
## Dynamically maintained `WEAVE-NPC-DEFAULT` chain
|
||||||
|
|
||||||
@@ -63,23 +78,58 @@ For each namespace network policy ingress rule peer/port combination:
|
|||||||
iptables -A WEAVE-NPC-INGRESS -p $PROTO [-m set --match-set $SRCSET] -m set --match-set $DSTSET --dport $DPORT -j ACCEPT
|
iptables -A WEAVE-NPC-INGRESS -p $PROTO [-m set --match-set $SRCSET] -m set --match-set $DSTSET --dport $DPORT -j ACCEPT
|
||||||
```
|
```
|
||||||
|
|
||||||
## Static `WEAVE-NPC` chain
|
## Static `WEAVE-NPC-EGRESS` chain
|
||||||
|
|
||||||
|
`WEAVE-NPC-EGRESS` chain contains static rules to ACCEPT traffic that is `RELATED,ESTABLISHED`
|
||||||
|
and run `NEW` traffic through `WEAVE-NPC-EGRESS-DEFAULT` followed by `WEAVE-NPC-EGRESS-CUSTOM` chains
|
||||||
|
|
||||||
Static configuration:
|
Static configuration:
|
||||||
|
|
||||||
```
|
```
|
||||||
iptables -A WEAVE-NPC -m state --state RELATED,ESTABLISHED -j ACCEPT
|
iptables -A WEAVE-NPC-EGRESS -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||||
iptables -A WEAVE-NPC -m state --state NEW -j WEAVE-NPC-DEFAULT
|
iptables -A WEAVE-NPC-EGRESS -m state --state NEW -j WEAVE-NPC-EGRESS-DEFAULT
|
||||||
iptables -A WEAVE-NPC -m state --state NEW -j WEAVE-NPC-INGRESS
|
iptables -A WEAVE-NPC-EGRESS -m state --state NEW -m mark ! --mark 0x40000/0x40000 -j WEAVE-NPC-EGRESS-CUSTOM
|
||||||
|
iptables -A WEAVE-NPC-EGRESS -m mark ! --mark 0x40000/0x40000 -j DROP
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Dynamically maintained `WEAVE-NPC-EGRESS-DEFAULT` chain
|
||||||
|
|
||||||
|
The policy controller maintains a rule in this chain for every
|
||||||
|
namespace whose egress isolation policy is `DefaultAllow`. The
|
||||||
|
purpose of this rule is simply to ACCEPT any traffic originating from such namespace before it reaches the egress chain.
|
||||||
|
|
||||||
|
```
|
||||||
|
iptables -A WEAVE-NPC-EGRESS-DEFAULT -m set --match-set $NSIPSET src -j WEAVE-NPC-EGRESS-ACCEPT
|
||||||
|
iptables -A WEAVE-NPC-EGRESS-DEFAULT -m set --match-set $NSIPSET src -j RETURN
|
||||||
|
```
|
||||||
|
|
||||||
|
## Static `WEAVE-NPC-EGRESS-ACCEPT` chain
|
||||||
|
|
||||||
|
`WEAVE-NPC-EGRESS-ACCEPTS` chain contains static rules to mark traffic
|
||||||
|
|
||||||
|
Static configuration:
|
||||||
|
|
||||||
|
```
|
||||||
|
iptables -A WEAVE-NPC-EGRESS-ACCEPT -j MARK --set-xmark 0x40000/0x40000
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dynamically maintained `WEAVE-NPC-EGRESS-CUSTOM` chain
|
||||||
|
|
||||||
|
For each namespace network policy egress rule peer/port combination:
|
||||||
|
|
||||||
|
```
|
||||||
|
iptables -A WEAVE-NPC-EGRESS-CUSTOM -p $PROTO [-m set --match-set $SRCSET] -m set --match-set $DSTSET --dport $DPORT -j ACCEPT
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
# Steering traffic into the policy engine
|
# Steering traffic into the policy engine
|
||||||
|
|
||||||
To direct traffic into the policy engine:
|
To direct traffic into the policy engine:
|
||||||
|
|
||||||
```
|
```
|
||||||
iptables -A FORWARD -o weave -m physdev ! --physdev-out vethwe-bridge -j WEAVE-NPC
|
iptables -A INPUT -i weave -j WEAVE-NPC-EGRESS
|
||||||
iptables -A FORWARD -o weave -m physdev ! --physdev-out vethwe-bridge -j DROP
|
iptables -A FORWARD -i weave -j WEAVE-NPC-EGRESS
|
||||||
|
iptables -A FORWARD -o weave -j WEAVE-NPC
|
||||||
```
|
```
|
||||||
|
|
||||||
Note this only affects traffic which egresses the bridge on a physical
|
Note this only affects traffic which egresses the bridge on a physical
|
||||||
|
|||||||
+16
-3
@@ -74,6 +74,7 @@ type Allocator struct {
|
|||||||
isKnownPeer func(mesh.PeerName) bool
|
isKnownPeer func(mesh.PeerName) bool
|
||||||
quorum func() uint
|
quorum func() uint
|
||||||
now func() time.Time
|
now func() time.Time
|
||||||
|
tracker tracker.LocalRangeTracker
|
||||||
}
|
}
|
||||||
|
|
||||||
// PreClaims are IP addresses discovered before we could initialize IPAM
|
// PreClaims are IP addresses discovered before we could initialize IPAM
|
||||||
@@ -130,6 +131,7 @@ func NewAllocator(config Config) *Allocator {
|
|||||||
quorum: config.Quorum,
|
quorum: config.Quorum,
|
||||||
dead: make(map[string]time.Time),
|
dead: make(map[string]time.Time),
|
||||||
now: time.Now,
|
now: time.Now,
|
||||||
|
tracker: config.Tracker,
|
||||||
}
|
}
|
||||||
|
|
||||||
alloc.pendingClaims = make([]operation, len(config.PreClaims))
|
alloc.pendingClaims = make([]operation, len(config.PreClaims))
|
||||||
@@ -273,7 +275,7 @@ func (alloc *Allocator) spaceRequestDenied(sender mesh.PeerName, r address.Range
|
|||||||
for i := 0; i < len(alloc.pendingClaims); {
|
for i := 0; i < len(alloc.pendingClaims); {
|
||||||
claim := alloc.pendingClaims[i].(*claim)
|
claim := alloc.pendingClaims[i].(*claim)
|
||||||
if r.Contains(claim.cidr.Addr) {
|
if r.Contains(claim.cidr.Addr) {
|
||||||
claim.deniedBy(alloc, sender)
|
claim.deniedBy(alloc, sender, nil)
|
||||||
alloc.pendingClaims = append(alloc.pendingClaims[:i], alloc.pendingClaims[i+1:]...)
|
alloc.pendingClaims = append(alloc.pendingClaims[:i], alloc.pendingClaims[i+1:]...)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -812,6 +814,16 @@ func (alloc *Allocator) sendRingUpdate(dest mesh.PeerName) {
|
|||||||
alloc.gossip.GossipUnicast(dest, msg)
|
alloc.gossip.GossipUnicast(dest, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (alloc *Allocator) hasAllocations(rs []address.Range) bool {
|
||||||
|
for _, r := range rs {
|
||||||
|
alloc.debugf("hasAllocations: %v %v vs %v", r, alloc.space.NumFreeAddressesInRange(r), r.Size())
|
||||||
|
if alloc.space.NumFreeAddressesInRange(r) != r.Size() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (alloc *Allocator) update(sender mesh.PeerName, msg []byte) error {
|
func (alloc *Allocator) update(sender mesh.PeerName, msg []byte) error {
|
||||||
reader := bytes.NewReader(msg)
|
reader := bytes.NewReader(msg)
|
||||||
decoder := gob.NewDecoder(reader)
|
decoder := gob.NewDecoder(reader)
|
||||||
@@ -830,7 +842,7 @@ func (alloc *Allocator) update(sender mesh.PeerName, msg []byte) error {
|
|||||||
// If someone sent us a ring, merge it into ours. Note this will move us
|
// If someone sent us a ring, merge it into ours. Note this will move us
|
||||||
// out of the awaiting-consensus state if we didn't have a ring already.
|
// out of the awaiting-consensus state if we didn't have a ring already.
|
||||||
case data.Ring != nil:
|
case data.Ring != nil:
|
||||||
updated, err := alloc.ring.Merge(*data.Ring)
|
updated, err := alloc.ring.Merge(*data.Ring, alloc.hasAllocations)
|
||||||
switch err {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
if updated {
|
if updated {
|
||||||
@@ -908,6 +920,7 @@ func (alloc *Allocator) donateSpace(r address.Range, to mesh.PeerName) {
|
|||||||
alloc.debugln("Giving range", chunk, "to", to)
|
alloc.debugln("Giving range", chunk, "to", to)
|
||||||
alloc.ring.GrantRangeToHost(chunk.Start, chunk.End, to)
|
alloc.ring.GrantRangeToHost(chunk.Start, chunk.End, to)
|
||||||
alloc.persistRing()
|
alloc.persistRing()
|
||||||
|
alloc.gossip.GossipBroadcast(alloc.Gossip())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (alloc *Allocator) assertInvariants() {
|
func (alloc *Allocator) assertInvariants() {
|
||||||
@@ -983,7 +996,7 @@ func (alloc *Allocator) loadPersistedData() bool {
|
|||||||
alloc.persistOwned()
|
alloc.persistOwned()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !nameFound || !ringFound {
|
if !nameFound || !ringFound || persistedRing == nil || persistedRing.Empty() {
|
||||||
overwritePersisted("No valid persisted data")
|
overwritePersisted("No valid persisted data")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-4
@@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
type claim struct {
|
type claim struct {
|
||||||
resultChan chan<- error
|
resultChan chan<- error
|
||||||
|
tryCount int
|
||||||
ident string // a container ID, something like "weave:expose", or api.NoContainerID
|
ident string // a container ID, something like "weave:expose", or api.NoContainerID
|
||||||
cidr address.CIDR // single address being claimed
|
cidr address.CIDR // single address being claimed
|
||||||
isContainer bool // true if ident is a container ID
|
isContainer bool // true if ident is a container ID
|
||||||
@@ -19,6 +20,8 @@ type claim struct {
|
|||||||
hasBeenCancelled func() bool
|
hasBeenCancelled func() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxTryCount = 5
|
||||||
|
|
||||||
// Send an error (or nil for success) back to caller listening on resultChan
|
// Send an error (or nil for success) back to caller listening on resultChan
|
||||||
func (c *claim) sendResult(result error) {
|
func (c *claim) sendResult(result error) {
|
||||||
// Make sure we only send a result once, since listener stops listening after that
|
// Make sure we only send a result once, since listener stops listening after that
|
||||||
@@ -40,6 +43,8 @@ func (c *claim) Try(alloc *Allocator) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.tryCount++
|
||||||
|
|
||||||
addOwned := func() {
|
addOwned := func() {
|
||||||
if c.ident == api.NoContainerID {
|
if c.ident == api.NoContainerID {
|
||||||
alloc.addOwned(c.cidr.Addr.String(), c.cidr, c.isContainer)
|
alloc.addOwned(c.cidr.Addr.String(), c.cidr, c.isContainer)
|
||||||
@@ -90,8 +95,10 @@ func (c *claim) Try(alloc *Allocator) bool {
|
|||||||
if c.noErrorOnUnknown {
|
if c.noErrorOnUnknown {
|
||||||
alloc.infof("Claim %s for %s: %s; will try later.", c.cidr, c.ident, err)
|
alloc.infof("Claim %s for %s: %s; will try later.", c.cidr, c.ident, err)
|
||||||
c.sendResult(nil)
|
c.sendResult(nil)
|
||||||
} else { // just tell the user they can't do this.
|
} else if c.tryCount > maxTryCount {
|
||||||
c.deniedBy(alloc, owner)
|
// give up, tell the user they can't do this
|
||||||
|
c.deniedBy(alloc, owner, err)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -128,12 +135,16 @@ func (c *claim) Try(alloc *Allocator) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *claim) deniedBy(alloc *Allocator, owner mesh.PeerName) {
|
func (c *claim) deniedBy(alloc *Allocator, owner mesh.PeerName, err error) {
|
||||||
name, found := alloc.nicknames[owner]
|
name, found := alloc.nicknames[owner]
|
||||||
if found {
|
if found {
|
||||||
name = " (" + name + ")"
|
name = " (" + name + ")"
|
||||||
}
|
}
|
||||||
c.sendResult(fmt.Errorf("address %s is owned by other peer %s%s", c.cidr.String(), owner, name))
|
reason := ""
|
||||||
|
if err != nil {
|
||||||
|
reason = fmt.Sprintf(" - %s", err)
|
||||||
|
}
|
||||||
|
c.sendResult(fmt.Errorf("address %s is owned by other peer %s%s%s", c.cidr.String(), owner, name, reason))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *claim) Cancel() {
|
func (c *claim) Cancel() {
|
||||||
|
|||||||
+5
-1
@@ -88,7 +88,7 @@ func (alloc *Allocator) handleHTTPClaim(dockerCli *docker.Client, w http.Respons
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HandleHTTP wires up ipams HTTP endpoints to the provided mux.
|
// HandleHTTP wires up ipams HTTP endpoints to the provided mux.
|
||||||
func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CIDR, tracker string, dockerCli *docker.Client) {
|
func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CIDR, dockerCli *docker.Client) {
|
||||||
router.Methods("GET").Path("/ipinfo/defaultsubnet").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
router.Methods("GET").Path("/ipinfo/defaultsubnet").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Fprintf(w, "%s", defaultSubnet)
|
fmt.Fprintf(w, "%s", defaultSubnet)
|
||||||
})
|
})
|
||||||
@@ -199,6 +199,10 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CID
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.Methods("GET").Path("/ipinfo/tracker").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
router.Methods("GET").Path("/ipinfo/tracker").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tracker := ""
|
||||||
|
if alloc.tracker != nil {
|
||||||
|
tracker = alloc.tracker.String()
|
||||||
|
}
|
||||||
fmt.Fprintf(w, tracker)
|
fmt.Fprintf(w, tracker)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
+2
-2
@@ -43,7 +43,7 @@ func listenHTTP(alloc *Allocator, subnet address.CIDR) int {
|
|||||||
router.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
|
router.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
|
||||||
io.WriteString(w, fmt.Sprintln(alloc))
|
io.WriteString(w, fmt.Sprintln(alloc))
|
||||||
})
|
})
|
||||||
alloc.HandleHTTP(router, subnet, "", nil)
|
alloc.HandleHTTP(router, subnet, nil)
|
||||||
|
|
||||||
httpListener, err := net.Listen("tcp", ":0")
|
httpListener, err := net.Listen("tcp", ":0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -132,7 +132,7 @@ func TestBadHttp(t *testing.T) {
|
|||||||
// Verb that's not handled
|
// Verb that's not handled
|
||||||
resp, err := doHTTP("HEAD", fmt.Sprintf("http://localhost:%d/ip/%s/%s", port, containerID, testAddr1))
|
resp, err := doHTTP("HEAD", fmt.Sprintf("http://localhost:%d/ip/%s/%s", port, containerID, testAddr1))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, http.StatusNotFound, resp.StatusCode, "http response")
|
require.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode, "http response")
|
||||||
// Mis-spelled URL
|
// Mis-spelled URL
|
||||||
resp, err = doHTTP("POST", fmt.Sprintf("http://localhost:%d/xip/%s/", port, containerID))
|
resp, err = doHTTP("POST", fmt.Sprintf("http://localhost:%d/xip/%s/", port, containerID))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ring
|
package ring
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/weaveworks/mesh"
|
"github.com/weaveworks/mesh"
|
||||||
@@ -25,6 +26,7 @@ func (e *entry) Equal(e2 *entry) bool {
|
|||||||
func (e *entry) update(peername mesh.PeerName, free address.Count) {
|
func (e *entry) update(peername mesh.PeerName, free address.Count) {
|
||||||
e.Peer = peername
|
e.Peer = peername
|
||||||
e.Version++
|
e.Version++
|
||||||
|
common.Log.Debugln(fmt.Sprintf("Updating ring token=%s current peer=%s updated peer=%s new version=%s", e.Token, e.Peer, peername, fmt.Sprint(e.Version)))
|
||||||
e.Free = free
|
e.Free = free
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+65
-18
@@ -213,7 +213,7 @@ func (r *Ring) GrantRangeToHost(start, end address.Address, peer mesh.PeerName)
|
|||||||
|
|
||||||
// Merge the given ring into this ring and indicate whether this ring
|
// Merge the given ring into this ring and indicate whether this ring
|
||||||
// got updated as a result.
|
// got updated as a result.
|
||||||
func (r *Ring) Merge(gossip Ring) (bool, error) {
|
func (r *Ring) Merge(gossip Ring, hasAllocations func(r []address.Range) bool) (bool, error) {
|
||||||
r.assertInvariants()
|
r.assertInvariants()
|
||||||
defer r.trackUpdates()()
|
defer r.trackUpdates()()
|
||||||
|
|
||||||
@@ -238,12 +238,22 @@ func (r *Ring) Merge(gossip Ring) (bool, error) {
|
|||||||
return false, ErrDifferentRange
|
return false, ErrDifferentRange
|
||||||
}
|
}
|
||||||
|
|
||||||
result, updated, err := r.Entries.merge(gossip.Entries, r.Peer)
|
result, updated, err := r.Entries.merge(gossip.Entries, r.Peer, r, hasAllocations)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset the free space for entries if there is invalid free space
|
||||||
|
// due to accepting an unexpected update from the peers
|
||||||
|
for i := 0; i < len(result); i++ {
|
||||||
|
distance := r.distance(result.entry(i).Token, result.entry(i+1).Token)
|
||||||
|
if result.entry(i).Peer == r.Peer && result.entry(i).Free > distance {
|
||||||
|
// case that can arise when a range that we own that got split and had no allocations
|
||||||
|
result.entry(i).Free = distance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := r.checkEntries(result); err != nil {
|
if err := r.checkEntries(result); err != nil {
|
||||||
return false, fmt.Errorf("Merge of incoming data causes: %s", err)
|
return false, fmt.Errorf("Merge of incoming data causes: %s", err)
|
||||||
}
|
}
|
||||||
@@ -260,7 +270,7 @@ func (r *Ring) Merge(gossip Ring) (bool, error) {
|
|||||||
// entries belonging to ourPeer. Returns the merged entries and an
|
// entries belonging to ourPeer. Returns the merged entries and an
|
||||||
// indication whether the merge resulted in any changes, i.e. the
|
// indication whether the merge resulted in any changes, i.e. the
|
||||||
// result differs from the original.
|
// result differs from the original.
|
||||||
func (es entries) merge(other entries, ourPeer mesh.PeerName) (result entries, updated bool, err error) {
|
func (es entries) merge(other entries, ourPeer mesh.PeerName, r *Ring, hasAllocations func(r []address.Range) bool) (result entries, updated bool, err error) {
|
||||||
var mine, theirs *entry
|
var mine, theirs *entry
|
||||||
var previousOwner *mesh.PeerName
|
var previousOwner *mesh.PeerName
|
||||||
addToResult := func(e entry) { result = append(result, &e) }
|
addToResult := func(e entry) { result = append(result, &e) }
|
||||||
@@ -274,6 +284,7 @@ func (es entries) merge(other entries, ourPeer mesh.PeerName) (result entries, u
|
|||||||
var i, j int
|
var i, j int
|
||||||
for i < len(es) && j < len(other) {
|
for i < len(es) && j < len(other) {
|
||||||
mine, theirs = es[i], other[j]
|
mine, theirs = es[i], other[j]
|
||||||
|
common.Log.Debugf("[ring %s]: Merge mine.Token=%s theirs.Token=%s mine.Peer=%s theirs.Peer=%s mine.Version=%v theirs.Version=%v", ourPeer, mine.Token, theirs.Token, mine.Peer, theirs.Peer, mine.Version, theirs.Version)
|
||||||
switch {
|
switch {
|
||||||
case mine.Token < theirs.Token:
|
case mine.Token < theirs.Token:
|
||||||
addToResult(*mine)
|
addToResult(*mine)
|
||||||
@@ -282,8 +293,11 @@ func (es entries) merge(other entries, ourPeer mesh.PeerName) (result entries, u
|
|||||||
case mine.Token > theirs.Token:
|
case mine.Token > theirs.Token:
|
||||||
// insert, checking that a range owned by us hasn't been split
|
// insert, checking that a range owned by us hasn't been split
|
||||||
if previousOwner != nil && *previousOwner == ourPeer && theirs.Peer != ourPeer {
|
if previousOwner != nil && *previousOwner == ourPeer && theirs.Peer != ourPeer {
|
||||||
err = errEntryInMyRange(theirs)
|
// check we have no allocations in the range that got split
|
||||||
return
|
if hasAllocations(r.makeRanges(theirs.Token, mine.Token)) {
|
||||||
|
err = errEntryInMyRange(theirs)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
addTheirs(*theirs)
|
addTheirs(*theirs)
|
||||||
j++
|
j++
|
||||||
@@ -292,17 +306,44 @@ func (es entries) merge(other entries, ourPeer mesh.PeerName) (result entries, u
|
|||||||
switch {
|
switch {
|
||||||
case mine.Version >= theirs.Version:
|
case mine.Version >= theirs.Version:
|
||||||
if mine.Version == theirs.Version && !mine.Equal(theirs) {
|
if mine.Version == theirs.Version && !mine.Equal(theirs) {
|
||||||
err = errInconsistentEntry(mine, theirs)
|
if mine.Peer == ourPeer {
|
||||||
return
|
// if we own the entry and has allocations
|
||||||
|
if hasAllocations(r.makeRanges(theirs.Token, es.entry(i+1).Token)) {
|
||||||
|
err = errInconsistentEntry(mine, theirs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tie-break here, pick the entry with the highest free count
|
||||||
|
if mine.Free >= theirs.Free {
|
||||||
|
addToResult(*mine)
|
||||||
|
previousOwner = &mine.Peer
|
||||||
|
} else {
|
||||||
|
addTheirs(*theirs)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addToResult(*mine)
|
||||||
|
previousOwner = &mine.Peer
|
||||||
}
|
}
|
||||||
addToResult(*mine)
|
|
||||||
previousOwner = &mine.Peer
|
|
||||||
case mine.Version < theirs.Version:
|
case mine.Version < theirs.Version:
|
||||||
if mine.Peer == ourPeer { // We shouldn't receive updates to our own tokens
|
if mine.Peer == ourPeer {
|
||||||
err = errNewerVersion(mine, theirs)
|
// We received update to our own tokens accept the received entry
|
||||||
return
|
// if either it belongs to a different peer and we do not have allocations
|
||||||
|
// in the range effectively given away, or it belongs to our own peer, in
|
||||||
|
// which case we should set our version to the one received plus one,
|
||||||
|
// effectively imposing our existing entry.
|
||||||
|
if theirs.Peer != ourPeer && !hasAllocations(r.makeRanges(mine.Token, es.entry(i+1).Token)) {
|
||||||
|
common.Log.Infof("ring: repair inconsistent data by accepting token %v from %v - no allocations up to %v", theirs.Token, theirs.Peer, es.entry(i+1).Token)
|
||||||
|
addTheirs(*theirs)
|
||||||
|
} else if theirs.Peer == ourPeer {
|
||||||
|
mine.Version = theirs.Version + 1
|
||||||
|
addToResult(*mine)
|
||||||
|
} else {
|
||||||
|
err = errNewerVersion(mine, theirs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addTheirs(*theirs)
|
||||||
}
|
}
|
||||||
addTheirs(*theirs)
|
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
j++
|
j++
|
||||||
@@ -334,6 +375,11 @@ func (r *Ring) Empty() bool {
|
|||||||
return len(r.Entries) == 0
|
return len(r.Entries) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convenience function to normalise a single range that may span zero
|
||||||
|
func (r *Ring) makeRanges(start, end address.Address) []address.Range {
|
||||||
|
return r.splitRangesOverZero([]address.Range{{Start: start, End: end}})
|
||||||
|
}
|
||||||
|
|
||||||
// Given a slice of ranges which are all in the right order except
|
// Given a slice of ranges which are all in the right order except
|
||||||
// possibly the last one spans zero, fix that up and return the slice
|
// possibly the last one spans zero, fix that up and return the slice
|
||||||
func (r *Ring) splitRangesOverZero(ranges []address.Range) []address.Range {
|
func (r *Ring) splitRangesOverZero(ranges []address.Range) []address.Range {
|
||||||
@@ -384,10 +430,7 @@ type RangeInfo struct {
|
|||||||
|
|
||||||
func (r *Ring) AllRangeInfo() (result []RangeInfo) {
|
func (r *Ring) AllRangeInfo() (result []RangeInfo) {
|
||||||
for i, entry := range r.Entries {
|
for i, entry := range r.Entries {
|
||||||
nextEntry := r.Entries.entry(i + 1)
|
for _, r := range r.makeRanges(entry.Token, r.Entries.entry(i+1).Token) {
|
||||||
ranges := []address.Range{{Start: entry.Token, End: nextEntry.Token}}
|
|
||||||
ranges = r.splitRangesOverZero(ranges)
|
|
||||||
for _, r := range ranges {
|
|
||||||
result = append(result, RangeInfo{entry.Peer, r, entry.Version})
|
result = append(result, RangeInfo{entry.Peer, r, entry.Version})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -485,6 +528,7 @@ func (r *Ring) ReportFree(freespace map[address.Address]address.Count) (updated
|
|||||||
|
|
||||||
entries[i].Free = free
|
entries[i].Free = free
|
||||||
entries[i].Version++
|
entries[i].Version++
|
||||||
|
common.Log.Debugf("[ring %s]: ReportFree token=%s peer=%s version=%v", r.Peer, entries[i].Token, entries[i].Peer, entries[i].Version)
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -565,7 +609,10 @@ func (r *Ring) Transfer(from, to mesh.PeerName) []address.Range {
|
|||||||
for i, entry := range r.Entries {
|
for i, entry := range r.Entries {
|
||||||
if entry.Peer == from {
|
if entry.Peer == from {
|
||||||
entry.Peer = to
|
entry.Peer = to
|
||||||
entry.Version++
|
// bump version by large value so that transfer of range takes precedence (if there is any conflict in merging entry)
|
||||||
|
// over other opertaions on the entry that increments the version
|
||||||
|
entry.Version = entry.Version + 100
|
||||||
|
common.Log.Debugf("[ring %s]: Transfer token=%s from=%s to=%s version=%v", r.Peer, entry.Token, from, to, entry.Version)
|
||||||
newRanges = append(newRanges, address.Range{Start: entry.Token, End: r.Entries.entry(i + 1).Token})
|
newRanges = append(newRanges, address.Range{Start: entry.Token, End: r.Entries.entry(i + 1).Token})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+76
-8
@@ -32,7 +32,27 @@ func ParseIP(s string) address.Address {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func merge(r1, r2 *Ring) error {
|
func merge(r1, r2 *Ring) error {
|
||||||
_, err := r1.Merge(*r2)
|
distance := func(start, end address.Address) address.Count {
|
||||||
|
if end > start {
|
||||||
|
return address.Count(end - start)
|
||||||
|
}
|
||||||
|
return address.Count((r1.End - start) + (end - r1.Start))
|
||||||
|
}
|
||||||
|
check := func(rs []address.Range) bool {
|
||||||
|
for _, r := range rs {
|
||||||
|
size := distance(r.Start, r.End)
|
||||||
|
entry, found := r1.Entries.get(r.Start)
|
||||||
|
if !found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if entry.Free < size {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := r1.Merge(*r2, check)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,6 +239,61 @@ func TestMergeSimple(t *testing.T) {
|
|||||||
require.Equal(t, ring2.Entries, ring1.Entries)
|
require.Equal(t, ring2.Entries, ring1.Entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMergeWithConflicts(t *testing.T) {
|
||||||
|
|
||||||
|
// received update to entry with a token and version identical to an entry we have
|
||||||
|
// but but holding different content, if there are no allocations, resolve the
|
||||||
|
// conflict by picking entry one with high free count
|
||||||
|
ring1 := NewRing(start, end, peer1name)
|
||||||
|
ring2 := NewRing(start, end, peer2name)
|
||||||
|
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 256, Version: 1}}
|
||||||
|
ring2.Entries = []*entry{{Token: start, Peer: peer2name, Free: 256, Version: 1}}
|
||||||
|
require.NoError(t, merge(ring1, ring2))
|
||||||
|
|
||||||
|
// received update to entry with a token and version identical to an entry we have
|
||||||
|
// but but holding different content, if there are allocations, then reject the received
|
||||||
|
// entry
|
||||||
|
ring1 = NewRing(start, end, peer1name)
|
||||||
|
ring2 = NewRing(start, end, peer2name)
|
||||||
|
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 128, Version: 1}}
|
||||||
|
ring2.Entries = []*entry{{Token: start, Peer: peer2name, Free: 256, Version: 1}}
|
||||||
|
require.Error(t, merge(ring1, ring2), "Expected error")
|
||||||
|
|
||||||
|
// received an entry with update to one of our own tokens and with new version,
|
||||||
|
// accept the received entry if its still going to belong to us
|
||||||
|
ring1 = NewRing(start, end, peer1name)
|
||||||
|
ring2 = NewRing(start, end, peer2name)
|
||||||
|
ring1.Entries = []*entry{{Token: start, Peer: peer1name}}
|
||||||
|
ring2.Entries = []*entry{{Token: start, Peer: peer1name, Version: 1}}
|
||||||
|
require.NoError(t, merge(ring1, ring2))
|
||||||
|
|
||||||
|
// received an entry with update to one of our own tokens and with new version,
|
||||||
|
// but belongs to a different peer, accept the received entry if we do not
|
||||||
|
// have allocations in the range
|
||||||
|
ring1 = NewRing(start, end, peer1name)
|
||||||
|
ring2 = NewRing(start, end, peer2name)
|
||||||
|
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 256}}
|
||||||
|
ring2.Entries = []*entry{{Token: start, Peer: peer2name, Free: 256, Version: 1}}
|
||||||
|
require.NoError(t, merge(ring1, ring2))
|
||||||
|
|
||||||
|
// received an entry with update to one of our own tokens and with new version,
|
||||||
|
// reject received entry if we have allocations in the range
|
||||||
|
ring1 = NewRing(start, end, peer1name)
|
||||||
|
ring2 = NewRing(start, end, peer2name)
|
||||||
|
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 128}}
|
||||||
|
ring2.Entries = []*entry{{Token: start, Peer: peer2name, Version: 1}}
|
||||||
|
require.Error(t, merge(ring1, ring2), "Expected error")
|
||||||
|
|
||||||
|
// we receive an entry that splits one of our ranges, giving some of it
|
||||||
|
// away to another peer. accept the entry provided that we have no allocations
|
||||||
|
// in the range that got given away.
|
||||||
|
ring1 = NewRing(start, end, peer1name)
|
||||||
|
ring2 = NewRing(start, end, peer2name)
|
||||||
|
ring1.Entries = []*entry{{Token: start, Peer: peer1name, Free: 256, Version: 1}}
|
||||||
|
ring2.Entries = []*entry{{Token: start, Peer: peer1name, Free: 128, Version: 2}, {Token: middle, Peer: peer2name, Free: 128}}
|
||||||
|
require.NoError(t, merge(ring1, ring2))
|
||||||
|
}
|
||||||
|
|
||||||
func TestMergeErrors(t *testing.T) {
|
func TestMergeErrors(t *testing.T) {
|
||||||
// Cannot Merge in an invalid ring
|
// Cannot Merge in an invalid ring
|
||||||
ring1 := NewRing(start, end, peer1name)
|
ring1 := NewRing(start, end, peer1name)
|
||||||
@@ -231,13 +306,6 @@ func TestMergeErrors(t *testing.T) {
|
|||||||
ring2.Entries = []*entry{}
|
ring2.Entries = []*entry{}
|
||||||
require.True(t, merge(ring1, ring2) == ErrDifferentRange, "Expected ErrDifferentRange")
|
require.True(t, merge(ring1, ring2) == ErrDifferentRange, "Expected ErrDifferentRange")
|
||||||
|
|
||||||
// Cannot Merge newer version of entry I own
|
|
||||||
ring2 = NewRing(start, end, peer2name)
|
|
||||||
ring1.Entries = []*entry{{Token: start, Peer: peer1name}}
|
|
||||||
ring2.Entries = []*entry{{Token: start, Peer: peer1name, Version: 1}}
|
|
||||||
fmt.Println(merge(ring1, ring2))
|
|
||||||
require.Error(t, merge(ring1, ring2), "Expected error")
|
|
||||||
|
|
||||||
// Cannot Merge two entries with same version but different hosts
|
// Cannot Merge two entries with same version but different hosts
|
||||||
ring1.Entries = []*entry{{Token: start, Peer: peer1name}}
|
ring1.Entries = []*entry{{Token: start, Peer: peer1name}}
|
||||||
ring2.Entries = []*entry{{Token: start, Peer: peer2name}}
|
ring2.Entries = []*entry{{Token: start, Peer: peer2name}}
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ func (m *mockGossipComms) GossipBroadcast(update mesh.GossipData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockGossipComms) GossipNeighbourSubset(update mesh.GossipData) {
|
||||||
|
m.GossipBroadcast(update)
|
||||||
|
}
|
||||||
func equalByteBuffer(a, b []byte) bool {
|
func equalByteBuffer(a, b []byte) bool {
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
return false
|
return false
|
||||||
@@ -117,7 +120,7 @@ func CheckAllExpectedMessagesSent(allocs ...*Allocator) {
|
|||||||
m := alloc.gossip.(*mockGossipComms)
|
m := alloc.gossip.(*mockGossipComms)
|
||||||
m.RLock()
|
m.RLock()
|
||||||
if len(m.messages) > 0 {
|
if len(m.messages) > 0 {
|
||||||
require.FailNow(m, fmt.Sprintf("%s: Gossip message(s) not sent as expected: \n%x", m.name, m.messages))
|
require.FailNow(m, fmt.Sprintf("%s: Gossip message(s) not sent as expected: \n%v", m.name, m.messages))
|
||||||
}
|
}
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-42
@@ -84,7 +84,7 @@ func NewAWSVPCTracker(bridgeName string) (*AWSVPCTracker, error) {
|
|||||||
func (t *AWSVPCTracker) HandleUpdate(prevRanges, currRanges []address.Range, local bool) error {
|
func (t *AWSVPCTracker) HandleUpdate(prevRanges, currRanges []address.Range, local bool) error {
|
||||||
t.debugf("replacing %q by %q; local(%t)", prevRanges, currRanges, local)
|
t.debugf("replacing %q by %q; local(%t)", prevRanges, currRanges, local)
|
||||||
|
|
||||||
prev, curr := removeCommon(address.NewCIDRs(merge(prevRanges)), address.NewCIDRs(merge(currRanges)))
|
prev, curr := RemoveCommon(address.NewCIDRs(Merge(prevRanges)), address.NewCIDRs(Merge(currRanges)))
|
||||||
|
|
||||||
// It might make sense to do the removal first and then add entries
|
// It might make sense to do the removal first and then add entries
|
||||||
// because of the 50 routes limit. However, in such case a container might
|
// because of the 50 routes limit. However, in such case a container might
|
||||||
@@ -123,6 +123,10 @@ func (t *AWSVPCTracker) HandleUpdate(prevRanges, currRanges []address.Range, loc
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *AWSVPCTracker) String() string {
|
||||||
|
return "awsvpc"
|
||||||
|
}
|
||||||
|
|
||||||
func (t *AWSVPCTracker) createVPCRoute(cidr string) (*ec2.CreateRouteOutput, error) {
|
func (t *AWSVPCTracker) createVPCRoute(cidr string) (*ec2.CreateRouteOutput, error) {
|
||||||
route := &ec2.CreateRouteInput{
|
route := &ec2.CreateRouteInput{
|
||||||
RouteTableId: &t.routeTableID,
|
RouteTableId: &t.routeTableID,
|
||||||
@@ -232,47 +236,6 @@ func (t *AWSVPCTracker) infof(fmt string, args ...interface{}) {
|
|||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
// merge merges adjacent range entries.
|
|
||||||
// The given slice has to be sorted in increasing order.
|
|
||||||
func merge(r []address.Range) []address.Range {
|
|
||||||
var merged []address.Range
|
|
||||||
|
|
||||||
for i := range r {
|
|
||||||
if prev := len(merged) - 1; prev >= 0 && merged[prev].End == r[i].Start {
|
|
||||||
merged[prev].End = r[i].End
|
|
||||||
} else {
|
|
||||||
merged = append(merged, r[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeCommon filters out CIDR ranges which are contained in both a and b slices.
|
|
||||||
// Both slices have to be sorted in increasing order.
|
|
||||||
func removeCommon(a, b []address.CIDR) (newA, newB []address.CIDR) {
|
|
||||||
i, j := 0, 0
|
|
||||||
|
|
||||||
for i < len(a) && j < len(b) {
|
|
||||||
switch {
|
|
||||||
case a[i].Start() < b[j].Start() || a[i].End() < b[j].End():
|
|
||||||
newA = append(newA, a[i])
|
|
||||||
i++
|
|
||||||
case a[i].Start() > b[j].Start() || a[i].End() > b[j].End():
|
|
||||||
newB = append(newB, b[j])
|
|
||||||
j++
|
|
||||||
default:
|
|
||||||
i++
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
newA = append(newA, a[i:]...)
|
|
||||||
newB = append(newB, b[j:]...)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseCIDR(cidr string) (*net.IPNet, error) {
|
func parseCIDR(cidr string) (*net.IPNet, error) {
|
||||||
ip, ipnet, err := net.ParseCIDR(cidr)
|
ip, ipnet, err := net.ParseCIDR(cidr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package tracker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/weaveworks/weave/net/address"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Merge merges adjacent range entries.
|
||||||
|
// The given slice has to be sorted in increasing order.
|
||||||
|
func Merge(r []address.Range) []address.Range {
|
||||||
|
var merged []address.Range
|
||||||
|
|
||||||
|
for i := range r {
|
||||||
|
if prev := len(merged) - 1; prev >= 0 && merged[prev].End == r[i].Start {
|
||||||
|
merged[prev].End = r[i].End
|
||||||
|
} else {
|
||||||
|
merged = append(merged, r[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return merged
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveCommon filters out CIDR ranges which are contained in both a and b slices.
|
||||||
|
// Both slices have to be sorted in increasing order.
|
||||||
|
func RemoveCommon(a, b []address.CIDR) (newA, newB []address.CIDR) {
|
||||||
|
i, j := 0, 0
|
||||||
|
|
||||||
|
for i < len(a) && j < len(b) {
|
||||||
|
switch {
|
||||||
|
case a[i].Start() < b[j].Start() || a[i].End() < b[j].End():
|
||||||
|
newA = append(newA, a[i])
|
||||||
|
i++
|
||||||
|
case a[i].Start() > b[j].Start() || a[i].End() > b[j].End():
|
||||||
|
newB = append(newB, b[j])
|
||||||
|
j++
|
||||||
|
default:
|
||||||
|
i++
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
newA = append(newA, a[i:]...)
|
||||||
|
newB = append(newB, b[j:]...)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ var (
|
|||||||
func TestRemoveCommon(t *testing.T) {
|
func TestRemoveCommon(t *testing.T) {
|
||||||
a := []address.CIDR{r0to127, r1dot0to255}
|
a := []address.CIDR{r0to127, r1dot0to255}
|
||||||
b := []address.CIDR{r1dot0to255, r2dot0to255}
|
b := []address.CIDR{r1dot0to255, r2dot0to255}
|
||||||
newA, newB := removeCommon(a, b)
|
newA, newB := RemoveCommon(a, b)
|
||||||
require.Equal(t, []address.CIDR{r0to127}, newA)
|
require.Equal(t, []address.CIDR{r0to127}, newA)
|
||||||
require.Equal(t, []address.CIDR{r2dot0to255}, newB)
|
require.Equal(t, []address.CIDR{r2dot0to255}, newB)
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ func TestMerge(t *testing.T) {
|
|||||||
r128to255.Range(),
|
r128to255.Range(),
|
||||||
r2dot0to255.Range(),
|
r2dot0to255.Range(),
|
||||||
}
|
}
|
||||||
require.Equal(t, []address.Range{r0to255.Range(), r2dot0to255.Range()}, merge(ranges))
|
require.Equal(t, []address.Range{r0to255.Range(), r2dot0to255.Range()}, Merge(ranges))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper
|
// Helper
|
||||||
@@ -17,4 +17,7 @@ type LocalRangeTracker interface {
|
|||||||
// The local parameter indicates whether the ranges belong to the peer
|
// The local parameter indicates whether the ranges belong to the peer
|
||||||
// by which the method is called.
|
// by which the method is called.
|
||||||
HandleUpdate(prevRanges, currRanges []address.Range, local bool) error
|
HandleUpdate(prevRanges, currRanges []address.Range, local bool) error
|
||||||
|
|
||||||
|
// String returns the tracker name
|
||||||
|
String() string
|
||||||
}
|
}
|
||||||
+1
-1
@@ -10,7 +10,7 @@ import (
|
|||||||
// Configure the ARP cache parameters for the given interface. This
|
// Configure the ARP cache parameters for the given interface. This
|
||||||
// makes containers react more quickly to a change in the MAC address
|
// makes containers react more quickly to a change in the MAC address
|
||||||
// associated with an IP address.
|
// associated with an IP address.
|
||||||
func ConfigureARPCache(procPath, name string) error {
|
func configureARPCache(procPath, name string) error {
|
||||||
if err := sysctl(procPath, fmt.Sprintf("net/ipv4/neigh/%s/base_reachable_time", name), "5"); err != nil {
|
if err := sysctl(procPath, fmt.Sprintf("net/ipv4/neigh/%s/base_reachable_time", name), "5"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+161
-52
@@ -5,13 +5,20 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/common"
|
||||||
"github.com/weaveworks/weave/common/odp"
|
"github.com/weaveworks/weave/common/odp"
|
||||||
|
"github.com/weaveworks/weave/ipam/tracker"
|
||||||
|
"github.com/weaveworks/weave/net/address"
|
||||||
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
|
"github.com/weaveworks/weave/npc"
|
||||||
)
|
)
|
||||||
|
|
||||||
/* This code implements three possible configurations to connect
|
/* This code implements three possible configurations to connect
|
||||||
@@ -47,11 +54,12 @@ datapath of old kernel versions (https://github.com/weaveworks/weave/issues/1577
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const (
|
const (
|
||||||
WeaveBridgeName = "weave"
|
WeaveBridgeName = "weave"
|
||||||
DatapathName = "datapath"
|
DatapathName = "datapath"
|
||||||
DatapathIfName = "vethwe-datapath"
|
DatapathIfName = "vethwe-datapath"
|
||||||
BridgeIfName = "vethwe-bridge"
|
BridgeIfName = "vethwe-bridge"
|
||||||
PcapIfName = "vethwe-pcap"
|
PcapIfName = "vethwe-pcap"
|
||||||
|
NoMasqLocalIpset = ipset.Name("weaver-no-masq-local")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Bridge interface {
|
type Bridge interface {
|
||||||
@@ -213,6 +221,7 @@ type BridgeConfig struct {
|
|||||||
MTU int
|
MTU int
|
||||||
Mac string
|
Mac string
|
||||||
Port int
|
Port int
|
||||||
|
NoMasqLocal bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (config *BridgeConfig) configuredBridgeType() Bridge {
|
func (config *BridgeConfig) configuredBridgeType() Bridge {
|
||||||
@@ -226,7 +235,7 @@ func (config *BridgeConfig) configuredBridgeType() Bridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger) (Bridge, error) {
|
func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger, ips ipset.Interface) (Bridge, error) {
|
||||||
existingBridgeType, err := ExistingBridgeType(config.WeaveBridgeName, config.DatapathName)
|
existingBridgeType, err := ExistingBridgeType(config.WeaveBridgeName, config.DatapathName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -252,7 +261,7 @@ func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger) (Br
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := configureIPTables(config); err != nil {
|
if err := configureIPTables(config, ips); err != nil {
|
||||||
return bridgeType, errors.Wrap(err, "configuring iptables")
|
return bridgeType, errors.Wrap(err, "configuring iptables")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,10 +284,20 @@ func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger) (Br
|
|||||||
return bridgeType, err
|
return bridgeType, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ConfigureARPCache(procPath, config.WeaveBridgeName); err != nil {
|
if err := monitorInterface(config.WeaveBridgeName, log); 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, errors.Wrapf(err, "configuring ARP cache on bridge %q", config.WeaveBridgeName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NB: No concurrent call to Expose is possible, as EnsureBridge is called
|
||||||
|
// before any service has been started.
|
||||||
|
if err := reexpose(config, log); err != nil {
|
||||||
|
return bridgeType, err
|
||||||
|
}
|
||||||
|
|
||||||
return bridgeType, nil
|
return bridgeType, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +309,10 @@ func (b bridgeImpl) initPrep(config *BridgeConfig) error {
|
|||||||
|
|
||||||
linkAttrs := netlink.NewLinkAttrs()
|
linkAttrs := netlink.NewLinkAttrs()
|
||||||
linkAttrs.Name = config.WeaveBridgeName
|
linkAttrs.Name = config.WeaveBridgeName
|
||||||
linkAttrs.HardwareAddr = mac
|
// NB: Do not set MAC addr when creating the bridge, set it manually
|
||||||
|
// afterwards instead. Otherwise, on an older than 3.14 kernel FDB
|
||||||
|
// entry won't be created which results in containers not being able to
|
||||||
|
// reach the bridge w/o promiscuous mode.
|
||||||
if config.MTU == 0 {
|
if config.MTU == 0 {
|
||||||
config.MTU = 65535
|
config.MTU = 65535
|
||||||
}
|
}
|
||||||
@@ -404,7 +426,7 @@ func (f fastdpImpl) attach(veth *netlink.Veth) error {
|
|||||||
return odp.AddDatapathInterfaceIfNotExist(f.datapathName, veth.Attrs().Name)
|
return odp.AddDatapathInterfaceIfNotExist(f.datapathName, veth.Attrs().Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureIPTables(config *BridgeConfig) error {
|
func configureIPTables(config *BridgeConfig, ips ipset.Interface) error {
|
||||||
ipt, err := iptables.New()
|
ipt, err := iptables.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "creating iptables object")
|
return errors.Wrap(err, "creating iptables object")
|
||||||
@@ -444,14 +466,27 @@ func configureIPTables(config *BridgeConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if config.NPC {
|
if config.NPC {
|
||||||
// Steer traffic via the NPC
|
// Steer traffic via the NPC.
|
||||||
_ = ipt.NewChain("filter", "WEAVE-NPC") // ignore error because it might already exist
|
|
||||||
// If WEAVE-NPC chain doesn't exist then creating a rule in the chain will fail
|
if err = ensureChains(ipt, "filter", npc.MainChain, npc.EgressChain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Steer egress traffic destined to local node.
|
||||||
|
if err = ipt.AppendUnique("filter", "INPUT", "-i", config.WeaveBridgeName, "-j", npc.EgressChain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
fwdRules = append(fwdRules,
|
fwdRules = append(fwdRules,
|
||||||
[][]string{
|
[][]string{
|
||||||
|
// Might include ingress traffic which is fine as long as we do not
|
||||||
|
// ACCEPT in WEAVE-NPC-EGRESS chain
|
||||||
|
{"-i", config.WeaveBridgeName,
|
||||||
|
"-m", "comment", "--comment", "NOTE: this must go before '-j KUBE-FORWARD'",
|
||||||
|
"-j", npc.EgressChain},
|
||||||
|
// The following rules are for ingress NPC processing
|
||||||
{"-o", config.WeaveBridgeName,
|
{"-o", config.WeaveBridgeName,
|
||||||
"-m", "comment", "--comment", "NOTE: this must go before '-j KUBE-FORWARD'",
|
"-m", "comment", "--comment", "NOTE: this must go before '-j KUBE-FORWARD'",
|
||||||
"-j", "WEAVE-NPC"},
|
"-j", npc.MainChain},
|
||||||
{"-o", config.WeaveBridgeName, "-m", "state", "--state", "NEW", "-j", "NFLOG", "--nflog-group", "86"},
|
{"-o", config.WeaveBridgeName, "-m", "state", "--state", "NEW", "-j", "NFLOG", "--nflog-group", "86"},
|
||||||
{"-o", config.WeaveBridgeName, "-j", "DROP"},
|
{"-o", config.WeaveBridgeName, "-j", "DROP"},
|
||||||
}...)
|
}...)
|
||||||
@@ -462,8 +497,11 @@ func configureIPTables(config *BridgeConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !config.NPC {
|
if !config.NPC {
|
||||||
// Create a chain for allowing ingress traffic when the bridge is exposed
|
// Create/Flush a chain for allowing ingress traffic when the bridge is exposed
|
||||||
_ = ipt.NewChain("filter", "WEAVE-EXPOSE")
|
if err := ipt.ClearChain("filter", "WEAVE-EXPOSE"); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to clear/create filter/WEAVE-EXPOSE chain")
|
||||||
|
}
|
||||||
|
|
||||||
fwdRules = append(fwdRules, []string{"-o", config.WeaveBridgeName, "-j", "WEAVE-EXPOSE"})
|
fwdRules = append(fwdRules, []string{"-o", config.WeaveBridgeName, "-j", "WEAVE-EXPOSE"})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,13 +514,74 @@ func configureIPTables(config *BridgeConfig) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a chain for masquerading
|
// Create a chain for masquerading
|
||||||
//
|
if err := ipt.ClearChain("nat", "WEAVE"); err != nil {
|
||||||
// NB: we do not clear the chain to preserve existing rules
|
return errors.Wrap(err, "failed to clear/create nat/WEAVE chain")
|
||||||
// inserted by "weave expose".
|
}
|
||||||
_ = ipt.NewChain("nat", "WEAVE")
|
if err := ipt.AppendUnique("nat", "POSTROUTING", "-j", "WEAVE"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return ipt.AppendUnique("nat", "POSTROUTING", "-j", "WEAVE")
|
// For the cases where the weave bridge is the default gateway for
|
||||||
|
// containers (e.g. Kubernetes): create the ipset to store CIDRs allocated
|
||||||
|
// by IPAM for local containers. In the case of Kubernetes, external traffic
|
||||||
|
// sent to these CIDRs avoids SNAT'ing so that NodePort with
|
||||||
|
// `"externalTrafficPolicy":"Local"` would receive packets with correct
|
||||||
|
// src IP addr.
|
||||||
|
if config.NoMasqLocal {
|
||||||
|
ips := ipset.New(common.LogLogger(), 0)
|
||||||
|
_ = ips.Destroy(NoMasqLocalIpset)
|
||||||
|
if err := ips.Create(NoMasqLocalIpset, ipset.HashNet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ipt.Insert("nat", "WEAVE", 1,
|
||||||
|
"-m", "set", "--match-set", string(NoMasqLocalIpset), "dst",
|
||||||
|
"-m", "comment", "--comment", "Prevent SNAT to locally running containers",
|
||||||
|
"-j", "RETURN"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NoMasqLocalTracker struct {
|
||||||
|
ips ipset.Interface
|
||||||
|
owner types.UID
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNoMasqLocalTracker(ips ipset.Interface) *NoMasqLocalTracker {
|
||||||
|
return &NoMasqLocalTracker{
|
||||||
|
ips: ips,
|
||||||
|
owner: types.UID(0), // dummy ipset owner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NoMasqLocalTracker) String() string {
|
||||||
|
return "no-masq-local"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NoMasqLocalTracker) HandleUpdate(prevRanges, currRanges []address.Range, local bool) error {
|
||||||
|
if !local {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
prev, curr := tracker.RemoveCommon(
|
||||||
|
address.NewCIDRs(tracker.Merge(prevRanges)),
|
||||||
|
address.NewCIDRs(tracker.Merge(currRanges)))
|
||||||
|
|
||||||
|
for _, cidr := range curr {
|
||||||
|
if err := t.ips.AddEntry(t.owner, NoMasqLocalIpset, cidr.String(), ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, cidr := range prev {
|
||||||
|
if err := t.ips.DelEntry(t.owner, NoMasqLocalIpset, cidr.String()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func linkSetUpByName(linkName string) error {
|
func linkSetUpByName(linkName string) error {
|
||||||
@@ -493,39 +592,49 @@ func linkSetUpByName(linkName string) error {
|
|||||||
return netlink.LinkSetUp(link)
|
return netlink.LinkSetUp(link)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensureRulesAtTop ensures the presence of given iptables rules.
|
func reexpose(config *BridgeConfig, log *logrus.Logger) error {
|
||||||
//
|
// Get existing IP addrs of the weave bridge.
|
||||||
// If any rule from the list is missing, the function deletes all given
|
// If the bridge hasn't been exposed, then this functions does nothing.
|
||||||
// rules and re-inserts them at the top of the chain to ensure the order of the rules.
|
//
|
||||||
func ensureRulesAtTop(table, chain string, rulespecs [][]string, ipt *iptables.IPTables) error {
|
// Ideally, we should consult IPAM for IP addrs allocated to "weave:expose",
|
||||||
allFound := true
|
// but we don't want to introduce dependency on IPAM, as weave should be able
|
||||||
|
// to run w/o IPAM.
|
||||||
for _, rs := range rulespecs {
|
link, err := netlink.LinkByName(config.WeaveBridgeName)
|
||||||
found, err := ipt.Exists(table, chain, rs...)
|
if err != nil {
|
||||||
if err != nil {
|
return errors.Wrapf(err, "cannot find bridge %q", config.WeaveBridgeName)
|
||||||
return errors.Wrapf(err, "ipt.Exists(%s, %s, %s)", table, chain, rs)
|
}
|
||||||
}
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
if !found {
|
if err != nil {
|
||||||
allFound = false
|
return errors.Wrapf(err, "cannot list IPv4 addrs of bridge %q", config.WeaveBridgeName)
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All rules exist, do nothing.
|
for _, addr := range addrs {
|
||||||
if allFound {
|
log.Infof("Re-exposing %s on bridge %q", addr.IPNet, config.WeaveBridgeName)
|
||||||
return nil
|
if err := Expose(config.WeaveBridgeName, addr.IPNet, config.AWSVPC, config.NPC, false); err != nil {
|
||||||
}
|
return errors.Wrapf(err, "unable to re-expose %s on bridge: %q", addr.IPNet, config.WeaveBridgeName)
|
||||||
|
|
||||||
for pos, rs := range rulespecs {
|
|
||||||
// If any is missing, then delete all, as we need to preserve the order of
|
|
||||||
// given rules. Ignore errors, as rule might not exist.
|
|
||||||
if !allFound {
|
|
||||||
ipt.Delete(table, chain, rs...)
|
|
||||||
}
|
|
||||||
if err := ipt.Insert(table, chain, pos+1, rs...); err != nil {
|
|
||||||
return errors.Wrapf(err, "ipt.Append(%s, %s, %s)", table, chain, rs)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func monitorInterface(ifaceName string, log *logrus.Logger) error {
|
||||||
|
_, err := netlink.LinkByName(ifaceName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to find link %q: %s", ifaceName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updatesChannel := make(chan netlink.LinkUpdate)
|
||||||
|
if err := netlink.LinkSubscribe(updatesChannel, nil); err != nil {
|
||||||
|
return errors.Wrapf(err, "error monitoring link %q for UP/DOWN notifications", ifaceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for update := range updatesChannel {
|
||||||
|
if update.Link.Attrs().Name == ifaceName && update.IfInfomsg.Flags&syscall.IFF_UP == 0 {
|
||||||
|
log.Errorf("Interface %q which needs to be in UP state for Weave functioning is found to be in DOWN state", ifaceName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+6
-3
@@ -18,7 +18,8 @@ import (
|
|||||||
// * "ipAddr" - IP addr to be assigned to the 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).
|
// * "removeDefaultRoute" - whether to remove a default route installed by the kernel (used only in the AWSVPC mode).
|
||||||
// * "npc" - whether is Weave NPC running.
|
// * "npc" - whether is Weave NPC running.
|
||||||
func Expose(bridgeName string, ipAddr *net.IPNet, removeDefaultRoute, npc bool) error {
|
// * "skipNAT" - whether to skip adding iptables NAT rules
|
||||||
|
func Expose(bridgeName string, ipAddr *net.IPNet, removeDefaultRoute, npc bool, skipNAT bool) error {
|
||||||
ipt, err := iptables.New()
|
ipt, err := iptables.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "iptables.New")
|
return errors.Wrap(err, "iptables.New")
|
||||||
@@ -29,8 +30,10 @@ func Expose(bridgeName string, ipAddr *net.IPNet, removeDefaultRoute, npc bool)
|
|||||||
return errors.Wrap(err, "addBridgeIPAddr")
|
return errors.Wrap(err, "addBridgeIPAddr")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := exposeNAT(ipt, cidr); err != nil {
|
if !skipNAT {
|
||||||
return errors.Wrap(err, "exposeNAT")
|
if err := exposeNAT(ipt, cidr); err != nil {
|
||||||
|
return errors.Wrap(err, "exposeNAT")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !npc {
|
if !npc {
|
||||||
|
|||||||
+23
-17
@@ -12,9 +12,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ func New(log *logrus.Logger) (*IPSec, error) {
|
|||||||
|
|
||||||
// InitSALocal initializes inbound ipsec from remotePeer and triggers
|
// InitSALocal initializes inbound ipsec from remotePeer and triggers
|
||||||
// the initialization on remotePeer.
|
// the initialization on remotePeer.
|
||||||
func (ipsec *IPSec) InitSALocal(localPeer, remotePeer mesh.PeerName, connUID uint64, localIP, remoteIP net.IP, udpPort int, sessionKey *[32]byte, initRemote func([]byte) error) error {
|
func (ipsec *IPSec) InitSALocal(localPeer, remotePeer mesh.PeerName, connUID uint64, localIP, remoteIP net.IP, udpPort int, sessionKey *[32]byte) ([]byte, error) {
|
||||||
// ID of inbound SPI
|
// ID of inbound SPI
|
||||||
spiID := getSPIId(remotePeer, localPeer, connUID)
|
spiID := getSPIId(remotePeer, localPeer, connUID)
|
||||||
|
|
||||||
@@ -95,17 +95,17 @@ func (ipsec *IPSec) InitSALocal(localPeer, remotePeer mesh.PeerName, connUID uin
|
|||||||
// Derive SA key
|
// Derive SA key
|
||||||
nonce, err := genNonce()
|
nonce, err := genNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "generate nonce")
|
return nil, errors.Wrap(err, "generate nonce")
|
||||||
}
|
}
|
||||||
key, err := deriveKey(sessionKey[:], nonce, localPeer)
|
key, err := deriveKey(sessionKey[:], nonce, localPeer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "derive key")
|
return nil, errors.Wrap(err, "derive key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate SA
|
// Allocate SA
|
||||||
sa, err := netlink.XfrmStateAllocSpi(xfrmAllocSpiState(remoteIP, localIP))
|
sa, err := netlink.XfrmStateAllocSpi(xfrmAllocSpiState(remoteIP, localIP))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, fmt.Sprintf("ip xfrm state allocspi (in, %s, %s)", remoteIP, localIP))
|
return nil, errors.Wrap(err, fmt.Sprintf("ip xfrm state allocspi (in, %s, %s)", remoteIP, localIP))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use SPI generated by the kernel. The kernel ensures {dstIP, spi} to
|
// Use SPI generated by the kernel. The kernel ensures {dstIP, spi} to
|
||||||
@@ -117,28 +117,24 @@ func (ipsec *IPSec) InitSALocal(localPeer, remotePeer mesh.PeerName, connUID uin
|
|||||||
// Create SA
|
// Create SA
|
||||||
if sa, err := xfrmState(remoteIP, localIP, spi, false, key); err == nil {
|
if sa, err := xfrmState(remoteIP, localIP, spi, false, key); err == nil {
|
||||||
if err := netlink.XfrmStateUpdate(sa); err != nil {
|
if err := netlink.XfrmStateUpdate(sa); err != nil {
|
||||||
return errors.Wrap(err, fmt.Sprintf("xfrm state update (in, %s, %s, 0x%x)", sa.Src, sa.Dst, sa.Spi))
|
return nil, errors.Wrap(err, fmt.Sprintf("xfrm state update (in, %s, %s, 0x%x)", sa.Src, sa.Dst, sa.Spi))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return errors.Wrap(err, "new xfrm state (in)")
|
return nil, errors.Wrap(err, "new xfrm state (in)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install iptables rules
|
// Install iptables rules
|
||||||
if err := ipsec.installDropNonEncrypted(localIP, remoteIP, udpPort, spi); err != nil {
|
if err := ipsec.installDropNonEncrypted(localIP, remoteIP, udpPort, spi); err != nil {
|
||||||
return errors.Wrap(err, fmt.Sprintf("install protecting rules (%s, %s, %d, 0x%x)", localIP, remoteIP, udpPort, spi))
|
return nil, errors.Wrap(err, fmt.Sprintf("install protecting rules (%s, %s, %d, 0x%x)", localIP, remoteIP, udpPort, spi))
|
||||||
}
|
}
|
||||||
|
|
||||||
si := spiInfo{spi: spi, isDirOut: false}
|
si := spiInfo{spi: spi, isDirOut: false}
|
||||||
ipsec.spiInfo[spiID] = si
|
ipsec.spiInfo[spiID] = si
|
||||||
ipsec.spis[spi] = &si
|
ipsec.spis[spi] = &si
|
||||||
|
|
||||||
// Trigger the initialization on the remote peer
|
// Generate the message to trigger initialization on the remote peer
|
||||||
msg := &msgInitSARemote{nonce, spi}
|
msg := &msgInitSARemote{nonce, spi}
|
||||||
if err := initRemote(msg.serialize()); err != nil {
|
return msg.serialize(), nil
|
||||||
return errors.Wrap(err, "send InitSARemote")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitSARemote initializes outbound ipsec to remotePeer.
|
// InitSARemote initializes outbound ipsec to remotePeer.
|
||||||
@@ -223,9 +219,19 @@ func (ipsec *IPSec) Destroy(localPeer, remotePeer mesh.PeerName, connUID uint64,
|
|||||||
|
|
||||||
if outSPIInfo, ok := ipsec.spiInfo[outSPIID]; ok {
|
if outSPIInfo, ok := ipsec.spiInfo[outSPIID]; ok {
|
||||||
ipsec.log.Infof("ipsec: destroy: out %s -> %s 0x%x", localIP, remoteIP, outSPIInfo.spi)
|
ipsec.log.Infof("ipsec: destroy: out %s -> %s 0x%x", localIP, remoteIP, outSPIInfo.spi)
|
||||||
|
policy, err := netlink.XfrmPolicyGet(xfrmPolicy(localIP, remoteIP, outSPIInfo.spi))
|
||||||
if err := netlink.XfrmPolicyDel(xfrmPolicy(localIP, remoteIP, outSPIInfo.spi)); err != nil {
|
if err != nil {
|
||||||
ipsec.log.Warnf("ipsec: xfrm policy del (%s, %s, 0x%x) failed: %s", localIP, remoteIP, outSPIInfo.spi, err)
|
ipsec.log.Warnf("ipsec: xfrm policy get (%s, %s, 0x%x) failed: %s", localIP, remoteIP, outSPIInfo.spi, err)
|
||||||
|
} else {
|
||||||
|
if len(policy.Tmpls) == 1 {
|
||||||
|
if policy.Tmpls[0].Spi == int(outSPIInfo.spi) {
|
||||||
|
if err := netlink.XfrmPolicyDel(xfrmPolicy(localIP, remoteIP, outSPIInfo.spi)); err != nil {
|
||||||
|
ipsec.log.Warnf("ipsec: xfrm policy del (%s, %s, 0x%x) failed: %s", localIP, remoteIP, outSPIInfo.spi, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ipsec.log.Debugf("ipsec: xfrm not my policy (%s, %s, 0x%x) got 0x%x ", localIP, remoteIP, outSPIInfo.spi, policy.Tmpls[0].Spi)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
outSA := &netlink.XfrmState{
|
outSA := &netlink.XfrmState{
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package ipset
|
package ipset
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -16,6 +19,7 @@ type Type string
|
|||||||
const (
|
const (
|
||||||
ListSet = Type("list:set")
|
ListSet = Type("list:set")
|
||||||
HashIP = Type("hash:ip")
|
HashIP = Type("hash:ip")
|
||||||
|
HashNet = Type("hash:net")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
@@ -40,6 +44,7 @@ type entryKey struct {
|
|||||||
type ipset struct {
|
type ipset struct {
|
||||||
*log.Logger
|
*log.Logger
|
||||||
enableComments bool
|
enableComments bool
|
||||||
|
maxListSize int
|
||||||
// List of users per ipset entry. User is either a namespace or a pod.
|
// List of users per ipset entry. User is either a namespace or a pod.
|
||||||
// There might be multiple users for the same ipset & entry pair because
|
// There might be multiple users for the same ipset & entry pair because
|
||||||
// events from k8s API server might be out of order causing duplicate IPs:
|
// events from k8s API server might be out of order causing duplicate IPs:
|
||||||
@@ -47,15 +52,23 @@ type ipset struct {
|
|||||||
users map[entryKey]map[types.UID]struct{}
|
users map[entryKey]map[types.UID]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(logger *log.Logger) Interface {
|
func New(logger *log.Logger, maxListSize int) Interface {
|
||||||
ips := &ipset{
|
ips := &ipset{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
enableComments: true,
|
enableComments: true,
|
||||||
|
maxListSize: maxListSize,
|
||||||
users: make(map[entryKey]map[types.UID]struct{}),
|
users: make(map[entryKey]map[types.UID]struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for comment support
|
// Check for comment support
|
||||||
testIpsetName := Name("weave-test-comment")
|
|
||||||
|
// To prevent from a race when more than one process check for the support
|
||||||
|
// we append a random nonce to the test ipset name. The final name is
|
||||||
|
// shorter than 31 chars (max ipset name).
|
||||||
|
nonce := make([]byte, 4)
|
||||||
|
rand.Read(nonce)
|
||||||
|
testIpsetName := Name("weave-test-comment" + hex.EncodeToString(nonce))
|
||||||
|
|
||||||
// Clear it out if it already exists
|
// Clear it out if it already exists
|
||||||
_ = ips.Destroy(testIpsetName)
|
_ = ips.Destroy(testIpsetName)
|
||||||
// Test for comment support
|
// Test for comment support
|
||||||
@@ -71,6 +84,9 @@ func New(logger *log.Logger) Interface {
|
|||||||
|
|
||||||
func (i *ipset) Create(ipsetName Name, ipsetType Type) error {
|
func (i *ipset) Create(ipsetName Name, ipsetType Type) error {
|
||||||
args := []string{"create", string(ipsetName), string(ipsetType)}
|
args := []string{"create", string(ipsetName), string(ipsetType)}
|
||||||
|
if ipsetType == ListSet && i.maxListSize > 0 {
|
||||||
|
args = append(args, "size", fmt.Sprintf("%d", i.maxListSize))
|
||||||
|
}
|
||||||
if i.enableComments {
|
if i.enableComments {
|
||||||
args = append(args, "comment")
|
args = append(args, "comment")
|
||||||
}
|
}
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddChainWithRules creates a chain and appends given rules to it.
|
||||||
|
//
|
||||||
|
// If the chain exists, but its rules are not the same as the given ones, the
|
||||||
|
// function will flush the chain and then will append the rules.
|
||||||
|
func AddChainWithRules(ipt *iptables.IPTables, table, chain string, rulespecs [][]string) error {
|
||||||
|
if err := ensureChains(ipt, table, chain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
currRuleSpecs, err := ipt.List(table, chain)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "iptables -S. table: %q, chain: %q", table, chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First returned rule is "-N $(chain)", so ignore it
|
||||||
|
currRules := strings.Join(currRuleSpecs[1:], "\n")
|
||||||
|
rules := make([]string, 0)
|
||||||
|
for _, r := range rulespecs {
|
||||||
|
rules = append(rules, strings.Join(r, " "))
|
||||||
|
}
|
||||||
|
reqRules := strings.Join(rules, "\n")
|
||||||
|
|
||||||
|
if currRules == reqRules {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipt.ClearChain(table, chain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range rulespecs {
|
||||||
|
if err := ipt.Append(table, chain, r...); err != nil {
|
||||||
|
return errors.Wrapf(err, "iptables -A. table: %q, chain: %q, rule: %s", table, chain, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureChains creates given chains if they do not exist.
|
||||||
|
func ensureChains(ipt *iptables.IPTables, table string, chains ...string) error {
|
||||||
|
existingChains, err := ipt.ListChains(table)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "ipt.ListChains(%s)", table)
|
||||||
|
}
|
||||||
|
chainMap := make(map[string]struct{})
|
||||||
|
for _, c := range existingChains {
|
||||||
|
chainMap[c] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range chains {
|
||||||
|
if _, found := chainMap[c]; !found {
|
||||||
|
if err := ipt.NewChain(table, c); err != nil {
|
||||||
|
return errors.Wrapf(err, "ipt.NewChain(%s, %s)", table, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureRulesAtTop ensures the presence of given iptables rules.
|
||||||
|
//
|
||||||
|
// If any rule from the list is missing, the function deletes all given
|
||||||
|
// rules and re-inserts them at the top of the chain to ensure the order of the rules.
|
||||||
|
func ensureRulesAtTop(table, chain string, rulespecs [][]string, ipt *iptables.IPTables) error {
|
||||||
|
allFound := true
|
||||||
|
|
||||||
|
for _, rs := range rulespecs {
|
||||||
|
found, err := ipt.Exists(table, chain, rs...)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "ipt.Exists(%s, %s, %s)", table, chain, rs)
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
allFound = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All rules exist, do nothing.
|
||||||
|
if allFound {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for pos, rs := range rulespecs {
|
||||||
|
// If any is missing, then delete all, as we need to preserve the order of
|
||||||
|
// given rules. Ignore errors, as rule might not exist.
|
||||||
|
if !allFound {
|
||||||
|
ipt.Delete(table, chain, rs...)
|
||||||
|
}
|
||||||
|
if err := ipt.Insert(table, chain, pos+1, rs...); err != nil {
|
||||||
|
return errors.Wrapf(err, "ipt.Append(%s, %s, %s)", table, chain, rs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+25
-13
@@ -1,11 +1,9 @@
|
|||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"github.com/vishvananda/netns"
|
"github.com/vishvananda/netns"
|
||||||
@@ -17,7 +15,7 @@ type Dev struct {
|
|||||||
CIDRs []*net.IPNet `json:"CIDRs,omitempty"`
|
CIDRs []*net.IPNet `json:"CIDRs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LinkToNetDev(link netlink.Link) (Dev, error) {
|
func linkToNetDev(link netlink.Link) (Dev, error) {
|
||||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Dev{}, err
|
return Dev{}, err
|
||||||
@@ -90,16 +88,30 @@ func GetNetDevsByVethPeerIds(processID int, peerIDs []int) ([]Dev, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convert list of peerIDs into a map for faster lookup
|
||||||
|
indexes := make(map[int]struct{})
|
||||||
|
for _, id := range peerIDs {
|
||||||
|
indexes[id] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
var netdevs []Dev
|
var netdevs []Dev
|
||||||
peersStr := make([]string, len(peerIDs))
|
err = WithNetNS(netnsContainer, func() error {
|
||||||
for i, id := range peerIDs {
|
links, err := netlink.LinkList()
|
||||||
peersStr[i] = strconv.Itoa(id)
|
if err != nil {
|
||||||
}
|
return err
|
||||||
nds, err := WithNetNSByPid(processID, "list-netdevs", peersStr...)
|
}
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("list-netdevs failed: %s", err)
|
for _, link := range links {
|
||||||
}
|
if _, found := indexes[link.Attrs().Index]; found {
|
||||||
err = json.Unmarshal(nds, &netdevs)
|
netdev, err := linkToNetDev(link)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
netdevs = append(netdevs, netdev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
return netdevs, err
|
return netdevs, err
|
||||||
}
|
}
|
||||||
@@ -112,5 +124,5 @@ func GetBridgeNetDev(bridgeName string) (Dev, error) {
|
|||||||
return Dev{}, err
|
return Dev{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return LinkToNetDev(link)
|
return linkToNetDev(link)
|
||||||
}
|
}
|
||||||
+10
-36
@@ -1,10 +1,10 @@
|
|||||||
|
// +build go1.10
|
||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
@@ -14,19 +14,7 @@ import (
|
|||||||
|
|
||||||
var ErrLinkNotFound = errors.New("Link not found")
|
var ErrLinkNotFound = errors.New("Link not found")
|
||||||
|
|
||||||
// NB: The following function is unsafe, because:
|
func WithNetNS(ns netns.NsHandle, work func() error) error {
|
||||||
// - It changes a network namespace (netns) of an OS thread which runs
|
|
||||||
// the function. During execution, the Go runtime might clone a new OS thread
|
|
||||||
// for scheduling other go-routines, thus they might end up running in
|
|
||||||
// a "wrong" netns.
|
|
||||||
// - runtime.LockOSThread does not guarantee that a spawned go-routine on
|
|
||||||
// the locked thread will be run by it. Thus, the work function is
|
|
||||||
// not allowed to spawn any go-routine which is dependent on the given netns.
|
|
||||||
|
|
||||||
// Please see https://github.com/weaveworks/weave/issues/2388#issuecomment-228365069
|
|
||||||
// for more details and make sure that you understand the implications before
|
|
||||||
// using the function!
|
|
||||||
func WithNetNSUnsafe(ns netns.NsHandle, work func() error) error {
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
@@ -45,8 +33,8 @@ func WithNetNSUnsafe(ns netns.NsHandle, work func() error) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithNetNSLinkUnsafe(ns netns.NsHandle, ifName string, work func(link netlink.Link) error) error {
|
func WithNetNSLink(ns netns.NsHandle, ifName string, work func(link netlink.Link) error) error {
|
||||||
return WithNetNSUnsafe(ns, func() error {
|
return WithNetNS(ns, func() error {
|
||||||
link, err := netlink.LinkByName(ifName)
|
link, err := netlink.LinkByName(ifName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == errors.New("Link not found").Error() {
|
if err.Error() == errors.New("Link not found").Error() {
|
||||||
@@ -58,26 +46,12 @@ func WithNetNSLinkUnsafe(ns netns.NsHandle, ifName string, work func(link netlin
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var WeaveUtilCmd = "weaveutil"
|
func WithNetNSByPath(path string, work func() error) error {
|
||||||
|
ns, err := netns.GetFromPath(path)
|
||||||
// A safe version of WithNetNS* which creates a process executing
|
if err != nil {
|
||||||
// "nsenter --net=<ns-path> weaveutil <cmd> [args]".
|
return err
|
||||||
func WithNetNS(nsPath string, cmd string, args ...string) ([]byte, error) {
|
|
||||||
var stdout, stderr bytes.Buffer
|
|
||||||
|
|
||||||
args = append([]string{"--net=" + nsPath, WeaveUtilCmd, cmd}, args...)
|
|
||||||
c := exec.Command("nsenter", args...)
|
|
||||||
c.Stdout = &stdout
|
|
||||||
c.Stderr = &stderr
|
|
||||||
if err := c.Run(); err != nil {
|
|
||||||
return nil, fmt.Errorf("%s: %s", string(stderr.Bytes()), err)
|
|
||||||
}
|
}
|
||||||
|
return WithNetNS(ns, work)
|
||||||
return stdout.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithNetNSByPid(pid int, cmd string, args ...string) ([]byte, error) {
|
|
||||||
return WithNetNS(NSPathByPid(pid), cmd, args...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NSPathByPid(pid int) string {
|
func NSPathByPid(pid int) string {
|
||||||
|
|||||||
+36
-17
@@ -104,7 +104,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func interfaceExistsInNamespace(netNSPath string, ifName string) bool {
|
func interfaceExistsInNamespace(netNSPath string, ifName string) bool {
|
||||||
_, err := WithNetNS(netNSPath, "check-iface", ifName)
|
err := WithNetNSByPath(netNSPath, func() error {
|
||||||
|
_, err := netlink.LinkByName(ifName)
|
||||||
|
return err
|
||||||
|
})
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +128,9 @@ func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMult
|
|||||||
if err := netlink.LinkSetNsFd(veth, int(ns)); err != nil {
|
if err := netlink.LinkSetNsFd(veth, int(ns)); err != nil {
|
||||||
return fmt.Errorf("failed to move veth to container netns: %s", err)
|
return fmt.Errorf("failed to move veth to container netns: %s", err)
|
||||||
}
|
}
|
||||||
if _, err := WithNetNS(netNSPath, "setup-iface", peerName, ifName); err != nil {
|
if err := WithNetNS(ns, func() error {
|
||||||
|
return setupIface(peerName, ifName)
|
||||||
|
}); err != nil {
|
||||||
return fmt.Errorf("error setting up interface: %s", err)
|
return fmt.Errorf("error setting up interface: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -139,19 +144,16 @@ func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMult
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string{ifName, fmt.Sprintf("%t", withMulticastRoute)}
|
if err := WithNetNSLink(ns, ifName, func(veth netlink.Link) error {
|
||||||
for _, cidr := range cidrs {
|
return setupIfaceAddrs(veth, withMulticastRoute, cidrs)
|
||||||
args = append(args, cidr.String())
|
}); err != nil {
|
||||||
}
|
|
||||||
if _, err := WithNetNS(netNSPath, "setup-iface-addrs", args...); err != nil {
|
|
||||||
return fmt.Errorf("error setting up interface addresses: %s", err)
|
return fmt.Errorf("error setting up interface addresses: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupIfaceAddrs is the implementation of the 'setup-iface-addrs' call above,
|
// setupIfaceAddrs expects to be called in the container's netns
|
||||||
// running in another process in the container's netns
|
func setupIfaceAddrs(veth netlink.Link, withMulticastRoute bool, cidrs []*net.IPNet) error {
|
||||||
func SetupIfaceAddrs(veth netlink.Link, withMulticastRoute bool, cidrs []*net.IPNet) error {
|
|
||||||
newAddresses, err := AddAddresses(veth, cidrs)
|
newAddresses, err := AddAddresses(veth, cidrs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -203,9 +205,8 @@ func SetupIfaceAddrs(veth netlink.Link, withMulticastRoute bool, cidrs []*net.IP
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupIface is the implementation of the 'setup-iface' call above,
|
// setupIface expects to be called in the container's netns
|
||||||
// running in another process in the container's netns
|
func setupIface(ifaceName, newIfName string) error {
|
||||||
func SetupIface(ifaceName, newIfName string) error {
|
|
||||||
ipt, err := iptables.New()
|
ipt, err := iptables.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -219,14 +220,32 @@ func SetupIface(ifaceName, newIfName string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// This is only called by AttachContainer which is only called in host pid namespace
|
// This is only called by AttachContainer which is only called in host pid namespace
|
||||||
if err := ConfigureARPCache("/proc", newIfName); err != nil {
|
if err := configureARPCache("/proc", newIfName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ipt.Append("filter", "INPUT", "-i", newIfName, "-d", "224.0.0.0/4", "-j", "DROP")
|
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
|
// configureARP is a helper for the Docker plugin which doesn't set the addresses itself
|
||||||
// after calling the function as it changes netns via WithNetNSLinkUnsafe.
|
func ConfigureARP(prefix, rootPath string) error {
|
||||||
|
links, err := netlink.LinkList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, link := range links {
|
||||||
|
ifName := link.Attrs().Name
|
||||||
|
if strings.HasPrefix(ifName, prefix) {
|
||||||
|
configureARPCache(rootPath+"/proc", ifName)
|
||||||
|
if addrs, err := netlink.AddrList(link, netlink.FAMILY_V4); err == nil {
|
||||||
|
for _, addr := range addrs {
|
||||||
|
arping.GratuitousArpOverIfaceByName(addr.IPNet.IP, ifName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func DetachContainer(netNSPath, id, ifName string, cidrs []*net.IPNet) error {
|
func DetachContainer(netNSPath, id, ifName string, cidrs []*net.IPNet) error {
|
||||||
ns, err := netns.GetFromPath(netNSPath)
|
ns, err := netns.GetFromPath(netNSPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -239,7 +258,7 @@ func DetachContainer(netNSPath, id, ifName string, cidrs []*net.IPNet) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return WithNetNSLinkUnsafe(ns, ifName, func(veth netlink.Link) error {
|
return WithNetNSLink(ns, ifName, func(veth netlink.Link) error {
|
||||||
existingAddrs, err := netlink.AddrList(veth, netlink.FAMILY_V4)
|
existingAddrs, err := netlink.AddrList(veth, netlink.FAMILY_V4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get IP address for %q: %v", veth.Attrs().Name, err)
|
return fmt.Errorf("failed to get IP address for %q: %v", veth.Attrs().Name, err)
|
||||||
|
|||||||
+161
-141
@@ -4,173 +4,190 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
apiv1 "k8s.io/api/core/v1"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
extnapi "k8s.io/api/extensions/v1beta1"
|
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
|
||||||
"github.com/weaveworks/weave/npc/ipset"
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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) {
|
|
||||||
|
|
||||||
nsSelectors = make(map[string]*selectorSpec)
|
|
||||||
podSelectors = make(map[string]*selectorSpec)
|
|
||||||
rules = make(map[string]*ruleSpec)
|
|
||||||
|
|
||||||
dstSelector, err := newSelectorSpec(&policy.Spec.PodSelector, true, ns.name, ipset.HashIP)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
podSelectors[dstSelector.key] = dstSelector
|
|
||||||
|
|
||||||
for _, ingressRule := range policy.Spec.Ingress {
|
|
||||||
if ingressRule.Ports != nil && len(ingressRule.Ports) == 0 {
|
|
||||||
// Ports is empty, this rule matches no ports (no traffic matches).
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ingressRule.From != nil && len(ingressRule.From) == 0 {
|
|
||||||
// From is empty, this rule matches no sources (no traffic matches).
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ingressRule.From == nil {
|
|
||||||
// From is not provided, this rule matches all sources (traffic not restricted by source).
|
|
||||||
if ingressRule.Ports == nil {
|
|
||||||
// Ports is not provided, this rule matches all ports (traffic not restricted by port).
|
|
||||||
rule := newRuleSpec(nil, nil, dstSelector, nil)
|
|
||||||
rules[rule.key] = rule
|
|
||||||
} else {
|
|
||||||
// Ports is present and contains at least one item, then this rule allows traffic
|
|
||||||
// only if the traffic matches at least one port in the ports list.
|
|
||||||
withNormalisedProtoAndPortLegacy(ingressRule.Ports, func(proto, port string) {
|
|
||||||
rule := newRuleSpec(&proto, nil, dstSelector, &port)
|
|
||||||
rules[rule.key] = rule
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// From is present and contains at least on item, this rule allows traffic only if the
|
|
||||||
// traffic matches at least one item in the from list.
|
|
||||||
for _, peer := range ingressRule.From {
|
|
||||||
var srcSelector *selectorSpec
|
|
||||||
if peer.PodSelector != nil {
|
|
||||||
srcSelector, err = newSelectorSpec(peer.PodSelector, 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, false, "", ipset.ListSet)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
nsSelectors[srcSelector.key] = srcSelector
|
|
||||||
}
|
|
||||||
|
|
||||||
if ingressRule.Ports == nil {
|
|
||||||
// Ports is not provided, this rule matches all ports (traffic not restricted by port).
|
|
||||||
rule := newRuleSpec(nil, srcSelector, dstSelector, nil)
|
|
||||||
rules[rule.key] = rule
|
|
||||||
} else {
|
|
||||||
// Ports is present and contains at least one item, then this rule allows traffic
|
|
||||||
// only if the traffic matches at least one port in the ports list.
|
|
||||||
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) (
|
func (ns *ns) analysePolicy(policy *networkingv1.NetworkPolicy) (
|
||||||
rules map[string]*ruleSpec,
|
rules map[string]*ruleSpec,
|
||||||
nsSelectors, podSelectors map[string]*selectorSpec,
|
nsSelectors, podSelectors, namespacedPodSelectors map[string]*selectorSpec,
|
||||||
|
ipBlocks map[string]*ipBlockSpec,
|
||||||
err error) {
|
err error) {
|
||||||
|
|
||||||
nsSelectors = make(map[string]*selectorSpec)
|
nsSelectors = make(map[string]*selectorSpec)
|
||||||
podSelectors = make(map[string]*selectorSpec)
|
podSelectors = make(map[string]*selectorSpec)
|
||||||
|
namespacedPodSelectors = make(map[string]*selectorSpec)
|
||||||
|
|
||||||
|
ipBlocks = make(map[string]*ipBlockSpec)
|
||||||
rules = make(map[string]*ruleSpec)
|
rules = make(map[string]*ruleSpec)
|
||||||
|
policyTypes := make([]policyType, 0)
|
||||||
|
|
||||||
|
for _, pt := range policy.Spec.PolicyTypes {
|
||||||
|
if pt == networkingv1.PolicyTypeIngress {
|
||||||
|
policyTypes = append(policyTypes, policyTypeIngress)
|
||||||
|
}
|
||||||
|
if pt == networkingv1.PolicyTypeEgress {
|
||||||
|
policyTypes = append(policyTypes, policyTypeEgress)
|
||||||
|
}
|
||||||
|
}
|
||||||
// If empty, matches all pods in a namespace
|
// If empty, matches all pods in a namespace
|
||||||
dstSelector, err := newSelectorSpec(&policy.Spec.PodSelector, true, ns.name, ipset.HashIP)
|
targetSelector, err := newSelectorSpec(&policy.Spec.PodSelector, nil, policyTypes, ns.name, ipset.HashIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// To prevent dstSelector being overwritten by a subsequent selector with
|
// To prevent targetSelector being overwritten by a subsequent selector with
|
||||||
// the same key, addIfNotExist MUST be used when adding to podSelectors.
|
// the same key, addIfNotExist MUST be used when adding to podSelectors.
|
||||||
// Otherwise, dstSelector' "dst" property might be lost resulting in
|
// Otherwise, policyTypes of the selector might be lost resulting in
|
||||||
// an invalid content of the "default-allow" ipset.
|
// an invalid content in any "default-allow" ipset.
|
||||||
addIfNotExist(dstSelector, podSelectors)
|
addIfNotExist(targetSelector, podSelectors)
|
||||||
|
|
||||||
// If ingress is empty then this NetworkPolicy does not allow any traffic
|
// If ingress is empty then this NetworkPolicy does not allow any ingress traffic
|
||||||
if policy.Spec.Ingress == nil || len(policy.Spec.Ingress) == 0 {
|
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
|
||||||
|
|
||||||
for _, ingressRule := range policy.Spec.Ingress {
|
if !allPorts {
|
||||||
// If Ports is empty or missing, this rule matches all ports
|
if err = checkForNamedPorts(ingressRule.Ports); err != nil {
|
||||||
allPorts := ingressRule.Ports == nil || len(ingressRule.Ports) == 0
|
return nil, nil, nil, nil, nil, fmt.Errorf("named ports in network policies is not supported yet. "+
|
||||||
// If From is empty or missing, this rule matches all sources
|
"Rejecting network policy: %s from further processing. "+err.Error(), policy.Name)
|
||||||
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
|
|
||||||
}
|
|
||||||
addIfNotExist(srcSelector, podSelectors)
|
|
||||||
|
|
||||||
} 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 allSources {
|
||||||
if allPorts {
|
if allPorts {
|
||||||
rule := newRuleSpec(nil, srcSelector, dstSelector, nil)
|
rule := newRuleSpec(policyTypeIngress, nil, nil, targetSelector, nil)
|
||||||
rules[rule.key] = rule
|
rules[rule.key] = rule
|
||||||
} else {
|
} else {
|
||||||
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
||||||
rule := newRuleSpec(&proto, srcSelector, dstSelector, &port)
|
rule := newRuleSpec(policyTypeIngress, &proto, nil, targetSelector, &port)
|
||||||
rules[rule.key] = rule
|
rules[rule.key] = rule
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for _, peer := range ingressRule.From {
|
||||||
|
var srcSelector *selectorSpec
|
||||||
|
var srcRuleHost ruleHost
|
||||||
|
|
||||||
|
// NetworkPolicyPeer describes a peer to allow traffic from.
|
||||||
|
if peer.PodSelector != nil && peer.NamespaceSelector != nil {
|
||||||
|
srcSelector, err = newSelectorSpec(peer.PodSelector, peer.NamespaceSelector, nil, "", ipset.HashIP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
addIfNotExist(srcSelector, namespacedPodSelectors)
|
||||||
|
srcRuleHost = srcSelector
|
||||||
|
} else if peer.PodSelector != nil {
|
||||||
|
srcSelector, err = newSelectorSpec(peer.PodSelector, nil, nil, ns.name, ipset.HashIP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
addIfNotExist(srcSelector, podSelectors)
|
||||||
|
srcRuleHost = srcSelector
|
||||||
|
} else if peer.NamespaceSelector != nil {
|
||||||
|
srcSelector, err = newSelectorSpec(nil, peer.NamespaceSelector, nil, "", ipset.ListSet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
nsSelectors[srcSelector.key] = srcSelector
|
||||||
|
srcRuleHost = srcSelector
|
||||||
|
} else if peer.IPBlock != nil {
|
||||||
|
ipBlock := newIPBlockSpec(peer.IPBlock, ns.name)
|
||||||
|
ipBlocks[ipBlock.key] = ipBlock
|
||||||
|
srcRuleHost = ipBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if allPorts {
|
||||||
|
rule := newRuleSpec(policyTypeIngress, nil, srcRuleHost, targetSelector, nil)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
} else {
|
||||||
|
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
||||||
|
rule := newRuleSpec(policyTypeIngress, &proto, srcRuleHost, targetSelector, &port)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rules, nsSelectors, podSelectors, nil
|
// If egress is empty then this NetworkPolicy does not allow any egress traffic
|
||||||
|
if policy.Spec.Egress != nil && len(policy.Spec.Egress) != 0 {
|
||||||
|
for _, egressRule := range policy.Spec.Egress {
|
||||||
|
// If Ports is empty or missing, this rule matches all ports
|
||||||
|
allPorts := egressRule.Ports == nil || len(egressRule.Ports) == 0
|
||||||
|
// If To is empty or missing, this rule matches all destinations
|
||||||
|
allDestinations := egressRule.To == nil || len(egressRule.To) == 0
|
||||||
|
|
||||||
|
if !allPorts {
|
||||||
|
if err = checkForNamedPorts(egressRule.Ports); err != nil {
|
||||||
|
return nil, nil, nil, nil, nil, fmt.Errorf("named ports in network policies is not supported yet. "+
|
||||||
|
"Rejecting network policy: %s from further processing. "+err.Error(), policy.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if allDestinations {
|
||||||
|
if allPorts {
|
||||||
|
rule := newRuleSpec(policyTypeEgress, nil, targetSelector, nil, nil)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
} else {
|
||||||
|
withNormalisedProtoAndPort(egressRule.Ports, func(proto, port string) {
|
||||||
|
rule := newRuleSpec(policyTypeEgress, &proto, targetSelector, nil, &port)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, peer := range egressRule.To {
|
||||||
|
var dstSelector *selectorSpec
|
||||||
|
var dstRuleHost ruleHost
|
||||||
|
|
||||||
|
// NetworkPolicyPeer describes a peer to allow traffic to.
|
||||||
|
if peer.PodSelector != nil && peer.NamespaceSelector != nil {
|
||||||
|
dstSelector, err = newSelectorSpec(peer.PodSelector, peer.NamespaceSelector, nil, "", ipset.HashIP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
addIfNotExist(dstSelector, namespacedPodSelectors)
|
||||||
|
dstRuleHost = dstSelector
|
||||||
|
} else if peer.PodSelector != nil {
|
||||||
|
dstSelector, err = newSelectorSpec(peer.PodSelector, nil, nil, ns.name, ipset.HashIP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
addIfNotExist(dstSelector, podSelectors)
|
||||||
|
dstRuleHost = dstSelector
|
||||||
|
|
||||||
|
} else if peer.NamespaceSelector != nil {
|
||||||
|
dstSelector, err = newSelectorSpec(nil, peer.NamespaceSelector, nil, "", ipset.ListSet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
nsSelectors[dstSelector.key] = dstSelector
|
||||||
|
dstRuleHost = dstSelector
|
||||||
|
} else if peer.IPBlock != nil {
|
||||||
|
ipBlock := newIPBlockSpec(peer.IPBlock, ns.name)
|
||||||
|
ipBlocks[ipBlock.key] = ipBlock
|
||||||
|
dstRuleHost = ipBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if allPorts {
|
||||||
|
rule := newRuleSpec(policyTypeEgress, nil, targetSelector, dstRuleHost, nil)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
} else {
|
||||||
|
withNormalisedProtoAndPort(egressRule.Ports, func(proto, port string) {
|
||||||
|
rule := newRuleSpec(policyTypeEgress, &proto, targetSelector, dstRuleHost, &port)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules, nsSelectors, podSelectors, namespacedPodSelectors, ipBlocks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addIfNotExist(s *selectorSpec, ss map[string]*selectorSpec) {
|
func addIfNotExist(s *selectorSpec, ss map[string]*selectorSpec) {
|
||||||
@@ -179,12 +196,6 @@ func addIfNotExist(s *selectorSpec, ss map[string]*selectorSpec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func withNormalisedProtoAndPortLegacy(npps []extnapi.NetworkPolicyPort, f func(proto, port string)) {
|
|
||||||
for _, npp := range npps {
|
|
||||||
f(proto(npp.Protocol), port(npp.Port))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func withNormalisedProtoAndPort(npps []networkingv1.NetworkPolicyPort, f func(proto, port string)) {
|
func withNormalisedProtoAndPort(npps []networkingv1.NetworkPolicyPort, f func(proto, port string)) {
|
||||||
for _, npp := range npps {
|
for _, npp := range npps {
|
||||||
f(proto(npp.Protocol), port(npp.Port))
|
f(proto(npp.Protocol), port(npp.Port))
|
||||||
@@ -216,3 +227,12 @@ func port(p *intstr.IntOrString) string {
|
|||||||
|
|
||||||
return port
|
return port
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkForNamedPorts(ports []networkingv1.NetworkPolicyPort) error {
|
||||||
|
for _, npProtocolPort := range ports {
|
||||||
|
if npProtocolPort.Port != nil && npProtocolPort.Port.Type == intstr.String {
|
||||||
|
return fmt.Errorf("named port %s in network policy", port(npProtocolPort.Port))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -7,6 +7,12 @@ const (
|
|||||||
DefaultChain = "WEAVE-NPC-DEFAULT"
|
DefaultChain = "WEAVE-NPC-DEFAULT"
|
||||||
IngressChain = "WEAVE-NPC-INGRESS"
|
IngressChain = "WEAVE-NPC-INGRESS"
|
||||||
|
|
||||||
|
EgressChain = "WEAVE-NPC-EGRESS"
|
||||||
|
EgressDefaultChain = "WEAVE-NPC-EGRESS-DEFAULT"
|
||||||
|
EgressCustomChain = "WEAVE-NPC-EGRESS-CUSTOM"
|
||||||
|
EgressMarkChain = "WEAVE-NPC-EGRESS-ACCEPT"
|
||||||
|
EgressMark = "0x40000/0x40000"
|
||||||
|
|
||||||
IpsetNamePrefix = "weave-"
|
IpsetNamePrefix = "weave-"
|
||||||
|
|
||||||
LocalIpset = IpsetNamePrefix + "local-pods"
|
LocalIpset = IpsetNamePrefix + "local-pods"
|
||||||
|
|||||||
+76
-19
@@ -1,15 +1,18 @@
|
|||||||
package npc
|
package npc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
coreapi "k8s.io/api/core/v1"
|
coreapi "k8s.io/api/core/v1"
|
||||||
extnapi "k8s.io/api/extensions/v1beta1"
|
extnapi "k8s.io/api/extensions/v1beta1"
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
"github.com/weaveworks/weave/common"
|
||||||
"github.com/weaveworks/weave/npc/ipset"
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
"github.com/weaveworks/weave/npc/iptables"
|
"github.com/weaveworks/weave/npc/iptables"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,33 +35,33 @@ type controller struct {
|
|||||||
|
|
||||||
nodeName string // my node name
|
nodeName string // my node name
|
||||||
|
|
||||||
ipt iptables.Interface
|
ipt iptables.Interface
|
||||||
ips ipset.Interface
|
ips ipset.Interface
|
||||||
|
clientset kubernetes.Interface
|
||||||
nss map[string]*ns // ns name -> ns struct
|
nss map[string]*ns // ns name -> ns struct
|
||||||
nsSelectors *selectorSet // selector string -> nsSelector
|
nsSelectors *selectorSet // selector string -> nsSelector
|
||||||
|
namespacedPodSelectors *selectorSet
|
||||||
legacy bool // use legacy network policy semantics (k8s pre-1.7)
|
defaultEgressDrop bool // flag to track if base iptable rule to drop egress traffic is added or not
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(nodeName string, legacy bool, ipt iptables.Interface, ips ipset.Interface) NetworkPolicyController {
|
func New(nodeName string, ipt iptables.Interface, ips ipset.Interface, clientset kubernetes.Interface) NetworkPolicyController {
|
||||||
c := &controller{
|
c := &controller{
|
||||||
nodeName: nodeName,
|
nodeName: nodeName,
|
||||||
legacy: legacy,
|
ipt: ipt,
|
||||||
ipt: ipt,
|
ips: ips,
|
||||||
ips: ips,
|
clientset: clientset,
|
||||||
nss: make(map[string]*ns)}
|
nss: make(map[string]*ns)}
|
||||||
|
|
||||||
doNothing := func(*selector) error { return nil }
|
doNothing := func(*selector, policyType) error { return nil }
|
||||||
c.nsSelectors = newSelectorSet(ips, c.onNewNsSelector, doNothing, doNothing)
|
c.nsSelectors = newSelectorSet(ips, c.onNewNsSelector, doNothing, doNothing)
|
||||||
|
c.namespacedPodSelectors = newSelectorSet(ips, c.onNewNamespacePodsSelector, doNothing, doNothing)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (npc *controller) onNewNsSelector(selector *selector) error {
|
func (npc *controller) onNewNsSelector(selector *selector) error {
|
||||||
for _, ns := range npc.nss {
|
for _, ns := range npc.nss {
|
||||||
if ns.namespace != nil {
|
if ns.namespace != nil {
|
||||||
if selector.matches(ns.namespace.ObjectMeta.Labels) {
|
if selector.matchesNamespaceSelector(ns.namespace.ObjectMeta.Labels) {
|
||||||
if err := selector.addEntry(ns.namespace.ObjectMeta.UID, string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
|
if err := selector.addEntry(ns.namespace.ObjectMeta.UID, string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -68,10 +71,32 @@ func (npc *controller) onNewNsSelector(selector *selector) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (npc *controller) onNewNamespacePodsSelector(selector *selector) error {
|
||||||
|
for _, ns := range npc.nss {
|
||||||
|
if ns.namespace != nil && len(ns.pods) > 0 {
|
||||||
|
for _, pod := range ns.pods {
|
||||||
|
if hasIP(pod) {
|
||||||
|
if selector.matchesNamespacedPodSelector(pod.ObjectMeta.Labels, ns.namespace.ObjectMeta.Labels) {
|
||||||
|
if err := selector.addEntry(pod.ObjectMeta.UID, pod.Status.PodIP, podComment(pod)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (npc *controller) withNS(name string, f func(ns *ns) error) error {
|
func (npc *controller) withNS(name string, f func(ns *ns) error) error {
|
||||||
ns, found := npc.nss[name]
|
ns, found := npc.nss[name]
|
||||||
if !found {
|
if !found {
|
||||||
newNs, err := newNS(name, npc.nodeName, npc.legacy, npc.ipt, npc.ips, npc.nsSelectors)
|
namespace, err := npc.clientset.CoreV1().Namespaces().Get(name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newNs, err := newNS(name, npc.nodeName, npc.ipt, npc.ips, npc.nsSelectors, npc.namespacedPodSelectors, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -124,11 +149,26 @@ func (npc *controller) AddNetworkPolicy(obj interface{}) error {
|
|||||||
npc.Lock()
|
npc.Lock()
|
||||||
defer npc.Unlock()
|
defer npc.Unlock()
|
||||||
|
|
||||||
|
// lazily add default rule to drop egress traffic only when network policies are applied
|
||||||
|
if !npc.defaultEgressDrop {
|
||||||
|
egressNetworkPolicy, err := isEgressNetworkPolicy(obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if egressNetworkPolicy {
|
||||||
|
npc.defaultEgressDrop = true
|
||||||
|
if err := npc.ipt.Append(TableFilter, EgressChain,
|
||||||
|
"-m", "mark", "!", "--mark", EgressMark, "-j", "DROP"); err != nil {
|
||||||
|
npc.defaultEgressDrop = false
|
||||||
|
return fmt.Errorf("Failed to add iptable rule to drop egress traffic from the pods by default due to %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nsName, err := nsName(obj)
|
nsName, err := nsName(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
common.Log.Infof("EVENT AddNetworkPolicy %s", js(obj))
|
common.Log.Infof("EVENT AddNetworkPolicy %s", js(obj))
|
||||||
return npc.withNS(nsName, func(ns *ns) error {
|
return npc.withNS(nsName, func(ns *ns) error {
|
||||||
return errors.Wrap(ns.addNetworkPolicy(obj), "add network policy")
|
return errors.Wrap(ns.addNetworkPolicy(obj), "add network policy")
|
||||||
@@ -205,3 +245,20 @@ func nsName(obj interface{}) (string, error) {
|
|||||||
|
|
||||||
return "", errInvalidNetworkPolicyObjType
|
return "", errInvalidNetworkPolicyObjType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isEgressNetworkPolicy(obj interface{}) (bool, error) {
|
||||||
|
if policy, ok := obj.(*networkingv1.NetworkPolicy); ok {
|
||||||
|
if len(policy.Spec.PolicyTypes) > 0 {
|
||||||
|
for _, policyType := range policy.Spec.PolicyTypes {
|
||||||
|
if policyType == networkingv1.PolicyTypeEgress {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if policy.Spec.Egress != nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, errInvalidNetworkPolicyObjType
|
||||||
|
}
|
||||||
+281
-45
@@ -1,17 +1,20 @@
|
|||||||
package npc
|
package npc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/weaveworks/weave/npc/ipset"
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
coreapi "k8s.io/api/core/v1"
|
coreapi "k8s.io/api/core/v1"
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockSet struct {
|
type mockSet struct {
|
||||||
@@ -103,18 +106,46 @@ func (i *mockIPSet) List(prefix string) ([]ipset.Name, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mockIPTables struct {
|
type mockIPTables struct {
|
||||||
|
rules map[string]map[string]struct{} // chain -> rulespec -> struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockIPTables() *mockIPTables {
|
||||||
|
return &mockIPTables{rules: make(map[string]map[string]struct{})}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ipt *mockIPTables) Append(table, chain string, rulespec ...string) error {
|
func (ipt *mockIPTables) Append(table, chain string, rulespec ...string) error {
|
||||||
|
if table != TableFilter {
|
||||||
|
return fmt.Errorf("invalid table: %q", table)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found := ipt.rules[chain]; !found {
|
||||||
|
ipt.rules[chain] = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
rule := strings.Join(rulespec, " ")
|
||||||
|
if _, found := ipt.rules[chain][rule]; found {
|
||||||
|
return fmt.Errorf("rule already exists. chain: %q, rule: %q", chain, rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipt.rules[chain][rule] = struct{}{}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ipt *mockIPTables) Delete(table, chain string, rulespec ...string) error {
|
func (ipt *mockIPTables) Delete(table, chain string, rulespec ...string) error {
|
||||||
|
rule := strings.Join(rulespec, " ")
|
||||||
|
|
||||||
|
if _, found := ipt.rules[chain][rule]; !found {
|
||||||
|
return fmt.Errorf("rule does not exist. chain: %q rule: %q", chain, rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(ipt.rules[chain], rule)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ipt *mockIPTables) Insert(table, chain string, pos int, rulespec ...string) error {
|
func (ipt *mockIPTables) Insert(table, chain string, pos int, rulespec ...string) error {
|
||||||
return nil
|
return errors.New("Not Implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
||||||
@@ -144,6 +175,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
|||||||
Namespace: "destination",
|
Namespace: "destination",
|
||||||
},
|
},
|
||||||
Spec: networkingv1.NetworkPolicySpec{
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||||
Ingress: []networkingv1.NetworkPolicyIngressRule{
|
Ingress: []networkingv1.NetworkPolicyIngressRule{
|
||||||
{
|
{
|
||||||
From: []networkingv1.NetworkPolicyPeer{
|
From: []networkingv1.NetworkPolicyPeer{
|
||||||
@@ -167,7 +199,10 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
|||||||
|
|
||||||
// Namespaces first
|
// Namespaces first
|
||||||
m := newMockIPSet()
|
m := newMockIPSet()
|
||||||
controller := New("foo", false, &mockIPTables{}, &m)
|
client := fake.NewSimpleClientset()
|
||||||
|
controller := New("foo", newMockIPTables(), &m, client)
|
||||||
|
client.CoreV1().Namespaces().Create(sourceNamespace)
|
||||||
|
client.CoreV1().Namespaces().Create(destinationNamespace)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
selectorIPSetName = "weave-I239Zp%sCvoVt*D6u=A!2]YEk"
|
selectorIPSetName = "weave-I239Zp%sCvoVt*D6u=A!2]YEk"
|
||||||
@@ -183,7 +218,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
|||||||
|
|
||||||
// NetworkPolicy first
|
// NetworkPolicy first
|
||||||
m = newMockIPSet()
|
m = newMockIPSet()
|
||||||
controller = New("foo", false, &mockIPTables{}, &m)
|
controller = New("foo", newMockIPTables(), &m, &fake.Clientset{})
|
||||||
|
|
||||||
controller.AddNetworkPolicy(networkPolicy)
|
controller.AddNetworkPolicy(networkPolicy)
|
||||||
|
|
||||||
@@ -193,17 +228,18 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
|||||||
require.Equal(t, true, len(m.sets[selectorIPSetName].subSets[sourceIPSetName]) > 0)
|
require.Equal(t, true, len(m.sets[selectorIPSetName].subSets[sourceIPSetName]) > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests default-allow ipset behavior when running in non-legacy mode.
|
// Tests default-allow ipset behavior
|
||||||
func TestDefaultAllow(t *testing.T) {
|
func TestDefaultAllow(t *testing.T) {
|
||||||
const (
|
const (
|
||||||
defaultAllowIPSetName = "weave-E.1.0W^NGSp]0_t5WwH/]gX@L"
|
ingressDefaultAllowIPSetName = "weave-;rGqyMIl1HN^cfDki~Z$3]6!N"
|
||||||
fooPodIP = "10.32.0.10"
|
egressDefaultAllowIPSetName = "weave-s_+ChJId4Uy_$}G;WdH|~TK)I"
|
||||||
barPodIP = "10.32.0.11"
|
fooPodIP = "10.32.0.10"
|
||||||
barPodNewIP = "10.32.0.12"
|
barPodIP = "10.32.0.11"
|
||||||
|
barPodNewIP = "10.32.0.12"
|
||||||
)
|
)
|
||||||
|
|
||||||
m := newMockIPSet()
|
m := newMockIPSet()
|
||||||
controller := New("bar", false, &mockIPTables{}, &m)
|
controller := New("bar", newMockIPTables(), &m, &fake.Clientset{})
|
||||||
|
|
||||||
defaultNamespace := &coreapi.Namespace{
|
defaultNamespace := &coreapi.Namespace{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -213,7 +249,8 @@ func TestDefaultAllow(t *testing.T) {
|
|||||||
controller.AddNamespace(defaultNamespace)
|
controller.AddNamespace(defaultNamespace)
|
||||||
|
|
||||||
// Should create an ipset for default-allow
|
// Should create an ipset for default-allow
|
||||||
require.Contains(t, m.sets, defaultAllowIPSetName)
|
require.Contains(t, m.sets, ingressDefaultAllowIPSetName)
|
||||||
|
require.Contains(t, m.sets, egressDefaultAllowIPSetName)
|
||||||
|
|
||||||
podFoo := &coreapi.Pod{
|
podFoo := &coreapi.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -225,7 +262,8 @@ func TestDefaultAllow(t *testing.T) {
|
|||||||
controller.AddPod(podFoo)
|
controller.AddPod(podFoo)
|
||||||
|
|
||||||
// Should add the foo pod to default-allow
|
// Should add the foo pod to default-allow
|
||||||
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
require.True(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
podBar := &coreapi.Pod{
|
podBar := &coreapi.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -236,10 +274,12 @@ func TestDefaultAllow(t *testing.T) {
|
|||||||
Status: coreapi.PodStatus{PodIP: barPodIP}}
|
Status: coreapi.PodStatus{PodIP: barPodIP}}
|
||||||
podBarNoIP := &coreapi.Pod{ObjectMeta: podBar.ObjectMeta}
|
podBarNoIP := &coreapi.Pod{ObjectMeta: podBar.ObjectMeta}
|
||||||
controller.AddPod(podBarNoIP)
|
controller.AddPod(podBarNoIP)
|
||||||
|
|
||||||
controller.UpdatePod(podBarNoIP, podBar)
|
controller.UpdatePod(podBarNoIP, podBar)
|
||||||
|
|
||||||
// Should add the bar pod to default-allow
|
// Should add the bar pod to default-allow
|
||||||
require.True(t, m.entryExists(defaultAllowIPSetName, barPodIP))
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, barPodIP))
|
||||||
|
require.True(t, m.entryExists(egressDefaultAllowIPSetName, barPodIP))
|
||||||
|
|
||||||
// Allow access from the bar pod to the foo pod
|
// Allow access from the bar pod to the foo pod
|
||||||
netpol := &networkingv1.NetworkPolicy{
|
netpol := &networkingv1.NetworkPolicy{
|
||||||
@@ -248,6 +288,10 @@ func TestDefaultAllow(t *testing.T) {
|
|||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
},
|
},
|
||||||
Spec: networkingv1.NetworkPolicySpec{
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{
|
||||||
|
networkingv1.PolicyTypeIngress,
|
||||||
|
networkingv1.PolicyTypeEgress,
|
||||||
|
},
|
||||||
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "foo"}},
|
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "foo"}},
|
||||||
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||||
From: []networkingv1.NetworkPolicyPeer{{
|
From: []networkingv1.NetworkPolicyPeer{{
|
||||||
@@ -256,66 +300,82 @@ func TestDefaultAllow(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
|
Egress: []networkingv1.NetworkPolicyEgressRule{{
|
||||||
|
To: []networkingv1.NetworkPolicyPeer{{
|
||||||
|
IPBlock: &networkingv1.IPBlock{CIDR: "192.168.48.0/24"},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
controller.AddNetworkPolicy(netpol)
|
controller.AddNetworkPolicy(netpol)
|
||||||
|
|
||||||
// Should remove the foo pod from default-allow as the netpol selects it
|
// Should remove the foo pod from default-allow as the netpol selects it
|
||||||
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||||
require.True(t, m.entryExists(defaultAllowIPSetName, barPodIP))
|
require.False(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, barPodIP))
|
||||||
|
require.True(t, m.entryExists(egressDefaultAllowIPSetName, barPodIP))
|
||||||
|
|
||||||
podBarWithNewIP := *podBar
|
podBarWithNewIP := *podBar
|
||||||
podBarWithNewIP.Status.PodIP = barPodNewIP
|
podBarWithNewIP.Status.PodIP = barPodNewIP
|
||||||
controller.UpdatePod(podBar, &podBarWithNewIP)
|
controller.UpdatePod(podBar, &podBarWithNewIP)
|
||||||
|
|
||||||
// Should update IP addr of the bar pod in default-allow
|
// Should update IP addr of the bar pod in default-allow
|
||||||
require.False(t, m.entryExists(defaultAllowIPSetName, barPodIP))
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, barPodIP))
|
||||||
require.True(t, m.entryExists(defaultAllowIPSetName, barPodNewIP))
|
require.False(t, m.entryExists(egressDefaultAllowIPSetName, barPodIP))
|
||||||
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, barPodNewIP))
|
||||||
|
require.True(t, m.entryExists(egressDefaultAllowIPSetName, barPodNewIP))
|
||||||
|
|
||||||
controller.UpdatePod(&podBarWithNewIP, podBarNoIP)
|
controller.UpdatePod(&podBarWithNewIP, podBarNoIP)
|
||||||
// Should remove the bar pod from default-allow as it does not have any IP addr
|
// Should remove the bar pod from default-allow as it does not have any IP addr
|
||||||
require.False(t, m.entryExists(defaultAllowIPSetName, barPodNewIP))
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, barPodNewIP))
|
||||||
|
require.False(t, m.entryExists(egressDefaultAllowIPSetName, barPodNewIP))
|
||||||
|
|
||||||
podFooWithNewLabel := *podFoo
|
podFooWithNewLabel := *podFoo
|
||||||
podFooWithNewLabel.ObjectMeta.Labels = map[string]string{"run": "new-foo"}
|
podFooWithNewLabel.ObjectMeta.Labels = map[string]string{"run": "new-foo"}
|
||||||
controller.UpdatePod(podFoo, &podFooWithNewLabel)
|
controller.UpdatePod(podFoo, &podFooWithNewLabel)
|
||||||
|
|
||||||
// Should bring back the foo pod to default-allow as it does not match dst of any netpol
|
// Should bring back the foo pod to default-allow as it does not match dst of any netpol
|
||||||
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
require.True(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
controller.UpdatePod(&podFooWithNewLabel, podFoo)
|
controller.UpdatePod(&podFooWithNewLabel, podFoo)
|
||||||
// Should remove from default-allow as it matches the netpol after the update
|
// Should remove from default-allow as it matches the netpol after the update
|
||||||
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
require.False(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
controller.DeleteNetworkPolicy(netpol)
|
controller.DeleteNetworkPolicy(netpol)
|
||||||
// Should bring back the foo pod to default-allow as no netpol selects it
|
// Should bring back the foo pod to default-allow as no netpol selects it
|
||||||
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
require.True(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
controller.DeletePod(podFoo)
|
controller.DeletePod(podFoo)
|
||||||
// Should remove foo pod from default-allow
|
// Should remove foo pod from default-allow
|
||||||
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
require.False(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
controller.DeleteNamespace(defaultNamespace)
|
controller.DeleteNamespace(defaultNamespace)
|
||||||
// Should remove default ipset
|
// Should remove default ipset
|
||||||
require.NotContains(t, m.sets, defaultAllowIPSetName)
|
require.NotContains(t, m.sets, ingressDefaultAllowIPSetName)
|
||||||
|
require.NotContains(t, m.sets, egressDefaultAllowIPSetName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOutOfOrderPodEvents(t *testing.T) {
|
func TestOutOfOrderPodEvents(t *testing.T) {
|
||||||
const (
|
const (
|
||||||
defaultAllowIPSetName = "weave-E.1.0W^NGSp]0_t5WwH/]gX@L"
|
ingressDefaultAllowIPSetName = "weave-;rGqyMIl1HN^cfDki~Z$3]6!N"
|
||||||
runBarIPSetName = "weave-bZ~x=yBgzH)Ht()K*Uv3z{M]Y"
|
runBarIPSetName = "weave-bZ~x=yBgzH)Ht()K*Uv3z{M]Y"
|
||||||
podIP = "10.32.0.10"
|
podIP = "10.32.0.10"
|
||||||
)
|
)
|
||||||
|
|
||||||
m := newMockIPSet()
|
m := newMockIPSet()
|
||||||
controller := New("qux", false, &mockIPTables{}, &m)
|
client := fake.NewSimpleClientset()
|
||||||
|
controller := New("qux", newMockIPTables(), &m, client)
|
||||||
|
|
||||||
defaultNamespace := &coreapi.Namespace{
|
defaultNamespace := &coreapi.Namespace{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "default",
|
Name: "default",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
client.CoreV1().Namespaces().Create(defaultNamespace)
|
||||||
controller.AddNamespace(defaultNamespace)
|
controller.AddNamespace(defaultNamespace)
|
||||||
|
|
||||||
podFoo := &coreapi.Pod{
|
podFoo := &coreapi.Pod{
|
||||||
@@ -328,7 +388,7 @@ func TestOutOfOrderPodEvents(t *testing.T) {
|
|||||||
controller.AddPod(podFoo)
|
controller.AddPod(podFoo)
|
||||||
|
|
||||||
// Should be in default-allow as no netpol selects podFoo
|
// Should be in default-allow as no netpol selects podFoo
|
||||||
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
|
||||||
netpol := &networkingv1.NetworkPolicy{
|
netpol := &networkingv1.NetworkPolicy{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -336,6 +396,7 @@ func TestOutOfOrderPodEvents(t *testing.T) {
|
|||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
},
|
},
|
||||||
Spec: networkingv1.NetworkPolicySpec{
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||||
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "foo"}},
|
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "foo"}},
|
||||||
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||||
From: []networkingv1.NetworkPolicyPeer{{
|
From: []networkingv1.NetworkPolicyPeer{{
|
||||||
@@ -349,7 +410,7 @@ func TestOutOfOrderPodEvents(t *testing.T) {
|
|||||||
controller.AddNetworkPolicy(netpol)
|
controller.AddNetworkPolicy(netpol)
|
||||||
|
|
||||||
// Shouldn't be in default-allow as netpol above selects podFoo
|
// Shouldn't be in default-allow as netpol above selects podFoo
|
||||||
require.False(t, m.entryExists(defaultAllowIPSetName, podIP))
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
|
||||||
podBar := &coreapi.Pod{
|
podBar := &coreapi.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -361,8 +422,8 @@ func TestOutOfOrderPodEvents(t *testing.T) {
|
|||||||
controller.AddPod(podBar)
|
controller.AddPod(podBar)
|
||||||
|
|
||||||
// Should be in default-allow as no netpol selects podBar
|
// Should be in default-allow as no netpol selects podBar
|
||||||
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
require.True(t, m.Exist(podBar.ObjectMeta.UID, defaultAllowIPSetName, podIP))
|
require.True(t, m.Exist(podBar.ObjectMeta.UID, ingressDefaultAllowIPSetName, podIP))
|
||||||
// Should be in run=bar ipset
|
// Should be in run=bar ipset
|
||||||
require.True(t, m.entryExists(runBarIPSetName, podIP))
|
require.True(t, m.entryExists(runBarIPSetName, podIP))
|
||||||
|
|
||||||
@@ -373,30 +434,32 @@ func TestOutOfOrderPodEvents(t *testing.T) {
|
|||||||
|
|
||||||
// Should be in default-allow as no netpol selects podBar and podFoo removal
|
// Should be in default-allow as no netpol selects podBar and podFoo removal
|
||||||
// should not affect podBar in default-allow
|
// should not affect podBar in default-allow
|
||||||
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
|
||||||
controller.DeletePod(podBar)
|
controller.DeletePod(podBar)
|
||||||
|
|
||||||
// Should remove from default-allow and run=bar ipsets
|
// Should remove from default-allow and run=bar ipsets
|
||||||
require.Equal(t, 0, len(m.sets[defaultAllowIPSetName].subSets))
|
require.Equal(t, 0, len(m.sets[ingressDefaultAllowIPSetName].subSets))
|
||||||
require.False(t, m.entryExists(runBarIPSetName, podIP))
|
require.False(t, m.entryExists(runBarIPSetName, podIP))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test case for https://github.com/weaveworks/weave/issues/3222
|
// Test case for https://github.com/weaveworks/weave/issues/3222
|
||||||
func TestNewDstSelector(t *testing.T) {
|
func TestNewTargetSelector(t *testing.T) {
|
||||||
const (
|
const (
|
||||||
defaultAllowIPSetName = "weave-E.1.0W^NGSp]0_t5WwH/]gX@L"
|
ingressDefaultAllowIPSetName = "weave-;rGqyMIl1HN^cfDki~Z$3]6!N"
|
||||||
podIP = "10.32.0.10"
|
podIP = "10.32.0.10"
|
||||||
)
|
)
|
||||||
|
|
||||||
m := newMockIPSet()
|
m := newMockIPSet()
|
||||||
controller := New("baz", false, &mockIPTables{}, &m)
|
client := fake.NewSimpleClientset()
|
||||||
|
controller := New("baz", newMockIPTables(), &m, client)
|
||||||
|
|
||||||
defaultNamespace := &coreapi.Namespace{
|
defaultNamespace := &coreapi.Namespace{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "default",
|
Name: "default",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
client.CoreV1().Namespaces().Create(defaultNamespace)
|
||||||
controller.AddNamespace(defaultNamespace)
|
controller.AddNamespace(defaultNamespace)
|
||||||
|
|
||||||
podFoo := &coreapi.Pod{
|
podFoo := &coreapi.Pod{
|
||||||
@@ -408,7 +471,7 @@ func TestNewDstSelector(t *testing.T) {
|
|||||||
Status: coreapi.PodStatus{PodIP: podIP}}
|
Status: coreapi.PodStatus{PodIP: podIP}}
|
||||||
controller.AddPod(podFoo)
|
controller.AddPod(podFoo)
|
||||||
|
|
||||||
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
|
||||||
netpolBar := &networkingv1.NetworkPolicy{
|
netpolBar := &networkingv1.NetworkPolicy{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -417,6 +480,7 @@ func TestNewDstSelector(t *testing.T) {
|
|||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
},
|
},
|
||||||
Spec: networkingv1.NetworkPolicySpec{
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||||
PodSelector: metav1.LabelSelector{},
|
PodSelector: metav1.LabelSelector{},
|
||||||
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||||
From: []networkingv1.NetworkPolicyPeer{{
|
From: []networkingv1.NetworkPolicyPeer{{
|
||||||
@@ -427,8 +491,8 @@ func TestNewDstSelector(t *testing.T) {
|
|||||||
}
|
}
|
||||||
controller.AddNetworkPolicy(netpolBar)
|
controller.AddNetworkPolicy(netpolBar)
|
||||||
|
|
||||||
// netpolBar dst selector selects podFoo
|
// netpolBar target selector selects podFoo
|
||||||
require.False(t, m.entryExists(defaultAllowIPSetName, podIP))
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
|
||||||
netpolFoo := &networkingv1.NetworkPolicy{
|
netpolFoo := &networkingv1.NetworkPolicy{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -437,6 +501,7 @@ func TestNewDstSelector(t *testing.T) {
|
|||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
},
|
},
|
||||||
Spec: networkingv1.NetworkPolicySpec{
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||||
PodSelector: metav1.LabelSelector{},
|
PodSelector: metav1.LabelSelector{},
|
||||||
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||||
From: []networkingv1.NetworkPolicyPeer{{
|
From: []networkingv1.NetworkPolicyPeer{{
|
||||||
@@ -448,9 +513,180 @@ func TestNewDstSelector(t *testing.T) {
|
|||||||
controller.AddNetworkPolicy(netpolFoo)
|
controller.AddNetworkPolicy(netpolFoo)
|
||||||
|
|
||||||
controller.DeleteNetworkPolicy(netpolBar)
|
controller.DeleteNetworkPolicy(netpolBar)
|
||||||
// netpolFoo dst-selects podFoo
|
// netpolFoo target-selects podFoo
|
||||||
require.False(t, m.entryExists(defaultAllowIPSetName, podIP))
|
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
controller.DeleteNetworkPolicy(netpolFoo)
|
controller.DeleteNetworkPolicy(netpolFoo)
|
||||||
// No netpol dst-selects podFoo
|
// No netpol target-selects podFoo
|
||||||
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
|
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEgressPolicyWithIPBlock(t *testing.T) {
|
||||||
|
const (
|
||||||
|
fooPodIP = "10.32.0.10"
|
||||||
|
exceptIPSetName = "weave-j:W$5om!$8JS})buYAD7q^#sX"
|
||||||
|
exceptIPSetNameInNonDefault = "weave-|*P{aL2C@#N/1IZ!IvQItv_pt"
|
||||||
|
)
|
||||||
|
|
||||||
|
m := newMockIPSet()
|
||||||
|
ipt := newMockIPTables()
|
||||||
|
client := fake.NewSimpleClientset()
|
||||||
|
controller := New("foo", ipt, &m, client)
|
||||||
|
|
||||||
|
defaultNamespace := &coreapi.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nonDefaultNamespace := &coreapi.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "non-default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
client.CoreV1().Namespaces().Create(defaultNamespace)
|
||||||
|
client.CoreV1().Namespaces().Create(nonDefaultNamespace)
|
||||||
|
|
||||||
|
netpolFoo := &networkingv1.NetworkPolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "netpol-foo",
|
||||||
|
Name: "allow-from-bar-to-foo",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{
|
||||||
|
networkingv1.PolicyTypeEgress,
|
||||||
|
},
|
||||||
|
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "bar"}},
|
||||||
|
Egress: []networkingv1.NetworkPolicyEgressRule{{
|
||||||
|
To: []networkingv1.NetworkPolicyPeer{{
|
||||||
|
IPBlock: &networkingv1.IPBlock{
|
||||||
|
CIDR: "192.168.48.0/24",
|
||||||
|
Except: []string{
|
||||||
|
"192.168.48.1/32",
|
||||||
|
"192.168.48.2/32",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := controller.AddNetworkPolicy(netpolFoo)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, 2, len(m.sets[exceptIPSetName].subSets))
|
||||||
|
require.True(t, m.entryExists(exceptIPSetName, "192.168.48.1/32"))
|
||||||
|
require.True(t, m.entryExists(exceptIPSetName, "192.168.48.2/32"))
|
||||||
|
|
||||||
|
// Each egress rule is represented as two iptables rules (-J MARK and -J RETURN).
|
||||||
|
require.Equal(t, 2, len(ipt.rules[EgressCustomChain]))
|
||||||
|
for rule := range ipt.rules[EgressCustomChain] {
|
||||||
|
require.Contains(t, rule, "-d 192.168.48.0/24 -m set ! --match-set "+exceptIPSetName+" dst")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we create a new ipset for the ipBlock bellow. An ipset with the
|
||||||
|
// same content already exists (created by netpolFoo), but we need to create
|
||||||
|
// a new one, as netpolBar is in a different namespace and weave-npc does
|
||||||
|
// object refcounting only within namespace boundaries.
|
||||||
|
netpolBar := &networkingv1.NetworkPolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "netpol-foo-bar",
|
||||||
|
Name: "allow-from-bar-to-foo-in-non-default",
|
||||||
|
Namespace: "non-default",
|
||||||
|
},
|
||||||
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{
|
||||||
|
networkingv1.PolicyTypeEgress,
|
||||||
|
},
|
||||||
|
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "bar"}},
|
||||||
|
Egress: []networkingv1.NetworkPolicyEgressRule{{
|
||||||
|
To: []networkingv1.NetworkPolicyPeer{{
|
||||||
|
IPBlock: &networkingv1.IPBlock{
|
||||||
|
CIDR: "192.168.48.0/24",
|
||||||
|
Except: []string{
|
||||||
|
"192.168.48.1/32",
|
||||||
|
"192.168.48.2/32",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err = controller.AddNetworkPolicy(netpolBar)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(m.sets[exceptIPSetNameInNonDefault].subSets))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case for https://github.com/weaveworks/weave/issues/3653
|
||||||
|
func TestIngressPolicyWithIPBlockAndPortSpecified(t *testing.T) {
|
||||||
|
const (
|
||||||
|
barPodIP = "10.32.0.11"
|
||||||
|
runBarIPSetName = "weave-bZ~x=yBgzH)Ht()K*Uv3z{M]Y"
|
||||||
|
)
|
||||||
|
|
||||||
|
m := newMockIPSet()
|
||||||
|
ipt := newMockIPTables()
|
||||||
|
client := fake.NewSimpleClientset()
|
||||||
|
controller := New("any", ipt, &m, client)
|
||||||
|
|
||||||
|
defaultNamespace := &coreapi.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
client.CoreV1().Namespaces().Create(defaultNamespace)
|
||||||
|
|
||||||
|
podBar := &coreapi.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "bar",
|
||||||
|
Namespace: "default",
|
||||||
|
Name: "bar",
|
||||||
|
Labels: map[string]string{"run": "bar"}},
|
||||||
|
Status: coreapi.PodStatus{PodIP: barPodIP}}
|
||||||
|
controller.AddPod(podBar)
|
||||||
|
defer controller.DeletePod(podBar)
|
||||||
|
|
||||||
|
portProtocol := coreapi.ProtocolTCP
|
||||||
|
port := intstr.FromInt(80)
|
||||||
|
netpolicty := &networkingv1.NetworkPolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "ipblock-bar",
|
||||||
|
Name: "allow-ipblock-to-bar",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{
|
||||||
|
networkingv1.PolicyTypeIngress,
|
||||||
|
},
|
||||||
|
PodSelector: metav1.LabelSelector{MatchLabels: podBar.Labels},
|
||||||
|
Ingress: []networkingv1.NetworkPolicyIngressRule{
|
||||||
|
{
|
||||||
|
Ports: []networkingv1.NetworkPolicyPort{
|
||||||
|
{
|
||||||
|
Protocol: &portProtocol,
|
||||||
|
Port: &port,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
From: []networkingv1.NetworkPolicyPeer{
|
||||||
|
{
|
||||||
|
IPBlock: &networkingv1.IPBlock{
|
||||||
|
CIDR: "192.168.48.4/32",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := controller.AddNetworkPolicy(netpolicty)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer controller.DeleteNetworkPolicy(netpolicty)
|
||||||
|
|
||||||
|
require.Equal(t, 1, len(m.sets[runBarIPSetName].subSets))
|
||||||
|
require.True(t, m.entryExists(runBarIPSetName, barPodIP))
|
||||||
|
|
||||||
|
require.Equal(t, 1, len(ipt.rules[IngressChain]))
|
||||||
|
for rule := range ipt.rules[IngressChain] {
|
||||||
|
require.Contains(t, rule, "-s 192.168.48.4/32 -m set --match-set "+runBarIPSetName+" dst --dport 80")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
package npc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/common"
|
||||||
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ipBlockSpec struct {
|
||||||
|
key string
|
||||||
|
ipsetName ipset.Name // ipset for storing excepted CIDRs
|
||||||
|
ipBlock *networkingv1.IPBlock
|
||||||
|
nsName string // Namespace name
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIPBlockSpec(ipb *networkingv1.IPBlock, nsName string) *ipBlockSpec {
|
||||||
|
spec := &ipBlockSpec{ipBlock: ipb, nsName: nsName}
|
||||||
|
|
||||||
|
if len(ipb.Except) > 0 {
|
||||||
|
sort.Strings(ipb.Except)
|
||||||
|
spec.key = strings.Join(ipb.Except, " ")
|
||||||
|
spec.ipsetName = ipset.Name(IpsetNamePrefix + shortName(nsName+":"+"ipblock-except:"+spec.key))
|
||||||
|
}
|
||||||
|
|
||||||
|
return spec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spec *ipBlockSpec) getRuleSpec(src bool) ([]string, string) {
|
||||||
|
dir, dirOpt := "dst", "-d"
|
||||||
|
if src {
|
||||||
|
dir, dirOpt = "src", "-s"
|
||||||
|
}
|
||||||
|
|
||||||
|
if spec.key == "" {
|
||||||
|
rule := []string{dirOpt, spec.ipBlock.CIDR}
|
||||||
|
comment := fmt.Sprintf("cidr: %s", spec.ipBlock.CIDR)
|
||||||
|
return rule, comment
|
||||||
|
}
|
||||||
|
|
||||||
|
rule := []string{dirOpt, spec.ipBlock.CIDR,
|
||||||
|
"-m", "set", "!", "--match-set", string(spec.ipsetName), dir}
|
||||||
|
comment := fmt.Sprintf("cidr: %s except [%s]", spec.ipBlock.CIDR, spec.key)
|
||||||
|
return rule, comment
|
||||||
|
}
|
||||||
|
|
||||||
|
type ipBlockSet struct {
|
||||||
|
ips ipset.Interface
|
||||||
|
users map[string]map[types.UID]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIPBlockSet(ips ipset.Interface) *ipBlockSet {
|
||||||
|
return &ipBlockSet{
|
||||||
|
ips: ips,
|
||||||
|
users: make(map[string]map[types.UID]struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ipBlockSet) deprovision(user types.UID, current, desired map[string]*ipBlockSpec) error {
|
||||||
|
for key, spec := range current {
|
||||||
|
if key == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found := desired[key]; !found {
|
||||||
|
delete(s.users[key], user)
|
||||||
|
if len(s.users[key]) == 0 {
|
||||||
|
common.Log.Infof("destroying ipset: %#v", spec)
|
||||||
|
if err := s.ips.Destroy(spec.ipsetName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(s.users, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ipBlockSet) provision(user types.UID, current, desired map[string]*ipBlockSpec) (err error) {
|
||||||
|
for key, spec := range desired {
|
||||||
|
if key == "" {
|
||||||
|
// No need to provision an ipBlock with empty list of excepted CIDRs
|
||||||
|
// (i.e. no ipset is needed for the related iptables rule).
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found := current[key]; !found {
|
||||||
|
if _, found := s.users[key]; !found {
|
||||||
|
common.Log.Infof("creating ipset: %#v", spec)
|
||||||
|
if err := s.ips.Create(spec.ipsetName, ipset.HashNet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(brb) Pass comment to ips.Create instead.
|
||||||
|
comment := "excepted from " + spec.ipBlock.CIDR
|
||||||
|
for _, cidr := range spec.ipBlock.Except {
|
||||||
|
if err = s.ips.AddEntry(user, spec.ipsetName, cidr, comment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.users[key] = make(map[types.UID]struct{})
|
||||||
|
}
|
||||||
|
s.users[key][user] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+253
-199
@@ -1,7 +1,6 @@
|
|||||||
package npc
|
package npc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@@ -13,7 +12,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/uuid"
|
"k8s.io/apimachinery/pkg/util/uuid"
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
"github.com/weaveworks/weave/common"
|
||||||
"github.com/weaveworks/weave/npc/ipset"
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
"github.com/weaveworks/weave/npc/iptables"
|
"github.com/weaveworks/weave/npc/iptables"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,47 +31,55 @@ type ns struct {
|
|||||||
uid types.UID // surrogate UID to own allPods selector
|
uid types.UID // surrogate UID to own allPods selector
|
||||||
allPods *selectorSpec // hash:ip ipset of all pod IPs in this namespace
|
allPods *selectorSpec // hash:ip ipset of all pod IPs in this namespace
|
||||||
|
|
||||||
// stores IP addrs of pods which are not selected by any dst podSelector of
|
// stores IP addrs of pods which are not selected by any target podSelector of
|
||||||
// any netpol; used only in non-legacy mode and is used as a dst in
|
// any netpol; used as a target in the WEAVE-NPC-{INGRESS,EGRESS}-DEFAULT
|
||||||
// the WEAVE-NPC-DEFAULT iptables chain.
|
// iptables chains.
|
||||||
defaultAllowIPSet ipset.Name
|
ingressDefaultAllowIPSet ipset.Name
|
||||||
|
egressDefaultAllowIPSet ipset.Name
|
||||||
|
|
||||||
nsSelectors *selectorSet
|
nsSelectors *selectorSet // reference to global selectorSet that is shared across the `ns`. Used to represent all pods in the matching namespaces
|
||||||
podSelectors *selectorSet
|
podSelectors *selectorSet // used to represent the matching pods in namespace respresented by this `ns`
|
||||||
rules *ruleSet
|
namespacedPodsSelectors *selectorSet // reference to global selectorSet that is shared across the `ns`. Used to represent matching pods in matching namespace
|
||||||
|
ipBlocks *ipBlockSet
|
||||||
legacy bool
|
rules *ruleSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNS(name, nodeName string, legacy bool, ipt iptables.Interface, ips ipset.Interface, nsSelectors *selectorSet) (*ns, error) {
|
func newNS(name, nodeName string, ipt iptables.Interface, ips ipset.Interface, nsSelectors *selectorSet, namespacedPodsSelectors *selectorSet, namespaceObj *coreapi.Namespace) (*ns, error) {
|
||||||
allPods, err := newSelectorSpec(&metav1.LabelSelector{}, false, name, ipset.HashIP)
|
allPods, err := newSelectorSpec(&metav1.LabelSelector{}, nil, nil, name, ipset.HashIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ns := &ns{
|
ns := &ns{
|
||||||
ipt: ipt,
|
ipt: ipt,
|
||||||
ips: ips,
|
ips: ips,
|
||||||
name: name,
|
name: name,
|
||||||
nodeName: nodeName,
|
namespace: namespaceObj,
|
||||||
pods: make(map[types.UID]*coreapi.Pod),
|
nodeName: nodeName,
|
||||||
policies: make(map[types.UID]interface{}),
|
pods: make(map[types.UID]*coreapi.Pod),
|
||||||
uid: uuid.NewUUID(),
|
policies: make(map[types.UID]interface{}),
|
||||||
allPods: allPods,
|
uid: uuid.NewUUID(),
|
||||||
nsSelectors: nsSelectors,
|
allPods: allPods,
|
||||||
rules: newRuleSet(ipt),
|
nsSelectors: nsSelectors,
|
||||||
legacy: legacy}
|
namespacedPodsSelectors: namespacedPodsSelectors,
|
||||||
|
ipBlocks: newIPBlockSet(ips),
|
||||||
ns.podSelectors = newSelectorSet(ips, ns.onNewPodSelector, ns.onNewDstPodSelector, ns.onDestroyDstPodSelector)
|
rules: newRuleSet(ipt),
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ns.podSelectors = newSelectorSet(ips, ns.onNewPodSelector, ns.onNewTargetPodSelector, ns.onDestroyTargetPodSelector)
|
||||||
|
|
||||||
|
ingressDefaultAllowIPSet := ipset.Name(IpsetNamePrefix + shortName("ingress-default-allow:"+name))
|
||||||
|
if err := ips.Create(ingressDefaultAllowIPSet, ipset.HashIP); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ns.ingressDefaultAllowIPSet = ingressDefaultAllowIPSet
|
||||||
|
|
||||||
|
egressDefaultAllowIPSet := ipset.Name(IpsetNamePrefix + shortName("egress-default-allow:"+name))
|
||||||
|
if err := ips.Create(egressDefaultAllowIPSet, ipset.HashIP); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ns.egressDefaultAllowIPSet = egressDefaultAllowIPSet
|
||||||
|
|
||||||
if err := ns.podSelectors.provision(ns.uid, nil, map[string]*selectorSpec{ns.allPods.key: ns.allPods}); err != nil {
|
if err := ns.podSelectors.provision(ns.uid, nil, map[string]*selectorSpec{ns.allPods.key: ns.allPods}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -91,7 +98,7 @@ func (ns *ns) destroy() error {
|
|||||||
func (ns *ns) onNewPodSelector(selector *selector) error {
|
func (ns *ns) onNewPodSelector(selector *selector) error {
|
||||||
for _, pod := range ns.pods {
|
for _, pod := range ns.pods {
|
||||||
if hasIP(pod) {
|
if hasIP(pod) {
|
||||||
if selector.matches(pod.ObjectMeta.Labels) {
|
if selector.matchesPodSelector(pod.ObjectMeta.Labels) {
|
||||||
if err := selector.addEntry(pod.ObjectMeta.UID, pod.Status.PodIP, podComment(pod)); err != nil {
|
if err := selector.addEntry(pod.ObjectMeta.UID, pod.Status.PodIP, podComment(pod)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -102,16 +109,13 @@ func (ns *ns) onNewPodSelector(selector *selector) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) onNewDstPodSelector(selector *selector) error {
|
func (ns *ns) onNewTargetPodSelector(selector *selector, policyType policyType) error {
|
||||||
if ns.legacy {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, pod := range ns.pods {
|
for _, pod := range ns.pods {
|
||||||
if hasIP(pod) {
|
if hasIP(pod) {
|
||||||
// Remove the pod from default-allow if dst podselector matches the pod
|
// Remove the pod from default-allow if dst podselector matches the pod
|
||||||
if selector.matches(pod.ObjectMeta.Labels) {
|
ipset := ns.defaultAllowIPSetName(policyType)
|
||||||
if err := ns.ips.DelEntry(pod.ObjectMeta.UID, ns.defaultAllowIPSet, pod.Status.PodIP); err != nil {
|
if selector.matchesPodSelector(pod.ObjectMeta.Labels) {
|
||||||
|
if err := ns.ips.DelEntry(pod.ObjectMeta.UID, ipset, pod.Status.PodIP); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,15 +125,11 @@ func (ns *ns) onNewDstPodSelector(selector *selector) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) onDestroyDstPodSelector(selector *selector) error {
|
func (ns *ns) onDestroyTargetPodSelector(selector *selector, policyType policyType) error {
|
||||||
if ns.legacy {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, pod := range ns.pods {
|
for _, pod := range ns.pods {
|
||||||
if hasIP(pod) {
|
if hasIP(pod) {
|
||||||
if selector.matches(pod.ObjectMeta.Labels) {
|
if selector.matchesPodSelector(pod.ObjectMeta.Labels) {
|
||||||
if err := ns.addToDefaultAllowIfNoMatching(pod); err != nil {
|
if err := ns.addToDefaultAllowIfNoMatching(pod, policyType); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,18 +139,19 @@ func (ns *ns) onDestroyDstPodSelector(selector *selector) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add pod IP addr to default-allow ipset if there are no matching dst selectors
|
// Add pod IP addr to default-allow ipset if there are no matching target selectors
|
||||||
func (ns *ns) addToDefaultAllowIfNoMatching(pod *coreapi.Pod) error {
|
func (ns *ns) addToDefaultAllowIfNoMatching(pod *coreapi.Pod, policyType policyType) error {
|
||||||
found := false
|
found := false
|
||||||
// TODO(mp) optimize (avoid iterating over selectors) by ref counting IP addrs.
|
// TODO(mp) optimize (avoid iterating over selectors) by ref counting IP addrs.
|
||||||
for _, s := range ns.podSelectors.entries {
|
for _, s := range ns.podSelectors.entries {
|
||||||
if ns.podSelectors.dstSelectorExist(s) && s.matches(pod.ObjectMeta.Labels) {
|
if ns.podSelectors.targetSelectorExist(s, policyType) && s.matchesPodSelector(pod.ObjectMeta.Labels) {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
if err := ns.ips.AddEntry(pod.ObjectMeta.UID, ns.defaultAllowIPSet, pod.Status.PodIP, podComment(pod)); err != nil {
|
ipset := ns.defaultAllowIPSetName(policyType)
|
||||||
|
if err := ns.ips.AddEntry(pod.ObjectMeta.UID, ipset, pod.Status.PodIP, podComment(pod)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,20 +172,26 @@ func (ns *ns) addPod(obj *coreapi.Pod) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ns.checkLocalPod(obj) {
|
foundIngress, foundEgress, err := ns.podSelectors.addToMatchingPodSelector(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, obj.Status.PodIP, podComment(obj))
|
||||||
ns.ips.AddEntry(obj.ObjectMeta.UID, LocalIpset, obj.Status.PodIP, podComment(obj))
|
|
||||||
}
|
|
||||||
|
|
||||||
found, err := ns.podSelectors.addToMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, obj.Status.PodIP, podComment(obj))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// If there are no matching dst selectors, add the pod to default-allow
|
// If there are no matching target selectors, add the pod to default-allow
|
||||||
if !ns.legacy && !found {
|
if !foundIngress {
|
||||||
if err := ns.ips.AddEntry(obj.ObjectMeta.UID, ns.defaultAllowIPSet, obj.Status.PodIP, podComment(obj)); err != nil {
|
if err := ns.ips.AddEntry(obj.ObjectMeta.UID, ns.ingressDefaultAllowIPSet, obj.Status.PodIP, podComment(obj)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !foundEgress {
|
||||||
|
if err := ns.ips.AddEntry(obj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, obj.Status.PodIP, podComment(obj)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ns.namespacedPodsSelectors.addToMatchingNamespacedPodSelector(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, ns.namespace.ObjectMeta.Labels, obj.Status.PodIP, podComment(obj))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -198,48 +205,48 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if hasIP(oldObj) && !hasIP(newObj) {
|
if hasIP(oldObj) && !hasIP(newObj) {
|
||||||
if ns.checkLocalPod(oldObj) {
|
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.ingressDefaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||||
ns.ips.DelEntry(oldObj.ObjectMeta.UID, LocalIpset, oldObj.Status.PodIP)
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.namespacedPodsSelectors.delFromMatchingNamespacedPodSelector(oldObj.ObjectMeta.UID, oldObj.ObjectMeta.Labels, ns.namespace.ObjectMeta.Labels, oldObj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ns.legacy {
|
return ns.podSelectors.delFromMatchingPodSelector(oldObj.ObjectMeta.UID, oldObj.ObjectMeta.Labels, oldObj.Status.PodIP)
|
||||||
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ns.podSelectors.delFromMatching(oldObj.ObjectMeta.UID, oldObj.ObjectMeta.Labels, oldObj.Status.PodIP)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hasIP(oldObj) && hasIP(newObj) {
|
if !hasIP(oldObj) && hasIP(newObj) {
|
||||||
if ns.checkLocalPod(newObj) {
|
foundIngress, foundEgress, err := ns.podSelectors.addToMatchingPodSelector(newObj.ObjectMeta.UID, newObj.ObjectMeta.Labels, newObj.Status.PodIP, podComment(newObj))
|
||||||
ns.ips.AddEntry(newObj.ObjectMeta.UID, LocalIpset, newObj.Status.PodIP, podComment(newObj))
|
|
||||||
}
|
|
||||||
found, err := ns.podSelectors.addToMatching(newObj.ObjectMeta.UID, newObj.ObjectMeta.Labels, newObj.Status.PodIP, podComment(newObj))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ns.legacy && !found {
|
if !foundIngress {
|
||||||
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.defaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.ingressDefaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !foundEgress {
|
||||||
|
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = ns.namespacedPodsSelectors.addToMatchingNamespacedPodSelector(newObj.ObjectMeta.UID, newObj.ObjectMeta.Labels, ns.namespace.ObjectMeta.Labels, newObj.Status.PodIP, podComment(newObj))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ns.legacy &&
|
if oldObj.Status.PodIP != newObj.Status.PodIP {
|
||||||
oldObj.Status.PodIP != newObj.Status.PodIP &&
|
if err := ns.updateDefaultAllowIPSetEntry(oldObj, newObj, ns.ingressDefaultAllowIPSet); err != nil {
|
||||||
ns.ips.Exist(oldObj.ObjectMeta.UID, ns.defaultAllowIPSet, oldObj.Status.PodIP) {
|
|
||||||
// Instead of iterating over all selectors we check whether old pod IP
|
|
||||||
// has been inserted into default-allow ipset to decide whether the IP
|
|
||||||
// in the ipset has to be updated.
|
|
||||||
|
|
||||||
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.defaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
if err := ns.updateDefaultAllowIPSetEntry(oldObj, newObj, ns.egressDefaultAllowIPSet); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,8 +255,8 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
|||||||
oldObj.Status.PodIP != newObj.Status.PodIP {
|
oldObj.Status.PodIP != newObj.Status.PodIP {
|
||||||
|
|
||||||
for _, ps := range ns.podSelectors.entries {
|
for _, ps := range ns.podSelectors.entries {
|
||||||
oldMatch := ps.matches(oldObj.ObjectMeta.Labels)
|
oldMatch := ps.matchesPodSelector(oldObj.ObjectMeta.Labels)
|
||||||
newMatch := ps.matches(newObj.ObjectMeta.Labels)
|
newMatch := ps.matchesPodSelector(newObj.ObjectMeta.Labels)
|
||||||
if oldMatch == newMatch && oldObj.Status.PodIP == newObj.Status.PodIP {
|
if oldMatch == newMatch && oldObj.Status.PodIP == newObj.Status.PodIP {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -264,16 +271,27 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ns.legacy && ns.podSelectors.dstSelectorExist(ps) {
|
if err := ns.addOrRemoveToDefaultAllowIPSet(ps, oldObj, newObj, oldMatch, newMatch, policyTypeIngress); err != nil {
|
||||||
switch {
|
return err
|
||||||
case !oldMatch && newMatch:
|
}
|
||||||
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
if err := ns.addOrRemoveToDefaultAllowIPSet(ps, oldObj, newObj, oldMatch, newMatch, policyTypeEgress); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case oldMatch && !newMatch:
|
}
|
||||||
if err := ns.addToDefaultAllowIfNoMatching(newObj); err != nil {
|
for _, ps := range ns.namespacedPodsSelectors.entries {
|
||||||
return err
|
oldMatch := ps.matchesNamespacedPodSelector(oldObj.ObjectMeta.Labels, ns.namespace.ObjectMeta.Labels)
|
||||||
}
|
newMatch := ps.matchesNamespacedPodSelector(newObj.ObjectMeta.Labels, ns.namespace.ObjectMeta.Labels)
|
||||||
|
if oldMatch == newMatch && oldObj.Status.PodIP == newObj.Status.PodIP {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if oldMatch {
|
||||||
|
if err := ps.delEntry(oldObj.ObjectMeta.UID, oldObj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newMatch {
|
||||||
|
if err := ps.addEntry(newObj.ObjectMeta.UID, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,6 +300,23 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ns *ns) addOrRemoveToDefaultAllowIPSet(ps *selector, oldObj, newObj *coreapi.Pod, oldMatch, newMatch bool, policyType policyType) error {
|
||||||
|
ipset := ns.defaultAllowIPSetName(policyType)
|
||||||
|
if ns.podSelectors.targetSelectorExist(ps, policyType) {
|
||||||
|
switch {
|
||||||
|
case !oldMatch && newMatch:
|
||||||
|
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ipset, oldObj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case oldMatch && !newMatch:
|
||||||
|
if err := ns.addToDefaultAllowIfNoMatching(newObj, policyType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
||||||
delete(ns.pods, obj.ObjectMeta.UID)
|
delete(ns.pods, obj.ObjectMeta.UID)
|
||||||
|
|
||||||
@@ -289,23 +324,25 @@ func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ns.checkLocalPod(obj) {
|
if err := ns.ips.DelEntry(obj.ObjectMeta.UID, ns.ingressDefaultAllowIPSet, obj.Status.PodIP); err != nil {
|
||||||
ns.ips.DelEntry(obj.ObjectMeta.UID, LocalIpset, obj.Status.PodIP)
|
return err
|
||||||
}
|
}
|
||||||
|
if err := ns.ips.DelEntry(obj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, obj.Status.PodIP); err != nil {
|
||||||
if !ns.legacy {
|
return err
|
||||||
if err := ns.ips.DelEntry(obj.ObjectMeta.UID, ns.defaultAllowIPSet, obj.Status.PodIP); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if err := ns.podSelectors.delFromMatchingPodSelector(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, obj.Status.PodIP); err != nil {
|
||||||
return ns.podSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, obj.Status.PodIP)
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.namespacedPodsSelectors.delFromMatchingNamespacedPodSelector(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, ns.namespace.ObjectMeta.Labels, obj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) addNetworkPolicy(obj interface{}) error {
|
func (ns *ns) addNetworkPolicy(obj interface{}) error {
|
||||||
// Analyse policy, determine which rules and ipsets are required
|
// Analyse policy, determine which rules and ipsets are required
|
||||||
|
|
||||||
uid, rules, nsSelectors, podSelectors, err := ns.analyse(obj)
|
uid, rules, nsSelectors, podSelectors, namespacedPodsSelectors, ipBlocks, err := ns.analyse(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -317,16 +354,22 @@ func (ns *ns) addNetworkPolicy(obj interface{}) error {
|
|||||||
if err := ns.podSelectors.provision(uid, nil, podSelectors); err != nil {
|
if err := ns.podSelectors.provision(uid, nil, podSelectors); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := ns.namespacedPodsSelectors.provision(uid, nil, namespacedPodsSelectors); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.ipBlocks.provision(uid, nil, ipBlocks); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return ns.rules.provision(uid, nil, rules)
|
return ns.rules.provision(uid, nil, rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) updateNetworkPolicy(oldObj, newObj interface{}) error {
|
func (ns *ns) updateNetworkPolicy(oldObj, newObj interface{}) error {
|
||||||
// Analyse the old and the new policy so we can determine differences
|
// Analyse the old and the new policy so we can determine differences
|
||||||
oldUID, oldRules, oldNsSelectors, oldPodSelectors, err := ns.analyse(oldObj)
|
oldUID, oldRules, oldNsSelectors, oldPodSelectors, oldNamespacedPodsSelectors, oldIPBlocks, err := ns.analyse(oldObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
newUID, newRules, newNsSelectors, newPodSelectors, err := ns.analyse(newObj)
|
newUID, newRules, newNsSelectors, newPodSelectors, newNamespacedPodsSelectors, newIPBlocks, err := ns.analyse(newObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -344,18 +387,30 @@ func (ns *ns) updateNetworkPolicy(oldObj, newObj interface{}) error {
|
|||||||
if err := ns.podSelectors.deprovision(oldUID, oldPodSelectors, newPodSelectors); err != nil {
|
if err := ns.podSelectors.deprovision(oldUID, oldPodSelectors, newPodSelectors); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := ns.namespacedPodsSelectors.deprovision(oldUID, oldNamespacedPodsSelectors, newNamespacedPodsSelectors); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.ipBlocks.deprovision(oldUID, oldIPBlocks, newIPBlocks); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := ns.nsSelectors.provision(oldUID, oldNsSelectors, newNsSelectors); err != nil {
|
if err := ns.nsSelectors.provision(oldUID, oldNsSelectors, newNsSelectors); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ns.podSelectors.provision(oldUID, oldPodSelectors, newPodSelectors); err != nil {
|
if err := ns.podSelectors.provision(oldUID, oldPodSelectors, newPodSelectors); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := ns.namespacedPodsSelectors.provision(oldUID, oldNamespacedPodsSelectors, newNamespacedPodsSelectors); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.ipBlocks.provision(oldUID, oldIPBlocks, newIPBlocks); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return ns.rules.provision(oldUID, oldRules, newRules)
|
return ns.rules.provision(oldUID, oldRules, newRules)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) deleteNetworkPolicy(obj interface{}) error {
|
func (ns *ns) deleteNetworkPolicy(obj interface{}) error {
|
||||||
// Analyse network policy to free resources
|
// Analyse network policy to free resources
|
||||||
uid, rules, nsSelectors, podSelectors, err := ns.analyse(obj)
|
uid, rules, nsSelectors, podSelectors, namespacedPodsSelectors, ipBlocks, err := ns.analyse(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -369,78 +424,97 @@ func (ns *ns) deleteNetworkPolicy(obj interface{}) error {
|
|||||||
if err := ns.nsSelectors.deprovision(uid, nsSelectors, nil); err != nil {
|
if err := ns.nsSelectors.deprovision(uid, nsSelectors, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ns.podSelectors.deprovision(uid, podSelectors, nil)
|
if err := ns.podSelectors.deprovision(uid, podSelectors, nil); err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
func bypassRule(nsIpsetName ipset.Name, namespace string) []string {
|
if err := ns.namespacedPodsSelectors.deprovision(uid, namespacedPodsSelectors, nil); err != nil {
|
||||||
return []string{"-m", "set", "--match-set", string(nsIpsetName), "dst", "-j", "ACCEPT", "-m", "comment", "--comment", "DefaultAllow isolation for namespace: " + namespace}
|
return err
|
||||||
}
|
}
|
||||||
|
if err := ns.ipBlocks.deprovision(uid, ipBlocks, nil); err != nil {
|
||||||
func (ns *ns) ensureBypassRule() error {
|
return err
|
||||||
var ipset ipset.Name
|
|
||||||
if ns.legacy {
|
|
||||||
ipset = ns.allPods.ipsetName
|
|
||||||
} else {
|
|
||||||
ipset = ns.defaultAllowIPSet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
common.Log.Debugf("ensuring rule for DefaultAllow in namespace: %s, set %s", ns.name, ipset)
|
return nil
|
||||||
return ns.ipt.Append(TableFilter, DefaultChain, bypassRule(ipset, ns.name)...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) deleteBypassRule() error {
|
func (ns *ns) updateDefaultAllowIPSetEntry(oldObj, newObj *coreapi.Pod, ipsetName ipset.Name) error {
|
||||||
var ipset ipset.Name
|
// Instead of iterating over all selectors we check whether old pod IP
|
||||||
if ns.legacy {
|
// has been inserted into default-allow ipset to decide whether the IP
|
||||||
ipset = ns.allPods.ipsetName
|
// in the ipset has to be updated.
|
||||||
} else {
|
if ns.ips.Exist(oldObj.ObjectMeta.UID, ipsetName, oldObj.Status.PodIP) {
|
||||||
ipset = ns.defaultAllowIPSet
|
|
||||||
|
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ipsetName, oldObj.Status.PodIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ipsetName, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func bypassRules(namespace string, ingress, egress ipset.Name) map[string][][]string {
|
||||||
|
return map[string][][]string{
|
||||||
|
DefaultChain: {
|
||||||
|
{"-m", "set", "--match-set", string(ingress), "dst", "-j", "ACCEPT",
|
||||||
|
"-m", "comment", "--comment", "DefaultAllow ingress isolation for namespace: " + namespace},
|
||||||
|
},
|
||||||
|
EgressDefaultChain: {
|
||||||
|
{"-m", "set", "--match-set", string(egress), "src", "-j", EgressMarkChain,
|
||||||
|
"-m", "comment", "--comment", "DefaultAllow egress isolation for namespace: " + namespace},
|
||||||
|
{"-m", "set", "--match-set", string(egress), "src", "-j", "RETURN",
|
||||||
|
"-m", "comment", "--comment", "DefaultAllow egress isolation for namespace: " + namespace},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *ns) ensureBypassRules() error {
|
||||||
|
for chain, rules := range bypassRules(ns.name, ns.ingressDefaultAllowIPSet, ns.egressDefaultAllowIPSet) {
|
||||||
|
for _, rule := range rules {
|
||||||
|
common.Log.Debugf("adding rule for DefaultAllow in namespace: %s, chain: %s, %s", ns.name, chain, rule)
|
||||||
|
if err := ns.ipt.Append(TableFilter, chain, rule...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
common.Log.Debugf("removing default rule in namespace: %s, set %s", ns.name, ipset)
|
return nil
|
||||||
return ns.ipt.Delete(TableFilter, DefaultChain, bypassRule(ipset, ns.name)...)
|
}
|
||||||
|
|
||||||
|
func (ns *ns) deleteBypassRules() error {
|
||||||
|
for chain, rules := range bypassRules(ns.name, ns.ingressDefaultAllowIPSet, ns.egressDefaultAllowIPSet) {
|
||||||
|
for _, rule := range rules {
|
||||||
|
common.Log.Debugf("removing rule for DefaultAllow in namespace: %s, chain: %s, %s", ns.name, chain, rule)
|
||||||
|
if err := ns.ipt.Delete(TableFilter, chain, rule...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) addNamespace(obj *coreapi.Namespace) error {
|
func (ns *ns) addNamespace(obj *coreapi.Namespace) error {
|
||||||
ns.namespace = obj
|
ns.namespace = obj
|
||||||
|
|
||||||
// Insert a rule to bypass policies if namespace is DefaultAllow
|
// Insert a rule to bypass policies
|
||||||
if !ns.isDefaultDeny(obj) {
|
if err := ns.ensureBypassRules(); err != nil {
|
||||||
if err := ns.ensureBypassRule(); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add namespace ipset to matching namespace selectors
|
// Add namespace ipset to matching namespace selectors
|
||||||
_, err := ns.nsSelectors.addToMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, string(ns.allPods.ipsetName), namespaceComment(ns))
|
err := ns.nsSelectors.addToMatchingNamespaceSelector(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, string(ns.allPods.ipsetName), namespaceComment(ns))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
||||||
ns.namespace = newObj
|
ns.namespace = newObj
|
||||||
|
|
||||||
// Update bypass rule if ingress default has changed
|
|
||||||
oldDefaultDeny := ns.isDefaultDeny(oldObj)
|
|
||||||
newDefaultDeny := ns.isDefaultDeny(newObj)
|
|
||||||
|
|
||||||
if oldDefaultDeny != newDefaultDeny {
|
|
||||||
common.Log.Infof("namespace DefaultDeny changed from %t to %t", oldDefaultDeny, newDefaultDeny)
|
|
||||||
if oldDefaultDeny {
|
|
||||||
if err := ns.ensureBypassRule(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if newDefaultDeny {
|
|
||||||
if err := ns.deleteBypassRule(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-evaluate namespace selector membership if labels have changed
|
// Re-evaluate namespace selector membership if labels have changed
|
||||||
if !equals(oldObj.ObjectMeta.Labels, newObj.ObjectMeta.Labels) {
|
if !equals(oldObj.ObjectMeta.Labels, newObj.ObjectMeta.Labels) {
|
||||||
for _, selector := range ns.nsSelectors.entries {
|
for _, selector := range ns.nsSelectors.entries {
|
||||||
oldMatch := selector.matches(oldObj.ObjectMeta.Labels)
|
oldMatch := selector.matchesNamespaceSelector(oldObj.ObjectMeta.Labels)
|
||||||
newMatch := selector.matches(newObj.ObjectMeta.Labels)
|
newMatch := selector.matchesNamespaceSelector(newObj.ObjectMeta.Labels)
|
||||||
if oldMatch == newMatch {
|
if oldMatch == newMatch {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -464,48 +538,26 @@ func (ns *ns) deleteNamespace(obj *coreapi.Namespace) error {
|
|||||||
ns.namespace = nil
|
ns.namespace = nil
|
||||||
|
|
||||||
// Remove bypass rule
|
// Remove bypass rule
|
||||||
if !ns.isDefaultDeny(obj) {
|
if err := ns.deleteBypassRules(); err != nil {
|
||||||
if err := ns.deleteBypassRule(); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove namespace ipset from any matching namespace selectors
|
// Remove namespace ipset from any matching namespace selectors
|
||||||
err := ns.nsSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
err := ns.nsSelectors.delFromMatchingNamespaceSelector(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ns.legacy {
|
if err := ns.ips.Destroy(ns.ingressDefaultAllowIPSet); err != nil {
|
||||||
return ns.ips.Destroy(ns.defaultAllowIPSet)
|
return err
|
||||||
|
}
|
||||||
|
if err := ns.ips.Destroy(ns.egressDefaultAllowIPSet); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
func hasIP(pod *coreapi.Pod) bool {
|
||||||
// Ensure pod isn't dead, has an IP address and isn't sharing the host network namespace
|
// 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" &&
|
return pod.Status.Phase != "Succeeded" && pod.Status.Phase != "Failed" &&
|
||||||
@@ -532,10 +584,19 @@ func podComment(pod *coreapi.Pod) string {
|
|||||||
return fmt.Sprintf("namespace: %s, pod: %s", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name)
|
return fmt.Sprintf("namespace: %s, pod: %s", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ns *ns) defaultAllowIPSetName(pt policyType) ipset.Name {
|
||||||
|
ipset := ns.ingressDefaultAllowIPSet
|
||||||
|
if pt == policyTypeEgress {
|
||||||
|
ipset = ns.egressDefaultAllowIPSet
|
||||||
|
}
|
||||||
|
return ipset
|
||||||
|
}
|
||||||
|
|
||||||
func (ns *ns) analyse(obj interface{}) (
|
func (ns *ns) analyse(obj interface{}) (
|
||||||
uid types.UID,
|
uid types.UID,
|
||||||
rules map[string]*ruleSpec,
|
rules map[string]*ruleSpec,
|
||||||
nsSelectors, podSelectors map[string]*selectorSpec,
|
nsSelectors, podSelectors, namespacedPodsSelectors map[string]*selectorSpec,
|
||||||
|
ipBlocks map[string]*ipBlockSpec,
|
||||||
err error) {
|
err error) {
|
||||||
|
|
||||||
switch p := obj.(type) {
|
switch p := obj.(type) {
|
||||||
@@ -550,16 +611,9 @@ func (ns *ns) analyse(obj interface{}) (
|
|||||||
ns.policies[uid] = obj
|
ns.policies[uid] = obj
|
||||||
|
|
||||||
// Analyse policy, determine which rules and ipsets are required
|
// Analyse policy, determine which rules and ipsets are required
|
||||||
if ns.legacy {
|
rules, nsSelectors, podSelectors, namespacedPodsSelectors, ipBlocks, err = ns.analysePolicy(obj.(*networkingv1.NetworkPolicy))
|
||||||
rules, nsSelectors, podSelectors, err = ns.analysePolicyLegacy(obj.(*extnapi.NetworkPolicy))
|
if err != nil {
|
||||||
if err != nil {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rules, nsSelectors, podSelectors, err = ns.analysePolicy(obj.(*networkingv1.NetworkPolicy))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
+64
-23
@@ -2,6 +2,7 @@ package npc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
@@ -10,38 +11,71 @@ import (
|
|||||||
"github.com/weaveworks/weave/npc/iptables"
|
"github.com/weaveworks/weave/npc/iptables"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ruleSpec struct {
|
type ruleHost interface {
|
||||||
key string
|
// getRuleSpec returns source or destination specification and comment which
|
||||||
args []string
|
// are used in an iptables rule.
|
||||||
|
//
|
||||||
|
// If src=true then the rulespec is for source, otherwise - for destination.
|
||||||
|
getRuleSpec(src bool) ([]string, string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRuleSpec(proto *string, srcHost *selectorSpec, dstHost *selectorSpec, dstPort *string) *ruleSpec {
|
type ruleSpec struct {
|
||||||
|
key string
|
||||||
|
args []string
|
||||||
|
policyType policyType
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRuleSpec(policyType policyType, proto *string, srcHost, dstHost ruleHost, dstPort *string) *ruleSpec {
|
||||||
args := []string{}
|
args := []string{}
|
||||||
if proto != nil {
|
if proto != nil {
|
||||||
args = append(args, "-p", *proto)
|
args = append(args, "-p", *proto)
|
||||||
}
|
}
|
||||||
srcComment := "anywhere"
|
srcComment := "anywhere"
|
||||||
if srcHost != nil {
|
if srcHost != nil && !reflect.ValueOf(srcHost).IsNil() {
|
||||||
args = append(args, "-m", "set", "--match-set", string(srcHost.ipsetName), "src")
|
rule, comment := srcHost.getRuleSpec(true)
|
||||||
if srcHost.nsName != "" {
|
args = append(args, rule...)
|
||||||
srcComment = fmt.Sprintf("pods: namespace: %s, selector: %s", srcHost.nsName, srcHost.key)
|
srcComment = comment
|
||||||
} else {
|
|
||||||
srcComment = fmt.Sprintf("namespaces: selector: %s", srcHost.key)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dstComment := "anywhere"
|
dstComment := "anywhere"
|
||||||
if dstHost != nil {
|
if dstHost != nil && !reflect.ValueOf(dstHost).IsNil() {
|
||||||
args = append(args, "-m", "set", "--match-set", string(dstHost.ipsetName), "dst")
|
rule, comment := dstHost.getRuleSpec(false)
|
||||||
dstComment = fmt.Sprintf("pods: namespace: %s, selector: %s", dstHost.nsName, dstHost.key)
|
args = append(args, rule...)
|
||||||
|
dstComment = comment
|
||||||
}
|
}
|
||||||
if dstPort != nil {
|
if dstPort != nil {
|
||||||
args = append(args, "--dport", *dstPort)
|
args = append(args, "--dport", *dstPort)
|
||||||
}
|
}
|
||||||
args = append(args, "-j", "ACCEPT")
|
// NOTE: if you remove the comment bellow, then embed `policyType` into `key`.
|
||||||
args = append(args, "-m", "comment", "--comment", fmt.Sprintf("%s -> %s", srcComment, dstComment))
|
// Otherwise, the rule won't be provisioned if it exists for other policy type.
|
||||||
|
args = append(args, "-m", "comment", "--comment", fmt.Sprintf("%s -> %s (%s)", srcComment, dstComment, policyTypeStr(policyType)))
|
||||||
key := strings.Join(args, " ")
|
key := strings.Join(args, " ")
|
||||||
|
|
||||||
return &ruleSpec{key, args}
|
return &ruleSpec{key, args, policyType}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spec *ruleSpec) iptChain() string {
|
||||||
|
if spec.policyType == policyTypeEgress {
|
||||||
|
return EgressCustomChain
|
||||||
|
}
|
||||||
|
return IngressChain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spec *ruleSpec) iptRuleSpecs() [][]string {
|
||||||
|
if spec.policyType == policyTypeIngress {
|
||||||
|
rule := make([]string, len(spec.args))
|
||||||
|
copy(rule, spec.args)
|
||||||
|
rule = append(rule, "-j", "ACCEPT")
|
||||||
|
return [][]string{rule}
|
||||||
|
}
|
||||||
|
|
||||||
|
// policyTypeEgress
|
||||||
|
ruleMark := make([]string, len(spec.args))
|
||||||
|
copy(ruleMark, spec.args)
|
||||||
|
ruleMark = append(ruleMark, "-j", EgressMarkChain)
|
||||||
|
ruleReturn := make([]string, len(spec.args))
|
||||||
|
copy(ruleReturn, spec.args)
|
||||||
|
ruleReturn = append(ruleReturn, "-j", "RETURN")
|
||||||
|
return [][]string{ruleMark, ruleReturn}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ruleSet struct {
|
type ruleSet struct {
|
||||||
@@ -58,10 +92,14 @@ func (rs *ruleSet) deprovision(user types.UID, current, desired map[string]*rule
|
|||||||
if _, found := desired[key]; !found {
|
if _, found := desired[key]; !found {
|
||||||
delete(rs.users[key], user)
|
delete(rs.users[key], user)
|
||||||
if len(rs.users[key]) == 0 {
|
if len(rs.users[key]) == 0 {
|
||||||
common.Log.Infof("deleting rule: %v", spec.args)
|
chain := spec.iptChain()
|
||||||
if err := rs.ipt.Delete(TableFilter, IngressChain, spec.args...); err != nil {
|
for _, rule := range spec.iptRuleSpecs() {
|
||||||
return err
|
common.Log.Infof("deleting rule %v from %q chain", rule, chain)
|
||||||
|
if err := rs.ipt.Delete(TableFilter, chain, rule...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(rs.users, key)
|
delete(rs.users, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,9 +112,12 @@ func (rs *ruleSet) provision(user types.UID, current, desired map[string]*ruleSp
|
|||||||
for key, spec := range desired {
|
for key, spec := range desired {
|
||||||
if _, found := current[key]; !found {
|
if _, found := current[key]; !found {
|
||||||
if _, found := rs.users[key]; !found {
|
if _, found := rs.users[key]; !found {
|
||||||
common.Log.Infof("adding rule: %v", spec.args)
|
chain := spec.iptChain()
|
||||||
if err := rs.ipt.Append(TableFilter, IngressChain, spec.args...); err != nil {
|
for _, rule := range spec.iptRuleSpecs() {
|
||||||
return err
|
common.Log.Infof("adding rule %v to %q chain", rule, chain)
|
||||||
|
if err := rs.ipt.Append(TableFilter, chain, rule...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rs.users[key] = make(map[types.UID]struct{})
|
rs.users[key] = make(map[types.UID]struct{})
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ func TestRegressionHandleNil3095(t *testing.T) {
|
|||||||
// Test for handling nil values
|
// Test for handling nil values
|
||||||
// https://github.com/weaveworks/weave/issues/3095
|
// https://github.com/weaveworks/weave/issues/3095
|
||||||
|
|
||||||
rs := newRuleSpec(nil, nil, nil, nil)
|
rs := newRuleSpec(policyTypeIngress, nil, nil, nil, nil)
|
||||||
|
|
||||||
require.NotEqual(t, rs.key, "")
|
require.NotEqual(t, rs.key, "")
|
||||||
|
|
||||||
|
|||||||
+141
-47
@@ -1,34 +1,50 @@
|
|||||||
package npc
|
package npc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
"github.com/weaveworks/weave/common"
|
||||||
"github.com/weaveworks/weave/npc/ipset"
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
)
|
)
|
||||||
|
|
||||||
type selectorSpec struct {
|
type selectorSpec struct {
|
||||||
key string // string representation (for hash keying/equality comparison)
|
key string // string representation (for hash keying/equality comparison)
|
||||||
selector labels.Selector // k8s Selector object (for matching)
|
podSelector labels.Selector // k8s Selector object (for matching pods)
|
||||||
dst bool // destination selector
|
namespaceSelector labels.Selector // k8s Selector object (for matching namespaces)
|
||||||
|
policyTypes []policyType // If non-empty, then selectorSpec is a target selector for given policyTypes.
|
||||||
|
|
||||||
ipsetType ipset.Type // type of ipset to provision
|
ipsetType ipset.Type // type of ipset to provision
|
||||||
ipsetName ipset.Name // generated ipset name
|
ipsetName ipset.Name // generated ipset name
|
||||||
nsName string // Namespace name
|
nsName string // Namespace name
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSelectorSpec(json *metav1.LabelSelector, dst bool, nsName string, ipsetType ipset.Type) (*selectorSpec, error) {
|
func newSelectorSpec(podJSON *metav1.LabelSelector, namespaceJSON *metav1.LabelSelector, policyType []policyType, nsName string, ipsetType ipset.Type) (*selectorSpec, error) {
|
||||||
selector, err := metav1.LabelSelectorAsSelector(json)
|
var podSelector, namespaceSelector labels.Selector
|
||||||
if err != nil {
|
var err error
|
||||||
return nil, err
|
var key string
|
||||||
|
if namespaceJSON != nil {
|
||||||
|
namespaceSelector, err = metav1.LabelSelectorAsSelector(namespaceJSON)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key = namespaceSelector.String()
|
||||||
|
}
|
||||||
|
if podJSON != nil {
|
||||||
|
podSelector, err = metav1.LabelSelectorAsSelector(podJSON)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key = key + podSelector.String()
|
||||||
}
|
}
|
||||||
key := selector.String()
|
|
||||||
return &selectorSpec{
|
return &selectorSpec{
|
||||||
key: key,
|
key: key,
|
||||||
selector: selector,
|
podSelector: podSelector,
|
||||||
dst: dst,
|
namespaceSelector: namespaceSelector,
|
||||||
|
policyTypes: policyType,
|
||||||
// We prefix the selector string with the namespace name when generating
|
// We prefix the selector string with the namespace name when generating
|
||||||
// the shortname because you can specify the same selector in multiple
|
// the shortname because you can specify the same selector in multiple
|
||||||
// namespaces - we need those to map to distinct ipsets
|
// namespaces - we need those to map to distinct ipsets
|
||||||
@@ -37,13 +53,38 @@ func newSelectorSpec(json *metav1.LabelSelector, dst bool, nsName string, ipsetT
|
|||||||
nsName: nsName}, nil
|
nsName: nsName}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (spec *selectorSpec) getRuleSpec(src bool) ([]string, string) {
|
||||||
|
dir := "dst"
|
||||||
|
if src {
|
||||||
|
dir = "src"
|
||||||
|
}
|
||||||
|
rule := []string{"-m", "set", "--match-set", string(spec.ipsetName), dir}
|
||||||
|
|
||||||
|
comment := "anywhere"
|
||||||
|
if spec.nsName != "" {
|
||||||
|
comment = fmt.Sprintf("pods: namespace: %s, selector: %s", spec.nsName, spec.key)
|
||||||
|
} else {
|
||||||
|
comment = fmt.Sprintf("namespaces: selector: %s", spec.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rule, comment
|
||||||
|
}
|
||||||
|
|
||||||
type selector struct {
|
type selector struct {
|
||||||
ips ipset.Interface
|
ips ipset.Interface
|
||||||
spec *selectorSpec
|
spec *selectorSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *selector) matches(labelMap map[string]string) bool {
|
func (s *selector) matchesPodSelector(podsLabelMap map[string]string) bool {
|
||||||
return s.spec.selector.Matches(labels.Set(labelMap))
|
return s.spec.podSelector.Matches(labels.Set(podsLabelMap))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *selector) matchesNamespaceSelector(namespaceLabelMap map[string]string) bool {
|
||||||
|
return s.spec.namespaceSelector.Matches(labels.Set(namespaceLabelMap))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *selector) matchesNamespacedPodSelector(podsLabelMap, namespaceLabelMap map[string]string) bool {
|
||||||
|
return s.spec.podSelector.Matches(labels.Set(podsLabelMap)) && s.spec.namespaceSelector.Matches(labels.Set(namespaceLabelMap))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *selector) addEntry(user types.UID, entry string, comment string) error {
|
func (s *selector) addEntry(user types.UID, entry string, comment string) error {
|
||||||
@@ -55,54 +96,81 @@ func (s *selector) delEntry(user types.UID, entry string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type selectorFn func(selector *selector) error
|
type selectorFn func(selector *selector) error
|
||||||
|
type selectorWithPolicyTypeFn func(selector *selector, policyType policyType) error
|
||||||
|
|
||||||
type selectorSet struct {
|
type selectorSet struct {
|
||||||
ips ipset.Interface
|
ips ipset.Interface
|
||||||
onNewSelector selectorFn
|
onNewSelector selectorFn
|
||||||
|
|
||||||
// invoked after dst selector has been provisioned for the first time
|
// invoked after target selector has been provisioned for the first time
|
||||||
onNewDstSelector selectorFn
|
onNewTargetSelector selectorWithPolicyTypeFn
|
||||||
// invoked after the last instance of dst selector has been deprovisioned
|
// invoked after the last instance of target selector has been deprovisioned
|
||||||
onDestroyDstSelector selectorFn
|
onDestroyTargetSelector selectorWithPolicyTypeFn
|
||||||
|
|
||||||
users map[string]map[types.UID]struct{} // list of users per selector
|
users map[string]map[types.UID]struct{} // list of users per selector
|
||||||
entries map[string]*selector
|
entries map[string]*selector
|
||||||
|
|
||||||
// We need to keep track of dst selector instances to be able to invoke
|
// We need to keep track of target selector instances to be able to invoke
|
||||||
// onNewDstSelector and onDestroyDstSelector callbacks at the right time;
|
// onNewTargetSelector and onDestroyTargetSelector callbacks at the right time;
|
||||||
// selectorSpec.Key -> count
|
// selectorSpec.Key -> policyType -> count
|
||||||
dstSelectorsCount map[string]int
|
targetSelectorsCount map[string]map[policyType]int
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSelectorSet(ips ipset.Interface, onNewSelector, onNewDstSelector selectorFn, onDestroyDstSelector selectorFn) *selectorSet {
|
func newSelectorSet(ips ipset.Interface, onNewSelector selectorFn, onNewTargetSelector, onDestroyTargetSelector selectorWithPolicyTypeFn) *selectorSet {
|
||||||
return &selectorSet{
|
return &selectorSet{
|
||||||
ips: ips,
|
ips: ips,
|
||||||
onNewSelector: onNewSelector,
|
onNewSelector: onNewSelector,
|
||||||
onNewDstSelector: onNewDstSelector,
|
onNewTargetSelector: onNewTargetSelector,
|
||||||
onDestroyDstSelector: onDestroyDstSelector,
|
onDestroyTargetSelector: onDestroyTargetSelector,
|
||||||
users: make(map[string]map[types.UID]struct{}),
|
users: make(map[string]map[types.UID]struct{}),
|
||||||
entries: make(map[string]*selector),
|
entries: make(map[string]*selector),
|
||||||
dstSelectorsCount: make(map[string]int)}
|
targetSelectorsCount: make(map[string]map[policyType]int)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *selectorSet) addToMatching(user types.UID, labelMap map[string]string, entry string, comment string) (bool, error) {
|
func (ss *selectorSet) addToMatchingPodSelector(user types.UID, podLabelsMap map[string]string, entry string, comment string) (bool, bool, error) {
|
||||||
found := false
|
foundIngress := false
|
||||||
|
foundEgress := false
|
||||||
for _, s := range ss.entries {
|
for _, s := range ss.entries {
|
||||||
if s.matches(labelMap) {
|
if s.matchesPodSelector(podLabelsMap) {
|
||||||
if ss.dstSelectorExist(s) {
|
if ss.targetSelectorExist(s, policyTypeIngress) {
|
||||||
found = true
|
foundIngress = true
|
||||||
|
}
|
||||||
|
if ss.targetSelectorExist(s, policyTypeEgress) {
|
||||||
|
foundEgress = true
|
||||||
}
|
}
|
||||||
if err := s.addEntry(user, entry, comment); err != nil {
|
if err := s.addEntry(user, entry, comment); err != nil {
|
||||||
return found, err
|
return foundIngress, foundEgress, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return found, nil
|
return foundIngress, foundEgress, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *selectorSet) delFromMatching(user types.UID, labelMap map[string]string, entry string) error {
|
func (ss *selectorSet) addToMatchingNamespaceSelector(user types.UID, namespaceLabelsMap map[string]string, entry string, comment string) error {
|
||||||
for _, s := range ss.entries {
|
for _, s := range ss.entries {
|
||||||
if s.matches(labelMap) {
|
if s.matchesNamespaceSelector(namespaceLabelsMap) {
|
||||||
|
if err := s.addEntry(user, entry, comment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *selectorSet) addToMatchingNamespacedPodSelector(user types.UID, podLabelsMap map[string]string, namespaceLabelsMap map[string]string, entry string, comment string) error {
|
||||||
|
for _, s := range ss.entries {
|
||||||
|
if s.matchesNamespacedPodSelector(podLabelsMap, namespaceLabelsMap) {
|
||||||
|
if err := s.addEntry(user, entry, comment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *selectorSet) delFromMatchingPodSelector(user types.UID, podLabelsMap map[string]string, entry string) error {
|
||||||
|
for _, s := range ss.entries {
|
||||||
|
if s.matchesPodSelector(podLabelsMap) {
|
||||||
if err := s.delEntry(user, entry); err != nil {
|
if err := s.delEntry(user, entry); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -111,8 +179,30 @@ func (ss *selectorSet) delFromMatching(user types.UID, labelMap map[string]strin
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *selectorSet) dstSelectorExist(s *selector) bool {
|
func (ss *selectorSet) delFromMatchingNamespaceSelector(user types.UID, namespaceLabelsMap map[string]string, entry string) error {
|
||||||
return ss.dstSelectorsCount[s.spec.key] > 0
|
for _, s := range ss.entries {
|
||||||
|
if s.matchesNamespaceSelector(namespaceLabelsMap) {
|
||||||
|
if err := s.delEntry(user, entry); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *selectorSet) delFromMatchingNamespacedPodSelector(user types.UID, podLabelsMap map[string]string, namespaceLabelsMap map[string]string, entry string) error {
|
||||||
|
for _, s := range ss.entries {
|
||||||
|
if s.matchesNamespacedPodSelector(podLabelsMap, namespaceLabelsMap) {
|
||||||
|
if err := s.delEntry(user, entry); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *selectorSet) targetSelectorExist(s *selector, policyType policyType) bool {
|
||||||
|
return ss.targetSelectorsCount[s.spec.key][policyType] > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*selectorSpec) error {
|
func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*selectorSpec) error {
|
||||||
@@ -129,13 +219,14 @@ func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*
|
|||||||
delete(ss.users, key)
|
delete(ss.users, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
if spec.dst {
|
for _, policyType := range spec.policyTypes {
|
||||||
ss.dstSelectorsCount[key]--
|
ss.targetSelectorsCount[key][policyType]--
|
||||||
if ss.dstSelectorsCount[key] == 0 {
|
if ss.targetSelectorsCount[key][policyType] == 0 {
|
||||||
if err := ss.onDestroyDstSelector(&selector{ss.ips, spec}); err != nil {
|
if err := ss.onDestroyTargetSelector(&selector{ss.ips, spec}, policyType); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO(brb) delete(...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,10 +251,13 @@ func (ss *selectorSet) provision(user types.UID, current, desired map[string]*se
|
|||||||
}
|
}
|
||||||
ss.users[key][user] = struct{}{}
|
ss.users[key][user] = struct{}{}
|
||||||
|
|
||||||
if spec.dst {
|
for _, pt := range spec.policyTypes {
|
||||||
ss.dstSelectorsCount[key]++
|
if _, found := ss.targetSelectorsCount[key]; !found {
|
||||||
if ss.dstSelectorsCount[key] == 1 {
|
ss.targetSelectorsCount[key] = make(map[policyType]int)
|
||||||
if err := ss.onNewDstSelector(selector); err != nil {
|
}
|
||||||
|
ss.targetSelectorsCount[key][pt]++
|
||||||
|
if ss.targetSelectorsCount[key][pt] == 1 {
|
||||||
|
if err := ss.onNewTargetSelector(selector, pt); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package npc
|
||||||
|
|
||||||
|
type policyType byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
policyTypeIngress policyType = iota
|
||||||
|
policyTypeEgress
|
||||||
|
)
|
||||||
|
|
||||||
|
func policyTypeStr(policyType policyType) string {
|
||||||
|
if policyType == policyTypeEgress {
|
||||||
|
return "egress"
|
||||||
|
}
|
||||||
|
return "ingress"
|
||||||
|
}
|
||||||
@@ -110,6 +110,10 @@ func (i *Ipam) ReleaseAddress(poolID string, address net.IP) error {
|
|||||||
return i.weave.ReleaseIPsFor(address.String())
|
return i.weave.ReleaseIPsFor(address.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Ipam) IsBuiltIn() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Functions required by ipamapi "contract" but not actually used.
|
// Functions required by ipamapi "contract" but not actually used.
|
||||||
|
|
||||||
func (i *Ipam) DiscoverNew(discoverapi.DiscoveryType, interface{}) error {
|
func (i *Ipam) DiscoverNew(discoverapi.DiscoveryType, interface{}) error {
|
||||||
|
|||||||
+25
-2
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ipam"
|
"github.com/containernetworking/cni/pkg/ipam"
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
@@ -117,6 +118,16 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
defer ns.Close()
|
defer ns.Close()
|
||||||
|
|
||||||
|
hostNs, err := netns.Get()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error accessing host network namespace: %s", err)
|
||||||
|
}
|
||||||
|
defer hostNs.Close()
|
||||||
|
|
||||||
|
if ns.Equal(hostNs) {
|
||||||
|
return fmt.Errorf("can not specify host network namespace as network namespace to which container should be added")
|
||||||
|
}
|
||||||
|
|
||||||
id := args.ContainerID
|
id := args.ContainerID
|
||||||
if len(id) < 5 {
|
if len(id) < 5 {
|
||||||
data := make([]byte, 5)
|
data := make([]byte, 5)
|
||||||
@@ -130,7 +141,7 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
|||||||
if err := weavenet.AttachContainer(args.Netns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&ip.Address}, false, conf.HairpinMode); err != nil {
|
if err := weavenet.AttachContainer(args.Netns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&ip.Address}, false, conf.HairpinMode); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := weavenet.WithNetNSLinkUnsafe(ns, args.IfName, func(link netlink.Link) error {
|
if err := weavenet.WithNetNSLink(ns, args.IfName, func(link netlink.Link) error {
|
||||||
return setupRoutes(link, args.IfName, ip.Address, ip.Gateway, result.Routes)
|
return setupRoutes(link, args.IfName, ip.Address, ip.Gateway, result.Routes)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("error setting up routes: %s", err)
|
return fmt.Errorf("error setting up routes: %s", err)
|
||||||
@@ -177,7 +188,13 @@ func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
// As of CNI 0.3 spec, runtimes can send blank if they just want the address deallocated
|
// As of CNI 0.3 spec, runtimes can send blank if they just want the address deallocated
|
||||||
if args.Netns != "" {
|
if args.Netns != "" {
|
||||||
if _, err = weavenet.WithNetNS(args.Netns, "del-iface", args.IfName); err != nil {
|
if err = weavenet.WithNetNSByPath(args.Netns, func() error {
|
||||||
|
link, err := netlink.LinkByName(args.IfName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return netlink.LinkDel(link)
|
||||||
|
}); err != nil {
|
||||||
// We log the error and carry on instead of returning nil, as there may still be resources to free in IPAM.
|
// We log the error and carry on instead of returning nil, as there may still be resources to free in IPAM.
|
||||||
logOnStderr(fmt.Errorf("error removing interface %q: %s", args.IfName, err))
|
logOnStderr(fmt.Errorf("error removing interface %q: %s", args.IfName, err))
|
||||||
}
|
}
|
||||||
@@ -190,7 +207,13 @@ func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
|
|||||||
err = ipam.ExecDel(conf.IPAM.Type, args.StdinData)
|
err = ipam.ExecDel(conf.IPAM.Type, args.StdinData)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), fmt.Sprintf("Delete: no addresses for %s", args.ContainerID)) {
|
||||||
|
// ignore the error in the case where there are no IP addresses associated with container
|
||||||
|
fmt.Fprintln(os.Stderr, "weave-cni:", fmt.Sprintf("Delete: no addresses for %s", args.ContainerID))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
logOnStderr(fmt.Errorf("unable to release IP address: %s", err))
|
logOnStderr(fmt.Errorf("unable to release IP address: %s", err))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,9 @@ func (w *watcher) ContainerStarted(id string) {
|
|||||||
rootDir = "/host"
|
rootDir = "/host"
|
||||||
}
|
}
|
||||||
netNSPath := weavenet.NSPathByPidWithRoot(rootDir, info.State.Pid)
|
netNSPath := weavenet.NSPathByPidWithRoot(rootDir, info.State.Pid)
|
||||||
if _, err := weavenet.WithNetNS(netNSPath, "configure-arp", weavenet.VethName, rootDir); err != nil {
|
if err := weavenet.WithNetNSByPath(netNSPath, func() error {
|
||||||
|
return weavenet.ConfigureARP(weavenet.VethName, rootDir)
|
||||||
|
}); err != nil {
|
||||||
w.driver.warn("ContainerStarted", "unable to configure interfaces: %s", err)
|
w.driver.warn("ContainerStarted", "unable to configure interfaces: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,236 +0,0 @@
|
|||||||
/*
|
|
||||||
Package main deals with weave-net peers on the cluster.
|
|
||||||
|
|
||||||
This involves peer management, such as getting the latest peers or removing defunct peers from the cluster
|
|
||||||
*/
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
api "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
"k8s.io/client-go/rest"
|
|
||||||
|
|
||||||
weaveapi "github.com/weaveworks/weave/api"
|
|
||||||
"github.com/weaveworks/weave/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
type nodeInfo struct {
|
|
||||||
name string
|
|
||||||
addr string
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the IP addresses of all nodes in the cluster
|
|
||||||
func getKubePeers(c *kubernetes.Clientset) ([]nodeInfo, error) {
|
|
||||||
nodeList, err := c.CoreV1().Nodes().List(api.ListOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
addresses := make([]nodeInfo, 0, len(nodeList.Items))
|
|
||||||
for _, peer := range nodeList.Items {
|
|
||||||
var internalIP, externalIP string
|
|
||||||
for _, addr := range peer.Status.Addresses {
|
|
||||||
// Check it's a valid ipv4 address
|
|
||||||
ip := net.ParseIP(addr.Address)
|
|
||||||
if ip == nil || ip.To4() == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if addr.Type == "InternalIP" {
|
|
||||||
internalIP = ip.To4().String()
|
|
||||||
} else if addr.Type == "ExternalIP" {
|
|
||||||
externalIP = ip.To4().String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback for cases where a Node has an ExternalIP but no InternalIP
|
|
||||||
if internalIP != "" {
|
|
||||||
addresses = append(addresses, nodeInfo{name: peer.Name, addr: internalIP})
|
|
||||||
} else if externalIP != "" {
|
|
||||||
addresses = append(addresses, nodeInfo{name: peer.Name, addr: externalIP})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return addresses, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
if err := cml.Init(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 1. Compare peers stored in the peerList against all peers reported by k8s now.
|
|
||||||
storedPeerList, err := cml.GetPeerList()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
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.GetAnnotation(KubePeersPrefix + 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(KubePeersPrefix+peer.PeerName, myPeerName); err == nil {
|
|
||||||
okToRemove = true
|
|
||||||
} else {
|
|
||||||
common.Log.Debugln("[kube-peers] error from UpdateAnnotation: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if okToRemove {
|
|
||||||
// 6. If step 4 or 5 succeeded, rmpeer X
|
|
||||||
result, err := weave.RmPeer(peer.PeerName)
|
|
||||||
common.Log.Infof("[kube-peers] rmpeer of %s: %s", 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)
|
|
||||||
if err := cml.UpdatePeerList(*storedPeerList); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 7b. Remove annotation with key X
|
|
||||||
return cml.RemoveAnnotation(KubePeersPrefix + 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,13 +2,10 @@
|
|||||||
In order to keep track of active weave peers, we use annotations on the Kubernetes cluster.
|
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.
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -29,43 +26,11 @@ type configMapAnnotations struct {
|
|||||||
cm *v1.ConfigMap
|
cm *v1.ConfigMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func newConfigMapAnnotations(ns string, configMapName string, clientset *kubernetes.Clientset) *configMapAnnotations {
|
func newConfigMapAnnotations(ns string, configMapName string, c kubernetes.Interface) *configMapAnnotations {
|
||||||
return &configMapAnnotations{
|
return &configMapAnnotations{
|
||||||
Namespace: ns,
|
Namespace: ns,
|
||||||
ConfigMapName: configMapName,
|
ConfigMapName: configMapName,
|
||||||
Client: clientset.CoreV1(),
|
Client: c.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++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,27 +76,6 @@ func (cml *configMapAnnotations) Init() error {
|
|||||||
return nil
|
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up a string so it meets the Kubernetes requiremements for Annotation keys:
|
// Clean up a string so it meets the Kubernetes requiremements for Annotation keys:
|
||||||
// name part must consist of alphanumeric characters, '-', '_' or '.', and must
|
// name part must consist of alphanumeric characters, '-', '_' or '.', and must
|
||||||
// start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc')
|
// start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc')
|
||||||
@@ -152,33 +96,33 @@ func (cml *configMapAnnotations) GetAnnotation(key string) (string, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cml *configMapAnnotations) UpdateAnnotation(key, value string) (err error) {
|
func (cml *configMapAnnotations) UpdateAnnotation(key, value string) (err error) {
|
||||||
if cml.cm == nil {
|
if cml.cm == nil || cml.cm.Annotations == nil {
|
||||||
return errors.New("endpoint not initialized, call Init first")
|
return errors.New("endpoint not initialized, call Init first")
|
||||||
}
|
}
|
||||||
// speculatively change the state, then replace with whatever comes back
|
// speculatively change the state, then replace with whatever comes back
|
||||||
// from Update(), which will be the latest on the server whatever happened
|
// from Update(), which will be the latest on the server, or nil if error
|
||||||
cml.cm.Annotations[cleanKey(key)] = value
|
cml.cm.Annotations[cleanKey(key)] = value
|
||||||
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cml *configMapAnnotations) RemoveAnnotation(key string) (err error) {
|
func (cml *configMapAnnotations) RemoveAnnotation(key string) (err error) {
|
||||||
if cml.cm == nil {
|
if cml.cm == nil || cml.cm.Annotations == nil {
|
||||||
return errors.New("endpoint not initialized, call Init first")
|
return errors.New("endpoint not initialized, call Init first")
|
||||||
}
|
}
|
||||||
// speculatively change the state, then replace with whatever comes back
|
// speculatively change the state, then replace with whatever comes back
|
||||||
// from Update(), which will be the latest on the server whatever happened
|
// from Update(), which will be the latest on the server, or nil if error
|
||||||
delete(cml.cm.Annotations, cleanKey(key))
|
delete(cml.cm.Annotations, cleanKey(key))
|
||||||
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cml *configMapAnnotations) RemoveAnnotationsWithValue(valueToRemove string) (err error) {
|
func (cml *configMapAnnotations) RemoveAnnotationsWithValue(valueToRemove string) (err error) {
|
||||||
if cml.cm == nil {
|
if cml.cm == nil || cml.cm.Annotations == nil {
|
||||||
return errors.New("endpoint not initialized, call Init first")
|
return errors.New("endpoint not initialized, call Init first")
|
||||||
}
|
}
|
||||||
// speculatively change the state, then replace with whatever comes back
|
// speculatively change the state, then replace with whatever comes back
|
||||||
// from Update(), which will be the latest on the server whatever happened
|
// from Update(), which will be the latest on the server, or nil if error
|
||||||
for key, value := range cml.cm.Annotations {
|
for key, value := range cml.cm.Annotations {
|
||||||
if value == valueToRemove {
|
if value == valueToRemove {
|
||||||
delete(cml.cm.Annotations, key) // don't need to clean this key as it came from the map
|
delete(cml.cm.Annotations, key) // don't need to clean this key as it came from the map
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Adapted from k8s.io/client-go/kubernetes/fake to use a modified ObjectTracker
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
|
"k8s.io/client-go/testing"
|
||||||
|
|
||||||
|
weavetracker "github.com/weaveworks/weave/testing/kubernetes/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var scheme = runtime.NewScheme()
|
||||||
|
var codecs = serializer.NewCodecFactory(scheme)
|
||||||
|
|
||||||
|
func NewSimpleClientset(objects ...runtime.Object) *fake.Clientset {
|
||||||
|
o := weavetracker.NewObjectTracker(scheme, codecs.UniversalDecoder())
|
||||||
|
for _, obj := range objects {
|
||||||
|
if err := o.Add(obj); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := fake.Clientset{}
|
||||||
|
ret.AddReactor("*", "*", weavetracker.ObjectReaction(o))
|
||||||
|
ret.AddWatchReactor("*", testing.DefaultWatchReactor(watch.NewFake(), nil))
|
||||||
|
|
||||||
|
return &ret
|
||||||
|
}
|
||||||
@@ -0,0 +1,390 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
api "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/client-go/informers"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
|
weaveapi "github.com/weaveworks/weave/api"
|
||||||
|
"github.com/weaveworks/weave/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type nodeInfo struct {
|
||||||
|
name string
|
||||||
|
addr string
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the IP addresses of all nodes in the cluster
|
||||||
|
func getKubePeers(c kubernetes.Interface, includeWithNoIPAddr bool) ([]nodeInfo, error) {
|
||||||
|
nodeList, err := c.CoreV1().Nodes().List(api.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
addresses := make([]nodeInfo, 0, len(nodeList.Items))
|
||||||
|
for _, peer := range nodeList.Items {
|
||||||
|
var internalIP, externalIP string
|
||||||
|
for _, addr := range peer.Status.Addresses {
|
||||||
|
// Check it's a valid ipv4 address
|
||||||
|
ip := net.ParseIP(addr.Address)
|
||||||
|
if ip == nil || ip.To4() == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if addr.Type == "InternalIP" {
|
||||||
|
internalIP = ip.To4().String()
|
||||||
|
} else if addr.Type == "ExternalIP" {
|
||||||
|
externalIP = ip.To4().String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback for cases where a Node has an ExternalIP but no InternalIP
|
||||||
|
if internalIP != "" {
|
||||||
|
// exclude self from the list of peers this node will peer with
|
||||||
|
if isLocalNodeIP(internalIP) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addresses = append(addresses, nodeInfo{name: peer.Name, addr: internalIP})
|
||||||
|
} else if externalIP != "" {
|
||||||
|
addresses = append(addresses, nodeInfo{name: peer.Name, addr: externalIP})
|
||||||
|
} else if includeWithNoIPAddr {
|
||||||
|
addresses = append(addresses, nodeInfo{name: peer.Name, addr: ""})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addresses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if given IP matches with one of the local IP's
|
||||||
|
func isLocalNodeIP(ip string) bool {
|
||||||
|
addrs, err := netlink.AddrList(nil, unix.AF_INET)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if addr.Peer.IP.String() == ip {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// (minimal, incomplete) interface so weaver can be mocked for testing.
|
||||||
|
type weaveClient interface {
|
||||||
|
RmPeer(peerName string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each of those peers that is no longer listed as a node by
|
||||||
|
// Kubernetes, remove it from Weave IPAM
|
||||||
|
func reclaimRemovedPeers(kube kubernetes.Interface, cml *configMapAnnotations, myPeerName, myNodeName string) error {
|
||||||
|
weave := weaveapi.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), common.Log)
|
||||||
|
for loopsWhenNothingChanged := 0; loopsWhenNothingChanged < 3; loopsWhenNothingChanged++ {
|
||||||
|
// 1. Compare peers stored in the peerList against all peers reported by k8s now.
|
||||||
|
storedPeerList, err := getRefreshedPeerList(cml)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nodes, err := getKubePeers(kube, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nodeSet := make(map[string]struct{}, len(nodes))
|
||||||
|
for _, node := range nodes {
|
||||||
|
nodeSet[node.name] = struct{}{}
|
||||||
|
}
|
||||||
|
peerMap := make(map[string]peerInfo, len(storedPeerList.Peers))
|
||||||
|
for _, peer := range storedPeerList.Peers {
|
||||||
|
if peer.PeerName != myPeerName {
|
||||||
|
peerMap[peer.PeerName] = peer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// remove entries from the peer map that are current nodes
|
||||||
|
for key, peer := range peerMap {
|
||||||
|
if _, found := nodeSet[peer.NodeName]; found {
|
||||||
|
// unless they have a duplicate of my NodeName but are not me
|
||||||
|
if peer.NodeName == myNodeName && peer.PeerName != myPeerName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
delete(peerMap, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// so the remainder is everything we want to clean up
|
||||||
|
common.Log.Debugln("[kube-peers] Nodes that have disappeared:", peerMap)
|
||||||
|
if len(peerMap) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// 2. Loop for each X in the first set and not in the second - we wish to remove X from our data structures
|
||||||
|
for _, peer := range peerMap {
|
||||||
|
if peer.PeerName == myPeerName { // Don't remove myself.
|
||||||
|
common.Log.Warnln("[kube-peers] not removing myself", peer)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
changed, err := reclaimPeer(weave, cml, peer.PeerName, myPeerName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if changed {
|
||||||
|
loopsWhenNothingChanged = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. Go back to step 1 until there is no difference between the two sets
|
||||||
|
// (or we hit the counter that says we've been round the loop 3 times and nothing happened)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to reclaim the IP addresses owned by peerName, using the
|
||||||
|
// Kubernetes api-server as a point of consensus so that only one peer
|
||||||
|
// actions the reclaim.
|
||||||
|
// Return a bool to show whether we attempted to change anything,
|
||||||
|
// and an error if something went wrong.
|
||||||
|
func reclaimPeer(weave weaveClient, cml *configMapAnnotations, peerName string, myPeerName string) (changed bool, err error) {
|
||||||
|
common.Log.Debugln("[kube-peers] Preparing to remove disappeared peer", peerName)
|
||||||
|
okToRemove := false
|
||||||
|
nonExistentPeer := false
|
||||||
|
|
||||||
|
// Re-read status from Kubernetes; speculative updates may leave it un-set
|
||||||
|
if err := cml.Init(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Check if there is an existing annotation with key X
|
||||||
|
existingAnnotation, found := cml.GetAnnotation(KubePeersPrefix + peerName)
|
||||||
|
if found {
|
||||||
|
common.Log.Debugln("[kube-peers] Existing annotation", existingAnnotation)
|
||||||
|
// 4. If annotation already contains my identity, ok;
|
||||||
|
if existingAnnotation == myPeerName {
|
||||||
|
okToRemove = true
|
||||||
|
} else {
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// handle an edge case where peer claimed to own the action to reclaim but no longer
|
||||||
|
// exists hence lock persists foever
|
||||||
|
if !storedPeerList.contains(existingAnnotation) {
|
||||||
|
nonExistentPeer = true
|
||||||
|
common.Log.Debugln("[kube-peers] Existing annotation", existingAnnotation, " has a non-existent peer so owning the reclaim action")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found || nonExistentPeer {
|
||||||
|
// 5. If non-existent, write an annotation with key X and contents "my identity"
|
||||||
|
common.Log.Debugln("[kube-peers] Noting I plan to remove ", peerName)
|
||||||
|
if err := cml.UpdateAnnotation(KubePeersPrefix+peerName, myPeerName); err == nil {
|
||||||
|
okToRemove = true
|
||||||
|
} else {
|
||||||
|
common.Log.Errorln("[kube-peers] error from UpdateAnnotation: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !okToRemove {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
// 6. If step 4 or 5 succeeded, rmpeer X
|
||||||
|
// we will try once more to see if the peerName has been removed from the annotation
|
||||||
|
// by another weave pod
|
||||||
|
storedPeerList, err := getRefreshedPeerList(cml)
|
||||||
|
if storedPeerList.contains(peerName) {
|
||||||
|
result, err := weave.RmPeer(peerName)
|
||||||
|
common.Log.Infof("[kube-peers] rmpeer of %s: %s", peerName, result)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = cml.LoopUpdate(func() error {
|
||||||
|
// 7aa. Remove any annotations Z* that have contents X
|
||||||
|
if err := cml.RemoveAnnotationsWithValue(peerName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 7a. Remove X from peerList
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if storedPeerList.contains(peerName) {
|
||||||
|
storedPeerList.remove(peerName)
|
||||||
|
if err := cml.UpdatePeerList(*storedPeerList); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
common.Log.Infof("[kube-peers] annotation was removed already for %s", peerName)
|
||||||
|
}
|
||||||
|
// 7b. Remove annotation with key X
|
||||||
|
return cml.RemoveAnnotation(KubePeersPrefix + peerName)
|
||||||
|
})
|
||||||
|
// 8. If step 5 failed due to optimistic lock conflict, stop: someone else is handling X
|
||||||
|
|
||||||
|
// Step 3-5 is to protect against two simultaneous rmpeers of X
|
||||||
|
// Step 4 is to pick up again after a restart between step 5 and step 7b
|
||||||
|
// If the peer doing the reclaim disappears between steps 5 and 7a, then someone will clean it up in step 7aa
|
||||||
|
// If peer doing the reclaim disappears forever between 7a and 7b then we get a dangling annotation
|
||||||
|
// This should be sufficiently rare that we don't care.
|
||||||
|
|
||||||
|
// Question: Should we narrow step 2 by checking against Weave Net IPAM?
|
||||||
|
// i.e. If peer X owns any address space and is marked unreachable, we want to rmpeer X
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// resetPeers replaces the peers list with current set of peers
|
||||||
|
func resetPeers(kube kubernetes.Interface) error {
|
||||||
|
nodes, err := getKubePeers(kube, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
peerList := make([]string, 0)
|
||||||
|
for _, node := range nodes {
|
||||||
|
peerList = append(peerList, node.addr)
|
||||||
|
}
|
||||||
|
weave := weaveapi.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), common.Log)
|
||||||
|
err = weave.ReplacePeers(peerList)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// regiesters with Kubernetes API server for node delete events. Node delete event handler
|
||||||
|
// invokes reclaimRemovedPeers to remove it from IPAM so that IP space is reclaimed
|
||||||
|
func registerForNodeUpdates(client *kubernetes.Clientset, stopCh <-chan struct{}, nodeName, peerName string) {
|
||||||
|
informerFactory := informers.NewSharedInformerFactory(client, 0)
|
||||||
|
nodeInformer := informerFactory.Core().V1().Nodes().Informer()
|
||||||
|
common.Log.Debugln("registering for updates for node delete events")
|
||||||
|
nodeInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||||
|
DeleteFunc: func(obj interface{}) {
|
||||||
|
var name string
|
||||||
|
if apiObj, ok := obj.(api.Object); ok {
|
||||||
|
name = apiObj.GetName()
|
||||||
|
} else {
|
||||||
|
name = fmt.Sprintf("%#v", obj)
|
||||||
|
}
|
||||||
|
common.Log.Debugln("Delete event for", name)
|
||||||
|
// add random delay to avoid all nodes acting on node delete event at the same
|
||||||
|
// time leading to contention to use `weave-net` configmap
|
||||||
|
r := rand.Intn(5000)
|
||||||
|
time.Sleep(time.Duration(r) * time.Millisecond)
|
||||||
|
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, client)
|
||||||
|
err := reclaimRemovedPeers(client, cml, peerName, nodeName)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Error while reclaiming space: %v", err)
|
||||||
|
}
|
||||||
|
err = resetPeers(client)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Error resetting peer list: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
informerFactory.WaitForCacheSync(stopCh)
|
||||||
|
informerFactory.Start(stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRefreshedPeerList will fetch the latest peer list from the config map annotation
|
||||||
|
func getRefreshedPeerList(cml *configMapAnnotations) (*peerList, error) {
|
||||||
|
if err := cml.Init(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return storedPeerList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
justReclaim bool
|
||||||
|
justCheck bool
|
||||||
|
justSetNodeStatus bool
|
||||||
|
peerName string
|
||||||
|
nodeName string
|
||||||
|
logLevel string
|
||||||
|
runReclaimDaemon bool
|
||||||
|
)
|
||||||
|
flag.BoolVar(&justReclaim, "reclaim", false, "reclaim IP space from dead peers")
|
||||||
|
flag.BoolVar(&runReclaimDaemon, "run-reclaim-daemon", false, "run background process that reclaim IP space from dead peers ")
|
||||||
|
flag.BoolVar(&justCheck, "check-peer-new", false, "return success if peer name is not stored in annotation")
|
||||||
|
flag.BoolVar(&justSetNodeStatus, "set-node-status", false, "set NodeNetworkUnavailable to false")
|
||||||
|
flag.StringVar(&peerName, "peer-name", "unknown", "name of this Weave Net peer")
|
||||||
|
flag.StringVar(&nodeName, "node-name", "unknown", "name of this Kubernetes node")
|
||||||
|
flag.StringVar(&logLevel, "log-level", "info", "logging level (debug, info, warning, error)")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
common.SetLogLevel(logLevel)
|
||||||
|
config, err := rest.InClusterConfig()
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Could not get cluster config: %v", err)
|
||||||
|
}
|
||||||
|
c, err := kubernetes.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Could not make Kubernetes connection: %v", err)
|
||||||
|
}
|
||||||
|
if justCheck {
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
exists, err := checkIamInPeerList(cml, c, peerName)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Could not check peer list: %v", err)
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
os.Exit(9)
|
||||||
|
} else {
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if justSetNodeStatus {
|
||||||
|
err := setNodeNetworkUnavailableFalse(c, nodeName)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] could not set node status: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if 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)
|
||||||
|
|
||||||
|
err = reclaimRemovedPeers(c, cml, peerName, nodeName)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Error while reclaiming space: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if runReclaimDaemon {
|
||||||
|
// Handle SIGINT and SIGTERM
|
||||||
|
ch := make(chan os.Signal)
|
||||||
|
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
registerForNodeUpdates(c, stopCh, nodeName, peerName)
|
||||||
|
<-ch
|
||||||
|
close(stopCh)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
peers, err := getKubePeers(c, false)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Could not get peers: %v", err)
|
||||||
|
}
|
||||||
|
for _, node := range peers {
|
||||||
|
fmt.Println(node.addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// based on k8s.io/kubernetes/pkg/controller/nodeipam/ipam/adapter.go
|
||||||
|
func setNodeNetworkUnavailableFalse(c kubernetes.Interface, nodeName string) error {
|
||||||
|
condition := v1.NodeCondition{
|
||||||
|
Type: v1.NodeNetworkUnavailable,
|
||||||
|
Status: v1.ConditionFalse,
|
||||||
|
Reason: "WeaveIsUp",
|
||||||
|
Message: "Weave pod has set this",
|
||||||
|
LastTransitionTime: metav1.Now(),
|
||||||
|
LastHeartbeatTime: metav1.Now(),
|
||||||
|
}
|
||||||
|
raw, err := json.Marshal(&[]v1.NodeCondition{condition})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
patch := []byte(fmt.Sprintf(`{"status":{"conditions":%s}}`, raw))
|
||||||
|
_, err = c.CoreV1().Nodes().PatchStatus(nodeName, patch)
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
This module deals with operations on the peerlist backed by Kubernetes' annotation mechanism.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
configMapName = "weave-net"
|
||||||
|
configMapNamespace = "kube-system"
|
||||||
|
)
|
||||||
|
|
||||||
|
type peerList struct {
|
||||||
|
Peers []peerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type peerInfo struct {
|
||||||
|
PeerName string // Weave internal unique ID
|
||||||
|
NodeName string // Kubernetes node name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pl *peerList) contains(peerName string) bool {
|
||||||
|
for _, peer := range pl.Peers {
|
||||||
|
if peer.PeerName == peerName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pl *peerList) add(peerName string, name string) {
|
||||||
|
pl.Peers = append(pl.Peers, peerInfo{PeerName: peerName, NodeName: name})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pl *peerList) remove(peerNameToRemove string) {
|
||||||
|
for i := 0; i < len(pl.Peers); {
|
||||||
|
if pl.Peers[i].PeerName == peerNameToRemove {
|
||||||
|
pl.Peers = append(pl.Peers[:i], pl.Peers[i+1:]...)
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cml *configMapAnnotations) GetPeerList() (*peerList, error) {
|
||||||
|
var record peerList
|
||||||
|
if cml.cm == nil {
|
||||||
|
return nil, errors.New("endpoint not initialized, call Init first")
|
||||||
|
}
|
||||||
|
if recordBytes, found := cml.cm.Annotations[KubePeersAnnotationKey]; found {
|
||||||
|
if err := json.Unmarshal([]byte(recordBytes), &record); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &record, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cml *configMapAnnotations) UpdatePeerList(list peerList) error {
|
||||||
|
recordBytes, err := json.Marshal(list)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return cml.UpdateAnnotation(KubePeersAnnotationKey, string(recordBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the list of all peers that have gone through this code path
|
||||||
|
func addMyselfToPeerList(cml *configMapAnnotations, c kubernetes.Interface, peerName, name string) (*peerList, error) {
|
||||||
|
var list *peerList
|
||||||
|
err := cml.LoopUpdate(func() error {
|
||||||
|
var err error
|
||||||
|
list, err = cml.GetPeerList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !list.contains(peerName) {
|
||||||
|
list.add(peerName, name)
|
||||||
|
err = cml.UpdatePeerList(*list)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkIamInPeerList(cml *configMapAnnotations, c kubernetes.Interface, peerName string) (bool, error) {
|
||||||
|
if err := cml.Init(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
list, err := cml.GetPeerList()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
common.Log.Debugf("[kube-peers] Checking peer %q against list %v", peerName, list)
|
||||||
|
return list.contains(peerName), nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
peerName1 = "01:00:00:00:00:00"
|
||||||
|
peerName2 = "02:00:00:00:00:00"
|
||||||
|
peerName3 = "03:00:00:00:00:00"
|
||||||
|
nodeName1 = "fake-node-1"
|
||||||
|
nodeName2 = "fake-node-2"
|
||||||
|
|
||||||
|
testIters = 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPeerListBasic(t *testing.T) {
|
||||||
|
c := NewSimpleClientset()
|
||||||
|
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
err := cml.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
list1, err := addMyselfToPeerList(cml, c, peerName1, nodeName1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(list1.Peers))
|
||||||
|
list2, err := addMyselfToPeerList(cml, c, peerName2, nodeName2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(list2.Peers))
|
||||||
|
|
||||||
|
check1, err := checkIamInPeerList(cml, c, peerName1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, true, check1)
|
||||||
|
check3, err := checkIamInPeerList(cml, c, peerName3)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, false, check3)
|
||||||
|
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
storedPeerList.remove(peerName1)
|
||||||
|
require.Equal(t, 1, len(storedPeerList.Peers))
|
||||||
|
err = cml.UpdatePeerList(*storedPeerList)
|
||||||
|
require.NoError(t, err)
|
||||||
|
check1, err = checkIamInPeerList(cml, c, peerName1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, false, check1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run function _f_ _iterations_ times, in _concurrency_
|
||||||
|
// number of goroutines
|
||||||
|
func doConcurrentIterations(iterations, concurrency int, f func(int)) {
|
||||||
|
iterationsPerThread := iterations / concurrency
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
for i := 0; i < concurrency; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(j int) {
|
||||||
|
defer wg.Done()
|
||||||
|
for k := 0; k < iterationsPerThread; k++ {
|
||||||
|
f((j * iterationsPerThread) + k)
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeerListFuzz(t *testing.T) {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
const (
|
||||||
|
testIters = 1000
|
||||||
|
maxNodes = 100
|
||||||
|
concurrency = 5
|
||||||
|
)
|
||||||
|
peerName := func(i int) string { return fmt.Sprintf("%02d:00:00:02:00:00", i) }
|
||||||
|
nodeName := func(i int) string { return fmt.Sprintf("node%d", i) }
|
||||||
|
|
||||||
|
stateLock := sync.Mutex{}
|
||||||
|
nodes := []int{}
|
||||||
|
nodeMap := map[int]bool{} // true if an operation is pending
|
||||||
|
|
||||||
|
c := NewSimpleClientset()
|
||||||
|
|
||||||
|
addNode := func() {
|
||||||
|
i := rand.Intn(maxNodes)
|
||||||
|
// add to test state first, then to Kubernetes
|
||||||
|
stateLock.Lock()
|
||||||
|
pending, found := nodeMap[i]
|
||||||
|
if !found {
|
||||||
|
nodes = append(nodes, i)
|
||||||
|
}
|
||||||
|
nodeMap[i] = true
|
||||||
|
stateLock.Unlock()
|
||||||
|
if pending { // someone else is adding or deleting this node
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
err := cml.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = addMyselfToPeerList(cml, c, peerName(i), nodeName(i))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
stateLock.Lock()
|
||||||
|
nodeMap[i] = false
|
||||||
|
stateLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
removeNode := func() {
|
||||||
|
stateLock.Lock()
|
||||||
|
if len(nodes) == 0 { // can't remove if there aren't any nodes
|
||||||
|
stateLock.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pos := rand.Intn(len(nodes))
|
||||||
|
i := nodes[pos]
|
||||||
|
if pending, found := nodeMap[i]; found {
|
||||||
|
if pending {
|
||||||
|
stateLock.Unlock()
|
||||||
|
return // someone else is adding or deleting this node
|
||||||
|
}
|
||||||
|
nodeMap[i] = true
|
||||||
|
} else {
|
||||||
|
t.Errorf("missing entry from node map: %d", i)
|
||||||
|
}
|
||||||
|
stateLock.Unlock()
|
||||||
|
|
||||||
|
// remove from Kubernetes first, then from test state
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
require.NoError(t, cml.Init())
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
require.NoError(t, err)
|
||||||
|
found := storedPeerList.contains(peerName(i))
|
||||||
|
require.True(t, found, "peer %d not found in stored list", i)
|
||||||
|
|
||||||
|
_, err = reclaimPeer(mockWeave{}, cml, peerName(i), fmt.Sprintf("deleter-%d", i))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, cml.Init())
|
||||||
|
storedPeerList, err = cml.GetPeerList()
|
||||||
|
require.NoError(t, err)
|
||||||
|
stateLock.Lock()
|
||||||
|
delete(nodeMap, i)
|
||||||
|
for pos = 0; pos < len(nodes); pos++ {
|
||||||
|
if nodes[pos] == i {
|
||||||
|
// Remove item from list by swapping it with last and reducing slice length by 1
|
||||||
|
nodes[pos] = nodes[len(nodes)-1]
|
||||||
|
nodes = nodes[:len(nodes)-1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stateLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
doConcurrentIterations(testIters, concurrency, func(iteration int) {
|
||||||
|
r := rand.Float32()
|
||||||
|
switch {
|
||||||
|
case 0.0 <= r && r < 0.4:
|
||||||
|
addNode()
|
||||||
|
|
||||||
|
case (0.4 <= r && r < 0.8):
|
||||||
|
removeNode()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
err := cml.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, len(storedPeerList.Peers), len(nodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockWeave struct{}
|
||||||
|
|
||||||
|
func (mockWeave) RmPeer(peerName string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
@@ -1,3 +1,14 @@
|
|||||||
FROM DOCKERHUB_USER/weaveARCH_EXT
|
FROM DOCKERHUB_USER/weaveARCH_EXT
|
||||||
ADD ./launch.sh ./kube-peers /home/weave/
|
|
||||||
|
# These labels are pretty static, and can therefore be added early on:
|
||||||
|
LABEL maintainer="Weaveworks <help@weave.works>" \
|
||||||
|
org.opencontainers.image.title="weave-kube" \
|
||||||
|
org.opencontainers.image.source="https://github.com/weaveworks/weave" \
|
||||||
|
org.opencontainers.image.vendor="Weaveworks"
|
||||||
|
|
||||||
|
ADD ./launch.sh ./kube-utils /home/weave/
|
||||||
ENTRYPOINT ["/home/weave/launch.sh"]
|
ENTRYPOINT ["/home/weave/launch.sh"]
|
||||||
|
|
||||||
|
# This label will change for every build, and should therefore be the last layer of the image:
|
||||||
|
ARG revision
|
||||||
|
LABEL org.opencontainers.image.revision="${revision}"
|
||||||
@@ -6,6 +6,34 @@ modprobe_safe() {
|
|||||||
modprobe $1 || echo "Ignore the error if \"$1\" is built-in in the kernel" >&2
|
modprobe $1 || echo "Ignore the error if \"$1\" is built-in in the kernel" >&2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Setup iptables backend to be legacy or nftable
|
||||||
|
setup_iptables_backend() {
|
||||||
|
if [ -n "${IPTABLES_BACKEND}" ]; then
|
||||||
|
mode=$IPTABLES_BACKEND
|
||||||
|
else
|
||||||
|
# auto-detect if iptables backend mode to use if not specified explicitly
|
||||||
|
num_legacy_lines=$( (iptables-legacy-save || true) 2>/dev/null | grep '^-' | wc -l)
|
||||||
|
num_nft_lines=$( (iptables-nft-save || true) 2>/dev/null | grep '^-' | wc -l)
|
||||||
|
if [ "${num_legacy_lines}" -ge 10 ]; then
|
||||||
|
mode="legacy"
|
||||||
|
else
|
||||||
|
if [ "${num_legacy_lines}" -ge "${num_nft_lines}" ]; then
|
||||||
|
mode="legacy"
|
||||||
|
else
|
||||||
|
mode="nft"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ $mode = "nft" ]; then
|
||||||
|
rm /sbin/iptables
|
||||||
|
rm /sbin/iptables-save
|
||||||
|
rm /sbin/iptables-restore
|
||||||
|
ln -s /sbin/iptables-nft /sbin/iptables
|
||||||
|
ln -s /sbin/iptables-nft-save /sbin/iptables-save
|
||||||
|
ln -s /sbin/iptables-nft-restore /sbin/iptables-restore
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Check whether xt_set actually exists
|
# Check whether xt_set actually exists
|
||||||
xt_set_exists() {
|
xt_set_exists() {
|
||||||
# Clean everything up in advance, in case there's leftovers
|
# Clean everything up in advance, in case there's leftovers
|
||||||
@@ -24,12 +52,14 @@ xt_set_exists() {
|
|||||||
[ -z "$NOT_EXIST" ] || (echo "\"xt_set\" does not exist" >&2 && return 1)
|
[ -z "$NOT_EXIST" ] || (echo "\"xt_set\" does not exist" >&2 && return 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setup_iptables_backend
|
||||||
|
|
||||||
# Default if not supplied - same as weave net default
|
# Default if not supplied - same as weave net default
|
||||||
IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
|
IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
|
||||||
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
||||||
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
|
METRICS_ADDR=${WEAVE_METRICS_ADDR:-0.0.0.0:6782}
|
||||||
HOST_ROOT=${HOST_ROOT:-/host}
|
HOST_ROOT=${HOST_ROOT:-/host}
|
||||||
CONN_LIMIT=${CONN_LIMIT:-30}
|
CONN_LIMIT=${CONN_LIMIT:-200}
|
||||||
DB_PREFIX=${DB_PREFIX:-/weavedb/weave-net}
|
DB_PREFIX=${DB_PREFIX:-/weavedb/weave-net}
|
||||||
|
|
||||||
# Check if the IP range overlaps anything existing on the host
|
# Check if the IP range overlaps anything existing on the host
|
||||||
@@ -54,12 +84,23 @@ else
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
STATUS_OPTS="--metrics-addr=$METRICS_ADDR"
|
||||||
|
# --status-addr exposes internal information, so only turn it on if asked to.
|
||||||
|
if [ -n "${WEAVE_STATUS_ADDR}" ]; then
|
||||||
|
STATUS_OPTS="$STATUS_OPTS --status-addr=$WEAVE_STATUS_ADDR"
|
||||||
|
fi
|
||||||
|
|
||||||
# Default is that npc will be running; allow caller to override
|
# Default is that npc will be running; allow caller to override
|
||||||
WEAVE_NPC_OPTS="--expect-npc"
|
WEAVE_NPC_OPTS="--expect-npc"
|
||||||
if [ "${EXPECT_NPC}" = "0" ]; then
|
if [ "${EXPECT_NPC}" = "0" ]; then
|
||||||
WEAVE_NPC_OPTS=""
|
WEAVE_NPC_OPTS=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
NO_MASQ_LOCAL_OPT=""
|
||||||
|
if [ -n "${NO_MASQ_LOCAL}" ]; then
|
||||||
|
NO_MASQ_LOCAL_OPT="--no-masq-local"
|
||||||
|
fi
|
||||||
|
|
||||||
# Kubernetes sets HOSTNAME to the host's hostname
|
# Kubernetes sets HOSTNAME to the host's hostname
|
||||||
# when running a pod in host namespace.
|
# when running a pod in host namespace.
|
||||||
NICKNAME_ARG=""
|
NICKNAME_ARG=""
|
||||||
@@ -74,7 +115,7 @@ router_bridge_opts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if [ -z "$KUBE_PEERS" ]; then
|
if [ -z "$KUBE_PEERS" ]; then
|
||||||
if ! KUBE_PEERS=$(/home/weave/kube-peers) || [ -z "$KUBE_PEERS" ]; then
|
if ! KUBE_PEERS=$(/home/weave/kube-utils); then
|
||||||
echo Failed to get peers >&2
|
echo Failed to get peers >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -99,7 +140,7 @@ fi
|
|||||||
if [ -f ${DB_PREFIX}data.db ]; then
|
if [ -f ${DB_PREFIX}data.db ]; then
|
||||||
if /usr/bin/weaveutil get-db-flag "$DB_PREFIX" peer-reclaim >/dev/null ; 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 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
|
if /home/weave/kube-utils -check-peer-new -peer-name="$PEERNAME" -log-level=debug ; then
|
||||||
# In order to avoid a CRDT clash, clean up
|
# In order to avoid a CRDT clash, clean up
|
||||||
echo "Peer not in list; removing persisted data" >&2
|
echo "Peer not in list; removing persisted data" >&2
|
||||||
rm -f ${DB_PREFIX}data.db
|
rm -f ${DB_PREFIX}data.db
|
||||||
@@ -133,10 +174,15 @@ post_start_actions() {
|
|||||||
/home/weave/weave --local setup-cni
|
/home/weave/weave --local setup-cni
|
||||||
|
|
||||||
# Attempt to run the reclaim process, but don't halt the script if it fails
|
# 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
|
/home/weave/kube-utils -reclaim -node-name="$HOSTNAME" -peer-name="$PEERNAME" -log-level=debug || true
|
||||||
|
|
||||||
# Expose the weave network so host processes can communicate with pods
|
# Expose the weave network so host processes can communicate with pods
|
||||||
/home/weave/weave --local expose $WEAVE_EXPOSE_IP
|
/home/weave/weave --local expose $WEAVE_EXPOSE_IP
|
||||||
|
|
||||||
|
# Mark network as up
|
||||||
|
/home/weave/kube-utils -set-node-status -node-name="$HOSTNAME"
|
||||||
|
|
||||||
|
/home/weave/kube-utils -run-reclaim-daemon -node-name="$HOSTNAME" -peer-name="$PEERNAME" -log-level=debug&
|
||||||
}
|
}
|
||||||
|
|
||||||
post_start_actions &
|
post_start_actions &
|
||||||
@@ -144,11 +190,12 @@ post_start_actions &
|
|||||||
/home/weave/weaver $EXTRA_ARGS --port=6783 $(router_bridge_opts) \
|
/home/weave/weaver $EXTRA_ARGS --port=6783 $(router_bridge_opts) \
|
||||||
--name="$PEERNAME" \
|
--name="$PEERNAME" \
|
||||||
--host-root=$HOST_ROOT \
|
--host-root=$HOST_ROOT \
|
||||||
--http-addr=$HTTP_ADDR --status-addr=$STATUS_ADDR --docker-api='' --no-dns \
|
--http-addr=$HTTP_ADDR $STATUS_OPTS --docker-api='' --no-dns \
|
||||||
--db-prefix="$DB_PREFIX" \
|
--db-prefix="$DB_PREFIX" \
|
||||||
--ipalloc-range=$IPALLOC_RANGE $NICKNAME_ARG \
|
--ipalloc-range=$IPALLOC_RANGE $NICKNAME_ARG \
|
||||||
--ipalloc-init $IPALLOC_INIT \
|
--ipalloc-init $IPALLOC_INIT \
|
||||||
--conn-limit=$CONN_LIMIT \
|
--conn-limit=$CONN_LIMIT \
|
||||||
$WEAVE_NPC_OPTS \
|
$WEAVE_NPC_OPTS \
|
||||||
|
$NO_MASQ_LOCAL_OPT \
|
||||||
"$@" \
|
"$@" \
|
||||||
$KUBE_PEERS
|
$KUBE_PEERS
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
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/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- pods
|
||||||
|
- namespaces
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- extensions
|
||||||
|
resources:
|
||||||
|
- networkpolicies
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- 'networking.k8s.io'
|
||||||
|
resources:
|
||||||
|
- networkpolicies
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- nodes/status
|
||||||
|
verbs:
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
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/v1
|
||||||
|
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/v1
|
||||||
|
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: apps/v1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
# Wait 5 seconds to let pod connect before rolling next pod
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
name: weave-net
|
||||||
|
minReadySeconds: 5
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: weave
|
||||||
|
command:
|
||||||
|
- /home/weave/launch.sh
|
||||||
|
env:
|
||||||
|
- name: HOSTNAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
image: 'weaveworks/weave-kube:latest'
|
||||||
|
imagePullPolicy: Always
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
host: 127.0.0.1
|
||||||
|
path: /status
|
||||||
|
port: 6784
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
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: 50m
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: xtables-lock
|
||||||
|
mountPath: /run/xtables.lock
|
||||||
|
readOnly: false
|
||||||
|
hostNetwork: true
|
||||||
|
dnsPolicy: ClusterFirstWithHostNet
|
||||||
|
hostPID: true
|
||||||
|
restartPolicy: Always
|
||||||
|
securityContext:
|
||||||
|
seLinuxOptions: {}
|
||||||
|
serviceAccountName: weave-net
|
||||||
|
tolerations:
|
||||||
|
- effect: NoSchedule
|
||||||
|
operator: Exists
|
||||||
|
- effect: NoExecute
|
||||||
|
operator: Exists
|
||||||
|
volumes:
|
||||||
|
- name: weavedb
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/weave
|
||||||
|
- name: cni-bin
|
||||||
|
hostPath:
|
||||||
|
path: /opt
|
||||||
|
- name: cni-bin2
|
||||||
|
hostPath:
|
||||||
|
path: /home
|
||||||
|
- name: cni-conf
|
||||||
|
hostPath:
|
||||||
|
path: /etc
|
||||||
|
- name: dbus
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/dbus
|
||||||
|
- name: lib-modules
|
||||||
|
hostPath:
|
||||||
|
path: /lib/modules
|
||||||
|
- name: xtables-lock
|
||||||
|
hostPath:
|
||||||
|
path: /run/xtables.lock
|
||||||
|
type: FileOrCreate
|
||||||
|
priorityClassName: system-node-critical
|
||||||
|
updateStrategy:
|
||||||
|
type: RollingUpdate
|
||||||
@@ -110,15 +110,14 @@ items:
|
|||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
image: 'weaveworks/weave-kube:latest'
|
image: 'weaveworks/weave-kube:latest'
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
path: /status
|
path: /status
|
||||||
port: 6784
|
port: 6784
|
||||||
initialDelaySeconds: 30
|
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: 10m
|
cpu: 50m
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
@@ -145,7 +144,7 @@ items:
|
|||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: 10m
|
cpu: 50m
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
@@ -157,6 +156,8 @@ items:
|
|||||||
tolerations:
|
tolerations:
|
||||||
- effect: NoSchedule
|
- effect: NoSchedule
|
||||||
operator: Exists
|
operator: Exists
|
||||||
|
- effect: NoExecute
|
||||||
|
operator: Exists
|
||||||
volumes:
|
volumes:
|
||||||
- name: weavedb
|
- name: weavedb
|
||||||
hostPath:
|
hostPath:
|
||||||
|
|||||||
@@ -25,14 +25,6 @@ items:
|
|||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
- watch
|
- watch
|
||||||
- apiGroups:
|
|
||||||
- extensions
|
|
||||||
resources:
|
|
||||||
- networkpolicies
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- 'networking.k8s.io'
|
- 'networking.k8s.io'
|
||||||
resources:
|
resources:
|
||||||
@@ -41,6 +33,13 @@ items:
|
|||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
- watch
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- nodes/status
|
||||||
|
verbs:
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
@@ -120,15 +119,14 @@ items:
|
|||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
image: 'weaveworks/weave-kube:latest'
|
image: 'weaveworks/weave-kube:latest'
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
path: /status
|
path: /status
|
||||||
port: 6784
|
port: 6784
|
||||||
initialDelaySeconds: 30
|
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: 10m
|
cpu: 50m
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
@@ -156,10 +154,9 @@ items:
|
|||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
image: 'weaveworks/weave-npc:latest'
|
image: 'weaveworks/weave-npc:latest'
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
#npc-args
|
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: 10m
|
cpu: 50m
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
@@ -175,6 +172,8 @@ items:
|
|||||||
tolerations:
|
tolerations:
|
||||||
- effect: NoSchedule
|
- effect: NoSchedule
|
||||||
operator: Exists
|
operator: Exists
|
||||||
|
- effect: NoExecute
|
||||||
|
operator: Exists
|
||||||
volumes:
|
volumes:
|
||||||
- name: weavedb
|
- name: weavedb
|
||||||
hostPath:
|
hostPath:
|
||||||
|
|||||||
@@ -0,0 +1,210 @@
|
|||||||
|
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/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- pods
|
||||||
|
- namespaces
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- extensions
|
||||||
|
resources:
|
||||||
|
- networkpolicies
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- 'networking.k8s.io'
|
||||||
|
resources:
|
||||||
|
- networkpolicies
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- nodes/status
|
||||||
|
verbs:
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
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/v1
|
||||||
|
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/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
name: weave-net
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
- apiVersion: extensions/v1beta1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
# Wait 5 seconds to let pod connect before rolling next pod
|
||||||
|
minReadySeconds: 5
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: weave
|
||||||
|
command:
|
||||||
|
- /home/weave/launch.sh
|
||||||
|
env:
|
||||||
|
- name: HOSTNAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
image: 'weaveworks/weave-kube:latest'
|
||||||
|
imagePullPolicy: Always
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
host: 127.0.0.1
|
||||||
|
path: /status
|
||||||
|
port: 6784
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
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: 50m
|
||||||
|
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
|
||||||
|
- effect: NoExecute
|
||||||
|
operator: Exists
|
||||||
|
volumes:
|
||||||
|
- name: weavedb
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/weave
|
||||||
|
- name: cni-bin
|
||||||
|
hostPath:
|
||||||
|
path: /opt
|
||||||
|
- name: cni-bin2
|
||||||
|
hostPath:
|
||||||
|
path: /home
|
||||||
|
- name: cni-conf
|
||||||
|
hostPath:
|
||||||
|
path: /etc
|
||||||
|
- name: dbus
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/dbus
|
||||||
|
- name: lib-modules
|
||||||
|
hostPath:
|
||||||
|
path: /lib/modules
|
||||||
|
- name: xtables-lock
|
||||||
|
hostPath:
|
||||||
|
path: /run/xtables.lock
|
||||||
|
type: FileOrCreate
|
||||||
|
updateStrategy:
|
||||||
|
type: RollingUpdate
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
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/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- pods
|
||||||
|
- namespaces
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- extensions
|
||||||
|
resources:
|
||||||
|
- networkpolicies
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- 'networking.k8s.io'
|
||||||
|
resources:
|
||||||
|
- networkpolicies
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- nodes/status
|
||||||
|
verbs:
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
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/v1
|
||||||
|
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/v1
|
||||||
|
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: apps/v1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
# Wait 5 seconds to let pod connect before rolling next pod
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
name: weave-net
|
||||||
|
minReadySeconds: 5
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: weave
|
||||||
|
command:
|
||||||
|
- /home/weave/launch.sh
|
||||||
|
env:
|
||||||
|
- name: HOSTNAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
image: 'weaveworks/weave-kube:latest'
|
||||||
|
imagePullPolicy: Always
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
host: 127.0.0.1
|
||||||
|
path: /status
|
||||||
|
port: 6784
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
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: 50m
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: xtables-lock
|
||||||
|
mountPath: /run/xtables.lock
|
||||||
|
readOnly: false
|
||||||
|
hostNetwork: true
|
||||||
|
dnsPolicy: ClusterFirstWithHostNet
|
||||||
|
hostPID: true
|
||||||
|
restartPolicy: Always
|
||||||
|
securityContext:
|
||||||
|
seLinuxOptions: {}
|
||||||
|
serviceAccountName: weave-net
|
||||||
|
tolerations:
|
||||||
|
- effect: NoSchedule
|
||||||
|
operator: Exists
|
||||||
|
- effect: NoExecute
|
||||||
|
operator: Exists
|
||||||
|
volumes:
|
||||||
|
- name: weavedb
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/weave
|
||||||
|
- name: cni-bin
|
||||||
|
hostPath:
|
||||||
|
path: /opt
|
||||||
|
- name: cni-bin2
|
||||||
|
hostPath:
|
||||||
|
path: /home
|
||||||
|
- name: cni-conf
|
||||||
|
hostPath:
|
||||||
|
path: /etc
|
||||||
|
- name: dbus
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/dbus
|
||||||
|
- name: lib-modules
|
||||||
|
hostPath:
|
||||||
|
path: /lib/modules
|
||||||
|
- name: xtables-lock
|
||||||
|
hostPath:
|
||||||
|
path: /run/xtables.lock
|
||||||
|
type: FileOrCreate
|
||||||
|
updateStrategy:
|
||||||
|
type: RollingUpdate
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: List
|
|
||||||
items:
|
|
||||||
- apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: weave-net
|
|
||||||
labels:
|
|
||||||
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:
|
|
||||||
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:
|
|
||||||
seLinuxOptions: {}
|
|
||||||
serviceAccountName: weave-net
|
|
||||||
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
|
|
||||||
@@ -1,17 +1,24 @@
|
|||||||
FROM ALPINE_BASEIMAGE
|
FROM ALPINE_BASEIMAGE
|
||||||
|
|
||||||
# If we're building for another architecture than amd64, the CROSS_BUILD_ placeholder is removed so e.g. CROSS_BUILD_COPY turns into COPY
|
# These labels are pretty static, and can therefore be added early on:
|
||||||
# If we're building normally, for amd64, CROSS_BUILD lines are removed
|
LABEL works.weave.role="system" \
|
||||||
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
maintainer="Weaveworks <help@weave.works>" \
|
||||||
|
org.opencontainers.image.title="Weave Net" \
|
||||||
LABEL maintainer "Weaveworks Inc <help@weave.works>"
|
org.opencontainers.image.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||||
LABEL works.weave.role=system \
|
org.opencontainers.image.url="https://weave.works" \
|
||||||
|
org.opencontainers.image.source="https://github.com/weaveworks/weave" \
|
||||||
|
org.opencontainers.image.vendor="Weaveworks" \
|
||||||
|
org.label-schema.schema-version="1.0" \
|
||||||
org.label-schema.name="Weave Net" \
|
org.label-schema.name="Weave Net" \
|
||||||
org.label-schema.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
org.label-schema.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||||
org.label-schema.url="https://weave.works" \
|
org.label-schema.url="https://weave.works" \
|
||||||
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
||||||
org.label-schema.vendor="Weaveworks"
|
org.label-schema.vendor="Weaveworks"
|
||||||
|
|
||||||
|
# If we're building for another architecture than amd64, the CROSS_BUILD_ placeholder is removed so e.g. CROSS_BUILD_COPY turns into COPY
|
||||||
|
# If we're building normally, for amd64, CROSS_BUILD lines are removed
|
||||||
|
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
||||||
|
|
||||||
RUN apk add --update \
|
RUN apk add --update \
|
||||||
iptables \
|
iptables \
|
||||||
ipset \
|
ipset \
|
||||||
@@ -20,4 +27,10 @@ RUN apk add --update \
|
|||||||
&& mknod /var/log/ulogd.pcap p
|
&& mknod /var/log/ulogd.pcap p
|
||||||
COPY ./weave-npc /usr/bin/weave-npc
|
COPY ./weave-npc /usr/bin/weave-npc
|
||||||
COPY ./ulogd.conf /etc/ulogd.conf
|
COPY ./ulogd.conf /etc/ulogd.conf
|
||||||
ENTRYPOINT ["/usr/bin/weave-npc"]
|
COPY ./launch.sh /usr/bin/
|
||||||
|
ENTRYPOINT ["/usr/bin/launch.sh"]
|
||||||
|
|
||||||
|
# These labels will change for every build, and should therefore be the last layer of the image:
|
||||||
|
ARG revision
|
||||||
|
LABEL org.opencontainers.image.revision="${revision}" \
|
||||||
|
org.label-schema.vcs-ref="${revision}"
|
||||||
Executable
+36
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Setup iptables backend to be legacy or nftable
|
||||||
|
setup_iptables_backend() {
|
||||||
|
if [ -n "${IPTABLES_BACKEND}" ]; then
|
||||||
|
mode=$IPTABLES_BACKEND
|
||||||
|
else
|
||||||
|
# auto-detect if iptables backend mode to use if not specified explicitly
|
||||||
|
num_legacy_lines=$( (iptables-legacy-save || true) 2>/dev/null | grep '^-' | wc -l)
|
||||||
|
num_nft_lines=$( (iptables-nft-save || true) 2>/dev/null | grep '^-' | wc -l)
|
||||||
|
if [ "${num_legacy_lines}" -ge 10 ]; then
|
||||||
|
mode="legacy"
|
||||||
|
else
|
||||||
|
if [ "${num_legacy_lines}" -ge "${num_nft_lines}" ]; then
|
||||||
|
mode="legacy"
|
||||||
|
else
|
||||||
|
mode="nft"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ $mode = "nft" ]; then
|
||||||
|
rm /sbin/iptables
|
||||||
|
rm /sbin/iptables-save
|
||||||
|
rm /sbin/iptables-restore
|
||||||
|
ln -s /sbin/iptables-nft /sbin/iptables
|
||||||
|
ln -s /sbin/iptables-nft-save /sbin/iptables-save
|
||||||
|
ln -s /sbin/iptables-nft-restore /sbin/iptables-restore
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_iptables_backend
|
||||||
|
|
||||||
|
# Start weave-npc with any flags specified in $EXTRA_ARGS as well as any flags passed to this container (for backwards compatibility)
|
||||||
|
exec /usr/bin/weave-npc $EXTRA_ARGS $@
|
||||||
+119
-26
@@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
coreapi "k8s.io/api/core/v1"
|
coreapi "k8s.io/api/core/v1"
|
||||||
extnapi "k8s.io/api/extensions/v1beta1"
|
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@@ -18,19 +17,21 @@ import (
|
|||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
"github.com/weaveworks/weave/common"
|
||||||
|
"github.com/weaveworks/weave/net"
|
||||||
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
"github.com/weaveworks/weave/npc"
|
"github.com/weaveworks/weave/npc"
|
||||||
"github.com/weaveworks/weave/npc/ipset"
|
|
||||||
"github.com/weaveworks/weave/npc/metrics"
|
"github.com/weaveworks/weave/npc/metrics"
|
||||||
"github.com/weaveworks/weave/npc/ulogd"
|
"github.com/weaveworks/weave/npc/ulogd"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "unreleased"
|
version = "unreleased"
|
||||||
metricsAddr string
|
metricsAddr string
|
||||||
logLevel string
|
logLevel string
|
||||||
allowMcast bool
|
allowMcast bool
|
||||||
nodeName string
|
nodeName string
|
||||||
legacy bool
|
maxList int
|
||||||
|
bridgePortName string
|
||||||
)
|
)
|
||||||
|
|
||||||
func handleError(err error) { common.CheckFatal(err) }
|
func handleError(err error) { common.CheckFatal(err) }
|
||||||
@@ -52,11 +53,31 @@ func resetIPTables(ipt *iptables.IPTables) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ipt.ClearChain(npc.TableFilter, npc.MainChain)
|
if err := ipt.ClearChain(npc.TableFilter, npc.MainChain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipt.ClearChain(npc.TableFilter, npc.EgressMarkChain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipt.ClearChain(npc.TableFilter, npc.EgressCustomChain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipt.ClearChain(npc.TableFilter, npc.EgressDefaultChain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do not clear npc.EgressChain here because otherwise, in the case of restarting
|
||||||
|
// weave-npc process, all egress traffic is allowed for a short period of time.
|
||||||
|
// The chain is created in createBaseRules.
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetIPSets(ips ipset.Interface) error {
|
func resetIPSets(ips ipset.Interface) error {
|
||||||
// Remove ipsets prefixed `weave-` only
|
// Remove ipsets prefixed `weave-` only.
|
||||||
|
|
||||||
sets, err := ips.List(npc.IpsetNamePrefix)
|
sets, err := ips.List(npc.IpsetNamePrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -76,6 +97,11 @@ func resetIPSets(ips ipset.Interface) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range sets {
|
for _, s := range sets {
|
||||||
|
// LocalIPset might be used by WEAVE-NPC-EGRESS chain which we do not
|
||||||
|
// flush, so we cannot destroy it.
|
||||||
|
if s == npc.LocalIpset {
|
||||||
|
continue
|
||||||
|
}
|
||||||
common.Log.Debugf("Destroying ipset '%s'", string(s))
|
common.Log.Debugf("Destroying ipset '%s'", string(s))
|
||||||
if err := ips.Destroy(s); err != nil {
|
if err := ips.Destroy(s); err != nil {
|
||||||
common.Log.Errorf("Failed to destroy ipset '%s'", string(s))
|
common.Log.Errorf("Failed to destroy ipset '%s'", string(s))
|
||||||
@@ -100,6 +126,12 @@ func createBaseRules(ipt *iptables.IPTables, ips ipset.Interface) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the destination address is not any of the local pods, let it through
|
||||||
|
if err := ipt.Append(npc.TableFilter, npc.MainChain,
|
||||||
|
"-m", "physdev", "--physdev-is-bridged", "--physdev-out="+bridgePortName, "-j", "ACCEPT"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := ipt.Append(npc.TableFilter, npc.MainChain,
|
if err := ipt.Append(npc.TableFilter, npc.MainChain,
|
||||||
"-m", "state", "--state", "NEW", "-j", string(npc.DefaultChain)); err != nil {
|
"-m", "state", "--state", "NEW", "-j", string(npc.DefaultChain)); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -110,12 +142,80 @@ func createBaseRules(ipt *iptables.IPTables, ips ipset.Interface) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the destination address is not any of the local pods, let it through
|
if err := ipt.Append(npc.TableFilter, npc.EgressMarkChain,
|
||||||
if err := ips.Create(npc.LocalIpset, ipset.HashIP); err != nil {
|
"-j", "MARK", "--set-xmark", npc.EgressMark); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ipt.Append(npc.TableFilter, npc.MainChain,
|
|
||||||
"-m", "set", "!", "--match-set", npc.LocalIpset, "dst", "-j", "ACCEPT")
|
// Egress rules:
|
||||||
|
//
|
||||||
|
// -A WEAVE-NPC-EGRESS -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||||
|
// -A WEAVE-NPC-EGRESS -m physdev --physdev-in vethwe-bridge --physdev-is-bridged -j RETURN
|
||||||
|
// -A WEAVE-NPC-EGRESS -m addrtype --dst-type LOCAL -j RETURN
|
||||||
|
// -A WEAVE-NPC-EGRESS -m state --state NEW -j WEAVE-NPC-EGRESS-DEFAULT
|
||||||
|
// -A WEAVE-NPC-EGRESS -m state --state NEW -m mark ! --mark 0x40000/0x40000 -j WEAVE-NPC-EGRESS-CUSTOM
|
||||||
|
// -A WEAVE-NPC-EGRESS -m state --state NEW -m mark ! --mark 0x40000/0x40000 -j NFLOG --nflog-group 86
|
||||||
|
// -A WEAVE-NPC-EGRESS -m mark ! --mark 0x40000/0x40000 -j DROP
|
||||||
|
//
|
||||||
|
// -A WEAVE-NPC-EGRESS-CUSTOM <rulespec> -j MARK --set-xmark 0x40000/0x40000
|
||||||
|
// -A WEAVE-NPC-EGRESS-CUSTOM <rulespec> -j RETURN
|
||||||
|
//
|
||||||
|
// -A WEAVE-NPC-EGRESS-DEFAULT <rulespec> -j MARK --set-xmark 0x40000/0x40000
|
||||||
|
// -A WEAVE-NPC-EGRESS-DEFAULT <rulespec> -j RETURN
|
||||||
|
//
|
||||||
|
// For each rule we create two (mark and return). We cannot just accept
|
||||||
|
// a packet if it matches any rule, as a packet might need to traverse
|
||||||
|
// the ingress npc as well which happens later in the chain (in some cases
|
||||||
|
// we cannot detect whether packet is ingress or egress, so we need to
|
||||||
|
// check both chains).
|
||||||
|
|
||||||
|
ruleSpecs := [][]string{
|
||||||
|
{"-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"},
|
||||||
|
// skip running through egress network policies for the traffic not coming from local pods
|
||||||
|
{"-m", "physdev", "--physdev-is-bridged", "--physdev-in=" + bridgePortName, "-j", "RETURN"},
|
||||||
|
// skip running through egress network policies for the traffic bound for IP address assigned for the bridge
|
||||||
|
{"-m", "addrtype", "--dst-type", "LOCAL", "-j", "RETURN"},
|
||||||
|
}
|
||||||
|
if allowMcast {
|
||||||
|
ruleSpecs = append(ruleSpecs, []string{"-d", "224.0.0.0/4", "-j", "RETURN"})
|
||||||
|
}
|
||||||
|
ruleSpecs = append(ruleSpecs, [][]string{
|
||||||
|
{"-m", "state", "--state", "NEW", "-j", string(npc.EgressDefaultChain)},
|
||||||
|
{"-m", "state", "--state", "NEW", "-m", "mark", "!", "--mark", npc.EgressMark, "-j", string(npc.EgressCustomChain)},
|
||||||
|
{"-m", "state", "--state", "NEW", "-m", "mark", "!", "--mark", npc.EgressMark, "-j", "NFLOG", "--nflog-group", "86"},
|
||||||
|
}...)
|
||||||
|
if err := net.AddChainWithRules(ipt, npc.TableFilter, npc.EgressChain, ruleSpecs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete `weave-local-pods` ipset which is no longer used by weave-npc
|
||||||
|
weaveLocalPodExist, err := ipsetExist(ips, npc.LocalIpset)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Errorf("Failed to look if ipset '%s' exists", npc.LocalIpset)
|
||||||
|
} else if weaveLocalPodExist {
|
||||||
|
common.Log.Debugf("Destroying ipset '%s'", npc.LocalIpset)
|
||||||
|
if err := ips.Destroy(npc.LocalIpset); err != nil {
|
||||||
|
common.Log.Errorf("Failed to destroy ipset '%s'", npc.LocalIpset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dummy way to check whether a given ipset exists.
|
||||||
|
// TODO(brb) Use "ipset -exist create <..>" for our purpose instead (for some reasons
|
||||||
|
// creating an ipset with -exist fails).
|
||||||
|
func ipsetExist(ips ipset.Interface, name ipset.Name) (bool, error) {
|
||||||
|
sets, err := ips.List(string(name))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
for _, s := range sets {
|
||||||
|
if s == name {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func root(cmd *cobra.Command, args []string) {
|
func root(cmd *cobra.Command, args []string) {
|
||||||
@@ -131,10 +231,6 @@ func root(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
common.Log.Infof("Starting Weaveworks NPC %s; node name %q", version, nodeName)
|
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 {
|
if err := metrics.Start(metricsAddr); err != nil {
|
||||||
common.Log.Fatalf("Failed to start metrics: %v", err)
|
common.Log.Fatalf("Failed to start metrics: %v", err)
|
||||||
}
|
}
|
||||||
@@ -152,13 +248,13 @@ func root(cmd *cobra.Command, args []string) {
|
|||||||
ipt, err := iptables.New()
|
ipt, err := iptables.New()
|
||||||
handleError(err)
|
handleError(err)
|
||||||
|
|
||||||
ips := ipset.New(common.LogLogger())
|
ips := ipset.New(common.LogLogger(), maxList)
|
||||||
|
|
||||||
handleError(resetIPTables(ipt))
|
handleError(resetIPTables(ipt))
|
||||||
handleError(resetIPSets(ips))
|
handleError(resetIPSets(ips))
|
||||||
handleError(createBaseRules(ipt, ips))
|
handleError(createBaseRules(ipt, ips))
|
||||||
|
|
||||||
npc := npc.New(nodeName, legacy, ipt, ips)
|
npc := npc.New(nodeName, ipt, ips, client)
|
||||||
|
|
||||||
nsController := makeController(client.Core().RESTClient(), "namespaces", &coreapi.Namespace{},
|
nsController := makeController(client.Core().RESTClient(), "namespaces", &coreapi.Namespace{},
|
||||||
cache.ResourceEventHandlerFuncs{
|
cache.ResourceEventHandlerFuncs{
|
||||||
@@ -219,11 +315,7 @@ func root(cmd *cobra.Command, args []string) {
|
|||||||
handleError(npc.UpdateNetworkPolicy(old, new))
|
handleError(npc.UpdateNetworkPolicy(old, new))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if legacy {
|
npController = makeController(client.NetworkingV1().RESTClient(), "networkpolicies", &networkingv1.NetworkPolicy{}, npHandlers)
|
||||||
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 nsController.Run(wait.NeverStop)
|
||||||
go podController.Run(wait.NeverStop)
|
go podController.Run(wait.NeverStop)
|
||||||
@@ -244,7 +336,8 @@ func main() {
|
|||||||
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "debug", "logging level (debug, info, warning, error)")
|
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "debug", "logging level (debug, info, warning, error)")
|
||||||
rootCmd.PersistentFlags().BoolVar(&allowMcast, "allow-mcast", true, "allow all multicast traffic")
|
rootCmd.PersistentFlags().BoolVar(&allowMcast, "allow-mcast", true, "allow all multicast traffic")
|
||||||
rootCmd.PersistentFlags().StringVar(&nodeName, "node-name", "", "only generate rules that apply to this node")
|
rootCmd.PersistentFlags().StringVar(&nodeName, "node-name", "", "only generate rules that apply to this node")
|
||||||
rootCmd.PersistentFlags().BoolVar(&legacy, "use-legacy-netpol", false, "use legacy network policies (pre k8s 1.7 vsn)")
|
rootCmd.PersistentFlags().IntVar(&maxList, "max-list-size", 1024, "maximum size of ipset list (for namespaces)")
|
||||||
|
rootCmd.PersistentFlags().StringVar(&bridgePortName, "bridge-port-name", "vethwe-bridge", "name of the brige port on which packets are received and sent")
|
||||||
|
|
||||||
handleError(rootCmd.Execute())
|
handleError(rootCmd.Execute())
|
||||||
}
|
}
|
||||||
+12
-2
@@ -1,7 +1,17 @@
|
|||||||
# This is a nearly-empty image that we use to create a data-only container for persistence
|
# This is a nearly-empty image that we use to create a data-only container for persistence
|
||||||
FROM scratch
|
FROM scratch
|
||||||
LABEL maintainer "Weaveworks Inc <help@weave.works>"
|
|
||||||
LABEL works.weave.role=system
|
# These labels are pretty static, and can therefore be added early on:
|
||||||
|
LABEL works.weave.role="system" \
|
||||||
|
maintainer="Weaveworks <help@weave.works>" \
|
||||||
|
org.opencontainers.image.title="weavedb" \
|
||||||
|
org.opencontainers.image.source="https://github.com/weaveworks/weave" \
|
||||||
|
org.opencontainers.image.vendor="Weaveworks"
|
||||||
|
|
||||||
ENTRYPOINT ["data-only"]
|
ENTRYPOINT ["data-only"]
|
||||||
# Work round Docker refusing to save an empty image
|
# Work round Docker refusing to save an empty image
|
||||||
COPY Dockerfile /
|
COPY Dockerfile /
|
||||||
|
|
||||||
|
# This label will change for every build, and should therefore be the last layer of the image:
|
||||||
|
ARG revision
|
||||||
|
LABEL org.opencontainers.image.revision="${revision}"
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
FROM DOCKERHUB_USER/weaveARCH_EXT
|
FROM DOCKERHUB_USER/weaveARCH_EXT
|
||||||
|
|
||||||
|
# These labels are pretty static, and can therefore be added early on:
|
||||||
|
LABEL maintainer="Weaveworks <help@weave.works>" \
|
||||||
|
org.opencontainers.image.title="weaveexec" \
|
||||||
|
org.opencontainers.image.source="https://github.com/weaveworks/weave" \
|
||||||
|
org.opencontainers.image.vendor="Weaveworks"
|
||||||
|
|
||||||
ENTRYPOINT ["/home/weave/sigproxy", "/home/weave/weave"]
|
ENTRYPOINT ["/home/weave/sigproxy", "/home/weave/weave"]
|
||||||
|
|
||||||
ADD ./sigproxy ./symlink /home/weave/
|
ADD ./sigproxy ./symlink /home/weave/
|
||||||
@@ -7,3 +13,7 @@ ADD ./weavewait /w/w
|
|||||||
ADD ./weavewait_noop /w-noop/w
|
ADD ./weavewait_noop /w-noop/w
|
||||||
ADD ./weavewait_nomcast /w-nomcast/w
|
ADD ./weavewait_nomcast /w-nomcast/w
|
||||||
WORKDIR /home/weave
|
WORKDIR /home/weave
|
||||||
|
|
||||||
|
# This label will change for every build, and should therefore be the last layer of the image:
|
||||||
|
ARG revision
|
||||||
|
LABEL org.opencontainers.image.revision="${revision}"
|
||||||
@@ -1,17 +1,24 @@
|
|||||||
FROM ALPINE_BASEIMAGE
|
FROM ALPINE_BASEIMAGE
|
||||||
|
|
||||||
# If we're building for another architecture than amd64, the CROSS_BUILD_ placeholder is removed so e.g. CROSS_BUILD_COPY turns into COPY
|
# These labels are pretty static, and can therefore be added early on:
|
||||||
# If we're building normally, for amd64, CROSS_BUILD lines are removed
|
LABEL works.weave.role="system" \
|
||||||
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
maintainer="Weaveworks <help@weave.works>" \
|
||||||
|
org.opencontainers.image.title="Weave Net" \
|
||||||
LABEL maintainer "Weaveworks Inc <help@weave.works>"
|
org.opencontainers.image.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||||
LABEL works.weave.role=system \
|
org.opencontainers.image.url="https://weave.works" \
|
||||||
|
org.opencontainers.image.source="https://github.com/weaveworks/weave" \
|
||||||
|
org.opencontainers.image.vendor="Weaveworks" \
|
||||||
|
org.label-schema.schema-version="1.0" \
|
||||||
org.label-schema.name="Weave Net" \
|
org.label-schema.name="Weave Net" \
|
||||||
org.label-schema.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
org.label-schema.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||||
org.label-schema.url="https://weave.works" \
|
org.label-schema.url="https://weave.works" \
|
||||||
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
||||||
org.label-schema.vendor="Weaveworks"
|
org.label-schema.vendor="Weaveworks"
|
||||||
|
|
||||||
|
# If we're building for another architecture than amd64, the CROSS_BUILD_ placeholder is removed so e.g. CROSS_BUILD_COPY turns into COPY
|
||||||
|
# If we're building normally, for amd64, CROSS_BUILD lines are removed
|
||||||
|
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
||||||
|
|
||||||
RUN apk add --update \
|
RUN apk add --update \
|
||||||
curl \
|
curl \
|
||||||
ethtool \
|
ethtool \
|
||||||
@@ -29,3 +36,8 @@ ADD ./weaveutil /usr/bin/
|
|||||||
ADD weavedata.db /weavedb/
|
ADD weavedata.db /weavedb/
|
||||||
ENTRYPOINT ["/home/weave/weaver"]
|
ENTRYPOINT ["/home/weave/weaver"]
|
||||||
WORKDIR /home/weave
|
WORKDIR /home/weave
|
||||||
|
|
||||||
|
# These labels will change for every build, and should therefore be the last layer of the image:
|
||||||
|
ARG revision
|
||||||
|
LABEL org.opencontainers.image.revision="${revision}" \
|
||||||
|
org.label-schema.vcs-ref="${revision}"
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/weaveworks/go-checkpoint"
|
checkpoint "github.com/weaveworks/go-checkpoint"
|
||||||
weave "github.com/weaveworks/weave/router"
|
weave "github.com/weaveworks/weave/router"
|
||||||
|
api "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
var checker *checkpoint.Checker
|
var checker *checkpoint.Checker
|
||||||
@@ -14,10 +19,12 @@ var newVersion atomic.Value
|
|||||||
var success atomic.Value
|
var success atomic.Value
|
||||||
|
|
||||||
const (
|
const (
|
||||||
updateCheckPeriod = 6 * time.Hour
|
updateCheckPeriod = 6 * time.Hour
|
||||||
|
configMapName = "weave-net"
|
||||||
|
configMapNamespace = "kube-system"
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkForUpdates(dockerVersion string, router *weave.NetworkRouter) {
|
func checkForUpdates(dockerVersion string, router *weave.NetworkRouter, clusterSize uint) {
|
||||||
newVersion.Store("")
|
newVersion.Store("")
|
||||||
success.Store(true)
|
success.Store(true)
|
||||||
|
|
||||||
@@ -53,6 +60,8 @@ func checkForUpdates(dockerVersion string, router *weave.NetworkRouter) {
|
|||||||
"kernel-version": kernelVersion,
|
"kernel-version": kernelVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkpointKubernetes(flags, clusterSize)
|
||||||
|
|
||||||
// Start background version checking
|
// Start background version checking
|
||||||
params := checkpoint.CheckParams{
|
params := checkpoint.CheckParams{
|
||||||
Product: "weave-net",
|
Product: "weave-net",
|
||||||
@@ -77,3 +86,37 @@ func checkpointFlags(router *weave.NetworkRouter) []checkpoint.Flag {
|
|||||||
}
|
}
|
||||||
return flags
|
return flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkpoint Kubernetes specific details
|
||||||
|
func checkpointKubernetes(flags map[string]string, clusterSize uint) {
|
||||||
|
// checks if weaver is running in Kubernetes
|
||||||
|
host := os.Getenv("KUBERNETES_SERVICE_HOST")
|
||||||
|
if len(host) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config, err := rest.InClusterConfig()
|
||||||
|
if err != nil {
|
||||||
|
Log.Printf("Could not get Kubernetes in-cluster config: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c, err := kubernetes.NewForConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
Log.Printf("Could not make Kubernetes client: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
k8sVersion, err := c.Discovery().ServerVersion()
|
||||||
|
if err != nil {
|
||||||
|
Log.Printf("Could not get Kubernetes version: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
flags["kubernetes-version"] = k8sVersion.String()
|
||||||
|
|
||||||
|
// use UID of `weave-net` configmap as unique ID of the Kubenerets cluster
|
||||||
|
cm, err := c.CoreV1().ConfigMaps(configMapNamespace).Get(configMapName, api.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
Log.Printf("Unable to fetch ConfigMap %s/%s to infer unique cluster ID", configMapNamespace, configMapName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
flags["kubernetes-cluster-uid"] = string(cm.ObjectMeta.UID)
|
||||||
|
flags["kubernetes-cluster-size"] = fmt.Sprint(clusterSize)
|
||||||
|
}
|
||||||
+7
-2
@@ -329,7 +329,7 @@ func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRout
|
|||||||
w.Write(json)
|
w.Write(json)
|
||||||
})
|
})
|
||||||
|
|
||||||
muxRouter.Methods("GET").Path("/report").Queries("format", "{format}").HandlerFunc(
|
muxRouter.Methods("GET").Path("/report").HandlerFunc(
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
funcs := template.FuncMap{
|
funcs := template.FuncMap{
|
||||||
"json": func(v interface{}) string {
|
"json": func(v interface{}) string {
|
||||||
@@ -337,7 +337,12 @@ func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRout
|
|||||||
return string(a)
|
return string(a)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
formatTemplate, err := template.New("format").Funcs(funcs).Parse(mux.Vars(r)["format"])
|
format, ok := r.URL.Query()["format"]
|
||||||
|
if !ok || len(format) != 1 {
|
||||||
|
http.Error(w, "error: must supply one format parameter", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
formatTemplate, err := template.New("format").Funcs(funcs).Parse(format[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
|||||||
+26
-9
@@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/weaveworks/weave/nameserver"
|
"github.com/weaveworks/weave/nameserver"
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
"github.com/weaveworks/weave/net/address"
|
"github.com/weaveworks/weave/net/address"
|
||||||
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
"github.com/weaveworks/weave/plugin"
|
"github.com/weaveworks/weave/plugin"
|
||||||
weaveproxy "github.com/weaveworks/weave/proxy"
|
weaveproxy "github.com/weaveworks/weave/proxy"
|
||||||
weave "github.com/weaveworks/weave/router"
|
weave "github.com/weaveworks/weave/router"
|
||||||
@@ -155,6 +156,7 @@ func main() {
|
|||||||
noDiscovery bool
|
noDiscovery bool
|
||||||
httpAddr string
|
httpAddr string
|
||||||
statusAddr string
|
statusAddr string
|
||||||
|
metricsAddr string
|
||||||
ipamConfig ipamConfig
|
ipamConfig ipamConfig
|
||||||
dockerAPI string
|
dockerAPI string
|
||||||
peers []string
|
peers []string
|
||||||
@@ -186,12 +188,13 @@ func main() {
|
|||||||
mflag.StringVar(&logLevel, []string{"-log-level"}, "info", "logging level (debug, info, warning, error)")
|
mflag.StringVar(&logLevel, []string{"-log-level"}, "info", "logging level (debug, info, warning, error)")
|
||||||
mflag.BoolVar(&pktdebug, []string{"-pkt-debug"}, false, "enable per-packet debug logging")
|
mflag.BoolVar(&pktdebug, []string{"-pkt-debug"}, false, "enable per-packet debug logging")
|
||||||
mflag.StringVar(&prof, []string{"-profile"}, "", "enable profiling and write profiles to given path")
|
mflag.StringVar(&prof, []string{"-profile"}, "", "enable profiling and write profiles to given path")
|
||||||
mflag.IntVar(&config.ConnLimit, []string{"-conn-limit"}, 100, "connection limit (0 for unlimited)")
|
mflag.IntVar(&config.ConnLimit, []string{"-conn-limit"}, 200, "connection limit (0 for unlimited)")
|
||||||
mflag.BoolVar(&noDiscovery, []string{"-no-discovery"}, false, "disable peer discovery")
|
mflag.BoolVar(&noDiscovery, []string{"-no-discovery"}, false, "disable peer discovery")
|
||||||
mflag.IntVar(&bufSzMB, []string{"-bufsz"}, 8, "capture buffer size in MB")
|
mflag.IntVar(&bufSzMB, []string{"-bufsz"}, 8, "capture buffer size in MB")
|
||||||
mflag.IntVar(&bridgeConfig.MTU, []string{"-mtu"}, 0, "MTU size")
|
mflag.IntVar(&bridgeConfig.MTU, []string{"-mtu"}, 0, "MTU size")
|
||||||
mflag.StringVar(&httpAddr, []string{"-http-addr"}, "", "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
|
mflag.StringVar(&httpAddr, []string{"-http-addr"}, "", "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||||
mflag.StringVar(&statusAddr, []string{"-status-addr"}, "", "address to bind status+metrics interface to (disabled if blank, absolute path indicates unix domain socket)")
|
mflag.StringVar(&statusAddr, []string{"-status-addr"}, "", "address to bind status+metrics interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||||
|
mflag.StringVar(&metricsAddr, []string{"-metrics-addr"}, "", "address to bind metrics interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||||
mflag.StringVar(&ipamConfig.Mode, []string{"-ipalloc-init"}, "", "allocator initialisation strategy (consensus, seed or observer)")
|
mflag.StringVar(&ipamConfig.Mode, []string{"-ipalloc-init"}, "", "allocator initialisation strategy (consensus, seed or observer)")
|
||||||
mflag.StringVar(&ipamConfig.IPRangeCIDR, []string{"-ipalloc-range"}, "", "IP address range reserved for automatic allocation, in CIDR notation")
|
mflag.StringVar(&ipamConfig.IPRangeCIDR, []string{"-ipalloc-range"}, "", "IP address range reserved for automatic allocation, in CIDR notation")
|
||||||
mflag.StringVar(&ipamConfig.IPSubnetCIDR, []string{"-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
|
mflag.StringVar(&ipamConfig.IPSubnetCIDR, []string{"-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
|
||||||
@@ -219,6 +222,7 @@ func main() {
|
|||||||
mflag.BoolVar(&pluginConfig.EnableV2Multicast, []string{"-plugin-v2-multicast"}, false, "enable multicast for Docker plugin (v2)")
|
mflag.BoolVar(&pluginConfig.EnableV2Multicast, []string{"-plugin-v2-multicast"}, false, "enable multicast for Docker plugin (v2)")
|
||||||
mflag.StringVar(&pluginConfig.Socket, []string{"-plugin-socket"}, "/run/docker/plugins/weave.sock", "plugin socket on which to listen")
|
mflag.StringVar(&pluginConfig.Socket, []string{"-plugin-socket"}, "/run/docker/plugins/weave.sock", "plugin socket on which to listen")
|
||||||
mflag.StringVar(&pluginConfig.MeshSocket, []string{"-plugin-mesh-socket"}, "/run/docker/plugins/weavemesh.sock", "plugin socket on which to listen in mesh mode")
|
mflag.StringVar(&pluginConfig.MeshSocket, []string{"-plugin-mesh-socket"}, "/run/docker/plugins/weavemesh.sock", "plugin socket on which to listen in mesh mode")
|
||||||
|
mflag.BoolVar(&bridgeConfig.NoMasqLocal, []string{"-no-masq-local"}, false, "do not SNAT external traffic sent to containers running on this node")
|
||||||
|
|
||||||
proxyConfig := newProxyConfig()
|
proxyConfig := newProxyConfig()
|
||||||
|
|
||||||
@@ -303,7 +307,8 @@ func main() {
|
|||||||
|
|
||||||
bridgeConfig.Mac = name.String()
|
bridgeConfig.Mac = name.String()
|
||||||
bridgeConfig.Port = config.Port
|
bridgeConfig.Port = config.Port
|
||||||
bridgeType, err := weavenet.EnsureBridge(procPath, &bridgeConfig, Log)
|
ips := ipset.New(common.LogLogger(), 0)
|
||||||
|
bridgeType, err := weavenet.EnsureBridge(procPath, &bridgeConfig, Log, ips)
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
Log.Println("Bridge type is", bridgeType)
|
Log.Println("Bridge type is", bridgeType)
|
||||||
|
|
||||||
@@ -334,6 +339,9 @@ func main() {
|
|||||||
if bridgeConfig.AWSVPC && !ipamConfig.Enabled() {
|
if bridgeConfig.AWSVPC && !ipamConfig.Enabled() {
|
||||||
Log.Fatalf("--awsvpc mode requires IPAM enabled")
|
Log.Fatalf("--awsvpc mode requires IPAM enabled")
|
||||||
}
|
}
|
||||||
|
if bridgeConfig.AWSVPC && bridgeConfig.NoMasqLocal {
|
||||||
|
Log.Fatalf("--awsvpc mode is not compatible with the --no-masq-local option")
|
||||||
|
}
|
||||||
|
|
||||||
db, err := db.NewBoltDB(dbPrefix)
|
db, err := db.NewBoltDB(dbPrefix)
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
@@ -377,7 +385,7 @@ func main() {
|
|||||||
dockerVersion = dockerCli.DockerVersion()
|
dockerVersion = dockerCli.DockerVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
checkForUpdates(dockerVersion, router)
|
checkForUpdates(dockerVersion, router, uint(len(peers)))
|
||||||
|
|
||||||
observeContainers := func(o docker.ContainerObserver) {
|
observeContainers := func(o docker.ContainerObserver) {
|
||||||
if dockerCli != nil {
|
if dockerCli != nil {
|
||||||
@@ -393,17 +401,19 @@ func main() {
|
|||||||
var (
|
var (
|
||||||
allocator *ipam.Allocator
|
allocator *ipam.Allocator
|
||||||
defaultSubnet address.CIDR
|
defaultSubnet address.CIDR
|
||||||
trackerName string
|
|
||||||
)
|
)
|
||||||
if ipamConfig.Enabled() {
|
if ipamConfig.Enabled() {
|
||||||
var t tracker.LocalRangeTracker
|
var t tracker.LocalRangeTracker
|
||||||
if bridgeConfig.AWSVPC {
|
if bridgeConfig.AWSVPC {
|
||||||
Log.Infoln("Creating AWSVPC LocalRangeTracker")
|
|
||||||
t, err = tracker.NewAWSVPCTracker(bridgeConfig.WeaveBridgeName)
|
t, err = tracker.NewAWSVPCTracker(bridgeConfig.WeaveBridgeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatalf("Cannot create AWSVPC LocalRangeTracker: %s", err)
|
Log.Fatalf("Cannot create AWSVPC LocalRangeTracker: %s", err)
|
||||||
}
|
}
|
||||||
trackerName = "awsvpc"
|
} else if bridgeConfig.NoMasqLocal {
|
||||||
|
t = weavenet.NewNoMasqLocalTracker(ips)
|
||||||
|
}
|
||||||
|
if t != nil {
|
||||||
|
Log.Infof("Using %q LocalRangeTracker", t)
|
||||||
}
|
}
|
||||||
|
|
||||||
preClaims, err := findExistingAddresses(dockerCli, bridgeConfig.WeaveBridgeName)
|
preClaims, err := findExistingAddresses(dockerCli, bridgeConfig.WeaveBridgeName)
|
||||||
@@ -451,7 +461,7 @@ func main() {
|
|||||||
if httpAddr != "" {
|
if httpAddr != "" {
|
||||||
muxRouter := mux.NewRouter()
|
muxRouter := mux.NewRouter()
|
||||||
if allocator != nil {
|
if allocator != nil {
|
||||||
allocator.HandleHTTP(muxRouter, defaultSubnet, trackerName, dockerCli)
|
allocator.HandleHTTP(muxRouter, defaultSubnet, dockerCli)
|
||||||
}
|
}
|
||||||
if ns != nil {
|
if ns != nil {
|
||||||
ns.HandleHTTP(muxRouter, dockerCli)
|
ns.HandleHTTP(muxRouter, dockerCli)
|
||||||
@@ -474,10 +484,17 @@ func main() {
|
|||||||
muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver))
|
muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver))
|
||||||
statusMux := http.NewServeMux()
|
statusMux := http.NewServeMux()
|
||||||
statusMux.Handle("/", muxRouter)
|
statusMux.Handle("/", muxRouter)
|
||||||
Log.Println("Listening for metrics requests on", statusAddr)
|
Log.Println("Listening for status+metrics requests on", statusAddr)
|
||||||
go listenAndServeHTTP(statusAddr, statusMux)
|
go listenAndServeHTTP(statusAddr, statusMux)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if metricsAddr != "" {
|
||||||
|
metricsMux := http.NewServeMux()
|
||||||
|
metricsMux.Handle("/metrics", metricsHandler(router, allocator, ns, dnsserver))
|
||||||
|
Log.Println("Listening for metrics requests on", metricsAddr)
|
||||||
|
go listenAndServeHTTP(metricsAddr, metricsMux)
|
||||||
|
}
|
||||||
|
|
||||||
if plugin != nil {
|
if plugin != nil {
|
||||||
go plugin.Start(httpAddr, dockerCli, waitReady.Add())
|
go plugin.Start(httpAddr, dockerCli, waitReady.Add())
|
||||||
}
|
}
|
||||||
@@ -496,7 +513,7 @@ func exposeForAWSVPC(alloc *ipam.Allocator, subnet address.CIDR, bridgeName stri
|
|||||||
addr, err := alloc.Allocate("weave:expose", subnet, false, func() bool { return false })
|
addr, err := alloc.Allocate("weave:expose", subnet, false, func() bool { return false })
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
cidr := address.MakeCIDR(subnet, addr)
|
cidr := address.MakeCIDR(subnet, addr)
|
||||||
err = weavenet.Expose(bridgeName, cidr.IPNet(), true, false)
|
err = weavenet.Expose(bridgeName, cidr.IPNet(), true, false, false)
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
Log.Printf("Bridge %q exposed on address %v", bridgeName, cidr)
|
Log.Printf("Bridge %q exposed on address %v", bridgeName, cidr)
|
||||||
ready()
|
ready()
|
||||||
|
|||||||
@@ -89,8 +89,9 @@ func containerFQDN(args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runContainer(args []string) error {
|
func parseContainerArgs(args []string) docker.CreateContainerOptions {
|
||||||
env := []string{}
|
env := []string{}
|
||||||
|
labels := map[string]string{}
|
||||||
name := ""
|
name := ""
|
||||||
net := ""
|
net := ""
|
||||||
pid := ""
|
pid := ""
|
||||||
@@ -109,6 +110,10 @@ func runContainer(args []string) error {
|
|||||||
env = append(env, args[i+1])
|
env = append(env, args[i+1])
|
||||||
}
|
}
|
||||||
args = append(args[:i], args[i+2:]...)
|
args = append(args[:i], args[i+2:]...)
|
||||||
|
case "-l", "--label":
|
||||||
|
key, value := parseLabel(args[i+1])
|
||||||
|
labels[key] = value
|
||||||
|
args = append(args[:i], args[i+2:]...)
|
||||||
case "--name":
|
case "--name":
|
||||||
name = args[i+1]
|
name = args[i+1]
|
||||||
args = append(args[:i], args[i+2:]...)
|
args = append(args[:i], args[i+2:]...)
|
||||||
@@ -161,19 +166,25 @@ func runContainer(args []string) error {
|
|||||||
image := args[0]
|
image := args[0]
|
||||||
cmds := args[1:]
|
cmds := args[1:]
|
||||||
|
|
||||||
|
config := docker.Config{Image: image, Env: env, Cmd: cmds, Labels: labels}
|
||||||
|
hostConfig := docker.HostConfig{NetworkMode: net, PidMode: pid, Privileged: privileged, RestartPolicy: restart, Binds: volumes, VolumesFrom: volumesFrom}
|
||||||
|
return docker.CreateContainerOptions{Name: name, Config: &config, HostConfig: &hostConfig}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runContainer(args []string) error {
|
||||||
|
containerOptions := parseContainerArgs(args)
|
||||||
|
|
||||||
c, err := docker.NewVersionedClientFromEnv("1.18")
|
c, err := docker.NewVersionedClientFromEnv("1.18")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to connect to docker: %s", err)
|
return fmt.Errorf("unable to connect to docker: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config := docker.Config{Image: image, Env: env, Cmd: cmds}
|
container, err := c.CreateContainer(containerOptions)
|
||||||
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create container: %s", err)
|
return fmt.Errorf("unable to create container: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.StartContainer(container.ID, &hostConfig)
|
err = c.StartContainer(container.ID, containerOptions.HostConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to start container: %s", err)
|
return fmt.Errorf("unable to start container: %s", err)
|
||||||
}
|
}
|
||||||
@@ -182,6 +193,14 @@ func runContainer(args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseLabel(s string) (key, value string) {
|
||||||
|
pos := strings.Index(s, "=")
|
||||||
|
if pos == -1 { // no value - set it to blank
|
||||||
|
return s, ""
|
||||||
|
}
|
||||||
|
return s[:pos], s[pos+1:]
|
||||||
|
}
|
||||||
|
|
||||||
func listContainers(args []string) error {
|
func listContainers(args []string) error {
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
cmdUsage("list-containers", "[<label>]")
|
cmdUsage("list-containers", "[<label>]")
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
docker "github.com/fsouza/go-dockerclient"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseContainerArgs(t *testing.T) {
|
||||||
|
args := []string{"--name", "weave", "--privileged", "--net", "host",
|
||||||
|
"-l", "foo=bar", "-l", "foobar",
|
||||||
|
"-v", "/var/run/docker.sock:/var/run/docker.sock", "-v", "/etc:/host/etc",
|
||||||
|
"--restart", "always", "--pid", "host", "--volumes-from", "weavedb",
|
||||||
|
"-e", "WEAVE_DEBUG", "-e", "EXEC_IMAGE=weaveworks/weaveexec:latest",
|
||||||
|
"weaveworks/weave:latest", "cmd", "arg1", "arg2"}
|
||||||
|
expected := docker.CreateContainerOptions{
|
||||||
|
Name: "weave",
|
||||||
|
Config: &docker.Config{
|
||||||
|
Image: "weaveworks/weave:latest",
|
||||||
|
Env: []string{"WEAVE_DEBUG", "EXEC_IMAGE=weaveworks/weaveexec:latest"},
|
||||||
|
Cmd: []string{"cmd", "arg1", "arg2"},
|
||||||
|
Labels: map[string]string{"foo": "bar", "foobar": ""},
|
||||||
|
},
|
||||||
|
HostConfig: &docker.HostConfig{
|
||||||
|
NetworkMode: "host",
|
||||||
|
PidMode: "host",
|
||||||
|
Privileged: true,
|
||||||
|
RestartPolicy: docker.RestartPolicy{Name: "always"},
|
||||||
|
Binds: []string{"/var/run/docker.sock:/var/run/docker.sock", "/etc:/host/etc"},
|
||||||
|
VolumesFrom: []string{"weavedb"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
have := parseContainerArgs(args)
|
||||||
|
require.Equal(t, expected, have)
|
||||||
|
}
|
||||||
@@ -4,10 +4,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var commands map[string]func([]string) error
|
var commands map[string]func([]string) error
|
||||||
@@ -36,12 +33,6 @@ func init() {
|
|||||||
"create-volume-container": createVolumeContainer,
|
"create-volume-container": createVolumeContainer,
|
||||||
"attach-container": attach,
|
"attach-container": attach,
|
||||||
"detach-container": detach,
|
"detach-container": detach,
|
||||||
"configure-arp": configureARP,
|
|
||||||
"check-iface": checkIface,
|
|
||||||
"del-iface": delIface,
|
|
||||||
"setup-iface": setupIface,
|
|
||||||
"setup-iface-addrs": setupIfaceAddrs,
|
|
||||||
"list-netdevs": listNetDevs,
|
|
||||||
"cni-net": cniNet,
|
"cni-net": cniNet,
|
||||||
"cni-ipam": cniIPAM,
|
"cni-ipam": cniIPAM,
|
||||||
"bridge-ip": bridgeIP,
|
"bridge-ip": bridgeIP,
|
||||||
@@ -55,14 +46,6 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// force re-exec of this binary
|
|
||||||
selfPath, err := filepath.EvalSymlinks("/proc/self/exe")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
weavenet.WeaveUtilCmd = selfPath
|
|
||||||
|
|
||||||
// If no args passed, act as CNI plugin based on executable name
|
// If no args passed, act as CNI plugin based on executable name
|
||||||
switch {
|
switch {
|
||||||
case len(os.Args) == 1 && strings.HasSuffix(os.Args[0], "weave-ipam"):
|
case len(os.Args) == 1 && strings.HasSuffix(os.Args[0], "weave-ipam"):
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/j-keck/arping"
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
|
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
|
||||||
)
|
|
||||||
|
|
||||||
// checkIface returns an error if the given interface cannot be found.
|
|
||||||
func checkIface(args []string) error {
|
|
||||||
if len(args) != 1 {
|
|
||||||
cmdUsage("check-iface", "<iface-name>")
|
|
||||||
}
|
|
||||||
ifaceName := args[0]
|
|
||||||
|
|
||||||
if _, err := netlink.LinkByName(ifaceName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func delIface(args []string) error {
|
|
||||||
if len(args) != 1 {
|
|
||||||
cmdUsage("del-iface", "<iface-name>")
|
|
||||||
}
|
|
||||||
ifName := args[0]
|
|
||||||
|
|
||||||
link, err := netlink.LinkByName(ifName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return netlink.LinkDel(link)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupIface renames the given iface and configures its ARP cache settings.
|
|
||||||
func setupIface(args []string) error {
|
|
||||||
if len(args) != 2 {
|
|
||||||
cmdUsage("setup-iface", "<iface-name> <new-iface-name>")
|
|
||||||
}
|
|
||||||
ifaceName := args[0]
|
|
||||||
newIfName := args[1]
|
|
||||||
|
|
||||||
return weavenet.SetupIface(ifaceName, newIfName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupIfaceAddrs sets up addresses on an interface. It expects to be called inside the container's netns.
|
|
||||||
func setupIfaceAddrs(args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
cmdUsage("setup-iface-addrs", "<iface-name> <with-multicast> <cidr>...")
|
|
||||||
}
|
|
||||||
link, err := netlink.LinkByName(args[0])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
withMulticastRoute, err := strconv.ParseBool(args[1])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cidrs, err := parseCIDRs(args[2:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return weavenet.SetupIfaceAddrs(link, withMulticastRoute, cidrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func configureARP(args []string) error {
|
|
||||||
if len(args) != 2 {
|
|
||||||
cmdUsage("configure-arp", "<iface-name-prefix> <root-path>")
|
|
||||||
}
|
|
||||||
prefix := args[0]
|
|
||||||
rootPath := args[1]
|
|
||||||
|
|
||||||
links, err := netlink.LinkList()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, link := range links {
|
|
||||||
ifName := link.Attrs().Name
|
|
||||||
if strings.HasPrefix(ifName, prefix) {
|
|
||||||
weavenet.ConfigureARPCache(rootPath+"/proc", ifName)
|
|
||||||
if addrs, err := netlink.AddrList(link, netlink.FAMILY_V4); err == nil {
|
|
||||||
for _, addr := range addrs {
|
|
||||||
arping.GratuitousArpOverIfaceByName(addr.IPNet.IP, ifName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// listNetDevs outputs network ifaces identified by the given indexes
|
|
||||||
// in the format of weavenet.Dev.
|
|
||||||
func listNetDevs(args []string) error {
|
|
||||||
if len(args) == 0 {
|
|
||||||
cmdUsage("list-netdevs", "<iface-index>[ <iface-index>]")
|
|
||||||
}
|
|
||||||
|
|
||||||
indexes := make(map[int]struct{})
|
|
||||||
for _, index := range args {
|
|
||||||
if index != "" {
|
|
||||||
id, err := strconv.Atoi(index)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
indexes[id] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
links, err := netlink.LinkList()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var netdevs []weavenet.Dev
|
|
||||||
|
|
||||||
for _, link := range links {
|
|
||||||
if _, found := indexes[link.Attrs().Index]; found {
|
|
||||||
netdev, err := weavenet.LinkToNetDev(link)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
netdevs = append(netdevs, netdev)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nds, err := json.Marshal(netdevs)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println(string(nds))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
Loaded 100 of 3531 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user