Skip to content

Add signal argument to server restart routine #236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c19fe8b
Handle incorrect test-run commands gracefully
Totktonada Nov 13, 2020
66f94cf
Give meaningful error messages for failed commands
Totktonada Nov 13, 2020
5276be5
Don't fail drop_cluster() on a stopped instance
Totktonada Nov 13, 2020
0fe6aa1
logging: trace test-run commands and responses
Totktonada Nov 18, 2020
b3f5b06
refactoring: coalesce send tarantool command code
Totktonada Nov 18, 2020
81dcee2
logging: trace tarantool commands and responses
Totktonada Nov 18, 2020
5836c94
refactoring: move decolor() to colorer.py
Totktonada Nov 18, 2020
e40dfe1
logging: prepend each log entry with a timestamp
Totktonada Nov 18, 2020
f724bec
logging: mark chunks without a newline at the end
Totktonada Nov 18, 2020
49eec04
logging: add --debug option to print test-run logs
Totktonada Nov 18, 2020
4571750
logging: show log timestamps on the terminal
Totktonada Nov 18, 2020
f5ceb6a
logging: revisit server start / stop messages
Totktonada Nov 18, 2020
08a4817
Don't reset no-output timer on irrelevant events
Totktonada Nov 19, 2020
7266cd5
refactoring: make 'debug' property class level
Totktonada Dec 1, 2020
9288c76
Allow to extract a schema version from a snapshot
ligurio Dec 2, 2020
9f5a32e
Add options for upgrade testing
ligurio Nov 9, 2020
26aa387
Update code with checks of conflicting arguments
ligurio Nov 17, 2020
1219c07
typo: fix spelling in a result file diff output
Totktonada Nov 29, 2020
cdedd83
Handle lack of a last result file at a worker hang
Totktonada Nov 29, 2020
3ca76f3
refactoring: provide internal qa_notice() function
Totktonada Nov 29, 2020
67bd47d
Send SIGKILL if server does not stop after timeout
rtokarev Aug 19, 2019
d27f264
Set PWD env variable to a worker working directory
Totktonada Dec 4, 2020
3110939
Move .tarantoolctl config to test-run repository
avtikhon Nov 23, 2020
e843552
Setup replication_sync_timeout at .tarantoolctl
avtikhon Dec 4, 2020
a0b89ce
Add signal argument to server restart routine
avtikhon Nov 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .tarantoolctl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-- Options for test-run tarantoolctl

-- Note: tonumber(nil) is nil.
local workdir = os.getenv('TEST_WORKDIR')
local replication_sync_timeout = tonumber(os.getenv('REPLICATION_SYNC_TIMEOUT'))

default_cfg = {
pid_file = workdir,
wal_dir = workdir,
memtx_dir = workdir,
vinyl_dir = workdir,
log = workdir,
background = false,
replication_sync_timeout = replication_sync_timeout,
}

instance_dir = workdir

-- vim: set ft=lua :
8 changes: 8 additions & 0 deletions lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ def module_init():
os.chdir(path)
setenv()

# Keep the PWD environment variable in sync with a current
# working directory. It does not strictly necessary, just to
# avoid any confusion.
os.environ['PWD'] = os.getcwd()

warn_unix_sockets_at_start(args.vardir)

# always run with clean (non-existent) 'var' directory
Expand All @@ -52,10 +57,13 @@ def module_init():
soext = sys.platform == 'darwin' and 'dylib' or 'so'
os.environ["LUA_PATH"] = SOURCEDIR+"/?.lua;"+SOURCEDIR+"/?/init.lua;;"
os.environ["LUA_CPATH"] = BUILDDIR+"/?."+soext+";;"
os.environ["REPLICATION_SYNC_TIMEOUT"] = str(args.replication_sync_timeout)

TarantoolServer.find_exe(args.builddir)
UnittestServer.find_exe(args.builddir)

Options().check_schema_upgrade_option(TarantoolServer.debug)


# Init
######
Expand Down
16 changes: 15 additions & 1 deletion lib/app_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from lib.colorer import color_log
from lib.preprocessor import TestState
from lib.server import Server
from lib.server import DEFAULT_SNAPSHOT_NAME
from lib.tarantool_server import Test
from lib.tarantool_server import TarantoolServer
from lib.tarantool_server import TarantoolStartError
Expand Down Expand Up @@ -43,6 +44,14 @@ def execute(self, server):
server.logfile, retval)
self.current_test_greenlet = tarantool

# Copy the snapshot right before starting the server.
# Otherwise pretest_clean() would remove it.
if server.snapshot_path:
snapshot_dest = os.path.join(server.vardir, DEFAULT_SNAPSHOT_NAME)
color_log("Copying snapshot {} to {}\n".format(
server.snapshot_path, snapshot_dest))
shutil.copy(server.snapshot_path, snapshot_dest)

try:
tarantool.start()
tarantool.join()
Expand Down Expand Up @@ -86,7 +95,12 @@ def logfile(self):
return os.path.join(self.vardir, file_name)

