From 74a498fed45df93bcf251255ab7558014294d18c Mon Sep 17 00:00:00 2001 From: zhangzujian Date: Fri, 28 Jun 2024 06:37:44 +0000 Subject: [PATCH] update node labels/annotations by json merge patch Signed-off-by: zhangzujian --- cmd/daemon/cniserver.go | 36 ++------- pkg/controller/external_gw.go | 50 +++---------- pkg/controller/node.go | 37 +++------ pkg/daemon/controller.go | 87 ++++++++-------------- pkg/daemon/ovs_linux.go | 32 +++----- pkg/ovn_ic_controller/ovn_ic_controller.go | 50 +++---------- pkg/util/k8s.go | 36 +++++++++ 7 files changed, 115 insertions(+), 213 deletions(-) diff --git a/cmd/daemon/cniserver.go b/cmd/daemon/cniserver.go index 22a1425c886..448ea42da7b 100644 --- a/cmd/daemon/cniserver.go +++ b/cmd/daemon/cniserver.go @@ -1,8 +1,6 @@ package daemon import ( - "context" - "encoding/json" "fmt" "net/http" "net/http/pprof" @@ -13,7 +11,6 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" kubeinformers "k8s.io/client-go/informers" "k8s.io/klog/v2" "k8s.io/sample-controller/pkg/signals" @@ -159,41 +156,18 @@ func initChassisAnno(cfg *daemon.Configuration) error { return err } - hostname := cfg.NodeName - node, err := cfg.KubeClient.CoreV1().Nodes().Get(context.Background(), hostname, v1.GetOptions{}) - if err != nil { - klog.Errorf("failed to get node %s %v", hostname, err) - return err - } - - chassistr := string(chassisID) - chassesName := strings.TrimSpace(chassistr) + chassesName := strings.TrimSpace(string(chassisID)) if chassesName == "" { // not ready yet err = fmt.Errorf("chassis id is empty") klog.Error(err) return err } - if annoChassesName, ok := node.Annotations[util.ChassisAnnotation]; ok { - if annoChassesName == chassesName { - return nil - } - klog.Infof("chassis id changed, old: %s, new: %s", annoChassesName, chassesName) - } - node.Annotations[util.ChassisAnnotation] = chassesName - patchPayloadTemplate := `[{ - "op": "%s", - "path": "/metadata/annotations", - "value": %s - }]` - op := "add" - raw, _ := json.Marshal(node.Annotations) - patchPayload := fmt.Sprintf(patchPayloadTemplate, op, raw) - _, err = cfg.KubeClient.CoreV1().Nodes().Patch(context.Background(), hostname, types.JSONPatchType, []byte(patchPayload), v1.PatchOptions{}, "") - if err != nil { - klog.Errorf("patch node %s failed %v", hostname, err) + annotations := map[string]any{util.ChassisAnnotation: chassesName} + if err = util.UpdateNodeAnnotations(cfg.KubeClient.CoreV1().Nodes(), cfg.NodeName, annotations); err != nil { + klog.Errorf("failed to update chassis annotation of node %s: %v", cfg.NodeName, err) return err } - klog.Infof("finish adding chassis annotation") + return nil } diff --git a/pkg/controller/external_gw.go b/pkg/controller/external_gw.go index c17f4d9e03e..32667597d6f 100644 --- a/pkg/controller/external_gw.go +++ b/pkg/controller/external_gw.go @@ -2,7 +2,6 @@ package controller import ( "context" - "encoding/json" "fmt" "reflect" "slices" @@ -82,25 +81,11 @@ func (c *Controller) removeExternalGateway() error { klog.Errorf("failed to list external gw nodes, %v", err) return err } - for _, cachedNode := range nodes { - no := cachedNode.DeepCopy() - patchPayloadTemplate := `[{ - "op": "%s", - "path": "/metadata/labels", - "value": %s - }]` - op := "replace" - if len(no.Labels) == 0 { - op = "add" - } - if no.Labels[util.ExGatewayLabel] != "false" { - no.Labels[util.ExGatewayLabel] = "false" - raw, _ := json.Marshal(no.Labels) - patchPayload := fmt.Sprintf(patchPayloadTemplate, op, raw) - if _, err = c.config.KubeClient.CoreV1().Nodes().Patch(context.Background(), no.Name, types.JSONPatchType, []byte(patchPayload), metav1.PatchOptions{}, ""); err != nil { - klog.Errorf("failed to patch external gw node %s, %v", no.Name, err) - return err - } + for _, node := range nodes { + labels := map[string]any{util.ExGatewayLabel: "false"} + if err = util.UpdateNodeLabels(c.config.KubeClient.CoreV1().Nodes(), node.Name, labels); err != nil { + klog.Errorf("failed to patch external gw node %s: %v", node.Name, err) + return err } } @@ -243,30 +228,17 @@ func (c *Controller) getGatewayChassis(config map[string]string) ([]string, erro } } for _, gw := range gwNodes { - cachedNode, err := c.nodesLister.Get(gw) + node, err := c.nodesLister.Get(gw) if err != nil { klog.Errorf("failed to get gw node %s, %v", gw, err) return nil, err } - node := cachedNode.DeepCopy() - patchPayloadTemplate := `[{ - "op": "%s", - "path": "/metadata/labels", - "value": %s - }]` - op := "replace" - if len(node.Labels) == 0 { - op = "add" - } - if node.Labels[util.ExGatewayLabel] != "true" { - node.Labels[util.ExGatewayLabel] = "true" - raw, _ := json.Marshal(node.Labels) - patchPayload := fmt.Sprintf(patchPayloadTemplate, op, raw) - if _, err = c.config.KubeClient.CoreV1().Nodes().Patch(context.Background(), gw, types.JSONPatchType, []byte(patchPayload), metav1.PatchOptions{}, ""); err != nil { - klog.Errorf("failed to patch external gw node %s, %v", gw, err) - return nil, err - } + labels := map[string]any{util.ExGatewayLabel: "true"} + if err = util.UpdateNodeLabels(c.config.KubeClient.CoreV1().Nodes(), node.Name, labels); err != nil { + klog.Errorf("failed to update annotations of node %s: %v", node.Name, err) + return nil, err } + annoChassisName := node.Annotations[util.ChassisAnnotation] if annoChassisName == "" { err := fmt.Errorf("node %s has no chassis annotation, kube-ovn-cni not ready", gw) diff --git a/pkg/controller/node.go b/pkg/controller/node.go index 377411cfd18..23708e783c3 100644 --- a/pkg/controller/node.go +++ b/pkg/controller/node.go @@ -2,7 +2,6 @@ package controller import ( "context" - "encoding/json" "fmt" "reflect" "slices" @@ -316,29 +315,17 @@ func (c *Controller) handleAddNode(key string) error { return err } - patchPayloadTemplate := `[{ - "op": "%s", - "path": "/metadata/annotations", - "value": %s - }]` - op := "replace" - if len(node.Annotations) == 0 { - node.Annotations = map[string]string{} - op = "add" - } - - node.Annotations[util.IPAddressAnnotation] = ipStr - node.Annotations[util.MacAddressAnnotation] = mac - node.Annotations[util.CidrAnnotation] = subnet.Spec.CIDRBlock - node.Annotations[util.GatewayAnnotation] = subnet.Spec.Gateway - node.Annotations[util.LogicalSwitchAnnotation] = c.config.NodeSwitch - node.Annotations[util.AllocatedAnnotation] = "true" - node.Annotations[util.PortNameAnnotation] = portName - raw, _ := json.Marshal(node.Annotations) - patchPayload := fmt.Sprintf(patchPayloadTemplate, op, raw) - _, err = c.config.KubeClient.CoreV1().Nodes().Patch(context.Background(), key, types.JSONPatchType, []byte(patchPayload), metav1.PatchOptions{}, "") - if err != nil { - klog.Errorf("patch node %s failed: %v", key, err) + annotations := map[string]any{ + util.IPAddressAnnotation: ipStr, + util.MacAddressAnnotation: mac, + util.CidrAnnotation: subnet.Spec.CIDRBlock, + util.GatewayAnnotation: subnet.Spec.Gateway, + util.LogicalSwitchAnnotation: c.config.NodeSwitch, + util.AllocatedAnnotation: "true", + util.PortNameAnnotation: portName, + } + if err = util.UpdateNodeAnnotations(c.config.KubeClient.CoreV1().Nodes(), node.Name, annotations); err != nil { + klog.Errorf("failed to update annotations of node %s: %v", node.Name, err) return err } @@ -364,7 +351,7 @@ func (c *Controller) handleAddNode(key string) error { } // ovn acl doesn't support address_set name with '-', so replace '-' by '.' - pgName := strings.ReplaceAll(node.Annotations[util.PortNameAnnotation], "-", ".") + pgName := strings.ReplaceAll(portName, "-", ".") if err = c.OVNNbClient.CreatePortGroup(pgName, map[string]string{networkPolicyKey: "node" + "/" + key}); err != nil { klog.Errorf("create port group %s for node %s: %v", pgName, key, err) return err diff --git a/pkg/daemon/controller.go b/pkg/daemon/controller.go index 7404d6dc218..36881f4509a 100644 --- a/pkg/daemon/controller.go +++ b/pkg/daemon/controller.go @@ -2,7 +2,6 @@ package daemon import ( "context" - "encoding/json" "fmt" "os/exec" "slices" @@ -275,43 +274,30 @@ func (c *Controller) initProviderNetwork(pn *kubeovnv1.ProviderNetwork, node *v1 } } + labels := map[string]any{ + fmt.Sprintf(util.ProviderNetworkReadyTemplate, pn.Name): nil, + fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, pn.Name): nil, + fmt.Sprintf(util.ProviderNetworkMtuTemplate, pn.Name): nil, + fmt.Sprintf(util.ProviderNetworkExcludeTemplate, pn.Name): nil, + } + var mtu int var err error klog.V(3).Infof("ovs init provider network %s", pn.Name) if mtu, err = c.ovsInitProviderNetwork(pn.Name, nic, pn.Spec.ExchangeLinkName, c.config.MacLearningFallback); err != nil { - if oldLen := len(node.Labels); oldLen != 0 { - delete(node.Labels, fmt.Sprintf(util.ProviderNetworkReadyTemplate, pn.Name)) - delete(node.Labels, fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, pn.Name)) - delete(node.Labels, fmt.Sprintf(util.ProviderNetworkMtuTemplate, pn.Name)) - if len(node.Labels) != oldLen { - raw, _ := json.Marshal(node.Labels) - patchPayload := fmt.Sprintf(`[{ "op": "replace", "path": "/metadata/labels", "value": %s }]`, raw) - _, err1 := c.config.KubeClient.CoreV1().Nodes().Patch(context.Background(), node.Name, types.JSONPatchType, []byte(patchPayload), metav1.PatchOptions{}) - if err1 != nil { - klog.Errorf("failed to patch node %s: %v", node.Name, err1) - } - } + delete(labels, fmt.Sprintf(util.ProviderNetworkExcludeTemplate, pn.Name)) + if err1 := util.UpdateNodeLabels(c.config.KubeClient.CoreV1().Nodes(), node.Name, labels); err1 != nil { + klog.Errorf("failed to update annotations of node %s: %v", node.Name, err1) } c.recordProviderNetworkErr(pn.Name, err.Error()) return err } - delete(node.Labels, fmt.Sprintf(util.ProviderNetworkExcludeTemplate, pn.Name)) - node.Labels[fmt.Sprintf(util.ProviderNetworkReadyTemplate, pn.Name)] = "true" - node.Labels[fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, pn.Name)] = nic - node.Labels[fmt.Sprintf(util.ProviderNetworkMtuTemplate, pn.Name)] = strconv.Itoa(mtu) - - patchPayloadTemplate := `[{ "op": "%s", "path": "/metadata/labels", "value": %s }]` - op := "replace" - if len(node.Labels) == 0 { - op = "add" - } - - raw, _ := json.Marshal(node.Labels) - patchPayload := fmt.Sprintf(patchPayloadTemplate, op, raw) - _, err = c.config.KubeClient.CoreV1().Nodes().Patch(context.Background(), node.Name, types.JSONPatchType, []byte(patchPayload), metav1.PatchOptions{}) - if err != nil { - klog.Errorf("failed to patch node %s: %v", node.Name, err) + labels[fmt.Sprintf(util.ProviderNetworkReadyTemplate, pn.Name)] = "true" + labels[fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, pn.Name)] = nic + labels[fmt.Sprintf(util.ProviderNetworkMtuTemplate, pn.Name)] = strconv.Itoa(mtu) + if err = util.UpdateNodeLabels(c.config.KubeClient.CoreV1().Nodes(), node.Name, labels); err != nil { + klog.Errorf("failed to update labels of node %s: %v", node.Name, err) return err } c.recordProviderNetworkErr(pn.Name, "") @@ -373,22 +359,14 @@ func (c *Controller) recordProviderNetworkErr(providerNetwork, errMsg string) { } func (c *Controller) cleanProviderNetwork(pn *kubeovnv1.ProviderNetwork, node *v1.Node) error { - patchPayloadTemplate := `[{ "op": "%s", "path": "/metadata/labels", "value": %s }]` - op := "replace" - if len(node.Labels) == 0 { - op = "add" - } - - var err error - delete(node.Labels, fmt.Sprintf(util.ProviderNetworkReadyTemplate, pn.Name)) - delete(node.Labels, fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, pn.Name)) - delete(node.Labels, fmt.Sprintf(util.ProviderNetworkMtuTemplate, pn.Name)) - node.Labels[fmt.Sprintf(util.ProviderNetworkExcludeTemplate, pn.Name)] = "true" - raw, _ := json.Marshal(node.Labels) - patchPayload := fmt.Sprintf(patchPayloadTemplate, op, raw) - _, err = c.config.KubeClient.CoreV1().Nodes().Patch(context.Background(), node.Name, types.JSONPatchType, []byte(patchPayload), metav1.PatchOptions{}) - if err != nil { - klog.Errorf("failed to patch node %s: %v", node.Name, err) + labels := map[string]any{ + fmt.Sprintf(util.ProviderNetworkReadyTemplate, pn.Name): nil, + fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, pn.Name): nil, + fmt.Sprintf(util.ProviderNetworkMtuTemplate, pn.Name): nil, + fmt.Sprintf(util.ProviderNetworkExcludeTemplate, pn.Name): "true", + } + if err := util.UpdateNodeLabels(c.config.KubeClient.CoreV1().Nodes(), node.Name, labels); err != nil { + klog.Errorf("failed to update labels of node %s: %v", node.Name, err) return err } @@ -410,17 +388,14 @@ func (c *Controller) handleDeleteProviderNetwork(pn *kubeovnv1.ProviderNetwork) return nil } - newNode := node.DeepCopy() - delete(newNode.Labels, fmt.Sprintf(util.ProviderNetworkReadyTemplate, pn.Name)) - delete(newNode.Labels, fmt.Sprintf(util.ProviderNetworkExcludeTemplate, pn.Name)) - delete(newNode.Labels, fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, pn.Name)) - delete(newNode.Labels, fmt.Sprintf(util.ProviderNetworkMtuTemplate, pn.Name)) - raw, _ := json.Marshal(newNode.Labels) - patchPayloadTemplate := `[{ "op": "replace", "path": "/metadata/labels", "value": %s }]` - patchPayload := fmt.Sprintf(patchPayloadTemplate, raw) - _, err = c.config.KubeClient.CoreV1().Nodes().Patch(context.Background(), node.Name, types.JSONPatchType, []byte(patchPayload), metav1.PatchOptions{}) - if err != nil { - klog.Errorf("failed to patch node %s: %v", node.Name, err) + labels := map[string]any{ + fmt.Sprintf(util.ProviderNetworkReadyTemplate, pn.Name): nil, + fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, pn.Name): nil, + fmt.Sprintf(util.ProviderNetworkMtuTemplate, pn.Name): nil, + fmt.Sprintf(util.ProviderNetworkExcludeTemplate, pn.Name): nil, + } + if err = util.UpdateNodeLabels(c.config.KubeClient.CoreV1().Nodes(), node.Name, labels); err != nil { + klog.Errorf("failed to update labels of node %s: %v", node.Name, err) return err } diff --git a/pkg/daemon/ovs_linux.go b/pkg/daemon/ovs_linux.go index 4aa97c6f372..9e9ab551367 100644 --- a/pkg/daemon/ovs_linux.go +++ b/pkg/daemon/ovs_linux.go @@ -3,7 +3,6 @@ package daemon import ( "bytes" "context" - "encoding/json" "errors" "fmt" "net" @@ -12,6 +11,7 @@ import ( "path" "path/filepath" "regexp" + "strconv" "strings" "syscall" "time" @@ -893,8 +893,8 @@ func (c *Controller) loopOvnExt0Check() { klog.Error(err) return } - if err = c.patchNodeExternalGwLabel(node.Name, false); err != nil { - klog.Errorf("failed to patch label on node %s, %v", node, err) + if err = c.patchNodeExternalGwLabel(false); err != nil { + klog.Errorf("failed to patch labels of node %s: %v", node.Name, err) return } return @@ -946,8 +946,8 @@ func (c *Controller) loopOvnExt0Check() { klog.Errorf("failed to setup ovnext0, %v", err) return } - if err = c.patchNodeExternalGwLabel(portName, true); err != nil { - klog.Errorf("failed to patch label on node %s, %v", node, err) + if err = c.patchNodeExternalGwLabel(true); err != nil { + klog.Errorf("failed to patch labels of node %s: %v", node.Name, err) return } if err = c.patchOvnEipStatus(portName, true); err != nil { @@ -983,31 +983,19 @@ func (c *Controller) patchOvnEipStatus(key string, ready bool) error { return nil } -func (c *Controller) patchNodeExternalGwLabel(key string, enabled bool) error { +func (c *Controller) patchNodeExternalGwLabel(enabled bool) error { node, err := c.nodesLister.Get(c.config.NodeName) if err != nil { klog.Errorf("failed to get node %s: %v", c.config.NodeName, err) return err } - if enabled { - node.Labels[util.NodeExtGwLabel] = "true" - } else { - node.Labels[util.NodeExtGwLabel] = "false" - } - - patchPayloadTemplate := `[{ "op": "%s", "path": "/metadata/labels", "value": %s }]` - op := "replace" - if len(node.Labels) == 0 { - op = "add" - } - - raw, _ := json.Marshal(node.Labels) - patchPayload := fmt.Sprintf(patchPayloadTemplate, op, raw) - if _, err = c.config.KubeClient.CoreV1().Nodes().Patch(context.Background(), key, types.JSONPatchType, []byte(patchPayload), metav1.PatchOptions{}); err != nil { - klog.Errorf("failed to patch node %s: %v", node.Name, err) + labels := map[string]any{util.NodeExtGwLabel: strconv.FormatBool(enabled)} + if err = util.UpdateNodeLabels(c.config.KubeClient.CoreV1().Nodes(), node.Name, labels); err != nil { + klog.Errorf("failed to update labels of node %s: %v", node.Name, err) return err } + return nil } diff --git a/pkg/ovn_ic_controller/ovn_ic_controller.go b/pkg/ovn_ic_controller/ovn_ic_controller.go index f645e8a204b..cccc76e7044 100644 --- a/pkg/ovn_ic_controller/ovn_ic_controller.go +++ b/pkg/ovn_ic_controller/ovn_ic_controller.go @@ -2,7 +2,6 @@ package ovn_ic_controller import ( "context" - "encoding/json" "fmt" "os" "os/exec" @@ -15,7 +14,6 @@ import ( k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/types" "k8s.io/klog/v2" kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1" @@ -243,25 +241,11 @@ func (c *Controller) removeInterConnection(azName string) error { klog.Errorf("failed to list nodes, %v", err) return err } - for _, cachedNode := range nodes { - no := cachedNode.DeepCopy() - patchPayloadTemplate := `[{ - "op": "%s", - "path": "/metadata/labels", - "value": %s - }]` - op := "replace" - if len(no.Labels) == 0 { - op = "add" - } - if no.Labels[util.ICGatewayLabel] != "false" { - no.Labels[util.ICGatewayLabel] = "false" - raw, _ := json.Marshal(no.Labels) - patchPayload := fmt.Sprintf(patchPayloadTemplate, op, raw) - if _, err = c.config.KubeClient.CoreV1().Nodes().Patch(context.Background(), no.Name, types.JSONPatchType, []byte(patchPayload), metav1.PatchOptions{}, ""); err != nil { - klog.Errorf("patch ic gw node %s failed %v", no.Name, err) - return err - } + for _, node := range nodes { + labels := map[string]any{util.ICGatewayLabel: "false"} + if err = util.UpdateNodeLabels(c.config.KubeClient.CoreV1().Nodes(), node.Name, labels); err != nil { + klog.Errorf("failed to patch ic gw node %s: %v", node.Name, err) + return err } } @@ -316,29 +300,15 @@ func (c *Controller) establishInterConnection(config map[string]string) error { } chassises[j] = chassis.Name - cachedNode, err := c.nodesLister.Get(gw) + node, err := c.nodesLister.Get(gw) if err != nil { klog.Errorf("failed to get gw node %q: %v", gw, err) return err } - node := cachedNode.DeepCopy() - patchPayloadTemplate := `[{ - "op": "%s", - "path": "/metadata/labels", - "value": %s - }]` - op := "replace" - if len(node.Labels) == 0 { - op = "add" - } - if node.Labels[util.ICGatewayLabel] != "true" { - node.Labels[util.ICGatewayLabel] = "true" - raw, _ := json.Marshal(node.Labels) - patchPayload := fmt.Sprintf(patchPayloadTemplate, op, raw) - if _, err = c.config.KubeClient.CoreV1().Nodes().Patch(context.Background(), gw, types.JSONPatchType, []byte(patchPayload), metav1.PatchOptions{}, ""); err != nil { - klog.Errorf("failed to patch gw node %q: %v", gw, err) - return err - } + labels := map[string]any{util.ICGatewayLabel: "true"} + if err = util.UpdateNodeLabels(c.config.KubeClient.CoreV1().Nodes(), node.Name, labels); err != nil { + klog.Errorf("failed to patch ic gw node %s: %v", node.Name, err) + return err } } diff --git a/pkg/util/k8s.go b/pkg/util/k8s.go index 261f2176780..1ade1cc1bdc 100644 --- a/pkg/util/k8s.go +++ b/pkg/util/k8s.go @@ -1,6 +1,8 @@ package util import ( + "context" + "encoding/json" "fmt" "net" "net/url" @@ -8,8 +10,11 @@ import ( "time" v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/selection" + "k8s.io/apimachinery/pkg/types" + clientv1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/klog/v2" ) @@ -92,3 +97,34 @@ func LabelSelectorNotEmpty(key string) (labels.Selector, error) { func GetTruncatedUID(uid string) string { return uid[len(uid)-12:] } + +func UpdateNodeLabels(cs clientv1.NodeInterface, node string, labels map[string]any) error { + buf, err := json.Marshal(labels) + if err != nil { + klog.Errorf("failed to marshal labels: %v", err) + return err + } + patch := fmt.Sprintf(`{"metadata":{"labels":%s}}`, string(buf)) + return nodeMergePatch(cs, node, patch) +} + +func UpdateNodeAnnotations(cs clientv1.NodeInterface, node string, annotations map[string]any) error { + buf, err := json.Marshal(annotations) + if err != nil { + klog.Errorf("failed to marshal annotations: %v", err) + return err + } + patch := fmt.Sprintf(`{"metadata":{"annotations":%s}}`, string(buf)) + return nodeMergePatch(cs, node, patch) +} + +// we do not use GenerateMergePatchPayload/GenerateStrategicMergePatchPayload, +// because we use a `null` value to delete a label/annotation +func nodeMergePatch(cs clientv1.NodeInterface, node, patch string) error { + _, err := cs.Patch(context.Background(), node, types.MergePatchType, []byte(patch), metav1.PatchOptions{}) + if err != nil { + klog.Errorf("failed to patch node %s with json merge patch %q: %v", node, patch, err) + return err + } + return nil +}