mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Merge pull request #30661 from saad-ali/safeDeviceUnmount
Automatic merge from submit-queue Prevent device unmount from deleting dir on failed unmount This PR cleans up the device unmount code for attachable volumes. Specifically it: * Prevents deletion of directory via `os.Remove` unless unmount succeeds. * Moves common shared device unmount logic to a common util file.
This commit is contained in:
commit
c5ab95cd79
@ -28,6 +28,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/util/exec"
|
"k8s.io/kubernetes/pkg/util/exec"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type awsElasticBlockStoreAttacher struct {
|
type awsElasticBlockStoreAttacher struct {
|
||||||
@ -218,7 +219,7 @@ func (detacher *awsElasticBlockStoreDetacher) WaitForDetach(devicePath string, t
|
|||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
glog.V(5).Infof("Checking device %q is detached.", devicePath)
|
glog.V(5).Infof("Checking device %q is detached.", devicePath)
|
||||||
if pathExists, err := pathExists(devicePath); err != nil {
|
if pathExists, err := volumeutil.PathExists(devicePath); err != nil {
|
||||||
return fmt.Errorf("Error checking if device path exists: %v", err)
|
return fmt.Errorf("Error checking if device path exists: %v", err)
|
||||||
} else if !pathExists {
|
} else if !pathExists {
|
||||||
return nil
|
return nil
|
||||||
@ -230,10 +231,5 @@ func (detacher *awsElasticBlockStoreDetacher) WaitForDetach(devicePath string, t
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (detacher *awsElasticBlockStoreDetacher) UnmountDevice(deviceMountPath string) error {
|
func (detacher *awsElasticBlockStoreDetacher) UnmountDevice(deviceMountPath string) error {
|
||||||
volume := path.Base(deviceMountPath)
|
return volumeutil.UnmountPath(deviceMountPath, detacher.mounter)
|
||||||
if err := unmountPDAndRemoveGlobalPath(deviceMountPath, detacher.mounter); err != nil {
|
|
||||||
glog.Errorf("Error unmounting %q: %v", volume, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@ -18,14 +18,13 @@ package aws_ebs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||||
"k8s.io/kubernetes/pkg/cloudprovider/providers/aws"
|
"k8s.io/kubernetes/pkg/cloudprovider/providers/aws"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -104,7 +103,7 @@ func (util *AWSDiskUtil) CreateVolume(c *awsElasticBlockStoreProvisioner) (strin
|
|||||||
// Returns the first path that exists, or empty string if none exist.
|
// Returns the first path that exists, or empty string if none exist.
|
||||||
func verifyDevicePath(devicePaths []string) (string, error) {
|
func verifyDevicePath(devicePaths []string) (string, error) {
|
||||||
for _, path := range devicePaths {
|
for _, path := range devicePaths {
|
||||||
if pathExists, err := pathExists(path); err != nil {
|
if pathExists, err := volumeutil.PathExists(path); err != nil {
|
||||||
return "", fmt.Errorf("Error checking if path exists: %v", err)
|
return "", fmt.Errorf("Error checking if path exists: %v", err)
|
||||||
} else if pathExists {
|
} else if pathExists {
|
||||||
return path, nil
|
return path, nil
|
||||||
@ -114,24 +113,11 @@ func verifyDevicePath(devicePaths []string) (string, error) {
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmount the global mount path, which should be the only one, and delete it.
|
|
||||||
func unmountPDAndRemoveGlobalPath(globalMountPath string, mounter mount.Interface) error {
|
|
||||||
if pathExists, pathErr := pathExists(globalMountPath); pathErr != nil {
|
|
||||||
return fmt.Errorf("Error checking if path exists: %v", pathErr)
|
|
||||||
} else if !pathExists {
|
|
||||||
glog.V(5).Infof("Warning: Unmount skipped because path does not exist: %v", globalMountPath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := mounter.Unmount(globalMountPath)
|
|
||||||
os.Remove(globalMountPath)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the first path that exists, or empty string if none exist.
|
// Returns the first path that exists, or empty string if none exist.
|
||||||
func verifyAllPathsRemoved(devicePaths []string) (bool, error) {
|
func verifyAllPathsRemoved(devicePaths []string) (bool, error) {
|
||||||
allPathsRemoved := true
|
allPathsRemoved := true
|
||||||
for _, path := range devicePaths {
|
for _, path := range devicePaths {
|
||||||
if exists, err := pathExists(path); err != nil {
|
if exists, err := volumeutil.PathExists(path); err != nil {
|
||||||
return false, fmt.Errorf("Error checking if path exists: %v", err)
|
return false, fmt.Errorf("Error checking if path exists: %v", err)
|
||||||
} else {
|
} else {
|
||||||
allPathsRemoved = allPathsRemoved && !exists
|
allPathsRemoved = allPathsRemoved && !exists
|
||||||
@ -159,18 +145,6 @@ func getDiskByIdPaths(partition string, devicePath string) []string {
|
|||||||
return devicePaths
|
return devicePaths
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the specified path exists
|
|
||||||
func pathExists(path string) (bool, error) {
|
|
||||||
_, err := os.Stat(path)
|
|
||||||
if err == nil {
|
|
||||||
return true, nil
|
|
||||||
} else if os.IsNotExist(err) {
|
|
||||||
return false, nil
|
|
||||||
} else {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return cloud provider
|
// Return cloud provider
|
||||||
func getCloudProvider(cloudProvider cloudprovider.Interface) (*aws.Cloud, error) {
|
func getCloudProvider(cloudProvider cloudprovider.Interface) (*aws.Cloud, error) {
|
||||||
awsCloudProvider, ok := cloudProvider.(*aws.Cloud)
|
awsCloudProvider, ok := cloudProvider.(*aws.Cloud)
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/util/exec"
|
"k8s.io/kubernetes/pkg/util/exec"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type cinderDiskAttacher struct {
|
type cinderDiskAttacher struct {
|
||||||
@ -130,7 +131,7 @@ func (attacher *cinderDiskAttacher) WaitForAttach(spec *volume.Spec, devicePath
|
|||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
glog.V(5).Infof("Checking Cinder disk %q is attached.", volumeID)
|
glog.V(5).Infof("Checking Cinder disk %q is attached.", volumeID)
|
||||||
probeAttachedVolume()
|
probeAttachedVolume()
|
||||||
exists, err := pathExists(devicePath)
|
exists, err := volumeutil.PathExists(devicePath)
|
||||||
if exists && err == nil {
|
if exists && err == nil {
|
||||||
glog.Infof("Successfully found attached Cinder disk %q.", volumeID)
|
glog.Infof("Successfully found attached Cinder disk %q.", volumeID)
|
||||||
return devicePath, nil
|
return devicePath, nil
|
||||||
@ -250,7 +251,7 @@ func (detacher *cinderDiskDetacher) WaitForDetach(devicePath string, timeout tim
|
|||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
glog.V(5).Infof("Checking device %q is detached.", devicePath)
|
glog.V(5).Infof("Checking device %q is detached.", devicePath)
|
||||||
if pathExists, err := pathExists(devicePath); err != nil {
|
if pathExists, err := volumeutil.PathExists(devicePath); err != nil {
|
||||||
return fmt.Errorf("Error checking if device path exists: %v", err)
|
return fmt.Errorf("Error checking if device path exists: %v", err)
|
||||||
} else if !pathExists {
|
} else if !pathExists {
|
||||||
return nil
|
return nil
|
||||||
@ -262,35 +263,5 @@ func (detacher *cinderDiskDetacher) WaitForDetach(devicePath string, timeout tim
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (detacher *cinderDiskDetacher) UnmountDevice(deviceMountPath string) error {
|
func (detacher *cinderDiskDetacher) UnmountDevice(deviceMountPath string) error {
|
||||||
volume := path.Base(deviceMountPath)
|
return volumeutil.UnmountPath(deviceMountPath, detacher.mounter)
|
||||||
if err := unmountPDAndRemoveGlobalPath(deviceMountPath, detacher.mounter); err != nil {
|
|
||||||
glog.Errorf("Error unmounting %q: %v", volume, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if the specified path exists
|
|
||||||
func pathExists(path string) (bool, error) {
|
|
||||||
_, err := os.Stat(path)
|
|
||||||
if err == nil {
|
|
||||||
return true, nil
|
|
||||||
} else if os.IsNotExist(err) {
|
|
||||||
return false, nil
|
|
||||||
} else {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmount the global mount path, which should be the only one, and delete it.
|
|
||||||
func unmountPDAndRemoveGlobalPath(globalMountPath string, mounter mount.Interface) error {
|
|
||||||
if pathExists, pathErr := pathExists(globalMountPath); pathErr != nil {
|
|
||||||
return fmt.Errorf("Error checking if path exists: %v", pathErr)
|
|
||||||
} else if !pathExists {
|
|
||||||
glog.V(5).Infof("Warning: Unmount skipped because path does not exist: %v", globalMountPath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := mounter.Unmount(globalMountPath)
|
|
||||||
os.Remove(globalMountPath)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type gcePersistentDiskAttacher struct {
|
type gcePersistentDiskAttacher struct {
|
||||||
@ -244,7 +245,7 @@ func (detacher *gcePersistentDiskDetacher) WaitForDetach(devicePath string, time
|
|||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
glog.V(5).Infof("Checking device %q is detached.", devicePath)
|
glog.V(5).Infof("Checking device %q is detached.", devicePath)
|
||||||
if pathExists, err := pathExists(devicePath); err != nil {
|
if pathExists, err := volumeutil.PathExists(devicePath); err != nil {
|
||||||
return fmt.Errorf("Error checking if device path exists: %v", err)
|
return fmt.Errorf("Error checking if device path exists: %v", err)
|
||||||
} else if !pathExists {
|
} else if !pathExists {
|
||||||
return nil
|
return nil
|
||||||
@ -256,5 +257,5 @@ func (detacher *gcePersistentDiskDetacher) WaitForDetach(devicePath string, time
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (detacher *gcePersistentDiskDetacher) UnmountDevice(deviceMountPath string) error {
|
func (detacher *gcePersistentDiskDetacher) UnmountDevice(deviceMountPath string) error {
|
||||||
return unmountPDAndRemoveGlobalPath(deviceMountPath, detacher.host.GetMounter())
|
return volumeutil.UnmountPath(deviceMountPath, detacher.host.GetMounter())
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ package gce_pd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@ -28,9 +27,9 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||||
gcecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/gce"
|
gcecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/gce"
|
||||||
"k8s.io/kubernetes/pkg/util/exec"
|
"k8s.io/kubernetes/pkg/util/exec"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -114,7 +113,7 @@ func verifyDevicePath(devicePaths []string, sdBeforeSet sets.String) (string, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, path := range devicePaths {
|
for _, path := range devicePaths {
|
||||||
if pathExists, err := pathExists(path); err != nil {
|
if pathExists, err := volumeutil.PathExists(path); err != nil {
|
||||||
return "", fmt.Errorf("Error checking if path exists: %v", err)
|
return "", fmt.Errorf("Error checking if path exists: %v", err)
|
||||||
} else if pathExists {
|
} else if pathExists {
|
||||||
return path, nil
|
return path, nil
|
||||||
@ -124,20 +123,6 @@ func verifyDevicePath(devicePaths []string, sdBeforeSet sets.String) (string, er
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmount the global PD mount, which should be the only one, and delete it.
|
|
||||||
// Does nothing if globalMountPath does not exist.
|
|
||||||
func unmountPDAndRemoveGlobalPath(globalMountPath string, mounter mount.Interface) error {
|
|
||||||
if pathExists, pathErr := pathExists(globalMountPath); pathErr != nil {
|
|
||||||
return fmt.Errorf("Error checking if path exists: %v", pathErr)
|
|
||||||
} else if !pathExists {
|
|
||||||
glog.V(5).Infof("Warning: Unmount skipped because path does not exist: %v", globalMountPath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := mounter.Unmount(globalMountPath)
|
|
||||||
os.Remove(globalMountPath)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the first path that exists, or empty string if none exist.
|
// Returns the first path that exists, or empty string if none exist.
|
||||||
func verifyAllPathsRemoved(devicePaths []string) (bool, error) {
|
func verifyAllPathsRemoved(devicePaths []string) (bool, error) {
|
||||||
allPathsRemoved := true
|
allPathsRemoved := true
|
||||||
@ -146,7 +131,7 @@ func verifyAllPathsRemoved(devicePaths []string) (bool, error) {
|
|||||||
// udevadm errors should not block disk detachment, log and continue
|
// udevadm errors should not block disk detachment, log and continue
|
||||||
glog.Errorf("%v", err)
|
glog.Errorf("%v", err)
|
||||||
}
|
}
|
||||||
if exists, err := pathExists(path); err != nil {
|
if exists, err := volumeutil.PathExists(path); err != nil {
|
||||||
return false, fmt.Errorf("Error checking if path exists: %v", err)
|
return false, fmt.Errorf("Error checking if path exists: %v", err)
|
||||||
} else {
|
} else {
|
||||||
allPathsRemoved = allPathsRemoved && !exists
|
allPathsRemoved = allPathsRemoved && !exists
|
||||||
@ -172,18 +157,6 @@ func getDiskByIdPaths(pdName string, partition string) []string {
|
|||||||
return devicePaths
|
return devicePaths
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the specified path exists
|
|
||||||
func pathExists(path string) (bool, error) {
|
|
||||||
_, err := os.Stat(path)
|
|
||||||
if err == nil {
|
|
||||||
return true, nil
|
|
||||||
} else if os.IsNotExist(err) {
|
|
||||||
return false, nil
|
|
||||||
} else {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return cloud provider
|
// Return cloud provider
|
||||||
func getCloudProvider(cloudProvider cloudprovider.Interface) (*gcecloud.GCECloud, error) {
|
func getCloudProvider(cloudProvider cloudprovider.Interface) (*gcecloud.GCECloud, error) {
|
||||||
var err error
|
var err error
|
||||||
|
@ -17,10 +17,12 @@ limitations under the License.
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
)
|
)
|
||||||
|
|
||||||
const readyFileName = "ready"
|
const readyFileName = "ready"
|
||||||
@ -60,3 +62,35 @@ func SetReady(dir string) {
|
|||||||
}
|
}
|
||||||
file.Close()
|
file.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmountPath is a common unmount routine that unmounts the given path and
|
||||||
|
// deletes the remaining directory if successful.
|
||||||
|
func UnmountPath(mountPath string, mounter mount.Interface) error {
|
||||||
|
if pathExists, pathErr := PathExists(mountPath); pathErr != nil {
|
||||||
|
return fmt.Errorf("Error checking if path exists: %v", pathErr)
|
||||||
|
} else if !pathExists {
|
||||||
|
glog.Warningf("Warning: Unmount skipped because path does not exist: %v", mountPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mounter.Unmount(mountPath)
|
||||||
|
if err == nil {
|
||||||
|
// Only delete directory on successful unmount
|
||||||
|
glog.V(5).Infof("Unmounted %q. Deleting path.", mountPath)
|
||||||
|
return os.Remove(mountPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathExists returns true if the specified path exists.
|
||||||
|
func PathExists(path string) (bool, error) {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
return true, nil
|
||||||
|
} else if os.IsNotExist(err) {
|
||||||
|
return false, nil
|
||||||
|
} else {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user