func TestEncode_Ptr(t *testing.T) { grace := int64(30) pod := &api.Pod{ ObjectMeta: api.ObjectMeta{ Labels: map[string]string{"name": "foo"}, }, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, SecurityContext: &api.PodSecurityContext{}, }, } obj := runtime.Object(pod) data, err := testapi.Default.Codec().Encode(obj) obj2, err2 := testapi.Default.Codec().Decode(data) if err != nil || err2 != nil { t.Fatalf("Failure: '%v' '%v'", err, err2) } if _, ok := obj2.(*api.Pod); !ok { t.Fatalf("Got wrong type") } if !api.Semantic.DeepEqual(obj2, pod) { t.Errorf("\nExpected:\n\n %#v,\n\nGot:\n\n %#vDiff: %v\n\n", pod, obj2, util.ObjectDiff(obj2, pod)) } }
func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) { printer := spew.ConfigState{DisableMethods: true} name := reflect.TypeOf(item).Elem().Name() data, err := codec.Encode(item) if err != nil { t.Errorf("%v: %v (%s)", name, err, printer.Sprintf("%#v", item)) return } obj2, err := codec.Decode(data) if err != nil { t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), printer.Sprintf("%#v", item)) return } if !api.Semantic.DeepEqual(item, obj2) { t.Errorf("1: %v: diff: %v\nCodec: %v\nSource:\n\n%#v\n\nEncoded:\n\n%s\n\nFinal:\n\n%#v", name, util.ObjectGoPrintDiff(item, obj2), codec, printer.Sprintf("%#v", item), string(data), printer.Sprintf("%#v", obj2)) return } obj3 := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) err = codec.DecodeInto(data, obj3) if err != nil { t.Errorf("2: %v: %v", name, err) return } if !api.Semantic.DeepEqual(item, obj3) { t.Errorf("3: %v: diff: %v\nCodec: %v", name, util.ObjectDiff(item, obj3), codec) return } }
func TestEtcdDelete(t *testing.T) { podA := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}, Spec: api.PodSpec{NodeName: "machine"}, } nodeWithPodA := tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: runtime.EncodeOrDie(testapi.Default.Codec(), podA), ModifiedIndex: 1, CreatedIndex: 1, }, }, E: nil, } emptyNode := tools.EtcdResponseWithError{ R: &etcd.Response{}, E: tools.EtcdErrorNotFound, } testContext := api.WithNamespace(api.NewContext(), "test") key := "foo" table := map[string]struct { existing tools.EtcdResponseWithError expect tools.EtcdResponseWithError errOK func(error) bool }{ "normal": { existing: nodeWithPodA, expect: emptyNode, errOK: func(err error) bool { return err == nil }, }, "notExisting": { existing: emptyNode, expect: emptyNode, errOK: func(err error) bool { return errors.IsNotFound(err) }, }, } for name, item := range table { fakeClient, registry := NewTestGenericEtcdRegistry(t) path := etcdtest.AddPrefix("pods/foo") fakeClient.Data[path] = item.existing obj, err := registry.Delete(testContext, key, nil) if !item.errOK(err) { t.Errorf("%v: unexpected error: %v (%#v)", name, err, obj) } if item.expect.E != nil { item.expect.E.(*etcd.EtcdError).Index = fakeClient.ChangeIndex } if e, a := item.expect, fakeClient.Data[path]; !api.Semantic.DeepDerivative(e, a) { t.Errorf("%v:\n%s", name, util.ObjectDiff(e, a)) } } }
func testPrinter(t *testing.T, printer ResourcePrinter, unmarshalFunc func(data []byte, v interface{}) error) { buf := bytes.NewBuffer([]byte{}) err := printer.PrintObj(&testData, buf) if err != nil { t.Fatal(err) } var poutput testStruct // Verify that given function runs without error. err = unmarshalFunc(buf.Bytes(), &poutput) if err != nil { t.Fatal(err) } // Use real decode function to undo the versioning process. poutput = testStruct{} err = runtime.YAMLDecoder(testapi.Default.Codec()).DecodeInto(buf.Bytes(), &poutput) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(testData, poutput) { t.Errorf("Test data and unmarshaled data are not equal: %v", util.ObjectDiff(poutput, testData)) } obj := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo"}, } buf.Reset() printer.PrintObj(obj, buf) var objOut api.Pod // Verify that given function runs without error. err = unmarshalFunc(buf.Bytes(), &objOut) if err != nil { t.Fatalf("unexpected error: %#v", err) } // Use real decode function to undo the versioning process. objOut = api.Pod{} err = runtime.YAMLDecoder(testapi.Default.Codec()).DecodeInto(buf.Bytes(), &objOut) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(obj, &objOut) { t.Errorf("Unexpected inequality:\n%v", util.ObjectDiff(obj, &objOut)) } }
func TestGet_NoError(t *testing.T) { r := NewTestREST(testResponse{code: 200, data: "ok"}) got, err := r.Get(api.NewContext(), "test1") if err != nil { t.Fatalf("Unexpected error: %v", err) } expect := createTestStatus("test1", api.ConditionTrue, "ok", "nil") if e, a := expect, got; !reflect.DeepEqual(e, a) { t.Errorf("Got unexpected object. Diff: %s", util.ObjectDiff(e, a)) } }
func TestList_NoError(t *testing.T) { r := NewTestREST(testResponse{code: 200, data: "ok"}) got, err := r.List(api.NewContext(), labels.Everything(), fields.Everything()) if err != nil { t.Fatalf("Unexpected error: %v", err) } expect := &api.ComponentStatusList{ Items: []api.ComponentStatus{*(createTestStatus("test1", api.ConditionTrue, "ok", "nil"))}, } if e, a := expect, got; !reflect.DeepEqual(e, a) { t.Errorf("Got unexpected object. Diff: %s", util.ObjectDiff(e, a)) } }
func TestGetAttrs(t *testing.T) { eventA := &api.Event{ ObjectMeta: api.ObjectMeta{Name: "f0118"}, InvolvedObject: api.ObjectReference{ Kind: "Pod", Name: "foo", Namespace: "baz", UID: "long uid string", APIVersion: testapi.Default.Version(), ResourceVersion: "0", FieldPath: "", }, Reason: "ForTesting", Source: api.EventSource{Component: "test"}, } label, field, err := getAttrs(eventA) if err != nil { t.Fatalf("Unexpected error %v", err) } if e, a := label, (labels.Set{}); !reflect.DeepEqual(e, a) { t.Errorf("diff: %s", util.ObjectDiff(e, a)) } expect := fields.Set{ "metadata.name": "f0118", "involvedObject.kind": "Pod", "involvedObject.name": "foo", "involvedObject.namespace": "baz", "involvedObject.uid": "long uid string", "involvedObject.apiVersion": testapi.Default.Version(), "involvedObject.resourceVersion": "0", "involvedObject.fieldPath": "", "reason": "ForTesting", "source": "test", } if e, a := expect, field; !reflect.DeepEqual(e, a) { t.Errorf("diff: %s", util.ObjectDiff(e, a)) } }
func TestList_UnknownError(t *testing.T) { r := NewTestREST(testResponse{code: 500, data: "", err: fmt.Errorf("fizzbuzz error")}) got, err := r.List(api.NewContext(), labels.Everything(), fields.Everything()) if err != nil { t.Fatalf("Unexpected error: %v", err) } expect := &api.ComponentStatusList{ Items: []api.ComponentStatus{ *(createTestStatus("test1", api.ConditionUnknown, "", "Get http://testserver1:8000/healthz: fizzbuzz error"))}, } if e, a := expect, got; !reflect.DeepEqual(e, a) { t.Errorf("Got unexpected object. Diff: %s", util.ObjectDiff(e, a)) } }
func TestUpdateStatus(t *testing.T) { storage, status, fakeClient := newStorage(t) ctx := api.NewDefaultContext() key, _ := storage.KeyFunc(ctx, "foo") key = etcdtest.AddPrefix(key) resourcequotaStart := validNewResourceQuota() fakeClient.Set(key, runtime.EncodeOrDie(testapi.Default.Codec(), resourcequotaStart), 0) resourcequotaIn := &api.ResourceQuota{ ObjectMeta: api.ObjectMeta{ Name: "foo", Namespace: api.NamespaceDefault, ResourceVersion: "1", }, Status: api.ResourceQuotaStatus{ Used: api.ResourceList{ api.ResourceCPU: resource.MustParse("1"), api.ResourceMemory: resource.MustParse("1Gi"), api.ResourcePods: resource.MustParse("1"), api.ResourceServices: resource.MustParse("1"), api.ResourceReplicationControllers: resource.MustParse("1"), api.ResourceQuotas: resource.MustParse("1"), }, Hard: api.ResourceList{ api.ResourceCPU: resource.MustParse("100"), api.ResourceMemory: resource.MustParse("4Gi"), api.ResourcePods: resource.MustParse("10"), api.ResourceServices: resource.MustParse("10"), api.ResourceReplicationControllers: resource.MustParse("10"), api.ResourceQuotas: resource.MustParse("1"), }, }, } expected := *resourcequotaStart expected.ResourceVersion = "2" expected.Labels = resourcequotaIn.Labels expected.Status = resourcequotaIn.Status _, _, err := status.Update(ctx, resourcequotaIn) if err != nil { t.Fatalf("Unexpected error: %v", err) } rqOut, err := storage.Get(ctx, "foo") if !api.Semantic.DeepEqual(&expected, rqOut) { t.Errorf("unexpected object: %s", util.ObjectDiff(&expected, rqOut)) } }
func doDeepCopyTest(t *testing.T, version, kind string, f *fuzz.Fuzzer) { item, err := api.Scheme.New(version, kind) if err != nil { t.Fatalf("Could not create a %s: %s", kind, err) } f.Fuzz(item) itemCopy, err := api.Scheme.DeepCopy(item) if err != nil { t.Errorf("Could not deep copy a %s: %s", kind, err) return } if !reflect.DeepEqual(item, itemCopy) { t.Errorf("\nexpected: %#v\n\ngot: %#v\n\ndiff: %v", item, itemCopy, util.ObjectDiff(item, itemCopy)) } }
func TestUpdateStatus(t *testing.T) { storage, statusStorage, fakeClient := newStorage(t) ctx := api.NewDefaultContext() fakeClient.TestIndex = true key, _ := storage.KeyFunc(ctx, "foo") key = etcdtest.AddPrefix(key) pvcStart := validNewPersistentVolumeClaim("foo", api.NamespaceDefault) fakeClient.Set(key, runtime.EncodeOrDie(testapi.Default.Codec(), pvcStart), 0) pvc := &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "foo", Namespace: api.NamespaceDefault, ResourceVersion: "1", }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("3Gi"), }, }, }, Status: api.PersistentVolumeClaimStatus{ Phase: api.ClaimBound, }, } expected := *pvcStart expected.ResourceVersion = "2" expected.Labels = pvc.Labels expected.Status = pvc.Status _, _, err := statusStorage.Update(ctx, pvc) if err != nil { t.Fatalf("Unexpected error: %v", err) } pvcOut, err := storage.Get(ctx, "foo") if err != nil { t.Errorf("unexpected error: %v", err) } if !api.Semantic.DeepEqual(&expected, pvcOut) { t.Errorf("unexpected object: %s", util.ObjectDiff(&expected, pvcOut)) } }
func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) { _, svc, _ := testData() node := &api.Node{ ObjectMeta: api.ObjectMeta{ Name: "foo", }, Spec: api.NodeSpec{ ExternalID: "ext", }, } f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ Codec: codec, Client: fake.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch req.URL.Path { case "/nodes/foo": return &http.Response{StatusCode: 200, Body: objBody(codec, node)}, nil case "/namespaces/test/services/bar": return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = "test" buf := bytes.NewBuffer([]byte{}) cmd := NewCmdGet(f, buf) cmd.SetOutput(buf) cmd.Run(cmd, []string{"services/bar", "node/foo"}) expected := []runtime.Object{&svc.Items[0], node} actual := tf.Printer.(*testPrinter).Objects if !api.Semantic.DeepEqual(expected, actual) { t.Errorf("unexpected object: %s", util.ObjectDiff(expected, actual)) } if len(buf.String()) == 0 { t.Errorf("unexpected empty output") } }
func TestScaleGet(t *testing.T) { storage, fakeClient := newStorage(t) ctx := api.WithNamespace(api.NewContext(), namespace) key := etcdtest.AddPrefix("/deployments/" + namespace + "/" + name) if _, err := fakeClient.Set(key, runtime.EncodeOrDie(testapi.Extensions.Codec(), &validDeployment), 0); err != nil { t.Fatalf("unexpected error: %v", err) } expect := &validScale obj, err := storage.Scale.Get(ctx, name) scale := obj.(*extensions.Scale) if err != nil { t.Fatalf("unexpected error: %v", err) } if e, a := expect, scale; !api.Semantic.DeepEqual(e, a) { t.Errorf("unexpected scale: %s", util.ObjectDiff(e, a)) } }
func TestGet(t *testing.T) { storage, fakeClient := newStorage(t) ctx := api.WithNamespace(api.NewContext(), "test") key := etcdtest.AddPrefix("/controllers/test/foo") if _, err := fakeClient.Set(key, runtime.EncodeOrDie(testapi.Default.Codec(), &validController), 0); err != nil { t.Fatalf("unexpected error: %v", err) } expect := &validScale obj, err := storage.Get(ctx, "foo") scale := obj.(*extensions.Scale) if err != nil { t.Fatalf("unexpected error: %v", err) } if e, a := expect, scale; !api.Semantic.DeepEqual(e, a) { t.Errorf("unexpected scale: %s", util.ObjectDiff(e, a)) } }
func (test configCommandTest) run(t *testing.T) string { out, actualConfig := testConfigCommand(test.args, test.startingConfig, t) testSetNilMapsToEmpties(reflect.ValueOf(&test.expectedConfig)) testSetNilMapsToEmpties(reflect.ValueOf(&actualConfig)) testClearLocationOfOrigin(&actualConfig) if !api.Semantic.DeepEqual(test.expectedConfig, actualConfig) { t.Errorf("diff: %v", util.ObjectDiff(test.expectedConfig, actualConfig)) t.Errorf("expected: %#v\n actual: %#v", test.expectedConfig, actualConfig) } for _, expectedOutput := range test.expectedOutputs { if !strings.Contains(out, expectedOutput) { t.Errorf("expected '%s' in output, got '%s'", expectedOutput, out) } } return out }
func TestSetListToRuntimeObjectArray(t *testing.T) { pl := &api.List{} list := []runtime.Object{ &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "3"}}, } err := runtime.SetList(pl, list) if err != nil { t.Fatalf("Unexpected error %v", err) } if e, a := len(list), len(pl.Items); e != a { t.Fatalf("Expected %v, got %v", e, a) } for i := range list { if e, a := list[i], pl.Items[i]; e != a { t.Fatalf("%d: unmatched: %s", i, util.ObjectDiff(e, a)) } } }
func (test stepParserTest) run(t *testing.T) { actualSteps, err := newNavigationSteps(test.path) if len(test.expectedError) != 0 { if err == nil { t.Errorf("Did not get %v", test.expectedError) } else { if !strings.Contains(err.Error(), test.expectedError) { t.Errorf("Expected %v, but got %v", test.expectedError, err) } } return } if err != nil { t.Errorf("Unexpected error: %v", err) } if !reflect.DeepEqual(test.expectedNavigationSteps, *actualSteps) { t.Errorf("diff: %v", util.ObjectDiff(test.expectedNavigationSteps, *actualSteps)) t.Errorf("expected: %#v\n actual: %#v", test.expectedNavigationSteps, *actualSteps) } }
func TestDoRequestFailed(t *testing.T) { status := &unversioned.Status{ Code: http.StatusNotFound, Status: unversioned.StatusFailure, Reason: unversioned.StatusReasonNotFound, Message: " \"\" not found", Details: &unversioned.StatusDetails{}, } expectedBody, _ := testapi.Default.Codec().Encode(status) fakeHandler := util.FakeHandler{ StatusCode: 404, ResponseBody: string(expectedBody), T: t, } testServer := httptest.NewServer(&fakeHandler) defer testServer.Close() c, err := RESTClientFor(&Config{ Host: testServer.URL, Version: testapi.Default.Version(), Codec: testapi.Default.Codec(), }) if err != nil { t.Fatalf("unexpected error: %v", err) } body, err := c.Get().Do().Raw() if err == nil || body != nil { t.Errorf("unexpected non-error: %#v", body) } ss, ok := err.(APIStatus) if !ok { t.Errorf("unexpected error type %v", err) } actual := ss.Status() if !reflect.DeepEqual(status, &actual) { t.Errorf("Unexpected mis-match: %s", util.ObjectDiff(status, &actual)) } }
func TestUpdateStatus(t *testing.T) { storage, statusStorage, fakeClient := newStorage(t) fakeClient.TestIndex = true ctx := api.NewContext() key, _ := storage.KeyFunc(ctx, "foo") key = etcdtest.AddPrefix(key) pvStart := validNewPersistentVolume("foo") fakeClient.Set(key, runtime.EncodeOrDie(testapi.Default.Codec(), pvStart), 0) pvIn := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "foo", ResourceVersion: "1", }, Status: api.PersistentVolumeStatus{ Phase: api.VolumeBound, }, } expected := *pvStart expected.ResourceVersion = "2" expected.Labels = pvIn.Labels expected.Status = pvIn.Status _, _, err := statusStorage.Update(ctx, pvIn) if err != nil { t.Fatalf("Unexpected error: %v", err) } pvOut, err := storage.Get(ctx, "foo") if err != nil { t.Errorf("unexpected error: %v", err) } if !api.Semantic.DeepEqual(&expected, pvOut) { t.Errorf("unexpected object: %s", util.ObjectDiff(&expected, pvOut)) } }
func TestEtcdUpdateNotScheduled(t *testing.T) { storage, _, _, fakeClient := newStorage(t) ctx := api.NewDefaultContext() fakeClient.TestIndex = true key, _ := storage.KeyFunc(ctx, "foo") key = etcdtest.AddPrefix(key) fakeClient.Set(key, runtime.EncodeOrDie(testapi.Default.Codec(), validNewPod()), 1) podIn := validChangedPod() _, _, err := storage.Update(ctx, podIn) if err != nil { t.Errorf("Unexpected error: %v", err) } response, err := fakeClient.Get(key, false, false) if err != nil { t.Fatalf("Unexpected error: %v", err) } podOut := &api.Pod{} testapi.Default.Codec().DecodeInto([]byte(response.Node.Value), podOut) if !api.Semantic.DeepEqual(podOut, podIn) { t.Errorf("objects differ: %v", util.ObjectDiff(podOut, podIn)) } }
func TestArrayOfRuntimeObject(t *testing.T) { s := runtime.NewScheme() s.AddKnownTypes("", &EmbeddedTest{}) s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{}) s.AddKnownTypes("", &ObjectTest{}) s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{}) internal := &ObjectTest{ Items: []runtime.Object{ &EmbeddedTest{ID: "foo"}, &EmbeddedTest{ID: "bar"}, // TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization &runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown","foo":"bar","kind":"OtherTest"}`)}, &ObjectTest{ Items: []runtime.Object{ &EmbeddedTest{ID: "baz"}, }, }, }, } wire, err := s.EncodeToVersion(internal, "v1test") if err != nil { t.Fatalf("unexpected error: %v", err) } t.Logf("Wire format is:\n%s\n", string(wire)) obj := &ObjectTestExternal{} if err := json.Unmarshal(wire, obj); err != nil { t.Fatalf("unexpected error: %v", err) } t.Logf("exact wire is: %s", string(obj.Items[0].RawJSON)) decoded, err := s.Decode(wire) if err != nil { t.Fatalf("unexpected error: %v", err) } list, err := runtime.ExtractList(decoded) if err != nil { t.Fatalf("unexpected error: %v", err) } if errs := runtime.DecodeList(list, s); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } list2, err := runtime.ExtractList(list[3]) if err != nil { t.Fatalf("unexpected error: %v", err) } if errs := runtime.DecodeList(list2, s); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } if err := runtime.SetList(list[3], list2); err != nil { t.Fatalf("unexpected error: %v", err) } internal.Items[2].(*runtime.Unknown).Kind = "OtherTest" internal.Items[2].(*runtime.Unknown).APIVersion = "unknown" if e, a := internal.Items, list; !reflect.DeepEqual(e, a) { t.Errorf("mismatched decoded: %s", util.ObjectDiff(e, a)) } }
func TestSchedulerForgetAssumedPodAfterDelete(t *testing.T) { eventBroadcaster := record.NewBroadcaster() defer eventBroadcaster.StartLogging(t.Logf).Stop() // Setup modeler so we control the contents of all 3 stores: assumed, // scheduled and queued scheduledPodStore := cache.NewStore(cache.MetaNamespaceKeyFunc) scheduledPodLister := &cache.StoreToPodLister{Store: scheduledPodStore} queuedPodStore := cache.NewFIFO(cache.MetaNamespaceKeyFunc) queuedPodLister := &cache.StoreToPodLister{Store: queuedPodStore} modeler := NewSimpleModeler(queuedPodLister, scheduledPodLister) // Create a fake clock used to timestamp entries and calculate ttl. Nothing // will expire till we flip to something older than the ttl, at which point // all entries inserted with fakeTime will expire. ttl := 30 * time.Second fakeTime := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) fakeClock := &util.FakeClock{Time: fakeTime} ttlPolicy := &cache.TTLPolicy{Ttl: ttl, Clock: fakeClock} assumedPodsStore := cache.NewFakeExpirationStore( cache.MetaNamespaceKeyFunc, nil, ttlPolicy, fakeClock) modeler.assumedPods = &cache.StoreToPodLister{Store: assumedPodsStore} // Port is the easiest way to cause a fit predicate failure podPort := 8080 firstPod := podWithPort("foo", "", podPort) // Create the scheduler config algo := NewGenericScheduler( map[string]algorithm.FitPredicate{"PodFitsHostPorts": predicates.PodFitsHostPorts}, []algorithm.PriorityConfig{}, modeler.PodLister(), rand.New(rand.NewSource(time.Now().UnixNano()))) var gotBinding *api.Binding c := &Config{ Modeler: modeler, NodeLister: algorithm.FakeNodeLister( api.NodeList{Items: []api.Node{{ObjectMeta: api.ObjectMeta{Name: "machine1"}}}}, ), Algorithm: algo, Binder: fakeBinder{func(b *api.Binding) error { scheduledPodStore.Add(podWithPort(b.Name, b.Target.Name, podPort)) gotBinding = b return nil }}, NextPod: func() *api.Pod { return queuedPodStore.Pop().(*api.Pod) }, Error: func(p *api.Pod, err error) { t.Errorf("Unexpected error when scheduling pod %+v: %v", p, err) }, Recorder: eventBroadcaster.NewRecorder(api.EventSource{Component: "scheduler"}), } // First scheduling pass should schedule the pod s := New(c) called := make(chan struct{}) events := eventBroadcaster.StartEventWatcher(func(e *api.Event) { if e, a := "Scheduled", e.Reason; e != a { t.Errorf("expected %v, got %v", e, a) } close(called) }) queuedPodStore.Add(firstPod) // queuedPodStore: [foo:8080] // scheduledPodStore: [] // assumedPods: [] s.scheduleOne() // queuedPodStore: [] // scheduledPodStore: [foo:8080] // assumedPods: [foo:8080] pod, exists, _ := scheduledPodStore.GetByKey("foo") if !exists { t.Errorf("Expected scheduled pod store to contain pod") } pod, exists, _ = queuedPodStore.GetByKey("foo") if exists { t.Errorf("Did not expect a queued pod, found %+v", pod) } pod, exists, _ = assumedPodsStore.GetByKey("foo") if !exists { t.Errorf("Assumed pod store should contain stale pod") } expectBind := &api.Binding{ ObjectMeta: api.ObjectMeta{Name: "foo"}, Target: api.ObjectReference{Kind: "Node", Name: "machine1"}, } if ex, ac := expectBind, gotBinding; !reflect.DeepEqual(ex, ac) { t.Errorf("Expected exact match on binding: %s", util.ObjectDiff(ex, ac)) } <-called events.Stop() scheduledPodStore.Delete(pod) _, exists, _ = assumedPodsStore.Get(pod) if !exists { t.Errorf("Expected pod %#v in assumed pod store", pod) } secondPod := podWithPort("bar", "", podPort) queuedPodStore.Add(secondPod) // queuedPodStore: [bar:8080] // scheduledPodStore: [] // assumedPods: [foo:8080] // Second scheduling pass will fail to schedule if the store hasn't expired // the deleted pod. This would normally happen with a timeout. //expirationPolicy.NeverExpire = util.NewStringSet() fakeClock.Time = fakeClock.Time.Add(ttl + 1) called = make(chan struct{}) events = eventBroadcaster.StartEventWatcher(func(e *api.Event) { if e, a := "Scheduled", e.Reason; e != a { t.Errorf("expected %v, got %v", e, a) } close(called) }) s.scheduleOne() expectBind = &api.Binding{ ObjectMeta: api.ObjectMeta{Name: "bar"}, Target: api.ObjectReference{Kind: "Node", Name: "machine1"}, } if ex, ac := expectBind, gotBinding; !reflect.DeepEqual(ex, ac) { t.Errorf("Expected exact match on binding: %s", util.ObjectDiff(ex, ac)) } <-called events.Stop() }
func TestScheduler(t *testing.T) { eventBroadcaster := record.NewBroadcaster() defer eventBroadcaster.StartLogging(t.Logf).Stop() errS := errors.New("scheduler") errB := errors.New("binder") table := []struct { injectBindError error sendPod *api.Pod algo algorithm.ScheduleAlgorithm expectErrorPod *api.Pod expectAssumedPod *api.Pod expectError error expectBind *api.Binding eventReason string }{ { sendPod: podWithID("foo", ""), algo: mockScheduler{"machine1", nil}, expectBind: &api.Binding{ObjectMeta: api.ObjectMeta{Name: "foo"}, Target: api.ObjectReference{Kind: "Node", Name: "machine1"}}, expectAssumedPod: podWithID("foo", "machine1"), eventReason: "Scheduled", }, { sendPod: podWithID("foo", ""), algo: mockScheduler{"machine1", errS}, expectError: errS, expectErrorPod: podWithID("foo", ""), eventReason: "FailedScheduling", }, { sendPod: podWithID("foo", ""), algo: mockScheduler{"machine1", nil}, expectBind: &api.Binding{ObjectMeta: api.ObjectMeta{Name: "foo"}, Target: api.ObjectReference{Kind: "Node", Name: "machine1"}}, injectBindError: errB, expectError: errB, expectErrorPod: podWithID("foo", ""), eventReason: "FailedScheduling", }, } for i, item := range table { var gotError error var gotPod *api.Pod var gotAssumedPod *api.Pod var gotBinding *api.Binding c := &Config{ Modeler: &FakeModeler{ AssumePodFunc: func(pod *api.Pod) { gotAssumedPod = pod }, }, NodeLister: algorithm.FakeNodeLister( api.NodeList{Items: []api.Node{{ObjectMeta: api.ObjectMeta{Name: "machine1"}}}}, ), Algorithm: item.algo, Binder: fakeBinder{func(b *api.Binding) error { gotBinding = b return item.injectBindError }}, Error: func(p *api.Pod, err error) { gotPod = p gotError = err }, NextPod: func() *api.Pod { return item.sendPod }, Recorder: eventBroadcaster.NewRecorder(api.EventSource{Component: "scheduler"}), } s := New(c) called := make(chan struct{}) events := eventBroadcaster.StartEventWatcher(func(e *api.Event) { if e, a := item.eventReason, e.Reason; e != a { t.Errorf("%v: expected %v, got %v", i, e, a) } close(called) }) s.scheduleOne() if e, a := item.expectAssumedPod, gotAssumedPod; !reflect.DeepEqual(e, a) { t.Errorf("%v: assumed pod: wanted %v, got %v", i, e, a) } if e, a := item.expectErrorPod, gotPod; !reflect.DeepEqual(e, a) { t.Errorf("%v: error pod: wanted %v, got %v", i, e, a) } if e, a := item.expectError, gotError; !reflect.DeepEqual(e, a) { t.Errorf("%v: error: wanted %v, got %v", i, e, a) } if e, a := item.expectBind, gotBinding; !reflect.DeepEqual(e, a) { t.Errorf("%v: error: %s", i, util.ObjectDiff(e, a)) } <-called events.Stop() } }
func TestEtcdUpdateStatus(t *testing.T) { storage, _, statusStorage, fakeClient := newStorage(t) ctx := api.NewDefaultContext() fakeClient.TestIndex = true key, _ := storage.KeyFunc(ctx, "foo") key = etcdtest.AddPrefix(key) podStart := api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "foo", Namespace: api.NamespaceDefault, }, Spec: api.PodSpec{ NodeName: "machine", Containers: []api.Container{ { Image: "foo:v1", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(), }, }, }, } fakeClient.Set(key, runtime.EncodeOrDie(testapi.Default.Codec(), &podStart), 0) podIn := api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "foo", ResourceVersion: "1", Labels: map[string]string{ "foo": "bar", }, }, Spec: api.PodSpec{ NodeName: "machine", Containers: []api.Container{ { Image: "foo:v2", ImagePullPolicy: api.PullIfNotPresent, TerminationMessagePath: api.TerminationMessagePathDefault, }, }, }, Status: api.PodStatus{ Phase: api.PodRunning, PodIP: "127.0.0.1", Message: "is now scheduled", }, } expected := podStart expected.ResourceVersion = "2" grace := int64(30) expected.Spec.TerminationGracePeriodSeconds = &grace expected.Spec.RestartPolicy = api.RestartPolicyAlways expected.Spec.DNSPolicy = api.DNSClusterFirst expected.Spec.Containers[0].ImagePullPolicy = api.PullIfNotPresent expected.Spec.Containers[0].TerminationMessagePath = api.TerminationMessagePathDefault expected.Labels = podIn.Labels expected.Status = podIn.Status _, _, err := statusStorage.Update(ctx, &podIn) if err != nil { t.Fatalf("Unexpected error: %v", err) } podOut, err := storage.Get(ctx, "foo") if err != nil { t.Errorf("unexpected error: %v", err) } if !api.Semantic.DeepEqual(&expected, podOut) { t.Errorf("unexpected object: %s", util.ObjectDiff(&expected, podOut)) } }
func TestEtcdWatch(t *testing.T) { testContext := api.WithNamespace(api.NewContext(), "test") noNamespaceContext := api.NewContext() table := map[string]struct { generic.Matcher context api.Context }{ "single": { Matcher: setMatcher{sets.NewString("foo")}, }, "multi": { Matcher: setMatcher{sets.NewString("foo", "bar")}, }, "singleNoNamespace": { Matcher: setMatcher{sets.NewString("foo")}, context: noNamespaceContext, }, } for name, m := range table { ctx := testContext if m.context != nil { ctx = m.context } podA := &api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "foo", Namespace: "test", ResourceVersion: "1", }, Spec: api.PodSpec{NodeName: "machine"}, } respWithPodA := &etcd.Response{ Node: &etcd.Node{ Key: "/registry/pods/test/foo", Value: runtime.EncodeOrDie(testapi.Default.Codec(), podA), ModifiedIndex: 1, CreatedIndex: 1, }, Action: "create", } fakeClient, registry := NewTestGenericEtcdRegistry(t) wi, err := registry.WatchPredicate(ctx, m, "1") if err != nil { t.Errorf("%v: unexpected error: %v", name, err) continue } fakeClient.WaitForWatchCompletion() go func() { fakeClient.WatchResponse <- respWithPodA }() got, open := <-wi.ResultChan() if !open { t.Errorf("%v: unexpected channel close", name) continue } if e, a := podA, got.Object; !api.Semantic.DeepDerivative(e, a) { t.Errorf("%v: difference: %s", name, util.ObjectDiff(e, a)) } } }
func TestEtcdCreate(t *testing.T) { podA := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test"}, Spec: api.PodSpec{NodeName: "machine"}, } podB := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test"}, Spec: api.PodSpec{NodeName: "machine2"}, } nodeWithPodA := tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: runtime.EncodeOrDie(testapi.Default.Codec(), podA), ModifiedIndex: 1, CreatedIndex: 1, }, }, E: nil, } emptyNode := tools.EtcdResponseWithError{ R: &etcd.Response{}, E: tools.EtcdErrorNotFound, } testContext := api.WithNamespace(api.NewContext(), "test") table := map[string]struct { existing tools.EtcdResponseWithError expect tools.EtcdResponseWithError toCreate runtime.Object objOK func(obj runtime.Object) bool errOK func(error) bool }{ "normal": { existing: emptyNode, toCreate: podA, objOK: hasCreated(t, podA), errOK: func(err error) bool { return err == nil }, }, "preExisting": { existing: nodeWithPodA, expect: nodeWithPodA, toCreate: podB, errOK: errors.IsAlreadyExists, }, } for name, item := range table { fakeClient, registry := NewTestGenericEtcdRegistry(t) path := etcdtest.AddPrefix("pods/foo") fakeClient.Data[path] = item.existing obj, err := registry.Create(testContext, item.toCreate) if !item.errOK(err) { t.Errorf("%v: unexpected error: %v", name, err) } actual := fakeClient.Data[path] if item.objOK != nil { if !item.objOK(obj) { t.Errorf("%v: unexpected returned: %v", name, obj) } actualObj, err := api.Scheme.Decode([]byte(actual.R.Node.Value)) if err != nil { t.Errorf("unable to decode stored value for %#v", actual) continue } if !item.objOK(actualObj) { t.Errorf("%v: unexpected response: %v", name, actual) } } else { if e, a := item.expect, actual; !api.Semantic.DeepDerivative(e, a) { t.Errorf("%v:\n%s", name, util.ObjectDiff(e, a)) } } } }
func TestEtcdUpdate(t *testing.T) { podA := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test"}, Spec: api.PodSpec{NodeName: "machine"}, } podB := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "1"}, Spec: api.PodSpec{NodeName: "machine2"}, } podAWithResourceVersion := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "3"}, Spec: api.PodSpec{NodeName: "machine"}, } nodeWithPodA := tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: runtime.EncodeOrDie(testapi.Default.Codec(), podA), ModifiedIndex: 1, CreatedIndex: 1, }, }, E: nil, } newerNodeWithPodA := tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: runtime.EncodeOrDie(testapi.Default.Codec(), podA), ModifiedIndex: 2, CreatedIndex: 1, }, }, E: nil, } nodeWithPodB := tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: runtime.EncodeOrDie(testapi.Default.Codec(), podB), ModifiedIndex: 1, CreatedIndex: 1, }, }, E: nil, } nodeWithPodAWithResourceVersion := tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Value: runtime.EncodeOrDie(testapi.Default.Codec(), podAWithResourceVersion), ModifiedIndex: 3, CreatedIndex: 1, }, }, E: nil, } emptyNode := tools.EtcdResponseWithError{ R: &etcd.Response{}, E: tools.EtcdErrorNotFound, } testContext := api.WithNamespace(api.NewContext(), "test") table := map[string]struct { existing tools.EtcdResponseWithError expect tools.EtcdResponseWithError toUpdate runtime.Object allowCreate bool allowUnconditionalUpdate bool objOK func(obj runtime.Object) bool errOK func(error) bool }{ "normal": { existing: nodeWithPodA, expect: nodeWithPodB, toUpdate: podB, errOK: func(err error) bool { return err == nil }, }, "notExisting": { existing: emptyNode, expect: emptyNode, toUpdate: podA, errOK: func(err error) bool { return errors.IsNotFound(err) }, }, "createIfNotFound": { existing: emptyNode, toUpdate: podA, allowCreate: true, objOK: hasCreated(t, podA), errOK: func(err error) bool { return err == nil }, }, "outOfDate": { existing: newerNodeWithPodA, expect: newerNodeWithPodA, toUpdate: podB, errOK: func(err error) bool { return errors.IsConflict(err) }, }, "unconditionalUpdate": { existing: nodeWithPodAWithResourceVersion, allowUnconditionalUpdate: true, toUpdate: podA, objOK: func(obj runtime.Object) bool { return true }, errOK: func(err error) bool { return err == nil }, }, } for name, item := range table { fakeClient, registry := NewTestGenericEtcdRegistry(t) registry.UpdateStrategy.(*testRESTStrategy).allowCreateOnUpdate = item.allowCreate registry.UpdateStrategy.(*testRESTStrategy).allowUnconditionalUpdate = item.allowUnconditionalUpdate path := etcdtest.AddPrefix("pods/foo") fakeClient.Data[path] = item.existing obj, _, err := registry.Update(testContext, item.toUpdate) if !item.errOK(err) { t.Errorf("%v: unexpected error: %v", name, err) } actual := fakeClient.Data[path] if item.objOK != nil { if !item.objOK(obj) { t.Errorf("%v: unexpected returned: %#v", name, obj) } actualObj, err := api.Scheme.Decode([]byte(actual.R.Node.Value)) if err != nil { t.Errorf("unable to decode stored value for %#v", actual) continue } if !item.objOK(actualObj) { t.Errorf("%v: unexpected response: %#v", name, actual) } } else { if e, a := item.expect, actual; !api.Semantic.DeepDerivative(e, a) { t.Errorf("%v:\n%s", name, util.ObjectDiff(e, a)) } } } }