This is an automated email from the ASF dual-hosted git repository. av pushed a commit to branch ignite-ducktape in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/ignite-ducktape by this push: new 5f3b0f1 Ducktape Service Specs (#8154) 5f3b0f1 is described below commit 5f3b0f18fa5aa6fa45b8cfb546414cf4c31dd3d0 Author: Maksim Timonin <timonin.ma...@gmail.com> AuthorDate: Mon Aug 17 15:15:54 2020 +0300 Ducktape Service Specs (#8154) --- modules/ducktests/tests/MANIFEST.in | 2 +- .../ducktests/tests/ignitetest/services/ignite.py | 28 +-- .../tests/ignitetest/services/ignite_app.py | 88 ++++++++- .../ducktests/tests/ignitetest/services/spark.py | 30 +-- .../ignitetest/services/utils/control_utility.py | 4 +- .../ignitetest/services/utils/ignite_aware.py | 90 ++------- .../ignitetest/services/utils/ignite_aware_app.py | 143 -------------- .../ignitetest/services/utils/ignite_config.py | 10 +- .../tests/ignitetest/services/utils/ignite_path.py | 8 +- .../services/utils/ignite_persistence.py | 64 +++++++ .../tests/ignitetest/services/utils/ignite_spec.py | 213 +++++++++++++++++++++ .../ignitetest/tests/add_node_rebalance_test.py | 4 +- .../ignitetest/tests/cellular_affinity_test.py | 4 +- .../tests/ignitetest/tests/control_utility_test.py | 6 +- .../tests/ignitetest/tests/discovery_test.py | 6 +- .../tests/ignitetest/tests/pme_free_switch_test.py | 4 +- .../ducktests/tests/ignitetest/tests/smoke_test.py | 21 +- .../tests/ignitetest/{tests => }/utils/__init__.py | 0 .../tests/ignitetest/{tests => }/utils/_mark.py | 2 +- .../ignitetest/{tests => }/utils/ignite_test.py | 0 .../tests/ignitetest/{tests => }/utils/version.py | 0 modules/ducktests/tests/setup.cfg | 30 --- modules/ducktests/tests/setup.py | 31 +-- 23 files changed, 434 insertions(+), 354 deletions(-) diff --git a/modules/ducktests/tests/MANIFEST.in b/modules/ducktests/tests/MANIFEST.in index edef4ab..6fcceb3 100644 --- a/modules/ducktests/tests/MANIFEST.in +++ b/modules/ducktests/tests/MANIFEST.in @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -recursive-include ignitetest */templates/* +recursive-include ignitetest **.j2 diff --git a/modules/ducktests/tests/ignitetest/services/ignite.py b/modules/ducktests/tests/ignitetest/services/ignite.py index 1c5a476..d0bb6d4 100644 --- a/modules/ducktests/tests/ignitetest/services/ignite.py +++ b/modules/ducktests/tests/ignitetest/services/ignite.py @@ -31,7 +31,7 @@ from ducktape.utils.util import wait_until from ignitetest.services.utils.concurrent import CountDownLatch, AtomicValue from ignitetest.services.utils.ignite_aware import IgniteAwareService -from ignitetest.tests.utils.version import DEV_BRANCH +from ignitetest.utils.version import DEV_BRANCH class IgniteService(IgniteAwareService): @@ -52,9 +52,11 @@ class IgniteService(IgniteAwareService): } # pylint: disable=R0913 - def __init__(self, context, num_nodes, modules=None, client_mode=False, version=DEV_BRANCH, properties="", - jvm_opts=None): - super(IgniteService, self).__init__(context, num_nodes, modules, client_mode, version, properties, jvm_opts) + def __init__(self, context, num_nodes, jvm_opts=None, properties="", client_mode=False, modules=None, + version=DEV_BRANCH): + super(IgniteService, self).__init__(context, num_nodes, properties, + client_mode=client_mode, modules=modules, version=version, + jvm_opts=jvm_opts) # pylint: disable=W0221 def start(self, timeout_sec=180): @@ -65,22 +67,6 @@ class IgniteService(IgniteAwareService): for node in self.nodes: self.await_node_started(node, timeout_sec) - def start_cmd(self, node): - jvm_opts = self.jvm_options + " " - jvm_opts += "-J-DIGNITE_SUCCESS_FILE=" + IgniteService.PERSISTENT_ROOT + "/success_file " - jvm_opts += "-J-Dlog4j.configDebug=true " - - cmd = "export EXCLUDE_TEST_CLASSES=true; " - cmd += "export IGNITE_LOG_DIR=" + IgniteService.PERSISTENT_ROOT + "; " - cmd += "export USER_LIBS=%s; " % ":".join(self.user_libs) - cmd += "%s %s %s 1>> %s 2>> %s &" % \ - (self.path.script("ignite.sh"), - jvm_opts, - IgniteService.CONFIG_FILE, - IgniteService.STDOUT_STDERR_CAPTURE, - IgniteService.STDOUT_STDERR_CAPTURE) - return cmd - def await_node_started(self, node, timeout_sec): """ Await topology ready event on node start. @@ -162,7 +148,7 @@ class IgniteService(IgniteAwareService): def clean_node(self, node): node.account.kill_java_processes(self.APP_SERVICE_CLASS, clean_shutdown=False, allow_fail=True) - node.account.ssh("sudo rm -rf -- %s" % IgniteService.PERSISTENT_ROOT, allow_fail=False) + node.account.ssh("sudo rm -rf -- %s" % self.PERSISTENT_ROOT, allow_fail=False) def thread_dump(self, node): """ diff --git a/modules/ducktests/tests/ignitetest/services/ignite_app.py b/modules/ducktests/tests/ignitetest/services/ignite_app.py index ef5120e..8d60dee 100644 --- a/modules/ducktests/tests/ignitetest/services/ignite_app.py +++ b/modules/ducktests/tests/ignitetest/services/ignite_app.py @@ -14,20 +14,90 @@ # limitations under the License. """ -This module contains the ignite application service allows to perform custom logic writen on java. +This module contains the base class to build Ignite aware application written on java. """ -from ignitetest.services.utils.ignite_aware_app import IgniteAwareApplicationService -from ignitetest.tests.utils.version import DEV_BRANCH +import re +from ignitetest.services.utils.ignite_aware import IgniteAwareService +from ignitetest.utils.version import DEV_BRANCH -class IgniteApplicationService(IgniteAwareApplicationService): + +class IgniteApplicationService(IgniteAwareService): """ - The Ignite application service allows to perform custom logic writen on java. + The base class to build Ignite aware application written on java. """ + SERVICE_JAVA_CLASS_NAME = "org.apache.ignite.internal.ducktest.utils.IgniteAwareApplicationService" + # pylint: disable=R0913 - def __init__(self, context, java_class_name, modules=None, client_mode=True, version=DEV_BRANCH, - properties="", params="", jvm_options=None, timeout_sec=60): - super(IgniteApplicationService, self).__init__(context, java_class_name, modules, client_mode, version, - properties, params, jvm_options, timeout_sec, start_ignite=True) + def __init__(self, context, java_class_name, params="", properties="", timeout_sec=60, modules=None, + client_mode=True, version=DEV_BRANCH, servicejava_class_name=SERVICE_JAVA_CLASS_NAME, + jvm_opts=None, start_ignite=True): + super(IgniteApplicationService, self).__init__(context, 1, properties, + client_mode=client_mode, + version=version, + modules=modules, + servicejava_class_name=servicejava_class_name, + java_class_name=java_class_name, + params=params, + jvm_opts=jvm_opts, + start_ignite=start_ignite) + + self.servicejava_class_name = servicejava_class_name + self.java_class_name = java_class_name + self.timeout_sec = timeout_sec + self.stop_timeout_sec = 10 + + def start(self): + super(IgniteApplicationService, self).start() + + self.logger.info("Waiting for Ignite aware Application (%s) to start..." % self.java_class_name) + + self.await_event("Topology snapshot", self.timeout_sec, from_the_beginning=True) + self.await_event("IGNITE_APPLICATION_INITIALIZED\\|IGNITE_APPLICATION_BROKEN", self.timeout_sec, + from_the_beginning=True) + + try: + self.await_event("IGNITE_APPLICATION_INITIALIZED", 1, from_the_beginning=True) + except Exception: + raise Exception("Java application execution failed. %s" % self.extract_result("ERROR")) + + # pylint: disable=W0221 + def stop_node(self, node, clean_shutdown=True, timeout_sec=20): + self.logger.info("%s Stopping node %s" % (self.__class__.__name__, str(node.account))) + node.account.kill_java_processes(self.servicejava_class_name, clean_shutdown=clean_shutdown, allow_fail=True) + + stopped = self.wait_node(node, timeout_sec=self.stop_timeout_sec) + assert stopped, "Node %s: did not stop within the specified timeout of %s seconds" % \ + (str(node.account), str(self.stop_timeout_sec)) + + self.await_event("IGNITE_APPLICATION_FINISHED\\|IGNITE_APPLICATION_BROKEN", from_the_beginning=True, + timeout_sec=timeout_sec) + + def clean_node(self, node): + if self.alive(node): + self.logger.warn("%s %s was still alive at cleanup time. Killing forcefully..." % + (self.__class__.__name__, node.account)) + + node.account.kill_java_processes(self.servicejava_class_name, clean_shutdown=False, allow_fail=True) + + node.account.ssh("rm -rf %s" % self.PERSISTENT_ROOT, allow_fail=False) + + def pids(self, node): + return node.account.java_pids(self.servicejava_class_name) + + def extract_result(self, name): + """ + :param name: Result parameter's name. + :return: Extracted result of application run. + """ + res = "" + + output = self.nodes[0].account.ssh_capture( + "grep '%s' %s" % (name + "->", self.STDOUT_STDERR_CAPTURE), allow_fail=False) + + for line in output: + res = re.search("%s(.*)%s" % (name + "->", "<-"), line).group(1) + + return res diff --git a/modules/ducktests/tests/ignitetest/services/spark.py b/modules/ducktests/tests/ignitetest/services/spark.py index 72ce9bc..a7cb65e 100644 --- a/modules/ducktests/tests/ignitetest/services/spark.py +++ b/modules/ducktests/tests/ignitetest/services/spark.py @@ -20,13 +20,11 @@ This module contains spark service class. import os.path from ducktape.cluster.remoteaccount import RemoteCommandError -from ducktape.services.service import Service +from ducktape.services.background_thread import BackgroundThreadService +from ignitetest.services.utils.ignite_persistence import PersistenceAware -from ignitetest.services.utils.ignite_aware import IgniteAwareService -from ignitetest.tests.utils.version import DEV_BRANCH - -class SparkService(IgniteAwareService): +class SparkService(BackgroundThreadService, PersistenceAware): """ Start a spark node. """ @@ -36,15 +34,12 @@ class SparkService(IgniteAwareService): logs = {} # pylint: disable=R0913 - def __init__(self, context, modules=None, version=DEV_BRANCH, num_nodes=3, properties=""): + def __init__(self, context, num_nodes=3): """ :param context: test context :param num_nodes: number of Ignite nodes. """ - modules = modules or [] - modules.extend(["ignite-spark"]) - - IgniteAwareService.__init__(self, context, num_nodes, modules, False, version, properties, None) + super(SparkService, self).__init__(context, num_nodes) self.log_level = "DEBUG" @@ -59,11 +54,14 @@ class SparkService(IgniteAwareService): } def start(self): - Service.start(self) + BackgroundThreadService.start(self) self.logger.info("Waiting for Spark to start...") def start_cmd(self, node): + """ + Prepare command to start Spark nodes + """ if node == self.nodes[0]: script = "start-master.sh" else: @@ -77,8 +75,7 @@ class SparkService(IgniteAwareService): return cmd - # pylint: disable=W0221 - def start_node(self, node, timeout_sec=30): + def start_node(self, node): self.init_persistent(node) cmd = self.start_cmd(node) @@ -93,6 +90,7 @@ class SparkService(IgniteAwareService): self.logger.debug("Monitoring - %s" % log_file) + timeout_sec = 30 with node.account.monitor_log(log_file) as monitor: node.account.ssh(cmd) monitor.wait_until(log_msg, timeout_sec=timeout_sec, backoff_sec=5, @@ -108,11 +106,17 @@ class SparkService(IgniteAwareService): node.account.ssh(os.path.join(SparkService.INSTALL_DIR, "sbin", "stop-slave.sh")) def clean_node(self, node): + """ + Clean spark persistence files + """ node.account.kill_java_processes(self.java_class_name(node), clean_shutdown=False, allow_fail=True) node.account.ssh("sudo rm -rf -- %s" % SparkService.SPARK_PERSISTENT_ROOT, allow_fail=False) def pids(self, node): + """ + :return: list of service pids on specific node + """ try: cmd = "jcmd | grep -e %s | awk '{print $1}'" % self.java_class_name(node) return list(node.account.ssh_capture(cmd, allow_fail=True, callback=int)) diff --git a/modules/ducktests/tests/ignitetest/services/utils/control_utility.py b/modules/ducktests/tests/ignitetest/services/utils/control_utility.py index ed91d18..2c13301 100644 --- a/modules/ducktests/tests/ignitetest/services/utils/control_utility.py +++ b/modules/ducktests/tests/ignitetest/services/utils/control_utility.py @@ -140,8 +140,8 @@ class ControlUtility: return output def __form_cmd(self, node, cmd): - return self._cluster.path.script("%s --host %s %s" % (self.BASE_COMMAND, node.account.externally_routable_ip, - cmd)) + return self._cluster.spec.path.script("%s --host %s %s" % + (self.BASE_COMMAND, node.account.externally_routable_ip, cmd)) @staticmethod def __parse_output(raw_output): diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py index 0b5e18f..77e48d7 100644 --- a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py +++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py @@ -17,62 +17,34 @@ This module contains the base class to build services aware of Ignite. """ -import os from abc import abstractmethod, ABCMeta from ducktape.services.background_thread import BackgroundThreadService from ducktape.utils.util import wait_until from six import add_metaclass -from ignitetest.services.utils.ignite_config import IgniteLoggerConfig, IgniteServerConfig, IgniteClientConfig -from ignitetest.services.utils.ignite_path import IgnitePath +from ignitetest.services.utils.ignite_spec import resolve_spec from ignitetest.services.utils.jmx_utils import ignite_jmx_mixin -from ignitetest.tests.utils.version import IgniteVersion +from ignitetest.services.utils.ignite_persistence import IgnitePersistenceAware @add_metaclass(ABCMeta) -class IgniteAwareService(BackgroundThreadService): +class IgniteAwareService(BackgroundThreadService, IgnitePersistenceAware): """ The base class to build services aware of Ignite. """ - # Root directory for persistent output - PERSISTENT_ROOT = "/mnt/service" - STDOUT_STDERR_CAPTURE = os.path.join(PERSISTENT_ROOT, "console.log") - WORK_DIR = os.path.join(PERSISTENT_ROOT, "work") - CONFIG_FILE = os.path.join(PERSISTENT_ROOT, "ignite-config.xml") - LOG4J_CONFIG_FILE = os.path.join(PERSISTENT_ROOT, "ignite-log4j.xml") - - logs = { - "console_log": { - "path": STDOUT_STDERR_CAPTURE, - "collect_default": True} - } # pylint: disable=R0913 - def __init__(self, context, num_nodes, modules, client_mode, version, properties, jvm_options): + def __init__(self, context, num_nodes, properties, **kwargs): + """ + **kwargs are params that passed to IgniteSpec + """ super(IgniteAwareService, self).__init__(context, num_nodes) - global_jvm_options = context.globals.get("jvm_opts", "") - - service_jvm_options = " ".join(map(lambda x: '-J' + x, jvm_options)) if jvm_options else "" - - self.jvm_options = " ".join(filter(None, [global_jvm_options, service_jvm_options])) - self.log_level = "DEBUG" self.properties = properties - if isinstance(version, IgniteVersion): - self.version = version - else: - self.version = IgniteVersion(version) - - self.path = IgnitePath(self.version, context) - self.client_mode = client_mode - - libs = modules or [] - libs.extend(["ignite-log4j"]) - - self.user_libs = list(map(lambda m: self.path.module(m) + "/*", libs)) + self.spec = resolve_spec(self, context, **kwargs) def start_node(self, node): self.init_persistent(node) @@ -88,26 +60,15 @@ class IgniteAwareService(BackgroundThreadService): Init persistent directory. :param node: Ignite service node. """ - logger_config = IgniteLoggerConfig().render(work_dir=self.WORK_DIR) - - node.account.mkdirs(self.PERSISTENT_ROOT) + super(IgniteAwareService, self).init_persistent(node) - node_config = self.config().render(config_dir=self.PERSISTENT_ROOT, - work_dir=self.WORK_DIR, - properties=self.properties, - consistent_id=node.account.externally_routable_ip) + node_config = self.spec.config().render(config_dir=self.PERSISTENT_ROOT, + work_dir=self.WORK_DIR, + properties=self.properties, + consistent_id=node.account.externally_routable_ip) setattr(node, "consistent_id", node.account.externally_routable_ip) node.account.create_file(self.CONFIG_FILE, node_config) - node.account.create_file(self.LOG4J_CONFIG_FILE, logger_config) - - @abstractmethod - def start_cmd(self, node): - """ - Start up command for service. - :param node: Ignite service node. - """ - raise NotImplementedError @abstractmethod def pids(self, node): @@ -117,18 +78,9 @@ class IgniteAwareService(BackgroundThreadService): """ raise NotImplementedError - def config(self): - """ - :return: Ignite node configuration. - """ - if self.client_mode: - return IgniteClientConfig(self.context) - - return IgniteServerConfig(self.context) - # pylint: disable=W0613 def _worker(self, idx, node): - cmd = self.start_cmd(node) + cmd = self.spec.command() self.logger.debug("Attempting to start Application Service on %s with command: %s" % (str(node.account), cmd)) @@ -141,7 +93,6 @@ class IgniteAwareService(BackgroundThreadService): """ return len(self.pids(node)) > 0 - # pylint: disable=R0913 def await_event_on_node(self, evt_message, node, timeout_sec, from_the_beginning=False, backoff_sec=5): """ Await for specific event message in a node's log file. @@ -171,16 +122,3 @@ class IgniteAwareService(BackgroundThreadService): for node in self.nodes: self.await_event_on_node(evt_message, node, timeout_sec, from_the_beginning=from_the_beginning, backoff_sec=backoff_sec) - - def execute(self, command): - """ - Execute command on all nodes. - :param command: Command to execute. - """ - for node in self.nodes: - cmd = "%s 1>> %s 2>> %s" % \ - (self.path.script(command), - self.STDOUT_STDERR_CAPTURE, - self.STDOUT_STDERR_CAPTURE) - - node.account.ssh(cmd) diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware_app.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_aware_app.py deleted file mode 100644 index 29e0a2c..0000000 --- a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware_app.py +++ /dev/null @@ -1,143 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -This module contains the base class to build Ignite aware application written on java. -""" - -import base64 -import json -import re - -from ignitetest.services.utils.ignite_aware import IgniteAwareService - - -class IgniteAwareApplicationService(IgniteAwareService): - """ - The base class to build Ignite aware application written on java. - """ - - # pylint: disable=R0913 - def __init__(self, context, java_class_name, modules, client_mode, version, properties, params, jvm_options, - timeout_sec, start_ignite=False, - service_java_class_name="org.apache.ignite.internal.ducktest.utils.IgniteAwareApplicationService"): - super(IgniteAwareApplicationService, self).__init__(context, 1, modules, client_mode, version, properties, - jvm_options) - - self.servicejava_class_name = service_java_class_name - self.java_class_name = java_class_name - self.timeout_sec = timeout_sec - self.stop_timeout_sec = 10 - self.params = params - self.start_ignite = start_ignite - - def start(self): - super(IgniteAwareApplicationService, self).start() - - self.logger.info("Waiting for Ignite aware Application (%s) to start..." % self.java_class_name) - - self.await_event("Topology snapshot", self.timeout_sec, from_the_beginning=True) - self.await_event("IGNITE_APPLICATION_INITIALIZED\\|IGNITE_APPLICATION_BROKEN", self.timeout_sec, - from_the_beginning=True) - - try: - self.await_event("IGNITE_APPLICATION_INITIALIZED", 1, from_the_beginning=True) - except Exception: - raise Exception("Java application execution failed. %s" % self.extract_result("ERROR")) - - def start_cmd(self, node): - cmd = self.env() - cmd += "%s %s %s 1>> %s 2>> %s &" % \ - (self.path.script("ignite.sh"), - self.jvm_opts(), - self.app_args(), - self.STDOUT_STDERR_CAPTURE, - self.STDOUT_STDERR_CAPTURE) - return cmd - - # pylint: disable=W0221 - def stop_node(self, node, clean_shutdown=True, timeout_sec=20): - self.logger.info("%s Stopping node %s" % (self.__class__.__name__, str(node.account))) - node.account.kill_java_processes(self.servicejava_class_name, clean_shutdown=clean_shutdown, allow_fail=True) - - stopped = self.wait_node(node, timeout_sec=self.stop_timeout_sec) - assert stopped, "Node %s: did not stop within the specified timeout of %s seconds" % \ - (str(node.account), str(self.stop_timeout_sec)) - - self.await_event("IGNITE_APPLICATION_FINISHED\\|IGNITE_APPLICATION_BROKEN", from_the_beginning=True, - timeout_sec=timeout_sec) - - def clean_node(self, node): - if self.alive(node): - self.logger.warn("%s %s was still alive at cleanup time. Killing forcefully..." % - (self.__class__.__name__, node.account)) - - node.account.kill_java_processes(self.servicejava_class_name, clean_shutdown=False, allow_fail=True) - - node.account.ssh("rm -rf %s" % self.PERSISTENT_ROOT, allow_fail=False) - - def app_args(self): - """ - :return: Application arguments. - """ - args = ",".join([str(self.start_ignite), self.java_class_name, IgniteAwareApplicationService.CONFIG_FILE]) - - if self.params != "": - args = ",".join([args, str(base64.b64encode(json.dumps(self.params).encode("UTF-8")))]) - - return args - - def pids(self, node): - return node.account.java_pids(self.servicejava_class_name) - - def jvm_opts(self): - """ - :return: Application JVM options. - """ - return "-J-DIGNITE_SUCCESS_FILE=" + self.PERSISTENT_ROOT + "/success_file " + \ - "-J-Dlog4j.configDebug=true " \ - "-J-Xmx1G " \ - "-J-ea " \ - "-J-DIGNITE_ALLOW_ATOMIC_OPS_IN_TX=false " + self.jvm_options - - def env(self): - """ - :return: Export string of additional environment variables. - """ - if not self.version.is_dev: - # Jackson requred to parse application params at java side. Release's version should be used. - for line in self.nodes[0].account.ssh_capture( - "ls -d %s/libs/optional/ignite-aws/* | grep jackson | tr '\n' ':' | sed 's/.$//'" % self.path.home): - self.user_libs.extend([line]) - - return "export MAIN_CLASS={main_class}; ".format(main_class=self.servicejava_class_name) + \ - "export EXCLUDE_TEST_CLASSES=true; " + \ - "export IGNITE_LOG_DIR={log_dir}; ".format(log_dir=self.PERSISTENT_ROOT) + \ - "export USER_LIBS=%s:/opt/ignite-dev/modules/ducktests/target/*; " % (":".join(self.user_libs)) - - def extract_result(self, name): - """ - :param name: Result parameter's name. - :return: Extracted result of application run. - """ - res = "" - - output = self.nodes[0].account.ssh_capture( - "grep '%s' %s" % (name + "->", self.STDOUT_STDERR_CAPTURE), allow_fail=False) - - for line in output: - res = re.search("%s(.*)%s" % (name + "->", "<-"), line).group(1) - - return res diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_config.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_config.py index 90b3bfa..a2bfe3d 100644 --- a/modules/ducktests/tests/ignitetest/services/utils/ignite_config.py +++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_config.py @@ -51,10 +51,7 @@ class IgniteServerConfig(Config): """ Ignite server node configuration. """ - def __init__(self, context): - path = DEFAULT_IGNITE_CONF - if 'ignite_server_config_path' in context.globals: - path = context.globals['ignite_server_config_path'] + def __init__(self, path=DEFAULT_IGNITE_CONF): super(IgniteServerConfig, self).__init__(path) @@ -62,10 +59,7 @@ class IgniteClientConfig(Config): """ Ignite client node configuration. """ - def __init__(self, context): - path = DEFAULT_IGNITE_CONF - if 'ignite_client_config_path' in context.globals: - path = context.globals['ignite_client_config_path'] + def __init__(self, path=DEFAULT_IGNITE_CONF): super(IgniteClientConfig, self).__init__(path) self.default_params.update(client_mode=True) diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_path.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_path.py index 2177e9b..4089112 100644 --- a/modules/ducktests/tests/ignitetest/services/utils/ignite_path.py +++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_path.py @@ -31,11 +31,9 @@ class IgnitePath: SCRATCH_ROOT = "/mnt" IGNITE_INSTALL_ROOT = "/opt" - def __init__(self, version, context): + def __init__(self, version, project="ignite"): self.version = version - self.project = context.globals.get("project", "ignite") - - home_dir = "%s-%s" % (self.project, str(self.version)) + home_dir = "%s-%s" % (project, str(self.version)) self.home = os.path.join(IgnitePath.IGNITE_INSTALL_ROOT, home_dir) def module(self, module_name): @@ -46,7 +44,7 @@ class IgnitePath: if self.version.is_dev: module_path = os.path.join("modules", module_name, "target") else: - module_path = os.path.join("libs", "optional", module_name) + module_path = os.path.join("libs", "optional", "ignite-%s" % module_name) return os.path.join(self.home, module_path) diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_persistence.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_persistence.py new file mode 100644 index 0000000..9c79b2e --- /dev/null +++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_persistence.py @@ -0,0 +1,64 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This module contains classes that represent persistent artifacts of tests +""" + +import os + +from ignitetest.services.utils.ignite_config import IgniteLoggerConfig + + +class PersistenceAware(object): + """ + This class contains basic persistence artifacts + """ + # Root directory for persistent output + PERSISTENT_ROOT = "/mnt/service" + STDOUT_STDERR_CAPTURE = os.path.join(PERSISTENT_ROOT, "console.log") + + logs = { + "console_log": { + "path": STDOUT_STDERR_CAPTURE, + "collect_default": True + } + } + + def init_persistent(self, node): + """ + Init persistent directory. + :param node: Service node. + """ + node.account.mkdirs(self.PERSISTENT_ROOT) + + +class IgnitePersistenceAware(PersistenceAware): + """ + This class contains Ignite persistence artifacts + """ + WORK_DIR = os.path.join(PersistenceAware.PERSISTENT_ROOT, "work") + CONFIG_FILE = os.path.join(PersistenceAware.PERSISTENT_ROOT, "ignite-config.xml") + LOG4J_CONFIG_FILE = os.path.join(PersistenceAware.PERSISTENT_ROOT, "ignite-log4j.xml") + + def init_persistent(self, node): + """ + Init persistent directory. + :param node: Ignite service node. + """ + super(IgnitePersistenceAware, self).init_persistent(node) + + logger_config = IgniteLoggerConfig().render(work_dir=self.WORK_DIR) + node.account.create_file(self.LOG4J_CONFIG_FILE, logger_config) diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_spec.py b/modules/ducktests/tests/ignitetest/services/utils/ignite_spec.py new file mode 100644 index 0000000..32468a9 --- /dev/null +++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_spec.py @@ -0,0 +1,213 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This module contains Spec classes that describes config and command line to start Ignite services +""" + +import base64 +import importlib +import json + +from ignitetest.services.utils.ignite_path import IgnitePath +from ignitetest.services.utils.ignite_config import IgniteClientConfig, IgniteServerConfig +from ignitetest.utils.version import DEV_BRANCH, IgniteVersion + +from ignitetest.services.utils.ignite_persistence import IgnitePersistenceAware + + +# pylint: disable=no-else-return +def resolve_spec(service, context, **kwargs): + """ + Resolve Spec classes for IgniteService and IgniteApplicationService + """ + def _resolve_spec(name, default): + if name in context.globals: + fqdn = context.globals[name] + (module, clazz) = fqdn.rsplit('.', 1) + module = importlib.import_module(module) + return getattr(module, clazz) + return default + + def is_impl(impl): + classes = map(lambda s: s.__name__, service.__class__.mro()) + impl_filter = list(filter(lambda c: c == impl, classes)) + return len(impl_filter) > 0 + + if is_impl("IgniteService"): + return _resolve_spec("NodeSpec", ApacheIgniteNodeSpec)(**kwargs) + elif is_impl("IgniteApplicationService"): + return _resolve_spec("AppSpec", ApacheIgniteApplicationSpec)(context=context, **kwargs) + else: + raise Exception("There is no specification for class %s" % type(service)) + + +class IgniteSpec(object): + """ + This class is a basic Spec + """ + def __init__(self, version, project, client_mode, jvm_opts): + if isinstance(version, IgniteVersion): + self.version = version + else: + self.version = IgniteVersion(version) + + self.path = IgnitePath(self.version, project) + self.envs = {} + self.jvm_opts = jvm_opts or [] + self.client_mode = client_mode + + def config(self): + """ + :return: config that service will use to start on a node + """ + if self.client_mode: + return IgniteClientConfig() + return IgniteServerConfig() + + def command(self): + """ + :return: string that represents command to run service on a node + """ + raise NotImplementedError() + + def _envs(self): + """ + :return: line with exports env variables: export A=B; export C=D; + """ + exports = ["export %s=%s" % (key, self.envs[key]) for key in self.envs] + return "; ".join(exports) + ";" + + def _jvm_opts(self): + """ + :return: line with extra JVM params for ignite.sh script: -J-Dparam=value -J-ea + """ + opts = ["-J%s" % o for o in self.jvm_opts] + return " ".join(opts) + + +class IgniteNodeSpec(IgniteSpec, IgnitePersistenceAware): + """ + Spec to run ignite node + """ + def __init__(self, **kwargs): + IgniteSpec.__init__(self, **kwargs) + + def command(self): + cmd = "%s %s %s %s 1>> %s 2>> %s &" % \ + (self._envs(), + self.path.script("ignite.sh"), + self._jvm_opts(), + self.CONFIG_FILE, + self.STDOUT_STDERR_CAPTURE, + self.STDOUT_STDERR_CAPTURE) + + return cmd + + +class IgniteApplicationSpec(IgniteSpec, IgnitePersistenceAware): + """ + Spec to run ignite application + """ + def __init__(self, **kwargs): + super(IgniteApplicationSpec, self).__init__(**kwargs) + self.args = "" + + def _app_args(self): + return ",".join(self.args) + + def command(self): + cmd = "%s %s %s %s 1>> %s 2>> %s &" % \ + (self._envs(), + self.path.script("ignite.sh"), + self._jvm_opts(), + self._app_args(), + self.STDOUT_STDERR_CAPTURE, + self.STDOUT_STDERR_CAPTURE) + + return cmd + + +### + + +class ApacheIgniteNodeSpec(IgniteNodeSpec, IgnitePersistenceAware): + """ + Implementation IgniteNodeSpec for Apache Ignite project + """ + def __init__(self, modules, **kwargs): + super(ApacheIgniteNodeSpec, self).__init__(project="ignite", **kwargs) + + libs = (modules or []) + libs.append("log4j") + libs = list(map(lambda m: self.path.module(m) + "/*", libs)) + + self.envs = { + 'EXCLUDE_TEST_CLASSES': 'true', + 'IGNITE_LOG_DIR': self.PERSISTENT_ROOT, + 'USER_LIBS': ":".join(libs) + } + + self.jvm_opts.extend([ + "-DIGNITE_SUCCESS_FILE=" + self.PERSISTENT_ROOT + "/success_file", + "-Dlog4j.configDebug=true" + ]) + + +class ApacheIgniteApplicationSpec(IgniteApplicationSpec, IgnitePersistenceAware): + """ + Implementation IgniteApplicationSpec for Apache Ignite project + """ + # pylint: disable=too-many-arguments + def __init__(self, context, modules, servicejava_class_name, java_class_name, params, start_ignite, **kwargs): + super(ApacheIgniteApplicationSpec, self).__init__(project="ignite", **kwargs) + self.context = context + + libs = modules or [] + libs.extend(["log4j"]) + + libs = [self.path.module(m) + "/*" for m in libs] + libs.append(IgnitePath(DEV_BRANCH).module("ducktests") + "/*") + libs.extend(self.__jackson()) + + self.envs = { + "MAIN_CLASS": servicejava_class_name, + "EXCLUDE_TEST_CLASSES": "true", + "IGNITE_LOG_DIR": self.PERSISTENT_ROOT, + "USER_LIBS": ":".join(libs) + } + + self.jvm_opts.extend([ + "-DIGNITE_SUCCESS_FILE=" + self.PERSISTENT_ROOT + "/success_file ", + "-Dlog4j.configDebug=true", + "-Xmx1G", + "-ea", + "-DIGNITE_ALLOW_ATOMIC_OPS_IN_TX=false" + ]) + + self.args = [ + str(start_ignite), + java_class_name, + self.CONFIG_FILE, + str(base64.b64encode(json.dumps(params).encode("UTF-8"))) + ] + + def __jackson(self): + if not self.version.is_dev: + aws = self.path.module("aws") + return self.context.cluster.nodes[0].account.ssh_capture( + "ls -d %s/* | grep jackson | tr '\n' ':' | sed 's/.$//'" % aws) + else: + return [] diff --git a/modules/ducktests/tests/ignitetest/tests/add_node_rebalance_test.py b/modules/ducktests/tests/ignitetest/tests/add_node_rebalance_test.py index 28fc4fb..0eebea8 100644 --- a/modules/ducktests/tests/ignitetest/tests/add_node_rebalance_test.py +++ b/modules/ducktests/tests/ignitetest/tests/add_node_rebalance_test.py @@ -22,8 +22,8 @@ from ducktape.mark.resource import cluster from ignitetest.services.ignite import IgniteService from ignitetest.services.ignite_app import IgniteApplicationService -from ignitetest.tests.utils.ignite_test import IgniteTest -from ignitetest.tests.utils.version import DEV_BRANCH, IgniteVersion, LATEST +from ignitetest.utils.ignite_test import IgniteTest +from ignitetest.utils.version import DEV_BRANCH, IgniteVersion, LATEST # pylint: disable=W0223 diff --git a/modules/ducktests/tests/ignitetest/tests/cellular_affinity_test.py b/modules/ducktests/tests/ignitetest/tests/cellular_affinity_test.py index 8087012..718d5da 100644 --- a/modules/ducktests/tests/ignitetest/tests/cellular_affinity_test.py +++ b/modules/ducktests/tests/ignitetest/tests/cellular_affinity_test.py @@ -23,8 +23,8 @@ from jinja2 import Template from ignitetest.services.ignite import IgniteService from ignitetest.services.ignite_app import IgniteApplicationService -from ignitetest.tests.utils.ignite_test import IgniteTest -from ignitetest.tests.utils.version import DEV_BRANCH +from ignitetest.utils.ignite_test import IgniteTest +from ignitetest.utils.version import DEV_BRANCH # pylint: disable=W0223 diff --git a/modules/ducktests/tests/ignitetest/tests/control_utility_test.py b/modules/ducktests/tests/ignitetest/tests/control_utility_test.py index a1b00de..f2c1a4c 100644 --- a/modules/ducktests/tests/ignitetest/tests/control_utility_test.py +++ b/modules/ducktests/tests/ignitetest/tests/control_utility_test.py @@ -23,9 +23,9 @@ from jinja2 import Template from ignitetest.services.ignite import IgniteService from ignitetest.services.utils.control_utility import ControlUtility, ControlUtilityError -from ignitetest.tests.utils import version_if -from ignitetest.tests.utils.ignite_test import IgniteTest -from ignitetest.tests.utils.version import DEV_BRANCH, LATEST_2_8, IgniteVersion, LATEST_2_7, V_2_8_0 +from ignitetest.utils import version_if +from ignitetest.utils.ignite_test import IgniteTest +from ignitetest.utils.version import DEV_BRANCH, LATEST_2_8, IgniteVersion, LATEST_2_7, V_2_8_0 # pylint: disable=W0223 diff --git a/modules/ducktests/tests/ignitetest/tests/discovery_test.py b/modules/ducktests/tests/ignitetest/tests/discovery_test.py index 8d93396..ee94d2c 100644 --- a/modules/ducktests/tests/ignitetest/tests/discovery_test.py +++ b/modules/ducktests/tests/ignitetest/tests/discovery_test.py @@ -29,8 +29,8 @@ from ignitetest.services.ignite import IgniteService from ignitetest.services.utils.ignite_aware import IgniteAwareService from ignitetest.services.utils.time_utils import epoch_mills from ignitetest.services.zk.zookeeper import ZookeeperService -from ignitetest.tests.utils.ignite_test import IgniteTest -from ignitetest.tests.utils.version import DEV_BRANCH, LATEST_2_7 +from ignitetest.utils.ignite_test import IgniteTest +from ignitetest.utils.version import DEV_BRANCH, LATEST_2_7 # pylint: disable=W0223 @@ -165,7 +165,7 @@ class DiscoveryTest(IgniteTest): self.servers = IgniteService( self.test_context, num_nodes=self.NUM_NODES, - modules=["ignite-zookeeper"], + modules=["zookeeper"], properties=properties, version=version) diff --git a/modules/ducktests/tests/ignitetest/tests/pme_free_switch_test.py b/modules/ducktests/tests/ignitetest/tests/pme_free_switch_test.py index 108fa9d..774ab41 100644 --- a/modules/ducktests/tests/ignitetest/tests/pme_free_switch_test.py +++ b/modules/ducktests/tests/ignitetest/tests/pme_free_switch_test.py @@ -25,8 +25,8 @@ from ducktape.mark.resource import cluster from ignitetest.services.ignite import IgniteService from ignitetest.services.ignite_app import IgniteApplicationService from ignitetest.services.utils.control_utility import ControlUtility -from ignitetest.tests.utils.ignite_test import IgniteTest -from ignitetest.tests.utils.version import DEV_BRANCH, LATEST_2_7, V_2_8_0, IgniteVersion +from ignitetest.utils.ignite_test import IgniteTest +from ignitetest.utils.version import DEV_BRANCH, LATEST_2_7, V_2_8_0, IgniteVersion # pylint: disable=W0223 diff --git a/modules/ducktests/tests/ignitetest/tests/smoke_test.py b/modules/ducktests/tests/ignitetest/tests/smoke_test.py index a52f382..a8dfc87 100644 --- a/modules/ducktests/tests/ignitetest/tests/smoke_test.py +++ b/modules/ducktests/tests/ignitetest/tests/smoke_test.py @@ -23,8 +23,8 @@ from ignitetest.services.ignite import IgniteService from ignitetest.services.ignite_app import IgniteApplicationService from ignitetest.services.spark import SparkService from ignitetest.services.zk.zookeeper import ZookeeperService -from ignitetest.tests.utils.ignite_test import IgniteTest -from ignitetest.tests.utils.version import DEV_BRANCH +from ignitetest.utils.ignite_test import IgniteTest +from ignitetest.utils.version import DEV_BRANCH # pylint: disable=W0223 @@ -42,6 +42,18 @@ class SmokeServicesTest(IgniteTest): pass @parametrize(version=str(DEV_BRANCH)) + def test_ignite_start_stop(self, version): + """ + Test that IgniteService correctly start and stop + """ + ignite = IgniteService( + self.test_context, + num_nodes=1, + version=version) + ignite.start() + ignite.stop() + + @parametrize(version=str(DEV_BRANCH)) def test_ignite_app_start_stop(self, version): """ Test that IgniteService and IgniteApplicationService correctly start and stop @@ -61,12 +73,11 @@ class SmokeServicesTest(IgniteTest): app.stop() ignite.stop() - @parametrize(version=str(DEV_BRANCH)) - def test_spark_start_stop(self, version): + def test_spark_start_stop(self): """ Test that SparkService correctly start and stop """ - spark = SparkService(self.test_context, version=version, num_nodes=2) + spark = SparkService(self.test_context, num_nodes=2) spark.start() spark.stop() diff --git a/modules/ducktests/tests/ignitetest/tests/utils/__init__.py b/modules/ducktests/tests/ignitetest/utils/__init__.py similarity index 100% rename from modules/ducktests/tests/ignitetest/tests/utils/__init__.py rename to modules/ducktests/tests/ignitetest/utils/__init__.py diff --git a/modules/ducktests/tests/ignitetest/tests/utils/_mark.py b/modules/ducktests/tests/ignitetest/utils/_mark.py similarity index 97% rename from modules/ducktests/tests/ignitetest/tests/utils/_mark.py rename to modules/ducktests/tests/ignitetest/utils/_mark.py index d5ea8cf..c4b2b35 100644 --- a/modules/ducktests/tests/ignitetest/tests/utils/_mark.py +++ b/modules/ducktests/tests/ignitetest/utils/_mark.py @@ -19,7 +19,7 @@ Module contains useful test decorators. import six from ducktape.mark._mark import Ignore, Mark -from ignitetest.tests.utils.version import IgniteVersion +from ignitetest.utils.version import IgniteVersion class VersionIf(Ignore): diff --git a/modules/ducktests/tests/ignitetest/tests/utils/ignite_test.py b/modules/ducktests/tests/ignitetest/utils/ignite_test.py similarity index 100% rename from modules/ducktests/tests/ignitetest/tests/utils/ignite_test.py rename to modules/ducktests/tests/ignitetest/utils/ignite_test.py diff --git a/modules/ducktests/tests/ignitetest/tests/utils/version.py b/modules/ducktests/tests/ignitetest/utils/version.py similarity index 100% rename from modules/ducktests/tests/ignitetest/tests/utils/version.py rename to modules/ducktests/tests/ignitetest/utils/version.py diff --git a/modules/ducktests/tests/setup.cfg b/modules/ducktests/tests/setup.cfg deleted file mode 100644 index 974d5bb..0000000 --- a/modules/ducktests/tests/setup.cfg +++ /dev/null @@ -1,30 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pytest configuration (can also be defined in tox.ini or pytest.ini file) -# -# This file defines naming convention and root search directory for autodiscovery of -# pytest unit tests for the system test service classes. -# -# To ease possible confusion, 'check' instead of 'test' as a prefix for unit tests, since -# many system test files, classes, and methods have 'test' somewhere in the name -[pytest] -testpaths=unit -python_files=check_*.py -python_classes=Check -python_functions=check_* - -# don't search inside any resources directory for unit tests -norecursedirs = resources diff --git a/modules/ducktests/tests/setup.py b/modules/ducktests/tests/setup.py index e82bdfb..8ad70b0 100644 --- a/modules/ducktests/tests/setup.py +++ b/modules/ducktests/tests/setup.py @@ -14,36 +14,13 @@ # limitations under the License. import re -import sys - from setuptools import find_packages, setup -from setuptools.command.test import test as TestCommand -version = '' + with open('ignitetest/__init__.py', 'r') as fd: version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1) -class PyTest(TestCommand): - user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")] - - def initialize_options(self): - TestCommand.initialize_options(self) - self.pytest_args = [] - - def finalize_options(self): - TestCommand.finalize_options(self) - self.test_args = [] - self.test_suite = True - - def run_tests(self): - # import here, cause outside the eggs aren't loaded - import pytest - print(self.pytest_args) - errno = pytest.main(self.pytest_args) - sys.exit(errno) - - # Note: when changing the version of ducktape, also revise tests/docker/Dockerfile setup(name="ignitetest", version=version, @@ -51,9 +28,7 @@ setup(name="ignitetest", author="Apache Ignite", platforms=["any"], license="apache2.0", - packages=find_packages(), + packages=find_packages(exclude=["ignitetest.tests", "ignitetest.tests.*"]), include_package_data=True, - install_requires=["ducktape==0.7.8", "requests==2.20.0"], - tests_require=["pytest", "mock", "monotonic"], - cmdclass={'test': PyTest} + install_requires=["ducktape==0.7.8", "requests==2.22.0", "monotonic==1.5"] )