-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a1592b3
commit 3e9150c
Showing
16 changed files
with
488 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package sftp | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/balazsgrill/potatodrive/bindings/utils" | ||
sftpclient "github.com/pkg/sftp" | ||
"github.com/spf13/afero" | ||
"github.com/spf13/afero/sftpfs" | ||
"golang.org/x/crypto/ssh" | ||
) | ||
|
||
type Config struct { | ||
User string `flag:"user,User name" reg:"User"` | ||
Password string `flag:"password,Password" reg:"Password"` | ||
Host string `flag:"host,Host:port" reg:"Host"` | ||
Basepath string `flag:"basepath,Base path on remote server" reg:"Basepath"` | ||
} | ||
|
||
func (c *Config) Validate() error { | ||
if c.Host == "" { | ||
return errors.New("host is mandatory") | ||
} | ||
if c.User == "" { | ||
return errors.New("user is mandatory") | ||
} | ||
if c.Password == "" { | ||
return errors.New("password is mandatory") | ||
} | ||
return nil | ||
} | ||
|
||
func (c *Config) Connect(onDisconnect func(error)) (afero.Fs, error) { | ||
config := ssh.ClientConfig{ | ||
User: c.User, | ||
Auth: []ssh.AuthMethod{ | ||
ssh.Password(c.Password), | ||
}, | ||
HostKeyCallback: ssh.InsecureIgnoreHostKey(), | ||
} | ||
conn, err := ssh.Dial("tcp", c.Host, &config) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
client, err := sftpclient.NewClient(conn) | ||
if err != nil { | ||
conn.Close() | ||
return nil, err | ||
} | ||
|
||
go func() { | ||
err := conn.Wait() | ||
client.Close() | ||
onDisconnect(err) | ||
}() | ||
|
||
return sftpfs.New(client), nil | ||
} | ||
|
||
func (c *Config) ToFileSystem() (afero.Fs, error) { | ||
var remote afero.Fs | ||
remote = &utils.ConnectingFs{ | ||
Connect: c.Connect, | ||
} | ||
if c.Basepath != "" { | ||
remote = utils.NewBasePathFs(remote, c.Basepath) | ||
} | ||
return remote, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package utils | ||
|
||
import ( | ||
"os" | ||
"sync" | ||
"time" | ||
|
||
"github.com/spf13/afero" | ||
) | ||
|
||
type ConnectingFs struct { | ||
Connect func(onDisconnect func(error)) (afero.Fs, error) | ||
|
||
lock sync.Mutex | ||
currentFs afero.Fs | ||
} | ||
|
||
var _ afero.Fs = (*ConnectingFs)(nil) | ||
|
||
func (cfs *ConnectingFs) Chmod(name string, mode os.FileMode) error { | ||
return cfs.withFs(func(fs afero.Fs) error { | ||
return fs.Chmod(name, mode) | ||
}) | ||
} | ||
|
||
func (cfs *ConnectingFs) MkdirAll(path string, perm os.FileMode) error { | ||
return cfs.withFs(func(fs afero.Fs) error { | ||
return fs.MkdirAll(path, perm) | ||
}) | ||
} | ||
|
||
func (cfs *ConnectingFs) Stat(name string) (os.FileInfo, error) { | ||
var fileInfo os.FileInfo | ||
err := cfs.withFs(func(fs afero.Fs) error { | ||
var err error | ||
fileInfo, err = fs.Stat(name) | ||
return err | ||
}) | ||
return fileInfo, err | ||
} | ||
func (cfs *ConnectingFs) Rename(oldname, newname string) error { | ||
return cfs.withFs(func(fs afero.Fs) error { | ||
return fs.Rename(oldname, newname) | ||
}) | ||
} | ||
func (cfs *ConnectingFs) RemoveAll(path string) error { | ||
return cfs.withFs(func(fs afero.Fs) error { | ||
return fs.RemoveAll(path) | ||
}) | ||
} | ||
func (cfs *ConnectingFs) Remove(name string) error { | ||
return cfs.withFs(func(fs afero.Fs) error { | ||
return fs.Remove(name) | ||
}) | ||
} | ||
func (cfs *ConnectingFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { | ||
var file afero.File | ||
err := cfs.withFs(func(fs afero.Fs) error { | ||
var err error | ||
file, err = fs.OpenFile(name, flag, perm) | ||
return err | ||
}) | ||
return file, err | ||
} | ||
func (cfs *ConnectingFs) Open(name string) (afero.File, error) { | ||
var file afero.File | ||
err := cfs.withFs(func(fs afero.Fs) error { | ||
var err error | ||
file, err = fs.Open(name) | ||
return err | ||
}) | ||
return file, err | ||
} | ||
func (cfs *ConnectingFs) Name() string { | ||
return "ConnectingFs" | ||
} | ||
func (cfs *ConnectingFs) Mkdir(name string, perm os.FileMode) error { | ||
return cfs.withFs(func(fs afero.Fs) error { | ||
return fs.Mkdir(name, perm) | ||
}) | ||
} | ||
func (cfs *ConnectingFs) Create(name string) (afero.File, error) { | ||
var file afero.File | ||
err := cfs.withFs(func(fs afero.Fs) error { | ||
var err error | ||
file, err = fs.Create(name) | ||
return err | ||
}) | ||
return file, err | ||
} | ||
func (cfs *ConnectingFs) Chtimes(name string, atime, mtime time.Time) error { | ||
return cfs.withFs(func(fs afero.Fs) error { | ||
return fs.Chtimes(name, atime, mtime) | ||
}) | ||
} | ||
func (cfs *ConnectingFs) Chown(name string, uid, gid int) error { | ||
return cfs.withFs(func(fs afero.Fs) error { | ||
return fs.Chown(name, uid, gid) | ||
}) | ||
} | ||
func (cfs *ConnectingFs) withFs(f func(fs afero.Fs) error) error { | ||
cfs.lock.Lock() | ||
defer cfs.lock.Unlock() | ||
if cfs.currentFs == nil { | ||
fs, err := cfs.Connect(func(error) { | ||
cfs.lock.Lock() | ||
defer cfs.lock.Unlock() | ||
cfs.currentFs = nil | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
cfs.currentFs = fs | ||
} | ||
return f(cfs.currentFs) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// Forked from afero, changed to join path segments by path.Join instead of filepath.Join | ||
|
||
package utils | ||
|
||
import ( | ||
"os" | ||
fpath "path" | ||
"path/filepath" | ||
"sort" | ||
|
||
"github.com/spf13/afero" | ||
) | ||
|
||
// readDirNames reads the directory named by dirname and returns | ||
// a sorted list of directory entries. | ||
// adapted from https://golang.org/src/path/filepath/path.go | ||
func readDirNames(fs afero.Fs, dirname string) ([]string, error) { | ||
f, err := fs.Open(dirname) | ||
if err != nil { | ||
return nil, err | ||
} | ||
names, err := f.Readdirnames(-1) | ||
f.Close() | ||
if err != nil { | ||
return nil, err | ||
} | ||
sort.Strings(names) | ||
return names, nil | ||
} | ||
|
||
// walk recursively descends path, calling walkFn | ||
// adapted from https://golang.org/src/path/filepath/path.go | ||
func walk(fs afero.Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error { | ||
err := walkFn(path, info, nil) | ||
if err != nil { | ||
if info.IsDir() && err == filepath.SkipDir { | ||
return nil | ||
} | ||
return err | ||
} | ||
|
||
if !info.IsDir() { | ||
return nil | ||
} | ||
|
||
names, err := readDirNames(fs, path) | ||
if err != nil { | ||
return walkFn(path, info, err) | ||
} | ||
|
||
for _, name := range names { | ||
filename := fpath.Join(path, name) | ||
fileInfo, err := lstatIfPossible(fs, filename) | ||
if err != nil { | ||
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { | ||
return err | ||
} | ||
} else { | ||
err = walk(fs, filename, fileInfo, walkFn) | ||
if err != nil { | ||
if !fileInfo.IsDir() || err != filepath.SkipDir { | ||
return err | ||
} | ||
} | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// if the filesystem supports it, use Lstat, else use fs.Stat | ||
func lstatIfPossible(fs afero.Fs, path string) (os.FileInfo, error) { | ||
if lfs, ok := fs.(afero.Lstater); ok { | ||
fi, _, err := lfs.LstatIfPossible(path) | ||
return fi, err | ||
} | ||
return fs.Stat(path) | ||
} | ||
|
||
// Walk walks the file tree rooted at root, calling walkFn for each file or | ||
// directory in the tree, including root. All errors that arise visiting files | ||
// and directories are filtered by walkFn. The files are walked in lexical | ||
// order, which makes the output deterministic but means that for very | ||
// large directories Walk can be inefficient. | ||
// Walk does not follow symbolic links. | ||
|
||
func Walk(fs afero.Fs, root string, walkFn filepath.WalkFunc) error { | ||
info, err := lstatIfPossible(fs, root) | ||
if err != nil { | ||
return walkFn(root, nil, err) | ||
} | ||
return walk(fs, root, info, walkFn) | ||
} |
Oops, something went wrong.