diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index 95156e31de8..040efc7eea5 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -22,7 +22,9 @@ import ( "fmt" "net/http" "os" + "path" "path/filepath" + "regexp" "strconv" "strings" "sync" @@ -456,7 +458,7 @@ func determineValidVolumes(pods []Pod) map[string]api.Volume { validVolumes := make(map[string]api.Volume) for _, pod := range pods { for _, volume := range pod.Manifest.Volumes { - identifier := pod.Manifest.ID + "/" + volume.Name + identifier := path.Join(pod.Manifest.ID, volume.Name) validVolumes[identifier] = volume } } @@ -467,32 +469,31 @@ func determineValidVolumes(pods []Pod) map[string]api.Volume { // active and mounted. Builds their respective Cleaner type in case they need to be deleted. func (kl *Kubelet) determineActiveVolumes() map[string]volume.Cleaner { activeVolumes := make(map[string]volume.Cleaner) - filepath.Walk(kl.rootDirectory, func(path string, info os.FileInfo, err error) error { - // Search for volume dir structure : $ROOTDIR/$PODID/volumes/$VOLUMETYPE/$VOLUMENAME - var name string - var podID string - // Extract volume type for dir structure - dir := getDir(path) - glog.Infof("Traversing filepath %s", path) - // Handle emptyDirectory types. - if dir == "empty" { - name = info.Name() - // Retrieve podID from dir structure - podID = getDir(filepath.Dir(filepath.Dir(path))) - glog.Infof("Found active volume %s of pod %s", name, podID) - identifier := podID + "/" + name - activeVolumes[identifier] = &volume.EmptyDirectoryCleaner{path} + filepath.Walk(kl.rootDirectory, func(fullPath string, info os.FileInfo, err error) error { + // Search for volume dir structure : (ROOT_DIR)/(POD_ID)/volumes/(VOLUME_KIND)/(VOLUME_NAME) + podIDRegex := "(?P[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)" + volumeNameRegex := "(?P[a-z0-9]([-a-z0-9]*[a-z0-9])?)" + kindRegex := "(?P(empty))" + regex := path.Join(kl.rootDirectory, podIDRegex, "volumes", kindRegex, volumeNameRegex) + regexMatcher, _ := regexp.Compile(regex) + if regexMatcher.MatchString(fullPath) { + // Extract info from the directory structure. + result := make(map[string]string) + substrings := regexMatcher.FindStringSubmatch(fullPath) + for i, label := range regexMatcher.SubexpNames() { + result[label] = substrings[i] + } + kind := result["volumeKind"] + name := result["volumeName"] + podID := result["podID"] + identifier := path.Join(podID, name) + activeVolumes[identifier], err = volume.CreateVolumeCleaner(kind, fullPath) } return nil }) return activeVolumes } -// Utility function to extract only the directory name. -func getDir(path string) string { - return filepath.Base(filepath.Dir(path)) -} - // Compares the map of active volumes to the map of valid volumes. // If an active volume does not have a respective valid volume, clean it up. func (kl *Kubelet) reconcileVolumes(pods []Pod) error { @@ -503,7 +504,10 @@ func (kl *Kubelet) reconcileVolumes(pods []Pod) error { for name, volume := range activeVolumes { if _, ok := validVolumes[name]; !ok { glog.Infof("Orphaned volume %s found, tearing down volume", name) - volume.TearDown() + err := volume.TearDown() + if err != nil { + glog.Errorf("Could not tear down volume %s", name) + } } } return nil diff --git a/pkg/volume/volume.go b/pkg/volume/volume.go index 1c8f3779715..506f7463416 100644 --- a/pkg/volume/volume.go +++ b/pkg/volume/volume.go @@ -44,7 +44,6 @@ type Builder interface { // The Cleaner interface provides the method to cleanup/unmount the volumes. type Cleaner interface { - Interface // TearDown unmounts the volume and removes traces of the SetUp procedure. TearDown() error } @@ -98,11 +97,7 @@ type EmptyDirectoryCleaner struct { // Simply delete everything in the directory. func (emptyDir *EmptyDirectoryCleaner) TearDown() error { - return os.RemoveAll(emptyDir.GetPath()) -} - -func (emptyDir *EmptyDirectoryCleaner) GetPath() string { - return emptyDir.Path + return os.RemoveAll(emptyDir.Path) } // Interprets API volume as a HostDirectory @@ -115,6 +110,10 @@ func CreateEmptyDirectoryBuilder(volume *api.Volume, podID string, rootDir strin return &EmptyDirectoryBuilder{volume.Name, podID, rootDir} } +func CreateEmptyDirectoryCleaner(path string) *EmptyDirectoryCleaner { + return &EmptyDirectoryCleaner{path} +} + // CreateVolumeBuilder returns a Builder capable of mounting a volume described by an // *api.Volume, or an error. func CreateVolumeBuilder(volume *api.Volume, podID string, rootDir string) (Builder, error) { @@ -136,3 +135,12 @@ func CreateVolumeBuilder(volume *api.Volume, podID string, rootDir string) (Buil } return vol, nil } + +func CreateVolumeCleaner(kind string, path string) (Cleaner, error) { + switch kind { + case "empty": + return CreateEmptyDirectoryCleaner(path), nil + default: + return nil, ErrUnsupportedVolumeType + } +} diff --git a/pkg/volume/volume_test.go b/pkg/volume/volume_test.go index 1d9455c1e66..482aa90dc2d 100644 --- a/pkg/volume/volume_test.go +++ b/pkg/volume/volume_test.go @@ -108,3 +108,27 @@ func TestCreateVolumeBuilders(t *testing.T) { } } } +func TestEmptySetUpAndTearDown(t *testing.T) { + volumes := []api.Volume{ + { + Name: "empty-dir", + Source: &api.VolumeSource{ + EmptyDirectory: &api.EmptyDirectory{}, + }, + }, + } + expectedPath := "/tmp/kubelet/fakeID/volumes/empty/empty-dir" + for _, volume := range volumes { + volumeBuilder, _ := CreateVolumeBuilder(&volume, "fakeID", "/tmp/kubelet") + volumeBuilder.SetUp() + if _, err := os.Stat(expectedPath); os.IsNotExist(err) { + t.Errorf("Mount directory %v does not exist after SetUp", expectedPath) + } + volumeCleaner, _ := CreateVolumeCleaner("empty", expectedPath) + volumeCleaner.TearDown() + if _, err := os.Stat(expectedPath); !os.IsNotExist(err) { + t.Errorf("Mount directory %v still exists after TearDown", expectedPath) + } + } + os.RemoveAll("/tmp/kubelet") +}