mirror of
https://github.com/stakater/Reloader.git
synced 2026-08-20 12:36:26 +00:00
refactor: Move all common reconcile logic to lister and reload handler
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
"github.com/stakater/Reloader/internal/pkg/webhook"
|
||||
"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.
|
||||
type ReloadHandler struct {
|
||||
Client client.Client
|
||||
Lister *workload.Lister
|
||||
ReloadService *reload.Service
|
||||
WebhookClient *webhook.Client
|
||||
Collectors *metrics.Collectors
|
||||
EventRecorder *events.Recorder
|
||||
Alerter alerting.Alerter
|
||||
}
|
||||
|
||||
// Process handles the reload workflow: list workloads, get decisions, webhook or apply.
|
||||
func (h *ReloadHandler) Process(
|
||||
ctx context.Context,
|
||||
namespace, resourceName, resourceKind string,
|
||||
resourceType reload.ResourceType,
|
||||
getDecisions func([]workload.WorkloadAccessor) []reload.ReloadDecision,
|
||||
log logr.Logger,
|
||||
) (ctrl.Result, error) {
|
||||
workloads, err := h.Lister.List(ctx, namespace)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to list workloads")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
decisions := reload.FilterDecisions(getDecisions(workloads))
|
||||
|
||||
if h.WebhookClient.IsConfigured() && len(decisions) > 0 {
|
||||
return h.sendWebhook(ctx, resourceName, namespace, resourceType, decisions, log)
|
||||
}
|
||||
|
||||
h.applyReloads(ctx, resourceName, namespace, resourceKind, resourceType, decisions, log)
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (h *ReloadHandler) sendWebhook(
|
||||
ctx context.Context,
|
||||
resourceName, namespace string,
|
||||
resourceType reload.ResourceType,
|
||||
decisions []reload.ReloadDecision,
|
||||
log logr.Logger,
|
||||
) (ctrl.Result, error) {
|
||||
var workloads []webhook.WorkloadInfo
|
||||
var hash string
|
||||
for _, d := range decisions {
|
||||
workloads = append(workloads, webhook.WorkloadInfo{
|
||||
Kind: string(d.Workload.Kind()),
|
||||
Name: d.Workload.GetName(),
|
||||
Namespace: d.Workload.GetNamespace(),
|
||||
})
|
||||
if hash == "" {
|
||||
hash = d.Hash
|
||||
}
|
||||
}
|
||||
|
||||
payload := webhook.Payload{
|
||||
Kind: string(resourceType),
|
||||
Namespace: namespace,
|
||||
ResourceName: resourceName,
|
||||
ResourceType: string(resourceType),
|
||||
Hash: hash,
|
||||
Timestamp: time.Now().UTC(),
|
||||
Workloads: workloads,
|
||||
}
|
||||
|
||||
if err := h.WebhookClient.Send(ctx, payload); err != nil {
|
||||
log.Error(err, "failed to send webhook notification")
|
||||
h.Collectors.RecordReload(false, namespace)
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
log.Info("webhook notification sent",
|
||||
"resource", resourceName,
|
||||
"workloadCount", len(workloads),
|
||||
)
|
||||
h.Collectors.RecordReload(true, namespace)
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (h *ReloadHandler) applyReloads(
|
||||
ctx context.Context,
|
||||
resourceName, resourceNamespace, resourceKind string,
|
||||
resourceType reload.ResourceType,
|
||||
decisions []reload.ReloadDecision,
|
||||
log logr.Logger,
|
||||
) {
|
||||
for _, decision := range decisions {
|
||||
log.Info("reloading workload",
|
||||
"workload", decision.Workload.GetName(),
|
||||
"kind", decision.Workload.Kind(),
|
||||
"reason", decision.Reason,
|
||||
)
|
||||
|
||||
updated, err := UpdateWorkloadWithRetry(
|
||||
ctx,
|
||||
h.Client,
|
||||
h.ReloadService,
|
||||
decision.Workload,
|
||||
resourceName,
|
||||
resourceType,
|
||||
resourceNamespace,
|
||||
decision.Hash,
|
||||
decision.AutoReload,
|
||||
)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to update workload",
|
||||
"workload", decision.Workload.GetName(),
|
||||
"kind", decision.Workload.Kind(),
|
||||
)
|
||||
h.EventRecorder.ReloadFailed(decision.Workload.GetObject(), resourceKind, resourceName, err)
|
||||
h.Collectors.RecordReload(false, resourceNamespace)
|
||||
continue
|
||||
}
|
||||
|
||||
if updated {
|
||||
h.EventRecorder.ReloadSuccess(decision.Workload.GetObject(), resourceKind, resourceName)
|
||||
h.Collectors.RecordReload(true, resourceNamespace)
|
||||
log.Info("workload reloaded successfully",
|
||||
"workload", decision.Workload.GetName(),
|
||||
"kind", decision.Workload.Kind(),
|
||||
)
|
||||
|
||||
if err := h.Alerter.Send(ctx, alerting.AlertMessage{
|
||||
WorkloadKind: string(decision.Workload.Kind()),
|
||||
WorkloadName: decision.Workload.GetName(),
|
||||
WorkloadNamespace: decision.Workload.GetNamespace(),
|
||||
ResourceKind: resourceKind,
|
||||
ResourceName: resourceName,
|
||||
ResourceNamespace: resourceNamespace,
|
||||
Timestamp: time.Now(),
|
||||
}); err != nil {
|
||||
log.Error(err, "failed to send alert")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user