You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by is...@apache.org on 2022/09/09 20:00:47 UTC

[ignite-3] 06/17: IGNITE-17424 Start and stop Ignite node

This is an automated email from the ASF dual-hosted git repository.

isapego pushed a commit to branch ignite-17424
in repository https://gitbox.apache.org/repos/asf/ignite-3.git

commit 61c64082a870d209db6b3648961b143eb61befff
Author: Igor Sapego <is...@apache.org>
AuthorDate: Mon Aug 29 23:02:00 2022 +0400

    IGNITE-17424 Start and stop Ignite node
---
 .../cpp/client-test/src/ignite_client_test.cpp     |  16 +-
 modules/platforms/cpp/test-common/CMakeLists.txt   |   1 +
 .../cpp/test-common/include/ignite_node.h          |  52 ++++---
 .../include/{ignite_node.h => process.h}           |  46 +++---
 .../platforms/cpp/test-common/include/test_utils.h |  19 ---
 .../platforms/cpp/test-common/src/ignite_node.cpp  |  51 ++++--
 modules/platforms/cpp/test-common/src/process.cpp  | 173 +++++++++++++++++++++
 .../platforms/cpp/test-common/src/test_utils.cpp   |  18 ---
 8 files changed, 263 insertions(+), 113 deletions(-)

diff --git a/modules/platforms/cpp/client-test/src/ignite_client_test.cpp b/modules/platforms/cpp/client-test/src/ignite_client_test.cpp
index 2413b1839b..7fc40ed724 100644
--- a/modules/platforms/cpp/client-test/src/ignite_client_test.cpp
+++ b/modules/platforms/cpp/client-test/src/ignite_client_test.cpp
@@ -42,25 +42,11 @@ protected:
 
 TEST_F(ClientTest, TestTest)
 {
-    std::cout << "Hello" << std::endl;
-
     ignite::IgniteNode node;
 
     node.start();
 
-    for (int i = 0; i < 20; ++i)
-    {
-        std::cout << node.getOutput();
-
-        std::this_thread::sleep_for(std::chrono::seconds(1));
-    }
+    std::this_thread::sleep_for(std::chrono::seconds(20));
 
     node.stop();
-
-    for (int i = 0; i < 2; ++i)
-    {
-        std::cout << node.getOutput();
-
-        std::this_thread::sleep_for(std::chrono::seconds(1));
-    }
 }
\ No newline at end of file
diff --git a/modules/platforms/cpp/test-common/CMakeLists.txt b/modules/platforms/cpp/test-common/CMakeLists.txt
index 396ee4ed72..a157d5ed98 100644
--- a/modules/platforms/cpp/test-common/CMakeLists.txt
+++ b/modules/platforms/cpp/test-common/CMakeLists.txt
@@ -24,6 +24,7 @@ include_directories(include)
 
 set(SOURCES
         src/ignite_node.cpp
+        src/process.cpp
         src/test_utils.cpp
 )
 
diff --git a/modules/platforms/cpp/test-common/include/ignite_node.h b/modules/platforms/cpp/test-common/include/ignite_node.h
index 224aa3e124..490a7985e0 100644
--- a/modules/platforms/cpp/test-common/include/ignite_node.h
+++ b/modules/platforms/cpp/test-common/include/ignite_node.h
@@ -15,46 +15,54 @@
  * limitations under the License.
  */
 
-#ifndef TEST_COMMON_IGNITE_NODE
-#define TEST_COMMON_IGNITE_NODE
+#pragma once
 
