Server-side apply on apps.cozystack.io applications never reports a conflict between field managers, so two clients applying the same application overwrite each other without noticing. The storage keeps no managedFields: every read rebuilds the object from its HelmRelease, and nothing stores the field owners on the way. Apply then always sees an object that nobody owns.
This breaks what SSA clients expect. A GitOps tool and a person, or two controllers, that set the same field should get a 409 and have to decide who wins. Here the last apply wins silently and the other side's value is gone.
I reproduced it through the in-process apiserver with a fake client on main (faf8a25):
- create a Redis with
spec.replicas: 0;
- apply
spec.replicas: 1 as field manager manager-a;
- apply
spec.replicas: 2 as manager-b, without forcing conflicts.
Step 3 succeeds. The object reads back with replicas: 2, and metadata.managedFields is empty after every step. I did not run it on a live cluster, but the same code path serves kubectl apply --server-side there.
A test in my dependency rollup branch pins today's behaviour (TestAppsAPIServerSideApplyTracksNoFieldOwners in pkg/cmd/server), so a fix has to flip it. To keep field owners across the HelmRelease round trip they need a place to live, for example an annotation on the HelmRelease that the storage reads back. REST.Update would also have to carry it over from the live object, the same way it carries the shard label and spec.suspend. That is the same spot #4482 is about.
Server-side apply on
apps.cozystack.ioapplications never reports a conflict between field managers, so two clients applying the same application overwrite each other without noticing. The storage keeps nomanagedFields: every read rebuilds the object from its HelmRelease, and nothing stores the field owners on the way. Apply then always sees an object that nobody owns.This breaks what SSA clients expect. A GitOps tool and a person, or two controllers, that set the same field should get a 409 and have to decide who wins. Here the last apply wins silently and the other side's value is gone.
I reproduced it through the in-process apiserver with a fake client on main (faf8a25):
spec.replicas: 0;spec.replicas: 1as field managermanager-a;spec.replicas: 2asmanager-b, without forcing conflicts.Step 3 succeeds. The object reads back with
replicas: 2, andmetadata.managedFieldsis empty after every step. I did not run it on a live cluster, but the same code path serveskubectl apply --server-sidethere.A test in my dependency rollup branch pins today's behaviour (
TestAppsAPIServerSideApplyTracksNoFieldOwnersinpkg/cmd/server), so a fix has to flip it. To keep field owners across the HelmRelease round trip they need a place to live, for example an annotation on the HelmRelease that the storage reads back.REST.Updatewould also have to carry it over from the live object, the same way it carries the shard label andspec.suspend. That is the same spot #4482 is about.