diff --git a/Makefile b/Makefile index baacdf3..bbcbbbd 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ -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 diff --git a/netshare/drivers/driver.go b/netshare/drivers/driver.go index 477ef78..90ce196 100644 --- a/netshare/drivers/driver.go +++ b/netshare/drivers/driver.go @@ -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{} } @@ -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", }, } } diff --git a/netshare/drivers/mounts.go b/netshare/drivers/mounts.go index 54f223a..546ef93 100644 --- a/netshare/drivers/mounts.go +++ b/netshare/drivers/mounts.go @@ -8,7 +8,7 @@ import ( ) const ( - ShareOpt = "share" + ShareOpt = "share" CreateOpt = "create" ) @@ -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) diff --git a/netshare/drivers/nfs.go b/netshare/drivers/nfs.go index ee61438..f896a8d 100644 --- a/netshare/drivers/nfs.go +++ b/netshare/drivers/nfs.go @@ -5,7 +5,6 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/go-plugins-helpers/volume" "os" - "strings" "path/filepath" ) @@ -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 { @@ -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 @@ -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()} @@ -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 { diff --git a/netshare/drivers/util.go b/netshare/drivers/util.go index 6e28a2a..281c766 100644 --- a/netshare/drivers/util.go +++ b/netshare/drivers/util.go @@ -6,6 +6,11 @@ import ( "os" "os/exec" "path/filepath" + "strings" +) + +const ( + ShareSplitIndentifer = "#" ) func createDest(dest string) error { @@ -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...) }