func TestAdmissionIgnoresSubresources(t *testing.T) { indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{"namespace": cache.MetaNamespaceIndexFunc}) handler := createResourceQuota(&testclient.Fake{}, indexer) quota := &api.ResourceQuota{} quota.Name = "quota" quota.Namespace = "test" quota.Status = api.ResourceQuotaStatus{ Hard: api.ResourceList{}, Used: api.ResourceList{}, } quota.Status.Hard[api.ResourceMemory] = resource.MustParse("2Gi") quota.Status.Used[api.ResourceMemory] = resource.MustParse("1Gi") indexer.Add(quota) newPod := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "123", Namespace: quota.Namespace}, Spec: api.PodSpec{ Volumes: []api.Volume{{Name: "vol"}}, Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "2Gi")}}, }} err := handler.Admit(admission.NewAttributesRecord(newPod, "Pod", newPod.Namespace, "123", "pods", "", admission.Create, nil)) if err == nil { t.Errorf("Expected an error because the pod exceeded allowed quota") } err = handler.Admit(admission.NewAttributesRecord(newPod, "Pod", newPod.Namespace, "123", "pods", "subresource", admission.Create, nil)) if err != nil { t.Errorf("Did not expect an error because the action went to a subresource: %v", err) } }
func TestIncrementUsageReplicationControllers(t *testing.T) { namespace := "default" client := testclient.NewSimpleFake(&api.ReplicationControllerList{ Items: []api.ReplicationController{ { ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace}, }, }, }) status := &api.ResourceQuotaStatus{ Hard: api.ResourceList{}, Used: api.ResourceList{}, } r := api.ResourceReplicationControllers status.Hard[r] = resource.MustParse("2") status.Used[r] = resource.MustParse("1") dirty, err := IncrementUsage(admission.NewAttributesRecord(&api.ReplicationController{}, "ReplicationController", namespace, "name", "replicationControllers", "", admission.Create, nil), status, client) if err != nil { t.Errorf("Unexpected error: %v", err) } if !dirty { t.Errorf("Expected the status to get incremented, therefore should have been dirty") } quantity := status.Used[r] if quantity.Value() != int64(2) { t.Errorf("Expected new item count to be 2, but was %s", quantity.String()) } }
func TestResourceHelpers(t *testing.T) { cpuLimit := resource.MustParse("10") memoryLimit := resource.MustParse("10G") resourceSpec := ResourceRequirements{ Limits: ResourceList{ "cpu": cpuLimit, "memory": memoryLimit, "qing.io/storage": memoryLimit, }, } if res := resourceSpec.Limits.Cpu(); *res != cpuLimit { t.Errorf("expected cpulimit %d, got %d", cpuLimit, res) } if res := resourceSpec.Limits.Memory(); *res != memoryLimit { t.Errorf("expected memorylimit %d, got %d", memoryLimit, res) } resourceSpec = ResourceRequirements{ Limits: ResourceList{ "memory": memoryLimit, "qing.io/storage": memoryLimit, }, } if res := resourceSpec.Limits.Cpu(); res.Value() != 0 { t.Errorf("expected cpulimit %d, got %d", 0, res) } if res := resourceSpec.Limits.Memory(); *res != memoryLimit { t.Errorf("expected memorylimit %d, got %d", memoryLimit, res) } }
func TestExceedUsagePods(t *testing.T) { namespace := "default" client := testclient.NewSimpleFake(&api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace}, Spec: api.PodSpec{ Volumes: []api.Volume{{Name: "vol"}}, Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}}, }, }, }, }) status := &api.ResourceQuotaStatus{ Hard: api.ResourceList{}, Used: api.ResourceList{}, } r := api.ResourcePods status.Hard[r] = resource.MustParse("1") status.Used[r] = resource.MustParse("1") _, err := IncrementUsage(admission.NewAttributesRecord(&api.Pod{}, "Pod", namespace, "name", "pods", "", admission.Create, nil), status, client) if err == nil { t.Errorf("Expected error because this would exceed your quota") } }
func TestUnboundedMemory(t *testing.T) { namespace := "default" client := testclient.NewSimpleFake(&api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace}, Spec: api.PodSpec{ Volumes: []api.Volume{{Name: "vol"}}, Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}}, }, }, }, }) status := &api.ResourceQuotaStatus{ Hard: api.ResourceList{}, Used: api.ResourceList{}, } r := api.ResourceMemory status.Hard[r] = resource.MustParse("10Gi") status.Used[r] = resource.MustParse("1Gi") newPod := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace}, Spec: api.PodSpec{ Volumes: []api.Volume{{Name: "vol"}}, Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("250m", "0")}}, }} _, err := IncrementUsage(admission.NewAttributesRecord(newPod, "Pod", namespace, "name", "pods", "", admission.Create, nil), status, client) if err == nil { t.Errorf("Expected memory unbounded usage error") } }
func TestNodeBuilder(t *testing.T) { node := &api.Node{ ObjectMeta: api.ObjectMeta{Name: "node1", Namespace: "should-not-have", ResourceVersion: "10"}, Spec: api.NodeSpec{}, Status: api.NodeStatus{ Capacity: api.ResourceList{ api.ResourceCPU: resource.MustParse("1000m"), api.ResourceMemory: resource.MustParse("1Mi"), }, }, } r, w := io.Pipe() go func() { defer w.Close() w.Write([]byte(runtime.EncodeOrDie(latest.Codec, node))) }() b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()). NamespaceParam("test").Stream(r, "STDIN") test := &testVisitor{} err := b.Do().Visit(test.Handle) if err != nil || len(test.Infos) != 1 { t.Fatalf("unexpected response: %v %#v", err, test.Infos) } info := test.Infos[0] if info.Name != "node1" || info.Namespace != "" || info.Object == nil { t.Errorf("unexpected info: %#v", info) } }
func TestCreateMinion(t *testing.T) { requestMinion := &api.Node{ ObjectMeta: api.ObjectMeta{ Name: "minion-1", }, Status: api.NodeStatus{ Capacity: api.ResourceList{ api.ResourceCPU: resource.MustParse("1000m"), api.ResourceMemory: resource.MustParse("1Mi"), }, }, Spec: api.NodeSpec{ Unschedulable: false, }, } c := &testClient{ Request: testRequest{ Method: "POST", Path: testapi.ResourcePath(getNodesResourceName(), "", ""), Body: requestMinion}, Response: Response{ StatusCode: 200, Body: requestMinion, }, } receivedMinion, err := c.Setup().Nodes().Create(requestMinion) c.Validate(t, receivedMinion, err) }
func TestIncrementUsagePods(t *testing.T) { namespace := "default" client := testclient.NewSimpleFake(&api.PodList{ Items: []api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace}, Spec: api.PodSpec{ Volumes: []api.Volume{{Name: "vol"}}, Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}}, }, }, }, }) status := &api.ResourceQuotaStatus{ Hard: api.ResourceList{}, Used: api.ResourceList{}, } r := api.ResourcePods status.Hard[r] = resource.MustParse("2") status.Used[r] = resource.MustParse("1") dirty, err := IncrementUsage(admission.NewAttributesRecord(&api.Pod{}, "Pod", namespace, "name", "pods", "", admission.Create, nil), status, client) if err != nil { t.Errorf("Unexpected error: %v", err) } if !dirty { t.Errorf("Expected the status to get incremented, therefore should have been dirty") } quantity := status.Used[r] if quantity.Value() != int64(2) { t.Errorf("Expected new item count to be 2, but was %s", quantity.String()) } }
func getResourceList(cpu, memory string) api.ResourceList { res := api.ResourceList{} if cpu != "" { res[api.ResourceCPU] = resource.MustParse(cpu) } if memory != "" { res[api.ResourceMemory] = resource.MustParse(memory) } return res }
func getResourceRequirements(cpu, memory string) api.ResourceRequirements { res := api.ResourceRequirements{} res.Limits = api.ResourceList{} if cpu != "" { res.Limits[api.ResourceCPU] = resource.MustParse(cpu) } if memory != "" { res.Limits[api.ResourceMemory] = resource.MustParse(memory) } return res }
func newNode(name string) *api.Node { return &api.Node{ ObjectMeta: api.ObjectMeta{Name: name}, Spec: api.NodeSpec{ ExternalID: name, }, Status: api.NodeStatus{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("10"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), }, }, } }
func TestPersistentVolumeClaimGet(t *testing.T) { ns := api.NamespaceDefault persistentVolumeClaim := &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "abc", Namespace: "foo", }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, }, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), }, }, }, } c := &testClient{ Request: testRequest{ Method: "GET", Path: testapi.ResourcePath(getPersistentVolumeClaimsResoureName(), ns, "abc"), Query: buildQueryValues(ns, nil), Body: nil, }, Response: Response{StatusCode: 200, Body: persistentVolumeClaim}, } response, err := c.Setup().PersistentVolumeClaims(ns).Get("abc") c.Validate(t, response, err) }
func TestMergePodResourceRequirements(t *testing.T) { limitRange := validLimitRange() // pod with no resources enumerated should get each resource from default expected := getResourceRequirements(getResourceList("", ""), getResourceList("", "")) pod := validPod("empty-resources", 1, expected) defaultRequirements := defaultContainerResourceRequirements(&limitRange) mergePodResourceRequirements(&pod, &defaultRequirements) for i := range pod.Spec.Containers { actual := pod.Spec.Containers[i].Resources if !api.Semantic.DeepEqual(expected, actual) { t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, expected, actual) } } // pod with some resources enumerated should only merge empty input := getResourceRequirements(getResourceList("", "512Mi"), getResourceList("", "")) pod = validPod("limit-memory", 1, input) expected = api.ResourceRequirements{ Limits: api.ResourceList{ api.ResourceCPU: defaultRequirements.Limits[api.ResourceCPU], api.ResourceMemory: resource.MustParse("512Mi"), }, Requests: api.ResourceList{}, } mergePodResourceRequirements(&pod, &defaultRequirements) for i := range pod.Spec.Containers { actual := pod.Spec.Containers[i].Resources if !api.Semantic.DeepEqual(expected, actual) { t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, expected, actual) } } }
func TestResourceQuotaStatusConversion(t *testing.T) { // should serialize as "0" expected := resource.NewQuantity(int64(0), resource.DecimalSI) if "0" != expected.String() { t.Errorf("Expected: 0, Actual: %v, do not require units", expected.String()) } parsed := resource.MustParse("0") if "0" != parsed.String() { t.Errorf("Expected: 0, Actual: %v, do not require units", parsed.String()) } quota := &api.ResourceQuota{} quota.Status = api.ResourceQuotaStatus{} quota.Status.Hard = api.ResourceList{} quota.Status.Used = api.ResourceList{} quota.Status.Hard[api.ResourcePods] = *expected // round-trip the object data, _ := versioned.Codec.Encode(quota) object, _ := versioned.Codec.Decode(data) after := object.(*api.ResourceQuota) actualQuantity := after.Status.Hard[api.ResourcePods] actual := &actualQuantity // should be "0", but was "0m" if expected.String() != actual.String() { t.Errorf("Expected %v, Actual %v", expected.String(), actual.String()) } }
func makePersistentVolume(serverIP string) *api.PersistentVolume { return &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ GenerateName: "nfs-", }, Spec: api.PersistentVolumeSpec{ PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimRecycle, Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("2Gi"), }, PersistentVolumeSource: api.PersistentVolumeSource{ NFS: &api.NFSVolumeSource{ Server: serverIP, Path: "/", ReadOnly: false, }, }, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, api.ReadWriteMany, }, }, } }
func createTestVolumes() []*api.PersistentVolume { return []*api.PersistentVolume{ { ObjectMeta: api.ObjectMeta{ UID: "gce-pd-10", Name: "gce003", }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), }, PersistentVolumeSource: api.PersistentVolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{ PDName: "gce123123123", FSType: "foo", }, }, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, }, }, }, { ObjectMeta: api.ObjectMeta{ UID: "nfs-5", Name: "nfs002", }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("5G"), }, PersistentVolumeSource: api.PersistentVolumeSource{ Glusterfs: &api.GlusterfsVolumeSource{ EndpointsName: "andintheend", Path: "theloveyoutakeisequaltotheloveyoumake", }, }, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, api.ReadWriteMany, }, }, }, } }
func createTestClaims() []*api.PersistentVolumeClaim { return []*api.PersistentVolumeClaim{ { ObjectMeta: api.ObjectMeta{ Name: "claim03", Namespace: api.NamespaceDefault, }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("500G"), }, }, }, }, { ObjectMeta: api.ObjectMeta{ Name: "claim01", Namespace: api.NamespaceDefault, }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany, api.ReadWriteOnce}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("8G"), }, }, }, }, { ObjectMeta: api.ObjectMeta{ Name: "claim02", Namespace: api.NamespaceDefault, }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany, api.ReadWriteOnce, api.ReadWriteMany}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("5G"), }, }, }, }, } }
func ExampleMustParse() { memorySize := resource.MustParse("5Gi") fmt.Printf("memorySize = %v (%v)\n", memorySize.Value(), memorySize.Format) diskSize := resource.MustParse("5G") fmt.Printf("diskSize = %v (%v)\n", diskSize.Value(), diskSize.Format) cores := resource.MustParse("5300m") fmt.Printf("milliCores = %v (%v)\n", cores.MilliValue(), cores.Format) cores2 := resource.MustParse("5.4") fmt.Printf("milliCores = %v (%v)\n", cores2.MilliValue(), cores2.Format) // Output: // memorySize = 5368709120 (BinarySI) // diskSize = 5000000000 (DecimalSI) // milliCores = 5300 (DecimalSI) // milliCores = 5400 (DecimalSI) }
// Instances returns an implementation of Instances for Rackspace. func (os *Rackspace) Instances() (cloudprovider.Instances, bool) { glog.V(2).Info("rackspace.Instances() called") compute, err := rackspace.NewComputeV2(os.provider, gophercloud.EndpointOpts{ Region: os.region, }) if err != nil { glog.Warningf("Failed to find compute endpoint: %v", err) return nil, false } pager := flavors.ListDetail(compute, nil) flavor_to_resource := make(map[string]*api.NodeResources) err = pager.EachPage(func(page pagination.Page) (bool, error) { flavorList, err := flavors.ExtractFlavors(page) if err != nil { return false, err } for _, flavor := range flavorList { rsrc := api.NodeResources{ Capacity: api.ResourceList{ api.ResourceCPU: *resource.NewMilliQuantity(int64(flavor.VCPUs*1000), resource.DecimalSI), api.ResourceMemory: resource.MustParse(fmt.Sprintf("%dMi", flavor.RAM)), "openstack.org/disk": resource.MustParse(fmt.Sprintf("%dG", flavor.Disk)), "openstack.org/rxTxFactor": *resource.NewQuantity(int64(flavor.RxTxFactor*1000), resource.DecimalSI), "openstack.org/swap": resource.MustParse(fmt.Sprintf("%dMi", flavor.Swap)), }, } flavor_to_resource[flavor.ID] = &rsrc } return true, nil }) if err != nil { glog.Warningf("Failed to find compute flavors: %v", err) return nil, false } glog.V(2).Infof("Found %v compute flavors", len(flavor_to_resource)) glog.V(1).Info("Claiming to support Instances") return &Instances{compute, flavor_to_resource}, true }
func TestExceedUsageServices(t *testing.T) { namespace := "default" client := testclient.NewSimpleFake(&api.ServiceList{ Items: []api.Service{ { ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace}, }, }, }) status := &api.ResourceQuotaStatus{ Hard: api.ResourceList{}, Used: api.ResourceList{}, } r := api.ResourceServices status.Hard[r] = resource.MustParse("1") status.Used[r] = resource.MustParse("1") _, err := IncrementUsage(admission.NewAttributesRecord(&api.Service{}, "Service", namespace, "name", "services", "", admission.Create, nil), status, client) if err == nil { t.Errorf("Expected error because this would exceed usage") } }
func TestExceedUsagePersistentVolumeClaims(t *testing.T) { namespace := "default" client := testclient.NewSimpleFake(&api.PersistentVolumeClaimList{ Items: []api.PersistentVolumeClaim{ { ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace}, }, }, }) status := &api.ResourceQuotaStatus{ Hard: api.ResourceList{}, Used: api.ResourceList{}, } r := api.ResourcePersistentVolumeClaims status.Hard[r] = resource.MustParse("1") status.Used[r] = resource.MustParse("1") _, err := IncrementUsage(admission.NewAttributesRecord(&api.PersistentVolumeClaim{}, "PersistentVolumeClaim", namespace, "name", "persistentVolumeClaims", "", admission.Create, nil), status, client) if err == nil { t.Errorf("Expected error for exceeding hard limits") } }
func TestResourceQuotaGet(t *testing.T) { ns := api.NamespaceDefault resourceQuota := &api.ResourceQuota{ ObjectMeta: api.ObjectMeta{ Name: "abc", Namespace: "foo", }, Spec: api.ResourceQuotaSpec{ Hard: api.ResourceList{ api.ResourceCPU: resource.MustParse("100"), api.ResourceMemory: resource.MustParse("10000"), api.ResourcePods: resource.MustParse("10"), api.ResourceServices: resource.MustParse("10"), api.ResourceReplicationControllers: resource.MustParse("10"), api.ResourceQuotas: resource.MustParse("10"), }, }, } c := &testClient{ Request: testRequest{ Method: "GET", Path: testapi.ResourcePath(getResourceQuotasResoureName(), ns, "abc"), Query: buildQueryValues(ns, nil), Body: nil, }, Response: Response{StatusCode: 200, Body: resourceQuota}, } response, err := c.Setup().ResourceQuotas(ns).Get("abc") c.Validate(t, response, err) }
func TestSemantic(t *testing.T) { table := []struct { a, b interface{} shouldEqual bool }{ {resource.MustParse("0"), resource.Quantity{}, true}, {resource.Quantity{}, resource.MustParse("0"), true}, {resource.Quantity{}, resource.MustParse("1m"), false}, { resource.Quantity{inf.NewDec(5, 0), resource.BinarySI}, resource.Quantity{inf.NewDec(5, 0), resource.DecimalSI}, true, }, {resource.MustParse("2m"), resource.MustParse("1m"), false}, } for index, item := range table { if e, a := item.shouldEqual, Semantic.DeepEqual(item.a, item.b); e != a { t.Errorf("case[%d], expected %v, got %v.", index, e, a) } } }
func TestEtcdUpdateStatus(t *testing.T) { storage, statusStorage, fakeClient, helper := 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(latest.Codec, pvcStart), 1) 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) } var pvcOut api.PersistentVolumeClaim key, _ = storage.KeyFunc(ctx, "foo") if err := helper.ExtractObj(key, &pvcOut, false); err != nil { t.Fatalf("Unexpected error: %v", err) } if !api.Semantic.DeepEqual(expected, pvcOut) { t.Errorf("unexpected object: %s", util.ObjectDiff(expected, pvcOut)) } }
func validNewPersistentVolumeClaim(name, ns string) *api.PersistentVolumeClaim { pv := &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: name, Namespace: ns, }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), }, }, }, Status: api.PersistentVolumeClaimStatus{ Phase: api.ClaimPending, }, } return pv }
func makePersistentVolumeClaim(ns string) *api.PersistentVolumeClaim { return &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ GenerateName: "pvc-", Namespace: ns, }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, api.ReadWriteMany, }, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("1Gi"), }, }, }, } }
func validNewPersistentVolume(name string) *api.PersistentVolume { pv := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: name, }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), }, AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, PersistentVolumeSource: api.PersistentVolumeSource{ HostPath: &api.HostPathVolumeSource{Path: "/foo"}, }, PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimRetain, }, Status: api.PersistentVolumeStatus{ Phase: api.VolumePending, Message: "bar", Reason: "foo", }, } return pv }
func validNewResourceQuota() *api.ResourceQuota { return &api.ResourceQuota{ ObjectMeta: api.ObjectMeta{ Name: "foo", Namespace: api.NamespaceDefault, }, Spec: api.ResourceQuotaSpec{ 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"), }, }, } }
func TestDescribeContainers(t *testing.T) { testCases := []struct { container api.Container status api.ContainerStatus expectedElements []string }{ // Running state. { container: api.Container{Name: "test", Image: "image"}, status: api.ContainerStatus{ Name: "test", State: api.ContainerState{ Running: &api.ContainerStateRunning{ StartedAt: util.NewTime(time.Now()), }, }, Ready: true, RestartCount: 7, }, expectedElements: []string{"test", "State", "Running", "Ready", "True", "Restart Count", "7", "Image", "image", "Started"}, }, // Waiting state. { container: api.Container{Name: "test", Image: "image"}, status: api.ContainerStatus{ Name: "test", State: api.ContainerState{ Waiting: &api.ContainerStateWaiting{ Reason: "potato", }, }, Ready: true, RestartCount: 7, }, expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "Reason", "potato"}, }, // Terminated state. { container: api.Container{Name: "test", Image: "image"}, status: api.ContainerStatus{ Name: "test", State: api.ContainerState{ Terminated: &api.ContainerStateTerminated{ StartedAt: util.NewTime(time.Now()), FinishedAt: util.NewTime(time.Now()), Reason: "potato", ExitCode: 2, }, }, Ready: true, RestartCount: 7, }, expectedElements: []string{"test", "State", "Terminated", "Ready", "True", "Restart Count", "7", "Image", "image", "Reason", "potato", "Started", "Finished", "Exit Code", "2"}, }, // No state defaults to waiting. { container: api.Container{Name: "test", Image: "image"}, status: api.ContainerStatus{ Name: "test", Ready: true, RestartCount: 7, }, expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image"}, }, // Using limits. { container: api.Container{ Name: "test", Image: "image", Resources: api.ResourceRequirements{ Limits: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("1000"), api.ResourceName(api.ResourceMemory): resource.MustParse("4G"), api.ResourceName(api.ResourceStorage): resource.MustParse("20G"), }, }, }, status: api.ContainerStatus{ Name: "test", Ready: true, RestartCount: 7, }, expectedElements: []string{"cpu", "1k", "memory", "4G", "storage", "20G"}, }, } for i, testCase := range testCases { out := new(bytes.Buffer) pod := api.Pod{ Spec: api.PodSpec{ Containers: []api.Container{testCase.container}, }, Status: api.PodStatus{ ContainerStatuses: []api.ContainerStatus{testCase.status}, }, } describeContainers(&pod, out) output := out.String() for _, expected := range testCase.expectedElements { if !strings.Contains(output, expected) { t.Errorf("Test case %d: expected to find %q in output: %q", i, expected, output) } } } }
func TestMonitorNodeStatusUpdateStatus(t *testing.T) { fakeNow := util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC) table := []struct { fakeNodeHandler *FakeNodeHandler timeToPass time.Duration newNodeStatus api.NodeStatus expectedEvictPods bool expectedRequestCount int expectedNodes []*api.Node }{ // Node created long time ago, without status: // Expect Unknown status posted from node controller. { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}), }, expectedRequestCount: 2, // List+Update expectedNodes: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionUnknown, Reason: fmt.Sprintf("Qinglet never posted node status."), LastHeartbeatTime: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), LastTransitionTime: fakeNow, }, }, }, }, }, }, // Node created recently, without status. // Expect no action from node controller (within startup grace period). { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: fakeNow, }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}), }, expectedRequestCount: 1, // List expectedNodes: nil, }, // Node created long time ago, with status updated by qinglet exceeds grace period. // Expect Unknown status posted from node controller. { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionTrue, // Node status hasn't been updated for 1hr. LastHeartbeatTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), }, }, Capacity: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("10"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), }, }, Spec: api.NodeSpec{ ExternalID: "node0", }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}), }, expectedRequestCount: 3, // (List+)List+Update timeToPass: time.Hour, newNodeStatus: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionTrue, // Node status hasn't been updated for 1hr. LastHeartbeatTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), }, }, Capacity: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("10"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), }, }, expectedNodes: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionUnknown, Reason: fmt.Sprintf("Qinglet stopped posting node status."), LastHeartbeatTime: util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: util.Time{util.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)}, }, }, Capacity: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("10"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), }, }, Spec: api.NodeSpec{ ExternalID: "node0", }, }, }, }, // Node created long time ago, with status updated recently. // Expect no action from node controller (within monitor grace period). { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: util.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionTrue, // Node status has just been updated. LastHeartbeatTime: fakeNow, LastTransitionTime: fakeNow, }, }, Capacity: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("10"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), }, }, Spec: api.NodeSpec{ ExternalID: "node0", }, }, }, Fake: testclient.NewSimpleFake(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}), }, expectedRequestCount: 1, // List expectedNodes: nil, }, } for _, item := range table { nodeController := NewNodeController(nil, item.fakeNodeHandler, 10, 5*time.Minute, NewPodEvictor(util.NewFakeRateLimiter()), testNodeMonitorGracePeriod, testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, false) nodeController.now = func() util.Time { return fakeNow } if err := nodeController.monitorNodeStatus(); err != nil { t.Errorf("unexpected error: %v", err) } if item.timeToPass > 0 { nodeController.now = func() util.Time { return util.Time{Time: fakeNow.Add(item.timeToPass)} } item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus if err := nodeController.monitorNodeStatus(); err != nil { t.Errorf("unexpected error: %v", err) } } if item.expectedRequestCount != item.fakeNodeHandler.RequestCount { t.Errorf("expected %v call, but got %v.", item.expectedRequestCount, item.fakeNodeHandler.RequestCount) } if len(item.fakeNodeHandler.UpdatedNodes) > 0 && !api.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodes) { t.Errorf("expected nodes %+v, got %+v", item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodes[0]) } } }