From bb6d78e528f607724f2fc842c4201f7bbbf11087 Mon Sep 17 00:00:00 2001 From: Mikhail Elhimov Date: Tue, 25 Feb 2025 04:11:42 +0300 Subject: [PATCH 1/2] fixup 956c5cd: remove duplicate entries in CHANGELOG --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e039403b2..62e9500be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added -- `tt pack `: added TCM file packaging. -- `tt aeon connect`: add connection from the cluster config. - `tt aeon connect`: add connection from the `app:insance_name`. ### Changed From ff4686f39ee97370062f1ea5b4a1993c4c467395 Mon Sep 17 00:00:00 2001 From: Mikhail Elhimov Date: Tue, 4 Mar 2025 12:26:11 +0300 Subject: [PATCH 2/2] connect: allow to disconnect with Ctrl+C/Ctrl+\ if execution hung Closes #613 @TarantoolBot document Title: `tt connect` allow to disconnect with Ctrl+C/Ctrl+\ if execution hung --- CHANGELOG.md | 2 ++ cli/connect/console.go | 23 ++++++++++++++++- test/integration/connect/test_connect.py | 33 +++++++++++++++++++++++- test/tt_helper.py | 13 ++++++++-- 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62e9500be..4ec698d36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed +- `tt connect`: allow to disconnect with `Ctrl+C` or `Ctrl+\` if script execution hung. + ### Fixed - `tt connect`: return Lua parse error. diff --git a/cli/connect/console.go b/cli/connect/console.go index 35bf4689f..fefcfc2c5 100644 --- a/cli/connect/console.go +++ b/cli/connect/console.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "os/signal" "sort" "strings" "syscall" @@ -276,7 +277,27 @@ func getExecutor(console *Console, connectCtx ConnectCtx) (func(string), error) console.livePrefixEnabled = false } - return executor, nil + signalable_executor := func(in string) { + // Signal handler. + handleSignals := func(console *Console, stop chan struct{}) { + sig := make(chan os.Signal, 1) + signal.Notify(sig, syscall.SIGINT, syscall.SIGQUIT) + select { + case <-stop: + return + case <-sig: + console.Close() + os.Exit(0) + } + } + + stop := make(chan struct{}) + go handleSignals(console, stop) + executor(in) + stop <- struct{}{} + } + + return signalable_executor, nil } func getCompleter(console *Console, connectCtx ConnectCtx) prompt.Completer { diff --git a/test/integration/connect/test_connect.py b/test/integration/connect/test_connect.py index e646fc8e7..17df03f60 100644 --- a/test/integration/connect/test_connect.py +++ b/test/integration/connect/test_connect.py @@ -3,6 +3,7 @@ import platform import re import shutil +import signal import subprocess import tempfile from pathlib import Path @@ -15,7 +16,7 @@ run_command_and_get_output, run_path, skip_if_cluster_app_unsupported, skip_if_quit_unsupported, skip_if_tarantool_ce, skip_if_tuple_format_supported, - skip_if_tuple_format_unsupported, wait_file) + skip_if_tuple_format_unsupported, wait_file, wait_files) tarantool_major_version, tarantool_minor_version = get_tarantool_version() @@ -3136,3 +3137,33 @@ def test_custom_evaler_errors(tt_cmd, tmpdir_with_cfg): finally: stop_app(tt_cmd, tmpdir, "test_app") + + +@pytest.mark.tt(app_path='test_single_app/test_app.lua') +@pytest.mark.parametrize('sig', [ + pytest.param(signal.SIGINT, id='SIGINT'), + pytest.param(signal.SIGQUIT, id='SIGQUIT'), +]) +def test_disconnect_by_signal(tt, sig): + # Start an instance. + tt.exec("start", "test_app") + # Check for start. + assert wait_files(5, [tt.path('configured')]) + + p = tt.popen("connect", "test_app", + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + + try: + p.communicate("while true do end", 2) + # Should not reach this code. + assert False + except subprocess.TimeoutExpired: + p.send_signal(sig) + + assert p.wait(1) is not None + + # Make sure the instance is stopped. + tt.exec("kill", "-f", "test_app") diff --git a/test/tt_helper.py b/test/tt_helper.py index 4b3d1f15b..baa2e649c 100644 --- a/test/tt_helper.py +++ b/test/tt_helper.py @@ -1,4 +1,5 @@ import os +import subprocess import utils @@ -39,8 +40,16 @@ def is_instance_of(inst, *targets): def exec(self, *args, **kwargs): args = list(filter(lambda x: x is not None, args)) cmd = [self.__tt_cmd, *args] - input = kwargs.get('input') - return utils.run_command_and_get_output(cmd, cwd=self.__work_dir, input=input) + tt_kwargs = dict(cwd=self.__work_dir) + tt_kwargs.update(kwargs) + return utils.run_command_and_get_output(cmd, **tt_kwargs) + + def popen(self, *args, **kwargs): + args = list(filter(lambda x: x is not None, args)) + cmd = [self.__tt_cmd, *args] + tt_kwargs = dict(cwd=self.__work_dir) + tt_kwargs.update(kwargs) + return subprocess.Popen(cmd, **tt_kwargs) def path(self, *paths): return os.path.join(self.__work_dir, *paths)