Skip to content

Commit

Permalink
Issue #81 - NFS: Support for clusters without the need for docker vol…
Browse files Browse the repository at this point in the history
…ume create
  • Loading branch information
gondor committed Oct 7, 2016
1 parent 5533e15 commit a72b5d1
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 44 deletions.
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
VERSION = 0.30
VERSION = 0.31
GO_FMT = gofmt -s -w -l .
GO_XC = goxc -os="linux" -bc="linux,amd64" -tasks-="rmbin"
GO_XC = goxc -os="linux" -bc="linux,amd64,arm" -tasks-="rmbin"

GOXC_FILE = .goxc.local.json

Expand All @@ -20,6 +20,7 @@ goxc:
$(shell echo ' }\n } \n}' >> $(GOXC_FILE))
$(GO_XC)
cp build/$(VERSION)/linux_amd64/docker-volume-netshare build/$(VERSION)/docker-volume-netshare_$(VERSION)_linux_amd64-bin
cp build/$(VERSION)/linux_arm/docker-volume-netshare build/$(VERSION)/docker-volume-netshare_$(VERSION)_linux_arm-bin

deps:
go get
Expand Down
44 changes: 27 additions & 17 deletions netshare/drivers/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,46 +26,56 @@ func (v volumeDriver) Create(r volume.Request) volume.Response {
v.m.Lock()
defer v.m.Unlock()

log.Debugf("Create volume -> name: %s, %v", r.Name, r.Options)
resName, resOpts := resolveName(r.Name)
if resOpts != nil {
// Check to make sure there aren't options, otherwise override
if len(r.Options) == 0 {
r.Options = resOpts
}
}
log.Debugf("Create volume -> name: %s, %v", resName, r.Options)

dest := mountpoint(v.root, r.Name)
dest := mountpoint(v.root, resName)
if err := createDest(dest); err != nil {
return volume.Response{Err: err.Error()}
}

v.mountm.Create(r.Name, dest, r.Options)
//if v.mountm.GetOption(r.Name, ShareOpt) != "" && v.mountm.GetOptionAsBool(r.Name, CreateOpt) {
// log.Debugf("Create volume -> name: %s, creating option found, creating: %s", r.Name, r.Options)
//
//}
v.mountm.Create(resName, dest, r.Options)
return volume.Response{}
}

