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")