Merge pull request #201 from xing-yang/controller_update
Update snapshot controller
This commit is contained in:
@@ -81,16 +81,8 @@ const controllerUpdateFailMsg = "snapshot controller failed to update"
|
|||||||
|
|
||||||
// syncContent deals with one key off the queue. It returns false when it's time to quit.
|
// syncContent deals with one key off the queue. It returns false when it's time to quit.
|
||||||
func (ctrl *csiSnapshotCommonController) syncContent(content *crdv1.VolumeSnapshotContent) error {
|
func (ctrl *csiSnapshotCommonController) syncContent(content *crdv1.VolumeSnapshotContent) error {
|
||||||
klog.V(5).Infof("synchronizing VolumeSnapshotContent[%s]", content.Name)
|
|
||||||
|
|
||||||
snapshotName := utils.SnapshotRefKey(&content.Spec.VolumeSnapshotRef)
|
snapshotName := utils.SnapshotRefKey(&content.Spec.VolumeSnapshotRef)
|
||||||
|
|
||||||
if utils.NeedToAddContentFinalizer(content) {
|
|
||||||
// Content is not being deleted -> it should have the finalizer.
|
|
||||||
klog.V(5).Infof("syncContent: Add Finalizer for VolumeSnapshotContent[%s]", content.Name)
|
|
||||||
return ctrl.addContentFinalizer(content)
|
|
||||||
}
|
|
||||||
|
|
||||||
klog.V(4).Infof("synchronizing VolumeSnapshotContent[%s]: content is bound to snapshot %s", content.Name, snapshotName)
|
klog.V(4).Infof("synchronizing VolumeSnapshotContent[%s]: content is bound to snapshot %s", content.Name, snapshotName)
|
||||||
// The VolumeSnapshotContent is reserved for a VolumeSnapshot;
|
// The VolumeSnapshotContent is reserved for a VolumeSnapshot;
|
||||||
// that VolumeSnapshot has not yet been bound to this VolumeSnapshotContent; the VolumeSnapshot sync will handle it.
|
// that VolumeSnapshot has not yet been bound to this VolumeSnapshotContent; the VolumeSnapshot sync will handle it.
|
||||||
@@ -98,23 +90,23 @@ func (ctrl *csiSnapshotCommonController) syncContent(content *crdv1.VolumeSnapsh
|
|||||||
klog.V(4).Infof("synchronizing VolumeSnapshotContent[%s]: VolumeSnapshotContent is pre-bound to VolumeSnapshot %s", content.Name, snapshotName)
|
klog.V(4).Infof("synchronizing VolumeSnapshotContent[%s]: VolumeSnapshotContent is pre-bound to VolumeSnapshot %s", content.Name, snapshotName)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Get the VolumeSnapshot by _name_
|
|
||||||
|
if utils.NeedToAddContentFinalizer(content) {
|
||||||
|
// Content is not being deleted -> it should have the finalizer.
|
||||||
|
klog.V(5).Infof("syncContent: Add Finalizer for VolumeSnapshotContent[%s]", content.Name)
|
||||||
|
return ctrl.addContentFinalizer(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if snapshot exists in cache store
|
||||||
|
// If getSnapshotFromStore returns (nil, nil), it means snapshot not found
|
||||||
|
// and it may have already been deleted, and it will fall into the
|
||||||
|
// snapshot == nil case below
|
||||||
var snapshot *crdv1.VolumeSnapshot
|
var snapshot *crdv1.VolumeSnapshot
|
||||||
obj, found, err := ctrl.snapshotStore.GetByKey(snapshotName)
|
snapshot, err := ctrl.getSnapshotFromStore(snapshotName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !found {
|
|
||||||
klog.V(4).Infof("synchronizing VolumeSnapshotContent[%s]: snapshot %s not found", content.Name, snapshotName)
|
|
||||||
// Fall through with snapshot = nil
|
|
||||||
} else {
|
|
||||||
var ok bool
|
|
||||||
snapshot, ok = obj.(*crdv1.VolumeSnapshot)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("cannot convert object from snapshot cache to snapshot %q!?: %#v", content.Name, obj)
|
|
||||||
}
|
|
||||||
klog.V(4).Infof("synchronizing VolumeSnapshotContent[%s]: snapshot %s found", content.Name, snapshotName)
|
|
||||||
}
|
|
||||||
if snapshot != nil && snapshot.UID != content.Spec.VolumeSnapshotRef.UID {
|
if snapshot != nil && snapshot.UID != content.Spec.VolumeSnapshotRef.UID {
|
||||||
// The snapshot that the content was pointing to was deleted, and another
|
// The snapshot that the content was pointing to was deleted, and another
|
||||||
// with the same name created.
|
// with the same name created.
|
||||||
@@ -122,8 +114,9 @@ func (ctrl *csiSnapshotCommonController) syncContent(content *crdv1.VolumeSnapsh
|
|||||||
// Treat the content as bound to a missing snapshot.
|
// Treat the content as bound to a missing snapshot.
|
||||||
snapshot = nil
|
snapshot = nil
|
||||||
} else {
|
} else {
|
||||||
// Check if content status is set to true and update snapshot status if so
|
// Check if snapshot.Status is different from content.Status and add snapshot to queue
|
||||||
if snapshot != nil && content.Status != nil && content.Status.ReadyToUse != nil && *content.Status.ReadyToUse == true {
|
// if there is a difference and it is worth triggering an snapshot status update.
|
||||||
|
if snapshot != nil && ctrl.needsUpdateSnapshotStatus(snapshot, content) {
|
||||||
klog.V(4).Infof("synchronizing VolumeSnapshotContent for snapshot [%s]: update snapshot status to true if needed.", snapshotName)
|
klog.V(4).Infof("synchronizing VolumeSnapshotContent for snapshot [%s]: update snapshot status to true if needed.", snapshotName)
|
||||||
// Manually trigger a snapshot status update to happen
|
// Manually trigger a snapshot status update to happen
|
||||||
// right away so that it is in-sync with the content status
|
// right away so that it is in-sync with the content status
|
||||||
@@ -131,33 +124,25 @@ func (ctrl *csiSnapshotCommonController) syncContent(content *crdv1.VolumeSnapsh
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger content deletion if snapshot has deletion
|
// NOTE(xyang): Do not trigger content deletion if
|
||||||
// timestamp or snapshot does not exist any more
|
// snapshot is nil. This is to avoid data loss if
|
||||||
|
// the user copied the yaml files and expect it to work
|
||||||
|
// in a different setup. In this case snapshot is nil.
|
||||||
|
// If we trigger content deletion, it will delete
|
||||||
|
// physical snapshot resource on the storage system
|
||||||
|
// and result in data loss!
|
||||||
|
//
|
||||||
|
// Trigger content deletion if snapshot is not nil
|
||||||
|
// and snapshot has deletion timestamp.
|
||||||
// If snapshot has deletion timestamp and finalizers, set
|
// If snapshot has deletion timestamp and finalizers, set
|
||||||
// AnnVolumeSnapshotBeingDeleted annotation on the content.
|
// AnnVolumeSnapshotBeingDeleted annotation on the content.
|
||||||
// This may trigger the deletion of the content in the
|
// This may trigger the deletion of the content in the
|
||||||
// sidecar controller depending on the deletion policy
|
// sidecar controller depending on the deletion policy
|
||||||
// on the content.
|
// on the content.
|
||||||
// Snapshot won't be deleted until content is deleted
|
// Snapshot won't be deleted until content is deleted
|
||||||
// due to the finalizer
|
// due to the finalizer.
|
||||||
if snapshot == nil || utils.IsSnapshotDeletionCandidate(snapshot) {
|
if snapshot != nil && utils.IsSnapshotDeletionCandidate(snapshot) {
|
||||||
// Set AnnVolumeSnapshotBeingDeleted if it is not set yet
|
return ctrl.setAnnVolumeSnapshotBeingDeleted(content)
|
||||||
if !metav1.HasAnnotation(content.ObjectMeta, utils.AnnVolumeSnapshotBeingDeleted) {
|
|
||||||
klog.V(5).Infof("syncContent: set annotation [%s] on content [%s].", utils.AnnVolumeSnapshotBeingDeleted, content.Name)
|
|
||||||
metav1.SetMetaDataAnnotation(&content.ObjectMeta, utils.AnnVolumeSnapshotBeingDeleted, "yes")
|
|
||||||
|
|
||||||
updateContent, err := ctrl.clientset.SnapshotV1beta1().VolumeSnapshotContents().Update(content)
|
|
||||||
if err != nil {
|
|
||||||
return newControllerUpdateError(content.Name, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = ctrl.storeContentUpdate(updateContent)
|
|
||||||
if err != nil {
|
|
||||||
klog.V(4).Infof("updating VolumeSnapshotContent[%s] error status: cannot update internal cache %v", content.Name, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
klog.V(5).Infof("syncContent: volume snapshot content %+v", content)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -171,53 +156,73 @@ func (ctrl *csiSnapshotCommonController) syncContent(content *crdv1.VolumeSnapsh
|
|||||||
func (ctrl *csiSnapshotCommonController) syncSnapshot(snapshot *crdv1.VolumeSnapshot) error {
|
func (ctrl *csiSnapshotCommonController) syncSnapshot(snapshot *crdv1.VolumeSnapshot) error {
|
||||||
klog.V(5).Infof("synchronizing VolumeSnapshot[%s]: %s", utils.SnapshotKey(snapshot), utils.GetSnapshotStatusForLogging(snapshot))
|
klog.V(5).Infof("synchronizing VolumeSnapshot[%s]: %s", utils.SnapshotKey(snapshot), utils.GetSnapshotStatusForLogging(snapshot))
|
||||||
|
|
||||||
err := ctrl.processFinalizersAndCheckandDeleteContent(snapshot)
|
if snapshot.ObjectMeta.DeletionTimestamp != nil {
|
||||||
if err != nil {
|
err := ctrl.processSnapshotWithDeletionTimestamp(snapshot)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
klog.V(5).Infof("syncSnapshot[%s]: check if we should add finalizers on snapshot", utils.SnapshotKey(snapshot))
|
||||||
|
ctrl.checkandAddSnapshotFinalizers(snapshot)
|
||||||
|
// Need to build or update snapshot.Status in following cases:
|
||||||
|
// 1) snapshot.Status is nil
|
||||||
|
// 2) snapshot.Status.ReadyToUse is false
|
||||||
|
// 3) snapshot.Status.BoundVolumeSnapshotContentName is not set
|
||||||
|
if !utils.IsSnapshotReady(snapshot) || !utils.IsBoundVolumeSnapshotContentNameSet(snapshot) {
|
||||||
|
return ctrl.syncUnreadySnapshot(snapshot)
|
||||||
|
}
|
||||||
|
return ctrl.syncReadySnapshot(snapshot)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
if !utils.IsSnapshotReady(snapshot) {
|
|
||||||
return ctrl.syncUnreadySnapshot(snapshot)
|
|
||||||
}
|
|
||||||
return ctrl.syncReadySnapshot(snapshot)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// processFinalizersAndCheckandDeleteContent processes finalizers and deletes the content when appropriate
|
// checkContentAndBoundStatus is a helper function that checks the following:
|
||||||
// It checks if contents exists, it checks if snapshot has bi-directional binding, it checks if
|
// - It checks if content exists and returns the content object if it exists and nil otherwise.
|
||||||
// finalizers should be added or removed, and it checks if content should be deleted and deletes it
|
// - It checks the deletionPolicy and returns true if policy is Delete and false otherwise.
|
||||||
// if needed.
|
// - It checks if snapshot and content are bound and returns true if bound and false otherwise.
|
||||||
func (ctrl *csiSnapshotCommonController) processFinalizersAndCheckandDeleteContent(snapshot *crdv1.VolumeSnapshot) error {
|
func (ctrl *csiSnapshotCommonController) checkContentAndBoundStatus(snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshotContent, bool, bool, error) {
|
||||||
klog.V(5).Infof("processFinalizersAndCheckandDeleteContent VolumeSnapshot[%s]: %s", utils.SnapshotKey(snapshot), utils.GetSnapshotStatusForLogging(snapshot))
|
|
||||||
|
|
||||||
// If content is deleted already, remove SnapshotBound finalizer
|
// If content is deleted already, remove SnapshotBound finalizer
|
||||||
content, err := ctrl.contentExists(snapshot)
|
content, err := ctrl.getContentFromStore(snapshot)
|
||||||
if err != nil {
|
if err != nil || content == nil {
|
||||||
return err
|
return nil, false, false, err
|
||||||
}
|
}
|
||||||
deleteContent := false
|
deleteContent := false
|
||||||
// It is possible for contentExists to return nil, nil
|
// It is possible for getContentFromStore to return nil, nil
|
||||||
if content != nil && content.Spec.DeletionPolicy == crdv1.VolumeSnapshotContentDelete {
|
if content.Spec.DeletionPolicy == crdv1.VolumeSnapshotContentDelete {
|
||||||
klog.V(5).Infof("processFinalizersAndCheckandDeleteContent: Content [%s] deletion policy [%s] is delete.", content.Name, content.Spec.DeletionPolicy)
|
klog.V(5).Infof("processFinalizersAndCheckandDeleteContent: Content [%s] deletion policy [%s] is delete.", content.Name, content.Spec.DeletionPolicy)
|
||||||
deleteContent = true
|
deleteContent = true
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshotBound := false
|
snapshotBound := false
|
||||||
// Check if the snapshot content is bound to the snapshot
|
// Check if the snapshot content is bound to the snapshot
|
||||||
if content != nil && utils.IsSnapshotBound(snapshot, content) {
|
if utils.IsSnapshotBound(snapshot, content) {
|
||||||
klog.Infof("syncSnapshot: VolumeSnapshot %s is bound to volumeSnapshotContent [%s]", snapshot.Name, content.Name)
|
klog.Infof("syncSnapshot: VolumeSnapshot %s is bound to volumeSnapshotContent [%s]", snapshot.Name, content.Name)
|
||||||
snapshotBound = true
|
snapshotBound = true
|
||||||
|
|
||||||
|
}
|
||||||
|
return content, deleteContent, snapshotBound, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// processSnapshotWithDeletionTimestamp processes finalizers and deletes the content when appropriate. It has the following steps:
|
||||||
|
// 1. Call a helper function checkContentAndBoundStatus() to check content and bound status.
|
||||||
|
// 2. Call checkandRemoveSnapshotFinalizersAndCheckandDeleteContent() with information obtained from step 1. This function name is very long but the name suggests what it does. It determines whether to remove finalizers on snapshot and whether to delete content.
|
||||||
|
// 3. Call checkandRemovePVCFinalizer() to determine whether to remove the finalizer on PVC.
|
||||||
|
func (ctrl *csiSnapshotCommonController) processSnapshotWithDeletionTimestamp(snapshot *crdv1.VolumeSnapshot) error {
|
||||||
|
klog.V(5).Infof("processSnapshotWithDeletionTimestamp VolumeSnapshot[%s]: %s", utils.SnapshotKey(snapshot), utils.GetSnapshotStatusForLogging(snapshot))
|
||||||
|
|
||||||
|
// If content is really not found in the cache store, err is nil
|
||||||
|
content, deleteContent, _, err := ctrl.checkContentAndBoundStatus(snapshot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
klog.V(5).Infof("processFinalizersAndCheckandDeleteContent[%s]: delete snapshot content and remove finalizer from snapshot if needed", utils.SnapshotKey(snapshot))
|
klog.V(5).Infof("processSnapshotWithDeletionTimestamp[%s]: delete snapshot content and remove finalizer from snapshot if needed", utils.SnapshotKey(snapshot))
|
||||||
err = ctrl.checkandRemoveSnapshotFinalizersAndCheckandDeleteContent(snapshot, content, deleteContent)
|
err = ctrl.checkandRemoveSnapshotFinalizersAndCheckandDeleteContent(snapshot, content, deleteContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
klog.V(5).Infof("processFinalizersAndCheckandDeleteContent[%s]: check if we should add finalizers on snapshot", utils.SnapshotKey(snapshot))
|
klog.V(5).Infof("processSnapshotWithDeletionTimestamp[%s]: check if we should remove finalizer on snapshot source and remove it if we can", utils.SnapshotKey(snapshot))
|
||||||
ctrl.checkandAddSnapshotFinalizers(snapshot, snapshotBound, deleteContent)
|
|
||||||
|
|
||||||
klog.V(5).Infof("processFinalizersAndCheckandDeleteContent[%s]: check if we should remove finalizer on snapshot source and remove it if we can", utils.SnapshotKey(snapshot))
|
|
||||||
|
|
||||||
// Check if we should remove finalizer on PVC and remove it if we can.
|
// Check if we should remove finalizer on PVC and remove it if we can.
|
||||||
errFinalizer := ctrl.checkandRemovePVCFinalizer(snapshot)
|
errFinalizer := ctrl.checkandRemovePVCFinalizer(snapshot)
|
||||||
@@ -229,19 +234,21 @@ func (ctrl *csiSnapshotCommonController) processFinalizersAndCheckandDeleteConte
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkandRemoveSnapshotFinalizersAndCheckandDeleteContent deletes the content and removes snapshot finalizers if needed
|
// checkandRemoveSnapshotFinalizersAndCheckandDeleteContent deletes the content and removes snapshot finalizers (VolumeSnapshotAsSourceFinalizer and VolumeSnapshotBoundFinalizer) if needed
|
||||||
func (ctrl *csiSnapshotCommonController) checkandRemoveSnapshotFinalizersAndCheckandDeleteContent(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapshotContent, deleteContent bool) error {
|
func (ctrl *csiSnapshotCommonController) checkandRemoveSnapshotFinalizersAndCheckandDeleteContent(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapshotContent, deleteContent bool) error {
|
||||||
klog.V(5).Infof("deleteContentAndSnapshotFinalizers VolumeSnapshot[%s]: %s", utils.SnapshotKey(snapshot), utils.GetSnapshotStatusForLogging(snapshot))
|
klog.V(5).Infof("checkandRemoveSnapshotFinalizersAndCheckandDeleteContent VolumeSnapshot[%s]: %s", utils.SnapshotKey(snapshot), utils.GetSnapshotStatusForLogging(snapshot))
|
||||||
|
|
||||||
var err error
|
|
||||||
// Check is snapshot deletionTimestamp is set and any finalizer is on
|
// Check is snapshot deletionTimestamp is set and any finalizer is on
|
||||||
if utils.IsSnapshotDeletionCandidate(snapshot) {
|
if utils.IsSnapshotDeletionCandidate(snapshot) {
|
||||||
// Volume snapshot should be deleted. Check if it's used
|
// Volume snapshot should be deleted. Check if it's used
|
||||||
// and remove finalizer if it's not.
|
// and remove finalizer if it's not.
|
||||||
// Check if a volume is being created from snapshot.
|
// Check if a volume is being created from snapshot.
|
||||||
inUse := ctrl.isVolumeBeingCreatedFromSnapshot(snapshot)
|
inUse := false
|
||||||
|
if content != nil {
|
||||||
|
inUse = ctrl.isVolumeBeingCreatedFromSnapshot(snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
klog.V(5).Infof("syncSnapshot[%s]: set DeletionTimeStamp on content.", utils.SnapshotKey(snapshot))
|
klog.V(5).Infof("checkandRemoveSnapshotFinalizersAndCheckandDeleteContent[%s]: set DeletionTimeStamp on content.", utils.SnapshotKey(snapshot))
|
||||||
// If content exists, set DeletionTimeStamp on the content;
|
// If content exists, set DeletionTimeStamp on the content;
|
||||||
// content won't be deleted immediately due to the finalizer
|
// content won't be deleted immediately due to the finalizer
|
||||||
if content != nil && deleteContent && !inUse {
|
if content != nil && deleteContent && !inUse {
|
||||||
@@ -253,8 +260,8 @@ func (ctrl *csiSnapshotCommonController) checkandRemoveSnapshotFinalizersAndChec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !inUse || (content == nil && err == nil) {
|
if !inUse {
|
||||||
klog.V(5).Infof("syncSnapshot: Remove Finalizer for VolumeSnapshot[%s]", utils.SnapshotKey(snapshot))
|
klog.V(5).Infof("checkandRemoveSnapshotFinalizersAndCheckandDeleteContent: Remove Finalizer for VolumeSnapshot[%s]", utils.SnapshotKey(snapshot))
|
||||||
doesContentExist := false
|
doesContentExist := false
|
||||||
if content != nil {
|
if content != nil {
|
||||||
doesContentExist = true
|
doesContentExist = true
|
||||||
@@ -266,9 +273,20 @@ func (ctrl *csiSnapshotCommonController) checkandRemoveSnapshotFinalizersAndChec
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checkandAddSnapshotFinalizers checks and adds snapshot finailzers when needed
|
// checkandAddSnapshotFinalizers checks and adds snapshot finailzers when needed
|
||||||
func (ctrl *csiSnapshotCommonController) checkandAddSnapshotFinalizers(snapshot *crdv1.VolumeSnapshot, snapshotBound bool, deleteContent bool) {
|
func (ctrl *csiSnapshotCommonController) checkandAddSnapshotFinalizers(snapshot *crdv1.VolumeSnapshot) error {
|
||||||
|
_, deleteContent, snapshotBound, err := ctrl.checkContentAndBoundStatus(snapshot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
addSourceFinalizer := false
|
addSourceFinalizer := false
|
||||||
addBoundFinalizer := false
|
addBoundFinalizer := false
|
||||||
|
// NOTE: Source finalizer will be added to snapshot if
|
||||||
|
// DeletionTimeStamp is nil and it is not set yet.
|
||||||
|
// This is because the logic to check whether a PVC is being
|
||||||
|
// created from the snapshot is expensive so we only go thru
|
||||||
|
// it when we need to remove this finalizer and make sure
|
||||||
|
// it is removed when it is not needed any more.
|
||||||
if utils.NeedToAddSnapshotAsSourceFinalizer(snapshot) {
|
if utils.NeedToAddSnapshotAsSourceFinalizer(snapshot) {
|
||||||
addSourceFinalizer = true
|
addSourceFinalizer = true
|
||||||
}
|
}
|
||||||
@@ -281,13 +299,14 @@ func (ctrl *csiSnapshotCommonController) checkandAddSnapshotFinalizers(snapshot
|
|||||||
klog.V(5).Infof("checkandAddSnapshotFinalizers: Add Finalizer for VolumeSnapshot[%s]", utils.SnapshotKey(snapshot))
|
klog.V(5).Infof("checkandAddSnapshotFinalizers: Add Finalizer for VolumeSnapshot[%s]", utils.SnapshotKey(snapshot))
|
||||||
ctrl.addSnapshotFinalizer(snapshot, addSourceFinalizer, addBoundFinalizer)
|
ctrl.addSnapshotFinalizer(snapshot, addSourceFinalizer, addBoundFinalizer)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// syncReadySnapshot checks the snapshot which has been bound to snapshot content successfully before.
|
// syncReadySnapshot checks the snapshot which has been bound to snapshot content successfully before.
|
||||||
// If there is any problem with the binding (e.g., snapshot points to a non-exist snapshot content), update the snapshot status and emit event.
|
// If there is any problem with the binding (e.g., snapshot points to a non-existent snapshot content), update the snapshot status and emit event.
|
||||||
func (ctrl *csiSnapshotCommonController) syncReadySnapshot(snapshot *crdv1.VolumeSnapshot) error {
|
func (ctrl *csiSnapshotCommonController) syncReadySnapshot(snapshot *crdv1.VolumeSnapshot) error {
|
||||||
if !utils.IsBoundVolumeSnapshotContentNameSet(snapshot) {
|
if !utils.IsBoundVolumeSnapshotContentNameSet(snapshot) {
|
||||||
return nil
|
return fmt.Errorf("snapshot %s is not bound to a content.", utils.SnapshotKey(snapshot))
|
||||||
}
|
}
|
||||||
obj, found, err := ctrl.contentStore.GetByKey(*snapshot.Status.BoundVolumeSnapshotContentName)
|
obj, found, err := ctrl.contentStore.GetByKey(*snapshot.Status.BoundVolumeSnapshotContentName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -356,10 +375,19 @@ func (ctrl *csiSnapshotCommonController) syncUnreadySnapshot(snapshot *crdv1.Vol
|
|||||||
return nil
|
return nil
|
||||||
} else { // snapshot.Spec.Source.VolumeSnapshotContentName == nil - dynamically creating snapshot
|
} else { // snapshot.Spec.Source.VolumeSnapshotContentName == nil - dynamically creating snapshot
|
||||||
klog.V(5).Infof("before getMatchSnapshotContent for snapshot %s", uniqueSnapshotName)
|
klog.V(5).Infof("before getMatchSnapshotContent for snapshot %s", uniqueSnapshotName)
|
||||||
if contentObj := ctrl.getMatchSnapshotContent(snapshot); contentObj != nil {
|
var contentObj *crdv1.VolumeSnapshotContent
|
||||||
|
contentObj, _ = ctrl.getContentFromStore(snapshot)
|
||||||
|
// Ignore err from getContentFromStore. If content not found
|
||||||
|
// in the cache store by the content name, try to search it from
|
||||||
|
// the ctrl.contentStore.List()
|
||||||
|
if contentObj == nil {
|
||||||
|
contentObj = ctrl.getMatchSnapshotContent(snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
if contentObj != nil {
|
||||||
klog.V(5).Infof("Found VolumeSnapshotContent object %s for snapshot %s", contentObj.Name, uniqueSnapshotName)
|
klog.V(5).Infof("Found VolumeSnapshotContent object %s for snapshot %s", contentObj.Name, uniqueSnapshotName)
|
||||||
if contentObj.Spec.Source.SnapshotHandle != nil {
|
if contentObj.Spec.Source.SnapshotHandle != nil {
|
||||||
ctrl.updateSnapshotErrorStatusWithEvent(snapshot, v1.EventTypeWarning, "SnapshotHandleNotFound", fmt.Sprintf("Snapshot handle not found in content %s", contentObj.Name))
|
ctrl.updateSnapshotErrorStatusWithEvent(snapshot, v1.EventTypeWarning, "SnapshotHandleSet", fmt.Sprintf("Snapshot handle should not be set in content %s for dynamic provisioning", uniqueSnapshotName))
|
||||||
return fmt.Errorf("snapshotHandle should not be set in the content for dynamic provisioning for snapshot %s", uniqueSnapshotName)
|
return fmt.Errorf("snapshotHandle should not be set in the content for dynamic provisioning for snapshot %s", uniqueSnapshotName)
|
||||||
}
|
}
|
||||||
newSnapshot, err := ctrl.bindandUpdateVolumeSnapshot(contentObj, snapshot)
|
newSnapshot, err := ctrl.bindandUpdateVolumeSnapshot(contentObj, snapshot)
|
||||||
@@ -368,25 +396,6 @@ func (ctrl *csiSnapshotCommonController) syncUnreadySnapshot(snapshot *crdv1.Vol
|
|||||||
}
|
}
|
||||||
klog.V(5).Infof("bindandUpdateVolumeSnapshot %v", newSnapshot)
|
klog.V(5).Infof("bindandUpdateVolumeSnapshot %v", newSnapshot)
|
||||||
return nil
|
return nil
|
||||||
} else if snapshot.Status != nil && snapshot.Status.BoundVolumeSnapshotContentName != nil {
|
|
||||||
contentObj, found, err := ctrl.contentStore.GetByKey(*snapshot.Status.BoundVolumeSnapshotContentName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
if snapshot.ObjectMeta.DeletionTimestamp == nil {
|
|
||||||
ctrl.updateSnapshotErrorStatusWithEvent(snapshot, v1.EventTypeWarning, "SnapshotContentNotFound", fmt.Sprintf("Content for snapshot %s not found, but deletion timestamp not set on snapshot", uniqueSnapshotName))
|
|
||||||
return fmt.Errorf("content for snapshot %s not found without deletion timestamp on snapshot", uniqueSnapshotName)
|
|
||||||
}
|
|
||||||
// NOTE: this is not an error now because we delete content before the snapshot
|
|
||||||
klog.V(5).Infof("Content for snapshot %s not found. It may be already deleted as expected.", uniqueSnapshotName)
|
|
||||||
} else {
|
|
||||||
klog.V(5).Infof("converting content object for snapshot %s", uniqueSnapshotName)
|
|
||||||
_, ok := contentObj.(*crdv1.VolumeSnapshotContent)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected volume snapshot content, got %+v", contentObj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if snapshot.Status == nil || snapshot.Status.Error == nil || isControllerUpdateFailError(snapshot.Status.Error) {
|
} else if snapshot.Status == nil || snapshot.Status.Error == nil || isControllerUpdateFailError(snapshot.Status.Error) {
|
||||||
if snapshot.Spec.Source.PersistentVolumeClaimName == nil {
|
if snapshot.Spec.Source.PersistentVolumeClaimName == nil {
|
||||||
ctrl.updateSnapshotErrorStatusWithEvent(snapshot, v1.EventTypeWarning, "SnapshotPVCSourceMissing", fmt.Sprintf("PVC source for snapshot %s is missing", uniqueSnapshotName))
|
ctrl.updateSnapshotErrorStatusWithEvent(snapshot, v1.EventTypeWarning, "SnapshotPVCSourceMissing", fmt.Sprintf("PVC source for snapshot %s is missing", uniqueSnapshotName))
|
||||||
@@ -895,6 +904,39 @@ func (ctrl *csiSnapshotCommonController) bindandUpdateVolumeSnapshot(snapshotCon
|
|||||||
return snapshotCopy, nil
|
return snapshotCopy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// needsUpdateSnapshotStatus compares snapshot status with the content status and decide
|
||||||
|
// if snapshot status needs to be updated based on content status
|
||||||
|
func (ctrl *csiSnapshotCommonController) needsUpdateSnapshotStatus(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapshotContent) bool {
|
||||||
|
klog.V(5).Infof("needsUpdateSnapshotStatus[%s]", utils.SnapshotKey(snapshot))
|
||||||
|
|
||||||
|
if snapshot.Status == nil && content.Status != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if content.Status == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if snapshot.Status.BoundVolumeSnapshotContentName == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if snapshot.Status.CreationTime == nil && content.Status.CreationTime != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if snapshot.Status.ReadyToUse == nil && content.Status.ReadyToUse != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if snapshot.Status.ReadyToUse != nil && content.Status.ReadyToUse != nil && snapshot.Status.ReadyToUse != content.Status.ReadyToUse {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if snapshot.Status.RestoreSize == nil && content.Status.RestoreSize != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if snapshot.Status.RestoreSize != nil && snapshot.Status.RestoreSize.IsZero() && content.Status.RestoreSize != nil && *content.Status.RestoreSize > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateSnapshotStatus updates snapshot status based on content status
|
// UpdateSnapshotStatus updates snapshot status based on content status
|
||||||
func (ctrl *csiSnapshotCommonController) updateSnapshotStatus(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapshotContent) (*crdv1.VolumeSnapshot, error) {
|
func (ctrl *csiSnapshotCommonController) updateSnapshotStatus(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapshotContent) (*crdv1.VolumeSnapshot, error) {
|
||||||
klog.V(5).Infof("updateSnapshotStatus[%s]", utils.SnapshotKey(snapshot))
|
klog.V(5).Infof("updateSnapshotStatus[%s]", utils.SnapshotKey(snapshot))
|
||||||
@@ -987,11 +1029,34 @@ func (ctrl *csiSnapshotCommonController) getVolumeFromVolumeSnapshot(snapshot *c
|
|||||||
return nil, fmt.Errorf("failed to retrieve PV %s from the API server: %q", pvName, err)
|
return nil, fmt.Errorf("failed to retrieve PV %s from the API server: %q", pvName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify binding between PV/PVC is still valid
|
||||||
|
bound := ctrl.isVolumeBoundToClaim(pv, pvc)
|
||||||
|
if bound == false {
|
||||||
|
klog.Warningf("binding between PV %s and PVC %s is broken", pvName, pvc.Name)
|
||||||
|
return nil, fmt.Errorf("claim in dataSource not bound or invalid")
|
||||||
|
}
|
||||||
|
|
||||||
klog.V(5).Infof("getVolumeFromVolumeSnapshot: snapshot [%s] PV name [%s]", snapshot.Name, pvName)
|
klog.V(5).Infof("getVolumeFromVolumeSnapshot: snapshot [%s] PV name [%s]", snapshot.Name, pvName)
|
||||||
|
|
||||||
return pv, nil
|
return pv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isVolumeBoundToClaim returns true, if given volume is pre-bound or bound
|
||||||
|
// to specific claim. Both claim.Name and claim.Namespace must be equal.
|
||||||
|
// If claim.UID is present in volume.Spec.ClaimRef, it must be equal too.
|
||||||
|
func (ctrl *csiSnapshotCommonController) isVolumeBoundToClaim(volume *v1.PersistentVolume, claim *v1.PersistentVolumeClaim) bool {
|
||||||
|
if volume.Spec.ClaimRef == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if claim.Name != volume.Spec.ClaimRef.Name || claim.Namespace != volume.Spec.ClaimRef.Namespace {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if volume.Spec.ClaimRef.UID != "" && claim.UID != volume.Spec.ClaimRef.UID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (ctrl *csiSnapshotCommonController) getStorageClassFromVolumeSnapshot(snapshot *crdv1.VolumeSnapshot) (*storagev1.StorageClass, error) {
|
func (ctrl *csiSnapshotCommonController) getStorageClassFromVolumeSnapshot(snapshot *crdv1.VolumeSnapshot) (*storagev1.StorageClass, error) {
|
||||||
// Get storage class from PVC or PV
|
// Get storage class from PVC or PV
|
||||||
pvc, err := ctrl.getClaimFromVolumeSnapshot(snapshot)
|
pvc, err := ctrl.getClaimFromVolumeSnapshot(snapshot)
|
||||||
@@ -1173,7 +1238,10 @@ func (ctrl *csiSnapshotCommonController) removeSnapshotFinalizer(snapshot *crdv1
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctrl *csiSnapshotCommonController) contentExists(snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshotContent, error) {
|
// getContentFromStore finds content from the cache store.
|
||||||
|
// If getContentFromStore returns (nil, nil), it means content not found
|
||||||
|
// and it may have already been deleted.
|
||||||
|
func (ctrl *csiSnapshotCommonController) getContentFromStore(snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshotContent, error) {
|
||||||
var contentName string
|
var contentName string
|
||||||
if snapshot.Status != nil && snapshot.Status.BoundVolumeSnapshotContentName != nil {
|
if snapshot.Status != nil && snapshot.Status.BoundVolumeSnapshotContentName != nil {
|
||||||
contentName = *snapshot.Status.BoundVolumeSnapshotContentName
|
contentName = *snapshot.Status.BoundVolumeSnapshotContentName
|
||||||
@@ -1196,3 +1264,51 @@ func (ctrl *csiSnapshotCommonController) contentExists(snapshot *crdv1.VolumeSna
|
|||||||
// Found in content cache store and convert object is successful
|
// Found in content cache store and convert object is successful
|
||||||
return content, nil
|
return content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getSnapshotFromStore finds snapshot from the cache store.
|
||||||
|
// If getSnapshotFromStore returns (nil, nil), it means snapshot not found
|
||||||
|
// and it may have already been deleted.
|
||||||
|
func (ctrl *csiSnapshotCommonController) getSnapshotFromStore(snapshotName string) (*crdv1.VolumeSnapshot, error) {
|
||||||
|
// Get the VolumeSnapshot by _name_
|
||||||
|
var snapshot *crdv1.VolumeSnapshot
|
||||||
|
obj, found, err := ctrl.snapshotStore.GetByKey(snapshotName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
klog.V(4).Infof("getSnapshotFromStore: snapshot %s not found", snapshotName)
|
||||||
|
// Fall through with snapshot = nil
|
||||||
|
return nil, nil
|
||||||
|
} else {
|
||||||
|
var ok bool
|
||||||
|
snapshot, ok = obj.(*crdv1.VolumeSnapshot)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cannot convert object from snapshot cache to snapshot %q!?: %#v", snapshotName, obj)
|
||||||
|
}
|
||||||
|
klog.V(4).Infof("getSnapshotFromStore: snapshot %s found", snapshotName)
|
||||||
|
}
|
||||||
|
return snapshot, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl *csiSnapshotCommonController) setAnnVolumeSnapshotBeingDeleted(content *crdv1.VolumeSnapshotContent) error {
|
||||||
|
// Set AnnVolumeSnapshotBeingDeleted if it is not set yet
|
||||||
|
if !metav1.HasAnnotation(content.ObjectMeta, utils.AnnVolumeSnapshotBeingDeleted) {
|
||||||
|
klog.V(5).Infof("setAnnVolumeSnapshotBeingDeleted: set annotation [%s] on content [%s].", utils.AnnVolumeSnapshotBeingDeleted, content.Name)
|
||||||
|
metav1.SetMetaDataAnnotation(&content.ObjectMeta, utils.AnnVolumeSnapshotBeingDeleted, "yes")
|
||||||
|
|
||||||
|
updateContent, err := ctrl.clientset.SnapshotV1beta1().VolumeSnapshotContents().Update(content)
|
||||||
|
if err != nil {
|
||||||
|
return newControllerUpdateError(content.Name, err.Error())
|
||||||
|
}
|
||||||
|
// update content if update is successful
|
||||||
|
content = updateContent
|
||||||
|
|
||||||
|
_, err = ctrl.storeContentUpdate(content)
|
||||||
|
if err != nil {
|
||||||
|
klog.V(4).Infof("setAnnVolumeSnapshotBeingDeleted for content [%s]: cannot update internal cache %v", content.Name, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
klog.V(5).Infof("setAnnVolumeSnapshotBeingDeleted: volume snapshot content %+v", content)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@@ -319,25 +319,25 @@ func NoResyncPeriodFunc() time.Duration {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// needToAddContentFinalizer checks if a Finalizer needs to be added for the volume snapshot content.
|
// NeedToAddContentFinalizer checks if a Finalizer needs to be added for the volume snapshot content.
|
||||||
func NeedToAddContentFinalizer(content *crdv1.VolumeSnapshotContent) bool {
|
func NeedToAddContentFinalizer(content *crdv1.VolumeSnapshotContent) bool {
|
||||||
return content.ObjectMeta.DeletionTimestamp == nil && !slice.ContainsString(content.ObjectMeta.Finalizers, VolumeSnapshotContentFinalizer, nil)
|
return content.ObjectMeta.DeletionTimestamp == nil && !slice.ContainsString(content.ObjectMeta.Finalizers, VolumeSnapshotContentFinalizer, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// isSnapshotDeletionCandidate checks if a volume snapshot deletionTimestamp
|
// IsSnapshotDeletionCandidate checks if a volume snapshot deletionTimestamp
|
||||||
// is set and any finalizer is on the snapshot.
|
// is set and any finalizer is on the snapshot.
|
||||||
func IsSnapshotDeletionCandidate(snapshot *crdv1.VolumeSnapshot) bool {
|
func IsSnapshotDeletionCandidate(snapshot *crdv1.VolumeSnapshot) bool {
|
||||||
return snapshot.ObjectMeta.DeletionTimestamp != nil && (slice.ContainsString(snapshot.ObjectMeta.Finalizers, VolumeSnapshotAsSourceFinalizer, nil) || slice.ContainsString(snapshot.ObjectMeta.Finalizers, VolumeSnapshotBoundFinalizer, nil))
|
return snapshot.ObjectMeta.DeletionTimestamp != nil && (slice.ContainsString(snapshot.ObjectMeta.Finalizers, VolumeSnapshotAsSourceFinalizer, nil) || slice.ContainsString(snapshot.ObjectMeta.Finalizers, VolumeSnapshotBoundFinalizer, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
// needToAddSnapshotAsSourceFinalizer checks if a Finalizer needs to be added for the volume snapshot as a source for PVC.
|
// NeedToAddSnapshotAsSourceFinalizer checks if a Finalizer needs to be added for the volume snapshot as a source for PVC.
|
||||||
func NeedToAddSnapshotAsSourceFinalizer(snapshot *crdv1.VolumeSnapshot) bool {
|
func NeedToAddSnapshotAsSourceFinalizer(snapshot *crdv1.VolumeSnapshot) bool {
|
||||||
return snapshot.ObjectMeta.DeletionTimestamp == nil && !slice.ContainsString(snapshot.ObjectMeta.Finalizers, VolumeSnapshotAsSourceFinalizer, nil)
|
return snapshot.ObjectMeta.DeletionTimestamp == nil && !slice.ContainsString(snapshot.ObjectMeta.Finalizers, VolumeSnapshotAsSourceFinalizer, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// needToAddSnapshotBoundFinalizer checks if a Finalizer needs to be added for the bound volume snapshot.
|
// NeedToAddSnapshotBoundFinalizer checks if a Finalizer needs to be added for the bound volume snapshot.
|
||||||
func NeedToAddSnapshotBoundFinalizer(snapshot *crdv1.VolumeSnapshot) bool {
|
func NeedToAddSnapshotBoundFinalizer(snapshot *crdv1.VolumeSnapshot) bool {
|
||||||
return snapshot.ObjectMeta.DeletionTimestamp == nil && !slice.ContainsString(snapshot.ObjectMeta.Finalizers, VolumeSnapshotBoundFinalizer, nil) && snapshot.Status != nil && snapshot.Status.BoundVolumeSnapshotContentName != nil
|
return snapshot.ObjectMeta.DeletionTimestamp == nil && !slice.ContainsString(snapshot.ObjectMeta.Finalizers, VolumeSnapshotBoundFinalizer, nil) && IsBoundVolumeSnapshotContentNameSet(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deprecationWarning(deprecatedParam, newParam, removalVersion string) string {
|
func deprecationWarning(deprecatedParam, newParam, removalVersion string) string {
|
||||||
|
Reference in New Issue
Block a user