func (v volumeDriver) Remove(r volume.Request) volume.Response {
log.Debugf("Entering Remove: name: %s, options %v", r.Name, r.Options)

resolvedName, _ := resolveName(r.Name)

log.Debugf("Entering Remove: name: %s, resolved-name: %s, options %v", r.Name, resolvedName, r.Options)
v.m.Lock()
defer v.m.Unlock()

if err := v.mountm.Delete(r.Name); err != nil {
if err := v.mountm.Delete(resolvedName); err != nil {
return volume.Response{Err: err.Error()}
}
return volume.Response{}
}

func (v volumeDriver) Path(r volume.Request) volume.Response {
log.Debugf("Host path for %s is at %s", r.Name, mountpoint(v.root, r.Name))
return volume.Response{Mountpoint: mountpoint(v.root, r.Name)}
resolvedName, _ := resolveName(r.Name)

log.Debugf("Host path for %s (%s) is at %s", r.Name, resolvedName, mountpoint(v.root, resolvedName))
return volume.Response{Mountpoint: mountpoint(v.root, resolvedName)}
}

func (v volumeDriver) Get(r volume.Request) volume.Response {
log.Debugf("Entering Get: %v", r)
v.m.Lock()
defer v.m.Unlock()
hostdir := mountpoint(v.root, r.Name)
resolvedName, _ := resolveName(r.Name)

hostdir := mountpoint(v.root, resolvedName)

if v.mountm.HasMount(r.Name) {
log.Debugf("Get: mount found for %s, host directory: %s", r.Name, hostdir)
return volume.Response{Volume: &volume.Volume{Name: r.Name, Mountpoint: hostdir}}
if v.mountm.HasMount(resolvedName) {
log.Debugf("Get: mount found for %s, host directory: %s", resolvedName, hostdir)
return volume.Response{Volume: &volume.Volume{Name: resolvedName, Mountpoint: hostdir}}
}
return volume.Response{}
}
Expand All @@ -78,8 +88,8 @@ func (v volumeDriver) List(r volume.Request) volume.Response {
func (v volumeDriver) Capabilities(r volume.Request) volume.Response {
log.Debugf("Entering Capabilities: %v", r)
return volume.Response{
Capabilities: volume.Capability {
Scope: "global",
Capabilities: volume.Capability{
Scope: "local",
},
}
}
3 changes: 2 additions & 1 deletion netshare/drivers/mounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

const (
ShareOpt = "share"
ShareOpt = "share"
CreateOpt = "create"
)

Expand Down Expand Up @@ -111,6 +111,7 @@ func (m *mountManager) Create(name, hostdir string, opts map[string]string) *mou
}

func (m *mountManager) Delete(name string) error {
log.Debugf("Delete volume: %s, connections: %d", name, m.Count(name))
if m.HasMount(name) {
if m.Count(name) < 1 {
delete(m.mounts, name)
Expand Down
62 changes: 38 additions & 24 deletions netshare/drivers/nfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/docker/go-plugins-helpers/volume"
"os"
"strings"
"path/filepath"
)

Expand Down Expand Up @@ -42,13 +41,22 @@ func (n nfsDriver) Mount(r volume.MountRequest) volume.Response {
n.m.Lock()
defer n.m.Unlock()

resolvedName, resOpts := resolveName(r.Name)

hostdir := mountpoint(n.root, r.Name)
source := n.fixSource(r.Name, r.ID)
hostdir := mountpoint(n.root, resolvedName)
source := n.fixSource(resolvedName)

if n.mountm.HasMount(r.Name) && n.mountm.Count(r.Name) > 0 {
// Support adhoc mounts (outside of docker volume create)
// need to adjust source for ShareOpt
if resOpts != nil {
if share, found := resOpts[ShareOpt]; found {
source = n.fixSource(share)
}
}

if n.mountm.HasMount(resolvedName) && n.mountm.Count(resolvedName) > 0 {
log.Infof("Using existing NFS volume mount: %s", hostdir)
n.mountm.Increment(r.Name)
n.mountm.Increment(resolvedName)
if err := run(fmt.Sprintf("mountpoint -q %s", hostdir)); err != nil {
log.Infof("Existing NFS volume not mounted, force remount.")
} else {
Expand All @@ -62,15 +70,19 @@ func (n nfsDriver) Mount(r volume.MountRequest) volume.Response {
return volume.Response{Err: err.Error()}
}

if err := n.mountVolume(r.Name, source, hostdir, n.version); err != nil {
if n.mountm.HasMount(resolvedName) == false {
n.mountm.Create(resolvedName, hostdir, resOpts)
}

if err := n.mountVolume(resolvedName, source, hostdir, n.version); err != nil {
return volume.Response{Err: err.Error()}
}
n.mountm.Add(r.Name, hostdir)
n.mountm.Add(resolvedName, hostdir)

if n.mountm.GetOption(r.Name, ShareOpt) != "" && n.mountm.GetOptionAsBool(r.Name, CreateOpt) {
log.Infof("Mount: Share and Create options enabled - using %s as sub-dir mount", r.Name)
datavol := filepath.Join(hostdir, r.Name)
if err := createDest(filepath.Join(hostdir, r.Name)); err != nil {
if n.mountm.GetOption(resolvedName, ShareOpt) != "" && n.mountm.GetOptionAsBool(resolvedName, CreateOpt) {
log.Infof("Mount: Share and Create options enabled - using %s as sub-dir mount", resolvedName)
datavol := filepath.Join(hostdir, resolvedName)
if err := createDest(filepath.Join(hostdir, resolvedName)); err != nil {
return volume.Response{Err: err.Error()}
}
hostdir = datavol
Expand All @@ -84,24 +96,28 @@ func (n nfsDriver) Unmount(r volume.UnmountRequest) volume.Response {

n.m.Lock()
defer n.m.Unlock()
hostdir := mountpoint(n.root, r.Name)

if n.mountm.HasMount(r.Name) {
if n.mountm.Count(r.Name) > 1 {
log.Printf("Skipping unmount for %s - in use by other containers", r.Name)
n.mountm.Decrement(r.Name)
resolvedName, _ := resolveName(r.Name)

hostdir := mountpoint(n.root, resolvedName)

if n.mountm.HasMount(resolvedName) {
if n.mountm.Count(resolvedName) > 1 {
log.Printf("Skipping unmount for %s - in use by other containers", resolvedName)
n.mountm.Decrement(resolvedName)
return volume.Response{}
}
n.mountm.Decrement(r.Name)
n.mountm.Decrement(resolvedName)
}

log.Infof("Unmounting volume name %s from %s", r.Name, hostdir)
log.Infof("Unmounting volume name %s from %s", resolvedName, hostdir)

if err := run(fmt.Sprintf("umount %s", hostdir)); err != nil {
log.Errorf("Error unmounting volume from host: %s", err.Error())
return volume.Response{Err: err.Error()}
}

n.mountm.DeleteIfNotManaged(r.Name)
n.mountm.DeleteIfNotManaged(resolvedName)

if err := os.RemoveAll(hostdir); err != nil {
return volume.Response{Err: err.Error()}
Expand All @@ -110,13 +126,11 @@ func (n nfsDriver) Unmount(r volume.UnmountRequest) volume.Response {
return volume.Response{}
}

func (n nfsDriver) fixSource(name, id string) string {
func (n nfsDriver) fixSource(name string) string {
if n.mountm.HasOption(name, ShareOpt) {
return n.mountm.GetOption(name, ShareOpt)
return addShareColon(n.mountm.GetOption(name, ShareOpt))
}
source := strings.Split(name, "/")
source[0] = source[0] + ":"
return strings.Join(source, "/")
return addShareColon(name)
}

func (n nfsDriver) mountVolume(name, source, dest string, version int) error {
Expand Down
31 changes: 31 additions & 0 deletions netshare/drivers/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
)

const (
ShareSplitIndentifer = "#"
)

func createDest(dest string) error {
Expand All @@ -25,6 +30,32 @@ func createDest(dest string) error {
return nil
}

// Used to support on the fly volume creation using docker run. If = is in the name we split
// and elem[1] is the volume name
func resolveName(name string) (string, map[string]string) {
if strings.Contains(name, ShareSplitIndentifer) {
sharevol := strings.Split(name, ShareSplitIndentifer)
opts := map[string]string{}
opts[ShareOpt] = sharevol[0]
opts[CreateOpt] = "true"
return sharevol[1], opts
}
return name, nil
}

func shareDefinedWithVolume(name string) bool {
return strings.Contains(name, ShareSplitIndentifer)
}

func addShareColon(share string) string {
if strings.Contains(share, ":") {
return share
}
source := strings.Split(share, "/")
source[0] = source[0] + ":"
return strings.Join(source, "/")
}

func mountpoint(elem ...string) string {
return filepath.Join(elem...)
}
Expand Down

0 comments on commit a72b5d1

Please sign in to comment.