Merge pull request #3364 from whitewindmills/patch-util

use patch helper instead of jsonpatch
This commit is contained in:
karmada-bot 2023-04-10 19:10:58 +08:00 committed by GitHub
commit 9b4c9a8222
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 49 deletions

View File

@ -8,7 +8,6 @@ import (
"reflect" "reflect"
"time" "time"
jsonpatch "github.com/evanphx/json-patch/v5"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
@ -505,19 +504,11 @@ func (s *Scheduler) patchScheduleResultForResourceBinding(oldBinding *workv1alph
newBinding.Annotations[util.PolicyPlacementAnnotation] = placement newBinding.Annotations[util.PolicyPlacementAnnotation] = placement
newBinding.Spec.Clusters = scheduleResult newBinding.Spec.Clusters = scheduleResult
oldData, err := json.Marshal(oldBinding) patchBytes, err := helper.GenMergePatch(oldBinding, newBinding)
if err != nil { if err != nil {
return fmt.Errorf("failed to marshal the existing resource binding(%s/%s): %v", oldBinding.Namespace, oldBinding.Name, err) return err
} }
newData, err := json.Marshal(newBinding) if len(patchBytes) == 0 {
if err != nil {
return fmt.Errorf("failed to marshal the new resource binding(%s/%s): %v", newBinding.Namespace, newBinding.Name, err)
}
patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData)
if err != nil {
return fmt.Errorf("failed to create a merge patch: %v", err)
}
if "{}" == string(patchBytes) {
return nil return nil
} }
@ -649,19 +640,11 @@ func (s *Scheduler) patchScheduleResultForClusterResourceBinding(oldBinding *wor
newBinding.Annotations[util.PolicyPlacementAnnotation] = placement newBinding.Annotations[util.PolicyPlacementAnnotation] = placement
newBinding.Spec.Clusters = scheduleResult newBinding.Spec.Clusters = scheduleResult
oldData, err := json.Marshal(oldBinding) patchBytes, err := helper.GenMergePatch(oldBinding, newBinding)
if err != nil { if err != nil {
return fmt.Errorf("failed to marshal the existing cluster resource binding(%s): %v", oldBinding.Name, err) return err
} }
newData, err := json.Marshal(newBinding) if len(patchBytes) == 0 {
if err != nil {
return fmt.Errorf("failed to marshal the new resource binding(%s): %v", newBinding.Name, err)
}
patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData)
if err != nil {
return fmt.Errorf("failed to create a merge patch: %v", err)
}
if "{}" == string(patchBytes) {
return nil return nil
} }
@ -727,27 +710,32 @@ func patchBindingStatusCondition(karmadaClient karmadaclientset.Interface, rb *w
if newScheduledCondition.Status == metav1.ConditionTrue { if newScheduledCondition.Status == metav1.ConditionTrue {
updateRB.Status.SchedulerObservedGeneration = rb.Generation updateRB.Status.SchedulerObservedGeneration = rb.Generation
} }
if reflect.DeepEqual(rb.Status, updateRB.Status) {
return nil
}
return patchBindingStatus(karmadaClient, rb, updateRB) return patchBindingStatus(karmadaClient, rb, updateRB)
} }
// patchBindingStatusWithAffinityName patches schedule status with affinityName of ResourceBinding when necessary. // patchBindingStatusWithAffinityName patches schedule status with affinityName of ResourceBinding when necessary.
func patchBindingStatusWithAffinityName(karmadaClient karmadaclientset.Interface, rb *workv1alpha2.ResourceBinding, affinityName string) error { func patchBindingStatusWithAffinityName(karmadaClient karmadaclientset.Interface, rb *workv1alpha2.ResourceBinding, affinityName string) error {
klog.V(4).Infof("Begin to patch status with affinityName(%s) to ResourceBinding(%s/%s).", affinityName, rb.Namespace, rb.Name) if rb.Status.SchedulerObservedAffinityName == affinityName {
return nil
}
klog.V(4).Infof("Begin to patch status with affinityName(%s) to ResourceBinding(%s/%s).", affinityName, rb.Namespace, rb.Name)
updateRB := rb.DeepCopy() updateRB := rb.DeepCopy()
updateRB.Status.SchedulerObservedAffinityName = affinityName updateRB.Status.SchedulerObservedAffinityName = affinityName
return patchBindingStatus(karmadaClient, rb, updateRB) return patchBindingStatus(karmadaClient, rb, updateRB)
} }
func patchBindingStatus(karmadaClient karmadaclientset.Interface, rb, updateRB *workv1alpha2.ResourceBinding) error { func patchBindingStatus(karmadaClient karmadaclientset.Interface, rb, updateRB *workv1alpha2.ResourceBinding) error {
// Short path, ignore patch if no change.
if reflect.DeepEqual(rb.Status, updateRB.Status) {
return nil
}
patchBytes, err := helper.GenMergePatch(rb, updateRB) patchBytes, err := helper.GenMergePatch(rb, updateRB)
if err != nil { if err != nil {
return fmt.Errorf("failed to create a merge patch: %v", err) return err
}
if len(patchBytes) == 0 {
return nil
} }
_, err = karmadaClient.WorkV1alpha2().ResourceBindings(rb.Namespace).Patch(context.TODO(), rb.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}, "status") _, err = karmadaClient.WorkV1alpha2().ResourceBindings(rb.Namespace).Patch(context.TODO(), rb.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}, "status")
@ -771,11 +759,19 @@ func patchClusterBindingStatusCondition(karmadaClient karmadaclientset.Interface
if newScheduledCondition.Status == metav1.ConditionTrue { if newScheduledCondition.Status == metav1.ConditionTrue {
updateCRB.Status.SchedulerObservedGeneration = crb.Generation updateCRB.Status.SchedulerObservedGeneration = crb.Generation
} }
if reflect.DeepEqual(crb.Status, updateCRB.Status) {
return nil
}
return patchClusterResourceBindingStatus(karmadaClient, crb, updateCRB) return patchClusterResourceBindingStatus(karmadaClient, crb, updateCRB)
} }
// patchClusterBindingStatusWithAffinityName patches schedule status with affinityName of ClusterResourceBinding when necessary. // patchClusterBindingStatusWithAffinityName patches schedule status with affinityName of ClusterResourceBinding when necessary.
func patchClusterBindingStatusWithAffinityName(karmadaClient karmadaclientset.Interface, crb *workv1alpha2.ClusterResourceBinding, affinityName string) error { func patchClusterBindingStatusWithAffinityName(karmadaClient karmadaclientset.Interface, crb *workv1alpha2.ClusterResourceBinding, affinityName string) error {
if crb.Status.SchedulerObservedAffinityName == affinityName {
return nil
}
klog.V(4).Infof("Begin to patch status with affinityName(%s) to ClusterResourceBinding(%s).", affinityName, crb.Name) klog.V(4).Infof("Begin to patch status with affinityName(%s) to ClusterResourceBinding(%s).", affinityName, crb.Name)
updateCRB := crb.DeepCopy() updateCRB := crb.DeepCopy()
updateCRB.Status.SchedulerObservedAffinityName = affinityName updateCRB.Status.SchedulerObservedAffinityName = affinityName
@ -783,14 +779,12 @@ func patchClusterBindingStatusWithAffinityName(karmadaClient karmadaclientset.In
} }
func patchClusterResourceBindingStatus(karmadaClient karmadaclientset.Interface, crb, updateCRB *workv1alpha2.ClusterResourceBinding) error { func patchClusterResourceBindingStatus(karmadaClient karmadaclientset.Interface, crb, updateCRB *workv1alpha2.ClusterResourceBinding) error {
// Short path, ignore patch if no change.
if reflect.DeepEqual(crb.Status, updateCRB.Status) {
return nil
}
patchBytes, err := helper.GenMergePatch(crb, updateCRB) patchBytes, err := helper.GenMergePatch(crb, updateCRB)
if err != nil { if err != nil {
return fmt.Errorf("failed to create a merge patch: %v", err) return err
}
if len(patchBytes) == 0 {
return nil
} }
_, err = karmadaClient.WorkV1alpha2().ClusterResourceBindings().Patch(context.TODO(), crb.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}, "status") _, err = karmadaClient.WorkV1alpha2().ClusterResourceBindings().Patch(context.TODO(), crb.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}, "status")

View File

@ -11,7 +11,7 @@ import (
// original object to the modified object. // original object to the modified object.
// The merge patch format is primarily intended for use with the HTTP PATCH method // The merge patch format is primarily intended for use with the HTTP PATCH method
// as a means of describing a set of modifications to a target resource's content. // as a means of describing a set of modifications to a target resource's content.
func GenMergePatch(originalObj interface{}, modifiedObj interface{}) (patchBytes []byte, err error) { func GenMergePatch(originalObj interface{}, modifiedObj interface{}) ([]byte, error) {
originalBytes, err := json.Marshal(originalObj) originalBytes, err := json.Marshal(originalObj)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to marshal original object: %w", err) return nil, fmt.Errorf("failed to marshal original object: %w", err)
@ -20,10 +20,15 @@ func GenMergePatch(originalObj interface{}, modifiedObj interface{}) (patchBytes
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to marshal modified object: %w", err) return nil, fmt.Errorf("failed to marshal modified object: %w", err)
} }
patchBytes, err = jsonpatch.CreateMergePatch(originalBytes, modifiedBytes) patchBytes, err := jsonpatch.CreateMergePatch(originalBytes, modifiedBytes)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create a merge patch: %w", err) return nil, fmt.Errorf("failed to create a merge patch: %w", err)
} }
// It's unnecessary to patch.
if string(patchBytes) == "{}" {
return nil, nil
}
return patchBytes, nil return patchBytes, nil
} }

