Compare commits

...
11 Commits
Author SHA1 Message Date
Bryan Boreham 59fe76f29b Add release v2.1.2 2017-11-27 14:06:33 +00:00
Bryan Boreham 2ab01b55c9 Merge branch 'master' into 2.1 2017-11-27 11:45:15 +00:00
Bryan Boreham 461f9e736b Merge pull request #3186 from weaveworks/issues/3184-update-mesh
Update vendored mesh dep
2017-11-27 11:34:15 +00:00
Bryan Boreham 8e16b03ae8 Merge pull request #3181 from weaveworks/issues/3177-ooo
npc: Track ipset entries per user and refcount default-allow ipsets
2017-11-27 11:01:59 +00:00
Martynas Pumputis 6469809b9a Update vendored mesh dep 2017-11-22 16:07:19 +02:00
Martynas Pumputis 0a0dbe115a npc: Track ipset entries per user
There was a bug in handling "default-allow" ipsets - it could not
cope with out of order receive of Pod updates. E.g:

    1) AddPod(Pod_A, PodIP_A),
    2) AddPod(Pod_B, PodIP_A),
    3) DeletePod(Pod_A),

would result with PodIP_A being removed from "default-allow" ipset,
as such ipset entries were not refcounted. Bypassing of refcounting
was due to the fact that adding a netpol which dst-selects a Pod which
IP addr is in "default-allow" ipset has to remove the IP addr from
the ipset regardless of refcount value of the IP addr entry in the
ipset. So, refcounting in such case didn't make sense and it was
bypassed.

This commit changes the way entries are refcounted in ipsets, and
it makes "default-allow" ipset the same citizen as other ipsets, so
from now "default-allow" ipset is refcounted.
2017-11-21 14:41:29 +02:00
Bryan Boreham ebbf3d4244 Add release v2.1.1 2017-11-17 14:17:31 +00:00
Bryan Boreham ceb36d1891 Typo 2017-11-17 14:15:10 +00:00
Bryan Boreham 4caa850853 Merge pull request #3176 from weaveworks/issues/3175-fix-install
Fix 2.1.0 install issues
2017-11-17 14:14:41 +00:00
Bryan Boreham 96cecf1796 Pull images before running inside weaveexec container
Now we don't bundle the docker command inside weaveexec, we need to do
an explicit 'docker pull' to ensure the images are there.

Also set the weavedb tag to :latest to remove a warning from docker
2017-11-17 13:51:10 +00:00
Bryan Boreham 51c7af8fb4 Set IMAGE before using it 2017-11-17 13:31:46 +00:00
8 changed files with 274 additions and 169 deletions

No files matched your search

+20 -1
View File
@@ -1,3 +1,22 @@
## 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 +54,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
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
}
}
+28 -25
View File
@@ -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
View File
@@ -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
} }
} }
+12 -3
View File
@@ -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"