Skip to content

Commit 8b1e55d

Browse files
committed
controller add CozystackResourceDefinition reconciler
Signed-off-by: kklinch0 <[email protected]>
1 parent cd80a73 commit 8b1e55d

2 files changed

Lines changed: 125 additions & 0 deletions

File tree

‎cmd/cozystack-controller/main.go‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,14 @@ func main() {
206206
os.Exit(1)
207207
}
208208

209+
if err = (&controller.CozystackResourceDefinitionReconciler{
210+
Client: mgr.GetClient(),
211+
Scheme: mgr.GetScheme(),
212+
}).SetupWithManager(mgr); err != nil {
213+
setupLog.Error(err, "unable to create controller", "controller", "CozystackResourceDefinitionReconciler")
214+
os.Exit(1)
215+
}
216+
209217
// +kubebuilder:scaffold:builder
210218

211219
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package controller
2+
3+
import (
4+
"context"
5+
"sync"
6+
"time"
7+
8+
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
9+
appsv1 "k8s.io/api/apps/v1"
10+
"k8s.io/apimachinery/pkg/runtime"
11+
"k8s.io/apimachinery/pkg/types"
12+
ctrl "sigs.k8s.io/controller-runtime"
13+
"sigs.k8s.io/controller-runtime/pkg/client"
14+
"sigs.k8s.io/controller-runtime/pkg/handler"
15+
"sigs.k8s.io/controller-runtime/pkg/log"
16+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
17+
)
18+
19+
type CozystackResourceDefinitionReconciler struct {
20+
client.Client
21+
Scheme *runtime.Scheme
22+
23+
// Configurable debounce duration
24+
Debounce time.Duration
25+
26+
// Internal state for debouncing
27+
mu sync.Mutex
28+
lastEvent time.Time // Time of last CRUD event on CozystackResourceDefinition
29+
lastHandled time.Time // Last time the Deployment was actually restarted
30+
}
31+
32+
// Reconcile handles the logic to restart the target Deployment only once,
33+
// even if multiple events occur close together
34+
func (r *CozystackResourceDefinitionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
35+
log := log.FromContext(ctx)
36+
37+
// Only respond to our target deployment
38+
if req.Namespace != "cozy-system" || req.Name != "cozystack-api" {
39+
return ctrl.Result{}, nil
40+
}
41+
42+
r.mu.Lock()
43+
le := r.lastEvent
44+
lh := r.lastHandled
45+
debounce := r.Debounce
46+
r.mu.Unlock()
47+
48+
if debounce <= 0 {
49+
debounce = 5 * time.Second
50+
}
51+
52+
// No events received yet — nothing to do
53+
if le.IsZero() {
54+
return ctrl.Result{}, nil
55+
}
56+
57+
// Wait until the debounce duration has passed since the last event
58+
if d := time.Since(le); d < debounce {
59+
return ctrl.Result{RequeueAfter: debounce - d}, nil
60+
}
61+
62+
// Already handled this event — skip restart
63+
if !lh.Before(le) {
64+
return ctrl.Result{}, nil
65+
}
66+
67+
// Perform the restart by patching the deployment annotation
68+
deploy := &appsv1.Deployment{}
69+
if err := r.Get(ctx, types.NamespacedName{Namespace: "cozy-system", Name: "cozystack-api"}, deploy); err != nil {
70+
log.Error(err, "Failed to get Deployment cozy-system/cozystack-api")
71+
return ctrl.Result{}, client.IgnoreNotFound(err)
72+
}
73+
74+
patch := client.MergeFrom(deploy.DeepCopy())
75+
if deploy.Spec.Template.Annotations == nil {
76+
deploy.Spec.Template.Annotations = make(map[string]string)
77+
}
78+
deploy.Spec.Template.Annotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339)
79+
80+
if err := r.Patch(ctx, deploy, patch); err != nil {
81+
log.Error(err, "Failed to patch Deployment annotation")
82+
return ctrl.Result{}, err
83+
}
84+
85+
// Mark this event as handled
86+
r.mu.Lock()
87+
r.lastHandled = le
88+
r.mu.Unlock()
89+
90+
log.Info("Deployment cozy-system/cozystack-api successfully restarted")
91+
return ctrl.Result{}, nil
92+
}
93+
94+
// SetupWithManager configures how the controller listens to events
95+
func (r *CozystackResourceDefinitionReconciler) SetupWithManager(mgr ctrl.Manager) error {
96+
if r.Debounce == 0 {
97+
r.Debounce = 5 * time.Second
98+
}
99+
100+
return ctrl.NewControllerManagedBy(mgr).
101+
Named("cozystack-restart-controller").
102+
Watches(
103+
&cozyv1alpha1.CozystackResourceDefinition{},
104+
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
105+
r.mu.Lock()
106+
r.lastEvent = time.Now()
107+
r.mu.Unlock()
108+
return []reconcile.Request{{
109+
NamespacedName: types.NamespacedName{
110+
Namespace: "cozy-system",
111+
Name: "cozystack-api",
112+
},
113+
}}
114+
}),
115+
).
116+
Complete(r)
117+
}

0 commit comments

Comments
 (0)