func TestDecoratedWatcher(t *testing.T) { w := watch.NewFake() decorator := func(obj runtime.Object) error { pod := obj.(*api.Pod) pod.Annotations = map[string]string{"decorated": "true"} return nil } dw := newDecoratedWatcher(w, decorator) defer dw.Stop() go w.Add(&api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}) select { case e := <-dw.ResultChan(): pod, ok := e.Object.(*api.Pod) if !ok { t.Errorf("Should received object of type *api.Pod, get type (%T)", e.Object) return } if pod.Annotations["decorated"] != "true" { t.Errorf("pod.Annotations[\"decorated\"], want=%s, get=%s", "true", pod.Labels["decorated"]) } case <-time.After(wait.ForeverTestTimeout): t.Errorf("timeout after %v", wait.ForeverTestTimeout) } }
func TestRunUntil(t *testing.T) { stopCh := make(chan struct{}) store := NewStore(MetaNamespaceKeyFunc) r := NewReflector(&testLW{}, &v1.Pod{}, store, 0) fw := watch.NewFake() r.listerWatcher = &testLW{ WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { return fw, nil }, ListFunc: func(options v1.ListOptions) (runtime.Object, error) { return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil }, } r.RunUntil(stopCh) // Synchronously add a dummy pod into the watch channel so we // know the RunUntil go routine is in the watch handler. fw.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}) close(stopCh) select { case _, ok := <-fw.ResultChan(): if ok { t.Errorf("Watch channel left open after stopping the watch") } case <-time.After(wait.ForeverTestTimeout): t.Errorf("the cancellation is at least %s late", wait.ForeverTestTimeout.String()) break } }
func TestReflectorResync(t *testing.T) { iteration := 0 stopCh := make(chan struct{}) rerr := errors.New("expected resync reached") s := &FakeCustomStore{ ResyncFunc: func() error { iteration++ if iteration == 2 { return rerr } return nil }, } lw := &testLW{ WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { fw := watch.NewFake() return fw, nil }, ListFunc: func(options v1.ListOptions) (runtime.Object, error) { return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "0"}}, nil }, } resyncPeriod := 1 * time.Millisecond r := NewReflector(lw, &v1.Pod{}, s, resyncPeriod) if err := r.ListAndWatch(stopCh); err != nil { // error from Resync is not propaged up to here. t.Errorf("expected error %v", err) } if iteration != 2 { t.Errorf("exactly 2 iterations were expected, got: %v", iteration) } }
func CreateTestClient() *fake.Clientset { fakeClient := &fake.Clientset{} fakeClient.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) { obj := &v1.PodList{} podNamePrefix := "mypod" namespace := "mynamespace" for i := 0; i < 5; i++ { podName := fmt.Sprintf("%s-%d", podNamePrefix, i) pod := v1.Pod{ Status: v1.PodStatus{ Phase: v1.PodRunning, }, ObjectMeta: metav1.ObjectMeta{ Name: podName, Namespace: namespace, Labels: map[string]string{ "name": podName, }, }, Spec: v1.PodSpec{ Containers: []v1.Container{ { Name: "containerName", Image: "containerImage", VolumeMounts: []v1.VolumeMount{ { Name: "volumeMountName", ReadOnly: false, MountPath: "/mnt", }, }, }, }, Volumes: []v1.Volume{ { Name: "volumeName", VolumeSource: v1.VolumeSource{ GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{ PDName: "pdName", FSType: "ext4", ReadOnly: false, }, }, }, }, }, } obj.Items = append(obj.Items, pod) } return true, obj, nil }) fakeWatch := watch.NewFake() fakeClient.AddWatchReactor("*", core.DefaultWatchReactor(fakeWatch, nil)) return fakeClient }
func TestReflectorListAndWatch(t *testing.T) { createdFakes := make(chan *watch.FakeWatcher) // The ListFunc says that it's at revision 1. Therefore, we expect our WatchFunc // to get called at the beginning of the watch with 1, and again with 3 when we // inject an error. expectedRVs := []string{"1", "3"} lw := &testLW{ WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { rv := options.ResourceVersion fw := watch.NewFake() if e, a := expectedRVs[0], rv; e != a { t.Errorf("Expected rv %v, but got %v", e, a) } expectedRVs = expectedRVs[1:] // channel is not buffered because the for loop below needs to block. But // we don't want to block here, so report the new fake via a go routine. go func() { createdFakes <- fw }() return fw, nil }, ListFunc: func(options v1.ListOptions) (runtime.Object, error) { return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil }, } s := NewFIFO(MetaNamespaceKeyFunc) r := NewReflector(lw, &v1.Pod{}, s, 0) go r.ListAndWatch(wait.NeverStop) ids := []string{"foo", "bar", "baz", "qux", "zoo"} var fw *watch.FakeWatcher for i, id := range ids { if fw == nil { fw = <-createdFakes } sendingRV := strconv.FormatUint(uint64(i+2), 10) fw.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: id, ResourceVersion: sendingRV}}) if sendingRV == "3" { // Inject a failure. fw.Stop() fw = nil } } // Verify we received the right ids with the right resource versions. for i, id := range ids { pod := Pop(s).(*v1.Pod) if e, a := id, pod.Name; e != a { t.Errorf("%v: Expected %v, got %v", i, e, a) } if e, a := strconv.FormatUint(uint64(i+2), 10), pod.ResourceVersion; e != a { t.Errorf("%v: Expected %v, got %v", i, e, a) } } if len(expectedRVs) != 0 { t.Error("called watchStarter an unexpected number of times") } }
func TestReflectorWatchHandler(t *testing.T) { s := NewStore(MetaNamespaceKeyFunc) g := NewReflector(&testLW{}, &v1.Pod{}, s, 0) fw := watch.NewFake() s.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}) s.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}) go func() { fw.Add(&v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "rejected"}}) fw.Delete(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}) fw.Modify(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "55"}}) fw.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "baz", ResourceVersion: "32"}}) fw.Stop() }() var resumeRV string err := g.watchHandler(fw, &resumeRV, nevererrc, wait.NeverStop) if err != nil { t.Errorf("unexpected error %v", err) } mkPod := func(id string, rv string) *v1.Pod { return &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: id, ResourceVersion: rv}} } table := []struct { Pod *v1.Pod exists bool }{ {mkPod("foo", ""), false}, {mkPod("rejected", ""), false}, {mkPod("bar", "55"), true}, {mkPod("baz", "32"), true}, } for _, item := range table { obj, exists, _ := s.Get(item.Pod) if e, a := item.exists, exists; e != a { t.Errorf("%v: expected %v, got %v", item.Pod, e, a) } if !exists { continue } if e, a := item.Pod.ResourceVersion, obj.(*v1.Pod).ResourceVersion; e != a { t.Errorf("%v: expected %v, got %v", item.Pod, e, a) } } // RV should send the last version we see. if e, a := "32", resumeRV; e != a { t.Errorf("expected %v, got %v", e, a) } // last sync resource version should be the last version synced with store if e, a := "32", g.LastSyncResourceVersion(); e != a { t.Errorf("expected %v, got %v", e, a) } }
func TestReflectorStopWatch(t *testing.T) { s := NewStore(MetaNamespaceKeyFunc) g := NewReflector(&testLW{}, &v1.Pod{}, s, 0) fw := watch.NewFake() var resumeRV string stopWatch := make(chan struct{}, 1) stopWatch <- struct{}{} err := g.watchHandler(fw, &resumeRV, nevererrc, stopWatch) if err != errorStopRequested { t.Errorf("expected stop error, got %q", err) } }
func TestReflectorWatchHandlerError(t *testing.T) { s := NewStore(MetaNamespaceKeyFunc) g := NewReflector(&testLW{}, &v1.Pod{}, s, 0) fw := watch.NewFake() go func() { fw.Stop() }() var resumeRV string err := g.watchHandler(fw, &resumeRV, nevererrc, wait.NeverStop) if err == nil { t.Errorf("unexpected non-error") } }
func TestMutationDetector(t *testing.T) { fakeWatch := watch.NewFake() lw := &testLW{ WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { return fakeWatch, nil }, ListFunc: func(options v1.ListOptions) (runtime.Object, error) { return &v1.PodList{}, nil }, } pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "anything", Labels: map[string]string{"check": "foo"}, }, } stopCh := make(chan struct{}) defer close(stopCh) addReceived := make(chan bool) mutationFound := make(chan bool) informer := NewSharedInformer(lw, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer) informer.cacheMutationDetector = &defaultCacheMutationDetector{ name: "name", period: 1 * time.Second, failureFunc: func(message string) { mutationFound <- true }, } informer.AddEventHandler( ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { addReceived <- true }, }, ) go informer.Run(stopCh) fakeWatch.Add(pod) select { case <-addReceived: } pod.Labels["change"] = "true" select { case <-mutationFound: } }
// NewSimpleClientset returns a clientset that will respond with the provided objects. // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, // without applying any validations and/or defaults. It shouldn't be considered a replacement // for a real clientset and is mostly useful in simple unit tests. func NewSimpleClientset(objects ...runtime.Object) *Clientset { o := core.NewObjectTracker(api.Scheme, api.Codecs.UniversalDecoder()) for _, obj := range objects { if err := o.Add(obj); err != nil { panic(err) } } fakePtr := core.Fake{} fakePtr.AddReactor("*", "*", core.ObjectReaction(o, api.Registry.RESTMapper())) fakePtr.AddWatchReactor("*", core.DefaultWatchReactor(watch.NewFake(), nil)) return &Clientset{fakePtr} }
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 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 TestDecoratedWatcherError(t *testing.T) { w := watch.NewFake() expErr := fmt.Errorf("expected error") decorator := func(obj runtime.Object) error { return expErr } dw := newDecoratedWatcher(w, decorator) defer dw.Stop() go w.Add(&api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}) select { case e := <-dw.ResultChan(): if e.Type != watch.Error { t.Errorf("event type want=%v, get=%v", watch.Error, e.Type) } case <-time.After(wait.ForeverTestTimeout): t.Errorf("timeout after %v", wait.ForeverTestTimeout) } }
func TestNewSourceApiserver_TwoNamespacesSameName(t *testing.T) { pod1 := v1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "p", Namespace: "one"}, Spec: v1.PodSpec{Containers: []v1.Container{{Image: "image/one"}}}} pod2 := v1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "p", Namespace: "two"}, Spec: v1.PodSpec{Containers: []v1.Container{{Image: "image/blah"}}}} // Setup fake api client. fakeWatch := watch.NewFake() lw := fakePodLW{ listResp: &v1.PodList{Items: []v1.Pod{pod1, pod2}}, watchResp: fakeWatch, } ch := make(chan interface{}) newSourceApiserverFromLW(lw, ch) got, ok := <-ch if !ok { t.Errorf("Unable to read from channel when expected") } update := got.(kubetypes.PodUpdate) // Make sure that we get both pods. Catches bug #2294. if !(len(update.Pods) == 2) { t.Errorf("Expected %d, Got %d", 2, len(update.Pods)) } // Delete pod1 fakeWatch.Delete(&pod1) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } update = got.(kubetypes.PodUpdate) if !(len(update.Pods) == 1) { t.Errorf("Expected %d, Got %d", 1, len(update.Pods)) } }
func TestNewSourceApiserverInitialEmptySendsEmptyPodUpdate(t *testing.T) { // Setup fake api client. fakeWatch := watch.NewFake() lw := fakePodLW{ listResp: &v1.PodList{Items: []v1.Pod{}}, watchResp: fakeWatch, } ch := make(chan interface{}) newSourceApiserverFromLW(lw, ch) got, ok := <-ch if !ok { t.Errorf("Unable to read from channel when expected") } update := got.(kubetypes.PodUpdate) expected := CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource) if !api.Semantic.DeepEqual(expected, update) { t.Errorf("Expected %#v; Got %#v", expected, update) } }
func TestListWatchUntil(t *testing.T) { fw := watch.NewFake() go func() { var obj *v1.Pod fw.Modify(obj) }() listwatch := lw{ list: &v1.PodList{Items: []v1.Pod{{}}}, watch: fw, } conditions := []watch.ConditionFunc{ func(event watch.Event) (bool, error) { t.Logf("got %#v", event) return event.Type == watch.Added, nil }, func(event watch.Event) (bool, error) { t.Logf("got %#v", event) return event.Type == watch.Modified, nil }, } timeout := 10 * time.Second lastEvent, err := ListWatchUntil(timeout, listwatch, conditions...) if err != nil { t.Fatalf("expected nil error, got %#v", err) } if lastEvent == nil { t.Fatal("expected an event") } if lastEvent.Type != watch.Modified { t.Fatalf("expected MODIFIED event type, got %v", lastEvent.Type) } if got, isPod := lastEvent.Object.(*v1.Pod); !isPod { t.Fatalf("expected a pod event, got %#v", got) } }
func TestReflectorForWatchCache(t *testing.T) { store := newTestWatchCache(5) { _, version, err := store.WaitUntilFreshAndList(0, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } if version != 0 { t.Errorf("unexpected resource version: %d", version) } } lw := &testLW{ WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { fw := watch.NewFake() go fw.Stop() return fw, nil }, ListFunc: func(options v1.ListOptions) (runtime.Object, error) { return &api.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10"}}, nil }, } r := cache.NewReflector(lw, &api.Pod{}, store, 0) r.ListAndWatch(wait.NeverStop) { _, version, err := store.WaitUntilFreshAndList(10, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } if version != 10 { t.Errorf("unexpected resource version: %d", version) } } }
func TestCloseWatchChannelOnError(t *testing.T) { r := NewReflector(&testLW{}, &v1.Pod{}, NewStore(MetaNamespaceKeyFunc), 0) pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}} fw := watch.NewFake() r.listerWatcher = &testLW{ WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { return fw, nil }, ListFunc: func(options v1.ListOptions) (runtime.Object, error) { return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil }, } go r.ListAndWatch(wait.NeverStop) fw.Error(pod) select { case _, ok := <-fw.ResultChan(): if ok { t.Errorf("Watch channel left open after cancellation") } case <-time.After(wait.ForeverTestTimeout): t.Errorf("the cancellation is at least %s late", wait.ForeverTestTimeout.String()) break } }
func TestGetFirstPod(t *testing.T) { labelSet := map[string]string{"test": "selector"} tests := []struct { name string podList *api.PodList watching []watch.Event sortBy func([]*v1.Pod) sort.Interface expected *api.Pod expectedNum int expectedErr bool }{ { name: "kubectl logs - two ready pods", podList: newPodList(2, -1, -1, labelSet), sortBy: func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) }, expected: &api.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod-1", Namespace: api.NamespaceDefault, CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 0, 0, time.UTC), Labels: map[string]string{"test": "selector"}, }, Status: api.PodStatus{ Conditions: []api.PodCondition{ { Status: api.ConditionTrue, Type: api.PodReady, }, }, }, }, expectedNum: 2, }, { name: "kubectl logs - one unhealthy, one healthy", podList: newPodList(2, -1, 1, labelSet), sortBy: func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) }, expected: &api.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod-2", Namespace: api.NamespaceDefault, CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 1, 0, time.UTC), Labels: map[string]string{"test": "selector"}, }, Status: api.PodStatus{ Conditions: []api.PodCondition{ { Status: api.ConditionTrue, Type: api.PodReady, }, }, ContainerStatuses: []api.ContainerStatus{{RestartCount: 5}}, }, }, expectedNum: 2, }, { name: "kubectl attach - two ready pods", podList: newPodList(2, -1, -1, labelSet), sortBy: func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }, expected: &api.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod-1", Namespace: api.NamespaceDefault, CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 0, 0, time.UTC), Labels: map[string]string{"test": "selector"}, }, Status: api.PodStatus{ Conditions: []api.PodCondition{ { Status: api.ConditionTrue, Type: api.PodReady, }, }, }, }, expectedNum: 2, }, { name: "kubectl attach - wait for ready pod", podList: newPodList(1, 1, -1, labelSet), watching: []watch.Event{ { Type: watch.Modified, Object: &api.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod-1", Namespace: api.NamespaceDefault, CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 0, 0, time.UTC), Labels: map[string]string{"test": "selector"}, }, Status: api.PodStatus{ Conditions: []api.PodCondition{ { Status: api.ConditionTrue, Type: api.PodReady, }, }, }, }, }, }, sortBy: func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }, expected: &api.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod-1", Namespace: api.NamespaceDefault, CreationTimestamp: metav1.Date(2016, time.April, 1, 1, 0, 0, 0, time.UTC), Labels: map[string]string{"test": "selector"}, }, Status: api.PodStatus{ Conditions: []api.PodCondition{ { Status: api.ConditionTrue, Type: api.PodReady, }, }, }, }, expectedNum: 1, }, } for i := range tests { test := tests[i] fake := fake.NewSimpleClientset(test.podList) if len(test.watching) > 0 { watcher := watch.NewFake() for _, event := range test.watching { switch event.Type { case watch.Added: go watcher.Add(event.Object) case watch.Modified: go watcher.Modify(event.Object) } } fake.PrependWatchReactor("pods", testcore.DefaultWatchReactor(watcher, nil)) } selector := labels.Set(labelSet).AsSelector() pod, numPods, err := GetFirstPod(fake.Core(), api.NamespaceDefault, selector, 1*time.Minute, test.sortBy) pod.Spec.SecurityContext = nil if !test.expectedErr && err != nil { t.Errorf("%s: unexpected error: %v", test.name, err) continue } if test.expectedErr && err == nil { t.Errorf("%s: expected an error", test.name) continue } if test.expectedNum != numPods { t.Errorf("%s: expected %d pods, got %d", test.name, test.expectedNum, numPods) continue } if !reflect.DeepEqual(test.expected, pod) { t.Errorf("%s:\nexpected pod:\n%#v\ngot:\n%#v\n\n", test.name, test.expected, pod) } } }
func TestReplicaSetController(t *testing.T) { flag.Set("logtostderr", "true") flag.Set("v", "5") flag.Parse() replicaSetReviewDelay = 10 * time.Millisecond clusterAvailableDelay = 20 * time.Millisecond clusterUnavailableDelay = 60 * time.Millisecond allReplicaSetReviewDelay = 120 * time.Millisecond fedclientset := fedclientfake.NewSimpleClientset() fedrswatch := watch.NewFake() fedclientset.PrependWatchReactor("replicasets", core.DefaultWatchReactor(fedrswatch, nil)) fedclientset.Federation().Clusters().Create(testutil.NewCluster("k8s-1", apiv1.ConditionTrue)) fedclientset.Federation().Clusters().Create(testutil.NewCluster("k8s-2", apiv1.ConditionTrue)) kube1clientset := kubeclientfake.NewSimpleClientset() kube1rswatch := watch.NewFake() kube1clientset.PrependWatchReactor("replicasets", core.DefaultWatchReactor(kube1rswatch, nil)) kube1Podwatch := watch.NewFake() kube1clientset.PrependWatchReactor("pods", core.DefaultWatchReactor(kube1Podwatch, nil)) kube2clientset := kubeclientfake.NewSimpleClientset() kube2rswatch := watch.NewFake() kube2clientset.PrependWatchReactor("replicasets", core.DefaultWatchReactor(kube2rswatch, nil)) kube2Podwatch := watch.NewFake() kube2clientset.PrependWatchReactor("pods", core.DefaultWatchReactor(kube2Podwatch, nil)) fedInformerClientFactory := func(cluster *fedv1.Cluster) (kubeclientset.Interface, error) { switch cluster.Name { case "k8s-1": return kube1clientset, nil case "k8s-2": return kube2clientset, nil default: return nil, fmt.Errorf("Unknown cluster: %v", cluster.Name) } } replicaSetController := NewReplicaSetController(fedclientset) rsFedinformer := testutil.ToFederatedInformerForTestOnly(replicaSetController.fedReplicaSetInformer) rsFedinformer.SetClientFactory(fedInformerClientFactory) podFedinformer := testutil.ToFederatedInformerForTestOnly(replicaSetController.fedPodInformer) podFedinformer.SetClientFactory(fedInformerClientFactory) stopChan := make(chan struct{}) defer close(stopChan) go replicaSetController.Run(1, stopChan) rs := newReplicaSetWithReplicas("rs", 9) rs, _ = fedclientset.Extensions().ReplicaSets(apiv1.NamespaceDefault).Create(rs) fedrswatch.Add(rs) time.Sleep(1 * time.Second) rs1, _ := kube1clientset.Extensions().ReplicaSets(apiv1.NamespaceDefault).Get(rs.Name, metav1.GetOptions{}) kube1rswatch.Add(rs1) rs1.Status.Replicas = *rs1.Spec.Replicas rs1.Status.FullyLabeledReplicas = *rs1.Spec.Replicas rs1.Status.ReadyReplicas = *rs1.Spec.Replicas rs1.Status.AvailableReplicas = *rs1.Spec.Replicas rs1, _ = kube1clientset.Extensions().ReplicaSets(apiv1.NamespaceDefault).UpdateStatus(rs1) kube1rswatch.Modify(rs1) rs2, _ := kube2clientset.Extensions().ReplicaSets(apiv1.NamespaceDefault).Get(rs.Name, metav1.GetOptions{}) kube2rswatch.Add(rs2) rs2.Status.Replicas = *rs2.Spec.Replicas rs2.Status.FullyLabeledReplicas = *rs2.Spec.Replicas rs2.Status.ReadyReplicas = *rs2.Spec.Replicas rs2.Status.AvailableReplicas = *rs2.Spec.Replicas rs2, _ = kube2clientset.Extensions().ReplicaSets(apiv1.NamespaceDefault).UpdateStatus(rs2) kube2rswatch.Modify(rs2) time.Sleep(1 * time.Second) rs, _ = fedclientset.Extensions().ReplicaSets(apiv1.NamespaceDefault).Get(rs.Name, metav1.GetOptions{}) assert.Equal(t, *rs.Spec.Replicas, *rs1.Spec.Replicas+*rs2.Spec.Replicas) assert.Equal(t, rs.Status.Replicas, rs1.Status.Replicas+rs2.Status.Replicas) assert.Equal(t, rs.Status.FullyLabeledReplicas, rs1.Status.FullyLabeledReplicas+rs2.Status.FullyLabeledReplicas) assert.Equal(t, rs.Status.ReadyReplicas, rs1.Status.ReadyReplicas+rs2.Status.ReadyReplicas) assert.Equal(t, rs.Status.AvailableReplicas, rs1.Status.AvailableReplicas+rs2.Status.AvailableReplicas) var replicas int32 = 20 rs.Spec.Replicas = &replicas rs, _ = fedclientset.Extensions().ReplicaSets(apiv1.NamespaceDefault).Update(rs) fedrswatch.Modify(rs) time.Sleep(1 * time.Second) rs1, _ = kube1clientset.Extensions().ReplicaSets(apiv1.NamespaceDefault).Get(rs.Name, metav1.GetOptions{}) rs1.Status.Replicas = *rs1.Spec.Replicas rs1.Status.FullyLabeledReplicas = *rs1.Spec.Replicas rs1.Status.ReadyReplicas = *rs1.Spec.Replicas rs1.Status.AvailableReplicas = *rs1.Spec.Replicas rs1, _ = kube1clientset.Extensions().ReplicaSets(apiv1.NamespaceDefault).UpdateStatus(rs1) kube1rswatch.Modify(rs1) rs2, _ = kube2clientset.Extensions().ReplicaSets(apiv1.NamespaceDefault).Get(rs.Name, metav1.GetOptions{}) rs2.Status.Replicas = *rs2.Spec.Replicas rs2.Status.FullyLabeledReplicas = *rs2.Spec.Replicas rs2.Status.ReadyReplicas = *rs2.Spec.Replicas rs2.Status.AvailableReplicas = *rs2.Spec.Replicas rs2, _ = kube2clientset.Extensions().ReplicaSets(apiv1.NamespaceDefault).UpdateStatus(rs2) kube2rswatch.Modify(rs2) time.Sleep(1 * time.Second) rs, _ = fedclientset.Extensions().ReplicaSets(apiv1.NamespaceDefault).Get(rs.Name, metav1.GetOptions{}) assert.Equal(t, *rs.Spec.Replicas, *rs1.Spec.Replicas+*rs2.Spec.Replicas) assert.Equal(t, rs.Status.Replicas, rs1.Status.Replicas+rs2.Status.Replicas) assert.Equal(t, rs.Status.FullyLabeledReplicas, rs1.Status.FullyLabeledReplicas+rs2.Status.FullyLabeledReplicas) assert.Equal(t, rs.Status.ReadyReplicas, rs1.Status.ReadyReplicas+rs2.Status.ReadyReplicas) assert.Equal(t, rs.Status.AvailableReplicas, rs1.Status.AvailableReplicas+rs2.Status.AvailableReplicas) }
func (tc *testCase) prepareTestClient(t *testing.T) *fake.Clientset { namespace := "test-namespace" hpaName := "test-hpa" podNamePrefix := "test-pod" selector := &metav1.LabelSelector{ MatchLabels: map[string]string{"name": podNamePrefix}, } tc.Lock() tc.scaleUpdated = false tc.statusUpdated = false tc.eventCreated = false tc.processed = make(chan string, 100) if tc.CPUCurrent == 0 { tc.computeCPUCurrent() } // TODO(madhusudancs): HPA only supports resources in extensions/v1beta1 right now. Add // tests for "v1" replicationcontrollers when HPA adds support for cross-group scale. if tc.resource == nil { tc.resource = &fakeResource{ name: "test-rc", apiVersion: "extensions/v1beta1", kind: "replicationcontrollers", } } tc.Unlock() fakeClient := &fake.Clientset{} fakeClient.AddReactor("list", "horizontalpodautoscalers", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() obj := &autoscaling.HorizontalPodAutoscalerList{ Items: []autoscaling.HorizontalPodAutoscaler{ { ObjectMeta: metav1.ObjectMeta{ Name: hpaName, Namespace: namespace, SelfLink: "experimental/v1/namespaces/" + namespace + "/horizontalpodautoscalers/" + hpaName, }, Spec: autoscaling.HorizontalPodAutoscalerSpec{ ScaleTargetRef: autoscaling.CrossVersionObjectReference{ Kind: tc.resource.kind, Name: tc.resource.name, APIVersion: tc.resource.apiVersion, }, MinReplicas: &tc.minReplicas, MaxReplicas: tc.maxReplicas, }, Status: autoscaling.HorizontalPodAutoscalerStatus{ CurrentReplicas: tc.initialReplicas, DesiredReplicas: tc.initialReplicas, }, }, }, } if tc.CPUTarget > 0.0 { obj.Items[0].Spec.TargetCPUUtilizationPercentage = &tc.CPUTarget } if tc.cmTarget != nil { b, err := json.Marshal(tc.cmTarget) if err != nil { t.Fatalf("Failed to marshal cm: %v", err) } obj.Items[0].Annotations = make(map[string]string) obj.Items[0].Annotations[HpaCustomMetricsTargetAnnotationName] = string(b) } return true, obj, nil }) fakeClient.AddReactor("get", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() obj := &extensions.Scale{ ObjectMeta: metav1.ObjectMeta{ Name: tc.resource.name, Namespace: namespace, }, Spec: extensions.ScaleSpec{ Replicas: tc.initialReplicas, }, Status: extensions.ScaleStatus{ Replicas: tc.initialReplicas, Selector: selector.MatchLabels, }, } return true, obj, nil }) fakeClient.AddReactor("get", "deployments", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() obj := &extensions.Scale{ ObjectMeta: metav1.ObjectMeta{ Name: tc.resource.name, Namespace: namespace, }, Spec: extensions.ScaleSpec{ Replicas: tc.initialReplicas, }, Status: extensions.ScaleStatus{ Replicas: tc.initialReplicas, Selector: selector.MatchLabels, }, } return true, obj, nil }) fakeClient.AddReactor("get", "replicasets", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() obj := &extensions.Scale{ ObjectMeta: metav1.ObjectMeta{ Name: tc.resource.name, Namespace: namespace, }, Spec: extensions.ScaleSpec{ Replicas: tc.initialReplicas, }, Status: extensions.ScaleStatus{ Replicas: tc.initialReplicas, Selector: selector.MatchLabels, }, } return true, obj, nil }) fakeClient.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() obj := &v1.PodList{} for i := 0; i < len(tc.reportedCPURequests); i++ { podReadiness := v1.ConditionTrue if tc.reportedPodReadiness != nil { podReadiness = tc.reportedPodReadiness[i] } podName := fmt.Sprintf("%s-%d", podNamePrefix, i) pod := v1.Pod{ Status: v1.PodStatus{ Phase: v1.PodRunning, Conditions: []v1.PodCondition{ { Type: v1.PodReady, Status: podReadiness, }, }, }, ObjectMeta: metav1.ObjectMeta{ Name: podName, Namespace: namespace, Labels: map[string]string{ "name": podNamePrefix, }, }, Spec: v1.PodSpec{ Containers: []v1.Container{ { Resources: v1.ResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceCPU: tc.reportedCPURequests[i], }, }, }, }, }, } obj.Items = append(obj.Items, pod) } return true, obj, nil }) fakeClient.AddProxyReactor("services", func(action core.Action) (handled bool, ret restclient.ResponseWrapper, err error) { tc.Lock() defer tc.Unlock() var heapsterRawMemResponse []byte if tc.useMetricsApi { metrics := metricsapi.PodMetricsList{} for i, cpu := range tc.reportedLevels { podMetric := metricsapi.PodMetrics{ ObjectMeta: v1.ObjectMeta{ Name: fmt.Sprintf("%s-%d", podNamePrefix, i), Namespace: namespace, }, Timestamp: unversioned.Time{Time: time.Now()}, Containers: []metricsapi.ContainerMetrics{ { Name: "container", Usage: v1.ResourceList{ v1.ResourceCPU: *resource.NewMilliQuantity( int64(cpu), resource.DecimalSI), v1.ResourceMemory: *resource.NewQuantity( int64(1024*1024), resource.BinarySI), }, }, }, } metrics.Items = append(metrics.Items, podMetric) } heapsterRawMemResponse, _ = json.Marshal(&metrics) } else { // only return the pods that we actually asked for proxyAction := action.(core.ProxyGetAction) pathParts := strings.Split(proxyAction.GetPath(), "/") // pathParts should look like [ api, v1, model, namespaces, $NS, pod-list, $PODS, metrics, $METRIC... ] if len(pathParts) < 9 { return true, nil, fmt.Errorf("invalid heapster path %q", proxyAction.GetPath()) } podNames := strings.Split(pathParts[7], ",") podPresent := make([]bool, len(tc.reportedLevels)) for _, name := range podNames { if len(name) <= len(podNamePrefix)+1 { return true, nil, fmt.Errorf("unknown pod %q", name) } num, err := strconv.Atoi(name[len(podNamePrefix)+1:]) if err != nil { return true, nil, fmt.Errorf("unknown pod %q", name) } podPresent[num] = true } timestamp := time.Now() metrics := heapster.MetricResultList{} for i, level := range tc.reportedLevels { if !podPresent[i] { continue } metric := heapster.MetricResult{ Metrics: []heapster.MetricPoint{{Timestamp: timestamp, Value: level, FloatValue: nil}}, LatestTimestamp: timestamp, } metrics.Items = append(metrics.Items, metric) } heapsterRawMemResponse, _ = json.Marshal(&metrics) } return true, newFakeResponseWrapper(heapsterRawMemResponse), nil }) fakeClient.AddReactor("update", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() obj := action.(core.UpdateAction).GetObject().(*extensions.Scale) replicas := action.(core.UpdateAction).GetObject().(*extensions.Scale).Spec.Replicas assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the RC should be as expected") tc.scaleUpdated = true return true, obj, nil }) fakeClient.AddReactor("update", "deployments", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() obj := action.(core.UpdateAction).GetObject().(*extensions.Scale) replicas := action.(core.UpdateAction).GetObject().(*extensions.Scale).Spec.Replicas assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the deployment should be as expected") tc.scaleUpdated = true return true, obj, nil }) fakeClient.AddReactor("update", "replicasets", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() obj := action.(core.UpdateAction).GetObject().(*extensions.Scale) replicas := action.(core.UpdateAction).GetObject().(*extensions.Scale).Spec.Replicas assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the replicaset should be as expected") tc.scaleUpdated = true return true, obj, nil }) fakeClient.AddReactor("update", "horizontalpodautoscalers", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() obj := action.(core.UpdateAction).GetObject().(*autoscaling.HorizontalPodAutoscaler) assert.Equal(t, namespace, obj.Namespace, "the HPA namespace should be as expected") assert.Equal(t, hpaName, obj.Name, "the HPA name should be as expected") assert.Equal(t, tc.desiredReplicas, obj.Status.DesiredReplicas, "the desired replica count reported in the object status should be as expected") if tc.verifyCPUCurrent { assert.NotNil(t, obj.Status.CurrentCPUUtilizationPercentage, "the reported CPU utilization percentage should be non-nil") assert.Equal(t, tc.CPUCurrent, *obj.Status.CurrentCPUUtilizationPercentage, "the report CPU utilization percentage should be as expected") } tc.statusUpdated = true // Every time we reconcile HPA object we are updating status. tc.processed <- obj.Name return true, obj, nil }) fakeClient.AddReactor("*", "events", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() obj := action.(core.CreateAction).GetObject().(*v1.Event) if tc.verifyEvents { switch obj.Reason { case "SuccessfulRescale": assert.Equal(t, fmt.Sprintf("New size: %d; reason: CPU utilization above target", tc.desiredReplicas), obj.Message) case "DesiredReplicasComputed": assert.Equal(t, fmt.Sprintf( "Computed the desired num of replicas: %d (avgCPUutil: %d, current replicas: %d)", tc.desiredReplicas, (int64(tc.reportedLevels[0])*100)/tc.reportedCPURequests[0].MilliValue(), tc.initialReplicas), obj.Message) default: assert.False(t, true, fmt.Sprintf("Unexpected event: %s / %s", obj.Reason, obj.Message)) } } tc.eventCreated = true return true, obj, nil }) fakeWatch := watch.NewFake() fakeClient.AddWatchReactor("*", core.DefaultWatchReactor(fakeWatch, nil)) return fakeClient }
func TestReplicationControllerStop(t *testing.T) { name := "foo" ns := "default" tests := []struct { Name string Objs []runtime.Object StopError error ExpectedActions []string }{ { Name: "OnlyOneRC", Objs: []runtime.Object{ &api.ReplicationControllerList{ // LIST Items: []api.ReplicationController{ { ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: ns, }, Spec: api.ReplicationControllerSpec{ Replicas: 0, Selector: map[string]string{"k1": "v1"}}, }, }, }, }, StopError: nil, ExpectedActions: []string{"get", "list", "get", "update", "get", "delete"}, }, { Name: "NoOverlapping", Objs: []runtime.Object{ &api.ReplicationControllerList{ // LIST Items: []api.ReplicationController{ { ObjectMeta: metav1.ObjectMeta{ Name: "baz", Namespace: ns, }, Spec: api.ReplicationControllerSpec{ Replicas: 0, Selector: map[string]string{"k3": "v3"}}, }, { ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: ns, }, Spec: api.ReplicationControllerSpec{ Replicas: 0, Selector: map[string]string{"k1": "v1"}}, }, }, }, }, StopError: nil, ExpectedActions: []string{"get", "list", "get", "update", "get", "delete"}, }, { Name: "OverlappingError", Objs: []runtime.Object{ &api.ReplicationControllerList{ // LIST Items: []api.ReplicationController{ { ObjectMeta: metav1.ObjectMeta{ Name: "baz", Namespace: ns, }, Spec: api.ReplicationControllerSpec{ Replicas: 0, Selector: map[string]string{"k1": "v1", "k2": "v2"}}, }, { ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: ns, }, Spec: api.ReplicationControllerSpec{ Replicas: 0, Selector: map[string]string{"k1": "v1"}}, }, }, }, }, StopError: fmt.Errorf("Detected overlapping controllers for rc foo: baz, please manage deletion individually with --cascade=false."), ExpectedActions: []string{"get", "list"}, }, { Name: "OverlappingButSafeDelete", Objs: []runtime.Object{ &api.ReplicationControllerList{ // LIST Items: []api.ReplicationController{ { ObjectMeta: metav1.ObjectMeta{ Name: "baz", Namespace: ns, }, Spec: api.ReplicationControllerSpec{ Replicas: 0, Selector: map[string]string{"k1": "v1", "k2": "v2", "k3": "v3"}}, }, { ObjectMeta: metav1.ObjectMeta{ Name: "zaz", Namespace: ns, }, Spec: api.ReplicationControllerSpec{ Replicas: 0, Selector: map[string]string{"k1": "v1"}}, }, { ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: ns, }, Spec: api.ReplicationControllerSpec{ Replicas: 0, Selector: map[string]string{"k1": "v1", "k2": "v2"}}, }, }, }, }, StopError: fmt.Errorf("Detected overlapping controllers for rc foo: baz,zaz, please manage deletion individually with --cascade=false."), ExpectedActions: []string{"get", "list"}, }, { Name: "TwoExactMatchRCs", Objs: []runtime.Object{ &api.ReplicationControllerList{ // LIST Items: []api.ReplicationController{ { ObjectMeta: metav1.ObjectMeta{ Name: "zaz", Namespace: ns, }, Spec: api.ReplicationControllerSpec{ Replicas: 0, Selector: map[string]string{"k1": "v1"}}, }, { ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: ns, }, Spec: api.ReplicationControllerSpec{ Replicas: 0, Selector: map[string]string{"k1": "v1"}}, }, }, }, }, StopError: nil, ExpectedActions: []string{"get", "list", "delete"}, }, } for _, test := range tests { copiedForWatch, err := api.Scheme.Copy(test.Objs[0]) if err != nil { t.Fatalf("%s unexpected error: %v", test.Name, err) } fake := fake.NewSimpleClientset(test.Objs...) fakeWatch := watch.NewFake() fake.PrependWatchReactor("replicationcontrollers", testcore.DefaultWatchReactor(fakeWatch, nil)) go func() { fakeWatch.Add(copiedForWatch) }() reaper := ReplicationControllerReaper{fake.Core(), time.Millisecond, time.Millisecond} err = reaper.Stop(ns, name, 0, nil) if !reflect.DeepEqual(err, test.StopError) { t.Errorf("%s unexpected error: %v", test.Name, err) continue } actions := fake.Actions() if len(actions) != len(test.ExpectedActions) { t.Errorf("%s unexpected actions: %v, expected %d actions got %d", test.Name, actions, len(test.ExpectedActions), len(actions)) continue } for i, verb := range test.ExpectedActions { if actions[i].GetResource().GroupResource() != api.Resource("replicationcontrollers") { t.Errorf("%s unexpected action: %+v, expected %s-replicationController", test.Name, actions[i], verb) } if actions[i].GetVerb() != verb { t.Errorf("%s unexpected action: %+v, expected %s-replicationController", test.Name, actions[i], verb) } } } }
// Watch watches Nodes in a fake store. func (m *FakeNodeHandler) Watch(opts v1.ListOptions) (watch.Interface, error) { return watch.NewFake(), nil }
func TestReflectorListAndWatchWithErrors(t *testing.T) { mkPod := func(id string, rv string) *v1.Pod { return &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: id, ResourceVersion: rv}} } mkList := func(rv string, pods ...*v1.Pod) *v1.PodList { list := &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: rv}} for _, pod := range pods { list.Items = append(list.Items, *pod) } return list } table := []struct { list *v1.PodList listErr error events []watch.Event watchErr error }{ { list: mkList("1"), events: []watch.Event{ {Type: watch.Added, Object: mkPod("foo", "2")}, {Type: watch.Added, Object: mkPod("bar", "3")}, }, }, { list: mkList("3", mkPod("foo", "2"), mkPod("bar", "3")), events: []watch.Event{ {Type: watch.Deleted, Object: mkPod("foo", "4")}, {Type: watch.Added, Object: mkPod("qux", "5")}, }, }, { listErr: fmt.Errorf("a list error"), }, { list: mkList("5", mkPod("bar", "3"), mkPod("qux", "5")), watchErr: fmt.Errorf("a watch error"), }, { list: mkList("5", mkPod("bar", "3"), mkPod("qux", "5")), events: []watch.Event{ {Type: watch.Added, Object: mkPod("baz", "6")}, }, }, { list: mkList("6", mkPod("bar", "3"), mkPod("qux", "5"), mkPod("baz", "6")), }, } s := NewFIFO(MetaNamespaceKeyFunc) for line, item := range table { if item.list != nil { // Test that the list is what currently exists in the store. current := s.List() checkMap := map[string]string{} for _, item := range current { pod := item.(*v1.Pod) checkMap[pod.Name] = pod.ResourceVersion } for _, pod := range item.list.Items { if e, a := pod.ResourceVersion, checkMap[pod.Name]; e != a { t.Errorf("%v: expected %v, got %v for pod %v", line, e, a, pod.Name) } } if e, a := len(item.list.Items), len(checkMap); e != a { t.Errorf("%v: expected %v, got %v", line, e, a) } } watchRet, watchErr := item.events, item.watchErr lw := &testLW{ WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if watchErr != nil { return nil, watchErr } watchErr = fmt.Errorf("second watch") fw := watch.NewFake() go func() { for _, e := range watchRet { fw.Action(e.Type, e.Object) } fw.Stop() }() return fw, nil }, ListFunc: func(options v1.ListOptions) (runtime.Object, error) { return item.list, item.listErr }, } r := NewReflector(lw, &v1.Pod{}, s, 0) r.ListAndWatch(wait.NeverStop) } }
func TestNewEndpointsSourceApi_UpdatesAndMultipleEndpoints(t *testing.T) { endpoints1v1 := &api.Endpoints{ ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "e1"}, Subsets: []api.EndpointSubset{{ Addresses: []api.EndpointAddress{ {IP: "1.2.3.4"}, }, Ports: []api.EndpointPort{{Port: 8080, Protocol: "TCP"}}, }}, } endpoints1v2 := &api.Endpoints{ ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "e1"}, Subsets: []api.EndpointSubset{{ Addresses: []api.EndpointAddress{ {IP: "1.2.3.4"}, {IP: "4.3.2.1"}, }, Ports: []api.EndpointPort{{Port: 8080, Protocol: "TCP"}}, }}, } endpoints2 := &api.Endpoints{ ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "e2"}, Subsets: []api.EndpointSubset{{ Addresses: []api.EndpointAddress{ {IP: "5.6.7.8"}, }, Ports: []api.EndpointPort{{Port: 80, Protocol: "TCP"}}, }}, } // Setup fake api client. fakeWatch := watch.NewFake() lw := fakeLW{ listResp: &api.EndpointsList{Items: []api.Endpoints{}}, watchResp: fakeWatch, } ch := make(chan EndpointsUpdate) cache.NewReflector(lw, &api.Endpoints{}, NewEndpointsStore(nil, ch), 30*time.Second).Run() got, ok := <-ch if !ok { t.Errorf("Unable to read from channel when expected") } expected := EndpointsUpdate{Op: SET, Endpoints: []api.Endpoints{}} if !api.Semantic.DeepEqual(expected, got) { t.Errorf("Expected %#v; Got %#v", expected, got) } // Add the first endpoints fakeWatch.Add(endpoints1v1) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } expected = EndpointsUpdate{Op: SET, Endpoints: []api.Endpoints{*endpoints1v1}} if !api.Semantic.DeepEqual(expected, got) { t.Errorf("Expected %#v; Got %#v", expected, got) } // Add another endpoints fakeWatch.Add(endpoints2) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } // Could be sorted either of these two ways: expectedA := EndpointsUpdate{Op: SET, Endpoints: []api.Endpoints{*endpoints1v1, *endpoints2}} expectedB := EndpointsUpdate{Op: SET, Endpoints: []api.Endpoints{*endpoints2, *endpoints1v1}} if !api.Semantic.DeepEqual(expectedA, got) && !api.Semantic.DeepEqual(expectedB, got) { t.Errorf("Expected %#v or %#v, Got %#v", expectedA, expectedB, got) } // Modify endpoints1 fakeWatch.Modify(endpoints1v2) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } expectedA = EndpointsUpdate{Op: SET, Endpoints: []api.Endpoints{*endpoints1v2, *endpoints2}} expectedB = EndpointsUpdate{Op: SET, Endpoints: []api.Endpoints{*endpoints2, *endpoints1v2}} if !api.Semantic.DeepEqual(expectedA, got) && !api.Semantic.DeepEqual(expectedB, got) { t.Errorf("Expected %#v or %#v, Got %#v", expectedA, expectedB, got) } // Delete endpoints1 fakeWatch.Delete(endpoints1v2) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } expected = EndpointsUpdate{Op: SET, Endpoints: []api.Endpoints{*endpoints2}} if !api.Semantic.DeepEqual(expected, got) { t.Errorf("Expected %#v, Got %#v", expected, got) } // Delete endpoints2 fakeWatch.Delete(endpoints2) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } expected = EndpointsUpdate{Op: SET, Endpoints: []api.Endpoints{}} if !api.Semantic.DeepEqual(expected, got) { t.Errorf("Expected %#v, Got %#v", expected, got) } }
func TestNewSourceApiserver_UpdatesAndMultiplePods(t *testing.T) { pod1v1 := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "p"}, Spec: v1.PodSpec{Containers: []v1.Container{{Image: "image/one"}}}} pod1v2 := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "p"}, Spec: v1.PodSpec{Containers: []v1.Container{{Image: "image/two"}}}} pod2 := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "q"}, Spec: v1.PodSpec{Containers: []v1.Container{{Image: "image/blah"}}}} // Setup fake api client. fakeWatch := watch.NewFake() lw := fakePodLW{ listResp: &v1.PodList{Items: []v1.Pod{*pod1v1}}, watchResp: fakeWatch, } ch := make(chan interface{}) newSourceApiserverFromLW(lw, ch) got, ok := <-ch if !ok { t.Errorf("Unable to read from channel when expected") } update := got.(kubetypes.PodUpdate) expected := CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource, pod1v1) if !api.Semantic.DeepEqual(expected, update) { t.Errorf("Expected %#v; Got %#v", expected, update) } // Add another pod fakeWatch.Add(pod2) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } update = got.(kubetypes.PodUpdate) // Could be sorted either of these two ways: expectedA := CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource, pod1v1, pod2) expectedB := CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource, pod2, pod1v1) if !api.Semantic.DeepEqual(expectedA, update) && !api.Semantic.DeepEqual(expectedB, update) { t.Errorf("Expected %#v or %#v, Got %#v", expectedA, expectedB, update) } // Modify pod1 fakeWatch.Modify(pod1v2) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } update = got.(kubetypes.PodUpdate) expectedA = CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource, pod1v2, pod2) expectedB = CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource, pod2, pod1v2) if !api.Semantic.DeepEqual(expectedA, update) && !api.Semantic.DeepEqual(expectedB, update) { t.Errorf("Expected %#v or %#v, Got %#v", expectedA, expectedB, update) } // Delete pod1 fakeWatch.Delete(pod1v2) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } update = got.(kubetypes.PodUpdate) expected = CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource, pod2) if !api.Semantic.DeepEqual(expected, update) { t.Errorf("Expected %#v, Got %#v", expected, update) } // Delete pod2 fakeWatch.Delete(pod2) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } update = got.(kubetypes.PodUpdate) expected = CreatePodUpdate(kubetypes.SET, kubetypes.ApiserverSource) if !api.Semantic.DeepEqual(expected, update) { t.Errorf("Expected %#v, Got %#v", expected, update) } }
func TestNewServicesSourceApi_UpdatesAndMultipleServices(t *testing.T) { service1v1 := &api.Service{ ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "s1"}, Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 10}}}} service1v2 := &api.Service{ ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "s1"}, Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 20}}}} service2 := &api.Service{ ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "s2"}, Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 30}}}} // Setup fake api client. fakeWatch := watch.NewFake() lw := fakeLW{ listResp: &api.ServiceList{Items: []api.Service{}}, watchResp: fakeWatch, } ch := make(chan ServiceUpdate) cache.NewReflector(lw, &api.Service{}, NewServiceStore(nil, ch), 30*time.Second).Run() got, ok := <-ch if !ok { t.Errorf("Unable to read from channel when expected") } expected := ServiceUpdate{Op: SET, Services: []api.Service{}} if !api.Semantic.DeepEqual(expected, got) { t.Errorf("Expected %#v; Got %#v", expected, got) } // Add the first service fakeWatch.Add(service1v1) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } expected = ServiceUpdate{Op: SET, Services: []api.Service{*service1v1}} if !api.Semantic.DeepEqual(expected, got) { t.Errorf("Expected %#v; Got %#v", expected, got) } // Add another service fakeWatch.Add(service2) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } // Could be sorted either of these two ways: expectedA := ServiceUpdate{Op: SET, Services: []api.Service{*service1v1, *service2}} expectedB := ServiceUpdate{Op: SET, Services: []api.Service{*service2, *service1v1}} if !api.Semantic.DeepEqual(expectedA, got) && !api.Semantic.DeepEqual(expectedB, got) { t.Errorf("Expected %#v or %#v, Got %#v", expectedA, expectedB, got) } // Modify service1 fakeWatch.Modify(service1v2) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } expectedA = ServiceUpdate{Op: SET, Services: []api.Service{*service1v2, *service2}} expectedB = ServiceUpdate{Op: SET, Services: []api.Service{*service2, *service1v2}} if !api.Semantic.DeepEqual(expectedA, got) && !api.Semantic.DeepEqual(expectedB, got) { t.Errorf("Expected %#v or %#v, Got %#v", expectedA, expectedB, got) } // Delete service1 fakeWatch.Delete(service1v2) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } expected = ServiceUpdate{Op: SET, Services: []api.Service{*service2}} if !api.Semantic.DeepEqual(expected, got) { t.Errorf("Expected %#v, Got %#v", expected, got) } // Delete service2 fakeWatch.Delete(service2) got, ok = <-ch if !ok { t.Errorf("Unable to read from channel when expected") } expected = ServiceUpdate{Op: SET, Services: []api.Service{}} if !api.Semantic.DeepEqual(expected, got) { t.Errorf("Expected %#v, Got %#v", expected, got) } }
func TestWatchHTTPTimeout(t *testing.T) { watcher := watch.NewFake() timeoutCh := make(chan time.Time) done := make(chan struct{}) info, ok := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), runtime.ContentTypeJSON) if !ok || info.StreamSerializer == nil { t.Fatal(info) } serializer := info.StreamSerializer // Setup a new watchserver watchServer := &handlers.WatchServer{ Watching: watcher, MediaType: "testcase/json", Framer: serializer.Framer, Encoder: newCodec, EmbeddedEncoder: newCodec, Fixup: func(obj runtime.Object) {}, TimeoutFactory: &fakeTimeoutFactory{timeoutCh, done}, } s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { watchServer.ServeHTTP(w, req) })) defer s.Close() // Setup a client dest, _ := url.Parse(s.URL) dest.Path = "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/simple" dest.RawQuery = "watch=true" req, _ := http.NewRequest("GET", dest.String(), nil) client := http.Client{} resp, err := client.Do(req) watcher.Add(&apitesting.Simple{TypeMeta: metav1.TypeMeta{APIVersion: newGroupVersion.String()}}) // Make sure we can actually watch an endpoint decoder := json.NewDecoder(resp.Body) var got watchJSON err = decoder.Decode(&got) if err != nil { t.Fatalf("Unexpected error: %v", err) } // Timeout and check for leaks close(timeoutCh) select { case <-done: if !watcher.Stopped { t.Errorf("Leaked watch on timeout") } case <-time.After(wait.ForeverTestTimeout): t.Errorf("Failed to stop watcher after %s of timeout signal", wait.ForeverTestTimeout.String()) } // Make sure we can't receive any more events through the timeout watch err = decoder.Decode(&got) if err != io.EOF { t.Errorf("Unexpected non-error") } }
// Basic test for Federated Informer. Checks whether the subinformer are added and deleted // when the corresponding cluster entries appear and disappear from etcd. func TestFederatedInformer(t *testing.T) { fakeFederationClient := &fakefederationclientset.Clientset{} // Add a single cluster to federation and remove it when needed. cluster := federationapi.Cluster{ ObjectMeta: metav1.ObjectMeta{ Name: "mycluster", }, Status: federationapi.ClusterStatus{ Conditions: []federationapi.ClusterCondition{ {Type: federationapi.ClusterReady, Status: apiv1.ConditionTrue}, }, }, } fakeFederationClient.AddReactor("list", "clusters", func(action core.Action) (bool, runtime.Object, error) { return true, &federationapi.ClusterList{Items: []federationapi.Cluster{cluster}}, nil }) deleteChan := make(chan struct{}) fakeFederationClient.AddWatchReactor("clusters", func(action core.Action) (bool, watch.Interface, error) { fakeWatch := watch.NewFake() go func() { <-deleteChan fakeWatch.Delete(&cluster) }() return true, fakeWatch, nil }) fakeKubeClient := &fakekubeclientset.Clientset{} // There is a single service ns1/s1 in cluster mycluster. service := apiv1.Service{ ObjectMeta: metav1.ObjectMeta{ Namespace: "ns1", Name: "s1", }, } fakeKubeClient.AddReactor("list", "services", func(action core.Action) (bool, runtime.Object, error) { return true, &apiv1.ServiceList{Items: []apiv1.Service{service}}, nil }) fakeKubeClient.AddWatchReactor("services", func(action core.Action) (bool, watch.Interface, error) { return true, watch.NewFake(), nil }) targetInformerFactory := func(cluster *federationapi.Cluster, clientset kubeclientset.Interface) (cache.Store, cache.Controller) { return cache.NewInformer( &cache.ListWatch{ ListFunc: func(options apiv1.ListOptions) (runtime.Object, error) { return clientset.Core().Services(apiv1.NamespaceAll).List(options) }, WatchFunc: func(options apiv1.ListOptions) (watch.Interface, error) { return clientset.Core().Services(apiv1.NamespaceAll).Watch(options) }, }, &apiv1.Service{}, 10*time.Second, cache.ResourceEventHandlerFuncs{}) } addedClusters := make(chan string, 1) deletedClusters := make(chan string, 1) lifecycle := ClusterLifecycleHandlerFuncs{ ClusterAvailable: func(cluster *federationapi.Cluster) { addedClusters <- cluster.Name close(addedClusters) }, ClusterUnavailable: func(cluster *federationapi.Cluster, _ []interface{}) { deletedClusters <- cluster.Name close(deletedClusters) }, } informer := NewFederatedInformer(fakeFederationClient, targetInformerFactory, &lifecycle).(*federatedInformerImpl) informer.clientFactory = func(cluster *federationapi.Cluster) (kubeclientset.Interface, error) { return fakeKubeClient, nil } assert.NotNil(t, informer) informer.Start() // Wait until mycluster is synced. for !informer.GetTargetStore().ClustersSynced([]*federationapi.Cluster{&cluster}) { time.Sleep(time.Millisecond * 100) } readyClusters, err := informer.GetReadyClusters() assert.NoError(t, err) assert.Contains(t, readyClusters, &cluster) serviceList, err := informer.GetTargetStore().List() assert.NoError(t, err) federatedService := FederatedObject{ClusterName: "mycluster", Object: &service} assert.Contains(t, serviceList, federatedService) service1, found, err := informer.GetTargetStore().GetByKey("mycluster", "ns1/s1") assert.NoError(t, err) assert.True(t, found) assert.EqualValues(t, &service, service1) assert.Equal(t, "mycluster", <-addedClusters) // All checked, lets delete the cluster. deleteChan <- struct{}{} for !informer.GetTargetStore().ClustersSynced([]*federationapi.Cluster{}) { time.Sleep(time.Millisecond * 100) } readyClusters, err = informer.GetReadyClusters() assert.NoError(t, err) assert.Empty(t, readyClusters) serviceList, err = informer.GetTargetStore().List() assert.NoError(t, err) assert.Empty(t, serviceList) assert.Equal(t, "mycluster", <-deletedClusters) // Test complete. informer.Stop() }