mirror of
https://github.com/stakater/Reloader.git
synced 2026-05-17 06:06:39 +00:00
Add e2e parallel
Signed-off-by: faizanahmad055 <faizan.ahmad55@outlook.com>
This commit is contained in:
@@ -2,7 +2,6 @@ package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -48,11 +47,46 @@ type Controller struct {
|
||||
// read by the informer event handlers, so they must be atomic.
|
||||
var secretControllerInitialized atomic.Bool
|
||||
var configmapControllerInitialized atomic.Bool
|
||||
var selectedNamespacesCache []string
|
||||
|
||||
// selectedNamespacesCache holds an immutable snapshot of the set of namespace
|
||||
// names that match the namespace label selector. Written exclusively by the
|
||||
// namespace controller's informer goroutine; read concurrently by configmap/
|
||||
// secret controller informer goroutines. Using atomic.Value with an immutable
|
||||
// map[string]struct{} snapshot avoids mutexes and prevents data races.
|
||||
var selectedNamespacesCache atomic.Value // always stores map[string]struct{}
|
||||
|
||||
// loadSelectedNamespaces returns the current namespace snapshot (never nil).
|
||||
func loadSelectedNamespaces() map[string]struct{} {
|
||||
if v := selectedNamespacesCache.Load(); v != nil {
|
||||
return v.(map[string]struct{})
|
||||
}
|
||||
return map[string]struct{}{}
|
||||
}
|
||||
|
||||
// storeSelectedNamespaces replaces the current snapshot with one built from ns.
|
||||
// It is the only mutator of selectedNamespacesCache and is called only from
|
||||
// the namespace controller's informer goroutine (or from tests for setup).
|
||||
func storeSelectedNamespaces(ns []string) {
|
||||
m := make(map[string]struct{}, len(ns))
|
||||
for _, n := range ns {
|
||||
m[n] = struct{}{}
|
||||
}
|
||||
selectedNamespacesCache.Store(m)
|
||||
}
|
||||
|
||||
// loadSelectedNamespacesList returns the current namespace names as a slice.
|
||||
// Intended for use in tests where slice-based assertions are more convenient.
|
||||
func loadSelectedNamespacesList() []string {
|
||||
m := loadSelectedNamespaces()
|
||||
result := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
result = append(result, k)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// NewController for initializing a Controller
|
||||
func NewController(client kubernetes.Interface, resource string, namespace string, ignoredNamespaces []string, namespaceLabelSelector string, resourceLabelSelector string, collectors metrics.Collectors) (*Controller,
|
||||
error) {
|
||||
func NewController(client kubernetes.Interface, resource string, namespace string, ignoredNamespaces []string, namespaceLabelSelector string, resourceLabelSelector string, collectors metrics.Collectors) (*Controller, error) {
|
||||
if options.SyncAfterRestart {
|
||||
secretControllerInitialized.Store(true)
|
||||
configmapControllerInitialized.Store(true)
|
||||
@@ -155,36 +189,45 @@ func (c *Controller) resourceInSelectedNamespaces(raw interface{}) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
namespaces := loadSelectedNamespaces()
|
||||
var ns string
|
||||
switch object := raw.(type) {
|
||||
case *v1.ConfigMap:
|
||||
if slices.Contains(selectedNamespacesCache, object.GetNamespace()) {
|
||||
return true
|
||||
}
|
||||
ns = object.GetNamespace()
|
||||
case *v1.Secret:
|
||||
if slices.Contains(selectedNamespacesCache, object.GetNamespace()) {
|
||||
return true
|
||||
}
|
||||
ns = object.GetNamespace()
|
||||
case *csiv1.SecretProviderClassPodStatus:
|
||||
if slices.Contains(selectedNamespacesCache, object.GetNamespace()) {
|
||||
return true
|
||||
}
|
||||
ns = object.GetNamespace()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
_, ok := namespaces[ns]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (c *Controller) addSelectedNamespaceToCache(namespace v1.Namespace) {
|
||||
selectedNamespacesCache = append(selectedNamespacesCache, namespace.GetName())
|
||||
old := loadSelectedNamespaces()
|
||||
next := make(map[string]struct{}, len(old)+1)
|
||||
for k := range old {
|
||||
next[k] = struct{}{}
|
||||
}
|
||||
next[namespace.GetName()] = struct{}{}
|
||||
selectedNamespacesCache.Store(next)
|
||||
logrus.Infof("added namespace to be watched: %s", namespace.GetName())
|
||||
}
|
||||
|
||||
func (c *Controller) removeSelectedNamespaceFromCache(namespace v1.Namespace) {
|
||||
for i, v := range selectedNamespacesCache {
|
||||
if v == namespace.GetName() {
|
||||
selectedNamespacesCache = append(selectedNamespacesCache[:i], selectedNamespacesCache[i+1:]...)
|
||||
logrus.Infof("removed namespace from watch: %s", namespace.GetName())
|
||||
return
|
||||
}
|
||||
old := loadSelectedNamespaces()
|
||||
if _, ok := old[namespace.GetName()]; !ok {
|
||||
return
|
||||
}
|
||||
next := make(map[string]struct{}, len(old))
|
||||
for k := range old {
|
||||
next[k] = struct{}{}
|
||||
}
|
||||
delete(next, namespace.GetName())
|
||||
selectedNamespacesCache.Store(next)
|
||||
logrus.Infof("removed namespace from watch: %s", namespace.GetName())
|
||||
}
|
||||
|
||||
// Update function to add an old object and a new object to the queue in case of updating a resource
|
||||
@@ -319,6 +362,9 @@ func (c *Controller) processNextItem() bool {
|
||||
rh, ok := resourceHandler.(handler.ResourceHandler)
|
||||
if !ok {
|
||||
logrus.Errorf("Invalid resource handler type: %T", resourceHandler)
|
||||
// Clear rate-limiter state so the item doesn't leak memory in the queue.
|
||||
c.queue.Forget(resourceHandler)
|
||||
c.collectors.RecordError("invalid_handler_type")
|
||||
return true
|
||||
}
|
||||
err := rh.Handle()
|
||||
|
||||
@@ -45,7 +45,7 @@ func (m *mockResourceHandler) GetEnqueueTime() time.Time {
|
||||
func resetGlobalState() {
|
||||
secretControllerInitialized.Store(false)
|
||||
configmapControllerInitialized.Store(false)
|
||||
selectedNamespacesCache = []string{}
|
||||
storeSelectedNamespaces([]string{})
|
||||
}
|
||||
|
||||
// newTestController creates a controller for testing without starting informers
|
||||
@@ -223,12 +223,12 @@ func TestResourceInSelectedNamespaces(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.name, func(t *testing.T) {
|
||||
resetGlobalState()
|
||||
selectedNamespacesCache = tt.cachedNamespaces
|
||||
resetGlobalState()
|
||||
storeSelectedNamespaces(tt.cachedNamespaces)
|
||||
|
||||
c := newTestController([]string{}, tt.namespaceSelector)
|
||||
result := c.resourceInSelectedNamespaces(tt.resource)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
c := newTestController([]string{}, tt.namespaceSelector)
|
||||
result := c.resourceInSelectedNamespaces(tt.resource)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -244,17 +244,17 @@ func TestAddSelectedNamespaceToCache(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "namespace-1"},
|
||||
}
|
||||
c.addSelectedNamespaceToCache(ns1)
|
||||
assert.Contains(t, selectedNamespacesCache, "namespace-1")
|
||||
assert.Len(t, selectedNamespacesCache, 1)
|
||||
assert.Contains(t, loadSelectedNamespaces(), "namespace-1")
|
||||
assert.Len(t, loadSelectedNamespaces(), 1)
|
||||
|
||||
// Add second namespace
|
||||
ns2 := v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "namespace-2"},
|
||||
}
|
||||
c.addSelectedNamespaceToCache(ns2)
|
||||
assert.Contains(t, selectedNamespacesCache, "namespace-1")
|
||||
assert.Contains(t, selectedNamespacesCache, "namespace-2")
|
||||
assert.Len(t, selectedNamespacesCache, 2)
|
||||
assert.Contains(t, loadSelectedNamespaces(), "namespace-1")
|
||||
assert.Contains(t, loadSelectedNamespaces(), "namespace-2")
|
||||
assert.Len(t, loadSelectedNamespaces(), 2)
|
||||
}
|
||||
|
||||
func TestRemoveSelectedNamespaceFromCache(t *testing.T) {
|
||||
@@ -293,16 +293,16 @@ func TestRemoveSelectedNamespaceFromCache(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.name, func(t *testing.T) {
|
||||
resetGlobalState()
|
||||
selectedNamespacesCache = tt.initialCache
|
||||
resetGlobalState()
|
||||
storeSelectedNamespaces(tt.initialCache)
|
||||
|
||||
c := newTestController([]string{}, "env=prod")
|
||||
ns := v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: tt.namespaceToRemove},
|
||||
}
|
||||
c.removeSelectedNamespaceFromCache(ns)
|
||||
c := newTestController([]string{}, "env=prod")
|
||||
ns := v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: tt.namespaceToRemove},
|
||||
}
|
||||
c.removeSelectedNamespaceFromCache(ns)
|
||||
|
||||
assert.Equal(t, tt.expectedCache, selectedNamespacesCache)
|
||||
assert.ElementsMatch(t, tt.expectedCache, loadSelectedNamespacesList())
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -500,10 +500,10 @@ func TestUpdateHandler(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.name, func(t *testing.T) {
|
||||
resetGlobalState()
|
||||
if tt.cachedNamespaces != nil {
|
||||
selectedNamespacesCache = tt.cachedNamespaces
|
||||
}
|
||||
resetGlobalState()
|
||||
if tt.cachedNamespaces != nil {
|
||||
storeSelectedNamespaces(tt.cachedNamespaces)
|
||||
}
|
||||
|
||||
c := newTestController(tt.ignoredNamespaces, tt.namespaceSelector)
|
||||
c.Update(tt.oldResource, tt.newResource)
|
||||
@@ -675,13 +675,13 @@ func TestAddHandlerWithNamespaceEvent(t *testing.T) {
|
||||
|
||||
c.Add(ns)
|
||||
|
||||
assert.Contains(t, selectedNamespacesCache, "new-namespace")
|
||||
assert.Contains(t, loadSelectedNamespaces(), "new-namespace")
|
||||
assert.Equal(t, 0, c.queue.Len(), "Namespace add should not queue anything")
|
||||
}
|
||||
|
||||
func TestDeleteHandlerWithNamespaceEvent(t *testing.T) {
|
||||
resetGlobalState()
|
||||
selectedNamespacesCache = []string{"ns-1", "ns-to-delete", "ns-2"}
|
||||
storeSelectedNamespaces([]string{"ns-1", "ns-to-delete", "ns-2"})
|
||||
|
||||
c := newTestController([]string{}, "env=prod")
|
||||
options.ReloadOnDelete = "true"
|
||||
@@ -694,9 +694,9 @@ func TestDeleteHandlerWithNamespaceEvent(t *testing.T) {
|
||||
|
||||
c.Delete(ns)
|
||||
|
||||
assert.NotContains(t, selectedNamespacesCache, "ns-to-delete")
|
||||
assert.Contains(t, selectedNamespacesCache, "ns-1")
|
||||
assert.Contains(t, selectedNamespacesCache, "ns-2")
|
||||
assert.NotContains(t, loadSelectedNamespaces(), "ns-to-delete")
|
||||
assert.Contains(t, loadSelectedNamespaces(), "ns-1")
|
||||
assert.Contains(t, loadSelectedNamespaces(), "ns-2")
|
||||
assert.Equal(t, 0, c.queue.Len(), "Namespace delete should not queue anything")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user