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 TestExtractPodsFromHTTP(t *testing.T) { hostname := "different-value" grace := int64(30) var testCases = []struct { desc string pods runtime.Object expected kubelet.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}}, }, }, expected: CreatePodUpdate(kubelet.SET, kubelet.HTTPSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "111", Name: "foo" + "-" + hostname, Namespace: "mynamespace", Annotations: map[string]string{kubelet.ConfigHashAnnotationKey: "111"}, SelfLink: getSelfLink("foo-"+hostname, "mynamespace"), }, Spec: api.PodSpec{ NodeName: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, 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}}, }, }, { 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", Annotations: map[string]string{kubelet.ConfigHashAnnotationKey: "111"}, SelfLink: getSelfLink("foo-"+hostname, kubelet.NamespaceDefault), }, Spec: api.PodSpec{ NodeName: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, 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{kubelet.ConfigHashAnnotationKey: "222"}, SelfLink: getSelfLink("bar-"+hostname, kubelet.NamespaceDefault), }, Spec: api.PodSpec{ NodeName: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, 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).(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)) } } } }
func TestReadPodsFromFile(t *testing.T) { hostname := "random-test-hostname" grace := int64(30) var testCases = []struct { desc string pod runtime.Object expected kubelet.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()}}, }, }, expected: CreatePodUpdate(kubelet.SET, kubelet.FileSource, &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "test-" + hostname, UID: "12345", Namespace: "mynamespace", Annotations: map[string]string{kubelet.ConfigHashAnnotationKey: "12345"}, SelfLink: getSelfLink("test-"+hostname, "mynamespace"), }, Spec: api.PodSpec{ NodeName: hostname, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, 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.Default.Converter().Convert(&testCase.pod, &versionedPod) if err != nil { t.Fatalf("%s: error in versioning the pod: %v", testCase.desc, err) } fileContents, err := testapi.Default.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(util.ForeverTestTimeout): 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 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) 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 []error{fmt.Errorf("no validation defined for %#v", obj)} } return errors }