// Package network provides SSH client and retry utilities. package network import ( "context" "sync" "github.com/jfraeys/fetch_ml/internal/logging" ) // SSHPool manages a pool of SSH client connections. type SSHPool struct { factory func() (*SSHClient, error) pool chan *SSHClient active int maxConns int mu sync.Mutex logger *logging.Logger } // NewSSHPool creates a new SSH connection pool. func NewSSHPool(maxConns int, factory func() (*SSHClient, error), logger *logging.Logger) *SSHPool { return &SSHPool{ factory: factory, pool: make(chan *SSHClient, maxConns), maxConns: maxConns, logger: logger, } } // Get retrieves an SSH client from the pool or creates a new one. func (p *SSHPool) Get(ctx context.Context) (*SSHClient, error) { select { case conn := <-p.pool: return conn, nil case <-ctx.Done(): return nil, ctx.Err() default: p.mu.Lock() if p.active < p.maxConns { p.active++ p.mu.Unlock() return p.factory() } p.mu.Unlock() // Wait for available connection select { case conn := <-p.pool: return conn, nil case <-ctx.Done(): return nil, ctx.Err() } } } // Put returns an SSH client to the pool. func (p *SSHPool) Put(conn *SSHClient) { select { case p.pool <- conn: default: // Pool is full, close connection err := conn.Close() if err != nil { p.logger.Warn("failed to close SSH connection", "error", err) } p.mu.Lock() p.active-- p.mu.Unlock() } } // Close closes all connections in the pool. func (p *SSHPool) Close() { p.mu.Lock() defer p.mu.Unlock() // Close all connections in the pool close(p.pool) for conn := range p.pool { err := conn.Close() if err != nil { p.logger.Warn("failed to close SSH connection", "error", err) } } // Reset active count p.active = 0 }