// TestValidNamespace validates that namespace rules are enforced on a resource prior to create or update func TestValidNamespace(t *testing.T) { ctx := api.NewDefaultContext() namespace, _ := api.NamespaceFrom(ctx) resource := api.ReplicationController{} if !api.ValidNamespace(ctx, &resource.ObjectMeta) { t.Errorf("expected success") } if namespace != resource.Namespace { t.Errorf("expected resource to have the default namespace assigned during validation") } resource = api.ReplicationController{ObjectMeta: api.ObjectMeta{Namespace: "other"}} if api.ValidNamespace(ctx, &resource.ObjectMeta) { t.Errorf("Expected error that resource and context errors do not match because resource has different namespace") } ctx = api.NewContext() if api.ValidNamespace(ctx, &resource.ObjectMeta) { t.Errorf("Expected error that resource and context errors do not match since context has no namespace") } ctx = api.NewContext() ns := api.NamespaceValue(ctx) if ns != "" { t.Errorf("Expected the empty string") } }
func TestStoreWatch(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", }, Spec: api.PodSpec{NodeName: "machine"}, } server, registry := NewTestGenericStoreRegistry(t) wi, err := registry.WatchPredicate(ctx, m, "0") if err != nil { t.Errorf("%v: unexpected error: %v", name, err) } else { obj, err := registry.Create(testContext, podA) if err != nil { got, open := <-wi.ResultChan() if !open { t.Errorf("%v: unexpected channel close", name) } else { if e, a := obj, got.Object; !reflect.DeepEqual(e, a) { t.Errorf("Expected %#v, got %#v", e, a) } } } wi.Stop() } server.Terminate(t) } }
func TestStoreBasicExport(t *testing.T) { podA := api.Pod{ ObjectMeta: api.ObjectMeta{ Namespace: "test", Name: "foo", Labels: map[string]string{}, }, Spec: api.PodSpec{NodeName: "machine"}, Status: api.PodStatus{HostIP: "1.2.3.4"}, } server, registry := NewTestGenericStoreRegistry(t) defer server.Terminate(t) testContext := api.WithNamespace(api.NewContext(), "test") registry.UpdateStrategy.(*testRESTStrategy).allowCreateOnUpdate = true if !updateAndVerify(t, testContext, registry, &podA) { t.Errorf("Unexpected error updating podA") } obj, err := registry.Export(testContext, podA.Name, unversioned.ExportOptions{}) if err != nil { t.Errorf("unexpected error: %v", err) } exportedPod := obj.(*api.Pod) if exportedPod.Labels["prepare_create"] != "true" { t.Errorf("expected: prepare_create->true, found: %s", exportedPod.Labels["prepare_create"]) } delete(exportedPod.Labels, "prepare_create") exportObjectMeta(&podA.ObjectMeta, false) podA.Spec = exportedPod.Spec if !reflect.DeepEqual(&podA, exportedPod) { t.Errorf("expected:\n%v\nsaw:\n%v\n", &podA, exportedPod) } }
func TestStoreDelete(t *testing.T) { podA := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo"}, Spec: api.PodSpec{NodeName: "machine"}, } testContext := api.WithNamespace(api.NewContext(), "test") server, registry := NewTestGenericStoreRegistry(t) defer server.Terminate(t) // test failure condition _, err := registry.Delete(testContext, podA.Name, nil) if !errors.IsNotFound(err) { t.Errorf("Unexpected error: %v", err) } // create pod _, err = registry.Create(testContext, podA) if err != nil { t.Errorf("Unexpected error: %v", err) } // delete object _, err = registry.Delete(testContext, podA.Name, nil) if err != nil { t.Errorf("Unexpected error: %v", err) } // try to get a item which should be deleted _, err = registry.Get(testContext, podA.Name) if !errors.IsNotFound(err) { t.Errorf("Unexpected error: %v", err) } }
func TestStoreDeleteCollection(t *testing.T) { podA := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} podB := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "bar"}} testContext := api.WithNamespace(api.NewContext(), "test") server, registry := NewTestGenericStoreRegistry(t) defer server.Terminate(t) if _, err := registry.Create(testContext, podA); err != nil { t.Errorf("Unexpected error: %v", err) } if _, err := registry.Create(testContext, podB); err != nil { t.Errorf("Unexpected error: %v", err) } // Delete all pods. deleted, err := registry.DeleteCollection(testContext, nil, &api.ListOptions{}) if err != nil { t.Fatalf("Unexpected error: %v", err) } deletedPods := deleted.(*api.PodList) if len(deletedPods.Items) != 2 { t.Errorf("Unexpected number of pods deleted: %d, expected: 2", len(deletedPods.Items)) } if _, err := registry.Get(testContext, podA.Name); !errors.IsNotFound(err) { t.Errorf("Unexpected error: %v", err) } if _, err := registry.Get(testContext, podB.Name); !errors.IsNotFound(err) { t.Errorf("Unexpected error: %v", err) } }
func TestUpdate(t *testing.T) { storage, server, si := newStorage(t) defer server.Terminate(t) ctx := api.WithNamespace(api.NewContext(), "test") key := etcdtest.AddPrefix("/controllers/test/foo") if err := si.Create(ctx, key, &validController, nil, 0); err != nil { t.Fatalf("unexpected error: %v", err) } replicas := int32(12) update := extensions.Scale{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test"}, Spec: extensions.ScaleSpec{ Replicas: replicas, }, } if _, _, err := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { t.Fatalf("unexpected error: %v", err) } obj, err := storage.Get(ctx, "foo") if err != nil { t.Fatalf("unexpected error: %v", err) } updated := obj.(*extensions.Scale) if updated.Spec.Replicas != replicas { t.Errorf("wrong replicas count expected: %d got: %d", replicas, updated.Spec.Replicas) } }
func TestCreateSetsFields(t *testing.T) { storage, server := newStorage(t) defer server.Terminate(t) namespace := validNewNamespace() ctx := api.NewContext() _, err := storage.Create(ctx, namespace) if err != nil { t.Fatalf("unexpected error: %v", err) } object, err := storage.Get(ctx, "foo") if err != nil { t.Errorf("unexpected error: %v", err) } actual := object.(*api.Namespace) if actual.Name != namespace.Name { t.Errorf("unexpected namespace: %#v", actual) } if len(actual.UID) == 0 { t.Errorf("expected namespace UID to be set: %#v", actual) } if actual.Status.Phase != api.NamespaceActive { t.Errorf("expected namespace phase to be set to active, but %v", actual.Status.Phase) } }
func TestStatusUpdate(t *testing.T) { storage, server := newStorage(t) defer server.Terminate(t) ctx := api.WithNamespace(api.NewContext(), api.NamespaceDefault) key := etcdtest.AddPrefix("/replicasets/" + api.NamespaceDefault + "/foo") if err := storage.ReplicaSet.Storage.Create(ctx, key, &validReplicaSet, nil, 0); err != nil { t.Fatalf("unexpected error: %v", err) } update := extensions.ReplicaSet{ ObjectMeta: validReplicaSet.ObjectMeta, Spec: extensions.ReplicaSetSpec{ Replicas: defaultReplicas, }, Status: extensions.ReplicaSetStatus{ Replicas: defaultReplicas, }, } if _, _, err := storage.Status.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { t.Fatalf("unexpected error: %v", err) } obj, err := storage.ReplicaSet.Get(ctx, "foo") if err != nil { t.Fatalf("unexpected error: %v", err) } rs := obj.(*extensions.ReplicaSet) if rs.Spec.Replicas != 7 { t.Errorf("we expected .spec.replicas to not be updated but it was updated to %v", rs.Spec.Replicas) } if rs.Status.Replicas != defaultReplicas { t.Errorf("we expected .status.replicas to be updated to %d but it was %v", defaultReplicas, rs.Status.Replicas) } }
func TestUpdateStatus(t *testing.T) { storage, statusStorage, server := newStorage(t) defer server.Terminate(t) ctx := api.NewContext() key, _ := storage.KeyFunc(ctx, "foo") key = etcdtest.AddPrefix(key) pvStart := validNewPersistentVolume("foo") err := storage.Storage.Create(ctx, key, pvStart, nil, 0) if err != nil { t.Errorf("unexpected error: %v", err) } pvIn := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "foo", }, Status: api.PersistentVolumeStatus{ Phase: api.VolumeBound, }, } _, _, err = statusStorage.Update(ctx, pvIn.Name, rest.DefaultUpdatedObjectInfo(pvIn, api.Scheme)) if err != nil { t.Fatalf("Unexpected error: %v", err) } obj, err := storage.Get(ctx, "foo") if err != nil { t.Errorf("unexpected error: %v", err) } pvOut := obj.(*api.PersistentVolume) // only compare the relevant change b/c metadata will differ if !api.Semantic.DeepEqual(pvIn.Status, pvOut.Status) { t.Errorf("unexpected object: %s", diff.ObjectDiff(pvIn.Status, pvOut.Status)) } }
// testGetDifferentNamespace ensures same-name objects in different namespaces do not clash func (t *Tester) testGetDifferentNamespace(obj runtime.Object) { if t.clusterScope { t.Fatalf("the test does not work in in cluster-scope") } objMeta := t.getObjectMetaOrFail(obj) objMeta.Name = t.namer(5) ctx1 := api.WithNamespace(api.NewContext(), "bar3") objMeta.Namespace = api.NamespaceValue(ctx1) _, err := t.storage.(rest.Creater).Create(ctx1, obj) if err != nil { t.Errorf("unexpected error: %v", err) } ctx2 := api.WithNamespace(api.NewContext(), "bar4") objMeta.Namespace = api.NamespaceValue(ctx2) _, err = t.storage.(rest.Creater).Create(ctx2, obj) if err != nil { t.Errorf("unexpected error: %v", err) } got1, err := t.storage.(rest.Getter).Get(ctx1, objMeta.Name) if err != nil { t.Errorf("unexpected error: %v", err) } got1Meta := t.getObjectMetaOrFail(got1) if got1Meta.Name != objMeta.Name { t.Errorf("unexpected name of object: %#v, expected: %s", got1, objMeta.Name) } if got1Meta.Namespace != api.NamespaceValue(ctx1) { t.Errorf("unexpected namespace of object: %#v, expected: %s", got1, api.NamespaceValue(ctx1)) } got2, err := t.storage.(rest.Getter).Get(ctx2, objMeta.Name) if err != nil { t.Errorf("unexpected error: %v", err) } got2Meta := t.getObjectMetaOrFail(got2) if got2Meta.Name != objMeta.Name { t.Errorf("unexpected name of object: %#v, expected: %s", got2, objMeta.Name) } if got2Meta.Namespace != api.NamespaceValue(ctx2) { t.Errorf("unexpected namespace of object: %#v, expected: %s", got2, api.NamespaceValue(ctx2)) } }
// createReplicaSet is a helper function that returns a ReplicaSet with the updated resource version. func createReplicaSet(storage *REST, rs extensions.ReplicaSet, t *testing.T) (extensions.ReplicaSet, error) { ctx := api.WithNamespace(api.NewContext(), rs.Namespace) obj, err := storage.Create(ctx, &rs) if err != nil { t.Errorf("Failed to create ReplicaSet, %v", err) } newRS := obj.(*extensions.ReplicaSet) return *newRS, nil }
func (t *Tester) testGetMimatchedNamespace(obj runtime.Object) { ctx1 := api.WithNamespace(api.NewContext(), "bar1") ctx2 := api.WithNamespace(api.NewContext(), "bar2") objMeta := t.getObjectMetaOrFail(obj) objMeta.Name = t.namer(4) objMeta.Namespace = api.NamespaceValue(ctx1) _, err := t.storage.(rest.Creater).Create(ctx1, obj) if err != nil { t.Errorf("unexpected error: %v", err) } _, err = t.storage.(rest.Getter).Get(ctx2, t.namer(4)) if t.clusterScope { if err != nil { t.Errorf("unexpected error: %v", err) } } else { if !errors.IsNotFound(err) { t.Errorf("unexpected error returned: %#v", err) } } }
func TestStoreUpdate(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"}, } podAWithResourceVersion := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "7"}, Spec: api.PodSpec{NodeName: "machine"}, } testContext := api.WithNamespace(api.NewContext(), "test") server, registry := NewTestGenericStoreRegistry(t) defer server.Terminate(t) // Test1 try to update a non-existing node _, _, err := registry.Update(testContext, podA.Name, rest.DefaultUpdatedObjectInfo(podA, api.Scheme)) if !errors.IsNotFound(err) { t.Errorf("Unexpected error: %v", err) } // Test2 createIfNotFound and verify registry.UpdateStrategy.(*testRESTStrategy).allowCreateOnUpdate = true if !updateAndVerify(t, testContext, registry, podA) { t.Errorf("Unexpected error updating podA") } registry.UpdateStrategy.(*testRESTStrategy).allowCreateOnUpdate = false // Test3 outofDate _, _, err = registry.Update(testContext, podAWithResourceVersion.Name, rest.DefaultUpdatedObjectInfo(podAWithResourceVersion, api.Scheme)) if !errors.IsConflict(err) { t.Errorf("Unexpected error updating podAWithResourceVersion: %v", err) } // Test4 normal update and verify if !updateAndVerify(t, testContext, registry, podB) { t.Errorf("Unexpected error updating podB") } // Test5 unconditional update // NOTE: The logic for unconditional updates doesn't make sense to me, and imho should be removed. // doUnconditionalUpdate := resourceVersion == 0 && e.UpdateStrategy.AllowUnconditionalUpdate() // ^^ That condition can *never be true due to the creation of root objects. // // registry.UpdateStrategy.(*testRESTStrategy).allowUnconditionalUpdate = true // updateAndVerify(t, testContext, registry, podAWithResourceVersion) }
func (t *Tester) testCreateIgnoresContextNamespace(valid runtime.Object) { // Ignore non-empty namespace in context ctx := api.WithNamespace(api.NewContext(), "not-default2") // Ideally, we'd get an error back here, but at least verify the namespace wasn't persisted created, err := t.storage.(rest.Creater).Create(ctx, copyOrDie(valid)) if err != nil { t.Fatalf("Unexpected error: %v", err) } defer t.delete(ctx, created) createdObjectMeta := t.getObjectMetaOrFail(created) if createdObjectMeta.Namespace != api.NamespaceNone { t.Errorf("Expected empty namespace on created object, got '%v'", createdObjectMeta.Namespace) } }
// TestNamespaceContext validates that a namespace can be get/set on a context object func TestNamespaceContext(t *testing.T) { ctx := api.NewDefaultContext() result, ok := api.NamespaceFrom(ctx) if !ok { t.Errorf("Error getting namespace") } if api.NamespaceDefault != result { t.Errorf("Expected: %v, Actual: %v", api.NamespaceDefault, result) } ctx = api.NewContext() result, ok = api.NamespaceFrom(ctx) if ok { t.Errorf("Should not be ok because there is no namespace on the context") } }
func TestGet(t *testing.T) { storage, server, si := newStorage(t) defer server.Terminate(t) ctx := api.WithNamespace(api.NewContext(), "test") key := etcdtest.AddPrefix("/controllers/test/foo") if err := si.Create(ctx, key, &validController, nil, 0); err != nil { t.Fatalf("unexpected error: %v", err) } obj, err := storage.Get(ctx, "foo") if err != nil { t.Fatalf("unexpected error: %v", err) } scale := obj.(*extensions.Scale) if scale.Spec.Replicas != validReplicas { t.Errorf("wrong replicas count expected: %d got: %d", validReplicas, scale.Spec.Replicas) } }
func TestScaleUpdate(t *testing.T) { storage, server := newStorage(t) defer server.Terminate(t) name := "foo" var rs extensions.ReplicaSet ctx := api.WithNamespace(api.NewContext(), api.NamespaceDefault) key := etcdtest.AddPrefix("/replicasets/" + api.NamespaceDefault + "/" + name) if err := storage.ReplicaSet.Storage.Create(ctx, key, &validReplicaSet, &rs, 0); err != nil { t.Fatalf("error setting new replica set (key: %s) %v: %v", key, validReplicaSet, err) } replicas := 12 update := extensions.Scale{ ObjectMeta: api.ObjectMeta{ Name: name, Namespace: api.NamespaceDefault, }, Spec: extensions.ScaleSpec{ Replicas: int32(replicas), }, } if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { t.Fatalf("error updating scale %v: %v", update, err) } obj, err := storage.Scale.Get(ctx, name) if err != nil { t.Fatalf("error fetching scale for %s: %v", name, err) } scale := obj.(*extensions.Scale) if scale.Spec.Replicas != int32(replicas) { t.Errorf("wrong replicas count expected: %d got: %d", replicas, scale.Spec.Replicas) } update.ResourceVersion = rs.ResourceVersion update.Spec.Replicas = 15 if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil && !errors.IsConflict(err) { t.Fatalf("unexpected error, expecting an update conflict but got %v", err) } }
func TestStoreGet(t *testing.T) { podA := &api.Pod{ ObjectMeta: api.ObjectMeta{Namespace: "test", Name: "foo"}, Spec: api.PodSpec{NodeName: "machine"}, } testContext := api.WithNamespace(api.NewContext(), "test") server, registry := NewTestGenericStoreRegistry(t) defer server.Terminate(t) _, err := registry.Get(testContext, podA.Name) if !errors.IsNotFound(err) { t.Errorf("Unexpected error: %v", err) } registry.UpdateStrategy.(*testRESTStrategy).allowCreateOnUpdate = true if !updateAndVerify(t, testContext, registry, podA) { t.Errorf("Unexpected error updating podA") } }
func TestStoreDeleteCollectionNotFound(t *testing.T) { server, registry := NewTestGenericStoreRegistry(t) defer server.Terminate(t) testContext := api.WithNamespace(api.NewContext(), "test") podA := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} podB := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "bar"}} for i := 0; i < 10; i++ { // Setup if _, err := registry.Create(testContext, podA); err != nil { t.Errorf("Unexpected error: %v", err) } if _, err := registry.Create(testContext, podB); err != nil { t.Errorf("Unexpected error: %v", err) } // Kick off multiple delete collection calls to test notfound behavior wg := &sync.WaitGroup{} for j := 0; j < 2; j++ { wg.Add(1) go func() { defer wg.Done() _, err := registry.DeleteCollection(testContext, nil, &api.ListOptions{}) if err != nil { t.Fatalf("Unexpected error: %v", err) } }() } wg.Wait() if _, err := registry.Get(testContext, podA.Name); !errors.IsNotFound(err) { t.Errorf("Unexpected error: %v", err) } if _, err := registry.Get(testContext, podB.Name); !errors.IsNotFound(err) { t.Errorf("Unexpected error: %v", err) } } }
func TestScaleGet(t *testing.T) { storage, server := newStorage(t) defer server.Terminate(t) name := "foo" var rs extensions.ReplicaSet ctx := api.WithNamespace(api.NewContext(), api.NamespaceDefault) key := etcdtest.AddPrefix("/replicasets/" + api.NamespaceDefault + "/" + name) if err := storage.ReplicaSet.Storage.Create(ctx, key, &validReplicaSet, &rs, 0); err != nil { t.Fatalf("error setting new replica set (key: %s) %v: %v", key, validReplicaSet, err) } want := &extensions.Scale{ ObjectMeta: api.ObjectMeta{ Name: name, Namespace: api.NamespaceDefault, UID: rs.UID, ResourceVersion: rs.ResourceVersion, CreationTimestamp: rs.CreationTimestamp, }, Spec: extensions.ScaleSpec{ Replicas: validReplicaSet.Spec.Replicas, }, Status: extensions.ScaleStatus{ Replicas: validReplicaSet.Status.Replicas, Selector: validReplicaSet.Spec.Selector, }, } obj, err := storage.Scale.Get(ctx, name) got := obj.(*extensions.Scale) if err != nil { t.Fatalf("error fetching scale for %s: %v", name, err) } if !api.Semantic.DeepEqual(got, want) { t.Errorf("unexpected scale: %s", diff.ObjectDiff(got, want)) } }
func TestDeleteNamespaceWithCompleteFinalizers(t *testing.T) { storage, server := newStorage(t) defer server.Terminate(t) key := etcdtest.AddPrefix("namespaces/foo") ctx := api.NewContext() now := unversioned.Now() namespace := &api.Namespace{ ObjectMeta: api.ObjectMeta{ Name: "foo", DeletionTimestamp: &now, }, Spec: api.NamespaceSpec{ Finalizers: []api.FinalizerName{}, }, Status: api.NamespaceStatus{Phase: api.NamespaceActive}, } if err := storage.Storage.Create(ctx, key, namespace, nil, 0); err != nil { t.Fatalf("unexpected error: %v", err) } if _, err := storage.Delete(ctx, "foo", nil); err != nil { t.Errorf("unexpected error: %v", err) } }
// Test whether objects deleted with DeleteCollection are correctly delivered // to watchers. func TestStoreDeleteCollectionWithWatch(t *testing.T) { podA := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} testContext := api.WithNamespace(api.NewContext(), "test") server, registry := NewTestGenericStoreRegistry(t) defer server.Terminate(t) objCreated, err := registry.Create(testContext, podA) if err != nil { t.Fatalf("Unexpected error: %v", err) } podCreated := objCreated.(*api.Pod) watcher, err := registry.WatchPredicate(testContext, setMatcher{sets.NewString("foo")}, podCreated.ResourceVersion) if err != nil { t.Fatalf("Unexpected error: %v", err) } defer watcher.Stop() if _, err := registry.DeleteCollection(testContext, nil, &api.ListOptions{}); err != nil { t.Fatalf("Unexpected error: %v", err) } got, open := <-watcher.ResultChan() if !open { t.Errorf("Unexpected channel close") } else { if got.Type != "DELETED" { t.Errorf("Unexpected event type: %s", got.Type) } gotObject := got.Object.(*api.Pod) gotObject.ResourceVersion = podCreated.ResourceVersion if e, a := podCreated, gotObject; !reflect.DeepEqual(e, a) { t.Errorf("Expected: %#v, got: %#v", e, a) } } }
func TestStoreCreate(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"}, } testContext := api.WithNamespace(api.NewContext(), "test") server, registry := NewTestGenericStoreRegistry(t) defer server.Terminate(t) // create the object objA, err := registry.Create(testContext, podA) if err != nil { t.Errorf("Unexpected error: %v", err) } // get the object checkobj, err := registry.Get(testContext, podA.Name) if err != nil { t.Errorf("Unexpected error: %v", err) } // verify objects are equal if e, a := objA, checkobj; !reflect.DeepEqual(e, a) { t.Errorf("Expected %#v, got %#v", e, a) } // now try to create the second pod _, err = registry.Create(testContext, podB) if !errors.IsAlreadyExists(err) { t.Errorf("Unexpected error: %v", err) } }
// TestContext returns a namespaced context that will be used when making storage calls. // Namespace is determined by TestNamespace() func (t *Tester) TestContext() api.Context { if t.clusterScope { return api.NewContext() } return api.WithNamespace(api.NewContext(), t.TestNamespace()) }
func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { proxyHandlerTraceID := rand.Int63() var verb string var apiResource string var httpCode int reqStart := time.Now() defer metrics.Monitor(&verb, &apiResource, net.GetHTTPClient(req), w.Header().Get("Content-Type"), httpCode, reqStart) requestInfo, err := r.requestInfoResolver.GetRequestInfo(req) if err != nil || !requestInfo.IsResourceRequest { notFound(w, req) httpCode = http.StatusNotFound return } verb = requestInfo.Verb namespace, resource, parts := requestInfo.Namespace, requestInfo.Resource, requestInfo.Parts ctx, ok := r.context.Get(req) if !ok { ctx = api.NewContext() } ctx = api.WithNamespace(ctx, namespace) if len(parts) < 2 { notFound(w, req) httpCode = http.StatusNotFound return } id := parts[1] remainder := "" if len(parts) > 2 { proxyParts := parts[2:] remainder = strings.Join(proxyParts, "/") if strings.HasSuffix(req.URL.Path, "/") { // The original path had a trailing slash, which has been stripped // by KindAndNamespace(). We should add it back because some // servers (like etcd) require it. remainder = remainder + "/" } } storage, ok := r.storage[resource] if !ok { httplog.LogOf(req, w).Addf("'%v' has no storage object", resource) notFound(w, req) httpCode = http.StatusNotFound return } apiResource = resource gv := unversioned.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion} redirector, ok := storage.(rest.Redirector) if !ok { httplog.LogOf(req, w).Addf("'%v' is not a redirector", resource) httpCode = errorNegotiated(errors.NewMethodNotSupported(api.Resource(resource), "proxy"), r.serializer, gv, w, req) return } location, roundTripper, err := redirector.ResourceLocation(ctx, id) if err != nil { httplog.LogOf(req, w).Addf("Error getting ResourceLocation: %v", err) httpCode = errorNegotiated(err, r.serializer, gv, w, req) return } if location == nil { httplog.LogOf(req, w).Addf("ResourceLocation for %v returned nil", id) notFound(w, req) httpCode = http.StatusNotFound return } if roundTripper != nil { glog.V(5).Infof("[%x: %v] using transport %T...", proxyHandlerTraceID, req.URL, roundTripper) } // Default to http if location.Scheme == "" { location.Scheme = "http" } // Add the subpath if len(remainder) > 0 { location.Path = singleJoiningSlash(location.Path, remainder) } // Start with anything returned from the storage, and add the original request's parameters values := location.Query() for k, vs := range req.URL.Query() { for _, v := range vs { values.Add(k, v) } } location.RawQuery = values.Encode() newReq, err := http.NewRequest(req.Method, location.String(), req.Body) if err != nil { httpCode = errorNegotiated(err, r.serializer, gv, w, req) return } httpCode = http.StatusOK newReq.Header = req.Header newReq.ContentLength = req.ContentLength // Copy the TransferEncoding is for future-proofing. Currently Go only supports "chunked" and // it can determine the TransferEncoding based on ContentLength and the Body. newReq.TransferEncoding = req.TransferEncoding // TODO convert this entire proxy to an UpgradeAwareProxy similar to // https://github.com/openshift/origin/blob/master/pkg/util/httpproxy/upgradeawareproxy.go. // That proxy needs to be modified to support multiple backends, not just 1. if r.tryUpgrade(w, req, newReq, location, roundTripper, gv) { return } // Redirect requests of the form "/{resource}/{name}" to "/{resource}/{name}/" // This is essentially a hack for http://issue.k8s.io/4958. // Note: Keep this code after tryUpgrade to not break that flow. if len(parts) == 2 && !strings.HasSuffix(req.URL.Path, "/") { var queryPart string if len(req.URL.RawQuery) > 0 { queryPart = "?" + req.URL.RawQuery } w.Header().Set("Location", req.URL.Path+"/"+queryPart) w.WriteHeader(http.StatusMovedPermanently) return } start := time.Now() glog.V(4).Infof("[%x] Beginning proxy %s...", proxyHandlerTraceID, req.URL) defer func() { glog.V(4).Infof("[%x] Proxy %v finished %v.", proxyHandlerTraceID, req.URL, time.Now().Sub(start)) }() proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: location.Scheme, Host: location.Host}) alreadyRewriting := false if roundTripper != nil { _, alreadyRewriting = roundTripper.(*proxyutil.Transport) glog.V(5).Infof("[%x] Not making a reriting transport for proxy %s...", proxyHandlerTraceID, req.URL) } if !alreadyRewriting { glog.V(5).Infof("[%x] making a transport for proxy %s...", proxyHandlerTraceID, req.URL) prepend := path.Join(r.prefix, resource, id) if len(namespace) > 0 { prepend = path.Join(r.prefix, "namespaces", namespace, resource, id) } pTransport := &proxyutil.Transport{ Scheme: req.URL.Scheme, Host: req.URL.Host, PathPrepend: prepend, RoundTripper: roundTripper, } roundTripper = pTransport } proxy.Transport = roundTripper proxy.FlushInterval = 200 * time.Millisecond proxy.ServeHTTP(w, newReq) }
func TestAppliesTo(t *testing.T) { tests := []struct { subjects []rbac.Subject ctx api.Context appliesTo bool testCase string }{ { subjects: []rbac.Subject{ {Kind: rbac.UserKind, Name: "foobar"}, }, ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}), appliesTo: true, testCase: "single subject that matches username", }, { subjects: []rbac.Subject{ {Kind: rbac.UserKind, Name: "barfoo"}, {Kind: rbac.UserKind, Name: "foobar"}, }, ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}), appliesTo: true, testCase: "multiple subjects, one that matches username", }, { subjects: []rbac.Subject{ {Kind: rbac.UserKind, Name: "barfoo"}, {Kind: rbac.UserKind, Name: "foobar"}, }, ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam"}), appliesTo: false, testCase: "multiple subjects, none that match username", }, { subjects: []rbac.Subject{ {Kind: rbac.UserKind, Name: "barfoo"}, {Kind: rbac.GroupKind, Name: "foobar"}, }, ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}), appliesTo: true, testCase: "multiple subjects, one that match group", }, { subjects: []rbac.Subject{ {Kind: rbac.UserKind, Name: "barfoo"}, {Kind: rbac.GroupKind, Name: "foobar"}, }, ctx: api.WithNamespace( api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}), "namespace1", ), appliesTo: true, testCase: "multiple subjects, one that match group, should ignore namespace", }, { subjects: []rbac.Subject{ {Kind: rbac.UserKind, Name: "barfoo"}, {Kind: rbac.GroupKind, Name: "foobar"}, {Kind: rbac.ServiceAccountKind, Name: "kube-system", Namespace: "default"}, }, ctx: api.WithNamespace( api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "system:serviceaccount:kube-system:default"}), "default", ), appliesTo: true, testCase: "multiple subjects with a service account that matches", }, { subjects: []rbac.Subject{ {Kind: rbac.UserKind, Name: "*"}, }, ctx: api.WithNamespace( api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}), "default", ), appliesTo: true, testCase: "multiple subjects with a service account that matches", }, } for _, tc := range tests { got, err := appliesTo(tc.ctx, tc.subjects) if err != nil { t.Errorf("case %q %v", tc.testCase, err) continue } if got != tc.appliesTo { t.Errorf("case %q want appliesTo=%t, got appliesTo=%t", tc.testCase, tc.appliesTo, got) } } }
func TestStoreDeleteWithOrphanDependents(t *testing.T) { EnableGarbageCollector = true defer func() { EnableGarbageCollector = false }() podWithOrphanFinalizer := func(name string) *api.Pod { return &api.Pod{ ObjectMeta: api.ObjectMeta{Name: name, Finalizers: []string{"foo.com/x", api.FinalizerOrphan, "bar.com/y"}}, Spec: api.PodSpec{NodeName: "machine"}, } } podWithOtherFinalizers := func(name string) *api.Pod { return &api.Pod{ ObjectMeta: api.ObjectMeta{Name: name, Finalizers: []string{"foo.com/x", "bar.com/y"}}, Spec: api.PodSpec{NodeName: "machine"}, } } podWithNoFinalizer := func(name string) *api.Pod { return &api.Pod{ ObjectMeta: api.ObjectMeta{Name: name}, Spec: api.PodSpec{NodeName: "machine"}, } } podWithOnlyOrphanFinalizer := func(name string) *api.Pod { return &api.Pod{ ObjectMeta: api.ObjectMeta{Name: name, Finalizers: []string{api.FinalizerOrphan}}, Spec: api.PodSpec{NodeName: "machine"}, } } trueVar, falseVar := true, false orphanOptions := &api.DeleteOptions{OrphanDependents: &trueVar} nonOrphanOptions := &api.DeleteOptions{OrphanDependents: &falseVar} nilOrphanOptions := &api.DeleteOptions{} testcases := []struct { pod *api.Pod options *api.DeleteOptions expectNotFound bool updatedFinalizers []string }{ // cases run with DeleteOptions.OrphanDedependents=true { podWithOrphanFinalizer("pod1"), orphanOptions, false, []string{"foo.com/x", api.FinalizerOrphan, "bar.com/y"}, }, { podWithOtherFinalizers("pod2"), orphanOptions, false, []string{"foo.com/x", "bar.com/y", api.FinalizerOrphan}, }, { podWithNoFinalizer("pod3"), orphanOptions, false, []string{api.FinalizerOrphan}, }, { podWithOnlyOrphanFinalizer("pod4"), orphanOptions, false, []string{api.FinalizerOrphan}, }, // cases run with DeleteOptions.OrphanDedependents=false { podWithOrphanFinalizer("pod5"), nonOrphanOptions, false, []string{"foo.com/x", "bar.com/y"}, }, { podWithOtherFinalizers("pod6"), nonOrphanOptions, false, []string{"foo.com/x", "bar.com/y"}, }, { podWithNoFinalizer("pod7"), nonOrphanOptions, true, []string{}, }, { podWithOnlyOrphanFinalizer("pod8"), nonOrphanOptions, true, []string{}, }, // cases run with nil DeleteOptions, the finalizers are not updated. { podWithOrphanFinalizer("pod9"), nil, false, []string{"foo.com/x", api.FinalizerOrphan, "bar.com/y"}, }, { podWithOtherFinalizers("pod10"), nil, false, []string{"foo.com/x", "bar.com/y"}, }, { podWithNoFinalizer("pod11"), nil, true, []string{}, }, { podWithOnlyOrphanFinalizer("pod12"), nil, false, []string{api.FinalizerOrphan}, }, // cases run with non-nil DeleteOptions, but nil OrphanDependents, it's treated as OrphanDependents=true { podWithOrphanFinalizer("pod13"), nilOrphanOptions, false, []string{"foo.com/x", api.FinalizerOrphan, "bar.com/y"}, }, { podWithOtherFinalizers("pod14"), nilOrphanOptions, false, []string{"foo.com/x", "bar.com/y"}, }, { podWithNoFinalizer("pod15"), nilOrphanOptions, true, []string{}, }, { podWithOnlyOrphanFinalizer("pod16"), nilOrphanOptions, false, []string{api.FinalizerOrphan}, }, } testContext := api.WithNamespace(api.NewContext(), "test") server, registry := NewTestGenericStoreRegistry(t) defer server.Terminate(t) for _, tc := range testcases { // create pod _, err := registry.Create(testContext, tc.pod) if err != nil { t.Fatalf("Unexpected error: %v", err) } _, err = registry.Delete(testContext, tc.pod.Name, tc.options) if err != nil { t.Fatalf("Unexpected error: %v", err) } obj, err := registry.Get(testContext, tc.pod.Name) if tc.expectNotFound && (err == nil || !errors.IsNotFound(err)) { t.Fatalf("Unexpected error: %v", err) } if !tc.expectNotFound && err != nil { t.Fatalf("Unexpected error: %v", err) } if !tc.expectNotFound { pod, ok := obj.(*api.Pod) if !ok { t.Fatalf("Expect the object to be a pod, but got %#v", obj) } if pod.ObjectMeta.DeletionTimestamp == nil { t.Errorf("Expect the object to have DeletionTimestamp set, but got %#v", pod.ObjectMeta) } if pod.ObjectMeta.DeletionGracePeriodSeconds == nil || *pod.ObjectMeta.DeletionGracePeriodSeconds != 0 { t.Errorf("Expect the object to have 0 DeletionGracePeriodSecond, but got %#v", pod.ObjectMeta) } if e, a := tc.updatedFinalizers, pod.ObjectMeta.Finalizers; !reflect.DeepEqual(e, a) { t.Errorf("Expect object %s to have finalizers %v, got %v", pod.ObjectMeta.Name, e, a) } } } }
func TestStoreHandleFinalizers(t *testing.T) { EnableGarbageCollector = true defer func() { EnableGarbageCollector = false }() podWithFinalizer := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", Finalizers: []string{"foo.com/x"}}, Spec: api.PodSpec{NodeName: "machine"}, } testContext := api.WithNamespace(api.NewContext(), "test") server, registry := NewTestGenericStoreRegistry(t) defer server.Terminate(t) // create pod _, err := registry.Create(testContext, podWithFinalizer) if err != nil { t.Errorf("Unexpected error: %v", err) } // delete object with nil delete options doesn't delete the object _, err = registry.Delete(testContext, podWithFinalizer.Name, nil) if err != nil { t.Errorf("Unexpected error: %v", err) } // the object should still exist obj, err := registry.Get(testContext, podWithFinalizer.Name) if err != nil { t.Errorf("Unexpected error: %v", err) } podWithFinalizer, ok := obj.(*api.Pod) if !ok { t.Errorf("Unexpected object: %#v", obj) } if podWithFinalizer.ObjectMeta.DeletionTimestamp == nil { t.Errorf("Expect the object to have DeletionTimestamp set, but got %#v", podWithFinalizer.ObjectMeta) } if podWithFinalizer.ObjectMeta.DeletionGracePeriodSeconds == nil || *podWithFinalizer.ObjectMeta.DeletionGracePeriodSeconds != 0 { t.Errorf("Expect the object to have 0 DeletionGracePeriodSecond, but got %#v", podWithFinalizer.ObjectMeta) } updatedPodWithFinalizer := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", Finalizers: []string{"foo.com/x"}, ResourceVersion: podWithFinalizer.ObjectMeta.ResourceVersion}, Spec: api.PodSpec{NodeName: "machine"}, } _, _, err = registry.Update(testContext, updatedPodWithFinalizer.ObjectMeta.Name, rest.DefaultUpdatedObjectInfo(updatedPodWithFinalizer, api.Scheme)) if err != nil { t.Errorf("Unexpected error: %v", err) } // the object should still exist, because it still has a finalizer obj, err = registry.Get(testContext, podWithFinalizer.Name) if err != nil { t.Errorf("Unexpected error: %v", err) } podWithFinalizer, ok = obj.(*api.Pod) if !ok { t.Errorf("Unexpected object: %#v", obj) } podWithNoFinalizer := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: podWithFinalizer.ObjectMeta.ResourceVersion}, Spec: api.PodSpec{NodeName: "anothermachine"}, } _, _, err = registry.Update(testContext, podWithFinalizer.ObjectMeta.Name, rest.DefaultUpdatedObjectInfo(podWithNoFinalizer, api.Scheme)) if err != nil { t.Errorf("Unexpected error: %v", err) } // the pod should be removed, because it's finalizer is removed _, err = registry.Get(testContext, podWithFinalizer.Name) if !errors.IsNotFound(err) { t.Errorf("Unexpected error: %v", err) } }
func TestDefaultRuleResolver(t *testing.T) { ruleReadPods := rbac.PolicyRule{ Verbs: []string{"GET", "WATCH"}, APIGroups: []string{"v1"}, Resources: []string{"pods"}, } ruleReadServices := rbac.PolicyRule{ Verbs: []string{"GET", "WATCH"}, APIGroups: []string{"v1"}, Resources: []string{"services"}, } ruleWriteNodes := rbac.PolicyRule{ Verbs: []string{"PUT", "CREATE", "UPDATE"}, APIGroups: []string{"v1"}, Resources: []string{"nodes"}, } ruleAdmin := rbac.PolicyRule{ Verbs: []string{"*"}, APIGroups: []string{"*"}, Resources: []string{"*"}, } staticRoles1 := staticRoles{ roles: []rbac.Role{ { ObjectMeta: api.ObjectMeta{Namespace: "namespace1", Name: "readthings"}, Rules: []rbac.PolicyRule{ruleReadPods, ruleReadServices}, }, }, clusterRoles: []rbac.ClusterRole{ { ObjectMeta: api.ObjectMeta{Name: "cluster-admin"}, Rules: []rbac.PolicyRule{ruleAdmin}, }, { ObjectMeta: api.ObjectMeta{Name: "write-nodes"}, Rules: []rbac.PolicyRule{ruleWriteNodes}, }, }, roleBindings: []rbac.RoleBinding{ { ObjectMeta: api.ObjectMeta{Namespace: "namespace1"}, Subjects: []rbac.Subject{ {Kind: rbac.UserKind, Name: "foobar"}, {Kind: rbac.GroupKind, Name: "group1"}, }, RoleRef: api.ObjectReference{Kind: "Role", Namespace: "namespace1", Name: "readthings"}, }, }, clusterRoleBindings: []rbac.ClusterRoleBinding{ { Subjects: []rbac.Subject{ {Kind: rbac.UserKind, Name: "admin"}, {Kind: rbac.GroupKind, Name: "admin"}, }, RoleRef: api.ObjectReference{Kind: "ClusterRole", Name: "cluster-admin"}, }, }, } tests := []struct { staticRoles // For a given context, what are the rules that apply? ctx api.Context effectiveRules []rbac.PolicyRule }{ { staticRoles: staticRoles1, ctx: api.WithNamespace( api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}), "namespace1", ), effectiveRules: []rbac.PolicyRule{ruleReadPods, ruleReadServices}, }, { staticRoles: staticRoles1, ctx: api.WithNamespace( // Same as above but diffrerent namespace. Should return no rules. api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}), "namespace2", ), effectiveRules: []rbac.PolicyRule{}, }, { staticRoles: staticRoles1, // GetEffectivePolicyRules only returns the policies for the namespace, not the master namespace. ctx: api.WithNamespace( api.WithUser(api.NewContext(), &user.DefaultInfo{ Name: "foobar", Groups: []string{"admin"}, }), "namespace1", ), effectiveRules: []rbac.PolicyRule{ruleReadPods, ruleReadServices}, }, { staticRoles: staticRoles1, // Same as above but without a namespace. Only cluster rules should apply. ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{ Name: "foobar", Groups: []string{"admin"}, }), effectiveRules: []rbac.PolicyRule{ruleAdmin}, }, { staticRoles: staticRoles1, ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{}), effectiveRules: []rbac.PolicyRule{}, }, } for i, tc := range tests { ruleResolver := newMockRuleResolver(&tc.staticRoles) rules, err := ruleResolver.GetEffectivePolicyRules(tc.ctx) if err != nil { t.Errorf("case %d: GetEffectivePolicyRules(context)=%v", i, err) continue } // Sort for deep equals sort.Sort(byHash(rules)) sort.Sort(byHash(tc.effectiveRules)) if !reflect.DeepEqual(rules, tc.effectiveRules) { ruleDiff := diff.ObjectDiff(rules, tc.effectiveRules) t.Errorf("case %d: %s", i, ruleDiff) } } }
func TestStoreList(t *testing.T) { podA := &api.Pod{ ObjectMeta: api.ObjectMeta{Namespace: "test", Name: "bar"}, Spec: api.PodSpec{NodeName: "machine"}, } podB := &api.Pod{ ObjectMeta: api.ObjectMeta{Namespace: "test", Name: "foo"}, Spec: api.PodSpec{NodeName: "machine"}, } testContext := api.WithNamespace(api.NewContext(), "test") noNamespaceContext := api.NewContext() table := map[string]struct { in *api.PodList m generic.Matcher out runtime.Object context api.Context }{ "notFound": { in: nil, m: everythingMatcher{}, out: &api.PodList{Items: []api.Pod{}}, }, "normal": { in: &api.PodList{Items: []api.Pod{*podA, *podB}}, m: everythingMatcher{}, out: &api.PodList{Items: []api.Pod{*podA, *podB}}, }, "normalFiltered": { in: &api.PodList{Items: []api.Pod{*podA, *podB}}, m: setMatcher{sets.NewString("foo")}, out: &api.PodList{Items: []api.Pod{*podB}}, }, "normalFilteredNoNamespace": { in: &api.PodList{Items: []api.Pod{*podA, *podB}}, m: setMatcher{sets.NewString("foo")}, out: &api.PodList{Items: []api.Pod{*podB}}, context: noNamespaceContext, }, "normalFilteredMatchMultiple": { in: &api.PodList{Items: []api.Pod{*podA, *podB}}, m: setMatcher{sets.NewString("foo", "makeMatchSingleReturnFalse")}, out: &api.PodList{Items: []api.Pod{*podB}}, }, } for name, item := range table { ctx := testContext if item.context != nil { ctx = item.context } server, registry := NewTestGenericStoreRegistry(t) if item.in != nil { if err := storagetesting.CreateList("/pods", registry.Storage, item.in); err != nil { t.Errorf("Unexpected error %v", err) } } list, err := registry.ListPredicate(ctx, item.m, nil) if err != nil { t.Errorf("Unexpected error %v", err) continue } // DeepDerivative e,a is needed here b/c the storage layer sets ResourceVersion if e, a := item.out, list; !api.Semantic.DeepDerivative(e, a) { t.Errorf("%v: Expected %#v, got %#v", name, e, a) } server.Terminate(t) } }