Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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:circle20-2e6504d3
|
||||||
|
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
|
||||||
|
|||||||
+138
@@ -1,3 +1,141 @@
|
|||||||
|
## 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
+1131
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
|
||||||
@@ -101,6 +101,7 @@ 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 +109,20 @@ NET_PLUGIN_LATEST=latest_release
|
|||||||
# Paths to all relevant binaries that should be compiled
|
# Paths to all relevant binaries that should be compiled
|
||||||
WEAVER_EXE=prog/weaver/weaver
|
WEAVER_EXE=prog/weaver/weaver
|
||||||
SIGPROXY_EXE=prog/sigproxy/sigproxy
|
SIGPROXY_EXE=prog/sigproxy/sigproxy
|
||||||
KUBEPEERS_EXE=prog/kube-peers/kube-peers
|
KUBEUTILS_EXE=prog/kube-utils/kube-utils
|
||||||
WEAVENPC_EXE=prog/weave-npc/weave-npc
|
WEAVENPC_EXE=prog/weave-npc/weave-npc
|
||||||
WEAVEWAIT_EXE=prog/weavewait/weavewait
|
WEAVEWAIT_EXE=prog/weavewait/weavewait
|
||||||
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
|
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
|
||||||
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
|
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
|
||||||
WEAVEUTIL_EXE=prog/weaveutil/weaveutil
|
WEAVEUTIL_EXE=prog/weaveutil/weaveutil
|
||||||
RUNNER_EXE=tools/runner/runner
|
RUNNER_EXE=tools/runner/runner
|
||||||
MANIFEST_TOOL_DIR=vendor/github.com/estesp/manifest-tool
|
# 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
|
||||||
@@ -179,11 +180,10 @@ $(WEAVER_EXE): api/*.go plugin/*.go plugin/*/*
|
|||||||
$(WEAVER_EXE): proxy/*.go
|
$(WEAVER_EXE): proxy/*.go
|
||||||
$(WEAVEUTIL_EXE): prog/weaveutil/*.go net/*.go plugin/net/*.go plugin/ipam/*.go db/*.go
|
$(WEAVEUTIL_EXE): prog/weaveutil/*.go net/*.go plugin/net/*.go plugin/ipam/*.go db/*.go
|
||||||
$(SIGPROXY_EXE): prog/sigproxy/*.go
|
$(SIGPROXY_EXE): prog/sigproxy/*.go
|
||||||
$(KUBEPEERS_EXE): prog/kube-peers/*.go
|
$(KUBEUTILS_EXE): prog/kube-utils/*.go
|
||||||
$(WEAVENPC_EXE): prog/weave-npc/*.go npc/*.go npc/*/*.go
|
$(WEAVENPC_EXE): prog/weave-npc/*.go npc/*.go npc/*/*.go
|
||||||
$(TEST_TLS_EXE): test/tls/*.go
|
$(TEST_TLS_EXE): test/tls/*.go
|
||||||
$(RUNNER_EXE): tools/runner/*.go
|
$(RUNNER_EXE): tools/runner/*.go
|
||||||
$(MANIFEST_TOOL_EXE): $(MANIFEST_TOOL_DIR)/*.go
|
|
||||||
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
|
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
|
||||||
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
|
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
|
||||||
$(WEAVEWAIT_NOMCAST_EXE): prog/weavewait/*.go net/*.go
|
$(WEAVEWAIT_NOMCAST_EXE): prog/weavewait/*.go net/*.go
|
||||||
@@ -196,9 +196,6 @@ ifeq ($(BUILD_IN_CONTAINER),true)
|
|||||||
# This make target compiles all binaries inside of the weaveworks/build container
|
# This make target compiles all binaries inside of the weaveworks/build container
|
||||||
# It bind-mounts the source into the container and passes all important variables
|
# It bind-mounts the source into the container and passes all important variables
|
||||||
exes $(EXES) tests lint: $(BUILD_UPTODATE)
|
exes $(EXES) tests lint: $(BUILD_UPTODATE)
|
||||||
git submodule update --init
|
|
||||||
# Containernetworking has another copy of vishvananda/netlink which leads to duplicate definitions
|
|
||||||
-@rm -r vendor/github.com/containernetworking/cni/vendor
|
|
||||||
@mkdir -p $(shell pwd)/.pkg
|
@mkdir -p $(shell pwd)/.pkg
|
||||||
$(SUDO) docker run $(RM) $(RUN_FLAGS) \
|
$(SUDO) docker run $(RM) $(RUN_FLAGS) \
|
||||||
-v $(shell pwd):/go/src/github.com/weaveworks/weave \
|
-v $(shell pwd):/go/src/github.com/weaveworks/weave \
|
||||||
@@ -220,7 +217,7 @@ else
|
|||||||
endif
|
endif
|
||||||
$(NETGO_CHECK)
|
$(NETGO_CHECK)
|
||||||
|
|
||||||
$(WEAVEUTIL_EXE) $(KUBEPEERS_EXE) $(WEAVENPC_EXE) $(NETWORKTESTER_EXE):
|
$(WEAVEUTIL_EXE) $(KUBEUTILS_EXE) $(WEAVENPC_EXE) $(NETWORKTESTER_EXE):
|
||||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||||
$(NETGO_CHECK)
|
$(NETGO_CHECK)
|
||||||
|
|
||||||
@@ -234,7 +231,7 @@ $(WEAVEWAIT_NOMCAST_EXE):
|
|||||||
|
|
||||||
# These programs need a separate rule as they fail the netgo check in
|
# These programs need a separate rule as they fail the netgo check in
|
||||||
# the main build stanza due to not importing net package
|
# the main build stanza due to not importing net package
|
||||||
$(SIGPROXY_EXE) $(TEST_TLS_EXE) $(WEAVEWAIT_NOOP_EXE) $(RUNNER_EXE) $(MANIFEST_TOOL_EXE):
|
$(SIGPROXY_EXE) $(TEST_TLS_EXE) $(WEAVEWAIT_NOOP_EXE) $(RUNNER_EXE):
|
||||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
@@ -247,8 +244,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 +272,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 +280,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 +300,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 +320,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 +376,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:
|
||||||
@@ -398,7 +396,7 @@ clean-bin:
|
|||||||
|
|
||||||
clean: clean-bin clean-work-dir
|
clean: clean-bin clean-work-dir
|
||||||
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(BUILD_IMAGE)
|
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(BUILD_IMAGE)
|
||||||
rm -rf test/tls/*.pem test/coverage.* test/coverage $(BUILD_UPTODATE) $(MANIFEST_TOOL_EXE)
|
rm -rf test/tls/*.pem test/coverage.* test/coverage $(BUILD_UPTODATE)
|
||||||
|
|
||||||
build:
|
build:
|
||||||
$(SUDO) go clean -i net
|
$(SUDO) go clean -i net
|
||||||
|
|||||||
@@ -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
|
|
||||||
+15
-18
@@ -9,8 +9,6 @@ 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.8.5/terraform_0.8.5_linux_amd64.zip | gunzip >terraform && chmod +x terraform && sudo mv terraform /usr/bin
|
||||||
}
|
}
|
||||||
@@ -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)."
|
||||||
+15
@@ -92,6 +92,7 @@ build() {
|
|||||||
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.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
|
||||||
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
|
||||||
@@ -181,6 +182,13 @@ 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"
|
||||||
|
|
||||||
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"
|
||||||
}
|
}
|
||||||
@@ -289,6 +297,13 @@ 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"
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
+8
-3
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.8.1
|
FROM golang:1.10.1-stretch
|
||||||
|
|
||||||
# Support Raspberry Pi 2 and newer
|
# Support Raspberry Pi 2 and newer
|
||||||
ENV GOARM 7
|
ENV GOARM 7
|
||||||
@@ -50,7 +50,9 @@ 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
|
||||||
@@ -74,7 +76,7 @@ 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 +88,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"]
|
||||||
-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"
|
|
||||||
@@ -231,7 +231,7 @@ func (c *Client) EnsureNetwork(networkName, driver, subnet string, options map[s
|
|||||||
Name: networkName,
|
Name: networkName,
|
||||||
CheckDuplicate: true,
|
CheckDuplicate: true,
|
||||||
Driver: driver,
|
Driver: driver,
|
||||||
IPAM: docker.IPAMOptions{
|
IPAM: &docker.IPAMOptions{
|
||||||
Driver: driver,
|
Driver: driver,
|
||||||
Config: []docker.IPAMConfig{{Subnet: subnet}},
|
Config: []docker.IPAMConfig{{Subnet: subnet}},
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type textFormatter struct {
|
type textFormatter struct {
|
||||||
|
|||||||
+4
-4
@@ -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
|
||||||
|
|
||||||
@@ -66,5 +66,5 @@ To add new options to how weave should run with each invocation, you would do th
|
|||||||
* 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-1.6.yaml` and `weave-daemonset.yaml`
|
||||||
* if it should be set via `weaver` globally on its one-time initialization invocation, pass it on in `launch.sh`
|
* if it 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 mode (default kubeadm options).
|
||||||
|
* Hosts: `h1` and `h2`.
|
||||||
|
|
||||||
|
## Source IP
|
||||||
|
|
||||||
|
\# | Src | Target | Dst | Src IP | `-j WEAVE-NPC`
|
||||||
|
---|-------------|-------------------|--------|------------------|--------------
|
||||||
|
1 | Pod_h1 | ip(Pod_h2) | Pod_h2 | ip(Pod_h1) | OK
|
||||||
|
2 | h1 | ClusterIP | Pod_h1 | ip(weave_h1) | **NOK**
|
||||||
|
3 | h1 | ClusterIP | Pod_h2 | ip(weave_h1) | OK
|
||||||
|
4 | Pod_h1 | ClusterIP | Pod_h1 | ip(weave_h1) | **NOK**
|
||||||
|
5 | Pod_h1 | ClusterIP | Pod_h2 | ip(weave_h1) | OK
|
||||||
|
6 | h1 | ip(h1):NodePort | Pod_h1 | ip(weave_h1) | **NOK**
|
||||||
|
7 | h1 | ip(h1):NodePort | Pod_h2 | ip(weave_h1) | OK
|
||||||
|
8 | h1 | ip(h2):NodePort | Pod_h1 | ip(weave_h2) | OK
|
||||||
|
9 | h1 | ip(h2):NodePort | Pod_h2 | ip(weave_h2) | ??? Can't reproduce
|
||||||
|
10 | Pod_h1 | ip(h1):NodePort | Pod_h1 | ip(weave_h1) | **NOK**
|
||||||
|
11 | Pod_h1 | ip(h1):NodePort | Pod_h2 | ip(weave_h1) | OK
|
||||||
|
12 | Pod_h1 | ip(h2):NodePort | Pod_h1 | ip(weave_h2) | OK
|
||||||
|
13 | Pod_h1 | ip(h2):NodePort | Pod_h2 | ip(weave_h2) | OK
|
||||||
|
|
||||||
|
Remarks:
|
||||||
|
|
||||||
|
* **Src IP** is of a packet which is captured on the weave bridge.
|
||||||
|
* **-j WEAVE-NPC** - whether a packet enters the `filter/WEAVE-NPC` iptables chain (OK = NetworkPolicy is enforced as required).
|
||||||
|
* **Pod_h1** - a Pod running on the `h1` host.
|
||||||
|
* **ip(weave_h1)** - IP addr of the weave bridge on the `h1` host.
|
||||||
@@ -127,6 +127,44 @@ Finally, for **Mainline** releases only:
|
|||||||
|
|
||||||
* Images tagged `latest` are updated on DockerHub
|
* Images tagged `latest` are updated on DockerHub
|
||||||
|
|
||||||
|
Now, you can validate whether images were published for all platforms:
|
||||||
|
|
||||||
|
# Required platforms for each image:
|
||||||
|
grep '^ML_PLATFORMS=' Makefile
|
||||||
|
|
||||||
|
# Published platforms per image:
|
||||||
|
for img in $(grep '^PUBLISH=' Makefile); do
|
||||||
|
img="weaveworks/$(echo $img | cut -d_ -f2):${TAG#v}"
|
||||||
|
platforms=$(vendor/github.com/estesp/manifest-tool/manifest-tool \
|
||||||
|
inspect --raw "$img" | \
|
||||||
|
jq '.[].Platform | .os + "/" +.architecture' | \
|
||||||
|
tr '\n' ' ')
|
||||||
|
echo "$img: $platforms"
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
### Docker Store
|
||||||
|
|
||||||
|
Weave Net is [available in Docker Store](https://store.docker.com/plugins/weave-net-plugin) and should also be released there.
|
||||||
|
|
||||||
|
* Go to https://store.docker.com/ and log in.
|
||||||
|
* Go to "[_Publish_](https://store.docker.com/publisher/center)".
|
||||||
|
* Under "_My Products_", select "_owners (weaveworks)_". Weave Net should be listed among our products.
|
||||||
|
* Click "_Actions_" > "_Edit Product_".
|
||||||
|
* Go to "_Plans & Pricing_" > "_Free Tier_".
|
||||||
|
* Under "_Source Repositories & Tags_", click on "_Add Source_", select "_net-plugin_" under "_Repository_" and the version to release under "_Tag_".
|
||||||
|
* Under "_Resources_" > "_Installation Instructions_", update the lines containing `export NET_VERSION=<version>`.
|
||||||
|
* Click "_Submit For Review_". You should see a message like: "_Your product has been submitted for approval. [...] We'll be in touch with next steps soon!_".
|
||||||
|
* You should receive an email saying:
|
||||||
|
|
||||||
|
> This email confirms that we received your submission on \<date\> of Weave Net to the Docker Store.
|
||||||
|
>
|
||||||
|
> We're reviewing your submission to ensure that it meets our [security guidelines](https://success.docker.com/Store#Security_and_Audit_Policies) and complies with our [best practices](https://success.docker.com/Store#Create_Great_Content). Don't worry! We'll let you know if there's anything you need to change before we publish your submission. You should hear back from us within the next 14 days.
|
||||||
|
>
|
||||||
|
> Thanks for submitting your content to the Docker Store!
|
||||||
|
|
||||||
|
* Hope Docker eventually performs the release or contacts us. If nothing happens within 14 days, contact their support team: publisher-support@docker.com.
|
||||||
|
|
||||||
### Finish up
|
### Finish up
|
||||||
|
|
||||||
* If not on master, merge branch into master and push to GitHub.
|
* If not on master, merge branch into master and push to GitHub.
|
||||||
|
|||||||
+3
-1
@@ -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))
|
||||||
@@ -983,7 +985,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
|
||||||
}
|
}
|
||||||
|
|||||||
+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)
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ func CheckAllExpectedMessagesSent(allocs ...*Allocator) {
|
|||||||
m := alloc.gossip.(*mockGossipComms)
|
m := alloc.gossip.(*mockGossipComms)
|
||||||
m.RLock()
|
m.RLock()
|
||||||
if len(m.messages) > 0 {
|
if len(m.messages) > 0 {
|
||||||
require.FailNow(m, fmt.Sprintf("%s: Gossip message(s) not sent as expected: \n%x", m.name, m.messages))
|
require.FailNow(m, fmt.Sprintf("%s: Gossip message(s) not sent as expected: \n%v", m.name, m.messages))
|
||||||
}
|
}
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-42
@@ -84,7 +84,7 @@ func NewAWSVPCTracker(bridgeName string) (*AWSVPCTracker, error) {
|
|||||||
func (t *AWSVPCTracker) HandleUpdate(prevRanges, currRanges []address.Range, local bool) error {
|
func (t *AWSVPCTracker) HandleUpdate(prevRanges, currRanges []address.Range, local bool) error {
|
||||||
t.debugf("replacing %q by %q; local(%t)", prevRanges, currRanges, local)
|
t.debugf("replacing %q by %q; local(%t)", prevRanges, currRanges, local)
|
||||||
|
|
||||||
prev, curr := removeCommon(address.NewCIDRs(merge(prevRanges)), address.NewCIDRs(merge(currRanges)))
|
prev, curr := RemoveCommon(address.NewCIDRs(Merge(prevRanges)), address.NewCIDRs(Merge(currRanges)))
|
||||||
|
|
||||||
// It might make sense to do the removal first and then add entries
|
// It might make sense to do the removal first and then add entries
|
||||||
// because of the 50 routes limit. However, in such case a container might
|
// because of the 50 routes limit. However, in such case a container might
|
||||||
@@ -123,6 +123,10 @@ func (t *AWSVPCTracker) HandleUpdate(prevRanges, currRanges []address.Range, loc
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *AWSVPCTracker) String() string {
|
||||||
|
return "awsvpc"
|
||||||
|
}
|
||||||
|
|
||||||
func (t *AWSVPCTracker) createVPCRoute(cidr string) (*ec2.CreateRouteOutput, error) {
|
func (t *AWSVPCTracker) createVPCRoute(cidr string) (*ec2.CreateRouteOutput, error) {
|
||||||
route := &ec2.CreateRouteInput{
|
route := &ec2.CreateRouteInput{
|
||||||
RouteTableId: &t.routeTableID,
|
RouteTableId: &t.routeTableID,
|
||||||
@@ -232,47 +236,6 @@ func (t *AWSVPCTracker) infof(fmt string, args ...interface{}) {
|
|||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
// merge merges adjacent range entries.
|
|
||||||
// The given slice has to be sorted in increasing order.
|
|
||||||
func merge(r []address.Range) []address.Range {
|
|
||||||
var merged []address.Range
|
|
||||||
|
|
||||||
for i := range r {
|
|
||||||
if prev := len(merged) - 1; prev >= 0 && merged[prev].End == r[i].Start {
|
|
||||||
merged[prev].End = r[i].End
|
|
||||||
} else {
|
|
||||||
merged = append(merged, r[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeCommon filters out CIDR ranges which are contained in both a and b slices.
|
|
||||||
// Both slices have to be sorted in increasing order.
|
|
||||||
func removeCommon(a, b []address.CIDR) (newA, newB []address.CIDR) {
|
|
||||||
i, j := 0, 0
|
|
||||||
|
|
||||||
for i < len(a) && j < len(b) {
|
|
||||||
switch {
|
|
||||||
case a[i].Start() < b[j].Start() || a[i].End() < b[j].End():
|
|
||||||
newA = append(newA, a[i])
|
|
||||||
i++
|
|
||||||
case a[i].Start() > b[j].Start() || a[i].End() > b[j].End():
|
|
||||||
newB = append(newB, b[j])
|
|
||||||
j++
|
|
||||||
default:
|
|
||||||
i++
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
newA = append(newA, a[i:]...)
|
|
||||||
newB = append(newB, b[j:]...)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseCIDR(cidr string) (*net.IPNet, error) {
|
func parseCIDR(cidr string) (*net.IPNet, error) {
|
||||||
ip, ipnet, err := net.ParseCIDR(cidr)
|
ip, ipnet, err := net.ParseCIDR(cidr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package tracker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/weaveworks/weave/net/address"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Merge merges adjacent range entries.
|
||||||
|
// The given slice has to be sorted in increasing order.
|
||||||
|
func Merge(r []address.Range) []address.Range {
|
||||||
|
var merged []address.Range
|
||||||
|
|
||||||
|
for i := range r {
|
||||||
|
if prev := len(merged) - 1; prev >= 0 && merged[prev].End == r[i].Start {
|
||||||
|
merged[prev].End = r[i].End
|
||||||
|
} else {
|
||||||
|
merged = append(merged, r[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return merged
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveCommon filters out CIDR ranges which are contained in both a and b slices.
|
||||||
|
// Both slices have to be sorted in increasing order.
|
||||||
|
func RemoveCommon(a, b []address.CIDR) (newA, newB []address.CIDR) {
|
||||||
|
i, j := 0, 0
|
||||||
|
|
||||||
|
for i < len(a) && j < len(b) {
|
||||||
|
switch {
|
||||||
|
case a[i].Start() < b[j].Start() || a[i].End() < b[j].End():
|
||||||
|
newA = append(newA, a[i])
|
||||||
|
i++
|
||||||
|
case a[i].Start() > b[j].Start() || a[i].End() > b[j].End():
|
||||||
|
newB = append(newB, b[j])
|
||||||
|
j++
|
||||||
|
default:
|
||||||
|
i++
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
newA = append(newA, a[i:]...)
|
||||||
|
newB = append(newB, b[j:]...)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ var (
|
|||||||
func TestRemoveCommon(t *testing.T) {
|
func TestRemoveCommon(t *testing.T) {
|
||||||
a := []address.CIDR{r0to127, r1dot0to255}
|
a := []address.CIDR{r0to127, r1dot0to255}
|
||||||
b := []address.CIDR{r1dot0to255, r2dot0to255}
|
b := []address.CIDR{r1dot0to255, r2dot0to255}
|
||||||
newA, newB := removeCommon(a, b)
|
newA, newB := RemoveCommon(a, b)
|
||||||
require.Equal(t, []address.CIDR{r0to127}, newA)
|
require.Equal(t, []address.CIDR{r0to127}, newA)
|
||||||
require.Equal(t, []address.CIDR{r2dot0to255}, newB)
|
require.Equal(t, []address.CIDR{r2dot0to255}, newB)
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ func TestMerge(t *testing.T) {
|
|||||||
r128to255.Range(),
|
r128to255.Range(),
|
||||||
r2dot0to255.Range(),
|
r2dot0to255.Range(),
|
||||||
}
|
}
|
||||||
require.Equal(t, []address.Range{r0to255.Range(), r2dot0to255.Range()}, merge(ranges))
|
require.Equal(t, []address.Range{r0to255.Range(), r2dot0to255.Range()}, Merge(ranges))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper
|
// Helper
|
||||||
@@ -17,4 +17,7 @@ type LocalRangeTracker interface {
|
|||||||
// The local parameter indicates whether the ranges belong to the peer
|
// The local parameter indicates whether the ranges belong to the peer
|
||||||
// by which the method is called.
|
// by which the method is called.
|
||||||
HandleUpdate(prevRanges, currRanges []address.Range, local bool) error
|
HandleUpdate(prevRanges, currRanges []address.Range, local bool) error
|
||||||
|
|
||||||
|
// String returns the tracker name
|
||||||
|
String() string
|
||||||
}
|
}
|
||||||
+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 {
|
||||||
|
|||||||
+1
-1
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
+146
-140
@@ -4,173 +4,176 @@ 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 map[string]*selectorSpec,
|
||||||
|
ipBlocks map[string]*ipBlockSpec,
|
||||||
err error) {
|
err error) {
|
||||||
|
|
||||||
nsSelectors = make(map[string]*selectorSpec)
|
nsSelectors = make(map[string]*selectorSpec)
|
||||||
podSelectors = make(map[string]*selectorSpec)
|
podSelectors = make(map[string]*selectorSpec)
|
||||||
|
ipBlocks = make(map[string]*ipBlockSpec)
|
||||||
rules = make(map[string]*ruleSpec)
|
rules = make(map[string]*ruleSpec)
|
||||||
|
policyTypes := make([]policyType, 0)
|
||||||
|
|
||||||
|
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, policyTypes, ns.name, ipset.HashIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return 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, 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.
|
||||||
|
// Exactly one of its fields must be specified.
|
||||||
|
if peer.PodSelector != nil {
|
||||||
|
srcSelector, err = newSelectorSpec(peer.PodSelector, nil, ns.name, ipset.HashIP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
addIfNotExist(srcSelector, podSelectors)
|
||||||
|
srcRuleHost = srcSelector
|
||||||
|
} else if peer.NamespaceSelector != nil {
|
||||||
|
srcSelector, err = newSelectorSpec(peer.NamespaceSelector, nil, "", ipset.ListSet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
nsSelectors[srcSelector.key] = srcSelector
|
||||||
|
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, srcSelector, targetSelector, &port)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rules, nsSelectors, podSelectors, nil
|
// If egress is empty then this NetworkPolicy does not allow any egress traffic
|
||||||
|
if policy.Spec.Egress != nil && len(policy.Spec.Egress) != 0 {
|
||||||
|
for _, egressRule := range policy.Spec.Egress {
|
||||||
|
// If Ports is empty or missing, this rule matches all ports
|
||||||
|
allPorts := egressRule.Ports == nil || len(egressRule.Ports) == 0
|
||||||
|
// If To is empty or missing, this rule matches all destinations
|
||||||
|
allDestinations := egressRule.To == nil || len(egressRule.To) == 0
|
||||||
|
|
||||||
|
if !allPorts {
|
||||||
|
if err = checkForNamedPorts(egressRule.Ports); err != nil {
|
||||||
|
return 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.
|
||||||
|
// Exactly one of its fields must be specified.
|
||||||
|
if peer.PodSelector != nil {
|
||||||
|
dstSelector, err = newSelectorSpec(peer.PodSelector, nil, ns.name, ipset.HashIP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
addIfNotExist(dstSelector, podSelectors)
|
||||||
|
dstRuleHost = dstSelector
|
||||||
|
|
||||||
|
} else if peer.NamespaceSelector != nil {
|
||||||
|
dstSelector, err = newSelectorSpec(peer.NamespaceSelector, nil, "", ipset.ListSet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
nsSelectors[dstSelector.key] = dstSelector
|
||||||
|
dstRuleHost = dstSelector
|
||||||
|
} else if peer.IPBlock != nil {
|
||||||
|
ipBlock := newIPBlockSpec(peer.IPBlock, ns.name)
|
||||||
|
ipBlocks[ipBlock.key] = ipBlock
|
||||||
|
dstRuleHost = ipBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
if allPorts {
|
||||||
|
rule := newRuleSpec(policyTypeEgress, nil, targetSelector, dstRuleHost, nil)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
} else {
|
||||||
|
withNormalisedProtoAndPort(egressRule.Ports, func(proto, port string) {
|
||||||
|
rule := newRuleSpec(policyTypeEgress, &proto, targetSelector, dstRuleHost, &port)
|
||||||
|
rules[rule.key] = rule
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules, nsSelectors, podSelectors, ipBlocks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addIfNotExist(s *selectorSpec, ss map[string]*selectorSpec) {
|
func addIfNotExist(s *selectorSpec, ss map[string]*selectorSpec) {
|
||||||
@@ -179,12 +182,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 +213,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"
|
||||||
|
|||||||
+4
-7
@@ -9,7 +9,7 @@ import (
|
|||||||
networkingv1 "k8s.io/api/networking/v1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
"github.com/weaveworks/weave/common"
|
||||||
"github.com/weaveworks/weave/npc/ipset"
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
"github.com/weaveworks/weave/npc/iptables"
|
"github.com/weaveworks/weave/npc/iptables"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,19 +37,16 @@ type controller struct {
|
|||||||
|
|
||||||
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
|
||||||
|
|
||||||
legacy bool // use legacy network policy semantics (k8s pre-1.7)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(nodeName string, legacy bool, ipt iptables.Interface, ips ipset.Interface) NetworkPolicyController {
|
func New(nodeName string, ipt iptables.Interface, ips ipset.Interface) NetworkPolicyController {
|
||||||
c := &controller{
|
c := &controller{
|
||||||
nodeName: nodeName,
|
nodeName: nodeName,
|
||||||
legacy: legacy,
|
|
||||||
ipt: ipt,
|
ipt: ipt,
|
||||||
ips: ips,
|
ips: ips,
|
||||||
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)
|
||||||
|
|
||||||
return c
|
return c
|
||||||
@@ -71,7 +68,7 @@ func (npc *controller) onNewNsSelector(selector *selector) error {
|
|||||||
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)
|
newNs, err := newNS(name, npc.nodeName, npc.ipt, npc.ips, npc.nsSelectors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+183
-45
@@ -1,12 +1,14 @@
|
|||||||
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"
|
||||||
@@ -103,18 +105,46 @@ func (i *mockIPSet) List(prefix string) ([]ipset.Name, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mockIPTables struct {
|
type mockIPTables struct {
|
||||||
|
rules map[string]map[string]struct{} // chain -> rulespec -> struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockIPTables() *mockIPTables {
|
||||||
|
return &mockIPTables{rules: make(map[string]map[string]struct{})}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ipt *mockIPTables) Append(table, chain string, rulespec ...string) error {
|
func (ipt *mockIPTables) Append(table, chain string, rulespec ...string) error {
|
||||||
|
if table != TableFilter {
|
||||||
|
return fmt.Errorf("invalid table: %q", table)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found := ipt.rules[chain]; !found {
|
||||||
|
ipt.rules[chain] = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
rule := strings.Join(rulespec, " ")
|
||||||
|
if _, found := ipt.rules[chain][rule]; found {
|
||||||
|
return fmt.Errorf("rule already exists. chain: %q, rule: %q", chain, rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipt.rules[chain][rule] = struct{}{}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ipt *mockIPTables) Delete(table, chain string, rulespec ...string) error {
|
func (ipt *mockIPTables) Delete(table, chain string, rulespec ...string) error {
|
||||||
|
rule := strings.Join(rulespec, " ")
|
||||||
|
|
||||||
|
if _, found := ipt.rules[chain][rule]; !found {
|
||||||
|
return fmt.Errorf("rule does not exist. chain: %q rule: %q", chain, rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(ipt.rules[chain], rule)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ipt *mockIPTables) Insert(table, chain string, pos int, rulespec ...string) error {
|
func (ipt *mockIPTables) Insert(table, chain string, pos int, rulespec ...string) error {
|
||||||
return nil
|
return errors.New("Not Implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
||||||
@@ -144,6 +174,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 +198,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
|||||||
|
|
||||||
// Namespaces first
|
// Namespaces first
|
||||||
m := newMockIPSet()
|
m := newMockIPSet()
|
||||||
controller := New("foo", false, &mockIPTables{}, &m)
|
controller := New("foo", newMockIPTables(), &m)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
selectorIPSetName = "weave-I239Zp%sCvoVt*D6u=A!2]YEk"
|
selectorIPSetName = "weave-I239Zp%sCvoVt*D6u=A!2]YEk"
|
||||||
@@ -183,7 +214,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)
|
||||||
|
|
||||||
controller.AddNetworkPolicy(networkPolicy)
|
controller.AddNetworkPolicy(networkPolicy)
|
||||||
|
|
||||||
@@ -193,17 +224,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)
|
||||||
|
|
||||||
defaultNamespace := &coreapi.Namespace{
|
defaultNamespace := &coreapi.Namespace{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -213,7 +245,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 +258,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 +270,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 +284,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,60 +296,74 @@ 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)
|
controller := New("qux", newMockIPTables(), &m)
|
||||||
|
|
||||||
defaultNamespace := &coreapi.Namespace{
|
defaultNamespace := &coreapi.Namespace{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -328,7 +382,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 +390,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 +404,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 +416,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,24 +428,24 @@ 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)
|
controller := New("baz", newMockIPTables(), &m)
|
||||||
|
|
||||||
defaultNamespace := &coreapi.Namespace{
|
defaultNamespace := &coreapi.Namespace{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -408,7 +463,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 +472,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 +483,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 +493,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 +505,90 @@ 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()
|
||||||
|
controller := New("foo", ipt, &m)
|
||||||
|
|
||||||
|
netpolFoo := &networkingv1.NetworkPolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "netpol-foo",
|
||||||
|
Name: "allow-from-bar-to-foo",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{
|
||||||
|
networkingv1.PolicyTypeEgress,
|
||||||
|
},
|
||||||
|
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "bar"}},
|
||||||
|
Egress: []networkingv1.NetworkPolicyEgressRule{{
|
||||||
|
To: []networkingv1.NetworkPolicyPeer{{
|
||||||
|
IPBlock: &networkingv1.IPBlock{
|
||||||
|
CIDR: "192.168.48.0/24",
|
||||||
|
Except: []string{
|
||||||
|
"192.168.48.1/32",
|
||||||
|
"192.168.48.2/32",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := controller.AddNetworkPolicy(netpolFoo)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, 2, len(m.sets[exceptIPSetName].subSets))
|
||||||
|
require.True(t, m.entryExists(exceptIPSetName, "192.168.48.1/32"))
|
||||||
|
require.True(t, m.entryExists(exceptIPSetName, "192.168.48.2/32"))
|
||||||
|
|
||||||
|
// Each egress rule is represented as two iptables rules (-J MARK and -J RETURN).
|
||||||
|
require.Equal(t, 2, len(ipt.rules[EgressCustomChain]))
|
||||||
|
for rule := range ipt.rules[EgressCustomChain] {
|
||||||
|
require.Contains(t, rule, "-d 192.168.48.0/24 -m set ! --match-set "+exceptIPSetName+" dst")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we create a new ipset for the ipBlock bellow. An ipset with the
|
||||||
|
// same content already exists (created by netpolFoo), but we need to create
|
||||||
|
// a new one, as netpolBar is in a different namespace and weave-npc does
|
||||||
|
// object refcounting only within namespace boundaries.
|
||||||
|
netpolBar := &networkingv1.NetworkPolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
UID: "netpol-foo-bar",
|
||||||
|
Name: "allow-from-bar-to-foo-in-non-default",
|
||||||
|
Namespace: "non-default",
|
||||||
|
},
|
||||||
|
Spec: networkingv1.NetworkPolicySpec{
|
||||||
|
PolicyTypes: []networkingv1.PolicyType{
|
||||||
|
networkingv1.PolicyTypeEgress,
|
||||||
|
},
|
||||||
|
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "bar"}},
|
||||||
|
Egress: []networkingv1.NetworkPolicyEgressRule{{
|
||||||
|
To: []networkingv1.NetworkPolicyPeer{{
|
||||||
|
IPBlock: &networkingv1.IPBlock{
|
||||||
|
CIDR: "192.168.48.0/24",
|
||||||
|
Except: []string{
|
||||||
|
"192.168.48.1/32",
|
||||||
|
"192.168.48.2/32",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err = controller.AddNetworkPolicy(netpolBar)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(m.sets[exceptIPSetNameInNonDefault].subSets))
|
||||||
}
|
}
|
||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
package npc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/common"
|
||||||
|
"github.com/weaveworks/weave/net/ipset"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ipBlockSpec struct {
|
||||||
|
key string
|
||||||
|
ipsetName ipset.Name // ipset for storing excepted CIDRs
|
||||||
|
ipBlock *networkingv1.IPBlock
|
||||||
|
nsName string // Namespace name
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIPBlockSpec(ipb *networkingv1.IPBlock, nsName string) *ipBlockSpec {
|
||||||
|
spec := &ipBlockSpec{ipBlock: ipb, nsName: nsName}
|
||||||
|
|
||||||
|
if len(ipb.Except) > 0 {
|
||||||
|
sort.Strings(ipb.Except)
|
||||||
|
spec.key = strings.Join(ipb.Except, " ")
|
||||||
|
spec.ipsetName = ipset.Name(IpsetNamePrefix + shortName(nsName+":"+"ipblock-except:"+spec.key))
|
||||||
|
}
|
||||||
|
|
||||||
|
return spec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spec *ipBlockSpec) getRuleSpec(src bool) ([]string, string) {
|
||||||
|
dir, dirOpt := "dst", "-d"
|
||||||
|
if src {
|
||||||
|
dir, dirOpt = "src", "-s"
|
||||||
|
}
|
||||||
|
|
||||||
|
if spec.key == "" {
|
||||||
|
rule := []string{dirOpt, spec.ipBlock.CIDR}
|
||||||
|
comment := fmt.Sprintf("cidr: %s", spec.ipBlock.CIDR)
|
||||||
|
return rule, comment
|
||||||
|
}
|
||||||
|
|
||||||
|
rule := []string{dirOpt, spec.ipBlock.CIDR,
|
||||||
|
"-m", "set", "!", "--match-set", string(spec.ipsetName), dir}
|
||||||
|
comment := fmt.Sprintf("cidr: %s except [%s]", spec.ipBlock.CIDR, spec.key)
|
||||||
|
return rule, comment
|
||||||
|
}
|
||||||
|
|
||||||
|
type ipBlockSet struct {
|
||||||
|
ips ipset.Interface
|
||||||
|
users map[string]map[types.UID]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIPBlockSet(ips ipset.Interface) *ipBlockSet {
|
||||||
|
return &ipBlockSet{
|
||||||
|
ips: ips,
|
||||||
|
users: make(map[string]map[types.UID]struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ipBlockSet) deprovision(user types.UID, current, desired map[string]*ipBlockSpec) error {
|
||||||
|
for key, spec := range current {
|
||||||
|
if key == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found := desired[key]; !found {
|
||||||
|
delete(s.users[key], user)
|
||||||
|
if len(s.users[key]) == 0 {
|
||||||
|
common.Log.Infof("destroying ipset: %#v", spec)
|
||||||
|
if err := s.ips.Destroy(spec.ipsetName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(s.users, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ipBlockSet) provision(user types.UID, current, desired map[string]*ipBlockSpec) (err error) {
|
||||||
|
for key, spec := range desired {
|
||||||
|
if key == "" {
|
||||||
|
// No need to provision an ipBlock with empty list of excepted CIDRs
|
||||||
|
// (i.e. no ipset is needed for the related iptables rule).
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found := current[key]; !found {
|
||||||
|
if _, found := s.users[key]; !found {
|
||||||
|
common.Log.Infof("creating ipset: %#v", spec)
|
||||||
|
if err := s.ips.Create(spec.ipsetName, ipset.HashNet); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(brb) Pass comment to ips.Create instead.
|
||||||
|
comment := "excepted from " + spec.ipBlock.CIDR
|
||||||
|
for _, cidr := range spec.ipBlock.Except {
|
||||||
|
if err = s.ips.AddEntry(user, spec.ipsetName, cidr, comment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.users[key] = make(map[types.UID]struct{})
|
||||||
|
}
|
||||||
|
s.users[key][user] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+179
-174
@@ -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,20 +31,20 @@ 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
|
||||||
podSelectors *selectorSet
|
podSelectors *selectorSet
|
||||||
|
ipBlocks *ipBlockSet
|
||||||
rules *ruleSet
|
rules *ruleSet
|
||||||
|
|
||||||
legacy bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) (*ns, error) {
|
||||||
allPods, err := newSelectorSpec(&metav1.LabelSelector{}, false, name, ipset.HashIP)
|
allPods, err := newSelectorSpec(&metav1.LabelSelector{}, nil, name, ipset.HashIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -60,19 +59,24 @@ func newNS(name, nodeName string, legacy bool, ipt iptables.Interface, ips ipset
|
|||||||
uid: uuid.NewUUID(),
|
uid: uuid.NewUUID(),
|
||||||
allPods: allPods,
|
allPods: allPods,
|
||||||
nsSelectors: nsSelectors,
|
nsSelectors: nsSelectors,
|
||||||
|
ipBlocks: newIPBlockSet(ips),
|
||||||
rules: newRuleSet(ipt),
|
rules: newRuleSet(ipt),
|
||||||
legacy: legacy}
|
|
||||||
|
|
||||||
ns.podSelectors = newSelectorSet(ips, ns.onNewPodSelector, ns.onNewDstPodSelector, ns.onDestroyDstPodSelector)
|
|
||||||
|
|
||||||
if !legacy {
|
|
||||||
defaultAllowIPSet := ipset.Name(IpsetNamePrefix + shortName("default-allow:"+name))
|
|
||||||
if err := ips.Create(defaultAllowIPSet, ipset.HashIP); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ns.defaultAllowIPSet = defaultAllowIPSet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
@@ -102,16 +106,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
|
||||||
|
ipset := ns.defaultAllowIPSetName(policyType)
|
||||||
if selector.matches(pod.ObjectMeta.Labels) {
|
if selector.matches(pod.ObjectMeta.Labels) {
|
||||||
if err := ns.ips.DelEntry(pod.ObjectMeta.UID, ns.defaultAllowIPSet, pod.Status.PodIP); err != nil {
|
if err := ns.ips.DelEntry(pod.ObjectMeta.UID, ipset, pod.Status.PodIP); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,15 +122,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.matches(pod.ObjectMeta.Labels) {
|
||||||
if err := ns.addToDefaultAllowIfNoMatching(pod); err != nil {
|
if err := ns.addToDefaultAllowIfNoMatching(pod, policyType); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,18 +136,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.matches(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,17 +169,18 @@ func (ns *ns) addPod(obj *coreapi.Pod) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ns.checkLocalPod(obj) {
|
foundIngress, foundEgress, err := ns.podSelectors.addToMatching(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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundEgress {
|
||||||
|
if err := ns.ips.AddEntry(obj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, obj.Status.PodIP, podComment(obj)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,30 +197,29 @@ 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 {
|
||||||
if !ns.legacy {
|
return err
|
||||||
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)
|
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.addToMatching(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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundEgress {
|
||||||
|
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,17 +227,11 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
|||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,17 +256,11 @@ 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 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,6 +268,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,14 +292,11 @@ 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ns.podSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, obj.Status.PodIP)
|
return ns.podSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, obj.Status.PodIP)
|
||||||
@@ -305,7 +305,7 @@ func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
|||||||
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, ipBlocks, err := ns.analyse(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -317,16 +317,19 @@ 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.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, 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, newIPBlocks, err := ns.analyse(newObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -344,18 +347,24 @@ 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.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.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, ipBlocks, err := ns.analyse(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -369,73 +378,89 @@ 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.ipBlocks.deprovision(uid, ipBlocks, nil); err != nil {
|
||||||
return []string{"-m", "set", "--match-set", string(nsIpsetName), "dst", "-j", "ACCEPT", "-m", "comment", "--comment", "DefaultAllow isolation for namespace: " + namespace}
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *ns) ensureBypassRule() error {
|
|
||||||
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.addToMatching(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 {
|
||||||
@@ -464,10 +489,8 @@ 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
|
||||||
@@ -476,36 +499,16 @@ func (ns *ns) deleteNamespace(obj *coreapi.Namespace) error {
|
|||||||
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 +535,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 map[string]*selectorSpec,
|
||||||
|
ipBlocks map[string]*ipBlockSpec,
|
||||||
err error) {
|
err error) {
|
||||||
|
|
||||||
switch p := obj.(type) {
|
switch p := obj.(type) {
|
||||||
@@ -550,16 +562,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, 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, "")
|
||||||
|
|
||||||
|
|||||||
+66
-38
@@ -1,34 +1,36 @@
|
|||||||
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)
|
selector labels.Selector // k8s Selector object (for matching)
|
||||||
dst bool // destination selector
|
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(json *metav1.LabelSelector, policyType []policyType, nsName string, ipsetType ipset.Type) (*selectorSpec, error) {
|
||||||
selector, err := metav1.LabelSelectorAsSelector(json)
|
selector, err := metav1.LabelSelectorAsSelector(json)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
key := selector.String()
|
key := selector.String()
|
||||||
return &selectorSpec{
|
return &selectorSpec{
|
||||||
key: key,
|
key: key,
|
||||||
selector: selector,
|
selector: selector,
|
||||||
dst: dst,
|
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,6 +39,23 @@ 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
|
||||||
@@ -55,49 +74,54 @@ 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) addToMatching(user types.UID, labelMap 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.matches(labelMap) {
|
||||||
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) delFromMatching(user types.UID, labelMap map[string]string, entry string) error {
|
||||||
@@ -111,8 +135,8 @@ 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) targetSelectorExist(s *selector, policyType policyType) bool {
|
||||||
return ss.dstSelectorsCount[s.spec.key] > 0
|
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 +153,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 +185,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 {
|
||||||
|
|||||||
+18
-2
@@ -117,6 +117,16 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
defer ns.Close()
|
defer ns.Close()
|
||||||
|
|
||||||
|
hostNs, err := netns.Get()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error accessing host network namespace: %s", err)
|
||||||
|
}
|
||||||
|
defer hostNs.Close()
|
||||||
|
|
||||||
|
if ns.Equal(hostNs) {
|
||||||
|
return fmt.Errorf("can not specify host network namespace as network namespace to which container should be added")
|
||||||
|
}
|
||||||
|
|
||||||
id := args.ContainerID
|
id := args.ContainerID
|
||||||
if len(id) < 5 {
|
if len(id) < 5 {
|
||||||
data := make([]byte, 5)
|
data := make([]byte, 5)
|
||||||
@@ -130,7 +140,7 @@ func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
|
|||||||
if err := weavenet.AttachContainer(args.Netns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&ip.Address}, false, conf.HairpinMode); err != nil {
|
if err := weavenet.AttachContainer(args.Netns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&ip.Address}, false, conf.HairpinMode); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := weavenet.WithNetNSLinkUnsafe(ns, args.IfName, func(link netlink.Link) error {
|
if err := weavenet.WithNetNSLink(ns, args.IfName, func(link netlink.Link) error {
|
||||||
return setupRoutes(link, args.IfName, ip.Address, ip.Gateway, result.Routes)
|
return setupRoutes(link, args.IfName, ip.Address, ip.Gateway, result.Routes)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("error setting up routes: %s", err)
|
return fmt.Errorf("error setting up routes: %s", err)
|
||||||
@@ -177,7 +187,13 @@ func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
// As of CNI 0.3 spec, runtimes can send blank if they just want the address deallocated
|
// As of CNI 0.3 spec, runtimes can send blank if they just want the address deallocated
|
||||||
if args.Netns != "" {
|
if args.Netns != "" {
|
||||||
if _, err = weavenet.WithNetNS(args.Netns, "del-iface", args.IfName); err != nil {
|
if err = weavenet.WithNetNSByPath(args.Netns, func() error {
|
||||||
|
link, err := netlink.LinkByName(args.IfName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return netlink.LinkDel(link)
|
||||||
|
}); err != nil {
|
||||||
// We log the error and carry on instead of returning nil, as there may still be resources to free in IPAM.
|
// We log the error and carry on instead of returning nil, as there may still be resources to free in IPAM.
|
||||||
logOnStderr(fmt.Errorf("error removing interface %q: %s", args.IfName, err))
|
logOnStderr(fmt.Errorf("error removing interface %q: %s", args.IfName, err))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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')
|
||||||
@@ -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,329 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
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 != "" {
|
||||||
|
addresses = append(addresses, nodeInfo{name: peer.Name, addr: internalIP})
|
||||||
|
} else if externalIP != "" {
|
||||||
|
addresses = append(addresses, nodeInfo{name: peer.Name, addr: externalIP})
|
||||||
|
} else if includeWithNoIPAddr {
|
||||||
|
addresses = append(addresses, nodeInfo{name: peer.Name, addr: ""})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addresses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// (minimal, incomplete) interface so weaver can be mocked for testing.
|
||||||
|
type weaveClient interface {
|
||||||
|
RmPeer(peerName string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each of those peers that is no longer listed as a node by
|
||||||
|
// Kubernetes, remove it from Weave IPAM
|
||||||
|
func reclaimRemovedPeers(kube kubernetes.Interface, cml *configMapAnnotations, myPeerName, myNodeName string) error {
|
||||||
|
weave := weaveapi.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), common.Log)
|
||||||
|
for loopsWhenNothingChanged := 0; loopsWhenNothingChanged < 3; loopsWhenNothingChanged++ {
|
||||||
|
if err := cml.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 1. Compare peers stored in the peerList against all peers reported by k8s now.
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nodes, err := getKubePeers(kube, true)
|
||||||
|
nodeSet := make(map[string]struct{}, len(nodes))
|
||||||
|
for _, node := range nodes {
|
||||||
|
nodeSet[node.name] = struct{}{}
|
||||||
|
}
|
||||||
|
peerMap := make(map[string]peerInfo, len(storedPeerList.Peers))
|
||||||
|
for _, peer := range storedPeerList.Peers {
|
||||||
|
peerMap[peer.PeerName] = peer
|
||||||
|
}
|
||||||
|
// remove entries from the peer map that are current nodes
|
||||||
|
for key, peer := range peerMap {
|
||||||
|
if _, found := nodeSet[peer.NodeName]; found {
|
||||||
|
// unless they have a duplicate of my NodeName but are not me
|
||||||
|
if peer.NodeName == myNodeName && peer.PeerName != myPeerName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
delete(peerMap, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// so the remainder is everything we want to clean up
|
||||||
|
common.Log.Debugln("[kube-peers] Nodes that have disappeared:", peerMap)
|
||||||
|
if len(peerMap) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// 2. Loop for each X in the first set and not in the second - we wish to remove X from our data structures
|
||||||
|
for _, peer := range peerMap {
|
||||||
|
if peer.PeerName == myPeerName { // Don't remove myself.
|
||||||
|
common.Log.Warnln("[kube-peers] not removing myself", peer)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
changed, err := reclaimPeer(weave, cml, storedPeerList, 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, storedPeerList *peerList, peerName string, myPeerName string) (changed bool, err error) {
|
||||||
|
common.Log.Debugln("[kube-peers] Preparing to remove disappeared peer", peerName)
|
||||||
|
okToRemove := false
|
||||||
|
nonExistentPeer := false
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
// 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
|
||||||
|
result, err := weave.RmPeer(peerName)
|
||||||
|
common.Log.Infof("[kube-peers] rmpeer of %s: %s", peerName, result)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
err = cml.LoopUpdate(func() error {
|
||||||
|
// 7aa. Remove any annotations Z* that have contents X
|
||||||
|
if err := cml.RemoveAnnotationsWithValue(peerName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 7a. Remove X from peerList
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
storedPeerList.remove(peerName)
|
||||||
|
if err := cml.UpdatePeerList(*storedPeerList); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 7b. Remove annotation with key X
|
||||||
|
return cml.RemoveAnnotation(KubePeersPrefix + peerName)
|
||||||
|
})
|
||||||
|
// 8. If step 5 failed due to optimistic lock conflict, stop: someone else is handling X
|
||||||
|
|
||||||
|
// Step 3-5 is to protect against two simultaneous rmpeers of X
|
||||||
|
// Step 4 is to pick up again after a restart between step 5 and step 7b
|
||||||
|
// If the peer doing the reclaim disappears between steps 5 and 7a, then someone will clean it up in step 7aa
|
||||||
|
// If peer doing the reclaim disappears forever between 7a and 7b then we get a dangling annotation
|
||||||
|
// This should be sufficiently rare that we don't care.
|
||||||
|
|
||||||
|
// Question: Should we narrow step 2 by checking against Weave Net IPAM?
|
||||||
|
// i.e. If peer X owns any address space and is marked unreachable, we want to rmpeer X
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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{}) {
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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)
|
||||||
|
|
||||||
|
err = reclaimRemovedPeers(c, cml, peerName, nodeName)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Fatalf("[kube-peers] Error while reclaiming space: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
peers, err := getKubePeers(c, false)
|
||||||
|
for _, node := range peers {
|
||||||
|
fmt.Println(node.addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if runReclaimDaemon {
|
||||||
|
// Handle SIGINT and SIGTERM
|
||||||
|
ch := make(chan os.Signal)
|
||||||
|
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
registerForNodeUpdates(c, stopCh, nodeName, peerName)
|
||||||
|
<-ch
|
||||||
|
close(stopCh)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// based on k8s.io/kubernetes/pkg/controller/nodeipam/ipam/adapter.go
|
||||||
|
func setNodeNetworkUnavailableFalse(c kubernetes.Interface, nodeName string) error {
|
||||||
|
condition := v1.NodeCondition{
|
||||||
|
Type: v1.NodeNetworkUnavailable,
|
||||||
|
Status: v1.ConditionFalse,
|
||||||
|
Reason: "WeaveIsUp",
|
||||||
|
Message: "Weave pod has set this",
|
||||||
|
LastTransitionTime: metav1.Now(),
|
||||||
|
LastHeartbeatTime: metav1.Now(),
|
||||||
|
}
|
||||||
|
raw, err := json.Marshal(&[]v1.NodeCondition{condition})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
patch := []byte(fmt.Sprintf(`{"status":{"conditions":%s}}`, raw))
|
||||||
|
_, err = c.CoreV1().Nodes().PatchStatus(nodeName, patch)
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
This module deals with operations on the peerlist backed by Kubernetes' annotation mechanism.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
|
||||||
|
"github.com/weaveworks/weave/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
configMapName = "weave-net"
|
||||||
|
configMapNamespace = "kube-system"
|
||||||
|
)
|
||||||
|
|
||||||
|
type peerList struct {
|
||||||
|
Peers []peerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type peerInfo struct {
|
||||||
|
PeerName string // Weave internal unique ID
|
||||||
|
NodeName string // Kubernetes node name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pl *peerList) contains(peerName string) bool {
|
||||||
|
for _, peer := range pl.Peers {
|
||||||
|
if peer.PeerName == peerName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pl *peerList) add(peerName string, name string) {
|
||||||
|
pl.Peers = append(pl.Peers, peerInfo{PeerName: peerName, NodeName: name})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pl *peerList) remove(peerNameToRemove string) {
|
||||||
|
for i := 0; i < len(pl.Peers); {
|
||||||
|
if pl.Peers[i].PeerName == peerNameToRemove {
|
||||||
|
pl.Peers = append(pl.Peers[:i], pl.Peers[i+1:]...)
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cml *configMapAnnotations) GetPeerList() (*peerList, error) {
|
||||||
|
var record peerList
|
||||||
|
if cml.cm == nil {
|
||||||
|
return nil, errors.New("endpoint not initialized, call Init first")
|
||||||
|
}
|
||||||
|
if recordBytes, found := cml.cm.Annotations[KubePeersAnnotationKey]; found {
|
||||||
|
if err := json.Unmarshal([]byte(recordBytes), &record); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &record, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cml *configMapAnnotations) UpdatePeerList(list peerList) error {
|
||||||
|
recordBytes, err := json.Marshal(list)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return cml.UpdateAnnotation(KubePeersAnnotationKey, string(recordBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the list of all peers that have gone through this code path
|
||||||
|
func addMyselfToPeerList(cml *configMapAnnotations, c kubernetes.Interface, peerName, name string) (*peerList, error) {
|
||||||
|
var list *peerList
|
||||||
|
err := cml.LoopUpdate(func() error {
|
||||||
|
var err error
|
||||||
|
list, err = cml.GetPeerList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !list.contains(peerName) {
|
||||||
|
list.add(peerName, name)
|
||||||
|
err = cml.UpdatePeerList(*list)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkIamInPeerList(cml *configMapAnnotations, c kubernetes.Interface, peerName string) (bool, error) {
|
||||||
|
if err := cml.Init(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
list, err := cml.GetPeerList()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
common.Log.Debugf("[kube-peers] Checking peer %q against list %v", peerName, list)
|
||||||
|
return list.contains(peerName), nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
peerName1 = "01:00:00:00:00:00"
|
||||||
|
peerName2 = "02:00:00:00:00:00"
|
||||||
|
peerName3 = "03:00:00:00:00:00"
|
||||||
|
nodeName1 = "fake-node-1"
|
||||||
|
nodeName2 = "fake-node-2"
|
||||||
|
|
||||||
|
testIters = 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPeerListBasic(t *testing.T) {
|
||||||
|
c := NewSimpleClientset()
|
||||||
|
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
err := cml.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
list1, err := addMyselfToPeerList(cml, c, peerName1, nodeName1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(list1.Peers))
|
||||||
|
list2, err := addMyselfToPeerList(cml, c, peerName2, nodeName2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(list2.Peers))
|
||||||
|
|
||||||
|
check1, err := checkIamInPeerList(cml, c, peerName1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, true, check1)
|
||||||
|
check3, err := checkIamInPeerList(cml, c, peerName3)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, false, check3)
|
||||||
|
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
storedPeerList.remove(peerName1)
|
||||||
|
require.Equal(t, 1, len(storedPeerList.Peers))
|
||||||
|
err = cml.UpdatePeerList(*storedPeerList)
|
||||||
|
require.NoError(t, err)
|
||||||
|
check1, err = checkIamInPeerList(cml, c, peerName1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, false, check1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run function _f_ _iterations_ times, in _concurrency_
|
||||||
|
// number of goroutines
|
||||||
|
func doConcurrentIterations(iterations, concurrency int, f func(int)) {
|
||||||
|
iterationsPerThread := iterations / concurrency
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
for i := 0; i < concurrency; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(j int) {
|
||||||
|
defer wg.Done()
|
||||||
|
for k := 0; k < iterationsPerThread; k++ {
|
||||||
|
f((j * iterationsPerThread) + k)
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeerListFuzz(t *testing.T) {
|
||||||
|
const (
|
||||||
|
testIters = 1000
|
||||||
|
maxNodes = 100
|
||||||
|
concurrency = 5
|
||||||
|
)
|
||||||
|
peerName := func(i int) string { return fmt.Sprintf("%02d:00:00:02:00:00", i) }
|
||||||
|
nodeName := func(i int) string { return fmt.Sprintf("node%d", i) }
|
||||||
|
|
||||||
|
stateLock := sync.Mutex{}
|
||||||
|
nodes := []int{}
|
||||||
|
nodeMap := map[int]bool{} // true if an operation is pending
|
||||||
|
|
||||||
|
c := NewSimpleClientset()
|
||||||
|
|
||||||
|
addNode := func() {
|
||||||
|
i := rand.Intn(maxNodes)
|
||||||
|
// add to test state first, then to Kubernetes
|
||||||
|
stateLock.Lock()
|
||||||
|
pending, found := nodeMap[i]
|
||||||
|
if !found {
|
||||||
|
nodes = append(nodes, i)
|
||||||
|
}
|
||||||
|
nodeMap[i] = true
|
||||||
|
stateLock.Unlock()
|
||||||
|
if pending { // someone else is adding or deleting this node
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
err := cml.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = addMyselfToPeerList(cml, c, peerName(i), nodeName(i))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
stateLock.Lock()
|
||||||
|
nodeMap[i] = false
|
||||||
|
stateLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
removeNode := func() {
|
||||||
|
stateLock.Lock()
|
||||||
|
if len(nodes) == 0 { // can't remove if there aren't any nodes
|
||||||
|
stateLock.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pos := rand.Intn(len(nodes))
|
||||||
|
i := nodes[pos]
|
||||||
|
if pending, found := nodeMap[i]; found {
|
||||||
|
if pending {
|
||||||
|
stateLock.Unlock()
|
||||||
|
return // someone else is adding or deleting this node
|
||||||
|
}
|
||||||
|
nodeMap[i] = true
|
||||||
|
} else {
|
||||||
|
t.Errorf("missing entry from node map: %d", i)
|
||||||
|
}
|
||||||
|
stateLock.Unlock()
|
||||||
|
|
||||||
|
// remove from Kubernetes first, then from test state
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
require.NoError(t, cml.Init())
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
require.NoError(t, err)
|
||||||
|
found := storedPeerList.contains(peerName(i))
|
||||||
|
require.True(t, found, "peer %d not found in stored list", i)
|
||||||
|
|
||||||
|
_, err = reclaimPeer(mockWeave{}, cml, storedPeerList, peerName(i), fmt.Sprintf("deleter-%d", i))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
storedPeerList, err = cml.GetPeerList()
|
||||||
|
require.NoError(t, err)
|
||||||
|
stateLock.Lock()
|
||||||
|
delete(nodeMap, i)
|
||||||
|
for pos = 0; pos < len(nodes); pos++ {
|
||||||
|
if nodes[pos] == i {
|
||||||
|
// Remove item from list by swapping it with last and reducing slice length by 1
|
||||||
|
nodes[pos] = nodes[len(nodes)-1]
|
||||||
|
nodes = nodes[:len(nodes)-1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stateLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
doConcurrentIterations(testIters, concurrency, func(iteration int) {
|
||||||
|
r := rand.Float32()
|
||||||
|
switch {
|
||||||
|
case 0.0 <= r && r < 0.4:
|
||||||
|
addNode()
|
||||||
|
|
||||||
|
case (0.4 <= r && r < 0.8):
|
||||||
|
removeNode()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
cml := newConfigMapAnnotations(configMapNamespace, configMapName, c)
|
||||||
|
err := cml.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
storedPeerList, err := cml.GetPeerList()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, len(storedPeerList.Peers), len(nodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockWeave struct{}
|
||||||
|
|
||||||
|
func (mockWeave) RmPeer(peerName string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
@@ -1,3 +1,14 @@
|
|||||||
FROM DOCKERHUB_USER/weaveARCH_EXT
|
FROM DOCKERHUB_USER/weaveARCH_EXT
|
||||||
ADD ./launch.sh ./kube-peers /home/weave/
|
|
||||||
|
# These labels are pretty static, and can therefore be added early on:
|
||||||
|
LABEL maintainer="Weaveworks <help@weave.works>" \
|
||||||
|
org.opencontainers.image.title="weave-kube" \
|
||||||
|
org.opencontainers.image.source="https://github.com/weaveworks/weave" \
|
||||||
|
org.opencontainers.image.vendor="Weaveworks"
|
||||||
|
|
||||||
|
ADD ./launch.sh ./kube-utils /home/weave/
|
||||||
ENTRYPOINT ["/home/weave/launch.sh"]
|
ENTRYPOINT ["/home/weave/launch.sh"]
|
||||||
|
|
||||||
|
# This label will change for every build, and should therefore be the last layer of the image:
|
||||||
|
ARG revision
|
||||||
|
LABEL org.opencontainers.image.revision="${revision}"
|
||||||
@@ -27,9 +27,9 @@ xt_set_exists() {
|
|||||||
# Default if not supplied - same as weave net default
|
# Default if not supplied - same as weave net default
|
||||||
IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
|
IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
|
||||||
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
||||||
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
|
METRICS_ADDR=${WEAVE_METRICS_ADDR:-0.0.0.0:6782}
|
||||||
HOST_ROOT=${HOST_ROOT:-/host}
|
HOST_ROOT=${HOST_ROOT:-/host}
|
||||||
CONN_LIMIT=${CONN_LIMIT:-30}
|
CONN_LIMIT=${CONN_LIMIT:-100}
|
||||||
DB_PREFIX=${DB_PREFIX:-/weavedb/weave-net}
|
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 +54,23 @@ else
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
STATUS_OPTS="--metrics-addr=$METRICS_ADDR"
|
||||||
|
# --status-addr exposes internal information, so only turn it on if asked to.
|
||||||
|
if [ -n "${WEAVE_STATUS_ADDR}" ]; then
|
||||||
|
STATUS_OPTS="$STATUS_OPTS --status-addr=$WEAVE_STATUS_ADDR"
|
||||||
|
fi
|
||||||
|
|
||||||
# Default is that npc will be running; allow caller to override
|
# Default is that npc will be running; allow caller to override
|
||||||
WEAVE_NPC_OPTS="--expect-npc"
|
WEAVE_NPC_OPTS="--expect-npc"
|
||||||
if [ "${EXPECT_NPC}" = "0" ]; then
|
if [ "${EXPECT_NPC}" = "0" ]; then
|
||||||
WEAVE_NPC_OPTS=""
|
WEAVE_NPC_OPTS=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
NO_MASQ_LOCAL_OPT=""
|
||||||
|
if [ -n "${NO_MASQ_LOCAL}" ]; then
|
||||||
|
NO_MASQ_LOCAL_OPT="--no-masq-local"
|
||||||
|
fi
|
||||||
|
|
||||||
# Kubernetes sets HOSTNAME to the host's hostname
|
# Kubernetes sets HOSTNAME to the host's hostname
|
||||||
# when running a pod in host namespace.
|
# when running a pod in host namespace.
|
||||||
NICKNAME_ARG=""
|
NICKNAME_ARG=""
|
||||||
@@ -74,7 +85,7 @@ router_bridge_opts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if [ -z "$KUBE_PEERS" ]; then
|
if [ -z "$KUBE_PEERS" ]; then
|
||||||
if ! KUBE_PEERS=$(/home/weave/kube-peers) || [ -z "$KUBE_PEERS" ]; then
|
if ! KUBE_PEERS=$(/home/weave/kube-utils) || [ -z "$KUBE_PEERS" ]; then
|
||||||
echo Failed to get peers >&2
|
echo Failed to get peers >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -99,7 +110,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 +144,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 +160,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
|
||||||
@@ -110,12 +110,11 @@ 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: 10m
|
||||||
|
|||||||
@@ -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,12 +119,11 @@ 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: 10m
|
||||||
@@ -156,7 +154,6 @@ 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: 10m
|
||||||
|
|||||||
@@ -0,0 +1,208 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: List
|
||||||
|
items:
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- pods
|
||||||
|
- namespaces
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- extensions
|
||||||
|
resources:
|
||||||
|
- networkpolicies
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- 'networking.k8s.io'
|
||||||
|
resources:
|
||||||
|
- networkpolicies
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- nodes/status
|
||||||
|
verbs:
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
roleRef:
|
||||||
|
kind: ClusterRole
|
||||||
|
name: weave-net
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
resourceNames:
|
||||||
|
- weave-net
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- ''
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
name: weave-net
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
- apiVersion: extensions/v1beta1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: weave-net
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
# Wait 5 seconds to let pod connect before rolling next pod
|
||||||
|
minReadySeconds: 5
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
name: weave-net
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: weave
|
||||||
|
command:
|
||||||
|
- /home/weave/launch.sh
|
||||||
|
env:
|
||||||
|
- name: HOSTNAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
image: 'weaveworks/weave-kube:latest'
|
||||||
|
imagePullPolicy: Always
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
host: 127.0.0.1
|
||||||
|
path: /status
|
||||||
|
port: 6784
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: weavedb
|
||||||
|
mountPath: /weavedb
|
||||||
|
- name: cni-bin
|
||||||
|
mountPath: /host/opt
|
||||||
|
- name: cni-bin2
|
||||||
|
mountPath: /host/home
|
||||||
|
- name: cni-conf
|
||||||
|
mountPath: /host/etc
|
||||||
|
- name: dbus
|
||||||
|
mountPath: /host/var/lib/dbus
|
||||||
|
- name: lib-modules
|
||||||
|
mountPath: /lib/modules
|
||||||
|
- name: xtables-lock
|
||||||
|
mountPath: /run/xtables.lock
|
||||||
|
readOnly: false
|
||||||
|
- name: weave-npc
|
||||||
|
env:
|
||||||
|
- name: HOSTNAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
image: 'weaveworks/weave-npc:latest'
|
||||||
|
imagePullPolicy: Always
|
||||||
|
#npc-args
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: xtables-lock
|
||||||
|
mountPath: /run/xtables.lock
|
||||||
|
readOnly: false
|
||||||
|
hostNetwork: true
|
||||||
|
hostPID: true
|
||||||
|
restartPolicy: Always
|
||||||
|
securityContext:
|
||||||
|
seLinuxOptions: {}
|
||||||
|
serviceAccountName: weave-net
|
||||||
|
tolerations:
|
||||||
|
- effect: NoSchedule
|
||||||
|
operator: Exists
|
||||||
|
volumes:
|
||||||
|
- name: weavedb
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/weave
|
||||||
|
- name: cni-bin
|
||||||
|
hostPath:
|
||||||
|
path: /opt
|
||||||
|
- name: cni-bin2
|
||||||
|
hostPath:
|
||||||
|
path: /home
|
||||||
|
- name: cni-conf
|
||||||
|
hostPath:
|
||||||
|
path: /etc
|
||||||
|
- name: dbus
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/dbus
|
||||||
|
- name: lib-modules
|
||||||
|
hostPath:
|
||||||
|
path: /lib/modules
|
||||||
|
- name: xtables-lock
|
||||||
|
hostPath:
|
||||||
|
path: /run/xtables.lock
|
||||||
|
type: FileOrCreate
|
||||||
|
updateStrategy:
|
||||||
|
type: RollingUpdate
|
||||||
@@ -36,12 +36,11 @@ 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: 10m
|
||||||
|
|||||||
@@ -1,17 +1,24 @@
|
|||||||
FROM ALPINE_BASEIMAGE
|
FROM ALPINE_BASEIMAGE
|
||||||
|
|
||||||
# If we're building for another architecture than amd64, the CROSS_BUILD_ placeholder is removed so e.g. CROSS_BUILD_COPY turns into COPY
|
# These labels are pretty static, and can therefore be added early on:
|
||||||
# If we're building normally, for amd64, CROSS_BUILD lines are removed
|
LABEL works.weave.role="system" \
|
||||||
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
maintainer="Weaveworks <help@weave.works>" \
|
||||||
|
org.opencontainers.image.title="Weave Net" \
|
||||||
LABEL maintainer "Weaveworks Inc <help@weave.works>"
|
org.opencontainers.image.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||||
LABEL works.weave.role=system \
|
org.opencontainers.image.url="https://weave.works" \
|
||||||
|
org.opencontainers.image.source="https://github.com/weaveworks/weave" \
|
||||||
|
org.opencontainers.image.vendor="Weaveworks" \
|
||||||
|
org.label-schema.schema-version="1.0" \
|
||||||
org.label-schema.name="Weave Net" \
|
org.label-schema.name="Weave Net" \
|
||||||
org.label-schema.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
org.label-schema.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||||
org.label-schema.url="https://weave.works" \
|
org.label-schema.url="https://weave.works" \
|
||||||
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
||||||
org.label-schema.vendor="Weaveworks"
|
org.label-schema.vendor="Weaveworks"
|
||||||
|
|
||||||
|
# If we're building for another architecture than amd64, the CROSS_BUILD_ placeholder is removed so e.g. CROSS_BUILD_COPY turns into COPY
|
||||||
|
# If we're building normally, for amd64, CROSS_BUILD lines are removed
|
||||||
|
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
||||||
|
|
||||||
RUN apk add --update \
|
RUN apk add --update \
|
||||||
iptables \
|
iptables \
|
||||||
ipset \
|
ipset \
|
||||||
@@ -21,3 +28,8 @@ RUN apk add --update \
|
|||||||
COPY ./weave-npc /usr/bin/weave-npc
|
COPY ./weave-npc /usr/bin/weave-npc
|
||||||
COPY ./ulogd.conf /etc/ulogd.conf
|
COPY ./ulogd.conf /etc/ulogd.conf
|
||||||
ENTRYPOINT ["/usr/bin/weave-npc"]
|
ENTRYPOINT ["/usr/bin/weave-npc"]
|
||||||
|
|
||||||
|
# These labels will change for every build, and should therefore be the last layer of the image:
|
||||||
|
ARG revision
|
||||||
|
LABEL org.opencontainers.image.revision="${revision}" \
|
||||||
|
org.label-schema.vcs-ref="${revision}"
|
||||||
+117
-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-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,78 @@ 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 -j RETURN
|
||||||
|
// -A WEAVE-NPC-EGRESS -d 224.0.0.0/4 -j RETURN
|
||||||
|
// -A WEAVE-NPC-EGRESS -m state --state NEW -j WEAVE-NPC-EGRESS-DEFAULT
|
||||||
|
// -A WEAVE-NPC-EGRESS -m state --state NEW -m mark ! --mark 0x40000/0x40000 -j WEAVE-NPC-EGRESS-CUSTOM
|
||||||
|
// -A WEAVE-NPC-EGRESS -m state --state NEW -m mark ! --mark 0x40000/0x40000 -j NFLOG --nflog-group 86
|
||||||
|
// -A WEAVE-NPC-EGRESS -m mark ! --mark 0x40000/0x40000 -j DROP
|
||||||
|
//
|
||||||
|
// -A WEAVE-NPC-EGRESS-CUSTOM <rulespec> -j MARK --set-xmark 0x40000/0x40000
|
||||||
|
// -A WEAVE-NPC-EGRESS-CUSTOM <rulespec> -j RETURN
|
||||||
|
//
|
||||||
|
// -A WEAVE-NPC-EGRESS-DEFAULT <rulespec> -j MARK --set-xmark 0x40000/0x40000
|
||||||
|
// -A WEAVE-NPC-EGRESS-DEFAULT <rulespec> -j RETURN
|
||||||
|
//
|
||||||
|
// For each rule we create two (mark and return). We cannot just accept
|
||||||
|
// a packet if it matches any rule, as a packet might need to traverse
|
||||||
|
// the ingress npc as well which happens later in the chain (in some cases
|
||||||
|
// we cannot detect whether packet is ingress or egress, so we need to
|
||||||
|
// check both chains).
|
||||||
|
|
||||||
|
ruleSpecs := [][]string{
|
||||||
|
{"-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"},
|
||||||
|
{"-m", "physdev", "--physdev-in=" + bridgePortName, "-j", "RETURN"},
|
||||||
|
}
|
||||||
|
if allowMcast {
|
||||||
|
ruleSpecs = append(ruleSpecs, []string{"-d", "224.0.0.0/4", "-j", "RETURN"})
|
||||||
|
}
|
||||||
|
ruleSpecs = append(ruleSpecs, [][]string{
|
||||||
|
{"-m", "state", "--state", "NEW", "-j", string(npc.EgressDefaultChain)},
|
||||||
|
{"-m", "state", "--state", "NEW", "-m", "mark", "!", "--mark", npc.EgressMark, "-j", string(npc.EgressCustomChain)},
|
||||||
|
{"-m", "state", "--state", "NEW", "-m", "mark", "!", "--mark", npc.EgressMark, "-j", "NFLOG", "--nflog-group", "86"},
|
||||||
|
{"-m", "mark", "!", "--mark", npc.EgressMark, "-j", "DROP"},
|
||||||
|
}...)
|
||||||
|
if err := net.AddChainWithRules(ipt, npc.TableFilter, npc.EgressChain, ruleSpecs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 +229,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 +246,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)
|
||||||
|
|
||||||
nsController := makeController(client.Core().RESTClient(), "namespaces", &coreapi.Namespace{},
|
nsController := makeController(client.Core().RESTClient(), "namespaces", &coreapi.Namespace{},
|
||||||
cache.ResourceEventHandlerFuncs{
|
cache.ResourceEventHandlerFuncs{
|
||||||
@@ -219,11 +313,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 +334,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
|
||||||
|
|||||||
+25
-8
@@ -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
|
||||||
@@ -192,6 +194,7 @@ func main() {
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
docker "github.com/fsouza/go-dockerclient"
|
docker "github.com/fsouza/go-dockerclient"
|
||||||
)
|
)
|
||||||
@@ -58,7 +59,7 @@ func isDockerPluginEnabled(args []string) error {
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
plugins, err := c.PluginList(ctx)
|
plugins, err := c.PluginList(ctx, filters.Args{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -225,7 +225,7 @@ func (proxy *Proxy) findVolume(v string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
Log.Infof("%s %s", r.Method, r.URL)
|
Log.Debugf("%s %s", r.Method, r.URL)
|
||||||
path := r.URL.Path
|
path := r.URL.Path
|
||||||
var i interceptor
|
var i interceptor
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
+45
-7
@@ -582,9 +582,11 @@ type fastDatapathForwarder struct {
|
|||||||
ackedHeartbeat bool
|
ackedHeartbeat bool
|
||||||
stopChan chan struct{}
|
stopChan chan struct{}
|
||||||
stopped bool
|
stopped bool
|
||||||
|
healthy bool
|
||||||
establishedChan chan struct{}
|
established bool
|
||||||
errorChan chan error
|
establishedChan chan struct{}
|
||||||
|
errorChan chan error
|
||||||
|
healthChan chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fastdp fastDatapathOverlay) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
|
func (fastdp fastDatapathOverlay) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
|
||||||
@@ -622,6 +624,7 @@ func (fastdp fastDatapathOverlay) PrepareConnection(params mesh.OverlayConnectio
|
|||||||
connUID: params.ConnUID,
|
connUID: params.ConnUID,
|
||||||
vxlanVportID: vxlanVportID,
|
vxlanVportID: vxlanVportID,
|
||||||
sessionKey: params.SessionKey,
|
sessionKey: params.SessionKey,
|
||||||
|
healthy: true,
|
||||||
|
|
||||||
remoteAddr: remoteAddr,
|
remoteAddr: remoteAddr,
|
||||||
heartbeatInterval: FastHeartbeat,
|
heartbeatInterval: FastHeartbeat,
|
||||||
@@ -629,6 +632,7 @@ func (fastdp fastDatapathOverlay) PrepareConnection(params mesh.OverlayConnectio
|
|||||||
|
|
||||||
establishedChan: make(chan struct{}),
|
establishedChan: make(chan struct{}),
|
||||||
errorChan: make(chan error, 1),
|
errorChan: make(chan error, 1),
|
||||||
|
healthChan: make(chan bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
return fwd, nil
|
return fwd, nil
|
||||||
@@ -699,19 +703,40 @@ func (fwd *fastDatapathForwarder) ErrorChannel() <-chan error {
|
|||||||
return fwd.errorChan
|
return fwd.errorChan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fwd *fastDatapathForwarder) HealthChannel() <-chan bool {
|
||||||
|
return fwd.healthChan
|
||||||
|
}
|
||||||
|
|
||||||
func (fwd *fastDatapathForwarder) doHeartbeats() {
|
func (fwd *fastDatapathForwarder) doHeartbeats() {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
for err == nil {
|
for err == nil {
|
||||||
select {
|
select {
|
||||||
case <-fwd.heartbeatTimer.C:
|
case <-fwd.heartbeatTimer.C:
|
||||||
if fwd.confirmed {
|
if fwd.confirmed {
|
||||||
|
log.Debug(fwd.logPrefix(), "sending Heartbeat to peer")
|
||||||
fwd.sendHeartbeat()
|
fwd.sendHeartbeat()
|
||||||
}
|
}
|
||||||
fwd.heartbeatTimer.Reset(fwd.heartbeatInterval)
|
fwd.heartbeatTimer.Reset(fwd.heartbeatInterval)
|
||||||
|
|
||||||
case <-fwd.heartbeatTimeout.C:
|
case <-fwd.heartbeatTimeout.C:
|
||||||
err = fmt.Errorf("timed out waiting for vxlan heartbeat")
|
log.Debug(fwd.logPrefix(), "missed Heartbeat from peer, marking fastdp forwarder as un-healthy")
|
||||||
|
|
||||||
|
// treat missed heartbeat as transient error. Indicate to overlay forwarder
|
||||||
|
// that Forwarder is un-healthy so it can pick next best forwarder
|
||||||
|
fwd.healthChan <- false
|
||||||
|
|
||||||
|
// switch from fast-heartbeat to slow-heartbeat to avoid aggressive heartbeats
|
||||||
|
// when there is no estbalished session with peer yet
|
||||||
|
if fwd.heartbeatInterval != SlowHeartbeat {
|
||||||
|
fwd.heartbeatInterval = SlowHeartbeat
|
||||||
|
if fwd.heartbeatTimer != nil {
|
||||||
|
fwd.heartbeatTimer.Reset(fwd.heartbeatInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fwd.healthy {
|
||||||
|
fwd.healthy = false
|
||||||
|
}
|
||||||
|
|
||||||
case <-fwd.stopChan:
|
case <-fwd.stopChan:
|
||||||
return
|
return
|
||||||
@@ -744,7 +769,6 @@ func (fwd *fastDatapathForwarder) handleError(err error) {
|
|||||||
|
|
||||||
func (fwd *fastDatapathForwarder) sendHeartbeat() {
|
func (fwd *fastDatapathForwarder) sendHeartbeat() {
|
||||||
fwd.lock.RLock()
|
fwd.lock.RLock()
|
||||||
log.Debug(fwd.logPrefix(), "sendHeartbeat")
|
|
||||||
|
|
||||||
// the heartbeat payload consists of the 64-bit connection uid
|
// the heartbeat payload consists of the 64-bit connection uid
|
||||||
// followed by the 16-bit packet size.
|
// followed by the 16-bit packet size.
|
||||||
@@ -801,13 +825,23 @@ func (fwd *fastDatapathForwarder) handleVxlanSpecialPacket(frame []byte, sender
|
|||||||
|
|
||||||
if !fwd.ackedHeartbeat {
|
if !fwd.ackedHeartbeat {
|
||||||
fwd.ackedHeartbeat = true
|
fwd.ackedHeartbeat = true
|
||||||
|
log.Debug(fwd.logPrefix(), "Ack Heartbeat from peer")
|
||||||
fwd.handleError(fwd.sendControlMsg(FastDatapathHeartbeatAck, nil))
|
fwd.handleError(fwd.sendControlMsg(FastDatapathHeartbeatAck, nil))
|
||||||
|
} else {
|
||||||
|
log.Debug(fwd.logPrefix(), "Got Heartbeat Ack from peer")
|
||||||
}
|
}
|
||||||
|
|
||||||
// we can receive a heartbeat before Confirm() has set up
|
// we can receive a heartbeat before Confirm() has set up
|
||||||
// heartbeatTimeout
|
// heartbeatTimeout
|
||||||
if fwd.heartbeatTimeout != nil {
|
if fwd.heartbeatTimeout != nil {
|
||||||
fwd.heartbeatTimeout.Reset(HeartbeatTimeout)
|
fwd.heartbeatTimeout.Reset(HeartbeatTimeout)
|
||||||
|
if !fwd.healthy {
|
||||||
|
// If fastdp was marked as unhealthy earlier due to missed heartbeat then indicate to
|
||||||
|
// overlay forwarder that Forwarder is healthy now so it can pick next best forwarder
|
||||||
|
log.Debug(fwd.logPrefix(), "got Heartbeat from peer, marking fastdp forwarder as healthy")
|
||||||
|
fwd.healthy = true
|
||||||
|
fwd.healthChan <- true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -833,8 +867,12 @@ func (fwd *fastDatapathForwarder) Attrs() map[string]interface{} {
|
|||||||
func (fwd *fastDatapathForwarder) handleHeartbeatAck() {
|
func (fwd *fastDatapathForwarder) handleHeartbeatAck() {
|
||||||
log.Debug(fwd.logPrefix(), "handleHeartbeatAck")
|
log.Debug(fwd.logPrefix(), "handleHeartbeatAck")
|
||||||
|
|
||||||
if fwd.heartbeatInterval != SlowHeartbeat {
|
if !fwd.established {
|
||||||
close(fwd.establishedChan)
|
close(fwd.establishedChan)
|
||||||
|
fwd.established = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if fwd.heartbeatInterval != SlowHeartbeat {
|
||||||
fwd.heartbeatInterval = SlowHeartbeat
|
fwd.heartbeatInterval = SlowHeartbeat
|
||||||
if fwd.heartbeatTimer != nil {
|
if fwd.heartbeatTimer != nil {
|
||||||
fwd.heartbeatTimer.Reset(fwd.heartbeatInterval)
|
fwd.heartbeatTimer.Reset(fwd.heartbeatInterval)
|
||||||
|
|||||||
+9
-1
@@ -3,6 +3,7 @@ package router
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
@@ -37,7 +38,14 @@ func (router *NetworkRouter) HandleHTTP(muxRouter *mux.Router) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = net.Expose(router.BridgeConfig.WeaveBridgeName, cidr.IPNet(), router.BridgeConfig.AWSVPC, router.BridgeConfig.NPC); err != nil {
|
var skipNAT bool
|
||||||
|
if r.FormValue("skipNAT") != "" {
|
||||||
|
if skipNAT, err = strconv.ParseBool(r.FormValue("skipNAT")); err != nil {
|
||||||
|
http.Error(w, fmt.Sprint("unable to parse skipNAT option: ", err.Error()), http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = net.Expose(router.BridgeConfig.WeaveBridgeName, cidr.IPNet(), router.BridgeConfig.AWSVPC, router.BridgeConfig.NPC, skipNAT); err != nil {
|
||||||
http.Error(w, fmt.Sprint("unable to expose: ", err.Error()), http.StatusInternalServerError)
|
http.Error(w, fmt.Sprint("unable to expose: ", err.Error()), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ type OverlayForwarder interface {
|
|||||||
// Confirm(). The return value nil means the key could not be
|
// Confirm(). The return value nil means the key could not be
|
||||||
// handled by this forwarder.
|
// handled by this forwarder.
|
||||||
Forward(ForwardPacketKey) FlowOp
|
Forward(ForwardPacketKey) FlowOp
|
||||||
|
|
||||||
|
HealthChannel() <-chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type NullNetworkOverlay struct{ mesh.NullOverlay }
|
type NullNetworkOverlay struct{ mesh.NullOverlay }
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ type overlaySwitchForwarder struct {
|
|||||||
alreadyEstablished bool
|
alreadyEstablished bool
|
||||||
establishedChan chan struct{}
|
establishedChan chan struct{}
|
||||||
errorChan chan error
|
errorChan chan error
|
||||||
|
healthChan chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// A subsidiary forwarder
|
// A subsidiary forwarder
|
||||||
@@ -154,6 +155,9 @@ type subForwarder struct {
|
|||||||
|
|
||||||
// closed to tell the forwarder monitor goroutine to stop
|
// closed to tell the forwarder monitor goroutine to stop
|
||||||
stopChan chan<- struct{}
|
stopChan chan<- struct{}
|
||||||
|
|
||||||
|
// set to true to skip this forwarder in chooseBest()
|
||||||
|
onHold bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// An event from a subsidiary forwarder
|
// An event from a subsidiary forwarder
|
||||||
@@ -166,6 +170,9 @@ type subForwarderEvent struct {
|
|||||||
|
|
||||||
// is this an error event?
|
// is this an error event?
|
||||||
err error
|
err error
|
||||||
|
|
||||||
|
// event to indicate if forwarder is in healthy state to be considered for best forwarded
|
||||||
|
healthy *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (osw *OverlaySwitch) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
|
func (osw *OverlaySwitch) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
|
||||||
@@ -248,6 +255,9 @@ loop:
|
|||||||
e.established = true
|
e.established = true
|
||||||
establishedChan = nil
|
establishedChan = nil
|
||||||
|
|
||||||
|
case healthy := <-fwd.HealthChannel():
|
||||||
|
e.healthy = &healthy
|
||||||
|
|
||||||
case err := <-fwd.ErrorChannel():
|
case err := <-fwd.ErrorChannel():
|
||||||
e.err = err
|
e.err = err
|
||||||
|
|
||||||
@@ -282,6 +292,8 @@ loop:
|
|||||||
fwd.established(e.index)
|
fwd.established(e.index)
|
||||||
case e.err != nil:
|
case e.err != nil:
|
||||||
fwd.error(e.index, e.err)
|
fwd.error(e.index, e.err)
|
||||||
|
case e.healthy != nil:
|
||||||
|
fwd.healthCheck(e.index, *e.healthy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,6 +330,23 @@ func (fwd *overlaySwitchForwarder) error(index int, err error) {
|
|||||||
fwd.chooseBest()
|
fwd.chooseBest()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fwd *overlaySwitchForwarder) healthCheck(index int, healthy bool) {
|
||||||
|
fwd.lock.Lock()
|
||||||
|
defer fwd.lock.Unlock()
|
||||||
|
|
||||||
|
if healthy == true && fwd.forwarders[index].onHold {
|
||||||
|
log.Debug(fwd.logPrefix(), "Adding "+fwd.forwarders[index].overlayName+" to the list of forwarders")
|
||||||
|
fwd.forwarders[index].onHold = false
|
||||||
|
fwd.chooseBest()
|
||||||
|
}
|
||||||
|
|
||||||
|
if healthy == false && !fwd.forwarders[index].onHold {
|
||||||
|
log.Debug(fwd.logPrefix(), "Removing "+fwd.forwarders[index].overlayName+" from the list of forwarders")
|
||||||
|
fwd.forwarders[index].onHold = true
|
||||||
|
fwd.chooseBest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (fwd *overlaySwitchForwarder) stopFrom(index int) {
|
func (fwd *overlaySwitchForwarder) stopFrom(index int) {
|
||||||
for index < len(fwd.forwarders) {
|
for index < len(fwd.forwarders) {
|
||||||
subFwd := &fwd.forwarders[index]
|
subFwd := &fwd.forwarders[index]
|
||||||
@@ -337,7 +366,7 @@ func (fwd *overlaySwitchForwarder) chooseBest() {
|
|||||||
|
|
||||||
for i := range fwd.forwarders {
|
for i := range fwd.forwarders {
|
||||||
subFwd := &fwd.forwarders[i]
|
subFwd := &fwd.forwarders[i]
|
||||||
if subFwd.fwd == nil {
|
if subFwd.fwd == nil || subFwd.onHold {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,6 +444,10 @@ func (fwd *overlaySwitchForwarder) ErrorChannel() <-chan error {
|
|||||||
return fwd.errorChan
|
return fwd.errorChan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fwd *overlaySwitchForwarder) HealthChannel() <-chan bool {
|
||||||
|
return fwd.healthChan
|
||||||
|
}
|
||||||
|
|
||||||
func (fwd *overlaySwitchForwarder) Stop() {
|
func (fwd *overlaySwitchForwarder) Stop() {
|
||||||
fwd.lock.Lock()
|
fwd.lock.Lock()
|
||||||
defer fwd.lock.Unlock()
|
defer fwd.lock.Unlock()
|
||||||
|
|||||||
@@ -326,6 +326,7 @@ type sleeveForwarder struct {
|
|||||||
// listener channels
|
// listener channels
|
||||||
establishedChan chan struct{}
|
establishedChan chan struct{}
|
||||||
errorChan chan error
|
errorChan chan error
|
||||||
|
healthChan chan bool
|
||||||
|
|
||||||
// Explicitly locked state
|
// Explicitly locked state
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
@@ -445,6 +446,10 @@ func (fwd *sleeveForwarder) ErrorChannel() <-chan error {
|
|||||||
return fwd.errorChan
|
return fwd.errorChan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fwd *sleeveForwarder) HealthChannel() <-chan bool {
|
||||||
|
return fwd.healthChan
|
||||||
|
}
|
||||||
|
|
||||||
type curriedForward struct {
|
type curriedForward struct {
|
||||||
NonDiscardingFlowOp
|
NonDiscardingFlowOp
|
||||||
fwd *sleeveForwarder
|
fwd *sleeveForwarder
|
||||||
|
|||||||
+17
-15
@@ -6,27 +6,28 @@ search_type: Documentation
|
|||||||
You only need to build Weave Net if you want to work on the Weave Net codebase
|
You only need to build Weave Net if you want to work on the Weave Net codebase
|
||||||
(or you just enjoy building software).
|
(or you just enjoy building software).
|
||||||
|
|
||||||
Apart from the `weave` shell script, weave is delivered as a set of
|
Apart from the `weave` shell script, Weave Net is delivered as a set of
|
||||||
container images. There is no distribution-specific packaging, so in
|
container images. There is no distribution-specific packaging, so in
|
||||||
principle it shouldn't matter which Linux distribution you build
|
principle it shouldn't matter which Linux distribution you build
|
||||||
under. But naturally, Docker is a prerequisite (version 1.6.0 or
|
under. But naturally, Docker is a prerequisite (version 1.6.0 or
|
||||||
later).
|
later).
|
||||||
|
|
||||||
The only way to build is by using the the build container; the
|
The only way to build is by using the the build container; the
|
||||||
Makefile is setup to make this transparent. This method is
|
`Makefile` is setup to make this transparent. This method is
|
||||||
documented below, and can be run directly on your machine or using
|
documented below, and can be run directly on your machine or using
|
||||||
a Vagrant VM.
|
a Vagrant VM.
|
||||||
|
|
||||||
## <a name="ubuntu"></a>Building directly on your machine
|
## <a name="ubuntu"></a>Building directly on your machine
|
||||||
|
|
||||||
The weave git repository should be cloned into
|
The weave git repository should be cloned into
|
||||||
`$GOPATH/src/github.com/weaveworks/weave`, in accordance with [the go
|
`$GOPATH/src/github.com/weaveworks/weave`, in accordance with [the Go
|
||||||
workspace conventions](https://golang.org/doc/code.html#Workspaces):
|
workspace conventions](https://golang.org/doc/code.html#Workspaces):
|
||||||
|
|
||||||
```
|
```
|
||||||
$ WEAVE=github.com/weaveworks/weave
|
$ WEAVE=github.com/weaveworks/weave
|
||||||
$ git clone https://$WEAVE $GOPATH/src/$WEAVE
|
$ git clone https://$WEAVE $GOPATH/src/$WEAVE
|
||||||
$ cd $GOPATH/src/$WEAVE
|
$ cd $GOPATH/src/$WEAVE
|
||||||
|
$ git submodule update --init
|
||||||
```
|
```
|
||||||
|
|
||||||
Next install Docker if you haven't already, by following the instructions
|
Next install Docker if you haven't already, by following the instructions
|
||||||
@@ -44,7 +45,7 @@ On a fresh repository, the Makefile will do the following:
|
|||||||
- build the weave components in the build container
|
- build the weave components in the build container
|
||||||
- package them into two Docker images (`weaveworks/weave`,
|
- package them into two Docker images (`weaveworks/weave`,
|
||||||
`weaveworks/weaveexec`)
|
`weaveworks/weaveexec`)
|
||||||
- Exported these images as `weave.tar.gz`
|
- export these images as `weave.tar.gz`.
|
||||||
|
|
||||||
The first two steps may take a while - don't worry, they are
|
The first two steps may take a while - don't worry, they are
|
||||||
are cached and should not need to be redone very often.
|
are cached and should not need to be redone very often.
|
||||||
@@ -63,18 +64,19 @@ First, check out the code:
|
|||||||
```
|
```
|
||||||
$ git clone https://github.com/weaveworks/weave
|
$ git clone https://github.com/weaveworks/weave
|
||||||
$ cd weave
|
$ cd weave
|
||||||
|
$ git submodule update --init
|
||||||
```
|
```
|
||||||
|
|
||||||
The `Vagrantfile` in the top directory constructs a VM that has
|
The `Vagrantfile` in the top directory constructs a VM that has:
|
||||||
|
|
||||||
* docker installed
|
* Docker installed
|
||||||
* go tools installed
|
* Go tools installed
|
||||||
* weave dependencies installed
|
* Weave's dependencies installed
|
||||||
* $GOPATH set to ~
|
* `$GOPATH` set to `~`
|
||||||
* the local working directory mapped as a synced folder into the
|
* the local working directory mapped as a synced folder into the
|
||||||
right place in $GOPATH
|
right place in `$GOPATH`.
|
||||||
|
|
||||||
Once you are in the working directory you can issue
|
Once you are in the working directory you can issue:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ vagrant up
|
$ vagrant up
|
||||||
@@ -86,7 +88,7 @@ installation is done just once). The working directory is sync'ed with
|
|||||||
use git and so on in the regular filesystem.
|
use git and so on in the regular filesystem.
|
||||||
|
|
||||||
To build and run the code, you need to use the VM. To log in and build
|
To build and run the code, you need to use the VM. To log in and build
|
||||||
the weave image, do
|
the weave image, do:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ vagrant ssh
|
$ vagrant ssh
|
||||||
@@ -94,7 +96,7 @@ vm$ cd src/github.com/weaveworks/weave
|
|||||||
vm$ make
|
vm$ make
|
||||||
```
|
```
|
||||||
|
|
||||||
The Docker daemon is also running in this VM, so you can then do
|
The Docker daemon is also running in this VM, so you can then do:
|
||||||
|
|
||||||
```
|
```
|
||||||
vm$ sudo ./weave launch
|
vm$ sudo ./weave launch
|
||||||
@@ -104,7 +106,7 @@ vm$ sudo docker ps
|
|||||||
and so on.
|
and so on.
|
||||||
|
|
||||||
If you are looking to just do a build and not run anything on this VM,
|
If you are looking to just do a build and not run anything on this VM,
|
||||||
you can do so with
|
you can do so with:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ vagrant ssh -c 'make -C src/github.com/weaveworks/weave'
|
$ vagrant ssh -c 'make -C src/github.com/weaveworks/weave'
|
||||||
@@ -112,7 +114,7 @@ $ vagrant ssh -c 'make -C src/github.com/weaveworks/weave'
|
|||||||
|
|
||||||
you should then find a `weave.tar.gz` container snapshot tarball in the
|
you should then find a `weave.tar.gz` container snapshot tarball in the
|
||||||
top-level directory. You can use that snapshot with `docker load`
|
top-level directory. You can use that snapshot with `docker load`
|
||||||
against a different host, e.g.
|
against a different host, e.g.:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ export DOCKER_HOST=tcp://<HOST:PORT>
|
$ export DOCKER_HOST=tcp://<HOST:PORT>
|
||||||
|
|||||||
+14
-1
@@ -40,6 +40,18 @@ A simple way to accomplish this would be to run Weave on the host and then run,
|
|||||||
Yet another option is to expose a port from the container on host B and then connect to it. You can read about exposing ports in [Exporting Services](/site/tasks/manage/service-management.md#exporting).
|
Yet another option is to expose a port from the container on host B and then connect to it. You can read about exposing ports in [Exporting Services](/site/tasks/manage/service-management.md#exporting).
|
||||||
|
|
||||||
|
|
||||||
|
<a name="duplicate-peer"></a>
|
||||||
|
**Q: Why am I seeing "peer names collision" and failed connections?**
|
||||||
|
|
||||||
|
This sometimes happens when machines are cloned; we require each
|
||||||
|
machine in your cluster to have a unique identity.
|
||||||
|
|
||||||
|
For more information see [Peer Names](/site/operational-guide/concepts.md#peer-name).
|
||||||
|
|
||||||
|
Depending on your Linux distribution you may need to [set up
|
||||||
|
machine-id](https://www.freedesktop.org/software/systemd/man/machine-id.html)
|
||||||
|
or [generate a dbus id](https://dbus.freedesktop.org/doc/dbus-uuidgen.1.html).
|
||||||
|
|
||||||
<a name="duplicate-ip"></a>
|
<a name="duplicate-ip"></a>
|
||||||
**Q: Why am I seeing the same IP address assigned to two different containers on different hosts?**
|
**Q: Why am I seeing the same IP address assigned to two different containers on different hosts?**
|
||||||
|
|
||||||
@@ -92,7 +104,8 @@ Yes, of course! Weave allows you to run isolated networks and still allow open
|
|||||||
You must permit traffic to flow through TCP 6783 and UDP 6783/6784,
|
You must permit traffic to flow through TCP 6783 and UDP 6783/6784,
|
||||||
which are Weave’s control and data ports.
|
which are Weave’s control and data ports.
|
||||||
|
|
||||||
The daemon also uses TCP port 6782 for [metrics](/site/tasks/manage/metrics.md), but
|
The daemon also uses TCP 6781/6782 for
|
||||||
|
[metrics](/site/tasks/manage/metrics.md#metrics-endpoint-addresses), but
|
||||||
you would only need to open up this port if you wish to collect metrics
|
you would only need to open up this port if you wish to collect metrics
|
||||||
from another host.
|
from another host.
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ Install Weave Net from the command line on its own or if you are using Docker, K
|
|||||||
* [Integrating Kubernetes and Mesos via the CNI Plugin](https://www.weave.works/docs/net/latest/kubernetes/)
|
* [Integrating Kubernetes and Mesos via the CNI Plugin](https://www.weave.works/docs/net/latest/kubernetes/)
|
||||||
* [Integrating Kubernetes via the Addon](https://www.weave.works/docs/net/latest/kubernetes/kube-addon/)
|
* [Integrating Kubernetes via the Addon](https://www.weave.works/docs/net/latest/kubernetes/kube-addon/)
|
||||||
* [Installation](https://www.weave.works/docs/net/latest/kubernetes/kube-addon/#install)
|
* [Installation](https://www.weave.works/docs/net/latest/kubernetes/kube-addon/#install)
|
||||||
* [Upgrading Kubernetes to version 1.6](https://www.weave.works/docs/net/latest/kubernetes/kube-addon/#kube-1.6-upgrade)
|
|
||||||
* [Upgrading the Daemon Sets](https://www.weave.works/docs/net/latest/kubernetes/kube-addon/#daemon-sets)
|
* [Upgrading the Daemon Sets](https://www.weave.works/docs/net/latest/kubernetes/kube-addon/#daemon-sets)
|
||||||
* [CPU and Memory Requirements](https://www.weave.works/docs/net/latest/kubernetes/kube-addon/#resources)
|
* [CPU and Memory Requirements](https://www.weave.works/docs/net/latest/kubernetes/kube-addon/#resources)
|
||||||
* [Pod Eviction](https://www.weave.works/docs/net/latest/kubernetes/kube-addon/#eviction)
|
* [Pod Eviction](https://www.weave.works/docs/net/latest/kubernetes/kube-addon/#eviction)
|
||||||
|
|||||||
@@ -48,10 +48,8 @@ To disable this check, run the following before launching Weave Net:
|
|||||||
|
|
||||||
### Guides for Specific Platforms
|
### Guides for Specific Platforms
|
||||||
|
|
||||||
CoreOS users see [here](/guides/networking-docker-containers-with-weave-on-coreos/) for an example of installing Weave using cloud-config.
|
Amazon ECS users see [here](https://www.weave.works/docs/scope/latest/ami/)
|
||||||
|
for the latest Weave AMIs.
|
||||||
Amazon ECS users see [here](https://github.com/weaveworks/integrations/blob/master/aws/ecs/README.md)
|
|
||||||
for the latest Weave AMIs and [here](https://www.weave.works/docs/tutorials/old-guides/ecs/) to get started with Weave Net on ECS.
|
|
||||||
|
|
||||||
If you're on Amazon EC2, the standard installation instructions at the
|
If you're on Amazon EC2, the standard installation instructions at the
|
||||||
top of this page, provide the simplest setup and the most flexibility.
|
top of this page, provide the simplest setup and the most flexibility.
|
||||||
|
|||||||
Loaded 100 of 3417 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user