Commit 43233caa authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #37871 from Random-Liu/use-patch-in-kubelet

Automatic merge from submit-queue (batch tested with PRs 36692, 37871) Use PatchStatus to update node status in kubelet. Fixes https://github.com/kubernetes/kubernetes/issues/37771. This PR changes kubelet to update node status with `PatchStatus`. @caesarxuchao @ymqytw told me that there is a limitation of current `CreateTwoWayMergePatch`, it doesn't support primitive type slice which uses strategic merge. * I checked the node status, the only primitive type slices in NodeStatus are as follows, they are not using strategic merge: * [`ContainerImage.Names`](https://github.com/kubernetes/kubernetes/blob/master/pkg/api/v1/types.go#L2963) * [`VolumesInUse`](https://github.com/kubernetes/kubernetes/blob/master/pkg/api/v1/types.go#L2909) * Volume package is already [using `CreateStrategicMergePath` to generate node status update patch](https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/volume/attachdetach/statusupdater/node_status_updater.go#L111), and till now everything is fine. @yujuhong @dchen1107 /cc @kubernetes/sig-node
parents e6050f18 beba1ebb
...@@ -91,6 +91,7 @@ func (nsu *nodeStatusUpdater) UpdateNodeStatuses() error { ...@@ -91,6 +91,7 @@ func (nsu *nodeStatusUpdater) UpdateNodeStatuses() error {
clonedNode) clonedNode)
} }
// TODO: Change to pkg/util/node.UpdateNodeStatus.
oldData, err := json.Marshal(node) oldData, err := json.Marshal(node)
if err != nil { if err != nil {
return fmt.Errorf( return fmt.Errorf(
......
...@@ -51,6 +51,7 @@ go_library( ...@@ -51,6 +51,7 @@ go_library(
"//pkg/client/clientset_generated/release_1_5:go_default_library", "//pkg/client/clientset_generated/release_1_5:go_default_library",
"//pkg/client/record:go_default_library", "//pkg/client/record:go_default_library",
"//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider:go_default_library",
"//pkg/conversion:go_default_library",
"//pkg/fieldpath:go_default_library", "//pkg/fieldpath:go_default_library",
"//pkg/fields:go_default_library", "//pkg/fields:go_default_library",
"//pkg/kubelet/api:go_default_library", "//pkg/kubelet/api:go_default_library",
...@@ -192,6 +193,7 @@ go_test( ...@@ -192,6 +193,7 @@ go_test(
"//pkg/util/rand:go_default_library", "//pkg/util/rand:go_default_library",
"//pkg/util/runtime:go_default_library", "//pkg/util/runtime:go_default_library",
"//pkg/util/sets:go_default_library", "//pkg/util/sets:go_default_library",
"//pkg/util/strategicpatch:go_default_library",
"//pkg/util/testing:go_default_library", "//pkg/util/testing:go_default_library",
"//pkg/util/uuid:go_default_library", "//pkg/util/uuid:go_default_library",
"//pkg/util/wait:go_default_library", "//pkg/util/wait:go_default_library",
......
...@@ -32,11 +32,14 @@ import ( ...@@ -32,11 +32,14 @@ import (
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1" metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/kubelet/cadvisor" "k8s.io/kubernetes/pkg/kubelet/cadvisor"
"k8s.io/kubernetes/pkg/kubelet/events" "k8s.io/kubernetes/pkg/kubelet/events"
"k8s.io/kubernetes/pkg/kubelet/util/sliceutils" "k8s.io/kubernetes/pkg/kubelet/util/sliceutils"
"k8s.io/kubernetes/pkg/types"
utilnet "k8s.io/kubernetes/pkg/util/net" utilnet "k8s.io/kubernetes/pkg/util/net"
nodeutil "k8s.io/kubernetes/pkg/util/node"
"k8s.io/kubernetes/pkg/version" "k8s.io/kubernetes/pkg/version"
"k8s.io/kubernetes/pkg/volume/util/volumehelper" "k8s.io/kubernetes/pkg/volume/util/volumehelper"
) )
...@@ -110,6 +113,18 @@ func (kl *Kubelet) tryRegisterWithApiServer(node *v1.Node) bool { ...@@ -110,6 +113,18 @@ func (kl *Kubelet) tryRegisterWithApiServer(node *v1.Node) bool {
return false return false
} }
clonedNode, err := conversion.NewCloner().DeepCopy(existingNode)
if err != nil {
glog.Errorf("Unable to clone %q node object %#v: %v", kl.nodeName, existingNode, err)
return false
}
originalNode, ok := clonedNode.(*v1.Node)
if !ok || originalNode == nil {
glog.Errorf("Unable to cast %q node object %#v to v1.Node", kl.nodeName, clonedNode)
return false
}
if existingNode.Spec.ExternalID == node.Spec.ExternalID { if existingNode.Spec.ExternalID == node.Spec.ExternalID {
glog.Infof("Node %s was previously registered", kl.nodeName) glog.Infof("Node %s was previously registered", kl.nodeName)
...@@ -118,7 +133,8 @@ func (kl *Kubelet) tryRegisterWithApiServer(node *v1.Node) bool { ...@@ -118,7 +133,8 @@ func (kl *Kubelet) tryRegisterWithApiServer(node *v1.Node) bool {
// annotation. // annotation.
requiresUpdate := kl.reconcileCMADAnnotationWithExistingNode(node, existingNode) requiresUpdate := kl.reconcileCMADAnnotationWithExistingNode(node, existingNode)
if requiresUpdate { if requiresUpdate {
if _, err := kl.kubeClient.Core().Nodes().UpdateStatus(existingNode); err != nil { if _, err := nodeutil.PatchNodeStatus(kl.kubeClient, types.NodeName(kl.nodeName),
originalNode, existingNode); err != nil {
glog.Errorf("Unable to reconcile node %q with API server: error updating node: %v", kl.nodeName, err) glog.Errorf("Unable to reconcile node %q with API server: error updating node: %v", kl.nodeName, err)
return false return false
} }
...@@ -347,20 +363,30 @@ func (kl *Kubelet) tryUpdateNodeStatus(tryNumber int) error { ...@@ -347,20 +363,30 @@ func (kl *Kubelet) tryUpdateNodeStatus(tryNumber int) error {
} }
node := &nodes.Items[0] node := &nodes.Items[0]
clonedNode, err := conversion.NewCloner().DeepCopy(node)
if err != nil {
return fmt.Errorf("error clone node %q: %v", kl.nodeName, err)
}
originalNode, ok := clonedNode.(*v1.Node)
if !ok || originalNode == nil {
return fmt.Errorf("failed to cast %q node object %#v to v1.Node", kl.nodeName, clonedNode)
}
kl.updatePodCIDR(node.Spec.PodCIDR) kl.updatePodCIDR(node.Spec.PodCIDR)
if err := kl.setNodeStatus(node); err != nil { if err := kl.setNodeStatus(node); err != nil {
return err return err
} }
// Update the current status on the API server // Patch the current status on the API server
updatedNode, err := kl.kubeClient.Core().Nodes().UpdateStatus(node) updatedNode, err := nodeutil.PatchNodeStatus(kl.kubeClient, types.NodeName(kl.nodeName), originalNode, node)
if err != nil {
return err
}
// If update finishes sucessfully, mark the volumeInUse as reportedInUse to indicate // If update finishes sucessfully, mark the volumeInUse as reportedInUse to indicate
// those volumes are already updated in the node's status // those volumes are already updated in the node's status
if err == nil { kl.volumeManager.MarkVolumesAsReportedInUse(updatedNode.Status.VolumesInUse)
kl.volumeManager.MarkVolumesAsReportedInUse( return nil
updatedNode.Status.VolumesInUse)
}
return err
} }
// recordNodeStatusEvent records an event of the given type with the given // recordNodeStatusEvent records an event of the given type with the given
......
...@@ -20,6 +20,7 @@ go_library( ...@@ -20,6 +20,7 @@ go_library(
"//pkg/apis/meta/v1:go_default_library", "//pkg/apis/meta/v1:go_default_library",
"//pkg/client/clientset_generated/release_1_5:go_default_library", "//pkg/client/clientset_generated/release_1_5:go_default_library",
"//pkg/types:go_default_library", "//pkg/types:go_default_library",
"//pkg/util/strategicpatch:go_default_library",
"//vendor:github.com/golang/glog", "//vendor:github.com/golang/glog",
], ],
) )
......
...@@ -30,6 +30,7 @@ import ( ...@@ -30,6 +30,7 @@ import (
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1" metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
"k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/strategicpatch"
) )
const ( const (
...@@ -153,3 +154,32 @@ func SetNodeCondition(c clientset.Interface, node types.NodeName, condition v1.N ...@@ -153,3 +154,32 @@ func SetNodeCondition(c clientset.Interface, node types.NodeName, condition v1.N
_, err = c.Core().Nodes().PatchStatus(string(node), patch) _, err = c.Core().Nodes().PatchStatus(string(node), patch)
return err return err
} }
// PatchNodeStatus patches node status.
func PatchNodeStatus(c clientset.Interface, nodeName types.NodeName, oldNode *v1.Node, newNode *v1.Node) (*v1.Node, error) {
oldData, err := json.Marshal(oldNode)
if err != nil {
return nil, fmt.Errorf("failed to marshal old node %#v for node %q: %v", oldNode, nodeName, err)
}
// Reset spec to make sure only patch for Status or ObjectMeta is generated.
// Note that we don't reset ObjectMeta here, because:
// 1. This aligns with Nodes().UpdateStatus().
// 2. Some component does use this to update node annotations.
newNode.Spec = oldNode.Spec
newData, err := json.Marshal(newNode)
if err != nil {
return nil, fmt.Errorf("failed to marshal new node %#v for node %q: %v", newNode, nodeName, err)
}
patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Node{})
if err != nil {
return nil, fmt.Errorf("failed to create patch for node %q: %v", nodeName, err)
}
updatedNode, err := c.Core().Nodes().Patch(string(nodeName), api.StrategicMergePatchType, patchBytes, "status")
if err != nil {
return nil, fmt.Errorf("failed to patch status %q for node %q: %v", patchBytes, nodeName, err)
}
return updatedNode, nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment