@@ -45,11 +45,11 @@ type OperatorReconciler struct {
4545 client.Client
4646
4747 log logr.Logger
48- mu sync.RWMutex
4948 factory decorators.OperatorFactory
5049
51- // operators contains the names of Operators the OperatorReconciler has observed exist.
52- operators map [types.NamespacedName ]struct {}
50+ // last observed resourceVersion for known Operators
51+ lastResourceVersion map [types.NamespacedName ]string
52+ mu sync.RWMutex
5353}
5454
5555// +kubebuilder:rbac:groups=operators.coreos.com,resources=operators,verbs=create;update;patch;delete
@@ -101,9 +101,9 @@ func NewOperatorReconciler(cli client.Client, log logr.Logger, scheme *runtime.S
101101 return & OperatorReconciler {
102102 Client : cli ,
103103
104- log : log ,
105- factory : factory ,
106- operators : map [types.NamespacedName ]struct {} {},
104+ log : log ,
105+ factory : factory ,
106+ lastResourceVersion : map [types.NamespacedName ]string {},
107107 }, nil
108108}
109109
@@ -115,40 +115,55 @@ func (r *OperatorReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
115115 log := r .log .WithValues ("request" , req )
116116 log .V (1 ).Info ("reconciling operator" )
117117
118- // Fetch the Operator from the cache
118+ // Get the Operator
119119 ctx := context .TODO ()
120+ create := false
121+ name := req .NamespacedName .Name
120122 in := & operatorsv1.Operator {}
121123 if err := r .Get (ctx , req .NamespacedName , in ); err != nil {
122124 if apierrors .IsNotFound (err ) {
123- log .Info ("Could not find Operator" )
124- r .unobserve (req .NamespacedName )
125- // TODO(njhale): Recreate operator if we can find any components.
125+ create = true
126+ in .SetName (name )
126127 } else {
127- log .Error (err , "Error finding Operator" )
128+ log .Error (err , "Error requesting Operator" )
129+ return reconcile.Result {Requeue : true }, nil
128130 }
131+ }
129132
130- return reconcile.Result {}, nil
133+ if ! create {
134+ if rv , ok := r .getLastResourceVersion (req .NamespacedName ); ok && rv == in .ResourceVersion {
135+ log .V (1 ).Info ("Operator is already up-to-date" )
136+ return reconcile.Result {}, nil
137+ }
131138 }
132- r .observe (req .NamespacedName )
133139
134140 // Wrap with convenience decorator
135141 operator , err := r .factory .NewOperator (in )
136142 if err != nil {
137143 log .Error (err , "Could not wrap Operator with convenience decorator" )
138- return reconcile.Result {}, nil
144+ return reconcile.Result {Requeue : true }, nil
139145 }
140146
141147 if err = r .updateComponents (ctx , operator ); err != nil {
142148 log .Error (err , "Could not update components" )
143- return reconcile.Result {}, nil
149+ return reconcile.Result {Requeue : true }, nil
144150
145151 }
146152
147- if err := r .Status ().Update (ctx , operator .Operator ); err != nil {
148- log .Error (err , "Could not update Operator status" )
149- return ctrl.Result {}, err
153+ if create {
154+ if err := r .Create (context .Background (), operator .Operator ); err != nil && ! apierrors .IsAlreadyExists (err ) {
155+ r .log .Error (err , "Could not create Operator" , "operator" , name )
156+ return ctrl.Result {Requeue : true }, nil
157+ }
158+ } else {
159+ if err := r .Status ().Update (ctx , operator .Operator ); err != nil {
160+ log .Error (err , "Could not update Operator status" )
161+ return ctrl.Result {Requeue : true }, nil
162+ }
150163 }
151164
165+ r .setLastResourceVersion (req .NamespacedName , operator .GetResourceVersion ())
166+
152167 return ctrl.Result {}, nil
153168}
154169
@@ -196,45 +211,36 @@ func (r *OperatorReconciler) listComponents(ctx context.Context, selector labels
196211 return componentLists , nil
197212}
198213
199- func (r * OperatorReconciler ) observed (name types.NamespacedName ) bool {
214+ func (r * OperatorReconciler ) getLastResourceVersion (name types.NamespacedName ) ( string , bool ) {
200215 r .mu .RLock ()
201216 defer r .mu .RUnlock ()
202- _ , ok := r .operators [name ]
203- return ok
217+ rv , ok := r .lastResourceVersion [name ]
218+ return rv , ok
204219}
205220
206- func (r * OperatorReconciler ) observe (name types.NamespacedName ) {
221+ func (r * OperatorReconciler ) setLastResourceVersion (name types.NamespacedName , rv string ) {
207222 r .mu .Lock ()
208223 defer r .mu .Unlock ()
209- r .operators [name ] = struct {}{}
224+ r .lastResourceVersion [name ] = rv
210225}
211226
212- func (r * OperatorReconciler ) unobserve (name types.NamespacedName ) {
227+ func (r * OperatorReconciler ) unsetLastResourceVersion (name types.NamespacedName ) {
213228 r .mu .Lock ()
214229 defer r .mu .Unlock ()
215- delete (r .operators , name )
230+ delete (r .lastResourceVersion , name )
216231}
217232
218- func (r * OperatorReconciler ) mapComponentRequests (obj handler.MapObject ) (requests []reconcile.Request ) {
233+ func (r * OperatorReconciler ) mapComponentRequests (obj handler.MapObject ) []reconcile.Request {
234+ var requests []reconcile.Request
219235 if obj .Meta == nil {
220- return
236+ return requests
221237 }
222238
223239 for _ , name := range decorators .OperatorNames (obj .Meta .GetLabels ()) {
224- // Only enqueue if we can find the operator in our cache
225- if r .observed (name ) {
226- requests = append (requests , reconcile.Request {NamespacedName : name })
227- continue
228- }
229-
230- // Otherwise, best-effort generate a new operator
231- // TODO(njhale): Implement verification that the operator-discovery admission webhook accepted this label (JWT or maybe sign a set of fields?)
232- operator := & operatorsv1.Operator {}
233- operator .SetName (name .Name )
234- if err := r .Create (context .Background (), operator ); err != nil && ! apierrors .IsAlreadyExists (err ) {
235- r .log .Error (err , "couldn't generate operator" , "operator" , name , "component" , obj .Meta .GetSelfLink ())
236- }
240+ // unset the last recorded resource version so the Operator will reconcile
241+ r .unsetLastResourceVersion (name )
242+ requests = append (requests , reconcile.Request {NamespacedName : name })
237243 }
238244
239- return
245+ return requests
240246}
0 commit comments