refactor(reload): move resource types and reload decision structs to dedicated files and remove redundant code

This commit is contained in:
TheiLLeniumStudios
2025-12-28 08:47:57 +01:00
parent 3a8c300d35
commit fa60c1d853
9 changed files with 228 additions and 205 deletions
+39
View File
@@ -0,0 +1,39 @@
package controller
import (
"github.com/stakater/Reloader/internal/pkg/config"
"github.com/stakater/Reloader/internal/pkg/reload"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"
)
// BuildEventFilter combines a resource-specific predicate with common filters.
func BuildEventFilter(resourcePredicate predicate.Predicate, cfg *config.Config, initialized *bool) predicate.Predicate {
return predicate.And(
resourcePredicate,
reload.NamespaceFilterPredicate(cfg),
reload.LabelSelectorPredicate(cfg),
reload.IgnoreAnnotationPredicate(cfg),
createEventPredicate(cfg, initialized),
)
}
func createEventPredicate(cfg *config.Config, initialized *bool) predicate.Predicate {
return predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
if !*initialized && !cfg.SyncAfterRestart {
return false
}
return cfg.ReloadOnCreate
},
UpdateFunc: func(e event.UpdateEvent) bool {
return true
},
DeleteFunc: func(e event.DeleteEvent) bool {
return cfg.ReloadOnDelete
},
GenericFunc: func(e event.GenericEvent) bool {
return false
},
}
}
-34
View File
@@ -6,7 +6,6 @@ import (
"github.com/go-logr/logr"
"github.com/stakater/Reloader/internal/pkg/alerting"
"github.com/stakater/Reloader/internal/pkg/config"
"github.com/stakater/Reloader/internal/pkg/events"
"github.com/stakater/Reloader/internal/pkg/metrics"
"github.com/stakater/Reloader/internal/pkg/reload"
@@ -14,8 +13,6 @@ import (
"github.com/stakater/Reloader/internal/pkg/workload"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"
)
// ReloadHandler handles the common reload workflow.
@@ -154,34 +151,3 @@ func (h *ReloadHandler) applyReloads(
}
}
}
// BuildEventFilter combines a resource-specific predicate with common filters.
func BuildEventFilter(resourcePredicate predicate.Predicate, cfg *config.Config, initialized *bool) predicate.Predicate {
return predicate.And(
resourcePredicate,
reload.NamespaceFilterPredicate(cfg),
reload.LabelSelectorPredicate(cfg),
reload.IgnoreAnnotationPredicate(cfg),
createEventPredicate(cfg, initialized),
)
}
func createEventPredicate(cfg *config.Config, initialized *bool) predicate.Predicate {
return predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
if !*initialized && !cfg.SyncAfterRestart {
return false
}
return cfg.ReloadOnCreate
},
UpdateFunc: func(e event.UpdateEvent) bool {
return true
},
DeleteFunc: func(e event.DeleteEvent) bool {
return cfg.ReloadOnDelete
},
GenericFunc: func(e event.GenericEvent) bool {
return false
},
}
}
-72
View File
@@ -3,19 +3,14 @@
package metadata
import (
"context"
"encoding/json"
"fmt"
"os"
"runtime"
"time"
"github.com/go-logr/logr"
"github.com/stakater/Reloader/internal/pkg/config"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
const (
@@ -195,73 +190,6 @@ func (m *MetaInfo) ToConfigMap() *corev1.ConfigMap {
}
}
// Publisher handles creating and updating the metadata ConfigMap.
type Publisher struct {
client client.Client
cfg *config.Config
log logr.Logger
}
// NewPublisher creates a new Publisher.
func NewPublisher(c client.Client, cfg *config.Config, log logr.Logger) *Publisher {
return &Publisher{
client: c,
cfg: cfg,
log: log,
}
}
// Publish creates or updates the metadata ConfigMap.
func (p *Publisher) Publish(ctx context.Context) error {
namespace := os.Getenv(EnvReloaderNamespace)
if namespace == "" {
p.log.Info("RELOADER_NAMESPACE is not set, skipping meta info configmap creation")
return nil
}
metaInfo := NewMetaInfo(p.cfg)
configMap := metaInfo.ToConfigMap()
existing := &corev1.ConfigMap{}
err := p.client.Get(ctx, client.ObjectKey{
Name: ConfigMapName,
Namespace: namespace,
}, existing)
if err != nil {
if !errors.IsNotFound(err) {
return fmt.Errorf("failed to get existing meta info configmap: %w", err)
}
p.log.Info("Creating meta info configmap")
if err := p.client.Create(ctx, configMap, client.FieldOwner(FieldManager)); err != nil {
return fmt.Errorf("failed to create meta info configmap: %w", err)
}
p.log.Info("Meta info configmap created successfully")
return nil
}
p.log.Info("Meta info configmap already exists, updating it")
existing.Data = configMap.Data
existing.Labels = configMap.Labels
if err := p.client.Update(ctx, existing, client.FieldOwner(FieldManager)); err != nil {
return fmt.Errorf("failed to update meta info configmap: %w", err)
}
p.log.Info("Meta info configmap updated successfully")
return nil
}
// PublishMetaInfoConfigMap is a convenience function that creates a Publisher and calls Publish.
func PublishMetaInfoConfigMap(ctx context.Context, c client.Client, cfg *config.Config, log logr.Logger) error {
publisher := NewPublisher(c, cfg, log)
return publisher.Publish(ctx)
}
// CreateOrUpdate creates or updates the metadata ConfigMap using the provided client.
func CreateOrUpdate(c client.Client, cfg *config.Config, log logr.Logger) error {
ctx := context.Background()
return PublishMetaInfoConfigMap(ctx, c, cfg, log)
}
func toJSON(data interface{}) string {
jsonData, err := json.Marshal(data)
if err != nil {
+80
View File
@@ -0,0 +1,80 @@
package metadata
import (
"context"
"fmt"
"os"
"github.com/go-logr/logr"
"github.com/stakater/Reloader/internal/pkg/config"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// Publisher handles creating and updating the metadata ConfigMap.
type Publisher struct {
client client.Client
cfg *config.Config
log logr.Logger
}
// NewPublisher creates a new Publisher.
func NewPublisher(c client.Client, cfg *config.Config, log logr.Logger) *Publisher {
return &Publisher{
client: c,
cfg: cfg,
log: log,
}
}
// Publish creates or updates the metadata ConfigMap.
func (p *Publisher) Publish(ctx context.Context) error {
namespace := os.Getenv(EnvReloaderNamespace)
if namespace == "" {
p.log.Info("RELOADER_NAMESPACE is not set, skipping meta info configmap creation")
return nil
}
metaInfo := NewMetaInfo(p.cfg)
configMap := metaInfo.ToConfigMap()
existing := &corev1.ConfigMap{}
err := p.client.Get(ctx, client.ObjectKey{
Name: ConfigMapName,
Namespace: namespace,
}, existing)
if err != nil {
if !errors.IsNotFound(err) {
return fmt.Errorf("failed to get existing meta info configmap: %w", err)
}
p.log.Info("Creating meta info configmap")
if err := p.client.Create(ctx, configMap, client.FieldOwner(FieldManager)); err != nil {
return fmt.Errorf("failed to create meta info configmap: %w", err)
}
p.log.Info("Meta info configmap created successfully")
return nil
}
p.log.Info("Meta info configmap already exists, updating it")
existing.Data = configMap.Data
existing.Labels = configMap.Labels
if err := p.client.Update(ctx, existing, client.FieldOwner(FieldManager)); err != nil {
return fmt.Errorf("failed to update meta info configmap: %w", err)
}
p.log.Info("Meta info configmap updated successfully")
return nil
}
// PublishMetaInfoConfigMap is a convenience function that creates a Publisher and calls Publish.
func PublishMetaInfoConfigMap(ctx context.Context, c client.Client, cfg *config.Config, log logr.Logger) error {
publisher := NewPublisher(c, cfg, log)
return publisher.Publish(ctx)
}
// CreateOrUpdate creates or updates the metadata ConfigMap using the provided client.
func CreateOrUpdate(c client.Client, cfg *config.Config, log logr.Logger) error {
ctx := context.Background()
return PublishMetaInfoConfigMap(ctx, c, cfg, log)
}
+56
View File
@@ -0,0 +1,56 @@
package reload
import (
corev1 "k8s.io/api/core/v1"
)
// EventType represents the type of change event.
type EventType string
const (
// EventTypeCreate indicates a resource was created.
EventTypeCreate EventType = "create"
// EventTypeUpdate indicates a resource was updated.
EventTypeUpdate EventType = "update"
// EventTypeDelete indicates a resource was deleted.
EventTypeDelete EventType = "delete"
)
// 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) }
+30
View File
@@ -0,0 +1,30 @@
package reload
import (
"github.com/stakater/Reloader/internal/pkg/workload"
)
// ReloadDecision contains the result of evaluating whether to reload a workload.
type ReloadDecision struct {
// Workload is the workload accessor.
Workload workload.WorkloadAccessor
// ShouldReload indicates whether the workload should be reloaded.
ShouldReload bool
// AutoReload indicates if this is an auto-reload.
AutoReload bool
// Reason provides a human-readable explanation.
Reason string
// Hash is the computed hash of the resource content.
Hash string
}
// FilterDecisions returns only decisions where ShouldReload is true.
func FilterDecisions(decisions []ReloadDecision) []ReloadDecision {
var result []ReloadDecision
for _, d := range decisions {
if d.ShouldReload {
result = append(result, d)
}
}
return result
}
-22
View File
@@ -7,28 +7,6 @@ import (
"github.com/stakater/Reloader/internal/pkg/config"
)
// ResourceType represents the type of Kubernetes resource.
type ResourceType string
const (
// ResourceTypeConfigMap represents a ConfigMap resource.
ResourceTypeConfigMap ResourceType = "configmap"
// ResourceTypeSecret represents a Secret resource.
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
+23
View File
@@ -0,0 +1,23 @@
package reload
// ResourceType represents the type of Kubernetes resource.
type ResourceType string
const (
// ResourceTypeConfigMap represents a ConfigMap resource.
ResourceTypeConfigMap ResourceType = "configmap"
// ResourceTypeSecret represents a Secret resource.
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)
}
}
-77
View File
@@ -28,82 +28,6 @@ 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
const (
// EventTypeCreate indicates a resource was created.
EventTypeCreate EventType = "create"
// EventTypeUpdate indicates a resource was updated.
EventTypeUpdate EventType = "update"
// EventTypeDelete indicates a resource was deleted.
EventTypeDelete EventType = "delete"
)
// ReloadDecision contains the result of evaluating whether to reload a workload.
type ReloadDecision struct {
// Workload is the workload accessor.
Workload workload.WorkloadAccessor
// ShouldReload indicates whether the workload should be reloaded.
ShouldReload bool
// AutoReload indicates if this is an auto-reload.
AutoReload bool
// Reason provides a human-readable explanation.
Reason string
// Hash is the computed hash of the resource content.
Hash string
}
// FilterDecisions returns only decisions where ShouldReload is true.
func FilterDecisions(decisions []ReloadDecision) []ReloadDecision {
var result []ReloadDecision
for _, d := range decisions {
if d.ShouldReload {
result = append(result, d)
}
}
return result
}
// Process evaluates all workloads to determine which should be reloaded.
func (s *Service) Process(change ResourceChange, workloads []workload.WorkloadAccessor) []ReloadDecision {
if change.IsNil() {
@@ -129,7 +53,6 @@ func (s *Service) Process(change ResourceChange, workloads []workload.WorkloadAc
)
}
func (s *Service) processResource(
resourceName string,
resourceNamespace string,