You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ni...@apache.org on 2020/05/25 08:08:23 UTC
[ignite] 08/24: ignite-ducktape: WIP.
This is an automated email from the ASF dual-hosted git repository.
nizhikov pushed a commit to branch ignite-ducktape
in repository https://gitbox.apache.org/repos/asf/ignite.git
commit f43ac878c357c1540d19302c5ceef47b7cdc7d85
Author: Nikolay Izhikov <ni...@apache.org>
AuthorDate: Fri Apr 10 10:17:08 2020 +0300
ignite-ducktape: WIP.
---
tests/docker/Dockerfile | 6 +-
tests/docker/build/cluster.json | 116 +--------------------
tests/docker/build/node_hosts | 6 +-
tests/docker/ducker-ignite | 4 +-
tests/docker/run_tests.sh | 2 +-
.../ignitetest/{tests => ignite_utils}/__init__.py | 0
.../ignite_config.py} | 33 +++---
tests/ignitetest/ignite_utils/ignite_path.py | 109 +++++++++++++++++++
tests/ignitetest/services/ignite/ignite.py | 21 ++--
tests/ignitetest/{tests => suites}/__init__.py | 0
.../add_node_rebalance_test.py | 14 ++-
11 files changed, 163 insertions(+), 148 deletions(-)
diff --git a/tests/docker/Dockerfile b/tests/docker/Dockerfile
index eb53d8c..390d849 100644
--- a/tests/docker/Dockerfile
+++ b/tests/docker/Dockerfile
@@ -45,11 +45,13 @@ RUN echo 'PermitUserEnvironment yes' >> /etc/ssh/sshd_config
# Install binary test dependencies.
# we use the same versions as in vagrant/base.sh
ARG IGNITE_MIRROR="https://apache-mirror.rbc.ru/pub/apache/"
+ARG IGNITE_NAME="ignite-2.8.0"
ARG RELEASE_NAME="apache-ignite-2.8.0-bin"
RUN chmod a+wr /opt
RUN curl -s "$IGNITE_MIRROR/ignite/2.8.0/$RELEASE_NAME.zip" > /opt/$RELEASE_NAME.zip
-RUN cd /opt && unzip $RELEASE_NAME.zip
-RUN chmod a+wr /opt/$RELEASE_NAME -R
+RUN cd /opt && unzip $RELEASE_NAME.zip && rm $RELEASE_NAME.zip
+RUN mv /opt/$RELEASE_NAME /opt/$IGNITE_NAME
+RUN chmod a+wr /opt/$IGNITE_NAME -R
# The version of Kibosh to use for testing.
# If you update this, also update vagrant/base.sh
diff --git a/tests/docker/build/cluster.json b/tests/docker/build/cluster.json
index e0de330..6e5e46e 100644
--- a/tests/docker/build/cluster.json
+++ b/tests/docker/build/cluster.json
@@ -37,10 +37,8 @@
"port": 22,
"user": "ducker"
}
- }
- ]
-}
-
+ },
+ {
"externally_routable_ip": "ducker04",
"ssh_config": {
"host": "ducker04",
@@ -50,116 +48,6 @@
"port": 22,
"user": "ducker"
}
- },
- {
- "externally_routable_ip": "ducker05",
- "ssh_config": {
- "host": "ducker05",
- "hostname": "ducker05",
- "identityfile": "/home/ducker/.ssh/id_rsa",
- "password": "",
- "port": 22,
- "user": "ducker"
- }
- },
- {
- "externally_routable_ip": "ducker06",
- "ssh_config": {
- "host": "ducker06",
- "hostname": "ducker06",
- "identityfile": "/home/ducker/.ssh/id_rsa",
- "password": "",
- "port": 22,
- "user": "ducker"
- }
- },
- {
- "externally_routable_ip": "ducker07",
- "ssh_config": {
- "host": "ducker07",
- "hostname": "ducker07",
- "identityfile": "/home/ducker/.ssh/id_rsa",
- "password": "",
- "port": 22,
- "user": "ducker"
- }
- },
- {
- "externally_routable_ip": "ducker08",
- "ssh_config": {
- "host": "ducker08",
- "hostname": "ducker08",
- "identityfile": "/home/ducker/.ssh/id_rsa",
- "password": "",
- "port": 22,
- "user": "ducker"
- }
- },
- {
- "externally_routable_ip": "ducker09",
- "ssh_config": {
- "host": "ducker09",
- "hostname": "ducker09",
- "identityfile": "/home/ducker/.ssh/id_rsa",
- "password": "",
- "port": 22,
- "user": "ducker"
- }
- },
- {
- "externally_routable_ip": "ducker10",
- "ssh_config": {
- "host": "ducker10",
- "hostname": "ducker10",
- "identityfile": "/home/ducker/.ssh/id_rsa",
- "password": "",
- "port": 22,
- "user": "ducker"
- }
- },
- {
- "externally_routable_ip": "ducker11",
- "ssh_config": {
- "host": "ducker11",
- "hostname": "ducker11",
- "identityfile": "/home/ducker/.ssh/id_rsa",
- "password": "",
- "port": 22,
- "user": "ducker"
- }
- },
- {
- "externally_routable_ip": "ducker12",
- "ssh_config": {
- "host": "ducker12",
- "hostname": "ducker12",
- "identityfile": "/home/ducker/.ssh/id_rsa",
- "password": "",
- "port": 22,
- "user": "ducker"
- }
- },
- {
- "externally_routable_ip": "ducker13",
- "ssh_config": {
- "host": "ducker13",
- "hostname": "ducker13",
- "identityfile": "/home/ducker/.ssh/id_rsa",
- "password": "",
- "port": 22,
- "user": "ducker"
- }
- },
- {
- "externally_routable_ip": "ducker14",
- "ssh_config": {
- "host": "ducker14",
- "hostname": "ducker14",
- "identityfile": "/home/ducker/.ssh/id_rsa",
- "password": "",
- "port": 22,
- "user": "ducker"
- }
}
]
}
diff --git a/tests/docker/build/node_hosts b/tests/docker/build/node_hosts
index 291afdf..9da72b8 100644
--- a/tests/docker/build/node_hosts
+++ b/tests/docker/build/node_hosts
@@ -1,6 +1,6 @@
-172.18.0.2 ducker01
-172.18.0.3 ducker02
-172.18.0.4 ducker03
+172.26.0.2 ducker01
+172.26.0.3 ducker02
+172.26.0.4 ducker03
172.26.0.5 ducker04
172.26.0.6 ducker05
172.26.0.7 ducker06
diff --git a/tests/docker/ducker-ignite b/tests/docker/ducker-ignite
index 2a90c04..c2f2cf8 100755
--- a/tests/docker/ducker-ignite
+++ b/tests/docker/ducker-ignite
@@ -39,7 +39,7 @@ docker_build_memory_limit="3200m"
docker_run_memory_limit="2000m"
# The default number of cluster nodes to bring up if a number is not specified.
-default_num_nodes=14
+default_num_nodes=4
# The default OpenJDK base image.
default_jdk="openjdk:8"
@@ -369,6 +369,8 @@ attempting to start new ones."
generate_cluster_json_file() {
local num_nodes="${1}"
local path="${2}"
+ rm ${path}
+ touch ${path}
exec 3<> "${path}"
cat<<EOF >&3
{
diff --git a/tests/docker/run_tests.sh b/tests/docker/run_tests.sh
index 96064bf..653b46a 100755
--- a/tests/docker/run_tests.sh
+++ b/tests/docker/run_tests.sh
@@ -16,7 +16,7 @@
# limitations under the License.
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-IGNITE_NUM_CONTAINERS=${IGNITE_NUM_CONTAINERS:-3}
+IGNITE_NUM_CONTAINERS=${IGNITE_NUM_CONTAINERS:-4}
TC_PATHS=${TC_PATHS:-./ignitetest/}
die() {
diff --git a/tests/ignitetest/tests/__init__.py b/tests/ignitetest/ignite_utils/__init__.py
similarity index 100%
copy from tests/ignitetest/tests/__init__.py
copy to tests/ignitetest/ignite_utils/__init__.py
diff --git a/tests/ignitetest/tests/ignite_test.py b/tests/ignitetest/ignite_utils/ignite_config.py
similarity index 52%
rename from tests/ignitetest/tests/ignite_test.py
rename to tests/ignitetest/ignite_utils/ignite_config.py
index c30a8fd..416391a 100644
--- a/tests/ignitetest/tests/ignite_test.py
+++ b/tests/ignitetest/ignite_utils/ignite_config.py
@@ -13,23 +13,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from ducktape.tests.test import Test
+"""
+This module renders Ignite config and all related artifacts
+"""
-from ignitetest.services.ignite import IgniteService
+class IgniteConfig:
+ def __init__(self, project="ignite"):
+ self.project = project
+ def render(self, work_dir):
+ return """<?xml version="1.0" encoding="UTF-8"?>
-class IgniteTest(Test):
- """
- Helper class that manages setting up a Ignite cluster. Use this if the
- default settings for Ignite are sufficient for your test; any customization
- needs to be done manually. Your run() method should call tearDown and
- setUp. The Ignite service are available as the fields IgniteTest.ignite.
- """
- def __init__(self, test_context):
- super(IgniteTest, self).__init__(test_context)
-
- self.ignite = IgniteService(test_context, self.num_brokers)
-
- def setUp(self):
- self.ignite.start()
\ No newline at end of file
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd">
+ <bean class="org.apache.ignite.configuration.IgniteConfiguration">
+ <property name="workDirectory" value="{0}" />
+ </bean>
+</beans>
+ """.format(work_dir)
diff --git a/tests/ignitetest/ignite_utils/ignite_path.py b/tests/ignitetest/ignite_utils/ignite_path.py
new file mode 100644
index 0000000..6b733d6
--- /dev/null
+++ b/tests/ignitetest/ignite_utils/ignite_path.py
@@ -0,0 +1,109 @@
+# 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.
+
+import importlib
+import os
+
+from ignitetest.version import get_version, IgniteVersion, DEV_BRANCH
+
+
+"""This module serves a few purposes:
+
+First, it gathers information about path layout in a single place, and second, it
+makes the layout of the Ignite installation pluggable, so that users are not forced
+to use the layout assumed in the IgnitePathResolver class.
+"""
+
+SCRATCH_ROOT = "/mnt"
+IGNITE_INSTALL_ROOT = "/opt"
+
+def create_path_resolver(context, project="ignite"):
+ """Factory for generating a path resolver class
+
+ This will first check for a fully qualified path resolver classname in context.globals.
+
+ If present, construct a new instance, else default to IgniteSystemTestPathResolver
+ """
+ assert project is not None
+
+ resolver_fully_qualified_classname = "ignitetest.ignite_utils.ignite_path.IgniteSystemTestPathResolver"
+ # Using the fully qualified classname, import the resolver class
+ (module_name, resolver_class_name) = resolver_fully_qualified_classname.rsplit('.', 1)
+ cluster_mod = importlib.import_module(module_name)
+ path_resolver_class = getattr(cluster_mod, resolver_class_name)
+ path_resolver = path_resolver_class(context, project)
+
+ return path_resolver
+
+
+class IgnitePathResolverMixin(object):
+ """Mixin to automatically provide pluggable path resolution functionality to any class using it.
+
+ Keep life simple, and don't add a constructor to this class:
+ Since use of a mixin entails multiple inheritence, it is *much* simpler to reason about the interaction of this
+ class with subclasses if we don't have to worry about method resolution order, constructor signatures etc.
+ """
+
+ @property
+ def path(self):
+ if not hasattr(self, "_path"):
+ setattr(self, "_path", create_path_resolver(self.context, "ignite"))
+ if hasattr(self.context, "logger") and self.context.logger is not None:
+ self.context.logger.debug("Using path resolver %s" % self._path.__class__.__name__)
+
+ return self._path
+
+
+class IgniteSystemTestPathResolver(object):
+ """Path resolver for Ignite system tests which assumes the following layout:
+
+ /opt/ignite-dev # Current version of Ignite under test
+ /opt/ignite-2.7.6 # Example of an older version of Ignite installed from tarball
+ /opt/ignite-<version> # Other previous versions of Ignite
+ ...
+ """
+ def __init__(self, context, project="ignite"):
+ self.context = context
+ self.project = project
+
+ def home(self, node_or_version=DEV_BRANCH, project=None):
+ version = self._version(node_or_version)
+ home_dir = project or self.project
+ if version is not None:
+ home_dir += "-%s" % str(version)
+
+ return os.path.join(IGNITE_INSTALL_ROOT, home_dir)
+
+ def bin(self, node_or_version=DEV_BRANCH, project=None):
+ version = self._version(node_or_version)
+ return os.path.join(self.home(version, project=project), "bin")
+
+ def script(self, script_name, node_or_version=DEV_BRANCH, project=None):
+ version = self._version(node_or_version)
+ return os.path.join(self.bin(version, project=project), script_name)
+
+ def jar(self, jar_name, node_or_version=DEV_BRANCH, project=None):
+ version = self._version(node_or_version)
+ return os.path.join(self.home(version, project=project), JARS[str(version)][jar_name])
+
+ def scratch_space(self, service_instance):
+ return os.path.join(SCRATCH_ROOT, service_instance.service_id)
+
+ def _version(self, node_or_version):
+ if isinstance(node_or_version, IgniteVersion):
+ return node_or_version
+ else:
+ return get_version(node_or_version)
+
diff --git a/tests/ignitetest/services/ignite/ignite.py b/tests/ignitetest/services/ignite/ignite.py
index 7db90ec..af60986 100644
--- a/tests/ignitetest/services/ignite/ignite.py
+++ b/tests/ignitetest/services/ignite/ignite.py
@@ -20,19 +20,19 @@ from ducktape.services.service import Service
from ducktape.utils.util import wait_until
from ducktape.cluster.remoteaccount import RemoteCommandError
-from ignitetest.version import DEV_VERSION
+from ignitetest.ignite_utils.ignite_config import IgniteConfig
+from ignitetest.ignite_utils.ignite_path import IgnitePathResolverMixin
+from ignitetest.version import DEV_BRANCH
-class IgniteService(Service):
+class IgniteService(IgnitePathResolverMixin, Service):
PERSISTENT_ROOT = "/mnt/ignite"
+ WORK_DIR = os.path.join(PERSISTENT_ROOT, "work")
CONFIG_FILE = os.path.join(PERSISTENT_ROOT, "ignite-config.xml")
HEAP_DUMP_FILE = os.path.join(PERSISTENT_ROOT, "ignite-heap.bin")
STDOUT_STDERR_CAPTURE = os.path.join(PERSISTENT_ROOT, "console.log")
- def __init__(self,
- context,
- num_nodes,
- version=DEV_VERSION):
+ def __init__(self, context, num_nodes=3, version=DEV_BRANCH):
"""
:param context: test context
:param num_nodes: number of Ignite nodes.
@@ -40,6 +40,7 @@ class IgniteService(Service):
Service.__init__(self, context, num_nodes)
self.log_level = "DEBUG"
+ self.config = IgniteConfig()
for node in self.nodes:
node.version = version
@@ -64,14 +65,15 @@ class IgniteService(Service):
IgniteService.STDOUT_STDERR_CAPTURE)
return cmd
- def start_node(self, node, timeout_sec=60):
+ def start_node(self, node, timeout_sec=180):
node.account.mkdirs(IgniteService.PERSISTENT_ROOT)
+ node.account.create_file(IgniteService.CONFIG_FILE, self.config.render(IgniteService.WORK_DIR))
cmd = self.start_cmd(node)
self.logger.debug("Attempting to start IgniteService on %s with command: %s" % (str(node.account), cmd))
with node.account.monitor_log(IgniteService.STDOUT_STDERR_CAPTURE) as monitor:
node.account.ssh(cmd)
- monitor.wait_until("Ignite\s*Server.*started", timeout_sec=timeout_sec, backoff_sec=.25,
+ monitor.wait_until("Topology snapshot", timeout_sec=timeout_sec, backoff_sec=.25,
err_msg="Ignite server didn't finish startup in %d seconds" % timeout_sec)
if len(self.pids(node)) == 0:
@@ -111,3 +113,6 @@ class IgniteService(Service):
node.account.kill_java_processes(self.java_class_name(),
clean_shutdown=False, allow_fail=True)
node.account.ssh("sudo rm -rf -- %s" % IgniteService.PERSISTENT_ROOT, allow_fail=False)
+
+ def java_class_name(self):
+ return "org.apache.ignite.startup.cmdline.CommandLineStartup"
diff --git a/tests/ignitetest/tests/__init__.py b/tests/ignitetest/suites/__init__.py
similarity index 100%
rename from tests/ignitetest/tests/__init__.py
rename to tests/ignitetest/suites/__init__.py
diff --git a/tests/ignitetest/rebalance/add_node_rebalance_test.py b/tests/ignitetest/suites/add_node_rebalance_test.py
similarity index 78%
rename from tests/ignitetest/rebalance/add_node_rebalance_test.py
rename to tests/ignitetest/suites/add_node_rebalance_test.py
index 501bf42..6b354ce 100644
--- a/tests/ignitetest/rebalance/add_node_rebalance_test.py
+++ b/tests/ignitetest/suites/add_node_rebalance_test.py
@@ -13,15 +13,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from ignitetest.tests.ignite_test import IgniteTest
+from ducktape.tests.test import Test
+from ignitetest.services.ignite.ignite import IgniteService
-class AddNodeRebalanceTest(IgniteTest):
+
+class AddNodeRebalanceTest(Test):
"""
Test performs rebalance tests.
"""
def __init__(self, test_context):
- super(IgniteTest, self).__init__(test_context)
+ super(AddNodeRebalanceTest, self).__init__(test_context=test_context)
+ self.ignite = IgniteService(test_context)
+
+ def setUp(self):
+ self.ignite.start()
def teardown(self):
self.ignite.stop()
@@ -34,5 +40,7 @@ class AddNodeRebalanceTest(IgniteTest):
* Start one more node.
* Await for rebalance to finish.
"""
+ self.logger.info("Start add node rebalance test.")
+
for node in self.ignite.nodes:
node.account.ssh("touch /opt/hello-from-test.txt")