@@ -18,47 +18,117 @@ package controller
1818
1919import (
2020 "context"
21+ "sync"
2122
2223 v1 "github.com/checkpoint-restore/checkpoint-restore-operator/api/v1"
2324 "k8s.io/apimachinery/pkg/runtime"
2425 "k8s.io/client-go/rest"
2526 ctrl "sigs.k8s.io/controller-runtime"
2627 "sigs.k8s.io/controller-runtime/pkg/client"
28+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
2729 "sigs.k8s.io/controller-runtime/pkg/log"
2830)
2931
3032//+kubebuilder:rbac:groups=criu.org,resources=checkpointschedules,verbs=get;list;watch;create;update;patch;delete
3133//+kubebuilder:rbac:groups=criu.org,resources=checkpointschedules/status,verbs=get;update;patch
3234//+kubebuilder:rbac:groups=criu.org,resources=checkpointschedules/finalizers,verbs=update
35+ //+kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch;patch
36+
37+ const checkpointScheduleFinalizer = "criu.org/checkpoint-schedule-finalizer"
38+
39+ type Stoppable interface {
40+ Stop ()
41+ }
3342
3443type CheckpointScheduleReconciler struct {
3544 client.Client
36- Scheme * runtime.Scheme
37- RestConfig * rest.Config
45+ Scheme * runtime.Scheme
46+ RestConfig * rest.Config
47+ activeTriggers map [string ][]Stoppable
48+ mu sync.Mutex
3849}
3950
4051func (r * CheckpointScheduleReconciler ) Reconcile (
4152 ctx context.Context ,
4253 req ctrl.Request ,
4354) (ctrl.Result , error ) {
4455 logger := log .FromContext (ctx )
45- // fetch the CR
56+
4657 schedule := & v1.CheckpointSchedule {}
4758 if err := r .Get (ctx , req .NamespacedName , schedule ); err != nil {
4859 return ctrl.Result {}, client .IgnoreNotFound (err )
4960 }
61+
62+ key := req .NamespacedName .String ()
63+
64+ // CR is being deleted — stop triggers and remove finalizer
65+ if ! schedule .DeletionTimestamp .IsZero () {
66+ r .stopTriggers (key )
67+ if controllerutil .ContainsFinalizer (schedule , checkpointScheduleFinalizer ) {
68+ controllerutil .RemoveFinalizer (schedule , checkpointScheduleFinalizer )
69+ if err := r .Update (ctx , schedule ); err != nil {
70+ return ctrl.Result {}, err
71+ }
72+ }
73+ return ctrl.Result {}, nil
74+ }
75+
76+ // add finalizer if not present
77+ if ! controllerutil .ContainsFinalizer (schedule , checkpointScheduleFinalizer ) {
78+ controllerutil .AddFinalizer (schedule , checkpointScheduleFinalizer )
79+ if err := r .Update (ctx , schedule ); err != nil {
80+ return ctrl.Result {}, err
81+ }
82+ return ctrl.Result {}, nil
83+ }
84+
85+ logger .Info ("reconciling" , "name" , schedule .Name )
86+
87+ // already running — don't start duplicates
88+ r .mu .Lock ()
89+ _ , running := r .activeTriggers [key ]
90+ r .mu .Unlock ()
91+ if running {
92+ return ctrl.Result {}, nil
93+ }
94+
5095 creator := NewCheckpointCreator (r .Client , r .RestConfig )
96+ var triggers []Stoppable
97+
98+ if schedule .Spec .Triggers .Schedule != "" {
99+ t := NewScheduleTrigger (r .Client , creator , schedule )
100+ if err := t .Start (ctx ); err != nil {
101+ logger .Error (err , "failed to start schedule trigger" )
102+ return ctrl.Result {}, err
103+ }
104+ triggers = append (triggers , t )
105+ }
51106
52- trigger := NewScheduleTrigger ( r . Client , creator , schedule )
53- if err := trigger . Start ( ctx ); err != nil {
54- logger . Error ( err , "failed to start schedule trigger" )
55- return ctrl. Result {}, err
107+ if schedule . Spec . Triggers . OnAnnotation {
108+ t := NewAnnotationTrigger ( r . Client , creator , schedule )
109+ t . Start ( ctx )
110+ triggers = append ( triggers , t )
56111 }
57112
58- logger .Info ("schedule trigger started" , "name" , schedule .Name )
113+ r .mu .Lock ()
114+ if r .activeTriggers == nil {
115+ r .activeTriggers = make (map [string ][]Stoppable )
116+ }
117+ r .activeTriggers [key ] = triggers
118+ r .mu .Unlock ()
119+
59120 return ctrl.Result {}, nil
121+ }
60122
123+ func (r * CheckpointScheduleReconciler ) stopTriggers (key string ) {
124+ r .mu .Lock ()
125+ defer r .mu .Unlock ()
126+ for _ , t := range r .activeTriggers [key ] {
127+ t .Stop ()
128+ }
129+ delete (r .activeTriggers , key )
61130}
131+
62132func (r * CheckpointScheduleReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
63133 return ctrl .NewControllerManagedBy (mgr ).
64134 For (& v1.CheckpointSchedule {}).
0 commit comments