@@ -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,21 +115,27 @@ 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 {}, nil
128
130
}
131
+ }
129
132
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
+ }
131
138
}
132
- r .observe (req .NamespacedName )
133
139
134
140
// Wrap with convenience decorator
135
141
operator , err := r .factory .NewOperator (in )
@@ -144,11 +150,19 @@ func (r *OperatorReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
144
150
145
151
}
146
152
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 ); err != nil && ! apierrors .IsAlreadyExists (err ) {
155
+ r .log .Error (err , "Could not create Operator" , "operator" , name )
156
+ }
157
+ } else {
158
+ if err := r .Status ().Update (ctx , operator .Operator ); err != nil {
159
+ log .Error (err , "Could not update Operator status" )
160
+ return ctrl.Result {}, err
161
+ }
150
162
}
151
163
164
+ r .setLastResourceVersion (req .NamespacedName , operator .Operator .ResourceVersion )
165
+
152
166
return ctrl.Result {}, nil
153
167
}
154
168
@@ -196,45 +210,36 @@ func (r *OperatorReconciler) listComponents(ctx context.Context, selector labels
196
210
return componentLists , nil
197
211
}
198
212
199
- func (r * OperatorReconciler ) observed (name types.NamespacedName ) bool {
213
+ func (r * OperatorReconciler ) getLastResourceVersion (name types.NamespacedName ) ( string , bool ) {
200
214
r .mu .RLock ()
201
215
defer r .mu .RUnlock ()
202
- _ , ok := r .operators [name ]
203
- return ok
216
+ rv , ok := r .lastResourceVersion [name ]
217
+ return rv , ok
204
218
}
205
219
206
- func (r * OperatorReconciler ) observe (name types.NamespacedName ) {
220
+ func (r * OperatorReconciler ) setLastResourceVersion (name types.NamespacedName , rv string ) {
207
221
r .mu .Lock ()
208
222
defer r .mu .Unlock ()
209
- r .operators [name ] = struct {}{}
223
+ r .lastResourceVersion [name ] = rv
210
224
}
211
225
212
- func (r * OperatorReconciler ) unobserve (name types.NamespacedName ) {
226
+ func (r * OperatorReconciler ) unsetLastResourceVersion (name types.NamespacedName ) {
213
227
r .mu .Lock ()
214
228
defer r .mu .Unlock ()
215
- delete (r .operators , name )
229
+ delete (r .lastResourceVersion , name )
216
230
}
217
231
218
- func (r * OperatorReconciler ) mapComponentRequests (obj handler.MapObject ) (requests []reconcile.Request ) {
232
+ func (r * OperatorReconciler ) mapComponentRequests (obj handler.MapObject ) []reconcile.Request {
233
+ var requests []reconcile.Request
219
234
if obj .Meta == nil {
220
- return
235
+ return requests
221
236
}
222
237
223
238
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
- }
239
+ // unset the last recorded resource version so the Operator will reconcile
240
+ r .unsetLastResourceVersion (name )
241
+ requests = append (requests , reconcile.Request {NamespacedName : name })
237
242
}
238
243
239
- return
244
+ return requests
240
245
}
0 commit comments