def prepare_args(self, args=[]):
return [os.path.join(os.getcwd(), self.current_test.name)] + args
cli_args = [os.path.join(os.getcwd(), self.current_test.name)] + args
if self.disable_schema_upgrade:
cli_args = [self.binary, '-e',
self.DISABLE_AUTO_UPGRADE] + cli_args

return cli_args

def deploy(self, vardir=None, silent=True, need_init=True):
self.vardir = vardir
Expand Down
41 changes: 41 additions & 0 deletions lib/colorer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import re
import sys
from lib.singleton import Singleton

Expand All @@ -19,6 +20,33 @@ def color_log(*args, **kwargs):
color_stdout(*args, **kwargs)


def qa_notice(*args, **kwargs):
""" Print a notice for an QA engineer at the terminal.

Example::

* [QA Notice]
*
* Attempt to stop already stopped server 'foo'
*
"""
# Import from the function to avoid recursive import.
from lib.utils import prefix_each_line

# Use 'info' color by default (yellow).
if 'schema' not in kwargs:
kwargs = dict(kwargs, schema='info')

# Join all positional arguments (like color_stdout() do) and
# decorate with a header and asterisks.
data = ''.join([str(msg) for msg in args])
data = prefix_each_line('* ', data)
data = '\n* [QA Notice]\n*\n{}*\n'.format(data)

# Write out.
color_stdout(data, **kwargs)


class CSchema(object):
objects = {}

Expand Down Expand Up @@ -59,7 +87,10 @@ class SchemaAscetic(CSchema):
'test_skip': {'fgcolor': 'grey'},
'test_disa': {'fgcolor': 'grey'},
'error': {'fgcolor': 'red'},
'info': {'fgcolor': 'yellow'},
'test_var': {'fgcolor': 'yellow'},
'test-run command': {'fgcolor': 'green'},
'tarantool command': {'fgcolor': 'blue'},
}


Expand All @@ -86,6 +117,8 @@ class SchemaPretty(CSchema):
'tr_text': {'fgcolor': 'green'},
'log': {'fgcolor': 'grey'},
'test_var': {'fgcolor': 'yellow'},
'test-run command': {'fgcolor': 'green'},
'tarantool command': {'fgcolor': 'blue'},
}


Expand Down Expand Up @@ -142,6 +175,7 @@ class Colorer(object):
begin = "\033["
end = "m"
disable = begin+'0'+end
color_re = re.compile('\033' + r'\[\d(?:;\d\d)?m')

def __init__(self):
# These two fields can be filled later. It's for passing output from
Expand Down Expand Up @@ -243,9 +277,16 @@ def fileno(self):
def isatty(self):
return self.is_term

def decolor(self, data):
return self.color_re.sub('', data)


# Globals
#########


color_stdout = Colorer()


def decolor(data):
return color_stdout.decolor(data)
13 changes: 13 additions & 0 deletions lib/inspector.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
from gevent.server import StreamServer

from lib.utils import find_port
from lib.utils import prefix_each_line
from lib.colorer import color_stdout
from lib.colorer import color_log
from lib.colorer import qa_notice

from lib.tarantool_server import TarantoolStartError
from lib.preprocessor import LuaPreprocessorException


# Module initialization
Expand Down Expand Up @@ -90,11 +94,17 @@ def handle(self, socket, addr):
self.sem.acquire()

for line in self.readline(socket):
color_log('DEBUG: test-run received command: {}\n'.format(line),
schema='test-run command')

try:
result = self.parser.parse_preprocessor(line)
except (KeyboardInterrupt, TarantoolStartError):
# propagate to the main greenlet
raise
except LuaPreprocessorException as e:
qa_notice(str(e))
result = {'error': str(e)}
except Exception as e:
self.parser.kill_current_test()
color_stdout('\nTarantoolInpector.handle() received the ' +
Expand All @@ -106,6 +116,9 @@ def handle(self, socket, addr):
result = yaml.dump(result)
if not result.endswith('...\n'):
result = result + '...\n'
color_log("DEBUG: test-run's response for [{}]\n{}\n".format(
line, prefix_each_line(' | ', result)),
schema='test-run command')
socket.sendall(result)

self.sem.release()
Expand Down
59 changes: 55 additions & 4 deletions lib/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ def __init__(self):
help="""Print TAP13 test output to log.
Default: false.""")

parser.add_argument(
'--debug',
dest='debug',
action='store_true',
default=False,
help="""Print test-run logs to the terminal.
Default: false.""")