-//#include <cstdio>
+#include "process.h"
 
 namespace ignite
 {
     class IgniteNode
     {
     public:
-//        /**
-//         * Constructor.
-//         */
-//        IgniteNode() = default;
+        /**
+         * Constructor.
+         */
+        IgniteNode() = default;
 
-//        /**
-//         * Destructor.
-//         */
-//        ~IgniteNode() = default;
+        /**
+         * Destructor.
+         */
+        ~IgniteNode() = default;
 
         /**
          * Start node.
+         *
+         * @param dryRun Perform a dry run. Mostly used to ensure that code is compiled and all artifacts are downloaded.
          */
-        void start();
+        void start(bool dryRun = false);
 
         /**
          * Stop node.
          */
         void stop();
 
-        /**
-         * Get current node output.
-         *
-         * @param max Max bytes to get.
-         * @return Output.
-         */
-        std::string getOutput(int max = 1024);
+//        /**
+//         * Check whether node is still running.
+//         *
+//         * @return @c true if the node is running.
+//         */
+//        bool isRunning();
+//
+//        /**
+//         * Get current node output.
+//         *
+//         * @param max Max bytes to get.
+//         * @return Output.
+//         */
+//        std::string getOutput(int max = 1024);
+
     private:
-        FILE* stream;
+        /** Underlying process. */
+        std::unique_ptr<Process> process;
     };
 } // namespace ignite
-
-#endif // TEST_COMMON_IGNITE_NODE
diff --git a/modules/platforms/cpp/test-common/include/ignite_node.h b/modules/platforms/cpp/test-common/include/process.h
similarity index 62%
copy from modules/platforms/cpp/test-common/include/ignite_node.h
copy to modules/platforms/cpp/test-common/include/process.h
index 224aa3e124..b2d6d50121 100644
--- a/modules/platforms/cpp/test-common/include/ignite_node.h
+++ b/modules/platforms/cpp/test-common/include/process.h
@@ -15,46 +15,44 @@
  * limitations under the License.
  */
 
-#ifndef TEST_COMMON_IGNITE_NODE
-#define TEST_COMMON_IGNITE_NODE
+#pragma once
 
-//#include <cstdio>
+#include <string>
 
 namespace ignite
 {
-    class IgniteNode
+    class Process
     {
     public:
-//        /**
-//         * Constructor.
-//         */
-//        IgniteNode() = default;
+        /**
+         * Destructor.
+         */
+        virtual ~Process() = default;
 
-//        /**
-//         * Destructor.
-//         */
-//        ~IgniteNode() = default;
+        /**
+         * Make new process instance.
+         *
+         * @param command Command.
+         * @param workDir Working directory.
+         * @return Process.
+         */
+        static std::unique_ptr<Process> make(std::string command, std::string workDir);
 
         /**
-         * Start node.
+         * Start process.
          */
-        void start();
+        virtual bool start() = 0;
 
         /**
-         * Stop node.
+         * Stop process.
          */
-        void stop();
+        virtual void stop() = 0;
 
+    protected:
         /**
-         * Get current node output.
-         *
-         * @param max Max bytes to get.
-         * @return Output.
+         * Constructor.
          */
-        std::string getOutput(int max = 1024);
-    private:
-        FILE* stream;
+        Process() = default;
     };
 } // namespace ignite
 
-#endif // TEST_COMMON_IGNITE_NODE
diff --git a/modules/platforms/cpp/test-common/include/test_utils.h b/modules/platforms/cpp/test-common/include/test_utils.h
index ca0d00a51f..01626fd703 100644
--- a/modules/platforms/cpp/test-common/include/test_utils.h
+++ b/modules/platforms/cpp/test-common/include/test_utils.h
@@ -40,23 +40,4 @@ namespace ignite
      * Get path to maven executable.
      */
     std::string getMavenPath();
-
-    /**
-     * Open process.
-     *
-     * @param command System shell command line instruction.
-     * @param type Mode of the returned process output stream. Can be one of the following:
-     *  "r" - The calling process can read the spawned command's standard output using the returned stream.
-     *  "w" - The calling process can write to the spawned command's standard input using the returned stream.
-     * @return File stream for the process.
-     */
-    FILE* processOpen(const char *command, const char *type);
-
-    /**
-     * Waits for the associated process to terminate and returns the exit status of the command.
-     *
-     * @param stream Return value from the previous call to processOpen().
-     * @return Returns the exit status of the terminating command processor, or -1 if an error occurs.
-     */
-    int processClose(FILE* stream);
 } // namespace ignite
