Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix wrong MergePatchOrCreate usage #4057

Merged
merged 1 commit into from
May 14, 2021

Conversation

timebertt
Copy link
Member

How to categorize this PR?

/kind bug

⚠️ TL;DR

When using client.MergeFrom (e.g. in MergePatchOrCreate), the object should already be filled with the last-known/last applied state, so that client.MergeFrom will compute the correct patch including removal of optional fields, that shall be unset with the patch.
For this, I added a helper func GetAndCreateOrMergePatch, which can be used analogously to CreateOrUpdate, but is safe to use with a cached client (won't run into conflicts), as long as operating on fields that are exclusively owned by the controller.

What this PR does / why we need it

In one of my recent refactorings I switched to MergePatchOrCreate for syncing the cluster resource to the Seed here.

However I missed an import detail wrt using client.MergeFrom:
When computing a patch with client.MergeFrom from an empty object (like in the usage on master linked above), the patch won't remove optional fields/sections (marked with omitempty) that are now unset in comparison to the current state on the cluster, because the patch does not marshal empty optional fields.

For example:
When a Shoot was previously hibernated and the Cluster object looked like this:

apiVersion: extensions.gardener.cloud/v1alpha1
kind: Cluster
# ...
spec:
  # ...
  shoot:
    apiVersion: core.gardener.cloud/v1beta1
    kind: Shoot
    spec:
      hibernation:
        enabled: true
# ...

and the hibernation section is removed in the Shoot object to wake it up, the patch will just look similar to this:

{"spec":{"shoot":{"apiVersion":"core.gardener.cloud/v1beta1","kind":"Shoot","metadata":{"creationTimestamp":null},"spec":{"cloudProfileName":"","kubernetes":{"version":""},"networking":{"type":""},"provider":{"type":"","workers":null},"region":"","secretBindingName":""},"status":{"gardener":{"id":"","name":"","version":""},"hibernated":false,"technicalID":"","uid":""}}}}

Note, that there is no hibernation section in the patch, which means that the hibernation section in the Cluster resource will not be changed/removed.

In an ideal world, we would just use SSA (server-side-apply) and don't care about anything of this at all (just send the desired state of the fields we care about and let the API server take care of applying the changes) but unfortunately, we still have to support older k8s versions for Seeds, that don't support SSA.

@timebertt timebertt requested a review from a team as a code owner May 14, 2021 08:06
@gardener-robot gardener-robot added kind/bug Bug needs/review size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels May 14, 2021
@timebertt
Copy link
Member Author

/invite @ialidzhikov
/cc @rfranzke

Copy link
Member

@ialidzhikov ialidzhikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@ialidzhikov ialidzhikov merged commit 906ffd1 into gardener:master May 14, 2021
@timebertt timebertt deleted the fix/merge-from branch May 14, 2021 11:32
timebertt added a commit to timebertt/gardener that referenced this pull request May 31, 2021
For the reasons explained in gardener#4057, MergePatchOrCreate is not safe to use
without reading objects from the API server / cache first. Ideally we would
switch to server-side apply to simplify such operations on controlled objects
(e.g. extension resources), though we cannot leverage it for now because of
several issues described in gardener#4122.
timebertt added a commit to timebertt/gardener that referenced this pull request May 31, 2021
As described in gardener#4057, *PatchOrCreate is easy to use incorrectly.
Remove *PatchOrCreate funcs in order to minimize the risk of misuse.
There might still be good use cases for this mechanism, though they
are limited. If there is such a good use case, we can use it also
without a helper func.
rfranzke pushed a commit that referenced this pull request Jun 1, 2021
* Cleanup unused/dead code

* Make EXPECTPatch easier to debug

* Refactor extensions helper funcs

This prefactoring performs several changes in the extension helper funcs:
- the funcs directly work on the passed in objects instead of creating
  new objects via separately passed newObjFuncs. Passed objects are
  expected to be filled with the latest state the controller/component
  applied/observed/retrieved, but at least namespace and name.
  This way, controllers can keep working on a single in-memory object
  instead of retrieving the same object over and over again.
- WaitUntilExtensionObjectReady additionally checks that the added
  timestamp annotation is present on the object to prevent false early
  exits when reading stale data (e.g. from a cached client).
- makes naming more consistent and clearer

This provides the following benefits:
- makes the funcs ready to be used with cached clients, as they are able
  to properly handle stale data now
- controllers/components automatically have the latest state of the
  objects present without retrieving them again from the API server.
  This will ease further refactorings and allows to safely use
  patches in a lot of cases instead of CreateOrUpdate (+RetryOnConflict).
- the funcs are easier to use and comprehend: only the desired object
  needs to be passed, no need to pass newObjFuncs, namespace and name
- the funcs are more similar to other helper funcs (e.g. CreateOrUpdate)
  and the mechanisms employed in e.g. c-r clients
- no need to pass a client.Object to postReadyFuncs which needs to be
  casted again

* Make BackupBucket controller ready for cached client

* Make BackupEntry controller/component ready for cached client

* Make ContainerRuntime component ready for cached client

* Make ControlPlane component ready for cached client

* Make Extension component ready for cached client

* Make Infrastructure component ready for cached client

* Make Network component ready for cached client

* Make OperatingSystemConfig component ready for cached client

* Make Worker component ready for cached client

* Fix failing unit test

* Address review feedback

* Switch from MergePatchOrCreate to GetAndCreateOrMergePatch

For the reasons explained in #4057, MergePatchOrCreate is not safe to use
without reading objects from the API server / cache first. Ideally we would
switch to server-side apply to simplify such operations on controlled objects
(e.g. extension resources), though we cannot leverage it for now because of
several issues described in #4122.

* Remove *PatchOrCreate funcs

As described in #4057, *PatchOrCreate is easy to use incorrectly.
Remove *PatchOrCreate funcs in order to minimize the risk of misuse.
There might still be good use cases for this mechanism, though they
are limited. If there is such a good use case, we can use it also
without a helper func.
krgostev pushed a commit to krgostev/gardener that referenced this pull request Apr 21, 2022
krgostev pushed a commit to krgostev/gardener that referenced this pull request Apr 21, 2022
* Cleanup unused/dead code

* Make EXPECTPatch easier to debug

* Refactor extensions helper funcs

This prefactoring performs several changes in the extension helper funcs:
- the funcs directly work on the passed in objects instead of creating
  new objects via separately passed newObjFuncs. Passed objects are
  expected to be filled with the latest state the controller/component
  applied/observed/retrieved, but at least namespace and name.
  This way, controllers can keep working on a single in-memory object
  instead of retrieving the same object over and over again.
- WaitUntilExtensionObjectReady additionally checks that the added
  timestamp annotation is present on the object to prevent false early
  exits when reading stale data (e.g. from a cached client).
- makes naming more consistent and clearer

This provides the following benefits:
- makes the funcs ready to be used with cached clients, as they are able
  to properly handle stale data now
- controllers/components automatically have the latest state of the
  objects present without retrieving them again from the API server.
  This will ease further refactorings and allows to safely use
  patches in a lot of cases instead of CreateOrUpdate (+RetryOnConflict).
- the funcs are easier to use and comprehend: only the desired object
  needs to be passed, no need to pass newObjFuncs, namespace and name
- the funcs are more similar to other helper funcs (e.g. CreateOrUpdate)
  and the mechanisms employed in e.g. c-r clients
- no need to pass a client.Object to postReadyFuncs which needs to be
  casted again

* Make BackupBucket controller ready for cached client

* Make BackupEntry controller/component ready for cached client

* Make ContainerRuntime component ready for cached client

* Make ControlPlane component ready for cached client

* Make Extension component ready for cached client

* Make Infrastructure component ready for cached client

* Make Network component ready for cached client

* Make OperatingSystemConfig component ready for cached client

* Make Worker component ready for cached client

* Fix failing unit test

* Address review feedback

* Switch from MergePatchOrCreate to GetAndCreateOrMergePatch

For the reasons explained in gardener#4057, MergePatchOrCreate is not safe to use
without reading objects from the API server / cache first. Ideally we would
switch to server-side apply to simplify such operations on controlled objects
(e.g. extension resources), though we cannot leverage it for now because of
several issues described in gardener#4122.

* Remove *PatchOrCreate funcs

As described in gardener#4057, *PatchOrCreate is easy to use incorrectly.
Remove *PatchOrCreate funcs in order to minimize the risk of misuse.
There might still be good use cases for this mechanism, though they
are limited. If there is such a good use case, we can use it also
without a helper func.
krgostev pushed a commit to krgostev/gardener that referenced this pull request Jul 5, 2022
krgostev pushed a commit to krgostev/gardener that referenced this pull request Jul 5, 2022
* Cleanup unused/dead code

* Make EXPECTPatch easier to debug

* Refactor extensions helper funcs

This prefactoring performs several changes in the extension helper funcs:
- the funcs directly work on the passed in objects instead of creating
  new objects via separately passed newObjFuncs. Passed objects are
  expected to be filled with the latest state the controller/component
  applied/observed/retrieved, but at least namespace and name.
  This way, controllers can keep working on a single in-memory object
  instead of retrieving the same object over and over again.
- WaitUntilExtensionObjectReady additionally checks that the added
  timestamp annotation is present on the object to prevent false early
  exits when reading stale data (e.g. from a cached client).
- makes naming more consistent and clearer

This provides the following benefits:
- makes the funcs ready to be used with cached clients, as they are able
  to properly handle stale data now
- controllers/components automatically have the latest state of the
  objects present without retrieving them again from the API server.
  This will ease further refactorings and allows to safely use
  patches in a lot of cases instead of CreateOrUpdate (+RetryOnConflict).
- the funcs are easier to use and comprehend: only the desired object
  needs to be passed, no need to pass newObjFuncs, namespace and name
- the funcs are more similar to other helper funcs (e.g. CreateOrUpdate)
  and the mechanisms employed in e.g. c-r clients
- no need to pass a client.Object to postReadyFuncs which needs to be
  casted again

* Make BackupBucket controller ready for cached client

* Make BackupEntry controller/component ready for cached client

* Make ContainerRuntime component ready for cached client

* Make ControlPlane component ready for cached client

* Make Extension component ready for cached client

* Make Infrastructure component ready for cached client

* Make Network component ready for cached client

* Make OperatingSystemConfig component ready for cached client

* Make Worker component ready for cached client

* Fix failing unit test

* Address review feedback

* Switch from MergePatchOrCreate to GetAndCreateOrMergePatch

For the reasons explained in gardener#4057, MergePatchOrCreate is not safe to use
without reading objects from the API server / cache first. Ideally we would
switch to server-side apply to simplify such operations on controlled objects
(e.g. extension resources), though we cannot leverage it for now because of
several issues described in gardener#4122.

* Remove *PatchOrCreate funcs

As described in gardener#4057, *PatchOrCreate is easy to use incorrectly.
Remove *PatchOrCreate funcs in order to minimize the risk of misuse.
There might still be good use cases for this mechanism, though they
are limited. If there is such a good use case, we can use it also
without a helper func.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Bug size/M Denotes a PR that changes 30-99 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants