Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
b8ce722a2c | ||
|
|
b8fa4af06a | ||
|
|
ea8f041a20 | ||
|
|
52e8cdb011 | ||
|
|
ac9f47783c | ||
|
|
27f9aa16d6 | ||
|
|
08dc649b9a | ||
|
|
bb1a373272 | ||
|
|
e192f68891 | ||
|
|
73654791c1 | ||
|
|
8908458942 | ||
|
|
26792283ab | ||
|
|
32a92a4695 | ||
|
|
3bea2ae419 | ||
|
|
58eaf19026 | ||
|
|
d513e68fab | ||
|
|
ca8bbb27a0 | ||
|
|
287dab27ae | ||
|
|
25c26519ed | ||
|
|
cf66c8d507 | ||
|
|
318bb767a5 | ||
|
|
ad6148f0f6 | ||
|
|
8042d3f22a | ||
|
|
c7259fc424 | ||
|
|
62c949df6e | ||
|
|
8c7dc342e0 | ||
|
|
d15439aeac | ||
|
|
c19c64a990 | ||
|
|
8e374d9416 | ||
|
|
49216ce365 | ||
|
|
812c58297f | ||
|
|
2a04df1049 | ||
|
|
2eda624653 | ||
|
|
2f8aa7a351 | ||
|
|
b6115f0147 | ||
|
|
5ff22a1a77 | ||
|
|
7fdf8fee13 | ||
|
|
45a9ea666e | ||
|
|
efd5d0e410 | ||
|
|
6dbe95a0b5 | ||
|
|
a0ec06a487 | ||
|
|
ebf4b4b48a | ||
|
|
4f99efaaf4 | ||
|
|
62c30d9130 | ||
|
|
21fb953d0f | ||
|
|
27c98070e8 | ||
|
|
039750a7fa | ||
|
|
9b0d1bd054 | ||
|
|
4c1c2a5661 | ||
|
|
791d9aecfc | ||
|
|
48aaac90fb | ||
|
|
bfaa3a3a92 | ||
|
|
6dce685105 | ||
|
|
8c4e33faef | ||
|
|
81582b04f9 | ||
|
|
dbcc72a6f6 | ||
|
|
9f207f161a | ||
|
|
1ee4039681 | ||
|
|
3203814940 | ||
|
|
acc8c33260 | ||
|
|
22ecf505e3 | ||
|
|
c2c14410d2 | ||
|
|
ba578c91a7 | ||
|
|
7b46391e30 | ||
|
|
ae551cafea | ||
|
|
672c770dbe | ||
|
|
847046a225 | ||
|
|
4da998ab45 | ||
|
|
b534ea60f5 | ||
|
|
549ccff5eb | ||
|
|
9cf6749b2e | ||
|
|
d2999cf1d7 | ||
|
|
391b55ceaf | ||
|
|
e05345d3bb | ||
|
|
67bd54e55d | ||
|
|
20919fd6db | ||
|
|
ada2af04ff | ||
|
|
fcb6f762a9 | ||
|
|
5645f31934 | ||
|
|
a7295e2d03 | ||
|
|
c3e6ac2cac | ||
|
|
f1f3c8c8fe | ||
|
|
81b2f2e15a | ||
|
|
f6fdaf6492 | ||
|
|
519c5d686a | ||
|
|
f7e62ccbcc | ||
|
|
19161c0c92 | ||
|
|
c528303841 | ||
|
|
a439288765 | ||
|
|
416410912d | ||
|
|
b3f26f00e2 | ||
|
|
90e6ecdd11 | ||
|
|
842354bc5a | ||
|
|
4d3875cec2 | ||
|
|
74dfe49046 | ||
|
|
d0f23ed9e0 | ||
|
|
8a0992315e | ||
|
|
18b5cb10dc | ||
|
|
8afc6f208d | ||
|
|
d35d931179 | ||
|
|
093174e757 | ||
|
|
ac78762910 | ||
|
|
fff656f7e4 | ||
|
|
83b36e4c76 | ||
|
|
b42895b5c0 | ||
|
|
42e4b9addc | ||
|
|
91ab0e4a76 | ||
|
|
51feec3785 | ||
|
|
20eee3818e | ||
|
|
94cd6226ea | ||
|
|
5534ecf1b1 | ||
|
|
d498df3933 | ||
|
|
75bf62ca80 | ||
|
|
ad42357dd6 | ||
|
|
2de899342a | ||
|
|
8df48dc69d | ||
|
|
0f2809d2e9 | ||
|
|
aa44153af7 | ||
|
|
ce9c5162bd | ||
|
|
5c0da56ad5 | ||
|
|
08858c2a10 | ||
|
|
0268aeb25f | ||
|
|
ea3aba8971 | ||
|
|
265bfcf9d7 | ||
|
|
7a9fae5892 | ||
|
|
f66b0cc0aa | ||
|
|
e7f30106c0 | ||
|
|
86d2800b59 | ||
|
|
e65006042d | ||
|
|
65efc89b78 | ||
|
|
ae38834507 | ||
|
|
4261777613 | ||
|
|
7bc089f5c5 | ||
|
|
c46e3e8cf7 | ||
|
|
180d554205 | ||
|
|
a450d2e741 | ||
|
|
d528196033 | ||
|
|
0da2cbf84e | ||
|
|
20f981bc42 | ||
|
|
9a5514ffe0 | ||
|
|
439b4ad190 | ||
|
|
9a9c06966b | ||
|
|
b3f9baac8f | ||
|
|
bb961489b0 | ||
|
|
d6ae1f8375 | ||
|
|
5a1361167e | ||
|
|
4d5172f67e | ||
|
|
6f01c31458 | ||
|
|
6cfb208f3f | ||
|
|
3074892f1b | ||
|
|
b05d977cf9 | ||
|
|
36d183d988 | ||
|
|
b154fd56fb | ||
|
|
3419f69246 | ||
|
|
8e23301028 | ||
|
|
72724ee389 | ||
|
|
ae59a053b1 | ||
|
|
e71919226b | ||
|
|
6c5dd75216 | ||
|
|
23095728f5 | ||
|
|
498dacb4d2 | ||
|
|
b373a069e9 | ||
|
|
1d67e2f955 | ||
|
|
9a6935bf40 | ||
|
|
921ffefd90 | ||
|
|
a4fccd43eb | ||
|
|
48e1a8295b | ||
|
|
890c8c1905 | ||
|
|
38d50ce6fb | ||
|
|
14768d6947 | ||
|
|
b96399b563 | ||
|
|
d8c008dc7d | ||
|
|
b2ae5f4f4b | ||
|
|
ba871d7855 | ||
|
|
a0c8fc2ddb | ||
|
|
2a12053122 | ||
|
|
1860f21efb | ||
|
|
541adf6bb1 | ||
|
|
a71e90ddb8 | ||
|
|
60f4b3cdb8 | ||
|
|
0c249fa126 | ||
|
|
db1b57900b | ||
|
|
12b4898b3d | ||
|
|
f5f9bab77c | ||
|
|
ba7aa674d1 | ||
|
|
f670be4315 | ||
|
|
a658861fde | ||
|
|
192e5a1f79 | ||
|
|
475b15e372 | ||
|
|
d2ed91ccc8 | ||
|
|
333f4a1d3c | ||
|
|
832fe2f0b0 | ||
|
|
ea76185873 | ||
|
|
51c3ea184a | ||
|
|
692bccc4fc | ||
|
|
51c2a0d732 | ||
|
|
0205adcedb | ||
|
|
a3fd36e2bb |
No files matched your search
+3
-2
@@ -46,8 +46,8 @@ prog/weavewait/weavewait_noop
|
||||
prog/weavewait/weavewait_nomcast
|
||||
prog/weaveutil/weaveutil
|
||||
prog/*/Dockerfile.weaveworks
|
||||
prog/kube-peers/kube-peers
|
||||
prog/weave-kube/kube-peers
|
||||
prog/kube-utils/kube-utils
|
||||
prog/weave-kube/kube-utils
|
||||
prog/weave-npc/weave-npc
|
||||
prog/plugin/plugin
|
||||
testing/cover/cover
|
||||
@@ -68,6 +68,7 @@ test/tls/tls
|
||||
test/tls/*.pem
|
||||
test/coverage
|
||||
test/coverage.*
|
||||
test/images/network-tester/webserver
|
||||
*qemu-*-static
|
||||
terraform.tfstate
|
||||
terraform.tfstate.backup
|
||||
|
||||
-177
@@ -1,180 +1,3 @@
|
||||
[submodule "tools"]
|
||||
path = 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
|
||||
+135
@@ -1,3 +1,138 @@
|
||||
## Release 2.4.0
|
||||
|
||||
This release introduces a support for Kubernetes Egress Network Policy (#2624, #3313)
|
||||
and adds a mechanism for preserving the client source IP address to enable
|
||||
`externalTrafficPolicy: Local` on Kubernetes (#2924, #3298).
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Increase the ipset list size which prevents weave-npc from crashing on older
|
||||
kernels when more than eight Kubernetes Namespaces are used (#3289, #3305).
|
||||
* Avoid a possible livelock when reclaiming IP address space in weave-kube (#3317).
|
||||
* Ensure `xtables.lock` is mounted as a file so that kube-proxy can take the lock
|
||||
if it has started after Weave Net (#3351, #3353).
|
||||
* Upgrade the CNI plugin symlinks only if the plugin has changed (#3337, #3345).
|
||||
|
||||
### Other improvements
|
||||
|
||||
* Manipulate the Kubernetes node status `NetworkUnavailable` so that Pods can be
|
||||
scheduled on nodes when the GCE cloud provider is in use (#3249, #3307, #3332, #3334).
|
||||
* Refrain from creating a subprocess for configuring a network interface in
|
||||
a container network namespace (#3291).
|
||||
* Protect against handling the CNI plugin request with the host namespace which
|
||||
prevents Weave Net from misconfiguring the host network (#3206, #3346).
|
||||
* Weave Net can be run on minikube VM (#3124).
|
||||
* Add `org.opencontainers.image.*` labels to Dockerfiles to improve association
|
||||
of the container images with git revisions (#3299).
|
||||
* Improve the error message when running `weave reset` on Kubernetes (#3319).
|
||||
|
||||
### Build and Testing
|
||||
|
||||
* Use `dep` instead of `git submodules` for managing external packages (#3268).
|
||||
* Fix usage of `manifest-tool` in Makefile (#3320).
|
||||
* Update Kubernetes to 1.11 for the integration tests (#3340).
|
||||
|
||||
### External Contributors
|
||||
|
||||
Thanks to the following contributors:
|
||||
|
||||
* @kitt1987
|
||||
* @louismunro
|
||||
* @Nodraak
|
||||
* @stevenjohnstone
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/73?closed=1)
|
||||
|
||||
## Release 2.3.0
|
||||
|
||||
### Security fixes
|
||||
|
||||
* By default, do not expose Weave "/status" and "/report" to all (0.0.0.0) when
|
||||
running on Kubernetes #3271
|
||||
|
||||
### Other improvements
|
||||
|
||||
* Increase the default connection limit for Weave peers (from 30 to 100) when
|
||||
running on Kubernetes, so that more peers could directly connect #3265
|
||||
|
||||
## Build and test
|
||||
|
||||
* Build Weave Net with Go 1.10.1 #3273
|
||||
* Run integration tests against Kubernetes 1.10.0 #3266
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/70?closed=1)
|
||||
|
||||
## Release 2.2.1
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fix a bug in weave-npc which would allow ingress traffic to Kubernetes Pods selected
|
||||
by a NetworkPolicy in which source and destination selectors were the same #3222,#3237
|
||||
* Fix a bug in weave-npc which would crash if a previously deleted Kubernetes Namespace
|
||||
has been created again #3247,#3250
|
||||
|
||||
### Other improvements
|
||||
|
||||
* Increase the default connection limit for Weave peers (from 30 to 100), so that
|
||||
more peers could directly connect #3234
|
||||
* When doing a rolling update of Weave Net on Kubernetes, allow each node five seconds
|
||||
to initialize before rolling next Weave Net Pod, so that issues at startup will halt
|
||||
the rollout and not spread across the whole cluster #3235
|
||||
* Install common CA certificates from Alpine Linux package instead of copying
|
||||
them manually #3236
|
||||
|
||||
### External contributors
|
||||
|
||||
Thanks to the following contributors:
|
||||
|
||||
* @alok87
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/71?closed=1)
|
||||
|
||||
## Release 2.2.0
|
||||
|
||||
This release improves the way Weave Net configures Linux network
|
||||
devices and network filter rules, so that it is more robust in the face
|
||||
of unexpected changes in its environment. #3204,#3224
|
||||
|
||||
As a consequence of these changes, the `weave attach` command will now
|
||||
fail unless the Weave Net daemon is up and running - previously it was
|
||||
possible to run independently as long as you managed all IP addresses
|
||||
yourself.
|
||||
|
||||
## Other improvements
|
||||
|
||||
* Update library miekg/dns for CVE-2017-15133 (details under embargo) #3223,#3227
|
||||
* Reduce the volume of logging from weave-npc #3183
|
||||
* Add ability to set log level for Docker "v2" plugin, and change
|
||||
default log level from DEBUG to INFO #3197
|
||||
* Downgrade log messages about Discovery and Expiration to DEBUG level #3202,#3203
|
||||
* Use command-line parameter for WeaveDNS address in Docker proxy #3196
|
||||
|
||||
## Bug fixes
|
||||
|
||||
* Ensure that rules to block traffic for NetworkPolicy are placed
|
||||
ahead of rules that Kubernetes has added to allow other traffic #3209,#3210
|
||||
|
||||
## Build and test
|
||||
|
||||
* Update CI tests to use Kubernetes 1.9.2 #3229
|
||||
* Remove "daily update" from test VMs that only run for a few minutes #3224
|
||||
|
||||
## External Contributors
|
||||
|
||||
Thanks to the following contributors:
|
||||
@vetal4444
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/67?closed=1)
|
||||
|
||||
|
||||
## Release 2.1.3
|
||||
|
||||
This release fixes a race-condition in the IP reclaim code for weave-kube
|
||||
where two nodes could end up fighting over the same space and break
|
||||
connectivity #3190, #3192
|
||||
|
||||
## Release 2.1.2
|
||||
|
||||
This release fixes a couple of bugs discovered since the release of Weave Net 2.1.0
|
||||
|
||||
@@ -24,6 +24,25 @@ taking the time to read this guide.
|
||||
* 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.
|
||||
|
||||
## 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
|
||||
See [WorkingOnWeave](https://github.com/weaveworks/weave/wiki/WorkingOnWeave)
|
||||
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
DOCKER_VERSION=17.06
|
||||
KUBERNETES_VERSION=1.7.8
|
||||
KUBERNETES_CNI_VERSION=0.5.1
|
||||
KUBERNETES_VERSION=1.11.0
|
||||
KUBERNETES_CNI_VERSION=0.6.0
|
||||
Generated
+782
@@ -0,0 +1,782 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/Azure/go-ansiterm"
|
||||
packages = [
|
||||
".",
|
||||
"winterm"
|
||||
]
|
||||
revision = "d6e3b3328b783f23731bc4d058875b0371ff8109"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/Microsoft/go-winio"
|
||||
packages = ["."]
|
||||
revision = "7da180ee92d8bd8bb8c37fc560e673e6557c392f"
|
||||
version = "v0.4.7"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/Nvveen/Gotty"
|
||||
packages = ["."]
|
||||
revision = "cd527374f1e5bff4938207604a14f2e38a9cf512"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/andybalholm/go-bit"
|
||||
packages = ["."]
|
||||
revision = "34813fe0e46a87398d88d0315e0f22615592b242"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/armon/go-socks5"
|
||||
packages = ["."]
|
||||
revision = "e75332964ef517daa070d7c38a9466a0d687e0a5"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/aws/aws-sdk-go"
|
||||
packages = [
|
||||
"aws",
|
||||
"aws/awserr",
|
||||
"aws/awsutil",
|
||||
"aws/client",
|
||||
"aws/client/metadata",
|
||||
"aws/corehandlers",
|
||||
"aws/credentials",
|
||||
"aws/credentials/ec2rolecreds",
|
||||
"aws/credentials/endpointcreds",
|
||||
"aws/credentials/stscreds",
|
||||
"aws/defaults",
|
||||
"aws/ec2metadata",
|
||||
"aws/endpoints",
|
||||
"aws/request",
|
||||
"aws/session",
|
||||
"aws/signer/v4",
|
||||
"internal/sdkio",
|
||||
"internal/sdkrand",
|
||||
"internal/shareddefaults",
|
||||
"private/protocol",
|
||||
"private/protocol/ec2query",
|
||||
"private/protocol/query",
|
||||
"private/protocol/query/queryutil",
|
||||
"private/protocol/rest",
|
||||
"private/protocol/xml/xmlutil",
|
||||
"service/ec2",
|
||||
"service/sts"
|
||||
]
|
||||
revision = "851c8ce700ee981b0ce28634721c2a925c6f3d0b"
|
||||
version = "v1.13.22"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/beorn7/perks"
|
||||
packages = ["quantile"]
|
||||
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/boltdb/bolt"
|
||||
packages = ["."]
|
||||
revision = "2f1ce7a837dcb8da3ec595b1dac9d0632f0f99e8"
|
||||
version = "v1.3.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/containerd/continuity"
|
||||
packages = ["pathdriver"]
|
||||
revision = "3e8f2ea4b190484acb976a5b378d373429639a1a"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/containernetworking/cni"
|
||||
packages = [
|
||||
"pkg/invoke",
|
||||
"pkg/ip",
|
||||
"pkg/ipam",
|
||||
"pkg/ns",
|
||||
"pkg/skel",
|
||||
"pkg/types",
|
||||
"pkg/types/020",
|
||||
"pkg/types/current",
|
||||
"pkg/utils/hwaddr",
|
||||
"pkg/version"
|
||||
]
|
||||
revision = "137b4975ecab6e1f0c24c1e3c228a50a3cfba75e"
|
||||
version = "v0.5.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/coreos/go-iptables"
|
||||
packages = ["iptables"]
|
||||
revision = "b5b1876b170881a8259f036445ee89c8669db386"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/distribution"
|
||||
packages = [
|
||||
"digestset",
|
||||
"reference"
|
||||
]
|
||||
revision = "83389a148052d74ac602f5f1d62f86ff2f3c4aa5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/docker"
|
||||
packages = [
|
||||
"api",
|
||||
"api/types",
|
||||
"api/types/blkiodev",
|
||||
"api/types/container",
|
||||
"api/types/events",
|
||||
"api/types/filters",
|
||||
"api/types/image",
|
||||
"api/types/mount",
|
||||
"api/types/network",
|
||||
"api/types/registry",
|
||||
"api/types/strslice",
|
||||
"api/types/swarm",
|
||||
"api/types/swarm/runtime",
|
||||
"api/types/time",
|
||||
"api/types/versions",
|
||||
"api/types/volume",
|
||||
"client",
|
||||
"opts",
|
||||
"pkg/archive",
|
||||
"pkg/fileutils",
|
||||
"pkg/homedir",
|
||||
"pkg/idtools",
|
||||
"pkg/ioutils",
|
||||
"pkg/jsonmessage",
|
||||
"pkg/longpath",
|
||||
"pkg/mount",
|
||||
"pkg/plugingetter",
|
||||
"pkg/plugins",
|
||||
"pkg/plugins/transport",
|
||||
"pkg/pools",
|
||||
"pkg/stdcopy",
|
||||
"pkg/system",
|
||||
"pkg/term",
|
||||
"pkg/term/windows"
|
||||
]
|
||||
revision = "38588656727d19f4a36d14c3620ecaff73160315"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/docker/go-connections"
|
||||
packages = [
|
||||
"nat",
|
||||
"sockets",
|
||||
"tlsconfig"
|
||||
]
|
||||
revision = "3ede32e2033de7505e6500d6c868c2b9ed9f169d"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/docker/go-units"
|
||||
packages = ["."]
|
||||
revision = "0dadbb0345b35ec7ef35e228dabb8de89a65bf52"
|
||||
version = "v0.3.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/docker/libnetwork"
|
||||
packages = [
|
||||
"discoverapi",
|
||||
"driverapi",
|
||||
"drivers/remote/api",
|
||||
"ipamapi",
|
||||
"ipams/remote/api",
|
||||
"netlabel",
|
||||
"types"
|
||||
]
|
||||
revision = "119badbd"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/docker/machine"
|
||||
packages = [
|
||||
"libmachine/auth",
|
||||
"libmachine/cert",
|
||||
"libmachine/log",
|
||||
"libmachine/mcnutils",
|
||||
"version"
|
||||
]
|
||||
revision = "89b833253d9412716a0291cbdccc94454c33d1b5"
|
||||
version = "v0.14.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fsouza/go-dockerclient"
|
||||
packages = ["."]
|
||||
revision = "ca33ff277b527ce11b793e62f9ba244129b01caf"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/ghodss/yaml"
|
||||
packages = ["."]
|
||||
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/go-ini/ini"
|
||||
packages = ["."]
|
||||
revision = "6333e38ac20b8949a8dd68baa3650f4dee8f39f0"
|
||||
version = "v1.33.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gogo/protobuf"
|
||||
packages = [
|
||||
"proto",
|
||||
"sortkeys"
|
||||
]
|
||||
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/golang/glog"
|
||||
packages = ["."]
|
||||
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = [
|
||||
"proto",
|
||||
"ptypes",
|
||||
"ptypes/any",
|
||||
"ptypes/duration",
|
||||
"ptypes/timestamp"
|
||||
]
|
||||
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/google/btree"
|
||||
packages = ["."]
|
||||
revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/google/gofuzz"
|
||||
packages = ["."]
|
||||
revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/google/gopacket"
|
||||
packages = [
|
||||
".",
|
||||
"layers",
|
||||
"pcap",
|
||||
"pcapgo"
|
||||
]
|
||||
revision = "11c65f1ca9081dfea43b4f9643f5c155583b73ba"
|
||||
version = "v1.1.14"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/googleapis/gnostic"
|
||||
packages = [
|
||||
"OpenAPIv2",
|
||||
"compiler",
|
||||
"extensions"
|
||||
]
|
||||
revision = "ee43cbb60db7bd22502942cccbc39059117352ab"
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/context"
|
||||
packages = ["."]
|
||||
revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a"
|
||||
version = "v1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/mux"
|
||||
packages = ["."]
|
||||
revision = "53c1911da2b537f792e7cafcb446b05ffe33b996"
|
||||
version = "v1.6.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/gregjones/httpcache"
|
||||
packages = [
|
||||
".",
|
||||
"diskcache"
|
||||
]
|
||||
revision = "9cad4c3443a7200dd6400aef47183728de563a38"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/go-cleanhttp"
|
||||
packages = ["."]
|
||||
revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/golang-lru"
|
||||
packages = [
|
||||
".",
|
||||
"simplelru"
|
||||
]
|
||||
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
packages = ["."]
|
||||
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/j-keck/arping"
|
||||
packages = ["."]
|
||||
revision = "2cf9dc699c5640a7e2c81403a44127bf28033600"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/jmespath/go-jmespath"
|
||||
packages = ["."]
|
||||
revision = "0b12d6b5"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/json-iterator/go"
|
||||
packages = ["."]
|
||||
revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4"
|
||||
version = "1.1.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/juju/ratelimit"
|
||||
packages = ["."]
|
||||
revision = "59fac5042749a5afb9af70e813da1dd5474f0167"
|
||||
version = "1.0.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
packages = ["."]
|
||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||
version = "v0.0.9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/matttproud/golang_protobuf_extensions"
|
||||
packages = ["pbutil"]
|
||||
revision = "3247c84500bff8d9fb6d579d800f20b3e091582c"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mgutz/ansi"
|
||||
packages = ["."]
|
||||
revision = "9520e82c474b0a04dd04f8a40959027271bab992"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/miekg/dns"
|
||||
packages = ["."]
|
||||
revision = "5364553f1ee9cddc7ac8b62dce148309c386695b"
|
||||
version = "v1.0.4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/modern-go/concurrent"
|
||||
packages = ["."]
|
||||
revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
|
||||
version = "1.0.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/modern-go/reflect2"
|
||||
packages = ["."]
|
||||
revision = "1df9eeb2bb81f327b96228865c5687bc2194af3f"
|
||||
version = "1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/opencontainers/go-digest"
|
||||
packages = ["."]
|
||||
revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf"
|
||||
version = "v1.0.0-rc1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/opencontainers/image-spec"
|
||||
packages = [
|
||||
"specs-go",
|
||||
"specs-go/v1"
|
||||
]
|
||||
revision = "d60099175f88c47cd379c4738d158884749ed235"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/opencontainers/runc"
|
||||
packages = [
|
||||
"libcontainer/system",
|
||||
"libcontainer/user"
|
||||
]
|
||||
revision = "baf6536d6259209c3edfa2b22237af82942d3dfa"
|
||||
version = "v0.1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pborman/uuid"
|
||||
packages = ["."]
|
||||
revision = "e790cca94e6cc75c7064b1332e63811d4aae1a53"
|
||||
version = "v1.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/petar/GoLLRB"
|
||||
packages = ["llrb"]
|
||||
revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/peterbourgon/diskv"
|
||||
packages = ["."]
|
||||
revision = "5f041e8faa004a95c88a202771f4cc3e991971e6"
|
||||
version = "v2.0.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/profile"
|
||||
packages = ["."]
|
||||
revision = "5b67d428864e92711fcbd2f8629456121a56d91f"
|
||||
version = "v1.2.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/prometheus/client_golang"
|
||||
packages = [
|
||||
"prometheus",
|
||||
"prometheus/promhttp"
|
||||
]
|
||||
revision = "f504d69affe11ec1ccb2e5948127f86878c9fd57"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/prometheus/client_model"
|
||||
packages = ["go"]
|
||||
revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/prometheus/common"
|
||||
packages = [
|
||||
"expfmt",
|
||||
"internal/bitbucket.org/ww/goautoneg",
|
||||
"model"
|
||||
]
|
||||
revision = "38c53a9f4bfcd932d1b00bfc65e256a7fba6b37a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/prometheus/procfs"
|
||||
packages = [
|
||||
".",
|
||||
"internal/util",
|
||||
"nfs",
|
||||
"xfs"
|
||||
]
|
||||
revision = "780932d4fbbe0e69b84c34c20f5c8d0981e109ea"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
revision = "d682213848ed68c0a260ca37d6dd5ace8423f5ba"
|
||||
version = "v1.0.4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = ["."]
|
||||
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
|
||||
version = "v0.0.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = [
|
||||
"assert",
|
||||
"require"
|
||||
]
|
||||
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||
version = "v1.2.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/vishvananda/netlink"
|
||||
packages = [
|
||||
".",
|
||||
"nl"
|
||||
]
|
||||
revision = "a2ad57a690f3caf3015351d2d6e1c0b95c349752"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/vishvananda/netns"
|
||||
packages = ["."]
|
||||
revision = "be1fbeda19366dea804f00efff2dd73a1642fdcc"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/weaveworks/common"
|
||||
packages = [
|
||||
"mflag",
|
||||
"mflagext",
|
||||
"signals"
|
||||
]
|
||||
revision = "ebab3a78900a09cebce8d0b37f2a69d474796bf4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/weaveworks/go-checkpoint"
|
||||
packages = ["."]
|
||||
revision = "ebbb8b0518ab326368631225cabc9435fe580dcc"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/weaveworks/go-odp"
|
||||
packages = ["odp"]
|
||||
revision = "eec09c2fbfb96be47bfe11165ce300ad9a60bef5"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/weaveworks/mesh"
|
||||
packages = ["."]
|
||||
revision = "38e8100dfbc7b28ae8ffde5a2bbeceec57ffa52a"
|
||||
version = "v0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"curve25519",
|
||||
"ed25519",
|
||||
"ed25519/internal/edwards25519",
|
||||
"hkdf",
|
||||
"nacl/box",
|
||||
"nacl/secretbox",
|
||||
"pbkdf2",
|
||||
"poly1305",
|
||||
"salsa20/salsa",
|
||||
"scrypt",
|
||||
"ssh/terminal"
|
||||
]
|
||||
revision = "88942b9c40a4c9d203b82b3731787b672d6e809b"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"bpf",
|
||||
"context",
|
||||
"context/ctxhttp",
|
||||
"http2",
|
||||
"http2/hpack",
|
||||
"idna",
|
||||
"internal/iana",
|
||||
"internal/socket",
|
||||
"ipv4",
|
||||
"ipv6",
|
||||
"lex/httplex",
|
||||
"proxy"
|
||||
]
|
||||
revision = "6078986fec03a1dcc236c34816c71b0e05018fda"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"unix",
|
||||
"windows"
|
||||
]
|
||||
revision = "13d03a9a82fba647c21a0ef8fba44a795d0f0835"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"collate",
|
||||
"collate/build",
|
||||
"internal/colltab",
|
||||
"internal/gen",
|
||||
"internal/tag",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
"language",
|
||||
"secure/bidirule",
|
||||
"transform",
|
||||
"unicode/bidi",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
"unicode/rangetable"
|
||||
]
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/tools"
|
||||
packages = ["cover"]
|
||||
revision = "77106db15f689a60e7d4e085d967ac557b918fb2"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/inf.v0"
|
||||
packages = ["."]
|
||||
revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
|
||||
version = "v0.9.1"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
revision = "86f5ed62f8a0ee96bd888d2efdfd6d4fb100a4eb"
|
||||
version = "v2.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "k8s.io/api"
|
||||
packages = [
|
||||
"admissionregistration/v1alpha1",
|
||||
"admissionregistration/v1beta1",
|
||||
"apps/v1",
|
||||
"apps/v1beta1",
|
||||
"apps/v1beta2",
|
||||
"authentication/v1",
|
||||
"authentication/v1beta1",
|
||||
"authorization/v1",
|
||||
"authorization/v1beta1",
|
||||
"autoscaling/v1",
|
||||
"autoscaling/v2beta1",
|
||||
"batch/v1",
|
||||
"batch/v1beta1",
|
||||
"batch/v2alpha1",
|
||||
"certificates/v1beta1",
|
||||
"core/v1",
|
||||
"events/v1beta1",
|
||||
"extensions/v1beta1",
|
||||
"networking/v1",
|
||||
"policy/v1beta1",
|
||||
"rbac/v1",
|
||||
"rbac/v1alpha1",
|
||||
"rbac/v1beta1",
|
||||
"scheduling/v1alpha1",
|
||||
"settings/v1alpha1",
|
||||
"storage/v1",
|
||||
"storage/v1alpha1",
|
||||
"storage/v1beta1"
|
||||
]
|
||||
revision = "055879e38033d1526b0fe169089a055c33fa3990"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "k8s.io/apimachinery"
|
||||
packages = [
|
||||
"pkg/api/errors",
|
||||
"pkg/api/meta",
|
||||
"pkg/api/resource",
|
||||
"pkg/apis/meta/internalversion",
|
||||
"pkg/apis/meta/v1",
|
||||
"pkg/apis/meta/v1/unstructured",
|
||||
"pkg/apis/meta/v1beta1",
|
||||
"pkg/conversion",
|
||||
"pkg/conversion/queryparams",
|
||||
"pkg/fields",
|
||||
"pkg/labels",
|
||||
"pkg/runtime",
|
||||
"pkg/runtime/schema",
|
||||
"pkg/runtime/serializer",
|
||||
"pkg/runtime/serializer/json",
|
||||
"pkg/runtime/serializer/protobuf",
|
||||
"pkg/runtime/serializer/recognizer",
|
||||
"pkg/runtime/serializer/streaming",
|
||||
"pkg/runtime/serializer/versioning",
|
||||
"pkg/selection",
|
||||
"pkg/types",
|
||||
"pkg/util/cache",
|
||||
"pkg/util/clock",
|
||||
"pkg/util/diff",
|
||||
"pkg/util/errors",
|
||||
"pkg/util/framer",
|
||||
"pkg/util/intstr",
|
||||
"pkg/util/json",
|
||||
"pkg/util/net",
|
||||
"pkg/util/runtime",
|
||||
"pkg/util/sets",
|
||||
"pkg/util/uuid",
|
||||
"pkg/util/validation",
|
||||
"pkg/util/validation/field",
|
||||
"pkg/util/wait",
|
||||
"pkg/util/yaml",
|
||||
"pkg/version",
|
||||
"pkg/watch",
|
||||
"third_party/forked/golang/reflect"
|
||||
]
|
||||
revision = "210bcde8e5a7715fe776460ca1df1a3f2738725e"
|
||||
|
||||
[[projects]]
|
||||
name = "k8s.io/client-go"
|
||||
packages = [
|
||||
"discovery",
|
||||
"kubernetes",
|
||||
"kubernetes/scheme",
|
||||
"kubernetes/typed/admissionregistration/v1alpha1",
|
||||
"kubernetes/typed/admissionregistration/v1beta1",
|
||||
"kubernetes/typed/apps/v1",
|
||||
"kubernetes/typed/apps/v1beta1",
|
||||
"kubernetes/typed/apps/v1beta2",
|
||||
"kubernetes/typed/authentication/v1",
|
||||
"kubernetes/typed/authentication/v1beta1",
|
||||
"kubernetes/typed/authorization/v1",
|
||||
"kubernetes/typed/authorization/v1beta1",
|
||||
"kubernetes/typed/autoscaling/v1",
|
||||
"kubernetes/typed/autoscaling/v2beta1",
|
||||
"kubernetes/typed/batch/v1",
|
||||
"kubernetes/typed/batch/v1beta1",
|
||||
"kubernetes/typed/batch/v2alpha1",
|
||||
"kubernetes/typed/certificates/v1beta1",
|
||||
"kubernetes/typed/core/v1",
|
||||
"kubernetes/typed/events/v1beta1",
|
||||
"kubernetes/typed/extensions/v1beta1",
|
||||
"kubernetes/typed/networking/v1",
|
||||
"kubernetes/typed/policy/v1beta1",
|
||||
"kubernetes/typed/rbac/v1",
|
||||
"kubernetes/typed/rbac/v1alpha1",
|
||||
"kubernetes/typed/rbac/v1beta1",
|
||||
"kubernetes/typed/scheduling/v1alpha1",
|
||||
"kubernetes/typed/settings/v1alpha1",
|
||||
"kubernetes/typed/storage/v1",
|
||||
"kubernetes/typed/storage/v1alpha1",
|
||||
"kubernetes/typed/storage/v1beta1",
|
||||
"pkg/version",
|
||||
"rest",
|
||||
"rest/watch",
|
||||
"tools/cache",
|
||||
"tools/clientcmd/api",
|
||||
"tools/metrics",
|
||||
"tools/pager",
|
||||
"tools/reference",
|
||||
"transport",
|
||||
"util/buffer",
|
||||
"util/cert",
|
||||
"util/flowcontrol",
|
||||
"util/integer"
|
||||
]
|
||||
revision = "78700dec6369ba22221b72770783300f143df150"
|
||||
version = "v6.0.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "4797cbcb2a3e44dbdfcb6f7a7aba5a86079a150d9cf4d782eb7f69be973c48db"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# Pin to version before sctp was added, because latest libnetwork is
|
||||
# incompatible with latest sctp
|
||||
[[constraint]]
|
||||
name = "github.com/docker/libnetwork"
|
||||
revision = "119badbd"
|
||||
|
||||
# Encourage dep not to use the docker-1.9 version
|
||||
[[constraint]]
|
||||
name = "github.com/fsouza/go-dockerclient"
|
||||
version = "1"
|
||||
|
||||
# Use recent version: last tagged version is years old
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/prometheus/client_golang"
|
||||
|
||||
# For compatibility with fsouza/go-dockerclient
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/docker"
|
||||
|
||||
# Most recent release tag is incompatible with docker/docker; see https://github.com/docker/distribution/issues/2503
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/distribution"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
@@ -101,6 +101,7 @@ endif
|
||||
# The name of the user that this Makefile should produce image artifacts for. Can/should be overridden
|
||||
DOCKERHUB_USER?=weaveworks
|
||||
# The default version that's chosen when pushing the images. Can/should be overridden
|
||||
GIT_REVISION=$(shell git rev-parse HEAD)
|
||||
WEAVE_VERSION?=git-$(shell git rev-parse --short=12 HEAD)
|
||||
# Docker Store does not allow the "latest" tag.
|
||||
NET_PLUGIN_LATEST=latest_release
|
||||
@@ -108,20 +109,20 @@ NET_PLUGIN_LATEST=latest_release
|
||||
# Paths to all relevant binaries that should be compiled
|
||||
WEAVER_EXE=prog/weaver/weaver
|
||||
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
|
||||
WEAVEWAIT_EXE=prog/weavewait/weavewait
|
||||
WEAVEWAIT_NOOP_EXE=prog/weavewait/weavewait_noop
|
||||
WEAVEWAIT_NOMCAST_EXE=prog/weavewait/weavewait_nomcast
|
||||
WEAVEUTIL_EXE=prog/weaveutil/weaveutil
|
||||
RUNNER_EXE=tools/runner/runner
|
||||
MANIFEST_TOOL_DIR=vendor/github.com/estesp/manifest-tool
|
||||
MANIFEST_TOOL_EXE=$(MANIFEST_TOOL_DIR)/manifest-tool
|
||||
# manifest-tool needs registry credentials; we assume here that the active user has logged in
|
||||
MANIFEST_TOOL_CMD=docker run --rm -v $(HOME)/.docker:/.docker weshigbee/manifest-tool --docker-cfg=/.docker
|
||||
TEST_TLS_EXE=test/tls/tls
|
||||
NETWORKTESTER_EXE=test/images/network-tester/webserver
|
||||
|
||||
# All binaries together in a list
|
||||
EXES=$(WEAVER_EXE) $(SIGPROXY_EXE) $(KUBEPEERS_EXE) $(WEAVENPC_EXE) $(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
|
||||
BUILD_UPTODATE=.build.uptodate
|
||||
@@ -179,11 +180,10 @@ $(WEAVER_EXE): api/*.go plugin/*.go plugin/*/*
|
||||
$(WEAVER_EXE): proxy/*.go
|
||||
$(WEAVEUTIL_EXE): prog/weaveutil/*.go net/*.go plugin/net/*.go plugin/ipam/*.go db/*.go
|
||||
$(SIGPROXY_EXE): prog/sigproxy/*.go
|
||||
$(KUBEPEERS_EXE): prog/kube-peers/*.go
|
||||
$(KUBEUTILS_EXE): prog/kube-utils/*.go
|
||||
$(WEAVENPC_EXE): prog/weave-npc/*.go npc/*.go npc/*/*.go
|
||||
$(TEST_TLS_EXE): test/tls/*.go
|
||||
$(RUNNER_EXE): tools/runner/*.go
|
||||
$(MANIFEST_TOOL_EXE): $(MANIFEST_TOOL_DIR)/*.go
|
||||
$(WEAVEWAIT_NOOP_EXE): prog/weavewait/*.go
|
||||
$(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go
|
||||
$(WEAVEWAIT_NOMCAST_EXE): prog/weavewait/*.go net/*.go
|
||||
@@ -196,9 +196,6 @@ ifeq ($(BUILD_IN_CONTAINER),true)
|
||||
# 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
|
||||
exes $(EXES) tests lint: $(BUILD_UPTODATE)
|
||||
git submodule update --init
|
||||
# Containernetworking has another copy of vishvananda/netlink which leads to duplicate definitions
|
||||
-@rm -r vendor/github.com/containernetworking/cni/vendor
|
||||
@mkdir -p $(shell pwd)/.pkg
|
||||
$(SUDO) docker run $(RM) $(RUN_FLAGS) \
|
||||
-v $(shell pwd):/go/src/github.com/weaveworks/weave \
|
||||
@@ -220,7 +217,7 @@ else
|
||||
endif
|
||||
$(NETGO_CHECK)
|
||||
|
||||
$(WEAVEUTIL_EXE) $(KUBEPEERS_EXE) $(WEAVENPC_EXE) $(NETWORKTESTER_EXE):
|
||||
$(WEAVEUTIL_EXE) $(KUBEUTILS_EXE) $(WEAVENPC_EXE) $(NETWORKTESTER_EXE):
|
||||
go build $(BUILD_FLAGS) -o $@ ./$(@D)
|
||||
$(NETGO_CHECK)
|
||||
|
||||
@@ -234,7 +231,7 @@ $(WEAVEWAIT_NOMCAST_EXE):
|
||||
|
||||
# These programs need a separate rule as they fail the netgo check in
|
||||
# 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)
|
||||
|
||||
tests:
|
||||
@@ -274,7 +271,7 @@ endif
|
||||
$(WEAVER_UPTODATE): prog/weaver/Dockerfile.$(DOCKERHUB_USER) $(WEAVER_EXE) weave $(WEAVEUTIL_EXE)
|
||||
cp $(WEAVEUTIL_EXE) prog/weaver/weaveutil
|
||||
cp weave prog/weaver/weave
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build -f prog/weaver/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVER_IMAGE) prog/weaver
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker build --build-arg=revision=$(GIT_REVISION) -f prog/weaver/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVER_IMAGE) prog/weaver
|
||||
touch $@
|
||||
|
||||
$(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) prog/weaveexec/symlink $(SIGPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVEWAIT_NOOP_EXE) $(WEAVEWAIT_NOMCAST_EXE) $(WEAVER_UPTODATE)
|
||||
@@ -282,7 +279,7 @@ $(WEAVEEXEC_UPTODATE): prog/weaveexec/Dockerfile.$(DOCKERHUB_USER) prog/weaveexe
|
||||
cp $(WEAVEWAIT_EXE) prog/weaveexec/weavewait
|
||||
cp $(WEAVEWAIT_NOOP_EXE) prog/weaveexec/weavewait_noop
|
||||
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 $@
|
||||
|
||||
# Builds Docker plugin.
|
||||
@@ -302,17 +299,17 @@ $(PLUGIN_UPTODATE): prog/net-plugin/launch.sh prog/net-plugin/config.json $(WEAV
|
||||
$(SUDO) docker plugin create $(PLUGIN_IMAGE):$(NET_PLUGIN_LATEST) prog/net-plugin
|
||||
touch $@
|
||||
|
||||
$(WEAVEKUBE_UPTODATE): prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) prog/weave-kube/launch.sh $(KUBEPEERS_EXE) $(WEAVER_UPTODATE)
|
||||
cp $(KUBEPEERS_EXE) prog/weave-kube/
|
||||
$(SUDO) docker build -f prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) -t $(WEAVEKUBE_IMAGE) prog/weave-kube
|
||||
$(WEAVEKUBE_UPTODATE): prog/weave-kube/Dockerfile.$(DOCKERHUB_USER) prog/weave-kube/launch.sh $(KUBEUTILS_EXE) $(WEAVER_UPTODATE)
|
||||
cp $(KUBEUTILS_EXE) 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 $@
|
||||
|
||||
$(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 $@
|
||||
|
||||
$(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 $@
|
||||
|
||||
$(NETWORKTESTER_UPTODATE): test/images/network-tester/Dockerfile $(NETWORKTESTER_EXE)
|
||||
@@ -322,7 +319,7 @@ $(NETWORKTESTER_UPTODATE): test/images/network-tester/Dockerfile $(NETWORKTESTER
|
||||
$(WEAVE_EXPORT): $(IMAGES_UPTODATE) $(WEAVEDB_UPTODATE)
|
||||
$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker save $(addsuffix :latest,$(IMAGES)) | gzip > $@
|
||||
|
||||
tools/.git $(MANIFEST_TOOL_DIR)/.git:
|
||||
tools/.git:
|
||||
git submodule update --init
|
||||
|
||||
# CODE FOR PUBLISHING THE IMAGES
|
||||
@@ -378,13 +375,13 @@ endif
|
||||
|
||||
# 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
|
||||
$(PUSH_ML): push_ml_%: $(MANIFEST_TOOL_EXE)
|
||||
$(PUSH_ML): push_ml_%:
|
||||
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
|
||||
ifneq ($(UPDATE_LATEST),false)
|
||||
# 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
|
||||
|
||||
clean-work-dir:
|
||||
@@ -398,7 +395,7 @@ clean-bin:
|
||||
|
||||
clean: clean-bin clean-work-dir
|
||||
-$(SUDO) DOCKER_HOST=$(DOCKER_HOST) docker rmi $(BUILD_IMAGE)
|
||||
rm -rf test/tls/*.pem test/coverage.* test/coverage $(BUILD_UPTODATE) $(MANIFEST_TOOL_EXE)
|
||||
rm -rf test/tls/*.pem test/coverage.* test/coverage $(BUILD_UPTODATE)
|
||||
|
||||
build:
|
||||
$(SUDO) go clean -i net
|
||||
|
||||
+17
-2
@@ -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-k8s-1.6.yaml
|
||||
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.7.yaml
|
||||
sed -i.bak -e "s/:latest/:$VERSION/" -e "/imagePullPolicy: Always/d" ./prog/weave-kube/weave-daemonset-k8s-1.8.yaml
|
||||
make SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER
|
||||
|
||||
if make tests; then
|
||||
@@ -181,6 +182,13 @@ draft() {
|
||||
--name "weave-daemonset-k8s-1.7.yaml" \
|
||||
--file "./prog/weave-kube/weave-daemonset-k8s-1.7.yaml"
|
||||
|
||||
github-release upload \
|
||||
--user $GITHUB_USER \
|
||||
--repo weave \
|
||||
--tag $LATEST_TAG \
|
||||
--name "weave-daemonset-k8s-1.8.yaml" \
|
||||
--file "./prog/weave-kube/weave-daemonset-k8s-1.8.yaml"
|
||||
|
||||
echo "** Draft $TYPE $RELEASE_NAME $VERSION created at"
|
||||
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
||||
}
|
||||
@@ -196,7 +204,7 @@ publish() {
|
||||
|
||||
if [ "$RELEASE_TYPE" = 'PRERELEASE' ] ; then
|
||||
echo "== Building and pushing plugin"
|
||||
make UPDATE_LATEST=$IS_MAINLINE SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
|
||||
make UPDATE_LATEST=false SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
|
||||
echo "** Plugin built and pushed"
|
||||
|
||||
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
|
||||
@@ -232,7 +240,7 @@ publish() {
|
||||
echo '** Sanity checks OK for publishing tag' $LATEST_TAG as $DOCKERHUB_USER/weave:$VERSION
|
||||
|
||||
echo "== Building and pushing plugin"
|
||||
make UPDATE_LATEST=$IS_MAINLINE SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
|
||||
make UPDATE_LATEST=true SUDO=$SUDO WEAVE_VERSION=$VERSION DOCKERHUB_USER=$DOCKERHUB_USER plugin_publish
|
||||
echo "** Plugin built and pushed"
|
||||
|
||||
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
|
||||
@@ -289,6 +297,13 @@ publish() {
|
||||
--name "weave-daemonset-k8s-1.7.yaml" \
|
||||
--file "./prog/weave-kube/weave-daemonset-k8s-1.7.yaml"
|
||||
|
||||
github-release upload \
|
||||
--user $GITHUB_USER \
|
||||
--repo weave \
|
||||
--tag latest_release \
|
||||
--name "weave-daemonset-k8s-1.8.yaml" \
|
||||
--file "./prog/weave-kube/weave-daemonset-k8s-1.8.yaml"
|
||||
|
||||
echo "** Release $RELEASE_NAME $VERSION published at"
|
||||
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/$LATEST_TAG"
|
||||
echo -e "\thttps://github.com/$GITHUB_USER/weave/releases/latest_release"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.8.1
|
||||
FROM golang:1.10.1-stretch
|
||||
|
||||
# Support Raspberry Pi 2 and newer
|
||||
ENV GOARM 7
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ test:
|
||||
parallel: true
|
||||
- "$SRCDIR/bin/circle-test-smoke":
|
||||
parallel: true
|
||||
timeout: 300
|
||||
timeout: 360
|
||||
post:
|
||||
- "$SRCDIR/bin/circle-test-teardown":
|
||||
parallel: true
|
||||
|
||||
@@ -231,7 +231,7 @@ func (c *Client) EnsureNetwork(networkName, driver, subnet string, options map[s
|
||||
Name: networkName,
|
||||
CheckDuplicate: true,
|
||||
Driver: driver,
|
||||
IPAM: docker.IPAMOptions{
|
||||
IPAM: &docker.IPAMOptions{
|
||||
Driver: driver,
|
||||
Config: []docker.IPAMConfig{{Subnet: subnet}},
|
||||
},
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type textFormatter struct {
|
||||
|
||||
@@ -75,6 +75,14 @@ func DeleteDatapath(dpname string) error {
|
||||
}
|
||||
|
||||
func AddDatapathInterface(dpname string, ifname string) error {
|
||||
return addDatapathInterface(dpname, ifname, false)
|
||||
}
|
||||
|
||||
func AddDatapathInterfaceIfNotExist(dpname string, ifname string) error {
|
||||
return addDatapathInterface(dpname, ifname, true)
|
||||
}
|
||||
|
||||
func addDatapathInterface(dpname, ifname string, ignoreIfExist bool) error {
|
||||
dpif, err := odp.NewDpif()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -87,5 +95,9 @@ func AddDatapathInterface(dpname string, ifname string) error {
|
||||
}
|
||||
|
||||
_, err = dp.CreateVport(odp.NewNetdevVportSpec(ifname))
|
||||
if ignoreIfExist && err == odp.NetlinkError(syscall.EEXIST) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
# Source IP addr of packet sent to k8s Service
|
||||
|
||||
## Setup
|
||||
|
||||
* Weave Net: v2.2.1.
|
||||
* Kubernetes: v1.10.0.
|
||||
* kube-proxy: iptables mode (default kubeadm options).
|
||||
* Hosts: `h1` and `h2`.
|
||||
|
||||
## Source IP
|
||||
|
||||
\# | Src | Target | Dst | Src IP | `-j WEAVE-NPC`
|
||||
---|-------------|-------------------|--------|------------------|--------------
|
||||
1 | Pod_h1 | ip(Pod_h2) | Pod_h2 | ip(Pod_h1) | OK
|
||||
2 | h1 | ClusterIP | Pod_h1 | ip(weave_h1) | **NOK**
|
||||
3 | h1 | ClusterIP | Pod_h2 | ip(weave_h1) | OK
|
||||
4 | Pod_h1 | ClusterIP | Pod_h1 | ip(weave_h1) | **NOK**
|
||||
5 | Pod_h1 | ClusterIP | Pod_h2 | ip(weave_h1) | OK
|
||||
6 | h1 | ip(h1):NodePort | Pod_h1 | ip(weave_h1) | **NOK**
|
||||
7 | h1 | ip(h1):NodePort | Pod_h2 | ip(weave_h1) | OK
|
||||
8 | h1 | ip(h2):NodePort | Pod_h1 | ip(weave_h2) | OK
|
||||
9 | h1 | ip(h2):NodePort | Pod_h2 | ip(weave_h2) | ??? Can't reproduce
|
||||
10 | Pod_h1 | ip(h1):NodePort | Pod_h1 | ip(weave_h1) | **NOK**
|
||||
11 | Pod_h1 | ip(h1):NodePort | Pod_h2 | ip(weave_h1) | OK
|
||||
12 | Pod_h1 | ip(h2):NodePort | Pod_h1 | ip(weave_h2) | OK
|
||||
13 | Pod_h1 | ip(h2):NodePort | Pod_h2 | ip(weave_h2) | OK
|
||||
|
||||
Remarks:
|
||||
|
||||
* **Src IP** is of a packet which is captured on the weave bridge.
|
||||
* **-j WEAVE-NPC** - whether a packet enters the `filter/WEAVE-NPC` iptables chain (OK = NetworkPolicy is enforced as required).
|
||||
* **Pod_h1** - a Pod running on the `h1` host.
|
||||
* **ip(weave_h1)** - IP addr of the weave bridge on the `h1` host.
|
||||
+40
-2
@@ -127,14 +127,52 @@ Finally, for **Mainline** releases only:
|
||||
|
||||
* Images tagged `latest` are updated on DockerHub
|
||||
|
||||
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
|
||||
|
||||
* If not on master, merge branch into master and push to GitHub.
|
||||
* Close the [milestone](https://github.com/weaveworks/weave/milestones) in GitHub and create the next milestone
|
||||
* Update the `#weavenetwork` topic heading on freenode (requires 'chanops' permission)
|
||||
* For a mainline release vX.Y.0, create a release branch X.Y from the
|
||||
tag and push to GitHub - this will result in X.Y.0 site docs being
|
||||
published to https://www.weave.works
|
||||
tag, push to GitHub and set [WEAVE_NET_REV](https://github.com/weaveworks/website-next/blob/master/Makefile)
|
||||
to X.Y via a PR - this will result in X.Y.0 site docs being published to https://www.weave.works
|
||||
* Add the new version of `weave-net` to the checkpoint system at
|
||||
https://checkpoint-api.weave.works/admin
|
||||
* File a PR to update the version of the daemonset at
|
||||
|
||||
@@ -74,6 +74,7 @@ type Allocator struct {
|
||||
isKnownPeer func(mesh.PeerName) bool
|
||||
quorum func() uint
|
||||
now func() time.Time
|
||||
tracker tracker.LocalRangeTracker
|
||||
}
|
||||
|
||||
// PreClaims are IP addresses discovered before we could initialize IPAM
|
||||
@@ -130,6 +131,7 @@ func NewAllocator(config Config) *Allocator {
|
||||
quorum: config.Quorum,
|
||||
dead: make(map[string]time.Time),
|
||||
now: time.Now,
|
||||
tracker: config.Tracker,
|
||||
}
|
||||
|
||||
alloc.pendingClaims = make([]operation, len(config.PreClaims))
|
||||
|
||||
+2
-2
@@ -88,7 +88,7 @@ func (alloc *Allocator) handleHTTPClaim(dockerCli *docker.Client, w http.Respons
|
||||
}
|
||||
|
||||
// HandleHTTP wires up ipams HTTP endpoints to the provided mux.
|
||||
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) {
|
||||
fmt.Fprintf(w, "%s", defaultSubnet)
|
||||
})
|
||||
@@ -199,6 +199,6 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CID
|
||||
})
|
||||
|
||||
router.Methods("GET").Path("/ipinfo/tracker").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, tracker)
|
||||
fmt.Fprintf(w, alloc.tracker.String())
|
||||
})
|
||||
}
|
||||
+2
-2
@@ -43,7 +43,7 @@ func listenHTTP(alloc *Allocator, subnet address.CIDR) int {
|
||||
router.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
|
||||
io.WriteString(w, fmt.Sprintln(alloc))
|
||||
})
|
||||
alloc.HandleHTTP(router, subnet, "", nil)
|
||||
alloc.HandleHTTP(router, subnet, nil)
|
||||
|
||||
httpListener, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
@@ -132,7 +132,7 @@ func TestBadHttp(t *testing.T) {
|
||||
// Verb that's not handled
|
||||
resp, err := doHTTP("HEAD", fmt.Sprintf("http://localhost:%d/ip/%s/%s", port, containerID, testAddr1))
|
||||
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
|
||||
resp, err = doHTTP("POST", fmt.Sprintf("http://localhost:%d/xip/%s/", port, containerID))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -117,7 +117,7 @@ func CheckAllExpectedMessagesSent(allocs ...*Allocator) {
|
||||
m := alloc.gossip.(*mockGossipComms)
|
||||
m.RLock()
|
||||
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()
|
||||
}
|
||||
|
||||
+5
-42
@@ -84,7 +84,7 @@ func NewAWSVPCTracker(bridgeName string) (*AWSVPCTracker, error) {
|
||||
func (t *AWSVPCTracker) HandleUpdate(prevRanges, currRanges []address.Range, local bool) error {
|
||||
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
|
||||
// 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
|
||||
}
|
||||
|
||||
func (t *AWSVPCTracker) String() string {
|
||||
return "awsvpc"
|
||||
}
|
||||
|
||||
func (t *AWSVPCTracker) createVPCRoute(cidr string) (*ec2.CreateRouteOutput, error) {
|
||||
route := &ec2.CreateRouteInput{
|
||||
RouteTableId: &t.routeTableID,
|
||||
@@ -232,47 +236,6 @@ func (t *AWSVPCTracker) infof(fmt string, args ...interface{}) {
|
||||
|
||||
// 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) {
|
||||
ip, ipnet, err := net.ParseCIDR(cidr)
|
||||
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) {
|
||||
a := []address.CIDR{r0to127, r1dot0to255}
|
||||
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{r2dot0to255}, newB)
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func TestMerge(t *testing.T) {
|
||||
r128to255.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
|
||||
@@ -17,4 +17,7 @@ type LocalRangeTracker interface {
|
||||
// The local parameter indicates whether the ranges belong to the peer
|
||||
// by which the method is called.
|
||||
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
|
||||
// makes containers react more quickly to a change in the MAC 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
+169
-47
@@ -6,12 +6,18 @@ import (
|
||||
"net"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
"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
|
||||
@@ -47,11 +53,12 @@ datapath of old kernel versions (https://github.com/weaveworks/weave/issues/1577
|
||||
*/
|
||||
|
||||
const (
|
||||
WeaveBridgeName = "weave"
|
||||
DatapathName = "datapath"
|
||||
DatapathIfName = "vethwe-datapath"
|
||||
BridgeIfName = "vethwe-bridge"
|
||||
PcapIfName = "vethwe-pcap"
|
||||
WeaveBridgeName = "weave"
|
||||
DatapathName = "datapath"
|
||||
DatapathIfName = "vethwe-datapath"
|
||||
BridgeIfName = "vethwe-bridge"
|
||||
PcapIfName = "vethwe-pcap"
|
||||
NoMasqLocalIpset = ipset.Name("weaver-no-masq-local")
|
||||
)
|
||||
|
||||
type Bridge interface {
|
||||
@@ -213,6 +220,7 @@ type BridgeConfig struct {
|
||||
MTU int
|
||||
Mac string
|
||||
Port int
|
||||
NoMasqLocal bool
|
||||
}
|
||||
|
||||
func (config *BridgeConfig) configuredBridgeType() Bridge {
|
||||
@@ -226,13 +234,20 @@ func (config *BridgeConfig) configuredBridgeType() Bridge {
|
||||
}
|
||||
}
|
||||
|
||||
func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger) (Bridge, error) {
|
||||
bridgeType, err := ExistingBridgeType(config.WeaveBridgeName, config.DatapathName)
|
||||
if bridgeType != nil || err != nil {
|
||||
return bridgeType, err
|
||||
func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger, ips ipset.Interface) (Bridge, error) {
|
||||
existingBridgeType, err := ExistingBridgeType(config.WeaveBridgeName, config.DatapathName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bridgeType := config.configuredBridgeType()
|
||||
|
||||
if existingBridgeType != nil && bridgeType.String() != existingBridgeType.String() {
|
||||
return nil,
|
||||
fmt.Errorf("Existing bridge type %q is different than requested %q. Please do 'weave reset' and try again",
|
||||
existingBridgeType, bridgeType)
|
||||
}
|
||||
|
||||
bridgeType = config.configuredBridgeType()
|
||||
for {
|
||||
if err := bridgeType.init(config); err != nil {
|
||||
if errors.Cause(err) == errBridgeNotSupported {
|
||||
@@ -245,7 +260,7 @@ func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger) (Br
|
||||
break
|
||||
}
|
||||
|
||||
if err := configureIPTables(config); err != nil {
|
||||
if err := configureIPTables(config, ips); err != nil {
|
||||
return bridgeType, errors.Wrap(err, "configuring iptables")
|
||||
}
|
||||
|
||||
@@ -268,10 +283,16 @@ func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger) (Br
|
||||
return bridgeType, err
|
||||
}
|
||||
|
||||
if err := ConfigureARPCache(procPath, config.WeaveBridgeName); err != nil {
|
||||
if err := configureARPCache(procPath, config.WeaveBridgeName); err != nil {
|
||||
return bridgeType, errors.Wrapf(err, "configuring ARP cache on bridge %q", config.WeaveBridgeName)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -288,7 +309,7 @@ func (b bridgeImpl) initPrep(config *BridgeConfig) error {
|
||||
config.MTU = 65535
|
||||
}
|
||||
b.bridge = &netlink.Bridge{LinkAttrs: linkAttrs}
|
||||
if err = netlink.LinkAdd(b.bridge); err != nil {
|
||||
if err := LinkAddIfNotExist(b.bridge); err != nil {
|
||||
return errors.Wrapf(err, "creating bridge %q", config.WeaveBridgeName)
|
||||
}
|
||||
if err := netlink.LinkSetHardwareAddr(b.bridge, mac); err != nil {
|
||||
@@ -320,7 +341,7 @@ func (b bridgeImpl) init(config *BridgeConfig) error {
|
||||
if err := b.initPrep(config); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := CreateAndAttachVeth(BridgeIfName, PcapIfName, config.WeaveBridgeName, config.MTU, true, func(veth netlink.Link) error {
|
||||
if _, err := CreateAndAttachVeth(BridgeIfName, PcapIfName, config.WeaveBridgeName, config.MTU, true, false, func(veth netlink.Link) error {
|
||||
return netlink.LinkSetUp(veth)
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "creating pcap veth pair")
|
||||
@@ -370,11 +391,11 @@ func (bf bridgedFastdpImpl) init(config *BridgeConfig) error {
|
||||
if err := bf.bridgeImpl.initPrep(config); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := CreateAndAttachVeth(BridgeIfName, DatapathIfName, config.WeaveBridgeName, config.MTU, true, func(veth netlink.Link) error {
|
||||
if _, err := CreateAndAttachVeth(BridgeIfName, DatapathIfName, config.WeaveBridgeName, config.MTU, true, false, func(veth netlink.Link) error {
|
||||
if err := netlink.LinkSetUp(veth); err != nil {
|
||||
return errors.Wrapf(err, "setting link up on %q", veth.Attrs().Name)
|
||||
}
|
||||
if err := odp.AddDatapathInterface(bf.datapathName, veth.Attrs().Name); err != nil {
|
||||
if err := odp.AddDatapathInterfaceIfNotExist(bf.datapathName, veth.Attrs().Name); err != nil {
|
||||
return errors.Wrapf(err, "adding interface %q to datapath %q", veth.Attrs().Name, bf.datapathName)
|
||||
}
|
||||
return nil
|
||||
@@ -394,20 +415,21 @@ func (bf bridgedFastdpImpl) attach(veth *netlink.Veth) error {
|
||||
}
|
||||
|
||||
func (f fastdpImpl) attach(veth *netlink.Veth) error {
|
||||
return odp.AddDatapathInterface(f.datapathName, veth.Attrs().Name)
|
||||
return odp.AddDatapathInterfaceIfNotExist(f.datapathName, veth.Attrs().Name)
|
||||
}
|
||||
|
||||
func configureIPTables(config *BridgeConfig) error {
|
||||
func configureIPTables(config *BridgeConfig, ips ipset.Interface) error {
|
||||
ipt, err := iptables.New()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "creating iptables object")
|
||||
}
|
||||
|
||||
// The order among weave filter/FORWARD rules is important!
|
||||
fwdRules := make([][]string, 0)
|
||||
|
||||
if config.DockerBridgeName != "" {
|
||||
if config.WeaveBridgeName != config.DockerBridgeName {
|
||||
err := ipt.Insert("filter", "FORWARD", 1, "-i", config.DockerBridgeName, "-o", config.WeaveBridgeName, "-j", "DROP")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fwdRules = append(fwdRules, []string{"-i", config.DockerBridgeName, "-o", config.WeaveBridgeName, "-j", "DROP"})
|
||||
}
|
||||
|
||||
dockerBridgeIP, err := FindBridgeIP(config.DockerBridgeName, nil)
|
||||
@@ -436,48 +458,122 @@ func configureIPTables(config *BridgeConfig) error {
|
||||
}
|
||||
|
||||
if config.NPC {
|
||||
// Steer traffic via the NPC
|
||||
_ = ipt.NewChain("filter", "WEAVE-NPC") // ignore error because it might already exist
|
||||
// If WEAVE-NPC chain doesn't exist then next line will fail
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "WEAVE-NPC"); err != nil {
|
||||
// Steer traffic via the NPC.
|
||||
|
||||
if err = ensureChains(ipt, "filter", npc.MainChain, npc.EgressChain); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-m", "state", "--state", "NEW", "-j", "NFLOG", "--nflog-group", "86"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "DROP"); err != nil {
|
||||
|
||||
// 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,
|
||||
[][]string{
|
||||
// Might include ingress traffic which is fine as long as we do not
|
||||
// ACCEPT in WEAVE-NPC-EGRESS chain
|
||||
{"-i", config.WeaveBridgeName,
|
||||
"-m", "comment", "--comment", "NOTE: this must go before '-j KUBE-FORWARD'",
|
||||
"-j", npc.EgressChain},
|
||||
// The following rules are for ingress NPC processing
|
||||
{"-o", config.WeaveBridgeName,
|
||||
"-m", "comment", "--comment", "NOTE: this must go before '-j KUBE-FORWARD'",
|
||||
"-j", npc.MainChain},
|
||||
{"-o", config.WeaveBridgeName, "-m", "state", "--state", "NEW", "-j", "NFLOG", "--nflog-group", "86"},
|
||||
{"-o", config.WeaveBridgeName, "-j", "DROP"},
|
||||
}...)
|
||||
} else {
|
||||
// Work around the situation where there are no rules allowing traffic
|
||||
// across our bridge. E.g. ufw
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-i", config.WeaveBridgeName, "-o", config.WeaveBridgeName, "-j", "ACCEPT"); err != nil {
|
||||
return err
|
||||
}
|
||||
fwdRules = append(fwdRules, []string{"-i", config.WeaveBridgeName, "-o", config.WeaveBridgeName, "-j", "ACCEPT"})
|
||||
}
|
||||
|
||||
if !config.NPC {
|
||||
// Create a chain for allowing ingress traffic when the bridge is exposed
|
||||
_ = ipt.NewChain("filter", "WEAVE-EXPOSE")
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "WEAVE-EXPOSE"); err != nil {
|
||||
// Create/Flush a chain for allowing ingress traffic when the bridge is exposed
|
||||
if err := ipt.ClearChain("filter", "WEAVE-EXPOSE"); err != nil {
|
||||
return errors.Wrap(err, "failed to clear/create filter/WEAVE-EXPOSE chain")
|
||||
}
|
||||
|
||||
fwdRules = append(fwdRules, []string{"-o", config.WeaveBridgeName, "-j", "WEAVE-EXPOSE"})
|
||||
}
|
||||
|
||||
// Forward from weave to the rest of the world
|
||||
fwdRules = append(fwdRules, []string{"-i", config.WeaveBridgeName, "!", "-o", config.WeaveBridgeName, "-j", "ACCEPT"})
|
||||
// and allow replies back
|
||||
fwdRules = append(fwdRules, []string{"-o", config.WeaveBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"})
|
||||
|
||||
if err := ensureRulesAtTop("filter", "FORWARD", fwdRules, ipt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a chain for masquerading
|
||||
if err := ipt.ClearChain("nat", "WEAVE"); err != nil {
|
||||
return errors.Wrap(err, "failed to clear/create nat/WEAVE chain")
|
||||
}
|
||||
if err := ipt.AppendUnique("nat", "POSTROUTING", "-j", "WEAVE"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// For the cases where the weave bridge is the default gateway for
|
||||
// containers (e.g. Kubernetes): create the ipset to store CIDRs allocated
|
||||
// by IPAM for local containers. In the case of Kubernetes, external traffic
|
||||
// sent to these CIDRs avoids SNAT'ing so that NodePort with
|
||||
// `"externalTrafficPolicy":"Local"` would receive packets with correct
|
||||
// src IP addr.
|
||||
if config.NoMasqLocal {
|
||||
ips := ipset.New(common.LogLogger(), 0)
|
||||
_ = ips.Destroy(NoMasqLocalIpset)
|
||||
if err := ips.Create(NoMasqLocalIpset, ipset.HashNet); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ipt.Insert("nat", "WEAVE", 1,
|
||||
"-m", "set", "--match-set", string(NoMasqLocalIpset), "dst",
|
||||
"-m", "comment", "--comment", "Prevent SNAT to locally running containers",
|
||||
"-j", "RETURN"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Forward from weave to the rest of the world
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-i", config.WeaveBridgeName, "!", "-o", config.WeaveBridgeName, "-j", "ACCEPT"); err != nil {
|
||||
return err
|
||||
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
|
||||
}
|
||||
// and allow replies back
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *NoMasqLocalTracker) String() string {
|
||||
return "no-masq-local"
|
||||
}
|
||||
|
||||
func (t *NoMasqLocalTracker) HandleUpdate(prevRanges, currRanges []address.Range, local bool) error {
|
||||
if !local {
|
||||
return nil
|
||||
}
|
||||
|
||||
// create a chain for masquerading
|
||||
if err = ipt.ClearChain("nat", "WEAVE"); err != nil {
|
||||
return errors.Wrap(err, "clearing WEAVE chain")
|
||||
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
|
||||
}
|
||||
}
|
||||
return ipt.AppendUnique("nat", "POSTROUTING", "-j", "WEAVE")
|
||||
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 {
|
||||
@@ -487,3 +583,29 @@ func linkSetUpByName(linkName string) error {
|
||||
}
|
||||
return netlink.LinkSetUp(link)
|
||||
}
|
||||
|
||||
func reexpose(config *BridgeConfig, log *logrus.Logger) error {
|
||||
// Get existing IP addrs of the weave bridge.
|
||||
// If the bridge hasn't been exposed, then this functions does nothing.
|
||||
//
|
||||
// Ideally, we should consult IPAM for IP addrs allocated to "weave:expose",
|
||||
// but we don't want to introduce dependency on IPAM, as weave should be able
|
||||
// to run w/o IPAM.
|
||||
link, err := netlink.LinkByName(config.WeaveBridgeName)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "cannot find bridge %q", config.WeaveBridgeName)
|
||||
}
|
||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "cannot list IPv4 addrs of bridge %q", config.WeaveBridgeName)
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
log.Infof("Re-exposing %s on bridge %q", addr.IPNet, config.WeaveBridgeName)
|
||||
if err := Expose(config.WeaveBridgeName, addr.IPNet, config.AWSVPC, config.NPC); err != nil {
|
||||
return errors.Wrapf(err, "unable to re-expose %s on bridge: %q", addr.IPNet, config.WeaveBridgeName)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+1
-1
@@ -12,9 +12,9 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package ipset
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
@@ -16,6 +19,7 @@ type Type string
|
||||
const (
|
||||
ListSet = Type("list:set")
|
||||
HashIP = Type("hash:ip")
|
||||
HashNet = Type("hash:net")
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
@@ -40,6 +44,7 @@ type entryKey struct {
|
||||
type ipset struct {
|
||||
*log.Logger
|
||||
enableComments bool
|
||||
maxListSize int
|
||||
// List of users per ipset entry. User is either a namespace or a pod.
|
||||
// There might be multiple users for the same ipset & entry pair because
|
||||
// events from k8s API server might be out of order causing duplicate IPs:
|
||||
@@ -47,15 +52,23 @@ type ipset struct {
|
||||
users map[entryKey]map[types.UID]struct{}
|
||||
}
|
||||
|
||||
func New(logger *log.Logger) Interface {
|
||||
func New(logger *log.Logger, maxListSize int) Interface {
|
||||
ips := &ipset{
|
||||
Logger: logger,
|
||||
enableComments: true,
|
||||
maxListSize: maxListSize,
|
||||
users: make(map[entryKey]map[types.UID]struct{}),
|
||||
}
|
||||
|
||||
// Check for comment support
|
||||
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
|
||||
_ = ips.Destroy(testIpsetName)
|
||||
// Test for comment support
|
||||
@@ -71,6 +84,9 @@ func New(logger *log.Logger) Interface {
|
||||
|
||||
func (i *ipset) Create(ipsetName Name, ipsetType Type) error {
|
||||
args := []string{"create", string(ipsetName), string(ipsetType)}
|
||||
if ipsetType == ListSet && i.maxListSize > 0 {
|
||||
args = append(args, "size", fmt.Sprintf("%d", i.maxListSize))
|
||||
}
|
||||
if i.enableComments {
|
||||
args = append(args, "comment")
|
||||
}
|
||||
+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
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
@@ -17,7 +15,7 @@ type Dev struct {
|
||||
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)
|
||||
if err != nil {
|
||||
return Dev{}, err
|
||||
@@ -90,16 +88,30 @@ func GetNetDevsByVethPeerIds(processID int, peerIDs []int) ([]Dev, error) {
|
||||
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
|
||||
peersStr := make([]string, len(peerIDs))
|
||||
for i, id := range peerIDs {
|
||||
peersStr[i] = strconv.Itoa(id)
|
||||
}
|
||||
nds, err := WithNetNSByPid(processID, "list-netdevs", peersStr...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list-netdevs failed: %s", err)
|
||||
}
|
||||
err = json.Unmarshal(nds, &netdevs)
|
||||
err = WithNetNS(netnsContainer, func() error {
|
||||
links, err := netlink.LinkList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, link := range links {
|
||||
if _, found := indexes[link.Attrs().Index]; found {
|
||||
netdev, err := linkToNetDev(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
netdevs = append(netdevs, netdev)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return netdevs, err
|
||||
}
|
||||
@@ -112,5 +124,5 @@ func GetBridgeNetDev(bridgeName string) (Dev, error) {
|
||||
return Dev{}, err
|
||||
}
|
||||
|
||||
return LinkToNetDev(link)
|
||||
return linkToNetDev(link)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func LinkAddIfNotExist(link netlink.Link) error {
|
||||
err := netlink.LinkAdd(link)
|
||||
if err != nil && err == syscall.EEXIST {
|
||||
return nil
|
||||
}
|
||||
return errors.Wrapf(err, "creating link %q", link)
|
||||
}
|
||||
+10
-36
@@ -1,10 +1,10 @@
|
||||
// +build go1.10
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
@@ -14,19 +14,7 @@ import (
|
||||
|
||||
var ErrLinkNotFound = errors.New("Link not found")
|
||||
|
||||
// NB: The following function is unsafe, because:
|
||||
// - 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 {
|
||||
func WithNetNS(ns netns.NsHandle, work func() error) error {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
@@ -45,8 +33,8 @@ func WithNetNSUnsafe(ns netns.NsHandle, work func() error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func WithNetNSLinkUnsafe(ns netns.NsHandle, ifName string, work func(link netlink.Link) error) error {
|
||||
return WithNetNSUnsafe(ns, func() error {
|
||||
func WithNetNSLink(ns netns.NsHandle, ifName string, work func(link netlink.Link) error) error {
|
||||
return WithNetNS(ns, func() error {
|
||||
link, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
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"
|
||||
|
||||
// A safe version of WithNetNS* which creates a process executing
|
||||
// "nsenter --net=<ns-path> weaveutil <cmd> [args]".
|
||||
func WithNetNS(nsPath string, cmd string, args ...string) ([]byte, error) {
|
||||
var stdout, stderr bytes.Buffer
|
||||
|
||||
args = append([]string{"--net=" + nsPath, WeaveUtilCmd, cmd}, args...)
|
||||
c := exec.Command("nsenter", args...)
|
||||
c.Stdout = &stdout
|
||||
c.Stderr = &stderr
|
||||
if err := c.Run(); err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", string(stderr.Bytes()), err)
|
||||
func WithNetNSByPath(path string, work func() error) error {
|
||||
ns, err := netns.GetFromPath(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return stdout.Bytes(), nil
|
||||
}
|
||||
|
||||
func WithNetNSByPid(pid int, cmd string, args ...string) ([]byte, error) {
|
||||
return WithNetNS(NSPathByPid(pid), cmd, args...)
|
||||
return WithNetNS(ns, work)
|
||||
}
|
||||
|
||||
func NSPathByPid(pid int) string {
|
||||
|
||||
+44
-20
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
// create and attach a veth to the Weave bridge
|
||||
func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, keepTXOn bool, init func(peer netlink.Link) error) (*netlink.Veth, error) {
|
||||
func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, keepTXOn bool, errIfLinkExist bool, init func(peer netlink.Link) error) (*netlink.Veth, error) {
|
||||
bridge, err := netlink.LinkByName(bridgeName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`bridge "%s" not present; did you launch weave?`, bridgeName)
|
||||
@@ -28,7 +28,12 @@ func CreateAndAttachVeth(name, peerName, bridgeName string, mtu int, keepTXOn bo
|
||||
MTU: mtu},
|
||||
PeerName: peerName,
|
||||
}
|
||||
if err := netlink.LinkAdd(veth); err != nil {
|
||||
|
||||
linkAdd := LinkAddIfNotExist
|
||||
if errIfLinkExist {
|
||||
linkAdd = netlink.LinkAdd
|
||||
}
|
||||
if err := linkAdd(veth); err != nil {
|
||||
return nil, fmt.Errorf(`could not create veth pair %s-%s: %s`, name, peerName, err)
|
||||
}
|
||||
|
||||
@@ -99,7 +104,10 @@ const (
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -116,11 +124,13 @@ func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMult
|
||||
id = id[:maxIDLen] // trim passed ID if too long
|
||||
}
|
||||
name, peerName := vethPrefix+"pl"+id, vethPrefix+"pg"+id
|
||||
veth, err := CreateAndAttachVeth(name, peerName, bridgeName, mtu, keepTXOn, func(veth netlink.Link) error {
|
||||
veth, err := CreateAndAttachVeth(name, peerName, bridgeName, mtu, keepTXOn, true, func(veth netlink.Link) error {
|
||||
if err := netlink.LinkSetNsFd(veth, int(ns)); err != nil {
|
||||
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 nil
|
||||
@@ -134,19 +144,16 @@ func AttachContainer(netNSPath, id, ifName, bridgeName string, mtu int, withMult
|
||||
|
||||
}
|
||||
|
||||
args := []string{ifName, fmt.Sprintf("%t", withMulticastRoute)}
|
||||
for _, cidr := range cidrs {
|
||||
args = append(args, cidr.String())
|
||||
}
|
||||
if _, err := WithNetNS(netNSPath, "setup-iface-addrs", args...); err != nil {
|
||||
if err := WithNetNSLink(ns, ifName, func(veth netlink.Link) error {
|
||||
return setupIfaceAddrs(veth, withMulticastRoute, cidrs)
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error setting up interface addresses: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupIfaceAddrs is the implementation of the 'setup-iface-addrs' call above,
|
||||
// running in another process in the container's netns
|
||||
func SetupIfaceAddrs(veth netlink.Link, withMulticastRoute bool, cidrs []*net.IPNet) error {
|
||||
// setupIfaceAddrs expects to be called in the container's netns
|
||||
func setupIfaceAddrs(veth netlink.Link, withMulticastRoute bool, cidrs []*net.IPNet) error {
|
||||
newAddresses, err := AddAddresses(veth, cidrs)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -198,9 +205,8 @@ func SetupIfaceAddrs(veth netlink.Link, withMulticastRoute bool, cidrs []*net.IP
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupIface is the implementation of the 'setup-iface' call above,
|
||||
// running in another process in the container's netns
|
||||
func SetupIface(ifaceName, newIfName string) error {
|
||||
// setupIface expects to be called in the container's netns
|
||||
func setupIface(ifaceName, newIfName string) error {
|
||||
ipt, err := iptables.New()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -214,14 +220,32 @@ func SetupIface(ifaceName, newIfName string) error {
|
||||
return err
|
||||
}
|
||||
// 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 ipt.Append("filter", "INPUT", "-i", newIfName, "-d", "224.0.0.0/4", "-j", "DROP")
|
||||
}
|
||||
|
||||
// NB: This function can be used only by a process that terminates immediately
|
||||
// after calling the function as it changes netns via WithNetNSLinkUnsafe.
|
||||
// configureARP is a helper for the Docker plugin which doesn't set the addresses itself
|
||||
func ConfigureARP(prefix, rootPath string) error {
|
||||
links, err := netlink.LinkList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, link := range links {
|
||||
ifName := link.Attrs().Name
|
||||
if strings.HasPrefix(ifName, prefix) {
|
||||
configureARPCache(rootPath+"/proc", ifName)
|
||||
if addrs, err := netlink.AddrList(link, netlink.FAMILY_V4); err == nil {
|
||||
for _, addr := range addrs {
|
||||
arping.GratuitousArpOverIfaceByName(addr.IPNet.IP, ifName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DetachContainer(netNSPath, id, ifName string, cidrs []*net.IPNet) error {
|
||||
ns, err := netns.GetFromPath(netNSPath)
|
||||
if err != nil {
|
||||
@@ -234,7 +258,7 @@ func DetachContainer(netNSPath, id, ifName string, cidrs []*net.IPNet) error {
|
||||
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)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get IP address for %q: %v", veth.Attrs().Name, err)
|
||||
|
||||
+124
-134
@@ -4,173 +4,163 @@ import (
|
||||
"fmt"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
extnapi "k8s.io/api/extensions/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
"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) (
|
||||
rules map[string]*ruleSpec,
|
||||
nsSelectors, podSelectors map[string]*selectorSpec,
|
||||
ipBlocks map[string]*ipBlockSpec,
|
||||
err error) {
|
||||
|
||||
nsSelectors = make(map[string]*selectorSpec)
|
||||
podSelectors = make(map[string]*selectorSpec)
|
||||
ipBlocks = make(map[string]*ipBlockSpec)
|
||||
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
|
||||
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 {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
podSelectors[dstSelector.key] = dstSelector
|
||||
|
||||
// If ingress is empty then this NetworkPolicy does not allow any traffic
|
||||
if policy.Spec.Ingress == nil || len(policy.Spec.Ingress) == 0 {
|
||||
return
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
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
|
||||
// To prevent targetSelector being overwritten by a subsequent selector with
|
||||
// the same key, addIfNotExist MUST be used when adding to podSelectors.
|
||||
// Otherwise, policyTypes of the selector might be lost resulting in
|
||||
// an invalid content in any "default-allow" ipset.
|
||||
addIfNotExist(targetSelector, podSelectors)
|
||||
|
||||
if allSources {
|
||||
if allPorts {
|
||||
rule := newRuleSpec(nil, nil, dstSelector, nil)
|
||||
rules[rule.key] = rule
|
||||
} else {
|
||||
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
||||
rule := newRuleSpec(&proto, nil, dstSelector, &port)
|
||||
rules[rule.key] = rule
|
||||
})
|
||||
}
|
||||
} else {
|
||||
for _, peer := range ingressRule.From {
|
||||
var srcSelector *selectorSpec
|
||||
|
||||
// NetworkPolicyPeer describes a peer to allow traffic from.
|
||||
// Exactly one of its fields must be specified.
|
||||
if peer.PodSelector != nil {
|
||||
srcSelector, err = newSelectorSpec(peer.PodSelector, false, ns.name, ipset.HashIP)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
podSelectors[srcSelector.key] = srcSelector
|
||||
|
||||
} else if peer.NamespaceSelector != nil {
|
||||
srcSelector, err = newSelectorSpec(peer.NamespaceSelector, false, "", ipset.ListSet)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
nsSelectors[srcSelector.key] = srcSelector
|
||||
}
|
||||
// If ingress is empty then this NetworkPolicy does not allow any ingress traffic
|
||||
if policy.Spec.Ingress != nil && len(policy.Spec.Ingress) != 0 {
|
||||
for _, ingressRule := range policy.Spec.Ingress {
|
||||
// If Ports is empty or missing, this rule matches all ports
|
||||
allPorts := ingressRule.Ports == nil || len(ingressRule.Ports) == 0
|
||||
// If From is empty or missing, this rule matches all sources
|
||||
allSources := ingressRule.From == nil || len(ingressRule.From) == 0
|
||||
|
||||
if allSources {
|
||||
if allPorts {
|
||||
rule := newRuleSpec(nil, srcSelector, dstSelector, nil)
|
||||
rule := newRuleSpec(policyTypeIngress, nil, nil, targetSelector, nil)
|
||||
rules[rule.key] = rule
|
||||
} else {
|
||||
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
||||
rule := newRuleSpec(&proto, srcSelector, dstSelector, &port)
|
||||
rule := newRuleSpec(policyTypeIngress, &proto, nil, targetSelector, &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, nil, ns.name, ipset.HashIP)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
addIfNotExist(srcSelector, podSelectors)
|
||||
|
||||
} else if peer.NamespaceSelector != nil {
|
||||
srcSelector, err = newSelectorSpec(peer.NamespaceSelector, nil, "", ipset.ListSet)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
nsSelectors[srcSelector.key] = srcSelector
|
||||
}
|
||||
|
||||
if allPorts {
|
||||
rule := newRuleSpec(policyTypeIngress, nil, srcSelector, targetSelector, nil)
|
||||
rules[rule.key] = rule
|
||||
} else {
|
||||
withNormalisedProtoAndPort(ingressRule.Ports, func(proto, port string) {
|
||||
rule := newRuleSpec(policyTypeIngress, &proto, srcSelector, targetSelector, &port)
|
||||
rules[rule.key] = rule
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nsSelectors, podSelectors, nil
|
||||
// If egress is empty then this NetworkPolicy does not allow any egress traffic
|
||||
if policy.Spec.Egress != nil && len(policy.Spec.Egress) != 0 {
|
||||
for _, egressRule := range policy.Spec.Egress {
|
||||
// If Ports is empty or missing, this rule matches all ports
|
||||
allPorts := egressRule.Ports == nil || len(egressRule.Ports) == 0
|
||||
// If To is empty or missing, this rule matches all destinations
|
||||
allDestinations := egressRule.To == nil || len(egressRule.To) == 0
|
||||
|
||||
if allDestinations {
|
||||
if allPorts {
|
||||
rule := newRuleSpec(policyTypeEgress, nil, targetSelector, nil, nil)
|
||||
rules[rule.key] = rule
|
||||
} else {
|
||||
withNormalisedProtoAndPort(egressRule.Ports, func(proto, port string) {
|
||||
rule := newRuleSpec(policyTypeEgress, &proto, targetSelector, nil, &port)
|
||||
rules[rule.key] = rule
|
||||
})
|
||||
}
|
||||
} else {
|
||||
for _, peer := range egressRule.To {
|
||||
var dstSelector *selectorSpec
|
||||
var dstRuleHost ruleHost
|
||||
|
||||
// NetworkPolicyPeer describes a peer to allow traffic to.
|
||||
// Exactly one of its fields must be specified.
|
||||
if peer.PodSelector != nil {
|
||||
dstSelector, err = newSelectorSpec(peer.PodSelector, nil, ns.name, ipset.HashIP)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
addIfNotExist(dstSelector, podSelectors)
|
||||
dstRuleHost = dstSelector
|
||||
|
||||
} else if peer.NamespaceSelector != nil {
|
||||
dstSelector, err = newSelectorSpec(peer.NamespaceSelector, nil, "", ipset.ListSet)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
nsSelectors[dstSelector.key] = dstSelector
|
||||
dstRuleHost = dstSelector
|
||||
} else if peer.IPBlock != nil {
|
||||
ipBlock := newIPBlockSpec(peer.IPBlock, ns.name)
|
||||
ipBlocks[ipBlock.key] = ipBlock
|
||||
dstRuleHost = ipBlock
|
||||
}
|
||||
|
||||
if allPorts {
|
||||
rule := newRuleSpec(policyTypeEgress, nil, targetSelector, dstRuleHost, nil)
|
||||
rules[rule.key] = rule
|
||||
} else {
|
||||
withNormalisedProtoAndPort(egressRule.Ports, func(proto, port string) {
|
||||
rule := newRuleSpec(policyTypeEgress, &proto, targetSelector, dstRuleHost, &port)
|
||||
rules[rule.key] = rule
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nsSelectors, podSelectors, ipBlocks, nil
|
||||
}
|
||||
|
||||
func withNormalisedProtoAndPortLegacy(npps []extnapi.NetworkPolicyPort, f func(proto, port string)) {
|
||||
for _, npp := range npps {
|
||||
f(proto(npp.Protocol), port(npp.Port))
|
||||
func addIfNotExist(s *selectorSpec, ss map[string]*selectorSpec) {
|
||||
if _, ok := ss[s.key]; !ok {
|
||||
ss[s.key] = s
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,12 @@ const (
|
||||
DefaultChain = "WEAVE-NPC-DEFAULT"
|
||||
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-"
|
||||
|
||||
LocalIpset = IpsetNamePrefix + "local-pods"
|
||||
|
||||
+4
-7
@@ -9,7 +9,7 @@ import (
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
"github.com/weaveworks/weave/net/ipset"
|
||||
"github.com/weaveworks/weave/npc/iptables"
|
||||
)
|
||||
|
||||
@@ -37,19 +37,16 @@ type controller struct {
|
||||
|
||||
nss map[string]*ns // ns name -> ns struct
|
||||
nsSelectors *selectorSet // selector string -> nsSelector
|
||||
|
||||
legacy bool // use legacy network policy semantics (k8s pre-1.7)
|
||||
}
|
||||
|
||||
func New(nodeName string, legacy bool, ipt iptables.Interface, ips ipset.Interface) NetworkPolicyController {
|
||||
func New(nodeName string, ipt iptables.Interface, ips ipset.Interface) NetworkPolicyController {
|
||||
c := &controller{
|
||||
nodeName: nodeName,
|
||||
legacy: legacy,
|
||||
ipt: ipt,
|
||||
ips: ips,
|
||||
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)
|
||||
|
||||
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 {
|
||||
ns, found := npc.nss[name]
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
+248
-32
@@ -1,12 +1,14 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
"github.com/weaveworks/weave/net/ipset"
|
||||
coreapi "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/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 {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (ipt *mockIPTables) Insert(table, chain string, pos int, rulespec ...string) error {
|
||||
return nil
|
||||
return errors.New("Not Implemented")
|
||||
}
|
||||
|
||||
func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
||||
@@ -144,6 +174,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
||||
Namespace: "destination",
|
||||
},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||
Ingress: []networkingv1.NetworkPolicyIngressRule{
|
||||
{
|
||||
From: []networkingv1.NetworkPolicyPeer{
|
||||
@@ -167,7 +198,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
||||
|
||||
// Namespaces first
|
||||
m := newMockIPSet()
|
||||
controller := New("foo", false, &mockIPTables{}, &m)
|
||||
controller := New("foo", newMockIPTables(), &m)
|
||||
|
||||
const (
|
||||
selectorIPSetName = "weave-I239Zp%sCvoVt*D6u=A!2]YEk"
|
||||
@@ -183,7 +214,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
||||
|
||||
// NetworkPolicy first
|
||||
m = newMockIPSet()
|
||||
controller = New("foo", false, &mockIPTables{}, &m)
|
||||
controller = New("foo", newMockIPTables(), &m)
|
||||
|
||||
controller.AddNetworkPolicy(networkPolicy)
|
||||
|
||||
@@ -193,17 +224,18 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
||||
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) {
|
||||
const (
|
||||
defaultAllowIPSetName = "weave-E.1.0W^NGSp]0_t5WwH/]gX@L"
|
||||
fooPodIP = "10.32.0.10"
|
||||
barPodIP = "10.32.0.11"
|
||||
barPodNewIP = "10.32.0.12"
|
||||
ingressDefaultAllowIPSetName = "weave-;rGqyMIl1HN^cfDki~Z$3]6!N"
|
||||
egressDefaultAllowIPSetName = "weave-s_+ChJId4Uy_$}G;WdH|~TK)I"
|
||||
fooPodIP = "10.32.0.10"
|
||||
barPodIP = "10.32.0.11"
|
||||
barPodNewIP = "10.32.0.12"
|
||||
)
|
||||
|
||||
m := newMockIPSet()
|
||||
controller := New("bar", false, &mockIPTables{}, &m)
|
||||
controller := New("bar", newMockIPTables(), &m)
|
||||
|
||||
defaultNamespace := &coreapi.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -213,7 +245,8 @@ func TestDefaultAllow(t *testing.T) {
|
||||
controller.AddNamespace(defaultNamespace)
|
||||
|
||||
// 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{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -225,7 +258,8 @@ func TestDefaultAllow(t *testing.T) {
|
||||
controller.AddPod(podFoo)
|
||||
|
||||
// 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{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -236,10 +270,12 @@ func TestDefaultAllow(t *testing.T) {
|
||||
Status: coreapi.PodStatus{PodIP: barPodIP}}
|
||||
podBarNoIP := &coreapi.Pod{ObjectMeta: podBar.ObjectMeta}
|
||||
controller.AddPod(podBarNoIP)
|
||||
|
||||
controller.UpdatePod(podBarNoIP, podBar)
|
||||
|
||||
// Should add the bar pod to default-allow
|
||||
require.True(t, m.entryExists(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
|
||||
netpol := &networkingv1.NetworkPolicy{
|
||||
@@ -248,6 +284,10 @@ func TestDefaultAllow(t *testing.T) {
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
PolicyTypes: []networkingv1.PolicyType{
|
||||
networkingv1.PolicyTypeIngress,
|
||||
networkingv1.PolicyTypeEgress,
|
||||
},
|
||||
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "foo"}},
|
||||
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||
From: []networkingv1.NetworkPolicyPeer{{
|
||||
@@ -256,55 +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)
|
||||
|
||||
// Should remove the foo pod from default-allow as the netpol selects it
|
||||
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, barPodIP))
|
||||
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||
require.False(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, barPodIP))
|
||||
require.True(t, m.entryExists(egressDefaultAllowIPSetName, barPodIP))
|
||||
|
||||
podBarWithNewIP := *podBar
|
||||
podBarWithNewIP.Status.PodIP = barPodNewIP
|
||||
controller.UpdatePod(podBar, &podBarWithNewIP)
|
||||
|
||||
// Should update IP addr of the bar pod in default-allow
|
||||
require.False(t, m.entryExists(defaultAllowIPSetName, barPodIP))
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, barPodNewIP))
|
||||
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, barPodIP))
|
||||
require.False(t, m.entryExists(egressDefaultAllowIPSetName, barPodIP))
|
||||
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, barPodNewIP))
|
||||
require.True(t, m.entryExists(egressDefaultAllowIPSetName, barPodNewIP))
|
||||
|
||||
controller.UpdatePod(&podBarWithNewIP, podBarNoIP)
|
||||
// Should remove the bar pod from default-allow as it does not have any IP addr
|
||||
require.False(t, m.entryExists(defaultAllowIPSetName, barPodNewIP))
|
||||
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, barPodNewIP))
|
||||
require.False(t, m.entryExists(egressDefaultAllowIPSetName, barPodNewIP))
|
||||
|
||||
podFooWithNewLabel := *podFoo
|
||||
podFooWithNewLabel.ObjectMeta.Labels = map[string]string{"run": "new-foo"}
|
||||
controller.UpdatePod(podFoo, &podFooWithNewLabel)
|
||||
|
||||
// Should bring back the foo pod to default-allow as it does not match dst of any netpol
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||
require.True(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||
|
||||
controller.UpdatePod(&podFooWithNewLabel, podFoo)
|
||||
// Should remove from default-allow as it matches the netpol after the update
|
||||
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||
require.False(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||
|
||||
controller.DeleteNetworkPolicy(netpol)
|
||||
// Should bring back the foo pod to default-allow as no netpol selects it
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||
require.True(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||
|
||||
controller.DeletePod(podFoo)
|
||||
// Should remove foo pod from default-allow
|
||||
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, fooPodIP))
|
||||
require.False(t, m.entryExists(egressDefaultAllowIPSetName, fooPodIP))
|
||||
|
||||
controller.DeleteNamespace(defaultNamespace)
|
||||
// Should remove default ipset
|
||||
require.NotContains(t, m.sets, ingressDefaultAllowIPSetName)
|
||||
require.NotContains(t, m.sets, egressDefaultAllowIPSetName)
|
||||
}
|
||||
|
||||
func TestOutOfOrderPodEvents(t *testing.T) {
|
||||
const (
|
||||
defaultAllowIPSetName = "weave-E.1.0W^NGSp]0_t5WwH/]gX@L"
|
||||
runBarIPSetName = "weave-bZ~x=yBgzH)Ht()K*Uv3z{M]Y"
|
||||
podIP = "10.32.0.10"
|
||||
ingressDefaultAllowIPSetName = "weave-;rGqyMIl1HN^cfDki~Z$3]6!N"
|
||||
runBarIPSetName = "weave-bZ~x=yBgzH)Ht()K*Uv3z{M]Y"
|
||||
podIP = "10.32.0.10"
|
||||
)
|
||||
|
||||
m := newMockIPSet()
|
||||
controller := New("qux", false, &mockIPTables{}, &m)
|
||||
controller := New("qux", newMockIPTables(), &m)
|
||||
|
||||
defaultNamespace := &coreapi.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -323,7 +382,7 @@ func TestOutOfOrderPodEvents(t *testing.T) {
|
||||
controller.AddPod(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{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -331,6 +390,7 @@ func TestOutOfOrderPodEvents(t *testing.T) {
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "foo"}},
|
||||
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||
From: []networkingv1.NetworkPolicyPeer{{
|
||||
@@ -344,7 +404,7 @@ func TestOutOfOrderPodEvents(t *testing.T) {
|
||||
controller.AddNetworkPolicy(netpol)
|
||||
|
||||
// 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{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -356,8 +416,8 @@ func TestOutOfOrderPodEvents(t *testing.T) {
|
||||
controller.AddPod(podBar)
|
||||
|
||||
// Should be in default-allow as no netpol selects podBar
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
|
||||
require.True(t, m.Exist(podBar.ObjectMeta.UID, defaultAllowIPSetName, podIP))
|
||||
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||
require.True(t, m.Exist(podBar.ObjectMeta.UID, ingressDefaultAllowIPSetName, podIP))
|
||||
// Should be in run=bar ipset
|
||||
require.True(t, m.entryExists(runBarIPSetName, podIP))
|
||||
|
||||
@@ -368,11 +428,167 @@ func TestOutOfOrderPodEvents(t *testing.T) {
|
||||
|
||||
// Should be in default-allow as no netpol selects podBar and podFoo removal
|
||||
// should not affect podBar in default-allow
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
|
||||
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||
|
||||
controller.DeletePod(podBar)
|
||||
|
||||
// Should remove from default-allow and run=bar ipsets
|
||||
require.Equal(t, 0, len(m.sets[defaultAllowIPSetName].subSets))
|
||||
require.Equal(t, 0, len(m.sets[ingressDefaultAllowIPSetName].subSets))
|
||||
require.False(t, m.entryExists(runBarIPSetName, podIP))
|
||||
}
|
||||
|
||||
// Test case for https://github.com/weaveworks/weave/issues/3222
|
||||
func TestNewTargetSelector(t *testing.T) {
|
||||
const (
|
||||
ingressDefaultAllowIPSetName = "weave-;rGqyMIl1HN^cfDki~Z$3]6!N"
|
||||
podIP = "10.32.0.10"
|
||||
)
|
||||
|
||||
m := newMockIPSet()
|
||||
controller := New("baz", newMockIPTables(), &m)
|
||||
|
||||
defaultNamespace := &coreapi.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default",
|
||||
},
|
||||
}
|
||||
controller.AddNamespace(defaultNamespace)
|
||||
|
||||
podFoo := &coreapi.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "foo",
|
||||
Namespace: "default",
|
||||
Name: "foo",
|
||||
Labels: map[string]string{"run": "foo"}},
|
||||
Status: coreapi.PodStatus{PodIP: podIP}}
|
||||
controller.AddPod(podFoo)
|
||||
|
||||
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||
|
||||
netpolBar := &networkingv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "netpol-bar",
|
||||
Name: "allow-from-default-1",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||
PodSelector: metav1.LabelSelector{},
|
||||
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||
From: []networkingv1.NetworkPolicyPeer{{
|
||||
PodSelector: &metav1.LabelSelector{},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
}
|
||||
controller.AddNetworkPolicy(netpolBar)
|
||||
|
||||
// netpolBar target selector selects podFoo
|
||||
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||
|
||||
netpolFoo := &networkingv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "netpol-foo",
|
||||
Name: "allow-from-default-2",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
|
||||
PodSelector: metav1.LabelSelector{},
|
||||
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||
From: []networkingv1.NetworkPolicyPeer{{
|
||||
PodSelector: &metav1.LabelSelector{},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
}
|
||||
controller.AddNetworkPolicy(netpolFoo)
|
||||
|
||||
controller.DeleteNetworkPolicy(netpolBar)
|
||||
// netpolFoo target-selects podFoo
|
||||
require.False(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||
controller.DeleteNetworkPolicy(netpolFoo)
|
||||
// No netpol target-selects podFoo
|
||||
require.True(t, m.entryExists(ingressDefaultAllowIPSetName, podIP))
|
||||
}
|
||||
|
||||
func TestEgressPolicyWithIPBlock(t *testing.T) {
|
||||
const (
|
||||
fooPodIP = "10.32.0.10"
|
||||
exceptIPSetName = "weave-j:W$5om!$8JS})buYAD7q^#sX"
|
||||
exceptIPSetNameInNonDefault = "weave-|*P{aL2C@#N/1IZ!IvQItv_pt"
|
||||
)
|
||||
|
||||
m := newMockIPSet()
|
||||
ipt := newMockIPTables()
|
||||
controller := New("foo", ipt, &m)
|
||||
|
||||
netpolFoo := &networkingv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "netpol-foo",
|
||||
Name: "allow-from-bar-to-foo",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
PolicyTypes: []networkingv1.PolicyType{
|
||||
networkingv1.PolicyTypeEgress,
|
||||
},
|
||||
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "bar"}},
|
||||
Egress: []networkingv1.NetworkPolicyEgressRule{{
|
||||
To: []networkingv1.NetworkPolicyPeer{{
|
||||
IPBlock: &networkingv1.IPBlock{
|
||||
CIDR: "192.168.48.0/24",
|
||||
Except: []string{
|
||||
"192.168.48.1/32",
|
||||
"192.168.48.2/32",
|
||||
},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
}
|
||||
err := controller.AddNetworkPolicy(netpolFoo)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 2, len(m.sets[exceptIPSetName].subSets))
|
||||
require.True(t, m.entryExists(exceptIPSetName, "192.168.48.1/32"))
|
||||
require.True(t, m.entryExists(exceptIPSetName, "192.168.48.2/32"))
|
||||
|
||||
// Each egress rule is represented as two iptables rules (-J MARK and -J RETURN).
|
||||
require.Equal(t, 2, len(ipt.rules[EgressCustomChain]))
|
||||
for rule := range ipt.rules[EgressCustomChain] {
|
||||
require.Contains(t, rule, "-d 192.168.48.0/24 -m set ! --match-set "+exceptIPSetName+" dst")
|
||||
}
|
||||
|
||||
// Check that we create a new ipset for the ipBlock bellow. An ipset with the
|
||||
// same content already exists (created by netpolFoo), but we need to create
|
||||
// a new one, as netpolBar is in a different namespace and weave-npc does
|
||||
// object refcounting only within namespace boundaries.
|
||||
netpolBar := &networkingv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "netpol-foo-bar",
|
||||
Name: "allow-from-bar-to-foo-in-non-default",
|
||||
Namespace: "non-default",
|
||||
},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
PolicyTypes: []networkingv1.PolicyType{
|
||||
networkingv1.PolicyTypeEgress,
|
||||
},
|
||||
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "bar"}},
|
||||
Egress: []networkingv1.NetworkPolicyEgressRule{{
|
||||
To: []networkingv1.NetworkPolicyPeer{{
|
||||
IPBlock: &networkingv1.IPBlock{
|
||||
CIDR: "192.168.48.0/24",
|
||||
Except: []string{
|
||||
"192.168.48.1/32",
|
||||
"192.168.48.2/32",
|
||||
},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
}
|
||||
err = controller.AddNetworkPolicy(netpolBar)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(m.sets[exceptIPSetNameInNonDefault].subSets))
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/net/ipset"
|
||||
)
|
||||
|
||||
type ipBlockSpec struct {
|
||||
key string
|
||||
ipsetName ipset.Name // ipset for storing excepted CIDRs
|
||||
ipBlock *networkingv1.IPBlock
|
||||
nsName string // Namespace name
|
||||
}
|
||||
|
||||
func newIPBlockSpec(ipb *networkingv1.IPBlock, nsName string) *ipBlockSpec {
|
||||
spec := &ipBlockSpec{ipBlock: ipb, nsName: nsName}
|
||||
|
||||
if len(ipb.Except) > 0 {
|
||||
sort.Strings(ipb.Except)
|
||||
spec.key = strings.Join(ipb.Except, " ")
|
||||
spec.ipsetName = ipset.Name(IpsetNamePrefix + shortName(nsName+":"+"ipblock-except:"+spec.key))
|
||||
}
|
||||
|
||||
return spec
|
||||
}
|
||||
|
||||
func (spec *ipBlockSpec) getRuleSpec(src bool) ([]string, string) {
|
||||
dir, dirOpt := "dst", "-d"
|
||||
if src {
|
||||
dir, dirOpt = "src", "-s"
|
||||
}
|
||||
|
||||
if spec.key == "" {
|
||||
rule := []string{dirOpt, spec.ipBlock.CIDR}
|
||||
comment := fmt.Sprintf("cidr: %s", spec.ipBlock.CIDR)
|
||||
return rule, comment
|
||||
}
|
||||
|
||||
rule := []string{dirOpt, spec.ipBlock.CIDR,
|
||||
"-m", "set", "!", "--match-set", string(spec.ipsetName), dir}
|
||||
comment := fmt.Sprintf("cidr: %s except [%s]", spec.ipBlock.CIDR, spec.key)
|
||||
return rule, comment
|
||||
}
|
||||
|
||||
type ipBlockSet struct {
|
||||
ips ipset.Interface
|
||||
users map[string]map[types.UID]struct{}
|
||||
}
|
||||
|
||||
func newIPBlockSet(ips ipset.Interface) *ipBlockSet {
|
||||
return &ipBlockSet{
|
||||
ips: ips,
|
||||
users: make(map[string]map[types.UID]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ipBlockSet) deprovision(user types.UID, current, desired map[string]*ipBlockSpec) error {
|
||||
for key, spec := range current {
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, found := desired[key]; !found {
|
||||
delete(s.users[key], user)
|
||||
if len(s.users[key]) == 0 {
|
||||
common.Log.Infof("destroying ipset: %#v", spec)
|
||||
if err := s.ips.Destroy(spec.ipsetName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delete(s.users, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ipBlockSet) provision(user types.UID, current, desired map[string]*ipBlockSpec) (err error) {
|
||||
for key, spec := range desired {
|
||||
if key == "" {
|
||||
// No need to provision an ipBlock with empty list of excepted CIDRs
|
||||
// (i.e. no ipset is needed for the related iptables rule).
|
||||
continue
|
||||
}
|
||||
|
||||
if _, found := current[key]; !found {
|
||||
if _, found := s.users[key]; !found {
|
||||
common.Log.Infof("creating ipset: %#v", spec)
|
||||
if err := s.ips.Create(spec.ipsetName, ipset.HashNet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(brb) Pass comment to ips.Create instead.
|
||||
comment := "excepted from " + spec.ipBlock.CIDR
|
||||
for _, cidr := range spec.ipBlock.Except {
|
||||
if err = s.ips.AddEntry(user, spec.ipsetName, cidr, comment); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s.users[key] = make(map[types.UID]struct{})
|
||||
}
|
||||
s.users[key][user] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+49
@@ -4,7 +4,56 @@ import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Return JSON suitable for logging an API object.
|
||||
func js(v interface{}) string {
|
||||
// Get the raw JSON
|
||||
a, _ := json.Marshal(v)
|
||||
// Convert this back into a tree of key-value maps
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal(a, &m); err != nil {
|
||||
// If that didn't work, just return the raw version
|
||||
return string(a)
|
||||
}
|
||||
// Trim some bulk, and potentially sensitive areas
|
||||
withMap(m["metadata"], func(status map[string]interface{}) {
|
||||
delete(status, "ownerReferences")
|
||||
})
|
||||
withMap(m["spec"], func(spec map[string]interface{}) {
|
||||
delete(spec, "tolerations")
|
||||
delete(spec, "volumes")
|
||||
rangeSlice(spec["containers"], func(container map[string]interface{}) {
|
||||
delete(container, "args")
|
||||
delete(container, "command")
|
||||
delete(container, "env")
|
||||
delete(container, "livenessProbe")
|
||||
delete(container, "resources")
|
||||
delete(container, "securityContext")
|
||||
delete(container, "volumeMounts")
|
||||
})
|
||||
})
|
||||
withMap(m["status"], func(status map[string]interface{}) {
|
||||
delete(status, "containerStatuses")
|
||||
})
|
||||
// Now marshall what's left to JSON
|
||||
a, _ = json.Marshal(m)
|
||||
return string(a)
|
||||
}
|
||||
|
||||
// Helper function: operate on a map node from a tree of key-value maps
|
||||
func withMap(m interface{}, f func(map[string]interface{})) {
|
||||
if v, ok := m.(map[string]interface{}); ok {
|
||||
f(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function: operate on all nodes under i which is a slice in a
|
||||
// tree of key-value maps
|
||||
func rangeSlice(i interface{}, f func(map[string]interface{})) {
|
||||
if s, ok := i.([]interface{}); ok {
|
||||
for _, v := range s {
|
||||
if m, ok := v.(map[string]interface{}); ok {
|
||||
f(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+185
-156
@@ -1,7 +1,6 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
"github.com/weaveworks/weave/net/ipset"
|
||||
"github.com/weaveworks/weave/npc/iptables"
|
||||
)
|
||||
|
||||
@@ -32,20 +31,20 @@ type ns struct {
|
||||
uid types.UID // surrogate UID to own allPods selector
|
||||
allPods *selectorSpec // hash:ip ipset of all pod IPs in this namespace
|
||||
|
||||
// stores IP addrs of pods which are not selected by any dst podSelector of
|
||||
// any netpol; used only in non-legacy mode and is used as a dst in
|
||||
// the WEAVE-NPC-DEFAULT iptables chain.
|
||||
defaultAllowIPSet ipset.Name
|
||||
// stores IP addrs of pods which are not selected by any target podSelector of
|
||||
// any netpol; used as a target in the WEAVE-NPC-{INGRESS,EGRESS}-DEFAULT
|
||||
// iptables chains.
|
||||
ingressDefaultAllowIPSet ipset.Name
|
||||
egressDefaultAllowIPSet ipset.Name
|
||||
|
||||
nsSelectors *selectorSet
|
||||
podSelectors *selectorSet
|
||||
ipBlocks *ipBlockSet
|
||||
rules *ruleSet
|
||||
|
||||
legacy bool
|
||||
}
|
||||
|
||||
func newNS(name, nodeName string, legacy bool, ipt iptables.Interface, ips ipset.Interface, nsSelectors *selectorSet) (*ns, error) {
|
||||
allPods, err := newSelectorSpec(&metav1.LabelSelector{}, false, name, ipset.HashIP)
|
||||
func newNS(name, nodeName string, ipt iptables.Interface, ips ipset.Interface, nsSelectors *selectorSet) (*ns, error) {
|
||||
allPods, err := newSelectorSpec(&metav1.LabelSelector{}, nil, name, ipset.HashIP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -60,19 +59,24 @@ func newNS(name, nodeName string, legacy bool, ipt iptables.Interface, ips ipset
|
||||
uid: uuid.NewUUID(),
|
||||
allPods: allPods,
|
||||
nsSelectors: nsSelectors,
|
||||
ipBlocks: newIPBlockSet(ips),
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -102,16 +106,13 @@ func (ns *ns) onNewPodSelector(selector *selector) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) onNewDstPodSelector(selector *selector) error {
|
||||
if ns.legacy {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) onNewTargetPodSelector(selector *selector, policyType policyType) error {
|
||||
for _, pod := range ns.pods {
|
||||
if hasIP(pod) {
|
||||
// Remove the pod from default-allow if dst podselector matches the pod
|
||||
ipset := ns.defaultAllowIPSetName(policyType)
|
||||
if selector.matches(pod.ObjectMeta.Labels) {
|
||||
if err := ns.ips.DelEntry(pod.ObjectMeta.UID, ns.defaultAllowIPSet, pod.Status.PodIP); err != nil {
|
||||
if err := ns.ips.DelEntry(pod.ObjectMeta.UID, ipset, pod.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -121,15 +122,11 @@ func (ns *ns) onNewDstPodSelector(selector *selector) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) onDestroyDstPodSelector(selector *selector) error {
|
||||
if ns.legacy {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) onDestroyTargetPodSelector(selector *selector, policyType policyType) error {
|
||||
for _, pod := range ns.pods {
|
||||
if hasIP(pod) {
|
||||
if selector.matches(pod.ObjectMeta.Labels) {
|
||||
if err := ns.addToDefaultAllowIfNoMatching(pod); err != nil {
|
||||
if err := ns.addToDefaultAllowIfNoMatching(pod, policyType); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -139,18 +136,19 @@ func (ns *ns) onDestroyDstPodSelector(selector *selector) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add pod IP addr to default-allow ipset if there are no matching dst selectors
|
||||
func (ns *ns) addToDefaultAllowIfNoMatching(pod *coreapi.Pod) error {
|
||||
// Add pod IP addr to default-allow ipset if there are no matching target selectors
|
||||
func (ns *ns) addToDefaultAllowIfNoMatching(pod *coreapi.Pod, policyType policyType) error {
|
||||
found := false
|
||||
// TODO(mp) optimize (avoid iterating over selectors) by ref counting IP addrs.
|
||||
for _, s := range ns.podSelectors.entries {
|
||||
if ns.podSelectors.dstSelectorExist(s) && s.matches(pod.ObjectMeta.Labels) {
|
||||
if ns.podSelectors.targetSelectorExist(s, policyType) && s.matches(pod.ObjectMeta.Labels) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -175,13 +173,18 @@ func (ns *ns) addPod(obj *coreapi.Pod) error {
|
||||
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))
|
||||
foundIngress, foundEgress, err := ns.podSelectors.addToMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, obj.Status.PodIP, podComment(obj))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If there are no matching dst selectors, add the pod to default-allow
|
||||
if !ns.legacy && !found {
|
||||
if err := ns.ips.AddEntry(obj.ObjectMeta.UID, ns.defaultAllowIPSet, obj.Status.PodIP, podComment(obj)); err != nil {
|
||||
// If there are no matching target selectors, add the pod to default-allow
|
||||
if !foundIngress {
|
||||
if err := ns.ips.AddEntry(obj.ObjectMeta.UID, ns.ingressDefaultAllowIPSet, obj.Status.PodIP, podComment(obj)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !foundEgress {
|
||||
if err := ns.ips.AddEntry(obj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, obj.Status.PodIP, podComment(obj)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -202,10 +205,11 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
ns.ips.DelEntry(oldObj.ObjectMeta.UID, LocalIpset, oldObj.Status.PodIP)
|
||||
}
|
||||
|
||||
if !ns.legacy {
|
||||
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.ingressDefaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ns.podSelectors.delFromMatching(oldObj.ObjectMeta.UID, oldObj.ObjectMeta.Labels, oldObj.Status.PodIP)
|
||||
@@ -215,13 +219,18 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
if ns.checkLocalPod(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))
|
||||
foundIngress, foundEgress, err := ns.podSelectors.addToMatching(newObj.ObjectMeta.UID, newObj.ObjectMeta.Labels, newObj.Status.PodIP, podComment(newObj))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ns.legacy && !found {
|
||||
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.defaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||
if !foundIngress {
|
||||
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.ingressDefaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !foundEgress {
|
||||
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -229,17 +238,11 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !ns.legacy &&
|
||||
oldObj.Status.PodIP != newObj.Status.PodIP &&
|
||||
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 {
|
||||
if oldObj.Status.PodIP != newObj.Status.PodIP {
|
||||
if err := ns.updateDefaultAllowIPSetEntry(oldObj, newObj, ns.ingressDefaultAllowIPSet); err != nil {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -264,17 +267,11 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
}
|
||||
}
|
||||
|
||||
if !ns.legacy && ns.podSelectors.dstSelectorExist(ps) {
|
||||
switch {
|
||||
case !oldMatch && newMatch:
|
||||
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
case oldMatch && !newMatch:
|
||||
if err := ns.addToDefaultAllowIfNoMatching(newObj); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := ns.addOrRemoveToDefaultAllowIPSet(ps, oldObj, newObj, oldMatch, newMatch, policyTypeIngress); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.addOrRemoveToDefaultAllowIPSet(ps, oldObj, newObj, oldMatch, newMatch, policyTypeEgress); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,6 +279,23 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
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 {
|
||||
delete(ns.pods, obj.ObjectMeta.UID)
|
||||
|
||||
@@ -293,10 +307,11 @@ func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
||||
ns.ips.DelEntry(obj.ObjectMeta.UID, LocalIpset, obj.Status.PodIP)
|
||||
}
|
||||
|
||||
if !ns.legacy {
|
||||
if err := ns.ips.DelEntry(obj.ObjectMeta.UID, ns.defaultAllowIPSet, obj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.ips.DelEntry(obj.ObjectMeta.UID, ns.ingressDefaultAllowIPSet, obj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.ips.DelEntry(obj.ObjectMeta.UID, ns.egressDefaultAllowIPSet, obj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ns.podSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, obj.Status.PodIP)
|
||||
@@ -305,7 +320,7 @@ func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
||||
func (ns *ns) addNetworkPolicy(obj interface{}) error {
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
@@ -317,16 +332,19 @@ func (ns *ns) addNetworkPolicy(obj interface{}) error {
|
||||
if err := ns.podSelectors.provision(uid, nil, podSelectors); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.ipBlocks.provision(uid, nil, ipBlocks); err != nil {
|
||||
return err
|
||||
}
|
||||
return ns.rules.provision(uid, nil, rules)
|
||||
}
|
||||
|
||||
func (ns *ns) updateNetworkPolicy(oldObj, newObj interface{}) error {
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
newUID, newRules, newNsSelectors, newPodSelectors, err := ns.analyse(newObj)
|
||||
newUID, newRules, newNsSelectors, newPodSelectors, newIPBlocks, err := ns.analyse(newObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -344,18 +362,24 @@ func (ns *ns) updateNetworkPolicy(oldObj, newObj interface{}) error {
|
||||
if err := ns.podSelectors.deprovision(oldUID, oldPodSelectors, newPodSelectors); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.ipBlocks.deprovision(oldUID, oldIPBlocks, newIPBlocks); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.nsSelectors.provision(oldUID, oldNsSelectors, newNsSelectors); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.podSelectors.provision(oldUID, oldPodSelectors, newPodSelectors); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.ipBlocks.provision(oldUID, oldIPBlocks, newIPBlocks); err != nil {
|
||||
return err
|
||||
}
|
||||
return ns.rules.provision(oldUID, oldRules, newRules)
|
||||
}
|
||||
|
||||
func (ns *ns) deleteNetworkPolicy(obj interface{}) error {
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
@@ -369,73 +393,89 @@ func (ns *ns) deleteNetworkPolicy(obj interface{}) error {
|
||||
if err := ns.nsSelectors.deprovision(uid, nsSelectors, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return ns.podSelectors.deprovision(uid, podSelectors, nil)
|
||||
}
|
||||
|
||||
func bypassRule(nsIpsetName ipset.Name, namespace string) []string {
|
||||
return []string{"-m", "set", "--match-set", string(nsIpsetName), "dst", "-j", "ACCEPT", "-m", "comment", "--comment", "DefaultAllow isolation for namespace: " + namespace}
|
||||
}
|
||||
|
||||
func (ns *ns) ensureBypassRule() error {
|
||||
var ipset ipset.Name
|
||||
if ns.legacy {
|
||||
ipset = ns.allPods.ipsetName
|
||||
} else {
|
||||
ipset = ns.defaultAllowIPSet
|
||||
if err := ns.podSelectors.deprovision(uid, podSelectors, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.ipBlocks.deprovision(uid, ipBlocks, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
common.Log.Debugf("ensuring rule for DefaultAllow in namespace: %s, set %s", ns.name, ipset)
|
||||
return ns.ipt.Append(TableFilter, DefaultChain, bypassRule(ipset, ns.name)...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) deleteBypassRule() error {
|
||||
var ipset ipset.Name
|
||||
if ns.legacy {
|
||||
ipset = ns.allPods.ipsetName
|
||||
} else {
|
||||
ipset = ns.defaultAllowIPSet
|
||||
func (ns *ns) updateDefaultAllowIPSetEntry(oldObj, newObj *coreapi.Pod, ipsetName ipset.Name) error {
|
||||
// Instead of iterating over all selectors we check whether old pod IP
|
||||
// has been inserted into default-allow ipset to decide whether the IP
|
||||
// in the ipset has to be updated.
|
||||
if ns.ips.Exist(oldObj.ObjectMeta.UID, ipsetName, oldObj.Status.PodIP) {
|
||||
|
||||
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ipsetName, oldObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ipsetName, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func 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 ns.ipt.Delete(TableFilter, DefaultChain, bypassRule(ipset, ns.name)...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) deleteBypassRules() error {
|
||||
for chain, rules := range bypassRules(ns.name, ns.ingressDefaultAllowIPSet, ns.egressDefaultAllowIPSet) {
|
||||
for _, rule := range rules {
|
||||
common.Log.Debugf("removing rule for DefaultAllow in namespace: %s, chain: %s, %s", ns.name, chain, rule)
|
||||
if err := ns.ipt.Delete(TableFilter, chain, rule...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ns) addNamespace(obj *coreapi.Namespace) error {
|
||||
ns.namespace = obj
|
||||
|
||||
// Insert a rule to bypass policies if namespace is DefaultAllow
|
||||
if !ns.isDefaultDeny(obj) {
|
||||
if err := ns.ensureBypassRule(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Insert a rule to bypass policies
|
||||
if err := ns.ensureBypassRules(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
||||
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
|
||||
if !equals(oldObj.ObjectMeta.Labels, newObj.ObjectMeta.Labels) {
|
||||
for _, selector := range ns.nsSelectors.entries {
|
||||
@@ -464,37 +504,24 @@ func (ns *ns) deleteNamespace(obj *coreapi.Namespace) error {
|
||||
ns.namespace = nil
|
||||
|
||||
// Remove bypass rule
|
||||
if !ns.isDefaultDeny(obj) {
|
||||
if err := ns.deleteBypassRule(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.deleteBypassRules(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove namespace ipset from any matching namespace selectors
|
||||
return ns.nsSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
||||
}
|
||||
|
||||
func (ns *ns) isDefaultDeny(namespace *coreapi.Namespace) bool {
|
||||
nnpJSON, found := namespace.ObjectMeta.Annotations["net.beta.kubernetes.io/network-policy"]
|
||||
if !found {
|
||||
return false
|
||||
err := ns.nsSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ns.legacy {
|
||||
common.Log.Warn("DefaultDeny annotation is supported only in legacy mode (--use-legacy-netpol)")
|
||||
return false
|
||||
if err := ns.ips.Destroy(ns.ingressDefaultAllowIPSet); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.ips.Destroy(ns.egressDefaultAllowIPSet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasIP(pod *coreapi.Pod) bool {
|
||||
@@ -523,10 +550,19 @@ func podComment(pod *coreapi.Pod) string {
|
||||
return fmt.Sprintf("namespace: %s, pod: %s", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name)
|
||||
}
|
||||
|
||||
func (ns *ns) defaultAllowIPSetName(pt policyType) ipset.Name {
|
||||
ipset := ns.ingressDefaultAllowIPSet
|
||||
if pt == policyTypeEgress {
|
||||
ipset = ns.egressDefaultAllowIPSet
|
||||
}
|
||||
return ipset
|
||||
}
|
||||
|
||||
func (ns *ns) analyse(obj interface{}) (
|
||||
uid types.UID,
|
||||
rules map[string]*ruleSpec,
|
||||
nsSelectors, podSelectors map[string]*selectorSpec,
|
||||
ipBlocks map[string]*ipBlockSpec,
|
||||
err error) {
|
||||
|
||||
switch p := obj.(type) {
|
||||
@@ -541,16 +577,9 @@ func (ns *ns) analyse(obj interface{}) (
|
||||
ns.policies[uid] = obj
|
||||
|
||||
// Analyse policy, determine which rules and ipsets are required
|
||||
if ns.legacy {
|
||||
rules, nsSelectors, podSelectors, err = ns.analysePolicyLegacy(obj.(*extnapi.NetworkPolicy))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
rules, nsSelectors, podSelectors, err = ns.analysePolicy(obj.(*networkingv1.NetworkPolicy))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rules, nsSelectors, podSelectors, ipBlocks, err = ns.analysePolicy(obj.(*networkingv1.NetworkPolicy))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
+64
-23
@@ -2,6 +2,7 @@ package npc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
@@ -10,38 +11,71 @@ import (
|
||||
"github.com/weaveworks/weave/npc/iptables"
|
||||
)
|
||||
|
||||
type ruleSpec struct {
|
||||
key string
|
||||
args []string
|
||||
type ruleHost interface {
|
||||
// getRuleSpec returns source or destination specification and comment which
|
||||
// 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{}
|
||||
if proto != nil {
|
||||
args = append(args, "-p", *proto)
|
||||
}
|
||||
srcComment := "anywhere"
|
||||
if srcHost != nil {
|
||||
args = append(args, "-m", "set", "--match-set", string(srcHost.ipsetName), "src")
|
||||
if srcHost.nsName != "" {
|
||||
srcComment = fmt.Sprintf("pods: namespace: %s, selector: %s", srcHost.nsName, srcHost.key)
|
||||
} else {
|
||||
srcComment = fmt.Sprintf("namespaces: selector: %s", srcHost.key)
|
||||
}
|
||||
if srcHost != nil && !reflect.ValueOf(srcHost).IsNil() {
|
||||
rule, comment := srcHost.getRuleSpec(true)
|
||||
args = append(args, rule...)
|
||||
srcComment = comment
|
||||
}
|
||||
dstComment := "anywhere"
|
||||
if dstHost != nil {
|
||||
args = append(args, "-m", "set", "--match-set", string(dstHost.ipsetName), "dst")
|
||||
dstComment = fmt.Sprintf("pods: namespace: %s, selector: %s", dstHost.nsName, dstHost.key)
|
||||
if dstHost != nil && !reflect.ValueOf(dstHost).IsNil() {
|
||||
rule, comment := dstHost.getRuleSpec(false)
|
||||
args = append(args, rule...)
|
||||
dstComment = comment
|
||||
}
|
||||
if dstPort != nil {
|
||||
args = append(args, "--dport", *dstPort)
|
||||
}
|
||||
args = append(args, "-j", "ACCEPT")
|
||||
args = append(args, "-m", "comment", "--comment", fmt.Sprintf("%s -> %s", srcComment, dstComment))
|
||||
// NOTE: if you remove the comment bellow, then embed `policyType` into `key`.
|
||||
// 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, " ")
|
||||
|
||||
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 {
|
||||
@@ -58,10 +92,14 @@ func (rs *ruleSet) deprovision(user types.UID, current, desired map[string]*rule
|
||||
if _, found := desired[key]; !found {
|
||||
delete(rs.users[key], user)
|
||||
if len(rs.users[key]) == 0 {
|
||||
common.Log.Infof("deleting rule: %v", spec.args)
|
||||
if err := rs.ipt.Delete(TableFilter, IngressChain, spec.args...); err != nil {
|
||||
return err
|
||||
chain := spec.iptChain()
|
||||
for _, rule := range spec.iptRuleSpecs() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -74,9 +112,12 @@ func (rs *ruleSet) provision(user types.UID, current, desired map[string]*ruleSp
|
||||
for key, spec := range desired {
|
||||
if _, found := current[key]; !found {
|
||||
if _, found := rs.users[key]; !found {
|
||||
common.Log.Infof("adding rule: %v", spec.args)
|
||||
if err := rs.ipt.Append(TableFilter, IngressChain, spec.args...); err != nil {
|
||||
return err
|
||||
chain := spec.iptChain()
|
||||
for _, rule := range spec.iptRuleSpecs() {
|
||||
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{})
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ func TestRegressionHandleNil3095(t *testing.T) {
|
||||
// Test for handling nil values
|
||||
// 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, "")
|
||||
|
||||
|
||||
+66
-38
@@ -1,34 +1,36 @@
|
||||
package npc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/npc/ipset"
|
||||
"github.com/weaveworks/weave/net/ipset"
|
||||
)
|
||||
|
||||
type selectorSpec struct {
|
||||
key string // string representation (for hash keying/equality comparison)
|
||||
selector labels.Selector // k8s Selector object (for matching)
|
||||
dst bool // destination selector
|
||||
key string // string representation (for hash keying/equality comparison)
|
||||
selector labels.Selector // k8s Selector object (for matching)
|
||||
policyTypes []policyType // If non-empty, then selectorSpec is a target selector for given policyTypes.
|
||||
|
||||
ipsetType ipset.Type // type of ipset to provision
|
||||
ipsetName ipset.Name // generated ipset 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := selector.String()
|
||||
return &selectorSpec{
|
||||
key: key,
|
||||
selector: selector,
|
||||
dst: dst,
|
||||
key: key,
|
||||
selector: selector,
|
||||
policyTypes: policyType,
|
||||
// We prefix the selector string with the namespace name when generating
|
||||
// the shortname because you can specify the same selector in multiple
|
||||
// namespaces - we need those to map to distinct ipsets
|
||||
@@ -37,6 +39,23 @@ func newSelectorSpec(json *metav1.LabelSelector, dst bool, nsName string, ipsetT
|
||||
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 {
|
||||
ips ipset.Interface
|
||||
spec *selectorSpec
|
||||
@@ -55,49 +74,54 @@ func (s *selector) delEntry(user types.UID, entry string) error {
|
||||
}
|
||||
|
||||
type selectorFn func(selector *selector) error
|
||||
type selectorWithPolicyTypeFn func(selector *selector, policyType policyType) error
|
||||
|
||||
type selectorSet struct {
|
||||
ips ipset.Interface
|
||||
onNewSelector selectorFn
|
||||
|
||||
// invoked after dst selector has been provisioned for the first time
|
||||
onNewDstSelector selectorFn
|
||||
// invoked after the last instance of dst selector has been deprovisioned
|
||||
onDestroyDstSelector selectorFn
|
||||
// invoked after target selector has been provisioned for the first time
|
||||
onNewTargetSelector selectorWithPolicyTypeFn
|
||||
// invoked after the last instance of target selector has been deprovisioned
|
||||
onDestroyTargetSelector selectorWithPolicyTypeFn
|
||||
|
||||
users map[string]map[types.UID]struct{} // list of users per selector
|
||||
entries map[string]*selector
|
||||
|
||||
// We need to keep track of dst selector instances to be able to invoke
|
||||
// onNewDstSelector and onDestroyDstSelector callbacks at the right time;
|
||||
// selectorSpec.Key -> count
|
||||
dstSelectorsCount map[string]int
|
||||
// We need to keep track of target selector instances to be able to invoke
|
||||
// onNewTargetSelector and onDestroyTargetSelector callbacks at the right time;
|
||||
// selectorSpec.Key -> policyType -> count
|
||||
targetSelectorsCount map[string]map[policyType]int
|
||||
}
|
||||
|
||||
func newSelectorSet(ips ipset.Interface, onNewSelector, onNewDstSelector selectorFn, onDestroyDstSelector selectorFn) *selectorSet {
|
||||
func newSelectorSet(ips ipset.Interface, onNewSelector selectorFn, onNewTargetSelector, onDestroyTargetSelector selectorWithPolicyTypeFn) *selectorSet {
|
||||
return &selectorSet{
|
||||
ips: ips,
|
||||
onNewSelector: onNewSelector,
|
||||
onNewDstSelector: onNewDstSelector,
|
||||
onDestroyDstSelector: onDestroyDstSelector,
|
||||
ips: ips,
|
||||
onNewSelector: onNewSelector,
|
||||
onNewTargetSelector: onNewTargetSelector,
|
||||
onDestroyTargetSelector: onDestroyTargetSelector,
|
||||
users: make(map[string]map[types.UID]struct{}),
|
||||
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) {
|
||||
found := false
|
||||
func (ss *selectorSet) addToMatching(user types.UID, labelMap map[string]string, entry string, comment string) (bool, bool, error) {
|
||||
foundIngress := false
|
||||
foundEgress := false
|
||||
for _, s := range ss.entries {
|
||||
if s.matches(labelMap) {
|
||||
if ss.dstSelectorExist(s) {
|
||||
found = true
|
||||
if ss.targetSelectorExist(s, policyTypeIngress) {
|
||||
foundIngress = true
|
||||
}
|
||||
if ss.targetSelectorExist(s, policyTypeEgress) {
|
||||
foundEgress = true
|
||||
}
|
||||
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 {
|
||||
@@ -111,8 +135,8 @@ func (ss *selectorSet) delFromMatching(user types.UID, labelMap map[string]strin
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *selectorSet) dstSelectorExist(s *selector) bool {
|
||||
return ss.dstSelectorsCount[s.spec.key] > 0
|
||||
func (ss *selectorSet) targetSelectorExist(s *selector, policyType policyType) bool {
|
||||
return ss.targetSelectorsCount[s.spec.key][policyType] > 0
|
||||
}
|
||||
|
||||
func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*selectorSpec) error {
|
||||
@@ -129,13 +153,14 @@ func (ss *selectorSet) deprovision(user types.UID, current, desired map[string]*
|
||||
delete(ss.users, key)
|
||||
}
|
||||
|
||||
if spec.dst {
|
||||
ss.dstSelectorsCount[key]--
|
||||
if ss.dstSelectorsCount[key] == 0 {
|
||||
if err := ss.onDestroyDstSelector(&selector{ss.ips, spec}); err != nil {
|
||||
for _, policyType := range spec.policyTypes {
|
||||
ss.targetSelectorsCount[key][policyType]--
|
||||
if ss.targetSelectorsCount[key][policyType] == 0 {
|
||||
if err := ss.onDestroyTargetSelector(&selector{ss.ips, spec}, policyType); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// TODO(brb) delete(...)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,10 +185,13 @@ func (ss *selectorSet) provision(user types.UID, current, desired map[string]*se
|
||||
}
|
||||
ss.users[key][user] = struct{}{}
|
||||
|
||||
if spec.dst {
|
||||
ss.dstSelectorsCount[key]++
|
||||
if ss.dstSelectorsCount[key] == 1 {
|
||||
if err := ss.onNewDstSelector(selector); err != nil {
|
||||
for _, pt := range spec.policyTypes {
|
||||
if _, found := ss.targetSelectorsCount[key]; !found {
|
||||
ss.targetSelectorsCount[key] = make(map[policyType]int)
|
||||
}
|
||||
ss.targetSelectorsCount[key][pt]++
|
||||
if ss.targetSelectorsCount[key][pt] == 1 {
|
||||
if err := ss.onNewTargetSelector(selector, pt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
func (i *Ipam) IsBuiltIn() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Functions required by ipamapi "contract" but not actually used.
|
||||
|
||||
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()
|
||||
|
||||
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
|
||||
if len(id) < 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 {
|
||||
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)
|
||||
}); err != nil {
|
||||
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
|
||||
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.
|
||||
logOnStderr(fmt.Errorf("error removing interface %q: %s", args.IfName, err))
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ func (driver *driver) CreateEndpoint(create *api.CreateEndpointRequest) (*api.Cr
|
||||
|
||||
// create veths. note we assume endpoint IDs are unique in the first 9 chars
|
||||
name, peerName := vethPair(create.EndpointID)
|
||||
if _, err := weavenet.CreateAndAttachVeth(name, peerName, weavenet.WeaveBridgeName, 0, false, nil); err != nil {
|
||||
if _, err := weavenet.CreateAndAttachVeth(name, peerName, weavenet.WeaveBridgeName, 0, false, true, nil); err != nil {
|
||||
return nil, driver.error("JoinEndpoint", "%s", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,9 @@ func (w *watcher) ContainerStarted(id string) {
|
||||
rootDir = "/host"
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,10 @@ const (
|
||||
retryPeriod = time.Second * 2
|
||||
jitterFactor = 1.0
|
||||
|
||||
// Prefix all our annotation keys with this string so they don't clash with anyone else's
|
||||
KubePeersPrefix = "kube-peers.weave.works/"
|
||||
// KubePeersAnnotationKey is the default annotation key
|
||||
KubePeersAnnotationKey = "kube-peers.weave.works/peers"
|
||||
KubePeersAnnotationKey = KubePeersPrefix + "peers"
|
||||
)
|
||||
|
||||
func (cml *configMapAnnotations) Init() error {
|
||||
@@ -130,46 +132,59 @@ func (cml *configMapAnnotations) UpdatePeerList(list peerList) error {
|
||||
return cml.UpdateAnnotation(KubePeersAnnotationKey, string(recordBytes))
|
||||
}
|
||||
|
||||
func (cml *configMapAnnotations) UpdateAnnotation(key, value string) error {
|
||||
// Clean up a string so it meets the Kubernetes requiremements for Annotation keys:
|
||||
// name part must consist of alphanumeric characters, '-', '_' or '.', and must
|
||||
// start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc')
|
||||
func cleanKey(key string) string {
|
||||
buf := []byte(key)
|
||||
for i, c := range buf {
|
||||
if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '/' {
|
||||
continue
|
||||
}
|
||||
buf[i] = '_'
|
||||
}
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
func (cml *configMapAnnotations) GetAnnotation(key string) (string, bool) {
|
||||
value, ok := cml.cm.Annotations[cleanKey(key)]
|
||||
return value, ok
|
||||
}
|
||||
|
||||
func (cml *configMapAnnotations) UpdateAnnotation(key, value string) (err error) {
|
||||
if cml.cm == nil {
|
||||
return errors.New("endpoint not initialized, call Init first")
|
||||
}
|
||||
cm := cml.cm
|
||||
cm.Annotations[key] = value
|
||||
cm, err := cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||
if err == nil {
|
||||
cml.cm = cm
|
||||
}
|
||||
// speculatively change the state, then replace with whatever comes back
|
||||
// from Update(), which will be the latest on the server whatever happened
|
||||
cml.cm.Annotations[cleanKey(key)] = value
|
||||
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||
return err
|
||||
}
|
||||
|
||||
func (cml *configMapAnnotations) RemoveAnnotation(key string) error {
|
||||
func (cml *configMapAnnotations) RemoveAnnotation(key string) (err error) {
|
||||
if cml.cm == nil {
|
||||
return errors.New("endpoint not initialized, call Init first")
|
||||
}
|
||||
cm := cml.cm
|
||||
delete(cm.Annotations, key)
|
||||
cm, err := cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||
if err == nil {
|
||||
cml.cm = cm
|
||||
}
|
||||
// speculatively change the state, then replace with whatever comes back
|
||||
// from Update(), which will be the latest on the server whatever happened
|
||||
delete(cml.cm.Annotations, cleanKey(key))
|
||||
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||
return err
|
||||
}
|
||||
|
||||
func (cml *configMapAnnotations) RemoveAnnotationsWithValue(valueToRemove string) error {
|
||||
func (cml *configMapAnnotations) RemoveAnnotationsWithValue(valueToRemove string) (err error) {
|
||||
if cml.cm == nil {
|
||||
return errors.New("endpoint not initialized, call Init first")
|
||||
}
|
||||
cm := cml.cm
|
||||
for key, value := range cm.Annotations {
|
||||
// speculatively change the state, then replace with whatever comes back
|
||||
// from Update(), which will be the latest on the server whatever happened
|
||||
for key, value := range cml.cm.Annotations {
|
||||
if value == valueToRemove {
|
||||
delete(cm.Annotations, key)
|
||||
delete(cml.cm.Annotations, key) // don't need to clean this key as it came from the map
|
||||
}
|
||||
}
|
||||
cm, err := cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||
if err == nil {
|
||||
cml.cm = cm
|
||||
}
|
||||
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -97,7 +97,10 @@ func checkIamInPeerList(cml *configMapAnnotations, c *kubernetes.Clientset, peer
|
||||
// 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 {
|
||||
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 {
|
||||
@@ -116,10 +119,14 @@ func reclaimRemovedPeers(weave *weaveapi.Client, cml *configMapAnnotations, node
|
||||
}
|
||||
// 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
|
||||
}
|
||||
common.Log.Debugln("[kube-peers] Preparing to remove disappeared peer", peer)
|
||||
okToRemove := false
|
||||
// 3. Check if there is an existing annotation with key X
|
||||
if existingAnnotation, found := cml.cm.Annotations[peer.PeerName]; found {
|
||||
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 {
|
||||
@@ -128,17 +135,20 @@ func reclaimRemovedPeers(weave *weaveapi.Client, cml *configMapAnnotations, node
|
||||
} else {
|
||||
// 5. If non-existent, write an annotation with key X and contents "my identity"
|
||||
common.Log.Debugln("[kube-peers] Noting I plan to remove ", peer.PeerName)
|
||||
if err := cml.UpdateAnnotation(peer.PeerName, myPeerName); err == nil {
|
||||
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.Infoln("[kube-peers] rmpeer of", peer.PeerName, ":", result)
|
||||
common.Log.Infof("[kube-peers] rmpeer of %s: %s", peer.PeerName, result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loopsWhenNothingChanged = 0
|
||||
cml.LoopUpdate(func() error {
|
||||
// 7aa. Remove any annotations Z* that have contents X
|
||||
if err := cml.RemoveAnnotationsWithValue(peer.PeerName); err != nil {
|
||||
@@ -146,15 +156,12 @@ func reclaimRemovedPeers(weave *weaveapi.Client, cml *configMapAnnotations, node
|
||||
}
|
||||
// 7a. Remove X from peerList
|
||||
storedPeerList.remove(peer.PeerName)
|
||||
common.Log.Infoln("[kube-peers] Removing peer ", peer.PeerName, ". Expecting to remove linked annotation next.")
|
||||
if err := cml.UpdatePeerList(*storedPeerList); err != nil {
|
||||
return err
|
||||
}
|
||||
common.Log.Infoln("[kube-peers] Removing annotation ", peer.PeerName)
|
||||
// 7b. Remove annotation with key X
|
||||
return cml.RemoveAnnotation(peer.PeerName)
|
||||
return cml.RemoveAnnotation(KubePeersPrefix + peer.PeerName)
|
||||
})
|
||||
common.Log.Debugln("[kube-peers] Finished removal of ", peer.PeerName)
|
||||
}
|
||||
// 8. If step 5 failed due to optimistic lock conflict, stop: someone else is handling X
|
||||
|
||||
@@ -166,6 +173,7 @@ func reclaimRemovedPeers(weave *weaveapi.Client, cml *configMapAnnotations, node
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
// 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
|
||||
@@ -174,14 +182,16 @@ func reclaimRemovedPeers(weave *weaveapi.Client, cml *configMapAnnotations, node
|
||||
|
||||
func main() {
|
||||
var (
|
||||
justReclaim bool
|
||||
justCheck bool
|
||||
peerName string
|
||||
nodeName string
|
||||
logLevel string
|
||||
justReclaim bool
|
||||
justCheck bool
|
||||
justSetNodeStatus bool
|
||||
peerName string
|
||||
nodeName string
|
||||
logLevel string
|
||||
)
|
||||
flag.BoolVar(&justReclaim, "reclaim", false, "reclaim IP space from dead peers")
|
||||
flag.BoolVar(&justCheck, "check-peer-new", false, "return success if peer name is not stored in annotation")
|
||||
flag.BoolVar(&justSetNodeStatus, "set-node-status", false, "set NodeNetworkUnavailable to false")
|
||||
flag.StringVar(&peerName, "peer-name", "unknown", "name of this Weave Net peer")
|
||||
flag.StringVar(&nodeName, "node-name", "unknown", "name of this Kubernetes node")
|
||||
flag.StringVar(&logLevel, "log-level", "info", "logging level (debug, info, warning, error)")
|
||||
@@ -208,6 +218,13 @@ func main() {
|
||||
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
|
||||
}
|
||||
peers, err := getKubePeers(c)
|
||||
if err != nil {
|
||||
common.Log.Fatalf("[kube-peers] Could not get peers: %v", err)
|
||||
@@ -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.Clientset, 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
|
||||
}
|
||||
@@ -50,6 +50,14 @@
|
||||
}
|
||||
],
|
||||
"env": [
|
||||
{
|
||||
"description": "Log level",
|
||||
"name": "LOG_LEVEL",
|
||||
"settable": [
|
||||
"value"
|
||||
],
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"description": "Extra args to `weaver` and `plugin`",
|
||||
"name": "EXTRA_ARGS",
|
||||
|
||||
@@ -7,6 +7,7 @@ IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
|
||||
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
||||
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
|
||||
HOST_ROOT=${HOST_ROOT:-/host}
|
||||
LOG_LEVEL=${LOG_LEVEL:-info}
|
||||
WEAVE_DIR="/host/var/lib/weave"
|
||||
|
||||
mkdir $WEAVE_DIR || true
|
||||
@@ -44,7 +45,7 @@ exec /home/weave/weaver $EXTRA_ARGS --port=6783 $(router_bridge_opts) \
|
||||
--no-dns \
|
||||
--ipalloc-range=$IPALLOC_RANGE \
|
||||
--nickname "$(hostname)" \
|
||||
--log-level=debug \
|
||||
--log-level=$LOG_LEVEL \
|
||||
--db-prefix="$WEAVE_DIR/weave" \
|
||||
--plugin-v2 \
|
||||
$(multicast_opt) \
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
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"]
|
||||
|
||||
# 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
|
||||
IPALLOC_RANGE=${IPALLOC_RANGE:-10.32.0.0/12}
|
||||
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
||||
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
|
||||
METRICS_ADDR=${WEAVE_METRICS_ADDR:-0.0.0.0:6782}
|
||||
HOST_ROOT=${HOST_ROOT:-/host}
|
||||
CONN_LIMIT=${CONN_LIMIT:-30}
|
||||
CONN_LIMIT=${CONN_LIMIT:-100}
|
||||
DB_PREFIX=${DB_PREFIX:-/weavedb/weave-net}
|
||||
|
||||
# Check if the IP range overlaps anything existing on the host
|
||||
@@ -54,12 +54,23 @@ else
|
||||
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
|
||||
WEAVE_NPC_OPTS="--expect-npc"
|
||||
if [ "${EXPECT_NPC}" = "0" ]; then
|
||||
WEAVE_NPC_OPTS=""
|
||||
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
|
||||
# when running a pod in host namespace.
|
||||
NICKNAME_ARG=""
|
||||
@@ -74,7 +85,7 @@ router_bridge_opts() {
|
||||
}
|
||||
|
||||
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
|
||||
exit 1
|
||||
fi
|
||||
@@ -99,7 +110,7 @@ fi
|
||||
if [ -f ${DB_PREFIX}data.db ]; then
|
||||
if /usr/bin/weaveutil get-db-flag "$DB_PREFIX" peer-reclaim >/dev/null ; then
|
||||
# If this peer name is not stored in the list then we were removed by another peer.
|
||||
if /home/weave/kube-peers -check-peer-new -peer-name="$PEERNAME" -log-level=debug ; then
|
||||
if /home/weave/kube-utils -check-peer-new -peer-name="$PEERNAME" -log-level=debug ; then
|
||||
# In order to avoid a CRDT clash, clean up
|
||||
echo "Peer not in list; removing persisted data" >&2
|
||||
rm -f ${DB_PREFIX}data.db
|
||||
@@ -133,10 +144,13 @@ post_start_actions() {
|
||||
/home/weave/weave --local setup-cni
|
||||
|
||||
# Attempt to run the reclaim process, but don't halt the script if it fails
|
||||
/home/weave/kube-peers -reclaim -node-name="$HOSTNAME" -peer-name="$PEERNAME" -log-level=debug || true
|
||||
/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
|
||||
/home/weave/weave --local expose $WEAVE_EXPOSE_IP
|
||||
|
||||
# Mark network as up
|
||||
/home/weave/kube-utils -set-node-status -node-name="$HOSTNAME"
|
||||
}
|
||||
|
||||
post_start_actions &
|
||||
@@ -144,11 +158,12 @@ post_start_actions &
|
||||
/home/weave/weaver $EXTRA_ARGS --port=6783 $(router_bridge_opts) \
|
||||
--name="$PEERNAME" \
|
||||
--host-root=$HOST_ROOT \
|
||||
--http-addr=$HTTP_ADDR --status-addr=$STATUS_ADDR --docker-api='' --no-dns \
|
||||
--http-addr=$HTTP_ADDR $STATUS_OPTS --docker-api='' --no-dns \
|
||||
--db-prefix="$DB_PREFIX" \
|
||||
--ipalloc-range=$IPALLOC_RANGE $NICKNAME_ARG \
|
||||
--ipalloc-init $IPALLOC_INIT \
|
||||
--conn-limit=$CONN_LIMIT \
|
||||
$WEAVE_NPC_OPTS \
|
||||
$NO_MASQ_LOCAL_OPT \
|
||||
"$@" \
|
||||
$KUBE_PEERS
|
||||
@@ -25,14 +25,6 @@ items:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
- networkpolicies
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- 'networking.k8s.io'
|
||||
resources:
|
||||
@@ -41,6 +33,13 @@ items:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ''
|
||||
resources:
|
||||
- nodes/status
|
||||
verbs:
|
||||
- patch
|
||||
- update
|
||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
@@ -101,6 +100,8 @@ items:
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
spec:
|
||||
# Wait 5 seconds to let pod connect before rolling next pod
|
||||
minReadySeconds: 5
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@@ -154,7 +155,6 @@ items:
|
||||
fieldPath: spec.nodeName
|
||||
image: 'weaveworks/weave-npc:latest'
|
||||
imagePullPolicy: Always
|
||||
#npc-args
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
- apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: weave-net
|
||||
labels:
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: weave-net
|
||||
labels:
|
||||
name: weave-net
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ''
|
||||
resources:
|
||||
- pods
|
||||
- namespaces
|
||||
- nodes
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
- networkpolicies
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- 'networking.k8s.io'
|
||||
resources:
|
||||
- networkpolicies
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ''
|
||||
resources:
|
||||
- nodes/status
|
||||
verbs:
|
||||
- patch
|
||||
- update
|
||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: weave-net
|
||||
labels:
|
||||
name: weave-net
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: weave-net
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
labels:
|
||||
name: weave-net
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ''
|
||||
resources:
|
||||
- configmaps
|
||||
resourceNames:
|
||||
- weave-net
|
||||
verbs:
|
||||
- get
|
||||
- update
|
||||
- apiGroups:
|
||||
- ''
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- create
|
||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
labels:
|
||||
name: weave-net
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: weave-net
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
- apiVersion: extensions/v1beta1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: weave-net
|
||||
labels:
|
||||
name: weave-net
|
||||
namespace: kube-system
|
||||
spec:
|
||||
# Wait 5 seconds to let pod connect before rolling next pod
|
||||
minReadySeconds: 5
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: weave-net
|
||||
spec:
|
||||
containers:
|
||||
- name: weave
|
||||
command:
|
||||
- /home/weave/launch.sh
|
||||
env:
|
||||
- name: HOSTNAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
apiVersion: v1
|
||||
fieldPath: spec.nodeName
|
||||
image: 'weaveworks/weave-kube:latest'
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
host: 127.0.0.1
|
||||
path: /status
|
||||
port: 6784
|
||||
initialDelaySeconds: 30
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: weavedb
|
||||
mountPath: /weavedb
|
||||
- name: cni-bin
|
||||
mountPath: /host/opt
|
||||
- name: cni-bin2
|
||||
mountPath: /host/home
|
||||
- name: cni-conf
|
||||
mountPath: /host/etc
|
||||
- name: dbus
|
||||
mountPath: /host/var/lib/dbus
|
||||
- name: lib-modules
|
||||
mountPath: /lib/modules
|
||||
- name: xtables-lock
|
||||
mountPath: /run/xtables.lock
|
||||
readOnly: false
|
||||
- name: weave-npc
|
||||
env:
|
||||
- name: HOSTNAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
apiVersion: v1
|
||||
fieldPath: spec.nodeName
|
||||
image: 'weaveworks/weave-npc:latest'
|
||||
imagePullPolicy: Always
|
||||
#npc-args
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: xtables-lock
|
||||
mountPath: /run/xtables.lock
|
||||
readOnly: false
|
||||
hostNetwork: true
|
||||
hostPID: true
|
||||
restartPolicy: Always
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
serviceAccountName: weave-net
|
||||
tolerations:
|
||||
- effect: NoSchedule
|
||||
operator: Exists
|
||||
volumes:
|
||||
- name: weavedb
|
||||
hostPath:
|
||||
path: /var/lib/weave
|
||||
- name: cni-bin
|
||||
hostPath:
|
||||
path: /opt
|
||||
- name: cni-bin2
|
||||
hostPath:
|
||||
path: /home
|
||||
- name: cni-conf
|
||||
hostPath:
|
||||
path: /etc
|
||||
- name: dbus
|
||||
hostPath:
|
||||
path: /var/lib/dbus
|
||||
- name: lib-modules
|
||||
hostPath:
|
||||
path: /lib/modules
|
||||
- name: xtables-lock
|
||||
hostPath:
|
||||
path: /run/xtables.lock
|
||||
type: FileOrCreate
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
@@ -1,17 +1,24 @@
|
||||
FROM ALPINE_BASEIMAGE
|
||||
|
||||
# If we're building for another architecture than amd64, the CROSS_BUILD_ placeholder is removed so e.g. CROSS_BUILD_COPY turns into COPY
|
||||
# If we're building normally, for amd64, CROSS_BUILD lines are removed
|
||||
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
||||
|
||||
LABEL maintainer "Weaveworks Inc <help@weave.works>"
|
||||
LABEL works.weave.role=system \
|
||||
# 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="Weave Net" \
|
||||
org.opencontainers.image.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||
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.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||
org.label-schema.url="https://weave.works" \
|
||||
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
||||
org.label-schema.vendor="Weaveworks"
|
||||
|
||||
# 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 \
|
||||
iptables \
|
||||
ipset \
|
||||
@@ -21,3 +28,8 @@ RUN apk add --update \
|
||||
COPY ./weave-npc /usr/bin/weave-npc
|
||||
COPY ./ulogd.conf /etc/ulogd.conf
|
||||
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}"
|
||||
+107
-20
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/spf13/cobra"
|
||||
coreapi "k8s.io/api/core/v1"
|
||||
extnapi "k8s.io/api/extensions/v1beta1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -18,8 +17,9 @@ import (
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"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/ipset"
|
||||
"github.com/weaveworks/weave/npc/metrics"
|
||||
"github.com/weaveworks/weave/npc/ulogd"
|
||||
)
|
||||
@@ -30,7 +30,7 @@ var (
|
||||
logLevel string
|
||||
allowMcast bool
|
||||
nodeName string
|
||||
legacy bool
|
||||
maxList int
|
||||
)
|
||||
|
||||
func handleError(err error) { common.CheckFatal(err) }
|
||||
@@ -52,11 +52,31 @@ func resetIPTables(ipt *iptables.IPTables) error {
|
||||
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 {
|
||||
// Remove ipsets prefixed `weave-` only
|
||||
// Remove ipsets prefixed `weave-` only.
|
||||
|
||||
sets, err := ips.List(npc.IpsetNamePrefix)
|
||||
if err != nil {
|
||||
@@ -76,6 +96,11 @@ func resetIPSets(ips ipset.Interface) error {
|
||||
}
|
||||
|
||||
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))
|
||||
if err := ips.Destroy(s); err != nil {
|
||||
common.Log.Errorf("Failed to destroy ipset '%s'", string(s))
|
||||
@@ -111,11 +136,81 @@ 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 := ips.Create(npc.LocalIpset, ipset.HashIP); err != nil {
|
||||
found, err := ipsetExist(ips, npc.LocalIpset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ipt.Append(npc.TableFilter, npc.MainChain,
|
||||
"-m", "set", "!", "--match-set", npc.LocalIpset, "dst", "-j", "ACCEPT")
|
||||
if !found {
|
||||
if err := ips.Create(npc.LocalIpset, ipset.HashIP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := ipt.Append(npc.TableFilter, npc.MainChain,
|
||||
"-m", "set", "!", "--match-set", npc.LocalIpset, "dst", "-j", "ACCEPT"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ipt.Append(npc.TableFilter, npc.EgressMarkChain,
|
||||
"-j", "MARK", "--set-xmark", npc.EgressMark); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Egress rules:
|
||||
//
|
||||
// -A WEAVE-NPC-EGRESS -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||
// -A WEAVE-NPC-EGRESS -m set ! --match-set weave-local-pods src -j RETURN
|
||||
// -A WEAVE-NPC-EGRESS -d 224.0.0.0/4 -j RETURN
|
||||
// -A WEAVE-NPC-EGRESS -m state --state NEW -j WEAVE-NPC-EGRESS-DEFAULT
|
||||
// -A WEAVE-NPC-EGRESS -m state --state NEW -m mark ! --mark 0x40000/0x40000 -j WEAVE-NPC-EGRESS-CUSTOM
|
||||
// -A WEAVE-NPC-EGRESS -m state --state NEW -m mark ! --mark 0x40000/0x40000 -j NFLOG --nflog-group 86
|
||||
// -A WEAVE-NPC-EGRESS -m mark ! --mark 0x40000/0x40000 -j DROP
|
||||
//
|
||||
// -A WEAVE-NPC-EGRESS-CUSTOM <rulespec> -j MARK --set-xmark 0x40000/0x40000
|
||||
// -A WEAVE-NPC-EGRESS-CUSTOM <rulespec> -j RETURN
|
||||
//
|
||||
// -A WEAVE-NPC-EGRESS-DEFAULT <rulespec> -j MARK --set-xmark 0x40000/0x40000
|
||||
// -A WEAVE-NPC-EGRESS-DEFAULT <rulespec> -j RETURN
|
||||
//
|
||||
// For each rule we create two (mark and return). We cannot just accept
|
||||
// a packet if it matches any rule, as a packet might need to traverse
|
||||
// the ingress npc as well which happens later in the chain (in some cases
|
||||
// we cannot detect whether packet is ingress or egress, so we need to
|
||||
// check both chains).
|
||||
|
||||
ruleSpecs := [][]string{
|
||||
{"-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"},
|
||||
{"-m", "state", "--state", "NEW", "-m", "set", "!", "--match-set", npc.LocalIpset, "src", "-j", "RETURN"},
|
||||
}
|
||||
if allowMcast {
|
||||
ruleSpecs = append(ruleSpecs, []string{"-d", "224.0.0.0/4", "-j", "RETURN"})
|
||||
}
|
||||
ruleSpecs = append(ruleSpecs, [][]string{
|
||||
{"-m", "state", "--state", "NEW", "-j", string(npc.EgressDefaultChain)},
|
||||
{"-m", "state", "--state", "NEW", "-m", "mark", "!", "--mark", npc.EgressMark, "-j", string(npc.EgressCustomChain)},
|
||||
{"-m", "state", "--state", "NEW", "-m", "mark", "!", "--mark", npc.EgressMark, "-j", "NFLOG", "--nflog-group", "86"},
|
||||
{"-m", "mark", "!", "--mark", npc.EgressMark, "-j", "DROP"},
|
||||
}...)
|
||||
if err := net.AddChainWithRules(ipt, npc.TableFilter, npc.EgressChain, ruleSpecs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -131,10 +226,6 @@ func root(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
common.Log.Infof("Starting Weaveworks NPC %s; node name %q", version, nodeName)
|
||||
|
||||
if legacy {
|
||||
common.Log.Info("Running in legacy mode (k8s pre-1.7 network policy semantics)")
|
||||
}
|
||||
|
||||
if err := metrics.Start(metricsAddr); err != nil {
|
||||
common.Log.Fatalf("Failed to start metrics: %v", err)
|
||||
}
|
||||
@@ -152,13 +243,13 @@ func root(cmd *cobra.Command, args []string) {
|
||||
ipt, err := iptables.New()
|
||||
handleError(err)
|
||||
|
||||
ips := ipset.New(common.LogLogger())
|
||||
ips := ipset.New(common.LogLogger(), maxList)
|
||||
|
||||
handleError(resetIPTables(ipt))
|
||||
handleError(resetIPSets(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{},
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
@@ -219,11 +310,7 @@ func root(cmd *cobra.Command, args []string) {
|
||||
handleError(npc.UpdateNetworkPolicy(old, new))
|
||||
},
|
||||
}
|
||||
if legacy {
|
||||
npController = makeController(client.Extensions().RESTClient(), "networkpolicies", &extnapi.NetworkPolicy{}, npHandlers)
|
||||
} else {
|
||||
npController = makeController(client.NetworkingV1().RESTClient(), "networkpolicies", &networkingv1.NetworkPolicy{}, npHandlers)
|
||||
}
|
||||
npController = makeController(client.NetworkingV1().RESTClient(), "networkpolicies", &networkingv1.NetworkPolicy{}, npHandlers)
|
||||
|
||||
go nsController.Run(wait.NeverStop)
|
||||
go podController.Run(wait.NeverStop)
|
||||
@@ -244,7 +331,7 @@ func main() {
|
||||
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "debug", "logging level (debug, info, warning, error)")
|
||||
rootCmd.PersistentFlags().BoolVar(&allowMcast, "allow-mcast", true, "allow all multicast traffic")
|
||||
rootCmd.PersistentFlags().StringVar(&nodeName, "node-name", "", "only generate rules that apply to this node")
|
||||
rootCmd.PersistentFlags().BoolVar(&legacy, "use-legacy-netpol", false, "use legacy network policies (pre k8s 1.7 vsn)")
|
||||
rootCmd.PersistentFlags().IntVar(&maxList, "max-list-size", 1024, "maximum size of ipset list (for namespaces)")
|
||||
|
||||
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
|
||||
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"]
|
||||
# Work round Docker refusing to save an empty image
|
||||
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
|
||||
|
||||
# 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"]
|
||||
|
||||
ADD ./sigproxy ./symlink /home/weave/
|
||||
@@ -7,3 +13,7 @@ ADD ./weavewait /w/w
|
||||
ADD ./weavewait_noop /w-noop/w
|
||||
ADD ./weavewait_nomcast /w-nomcast/w
|
||||
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
|
||||
|
||||
# If we're building for another architecture than amd64, the CROSS_BUILD_ placeholder is removed so e.g. CROSS_BUILD_COPY turns into COPY
|
||||
# If we're building normally, for amd64, CROSS_BUILD lines are removed
|
||||
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
||||
|
||||
LABEL maintainer "Weaveworks Inc <help@weave.works>"
|
||||
LABEL works.weave.role=system \
|
||||
# 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="Weave Net" \
|
||||
org.opencontainers.image.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||
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.description="Weave Net creates a virtual network that connects Docker containers across multiple hosts and enables their automatic discovery" \
|
||||
org.label-schema.url="https://weave.works" \
|
||||
org.label-schema.vcs-url="https://github.com/weaveworks/weave" \
|
||||
org.label-schema.vendor="Weaveworks"
|
||||
|
||||
# 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 \
|
||||
curl \
|
||||
ethtool \
|
||||
@@ -21,11 +28,16 @@ RUN apk add --update \
|
||||
util-linux \
|
||||
conntrack-tools \
|
||||
bind-tools \
|
||||
ca-certificates \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
ADD ./weave ./weaver /home/weave/
|
||||
ADD ./weaveutil /usr/bin/
|
||||
ADD weavedata.db /weavedb/
|
||||
COPY ca-certificates.crt /etc/ssl/certs/
|
||||
ENTRYPOINT ["/home/weave/weaver"]
|
||||
WORKDIR /home/weave
|
||||
|
||||
# These labels will change for every build, and should therefore be the last layer of the image:
|
||||
ARG revision
|
||||
LABEL org.opencontainers.image.revision="${revision}" \
|
||||
org.label-schema.vcs-ref="${revision}"
|
||||
File diff suppressed because it is too large.
Load diff
+7
-2
@@ -329,7 +329,7 @@ func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRout
|
||||
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) {
|
||||
funcs := template.FuncMap{
|
||||
"json": func(v interface{}) string {
|
||||
@@ -337,7 +337,12 @@ func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRout
|
||||
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 {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
|
||||
+43
-20
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/weaveworks/weave/nameserver"
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
"github.com/weaveworks/weave/net/ipset"
|
||||
"github.com/weaveworks/weave/plugin"
|
||||
weaveproxy "github.com/weaveworks/weave/proxy"
|
||||
weave "github.com/weaveworks/weave/router"
|
||||
@@ -45,12 +46,20 @@ type ipamConfig struct {
|
||||
}
|
||||
|
||||
type dnsConfig struct {
|
||||
Domain string
|
||||
ListenAddress string
|
||||
TTL int
|
||||
ClientTimeout time.Duration
|
||||
EffectiveListenAddress string
|
||||
ResolvConf string
|
||||
Domain string
|
||||
ListenAddress string
|
||||
TTL int
|
||||
ClientTimeout time.Duration
|
||||
ResolvConf string
|
||||
}
|
||||
|
||||
// return the address part of the DNS listen address, without ":53" on the end
|
||||
func (c dnsConfig) addressOnly() string {
|
||||
addr, _, err := net.SplitHostPort(c.ListenAddress)
|
||||
if err != nil {
|
||||
Log.Fatalf("Error when parsing DNS listen address %q: %s", c.ListenAddress, err)
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
func (c *ipamConfig) Enabled() bool {
|
||||
@@ -147,6 +156,7 @@ func main() {
|
||||
noDiscovery bool
|
||||
httpAddr string
|
||||
statusAddr string
|
||||
metricsAddr string
|
||||
ipamConfig ipamConfig
|
||||
dockerAPI string
|
||||
peers []string
|
||||
@@ -178,12 +188,13 @@ func main() {
|
||||
mflag.StringVar(&logLevel, []string{"-log-level"}, "info", "logging level (debug, info, warning, error)")
|
||||
mflag.BoolVar(&pktdebug, []string{"-pkt-debug"}, false, "enable per-packet debug logging")
|
||||
mflag.StringVar(&prof, []string{"-profile"}, "", "enable profiling and write profiles to given path")
|
||||
mflag.IntVar(&config.ConnLimit, []string{"-conn-limit"}, 30, "connection limit (0 for unlimited)")
|
||||
mflag.IntVar(&config.ConnLimit, []string{"-conn-limit"}, 100, "connection limit (0 for unlimited)")
|
||||
mflag.BoolVar(&noDiscovery, []string{"-no-discovery"}, false, "disable peer discovery")
|
||||
mflag.IntVar(&bufSzMB, []string{"-bufsz"}, 8, "capture buffer size in MB")
|
||||
mflag.IntVar(&bridgeConfig.MTU, []string{"-mtu"}, 0, "MTU size")
|
||||
mflag.StringVar(&httpAddr, []string{"-http-addr"}, "", "address to bind HTTP interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||
mflag.StringVar(&statusAddr, []string{"-status-addr"}, "", "address to bind status+metrics interface to (disabled if blank, absolute path indicates unix domain socket)")
|
||||
mflag.StringVar(&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.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")
|
||||
@@ -193,10 +204,10 @@ func main() {
|
||||
mflag.StringVar(&dnsConfig.ListenAddress, []string{"-dns-listen-address"}, nameserver.DefaultListenAddress, "address to listen on for DNS requests")
|
||||
mflag.IntVar(&dnsConfig.TTL, []string{"-dns-ttl"}, nameserver.DefaultTTL, "TTL for DNS request from our domain")
|
||||
mflag.DurationVar(&dnsConfig.ClientTimeout, []string{"-dns-fallback-timeout"}, nameserver.DefaultClientTimeout, "timeout for fallback DNS requests")
|
||||
mflag.StringVar(&dnsConfig.EffectiveListenAddress, []string{"-dns-effective-listen-address"}, "", "address DNS will actually be listening, after Docker port mapping")
|
||||
mflag.StringVar(&dnsConfig.ResolvConf, []string{"-resolv-conf"}, "", "path to resolver configuration for fallback DNS lookups")
|
||||
mflag.StringVar(&bridgeConfig.DatapathName, []string{"-datapath"}, "", "ODP datapath name")
|
||||
mflag.BoolVar(&bridgeConfig.NoFastdp, []string{"-no-fastdp"}, false, "Disable Fast Datapath")
|
||||
mflag.BoolVar(&bridgeConfig.NoBridgedFastdp, []string{"-no-bridged-fastdp"}, false, "Disable Bridged Fast Datapath")
|
||||
mflag.StringVar(&trustedSubnetStr, []string{"-trusted-subnets"}, "", "comma-separated list of trusted subnets in CIDR notation")
|
||||
mflag.StringVar(&dbPrefix, []string{"-db-prefix"}, "/weavedb/weave", "pathname/prefix of filename to store data")
|
||||
mflag.StringVar(&procPath, []string{"-proc-path"}, "/proc", "path to reach host /proc filesystem")
|
||||
@@ -211,6 +222,7 @@ func main() {
|
||||
mflag.BoolVar(&pluginConfig.EnableV2Multicast, []string{"-plugin-v2-multicast"}, false, "enable multicast for Docker plugin (v2)")
|
||||
mflag.StringVar(&pluginConfig.Socket, []string{"-plugin-socket"}, "/run/docker/plugins/weave.sock", "plugin socket on which to listen")
|
||||
mflag.StringVar(&pluginConfig.MeshSocket, []string{"-plugin-mesh-socket"}, "/run/docker/plugins/weavemesh.sock", "plugin socket on which to listen in mesh mode")
|
||||
mflag.BoolVar(&bridgeConfig.NoMasqLocal, []string{"-no-masq-local"}, false, "do not SNAT external traffic sent to containers running on this node")
|
||||
|
||||
proxyConfig := newProxyConfig()
|
||||
|
||||
@@ -263,6 +275,8 @@ func main() {
|
||||
var proxy *weaveproxy.Proxy
|
||||
var err error
|
||||
if proxyConfig.Enabled {
|
||||
proxyConfig.DNSListenAddress = dnsConfig.addressOnly()
|
||||
proxyConfig.DNSDomain = dnsConfig.Domain
|
||||
if noDNS {
|
||||
proxyConfig.WithoutDNS = true
|
||||
}
|
||||
@@ -293,7 +307,8 @@ func main() {
|
||||
|
||||
bridgeConfig.Mac = name.String()
|
||||
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)
|
||||
Log.Println("Bridge type is", bridgeType)
|
||||
|
||||
@@ -324,6 +339,9 @@ func main() {
|
||||
if bridgeConfig.AWSVPC && !ipamConfig.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)
|
||||
checkFatal(err)
|
||||
@@ -383,17 +401,19 @@ func main() {
|
||||
var (
|
||||
allocator *ipam.Allocator
|
||||
defaultSubnet address.CIDR
|
||||
trackerName string
|
||||
)
|
||||
if ipamConfig.Enabled() {
|
||||
var t tracker.LocalRangeTracker
|
||||
if bridgeConfig.AWSVPC {
|
||||
Log.Infoln("Creating AWSVPC LocalRangeTracker")
|
||||
t, err = tracker.NewAWSVPCTracker(bridgeConfig.WeaveBridgeName)
|
||||
if err != nil {
|
||||
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)
|
||||
@@ -441,7 +461,7 @@ func main() {
|
||||
if httpAddr != "" {
|
||||
muxRouter := mux.NewRouter()
|
||||
if allocator != nil {
|
||||
allocator.HandleHTTP(muxRouter, defaultSubnet, trackerName, dockerCli)
|
||||
allocator.HandleHTTP(muxRouter, defaultSubnet, dockerCli)
|
||||
}
|
||||
if ns != nil {
|
||||
ns.HandleHTTP(muxRouter, dockerCli)
|
||||
@@ -464,10 +484,17 @@ func main() {
|
||||
muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver))
|
||||
statusMux := http.NewServeMux()
|
||||
statusMux.Handle("/", muxRouter)
|
||||
Log.Println("Listening for metrics requests on", statusAddr)
|
||||
Log.Println("Listening for status+metrics requests on", statusAddr)
|
||||
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 {
|
||||
go plugin.Start(httpAddr, dockerCli, waitReady.Add())
|
||||
}
|
||||
@@ -635,17 +662,13 @@ func createDNSServer(config dnsConfig, router *mesh.Router, isKnownPeer func(mes
|
||||
gossip, err := router.NewGossip("nameserver", ns)
|
||||
checkFatal(err)
|
||||
ns.SetGossip(gossip)
|
||||
upstream := nameserver.NewUpstream(config.ResolvConf, config.EffectiveListenAddress)
|
||||
upstream := nameserver.NewUpstream(config.ResolvConf, config.addressOnly())
|
||||
dnsserver, err := nameserver.NewDNSServer(ns, config.Domain, config.ListenAddress,
|
||||
upstream, uint32(config.TTL), config.ClientTimeout)
|
||||
if err != nil {
|
||||
Log.Fatal("Unable to start dns server: ", err)
|
||||
}
|
||||
listenAddr := config.ListenAddress
|
||||
if config.EffectiveListenAddress != "" {
|
||||
listenAddr = config.EffectiveListenAddress
|
||||
}
|
||||
Log.Println("Listening for DNS queries on", listenAddr)
|
||||
Log.Println("Listening for DNS queries on", config.ListenAddress)
|
||||
return ns, dnsserver
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
@@ -15,8 +14,8 @@ import (
|
||||
)
|
||||
|
||||
func attach(args []string) error {
|
||||
if len(args) < 4 {
|
||||
cmdUsage("attach-container", "[--no-multicast-route] [--keep-tx-on] [--hairpin-mode=true|false] <container-id> <bridge-name> <mtu> <cidr>...")
|
||||
if len(args) < 3 {
|
||||
cmdUsage("attach-container", "[--no-multicast-route] [--keep-tx-on] [--hairpin-mode=true|false] <container-id> <bridge-name> <cidr>...")
|
||||
}
|
||||
|
||||
keepTXOn := false
|
||||
@@ -52,16 +51,12 @@ func attach(args []string) error {
|
||||
} else if nsHost.Equal(nsContainer) {
|
||||
return fmt.Errorf("Container is running in the host network namespace, and therefore cannot be\nconnected to weave - perhaps the container was started with --net=host")
|
||||
}
|
||||
mtu, err := strconv.Atoi(args[2])
|
||||
if err != nil && args[3] != "" {
|
||||
return fmt.Errorf("unable to parse mtu %q: %s", args[2], err)
|
||||
}
|
||||
cidrs, err := parseCIDRs(args[3:])
|
||||
cidrs, err := parseCIDRs(args[2:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = weavenet.AttachContainer(weavenet.NSPathByPid(pid), fmt.Sprint(pid), weavenet.VethName, args[1], mtu, withMulticastRoute, cidrs, keepTXOn, hairpinMode)
|
||||
err = weavenet.AttachContainer(weavenet.NSPathByPid(pid), fmt.Sprint(pid), weavenet.VethName, args[1], 0, withMulticastRoute, cidrs, keepTXOn, hairpinMode)
|
||||
// If we detected an error but the container has died, tell the user that instead.
|
||||
if err != nil && !processExists(pid) {
|
||||
err = fmt.Errorf("Container %s died", args[0])
|
||||
|
||||
@@ -2,9 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/weaveworks/weave/common"
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
|
||||
@@ -22,36 +20,3 @@ func detectBridgeType(args []string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createBridge(args []string) error {
|
||||
if len(args) != 10 {
|
||||
cmdUsage("create-bridge", "<docker-bridge-name> <weave-bridge-name> <datapath-name> <mtu> <port> <mac> <no-fastdp> <no-bridged-fastdp> <proc-path> <expect-npc>")
|
||||
}
|
||||
|
||||
mtu, err := strconv.Atoi(args[3])
|
||||
if err != nil && args[3] != "" {
|
||||
return err
|
||||
}
|
||||
port, err := strconv.Atoi(args[4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config := weavenet.BridgeConfig{
|
||||
DockerBridgeName: args[0],
|
||||
WeaveBridgeName: args[1],
|
||||
DatapathName: args[2],
|
||||
MTU: mtu,
|
||||
Port: port,
|
||||
Mac: args[5],
|
||||
NoFastdp: args[6] != "",
|
||||
NoBridgedFastdp: args[7] != "",
|
||||
NPC: args[9] == "--expect-npc",
|
||||
}
|
||||
procPath := args[8]
|
||||
bridgeType, err := weavenet.EnsureBridge(procPath, &config, common.Log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(bridgeType.String())
|
||||
return nil
|
||||
}
|
||||
@@ -4,10 +4,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
weavenet "github.com/weaveworks/weave/net"
|
||||
)
|
||||
|
||||
var commands map[string]func([]string) error
|
||||
@@ -17,7 +14,6 @@ func init() {
|
||||
"help": help,
|
||||
"netcheck": netcheck,
|
||||
"docker-tls-args": dockerTLSArgs,
|
||||
"create-bridge": createBridge,
|
||||
"detect-bridge-type": detectBridgeType,
|
||||
"create-datapath": createDatapath,
|
||||
"delete-datapath": deleteDatapath,
|
||||
@@ -37,12 +33,6 @@ func init() {
|
||||
"create-volume-container": createVolumeContainer,
|
||||
"attach-container": attach,
|
||||
"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-ipam": cniIPAM,
|
||||
"bridge-ip": bridgeIP,
|
||||
@@ -56,14 +46,6 @@ func init() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
// force re-exec of this binary
|
||||
selfPath, err := filepath.EvalSymlinks("/proc/self/exe")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
weavenet.WeaveUtilCmd = selfPath
|
||||
|
||||
// If no args passed, act as CNI plugin based on executable name
|
||||
switch {
|
||||
case len(os.Args) == 1 && strings.HasSuffix(os.Args[0], "weave-ipam"):
|
||||
|
||||
@@ -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"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/client"
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
@@ -58,7 +59,7 @@ func isDockerPluginEnabled(args []string) error {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
plugins, err := c.PluginList(ctx)
|
||||
plugins, err := c.PluginList(ctx, filters.Args{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -68,11 +68,11 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" {
|
||||
if err := i.setHostname(container, hostname, dnsDomain); err != nil {
|
||||
if !i.proxy.WithoutDNS {
|
||||
if err := i.setHostname(container, hostname, i.proxy.DNSDomain); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := i.proxy.setWeaveDNS(hostConfig, hostname, dnsDomain); err != nil {
|
||||
if err := i.proxy.setWeaveDNS(hostConfig, hostname, i.proxy.DNSDomain); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
+3
-19
@@ -61,6 +61,8 @@ type Config struct {
|
||||
NoRewriteHosts bool
|
||||
TLSConfig TLSConfig
|
||||
WithoutDNS bool
|
||||
DNSListenAddress string
|
||||
DNSDomain string
|
||||
NoMulticastRoute bool
|
||||
KeepTXOn bool
|
||||
DockerBridge string
|
||||
@@ -78,7 +80,6 @@ type Proxy struct {
|
||||
Config
|
||||
client *weavedocker.Client
|
||||
weave *weaveapi.Client
|
||||
dockerBridgeIP string
|
||||
hostnameMatchRegexp *regexp.Regexp
|
||||
weaveWaitVolume string
|
||||
weaveWaitNoopVolume string
|
||||
@@ -158,15 +159,6 @@ func NewProxy(c Config) (*Proxy, error) {
|
||||
|
||||
Log.Info(p.client.Info())
|
||||
|
||||
if !p.WithoutDNS {
|
||||
ip, err := weavenet.FindBridgeIP(c.DockerBridge, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.dockerBridgeIP = ip.String()
|
||||
Log.Infof("Using docker bridge IP for DNS: %v", p.dockerBridgeIP)
|
||||
}
|
||||
|
||||
p.hostnameMatchRegexp, err = regexp.Compile(c.HostnameMatch)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Incorrect hostname match '%s': %s", c.HostnameMatch, err.Error())
|
||||
@@ -595,7 +587,7 @@ func (proxy *Proxy) setWeaveDNS(hostConfig jsonObject, hostname, dnsDomain strin
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hostConfig["Dns"] = append(dns, proxy.dockerBridgeIP)
|
||||
hostConfig["Dns"] = append(dns, proxy.DNSListenAddress)
|
||||
|
||||
dnsSearch, err := hostConfig.StringArray("DnsSearch")
|
||||
if err != nil {
|
||||
@@ -612,14 +604,6 @@ func (proxy *Proxy) setWeaveDNS(hostConfig jsonObject, hostname, dnsDomain strin
|
||||
return nil
|
||||
}
|
||||
|
||||
func (proxy *Proxy) getDNSDomain() string {
|
||||
if proxy.WithoutDNS {
|
||||
return ""
|
||||
}
|
||||
domain, _ := proxy.weave.DNSDomain()
|
||||
return domain
|
||||
}
|
||||
|
||||
func (proxy *Proxy) updateContainerNetworkSettings(container jsonObject) error {
|
||||
containerID, err := container.String("Id")
|
||||
if err != nil {
|
||||
|
||||
@@ -50,8 +50,8 @@ func (i *startContainerInterceptor) InterceptRequest(r *http.Request) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" {
|
||||
if err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, dnsDomain); err != nil {
|
||||
if !i.proxy.WithoutDNS {
|
||||
if err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, i.proxy.DNSDomain); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ func NewNetworkRouter(config mesh.Config, networkConfig NetworkConfig, bridgeCon
|
||||
router.Routes.OnChange(overlay.InvalidateRoutes)
|
||||
router.Macs = NewMacCache(macMaxAge,
|
||||
func(mac net.HardwareAddr, peer *mesh.Peer) {
|
||||
log.Println("Expired MAC", mac, "at", peer)
|
||||
log.Debugln("Expired MAC", mac, "at", peer)
|
||||
})
|
||||
router.Peers.OnGC(func(peer *mesh.Peer) { router.Macs.Delete(peer) })
|
||||
return router, nil
|
||||
@@ -92,7 +92,7 @@ func (router *NetworkRouter) handleCapturedPacket(key PacketKey) FlowOp {
|
||||
|
||||
switch newSrcMac, conflictPeer := router.Macs.Add(srcMac, router.Ourself.Peer); {
|
||||
case newSrcMac:
|
||||
log.Println("Discovered local MAC", srcMac)
|
||||
log.Debugln("Discovered local MAC", srcMac)
|
||||
case conflictPeer != nil:
|
||||
// The MAC cache has an entry for the source MAC
|
||||
// associated with another peer. This probably means
|
||||
|
||||
+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
|
||||
(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
|
||||
principle it shouldn't matter which Linux distribution you build
|
||||
under. But naturally, Docker is a prerequisite (version 1.6.0 or
|
||||
later).
|
||||
|
||||
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
|
||||
a Vagrant VM.
|
||||
|
||||
## <a name="ubuntu"></a>Building directly on your machine
|
||||
|
||||
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):
|
||||
|
||||
```
|
||||
$ WEAVE=github.com/weaveworks/weave
|
||||
$ git clone https://$WEAVE $GOPATH/src/$WEAVE
|
||||
$ cd $GOPATH/src/$WEAVE
|
||||
$ git submodule update --init
|
||||
```
|
||||
|
||||
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
|
||||
- package them into two Docker images (`weaveworks/weave`,
|
||||
`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
|
||||
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
|
||||
$ 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
|
||||
* go tools installed
|
||||
* weave dependencies installed
|
||||
* $GOPATH set to ~
|
||||
* Docker installed
|
||||
* Go tools installed
|
||||
* Weave's dependencies installed
|
||||
* `$GOPATH` set to `~`
|
||||
* 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
|
||||
@@ -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.
|
||||
|
||||
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
|
||||
@@ -94,7 +96,7 @@ vm$ cd src/github.com/weaveworks/weave
|
||||
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
|
||||
@@ -104,7 +106,7 @@ vm$ sudo docker ps
|
||||
and so on.
|
||||
|
||||
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'
|
||||
@@ -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
|
||||
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>
|
||||
|
||||
+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).
|
||||
|
||||
|
||||
<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>
|
||||
**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,
|
||||
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
|
||||
from another host.
|
||||
|
||||
|
||||
@@ -7,19 +7,26 @@ search_type: Documentation
|
||||
The following topics are discussed:
|
||||
|
||||
* [Installation](#install)
|
||||
* [Upgrading Kubernetes to version 1.6](#kube-1.6-upgrade)
|
||||
* [Upgrading the Daemon Sets](#daemon-sets)
|
||||
* [CPU and Memory Requirements](#resources)
|
||||
* [Pod Eviction](#eviction)
|
||||
* [Network Policy Controller](#npc)
|
||||
* [Upgrading Kubernetes to version 1.6](#kube-1.6-upgrade)
|
||||
* [Upgrading the Daemon Sets](#daemon-sets)
|
||||
* [CPU and Memory Requirements](#resources)
|
||||
* [Pod Eviction](#eviction)
|
||||
* [Features](#features)
|
||||
* [Pod Network](#pod-network)
|
||||
* [Network Policy](#npc)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
* [Troubleshooting Blocked Connections](#blocked-connections)
|
||||
* [Things to watch out for](#key-points)
|
||||
* [Reading the logs](#logs)
|
||||
* [Troubleshooting Blocked Connections](#blocked-connections)
|
||||
* [Things to watch out for](#key-points)
|
||||
* [Changing Configuration Options](#configuration-options)
|
||||
|
||||
|
||||
## <a name="install"></a> Installation
|
||||
|
||||
*Before installing Weave Net, you should make sure the following ports are not
|
||||
blocked by your firewall: TCP 6783 and UDP 6783/6784.
|
||||
For more details, see the [FAQ](/site/faq.md#ports).*
|
||||
|
||||
Weave Net can be installed onto your CNI-enabled Kubernetes cluster
|
||||
with a single command:
|
||||
|
||||
@@ -147,16 +154,33 @@ mypod-09vkd 0/1 Evicted 0 1h <none> node-1
|
||||
If you see this in your cluster, consider some of the above steps to
|
||||
reduce disruption.
|
||||
|
||||
## <a name="npc"></a>Network Policy Controller
|
||||
## <a name="features"></a>Features
|
||||
|
||||
The addon also supports the [Kubernetes policy
|
||||
API](http://kubernetes.io/docs/user-guide/networkpolicies/) so that
|
||||
you can securely isolate pods from each other based on namespaces and
|
||||
### <a name="pod-network"></a>Pod Network
|
||||
|
||||
Weave Net provides a network to connect all pods together,
|
||||
implementing the [Kubernetes
|
||||
model](https://kubernetes.io/docs/concepts/cluster-administration/networking/#kubernetes-model).
|
||||
|
||||
Kubernetes uses the _Container Network Interface_
|
||||
([CNI](https://github.com/containernetworking/cni)) to join pods onto Weave Net.
|
||||
|
||||
Kubernetes implements many network features itself on top of the pod
|
||||
network. This includes
|
||||
[Services](https://kubernetes.io/docs/concepts/services-networking/service/),
|
||||
[Service Discovery via DNS](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/)
|
||||
and [Ingress into the cluster](https://kubernetes.io/docs/concepts/services-networking/ingress/).
|
||||
WeaveDNS is disabled when using the Kubernetes addon.
|
||||
|
||||
### <a name="npc"></a>Network Policy
|
||||
|
||||
[Kubernetes Network Policies](https://kubernetes.io/docs/concepts/services-networking/network-policies/) let
|
||||
you securely isolate pods from each other based on namespaces and
|
||||
labels. For more information on configuring network policies in
|
||||
Kubernetes see the
|
||||
[walkthrough](http://kubernetes.io/docs/getting-started-guides/network-policy/walkthrough/)
|
||||
[walkthrough](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy/)
|
||||
and the [NetworkPolicy API object
|
||||
definition](http://kubernetes.io/docs/api-reference/extensions/v1beta1/definitions/#_v1beta1_networkpolicy).
|
||||
definition](https://v1-7.docs.kubernetes.io/docs/api-reference/v1.7/#networkpolicy-v1-networking)
|
||||
|
||||
**Note:** as of version 1.9 of Weave Net, the Network Policy
|
||||
Controller allows all multicast traffic. Since a single multicast
|
||||
@@ -167,7 +191,46 @@ definition](http://kubernetes.io/docs/api-reference/extensions/v1beta1/definitio
|
||||
|
||||
## <a name="troubleshooting"></a> Troubleshooting
|
||||
|
||||
The status of Weave Net can be checked by running its CLI commands. This can be done in various ways:
|
||||
The first thing to check is whether Weave Net is up and
|
||||
running. The `kubectl apply` command you used to install it only
|
||||
_requests_ that it be downloaded and started; if anything goes wrong
|
||||
at startup, those details will only be visible in the [logs](#logs) of
|
||||
the container(s).
|
||||
|
||||
To check what is running:
|
||||
|
||||
```
|
||||
$ kubectl get pods -n kube-system -l name=weave-net
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
weave-net-1jkl6 2/2 Running 0 1d
|
||||
weave-net-bskbv 2/2 Running 0 1d
|
||||
weave-net-m4x1b 2/2 Running 0 1d
|
||||
```
|
||||
|
||||
You should see one line for each node in your cluster; each line
|
||||
should have STATUS "Running", and READY should be 2 out of 2. If you
|
||||
see a STATUS like "Error" or "CrashLoopBackoff", look in the logs of
|
||||
the container with that status.
|
||||
|
||||
### <a name="logs"></a> Reading the logs
|
||||
|
||||
Pick one of the pods from the list output by `kubectl get pods` and
|
||||
ask for the logs like this:
|
||||
|
||||
```
|
||||
$ kubectl logs -n kube-system weave-net-1jkl6 weave
|
||||
```
|
||||
|
||||
For easier viewing, pipe the output into a file , especially if
|
||||
it is long.
|
||||
|
||||
Many Kubernetes network issues occur at a higher level than Weave Net.
|
||||
The [Kubernetes Service Debugging Guide]
|
||||
(https://kubernetes.io/docs/tasks/debug-application-cluster/debug-service/)
|
||||
has a detailed step-by-step guide.
|
||||
|
||||
Once it is up and running, the status of Weave Net can be checked by
|
||||
running its CLI commands. This can be done in various ways:
|
||||
|
||||
1\. [Install the `weave` script](/site/installing-weave.md) and run:
|
||||
|
||||
@@ -278,7 +341,15 @@ UDP connection from 10.32.0.7:56648 to 10.32.0.11:80 blocked by Weave NPC.
|
||||
impossible to correctly enforce network policies that restrict which
|
||||
pods can talk.
|
||||
- If you do set the `--cluster-cidr` option on kube-proxy, make sure
|
||||
it matches the `IPALLOC_RANGE` given to Weave Net (see below)
|
||||
it matches the `IPALLOC_RANGE` given to Weave Net (see below).
|
||||
- IP forwarding must be enabled on each node, in order for pods to
|
||||
access Kubernetes services or other IP addresses on another
|
||||
network. Check this with `sysctl net.ipv4.ip_forward`; the result
|
||||
should be `1`. (Be aware that there can be security implications of
|
||||
enabling IP forwarding).
|
||||
- Weave Net can be run on minikube v0.28 or later with the default CNI config shipped with minikube
|
||||
being disabled. See [#3124](https://github.com/weaveworks/weave/issues/3124#issuecomment-397820940)
|
||||
for more details.
|
||||
|
||||
## <a name="configuration-options"></a> Changing Configuration Options
|
||||
|
||||
@@ -287,11 +358,11 @@ UDP connection from 10.32.0.7:56648 to 10.32.0.11:80 blocked by Weave NPC.
|
||||
You can customise the YAML you get from `cloud.weave.works` by passing some of Weave Net's options, arguments and environment variables as query parameters:
|
||||
|
||||
- `version`: Weave Net's version. Default: `latest`, i.e. latest release. *N.B.*: This only changes the specified version inside the generated YAML file, it does not ensure that the rest of the YAML is compatible with that version. To freeze the YAML version save a copy of the YAML file from the [release page](https://github.com/weaveworks/weave/releases) and use that copy instead of downloading it each time from `cloud.weave.works`.
|
||||
- `password-secret`: name of the Kubernetes secret containing your password.
|
||||
- `password-secret`: name of the Kubernetes secret containing your password. *N.B*: The Kubernetes secret name must correspond to a name of a file containing your password.
|
||||
Example:
|
||||
|
||||
$ echo "s3cr3tp4ssw0rd" > /var/lib/weave/weave-passwd.txt
|
||||
$ kubectl create secret -n kube-system generic weave-passwd --from-file=/var/lib/weave/weave-passwd.txt
|
||||
$ echo "s3cr3tp4ssw0rd" > /var/lib/weave/weave-passwd
|
||||
$ kubectl create secret -n kube-system generic weave-passwd --from-file=/var/lib/weave/weave-passwd
|
||||
$ kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')&password-secret=weave-passwd"
|
||||
|
||||
- `known-peers`: comma-separated list of hosts. Default: empty.
|
||||
@@ -304,6 +375,8 @@ The list of variables you can set is:
|
||||
|
||||
* `CHECKPOINT_DISABLE` - if set to 1, disable checking for new Weave Net
|
||||
versions (default is blank, i.e. check is enabled)
|
||||
* `CONN_LIMIT` - soft limit on the number of connections between
|
||||
peers. Defaults to 30.
|
||||
* `HAIRPIN_MODE` - Weave Net defaults to enabling hairpin on the bridge side of
|
||||
the `veth` pair for containers attached. If you need to disable hairpin, e.g. your
|
||||
kernel is one of those that can panic if hairpin is enabled, then you can disable it
|
||||
@@ -319,11 +392,18 @@ The list of variables you can set is:
|
||||
* `WEAVE_EXPOSE_IP` - set the IP address used as a gateway from the
|
||||
Weave network to the host network - this is useful if you are
|
||||
configuring the addon as a static pod.
|
||||
* `WEAVE_METRICS_ADDR` - address and port that the Weave Net
|
||||
daemon will serve Prometheus-style metrics on (defaults to 0.0.0.0:6782)
|
||||
* `WEAVE_STATUS_ADDR` - address and port that the Weave Net
|
||||
daemon will serve status requests on (defaults to disabled)
|
||||
* `WEAVE_MTU` - Weave Net defaults to 1376 bytes, but you can set a
|
||||
smaller size if your underlying network has a tighter limit, or set
|
||||
a larger size for better performance if your network supports jumbo
|
||||
frames - see [here](/site/tasks/manage/fastdp.md#mtu) for more
|
||||
details.
|
||||
* `NO_MASQ_LOCAL` - set to 1 to preserve the client source IP address when
|
||||
accessing Service annotated with `service.spec.externalTrafficPolicy=Local`.
|
||||
The feature works only with Weave IPAM (default).
|
||||
|
||||
Example:
|
||||
```
|
||||
|
||||
@@ -50,7 +50,7 @@ peers and their reachability can be obtained with
|
||||
|
||||
```
|
||||
$ weave status ipam
|
||||
00:00:00:00:00:01(one) 349526 IPs (33.3% of total)
|
||||
00:00:00:00:00:01(one) 349526 IPs (33.3% of total) (2 active)
|
||||
00:00:00:00:00:02(two) 349525 IPs (33.3% of total)
|
||||
00:00:00:00:00:03(three) 349525 IPs (33.3% of total) - unreachable!
|
||||
```
|
||||
@@ -59,9 +59,26 @@ Columns are as follows:
|
||||
|
||||
* Peer Name and Nickname
|
||||
* Absolute quantity/percentage of allocation range managed by peer
|
||||
* Active - number of IP addresses in use on that peer - this is only
|
||||
available for the peer where you run the status command.
|
||||
* Indication of unreachability. This means that the peer is not
|
||||
visible (directly or indirectly) to the peer on which `weave status
|
||||
ipam` was run; whilst this could be a transient condition due to a
|
||||
partition, it may be because the peer has failed and needs to be
|
||||
removed administratively - see [Starting, Stopping and Removing
|
||||
Peers](/site/tasks/ipam/stop-remove-peers-ipam.md) for more details.
|
||||
|
||||
|
||||
### <a name="seeded-different-peers"></a>Seeded by Different Peers
|
||||
|
||||
If you see the message: `IP allocation was seeded by different peers`,
|
||||
this means that some Weave Net peers were initialized into one cluster
|
||||
and some into another cluster; Weave Net cannot operate in this state.
|
||||
|
||||
To recover, you need to eliminate the IPAM data from the affected
|
||||
nodes and restart. If you installed via the Kubernetes Addon, this
|
||||
data will be in a file under `/var/lib/weave` on the node - delete
|
||||
this file and restart the node.
|
||||
|
||||
For other installations, run `weave reset` and restart. Any existing
|
||||
connections to containers will be lost.
|
||||
@@ -10,9 +10,6 @@ controller](/site/kubernetes/kube-addon.md#npc).
|
||||
|
||||
### Router Metrics
|
||||
|
||||
The endpoint address is `localhost:6782`; the following metrics are
|
||||
exposed:
|
||||
|
||||
* `weave_connections` - Number of peer-to-peer connections.
|
||||
* `weave_connection_terminations_total` - Number of peer-to-peer
|
||||
connections terminated.
|
||||
@@ -25,27 +22,35 @@ exposed:
|
||||
* `weave_ipam_pending_allocates` - Number of pending allocates.
|
||||
* `weave_ipam_pending_claims` - Number of pending claims.
|
||||
|
||||
#### Publish Router Metrics Endpoint
|
||||
|
||||
By default, when started via `weave launch`, weave listens on its local
|
||||
interface to serve metrics. To publish your metrics throughout your cluster,
|
||||
e.g. if your prometheus server is installed on a different host machine,
|
||||
you need to set `WEAVE_STATUS_ADDR` to your corresponding IP and port.
|
||||
Default port is 6782.
|
||||
|
||||
`WEAVE_STATUS_ADDR=X.X.X.X:PORT`
|
||||
|
||||
You can set `WEAVE_STATUS_ADDR=0.0.0.0:6782` to listen on all interfaces,
|
||||
but be aware, this may expose your metrics to the public internet.
|
||||
|
||||
### Kubernetes Network Policy Controller Metrics
|
||||
|
||||
The endpoint address is `localhost:6781`; the following metric is
|
||||
The following metric is
|
||||
exposed:
|
||||
|
||||
* `weavenpc_blocked_connections_total` - Connection attempts blocked
|
||||
by policy controller.
|
||||
|
||||
### Metrics Endpoint Addresses
|
||||
|
||||
When installed as a Kubernetes Addon, the router listens for metrics
|
||||
requests on 0.0.0.0:6782 and the Network Policy Controller listens on
|
||||
0.0.0.0:6781. No other requests are served on these endpoints.
|
||||
|
||||
>Note: If your Kubernetes hosts are exposed to the public internet
|
||||
then these metrics endpoints will also be exposed.
|
||||
|
||||
When started via `weave launch`, by default weave listens on its local
|
||||
interface to serve metrics and other read-only status requests. To
|
||||
publish your metrics throughout your cluster, you can set
|
||||
`WEAVE_STATUS_ADDR`:
|
||||
|
||||
`WEAVE_STATUS_ADDR=X.X.X.X:PORT`
|
||||
|
||||
Set it to an empty string to disable.
|
||||
|
||||
You can also pass the parameter `--metrics-addr=X.X.X.X:PORT` to
|
||||
`weave launch` to specify an address to listen for metrics only.
|
||||
|
||||
# Static Configuration for Weave Net
|
||||
|
||||
The following YAML fragment can be used in your Prometheus configuration to
|
||||
|
||||
@@ -43,6 +43,10 @@ To disable an application container's use of weaveDNS, add the
|
||||
`--without-dns` option to `weave launch`. To
|
||||
disable weaveDNS itself, launch weave with the `--no-dns` option.
|
||||
|
||||
> **Note** WeaveDNS is not part of the Weave Net Kubernetes add-on.
|
||||
Kubernetes has its own DNS service, integrated with Kubernetes
|
||||
Services, and WeaveDNS does not duplicate that functionality.
|
||||
|
||||
**See Also**
|
||||
|
||||
* [Integrating Docker via the API Proxy](/site/tasks/weave-docker-api/weave-docker-api.md)
|
||||
|
||||
@@ -157,6 +157,9 @@ The columns are as follows:
|
||||
the encryption mode, data transport method, remote peer name and
|
||||
nickname for pending and established connections, mtu if known
|
||||
|
||||
Specific error messages:
|
||||
* `IP allocation was seeded by different peers` - [more details here](/site/ipam/troubleshooting-ipam.md#seeded-different-peers)
|
||||
|
||||
### <a name="weave-status-peers"></a>List Peers
|
||||
|
||||
Detailed information on peers can be obtained with `weave status
|
||||
@@ -329,5 +332,6 @@ and the container image versions as git hashes.
|
||||
|
||||
**See Also**
|
||||
|
||||
* [Troubleshooting the Kubernetes Addon](/site/kubernetes/kube-addon/#troubleshooting)
|
||||
* [Troubleshooting IPAM](/site/tasks/ipam/troubleshooting-ipam.md)
|
||||
* [Troubleshooting the Proxy](/site/tasks/weave-docker-api/using-proxy.md)
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
#! /bin/bash
|
||||
|
||||
. "$(dirname "$0")/config.sh"
|
||||
|
||||
C1=10.32.0.2
|
||||
C2=10.32.0.3
|
||||
|
||||
kill_weaver() {
|
||||
run_on $HOST1 sudo ip link set weave down
|
||||
WEAVER_PID=$(container_pid $HOST1 weave)
|
||||
run_on $HOST1 sudo kill -9 $WEAVER_PID
|
||||
sleep 3
|
||||
}
|
||||
|
||||
start_suite "Re-create bridge after restart"
|
||||
|
||||
# Should create a bridge of the "bridge" type.
|
||||
WEAVE_NO_FASTDP=1 weave_on $HOST1 launch
|
||||
WEAVE_NO_FASTDP=1 weave_on $HOST2 launch $HOST1
|
||||
|
||||
start_container $HOST1 $C1/24 --name=c1
|
||||
start_container $HOST2 $C2/24 --name=c2
|
||||
assert_raises "exec_on $HOST1 c1 $PING $C2"
|
||||
|
||||
kill_weaver # should re-create the bridge
|
||||
|
||||
assert_raises "exec_on $HOST1 c1 $PING $C2"
|
||||
|
||||
# Should create a bridge of the "bridged_fastdp" type
|
||||
weave_on $HOST1 reset
|
||||
weave_on $HOST1 launch $HOST2
|
||||
weave_on $HOST1 attach $C1/24 c1
|
||||
assert_raises "exec_on $HOST1 c1 $PING $C2"
|
||||
|
||||
kill_weaver # should re-create the bridge
|
||||
|
||||
assert_raises "exec_on $HOST1 c1 $PING $C2"
|
||||
|
||||
# test restore of iptables
|
||||
|
||||
weave_on $HOST1 reset
|
||||
# `--expect-npc` to trigger creation of WEAVE-NPC iptables chain.
|
||||
weave_on $HOST1 launch --expect-npc
|
||||
# To create POSTROUTING rules.
|
||||
weave_on $HOST1 expose
|
||||
|
||||
IPT_BEFORE=$(mktemp)
|
||||
IPT_AFTER=$(mktemp)
|
||||
run_on $HOST1 "sudo iptables-save | grep -i weave | grep -v '\[.*:.*\]' > $IPT_BEFORE"
|
||||
|
||||
run_on $HOST1 "sudo iptables -t filter -D FORWARD -o weave -m comment --comment \"NOTE: this must go before '-j KUBE-FORWARD'\" -j WEAVE-NPC"
|
||||
run_on $HOST1 sudo iptables -t nat -D POSTROUTING -j WEAVE
|
||||
kill_weaver # should re-create the bridge and iptables friends
|
||||
|
||||
# Rudimentary check that weave related iptables rules has been restored
|
||||
run_on $HOST1 "sudo iptables-save | grep -i weave | grep -v '\[.*:.*\]' > $IPT_AFTER"
|
||||
assert_raises "run_on $HOST1 diff $IPT_BEFORE $IPT_AFTER"
|
||||
|
||||
end_suite
|
||||
@@ -21,7 +21,7 @@ start_container_local_plugin $HOST1 --name=c3
|
||||
|
||||
assert "show_multicast_route_on $HOST1 c1"
|
||||
assert "show_multicast_route_on $HOST1 c2"
|
||||
assert "show_multicast_route_on $HOST1 c3" "224.0.0.0/4 dev ethwe0 "
|
||||
assert "show_multicast_route_on $HOST1 c3" "224.0.0.0/4 dev ethwe0 scope link "
|
||||
|
||||
# Now try via docker network options
|
||||
# using ssh rather than docker -H because CircleCI docker client is older
|
||||
@@ -30,7 +30,7 @@ $SSH $HOST1 docker run --net=testmcastfalse --name c4 -di $SMALL_IMAGE /bin/sh
|
||||
assert "show_multicast_route_on $HOST1 c4"
|
||||
$SSH $HOST1 docker network create --driver weavemesh --ipam-driver weavemesh --opt works.weave.multicast testmcasttrue
|
||||
$SSH $HOST1 docker run --net=testmcasttrue --name c5 -di $SMALL_IMAGE /bin/sh
|
||||
assert "show_multicast_route_on $HOST1 c5" "224.0.0.0/4 dev ethwe0 "
|
||||
assert "show_multicast_route_on $HOST1 c5" "224.0.0.0/4 dev ethwe0 scope link "
|
||||
|
||||
# Ensure current proxy options are obeyed on container start
|
||||
docker_on $HOST1 stop -t 1 c2
|
||||
@@ -39,7 +39,7 @@ weave_on $HOST1 launch
|
||||
|
||||
proxy docker_on $HOST1 start c2
|
||||
|
||||
assert "show_multicast_route_on $HOST1 c2" "224.0.0.0/4 dev ethwe "
|
||||
assert "show_multicast_route_on $HOST1 c2" "224.0.0.0/4 dev ethwe scope link "
|
||||
|
||||
docker_on $HOST1 rm -f c3 c4 c5
|
||||
$SSH $HOST1 docker network rm testmcasttrue testmcastfalse
|
||||
|
||||
+155
-25
@@ -4,7 +4,11 @@
|
||||
|
||||
tear_down_kubeadm() {
|
||||
for host in $HOSTS; do
|
||||
run_on $host "sudo kubeadm reset && sudo rm -r -f /opt/cni/bin/*weave*"
|
||||
run_on $host "sudo kubeadm reset --force && sudo rm -r -f /opt/cni/bin/*weave*"
|
||||
# Remove the rule to ensure that api-server is not accessible while kube-proxy
|
||||
# is not ready. Otherwise, we have a race between "-j KUBE-FORWARD" and "-j WEAVE-NPC"
|
||||
# which makes the NodePort isolation test case to fail.
|
||||
run_on $host "sudo iptables -t nat -D PREROUTING -m comment --comment \"kubernetes service portals\" -j KUBE-SERVICES" || true
|
||||
done
|
||||
}
|
||||
|
||||
@@ -18,6 +22,7 @@ NUM_HOSTS=$(howmany $HOSTS)
|
||||
SUCCESS="$(( $NUM_HOSTS * ($NUM_HOSTS-1) )) established"
|
||||
KUBECTL="sudo kubectl --kubeconfig /etc/kubernetes/admin.conf"
|
||||
KUBE_PORT=6443
|
||||
WEAVE_NETWORK=10.32.0.0/12
|
||||
IMAGE=weaveworks/network-tester:latest
|
||||
DOMAIN=nettest.default.svc.cluster.local.
|
||||
|
||||
@@ -29,27 +34,25 @@ docker_on $HOST1 run --rm --privileged --net=host --entrypoint=/usr/sbin/ipset w
|
||||
|
||||
# kubeadm init upgrades to latest Kubernetes version by default, therefore we try to lock the version using the below option:
|
||||
k8s_version="$(run_on $HOST1 "kubelet --version" | grep -oP "(?<=Kubernetes )v[\d\.\-beta]+")"
|
||||
k8s_version_option="$([[ "$k8s_version" > "v1.6" ]] && echo "kubernetes-version" || echo "use-kubernetes-version")"
|
||||
|
||||
for host in $HOSTS; do
|
||||
if [ $host = $HOST1 ] ; then
|
||||
run_on $host "sudo systemctl start kubelet && sudo kubeadm init --$k8s_version_option=$k8s_version --token=$TOKEN"
|
||||
run_on $host "sudo systemctl start kubelet && sudo kubeadm init --kubernetes-version=$k8s_version --token=$TOKEN --pod-network-cidr=$WEAVE_NETWORK"
|
||||
else
|
||||
run_on $host "sudo systemctl start kubelet && sudo kubeadm join --token=$TOKEN $HOST1IP:$KUBE_PORT"
|
||||
run_on $host "sudo systemctl start kubelet && sudo kubeadm join --token=$TOKEN --discovery-token-unsafe-skip-ca-verification $HOST1IP:$KUBE_PORT"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$COVERAGE" ]; then
|
||||
COVERAGE_ARGS="env:\\n - name: EXTRA_ARGS\\n value: \"-test.coverprofile=/home/weave/cover.prof --\""
|
||||
WEAVE_ENV_VARS="env:\\n - name: EXTRA_ARGS\\n value: \"-test.coverprofile=/home/weave/cover.prof --\""
|
||||
else
|
||||
COVERAGE_ARGS="env:"
|
||||
WEAVE_ENV_VARS="env:"
|
||||
fi
|
||||
|
||||
# Ensure Kubernetes uses locally built container images and inject code coverage environment variable (or do nothing depending on $COVERAGE):
|
||||
sed -e "s%imagePullPolicy: Always%imagePullPolicy: Never%" \
|
||||
-e "s%env:%$COVERAGE_ARGS%" \
|
||||
-e "s%#npc-args% args:\n - '--use-legacy-netpol'%" \
|
||||
"$(dirname "$0")/../prog/weave-kube/weave-daemonset-k8s-1.7.yaml" | run_on "$HOST1" "$KUBECTL apply -n kube-system -f -"
|
||||
-e "s%env:%$WEAVE_ENV_VARS%" \
|
||||
"$(dirname "$0")/../prog/weave-kube/weave-daemonset-k8s-1.8.yaml" | run_on "$HOST1" "$KUBECTL apply -n kube-system -f -"
|
||||
|
||||
sleep 5
|
||||
|
||||
@@ -59,7 +62,7 @@ check_connections() {
|
||||
|
||||
assert_raises 'wait_for_x check_connections "connections to establish"'
|
||||
|
||||
# Check we can ping between the Weave bridg IPs on each host
|
||||
# Check we can ping between the Weave bridge IPs on each host
|
||||
HOST1EXPIP=$($SSH $HOST1 "weave expose" || true)
|
||||
HOST2EXPIP=$($SSH $HOST2 "weave expose" || true)
|
||||
HOST3EXPIP=$($SSH $HOST3 "weave expose" || true)
|
||||
@@ -72,10 +75,19 @@ assert "run_on $HOST1 ps aux | grep -c '[d]efunct'" "0"
|
||||
assert "run_on $HOST2 ps aux | grep -c '[d]efunct'" "0"
|
||||
assert "run_on $HOST3 ps aux | grep -c '[d]efunct'" "0"
|
||||
|
||||
# Set up a simple network policy so all our test pods can talk to each other
|
||||
run_on $HOST1 "$KUBECTL annotate ns default net.beta.kubernetes.io/network-policy='{\"ingress\":{\"isolation\":\"DefaultDeny\"}}'"
|
||||
# Set up simple network policies so all our test pods can talk to each other
|
||||
run_on $HOST1 "$KUBECTL apply -f -" <<EOF
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default-deny
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
EOF
|
||||
run_on $HOST1 "$KUBECTL apply -f -" <<EOF
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: test840
|
||||
@@ -98,7 +110,7 @@ EOF
|
||||
|
||||
# Another policy, this time with no 'from' section, just to check that doesn't cause a crash
|
||||
run_on $HOST1 "$KUBECTL apply -f -" <<EOF
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: test840f
|
||||
@@ -134,6 +146,22 @@ spec:
|
||||
run: nettest
|
||||
EOF
|
||||
|
||||
# And a NodePort service so we can test virtual IP access
|
||||
run_on $HOST1 "$KUBECTL create -f -" <<EOF
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: netvirt
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8080
|
||||
nodePort: 31138
|
||||
selector:
|
||||
run: nettest
|
||||
EOF
|
||||
|
||||
podName=$($SSH $HOST1 "$KUBECTL get pods -l run=nettest -o go-template='{{(index .items 0).metadata.name}}'")
|
||||
|
||||
check_all_pods_communicate() {
|
||||
@@ -159,16 +187,14 @@ run_on $HOST1 "$KUBECTL run nettest-deny --labels=\"access=deny,run=nettest-deny
|
||||
denyPodName=$($SSH $HOST1 "$KUBECTL get pods -l run=nettest-deny -o go-template='{{(index .items 0).metadata.name}}'")
|
||||
assert_raises "! $SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$DOMAIN:8080/status >/dev/null"
|
||||
|
||||
# Restart weave-net with npc in non-legacy mode
|
||||
$SSH $HOST1 "$KUBECTL delete ds weave-net -n=kube-system"
|
||||
sed -e "s%imagePullPolicy: Always%imagePullPolicy: Never%" \
|
||||
-e "s%env:%$COVERAGE_ARGS%" \
|
||||
"$(dirname "$0")/../prog/weave-kube/weave-daemonset-k8s-1.7.yaml" | run_on "$HOST1" "$KUBECTL apply -n kube-system -f -"
|
||||
# check access via virtual IP
|
||||
VIRTUAL_IP=$($SSH $HOST1 $KUBECTL get service netvirt -o template --template={{.spec.clusterIP}})
|
||||
assert_raises "$SSH $HOST1 $KUBECTL exec $podName -- curl -s -S -f -m 2 http://$VIRTUAL_IP/status >/dev/null"
|
||||
assert_raises "! $SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$VIRTUAL_IP/status >/dev/null"
|
||||
|
||||
assert_raises 'wait_for_x check_all_pods_communicate pods'
|
||||
|
||||
# nettest-deny should still not be able to reach nettest pods
|
||||
assert_raises "! $SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$DOMAIN:8080/status >/dev/null"
|
||||
# host should not be able to reach pods via service virtual IP or NodePort
|
||||
assert_raises "! $SSH $HOST1 curl -s -S -f -m 2 http://$VIRTUAL_IP/status >/dev/null"
|
||||
assert_raises "! $SSH $HOST1 curl -s -S -f -m 2 http://$HOST2:31138/status >/dev/null"
|
||||
|
||||
# allow access for nettest-deny
|
||||
run_on $HOST1 "$KUBECTL apply -f -" <<EOF
|
||||
@@ -196,12 +222,39 @@ run_on $HOST1 "$KUBECTL delete netpol allow-nettest-deny"
|
||||
# nettest-deny should still not be able to reach nettest pods
|
||||
assert_raises "! $SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$DOMAIN:8080/status >/dev/null"
|
||||
|
||||
# allow access for all
|
||||
# Create many namespaces to stress namespaceSelector
|
||||
for n in 1 2 3 4 5 6 7 8 9 10; do
|
||||
run_on $HOST1 "$KUBECTL create namespace namespace${n}"
|
||||
done
|
||||
|
||||
# allow access from any namespace
|
||||
run_on $HOST1 "$KUBECTL apply -f -" <<EOF
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-nettest-deny
|
||||
name: allow-any-namespace
|
||||
namespace: default
|
||||
spec:
|
||||
podSelector: {}
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector: {}
|
||||
EOF
|
||||
|
||||
# Should be able to access from the "deny" pod now
|
||||
assert_raises "$SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$DOMAIN:8080/status >/dev/null"
|
||||
|
||||
# host should still not be able to reach pods via service virtual IP or NodePort
|
||||
# because host is not in a namespace
|
||||
assert_raises "! $SSH $HOST1 curl -s -S -f -m 2 http://$VIRTUAL_IP/status >/dev/null"
|
||||
assert_raises "! $SSH $HOST1 curl -s -S -f -m 2 http://$HOST2:31138/status >/dev/null"
|
||||
|
||||
# allow access from anywhere
|
||||
run_on $HOST1 "$KUBECTL apply -f -" <<EOF
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-anywhere
|
||||
namespace: default
|
||||
spec:
|
||||
podSelector: {}
|
||||
@@ -209,8 +262,85 @@ spec:
|
||||
- {}
|
||||
EOF
|
||||
|
||||
# Virtual IP and NodePort should now work
|
||||
assert_raises "$SSH $HOST1 curl -s -S -f -m 2 http://$VIRTUAL_IP/status >/dev/null"
|
||||
assert_raises "$SSH $HOST1 curl -s -S -f -m 2 http://$HOST2:31138/status >/dev/null"
|
||||
|
||||
# allow nettest-deny to access only to 8.8.8.0/24
|
||||
run_on $HOST1 "$KUBECTL apply -f -" <<EOF
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: egress-nettest-deny-dns
|
||||
namespace: default
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
run: nettest-deny
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 8.8.8.0/24
|
||||
except:
|
||||
- 8.8.8.4/32
|
||||
EOF
|
||||
|
||||
assert_raises "! $SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$DOMAIN:8080/status >/dev/null"
|
||||
assert_raises "! $SSH $HOST1 $KUBECTL exec $denyPodName -- dig @8.8.8.4 google.com >/dev/null"
|
||||
assert_raises "$SSH $HOST1 $KUBECTL exec $denyPodName -- dig @8.8.8.8 google.com >/dev/null"
|
||||
|
||||
# also, allow nettest-deny to access nettest and kube-system (for kube-dns)
|
||||
run_on $HOST1 "$KUBECTL label namespace kube-system name=kube-system"
|
||||
run_on $HOST1 "$KUBECTL replace -f -" <<EOF
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: egress-nettest-deny-dns
|
||||
namespace: default
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
run: nettest-deny
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 8.8.8.0/24
|
||||
except:
|
||||
- 8.8.8.4/32
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
run: nettest
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
name: kube-system
|
||||
EOF
|
||||
|
||||
assert_raises "$SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$DOMAIN:8080/status >/dev/null"
|
||||
|
||||
# Passing --no-masq-local and setting externalTrafficPolicy to Local must preserve
|
||||
# the client source IP addr
|
||||
|
||||
CLIENT_IP_MASQ="$($SSH $HOST1 curl -sS http://$HOST2:31138/client_ip)"
|
||||
|
||||
WEAVE_ENV_VARS="${WEAVE_ENV_VARS}\\n - name: NO_MASQ_LOCAL\\n value: \"1\""
|
||||
$SSH $HOST1 "$KUBECTL delete ds weave-net -n=kube-system"
|
||||
sed -e "s%imagePullPolicy: Always%imagePullPolicy: Never%" \
|
||||
-e "s%env:%$WEAVE_ENV_VARS%" \
|
||||
"$(dirname "$0")/../prog/weave-kube/weave-daemonset-k8s-1.8.yaml" | run_on "$HOST1" "$KUBECTL apply -n kube-system -f -"
|
||||
|
||||
sleep 5
|
||||
|
||||
run_on $HOST1 "$KUBECTL patch svc netvirt -p '{\"spec\":{\"externalTrafficPolicy\":\"Local\"}}'"
|
||||
|
||||
CLIENT_IP_NO_MASQ="$($SSH $HOST1 curl -sS http://$HOST2:31138/client_ip)"
|
||||
|
||||
assert_raises "[ $CLIENT_IP_NO_MASQ != $CLIENT_IP_MASQ ]"
|
||||
assert_raises "[ $HOST1IP == $CLIENT_IP_NO_MASQ ]"
|
||||
|
||||
tear_down_kubeadm
|
||||
|
||||
# Destroy our test ipset, and implicitly check it is still there
|
||||
|
||||
@@ -27,7 +27,7 @@ fi
|
||||
function teardown_kubernetes_cluster {
|
||||
greyly echo "Tearing down kubernetes cluster"
|
||||
for host in $HOSTS; do
|
||||
run_on $host "sudo kubeadm reset && sudo rm -r -f /opt/cni/bin/*weave*"
|
||||
run_on $host "sudo kubeadm reset --force && sudo rm -r -f /opt/cni/bin/*weave*"
|
||||
done
|
||||
}
|
||||
|
||||
@@ -38,13 +38,12 @@ function setup_kubernetes_cluster {
|
||||
|
||||
# kubeadm init upgrades to latest Kubernetes version by default, therefore we try to lock the version using the below option:
|
||||
k8s_version="$(run_on $HOST1 "kubelet --version" | grep -oP "(?<=Kubernetes )v[\d\.\-beta]+")"
|
||||
k8s_version_option="$([[ "$k8s_version" > "v1.6" ]] && echo "kubernetes-version" || echo "use-kubernetes-version")"
|
||||
|
||||
for host in $HOSTS; do
|
||||
if [ $host = $HOST1 ] ; then
|
||||
run_on $host "sudo systemctl start kubelet && sudo kubeadm init --$k8s_version_option=$k8s_version --token=$TOKEN"
|
||||
run_on $host "sudo systemctl start kubelet && sudo kubeadm init --kubernetes-version=$k8s_version --token=$TOKEN"
|
||||
else
|
||||
run_on $host "sudo systemctl start kubelet && sudo kubeadm join --token=$TOKEN $HOST1IP:$KUBE_PORT"
|
||||
run_on $host "sudo systemctl start kubelet && sudo kubeadm join --token=$TOKEN --discovery-token-unsafe-skip-ca-verification $HOST1IP:$KUBE_PORT"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
+79
-1
@@ -13,7 +13,7 @@ it is recommended to run the tests from the build VM and not the host.
|
||||
|
||||
> **NOTE WELL** The number of hosts required by a test must be specified in the test name. For example, `000_some_test.sh` will use one host, while `001_some_other_3_test.sh` will use three hosts. The number of hosts must be the last part of the test name before the `_test.sh` prefix. See `/tools/runner/runner.go` for details
|
||||
|
||||
## Running tests
|
||||
## Running tests locally using VMs
|
||||
|
||||
**TL;DR**: You can run the steps 2. to 7. below with one command:
|
||||
|
||||
@@ -23,46 +23,62 @@ it is recommended to run the tests from the build VM and not the host.
|
||||
|
||||
1. Start the build virtual machine (see above article for more details):
|
||||
|
||||
```
|
||||
vagrant up
|
||||
```
|
||||
|
||||
2. Start the three testing VMs:
|
||||
|
||||
```
|
||||
cd test
|
||||
vagrant up
|
||||
```
|
||||
|
||||
3. SSH into the build VM and go to Weave Net's sources:
|
||||
|
||||
```
|
||||
cd ..
|
||||
vagrant ssh
|
||||
# you are now on the build VM:
|
||||
cd ~/weave
|
||||
```
|
||||
|
||||
4. Compile all code and dependencies:
|
||||
|
||||
```
|
||||
make
|
||||
make testrunner
|
||||
cd test
|
||||
```
|
||||
|
||||
5. Upload the weave images from where the `Makefile` puts them (`weave.tar.gz`) to
|
||||
the three docker hosts, `docker load` these, and copies the `weave` script over:
|
||||
|
||||
```
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
6. Run individual tests, e.g.:
|
||||
|
||||
```
|
||||
./200_dns_test.sh
|
||||
```
|
||||
|
||||
or run all tests (everything named `*_test.sh`):
|
||||
|
||||
```
|
||||
./run_all.sh
|
||||
```
|
||||
|
||||
7. Stop all VMs:
|
||||
|
||||
```
|
||||
exit
|
||||
# you are now on your host machine
|
||||
vagrant destroy -f
|
||||
cd test
|
||||
vagrant destroy -f
|
||||
```
|
||||
|
||||
|
||||
## Using other VMs
|
||||
@@ -92,3 +108,65 @@ When a new version of Docker is released, you willneed to update the GCE test im
|
||||
To do this, change the Docker version in `run-integration-tests.sh` and push the change.
|
||||
Next build in CircleCI will detect that there is no template for this version of Docker and will first create the template before running tests.
|
||||
Subsequent builds will then simply re-use the template.
|
||||
|
||||
## Running the tests on a cloud provider
|
||||
|
||||
Running the tests against a remote provider instead of local vagrant VMs is a little more involved, and reproduces the environment used for CI. It is recommended that you use the build VM to coordinate the build phase. Make sure you have a recent version of [ansible](https://github.com/ansible/ansible) installed, terraform will fail to provision the VMs.
|
||||
|
||||
1. Create the build VM in the repository root and log in
|
||||
|
||||
```
|
||||
# On your development machine
|
||||
$ vagrant up
|
||||
$ vagrant ssh
|
||||
```
|
||||
|
||||
2. Once in the build vm, install the `gcloud` tools as [per the documentation](https://cloud.google.com/sdk/downloads). You need to do this manually as the circleCI VMs are already provisioned with `gcloud`.
|
||||
|
||||
```
|
||||
# On the build VM
|
||||
$ curl https://sdk.cloud.google.com | bash
|
||||
$ exec -l $SHELL
|
||||
```
|
||||
|
||||
You don't need to initialise the `gcloud` tools by running `gcloud init`.
|
||||
|
||||
3. Export the secret key needed to decrypt the cloud credentials - This assumes you're using the build tools
|
||||
|
||||
```
|
||||
# On the build VM
|
||||
$ export SECRET_KEY="XXXXXXXXXX"
|
||||
```
|
||||
|
||||
4. Navigate to the test directory and provision the VMs
|
||||
|
||||
```
|
||||
# On the build VM
|
||||
$ cd weave/test
|
||||
/weave/test$ ./run-integration-tests.sh up
|
||||
```
|
||||
5. Run the tests you need
|
||||
|
||||
```
|
||||
# On the build VM
|
||||
/weave/test$ export TESTS="./090_docker_restart_policy_2_test.sh"
|
||||
/weave/test$ ./run-integration-tests.sh test
|
||||
```
|
||||
|
||||
6. Don't forget to close down your remote VMs when you're done
|
||||
|
||||
```
|
||||
# On the build VM
|
||||
/weave/test$ ./run-integration-tests.sh destroy
|
||||
```
|
||||
|
||||
7. And switch off your build VM
|
||||
|
||||
```
|
||||
# On the build VM
|
||||
/weave/test$ exit
|
||||
|
||||
# On your development machine
|
||||
$ vagrant destroy -f
|
||||
# or "vagrant suspend" if you want to preserve the machine state
|
||||
```
|
||||
Vendored
+1
-1
@@ -25,7 +25,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.vm.define hostname do |host|
|
||||
host.vm.provider :virtualbox do |vb|
|
||||
vb.name = hostname
|
||||
vb.memory = 384
|
||||
vb.memory = 2048
|
||||
configure_nat_dns(vb)
|
||||
end
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
FROM bash
|
||||
RUN apk add --no-cache curl
|
||||
RUN apk add --no-cache curl bind-tools
|
||||
ADD webserver webserver
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["/webserver"]
|
||||
@@ -13,6 +13,8 @@
|
||||
// Visit /status to see pass/running/fail determination. (literally, it will
|
||||
// return one of those words.)
|
||||
//
|
||||
// Visit /client_ip to see the source IP addr of the request.
|
||||
//
|
||||
// /write is used by other network test servers to register connectivity.
|
||||
package main
|
||||
|
||||
@@ -78,6 +80,20 @@ func (s *State) serveStatus(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "fail")
|
||||
}
|
||||
|
||||
// serveClientIP returns the client source IP addr.
|
||||
func (s *State) serveClientIP(w http.ResponseWriter, r *http.Request) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
go s.Logf("Warning: unable to parse remote addr: %s", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, host)
|
||||
}
|
||||
|
||||
// serveRead writes our json encoded state
|
||||
func (s *State) serveRead(w http.ResponseWriter, r *http.Request) {
|
||||
s.lock.Lock()
|
||||
@@ -186,6 +202,7 @@ func main() {
|
||||
http.HandleFunc("/read", state.serveRead)
|
||||
http.HandleFunc("/write", state.serveWrite)
|
||||
http.HandleFunc("/status", state.serveStatus)
|
||||
http.HandleFunc("/client_ip", state.serveClientIP)
|
||||
|
||||
go log.Fatal(http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", *port), nil))
|
||||
|
||||
|
||||
+1
-1
@@ -23,4 +23,4 @@ if [ -n "$CIRCLECI" -o -n "$PARALLEL" ]; then
|
||||
RUNNER_ARGS="$RUNNER_ARGS -parallel"
|
||||
fi
|
||||
|
||||
HOSTS="$HOSTS" "${DIR}/../tools/runner/runner" -timeout=300 $RUNNER_ARGS $TESTS
|
||||
HOSTS="$HOSTS" "${DIR}/../tools/runner/runner" -timeout=360 $RUNNER_ARGS $TESTS
|
||||
+18
-18
@@ -41,15 +41,15 @@ func main() {
|
||||
log.Fatalf("The client key already exists. Please remove it or specify a different key/cert.")
|
||||
}
|
||||
|
||||
err = cert.GenerateCert(
|
||||
[]string{""},
|
||||
clientCertPath,
|
||||
clientKeyPath,
|
||||
caCertPath,
|
||||
caKeyPath,
|
||||
org,
|
||||
bits,
|
||||
)
|
||||
err = cert.GenerateCert(&cert.Options{
|
||||
Hosts: []string{""},
|
||||
CertFile: clientCertPath,
|
||||
KeyFile: clientKeyPath,
|
||||
CAFile: caCertPath,
|
||||
CAKeyFile: caKeyPath,
|
||||
Org: org,
|
||||
Bits: bits,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Error generating client certificate: %s", err)
|
||||
}
|
||||
@@ -64,15 +64,15 @@ func main() {
|
||||
|
||||
if _, err := os.Stat(serverCertPath); os.IsNotExist(err) {
|
||||
log.Printf("Creating server certificate: %s", serverCertPath)
|
||||
err = cert.GenerateCert(
|
||||
[]string{ip},
|
||||
serverCertPath,
|
||||
serverKeyPath,
|
||||
caCertPath,
|
||||
caKeyPath,
|
||||
org,
|
||||
bits,
|
||||
)
|
||||
err = cert.GenerateCert(&cert.Options{
|
||||
Hosts: []string{ip},
|
||||
CertFile: serverCertPath,
|
||||
KeyFile: serverKeyPath,
|
||||
CAFile: caCertPath,
|
||||
CAKeyFile: caKeyPath,
|
||||
Org: org,
|
||||
Bits: bits,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("error generating server cert: %s", err)
|
||||
}
|
||||
|
||||
+1
-1
Submodule tools updated: 74dc626b6d...ca69607f79.
+1
-1
@@ -16,7 +16,7 @@ def ansibleize(h)
|
||||
end
|
||||
|
||||
def get_go_version_from_build_dockerfile()
|
||||
go_regexp = /FROM golang:(\S*).*?/
|
||||
go_regexp = /FROM golang:([\d\.]*).*?/
|
||||
dockerfile_path = File.expand_path(File.join(File.dirname(__FILE__), 'build', 'Dockerfile'))
|
||||
go_version = File.readlines(dockerfile_path).select { |line| line.match(go_regexp) }.first.match(go_regexp).captures.first
|
||||
if go_version.nil?
|
||||
|
||||
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
|
||||
# Managing Vendored Dependencies with `git submodule`
|
||||
|
||||
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.
|
||||
|
||||
## Adding a New Dependency
|
||||
|
||||
~/weave$ git submodule add https://example.com/organisation/module vendor/example.com/organisation/module
|
||||
|
||||
## Update All Dependencies to Upstream `master`
|
||||
|
||||
~/weave$ git submodule foreach git pull origin master
|
||||
|
||||
## Force a Specific Dependency to a Particular Commit
|
||||
|
||||
~/weave$ (cd vendor/example.com/organisation/module && git checkout <commit-ish>)
|
||||
~/weave$ git add vendor/example.com/organisation/module
|
||||
|
||||
## Remove a Dependency
|
||||
|
||||
~/weave$ git submodule deinit vendor/example.com/organisation/module
|
||||
~/weave$ git rm vendor/example.com/organisation/module
|
||||
~/weave$ rm -rf .git/modules/vendor/example.con/organisation/module
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Microsoft Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
# go-ansiterm
|
||||
|
||||
This is a cross platform Ansi Terminal Emulation library. It reads a stream of Ansi characters and produces the appropriate function calls. The results of the function calls are platform dependent.
|
||||
|
||||
For example the parser might receive "ESC, [, A" as a stream of three characters. This is the code for Cursor Up (http://www.vt100.net/docs/vt510-rm/CUU). The parser then calls the cursor up function (CUU()) on an event handler. The event handler determines what platform specific work must be done to cause the cursor to move up one position.
|
||||
|
||||
The parser (parser.go) is a partial implementation of this state machine (http://vt100.net/emu/vt500_parser.png). There are also two event handler implementations, one for tests (test_event_handler.go) to validate that the expected events are being produced and called, the other is a Windows implementation (winterm/win_event_handler.go).
|
||||
|
||||
See parser_test.go for examples exercising the state machine and generating appropriate function calls.
|
||||
|
||||
-----
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
package ansiterm
|
||||
|
||||
const LogEnv = "DEBUG_TERMINAL"
|
||||
|
||||
// ANSI constants
|
||||
// References:
|
||||
// -- http://www.ecma-international.org/publications/standards/Ecma-048.htm
|
||||
// -- http://man7.org/linux/man-pages/man4/console_codes.4.html
|
||||
// -- http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html
|
||||
// -- http://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// -- http://vt100.net/emu/dec_ansi_parser
|
||||
// -- http://vt100.net/emu/vt500_parser.svg
|
||||
// -- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
||||
// -- http://www.inwap.com/pdp10/ansicode.txt
|
||||
const (
|
||||
// ECMA-48 Set Graphics Rendition
|
||||
// Note:
|
||||
// -- Constants leading with an underscore (e.g., _ANSI_xxx) are unsupported or reserved
|
||||
// -- Fonts could possibly be supported via SetCurrentConsoleFontEx
|
||||
// -- Windows does not expose the per-window cursor (i.e., caret) blink times
|
||||
ANSI_SGR_RESET = 0
|
||||
ANSI_SGR_BOLD = 1
|
||||
ANSI_SGR_DIM = 2
|
||||
_ANSI_SGR_ITALIC = 3
|
||||
ANSI_SGR_UNDERLINE = 4
|
||||
_ANSI_SGR_BLINKSLOW = 5
|
||||
_ANSI_SGR_BLINKFAST = 6
|
||||
ANSI_SGR_REVERSE = 7
|
||||
_ANSI_SGR_INVISIBLE = 8
|
||||
_ANSI_SGR_LINETHROUGH = 9
|
||||
_ANSI_SGR_FONT_00 = 10
|
||||
_ANSI_SGR_FONT_01 = 11
|
||||
_ANSI_SGR_FONT_02 = 12
|
||||
_ANSI_SGR_FONT_03 = 13
|
||||
_ANSI_SGR_FONT_04 = 14
|
||||
_ANSI_SGR_FONT_05 = 15
|
||||
_ANSI_SGR_FONT_06 = 16
|
||||
_ANSI_SGR_FONT_07 = 17
|
||||
_ANSI_SGR_FONT_08 = 18
|
||||
_ANSI_SGR_FONT_09 = 19
|
||||
_ANSI_SGR_FONT_10 = 20
|
||||
_ANSI_SGR_DOUBLEUNDERLINE = 21
|
||||
ANSI_SGR_BOLD_DIM_OFF = 22
|
||||
_ANSI_SGR_ITALIC_OFF = 23
|
||||
ANSI_SGR_UNDERLINE_OFF = 24
|
||||
_ANSI_SGR_BLINK_OFF = 25
|
||||
_ANSI_SGR_RESERVED_00 = 26
|
||||
ANSI_SGR_REVERSE_OFF = 27
|
||||
_ANSI_SGR_INVISIBLE_OFF = 28
|
||||
_ANSI_SGR_LINETHROUGH_OFF = 29
|
||||
ANSI_SGR_FOREGROUND_BLACK = 30
|
||||
ANSI_SGR_FOREGROUND_RED = 31
|
||||
ANSI_SGR_FOREGROUND_GREEN = 32
|
||||
ANSI_SGR_FOREGROUND_YELLOW = 33
|
||||
ANSI_SGR_FOREGROUND_BLUE = 34
|
||||
ANSI_SGR_FOREGROUND_MAGENTA = 35
|
||||
ANSI_SGR_FOREGROUND_CYAN = 36
|
||||
ANSI_SGR_FOREGROUND_WHITE = 37
|
||||
_ANSI_SGR_RESERVED_01 = 38
|
||||
ANSI_SGR_FOREGROUND_DEFAULT = 39
|
||||
ANSI_SGR_BACKGROUND_BLACK = 40
|
||||
ANSI_SGR_BACKGROUND_RED = 41
|
||||
ANSI_SGR_BACKGROUND_GREEN = 42
|
||||
ANSI_SGR_BACKGROUND_YELLOW = 43
|
||||
ANSI_SGR_BACKGROUND_BLUE = 44
|
||||
ANSI_SGR_BACKGROUND_MAGENTA = 45
|
||||
ANSI_SGR_BACKGROUND_CYAN = 46
|
||||
ANSI_SGR_BACKGROUND_WHITE = 47
|
||||
_ANSI_SGR_RESERVED_02 = 48
|
||||
ANSI_SGR_BACKGROUND_DEFAULT = 49
|
||||
// 50 - 65: Unsupported
|
||||
|
||||
ANSI_MAX_CMD_LENGTH = 4096
|
||||
|
||||
MAX_INPUT_EVENTS = 128
|
||||
DEFAULT_WIDTH = 80
|
||||
DEFAULT_HEIGHT = 24
|
||||
|
||||
ANSI_BEL = 0x07
|
||||
ANSI_BACKSPACE = 0x08
|
||||
ANSI_TAB = 0x09
|
||||
ANSI_LINE_FEED = 0x0A
|
||||
ANSI_VERTICAL_TAB = 0x0B
|
||||
ANSI_FORM_FEED = 0x0C
|
||||
ANSI_CARRIAGE_RETURN = 0x0D
|
||||
ANSI_ESCAPE_PRIMARY = 0x1B
|
||||
ANSI_ESCAPE_SECONDARY = 0x5B
|
||||
ANSI_OSC_STRING_ENTRY = 0x5D
|
||||
ANSI_COMMAND_FIRST = 0x40
|
||||
ANSI_COMMAND_LAST = 0x7E
|
||||
DCS_ENTRY = 0x90
|
||||
CSI_ENTRY = 0x9B
|
||||
OSC_STRING = 0x9D
|
||||
ANSI_PARAMETER_SEP = ";"
|
||||
ANSI_CMD_G0 = '('
|
||||
ANSI_CMD_G1 = ')'
|
||||
ANSI_CMD_G2 = '*'
|
||||
ANSI_CMD_G3 = '+'
|
||||
ANSI_CMD_DECPNM = '>'
|
||||
ANSI_CMD_DECPAM = '='
|
||||
ANSI_CMD_OSC = ']'
|
||||
ANSI_CMD_STR_TERM = '\\'
|
||||
|
||||
KEY_CONTROL_PARAM_2 = ";2"
|
||||
KEY_CONTROL_PARAM_3 = ";3"
|
||||
KEY_CONTROL_PARAM_4 = ";4"
|
||||
KEY_CONTROL_PARAM_5 = ";5"
|
||||
KEY_CONTROL_PARAM_6 = ";6"
|
||||
KEY_CONTROL_PARAM_7 = ";7"
|
||||
KEY_CONTROL_PARAM_8 = ";8"
|
||||
KEY_ESC_CSI = "\x1B["
|
||||
KEY_ESC_N = "\x1BN"
|
||||
KEY_ESC_O = "\x1BO"
|
||||
|
||||
FILL_CHARACTER = ' '
|
||||
)
|
||||
|
||||
func getByteRange(start byte, end byte) []byte {
|
||||
bytes := make([]byte, 0, 32)
|
||||
for i := start; i <= end; i++ {
|
||||
bytes = append(bytes, byte(i))
|
||||
}
|
||||
|
||||
return bytes
|
||||
}
|
||||
|
||||
var toGroundBytes = getToGroundBytes()
|
||||
var executors = getExecuteBytes()
|
||||
|
||||
// SPACE 20+A0 hex Always and everywhere a blank space
|
||||
// Intermediate 20-2F hex !"#$%&'()*+,-./
|
||||
var intermeds = getByteRange(0x20, 0x2F)
|
||||
|
||||
// Parameters 30-3F hex 0123456789:;<=>?
|
||||
// CSI Parameters 30-39, 3B hex 0123456789;
|
||||
var csiParams = getByteRange(0x30, 0x3F)
|
||||
|
||||
var csiCollectables = append(getByteRange(0x30, 0x39), getByteRange(0x3B, 0x3F)...)
|
||||
|
||||
// Uppercase 40-5F hex @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
|
||||
var upperCase = getByteRange(0x40, 0x5F)
|
||||
|
||||
// Lowercase 60-7E hex `abcdefghijlkmnopqrstuvwxyz{|}~
|
||||
var lowerCase = getByteRange(0x60, 0x7E)
|
||||
|
||||
// Alphabetics 40-7E hex (all of upper and lower case)
|
||||
var alphabetics = append(upperCase, lowerCase...)
|
||||
|
||||
var printables = getByteRange(0x20, 0x7F)
|
||||
|
||||
var escapeIntermediateToGroundBytes = getByteRange(0x30, 0x7E)
|
||||
var escapeToGroundBytes = getEscapeToGroundBytes()
|
||||
|
||||
// See http://www.vt100.net/emu/vt500_parser.png for description of the complex
|
||||
// byte ranges below
|
||||
|
||||
func getEscapeToGroundBytes() []byte {
|
||||
escapeToGroundBytes := getByteRange(0x30, 0x4F)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x51, 0x57)...)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, 0x59)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, 0x5A)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, 0x5C)
|
||||
escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x60, 0x7E)...)
|
||||
return escapeToGroundBytes
|
||||
}
|
||||
|
||||
func getExecuteBytes() []byte {
|
||||
executeBytes := getByteRange(0x00, 0x17)
|
||||
executeBytes = append(executeBytes, 0x19)
|
||||
executeBytes = append(executeBytes, getByteRange(0x1C, 0x1F)...)
|
||||
return executeBytes
|
||||
}
|
||||
|
||||
func getToGroundBytes() []byte {
|
||||
groundBytes := []byte{0x18}
|
||||
groundBytes = append(groundBytes, 0x1A)
|
||||
groundBytes = append(groundBytes, getByteRange(0x80, 0x8F)...)
|
||||
groundBytes = append(groundBytes, getByteRange(0x91, 0x97)...)
|
||||
groundBytes = append(groundBytes, 0x99)
|
||||
groundBytes = append(groundBytes, 0x9A)
|
||||
groundBytes = append(groundBytes, 0x9C)
|
||||
return groundBytes
|
||||
}
|
||||
|
||||
// Delete 7F hex Always and everywhere ignored
|
||||
// C1 Control 80-9F hex 32 additional control characters
|
||||
// G1 Displayable A1-FE hex 94 additional displayable characters
|
||||
// Special A0+FF hex Same as SPACE and DELETE
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package ansiterm
|
||||
|
||||
type ansiContext struct {
|
||||
currentChar byte
|
||||
paramBuffer []byte
|
||||
interBuffer []byte
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package ansiterm
|
||||
|
||||
type csiEntryState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (csiState csiEntryState) Handle(b byte) (s state, e error) {
|
||||
csiState.parser.logf("CsiEntry::Handle %#x", b)
|
||||
|
||||
nextState, err := csiState.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case sliceContains(alphabetics, b):
|
||||
return csiState.parser.ground, nil
|
||||
case sliceContains(csiCollectables, b):
|
||||
return csiState.parser.csiParam, nil
|
||||
case sliceContains(executors, b):
|
||||
return csiState, csiState.parser.execute()
|
||||
}
|
||||
|
||||
return csiState, nil
|
||||
}
|
||||
|
||||
func (csiState csiEntryState) Transition(s state) error {
|
||||
csiState.parser.logf("CsiEntry::Transition %s --> %s", csiState.Name(), s.Name())
|
||||
csiState.baseState.Transition(s)
|
||||
|
||||
switch s {
|
||||
case csiState.parser.ground:
|
||||
return csiState.parser.csiDispatch()
|
||||
case csiState.parser.csiParam:
|
||||
switch {
|
||||
case sliceContains(csiParams, csiState.parser.context.currentChar):
|
||||
csiState.parser.collectParam()
|
||||
case sliceContains(intermeds, csiState.parser.context.currentChar):
|
||||
csiState.parser.collectInter()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (csiState csiEntryState) Enter() error {
|
||||
csiState.parser.clear()
|
||||
return nil
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package ansiterm
|
||||
|
||||
type csiParamState struct {
|
||||
baseState
|
||||
}
|
||||
|
||||
func (csiState csiParamState) Handle(b byte) (s state, e error) {
|
||||
csiState.parser.logf("CsiParam::Handle %#x", b)
|
||||
|
||||
nextState, err := csiState.baseState.Handle(b)
|
||||
if nextState != nil || err != nil {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case sliceContains(alphabetics, b):
|
||||
return csiState.parser.ground, nil
|
||||
case sliceContains(csiCollectables, b):
|
||||
csiState.parser.collectParam()
|
||||
return csiState, nil
|
||||
case sliceContains(executors, b):
|
||||
return csiState, csiState.parser.execute()
|
||||
}
|
||||
|
||||
return csiState, nil
|
||||
}
|
||||
|
||||
func (csiState csiParamState) Transition(s state) error {
|
||||
csiState.parser.logf("CsiParam::Transition %s --> %s", csiState.Name(), s.Name())
|
||||
csiState.baseState.Transition(s)
|
||||
|
||||
switch s {
|
||||
case csiState.parser.ground:
|
||||
return csiState.parser.csiDispatch()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Loaded 100 of 3009 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user