Skip to content
Snippets Groups Projects
Select Git revision
  • 56f174e768b25966a3bc1678f6ea659b6913b0b1
  • master default protected
  • 0.5.9
  • 0.5.8
  • 0.5.7
  • 0.5.6
  • 0.5.5
  • 0.5.4
  • 0.5.3
  • 0.5.2
  • 0.5.1
  • 0.5.0
  • 0.4.17
  • 0.4.16
  • 0.4.15
  • 0.4.14
  • 0.4.13
  • 0.4.12
  • 0.4.11
  • 0.4.10
  • 0.4.9
  • 0.4.8
22 results

target-conan.mk

Blame
  • runnable-sftp.go 4.38 KiB
    package jobqueue
    
    import (
    	"fmt"
    	"github.com/pkg/sftp"
    	"golang.org/x/crypto/ssh"
    	"io"
    	"os"
    )
    
    // SFTPResult is a result of a sftp
    type SFTPResult struct {
    	FilesCopied []string
    }
    
    const (
    	CredentialTypePassword = "password"
    	CredentialTypeKey      = "key"
    )
    
    type Direction string
    
    const (
    	LocalToRemote Direction = "LocalToRemote"
    	RemoteToLocal Direction = "RemoteToLocal"
    )
    
    type SFTPRunnable struct {
    	Host              string
    	Port              int
    	User              string
    	Insecure          bool
    	Credential        string
    	CredentialType    string
    	HostKey           string
    	SrcDir            string
    	DstDir            string
    	TransferDirection Direction
    }
    
    func (s *SFTPRunnable) Run() (RunResult[SFTPResult], error) {
    
    	var authMethod ssh.AuthMethod
    
    	// Auth
    	switch s.CredentialType {
    	case CredentialTypePassword:
    		authMethod = ssh.Password(s.Credential)
    	case CredentialTypeKey:
    		key, err := ssh.ParsePrivateKey([]byte(s.Credential))
    		if err != nil {
    			return RunResult[SFTPResult]{Status: ResultStatusFailed}, err
    		}
    		authMethod = ssh.PublicKeys(key)
    	default:
    		return RunResult[SFTPResult]{Status: ResultStatusFailed}, ErrUnsupportedCredentialType
    	}
    
    	var hkCallback ssh.HostKeyCallback
    
    	if s.HostKey != "" {
    
    		hostkeyBytes := []byte(s.HostKey)
    		hostKey, err := ssh.ParsePublicKey(hostkeyBytes)
    		if err != nil {
    			return RunResult[SFTPResult]{Status: ResultStatusFailed}, err
    		}
    
    		hkCallback = ssh.FixedHostKey(hostKey)
    	} else {
    		if s.Insecure {
    			hkCallback = ssh.InsecureIgnoreHostKey()
    		} else {
    			hkCallback = ssh.FixedHostKey(nil)
    		}
    	}
    
    	config := &ssh.ClientConfig{
    		User: s.User,
    		Auth: []ssh.AuthMethod{
    			authMethod,
    		},
    		HostKeyCallback: hkCallback,
    	}
    
    	client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", s.Host, s.Port), config)
    	if err != nil {
    		return RunResult[SFTPResult]{Status: ResultStatusFailed}, err
    	}
    	defer client.Close()
    
    	sftpClient, err := sftp.NewClient(client)
    	if err != nil {
    		return RunResult[SFTPResult]{Status: ResultStatusFailed}, err
    	}
    	defer sftpClient.Close()
    
    	var filesCopied []string
    
    	switch s.TransferDirection {
    	case LocalToRemote:
    		filesCopied, err = s.copyLocalToRemote(sftpClient)
    	case RemoteToLocal:
    		filesCopied, err = s.copyRemoteToLocal(sftpClient)
    	default:
    		return RunResult[SFTPResult]{Status: ResultStatusFailed}, ErrUnsupportedTransferDirection
    	}
    
    	if err != nil {
    		return RunResult[SFTPResult]{Status: ResultStatusFailed}, err
    	}
    
    	if err != nil {
    		return RunResult[SFTPResult]{Status: ResultStatusFailed}, err
    	}
    
    	return RunResult[SFTPResult]{Status: ResultStatusSuccess, Data: SFTPResult{FilesCopied: filesCopied}}, nil
    }
    
    func copyFile(src io.Reader, dst io.Writer) error {
    	_, err := io.Copy(dst, src)
    	return err
    }
    
    func (s *SFTPRunnable) copyLocalToRemote(sftpClient *sftp.Client) ([]string, error) {
    
    	var filesCopied []string
    
    	// create destination directory
    	err := sftpClient.MkdirAll(s.DstDir)
    	if err != nil {
    		return nil, err
    	}
    
    	// copy files
    	files, err := os.ReadDir(s.SrcDir)
    	if err != nil {
    		return nil, err
    	}
    
    	for _, file := range files {
    		if file.IsDir() {
    			continue
    		}
    
    		srcFile, err := os.Open(fmt.Sprintf("%s/%s", s.SrcDir, file.Name()))
    		if err != nil {
    			return nil, err
    		}
    		dstFile, err := sftpClient.Create(fmt.Sprintf("%s/%s", s.DstDir, file.Name()))
    		if err != nil {
    			_ = srcFile.Close()
    			return nil, err
    		}
    		err = copyFile(srcFile, dstFile)
    		_ = srcFile.Close()
    		_ = dstFile.Close()
    		if err != nil {
    			return nil, err
    		}
    
    		filesCopied = append(filesCopied, fmt.Sprintf("%s/%s", s.DstDir, file.Name()))
    	}
    
    	return filesCopied, nil
    }
    
    func (s *SFTPRunnable) copyRemoteToLocal(sftpClient *sftp.Client) ([]string, error) {
    
    	var filesCopied []string
    
    	// create destination directory
    	err := os.MkdirAll(s.DstDir, 0755)
    	if err != nil {
    		return nil, err
    	}
    
    	// copy files
    	files, err := sftpClient.ReadDir(s.SrcDir)
    	if err != nil {
    		return nil, err
    	}
    
    	for _, file := range files {
    		if file.IsDir() {
    			continue
    		}
    
    		srcFile, err := sftpClient.Open(fmt.Sprintf("%s/%s", s.SrcDir, file.Name()))
    		if err != nil {
    			return nil, err
    		}
    		dstFile, err := os.Create(fmt.Sprintf("%s/%s", s.DstDir, file.Name()))
    		if err != nil {
    			_ = srcFile.Close()
    			return nil, err
    		}
    		err = copyFile(srcFile, dstFile)
    		_ = srcFile.Close()
    		_ = dstFile.Close()
    		if err != nil {
    			return nil, err
    		}
    
    		filesCopied = append(filesCopied, fmt.Sprintf("%s/%s", s.DstDir, file.Name()))
    	}
    
    	return filesCopied, nil
    }