// Even though iSCSI plugin has attacher/detacher implementation, iSCSI plugin
// needs volume detach operation during TearDownDevice(). This method is only
// chance that operations are done on kubelet node during volume teardown sequences.
540111
func (c *iscsiDiskUnmapper) TearDownDevice(mapPath, _ string) error {
	err := c.manager.DetachBlockISCSIDisk(*c, mapPath)
	if err != nil {
		return fmt.Errorf("iscsi: failed to detach disk: %s\nError: %v", mapPath, err)
	}
	klog.V(4).Infof("iscsi: %q is unmounted, deleting the directory", mapPath)
	err = os.RemoveAll(mapPath)
	if err != nil {
		return fmt.Errorf("iscsi: failed to delete the directory: %s\nError: %v", mapPath, err)
	}
	klog.V(4).Infof("iscsi: successfully detached disk: %s", mapPath)
	return nil
}
1245538
func (s *VolumeServer) Detach(
	ctx context.Context,
	req *api.SdkVolumeDetachRequest,
) (*api.SdkVolumeDetachResponse, error) {
	if s.cluster() == nil || s.driver(ctx) == nil {
		return nil, status.Error(codes.Unavailable, "Resource has not been initialized")
	}

	if len(req.GetVolumeId()) == 0 {
		return nil, status.Error(codes.InvalidArgument, "Must supply volume id")
	}

	// Get access rights
	err := s.checkAccessForVolumeId(ctx, req.GetVolumeId(), api.Ownership_Write)
	if err != nil && !IsErrorNotFound(err) {
		return nil, err
	}

	// Check options
	options := req.GetDriverOptions()
	if options == nil {
		options = make(map[string]string)
	}
	options[mountattachoptions.OptionsRedirectDetach] = "true"
	if req.GetOptions() != nil {
		options[mountattachoptions.OptionsForceDetach] = fmt.Sprint(req.GetOptions().GetForce())
		options[mountattachoptions.OptionsUnmountBeforeDetach] = fmt.Sprint(req.GetOptions().GetUnmountBeforeDetach())
	}

	err = s.driver(ctx).Detach(req.GetVolumeId(), options)
	if err != nil && !IsErrorNotFound(err) {
		return nil, status.Errorf(
			codes.Internal,
			"Failed to detach volume %s: %v",
			req.GetVolumeId(),
			err)
	}

	return &api.SdkVolumeDetachResponse{}, nil
}
1158143
func (s *VolumeService) DetachVolume(p *DetachVolumeParams) (*DetachVolumeResponse, error) {
	resp, err := s.cs.newRequest("detachVolume", p.toURLValues())
	if err != nil {
		return nil, err
	}

	var r DetachVolumeResponse
	if err := json.Unmarshal(resp, &r); err != nil {
		return nil, err
	}

	// If we have a async client, we need to wait for the async result
	if s.cs.async {
		b, err := s.cs.GetAsyncJobResult(r.JobID, s.cs.timeout)
		if err != nil {
			if err == AsyncTimeoutErr {
				return &r, err
			}
			return nil, err
		}

		b, err = getRawValue(b)
		if err != nil {
			return nil, err
		}

		if err := json.Unmarshal(b, &r); err != nil {
			return nil, err
		}
	}

	return &r, nil
}
1122160
func (d *driver) VolumeRemove(
	ctx types.Context,
	volumeID string,
	opts *types.VolumeRemoveOpts) error {
	// Initialize for logging
	fields := map[string]interface{}{
		"provider": d.Name(),
		"volumeID": volumeID,
	}

	//TODO check if volume is attached? if so fail

	// Delete volume via EC2 API call
	dvInput := &awsec2.DeleteVolumeInput{
		VolumeId: &volumeID,
	}
	_, err := mustSession(ctx).DeleteVolume(dvInput)
	if err != nil {
		return goof.WithFieldsE(fields, "error deleting volume", err)
	}

	return nil
}
528302
func (oe *operationExecutor) ReconstructVolumeOperation(
	volumeMode v1.PersistentVolumeMode,
	plugin volume.VolumePlugin,
	mapperPlugin volume.BlockVolumePlugin,
	uid types.UID,
	podName volumetypes.UniquePodName,
	volumeSpecName string,
	volumePath string,
	pluginName string) (*volume.Spec, error) {

	// Filesystem Volume case
	if volumeMode == v1.PersistentVolumeFilesystem {
		// Create volumeSpec from mount path
		klog.V(5).Infof("Starting operationExecutor.ReconstructVolumepodName")
		volumeSpec, err := plugin.ConstructVolumeSpec(volumeSpecName, volumePath)
		if err != nil {
			return nil, err
		}
		return volumeSpec, nil
	}

	// Block Volume case
	// Create volumeSpec from mount path
	klog.V(5).Infof("Starting operationExecutor.ReconstructVolume")
	if mapperPlugin == nil {
		return nil, fmt.Errorf("Could not find block volume plugin %q (spec.Name: %q) pod %q (UID: %q)",
			pluginName,
			volumeSpecName,
			podName,
			uid)
	}
	// volumePath contains volumeName on the path. In the case of block volume, {volumeName} is symbolic link
	// corresponding to raw block device.
	// ex. volumePath: pods/{podUid}}/{DefaultKubeletVolumeDevicesDirName}/{escapeQualifiedPluginName}/{volumeName}
	volumeSpec, err := mapperPlugin.ConstructBlockVolumeSpec(uid, volumeSpecName, volumePath)
	if err != nil {
		return nil, err
	}
	return volumeSpec, nil
}
1341944
func deleteDevices(c iscsiDiskUnmounter) error {
	lunNumber, err := strconv.Atoi(c.iscsiDisk.Lun)
	if err != nil {
		klog.Errorf("iscsi delete devices: lun is not a number: %s\nError: %v", c.iscsiDisk.Lun, err)
		return err
	}
	// Enumerate the devices so we can delete them
	deviceNames, err := c.deviceUtil.FindDevicesForISCSILun(c.iscsiDisk.Iqn, lunNumber)
	if err != nil {
		klog.Errorf("iscsi delete devices: could not get devices associated with LUN %d on target %s\nError: %v",
			lunNumber, c.iscsiDisk.Iqn, err)
		return err
	}
	// Find the multipath device path(s)
	mpathDevices := make(map[string]bool)
	for _, deviceName := range deviceNames {
		path := "/dev/" + deviceName
		// check if the dev is using mpio and if so mount it via the dm-XX device
		if mappedDevicePath := c.deviceUtil.FindMultipathDeviceForDevice(path); mappedDevicePath != "" {
			mpathDevices[mappedDevicePath] = true
		}
	}
	// Flush any multipath device maps
	for mpathDevice := range mpathDevices {
		_, err = c.exec.Run("multipath", "-f", mpathDevice)
		if err != nil {
			klog.Warningf("Warning: Failed to flush multipath device map: %s\nError: %v", mpathDevice, err)
			// Fall through -- keep deleting the block devices
		}
		klog.V(4).Infof("Flushed multipath device: %s", mpathDevice)
	}
	for _, deviceName := range deviceNames {
		err = deleteDevice(deviceName)
		if err != nil {
			klog.Warningf("Warning: Failed to delete block device: %s\nError: %v", deviceName, err)
			// Fall through -- keep deleting other block devices
		}
	}
	return nil
}
308525
func detachZeroCopyBytes(isBytesReader bool, dest []byte, in []byte) (out []byte) {
	if xlen := len(in); xlen > 0 {
		if isBytesReader || xlen <= scratchByteArrayLen {
			if cap(dest) >= xlen {
				out = dest[:xlen]
			} else {
				out = make([]byte, xlen)
			}
			copy(out, in)
			return
		}
	}
	return in
}
593413
func (m *csiBlockMapper) TearDownDevice(globalMapPath, devicePath string) error {
	if !m.plugin.blockEnabled {
		return errors.New("CSIBlockVolume feature not enabled")
	}

	klog.V(4).Infof(log("unmapper.TearDownDevice(globalMapPath=%s; devicePath=%s)", globalMapPath, devicePath))

	ctx, cancel := context.WithTimeout(context.Background(), csiTimeout)
	defer cancel()

	csiClient, err := m.csiClientGetter.Get()
	if err != nil {
		klog.Error(log("blockMapper.TearDownDevice failed to get CSI client: %v", err))
		return err
	}

	// Call NodeUnpublishVolume
	publishPath := m.getPublishPath()
	if _, err := os.Stat(publishPath); err != nil {
		if os.IsNotExist(err) {
			klog.V(4).Infof(log("blockMapper.TearDownDevice publishPath(%s) has already been deleted, skip calling NodeUnpublishVolume", publishPath))
		} else {
			return err
		}
	} else {
		err := m.unpublishVolumeForBlock(ctx, csiClient, publishPath)
		if err != nil {
			return err
		}
	}

	// Call NodeUnstageVolume
	stagingPath := m.getStagingPath()
	if _, err := os.Stat(stagingPath); err != nil {
		if os.IsNotExist(err) {
			klog.V(4).Infof(log("blockMapper.TearDownDevice stagingPath(%s) has already been deleted, skip calling NodeUnstageVolume", stagingPath))
		} else {
			return err
		}
	} else {
		err := m.unstageVolumeForBlock(ctx, csiClient, stagingPath)
		if err != nil {
			return err
		}
	}

	return nil
}
1251937
func (detacher *vsphereVMDKDetacher) Detach(volumeName string, nodeName types.NodeName) error {

	volPath := getVolPathfromVolumeName(volumeName)
	attached, err := detacher.vsphereVolumes.DiskIsAttached(volPath, nodeName)
	if err != nil {
		// Log error and continue with detach
		klog.Errorf(
			"Error checking if volume (%q) is already attached to current node (%q). Will continue and try detach anyway. err=%v",
			volPath, nodeName, err)
	}

	if err == nil && !attached {
		// Volume is already detached from node.
		klog.Infof("detach operation was successful. volume %q is already detached from node %q.", volPath, nodeName)
		return nil
	}

	attachdetachMutex.LockKey(string(nodeName))
	defer attachdetachMutex.UnlockKey(string(nodeName))
	if err := detacher.vsphereVolumes.DetachDisk(volPath, nodeName); err != nil {
		klog.Errorf("Error detaching volume %q: %v", volPath, err)
		return err
	}
	return nil
}
225644
func (d *flexVolumeDetacher) Detach(volumeName string, hostName types.NodeName) error {

	call := d.plugin.NewDriverCall(detachCmd)
	call.Append(volumeName)
	call.Append(string(hostName))

	_, err := call.Run()
	if isCmdNotSupportedErr(err) {
		return (*detacherDefaults)(d).Detach(volumeName, hostName)
	}
	return err
}
50360
func (c *Client) DetachVolume(p *DetachVolumeParameter) (*Volume, error) {
	obj, err := c.Request("detachVolume", convertParamToMap(p))
	if err != nil {
		return nil, err
	}
	return obj.(*Volume), err
}
34318
func (lvs *loopVolumeSource) DetachVolumes(ctx context.ProviderCallContext, args []storage.VolumeAttachmentParams) ([]error, error) {
	results := make([]error, len(args))
	for i, arg := range args {
		if err := lvs.detachVolume(arg.Volume); err != nil {
			results[i] = errors.Annotatef(err, "detaching volume %s", arg.Volume.Id())
		}
	}
	return results, nil
}
1288890
func (v *volumeSource) DetachVolumes(ctx context.ProviderCallContext, attachParams []storage.VolumeAttachmentParams) ([]error, error) {
	// noop
	return make([]error, len(attachParams)), nil
}
635636
func (d *driver) VolumeAttach(
	ctx types.Context,
	volumeID string,
	opts *types.VolumeAttachOpts) (*types.Volume, string, error) {
	// review volume with attachments to any host
	ec2vols, err := d.getVolume(ctx, volumeID, "")
	if err != nil {
		return nil, "", goof.WithError("error getting volume", err)
	}
	volumes, convErr := d.toTypesVolume(
		ctx, ec2vols, types.VolAttReqTrue)
	if convErr != nil {
		return nil, "", goof.WithError(
			"error converting to types.Volume", convErr)
	}

	// Check if there a volume to attach
	if len(volumes) == 0 {
		return nil, "", goof.New("no volume found")
	}
	// Check if volume is already attached
	if len(volumes[0].Attachments) > 0 {
		// Detach already attached volume if forced
		if !opts.Force {
			return nil, "", errVolAlreadyAttached
		}
		_, err := d.VolumeDetach(
			ctx,
			volumeID,
			&types.VolumeDetachOpts{
				Force: opts.Force,
				Opts:  opts.Opts,
			})
		if err != nil {
			return nil, "", goof.WithError("error detaching volume", err)
		}
	}

	if opts.NextDevice == nil {
		return nil, "", errMissingNextDevice
	}

	// Attach volume via helper function which uses EC2 API call
	err = d.attachVolume(ctx, volumeID, volumes[0].Name, *opts.NextDevice)
	if err != nil {
		return nil, "", goof.WithFieldsE(
			log.Fields{
				"provider": d.Name(),
				"volumeID": volumeID},
			"error attaching volume",
			err,
		)
	}

	// Wait for volume's status to update
	if err = d.waitVolumeComplete(ctx, volumeID, waitVolumeAttach); err != nil {
		return nil, "", goof.WithError("error waiting for volume attach", err)
	}

	// Check if successful attach
	attachedVol, err := d.VolumeInspect(
		ctx, volumeID, &types.VolumeInspectOpts{
			Attachments: types.VolAttReqTrue,
			Opts:        opts.Opts,
		})
	if err != nil {
		return nil, "", goof.WithError("error getting volume", err)
	}

	// Token is the attachment's device name, which will be matched
	// to the executor's device ID
	return attachedVol, *opts.NextDevice, nil
}
544402
func (a *ALIVolumes) AttachVolume(volume *Volume) error {
	// TODO: what if this volume has already been attached to another instance?
	// Aliyun Disk can only be attached to one instance
	if volume.LocalDevice == "" && volume.AttachedTo == "" {
		attachDiskArgs := &ecs.AttachDiskArgs{
			InstanceId: a.instanceId,
			DiskId:     volume.ID,
			// TODO: DeleteWithInstance?
		}
		err := a.client.AttachDisk(attachDiskArgs)
		if err != nil {
			return fmt.Errorf("error attach disk %q: %v", volume.ID, err)
		}

		// TODO: Do we have to wait for attach to complete?
		// retrieve device info
		args := &ecs.DescribeDisksArgs{
			RegionId: common.Region(a.region),
			ZoneId:   a.zone,
			DiskIds:  []string{volume.ID},
		}
		disks, _, err := a.client.DescribeDisks(args)
		if err != nil || len(disks) == 0 {
			return fmt.Errorf("error querying Aliyun disk %q: %v", volume.ID, err)
		}

		volume.LocalDevice = disks[0].Device
		volume.AttachedTo = a.instanceId
	} else if volume.AttachedTo != a.instanceId {
		return fmt.Errorf("cannot reattach an attached disk without detaching it first")
	}
	return nil
}
1067799
func (v *azureVolumeSource) DetachVolumes(ctx context.ProviderCallContext, attachParams []storage.VolumeAttachmentParams) ([]error, error) {
	results := make([]error, len(attachParams))
	instanceIds := make([]instance.Id, len(attachParams))
	for i, p := range attachParams {
		instanceIds[i] = p.InstanceId
	}
	if len(instanceIds) == 0 {
		return results, nil
	}
	virtualMachines, err := v.virtualMachines(ctx, instanceIds)
	if err != nil {
		return nil, errors.Annotate(err, "getting virtual machines")
	}

	// Update VirtualMachine objects in-memory,
	// and then perform the updates all at once.
	//
	// An detachment does not require an update
	// if the disk isn't attached, so we keep a
	// record of which VMs need updating.
	changed := make(map[instance.Id]bool, len(virtualMachines))
	for i, p := range attachParams {
		vm, ok := virtualMachines[p.InstanceId]
		if !ok {
			continue
		}
		if vm.err != nil {
			results[i] = vm.err
			continue
		}
		if v.detachVolume(vm.vm, p) {
			changed[p.InstanceId] = true
		}
	}
	for _, instanceId := range instanceIds {
		if !changed[instanceId] {
			delete(virtualMachines, instanceId)
		}
	}

	updateResults, err := v.updateVirtualMachines(ctx, virtualMachines, instanceIds)
	if err != nil {
		return nil, errors.Annotate(err, "updating virtual machines")
	}
	for i, err := range updateResults {
		if results[i] != nil || err == nil {
			continue
		}
		results[i] = err
	}
	return results, nil
}
635007
func (s *oracleVolumeSource) DetachVolumes(ctx context.ProviderCallContext, params []storage.VolumeAttachmentParams) ([]error, error) {
	attachAsMap, err := s.getStorageAttachments()
	if err != nil {
		return nil, errors.Trace(err)
	}
	toDelete := make([]string, len(params))
	ret := make([]error, len(params))
	for i, val := range params {
		found := false
		for _, attach := range attachAsMap[string(val.InstanceId)] {
			if val.VolumeId == attach.Storage_volume_name {
				toDelete[i] = attach.Name
				found = true
			}
		}
		if !found {
			toDelete[i] = ""
			ret[i] = errors.NotFoundf(
				"volume attachment for instance %v and volumeID %v not found",
				val.InstanceId, val.VolumeId)
		}
	}
	for i, val := range toDelete {
		if val == "" {
			continue
		}
		ret[i] = s.api.DeleteStorageAttachment(val)
	}
	return ret, nil
}