Skip to content

Commit e8d6d37

Browse files
authored
Merge pull request kubernetes#90561 from cici37/util
Copy patchNodeStatus logic to cloud-provider
2 parents 03a927e + b8bac02 commit e8d6d37

File tree

4 files changed

+143
-3
lines changed

4 files changed

+143
-3
lines changed

pkg/controller/cloud/BUILD

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ go_library(
1010
visibility = ["//visibility:public"],
1111
deps = [
1212
"//pkg/controller:go_default_library",
13-
"//pkg/util/node:go_default_library",
1413
"//staging/src/k8s.io/api/core/v1:go_default_library",
1514
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
1615
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",

pkg/controller/cloud/node_controller.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import (
3939
cloudproviderapi "k8s.io/cloud-provider/api"
4040
cloudnodeutil "k8s.io/cloud-provider/node/helpers"
4141
"k8s.io/klog"
42-
nodeutil "k8s.io/kubernetes/pkg/util/node"
4342
)
4443

4544
// labelReconcileInfo lists Node labels to reconcile, and how to reconcile them.
@@ -280,7 +279,7 @@ func (cnc *CloudNodeController) updateNodeAddress(ctx context.Context, node *v1.
280279
}
281280
newNode := node.DeepCopy()
282281
newNode.Status.Addresses = nodeAddresses
283-
_, _, err = nodeutil.PatchNodeStatus(cnc.kubeClient.CoreV1(), types.NodeName(node.Name), node, newNode)
282+
_, _, err = cloudnodeutil.PatchNodeStatus(cnc.kubeClient.CoreV1(), types.NodeName(node.Name), node, newNode)
284283
if err != nil {
285284
klog.Errorf("Error patching node with cloud ip addresses = [%v]", err)
286285
}

staging/src/k8s.io/cloud-provider/node/helpers/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ go_library(
66
"address.go",
77
"conditions.go",
88
"labels.go",
9+
"status.go",
910
"taints.go",
1011
],
1112
importmap = "k8s.io/kubernetes/vendor/k8s.io/cloud-provider/node/helpers",
@@ -20,6 +21,7 @@ go_library(
2021
"//staging/src/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
2122
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
2223
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
24+
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
2325
"//staging/src/k8s.io/client-go/util/retry:go_default_library",
2426
"//vendor/k8s.io/klog:go_default_library",
2527
],
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
/*
18+
19+
NOTE: the contents of this file has been copied from k8s.io/kubernetes/pkg/util/node. The reason for duplicating this code is to remove
20+
dependencies for cloud controller manager.
21+
*/
22+
23+
package helpers
24+
25+
import (
26+
"context"
27+
"encoding/json"
28+
"fmt"
29+
30+
v1 "k8s.io/api/core/v1"
31+
"k8s.io/apimachinery/pkg/api/equality"
32+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33+
"k8s.io/apimachinery/pkg/types"
34+
"k8s.io/apimachinery/pkg/util/strategicpatch"
35+
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
36+
)
37+
38+
// PatchNodeStatus patches node status.
39+
func PatchNodeStatus(c v1core.CoreV1Interface, nodeName types.NodeName, oldNode *v1.Node, newNode *v1.Node) (*v1.Node, []byte, error) {
40+
patchBytes, err := preparePatchBytesforNodeStatus(nodeName, oldNode, newNode)
41+
if err != nil {
42+
return nil, nil, err
43+
}
44+
45+
updatedNode, err := c.Nodes().Patch(context.TODO(), string(nodeName), types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}, "status")
46+
if err != nil {
47+
return nil, nil, fmt.Errorf("failed to patch status %q for node %q: %v", patchBytes, nodeName, err)
48+
}
49+
return updatedNode, patchBytes, nil
50+
}
51+
52+
func preparePatchBytesforNodeStatus(nodeName types.NodeName, oldNode *v1.Node, newNode *v1.Node) ([]byte, error) {
53+
oldData, err := json.Marshal(oldNode)
54+
if err != nil {
55+
return nil, fmt.Errorf("failed to Marshal oldData for node %q: %v", nodeName, err)
56+
}
57+
58+
// NodeStatus.Addresses is incorrectly annotated as patchStrategy=merge, which
59+
// will cause strategicpatch.CreateTwoWayMergePatch to create an incorrect patch
60+
// if it changed.
61+
manuallyPatchAddresses := (len(oldNode.Status.Addresses) > 0) && !equality.Semantic.DeepEqual(oldNode.Status.Addresses, newNode.Status.Addresses)
62+
63+
// Reset spec to make sure only patch for Status or ObjectMeta is generated.
64+
// Note that we don't reset ObjectMeta here, because:
65+
// 1. This aligns with Nodes().UpdateStatus().
66+
// 2. Some component does use this to update node annotations.
67+
diffNode := newNode.DeepCopy()
68+
diffNode.Spec = oldNode.Spec
69+
if manuallyPatchAddresses {
70+
diffNode.Status.Addresses = oldNode.Status.Addresses
71+
}
72+
newData, err := json.Marshal(diffNode)
73+
if err != nil {
74+
return nil, fmt.Errorf("failed to Marshal newData for node %q: %v", nodeName, err)
75+
}
76+
77+
patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Node{})
78+
if err != nil {
79+
return nil, fmt.Errorf("failed to CreateTwoWayMergePatch for node %q: %v", nodeName, err)
80+
}
81+
if manuallyPatchAddresses {
82+
patchBytes, err = fixupPatchForNodeStatusAddresses(patchBytes, newNode.Status.Addresses)
83+
if err != nil {
84+
return nil, fmt.Errorf("failed to fix up NodeAddresses in patch for node %q: %v", nodeName, err)
85+
}
86+
}
87+
88+
return patchBytes, nil
89+
}
90+
91+
// fixupPatchForNodeStatusAddresses adds a replace-strategy patch for Status.Addresses to
92+
// the existing patch
93+
func fixupPatchForNodeStatusAddresses(patchBytes []byte, addresses []v1.NodeAddress) ([]byte, error) {
94+
// Given patchBytes='{"status": {"conditions": [ ... ], "phase": ...}}' and
95+
// addresses=[{"type": "InternalIP", "address": "10.0.0.1"}], we need to generate:
96+
//
97+
// {
98+
// "status": {
99+
// "conditions": [ ... ],
100+
// "phase": ...,
101+
// "addresses": [
102+
// {
103+
// "type": "InternalIP",
104+
// "address": "10.0.0.1"
105+
// },
106+
// {
107+
// "$patch": "replace"
108+
// }
109+
// ]
110+
// }
111+
// }
112+
113+
var patchMap map[string]interface{}
114+
if err := json.Unmarshal(patchBytes, &patchMap); err != nil {
115+
return nil, err
116+
}
117+
118+
addrBytes, err := json.Marshal(addresses)
119+
if err != nil {
120+
return nil, err
121+
}
122+
var addrArray []interface{}
123+
if err := json.Unmarshal(addrBytes, &addrArray); err != nil {
124+
return nil, err
125+
}
126+
addrArray = append(addrArray, map[string]interface{}{"$patch": "replace"})
127+
128+
status := patchMap["status"]
129+
if status == nil {
130+
status = map[string]interface{}{}
131+
patchMap["status"] = status
132+
}
133+
statusMap, ok := status.(map[string]interface{})
134+
if !ok {
135+
return nil, fmt.Errorf("unexpected data in patch")
136+
}
137+
statusMap["addresses"] = addrArray
138+
139+
return json.Marshal(patchMap)
140+
}

0 commit comments

Comments
 (0)