You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sk...@apache.org on 2021/06/07 12:29:46 UTC

[ignite-3] branch main updated: IGNITE-14413 Added start of new node from cli interface. Fixes #157

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 44d69d3  IGNITE-14413 Added start of new node from cli interface. Fixes #157
44d69d3 is described below

commit 44d69d37013c81ce5055a262e7961c72b4ec3d7c
Author: Kirill Gusakov <kg...@gmail.com>
AuthorDate: Mon Jun 7 15:29:09 2021 +0300

    IGNITE-14413 Added start of new node from cli interface. Fixes #157
    
    Signed-off-by: Slava Koptilin <sl...@gmail.com>
---
 .../main/java/org/apache/ignite/app/Ignition.java  |   7 +-
 .../org/apache/ignite/app/IgnitionManager.java     |  12 +-
 .../schemas/runner/NodeConfigurationSchema.java    |   5 -
 .../ignite/cli/builtins/node/NodeManager.java      |  52 ++++----
 .../apache/ignite/cli/spec/NodeCommandSpec.java    |  19 ++-
 .../apache/ignite/cli/IgniteCliInterfaceTest.java  |  14 +--
 .../internal/metastorage/MetaStorageManager.java   |  18 ++-
 .../runner/app/DynamicTableCreationTest.java       |  30 +++--
 .../ignite/internal/runner/app/IgnitionTest.java   |  28 ++---
 .../internal/runner/app/TableCreationTest.java     |  25 ++--
 .../org/apache/ignite/app/IgniteCliRunner.java     | 134 +++++++++++++++++++++
 .../java/org/apache/ignite/app/IgniteRunner.java   |  25 ----
 .../apache/ignite/internal/app/IgnitionImpl.java   |  15 +--
 .../internal/table/distributed/TableManager.java   |   2 +-
 .../ignite/internal/table/TableManagerTest.java    |   3 +-
 .../apache/ignite/internal/vault/VaultManager.java |  34 ++++++
 16 files changed, 280 insertions(+), 143 deletions(-)

diff --git a/modules/api/src/main/java/org/apache/ignite/app/Ignition.java b/modules/api/src/main/java/org/apache/ignite/app/Ignition.java
index 9904f4c..4bacc4e 100644
--- a/modules/api/src/main/java/org/apache/ignite/app/Ignition.java
+++ b/modules/api/src/main/java/org/apache/ignite/app/Ignition.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.app;
 
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -26,9 +27,11 @@ import org.jetbrains.annotations.Nullable;
 public interface Ignition {
     /**
      * Starts Ignite node with optional bootstrap configuration in json format.
-     * @param jsonStrBootstrapCfg Node configuration in json format.
+     *
+     * @param name Name of the node. Couldn't be {@code null}.
+     * @param jsonStrBootstrapCfg Node configuration in json format. Could be {@code null}.
      * @return Started Ignite node.
      */
     // TODO: IGNITE-14599 Add generic way to bootstrap configuration.
-    public Ignite start(@Nullable String jsonStrBootstrapCfg);
+    public Ignite start(@NotNull String name, @Nullable String jsonStrBootstrapCfg);
 }
