Skip to content

Commit

Permalink
Merge pull request karmada-io#4094 from ctripcloud/fix-binding-status…
Browse files Browse the repository at this point in the history
…-error-rate

Use Patch() when binding-status controller update workload's status
  • Loading branch information
karmada-bot authored Dec 9, 2023
2 parents 9bebd4c + d423206 commit 33fdf52
Show file tree
Hide file tree
Showing 3 changed files with 349 additions and 3 deletions.
19 changes: 16 additions & 3 deletions pkg/controllers/status/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/dynamic"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/builder"
Expand All @@ -34,6 +35,7 @@ import (
workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
"github.com/karmada-io/karmada/pkg/resourceinterpreter"
"github.com/karmada-io/karmada/pkg/util/helper"
"github.com/karmada-io/karmada/pkg/util/restmapper"
)

Expand Down Expand Up @@ -119,13 +121,24 @@ func updateResourceStatus(
klog.Errorf("Failed to aggregate status for resource(%s/%s/%s, Error: %v", gvr, resource.GetNamespace(), resource.GetName(), err)
return err
}
if reflect.DeepEqual(resource, newObj) {

oldStatus, _, _ := unstructured.NestedFieldNoCopy(resource.Object, "status")
newStatus, _, _ := unstructured.NestedFieldNoCopy(newObj.Object, "status")
if reflect.DeepEqual(oldStatus, newStatus) {
klog.V(3).Infof("Ignore update resource(%s/%s/%s) status as up to date.", gvr, resource.GetNamespace(), resource.GetName())
return nil
}

if _, err = dynamicClient.Resource(gvr).Namespace(resource.GetNamespace()).UpdateStatus(context.TODO(), newObj, metav1.UpdateOptions{}); err != nil {
klog.Errorf("Failed to update resource(%s/%s/%s), Error: %v", gvr, resource.GetNamespace(), resource.GetName(), err)
patchBytes, err := helper.GenReplaceFieldJSONPatch("/status", oldStatus, newStatus)
if err != nil {
klog.Errorf("Failed to gen patch bytes for resource(%s/%s/%s, Error: %v", gvr, resource.GetNamespace(), resource.GetName(), err)
return err
}

_, err = dynamicClient.Resource(gvr).Namespace(resource.GetNamespace()).
Patch(context.TODO(), resource.GetName(), types.JSONPatchType, patchBytes, metav1.PatchOptions{}, "status")
if err != nil {
klog.Error("Failed to update resource(%s/%s/%s), Error: %v", gvr, resource.GetNamespace(), resource.GetName(), err)
return err
}
klog.V(3).Infof("Update resource(%s/%s/%s) status successfully.", gvr, resource.GetNamespace(), resource.GetName())
Expand Down
55 changes: 55 additions & 0 deletions pkg/util/helper/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,28 @@ package helper
import (
"encoding/json"
"fmt"
"reflect"

jsonpatch "github.com/evanphx/json-patch/v5"
)

// RFC6902 JSONPatch operations
const (
JSONPatchOPAdd = "add"
JSONPatchOPReplace = "replace"
JSONPatchOPRemove = "remove"
JSONPatchOPMove = "move"
JSONPatchOPCopy = "copy"
JSONPatchOPTest = "test"
)

type jsonPatch struct {
OP string `json:"op"`
From string `json:"from,omitempty"`
Path string `json:"path"`
Value interface{} `json:"value,omitempty"`
}

// GenMergePatch will return a merge patch document capable of converting the
// original object to the modified object.
// The merge patch format is primarily intended for use with the HTTP PATCH method
Expand All @@ -48,3 +66,40 @@ func GenMergePatch(originalObj interface{}, modifiedObj interface{}) ([]byte, er

return patchBytes, nil
}

// GenReplaceFieldJSONPatch returns the RFC6902 JSONPatch array as []byte, which is used to simply
// add/replace/delete certain JSON **Object** field.
func GenReplaceFieldJSONPatch(path string, originalFieldValue, newFieldValue interface{}) ([]byte, error) {
if reflect.DeepEqual(originalFieldValue, newFieldValue) {
return nil, nil
}
if newFieldValue == nil {
return GenJSONPatch(JSONPatchOPRemove, "", path, nil)
}
// The implementation of "add" and "replace" for JSON objects is actually the same
// in "github.com/evanphx/json-patch/v5", which is used by Karmada and K8s.
// We implemented it here just to follow the RFC6902.
if originalFieldValue == nil {
return GenJSONPatch(JSONPatchOPAdd, "", path, newFieldValue)
}
return GenJSONPatch(JSONPatchOPReplace, "", path, newFieldValue)
}

// GenJSONPatch return JSONPatch array as []byte according to RFC6902
func GenJSONPatch(op, from, path string, value interface{}) ([]byte, error) {
jp := jsonPatch{
OP: op,
Path: path,
}
switch op {
case JSONPatchOPAdd, JSONPatchOPReplace, JSONPatchOPTest:
jp.Value = value
case JSONPatchOPMove, JSONPatchOPCopy:
jp.From = from
case JSONPatchOPRemove:
default:
return nil, fmt.Errorf("unrecognized JSONPatch OP: %s", op)
}

return json.Marshal([]jsonPatch{jp})
}
Loading

0 comments on commit 33fdf52

Please sign in to comment.