Skip to content

Commit b85df58

Browse files
committed
prevent no-op hotlooping on Operators
1 parent 8c96973 commit b85df58

File tree

1 file changed

+47
-41
lines changed

1 file changed

+47
-41
lines changed

Diff for: pkg/controller/operators/operator_controller.go

+47-41
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)