Define the Connection interface that abstracts file operations, command execution, and tmux management for both local and remote (SSH) contexts. Includes: - Connection interface with file, exec, and tmux operations - FileInfo interface for remote stat results - BasicFileInfo implementation with JSON serialization - Error types for connection failures This enables Gas Town to manage rigs on remote machines using the same interface as local operations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
171 lines
4.7 KiB
Go
171 lines
4.7 KiB
Go
// Package connection provides an abstraction for local and remote operations.
|
|
// This allows Gas Town to manage rigs on remote machines via SSH using
|
|
// the same interface as local operations.
|
|
package connection
|
|
|
|
import (
|
|
"io/fs"
|
|
"time"
|
|
)
|
|
|
|
// Connection abstracts file operations, command execution, and tmux management
|
|
// for both local and remote (SSH) execution contexts.
|
|
type Connection interface {
|
|
// Identification
|
|
|
|
// Name returns a human-readable name for this connection.
|
|
Name() string
|
|
|
|
// IsLocal returns true if this is a local connection.
|
|
IsLocal() bool
|
|
|
|
// File operations
|
|
|
|
// ReadFile reads the named file and returns its contents.
|
|
ReadFile(path string) ([]byte, error)
|
|
|
|
// WriteFile writes data to the named file with the given permissions.
|
|
WriteFile(path string, data []byte, perm fs.FileMode) error
|
|
|
|
// MkdirAll creates a directory and all parent directories.
|
|
MkdirAll(path string, perm fs.FileMode) error
|
|
|
|
// Remove removes the named file or empty directory.
|
|
Remove(path string) error
|
|
|
|
// RemoveAll removes the named file or directory and any children.
|
|
RemoveAll(path string) error
|
|
|
|
// Stat returns file info for the named file.
|
|
Stat(path string) (FileInfo, error)
|
|
|
|
// Glob returns the names of all files matching the pattern.
|
|
Glob(pattern string) ([]string, error)
|
|
|
|
// Exists returns true if the path exists.
|
|
Exists(path string) (bool, error)
|
|
|
|
// Command execution
|
|
|
|
// Exec runs a command and returns its combined output.
|
|
Exec(cmd string, args ...string) ([]byte, error)
|
|
|
|
// ExecDir runs a command in the specified directory.
|
|
ExecDir(dir, cmd string, args ...string) ([]byte, error)
|
|
|
|
// ExecEnv runs a command with additional environment variables.
|
|
ExecEnv(env map[string]string, cmd string, args ...string) ([]byte, error)
|
|
|
|
// Tmux operations
|
|
|
|
// TmuxNewSession creates a new tmux session with the given name.
|
|
TmuxNewSession(name, dir string) error
|
|
|
|
// TmuxKillSession terminates the named tmux session.
|
|
TmuxKillSession(name string) error
|
|
|
|
// TmuxSendKeys sends keys to the named tmux session.
|
|
TmuxSendKeys(session, keys string) error
|
|
|
|
// TmuxCapturePane captures the last N lines from a tmux pane.
|
|
TmuxCapturePane(session string, lines int) (string, error)
|
|
|
|
// TmuxHasSession returns true if the named tmux session exists.
|
|
TmuxHasSession(name string) (bool, error)
|
|
|
|
// TmuxListSessions returns a list of all tmux session names.
|
|
TmuxListSessions() ([]string, error)
|
|
}
|
|
|
|
// FileInfo abstracts fs.FileInfo for use over remote connections.
|
|
// This is needed because fs.FileInfo contains methods that can't be
|
|
// easily serialized over SSH.
|
|
type FileInfo interface {
|
|
// Name returns the base name of the file.
|
|
Name() string
|
|
|
|
// Size returns the length in bytes.
|
|
Size() int64
|
|
|
|
// Mode returns the file mode bits.
|
|
Mode() fs.FileMode
|
|
|
|
// ModTime returns the modification time.
|
|
ModTime() time.Time
|
|
|
|
// IsDir returns true if this is a directory.
|
|
IsDir() bool
|
|
}
|
|
|
|
// BasicFileInfo is a simple implementation of FileInfo.
|
|
type BasicFileInfo struct {
|
|
FileName string `json:"name"`
|
|
FileSize int64 `json:"size"`
|
|
FileMode fs.FileMode `json:"mode"`
|
|
FileModTime time.Time `json:"mod_time"`
|
|
FileIsDir bool `json:"is_dir"`
|
|
}
|
|
|
|
// Name implements FileInfo.
|
|
func (f BasicFileInfo) Name() string { return f.FileName }
|
|
|
|
// Size implements FileInfo.
|
|
func (f BasicFileInfo) Size() int64 { return f.FileSize }
|
|
|
|
// Mode implements FileInfo.
|
|
func (f BasicFileInfo) Mode() fs.FileMode { return f.FileMode }
|
|
|
|
// ModTime implements FileInfo.
|
|
func (f BasicFileInfo) ModTime() time.Time { return f.FileModTime }
|
|
|
|
// IsDir implements FileInfo.
|
|
func (f BasicFileInfo) IsDir() bool { return f.FileIsDir }
|
|
|
|
// FromOSFileInfo creates a BasicFileInfo from an os.FileInfo.
|
|
func FromOSFileInfo(fi fs.FileInfo) BasicFileInfo {
|
|
return BasicFileInfo{
|
|
FileName: fi.Name(),
|
|
FileSize: fi.Size(),
|
|
FileMode: fi.Mode(),
|
|
FileModTime: fi.ModTime(),
|
|
FileIsDir: fi.IsDir(),
|
|
}
|
|
}
|
|
|
|
// Error types for connection operations.
|
|
type (
|
|
// ConnectionError indicates a connection-level failure.
|
|
ConnectionError struct {
|
|
Op string // Operation that failed (e.g., "connect", "exec")
|
|
Machine string // Machine name or address
|
|
Err error // Underlying error
|
|
}
|
|
|
|
// NotFoundError indicates a file or resource was not found.
|
|
NotFoundError struct {
|
|
Path string
|
|
}
|
|
|
|
// PermissionError indicates an access permission failure.
|
|
PermissionError struct {
|
|
Path string
|
|
Op string
|
|
}
|
|
)
|
|
|
|
func (e *ConnectionError) Error() string {
|
|
return "connection " + e.Op + " on " + e.Machine + ": " + e.Err.Error()
|
|
}
|
|
|
|
func (e *ConnectionError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
func (e *NotFoundError) Error() string {
|
|
return "not found: " + e.Path
|
|
}
|
|
|
|
func (e *PermissionError) Error() string {
|
|
return "permission denied: " + e.Op + " " + e.Path
|
|
}
|