From aef159aa407aae8f8edf21ae2ef54b9df6dad07b Mon Sep 17 00:00:00 2001 From: Chris Bandy Date: Wed, 3 Mar 2021 08:55:57 -0600 Subject: [PATCH] =?UTF-8?q?=E2=9A=A0=20Change=20client.Patch=20to=20take?= =?UTF-8?q?=20client.Object?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to use metav1 accessors to speed up the merge-patch implementation. --- pkg/client/interfaces.go | 2 +- pkg/client/patch.go | 61 +++---------------- .../controllerutil/controllerutil.go | 4 +- 3 files changed, 13 insertions(+), 54 deletions(-) diff --git a/pkg/client/interfaces.go b/pkg/client/interfaces.go index 09636968f1..4dc6eb79a0 100644 --- a/pkg/client/interfaces.go +++ b/pkg/client/interfaces.go @@ -39,7 +39,7 @@ type Patch interface { // Type is the PatchType of the patch. Type() types.PatchType // Data is the raw data representing the patch. - Data(obj runtime.Object) ([]byte, error) + Data(obj Object) ([]byte, error) } // TODO(directxman12): is there a sane way to deal with get/delete options? diff --git a/pkg/client/patch.go b/pkg/client/patch.go index 07ef3e2d59..260db896fb 100644 --- a/pkg/client/patch.go +++ b/pkg/client/patch.go @@ -20,8 +20,6 @@ import ( "fmt" jsonpatch "github.com/evanphx/json-patch" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/json" @@ -47,7 +45,7 @@ func (s *patch) Type() types.PatchType { } // Data implements Patch. -func (s *patch) Data(obj runtime.Object) ([]byte, error) { +func (s *patch) Data(obj Object) ([]byte, error) { return s.data, nil } @@ -87,7 +85,7 @@ type MergeFromOptions struct { } type mergeFromPatch struct { - from runtime.Object + from Object opts MergeFromOptions } @@ -96,8 +94,12 @@ func (s *mergeFromPatch) Type() types.PatchType { return types.MergePatchType } -func (*mergeFromPatch) data(original, modified Object, opts MergeFromOptions) ([]byte, error) { - if opts.OptimisticLock { +// Data implements Patch. +func (s *mergeFromPatch) Data(obj Object) ([]byte, error) { + original := s.from + modified := obj + + if s.opts.OptimisticLock { version := original.GetResourceVersion() if len(version) == 0 { return nil, fmt.Errorf("cannot use OptimisticLock, object %q does not have any resource version we can use", original) @@ -120,64 +122,21 @@ func (*mergeFromPatch) data(original, modified Object, opts MergeFromOptions) ([ return nil, err } - return jsonpatch.CreateMergePatch(originalJSON, modifiedJSON) -} - -// Data implements Patch. -func (s *mergeFromPatch) Data(obj runtime.Object) ([]byte, error) { - fromObject, fromOk := s.from.(Object) - objObject, objOk := obj.(Object) - - if fromOk && objOk { - return s.data(fromObject, objObject, s.opts) - } - - originalJSON, err := json.Marshal(s.from) - if err != nil { - return nil, err - } - - modifiedJSON, err := json.Marshal(obj) - if err != nil { - return nil, err - } - data, err := jsonpatch.CreateMergePatch(originalJSON, modifiedJSON) if err != nil { return nil, err } - if s.opts.OptimisticLock { - dataMap := map[string]interface{}{} - if err := json.Unmarshal(data, &dataMap); err != nil { - return nil, err - } - fromMeta, ok := s.from.(metav1.Object) - if !ok { - return nil, fmt.Errorf("cannot use OptimisticLock, from object %q is not a valid metav1.Object", s.from) - } - resourceVersion := fromMeta.GetResourceVersion() - if len(resourceVersion) == 0 { - return nil, fmt.Errorf("cannot use OptimisticLock, from object %q does not have any resource version we can use", s.from) - } - u := &unstructured.Unstructured{Object: dataMap} - u.SetResourceVersion(resourceVersion) - data, err = json.Marshal(u) - if err != nil { - return nil, err - } - } - return data, nil } // MergeFrom creates a Patch that patches using the merge-patch strategy with the given object as base. -func MergeFrom(obj runtime.Object) Patch { +func MergeFrom(obj Object) Patch { return &mergeFromPatch{from: obj} } // MergeFromWithOptions creates a Patch that patches using the merge-patch strategy with the given object as base. -func MergeFromWithOptions(obj runtime.Object, opts ...MergeFromOption) Patch { +func MergeFromWithOptions(obj Object, opts ...MergeFromOption) Patch { options := &MergeFromOptions{} for _, opt := range opts { opt.ApplyToMergeFrom(options) diff --git a/pkg/controller/controllerutil/controllerutil.go b/pkg/controller/controllerutil/controllerutil.go index 462781bd37..07bf8e632d 100644 --- a/pkg/controller/controllerutil/controllerutil.go +++ b/pkg/controller/controllerutil/controllerutil.go @@ -249,8 +249,8 @@ func CreateOrPatch(ctx context.Context, c client.Client, obj client.Object, f Mu } // Create patches for the object and its possible status. - objPatch := client.MergeFrom(obj.DeepCopyObject()) - statusPatch := client.MergeFrom(obj.DeepCopyObject()) + objPatch := client.MergeFrom(obj.DeepCopyObject().(client.Object)) + statusPatch := client.MergeFrom(obj.DeepCopyObject().(client.Object)) // Create a copy of the original object as well as converting that copy to // unstructured data.