2014-08-28 10:18:08 -04:00
|
|
|
package volumes
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sync"
|
|
|
|
|
2014-10-24 13:12:35 -04:00
|
|
|
log "github.com/Sirupsen/logrus"
|
2014-10-24 18:11:48 -04:00
|
|
|
"github.com/docker/docker/daemon/graphdriver"
|
2015-03-24 07:25:26 -04:00
|
|
|
"github.com/docker/docker/pkg/stringid"
|
2014-08-28 10:18:08 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
type Repository struct {
|
|
|
|
configPath string
|
|
|
|
driver graphdriver.Driver
|
|
|
|
volumes map[string]*Volume
|
|
|
|
lock sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRepository(configPath string, driver graphdriver.Driver) (*Repository, error) {
|
|
|
|
abspath, err := filepath.Abs(configPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the config path
|
|
|
|
if err := os.MkdirAll(abspath, 0700); err != nil && !os.IsExist(err) {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
repo := &Repository{
|
|
|
|
driver: driver,
|
|
|
|
configPath: abspath,
|
|
|
|
volumes: make(map[string]*Volume),
|
|
|
|
}
|
|
|
|
|
|
|
|
return repo, repo.restore()
|
|
|
|
}
|
|
|
|
|
2014-09-26 20:42:24 -04:00
|
|
|
func (r *Repository) newVolume(path string, writable bool) (*Volume, error) {
|
2014-08-28 10:18:08 -04:00
|
|
|
var (
|
|
|
|
isBindMount bool
|
|
|
|
err error
|
2015-03-24 07:25:26 -04:00
|
|
|
id = stringid.GenerateRandomID()
|
2014-08-28 10:18:08 -04:00
|
|
|
)
|
|
|
|
if path != "" {
|
|
|
|
isBindMount = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if path == "" {
|
|
|
|
path, err = r.createNewVolumePath(id)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2014-10-20 15:27:26 -04:00
|
|
|
path = filepath.Clean(path)
|
2014-08-28 10:18:08 -04:00
|
|
|
|
2015-01-21 21:40:19 -05:00
|
|
|
// Ignore the error here since the path may not exist
|
|
|
|
// Really just want to make sure the path we are using is real(or non-existant)
|
|
|
|
if cleanPath, err := filepath.EvalSymlinks(path); err == nil {
|
|
|
|
path = cleanPath
|
2014-08-28 10:18:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
v := &Volume{
|
|
|
|
ID: id,
|
|
|
|
Path: path,
|
|
|
|
repository: r,
|
|
|
|
Writable: writable,
|
2014-10-02 20:46:17 -04:00
|
|
|
containers: make(map[string]struct{}),
|
2014-08-28 10:18:08 -04:00
|
|
|
configPath: r.configPath + "/" + id,
|
|
|
|
IsBindMount: isBindMount,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := v.initialize(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-09-26 20:42:24 -04:00
|
|
|
|
|
|
|
return v, r.add(v)
|
2014-08-28 10:18:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Repository) restore() error {
|
|
|
|
dir, err := ioutil.ReadDir(r.configPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, v := range dir {
|
|
|
|
id := v.Name()
|
2014-10-08 13:13:32 -04:00
|
|
|
vol := &Volume{
|
|
|
|
ID: id,
|
|
|
|
configPath: r.configPath + "/" + id,
|
|
|
|
containers: make(map[string]struct{}),
|
|
|
|
}
|
|
|
|
if err := vol.FromDisk(); err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
log.Debugf("Error restoring volume: %v", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err := vol.initialize(); err != nil {
|
|
|
|
log.Debugf("%s", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := r.add(vol); err != nil {
|
|
|
|
log.Debugf("Error restoring volume: %v", err)
|
2014-08-28 10:18:08 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Repository) Get(path string) *Volume {
|
|
|
|
r.lock.Lock()
|
2014-09-26 14:28:21 -04:00
|
|
|
vol := r.get(path)
|
2014-08-28 10:18:08 -04:00
|
|
|
r.lock.Unlock()
|
|
|
|
return vol
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Repository) get(path string) *Volume {
|
2014-09-26 14:28:21 -04:00
|
|
|
path, err := filepath.EvalSymlinks(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
2014-10-20 15:27:26 -04:00
|
|
|
return r.volumes[filepath.Clean(path)]
|
2014-08-28 10:18:08 -04:00
|
|
|
}
|
|
|
|
|
2014-09-26 20:42:24 -04:00
|
|
|
func (r *Repository) add(volume *Volume) error {
|
2014-08-28 10:18:08 -04:00
|
|
|
if vol := r.get(volume.Path); vol != nil {
|
|
|
|
return fmt.Errorf("Volume exists: %s", volume.ID)
|
|
|
|
}
|
|
|
|
r.volumes[volume.Path] = volume
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Repository) Delete(path string) error {
|
|
|
|
r.lock.Lock()
|
|
|
|
defer r.lock.Unlock()
|
2014-09-26 14:28:21 -04:00
|
|
|
path, err := filepath.EvalSymlinks(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-10-20 15:27:26 -04:00
|
|
|
volume := r.get(filepath.Clean(path))
|
2014-08-28 10:18:08 -04:00
|
|
|
if volume == nil {
|
|
|
|
return fmt.Errorf("Volume %s does not exist", path)
|
|
|
|
}
|
|
|
|
|
2014-10-02 20:46:17 -04:00
|
|
|
containers := volume.Containers()
|
|
|
|
if len(containers) > 0 {
|
|
|
|
return fmt.Errorf("Volume %s is being used and cannot be removed: used by containers %s", volume.Path, containers)
|
2014-08-28 10:18:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.RemoveAll(volume.configPath); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-01-23 14:14:52 -05:00
|
|
|
if !volume.IsBindMount {
|
|
|
|
if err := r.driver.Remove(volume.ID); err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
2014-08-28 10:18:08 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-23 13:58:28 -05:00
|
|
|
delete(r.volumes, volume.Path)
|
2014-08-28 10:18:08 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Repository) createNewVolumePath(id string) (string, error) {
|
|
|
|
if err := r.driver.Create(id, ""); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
path, err := r.driver.Get(id, "")
|
|
|
|
if err != nil {
|
2014-10-08 13:13:32 -04:00
|
|
|
return "", fmt.Errorf("Driver %s failed to get volume rootfs %s: %v", r.driver, id, err)
|
2014-08-28 10:18:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return path, nil
|
|
|
|
}
|
2014-09-26 20:42:24 -04:00
|
|
|
|
|
|
|
func (r *Repository) FindOrCreateVolume(path string, writable bool) (*Volume, error) {
|
|
|
|
r.lock.Lock()
|
|
|
|
defer r.lock.Unlock()
|
|
|
|
|
|
|
|
if path == "" {
|
|
|
|
return r.newVolume(path, writable)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v := r.get(path); v != nil {
|
|
|
|
return v, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return r.newVolume(path, writable)
|
|
|
|
}
|