\ No newline at end of file
diff --git a/modules/platforms/cpp/test-common/src/ignite_node.cpp b/modules/platforms/cpp/test-common/src/ignite_node.cpp
index 1fb4bfb6eb..47807989a8 100644
--- a/modules/platforms/cpp/test-common/src/ignite_node.cpp
+++ b/modules/platforms/cpp/test-common/src/ignite_node.cpp
@@ -15,17 +15,18 @@
  * limitations under the License.
  */
 
-#include <iostream>
+#include <filesystem>
 #include <stdexcept>
 #include <vector>
 #include <utility>
+#include <iostream>
 
 #include "ignite_node.h"
 #include "test_utils.h"
 
 namespace ignite
 {
-    void IgniteNode::start()
+    void IgniteNode::start(bool dryRun)
     {
         std::string home = resolveIgniteHome();
         if (home.empty())
@@ -39,24 +40,44 @@ namespace ignite
         "/bin/bash -c ";
 #endif
 
-        command += getMavenPath() + " " + "exec:java@platform-test-node-runner";
+        command += getMavenPath() + " exec:java@platform-test-node-runner";
+
+        if (dryRun)
+            command += " -Dexec.args=dry-run";
+
+        auto workDir = std::filesystem::path(home) / "modules" / "runner";
+
+        std::cout << "IGNITE_HOME=" << home << std::endl;
+        std::cout << "working dir=" << workDir << std::endl;
+        std::cout << "command=" << command << std::endl;
 
-        stream = processOpen(command.c_str(), "r");
+        process = Process::make(command, workDir.string());
+        if (!process->start())
+        {
+            throw std::runtime_error("Failed to invoke Ignite command: '" + command + "'");
+
+            process.reset();
+        }
     }
 
     void IgniteNode::stop()
     {
-        if (stream)
-            processClose(stream);
+        if (process)
+            process->stop();
     }
 
-    std::string IgniteNode::getOutput(int max)
-    {
-        std::string buffer(max, 0);
-
-        size_t actual = std::fread(buffer.data(), 1, max, stream);
-        buffer.resize(actual);
-
-        return buffer;
-    }
+//    bool IgniteNode::isRunning()
+//    {
+//        return std::feof(stream) == 0 && std::ferror(stream) == 0;
+//    }
+//
+//    std::string IgniteNode::getOutput(int max)
+//    {
+//        std::string buffer(max, 0);
+//
+//        size_t actual = std::fread(buffer.data(), 1, max, stream);
+//        buffer.resize(actual);
+//
+//        return buffer;
+//    }
 } // namespace ignite
