k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/kubelet/config/http_test.go (about) 1 /* 2 Copyright 2014 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 config 18 19 import ( 20 "encoding/json" 21 "net/http" 22 "net/http/httptest" 23 "testing" 24 "time" 25 26 "k8s.io/api/core/v1" 27 apiequality "k8s.io/apimachinery/pkg/api/equality" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/runtime" 30 "k8s.io/apimachinery/pkg/types" 31 clientscheme "k8s.io/client-go/kubernetes/scheme" 32 utiltesting "k8s.io/client-go/util/testing" 33 api "k8s.io/kubernetes/pkg/apis/core" 34 k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1" 35 "k8s.io/kubernetes/pkg/apis/core/validation" 36 kubetypes "k8s.io/kubernetes/pkg/kubelet/types" 37 ) 38 39 func TestURLErrorNotExistNoUpdate(t *testing.T) { 40 ch := make(chan interface{}) 41 NewSourceURL("http://localhost:49575/_not_found_", http.Header{}, "localhost", time.Millisecond, ch) 42 select { 43 case got := <-ch: 44 t.Errorf("Expected no update, Got %#v", got) 45 case <-time.After(2 * time.Millisecond): 46 } 47 } 48 49 func TestExtractFromHttpBadness(t *testing.T) { 50 ch := make(chan interface{}, 1) 51 c := sourceURL{"http://localhost:49575/_not_found_", http.Header{}, "other", ch, nil, 0, http.DefaultClient} 52 if err := c.extractFromURL(); err == nil { 53 t.Errorf("Expected error") 54 } 55 expectEmptyChannel(t, ch) 56 } 57 58 func TestExtractInvalidPods(t *testing.T) { 59 var testCases = []struct { 60 desc string 61 pod *v1.Pod 62 }{ 63 { 64 desc: "No version", 65 pod: &v1.Pod{TypeMeta: metav1.TypeMeta{APIVersion: ""}}, 66 }, 67 { 68 desc: "Invalid version", 69 pod: &v1.Pod{TypeMeta: metav1.TypeMeta{APIVersion: "v1betta2"}}, 70 }, 71 { 72 desc: "Invalid volume name", 73 pod: &v1.Pod{ 74 TypeMeta: metav1.TypeMeta{APIVersion: "v1"}, 75 Spec: v1.PodSpec{ 76 Volumes: []v1.Volume{{Name: "_INVALID_"}}, 77 }, 78 }, 79 }, 80 { 81 desc: "Duplicate volume names", 82 pod: &v1.Pod{ 83 TypeMeta: metav1.TypeMeta{APIVersion: "v1"}, 84 Spec: v1.PodSpec{ 85 Volumes: []v1.Volume{{Name: "repeated"}, {Name: "repeated"}}, 86 }, 87 }, 88 }, 89 { 90 desc: "Unspecified container name", 91 pod: &v1.Pod{ 92 TypeMeta: metav1.TypeMeta{APIVersion: "v1"}, 93 Spec: v1.PodSpec{ 94 Containers: []v1.Container{{Name: ""}}, 95 }, 96 }, 97 }, 98 { 99 desc: "Invalid container name", 100 pod: &v1.Pod{ 101 TypeMeta: metav1.TypeMeta{APIVersion: "v1"}, 102 Spec: v1.PodSpec{ 103 Containers: []v1.Container{{Name: "_INVALID_"}}, 104 }, 105 }, 106 }, 107 } 108 for _, testCase := range testCases { 109 data, err := json.Marshal(testCase.pod) 110 if err != nil { 111 t.Fatalf("%s: Some weird json problem: %v", testCase.desc, err) 112 } 113 fakeHandler := utiltesting.FakeHandler{ 114 StatusCode: http.StatusOK, 115 ResponseBody: string(data), 116 } 117 testServer := httptest.NewServer(&fakeHandler) 118 defer testServer.Close() 119 ch := make(chan interface{}, 1) 120 c := sourceURL{testServer.URL, http.Header{}, "localhost", ch, nil, 0, http.DefaultClient} 121 if err := c.extractFromURL(); err == nil { 122 t.Errorf("%s: Expected error", testCase.desc) 123 } 124 } 125 } 126 127 func TestExtractPodsFromHTTP(t *testing.T) { 128 nodeName := "different-value" 129 130 grace := int64(30) 131 enableServiceLinks := v1.DefaultEnableServiceLinks 132 var testCases = []struct { 133 desc string 134 pods runtime.Object 135 expected kubetypes.PodUpdate 136 }{ 137 { 138 desc: "Single pod", 139 pods: &v1.Pod{ 140 TypeMeta: metav1.TypeMeta{ 141 Kind: "Pod", 142 APIVersion: "", 143 }, 144 ObjectMeta: metav1.ObjectMeta{ 145 Name: "foo", 146 UID: "111", 147 Namespace: "mynamespace", 148 }, 149 Spec: v1.PodSpec{ 150 NodeName: string(nodeName), 151 Containers: []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways, TerminationMessagePolicy: v1.TerminationMessageReadFile}}, 152 SecurityContext: &v1.PodSecurityContext{}, 153 SchedulerName: v1.DefaultSchedulerName, 154 }, 155 Status: v1.PodStatus{ 156 Phase: v1.PodPending, 157 }, 158 }, 159 expected: CreatePodUpdate(kubetypes.SET, 160 kubetypes.HTTPSource, 161 &v1.Pod{ 162 ObjectMeta: metav1.ObjectMeta{ 163 UID: "111", 164 Name: "foo" + "-" + nodeName, 165 Namespace: "mynamespace", 166 Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "111"}, 167 }, 168 Spec: v1.PodSpec{ 169 NodeName: nodeName, 170 RestartPolicy: v1.RestartPolicyAlways, 171 DNSPolicy: v1.DNSClusterFirst, 172 SecurityContext: &v1.PodSecurityContext{}, 173 TerminationGracePeriodSeconds: &grace, 174 SchedulerName: v1.DefaultSchedulerName, 175 EnableServiceLinks: &enableServiceLinks, 176 177 Containers: []v1.Container{{ 178 Name: "1", 179 Image: "foo", 180 TerminationMessagePath: "/dev/termination-log", 181 ImagePullPolicy: "Always", 182 TerminationMessagePolicy: v1.TerminationMessageReadFile, 183 }}, 184 }, 185 Status: v1.PodStatus{ 186 Phase: v1.PodPending, 187 }, 188 }), 189 }, 190 { 191 desc: "Multiple pods", 192 pods: &v1.PodList{ 193 TypeMeta: metav1.TypeMeta{ 194 Kind: "PodList", 195 APIVersion: "", 196 }, 197 Items: []v1.Pod{ 198 { 199 ObjectMeta: metav1.ObjectMeta{ 200 Name: "foo", 201 UID: "111", 202 }, 203 Spec: v1.PodSpec{ 204 NodeName: nodeName, 205 Containers: []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways, TerminationMessagePolicy: v1.TerminationMessageReadFile}}, 206 SecurityContext: &v1.PodSecurityContext{}, 207 SchedulerName: v1.DefaultSchedulerName, 208 }, 209 Status: v1.PodStatus{ 210 Phase: v1.PodPending, 211 }, 212 }, 213 { 214 ObjectMeta: metav1.ObjectMeta{ 215 Name: "bar", 216 UID: "222", 217 }, 218 Spec: v1.PodSpec{ 219 NodeName: nodeName, 220 Containers: []v1.Container{{Name: "2", Image: "bar:bartag", ImagePullPolicy: "", TerminationMessagePolicy: v1.TerminationMessageReadFile}}, 221 SecurityContext: &v1.PodSecurityContext{}, 222 SchedulerName: v1.DefaultSchedulerName, 223 }, 224 Status: v1.PodStatus{ 225 Phase: v1.PodPending, 226 }, 227 }, 228 }, 229 }, 230 expected: CreatePodUpdate(kubetypes.SET, 231 kubetypes.HTTPSource, 232 &v1.Pod{ 233 ObjectMeta: metav1.ObjectMeta{ 234 UID: "111", 235 Name: "foo" + "-" + nodeName, 236 Namespace: "default", 237 Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "111"}, 238 }, 239 Spec: v1.PodSpec{ 240 NodeName: nodeName, 241 RestartPolicy: v1.RestartPolicyAlways, 242 DNSPolicy: v1.DNSClusterFirst, 243 TerminationGracePeriodSeconds: &grace, 244 SecurityContext: &v1.PodSecurityContext{}, 245 SchedulerName: v1.DefaultSchedulerName, 246 EnableServiceLinks: &enableServiceLinks, 247 248 Containers: []v1.Container{{ 249 Name: "1", 250 Image: "foo", 251 TerminationMessagePath: "/dev/termination-log", 252 ImagePullPolicy: "Always", 253 TerminationMessagePolicy: v1.TerminationMessageReadFile, 254 }}, 255 }, 256 Status: v1.PodStatus{ 257 Phase: v1.PodPending, 258 }, 259 }, 260 &v1.Pod{ 261 ObjectMeta: metav1.ObjectMeta{ 262 UID: "222", 263 Name: "bar" + "-" + nodeName, 264 Namespace: "default", 265 Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "222"}, 266 }, 267 Spec: v1.PodSpec{ 268 NodeName: nodeName, 269 RestartPolicy: v1.RestartPolicyAlways, 270 DNSPolicy: v1.DNSClusterFirst, 271 TerminationGracePeriodSeconds: &grace, 272 SecurityContext: &v1.PodSecurityContext{}, 273 SchedulerName: v1.DefaultSchedulerName, 274 EnableServiceLinks: &enableServiceLinks, 275 276 Containers: []v1.Container{{ 277 Name: "2", 278 Image: "bar:bartag", 279 TerminationMessagePath: "/dev/termination-log", 280 ImagePullPolicy: "IfNotPresent", 281 TerminationMessagePolicy: v1.TerminationMessageReadFile, 282 }}, 283 }, 284 Status: v1.PodStatus{ 285 Phase: v1.PodPending, 286 }, 287 }), 288 }, 289 } 290 291 for _, testCase := range testCases { 292 data, err := runtime.Encode(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), testCase.pods) 293 if err != nil { 294 t.Fatalf("%s: error in encoding the pod: %v", testCase.desc, err) 295 } 296 fakeHandler := utiltesting.FakeHandler{ 297 StatusCode: http.StatusOK, 298 ResponseBody: string(data), 299 } 300 testServer := httptest.NewServer(&fakeHandler) 301 defer testServer.Close() 302 ch := make(chan interface{}, 1) 303 c := sourceURL{testServer.URL, http.Header{}, types.NodeName(nodeName), ch, nil, 0, http.DefaultClient} 304 if err := c.extractFromURL(); err != nil { 305 t.Errorf("%s: Unexpected error: %v", testCase.desc, err) 306 continue 307 } 308 update := (<-ch).(kubetypes.PodUpdate) 309 310 if !apiequality.Semantic.DeepEqual(testCase.expected, update) { 311 t.Errorf("%s: Expected: %#v, Got: %#v", testCase.desc, testCase.expected, update) 312 } 313 for _, pod := range update.Pods { 314 // TODO: remove the conversion when validation is performed on versioned objects. 315 internalPod := &api.Pod{} 316 if err := k8s_api_v1.Convert_v1_Pod_To_core_Pod(pod, internalPod, nil); err != nil { 317 t.Fatalf("%s: Cannot convert pod %#v, %#v", testCase.desc, pod, err) 318 } 319 if errs := validation.ValidatePodCreate(internalPod, validation.PodValidationOptions{}); len(errs) != 0 { 320 t.Errorf("%s: Expected no validation errors on %#v, Got %v", testCase.desc, pod, errs.ToAggregate()) 321 } 322 } 323 } 324 } 325 326 func TestURLWithHeader(t *testing.T) { 327 pod := &v1.Pod{ 328 TypeMeta: metav1.TypeMeta{ 329 APIVersion: "v1", 330 Kind: "Pod", 331 }, 332 ObjectMeta: metav1.ObjectMeta{ 333 Name: "foo", 334 UID: "111", 335 Namespace: "mynamespace", 336 }, 337 Spec: v1.PodSpec{ 338 NodeName: "localhost", 339 Containers: []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways}}, 340 }, 341 } 342 data, err := json.Marshal(pod) 343 if err != nil { 344 t.Fatalf("Unexpected json marshalling error: %v", err) 345 } 346 fakeHandler := utiltesting.FakeHandler{ 347 StatusCode: http.StatusOK, 348 ResponseBody: string(data), 349 } 350 testServer := httptest.NewServer(&fakeHandler) 351 defer testServer.Close() 352 ch := make(chan interface{}, 1) 353 header := make(http.Header) 354 header.Set("Metadata-Flavor", "Google") 355 c := sourceURL{testServer.URL, header, "localhost", ch, nil, 0, http.DefaultClient} 356 if err := c.extractFromURL(); err != nil { 357 t.Fatalf("Unexpected error extracting from URL: %v", err) 358 } 359 update := (<-ch).(kubetypes.PodUpdate) 360 361 headerVal := fakeHandler.RequestReceived.Header["Metadata-Flavor"] 362 if len(headerVal) != 1 || headerVal[0] != "Google" { 363 t.Errorf("Header missing expected entry %v. Got %v", header, fakeHandler.RequestReceived.Header) 364 } 365 if len(update.Pods) != 1 { 366 t.Errorf("Received wrong number of pods, expected one: %v", update.Pods) 367 } 368 }