k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/kubeadm/app/phases/addons/proxy/proxy_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 proxy 18 19 import ( 20 "bytes" 21 "context" 22 "os" 23 "strings" 24 "testing" 25 "time" 26 27 "github.com/lithammer/dedent" 28 29 apps "k8s.io/api/apps/v1" 30 v1 "k8s.io/api/core/v1" 31 apierrors "k8s.io/apimachinery/pkg/api/errors" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 "k8s.io/apimachinery/pkg/runtime" 34 clientsetfake "k8s.io/client-go/kubernetes/fake" 35 clientsetscheme "k8s.io/client-go/kubernetes/scheme" 36 core "k8s.io/client-go/testing" 37 38 kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" 39 kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" 40 configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" 41 ) 42 43 func TestCompileManifests(t *testing.T) { 44 var tests = []struct { 45 name string 46 manifest string 47 data interface{} 48 }{ 49 { 50 name: "KubeProxyConfigMap19", 51 manifest: KubeProxyConfigMap19, 52 data: struct { 53 ControlPlaneEndpoint, ProxyConfig, ProxyConfigMap, ProxyConfigMapKey string 54 }{ 55 ControlPlaneEndpoint: "foo", 56 ProxyConfig: " bindAddress: 0.0.0.0\n clusterCIDR: 192.168.1.1\n enableProfiling: false", 57 ProxyConfigMap: "bar", 58 ProxyConfigMapKey: "baz", 59 }, 60 }, 61 { 62 name: "KubeProxyDaemonSet19", 63 manifest: KubeProxyDaemonSet19, 64 data: struct{ Image, ProxyConfigMap, ProxyConfigMapKey string }{ 65 Image: "foo", 66 ProxyConfigMap: "bar", 67 ProxyConfigMapKey: "baz", 68 }, 69 }, 70 } 71 for _, rt := range tests { 72 t.Run(rt.name, func(t *testing.T) { 73 _, err := kubeadmutil.ParseTemplate(rt.manifest, rt.data) 74 if err != nil { 75 t.Errorf("unexpected ParseTemplate failure: %+v", err) 76 } 77 }) 78 } 79 } 80 81 func TestEnsureProxyAddon(t *testing.T) { 82 type SimulatedError int 83 const ( 84 NoError SimulatedError = iota 85 ServiceAccountError 86 InvalidControlPlaneEndpoint 87 IPv6SetBindAddress 88 ) 89 90 var testCases = []struct { 91 name string 92 simError SimulatedError 93 expErrString string 94 expBindAddr string 95 expClusterCIDR string 96 }{ 97 { 98 name: "Successful proxy addon", 99 simError: NoError, 100 expErrString: "", 101 expBindAddr: "0.0.0.0", 102 expClusterCIDR: "5.6.7.8/24", 103 }, { 104 name: "Simulated service account error", 105 simError: ServiceAccountError, 106 expErrString: "error when creating kube-proxy service account", 107 expBindAddr: "0.0.0.0", 108 expClusterCIDR: "5.6.7.8/24", 109 }, { 110 name: "IPv6 AdvertiseAddress address", 111 simError: IPv6SetBindAddress, 112 expErrString: "", 113 expBindAddr: "::", 114 expClusterCIDR: "2001:101::/96", 115 }, 116 } 117 118 // Override the default timeouts to be shorter 119 defaultTimeouts := kubeadmapi.GetActiveTimeouts() 120 defaultAPICallTimeout := defaultTimeouts.KubernetesAPICall 121 defaultTimeouts.KubernetesAPICall = &metav1.Duration{Duration: time.Microsecond * 500} 122 defer func() { 123 defaultTimeouts.KubernetesAPICall = defaultAPICallTimeout 124 }() 125 126 for _, tc := range testCases { 127 t.Run(tc.name, func(t *testing.T) { 128 // Create a fake client and set up default test configuration 129 client := clientsetfake.NewSimpleClientset() 130 131 // TODO: Consider using a YAML file instead for this that makes it possible to specify YAML documents for the ComponentConfigs 132 initConfiguration, err := configutil.DefaultedStaticInitConfiguration() 133 if err != nil { 134 t.Errorf("test failed to convert external to internal version: %v", err) 135 return 136 } 137 138 initConfiguration.LocalAPIEndpoint = kubeadmapi.APIEndpoint{ 139 AdvertiseAddress: "1.2.3.4", 140 BindPort: 1234, 141 } 142 143 initConfiguration.ClusterConfiguration.Networking.PodSubnet = "5.6.7.8/24" 144 initConfiguration.ClusterConfiguration.ImageRepository = "someRepo" 145 146 // Simulate an error if necessary 147 switch tc.simError { 148 case ServiceAccountError: 149 client.PrependReactor("create", "serviceaccounts", func(action core.Action) (bool, runtime.Object, error) { 150 return true, nil, apierrors.NewUnauthorized("") 151 }) 152 case InvalidControlPlaneEndpoint: 153 initConfiguration.LocalAPIEndpoint.AdvertiseAddress = "1.2.3" 154 case IPv6SetBindAddress: 155 initConfiguration.LocalAPIEndpoint.AdvertiseAddress = "1:2::3:4" 156 initConfiguration.ClusterConfiguration.Networking.PodSubnet = "2001:101::/48" 157 } 158 159 err = EnsureProxyAddon(&initConfiguration.ClusterConfiguration, &initConfiguration.LocalAPIEndpoint, client, os.Stdout, false) 160 161 // Compare actual to expected errors 162 actErr := "No error" 163 if err != nil { 164 actErr = err.Error() 165 } 166 expErr := "No error" 167 if tc.expErrString != "" { 168 expErr = tc.expErrString 169 } 170 if !strings.Contains(actErr, expErr) { 171 t.Errorf( 172 "%s test failed, expected: %s, got: %s", 173 tc.name, 174 expErr, 175 actErr) 176 } 177 }) 178 } 179 } 180 181 func TestDaemonSetsHaveSystemNodeCriticalPriorityClassName(t *testing.T) { 182 testCases := []struct { 183 name string 184 manifest string 185 data interface{} 186 }{ 187 { 188 name: "KubeProxyDaemonSet19", 189 manifest: KubeProxyDaemonSet19, 190 data: struct{ Image, ProxyConfigMap, ProxyConfigMapKey string }{ 191 Image: "foo", 192 ProxyConfigMap: "foo", 193 ProxyConfigMapKey: "foo", 194 }, 195 }, 196 } 197 for _, testCase := range testCases { 198 t.Run(testCase.name, func(t *testing.T) { 199 daemonSetBytes, _ := kubeadmutil.ParseTemplate(testCase.manifest, testCase.data) 200 daemonSet := &apps.DaemonSet{} 201 if err := runtime.DecodeInto(clientsetscheme.Codecs.UniversalDecoder(), daemonSetBytes, daemonSet); err != nil { 202 t.Errorf("unexpected error: %v", err) 203 } 204 if daemonSet.Spec.Template.Spec.PriorityClassName != "system-node-critical" { 205 t.Errorf("expected to see system-node-critical priority class name. Got %q instead", daemonSet.Spec.Template.Spec.PriorityClassName) 206 } 207 }) 208 } 209 } 210 211 func TestPrintOrCreateKubeProxyObjects(t *testing.T) { 212 tests := []struct { 213 name string 214 printManifest bool 215 wantOut string 216 wantErr bool 217 }{ 218 { 219 name: "do not print manifest", 220 printManifest: false, 221 wantOut: "[addons] Applied essential addon: kube-proxy\n", 222 wantErr: false, 223 }, 224 { 225 name: "print manifest", 226 printManifest: true, 227 wantOut: dedent.Dedent(`--- 228 apiVersion: v1 229 kind: ServiceAccount 230 metadata: 231 creationTimestamp: null 232 name: kube-proxy 233 namespace: kube-system 234 --- 235 apiVersion: rbac.authorization.k8s.io/v1 236 kind: ClusterRoleBinding 237 metadata: 238 creationTimestamp: null 239 name: kubeadm:node-proxier 240 roleRef: 241 apiGroup: rbac.authorization.k8s.io 242 kind: ClusterRole 243 name: system:node-proxier 244 subjects: 245 - kind: ServiceAccount 246 name: kube-proxy 247 namespace: kube-system 248 --- 249 apiVersion: rbac.authorization.k8s.io/v1 250 kind: Role 251 metadata: 252 creationTimestamp: null 253 name: kube-proxy 254 namespace: kube-system 255 rules: 256 - apiGroups: 257 - "" 258 resourceNames: 259 - kube-proxy 260 resources: 261 - configmaps 262 verbs: 263 - get 264 --- 265 apiVersion: rbac.authorization.k8s.io/v1 266 kind: RoleBinding 267 metadata: 268 creationTimestamp: null 269 name: kube-proxy 270 namespace: kube-system 271 roleRef: 272 apiGroup: rbac.authorization.k8s.io 273 kind: Role 274 name: kube-proxy 275 subjects: 276 - kind: Group 277 name: system:bootstrappers:kubeadm:default-node-token 278 --- 279 foo 280 --- 281 bar 282 `), 283 wantErr: false, 284 }, 285 } 286 for _, tt := range tests { 287 t.Run(tt.name, func(t *testing.T) { 288 out := &bytes.Buffer{} 289 client := newMockClientForTest(t) 290 cmByte := []byte{'\n', 'f', 'o', 'o', '\n'} 291 dsByte := []byte{'\n', 'b', 'a', 'r', '\n'} 292 if err := printOrCreateKubeProxyObjects(cmByte, dsByte, client, out, tt.printManifest); (err != nil) != tt.wantErr { 293 t.Fatalf("printOrCreateKubeProxyObjects() error = %v, wantErr %v", err, tt.wantErr) 294 } 295 if gotOut := out.String(); gotOut != tt.wantOut { 296 t.Fatalf("printOrCreateKubeProxyObjects() = %v, want %v", gotOut, tt.wantOut) 297 } 298 }) 299 } 300 } 301 302 func newMockClientForTest(t *testing.T) *clientsetfake.Clientset { 303 client := clientsetfake.NewSimpleClientset() 304 _, err := client.AppsV1().DaemonSets(metav1.NamespaceSystem).Create(context.TODO(), &apps.DaemonSet{ 305 TypeMeta: metav1.TypeMeta{ 306 Kind: "DaemonSet", 307 APIVersion: "apps/v1", 308 }, 309 ObjectMeta: metav1.ObjectMeta{ 310 Name: "kube-proxy", 311 Namespace: metav1.NamespaceSystem, 312 Labels: map[string]string{ 313 "k8s-app": "kube-proxy", 314 }, 315 }, 316 Spec: apps.DaemonSetSpec{ 317 Template: v1.PodTemplateSpec{}, 318 }, 319 }, metav1.CreateOptions{}) 320 if err != nil { 321 t.Fatalf("error creating Daemonset: %v", err) 322 } 323 324 return client 325 }