1
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
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 }
369
View as plain text