diff --git a/modules/api/src/main/java/org/apache/ignite/app/IgnitionManager.java b/modules/api/src/main/java/org/apache/ignite/app/IgnitionManager.java
index 1d66c30..0ebc6f5 100644
--- a/modules/api/src/main/java/org/apache/ignite/app/IgnitionManager.java
+++ b/modules/api/src/main/java/org/apache/ignite/app/IgnitionManager.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.app;
 
 import java.util.ServiceLoader;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -29,21 +30,24 @@ public class IgnitionManager {
 
     /**
      * Starts Ignite node with optional bootstrap configuration in json format.
+     *
+     * @param nodeName Name of the node.
      * @param jsonStrBootstrapCfg Node configuration in json format.
      * @return Started Ignite node.
      */
     // TODO IGNITE-14580 Add exception handling logic to IgnitionProcessor.
-    public static synchronized Ignite start(@Nullable String jsonStrBootstrapCfg) {
+    public static synchronized Ignite start(@NotNull String nodeName, @Nullable String jsonStrBootstrapCfg) {
         if (ignition == null) {
             ServiceLoader<Ignition> ldr = ServiceLoader.load(Ignition.class);
             ignition = ldr.iterator().next();
         }
 
-        return ignition.start(jsonStrBootstrapCfg);
+        return ignition.start(nodeName, jsonStrBootstrapCfg);
     }
 
     /**
      * Starts Ignite node with optional bootstrap configuration in json format.
+     * @param nodeName Name of the node.
      * @param jsonStrBootstrapCfg Node configuration in json format.
      * @param clsLdr The class loader to be used to load provider-configuration files
      * and provider classes, or {@code null} if the system class
@@ -51,12 +55,12 @@ public class IgnitionManager {
      * @return Started Ignite node.
      */
     // TODO IGNITE-14580 Add exception handling logic to IgnitionProcessor.
-    public static synchronized Ignite start(@Nullable String jsonStrBootstrapCfg, @Nullable ClassLoader clsLdr) {
+    public static synchronized Ignite start(@NotNull String nodeName, @Nullable String jsonStrBootstrapCfg, @Nullable ClassLoader clsLdr) {
         if (ignition == null) {
             ServiceLoader<Ignition> ldr = ServiceLoader.load(Ignition.class, clsLdr);
             ignition = ldr.iterator().next();
         }
 
-        return ignition.start(jsonStrBootstrapCfg);
+        return ignition.start(nodeName, jsonStrBootstrapCfg);
     }
 }
diff --git a/modules/api/src/main/java/org/apache/ignite/configuration/schemas/runner/NodeConfigurationSchema.java b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/runner/NodeConfigurationSchema.java
index 0d3d9f1..c0545c4 100644
--- a/modules/api/src/main/java/org/apache/ignite/configuration/schemas/runner/NodeConfigurationSchema.java
+++ b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/runner/NodeConfigurationSchema.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.configuration.schemas.runner;
 
-import java.util.UUID;
 import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 import org.apache.ignite.configuration.annotation.Value;
 import org.apache.ignite.configuration.storage.ConfigurationType;
@@ -27,10 +26,6 @@ import org.apache.ignite.configuration.storage.ConfigurationType;
  */
 @ConfigurationRoot(rootName = "node", type = ConfigurationType.LOCAL)
 public class NodeConfigurationSchema {
-    /** Uniq local node name. */
-    @Value(hasDefault = true)
-    public final String name = UUID.randomUUID().toString();
-
     /** It is a copy of appropriate property from the cluster configuration. */
     @Value(hasDefault = true)
     public final String[] metastorageNodes = new String[0];
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/node/NodeManager.java b/modules/cli/src/main/java/org/apache/ignite/cli/builtins/node/NodeManager.java
index ec93d8d..5e4b1f2 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/node/NodeManager.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/builtins/node/NodeManager.java
@@ -43,7 +43,7 @@ import org.jline.terminal.Terminal;
 @Singleton
 public class NodeManager {
     /** Entry point of core Ignite artifact for running new node. */
-    private static final String MAIN_CLASS = "org.apache.ignite.app.IgniteRunner";
+    private static final String MAIN_CLASS = "org.apache.ignite.app.IgniteCliRunner";
 
     /** Timeout for successful node start. */
     private static final Duration NODE_START_TIMEOUT = Duration.ofSeconds(30);
@@ -70,19 +70,19 @@ public class NodeManager {
      * It has very naive implementation of successful run check -
      * just waiting for appropriate message in the node logs.
      *
-     * @param consistentId Node consistent id.
+     * @param nodeName Node name.
      * @param logDir Log dir for receiving node state.
      * @param pidsDir Dir where pid files of running nodes will be stored.
      * @param srvCfg Config for Ignite node
      * @param out PrintWriter for user messages.
      * @return Information about successfully started node
      */
-    public RunningNode start(String consistentId, Path logDir, Path pidsDir, Path srvCfg, PrintWriter out) {
-        if (getRunningNodes(logDir, pidsDir).stream().anyMatch(n -> n.consistentId.equals(consistentId)))
-            throw new IgniteCLIException("Node with consistentId " + consistentId + " is already exist");
+    public RunningNode start(String nodeName, Path logDir, Path pidsDir, Path srvCfg, PrintWriter out) {
+        if (getRunningNodes(logDir, pidsDir).stream().anyMatch(n -> n.name.equals(nodeName)))
+            throw new IgniteCLIException("Node with nodeName " + nodeName + " is already exist");
 
         try {
-            Path logFile = logFile(logDir, consistentId);
+            Path logFile = logFile(logDir, nodeName);
             if (Files.exists(logFile))
                 Files.delete(logFile);
 
@@ -100,6 +100,8 @@ public class NodeManager {
                 cmdArgs.add(srvCfg.toAbsolutePath().toString());
             }
 
+            cmdArgs.add(nodeName);
+
             ProcessBuilder pb = new ProcessBuilder(
                 cmdArgs
             )
@@ -121,9 +123,9 @@ public class NodeManager {
                 throw new IgniteCLIException("Waiting for node start was failed", e);
             }
 
-            createPidFile(consistentId, p.pid(), pidsDir);
+            createPidFile(nodeName, p.pid(), pidsDir);
 
-            return new RunningNode(p.pid(), consistentId, logFile);
+            return new RunningNode(p.pid(), nodeName, logFile);
         }
         catch (IOException e) {
             throw new IgniteCLIException("Can't load classpath", e);
@@ -188,17 +190,17 @@ public class NodeManager {
     /**
      * Creates pid file for Ignite node.
      *
-     * @param consistentId Node consistent id.
+     * @param nodeName Node name.
      * @param pid Pid
      * @param pidsDir Dir for storing pid files.
      */
-    public void createPidFile(String consistentId, long pid, Path pidsDir) {
+    public void createPidFile(String nodeName, long pid, Path pidsDir) {
         if (!Files.exists(pidsDir)) {
             if (!pidsDir.toFile().mkdirs())
                 throw new IgniteCLIException("Can't create directory for storing the process pids: " + pidsDir);
         }
 
-        Path pidPath = pidsDir.resolve(consistentId + "_" + System.currentTimeMillis() + ".pid");
+        Path pidPath = pidsDir.resolve(nodeName + "_" + System.currentTimeMillis() + ".pid");
 
         try (FileWriter fileWriter = new FileWriter(pidPath.toFile())) {
             fileWriter.write(String.valueOf(pid));
@@ -235,9 +237,9 @@ public class NodeManager {
                         if (filename.lastIndexOf('_') == -1)
                             return Optional.<RunningNode>empty();
                         else {
-                            String consistentId = filename.substring(0, filename.lastIndexOf('_'));
+                            String nodeName = filename.substring(0, filename.lastIndexOf('_'));
 
-                            return Optional.of(new RunningNode(pid, consistentId, logFile(logDir, consistentId)));
+                            return Optional.of(new RunningNode(pid, nodeName, logFile(logDir, nodeName)));
                         }
 
                     })
@@ -253,18 +255,18 @@ public class NodeManager {
     }
 
     /**
-     * Stops the node by consistent id and waits for success.
+     * Stops the node by name and waits for success.
      *
-     * @param consistentId Node consistent id.
+     * @param nodeName Node name.
      * @param pidsDir Dir with running nodes pids.
      * @return true if stopped, false otherwise.
      */
-    public boolean stopWait(String consistentId, Path pidsDir) {
+    public boolean stopWait(String nodeName, Path pidsDir) {
         if (Files.exists(pidsDir)) {
             try {
                 List<Path> files = Files.find(pidsDir, 1,
                     (f, attrs) ->
-                        f.getFileName().toString().startsWith(consistentId + "_")).collect(Collectors.toList());
+                        f.getFileName().toString().startsWith(nodeName + "_")).collect(Collectors.toList());
 
                 if (!files.isEmpty()) {
                     return files.stream().map(f -> {
@@ -283,7 +285,7 @@ public class NodeManager {
                     }).reduce((a, b) -> a && b).orElse(false);
                 }
                 else
-                    throw new IgniteCLIException("Can't find node with consistent id " + consistentId);
+                    throw new IgniteCLIException("Can't find node with name" + nodeName);
             }
             catch (IOException e) {
                 throw new IgniteCLIException("Can't open directory with pid files " + pidsDir);
@@ -308,11 +310,11 @@ public class NodeManager {
 
     /**
      * @param logDir Ignite log dir.
-     * @param consistentId Node consistent id.
+     * @param nodeName Node name.
      * @return Path of node log file.
      */
-    private static Path logFile(Path logDir, String consistentId) {
-          return logDir.resolve(consistentId + ".log");
+    private static Path logFile(Path logDir, String nodeName) {
+          return logDir.resolve(nodeName + ".log");
     }
 
     /**
@@ -324,7 +326,7 @@ public class NodeManager {
         public final long pid;
 
         /** Consistent id. */
-        public final String consistentId;
+        public final String name;
 
         /** Path to log file. */
         public final Path logFile;
@@ -333,12 +335,12 @@ public class NodeManager {
          * Creates info about running node.
          *
          * @param pid Pid.
-         * @param consistentId Consistent id.
+         * @param name Consistent id.
          * @param logFile Log file.
          */
-        public RunningNode(long pid, String consistentId, Path logFile) {
+        public RunningNode(long pid, String name, Path logFile) {
             this.pid = pid;
-            this.consistentId = consistentId;
+            this.name = name;
             this.logFile = logFile;
         }
     }
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/spec/NodeCommandSpec.java b/modules/cli/src/main/java/org/apache/ignite/cli/spec/NodeCommandSpec.java
index fdcf9c2..e394626 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/spec/NodeCommandSpec.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/spec/NodeCommandSpec.java
@@ -60,8 +60,8 @@ public class NodeCommandSpec extends CategorySpec {
         private NodeManager nodeMgr;
 
         /** Consistent id, which will be used by new node. */
-        @CommandLine.Parameters(paramLabel = "consistent-id", description = "Consistent ID of the new node")
-        public String consistentId;
+        @CommandLine.Parameters(paramLabel = "name", description = "Name of the new node")
+        public String nodeName;
 
         /** Path to node config. */
         @CommandLine.Option(names = "--config", description = "Configuration file to start the node with")
@@ -74,19 +74,19 @@ public class NodeCommandSpec extends CategorySpec {
             PrintWriter out = spec.commandLine().getOut();
             ColorScheme cs = spec.commandLine().getColorScheme();
 
-            NodeManager.RunningNode node = nodeMgr.start(consistentId, ignitePaths.logDir,
+            NodeManager.RunningNode node = nodeMgr.start(nodeName, ignitePaths.logDir,
                 ignitePaths.cliPidsDir(),
                 configPath,
                 out);
 
             out.println();
             out.println("Node is successfully started. To stop, type " +
-                cs.commandText("ignite node stop ") + cs.parameterText(node.consistentId));
+                cs.commandText("ignite node stop ") + cs.parameterText(node.name));
             out.println();
 
             Table tbl = new Table(0, cs);
 
-            tbl.addRow("@|bold Consistent ID|@", node.consistentId);
+            tbl.addRow("@|bold Node name|@", node.name);
             tbl.addRow("@|bold PID|@", node.pid);
             tbl.addRow("@|bold Log File|@", node.logFile);
 
@@ -170,9 +170,8 @@ public class NodeCommandSpec extends CategorySpec {
 
                 tbl.addRow("@|bold Consistent ID|@", "@|bold PID|@", "@|bold Log File|@");
 
-                for (NodeManager.RunningNode node : nodes) {
-                    tbl.addRow(node.consistentId, node.pid, node.logFile);
-                }
+                for (NodeManager.RunningNode node : nodes)
+                    tbl.addRow(node.name, node.pid, node.logFile);
 
                 out.println(tbl);
             }
@@ -197,14 +196,12 @@ public class NodeCommandSpec extends CategorySpec {
 
                 out.println(Ansi.AUTO.string("@|bold Current Ignite node classpath:|@"));
 
-                for (String item : items) {
+                for (String item : items)
                     out.println("    " + item);
-                }
             }
             catch (IOException e) {
                 throw new IgniteCLIException("Can't get current classpath", e);
             }
         }
     }
-
 }
diff --git a/modules/cli/src/test/java/org/apache/ignite/cli/IgniteCliInterfaceTest.java b/modules/cli/src/test/java/org/apache/ignite/cli/IgniteCliInterfaceTest.java
index 1946f2d..c24231d 100644
--- a/modules/cli/src/test/java/org/apache/ignite/cli/IgniteCliInterfaceTest.java
+++ b/modules/cli/src/test/java/org/apache/ignite/cli/IgniteCliInterfaceTest.java
@@ -336,13 +336,13 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
                 cli.getOut());
 
             assertEquals("\nNode is successfully started. To stop, type ignite node stop " + nodeName + "\n\n" +
-                "+---------------+---------+\n" +
-                "| Consistent ID | node1   |\n" +
-                "+---------------+---------+\n" +
-                "| PID           | 1       |\n" +
-                "+---------------+---------+\n" +
-                "| Log File      | logfile |\n" +
-                "+---------------+---------+\n",
+                "+-----------+---------+\n" +
+                "| Node name | node1   |\n" +
+                "+-----------+---------+\n" +
+                "| PID       | 1       |\n" +
+                "+-----------+---------+\n" +
+                "| Log File  | logfile |\n" +
+                "+-----------+---------+\n",
                 out.toString());
         }
 
diff --git a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/MetaStorageManager.java b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/MetaStorageManager.java
index 6801c75..d56a550 100644
--- a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/MetaStorageManager.java
+++ b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/MetaStorageManager.java
@@ -122,9 +122,6 @@ public class MetaStorageManager {
         watchAggregator = new WatchAggregator();
         deployFut = new CompletableFuture<>();
 
-        String locNodeName = locCfgMgr.configurationRegistry().getConfiguration(NodeConfiguration.KEY)
-            .name().value();
-
         String[] metastorageNodes = locCfgMgr.configurationRegistry().getConfiguration(NodeConfiguration.KEY)
             .metastorageNodes().value();
 
@@ -503,20 +500,19 @@ public class MetaStorageManager {
      * @param configurationMgr Configuration manager.
      * @return {@code true} if the node has meta storage, {@code false} otherwise.
      */
-    public static boolean hasMetastorageLocally(ConfigurationManager configurationMgr) {
-        String locNodeName = configurationMgr
-            .configurationRegistry()
-            .getConfiguration(NodeConfiguration.KEY)
-            .name()
-            .value();
-
+    public boolean hasMetastorageLocally(ConfigurationManager configurationMgr) {
         String[] metastorageMembers = configurationMgr
             .configurationRegistry()
             .getConfiguration(NodeConfiguration.KEY)
             .metastorageNodes()
             .value();
 
-        return hasMetastorage(locNodeName, metastorageMembers);
+        try {
+            return hasMetastorage(vaultMgr.name(), metastorageMembers);
+        }
+        catch (IgniteInternalCheckedException e) {
+            throw new IgniteInternalException(e);
+        }
     }
 
     // TODO: IGNITE-14691 Temporally solution that should be removed after implementing reactive watches.
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/DynamicTableCreationTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/DynamicTableCreationTest.java
index fa2eb95..6c76f04 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/DynamicTableCreationTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/DynamicTableCreationTest.java
@@ -18,7 +18,9 @@
 package org.apache.ignite.internal.runner.app;
 
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 import org.apache.ignite.app.Ignite;
 import org.apache.ignite.app.IgnitionManager;
@@ -47,41 +49,37 @@ class DynamicTableCreationTest {
     private static final IgniteLogger LOG = IgniteLogger.forClass(SchemaManager.class);
 
     /** Nodes bootstrap configuration. */
-    private final String[] nodesBootstrapCfg =
-        {
-            "{\n" +
+    private final Map<String, String> nodesBootstrapCfg = new LinkedHashMap<>() {{
+            put("node0", "{\n" +
                 "  \"node\": {\n" +
-                "    \"name\":node0,\n" +
                 "    \"metastorageNodes\":[ \"node0\" ]\n" +
                 "  },\n" +
                 "  \"network\": {\n" +
                 "    \"port\":3344,\n" +
                 "    \"netClusterNodes\":[ \"localhost:3344\", \"localhost:3345\", \"localhost:3346\" ]\n" +
                 "  }\n" +
-                "}",
+                "}");
 
-            "{\n" +
+            put("node1", "{\n" +
                 "  \"node\": {\n" +
-                "    \"name\":node1,\n" +
                 "    \"metastorageNodes\":[ \"node0\" ]\n" +
                 "  },\n" +
                 "  \"network\": {\n" +
                 "    \"port\":3345,\n" +
                 "    \"netClusterNodes\":[ \"localhost:3344\", \"localhost:3345\", \"localhost:3346\" ]\n" +
                 "  }\n" +
-                "}",
+                "}");
 
-            "{\n" +
+            put("node2", "{\n" +
                 "  \"node\": {\n" +
-                "    \"name\":node2,\n" +
                 "    \"metastorageNodes\":[ \"node0\"]\n" +
                 "  },\n" +
                 "  \"network\": {\n" +
                 "    \"port\":3346,\n" +
                 "    \"netClusterNodes\":[ \"localhost:3344\", \"localhost:3345\", \"localhost:3346\" ]\n" +
                 "  }\n" +
-                "}",
-        };
+                "}");
+        }};
 
     /**
      * Check dynamic table creation.
@@ -90,8 +88,8 @@ class DynamicTableCreationTest {
     void testDynamicSimpleTableCreation() {
         List<Ignite> clusterNodes = new ArrayList<>();
 
-        for (String nodeBootstrapCfg : nodesBootstrapCfg)
-            clusterNodes.add(IgnitionManager.start(nodeBootstrapCfg));
+        for (Map.Entry<String, String> nodeBootstrapCfg : nodesBootstrapCfg.entrySet())
+            clusterNodes.add(IgnitionManager.start(nodeBootstrapCfg.getKey(), nodeBootstrapCfg.getValue()));
 
         assertEquals(3, clusterNodes.size());
 
@@ -159,8 +157,8 @@ class DynamicTableCreationTest {
     void testDynamicTableCreation() {
         List<Ignite> clusterNodes = new ArrayList<>();
 
-        for (String nodeBootstrapCfg : nodesBootstrapCfg)
-            clusterNodes.add(IgnitionManager.start(nodeBootstrapCfg));
+        for (Map.Entry<String, String> nodeBootstrapCfg : nodesBootstrapCfg.entrySet())
+            clusterNodes.add(IgnitionManager.start(nodeBootstrapCfg.getKey(), nodeBootstrapCfg.getValue()));
 
         assertEquals(3, clusterNodes.size());
 
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/IgnitionTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/IgnitionTest.java
index f914c51..e5a25a8 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/IgnitionTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/IgnitionTest.java
@@ -18,7 +18,9 @@
 package org.apache.ignite.internal.runner.app;
 
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import org.apache.ignite.app.Ignite;
 import org.apache.ignite.app.IgnitionManager;
 import org.junit.jupiter.api.Assertions;
@@ -30,41 +32,37 @@ import org.junit.jupiter.api.Test;
  */
 class IgnitionTest {
     /** Nodes bootstrap configuration. */
-    private final String[] nodesBootstrapCfg =
-        {
-            "{\n" +
+    private final Map<String, String> nodesBootstrapCfg = new LinkedHashMap<>() {{
+            put("node0", "{\n" +
                 "  \"node\": {\n" +
-                "    \"name\":node0,\n" +
                 "    \"metastorageNodes\":[ \"node0\" ]\n" +
                 "  },\n" +
                 "  \"network\": {\n" +
                 "    \"port\":3344,\n" +
                 "    \"netClusterNodes\":[ \"localhost:3344\", \"localhost:3345\", \"localhost:3346\" ]\n" +
                 "  }\n" +
-                "}",
+                "}");
 
-            "{\n" +
+            put("node1", "{\n" +
                 "  \"node\": {\n" +
-                "    \"name\":node1,\n" +
                 "    \"metastorageNodes\":[ \"node0\" ]\n" +
                 "  },\n" +
                 "  \"network\": {\n" +
                 "    \"port\":3345,\n" +
                 "    \"netClusterNodes\":[ \"localhost:3344\", \"localhost:3345\", \"localhost:3346\" ]\n" +
                 "  }\n" +
-                "}",
+                "}");
 
-            "{\n" +
+            put("node2", "{\n" +
                 "  \"node\": {\n" +
-                "    \"name\":node2,\n" +
                 "    \"metastorageNodes\":[ \"node0\" ]\n" +
                 "  },\n" +
                 "  \"network\": {\n" +
                 "    \"port\":3346,\n" +
                 "    \"netClusterNodes\":[ \"localhost:3344\", \"localhost:3345\", \"localhost:3346\" ]\n" +
                 "  }\n" +
-                "}",
-        };
+                "}");
+        }};
 
     /**
      * Check that Ignition.start() with bootstrap configuration returns Ignite instance.
@@ -73,8 +71,8 @@ class IgnitionTest {
     void testNodesStartWithBootstrapConfiguration() {
         List<Ignite> startedNodes = new ArrayList<>();
 
-        for (String nodeBootstrapCfg : nodesBootstrapCfg)
-            startedNodes.add(IgnitionManager.start(nodeBootstrapCfg));
+        for (Map.Entry<String, String> nodeBootstrapCfg : nodesBootstrapCfg.entrySet())
+            startedNodes.add(IgnitionManager.start(nodeBootstrapCfg.getKey(), nodeBootstrapCfg.getValue()));
 
         Assertions.assertEquals(3, startedNodes.size());
 
@@ -87,7 +85,7 @@ class IgnitionTest {
     @Test
     @Disabled("https://issues.apache.org/jira/browse/IGNITE-14709")
     void testNodeStartWithoutBootstrapConfiguration() {
-        Ignite ignite = IgnitionManager.start(null);
+        Ignite ignite = IgnitionManager.start("node0", null);
 
         Assertions.assertNotNull(ignite);
     }
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/TableCreationTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/TableCreationTest.java
index e9834ae..90a5a39 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/TableCreationTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/TableCreationTest.java
@@ -18,7 +18,9 @@
 package org.apache.ignite.internal.runner.app;
 
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 import org.apache.ignite.app.Ignite;
 import org.apache.ignite.app.IgnitionManager;
@@ -38,9 +40,8 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 @Disabled("https://issues.apache.org/jira/browse/IGNITE-14578")
 class TableCreationTest {
     /** Nodes bootstrap configuration with preconfigured tables. */
-    private final String[] nodesBootstrapCfg =
-        {
-            "{\n" +
+    private final LinkedHashMap<String, String> nodesBootstrapCfg = new LinkedHashMap<>() {{
+            put("node0", "{\n" +
                 "  \"node\": {\n" +
                 "    \"name\":node0,\n" +
                 "    \"metastorageNodes\":[ \"node0\", \"node1\" ]\n" +
@@ -124,30 +125,28 @@ class TableCreationTest {
                 "           }\n" + /* Table. */
                 "       }\n" + /* Tables. */
                 "  }\n" + /* Root. */
-                "}",
+                "}");
 
-            "{\n" +
+            put("node1", "{\n" +
                 "  \"node\": {\n" +
-                "    \"name\":node1,\n" +
                 "    \"metastorageNodes\":[ \"node0\", \"node1\" ]\n" +
                 "  },\n" +
                 "  \"network\": {\n" +
                 "    \"port\":3345,\n" +
                 "    \"netClusterNodes\":[ \"localhost:3344\", \"localhost:3345\", \"localhost:3346\" ]\n" +
                 "  }\n" +
-                "}",
+                "}");
 
-            "{\n" +
+            put("node2", "{\n" +
                 "  \"node\": {\n" +
-                "    \"name\":node2,\n" +
                 "    \"metastorageNodes\":[ \"node0\", \"node1\" ]\n" +
                 "  },\n" +
                 "  \"network\": {\n" +
                 "    \"port\":3346,\n" +
                 "    \"netClusterNodes\":[ \"localhost:3344\", \"localhost:3345\", \"localhost:3346\" ]\n" +
                 "  }\n" +
-                "}",
-        };
+                "}");
+        }};
 
     /**
      * Check table creation via bootstrap configuration with pre-configured table.
@@ -156,8 +155,8 @@ class TableCreationTest {
     void testInitialSimpleTableConfiguration() {
         List<Ignite> clusterNodes = new ArrayList<>();
 
-        for (String nodeBootstrapCfg : nodesBootstrapCfg)
-            clusterNodes.add(IgnitionManager.start(nodeBootstrapCfg));
+        for (Map.Entry<String, String> nodeBootstrapCfg : nodesBootstrapCfg.entrySet())
+            clusterNodes.add(IgnitionManager.start(nodeBootstrapCfg.getKey(), nodeBootstrapCfg.getValue()));
 
         assertEquals(3, clusterNodes.size());
 
diff --git a/modules/runner/src/main/java/org/apache/ignite/app/IgniteCliRunner.java b/modules/runner/src/main/java/org/apache/ignite/app/IgniteCliRunner.java
new file mode 100644
index 0000000..b99ada2
--- /dev/null
+++ b/modules/runner/src/main/java/org/apache/ignite/app/IgniteCliRunner.java
@@ -0,0 +1,134 @@
+/*
+ * 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.app;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import org.apache.ignite.internal.app.IgnitionImpl;
+import org.apache.ignite.lang.IgniteInternalCheckedException;
+
+/**
+ * The main entry point for run new Ignite node from CLI toolchain.
+ */
+public class IgniteCliRunner {
+    /** CLI usage message. */
+    private static String usage = "IgniteCliRunner [--config conf] nodeName";
+
+    /**
+     * Main method for run new Ignite node.
+     *
+     * For CLI args info see {@link IgniteCliRunner#usage}
+     *
+     * @param args CLI args to start new node.
+     * @throws IOException if any issues with reading config file.
+     */
+    public static void main(String[] args) throws IOException {
+        Args parsedArgs = null;
+
+        try {
+            parsedArgs = Args.parseArgs(args);
+        }
+        catch (Args.ParseException e) {
+            if (e.getMessage() != null)
+                System.out.println(e.getMessage() + "\n");
+
+            System.out.println(usage);
+
+            System.exit(1);
+        }
+
+        String jsonCfgStr = null;
+
+        if (parsedArgs.config != null)
+            jsonCfgStr = Files.readString(parsedArgs.config);
+
+        var ignition = new IgnitionImpl();
+
+        ignition.start(parsedArgs.nodeName, jsonCfgStr);
+    }
+
+    /**
+     * Simple value object with parsed CLI args of ignite runner.
+     */
+    private static class Args {
+        /** Name of the node. */
+        private final String nodeName;
+
+        /** Path to config file. */
+        private final Path config;
+
+        /**
+         * Creates new instance with parsed arguments.
+         *
+         * @param nodeName Name of the node.
+         * @param config Path to config file.
+         */
+        private Args(String nodeName, Path config) {
+            this.nodeName = nodeName;
+            this.config = config;
+        }
+
+        /**
+         * Simple CLI arguments parser.
+         *
+         * @param args CLI arguments.
+         * @return Parsed arguments.
+         * @throws ParseException if required args are absent.
+         */
+        private static Args parseArgs(String[] args) throws ParseException {
+            if (args.length == 1)
+                return new Args(args[0], null);
+            else if (args.length == 3) {
+                if ("--config".equals(args[0])) {
+                    try {
+                        return new Args(args[2], Path.of(args[1]));
+                    }
+                    catch (InvalidPathException e) {
+                        throw new ParseException("Couldn't parse configuration path.");
+                    }
+                }
+                else
+                    throw new ParseException();
+            }
+            else
+                throw new ParseException();
+        }
+
+        /**
+         * Exception for indicate any problems with parsing CLI args.
+         */
+        private static class ParseException extends IgniteInternalCheckedException {
+            /**
+             * Creates new exception of parsing.
+             *
+             * @param msg Message.
+             */
+            private ParseException(String msg) {
+                super(msg);
+            }
+
+            /**
+             * Creates new exception of parsing.
+             */
+            private ParseException() {
+            }
+        }
+    }
+}
diff --git a/modules/runner/src/main/java/org/apache/ignite/app/IgniteRunner.java b/modules/runner/src/main/java/org/apache/ignite/app/IgniteRunner.java
deleted file mode 100644
index 61c5f9c..0000000
--- a/modules/runner/src/main/java/org/apache/ignite/app/IgniteRunner.java
+++ /dev/null
@@ -1,25 +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.
- */
-
-package org.apache.ignite.app;
-
-/**
- * Sample application integrating new configuration module and providing standard REST API to access and modify it.
- */
-public class IgniteRunner {
-    // TODO: IGNITE-14413 Start of Ignite node should be supported by ignite-ctl tool
-}
diff --git a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgnitionImpl.java b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgnitionImpl.java
index 0631ce5..fbf8f37 100644
--- a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgnitionImpl.java
+++ b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgnitionImpl.java
@@ -50,6 +50,8 @@ import org.apache.ignite.network.MessageSerializationRegistryImpl;
 import org.apache.ignite.network.scalecube.ScaleCubeClusterServiceFactory;
 import org.apache.ignite.table.manager.IgniteTables;
 import org.apache.ignite.utils.IgniteProperties;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Implementation of an entry point for handling grid lifecycle.
@@ -79,12 +81,16 @@ public class IgnitionImpl implements Ignition {
     private static final String VER_KEY = "version";
 
     /** {@inheritDoc} */
-    @Override public synchronized Ignite start(String jsonStrBootstrapCfg) {
+    @Override public synchronized Ignite start(@NotNull String nodeName, @Nullable String jsonStrBootstrapCfg) {
+        assert !StringUtil.isNullOrEmpty(nodeName) : "Node local name is empty";
+
         ackBanner();
 
         // Vault Component startup.
         VaultManager vaultMgr = new VaultManager(new VaultServiceImpl());
 
+        vaultMgr.putName(nodeName).join();
+
         boolean cfgBootstrappedFromPds = vaultMgr.bootstrapped();
 
         List<RootKey<?, ?>> rootKeys = Arrays.asList(
@@ -117,15 +123,10 @@ public class IgnitionImpl implements Ignition {
 
         var serializationRegistry = new MessageSerializationRegistryImpl();
 
-        String localNodeName = locConfigurationMgr.configurationRegistry().getConfiguration(NodeConfiguration.KEY)
-            .name().value();
-
-        assert !StringUtil.isNullOrEmpty(localNodeName) : "Node local name is empty";
-
         // Network startup.
         ClusterService clusterNetSvc = new ScaleCubeClusterServiceFactory().createClusterService(
             new ClusterLocalConfiguration(
-                localNodeName,
+                nodeName,
                 netConfigurationView.port(),
                 Arrays.asList(netConfigurationView.netClusterNodes()),
                 serializationRegistry
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
index d4deb6d..4291047 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
@@ -205,7 +205,7 @@ public class TableManager extends Producer<TableEvent, TableEventParameters> imp
 
             List<CompletableFuture<Boolean>> futs = new ArrayList<>();
 
-            boolean hasMetastorageLocally = MetaStorageManager.hasMetastorageLocally(configurationMgr);
+            boolean hasMetastorageLocally = metaStorageMgr.hasMetastorageLocally(configurationMgr);
 
             for (String tblName : tablesToStart) {
                 TableView tableView = ctx.newValue().get(tblName);
diff --git a/modules/table/src/test/java/org/apache/ignite/internal/table/TableManagerTest.java b/modules/table/src/test/java/org/apache/ignite/internal/table/TableManagerTest.java
index fc09474..24b6ea1 100644
--- a/modules/table/src/test/java/org/apache/ignite/internal/table/TableManagerTest.java
+++ b/modules/table/src/test/java/org/apache/ignite/internal/table/TableManagerTest.java
@@ -110,7 +110,6 @@ public class TableManagerTest {
 
             cfrMgr.bootstrap("{\n" +
                 "   \"node\":{\n" +
-                "      \"name\":\"node1\",\n" +
                 "      \"metastorageNodes\":[\n" +
                 "         \"" + NODE_NAME + "\"\n" +
                 "      ]\n" +
@@ -285,6 +284,8 @@ public class TableManagerTest {
         ClusterNode node,
         CompletableFuture<UUID> tblIdFut
     ) {
+        when(mm.hasMetastorageLocally(any())).thenReturn(true);
+
         when(mm.invoke((Condition)any(), (Operation)any(), (Operation)any())).thenAnswer(invokation -> {
             Condition condition = (Condition)invokation.getArgument(0);
 
diff --git a/modules/vault/src/main/java/org/apache/ignite/internal/vault/VaultManager.java b/modules/vault/src/main/java/org/apache/ignite/internal/vault/VaultManager.java
index cbbc6f5..2686772 100644
--- a/modules/vault/src/main/java/org/apache/ignite/internal/vault/VaultManager.java
+++ b/modules/vault/src/main/java/org/apache/ignite/internal/vault/VaultManager.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.vault;
 
+import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -29,6 +30,7 @@ import org.apache.ignite.internal.vault.service.VaultService;
 import org.apache.ignite.lang.ByteArray;
 import org.apache.ignite.lang.IgniteInternalCheckedException;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * VaultManager is responsible for handling {@link VaultService} lifecycle
@@ -38,6 +40,9 @@ public class VaultManager {
     /** Special key for vault where applied revision for {@code putAll} operation is stored. */
     private static ByteArray APPLIED_REV = ByteArray.fromString("applied_revision");
 
+    /** Special key, which reserved for storing the name of the current node. */
+    private static final ByteArray NODE_NAME = ByteArray.fromString("node_name");
+
     /** Mutex. */
     private final Object mux = new Object();
 
@@ -172,6 +177,35 @@ public class VaultManager {
     }
 
     /**
+     * Persist node name to the vault.
+     *
+     * @param name Node name to persist. Couldn't be null.
+     * @return Future representing pending completion of the operation. Couldn't be {@code null}.
+     */
+    @NotNull public CompletableFuture<Void> putName(@NotNull String name) {
+        return put(NODE_NAME, name.getBytes(StandardCharsets.UTF_8));
+    }
+
+    /**
+     * @return Node name, if was stored earlier. Could be {@code null}.
+     * @throws IgniteInternalCheckedException If couldn't get node name from the vault.
+     */
+    @Nullable public String name() throws IgniteInternalCheckedException {
+        synchronized (mux) {
+            try {
+                byte[] nodeName = vaultService.get(NODE_NAME).get().value();
+                if (nodeName != null)
+                    return new String(nodeName, StandardCharsets.UTF_8);
+                else
+                    return null;
+            }
+            catch (InterruptedException | ExecutionException e) {
+                throw new IgniteInternalCheckedException("Error occurred when getting node name", e);
+            }
+        }
+    }
+
+    /**
      * See {@link VaultService#watch(VaultWatch)}
      *
      * @param vaultWatch Watch which will notify for each update.