Skip to content

Commit

Permalink
Updating EndpointSlice controllers to avoid duplicate creations
Browse files Browse the repository at this point in the history
This updates the StaleSlices() method in EndpointSliceTracker to also
ensure that the tracker does not have more slices than have been
provided.

Co-Authored-By: Swetha Repakula <srepakula@google.com>
  • Loading branch information
2 people authored and aojea committed May 6, 2021
1 parent b31ecc8 commit 7b3b4fb
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 8 deletions.
31 changes: 29 additions & 2 deletions pkg/controller/endpointslice/endpointslice_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/rand"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
Expand All @@ -56,19 +57,45 @@ type endpointSliceController struct {
}

func newController(nodeNames []string, batchPeriod time.Duration) (*fake.Clientset, *endpointSliceController) {
client := newClientset()
client := fake.NewSimpleClientset()

informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
nodeInformer := informerFactory.Core().V1().Nodes()
indexer := nodeInformer.Informer().GetIndexer()
for _, nodeName := range nodeNames {
indexer.Add(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: nodeName}})
}

esInformer := informerFactory.Discovery().V1beta1().EndpointSlices()
esIndexer := esInformer.Informer().GetIndexer()

// These reactors are required to mock functionality that would be covered
// automatically if we weren't using the fake client.
client.PrependReactor("create", "endpointslices", k8stesting.ReactionFunc(func(action k8stesting.Action) (bool, runtime.Object, error) {
endpointSlice := action.(k8stesting.CreateAction).GetObject().(*discovery.EndpointSlice)

if endpointSlice.ObjectMeta.GenerateName != "" {
endpointSlice.ObjectMeta.Name = fmt.Sprintf("%s-%s", endpointSlice.ObjectMeta.GenerateName, rand.String(8))
endpointSlice.ObjectMeta.GenerateName = ""
}
endpointSlice.Generation = 1
esIndexer.Add(endpointSlice)

return false, endpointSlice, nil
}))
client.PrependReactor("update", "endpointslices", k8stesting.ReactionFunc(func(action k8stesting.Action) (bool, runtime.Object, error) {
endpointSlice := action.(k8stesting.CreateAction).GetObject().(*discovery.EndpointSlice)
endpointSlice.Generation++
esIndexer.Update(endpointSlice)

return false, endpointSlice, nil
}))

esController := NewController(
informerFactory.Core().V1().Pods(),
informerFactory.Core().V1().Services(),
nodeInformer,
informerFactory.Discovery().V1beta1().EndpointSlices(),
esInformer,
int32(100),
client,
batchPeriod)
Expand Down
20 changes: 17 additions & 3 deletions pkg/controller/endpointslice/endpointslice_tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,12 @@ func (est *endpointSliceTracker) ShouldSync(endpointSlice *discovery.EndpointSli
return !ok || endpointSlice.Generation > g
}

