Bug Report
What did you do?
A clear and concise description of the steps you took (or insert a code snippet).
As documented here, in order to avoid CR status changes causing an infinite loop of the reconcile() function, we should make the status block a subresource, following steps outlined here.
However, we have found that even though we successfully set the CRD status attribute as a subresource, we still see infinite looping of reconcile() when status is changed. A simple example to reproduce, using the canonical PodSet operator demo, would have the following types spec:
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=podsets,scope=Namespaced
type PodSet struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec PodSetSpec `json:"spec,omitempty"`
Status PodSetStatus `json:"status,omitempty"`
}
which results in a CRD with a subresource, as expected:
...
spec:
group: app.example.com
names:
kind: PodSet
listKind: PodSetList
plural: podsets
singular: podset
scope: Namespaced
subresources:
status: {}
validation:
...
However, placing the following code in the controller reconcile() function:
// NOTE: the 'Updated' timestamp will change each time reconcile() is called
// If status is not treated correctly as a subresource
instance.Status.Updated = time.Now().String()
err = r.client.Status().Update(context.TODO(), podSet)
if err != nil {
reqLogger.Error(err, "failed to update the podSet")
return reconcile.Result{}, err
}
This updates the 'Updated' timestamp on the status object every time reconcile() is called. According to the documentation, this change to the status object should not trigger a call of the reconcile() function because the status object is a subresource, thus avoiding an infinite loop. However, the infinite loop occurs. Is the documentation wrong, is there a bug, or am I missing something?
What did you expect to see?
When setting the 'status' attribute as a subresource of a CRD, I should be able to update status in the reconcile() function without re-invoking the reconcile() loop.
What did you see instead? Under which circumstances?
When updating the status attribute, even though its a subresource, I see an infinite loop of the reconcile() function.
Environment
- operator-sdk version: v0.15.2
- Kubernetes version information: 1.16.2
-
Kubernetes cluster kind: openshift
-
Are you writing your operator in ansible, helm, or go? go
Possible Solution
Additional context
I've created a functional reproduction of the issue in this repo
Bug Report
What did you do?
A clear and concise description of the steps you took (or insert a code snippet).
As documented here, in order to avoid CR status changes causing an infinite loop of the reconcile() function, we should make the
statusblock a subresource, following steps outlined here.However, we have found that even though we successfully set the CRD status attribute as a subresource, we still see infinite looping of reconcile() when status is changed. A simple example to reproduce, using the canonical PodSet operator demo, would have the following types spec:
which results in a CRD with a subresource, as expected:
However, placing the following code in the controller reconcile() function:
This updates the 'Updated' timestamp on the status object every time reconcile() is called. According to the documentation, this change to the status object should not trigger a call of the reconcile() function because the status object is a subresource, thus avoiding an infinite loop. However, the infinite loop occurs. Is the documentation wrong, is there a bug, or am I missing something?
What did you expect to see?
When setting the 'status' attribute as a subresource of a CRD, I should be able to update status in the reconcile() function without re-invoking the reconcile() loop.
What did you see instead? Under which circumstances?
When updating the status attribute, even though its a subresource, I see an infinite loop of the reconcile() function.
Environment
Kubernetes cluster kind: openshift
Are you writing your operator in ansible, helm, or go? go
Possible Solution
Additional context
I've created a functional reproduction of the issue in this repo