Skip to content

Commit

Permalink
connect: allow to disconnect with Ctrl+C/Ctrl+\ if execution hung
Browse files Browse the repository at this point in the history
Closes #613

@TarantoolBot document
Title: `tt connect` allow to disconnect with Ctrl+C/Ctrl+\ if execution hung
  • Loading branch information
elhimov committed Mar 4, 2025
1 parent bb6d78e commit 40aba86
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- `tt aeon connect`: add connection from the `app:insance_name`.

### Changed
- `tt connect`: allow to disconnect with Ctrl+C or Ctrl+\ if script execution hung.

### Fixed

Expand Down
23 changes: 22 additions & 1 deletion cli/connect/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
"os/signal"
"sort"
"strings"
"syscall"
Expand Down Expand Up @@ -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{}) {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGQUIT)
select {
case <-stop:
return
case <-sigCh:
console.Close()
os.Exit(0)
}
}

stopHandleSignalCh := make(chan struct{})
go handleSignals(console, stopHandleSignalCh)
executor(in)
stopHandleSignalCh <- struct{}{}
}

return signalable_executor, nil
}

func getCompleter(console *Console, connectCtx ConnectCtx) prompt.Completer {
Expand Down
33 changes: 32 additions & 1 deletion test/integration/connect/test_connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import re
import shutil
import subprocess
import signal
import tempfile
from pathlib import Path

Expand All @@ -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()

Expand Down Expand Up @@ -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")
13 changes: 11 additions & 2 deletions test/tt_helper.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import subprocess

import utils

Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 40aba86

Please sign in to comment.