2014-07-15 01:39:30 +00:00
|
|
|
/*
|
|
|
|
Copyright 2014 Google Inc. All rights reserved.
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2014-07-16 19:30:51 +00:00
|
|
|
package volume
|
2014-07-15 01:39:30 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2014-07-30 21:04:19 +00:00
|
|
|
"io/ioutil"
|
2014-07-16 19:30:51 +00:00
|
|
|
"os"
|
2014-07-19 00:13:34 +00:00
|
|
|
"path"
|
|
|
|
|
2014-07-15 01:39:30 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
2014-07-30 21:04:19 +00:00
|
|
|
"github.com/golang/glog"
|
2014-07-15 01:39:30 +00:00
|
|
|
)
|
|
|
|
|
2014-07-27 18:38:35 +00:00
|
|
|
var ErrUnsupportedVolumeType = errors.New("unsupported volume type")
|
|
|
|
|
2014-07-30 21:04:19 +00:00
|
|
|
// Interface is a directory used by pods or hosts.
|
2014-09-02 10:00:28 +00:00
|
|
|
// All method implementations of methods in the volume interface must be idempotent.
|
2014-07-15 01:39:30 +00:00
|
|
|
type Interface interface {
|
2014-07-25 21:17:02 +00:00
|
|
|
// GetPath returns the directory path the volume is mounted to.
|
|
|
|
GetPath() string
|
|
|
|
}
|
|
|
|
|
2014-09-02 10:00:28 +00:00
|
|
|
// Builder interface provides method to set up/mount the volume.
|
2014-07-25 21:17:02 +00:00
|
|
|
type Builder interface {
|
2014-07-30 21:04:19 +00:00
|
|
|
// Uses Interface to provide the path for Docker binds.
|
2014-07-25 21:17:02 +00:00
|
|
|
Interface
|
2014-07-27 18:38:35 +00:00
|
|
|
// SetUp prepares and mounts/unpacks the volume to a directory path.
|
2014-07-24 20:45:55 +00:00
|
|
|
SetUp() error
|
2014-07-25 21:17:02 +00:00
|
|
|
}
|
2014-07-27 18:38:35 +00:00
|
|
|
|
2014-09-02 10:00:28 +00:00
|
|
|
// Cleaner interface provides method to cleanup/unmount the volumes.
|
2014-07-25 21:17:02 +00:00
|
|
|
type Cleaner interface {
|
2014-07-27 18:38:35 +00:00
|
|
|
// TearDown unmounts the volume and removes traces of the SetUp procedure.
|
2014-07-24 20:45:55 +00:00
|
|
|
TearDown() error
|
2014-07-15 01:39:30 +00:00
|
|
|
}
|
|
|
|
|
2014-10-01 20:35:21 +00:00
|
|
|
// HostDir volumes represent a bare host directory mount.
|
2014-07-16 19:30:51 +00:00
|
|
|
// The directory in Path will be directly exposed to the container.
|
2014-10-01 20:35:21 +00:00
|
|
|
type HostDir struct {
|
2014-07-15 01:39:30 +00:00
|
|
|
Path string
|
|
|
|
}
|
|
|
|
|
2014-09-02 10:00:28 +00:00
|
|
|
// SetUp implements interface definitions, even though host directory
|
|
|
|
// mounts don't require any setup or cleanup.
|
2014-10-01 20:35:21 +00:00
|
|
|
func (hostVol *HostDir) SetUp() error {
|
2014-07-24 20:45:55 +00:00
|
|
|
return nil
|
|
|
|
}
|
2014-07-15 01:39:30 +00:00
|
|
|
|
2014-10-01 20:35:21 +00:00
|
|
|
func (hostVol *HostDir) GetPath() string {
|
2014-07-15 01:39:30 +00:00
|
|
|
return hostVol.Path
|
|
|
|
}
|
|
|
|
|
2014-10-01 20:35:21 +00:00
|
|
|
// EmptyDir volumes are temporary directories exposed to the pod.
|
2014-07-16 19:30:51 +00:00
|
|
|
// These do not persist beyond the lifetime of a pod.
|
2014-10-01 20:35:21 +00:00
|
|
|
type EmptyDir struct {
|
2014-07-19 00:13:34 +00:00
|
|
|
Name string
|
|
|
|
PodID string
|
|
|
|
RootDir string
|
2014-07-16 19:30:51 +00:00
|
|
|
}
|
|
|
|
|
2014-09-02 10:00:28 +00:00
|
|
|
// SetUp creates new directory.
|
2014-10-01 20:35:21 +00:00
|
|
|
func (emptyDir *EmptyDir) SetUp() error {
|
2014-07-19 00:13:34 +00:00
|
|
|
path := emptyDir.GetPath()
|
2014-07-24 20:45:55 +00:00
|
|
|
err := os.MkdirAll(path, 0750)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2014-07-16 19:32:59 +00:00
|
|
|
}
|
2014-07-24 20:45:55 +00:00
|
|
|
return nil
|
2014-07-16 19:30:51 +00:00
|
|
|
}
|
|
|
|
|
2014-10-01 20:35:21 +00:00
|
|
|
func (emptyDir *EmptyDir) GetPath() string {
|
2014-07-25 21:17:02 +00:00
|
|
|
return path.Join(emptyDir.RootDir, emptyDir.PodID, "volumes", "empty", emptyDir.Name)
|
|
|
|
}
|
|
|
|
|
2014-10-01 20:35:21 +00:00
|
|
|
func (emptyDir *EmptyDir) renameDirectory() (string, error) {
|
2014-07-30 21:04:19 +00:00
|
|
|
oldPath := emptyDir.GetPath()
|
|
|
|
newPath, err := ioutil.TempDir(path.Dir(oldPath), emptyDir.Name+".deleting~")
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
err = os.Rename(oldPath, newPath)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return newPath, nil
|
2014-07-25 21:17:02 +00:00
|
|
|
}
|
|
|
|
|
2014-09-02 10:00:28 +00:00
|
|
|
// TearDown simply deletes everything in the directory.
|
2014-10-01 20:35:21 +00:00
|
|
|
func (emptyDir *EmptyDir) TearDown() error {
|
2014-07-30 21:04:19 +00:00
|
|
|
tmpDir, err := emptyDir.renameDirectory()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = os.RemoveAll(tmpDir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
2014-07-16 19:30:51 +00:00
|
|
|
}
|
2014-07-16 19:32:59 +00:00
|
|
|
|
2014-10-01 20:35:21 +00:00
|
|
|
// createHostDir interprets API volume as a HostDir.
|
|
|
|
func createHostDir(volume *api.Volume) *HostDir {
|
|
|
|
return &HostDir{volume.Source.HostDir.Path}
|
2014-07-16 19:30:51 +00:00
|
|
|
}
|
|
|
|
|
2014-10-01 20:35:21 +00:00
|
|
|
// createEmptyDir interprets API volume as an EmptyDir.
|
|
|
|
func createEmptyDir(volume *api.Volume, podID string, rootDir string) *EmptyDir {
|
|
|
|
return &EmptyDir{volume.Name, podID, rootDir}
|
2014-07-15 01:39:30 +00:00
|
|
|
}
|
|
|
|
|
2014-07-25 21:17:02 +00:00
|
|
|
// 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) {
|
2014-07-16 19:32:59 +00:00
|
|
|
source := volume.Source
|
|
|
|
// TODO(jonesdl) We will want to throw an error here when we no longer
|
|
|
|
// support the default behavior.
|
|
|
|
if source == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2014-07-25 21:17:02 +00:00
|
|
|
var vol Builder
|
2014-07-16 19:30:51 +00:00
|
|
|
// TODO(jonesdl) We should probably not check every pointer and directly
|
|
|
|
// resolve these types instead.
|
2014-10-01 20:35:21 +00:00
|
|
|
if source.HostDir != nil {
|
|
|
|
vol = createHostDir(volume)
|
|
|
|
} else if source.EmptyDir != nil {
|
|
|
|
vol = createEmptyDir(volume, podID, rootDir)
|
2014-07-15 01:39:30 +00:00
|
|
|
} else {
|
2014-07-27 18:38:35 +00:00
|
|
|
return nil, ErrUnsupportedVolumeType
|
2014-07-15 01:39:30 +00:00
|
|
|
}
|
2014-07-16 19:32:59 +00:00
|
|
|
return vol, nil
|
2014-07-15 01:39:30 +00:00
|
|
|
}
|
2014-07-29 17:20:50 +00:00
|
|
|
|
2014-07-29 17:51:59 +00:00
|
|
|
// CreateVolumeCleaner returns a Cleaner capable of tearing down a volume.
|
2014-07-30 21:04:19 +00:00
|
|
|
func CreateVolumeCleaner(kind string, name string, podID string, rootDir string) (Cleaner, error) {
|
2014-07-29 17:20:50 +00:00
|
|
|
switch kind {
|
|
|
|
case "empty":
|
2014-10-01 20:35:21 +00:00
|
|
|
return &EmptyDir{name, podID, rootDir}, nil
|
2014-07-29 17:20:50 +00:00
|
|
|
default:
|
|
|
|
return nil, ErrUnsupportedVolumeType
|
|
|
|
}
|
|
|
|
}
|
2014-07-30 21:04:19 +00:00
|
|
|
|
2014-09-02 10:00:28 +00:00
|
|
|
// GetCurrentVolumes examines directory structure to determine volumes that are
|
|
|
|
// presently active and mounted. Returns a map of Cleaner types.
|
2014-07-30 21:04:19 +00:00
|
|
|
func GetCurrentVolumes(rootDirectory string) map[string]Cleaner {
|
|
|
|
currentVolumes := make(map[string]Cleaner)
|
|
|
|
mountPath := rootDirectory
|
|
|
|
podIDDirs, err := ioutil.ReadDir(mountPath)
|
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("Could not read directory: %s, (%s)", mountPath, err)
|
|
|
|
}
|
|
|
|
// Volume information is extracted from the directory structure:
|
|
|
|
// (ROOT_DIR)/(POD_ID)/volumes/(VOLUME_KIND)/(VOLUME_NAME)
|
|
|
|
for _, podIDDir := range podIDDirs {
|
2014-08-18 16:05:08 +00:00
|
|
|
if !podIDDir.IsDir() {
|
|
|
|
continue
|
|
|
|
}
|
2014-07-30 21:04:19 +00:00
|
|
|
podID := podIDDir.Name()
|
|
|
|
podIDPath := path.Join(mountPath, podID, "volumes")
|
|
|
|
volumeKindDirs, err := ioutil.ReadDir(podIDPath)
|
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("Could not read directory: %s, (%s)", podIDPath, err)
|
|
|
|
}
|
|
|
|
for _, volumeKindDir := range volumeKindDirs {
|
|
|
|
volumeKind := volumeKindDir.Name()
|
|
|
|
volumeKindPath := path.Join(podIDPath, volumeKind)
|
|
|
|
volumeNameDirs, err := ioutil.ReadDir(volumeKindPath)
|
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("Could not read directory: %s, (%s)", volumeKindPath, err)
|
|
|
|
}
|
|
|
|
for _, volumeNameDir := range volumeNameDirs {
|
|
|
|
volumeName := volumeNameDir.Name()
|
|
|
|
identifier := path.Join(podID, volumeName)
|
|
|
|
// TODO(thockin) This should instead return a reference to an extant volume object
|
|
|
|
cleaner, err := CreateVolumeCleaner(volumeKind, volumeName, podID, rootDirectory)
|
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("Could not create volume cleaner: %s, (%s)", volumeNameDirs, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
currentVolumes[identifier] = cleaner
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return currentVolumes
|
|
|
|
}
|