From c19058a66e4d6594a9fe2fd7c4150a4cc789e0c0 Mon Sep 17 00:00:00 2001 From: TheiLLeniumStudios <104288623+TheiLLeniumStudios@users.noreply.github.com> Date: Sun, 28 Dec 2025 08:47:55 +0100 Subject: [PATCH] refactor: Re-use a lot of code and move to specific packages --- .../pkg/controller/configmap_reconciler.go | 8 +- .../pkg/controller/deployment_reconciler.go | 21 +-- internal/pkg/controller/handler.go | 12 +- internal/pkg/controller/retry.go | 138 +++++++----------- internal/pkg/controller/secret_reconciler.go | 8 +- internal/pkg/reload/matcher.go | 12 ++ internal/pkg/reload/predicate.go | 67 ++++----- internal/pkg/reload/service.go | 71 ++++----- internal/pkg/reload/service_test.go | 16 +- internal/pkg/workload/cronjob.go | 92 +----------- internal/pkg/workload/daemonset.go | 88 +---------- internal/pkg/workload/deployment.go | 90 +----------- internal/pkg/workload/job.go | 88 +---------- internal/pkg/workload/rollout.go | 92 +----------- internal/pkg/workload/statefulset.go | 88 +---------- internal/pkg/workload/uses.go | 77 ++++++++++ 16 files changed, 251 insertions(+), 717 deletions(-) create mode 100644 internal/pkg/workload/uses.go diff --git a/internal/pkg/controller/configmap_reconciler.go b/internal/pkg/controller/configmap_reconciler.go index 3f744ea5..ef60e9fc 100644 --- a/internal/pkg/controller/configmap_reconciler.go +++ b/internal/pkg/controller/configmap_reconciler.go @@ -62,9 +62,9 @@ func (r *ConfigMapReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, nil } - return r.reloadHandler().Process(ctx, cm.Namespace, cm.Name, "ConfigMap", reload.ResourceTypeConfigMap, + return r.reloadHandler().Process(ctx, cm.Namespace, cm.Name, reload.ResourceTypeConfigMap, func(workloads []workload.WorkloadAccessor) []reload.ReloadDecision { - return r.ReloadService.ProcessConfigMap(reload.ConfigMapChange{ + return r.ReloadService.Process(reload.ConfigMapChange{ ConfigMap: &cm, EventType: reload.EventTypeUpdate, }, workloads) @@ -81,9 +81,9 @@ func (r *ConfigMapReconciler) handleDelete(ctx context.Context, req ctrl.Request cm.Name = req.Name cm.Namespace = req.Namespace - return r.reloadHandler().Process(ctx, req.Namespace, req.Name, "ConfigMap", reload.ResourceTypeConfigMap, + return r.reloadHandler().Process(ctx, req.Namespace, req.Name, reload.ResourceTypeConfigMap, func(workloads []workload.WorkloadAccessor) []reload.ReloadDecision { - return r.ReloadService.ProcessConfigMap(reload.ConfigMapChange{ + return r.ReloadService.Process(reload.ConfigMapChange{ ConfigMap: cm, EventType: reload.EventTypeDelete, }, workloads) diff --git a/internal/pkg/controller/deployment_reconciler.go b/internal/pkg/controller/deployment_reconciler.go index d28272c5..08c71ab4 100644 --- a/internal/pkg/controller/deployment_reconciler.go +++ b/internal/pkg/controller/deployment_reconciler.go @@ -26,6 +26,7 @@ type DeploymentReconciler struct { // Reconcile handles Deployment pause expiration. func (r *DeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.Log.WithValues("deployment", req.NamespacedName) + log.Info("Deployment reconciling ", "namespace", req.Namespace, "name", req.Name) var deploy appsv1.Deployment if err := r.Get(ctx, req.NamespacedName, &deploy); err != nil { @@ -76,14 +77,16 @@ func (r *DeploymentReconciler) SetupWithManager(mgr ctrl.Manager) error { // pausedByReloaderPredicate returns a predicate that only selects deployments // that have been paused by Reloader (have the paused-at annotation). func (r *DeploymentReconciler) pausedByReloaderPredicate() predicate.Predicate { - return predicate.NewPredicateFuncs(func(obj client.Object) bool { - annotations := obj.GetAnnotations() - if annotations == nil { - return false - } + return predicate.NewPredicateFuncs( + func(obj client.Object) bool { + annotations := obj.GetAnnotations() + if annotations == nil { + return false + } - // Only process if deployment has our paused-at annotation - _, hasPausedAt := annotations[r.Config.Annotations.PausedAt] - return hasPausedAt - }) + // Only process if deployment has our paused-at annotation + _, hasPausedAt := annotations[r.Config.Annotations.PausedAt] + return hasPausedAt + }, + ) } diff --git a/internal/pkg/controller/handler.go b/internal/pkg/controller/handler.go index b3ac4d2f..caa71fb8 100644 --- a/internal/pkg/controller/handler.go +++ b/internal/pkg/controller/handler.go @@ -32,7 +32,7 @@ type ReloadHandler struct { // Process handles the reload workflow: list workloads, get decisions, webhook or apply. func (h *ReloadHandler) Process( ctx context.Context, - namespace, resourceName, resourceKind string, + namespace, resourceName string, resourceType reload.ResourceType, getDecisions func([]workload.WorkloadAccessor) []reload.ReloadDecision, log logr.Logger, @@ -49,7 +49,7 @@ func (h *ReloadHandler) Process( return h.sendWebhook(ctx, resourceName, namespace, resourceType, decisions, log) } - h.applyReloads(ctx, resourceName, namespace, resourceKind, resourceType, decisions, log) + h.applyReloads(ctx, resourceName, namespace, resourceType, decisions, log) return ctrl.Result{}, nil } @@ -99,7 +99,7 @@ func (h *ReloadHandler) sendWebhook( func (h *ReloadHandler) applyReloads( ctx context.Context, - resourceName, resourceNamespace, resourceKind string, + resourceName, resourceNamespace string, resourceType reload.ResourceType, decisions []reload.ReloadDecision, log logr.Logger, @@ -127,13 +127,13 @@ func (h *ReloadHandler) applyReloads( "workload", decision.Workload.GetName(), "kind", decision.Workload.Kind(), ) - h.EventRecorder.ReloadFailed(decision.Workload.GetObject(), resourceKind, resourceName, err) + h.EventRecorder.ReloadFailed(decision.Workload.GetObject(), resourceType.Kind(), resourceName, err) h.Collectors.RecordReload(false, resourceNamespace) continue } if updated { - h.EventRecorder.ReloadSuccess(decision.Workload.GetObject(), resourceKind, resourceName) + h.EventRecorder.ReloadSuccess(decision.Workload.GetObject(), resourceType.Kind(), resourceName) h.Collectors.RecordReload(true, resourceNamespace) log.Info("workload reloaded successfully", "workload", decision.Workload.GetName(), @@ -144,7 +144,7 @@ func (h *ReloadHandler) applyReloads( WorkloadKind: string(decision.Workload.Kind()), WorkloadName: decision.Workload.GetName(), WorkloadNamespace: decision.Workload.GetNamespace(), - ResourceKind: resourceKind, + ResourceKind: resourceType.Kind(), ResourceName: resourceName, ResourceNamespace: resourceNamespace, Timestamp: time.Now(), diff --git a/internal/pkg/controller/retry.go b/internal/pkg/controller/retry.go index f8af3de0..b9106490 100644 --- a/internal/pkg/controller/retry.go +++ b/internal/pkg/controller/retry.go @@ -43,6 +43,52 @@ func UpdateWorkloadWithRetry( } } +// retryWithReload wraps the common retry logic for workload updates. +// It handles re-fetching on conflict, applying reload changes, and calling the update function. +func retryWithReload( + ctx context.Context, + c client.Client, + reloadService *reload.Service, + wl workload.WorkloadAccessor, + resourceName string, + resourceType reload.ResourceType, + namespace string, + hash string, + autoReload bool, + updateFn func() error, +) (bool, error) { + var updated bool + isFirstAttempt := true + + err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { + if !isFirstAttempt { + obj := wl.GetObject() + key := client.ObjectKeyFromObject(obj) + if err := c.Get(ctx, key, obj); err != nil { + if errors.IsNotFound(err) { + return nil + } + return err + } + } + isFirstAttempt = false + + var applyErr error + updated, applyErr = reloadService.ApplyReload(ctx, wl, resourceName, resourceType, namespace, hash, autoReload) + if applyErr != nil { + return applyErr + } + + if !updated { + return nil + } + + return updateFn() + }) + + return updated, err +} + // updateStandardWorkload updates Deployments, DaemonSets, StatefulSets, etc. func updateStandardWorkload( ctx context.Context, @@ -55,48 +101,10 @@ func updateStandardWorkload( hash string, autoReload bool, ) (bool, error) { - var updated bool - isFirstAttempt := true - - err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { - // On retry, re-fetch the object to get the latest ResourceVersion - if !isFirstAttempt { - obj := wl.GetObject() - key := client.ObjectKeyFromObject(obj) - if err := c.Get(ctx, key, obj); err != nil { - if errors.IsNotFound(err) { - // Object was deleted, nothing to update - return nil - } - return err - } - } - isFirstAttempt = false - - // Apply reload changes (this modifies the workload in-place) - var applyErr error - updated, applyErr = reloadService.ApplyReload( - ctx, - wl, - resourceName, - resourceType, - namespace, - hash, - autoReload, - ) - if applyErr != nil { - return applyErr - } - - if !updated { - return nil - } - - // Attempt update with field ownership - return c.Update(ctx, wl.GetObject(), client.FieldOwner(FieldManager)) - }) - - return updated, err + return retryWithReload(ctx, c, reloadService, wl, resourceName, resourceType, namespace, hash, autoReload, + func() error { + return c.Update(ctx, wl.GetObject(), client.FieldOwner(FieldManager)) + }) } // updateJobWithRecreate deletes the Job and recreates it with the updated spec. @@ -254,46 +262,8 @@ func updateArgoRollout( return false, nil } - var updated bool - isFirstAttempt := true - - err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { - // On retry, re-fetch the object to get the latest ResourceVersion - if !isFirstAttempt { - obj := rolloutWl.GetObject() - key := client.ObjectKeyFromObject(obj) - if err := c.Get(ctx, key, obj); err != nil { - if errors.IsNotFound(err) { - // Object was deleted, nothing to update - return nil - } - return err - } - } - isFirstAttempt = false - - // Apply reload changes (this modifies the workload in-place) - var applyErr error - updated, applyErr = reloadService.ApplyReload( - ctx, - wl, - resourceName, - resourceType, - namespace, - hash, - autoReload, - ) - if applyErr != nil { - return applyErr - } - - if !updated { - return nil - } - - // Use the RolloutWorkload's Update method which handles the rollout strategy - return rolloutWl.Update(ctx, c) - }) - - return updated, err + return retryWithReload(ctx, c, reloadService, wl, resourceName, resourceType, namespace, hash, autoReload, + func() error { + return rolloutWl.Update(ctx, c) + }) } diff --git a/internal/pkg/controller/secret_reconciler.go b/internal/pkg/controller/secret_reconciler.go index c6c79131..c28f8e38 100644 --- a/internal/pkg/controller/secret_reconciler.go +++ b/internal/pkg/controller/secret_reconciler.go @@ -62,9 +62,9 @@ func (r *SecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, nil } - return r.reloadHandler().Process(ctx, secret.Namespace, secret.Name, "Secret", reload.ResourceTypeSecret, + return r.reloadHandler().Process(ctx, secret.Namespace, secret.Name, reload.ResourceTypeSecret, func(workloads []workload.WorkloadAccessor) []reload.ReloadDecision { - return r.ReloadService.ProcessSecret(reload.SecretChange{ + return r.ReloadService.Process(reload.SecretChange{ Secret: &secret, EventType: reload.EventTypeUpdate, }, workloads) @@ -78,9 +78,9 @@ func (r *SecretReconciler) handleDelete(ctx context.Context, req ctrl.Request, l secret.Name = req.Name secret.Namespace = req.Namespace - return r.reloadHandler().Process(ctx, req.Namespace, req.Name, "Secret", reload.ResourceTypeSecret, + return r.reloadHandler().Process(ctx, req.Namespace, req.Name, reload.ResourceTypeSecret, func(workloads []workload.WorkloadAccessor) []reload.ReloadDecision { - return r.ReloadService.ProcessSecret(reload.SecretChange{ + return r.ReloadService.Process(reload.SecretChange{ Secret: secret, EventType: reload.EventTypeDelete, }, workloads) diff --git a/internal/pkg/reload/matcher.go b/internal/pkg/reload/matcher.go index 6f56d8ef..d7a26fbd 100644 --- a/internal/pkg/reload/matcher.go +++ b/internal/pkg/reload/matcher.go @@ -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 diff --git a/internal/pkg/reload/predicate.go b/internal/pkg/reload/predicate.go index 030ded5d..f24c60a7 100644 --- a/internal/pkg/reload/predicate.go +++ b/internal/pkg/reload/predicate.go @@ -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. diff --git a/internal/pkg/reload/service.go b/internal/pkg/reload/service.go index e9ff12b6..acec2d59 100644 --- a/internal/pkg/reload/service.go +++ b/internal/pkg/reload/service.go @@ -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, diff --git a/internal/pkg/reload/service_test.go b/internal/pkg/reload/service_test.go index 06880424..4b260a25 100644 --- a/internal/pkg/reload/service_test.go +++ b/internal/pkg/reload/service_test.go @@ -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 diff --git a/internal/pkg/workload/cronjob.go b/internal/pkg/workload/cronjob.go index 42df8eca..80d672e3 100644 --- a/internal/pkg/workload/cronjob.go +++ b/internal/pkg/workload/cronjob.go @@ -103,99 +103,11 @@ func (w *CronJobWorkload) GetEnvFromSources() []corev1.EnvFromSource { } func (w *CronJobWorkload) UsesConfigMap(name string) bool { - spec := &w.cronjob.Spec.JobTemplate.Spec.Template.Spec - - // Check volumes - for _, vol := range spec.Volumes { - if vol.ConfigMap != nil && vol.ConfigMap.Name == name { - return true - } - if vol.Projected != nil { - for _, source := range vol.Projected.Sources { - if source.ConfigMap != nil && source.ConfigMap.Name == name { - return true - } - } - } - } - - // Check containers - for _, container := range spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name { - return true - } - } - } - - // Check init containers - for _, container := range spec.InitContainers { - for _, envFrom := range container.EnvFrom { - if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name { - return true - } - } - } - - return false + return SpecUsesConfigMap(&w.cronjob.Spec.JobTemplate.Spec.Template.Spec, name) } func (w *CronJobWorkload) UsesSecret(name string) bool { - spec := &w.cronjob.Spec.JobTemplate.Spec.Template.Spec - - // Check volumes - for _, vol := range spec.Volumes { - if vol.Secret != nil && vol.Secret.SecretName == name { - return true - } - if vol.Projected != nil { - for _, source := range vol.Projected.Sources { - if source.Secret != nil && source.Secret.Name == name { - return true - } - } - } - } - - // Check containers - for _, container := range spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name { - return true - } - } - } - - // Check init containers - for _, container := range spec.InitContainers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name { - return true - } - } - } - - return false + return SpecUsesSecret(&w.cronjob.Spec.JobTemplate.Spec.Template.Spec, name) } func (w *CronJobWorkload) GetOwnerReferences() []metav1.OwnerReference { diff --git a/internal/pkg/workload/daemonset.go b/internal/pkg/workload/daemonset.go index ca51f4b5..85ac7b05 100644 --- a/internal/pkg/workload/daemonset.go +++ b/internal/pkg/workload/daemonset.go @@ -96,95 +96,11 @@ func (w *DaemonSetWorkload) GetEnvFromSources() []corev1.EnvFromSource { } func (w *DaemonSetWorkload) UsesConfigMap(name string) bool { - // Check volumes - for _, vol := range w.daemonset.Spec.Template.Spec.Volumes { - if vol.ConfigMap != nil && vol.ConfigMap.Name == name { - return true - } - if vol.Projected != nil { - for _, source := range vol.Projected.Sources { - if source.ConfigMap != nil && source.ConfigMap.Name == name { - return true - } - } - } - } - - // Check envFrom - for _, container := range w.daemonset.Spec.Template.Spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name { - return true - } - } - } - - // Check init containers - for _, container := range w.daemonset.Spec.Template.Spec.InitContainers { - for _, envFrom := range container.EnvFrom { - if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name { - return true - } - } - } - - return false + return SpecUsesConfigMap(&w.daemonset.Spec.Template.Spec, name) } func (w *DaemonSetWorkload) UsesSecret(name string) bool { - // Check volumes - for _, vol := range w.daemonset.Spec.Template.Spec.Volumes { - if vol.Secret != nil && vol.Secret.SecretName == name { - return true - } - if vol.Projected != nil { - for _, source := range vol.Projected.Sources { - if source.Secret != nil && source.Secret.Name == name { - return true - } - } - } - } - - // Check envFrom - for _, container := range w.daemonset.Spec.Template.Spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name { - return true - } - } - } - - // Check init containers - for _, container := range w.daemonset.Spec.Template.Spec.InitContainers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name { - return true - } - } - } - - return false + return SpecUsesSecret(&w.daemonset.Spec.Template.Spec, name) } func (w *DaemonSetWorkload) GetOwnerReferences() []metav1.OwnerReference { diff --git a/internal/pkg/workload/deployment.go b/internal/pkg/workload/deployment.go index 1b5ab5f8..e4ebefb5 100644 --- a/internal/pkg/workload/deployment.go +++ b/internal/pkg/workload/deployment.go @@ -96,97 +96,11 @@ func (w *DeploymentWorkload) GetEnvFromSources() []corev1.EnvFromSource { } func (w *DeploymentWorkload) UsesConfigMap(name string) bool { - // Check volumes - for _, vol := range w.deployment.Spec.Template.Spec.Volumes { - if vol.ConfigMap != nil && vol.ConfigMap.Name == name { - return true - } - if vol.Projected != nil { - for _, source := range vol.Projected.Sources { - if source.ConfigMap != nil && source.ConfigMap.Name == name { - return true - } - } - } - } - - // Check envFrom - for _, container := range w.deployment.Spec.Template.Spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name { - return true - } - } - // Check individual env vars - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name { - return true - } - } - } - - // Check init containers - for _, container := range w.deployment.Spec.Template.Spec.InitContainers { - for _, envFrom := range container.EnvFrom { - if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name { - return true - } - } - } - - return false + return SpecUsesConfigMap(&w.deployment.Spec.Template.Spec, name) } func (w *DeploymentWorkload) UsesSecret(name string) bool { - // Check volumes - for _, vol := range w.deployment.Spec.Template.Spec.Volumes { - if vol.Secret != nil && vol.Secret.SecretName == name { - return true - } - if vol.Projected != nil { - for _, source := range vol.Projected.Sources { - if source.Secret != nil && source.Secret.Name == name { - return true - } - } - } - } - - // Check envFrom - for _, container := range w.deployment.Spec.Template.Spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name { - return true - } - } - // Check individual env vars - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name { - return true - } - } - } - - // Check init containers - for _, container := range w.deployment.Spec.Template.Spec.InitContainers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name { - return true - } - } - } - - return false + return SpecUsesSecret(&w.deployment.Spec.Template.Spec, name) } func (w *DeploymentWorkload) GetOwnerReferences() []metav1.OwnerReference { diff --git a/internal/pkg/workload/job.go b/internal/pkg/workload/job.go index 85b01e9b..4e6c9fc6 100644 --- a/internal/pkg/workload/job.go +++ b/internal/pkg/workload/job.go @@ -102,95 +102,11 @@ func (w *JobWorkload) GetEnvFromSources() []corev1.EnvFromSource { } func (w *JobWorkload) UsesConfigMap(name string) bool { - // Check volumes - for _, vol := range w.job.Spec.Template.Spec.Volumes { - if vol.ConfigMap != nil && vol.ConfigMap.Name == name { - return true - } - if vol.Projected != nil { - for _, source := range vol.Projected.Sources { - if source.ConfigMap != nil && source.ConfigMap.Name == name { - return true - } - } - } - } - - // Check containers - for _, container := range w.job.Spec.Template.Spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name { - return true - } - } - } - - // Check init containers - for _, container := range w.job.Spec.Template.Spec.InitContainers { - for _, envFrom := range container.EnvFrom { - if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name { - return true - } - } - } - - return false + return SpecUsesConfigMap(&w.job.Spec.Template.Spec, name) } func (w *JobWorkload) UsesSecret(name string) bool { - // Check volumes - for _, vol := range w.job.Spec.Template.Spec.Volumes { - if vol.Secret != nil && vol.Secret.SecretName == name { - return true - } - if vol.Projected != nil { - for _, source := range vol.Projected.Sources { - if source.Secret != nil && source.Secret.Name == name { - return true - } - } - } - } - - // Check containers - for _, container := range w.job.Spec.Template.Spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name { - return true - } - } - } - - // Check init containers - for _, container := range w.job.Spec.Template.Spec.InitContainers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name { - return true - } - } - } - - return false + return SpecUsesSecret(&w.job.Spec.Template.Spec, name) } func (w *JobWorkload) GetOwnerReferences() []metav1.OwnerReference { diff --git a/internal/pkg/workload/rollout.go b/internal/pkg/workload/rollout.go index 7ea9643d..f19c1713 100644 --- a/internal/pkg/workload/rollout.go +++ b/internal/pkg/workload/rollout.go @@ -137,99 +137,11 @@ func (w *RolloutWorkload) GetEnvFromSources() []corev1.EnvFromSource { } func (w *RolloutWorkload) UsesConfigMap(name string) bool { - spec := &w.rollout.Spec.Template.Spec - - // Check volumes - for _, vol := range spec.Volumes { - if vol.ConfigMap != nil && vol.ConfigMap.Name == name { - return true - } - if vol.Projected != nil { - for _, source := range vol.Projected.Sources { - if source.ConfigMap != nil && source.ConfigMap.Name == name { - return true - } - } - } - } - - // Check containers - for _, container := range spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name { - return true - } - } - } - - // Check init containers - for _, container := range spec.InitContainers { - for _, envFrom := range container.EnvFrom { - if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name { - return true - } - } - } - - return false + return SpecUsesConfigMap(&w.rollout.Spec.Template.Spec, name) } func (w *RolloutWorkload) UsesSecret(name string) bool { - spec := &w.rollout.Spec.Template.Spec - - // Check volumes - for _, vol := range spec.Volumes { - if vol.Secret != nil && vol.Secret.SecretName == name { - return true - } - if vol.Projected != nil { - for _, source := range vol.Projected.Sources { - if source.Secret != nil && source.Secret.Name == name { - return true - } - } - } - } - - // Check containers - for _, container := range spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name { - return true - } - } - } - - // Check init containers - for _, container := range spec.InitContainers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name { - return true - } - } - } - - return false + return SpecUsesSecret(&w.rollout.Spec.Template.Spec, name) } func (w *RolloutWorkload) GetOwnerReferences() []metav1.OwnerReference { diff --git a/internal/pkg/workload/statefulset.go b/internal/pkg/workload/statefulset.go index 003cef3d..31dddeb2 100644 --- a/internal/pkg/workload/statefulset.go +++ b/internal/pkg/workload/statefulset.go @@ -96,95 +96,11 @@ func (w *StatefulSetWorkload) GetEnvFromSources() []corev1.EnvFromSource { } func (w *StatefulSetWorkload) UsesConfigMap(name string) bool { - // Check volumes - for _, vol := range w.statefulset.Spec.Template.Spec.Volumes { - if vol.ConfigMap != nil && vol.ConfigMap.Name == name { - return true - } - if vol.Projected != nil { - for _, source := range vol.Projected.Sources { - if source.ConfigMap != nil && source.ConfigMap.Name == name { - return true - } - } - } - } - - // Check envFrom - for _, container := range w.statefulset.Spec.Template.Spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name { - return true - } - } - } - - // Check init containers - for _, container := range w.statefulset.Spec.Template.Spec.InitContainers { - for _, envFrom := range container.EnvFrom { - if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name { - return true - } - } - } - - return false + return SpecUsesConfigMap(&w.statefulset.Spec.Template.Spec, name) } func (w *StatefulSetWorkload) UsesSecret(name string) bool { - // Check volumes - for _, vol := range w.statefulset.Spec.Template.Spec.Volumes { - if vol.Secret != nil && vol.Secret.SecretName == name { - return true - } - if vol.Projected != nil { - for _, source := range vol.Projected.Sources { - if source.Secret != nil && source.Secret.Name == name { - return true - } - } - } - } - - // Check envFrom - for _, container := range w.statefulset.Spec.Template.Spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name { - return true - } - } - } - - // Check init containers - for _, container := range w.statefulset.Spec.Template.Spec.InitContainers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name { - return true - } - } - } - - return false + return SpecUsesSecret(&w.statefulset.Spec.Template.Spec, name) } func (w *StatefulSetWorkload) GetOwnerReferences() []metav1.OwnerReference { diff --git a/internal/pkg/workload/uses.go b/internal/pkg/workload/uses.go new file mode 100644 index 00000000..fd37a2f3 --- /dev/null +++ b/internal/pkg/workload/uses.go @@ -0,0 +1,77 @@ +package workload + +import corev1 "k8s.io/api/core/v1" + +// SpecUsesConfigMap checks if a PodSpec references the named ConfigMap. +func SpecUsesConfigMap(spec *corev1.PodSpec, name string) bool { + for _, vol := range spec.Volumes { + if vol.ConfigMap != nil && vol.ConfigMap.Name == name { + return true + } + if vol.Projected != nil { + for _, source := range vol.Projected.Sources { + if source.ConfigMap != nil && source.ConfigMap.Name == name { + return true + } + } + } + } + + if containersUseConfigMap(spec.Containers, name) { + return true + } + return containersUseConfigMap(spec.InitContainers, name) +} + +func containersUseConfigMap(containers []corev1.Container, name string) bool { + for _, container := range containers { + for _, envFrom := range container.EnvFrom { + if envFrom.ConfigMapRef != nil && envFrom.ConfigMapRef.Name == name { + return true + } + } + for _, env := range container.Env { + if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == name { + return true + } + } + } + return false +} + +// SpecUsesSecret checks if a PodSpec references the named Secret. +func SpecUsesSecret(spec *corev1.PodSpec, name string) bool { + for _, vol := range spec.Volumes { + if vol.Secret != nil && vol.Secret.SecretName == name { + return true + } + if vol.Projected != nil { + for _, source := range vol.Projected.Sources { + if source.Secret != nil && source.Secret.Name == name { + return true + } + } + } + } + + if containersUseSecret(spec.Containers, name) { + return true + } + return containersUseSecret(spec.InitContainers, name) +} + +func containersUseSecret(containers []corev1.Container, name string) bool { + for _, container := range containers { + for _, envFrom := range container.EnvFrom { + if envFrom.SecretRef != nil && envFrom.SecretRef.Name == name { + return true + } + } + for _, env := range container.Env { + if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.Name == name { + return true + } + } + } + return false +}