github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/edge/pkg/edged/edged_volumes.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6      http://www.apache.org/licenses/LICENSE-2.0
     7  Unless required by applicable law or agreed to in writing, software
     8  distributed under the License is distributed on an "AS IS" BASIS,
     9  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  See the License for the specific language governing permissions and
    11  limitations under the License.
    12  
    13  @CHANGELOG
    14  KubeEdge Authors: To create mini-kubelet for edge deployment scenario,
    15  This file is derived from K8S Kubelet code with reduced set of methods
    16  Changes done are
    17  1. Most functions in this file is come from "k8s.io/kubernetes/pkg/kubelet/kubelet_volumes.go"
    18     and made some variant.
    19  */
    20  
    21  package edged
    22  
    23  import (
    24  	"fmt"
    25  	"strconv"
    26  	"strings"
    27  
    28  	api "k8s.io/api/core/v1"
    29  	"k8s.io/apimachinery/pkg/types"
    30  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    31  	"k8s.io/apimachinery/pkg/util/sets"
    32  	"k8s.io/klog"
    33  	"k8s.io/kubernetes/pkg/kubelet/container"
    34  	"k8s.io/kubernetes/pkg/util/removeall"
    35  	"k8s.io/kubernetes/pkg/volume"
    36  	volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
    37  	utilio "k8s.io/utils/io"
    38  	"k8s.io/utils/mount"
    39  )
    40  
    41  // newVolumeMounterFromPlugins attempts to find a plugin by volume spec, pod
    42  // and volume options and then creates a Mounter.
    43  // Returns a valid Unmounter or an error.
    44  func (e *edged) newVolumeMounterFromPlugins(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Mounter, error) {
    45  	plugin, err := e.volumePluginMgr.FindPluginBySpec(spec)
    46  	if err != nil {
    47  		return nil, fmt.Errorf("can't use volume plugins for %s: %v", spec.Name(), err)
    48  	}
    49  
    50  	physicalMounter, err := plugin.NewMounter(spec, pod, opts)
    51  	if err != nil {
    52  		return nil, fmt.Errorf("failed to instantiate mounter for volume: %s using plugin: %s with a root cause: %v", spec.Name(), plugin.GetPluginName(), err)
    53  	}
    54  	klog.Infof("Using volume plugin %q to mount %s", plugin.GetPluginName(), spec.Name())
    55  	return physicalMounter, nil
    56  }
    57  
    58  // cleanupOrphanedPodDirs removes the volumes of pods that should not be
    59  // running and that have no containers running.
    60  func (e *edged) cleanupOrphanedPodDirs(pods []*api.Pod, containerRunningPods []*container.Pod) error {
    61  
    62  	allPods := sets.NewString()
    63  	for _, pod := range pods {
    64  		allPods.Insert(string(pod.UID))
    65  	}
    66  
    67  	for _, pod := range containerRunningPods {
    68  		allPods.Insert(string(pod.ID))
    69  	}
    70  
    71  	found, err := e.listPodsFromDisk()
    72  	if err != nil {
    73  		return err
    74  	}
    75  	orphanRemovalErrors := []error{}
    76  	orphanVolumeErrors := []error{}
    77  
    78  	for _, uid := range found {
    79  		if allPods.Has(string(uid)) {
    80  			continue
    81  		}
    82  		// If volumes have not been unmounted/detached, do not delete directory.
    83  		// Doing so may result in corruption of data.
    84  		if podVolumesExist := e.podVolumesExist(uid); podVolumesExist {
    85  			klog.Infof("Orphaned pod %q found, but volumes are not cleaned up", uid)
    86  			continue
    87  		}
    88  		// If there are still volume directories, do not delete directory
    89  		volumePaths, err := e.getPodVolumePathListFromDisk(uid)
    90  		if err != nil {
    91  			orphanVolumeErrors = append(orphanVolumeErrors, fmt.Errorf("Orphaned pod %q found, but error %v occurred during reading volume dir from disk", uid, err))
    92  			continue
    93  		}
    94  		if len(volumePaths) > 0 {
    95  			orphanVolumeErrors = append(orphanVolumeErrors, fmt.Errorf("Orphaned pod %q found, but volume paths are still present on disk", uid))
    96  			continue
    97  		}
    98  
    99  		// TODO: by paas group
   100  		// k8s has no cleanupMoutPoints, what the purpose of this method?
   101  		// can be contributed?
   102  		klog.Infof("Clearing up volume directories of orphaned pod %q.", uid)
   103  		if err := e.cleanupMountPoints(e.getPodVolumesDir(uid)); err != nil {
   104  			klog.Warningf("Failed to clearing up volume mount points of pod %q: %s.", uid, err)
   105  		}
   106  		// If there are any volume-subpaths, do not cleanup directories
   107  		volumeSubpathExists, err := e.podVolumeSubpathsDirExists(uid)
   108  		if err != nil {
   109  			orphanVolumeErrors = append(orphanVolumeErrors, fmt.Errorf("Orphaned pod %q found, but error %v occurred during reading of volume-subpaths dir from disk", uid, err))
   110  			continue
   111  		}
   112  		if volumeSubpathExists {
   113  			orphanVolumeErrors = append(orphanVolumeErrors, fmt.Errorf("Orphaned pod %q found, but volume subpaths are still present on disk", uid))
   114  			continue
   115  		}
   116  
   117  		klog.Infof("Orphaned pod %q found, removing", uid)
   118  		if err := removeall.RemoveAllOneFilesystem(e.mounter, e.getPodDir(uid)); err != nil {
   119  			klog.Errorf("Failed to remove orphaned pod %q dir; err: %v", uid, err)
   120  			orphanRemovalErrors = append(orphanRemovalErrors, err)
   121  		}
   122  	}
   123  
   124  	logSpew := func(errs []error) {
   125  		if len(errs) > 0 {
   126  			klog.Errorf("%v : There were a total of %v errors similar to this. Turn up verbosity to see them.", errs[0], len(errs))
   127  			for _, err := range errs {
   128  				klog.Infof("Orphan pod: %v", err)
   129  			}
   130  		}
   131  	}
   132  
   133  	logSpew(orphanVolumeErrors)
   134  	logSpew(orphanRemovalErrors)
   135  	return utilerrors.NewAggregate(orphanRemovalErrors)
   136  }
   137  
   138  // cleanMountPoints traverses the volume directory to find out all mount points and umount them
   139  func (e *edged) cleanupMountPoints(volumeDir string) error {
   140  	mounter := mount.New("")
   141  	mountPoints, err := listProcMounts(volumeDir)
   142  	if err != nil {
   143  		return err
   144  	}
   145  	errlist := []error{}
   146  	for _, mp := range mountPoints {
   147  		if err := mounter.Unmount(mp.Path); err != nil {
   148  			errlist = append(errlist, err)
   149  		}
   150  	}
   151  	if len(errlist) != 0 {
   152  		return utilerrors.NewAggregate(errlist)
   153  	}
   154  	return nil
   155  }
   156  
   157  // podVolumesExist checks with the volume manager and returns true any of the
   158  // pods for the specified volume are mounted.
   159  func (e *edged) podVolumesExist(podUID types.UID) bool {
   160  	if mountedVolumes :=
   161  		e.volumeManager.GetMountedVolumesForPod(
   162  			volumetypes.UniquePodName(podUID)); len(mountedVolumes) > 0 {
   163  		return true
   164  	}
   165  
   166  	return false
   167  }
   168  
   169  // listProcMounts is come from k8s.io\kubernetes\pkg\util\mount.listProcMounts
   170  func listProcMounts(mountFilePath string) ([]mount.MountPoint, error) {
   171  	listTryTime, expectedNumFieldsPerLine := 3, 6
   172  	content, err := utilio.ConsistentRead(mountFilePath, listTryTime)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	out := []mount.MountPoint{}
   178  	lines := strings.Split(string(content), "\n")
   179  	for _, line := range lines {
   180  		if line == "" {
   181  			// the last split() item is empty string following the last \n
   182  			continue
   183  		}
   184  		fields := strings.Fields(line)
   185  		if len(fields) != expectedNumFieldsPerLine {
   186  			return nil, fmt.Errorf("wrong number of fields (expected %d, got %d): %s", expectedNumFieldsPerLine, len(fields), line)
   187  		}
   188  
   189  		mp := mount.MountPoint{
   190  			Device: fields[0],
   191  			Path:   fields[1],
   192  			Type:   fields[2],
   193  			Opts:   strings.Split(fields[3], ","),
   194  		}
   195  
   196  		freq, err := strconv.Atoi(fields[4])
   197  		if err != nil {
   198  			return nil, err
   199  		}
   200  		mp.Freq = freq
   201  
   202  		pass, err := strconv.Atoi(fields[5])
   203  		if err != nil {
   204  			return nil, err
   205  		}
   206  		mp.Pass = pass
   207  
   208  		out = append(out, mp)
   209  	}
   210  	return out, nil
   211  }