k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/kubeadm/app/phases/markcontrolplane/markcontrolplane_test.go (about) 1 /* 2 Copyright 2017 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 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 // Note: this test takes advantage of the deterministic marshalling of 36 // JSON provided by strategicpatch so that "expectedPatch" can use a 37 // string equality test instead of a logical JSON equality test. That 38 // will need to change if strategicpatch's behavior changes in the 39 // future. 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 }