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

apkhmv 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 9fd8459597 IGNITE-18608 Support SSL for REST in CLI (#1759)
9fd8459597 is described below

commit 9fd84595972928b907f5a551356bb99c1a289e32
Author: Ivan Gagarkin <[email protected]>
AuthorDate: Tue Mar 14 15:48:29 2023 +0400

    IGNITE-18608 Support SSL for REST in CLI (#1759)
    
    ---------
    
    Co-authored-by: Ivan Gagarkin <[email protected]>
    Co-authored-by: Aleksandr Pakhomov <[email protected]>
---
 .../commands/questions/ItConnectToClusterTest.java |   4 +-
 .../cli/ssl/CliSslIntegrationTestBase.java         |  79 +++++++++++
 .../apache/ignite/internal/cli/ssl/ItSslTest.java  |  73 ++++++++++
 .../src/integrationTest/resources/ssl/keystore.p12 | Bin 0 -> 4533 bytes
 .../integrationTest/resources/ssl/truststore.jks   | Bin 0 -> 1738 bytes
 .../java/org/apache/ignite/internal/cli/Main.java  |   2 +-
 .../internal/cli/call/connect/ConnectCall.java     |   4 +-
 .../ignite/internal/cli/commands/Options.java      |   4 +-
 .../commands/cluster/ClusterUrlProfileMixin.java   |   4 +-
 .../cli/commands/node/NodeUrlProfileMixin.java     |   4 +-
 .../questions/ConnectToClusterQuestion.java        |  10 +-
 .../cli/config/CachedConfigManagerProvider.java    |   2 +-
 .../ignite/internal/cli/config/CliConfigKeys.java  | 117 ++++++++++++++++
 .../internal/cli/config/ConfigConstants.java       | 100 --------------
 .../internal/cli/config/ini/IniConfigManager.java  |  10 +-
 .../ignite/internal/cli/core/ApiClientBuilder.java | 148 +++++++++++++++++++++
 .../ignite/internal/cli/core/ApiClientFactory.java |  34 ++++-
 .../handler/IgniteCliApiExceptionHandler.java      |   6 +
 .../cli/core/repl/SessionDefaultValueProvider.java |   4 +-
 .../completer/DynamicCompleterActivationPoint.java |  42 +++---
 .../cli/CliConfigDynamicCompleterFactory.java}     |  23 ++--
 .../cliconfig/CliConfigSetCommandTest.java         |  20 +--
 22 files changed, 526 insertions(+), 164 deletions(-)

diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java
index 12d27b35c2..1214b97217 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToClusterTest.java
@@ -29,7 +29,7 @@ import java.nio.file.Path;
 import 
org.apache.ignite.internal.cli.commands.CliCommandTestInitializedIntegrationBase;
 import org.apache.ignite.internal.cli.commands.TopLevelCliReplCommand;
 import 
org.apache.ignite.internal.cli.commands.cliconfig.TestConfigManagerHelper;
-import org.apache.ignite.internal.cli.config.ConfigConstants;
+import org.apache.ignite.internal.cli.config.CliConfigKeys;
 import org.apache.ignite.internal.cli.config.TestStateConfigHelper;
 import org.apache.ignite.internal.cli.config.TestStateConfigProvider;
 import org.apache.ignite.internal.cli.config.ini.IniConfigManager;
@@ -134,7 +134,7 @@ class ItConnectToClusterTest extends 
CliCommandTestInitializedIntegrationBase {
         // And prompt is changed to connect
         String promptAfter = Ansi.OFF.string(promptProvider.getPrompt());
         assertThat(promptAfter).isEqualTo("[" + nodeName() + "]> ");
-        
assertThat(configManagerProvider.get().getCurrentProperty(ConfigConstants.CLUSTER_URL))
+        
assertThat(configManagerProvider.get().getCurrentProperty(CliConfigKeys.CLUSTER_URL.value()))
                 .isEqualTo("http://localhost:10300";);
     }
 
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/ssl/CliSslIntegrationTestBase.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/ssl/CliSslIntegrationTestBase.java
new file mode 100644
index 0000000000..6b33a91e3b
--- /dev/null
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/ssl/CliSslIntegrationTestBase.java
@@ -0,0 +1,79 @@
+/*
+ * 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.cli.ssl;
+
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.util.Objects;
+import 
org.apache.ignite.internal.cli.commands.CliCommandTestNotInitializedIntegrationBase;
+
+/**
+ * Integration test base for SSL tests.
+ */
+public class CliSslIntegrationTestBase extends 
CliCommandTestNotInitializedIntegrationBase {
+
+    private static final String keyStorePath = "ssl/keystore.p12";
+    private static final String keyStorePassword = "changeit";
+    private static final String trustStorePath = "ssl/truststore.jks";
+    private static final String trustStorePassword = "changeit";
+
+    /**
+     * Template for node bootstrap config with Scalecube and Logical Topology 
settings for fast failure detection.
+     */
+    protected static final String REST_SSL_BOOTSTRAP_CONFIG = "{\n"
+            + "  network: {\n"
+            + "    port: {},\n"
+            + "    nodeFinder: {\n"
+            + "      netClusterNodes: [ {} ]\n"
+            + "    },\n"
+            + "  },\n"
+            + "  rest: {"
+            + "    dualProtocol: " + true + ",\n"
+            + "    ssl: {\n"
+            + "      enabled: " + true + ",\n"
+            + "      clientAuth: \"require\",\n"
+            + "      keyStore: {\n"
+            + "        path: \"" + getResourcePath(keyStorePath) + "\",\n"
+            + "        password: " + keyStorePassword + "\n"
+            + "      }, \n"
+            + "      trustStore: {\n"
+            + "        type: JKS,\n"
+            + "        path: \"" + getResourcePath(trustStorePath) + "\",\n"
+            + "        password: " + trustStorePassword + "\n"
+            + "      }\n"
+            + "    }\n"
+            + "  }\n"
+            + "}";
+
+    protected static String getResourcePath(String resource) {
+        try {
+            URL url = 
CliSslIntegrationTestBase.class.getClassLoader().getResource(resource);
+            Objects.requireNonNull(url, "Resource " + resource + " not 
found.");
+            Path path = Path.of(url.toURI()); // Properly extract file system 
path from the "file:" URL
+            return path.toString().replace("\\", "\\\\"); // Escape 
backslashes for the config parser
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e); // Shouldn't happen since URL is 
obtained from the class loader
+        }
+    }
+
+    @Override
+    protected String nodeBootstrapConfigTemplate() {
+        return REST_SSL_BOOTSTRAP_CONFIG;
+    }
+}
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/ssl/ItSslTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/ssl/ItSslTest.java
new file mode 100644
index 0000000000..5c831545ef
--- /dev/null
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/ssl/ItSslTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.cli.ssl;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/** Tests for SSL. */
+public class ItSslTest extends CliSslIntegrationTestBase {
+
+    /** Trust store path. */
+    private static final String trustStorePath = "ssl/truststore.jks";
+
+    /** Trust store password. */
+    private static final String trustStorePassword = "changeit";
+
+    /** Key store path. */
+    private static final String keyStorePath = "ssl/keystore.p12";
+
+    /** Key store password. */
+    private static final String keyStorePassword = "changeit";
+
+    @Test
+    @DisplayName("Should get SSL error, when connect to secured node without 
SSL settings")
+    void connectToSecuredNodeWithoutSslSettings() {
+        // When connect via HTTPS without SSL
+        execute("connect", "https://localhost:10401";);
+
+        // Then
+        assertAll(
+                () -> assertErrOutputContains("SSL error"),
+                this::assertOutputIsEmpty
+        );
+    }
+
+    @Test
+    @DisplayName("Should connect to cluster with given url")
+    void connectToSecuredNode() {
+        // When set up ssl configuration
+        execute("cli", "config", "set", "ignite.rest.key-store.path=" + 
getResourcePath(keyStorePath));
+        execute("cli", "config", "set", "ignite.rest.key-store.password=" + 
keyStorePassword);
+        execute("cli", "config", "set", "ignite.rest.trust-store.path=" + 
getResourcePath(trustStorePath));
+        execute("cli", "config", "set", "ignite.rest.trust-store.password=" + 
trustStorePassword);
+        resetOutput();
+
+        // And connect via HTTPS
+        execute("connect", "https://localhost:10401";);
+
+        // Then
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Connected to 
https://localhost:10401";)
+        );
+    }
+
+}
diff --git a/modules/cli/src/integrationTest/resources/ssl/keystore.p12 
b/modules/cli/src/integrationTest/resources/ssl/keystore.p12
new file mode 100644
index 0000000000..d929618bd6
Binary files /dev/null and 
b/modules/cli/src/integrationTest/resources/ssl/keystore.p12 differ
diff --git a/modules/cli/src/integrationTest/resources/ssl/truststore.jks 
b/modules/cli/src/integrationTest/resources/ssl/truststore.jks
new file mode 100644
index 0000000000..45052de67f
Binary files /dev/null and 
b/modules/cli/src/integrationTest/resources/ssl/truststore.jks differ
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
index 60e361da8e..fedade22c4 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
@@ -17,7 +17,7 @@
 
 package org.apache.ignite.internal.cli;
 
-import static 
org.apache.ignite.internal.cli.config.ConfigConstants.IGNITE_CLI_LOGS_DIR;
+import static 
org.apache.ignite.internal.cli.config.CliConfigKeys.Constants.IGNITE_CLI_LOGS_DIR;
 
 import io.micronaut.configuration.picocli.MicronautFactory;
 import io.micronaut.context.ApplicationContext;
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
index 333ae6b973..f545f2f08d 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
@@ -21,7 +21,7 @@ import com.google.gson.Gson;
 import jakarta.inject.Singleton;
 import java.net.MalformedURLException;
 import java.util.Objects;
-import org.apache.ignite.internal.cli.config.ConfigConstants;
+import org.apache.ignite.internal.cli.config.CliConfigKeys;
 import org.apache.ignite.internal.cli.config.StateConfigProvider;
 import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.JdbcUrl;
@@ -69,7 +69,7 @@ public class ConnectCall implements Call<ConnectCallInput, 
String> {
         }
         try {
             String configuration = fetchNodeConfiguration(nodeUrl);
-            
stateConfigProvider.get().setProperty(ConfigConstants.LAST_CONNECTED_URL, 
nodeUrl);
+            
stateConfigProvider.get().setProperty(CliConfigKeys.LAST_CONNECTED_URL.value(), 
nodeUrl);
             session.connect(new SessionInfo(nodeUrl, fetchNodeName(nodeUrl), 
constructJdbcUrl(configuration, nodeUrl)));
             return 
DefaultCallOutput.success(MessageUiComponent.fromMessage("Connected to %s", 
UiElements.url(nodeUrl)).render());
         } catch (ApiException | IllegalArgumentException e) {
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
index 1125a9b0cb..c68ecfadf7 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
@@ -17,7 +17,7 @@
 
 package org.apache.ignite.internal.cli.commands;
 
-import org.apache.ignite.internal.cli.config.ConfigConstants;
+import org.apache.ignite.internal.cli.config.CliConfigKeys;
 
 /**
  * Constants to use in {@code Option} annotations for commands.
@@ -83,7 +83,7 @@ public enum Options {
         public static final String CLUSTER_URL_OPTION_DESC = "URL of cluster 
endpoint";
 
         /** Cluster endpoint URL option description key. */
-        public static final String CLUSTER_URL_KEY = 
ConfigConstants.CLUSTER_URL;
+        public static final String CLUSTER_URL_KEY = 
CliConfigKeys.Constants.CLUSTER_URL;
 
         /** Node URL option long name. */
         public static final String NODE_URL_OPTION = "--node-url";
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/ClusterUrlProfileMixin.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/ClusterUrlProfileMixin.java
index b52c020130..c91f62d005 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/ClusterUrlProfileMixin.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/ClusterUrlProfileMixin.java
@@ -19,7 +19,7 @@ package org.apache.ignite.internal.cli.commands.cluster;
 
 import jakarta.inject.Inject;
 import org.apache.ignite.internal.cli.commands.ProfileMixin;
-import org.apache.ignite.internal.cli.config.ConfigConstants;
+import org.apache.ignite.internal.cli.config.CliConfigKeys;
 import org.apache.ignite.internal.cli.config.ConfigManager;
 import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
 import picocli.CommandLine.Mixin;
@@ -49,7 +49,7 @@ public class ClusterUrlProfileMixin {
             return clusterUrl.getClusterUrl();
         } else {
             ConfigManager configManager = configManagerProvider.get();
-            return configManager.getProperty(ConfigConstants.CLUSTER_URL, 
profileName.getProfileName());
+            return 
configManager.getProperty(CliConfigKeys.CLUSTER_URL.value(), 
profileName.getProfileName());
         }
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlProfileMixin.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlProfileMixin.java
index cd17795b95..88969351ed 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlProfileMixin.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeUrlProfileMixin.java
@@ -19,7 +19,7 @@ package org.apache.ignite.internal.cli.commands.node;
 
 import jakarta.inject.Inject;
 import org.apache.ignite.internal.cli.commands.ProfileMixin;
-import org.apache.ignite.internal.cli.config.ConfigConstants;
+import org.apache.ignite.internal.cli.config.CliConfigKeys;
 import org.apache.ignite.internal.cli.config.ConfigManager;
 import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
 import picocli.CommandLine.Mixin;
@@ -49,7 +49,7 @@ public class NodeUrlProfileMixin {
             return nodeUrl.getNodeUrl();
         } else {
             ConfigManager configManager = configManagerProvider.get();
-            return configManager.getProperty(ConfigConstants.CLUSTER_URL, 
profileName.getProfileName());
+            return 
configManager.getProperty(CliConfigKeys.CLUSTER_URL.value(), 
profileName.getProfileName());
         }
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/questions/ConnectToClusterQuestion.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/questions/ConnectToClusterQuestion.java
index 4f5f4df768..a0d389452e 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/questions/ConnectToClusterQuestion.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/questions/ConnectToClusterQuestion.java
@@ -22,7 +22,7 @@ import jakarta.inject.Singleton;
 import java.util.Objects;
 import org.apache.ignite.internal.cli.call.connect.ConnectCall;
 import org.apache.ignite.internal.cli.call.connect.ConnectCallInput;
-import org.apache.ignite.internal.cli.config.ConfigConstants;
+import org.apache.ignite.internal.cli.config.CliConfigKeys;
 import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
 import org.apache.ignite.internal.cli.config.StateConfigProvider;
 import org.apache.ignite.internal.cli.core.flow.builder.FlowBuilder;
@@ -64,7 +64,7 @@ public class ConnectToClusterQuestion {
             return Flows.from(url);
         }
 
-        String defaultUrl = 
configManagerProvider.get().getCurrentProperty(ConfigConstants.CLUSTER_URL);
+        String defaultUrl = 
configManagerProvider.get().getCurrentProperty(CliConfigKeys.CLUSTER_URL.value());
 
         QuestionUiComponent questionUiComponent = 
QuestionUiComponent.fromQuestion(
                 "You are not connected to node. Do you want to connect to the 
default node %s? %s ",
@@ -112,8 +112,8 @@ public class ConnectToClusterQuestion {
         if (session.info() != null) {
             return;
         }
-        String defaultUrl = 
configManagerProvider.get().getCurrentProperty(ConfigConstants.CLUSTER_URL);
-        String lastConnectedUrl = 
stateConfigProvider.get().getProperty(ConfigConstants.LAST_CONNECTED_URL);
+        String defaultUrl = 
configManagerProvider.get().getCurrentProperty(CliConfigKeys.CLUSTER_URL.value());
+        String lastConnectedUrl = 
stateConfigProvider.get().getProperty(CliConfigKeys.LAST_CONNECTED_URL.value());
         QuestionUiComponent question;
         String clusterUrl;
         if (lastConnectedUrl != null) {
@@ -144,7 +144,7 @@ public class ConnectToClusterQuestion {
         return Flows.acceptQuestion(QuestionUiComponent.fromQuestion(
                 "Would you like to use %s as the default URL? %s ", 
UiElements.url(lastConnectedUrl), UiElements.yesNo()
                 ), () -> {
-                    
configManagerProvider.get().setProperty(ConfigConstants.CLUSTER_URL, 
lastConnectedUrl);
+                    
configManagerProvider.get().setProperty(CliConfigKeys.CLUSTER_URL.value(), 
lastConnectedUrl);
                     return "Config saved";
                 }
         );
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CachedConfigManagerProvider.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CachedConfigManagerProvider.java
index c00c2353d7..cdf74a9fa8 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CachedConfigManagerProvider.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CachedConfigManagerProvider.java
@@ -25,7 +25,7 @@ import 
org.apache.ignite.internal.cli.config.ini.IniConfigManager;
  */
 @Singleton
 public class CachedConfigManagerProvider implements ConfigManagerProvider {
-    private final ConfigManager configManager = new 
IniConfigManager(ConfigConstants.getConfigFile());
+    private final ConfigManager configManager = new 
IniConfigManager(CliConfigKeys.getConfigFile());
 
     @Override
     public ConfigManager get() {
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
new file mode 100644
index 0000000000..15383b5472
--- /dev/null
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
@@ -0,0 +1,117 @@
+/*
+ * 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.cli.config;
+
+import java.io.File;
+import java.nio.file.Path;
+
+/** CLI config keys and constants. */
+public enum CliConfigKeys {
+
+    /** Default cluster or node URL property name. */
+    CLUSTER_URL(Constants.CLUSTER_URL),
+
+    /** Last connected cluster or node URL property name. */
+    LAST_CONNECTED_URL(Constants.LAST_CONNECTED_URL),
+
+    /** REST trust store path property name. */
+    REST_TRUST_STORE_PATH(Constants.REST_TRUST_STORE_PATH),
+
+    /** REST trust store password property name. */
+    REST_TRUST_STORE_PASSWORD(Constants.REST_TRUST_STORE_PASSWORD),
+
+    /** REST key store path property name. */
+    REST_KEY_STORE_PATH(Constants.REST_KEY_STORE_PATH),
+
+    /** REST key store password property name. */
+    REST_KEY_STORE_PASSWORD(Constants.REST_KEY_STORE_PASSWORD),
+
+    /** Default JDBC URL property name. */
+    JDBC_URL(Constants.JDBC_URL);
+
+    private final String value;
+
+    public String value() {
+        return value;
+    }
+
+    /** Constants for CLI config. */
+    public static final class Constants {
+        public static final String CLUSTER_URL = "ignite.cluster-endpoint-url";
+
+        public static final String LAST_CONNECTED_URL = 
"ignite.last-connected-url";
+
+        public static final String REST_TRUST_STORE_PATH = 
"ignite.rest.trust-store.path";
+
+        public static final String REST_TRUST_STORE_PASSWORD = 
"ignite.rest.trust-store.password";
+
+        public static final String REST_KEY_STORE_PATH = 
"ignite.rest.key-store.path";
+
+        public static final String REST_KEY_STORE_PASSWORD = 
"ignite.rest.key-store.password";
+
+        public static final String JDBC_URL = "ignite.jdbc-url";
+
+        /** Environment variable which points to the base directory for 
configuration files according to XDG spec. */
+        private static final String XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
+
+        /** Subdirectory name under the base configuration directory for 
ignite configuration files. */
+        private static final String PARENT_FOLDER_NAME = "ignitecli";
+
+        /** Main configuration file name. */
+        private static final String CONFIG_FILE_NAME = "defaults";
+
+        /** Environment variable which points to the configuration file. */
+        private static final String IGNITE_CLI_CONFIG_FILE = 
"IGNITE_CLI_CONFIG_FILE";
+
+        /** Environment variable which points to the CLI logs folder. */
+        public static final String IGNITE_CLI_LOGS_DIR = "IGNITE_CLI_LOGS_DIR";
+
+        /** Current profile property name. */
+        public static final String CURRENT_PROFILE = "current_profile";
+    }
+
+    CliConfigKeys(String value) {
+        this.value = value;
+    }
+
+    /**
+     * Gets the {@link File} with user-specific configuration file.
+     * The file location can be overridden using {@code 
IGNITE_CLI_CONFIG_FILE} environment variable,
+     * otherwise base directory is specified by the
+     * <a 
href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html";>XDG
 Base Directory Specification</a>
+     * and the configuration file name is {@code ignitecli/defaults} under the 
base directory.
+     *
+     * @return configuration file.
+     */
+    public static File getConfigFile() {
+        String configFile = System.getenv(Constants.IGNITE_CLI_CONFIG_FILE);
+        if (configFile != null) {
+            return new File(configFile);
+        }
+        return 
getConfigRoot().resolve(Constants.PARENT_FOLDER_NAME).resolve(Constants.CONFIG_FILE_NAME).toFile();
+    }
+
+    private static Path getConfigRoot() {
+        String xdgConfigHome = System.getenv(Constants.XDG_CONFIG_HOME);
+        if (xdgConfigHome != null) {
+            return Path.of(xdgConfigHome);
+        } else {
+            return Path.of(System.getProperty("user.home"), ".config");
+        }
+    }
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ConfigConstants.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ConfigConstants.java
deleted file mode 100644
index 3ded73b0c3..0000000000
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ConfigConstants.java
+++ /dev/null
@@ -1,100 +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.internal.cli.config;
-
-import java.io.File;
-import java.nio.file.Path;
-
-/**
- * Util class for config CLI.
- */
-public final class ConfigConstants {
-    /**
-     * Environment variable which points to the base directory for 
configuration files according to XDG spec.
-     */
-    private static final String XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
-
-    /**
-     * Subdirectory name under the base configuration directory for ignite 
configuration files.
-     */
-    private static final String PARENT_FOLDER_NAME = "ignitecli";
-
-    /**
-     * Main configuration file name.
-     */
-    private static final String CONFIG_FILE_NAME = "defaults";
-
-    /**
-     * Environment variable which points to the configuration file.
-     */
-    private static final String IGNITE_CLI_CONFIG_FILE = 
"IGNITE_CLI_CONFIG_FILE";
-
-    /**
-     * Environment variable which points to the CLI logs folder.
-     */
-    public static final String IGNITE_CLI_LOGS_DIR = "IGNITE_CLI_LOGS_DIR";
-
-    /**
-     * Current profile property name.
-     */
-    public static final String CURRENT_PROFILE = "current_profile";
-
-    /**
-     * Default cluster or node URL property name.
-     */
-    public static final String CLUSTER_URL = "ignite.cluster-endpoint-url";
-
-    /**
-     * Default JDBC URL property name.
-     */
-    public static final String JDBC_URL = "ignite.jdbc-url";
-
-    /**
-     * Last connected URL property name.
-     */
-    public static final String LAST_CONNECTED_URL = 
"ignite.last-connected-url";
-
-    private ConfigConstants() {
-    }
-
-    /**
-     * Gets the {@link File} with user-specific configuration file.
-     * The file location can be overridden using {@code 
IGNITE_CLI_CONFIG_FILE} environment variable,
-     * otherwise base directory is specified by the
-     * <a 
href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html";>XDG
 Base Directory Specification</a>
-     * and the configuration file name is {@code ignitecli/defaults} under the 
base directory.
-     *
-     * @return configuration file.
-     */
-    public static File getConfigFile() {
-        String configFile = System.getenv(IGNITE_CLI_CONFIG_FILE);
-        if (configFile != null) {
-            return new File(configFile);
-        }
-        return 
getConfigRoot().resolve(PARENT_FOLDER_NAME).resolve(CONFIG_FILE_NAME).toFile();
-    }
-
-    private static Path getConfigRoot() {
-        String xdgConfigHome = System.getenv(XDG_CONFIG_HOME);
-        if (xdgConfigHome != null) {
-            return Path.of(xdgConfigHome);
-        } else {
-            return Path.of(System.getProperty("user.home"), ".config");
-        }
-    }
-}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
index 89070121ed..a509a75c96 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
@@ -17,9 +17,9 @@
 
 package org.apache.ignite.internal.cli.config.ini;
 
-import static 
org.apache.ignite.internal.cli.config.ConfigConstants.CLUSTER_URL;
-import static 
org.apache.ignite.internal.cli.config.ConfigConstants.CURRENT_PROFILE;
-import static org.apache.ignite.internal.cli.config.ConfigConstants.JDBC_URL;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.CLUSTER_URL;
+import static 
org.apache.ignite.internal.cli.config.CliConfigKeys.Constants.CURRENT_PROFILE;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.JDBC_URL;
 
 import java.io.File;
 import java.io.IOException;
@@ -117,8 +117,8 @@ public class IniConfigManager implements ConfigManager {
             IniFile ini = new IniFile(file);
             ini.getTopLevelSection().setProperty("current_profile", 
DEFAULT_PROFILE_NAME);
             IniSection defaultSection = 
ini.createSection(DEFAULT_PROFILE_NAME);
-            defaultSection.setProperty(CLUSTER_URL, "http://localhost:10300";);
-            defaultSection.setProperty(JDBC_URL, 
"jdbc:ignite:thin://127.0.0.1:10800");
+            defaultSection.setProperty(CLUSTER_URL.value(), 
"http://localhost:10300";);
+            defaultSection.setProperty(JDBC_URL.value(), 
"jdbc:ignite:thin://127.0.0.1:10800");
             ini.store();
             return ini;
         } catch (IOException e) {
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientBuilder.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientBuilder.java
new file mode 100644
index 0000000000..6cb9de4a1a
--- /dev/null
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientBuilder.java
@@ -0,0 +1,148 @@
+/*
+ * 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.cli.core;
+
+import static org.apache.ignite.internal.util.StringUtils.nullOrBlank;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import okhttp3.OkHttpClient;
+import okhttp3.OkHttpClient.Builder;
+import okhttp3.internal.tls.OkHostnameVerifier;
+import org.apache.ignite.rest.client.invoker.ApiClient;
+import org.jetbrains.annotations.NotNull;
+
+class ApiClientBuilder {
+    private String basePath;
+    private String keyStorePath;
+    private String keyStorePassword;
+    private String trustStorePath;
+    private String trustStorePassword;
+
+    static ApiClientBuilder create() {
+        return new ApiClientBuilder();
+    }
+
+    ApiClientBuilder basePath(String basePath) {
+        this.basePath = basePath;
+        return this;
+    }
+
+    ApiClientBuilder keyStorePath(String keyStorePath) {
+        this.keyStorePath = keyStorePath;
+        return this;
+    }
+
+    ApiClientBuilder keyStorePassword(String keyStorePassword) {
+        this.keyStorePassword = keyStorePassword;
+        return this;
+    }
+
+    ApiClientBuilder trustStorePath(String trustStorePath) {
+        this.trustStorePath = trustStorePath;
+        return this;
+    }
+
+    ApiClientBuilder trustStorePassword(String trustStorePassword) {
+        this.trustStorePassword = trustStorePassword;
+        return this;
+    }
+
+    ApiClient build() throws CertificateException,
+            NoSuchAlgorithmException,
+            KeyStoreException,
+            IOException,
+            UnrecoverableKeyException,
+            KeyManagementException {
+
+        Builder builder = new Builder();
+
+        if (!nullOrBlank(keyStorePath) || !nullOrBlank(trustStorePath)) {
+            applySslSettings(builder);
+        }
+
+        OkHttpClient okHttpClient = builder.build();
+
+        return new ApiClient(okHttpClient)
+                .setBasePath(basePath);
+    }
+
+    private Builder applySslSettings(Builder builder) throws 
UnrecoverableKeyException,
+            CertificateException,
+            NoSuchAlgorithmException,
+            KeyStoreException,
+            IOException,
+            KeyManagementException {
+
+        TrustManagerFactory trustManagerFactory = trustManagerFactory();
+        KeyManagerFactory keyManagerFactory = keyManagerFactory();
+
+        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+        KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
+
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(keyManagers, trustManagers, new SecureRandom());
+        return builder.sslSocketFactory(sslContext.getSocketFactory(), 
(X509TrustManager) trustManagers[0])
+                .hostnameVerifier(OkHostnameVerifier.INSTANCE);
+    }
+
+    @NotNull
+    private KeyManagerFactory keyManagerFactory()
+            throws NoSuchAlgorithmException, KeyStoreException, 
UnrecoverableKeyException, CertificateException, IOException {
+        KeyManagerFactory keyManagerFactory = 
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+
+        if (nullOrBlank(keyStorePath)) {
+            keyManagerFactory.init(null, null);
+        } else {
+            char[] password = keyStorePassword == null ? null : 
keyStorePassword.toCharArray();
+            KeyStore keyStore = KeyStore.getInstance(new File(keyStorePath), 
password);
+            keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
+        }
+
+        return keyManagerFactory;
+    }
+
+    @NotNull
+    private TrustManagerFactory trustManagerFactory()
+            throws NoSuchAlgorithmException, KeyStoreException, 
CertificateException, IOException {
+        TrustManagerFactory trustManagerFactory = 
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+
+        if (nullOrBlank(trustStorePath)) {
+            trustManagerFactory.init((KeyStore) null);
+        } else {
+            char[] password = trustStorePassword == null ? null : 
trustStorePassword.toCharArray();
+            KeyStore trustStore = KeyStore.getInstance(new 
File(trustStorePath), password);
+            trustManagerFactory.init(trustStore);
+        }
+
+        return trustManagerFactory;
+    }
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientFactory.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientFactory.java
index bcd7595ae6..aaf9218667 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientFactory.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientFactory.java
@@ -17,9 +17,18 @@
 
 package org.apache.ignite.internal.cli.core;
 
+import static 
org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PASSWORD;
+import static 
org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PATH;
+import static 
org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PASSWORD;
+import static 
org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PATH;
+
+import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import org.apache.ignite.internal.cli.config.ConfigManager;
+import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
+import org.apache.ignite.internal.cli.core.exception.IgniteCliException;
 import org.apache.ignite.internal.cli.logger.CliLoggers;
 import org.apache.ignite.rest.client.invoker.ApiClient;
 
@@ -28,8 +37,16 @@ import org.apache.ignite.rest.client.invoker.ApiClient;
  */
 @Singleton
 public class ApiClientFactory {
+
+    @Inject
+    private final ConfigManagerProvider configManagerProvider;
+
     private final Map<String, ApiClient> clients = new ConcurrentHashMap<>();
 
+    public ApiClientFactory(ConfigManagerProvider configManagerProvider) {
+        this.configManagerProvider = configManagerProvider;
+    }
+
     /**
      * Gets {@link ApiClient} for the base path.
      *
@@ -37,8 +54,23 @@ public class ApiClientFactory {
      * @return created API client.
      */
     public ApiClient getClient(String path) {
-        ApiClient apiClient = clients.computeIfAbsent(path, s -> new 
ApiClient().setBasePath(s));
+        ApiClient apiClient = clients.computeIfAbsent(path, 
this::createClient);
         CliLoggers.addApiClient(path, apiClient);
         return apiClient;
     }
+
+    private ApiClient createClient(String path) {
+        try {
+            ConfigManager configManager = configManagerProvider.get();
+            return ApiClientBuilder.create()
+                    .basePath(path)
+                    
.keyStorePath(configManager.getCurrentProperty(REST_KEY_STORE_PATH.value()))
+                    
.keyStorePassword(configManager.getCurrentProperty(REST_KEY_STORE_PASSWORD.value()))
+                    
.trustStorePath(configManager.getCurrentProperty(REST_TRUST_STORE_PATH.value()))
+                    
.trustStorePassword(configManager.getCurrentProperty(REST_TRUST_STORE_PASSWORD.value()))
+                    .build();
+        } catch (Exception e) {
+            throw new IgniteCliException("Couldn't build REST client", e);
+        }
+    }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
index 79b15f6a0c..a3f99a0098 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
@@ -23,6 +23,7 @@ import java.net.ConnectException;
 import java.net.UnknownHostException;
 import java.util.List;
 import java.util.stream.Collectors;
+import javax.net.ssl.SSLException;
 import org.apache.ignite.internal.cli.core.exception.ExceptionHandler;
 import org.apache.ignite.internal.cli.core.exception.ExceptionWriter;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
@@ -60,6 +61,11 @@ public class IgniteCliApiExceptionHandler implements 
ExceptionHandler<IgniteCliA
                         .header("Node unavailable")
                         .details("Could not connect to node with URL %s", 
UiElements.url(e.getUrl()))
                         .verbose(apiCause.getMessage());
+            } else if (apiCause instanceof SSLException) {
+                errorComponentBuilder
+                        .header("SSL error")
+                        .details("Could not connect to node with URL %s. Check 
SSL configuration", UiElements.url(e.getUrl()))
+                        .verbose(apiCause.getMessage());
             } else if (apiCause != null) {
                 errorComponentBuilder.header(apiCause.getMessage());
             } else {
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/SessionDefaultValueProvider.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/SessionDefaultValueProvider.java
index eee0fd62b4..ad740701d8 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/SessionDefaultValueProvider.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/SessionDefaultValueProvider.java
@@ -19,7 +19,7 @@ package org.apache.ignite.internal.cli.core.repl;
 
 import jakarta.inject.Singleton;
 import java.util.Objects;
-import org.apache.ignite.internal.cli.config.ConfigConstants;
+import org.apache.ignite.internal.cli.config.CliConfigKeys;
 import org.apache.ignite.internal.cli.config.ConfigDefaultValueProvider;
 import picocli.CommandLine.IDefaultValueProvider;
 import picocli.CommandLine.Model.ArgSpec;
@@ -49,7 +49,7 @@ public class SessionDefaultValueProvider implements 
IDefaultValueProvider {
     public String defaultValue(ArgSpec argSpec) throws Exception {
         SessionInfo sessionInfo = session.info();
         if (sessionInfo != null) {
-            if (Objects.equals(argSpec.descriptionKey(), 
ConfigConstants.JDBC_URL)) {
+            if (Objects.equals(argSpec.descriptionKey(), 
CliConfigKeys.JDBC_URL)) {
                 return sessionInfo.jdbcUrl();
             }
         }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
index 515ab94aa9..4d8cc62e96 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
@@ -17,8 +17,10 @@
 
 package org.apache.ignite.internal.cli.core.repl.completer;
 
+import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
 import org.apache.ignite.internal.cli.commands.Options;
+import 
org.apache.ignite.internal.cli.core.repl.completer.cli.CliConfigDynamicCompleterFactory;
 import 
org.apache.ignite.internal.cli.core.repl.completer.cluster.ClusterUrlDynamicCompleterFactory;
 import 
org.apache.ignite.internal.cli.core.repl.completer.filter.ExclusionsCompleterFilter;
 import 
org.apache.ignite.internal.cli.core.repl.completer.hocon.ClusterConfigDynamicCompleterFactory;
@@ -31,26 +33,18 @@ import 
org.apache.ignite.internal.cli.core.repl.completer.path.FilePathCompleter
 @Singleton
 public class DynamicCompleterActivationPoint {
 
-    private final NodeNameDynamicCompleterFactory 
nodeNameDynamicCompleterFactory;
-    private final ClusterConfigDynamicCompleterFactory 
clusterConfigDynamicCompleterFactory;
-    private final NodeConfigDynamicCompleterFactory 
nodeConfigDynamicCompleterFactory;
-    private final ClusterUrlDynamicCompleterFactory 
clusterUrlDynamicCompleterFactory;
-    private final JdbcUrlDynamicCompleterFactory 
jdbcUrlDynamicCompleterFactory;
-
-    /** Main constructor. */
-    public DynamicCompleterActivationPoint(
-            NodeNameDynamicCompleterFactory nodeNameDynamicCompleterFactory,
-            ClusterConfigDynamicCompleterFactory 
clusterConfigDynamicCompleterFactory,
-            NodeConfigDynamicCompleterFactory 
nodeConfigDynamicCompleterFactory,
-            ClusterUrlDynamicCompleterFactory 
clusterUrlDynamicCompleterFactory,
-            JdbcUrlDynamicCompleterFactory jdbcUrlDynamicCompleterFactory) {
-        this.nodeNameDynamicCompleterFactory = nodeNameDynamicCompleterFactory;
-        this.clusterConfigDynamicCompleterFactory = 
clusterConfigDynamicCompleterFactory;
-        this.nodeConfigDynamicCompleterFactory = 
nodeConfigDynamicCompleterFactory;
-        this.clusterUrlDynamicCompleterFactory = 
clusterUrlDynamicCompleterFactory;
-        this.jdbcUrlDynamicCompleterFactory = jdbcUrlDynamicCompleterFactory;
-    }
-
+    @Inject
+    private NodeNameDynamicCompleterFactory nodeNameDynamicCompleterFactory;
+    @Inject
+    private ClusterConfigDynamicCompleterFactory 
clusterConfigDynamicCompleterFactory;
+    @Inject
+    private NodeConfigDynamicCompleterFactory 
nodeConfigDynamicCompleterFactory;
+    @Inject
+    private ClusterUrlDynamicCompleterFactory 
clusterUrlDynamicCompleterFactory;
+    @Inject
+    private JdbcUrlDynamicCompleterFactory jdbcUrlDynamicCompleterFactory;
+    @Inject
+    private CliConfigDynamicCompleterFactory cliConfigDynamicCompleterFactory;
 
     /**
      * Registers all dynamic completers in given {@link 
DynamicCompleterRegistry}.
@@ -117,5 +111,13 @@ public class DynamicCompleterActivationPoint {
                         .exclusiveEnableOptions().build(),
                 clusterUrlDynamicCompleterFactory
         );
+
+        registry.register(
+                CompleterConf.builder()
+                        .command("cli", "config", "set")
+                        .command("cli", "config", "get")
+                        .build(),
+                cliConfigDynamicCompleterFactory
+        );
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CachedConfigManagerProvider.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/cli/CliConfigDynamicCompleterFactory.java
similarity index 50%
copy from 
modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CachedConfigManagerProvider.java
copy to 
modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/cli/CliConfigDynamicCompleterFactory.java
index c00c2353d7..fd54ecab5d 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CachedConfigManagerProvider.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/cli/CliConfigDynamicCompleterFactory.java
@@ -15,20 +15,25 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.cli.config;
+package org.apache.ignite.internal.cli.core.repl.completer.cli;
 
 import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.config.ini.IniConfigManager;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import org.apache.ignite.internal.cli.config.CliConfigKeys;
+import org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleter;
+import 
org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleterFactory;
+import 
org.apache.ignite.internal.cli.core.repl.completer.StringDynamicCompleter;
 
-/**
- * Provider for {@link ConfigManager}.
- */
+/** Dynamic completer for CLI config keys. */
 @Singleton
-public class CachedConfigManagerProvider implements ConfigManagerProvider {
-    private final ConfigManager configManager = new 
IniConfigManager(ConfigConstants.getConfigFile());
+public class CliConfigDynamicCompleterFactory implements 
DynamicCompleterFactory {
+    private final StringDynamicCompleter completer = new 
StringDynamicCompleter(
+            
Arrays.stream(CliConfigKeys.values()).map(CliConfigKeys::value).collect(Collectors.toSet())
+    );
 
     @Override
-    public ConfigManager get() {
-        return configManager;
+    public DynamicCompleter getDynamicCompleter(String[] words) {
+        return completer;
     }
 }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigSetCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigSetCommandTest.java
index aac53fac00..0cbc173af9 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigSetCommandTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/cliconfig/CliConfigSetCommandTest.java
@@ -20,7 +20,7 @@ package org.apache.ignite.internal.cli.commands.cliconfig;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertAll;
 
-import org.apache.ignite.internal.cli.config.ConfigConstants;
+import org.apache.ignite.internal.cli.config.CliConfigKeys;
 import org.apache.ignite.internal.cli.config.ConfigManager;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
@@ -61,13 +61,13 @@ class CliConfigSetCommandTest extends 
CliConfigCommandTestBase {
     @DisplayName("Sets single value")
     void singleKeySet() {
         // When executed with key
-        execute(ConfigConstants.CLUSTER_URL + "=" + "test");
+        execute(CliConfigKeys.CLUSTER_URL.value() + "=" + "test");
 
         assertAll(
                 this::assertExitCodeIsZero,
                 this::assertOutputIsEmpty,
                 this::assertErrOutputIsEmpty,
-                () -> 
assertThat(configManagerProvider.get().getCurrentProperty(ConfigConstants.CLUSTER_URL)).isEqualTo("test")
+                () -> 
assertThat(configManagerProvider.get().getCurrentProperty(CliConfigKeys.CLUSTER_URL.value())).isEqualTo("test")
         );
     }
 
@@ -83,8 +83,8 @@ class CliConfigSetCommandTest extends 
CliConfigCommandTestBase {
                 this::assertExitCodeIsZero,
                 this::assertOutputIsEmpty,
                 this::assertErrOutputIsEmpty,
-                () -> 
assertThat(configManager.getCurrentProperty(ConfigConstants.CLUSTER_URL)).isEqualTo("test"),
-                () -> 
assertThat(configManager.getCurrentProperty(ConfigConstants.JDBC_URL)).isEqualTo("test2")
+                () -> 
assertThat(configManager.getCurrentProperty(CliConfigKeys.CLUSTER_URL.value())).isEqualTo("test"),
+                () -> 
assertThat(configManager.getCurrentProperty(CliConfigKeys.JDBC_URL.value())).isEqualTo("test2")
         );
     }
 
@@ -100,10 +100,10 @@ class CliConfigSetCommandTest extends 
CliConfigCommandTestBase {
                 this::assertExitCodeIsZero,
                 this::assertOutputIsEmpty,
                 this::assertErrOutputIsEmpty,
-                () -> 
assertThat(configManager.getCurrentProperty(ConfigConstants.CLUSTER_URL)).isNotEqualTo("test"),
-                () -> 
assertThat(configManager.getCurrentProperty(ConfigConstants.JDBC_URL)).isNotEqualTo("test2"),
-                () -> 
assertThat(configManager.getProfile("owner").getProperty(ConfigConstants.CLUSTER_URL)).isEqualTo("test"),
-                () -> 
assertThat(configManager.getProfile("owner").getProperty(ConfigConstants.JDBC_URL)).isEqualTo("test2"));
+                () -> 
assertThat(configManager.getCurrentProperty(CliConfigKeys.CLUSTER_URL.value())).isNotEqualTo("test"),
+                () -> 
assertThat(configManager.getCurrentProperty(CliConfigKeys.JDBC_URL.value())).isNotEqualTo("test2"),
+                () -> 
assertThat(configManager.getProfile("owner").getProperty(CliConfigKeys.CLUSTER_URL.value())).isEqualTo("test"),
+                () -> 
assertThat(configManager.getProfile("owner").getProperty(CliConfigKeys.JDBC_URL.value())).isEqualTo("test2"));
     }
 
     @Test
@@ -116,7 +116,7 @@ class CliConfigSetCommandTest extends 
CliConfigCommandTestBase {
                 () -> assertExitCodeIs(1),
                 this::assertOutputIsEmpty,
                 () -> assertErrOutputContains("Profile notExist not found"),
-                () -> 
assertThat(configManager.getCurrentProperty(ConfigConstants.CLUSTER_URL)).isNotEqualTo("test")
+                () -> 
assertThat(configManager.getCurrentProperty(CliConfigKeys.CLUSTER_URL.value())).isNotEqualTo("test")
         );
     }
 }

Reply via email to