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 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 }
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 }
// 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 }
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 }
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) } }() } }
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()) } } } }
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 }) }
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: ¬Priv, // 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 } } }
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) } } }