refactor: Re-use a lot of code and move to specific packages

This commit is contained in:
TheiLLeniumStudios
2025-12-28 08:47:55 +01:00
parent 3defc8bb29
commit c19058a66e
16 changed files with 251 additions and 717 deletions
+12
View File
@@ -17,6 +17,18 @@ const (
ResourceTypeSecret ResourceType = "secret"
)
// Kind returns the capitalized Kubernetes Kind (e.g., "ConfigMap", "Secret").
func (r ResourceType) Kind() string {
switch r {
case ResourceTypeConfigMap:
return "ConfigMap"
case ResourceTypeSecret:
return "Secret"
default:
return string(r)
}
}
// MatchResult contains the result of checking if a workload should be reloaded.
type MatchResult struct {
ShouldReload bool
+25 -42
View File
@@ -8,68 +8,51 @@ import (
"sigs.k8s.io/controller-runtime/pkg/predicate"
)
// ConfigMapPredicates returns predicates for filtering ConfigMap events.
func ConfigMapPredicates(cfg *config.Config, hasher *Hasher) predicate.Predicate {
// resourcePredicates returns predicates for filtering resource events.
// The hashFn computes a hash from old and new objects to detect content changes.
func resourcePredicates(cfg *config.Config, hashFn func(old, new client.Object) (string, string, bool)) predicate.Predicate {
return predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
// Only process create events if ReloadOnCreate is enabled
// or if SyncAfterRestart is enabled (for initial sync)
return cfg.ReloadOnCreate || cfg.SyncAfterRestart
},
UpdateFunc: func(e event.UpdateEvent) bool {
// Always process updates, but filter by content change
oldCM, okOld := e.ObjectOld.(*corev1.ConfigMap)
newCM, okNew := e.ObjectNew.(*corev1.ConfigMap)
if !okOld || !okNew {
oldHash, newHash, ok := hashFn(e.ObjectOld, e.ObjectNew)
if !ok {
return false
}
// Check if the data actually changed
oldHash := hasher.HashConfigMap(oldCM)
newHash := hasher.HashConfigMap(newCM)
return oldHash != newHash
},
DeleteFunc: func(e event.DeleteEvent) bool {
// Only process delete events if ReloadOnDelete is enabled
return cfg.ReloadOnDelete
},
GenericFunc: func(e event.GenericEvent) bool {
// Ignore generic events
return false
},
}
}
// ConfigMapPredicates returns predicates for filtering ConfigMap events.
func ConfigMapPredicates(cfg *config.Config, hasher *Hasher) predicate.Predicate {
return resourcePredicates(cfg, func(old, new client.Object) (string, string, bool) {
oldCM, okOld := old.(*corev1.ConfigMap)
newCM, okNew := new.(*corev1.ConfigMap)
if !okOld || !okNew {
return "", "", false
}
return hasher.HashConfigMap(oldCM), hasher.HashConfigMap(newCM), true
})
}
// SecretPredicates returns predicates for filtering Secret events.
func SecretPredicates(cfg *config.Config, hasher *Hasher) predicate.Predicate {
return predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
// Only process create events if ReloadOnCreate is enabled
// or if SyncAfterRestart is enabled (for initial sync)
return cfg.ReloadOnCreate || cfg.SyncAfterRestart
},
UpdateFunc: func(e event.UpdateEvent) bool {
// Always process updates, but filter by content change
oldSecret, okOld := e.ObjectOld.(*corev1.Secret)
newSecret, okNew := e.ObjectNew.(*corev1.Secret)
if !okOld || !okNew {
return false
}
// Check if the data actually changed
oldHash := hasher.HashSecret(oldSecret)
newHash := hasher.HashSecret(newSecret)
return oldHash != newHash
},
DeleteFunc: func(e event.DeleteEvent) bool {
// Only process delete events if ReloadOnDelete is enabled
return cfg.ReloadOnDelete
},
GenericFunc: func(e event.GenericEvent) bool {
// Ignore generic events
return false
},
}
return resourcePredicates(cfg, func(old, new client.Object) (string, string, bool) {
oldSecret, okOld := old.(*corev1.Secret)
newSecret, okNew := new.(*corev1.Secret)
if !okOld || !okNew {
return "", "", false
}
return hasher.HashSecret(oldSecret), hasher.HashSecret(newSecret), true
})
}
// NamespaceChecker defines the interface for checking if a namespace is allowed.
+37 -34
View File
@@ -28,18 +28,45 @@ func NewService(cfg *config.Config) *Service {
}
}
// ResourceChange represents a change event for a ConfigMap or Secret.
type ResourceChange interface {
IsNil() bool
GetEventType() EventType
GetName() string
GetNamespace() string
GetAnnotations() map[string]string
GetResourceType() ResourceType
ComputeHash(hasher *Hasher) string
}
// ConfigMapChange represents a change event for a ConfigMap.
type ConfigMapChange struct {
ConfigMap *corev1.ConfigMap
EventType EventType
}
func (c ConfigMapChange) IsNil() bool { return c.ConfigMap == nil }
func (c ConfigMapChange) GetEventType() EventType { return c.EventType }
func (c ConfigMapChange) GetName() string { return c.ConfigMap.Name }
func (c ConfigMapChange) GetNamespace() string { return c.ConfigMap.Namespace }
func (c ConfigMapChange) GetAnnotations() map[string]string { return c.ConfigMap.Annotations }
func (c ConfigMapChange) GetResourceType() ResourceType { return ResourceTypeConfigMap }
func (c ConfigMapChange) ComputeHash(h *Hasher) string { return h.HashConfigMap(c.ConfigMap) }
// SecretChange represents a change event for a Secret.
type SecretChange struct {
Secret *corev1.Secret
EventType EventType
}
func (c SecretChange) IsNil() bool { return c.Secret == nil }
func (c SecretChange) GetEventType() EventType { return c.EventType }
func (c SecretChange) GetName() string { return c.Secret.Name }
func (c SecretChange) GetNamespace() string { return c.Secret.Namespace }
func (c SecretChange) GetAnnotations() map[string]string { return c.Secret.Annotations }
func (c SecretChange) GetResourceType() ResourceType { return ResourceTypeSecret }
func (c SecretChange) ComputeHash(h *Hasher) string { return h.HashSecret(c.Secret) }
// EventType represents the type of change event.
type EventType string
@@ -77,55 +104,31 @@ func FilterDecisions(decisions []ReloadDecision) []ReloadDecision {
return result
}
// ProcessConfigMap evaluates all workloads to determine which should be reloaded.
func (s *Service) ProcessConfigMap(change ConfigMapChange, workloads []workload.WorkloadAccessor) []ReloadDecision {
if change.ConfigMap == nil {
// Process evaluates all workloads to determine which should be reloaded.
func (s *Service) Process(change ResourceChange, workloads []workload.WorkloadAccessor) []ReloadDecision {
if change.IsNil() {
return nil
}
if !s.shouldProcessEvent(change.EventType) {
if !s.shouldProcessEvent(change.GetEventType()) {
return nil
}
hash := s.hasher.HashConfigMap(change.ConfigMap)
if change.EventType == EventTypeDelete {
hash := change.ComputeHash(s.hasher)
if change.GetEventType() == EventTypeDelete {
hash = s.hasher.EmptyHash()
}
return s.processResource(
change.ConfigMap.Name,
change.ConfigMap.Namespace,
change.ConfigMap.Annotations,
ResourceTypeConfigMap,
change.GetName(),
change.GetNamespace(),
change.GetAnnotations(),
change.GetResourceType(),
hash,
workloads,
)
}
// ProcessSecret evaluates all workloads to determine which should be reloaded.
func (s *Service) ProcessSecret(change SecretChange, workloads []workload.WorkloadAccessor) []ReloadDecision {
if change.Secret == nil {
return nil
}
if !s.shouldProcessEvent(change.EventType) {
return nil
}
hash := s.hasher.HashSecret(change.Secret)
if change.EventType == EventTypeDelete {
hash = s.hasher.EmptyHash()
}
return s.processResource(
change.Secret.Name,
change.Secret.Namespace,
change.Secret.Annotations,
ResourceTypeSecret,
hash,
workloads,
)
}
func (s *Service) processResource(
resourceName string,
+8 -8
View File
@@ -51,7 +51,7 @@ func TestService_ProcessConfigMap_AutoReload(t *testing.T) {
EventType: EventTypeUpdate,
}
decisions := svc.ProcessConfigMap(change, workloads)
decisions := svc.Process(change, workloads)
if len(decisions) != 1 {
t.Fatalf("Expected 1 decision, got %d", len(decisions))
@@ -98,7 +98,7 @@ func TestService_ProcessConfigMap_ExplicitAnnotation(t *testing.T) {
EventType: EventTypeUpdate,
}
decisions := svc.ProcessConfigMap(change, workloads)
decisions := svc.Process(change, workloads)
if len(decisions) != 1 {
t.Fatalf("Expected 1 decision, got %d", len(decisions))
@@ -157,7 +157,7 @@ func TestService_ProcessConfigMap_IgnoredResource(t *testing.T) {
EventType: EventTypeUpdate,
}
decisions := svc.ProcessConfigMap(change, workloads)
decisions := svc.Process(change, workloads)
// Should still get a decision, but ShouldReload should be false
for _, d := range decisions {
@@ -205,7 +205,7 @@ func TestService_ProcessSecret_AutoReload(t *testing.T) {
EventType: EventTypeUpdate,
}
decisions := svc.ProcessSecret(change, workloads)
decisions := svc.Process(change, workloads)
if len(decisions) != 1 {
t.Fatalf("Expected 1 decision, got %d", len(decisions))
@@ -246,7 +246,7 @@ func TestService_ProcessConfigMap_DeleteEvent(t *testing.T) {
EventType: EventTypeDelete,
}
decisions := svc.ProcessConfigMap(change, workloads)
decisions := svc.Process(change, workloads)
if len(decisions) != 1 {
t.Fatalf("Expected 1 decision, got %d", len(decisions))
@@ -287,7 +287,7 @@ func TestService_ProcessConfigMap_DeleteEventDisabled(t *testing.T) {
EventType: EventTypeDelete,
}
decisions := svc.ProcessConfigMap(change, workloads)
decisions := svc.Process(change, workloads)
// Should return nil when delete events are disabled
if decisions != nil {
@@ -496,7 +496,7 @@ func TestService_ProcessConfigMap_MultipleWorkloads(t *testing.T) {
EventType: EventTypeUpdate,
}
decisions := svc.ProcessConfigMap(change, workloads)
decisions := svc.Process(change, workloads)
if len(decisions) != 3 {
t.Fatalf("Expected 3 decisions, got %d", len(decisions))
@@ -572,7 +572,7 @@ func TestService_ProcessConfigMap_DifferentNamespaces(t *testing.T) {
EventType: EventTypeUpdate,
}
decisions := svc.ProcessConfigMap(change, workloads)
decisions := svc.Process(change, workloads)
// Should only affect deploy1 (same namespace)
reloadCount := 0