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