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:29 UTC

[ignite] 14/24: ignite-ducktape: IgniteClientApplication added and working.

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 f8b176a272d1cbdbadb3c6b66ae301b91615f0bc
Author: Nikolay Izhikov <ni...@apache.org>
AuthorDate: Wed May 6 16:05:52 2020 +0300

    ignite-ducktape: IgniteClientApplication added and working.
---
 .../ignite/internal/test/IgniteApplication.java    |  52 ++++++++
 tests/docker/build/cluster.json                    |  11 ++
 tests/docker/build/node_hosts                      |   8 +-
 tests/docker/run_tests.sh                          |   2 +-
 tests/ignitetest/ignite_utils/ignite_config.py     |   2 +-
 tests/ignitetest/ignite_utils/ignite_path.py       |  71 ++---------
 tests/ignitetest/services/ignite/ignite.py         |  10 +-
 tests/ignitetest/services/ignite_client_app.py     | 137 +++++++++------------
 tests/ignitetest/suites/add_node_rebalance_test.py |   6 +-
 9 files changed, 143 insertions(+), 156 deletions(-)

diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/test/IgniteApplication.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/test/IgniteApplication.java
new file mode 100644
index 0000000..dffa891
--- /dev/null
+++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/test/IgniteApplication.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.test;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgnitionEx;
+import org.apache.ignite.internal.processors.resource.GridSpringResourceContext;
+import org.apache.ignite.lang.IgniteBiTuple;
+
+public class IgniteApplication {
+    public static final String CONFIG_PATH = "/mnt/client_app/ignite-config.xml";
+
+    public static void main(String[] args) throws IgniteCheckedException {
+        IgniteBiTuple<IgniteConfiguration, GridSpringResourceContext> cfgs = IgnitionEx.loadConfiguration(CONFIG_PATH);
+        IgniteConfiguration cfg = cfgs.get1();
+
+        cfg.setClientMode(true);
+
+        System.out.println("Starting Ignite client...");
+
+        try (Ignite ign = Ignition.start(cfg)) {
+            System.out.println("Creating cache...");
+
+            IgniteCache<Integer, Integer> cache = ign.createCache("test-cache");
+
+            for (int i = 0; i < 1000; i++) {
+                cache.put(i, i);
+            }
+
+            System.out.println("Ignite Client Finish.");
+        }
+    }
+}
diff --git a/tests/docker/build/cluster.json b/tests/docker/build/cluster.json
index 6e5e46e..297d9c4 100644
--- a/tests/docker/build/cluster.json
+++ b/tests/docker/build/cluster.json
@@ -48,6 +48,17 @@
         "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"
+      }
     }
   ]
 }
diff --git a/tests/docker/build/node_hosts b/tests/docker/build/node_hosts
index e1183dd..9da72b8 100644
--- a/tests/docker/build/node_hosts
+++ b/tests/docker/build/node_hosts
@@ -1,7 +1,7 @@
-172.22.0.2	ducker01
-172.22.0.3	ducker02
-172.22.0.4	ducker03
-172.22.0.5	ducker04
+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
 172.26.0.8	ducker07
