func filterInvalidPods(pods []*api.Pod, source string, recorder record.EventRecorder) (filtered []*api.Pod) { names := util.StringSet{} for i, pod := range pods { var errlist []error if errs := validation.ValidatePod(pod); len(errs) != 0 { errlist = append(errlist, errs...) // If validation fails, don't trust it any further - // even Name could be bad. } else { name := kubecontainer.GetPodFullName(pod) if names.Has(name) { errlist = append(errlist, fielderrors.NewFieldDuplicate("name", pod.Name)) } else { names.Insert(name) } } if len(errlist) > 0 { name := bestPodIdentString(pod) err := utilerrors.NewAggregate(errlist) glog.Warningf("Pod[%d] (%s) from %s failed validation, ignoring: %v", i+1, name, source, err) recorder.Eventf(pod, "failedValidation", "Error validating pod %s from %s, ignoring: %v", name, source, err) continue } filtered = append(filtered, pod) } return }
func tryDecodePodList(data []byte, defaultFn defaultFunc) (parsed bool, pods api.PodList, err error) { json, err := utilyaml.ToJSON(data) if err != nil { return false, api.PodList{}, err } obj, err := api.Scheme.Decode(json) if err != nil { return false, pods, err } // Check whether the object could be converted to list of pods. if _, ok := obj.(*api.PodList); !ok { err = fmt.Errorf("invalid pods list: %+v", obj) return false, pods, err } newPods := obj.(*api.PodList) // Apply default values and validate pods. for i := range newPods.Items { newPod := &newPods.Items[i] if err = defaultFn(newPod); err != nil { return true, pods, err } if errs := validation.ValidatePod(newPod); len(errs) > 0 { err = fmt.Errorf("invalid pod: %v", errs) return true, pods, err } } return true, *newPods, err }
func tryDecodeSinglePod(data []byte, defaultFn defaultFunc) (parsed bool, pod *api.Pod, err error) { // JSON is valid YAML, so this should work for everything. json, err := utilyaml.ToJSON(data) if err != nil { return false, nil, err } obj, err := api.Scheme.Decode(json) if err != nil { return false, pod, err } // Check whether the object could be converted to single pod. if _, ok := obj.(*api.Pod); !ok { err = fmt.Errorf("invalid pod: %+v", obj) return false, pod, err } newPod := obj.(*api.Pod) // Apply default values and validate the pod. if err = defaultFn(newPod); err != nil { return true, pod, err } if errs := validation.ValidatePod(newPod); len(errs) > 0 { err = fmt.Errorf("invalid pod: %v", errs) return true, pod, err } return true, newPod, nil }
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) } } }
func (rs *RegistryStorage) Update(obj interface{}) (<-chan interface{}, error) { pod := obj.(*api.Pod) if errs := validation.ValidatePod(pod); len(errs) > 0 { return nil, errors.NewInvalid("pod", pod.ID, errs) } return apiserver.MakeAsync(func() (interface{}, error) { if err := rs.registry.UpdatePod(*pod); err != nil { return nil, err } return rs.registry.GetPod(pod.ID) }), nil }
func (rs *REST) Update(obj runtime.Object) (<-chan runtime.Object, error) { pod := obj.(*api.Pod) if errs := validation.ValidatePod(pod); len(errs) > 0 { return nil, errors.NewInvalid("pod", pod.ID, errs) } return apiserver.MakeAsync(func() (runtime.Object, error) { if err := rs.registry.UpdatePod(pod); err != nil { return nil, err } return rs.registry.GetPod(pod.ID) }), nil }
func validateObject(path string, obj runtime.Object, t *testing.T) { // if an object requires a namespace server side, be sure that it is filled in for validation if validation.HasObjectMeta(obj) { namespaceRequired, err := validation.GetRequiresNamespace(obj) if err != nil { t.Errorf("Expected no error, Got %v", err) return } if namespaceRequired { objectMeta, err := kapi.ObjectMetaFor(obj) if err != nil { t.Errorf("Expected no error, Got %v", err) return } objectMeta.Namespace = kapi.NamespaceDefault } } switch typedObj := obj.(type) { case *kapi.Pod: if errors := kvalidation.ValidatePod(typedObj); len(errors) > 0 { t.Errorf("%s did not validate correctly: %v", path, errors) } case *kapi.Service: if errors := kvalidation.ValidateService(typedObj); len(errors) > 0 { t.Errorf("%s did not validate correctly: %v", path, errors) } case *kapi.List, *imageapi.ImageStreamList: if list, err := runtime.ExtractList(typedObj); err == nil { runtime.DecodeList(list, kapi.Scheme) for i := range list { validateObject(path, list[i], t) } } else { t.Errorf("Expected no error, Got %v", err) } default: if errors := validation.Validator.Validate(obj); len(errors) > 0 { t.Errorf("%s with %v did not validate correctly: %v", path, reflect.TypeOf(obj), errors) } } }
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) { pod := obj.(*api.Pod) if !api.ValidNamespace(ctx, &pod.ObjectMeta) { return nil, errors.NewConflict("pod", pod.Namespace, fmt.Errorf("Pod.Namespace does not match the provided context")) } if errs := validation.ValidatePod(pod); len(errs) > 0 { return nil, errors.NewInvalid("pod", pod.Name, errs) } return apiserver.MakeAsync(func() (runtime.Object, error) { if err := rs.registry.UpdatePod(ctx, pod); err != nil { return nil, err } return rs.registry.GetPod(ctx, pod.Name) }), nil }
func validateObject(obj runtime.Object) (errors []error) { switch t := obj.(type) { case *api.ReplicationController: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateReplicationController(t) case *api.ReplicationControllerList: for i := range t.Items { errors = append(errors, validateObject(&t.Items[i])...) } case *api.Service: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateService(t) case *api.ServiceList: for i := range t.Items { errors = append(errors, validateObject(&t.Items[i])...) } case *api.Pod: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidatePod(t) case *api.PodList: for i := range t.Items { errors = append(errors, validateObject(&t.Items[i])...) } case *api.PersistentVolume: errors = validation.ValidatePersistentVolume(t) case *api.PersistentVolumeClaim: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidatePersistentVolumeClaim(t) case *api.PodTemplate: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidatePodTemplate(t) default: return []error{fmt.Errorf("no validation defined for %#v", obj)} } return errors }
func TestCompatibility_v1_Pod(t *testing.T) { // Test "spec.host" -> "spec.nodeName" expectedHost := "my-host" // Test "spec.serviceAccount" -> "spec.serviceAccountName" expectedServiceAccount := "my-service-account" // Test "tcp" protocol gets converted to "TCP" and validated originalProtocol := "tcp" expectedProtocol := "TCP" input := []byte(fmt.Sprintf(` { "kind":"Pod", "apiVersion":"v1", "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, "spec": { "host":"%s", "serviceAccount":"%s", "containers":[{ "name":"my-container-name", "image":"my-container-image", "ports":[{"containerPort":1,"protocol":"%s"}] }] } } `, expectedHost, expectedServiceAccount, originalProtocol)) t.Log("Testing 1.0.0 v1 migration added in PR #3592") testCompatibility( t, "v1", input, func(obj runtime.Object) fielderrors.ValidationErrorList { return validation.ValidatePod(obj.(*api.Pod)) }, map[string]string{ "spec.host": expectedHost, "spec.nodeName": expectedHost, "spec.serviceAccount": expectedServiceAccount, "spec.serviceAccountName": expectedServiceAccount, "spec.containers[0].ports[0].protocol": expectedProtocol, }, ) }
func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { pod := obj.(*api.Pod) if len(pod.ID) == 0 { pod.ID = uuid.NewUUID().String() } pod.DesiredState.Manifest.ID = pod.ID if errs := validation.ValidatePod(pod); len(errs) > 0 { return nil, errors.NewInvalid("pod", pod.ID, errs) } pod.CreationTimestamp = util.Now() return apiserver.MakeAsync(func() (interface{}, error) { if err := rs.registry.CreatePod(*pod); err != nil { return nil, err } return rs.registry.GetPod(pod.ID) }), nil }
func (rs *REST) Create(obj runtime.Object) (<-chan runtime.Object, error) { pod := obj.(*api.Pod) pod.DesiredState.Manifest.UUID = uuid.NewUUID().String() if len(pod.ID) == 0 { pod.ID = pod.DesiredState.Manifest.UUID } pod.DesiredState.Manifest.ID = pod.ID if errs := validation.ValidatePod(pod); len(errs) > 0 { return nil, errors.NewInvalid("pod", pod.ID, errs) } pod.CreationTimestamp = util.Now() return apiserver.MakeAsync(func() (runtime.Object, error) { if err := rs.registry.CreatePod(pod); err != nil { return nil, err } return rs.registry.GetPod(pod.ID) }), nil }
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) { pod := obj.(*api.Pod) if !api.ValidNamespace(ctx, &pod.ObjectMeta) { return nil, errors.NewConflict("pod", pod.Namespace, fmt.Errorf("Pod.Namespace does not match the provided context")) } api.FillObjectMetaSystemFields(ctx, &pod.ObjectMeta) if len(pod.Name) == 0 { // TODO properly handle auto-generated names. // See https://github.com/GoogleCloudPlatform/kubernetes/issues/148 170 & 1135 pod.Name = string(pod.UID) } if errs := validation.ValidatePod(pod); len(errs) > 0 { return nil, errors.NewInvalid("pod", pod.Name, errs) } return apiserver.MakeAsync(func() (runtime.Object, error) { if err := rs.registry.CreatePod(ctx, pod); err != nil { return nil, err } return rs.registry.GetPod(ctx, pod.Name) }), nil }
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) { pod := obj.(*api.Pod) if !api.ValidNamespace(ctx, &pod.ObjectMeta) { return nil, errors.NewConflict("pod", pod.Namespace, fmt.Errorf("Pod.Namespace does not match the provided context")) } pod.DesiredState.Manifest.UUID = util.NewUUID().String() if len(pod.Name) == 0 { pod.Name = pod.DesiredState.Manifest.UUID } pod.DesiredState.Manifest.ID = pod.Name if errs := validation.ValidatePod(pod); len(errs) > 0 { return nil, errors.NewInvalid("pod", pod.Name, errs) } pod.CreationTimestamp = util.Now() return apiserver.MakeAsync(func() (runtime.Object, error) { if err := rs.registry.CreatePod(ctx, pod); err != nil { return nil, err } return rs.registry.GetPod(ctx, pod.Name) }), nil }
func validateObject(obj runtime.Object) (errors []error) { ctx := api.NewDefaultContext() switch t := obj.(type) { case *api.ReplicationController: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateReplicationController(t) case *api.ReplicationControllerList: for i := range t.Items { errors = append(errors, validateObject(&t.Items[i])...) } case *api.Service: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } api.ValidNamespace(ctx, &t.ObjectMeta) errors = validation.ValidateService(t) case *api.ServiceList: for i := range t.Items { errors = append(errors, validateObject(&t.Items[i])...) } case *api.Pod: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } api.ValidNamespace(ctx, &t.ObjectMeta) errors = validation.ValidatePod(t) case *api.PodList: for i := range t.Items { errors = append(errors, validateObject(&t.Items[i])...) } default: return []error{fmt.Errorf("no validation defined for %#v", obj)} } return errors }
func TestExtractFromDir(t *testing.T) { 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, ch} err = c.extractFromPath() if err != nil { t.Fatalf("Unexpected error: %v", err) } update := (<-ch).(kubelet.PodUpdate) for i := range update.Pods { // Pod name is generated with hash and is unique. Skip the comparision // here by setting it to a simple value. update.Pods[i].Name = manifests[i].ID update.Pods[i].SelfLink = "" } expected := CreatePodUpdate(kubelet.SET, kubelet.FileSource, pods...) for i := range expected.Pods { // Pod name is generated with hash and is unique. Skip the comparision // here by setting it to a simple value. expected.Pods[i].Name = manifests[i].ID } 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 i := range update.Pods { if errs := validation.ValidatePod(&update.Pods[i]); len(errs) != 0 { t.Errorf("Expected no validation errors on %#v, Got %q", update.Pods[i], errs) } } }
// Validate validates a new pod. func (podStrategy) Validate(obj runtime.Object) errors.ValidationErrorList { pod := obj.(*api.Pod) return validation.ValidatePod(pod) }
func TestReadPodsFromFile(t *testing.T) { hostname := "random-test-hostname" var testCases = []struct { desc string pod runtime.Object expected kubelet.PodUpdate }{ { desc: "Simple pod", pod: &api.Pod{ TypeMeta: api.TypeMeta{ Kind: "Pod", APIVersion: "", }, ObjectMeta: api.ObjectMeta{ Name: "test", UID: "12345", Namespace: "mynamespace", }, Spec: api.PodSpec{ Containers: []api.Container{{Name: "image", Image: "test/image", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}}, }, }, expected: CreatePodUpdate(kubelet.SET, kubelet.FileSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "test-" + hostname, UID: "12345", Namespace: "mynamespace", SelfLink: getSelfLink("test-"+hostname, "mynamespace"), }, Spec: api.PodSpec{ NodeName: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{ Name: "image", Image: "test/image", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "IfNotPresent", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}}, }, }), }, { desc: "Pod without ID", pod: &api.Pod{ TypeMeta: api.TypeMeta{ Kind: "Pod", APIVersion: "", }, ObjectMeta: api.ObjectMeta{ // No name UID: "12345", }, Spec: api.PodSpec{ Containers: []api.Container{{Name: "image", Image: "test/image", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}}, }, }, 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{ NodeName: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{ Name: "image", Image: "test/image", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "IfNotPresent", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}}, }, }), }, } for _, testCase := range testCases { func() { var versionedPod runtime.Object err := testapi.Converter().Convert(&testCase.pod, &versionedPod) if err != nil { t.Fatalf("error in versioning the pod: %s", testCase.desc, err) } fileContents, err := testapi.Codec().Encode(versionedPod) if err != nil { t.Fatalf("%s: error in encoding the pod: %v", testCase.desc, err) } file := writeTestFile(t, os.TempDir(), "test_pod_config", string(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) } }() } }
// ValidateUpdate is the default update validation for an end user. func (podStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fielderrors.ValidationErrorList { errorList := validation.ValidatePod(obj.(*api.Pod)) return append(errorList, validation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod))...) }
// Validate validates a new pod. func (podStrategy) Validate(ctx api.Context, obj runtime.Object) fielderrors.ValidationErrorList { pod := obj.(*api.Pod) return validation.ValidatePod(pod) }
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) } }() } }
func TestExtractFromHTTP(t *testing.T) { hostname, _ := os.Hostname() hostname = strings.ToLower(hostname) 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: "/api/v1beta1/pods/foo", }, Spec: api.PodSpec{ 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: api.ContainerManifest{Version: "v1beta1", UUID: "111"}, expected: CreatePodUpdate(kubelet.SET, kubelet.HTTPSource, api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "111", Name: "111" + "-" + hostname, Namespace: "foobar", }, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, }, }), }, { 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: "/api/v1beta1/pods/foo", }, Spec: api.PodSpec{ 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: "/api/v1beta1/pods/foo", }, Spec: api.PodSpec{ 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: "/api/v1beta1/pods/bar", }, Spec: api.PodSpec{ 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, 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 i := range update.Pods { if errs := validation.ValidatePod(&update.Pods[i]); len(errs) != 0 { t.Errorf("%s: Expected no validation errors on %#v, Got %v", testCase.desc, update.Pods[i], errors.NewAggregate(errs)) } } } }
func TestExtractPodsFromHTTP(t *testing.T) { hostname := "different-value" var testCases = []struct { desc string pods runtime.Object expected kubelet.PodUpdate }{ { desc: "Single pod", pods: &api.Pod{ TypeMeta: api.TypeMeta{ Kind: "Pod", APIVersion: "", }, ObjectMeta: api.ObjectMeta{ Name: "foo", UID: "111", Namespace: "mynamespace", }, Spec: api.PodSpec{ NodeName: hostname, Containers: []api.Container{{Name: "1", Image: "foo", ImagePullPolicy: api.PullAlways}}, }, }, expected: CreatePodUpdate(kubelet.SET, kubelet.HTTPSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "111", Name: "foo" + "-" + hostname, Namespace: "mynamespace", SelfLink: getSelfLink("foo-"+hostname, "mynamespace"), }, Spec: api.PodSpec{ NodeName: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{ Name: "1", Image: "foo", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "Always", }}, }, }), }, { desc: "Multiple pods", pods: &api.PodList{ TypeMeta: api.TypeMeta{ Kind: "PodList", APIVersion: "", }, Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{ Name: "foo", UID: "111", }, Spec: api.PodSpec{ NodeName: hostname, Containers: []api.Container{{Name: "1", Image: "foo", ImagePullPolicy: api.PullAlways}}, }, }, { ObjectMeta: api.ObjectMeta{ Name: "bar", UID: "222", }, Spec: api.PodSpec{ NodeName: hostname, Containers: []api.Container{{Name: "2", Image: "bar", ImagePullPolicy: ""}}, }, }, }, }, expected: CreatePodUpdate(kubelet.SET, kubelet.HTTPSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "111", Name: "foo" + "-" + hostname, Namespace: "default", SelfLink: getSelfLink("foo-"+hostname, kubelet.NamespaceDefault), }, Spec: api.PodSpec{ NodeName: 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: "default", SelfLink: getSelfLink("bar-"+hostname, kubelet.NamespaceDefault), }, Spec: api.PodSpec{ NodeName: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{ Name: "2", Image: "bar", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "IfNotPresent", }}, }, }), }, } for _, testCase := range testCases { var versionedPods runtime.Object err := testapi.Converter().Convert(&testCase.pods, &versionedPods) if err != nil { t.Fatalf("error in versioning the pods: %s", testCase.desc, err) } data, err := testapi.Codec().Encode(versionedPods) if err != nil { t.Fatalf("%s: error in encoding the pod: %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) 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)) } } } }