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 | ||
|
|
ebbf3d4244 | ||
|
|
ceb36d1891 | ||
|
|
4caa850853 | ||
|
|
96cecf1796 | ||
|
|
51c7af8fb4 | ||
|
|
a3fd36e2bb |
No files matched your search
+64
-1
@@ -1,3 +1,66 @@
|
|||||||
|
## 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
|
||||||
|
|
||||||
## Release 2.1.0
|
## Release 2.1.0
|
||||||
|
|
||||||
##New Features
|
##New Features
|
||||||
@@ -35,7 +98,7 @@ addresses when many nodes are added and deleted over a long period.
|
|||||||
* Images are now also built for the ppc64le platform #3129
|
* Images are now also built for the ppc64le platform #3129
|
||||||
* Tweak build scripts to run on OSX as well as Linux #3135
|
* Tweak build scripts to run on OSX as well as Linux #3135
|
||||||
|
|
||||||
##External Contributers
|
##External Contributors
|
||||||
|
|
||||||
Thanks to the following contributors:
|
Thanks to the following contributors:
|
||||||
@caarlos0
|
@caarlos0
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
DOCKER_VERSION=17.06
|
DOCKER_VERSION=17.06
|
||||||
KUBERNETES_VERSION=1.7.8
|
KUBERNETES_VERSION=1.9.2
|
||||||
KUBERNETES_CNI_VERSION=0.5.1
|
KUBERNETES_CNI_VERSION=0.6.0
|
||||||
+2
-2
@@ -196,7 +196,7 @@ publish() {
|
|||||||
|
|
||||||
if [ "$RELEASE_TYPE" = 'PRERELEASE' ] ; then
|
if [ "$RELEASE_TYPE" = 'PRERELEASE' ] ; then
|
||||||
echo "== Building and pushing plugin"
|
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 "** Plugin built and pushed"
|
||||||
|
|
||||||
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
|
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 '** Sanity checks OK for publishing tag' $LATEST_TAG as $DOCKERHUB_USER/weave:$VERSION
|
||||||
|
|
||||||
echo "== Building and pushing plugin"
|
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 "** Plugin built and pushed"
|
||||||
|
|
||||||
echo "== Tagging and pushing images on docker hub as user $DOCKERHUB_USER"
|
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 {
|
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()
|
dpif, err := odp.NewDpif()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -87,5 +95,9 @@ func AddDatapathInterface(dpname string, ifname string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err = dp.CreateVport(odp.NewNetdevVportSpec(ifname))
|
_, err = dp.CreateVport(odp.NewNetdevVportSpec(ifname))
|
||||||
|
if ignoreIfExist && err == odp.NetlinkError(syscall.EEXIST) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
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
|
* 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)
|
* 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
|
* 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
|
tag, push to GitHub and set [WEAVE_NET_REV](https://github.com/weaveworks/website-next/blob/master/Makefile)
|
||||||
published to https://www.weave.works
|
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
|
* Add the new version of `weave-net` to the checkpoint system at
|
||||||
https://checkpoint-api.weave.works/admin
|
https://checkpoint-api.weave.works/admin
|
||||||
* File a PR to update the version of the daemonset at
|
* File a PR to update the version of the daemonset at
|
||||||
|
|||||||
+78
-36
@@ -227,12 +227,19 @@ func (config *BridgeConfig) configuredBridgeType() Bridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger) (Bridge, error) {
|
func EnsureBridge(procPath string, config *BridgeConfig, log *logrus.Logger) (Bridge, error) {
|
||||||
bridgeType, err := ExistingBridgeType(config.WeaveBridgeName, config.DatapathName)
|
existingBridgeType, err := ExistingBridgeType(config.WeaveBridgeName, config.DatapathName)
|
||||||
if bridgeType != nil || err != nil {
|
if err != nil {
|
||||||
return bridgeType, err
|
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 {
|
for {
|
||||||
if err := bridgeType.init(config); err != nil {
|
if err := bridgeType.init(config); err != nil {
|
||||||
if errors.Cause(err) == errBridgeNotSupported {
|
if errors.Cause(err) == errBridgeNotSupported {
|
||||||
@@ -288,7 +295,7 @@ func (b bridgeImpl) initPrep(config *BridgeConfig) error {
|
|||||||
config.MTU = 65535
|
config.MTU = 65535
|
||||||
}
|
}
|
||||||
b.bridge = &netlink.Bridge{LinkAttrs: linkAttrs}
|
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)
|
return errors.Wrapf(err, "creating bridge %q", config.WeaveBridgeName)
|
||||||
}
|
}
|
||||||
if err := netlink.LinkSetHardwareAddr(b.bridge, mac); err != nil {
|
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 {
|
if err := b.initPrep(config); err != nil {
|
||||||
return err
|
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)
|
return netlink.LinkSetUp(veth)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return errors.Wrap(err, "creating pcap veth pair")
|
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 {
|
if err := bf.bridgeImpl.initPrep(config); err != nil {
|
||||||
return err
|
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 {
|
if err := netlink.LinkSetUp(veth); err != nil {
|
||||||
return errors.Wrapf(err, "setting link up on %q", veth.Attrs().Name)
|
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 errors.Wrapf(err, "adding interface %q to datapath %q", veth.Attrs().Name, bf.datapathName)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -394,7 +401,7 @@ func (bf bridgedFastdpImpl) attach(veth *netlink.Veth) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f fastdpImpl) attach(veth *netlink.Veth) error {
|
func (f fastdpImpl) attach(veth *netlink.Veth) error {
|
||||||
return odp.AddDatapathInterface(f.datapathName, veth.Attrs().Name)
|
return odp.AddDatapathInterfaceIfNotExist(f.datapathName, veth.Attrs().Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureIPTables(config *BridgeConfig) error {
|
func configureIPTables(config *BridgeConfig) error {
|
||||||
@@ -402,12 +409,13 @@ func configureIPTables(config *BridgeConfig) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "creating iptables object")
|
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.DockerBridgeName != "" {
|
||||||
if config.WeaveBridgeName != config.DockerBridgeName {
|
if config.WeaveBridgeName != config.DockerBridgeName {
|
||||||
err := ipt.Insert("filter", "FORWARD", 1, "-i", config.DockerBridgeName, "-o", config.WeaveBridgeName, "-j", "DROP")
|
fwdRules = append(fwdRules, []string{"-i", config.DockerBridgeName, "-o", config.WeaveBridgeName, "-j", "DROP"})
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dockerBridgeIP, err := FindBridgeIP(config.DockerBridgeName, nil)
|
dockerBridgeIP, err := FindBridgeIP(config.DockerBridgeName, nil)
|
||||||
@@ -438,45 +446,42 @@ func configureIPTables(config *BridgeConfig) error {
|
|||||||
if config.NPC {
|
if config.NPC {
|
||||||
// Steer traffic via the NPC
|
// Steer traffic via the NPC
|
||||||
_ = ipt.NewChain("filter", "WEAVE-NPC") // ignore error because it might already exist
|
_ = 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 WEAVE-NPC chain doesn't exist then creating a rule in the chain will fail
|
||||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "WEAVE-NPC"); err != nil {
|
fwdRules = append(fwdRules,
|
||||||
return err
|
[][]string{
|
||||||
}
|
{"-o", config.WeaveBridgeName,
|
||||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-m", "state", "--state", "NEW", "-j", "NFLOG", "--nflog-group", "86"); err != nil {
|
"-m", "comment", "--comment", "NOTE: this must go before '-j KUBE-FORWARD'",
|
||||||
return err
|
"-j", "WEAVE-NPC"},
|
||||||
}
|
{"-o", config.WeaveBridgeName, "-m", "state", "--state", "NEW", "-j", "NFLOG", "--nflog-group", "86"},
|
||||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "DROP"); err != nil {
|
{"-o", config.WeaveBridgeName, "-j", "DROP"},
|
||||||
return err
|
}...)
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Work around the situation where there are no rules allowing traffic
|
// Work around the situation where there are no rules allowing traffic
|
||||||
// across our bridge. E.g. ufw
|
// across our bridge. E.g. ufw
|
||||||
if err = ipt.AppendUnique("filter", "FORWARD", "-i", config.WeaveBridgeName, "-o", config.WeaveBridgeName, "-j", "ACCEPT"); err != nil {
|
fwdRules = append(fwdRules, []string{"-i", config.WeaveBridgeName, "-o", config.WeaveBridgeName, "-j", "ACCEPT"})
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !config.NPC {
|
if !config.NPC {
|
||||||
// Create a chain for allowing ingress traffic when the bridge is exposed
|
// Create a chain for allowing ingress traffic when the bridge is exposed
|
||||||
_ = ipt.NewChain("filter", "WEAVE-EXPOSE")
|
_ = ipt.NewChain("filter", "WEAVE-EXPOSE")
|
||||||
if err = ipt.AppendUnique("filter", "FORWARD", "-o", config.WeaveBridgeName, "-j", "WEAVE-EXPOSE"); err != nil {
|
fwdRules = append(fwdRules, []string{"-o", config.WeaveBridgeName, "-j", "WEAVE-EXPOSE"})
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward from weave to the rest of the world
|
// 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 {
|
fwdRules = append(fwdRules, []string{"-i", config.WeaveBridgeName, "!", "-o", config.WeaveBridgeName, "-j", "ACCEPT"})
|
||||||
return err
|
|
||||||
}
|
|
||||||
// and allow replies back
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a chain for masquerading
|
// 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")
|
return ipt.AppendUnique("nat", "POSTROUTING", "-j", "WEAVE")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,3 +492,40 @@ func linkSetUpByName(linkName string) error {
|
|||||||
}
|
}
|
||||||
return netlink.LinkSetUp(link)
|
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
|
// 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)
|
bridge, err := netlink.LinkByName(bridgeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(`bridge "%s" not present; did you launch weave?`, bridgeName)
|
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},
|
MTU: mtu},
|
||||||
PeerName: peerName,
|
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)
|
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
|
id = id[:maxIDLen] // trim passed ID if too long
|
||||||
}
|
}
|
||||||
name, peerName := vethPrefix+"pl"+id, vethPrefix+"pg"+id
|
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 {
|
if err := netlink.LinkSetNsFd(veth, int(ns)); err != nil {
|
||||||
return fmt.Errorf("failed to move veth to container netns: %s", err)
|
return fmt.Errorf("failed to move veth to container netns: %s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -59,7 +59,7 @@ func (npc *controller) onNewNsSelector(selector *selector) error {
|
|||||||
for _, ns := range npc.nss {
|
for _, ns := range npc.nss {
|
||||||
if ns.namespace != nil {
|
if ns.namespace != nil {
|
||||||
if selector.matches(ns.namespace.ObjectMeta.Labels) {
|
if selector.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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+123
-55
@@ -10,13 +10,14 @@ import (
|
|||||||
coreapi "k8s.io/api/core/v1"
|
coreapi "k8s.io/api/core/v1"
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockSet struct {
|
type mockSet struct {
|
||||||
name ipset.Name
|
name ipset.Name
|
||||||
setType ipset.Type
|
setType ipset.Type
|
||||||
subSets map[string]bool
|
subSets map[string]map[types.UID]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockIPSet struct {
|
type mockIPSet struct {
|
||||||
@@ -24,70 +25,57 @@ type mockIPSet struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newMockIPSet() mockIPSet {
|
func newMockIPSet() mockIPSet {
|
||||||
i := mockIPSet{
|
return mockIPSet{sets: make(map[string]mockSet)}
|
||||||
sets: make(map[string]mockSet),
|
|
||||||
}
|
|
||||||
|
|
||||||
return i
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockIPSet) Create(ipsetName ipset.Name, ipsetType ipset.Type) error {
|
func (i *mockIPSet) Create(ipsetName ipset.Name, ipsetType ipset.Type) error {
|
||||||
if _, ok := i.sets[string(ipsetName)]; ok {
|
if _, ok := i.sets[string(ipsetName)]; ok {
|
||||||
return errors.Errorf("ipset %s already exists", ipsetName)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockIPSet) AddEntry(ipsetName ipset.Name, entry string, comment string) error {
|
func (i *mockIPSet) AddEntry(user types.UID, ipsetName ipset.Name, entry string, comment string) error {
|
||||||
return i.addEntry(ipsetName, entry, comment, true)
|
log.Printf("adding entry %s to %s for %s", entry, ipsetName, user)
|
||||||
}
|
|
||||||
|
|
||||||
func (i *mockIPSet) AddEntryIfNotExist(ipsetName ipset.Name, entry string, comment string) error {
|
|
||||||
return i.addEntry(ipsetName, entry, comment, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *mockIPSet) addEntry(ipsetName ipset.Name, entry string, comment string, checkIfExists bool) error {
|
|
||||||
log.Printf("adding entry %s to %s", entry, ipsetName)
|
|
||||||
if _, ok := i.sets[string(ipsetName)]; !ok {
|
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 i.sets[string(ipsetName)].subSets[entry] == nil {
|
||||||
if _, ok := i.sets[string(ipsetName)].subSets[entry]; ok {
|
i.sets[string(ipsetName)].subSets[entry] = make(map[types.UID]bool)
|
||||||
return errors.Errorf("ipset %s is already a member of %s", entry, ipsetName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockIPSet) DelEntry(ipsetName ipset.Name, entry string) error {
|
func (i *mockIPSet) DelEntry(user types.UID, ipsetName ipset.Name, entry string) error {
|
||||||
return i.delEntry(ipsetName, entry, true)
|
log.Printf("deleting entry %s from %s for %s", entry, ipsetName, user)
|
||||||
}
|
|
||||||
|
|
||||||
func (i *mockIPSet) DelEntryIfExists(ipsetName ipset.Name, entry string) error {
|
|
||||||
return i.delEntry(ipsetName, entry, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *mockIPSet) delEntry(ipsetName ipset.Name, entry string, checkIfExists bool) error {
|
|
||||||
log.Printf("deleting entry %s from %s", entry, ipsetName)
|
|
||||||
if _, ok := i.sets[string(ipsetName)]; !ok {
|
if _, ok := i.sets[string(ipsetName)]; !ok {
|
||||||
return errors.Errorf("ipset %s does not exist", ipsetName)
|
return errors.Errorf("ipset %s does not exist", ipsetName)
|
||||||
}
|
}
|
||||||
if checkIfExists {
|
if _, ok := i.sets[string(ipsetName)].subSets[entry][user]; !ok {
|
||||||
if _, ok := i.sets[string(ipsetName)].subSets[entry]; !ok {
|
return errors.Errorf("user %s does not own entry %s", user, entry)
|
||||||
return errors.Errorf("ipset %s is not a member of %s", entry, ipsetName)
|
}
|
||||||
}
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockIPSet) Exist(ipsetName ipset.Name, entry string) bool {
|
func (i *mockIPSet) Exist(user types.UID, ipsetName ipset.Name, entry string) bool {
|
||||||
_, found := i.sets[string(ipsetName)].subSets[entry]
|
_, ok := i.sets[string(ipsetName)].subSets[entry][user]
|
||||||
return found
|
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 {
|
func (i *mockIPSet) Flush(ipsetName ipset.Name) error {
|
||||||
@@ -191,7 +179,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
|||||||
|
|
||||||
controller.AddNetworkPolicy(networkPolicy)
|
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
|
// NetworkPolicy first
|
||||||
m = newMockIPSet()
|
m = newMockIPSet()
|
||||||
@@ -202,8 +190,7 @@ func TestRegressionPolicyNamespaceOrdering3059(t *testing.T) {
|
|||||||
controller.AddNamespace(sourceNamespace)
|
controller.AddNamespace(sourceNamespace)
|
||||||
controller.AddNamespace(destinationNamespace)
|
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.
|
// Tests default-allow ipset behavior when running in non-legacy mode.
|
||||||
@@ -238,7 +225,7 @@ func TestDefaultAllow(t *testing.T) {
|
|||||||
controller.AddPod(podFoo)
|
controller.AddPod(podFoo)
|
||||||
|
|
||||||
// Should add the foo pod to default-allow
|
// Should add the foo pod to default-allow
|
||||||
require.True(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
podBar := &coreapi.Pod{
|
podBar := &coreapi.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -252,7 +239,7 @@ func TestDefaultAllow(t *testing.T) {
|
|||||||
controller.UpdatePod(podBarNoIP, podBar)
|
controller.UpdatePod(podBarNoIP, podBar)
|
||||||
|
|
||||||
// Should add the bar pod to default-allow
|
// Should add the bar pod to default-allow
|
||||||
require.True(t, m.Exist(defaultAllowIPSetName, barPodIP))
|
require.True(t, m.entryExists(defaultAllowIPSetName, barPodIP))
|
||||||
|
|
||||||
// Allow access from the bar pod to the foo pod
|
// Allow access from the bar pod to the foo pod
|
||||||
netpol := &networkingv1.NetworkPolicy{
|
netpol := &networkingv1.NetworkPolicy{
|
||||||
@@ -274,37 +261,118 @@ func TestDefaultAllow(t *testing.T) {
|
|||||||
controller.AddNetworkPolicy(netpol)
|
controller.AddNetworkPolicy(netpol)
|
||||||
|
|
||||||
// Should remove the foo pod from default-allow as the netpol selects it
|
// Should remove the foo pod from default-allow as the netpol selects it
|
||||||
require.False(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||||
require.True(t, m.Exist(defaultAllowIPSetName, barPodIP))
|
require.True(t, m.entryExists(defaultAllowIPSetName, barPodIP))
|
||||||
|
|
||||||
podBarWithNewIP := *podBar
|
podBarWithNewIP := *podBar
|
||||||
podBarWithNewIP.Status.PodIP = barPodNewIP
|
podBarWithNewIP.Status.PodIP = barPodNewIP
|
||||||
controller.UpdatePod(podBar, &podBarWithNewIP)
|
controller.UpdatePod(podBar, &podBarWithNewIP)
|
||||||
|
|
||||||
// Should update IP addr of the bar pod in default-allow
|
// Should update IP addr of the bar pod in default-allow
|
||||||
require.False(t, m.Exist(defaultAllowIPSetName, barPodIP))
|
require.False(t, m.entryExists(defaultAllowIPSetName, barPodIP))
|
||||||
require.True(t, m.Exist(defaultAllowIPSetName, barPodNewIP))
|
require.True(t, m.entryExists(defaultAllowIPSetName, barPodNewIP))
|
||||||
|
|
||||||
controller.UpdatePod(&podBarWithNewIP, podBarNoIP)
|
controller.UpdatePod(&podBarWithNewIP, podBarNoIP)
|
||||||
// Should remove the bar pod from default-allow as it does not have any IP addr
|
// Should remove the bar pod from default-allow as it does not have any IP addr
|
||||||
require.False(t, m.Exist(defaultAllowIPSetName, barPodNewIP))
|
require.False(t, m.entryExists(defaultAllowIPSetName, barPodNewIP))
|
||||||
|
|
||||||
podFooWithNewLabel := *podFoo
|
podFooWithNewLabel := *podFoo
|
||||||
podFooWithNewLabel.ObjectMeta.Labels = map[string]string{"run": "new-foo"}
|
podFooWithNewLabel.ObjectMeta.Labels = map[string]string{"run": "new-foo"}
|
||||||
controller.UpdatePod(podFoo, &podFooWithNewLabel)
|
controller.UpdatePod(podFoo, &podFooWithNewLabel)
|
||||||
|
|
||||||
// Should bring back the foo pod to default-allow as it does not match dst of any netpol
|
// Should bring back the foo pod to default-allow as it does not match dst of any netpol
|
||||||
require.True(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
controller.UpdatePod(&podFooWithNewLabel, podFoo)
|
controller.UpdatePod(&podFooWithNewLabel, podFoo)
|
||||||
// Should remove from default-allow as it matches the netpol after the update
|
// Should remove from default-allow as it matches the netpol after the update
|
||||||
require.False(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
require.False(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
controller.DeleteNetworkPolicy(netpol)
|
controller.DeleteNetworkPolicy(netpol)
|
||||||
// Should bring back the foo pod to default-allow as no netpol selects it
|
// Should bring back the foo pod to default-allow as no netpol selects it
|
||||||
require.True(t, m.Exist(defaultAllowIPSetName, fooPodIP))
|
require.True(t, m.entryExists(defaultAllowIPSetName, fooPodIP))
|
||||||
|
|
||||||
controller.DeletePod(podFoo)
|
controller.DeletePod(podFoo)
|
||||||
// Should remove foo pod from default-allow
|
// Should remove foo pod from default-allow
|
||||||
require.False(t, m.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"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Name string
|
type Name string
|
||||||
@@ -19,11 +20,9 @@ const (
|
|||||||
|
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
Create(ipsetName Name, ipsetType Type) error
|
Create(ipsetName Name, ipsetType Type) error
|
||||||
AddEntry(ipsetName Name, entry string, comment string) error
|
AddEntry(user types.UID, ipsetName Name, entry string, comment string) error
|
||||||
AddEntryIfNotExist(ipsetName Name, entry string, comment string) error
|
DelEntry(user types.UID, ipsetName Name, entry string) error
|
||||||
DelEntry(ipsetName Name, entry string) error
|
Exist(user types.UID, ipsetName Name, entry string) bool
|
||||||
DelEntryIfExists(ipsetName Name, entry string) error
|
|
||||||
Exist(ipsetName Name, entry string) bool
|
|
||||||
Flush(ipsetName Name) error
|
Flush(ipsetName Name) error
|
||||||
Destroy(ipsetName Name) error
|
Destroy(ipsetName Name) error
|
||||||
|
|
||||||
@@ -33,14 +32,27 @@ type Interface interface {
|
|||||||
DestroyAll() error
|
DestroyAll() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type entryKey struct {
|
||||||
|
ipsetName Name
|
||||||
|
entry string
|
||||||
|
}
|
||||||
|
|
||||||
type ipset struct {
|
type ipset struct {
|
||||||
refCount
|
|
||||||
*log.Logger
|
*log.Logger
|
||||||
enableComments bool
|
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 {
|
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
|
// Check for comment support
|
||||||
testIpsetName := Name("weave-test-comment")
|
testIpsetName := Name("weave-test-comment")
|
||||||
@@ -65,11 +77,15 @@ func (i *ipset) Create(ipsetName Name, ipsetType Type) error {
|
|||||||
return doExec(args...)
|
return doExec(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ipset) AddEntry(ipsetName Name, entry string, comment string) error {
|
func (i *ipset) AddEntry(user types.UID, ipsetName Name, entry string, comment string) error {
|
||||||
i.Logger.Printf("adding entry %s to %s", entry, ipsetName)
|
i.Logger.Printf("adding entry %s to %s of %s", entry, ipsetName, user)
|
||||||
if i.inc(ipsetName, entry) > 1 { // already in the set
|
|
||||||
|
if !i.addUser(user, ipsetName, entry) { // already in the set
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.Logger.Printf("added entry %s to %s of %s", entry, ipsetName, user)
|
||||||
|
|
||||||
args := []string{"add", string(ipsetName), entry}
|
args := []string{"add", string(ipsetName), entry}
|
||||||
if i.enableComments {
|
if i.enableComments {
|
||||||
args = append(args, "comment", comment)
|
args = append(args, "comment", comment)
|
||||||
@@ -77,53 +93,39 @@ func (i *ipset) AddEntry(ipsetName Name, entry string, comment string) error {
|
|||||||
return doExec(args...)
|
return doExec(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddEntryIfNotExist does the same as AddEntry but bypasses the ref counting.
|
func (i *ipset) DelEntry(user types.UID, ipsetName Name, entry string) error {
|
||||||
// Should be used only with "default-allow" ipsets.
|
i.Logger.Printf("deleting entry %s from %s of %s", entry, ipsetName, user)
|
||||||
func (i *ipset) AddEntryIfNotExist(ipsetName Name, entry string, comment string) error {
|
|
||||||
if i.count(ipsetName, entry) == 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return i.AddEntry(ipsetName, entry, comment)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipset) DelEntry(ipsetName Name, entry string) error {
|
if !i.delUser(user, ipsetName, entry) { // still needed
|
||||||
i.Logger.Printf("deleting entry %s from %s", entry, ipsetName)
|
|
||||||
if i.dec(ipsetName, entry) > 0 { // still needed
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.Logger.Printf("deleted entry %s from %s of %s", entry, ipsetName, user)
|
||||||
|
|
||||||
return doExec("del", string(ipsetName), entry)
|
return doExec("del", string(ipsetName), entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DelEntryIfExists does the same as DelEntry but bypasses the ref counting.
|
func (i *ipset) Exist(user types.UID, ipsetName Name, entry string) bool {
|
||||||
// Should be used only with "default-allow" ipsets.
|
return i.existUser(user, ipsetName, entry)
|
||||||
func (i *ipset) DelEntryIfExists(ipsetName Name, entry string) error {
|
|
||||||
if i.count(ipsetName, entry) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return i.DelEntry(ipsetName, entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipset) Exist(ipsetName Name, entry string) bool {
|
|
||||||
return i.count(ipsetName, entry) > 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ipset) Flush(ipsetName Name) error {
|
func (i *ipset) Flush(ipsetName Name) error {
|
||||||
i.removeSet(ipsetName)
|
i.removeSetFromUsers(ipsetName)
|
||||||
return doExec("flush", string(ipsetName))
|
return doExec("flush", string(ipsetName))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ipset) FlushAll() error {
|
func (i *ipset) FlushAll() error {
|
||||||
i.refCount = newRefCount()
|
i.users = make(map[entryKey]map[types.UID]struct{})
|
||||||
return doExec("flush")
|
return doExec("flush")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ipset) Destroy(ipsetName Name) error {
|
func (i *ipset) Destroy(ipsetName Name) error {
|
||||||
i.removeSet(ipsetName)
|
i.removeSetFromUsers(ipsetName)
|
||||||
return doExec("destroy", string(ipsetName))
|
return doExec("destroy", string(ipsetName))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ipset) DestroyAll() error {
|
func (i *ipset) DestroyAll() error {
|
||||||
i.refCount = newRefCount()
|
i.users = make(map[entryKey]map[types.UID]struct{})
|
||||||
return doExec("destroy")
|
return doExec("destroy")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,48 +147,52 @@ func (i *ipset) List(prefix string) ([]Name, error) {
|
|||||||
return selected, err
|
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 {
|
func doExec(args ...string) error {
|
||||||
if output, err := exec.Command("ipset", args...).CombinedOutput(); err != nil {
|
if output, err := exec.Command("ipset", args...).CombinedOutput(); err != nil {
|
||||||
return errors.Wrapf(err, "ipset %v failed: %s", args, output)
|
return errors.Wrapf(err, "ipset %v failed: %s", args, output)
|
||||||
}
|
}
|
||||||
return nil
|
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"
|
"encoding/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Return JSON suitable for logging an API object.
|
||||||
func js(v interface{}) string {
|
func js(v interface{}) string {
|
||||||
|
// Get the raw JSON
|
||||||
a, _ := json.Marshal(v)
|
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)
|
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 {
|
for _, pod := range ns.pods {
|
||||||
if hasIP(pod) {
|
if hasIP(pod) {
|
||||||
if selector.matches(pod.ObjectMeta.Labels) {
|
if selector.matches(pod.ObjectMeta.Labels) {
|
||||||
if err := 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ func (ns *ns) onNewDstPodSelector(selector *selector) error {
|
|||||||
if hasIP(pod) {
|
if hasIP(pod) {
|
||||||
// Remove the pod from default-allow if dst podselector matches the pod
|
// Remove the pod from default-allow if dst podselector matches the pod
|
||||||
if selector.matches(pod.ObjectMeta.Labels) {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,7 +150,7 @@ func (ns *ns) addToDefaultAllowIfNoMatching(pod *coreapi.Pod) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,16 +172,16 @@ func (ns *ns) addPod(obj *coreapi.Pod) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ns.checkLocalPod(obj) {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// If there are no matching dst selectors, add the pod to default-allow
|
// If there are no matching dst selectors, add the pod to default-allow
|
||||||
if !ns.legacy && !found {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,29 +199,29 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
|||||||
|
|
||||||
if hasIP(oldObj) && !hasIP(newObj) {
|
if hasIP(oldObj) && !hasIP(newObj) {
|
||||||
if ns.checkLocalPod(oldObj) {
|
if ns.checkLocalPod(oldObj) {
|
||||||
ns.ips.DelEntry(LocalIpset, oldObj.Status.PodIP)
|
ns.ips.DelEntry(oldObj.ObjectMeta.UID, LocalIpset, oldObj.Status.PodIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ns.legacy {
|
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 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 !hasIP(oldObj) && hasIP(newObj) {
|
||||||
if ns.checkLocalPod(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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ns.legacy && !found {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -231,12 +231,15 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
|||||||
|
|
||||||
if !ns.legacy &&
|
if !ns.legacy &&
|
||||||
oldObj.Status.PodIP != newObj.Status.PodIP &&
|
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
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,12 +254,12 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if oldMatch {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if newMatch {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,7 +267,7 @@ func (ns *ns) updatePod(oldObj, newObj *coreapi.Pod) error {
|
|||||||
if !ns.legacy && ns.podSelectors.dstSelectorExist(ps) {
|
if !ns.legacy && ns.podSelectors.dstSelectorExist(ps) {
|
||||||
switch {
|
switch {
|
||||||
case !oldMatch && newMatch:
|
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
|
return err
|
||||||
}
|
}
|
||||||
case oldMatch && !newMatch:
|
case oldMatch && !newMatch:
|
||||||
@@ -287,16 +290,16 @@ func (ns *ns) deletePod(obj *coreapi.Pod) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ns.checkLocalPod(obj) {
|
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 !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 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 {
|
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
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,12 +445,12 @@ func (ns *ns) updateNamespace(oldObj, newObj *coreapi.Namespace) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if oldMatch {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if newMatch {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -468,7 +471,7 @@ func (ns *ns) deleteNamespace(obj *coreapi.Namespace) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove namespace ipset from any matching namespace selectors
|
// 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 {
|
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))
|
return s.spec.selector.Matches(labels.Set(labelMap))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *selector) addEntry(entry string, comment string) error {
|
func (s *selector) addEntry(user types.UID, entry string, comment string) error {
|
||||||
return s.ips.AddEntry(s.spec.ipsetName, entry, comment)
|
return s.ips.AddEntry(user, s.spec.ipsetName, entry, comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *selector) delEntry(entry string) error {
|
func (s *selector) delEntry(user types.UID, entry string) error {
|
||||||
return s.ips.DelEntry(s.spec.ipsetName, entry)
|
return s.ips.DelEntry(user, s.spec.ipsetName, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
type selectorFn func(selector *selector) error
|
type selectorFn func(selector *selector) error
|
||||||
@@ -85,14 +85,14 @@ func newSelectorSet(ips ipset.Interface, onNewSelector, onNewDstSelector selecto
|
|||||||
dstSelectorsCount: make(map[string]int)}
|
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
|
found := false
|
||||||
for _, s := range ss.entries {
|
for _, s := range ss.entries {
|
||||||
if s.matches(labelMap) {
|
if s.matches(labelMap) {
|
||||||
if ss.dstSelectorExist(s) {
|
if ss.dstSelectorExist(s) {
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
if err := s.addEntry(entry, comment); err != nil {
|
if err := s.addEntry(user, entry, comment); err != nil {
|
||||||
return found, err
|
return found, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,10 +100,10 @@ func (ss *selectorSet) addToMatching(labelMap map[string]string, entry string, c
|
|||||||
return found, nil
|
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 {
|
for _, s := range ss.entries {
|
||||||
if s.matches(labelMap) {
|
if s.matches(labelMap) {
|
||||||
if err := s.delEntry(entry); err != nil {
|
if err := s.delEntry(user, entry); err != nil {
|
||||||
return err
|
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
|
// create veths. note we assume endpoint IDs are unique in the first 9 chars
|
||||||
name, peerName := vethPair(create.EndpointID)
|
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)
|
return nil, driver.error("JoinEndpoint", "%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,8 +73,10 @@ const (
|
|||||||
retryPeriod = time.Second * 2
|
retryPeriod = time.Second * 2
|
||||||
jitterFactor = 1.0
|
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 is the default annotation key
|
||||||
KubePeersAnnotationKey = "kube-peers.weave.works/peers"
|
KubePeersAnnotationKey = KubePeersPrefix + "peers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (cml *configMapAnnotations) Init() error {
|
func (cml *configMapAnnotations) Init() error {
|
||||||
@@ -130,46 +132,59 @@ func (cml *configMapAnnotations) UpdatePeerList(list peerList) error {
|
|||||||
return cml.UpdateAnnotation(KubePeersAnnotationKey, string(recordBytes))
|
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 {
|
if cml.cm == nil {
|
||||||
return errors.New("endpoint not initialized, call Init first")
|
return errors.New("endpoint not initialized, call Init first")
|
||||||
}
|
}
|
||||||
cm := cml.cm
|
// speculatively change the state, then replace with whatever comes back
|
||||||
cm.Annotations[key] = value
|
// from Update(), which will be the latest on the server whatever happened
|
||||||
cm, err := cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
cml.cm.Annotations[cleanKey(key)] = value
|
||||||
if err == nil {
|
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||||
cml.cm = cm
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cml *configMapAnnotations) RemoveAnnotation(key string) error {
|
func (cml *configMapAnnotations) RemoveAnnotation(key string) (err error) {
|
||||||
if cml.cm == nil {
|
if cml.cm == nil {
|
||||||
return errors.New("endpoint not initialized, call Init first")
|
return errors.New("endpoint not initialized, call Init first")
|
||||||
}
|
}
|
||||||
cm := cml.cm
|
// speculatively change the state, then replace with whatever comes back
|
||||||
delete(cm.Annotations, key)
|
// from Update(), which will be the latest on the server whatever happened
|
||||||
cm, err := cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
delete(cml.cm.Annotations, cleanKey(key))
|
||||||
if err == nil {
|
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||||
cml.cm = cm
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cml *configMapAnnotations) RemoveAnnotationsWithValue(valueToRemove string) error {
|
func (cml *configMapAnnotations) RemoveAnnotationsWithValue(valueToRemove string) (err error) {
|
||||||
if cml.cm == nil {
|
if cml.cm == nil {
|
||||||
return errors.New("endpoint not initialized, call Init first")
|
return errors.New("endpoint not initialized, call Init first")
|
||||||
}
|
}
|
||||||
cm := cml.cm
|
// speculatively change the state, then replace with whatever comes back
|
||||||
for key, value := range cm.Annotations {
|
// from Update(), which will be the latest on the server whatever happened
|
||||||
|
for key, value := range cml.cm.Annotations {
|
||||||
if value == valueToRemove {
|
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)
|
cml.cm, err = cml.Client.ConfigMaps(cml.Namespace).Update(cml.cm)
|
||||||
if err == nil {
|
|
||||||
cml.cm = cm
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,6 +98,9 @@ func checkIamInPeerList(cml *configMapAnnotations, c *kubernetes.Clientset, peer
|
|||||||
// Kubernetes, remove it from Weave IPAM
|
// Kubernetes, remove it from Weave IPAM
|
||||||
func reclaimRemovedPeers(weave *weaveapi.Client, cml *configMapAnnotations, nodes []nodeInfo, myPeerName string) error {
|
func reclaimRemovedPeers(weave *weaveapi.Client, cml *configMapAnnotations, nodes []nodeInfo, myPeerName string) error {
|
||||||
for {
|
for {
|
||||||
|
if err := cml.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// 1. Compare peers stored in the peerList against all peers reported by k8s now.
|
// 1. Compare peers stored in the peerList against all peers reported by k8s now.
|
||||||
storedPeerList, err := cml.GetPeerList()
|
storedPeerList, err := cml.GetPeerList()
|
||||||
if err != nil {
|
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)
|
common.Log.Debugln("[kube-peers] Preparing to remove disappeared peer", peer)
|
||||||
okToRemove := false
|
okToRemove := false
|
||||||
// 3. Check if there is an existing annotation with key X
|
// 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)
|
common.Log.Debugln("[kube-peers] Existing annotation", existingAnnotation)
|
||||||
// 4. If annotation already contains my identity, ok;
|
// 4. If annotation already contains my identity, ok;
|
||||||
if existingAnnotation == myPeerName {
|
if existingAnnotation == myPeerName {
|
||||||
@@ -128,14 +131,16 @@ func reclaimRemovedPeers(weave *weaveapi.Client, cml *configMapAnnotations, node
|
|||||||
} else {
|
} else {
|
||||||
// 5. If non-existent, write an annotation with key X and contents "my identity"
|
// 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)
|
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
|
okToRemove = true
|
||||||
|
} else {
|
||||||
|
common.Log.Debugln("[kube-peers] error from UpdateAnnotation: ", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if okToRemove {
|
if okToRemove {
|
||||||
// 6. If step 4 or 5 succeeded, rmpeer X
|
// 6. If step 4 or 5 succeeded, rmpeer X
|
||||||
result, err := weave.RmPeer(peer.PeerName)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -146,15 +151,12 @@ func reclaimRemovedPeers(weave *weaveapi.Client, cml *configMapAnnotations, node
|
|||||||
}
|
}
|
||||||
// 7a. Remove X from peerList
|
// 7a. Remove X from peerList
|
||||||
storedPeerList.remove(peer.PeerName)
|
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 {
|
if err := cml.UpdatePeerList(*storedPeerList); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
common.Log.Infoln("[kube-peers] Removing annotation ", peer.PeerName)
|
|
||||||
// 7b. Remove annotation with key X
|
// 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
|
// 8. If step 5 failed due to optimistic lock conflict, stop: someone else is handling X
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,14 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"env": [
|
"env": [
|
||||||
|
{
|
||||||
|
"description": "Log level",
|
||||||
|
"name": "LOG_LEVEL",
|
||||||
|
"settable": [
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Extra args to `weaver` and `plugin`",
|
"description": "Extra args to `weaver` and `plugin`",
|
||||||
"name": "EXTRA_ARGS",
|
"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}
|
HTTP_ADDR=${WEAVE_HTTP_ADDR:-127.0.0.1:6784}
|
||||||
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
|
STATUS_ADDR=${WEAVE_STATUS_ADDR:-0.0.0.0:6782}
|
||||||
HOST_ROOT=${HOST_ROOT:-/host}
|
HOST_ROOT=${HOST_ROOT:-/host}
|
||||||
|
LOG_LEVEL=${LOG_LEVEL:-info}
|
||||||
WEAVE_DIR="/host/var/lib/weave"
|
WEAVE_DIR="/host/var/lib/weave"
|
||||||
|
|
||||||
mkdir $WEAVE_DIR || true
|
mkdir $WEAVE_DIR || true
|
||||||
@@ -44,7 +45,7 @@ exec /home/weave/weaver $EXTRA_ARGS --port=6783 $(router_bridge_opts) \
|
|||||||
--no-dns \
|
--no-dns \
|
||||||
--ipalloc-range=$IPALLOC_RANGE \
|
--ipalloc-range=$IPALLOC_RANGE \
|
||||||
--nickname "$(hostname)" \
|
--nickname "$(hostname)" \
|
||||||
--log-level=debug \
|
--log-level=$LOG_LEVEL \
|
||||||
--db-prefix="$WEAVE_DIR/weave" \
|
--db-prefix="$WEAVE_DIR/weave" \
|
||||||
--plugin-v2 \
|
--plugin-v2 \
|
||||||
$(multicast_opt) \
|
$(multicast_opt) \
|
||||||
|
|||||||
+19
-13
@@ -45,12 +45,20 @@ type ipamConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type dnsConfig struct {
|
type dnsConfig struct {
|
||||||
Domain string
|
Domain string
|
||||||
ListenAddress string
|
ListenAddress string
|
||||||
TTL int
|
TTL int
|
||||||
ClientTimeout time.Duration
|
ClientTimeout time.Duration
|
||||||
EffectiveListenAddress string
|
ResolvConf string
|
||||||
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 {
|
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.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.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.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(&dnsConfig.ResolvConf, []string{"-resolv-conf"}, "", "path to resolver configuration for fallback DNS lookups")
|
||||||
mflag.StringVar(&bridgeConfig.DatapathName, []string{"-datapath"}, "", "ODP datapath name")
|
mflag.StringVar(&bridgeConfig.DatapathName, []string{"-datapath"}, "", "ODP datapath name")
|
||||||
mflag.BoolVar(&bridgeConfig.NoFastdp, []string{"-no-fastdp"}, false, "Disable Fast Datapath")
|
mflag.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(&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(&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")
|
mflag.StringVar(&procPath, []string{"-proc-path"}, "/proc", "path to reach host /proc filesystem")
|
||||||
@@ -263,6 +271,8 @@ func main() {
|
|||||||
var proxy *weaveproxy.Proxy
|
var proxy *weaveproxy.Proxy
|
||||||
var err error
|
var err error
|
||||||
if proxyConfig.Enabled {
|
if proxyConfig.Enabled {
|
||||||
|
proxyConfig.DNSListenAddress = dnsConfig.addressOnly()
|
||||||
|
proxyConfig.DNSDomain = dnsConfig.Domain
|
||||||
if noDNS {
|
if noDNS {
|
||||||
proxyConfig.WithoutDNS = true
|
proxyConfig.WithoutDNS = true
|
||||||
}
|
}
|
||||||
@@ -635,17 +645,13 @@ func createDNSServer(config dnsConfig, router *mesh.Router, isKnownPeer func(mes
|
|||||||
gossip, err := router.NewGossip("nameserver", ns)
|
gossip, err := router.NewGossip("nameserver", ns)
|
||||||
checkFatal(err)
|
checkFatal(err)
|
||||||
ns.SetGossip(gossip)
|
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,
|
dnsserver, err := nameserver.NewDNSServer(ns, config.Domain, config.ListenAddress,
|
||||||
upstream, uint32(config.TTL), config.ClientTimeout)
|
upstream, uint32(config.TTL), config.ClientTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.Fatal("Unable to start dns server: ", err)
|
Log.Fatal("Unable to start dns server: ", err)
|
||||||
}
|
}
|
||||||
listenAddr := config.ListenAddress
|
Log.Println("Listening for DNS queries on", config.ListenAddress)
|
||||||
if config.EffectiveListenAddress != "" {
|
|
||||||
listenAddr = config.EffectiveListenAddress
|
|
||||||
}
|
|
||||||
Log.Println("Listening for DNS queries on", listenAddr)
|
|
||||||
return ns, dnsserver
|
return ns, dnsserver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
@@ -15,8 +14,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func attach(args []string) error {
|
func attach(args []string) error {
|
||||||
if len(args) < 4 {
|
if len(args) < 3 {
|
||||||
cmdUsage("attach-container", "[--no-multicast-route] [--keep-tx-on] [--hairpin-mode=true|false] <container-id> <bridge-name> <mtu> <cidr>...")
|
cmdUsage("attach-container", "[--no-multicast-route] [--keep-tx-on] [--hairpin-mode=true|false] <container-id> <bridge-name> <cidr>...")
|
||||||
}
|
}
|
||||||
|
|
||||||
keepTXOn := false
|
keepTXOn := false
|
||||||
@@ -52,16 +51,12 @@ func attach(args []string) error {
|
|||||||
} else if nsHost.Equal(nsContainer) {
|
} 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")
|
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])
|
cidrs, err := parseCIDRs(args[2:])
|
||||||
if err != nil && args[3] != "" {
|
|
||||||
return fmt.Errorf("unable to parse mtu %q: %s", args[2], err)
|
|
||||||
}
|
|
||||||
cidrs, err := parseCIDRs(args[3:])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 we detected an error but the container has died, tell the user that instead.
|
||||||
if err != nil && !processExists(pid) {
|
if err != nil && !processExists(pid) {
|
||||||
err = fmt.Errorf("Container %s died", args[0])
|
err = fmt.Errorf("Container %s died", args[0])
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/weaveworks/weave/common"
|
|
||||||
weavenet "github.com/weaveworks/weave/net"
|
weavenet "github.com/weaveworks/weave/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,36 +20,3 @@ func detectBridgeType(args []string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
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,
|
"help": help,
|
||||||
"netcheck": netcheck,
|
"netcheck": netcheck,
|
||||||
"docker-tls-args": dockerTLSArgs,
|
"docker-tls-args": dockerTLSArgs,
|
||||||
"create-bridge": createBridge,
|
|
||||||
"detect-bridge-type": detectBridgeType,
|
"detect-bridge-type": detectBridgeType,
|
||||||
"create-datapath": createDatapath,
|
"create-datapath": createDatapath,
|
||||||
"delete-datapath": deleteDatapath,
|
"delete-datapath": deleteDatapath,
|
||||||
|
|||||||
@@ -68,11 +68,11 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" {
|
if !i.proxy.WithoutDNS {
|
||||||
if err := i.setHostname(container, hostname, dnsDomain); err != nil {
|
if err := i.setHostname(container, hostname, i.proxy.DNSDomain); err != nil {
|
||||||
return err
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-19
@@ -61,6 +61,8 @@ type Config struct {
|
|||||||
NoRewriteHosts bool
|
NoRewriteHosts bool
|
||||||
TLSConfig TLSConfig
|
TLSConfig TLSConfig
|
||||||
WithoutDNS bool
|
WithoutDNS bool
|
||||||
|
DNSListenAddress string
|
||||||
|
DNSDomain string
|
||||||
NoMulticastRoute bool
|
NoMulticastRoute bool
|
||||||
KeepTXOn bool
|
KeepTXOn bool
|
||||||
DockerBridge string
|
DockerBridge string
|
||||||
@@ -78,7 +80,6 @@ type Proxy struct {
|
|||||||
Config
|
Config
|
||||||
client *weavedocker.Client
|
client *weavedocker.Client
|
||||||
weave *weaveapi.Client
|
weave *weaveapi.Client
|
||||||
dockerBridgeIP string
|
|
||||||
hostnameMatchRegexp *regexp.Regexp
|
hostnameMatchRegexp *regexp.Regexp
|
||||||
weaveWaitVolume string
|
weaveWaitVolume string
|
||||||
weaveWaitNoopVolume string
|
weaveWaitNoopVolume string
|
||||||
@@ -158,15 +159,6 @@ func NewProxy(c Config) (*Proxy, error) {
|
|||||||
|
|
||||||
Log.Info(p.client.Info())
|
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)
|
p.hostnameMatchRegexp, err = regexp.Compile(c.HostnameMatch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Incorrect hostname match '%s': %s", c.HostnameMatch, err.Error())
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hostConfig["Dns"] = append(dns, proxy.dockerBridgeIP)
|
hostConfig["Dns"] = append(dns, proxy.DNSListenAddress)
|
||||||
|
|
||||||
dnsSearch, err := hostConfig.StringArray("DnsSearch")
|
dnsSearch, err := hostConfig.StringArray("DnsSearch")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -612,14 +604,6 @@ func (proxy *Proxy) setWeaveDNS(hostConfig jsonObject, hostname, dnsDomain strin
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (proxy *Proxy) getDNSDomain() string {
|
|
||||||
if proxy.WithoutDNS {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
domain, _ := proxy.weave.DNSDomain()
|
|
||||||
return domain
|
|
||||||
}
|
|
||||||
|
|
||||||
func (proxy *Proxy) updateContainerNetworkSettings(container jsonObject) error {
|
func (proxy *Proxy) updateContainerNetworkSettings(container jsonObject) error {
|
||||||
containerID, err := container.String("Id")
|
containerID, err := container.String("Id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ func (i *startContainerInterceptor) InterceptRequest(r *http.Request) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" {
|
if !i.proxy.WithoutDNS {
|
||||||
if err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, dnsDomain); err != nil {
|
if err := i.proxy.setWeaveDNS(hostConfig, container.Config.Hostname, i.proxy.DNSDomain); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ func NewNetworkRouter(config mesh.Config, networkConfig NetworkConfig, bridgeCon
|
|||||||
router.Routes.OnChange(overlay.InvalidateRoutes)
|
router.Routes.OnChange(overlay.InvalidateRoutes)
|
||||||
router.Macs = NewMacCache(macMaxAge,
|
router.Macs = NewMacCache(macMaxAge,
|
||||||
func(mac net.HardwareAddr, peer *mesh.Peer) {
|
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) })
|
router.Peers.OnGC(func(peer *mesh.Peer) { router.Macs.Delete(peer) })
|
||||||
return router, nil
|
return router, nil
|
||||||
@@ -92,7 +92,7 @@ func (router *NetworkRouter) handleCapturedPacket(key PacketKey) FlowOp {
|
|||||||
|
|
||||||
switch newSrcMac, conflictPeer := router.Macs.Add(srcMac, router.Ourself.Peer); {
|
switch newSrcMac, conflictPeer := router.Macs.Add(srcMac, router.Ourself.Peer); {
|
||||||
case newSrcMac:
|
case newSrcMac:
|
||||||
log.Println("Discovered local MAC", srcMac)
|
log.Debugln("Discovered local MAC", srcMac)
|
||||||
case conflictPeer != nil:
|
case conflictPeer != nil:
|
||||||
// The MAC cache has an entry for the source MAC
|
// The MAC cache has an entry for the source MAC
|
||||||
// associated with another peer. This probably means
|
// associated with another peer. This probably means
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ The following topics are discussed:
|
|||||||
* [Upgrading the Daemon Sets](#daemon-sets)
|
* [Upgrading the Daemon Sets](#daemon-sets)
|
||||||
* [CPU and Memory Requirements](#resources)
|
* [CPU and Memory Requirements](#resources)
|
||||||
* [Pod Eviction](#eviction)
|
* [Pod Eviction](#eviction)
|
||||||
* [Network Policy Controller](#npc)
|
* [Features](#features)
|
||||||
|
* [Pod Network](#pod-network)
|
||||||
|
* [Network Policy](#npc)
|
||||||
* [Troubleshooting](#troubleshooting)
|
* [Troubleshooting](#troubleshooting)
|
||||||
* [Troubleshooting Blocked Connections](#blocked-connections)
|
* [Troubleshooting Blocked Connections](#blocked-connections)
|
||||||
* [Things to watch out for](#key-points)
|
* [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
|
If you see this in your cluster, consider some of the above steps to
|
||||||
reduce disruption.
|
reduce disruption.
|
||||||
|
|
||||||
## <a name="npc"></a>Network Policy Controller
|
## <a name="features"></a>Features
|
||||||
|
|
||||||
The addon also supports the [Kubernetes policy
|
### <a name="pod-network"></a>Pod Network
|
||||||
API](http://kubernetes.io/docs/user-guide/networkpolicies/) so that
|
|
||||||
you can securely isolate pods from each other based on namespaces and
|
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
|
labels. For more information on configuring network policies in
|
||||||
Kubernetes see the
|
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
|
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
|
**Note:** as of version 1.9 of Weave Net, the Network Policy
|
||||||
Controller allows all multicast traffic. Since a single multicast
|
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
|
## <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:
|
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:
|
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`.
|
- `disable-npc`: boolean (`true|false`). Default: `false`.
|
||||||
- `env.NAME=VALUE`: add environment variable `NAME` and set it to `VALUE`.
|
- `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`
|
- `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:
|
The list of variables you can set is:
|
||||||
|
|
||||||
* `CHECKPOINT_DISABLE` - if set to 1, disable checking for new Weave Net
|
* `CHECKPOINT_DISABLE` - if set to 1, disable checking for new Weave Net
|
||||||
versions (default is blank, i.e. check is enabled)
|
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
|
* `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
|
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
|
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
|
$ 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:02(two) 349525 IPs (33.3% of total)
|
||||||
00:00:00:00:00:03(three) 349525 IPs (33.3% of total) - unreachable!
|
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
|
* Peer Name and Nickname
|
||||||
* Absolute quantity/percentage of allocation range managed by peer
|
* 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
|
* Indication of unreachability. This means that the peer is not
|
||||||
visible (directly or indirectly) to the peer on which `weave status
|
visible (directly or indirectly) to the peer on which `weave status
|
||||||
ipam` was run; whilst this could be a transient condition due to a
|
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
|
`--without-dns` option to `weave launch`. To
|
||||||
disable weaveDNS itself, launch weave with the `--no-dns` option.
|
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**
|
**See Also**
|
||||||
|
|
||||||
* [Integrating Docker via the API Proxy](/site/tasks/weave-docker-api/weave-docker-api.md)
|
* [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 c1"
|
||||||
assert "show_multicast_route_on $HOST1 c2"
|
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
|
# Now try via docker network options
|
||||||
# using ssh rather than docker -H because CircleCI docker client is older
|
# 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"
|
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 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
|
$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
|
# Ensure current proxy options are obeyed on container start
|
||||||
docker_on $HOST1 stop -t 1 c2
|
docker_on $HOST1 stop -t 1 c2
|
||||||
@@ -39,7 +39,7 @@ weave_on $HOST1 launch
|
|||||||
|
|
||||||
proxy docker_on $HOST1 start c2
|
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
|
docker_on $HOST1 rm -f c3 c4 c5
|
||||||
$SSH $HOST1 docker network rm testmcasttrue testmcastfalse
|
$SSH $HOST1 docker network rm testmcasttrue testmcastfalse
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
tear_down_kubeadm() {
|
tear_down_kubeadm() {
|
||||||
for host in $HOSTS; do
|
for host in $HOSTS; do
|
||||||
run_on $host "sudo kubeadm reset && sudo rm -r -f /opt/cni/bin/*weave*"
|
run_on $host "sudo kubeadm reset && 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
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,6 +22,7 @@ NUM_HOSTS=$(howmany $HOSTS)
|
|||||||
SUCCESS="$(( $NUM_HOSTS * ($NUM_HOSTS-1) )) established"
|
SUCCESS="$(( $NUM_HOSTS * ($NUM_HOSTS-1) )) established"
|
||||||
KUBECTL="sudo kubectl --kubeconfig /etc/kubernetes/admin.conf"
|
KUBECTL="sudo kubectl --kubeconfig /etc/kubernetes/admin.conf"
|
||||||
KUBE_PORT=6443
|
KUBE_PORT=6443
|
||||||
|
WEAVE_NETWORK=10.32.0.0/12
|
||||||
IMAGE=weaveworks/network-tester:latest
|
IMAGE=weaveworks/network-tester:latest
|
||||||
DOMAIN=nettest.default.svc.cluster.local.
|
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
|
for host in $HOSTS; do
|
||||||
if [ $host = $HOST1 ] ; then
|
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
|
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
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -134,6 +139,22 @@ spec:
|
|||||||
run: nettest
|
run: nettest
|
||||||
EOF
|
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}}'")
|
podName=$($SSH $HOST1 "$KUBECTL get pods -l run=nettest -o go-template='{{(index .items 0).metadata.name}}'")
|
||||||
|
|
||||||
check_all_pods_communicate() {
|
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
|
# 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"
|
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
|
# allow access for nettest-deny
|
||||||
run_on $HOST1 "$KUBECTL apply -f -" <<EOF
|
run_on $HOST1 "$KUBECTL apply -f -" <<EOF
|
||||||
apiVersion: networking.k8s.io/v1
|
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"
|
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
|
tear_down_kubeadm
|
||||||
|
|
||||||
# Destroy our test ipset, and implicitly check it is still there
|
# Destroy our test ipset, and implicitly check it is still there
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ function setup_kubernetes_cluster {
|
|||||||
if [ $host = $HOST1 ] ; then
|
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"
|
||||||
else
|
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
|
fi
|
||||||
done
|
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
|
> **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:
|
**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):
|
1. Start the build virtual machine (see above article for more details):
|
||||||
|
|
||||||
|
```
|
||||||
vagrant up
|
vagrant up
|
||||||
|
```
|
||||||
|
|
||||||
2. Start the three testing VMs:
|
2. Start the three testing VMs:
|
||||||
|
|
||||||
|
```
|
||||||
cd test
|
cd test
|
||||||
vagrant up
|
vagrant up
|
||||||
|
```
|
||||||
|
|
||||||
3. SSH into the build VM and go to Weave Net's sources:
|
3. SSH into the build VM and go to Weave Net's sources:
|
||||||
|
|
||||||
|
```
|
||||||
cd ..
|
cd ..
|
||||||
vagrant ssh
|
vagrant ssh
|
||||||
# you are now on the build VM:
|
# you are now on the build VM:
|
||||||
cd ~/weave
|
cd ~/weave
|
||||||
|
```
|
||||||
|
|
||||||
4. Compile all code and dependencies:
|
4. Compile all code and dependencies:
|
||||||
|
|
||||||
|
```
|
||||||
make
|
make
|
||||||
make testrunner
|
make testrunner
|
||||||
cd test
|
cd test
|
||||||
|
```
|
||||||
|
|
||||||
5. Upload the weave images from where the `Makefile` puts them (`weave.tar.gz`) to
|
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:
|
the three docker hosts, `docker load` these, and copies the `weave` script over:
|
||||||
|
|
||||||
|
```
|
||||||
./setup.sh
|
./setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
6. Run individual tests, e.g.:
|
6. Run individual tests, e.g.:
|
||||||
|
|
||||||
|
```
|
||||||
./200_dns_test.sh
|
./200_dns_test.sh
|
||||||
|
```
|
||||||
|
|
||||||
or run all tests (everything named `*_test.sh`):
|
or run all tests (everything named `*_test.sh`):
|
||||||
|
|
||||||
|
```
|
||||||
./run_all.sh
|
./run_all.sh
|
||||||
|
```
|
||||||
|
|
||||||
7. Stop all VMs:
|
7. Stop all VMs:
|
||||||
|
|
||||||
|
```
|
||||||
exit
|
exit
|
||||||
# you are now on your host machine
|
# you are now on your host machine
|
||||||
vagrant destroy -f
|
vagrant destroy -f
|
||||||
cd test
|
cd test
|
||||||
vagrant destroy -f
|
vagrant destroy -f
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Using other VMs
|
## 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.
|
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.
|
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.
|
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|
|
config.vm.define hostname do |host|
|
||||||
host.vm.provider :virtualbox do |vb|
|
host.vm.provider :virtualbox do |vb|
|
||||||
vb.name = hostname
|
vb.name = hostname
|
||||||
vb.memory = 384
|
vb.memory = 2048
|
||||||
configure_nat_dns(vb)
|
configure_nat_dns(vb)
|
||||||
end
|
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.
@@ -15,7 +15,9 @@ MIN_DOCKER_VERSION=1.10.0
|
|||||||
DOCKERHUB_USER=${DOCKERHUB_USER:-weaveworks}
|
DOCKERHUB_USER=${DOCKERHUB_USER:-weaveworks}
|
||||||
BASE_EXEC_IMAGE=$DOCKERHUB_USER/weaveexec
|
BASE_EXEC_IMAGE=$DOCKERHUB_USER/weaveexec
|
||||||
EXEC_IMAGE=$BASE_EXEC_IMAGE:$IMAGE_VERSION
|
EXEC_IMAGE=$BASE_EXEC_IMAGE:$IMAGE_VERSION
|
||||||
WEAVEDB_IMAGE=$DOCKERHUB_USER/weavedb
|
WEAVEDB_IMAGE=$DOCKERHUB_USER/weavedb:latest
|
||||||
|
BASE_IMAGE=$DOCKERHUB_USER/weave
|
||||||
|
IMAGE=$BASE_IMAGE:$IMAGE_VERSION
|
||||||
PROXY_HOST=${PROXY_HOST:-$(echo "${DOCKER_HOST#tcp://}" | cut -s -d: -f1)}
|
PROXY_HOST=${PROXY_HOST:-$(echo "${DOCKER_HOST#tcp://}" | cut -s -d: -f1)}
|
||||||
PROXY_HOST=${PROXY_HOST:-127.0.0.1}
|
PROXY_HOST=${PROXY_HOST:-127.0.0.1}
|
||||||
DOCKER_CLIENT_HOST=${DOCKER_CLIENT_HOST:-$DOCKER_HOST}
|
DOCKER_CLIENT_HOST=${DOCKER_CLIENT_HOST:-$DOCKER_HOST}
|
||||||
@@ -217,6 +219,15 @@ case "$1" in
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
launch)
|
||||||
|
# this is necessary because 'util_op run-container' doesn't do
|
||||||
|
# an implied pull like 'docker run' does
|
||||||
|
if [ -z "$IS_LOCAL" ] ; then
|
||||||
|
for img in $IMAGE $WEAVEDB_IMAGE ; do
|
||||||
|
docker inspect -f "_" $img >/dev/null 2>&1 || docker pull $img
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
;;
|
||||||
setup)
|
setup)
|
||||||
# call 'docker pull' outside weaveexec so we don't need a
|
# call 'docker pull' outside weaveexec so we don't need a
|
||||||
# docker binary inside
|
# docker binary inside
|
||||||
@@ -243,8 +254,6 @@ fi
|
|||||||
|
|
||||||
# Default restart policy for router/proxy
|
# Default restart policy for router/proxy
|
||||||
RESTART_POLICY="--restart always"
|
RESTART_POLICY="--restart always"
|
||||||
BASE_IMAGE=$DOCKERHUB_USER/weave
|
|
||||||
IMAGE=$BASE_IMAGE:$IMAGE_VERSION
|
|
||||||
CONTAINER_NAME=${WEAVE_CONTAINER_NAME:-weave}
|
CONTAINER_NAME=${WEAVE_CONTAINER_NAME:-weave}
|
||||||
|
|
||||||
PLUGIN_NAME="$DOCKERHUB_USER/net-plugin"
|
PLUGIN_NAME="$DOCKERHUB_USER/net-plugin"
|
||||||
@@ -396,27 +405,6 @@ detect_bridge_type() {
|
|||||||
MTU=${WEAVE_MTU:-$(cat /sys/class/net/$BRIDGE/mtu)}
|
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() {
|
validate_bridge_type() {
|
||||||
detect_bridge_type || return 0
|
detect_bridge_type || return 0
|
||||||
|
|
||||||
@@ -548,6 +536,7 @@ router_bridge_opts() {
|
|||||||
echo --docker-bridge "$DOCKER_BRIDGE" --weave-bridge "$BRIDGE" --datapath "$DATAPATH"
|
echo --docker-bridge "$DOCKER_BRIDGE" --weave-bridge "$BRIDGE" --datapath "$DATAPATH"
|
||||||
[ -z "$WEAVE_MTU" ] || echo --mtu "$WEAVE_MTU"
|
[ -z "$WEAVE_MTU" ] || echo --mtu "$WEAVE_MTU"
|
||||||
[ -z $WEAVE_NO_FASTDP ] || echo --no-fastdp
|
[ -z $WEAVE_NO_FASTDP ] || echo --no-fastdp
|
||||||
|
[ -z $WEAVE_NO_BRIDGED_FASTDP ] || echo --no-bridged-fastdp
|
||||||
}
|
}
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
@@ -909,9 +898,7 @@ check_overlap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
detect_awsvpc() {
|
detect_awsvpc() {
|
||||||
# Ignoring errors here: if we cannot detect AWSVPC we will skip the relevant
|
[ "$(call_weave GET /ipinfo/tracker 2>/dev/null)" != "awsvpc" ] || AWSVPC=1
|
||||||
# steps, because "attach" should work without the weave router running.
|
|
||||||
[ "$(call_weave GET /ipinfo/tracker)" != "awsvpc" ] || AWSVPC=1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Call IPAM as necessary to lookup or allocate addresses
|
# Call IPAM as necessary to lookup or allocate addresses
|
||||||
@@ -1247,7 +1234,6 @@ launch() {
|
|||||||
--host-root=/host \
|
--host-root=/host \
|
||||||
$(router_bridge_opts) \
|
$(router_bridge_opts) \
|
||||||
--ipalloc-range "$IPRANGE" \
|
--ipalloc-range "$IPRANGE" \
|
||||||
--dns-effective-listen-address $DOCKER_BRIDGE_IP \
|
|
||||||
--dns-listen-address $DOCKER_BRIDGE_IP:53 \
|
--dns-listen-address $DOCKER_BRIDGE_IP:53 \
|
||||||
--http-addr $HTTP_ADDR \
|
--http-addr $HTTP_ADDR \
|
||||||
--status-addr $STATUS_ADDR \
|
--status-addr $STATUS_ADDR \
|
||||||
@@ -1427,7 +1413,6 @@ case "$COMMAND" in
|
|||||||
done
|
done
|
||||||
[ $# -eq 1 ] || usage
|
[ $# -eq 1 ] || usage
|
||||||
CONTAINER=$(util_op container-id $1)
|
CONTAINER=$(util_op container-id $1)
|
||||||
create_bridge
|
|
||||||
ipam_cidrs allocate $CONTAINER $CIDR_ARGS
|
ipam_cidrs allocate $CONTAINER $CIDR_ARGS
|
||||||
if [ -n "$REWRITE_HOSTS" ] ; then
|
if [ -n "$REWRITE_HOSTS" ] ; then
|
||||||
# rewrite /etc/hosts, unlinking the file (so Docker does
|
# rewrite /etc/hosts, unlinking the file (so Docker does
|
||||||
@@ -1435,7 +1420,7 @@ case "$COMMAND" in
|
|||||||
util_op rewrite-etc-hosts "$CONTAINER" "$EXEC_IMAGE" "$ALL_CIDRS" $DNS_EXTRA_HOSTS
|
util_op rewrite-etc-hosts "$CONTAINER" "$EXEC_IMAGE" "$ALL_CIDRS" $DNS_EXTRA_HOSTS
|
||||||
fi
|
fi
|
||||||
[ -n "$AWSVPC" ] && ATTACH_ARGS="--no-multicast-route --keep-tx-on"
|
[ -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
|
[ -n "$WITHOUT_DNS" ] || when_weave_running with_container_fqdn $CONTAINER put_dns_fqdn $ALL_CIDRS
|
||||||
show_addrs $ALL_CIDRS
|
show_addrs $ALL_CIDRS
|
||||||
;;
|
;;
|
||||||
@@ -1485,7 +1470,6 @@ case "$COMMAND" in
|
|||||||
[ $# -eq 2 -a "$1" = "-h" ] || usage
|
[ $# -eq 2 -a "$1" = "-h" ] || usage
|
||||||
FQDN="$2"
|
FQDN="$2"
|
||||||
fi
|
fi
|
||||||
create_bridge
|
|
||||||
expose_ip
|
expose_ip
|
||||||
show_addrs $ALL_CIDRS
|
show_addrs $ALL_CIDRS
|
||||||
;;
|
;;
|
||||||
@@ -1493,7 +1477,6 @@ case "$COMMAND" in
|
|||||||
collect_cidr_args "$@"
|
collect_cidr_args "$@"
|
||||||
shift $CIDR_ARG_COUNT
|
shift $CIDR_ARG_COUNT
|
||||||
ipam_cidrs lookup weave:expose $CIDR_ARGS
|
ipam_cidrs lookup weave:expose $CIDR_ARGS
|
||||||
create_bridge
|
|
||||||
for CIDR in $ALL_CIDRS ; do
|
for CIDR in $ALL_CIDRS ; do
|
||||||
if ip addr show dev $BRIDGE | grep -qF $CIDR ; then
|
if ip addr show dev $BRIDGE | grep -qF $CIDR ; then
|
||||||
ip addr del dev $BRIDGE $CIDR
|
ip addr del dev $BRIDGE $CIDR
|
||||||
|
|||||||
Reference in new issue
Block a user