diff --git a/tests/docker/run_tests.sh b/tests/docker/run_tests.sh
index 653b46a..9de2317 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:-4}
+IGNITE_NUM_CONTAINERS=${IGNITE_NUM_CONTAINERS:-5}
 TC_PATHS=${TC_PATHS:-./ignitetest/}
 
 die() {
diff --git a/tests/ignitetest/ignite_utils/ignite_config.py b/tests/ignitetest/ignite_utils/ignite_config.py
index b0008a6..534df42 100644
--- a/tests/ignitetest/ignite_utils/ignite_config.py
+++ b/tests/ignitetest/ignite_utils/ignite_config.py
@@ -49,7 +49,7 @@ class IgniteConfig:
     <appender name="CONSOLE_ERR" class="org.apache.log4j.ConsoleAppender">
         <param name="Target" value="System.err"/>
 
-        <param name="Threshold" value="ERROR"/>
+        <param name="Threshold" value="INFO"/>
 
         <layout class="org.apache.log4j.PatternLayout">
             <param name="ConversionPattern" value="[%d{{ISO8601}}][%-5p][%t][%c{{1}}] %m%n"/>
diff --git a/tests/ignitetest/ignite_utils/ignite_path.py b/tests/ignitetest/ignite_utils/ignite_path.py
index 6b733d6..6307175 100644
--- a/tests/ignitetest/ignite_utils/ignite_path.py
+++ b/tests/ignitetest/ignite_utils/ignite_path.py
@@ -13,60 +13,19 @@
 # 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.
+"""
+This module provides Ignite path methods
 """
 
-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 IgnitePath:
+    SCRATCH_ROOT = "/mnt"
+    IGNITE_INSTALL_ROOT = "/opt"
 
-class IgniteSystemTestPathResolver(object):
     """Path resolver for Ignite system tests which assumes the following layout:
 
         /opt/ignite-dev          # Current version of Ignite under test
@@ -74,8 +33,8 @@ class IgniteSystemTestPathResolver(object):
         /opt/ignite-<version>    # Other previous versions of Ignite
         ...
     """
-    def __init__(self, context, project="ignite"):
-        self.context = context
+
+    def __init__(self, project="ignite"):
         self.project = project
 
     def home(self, node_or_version=DEV_BRANCH, project=None):
@@ -84,26 +43,14 @@ class IgniteSystemTestPathResolver(object):
         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")
+        return os.path.join(IgnitePath.IGNITE_INSTALL_ROOT, home_dir)
 
     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)
+        return os.path.join(self.home(version, project=project), "bin", script_name)
 
     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 1dcdbdc..f8f208a 100644
--- a/tests/ignitetest/services/ignite/ignite.py
+++ b/tests/ignitetest/services/ignite/ignite.py
@@ -18,14 +18,13 @@ import signal
 
 from ducktape.services.service import Service
 from ducktape.utils.util import wait_until
-from ducktape.cluster.remoteaccount import RemoteCommandError
 
 from ignitetest.ignite_utils.ignite_config import IgniteConfig
-from ignitetest.ignite_utils.ignite_path import IgnitePathResolverMixin
+from ignitetest.ignite_utils.ignite_path import IgnitePath
 from ignitetest.version import DEV_BRANCH
 
 
-class IgniteService(IgnitePathResolverMixin, Service):
+class IgniteService(Service):
     PERSISTENT_ROOT = "/mnt/ignite"
     WORK_DIR = os.path.join(PERSISTENT_ROOT, "work")
     CONFIG_FILE = os.path.join(PERSISTENT_ROOT, "ignite-config.xml")
@@ -43,7 +42,7 @@ class IgniteService(IgnitePathResolverMixin, Service):
             "collect_default": False}
     }
 
-    def __init__(self, context, num_nodes=3, version=DEV_BRANCH):
+    def __init__(self, context, num_nodes=1, version=DEV_BRANCH):
         """
         :param context: test context
         :param num_nodes: number of Ignite nodes.
@@ -52,6 +51,7 @@ class IgniteService(IgnitePathResolverMixin, Service):
 
         self.log_level = "DEBUG"
         self.config = IgniteConfig()
+        self.path = IgnitePath()
 
         for node in self.nodes:
             node.version = version
@@ -85,7 +85,7 @@ class IgniteService(IgnitePathResolverMixin, Service):
         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("Topology snapshot", timeout_sec=timeout_sec, backoff_sec=.25,
+            monitor.wait_until("Topology snapshot", timeout_sec=timeout_sec, backoff_sec=5,
                                err_msg="Ignite server didn't finish startup in %d seconds" % timeout_sec)
 
         if len(self.pids(node)) == 0:
diff --git a/tests/ignitetest/services/ignite_client_app.py b/tests/ignitetest/services/ignite_client_app.py
index 4462e71..82a661a 100644
--- a/tests/ignitetest/services/ignite_client_app.py
+++ b/tests/ignitetest/services/ignite_client_app.py
@@ -15,35 +15,29 @@
 
 import os
 
-from ducktape.cluster.remoteaccount import RemoteCommandError
 from ducktape.services.background_thread import BackgroundThreadService
 
-from ignitetest.directory_layout.ignite_path import IgnitePathResolverMixin
-
+from ignitetest.ignite_utils.ignite_config import IgniteConfig
+from ignitetest.ignite_utils.ignite_path import IgnitePath
 from ignitetest.version import DEV_BRANCH
 
 """