parser.add_argument(
"--force",
dest="is_force",
Expand Down Expand Up @@ -193,6 +201,20 @@ def __init__(self):
--valgrind, --long options is passed).
Note: The option works now only with parallel testing.""")

parser.add_argument(
"--replication-sync-timeout",
dest="replication_sync_timeout",
default=100,
type=int,
help="""The number of seconds that a replica will wait when
trying to sync with a master in a cluster, or a quorum of
masters, after connecting or during configuration update.
This could fail indefinitely if replication_sync_lag is smaller
than network latency, or if the replica cannot keep pace with
master updates. If replication_sync_timeout expires, the replica
enters orphan status.
Default: 100 [seconds].""")

parser.add_argument(
"--luacov",
dest="luacov",
Expand All @@ -209,6 +231,20 @@ def __init__(self):
help="""Update or create file with reference output (.result).
Default: false.""")

parser.add_argument(
"--snapshot",
dest='snapshot_path',
default=None,
type=os.path.abspath,
help="""Path to snapshot that will be loaded before testing.""")

parser.add_argument(
"--disable-schema-upgrade",
dest='disable_schema_upgrade',
action="store_true",
default=False,
help="""Disable schema upgrade on testing with snapshots.""")

# XXX: We can use parser.parse_intermixed_args() on
# Python 3.7 to understand commands like
# ./test-run.py foo --exclude bar baz
Expand All @@ -220,12 +256,27 @@ def check(self):
check_error = False
conflict_options = ('valgrind', 'gdb', 'lldb', 'strace')
for op1, op2 in product(conflict_options, repeat=2):
if op1 != op2 and getattr(self, op1, '') and \
getattr(self, op2, ''):
format_str = "Error: option --{} is not compatible \
with option --{}"
if op1 != op2 and getattr(self.args, op1, '') and \
getattr(self.args, op2, ''):
format_str = "\nError: option --{} is not compatible with option --{}\n"
color_stdout(format_str.format(op1, op2), schema='error')
check_error = True
break

snapshot_path = self.args.snapshot_path
if self.args.disable_schema_upgrade and not snapshot_path:
color_stdout("\nOption --disable-schema-upgrade requires --snapshot\n",
schema='error')
check_error = True

if snapshot_path and not os.path.exists(snapshot_path):
color_stdout("\nPath {} does not exist\n".format(snapshot_path), schema='error')
check_error = True

if check_error:
exit(-1)

def check_schema_upgrade_option(self, is_debug):
if self.args.disable_schema_upgrade and not is_debug:
color_stdout("Can't disable schema upgrade on release build\n", schema='error')
exit(1)
32 changes: 22 additions & 10 deletions lib/preprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,26 @@ class Namespace(object):


class LuaPreprocessorException(Exception):
""" Raised when evaluation of a test-run command failed.

This exception is displayed as QA notice on the terminal.

A Lua error is raised in a test code. The raised object is
a string with given error message.

All other errors that are raised during a command
evaluation are considered as show stoppers and lead to
stop execution of a test. KeyboardInterrupt and
TarantoolStartError stops testing at all, see
inspector.py.
"""

def __init__(self, val):
super(LuaPreprocessorException, self).__init__()
self.value = val

def __str__(self):
return "lua preprocessor error: {0}".format(repr(self.value))
return self.value


class TestState(object):
Expand Down Expand Up @@ -169,9 +183,6 @@ def options(self, key, value):
"Wrong option: {0}".format(repr(key)))

def server_start(self, ctype, sname, opts):
color_log('\nDEBUG: TestState[%s].server_start(%s, %s, %s)\n' % (
hex(id(self)), str(ctype), str(sname), str(opts)),
schema='test_var')
if sname not in self.servers:
raise LuaPreprocessorException(
'Can\'t start nonexistent server {0}'.format(repr(sname)))
Expand Down Expand Up @@ -209,12 +220,12 @@ def server_start(self, ctype, sname, opts):
return not crash_occured

def server_stop(self, ctype, sname, opts):
color_log('\nDEBUG: TestState[%s].server_stop(%s, %s, %s)\n' % (
hex(id(self)), str(ctype), str(sname), str(opts)),
schema='test_var')
if sname not in self.servers:
raise LuaPreprocessorException(
'Can\'t stop nonexistent server {0}'.format(repr(sname)))
if not self.servers[sname].status:
raise LuaPreprocessorException(
'Attempt to stop already stopped server {0}'.format(repr(sname)))
self.connections[sname].disconnect()
self.connections.pop(sname)
if 'signal' in opts:
Expand All @@ -230,9 +241,6 @@ def server_stop(self, ctype, sname, opts):
self.servers[sname].stop(silent=True)

def server_create(self, ctype, sname, opts):
color_log('\nDEBUG: TestState[%s].server_create(%s, %s, %s)\n' % (
hex(id(self)), str(ctype), str(sname), str(opts)),
schema='test_var')
if sname in self.servers:
raise LuaPreprocessorException(
'Server {0} already exists'.format(repr(sname)))
Expand Down Expand Up @@ -385,6 +393,10 @@ def filter(self, ctype, ref, ret):
"Wrong command for filters: {0}".format(repr(ctype)))

def lua_eval(self, name, expr, silent=True):
if name not in self.servers:
raise LuaPreprocessorException('Attempt to evaluate a command on ' +
'the nonexistent server {0}'.format(
repr(name)))
self.servers[name].admin.reconnect()
result = self.servers[name].admin(
'%s%s' % (expr, self.delimiter), silent=silent
Expand Down
Loading