@@ -45,11 +45,11 @@ type OperatorReconciler struct {
45
45
client.Client
46
46
47
47
log logr.Logger
48
- mu sync.RWMutex
49
48
factory decorators.OperatorFactory
50
49
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
53
53
}
54
54
55
55
// +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
101
101
return & OperatorReconciler {
102
102
Client : cli ,
103
103
104
- log : log ,
105
- factory : factory ,
106
- operators : map [types.NamespacedName ]struct {} {},
104
+ log : log ,
105
+ factory : factory ,
106
+ lastResourceVersion : map [types.NamespacedName ]string {},
107
107
}, nil
108
108
}
109
109
@@ -115,40 +115,61 @@ func (r *OperatorReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
115
115
log := r .log .WithValues ("request" , req )
116
116
log .V (1 ).Info ("reconciling operator" )
117
117
118
- // Fetch the Operator from the cache
118
+ // Get the Operator
119
119
ctx := context .TODO ()
120
+ create := false
121
+ name := req .NamespacedName .Name
120
122
in := & operatorsv1.Operator {}
121
123
if err := r .Get (ctx , req .NamespacedName , in ); err != nil {
122
124
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 )
126
127
} else {
127
- log .Error (err , "Error finding Operator" )
128
+ log .Error (err , "Error requesting Operator" )
129
+ return reconcile.Result {Requeue : true }, nil
128
130
}
131
+ }
129
132
133
+ rv , ok := r .getLastResourceVersion (req .NamespacedName )
134
+ if ! create && ok && rv == in .ResourceVersion {
135
+ log .V (1 ).Info ("Operator is already up-to-date" )
130
136
return reconcile.Result {}, nil
131
137
}
132
- r .observe (req .NamespacedName )
138
+
139
+ // Set the cached resource version to 0 so we can handle
140
+ // the race with requests enqueuing via mapComponentRequests
141
+ r .setLastResourceVersion (req .NamespacedName , "0" )
133
142
134
143
// Wrap with convenience decorator
135
144
operator , err := r .factory .NewOperator (in )
136
145
if err != nil {
137
146
log .Error (err , "Could not wrap Operator with convenience decorator" )
138
- return reconcile.Result {}, nil
147
+ return reconcile.Result {Requeue : true }, nil
139
148
}
140
149
141
150
if err = r .updateComponents (ctx , operator ); err != nil {
142
151
log .Error (err , "Could not update components" )
143
- return reconcile.Result {}, nil
152
+ return reconcile.Result {Requeue : true }, nil
144
153
145
154
}
146
155
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
156
+ if create {
157
+ if err := r .Create (context .Background (), operator .Operator ); err != nil && ! apierrors .IsAlreadyExists (err ) {
158
+ r .log .Error (err , "Could not create Operator" , "operator" , name )
159
+ return ctrl.Result {Requeue : true }, nil
160
+ }
161
+ } else {
162
+ if err := r .Status ().Update (ctx , operator .Operator ); err != nil {
163
+ log .Error (err , "Could not update Operator status" )
164
+ return ctrl.Result {Requeue : true }, nil
165
+ }
150
166
}
151
167
168
+ // Only set the resource version if it already exists.
169
+ // If it does not exist, it means mapComponentRequests was called
170
+ // while we were reconciling and we need to reconcile again
171
+ r .setLastResourceVersionIfExists (req .NamespacedName , operator .GetResourceVersion ())
172
+
152
173
return ctrl.Result {}, nil
153
174
}
154
175
@@ -196,45 +217,44 @@ func (r *OperatorReconciler) listComponents(ctx context.Context, selector labels
196
217
return componentLists , nil
197
218
}
198
219
199
- func (r * OperatorReconciler ) observed (name types.NamespacedName ) bool {
220
+ func (r * OperatorReconciler ) getLastResourceVersion (name types.NamespacedName ) ( string , bool ) {
200
221
r .mu .RLock ()
201
222
defer r .mu .RUnlock ()
202
- _ , ok := r .operators [name ]
203
- return ok
223
+ rv , ok := r .lastResourceVersion [name ]
224
+ return rv , ok
204
225
}
205
226
206
- func (r * OperatorReconciler ) observe (name types.NamespacedName ) {
227
+ func (r * OperatorReconciler ) setLastResourceVersion (name types.NamespacedName , rv string ) {
207
228
r .mu .Lock ()
208
229
defer r .mu .Unlock ()
209
- r .operators [name ] = struct {}{}
230
+ r .lastResourceVersion [name ] = rv
210
231
}
211
232
212
- func (r * OperatorReconciler ) unobserve (name types.NamespacedName ) {
233
+ func (r * OperatorReconciler ) setLastResourceVersionIfExists (name types.NamespacedName , rv string ) {
213
234
r .mu .Lock ()
214
235
defer r .mu .Unlock ()
215
- delete (r .operators , name )
236
+ if _ , ok := r .lastResourceVersion [name ]; ok {
237
+ r .lastResourceVersion [name ] = rv
238
+ }
216
239
}
217
240
218
- func (r * OperatorReconciler ) mapComponentRequests (obj handler.MapObject ) (requests []reconcile.Request ) {
241
+ func (r * OperatorReconciler ) unsetLastResourceVersion (name types.NamespacedName ) {
242
+ r .mu .Lock ()
243
+ defer r .mu .Unlock ()
244
+ delete (r .lastResourceVersion , name )
245
+ }
246
+
247
+ func (r * OperatorReconciler ) mapComponentRequests (obj handler.MapObject ) []reconcile.Request {
248
+ var requests []reconcile.Request
219
249
if obj .Meta == nil {
220
- return
250
+ return requests
221
251
}
222
252
223
253
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
- }
254
+ // unset the last recorded resource version so the Operator will reconcile
255
+ r .unsetLastResourceVersion (name )
256
+ requests = append (requests , reconcile.Request {NamespacedName : name })
237
257
}
238
258
239
- return
259
+ return requests
240
260
}
0 commit comments