Skip to content

Commit 7cc2b24

Browse files
committed
add resize for windows
1 parent b23fda3 commit 7cc2b24

File tree

1 file changed

+62
-16
lines changed

1 file changed

+62
-16
lines changed

src/aks-agent/azext_aks_agent/agent/k8s/pod_exec.py

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,27 +62,51 @@ def _get_terminal_size() -> Tuple[int, int]:
6262
return int(os.environ.get('LINES', 24)), int(os.environ.get('COLUMNS', 80))
6363

6464

65-
def _resize_terminal_handler(_signum, _frame, exec_stream):
65+
def _send_terminal_size(exec_stream, rows, cols):
6666
"""
67-
Handle terminal resize signal and send new size to pod via WebSocket channel.
67+
Send terminal size to pod via WebSocket channel.
6868
6969
Args:
70-
signum: Signal number
71-
frame: Current stack frame
7270
exec_stream: The WebSocket stream object
71+
rows: Terminal height
72+
cols: Terminal width
7373
"""
7474
try:
75-
rows, cols = _get_terminal_size()
76-
# Create resize message as JSON
7775
resize_message = json.dumps({
7876
"Width": cols,
7977
"Height": rows
8078
})
81-
# Send resize message through WebSocket channel 4 (RESIZE_CHANNEL)
8279
exec_stream.write_channel(RESIZE_CHANNEL, resize_message)
8380
logger.debug("Terminal resized to %dx%d", cols, rows)
8481
except Exception as e: # pylint: disable=broad-exception-caught
85-
logger.debug("Failed to resize terminal: %s", e)
82+
logger.debug("Failed to send terminal size: %s", e)
83+
84+
85+
def _monitor_resize_events_windows(exec_stream, stop_event):
86+
"""
87+
Monitor terminal resize events on Windows by polling.
88+
Implementation based on Kubernetes kubectl.
89+
Reference: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/kubectl/
90+
pkg/util/term/resizeevents_windows.go
91+
92+
Args:
93+
exec_stream: The WebSocket stream object
94+
stop_event: Threading event to stop monitoring
95+
"""
96+
last_size = _get_terminal_size()
97+
98+
while not stop_event.is_set() and exec_stream.is_open():
99+
try:
100+
current_size = _get_terminal_size()
101+
if current_size != last_size:
102+
_send_terminal_size(exec_stream, current_size[0], current_size[1])
103+
last_size = current_size
104+
except Exception as e: # pylint: disable=broad-exception-caught
105+
logger.debug("Error monitoring terminal size: %s", e)
106+
break
107+
108+
# Sleep to avoid hot looping (same interval as kubectl)
109+
stop_event.wait(0.25)
86110

87111

88112
def _set_terminal_raw_mode():
@@ -294,6 +318,8 @@ def exec_command_in_pod(pod_name: str, command: List[str], # pylint: disable=to
294318
fl = None
295319
cleanup_done = False
296320
windows_console_state = None
321+
resize_stop_event = None
322+
resize_thread = None
297323

298324
def cleanup():
299325
"""Cleanup function to ensure proper resource cleanup."""
@@ -304,7 +330,7 @@ def cleanup():
304330
return
305331
cleanup_done = True
306332

307-
# Restore signal handler
333+
# Restore signal handler (Unix/Linux). Windows does not use signal handlers for resize.
308334
if original_sigwinch and not IS_WINDOWS:
309335
try:
310336
signal.signal(signal.SIGWINCH, original_sigwinch)
@@ -403,13 +429,27 @@ def signal_handler(signum, _frame):
403429
if tty:
404430
terminal_state = _set_terminal_raw_mode()
405431

406-
# Set up terminal resize handler (Unix/Linux only)
407-
if not IS_WINDOWS and hasattr(signal, 'SIGWINCH'):
408-
def resize_handler(signum, frame):
409-
_resize_terminal_handler(signum, frame, resp)
410-
411-
# Register signal handler for terminal resize
412-
original_sigwinch = signal.signal(signal.SIGWINCH, resize_handler)
432+
# Set up terminal resize monitoring
433+
# Reference: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/kubectl/pkg/util/term/
434+
if IS_WINDOWS:
435+
# Windows: Poll for size changes in background thread
436+
resize_stop_event = threading.Event()
437+
resize_thread = threading.Thread(
438+
target=_monitor_resize_events_windows,
439+
args=(resp, resize_stop_event),
440+
daemon=True
441+
)
442+
resize_thread.start()
443+
elif hasattr(signal, 'SIGWINCH'):
444+
# Unix/Linux: Use SIGWINCH signal handler (must be in main thread)
445+
def sigwinch_handler(_signum, _frame):
446+
try:
447+
rows, cols = _get_terminal_size()
448+
_send_terminal_size(resp, rows, cols)
449+
except Exception as e: # pylint: disable=broad-exception-caught
450+
logger.debug("Error handling terminal resize: %s", e)
451+
452+
original_sigwinch = signal.signal(signal.SIGWINCH, sigwinch_handler)
413453

414454
# Set up heartbeat mechanism
415455
heartbeat_stop_event = threading.Event()
@@ -524,6 +564,12 @@ def resize_handler(signum, frame):
524564
logger.info("Pod exec session interrupted by user")
525565
return True
526566
finally:
567+
# Stop resize monitoring
568+
if resize_stop_event:
569+
resize_stop_event.set()
570+
if resize_thread and resize_thread.is_alive():
571+
resize_thread.join(timeout=2.0)
572+
527573
# Stop heartbeat
528574
if heartbeat_stop_event:
529575
heartbeat_stop_event.set()

0 commit comments

Comments
 (0)