2014-08-28 10:18:08 -04:00
|
|
|
package volumes
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/docker/docker/pkg/symlink"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Volume struct {
|
|
|
|
ID string
|
|
|
|
Path string
|
|
|
|
IsBindMount bool
|
|
|
|
Writable bool
|
2014-10-02 20:46:17 -04:00
|
|
|
containers map[string]struct{}
|
2014-08-28 10:18:08 -04:00
|
|
|
configPath string
|
|
|
|
repository *Repository
|
|
|
|
lock sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Volume) IsDir() (bool, error) {
|
|
|
|
stat, err := os.Stat(v.Path)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return stat.IsDir(), nil
|
|
|
|
}
|
|
|
|
|
2014-10-02 20:46:17 -04:00
|
|
|
func (v *Volume) Containers() []string {
|
|
|
|
v.lock.Lock()
|
|
|
|
|
|
|
|
var containers []string
|
|
|
|
for c := range v.containers {
|
|
|
|
containers = append(containers, c)
|
|
|
|
}
|
|
|
|
|
|
|
|
v.lock.Unlock()
|
|
|
|
return containers
|
|
|
|
}
|
|
|
|
|
2014-08-28 10:18:08 -04:00
|
|
|
func (v *Volume) RemoveContainer(containerId string) {
|
|
|
|
v.lock.Lock()
|
2014-10-02 20:46:17 -04:00
|
|
|
delete(v.containers, containerId)
|
2014-08-28 10:18:08 -04:00
|
|
|
v.lock.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Volume) AddContainer(containerId string) {
|
|
|
|
v.lock.Lock()
|
2014-10-02 20:46:17 -04:00
|
|
|
v.containers[containerId] = struct{}{}
|
2014-08-28 10:18:08 -04:00
|
|
|
v.lock.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Volume) initialize() error {
|
|
|
|
v.lock.Lock()
|
|
|
|
defer v.lock.Unlock()
|
|
|
|
|
2015-03-24 17:11:45 -04:00
|
|
|
if _, err := os.Stat(v.Path); err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
2015-01-21 12:04:43 -05:00
|
|
|
if err := os.MkdirAll(v.Path, 0755); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-08-28 10:18:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.MkdirAll(v.configPath, 0755); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return v.toDisk()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Volume) ToDisk() error {
|
|
|
|
v.lock.Lock()
|
|
|
|
defer v.lock.Unlock()
|
|
|
|
return v.toDisk()
|
|
|
|
}
|
2015-01-21 12:04:43 -05:00
|
|
|
|
2014-08-28 10:18:08 -04:00
|
|
|
func (v *Volume) toDisk() error {
|
2015-04-20 16:05:48 -04:00
|
|
|
jsonPath, err := v.jsonPath()
|
2014-08-28 10:18:08 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-04-20 16:05:48 -04:00
|
|
|
f, err := os.OpenFile(jsonPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
2014-08-28 10:18:08 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-04-20 16:05:48 -04:00
|
|
|
if err := json.NewEncoder(f).Encode(v); err != nil {
|
|
|
|
f.Close()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return f.Close()
|
2014-08-28 10:18:08 -04:00
|
|
|
}
|
2015-01-21 12:04:43 -05:00
|
|
|
|
2014-08-28 10:18:08 -04:00
|
|
|
func (v *Volume) FromDisk() error {
|
|
|
|
v.lock.Lock()
|
|
|
|
defer v.lock.Unlock()
|
|
|
|
pth, err := v.jsonPath()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-11-04 08:46:45 -05:00
|
|
|
jsonSource, err := os.Open(pth)
|
2014-08-28 10:18:08 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-11-04 08:46:45 -05:00
|
|
|
defer jsonSource.Close()
|
2014-08-28 10:18:08 -04:00
|
|
|
|
2014-11-04 08:46:45 -05:00
|
|
|
dec := json.NewDecoder(jsonSource)
|
|
|
|
|
|
|
|
return dec.Decode(v)
|
2014-08-28 10:18:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Volume) jsonPath() (string, error) {
|
2015-03-05 20:53:06 -05:00
|
|
|
return v.GetRootResourcePath("config.json")
|
2014-08-28 10:18:08 -04:00
|
|
|
}
|
2015-03-05 20:53:06 -05:00
|
|
|
|
|
|
|
// Evalutes `path` in the scope of the volume's root path, with proper path
|
|
|
|
// sanitisation. Symlinks are all scoped to the root of the volume, as
|
|
|
|
// though the volume's root was `/`.
|
|
|
|
//
|
|
|
|
// The volume's root path is the host-facing path of the root of the volume's
|
|
|
|
// mountpoint inside a container.
|
|
|
|
//
|
|
|
|
// NOTE: The returned path is *only* safely scoped inside the volume's root
|
|
|
|
// if no component of the returned path changes (such as a component
|
|
|
|
// symlinking to a different path) between using this method and using the
|
|
|
|
// path. See symlink.FollowSymlinkInScope for more details.
|
|
|
|
func (v *Volume) GetResourcePath(path string) (string, error) {
|
2014-08-28 10:18:08 -04:00
|
|
|
cleanPath := filepath.Join("/", path)
|
2015-03-05 20:53:06 -05:00
|
|
|
return symlink.FollowSymlinkInScope(filepath.Join(v.Path, cleanPath), v.Path)
|
2014-08-28 10:18:08 -04:00
|
|
|
}
|
2014-09-24 09:07:11 -04:00
|
|
|
|
2015-03-05 20:53:06 -05:00
|
|
|
// Evalutes `path` in the scope of the volume's config path, with proper path
|
|
|
|
// sanitisation. Symlinks are all scoped to the root of the config path, as
|
|
|
|
// though the config path was `/`.
|
|
|
|
//
|
|
|
|
// The config path of a volume is not exposed to the container and is just used
|
|
|
|
// to store volume configuration options and other internal information. If in
|
|
|
|
// doubt, you probably want to just use v.GetResourcePath.
|
|
|
|
//
|
|
|
|
// NOTE: The returned path is *only* safely scoped inside the volume's config
|
|
|
|
// path if no component of the returned path changes (such as a component
|
|
|
|
// symlinking to a different path) between using this method and using the
|
|
|
|
// path. See symlink.FollowSymlinkInScope for more details.
|
|
|
|
func (v *Volume) GetRootResourcePath(path string) (string, error) {
|
2014-09-24 09:07:11 -04:00
|
|
|
cleanPath := filepath.Join("/", path)
|
2015-03-05 20:53:06 -05:00
|
|
|
return symlink.FollowSymlinkInScope(filepath.Join(v.configPath, cleanPath), v.configPath)
|
2014-09-24 09:07:11 -04:00
|
|
|
}
|