2016-05-30 02:22:22 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2016 The Kubernetes Authors .
2016-05-30 02:22:22 +00:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
// Package operationexecutor implements interfaces that enable execution of
2016-07-27 21:53:40 +00:00
// attach, detach, mount, and unmount operations with a
// nestedpendingoperations so that more than one operation is never triggered
// on the same volume for the same pod.
2016-05-30 02:22:22 +00:00
package operationexecutor
import (
"fmt"
"time"
"github.com/golang/glog"
2016-08-17 03:33:06 +00:00
"k8s.io/kubernetes/pkg/api/errors"
2016-11-18 20:58:56 +00:00
"k8s.io/kubernetes/pkg/api/v1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
2016-06-21 16:13:23 +00:00
"k8s.io/kubernetes/pkg/client/record"
kevents "k8s.io/kubernetes/pkg/kubelet/events"
2016-05-30 02:22:22 +00:00
"k8s.io/kubernetes/pkg/types"
2016-07-06 17:42:56 +00:00
"k8s.io/kubernetes/pkg/util/mount"
2016-05-30 02:22:22 +00:00
"k8s.io/kubernetes/pkg/volume"
2016-07-14 05:38:54 +00:00
"k8s.io/kubernetes/pkg/volume/util/nestedpendingoperations"
2016-05-30 02:22:22 +00:00
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
2016-07-14 05:38:54 +00:00
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
2016-05-30 02:22:22 +00:00
)
// OperationExecutor defines a set of operations for attaching, detaching,
2016-07-14 05:38:54 +00:00
// mounting, or unmounting a volume that are executed with a NewNestedPendingOperations which
2016-05-30 02:22:22 +00:00
// prevents more than one operation from being triggered on the same volume.
//
// These operations should be idempotent (for example, AttachVolume should
// still succeed if the volume is already attached to the node, etc.). However,
// they depend on the volume plugins to implement this behavior.
//
// Once an operation completes successfully, the actualStateOfWorld is updated
// to indicate the volume is attached/detached/mounted/unmounted.
//
// If the OperationExecutor fails to start the operation because, for example,
// an operation with the same UniqueVolumeName is already pending, a non-nil
// error is returned.
//
// Once the operation is started, since it is executed asynchronously,
// errors are simply logged and the goroutine is terminated without updating
// actualStateOfWorld (callers are responsible for retrying as needed).
2016-06-16 06:48:04 +00:00
//
// Some of these operations may result in calls to the API server; callers are
// responsible for rate limiting on errors.
2016-05-30 02:22:22 +00:00
type OperationExecutor interface {
// AttachVolume attaches the volume to the node specified in volumeToAttach.
// It then updates the actual state of the world to reflect that.
AttachVolume ( volumeToAttach VolumeToAttach , actualStateOfWorld ActualStateOfWorldAttacherUpdater ) error
2016-10-14 21:21:58 +00:00
// VerifyVolumesAreAttached verifies the given list of volumes to see whether they are still attached to the node.
// If any volume is not attached right now, it will update the actual state of the world to reflect that.
// Note that this operation could be operated concurrently with other attach/detach operations.
// In theory (but very unlikely in practise), race condition among these operations might mark volume as detached
// even if it is attached. But reconciler can correct this in a short period of time.
VerifyVolumesAreAttached ( AttachedVolumes [ ] AttachedVolume , nodeName types . NodeName , actualStateOfWorld ActualStateOfWorldAttacherUpdater ) error
2016-05-30 02:22:22 +00:00
// DetachVolume detaches the volume from the node specified in
// volumeToDetach, and updates the actual state of the world to reflect
2016-06-27 00:33:01 +00:00
// that. If verifySafeToDetach is set, a call is made to the fetch the node
// object and it is used to verify that the volume does not exist in Node's
// Status.VolumesInUse list (operation fails with error if it is).
DetachVolume ( volumeToDetach AttachedVolume , verifySafeToDetach bool , actualStateOfWorld ActualStateOfWorldAttacherUpdater ) error
2016-05-30 02:22:22 +00:00
// MountVolume mounts the volume to the pod specified in volumeToMount.
// Specifically it will:
// * Wait for the device to finish attaching (for attachable volumes only).
// * Mount device to global mount path (for attachable volumes only).
// * Update actual state of world to reflect volume is globally mounted (for
// attachable volumes only).
// * Mount the volume to the pod specific path.
// * Update actual state of world to reflect volume is mounted to the pod
// path.
MountVolume ( waitForAttachTimeout time . Duration , volumeToMount VolumeToMount , actualStateOfWorld ActualStateOfWorldMounterUpdater ) error
// UnmountVolume unmounts the volume from the pod specified in
// volumeToUnmount and updates the actual state of the world to reflect that.
UnmountVolume ( volumeToUnmount MountedVolume , actualStateOfWorld ActualStateOfWorldMounterUpdater ) error
// UnmountDevice unmounts the volumes global mount path from the device (for
// attachable volumes only, freeing it for detach. It then updates the
// actual state of the world to reflect that.
2016-07-06 17:42:56 +00:00
UnmountDevice ( deviceToDetach AttachedVolume , actualStateOfWorld ActualStateOfWorldMounterUpdater , mounter mount . Interface ) error
2016-06-16 06:48:04 +00:00
// VerifyControllerAttachedVolume checks if the specified volume is present
// in the specified nodes AttachedVolumes Status field. It uses kubeClient
// to fetch the node object.
// If the volume is found, the actual state of the world is updated to mark
// the volume as attached.
// If the volume does not implement the attacher interface, it is assumed to
2016-10-03 09:29:50 +00:00
// be attached and the actual state of the world is updated accordingly.
2016-06-16 06:48:04 +00:00
// If the volume is not found or there is an error (fetching the node
// object, for example) then an error is returned which triggers exponential
// back off on retries.
2016-07-16 06:10:29 +00:00
VerifyControllerAttachedVolume ( volumeToMount VolumeToMount , nodeName types . NodeName , actualStateOfWorld ActualStateOfWorldAttacherUpdater ) error
2016-06-23 19:46:21 +00:00
// IsOperationPending returns true if an operation for the given volumeName and podName is pending,
// otherwise it returns false
2016-11-18 20:58:56 +00:00
IsOperationPending ( volumeName v1 . UniqueVolumeName , podName volumetypes . UniquePodName ) bool
2016-05-30 02:22:22 +00:00
}
// NewOperationExecutor returns a new instance of OperationExecutor.
func NewOperationExecutor (
2016-11-18 20:58:56 +00:00
kubeClient clientset . Interface ,
2016-06-21 16:13:23 +00:00
volumePluginMgr * volume . VolumePluginMgr ,
2016-11-03 19:15:52 +00:00
recorder record . EventRecorder ,
checkNodeCapabilitiesBeforeMount bool ) OperationExecutor {
2016-06-21 16:13:23 +00:00
2016-05-30 02:22:22 +00:00
return & operationExecutor {
2016-06-16 06:48:04 +00:00
kubeClient : kubeClient ,
volumePluginMgr : volumePluginMgr ,
2016-07-14 05:38:54 +00:00
pendingOperations : nestedpendingoperations . NewNestedPendingOperations (
2016-06-16 06:48:04 +00:00
true /* exponentialBackOffOnError */ ) ,
2016-06-21 16:13:23 +00:00
recorder : recorder ,
2016-11-03 19:15:52 +00:00
checkNodeCapabilitiesBeforeMount : checkNodeCapabilitiesBeforeMount ,
2016-05-30 02:22:22 +00:00
}
}
// ActualStateOfWorldMounterUpdater defines a set of operations updating the actual
// state of the world cache after successful mount/unmount.
type ActualStateOfWorldMounterUpdater interface {
// Marks the specified volume as mounted to the specified pod
2016-11-18 20:58:56 +00:00
MarkVolumeAsMounted ( podName volumetypes . UniquePodName , podUID types . UID , volumeName v1 . UniqueVolumeName , mounter volume . Mounter , outerVolumeSpecName string , volumeGidValue string ) error
2016-05-30 02:22:22 +00:00
// Marks the specified volume as unmounted from the specified pod
2016-11-18 20:58:56 +00:00
MarkVolumeAsUnmounted ( podName volumetypes . UniquePodName , volumeName v1 . UniqueVolumeName ) error
2016-05-30 02:22:22 +00:00
// Marks the specified volume as having been globally mounted.
2016-11-18 20:58:56 +00:00
MarkDeviceAsMounted ( volumeName v1 . UniqueVolumeName ) error
2016-05-30 02:22:22 +00:00
// Marks the specified volume as having its global mount unmounted.
2016-11-18 20:58:56 +00:00
MarkDeviceAsUnmounted ( volumeName v1 . UniqueVolumeName ) error
2016-05-30 02:22:22 +00:00
}
// ActualStateOfWorldAttacherUpdater defines a set of operations updating the
// actual state of the world cache after successful attach/detach/mount/unmount.
type ActualStateOfWorldAttacherUpdater interface {
2016-07-27 21:53:40 +00:00
// Marks the specified volume as attached to the specified node. If the
// volume name is supplied, that volume name will be used. If not, the
// volume name is computed using the result from querying the plugin.
//
// TODO: in the future, we should be able to remove the volumeName
// argument to this method -- since it is used only for attachable
// volumes. See issue 29695.
2016-11-18 20:58:56 +00:00
MarkVolumeAsAttached ( volumeName v1 . UniqueVolumeName , volumeSpec * volume . Spec , nodeName types . NodeName , devicePath string ) error
2016-05-30 02:22:22 +00:00
// Marks the specified volume as detached from the specified node
2016-11-18 20:58:56 +00:00
MarkVolumeAsDetached ( volumeName v1 . UniqueVolumeName , nodeName types . NodeName )
2016-09-07 22:30:16 +00:00
// Marks desire to detach the specified volume (remove the volume from the node's
// volumesToReportedAsAttached list)
2016-11-18 20:58:56 +00:00
RemoveVolumeFromReportAsAttached ( volumeName v1 . UniqueVolumeName , nodeName types . NodeName ) error
2016-09-07 22:30:16 +00:00
// Unmarks the desire to detach for the specified volume (add the volume back to
// the node's volumesToReportedAsAttached list)
2016-11-18 20:58:56 +00:00
AddVolumeToReportAsAttached ( volumeName v1 . UniqueVolumeName , nodeName types . NodeName )
2016-05-30 02:22:22 +00:00
}
// VolumeToAttach represents a volume that should be attached to a node.
type VolumeToAttach struct {
// VolumeName is the unique identifier for the volume that should be
// attached.
2016-11-18 20:58:56 +00:00
VolumeName v1 . UniqueVolumeName
2016-05-30 02:22:22 +00:00
// VolumeSpec is a volume spec containing the specification for the volume
// that should be attached.
VolumeSpec * volume . Spec
// NodeName is the identifier for the node that the volume should be
// attached to.
2016-07-16 06:10:29 +00:00
NodeName types . NodeName
2016-08-31 23:14:56 +00:00
// scheduledPods is a map containing the set of pods that reference this
// volume and are scheduled to the underlying node. The key in the map is
// the name of the pod and the value is a pod object containing more
// information about the pod.
2016-11-18 20:58:56 +00:00
ScheduledPods [ ] * v1 . Pod
2016-05-30 02:22:22 +00:00
}
// VolumeToMount represents a volume that should be attached to this node and
// mounted to the PodName.
type VolumeToMount struct {
// VolumeName is the unique identifier for the volume that should be
// mounted.
2016-11-18 20:58:56 +00:00
VolumeName v1 . UniqueVolumeName
2016-05-30 02:22:22 +00:00
// PodName is the unique identifier for the pod that the volume should be
// mounted to after it is attached.
PodName volumetypes . UniquePodName
// VolumeSpec is a volume spec containing the specification for the volume
// that should be mounted. Used to create NewMounter. Used to generate
// InnerVolumeSpecName.
VolumeSpec * volume . Spec
// outerVolumeSpecName is the podSpec.Volume[x].Name of the volume. If the
// volume was referenced through a persistent volume claim, this contains
// the podSpec.Volume[x].Name of the persistent volume claim.
OuterVolumeSpecName string
// Pod to mount the volume to. Used to create NewMounter.
2016-11-18 20:58:56 +00:00
Pod * v1 . Pod
2016-05-30 02:22:22 +00:00
// PluginIsAttachable indicates that the plugin for this volume implements
// the volume.Attacher interface
PluginIsAttachable bool
// VolumeGidValue contains the value of the GID annotation, if present.
VolumeGidValue string
2016-06-16 06:48:04 +00:00
// DevicePath contains the path on the node where the volume is attached.
// For non-attachable volumes this is empty.
DevicePath string
2016-06-27 00:33:01 +00:00
// ReportedInUse indicates that the volume was successfully added to the
// VolumesInUse field in the node's status.
ReportedInUse bool
2016-05-30 02:22:22 +00:00
}
// AttachedVolume represents a volume that is attached to a node.
type AttachedVolume struct {
// VolumeName is the unique identifier for the volume that is attached.
2016-11-18 20:58:56 +00:00
VolumeName v1 . UniqueVolumeName
2016-05-30 02:22:22 +00:00
// VolumeSpec is the volume spec containing the specification for the
// volume that is attached.
VolumeSpec * volume . Spec
// NodeName is the identifier for the node that the volume is attached to.
2016-07-16 06:10:29 +00:00
NodeName types . NodeName
2016-05-30 02:22:22 +00:00
// PluginIsAttachable indicates that the plugin for this volume implements
// the volume.Attacher interface
PluginIsAttachable bool
2016-07-06 17:42:56 +00:00
// DevicePath contains the path on the node where the volume is attached.
// For non-attachable volumes this is empty.
DevicePath string
2016-05-30 02:22:22 +00:00
}
// MountedVolume represents a volume that has successfully been mounted to a pod.
type MountedVolume struct {
// PodName is the unique identifier of the pod mounted to.
PodName volumetypes . UniquePodName
// VolumeName is the unique identifier of the volume mounted to the pod.
2016-11-18 20:58:56 +00:00
VolumeName v1 . UniqueVolumeName
2016-05-30 02:22:22 +00:00
// InnerVolumeSpecName is the volume.Spec.Name() of the volume. If the
// volume was referenced through a persistent volume claims, this contains
// the name of the bound persistent volume object.
// It is the name that plugins use in their pod mount path, i.e.
// /var/lib/kubelet/pods/{podUID}/volumes/{escapeQualifiedPluginName}/{innerVolumeSpecName}/
// PVC example,
// apiVersion: v1
// kind: PersistentVolume
// metadata:
// name: pv0003 <- InnerVolumeSpecName
// spec:
// capacity:
// storage: 5Gi
// accessModes:
// - ReadWriteOnce
// persistentVolumeReclaimPolicy: Recycle
// nfs:
// path: /tmp
// server: 172.17.0.2
// Non-PVC example:
// apiVersion: v1
// kind: Pod
// metadata:
// name: test-pd
// spec:
// containers:
// - image: gcr.io/google_containers/test-webserver
// name: test-container
// volumeMounts:
// - mountPath: /test-pd
// name: test-volume
// volumes:
// - name: test-volume <- InnerVolumeSpecName
// gcePersistentDisk:
// pdName: my-data-disk
// fsType: ext4
InnerVolumeSpecName string
// outerVolumeSpecName is the podSpec.Volume[x].Name of the volume. If the
// volume was referenced through a persistent volume claim, this contains
// the podSpec.Volume[x].Name of the persistent volume claim.
// PVC example:
// kind: Pod
// apiVersion: v1
// metadata:
// name: mypod
// spec:
// containers:
// - name: myfrontend
// image: dockerfile/nginx
// volumeMounts:
// - mountPath: "/var/www/html"
// name: mypd
// volumes:
// - name: mypd <- OuterVolumeSpecName
// persistentVolumeClaim:
// claimName: myclaim
// Non-PVC example:
// apiVersion: v1
// kind: Pod
// metadata:
// name: test-pd
// spec:
// containers:
// - image: gcr.io/google_containers/test-webserver
// name: test-container
// volumeMounts:
// - mountPath: /test-pd
// name: test-volume
// volumes:
// - name: test-volume <- OuterVolumeSpecName
// gcePersistentDisk:
// pdName: my-data-disk
// fsType: ext4
OuterVolumeSpecName string
// PluginName is the "Unescaped Qualified" name of the volume plugin used to
// mount and unmount this volume. It can be used to fetch the volume plugin
// to unmount with, on demand. It is also the name that plugins use, though
// escaped, in their pod mount path, i.e.
// /var/lib/kubelet/pods/{podUID}/volumes/{escapeQualifiedPluginName}/{outerVolumeSpecName}/
PluginName string
// PodUID is the UID of the pod mounted to. It is also the string used by
// plugins in their pod mount path, i.e.
// /var/lib/kubelet/pods/{podUID}/volumes/{escapeQualifiedPluginName}/{outerVolumeSpecName}/
PodUID types . UID
// Mounter is the volume mounter used to mount this volume. It is required
// by kubelet to create container.VolumeMap.
Mounter volume . Mounter
// VolumeGidValue contains the value of the GID annotation, if present.
VolumeGidValue string
}
type operationExecutor struct {
2016-06-16 06:48:04 +00:00
// Used to fetch objects from the API server like Node in the
// VerifyControllerAttachedVolume operation.
2016-11-18 20:58:56 +00:00
kubeClient clientset . Interface
2016-06-16 06:48:04 +00:00
2016-05-30 02:22:22 +00:00
// volumePluginMgr is the volume plugin manager used to create volume
// plugin objects.
volumePluginMgr * volume . VolumePluginMgr
2016-06-16 06:48:04 +00:00
2016-05-30 02:22:22 +00:00
// pendingOperations keeps track of pending attach and detach operations so
// multiple operations are not started on the same volume
2016-07-14 05:38:54 +00:00
pendingOperations nestedpendingoperations . NestedPendingOperations
2016-06-21 16:13:23 +00:00
// recorder is used to record events in the API server
recorder record . EventRecorder
2016-11-03 19:15:52 +00:00
// checkNodeCapabilitiesBeforeMount, if set, enables the CanMount check,
// which verifies that the components (binaries, etc.) required to mount
// the volume are available on the underlying node before attempting mount.
checkNodeCapabilitiesBeforeMount bool
2016-05-30 02:22:22 +00:00
}
2016-11-18 20:58:56 +00:00
func ( oe * operationExecutor ) IsOperationPending ( volumeName v1 . UniqueVolumeName , podName volumetypes . UniquePodName ) bool {
2016-06-23 19:46:21 +00:00
return oe . pendingOperations . IsOperationPending ( volumeName , podName )
}
2016-05-30 02:22:22 +00:00
func ( oe * operationExecutor ) AttachVolume (
volumeToAttach VolumeToAttach ,
actualStateOfWorld ActualStateOfWorldAttacherUpdater ) error {
attachFunc , err :=
oe . generateAttachVolumeFunc ( volumeToAttach , actualStateOfWorld )
if err != nil {
return err
}
return oe . pendingOperations . Run (
2016-07-14 05:38:54 +00:00
volumeToAttach . VolumeName , "" /* podName */ , attachFunc )
2016-05-30 02:22:22 +00:00
}
func ( oe * operationExecutor ) DetachVolume (
volumeToDetach AttachedVolume ,
2016-06-27 00:33:01 +00:00
verifySafeToDetach bool ,
2016-05-30 02:22:22 +00:00
actualStateOfWorld ActualStateOfWorldAttacherUpdater ) error {
detachFunc , err :=
2016-06-27 00:33:01 +00:00
oe . generateDetachVolumeFunc ( volumeToDetach , verifySafeToDetach , actualStateOfWorld )
2016-05-30 02:22:22 +00:00
if err != nil {
return err
}
return oe . pendingOperations . Run (
2016-07-14 05:38:54 +00:00
volumeToDetach . VolumeName , "" /* podName */ , detachFunc )
2016-05-30 02:22:22 +00:00
}
2016-10-14 21:21:58 +00:00
func ( oe * operationExecutor ) VerifyVolumesAreAttached (
attachedVolumes [ ] AttachedVolume ,
nodeName types . NodeName ,
actualStateOfWorld ActualStateOfWorldAttacherUpdater ) error {
volumesAreAttachedFunc , err :=
oe . generateVolumesAreAttachedFunc ( attachedVolumes , nodeName , actualStateOfWorld )
if err != nil {
return err
}
// Give an empty UniqueVolumeName so that this operation could be executed concurrently.
return oe . pendingOperations . Run ( "" /* volumeName */ , "" /* podName */ , volumesAreAttachedFunc )
}
2016-05-30 02:22:22 +00:00
func ( oe * operationExecutor ) MountVolume (
waitForAttachTimeout time . Duration ,
volumeToMount VolumeToMount ,
actualStateOfWorld ActualStateOfWorldMounterUpdater ) error {
mountFunc , err := oe . generateMountVolumeFunc (
waitForAttachTimeout , volumeToMount , actualStateOfWorld )
if err != nil {
return err
}
2016-07-14 05:38:54 +00:00
podName := volumetypes . UniquePodName ( "" )
2016-07-27 21:53:40 +00:00
// TODO: remove this -- not necessary
2016-07-14 05:38:54 +00:00
if ! volumeToMount . PluginIsAttachable {
// Non-attachable volume plugins can execute mount for multiple pods
// referencing the same volume in parallel
podName = volumehelper . GetUniquePodName ( volumeToMount . Pod )
}
2016-05-30 02:22:22 +00:00
return oe . pendingOperations . Run (
2016-07-14 05:38:54 +00:00
volumeToMount . VolumeName , podName , mountFunc )
2016-05-30 02:22:22 +00:00
}
func ( oe * operationExecutor ) UnmountVolume (
volumeToUnmount MountedVolume ,
actualStateOfWorld ActualStateOfWorldMounterUpdater ) error {
2016-06-23 19:46:21 +00:00
2016-05-30 02:22:22 +00:00
unmountFunc , err :=
oe . generateUnmountVolumeFunc ( volumeToUnmount , actualStateOfWorld )
if err != nil {
return err
}
2016-07-14 05:38:54 +00:00
// All volume plugins can execute mount for multiple pods referencing the
// same volume in parallel
podName := volumetypes . UniquePodName ( volumeToUnmount . PodUID )
2016-05-30 02:22:22 +00:00
return oe . pendingOperations . Run (
2016-07-14 05:38:54 +00:00
volumeToUnmount . VolumeName , podName , unmountFunc )
2016-05-30 02:22:22 +00:00
}
func ( oe * operationExecutor ) UnmountDevice (
deviceToDetach AttachedVolume ,
2016-07-06 17:42:56 +00:00
actualStateOfWorld ActualStateOfWorldMounterUpdater ,
mounter mount . Interface ) error {
2016-05-30 02:22:22 +00:00
unmountDeviceFunc , err :=
2016-07-06 17:42:56 +00:00
oe . generateUnmountDeviceFunc ( deviceToDetach , actualStateOfWorld , mounter )
2016-05-30 02:22:22 +00:00
if err != nil {
return err
}
return oe . pendingOperations . Run (
2016-07-14 05:38:54 +00:00
deviceToDetach . VolumeName , "" /* podName */ , unmountDeviceFunc )
2016-05-30 02:22:22 +00:00
}
2016-06-16 06:48:04 +00:00
func ( oe * operationExecutor ) VerifyControllerAttachedVolume (
volumeToMount VolumeToMount ,
2016-07-16 06:10:29 +00:00
nodeName types . NodeName ,
2016-06-16 06:48:04 +00:00
actualStateOfWorld ActualStateOfWorldAttacherUpdater ) error {
verifyControllerAttachedVolumeFunc , err :=
oe . generateVerifyControllerAttachedVolumeFunc ( volumeToMount , nodeName , actualStateOfWorld )
if err != nil {
return err
}
return oe . pendingOperations . Run (
2016-07-14 05:38:54 +00:00
volumeToMount . VolumeName , "" /* podName */ , verifyControllerAttachedVolumeFunc )
2016-06-16 06:48:04 +00:00
}
2016-10-14 21:21:58 +00:00
func ( oe * operationExecutor ) generateVolumesAreAttachedFunc (
attachedVolumes [ ] AttachedVolume ,
nodeName types . NodeName ,
actualStateOfWorld ActualStateOfWorldAttacherUpdater ) ( func ( ) error , error ) {
// volumesPerPlugin maps from a volume plugin to a list of volume specs which belong
// to this type of plugin
volumesPerPlugin := make ( map [ string ] [ ] * volume . Spec )
// volumeSpecMap maps from a volume spec to its unique volumeName which will be used
// when calling MarkVolumeAsDetached
2016-11-18 20:58:56 +00:00
volumeSpecMap := make ( map [ * volume . Spec ] v1 . UniqueVolumeName )
2016-10-14 21:21:58 +00:00
// Iterate each volume spec and put them into a map index by the pluginName
for _ , volumeAttached := range attachedVolumes {
volumePlugin , err :=
oe . volumePluginMgr . FindPluginBySpec ( volumeAttached . VolumeSpec )
if err != nil || volumePlugin == nil {
glog . Errorf (
"VolumesAreAttached.FindPluginBySpec failed for volume %q (spec.Name: %q) on node %q with error: %v" ,
volumeAttached . VolumeName ,
volumeAttached . VolumeSpec . Name ( ) ,
volumeAttached . NodeName ,
err )
}
volumeSpecList , pluginExists := volumesPerPlugin [ volumePlugin . GetPluginName ( ) ]
if ! pluginExists {
volumeSpecList = [ ] * volume . Spec { }
}
volumeSpecList = append ( volumeSpecList , volumeAttached . VolumeSpec )
volumesPerPlugin [ volumePlugin . GetPluginName ( ) ] = volumeSpecList
volumeSpecMap [ volumeAttached . VolumeSpec ] = volumeAttached . VolumeName
}
return func ( ) error {
// For each volume plugin, pass the list of volume specs to VolumesAreAttached to check
// whether the volumes are still attached.
for pluginName , volumesSpecs := range volumesPerPlugin {
attachableVolumePlugin , err :=
oe . volumePluginMgr . FindAttachablePluginByName ( pluginName )
if err != nil || attachableVolumePlugin == nil {
glog . Errorf (
"VolumeAreAttached.FindAttachablePluginBySpec failed for plugin %q with: %v" ,
pluginName ,
err )
continue
}
volumeAttacher , newAttacherErr := attachableVolumePlugin . NewAttacher ( )
if newAttacherErr != nil {
glog . Errorf (
"VolumesAreAttached failed for getting plugin %q with: %v" ,
pluginName ,
newAttacherErr )
continue
}
attached , areAttachedErr := volumeAttacher . VolumesAreAttached ( volumesSpecs , nodeName )
if areAttachedErr != nil {
glog . Errorf (
"VolumesAreAttached failed for checking on node %q with: %v" ,
nodeName ,
areAttachedErr )
continue
}
for spec , check := range attached {
if ! check {
actualStateOfWorld . MarkVolumeAsDetached ( volumeSpecMap [ spec ] , nodeName )
glog . V ( 1 ) . Infof ( "VerifyVolumesAreAttached determined volume %q (spec.Name: %q) is no longer attached to node %q, therefore it was marked as detached." ,
2016-11-17 07:00:50 +00:00
volumeSpecMap [ spec ] , spec . Name ( ) , nodeName )
2016-10-14 21:21:58 +00:00
}
}
}
return nil
} , nil
}
2016-05-30 02:22:22 +00:00
func ( oe * operationExecutor ) generateAttachVolumeFunc (
volumeToAttach VolumeToAttach ,
actualStateOfWorld ActualStateOfWorldAttacherUpdater ) ( func ( ) error , error ) {
// Get attacher plugin
attachableVolumePlugin , err :=
oe . volumePluginMgr . FindAttachablePluginBySpec ( volumeToAttach . VolumeSpec )
if err != nil || attachableVolumePlugin == nil {
return nil , fmt . Errorf (
"AttachVolume.FindAttachablePluginBySpec failed for volume %q (spec.Name: %q) from node %q with: %v" ,
volumeToAttach . VolumeName ,
volumeToAttach . VolumeSpec . Name ( ) ,
volumeToAttach . NodeName ,
err )
}
volumeAttacher , newAttacherErr := attachableVolumePlugin . NewAttacher ( )
if newAttacherErr != nil {
return nil , fmt . Errorf (
"AttachVolume.NewAttacher failed for volume %q (spec.Name: %q) from node %q with: %v" ,
volumeToAttach . VolumeName ,
volumeToAttach . VolumeSpec . Name ( ) ,
volumeToAttach . NodeName ,
newAttacherErr )
}
return func ( ) error {
// Execute attach
2016-06-16 06:48:04 +00:00
devicePath , attachErr := volumeAttacher . Attach (
2016-05-30 02:22:22 +00:00
volumeToAttach . VolumeSpec , volumeToAttach . NodeName )
if attachErr != nil {
2016-06-16 06:48:04 +00:00
// On failure, return error. Caller will log and retry.
2016-08-31 23:14:56 +00:00
err := fmt . Errorf (
"Failed to attach volume %q on node %q with: %v" ,
2016-05-30 02:22:22 +00:00
volumeToAttach . VolumeSpec . Name ( ) ,
volumeToAttach . NodeName ,
attachErr )
2016-08-31 23:14:56 +00:00
for _ , pod := range volumeToAttach . ScheduledPods {
2016-11-18 20:58:56 +00:00
oe . recorder . Eventf ( pod , v1 . EventTypeWarning , kevents . FailedMountVolume , err . Error ( ) )
2016-08-31 23:14:56 +00:00
}
return err
2016-05-30 02:22:22 +00:00
}
glog . Infof (
"AttachVolume.Attach succeeded for volume %q (spec.Name: %q) from node %q." ,
volumeToAttach . VolumeName ,
volumeToAttach . VolumeSpec . Name ( ) ,
volumeToAttach . NodeName )
// Update actual state of world
addVolumeNodeErr := actualStateOfWorld . MarkVolumeAsAttached (
2016-11-18 20:58:56 +00:00
v1 . UniqueVolumeName ( "" ) , volumeToAttach . VolumeSpec , volumeToAttach . NodeName , devicePath )
2016-05-30 02:22:22 +00:00
if addVolumeNodeErr != nil {
2016-06-16 06:48:04 +00:00
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-12-05 23:01:33 +00:00
"AttachVolume.MarkVolumeAsAttached failed for volume %q (spec.Name: %q) from node %q with: %v" ,
2016-05-30 02:22:22 +00:00
volumeToAttach . VolumeName ,
volumeToAttach . VolumeSpec . Name ( ) ,
volumeToAttach . NodeName ,
addVolumeNodeErr )
}
return nil
} , nil
}
func ( oe * operationExecutor ) generateDetachVolumeFunc (
volumeToDetach AttachedVolume ,
2016-06-27 00:33:01 +00:00
verifySafeToDetach bool ,
2016-05-30 02:22:22 +00:00
actualStateOfWorld ActualStateOfWorldAttacherUpdater ) ( func ( ) error , error ) {
// Get attacher plugin
attachableVolumePlugin , err :=
oe . volumePluginMgr . FindAttachablePluginBySpec ( volumeToDetach . VolumeSpec )
if err != nil || attachableVolumePlugin == nil {
return nil , fmt . Errorf (
"DetachVolume.FindAttachablePluginBySpec failed for volume %q (spec.Name: %q) from node %q with: %v" ,
volumeToDetach . VolumeName ,
volumeToDetach . VolumeSpec . Name ( ) ,
volumeToDetach . NodeName ,
err )
}
volumeName , err :=
attachableVolumePlugin . GetVolumeName ( volumeToDetach . VolumeSpec )
if err != nil {
return nil , fmt . Errorf (
"DetachVolume.GetVolumeName failed for volume %q (spec.Name: %q) from node %q with: %v" ,
volumeToDetach . VolumeName ,
volumeToDetach . VolumeSpec . Name ( ) ,
volumeToDetach . NodeName ,
err )
}
volumeDetacher , err := attachableVolumePlugin . NewDetacher ( )
if err != nil {
return nil , fmt . Errorf (
"DetachVolume.NewDetacher failed for volume %q (spec.Name: %q) from node %q with: %v" ,
volumeToDetach . VolumeName ,
volumeToDetach . VolumeSpec . Name ( ) ,
volumeToDetach . NodeName ,
err )
}
return func ( ) error {
2016-09-07 22:30:16 +00:00
var err error
2016-06-27 00:33:01 +00:00
if verifySafeToDetach {
2016-09-07 22:30:16 +00:00
err = oe . verifyVolumeIsSafeToDetach ( volumeToDetach )
2016-06-27 00:33:01 +00:00
}
2016-09-07 22:30:16 +00:00
if err == nil {
err = volumeDetacher . Detach ( volumeName , volumeToDetach . NodeName )
}
if err != nil {
// On failure, add volume back to ReportAsAttached list
actualStateOfWorld . AddVolumeToReportAsAttached (
volumeToDetach . VolumeName , volumeToDetach . NodeName )
2016-06-16 06:48:04 +00:00
return fmt . Errorf (
2016-05-30 02:22:22 +00:00
"DetachVolume.Detach failed for volume %q (spec.Name: %q) from node %q with: %v" ,
volumeToDetach . VolumeName ,
volumeToDetach . VolumeSpec . Name ( ) ,
volumeToDetach . NodeName ,
2016-09-07 22:30:16 +00:00
err )
2016-05-30 02:22:22 +00:00
}
glog . Infof (
"DetachVolume.Detach succeeded for volume %q (spec.Name: %q) from node %q." ,
volumeToDetach . VolumeName ,
volumeToDetach . VolumeSpec . Name ( ) ,
volumeToDetach . NodeName )
// Update actual state of world
actualStateOfWorld . MarkVolumeAsDetached (
volumeToDetach . VolumeName , volumeToDetach . NodeName )
return nil
} , nil
2016-08-17 03:33:06 +00:00
}
func ( oe * operationExecutor ) verifyVolumeIsSafeToDetach (
volumeToDetach AttachedVolume ) error {
// Fetch current node object
2016-07-16 06:10:29 +00:00
node , fetchErr := oe . kubeClient . Core ( ) . Nodes ( ) . Get ( string ( volumeToDetach . NodeName ) )
2016-08-17 03:33:06 +00:00
if fetchErr != nil {
if errors . IsNotFound ( fetchErr ) {
glog . Warningf ( "Node %q not found on API server. DetachVolume will skip safe to detach check." ,
volumeToDetach . NodeName ,
volumeToDetach . VolumeName ,
volumeToDetach . VolumeSpec . Name ( ) )
return nil
}
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
"DetachVolume failed fetching node from API server for volume %q (spec.Name: %q) from node %q with: %v" ,
volumeToDetach . VolumeName ,
volumeToDetach . VolumeSpec . Name ( ) ,
volumeToDetach . NodeName ,
fetchErr )
}
if node == nil {
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-12-05 23:01:33 +00:00
"DetachVolume failed fetching node from API server for volume %q (spec.Name: %q) from node %q. Error: node object retrieved from API server is nil" ,
2016-08-17 03:33:06 +00:00
volumeToDetach . VolumeName ,
volumeToDetach . VolumeSpec . Name ( ) ,
volumeToDetach . NodeName )
}
for _ , inUseVolume := range node . Status . VolumesInUse {
if inUseVolume == volumeToDetach . VolumeName {
2016-12-05 23:01:33 +00:00
return fmt . Errorf ( "DetachVolume failed for volume %q (spec.Name: %q) from node %q. Error: volume is still in use by node, according to Node status" ,
2016-08-17 03:33:06 +00:00
volumeToDetach . VolumeName ,
volumeToDetach . VolumeSpec . Name ( ) ,
volumeToDetach . NodeName )
}
}
// Volume is not marked as in use by node
glog . Infof ( "Verified volume is safe to detach for volume %q (spec.Name: %q) from node %q." ,
volumeToDetach . VolumeName ,
volumeToDetach . VolumeSpec . Name ( ) ,
volumeToDetach . NodeName )
return nil
2016-05-30 02:22:22 +00:00
}
func ( oe * operationExecutor ) generateMountVolumeFunc (
waitForAttachTimeout time . Duration ,
volumeToMount VolumeToMount ,
actualStateOfWorld ActualStateOfWorldMounterUpdater ) ( func ( ) error , error ) {
// Get mounter plugin
volumePlugin , err :=
oe . volumePluginMgr . FindPluginBySpec ( volumeToMount . VolumeSpec )
if err != nil || volumePlugin == nil {
return nil , fmt . Errorf (
"MountVolume.FindPluginBySpec failed for volume %q (spec.Name: %q) pod %q (UID: %q) with: %v" ,
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID ,
err )
}
volumeMounter , newMounterErr := volumePlugin . NewMounter (
volumeToMount . VolumeSpec ,
volumeToMount . Pod ,
volume . VolumeOptions { } )
if newMounterErr != nil {
return nil , fmt . Errorf (
"MountVolume.NewMounter failed for volume %q (spec.Name: %q) pod %q (UID: %q) with: %v" ,
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID ,
newMounterErr )
}
// Get attacher, if possible
attachableVolumePlugin , _ :=
oe . volumePluginMgr . FindAttachablePluginBySpec ( volumeToMount . VolumeSpec )
var volumeAttacher volume . Attacher
if attachableVolumePlugin != nil {
volumeAttacher , _ = attachableVolumePlugin . NewAttacher ( )
}
var fsGroup * int64
if volumeToMount . Pod . Spec . SecurityContext != nil &&
volumeToMount . Pod . Spec . SecurityContext . FSGroup != nil {
fsGroup = volumeToMount . Pod . Spec . SecurityContext . FSGroup
}
return func ( ) error {
if volumeAttacher != nil {
// Wait for attachable volumes to finish attaching
glog . Infof (
2016-06-22 19:56:58 +00:00
"Entering MountVolume.WaitForAttach for volume %q (spec.Name: %q) pod %q (UID: %q) DevicePath: %q" ,
2016-05-30 02:22:22 +00:00
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
2016-06-22 19:56:58 +00:00
volumeToMount . Pod . UID ,
volumeToMount . DevicePath )
2016-05-30 02:22:22 +00:00
devicePath , err := volumeAttacher . WaitForAttach (
2016-06-16 06:48:04 +00:00
volumeToMount . VolumeSpec , volumeToMount . DevicePath , waitForAttachTimeout )
2016-05-30 02:22:22 +00:00
if err != nil {
2016-06-16 06:48:04 +00:00
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-05-30 02:22:22 +00:00
"MountVolume.WaitForAttach failed for volume %q (spec.Name: %q) pod %q (UID: %q) with: %v" ,
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID ,
err )
}
glog . Infof (
"MountVolume.WaitForAttach succeeded for volume %q (spec.Name: %q) pod %q (UID: %q)." ,
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID )
deviceMountPath , err :=
volumeAttacher . GetDeviceMountPath ( volumeToMount . VolumeSpec )
if err != nil {
2016-06-16 06:48:04 +00:00
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-05-30 02:22:22 +00:00
"MountVolume.GetDeviceMountPath failed for volume %q (spec.Name: %q) pod %q (UID: %q) with: %v" ,
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID ,
err )
}
// Mount device to global mount path
err = volumeAttacher . MountDevice (
volumeToMount . VolumeSpec ,
devicePath ,
deviceMountPath )
if err != nil {
2016-06-16 06:48:04 +00:00
// On failure, return error. Caller will log and retry.
2016-06-21 16:13:23 +00:00
err := fmt . Errorf (
2016-05-30 02:22:22 +00:00
"MountVolume.MountDevice failed for volume %q (spec.Name: %q) pod %q (UID: %q) with: %v" ,
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID ,
err )
2016-11-18 20:58:56 +00:00
oe . recorder . Eventf ( volumeToMount . Pod , v1 . EventTypeWarning , kevents . FailedMountVolume , err . Error ( ) )
2016-06-21 16:13:23 +00:00
return err
2016-05-30 02:22:22 +00:00
}
glog . Infof (
2016-09-27 23:12:57 +00:00
"MountVolume.MountDevice succeeded for volume %q (spec.Name: %q) pod %q (UID: %q) device mount path %q" ,
2016-05-30 02:22:22 +00:00
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
2016-09-27 23:12:57 +00:00
volumeToMount . Pod . UID ,
deviceMountPath )
2016-05-30 02:22:22 +00:00
// Update actual state of world to reflect volume is globally mounted
markDeviceMountedErr := actualStateOfWorld . MarkDeviceAsMounted (
volumeToMount . VolumeName )
if markDeviceMountedErr != nil {
2016-06-16 06:48:04 +00:00
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-05-30 02:22:22 +00:00
"MountVolume.MarkDeviceAsMounted failed for volume %q (spec.Name: %q) pod %q (UID: %q) with: %v" ,
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID ,
markDeviceMountedErr )
}
}
2016-11-03 19:15:52 +00:00
if oe . checkNodeCapabilitiesBeforeMount {
if canMountErr := volumeMounter . CanMount ( ) ; canMountErr != nil {
errMsg := fmt . Sprintf ( "Unable to mount volume %v (spec.Name: %v) on pod %v (UID: %v). Verify that your node machine has the required components before attempting to mount this volume type. %s" , volumeToMount . VolumeName , volumeToMount . VolumeSpec . Name ( ) , volumeToMount . Pod . Name , volumeToMount . Pod . UID , canMountErr . Error ( ) )
2016-11-18 20:58:56 +00:00
oe . recorder . Eventf ( volumeToMount . Pod , v1 . EventTypeWarning , kevents . FailedMountVolume , errMsg )
2016-11-03 19:15:52 +00:00
glog . Errorf ( errMsg )
return fmt . Errorf ( errMsg )
}
}
2016-05-30 02:22:22 +00:00
// Execute mount
mountErr := volumeMounter . SetUp ( fsGroup )
if mountErr != nil {
2016-06-16 06:48:04 +00:00
// On failure, return error. Caller will log and retry.
2016-06-21 16:13:23 +00:00
err := fmt . Errorf (
2016-05-30 02:22:22 +00:00
"MountVolume.SetUp failed for volume %q (spec.Name: %q) pod %q (UID: %q) with: %v" ,
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID ,
mountErr )
2016-11-18 20:58:56 +00:00
oe . recorder . Eventf ( volumeToMount . Pod , v1 . EventTypeWarning , kevents . FailedMountVolume , err . Error ( ) )
2016-06-21 16:13:23 +00:00
return err
2016-05-30 02:22:22 +00:00
}
glog . Infof (
"MountVolume.SetUp succeeded for volume %q (spec.Name: %q) pod %q (UID: %q)." ,
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID )
// Update actual state of world
markVolMountedErr := actualStateOfWorld . MarkVolumeAsMounted (
volumeToMount . PodName ,
volumeToMount . Pod . UID ,
volumeToMount . VolumeName ,
volumeMounter ,
volumeToMount . OuterVolumeSpecName ,
volumeToMount . VolumeGidValue )
if markVolMountedErr != nil {
2016-06-16 06:48:04 +00:00
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-05-30 02:22:22 +00:00
"MountVolume.MarkVolumeAsMounted failed for volume %q (spec.Name: %q) pod %q (UID: %q) with: %v" ,
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID ,
markVolMountedErr )
}
return nil
} , nil
}
func ( oe * operationExecutor ) generateUnmountVolumeFunc (
volumeToUnmount MountedVolume ,
actualStateOfWorld ActualStateOfWorldMounterUpdater ) ( func ( ) error , error ) {
// Get mountable plugin
volumePlugin , err :=
oe . volumePluginMgr . FindPluginByName ( volumeToUnmount . PluginName )
if err != nil || volumePlugin == nil {
return nil , fmt . Errorf (
"UnmountVolume.FindPluginByName failed for volume %q (volume.spec.Name: %q) pod %q (UID: %q) err=%v" ,
volumeToUnmount . VolumeName ,
volumeToUnmount . OuterVolumeSpecName ,
volumeToUnmount . PodName ,
volumeToUnmount . PodUID ,
err )
}
volumeUnmounter , newUnmounterErr := volumePlugin . NewUnmounter (
volumeToUnmount . InnerVolumeSpecName , volumeToUnmount . PodUID )
if newUnmounterErr != nil {
return nil , fmt . Errorf (
"UnmountVolume.NewUnmounter failed for volume %q (volume.spec.Name: %q) pod %q (UID: %q) err=%v" ,
volumeToUnmount . VolumeName ,
volumeToUnmount . OuterVolumeSpecName ,
volumeToUnmount . PodName ,
volumeToUnmount . PodUID ,
newUnmounterErr )
}
return func ( ) error {
// Execute unmount
unmountErr := volumeUnmounter . TearDown ( )
if unmountErr != nil {
2016-06-16 06:48:04 +00:00
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-05-30 02:22:22 +00:00
"UnmountVolume.TearDown failed for volume %q (volume.spec.Name: %q) pod %q (UID: %q) with: %v" ,
volumeToUnmount . VolumeName ,
volumeToUnmount . OuterVolumeSpecName ,
volumeToUnmount . PodName ,
volumeToUnmount . PodUID ,
unmountErr )
}
glog . Infof (
2016-06-23 19:46:21 +00:00
"UnmountVolume.TearDown succeeded for volume %q (OuterVolumeSpecName: %q) pod %q (UID: %q). InnerVolumeSpecName %q. PluginName %q, VolumeGidValue %q" ,
2016-05-30 02:22:22 +00:00
volumeToUnmount . VolumeName ,
volumeToUnmount . OuterVolumeSpecName ,
volumeToUnmount . PodName ,
2016-06-23 19:46:21 +00:00
volumeToUnmount . PodUID ,
volumeToUnmount . InnerVolumeSpecName ,
volumeToUnmount . PluginName ,
volumeToUnmount . VolumeGidValue )
2016-05-30 02:22:22 +00:00
// Update actual state of world
markVolMountedErr := actualStateOfWorld . MarkVolumeAsUnmounted (
volumeToUnmount . PodName , volumeToUnmount . VolumeName )
if markVolMountedErr != nil {
// On failure, just log and exit
glog . Errorf (
"UnmountVolume.MarkVolumeAsUnmounted failed for volume %q (volume.spec.Name: %q) pod %q (UID: %q) with: %v" ,
volumeToUnmount . VolumeName ,
volumeToUnmount . OuterVolumeSpecName ,
volumeToUnmount . PodName ,
volumeToUnmount . PodUID ,
unmountErr )
}
return nil
} , nil
}
func ( oe * operationExecutor ) generateUnmountDeviceFunc (
deviceToDetach AttachedVolume ,
2016-07-06 17:42:56 +00:00
actualStateOfWorld ActualStateOfWorldMounterUpdater ,
mounter mount . Interface ) ( func ( ) error , error ) {
2016-05-30 02:22:22 +00:00
// Get attacher plugin
attachableVolumePlugin , err :=
oe . volumePluginMgr . FindAttachablePluginBySpec ( deviceToDetach . VolumeSpec )
if err != nil || attachableVolumePlugin == nil {
return nil , fmt . Errorf (
"UnmountDevice.FindAttachablePluginBySpec failed for volume %q (spec.Name: %q) with: %v" ,
deviceToDetach . VolumeName ,
deviceToDetach . VolumeSpec . Name ( ) ,
err )
}
volumeDetacher , err := attachableVolumePlugin . NewDetacher ( )
if err != nil {
return nil , fmt . Errorf (
"UnmountDevice.NewDetacher failed for volume %q (spec.Name: %q) with: %v" ,
deviceToDetach . VolumeName ,
deviceToDetach . VolumeSpec . Name ( ) ,
err )
}
volumeAttacher , err := attachableVolumePlugin . NewAttacher ( )
if err != nil {
return nil , fmt . Errorf (
"UnmountDevice.NewAttacher failed for volume %q (spec.Name: %q) with: %v" ,
deviceToDetach . VolumeName ,
deviceToDetach . VolumeSpec . Name ( ) ,
err )
}
return func ( ) error {
deviceMountPath , err :=
volumeAttacher . GetDeviceMountPath ( deviceToDetach . VolumeSpec )
if err != nil {
2016-06-16 06:48:04 +00:00
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-05-30 02:22:22 +00:00
"GetDeviceMountPath failed for volume %q (spec.Name: %q) with: %v" ,
deviceToDetach . VolumeName ,
deviceToDetach . VolumeSpec . Name ( ) ,
err )
}
2016-06-23 19:46:21 +00:00
refs , err := attachableVolumePlugin . GetDeviceMountRefs ( deviceMountPath )
if err != nil || len ( refs ) > 0 {
if err == nil {
err = fmt . Errorf ( "The device mount path %q is still mounted by other references %v" , deviceMountPath , refs )
}
return fmt . Errorf (
"GetDeviceMountRefs check failed for volume %q (spec.Name: %q) with: %v" ,
deviceToDetach . VolumeName ,
deviceToDetach . VolumeSpec . Name ( ) ,
err )
}
2016-05-30 02:22:22 +00:00
// Execute unmount
unmountDeviceErr := volumeDetacher . UnmountDevice ( deviceMountPath )
if unmountDeviceErr != nil {
2016-06-16 06:48:04 +00:00
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-05-30 02:22:22 +00:00
"UnmountDevice failed for volume %q (spec.Name: %q) with: %v" ,
deviceToDetach . VolumeName ,
deviceToDetach . VolumeSpec . Name ( ) ,
unmountDeviceErr )
}
2016-07-06 17:42:56 +00:00
// Before logging that UnmountDevice succeeded and moving on,
2016-10-14 17:05:14 +00:00
// use mounter.PathIsDevice to check if the path is a device,
// if so use mounter.DeviceOpened to check if the device is in use anywhere
2016-07-06 17:42:56 +00:00
// else on the system. Retry if it returns true.
2016-10-14 17:05:14 +00:00
isDevicePath , devicePathErr := mounter . PathIsDevice ( deviceToDetach . DevicePath )
var deviceOpened bool
var deviceOpenedErr error
if ! isDevicePath && devicePathErr == nil {
// not a device path or path doesn't exist
//TODO: refer to #36092
glog . V ( 3 ) . Infof ( "Not checking device path %s" , deviceToDetach . DevicePath )
deviceOpened = false
} else {
deviceOpened , deviceOpenedErr = mounter . DeviceOpened ( deviceToDetach . DevicePath )
if deviceOpenedErr != nil {
return fmt . Errorf (
"UnmountDevice.DeviceOpened failed for volume %q (spec.Name: %q) with: %v" ,
deviceToDetach . VolumeName ,
deviceToDetach . VolumeSpec . Name ( ) ,
deviceOpenedErr )
}
2016-07-06 17:42:56 +00:00
}
// The device is still in use elsewhere. Caller will log and retry.
if deviceOpened {
return fmt . Errorf (
"UnmountDevice failed for volume %q (spec.Name: %q) because the device is in use when it was no longer expected to be in use" ,
deviceToDetach . VolumeName ,
deviceToDetach . VolumeSpec . Name ( ) )
}
2016-05-30 02:22:22 +00:00
glog . Infof (
"UnmountDevice succeeded for volume %q (spec.Name: %q)." ,
deviceToDetach . VolumeName ,
deviceToDetach . VolumeSpec . Name ( ) )
// Update actual state of world
markDeviceUnmountedErr := actualStateOfWorld . MarkDeviceAsUnmounted (
deviceToDetach . VolumeName )
if markDeviceUnmountedErr != nil {
2016-06-16 06:48:04 +00:00
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-05-30 02:22:22 +00:00
"MarkDeviceAsUnmounted failed for device %q (spec.Name: %q) with: %v" ,
deviceToDetach . VolumeName ,
deviceToDetach . VolumeSpec . Name ( ) ,
markDeviceUnmountedErr )
}
return nil
} , nil
}
2016-06-16 06:48:04 +00:00
func ( oe * operationExecutor ) generateVerifyControllerAttachedVolumeFunc (
volumeToMount VolumeToMount ,
2016-07-16 06:10:29 +00:00
nodeName types . NodeName ,
2016-06-16 06:48:04 +00:00
actualStateOfWorld ActualStateOfWorldAttacherUpdater ) ( func ( ) error , error ) {
return func ( ) error {
if ! volumeToMount . PluginIsAttachable {
// If the volume does not implement the attacher interface, it is
2016-10-03 09:29:50 +00:00
// assumed to be attached and the actual state of the world is
2016-06-16 06:48:04 +00:00
// updated accordingly.
2016-07-27 21:53:40 +00:00
2016-06-16 06:48:04 +00:00
addVolumeNodeErr := actualStateOfWorld . MarkVolumeAsAttached (
2016-07-27 21:53:40 +00:00
volumeToMount . VolumeName , volumeToMount . VolumeSpec , nodeName , "" /* devicePath */ )
2016-06-16 06:48:04 +00:00
if addVolumeNodeErr != nil {
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-12-05 23:01:33 +00:00
"VerifyControllerAttachedVolume.MarkVolumeAsAttachedByUniqueVolumeName failed for volume %q (spec.Name: %q) pod %q (UID: %q) with: %v" ,
2016-06-16 06:48:04 +00:00
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID ,
addVolumeNodeErr )
}
return nil
}
2016-06-27 00:33:01 +00:00
if ! volumeToMount . ReportedInUse {
// If the given volume has not yet been added to the list of
// VolumesInUse in the node's volume status, do not proceed, return
// error. Caller will log and retry. The node status is updated
// periodically by kubelet, so it may take as much as 10 seconds
// before this clears.
// Issue #28141 to enable on demand status updates.
2016-12-05 23:01:33 +00:00
return fmt . Errorf ( "Volume %q (spec.Name: %q) pod %q (UID: %q) has not yet been added to the list of VolumesInUse in the node's volume status" ,
2016-06-27 00:33:01 +00:00
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID )
}
2016-06-16 06:48:04 +00:00
// Fetch current node object
2016-07-16 06:10:29 +00:00
node , fetchErr := oe . kubeClient . Core ( ) . Nodes ( ) . Get ( string ( nodeName ) )
2016-06-16 06:48:04 +00:00
if fetchErr != nil {
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-12-05 23:01:33 +00:00
"VerifyControllerAttachedVolume failed fetching node from API server. Volume %q (spec.Name: %q) pod %q (UID: %q). Error: %v" ,
2016-06-16 06:48:04 +00:00
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID ,
fetchErr )
}
if node == nil {
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-12-05 23:01:33 +00:00
"VerifyControllerAttachedVolume failed. Volume %q (spec.Name: %q) pod %q (UID: %q). Error: node object retrieved from API server is nil" ,
2016-06-16 06:48:04 +00:00
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID )
}
for _ , attachedVolume := range node . Status . VolumesAttached {
if attachedVolume . Name == volumeToMount . VolumeName {
addVolumeNodeErr := actualStateOfWorld . MarkVolumeAsAttached (
2016-11-18 20:58:56 +00:00
v1 . UniqueVolumeName ( "" ) , volumeToMount . VolumeSpec , nodeName , attachedVolume . DevicePath )
2016-06-22 19:56:58 +00:00
glog . Infof ( "Controller successfully attached volume %q (spec.Name: %q) pod %q (UID: %q) devicePath: %q" ,
2016-06-16 06:48:04 +00:00
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
2016-06-22 19:56:58 +00:00
volumeToMount . Pod . UID ,
attachedVolume . DevicePath )
2016-06-16 06:48:04 +00:00
if addVolumeNodeErr != nil {
// On failure, return error. Caller will log and retry.
return fmt . Errorf (
2016-12-05 23:01:33 +00:00
"VerifyControllerAttachedVolume.MarkVolumeAsAttached failed for volume %q (spec.Name: %q) pod %q (UID: %q) with: %v" ,
2016-06-16 06:48:04 +00:00
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID ,
addVolumeNodeErr )
}
return nil
}
}
// Volume not attached, return error. Caller will log and retry.
2016-12-05 23:01:33 +00:00
return fmt . Errorf ( "Volume %q (spec.Name: %q) pod %q (UID: %q) is not yet attached according to node status" ,
2016-06-16 06:48:04 +00:00
volumeToMount . VolumeName ,
volumeToMount . VolumeSpec . Name ( ) ,
volumeToMount . PodName ,
volumeToMount . Pod . UID )
} , nil
}