\ No newline at end of file
diff --git a/modules/platforms/cpp/test-common/src/process.cpp b/modules/platforms/cpp/test-common/src/process.cpp
new file mode 100644
index 0000000000..4620b53aec
--- /dev/null
+++ b/modules/platforms/cpp/test-common/src/process.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+#ifdef WIN32
+#   include <windows.h>
+#   include <tlhelp32.h>
+#endif // WIN32
+
+#include <filesystem>
+#include <utility>
+#include <vector>
+
+#include "process.h"
+
+namespace
+{
+#ifdef WIN32
+    /**
+     * Get process tree.
+     * @param processId ID of the parent process.
+     * @return Process tree.
+     */
+    std::vector<DWORD> getProcessTree(DWORD processId)
+    {
+        std::vector<DWORD> children;
+        PROCESSENTRY32 pe;
+
+        memset(&pe, 0, sizeof(PROCESSENTRY32));
+        pe.dwSize = sizeof(PROCESSENTRY32);
+
+        HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+
+        if (Process32First(hSnap, &pe))
+        {
+            BOOL bContinue = TRUE;
+
+            while (bContinue)
+            {
+                if (pe.th32ParentProcessID == processId)
+                    children.push_back(pe.th32ProcessID);
+
+                bContinue = Process32Next(hSnap, &pe);
+            }
+        }
+
+        std::vector<DWORD> tree(children);
+        for (auto procId : children)
+        {
+            std::vector<DWORD> childTree = getProcessTree(procId);
+            tree.insert(tree.end(), childTree.begin(), childTree.end());
+        }
+
+        return tree;
+    }
+
+    /**
+     * Implementation of Process for Windows.
+     */
+    class WinProcess : public ignite::Process
+    {
+    public:
+        /**
+         * Constructor.
+         *
+         * @param command Command.
+         * @param workDir Working directory.
+         */
+        WinProcess(std::string command, std::string workDir) :
+            running(false),
+            command(std::move(command)),
+            workDir(std::move(workDir)),
+            info{}
+        { }
+
+        /**
+         * Destructor.
+         */
+        ~WinProcess() override = default;
+
+
+        /**
+         * Start process.
+         */
+        bool start() override
+        {
+            if (running)
+                return false;
+
+            STARTUPINFO si;
+
+            std::memset(&si, 0, sizeof(si));
+            si.cb = sizeof(si);
+            std::memset(&info, 0, sizeof(info));
+
+            std::vector<char> cmd(command.begin(), command.end());
+            cmd.push_back(0);
+
+            BOOL success = CreateProcess(
+                    NULL, cmd.data(), NULL, NULL,
+                    FALSE, 0, NULL, workDir.c_str(),
+                    &si, &info);
+
+            running = success == TRUE;
+
+            return running;
+        }
+
+        /**
+         * Stop process.
+         */
+        void stop() override
+        {
+            std::vector<DWORD> processTree = getProcessTree(info.dwProcessId);
+            for (auto procId : processTree)
+            {
+                HANDLE hChildProc = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, procId);
+                if (hChildProc)
+                {
+                    TerminateProcess(hChildProc, 1);
+                    CloseHandle(hChildProc);
+                }
+            }
+
+            TerminateProcess(info.hProcess, 1);
+
+            WaitForSingleObject( info.hProcess, INFINITE );
+
+            CloseHandle( info.hProcess );
+            CloseHandle( info.hThread );
+        }
+
+    private:
+        /** Running flag. */
+        bool running;
+
+        /** Command. */
+        const std::string command;
+
+        /** Working directory. */
+        const std::string workDir;
+
+        /** Process information. */
+        PROCESS_INFORMATION info;
+    };
+
+#else
+#endif
+}
+
+namespace ignite
+{
+    std::unique_ptr<Process> Process::make(std::string command, std::string workDir)
+    {
+#ifdef WIN32
+        return std::unique_ptr<Process>(new WinProcess(std::move(command), std::move(workDir)));
+#else
+#endif
+    }
+} // namespace ignite
\ No newline at end of file
diff --git a/modules/platforms/cpp/test-common/src/test_utils.cpp b/modules/platforms/cpp/test-common/src/test_utils.cpp
index 65fe5b8975..f6b0a46386 100644
--- a/modules/platforms/cpp/test-common/src/test_utils.cpp
+++ b/modules/platforms/cpp/test-common/src/test_utils.cpp
@@ -91,22 +91,4 @@ namespace ignite
         // Currently, we only support systems with "mvn" command in PATH
         return "mvn";
     }
-
-    FILE *processOpen(const char *command, const char *type)
-    {
-#ifdef WIN32
-        return _popen(command, type);
-#else
-        return popen(command, type);
-#endif
-    }
-
-    int processClose(FILE *stream)
-    {
-#ifdef WIN32
-        return _pclose(stream);
-#else
-        return pclose(stream);
-#endif
-    }
 } // namespace ignite
\ No newline at end of file