Skip to content

Commit 3d43b56

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

File tree

1 file changed

+44
-39
lines changed

1 file changed

+44
-39
lines changed

pkg/controller/operators/operator_controller.go

Lines changed: 44 additions & 39 deletions
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,21 +115,27 @@ 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{}, 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)
@@ -144,11 +150,19 @@ func (r *OperatorReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
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); 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+
}
150162
}
151163

164+
r.setLastResourceVersion(req.NamespacedName, operator.Operator.ResourceVersion)
165+
152166
return ctrl.Result{}, nil
153167
}
154168

@@ -196,45 +210,36 @@ func (r *OperatorReconciler) listComponents(ctx context.Context, selector labels
196210
return componentLists, nil
197211
}
198212

199-
func (r *OperatorReconciler) observed(name types.NamespacedName) bool {
213+
func (r *OperatorReconciler) getLastResourceVersion(name types.NamespacedName) (string, bool) {
200214
r.mu.RLock()
201215
defer r.mu.RUnlock()
202-
_, ok := r.operators[name]
203-
return ok
216+
rv, ok := r.lastResourceVersion[name]
217+
return rv, ok
204218
}
205219

206-
func (r *OperatorReconciler) observe(name types.NamespacedName) {
220+
func (r *OperatorReconciler) setLastResourceVersion(name types.NamespacedName, rv string) {
207221
r.mu.Lock()
208222
defer r.mu.Unlock()
209-
r.operators[name] = struct{}{}
223+
r.lastResourceVersion[name] = rv
210224
}
211225

212-
func (r *OperatorReconciler) unobserve(name types.NamespacedName) {
226+
func (r *OperatorReconciler) unsetLastResourceVersion(name types.NamespacedName) {
213227
r.mu.Lock()
214228
defer r.mu.Unlock()
215-
delete(r.operators, name)
229+
delete(r.lastResourceVersion, name)
216230
}
217231

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
219234
if obj.Meta == nil {
220-
return
235+
return requests
221236
}
222237

223238
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})
237242
}
238243

239-
return
244+
return requests
240245
}

0 commit comments

Comments
 (0)