|
@@ -4,13 +4,16 @@
|
|
|
package local
|
|
|
|
|
|
import (
|
|
|
+ "encoding/json"
|
|
|
"fmt"
|
|
|
"io/ioutil"
|
|
|
"os"
|
|
|
"path/filepath"
|
|
|
"sync"
|
|
|
|
|
|
+ "github.com/Sirupsen/logrus"
|
|
|
"github.com/docker/docker/pkg/idtools"
|
|
|
+ "github.com/docker/docker/pkg/mount"
|
|
|
"github.com/docker/docker/utils"
|
|
|
"github.com/docker/docker/volume"
|
|
|
)
|
|
@@ -40,6 +43,11 @@ func (validationError) IsValidationError() bool {
|
|
|
return true
|
|
|
}
|
|
|
|
|
|
+type activeMount struct {
|
|
|
+ count uint64
|
|
|
+ mounted bool
|
|
|
+}
|
|
|
+
|
|
|
// New instantiates a new Root instance with the provided scope. Scope
|
|
|
// is the base path that the Root instance uses to store its
|
|
|
// volumes. The base path is created here if it does not exist.
|
|
@@ -63,13 +71,32 @@ func New(scope string, rootUID, rootGID int) (*Root, error) {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
+ mountInfos, err := mount.GetMounts()
|
|
|
+ if err != nil {
|
|
|
+ logrus.Debugf("error looking up mounts for local volume cleanup: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
for _, d := range dirs {
|
|
|
name := filepath.Base(d.Name())
|
|
|
- r.volumes[name] = &localVolume{
|
|
|
+ v := &localVolume{
|
|
|
driverName: r.Name(),
|
|
|
name: name,
|
|
|
path: r.DataPath(name),
|
|
|
}
|
|
|
+ r.volumes[name] = v
|
|
|
+ if b, err := ioutil.ReadFile(filepath.Join(name, "opts.json")); err == nil {
|
|
|
+ if err := json.Unmarshal(b, v.opts); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ // unmount anything that may still be mounted (for example, from an unclean shutdown)
|
|
|
+ for _, info := range mountInfos {
|
|
|
+ if info.Mountpoint == v.path {
|
|
|
+ mount.Unmount(v.path)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return r, nil
|
|
@@ -109,7 +136,7 @@ func (r *Root) Name() string {
|
|
|
// Create creates a new volume.Volume with the provided name, creating
|
|
|
// the underlying directory tree required for this volume in the
|
|
|
// process.
|
|
|
-func (r *Root) Create(name string, _ map[string]string) (volume.Volume, error) {
|
|
|
+func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error) {
|
|
|
if err := r.validateName(name); err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
@@ -129,11 +156,34 @@ func (r *Root) Create(name string, _ map[string]string) (volume.Volume, error) {
|
|
|
}
|
|
|
return nil, err
|
|
|
}
|
|
|
+
|
|
|
+ var err error
|
|
|
+ defer func() {
|
|
|
+ if err != nil {
|
|
|
+ os.RemoveAll(filepath.Dir(path))
|
|
|
+ }
|
|
|
+ }()
|
|
|
+
|
|
|
v = &localVolume{
|
|
|
driverName: r.Name(),
|
|
|
name: name,
|
|
|
path: path,
|
|
|
}
|
|
|
+
|
|
|
+ if opts != nil {
|
|
|
+ if err = setOpts(v, opts); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ var b []byte
|
|
|
+ b, err = json.Marshal(v.opts)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if err = ioutil.WriteFile(filepath.Join(filepath.Dir(path), "opts.json"), b, 600); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
r.volumes[name] = v
|
|
|
return v, nil
|
|
|
}
|
|
@@ -210,6 +260,10 @@ type localVolume struct {
|
|
|
path string
|
|
|
// driverName is the name of the driver that created the volume.
|
|
|
driverName string
|
|
|
+ // opts is the parsed list of options used to create the volume
|
|
|
+ opts *optsConfig
|
|
|
+ // active refcounts the active mounts
|
|
|
+ active activeMount
|
|
|
}
|
|
|
|
|
|
// Name returns the name of the given Volume.
|
|
@@ -229,10 +283,42 @@ func (v *localVolume) Path() string {
|
|
|
|
|
|
// Mount implements the localVolume interface, returning the data location.
|
|
|
func (v *localVolume) Mount() (string, error) {
|
|
|
+ v.m.Lock()
|
|
|
+ defer v.m.Unlock()
|
|
|
+ if v.opts != nil {
|
|
|
+ if !v.active.mounted {
|
|
|
+ if err := v.mount(); err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+ v.active.mounted = true
|
|
|
+ }
|
|
|
+ v.active.count++
|
|
|
+ }
|
|
|
return v.path, nil
|
|
|
}
|
|
|
|
|
|
// Umount is for satisfying the localVolume interface and does not do anything in this driver.
|
|
|
func (v *localVolume) Unmount() error {
|
|
|
+ v.m.Lock()
|
|
|
+ defer v.m.Unlock()
|
|
|
+ if v.opts != nil {
|
|
|
+ v.active.count--
|
|
|
+ if v.active.count == 0 {
|
|
|
+ if err := mount.Unmount(v.path); err != nil {
|
|
|
+ v.active.count++
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ v.active.mounted = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func validateOpts(opts map[string]string) error {
|
|
|
+ for opt := range opts {
|
|
|
+ if !validOpts[opt] {
|
|
|
+ return validationError{fmt.Errorf("invalid option key: %q", opt)}
|
|
|
+ }
|
|
|
+ }
|
|
|
return nil
|
|
|
}
|