func TestPlugin(t *testing.T) { var ( testPodUID = types.UID("test_pod_uid") testVolumeName = "test_volume_name" testNamespace = "test_configmap_namespace" testName = "test_configmap_name" volumeSpec = volumeSpec(testVolumeName, testName, 0644) configMap = configMap(testNamespace, testName) client = fake.NewSimpleClientset(&configMap) pluginMgr = volume.VolumePluginMgr{} tempDir, host = newTestHost(t, client) ) defer os.RemoveAll(tempDir) pluginMgr.InitPlugins(ProbeVolumePlugins(), host) plugin, err := pluginMgr.FindPluginByName(configMapPluginName) if err != nil { t.Errorf("Can't find the plugin by name") } pod := &v1.Pod{ObjectMeta: v1.ObjectMeta{Namespace: testNamespace, UID: testPodUID}} mounter, err := plugin.NewMounter(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{}) if err != nil { t.Errorf("Failed to make a new Mounter: %v", err) } if mounter == nil { t.Errorf("Got a nil Mounter") } vName, err := plugin.GetVolumeName(volume.NewSpecFromVolume(volumeSpec)) if err != nil { t.Errorf("Failed to GetVolumeName: %v", err) } if vName != "test_volume_name/test_configmap_name" { t.Errorf("Got unexpect VolumeName %v", vName) } volumePath := mounter.GetPath() if !strings.HasSuffix(volumePath, fmt.Sprintf("pods/test_pod_uid/volumes/kubernetes.io~configmap/test_volume_name")) { t.Errorf("Got unexpected path: %s", volumePath) } fsGroup := int64(1001) err = mounter.SetUp(&fsGroup) if err != nil { t.Errorf("Failed to setup volume: %v", err) } if _, err := os.Stat(volumePath); err != nil { if os.IsNotExist(err) { t.Errorf("SetUp() failed, volume path not created: %s", volumePath) } else { t.Errorf("SetUp() failed: %v", err) } } doTestConfigMapDataInVolume(volumePath, configMap, t) doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t) }
func TestDoNotDeleteMirrorPods(t *testing.T) { staticPod := getTestPod() staticPod.Annotations = map[string]string{kubetypes.ConfigSourceAnnotationKey: "file"} mirrorPod := getTestPod() mirrorPod.UID = "mirror-12345678" mirrorPod.Annotations = map[string]string{ kubetypes.ConfigSourceAnnotationKey: "api", kubetypes.ConfigMirrorAnnotationKey: "mirror", } // Set the deletion timestamp. mirrorPod.DeletionTimestamp = new(metav1.Time) client := fake.NewSimpleClientset(mirrorPod) m := newTestManager(client) m.podManager.AddPod(staticPod) m.podManager.AddPod(mirrorPod) // Verify setup. assert.True(t, kubepod.IsStaticPod(staticPod), "SetUp error: staticPod") assert.True(t, kubepod.IsMirrorPod(mirrorPod), "SetUp error: mirrorPod") assert.Equal(t, m.podManager.TranslatePodUID(mirrorPod.UID), staticPod.UID) status := getRandomPodStatus() now := metav1.Now() status.StartTime = &now m.SetPodStatus(staticPod, status) m.testSyncBatch() // Expect not to see an delete action. verifyActions(t, m.kubeClient, []core.Action{ core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: schema.GroupVersionResource{Resource: "pods"}}}, core.UpdateActionImpl{ActionImpl: core.ActionImpl{Verb: "update", Resource: schema.GroupVersionResource{Resource: "pods"}, Subresource: "status"}}, }) }
// TestGetNodeAddresses verifies that proper results are returned // when requesting node addresses. func TestGetNodeAddresses(t *testing.T) { assert := assert.New(t) fakeNodeClient := fake.NewSimpleClientset(makeNodeList([]string{"node1", "node2"}, apiv1.NodeResources{})).Core().Nodes() addressProvider := nodeAddressProvider{fakeNodeClient} // Fail case (no addresses associated with nodes) nodes, _ := fakeNodeClient.List(apiv1.ListOptions{}) addrs, err := addressProvider.externalAddresses() assert.Error(err, "addresses should have caused an error as there are no addresses.") assert.Equal([]string(nil), addrs) // Pass case with External type IP nodes, _ = fakeNodeClient.List(apiv1.ListOptions{}) for index := range nodes.Items { nodes.Items[index].Status.Addresses = []apiv1.NodeAddress{{Type: apiv1.NodeExternalIP, Address: "127.0.0.1"}} fakeNodeClient.Update(&nodes.Items[index]) } addrs, err = addressProvider.externalAddresses() assert.NoError(err, "addresses should not have returned an error.") assert.Equal([]string{"127.0.0.1", "127.0.0.1"}, addrs) // Pass case with LegacyHost type IP nodes, _ = fakeNodeClient.List(apiv1.ListOptions{}) for index := range nodes.Items { nodes.Items[index].Status.Addresses = []apiv1.NodeAddress{{Type: apiv1.NodeLegacyHostIP, Address: "127.0.0.2"}} fakeNodeClient.Update(&nodes.Items[index]) } addrs, err = addressProvider.externalAddresses() assert.NoError(err, "addresses failback should not have returned an error.") assert.Equal([]string{"127.0.0.2", "127.0.0.2"}, addrs) }
func TestPersistentClaimReadOnlyFlag(t *testing.T) { tmpDir, err := utiltesting.MkTmpdir("glusterfs_test") if err != nil { t.Fatalf("error creating temp dir: %v", err) } defer os.RemoveAll(tmpDir) pv := &v1.PersistentVolume{ ObjectMeta: v1.ObjectMeta{ Name: "pvA", }, Spec: v1.PersistentVolumeSpec{ PersistentVolumeSource: v1.PersistentVolumeSource{ Glusterfs: &v1.GlusterfsVolumeSource{EndpointsName: "ep", Path: "vol", ReadOnly: false}, }, ClaimRef: &v1.ObjectReference{ Name: "claimA", }, }, } claim := &v1.PersistentVolumeClaim{ ObjectMeta: v1.ObjectMeta{ Name: "claimA", Namespace: "nsA", }, Spec: v1.PersistentVolumeClaimSpec{ VolumeName: "pvA", }, Status: v1.PersistentVolumeClaimStatus{ Phase: v1.ClaimBound, }, } ep := &v1.Endpoints{ ObjectMeta: v1.ObjectMeta{ Namespace: "nsA", Name: "ep", }, Subsets: []v1.EndpointSubset{{ Addresses: []v1.EndpointAddress{{IP: "127.0.0.1"}}, Ports: []v1.EndpointPort{{Name: "foo", Port: 80, Protocol: v1.ProtocolTCP}}, }}, } client := fake.NewSimpleClientset(pv, claim, ep) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, client, nil)) plug, _ := plugMgr.FindPluginByName(glusterfsPluginName) // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes spec := volume.NewSpecFromPersistentVolume(pv, true) pod := &v1.Pod{ObjectMeta: v1.ObjectMeta{Namespace: "nsA", UID: types.UID("poduid")}} mounter, _ := plug.NewMounter(spec, pod, volume.VolumeOptions{}) if !mounter.GetAttributes().ReadOnly { t.Errorf("Expected true for mounter.IsReadOnly") } }
func TestPersistentClaimReadOnlyFlag(t *testing.T) { tmpDir, err := utiltesting.MkTmpdir("iscsi_test") if err != nil { t.Fatalf("error creating temp dir: %v", err) } defer os.RemoveAll(tmpDir) pv := &v1.PersistentVolume{ ObjectMeta: v1.ObjectMeta{ Name: "pvA", }, Spec: v1.PersistentVolumeSpec{ PersistentVolumeSource: v1.PersistentVolumeSource{ ISCSI: &v1.ISCSIVolumeSource{ TargetPortal: "127.0.0.1:3260", IQN: "iqn.2014-12.server:storage.target01", FSType: "ext4", Lun: 0, }, }, ClaimRef: &v1.ObjectReference{ Name: "claimA", }, }, } claim := &v1.PersistentVolumeClaim{ ObjectMeta: v1.ObjectMeta{ Name: "claimA", Namespace: "nsA", }, Spec: v1.PersistentVolumeClaimSpec{ VolumeName: "pvA", }, Status: v1.PersistentVolumeClaimStatus{ Phase: v1.ClaimBound, }, } client := fake.NewSimpleClientset(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, client, nil)) plug, _ := plugMgr.FindPluginByName(iscsiPluginName) // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes spec := volume.NewSpecFromPersistentVolume(pv, true) pod := &v1.Pod{ObjectMeta: v1.ObjectMeta{UID: types.UID("poduid")}} mounter, _ := plug.NewMounter(spec, pod, volume.VolumeOptions{}) if !mounter.GetAttributes().ReadOnly { t.Errorf("Expected true for mounter.IsReadOnly") } }
func TestPersistentClaimReadOnlyFlag(t *testing.T) { tmpDir, err := utiltesting.MkTmpdir("fc_test") if err != nil { t.Fatalf("error creating temp dir: %v", err) } defer os.RemoveAll(tmpDir) lun := int32(0) pv := &v1.PersistentVolume{ ObjectMeta: v1.ObjectMeta{ Name: "pvA", }, Spec: v1.PersistentVolumeSpec{ PersistentVolumeSource: v1.PersistentVolumeSource{ FC: &v1.FCVolumeSource{ TargetWWNs: []string{"some_wwn"}, FSType: "ext4", Lun: &lun, }, }, ClaimRef: &v1.ObjectReference{ Name: "claimA", }, }, } claim := &v1.PersistentVolumeClaim{ ObjectMeta: v1.ObjectMeta{ Name: "claimA", Namespace: "nsA", }, Spec: v1.PersistentVolumeClaimSpec{ VolumeName: "pvA", }, Status: v1.PersistentVolumeClaimStatus{ Phase: v1.ClaimBound, }, } client := fake.NewSimpleClientset(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, client, nil)) plug, _ := plugMgr.FindPluginByName(fcPluginName) // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes spec := volume.NewSpecFromPersistentVolume(pv, true) pod := &v1.Pod{ObjectMeta: v1.ObjectMeta{UID: types.UID("poduid")}} mounter, _ := plug.NewMounter(spec, pod, volume.VolumeOptions{}) if !mounter.GetAttributes().ReadOnly { t.Errorf("Expected true for mounter.IsReadOnly") } }
func TestFederationQueryWithoutCache(t *testing.T) { kd := newKubeDNS() kd.config.Federations = map[string]string{ "myfederation": "example.com", "secondfederation": "second.example.com", } kd.kubeClient = fake.NewSimpleClientset(newNodes()) testValidFederationQueries(t, kd) testInvalidFederationQueries(t, kd) }
func TestSyncBatch(t *testing.T) { syncer := newTestManager(&fake.Clientset{}) testPod := getTestPod() syncer.kubeClient = fake.NewSimpleClientset(testPod) syncer.SetPodStatus(testPod, getRandomPodStatus()) syncer.testSyncBatch() verifyActions(t, syncer.kubeClient, []core.Action{ core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: schema.GroupVersionResource{Resource: "pods"}}}, core.UpdateActionImpl{ActionImpl: core.ActionImpl{Verb: "update", Resource: schema.GroupVersionResource{Resource: "pods"}, Subresource: "status"}}, }, ) }
// Test the case where the plugin's ready file exists, but the volume dir is not a // mountpoint, which is the state the system will be in after reboot. The dir // should be mounter and the secret data written to it. func TestPluginReboot(t *testing.T) { var ( testPodUID = types.UID("test_pod_uid3") testVolumeName = "test_volume_name" testNamespace = "test_secret_namespace" testName = "test_secret_name" volumeSpec = volumeSpec(testVolumeName, testName, 0644) secret = secret(testNamespace, testName) client = fake.NewSimpleClientset(&secret) pluginMgr = volume.VolumePluginMgr{} rootDir, host = newTestHost(t, client) ) defer os.RemoveAll(rootDir) pluginMgr.InitPlugins(ProbeVolumePlugins(), host) plugin, err := pluginMgr.FindPluginByName(secretPluginName) if err != nil { t.Errorf("Can't find the plugin by name") } pod := &v1.Pod{ObjectMeta: v1.ObjectMeta{Namespace: testNamespace, UID: testPodUID}} mounter, err := plugin.NewMounter(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{}) if err != nil { t.Errorf("Failed to make a new Mounter: %v", err) } if mounter == nil { t.Errorf("Got a nil Mounter") } podMetadataDir := fmt.Sprintf("%v/pods/test_pod_uid3/plugins/kubernetes.io~secret/test_volume_name", rootDir) util.SetReady(podMetadataDir) volumePath := mounter.GetPath() if !strings.HasSuffix(volumePath, fmt.Sprintf("pods/test_pod_uid3/volumes/kubernetes.io~secret/test_volume_name")) { t.Errorf("Got unexpected path: %s", volumePath) } err = mounter.SetUp(nil) if err != nil { t.Errorf("Failed to setup volume: %v", err) } if _, err := os.Stat(volumePath); err != nil { if os.IsNotExist(err) { t.Errorf("SetUp() failed, volume path not created: %s", volumePath) } else { t.Errorf("SetUp() failed: %v", err) } } doTestSecretDataInVolume(volumePath, secret, t) doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t) }
func TestUpdateNodeStatusError(t *testing.T) { testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) kubelet := testKubelet.kubelet // No matching node for the kubelet testKubelet.fakeKubeClient.ReactionChain = fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{}}).ReactionChain if err := kubelet.updateNodeStatus(); err == nil { t.Errorf("unexpected non error: %v", err) } if len(testKubelet.fakeKubeClient.Actions()) != nodeStatusUpdateRetry { t.Errorf("unexpected actions: %v", testKubelet.fakeKubeClient.Actions()) } }
func TestServiceEvaluatorMatchesResources(t *testing.T) { kubeClient := fake.NewSimpleClientset() evaluator := NewServiceEvaluator(kubeClient) expected := quota.ToSet([]api.ResourceName{ api.ResourceServices, api.ResourceServicesNodePorts, api.ResourceServicesLoadBalancers, }) actual := quota.ToSet(evaluator.MatchesResources()) if !expected.Equal(actual) { t.Errorf("expected: %v, actual: %v", expected, actual) } }
func TestReconcilePodStatus(t *testing.T) { testPod := getTestPod() client := fake.NewSimpleClientset(testPod) syncer := newTestManager(client) syncer.SetPodStatus(testPod, getRandomPodStatus()) // Call syncBatch directly to test reconcile syncer.syncBatch() // The apiStatusVersions should be set now podStatus, ok := syncer.GetPodStatus(testPod.UID) if !ok { t.Fatalf("Should find pod status for pod: %#v", testPod) } testPod.Status = podStatus // If the pod status is the same, a reconciliation is not needed, // syncBatch should do nothing syncer.podManager.UpdatePod(testPod) if syncer.needsReconcile(testPod.UID, podStatus) { t.Errorf("Pod status is the same, a reconciliation is not needed") } client.ClearActions() syncer.syncBatch() verifyActions(t, client, []core.Action{}) // If the pod status is the same, only the timestamp is in Rfc3339 format (lower precision without nanosecond), // a reconciliation is not needed, syncBatch should do nothing. // The StartTime should have been set in SetPodStatus(). // TODO(random-liu): Remove this later when api becomes consistent for timestamp. normalizedStartTime := testPod.Status.StartTime.Rfc3339Copy() testPod.Status.StartTime = &normalizedStartTime syncer.podManager.UpdatePod(testPod) if syncer.needsReconcile(testPod.UID, podStatus) { t.Errorf("Pod status only differs for timestamp format, a reconciliation is not needed") } client.ClearActions() syncer.syncBatch() verifyActions(t, client, []core.Action{}) // If the pod status is different, a reconciliation is needed, syncBatch should trigger an update testPod.Status = getRandomPodStatus() syncer.podManager.UpdatePod(testPod) if !syncer.needsReconcile(testPod.UID, podStatus) { t.Errorf("Pod status is different, a reconciliation is needed") } client.ClearActions() syncer.syncBatch() verifyActions(t, client, []core.Action{ core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: schema.GroupVersionResource{Resource: "pods"}}}, core.UpdateActionImpl{ActionImpl: core.ActionImpl{Verb: "update", Resource: schema.GroupVersionResource{Resource: "pods"}, Subresource: "status"}}, }) }
func TestSyncResourceQuotaNoChange(t *testing.T) { resourceQuota := v1.ResourceQuota{ ObjectMeta: v1.ObjectMeta{ Namespace: "default", Name: "rq", }, Spec: v1.ResourceQuotaSpec{ Hard: v1.ResourceList{ v1.ResourceCPU: resource.MustParse("4"), }, }, Status: v1.ResourceQuotaStatus{ Hard: v1.ResourceList{ v1.ResourceCPU: resource.MustParse("4"), }, Used: v1.ResourceList{ v1.ResourceCPU: resource.MustParse("0"), }, }, } kubeClient := fake.NewSimpleClientset(&v1.PodList{}, &resourceQuota) resourceQuotaControllerOptions := &ResourceQuotaControllerOptions{ KubeClient: kubeClient, ResyncPeriod: controller.NoResyncPeriodFunc, Registry: install.NewRegistry(kubeClient, nil), GroupKindsToReplenish: []schema.GroupKind{ api.Kind("Pod"), api.Kind("Service"), api.Kind("ReplicationController"), api.Kind("PersistentVolumeClaim"), }, ControllerFactory: NewReplenishmentControllerFactoryFromClient(kubeClient), ReplenishmentResyncPeriod: controller.NoResyncPeriodFunc, } quotaController := NewResourceQuotaController(resourceQuotaControllerOptions) err := quotaController.syncResourceQuota(resourceQuota) if err != nil { t.Fatalf("Unexpected error %v", err) } expectedActionSet := sets.NewString( strings.Join([]string{"list", "pods", ""}, "-"), ) actionSet := sets.NewString() for _, action := range kubeClient.Actions() { actionSet.Insert(strings.Join([]string{action.GetVerb(), action.GetResource().Resource, action.GetSubresource()}, "-")) } if !actionSet.HasAll(expectedActionSet.List()...) { t.Errorf("Expected actions:\n%v\n but got:\n%v\nDifference:\n%v", expectedActionSet, actionSet, expectedActionSet.Difference(actionSet)) } }
func TestSyncBatchChecksMismatchedUID(t *testing.T) { syncer := newTestManager(&fake.Clientset{}) pod := getTestPod() pod.UID = "first" syncer.podManager.AddPod(pod) differentPod := getTestPod() differentPod.UID = "second" syncer.podManager.AddPod(differentPod) syncer.kubeClient = fake.NewSimpleClientset(pod) syncer.SetPodStatus(differentPod, getRandomPodStatus()) syncer.testSyncBatch() verifyActions(t, syncer.kubeClient, []core.Action{ core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: schema.GroupVersionResource{Resource: "pods"}}}, }) }
func TestPersistentClaimReadOnlyFlag(t *testing.T) { pv := &v1.PersistentVolume{ ObjectMeta: v1.ObjectMeta{ Name: "pvA", }, Spec: v1.PersistentVolumeSpec{ PersistentVolumeSource: v1.PersistentVolumeSource{ Quobyte: &v1.QuobyteVolumeSource{Registry: "reg:7861", Volume: "vol", ReadOnly: false, User: "******", Group: "root"}, }, ClaimRef: &v1.ObjectReference{ Name: "claimA", }, }, } claim := &v1.PersistentVolumeClaim{ ObjectMeta: v1.ObjectMeta{ Name: "claimA", Namespace: "nsA", }, Spec: v1.PersistentVolumeClaimSpec{ VolumeName: "pvA", }, Status: v1.PersistentVolumeClaimStatus{ Phase: v1.ClaimBound, }, } tmpDir, err := utiltesting.MkTmpdir("quobyte_test") if err != nil { t.Fatalf("error creating temp dir: %v", err) } defer os.RemoveAll(tmpDir) client := fake.NewSimpleClientset(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, client, nil)) plug, _ := plugMgr.FindPluginByName(quobytePluginName) // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes spec := volume.NewSpecFromPersistentVolume(pv, true) pod := &v1.Pod{ObjectMeta: v1.ObjectMeta{UID: types.UID("poduid")}} mounter, _ := plug.NewMounter(spec, pod, volume.VolumeOptions{}) if !mounter.GetAttributes().ReadOnly { t.Errorf("Expected true for mounter.IsReadOnly") } }
func TestPersistentClaimReadOnlyFlag(t *testing.T) { pv := &v1.PersistentVolume{ ObjectMeta: v1.ObjectMeta{ Name: "pvA", }, Spec: v1.PersistentVolumeSpec{ PersistentVolumeSource: v1.PersistentVolumeSource{ AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{}, }, ClaimRef: &v1.ObjectReference{ Name: "claimA", }, }, } claim := &v1.PersistentVolumeClaim{ ObjectMeta: v1.ObjectMeta{ Name: "claimA", Namespace: "nsA", }, Spec: v1.PersistentVolumeClaimSpec{ VolumeName: "pvA", }, Status: v1.PersistentVolumeClaimStatus{ Phase: v1.ClaimBound, }, } clientset := fake.NewSimpleClientset(pv, claim) tmpDir, err := utiltesting.MkTmpdir("awsebsTest") if err != nil { t.Fatalf("can't make a temp dir: %v", err) } defer os.RemoveAll(tmpDir) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost(tmpDir, clientset, nil)) plug, _ := plugMgr.FindPluginByName(awsElasticBlockStorePluginName) // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes spec := volume.NewSpecFromPersistentVolume(pv, true) pod := &v1.Pod{ObjectMeta: v1.ObjectMeta{UID: types.UID("poduid")}} mounter, _ := plug.NewMounter(spec, pod, volume.VolumeOptions{}) if !mounter.GetAttributes().ReadOnly { t.Errorf("Expected true for mounter.IsReadOnly") } }
func TestWatchPods(t *testing.T) { testJob := newJob(2, 2) clientset := fake.NewSimpleClientset(testJob) fakeWatch := watch.NewFake() clientset.PrependWatchReactor("pods", core.DefaultWatchReactor(fakeWatch, nil)) manager, sharedInformerFactory := newJobControllerFromClient(clientset, controller.NoResyncPeriodFunc) manager.podStoreSynced = alwaysReady manager.jobStoreSynced = alwaysReady // Put one job and one pod into the store sharedInformerFactory.Jobs().Informer().GetIndexer().Add(testJob) received := make(chan struct{}) // The pod update sent through the fakeWatcher should figure out the managing job and // send it into the syncHandler. manager.syncHandler = func(key string) error { ns, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { t.Errorf("Error getting namespace/name from key %v: %v", key, err) } job, err := manager.jobLister.Jobs(ns).Get(name) if err != nil { t.Errorf("Expected to find job under key %v: %v", key, err) } if !api.Semantic.DeepDerivative(job, testJob) { t.Errorf("\nExpected %#v,\nbut got %#v", testJob, job) close(received) return nil } close(received) return nil } // Start only the pod watcher and the workqueue, send a watch event, // and make sure it hits the sync method for the right job. stopCh := make(chan struct{}) defer close(stopCh) go sharedInformerFactory.Pods().Informer().Run(stopCh) go wait.Until(manager.worker, 10*time.Millisecond, stopCh) pods := newPodList(1, v1.PodRunning, testJob) testPod := pods[0] testPod.Status.Phase = v1.PodFailed fakeWatch.Add(&testPod) t.Log("Waiting for pod to reach syncHandler") <-received }
func TestStaleUpdates(t *testing.T) { pod := getTestPod() client := fake.NewSimpleClientset(pod) m := newTestManager(client) status := v1.PodStatus{Message: "initial status"} m.SetPodStatus(pod, status) status.Message = "first version bump" m.SetPodStatus(pod, status) status.Message = "second version bump" m.SetPodStatus(pod, status) verifyUpdates(t, m, 3) t.Logf("First sync pushes latest status.") m.testSyncBatch() verifyActions(t, m.kubeClient, []core.Action{ core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: schema.GroupVersionResource{Resource: "pods"}}}, core.UpdateActionImpl{ActionImpl: core.ActionImpl{Verb: "update", Resource: schema.GroupVersionResource{Resource: "pods"}, Subresource: "status"}}, }) client.ClearActions() for i := 0; i < 2; i++ { t.Logf("Next 2 syncs should be ignored (%d).", i) m.testSyncBatch() verifyActions(t, m.kubeClient, []core.Action{}) } t.Log("Unchanged status should not send an update.") m.SetPodStatus(pod, status) verifyUpdates(t, m, 0) t.Log("... unless it's stale.") m.apiStatusVersions[pod.UID] = m.apiStatusVersions[pod.UID] - 1 m.SetPodStatus(pod, status) m.testSyncBatch() verifyActions(t, m.kubeClient, []core.Action{ core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: schema.GroupVersionResource{Resource: "pods"}}}, core.UpdateActionImpl{ActionImpl: core.ActionImpl{Verb: "update", Resource: schema.GroupVersionResource{Resource: "pods"}, Subresource: "status"}}, }) // Nothing stuck in the pipe. verifyUpdates(t, m, 0) }
func (f *fixture) run(deploymentName string) { f.client = fake.NewSimpleClientset(f.objects...) informers := informers.NewSharedInformerFactory(f.client, nil, controller.NoResyncPeriodFunc()) c := NewDeploymentController(informers.Deployments(), informers.ReplicaSets(), informers.Pods(), f.client) c.eventRecorder = &record.FakeRecorder{} c.dListerSynced = alwaysReady c.rsListerSynced = alwaysReady c.podListerSynced = alwaysReady for _, d := range f.dLister { c.dLister.Indexer.Add(d) } for _, rs := range f.rsLister { c.rsLister.Indexer.Add(rs) } for _, pod := range f.podLister { c.podLister.Indexer.Add(pod) } stopCh := make(chan struct{}) defer close(stopCh) informers.Start(stopCh) err := c.syncDeployment(deploymentName) if err != nil { f.t.Errorf("error syncing deployment: %v", err) } actions := filterInformerActions(f.client.Actions()) for i, action := range actions { if len(f.actions) < i+1 { f.t.Errorf("%d unexpected actions: %+v", len(actions)-len(f.actions), actions[i:]) break } expectedAction := f.actions[i] if !expectedAction.Matches(action.GetVerb(), action.GetResource().Resource) { f.t.Errorf("Expected\n\t%#v\ngot\n\t%#v", expectedAction, action) continue } } if len(f.actions) > len(actions) { f.t.Errorf("%d additional expected actions:%+v", len(f.actions)-len(actions), f.actions[len(actions):]) } }
func TestGetMountedVolumesForPodAndGetVolumesInUse(t *testing.T) { tmpDir, err := utiltesting.MkTmpdir("volumeManagerTest") if err != nil { t.Fatalf("can't make a temp dir: %v", err) } defer os.RemoveAll(tmpDir) podManager := kubepod.NewBasicPodManager(podtest.NewFakeMirrorClient()) node, pod, pv, claim := createObjects() kubeClient := fake.NewSimpleClientset(node, pod, pv, claim) manager, err := newTestVolumeManager(tmpDir, podManager, kubeClient) if err != nil { t.Fatalf("Failed to initialize volume manager: %v", err) } stopCh := runVolumeManager(manager) defer close(stopCh) podManager.SetPods([]*v1.Pod{pod}) // Fake node status update go simulateVolumeInUseUpdate( v1.UniqueVolumeName(node.Status.VolumesAttached[0].Name), stopCh, manager) err = manager.WaitForAttachAndMount(pod) if err != nil { t.Errorf("Expected success: %v", err) } expectedMounted := pod.Spec.Volumes[0].Name actualMounted := manager.GetMountedVolumesForPod(types.UniquePodName(pod.ObjectMeta.UID)) if _, ok := actualMounted[expectedMounted]; !ok || (len(actualMounted) != 1) { t.Errorf("Expected %v to be mounted to pod but got %v", expectedMounted, actualMounted) } expectedInUse := []v1.UniqueVolumeName{v1.UniqueVolumeName(node.Status.VolumesAttached[0].Name)} actualInUse := manager.GetVolumesInUse() if !reflect.DeepEqual(expectedInUse, actualInUse) { t.Errorf("Expected %v to be in use but got %v", expectedInUse, actualInUse) } }
func TestWatchJobs(t *testing.T) { clientset := fake.NewSimpleClientset() fakeWatch := watch.NewFake() clientset.PrependWatchReactor("jobs", core.DefaultWatchReactor(fakeWatch, nil)) manager, sharedInformerFactory := newJobControllerFromClient(clientset, controller.NoResyncPeriodFunc) manager.podStoreSynced = alwaysReady manager.jobStoreSynced = alwaysReady var testJob batch.Job received := make(chan struct{}) // The update sent through the fakeWatcher should make its way into the workqueue, // and eventually into the syncHandler. manager.syncHandler = func(key string) error { defer close(received) ns, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { t.Errorf("Error getting namespace/name from key %v: %v", key, err) } job, err := manager.jobLister.Jobs(ns).Get(name) if err != nil || job == nil { t.Errorf("Expected to find job under key %v: %v", key, err) return nil } if !api.Semantic.DeepDerivative(*job, testJob) { t.Errorf("Expected %#v, but got %#v", testJob, *job) } return nil } // Start only the job watcher and the workqueue, send a watch event, // and make sure it hits the sync method. stopCh := make(chan struct{}) defer close(stopCh) sharedInformerFactory.Start(stopCh) go manager.Run(1, stopCh) // We're sending new job to see if it reaches syncHandler. testJob.Namespace = "bar" testJob.Name = "foo" fakeWatch.Add(&testJob) t.Log("Waiting for job to reach syncHandler") <-received }
func TestPersistentClaimReadOnlyFlag(t *testing.T) { pv := &v1.PersistentVolume{ ObjectMeta: v1.ObjectMeta{ Name: "pvA", }, Spec: v1.PersistentVolumeSpec{ PersistentVolumeSource: v1.PersistentVolumeSource{ AzureFile: &v1.AzureFileVolumeSource{}, }, ClaimRef: &v1.ObjectReference{ Name: "claimA", }, }, } claim := &v1.PersistentVolumeClaim{ ObjectMeta: v1.ObjectMeta{ Name: "claimA", Namespace: "nsA", }, Spec: v1.PersistentVolumeClaimSpec{ VolumeName: "pvA", }, Status: v1.PersistentVolumeClaimStatus{ Phase: v1.ClaimBound, }, } client := fake.NewSimpleClientset(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volumetest.NewFakeVolumeHost("/tmp/fake", client, nil)) plug, _ := plugMgr.FindPluginByName(azureFilePluginName) // readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes spec := volume.NewSpecFromPersistentVolume(pv, true) pod := &v1.Pod{ObjectMeta: v1.ObjectMeta{UID: types.UID("poduid")}} mounter, _ := plug.NewMounter(spec, pod, volume.VolumeOptions{}) if !mounter.GetAttributes().ReadOnly { t.Errorf("Expected true for mounter.IsReadOnly") } }
func TestPersistentVolumeClaimEvaluatorUsage(t *testing.T) { validClaim := testVolumeClaim("foo", "ns", api.PersistentVolumeClaimSpec{ Selector: &metav1.LabelSelector{ MatchExpressions: []metav1.LabelSelectorRequirement{ { Key: "key2", Operator: "Exists", }, }, }, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, }, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("10Gi"), }, }, }) kubeClient := fake.NewSimpleClientset() evaluator := NewPersistentVolumeClaimEvaluator(kubeClient, nil) testCases := map[string]struct { pvc *api.PersistentVolumeClaim usage api.ResourceList }{ "pvc-usage": { pvc: validClaim, usage: api.ResourceList{ api.ResourceRequestsStorage: resource.MustParse("10Gi"), api.ResourcePersistentVolumeClaims: resource.MustParse("1"), }, }, } for testName, testCase := range testCases { actual := evaluator.Usage(testCase.pvc) if !quota.Equals(testCase.usage, actual) { t.Errorf("%s expected: %v, actual: %v", testName, testCase.usage, actual) } } }
func TestDescribeDeployment(t *testing.T) { fake := fake.NewSimpleClientset() versionedFake := versionedfake.NewSimpleClientset(&v1beta1.Deployment{ ObjectMeta: v1.ObjectMeta{ Name: "bar", Namespace: "foo", }, Spec: v1beta1.DeploymentSpec{ Selector: &unversioned.LabelSelector{}, Template: v1.PodTemplateSpec{}, }, }) d := DeploymentDescriber{fake, versionedFake} out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("unexpected error: %v", err) } if !strings.Contains(out, "bar") || !strings.Contains(out, "foo") { t.Errorf("unexpected out: %s", out) } }
func TestDeletePods(t *testing.T) { pod := getTestPod() // Set the deletion timestamp. pod.DeletionTimestamp = new(metav1.Time) client := fake.NewSimpleClientset(pod) m := newTestManager(client) m.podManager.AddPod(pod) status := getRandomPodStatus() now := metav1.Now() status.StartTime = &now m.SetPodStatus(pod, status) m.testSyncBatch() // Expect to see an delete action. verifyActions(t, m.kubeClient, []core.Action{ core.GetActionImpl{ActionImpl: core.ActionImpl{Verb: "get", Resource: schema.GroupVersionResource{Resource: "pods"}}}, core.UpdateActionImpl{ActionImpl: core.ActionImpl{Verb: "update", Resource: schema.GroupVersionResource{Resource: "pods"}, Subresource: "status"}}, core.DeleteActionImpl{ActionImpl: core.ActionImpl{Verb: "delete", Resource: schema.GroupVersionResource{Resource: "pods"}}}, }) }
// Verifies that querying KubeDNS for a federation service returns the // DNS hostname if no endpoint exists and returns the local cluster IP // if endpoints exist. func TestFederationService(t *testing.T) { kd := newKubeDNS() kd.config.Federations = map[string]string{ "myfederation": "example.com", } kd.kubeClient = fake.NewSimpleClientset(newNodes()) // Verify that querying for federation service returns the federation domain name. verifyRecord("testservice.default.myfederation.svc.cluster.local.", federatedServiceFQDN, t, kd) // Add a local service without any endpoint. s := newService(testNamespace, testService, "1.2.3.4", "", 80) assert.NoError(t, kd.servicesStore.Add(s)) kd.newService(s) // Verify that querying for federation service still returns the federation domain name. verifyRecord(getFederationServiceFQDN(kd, s, "myfederation"), federatedServiceFQDN, t, kd) // Now add an endpoint. endpoints := newEndpoints(s, newSubsetWithOnePort("", 80, "10.0.0.1")) assert.NoError(t, kd.endpointsStore.Add(endpoints)) kd.updateService(s, s) // Verify that querying for federation service returns the local service domain name this time. verifyRecord(getFederationServiceFQDN(kd, s, "myfederation"), "testservice.default.svc.cluster.local.", t, kd) // Remove the endpoint. endpoints.Subsets = []v1.EndpointSubset{} kd.handleEndpointAdd(endpoints) kd.updateService(s, s) // Verify that querying for federation service returns the federation domain name again. verifyRecord(getFederationServiceFQDN(kd, s, "myfederation"), federatedServiceFQDN, t, kd) }
func TestGCTerminated(t *testing.T) { type nameToPhase struct { name string phase v1.PodPhase } testCases := []struct { pods []nameToPhase threshold int deletedPodNames sets.String }{ { pods: []nameToPhase{ {name: "a", phase: v1.PodFailed}, {name: "b", phase: v1.PodSucceeded}, }, threshold: 0, // threshold = 0 disables terminated pod deletion deletedPodNames: sets.NewString(), }, { pods: []nameToPhase{ {name: "a", phase: v1.PodFailed}, {name: "b", phase: v1.PodSucceeded}, {name: "c", phase: v1.PodFailed}, }, threshold: 1, deletedPodNames: sets.NewString("a", "b"), }, { pods: []nameToPhase{ {name: "a", phase: v1.PodRunning}, {name: "b", phase: v1.PodSucceeded}, {name: "c", phase: v1.PodFailed}, }, threshold: 1, deletedPodNames: sets.NewString("b"), }, { pods: []nameToPhase{ {name: "a", phase: v1.PodFailed}, {name: "b", phase: v1.PodSucceeded}, }, threshold: 1, deletedPodNames: sets.NewString("a"), }, { pods: []nameToPhase{ {name: "a", phase: v1.PodFailed}, {name: "b", phase: v1.PodSucceeded}, }, threshold: 5, deletedPodNames: sets.NewString(), }, } for i, test := range testCases { client := fake.NewSimpleClientset(&v1.NodeList{Items: []v1.Node{*testutil.NewNode("node")}}) gcc := NewFromClient(client, test.threshold) deletedPodNames := make([]string, 0) var lock sync.Mutex gcc.deletePod = func(_, name string) error { lock.Lock() defer lock.Unlock() deletedPodNames = append(deletedPodNames, name) return nil } creationTime := time.Unix(0, 0) for _, pod := range test.pods { creationTime = creationTime.Add(1 * time.Hour) gcc.podStore.Indexer.Add(&v1.Pod{ ObjectMeta: v1.ObjectMeta{Name: pod.name, CreationTimestamp: metav1.Time{Time: creationTime}}, Status: v1.PodStatus{Phase: pod.phase}, Spec: v1.PodSpec{NodeName: "node"}, }) } gcc.podController = &FakeController{} gcc.gc() pass := true for _, pod := range deletedPodNames { if !test.deletedPodNames.Has(pod) { pass = false } } if len(deletedPodNames) != len(test.deletedPodNames) { pass = false } if !pass { t.Errorf("[%v]pod's deleted expected and actual did not match.\n\texpected: %v\n\tactual: %v", i, test.deletedPodNames, deletedPodNames) } } }
func TestGCUnscheduledTerminating(t *testing.T) { type nameToPhase struct { name string phase v1.PodPhase deletionTimeStamp *metav1.Time nodeName string } testCases := []struct { name string pods []nameToPhase deletedPodNames sets.String }{ { name: "Unscheduled pod in any phase must be deleted", pods: []nameToPhase{ {name: "a", phase: v1.PodFailed, deletionTimeStamp: &metav1.Time{}, nodeName: ""}, {name: "b", phase: v1.PodSucceeded, deletionTimeStamp: &metav1.Time{}, nodeName: ""}, {name: "c", phase: v1.PodRunning, deletionTimeStamp: &metav1.Time{}, nodeName: ""}, }, deletedPodNames: sets.NewString("a", "b", "c"), }, { name: "Scheduled pod in any phase must not be deleted", pods: []nameToPhase{ {name: "a", phase: v1.PodFailed, deletionTimeStamp: nil, nodeName: ""}, {name: "b", phase: v1.PodSucceeded, deletionTimeStamp: nil, nodeName: "node"}, {name: "c", phase: v1.PodRunning, deletionTimeStamp: &metav1.Time{}, nodeName: "node"}, }, deletedPodNames: sets.NewString(), }, } for i, test := range testCases { client := fake.NewSimpleClientset() gcc := NewFromClient(client, -1) deletedPodNames := make([]string, 0) var lock sync.Mutex gcc.deletePod = func(_, name string) error { lock.Lock() defer lock.Unlock() deletedPodNames = append(deletedPodNames, name) return nil } creationTime := time.Unix(0, 0) for _, pod := range test.pods { creationTime = creationTime.Add(1 * time.Hour) gcc.podStore.Indexer.Add(&v1.Pod{ ObjectMeta: v1.ObjectMeta{Name: pod.name, CreationTimestamp: metav1.Time{Time: creationTime}, DeletionTimestamp: pod.deletionTimeStamp}, Status: v1.PodStatus{Phase: pod.phase}, Spec: v1.PodSpec{NodeName: pod.nodeName}, }) } gcc.podController = &FakeController{} pods, err := gcc.podStore.List(labels.Everything()) if err != nil { t.Errorf("Error while listing all Pods: %v", err) return } gcc.gcUnscheduledTerminating(pods) pass := true for _, pod := range deletedPodNames { if !test.deletedPodNames.Has(pod) { pass = false } } if len(deletedPodNames) != len(test.deletedPodNames) { pass = false } if !pass { t.Errorf("[%v]pod's deleted expected and actual did not match.\n\texpected: %v\n\tactual: %v, test: %v", i, test.deletedPodNames, deletedPodNames, test.name) } } }
func TestDescribeEvents(t *testing.T) { events := &api.EventList{ Items: []api.Event{ { ObjectMeta: api.ObjectMeta{ Namespace: "foo", }, Source: api.EventSource{Component: "kubelet"}, Message: "Item 1", FirstTimestamp: unversioned.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), LastTimestamp: unversioned.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), Count: 1, Type: api.EventTypeNormal, }, }, } m := map[string]Describer{ "DaemonSetDescriber": &DaemonSetDescriber{ fake.NewSimpleClientset(&extensions.DaemonSet{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "foo", }, }, events), }, "DeploymentDescriber": &DeploymentDescriber{ fake.NewSimpleClientset(events), versionedfake.NewSimpleClientset(&v1beta1.Deployment{ ObjectMeta: v1.ObjectMeta{ Name: "bar", Namespace: "foo", }, Spec: v1beta1.DeploymentSpec{ Selector: &unversioned.LabelSelector{}, }, }), }, "EndpointsDescriber": &EndpointsDescriber{ fake.NewSimpleClientset(&api.Endpoints{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "foo", }, }, events), }, // TODO(jchaloup): add tests for: // - HorizontalPodAutoscalerDescriber // - IngressDescriber // - JobDescriber "NodeDescriber": &NodeDescriber{ fake.NewSimpleClientset(&api.Node{ ObjectMeta: api.ObjectMeta{ Name: "bar", SelfLink: "url/url/url", }, }, events), }, "PersistentVolumeDescriber": &PersistentVolumeDescriber{ fake.NewSimpleClientset(&api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "bar", SelfLink: "url/url/url", }, }, events), }, "PodDescriber": &PodDescriber{ fake.NewSimpleClientset(&api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "foo", SelfLink: "url/url/url", }, }, events), }, "ReplicaSetDescriber": &ReplicaSetDescriber{ fake.NewSimpleClientset(&extensions.ReplicaSet{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "foo", }, }, events), }, "ReplicationControllerDescriber": &ReplicationControllerDescriber{ fake.NewSimpleClientset(&api.ReplicationController{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "foo", }, }, events), }, "Service": &ServiceDescriber{ fake.NewSimpleClientset(&api.Service{ ObjectMeta: api.ObjectMeta{ Name: "bar", Namespace: "foo", }, }, events), }, "StorageClass": &StorageClassDescriber{ fake.NewSimpleClientset(&storage.StorageClass{ ObjectMeta: api.ObjectMeta{ Name: "bar", }, }, events), }, } for name, d := range m { out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("unexpected error for %q: %v", name, err) } if !strings.Contains(out, "bar") { t.Errorf("unexpected out for %q: %s", name, out) } if !strings.Contains(out, "Events:") { t.Errorf("events not found for %q when ShowEvents=true: %s", name, out) } out, err = d.Describe("foo", "bar", DescriberSettings{ShowEvents: false}) if err != nil { t.Errorf("unexpected error for %q: %s", name, err) } if !strings.Contains(out, "bar") { t.Errorf("unexpected out for %q: %s", name, out) } if strings.Contains(out, "Events:") { t.Errorf("events found for %q when ShowEvents=false: %s", name, out) } } }
func TestServiceAccountCreation(t *testing.T) { ns := v1.NamespaceDefault defaultName := "default" managedName := "managed" activeNS := &v1.Namespace{ ObjectMeta: v1.ObjectMeta{Name: ns}, Status: v1.NamespaceStatus{ Phase: v1.NamespaceActive, }, } terminatingNS := &v1.Namespace{ ObjectMeta: v1.ObjectMeta{Name: ns}, Status: v1.NamespaceStatus{ Phase: v1.NamespaceTerminating, }, } defaultServiceAccount := &v1.ServiceAccount{ ObjectMeta: v1.ObjectMeta{ Name: defaultName, Namespace: ns, ResourceVersion: "1", }, } managedServiceAccount := &v1.ServiceAccount{ ObjectMeta: v1.ObjectMeta{ Name: managedName, Namespace: ns, ResourceVersion: "1", }, } unmanagedServiceAccount := &v1.ServiceAccount{ ObjectMeta: v1.ObjectMeta{ Name: "other-unmanaged", Namespace: ns, ResourceVersion: "1", }, } testcases := map[string]struct { ExistingNamespace *v1.Namespace ExistingServiceAccounts []*v1.ServiceAccount AddedNamespace *v1.Namespace UpdatedNamespace *v1.Namespace DeletedServiceAccount *v1.ServiceAccount ExpectCreatedServiceAccounts []string }{ "new active namespace missing serviceaccounts": { ExistingServiceAccounts: []*v1.ServiceAccount{}, AddedNamespace: activeNS, ExpectCreatedServiceAccounts: sets.NewString(defaultName, managedName).List(), }, "new active namespace missing serviceaccount": { ExistingServiceAccounts: []*v1.ServiceAccount{managedServiceAccount}, AddedNamespace: activeNS, ExpectCreatedServiceAccounts: []string{defaultName}, }, "new active namespace with serviceaccounts": { ExistingServiceAccounts: []*v1.ServiceAccount{defaultServiceAccount, managedServiceAccount}, AddedNamespace: activeNS, ExpectCreatedServiceAccounts: []string{}, }, "new terminating namespace": { ExistingServiceAccounts: []*v1.ServiceAccount{}, AddedNamespace: terminatingNS, ExpectCreatedServiceAccounts: []string{}, }, "updated active namespace missing serviceaccounts": { ExistingServiceAccounts: []*v1.ServiceAccount{}, UpdatedNamespace: activeNS, ExpectCreatedServiceAccounts: sets.NewString(defaultName, managedName).List(), }, "updated active namespace missing serviceaccount": { ExistingServiceAccounts: []*v1.ServiceAccount{defaultServiceAccount}, UpdatedNamespace: activeNS, ExpectCreatedServiceAccounts: []string{managedName}, }, "updated active namespace with serviceaccounts": { ExistingServiceAccounts: []*v1.ServiceAccount{defaultServiceAccount, managedServiceAccount}, UpdatedNamespace: activeNS, ExpectCreatedServiceAccounts: []string{}, }, "updated terminating namespace": { ExistingServiceAccounts: []*v1.ServiceAccount{}, UpdatedNamespace: terminatingNS, ExpectCreatedServiceAccounts: []string{}, }, "deleted serviceaccount without namespace": { DeletedServiceAccount: defaultServiceAccount, ExpectCreatedServiceAccounts: []string{}, }, "deleted serviceaccount with active namespace": { ExistingServiceAccounts: []*v1.ServiceAccount{managedServiceAccount}, ExistingNamespace: activeNS, DeletedServiceAccount: defaultServiceAccount, ExpectCreatedServiceAccounts: []string{defaultName}, }, "deleted serviceaccount with terminating namespace": { ExistingNamespace: terminatingNS, DeletedServiceAccount: defaultServiceAccount, ExpectCreatedServiceAccounts: []string{}, }, "deleted unmanaged serviceaccount with active namespace": { ExistingServiceAccounts: []*v1.ServiceAccount{defaultServiceAccount, managedServiceAccount}, ExistingNamespace: activeNS, DeletedServiceAccount: unmanagedServiceAccount, ExpectCreatedServiceAccounts: []string{}, }, "deleted unmanaged serviceaccount with terminating namespace": { ExistingNamespace: terminatingNS, DeletedServiceAccount: unmanagedServiceAccount, ExpectCreatedServiceAccounts: []string{}, }, } for k, tc := range testcases { client := fake.NewSimpleClientset(defaultServiceAccount, managedServiceAccount) informers := informers.NewSharedInformerFactory(fake.NewSimpleClientset(), nil, controller.NoResyncPeriodFunc()) options := DefaultServiceAccountsControllerOptions() options.ServiceAccounts = []v1.ServiceAccount{ {ObjectMeta: v1.ObjectMeta{Name: defaultName}}, {ObjectMeta: v1.ObjectMeta{Name: managedName}}, } controller := NewServiceAccountsController(informers.ServiceAccounts(), informers.Namespaces(), client, options) controller.saLister = &cache.StoreToServiceAccountLister{Indexer: cache.NewIndexer(cache.DeletionHandlingMetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})} controller.nsLister = &cache.IndexerToNamespaceLister{Indexer: cache.NewIndexer(cache.DeletionHandlingMetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})} controller.saSynced = alwaysReady controller.nsSynced = alwaysReady syncCalls := make(chan struct{}) controller.syncHandler = func(key string) error { err := controller.syncNamespace(key) if err != nil { t.Logf("%s: %v", k, err) } syncCalls <- struct{}{} return err } stopCh := make(chan struct{}) defer close(stopCh) go controller.Run(1, stopCh) if tc.ExistingNamespace != nil { controller.nsLister.Add(tc.ExistingNamespace) } for _, s := range tc.ExistingServiceAccounts { controller.saLister.Indexer.Add(s) } if tc.AddedNamespace != nil { controller.nsLister.Add(tc.AddedNamespace) controller.namespaceAdded(tc.AddedNamespace) } if tc.UpdatedNamespace != nil { controller.nsLister.Add(tc.UpdatedNamespace) controller.namespaceUpdated(nil, tc.UpdatedNamespace) } if tc.DeletedServiceAccount != nil { controller.serviceAccountDeleted(tc.DeletedServiceAccount) } // wait to be called select { case <-syncCalls: case <-time.After(10 * time.Second): t.Errorf("%s: took too long", k) } actions := client.Actions() if len(tc.ExpectCreatedServiceAccounts) != len(actions) { t.Errorf("%s: Expected to create accounts %#v. Actual actions were: %#v", k, tc.ExpectCreatedServiceAccounts, actions) continue } for i, expectedName := range tc.ExpectCreatedServiceAccounts { action := actions[i] if !action.Matches("create", "serviceaccounts") { t.Errorf("%s: Unexpected action %s", k, action) break } createdAccount := action.(core.CreateAction).GetObject().(*v1.ServiceAccount) if createdAccount.Name != expectedName { t.Errorf("%s: Expected %s to be created, got %s", k, expectedName, createdAccount.Name) } } } }