Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a9fae5892 | ||
|
|
f66b0cc0aa | ||
|
|
e7f30106c0 | ||
|
|
86d2800b59 | ||
|
|
e65006042d | ||
|
|
65efc89b78 | ||
|
|
ae38834507 | ||
|
|
4261777613 | ||
|
|
7bc089f5c5 | ||
|
|
c46e3e8cf7 | ||
|
|
180d554205 | ||
|
|
a450d2e741 | ||
|
|
d528196033 | ||
|
|
0da2cbf84e | ||
|
|
20f981bc42 | ||
|
|
9a5514ffe0 | ||
|
|
439b4ad190 | ||
|
|
9a9c06966b | ||
|
|
b3f9baac8f | ||
|
|
bb961489b0 | ||
|
|
d6ae1f8375 | ||
|
|
5a1361167e | ||
|
|
4d5172f67e | ||
|
|
6f01c31458 | ||
|
|
6cfb208f3f | ||
|
|
3074892f1b | ||
|
|
b05d977cf9 | ||
|
|
36d183d988 | ||
|
|
b154fd56fb | ||
|
|
3419f69246 | ||
|
|
8e23301028 | ||
|
|
72724ee389 | ||
|
|
ae59a053b1 | ||
|
|
e71919226b | ||
|
|
6c5dd75216 | ||
|
|
23095728f5 | ||
|
|
498dacb4d2 | ||
|
|
b373a069e9 | ||
|
|
1d67e2f955 | ||
|
|
9a6935bf40 | ||
|
|
921ffefd90 | ||
|
|
a4fccd43eb | ||
|
|
48e1a8295b | ||
|
|
890c8c1905 | ||
|
|
38d50ce6fb | ||
|
|
14768d6947 | ||
|
|
b96399b563 | ||
|
|
d8c008dc7d | ||
|
|
b2ae5f4f4b | ||
|
|
ba871d7855 | ||
|
|
a0c8fc2ddb | ||
|
|
2a12053122 | ||
|
|
1860f21efb | ||
|
|
541adf6bb1 | ||
|
|
a71e90ddb8 | ||
|
|
60f4b3cdb8 | ||
|
|
0c249fa126 | ||
|
|
db1b57900b | ||
|
|
12b4898b3d | ||
|
|
f5f9bab77c | ||
|
|
ba7aa674d1 | ||
|
|
f670be4315 | ||
|
|
a658861fde | ||
|
|
192e5a1f79 | ||
|
|
475b15e372 | ||
|
|
d2ed91ccc8 | ||
|
|
333f4a1d3c | ||
|
|
832fe2f0b0 | ||
|
|
ea76185873 | ||
|
|
51c3ea184a | ||
|
|
692bccc4fc | ||
|
|
59fe76f29b | ||
|
|
51c2a0d732 | ||
|
|
2ab01b55c9 | ||
|
|
461f9e736b | ||
|
|
8e16b03ae8 | ||
|
|
6469809b9a | ||
|
|
0205adcedb | ||
|
|
0a0dbe115a | ||
|
|
a3fd36e2bb |
No files matched your search
@@ -1,3 +1,62 @@
|
||||
## Release 2.2.0
|
||||
|
||||
This release improves the way Weave Net configures Linux network
|
||||
devices and network filter rules, so that it is more robust in the face
|
||||
of unexpected changes in its environment. #3204,#3224
|
||||
|
||||
As a consequence of these changes, the `weave attach` command will now
|
||||
fail unless the Weave Net daemon is up and running - previously it was
|
||||
possible to run independently as long as you managed all IP addresses
|
||||
yourself.
|
||||
|
||||
## Other improvements
|
||||
|
||||
* Update library miekg/dns for CVE-2017-15133 (details under embargo) #3223,#3227
|
||||
* Reduce the volume of logging from weave-npc #3183
|
||||
* Add ability to set log level for Docker "v2" plugin, and change
|
||||
default log level from DEBUG to INFO #3197
|
||||
* Downgrade log messages about Discovery and Expiration to DEBUG level #3202,#3203
|
||||
* Use command-line parameter for WeaveDNS address in Docker proxy #3196
|
||||
|
||||
## Bug fixes
|
||||
|
||||
* Ensure that rules to block traffic for NetworkPolicy are placed
|
||||
ahead of rules that Kubernetes has added to allow other traffic #3209,#3210
|
||||
|
||||
## Build and test
|
||||
|
||||
* Update CI tests to use Kubernetes 1.9.2 #3229
|
||||
* Remove "daily update" from test VMs that only run for a few minutes #3224
|
||||
|
||||
## External Contributors
|
||||
|
||||
Thanks to the following contributors:
|
||||
@vetal4444
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/67?closed=1)
|
||||
|
||||
|
||||
## Release 2.1.3
|
||||
|
||||
This release fixes a race-condition in the IP reclaim code for weave-kube
|
||||
where two nodes could end up fighting over the same space and break
|
||||
connectivity #3190, #3192
|
||||
|
||||
## Release 2.1.2
|
||||
|
||||
This release fixes a couple of bugs discovered since the release of Weave Net 2.1.0
|
||||
|
||||
##Bug fixes
|
||||
|
||||
* Fix crash seen when starting 10-15 nodes simultaneously #3184,#3186
|
||||
* Fix NetworkPolicy blocking traffic if updates come out of order from Kubernetes #3177,#3181
|
||||
|
||||
Thanks to the following contributors:
|
||||
@zignig
|
||||
|
||||
[Full list of changes](https://github.com/weaveworks/weave/milestone/66?closed=1)
|
||||
|
||||
|
||||
## Release 2.1.1
|
||||
|
||||
As 2.1.0, but fixing a couple of installation glitches. #3175,#3176
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
DOCKER_VERSION=17.06
|
||||
KUBERNETES_VERSION=1.7.8
|
||||
KUBERNETES_CNI_VERSION=0.5.1
|
||||
KUBERNETES_VERSION=1.9.2
|
||||
KUBERNETES_CNI_VERSION=0.6.0
|
||||
+2
-2
@@ -196,7 +196,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 +232,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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -133,8 +133,8 @@ Finally, for **Mainline** releases only:
|
||||
* 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
|
||||
|
||||
+78
-36
@@ -227,12 +227,19 @@ 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
|
||||
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 {
|
||||
@@ -288,7 +295,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 +327,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 +377,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,7 +401,7 @@ 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 {
|
||||
@@ -402,12 +409,13 @@ func configureIPTables(config *BridgeConfig) error {
|
||||
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)
|
||||
@@ -438,45 +446,42 @@ 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 {
|
||||
return err
|
||||
}
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-m", "state", "--state", "NEW", "-j", "NFLOG", "--nflog-group", "86"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "DROP"); err != nil {
|
||||
return err
|
||||
}
|
||||
// If WEAVE-NPC chain doesn't exist then creating a rule in the chain will fail
|
||||
fwdRules = append(fwdRules,
|
||||
[][]string{
|
||||
{"-o", config.WeaveBridgeName,
|
||||
"-m", "comment", "--comment", "NOTE: this must go before '-j KUBE-FORWARD'",
|
||||
"-j", "WEAVE-NPC"},
|
||||
{"-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 {
|
||||
return err
|
||||
}
|
||||
fwdRules = append(fwdRules, []string{"-o", config.WeaveBridgeName, "-j", "WEAVE-EXPOSE"})
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
fwdRules = append(fwdRules, []string{"-i", config.WeaveBridgeName, "!", "-o", config.WeaveBridgeName, "-j", "ACCEPT"})
|
||||
// and allow replies back
|
||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"); err != nil {
|
||||
fwdRules = append(fwdRules, []string{"-o", config.WeaveBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"})
|
||||
|
||||
if err := ensureRulesAtTop("filter", "FORWARD", fwdRules, ipt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create a chain for masquerading
|
||||
if err = ipt.ClearChain("nat", "WEAVE"); err != nil {
|
||||
return errors.Wrap(err, "clearing WEAVE chain")
|
||||
}
|
||||
//
|
||||
// NB: we do not clear the chain to preserve existing rules
|
||||
// inserted by "weave expose".
|
||||
_ = ipt.NewChain("nat", "WEAVE")
|
||||
|
||||
return ipt.AppendUnique("nat", "POSTROUTING", "-j", "WEAVE")
|
||||
}
|
||||
|
||||
@@ -487,3 +492,40 @@ func linkSetUpByName(linkName string) error {
|
||||
}
|
||||
return netlink.LinkSetUp(link)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
+8
-3
@@ -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)
|
||||
}
|
||||
|
||||
@@ -116,7 +121,7 @@ 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)
|
||||
}
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ func (npc *controller) onNewNsSelector(selector *selector) error {
|
||||
for _, ns := range npc.nss {
|
||||
if ns.namespace != nil {
|
||||
if selector.matches(ns.namespace.ObjectMeta.Labels) {
|
||||
if err := selector.addEntry(string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
|
||||
if err := selector.addEntry(ns.namespace.ObjectMeta.UID, string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
+123
-55
@@ -10,13 +10,14 @@ import (
|
||||
coreapi "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
type mockSet struct {
|
||||
name ipset.Name
|
||||
setType ipset.Type
|
||||
subSets map[string]bool
|
||||
subSets map[string]map[types.UID]bool
|
||||
}
|
||||
|
||||
type mockIPSet struct {
|
||||
@@ -24,70 +25,57 @@ type mockIPSet struct {
|
||||
}
|
||||
|
||||
func newMockIPSet() mockIPSet {
|
||||
i := mockIPSet{
|
||||
sets: make(map[string]mockSet),
|
||||
}
|
||||
|
||||
return i
|
||||
return mockIPSet{sets: make(map[string]mockSet)}
|
||||
}
|
||||
|
||||
func (i *mockIPSet) Create(ipsetName ipset.Name, ipsetType ipset.Type) error {
|
||||
if _, ok := i.sets[string(ipsetName)]; ok {
|
||||
return errors.Errorf("ipset %s already exists", ipsetName)
|
||||
}
|
||||
i.sets[string(ipsetName)] = mockSet{name: ipsetName, setType: ipsetType, subSets: make(map[string]bool)}
|
||||
i.sets[string(ipsetName)] = mockSet{name: ipsetName, setType: ipsetType, subSets: make(map[string]map[types.UID]bool)}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *mockIPSet) AddEntry(ipsetName ipset.Name, entry string, comment string) error {
|
||||
return i.addEntry(ipsetName, entry, comment, true)
|
||||
}
|
||||
|
||||
func (i *mockIPSet) AddEntryIfNotExist(ipsetName ipset.Name, entry string, comment string) error {
|
||||
return i.addEntry(ipsetName, entry, comment, false)
|
||||
}
|
||||
|
||||
func (i *mockIPSet) addEntry(ipsetName ipset.Name, entry string, comment string, checkIfExists bool) error {
|
||||
log.Printf("adding entry %s to %s", entry, ipsetName)
|
||||
func (i *mockIPSet) AddEntry(user types.UID, ipsetName ipset.Name, entry string, comment string) error {
|
||||
log.Printf("adding entry %s to %s for %s", entry, ipsetName, user)
|
||||
if _, ok := i.sets[string(ipsetName)]; !ok {
|
||||
return errors.Errorf("ipset %s does not exist", entry)
|
||||
return errors.Errorf("%s does not exist", entry)
|
||||
}
|
||||
if checkIfExists {
|
||||
if _, ok := i.sets[string(ipsetName)].subSets[entry]; ok {
|
||||
return errors.Errorf("ipset %s is already a member of %s", entry, ipsetName)
|
||||
}
|
||||
if i.sets[string(ipsetName)].subSets[entry] == nil {
|
||||
i.sets[string(ipsetName)].subSets[entry] = make(map[types.UID]bool)
|
||||
}
|
||||
i.sets[string(ipsetName)].subSets[entry] = true
|
||||
if _, ok := i.sets[string(ipsetName)].subSets[entry][user]; ok {
|
||||
return errors.Errorf("user %s already owns entry %s", user, entry)
|
||||
}
|
||||
i.sets[string(ipsetName)].subSets[entry][user] = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *mockIPSet) DelEntry(ipsetName ipset.Name, entry string) error {
|
||||
return i.delEntry(ipsetName, entry, true)
|
||||
}
|
||||
|
||||
func (i *mockIPSet) DelEntryIfExists(ipsetName ipset.Name, entry string) error {
|
||||
return i.delEntry(ipsetName, entry, false)
|
||||
}
|
||||
|
||||
func (i *mockIPSet) delEntry(ipsetName ipset.Name, entry string, checkIfExists bool) error {
|
||||
log.Printf("deleting entry %s from %s", entry, ipsetName)
|
||||
func (i *mockIPSet) DelEntry(user types.UID, ipsetName ipset.Name, entry string) error {
|
||||
log.Printf("deleting entry %s from %s for %s", entry, ipsetName, user)
|
||||
if _, ok := i.sets[string(ipsetName)]; !ok {
|
||||
return errors.Errorf("ipset %s does not exist", ipsetName)
|
||||
}
|
||||
if checkIfExists {
|
||||
if _, ok := i.sets[string(ipsetName)].subSets[entry]; !ok {
|
||||
return errors.Errorf("ipset %s is not a member of %s", entry, ipsetName)
|
||||
}
|
||||
if _, ok := i.sets[string(ipsetName)].subSets[entry][user]; !ok {
|
||||
return errors.Errorf("user %s does not own entry %s", user, entry)
|
||||
}
|
||||
delete(i.sets[string(ipsetName)].subSets[entry], user)
|
||||
|
||||
if len(i.sets[string(ipsetName)].subSets[entry]) == 0 {
|
||||
delete(i.sets[string(ipsetName)].subSets, entry)
|
||||
}
|
||||
delete(i.sets[string(ipsetName)].subSets, entry)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *mockIPSet) Exist(ipsetName ipset.Name, entry string) bool {
|
||||
_, found := i.sets[string(ipsetName)].subSets[entry]
|
||||
return found
|
||||
func (i *mockIPSet) Exist(user types.UID, ipsetName ipset.Name, entry string) bool {
|
||||
_, ok := i.sets[string(ipsetName)].subSets[entry][user]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (i *mockIPSet) entryExists(ipsetName ipset.Name, entry string) bool {
|
||||
return len(i.sets[string(ipsetName)].subSets[entry]) > 0
|
||||
}
|
||||
|
||||
func (i *mockIPSet) Flush(ipsetName ipset.Name) error {
|
||||
@@ -191,7 +179,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
||||
|
||||
controller.AddNetworkPolicy(networkPolicy)
|
||||
|
||||
require.Equal(t, true, m.sets[selectorIPSetName].subSets[sourceIPSetName])
|
||||
require.Equal(t, true, len(m.sets[selectorIPSetName].subSets[sourceIPSetName]) > 0)
|
||||
|
||||
// NetworkPolicy first
|
||||
m = newMockIPSet()
|
||||
@@ -202,8 +190,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
||||
controller.AddNamespace(sourceNamespace)
|
||||
controller.AddNamespace(destinationNamespace)
|
||||
|
||||
require.Equal(t, true, m.sets[selectorIPSetName].subSets[sourceIPSetName])
|
||||
|
||||
require.Equal(t, true, len(m.sets[selectorIPSetName].subSets[sourceIPSetName]) > 0)
|
||||
}
|
||||
|
||||
// Tests default-allow ipset behavior when running in non-legacy mode.
|
||||
@@ -238,7 +225,7 @@ func TestDefaultAllow(t *testing.T) {
|
||||
controller.AddPod(podFoo)
|
||||
|
||||
// Should add the foo pod to default-allow
|
||||
require.True(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||
|
||||
podBar := &coreapi.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -252,7 +239,7 @@ func TestDefaultAllow(t *testing.T) {
|
||||
controller.UpdatePod(podBarNoIP, podBar)
|
||||
|
||||
// Should add the bar pod to default-allow
|
||||
require.True(t, m.Exist(defaultAllowIPSetName, barPodIP))
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, barPodIP))
|
||||
|
||||
// Allow access from the bar pod to the foo pod
|
||||
netpol := &networkingv1.NetworkPolicy{
|
||||
@@ -274,37 +261,118 @@ func TestDefaultAllow(t *testing.T) {
|
||||
controller.AddNetworkPolicy(netpol)
|
||||
|
||||
// Should remove the foo pod from default-allow as the netpol selects it
|
||||
require.False(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
||||
require.True(t, m.Exist(defaultAllowIPSetName, barPodIP))
|
||||
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, barPodIP))
|
||||
|
||||
podBarWithNewIP := *podBar
|
||||
podBarWithNewIP.Status.PodIP = barPodNewIP
|
||||
controller.UpdatePod(podBar, &podBarWithNewIP)
|
||||
|
||||
// Should update IP addr of the bar pod in default-allow
|
||||
require.False(t, m.Exist(defaultAllowIPSetName, barPodIP))
|
||||
require.True(t, m.Exist(defaultAllowIPSetName, barPodNewIP))
|
||||
require.False(t, m.entryExists(defaultAllowIPSetName, barPodIP))
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, barPodNewIP))
|
||||
|
||||
controller.UpdatePod(&podBarWithNewIP, podBarNoIP)
|
||||
// Should remove the bar pod from default-allow as it does not have any IP addr
|
||||
require.False(t, m.Exist(defaultAllowIPSetName, barPodNewIP))
|
||||
require.False(t, m.entryExists(defaultAllowIPSetName, barPodNewIP))
|
||||
|
||||
podFooWithNewLabel := *podFoo
|
||||
podFooWithNewLabel.ObjectMeta.Labels = map[string]string{"run": "new-foo"}
|
||||
controller.UpdatePod(podFoo, &podFooWithNewLabel)
|
||||
|
||||
// Should bring back the foo pod to default-allow as it does not match dst of any netpol
|
||||
require.True(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||
|
||||
controller.UpdatePod(&podFooWithNewLabel, podFoo)
|
||||
// Should remove from default-allow as it matches the netpol after the update
|
||||
require.False(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
||||
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||
|
||||
controller.DeleteNetworkPolicy(netpol)
|
||||
// Should bring back the foo pod to default-allow as no netpol selects it
|
||||
require.True(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||
|
||||
controller.DeletePod(podFoo)
|
||||
// Should remove foo pod from default-allow
|
||||
require.False(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
||||
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||
}
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
m := newMockIPSet()
|
||||
controller := New("qux", false, &mockIPTables{}, &m)
|
||||
|
||||
defaultNamespace := &coreapi.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default",
|
||||
},
|
||||
}
|
||||
controller.AddNamespace(defaultNamespace)
|
||||
|
||||
podFoo := &coreapi.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "foo",
|
||||
Namespace: "default",
|
||||
Name: "foo",
|
||||
Labels: map[string]string{"run": "foo"}},
|
||||
Status: coreapi.PodStatus{PodIP: podIP}}
|
||||
controller.AddPod(podFoo)
|
||||
|
||||
// Should be in default-allow as no netpol selects podFoo
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
|
||||
|
||||
netpol := &networkingv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "allow-from-bar-to-foo",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"run": "foo"}},
|
||||
Ingress: []networkingv1.NetworkPolicyIngressRule{{
|
||||
From: []networkingv1.NetworkPolicyPeer{{
|
||||
PodSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"run": "bar"},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
}
|
||||
controller.AddNetworkPolicy(netpol)
|
||||
|
||||
// Shouldn't be in default-allow as netpol above selects podFoo
|
||||
require.False(t, m.entryExists(defaultAllowIPSetName, podIP))
|
||||
|
||||
podBar := &coreapi.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "bar",
|
||||
Namespace: "default",
|
||||
Name: "bar",
|
||||
Labels: map[string]string{"run": "bar"}},
|
||||
Status: coreapi.PodStatus{PodIP: podIP}}
|
||||
controller.AddPod(podBar)
|
||||
|
||||
// Should be in default-allow as no netpol selects podBar
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
|
||||
require.True(t, m.Exist(podBar.ObjectMeta.UID, defaultAllowIPSetName, podIP))
|
||||
// Should be in run=bar ipset
|
||||
require.True(t, m.entryExists(runBarIPSetName, podIP))
|
||||
|
||||
controller.DeletePod(podFoo)
|
||||
// Multiple duplicate events should not affect npc state
|
||||
controller.DeletePod(podFoo)
|
||||
controller.DeletePod(podFoo)
|
||||
|
||||
// Should be in default-allow as no netpol selects podBar and podFoo removal
|
||||
// should not affect podBar in default-allow
|
||||
require.True(t, m.entryExists(defaultAllowIPSetName, podIP))
|
||||
|
||||
controller.DeletePod(podBar)
|
||||
|
||||
// Should remove from default-allow and run=bar ipsets
|
||||
require.Equal(t, 0, len(m.sets[defaultAllowIPSetName].subSets))
|
||||
require.False(t, m.entryExists(runBarIPSetName, podIP))
|
||||
}
|
||||
+81
-75
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
type Name string
|
||||
@@ -19,11 +20,9 @@ const (
|
||||
|
||||
type Interface interface {
|
||||
Create(ipsetName Name, ipsetType Type) error
|
||||
AddEntry(ipsetName Name, entry string, comment string) error
|
||||
AddEntryIfNotExist(ipsetName Name, entry string, comment string) error
|
||||
DelEntry(ipsetName Name, entry string) error
|
||||
DelEntryIfExists(ipsetName Name, entry string) error
|
||||
Exist(ipsetName Name, entry string) bool
|
||||
AddEntry(user types.UID, ipsetName Name, entry string, comment string) error
|
||||
DelEntry(user types.UID, ipsetName Name, entry string) error
|
||||
Exist(user types.UID, ipsetName Name, entry string) bool
|
||||
Flush(ipsetName Name) error
|
||||
Destroy(ipsetName Name) error
|
||||
|
||||
@@ -33,14 +32,27 @@ type Interface interface {
|
||||
DestroyAll() error
|
||||
}
|
||||
|
||||
type entryKey struct {
|
||||
ipsetName Name
|
||||
entry string
|
||||
}
|
||||
|
||||
type ipset struct {
|
||||
refCount
|
||||
*log.Logger
|
||||
enableComments bool
|
||||
// List of users per ipset entry. User is either a namespace or a pod.
|
||||
// There might be multiple users for the same ipset & entry pair because
|
||||
// events from k8s API server might be out of order causing duplicate IPs:
|
||||
// https://github.com/weaveworks/weave/issues/2792.
|
||||
users map[entryKey]map[types.UID]struct{}
|
||||
}
|
||||
|
||||
func New(logger *log.Logger) Interface {
|
||||
ips := &ipset{refCount: newRefCount(), Logger: logger, enableComments: true}
|
||||
ips := &ipset{
|
||||
Logger: logger,
|
||||
enableComments: true,
|
||||
users: make(map[entryKey]map[types.UID]struct{}),
|
||||
}
|
||||
|
||||
// Check for comment support
|
||||
testIpsetName := Name("weave-test-comment")
|
||||
@@ -65,11 +77,15 @@ func (i *ipset) Create(ipsetName Name, ipsetType Type) error {
|
||||
return doExec(args...)
|
||||
}
|
||||
|
||||
func (i *ipset) AddEntry(ipsetName Name, entry string, comment string) error {
|
||||
i.Logger.Printf("adding entry %s to %s", entry, ipsetName)
|
||||
if i.inc(ipsetName, entry) > 1 { // already in the set
|
||||
func (i *ipset) AddEntry(user types.UID, ipsetName Name, entry string, comment string) error {
|
||||
i.Logger.Printf("adding entry %s to %s of %s", entry, ipsetName, user)
|
||||
|
||||
if !i.addUser(user, ipsetName, entry) { // already in the set
|
||||
return nil
|
||||
}
|
||||
|
||||
i.Logger.Printf("added entry %s to %s of %s", entry, ipsetName, user)
|
||||
|
||||
args := []string{"add", string(ipsetName), entry}
|
||||
if i.enableComments {
|
||||
args = append(args, "comment", comment)
|
||||
@@ -77,53 +93,39 @@ func (i *ipset) AddEntry(ipsetName Name, entry string, comment string) error {
|
||||
return doExec(args...)
|
||||
}
|
||||
|
||||
// AddEntryIfNotExist does the same as AddEntry but bypasses the ref counting.
|
||||
// Should be used only with "default-allow" ipsets.
|
||||
func (i *ipset) AddEntryIfNotExist(ipsetName Name, entry string, comment string) error {
|
||||
if i.count(ipsetName, entry) == 1 {
|
||||
return nil
|
||||
}
|
||||
return i.AddEntry(ipsetName, entry, comment)
|
||||
}
|
||||
func (i *ipset) DelEntry(user types.UID, ipsetName Name, entry string) error {
|
||||
i.Logger.Printf("deleting entry %s from %s of %s", entry, ipsetName, user)
|
||||
|
||||
func (i *ipset) DelEntry(ipsetName Name, entry string) error {
|
||||
i.Logger.Printf("deleting entry %s from %s", entry, ipsetName)
|
||||
if i.dec(ipsetName, entry) > 0 { // still needed
|
||||
if !i.delUser(user, ipsetName, entry) { // still needed
|
||||
return nil
|
||||
}
|
||||
|
||||
i.Logger.Printf("deleted entry %s from %s of %s", entry, ipsetName, user)
|
||||
|
||||
return doExec("del", string(ipsetName), entry)
|
||||
}
|
||||
|
||||
// DelEntryIfExists does the same as DelEntry but bypasses the ref counting.
|
||||
// Should be used only with "default-allow" ipsets.
|
||||
func (i *ipset) DelEntryIfExists(ipsetName Name, entry string) error {
|
||||
if i.count(ipsetName, entry) == 0 {
|
||||
return nil
|
||||
}
|
||||
return i.DelEntry(ipsetName, entry)
|
||||
}
|
||||
|
||||
func (i *ipset) Exist(ipsetName Name, entry string) bool {
|
||||
return i.count(ipsetName, entry) > 0
|
||||
func (i *ipset) Exist(user types.UID, ipsetName Name, entry string) bool {
|
||||
return i.existUser(user, ipsetName, entry)
|
||||
}
|
||||
|
||||
func (i *ipset) Flush(ipsetName Name) error {
|
||||
i.removeSet(ipsetName)
|
||||
i.removeSetFromUsers(ipsetName)
|
||||
return doExec("flush", string(ipsetName))
|
||||
}
|
||||
|
||||
func (i *ipset) FlushAll() error {
|
||||
i.refCount = newRefCount()
|
||||
i.users = make(map[entryKey]map[types.UID]struct{})
|
||||
return doExec("flush")
|
||||
}
|
||||
|
||||
func (i *ipset) Destroy(ipsetName Name) error {
|
||||
i.removeSet(ipsetName)
|
||||
i.removeSetFromUsers(ipsetName)
|
||||
return doExec("destroy", string(ipsetName))
|
||||
}
|
||||
|
||||
func (i *ipset) DestroyAll() error {
|
||||
i.refCount = newRefCount()
|
||||
i.users = make(map[entryKey]map[types.UID]struct{})
|
||||
return doExec("destroy")
|
||||
}
|
||||
|
||||
@@ -145,48 +147,52 @@ func (i *ipset) List(prefix string) ([]Name, error) {
|
||||
return selected, err
|
||||
}
|
||||
|
||||
// Returns true if entry does not exist in ipset (entry has to be inserted into ipset).
|
||||
func (i *ipset) addUser(user types.UID, ipsetName Name, entry string) bool {
|
||||
k := entryKey{ipsetName, entry}
|
||||
add := false
|
||||
|
||||
if i.users[k] == nil {
|
||||
i.users[k] = make(map[types.UID]struct{})
|
||||
}
|
||||
if len(i.users[k]) == 0 {
|
||||
add = true
|
||||
}
|
||||
i.users[k][user] = struct{}{}
|
||||
|
||||
return add
|
||||
}
|
||||
|
||||
// Returns true if user is the last owner of entry (entry has to be removed from ipset).
|
||||
func (i *ipset) delUser(user types.UID, ipsetName Name, entry string) bool {
|
||||
k := entryKey{ipsetName, entry}
|
||||
|
||||
oneLeft := len(i.users[k]) == 1
|
||||
delete(i.users[k], user)
|
||||
|
||||
if len(i.users[k]) == 0 {
|
||||
delete(i.users, k)
|
||||
}
|
||||
|
||||
return oneLeft && (len(i.users[k]) == 0)
|
||||
}
|
||||
|
||||
func (i *ipset) existUser(user types.UID, ipsetName Name, entry string) bool {
|
||||
_, ok := i.users[entryKey{ipsetName, entry}][user]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (i *ipset) removeSetFromUsers(ipsetName Name) {
|
||||
for k := range i.users {
|
||||
if k.ipsetName == ipsetName {
|
||||
delete(i.users, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func doExec(args ...string) error {
|
||||
if output, err := exec.Command("ipset", args...).CombinedOutput(); err != nil {
|
||||
return errors.Wrapf(err, "ipset %v failed: %s", args, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reference-counting
|
||||
type key struct {
|
||||
ipsetName Name
|
||||
entry string
|
||||
}
|
||||
|
||||
// note no locking is required as all operations are serialised in the controller
|
||||
type refCount struct {
|
||||
ref map[key]int
|
||||
}
|
||||
|
||||
func newRefCount() refCount {
|
||||
return refCount{ref: make(map[key]int)}
|
||||
}
|
||||
|
||||
func (rc *refCount) inc(ipsetName Name, entry string) int {
|
||||
k := key{ipsetName, entry}
|
||||
rc.ref[k]++
|
||||
return rc.ref[k]
|
||||
}
|
||||
|
||||
func (rc *refCount) dec(ipsetName Name, entry string) int {
|
||||
k := key{ipsetName, entry}
|
||||
rc.ref[k]--
|
||||
return rc.ref[k]
|
||||
}
|
||||
|
||||
func (rc *refCount) count(ipsetName Name, entry string) int {
|
||||
return rc.ref[key{ipsetName, entry}]
|
||||
}
|
||||
|
||||
func (rc *refCount) removeSet(ipsetName Name) {
|
||||
for k := range rc.ref {
|
||||
if k.ipsetName == ipsetName {
|
||||
delete(rc.ref, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
+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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
-25
@@ -92,7 +92,7 @@ func (ns *ns) onNewPodSelector(selector *selector) error {
|
||||
for _, pod := range ns.pods {
|
||||
if hasIP(pod) {
|
||||
if selector.matches(pod.ObjectMeta.Labels) {
|
||||
if err := selector.addEntry(pod.Status.PodIP, podComment(pod)); err != nil {
|
||||
if err := selector.addEntry(pod.ObjectMeta.UID, pod.Status.PodIP, podComment(pod)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ func (ns *ns) onNewDstPodSelector(selector *selector) error {
|
||||
if hasIP(pod) {
|
||||
// Remove the pod from default-allow if dst podselector matches the pod
|
||||
if selector.matches(pod.ObjectMeta.Labels) {
|
||||
if err := ns.ips.DelEntryIfExists(ns.defaultAllowIPSet, pod.Status.PodIP); err != nil {
|
||||
if err := ns.ips.DelEntry(pod.ObjectMeta.UID, ns.defaultAllowIPSet, pod.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -150,7 +150,7 @@ func (ns *ns) addToDefaultAllowIfNoMatching(pod *coreapi.Pod) error {
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
if err := ns.ips.AddEntryIfNotExist(ns.defaultAllowIPSet, pod.Status.PodIP, podComment(pod)); err != nil {
|
||||
if err := ns.ips.AddEntry(pod.ObjectMeta.UID, ns.defaultAllowIPSet, pod.Status.PodIP, podComment(pod)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -172,16 +172,16 @@ func (ns *ns) addPod(obj *coreapi.Pod) error {
|
||||
}
|
||||
|
||||
if ns.checkLocalPod(obj) {
|
||||
ns.ips.AddEntry(LocalIpset, obj.Status.PodIP, podComment(obj))
|
||||
ns.ips.AddEntry(obj.ObjectMeta.UID, LocalIpset, obj.Status.PodIP, podComment(obj))
|
||||
}
|
||||
|
||||
found, err := ns.podSelectors.addToMatching(obj.ObjectMeta.Labels, obj.Status.PodIP, podComment(obj))
|
||||
found, 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.AddEntryIfNotExist(ns.defaultAllowIPSet, obj.Status.PodIP, podComment(obj)); err != nil {
|
||||
if err := ns.ips.AddEntry(obj.ObjectMeta.UID, ns.defaultAllowIPSet, obj.Status.PodIP, podComment(obj)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -199,29 +199,29 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
|
||||
if hasIP(oldObj) && !hasIP(newObj) {
|
||||
if ns.checkLocalPod(oldObj) {
|
||||
ns.ips.DelEntry(LocalIpset, oldObj.Status.PodIP)
|
||||
ns.ips.DelEntry(oldObj.ObjectMeta.UID, LocalIpset, oldObj.Status.PodIP)
|
||||
}
|
||||
|
||||
if !ns.legacy {
|
||||
if err := ns.ips.DelEntryIfExists(ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return ns.podSelectors.delFromMatching(oldObj.ObjectMeta.Labels, oldObj.Status.PodIP)
|
||||
return ns.podSelectors.delFromMatching(oldObj.ObjectMeta.UID, oldObj.ObjectMeta.Labels, oldObj.Status.PodIP)
|
||||
}
|
||||
|
||||
if !hasIP(oldObj) && hasIP(newObj) {
|
||||
if ns.checkLocalPod(newObj) {
|
||||
ns.ips.AddEntry(LocalIpset, newObj.Status.PodIP, podComment(newObj))
|
||||
ns.ips.AddEntry(newObj.ObjectMeta.UID, LocalIpset, newObj.Status.PodIP, podComment(newObj))
|
||||
}
|
||||
found, err := ns.podSelectors.addToMatching(newObj.ObjectMeta.Labels, newObj.Status.PodIP, podComment(newObj))
|
||||
found, 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.AddEntryIfNotExist(ns.defaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.defaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -231,12 +231,15 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
|
||||
if !ns.legacy &&
|
||||
oldObj.Status.PodIP != newObj.Status.PodIP &&
|
||||
ns.ips.Exist(ns.defaultAllowIPSet, oldObj.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.DelEntryIfExists(ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ns.ips.AddEntryIfNotExist(ns.defaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||
if err := ns.ips.AddEntry(newObj.ObjectMeta.UID, ns.defaultAllowIPSet, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -251,12 +254,12 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
continue
|
||||
}
|
||||
if oldMatch {
|
||||
if err := ps.delEntry(oldObj.Status.PodIP); err != nil {
|
||||
if err := ps.delEntry(oldObj.ObjectMeta.UID, oldObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if newMatch {
|
||||
if err := ps.addEntry(newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||
if err := ps.addEntry(newObj.ObjectMeta.UID, newObj.Status.PodIP, podComment(newObj)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -264,7 +267,7 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
||||
if !ns.legacy && ns.podSelectors.dstSelectorExist(ps) {
|
||||
switch {
|
||||
case !oldMatch && newMatch:
|
||||
if err := ns.ips.DelEntryIfExists(ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||
if err := ns.ips.DelEntry(oldObj.ObjectMeta.UID, ns.defaultAllowIPSet, oldObj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
case oldMatch && !newMatch:
|
||||
@@ -287,16 +290,16 @@ func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
||||
}
|
||||
|
||||
if ns.checkLocalPod(obj) {
|
||||
ns.ips.DelEntry(LocalIpset, obj.Status.PodIP)
|
||||
ns.ips.DelEntry(obj.ObjectMeta.UID, LocalIpset, obj.Status.PodIP)
|
||||
}
|
||||
|
||||
if !ns.legacy {
|
||||
if err := ns.ips.DelEntryIfExists(ns.defaultAllowIPSet, obj.Status.PodIP); err != nil {
|
||||
if err := ns.ips.DelEntry(obj.ObjectMeta.UID, ns.defaultAllowIPSet, obj.Status.PodIP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return ns.podSelectors.delFromMatching(obj.ObjectMeta.Labels, obj.Status.PodIP)
|
||||
return ns.podSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, obj.Status.PodIP)
|
||||
}
|
||||
|
||||
func (ns *ns) addNetworkPolicy(obj interface{}) error {
|
||||
@@ -408,7 +411,7 @@ func (ns *ns) addNamespace(obj *coreapi.Namespace) error {
|
||||
}
|
||||
|
||||
// Add namespace ipset to matching namespace selectors
|
||||
_, err := ns.nsSelectors.addToMatching(obj.ObjectMeta.Labels, string(ns.allPods.ipsetName), namespaceComment(ns))
|
||||
_, err := ns.nsSelectors.addToMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, string(ns.allPods.ipsetName), namespaceComment(ns))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -442,12 +445,12 @@ func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
||||
continue
|
||||
}
|
||||
if oldMatch {
|
||||
if err := selector.delEntry(string(ns.allPods.ipsetName)); err != nil {
|
||||
if err := selector.delEntry(ns.namespace.ObjectMeta.UID, string(ns.allPods.ipsetName)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if newMatch {
|
||||
if err := selector.addEntry(string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
|
||||
if err := selector.addEntry(ns.namespace.ObjectMeta.UID, string(ns.allPods.ipsetName), namespaceComment(ns)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -468,7 +471,7 @@ func (ns *ns) deleteNamespace(obj *coreapi.Namespace) error {
|
||||
}
|
||||
|
||||
// Remove namespace ipset from any matching namespace selectors
|
||||
return ns.nsSelectors.delFromMatching(obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
||||
return ns.nsSelectors.delFromMatching(obj.ObjectMeta.UID, obj.ObjectMeta.Labels, string(ns.allPods.ipsetName))
|
||||
}
|
||||
|
||||
func (ns *ns) isDefaultDeny(namespace *coreapi.Namespace) bool {
|
||||
|
||||
+8
-8
@@ -46,12 +46,12 @@ func (s *selector) matches(labelMap map[string]string) bool {
|
||||
return s.spec.selector.Matches(labels.Set(labelMap))
|
||||
}
|
||||
|
||||
func (s *selector) addEntry(entry string, comment string) error {
|
||||
return s.ips.AddEntry(s.spec.ipsetName, entry, comment)
|
||||
func (s *selector) addEntry(user types.UID, entry string, comment string) error {
|
||||
return s.ips.AddEntry(user, s.spec.ipsetName, entry, comment)
|
||||
}
|
||||
|
||||
func (s *selector) delEntry(entry string) error {
|
||||
return s.ips.DelEntry(s.spec.ipsetName, entry)
|
||||
func (s *selector) delEntry(user types.UID, entry string) error {
|
||||
return s.ips.DelEntry(user, s.spec.ipsetName, entry)
|
||||
}
|
||||
|
||||
type selectorFn func(selector *selector) error
|
||||
@@ -85,14 +85,14 @@ func newSelectorSet(ips ipset.Interface, onNewSelector, onNewDstSelector selecto
|
||||
dstSelectorsCount: make(map[string]int)}
|
||||
}
|
||||
|
||||
func (ss *selectorSet) addToMatching(labelMap map[string]string, entry string, comment string) (bool, error) {
|
||||
func (ss *selectorSet) addToMatching(user types.UID, labelMap map[string]string, entry string, comment string) (bool, error) {
|
||||
found := false
|
||||
for _, s := range ss.entries {
|
||||
if s.matches(labelMap) {
|
||||
if ss.dstSelectorExist(s) {
|
||||
found = true
|
||||
}
|
||||
if err := s.addEntry(entry, comment); err != nil {
|
||||
if err := s.addEntry(user, entry, comment); err != nil {
|
||||
return found, err
|
||||
}
|
||||
}
|
||||
@@ -100,10 +100,10 @@ func (ss *selectorSet) addToMatching(labelMap map[string]string, entry string, c
|
||||
return found, nil
|
||||
}
|
||||
|
||||
func (ss *selectorSet) delFromMatching(labelMap map[string]string, entry string) error {
|
||||
func (ss *selectorSet) delFromMatching(user types.UID, labelMap map[string]string, entry string) error {
|
||||
for _, s := range ss.entries {
|
||||
if s.matches(labelMap) {
|
||||
if err := s.delEntry(entry); err != nil {
|
||||
if err := s.delEntry(user, entry); err != nil {
|
||||
return 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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,9 @@ func checkIamInPeerList(cml *configMapAnnotations, c *kubernetes.Clientset, peer
|
||||
// Kubernetes, remove it from Weave IPAM
|
||||
func reclaimRemovedPeers(weave *weaveapi.Client, cml *configMapAnnotations, nodes []nodeInfo, myPeerName string) error {
|
||||
for {
|
||||
if err := cml.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
// 1. Compare peers stored in the peerList against all peers reported by k8s now.
|
||||
storedPeerList, err := cml.GetPeerList()
|
||||
if err != nil {
|
||||
@@ -119,7 +122,7 @@ func reclaimRemovedPeers(weave *weaveapi.Client, cml *configMapAnnotations, node
|
||||
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,14 +131,16 @@ 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
|
||||
}
|
||||
@@ -146,15 +151,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
|
||||
|
||||
|
||||
@@ -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) \
|
||||
|
||||
+19
-13
@@ -45,12 +45,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 {
|
||||
@@ -193,10 +201,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")
|
||||
@@ -263,6 +271,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
|
||||
}
|
||||
@@ -635,17 +645,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
|
||||
}
|
||||
@@ -17,7 +17,6 @@ func init() {
|
||||
"help": help,
|
||||
"netcheck": netcheck,
|
||||
"docker-tls-args": dockerTLSArgs,
|
||||
"create-bridge": createBridge,
|
||||
"detect-bridge-type": detectBridgeType,
|
||||
"create-datapath": createDatapath,
|
||||
"delete-datapath": deleteDatapath,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,7 +11,9 @@ The following topics are discussed:
|
||||
* [Upgrading the Daemon Sets](#daemon-sets)
|
||||
* [CPU and Memory Requirements](#resources)
|
||||
* [Pod Eviction](#eviction)
|
||||
* [Network Policy Controller](#npc)
|
||||
* [Features](#features)
|
||||
* [Pod Network](#pod-network)
|
||||
* [Network Policy](#npc)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
* [Troubleshooting Blocked Connections](#blocked-connections)
|
||||
* [Things to watch out for](#key-points)
|
||||
@@ -147,16 +149,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,6 +186,11 @@ definition](http://kubernetes.io/docs/api-reference/extensions/v1beta1/definitio
|
||||
|
||||
## <a name="troubleshooting"></a> Troubleshooting
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
@@ -299,11 +323,14 @@ You can customise the YAML you get from `cloud.weave.works` by passing some of W
|
||||
- `disable-npc`: boolean (`true|false`). Default: `false`.
|
||||
- `env.NAME=VALUE`: add environment variable `NAME` and set it to `VALUE`.
|
||||
- `seLinuxOptions.NAME=VALUE`: add SELinux option `NAME` and set it to `VALUE`, e.g. `seLinuxOptions.type=spc_t`
|
||||
- `use-legacy-netpol`: use [legacy NetworkPolicy semantics](https://v1-6.docs.kubernetes.io/docs/api-reference/v1.6/#networkpolicy-v1beta1-extensions), boolean (`true|false`). Default: `true` for Kubernetes version <= 1.6, `false` for > 1.6.
|
||||
|
||||
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
|
||||
|
||||
@@ -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,6 +59,8 @@ 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
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
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
tear_down_kubeadm() {
|
||||
for host in $HOSTS; do
|
||||
run_on $host "sudo kubeadm reset && 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.
|
||||
|
||||
@@ -33,9 +38,9 @@ k8s_version_option="$([[ "$k8s_version" > "v1.6" ]] && echo "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 --$k8s_version_option=$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
|
||||
|
||||
@@ -134,6 +139,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() {
|
||||
@@ -170,6 +191,15 @@ 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"
|
||||
|
||||
# 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"
|
||||
|
||||
# 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
|
||||
apiVersion: networking.k8s.io/v1
|
||||
@@ -211,6 +241,10 @@ EOF
|
||||
|
||||
assert_raises "$SSH $HOST1 $KUBECTL exec $denyPodName -- curl -s -S -f -m 2 http://$DOMAIN:8080/status >/dev/null"
|
||||
|
||||
# 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"
|
||||
|
||||
tear_down_kubeadm
|
||||
|
||||
# Destroy our test ipset, and implicitly check it is still there
|
||||
|
||||
@@ -44,7 +44,7 @@ function setup_kubernetes_cluster {
|
||||
if [ $host = $HOST1 ] ; then
|
||||
run_on $host "sudo systemctl start kubelet && sudo kubeadm init --$k8s_version_option=$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
|
||||
|
||||
|
||||
+1
-1
Submodule tools updated: 74dc626b6d...ca69607f79.
+1
-1
Submodule vendor/github.com/miekg/dns updated: c9d1302d54...5364553f1e.
+1
-1
Submodule vendor/github.com/vishvananda/netlink updated: c19091b1c6...f67b75edbf.
+1
-1
Submodule vendor/github.com/weaveworks/mesh updated: 9a38245587...38e8100dfb.
@@ -405,27 +405,6 @@ detect_bridge_type() {
|
||||
MTU=${WEAVE_MTU:-$(cat /sys/class/net/$BRIDGE/mtu)}
|
||||
}
|
||||
|
||||
try_create_bridge() {
|
||||
# Pass /dev/null because we do not have access to the weavedb files when this runs; BoltDB
|
||||
# cannot open the file anyway if weaver is running, which is likely for attach|expose|hide.
|
||||
MAC=$(util_op unique-id "/dev/null" "$HOST_ROOT")
|
||||
BRIDGE_TYPE=$(util_op create-bridge "$DOCKER_BRIDGE" "$BRIDGE" "$DATAPATH" "$WEAVE_MTU" "$PORT" "$MAC" "$WEAVE_NO_FASTDP" "$WEAVE_NO_BRIDGED_FASTDP" "$PROC_PATH" "$1")
|
||||
# Set some variables that are expected by code that comes later
|
||||
[ "$BRIDGE_TYPE" != "fastdp" ] || DATAPATH="$BRIDGE"
|
||||
MTU=${WEAVE_MTU:-$(cat /sys/class/net/$BRIDGE/mtu)}
|
||||
}
|
||||
|
||||
create_bridge() {
|
||||
validate_bridge_type
|
||||
if ! try_create_bridge "$@" ; then
|
||||
echo "Creating bridge '$BRIDGE' failed" >&2
|
||||
# reset to original value so we destroy both kinds
|
||||
DATAPATH=datapath
|
||||
destroy_bridge
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
validate_bridge_type() {
|
||||
detect_bridge_type || return 0
|
||||
|
||||
@@ -557,6 +536,7 @@ router_bridge_opts() {
|
||||
echo --docker-bridge "$DOCKER_BRIDGE" --weave-bridge "$BRIDGE" --datapath "$DATAPATH"
|
||||
[ -z "$WEAVE_MTU" ] || echo --mtu "$WEAVE_MTU"
|
||||
[ -z $WEAVE_NO_FASTDP ] || echo --no-fastdp
|
||||
[ -z $WEAVE_NO_BRIDGED_FASTDP ] || echo --no-bridged-fastdp
|
||||
}
|
||||
|
||||
######################################################################
|
||||
@@ -918,9 +898,7 @@ check_overlap() {
|
||||
}
|
||||
|
||||
detect_awsvpc() {
|
||||
# Ignoring errors here: if we cannot detect AWSVPC we will skip the relevant
|
||||
# steps, because "attach" should work without the weave router running.
|
||||
[ "$(call_weave GET /ipinfo/tracker)" != "awsvpc" ] || AWSVPC=1
|
||||
[ "$(call_weave GET /ipinfo/tracker 2>/dev/null)" != "awsvpc" ] || AWSVPC=1
|
||||
}
|
||||
|
||||
# Call IPAM as necessary to lookup or allocate addresses
|
||||
@@ -1256,7 +1234,6 @@ launch() {
|
||||
--host-root=/host \
|
||||
$(router_bridge_opts) \
|
||||
--ipalloc-range "$IPRANGE" \
|
||||
--dns-effective-listen-address $DOCKER_BRIDGE_IP \
|
||||
--dns-listen-address $DOCKER_BRIDGE_IP:53 \
|
||||
--http-addr $HTTP_ADDR \
|
||||
--status-addr $STATUS_ADDR \
|
||||
@@ -1436,7 +1413,6 @@ case "$COMMAND" in
|
||||
done
|
||||
[ $# -eq 1 ] || usage
|
||||
CONTAINER=$(util_op container-id $1)
|
||||
create_bridge
|
||||
ipam_cidrs allocate $CONTAINER $CIDR_ARGS
|
||||
if [ -n "$REWRITE_HOSTS" ] ; then
|
||||
# rewrite /etc/hosts, unlinking the file (so Docker does
|
||||
@@ -1444,7 +1420,7 @@ case "$COMMAND" in
|
||||
util_op rewrite-etc-hosts "$CONTAINER" "$EXEC_IMAGE" "$ALL_CIDRS" $DNS_EXTRA_HOSTS
|
||||
fi
|
||||
[ -n "$AWSVPC" ] && ATTACH_ARGS="--no-multicast-route --keep-tx-on"
|
||||
util_op attach-container $ATTACH_ARGS $CONTAINER $BRIDGE $MTU $ALL_CIDRS >/dev/null
|
||||
util_op attach-container $ATTACH_ARGS $CONTAINER $BRIDGE $ALL_CIDRS >/dev/null
|
||||
[ -n "$WITHOUT_DNS" ] || when_weave_running with_container_fqdn $CONTAINER put_dns_fqdn $ALL_CIDRS
|
||||
show_addrs $ALL_CIDRS
|
||||
;;
|
||||
@@ -1494,7 +1470,6 @@ case "$COMMAND" in
|
||||
[ $# -eq 2 -a "$1" = "-h" ] || usage
|
||||
FQDN="$2"
|
||||
fi
|
||||
create_bridge
|
||||
expose_ip
|
||||
show_addrs $ALL_CIDRS
|
||||
;;
|
||||
@@ -1502,7 +1477,6 @@ case "$COMMAND" in
|
||||
collect_cidr_args "$@"
|
||||
shift $CIDR_ARG_COUNT
|
||||
ipam_cidrs lookup weave:expose $CIDR_ARGS
|
||||
create_bridge
|
||||
for CIDR in $ALL_CIDRS ; do
|
||||
if ip addr show dev $BRIDGE | grep -qF $CIDR ; then
|
||||
ip addr del dev $BRIDGE $CIDR
|
||||
|
||||
Reference in new issue
Block a user