Add --debug flag for WebSocket connection diagnostics
Shows WS URL, auth headers (truncated), full handshake request/response. Also improves the handshake failure error to include the raw server response. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,7 @@ module PortainerCli
|
|||||||
HELP
|
HELP
|
||||||
|
|
||||||
def self.run(argv)
|
def self.run(argv)
|
||||||
|
debug = argv.delete('--debug')
|
||||||
config = Config.load
|
config = Config.load
|
||||||
cmd = argv.shift&.downcase
|
cmd = argv.shift&.downcase
|
||||||
|
|
||||||
@@ -56,11 +57,11 @@ module PortainerCli
|
|||||||
when 'console'
|
when 'console'
|
||||||
require_config!(config)
|
require_config!(config)
|
||||||
client = Client.new(config)
|
client = Client.new(config)
|
||||||
Commands::Console.new(config, client).run(argv)
|
Commands::Console.new(config, client, debug: debug).run(argv)
|
||||||
when 'exec', 'shell'
|
when 'exec', 'shell'
|
||||||
require_config!(config)
|
require_config!(config)
|
||||||
client = Client.new(config)
|
client = Client.new(config)
|
||||||
Commands::Exec.new(config, client).run(argv)
|
Commands::Exec.new(config, client, debug: debug).run(argv)
|
||||||
when 'version', '--version', '-v'
|
when 'version', '--version', '-v'
|
||||||
puts "portainer-cli #{PortainerCli::VERSION}"
|
puts "portainer-cli #{PortainerCli::VERSION}"
|
||||||
when 'help', '--help', '-h', nil
|
when 'help', '--help', '-h', nil
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ module PortainerCli
|
|||||||
class Console
|
class Console
|
||||||
include Base
|
include Base
|
||||||
|
|
||||||
def initialize(config, client)
|
def initialize(config, client, debug: false)
|
||||||
@config = config
|
@config = config
|
||||||
@client = client
|
@client = client
|
||||||
|
@debug = debug
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(args)
|
def run(args)
|
||||||
@@ -42,7 +43,7 @@ module PortainerCli
|
|||||||
send_resize(endpoint_id, exec_id) if $stdout.tty?
|
send_resize(endpoint_id, exec_id) if $stdout.tty?
|
||||||
trap('SIGWINCH') { send_resize(endpoint_id, exec_id) } if $stdout.tty?
|
trap('SIGWINCH') { send_resize(endpoint_id, exec_id) } if $stdout.tty?
|
||||||
|
|
||||||
WebsocketExec.new(ws_url, @config.auth_header, ssl_verify: @config.ssl_verify).run
|
WebsocketExec.new(ws_url, @config.auth_header, ssl_verify: @config.ssl_verify, debug: @debug).run
|
||||||
|
|
||||||
$stderr.puts dim("\r\nSession ended.")
|
$stderr.puts dim("\r\nSession ended.")
|
||||||
rescue PortainerCli::Client::ApiError => e
|
rescue PortainerCli::Client::ApiError => e
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ module PortainerCli
|
|||||||
class Exec
|
class Exec
|
||||||
include Base
|
include Base
|
||||||
|
|
||||||
def initialize(config, client)
|
def initialize(config, client, debug: false)
|
||||||
@config = config
|
@config = config
|
||||||
@client = client
|
@client = client
|
||||||
|
@debug = debug
|
||||||
end
|
end
|
||||||
|
|
||||||
# portainer-cli exec <endpoint-id> <container-name-or-id> [-- cmd args...]
|
# portainer-cli exec <endpoint-id> <container-name-or-id> [-- cmd args...]
|
||||||
@@ -36,7 +37,7 @@ module PortainerCli
|
|||||||
send_resize(endpoint_id, exec_id) if $stdout.tty?
|
send_resize(endpoint_id, exec_id) if $stdout.tty?
|
||||||
trap('SIGWINCH') { send_resize(endpoint_id, exec_id) } if $stdout.tty?
|
trap('SIGWINCH') { send_resize(endpoint_id, exec_id) } if $stdout.tty?
|
||||||
|
|
||||||
WebsocketExec.new(ws_url, @config.auth_header, ssl_verify: @config.ssl_verify).run
|
WebsocketExec.new(ws_url, @config.auth_header, ssl_verify: @config.ssl_verify, debug: @debug).run
|
||||||
|
|
||||||
$stderr.puts dim("\r\nSession ended.")
|
$stderr.puts dim("\r\nSession ended.")
|
||||||
rescue PortainerCli::Client::ApiError => e
|
rescue PortainerCli::Client::ApiError => e
|
||||||
|
|||||||
@@ -10,17 +10,23 @@ module PortainerCli
|
|||||||
class WebsocketExec
|
class WebsocketExec
|
||||||
READ_SIZE = 4096
|
READ_SIZE = 4096
|
||||||
|
|
||||||
def initialize(url, auth_header, ssl_verify: true)
|
def initialize(url, auth_header, ssl_verify: true, debug: false)
|
||||||
@url = URI.parse(url)
|
@url = URI.parse(url)
|
||||||
@auth_header = auth_header
|
@auth_header = auth_header
|
||||||
@ssl_verify = ssl_verify
|
@ssl_verify = ssl_verify
|
||||||
|
@debug = debug
|
||||||
end
|
end
|
||||||
|
|
||||||
def run
|
def run
|
||||||
|
debug "WS URL: #{@url}"
|
||||||
|
debug "Auth headers: #{@auth_header.map { |k, v| "#{k}: #{v[0, 8]}..." }.join(', ')}"
|
||||||
|
|
||||||
socket = open_socket
|
socket = open_socket
|
||||||
|
|
||||||
handshake = perform_handshake(socket)
|
handshake = perform_handshake(socket)
|
||||||
raise "WebSocket handshake failed: #{handshake.error}" unless handshake.valid?
|
unless handshake.valid?
|
||||||
|
raise "WebSocket handshake failed: #{handshake.error}\nServer response:\n#{handshake.instance_variable_get(:@data)}"
|
||||||
|
end
|
||||||
|
|
||||||
run_io_loop(socket)
|
run_io_loop(socket)
|
||||||
ensure
|
ensure
|
||||||
@@ -50,18 +56,27 @@ module PortainerCli
|
|||||||
url: @url.to_s,
|
url: @url.to_s,
|
||||||
headers: @auth_header
|
headers: @auth_header
|
||||||
)
|
)
|
||||||
|
debug "Handshake request:\n#{handshake.to_s.chomp}"
|
||||||
socket.write(handshake.to_s)
|
socket.write(handshake.to_s)
|
||||||
|
|
||||||
|
response = +''
|
||||||
loop do
|
loop do
|
||||||
byte = socket.read(1)
|
byte = socket.read(1)
|
||||||
break if byte.nil?
|
break if byte.nil?
|
||||||
|
response << byte
|
||||||
handshake << byte
|
handshake << byte
|
||||||
break if handshake.finished?
|
break if handshake.finished?
|
||||||
end
|
end
|
||||||
|
debug "Handshake response:\n#{response.chomp}"
|
||||||
|
|
||||||
handshake
|
handshake
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def debug(msg)
|
||||||
|
return unless @debug
|
||||||
|
$stderr.puts "\e[2m[debug] #{msg}\e[0m"
|
||||||
|
end
|
||||||
|
|
||||||
def run_io_loop(socket)
|
def run_io_loop(socket)
|
||||||
incoming = WebSocket::Frame::Incoming::Client.new
|
incoming = WebSocket::Frame::Incoming::Client.new
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user