1
16
17 package markcontrolplane
18
19 import (
20 "bytes"
21 "encoding/json"
22 "net/http"
23 "net/http/httptest"
24 "testing"
25
26 v1 "k8s.io/api/core/v1"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 clientset "k8s.io/client-go/kubernetes"
29 restclient "k8s.io/client-go/rest"
30
31 kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
32 )
33
34 func TestMarkControlPlane(t *testing.T) {
35
36
37
38
39
40 tests := []struct {
41 name string
42 existingLabels []string
43 existingTaints []v1.Taint
44 newTaints []v1.Taint
45 expectedPatch string
46 }{
47 {
48 name: "apply default labels",
49 existingLabels: []string{""},
50 existingTaints: nil,
51 newTaints: nil,
52 expectedPatch: `{"metadata":{"labels":{"node-role.kubernetes.io/control-plane":"","node.kubernetes.io/exclude-from-external-load-balancers":""}}}`,
53 },
54 {
55 name: "control-plane taint missing",
56 existingLabels: []string{
57 kubeadmconstants.LabelNodeRoleControlPlane,
58 kubeadmconstants.LabelExcludeFromExternalLB,
59 },
60 existingTaints: nil,
61 newTaints: []v1.Taint{kubeadmconstants.ControlPlaneTaint},
62 expectedPatch: `{"spec":{"taints":[{"effect":"NoSchedule","key":"node-role.kubernetes.io/control-plane"}]}}`,
63 },
64 {
65 name: "nothing missing",
66 existingLabels: []string{
67 kubeadmconstants.LabelNodeRoleControlPlane,
68 kubeadmconstants.LabelExcludeFromExternalLB,
69 },
70 existingTaints: []v1.Taint{kubeadmconstants.ControlPlaneTaint},
71 newTaints: []v1.Taint{kubeadmconstants.ControlPlaneTaint},
72 expectedPatch: `{}`,
73 },
74 {
75 name: "has taint and no new taints wanted",
76 existingLabels: []string{
77 kubeadmconstants.LabelNodeRoleControlPlane,
78 kubeadmconstants.LabelExcludeFromExternalLB,
79 },
80 existingTaints: []v1.Taint{
81 {
82 Key: "node.cloudprovider.kubernetes.io/uninitialized",
83 Effect: v1.TaintEffectNoSchedule,
84 },
85 },
86 newTaints: nil,
87 expectedPatch: `{}`,
88 },
89 {
90 name: "has taint and should merge with wanted taint",
91 existingLabels: []string{
92 kubeadmconstants.LabelNodeRoleControlPlane,
93 kubeadmconstants.LabelExcludeFromExternalLB,
94 },
95 existingTaints: []v1.Taint{
96 {
97 Key: "node.cloudprovider.kubernetes.io/uninitialized",
98 Effect: v1.TaintEffectNoSchedule,
99 },
100 },
101 newTaints: []v1.Taint{kubeadmconstants.ControlPlaneTaint},
102 expectedPatch: `{"spec":{"taints":[{"effect":"NoSchedule","key":"node-role.kubernetes.io/control-plane"},{"effect":"NoSchedule","key":"node.cloudprovider.kubernetes.io/uninitialized"}]}}`,
103 },
104 }
105
106 for _, tc := range tests {
107 t.Run(tc.name, func(t *testing.T) {
108 nodename := "node01"
109 controlPlaneNode := &v1.Node{
110 ObjectMeta: metav1.ObjectMeta{
111 Name: nodename,
112 Labels: map[string]string{
113 v1.LabelHostname: nodename,
114 },
115 },
116 }
117
118 for _, label := range tc.existingLabels {
119 controlPlaneNode.ObjectMeta.Labels[label] = ""
120 }
121
122 if tc.existingTaints != nil {
123 controlPlaneNode.Spec.Taints = tc.existingTaints
124 }
125
126 jsonNode, err := json.Marshal(controlPlaneNode)
127 if err != nil {
128 t.Fatalf("unexpected encoding error: %v", err)
129 }
130
131 var patchRequest string
132 s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
133 w.Header().Set("Content-Type", "application/json")
134
135 if req.URL.Path != "/api/v1/nodes/"+nodename {
136 t.Errorf("request for unexpected HTTP resource: %v", req.URL.Path)
137 http.Error(w, "", http.StatusNotFound)
138 return
139 }
140
141 switch req.Method {
142 case "GET":
143 case "PATCH":
144 buf := new(bytes.Buffer)
145 buf.ReadFrom(req.Body)
146 patchRequest = buf.String()
147 default:
148 t.Errorf("request for unexpected HTTP verb: %v", req.Method)
149 http.Error(w, "", http.StatusNotFound)
150 return
151 }
152
153 w.WriteHeader(http.StatusOK)
154 w.Write(jsonNode)
155 }))
156 defer s.Close()
157
158 cs, err := clientset.NewForConfig(&restclient.Config{Host: s.URL})
159 if err != nil {
160 t.Fatalf("unexpected error building clientset: %v", err)
161 }
162
163 if err := MarkControlPlane(cs, nodename, tc.newTaints); err != nil {
164 t.Errorf("unexpected error: %v", err)
165 }
166
167 if tc.expectedPatch != patchRequest {
168 t.Errorf("unexpected error: wanted patch %v, got %v", tc.expectedPatch, patchRequest)
169 }
170 })
171 }
172 }
173
View as plain text