// TestAdmission func TestAdmission(t *testing.T) { namespaceObj := &api.Namespace{ ObjectMeta: api.ObjectMeta{ Name: "test", Namespace: "", }, Status: api.NamespaceStatus{ Phase: api.NamespaceActive, }, } store := cache.NewStore(cache.IndexFuncToKeyFuncAdapter(cache.MetaNamespaceIndexFunc)) store.Add(namespaceObj) mockClient := &testclient.Fake{} lfhandler := NewLifecycle(mockClient).(*lifecycle) lfhandler.store = store handler := admission.NewChainHandler(lfhandler) pod := api.Pod{ ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespaceObj.Namespace}, Spec: api.PodSpec{ Volumes: []api.Volume{{Name: "vol"}}, Containers: []api.Container{{Name: "ctr", Image: "image"}}, }, } err := handler.Admit(admission.NewAttributesRecord(&pod, "Pod", pod.Namespace, pod.Name, "pods", "", admission.Create, nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler: %v", err) } // change namespace state to terminating namespaceObj.Status.Phase = api.NamespaceTerminating store.Add(namespaceObj) // verify create operations in the namespace cause an error err = handler.Admit(admission.NewAttributesRecord(&pod, "Pod", pod.Namespace, pod.Name, "pods", "", admission.Create, nil)) if err == nil { t.Errorf("Expected error rejecting creates in a namespace when it is terminating") } // verify update operations in the namespace can proceed err = handler.Admit(admission.NewAttributesRecord(&pod, "Pod", pod.Namespace, pod.Name, "pods", "", admission.Update, nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler: %v", err) } // verify delete operations in the namespace can proceed err = handler.Admit(admission.NewAttributesRecord(nil, "Pod", pod.Namespace, pod.Name, "pods", "", admission.Delete, nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler: %v", err) } // verify delete of namespace default can never proceed err = handler.Admit(admission.NewAttributesRecord(nil, "Namespace", "", api.NamespaceDefault, "namespaces", "", admission.Delete, nil)) if err == nil { t.Errorf("Expected an error that this namespace can never be deleted") } // verify delete of namespace other than default can proceed err = handler.Admit(admission.NewAttributesRecord(nil, "Namespace", "", "other", "namespaces", "", admission.Delete, nil)) if err != nil { t.Errorf("Did not expect an error %v", err) } }
// TestPodAdmission verifies various scenarios involving pod/project/global node label selectors func TestPodAdmission(t *testing.T) { mockClient := &testclient.Fake{} project := &kapi.Namespace{ ObjectMeta: kapi.ObjectMeta{ Name: "testProject", Namespace: "", }, } projectStore := cache.NewStore(cache.IndexFuncToKeyFuncAdapter(cache.MetaNamespaceIndexFunc)) projectStore.Add(project) handler := &podNodeEnvironment{client: mockClient} pod := &kapi.Pod{ ObjectMeta: kapi.ObjectMeta{Name: "testPod"}, } tests := []struct { defaultNodeSelector string projectNodeSelector string podNodeSelector map[string]string mergedNodeSelector map[string]string ignoreProjectNodeSelector bool admit bool testName string }{ { defaultNodeSelector: "", podNodeSelector: map[string]string{}, mergedNodeSelector: map[string]string{}, ignoreProjectNodeSelector: true, admit: true, testName: "No node selectors", }, { defaultNodeSelector: "infra = false", podNodeSelector: map[string]string{}, mergedNodeSelector: map[string]string{"infra": "false"}, ignoreProjectNodeSelector: true, admit: true, testName: "Default node selector and no conflicts", }, { defaultNodeSelector: "", projectNodeSelector: "infra = false", podNodeSelector: map[string]string{}, mergedNodeSelector: map[string]string{"infra": "false"}, admit: true, testName: "Project node selector and no conflicts", }, { defaultNodeSelector: "infra = false", projectNodeSelector: "", podNodeSelector: map[string]string{}, mergedNodeSelector: map[string]string{}, admit: true, testName: "Empty project node selector and no conflicts", }, { defaultNodeSelector: "infra = false", projectNodeSelector: "infra=true", podNodeSelector: map[string]string{}, mergedNodeSelector: map[string]string{"infra": "true"}, admit: true, testName: "Default and project node selector, no conflicts", }, { defaultNodeSelector: "infra = false", projectNodeSelector: "infra=true", podNodeSelector: map[string]string{"env": "test"}, mergedNodeSelector: map[string]string{"infra": "true", "env": "test"}, admit: true, testName: "Project and pod node selector, no conflicts", }, { defaultNodeSelector: "env = test", projectNodeSelector: "infra=true", podNodeSelector: map[string]string{"infra": "false"}, mergedNodeSelector: map[string]string{"infra": "false"}, admit: false, testName: "Conflicting pod and project node selector, one label", }, { defaultNodeSelector: "env=dev", projectNodeSelector: "infra=false, env = test", podNodeSelector: map[string]string{"env": "dev", "color": "blue"}, mergedNodeSelector: map[string]string{"env": "dev", "color": "blue"}, admit: false, testName: "Conflicting pod and project node selector, multiple labels", }, } for _, test := range tests { projectcache.FakeProjectCache(mockClient, projectStore, test.defaultNodeSelector) if !test.ignoreProjectNodeSelector { project.ObjectMeta.Annotations = map[string]string{"openshift.io/node-selector": test.projectNodeSelector} } pod.Spec = kapi.PodSpec{NodeSelector: test.podNodeSelector} err := handler.Admit(admission.NewAttributesRecord(pod, "Pod", "namespace", project.ObjectMeta.Name, "pods", "", admission.Create, nil)) if test.admit && err != nil { t.Errorf("Test: %s, expected no error but got: %s", test.testName, err) } else if !test.admit && err == nil { t.Errorf("Test: %s, expected an error", test.testName) } if !labelselector.Equals(test.mergedNodeSelector, pod.Spec.NodeSelector) { t.Errorf("Test: %s, expected: %s but got: %s", test.testName, test.mergedNodeSelector, pod.Spec.NodeSelector) } } }
// TestAdmissionLifecycle verifies you cannot create Origin content if namespace is terminating func TestAdmissionLifecycle(t *testing.T) { namespaceObj := &kapi.Namespace{ ObjectMeta: kapi.ObjectMeta{ Name: "test", Namespace: "", }, Status: kapi.NamespaceStatus{ Phase: kapi.NamespaceActive, }, } store := cache.NewStore(cache.IndexFuncToKeyFuncAdapter(cache.MetaNamespaceIndexFunc)) store.Add(namespaceObj) mockClient := &testclient.Fake{} projectcache.FakeProjectCache(mockClient, store, "") handler := &lifecycle{client: mockClient} build := &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{Name: "buildid", Namespace: "other"}, Spec: buildapi.BuildSpec{ Source: buildapi.BuildSource{ Type: buildapi.BuildSourceGit, Git: &buildapi.GitBuildSource{ URI: "http://github.com/my/repository", }, ContextDir: "context", }, Strategy: buildapi.BuildStrategy{ Type: buildapi.DockerBuildStrategyType, DockerStrategy: &buildapi.DockerBuildStrategy{}, }, Output: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "DockerImage", Name: "repository/data", }, }, }, Status: buildapi.BuildStatus{ Phase: buildapi.BuildPhaseNew, }, } err := handler.Admit(admission.NewAttributesRecord(build, "Build", build.Namespace, "name", "builds", "", "CREATE", nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler: %v", err) } // change namespace state to terminating namespaceObj.Status.Phase = kapi.NamespaceTerminating store.Add(namespaceObj) // verify create operations in the namespace cause an error err = handler.Admit(admission.NewAttributesRecord(build, "Build", build.Namespace, "name", "builds", "", "CREATE", nil)) if err == nil { t.Errorf("Expected error rejecting creates in a namespace when it is terminating") } // verify update operations in the namespace can proceed err = handler.Admit(admission.NewAttributesRecord(build, "Build", build.Namespace, "name", "builds", "", "UPDATE", nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler: %v", err) } // verify delete operations in the namespace can proceed err = handler.Admit(admission.NewAttributesRecord(nil, "Build", build.Namespace, "name", "builds", "", "DELETE", nil)) if err != nil { t.Errorf("Unexpected error returned from admission handler: %v", err) } }