func (rc *reconciler) reconciliationLoopFunc() func() { return func() { // Detaches are triggered before attaches so that volumes referenced by // pods that are rescheduled to a different node are detached first. // Ensure volumes that should be detached are detached. for _, attachedVolume := range rc.actualStateOfWorld.GetAttachedVolumes() { if !rc.desiredStateOfWorld.VolumeExists( attachedVolume.VolumeName, attachedVolume.NodeName) { // Volume exists in actual state of world but not desired // Mark desire to detach timeElapsed, err := rc.actualStateOfWorld.MarkDesireToDetach(attachedVolume.VolumeName, attachedVolume.NodeName) if err != nil { glog.Errorf("Unexpected error actualStateOfWorld.MarkDesireToDetach(): %v", err) } // Update Node Status to indicate volume is no longer safe to mount. err = rc.nodeStatusUpdater.UpdateNodeStatuses() if err != nil { // Skip detaching this volume if unable to update node status glog.Infof("UpdateNodeStatuses failed with: %v", err) continue } if !attachedVolume.MountedByNode { glog.V(5).Infof("Attempting to start DetachVolume for volume %q from node %q", attachedVolume.VolumeName, attachedVolume.NodeName) err := rc.attacherDetacher.DetachVolume(attachedVolume.AttachedVolume, true /* verifySafeToDetach */, rc.actualStateOfWorld) if err == nil { glog.Infof("Started DetachVolume for volume %q from node %q", attachedVolume.VolumeName, attachedVolume.NodeName) } if err != nil && !nestedpendingoperations.IsAlreadyExists(err) && !exponentialbackoff.IsExponentialBackoff(err) { // Ignore nestedpendingoperations.IsAlreadyExists && exponentialbackoff.IsExponentialBackoff errors, they are expected. // Log all other errors. glog.Errorf( "operationExecutor.DetachVolume failed to start for volume %q (spec.Name: %q) from node %q with err: %v", attachedVolume.VolumeName, attachedVolume.VolumeSpec.Name(), attachedVolume.NodeName, err) } } else { // If volume is not safe to detach (is mounted) wait a max amount of time before detaching anyway. if timeElapsed > rc.maxWaitForUnmountDuration { glog.V(5).Infof("Attempting to start DetachVolume for volume %q from node %q. Volume is not safe to detach, but maxWaitForUnmountDuration expired.", attachedVolume.VolumeName, attachedVolume.NodeName) err := rc.attacherDetacher.DetachVolume(attachedVolume.AttachedVolume, false /* verifySafeToDetach */, rc.actualStateOfWorld) if err == nil { glog.Infof("Started DetachVolume for volume %q from node %q due to maxWaitForUnmountDuration expiry.", attachedVolume.VolumeName, attachedVolume.NodeName) } if err != nil && !nestedpendingoperations.IsAlreadyExists(err) && !exponentialbackoff.IsExponentialBackoff(err) { // Ignore nestedpendingoperations.IsAlreadyExists && exponentialbackoff.IsExponentialBackoff errors, they are expected. // Log all other errors. glog.Errorf( "operationExecutor.DetachVolume failed to start (maxWaitForUnmountDuration expiry) for volume %q (spec.Name: %q) from node %q with err: %v", attachedVolume.VolumeName, attachedVolume.VolumeSpec.Name(), attachedVolume.NodeName, err) } } } } } // Ensure volumes that should be attached are attached. for _, volumeToAttach := range rc.desiredStateOfWorld.GetVolumesToAttach() { if rc.actualStateOfWorld.VolumeNodeExists( volumeToAttach.VolumeName, volumeToAttach.NodeName) { // Volume/Node exists, touch it to reset detachRequestedTime glog.V(12).Infof("Volume %q/Node %q is attached--touching.", volumeToAttach.VolumeName, volumeToAttach.NodeName) _, err := rc.actualStateOfWorld.AddVolumeNode( volumeToAttach.VolumeSpec, volumeToAttach.NodeName, "" /* devicePath */) if err != nil { glog.Errorf("Unexpected error on actualStateOfWorld.AddVolumeNode(): %v", err) } } else { // Volume/Node doesn't exist, spawn a goroutine to attach it glog.V(5).Infof("Attempting to start AttachVolume for volume %q to node %q", volumeToAttach.VolumeName, volumeToAttach.NodeName) err := rc.attacherDetacher.AttachVolume(volumeToAttach.VolumeToAttach, rc.actualStateOfWorld) if err == nil { glog.Infof("Started AttachVolume for volume %q to node %q", volumeToAttach.VolumeName, volumeToAttach.NodeName) } if err != nil && !nestedpendingoperations.IsAlreadyExists(err) && !exponentialbackoff.IsExponentialBackoff(err) { // Ignore nestedpendingoperations.IsAlreadyExists && exponentialbackoff.IsExponentialBackoff errors, they are expected. // Log all other errors. glog.Errorf( "operationExecutor.AttachVolume failed to start for volume %q (spec.Name: %q) to node %q with err: %v", volumeToAttach.VolumeName, volumeToAttach.VolumeSpec.Name(), volumeToAttach.NodeName, err) } } } // Update Node Status err := rc.nodeStatusUpdater.UpdateNodeStatuses() if err != nil { glog.Infof("UpdateNodeStatuses failed with: %v", err) } } }
func (rc *reconciler) reconcile() { // Detaches are triggered before attaches so that volumes referenced by // pods that are rescheduled to a different node are detached first. // Ensure volumes that should be detached are detached. for _, attachedVolume := range rc.actualStateOfWorld.GetAttachedVolumes() { if !rc.desiredStateOfWorld.VolumeExists( attachedVolume.VolumeName, attachedVolume.NodeName) { // Set the detach request time elapsedTime, err := rc.actualStateOfWorld.SetDetachRequestTime(attachedVolume.VolumeName, attachedVolume.NodeName) if err != nil { glog.Errorf("Cannot trigger detach because it fails to set detach request time with error %v", err) continue } // Check whether timeout has reached the maximum waiting time timeout := elapsedTime > rc.maxWaitForUnmountDuration // Check whether volume is still mounted. Skip detach if it is still mounted unless timeout if attachedVolume.MountedByNode && !timeout { glog.V(12).Infof("Cannot trigger detach for volume %q on node %q because volume is still mounted", attachedVolume.VolumeName, attachedVolume.NodeName) continue } // Before triggering volume detach, mark volume as detached and update the node status // If it fails to update node status, skip detach volume rc.actualStateOfWorld.RemoveVolumeFromReportAsAttached(attachedVolume.VolumeName, attachedVolume.NodeName) // Update Node Status to indicate volume is no longer safe to mount. err = rc.nodeStatusUpdater.UpdateNodeStatuses() if err != nil { // Skip detaching this volume if unable to update node status glog.Errorf("UpdateNodeStatuses failed while attempting to report volume %q as attached to node %q with: %v ", attachedVolume.VolumeName, attachedVolume.NodeName, err) continue } // Trigger detach volume which requires verifing safe to detach step // If timeout is true, skip verifySafeToDetach check glog.V(5).Infof("Attempting to start DetachVolume for volume %q from node %q", attachedVolume.VolumeName, attachedVolume.NodeName) verifySafeToDetach := !timeout err = rc.attacherDetacher.DetachVolume(attachedVolume.AttachedVolume, verifySafeToDetach, rc.actualStateOfWorld) if err == nil { if !timeout { glog.Infof("Started DetachVolume for volume %q from node %q", attachedVolume.VolumeName, attachedVolume.NodeName) } else { glog.Infof("Started DetachVolume for volume %q from node %q. This volume is not safe to detach, but maxWaitForUnmountDuration %v expired, force detaching", attachedVolume.VolumeName, attachedVolume.NodeName, rc.maxWaitForUnmountDuration) } } if err != nil && !nestedpendingoperations.IsAlreadyExists(err) && !exponentialbackoff.IsExponentialBackoff(err) { // Ignore nestedpendingoperations.IsAlreadyExists && exponentialbackoff.IsExponentialBackoff errors, they are expected. // Log all other errors. glog.Errorf( "operationExecutor.DetachVolume failed to start for volume %q (spec.Name: %q) from node %q with err: %v", attachedVolume.VolumeName, attachedVolume.VolumeSpec.Name(), attachedVolume.NodeName, err) } } } // Ensure volumes that should be attached are attached. for _, volumeToAttach := range rc.desiredStateOfWorld.GetVolumesToAttach() { if rc.actualStateOfWorld.VolumeNodeExists( volumeToAttach.VolumeName, volumeToAttach.NodeName) { // Volume/Node exists, touch it to reset detachRequestedTime glog.V(5).Infof("Volume %q/Node %q is attached--touching.", volumeToAttach.VolumeName, volumeToAttach.NodeName) rc.actualStateOfWorld.ResetDetachRequestTime(volumeToAttach.VolumeName, volumeToAttach.NodeName) } else { // Volume/Node doesn't exist, spawn a goroutine to attach it glog.V(5).Infof("Attempting to start AttachVolume for volume %q to node %q", volumeToAttach.VolumeName, volumeToAttach.NodeName) err := rc.attacherDetacher.AttachVolume(volumeToAttach.VolumeToAttach, rc.actualStateOfWorld) if err == nil { glog.Infof("Started AttachVolume for volume %q to node %q", volumeToAttach.VolumeName, volumeToAttach.NodeName) } if err != nil && !nestedpendingoperations.IsAlreadyExists(err) && !exponentialbackoff.IsExponentialBackoff(err) { // Ignore nestedpendingoperations.IsAlreadyExists && exponentialbackoff.IsExponentialBackoff errors, they are expected. // Log all other errors. glog.Errorf( "operationExecutor.AttachVolume failed to start for volume %q (spec.Name: %q) to node %q with err: %v", volumeToAttach.VolumeName, volumeToAttach.VolumeSpec.Name(), volumeToAttach.NodeName, err) } } } // Update Node Status err := rc.nodeStatusUpdater.UpdateNodeStatuses() if err != nil { glog.Infof("UpdateNodeStatuses failed with: %v", err) } }
func (rc *reconciler) reconcile() { // Unmounts are triggered before mounts so that a volume that was // referenced by a pod that was deleted and is now referenced by another // pod is unmounted from the first pod before being mounted to the new // pod. // Ensure volumes that should be unmounted are unmounted. for _, mountedVolume := range rc.actualStateOfWorld.GetMountedVolumes() { if !rc.desiredStateOfWorld.PodExistsInVolume(mountedVolume.PodName, mountedVolume.VolumeName) { // Volume is mounted, unmount it glog.V(12).Infof("Attempting to start UnmountVolume for volume %q (spec.Name: %q) from pod %q (UID: %q).", mountedVolume.VolumeName, mountedVolume.OuterVolumeSpecName, mountedVolume.PodName, mountedVolume.PodUID) err := rc.operationExecutor.UnmountVolume( mountedVolume.MountedVolume, rc.actualStateOfWorld) if err != nil && !nestedpendingoperations.IsAlreadyExists(err) && !exponentialbackoff.IsExponentialBackoff(err) { // Ignore nestedpendingoperations.IsAlreadyExists and exponentialbackoff.IsExponentialBackoff errors, they are expected. // Log all other errors. glog.Errorf( "operationExecutor.UnmountVolume failed for volume %q (spec.Name: %q) pod %q (UID: %q) controllerAttachDetachEnabled: %v with err: %v", mountedVolume.VolumeName, mountedVolume.OuterVolumeSpecName, mountedVolume.PodName, mountedVolume.PodUID, rc.controllerAttachDetachEnabled, err) } if err == nil { glog.Infof("UnmountVolume operation started for volume %q (spec.Name: %q) from pod %q (UID: %q).", mountedVolume.VolumeName, mountedVolume.OuterVolumeSpecName, mountedVolume.PodName, mountedVolume.PodUID) } } } // Ensure volumes that should be attached/mounted are attached/mounted. for _, volumeToMount := range rc.desiredStateOfWorld.GetVolumesToMount() { volMounted, devicePath, err := rc.actualStateOfWorld.PodExistsInVolume(volumeToMount.PodName, volumeToMount.VolumeName) volumeToMount.DevicePath = devicePath if cache.IsVolumeNotAttachedError(err) { if rc.controllerAttachDetachEnabled || !volumeToMount.PluginIsAttachable { // Volume is not attached (or doesn't implement attacher), kubelet attach is disabled, wait // for controller to finish attaching volume. glog.V(12).Infof("Attempting to start VerifyControllerAttachedVolume for volume %q (spec.Name: %q) pod %q (UID: %q)", volumeToMount.VolumeName, volumeToMount.VolumeSpec.Name(), volumeToMount.PodName, volumeToMount.Pod.UID) err := rc.operationExecutor.VerifyControllerAttachedVolume( volumeToMount.VolumeToMount, rc.nodeName, rc.actualStateOfWorld) if err != nil && !nestedpendingoperations.IsAlreadyExists(err) && !exponentialbackoff.IsExponentialBackoff(err) { // Ignore nestedpendingoperations.IsAlreadyExists and exponentialbackoff.IsExponentialBackoff errors, they are expected. // Log all other errors. glog.Errorf( "operationExecutor.VerifyControllerAttachedVolume failed for volume %q (spec.Name: %q) pod %q (UID: %q) controllerAttachDetachEnabled: %v with err: %v", volumeToMount.VolumeName, volumeToMount.VolumeSpec.Name(), volumeToMount.PodName, volumeToMount.Pod.UID, rc.controllerAttachDetachEnabled, err) } if err == nil { glog.Infof("VerifyControllerAttachedVolume operation started for volume %q (spec.Name: %q) pod %q (UID: %q)", volumeToMount.VolumeName, volumeToMount.VolumeSpec.Name(), volumeToMount.PodName, volumeToMount.Pod.UID) } } else { // Volume is not attached to node, kubelet attach is enabled, volume implements an attacher, // so attach it volumeToAttach := operationexecutor.VolumeToAttach{ VolumeName: volumeToMount.VolumeName, VolumeSpec: volumeToMount.VolumeSpec, NodeName: rc.nodeName, } glog.V(12).Infof("Attempting to start AttachVolume for volume %q (spec.Name: %q) pod %q (UID: %q)", volumeToMount.VolumeName, volumeToMount.VolumeSpec.Name(), volumeToMount.PodName, volumeToMount.Pod.UID) err := rc.operationExecutor.AttachVolume(volumeToAttach, rc.actualStateOfWorld) if err != nil && !nestedpendingoperations.IsAlreadyExists(err) && !exponentialbackoff.IsExponentialBackoff(err) { // Ignore nestedpendingoperations.IsAlreadyExists and exponentialbackoff.IsExponentialBackoff errors, they are expected. // Log all other errors. glog.Errorf( "operationExecutor.AttachVolume failed for volume %q (spec.Name: %q) pod %q (UID: %q) controllerAttachDetachEnabled: %v with err: %v", volumeToMount.VolumeName, volumeToMount.VolumeSpec.Name(), volumeToMount.PodName, volumeToMount.Pod.UID, rc.controllerAttachDetachEnabled, err) } if err == nil { glog.Infof("AttachVolume operation started for volume %q (spec.Name: %q) pod %q (UID: %q)", volumeToMount.VolumeName, volumeToMount.VolumeSpec.Name(), volumeToMount.PodName, volumeToMount.Pod.UID) } } } else if !volMounted || cache.IsRemountRequiredError(err) { // Volume is not mounted, or is already mounted, but requires remounting remountingLogStr := "" if cache.IsRemountRequiredError(err) { remountingLogStr = "Volume is already mounted to pod, but remount was requested." } glog.V(12).Infof("Attempting to start MountVolume for volume %q (spec.Name: %q) to pod %q (UID: %q). %s", volumeToMount.VolumeName, volumeToMount.VolumeSpec.Name(), volumeToMount.PodName, volumeToMount.Pod.UID, remountingLogStr) err := rc.operationExecutor.MountVolume( rc.waitForAttachTimeout, volumeToMount.VolumeToMount, rc.actualStateOfWorld) if err != nil && !nestedpendingoperations.IsAlreadyExists(err) && !exponentialbackoff.IsExponentialBackoff(err) { // Ignore nestedpendingoperations.IsAlreadyExists and exponentialbackoff.IsExponentialBackoff errors, they are expected. // Log all other errors. glog.Errorf( "operationExecutor.MountVolume failed for volume %q (spec.Name: %q) pod %q (UID: %q) controllerAttachDetachEnabled: %v with err: %v", volumeToMount.VolumeName, volumeToMount.VolumeSpec.Name(), volumeToMount.PodName, volumeToMount.Pod.UID, rc.controllerAttachDetachEnabled, err) } if err == nil { logMsg := fmt.Sprintf("MountVolume operation started for volume %q (spec.Name: %q) to pod %q (UID: %q). %s", volumeToMount.VolumeName, volumeToMount.VolumeSpec.Name(), volumeToMount.PodName, volumeToMount.Pod.UID, remountingLogStr) if remountingLogStr == "" { glog.V(1).Infof(logMsg) } else { glog.V(5).Infof(logMsg) } } } } // Ensure devices that should be detached/unmounted are detached/unmounted. for _, attachedVolume := range rc.actualStateOfWorld.GetUnmountedVolumes() { if !rc.desiredStateOfWorld.VolumeExists(attachedVolume.VolumeName) { if attachedVolume.GloballyMounted { // Volume is globally mounted to device, unmount it glog.V(12).Infof("Attempting to start UnmountDevice for volume %q (spec.Name: %q)", attachedVolume.VolumeName, attachedVolume.VolumeSpec.Name()) err := rc.operationExecutor.UnmountDevice( attachedVolume.AttachedVolume, rc.actualStateOfWorld, rc.mounter) if err != nil && !nestedpendingoperations.IsAlreadyExists(err) && !exponentialbackoff.IsExponentialBackoff(err) { // Ignore nestedpendingoperations.IsAlreadyExists and exponentialbackoff.IsExponentialBackoff errors, they are expected. // Log all other errors. glog.Errorf( "operationExecutor.UnmountDevice failed for volume %q (spec.Name: %q) controllerAttachDetachEnabled: %v with err: %v", attachedVolume.VolumeName, attachedVolume.VolumeSpec.Name(), rc.controllerAttachDetachEnabled, err) } if err == nil { glog.Infof("UnmountDevice operation started for volume %q (spec.Name: %q)", attachedVolume.VolumeName, attachedVolume.VolumeSpec.Name()) } } else { // Volume is attached to node, detach it if rc.controllerAttachDetachEnabled || !attachedVolume.PluginIsAttachable { // Kubelet not responsible for detaching or this volume has a non-attachable volume plugin, // so just remove it to actualStateOfWorld without attach. rc.actualStateOfWorld.MarkVolumeAsDetached( attachedVolume.VolumeName, rc.nodeName) } else { // Only detach if kubelet detach is enabled glog.V(12).Infof("Attempting to start DetachVolume for volume %q (spec.Name: %q)", attachedVolume.VolumeName, attachedVolume.VolumeSpec.Name()) err := rc.operationExecutor.DetachVolume( attachedVolume.AttachedVolume, false /* verifySafeToDetach */, rc.actualStateOfWorld) if err != nil && !nestedpendingoperations.IsAlreadyExists(err) && !exponentialbackoff.IsExponentialBackoff(err) { // Ignore nestedpendingoperations.IsAlreadyExists && exponentialbackoff.IsExponentialBackoff errors, they are expected. // Log all other errors. glog.Errorf( "operationExecutor.DetachVolume failed for volume %q (spec.Name: %q) controllerAttachDetachEnabled: %v with err: %v", attachedVolume.VolumeName, attachedVolume.VolumeSpec.Name(), rc.controllerAttachDetachEnabled, err) } if err == nil { glog.Infof("DetachVolume operation started for volume %q (spec.Name: %q)", attachedVolume.VolumeName, attachedVolume.VolumeSpec.Name()) } } } } } }