func TestDecodeNumbers(t *testing.T) { // Start with a valid pod originalJSON := []byte(`{ "kind":"Pod", "apiVersion":"v1", "metadata":{"name":"pod","namespace":"foo"}, "spec":{ "containers":[{"name":"container","image":"container"}], "activeDeadlineSeconds":9223372036854775807 } }`) pod := &api.Pod{} // Decode with structured codec codec, err := testapi.GetCodecForObject(pod) if err != nil { t.Fatalf("unexpected error: %v", err) } err = codec.DecodeInto(originalJSON, pod) if err != nil { t.Fatalf("unexpected error: %v", err) } // ensure pod is valid if errs := validation.ValidatePod(pod); len(errs) > 0 { t.Fatalf("pod should be valid: %v", errs) } // Round-trip with unstructured codec unstructuredObj, err := runtime.UnstructuredJSONScheme.Decode(originalJSON) if err != nil { t.Fatalf("unexpected error: %v", err) } roundtripJSON, err := json.Marshal(unstructuredObj.(*runtime.Unstructured).Object) if err != nil { t.Fatalf("unexpected error: %v", err) } // Decode with structured codec again pod2 := &api.Pod{} err = codec.DecodeInto(roundtripJSON, pod2) if err != nil { t.Fatalf("unexpected error: %v", err) } // ensure pod is still valid if errs := validation.ValidatePod(pod2); len(errs) > 0 { t.Fatalf("pod should be valid: %v", errs) } // ensure round-trip preserved large integers if !reflect.DeepEqual(pod, pod2) { t.Fatalf("Expected\n\t%#v, got \n\t%#v", pod, pod2) } }
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 TestReadPodsFromFileExistAlready(t *testing.T) { hostname := types.NodeName("random-test-hostname") var testCases = getTestCases(hostname) for _, testCase := range testCases { func() { dirName, err := utiltesting.MkTmpdir("file-test") if err != nil { t.Fatalf("unable to create temp dir: %v", err) } defer os.RemoveAll(dirName) file := testCase.writeToFile(dirName, "test_pod_config", t) ch := make(chan interface{}) NewSourceFile(file, hostname, time.Millisecond, ch) select { case got := <-ch: update := got.(kubetypes.PodUpdate) for _, pod := range update.Pods { if errs := validation.ValidatePod(pod); len(errs) > 0 { t.Fatalf("%s: Invalid pod %#v, %#v", testCase.desc, pod, errs) } } if !api.Semantic.DeepEqual(testCase.expected, update) { t.Fatalf("%s: Expected %#v, Got %#v", testCase.desc, testCase.expected, update) } case <-time.After(wait.ForeverTestTimeout): t.Fatalf("%s: Expected update, timeout instead", testCase.desc) } }() } }
func expectUpdate(t *testing.T, ch chan interface{}, testCase *testCase) { timer := time.After(5 * time.Second) for { select { case got := <-ch: update := got.(kubetypes.PodUpdate) for _, pod := range update.Pods { // TODO: remove the conversion when validation is performed on versioned objects. internalPod := &api.Pod{} if err := v1.Convert_v1_Pod_To_api_Pod(pod, internalPod, nil); err != nil { t.Fatalf("%s: Cannot convert pod %#v, %#v", testCase.desc, pod, err) } if errs := validation.ValidatePod(internalPod); len(errs) > 0 { t.Fatalf("%s: Invalid pod %#v, %#v", testCase.desc, internalPod, errs) } } if !api.Semantic.DeepEqual(testCase.expected, update) { t.Fatalf("%s: Expected: %#v, Got: %#v", testCase.desc, testCase.expected, update) } return case <-timer: t.Fatalf("%s: Expected update, timeout instead", testCase.desc) } } }
func validateContainer(c kube.Container) error { validMeta := kube.ObjectMeta{ Name: "valid", Namespace: "object", } pod := kube.Pod{ ObjectMeta: validMeta, Spec: kube.PodSpec{ Containers: []kube.Container{c}, RestartPolicy: kube.RestartPolicyAlways, DNSPolicy: kube.DNSClusterFirst, }, } // fake volumes to allow validation for _, mount := range c.VolumeMounts { volume := kube.Volume{ Name: mount.Name, VolumeSource: kube.VolumeSource{EmptyDir: &kube.EmptyDirVolumeSource{}}, } pod.Spec.Volumes = append(pod.Spec.Volumes, volume) } errList := validation.ValidatePod(&pod) // Remove error for missing image field return errList.Filter(func(e error) bool { return e.Error() == NoImageErrStr }).ToAggregate() }
func tryDecodePodList(data []byte, defaultFn defaultFunc) (parsed bool, pods v1.PodList, err error) { obj, err := runtime.Decode(api.Codecs.UniversalDecoder(), data) 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 } } v1Pods := &v1.PodList{} if err := v1.Convert_api_PodList_To_v1_PodList(newPods, v1Pods, nil); err != nil { return true, pods, err } return true, *v1Pods, err }
func filterInvalidPods(pods []*api.Pod, source string, recorder record.EventRecorder) (filtered []*api.Pod) { names := sets.String{} for i, pod := range pods { var errlist field.ErrorList 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) { // TODO: when validation becomes versioned, this gets a bit // more complicated. errlist = append(errlist, field.Duplicate(field.NewPath("metadata", "name"), pod.Name)) } else { names.Insert(name) } } if len(errlist) > 0 { name := bestPodIdentString(pod) err := errlist.ToAggregate() glog.Warningf("Pod[%d] (%s) from %s failed validation, ignoring: %v", i+1, name, source, err) recorder.Eventf(pod, api.EventTypeWarning, kubecontainer.FailedValidation, "Error validating pod %s from %s, ignoring: %v", name, source, err) continue } filtered = append(filtered, pod) } return }
func filterInvalidPods(pods []*api.Pod, source string, recorder record.EventRecorder) (filtered []*api.Pod) { names := sets.String{} 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 TestCompatibility_v1_Pod(t *testing.T) { // Test "spec.serviceAccount" -> "spec.serviceAccountName" expectedServiceAccount := "my-service-account" input := []byte(fmt.Sprintf(` { "kind":"Pod", "apiVersion":"v1", "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, "spec": { "serviceAccount":"%s", "containers":[{ "name":"my-container-name", "image":"my-container-image" }] } } `, expectedServiceAccount)) t.Log("Testing 1.0.0 v1 migration added in PR #3592") testCompatibility( t, "v1", input, func(obj runtime.Object) field.ErrorList { return validation.ValidatePod(obj.(*api.Pod)) }, map[string]string{ "spec.serviceAccount": expectedServiceAccount, "spec.serviceAccountName": expectedServiceAccount, }, ) }
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 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 validatePod(pod *kube.Pod, ignoreContainers bool) error { errList := validation.ValidatePod(pod) // remove error for no containers if requested if ignoreContainers { errList = errList.Filter(func(e error) bool { return e.Error() == "spec.containers: Required value" }) } meta := pod.GetObjectMeta() if len(meta.GetName()) == 0 && len(meta.GetGenerateName()) > 0 { errList = errList.Filter(func(e error) bool { return e.Error() == "metadata.name: Required value: name or generateName is required" }) } return errList.ToAggregate() }
func expectUpdate(t *testing.T, ch chan interface{}, testCase *testCase) { timer := time.After(5 * time.Second) for { select { case got := <-ch: update := got.(kubetypes.PodUpdate) for _, pod := range update.Pods { if errs := validation.ValidatePod(pod); len(errs) > 0 { t.Fatalf("%s: Invalid pod %#v, %#v", testCase.desc, pod, errs) } } if !api.Semantic.DeepEqual(testCase.expected, update) { t.Fatalf("%s: Expected: %#v, Got: %#v", testCase.desc, testCase.expected, update) } return case <-timer: t.Fatalf("%s: Expected update, timeout instead", testCase.desc) } } }
// tryDecodeSinglePod was copied from pkg/kubelet/config/common.go v1.0.5 func tryDecodeSinglePod(data []byte) (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 := runtime.Decode(api.Codecs.UniversalDecoder(), 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) if errs := validation.ValidatePod(newPod); len(errs) > 0 { err = fmt.Errorf("invalid pod: %v", errs) return true, pod, err } return true, newPod, nil }
// Validate validates a new pod. func (podStrategy) Validate(ctx api.Context, obj runtime.Object) utilvalidation.ErrorList { pod := obj.(*api.Pod) return validation.ValidatePod(pod) }
func TestExtractPodsFromHTTP(t *testing.T) { hostname := "different-value" grace := int64(30) var testCases = []struct { desc string pods runtime.Object expected kubetypes.PodUpdate }{ { desc: "Single pod", pods: &api.Pod{ TypeMeta: unversioned.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}}, SecurityContext: &api.PodSecurityContext{}, }, }, expected: CreatePodUpdate(kubetypes.SET, kubetypes.HTTPSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "111", Name: "foo" + "-" + hostname, Namespace: "mynamespace", Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "111"}, SelfLink: getSelfLink("foo-"+hostname, "mynamespace"), }, Spec: api.PodSpec{ NodeName: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, SecurityContext: &api.PodSecurityContext{}, TerminationGracePeriodSeconds: &grace, Containers: []api.Container{{ Name: "1", Image: "foo", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "Always", }}, }, }), }, { desc: "Multiple pods", pods: &api.PodList{ TypeMeta: unversioned.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}}, SecurityContext: &api.PodSecurityContext{}, }, }, { ObjectMeta: api.ObjectMeta{ Name: "bar", UID: "222", }, Spec: api.PodSpec{ NodeName: hostname, Containers: []api.Container{{Name: "2", Image: "bar", ImagePullPolicy: ""}}, SecurityContext: &api.PodSecurityContext{}, }, }, }, }, expected: CreatePodUpdate(kubetypes.SET, kubetypes.HTTPSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "111", Name: "foo" + "-" + hostname, Namespace: "default", Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "111"}, SelfLink: getSelfLink("foo-"+hostname, kubetypes.NamespaceDefault), }, Spec: api.PodSpec{ NodeName: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, SecurityContext: &api.PodSecurityContext{}, 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", Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "222"}, SelfLink: getSelfLink("bar-"+hostname, kubetypes.NamespaceDefault), }, Spec: api.PodSpec{ NodeName: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, SecurityContext: &api.PodSecurityContext{}, Containers: []api.Container{{ Name: "2", Image: "bar", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "IfNotPresent", }}, }, }), }, } for _, testCase := range testCases { var versionedPods runtime.Object err := testapi.Default.Converter().Convert(&testCase.pods, &versionedPods) if err != nil { t.Fatalf("%s: error in versioning the pods: %s", testCase.desc, err) } data, err := testapi.Default.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, http.Header{}, hostname, ch, nil, 0} if err := c.extractFromURL(); err != nil { t.Errorf("%s: Unexpected error: %v", testCase.desc, err) continue } update := (<-ch).(kubetypes.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, errs.ToAggregate()) } } } }
// ValidateUpdate is the default update validation for an end user. func (podStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { errorList := validation.ValidatePod(obj.(*api.Pod)) return append(errorList, validation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod))...) }
// ValidateUpdate is the default update validation for an end user. func (podStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) utilvalidation.ErrorList { errorList := validation.ValidatePod(obj.(*api.Pod)) return append(errorList, validation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod))...) }
func validateObject(obj runtime.Object) (errors field.ErrorList) { 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) case *api.Endpoints: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateEndpoints(t) case *api.Namespace: errors = validation.ValidateNamespace(t) case *api.Secret: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateSecret(t) case *api.LimitRange: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateLimitRange(t) case *api.ResourceQuota: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateResourceQuota(t) case *extensions.Deployment: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = expvalidation.ValidateDeployment(t) case *extensions.Job: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } // Job needs generateSelector called before validation, and job.Validate does this. // See: https://github.com/kubernetes/kubernetes/issues/20951#issuecomment-187787040 t.ObjectMeta.UID = types.UID("fakeuid") errors = job.Strategy.Validate(nil, t) case *extensions.Ingress: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = expvalidation.ValidateIngress(t) case *extensions.DaemonSet: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = expvalidation.ValidateDaemonSet(t) default: errors = field.ErrorList{} errors = append(errors, field.InternalError(field.NewPath(""), fmt.Errorf("no validation defined for %#v", obj))) } return errors }
// Validate validates a new pod. func (podStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList { pod := obj.(*api.Pod) return validation.ValidatePod(pod) }
func TestReadPodsFromFile(t *testing.T) { nodeName := "random-test-hostname" grace := int64(30) var testCases = []struct { desc string pod runtime.Object expected kubetypes.PodUpdate }{ { desc: "Simple pod", pod: &api.Pod{ TypeMeta: unversioned.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()}}, SecurityContext: &api.PodSecurityContext{}, }, Status: api.PodStatus{ Phase: api.PodPending, }, }, expected: CreatePodUpdate(kubetypes.SET, kubetypes.FileSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "test-" + nodeName, UID: "12345", Namespace: "mynamespace", Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "12345"}, SelfLink: getSelfLink("test-"+nodeName, "mynamespace"), }, Spec: api.PodSpec{ NodeName: nodeName, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, Containers: []api.Container{{ Name: "image", Image: "test/image", TerminationMessagePath: "/dev/termination-log", ImagePullPolicy: "Always", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}}, SecurityContext: &api.PodSecurityContext{}, }, Status: api.PodStatus{ Phase: api.PodPending, }, }), }, } for _, testCase := range testCases { func() { var versionedPod runtime.Object err := testapi.Default.Converter().Convert(&testCase.pod, &versionedPod, nil) if err != nil { t.Fatalf("%s: error in versioning the pod: %v", testCase.desc, err) } fileContents, err := runtime.Encode(testapi.Default.Codec(), 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(), types.NodeName(nodeName), time.Millisecond, ch) select { case got := <-ch: update := got.(kubetypes.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(wait.ForeverTestTimeout): t.Errorf("%s: Expected update, timeout instead", testCase.desc) } }() } }
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) case *api.Endpoints: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateEndpoints(t) case *api.Namespace: errors = validation.ValidateNamespace(t) case *api.Secret: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateSecret(t) case *api.LimitRange: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateLimitRange(t) case *api.ResourceQuota: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateResourceQuota(t) default: return []error{fmt.Errorf("no validation defined for %#v", obj)} } return errors }
// Validate validates a new pod. func (podStrategy) Validate(ctx api.Context, obj runtime.Object) fielderrors.ValidationErrorList { glog.Info("pkg/registry/pod/strategy.go #Validate") pod := obj.(*api.Pod) return validation.ValidatePod(pod) }
func validateObject(obj runtime.Object) (errors field.ErrorList) { 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) case *api.Endpoints: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateEndpoints(t) case *api.Namespace: errors = validation.ValidateNamespace(t) case *api.Secret: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateSecret(t) case *api.LimitRange: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateLimitRange(t) case *api.ResourceQuota: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = validation.ValidateResourceQuota(t) case *extensions.Deployment: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = expvalidation.ValidateDeployment(t) case *extensions.Job: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = expvalidation.ValidateJob(t) case *extensions.Ingress: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = expvalidation.ValidateIngress(t) case *extensions.DaemonSet: if t.Namespace == "" { t.Namespace = api.NamespaceDefault } errors = expvalidation.ValidateDaemonSet(t) default: return field.ErrorList{field.InternalError(field.NewPath(""), fmt.Errorf("no validation defined for %#v", obj))} } return errors }
func TestDecodeNumbers(t *testing.T) { // Start with a valid pod originalJSON := []byte(`{ "kind":"Pod", "apiVersion":"v1", "metadata":{"name":"pod","namespace":"foo"}, "spec":{ "containers":[{"name":"container","image":"container"}], "activeDeadlineSeconds":1000030003 } }`) pod := &api.Pod{} // Decode with structured codec codec, err := testapi.GetCodecForObject(pod) if err != nil { t.Fatalf("unexpected error: %v", err) } err = runtime.DecodeInto(codec, originalJSON, pod) if err != nil { t.Fatalf("unexpected error: %v", err) } // ensure pod is valid if errs := validation.ValidatePod(pod); len(errs) > 0 { t.Fatalf("pod should be valid: %v", errs) } // Round-trip with unstructured codec unstructuredObj, err := runtime.Decode(runtime.UnstructuredJSONScheme, originalJSON) if err != nil { t.Fatalf("unexpected error: %v", err) } roundtripJSON, err := runtime.Encode(runtime.UnstructuredJSONScheme, unstructuredObj) if err != nil { t.Fatalf("unexpected error: %v", err) } // Make sure we serialize back out in int form if !strings.Contains(string(roundtripJSON), `"activeDeadlineSeconds":1000030003`) { t.Errorf("Expected %s, got %s", `"activeDeadlineSeconds":1000030003`, string(roundtripJSON)) } // Decode with structured codec again obj2, err := runtime.Decode(codec, roundtripJSON) if err != nil { t.Fatalf("unexpected error: %v", err) } // ensure pod is still valid pod2, ok := obj2.(*api.Pod) if !ok { t.Fatalf("expected an *api.Pod, got %#v", obj2) } if errs := validation.ValidatePod(pod2); len(errs) > 0 { t.Fatalf("pod should be valid: %v", errs) } // ensure round-trip preserved large integers if !reflect.DeepEqual(pod, pod2) { t.Fatalf("Expected\n\t%#v, got \n\t%#v", pod, pod2) } }
// TestCompatibility_v1_VolumeSource tests that the metadata field in volume // sources gets properly round-tripped func TestCompatibility_v1_VolumeSource(t *testing.T) { // Test volume source compatibility path := "test/volume/source/compat" metadata := []byte(fmt.Sprintf(` { "kind":"Pod", "apiVersion":"v1", "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, "spec": { "containers":[{ "name":"my-container-name", "image":"my-container-image", "ports":[{"containerPort":1,"protocol":"TCP"}] }], "volumes":[{ "name":"a-metadata-volume", "metadata":{"items":[{"name":"%s","fieldRef":{"apiVersion":"v1","fieldPath":"metadata.name"}}]} }] } } `, path)) downward := []byte(fmt.Sprintf(` { "kind":"Pod", "apiVersion":"v1", "metadata":{"name":"my-pod-name", "namespace":"my-pod-namespace"}, "spec": { "containers":[{ "name":"my-container-name", "image":"my-container-image", "ports":[{"containerPort":1,"protocol":"TCP"}] }], "volumes":[{ "name":"a-downwardapi-volume", "downwardAPI":{"items":[{"path":"%s","fieldRef":{"apiVersion":"v1","fieldPath":"metadata.name"}}]} }] } } `, path)) t.Log("Testing 1.0.6 v1 migration added in PR #4663") testCompatibility( t, "v1", metadata, func(obj runtime.Object) field.ErrorList { return validation.ValidatePod(obj.(*api.Pod)) }, map[string]string{ "spec.volumes[0].metadata.items[0].name": path, "spec.volumes[0].downwardAPI.items[0].path": path, }, ) testCompatibility( t, "v1", downward, func(obj runtime.Object) field.ErrorList { return validation.ValidatePod(obj.(*api.Pod)) }, map[string]string{ "spec.volumes[0].metadata.items[0].name": path, "spec.volumes[0].downwardAPI.items[0].path": path, }, ) }