func TestNoOpUpdates(t *testing.T) { destroyFunc, registry := NewTestGenericStoreRegistry(t) defer destroyFunc() newPod := func() *api.Pod { return &api.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: api.NamespaceDefault, Name: "foo", Labels: map[string]string{"prepare_create": "true"}, }, Spec: api.PodSpec{NodeName: "machine"}, } } var err error var createResult runtime.Object if createResult, err = registry.Create(genericapirequest.NewDefaultContext(), newPod()); err != nil { t.Fatalf("Unexpected error: %v", err) } createdPod, err := registry.Get(genericapirequest.NewDefaultContext(), "foo", &metav1.GetOptions{}) if err != nil { t.Fatalf("Unexpected error: %v", err) } var updateResult runtime.Object p := newPod() if updateResult, _, err = registry.Update(genericapirequest.NewDefaultContext(), p.Name, rest.DefaultUpdatedObjectInfo(p, api.Scheme)); err != nil { t.Fatalf("Unexpected error: %v", err) } // Check whether we do not return empty result on no-op update. if !reflect.DeepEqual(createResult, updateResult) { t.Errorf("no-op update should return a correct value, got: %#v", updateResult) } updatedPod, err := registry.Get(genericapirequest.NewDefaultContext(), "foo", &metav1.GetOptions{}) if err != nil { t.Fatalf("Unexpected error: %v", err) } createdMeta, err := meta.Accessor(createdPod) if err != nil { t.Fatalf("Unexpected error: %v", err) } updatedMeta, err := meta.Accessor(updatedPod) if err != nil { t.Fatalf("Unexpected error: %v", err) } if createdMeta.GetResourceVersion() != updatedMeta.GetResourceVersion() { t.Errorf("no-op update should be ignored and not written to etcd") } }
func TestServiceRegistryIPReallocation(t *testing.T) { storage, _ := NewTestREST(t, nil) svc1 := &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeClusterIP, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(6502), }}, }, } ctx := genericapirequest.NewDefaultContext() created_svc1, _ := storage.Create(ctx, svc1) created_service_1 := created_svc1.(*api.Service) if created_service_1.Name != "foo" { t.Errorf("Expected foo, but got %v", created_service_1.Name) } if !makeIPNet(t).Contains(net.ParseIP(created_service_1.Spec.ClusterIP)) { t.Errorf("Unexpected ClusterIP: %s", created_service_1.Spec.ClusterIP) } _, err := storage.Delete(ctx, created_service_1.Name) if err != nil { t.Errorf("Unexpected error deleting service: %v", err) } svc2 := &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "bar"}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeClusterIP, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(6502), }}, }, } ctx = genericapirequest.NewDefaultContext() created_svc2, _ := storage.Create(ctx, svc2) created_service_2 := created_svc2.(*api.Service) if created_service_2.Name != "bar" { t.Errorf("Expected bar, but got %v", created_service_2.Name) } if !makeIPNet(t).Contains(net.ParseIP(created_service_2.Spec.ClusterIP)) { t.Errorf("Unexpected ClusterIP: %s", created_service_2.Spec.ClusterIP) } }
func TestNamespaceStatusStrategy(t *testing.T) { ctx := genericapirequest.NewDefaultContext() if StatusStrategy.NamespaceScoped() { t.Errorf("Namespaces should not be namespace scoped") } if StatusStrategy.AllowCreateOnUpdate() { t.Errorf("Namespaces should not allow create on update") } now := metav1.Now() oldNamespace := &api.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "10", DeletionTimestamp: &now}, Spec: api.NamespaceSpec{Finalizers: []api.FinalizerName{"kubernetes"}}, Status: api.NamespaceStatus{Phase: api.NamespaceActive}, } namespace := &api.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "9", DeletionTimestamp: &now}, Status: api.NamespaceStatus{Phase: api.NamespaceTerminating}, } StatusStrategy.PrepareForUpdate(ctx, namespace, oldNamespace) if namespace.Status.Phase != api.NamespaceTerminating { t.Errorf("Namespace status updates should allow change of phase: %v", namespace.Status.Phase) } if len(namespace.Spec.Finalizers) != 1 || namespace.Spec.Finalizers[0] != api.FinalizerKubernetes { t.Errorf("PrepareForUpdate should have preserved old finalizers") } errs := StatusStrategy.ValidateUpdate(ctx, namespace, oldNamespace) if len(errs) != 0 { t.Errorf("Unexpected error %v", errs) } if namespace.ResourceVersion != "9" { t.Errorf("Incoming resource version on update should not be mutated") } }
func TestNamespaceFinalizeStrategy(t *testing.T) { ctx := genericapirequest.NewDefaultContext() if FinalizeStrategy.NamespaceScoped() { t.Errorf("Namespaces should not be namespace scoped") } if FinalizeStrategy.AllowCreateOnUpdate() { t.Errorf("Namespaces should not allow create on update") } oldNamespace := &api.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "10"}, Spec: api.NamespaceSpec{Finalizers: []api.FinalizerName{"kubernetes", "example.com/org"}}, Status: api.NamespaceStatus{Phase: api.NamespaceActive}, } namespace := &api.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "9"}, Spec: api.NamespaceSpec{Finalizers: []api.FinalizerName{"example.com/foo"}}, Status: api.NamespaceStatus{Phase: api.NamespaceTerminating}, } FinalizeStrategy.PrepareForUpdate(ctx, namespace, oldNamespace) if namespace.Status.Phase != api.NamespaceActive { t.Errorf("finalize updates should not allow change of phase: %v", namespace.Status.Phase) } if len(namespace.Spec.Finalizers) != 1 || string(namespace.Spec.Finalizers[0]) != "example.com/foo" { t.Errorf("PrepareForUpdate should have modified finalizers") } errs := StatusStrategy.ValidateUpdate(ctx, namespace, oldNamespace) if len(errs) != 0 { t.Errorf("Unexpected error %v", errs) } if namespace.ResourceVersion != "9" { t.Errorf("Incoming resource version on update should not be mutated") } }
// Ensure that when scheduler creates a binding for a pod that has already been deleted // by the API server, API server returns not-found error. func TestEtcdCreateBindingNoPod(t *testing.T) { storage, bindingStorage, _, server := newStorage(t) defer server.Terminate(t) defer storage.Store.DestroyFunc() ctx := genericapirequest.NewDefaultContext() // Assume that a pod has undergone the following: // - Create (apiserver) // - Schedule (scheduler) // - Delete (apiserver) _, err := bindingStorage.Create(ctx, &api.Binding{ ObjectMeta: metav1.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, Target: api.ObjectReference{Name: "machine"}, }) if err == nil { t.Fatalf("Expected not-found-error but got nothing") } if !errors.IsNotFound(storeerr.InterpretGetError(err, api.Resource("pods"), "foo")) { t.Fatalf("Unexpected error returned: %#v", err) } _, err = storage.Get(ctx, "foo", &metav1.GetOptions{}) if err == nil { t.Fatalf("Expected not-found-error but got nothing") } if !errors.IsNotFound(storeerr.InterpretGetError(err, api.Resource("pods"), "foo")) { t.Fatalf("Unexpected error: %v", err) } }
func TestServiceRegistryExternalService(t *testing.T) { ctx := genericapirequest.NewDefaultContext() storage, registry := NewTestREST(t, nil) svc := &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeLoadBalancer, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(6502), }}, }, } _, err := storage.Create(ctx, svc) if err != nil { t.Errorf("Failed to create service: %#v", err) } srv, err := registry.GetService(ctx, svc.Name, &metav1.GetOptions{}) if err != nil { t.Errorf("Unexpected error: %v", err) } if srv == nil { t.Errorf("Failed to find service: %s", svc.Name) } }
func TestEtcdUpdateNotScheduled(t *testing.T) { storage, _, _, server := newStorage(t) defer server.Terminate(t) defer storage.Store.DestroyFunc() ctx := genericapirequest.NewDefaultContext() if _, err := storage.Create(ctx, validNewPod()); err != nil { t.Fatalf("unexpected error: %v", err) } podIn := validChangedPod() _, _, err := storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(podIn, api.Scheme)) if err != nil { t.Errorf("Unexpected error: %v", err) } obj, err := storage.Get(ctx, validNewPod().ObjectMeta.Name, &metav1.GetOptions{}) if err != nil { t.Errorf("unexpected error: %v", err) } podOut := obj.(*api.Pod) // validChangedPod only changes the Labels, so were checking the update was valid if !api.Semantic.DeepEqual(podIn.Labels, podOut.Labels) { t.Errorf("objects differ: %v", diff.ObjectDiff(podOut, podIn)) } }
// Validate that the service creation fails when the requested port number is -1 func TestServiceRegistryExternalTrafficAnnotationNegative(t *testing.T) { ctx := genericapirequest.NewDefaultContext() storage, _ := NewTestREST(t, nil) svc := &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "external-lb-esipp", Annotations: map[string]string{ service.BetaAnnotationExternalTraffic: service.AnnotationValueExternalTrafficLocal, service.BetaAnnotationHealthCheckNodePort: "-1", }, }, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeLoadBalancer, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(6502), }}, }, } created_svc, err := storage.Create(ctx, svc) if created_svc == nil || err != nil { return } t.Errorf("Unexpected creation of service with invalid healthCheckNodePort specified") }
func TestServiceRegistryList(t *testing.T) { ctx := genericapirequest.NewDefaultContext() storage, registry := NewTestREST(t, nil) registry.CreateService(ctx, &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, }, }) registry.CreateService(ctx, &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "foo2", Namespace: api.NamespaceDefault}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar2": "baz2"}, }, }) registry.List.ResourceVersion = "1" s, _ := storage.List(ctx, nil) sl := s.(*api.ServiceList) if len(sl.Items) != 2 { t.Fatalf("Expected 2 services, but got %v", len(sl.Items)) } if e, a := "foo", sl.Items[0].Name; e != a { t.Errorf("Expected %v, but got %v", e, a) } if e, a := "foo2", sl.Items[1].Name; e != a { t.Errorf("Expected %v, but got %v", e, a) } if sl.ResourceVersion != "1" { t.Errorf("Unexpected resource version: %#v", sl) } }
func TestServiceRegistryIPLoadBalancer(t *testing.T) { storage, _ := NewTestREST(t, nil) svc := &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "1"}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeLoadBalancer, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(6502), }}, }, } ctx := genericapirequest.NewDefaultContext() created_svc, _ := storage.Create(ctx, svc) created_service := created_svc.(*api.Service) if created_service.Spec.Ports[0].Port != 6502 { t.Errorf("Expected port 6502, but got %v", created_service.Spec.Ports[0].Port) } if !makeIPNet(t).Contains(net.ParseIP(created_service.Spec.ClusterIP)) { t.Errorf("Unexpected ClusterIP: %s", created_service.Spec.ClusterIP) } update := deepCloneService(created_service) _, _, err := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update, api.Scheme)) if err != nil { t.Errorf("Unexpected error %v", err) } }
// TestValidNamespace validates that namespace rules are enforced on a resource prior to create or update func TestValidNamespace(t *testing.T) { ctx := genericapirequest.NewDefaultContext() namespace, _ := genericapirequest.NamespaceFrom(ctx) resource := api.ReplicationController{} if !ValidNamespace(ctx, &resource.ObjectMeta) { t.Fatalf("expected success") } if namespace != resource.Namespace { t.Fatalf("expected resource to have the default namespace assigned during validation") } resource = api.ReplicationController{ObjectMeta: metav1.ObjectMeta{Namespace: "other"}} if ValidNamespace(ctx, &resource.ObjectMeta) { t.Fatalf("Expected error that resource and context errors do not match because resource has different namespace") } ctx = genericapirequest.NewContext() if ValidNamespace(ctx, &resource.ObjectMeta) { t.Fatalf("Expected error that resource and context errors do not match since context has no namespace") } ctx = genericapirequest.NewContext() ns := genericapirequest.NamespaceValue(ctx) if ns != "" { t.Fatalf("Expected the empty string") } }
func TestServiceStatusStrategy(t *testing.T) { ctx := genericapirequest.NewDefaultContext() if !StatusStrategy.NamespaceScoped() { t.Errorf("Service must be namespace scoped") } oldService := makeValidService() newService := makeValidService() oldService.ResourceVersion = "4" newService.ResourceVersion = "4" newService.Spec.SessionAffinity = "ClientIP" newService.Status = api.ServiceStatus{ LoadBalancer: api.LoadBalancerStatus{ Ingress: []api.LoadBalancerIngress{ {IP: "127.0.0.2"}, }, }, } StatusStrategy.PrepareForUpdate(ctx, &newService, &oldService) if newService.Status.LoadBalancer.Ingress[0].IP != "127.0.0.2" { t.Errorf("Service status updates should allow change of status fields") } if newService.Spec.SessionAffinity != "None" { t.Errorf("PrepareForUpdate should have preserved old spec") } errs := StatusStrategy.ValidateUpdate(ctx, &newService, &oldService) if len(errs) != 0 { t.Errorf("Unexpected error %v", errs) } }
func TestEtcdCreateWithConflict(t *testing.T) { storage, bindingStorage, _, server := newStorage(t) defer server.Terminate(t) defer storage.Store.DestroyFunc() ctx := genericapirequest.NewDefaultContext() _, err := storage.Create(ctx, validNewPod()) if err != nil { t.Fatalf("unexpected error: %v", err) } // Suddenly, a wild scheduler appears: binding := api.Binding{ ObjectMeta: metav1.ObjectMeta{ Namespace: api.NamespaceDefault, Name: "foo", Annotations: map[string]string{"label1": "value1"}, }, Target: api.ObjectReference{Name: "machine"}, } _, err = bindingStorage.Create(ctx, &binding) if err != nil { t.Fatalf("unexpected error: %v", err) } _, err = bindingStorage.Create(ctx, &binding) if err == nil || !errors.IsConflict(err) { t.Fatalf("expected resource conflict error, not: %v", err) } }
func TestServiceRegistryUpdateMultiPortExternalService(t *testing.T) { ctx := genericapirequest.NewDefaultContext() storage, _ := NewTestREST(t, nil) // Create external load balancer. svc1 := &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "1"}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeLoadBalancer, Ports: []api.ServicePort{{ Name: "p", Port: 6502, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(6502), }, { Name: "q", Port: 8086, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(8086), }}, }, } if _, err := storage.Create(ctx, svc1); err != nil { t.Fatalf("Unexpected error: %v", err) } // Modify ports svc2 := deepCloneService(svc1) svc2.Spec.Ports[1].Port = 8088 if _, _, err := storage.Update(ctx, svc2.Name, rest.DefaultUpdatedObjectInfo(svc2, api.Scheme)); err != nil { t.Fatalf("Unexpected error: %v", err) } }
func TestEtcdCreateWithContainersNotFound(t *testing.T) { storage, bindingStorage, _, server := newStorage(t) defer server.Terminate(t) defer storage.Store.DestroyFunc() ctx := genericapirequest.NewDefaultContext() _, err := storage.Create(ctx, validNewPod()) if err != nil { t.Fatalf("unexpected error: %v", err) } // Suddenly, a wild scheduler appears: _, err = bindingStorage.Create(ctx, &api.Binding{ ObjectMeta: metav1.ObjectMeta{ Namespace: api.NamespaceDefault, Name: "foo", Annotations: map[string]string{"label1": "value1"}, }, Target: api.ObjectReference{Name: "machine"}, }) if err != nil { t.Fatalf("unexpected error: %v", err) } obj, err := storage.Get(ctx, "foo", &metav1.GetOptions{}) if err != nil { t.Fatalf("Unexpected error %v", err) } pod := obj.(*api.Pod) if !(pod.Annotations != nil && pod.Annotations["label1"] == "value1") { t.Fatalf("Pod annotations don't match the expected: %v", pod.Annotations) } }
// Validate allocation of a nodePort when the externalTraffic=OnlyLocal annotation is set // and type is LoadBalancer func TestServiceRegistryExternalTrafficAnnotationHealthCheckNodePortAllocation(t *testing.T) { ctx := genericapirequest.NewDefaultContext() storage, _ := NewTestREST(t, nil) svc := &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "external-lb-esipp", Annotations: map[string]string{ service.BetaAnnotationExternalTraffic: service.AnnotationValueExternalTrafficLocal, }, }, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeLoadBalancer, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(6502), }}, }, } created_svc, err := storage.Create(ctx, svc) if created_svc == nil || err != nil { t.Errorf("Unexpected failure creating service %v", err) } created_service := created_svc.(*api.Service) if !service.NeedsHealthCheck(created_service) { t.Errorf("Unexpected missing annotation %s", service.BetaAnnotationExternalTraffic) } port := service.GetServiceHealthCheckNodePort(created_service) if port == 0 { t.Errorf("Failed to allocate and create the health check node port annotation %s", service.BetaAnnotationHealthCheckNodePort) } }
func TestClusterStrategy(t *testing.T) { ctx := genericapirequest.NewDefaultContext() if Strategy.NamespaceScoped() { t.Errorf("Cluster should not be namespace scoped") } if Strategy.AllowCreateOnUpdate() { t.Errorf("Cluster should not allow create on update") } cluster := validNewCluster() Strategy.PrepareForCreate(ctx, cluster) if len(cluster.Status.Conditions) != 0 { t.Errorf("Cluster should not allow setting conditions on create") } errs := Strategy.Validate(ctx, cluster) if len(errs) != 0 { t.Errorf("Unexpected error validating %v", errs) } invalidCluster := invalidNewCluster() Strategy.PrepareForUpdate(ctx, invalidCluster, cluster) if reflect.DeepEqual(invalidCluster.Spec, cluster.Spec) || !reflect.DeepEqual(invalidCluster.Status, cluster.Status) { t.Error("Only spec is expected being changed") } errs = Strategy.ValidateUpdate(ctx, invalidCluster, cluster) if len(errs) == 0 { t.Errorf("Expected a validation error") } if cluster.ResourceVersion != "4" { t.Errorf("Incoming resource version on update should not be mutated") } }
func TestPodDisruptionBudgetStrategy(t *testing.T) { ctx := genericapirequest.NewDefaultContext() if !Strategy.NamespaceScoped() { t.Errorf("PodDisruptionBudget must be namespace scoped") } if Strategy.AllowCreateOnUpdate() { t.Errorf("PodDisruptionBudget should not allow create on update") } validSelector := map[string]string{"a": "b"} pdb := &policy.PodDisruptionBudget{ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, Spec: policy.PodDisruptionBudgetSpec{ MinAvailable: intstr.FromInt(3), Selector: &metav1.LabelSelector{MatchLabels: validSelector}, }, } Strategy.PrepareForCreate(ctx, pdb) errs := Strategy.Validate(ctx, pdb) if len(errs) != 0 { t.Errorf("Unexpected error validating %v", errs) } newPdb := &policy.PodDisruptionBudget{ ObjectMeta: metav1.ObjectMeta{Name: pdb.Name, Namespace: pdb.Namespace}, Spec: pdb.Spec, Status: policy.PodDisruptionBudgetStatus{ PodDisruptionsAllowed: 1, CurrentHealthy: 3, DesiredHealthy: 3, ExpectedPods: 3, }, } // Nothing in Spec changes: OK Strategy.PrepareForUpdate(ctx, newPdb, pdb) errs = Strategy.ValidateUpdate(ctx, newPdb, pdb) if len(errs) != 0 { t.Errorf("Unexpected error updating PodDisruptionBudget.") } // Changing the selector? No. newPdb.Spec.Selector = &metav1.LabelSelector{MatchLabels: map[string]string{"a": "bar"}} Strategy.PrepareForUpdate(ctx, newPdb, pdb) errs = Strategy.ValidateUpdate(ctx, newPdb, pdb) if len(errs) == 0 { t.Errorf("Expected a validation error since updates are disallowed on poddisruptionbudgets.") } newPdb.Spec.Selector = pdb.Spec.Selector // Changing MinAvailable? Also no. newPdb.Spec.MinAvailable = intstr.FromString("28%") Strategy.PrepareForUpdate(ctx, newPdb, pdb) errs = Strategy.ValidateUpdate(ctx, newPdb, pdb) if len(errs) == 0 { t.Errorf("Expected a validation error since updates are disallowed on poddisruptionbudgets.") } }
func TestControllerStrategy(t *testing.T) { ctx := genericapirequest.NewDefaultContext() if !Strategy.NamespaceScoped() { t.Errorf("ReplicationController must be namespace scoped") } if Strategy.AllowCreateOnUpdate() { t.Errorf("ReplicationController should not allow create on update") } validSelector := map[string]string{"a": "b"} validPodTemplate := api.PodTemplate{ Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: validSelector, }, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, }, }, } rc := &api.ReplicationController{ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, Spec: api.ReplicationControllerSpec{ Selector: validSelector, Template: &validPodTemplate.Template, }, Status: api.ReplicationControllerStatus{ Replicas: 1, ObservedGeneration: int64(10), }, } Strategy.PrepareForCreate(ctx, rc) if rc.Status.Replicas != 0 { t.Error("ReplicationController should not allow setting status.replicas on create") } if rc.Status.ObservedGeneration != int64(0) { t.Error("ReplicationController should not allow setting status.observedGeneration on create") } errs := Strategy.Validate(ctx, rc) if len(errs) != 0 { t.Errorf("Unexpected error validating %v", errs) } invalidRc := &api.ReplicationController{ ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "4"}, } Strategy.PrepareForUpdate(ctx, invalidRc, rc) errs = Strategy.ValidateUpdate(ctx, invalidRc, rc) if len(errs) == 0 { t.Errorf("Expected a validation error") } if invalidRc.ResourceVersion != "4" { t.Errorf("Incoming resource version on update should not be mutated") } }
func TestEtcdCreateBinding(t *testing.T) { ctx := genericapirequest.NewDefaultContext() testCases := map[string]struct { binding api.Binding errOK func(error) bool }{ "noName": { binding: api.Binding{ ObjectMeta: metav1.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, Target: api.ObjectReference{}, }, errOK: func(err error) bool { return err != nil }, }, "badKind": { binding: api.Binding{ ObjectMeta: metav1.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, Target: api.ObjectReference{Name: "machine1", Kind: "unknown"}, }, errOK: func(err error) bool { return err != nil }, }, "emptyKind": { binding: api.Binding{ ObjectMeta: metav1.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, Target: api.ObjectReference{Name: "machine2"}, }, errOK: func(err error) bool { return err == nil }, }, "kindNode": { binding: api.Binding{ ObjectMeta: metav1.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, Target: api.ObjectReference{Name: "machine3", Kind: "Node"}, }, errOK: func(err error) bool { return err == nil }, }, } for k, test := range testCases { storage, bindingStorage, _, server := newStorage(t) if _, err := storage.Create(ctx, validNewPod()); err != nil { t.Fatalf("%s: unexpected error: %v", k, err) } if _, err := bindingStorage.Create(ctx, &test.binding); !test.errOK(err) { t.Errorf("%s: unexpected error: %v", k, err) } else if err == nil { // If bind succeeded, verify Host field in pod's Spec. pod, err := storage.Get(ctx, validNewPod().ObjectMeta.Name, &metav1.GetOptions{}) if err != nil { t.Errorf("%s: unexpected error: %v", k, err) } else if pod.(*api.Pod).Spec.NodeName != test.binding.Target.Name { t.Errorf("%s: expected: %v, got: %v", k, pod.(*api.Pod).Spec.NodeName, test.binding.Target.Name) } } storage.Store.DestroyFunc() server.Terminate(t) } }
func TestControllerStatusStrategy(t *testing.T) { ctx := genericapirequest.NewDefaultContext() if !StatusStrategy.NamespaceScoped() { t.Errorf("ReplicationController must be namespace scoped") } if StatusStrategy.AllowCreateOnUpdate() { t.Errorf("ReplicationController should not allow create on update") } validSelector := map[string]string{"a": "b"} validPodTemplate := api.PodTemplate{ Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: validSelector, }, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, }, }, } oldController := &api.ReplicationController{ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault, ResourceVersion: "10"}, Spec: api.ReplicationControllerSpec{ Replicas: 3, Selector: validSelector, Template: &validPodTemplate.Template, }, Status: api.ReplicationControllerStatus{ Replicas: 1, ObservedGeneration: int64(10), }, } newController := &api.ReplicationController{ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault, ResourceVersion: "9"}, Spec: api.ReplicationControllerSpec{ Replicas: 1, Selector: validSelector, Template: &validPodTemplate.Template, }, Status: api.ReplicationControllerStatus{ Replicas: 3, ObservedGeneration: int64(11), }, } StatusStrategy.PrepareForUpdate(ctx, newController, oldController) if newController.Status.Replicas != 3 { t.Errorf("Replication controller status updates should allow change of replicas: %v", newController.Status.Replicas) } if newController.Spec.Replicas != 3 { t.Errorf("PrepareForUpdate should have preferred spec") } errs := StatusStrategy.ValidateUpdate(ctx, newController, oldController) if len(errs) != 0 { t.Errorf("Unexpected error %v", errs) } }
func TestStatefulSetStatusStrategy(t *testing.T) { ctx := genericapirequest.NewDefaultContext() if !StatusStrategy.NamespaceScoped() { t.Errorf("StatefulSet must be namespace scoped") } if StatusStrategy.AllowCreateOnUpdate() { t.Errorf("StatefulSet should not allow create on update") } validSelector := map[string]string{"a": "b"} validPodTemplate := api.PodTemplate{ Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: validSelector, }, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, }, }, } oldPS := &apps.StatefulSet{ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault, ResourceVersion: "10"}, Spec: apps.StatefulSetSpec{ Replicas: 3, Selector: &metav1.LabelSelector{MatchLabels: validSelector}, Template: validPodTemplate.Template, }, Status: apps.StatefulSetStatus{ Replicas: 1, }, } newPS := &apps.StatefulSet{ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault, ResourceVersion: "9"}, Spec: apps.StatefulSetSpec{ Replicas: 1, Selector: &metav1.LabelSelector{MatchLabels: validSelector}, Template: validPodTemplate.Template, }, Status: apps.StatefulSetStatus{ Replicas: 2, }, } StatusStrategy.PrepareForUpdate(ctx, newPS, oldPS) if newPS.Status.Replicas != 2 { t.Errorf("StatefulSet status updates should allow change of pods: %v", newPS.Status.Replicas) } if newPS.Spec.Replicas != 3 { t.Errorf("StatefulSet status updates should not clobber spec: %v", newPS.Spec) } errs := StatusStrategy.ValidateUpdate(ctx, newPS, oldPS) if len(errs) != 0 { t.Errorf("Unexpected error %v", errs) } }
// TestHasObjectMetaSystemFieldValues validates that true is returned if and only if all fields are populated func TestHasObjectMetaSystemFieldValues(t *testing.T) { ctx := genericapirequest.NewDefaultContext() resource := metav1.ObjectMeta{} if api.HasObjectMetaSystemFieldValues(&resource) { t.Errorf("the resource does not have all fields yet populated, but incorrectly reports it does") } FillObjectMetaSystemFields(ctx, &resource) if !api.HasObjectMetaSystemFieldValues(&resource) { t.Errorf("the resource does have all fields populated, but incorrectly reports it does not") } }
func TestServiceRegistryIPUpdate(t *testing.T) { storage, _ := NewTestREST(t, nil) svc := &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "1"}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeClusterIP, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(6502), }}, }, } ctx := genericapirequest.NewDefaultContext() created_svc, _ := storage.Create(ctx, svc) created_service := created_svc.(*api.Service) if created_service.Spec.Ports[0].Port != 6502 { t.Errorf("Expected port 6502, but got %v", created_service.Spec.Ports[0].Port) } if !makeIPNet(t).Contains(net.ParseIP(created_service.Spec.ClusterIP)) { t.Errorf("Unexpected ClusterIP: %s", created_service.Spec.ClusterIP) } update := deepCloneService(created_service) update.Spec.Ports[0].Port = 6503 updated_svc, _, _ := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update, api.Scheme)) updated_service := updated_svc.(*api.Service) if updated_service.Spec.Ports[0].Port != 6503 { t.Errorf("Expected port 6503, but got %v", updated_service.Spec.Ports[0].Port) } testIPs := []string{"1.2.3.93", "1.2.3.94", "1.2.3.95", "1.2.3.96"} testIP := "" for _, ip := range testIPs { if !storage.serviceIPs.(*ipallocator.Range).Has(net.ParseIP(ip)) { testIP = ip break } } update = deepCloneService(created_service) update.Spec.Ports[0].Port = 6503 update.Spec.ClusterIP = testIP // Error: Cluster IP is immutable _, _, err := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update, api.Scheme)) if err == nil || !errors.IsInvalid(err) { t.Errorf("Unexpected error type: %v", err) } }
func TestCreateSetsFields(t *testing.T) { storage, _, _, server := newStorage(t) defer server.Terminate(t) defer storage.Store.DestroyFunc() pod := validNewPod() _, err := storage.Create(genericapirequest.NewDefaultContext(), pod) if err != nil { t.Fatalf("unexpected error: %v", err) } ctx := genericapirequest.NewDefaultContext() object, err := storage.Get(ctx, "foo", &metav1.GetOptions{}) if err != nil { t.Errorf("unexpected error: %v", err) } actual := object.(*api.Pod) if actual.Name != pod.Name { t.Errorf("unexpected pod: %#v", actual) } if len(actual.UID) == 0 { t.Errorf("expected pod UID to be set: %#v", actual) } }
func TestServiceStorageValidatesCreate(t *testing.T) { storage, _ := NewTestREST(t, nil) failureCases := map[string]api.Service{ "empty ID": { ObjectMeta: metav1.ObjectMeta{Name: ""}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeClusterIP, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(6502), }}, }, }, "empty port": { ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeClusterIP, Ports: []api.ServicePort{{ Protocol: api.ProtocolTCP, }}, }, }, "missing targetPort": { ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeClusterIP, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, }}, }, }, } ctx := genericapirequest.NewDefaultContext() for _, failureCase := range failureCases { c, err := storage.Create(ctx, &failureCase) if c != nil { t.Errorf("Expected nil object") } if !errors.IsInvalid(err) { t.Errorf("Expected to get an invalid resource error, got %v", err) } } }
func TestServiceStorageValidatesUpdate(t *testing.T) { ctx := genericapirequest.NewDefaultContext() storage, registry := NewTestREST(t, nil) registry.CreateService(ctx, &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, }}, }, }) failureCases := map[string]api.Service{ "empty ID": { ObjectMeta: metav1.ObjectMeta{Name: ""}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeClusterIP, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(6502), }}, }, }, "invalid selector": { ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: api.ServiceSpec{ Selector: map[string]string{"ThisSelectorFailsValidation": "ok"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeClusterIP, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(6502), }}, }, }, } for _, failureCase := range failureCases { c, created, err := storage.Update(ctx, failureCase.Name, rest.DefaultUpdatedObjectInfo(&failureCase, api.Scheme)) if c != nil || created { t.Errorf("Expected nil object or created false") } if !errors.IsInvalid(err) { t.Errorf("Expected to get an invalid resource error, got %v", err) } } }
func TestGenerationNumber(t *testing.T) { storage, server := newStorage(t) defer server.Terminate(t) defer storage.Controller.Store.DestroyFunc() modifiedSno := *validNewController() modifiedSno.Generation = 100 modifiedSno.Status.ObservedGeneration = 10 ctx := genericapirequest.NewDefaultContext() rc, err := createController(storage.Controller, modifiedSno, t) ctrl, err := storage.Controller.Get(ctx, rc.Name, &metav1.GetOptions{}) if err != nil { t.Errorf("unexpected error: %v", err) } controller, _ := ctrl.(*api.ReplicationController) // Generation initialization if controller.Generation != 1 && controller.Status.ObservedGeneration != 0 { t.Fatalf("Unexpected generation number %v, status generation %v", controller.Generation, controller.Status.ObservedGeneration) } // Updates to spec should increment the generation number controller.Spec.Replicas += 1 storage.Controller.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller, api.Scheme)) if err != nil { t.Errorf("unexpected error: %v", err) } ctrl, err = storage.Controller.Get(ctx, rc.Name, &metav1.GetOptions{}) if err != nil { t.Errorf("unexpected error: %v", err) } controller, _ = ctrl.(*api.ReplicationController) if controller.Generation != 2 || controller.Status.ObservedGeneration != 0 { t.Fatalf("Unexpected generation, spec: %v, status: %v", controller.Generation, controller.Status.ObservedGeneration) } // Updates to status should not increment either spec or status generation numbers controller.Status.Replicas += 1 storage.Controller.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller, api.Scheme)) if err != nil { t.Errorf("unexpected error: %v", err) } ctrl, err = storage.Controller.Get(ctx, rc.Name, &metav1.GetOptions{}) if err != nil { t.Errorf("unexpected error: %v", err) } controller, _ = ctrl.(*api.ReplicationController) if controller.Generation != 2 || controller.Status.ObservedGeneration != 0 { t.Fatalf("Unexpected generation number, spec: %v, status: %v", controller.Generation, controller.Status.ObservedGeneration) } }
func TestServiceRegistryUpdate(t *testing.T) { ctx := genericapirequest.NewDefaultContext() storage, registry := NewTestREST(t, nil) svc, err := registry.CreateService(ctx, &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "1", Namespace: api.NamespaceDefault}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz1"}, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(6502), }}, }, }) if err != nil { t.Fatalf("Expected no error: %v", err) } updated_svc, created, err := storage.Update(ctx, "foo", rest.DefaultUpdatedObjectInfo(&api.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", ResourceVersion: svc.ResourceVersion}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz2"}, SessionAffinity: api.ServiceAffinityNone, Type: api.ServiceTypeClusterIP, Ports: []api.ServicePort{{ Port: 6502, Protocol: api.ProtocolTCP, TargetPort: intstr.FromInt(6502), }}, }, }, api.Scheme)) if err != nil { t.Fatalf("Expected no error: %v", err) } if updated_svc == nil { t.Errorf("Expected non-nil object") } if created { t.Errorf("expected not created") } updated_service := updated_svc.(*api.Service) if updated_service.Name != "foo" { t.Errorf("Expected foo, but got %v", updated_service.Name) } if e, a := "foo", registry.UpdatedID; e != a { t.Errorf("Expected %v, but got %v", e, a) } }
func TestServiceRegistryGet(t *testing.T) { ctx := genericapirequest.NewDefaultContext() storage, registry := NewTestREST(t, nil) registry.CreateService(ctx, &api.Service{ ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: api.ServiceSpec{ Selector: map[string]string{"bar": "baz"}, }, }) storage.Get(ctx, "foo", &metav1.GetOptions{}) if e, a := "foo", registry.GottenID; e != a { t.Errorf("Expected %v, but got %v", e, a) } }