Skip to content

Commit

Permalink
Support nfs local volume mounts
Browse files Browse the repository at this point in the history
Allow users to create volumes with nfs shares.

These volumes will get mounted when the first container gets started
and then will remain mounted until the system reboots or the volume is
removed.

Signed-off-by: Daniel J Walsh <[email protected]>
  • Loading branch information
rhatdan committed May 20, 2019
1 parent 8d54329 commit 49ee00a
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 3 deletions.
4 changes: 2 additions & 2 deletions cmd/podman/volume_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ var (
return volumeCreateCmd(&volumeCreateCommand)
},
Example: `podman volume create myvol
podman volume create
podman volume create --label foo=bar myvol`,
podman volume create --label foo=bar
podman volume create --opt type=nfs --opt o=addr=192.168.0.2,rw --opt device=:/nfsshare mynfsvol`,
}
)

Expand Down
10 changes: 9 additions & 1 deletion docs/podman-volume-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ Set metadata for a volume (e.g., --label mykey=value).

**-o**, **--opt**=[]

Set driver specific options.
Set driver specific options. To setup NFS volume you need to specify:

type: `-o type=nfs` To indicate the nfs mount.

o: `-o o=addr=nfsserver.example.com,rw` Options including the address of the nfs server.

device: `-o device=/nfsshare`, the remote nfs share.

## EXAMPLES

Expand All @@ -39,6 +45,8 @@ $ podman volume create myvol
$ podman volume create
$ podman volume create --label foo=bar myvol
# podman volume create --opt type=nfs --opt o=addr=192.168.0.2,rw --opt device=/nfsshare mynfsvol
```

## SEE ALSO
Expand Down
7 changes: 7 additions & 0 deletions libpod/runtime_ctr.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,13 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
return nil, errors.Wrapf(err, "error creating named volume %q", vol.Name)
}

options := newVol.Options()
if len(options) > 0 {
if err := newVol.Mount(); err != nil {
return nil, err
}
}

if err := ctr.copyWithTarFromImage(vol.Dest, newVol.MountPoint()); err != nil && !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "Failed to copy content into new volume mount %q", vol.Name)
}
Expand Down
62 changes: 62 additions & 0 deletions libpod/volume.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
package libpod

import (
"net"
"strings"

"github.com/containers/storage/pkg/mount"
"github.com/pkg/errors"
)

// Volume is the type used to create named volumes
// TODO: all volumes should be created using this and the Volume API
type Volume struct {
Expand Down Expand Up @@ -70,3 +78,57 @@ func (v *Volume) Scope() string {
func (v *Volume) IsCtrSpecific() bool {
return v.config.IsCtrSpecific
}

// Mount the volume
func (v *Volume) Mount() error {
if v.MountPoint() == "" {
return errors.Errorf("missing device in volume options")
}
mounted, err := mount.Mounted(v.MountPoint())
if err != nil {
return errors.Wrapf(err, "failed to determine if %v is mounted", v.Name())
}
if mounted {
return nil
}
options := v.Options()
if len(options) == 0 {
return errors.Errorf("volume %v is not mountable, no options available", v.Name())
}
mountOpts := options["o"]
device := options["device"]
if options["type"] == "nfs" {
if addrValue := getAddress(mountOpts); addrValue != "" && net.ParseIP(addrValue).To4() == nil {
ipAddr, err := net.ResolveIPAddr("ip", addrValue)
if err != nil {
return errors.Wrapf(err, "error resolving passed in nfs address")
}
mountOpts = strings.Replace(mountOpts, "addr="+addrValue, "addr="+ipAddr.String(), 1)
}
if device[0] != ':' {
device = ":" + device
}
}
err = mount.Mount(device, v.MountPoint(), options["type"], mountOpts)
return errors.Wrap(err, "failed to mount local volume")
}

// Unmount the volume from the system
func (v *Volume) Unmount() error {
if v.MountPoint() == "" {
return errors.Errorf("missing device in volume options")
}
return mount.Unmount(v.MountPoint())
}

// getAddress finds out address/hostname from options
func getAddress(opts string) string {
optsList := strings.Split(opts, ",")
for i := 0; i < len(optsList); i++ {
if strings.HasPrefix(optsList[i], "addr=") {
addr := strings.SplitN(optsList[i], "=", 2)[1]
return addr
}
}
return ""
}
1 change: 1 addition & 0 deletions libpod/volume_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ func newVolume(runtime *Runtime) (*Volume, error) {

// teardownStorage deletes the volume from volumePath
func (v *Volume) teardownStorage() error {
v.Unmount()
return os.RemoveAll(filepath.Join(v.runtime.config.VolumePath, v.Name()))
}

0 comments on commit 49ee00a

Please sign in to comment.