Merge pull request #28490 from wojtek-t/integration_namespace_3
Automatic merge from submit-queue Migrate most of the remaining integration tests to run in dedicated namespace (when possible).
This commit is contained in:
		| @@ -30,6 +30,7 @@ type NodeInterface interface { | ||||
| 	Create(node *api.Node) (*api.Node, error) | ||||
| 	List(opts api.ListOptions) (*api.NodeList, error) | ||||
| 	Delete(name string) error | ||||
| 	DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error | ||||
| 	Update(*api.Node) (*api.Node, error) | ||||
| 	UpdateStatus(*api.Node) (*api.Node, error) | ||||
| 	Watch(opts api.ListOptions) (watch.Interface, error) | ||||
| @@ -76,6 +77,16 @@ func (c *nodes) Delete(name string) error { | ||||
| 	return c.r.Delete().Resource(c.resourceName()).Name(name).Do().Error() | ||||
| } | ||||
|  | ||||
| // DeleteCollection deletes a collection of nodes. | ||||
| func (c *nodes) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { | ||||
| 	return c.r.Delete(). | ||||
| 		Resource(c.resourceName()). | ||||
| 		VersionedParams(&listOptions, api.ParameterCodec). | ||||
| 		Body(options). | ||||
| 		Do(). | ||||
| 		Error() | ||||
| } | ||||
|  | ||||
| // Update updates an existing node. | ||||
| func (c *nodes) Update(node *api.Node) (*api.Node, error) { | ||||
| 	result := &api.Node{} | ||||
|   | ||||
| @@ -68,6 +68,11 @@ func (c *FakeNodes) Delete(name string) error { | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (c *FakeNodes) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { | ||||
| 	_, err := c.Fake.Invokes(NewRootDeleteCollectionAction("nodes", listOptions), &api.NodeList{}) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (c *FakeNodes) Watch(opts api.ListOptions) (watch.Interface, error) { | ||||
| 	return c.Fake.InvokesWatch(NewRootWatchAction("nodes", opts)) | ||||
| } | ||||
|   | ||||
| @@ -185,12 +185,12 @@ func machine_3_Prioritizer(pod *api.Pod, nodes *api.NodeList) (*schedulerapi.Hos | ||||
| } | ||||
|  | ||||
| func TestSchedulerExtender(t *testing.T) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	_, s := framework.RunAMaster(nil) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	ns := framework.CreateTestingNamespace("scheduler-extender", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	restClient := client.NewOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}) | ||||
|  | ||||
| 	extender1 := &Extender{ | ||||
| @@ -240,15 +240,19 @@ func TestSchedulerExtender(t *testing.T) { | ||||
| 	} | ||||
| 	eventBroadcaster := record.NewBroadcaster() | ||||
| 	schedulerConfig.Recorder = eventBroadcaster.NewRecorder(api.EventSource{Component: api.DefaultSchedulerName}) | ||||
| 	eventBroadcaster.StartRecordingToSink(restClient.Events("")) | ||||
| 	eventBroadcaster.StartRecordingToSink(restClient.Events(ns.Name)) | ||||
| 	scheduler.New(schedulerConfig).Run() | ||||
|  | ||||
| 	defer close(schedulerConfig.StopEverything) | ||||
|  | ||||
| 	DoTestPodScheduling(t, restClient) | ||||
| 	DoTestPodScheduling(ns, t, restClient) | ||||
| } | ||||
|  | ||||
| func DoTestPodScheduling(t *testing.T, restClient *client.Client) { | ||||
| func DoTestPodScheduling(ns *api.Namespace, t *testing.T, restClient *client.Client) { | ||||
| 	// NOTE: This test cannot run in parallel, because it is creating and deleting | ||||
| 	// non-namespaced objects (Nodes). | ||||
| 	defer restClient.Nodes().DeleteCollection(nil, api.ListOptions{}) | ||||
|  | ||||
| 	goodCondition := api.NodeCondition{ | ||||
| 		Type:              api.NodeReady, | ||||
| 		Status:            api.ConditionTrue, | ||||
| @@ -279,7 +283,7 @@ func DoTestPodScheduling(t *testing.T, restClient *client.Client) { | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	myPod, err := restClient.Pods(api.NamespaceDefault).Create(pod) | ||||
| 	myPod, err := restClient.Pods(ns.Name).Create(pod) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to create pod: %v", err) | ||||
| 	} | ||||
| @@ -289,7 +293,7 @@ func DoTestPodScheduling(t *testing.T, restClient *client.Client) { | ||||
| 		t.Fatalf("Failed to schedule pod: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if myPod, err := restClient.Pods(api.NamespaceDefault).Get(myPod.Name); err != nil { | ||||
| 	if myPod, err := restClient.Pods(ns.Name).Get(myPod.Name); err != nil { | ||||
| 		t.Fatalf("Failed to get pod: %v", err) | ||||
| 	} else if myPod.Spec.NodeName != "machine3" { | ||||
| 		t.Fatalf("Failed to schedule using extender, expected machine3, got %v", myPod.Spec.NodeName) | ||||
|   | ||||
| @@ -64,9 +64,6 @@ const ( | ||||
| 	// Rc manifest used to create pods for benchmarks. | ||||
| 	// TODO: Convert this to a full path? | ||||
| 	TestRCManifest = "benchmark-controller.json" | ||||
|  | ||||
| 	// Test Namspace, for pods and rcs. | ||||
| 	TestNS = "test" | ||||
| ) | ||||
|  | ||||
| // MasterComponents is a control struct for all master components started via NewMasterComponents. | ||||
| @@ -326,23 +323,27 @@ func StartRC(controller *api.ReplicationController, restClient *client.Client) ( | ||||
| 	return ScaleRC(created.Name, created.Namespace, controller.Spec.Replicas, restClient) | ||||
| } | ||||
|  | ||||
| // StartPods check for numPods in TestNS. If they exist, it no-ops, otherwise it starts up | ||||
| // StartPods check for numPods in namespace. If they exist, it no-ops, otherwise it starts up | ||||
| // a temp rc, scales it to match numPods, then deletes the rc leaving behind the pods. | ||||
| func StartPods(numPods int, host string, restClient *client.Client) error { | ||||
| func StartPods(namespace string, numPods int, host string, restClient *client.Client) error { | ||||
| 	start := time.Now() | ||||
| 	defer func() { | ||||
| 		glog.Infof("StartPods took %v with numPods %d", time.Since(start), numPods) | ||||
| 	}() | ||||
| 	hostField := fields.OneTermEqualSelector(api.PodHostField, host) | ||||
| 	options := api.ListOptions{FieldSelector: hostField} | ||||
| 	pods, err := restClient.Pods(TestNS).List(options) | ||||
| 	pods, err := restClient.Pods(namespace).List(options) | ||||
| 	if err != nil || len(pods.Items) == numPods { | ||||
| 		return err | ||||
| 	} | ||||
| 	glog.Infof("Found %d pods that match host %v, require %d", len(pods.Items), hostField, numPods) | ||||
| 	// For the sake of simplicity, assume all pods in TestNS have selectors matching TestRCManifest. | ||||
| 	// For the sake of simplicity, assume all pods in namespace have selectors matching TestRCManifest. | ||||
| 	controller := RCFromManifest(TestRCManifest) | ||||
|  | ||||
| 	// Overwrite namespace | ||||
| 	controller.ObjectMeta.Namespace = namespace | ||||
| 	controller.Spec.Template.ObjectMeta.Namespace = namespace | ||||
|  | ||||
| 	// Make the rc unique to the given host. | ||||
| 	controller.Spec.Replicas = int32(numPods) | ||||
| 	controller.Spec.Template.Spec.NodeName = host | ||||
| @@ -355,7 +356,7 @@ func StartPods(numPods int, host string, restClient *client.Client) error { | ||||
| 	} else { | ||||
| 		// Delete the rc, otherwise when we restart master components for the next benchmark | ||||
| 		// the rc controller will race with the pods controller in the rc manager. | ||||
| 		return restClient.ReplicationControllers(TestNS).Delete(rc.Name) | ||||
| 		return restClient.ReplicationControllers(namespace).Delete(rc.Name) | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -20,7 +20,6 @@ package integration | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"net/http/httptest" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| @@ -36,7 +35,6 @@ import ( | ||||
| 	"k8s.io/kubernetes/pkg/client/restclient" | ||||
| 	"k8s.io/kubernetes/pkg/client/typed/dynamic" | ||||
| 	"k8s.io/kubernetes/pkg/controller/garbagecollector" | ||||
| 	"k8s.io/kubernetes/pkg/master" | ||||
| 	"k8s.io/kubernetes/pkg/registry/generic/registry" | ||||
| 	"k8s.io/kubernetes/pkg/types" | ||||
| 	"k8s.io/kubernetes/pkg/util/wait" | ||||
| @@ -59,7 +57,7 @@ const oneValidOwnerPodName = "test.pod.3" | ||||
| const toBeDeletedRCName = "test.rc.1" | ||||
| const remainingRCName = "test.rc.2" | ||||
|  | ||||
| func newPod(podName string, ownerReferences []v1.OwnerReference) *v1.Pod { | ||||
| func newPod(podName, podNamespace string, ownerReferences []v1.OwnerReference) *v1.Pod { | ||||
| 	for i := 0; i < len(ownerReferences); i++ { | ||||
| 		if len(ownerReferences[i].Kind) == 0 { | ||||
| 			ownerReferences[i].Kind = "ReplicationController" | ||||
| @@ -73,7 +71,7 @@ func newPod(podName string, ownerReferences []v1.OwnerReference) *v1.Pod { | ||||
| 		}, | ||||
| 		ObjectMeta: v1.ObjectMeta{ | ||||
| 			Name:            podName, | ||||
| 			Namespace:       framework.TestNS, | ||||
| 			Namespace:       podNamespace, | ||||
| 			OwnerReferences: ownerReferences, | ||||
| 		}, | ||||
| 		Spec: v1.PodSpec{ | ||||
| @@ -87,14 +85,14 @@ func newPod(podName string, ownerReferences []v1.OwnerReference) *v1.Pod { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func newOwnerRC(name string) *v1.ReplicationController { | ||||
| func newOwnerRC(name, namespace string) *v1.ReplicationController { | ||||
| 	return &v1.ReplicationController{ | ||||
| 		TypeMeta: unversioned.TypeMeta{ | ||||
| 			Kind:       "ReplicationController", | ||||
| 			APIVersion: "v1", | ||||
| 		}, | ||||
| 		ObjectMeta: v1.ObjectMeta{ | ||||
| 			Namespace: framework.TestNS, | ||||
| 			Namespace: namespace, | ||||
| 			Name:      name, | ||||
| 		}, | ||||
| 		Spec: v1.ReplicationControllerSpec{ | ||||
| @@ -116,22 +114,10 @@ func newOwnerRC(name string) *v1.ReplicationController { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func setup(t *testing.T) (*garbagecollector.GarbageCollector, clientset.Interface) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	var m *master.Master | ||||
| 	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||
| 		m.Handler.ServeHTTP(w, req) | ||||
| 	})) | ||||
| 	// TODO: close the http server | ||||
|  | ||||
| func setup(t *testing.T) (*httptest.Server, *garbagecollector.GarbageCollector, clientset.Interface) { | ||||
| 	masterConfig := framework.NewIntegrationTestMasterConfig() | ||||
| 	masterConfig.EnableCoreControllers = false | ||||
| 	m, err := master.New(masterConfig) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Error in bringing up the master: %v", err) | ||||
| 	} | ||||
| 	_, s := framework.RunAMaster(masterConfig) | ||||
|  | ||||
| 	clientSet, err := clientset.NewForConfig(&restclient.Config{Host: s.URL}) | ||||
| 	if err != nil { | ||||
| @@ -146,23 +132,28 @@ func setup(t *testing.T) (*garbagecollector.GarbageCollector, clientset.Interfac | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to create garbage collector") | ||||
| 	} | ||||
| 	return gc, clientSet | ||||
| 	return s, gc, clientSet | ||||
| } | ||||
|  | ||||
| // This test simulates the cascading deletion. | ||||
| func TestCascadingDeletion(t *testing.T) { | ||||
| 	gc, clientSet := setup(t) | ||||
| 	s, gc, clientSet := setup(t) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	ns := framework.CreateTestingNamespace("gc-cascading-deletion", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	oldEnableGarbageCollector := registry.EnableGarbageCollector | ||||
| 	registry.EnableGarbageCollector = true | ||||
| 	defer func() { registry.EnableGarbageCollector = oldEnableGarbageCollector }() | ||||
| 	rcClient := clientSet.Core().ReplicationControllers(framework.TestNS) | ||||
| 	podClient := clientSet.Core().Pods(framework.TestNS) | ||||
| 	rcClient := clientSet.Core().ReplicationControllers(ns.Name) | ||||
| 	podClient := clientSet.Core().Pods(ns.Name) | ||||
|  | ||||
| 	toBeDeletedRC, err := rcClient.Create(newOwnerRC(toBeDeletedRCName)) | ||||
| 	toBeDeletedRC, err := rcClient.Create(newOwnerRC(toBeDeletedRCName, ns.Name)) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to create replication controller: %v", err) | ||||
| 	} | ||||
| 	remainingRC, err := rcClient.Create(newOwnerRC(remainingRCName)) | ||||
| 	remainingRC, err := rcClient.Create(newOwnerRC(remainingRCName, ns.Name)) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to create replication controller: %v", err) | ||||
| 	} | ||||
| @@ -176,14 +167,14 @@ func TestCascadingDeletion(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| 	// this pod should be cascadingly deleted. | ||||
| 	pod := newPod(garbageCollectedPodName, []v1.OwnerReference{{UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRCName}}) | ||||
| 	pod := newPod(garbageCollectedPodName, ns.Name, []v1.OwnerReference{{UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRCName}}) | ||||
| 	_, err = podClient.Create(pod) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to create Pod: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// this pod shouldn't be cascadingly deleted, because it has a valid reference. | ||||
| 	pod = newPod(oneValidOwnerPodName, []v1.OwnerReference{ | ||||
| 	pod = newPod(oneValidOwnerPodName, ns.Name, []v1.OwnerReference{ | ||||
| 		{UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRCName}, | ||||
| 		{UID: remainingRC.ObjectMeta.UID, Name: remainingRCName}, | ||||
| 	}) | ||||
| @@ -193,7 +184,7 @@ func TestCascadingDeletion(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| 	// this pod shouldn't be cascadingly deleted, because it doesn't have an owner. | ||||
| 	pod = newPod(independentPodName, []v1.OwnerReference{}) | ||||
| 	pod = newPod(independentPodName, ns.Name, []v1.OwnerReference{}) | ||||
| 	_, err = podClient.Create(pod) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to create Pod: %v", err) | ||||
| @@ -253,13 +244,18 @@ func TestCascadingDeletion(t *testing.T) { | ||||
| // This test simulates the case where an object is created with an owner that | ||||
| // doesn't exist. It verifies the GC will delete such an object. | ||||
| func TestCreateWithNonExistentOwner(t *testing.T) { | ||||
| 	gc, clientSet := setup(t) | ||||
| 	s, gc, clientSet := setup(t) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	ns := framework.CreateTestingNamespace("gc-non-existing-owner", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	oldEnableGarbageCollector := registry.EnableGarbageCollector | ||||
| 	registry.EnableGarbageCollector = true | ||||
| 	defer func() { registry.EnableGarbageCollector = oldEnableGarbageCollector }() | ||||
| 	podClient := clientSet.Core().Pods(framework.TestNS) | ||||
| 	podClient := clientSet.Core().Pods(ns.Name) | ||||
|  | ||||
| 	pod := newPod(garbageCollectedPodName, []v1.OwnerReference{{UID: "doesn't matter", Name: toBeDeletedRCName}}) | ||||
| 	pod := newPod(garbageCollectedPodName, ns.Name, []v1.OwnerReference{{UID: "doesn't matter", Name: toBeDeletedRCName}}) | ||||
| 	_, err := podClient.Create(pod) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to create Pod: %v", err) | ||||
| @@ -288,13 +284,13 @@ func TestCreateWithNonExistentOwner(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func setupRCsPods(t *testing.T, gc *garbagecollector.GarbageCollector, clientSet clientset.Interface, nameSuffix string, initialFinalizers []string, options *api.DeleteOptions, wg *sync.WaitGroup, rcUIDs chan types.UID) { | ||||
| func setupRCsPods(t *testing.T, gc *garbagecollector.GarbageCollector, clientSet clientset.Interface, nameSuffix, namespace string, initialFinalizers []string, options *api.DeleteOptions, wg *sync.WaitGroup, rcUIDs chan types.UID) { | ||||
| 	defer wg.Done() | ||||
| 	rcClient := clientSet.Core().ReplicationControllers(framework.TestNS) | ||||
| 	podClient := clientSet.Core().Pods(framework.TestNS) | ||||
| 	rcClient := clientSet.Core().ReplicationControllers(namespace) | ||||
| 	podClient := clientSet.Core().Pods(namespace) | ||||
| 	// create rc. | ||||
| 	rcName := "test.rc." + nameSuffix | ||||
| 	rc := newOwnerRC(rcName) | ||||
| 	rc := newOwnerRC(rcName, namespace) | ||||
| 	rc.ObjectMeta.Finalizers = initialFinalizers | ||||
| 	rc, err := rcClient.Create(rc) | ||||
| 	if err != nil { | ||||
| @@ -305,7 +301,7 @@ func setupRCsPods(t *testing.T, gc *garbagecollector.GarbageCollector, clientSet | ||||
| 	var podUIDs []types.UID | ||||
| 	for j := 0; j < 3; j++ { | ||||
| 		podName := "test.pod." + nameSuffix + "-" + strconv.Itoa(j) | ||||
| 		pod := newPod(podName, []v1.OwnerReference{{UID: rc.ObjectMeta.UID, Name: rc.ObjectMeta.Name}}) | ||||
| 		pod := newPod(podName, namespace, []v1.OwnerReference{{UID: rc.ObjectMeta.UID, Name: rc.ObjectMeta.Name}}) | ||||
| 		_, err = podClient.Create(pod) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("Failed to create Pod: %v", err) | ||||
| @@ -325,9 +321,9 @@ func setupRCsPods(t *testing.T, gc *garbagecollector.GarbageCollector, clientSet | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func verifyRemainingObjects(t *testing.T, clientSet clientset.Interface, rcNum, podNum int) (bool, error) { | ||||
| 	rcClient := clientSet.Core().ReplicationControllers(framework.TestNS) | ||||
| 	podClient := clientSet.Core().Pods(framework.TestNS) | ||||
| func verifyRemainingObjects(t *testing.T, clientSet clientset.Interface, namespace string, rcNum, podNum int) (bool, error) { | ||||
| 	rcClient := clientSet.Core().ReplicationControllers(namespace) | ||||
| 	podClient := clientSet.Core().Pods(namespace) | ||||
| 	pods, err := podClient.List(api.ListOptions{}) | ||||
| 	if err != nil { | ||||
| 		return false, fmt.Errorf("Failed to list pods: %v", err) | ||||
| @@ -353,7 +349,12 @@ func verifyRemainingObjects(t *testing.T, clientSet clientset.Interface, rcNum, | ||||
| // e2e tests that put more stress. | ||||
| func TestStressingCascadingDeletion(t *testing.T) { | ||||
| 	t.Logf("starts garbage collector stress test") | ||||
| 	gc, clientSet := setup(t) | ||||
| 	s, gc, clientSet := setup(t) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	ns := framework.CreateTestingNamespace("gc-stressing-cascading-deletion", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	oldEnableGarbageCollector := registry.EnableGarbageCollector | ||||
| 	registry.EnableGarbageCollector = true | ||||
| 	defer func() { registry.EnableGarbageCollector = oldEnableGarbageCollector }() | ||||
| @@ -367,13 +368,13 @@ func TestStressingCascadingDeletion(t *testing.T) { | ||||
| 	rcUIDs := make(chan types.UID, collections*4) | ||||
| 	for i := 0; i < collections; i++ { | ||||
| 		// rc is created with empty finalizers, deleted with nil delete options, pods will be deleted | ||||
| 		go setupRCsPods(t, gc, clientSet, "collection1-"+strconv.Itoa(i), []string{}, nil, &wg, rcUIDs) | ||||
| 		go setupRCsPods(t, gc, clientSet, "collection1-"+strconv.Itoa(i), ns.Name, []string{}, nil, &wg, rcUIDs) | ||||
| 		// rc is created with the orphan finalizer, deleted with nil options, pods will remain. | ||||
| 		go setupRCsPods(t, gc, clientSet, "collection2-"+strconv.Itoa(i), []string{api.FinalizerOrphan}, nil, &wg, rcUIDs) | ||||
| 		go setupRCsPods(t, gc, clientSet, "collection2-"+strconv.Itoa(i), ns.Name, []string{api.FinalizerOrphan}, nil, &wg, rcUIDs) | ||||
| 		// rc is created with the orphan finalizer, deleted with DeleteOptions.OrphanDependents=false, pods will be deleted. | ||||
| 		go setupRCsPods(t, gc, clientSet, "collection3-"+strconv.Itoa(i), []string{api.FinalizerOrphan}, getNonOrphanOptions(), &wg, rcUIDs) | ||||
| 		go setupRCsPods(t, gc, clientSet, "collection3-"+strconv.Itoa(i), ns.Name, []string{api.FinalizerOrphan}, getNonOrphanOptions(), &wg, rcUIDs) | ||||
| 		// rc is created with empty finalizers, deleted with DeleteOptions.OrphanDependents=true, pods will remain. | ||||
| 		go setupRCsPods(t, gc, clientSet, "collection4-"+strconv.Itoa(i), []string{}, getOrphanOptions(), &wg, rcUIDs) | ||||
| 		go setupRCsPods(t, gc, clientSet, "collection4-"+strconv.Itoa(i), ns.Name, []string{}, getOrphanOptions(), &wg, rcUIDs) | ||||
| 	} | ||||
| 	wg.Wait() | ||||
| 	t.Logf("all pods are created, all replications controllers are created then deleted") | ||||
| @@ -390,14 +391,14 @@ func TestStressingCascadingDeletion(t *testing.T) { | ||||
| 		podsInEachCollection := 3 | ||||
| 		// see the comments on the calls to setupRCsPods for details | ||||
| 		remainingGroups := 2 | ||||
| 		return verifyRemainingObjects(t, clientSet, 0, collections*podsInEachCollection*remainingGroups) | ||||
| 		return verifyRemainingObjects(t, clientSet, ns.Name, 0, collections*podsInEachCollection*remainingGroups) | ||||
| 	}); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	t.Logf("number of remaining replication controllers and pods are as expected") | ||||
|  | ||||
| 	// verify the remaining pods all have "orphan" in their names. | ||||
| 	podClient := clientSet.Core().Pods(framework.TestNS) | ||||
| 	podClient := clientSet.Core().Pods(ns.Name) | ||||
| 	pods, err := podClient.List(api.ListOptions{}) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| @@ -420,14 +421,19 @@ func TestStressingCascadingDeletion(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestOrphaning(t *testing.T) { | ||||
| 	gc, clientSet := setup(t) | ||||
| 	s, gc, clientSet := setup(t) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	ns := framework.CreateTestingNamespace("gc-orphaning", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	oldEnableGarbageCollector := registry.EnableGarbageCollector | ||||
| 	registry.EnableGarbageCollector = true | ||||
| 	defer func() { registry.EnableGarbageCollector = oldEnableGarbageCollector }() | ||||
| 	podClient := clientSet.Core().Pods(framework.TestNS) | ||||
| 	rcClient := clientSet.Core().ReplicationControllers(framework.TestNS) | ||||
| 	podClient := clientSet.Core().Pods(ns.Name) | ||||
| 	rcClient := clientSet.Core().ReplicationControllers(ns.Name) | ||||
| 	// create the RC with the orphan finalizer set | ||||
| 	toBeDeletedRC := newOwnerRC(toBeDeletedRCName) | ||||
| 	toBeDeletedRC := newOwnerRC(toBeDeletedRCName, ns.Name) | ||||
| 	toBeDeletedRC, err := rcClient.Create(toBeDeletedRC) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to create replication controller: %v", err) | ||||
| @@ -438,7 +444,7 @@ func TestOrphaning(t *testing.T) { | ||||
| 	podsNum := 3 | ||||
| 	for i := 0; i < podsNum; i++ { | ||||
| 		podName := garbageCollectedPodName + strconv.Itoa(i) | ||||
| 		pod := newPod(podName, []v1.OwnerReference{{UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRCName}}) | ||||
| 		pod := newPod(podName, ns.Name, []v1.OwnerReference{{UID: toBeDeletedRC.ObjectMeta.UID, Name: toBeDeletedRCName}}) | ||||
| 		_, err = podClient.Create(pod) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("Failed to create Pod: %v", err) | ||||
|   | ||||
| @@ -64,7 +64,6 @@ func init() { | ||||
| 	Pods = *pods | ||||
| 	Workers = *workers | ||||
| 	Tasks = *tasks | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
| } | ||||
|  | ||||
| // getPods returns the cmd line -pods or b.N if -pods wasn't specified. | ||||
| @@ -98,7 +97,7 @@ func getIterations(bN int) int { | ||||
| } | ||||
|  | ||||
| // startPodsOnNodes creates numPods sharded across numNodes | ||||
| func startPodsOnNodes(numPods, numNodes int, restClient *client.Client) { | ||||
| func startPodsOnNodes(ns string, numPods, numNodes int, restClient *client.Client) { | ||||
| 	podsPerNode := numPods / numNodes | ||||
| 	if podsPerNode < 1 { | ||||
| 		podsPerNode = 1 | ||||
| @@ -114,6 +113,9 @@ func BenchmarkPodList(b *testing.B) { | ||||
| 	m := framework.NewMasterComponents(&framework.Config{nil, true, false, 250.0, 500}) | ||||
| 	defer m.Stop(true, true) | ||||
|  | ||||
| 	ns := framework.CreateTestingNamespace("benchmark-pod-list", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	numPods, numTasks, iter := getPods(b.N), getTasks(b.N), getIterations(b.N) | ||||
| 	podsPerNode := numPods / numTasks | ||||
| 	if podsPerNode < 1 { | ||||
| @@ -122,7 +124,7 @@ func BenchmarkPodList(b *testing.B) { | ||||
| 	glog.Infof("Starting benchmark: b.N %d, pods %d, workers %d, podsPerNode %d", | ||||
| 		b.N, numPods, numTasks, podsPerNode) | ||||
|  | ||||
| 	startPodsOnNodes(numPods, numTasks, m.RestClient) | ||||
| 	startPodsOnNodes(ns.Name, numPods, numTasks, m.RestClient) | ||||
| 	// Stop the rc manager so it doesn't steal resources | ||||
| 	m.Stop(false, true) | ||||
|  | ||||
| @@ -134,7 +136,7 @@ func BenchmarkPodList(b *testing.B) { | ||||
| 			defer func() { | ||||
| 				glog.V(3).Infof("Worker %d: Node %v listing pods took %v", id, host, time.Since(now)) | ||||
| 			}() | ||||
| 			if pods, err := m.RestClient.Pods(framework.TestNS).List( | ||||
| 			if pods, err := m.RestClient.Pods(ns.Name).List( | ||||
| 				labels.Everything(), | ||||
| 				fields.OneTermEqualSelector(client.PodHost, host)); err != nil { | ||||
| 				return err | ||||
| @@ -153,13 +155,16 @@ func BenchmarkPodListEtcd(b *testing.B) { | ||||
| 	m := framework.NewMasterComponents(&framework.Config{nil, true, false, 250.0, 500}) | ||||
| 	defer m.Stop(true, true) | ||||
|  | ||||
| 	ns := framework.CreateTestingNamespace("benchmark-pod-list-etcd", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	numPods, numTasks, iter := getPods(b.N), getTasks(b.N), getIterations(b.N) | ||||
| 	podsPerNode := numPods / numTasks | ||||
| 	if podsPerNode < 1 { | ||||
| 		podsPerNode = 1 | ||||
| 	} | ||||
|  | ||||
| 	startPodsOnNodes(numPods, numTasks, m.RestClient) | ||||
| 	startPodsOnNodes(ns.Name, numPods, numTasks, m.RestClient) | ||||
| 	// Stop the rc manager so it doesn't steal resources | ||||
| 	m.Stop(false, true) | ||||
|  | ||||
| @@ -173,7 +178,7 @@ func BenchmarkPodListEtcd(b *testing.B) { | ||||
| 			defer func() { | ||||
| 				glog.V(3).Infof("Worker %d: listing pods took %v", id, time.Since(now)) | ||||
| 			}() | ||||
| 			pods, err := m.RestClient.Pods(framework.TestNS).List(labels.Everything(), fields.Everything()) | ||||
| 			pods, err := m.RestClient.Pods(ns.Name).List(labels.Everything(), fields.Everything()) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|   | ||||
| @@ -104,23 +104,27 @@ func testSleep() { | ||||
| } | ||||
|  | ||||
| func TestPersistentVolumeRecycler(t *testing.T) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	glog.V(2).Infof("TestPersistentVolumeRecycler started") | ||||
| 	_, s := framework.RunAMaster(nil) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	testClient, ctrl, watchPV, watchPVC := createClients(t, s) | ||||
| 	ns := framework.CreateTestingNamespace("pv-recycler", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	testClient, ctrl, watchPV, watchPVC := createClients(ns, t, s) | ||||
| 	defer watchPV.Stop() | ||||
| 	defer watchPVC.Stop() | ||||
|  | ||||
| 	// NOTE: This test cannot run in parallel, because it is creating and deleting | ||||
| 	// non-namespaced objects (PersistenceVolumes). | ||||
| 	defer testClient.Core().PersistentVolumes().DeleteCollection(nil, api.ListOptions{}) | ||||
|  | ||||
| 	ctrl.Run() | ||||
| 	defer ctrl.Stop() | ||||
|  | ||||
| 	// This PV will be claimed, released, and recycled. | ||||
| 	pv := createPV("fake-pv-recycler", "/tmp/foo", "10G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, api.PersistentVolumeReclaimRecycle) | ||||
| 	pvc := createPVC("fake-pvc-recycler", "5G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}) | ||||
| 	pvc := createPVC("fake-pvc-recycler", ns.Name, "5G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}) | ||||
|  | ||||
| 	_, err := testClient.PersistentVolumes().Create(pv) | ||||
| 	if err != nil { | ||||
| @@ -128,7 +132,7 @@ func TestPersistentVolumeRecycler(t *testing.T) { | ||||
| 	} | ||||
| 	glog.V(2).Infof("TestPersistentVolumeRecycler pvc created") | ||||
|  | ||||
| 	_, err = testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvc) | ||||
| 	_, err = testClient.PersistentVolumeClaims(ns.Name).Create(pvc) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("Failed to create PersistentVolumeClaim: %v", err) | ||||
| 	} | ||||
| @@ -137,11 +141,11 @@ func TestPersistentVolumeRecycler(t *testing.T) { | ||||
| 	// wait until the controller pairs the volume and claim | ||||
| 	waitForPersistentVolumePhase(testClient, pv.Name, watchPV, api.VolumeBound) | ||||
| 	glog.V(2).Infof("TestPersistentVolumeRecycler pv bound") | ||||
| 	waitForPersistentVolumeClaimPhase(testClient, pvc.Name, watchPVC, api.ClaimBound) | ||||
| 	waitForPersistentVolumeClaimPhase(testClient, pvc.Name, ns.Name, watchPVC, api.ClaimBound) | ||||
| 	glog.V(2).Infof("TestPersistentVolumeRecycler pvc bound") | ||||
|  | ||||
| 	// deleting a claim releases the volume, after which it can be recycled | ||||
| 	if err := testClient.PersistentVolumeClaims(api.NamespaceDefault).Delete(pvc.Name, nil); err != nil { | ||||
| 	if err := testClient.PersistentVolumeClaims(ns.Name).Delete(pvc.Name, nil); err != nil { | ||||
| 		t.Errorf("error deleting claim %s", pvc.Name) | ||||
| 	} | ||||
| 	glog.V(2).Infof("TestPersistentVolumeRecycler pvc deleted") | ||||
| @@ -150,45 +154,48 @@ func TestPersistentVolumeRecycler(t *testing.T) { | ||||
| 	glog.V(2).Infof("TestPersistentVolumeRecycler pv released") | ||||
| 	waitForPersistentVolumePhase(testClient, pv.Name, watchPV, api.VolumeAvailable) | ||||
| 	glog.V(2).Infof("TestPersistentVolumeRecycler pv available") | ||||
|  | ||||
| } | ||||
|  | ||||
| func TestPersistentVolumeDeleter(t *testing.T) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	glog.V(2).Infof("TestPersistentVolumeDeleter started") | ||||
| 	_, s := framework.RunAMaster(nil) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	testClient, ctrl, watchPV, watchPVC := createClients(t, s) | ||||
| 	ns := framework.CreateTestingNamespace("pv-deleter", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	testClient, ctrl, watchPV, watchPVC := createClients(ns, t, s) | ||||
| 	defer watchPV.Stop() | ||||
| 	defer watchPVC.Stop() | ||||
|  | ||||
| 	// NOTE: This test cannot run in parallel, because it is creating and deleting | ||||
| 	// non-namespaced objects (PersistenceVolumes). | ||||
| 	defer testClient.Core().PersistentVolumes().DeleteCollection(nil, api.ListOptions{}) | ||||
|  | ||||
| 	ctrl.Run() | ||||
| 	defer ctrl.Stop() | ||||
|  | ||||
| 	// This PV will be claimed, released, and deleted. | ||||
| 	pv := createPV("fake-pv-deleter", "/tmp/foo", "10G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, api.PersistentVolumeReclaimDelete) | ||||
| 	pvc := createPVC("fake-pvc-deleter", "5G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}) | ||||
| 	pvc := createPVC("fake-pvc-deleter", ns.Name, "5G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}) | ||||
|  | ||||
| 	_, err := testClient.PersistentVolumes().Create(pv) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("Failed to create PersistentVolume: %v", err) | ||||
| 	} | ||||
| 	glog.V(2).Infof("TestPersistentVolumeDeleter pv created") | ||||
| 	_, err = testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvc) | ||||
| 	_, err = testClient.PersistentVolumeClaims(ns.Name).Create(pvc) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("Failed to create PersistentVolumeClaim: %v", err) | ||||
| 	} | ||||
| 	glog.V(2).Infof("TestPersistentVolumeDeleter pvc created") | ||||
| 	waitForPersistentVolumePhase(testClient, pv.Name, watchPV, api.VolumeBound) | ||||
| 	glog.V(2).Infof("TestPersistentVolumeDeleter pv bound") | ||||
| 	waitForPersistentVolumeClaimPhase(testClient, pvc.Name, watchPVC, api.ClaimBound) | ||||
| 	waitForPersistentVolumeClaimPhase(testClient, pvc.Name, ns.Name, watchPVC, api.ClaimBound) | ||||
| 	glog.V(2).Infof("TestPersistentVolumeDeleter pvc bound") | ||||
|  | ||||
| 	// deleting a claim releases the volume, after which it can be recycled | ||||
| 	if err := testClient.PersistentVolumeClaims(api.NamespaceDefault).Delete(pvc.Name, nil); err != nil { | ||||
| 	if err := testClient.PersistentVolumeClaims(ns.Name).Delete(pvc.Name, nil); err != nil { | ||||
| 		t.Errorf("error deleting claim %s", pvc.Name) | ||||
| 	} | ||||
| 	glog.V(2).Infof("TestPersistentVolumeDeleter pvc deleted") | ||||
| @@ -206,25 +213,28 @@ func TestPersistentVolumeDeleter(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestPersistentVolumeBindRace(t *testing.T) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	// Test a race binding many claims to a PV that is pre-bound to a specific | ||||
| 	// PVC. Only this specific PVC should get bound. | ||||
| 	glog.V(2).Infof("TestPersistentVolumeBindRace started") | ||||
| 	_, s := framework.RunAMaster(nil) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	testClient, ctrl, watchPV, watchPVC := createClients(t, s) | ||||
| 	ns := framework.CreateTestingNamespace("pv-bind-race", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	testClient, ctrl, watchPV, watchPVC := createClients(ns, t, s) | ||||
| 	defer watchPV.Stop() | ||||
| 	defer watchPVC.Stop() | ||||
|  | ||||
| 	// NOTE: This test cannot run in parallel, because it is creating and deleting | ||||
| 	// non-namespaced objects (PersistenceVolumes). | ||||
| 	defer testClient.Core().PersistentVolumes().DeleteCollection(nil, api.ListOptions{}) | ||||
|  | ||||
| 	ctrl.Run() | ||||
| 	defer ctrl.Stop() | ||||
|  | ||||
| 	pv := createPV("fake-pv-race", "/tmp/foo", "10G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, api.PersistentVolumeReclaimRetain) | ||||
|  | ||||
| 	pvc := createPVC("fake-pvc-race", "5G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}) | ||||
| 	pvc := createPVC("fake-pvc-race", ns.Name, "5G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}) | ||||
| 	counter := 0 | ||||
| 	maxClaims := 100 | ||||
| 	claims := []*api.PersistentVolumeClaim{} | ||||
| @@ -233,7 +243,7 @@ func TestPersistentVolumeBindRace(t *testing.T) { | ||||
| 		clone, _ := conversion.NewCloner().DeepCopy(pvc) | ||||
| 		newPvc, _ := clone.(*api.PersistentVolumeClaim) | ||||
| 		newPvc.ObjectMeta = api.ObjectMeta{Name: fmt.Sprintf("fake-pvc-race-%d", counter)} | ||||
| 		claim, err := testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(newPvc) | ||||
| 		claim, err := testClient.PersistentVolumeClaims(ns.Name).Create(newPvc) | ||||
| 		if err != nil { | ||||
| 			t.Fatal("Error creating newPvc: %v", err) | ||||
| 		} | ||||
| @@ -276,16 +286,20 @@ func TestPersistentVolumeBindRace(t *testing.T) { | ||||
|  | ||||
| // TestPersistentVolumeClaimLabelSelector test binding using label selectors | ||||
| func TestPersistentVolumeClaimLabelSelector(t *testing.T) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	_, s := framework.RunAMaster(nil) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	testClient, controller, watchPV, watchPVC := createClients(t, s) | ||||
| 	ns := framework.CreateTestingNamespace("pvc-label-selector", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	testClient, controller, watchPV, watchPVC := createClients(ns, t, s) | ||||
| 	defer watchPV.Stop() | ||||
| 	defer watchPVC.Stop() | ||||
|  | ||||
| 	// NOTE: This test cannot run in parallel, because it is creating and deleting | ||||
| 	// non-namespaced objects (PersistenceVolumes). | ||||
| 	defer testClient.Core().PersistentVolumes().DeleteCollection(nil, api.ListOptions{}) | ||||
|  | ||||
| 	controller.Run() | ||||
| 	defer controller.Stop() | ||||
|  | ||||
| @@ -296,7 +310,7 @@ func TestPersistentVolumeClaimLabelSelector(t *testing.T) { | ||||
|  | ||||
| 		pv_true  = createPV("pv-true", "/tmp/foo-label", "1G", modes, reclaim) | ||||
| 		pv_false = createPV("pv-false", "/tmp/foo-label", "1G", modes, reclaim) | ||||
| 		pvc      = createPVC("pvc-ls-1", "1G", modes) | ||||
| 		pvc      = createPVC("pvc-ls-1", ns.Name, "1G", modes) | ||||
| 	) | ||||
|  | ||||
| 	pv_true.ObjectMeta.SetLabels(map[string]string{"foo": "true"}) | ||||
| @@ -318,7 +332,7 @@ func TestPersistentVolumeClaimLabelSelector(t *testing.T) { | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	_, err = testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvc) | ||||
| 	_, err = testClient.PersistentVolumeClaims(ns.Name).Create(pvc) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to create PersistentVolumeClaim: %v", err) | ||||
| 	} | ||||
| @@ -326,7 +340,7 @@ func TestPersistentVolumeClaimLabelSelector(t *testing.T) { | ||||
|  | ||||
| 	waitForAnyPersistentVolumePhase(watchPV, api.VolumeBound) | ||||
| 	t.Log("volume bound") | ||||
| 	waitForPersistentVolumeClaimPhase(testClient, pvc.Name, watchPVC, api.ClaimBound) | ||||
| 	waitForPersistentVolumeClaimPhase(testClient, pvc.Name, ns.Name, watchPVC, api.ClaimBound) | ||||
| 	t.Log("claim bound") | ||||
|  | ||||
| 	pv, err := testClient.PersistentVolumes().Get("pv-false") | ||||
| @@ -351,16 +365,20 @@ func TestPersistentVolumeClaimLabelSelector(t *testing.T) { | ||||
| // TestPersistentVolumeClaimLabelSelectorMatchExpressions test binding using | ||||
| // MatchExpressions label selectors | ||||
| func TestPersistentVolumeClaimLabelSelectorMatchExpressions(t *testing.T) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	_, s := framework.RunAMaster(nil) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	testClient, controller, watchPV, watchPVC := createClients(t, s) | ||||
| 	ns := framework.CreateTestingNamespace("pvc-match-expresssions", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	testClient, controller, watchPV, watchPVC := createClients(ns, t, s) | ||||
| 	defer watchPV.Stop() | ||||
| 	defer watchPVC.Stop() | ||||
|  | ||||
| 	// NOTE: This test cannot run in parallel, because it is creating and deleting | ||||
| 	// non-namespaced objects (PersistenceVolumes). | ||||
| 	defer testClient.Core().PersistentVolumes().DeleteCollection(nil, api.ListOptions{}) | ||||
|  | ||||
| 	controller.Run() | ||||
| 	defer controller.Stop() | ||||
|  | ||||
| @@ -371,7 +389,7 @@ func TestPersistentVolumeClaimLabelSelectorMatchExpressions(t *testing.T) { | ||||
|  | ||||
| 		pv_true  = createPV("pv-true", "/tmp/foo-label", "1G", modes, reclaim) | ||||
| 		pv_false = createPV("pv-false", "/tmp/foo-label", "1G", modes, reclaim) | ||||
| 		pvc      = createPVC("pvc-ls-1", "1G", modes) | ||||
| 		pvc      = createPVC("pvc-ls-1", ns.Name, "1G", modes) | ||||
| 	) | ||||
|  | ||||
| 	pv_true.ObjectMeta.SetLabels(map[string]string{"foo": "valA", "bar": ""}) | ||||
| @@ -412,7 +430,7 @@ func TestPersistentVolumeClaimLabelSelectorMatchExpressions(t *testing.T) { | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	_, err = testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvc) | ||||
| 	_, err = testClient.PersistentVolumeClaims(ns.Name).Create(pvc) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to create PersistentVolumeClaim: %v", err) | ||||
| 	} | ||||
| @@ -420,7 +438,7 @@ func TestPersistentVolumeClaimLabelSelectorMatchExpressions(t *testing.T) { | ||||
|  | ||||
| 	waitForAnyPersistentVolumePhase(watchPV, api.VolumeBound) | ||||
| 	t.Log("volume bound") | ||||
| 	waitForPersistentVolumeClaimPhase(testClient, pvc.Name, watchPVC, api.ClaimBound) | ||||
| 	waitForPersistentVolumeClaimPhase(testClient, pvc.Name, ns.Name, watchPVC, api.ClaimBound) | ||||
| 	t.Log("claim bound") | ||||
|  | ||||
| 	pv, err := testClient.PersistentVolumes().Get("pv-false") | ||||
| @@ -445,16 +463,20 @@ func TestPersistentVolumeClaimLabelSelectorMatchExpressions(t *testing.T) { | ||||
| // TestPersistentVolumeMultiPVs tests binding of one PVC to 100 PVs with | ||||
| // different size. | ||||
| func TestPersistentVolumeMultiPVs(t *testing.T) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	_, s := framework.RunAMaster(nil) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	testClient, controller, watchPV, watchPVC := createClients(t, s) | ||||
| 	ns := framework.CreateTestingNamespace("multi-pvs", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	testClient, controller, watchPV, watchPVC := createClients(ns, t, s) | ||||
| 	defer watchPV.Stop() | ||||
| 	defer watchPVC.Stop() | ||||
|  | ||||
| 	// NOTE: This test cannot run in parallel, because it is creating and deleting | ||||
| 	// non-namespaced objects (PersistenceVolumes). | ||||
| 	defer testClient.Core().PersistentVolumes().DeleteCollection(nil, api.ListOptions{}) | ||||
|  | ||||
| 	controller.Run() | ||||
| 	defer controller.Stop() | ||||
|  | ||||
| @@ -466,7 +488,7 @@ func TestPersistentVolumeMultiPVs(t *testing.T) { | ||||
| 			[]api.PersistentVolumeAccessMode{api.ReadWriteOnce}, api.PersistentVolumeReclaimRetain) | ||||
| 	} | ||||
|  | ||||
| 	pvc := createPVC("pvc-2", strconv.Itoa(maxPVs/2)+"G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}) | ||||
| 	pvc := createPVC("pvc-2", ns.Name, strconv.Itoa(maxPVs/2)+"G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}) | ||||
|  | ||||
| 	for i := 0; i < maxPVs; i++ { | ||||
| 		_, err := testClient.PersistentVolumes().Create(pvs[i]) | ||||
| @@ -477,7 +499,7 @@ func TestPersistentVolumeMultiPVs(t *testing.T) { | ||||
| 	} | ||||
| 	t.Log("volumes created") | ||||
|  | ||||
| 	_, err := testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvc) | ||||
| 	_, err := testClient.PersistentVolumeClaims(ns.Name).Create(pvc) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("Failed to create PersistentVolumeClaim: %v", err) | ||||
| 	} | ||||
| @@ -486,7 +508,7 @@ func TestPersistentVolumeMultiPVs(t *testing.T) { | ||||
| 	// wait until the binder pairs the claim with a volume | ||||
| 	waitForAnyPersistentVolumePhase(watchPV, api.VolumeBound) | ||||
| 	t.Log("volume bound") | ||||
| 	waitForPersistentVolumeClaimPhase(testClient, pvc.Name, watchPVC, api.ClaimBound) | ||||
| 	waitForPersistentVolumeClaimPhase(testClient, pvc.Name, ns.Name, watchPVC, api.ClaimBound) | ||||
| 	t.Log("claim bound") | ||||
|  | ||||
| 	// only one PV is bound | ||||
| @@ -517,7 +539,7 @@ func TestPersistentVolumeMultiPVs(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| 	// deleting a claim releases the volume | ||||
| 	if err := testClient.PersistentVolumeClaims(api.NamespaceDefault).Delete(pvc.Name, nil); err != nil { | ||||
| 	if err := testClient.PersistentVolumeClaims(ns.Name).Delete(pvc.Name, nil); err != nil { | ||||
| 		t.Errorf("error deleting claim %s", pvc.Name) | ||||
| 	} | ||||
| 	t.Log("claim deleted") | ||||
| @@ -529,16 +551,20 @@ func TestPersistentVolumeMultiPVs(t *testing.T) { | ||||
| // TestPersistentVolumeMultiPVsPVCs tests binding of 100 PVC to 100 PVs. | ||||
| // This test is configurable by KUBE_INTEGRATION_PV_* variables. | ||||
| func TestPersistentVolumeMultiPVsPVCs(t *testing.T) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	_, s := framework.RunAMaster(nil) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	testClient, binder, watchPV, watchPVC := createClients(t, s) | ||||
| 	ns := framework.CreateTestingNamespace("multi-pvs-pvcs", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	testClient, binder, watchPV, watchPVC := createClients(ns, t, s) | ||||
| 	defer watchPV.Stop() | ||||
| 	defer watchPVC.Stop() | ||||
|  | ||||
| 	// NOTE: This test cannot run in parallel, because it is creating and deleting | ||||
| 	// non-namespaced objects (PersistenceVolumes). | ||||
| 	defer testClient.Core().PersistentVolumes().DeleteCollection(nil, api.ListOptions{}) | ||||
|  | ||||
| 	binder.Run() | ||||
| 	defer binder.Stop() | ||||
|  | ||||
| @@ -549,7 +575,7 @@ func TestPersistentVolumeMultiPVsPVCs(t *testing.T) { | ||||
| 		// This PV will be claimed, released, and deleted | ||||
| 		pvs[i] = createPV("pv-"+strconv.Itoa(i), "/tmp/foo"+strconv.Itoa(i), "1G", | ||||
| 			[]api.PersistentVolumeAccessMode{api.ReadWriteOnce}, api.PersistentVolumeReclaimRetain) | ||||
| 		pvcs[i] = createPVC("pvc-"+strconv.Itoa(i), "1G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}) | ||||
| 		pvcs[i] = createPVC("pvc-"+strconv.Itoa(i), ns.Name, "1G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}) | ||||
| 	} | ||||
|  | ||||
| 	// Create PVs first | ||||
| @@ -573,7 +599,7 @@ func TestPersistentVolumeMultiPVsPVCs(t *testing.T) { | ||||
| 	// Create the claims, again in a separate goroutine. | ||||
| 	go func() { | ||||
| 		for i := 0; i < objCount; i++ { | ||||
| 			_, _ = testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvcs[i]) | ||||
| 			_, _ = testClient.PersistentVolumeClaims(ns.Name).Create(pvcs[i]) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| @@ -595,7 +621,7 @@ func TestPersistentVolumeMultiPVsPVCs(t *testing.T) { | ||||
| 		} | ||||
| 		glog.V(2).Infof("PV %q is bound to PVC %q", pv.Name, pv.Spec.ClaimRef.Name) | ||||
|  | ||||
| 		pvc, err := testClient.PersistentVolumeClaims(api.NamespaceDefault).Get(pvcs[i].Name) | ||||
| 		pvc, err := testClient.PersistentVolumeClaims(ns.Name).Get(pvcs[i].Name) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("Unexpected error getting pvc: %v", err) | ||||
| 		} | ||||
| @@ -610,22 +636,27 @@ func TestPersistentVolumeMultiPVsPVCs(t *testing.T) { | ||||
| // TestPersistentVolumeProvisionMultiPVCs tests provisioning of many PVCs. | ||||
| // This test is configurable by KUBE_INTEGRATION_PV_* variables. | ||||
| func TestPersistentVolumeProvisionMultiPVCs(t *testing.T) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	_, s := framework.RunAMaster(nil) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	testClient, binder, watchPV, watchPVC := createClients(t, s) | ||||
| 	ns := framework.CreateTestingNamespace("provision-multi-pvs", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	testClient, binder, watchPV, watchPVC := createClients(ns, t, s) | ||||
| 	defer watchPV.Stop() | ||||
| 	defer watchPVC.Stop() | ||||
|  | ||||
| 	// NOTE: This test cannot run in parallel, because it is creating and deleting | ||||
| 	// non-namespaced objects (PersistenceVolumes). | ||||
| 	defer testClient.Core().PersistentVolumes().DeleteCollection(nil, api.ListOptions{}) | ||||
|  | ||||
| 	binder.Run() | ||||
| 	defer binder.Stop() | ||||
|  | ||||
| 	objCount := getObjectCount() | ||||
| 	pvcs := make([]*api.PersistentVolumeClaim, objCount) | ||||
| 	for i := 0; i < objCount; i++ { | ||||
| 		pvc := createPVC("pvc-provision-"+strconv.Itoa(i), "1G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}) | ||||
| 		pvc := createPVC("pvc-provision-"+strconv.Itoa(i), ns.Name, "1G", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}) | ||||
| 		pvc.Annotations = map[string]string{ | ||||
| 			"volume.alpha.kubernetes.io/storage-class": "", | ||||
| 		} | ||||
| @@ -637,7 +668,7 @@ func TestPersistentVolumeProvisionMultiPVCs(t *testing.T) { | ||||
| 	// early. It gets stuck with >3000 claims. | ||||
| 	go func() { | ||||
| 		for i := 0; i < objCount; i++ { | ||||
| 			_, _ = testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvcs[i]) | ||||
| 			_, _ = testClient.PersistentVolumeClaims(ns.Name).Create(pvcs[i]) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| @@ -666,7 +697,7 @@ func TestPersistentVolumeProvisionMultiPVCs(t *testing.T) { | ||||
|  | ||||
| 	// Delete the claims | ||||
| 	for i := 0; i < objCount; i++ { | ||||
| 		_ = testClient.PersistentVolumeClaims(api.NamespaceDefault).Delete(pvcs[i].Name, nil) | ||||
| 		_ = testClient.PersistentVolumeClaims(ns.Name).Delete(pvcs[i].Name, nil) | ||||
| 	} | ||||
|  | ||||
| 	// Wait for the PVs to get deleted by listing remaining volumes | ||||
| @@ -689,16 +720,20 @@ func TestPersistentVolumeProvisionMultiPVCs(t *testing.T) { | ||||
| // TestPersistentVolumeMultiPVsDiffAccessModes tests binding of one PVC to two | ||||
| // PVs with different access modes. | ||||
| func TestPersistentVolumeMultiPVsDiffAccessModes(t *testing.T) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	_, s := framework.RunAMaster(nil) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	testClient, controller, watchPV, watchPVC := createClients(t, s) | ||||
| 	ns := framework.CreateTestingNamespace("multi-pvs-diff-access", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	testClient, controller, watchPV, watchPVC := createClients(ns, t, s) | ||||
| 	defer watchPV.Stop() | ||||
| 	defer watchPVC.Stop() | ||||
|  | ||||
| 	// NOTE: This test cannot run in parallel, because it is creating and deleting | ||||
| 	// non-namespaced objects (PersistenceVolumes). | ||||
| 	defer testClient.Core().PersistentVolumes().DeleteCollection(nil, api.ListOptions{}) | ||||
|  | ||||
| 	controller.Run() | ||||
| 	defer controller.Stop() | ||||
|  | ||||
| @@ -708,7 +743,7 @@ func TestPersistentVolumeMultiPVsDiffAccessModes(t *testing.T) { | ||||
| 	pv_rwm := createPV("pv-rwm", "/tmp/bar", "10G", | ||||
| 		[]api.PersistentVolumeAccessMode{api.ReadWriteMany}, api.PersistentVolumeReclaimRetain) | ||||
|  | ||||
| 	pvc := createPVC("pvc-rwm", "5G", []api.PersistentVolumeAccessMode{api.ReadWriteMany}) | ||||
| 	pvc := createPVC("pvc-rwm", ns.Name, "5G", []api.PersistentVolumeAccessMode{api.ReadWriteMany}) | ||||
|  | ||||
| 	_, err := testClient.PersistentVolumes().Create(pv_rwm) | ||||
| 	if err != nil { | ||||
| @@ -720,7 +755,7 @@ func TestPersistentVolumeMultiPVsDiffAccessModes(t *testing.T) { | ||||
| 	} | ||||
| 	t.Log("volumes created") | ||||
|  | ||||
| 	_, err = testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvc) | ||||
| 	_, err = testClient.PersistentVolumeClaims(ns.Name).Create(pvc) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("Failed to create PersistentVolumeClaim: %v", err) | ||||
| 	} | ||||
| @@ -729,7 +764,7 @@ func TestPersistentVolumeMultiPVsDiffAccessModes(t *testing.T) { | ||||
| 	// wait until the controller pairs the volume and claim | ||||
| 	waitForAnyPersistentVolumePhase(watchPV, api.VolumeBound) | ||||
| 	t.Log("volume bound") | ||||
| 	waitForPersistentVolumeClaimPhase(testClient, pvc.Name, watchPVC, api.ClaimBound) | ||||
| 	waitForPersistentVolumeClaimPhase(testClient, pvc.Name, ns.Name, watchPVC, api.ClaimBound) | ||||
| 	t.Log("claim bound") | ||||
|  | ||||
| 	// only RWM PV is bound | ||||
| @@ -752,7 +787,7 @@ func TestPersistentVolumeMultiPVsDiffAccessModes(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| 	// deleting a claim releases the volume | ||||
| 	if err := testClient.PersistentVolumeClaims(api.NamespaceDefault).Delete(pvc.Name, nil); err != nil { | ||||
| 	if err := testClient.PersistentVolumeClaims(ns.Name).Delete(pvc.Name, nil); err != nil { | ||||
| 		t.Errorf("error deleting claim %s", pvc.Name) | ||||
| 	} | ||||
| 	t.Log("claim deleted") | ||||
| @@ -782,9 +817,9 @@ func waitForPersistentVolumePhase(client *clientset.Clientset, pvName string, w | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func waitForPersistentVolumeClaimPhase(client *clientset.Clientset, claimName string, w watch.Interface, phase api.PersistentVolumeClaimPhase) { | ||||
| func waitForPersistentVolumeClaimPhase(client *clientset.Clientset, claimName, namespace string, w watch.Interface, phase api.PersistentVolumeClaimPhase) { | ||||
| 	// Check if the claim is already in requested phase | ||||
| 	claim, err := client.Core().PersistentVolumeClaims(api.NamespaceDefault).Get(claimName) | ||||
| 	claim, err := client.Core().PersistentVolumeClaims(namespace).Get(claimName) | ||||
| 	if err == nil && claim.Status.Phase == phase { | ||||
| 		return | ||||
| 	} | ||||
| @@ -831,11 +866,21 @@ func waitForAnyPersistentVolumeClaimPhase(w watch.Interface, phase api.Persisten | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func createClients(t *testing.T, s *httptest.Server) (*clientset.Clientset, *persistentvolumecontroller.PersistentVolumeController, watch.Interface, watch.Interface) { | ||||
| func createClients(ns *api.Namespace, t *testing.T, s *httptest.Server) (*clientset.Clientset, *persistentvolumecontroller.PersistentVolumeController, watch.Interface, watch.Interface) { | ||||
| 	// Use higher QPS and Burst, there is a test for race conditions which | ||||
| 	// creates many objects and default values were too low. | ||||
| 	binderClient := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}, QPS: 1000000, Burst: 1000000}) | ||||
| 	testClient := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}, QPS: 1000000, Burst: 1000000}) | ||||
| 	binderClient := clientset.NewForConfigOrDie(&restclient.Config{ | ||||
| 		Host:          s.URL, | ||||
| 		ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}, | ||||
| 		QPS:           1000000, | ||||
| 		Burst:         1000000, | ||||
| 	}) | ||||
| 	testClient := clientset.NewForConfigOrDie(&restclient.Config{ | ||||
| 		Host:          s.URL, | ||||
| 		ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}, | ||||
| 		QPS:           1000000, | ||||
| 		Burst:         1000000, | ||||
| 	}) | ||||
|  | ||||
| 	host := volumetest.NewFakeVolumeHost("/tmp/fake", nil, nil, "" /* rootContext */) | ||||
| 	plugin := &volumetest.FakeVolumePlugin{ | ||||
| @@ -860,7 +905,7 @@ func createClients(t *testing.T, s *httptest.Server) (*clientset.Clientset, *per | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to watch PersistentVolumes: %v", err) | ||||
| 	} | ||||
| 	watchPVC, err := testClient.PersistentVolumeClaims(api.NamespaceDefault).Watch(api.ListOptions{}) | ||||
| 	watchPVC, err := testClient.PersistentVolumeClaims(ns.Name).Watch(api.ListOptions{}) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to watch PersistentVolumeClaimss: %v", err) | ||||
| 	} | ||||
| @@ -880,11 +925,11 @@ func createPV(name, path, cap string, mode []api.PersistentVolumeAccessMode, rec | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func createPVC(name, cap string, mode []api.PersistentVolumeAccessMode) *api.PersistentVolumeClaim { | ||||
| func createPVC(name, namespace, cap string, mode []api.PersistentVolumeAccessMode) *api.PersistentVolumeClaim { | ||||
| 	return &api.PersistentVolumeClaim{ | ||||
| 		ObjectMeta: api.ObjectMeta{ | ||||
| 			Name:      name, | ||||
| 			Namespace: api.NamespaceDefault, | ||||
| 			Namespace: namespace, | ||||
| 		}, | ||||
| 		Spec: api.PersistentVolumeClaimSpec{ | ||||
| 			Resources:   api.ResourceRequirements{Requests: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse(cap)}}, | ||||
|   | ||||
| @@ -54,9 +54,6 @@ func init() { | ||||
| // 	quota_test.go:100: Took 4.196205966s to scale up without quota | ||||
| // 	quota_test.go:115: Took 12.021640372s to scale up with quota | ||||
| func TestQuota(t *testing.T) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	initializationCh := make(chan struct{}) | ||||
| 	// Set up a master | ||||
| 	var m *master.Master | ||||
| @@ -81,6 +78,11 @@ func TestQuota(t *testing.T) { | ||||
| 	} | ||||
| 	close(initializationCh) | ||||
|  | ||||
| 	ns := framework.CreateTestingNamespace("quotaed", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
| 	ns2 := framework.CreateTestingNamespace("non-quotaed", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns2, s, t) | ||||
|  | ||||
| 	controllerCh := make(chan struct{}) | ||||
| 	defer close(controllerCh) | ||||
|  | ||||
| @@ -102,12 +104,15 @@ func TestQuota(t *testing.T) { | ||||
| 	go resourcequotacontroller.NewResourceQuotaController(resourceQuotaControllerOptions).Run(2, controllerCh) | ||||
|  | ||||
| 	startTime := time.Now() | ||||
| 	scale(t, api.NamespaceDefault, clientset) | ||||
| 	scale(t, ns2.Name, clientset) | ||||
| 	endTime := time.Now() | ||||
| 	t.Logf("Took %v to scale up without quota", endTime.Sub(startTime)) | ||||
|  | ||||
| 	quota := &api.ResourceQuota{ | ||||
| 		ObjectMeta: api.ObjectMeta{Name: "quota"}, | ||||
| 		ObjectMeta: api.ObjectMeta{ | ||||
| 			Name:      "quota", | ||||
| 			Namespace: ns.Name, | ||||
| 		}, | ||||
| 		Spec: api.ResourceQuotaSpec{ | ||||
| 			Hard: api.ResourceList{ | ||||
| 				api.ResourcePods: resource.MustParse("1000"), | ||||
| @@ -128,7 +133,7 @@ func waitForQuota(t *testing.T, quota *api.ResourceQuota, clientset *clientset.C | ||||
| 		t.Fatalf("unexpected error: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if _, err := clientset.Core().ResourceQuotas("quotaed").Create(quota); err != nil { | ||||
| 	if _, err := clientset.Core().ResourceQuotas(quota.Namespace).Create(quota); err != nil { | ||||
| 		t.Fatalf("unexpected error: %v", err) | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -26,14 +26,12 @@ import ( | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"net/http/httptest" | ||||
| 	"net/http/httputil" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/golang/glog" | ||||
|  | ||||
| 	"k8s.io/kubernetes/pkg/api" | ||||
| 	"k8s.io/kubernetes/pkg/api/testapi" | ||||
| 	"k8s.io/kubernetes/pkg/api/unversioned" | ||||
| 	"k8s.io/kubernetes/pkg/api/v1" | ||||
| @@ -228,6 +226,15 @@ var ( | ||||
|     } | ||||
|   } | ||||
| } | ||||
| ` | ||||
| 	podNamespace = ` | ||||
| { | ||||
|   "apiVersion": "` + testapi.Default.GroupVersion().String() + `", | ||||
|   "kind": "Namespace", | ||||
|   "metadata": { | ||||
| 	"name": "pod-namespace"%s | ||||
|   } | ||||
| } | ||||
| ` | ||||
| 	jobNamespace = ` | ||||
| { | ||||
| @@ -237,6 +244,15 @@ var ( | ||||
| 	"name": "job-namespace"%s | ||||
|   } | ||||
| } | ||||
| ` | ||||
| 	forbiddenNamespace = ` | ||||
| { | ||||
|   "apiVersion": "` + testapi.Default.GroupVersion().String() + `", | ||||
|   "kind": "Namespace", | ||||
|   "metadata": { | ||||
| 	"name": "forbidden-namespace"%s | ||||
|   } | ||||
| } | ||||
| ` | ||||
| ) | ||||
|  | ||||
| @@ -292,16 +308,19 @@ func TestRBAC(t *testing.T) { | ||||
| 				}, | ||||
| 			}, | ||||
| 			requests: []request{ | ||||
| 				// Create the namespace used later in the test | ||||
| 				{superUser, "POST", "", "namespaces", "", "", podNamespace, http.StatusCreated}, | ||||
|  | ||||
| 				{superUser, "GET", "", "pods", "", "", "", http.StatusOK}, | ||||
| 				{superUser, "GET", "", "pods", api.NamespaceDefault, "a", "", http.StatusNotFound}, | ||||
| 				{superUser, "POST", "", "pods", api.NamespaceDefault, "", aPod, http.StatusCreated}, | ||||
| 				{superUser, "GET", "", "pods", api.NamespaceDefault, "a", "", http.StatusOK}, | ||||
| 				{superUser, "GET", "", "pods", "pod-namespace", "a", "", http.StatusNotFound}, | ||||
| 				{superUser, "POST", "", "pods", "pod-namespace", "", aPod, http.StatusCreated}, | ||||
| 				{superUser, "GET", "", "pods", "pod-namespace", "a", "", http.StatusOK}, | ||||
|  | ||||
| 				{"bob", "GET", "", "pods", "", "", "", http.StatusForbidden}, | ||||
| 				{"bob", "GET", "", "pods", api.NamespaceDefault, "a", "", http.StatusForbidden}, | ||||
| 				{"bob", "GET", "", "pods", "pod-namespace", "a", "", http.StatusForbidden}, | ||||
|  | ||||
| 				{"pod-reader", "GET", "", "pods", "", "", "", http.StatusOK}, | ||||
| 				{"pod-reader", "POST", "", "pods", api.NamespaceDefault, "", aPod, http.StatusForbidden}, | ||||
| 				{"pod-reader", "POST", "", "pods", "pod-namespace", "", aPod, http.StatusForbidden}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -330,21 +349,22 @@ func TestRBAC(t *testing.T) { | ||||
| 			requests: []request{ | ||||
| 				// Create the namespace used later in the test | ||||
| 				{superUser, "POST", "", "namespaces", "", "", jobNamespace, http.StatusCreated}, | ||||
| 				{superUser, "POST", "", "namespaces", "", "", forbiddenNamespace, http.StatusCreated}, | ||||
|  | ||||
| 				{"user-with-no-permissions", "POST", "batch", "jobs", "job-namespace", "", aJob, http.StatusForbidden}, | ||||
| 				{"user-with-no-permissions", "GET", "batch", "jobs", "job-namespace", "pi", "", http.StatusForbidden}, | ||||
|  | ||||
| 				// job-writer-namespace cannot write to the "default" namespace | ||||
| 				{"job-writer-namespace", "GET", "batch", "jobs", "default", "", "", http.StatusForbidden}, | ||||
| 				{"job-writer-namespace", "GET", "batch", "jobs", "default", "pi", "", http.StatusForbidden}, | ||||
| 				{"job-writer-namespace", "POST", "batch", "jobs", "default", "", aJob, http.StatusForbidden}, | ||||
| 				{"job-writer-namespace", "GET", "batch", "jobs", "default", "pi", "", http.StatusForbidden}, | ||||
| 				// job-writer-namespace cannot write to the "forbidden-namespace" | ||||
| 				{"job-writer-namespace", "GET", "batch", "jobs", "forbidden-namespace", "", "", http.StatusForbidden}, | ||||
| 				{"job-writer-namespace", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusForbidden}, | ||||
| 				{"job-writer-namespace", "POST", "batch", "jobs", "forbidden-namespace", "", aJob, http.StatusForbidden}, | ||||
| 				{"job-writer-namespace", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusForbidden}, | ||||
|  | ||||
| 				// job-writer can write to any namespace | ||||
| 				{"job-writer", "GET", "batch", "jobs", "default", "", "", http.StatusOK}, | ||||
| 				{"job-writer", "GET", "batch", "jobs", "default", "pi", "", http.StatusNotFound}, | ||||
| 				{"job-writer", "POST", "batch", "jobs", "default", "", aJob, http.StatusCreated}, | ||||
| 				{"job-writer", "GET", "batch", "jobs", "default", "pi", "", http.StatusOK}, | ||||
| 				{"job-writer", "GET", "batch", "jobs", "forbidden-namespace", "", "", http.StatusOK}, | ||||
| 				{"job-writer", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusNotFound}, | ||||
| 				{"job-writer", "POST", "batch", "jobs", "forbidden-namespace", "", aJob, http.StatusCreated}, | ||||
| 				{"job-writer", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusOK}, | ||||
|  | ||||
| 				{"job-writer-namespace", "GET", "batch", "jobs", "job-namespace", "", "", http.StatusOK}, | ||||
| 				{"job-writer-namespace", "GET", "batch", "jobs", "job-namespace", "pi", "", http.StatusNotFound}, | ||||
| @@ -355,24 +375,13 @@ func TestRBAC(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| 	for i, tc := range tests { | ||||
| 		// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 		framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 		var m *master.Master | ||||
| 		s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 			m.Handler.ServeHTTP(w, r) | ||||
| 		})) | ||||
| 		defer s.Close() | ||||
|  | ||||
| 		// Create an API Server. | ||||
| 		masterConfig := framework.NewIntegrationTestMasterConfig() | ||||
| 		masterConfig.Authorizer = newRBACAuthorizer(t, superUser, masterConfig) | ||||
| 		masterConfig.Authenticator = newFakeAuthenticator() | ||||
| 		masterConfig.AuthorizerRBACSuperUser = superUser | ||||
| 		m, err := master.New(masterConfig) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("case %d: error bringing up master: %v", i, err) | ||||
| 		} | ||||
| 		_, s := framework.RunAMaster(masterConfig) | ||||
| 		defer s.Close() | ||||
|  | ||||
| 		// Bootstrap the API Server with the test case's initial roles. | ||||
| 		if err := tc.bootstrapRoles.bootstrap(clientForUser(superUser), s.URL); err != nil { | ||||
|   | ||||
| @@ -50,12 +50,12 @@ type nodeStateManager struct { | ||||
| } | ||||
|  | ||||
| func TestUnschedulableNodes(t *testing.T) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	_, s := framework.RunAMaster(nil) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	ns := framework.CreateTestingNamespace("unschedulable-nodes", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	restClient := client.NewOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}) | ||||
|  | ||||
| 	schedulerConfigFactory := factory.NewConfigFactory(restClient, api.DefaultSchedulerName, api.DefaultHardPodAffinitySymmetricWeight, api.DefaultFailureDomains) | ||||
| @@ -65,12 +65,12 @@ func TestUnschedulableNodes(t *testing.T) { | ||||
| 	} | ||||
| 	eventBroadcaster := record.NewBroadcaster() | ||||
| 	schedulerConfig.Recorder = eventBroadcaster.NewRecorder(api.EventSource{Component: api.DefaultSchedulerName}) | ||||
| 	eventBroadcaster.StartRecordingToSink(restClient.Events("")) | ||||
| 	eventBroadcaster.StartRecordingToSink(restClient.Events(ns.Name)) | ||||
| 	scheduler.New(schedulerConfig).Run() | ||||
|  | ||||
| 	defer close(schedulerConfig.StopEverything) | ||||
|  | ||||
| 	DoTestUnschedulableNodes(t, restClient, schedulerConfigFactory.NodeLister.Store) | ||||
| 	DoTestUnschedulableNodes(t, restClient, ns, schedulerConfigFactory.NodeLister.Store) | ||||
| } | ||||
|  | ||||
| func podScheduled(c *client.Client, podNamespace, podName string) wait.ConditionFunc { | ||||
| @@ -119,7 +119,11 @@ func waitForReflection(t *testing.T, s cache.Store, key string, passFunc func(n | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func DoTestUnschedulableNodes(t *testing.T, restClient *client.Client, nodeStore cache.Store) { | ||||
| func DoTestUnschedulableNodes(t *testing.T, restClient *client.Client, ns *api.Namespace, nodeStore cache.Store) { | ||||
| 	// NOTE: This test cannot run in parallel, because it is creating and deleting | ||||
| 	// non-namespaced objects (Nodes). | ||||
| 	defer restClient.Nodes().DeleteCollection(nil, api.ListOptions{}) | ||||
|  | ||||
| 	goodCondition := api.NodeCondition{ | ||||
| 		Type:              api.NodeReady, | ||||
| 		Status:            api.ConditionTrue, | ||||
| @@ -246,7 +250,7 @@ func DoTestUnschedulableNodes(t *testing.T, restClient *client.Client, nodeStore | ||||
| 				Containers: []api.Container{{Name: "container", Image: e2e.GetPauseImageName(restClient)}}, | ||||
| 			}, | ||||
| 		} | ||||
| 		myPod, err := restClient.Pods(api.NamespaceDefault).Create(pod) | ||||
| 		myPod, err := restClient.Pods(ns.Name).Create(pod) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("Failed to create pod: %v", err) | ||||
| 		} | ||||
| @@ -277,7 +281,7 @@ func DoTestUnschedulableNodes(t *testing.T, restClient *client.Client, nodeStore | ||||
| 			t.Logf("Test %d: Pod got scheduled on a schedulable node", i) | ||||
| 		} | ||||
|  | ||||
| 		err = restClient.Pods(api.NamespaceDefault).Delete(myPod.Name, api.NewDeleteOptions(0)) | ||||
| 		err = restClient.Pods(ns.Name).Delete(myPod.Name, api.NewDeleteOptions(0)) | ||||
| 		if err != nil { | ||||
| 			t.Errorf("Failed to delete pod: %v", err) | ||||
| 		} | ||||
| @@ -289,14 +293,14 @@ func DoTestUnschedulableNodes(t *testing.T, restClient *client.Client, nodeStore | ||||
| } | ||||
|  | ||||
| func TestMultiScheduler(t *testing.T) { | ||||
| 	// TODO: Limit the test to a single non-default namespace and clean this up at the end. | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	_, s := framework.RunAMaster(nil) | ||||
| 	// TODO: Uncomment when fix #19254 | ||||
| 	// This seems to be a different issue - it still doesn't work. | ||||
| 	// defer s.Close() | ||||
|  | ||||
| 	ns := framework.CreateTestingNamespace("multi-scheduler", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	/* | ||||
| 		This integration tests the multi-scheduler feature in the following way: | ||||
| 		1. create a default scheduler | ||||
| @@ -319,6 +323,10 @@ func TestMultiScheduler(t *testing.T) { | ||||
| 	// 1. create and start default-scheduler | ||||
| 	restClient := client.NewOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}) | ||||
|  | ||||
| 	// NOTE: This test cannot run in parallel, because it is creating and deleting | ||||
| 	// non-namespaced objects (Nodes). | ||||
| 	defer restClient.Nodes().DeleteCollection(nil, api.ListOptions{}) | ||||
|  | ||||
| 	schedulerConfigFactory := factory.NewConfigFactory(restClient, api.DefaultSchedulerName, api.DefaultHardPodAffinitySymmetricWeight, api.DefaultFailureDomains) | ||||
| 	schedulerConfig, err := schedulerConfigFactory.Create() | ||||
| 	if err != nil { | ||||
| @@ -326,7 +334,7 @@ func TestMultiScheduler(t *testing.T) { | ||||
| 	} | ||||
| 	eventBroadcaster := record.NewBroadcaster() | ||||
| 	schedulerConfig.Recorder = eventBroadcaster.NewRecorder(api.EventSource{Component: api.DefaultSchedulerName}) | ||||
| 	eventBroadcaster.StartRecordingToSink(restClient.Events("")) | ||||
| 	eventBroadcaster.StartRecordingToSink(restClient.Events(ns.Name)) | ||||
| 	scheduler.New(schedulerConfig).Run() | ||||
| 	// default-scheduler will be stopped later | ||||
|  | ||||
| @@ -344,21 +352,21 @@ func TestMultiScheduler(t *testing.T) { | ||||
|  | ||||
| 	// 3. create 3 pods for testing | ||||
| 	podWithNoAnnotation := createPod(restClient, "pod-with-no-annotation", nil) | ||||
| 	testPodNoAnnotation, err := restClient.Pods(api.NamespaceDefault).Create(podWithNoAnnotation) | ||||
| 	testPodNoAnnotation, err := restClient.Pods(ns.Name).Create(podWithNoAnnotation) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to create pod: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	schedulerAnnotationFitsDefault := map[string]string{"scheduler.alpha.kubernetes.io/name": "default-scheduler"} | ||||
| 	podWithAnnotationFitsDefault := createPod(restClient, "pod-with-annotation-fits-default", schedulerAnnotationFitsDefault) | ||||
| 	testPodWithAnnotationFitsDefault, err := restClient.Pods(api.NamespaceDefault).Create(podWithAnnotationFitsDefault) | ||||
| 	testPodWithAnnotationFitsDefault, err := restClient.Pods(ns.Name).Create(podWithAnnotationFitsDefault) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to create pod: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	schedulerAnnotationFitsFoo := map[string]string{"scheduler.alpha.kubernetes.io/name": "foo-scheduler"} | ||||
| 	podWithAnnotationFitsFoo := createPod(restClient, "pod-with-annotation-fits-foo", schedulerAnnotationFitsFoo) | ||||
| 	testPodWithAnnotationFitsFoo, err := restClient.Pods(api.NamespaceDefault).Create(podWithAnnotationFitsFoo) | ||||
| 	testPodWithAnnotationFitsFoo, err := restClient.Pods(ns.Name).Create(podWithAnnotationFitsFoo) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to create pod: %v", err) | ||||
| 	} | ||||
| @@ -397,7 +405,7 @@ func TestMultiScheduler(t *testing.T) { | ||||
| 	} | ||||
| 	eventBroadcaster2 := record.NewBroadcaster() | ||||
| 	schedulerConfig2.Recorder = eventBroadcaster2.NewRecorder(api.EventSource{Component: "foo-scheduler"}) | ||||
| 	eventBroadcaster2.StartRecordingToSink(restClient2.Events("")) | ||||
| 	eventBroadcaster2.StartRecordingToSink(restClient2.Events(ns.Name)) | ||||
| 	scheduler.New(schedulerConfig2).Run() | ||||
|  | ||||
| 	defer close(schedulerConfig2.StopEverything) | ||||
| @@ -412,11 +420,11 @@ func TestMultiScheduler(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| 	//	7. delete the pods that were scheduled by the default scheduler, and stop the default scheduler | ||||
| 	err = restClient.Pods(api.NamespaceDefault).Delete(testPodNoAnnotation.Name, api.NewDeleteOptions(0)) | ||||
| 	err = restClient.Pods(ns.Name).Delete(testPodNoAnnotation.Name, api.NewDeleteOptions(0)) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("Failed to delete pod: %v", err) | ||||
| 	} | ||||
| 	err = restClient.Pods(api.NamespaceDefault).Delete(testPodWithAnnotationFitsDefault.Name, api.NewDeleteOptions(0)) | ||||
| 	err = restClient.Pods(ns.Name).Delete(testPodWithAnnotationFitsDefault.Name, api.NewDeleteOptions(0)) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("Failed to delete pod: %v", err) | ||||
| 	} | ||||
| @@ -434,11 +442,11 @@ func TestMultiScheduler(t *testing.T) { | ||||
| 		//		- note: these two pods belong to default scheduler which no longer exists | ||||
| 		podWithNoAnnotation2 := createPod("pod-with-no-annotation2", nil) | ||||
| 		podWithAnnotationFitsDefault2 := createPod("pod-with-annotation-fits-default2", schedulerAnnotationFitsDefault) | ||||
| 		testPodNoAnnotation2, err := restClient.Pods(api.NamespaceDefault).Create(podWithNoAnnotation2) | ||||
| 		testPodNoAnnotation2, err := restClient.Pods(ns.Name).Create(podWithNoAnnotation2) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("Failed to create pod: %v", err) | ||||
| 		} | ||||
| 		testPodWithAnnotationFitsDefault2, err := restClient.Pods(api.NamespaceDefault).Create(podWithAnnotationFitsDefault2) | ||||
| 		testPodWithAnnotationFitsDefault2, err := restClient.Pods(ns.Name).Create(podWithAnnotationFitsDefault2) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("Failed to create pod: %v", err) | ||||
| 		} | ||||
| @@ -471,14 +479,19 @@ func createPod(client *client.Client, name string, annotation map[string]string) | ||||
|  | ||||
| // This test will verify scheduler can work well regardless of whether kubelet is allocatable aware or not. | ||||
| func TestAllocatable(t *testing.T) { | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	_, s := framework.RunAMaster(nil) | ||||
| 	defer s.Close() | ||||
|  | ||||
| 	ns := framework.CreateTestingNamespace("allocatable", s, t) | ||||
| 	defer framework.DeleteTestingNamespace(ns, s, t) | ||||
|  | ||||
| 	// 1. create and start default-scheduler | ||||
| 	restClient := client.NewOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}) | ||||
|  | ||||
| 	// NOTE: This test cannot run in parallel, because it is creating and deleting | ||||
| 	// non-namespaced objects (Nodes). | ||||
| 	defer restClient.Nodes().DeleteCollection(nil, api.ListOptions{}) | ||||
|  | ||||
| 	schedulerConfigFactory := factory.NewConfigFactory(restClient, api.DefaultSchedulerName, api.DefaultHardPodAffinitySymmetricWeight, api.DefaultFailureDomains) | ||||
| 	schedulerConfig, err := schedulerConfigFactory.Create() | ||||
| 	if err != nil { | ||||
| @@ -486,7 +499,7 @@ func TestAllocatable(t *testing.T) { | ||||
| 	} | ||||
| 	eventBroadcaster := record.NewBroadcaster() | ||||
| 	schedulerConfig.Recorder = eventBroadcaster.NewRecorder(api.EventSource{Component: api.DefaultSchedulerName}) | ||||
| 	eventBroadcaster.StartRecordingToSink(restClient.Events("")) | ||||
| 	eventBroadcaster.StartRecordingToSink(restClient.Events(ns.Name)) | ||||
| 	scheduler.New(schedulerConfig).Run() | ||||
| 	// default-scheduler will be stopped later | ||||
| 	defer close(schedulerConfig.StopEverything) | ||||
| @@ -528,7 +541,7 @@ func TestAllocatable(t *testing.T) { | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	testAllocPod, err := restClient.Pods(api.NamespaceDefault).Create(podResource) | ||||
| 	testAllocPod, err := restClient.Pods(ns.Name).Create(podResource) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Test allocatable unawareness failed to create pod: %v", err) | ||||
| 	} | ||||
| @@ -559,13 +572,13 @@ func TestAllocatable(t *testing.T) { | ||||
| 		t.Fatalf("Failed to update node with Status.Allocatable: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := restClient.Pods(api.NamespaceDefault).Delete(podResource.Name, &api.DeleteOptions{}); err != nil { | ||||
| 	if err := restClient.Pods(ns.Name).Delete(podResource.Name, &api.DeleteOptions{}); err != nil { | ||||
| 		t.Fatalf("Failed to remove first resource pod: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// 6. Make another pod with different name, same resource request | ||||
| 	podResource.ObjectMeta.Name = "pod-test-allocatable2" | ||||
| 	testAllocPod2, err := restClient.Pods(api.NamespaceDefault).Create(podResource) | ||||
| 	testAllocPod2, err := restClient.Pods(ns.Name).Create(podResource) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Test allocatable awareness failed to create pod: %v", err) | ||||
| 	} | ||||
|   | ||||
| @@ -336,8 +336,6 @@ func TestServiceAccountTokenAuthentication(t *testing.T) { | ||||
| // startServiceAccountTestServer returns a started server | ||||
| // It is the responsibility of the caller to ensure the returned stopFunc is called | ||||
| func startServiceAccountTestServer(t *testing.T) (*clientset.Clientset, restclient.Config, func()) { | ||||
| 	framework.DeleteAllEtcdKeys() | ||||
|  | ||||
| 	// Listener | ||||
| 	var m *master.Master | ||||
| 	apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 k8s-merge-robot
					k8s-merge-robot