func TestMissingFromIndex(t *testing.T) {
	api.ForTesting_ReferencesAllowBlankSelfLinks = true
	o := testclient.NewObjects(api.Scheme, api.Scheme)
	if err := testclient.AddObjectsFromPath("../../examples/persistent-volumes/claims/claim-01.yaml", o, api.Scheme); err != nil {
		t.Fatal(err)
	}
	if err := testclient.AddObjectsFromPath("../../examples/persistent-volumes/volumes/local-01.yaml", o, api.Scheme); err != nil {
		t.Fatal(err)
	}

	client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}

	pv, err := client.PersistentVolumes().Get("any")
	if err != nil {
		t.Error("Unexpected error getting PV from client: %v", err)
	}

	claim, error := client.PersistentVolumeClaims("ns").Get("any")
	if error != nil {
		t.Errorf("Unexpected error getting PVC from client: %v", err)
	}

	volumeIndex := NewPersistentVolumeOrderedIndex()
	mockClient := &mockBinderClient{
		volume: pv,
		claim:  claim,
	}

	// the default value of the PV is Pending.
	// if has previously been processed by the binder, it's status in etcd would be Available.
	// Only Pending volumes were being indexed and made ready for claims.
	pv.Status.Phase = api.VolumeAvailable

	// adds the volume to the index, making the volume available
	syncVolume(volumeIndex, mockClient, pv)
	if pv.Status.Phase != api.VolumeAvailable {
		t.Errorf("Expected phase %s but got %s", api.VolumeBound, pv.Status.Phase)
	}

	// an initial sync for a claim will bind it to an unbound volume, triggers state change
	err = syncClaim(volumeIndex, mockClient, claim)
	if err != nil {
		t.Fatalf("Expected Clam to be bound, instead got an error: %+v\n", err)
	}

	// state change causes another syncClaim to update statuses
	syncClaim(volumeIndex, mockClient, claim)
	// claim updated volume's status, causing an update and syncVolume call
	syncVolume(volumeIndex, mockClient, pv)

	if pv.Spec.ClaimRef == nil {
		t.Errorf("Expected ClaimRef but got nil for pv.Status.ClaimRef: %+v\n", pv)
	}

	if pv.Status.Phase != api.VolumeBound {
		t.Errorf("Expected phase %s but got %s", api.VolumeBound, pv.Status.Phase)
	}

	if claim.Status.Phase != api.ClaimBound {
		t.Errorf("Expected phase %s but got %s", api.ClaimBound, claim.Status.Phase)
	}
	if len(claim.Status.AccessModes) != len(pv.Spec.AccessModes) {
		t.Errorf("Expected phase %s but got %s", api.ClaimBound, claim.Status.Phase)
	}
	if claim.Status.AccessModes[0] != pv.Spec.AccessModes[0] {
		t.Errorf("Expected access mode %s but got %s", claim.Status.AccessModes[0], pv.Spec.AccessModes[0])
	}

	// pretend the user deleted their claim
	mockClient.claim = nil
	syncVolume(volumeIndex, mockClient, pv)

	if pv.Status.Phase != api.VolumeReleased {
		t.Errorf("Expected phase %s but got %s", api.VolumeReleased, pv.Status.Phase)
	}
}
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("../../examples/persistent-volumes/"+name, o, api.Scheme); err != nil {
			t.Fatal(err)
		}

		client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.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 TestBindingWithExamples(t *testing.T) {
	api.ForTesting_ReferencesAllowBlankSelfLinks = true
	o := testclient.NewObjects(api.Scheme, api.Scheme)
	if err := testclient.AddObjectsFromPath("../../examples/persistent-volumes/claims/claim-01.yaml", o, api.Scheme); err != nil {
		t.Fatal(err)
	}
	if err := testclient.AddObjectsFromPath("../../examples/persistent-volumes/volumes/local-01.yaml", o, api.Scheme); err != nil {
		t.Fatal(err)
	}

	client := &testclient.Fake{ReactFn: testclient.ObjectReaction(o, latest.RESTMapper)}

	pv, err := client.PersistentVolumes().Get("any")
	pv.Spec.PersistentVolumeReclaimPolicy = api.PersistentVolumeReclaimRecycle
	if err != nil {
		t.Error("Unexpected error getting PV from client: %v", err)
	}

	claim, error := client.PersistentVolumeClaims("ns").Get("any")
	if error != nil {
		t.Errorf("Unexpected error getting PVC from client: %v", err)
	}

	volumeIndex := NewPersistentVolumeOrderedIndex()
	mockClient := &mockBinderClient{
		volume: pv,
		claim:  claim,
	}

	plugMgr := volume.VolumePluginMgr{}
	plugMgr.InitPlugins(host_path.ProbeRecyclableVolumePlugins(newMockRecycler), volume.NewFakeVolumeHost("/tmp/fake", nil, nil))

	recycler := &PersistentVolumeRecycler{
		qingClient: client,
		client:     mockClient,
		pluginMgr:  plugMgr,
	}

	// adds the volume to the index, making the volume available
	syncVolume(volumeIndex, mockClient, pv)
	if pv.Status.Phase != api.VolumeAvailable {
		t.Errorf("Expected phase %s but got %s", api.VolumeBound, pv.Status.Phase)
	}

	// an initial sync for a claim will bind it to an unbound volume, triggers state change
	syncClaim(volumeIndex, mockClient, claim)
	// state change causes another syncClaim to update statuses
	syncClaim(volumeIndex, mockClient, claim)
	// claim updated volume's status, causing an update and syncVolume call
	syncVolume(volumeIndex, mockClient, pv)

	if pv.Spec.ClaimRef == nil {
		t.Errorf("Expected ClaimRef but got nil for pv.Status.ClaimRef: %+v\n", pv)
	}

	if pv.Status.Phase != api.VolumeBound {
		t.Errorf("Expected phase %s but got %s", api.VolumeBound, pv.Status.Phase)
	}

	if claim.Status.Phase != api.ClaimBound {
		t.Errorf("Expected phase %s but got %s", api.ClaimBound, claim.Status.Phase)
	}
	if len(claim.Status.AccessModes) != len(pv.Spec.AccessModes) {
		t.Errorf("Expected phase %s but got %s", api.ClaimBound, claim.Status.Phase)
	}
	if claim.Status.AccessModes[0] != pv.Spec.AccessModes[0] {
		t.Errorf("Expected access mode %s but got %s", claim.Status.AccessModes[0], pv.Spec.AccessModes[0])
	}

	// pretend the user deleted their claim
	mockClient.claim = nil
	syncVolume(volumeIndex, mockClient, pv)

	if pv.Status.Phase != api.VolumeReleased {
		t.Errorf("Expected phase %s but got %s", api.VolumeReleased, pv.Status.Phase)
	}
	if pv.Spec.ClaimRef == nil {
		t.Errorf("Expected non-nil ClaimRef: %+v", pv.Spec)
	}

	mockClient.volume = pv

	// released volumes with a PersistentVolumeReclaimPolicy (recycle/delete) can have further processing
	err = recycler.reclaimVolume(pv)
	if err != nil {
		t.Errorf("Unexpected error reclaiming volume: %+v", err)
	}
	if pv.Status.Phase != api.VolumePending {
		t.Errorf("Expected phase %s but got %s", api.VolumePending, pv.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, pv)

	if pv.Status.Phase != api.VolumeAvailable {
		t.Errorf("Expected phase %s but got %s", api.VolumeAvailable, pv.Status.Phase)
	}
	if pv.Spec.ClaimRef != nil {
		t.Errorf("Expected nil ClaimRef: %+v", pv.Spec)
	}
}