-The console consumer is a tool that reads data from Kafka and outputs it to standard output.
+The Ignite client application is a main class that implements custom logic.
+First CMD param is an absolute path to the Ignite config file.
 """
 
 
-class IgniteClientApp(IgnitePathResolverMixin, BackgroundThreadService):
+class IgniteClientApp(BackgroundThreadService):
     # Root directory for persistent output
     PERSISTENT_ROOT = "/mnt/client_app"
-    STDOUT_CAPTURE = os.path.join(PERSISTENT_ROOT, "client_app.stdout")
-    STDERR_CAPTURE = os.path.join(PERSISTENT_ROOT, "client_app.stderr")
-    LOG_DIR = os.path.join(PERSISTENT_ROOT, "logs")
-    LOG_FILE = os.path.join(LOG_DIR, "client_app.log")
+    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 = {
-        "consumer_stdout": {
-            "path": STDOUT_CAPTURE,
-            "collect_default": False},
-        "consumer_stderr": {
-            "path": STDERR_CAPTURE,
-            "collect_default": False},
-        "consumer_log": {
-            "path": LOG_FILE,
+        "console_log": {
+            "path": STDOUT_STDERR_CAPTURE,
             "collect_default": True}
     }
 
@@ -54,66 +48,30 @@ class IgniteClientApp(IgnitePathResolverMixin, BackgroundThreadService):
         """
         BackgroundThreadService.__init__(self, context, num_nodes)
 
+        self.stop_timeout_sec = 10
+        self.log_level = "DEBUG"
+        self.config = IgniteConfig()
+        self.path = IgnitePath()
+
         for node in self.nodes:
             node.version = version
 
     def start_cmd(self, node):
         """Return the start command appropriate for the given node."""
-        args = self.args.copy()
-        args['stdout'] = IgniteClientApp.STDOUT_CAPTURE
-        args['stderr'] = IgniteClientApp.STDERR_CAPTURE
-        args['log_dir'] = IgniteClientApp.LOG_DIR
-        args['config_file'] = IgniteClientApp.CONFIG_FILE
-        args['stdout'] = IgniteClientApp.STDOUT_CAPTURE
-        args['console_consumer'] = self.path.script("kafka-console-consumer.sh", node)
-
-        cmd = "%(console_consumer)s " \
-              "--topic %(topic)s " \
-              "--consumer.config %(config_file)s " % args
-
-        cmd += " 2>> %(stderr)s | tee -a %(stdout)s &" % args
-        return cmd
-
-    def pids(self, node):
-        return node.account.java_pids(self.java_class_name())
 
-    def alive(self, node):
-        return len(self.pids(node)) > 0
-
-    def _worker(self, idx, node):
-        node.account.ssh("mkdir -p %s" % IgniteClientApp.PERSISTENT_ROOT, allow_fail=False)
-
-        self.security_config = self.kafka.security_config.client_config(node=node,
-                                                                        jaas_override_variables=self.jaas_override_variables)
-        self.security_config.setup_node(node)
-
-        if self.client_prop_file_override:
-            prop_file = self.client_prop_file_override
-        else:
-            prop_file = self.prop_file(node)
-
-        # TODO: create ignite config: node.account.create_file(IgniteClientApp.CONFIG_FILE, prop_file)
-
-        # Run and capture output
-        cmd = self.start_cmd(node)
-        self.logger.debug("Console consumer %d command: %s", idx, cmd)
-
-        consumer_output = node.account.ssh_capture(cmd, allow_fail=False)
-
-        # TODO: create ignite version of waiting for a app finish
-        for line in consumer_output:
-            msg = line.strip()
-            if msg == "shutdown_complete":
-                # Note that we can only rely on shutdown_complete message if running 0.10.0 or greater
-                if node in self.clean_shutdown_nodes:
-                    raise Exception(
-                        "Unexpected shutdown event from consumer, already shutdown. Consumer index: %d" % idx)
-                self.clean_shutdown_nodes.add(node)
-            else:
-                if self.message_validator is not None:
-                    msg = self.message_validator(msg)
-                if msg is not None:
-                    self.messages_consumed[idx].append(msg)
+        jvm_opts = "-J-DIGNITE_SUCCESS_FILE=" + IgniteClientApp.PERSISTENT_ROOT + "/success_file "
+        jvm_opts += "-J-Dlog4j.configDebug=true"
+
+        cmd = "export MAIN_CLASS={main_class}; ".format(main_class=self.java_class_name())
+        cmd += "export EXCLUDE_TEST_CLASSES=true; "
+        cmd += "export IGNITE_LOG_DIR=" + IgniteClientApp.PERSISTENT_ROOT + "; "
+        cmd += "%s %s %s 1>> %s 2>> %s " % \
+               (self.path.script("ignite.sh", node),
+                jvm_opts,
+                IgniteClientApp.CONFIG_FILE,
+                IgniteClientApp.STDOUT_STDERR_CAPTURE,
+                IgniteClientApp.STDOUT_STDERR_CAPTURE)
+        return cmd
 
     def start_node(self, node):
         BackgroundThreadService.start_node(self, node)
