[Tarantool-patches] [test-run] Add options for upgrade testing
Leonid Vasiliev
lvasiliev at tarantool.org
Fri Nov 13 15:41:43 MSK 2020
Hi! Thank your for the patch.
LGTM.
To add per line comments, I copied "diff" and marked my comments with
"Comment!" in that.
See some minor comments below:
Option '--snapshot-version' specifies a path to snapshot
that will be loaded in Tarantool before testing.
Option '--disable-schema-upgrade' allows to skip execution
of Tarantool upgrade script using error injection mechanism.
Part of: https://github.com/tarantool/tarantool/issues/4801
---
lib/app_server.py | 30 ++++++++++++++++++++++++++++--
lib/options.py | 26 ++++++++++++++++++++++++++
lib/server.py | 32 ++++++++++++++++++++++++++++----
lib/tarantool_server.py | 33 ++++++++++++++++++++++++++++-----
lib/utils.py | 30 ++++++++++++++++++++++++++++++
5 files changed, 140 insertions(+), 11 deletions(-)
diff --git a/lib/app_server.py b/lib/app_server.py
index 2cb8a87..efe9096 100644
--- a/lib/app_server.py
+++ b/lib/app_server.py
@@ -1,12 +1,15 @@
import errno
import glob
import os
+import re
import shutil
+import shlex
+import subprocess
import sys
from gevent.subprocess import Popen, PIPE
-from lib.colorer import color_log
+from lib.colorer import color_log, color_stdout
from lib.preprocessor import TestState
from lib.server import Server
from lib.tarantool_server import Test
@@ -73,6 +76,10 @@ class AppServer(Server):
self.binary = TarantoolServer.binary
self.use_unix_sockets_iproto = ini['use_unix_sockets_iproto']
+ if self.disable_schema_upgrade and not self.test_debug():
+ color_stdout("Can't disable schema upgrade on release build\n",
+ schema='error')
+
@property
def logfile(self):
# remove suite name using basename
@@ -86,7 +93,12 @@ class AppServer(Server):
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
@@ -170,3 +182,17 @@ class AppServer(Server):
test_suite.ini))
test_suite.tests = tests
+
+ def test_option_get(self, option_list_str, silent=False):
+ args = [self.binary] + shlex.split(option_list_str)
+ if not silent:
+ print " ".join([os.path.basename(self.binary)] + args[1:])
+ output = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT).stdout.read()
+ return output
+
+ def test_debug(self):
+ if re.findall(r"-Debug", self.test_option_get("-V", True), re.I):
+ return True
+ return False
diff --git a/lib/options.py b/lib/options.py
index 47bbc0f..0a318ef 100644
--- a/lib/options.py
+++ b/lib/options.py
@@ -209,6 +209,20 @@ class Options:
help="""Update or create file with reference output
(.result).
Default: false.""")
+ parser.add_argument(
+ "--snapshot-path",
+ dest='snapshot_path',
+ default=None,
+ help="""Path to a Tarantool 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
@@ -227,5 +241,17 @@ class Options:
color_stdout(format_str.format(op1, op2), schema='error')
check_error = True
+ if getattr(self, 'disable_schema_upgrade', '') and \
+ not (self, 'snapshot_path', ''):
+ color_stdout("option --disable-schema-upgrade \
+ depends on --snapshot-version\n", schema='error')
+ check_error = True
+
+ if getattr(self, 'snapshot_path', ''):
+ snapshot_path = getattr(self, 'snapshot_path', '')
+ if not os.path.exists(snapshot_path):
+ color_stdout("path {} not exist\n", snapshot_path,
schema='error')
More than 80 characters per line.
+ check_error = True
+
if check_error:
exit(-1)
diff --git a/lib/server.py b/lib/server.py
index 321eac7..b994248 100644
--- a/lib/server.py
+++ b/lib/server.py
@@ -1,6 +1,7 @@
import glob
import os
import shutil
+import os.path
Comment! Unneeded. "os" has been imported previously.
from itertools import product
from lib.server_mixins import ValgrindMixin
from lib.server_mixins import GdbMixin
@@ -8,7 +9,8 @@ from lib.server_mixins import GdbServerMixin
from lib.server_mixins import LLdbMixin
from lib.server_mixins import StraceMixin
from lib.server_mixins import LuacovMixin
-from lib.colorer import color_stdout
+from lib.colorer import color_stdout, color_log
+from lib.options import Options
from lib.utils import print_tail_n
@@ -24,6 +26,9 @@ class Server(object):
DEFAULT_INSPECTOR = 0
TEST_RUN_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
".."))
+ DISABLE_AUTO_UPGRADE = "require('fiber').sleep(0) \
+ assert(box.error.injection.set('ERRINJ_AUTO_UPGRADE', true) ==
'ok', \
+ 'no such errinj')"
Comment! If I understand correctly, Turenko wanted to mark this with a
comment
about https://github.com/tarantool/tarantool/issues/4983
@property
def vardir(self):
@@ -73,8 +78,7 @@ class Server(object):
core = ini['core'].lower().strip()
cls.mdlname = "lib.{0}_server".format(core.replace(' ', '_'))
cls.clsname = "{0}Server".format(core.title().replace(' ', ''))
- corecls = __import__(cls.mdlname,
- fromlist=cls.clsname).__dict__[cls.clsname]
+ corecls = __import__(cls.mdlname,
fromlist=cls.clsname).__dict__[cls.clsname]
Comment! Unneeded changes and now it's more than 80 symbols per line.
return corecls.__new__(corecls, ini, *args, **kwargs)
def __init__(self, ini, test_suite=None):
@@ -84,6 +88,9 @@ class Server(object):
self.inspector_port = int(ini.get(
'inspector_port', self.DEFAULT_INSPECTOR
))
+ self.testdir = os.path.abspath(os.curdir)
+ self.disable_schema_upgrade = None
+ self.snapshot_path = Options().args.snapshot_path
# filled in {Test,AppTest,LuaTest,PythonTest}.execute()
# or passed through execfile() for PythonTest (see
@@ -94,6 +101,15 @@ class Server(object):
# default servers running in TestSuite.run_all()
self.test_suite = test_suite
+ self.disable_schema_upgrade = Options().args.disable_schema_upgrade
+ if self.snapshot_path:
+ self.snapshot_path = os.path.abspath(self.snapshot_path)
+ if os.path.exists(self.snapshot_path):
+ self.snapshot_basename =
os.path.basename(self.snapshot_path)
+ else:
+ color_stdout("\nSnapshot {} have been not
found\n".format(self.snapshot_path),
+ schema='error')
Comment! More than 80 characters per line. But I think it's ok here.
+
def prepare_args(self, args=[]):
return args
@@ -110,7 +126,15 @@ class Server(object):
os.remove(f)
def install(self, binary=None, vardir=None, mem=None, silent=True):
- pass
+ if self.snapshot_basename:
+ (instance_name, _) =
os.path.splitext(os.path.basename(self.script))
+ instance_dir = os.path.join(self.vardir, instance_name)
+ if not os.path.exists(instance_dir):
+ os.mkdir(instance_dir)
+ snapshot_dest = os.path.join(instance_dir,
"00000000000000000000.snap")
Comment! More than 80 characters per line.
+ color_log("Copying snapshot {} to {}\n".format(
+ self.snapshot_path, snapshot_dest))
+ shutil.copy(self.snapshot_path, snapshot_dest)
def init(self):
pass
diff --git a/lib/tarantool_server.py b/lib/tarantool_server.py
index fd102b9..4f4c6b4 100644
--- a/lib/tarantool_server.py
+++ b/lib/tarantool_server.py
@@ -27,7 +27,7 @@ from lib.colorer import color_stdout, color_log
from lib.preprocessor import TestState
from lib.server import Server
from lib.test import Test
-from lib.utils import find_port
+from lib.utils import find_port, extract_schema_from_snapshot
from lib.utils import format_process
from lib.utils import signame
from lib.utils import warn_unix_socket
@@ -609,7 +609,6 @@ class TarantoolServer(Server):
}
ini.update(_ini)
Server.__init__(self, ini, test_suite)
- self.testdir = os.path.abspath(os.curdir)
self.sourcedir = os.path.abspath(os.path.join(os.path.basename(
sys.argv[0]), "..", ".."))
self.name = "default"
@@ -641,6 +640,10 @@ class TarantoolServer(Server):
if 'test_run_current_test' in caller_globals.keys():
self.current_test = caller_globals['test_run_current_test']
+ if self.disable_schema_upgrade and not self.test_debug():
+ color_stdout("Can't disable schema upgrade on release build\n",
+ schema='error')
+
def __del__(self):
self.stop()
@@ -745,6 +748,9 @@ class TarantoolServer(Server):
path = os.path.join(self.vardir, self.name + '.control')
warn_unix_socket(path)
+ super(TarantoolServer, self).install(self.binary,
+ self.vardir, None,
silent=True)
+
def deploy(self, silent=True, **kwargs):
self.install(silent)
self.start(silent=silent, **kwargs)
@@ -776,7 +782,13 @@ class TarantoolServer(Server):
self.vardir)
def prepare_args(self, args=[]):
- return [self.ctl_path, 'start', os.path.basename(self.script)]
+ args
+ cli_args = [self.ctl_path, 'start',
+ os.path.basename(self.script)] + args
+ if self.disable_schema_upgrade:
+ cli_args = [self.binary, '-e',
+ self.DISABLE_AUTO_UPGRADE] + cli_args
+
+ return cli_args
def pretest_clean(self):
# Don't delete snap and logs for 'default' tarantool server
@@ -861,6 +873,18 @@ class TarantoolServer(Server):
self.admin = CON_SWITCH[self.tests_type]('localhost', port)
self.status = 'started'
+ if not self.ctl_path:
+ self.ctl_path = self.find_exe(self.builddir)
+ if self.disable_schema_upgrade:
+ version = extract_schema_from_snapshot(self.ctl_path,
+ self.builddir,
self.snapshot_path)
Comment! More than 80 symbols per line.
+ current_ver = yaml.safe_load(self.admin.execute(
+ 'box.space._schema:get{"version"}'))
+ if version == current_ver:
+ raise_msg = 'Versions are inequal ({} vs {})'.format(
+ version, current_ver)
+ raise Exception(raise_msg)
+
def crash_detect(self):
if self.crash_expected:
return
@@ -1066,7 +1090,6 @@ class TarantoolServer(Server):
if not silent:
print " ".join([os.path.basename(self.binary)] + args[1:])
output = subprocess.Popen(args,
- cwd=self.vardir,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT).stdout.read()
return output
@@ -1126,7 +1149,7 @@ class TarantoolServer(Server):
def get_param(self, param=None):
if param is not None:
return yaml.safe_load(self.admin("box.info." + param,
- silent=True))[0]
+ silent=True))[0]
return yaml.safe_load(self.admin("box.info", silent=True))
def get_lsn(self, node_id):
diff --git a/lib/utils.py b/lib/utils.py
index cc2f6b7..7401d5c 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -3,8 +3,10 @@ import sys
import six
import collections
import signal
+import subprocess
import random
import fcntl
+import json
import difflib
import time
from gevent import socket
@@ -264,3 +266,31 @@ def just_and_trim(src, width):
if len(src) > width:
return src[:width - 1] + '>'
return src.ljust(width)
+
+
+def extract_schema_from_snapshot(ctl_path, builddir, snapshot_path):
+ """
+ Extract schema version from snapshot, example of record:
+ {"HEADER":{"lsn":2,"type":"INSERT","timestamp":1584694286.0031},
+ "BODY":{"space_id":272,"tuple":["version",2,3,1]}}
+ """
Comment! If my internal python interpreter is working fine, the return value
is a list: "['version', 2, 3, 1]". Typically version is a string
like:"2.3.1".
I don't mind, but specify it in description.
+ SCHEMA_SPACE_ID = 272
+
+ ctl_args = [ctl_path, 'cat', snapshot_path,
+ '--format=json', '--show-system']
+ proc = subprocess.Popen(ctl_args, stdout=subprocess.PIPE)
+ version = None
+ while True:
+ line = proc.stdout.readline()
+ if not line:
+ break
+ json_line = json.loads(line.strip())
+ query_type = json_line["HEADER"]["type"]
+ space_id = json_line["BODY"]["space_id"]
+ if query_type == "INSERT" or query_type == "REPLACE" and
space_id == SCHEMA_SPACE_ID:
Comment! More than 80 characters per line.
+ query_tuple = json_line['BODY']['tuple']
+ if query_tuple[0] == 'version':
+ version = query_tuple
+ break
+
+ return version
--
2.7.4
More information about the Tarantool-patches
mailing list