From 02e50b393459f68f4a2240414fac24e3191b3ce0 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 6 Sep 2017 16:34:55 -0700 Subject: [PATCH 1/2] refactor code Signed-off-by: Victor Vieux --- .travis.yml | 4 + .travis/integration.sh | 99 +++++++--------- Makefile | 3 +- driver.go | 181 +++++++++++++++++++++++++++++ main.go | 257 ----------------------------------------- state.go | 41 +++++++ utils.go | 38 ++++++ volume.go | 51 ++++++++ 8 files changed, 361 insertions(+), 313 deletions(-) create mode 100644 driver.go create mode 100644 state.go create mode 100644 utils.go create mode 100644 volume.go diff --git a/.travis.yml b/.travis.yml index 55f6367..775e35b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,10 @@ - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" - sudo apt-get update - sudo apt-get -y install docker-ce + install: + - docker pull rastasheep/ubuntu-sshd + - docker pull busybox + - docker build -t sshd .travis/ssh env: TESTFILE=integration.sh script: diff --git a/.travis/integration.sh b/.travis/integration.sh index ef43642..3f13900 100755 --- a/.travis/integration.sh +++ b/.travis/integration.sh @@ -5,65 +5,54 @@ set -x TAG=test -# before_install -#curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - -#sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" -#sudo apt-get update -#sudo apt-get -y install docker-ce - -# install -sudo docker pull rastasheep/ubuntu-sshd -sudo docker pull busybox - docker build -t sshd .travis/ssh -#script # make the plugin -sudo PLUGIN_TAG=$TAG make +PLUGIN_TAG=$TAG make # enable the plugin -sudo docker plugin enable vieux/sshfs:$TAG +PLUGIN_TAG=$TAG make enable # list plugins -sudo docker plugin ls +docker plugin ls # start sshd -sudo docker run -d -p 2222:22 sshd - -# test1: simple -sudo docker volume create -d vieux/sshfs:$TAG -o sshcmd=root@localhost:/ -o port=2222 -o password=root sshvolume -sudo docker run --rm -v sshvolume:/write busybox sh -c "echo hello > /write/world" -sudo docker run --rm -v sshvolume:/read busybox grep -Fxq hello /read/world -#sudo cat /var/lib/docker/plugins/sshfs-state.json -sudo docker volume rm sshvolume - -# test2: allow_other -sudo docker volume create -d vieux/sshfs:$TAG -o sshcmd=root@localhost:/ -o allow_other -o port=2222 -o password=root sshvolume -sudo docker run --rm -v sshvolume:/write -u nobody busybox sh -c "echo hello > /write/world" +docker run -d -p 2222:22 sshd + +echo "# test1: simple" +docker volume create -d vieux/sshfs:$TAG -o sshcmd=root@localhost:/ -o port=2222 -o password=root sshvolume +docker run --rm -v sshvolume:/write busybox sh -c "echo hello > /write/world" +docker run --rm -v sshvolume:/read busybox grep -Fxq hello /read/world +#cat /var/lib/docker/plugins/sshfs-state.json +docker volume rm sshvolume + +echo "# test2: allow_other" +docker volume create -d vieux/sshfs:$TAG -o sshcmd=root@localhost:/ -o allow_other -o port=2222 -o password=root sshvolume +docker run --rm -v sshvolume:/write -u nobody busybox sh -c "echo hello > /write/world" docker run --rm -v sshvolume:/read -u nobody busybox grep -Fxq hello /read/world -#sudo cat /var/lib/docker/plugins/sshfs-state.json -sudo docker volume rm sshvolume - -# test3: compression -sudo docker volume create -d vieux/sshfs:$TAG -o sshcmd=root@localhost:/ -o Ciphers=arcfour -o Compression=no -o port=2222 -o password=root sshvolume -sudo docker run --rm -v sshvolume:/write busybox sh -c "echo hello > /write/world" -sudo docker run --rm -v sshvolume:/read busybox grep -Fxq hello /read/world -#sudo cat /var/lib/docker/plugins/sshfs-state.json -sudo docker volume rm sshvolume - -# test4: source -sudo docker plugin disable vieux/sshfs:$TAG -sudo docker plugin set vieux/sshfs:$TAG state.source=/tmp -sudo docker plugin enable vieux/sshfs:$TAG -sudo docker volume create -d vieux/sshfs:$TAG -o sshcmd=root@localhost:/ -o Ciphers=arcfour -o Compression=no -o port=2222 -o password=root sshvolume -sudo docker run --rm -v sshvolume:/write busybox sh -c "echo hello > /write/world" -sudo docker run --rm -v sshvolume:/read busybox grep -Fxq hello /read/world -#sudo cat /tmp/sshfs-state.json -sudo docker volume rm sshvolume - -# test5: ssh key -sudo docker plugin disable vieux/sshfs:$TAG -sudo docker plugin set vieux/sshfs:$TAG sshkey.source=`pwd`/.travis/ssh/ -sudo docker plugin enable vieux/sshfs:$TAG -sudo docker volume create -d vieux/sshfs:$TAG -o sshcmd=root@localhost:/ -o port=2222 sshvolume -sudo docker run --rm -v sshvolume:/write busybox sh -c "echo hello > /write/world" -sudo docker run --rm -v sshvolume:/read busybox grep -Fxq hello /read/world -#sudo cat /var/lib/docker/plugins/sshfs-state.json -sudo docker volume rm sshvolume +#cat /var/lib/docker/plugins/sshfs-state.json +docker volume rm sshvolume + +echo "# test3: compression" +docker volume create -d vieux/sshfs:$TAG -o sshcmd=root@localhost:/ -o Ciphers=arcfour -o Compression=no -o port=2222 -o password=root sshvolume +docker run --rm -v sshvolume:/write busybox sh -c "echo hello > /write/world" +docker run --rm -v sshvolume:/read busybox grep -Fxq hello /read/world +#cat /var/lib/docker/plugins/sshfs-state.json +docker volume rm sshvolume + +echo "# test4: source" +docker plugin disable vieux/sshfs:$TAG +docker plugin set vieux/sshfs:$TAG state.source=/tmp +docker plugin enable vieux/sshfs:$TAG +docker volume create -d vieux/sshfs:$TAG -o sshcmd=root@localhost:/ -o Ciphers=arcfour -o Compression=no -o port=2222 -o password=root sshvolume +docker run --rm -v sshvolume:/write busybox sh -c "echo hello > /write/world" +docker run --rm -v sshvolume:/read busybox grep -Fxq hello /read/world +#cat /tmp/sshfs-state.json +docker volume rm sshvolume + +echo "# test5: ssh key" +docker plugin disable vieux/sshfs:$TAG +docker plugin set vieux/sshfs:$TAG sshkey.source=`pwd`/.travis/ssh/ +docker plugin enable vieux/sshfs:$TAG +docker volume create -d vieux/sshfs:$TAG -o sshcmd=root@localhost:/ -o port=2222 sshvolume +docker run --rm -v sshvolume:/write busybox sh -c "echo hello > /write/world" +docker run --rm -v sshvolume:/read busybox grep -Fxq hello /read/world +#cat /var/lib/docker/plugins/sshfs-state.json +docker volume rm sshvolume diff --git a/Makefile b/Makefile index 393ab21..24c4428 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,8 @@ create: @docker plugin create ${PLUGIN_NAME}:${PLUGIN_TAG} ./plugin enable: - @echo "### enable plugin ${PLUGIN_NAME}:${PLUGIN_TAG}" + @echo "### enable plugin ${PLUGIN_NAME}:${PLUGIN_TAG}" + @docker plugin set ${PLUGIN_NAME}:${PLUGIN_TAG} DEBUG=1 @docker plugin enable ${PLUGIN_NAME}:${PLUGIN_TAG} push: clean rootfs create enable diff --git a/driver.go b/driver.go new file mode 100644 index 0000000..87aae62 --- /dev/null +++ b/driver.go @@ -0,0 +1,181 @@ +package main + +import ( + "os" + "path/filepath" + "sync" + + "github.com/Sirupsen/logrus" + "github.com/docker/go-plugins-helpers/volume" +) + +const socketAddress = "/run/docker/plugins/sshfs.sock" + +type sshfsDriver struct { + sync.RWMutex + + root string + statePath string + volumes map[string]*sshfsVolume +} + +func newSshfsDriver(root string) (*sshfsDriver, error) { + logrus.WithField("method", "new driver").Debug(root) + + statePath := filepath.Join(root, "state", "sshfs-state.json") + volumes, err := readState(statePath) + if err != nil { + return nil, err + } + + return &sshfsDriver{ + root: filepath.Join(root, "volumes"), + statePath: statePath, + volumes: volumes, + }, nil +} + +func (d *sshfsDriver) Create(r *volume.CreateRequest) error { + logrus.WithField("method", "create").Debugf("%#v", r) + + v, err := newSshfsVolume(d.root, r.Options) + if err != nil { + return err + } + + d.Lock() + defer d.Unlock() + + d.volumes[r.Name] = v + + return saveState(d.statePath, d.volumes) +} + +func (d *sshfsDriver) Remove(r *volume.RemoveRequest) error { + logrus.WithField("method", "remove").Debugf("%#v", r) + + d.Lock() + defer d.Unlock() + + v, ok := d.volumes[r.Name] + if !ok { + return logError("volume %s not found", r.Name) + } + + if v.connections != 0 { + return logError("volume %s is currently used by a container", r.Name) + } + + if err := os.RemoveAll(v.Mountpoint); err != nil { + return logError(err.Error()) + } + + delete(d.volumes, r.Name) + + return saveState(d.statePath, d.volumes) +} + +func (d *sshfsDriver) Path(r *volume.PathRequest) (*volume.PathResponse, error) { + logrus.WithField("method", "path").Debugf("%#v", r) + + d.RLock() + defer d.RUnlock() + + v, ok := d.volumes[r.Name] + if !ok { + return &volume.PathResponse{}, logError("volume %s not found", r.Name) + } + + return &volume.PathResponse{Mountpoint: v.Mountpoint}, nil +} + +func (d *sshfsDriver) Mount(r *volume.MountRequest) (*volume.MountResponse, error) { + logrus.WithField("method", "mount").Debugf("%#v", r) + + d.Lock() + defer d.Unlock() + + v, ok := d.volumes[r.Name] + if !ok { + return &volume.MountResponse{}, logError("volume %s not found", r.Name) + } + + if v.connections == 0 { + fi, err := os.Lstat(v.Mountpoint) + if os.IsNotExist(err) { + if err := os.MkdirAll(v.Mountpoint, 0755); err != nil { + return &volume.MountResponse{}, logError(err.Error()) + } + } else if err != nil { + return &volume.MountResponse{}, logError(err.Error()) + } + + if fi != nil && !fi.IsDir() { + return &volume.MountResponse{}, logError("%v already exist and it's not a directory", v.Mountpoint) + } + + if err := mountVolume(v); err != nil { + return &volume.MountResponse{}, logError(err.Error()) + } + } + + v.connections++ + + return &volume.MountResponse{Mountpoint: v.Mountpoint}, nil +} + +func (d *sshfsDriver) Unmount(r *volume.UnmountRequest) error { + logrus.WithField("method", "unmount").Debugf("%#v", r) + + d.Lock() + defer d.Unlock() + + v, ok := d.volumes[r.Name] + if !ok { + return logError("volume %s not found", r.Name) + } + + v.connections-- + + if v.connections <= 0 { + if err := unmountVolume(v.Mountpoint); err != nil { + return logError(err.Error()) + } + v.connections = 0 + } + + return nil +} + +func (d *sshfsDriver) Get(r *volume.GetRequest) (*volume.GetResponse, error) { + logrus.WithField("method", "get").Debugf("%#v", r) + + d.RLock() + defer d.RUnlock() + + v, ok := d.volumes[r.Name] + if !ok { + return &volume.GetResponse{}, logError("volume %s not found", r.Name) + } + + return &volume.GetResponse{Volume: &volume.Volume{Name: r.Name, Mountpoint: v.Mountpoint}}, nil +} + +func (d *sshfsDriver) List() (*volume.ListResponse, error) { + logrus.WithField("method", "list").Debug("") + + d.RLock() + defer d.RUnlock() + + var vols []*volume.Volume + for name, v := range d.volumes { + vols = append(vols, &volume.Volume{Name: name, Mountpoint: v.Mountpoint}) + } + return &volume.ListResponse{Volumes: vols}, nil +} + +func (d *sshfsDriver) Capabilities() *volume.CapabilitiesResponse { + logrus.WithField("method", "capabilities").Debug("") + + return &volume.CapabilitiesResponse{Capabilities: volume.Capability{Scope: "local"}} +} diff --git a/main.go b/main.go index 953c94f..aa6162f 100644 --- a/main.go +++ b/main.go @@ -1,271 +1,14 @@ package main import ( - "crypto/md5" - "encoding/json" - "fmt" - "io/ioutil" "log" "os" - "os/exec" - "path/filepath" "strconv" - "strings" - "sync" "github.com/Sirupsen/logrus" "github.com/docker/go-plugins-helpers/volume" ) -const socketAddress = "/run/docker/plugins/sshfs.sock" - -type sshfsVolume struct { - Password string - Sshcmd string - Port string - - Options []string - - Mountpoint string - connections int -} - -type sshfsDriver struct { - sync.RWMutex - - root string - statePath string - volumes map[string]*sshfsVolume -} - -func newSshfsDriver(root string) (*sshfsDriver, error) { - logrus.WithField("method", "new driver").Debug(root) - - d := &sshfsDriver{ - root: filepath.Join(root, "volumes"), - statePath: filepath.Join(root, "state", "sshfs-state.json"), - volumes: map[string]*sshfsVolume{}, - } - - data, err := ioutil.ReadFile(d.statePath) - if err != nil { - if os.IsNotExist(err) { - logrus.WithField("statePath", d.statePath).Debug("no state found") - } else { - return nil, err - } - } else { - if err := json.Unmarshal(data, &d.volumes); err != nil { - return nil, err - } - } - - return d, nil -} - -func (d *sshfsDriver) saveState() { - data, err := json.Marshal(d.volumes) - if err != nil { - logrus.WithField("statePath", d.statePath).Error(err) - return - } - - if err := ioutil.WriteFile(d.statePath, data, 0644); err != nil { - logrus.WithField("savestate", d.statePath).Error(err) - } -} - -func (d *sshfsDriver) Create(r *volume.CreateRequest) error { - logrus.WithField("method", "create").Debugf("%#v", r) - - d.Lock() - defer d.Unlock() - v := &sshfsVolume{} - - for key, val := range r.Options { - switch key { - case "sshcmd": - v.Sshcmd = val - case "password": - v.Password = val - case "port": - v.Port = val - default: - if val != "" { - v.Options = append(v.Options, key+"="+val) - } else { - v.Options = append(v.Options, key) - } - } - } - - if v.Sshcmd == "" { - return logError("'sshcmd' option required") - } - v.Mountpoint = filepath.Join(d.root, fmt.Sprintf("%x", md5.Sum([]byte(v.Sshcmd)))) - - d.volumes[r.Name] = v - - d.saveState() - - return nil -} - -func (d *sshfsDriver) Remove(r *volume.RemoveRequest) error { - logrus.WithField("method", "remove").Debugf("%#v", r) - - d.Lock() - defer d.Unlock() - - v, ok := d.volumes[r.Name] - if !ok { - return logError("volume %s not found", r.Name) - } - - if v.connections != 0 { - return logError("volume %s is currently used by a container", r.Name) - } - if err := os.RemoveAll(v.Mountpoint); err != nil { - return logError(err.Error()) - } - delete(d.volumes, r.Name) - d.saveState() - return nil -} - -func (d *sshfsDriver) Path(r *volume.PathRequest) (*volume.PathResponse, error) { - logrus.WithField("method", "path").Debugf("%#v", r) - - d.RLock() - defer d.RUnlock() - - v, ok := d.volumes[r.Name] - if !ok { - return &volume.PathResponse{}, logError("volume %s not found", r.Name) - } - - return &volume.PathResponse{Mountpoint: v.Mountpoint}, nil -} - -func (d *sshfsDriver) Mount(r *volume.MountRequest) (*volume.MountResponse, error) { - logrus.WithField("method", "mount").Debugf("%#v", r) - - d.Lock() - defer d.Unlock() - - v, ok := d.volumes[r.Name] - if !ok { - return &volume.MountResponse{}, logError("volume %s not found", r.Name) - } - - if v.connections == 0 { - fi, err := os.Lstat(v.Mountpoint) - if os.IsNotExist(err) { - if err := os.MkdirAll(v.Mountpoint, 0755); err != nil { - return &volume.MountResponse{}, logError(err.Error()) - } - } else if err != nil { - return &volume.MountResponse{}, logError(err.Error()) - } - - if fi != nil && !fi.IsDir() { - return &volume.MountResponse{}, logError("%v already exist and it's not a directory", v.Mountpoint) - } - - if err := d.mountVolume(v); err != nil { - return &volume.MountResponse{}, logError(err.Error()) - } - } - - v.connections++ - - return &volume.MountResponse{Mountpoint: v.Mountpoint}, nil -} - -func (d *sshfsDriver) Unmount(r *volume.UnmountRequest) error { - logrus.WithField("method", "unmount").Debugf("%#v", r) - - d.Lock() - defer d.Unlock() - v, ok := d.volumes[r.Name] - if !ok { - return logError("volume %s not found", r.Name) - } - - v.connections-- - - if v.connections <= 0 { - if err := d.unmountVolume(v.Mountpoint); err != nil { - return logError(err.Error()) - } - v.connections = 0 - } - - return nil -} - -func (d *sshfsDriver) Get(r *volume.GetRequest) (*volume.GetResponse, error) { - logrus.WithField("method", "get").Debugf("%#v", r) - - d.Lock() - defer d.Unlock() - - v, ok := d.volumes[r.Name] - if !ok { - return &volume.GetResponse{}, logError("volume %s not found", r.Name) - } - - return &volume.GetResponse{Volume: &volume.Volume{Name: r.Name, Mountpoint: v.Mountpoint}}, nil -} - -func (d *sshfsDriver) List() (*volume.ListResponse, error) { - logrus.WithField("method", "list").Debugf("") - - d.Lock() - defer d.Unlock() - - var vols []*volume.Volume - for name, v := range d.volumes { - vols = append(vols, &volume.Volume{Name: name, Mountpoint: v.Mountpoint}) - } - return &volume.ListResponse{Volumes: vols}, nil -} - -func (d *sshfsDriver) Capabilities() *volume.CapabilitiesResponse { - logrus.WithField("method", "capabilities").Debugf("") - - return &volume.CapabilitiesResponse{Capabilities: volume.Capability{Scope: "local"}} -} - -func (d *sshfsDriver) mountVolume(v *sshfsVolume) error { - cmd := exec.Command("sshfs", "-oStrictHostKeyChecking=no", v.Sshcmd, v.Mountpoint) - if v.Port != "" { - cmd.Args = append(cmd.Args, "-p", v.Port) - } - if v.Password != "" { - cmd.Args = append(cmd.Args, "-o", "workaround=rename", "-o", "password_stdin") - cmd.Stdin = strings.NewReader(v.Password) - } - - for _, option := range v.Options { - cmd.Args = append(cmd.Args, "-o", option) - } - - logrus.Debug(cmd.Args) - return cmd.Run() -} - -func (d *sshfsDriver) unmountVolume(target string) error { - cmd := fmt.Sprintf("umount %s", target) - logrus.Debug(cmd) - return exec.Command("sh", "-c", cmd).Run() -} - -func logError(format string, args ...interface{}) error { - logrus.Errorf(format, args...) - return fmt.Errorf(format, args) -} - func main() { debug := os.Getenv("DEBUG") if ok, _ := strconv.ParseBool(debug); ok { diff --git a/state.go b/state.go new file mode 100644 index 0000000..98dcbf2 --- /dev/null +++ b/state.go @@ -0,0 +1,41 @@ +package main + +import ( + "encoding/json" + "io/ioutil" + "os" + + "github.com/Sirupsen/logrus" +) + +func saveState(statePath string, volumes map[string]*sshfsVolume) error { + data, err := json.Marshal(volumes) + if err != nil { + logrus.WithField("statePath", statePath).Error(err) + return err + } + + if err := ioutil.WriteFile(statePath, data, 0644); err != nil { + logrus.WithField("savestate", statePath).Error(err) + return err + } + return nil +} + +func readState(statePath string) (map[string]*sshfsVolume, error) { + volumes := make(map[string]*sshfsVolume) + + data, err := ioutil.ReadFile(statePath) + if err != nil { + if os.IsNotExist(err) { + logrus.WithField("statePath", statePath).Debug("no state found") + } else { + return nil, err + } + } else { + if err := json.Unmarshal(data, &volumes); err != nil { + return nil, err + } + } + return volumes, nil +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..6d12c4e --- /dev/null +++ b/utils.go @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + "os/exec" + "strings" + + "github.com/Sirupsen/logrus" +) + +func mountVolume(v *sshfsVolume) error { + cmd := exec.Command("sshfs", "-oStrictHostKeyChecking=no", v.Sshcmd, v.Mountpoint) + if v.Port != "" { + cmd.Args = append(cmd.Args, "-p", v.Port) + } + if v.Password != "" { + cmd.Args = append(cmd.Args, "-o", "workaround=rename", "-o", "password_stdin") + cmd.Stdin = strings.NewReader(v.Password) + } + + for _, option := range v.Options { + cmd.Args = append(cmd.Args, "-o", option) + } + + logrus.Debug(cmd.Args) + return cmd.Run() +} + +func unmountVolume(target string) error { + cmd := fmt.Sprintf("umount %s", target) + logrus.Debug(cmd) + return exec.Command("sh", "-c", cmd).Run() +} + +func logError(format string, args ...interface{}) error { + logrus.Errorf(format, args...) + return fmt.Errorf(format, args) +} diff --git a/volume.go b/volume.go new file mode 100644 index 0000000..08ea7f0 --- /dev/null +++ b/volume.go @@ -0,0 +1,51 @@ +package main + +import ( + "crypto/md5" + "fmt" + "path/filepath" + + "github.com/Sirupsen/logrus" +) + +type sshfsVolume struct { + Password string + Sshcmd string + Port string + + Options []string + + Mountpoint string + connections int +} + +func newSshfsVolume(root string, options map[string]string) (*sshfsVolume, error) { + logrus.WithField("method", "new volume").Debugf("%s, %#v", root, options) + + var v sshfsVolume + + for key, val := range options { + switch key { + case "sshcmd": + v.Sshcmd = val + case "password": + v.Password = val + case "port": + v.Port = val + default: + if val != "" { + v.Options = append(v.Options, key+"="+val) + } else { + v.Options = append(v.Options, key) + } + } + } + + if v.Sshcmd == "" { + return nil, logError("'sshcmd' option required") + } + + v.Mountpoint = filepath.Join(root, fmt.Sprintf("%x", md5.Sum([]byte(v.Sshcmd)))) + + return &v, nil +} From 06570efa987b1832a9c815b3327308b465cc1b1c Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 7 Sep 2017 10:21:00 -0700 Subject: [PATCH 2/2] add restart test Signed-off-by: Victor Vieux --- .travis/integration.sh | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.travis/integration.sh b/.travis/integration.sh index 3f13900..75cc317 100755 --- a/.travis/integration.sh +++ b/.travis/integration.sh @@ -14,7 +14,7 @@ PLUGIN_TAG=$TAG make enable # list plugins docker plugin ls # start sshd -docker run -d -p 2222:22 sshd +docker run -d --name sshd -p 2222:22 sshd echo "# test1: simple" docker volume create -d vieux/sshfs:$TAG -o sshcmd=root@localhost:/ -o port=2222 -o password=root sshvolume @@ -37,7 +37,14 @@ docker run --rm -v sshvolume:/read busybox grep -Fxq hello /read/world #cat /var/lib/docker/plugins/sshfs-state.json docker volume rm sshvolume -echo "# test4: source" +echo "# test4: restart" +docker volume create -d vieux/sshfs:$TAG -o sshcmd=root@localhost:/ -o port=2222 -o password=root sshvolume +docker run --rm -v sshvolume:/write busybox sh -c "echo hello > /write/world" +docker restart sshd +docker run --rm -v sshvolume:/read busybox grep -Fxq hello /read/world +docker volume rm sshvolume + +echo "# test5: source" docker plugin disable vieux/sshfs:$TAG docker plugin set vieux/sshfs:$TAG state.source=/tmp docker plugin enable vieux/sshfs:$TAG @@ -47,7 +54,7 @@ docker run --rm -v sshvolume:/read busybox grep -Fxq hello /read/world #cat /tmp/sshfs-state.json docker volume rm sshvolume -echo "# test5: ssh key" +echo "# test6: ssh key" docker plugin disable vieux/sshfs:$TAG docker plugin set vieux/sshfs:$TAG sshkey.source=`pwd`/.travis/ssh/ docker plugin enable vieux/sshfs:$TAG @@ -56,3 +63,5 @@ docker run --rm -v sshvolume:/write busybox sh -c "echo hello > /write/world" docker run --rm -v sshvolume:/read busybox grep -Fxq hello /read/world #cat /var/lib/docker/plugins/sshfs-state.json docker volume rm sshvolume + +