mirror of
https://github.com/outbackdingo/cozystack.git
synced 2026-01-27 18:18:41 +00:00
118 lines
3.3 KiB
Go
118 lines
3.3 KiB
Go
package controller
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/handler"
|
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
|
)
|
|
|
|
type CozystackResourceDefinitionReconciler struct {
|
|
client.Client
|
|
Scheme *runtime.Scheme
|
|
|
|
// Configurable debounce duration
|
|
Debounce time.Duration
|
|
|
|
// Internal state for debouncing
|
|
mu sync.Mutex
|
|
lastEvent time.Time // Time of last CRUD event on CozystackResourceDefinition
|
|
lastHandled time.Time // Last time the Deployment was actually restarted
|
|
}
|
|
|
|
// Reconcile handles the logic to restart the target Deployment only once,
|
|
// even if multiple events occur close together
|
|
func (r *CozystackResourceDefinitionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
|
log := log.FromContext(ctx)
|
|
|
|
// Only respond to our target deployment
|
|
if req.Namespace != "cozy-system" || req.Name != "cozystack-api" {
|
|
return ctrl.Result{}, nil
|
|
}
|
|
|
|
r.mu.Lock()
|
|
le := r.lastEvent
|
|
lh := r.lastHandled
|
|
debounce := r.Debounce
|
|
r.mu.Unlock()
|
|
|
|
if debounce <= 0 {
|
|
debounce = 5 * time.Second
|
|
}
|
|
|
|
// No events received yet — nothing to do
|
|
if le.IsZero() {
|
|
return ctrl.Result{}, nil
|
|
}
|
|
|
|
// Wait until the debounce duration has passed since the last event
|
|
if d := time.Since(le); d < debounce {
|
|
return ctrl.Result{RequeueAfter: debounce - d}, nil
|
|
}
|
|
|
|
// Already handled this event — skip restart
|
|
if !lh.Before(le) {
|
|
return ctrl.Result{}, nil
|
|
}
|
|
|
|
// Perform the restart by patching the deployment annotation
|
|
deploy := &appsv1.Deployment{}
|
|
if err := r.Get(ctx, types.NamespacedName{Namespace: "cozy-system", Name: "cozystack-api"}, deploy); err != nil {
|
|
log.Error(err, "Failed to get Deployment cozy-system/cozystack-api")
|
|
return ctrl.Result{}, client.IgnoreNotFound(err)
|
|
}
|
|
|
|
patch := client.MergeFrom(deploy.DeepCopy())
|
|
if deploy.Spec.Template.Annotations == nil {
|
|
deploy.Spec.Template.Annotations = make(map[string]string)
|
|
}
|
|
deploy.Spec.Template.Annotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339)
|
|
|
|
if err := r.Patch(ctx, deploy, patch); err != nil {
|
|
log.Error(err, "Failed to patch Deployment annotation")
|
|
return ctrl.Result{}, err
|
|
}
|
|
|
|
// Mark this event as handled
|
|
r.mu.Lock()
|
|
r.lastHandled = le
|
|
r.mu.Unlock()
|
|
|
|
log.Info("Deployment cozy-system/cozystack-api successfully restarted")
|
|
return ctrl.Result{}, nil
|
|
}
|
|
|
|
// SetupWithManager configures how the controller listens to events
|
|
func (r *CozystackResourceDefinitionReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|
if r.Debounce == 0 {
|
|
r.Debounce = 5 * time.Second
|
|
}
|
|
|
|
return ctrl.NewControllerManagedBy(mgr).
|
|
Named("cozystack-restart-controller").
|
|
Watches(
|
|
&cozyv1alpha1.CozystackResourceDefinition{},
|
|
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
|
|
r.mu.Lock()
|
|
r.lastEvent = time.Now()
|
|
r.mu.Unlock()
|
|
return []reconcile.Request{{
|
|
NamespacedName: types.NamespacedName{
|
|
Namespace: "cozy-system",
|
|
Name: "cozystack-api",
|
|
},
|
|
}}
|
|
}),
|
|
).
|
|
Complete(r)
|
|
}
|