示例#1
0
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)
		}
	}
}
示例#2
0
func toInternalPodOrError(obj runtime.Object) (*api.Pod, error) {
	pod := &api.Pod{}
	switch t := obj.(type) {
	case *v1.Pod:
		if err := v1.Convert_v1_Pod_To_api_Pod(t, pod, nil); err != nil {
			return nil, err
		}
	case *api.Pod:
		pod = t
	default:
		return nil, fmt.Errorf("expect *api.Pod or *v1.Pod, got %v", t)
	}
	return pod, nil
}
示例#3
0
文件: pods.go 项目: nak3/kubernetes
func toInternalPodOrDie(obj runtime.Object) *api.Pod {
	pod := &api.Pod{}
	switch t := obj.(type) {
	case *v1.Pod:
		if err := v1.Convert_v1_Pod_To_api_Pod(t, pod, nil); err != nil {
			panic(err)
		}
	case *api.Pod:
		pod = t
	default:
		panic(fmt.Sprintf("expect *api.Pod or *v1.Pod, got %v", t))
	}
	return pod
}
示例#4
0
// GetFirstPod returns a pod matching the namespace and label selector
// and the number of all pods that match the label selector.
func GetFirstPod(client coreclient.PodsGetter, namespace string, selector labels.Selector, timeout time.Duration, sortBy func([]*v1.Pod) sort.Interface) (*api.Pod, int, error) {
	options := api.ListOptions{LabelSelector: selector}

	podList, err := client.Pods(namespace).List(options)
	if err != nil {
		return nil, 0, err
	}
	pods := []*v1.Pod{}
	for i := range podList.Items {
		pod := podList.Items[i]
		externalPod := &v1.Pod{}
		v1.Convert_api_Pod_To_v1_Pod(&pod, externalPod, nil)
		pods = append(pods, externalPod)
	}
	if len(pods) > 0 {
		sort.Sort(sortBy(pods))
		internalPod := &api.Pod{}
		v1.Convert_v1_Pod_To_api_Pod(pods[0], internalPod, nil)
		return internalPod, len(podList.Items), nil
	}

	// Watch until we observe a pod
	options.ResourceVersion = podList.ResourceVersion
	w, err := client.Pods(namespace).Watch(options)
	if err != nil {
		return nil, 0, err
	}
	defer w.Stop()

	condition := func(event watch.Event) (bool, error) {
		return event.Type == watch.Added || event.Type == watch.Modified, nil
	}
	event, err := watch.Until(timeout, w, condition)
	if err != nil {
		return nil, 0, err
	}
	pod, ok := event.Object.(*api.Pod)
	if !ok {
		return nil, 0, fmt.Errorf("%#v is not a pod event", event)
	}
	return pod, 1, nil
}
示例#5
0
func filterInvalidPods(pods []*v1.Pod, source string, recorder record.EventRecorder) (filtered []*v1.Pod) {
	names := sets.String{}
	for i, pod := range pods {
		var errlist field.ErrorList
		// 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 {
			name := kubecontainer.GetPodFullName(pod)
			glog.Warningf("Pod[%d] (%s) from %s failed to convert to v1, ignoring: %v", i+1, name, source, err)
			recorder.Eventf(pod, v1.EventTypeWarning, "FailedConversion", "Error converting pod %s from %s, ignoring: %v", name, source, err)
			continue
		}
		if errs := validation.ValidatePod(internalPod); 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, v1.EventTypeWarning, events.FailedValidation, "Error validating pod %s from %s, ignoring: %v", name, source, err)
			continue
		}
		filtered = append(filtered, pod)
	}
	return
}
示例#6
0
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 {
					// 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)
				}
			case <-time.After(wait.ForeverTestTimeout):
				t.Fatalf("%s: Expected update, timeout instead", testCase.desc)
			}
		}()
	}
}
示例#7
0
func TestExtractPodsFromHTTP(t *testing.T) {
	nodeName := "different-value"

	grace := int64(30)
	var testCases = []struct {
		desc     string
		pods     runtime.Object
		expected kubetypes.PodUpdate
	}{
		{
			desc: "Single pod",
			pods: &v1.Pod{
				TypeMeta: metav1.TypeMeta{
					Kind:       "Pod",
					APIVersion: "",
				},
				ObjectMeta: metav1.ObjectMeta{
					Name:      "foo",
					UID:       "111",
					Namespace: "mynamespace",
				},
				Spec: v1.PodSpec{
					NodeName:        string(nodeName),
					Containers:      []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways}},
					SecurityContext: &v1.PodSecurityContext{},
					SchedulerName:   api.DefaultSchedulerName,
				},
				Status: v1.PodStatus{
					Phase: v1.PodPending,
				},
			},
			expected: CreatePodUpdate(kubetypes.SET,
				kubetypes.HTTPSource,
				&v1.Pod{
					ObjectMeta: metav1.ObjectMeta{
						UID:         "111",
						Name:        "foo" + "-" + nodeName,
						Namespace:   "mynamespace",
						Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "111"},
						SelfLink:    getSelfLink("foo-"+nodeName, "mynamespace"),
					},
					Spec: v1.PodSpec{
						NodeName:                      nodeName,
						RestartPolicy:                 v1.RestartPolicyAlways,
						DNSPolicy:                     v1.DNSClusterFirst,
						SecurityContext:               &v1.PodSecurityContext{},
						TerminationGracePeriodSeconds: &grace,
						SchedulerName:                 api.DefaultSchedulerName,

						Containers: []v1.Container{{
							Name:  "1",
							Image: "foo",
							TerminationMessagePath: "/dev/termination-log",
							ImagePullPolicy:        "Always",
						}},
					},
					Status: v1.PodStatus{
						Phase: v1.PodPending,
					},
				}),
		},
		{
			desc: "Multiple pods",
			pods: &v1.PodList{
				TypeMeta: metav1.TypeMeta{
					Kind:       "PodList",
					APIVersion: "",
				},
				Items: []v1.Pod{
					{
						ObjectMeta: metav1.ObjectMeta{
							Name: "foo",
							UID:  "111",
						},
						Spec: v1.PodSpec{
							NodeName:        nodeName,
							Containers:      []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways}},
							SecurityContext: &v1.PodSecurityContext{},
							SchedulerName:   api.DefaultSchedulerName,
						},
						Status: v1.PodStatus{
							Phase: v1.PodPending,
						},
					},
					{
						ObjectMeta: metav1.ObjectMeta{
							Name: "bar",
							UID:  "222",
						},
						Spec: v1.PodSpec{
							NodeName:        nodeName,
							Containers:      []v1.Container{{Name: "2", Image: "bar:bartag", ImagePullPolicy: ""}},
							SecurityContext: &v1.PodSecurityContext{},
							SchedulerName:   api.DefaultSchedulerName,
						},
						Status: v1.PodStatus{
							Phase: v1.PodPending,
						},
					},
				},
			},
			expected: CreatePodUpdate(kubetypes.SET,
				kubetypes.HTTPSource,
				&v1.Pod{
					ObjectMeta: metav1.ObjectMeta{
						UID:         "111",
						Name:        "foo" + "-" + nodeName,
						Namespace:   "default",
						Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "111"},
						SelfLink:    getSelfLink("foo-"+nodeName, kubetypes.NamespaceDefault),
					},
					Spec: v1.PodSpec{
						NodeName:                      nodeName,
						RestartPolicy:                 v1.RestartPolicyAlways,
						DNSPolicy:                     v1.DNSClusterFirst,
						TerminationGracePeriodSeconds: &grace,
						SecurityContext:               &v1.PodSecurityContext{},
						SchedulerName:                 api.DefaultSchedulerName,

						Containers: []v1.Container{{
							Name:  "1",
							Image: "foo",
							TerminationMessagePath: "/dev/termination-log",
							ImagePullPolicy:        "Always",
						}},
					},
					Status: v1.PodStatus{
						Phase: v1.PodPending,
					},
				},
				&v1.Pod{
					ObjectMeta: metav1.ObjectMeta{
						UID:         "222",
						Name:        "bar" + "-" + nodeName,
						Namespace:   "default",
						Annotations: map[string]string{kubetypes.ConfigHashAnnotationKey: "222"},
						SelfLink:    getSelfLink("bar-"+nodeName, kubetypes.NamespaceDefault),
					},
					Spec: v1.PodSpec{
						NodeName:                      nodeName,
						RestartPolicy:                 v1.RestartPolicyAlways,
						DNSPolicy:                     v1.DNSClusterFirst,
						TerminationGracePeriodSeconds: &grace,
						SecurityContext:               &v1.PodSecurityContext{},
						SchedulerName:                 api.DefaultSchedulerName,

						Containers: []v1.Container{{
							Name:  "2",
							Image: "bar:bartag",
							TerminationMessagePath: "/dev/termination-log",
							ImagePullPolicy:        "IfNotPresent",
						}},
					},
					Status: v1.PodStatus{
						Phase: v1.PodPending,
					},
				}),
		},
	}

	for _, testCase := range testCases {
		var versionedPods runtime.Object
		err := testapi.Default.Converter().Convert(&testCase.pods, &versionedPods, nil)
		if err != nil {
			t.Fatalf("%s: error in versioning the pods: %s", testCase.desc, err)
		}
		data, err := runtime.Encode(testapi.Default.Codec(), versionedPods)
		if err != nil {
			t.Fatalf("%s: error in encoding the pod: %v", testCase.desc, err)
		}
		fakeHandler := utiltesting.FakeHandler{
			StatusCode:   200,
			ResponseBody: string(data),
		}
		testServer := httptest.NewServer(&fakeHandler)
		defer testServer.Close()
		ch := make(chan interface{}, 1)
		c := sourceURL{testServer.URL, http.Header{}, types.NodeName(nodeName), ch, nil, 0, http.DefaultClient}
		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 {
			// 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.Errorf("%s: Expected no validation errors on %#v, Got %v", testCase.desc, pod, errs.ToAggregate())
			}
		}
	}
}
示例#8
0
func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) {
	// add the internal version to Scheme
	api.AddToScheme(api.Scheme)
	// add the enabled external versions to Scheme
	for _, v := range externalVersions {
		if !registered.IsEnabledVersion(v) {
			glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v)
			continue
		}
		switch v {
		case v1.SchemeGroupVersion:
			v1.AddToScheme(api.Scheme)
		case v1beta3.SchemeGroupVersion:
			v1beta3.AddToScheme(api.Scheme)
		}
	}

	// This is a "fast-path" that avoids reflection for common types. It focuses on the objects that are
	// converted the most in the cluster.
	// TODO: generate one of these for every external API group - this is to prove the impact
	api.Scheme.AddGenericConversionFunc(func(objA, objB interface{}, s conversion.Scope) (bool, error) {
		switch a := objA.(type) {
		case *v1.Pod:
			switch b := objB.(type) {
			case *api.Pod:
				return true, v1.Convert_v1_Pod_To_api_Pod(a, b, s)
			}
		case *api.Pod:
			switch b := objB.(type) {
			case *v1.Pod:
				return true, v1.Convert_api_Pod_To_v1_Pod(a, b, s)
			}

		case *v1.Event:
			switch b := objB.(type) {
			case *api.Event:
				return true, v1.Convert_v1_Event_To_api_Event(a, b, s)
			}
		case *api.Event:
			switch b := objB.(type) {
			case *v1.Event:
				return true, v1.Convert_api_Event_To_v1_Event(a, b, s)
			}

		case *v1.ReplicationController:
			switch b := objB.(type) {
			case *api.ReplicationController:
				return true, v1.Convert_v1_ReplicationController_To_api_ReplicationController(a, b, s)
			}
		case *api.ReplicationController:
			switch b := objB.(type) {
			case *v1.ReplicationController:
				return true, v1.Convert_api_ReplicationController_To_v1_ReplicationController(a, b, s)
			}

		case *v1.Node:
			switch b := objB.(type) {
			case *api.Node:
				return true, v1.Convert_v1_Node_To_api_Node(a, b, s)
			}
		case *api.Node:
			switch b := objB.(type) {
			case *v1.Node:
				return true, v1.Convert_api_Node_To_v1_Node(a, b, s)
			}

		case *v1.Namespace:
			switch b := objB.(type) {
			case *api.Namespace:
				return true, v1.Convert_v1_Namespace_To_api_Namespace(a, b, s)
			}
		case *api.Namespace:
			switch b := objB.(type) {
			case *v1.Namespace:
				return true, v1.Convert_api_Namespace_To_v1_Namespace(a, b, s)
			}

		case *v1.Service:
			switch b := objB.(type) {
			case *api.Service:
				return true, v1.Convert_v1_Service_To_api_Service(a, b, s)
			}
		case *api.Service:
			switch b := objB.(type) {
			case *v1.Service:
				return true, v1.Convert_api_Service_To_v1_Service(a, b, s)
			}

		case *v1.Endpoints:
			switch b := objB.(type) {
			case *api.Endpoints:
				return true, v1.Convert_v1_Endpoints_To_api_Endpoints(a, b, s)
			}
		case *api.Endpoints:
			switch b := objB.(type) {
			case *v1.Endpoints:
				return true, v1.Convert_api_Endpoints_To_v1_Endpoints(a, b, s)
			}
		}
		return false, nil
	})
}
示例#9
0
func TestValidateContainerSecurityContextSuccess(t *testing.T) {
	var notPriv bool = false
	defaultPod := func() *api.Pod {
		return &api.Pod{
			Spec: api.PodSpec{
				SecurityContext: &api.PodSecurityContext{},
				Containers: []api.Container{
					{
						Name: defaultContainerName,
						SecurityContext: &api.SecurityContext{
							// expected to be set by defaulting mechanisms
							Privileged: &notPriv,
							// fill in the rest for test cases
						},
					},
				},
			},
		}
	}

	// success user strat
	userPSP := defaultPSP()
	var uid int64 = 999
	userPSP.Spec.RunAsUser = extensions.RunAsUserStrategyOptions{
		Rule:   extensions.RunAsUserStrategyMustRunAs,
		Ranges: []extensions.IDRange{{Min: uid, Max: uid}},
	}
	userPod := defaultPod()
	userPod.Spec.Containers[0].SecurityContext.RunAsUser = &uid

	// success selinux strat
	seLinuxPSP := defaultPSP()
	seLinuxPSP.Spec.SELinux = extensions.SELinuxStrategyOptions{
		Rule: extensions.SELinuxStrategyMustRunAs,
		SELinuxOptions: &api.SELinuxOptions{
			Level: "foo",
		},
	}
	seLinuxPod := defaultPod()
	seLinuxPod.Spec.Containers[0].SecurityContext.SELinuxOptions = &api.SELinuxOptions{
		Level: "foo",
	}

	appArmorPSP := defaultPSP()
	appArmorPSP.Annotations = map[string]string{
		apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault,
	}
	v1AppArmorPod := defaultV1Pod()
	apparmor.SetProfileName(v1AppArmorPod, defaultContainerName, apparmor.ProfileRuntimeDefault)
	appArmorPod := &api.Pod{}
	v1.Convert_v1_Pod_To_api_Pod(v1AppArmorPod, appArmorPod, nil)

	privPSP := defaultPSP()
	privPSP.Spec.Privileged = true
	privPod := defaultPod()
	var priv bool = true
	privPod.Spec.Containers[0].SecurityContext.Privileged = &priv

	capsPSP := defaultPSP()
	capsPSP.Spec.AllowedCapabilities = []api.Capability{"foo"}
	capsPod := defaultPod()
	capsPod.Spec.Containers[0].SecurityContext.Capabilities = &api.Capabilities{
		Add: []api.Capability{"foo"},
	}

	// pod should be able to request caps that are in the required set even if not specified in the allowed set
	requiredCapsPSP := defaultPSP()
	requiredCapsPSP.Spec.DefaultAddCapabilities = []api.Capability{"foo"}
	requiredCapsPod := defaultPod()
	requiredCapsPod.Spec.Containers[0].SecurityContext.Capabilities = &api.Capabilities{
		Add: []api.Capability{"foo"},
	}

	hostDirPSP := defaultPSP()
	hostDirPSP.Spec.Volumes = []extensions.FSType{extensions.HostPath}
	hostDirPod := defaultPod()
	hostDirPod.Spec.Volumes = []api.Volume{
		{
			Name: "bad volume",
			VolumeSource: api.VolumeSource{
				HostPath: &api.HostPathVolumeSource{},
			},
		},
	}

	hostPortPSP := defaultPSP()
	hostPortPSP.Spec.HostPorts = []extensions.HostPortRange{{Min: 1, Max: 1}}
	hostPortPod := defaultPod()
	hostPortPod.Spec.Containers[0].Ports = []api.ContainerPort{{HostPort: 1}}

	readOnlyRootFSPodFalse := defaultPod()
	readOnlyRootFSFalse := false
	readOnlyRootFSPodFalse.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = &readOnlyRootFSFalse

	readOnlyRootFSPodTrue := defaultPod()
	readOnlyRootFSTrue := true
	readOnlyRootFSPodTrue.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = &readOnlyRootFSTrue

	seccompPSP := defaultPSP()
	seccompPSP.Annotations = map[string]string{
		seccomp.AllowedProfilesAnnotationKey: "foo",
	}

	seccompPod := defaultPod()
	seccompPod.Annotations = map[string]string{
		api.SeccompContainerAnnotationKeyPrefix + seccompPod.Spec.Containers[0].Name: "foo",
	}

	seccompPodInherit := defaultPod()
	seccompPodInherit.Annotations = map[string]string{
		api.SeccompPodAnnotationKey: "foo",
	}

	errorCases := map[string]struct {
		pod *api.Pod
		psp *extensions.PodSecurityPolicy
	}{
		"pass user must run as PSP": {
			pod: userPod,
			psp: userPSP,
		},
		"pass seLinux must run as PSP": {
			pod: seLinuxPod,
			psp: seLinuxPSP,
		},
		"pass AppArmor allowed profiles": {
			pod: appArmorPod,
			psp: appArmorPSP,
		},
		"pass priv validating PSP": {
			pod: privPod,
			psp: privPSP,
		},
		"pass allowed caps validating PSP": {
			pod: capsPod,
			psp: capsPSP,
		},
		"pass required caps validating PSP": {
			pod: requiredCapsPod,
			psp: requiredCapsPSP,
		},
		"pass hostDir validating PSP": {
			pod: hostDirPod,
			psp: hostDirPSP,
		},
		"pass hostPort validating PSP": {
			pod: hostPortPod,
			psp: hostPortPSP,
		},
		"pass read only root fs - nil": {
			pod: defaultPod(),
			psp: defaultPSP(),
		},
		"pass read only root fs - false": {
			pod: readOnlyRootFSPodFalse,
			psp: defaultPSP(),
		},
		"pass read only root fs - true": {
			pod: readOnlyRootFSPodTrue,
			psp: defaultPSP(),
		},
		"pass seccomp container annotation": {
			pod: seccompPod,
			psp: seccompPSP,
		},
		"pass seccomp inherit pod annotation": {
			pod: seccompPodInherit,
			psp: seccompPSP,
		},
	}

	for k, v := range errorCases {
		provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
		if err != nil {
			t.Fatalf("unable to create provider %v", err)
		}
		errs := provider.ValidateContainerSecurityContext(v.pod, &v.pod.Spec.Containers[0], field.NewPath(""))
		if len(errs) != 0 {
			t.Errorf("%s expected validation pass but received errors %v\n%s", k, errs, spew.Sdump(v.pod.ObjectMeta))
			continue
		}
	}
}
示例#10
0
func TestValidateContainerSecurityContextFailures(t *testing.T) {
	// fail user strat
	failUserPSP := defaultPSP()
	var uid int64 = 999
	var badUID int64 = 1
	failUserPSP.Spec.RunAsUser = extensions.RunAsUserStrategyOptions{
		Rule:   extensions.RunAsUserStrategyMustRunAs,
		Ranges: []extensions.IDRange{{Min: uid, Max: uid}},
	}
	failUserPod := defaultPod()
	failUserPod.Spec.Containers[0].SecurityContext.RunAsUser = &badUID

	// fail selinux strat
	failSELinuxPSP := defaultPSP()
	failSELinuxPSP.Spec.SELinux = extensions.SELinuxStrategyOptions{
		Rule: extensions.SELinuxStrategyMustRunAs,
		SELinuxOptions: &api.SELinuxOptions{
			Level: "foo",
		},
	}
	failSELinuxPod := defaultPod()
	failSELinuxPod.Spec.Containers[0].SecurityContext.SELinuxOptions = &api.SELinuxOptions{
		Level: "bar",
	}

	failNilAppArmorPod := defaultPod()
	v1FailInvalidAppArmorPod := defaultV1Pod()
	apparmor.SetProfileName(v1FailInvalidAppArmorPod, defaultContainerName, apparmor.ProfileNamePrefix+"foo")
	failInvalidAppArmorPod := &api.Pod{}
	v1.Convert_v1_Pod_To_api_Pod(v1FailInvalidAppArmorPod, failInvalidAppArmorPod, nil)

	failAppArmorPSP := defaultPSP()
	failAppArmorPSP.Annotations = map[string]string{
		apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault,
	}

	failPrivPod := defaultPod()
	var priv bool = true
	failPrivPod.Spec.Containers[0].SecurityContext.Privileged = &priv

	failCapsPod := defaultPod()
	failCapsPod.Spec.Containers[0].SecurityContext.Capabilities = &api.Capabilities{
		Add: []api.Capability{"foo"},
	}

	failHostPortPod := defaultPod()
	failHostPortPod.Spec.Containers[0].Ports = []api.ContainerPort{{HostPort: 1}}

	readOnlyRootFSPSP := defaultPSP()
	readOnlyRootFSPSP.Spec.ReadOnlyRootFilesystem = true

	readOnlyRootFSPodFalse := defaultPod()
	readOnlyRootFS := false
	readOnlyRootFSPodFalse.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = &readOnlyRootFS

	failSeccompPod := defaultPod()
	failSeccompPod.Annotations = map[string]string{
		api.SeccompContainerAnnotationKeyPrefix + failSeccompPod.Spec.Containers[0].Name: "foo",
	}

	failSeccompPodInheritPodAnnotation := defaultPod()
	failSeccompPodInheritPodAnnotation.Annotations = map[string]string{
		api.SeccompPodAnnotationKey: "foo",
	}

	errorCases := map[string]struct {
		pod           *api.Pod
		psp           *extensions.PodSecurityPolicy
		expectedError string
	}{
		"failUserPSP": {
			pod:           failUserPod,
			psp:           failUserPSP,
			expectedError: "does not match required range",
		},
		"failSELinuxPSP": {
			pod:           failSELinuxPod,
			psp:           failSELinuxPSP,
			expectedError: "does not match required level",
		},
		"failNilAppArmor": {
			pod:           failNilAppArmorPod,
			psp:           failAppArmorPSP,
			expectedError: "AppArmor profile must be set",
		},
		"failInvalidAppArmor": {
			pod:           failInvalidAppArmorPod,
			psp:           failAppArmorPSP,
			expectedError: "localhost/foo is not an allowed profile. Allowed values: \"runtime/default\"",
		},
		"failPrivPSP": {
			pod:           failPrivPod,
			psp:           defaultPSP(),
			expectedError: "Privileged containers are not allowed",
		},
		"failCapsPSP": {
			pod:           failCapsPod,
			psp:           defaultPSP(),
			expectedError: "capability may not be added",
		},
		"failHostPortPSP": {
			pod:           failHostPortPod,
			psp:           defaultPSP(),
			expectedError: "Host port 1 is not allowed to be used.  Allowed ports: []",
		},
		"failReadOnlyRootFS - nil": {
			pod:           defaultPod(),
			psp:           readOnlyRootFSPSP,
			expectedError: "ReadOnlyRootFilesystem may not be nil and must be set to true",
		},
		"failReadOnlyRootFS - false": {
			pod:           readOnlyRootFSPodFalse,
			psp:           readOnlyRootFSPSP,
			expectedError: "ReadOnlyRootFilesystem must be set to true",
		},
		"failSeccompContainerAnnotation": {
			pod:           failSeccompPod,
			psp:           defaultPSP(),
			expectedError: "Forbidden: seccomp may not be set",
		},
		"failSeccompContainerPodAnnotation": {
			pod:           failSeccompPodInheritPodAnnotation,
			psp:           defaultPSP(),
			expectedError: "Forbidden: seccomp may not be set",
		},
	}

	for k, v := range errorCases {
		provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
		if err != nil {
			t.Fatalf("unable to create provider %v", err)
		}
		errs := provider.ValidateContainerSecurityContext(v.pod, &v.pod.Spec.Containers[0], field.NewPath(""))
		if len(errs) == 0 {
			t.Errorf("%s expected validation failure but did not receive errors", k)
			continue
		}
		if !strings.Contains(errs[0].Error(), v.expectedError) {
			t.Errorf("%s received unexpected error %v", k, errs)
		}
	}
}