func TestReadManifestFromFileWithDefaults(t *testing.T) { if !api.PreV1Beta3(testapi.Version()) { return } file := writeTestFile(t, os.TempDir(), "test_pod_config", fmt.Sprintf(`{ "version": "%s", "id": "test", "containers": [{ "name": "image", "image": "test/image" }] }`, testapi.Version())) defer os.Remove(file.Name()) ch := make(chan interface{}) NewSourceFile(file.Name(), "localhost", time.Millisecond, ch) select { case got := <-ch: update := got.(kubelet.PodUpdate) if update.Pods[0].UID == "" { t.Errorf("Unexpected UID: %s", update.Pods[0].UID) } case <-time.After(time.Second): t.Errorf("Expected update, timeout instead") } }
func TestPodTemplateList(t *testing.T) { if api.PreV1Beta3(testapi.Version()) { return } ns := api.NamespaceDefault podTemplateList := &api.PodTemplateList{ Items: []api.PodTemplate{ { ObjectMeta: api.ObjectMeta{ Name: "foo", Namespace: ns, }, }, }, } c := &testClient{ Request: testRequest{ Method: "GET", Path: testapi.ResourcePath(getPodTemplatesResoureName(), ns, ""), Query: buildQueryValues(ns, nil), Body: nil, }, Response: Response{StatusCode: 200, Body: podTemplateList}, } response, err := c.Setup().PodTemplates(ns).List(labels.Everything(), fields.Everything()) c.Validate(t, response, err) }
func makeTestServer(t *testing.T, namespace, name string, podResponse, controllerResponse, updateResponse serverResponse) (*httptest.Server, *util.FakeHandler) { fakePodHandler := util.FakeHandler{ StatusCode: podResponse.statusCode, ResponseBody: runtime.EncodeOrDie(testapi.Codec(), podResponse.obj.(runtime.Object)), } fakeControllerHandler := util.FakeHandler{ StatusCode: controllerResponse.statusCode, ResponseBody: runtime.EncodeOrDie(testapi.Codec(), controllerResponse.obj.(runtime.Object)), } fakeUpdateHandler := util.FakeHandler{ StatusCode: updateResponse.statusCode, ResponseBody: runtime.EncodeOrDie(testapi.Codec(), updateResponse.obj.(runtime.Object)), } mux := http.NewServeMux() mux.Handle(testapi.ResourcePath("pods", namespace, ""), &fakePodHandler) mux.Handle(testapi.ResourcePath(replicationControllerResourceName(), "", ""), &fakeControllerHandler) if !api.PreV1Beta3(testapi.Version()) && namespace != "" { mux.Handle(testapi.ResourcePath(replicationControllerResourceName(), namespace, ""), &fakeControllerHandler) } if name != "" { mux.Handle(testapi.ResourcePath(replicationControllerResourceName(), namespace, name), &fakeUpdateHandler) } mux.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { t.Errorf("unexpected request: %v", req.RequestURI) res.WriteHeader(http.StatusNotFound) }) return httptest.NewServer(mux), &fakeUpdateHandler }
func init() { if api.PreV1Beta3(testapi.Version()) { nodeResourceName = "minions" } else { nodeResourceName = "nodes" } }
func TestPodTemplateGet(t *testing.T) { if api.PreV1Beta3(testapi.Version()) { return } ns := api.NamespaceDefault podTemplate := &api.PodTemplate{ ObjectMeta: api.ObjectMeta{ Name: "abc", Namespace: ns, }, Template: api.PodTemplateSpec{}, } c := &testClient{ Request: testRequest{ Method: "GET", Path: testapi.ResourcePath(getPodTemplatesResoureName(), ns, "abc"), Query: buildQueryValues(ns, nil), Body: nil, }, Response: Response{StatusCode: 200, Body: podTemplate}, } response, err := c.Setup().PodTemplates(ns).Get("abc") c.Validate(t, response, err) }
func ResourcePathWithPrefixAndNamespaceQuery(prefix, resource, namespace, name string) string { path := ResourcePathWithPrefix(prefix, resource, namespace, name) // Add namespace as query param for pre v1beta3. if api.PreV1Beta3(Version()) && namespace != "" { path = path + "?namespace=" + namespace } return path }
// Delete deletes an existing service. func (c *services) Delete(name string) error { // v1beta3 does not allow DELETE without a namespace. needNamespace := !api.PreV1Beta3(c.r.APIVersion()) namespace := c.ns if needNamespace && len(namespace) == 0 { namespace = api.NamespaceDefault } return c.r.Delete().Namespace(c.ns).Resource("services").Name(name).Do().Error() }
func TestExtractFromDir(t *testing.T) { if !api.PreV1Beta3(testapi.Version()) { return } manifest, expectedPod := exampleManifestAndPod("1") manifest2, expectedPod2 := exampleManifestAndPod("2") manifests := []v1beta1.ContainerManifest{manifest, manifest2} pods := []*api.Pod{expectedPod, expectedPod2} files := make([]*os.File, len(manifests)) dirName, err := ioutil.TempDir("", "foo") if err != nil { t.Fatalf("Unexpected error: %v", err) } for i, manifest := range manifests { data, err := json.Marshal(manifest) if err != nil { t.Errorf("Unexpected error: %v", err) continue } file, err := ioutil.TempFile(dirName, manifest.ID) if err != nil { t.Errorf("Unexpected error: %v", err) continue } name := file.Name() if err := file.Close(); err != nil { t.Errorf("Unexpected error: %v", err) continue } ioutil.WriteFile(name, data, 0755) files[i] = file } ch := make(chan interface{}, 1) c := sourceFile{dirName, "an-example-host", ch} err = c.extractFromPath() if err != nil { t.Fatalf("Unexpected error: %v", err) } update := (<-ch).(kubelet.PodUpdate) expected := CreatePodUpdate(kubelet.SET, kubelet.FileSource, pods...) sort.Sort(sortedPods(update.Pods)) sort.Sort(sortedPods(expected.Pods)) if !api.Semantic.DeepDerivative(expected, update) { t.Fatalf("Expected %#v, Got %#v", expected, update) } for _, pod := range update.Pods { if errs := validation.ValidatePod(pod); len(errs) != 0 { t.Errorf("Expected no validation errors on %#v, Got %q", pod, errs) } } }
// Create creates a new service. func (c *services) Create(svc *api.Service) (result *api.Service, err error) { result = &api.Service{} // v1beta3 does not allow POST without a namespace. needNamespace := !api.PreV1Beta3(c.r.APIVersion()) namespace := c.ns if needNamespace && len(namespace) == 0 { namespace = api.NamespaceDefault } err = c.r.Post().Namespace(namespace).Resource("services").Body(svc).Do().Into(result) return }
func getSelfLink(name, namespace string) string { var selfLink string if api.PreV1Beta3(latest.Version) { selfLink = fmt.Sprintf("/api/"+latest.Version+"/pods/%s?namespace=%s", name, namespace) } else { if len(namespace) == 0 { namespace = api.NamespaceDefault } selfLink = fmt.Sprintf("/api/"+latest.Version+"/pods/namespaces/%s/%s", name, namespace) } return selfLink }
func TestPodTemplateDelete(t *testing.T) { if api.PreV1Beta3(testapi.Version()) { return } ns := api.NamespaceDefault c := &testClient{ Request: testRequest{Method: "DELETE", Path: testapi.ResourcePath(getPodTemplatesResoureName(), ns, "foo"), Query: buildQueryValues(ns, nil)}, Response: Response{StatusCode: 200}, } err := c.Setup().PodTemplates(ns).Delete("foo", nil) c.Validate(t, nil, err) }
func parseResourceVersion(response []byte) (string, float64, error) { var resultBodyMap map[string]interface{} err := json.Unmarshal(response, &resultBodyMap) if err != nil { return "", 0, fmt.Errorf("unexpected error unmarshaling resultBody: %v", err) } apiVersion, ok := resultBodyMap["apiVersion"].(string) if !ok { return "", 0, fmt.Errorf("unexpected error, apiVersion not found in JSON response: %v", string(response)) } if api.PreV1Beta3(apiVersion) { return parsePreV1Beta3ResourceVersion(resultBodyMap, response) } return parseV1Beta3ResourceVersion(resultBodyMap, response) }
func TestPodTemplateWatch(t *testing.T) { if api.PreV1Beta3(testapi.Version()) { return } c := &testClient{ Request: testRequest{ Method: "GET", Path: "/api/" + testapi.Version() + "/watch/" + getPodTemplatesResoureName(), Query: url.Values{"resourceVersion": []string{}}}, Response: Response{StatusCode: 200}, } _, err := c.Setup().PodTemplates(api.NamespaceAll).Watch(labels.Everything(), fields.Everything(), "") c.Validate(t, nil, err) }
// Update updates an existing service. func (c *services) Update(svc *api.Service) (result *api.Service, err error) { result = &api.Service{} if len(svc.ResourceVersion) == 0 { err = fmt.Errorf("invalid update object, missing resource version: %v", svc) return } // v1beta3 does not allow PUT without a namespace. needNamespace := !api.PreV1Beta3(c.r.APIVersion()) namespace := c.ns if needNamespace && len(namespace) == 0 { namespace = api.NamespaceDefault } err = c.r.Put().Namespace(namespace).Resource("services").Name(svc.Name).Body(svc).Do().Into(result) return }
// buildQueryValues is a convenience function for knowing if a namespace should be in a query param or not func buildQueryValues(namespace string, query url.Values) url.Values { v := url.Values{} if query != nil { for key, values := range query { for _, value := range values { v.Add(key, value) } } } if len(namespace) > 0 { if api.PreV1Beta3(testapi.Version()) { v.Set("namespace", namespace) } } return v }
func getFakeClient(t *testing.T, validURLs []string) (ClientPosterFunc, *httptest.Server) { handlerFunc := func(w http.ResponseWriter, r *http.Request) { for _, u := range validURLs { if u == r.RequestURI { return } } t.Errorf("Unexpected HTTP request: %s, expected %v", r.RequestURI, validURLs) } server := httptest.NewServer(http.HandlerFunc(handlerFunc)) return func(mapping *meta.RESTMapping) (RESTClientPoster, error) { fakeCodec := testapi.Codec() fakeUri, _ := url.Parse(server.URL + "/api/" + testapi.Version()) legacyBehavior := api.PreV1Beta3(testapi.Version()) return client.NewRESTClient(fakeUri, testapi.Version(), fakeCodec, legacyBehavior, 5, 10), nil }, server }
// Returns the appropriate path for the given prefix (watch, proxy, redirect, etc), resource, namespace and name. // For ex, this is of the form: // /api/v1beta1/watch/pods/pod0 for v1beta1 and // /api/v1beta3/watch/namespaces/foo/pods/pod0 for v1beta3. func ResourcePathWithPrefix(prefix, resource, namespace, name string) string { path := "/api/" + Version() if prefix != "" { path = path + "/" + prefix } if !api.PreV1Beta3(Version()) { if namespace != "" { path = path + "/namespaces/" + namespace } // Resource names in v1beta3 are lower case. resource = strings.ToLower(resource) } if resource != "" { path = path + "/" + resource } if name != "" { path = path + "/" + name } return path }
// WebHookURL returns the URL for the provided build config name and trigger policy, or ErrTriggerIsNotAWebHook // if the trigger is not a webhook type. func (c *buildConfigs) WebHookURL(name string, trigger *buildapi.BuildTriggerPolicy) (*url.URL, error) { if kapi.PreV1Beta3(c.r.APIVersion()) { switch { case trigger.GenericWebHook != nil: return c.r.Get().Namespace(c.ns).Resource("buildConfigHooks").Name(name).Suffix(trigger.GenericWebHook.Secret, "generic").URL(), nil case trigger.GitHubWebHook != nil: return c.r.Get().Namespace(c.ns).Resource("buildConfigHooks").Name(name).Suffix(trigger.GitHubWebHook.Secret, "github").URL(), nil default: return nil, ErrTriggerIsNotAWebHook } } switch { case trigger.GenericWebHook != nil: return c.r.Get().Namespace(c.ns).Resource("buildConfigs").Name(name).SubResource("webhooks").Suffix(trigger.GenericWebHook.Secret, "generic").URL(), nil case trigger.GitHubWebHook != nil: return c.r.Get().Namespace(c.ns).Resource("buildConfigs").Name(name).SubResource("webhooks").Suffix(trigger.GitHubWebHook.Secret, "github").URL(), nil default: return nil, ErrTriggerIsNotAWebHook } }
func getSCCResoureName() string { if api.PreV1Beta3(testapi.Version()) { return "securityContextConstraints" } return "securitycontextconstraints" }
func getPersistentVolumeClaimsResoureName() string { if api.PreV1Beta3(testapi.Version()) { return "persistentVolumeClaims" } return "persistentvolumeclaims" }
func getLimitRangesResourceName() string { if api.PreV1Beta3(testapi.Version()) { return "limitRanges" } return "limitranges" }
func getNodesResourceName() string { if api.PreV1Beta3(testapi.Version()) { return "minions" } return "nodes" }
func TestTemplateStrings(t *testing.T) { // This unit tests the "exists" function as well as the template from update.sh table := map[string]struct { pod api.Pod expect string }{ "nilInfo": {api.Pod{}, "false"}, "emptyInfo": {api.Pod{Status: api.PodStatus{ContainerStatuses: []api.ContainerStatus{}}}, "false"}, "fooExists": { api.Pod{ Status: api.PodStatus{ ContainerStatuses: []api.ContainerStatus{ { Name: "foo", }, }, }, }, "false", }, "barExists": { api.Pod{ Status: api.PodStatus{ ContainerStatuses: []api.ContainerStatus{ { Name: "bar", }, }, }, }, "false", }, "bothExist": { api.Pod{ Status: api.PodStatus{ ContainerStatuses: []api.ContainerStatus{ { Name: "foo", }, { Name: "bar", }, }, }, }, "false", }, "barValid": { api.Pod{ Status: api.PodStatus{ ContainerStatuses: []api.ContainerStatus{ { Name: "foo", }, { Name: "bar", State: api.ContainerState{ Running: &api.ContainerStateRunning{ StartedAt: util.Time{}, }, }, }, }, }, }, "false", }, "bothValid": { api.Pod{ Status: api.PodStatus{ ContainerStatuses: []api.ContainerStatus{ { Name: "foo", State: api.ContainerState{ Running: &api.ContainerStateRunning{ StartedAt: util.Time{}, }, }, }, { Name: "bar", State: api.ContainerState{ Running: &api.ContainerStateRunning{ StartedAt: util.Time{}, }, }, }, }, }, }, "true", }, } // The point of this test is to verify that the below template works. If you change this // template, you need to update hack/e2e-suite/update.sh. tmpl := `` if api.PreV1Beta3(testapi.Version()) { tmpl = `{{exists . "currentState" "info" "foo" "state" "running"}}` useThisToDebug := ` a: {{exists . "currentState"}} b: {{exists . "currentState" "info"}} c: {{exists . "currentState" "info" "foo"}} d: {{exists . "currentState" "info" "foo" "state"}} e: {{exists . "currentState" "info" "foo" "state" "running"}} f: {{exists . "currentState" "info" "foo" "state" "running" "startedAt"}}` _ = useThisToDebug // don't complain about unused var } else { tmpl = `{{if (exists . "status" "containerStatuses")}}{{range .status.containerStatuses}}{{if (and (eq .name "foo") (exists . "state" "running"))}}true{{end}}{{end}}{{end}}` } p, err := NewTemplatePrinter([]byte(tmpl)) if err != nil { t.Fatalf("tmpl fail: %v", err) } printer := NewVersionedPrinter(p, api.Scheme, testapi.Version()) for name, item := range table { buffer := &bytes.Buffer{} err = printer.PrintObj(&item.pod, buffer) if err != nil { t.Errorf("%v: unexpected err: %v", name, err) continue } actual := buffer.String() if len(actual) == 0 { actual = "false" } if e := item.expect; e != actual { t.Errorf("%v: expected %v, got %v", name, e, actual) } } }
// Returns the appropriate field label to use for name of the involved object as per the given API version. func getInvolvedObjectNameFieldLabel(version string) string { if api.PreV1Beta3(version) { return "involvedObject.id" } return "involvedObject.name" }
func TestExtractManifestFromHTTP(t *testing.T) { hostname := "random-hostname" // ContainerManifests are not supported v1beta3 onwards. if api.PreV1Beta3(testapi.Version()) { return } var testCases = []struct { desc string manifests interface{} expected kubelet.PodUpdate }{ { desc: "Single manifest", manifests: v1beta1.ContainerManifest{Version: "v1beta1", ID: "foo", UUID: "111", Containers: []v1beta1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1beta1.PullAlways}}}, expected: CreatePodUpdate(kubelet.SET, kubelet.HTTPSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "111", Name: "foo" + "-" + hostname, Namespace: "foobar", SelfLink: getSelfLink("foo-"+hostname, kubelet.NamespaceDefault), }, Spec: api.PodSpec{ Host: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{ Name: "1", Image: "foo", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "Always"}}, }, }), }, { desc: "Single manifest without ID", manifests: v1beta1.ContainerManifest{Version: "v1beta1", UUID: "111", Containers: []v1beta1.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}}, expected: CreatePodUpdate(kubelet.SET, kubelet.HTTPSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "111", Name: "111" + "-" + hostname, Namespace: "foobar", SelfLink: getSelfLink("111-"+hostname, kubelet.NamespaceDefault), }, Spec: api.PodSpec{ Host: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{ Name: "ctr", Image: "image", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "IfNotPresent"}}, }, }), }, { desc: "Single manifest with v1beta2", manifests: v1beta1.ContainerManifest{Version: "v1beta2", ID: "foo", UUID: "111", Containers: []v1beta1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1beta1.PullAlways}}}, expected: CreatePodUpdate(kubelet.SET, kubelet.HTTPSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "111", Name: "foo" + "-" + hostname, Namespace: "foobar", SelfLink: getSelfLink("foo-"+hostname, kubelet.NamespaceDefault), }, Spec: api.PodSpec{ Host: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{ Name: "1", Image: "foo", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "Always"}}, }, }), }, { desc: "Multiple manifests", manifests: []v1beta1.ContainerManifest{ {Version: "v1beta1", ID: "foo", UUID: "111", Containers: []v1beta1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1beta1.PullAlways}}}, {Version: "v1beta1", ID: "bar", UUID: "222", Containers: []v1beta1.Container{{Name: "1", Image: "foo", ImagePullPolicy: ""}}}, }, expected: CreatePodUpdate(kubelet.SET, kubelet.HTTPSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "111", Name: "foo" + "-" + hostname, Namespace: "foobar", SelfLink: getSelfLink("foo-"+hostname, kubelet.NamespaceDefault), }, Spec: api.PodSpec{ Host: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{ Name: "1", Image: "foo", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "Always"}}, }, }, &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "222", Name: "bar" + "-" + hostname, Namespace: "foobar", SelfLink: getSelfLink("bar-"+hostname, kubelet.NamespaceDefault), }, Spec: api.PodSpec{ Host: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{ Name: "1", Image: "foo", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "IfNotPresent"}}, }, }), }, { desc: "Empty Array", manifests: []v1beta1.ContainerManifest{}, expected: CreatePodUpdate(kubelet.SET, kubelet.HTTPSource), }, } for _, testCase := range testCases { data, err := json.Marshal(testCase.manifests) if err != nil { t.Fatalf("%s: Some weird json problem: %v", testCase.desc, err) } fakeHandler := util.FakeHandler{ StatusCode: 200, ResponseBody: string(data), } testServer := httptest.NewServer(&fakeHandler) defer testServer.Close() ch := make(chan interface{}, 1) c := sourceURL{testServer.URL, hostname, ch, nil} if err := c.extractFromURL(); err != nil { t.Errorf("%s: Unexpected error: %v", testCase.desc, err) continue } update := (<-ch).(kubelet.PodUpdate) for i := range update.Pods { // There's no way to provide namespace in ContainerManifest, so // it will be defaulted. if update.Pods[i].Namespace != kubelet.NamespaceDefault { t.Errorf("Unexpected namespace: %s", update.Pods[0].Namespace) } update.Pods[i].ObjectMeta.Namespace = "foobar" } if !api.Semantic.DeepEqual(testCase.expected, update) { t.Errorf("%s: Expected: %#v, Got: %#v", testCase.desc, testCase.expected, update) } for _, pod := range update.Pods { if errs := validation.ValidatePod(pod); len(errs) != 0 { t.Errorf("%s: Expected no validation errors on %#v, Got %v", testCase.desc, pod, errors.NewAggregate(errs)) } } } }
// resourceName returns node's URL resource name based on resource version. // Uses "minions" as the URL resource name for v1beta1 and v1beta2. func (c *nodes) resourceName() string { if api.PreV1Beta3(c.r.APIVersion()) { return "minions" } return "nodes" }
func getResourceQuotasResoureName() string { if api.PreV1Beta3(testapi.Version()) { return "resourceQuotas" } return "resourcequotas" }
func replicationControllerResourceName() string { if api.PreV1Beta3(testapi.Version()) { return "replicationControllers" } return "replicationcontrollers" }
func TestReadContainerManifestFromFile(t *testing.T) { // ContainerManifest is supported only for pre v1beta3 versions. if !api.PreV1Beta3(testapi.Version()) { return } hostname := "random-test-hostname" var testCases = []struct { desc string fileContents string expected kubelet.PodUpdate }{ { desc: "Manifest", fileContents: fmt.Sprintf(`{ "version": "%s", "uuid": "12345", "id": "test", "containers": [{ "name": "image", "image": "test/image", "imagePullPolicy": "PullAlways"}] }`, testapi.Version()), expected: CreatePodUpdate(kubelet.SET, kubelet.FileSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "test-" + hostname, UID: "12345", Namespace: kubelet.NamespaceDefault, SelfLink: getSelfLink("test-"+hostname, kubelet.NamespaceDefault), }, Spec: api.PodSpec{ Host: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{ Name: "image", Image: "test/image", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "Always"}}, }, }), }, { desc: "Manifest without ID", fileContents: fmt.Sprintf(`{ "version": "%s", "uuid": "12345", "containers": [{ "name": "image", "image": "test/image", "imagePullPolicy": "PullAlways"}] }`, testapi.Version()), expected: CreatePodUpdate(kubelet.SET, kubelet.FileSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "12345-" + hostname, UID: "12345", Namespace: kubelet.NamespaceDefault, SelfLink: getSelfLink("12345-"+hostname, kubelet.NamespaceDefault), }, Spec: api.PodSpec{ Host: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{ Name: "image", Image: "test/image", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "Always"}}, }, }), }, } for _, testCase := range testCases { func() { file := writeTestFile(t, os.TempDir(), "test_pod_config", testCase.fileContents) defer os.Remove(file.Name()) ch := make(chan interface{}) NewSourceFile(file.Name(), hostname, time.Millisecond, ch) select { case got := <-ch: update := got.(kubelet.PodUpdate) for _, pod := range update.Pods { if errs := validation.ValidatePod(pod); len(errs) > 0 { t.Errorf("%s: Invalid pod %#v, %#v", testCase.desc, pod, errs) } } if !api.Semantic.DeepEqual(testCase.expected, update) { t.Errorf("%s: Expected %#v, Got %#v", testCase.desc, testCase.expected, update) } case <-time.After(time.Second): t.Errorf("%s: Expected update, timeout instead", testCase.desc) } }() } }