Files
godoxy-yusing/internal/proxmox/lxc_command.go
yusing 09ddb925a3 refactor(proxmox): extract websocket command execution into reusable NodeCommand method
The LXCCommand method contained duplicate websocket handling logic for connecting to Proxmox's VNC terminal proxy. This refactoring extracts the common websocket connection, streaming, and cleanup logic into a new NodeCommand method on the Node type, allowing LXCCommand to simply format the pct command and delegate.

The go-proxmox submodule was also updated to access the NewNode constructor, which provides a cleaner API for creating node instances with the HTTP client.

- Moves ~100 lines of websocket handling from lxc_command.go to node.go
- Adds reusable NodeCommand method for executing commands via VNC websocket
- LXCCommand now simply calls NodeCommand with formatted command
- Maintains identical behavior and output streaming semantics
2026-01-25 12:43:26 +08:00

54 lines
1.8 KiB
Go

package proxmox
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"github.com/luthermonson/go-proxmox"
)
var ErrNoSession = fmt.Errorf("no session found, make sure username and password are set")
// closeTransportConnections forces close idle HTTP connections to prevent goroutine leaks.
// This is needed because the go-proxmox library's TermWebSocket closer doesn't close
// the underlying HTTP/2 connections, leaving goroutines stuck in writeLoop/readLoop.
func closeTransportConnections(httpClient *http.Client) {
if tr, ok := httpClient.Transport.(*http.Transport); ok {
tr.CloseIdleConnections()
}
}
// LXCCommand connects to the Proxmox VNC websocket and streams command output.
// It returns an io.ReadCloser that streams the command output.
func (n *Node) LXCCommand(ctx context.Context, vmid int, command string) (io.ReadCloser, error) {
node := proxmox.NewNode(n.client.Client, n.name)
lxc, err := node.Container(ctx, vmid)
if err != nil {
return nil, fmt.Errorf("failed to get container: %w", err)
}
if lxc.Status != "running" {
return io.NopCloser(bytes.NewReader(fmt.Appendf(nil, "container %d is not running, status: %s\n", vmid, lxc.Status))), nil
}
return n.NodeCommand(ctx, fmt.Sprintf("pct exec %d -- %s", vmid, command))
}
// LXCJournalctl streams journalctl output for the given service.
//
// If service is not empty, it will be used to filter the output by service.
// If limit is greater than 0, it will be used to limit the number of lines of output.
func (n *Node) LXCJournalctl(ctx context.Context, vmid int, service string, limit int) (io.ReadCloser, error) {
command := "journalctl -f"
if service != "" {
command = fmt.Sprintf("journalctl -u %q -f", service)
}
if limit > 0 {
command = fmt.Sprintf("%s -n %d", command, limit)
}
return n.LXCCommand(ctx, vmid, command)
}