// StaleSlices returns true if one or more of the provided EndpointSlices
// have older generations than the corresponding tracked ones or if the tracker
// is expecting one or more of the provided EndpointSlices to be deleted.
// StaleSlices returns true if any of the following are true:
// 1. One or more of the provided EndpointSlices have older generations than the
// corresponding tracked ones.
// 2. The tracker is expecting one or more of the provided EndpointSlices to be
// deleted.
// 3. The tracker is tracking EndpointSlices that have not been provided.
func (est *endpointSliceTracker) StaleSlices(service *v1.Service, endpointSlices []*discovery.EndpointSlice) bool {
est.lock.Lock()
defer est.lock.Unlock()
Expand All @@ -92,12 +95,23 @@ func (est *endpointSliceTracker) StaleSlices(service *v1.Service, endpointSlices
if !ok {
return false
}
providedSlices := map[types.UID]int64{}
for _, endpointSlice := range endpointSlices {
providedSlices[endpointSlice.UID] = endpointSlice.Generation
g, ok := gfs[endpointSlice.UID]
if ok && (g == deletionExpected || g > endpointSlice.Generation) {
return true
}
}
for uid, generation := range gfs {
if generation == deletionExpected {
continue
}
_, ok := providedSlices[uid]
if !ok {
return true
}
}
return false
}

Expand Down
24 changes: 24 additions & 0 deletions pkg/controller/endpointslice/endpointslice_tracker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,30 @@ func TestEndpointSliceTrackerStaleSlices(t *testing.T) {
serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
slicesParam: []*discovery.EndpointSlice{epSlice1NewerGen},
expectNewer: false,
}, {
name: "slice in params is expected to be deleted",
tracker: &endpointSliceTracker{
generationsByService: map[types.NamespacedName]generationsBySlice{
{Name: "svc1", Namespace: "ns1"}: {
epSlice1.UID: deletionExpected,
},
},
},
serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
slicesParam: []*discovery.EndpointSlice{epSlice1},
expectNewer: true,
}, {
name: "slice in tracker but not in params",
tracker: &endpointSliceTracker{
generationsByService: map[types.NamespacedName]generationsBySlice{
{Name: "svc1", Namespace: "ns1"}: {
epSlice1.UID: epSlice1.Generation,
},
},
},
serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
slicesParam: []*discovery.EndpointSlice{},
expectNewer: true,
}}

for _, tc := range testCases {
Expand Down
20 changes: 17 additions & 3 deletions pkg/controller/endpointslicemirroring/endpointslice_tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,12 @@ func (est *endpointSliceTracker) ShouldSync(endpointSlice *discovery.EndpointSli
return !ok || endpointSlice.Generation > g
}

// StaleSlices returns true if one or more of the provided EndpointSlices
// have older generations than the corresponding tracked ones or if the tracker
// is expecting one or more of the provided EndpointSlices to be deleted.
// StaleSlices returns true if any of the following are true:
// 1. One or more of the provided EndpointSlices have older generations than the
// corresponding tracked ones.
// 2. The tracker is expecting one or more of the provided EndpointSlices to be
// deleted.
// 3. The tracker is tracking EndpointSlices that have not been provided.
func (est *endpointSliceTracker) StaleSlices(service *v1.Service, endpointSlices []*discovery.EndpointSlice) bool {
est.lock.Lock()
defer est.lock.Unlock()
Expand All @@ -92,12 +95,23 @@ func (est *endpointSliceTracker) StaleSlices(service *v1.Service, endpointSlices
if !ok {
return false
}
providedSlices := map[types.UID]int64{}
for _, endpointSlice := range endpointSlices {
providedSlices[endpointSlice.UID] = endpointSlice.Generation
g, ok := gfs[endpointSlice.UID]
if ok && (g == deletionExpected || g > endpointSlice.Generation) {
return true
}
}
for uid, generation := range gfs {
if generation == deletionExpected {
continue
}
_, ok := providedSlices[uid]
if !ok {
return true
}
}
return false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,30 @@ func TestEndpointSliceTrackerStaleSlices(t *testing.T) {
serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
slicesParam: []*discovery.EndpointSlice{epSlice1NewerGen},
expectNewer: false,
}, {
name: "slice in params is expected to be deleted",
tracker: &endpointSliceTracker{
generationsByService: map[types.NamespacedName]generationsBySlice{
{Name: "svc1", Namespace: "ns1"}: {
epSlice1.UID: deletionExpected,
},
},
},
serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
slicesParam: []*discovery.EndpointSlice{epSlice1},
expectNewer: true,
}, {
name: "slice in tracker but not in params",
tracker: &endpointSliceTracker{
generationsByService: map[types.NamespacedName]generationsBySlice{
{Name: "svc1", Namespace: "ns1"}: {
epSlice1.UID: epSlice1.Generation,
},
},
},
serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
slicesParam: []*discovery.EndpointSlice{},
expectNewer: true,
}}

for _, tc := range testCases {
Expand Down

0 comments on commit 7b3b4fb

Please sign in to comment.