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 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, }, }, } }
// Create for hostPath simply creates a local /tmp/hostpath_pv/%s directory as a new PersistentVolume. // This Creater is meant for development and testing only and WILL NOT WORK in a multi-node cluster. func (r *hostPathCreater) Create() (*api.PersistentVolume, error) { fullpath := fmt.Sprintf("/tmp/hostpath_pv/%s", util.NewUUID()) err := os.MkdirAll(fullpath, 0750) if err != nil { return nil, err } return &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ GenerateName: "pv-hostpath-", Labels: map[string]string{ "createdby": "hostpath dynamic provisioner", }, }, Spec: api.PersistentVolumeSpec{ PersistentVolumeReclaimPolicy: r.options.PersistentVolumeReclaimPolicy, AccessModes: r.options.AccessModes, Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse(fmt.Sprintf("%dMi", r.options.CapacityMB)), }, PersistentVolumeSource: api.PersistentVolumeSource{ HostPath: &api.HostPathVolumeSource{ Path: fullpath, }, }, }, }, nil }
func TestPersistentVolumeStatusUpdate(t *testing.T) { persistentVolume := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "abc", ResourceVersion: "1", }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), }, PersistentVolumeSource: api.PersistentVolumeSource{ HostPath: &api.HostPathVolumeSource{Path: "/foo"}, }, }, Status: api.PersistentVolumeStatus{ Phase: api.VolumeBound, Message: "foo", }, } c := &testClient{ Request: testRequest{ Method: "PUT", Path: testapi.Default.ResourcePath(getPersistentVolumesResoureName(), "", "abc") + "/status", Query: buildQueryValues(nil)}, Response: Response{StatusCode: 200, Body: persistentVolume}, } response, err := c.Setup(t).PersistentVolumes().UpdateStatus(persistentVolume) c.Validate(t, response, err) }
func TestPersistentVolumeGet(t *testing.T) { persistentVolume := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "abc", Namespace: "foo", }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), }, PersistentVolumeSource: api.PersistentVolumeSource{ HostPath: &api.HostPathVolumeSource{Path: "/foo"}, }, }, } c := &testClient{ Request: testRequest{ Method: "GET", Path: testapi.Default.ResourcePath(getPersistentVolumesResoureName(), "", "abc"), Query: buildQueryValues(nil), Body: nil, }, Response: Response{StatusCode: 200, Body: persistentVolume}, } response, err := c.Setup(t).PersistentVolumes().Get("abc") c.Validate(t, response, err) }
func TestCalculateTimeoutForVolume(t *testing.T) { pv := &api.PersistentVolume{ Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("500M"), }, }, } timeout := CalculateTimeoutForVolume(50, 30, pv) if timeout != 50 { t.Errorf("Expected 50 for timeout but got %v", timeout) } pv.Spec.Capacity[api.ResourceStorage] = resource.MustParse("2Gi") timeout = CalculateTimeoutForVolume(50, 30, pv) if timeout != 60 { t.Errorf("Expected 60 for timeout but got %v", timeout) } pv.Spec.Capacity[api.ResourceStorage] = resource.MustParse("150Gi") timeout = CalculateTimeoutForVolume(50, 30, pv) if timeout != 4500 { t.Errorf("Expected 4500 for timeout but got %v", timeout) } }
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.Default.ResourcePath(getPersistentVolumeClaimsResoureName(), ns, "abc"), Query: buildQueryValues(nil), Body: nil, }, Response: Response{StatusCode: 200, Body: persistentVolumeClaim}, } response, err := c.Setup(t).PersistentVolumeClaims(ns).Get("abc") c.Validate(t, response, err) }
func validNewNode() *api.Node { return &api.Node{ ObjectMeta: api.ObjectMeta{ Name: "foo", Labels: map[string]string{ "name": "foo", }, }, Spec: api.NodeSpec{ ExternalID: "external", }, Status: api.NodeStatus{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("10"), api.ResourceName(api.ResourceMemory): resource.MustParse("0"), }, }, } }
// findByAccessModesAndStorageCapacity is a convenience method that calls Find w/ requisite matchPredicate for storage func (pvIndex *persistentVolumeOrderedIndex) findByAccessModesAndStorageCapacity(prebindKey string, modes []api.PersistentVolumeAccessMode, qty resource.Quantity) (*api.PersistentVolume, error) { pv := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Annotations: map[string]string{ createdForKey: prebindKey, }, }, Spec: api.PersistentVolumeSpec{ AccessModes: modes, Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): qty, }, }, } return pvIndex.Find(pv, matchStorageCapacity) }
func TestUpdate(t *testing.T) { storage, _, fakeClient := newStorage(t) test := registrytest.New(t, fakeClient, storage.Etcd).ClusterScope() test.TestUpdate( // valid validNewPersistentVolume("foo"), // updateFunc func(obj runtime.Object) runtime.Object { object := obj.(*api.PersistentVolume) object.Spec.Capacity = api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("20G"), } return object }, ) }
func TestUpdateStatus(t *testing.T) { storage, statusStorage, fakeClient := newStorage(t) ctx := api.NewDefaultContext() fakeClient.TestIndex = true key, _ := storage.KeyFunc(ctx, "foo") key = etcdtest.AddPrefix(key) pvcStart := validNewPersistentVolumeClaim("foo", api.NamespaceDefault) fakeClient.Set(key, runtime.EncodeOrDie(testapi.Default.Codec(), pvcStart), 0) pvc := &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "foo", Namespace: api.NamespaceDefault, ResourceVersion: "1", }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("3Gi"), }, }, }, Status: api.PersistentVolumeClaimStatus{ Phase: api.ClaimBound, }, } expected := *pvcStart expected.ResourceVersion = "2" expected.Labels = pvc.Labels expected.Status = pvc.Status _, _, err := statusStorage.Update(ctx, pvc) if err != nil { t.Fatalf("Unexpected error: %v", err) } pvcOut, err := storage.Get(ctx, "foo") if err != nil { t.Errorf("unexpected error: %v", err) } if !api.Semantic.DeepEqual(&expected, pvcOut) { t.Errorf("unexpected object: %s", util.ObjectDiff(&expected, pvcOut)) } }
func TestUpdate(t *testing.T) { storage, _, fakeClient := newStorage(t) test := registrytest.New(t, fakeClient, storage.Etcd) test.TestUpdate( // valid validNewPersistentVolumeClaim("foo", api.NamespaceDefault), // updateFunc func(obj runtime.Object) runtime.Object { object := obj.(*api.PersistentVolumeClaim) object.Spec.Resources = api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("20G"), }, } return object }, ) }
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 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 }
// populateResourceList takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2> func populateResourceList(spec string) (api.ResourceList, error) { // empty input gets a nil response to preserve generator test expected behaviors if spec == "" { return nil, nil } result := api.ResourceList{} resourceStatements := strings.Split(spec, ",") for _, resourceStatement := range resourceStatements { parts := strings.Split(resourceStatement, "=") if len(parts) != 2 { return nil, fmt.Errorf("Invalid argument syntax %v, expected <resource>=<value>", resourceStatement) } resourceName := api.ResourceName(parts[0]) resourceQuantity, err := resource.ParseQuantity(parts[1]) if err != nil { return nil, err } result[resourceName] = *resourceQuantity } return result, nil }
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 TestNodeDeletion(t *testing.T) { fakeNow := unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC) fakeNodeHandler := &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: unversioned.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", }, }, { ObjectMeta: api.ObjectMeta{ Name: "node1", CreationTimestamp: unversioned.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"), *newPod("pod1", "node1")}}), } nodeController := NewNodeController(nil, fakeNodeHandler, 5*time.Minute, util.NewFakeRateLimiter(), util.NewFakeRateLimiter(), testNodeMonitorGracePeriod, testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, false) nodeController.now = func() unversioned.Time { return fakeNow } if err := nodeController.monitorNodeStatus(); err != nil { t.Errorf("unexpected error: %v", err) } fakeNodeHandler.Delete("node1") if err := nodeController.monitorNodeStatus(); err != nil { t.Errorf("unexpected error: %v", err) } nodeController.podEvictor.Try(func(value TimedValue) (bool, time.Duration) { nodeController.deletePods(value.Value) return true, 0 }) podEvicted := false for _, action := range fakeNodeHandler.Actions() { if action.GetVerb() == "delete" && action.GetResource() == "pods" { podEvicted = true } } if !podEvicted { t.Error("expected pods to be evicted from the deleted node") } }
// FindBestMatchForClaim is a convenience method that finds a volume by the claim's AccessModes and requests for Storage func (pvIndex *persistentVolumeOrderedIndex) FindBestMatchForClaim(claim *api.PersistentVolumeClaim) (*api.PersistentVolume, error) { return pvIndex.findByAccessModesAndStorageCapacity(fmt.Sprintf("%s/%s", claim.Namespace, claim.Name), claim.Spec.AccessModes, claim.Spec.Resources.Requests[api.ResourceName(api.ResourceStorage)]) }
"strconv" "testing" "vulcan/kubernetes/pkg/api" "vulcan/kubernetes/pkg/api/resource" ) const ( standardMemoryAmount = 8000000000 ) var ( zeroRequestMemoryBestEffort = api.Container{ Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("5m"), api.ResourceName(api.ResourceMemory): resource.MustParse("0G"), }, Limits: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("5m"), api.ResourceName(api.ResourceMemory): resource.MustParse("10G"), }, }, } edgeMemoryBestEffort = api.Container{ Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceMemory): resource.MustParse("0G"), }, Limits: api.ResourceList{
func TestFindingPreboundVolumes(t *testing.T) { pv1 := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "pv1", Annotations: map[string]string{}, }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse("1Gi")}, PersistentVolumeSource: api.PersistentVolumeSource{HostPath: &api.HostPathVolumeSource{}}, AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, }, } pv5 := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "pv5", Annotations: map[string]string{}, }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse("5Gi")}, PersistentVolumeSource: api.PersistentVolumeSource{HostPath: &api.HostPathVolumeSource{}}, AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, }, } pv8 := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ Name: "pv8", Annotations: map[string]string{}, }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse("8Gi")}, PersistentVolumeSource: api.PersistentVolumeSource{HostPath: &api.HostPathVolumeSource{}}, AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, }, } claim := &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claim01", Namespace: "myns", }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Resources: api.ResourceRequirements{Requests: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse("1Gi")}}, }, } index := NewPersistentVolumeOrderedIndex() index.Add(pv1) index.Add(pv5) index.Add(pv8) // expected exact match on size volume, _ := index.FindBestMatchForClaim(claim) if volume.Name != pv1.Name { t.Errorf("Expected %s but got volume %s instead", pv1.Name, volume.Name) } // pretend the exact match is pre-bound. should get the next size up. pv1.Annotations[createdForKey] = "some/other/claim" volume, _ = index.FindBestMatchForClaim(claim) if volume.Name != pv5.Name { t.Errorf("Expected %s but got volume %s instead", pv5.Name, volume.Name) } // pretend the exact match is available but the largest volume is pre-bound to the claim. delete(pv1.Annotations, createdForKey) pv8.Annotations[createdForKey] = "myns/claim01" volume, _ = index.FindBestMatchForClaim(claim) if volume.Name != pv8.Name { t.Errorf("Expected %s but got volume %s instead", pv8.Name, volume.Name) } }
func TestPersistentVolumeRecycler(t *testing.T) { _, s := framework.RunAMaster(t) defer s.Close() deleteAllEtcdKeys() binderClient := client.NewOrDie(&client.Config{Host: s.URL, Version: testapi.Default.Version()}) recyclerClient := client.NewOrDie(&client.Config{Host: s.URL, Version: testapi.Default.Version()}) testClient := client.NewOrDie(&client.Config{Host: s.URL, Version: testapi.Default.Version()}) binder := volumeclaimbinder.NewPersistentVolumeClaimBinder(binderClient, 1*time.Second) binder.Run() defer binder.Stop() recycler, _ := volumeclaimbinder.NewPersistentVolumeRecycler(recyclerClient, 1*time.Second, []volume.VolumePlugin{&volume.FakeVolumePlugin{"plugin-name", volume.NewFakeVolumeHost("/tmp/fake", nil, nil)}}) recycler.Run() defer recycler.Stop() // This PV will be claimed, released, and recycled. pv := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{Name: "fake-pv"}, Spec: api.PersistentVolumeSpec{ PersistentVolumeSource: api.PersistentVolumeSource{HostPath: &api.HostPathVolumeSource{Path: "/tmp/foo"}}, Capacity: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse("10G")}, AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimRecycle, }, } pvc := &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{Name: "fake-pvc"}, Spec: api.PersistentVolumeClaimSpec{ Resources: api.ResourceRequirements{Requests: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse("5G")}}, AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, }, } w, _ := testClient.PersistentVolumes().Watch(labels.Everything(), fields.Everything(), "0") defer w.Stop() _, _ = testClient.PersistentVolumes().Create(pv) _, _ = testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvc) // wait until the binder pairs the volume and claim waitForPersistentVolumePhase(w, api.VolumeBound) // deleting a claim releases the volume, after which it can be recycled if err := testClient.PersistentVolumeClaims(api.NamespaceDefault).Delete(pvc.Name); err != nil { t.Errorf("error deleting claim %s", pvc.Name) } waitForPersistentVolumePhase(w, api.VolumeReleased) waitForPersistentVolumePhase(w, api.VolumeAvailable) // end of Recycler test. // Deleter test begins now. // tests are serial because running masters concurrently that delete keys may cause similar tests to time out deleteAllEtcdKeys() // change the reclamation policy of the PV for the next test pv.Spec.PersistentVolumeReclaimPolicy = api.PersistentVolumeReclaimDelete w, _ = testClient.PersistentVolumes().Watch(labels.Everything(), fields.Everything(), "0") defer w.Stop() _, _ = testClient.PersistentVolumes().Create(pv) _, _ = testClient.PersistentVolumeClaims(api.NamespaceDefault).Create(pvc) waitForPersistentVolumePhase(w, api.VolumeBound) // deleting a claim releases the volume, after which it can be recycled if err := testClient.PersistentVolumeClaims(api.NamespaceDefault).Delete(pvc.Name); err != nil { t.Errorf("error deleting claim %s", pvc.Name) } waitForPersistentVolumePhase(w, api.VolumeReleased) for { event := <-w.ResultChan() if event.Type == watch.Deleted { break } } }
func TestMonitorNodeStatusUpdateStatus(t *testing.T) { fakeNow := unversioned.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: unversioned.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: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionUnknown, Reason: "NodeStatusNeverUpdated", Message: fmt.Sprintf("Kubelet never posted node status."), LastHeartbeatTime: unversioned.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 kubelet exceeds grace period. // Expect Unknown status posted from node controller. { fakeNodeHandler: &FakeNodeHandler{ Existing: []*api.Node{ { ObjectMeta: api.ObjectMeta{ Name: "node0", CreationTimestamp: unversioned.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: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: unversioned.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: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: unversioned.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: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), }, Status: api.NodeStatus{ Conditions: []api.NodeCondition{ { Type: api.NodeReady, Status: api.ConditionUnknown, Reason: "NodeStatusStopUpdated", Message: fmt.Sprintf("Kubelet stopped posting node status."), LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), LastTransitionTime: unversioned.Time{Time: unversioned.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: unversioned.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, 5*time.Minute, util.NewFakeRateLimiter(), util.NewFakeRateLimiter(), testNodeMonitorGracePeriod, testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, false) nodeController.now = func() unversioned.Time { return fakeNow } if err := nodeController.monitorNodeStatus(); err != nil { t.Errorf("unexpected error: %v", err) } if item.timeToPass > 0 { nodeController.now = func() unversioned.Time { return unversioned.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]) } } }
func TestMatchVolume(t *testing.T) { volList := NewPersistentVolumeOrderedIndex() for _, pv := range createTestVolumes() { volList.Add(pv) } scenarios := map[string]struct { expectedMatch string claim *api.PersistentVolumeClaim }{ "successful-match-gce-10": { expectedMatch: "gce-pd-10", claim: &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claim01", Namespace: "myns", }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany, api.ReadWriteOnce}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("8G"), }, }, }, }, }, "successful-match-nfs-5": { expectedMatch: "nfs-5", claim: &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claim01", Namespace: "myns", }, 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"), }, }, }, }, }, "successful-skip-1g-bound-volume": { expectedMatch: "gce-pd-5", claim: &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claim01", Namespace: "myns", }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany, api.ReadWriteOnce}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("1G"), }, }, }, }, }, "successful-no-match": { expectedMatch: "", claim: &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claim01", Namespace: "myns", }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany, api.ReadWriteOnce}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("999G"), }, }, }, }, }, } for name, scenario := range scenarios { volume, err := volList.FindBestMatchForClaim(scenario.claim) if err != nil { t.Errorf("Unexpected error matching volume by claim: %v", err) } if scenario.expectedMatch != "" && volume == nil { t.Errorf("Expected match but received nil volume for scenario: %s", name) } if scenario.expectedMatch != "" && volume != nil && string(volume.UID) != scenario.expectedMatch { t.Errorf("Expected %s but got volume %s in scenario %s", scenario.expectedMatch, volume.UID, name) } if scenario.expectedMatch == "" && volume != nil { t.Errorf("Unexpected match for scenario: %s", name) } } }
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: unversioned.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: unversioned.NewTime(time.Now()), FinishedAt: unversioned.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"}, }, // Last Terminated { container: api.Container{Name: "test", Image: "image"}, status: api.ContainerStatus{ Name: "test", State: api.ContainerState{ Running: &api.ContainerStateRunning{ StartedAt: unversioned.NewTime(time.Now()), }, }, LastTerminationState: api.ContainerState{ Terminated: &api.ContainerStateTerminated{ StartedAt: unversioned.NewTime(time.Now().Add(time.Second * 3)), FinishedAt: unversioned.NewTime(time.Now()), Reason: "crashing", ExitCode: 3, }, }, Ready: true, RestartCount: 7, }, expectedElements: []string{"test", "State", "Terminated", "Ready", "True", "Restart Count", "7", "Image", "image", "Started", "Finished", "Exit Code", "2", "crashing", "3"}, }, // 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"}, }, //env { container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}}, status: api.ContainerStatus{ Name: "test", Ready: true, RestartCount: 7, }, expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz"}, }, // 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 TestFindingVolumeWithDifferentAccessModes(t *testing.T) { gce := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{UID: "001", Name: "gce"}, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse("10G")}, PersistentVolumeSource: api.PersistentVolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}}, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, }, }, } ebs := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{UID: "002", Name: "ebs"}, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse("10G")}, PersistentVolumeSource: api.PersistentVolumeSource{AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{}}, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, }, }, } nfs := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{UID: "003", Name: "nfs"}, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse("10G")}, PersistentVolumeSource: api.PersistentVolumeSource{NFS: &api.NFSVolumeSource{}}, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, api.ReadWriteMany, }, }, } claim := &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claim01", Namespace: "myns", }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Resources: api.ResourceRequirements{Requests: api.ResourceList{api.ResourceName(api.ResourceStorage): resource.MustParse("1G")}}, }, } index := NewPersistentVolumeOrderedIndex() index.Add(gce) index.Add(ebs) index.Add(nfs) volume, _ := index.FindBestMatchForClaim(claim) if volume.Name != ebs.Name { t.Errorf("Expected %s but got volume %s instead", ebs.Name, volume.Name) } claim.Spec.AccessModes = []api.PersistentVolumeAccessMode{api.ReadWriteOnce, api.ReadOnlyMany} volume, _ = index.FindBestMatchForClaim(claim) if volume.Name != gce.Name { t.Errorf("Expected %s but got volume %s instead", gce.Name, volume.Name) } // order of the requested modes should not matter claim.Spec.AccessModes = []api.PersistentVolumeAccessMode{api.ReadWriteMany, api.ReadWriteOnce, api.ReadOnlyMany} volume, _ = index.FindBestMatchForClaim(claim) if volume.Name != nfs.Name { t.Errorf("Expected %s but got volume %s instead", nfs.Name, volume.Name) } // fewer modes requested should still match claim.Spec.AccessModes = []api.PersistentVolumeAccessMode{api.ReadWriteMany} volume, _ = index.FindBestMatchForClaim(claim) if volume.Name != nfs.Name { t.Errorf("Expected %s but got volume %s instead", nfs.Name, volume.Name) } // pretend the exact match is bound. should get the next level up of modes. ebs.Spec.ClaimRef = &api.ObjectReference{} claim.Spec.AccessModes = []api.PersistentVolumeAccessMode{api.ReadWriteOnce} volume, _ = index.FindBestMatchForClaim(claim) if volume.Name != gce.Name { t.Errorf("Expected %s but got volume %s instead", gce.Name, volume.Name) } // continue up the levels of modes. gce.Spec.ClaimRef = &api.ObjectReference{} claim.Spec.AccessModes = []api.PersistentVolumeAccessMode{api.ReadWriteOnce} volume, _ = index.FindBestMatchForClaim(claim) if volume.Name != nfs.Name { t.Errorf("Expected %s but got volume %s instead", nfs.Name, volume.Name) } // partial mode request gce.Spec.ClaimRef = nil claim.Spec.AccessModes = []api.PersistentVolumeAccessMode{api.ReadOnlyMany} volume, _ = index.FindBestMatchForClaim(claim) if volume.Name != gce.Name { t.Errorf("Expected %s but got volume %s instead", gce.Name, volume.Name) } }
func TestGetPodsTotalRequests(t *testing.T) { testCases := []struct { pods []*api.Pod expectedReqs, expectedLimits map[api.ResourceName]resource.Quantity }{ { pods: []*api.Pod{ { Spec: api.PodSpec{ Containers: []api.Container{ { Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("1"), api.ResourceName(api.ResourceMemory): resource.MustParse("300Mi"), api.ResourceName(api.ResourceStorage): resource.MustParse("1G"), }, }, }, { Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("90m"), api.ResourceName(api.ResourceMemory): resource.MustParse("120Mi"), api.ResourceName(api.ResourceStorage): resource.MustParse("200M"), }, }, }, }, }, }, { Spec: api.PodSpec{ Containers: []api.Container{ { Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("60m"), api.ResourceName(api.ResourceMemory): resource.MustParse("43Mi"), api.ResourceName(api.ResourceStorage): resource.MustParse("500M"), }, }, }, { Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("34m"), api.ResourceName(api.ResourceMemory): resource.MustParse("83Mi"), api.ResourceName(api.ResourceStorage): resource.MustParse("700M"), }, }, }, }, }, }, }, expectedReqs: map[api.ResourceName]resource.Quantity{ api.ResourceName(api.ResourceCPU): resource.MustParse("1.184"), api.ResourceName(api.ResourceMemory): resource.MustParse("546Mi"), api.ResourceName(api.ResourceStorage): resource.MustParse("2.4G"), }, }, } for _, testCase := range testCases { reqs, _, err := getPodsTotalRequestsAndLimits(testCase.pods) if err != nil { t.Errorf("Unexpected error %v", err) } if !reflect.DeepEqual(reqs, testCase.expectedReqs) { t.Errorf("Expected %v, got %v", testCase.expectedReqs, reqs) } } }
func TestMatchingWithBoundVolumes(t *testing.T) { volumeIndex := NewPersistentVolumeOrderedIndex() // two similar volumes, one is bound pv1 := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ UID: "gce-pd-1", Name: "gce001", }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("1G"), }, PersistentVolumeSource: api.PersistentVolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, }, AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce, api.ReadOnlyMany}, // this one we're pretending is already bound ClaimRef: &api.ObjectReference{UID: "abc123"}, }, } pv2 := &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{ UID: "gce-pd-2", Name: "gce002", }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("1G"), }, PersistentVolumeSource: api.PersistentVolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, }, AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce, api.ReadOnlyMany}, }, } volumeIndex.Add(pv1) volumeIndex.Add(pv2) claim := &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{ Name: "claim01", Namespace: "myns", }, Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany, api.ReadWriteOnce}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("1G"), }, }, }, } volume, err := volumeIndex.FindBestMatchForClaim(claim) if err != nil { t.Fatalf("Unexpected error matching volume by claim: %v", err) } if volume == nil { t.Fatalf("Unexpected nil volume. Expected %s", pv2.Name) } if pv2.Name != volume.Name { t.Errorf("Expected %s but got volume %s instead", pv2.Name, volume.Name) } }
func TestExampleObjects(t *testing.T) { scenarios := map[string]struct { expected interface{} }{ "claims/claim-01.yaml": { expected: &api.PersistentVolumeClaim{ Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("3Gi"), }, }, }, }, }, "claims/claim-02.yaml": { expected: &api.PersistentVolumeClaim{ Spec: api.PersistentVolumeClaimSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("8Gi"), }, }, }, }, }, "volumes/local-01.yaml": { expected: &api.PersistentVolume{ Spec: api.PersistentVolumeSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("10Gi"), }, PersistentVolumeSource: api.PersistentVolumeSource{ HostPath: &api.HostPathVolumeSource{ Path: "/tmp/data01", }, }, }, }, }, "volumes/local-02.yaml": { expected: &api.PersistentVolume{ Spec: api.PersistentVolumeSpec{ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce}, Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("8Gi"), }, PersistentVolumeSource: api.PersistentVolumeSource{ HostPath: &api.HostPathVolumeSource{ Path: "/tmp/data02", }, }, PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimRecycle, }, }, }, } for name, scenario := range scenarios { o := testclient.NewObjects(api.Scheme, api.Scheme) if err := testclient.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/"+name, o, api.Scheme); err != nil { t.Fatal(err) } client := &testclient.Fake{} client.AddReactor("*", "*", testclient.ObjectReaction(o, api.RESTMapper)) if reflect.TypeOf(scenario.expected) == reflect.TypeOf(&api.PersistentVolumeClaim{}) { pvc, err := client.PersistentVolumeClaims("ns").Get("doesntmatter") if err != nil { t.Errorf("Error retrieving object: %v", err) } expected := scenario.expected.(*api.PersistentVolumeClaim) if pvc.Spec.AccessModes[0] != expected.Spec.AccessModes[0] { t.Errorf("Unexpected mismatch. Got %v wanted %v", pvc.Spec.AccessModes[0], expected.Spec.AccessModes[0]) } aQty := pvc.Spec.Resources.Requests[api.ResourceStorage] bQty := expected.Spec.Resources.Requests[api.ResourceStorage] aSize := aQty.Value() bSize := bQty.Value() if aSize != bSize { t.Errorf("Unexpected mismatch. Got %v wanted %v", aSize, bSize) } } if reflect.TypeOf(scenario.expected) == reflect.TypeOf(&api.PersistentVolume{}) { pv, err := client.PersistentVolumes().Get("doesntmatter") if err != nil { t.Errorf("Error retrieving object: %v", err) } expected := scenario.expected.(*api.PersistentVolume) if pv.Spec.AccessModes[0] != expected.Spec.AccessModes[0] { t.Errorf("Unexpected mismatch. Got %v wanted %v", pv.Spec.AccessModes[0], expected.Spec.AccessModes[0]) } aQty := pv.Spec.Capacity[api.ResourceStorage] bQty := expected.Spec.Capacity[api.ResourceStorage] aSize := aQty.Value() bSize := bQty.Value() if aSize != bSize { t.Errorf("Unexpected mismatch. Got %v wanted %v", aSize, bSize) } if pv.Spec.HostPath.Path != expected.Spec.HostPath.Path { t.Errorf("Unexpected mismatch. Got %v wanted %v", pv.Spec.HostPath.Path, expected.Spec.HostPath.Path) } } } }
func createTestVolumes() []*api.PersistentVolume { // these volumes are deliberately out-of-order to test indexing and sorting 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{}, }, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, }, }, }, { ObjectMeta: api.ObjectMeta{ UID: "gce-pd-20", Name: "gce004", }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("20G"), }, PersistentVolumeSource: api.PersistentVolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, }, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, }, // this one we're pretending is already bound ClaimRef: &api.ObjectReference{UID: "def456"}, }, }, { 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{}, }, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, api.ReadWriteMany, }, }, }, { ObjectMeta: api.ObjectMeta{ UID: "gce-pd-1", Name: "gce001", }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("1G"), }, PersistentVolumeSource: api.PersistentVolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, }, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, }, // this one we're pretending is already bound ClaimRef: &api.ObjectReference{UID: "abc123"}, }, }, { ObjectMeta: api.ObjectMeta{ UID: "nfs-10", Name: "nfs003", }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("10G"), }, PersistentVolumeSource: api.PersistentVolumeSource{ Glusterfs: &api.GlusterfsVolumeSource{}, }, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, api.ReadWriteMany, }, }, }, { ObjectMeta: api.ObjectMeta{ UID: "gce-pd-5", Name: "gce002", }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("5G"), }, PersistentVolumeSource: api.PersistentVolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}, }, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, }, }, }, { ObjectMeta: api.ObjectMeta{ UID: "nfs-1", Name: "nfs001", }, Spec: api.PersistentVolumeSpec{ Capacity: api.ResourceList{ api.ResourceName(api.ResourceStorage): resource.MustParse("1G"), }, PersistentVolumeSource: api.PersistentVolumeSource{ Glusterfs: &api.GlusterfsVolumeSource{}, }, AccessModes: []api.PersistentVolumeAccessMode{ api.ReadWriteOnce, api.ReadOnlyMany, api.ReadWriteMany, }, }, }, } }