@@ -121,7 +79,8 @@ class IgniteClientApp(IgnitePathResolverMixin, BackgroundThreadService):
     def stop_node(self, node):
         self.logger.info("%s Stopping node %s" % (self.__class__.__name__, str(node.account)))
         node.account.kill_java_processes(self.java_class_name(),
-                                         clean_shutdown=True, allow_fail=True)
+                                         clean_shutdown=True,
+                                         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" % \
@@ -131,16 +90,30 @@ class IgniteClientApp(IgnitePathResolverMixin, BackgroundThreadService):
         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.java_class_name(), clean_shutdown=False, allow_fail=True)
+
+        node.account.kill_java_processes(self.java_class_name(),
+                                         clean_shutdown=False,
+                                         allow_fail=True)
+
         node.account.ssh("rm -rf %s" % IgniteClientApp.PERSISTENT_ROOT, allow_fail=False)
-        self.security_config.clean_node(node)
+
+    def pids(self, node):
+        return node.account.java_pids(self.java_class_name())
+
+    def alive(self, node):
+        return len(self.pids(node)) > 0
+
+    def _worker(self, idx, node):
+        node.account.mkdirs(IgniteClientApp.PERSISTENT_ROOT)
+        node.account.create_file(IgniteClientApp.CONFIG_FILE,
+                                 self.config.render(IgniteClientApp.PERSISTENT_ROOT, IgniteClientApp.WORK_DIR))
+        node.account.create_file(IgniteClientApp.LOG4J_CONFIG_FILE,
+                                 self.config.render_log4j(IgniteClientApp.WORK_DIR))
+
+        # Just run application.
+        cmd = self.start_cmd(node)
+        self.logger.info("Ignite client application command: %s", cmd)
+        node.account.ssh(cmd, allow_fail=False)
 
     def java_class_name(self):
-        return "org.apache.ignite.test.IgniteApplication"
-
-    def has_log_message(self, node, message):
-        try:
-            node.account.ssh("grep '%s' %s" % (message, IgniteClientApp.LOG_FILE))
-        except RemoteCommandError:
-            return False
-        return True
+        return "org.apache.ignite.internal.test.IgniteApplication"
diff --git a/tests/ignitetest/suites/add_node_rebalance_test.py b/tests/ignitetest/suites/add_node_rebalance_test.py
index 6b354ce..24ca151 100644
--- a/tests/ignitetest/suites/add_node_rebalance_test.py
+++ b/tests/ignitetest/suites/add_node_rebalance_test.py
@@ -16,6 +16,7 @@
 from ducktape.tests.test import Test
 
 from ignitetest.services.ignite.ignite import IgniteService
+from ignitetest.services.ignite_client_app import IgniteClientApp
 
 
 class AddNodeRebalanceTest(Test):
@@ -41,6 +42,9 @@ class AddNodeRebalanceTest(Test):
             * Await for rebalance to finish.
         """
         self.logger.info("Start add node rebalance test.")
+        self.client = IgniteClientApp(self.test_context)
+        self.client.start()
+        self.client.wait()
 
         for node in self.ignite.nodes:
-            node.account.ssh("touch /opt/hello-from-test.txt")
+            node.account.ssh("touch /opt/hello-from-test-after-client.txt")