java-operator-sdk icon indicating copy to clipboard operation
java-operator-sdk copied to clipboard

Updating status sub-resource without an UpdateControl

Open bearpaws opened this issue 3 years ago • 10 comments

It seems the only way to update the status sub-resource via the SDK is to return an UpdateControl from Reconciler.reconcile().

Is there a way to update the status from the reconcile method (without causing a resource version conflict)?

In some cases it would be useful to update the status before the method returns, especially the first time it runs for a new resource, so that users get immediate feedback that the operator has reacted.

bearpaws avatar Feb 24 '22 21:02 bearpaws

You could technically use the kubernetes client to update your resource's status directly.

metacosm avatar Feb 24 '22 21:02 metacosm

I had tried that and it works, however when the subsequent status change from the UpdateControl is applied, I get a resource version conflict:

[EventHandler-myreconciler] INFO io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher - 
Kubernetes exception 409 Failure executing: PUT at: https://***/apis/***/v1alpha1/namespaces/***/***/test/status.
Message: Operation cannot be fulfilled on *** "test": the object has been modified; please apply your changes to the latest version and try again.

I presume the SDK is not expecting the status to be modified manually.

bearpaws avatar Feb 25 '22 18:02 bearpaws

@bearpaws what you can do is as a trick, to set the resource version manually to the resource you pass to UpdateControl, use the resource version returned from the manuall updated resource response. (.metadata.resourceVersion).
This is an optimistic locking failure.

csviri avatar Feb 25 '22 18:02 csviri

I think we should use patch for status update. Also, having the resource version change on a status update seems like something that shouldn't happen so we might want to look into it.

metacosm avatar Feb 25 '22 20:02 metacosm

Nevermind. I always confuse the generation and the resource version. 🤦 resourceVersion is updated on every write, while generation only changes when the spec changes.

https://stackoverflow.com/questions/47100389/what-is-the-difference-between-a-resourceversion-and-a-generation

metacosm avatar Feb 25 '22 20:02 metacosm

@bearpaws what you can do is as a trick, to set the resource version manually to the resource you pass to UpdateControl, use the resource version returned from the manuall updated resource response. (.metadata.resourceVersion).

Perhaps we should document this or even provide an API call for this scenario?

metacosm avatar Feb 25 '22 20:02 metacosm

I've confirmed that it works by passing along the new resource version. Thank you both.

I think providing an API call is a good idea, since it's not clear that manual changes like this won't break versioning assumptions made elsewhere. For my use case, the ideal place to use it would be from Reconciler.reconcile().

bearpaws avatar Feb 25 '22 20:02 bearpaws

I think the current API is enough. Kubernetes API is a restfull api and all fabricio methods returns the new version when successfull. As such, we should always use the returned entity for subsequent calls.

I always thought that's why the UpdateControl.updateStatus takes the whole resource instead of just the Status. The documentation for UpdateControl.updateResource state @param customResource customResource to use for update, maybe the UpdateControl.updateStatus should also be explicit.

Hacking with resourceVersion is coupling the code with the optimistic concurrency detection mechanism of kubernetes.

scrocquesel avatar Feb 25 '22 21:02 scrocquesel

Note that this is also true for ErrorStatusHandler.updateErrorStatus.

scrocquesel avatar Feb 25 '22 21:02 scrocquesel

Perhaps we should document this or even provide an API call for this scenario?

IMHO Not sure if it makes sense to provide an explicit API for this (also don't see how exactly). The whole scenario making more updates to the status in one reconciliation is rather exotic. We currently state in docs that we do optimistic locking for both status and/or resources update (as far I remember).

I think we should use patch for status update. Also, having the resource version change on a status update seems like something that shouldn't happen so we might want to look into it.

The patch would change the resource version too. Currently any change changes the resources version on k8s objects AFAIK. I remember discussion this, originally I also wanted to patch it to, that would not do optimistic locking. But after some discussion we decided to stick with the optimistic lock, since its more safe regarding consistency just to reconcile again (what should happen after a conflict).

I always thought that's why the UpdateControl.updateStatus takes the whole resource instead of just the Status. The documentation for UpdateControl.updateResource state @param customResource customResource to use for update, maybe the UpdateControl.updateStatus should also be explicit.

Will check but, also in go api it takes the whole resource, also in fabric8 library to update the status. Yes the docs should state this.

Also if you would set resourceVersion to null, it won't do optimistick locking as far I remember. So that is an another hack :)

csviri avatar Feb 25 '22 21:02 csviri

Since there was no activity on this for a long time, will close the issue.

csviri avatar Jan 30 '23 15:01 csviri