View File

@ -9,7 +9,7 @@ import (
) )
func TestGenMergePatch(t *testing.T) { func TestGenMergePatch(t *testing.T) {
testObj := workv1alpha2.ResourceBinding{ testObj := &workv1alpha2.ResourceBinding{
TypeMeta: metav1.TypeMeta{Kind: "ResourceBinding", APIVersion: "work.karmada.io/v1alpha2"}, TypeMeta: metav1.TypeMeta{Kind: "ResourceBinding", APIVersion: "work.karmada.io/v1alpha2"},
ObjectMeta: metav1.ObjectMeta{Name: "foo"}, ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: workv1alpha2.ResourceBindingSpec{Clusters: []workv1alpha2.TargetCluster{{Name: "foo", Replicas: 20}}}, Spec: workv1alpha2.ResourceBindingSpec{Clusters: []workv1alpha2.TargetCluster{{Name: "foo", Replicas: 20}}},
@ -68,7 +68,7 @@ func TestGenMergePatch(t *testing.T) {
modified := testObj.DeepCopy() modified := testObj.DeepCopy()
return modified return modified
}, },
expectedPatch: `{}`, expectedPatch: "",
expectErr: false, expectErr: false,
}, },
{ {
@ -77,22 +77,31 @@ func TestGenMergePatch(t *testing.T) {
var invalid = 0 var invalid = 0
return invalid return invalid
}, },
expectedPatch: ``, // empty(nil) patch expectedPatch: "",
expectErr: true, expectErr: true,
}, },
{
name: "update to empty annotations",
modifyFunc: func() interface{} {
modified := testObj.DeepCopy()
modified.Annotations = make(map[string]string, 0)
return modified
},
expectedPatch: "",
expectErr: false,
},
} }
for _, test := range tests { for _, tt := range tests {
tc := test t.Run(tt.name, func(t *testing.T) {
t.Run(test.name, func(t *testing.T) { patch, err := GenMergePatch(testObj, tt.modifyFunc())
patch, err := GenMergePatch(testObj, tc.modifyFunc()) if err != nil && tt.expectErr == false {
if err != nil && tc.expectErr == false {
t.Fatalf("unexpect error, but got: %v", err) t.Fatalf("unexpect error, but got: %v", err)
} else if err == nil && tc.expectErr == true { } else if err == nil && tt.expectErr == true {
t.Fatalf("expect error, but got none") t.Fatalf("expect error, but got none")
} }
if string(patch) != tc.expectedPatch { if string(patch) != tt.expectedPatch {
t.Fatalf("want patch: %s, but got :%s", tc.expectedPatch, string(patch)) t.Fatalf("want patch: %s, but got :%s", tt.expectedPatch, string(patch))
} }
}) })
} }