ollie/toolsrv/dial.go

84 lines
2.2 KiB
Go

package toolsrv
import (
"context"
"fmt"
"net"
)
// Addr describes how to reach a tool server.
type Addr struct {
// Network is the transport: "unix" (pre-existing socket) or "ssh".
Network string
// Host is the SSH target (user@host or user@host:port). Empty for local.
Host string
// CWD is the working directory on the target.
CWD string
// Socket is the Unix socket path to connect to.
// For local: set by Spawn(). For SSH: set by SpawnRemote().
Socket string
}
// LocalAddr returns an Addr for connecting to a local socket.
func LocalAddr(socket string) Addr {
return Addr{Network: "unix", Socket: socket}
}
// SSHAddr returns an Addr for a remote tool server over SSH.
func SSHAddr(host, cwd string) Addr {
return Addr{Network: "ssh", Host: host, CWD: cwd}
}
// Dial connects to a running tool server at the given address.
// The server must already be running (started via Spawn or SpawnRemote).
func Dial(ctx context.Context, addr Addr) (*Conn, error) {
switch addr.Network {
case "", "unix":
if addr.Socket == "" {
return nil, fmt.Errorf("toolsrv.Dial: socket path required for unix network")
}
conn, err := net.Dial("unix", addr.Socket)
if err != nil {
return nil, fmt.Errorf("toolsrv.Dial: %w", err)
}
c := NewConn(conn, func() { conn.Close() })
if err := c.Ping(); err != nil {
c.Close()
return nil, fmt.Errorf("toolsrv.Dial: ping failed: %w", err)
}
return c, nil
case "ssh":
// For SSH, the caller should use SpawnRemote first, then Dial the
// returned local socket. Direct SSH dial is a convenience that does both.
proc, err := SpawnRemote(ctx, RemoteConfig{
SSHTarget: addr.Host,
CWD: addr.CWD,
})
if err != nil {
return nil, err
}
conn, err := net.Dial("unix", proc.Socket)
if err != nil {
proc.Close()
return nil, fmt.Errorf("toolsrv.Dial ssh: %w", err)
}
// Wrap cleanup: closing the conn + the process.
c := NewConn(conn, func() {
conn.Close()
proc.Close()
})
if err := c.Ping(); err != nil {
c.Close()
return nil, fmt.Errorf("toolsrv.Dial ssh: ping failed: %w", err)
}
if info, err := c.FetchHostInfo(); err == nil {
c.Info = info
}
return c, nil
default:
return nil, fmt.Errorf("toolsrv.Dial: unsupported network %q", addr.Network)
}
}