Skip to content

Commit

Permalink
fix(kubeclient): retry patch on conflict (#2499)
Browse files Browse the repository at this point in the history
Signed-off-by: Hidde Beydals <[email protected]>
(cherry picked from commit 0e35be5)
  • Loading branch information
hiddeco authored and github-actions[bot] committed Sep 9, 2024
1 parent b0d83ca commit b104336
Showing 1 changed file with 38 additions and 31 deletions.
69 changes: 38 additions & 31 deletions internal/kubeclient/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
)

Expand Down Expand Up @@ -83,41 +84,47 @@ type UnstructuredPatchFn func(src, dest unstructured.Unstructured) error
func PatchUnstructured(ctx context.Context, c client.Client, obj ObjectWithKind, modify UnstructuredPatchFn) error {
destObj := unstructured.Unstructured{}
destObj.SetGroupVersionKind(obj.GroupVersionKind())
if err := c.Get(ctx, client.ObjectKeyFromObject(obj), &destObj); err != nil {
return fmt.Errorf(
"unable to get unstructured object for %s %q in namespace %q: %w",
destObj.GroupVersionKind().Kind, obj.GetName(), obj.GetNamespace(), err,
)
}

// Create a patch for the unstructured object.
//
// As we expect the object to be modified by the callback, while it may
// also simultaneously be modified by other clients (e.g. someone updating
// the object via `kubectl`), we use an optimistic lock to ensure that we
// only apply the patch if the object has not been modified since we
// fetched it.
patch := client.MergeFromWithOptions(destObj.DeepCopy(), client.MergeFromWithOptimisticLock{})

// Convert the typed object to an unstructured object.
srcObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
return fmt.Errorf("could not convert typed source object to unstructured object: %w", err)
}
srcApp := unstructured.Unstructured{Object: srcObj}

// Apply modifications to the unstructured object.
if err = modify(srcApp, destObj); err != nil {
return fmt.Errorf("failed to apply modifications to unstructured object: %w", err)
}

// Issue the patch to the unstructured object.
if err = c.Patch(ctx, &destObj, patch); err != nil {
return fmt.Errorf("failed to patch the object: %w", err)
if err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
if err := c.Get(ctx, client.ObjectKeyFromObject(obj), &destObj); err != nil {
return fmt.Errorf(
"unable to get unstructured object for %s %q in namespace %q: %w",
destObj.GroupVersionKind().Kind, obj.GetName(), obj.GetNamespace(), err,
)
}

// Create a patch for the unstructured object.
//
// As we expect the object to be modified by the callback, while it may
// also simultaneously be modified by other clients (e.g. someone updating
// the object via `kubectl`), we use an optimistic lock to ensure that we
// only apply the patch if the object has not been modified since we
// fetched it.
patch := client.MergeFromWithOptions(destObj.DeepCopy(), client.MergeFromWithOptimisticLock{})

// Convert the typed object to an unstructured object.
srcObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
return fmt.Errorf("could not convert typed source object to unstructured object: %w", err)
}
srcApp := unstructured.Unstructured{Object: srcObj}

// Apply modifications to the unstructured object.
if err = modify(srcApp, destObj); err != nil {
return fmt.Errorf("failed to apply modifications to unstructured object: %w", err)
}

// Issue the patch to the unstructured object.
if err = c.Patch(ctx, &destObj, patch); err != nil {
return fmt.Errorf("failed to patch the object: %w", err)
}
return nil
}); err != nil {
return err
}

// Convert the unstructured object back to the typed object.
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(destObj.Object, obj); err != nil {
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(destObj.Object, obj); err != nil {
return fmt.Errorf("error converting unstructured object to typed object: %w", err)
}

Expand Down

0 comments on commit b104336

Please sign in to comment.