ficsit-cli-flake/cli/disk/sftp.go

170 lines
3.7 KiB
Go
Raw Permalink Normal View History

2022-06-22 22:24:35 +00:00
package disk
import (
"bytes"
"errors"
"fmt"
2022-06-22 22:24:35 +00:00
"io"
"log/slog"
"net/url"
"os"
"github.com/pkg/sftp"
"golang.org/x/crypto/ssh"
2022-06-22 22:24:35 +00:00
)
var _ Disk = (*sftpDisk)(nil)
type sftpDisk struct {
client *sftp.Client
path string
2022-06-22 22:24:35 +00:00
}
type sftpEntry struct {
os.FileInfo
2022-06-22 22:24:35 +00:00
}
func (f sftpEntry) IsDir() bool {
return f.FileInfo.IsDir()
2022-06-22 22:24:35 +00:00
}
func (f sftpEntry) Name() string {
return f.FileInfo.Name()
2022-06-22 22:24:35 +00:00
}
func newSFTP(path string) (Disk, error) {
u, err := url.Parse(path)
if err != nil {
return nil, fmt.Errorf("failed to parse sftp url: %w", err)
}
password, ok := u.User.Password()
var auth []ssh.AuthMethod
if ok {
auth = append(auth, ssh.Password(password))
}
conn, err := ssh.Dial("tcp", u.Host, &ssh.ClientConfig{
User: u.User.Username(),
Auth: auth,
// TODO Somehow use systems hosts file
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
})
if err != nil {
return nil, fmt.Errorf("failed to connect to ssh server: %w", err)
}
client, err := sftp.NewClient(conn)
if err != nil {
return nil, fmt.Errorf("failed to create sftp client: %w", err)
}
slog.Info("logged into sftp")
return sftpDisk{
path: path,
client: client,
}, nil
2022-06-22 22:24:35 +00:00
}
func (l sftpDisk) Exists(path string) (bool, error) {
slog.Debug("checking if file exists", slog.String("path", clean(path)), slog.String("schema", "sftp"))
s, err := l.client.Stat(clean(path))
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
return false, fmt.Errorf("failed to check if file exists: %w", err)
}
return s != nil, nil
}
func (l sftpDisk) Read(path string) ([]byte, error) {
slog.Debug("reading file", slog.String("path", clean(path)), slog.String("schema", "sftp"))
f, err := l.client.Open(clean(path))
if err != nil {
return nil, fmt.Errorf("failed to retrieve path: %w", err)
}
defer f.Close()
data, err := io.ReadAll(f)
if err != nil {
return nil, fmt.Errorf("failed to read file: %w", err)
}
return data, nil
2022-06-22 22:24:35 +00:00
}
func (l sftpDisk) Write(path string, data []byte) error {
slog.Debug("writing to file", slog.String("path", clean(path)), slog.String("schema", "sftp"))
file, err := l.client.Create(clean(path))
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
defer file.Close()
if _, err = io.Copy(file, bytes.NewReader(data)); err != nil {
return fmt.Errorf("failed to write file: %w", err)
}
return nil
2022-06-22 22:24:35 +00:00
}
func (l sftpDisk) Remove(path string) error {
slog.Debug("deleting path", slog.String("path", clean(path)), slog.String("schema", "sftp"))
if err := l.client.Remove(clean(path)); err != nil {
if err := l.client.RemoveAll(clean(path)); err != nil {
return fmt.Errorf("failed to delete path: %w", err)
}
}
return nil
2022-06-22 22:24:35 +00:00
}
func (l sftpDisk) MkDir(path string) error {
slog.Debug("making directory", slog.String("path", clean(path)), slog.String("schema", "sftp"))
if err := l.client.MkdirAll(clean(path)); err != nil {
return fmt.Errorf("failed to make directory: %w", err)
}
return nil
2022-06-22 22:24:35 +00:00
}
func (l sftpDisk) ReadDir(path string) ([]Entry, error) {
slog.Debug("reading directory", slog.String("path", clean(path)), slog.String("schema", "sftp"))
dir, err := l.client.ReadDir(clean(path))
if err != nil {
return nil, fmt.Errorf("failed to list files in directory: %w", err)
}
entries := make([]Entry, len(dir))
for i, entry := range dir {
entries[i] = sftpEntry{
FileInfo: entry,
}
}
return entries, nil
2022-06-22 22:24:35 +00:00
}
func (l sftpDisk) Open(path string, _ int) (io.WriteCloser, error) {
slog.Debug("opening for writing", slog.String("path", clean(path)), slog.String("schema", "sftp"))
f, err := l.client.Create(clean(path))
if err != nil {
slog.Error("failed to open file", slog.Any("err", err))
}
return f, nil
2022-06-22 22:24:35 +00:00
}