| package volumes |
| |
| import ( |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "sync" |
| |
| log "github.com/Sirupsen/logrus" |
| "github.com/docker/docker/daemon/graphdriver" |
| "github.com/docker/docker/pkg/common" |
| ) |
| |
| 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() |
| } |
| |
| func (r *Repository) newVolume(path string, writable bool) (*Volume, error) { |
| var ( |
| isBindMount bool |
| err error |
| id = common.GenerateRandomID() |
| ) |
| if path != "" { |
| isBindMount = true |
| } |
| |
| if path == "" { |
| path, err = r.createNewVolumePath(id) |
| if err != nil { |
| return nil, err |
| } |
| } |
| path = filepath.Clean(path) |
| |
| // 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 |
| } |
| |
| v := &Volume{ |
| ID: id, |
| Path: path, |
| repository: r, |
| Writable: writable, |
| containers: make(map[string]struct{}), |
| configPath: r.configPath + "/" + id, |
| IsBindMount: isBindMount, |
| } |
| |
| if err := v.initialize(); err != nil { |
| return nil, err |
| } |
| |
| return v, r.add(v) |
| } |
| |
| func (r *Repository) restore() error { |
| dir, err := ioutil.ReadDir(r.configPath) |
| if err != nil { |
| return err |
| } |
| |
| for _, v := range dir { |
| id := v.Name() |
| 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) |
| } |
| } |
| return nil |
| } |
| |
| func (r *Repository) Get(path string) *Volume { |
| r.lock.Lock() |
| vol := r.get(path) |
| r.lock.Unlock() |
| return vol |
| } |
| |
| func (r *Repository) get(path string) *Volume { |
| path, err := filepath.EvalSymlinks(path) |
| if err != nil { |
| return nil |
| } |
| return r.volumes[filepath.Clean(path)] |
| } |
| |
| func (r *Repository) add(volume *Volume) error { |
| 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() |
| path, err := filepath.EvalSymlinks(path) |
| if err != nil { |
| return err |
| } |
| volume := r.get(filepath.Clean(path)) |
| if volume == nil { |
| return fmt.Errorf("Volume %s does not exist", path) |
| } |
| |
| 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) |
| } |
| |
| if err := os.RemoveAll(volume.configPath); err != nil { |
| return err |
| } |
| |
| if !volume.IsBindMount { |
| if err := r.driver.Remove(volume.ID); err != nil { |
| if !os.IsNotExist(err) { |
| return err |
| } |
| } |
| } |
| |
| delete(r.volumes, volume.Path) |
| 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 { |
| return "", fmt.Errorf("Driver %s failed to get volume rootfs %s: %v", r.driver, id, err) |
| } |
| |
| return path, nil |
| } |
| |
| 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) |
| } |