Allows users to submit options similar to the `mount` command when
creating a volume with the `local` volume driver.
For example:
```go
$ docker volume create -d local --opt type=nfs --opt device=myNfsServer:/data --opt o=noatime,nosuid
```
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
| ... | ... |
@@ -21,10 +21,12 @@ parent = "smn_cli" |
| 21 | 21 |
|
| 22 | 22 |
Creates a new volume that containers can consume and store data in. If a name is not specified, Docker generates a random name. You create a volume and then configure the container to use it, for example: |
| 23 | 23 |
|
| 24 |
- $ docker volume create --name hello |
|
| 25 |
- hello |
|
| 24 |
+```bash |
|
| 25 |
+$ docker volume create --name hello |
|
| 26 |
+hello |
|
| 26 | 27 |
|
| 27 |
- $ docker run -d -v hello:/world busybox ls /world |
|
| 28 |
+$ docker run -d -v hello:/world busybox ls /world |
|
| 29 |
+``` |
|
| 28 | 30 |
|
| 29 | 31 |
The mount is created inside the container's `/world` directory. Docker does not support relative paths for mount points inside the container. |
| 30 | 32 |
|
| ... | ... |
@@ -42,16 +44,32 @@ If you specify a volume name already in use on the current driver, Docker assume |
| 42 | 42 |
|
| 43 | 43 |
Some volume drivers may take options to customize the volume creation. Use the `-o` or `--opt` flags to pass driver options: |
| 44 | 44 |
|
| 45 |
- $ docker volume create --driver fake --opt tardis=blue --opt timey=wimey |
|
| 45 |
+```bash |
|
| 46 |
+$ docker volume create --driver fake --opt tardis=blue --opt timey=wimey |
|
| 47 |
+``` |
|
| 46 | 48 |
|
| 47 | 49 |
These options are passed directly to the volume driver. Options for |
| 48 | 50 |
different volume drivers may do different things (or nothing at all). |
| 49 | 51 |
|
| 50 |
-*Note*: The built-in `local` volume driver does not currently accept any options. |
|
| 52 |
+The built-in `local` driver on Windows does not support any options. |
|
| 53 |
+ |
|
| 54 |
+The built-in `local` driver on Linux accepts options similar to the linux `mount` |
|
| 55 |
+command: |
|
| 56 |
+ |
|
| 57 |
+```bash |
|
| 58 |
+$ docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 |
|
| 59 |
+``` |
|
| 60 |
+ |
|
| 61 |
+Another example: |
|
| 62 |
+ |
|
| 63 |
+```bash |
|
| 64 |
+$ docker volume create --driver local --opt type=btrfs --opt device=/dev/sda2 |
|
| 65 |
+``` |
|
| 66 |
+ |
|
| 51 | 67 |
|
| 52 | 68 |
## Related information |
| 53 | 69 |
|
| 54 | 70 |
* [volume inspect](volume_inspect.md) |
| 55 | 71 |
* [volume ls](volume_ls.md) |
| 56 | 72 |
* [volume rm](volume_rm.md) |
| 57 |
-* [Understand Data Volumes](../../userguide/containers/dockervolumes.md) |
|
| 58 | 73 |
\ No newline at end of file |
| 74 |
+* [Understand Data Volumes](../../userguide/containers/dockervolumes.md) |
| ... | ... |
@@ -218,3 +218,26 @@ func (s *DockerSuite) TestVolumeCliInspectTmplError(c *check.C) {
|
| 218 | 218 |
c.Assert(exitCode, checker.Equals, 1, check.Commentf("Output: %s", out))
|
| 219 | 219 |
c.Assert(out, checker.Contains, "Template parsing error") |
| 220 | 220 |
} |
| 221 |
+ |
|
| 222 |
+func (s *DockerSuite) TestVolumeCliCreateWithOpts(c *check.C) {
|
|
| 223 |
+ testRequires(c, DaemonIsLinux) |
|
| 224 |
+ |
|
| 225 |
+ dockerCmd(c, "volume", "create", "-d", "local", "--name", "test", "--opt=type=tmpfs", "--opt=device=tmpfs", "--opt=o=size=1m,uid=1000") |
|
| 226 |
+ out, _ := dockerCmd(c, "run", "-v", "test:/foo", "busybox", "mount") |
|
| 227 |
+ |
|
| 228 |
+ mounts := strings.Split(out, "\n") |
|
| 229 |
+ var found bool |
|
| 230 |
+ for _, m := range mounts {
|
|
| 231 |
+ if strings.Contains(m, "/foo") {
|
|
| 232 |
+ found = true |
|
| 233 |
+ info := strings.Fields(m) |
|
| 234 |
+ // tmpfs on <path> type tmpfs (rw,relatime,size=1024k,uid=1000) |
|
| 235 |
+ c.Assert(info[0], checker.Equals, "tmpfs") |
|
| 236 |
+ c.Assert(info[2], checker.Equals, "/foo") |
|
| 237 |
+ c.Assert(info[4], checker.Equals, "tmpfs") |
|
| 238 |
+ c.Assert(info[5], checker.Contains, "uid=1000") |
|
| 239 |
+ c.Assert(info[5], checker.Contains, "size=1024k") |
|
| 240 |
+ } |
|
| 241 |
+ } |
|
| 242 |
+ c.Assert(found, checker.Equals, true) |
|
| 243 |
+} |
| ... | ... |
@@ -15,11 +15,9 @@ docker-volume-create - Create a new volume |
| 15 | 15 |
|
| 16 | 16 |
Creates a new volume that containers can consume and store data in. If a name is not specified, Docker generates a random name. You create a volume and then configure the container to use it, for example: |
| 17 | 17 |
|
| 18 |
- ``` |
|
| 19 |
- $ docker volume create --name hello |
|
| 20 |
- hello |
|
| 21 |
- $ docker run -d -v hello:/world busybox ls /world |
|
| 22 |
- ``` |
|
| 18 |
+ $ docker volume create --name hello |
|
| 19 |
+ hello |
|
| 20 |
+ $ docker run -d -v hello:/world busybox ls /world |
|
| 23 | 21 |
|
| 24 | 22 |
The mount is created inside the container's `/src` directory. Docker doesn't not support relative paths for mount points inside the container. |
| 25 | 23 |
|
| ... | ... |
@@ -29,14 +27,22 @@ Multiple containers can use the same volume in the same time period. This is use |
| 29 | 29 |
|
| 30 | 30 |
Some volume drivers may take options to customize the volume creation. Use the `-o` or `--opt` flags to pass driver options: |
| 31 | 31 |
|
| 32 |
- ``` |
|
| 33 |
- $ docker volume create --driver fake --opt tardis=blue --opt timey=wimey |
|
| 34 |
- ``` |
|
| 32 |
+ $ docker volume create --driver fake --opt tardis=blue --opt timey=wimey |
|
| 35 | 33 |
|
| 36 | 34 |
These options are passed directly to the volume driver. Options for |
| 37 | 35 |
different volume drivers may do different things (or nothing at all). |
| 38 | 36 |
|
| 39 |
-*Note*: The built-in `local` volume driver does not currently accept any options. |
|
| 37 |
+The built-in `local` driver on Windows does not support any options. |
|
| 38 |
+ |
|
| 39 |
+The built-in `local` driver on Linux accepts options similar to the linux `mount` |
|
| 40 |
+command: |
|
| 41 |
+ |
|
| 42 |
+ $ docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 |
|
| 43 |
+ |
|
| 44 |
+Another example: |
|
| 45 |
+ |
|
| 46 |
+ $ docker volume create --driver local --opt type=btrfs --opt device=/dev/sda2 |
|
| 47 |
+ |
|
| 40 | 48 |
|
| 41 | 49 |
# OPTIONS |
| 42 | 50 |
**-d**, **--driver**="*local*" |
| ... | ... |
@@ -4,13 +4,16 @@ |
| 4 | 4 |
package local |
| 5 | 5 |
|
| 6 | 6 |
import ( |
| 7 |
+ "encoding/json" |
|
| 7 | 8 |
"fmt" |
| 8 | 9 |
"io/ioutil" |
| 9 | 10 |
"os" |
| 10 | 11 |
"path/filepath" |
| 11 | 12 |
"sync" |
| 12 | 13 |
|
| 14 |
+ "github.com/Sirupsen/logrus" |
|
| 13 | 15 |
"github.com/docker/docker/pkg/idtools" |
| 16 |
+ "github.com/docker/docker/pkg/mount" |
|
| 14 | 17 |
"github.com/docker/docker/utils" |
| 15 | 18 |
"github.com/docker/docker/volume" |
| 16 | 19 |
) |
| ... | ... |
@@ -40,6 +43,11 @@ func (validationError) IsValidationError() bool {
|
| 40 | 40 |
return true |
| 41 | 41 |
} |
| 42 | 42 |
|
| 43 |
+type activeMount struct {
|
|
| 44 |
+ count uint64 |
|
| 45 |
+ mounted bool |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 43 | 48 |
// New instantiates a new Root instance with the provided scope. Scope |
| 44 | 49 |
// is the base path that the Root instance uses to store its |
| 45 | 50 |
// 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) {
|
| 63 | 63 |
return nil, err |
| 64 | 64 |
} |
| 65 | 65 |
|
| 66 |
+ mountInfos, err := mount.GetMounts() |
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ logrus.Debugf("error looking up mounts for local volume cleanup: %v", err)
|
|
| 69 |
+ } |
|
| 70 |
+ |
|
| 66 | 71 |
for _, d := range dirs {
|
| 67 | 72 |
name := filepath.Base(d.Name()) |
| 68 |
- r.volumes[name] = &localVolume{
|
|
| 73 |
+ v := &localVolume{
|
|
| 69 | 74 |
driverName: r.Name(), |
| 70 | 75 |
name: name, |
| 71 | 76 |
path: r.DataPath(name), |
| 72 | 77 |
} |
| 78 |
+ r.volumes[name] = v |
|
| 79 |
+ if b, err := ioutil.ReadFile(filepath.Join(name, "opts.json")); err == nil {
|
|
| 80 |
+ if err := json.Unmarshal(b, v.opts); err != nil {
|
|
| 81 |
+ return nil, err |
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ // unmount anything that may still be mounted (for example, from an unclean shutdown) |
|
| 85 |
+ for _, info := range mountInfos {
|
|
| 86 |
+ if info.Mountpoint == v.path {
|
|
| 87 |
+ mount.Unmount(v.path) |
|
| 88 |
+ break |
|
| 89 |
+ } |
|
| 90 |
+ } |
|
| 91 |
+ } |
|
| 73 | 92 |
} |
| 74 | 93 |
|
| 75 | 94 |
return r, nil |
| ... | ... |
@@ -109,7 +136,7 @@ func (r *Root) Name() string {
|
| 109 | 109 |
// Create creates a new volume.Volume with the provided name, creating |
| 110 | 110 |
// the underlying directory tree required for this volume in the |
| 111 | 111 |
// process. |
| 112 |
-func (r *Root) Create(name string, _ map[string]string) (volume.Volume, error) {
|
|
| 112 |
+func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error) {
|
|
| 113 | 113 |
if err := r.validateName(name); err != nil {
|
| 114 | 114 |
return nil, err |
| 115 | 115 |
} |
| ... | ... |
@@ -129,11 +156,34 @@ func (r *Root) Create(name string, _ map[string]string) (volume.Volume, error) {
|
| 129 | 129 |
} |
| 130 | 130 |
return nil, err |
| 131 | 131 |
} |
| 132 |
+ |
|
| 133 |
+ var err error |
|
| 134 |
+ defer func() {
|
|
| 135 |
+ if err != nil {
|
|
| 136 |
+ os.RemoveAll(filepath.Dir(path)) |
|
| 137 |
+ } |
|
| 138 |
+ }() |
|
| 139 |
+ |
|
| 132 | 140 |
v = &localVolume{
|
| 133 | 141 |
driverName: r.Name(), |
| 134 | 142 |
name: name, |
| 135 | 143 |
path: path, |
| 136 | 144 |
} |
| 145 |
+ |
|
| 146 |
+ if opts != nil {
|
|
| 147 |
+ if err = setOpts(v, opts); err != nil {
|
|
| 148 |
+ return nil, err |
|
| 149 |
+ } |
|
| 150 |
+ var b []byte |
|
| 151 |
+ b, err = json.Marshal(v.opts) |
|
| 152 |
+ if err != nil {
|
|
| 153 |
+ return nil, err |
|
| 154 |
+ } |
|
| 155 |
+ if err = ioutil.WriteFile(filepath.Join(filepath.Dir(path), "opts.json"), b, 600); err != nil {
|
|
| 156 |
+ return nil, err |
|
| 157 |
+ } |
|
| 158 |
+ } |
|
| 159 |
+ |
|
| 137 | 160 |
r.volumes[name] = v |
| 138 | 161 |
return v, nil |
| 139 | 162 |
} |
| ... | ... |
@@ -210,6 +260,10 @@ type localVolume struct {
|
| 210 | 210 |
path string |
| 211 | 211 |
// driverName is the name of the driver that created the volume. |
| 212 | 212 |
driverName string |
| 213 |
+ // opts is the parsed list of options used to create the volume |
|
| 214 |
+ opts *optsConfig |
|
| 215 |
+ // active refcounts the active mounts |
|
| 216 |
+ active activeMount |
|
| 213 | 217 |
} |
| 214 | 218 |
|
| 215 | 219 |
// Name returns the name of the given Volume. |
| ... | ... |
@@ -229,10 +283,42 @@ func (v *localVolume) Path() string {
|
| 229 | 229 |
|
| 230 | 230 |
// Mount implements the localVolume interface, returning the data location. |
| 231 | 231 |
func (v *localVolume) Mount() (string, error) {
|
| 232 |
+ v.m.Lock() |
|
| 233 |
+ defer v.m.Unlock() |
|
| 234 |
+ if v.opts != nil {
|
|
| 235 |
+ if !v.active.mounted {
|
|
| 236 |
+ if err := v.mount(); err != nil {
|
|
| 237 |
+ return "", err |
|
| 238 |
+ } |
|
| 239 |
+ v.active.mounted = true |
|
| 240 |
+ } |
|
| 241 |
+ v.active.count++ |
|
| 242 |
+ } |
|
| 232 | 243 |
return v.path, nil |
| 233 | 244 |
} |
| 234 | 245 |
|
| 235 | 246 |
// Umount is for satisfying the localVolume interface and does not do anything in this driver. |
| 236 | 247 |
func (v *localVolume) Unmount() error {
|
| 248 |
+ v.m.Lock() |
|
| 249 |
+ defer v.m.Unlock() |
|
| 250 |
+ if v.opts != nil {
|
|
| 251 |
+ v.active.count-- |
|
| 252 |
+ if v.active.count == 0 {
|
|
| 253 |
+ if err := mount.Unmount(v.path); err != nil {
|
|
| 254 |
+ v.active.count++ |
|
| 255 |
+ return err |
|
| 256 |
+ } |
|
| 257 |
+ v.active.mounted = false |
|
| 258 |
+ } |
|
| 259 |
+ } |
|
| 260 |
+ return nil |
|
| 261 |
+} |
|
| 262 |
+ |
|
| 263 |
+func validateOpts(opts map[string]string) error {
|
|
| 264 |
+ for opt := range opts {
|
|
| 265 |
+ if !validOpts[opt] {
|
|
| 266 |
+ return validationError{fmt.Errorf("invalid option key: %q", opt)}
|
|
| 267 |
+ } |
|
| 268 |
+ } |
|
| 237 | 269 |
return nil |
| 238 | 270 |
} |
| ... | ... |
@@ -4,7 +4,10 @@ import ( |
| 4 | 4 |
"io/ioutil" |
| 5 | 5 |
"os" |
| 6 | 6 |
"runtime" |
| 7 |
+ "strings" |
|
| 7 | 8 |
"testing" |
| 9 |
+ |
|
| 10 |
+ "github.com/docker/docker/pkg/mount" |
|
| 8 | 11 |
) |
| 9 | 12 |
|
| 10 | 13 |
func TestRemove(t *testing.T) {
|
| ... | ... |
@@ -151,3 +154,96 @@ func TestValidateName(t *testing.T) {
|
| 151 | 151 |
} |
| 152 | 152 |
} |
| 153 | 153 |
} |
| 154 |
+ |
|
| 155 |
+func TestCreateWithOpts(t *testing.T) {
|
|
| 156 |
+ if runtime.GOOS == "windows" {
|
|
| 157 |
+ t.Skip() |
|
| 158 |
+ } |
|
| 159 |
+ |
|
| 160 |
+ rootDir, err := ioutil.TempDir("", "local-volume-test")
|
|
| 161 |
+ if err != nil {
|
|
| 162 |
+ t.Fatal(err) |
|
| 163 |
+ } |
|
| 164 |
+ defer os.RemoveAll(rootDir) |
|
| 165 |
+ |
|
| 166 |
+ r, err := New(rootDir, 0, 0) |
|
| 167 |
+ if err != nil {
|
|
| 168 |
+ t.Fatal(err) |
|
| 169 |
+ } |
|
| 170 |
+ |
|
| 171 |
+ if _, err := r.Create("test", map[string]string{"invalidopt": "notsupported"}); err == nil {
|
|
| 172 |
+ t.Fatal("expected invalid opt to cause error")
|
|
| 173 |
+ } |
|
| 174 |
+ |
|
| 175 |
+ vol, err := r.Create("test", map[string]string{"device": "tmpfs", "type": "tmpfs", "o": "size=1m,uid=1000"})
|
|
| 176 |
+ if err != nil {
|
|
| 177 |
+ t.Fatal(err) |
|
| 178 |
+ } |
|
| 179 |
+ v := vol.(*localVolume) |
|
| 180 |
+ |
|
| 181 |
+ dir, err := v.Mount() |
|
| 182 |
+ if err != nil {
|
|
| 183 |
+ t.Fatal(err) |
|
| 184 |
+ } |
|
| 185 |
+ defer func() {
|
|
| 186 |
+ if err := v.Unmount(); err != nil {
|
|
| 187 |
+ t.Fatal(err) |
|
| 188 |
+ } |
|
| 189 |
+ }() |
|
| 190 |
+ |
|
| 191 |
+ mountInfos, err := mount.GetMounts() |
|
| 192 |
+ if err != nil {
|
|
| 193 |
+ t.Fatal(err) |
|
| 194 |
+ } |
|
| 195 |
+ |
|
| 196 |
+ var found bool |
|
| 197 |
+ for _, info := range mountInfos {
|
|
| 198 |
+ if info.Mountpoint == dir {
|
|
| 199 |
+ found = true |
|
| 200 |
+ if info.Fstype != "tmpfs" {
|
|
| 201 |
+ t.Fatalf("expected tmpfs mount, got %q", info.Fstype)
|
|
| 202 |
+ } |
|
| 203 |
+ if info.Source != "tmpfs" {
|
|
| 204 |
+ t.Fatalf("expected tmpfs mount, got %q", info.Source)
|
|
| 205 |
+ } |
|
| 206 |
+ if !strings.Contains(info.VfsOpts, "uid=1000") {
|
|
| 207 |
+ t.Fatalf("expected mount info to have uid=1000: %q", info.VfsOpts)
|
|
| 208 |
+ } |
|
| 209 |
+ if !strings.Contains(info.VfsOpts, "size=1024k") {
|
|
| 210 |
+ t.Fatalf("expected mount info to have size=1024k: %q", info.VfsOpts)
|
|
| 211 |
+ } |
|
| 212 |
+ break |
|
| 213 |
+ } |
|
| 214 |
+ } |
|
| 215 |
+ |
|
| 216 |
+ if !found {
|
|
| 217 |
+ t.Fatal("mount not found")
|
|
| 218 |
+ } |
|
| 219 |
+ |
|
| 220 |
+ if v.active.count != 1 {
|
|
| 221 |
+ t.Fatalf("Expected active mount count to be 1, got %d", v.active.count)
|
|
| 222 |
+ } |
|
| 223 |
+ |
|
| 224 |
+ // test double mount |
|
| 225 |
+ if _, err := v.Mount(); err != nil {
|
|
| 226 |
+ t.Fatal(err) |
|
| 227 |
+ } |
|
| 228 |
+ if v.active.count != 2 {
|
|
| 229 |
+ t.Fatalf("Expected active mount count to be 2, got %d", v.active.count)
|
|
| 230 |
+ } |
|
| 231 |
+ |
|
| 232 |
+ if err := v.Unmount(); err != nil {
|
|
| 233 |
+ t.Fatal(err) |
|
| 234 |
+ } |
|
| 235 |
+ if v.active.count != 1 {
|
|
| 236 |
+ t.Fatalf("Expected active mount count to be 1, got %d", v.active.count)
|
|
| 237 |
+ } |
|
| 238 |
+ |
|
| 239 |
+ mounted, err := mount.Mounted(v.path) |
|
| 240 |
+ if err != nil {
|
|
| 241 |
+ t.Fatal(err) |
|
| 242 |
+ } |
|
| 243 |
+ if !mounted {
|
|
| 244 |
+ t.Fatal("expected mount to still be active")
|
|
| 245 |
+ } |
|
| 246 |
+} |
| ... | ... |
@@ -6,11 +6,28 @@ |
| 6 | 6 |
package local |
| 7 | 7 |
|
| 8 | 8 |
import ( |
| 9 |
+ "fmt" |
|
| 9 | 10 |
"path/filepath" |
| 10 | 11 |
"strings" |
| 12 |
+ |
|
| 13 |
+ "github.com/docker/docker/pkg/mount" |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+var ( |
|
| 17 |
+ oldVfsDir = filepath.Join("vfs", "dir")
|
|
| 18 |
+ |
|
| 19 |
+ validOpts = map[string]bool{
|
|
| 20 |
+ "type": true, // specify the filesystem type for mount, e.g. nfs |
|
| 21 |
+ "o": true, // generic mount options |
|
| 22 |
+ "device": true, // device to mount from |
|
| 23 |
+ } |
|
| 11 | 24 |
) |
| 12 | 25 |
|
| 13 |
-var oldVfsDir = filepath.Join("vfs", "dir")
|
|
| 26 |
+type optsConfig struct {
|
|
| 27 |
+ MountType string |
|
| 28 |
+ MountOpts string |
|
| 29 |
+ MountDevice string |
|
| 30 |
+} |
|
| 14 | 31 |
|
| 15 | 32 |
// scopedPath verifies that the path where the volume is located |
| 16 | 33 |
// is under Docker's root and the valid local paths. |
| ... | ... |
@@ -27,3 +44,26 @@ func (r *Root) scopedPath(realPath string) bool {
|
| 27 | 27 |
|
| 28 | 28 |
return false |
| 29 | 29 |
} |
| 30 |
+ |
|
| 31 |
+func setOpts(v *localVolume, opts map[string]string) error {
|
|
| 32 |
+ if len(opts) == 0 {
|
|
| 33 |
+ return nil |
|
| 34 |
+ } |
|
| 35 |
+ if err := validateOpts(opts); err != nil {
|
|
| 36 |
+ return err |
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ v.opts = &optsConfig{
|
|
| 40 |
+ MountType: opts["type"], |
|
| 41 |
+ MountOpts: opts["o"], |
|
| 42 |
+ MountDevice: opts["device"], |
|
| 43 |
+ } |
|
| 44 |
+ return nil |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func (v *localVolume) mount() error {
|
|
| 48 |
+ if v.opts.MountDevice == "" {
|
|
| 49 |
+ return fmt.Errorf("missing device in volume options")
|
|
| 50 |
+ } |
|
| 51 |
+ return mount.Mount(v.opts.MountDevice, v.path, v.opts.MountType, v.opts.MountOpts) |
|
| 52 |
+} |
| ... | ... |
@@ -4,10 +4,15 @@ |
| 4 | 4 |
package local |
| 5 | 5 |
|
| 6 | 6 |
import ( |
| 7 |
+ "fmt" |
|
| 7 | 8 |
"path/filepath" |
| 8 | 9 |
"strings" |
| 9 | 10 |
) |
| 10 | 11 |
|
| 12 |
+type optsConfig struct{}
|
|
| 13 |
+ |
|
| 14 |
+var validOpts map[string]bool |
|
| 15 |
+ |
|
| 11 | 16 |
// scopedPath verifies that the path where the volume is located |
| 12 | 17 |
// is under Docker's root and the valid local paths. |
| 13 | 18 |
func (r *Root) scopedPath(realPath string) bool {
|
| ... | ... |
@@ -16,3 +21,14 @@ func (r *Root) scopedPath(realPath string) bool {
|
| 16 | 16 |
} |
| 17 | 17 |
return false |
| 18 | 18 |
} |
| 19 |
+ |
|
| 20 |
+func setOpts(v *localVolume, opts map[string]string) error {
|
|
| 21 |
+ if len(opts) > 0 {
|
|
| 22 |
+ return fmt.Errorf("options are not supported on this platform")
|
|
| 23 |
+ } |
|
| 24 |
+ return nil |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+func (v *localVolume) mount() error {
|
|
| 28 |
+ return nil |
|
| 29 |
+} |