func TestBindingWithExamples(t *testing.T) { tmpDir, err := utiltesting.MkTmpdir("claimbinder-test") if err != nil { t.Fatalf("error creating temp dir: %v", err) } defer os.RemoveAll(tmpDir) codec := api.Codecs.UniversalDecoder() o := core.NewObjects(api.Scheme, codec) if err := core.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/claims/claim-01.yaml", o, codec); err != nil { t.Fatal(err) } if err := core.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/volumes/local-01.yaml", o, codec); err != nil { t.Fatal(err) } clientset := &fake.Clientset{} clientset.AddReactor("*", "*", core.ObjectReaction(o, registered.RESTMapper())) pv, err := clientset.Core().PersistentVolumes().Get("any") if err != nil { t.Errorf("Unexpected error getting PV from client: %v", err) } pv.Spec.PersistentVolumeReclaimPolicy = api.PersistentVolumeReclaimRecycle if err != nil { t.Errorf("Unexpected error getting PV from client: %v", err) } pv.ObjectMeta.SelfLink = testapi.Default.SelfLink("pv", "") // the default value of the PV is Pending. if processed at least once, its status in etcd is Available. // There was a bug where only Pending volumes were being indexed and made ready for claims. // Test that !Pending gets correctly added pv.Status.Phase = api.VolumeAvailable claim, error := clientset.Core().PersistentVolumeClaims("ns").Get("any") if error != nil { t.Errorf("Unexpected error getting PVC from client: %v", err) } claim.ObjectMeta.SelfLink = testapi.Default.SelfLink("pvc", "") volumeIndex := NewPersistentVolumeOrderedIndex() mockClient := &mockBinderClient{ volume: pv, claim: claim, } plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(host_path.ProbeRecyclableVolumePlugins(newMockRecycler, volume.VolumeConfig{}), volumetest.NewFakeVolumeHost(tmpDir, nil, nil)) recycler := &PersistentVolumeRecycler{ kubeClient: clientset, client: mockClient, pluginMgr: plugMgr, } // adds the volume to the index, making the volume available syncVolume(volumeIndex, mockClient, pv) if mockClient.volume.Status.Phase != api.VolumeAvailable { t.Errorf("Expected phase %s but got %s", api.VolumeAvailable, mockClient.volume.Status.Phase) } // add the claim to fake API server mockClient.UpdatePersistentVolumeClaim(claim) // an initial sync for a claim will bind it to an unbound volume syncClaim(volumeIndex, mockClient, claim) // bind expected on pv.Spec but status update hasn't happened yet if mockClient.volume.Spec.ClaimRef == nil { t.Errorf("Expected ClaimRef but got nil for pv.Status.ClaimRef\n") } if mockClient.volume.Status.Phase != api.VolumeAvailable { t.Errorf("Expected phase %s but got %s", api.VolumeAvailable, mockClient.volume.Status.Phase) } if mockClient.claim.Spec.VolumeName != pv.Name { t.Errorf("Expected claim.Spec.VolumeName %s but got %s", mockClient.claim.Spec.VolumeName, pv.Name) } if mockClient.claim.Status.Phase != api.ClaimBound { t.Errorf("Expected phase %s but got %s", api.ClaimBound, claim.Status.Phase) } // state changes in pvc triggers sync that sets pv attributes to pvc.Status syncClaim(volumeIndex, mockClient, claim) if len(mockClient.claim.Status.AccessModes) == 0 { t.Errorf("Expected %d access modes but got 0", len(pv.Spec.AccessModes)) } // persisting the bind to pv.Spec.ClaimRef triggers a sync syncVolume(volumeIndex, mockClient, mockClient.volume) if mockClient.volume.Status.Phase != api.VolumeBound { t.Errorf("Expected phase %s but got %s", api.VolumeBound, mockClient.volume.Status.Phase) } // pretend the user deleted their claim. periodic resync picks it up. mockClient.claim = nil syncVolume(volumeIndex, mockClient, mockClient.volume) if mockClient.volume.Status.Phase != api.VolumeReleased { t.Errorf("Expected phase %s but got %s", api.VolumeReleased, mockClient.volume.Status.Phase) } // released volumes with a PersistentVolumeReclaimPolicy (recycle/delete) can have further processing err = recycler.reclaimVolume(mockClient.volume) if err != nil { t.Errorf("Unexpected error reclaiming volume: %+v", err) } if mockClient.volume.Status.Phase != api.VolumePending { t.Errorf("Expected phase %s but got %s", api.VolumePending, mockClient.volume.Status.Phase) } // after the recycling changes the phase to Pending, the binder picks up again // to remove any vestiges of binding and make the volume Available again syncVolume(volumeIndex, mockClient, mockClient.volume) if mockClient.volume.Status.Phase != api.VolumeAvailable { t.Errorf("Expected phase %s but got %s", api.VolumeAvailable, mockClient.volume.Status.Phase) } if mockClient.volume.Spec.ClaimRef != nil { t.Errorf("Expected nil ClaimRef: %+v", mockClient.volume.Spec.ClaimRef) } }
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: "/somepath/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: "/somepath/data02", }, }, PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimRecycle, }, }, }, } for name, scenario := range scenarios { codec := api.Codecs.UniversalDecoder() o := core.NewObjects(api.Scheme, codec) if err := core.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/"+name, o, codec); err != nil { t.Fatal(err) } clientset := &fake.Clientset{} clientset.AddReactor("*", "*", core.ObjectReaction(o, registered.RESTMapper())) if reflect.TypeOf(scenario.expected) == reflect.TypeOf(&api.PersistentVolumeClaim{}) { pvc, err := clientset.Core().PersistentVolumeClaims("ns").Get("doesntmatter") if err != nil { t.Fatalf("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 := clientset.Core().PersistentVolumes().Get("doesntmatter") if err != nil { t.Fatalf("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) } } } }