diff --git a/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller.go b/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller.go index c182684b1c2..0b29f2d3549 100644 --- a/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller.go +++ b/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller.go @@ -193,6 +193,14 @@ func (binder *PersistentVolumeClaimBinder) deleteClaim(obj interface{}) { func syncVolume(volumeIndex *persistentVolumeOrderedIndex, binderClient binderClient, volume *api.PersistentVolume) (err error) { glog.V(5).Infof("Synchronizing PersistentVolume[%s], current phase: %s\n", volume.Name, volume.Status.Phase) + // The PV may have been modified by parallel call to syncVolume, load + // the current version. + newPv, err := binderClient.GetPersistentVolume(volume.Name) + if err != nil { + return fmt.Errorf("Cannot reload volume %s: %v", volume.Name, err) + } + volume = newPv + // volumes can be in one of the following states: // // VolumePending -- default value -- not bound to a claim and not yet processed through this controller. @@ -203,12 +211,15 @@ func syncVolume(volumeIndex *persistentVolumeOrderedIndex, binderClient binderCl currentPhase := volume.Status.Phase nextPhase := currentPhase + // Always store the newest volume state in local cache. _, exists, err := volumeIndex.Get(volume) if err != nil { return err } if !exists { volumeIndex.Add(volume) + } else { + volumeIndex.Update(volume) } if isBeingProvisioned(volume) { diff --git a/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go b/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go index 2de25700e84..138b228a3c4 100644 --- a/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go +++ b/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go @@ -120,6 +120,7 @@ func TestClaimRace(t *testing.T) { volumeIndex := NewPersistentVolumeOrderedIndex() mockClient := &mockBinderClient{} + mockClient.volume = v plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(host_path.ProbeRecyclableVolumePlugins(newMockRecycler, volume.VolumeConfig{}), volume.NewFakeVolumeHost(tmpDir, nil, nil)) @@ -276,7 +277,8 @@ func TestClaimSyncAfterVolumeProvisioning(t *testing.T) { volumeIndex := NewPersistentVolumeOrderedIndex() mockClient := &mockBinderClient{ - claim: claim, + claim: claim, + volume: pv, } plugMgr := volume.VolumePluginMgr{} diff --git a/pkg/controller/persistentvolume/persistentvolume_provisioner_controller.go b/pkg/controller/persistentvolume/persistentvolume_provisioner_controller.go index 919fceae99b..5f4856f4432 100644 --- a/pkg/controller/persistentvolume/persistentvolume_provisioner_controller.go +++ b/pkg/controller/persistentvolume/persistentvolume_provisioner_controller.go @@ -225,6 +225,14 @@ func (controller *PersistentVolumeProvisionerController) reconcileClaim(claim *a func (controller *PersistentVolumeProvisionerController) reconcileVolume(pv *api.PersistentVolume) error { glog.V(5).Infof("PersistentVolume[%s] reconciling", pv.Name) + // The PV may have been modified by parallel call to reconcileVolume, load + // the current version. + newPv, err := controller.client.GetPersistentVolume(pv.Name) + if err != nil { + return fmt.Errorf("Cannot reload volume %s: %v", pv.Name, err) + } + pv = newPv + if pv.Spec.ClaimRef == nil { glog.V(5).Infof("PersistentVolume[%s] is not bound to a claim. No provisioning required", pv.Name) return nil @@ -258,7 +266,7 @@ func (controller *PersistentVolumeProvisionerController) reconcileVolume(pv *api // provisioning is incomplete. Attempt to provision the volume. glog.V(5).Infof("PersistentVolume[%s] provisioning in progress", pv.Name) - err := provisionVolume(pv, controller) + err = provisionVolume(pv, controller) if err != nil { return fmt.Errorf("Error provisioning PersistentVolume[%s]: %v", pv.Name, err) } diff --git a/pkg/controller/persistentvolume/persistentvolume_provisioner_controller_test.go b/pkg/controller/persistentvolume/persistentvolume_provisioner_controller_test.go index fe7500958c5..7e076b2a1f2 100644 --- a/pkg/controller/persistentvolume/persistentvolume_provisioner_controller_test.go +++ b/pkg/controller/persistentvolume/persistentvolume_provisioner_controller_test.go @@ -183,6 +183,7 @@ func TestReconcileVolume(t *testing.T) { controller, mockClient, mockVolumePlugin := makeTestController() pv := makeTestVolume() pvc := makeTestClaim() + mockClient.volume = pv err := controller.reconcileVolume(pv) if err != nil { @@ -197,6 +198,7 @@ func TestReconcileVolume(t *testing.T) { // pretend the claim and volume are bound, no provisioning required claimRef, _ := api.GetReference(pvc) pv.Spec.ClaimRef = claimRef + mockClient.volume = pv err = controller.reconcileVolume(pv) if err != nil { t.Errorf("Unexpected error %v", err) @@ -204,6 +206,7 @@ func TestReconcileVolume(t *testing.T) { pv.Annotations[pvProvisioningRequiredAnnotationKey] = "!pvProvisioningCompleted" pv.Annotations[qosProvisioningKey] = "foo" + mockClient.volume = pv err = controller.reconcileVolume(pv) if !isAnnotationMatch(pvProvisioningRequiredAnnotationKey, pvProvisioningCompletedAnnotationValue, mockClient.volume.Annotations) {