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 eccada42fa IGNITE-20705 Switch user context when reconnecting (#2762)
eccada42fa is described below
commit eccada42faded5c109b95f4d7c3454b1c9e0642d
Author: Vadim Pakhnushev <[email protected]>
AuthorDate: Wed Nov 1 17:16:20 2023 +0300
IGNITE-20705 Switch user context when reconnecting (#2762)
---
...liCommandTestNotInitializedIntegrationBase.java | 5 +-
...tConnectWithBasicAuthenticationCommandTest.java | 144 ++++++++++++++++++++-
.../questions/ItConnectToBasicAuthClusterTest.java | 4 +-
.../ItConnectToSslAndAuthClusterTest.java | 19 +--
.../internal/cli/call/connect/ConnectCall.java | 29 ++++-
.../cli/call/connect/ConnectCallInput.java | 6 +-
.../cli/call/connect/ConnectWizardCall.java | 24 +++-
.../cli/call/connect/ConnectionChecker.java | 5 -
.../cli/commands/connect/ConnectOptions.java | 8 +-
.../cli/commands/connect/ConnectReplCommand.java | 28 ++--
.../internal/cli/commands/node/NodeNameOrUrl.java | 33 -----
.../questions/ConnectToClusterQuestion.java | 59 ++++++---
.../core/converters/NodeNameOrUrlConverter.java | 61 ---------
.../ignite/internal/cli/core/repl/Session.java | 10 +-
.../completer/DynamicCompleterActivationPoint.java | 6 -
.../repl/completer/DynamicCompletionInsider.java | 2 -
.../cli/core/repl/executor/ReplExecutorImpl.java | 10 +-
.../repl/executor/ReplExecutorProviderImpl.java | 6 +-
.../internal/cli/core/style/AnsiStringSupport.java | 2 +
.../cli/core/style/element/UiElements.java | 5 +
.../internal/cli/core/style/element/UiString.java | 2 +-
.../internal/cli/commands/CliCommandTestBase.java | 5 +-
.../cli/commands/UrlOptionsNegativeTest.java | 7 +-
.../cli/commands/connect/ConnectCommandTest.java | 5 +-
.../completer/DynamicCompleterRegistryTest.java | 10 +-
.../completer/DynamicCompletionInsiderTest.java | 6 -
.../cluster-configuration-with-enabled-auth.conf | 6 +
27 files changed, 284 insertions(+), 223 deletions(-)
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
index b0468651d8..cf350a9caf 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
@@ -27,9 +27,7 @@ import java.io.StringWriter;
import org.apache.ignite.internal.cli.CliIntegrationTestBase;
import
org.apache.ignite.internal.cli.commands.cliconfig.TestConfigManagerHelper;
import
org.apache.ignite.internal.cli.commands.cliconfig.TestConfigManagerProvider;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
import org.apache.ignite.internal.cli.config.ConfigDefaultValueProvider;
-import org.apache.ignite.internal.cli.core.converters.NodeNameOrUrlConverter;
import org.apache.ignite.internal.cli.core.repl.EventListeningActivationPoint;
import
org.apache.ignite.internal.cli.core.repl.context.CommandLineContextProvider;
import org.apache.ignite.internal.cli.core.repl.registry.JdbcUrlRegistry;
@@ -83,8 +81,7 @@ public class CliCommandTestNotInitializedIntegrationBase
extends CliIntegrationT
@BeforeEach
public void setUp() {
configManagerProvider.setConfigFile(TestConfigManagerHelper.createIntegrationTestsConfig());
- cmd = new CommandLine(getCommandClass(), new MicronautFactory(context))
- .registerConverter(NodeNameOrUrl.class, new
NodeNameOrUrlConverter(nodeNameRegistry));
+ cmd = new CommandLine(getCommandClass(), new
MicronautFactory(context));
cmd.setDefaultValueProvider(configDefaultValueProvider);
eventListeningActivationPoint.subscribe();
resetOutput();
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectWithBasicAuthenticationCommandTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectWithBasicAuthenticationCommandTest.java
index 89f8f8c28b..19c32ed0f9 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectWithBasicAuthenticationCommandTest.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectWithBasicAuthenticationCommandTest.java
@@ -246,8 +246,7 @@ class ItConnectWithBasicAuthenticationCommandTest extends
ItConnectToClusterTest
// Given basic authentication is NOT configured in config file
configManagerProvider.setConfigFile(createIntegrationTestsConfig());
// Given prompt before connect
- String promptBefore = getPrompt();
- assertThat(promptBefore).isEqualTo("[disconnected]> ");
+ assertThat(getPrompt()).isEqualTo("[disconnected]> ");
// And answer is "y"
bindAnswers("y");
@@ -292,4 +291,145 @@ class ItConnectWithBasicAuthenticationCommandTest extends
ItConnectToClusterTest
configManagerProvider.get().getCurrentProperty(Constants.BASIC_AUTHENTICATION_PASSWORD))
);
}
+
+ @Test
+ void reconnectWithDifferentAuthenticationParameters() throws IOException {
+ // Given basic authentication is configured in config file
+ configManagerProvider.setConfigFile(createIntegrationTestsConfig(),
createJdbcTestsBasicSecretConfig());
+
+ // Given prompt before connect
+ assertThat(getPrompt()).isEqualTo("[disconnected]> ");
+
+ // When connect with auth parameters
+ execute("connect", "--username", "admin", "--password", "password");
+
+ // Then
+ assertAll(
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("Connected to
http://localhost:10300")
+ );
+
+ // And prompt shows username and node name
+ assertThat(getPrompt()).isEqualTo("[admin:" + nodeName() + "]> ");
+
+ resetOutput();
+
+ // Should ask user to reconnect with different user, answer "y"
+ bindAnswers("y");
+
+ // When connect with different auth parameters
+ execute("connect", "--username", "admin1", "--password", "password");
+
+ // Then
+ assertAll(
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("Connected to
http://localhost:10300")
+ );
+
+ // And prompt shows username and node name
+ assertThat(getPrompt()).isEqualTo("[admin1:" + nodeName() + "]> ");
+ }
+
+ @Test
+ void reconnectWithAuthenticationParametersOverridingConfig() throws
IOException {
+ // Given basic authentication is configured in config file
+ configManagerProvider.setConfigFile(createIntegrationTestsConfig(),
createJdbcTestsBasicSecretConfig());
+
+ // Given prompt before connect
+ assertThat(getPrompt()).isEqualTo("[disconnected]> ");
+
+ // When connect without auth parameters
+ execute("connect");
+
+ // Then
+ assertAll(
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("Connected to
http://localhost:10300")
+ );
+
+ // And prompt shows username and node name
+ assertThat(getPrompt()).isEqualTo("[admin:" + nodeName() + "]> ");
+
+ resetOutput();
+
+ // Should ask user to reconnect with different user, answer "y"
+ bindAnswers("y");
+
+ // When connect with different auth parameters
+ execute("connect", "--username", "admin1", "--password", "password");
+
+ // Then
+ assertAll(
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("Connected to
http://localhost:10300")
+ );
+
+ // And prompt shows username and node name
+ assertThat(getPrompt()).isEqualTo("[admin1:" + nodeName() + "]> ");
+ }
+
+ @Test
+ void reconnectWithAuthenticationParametersFromConfig() throws IOException {
+ // Given basic authentication is configured in config file
+ configManagerProvider.setConfigFile(createIntegrationTestsConfig(),
createJdbcTestsBasicSecretConfig());
+
+ // Given prompt before connect
+ assertThat(getPrompt()).isEqualTo("[disconnected]> ");
+
+ // When connect with auth parameters overriding config
+ execute("connect", "--username", "admin1", "--password", "password");
+
+ // Then
+ assertAll(
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("Connected to
http://localhost:10300")
+ );
+
+ // And prompt shows username from parameters and node name
+ assertThat(getPrompt()).isEqualTo("[admin1:" + nodeName() + "]> ");
+
+ resetOutput();
+
+ // Should ask user to reconnect with different user, answer "y"
+ bindAnswers("y");
+
+ // When connect without auth parameters
+ execute("connect");
+
+ // Then
+ assertAll(
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("Connected to
http://localhost:10300")
+ );
+
+ // And prompt shows username from config and node name
+ assertThat(getPrompt()).isEqualTo("[admin:" + nodeName() + "]> ");
+ }
+
+ @Test
+ void shouldStoreCredentialsFromAnswer() throws IOException {
+ // Given basic authentication is NOT configured in config file
+ configManagerProvider.setConfigFile(createIntegrationTestsConfig());
+ // Given prompt before connect
+ assertThat(getPrompt()).isEqualTo("[disconnected]> ");
+
+ // And answer "y" to question on auth error, enter username and
password and answer "y" to save config question
+ bindAnswers("y", "admin", "password", "y");
+
+ // And connected
+ execute("connect", "--username", "admin", "--password",
"wrong-password");
+
+ // And output is
+ assertAll(
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputIs(
+ "Config saved" + System.lineSeparator() + "Connected
to http://localhost:10300" + System.lineSeparator())
+ );
+
+ // And prompt shows username and node name
+ assertThat(getPrompt()).isEqualTo("[admin:" + nodeName() + "]> ");
+ // And correct values are stored in config
+ assertEquals("admin",
configManagerProvider.get().getCurrentProperty(Constants.BASIC_AUTHENTICATION_USERNAME));
+ assertEquals("password",
configManagerProvider.get().getCurrentProperty(Constants.BASIC_AUTHENTICATION_PASSWORD));
+ }
}
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToBasicAuthClusterTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToBasicAuthClusterTest.java
index 2fe83492bb..68eeb56ed1 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToBasicAuthClusterTest.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToBasicAuthClusterTest.java
@@ -42,8 +42,8 @@ class ItConnectToBasicAuthClusterTest extends
ItConnectToClusterTestBase {
assertThat(getPrompt()).isEqualTo("[disconnected]> ");
// And answer to the reconnect question is "y", to the auth
configuration question is "y",
- // username and password are provided
- bindAnswers("y", "y", "admin", "password");
+ // username and password are provided and answer to save
authentication is "y"
+ bindAnswers("y", "y", "admin", "password", "y");
// When asked the question
question.askQuestionOnReplStart();
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToSslAndAuthClusterTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToSslAndAuthClusterTest.java
index 90a3255b3c..1b785e6612 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToSslAndAuthClusterTest.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToSslAndAuthClusterTest.java
@@ -59,9 +59,10 @@ class ItConnectToSslAndAuthClusterTest extends
ItConnectToClusterTestBase {
stateConfigProvider.config =
TestStateConfigHelper.createLastConnectedSslDefault();
// And answer to the reconnect question is "y", to the SSL
configuration question is "y",
- // trust store path and password are provided and key store is not
configured
+ // trust store path and password are provided and answer to key store
configuration is "n",
+ // answer to auth configuration is "y", username and password is
provided and answer to save authentication is "y"
//ToDo: check question as well IGNITE-20324
- bindAnswers("y", "y", NodeConfig.resolvedTruststorePath,
NodeConfig.trustStorePassword, "n", "y", "admin", "password");
+ bindAnswers("y", "y", NodeConfig.resolvedTruststorePath,
NodeConfig.trustStorePassword, "n", "y", "admin", "password", "y");
// When asked the question
question.askQuestionOnReplStart();
@@ -257,9 +258,10 @@ class ItConnectToSslAndAuthClusterTest extends
ItConnectToClusterTestBase {
// And last connected URL is equal to the default URL
stateConfigProvider.config =
TestStateConfigHelper.createLastConnectedSslDefault();
- // And answer to the reconnect question is "y", to the SSL
configuration question is "y",
- // trust store path and password are provided and key store is not
configured
- bindAnswers("y", NodeConfig.resolvedTruststorePath,
NodeConfig.trustStorePassword, "n", "y", "admin", "password");
+ // And answer to the SSL configuration question is "y", trust store
path and password are provided
+ // and answer to key store configuration is "n", answer to auth
configuration is "y", username and password is provided
+ // and answer to save authentication is "y"
+ bindAnswers("y", NodeConfig.resolvedTruststorePath,
NodeConfig.trustStorePassword, "n", "y", "admin", "password", "y");
// When connect with auth parameters
execute("connect", "--username", "admin", "--password",
"wrong-password");
@@ -298,10 +300,11 @@ class ItConnectToSslAndAuthClusterTest extends
ItConnectToClusterTestBase {
// And last connected URL is equal to the default URL
stateConfigProvider.config =
TestStateConfigHelper.createLastConnectedSslDefault();
- // And answer to the reconnect question is "y", to the SSL
configuration question is "y",
- // trust store path and password are provided and key store is not
configured
+ // And answer to the SSL configuration question is "y", trust store
path and password are provided
+ // and answer to key store configuration is "n", answer to auth
configuration is "y", username and password is provided
+ // and answer to save authentication is "y"
//ToDo: check question as well IGNITE-20324
- bindAnswers("y", NodeConfig.resolvedTruststorePath,
NodeConfig.trustStorePassword, "n", "y", "admin", "password");
+ bindAnswers("y", NodeConfig.resolvedTruststorePath,
NodeConfig.trustStorePassword, "n", "y", "admin", "password", "y");
// When connect with auth parameters
execute("connect");
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 739c784492..45338cce21 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
@@ -17,11 +17,14 @@
package org.apache.ignite.internal.cli.call.connect;
+import static
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion.askQuestionToStoreCredentials;
+import static
org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_USERNAME;
import static org.apache.ignite.internal.util.StringUtils.nullOrBlank;
import io.micronaut.http.HttpStatus;
import jakarta.inject.Singleton;
import java.util.Objects;
+import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
import org.apache.ignite.internal.cli.core.call.Call;
import org.apache.ignite.internal.cli.core.call.CallOutput;
import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
@@ -54,16 +57,25 @@ public class ConnectCall implements Call<ConnectCallInput,
String> {
private final ConnectionChecker connectionChecker;
+ private final ConfigManagerProvider configManagerProvider;
+
/**
* Constructor.
*/
- public ConnectCall(Session session, ApiClientFactory clientFactory,
EventPublisher eventPublisher,
- ConnectSuccessCall connectSuccessCall, ConnectionChecker
connectionChecker) {
+ public ConnectCall(
+ Session session,
+ ApiClientFactory clientFactory,
+ EventPublisher eventPublisher,
+ ConnectSuccessCall connectSuccessCall,
+ ConnectionChecker connectionChecker,
+ ConfigManagerProvider configManagerProvider
+ ) {
this.session = session;
this.clientFactory = clientFactory;
this.eventPublisher = eventPublisher;
this.connectSuccessCall = connectSuccessCall;
this.connectionChecker = connectionChecker;
+ this.configManagerProvider = configManagerProvider;
}
@Override
@@ -71,15 +83,21 @@ public class ConnectCall implements Call<ConnectCallInput,
String> {
String nodeUrl = input.url();
SessionInfo sessionInfo = session.info();
if (sessionInfo != null && Objects.equals(sessionInfo.nodeUrl(),
nodeUrl)) {
- MessageUiComponent message = MessageUiComponent.fromMessage("You
are already connected to %s", UiElements.url(nodeUrl));
- return DefaultCallOutput.success(message.render());
+ // This username will be used for connect by the connection
checker.
+ String username = input.username() != null
+ ? input.username()
+ :
configManagerProvider.get().getCurrentProperty(BASIC_AUTHENTICATION_USERNAME.value());
+ if (Objects.equals(sessionInfo.username(), username)) {
+ MessageUiComponent message =
MessageUiComponent.fromMessage("You are already connected to %s",
UiElements.url(nodeUrl));
+ return DefaultCallOutput.success(message.render());
+ }
}
try {
// Try without authentication first to check whether the
authentication is enabled on the cluster.
sessionInfo = connectWithoutAuthentication(nodeUrl);
if (sessionInfo == null) {
// Try with authentication
- sessionInfo = connectionChecker.checkConnection(input);
+ sessionInfo = connectionChecker.checkConnection(input);
if (!nullOrBlank(input.username()) &&
!nullOrBlank(input.password())) {
// Use current credentials as default for api clients
ApiClientSettings clientSettings =
ApiClientSettings.builder()
@@ -96,6 +114,7 @@ public class ConnectCall implements Call<ConnectCallInput,
String> {
sessionInfo.nodeUrl()));
}
+ askQuestionToStoreCredentials(configManagerProvider.get(),
input.username(), input.password());
return connectSuccessCall.execute(sessionInfo);
} catch (Exception e) {
if (session.info() != null) {
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCallInput.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCallInput.java
index 207eea1e7d..e1460b310c 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCallInput.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCallInput.java
@@ -37,7 +37,7 @@ public class ConnectCallInput implements CallInput {
this.password = password;
}
- String url() {
+ public String url() {
return url;
}
@@ -47,7 +47,7 @@ public class ConnectCallInput implements CallInput {
* @return username
*/
@Nullable
- String username() {
+ public String username() {
return username;
}
@@ -57,7 +57,7 @@ public class ConnectCallInput implements CallInput {
* @return password
*/
@Nullable
- String password() {
+ public String password() {
return password;
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectWizardCall.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectWizardCall.java
index adfa179595..9e312de69d 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectWizardCall.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectWizardCall.java
@@ -17,12 +17,14 @@
package org.apache.ignite.internal.cli.call.connect;
+import static
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion.askQuestionOnAuthError;
import static
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion.askQuestionOnSslError;
+import static
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion.askQuestionToStoreCredentials;
import io.micronaut.http.HttpStatus;
import jakarta.inject.Singleton;
import javax.net.ssl.SSLException;
-import
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion;
+import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
import org.apache.ignite.internal.cli.core.call.Call;
import org.apache.ignite.internal.cli.core.call.CallOutput;
import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
@@ -44,13 +46,21 @@ public class ConnectWizardCall implements
Call<ConnectCallInput, String> {
private final ConnectionChecker connectionChecker;
+ private final ConfigManagerProvider configManagerProvider;
+
/**
* Constructor.
*/
- public ConnectWizardCall(ConnectCall connectCall, ConnectionChecker
connectionChecker, ConnectSuccessCall connectSuccessCall) {
+ public ConnectWizardCall(
+ ConnectCall connectCall,
+ ConnectionChecker connectionChecker,
+ ConnectSuccessCall connectSuccessCall,
+ ConfigManagerProvider configManagerProvider
+ ) {
this.connectCall = connectCall;
this.connectionChecker = connectionChecker;
this.connectSuccessCall = connectSuccessCall;
+ this.configManagerProvider = configManagerProvider;
}
@Override
@@ -84,6 +94,7 @@ public class ConnectWizardCall implements
Call<ConnectCallInput, String> {
SessionInfo sessionInfo =
connectionChecker.checkConnection(input, result.value());
connectionChecker.saveSettings(input, result.value());
+ askQuestionToStoreCredentials(configManagerProvider.get(),
input.username(), input.password());
return connectSuccessCall.execute(sessionInfo);
} catch (ApiException exception) {
Throwable apiCause = exception.getCause();
@@ -106,16 +117,19 @@ public class ConnectWizardCall implements
Call<ConnectCallInput, String> {
}
private CallOutput<String> configureAuth(ConnectCallInput input,
CallOutput<String> output) {
- Flowable<AuthConfig> result =
ConnectToClusterQuestion.askQuestionOnAuthError().start(Flowable.empty());
+ Flowable<AuthConfig> result =
askQuestionOnAuthError().start(Flowable.empty());
if (result.hasResult()) {
+ String username = result.value().username();
+ String password = result.value().password();
ConnectCallInput connectCallInput = ConnectCallInput.builder()
.url(input.url())
- .username(result.value().username())
- .password(result.value().password())
+ .username(username)
+ .password(password)
.build();
try {
SessionInfo sessionInfo =
connectionChecker.checkConnection(connectCallInput);
connectionChecker.saveSettings(connectCallInput, null);
+ askQuestionToStoreCredentials(configManagerProvider.get(),
username, password);
return connectSuccessCall.execute(sessionInfo);
} catch (ApiException e) {
return DefaultCallOutput.failure(new IgniteCliApiException(e,
input.url()));
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectionChecker.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectionChecker.java
index 3e7479773a..ae4ff40ce3 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectionChecker.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectionChecker.java
@@ -160,10 +160,5 @@ public class ConnectionChecker {
manager.setProperty(REST_KEY_STORE_PASSWORD.value(),
sslConfig.keyStorePassword());
}
}
-
- if (!nullOrBlank(callInput.username()) &&
!nullOrBlank(callInput.password())) {
- manager.setProperty(BASIC_AUTHENTICATION_USERNAME.value(),
callInput.username());
- manager.setProperty(BASIC_AUTHENTICATION_PASSWORD.value(),
callInput.password());
- }
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectOptions.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectOptions.java
index b6c6827918..ff6080c341 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectOptions.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectOptions.java
@@ -31,12 +31,12 @@ import picocli.CommandLine.Option;
*/
public class ConnectOptions {
- @Option(names = {USERNAME_OPTION,
- USERNAME_OPTION_SHORT}, description = USERNAME_OPTION_DESC,
required = true)
+ @Option(names = {USERNAME_OPTION, USERNAME_OPTION_SHORT}, description =
USERNAME_OPTION_DESC,
+ required = true, defaultValue = Option.NULL_VALUE)
private String username;
- @Option(names = {PASSWORD_OPTION,
- PASSWORD_OPTION_SHORT}, description = PASSWORD_OPTION_DESC,
required = true)
+ @Option(names = {PASSWORD_OPTION, PASSWORD_OPTION_SHORT}, description =
PASSWORD_OPTION_DESC,
+ required = true, defaultValue = Option.NULL_VALUE)
private String password;
public String username() {
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
index 964ca46e72..01d221bc7f 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
@@ -18,16 +18,16 @@
package org.apache.ignite.internal.cli.commands.connect;
import static
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_URL_KEY;
-import static
org.apache.ignite.internal.cli.commands.Options.Constants.NODE_URL_OR_NAME_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.NODE_URL_OPTION_DESC;
import jakarta.inject.Inject;
+import java.net.URL;
import org.apache.ignite.internal.cli.call.connect.ConnectCallInput;
import org.apache.ignite.internal.cli.call.connect.ConnectWizardCall;
import org.apache.ignite.internal.cli.commands.BaseCommand;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
import
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion;
+import org.apache.ignite.internal.cli.core.converters.UrlConverter;
import org.apache.ignite.internal.cli.core.flow.builder.Flows;
-import org.jetbrains.annotations.Nullable;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
import picocli.CommandLine.Parameters;
@@ -39,8 +39,8 @@ import picocli.CommandLine.Parameters;
public class ConnectReplCommand extends BaseCommand implements Runnable {
/** Node URL option. */
- @Parameters(description = NODE_URL_OR_NAME_DESC, descriptionKey =
CLUSTER_URL_KEY)
- private NodeNameOrUrl nodeNameOrUrl;
+ @Parameters(description = NODE_URL_OPTION_DESC, descriptionKey =
CLUSTER_URL_KEY, converter = UrlConverter.class)
+ private URL nodeUrl;
@ArgGroup(exclusive = false)
private ConnectOptions connectOptions;
@@ -54,30 +54,18 @@ public class ConnectReplCommand extends BaseCommand
implements Runnable {
/** {@inheritDoc} */
@Override
public void run() {
- question.askQuestionIfConnected(nodeNameOrUrl.stringUrl())
- .map(this::connectCallInput)
+ question.askQuestionIfConnected(connectCallInput(nodeUrl.toString()))
.then(Flows.fromCall(connectCall))
- .onSuccess(() ->
question.askQuestionToStoreCredentials(username(connectOptions),
password(connectOptions)))
.verbose(verbose)
.print()
.start();
}
- @Nullable
- private String username(ConnectOptions connectOptions) {
- return connectOptions != null ? connectOptions.username() : null;
- }
-
- @Nullable
- private String password(ConnectOptions connectOptions) {
- return connectOptions != null ? connectOptions.password() : null;
- }
-
private ConnectCallInput connectCallInput(String nodeUrl) {
return ConnectCallInput.builder()
.url(nodeUrl)
- .username(username(connectOptions))
- .password(password(connectOptions))
+ .username(connectOptions != null ? connectOptions.username() :
null)
+ .password(connectOptions != null ? connectOptions.password() :
null)
.build();
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeNameOrUrl.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeNameOrUrl.java
deleted file mode 100644
index 8ec6a73209..0000000000
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeNameOrUrl.java
+++ /dev/null
@@ -1,33 +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.commands.node;
-
-import java.net.URL;
-
-/** Node URL. */
-public class NodeNameOrUrl {
- private final URL nodeUrl;
-
- public NodeNameOrUrl(URL nodeUrl) {
- this.nodeUrl = nodeUrl;
- }
-
- public String stringUrl() {
- return nodeUrl.toString();
- }
-}
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 54af28a023..37abc792ec 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
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.cli.commands.questions;
import static
org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_PASSWORD;
import static
org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_USERNAME;
import static
org.apache.ignite.internal.cli.core.style.component.QuestionUiComponent.fromYesNoQuestion;
+import static
org.apache.ignite.internal.cli.core.style.element.UiElements.username;
import static org.apache.ignite.internal.util.StringUtils.nullOrBlank;
import jakarta.inject.Inject;
@@ -30,6 +31,7 @@ import
org.apache.ignite.internal.cli.call.connect.ConnectCallInput;
import org.apache.ignite.internal.cli.call.connect.ConnectWizardCall;
import org.apache.ignite.internal.cli.call.connect.SslConfig;
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 org.apache.ignite.internal.cli.config.StateConfigProvider;
import org.apache.ignite.internal.cli.core.flow.Flow;
@@ -97,41 +99,64 @@ public class ConnectToClusterQuestion {
}
/**
- * Ask if the user really wants to connect if we are already connected and
the URL is different.
+ * Ask if the user really wants to connect if we are already connected and
the URL or authentication parameters are different.
*
- * @param nodeUrl Node URL.
- * @return {@link FlowBuilder} instance which provides the node URL if we
are not connected or connected to different URL or interrupts
- * if user's answer is negative.
+ * @param input Node URL and authentication parameters.
+ * @return {@link FlowBuilder} instance which provides the connect call
input if we are not connected or user agreed to reconnect or
+ * interrupts if user's answer is negative.
*/
- public FlowBuilder<Void, String> askQuestionIfConnected(String nodeUrl) {
+ public FlowBuilder<Void, ConnectCallInput>
askQuestionIfConnected(ConnectCallInput input) {
SessionInfo sessionInfo = session.info();
- if (sessionInfo != null && !Objects.equals(sessionInfo.nodeUrl(),
nodeUrl)) {
- QuestionUiComponent question = fromYesNoQuestion(
- "You are already connected to the %s, do you want to
connect to the %s?",
- UiElements.url(sessionInfo.nodeUrl()),
UiElements.url(nodeUrl)
- );
- return Flows.acceptQuestion(question, () -> nodeUrl);
+ if (sessionInfo != null) {
+ if (!Objects.equals(sessionInfo.nodeUrl(), input.url())) {
+ return Flows.acceptQuestion(fromYesNoQuestion(
+ "You are already connected to the %s, do you want to
connect to the %s?",
+ UiElements.url(sessionInfo.nodeUrl()),
UiElements.url(input.url())
+ ), () -> input);
+ }
+
+ String oldUsername = sessionInfo.username();
+ // This username will be used for connect by the connection
checker.
+ String newUsername = input.username() != null
+ ? input.username()
+ :
configManagerProvider.get().getCurrentProperty(BASIC_AUTHENTICATION_USERNAME.value());
+
+ if (newUsername != null) {
+ if (oldUsername == null) {
+ return Flows.acceptQuestion(fromYesNoQuestion(
+ "You are already connected to the %s, do you want
to connect as %s?",
+ UiElements.url(sessionInfo.nodeUrl()),
username(newUsername)
+ ), () -> input);
+ }
+ if (!oldUsername.equals(newUsername)) {
+ return Flows.acceptQuestion(fromYesNoQuestion(
+ "You are already connected to the %s as %s, do you
want to connect as %s?",
+ UiElements.url(sessionInfo.nodeUrl()),
username(oldUsername), username(newUsername)
+ ), () -> input);
+ }
+ }
}
- return Flows.from(nodeUrl);
+ return Flows.from(input);
}
/**
* Ask if the user wants to store credentials in config.
*
+ * @param configManager Config manager.
* @param username username.
* @param password password
*/
- public void askQuestionToStoreCredentials(@Nullable String username,
@Nullable String password) {
+ public static void askQuestionToStoreCredentials(ConfigManager
configManager, @Nullable String username, @Nullable String password) {
if (!nullOrBlank(username) && !nullOrBlank(password)) {
- String storedUsername =
configManagerProvider.get().getCurrentProperty(BASIC_AUTHENTICATION_USERNAME.value());
- String storedPassword =
configManagerProvider.get().getCurrentProperty(BASIC_AUTHENTICATION_PASSWORD.value());
+ String storedUsername =
configManager.getCurrentProperty(BASIC_AUTHENTICATION_USERNAME.value());
+ String storedPassword =
configManager.getCurrentProperty(BASIC_AUTHENTICATION_PASSWORD.value());
// Ask question only if cli config has different values.
if (!username.equals(storedUsername) ||
!password.equals(storedPassword)) {
QuestionUiComponent question = fromYesNoQuestion("Remember
current credentials?");
Flows.acceptQuestion(question, () -> {
-
configManagerProvider.get().setProperty(BASIC_AUTHENTICATION_USERNAME.value(),
username);
-
configManagerProvider.get().setProperty(BASIC_AUTHENTICATION_PASSWORD.value(),
password);
+
configManager.setProperty(BASIC_AUTHENTICATION_USERNAME.value(), username);
+
configManager.setProperty(BASIC_AUTHENTICATION_PASSWORD.value(), password);
return "Config saved";
}).print().start();
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/NodeNameOrUrlConverter.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/NodeNameOrUrlConverter.java
deleted file mode 100644
index 558a4fbb8b..0000000000
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/NodeNameOrUrlConverter.java
+++ /dev/null
@@ -1,61 +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.core.converters;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.regex.Pattern;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
-import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
-import picocli.CommandLine;
-import picocli.CommandLine.TypeConversionException;
-
-/** Converter for {@link NodeNameOrUrl}. */
-public class NodeNameOrUrlConverter implements
CommandLine.ITypeConverter<NodeNameOrUrl> {
-
- private static final Pattern URL_PATTERN = Pattern.compile("^.*[/:].*");
-
- private final NodeNameRegistry nodeNameRegistry;
-
- public NodeNameOrUrlConverter(NodeNameRegistry nodeNameRegistry) {
- this.nodeNameRegistry = nodeNameRegistry;
- }
-
- private static URL stringToUrl(String str) {
- try {
- return new URL(str);
- } catch (MalformedURLException e) {
- throw new TypeConversionException("Invalid URL '" + str + "' (" +
e.getMessage() + ")");
- }
- }
-
- @Override
- public NodeNameOrUrl convert(String input) throws Exception {
- boolean isUrl = URL_PATTERN.matcher(input).matches();
- if (isUrl) {
- return new NodeNameOrUrl(stringToUrl(input));
- } else {
- return new
NodeNameOrUrl(stringToUrl(findNodeUrlByNodeName(input)));
- }
- }
-
- private String findNodeUrlByNodeName(String name) {
- return nodeNameRegistry.nodeUrlByName(name)
- .orElseThrow(() -> new TypeConversionException("Node " + name
+ " not found. Provide valid name or use URL"));
- }
-}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/Session.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/Session.java
index 0f5ec6b008..313cdcbd0d 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/Session.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/Session.java
@@ -19,7 +19,6 @@ package org.apache.ignite.internal.cli.core.repl;
import jakarta.inject.Singleton;
import java.util.concurrent.atomic.AtomicReference;
-import org.apache.ignite.internal.cli.core.exception.ConnectionException;
import org.apache.ignite.internal.cli.event.ConnectionEventListener;
/**
@@ -30,19 +29,14 @@ public class Session implements ConnectionEventListener {
private final AtomicReference<SessionInfo> info = new AtomicReference<>();
- public Session() {
- }
-
@Override
public void onConnect(SessionInfo sessionInfo) {
- if (!info.compareAndSet(null, sessionInfo)) {
- throw new ConnectionException("Already connected to " +
info.get().nodeUrl());
- }
+ info.set(sessionInfo);
}
@Override
public void onDisconnect() {
- info.getAndSet(null);
+ info.set(null);
}
/** Returns {@link SessionInfo}. */
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 ae54ee9b1a..f2fd184338 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
@@ -103,12 +103,6 @@ public class DynamicCompleterActivationPoint {
.exclusiveEnableOptions().build(),
nodeNameDynamicCompleterFactory
);
- registry.register(
- CompleterConf.builder()
- .command("connect")
- .singlePositionalParameter().build(),
- nodeNameDynamicCompleterFactory
- );
registry.register(
CompleterConf.builder()
.command("cluster", "init")
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsider.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsider.java
index d963fb7793..2d618f45b7 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsider.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsider.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.cli.core.repl.completer;
import java.util.ArrayList;
import java.util.List;
import org.apache.ignite.internal.cli.commands.TopLevelCliReplCommand;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
import picocli.CommandLine;
import picocli.CommandLine.MissingParameterException;
import picocli.CommandLine.Model.ArgSpec;
@@ -33,7 +32,6 @@ public class DynamicCompletionInsider {
public DynamicCompletionInsider() {
commandLine = new CommandLine(TopLevelCliReplCommand.class);
- commandLine.registerConverter(NodeNameOrUrl.class, value -> null);
}
private static String[] trim(String[] typedWords) {
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
index cf06e43725..d9be1fd8cf 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
@@ -22,9 +22,7 @@ import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
import org.apache.ignite.internal.cli.config.StateFolderProvider;
-import org.apache.ignite.internal.cli.core.converters.NodeNameOrUrlConverter;
import org.apache.ignite.internal.cli.core.exception.ExceptionHandlers;
import
org.apache.ignite.internal.cli.core.exception.handler.PicocliExecutionExceptionHandler;
import
org.apache.ignite.internal.cli.core.exception.handler.ReplExceptionHandlers;
@@ -40,7 +38,6 @@ import
org.apache.ignite.internal.cli.core.repl.completer.filter.NonRepeatableOp
import
org.apache.ignite.internal.cli.core.repl.completer.filter.ShortOptionsFilter;
import
org.apache.ignite.internal.cli.core.repl.context.CommandLineContextProvider;
import org.apache.ignite.internal.cli.core.repl.expander.NoopExpander;
-import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
import org.jline.console.impl.SystemRegistryImpl;
import org.jline.reader.Completer;
import org.jline.reader.LineReader;
@@ -73,19 +70,15 @@ public class ReplExecutorImpl implements ReplExecutor {
private final Terminal terminal;
- private final NodeNameRegistry nodeNameRegistry;
-
/**
* Constructor.
*
* @param commandsFactory picocli commands factory.
* @param terminal terminal instance.
- * @param nodeNameRegistry node name registry.
*/
- public ReplExecutorImpl(PicocliCommandsFactory commandsFactory, Terminal
terminal, NodeNameRegistry nodeNameRegistry) {
+ public ReplExecutorImpl(PicocliCommandsFactory commandsFactory, Terminal
terminal) {
this.factory = commandsFactory;
this.terminal = terminal;
- this.nodeNameRegistry = nodeNameRegistry;
}
private static void createTailTipWidgets(SystemRegistryImpl registry,
LineReader reader) {
@@ -185,7 +178,6 @@ public class ReplExecutorImpl implements ReplExecutor {
}
CommandLineContextProvider.setCmd(cmd);
cmd.setExecutionExceptionHandler(new
PicocliExecutionExceptionHandler(exceptionHandlers));
- cmd.registerConverter(NodeNameOrUrl.class, new
NodeNameOrUrlConverter(nodeNameRegistry));
cmd.setTrimQuotes(true);
DynamicCompleterRegistry completerRegistry =
factory.create(DynamicCompleterRegistry.class);
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProviderImpl.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProviderImpl.java
index 8a66b5a485..93286ebfe2 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProviderImpl.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProviderImpl.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.cli.core.repl.executor;
import io.micronaut.configuration.picocli.MicronautFactory;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
import org.jline.terminal.Terminal;
import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
@@ -34,12 +33,9 @@ public class ReplExecutorProviderImpl implements
ReplExecutorProvider {
@Inject
private Terminal terminal;
- @Inject
- private NodeNameRegistry nodeNameRegistry;
-
@Override
public ReplExecutor get() {
- return new ReplExecutorImpl(factory, terminal, nodeNameRegistry);
+ return new ReplExecutorImpl(factory, terminal);
}
public void injectFactory(MicronautFactory micronautFactory) {
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupport.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupport.java
index 0ce4ff8d35..fadd6d355e 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupport.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupport.java
@@ -56,6 +56,7 @@ public final class AnsiStringSupport {
}
/** Marks given text with the configured before style. */
+ @Override
public String mark(String textToMark) {
if (style == Style.BOLD) {
return String.format("@|fg(%d),bold %s|@", color.code,
textToMark);
@@ -74,6 +75,7 @@ public final class AnsiStringSupport {
this.value = value;
}
+ @Override
public String mark(String textToMark) {
return String.format("@|%s %s|@", value, textToMark);
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiElements.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiElements.java
index 962212a96a..22a56834ce 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiElements.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiElements.java
@@ -57,4 +57,9 @@ public class UiElements {
public static UiElement yesNo() {
return new MarkedUiElement("[Y/n]", fg(Color.GRAY));
}
+
+ /** Username UI element. */
+ public static UiElement username(String content) {
+ return () -> content;
+ }
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiString.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiString.java
index 47a4451214..ba4baa1e10 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiString.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiString.java
@@ -27,7 +27,7 @@ public class UiString {
return template;
}
- Object[] elementsAsObjects =
Arrays.stream(elements).map(Object.class::cast).toArray();
+ Object[] elementsAsObjects =
Arrays.stream(elements).map(UiElement::represent).toArray();
return String.format(template, elementsAsObjects);
}
diff --git
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
index aaee3f48a6..3e5ee65bf4 100644
---
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
+++
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
@@ -26,8 +26,6 @@ import jakarta.inject.Inject;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
-import org.apache.ignite.internal.cli.core.converters.NodeNameOrUrlConverter;
import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
import org.junit.jupiter.api.BeforeEach;
import picocli.CommandLine;
@@ -53,8 +51,7 @@ public abstract class CliCommandTestBase {
@BeforeEach
public void setUp() {
- cmd = new CommandLine(getCommandClass(), new MicronautFactory(context))
- .registerConverter(NodeNameOrUrl.class, new
NodeNameOrUrlConverter(nodeNameRegistry));
+ cmd = new CommandLine(getCommandClass(), new
MicronautFactory(context));
sout = new StringWriter();
serr = new StringWriter();
cmd.setOut(new PrintWriter(sout));
diff --git
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/UrlOptionsNegativeTest.java
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/UrlOptionsNegativeTest.java
index 49aeda04a8..3cac6b330a 100644
---
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/UrlOptionsNegativeTest.java
+++
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/UrlOptionsNegativeTest.java
@@ -54,7 +54,6 @@ import
org.apache.ignite.internal.cli.commands.cluster.unit.ClusterUnitUndeployC
import
org.apache.ignite.internal.cli.commands.cluster.unit.ClusterUnitUndeployReplCommand;
import org.apache.ignite.internal.cli.commands.connect.ConnectCommand;
import org.apache.ignite.internal.cli.commands.connect.ConnectReplCommand;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
import
org.apache.ignite.internal.cli.commands.node.config.NodeConfigShowCommand;
import
org.apache.ignite.internal.cli.commands.node.config.NodeConfigShowReplCommand;
import
org.apache.ignite.internal.cli.commands.node.config.NodeConfigUpdateCommand;
@@ -71,7 +70,6 @@ import
org.apache.ignite.internal.cli.commands.node.status.NodeStatusCommand;
import
org.apache.ignite.internal.cli.commands.node.status.NodeStatusReplCommand;
import org.apache.ignite.internal.cli.commands.node.unit.NodeUnitListCommand;
import
org.apache.ignite.internal.cli.commands.node.unit.NodeUnitListReplCommand;
-import org.apache.ignite.internal.cli.core.converters.NodeNameOrUrlConverter;
import
org.apache.ignite.internal.cli.core.repl.context.CommandLineContextProvider;
import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
import org.apache.ignite.internal.testframework.WorkDirectory;
@@ -126,8 +124,7 @@ public class UrlOptionsNegativeTest {
private void setUp(Class<?> cmdClass) {
configManagerProvider.setConfigFile(TestConfigManagerHelper.createSectionWithDefaultProfileConfig());
MicronautFactory factory = new MicronautFactory(context);
- cmd = new CommandLine(cmdClass, factory)
- .registerConverter(NodeNameOrUrl.class, new
NodeNameOrUrlConverter(nodeNameRegistry));
+ cmd = new CommandLine(cmdClass, factory);
CommandLineContextProvider.setCmd(cmd);
sout = new StringWriter();
serr = new StringWriter();
@@ -314,7 +311,7 @@ public class UrlOptionsNegativeTest {
assertAll(
this::assertOutputIsEmpty,
- () -> assertErrOutputContains("Missing required parameter:
'<nodeNameOrUrl>'")
+ () -> assertErrOutputContains("Missing required parameter:
'<nodeUrl>'")
);
}
diff --git
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommandTest.java
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommandTest.java
index bee56dfc31..b2d44dd0fe 100644
---
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommandTest.java
+++
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommandTest.java
@@ -31,14 +31,13 @@ class ConnectCommandTest extends CliCommandTestBase {
}
@Test
- @DisplayName("Should throw error if provided an unknown node name")
- void unknownNodeName() {
+ void invalidNodeUrl() {
execute("nodeName");
assertAll(
() -> assertExitCodeIs(2),
this::assertOutputIsEmpty,
- () -> assertErrOutputContains("Node nodeName not found.
Provide valid name or use URL")
+ () -> assertErrOutputContains("Invalid URL 'nodeName' (no
protocol: nodeName)")
);
}
diff --git
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistryTest.java
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistryTest.java
index d5046002f5..26ab52ece3 100644
---
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistryTest.java
+++
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistryTest.java
@@ -324,22 +324,22 @@ class DynamicCompleterRegistryTest {
// Given completer for single positional parameter
registry.register(
CompleterConf.builder()
- .command("connect") // should be real commands
+ .command("cluster", "config", "show") // should be
real commands
.singlePositionalParameter()
.build(),
words -> completer1
);
// When there is no positional argument typed
- List<DynamicCompleter> connectCompleter =
registry.findCompleters(words("connect"));
+ List<DynamicCompleter> completers =
registry.findCompleters(words("cluster", "config", "show"));
// Then completer is returned
- assertThat(connectCompleter,
both(hasSize(1)).and(containsInAnyOrder(completer1)));
+ assertThat(completers, contains(completer1));
// When there is one positional argument typed
- List<DynamicCompleter> connectCompleterWithPositional =
registry.findCompleters(words("connect", "arg1", ""));
+ List<DynamicCompleter> completersWithPositional =
registry.findCompleters(words("cluster", "config", "show", "arg1", ""));
// Then completer is not returned
- assertThat(connectCompleterWithPositional, hasSize(0));
+ assertThat(completersWithPositional, is(empty()));
}
}
diff --git
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsiderTest.java
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsiderTest.java
index ae47b82a33..e1fb2e45e3 100644
---
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsiderTest.java
+++
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsiderTest.java
@@ -31,12 +31,6 @@ class DynamicCompletionInsiderTest {
private static Stream<Arguments> typedWordsSource() {
return Stream.of(
- // connect command needs a parameter
- Arguments.of(new String[]{"connect"}, false),
- // technically, we have a parameter, but it could be typing in
progress
- Arguments.of(new String[]{"connect", "node"}, false),
- // here we have completed parameter
- Arguments.of(new String[]{"connect", "node", ""}, true),
Arguments.of(new String[]{"cluster", "unit", "undeploy"},
false),
Arguments.of(new String[]{"cluster", "unit", "undeploy",
"unit.id", "--version"}, true),
Arguments.of(new String[]{"cluster", "unit", "undeploy",
"unit.id", "--version", "1.0.0"}, true),
diff --git
a/modules/cli/src/testFixtures/resources/cluster-configuration-with-enabled-auth.conf
b/modules/cli/src/testFixtures/resources/cluster-configuration-with-enabled-auth.conf
index 9931ed53b3..f9461400ef 100644
---
a/modules/cli/src/testFixtures/resources/cluster-configuration-with-enabled-auth.conf
+++
b/modules/cli/src/testFixtures/resources/cluster-configuration-with-enabled-auth.conf
@@ -7,6 +7,12 @@ security: {
type: basic,
username: admin,
password: password
+ },
+ {
+ name: basic1,
+ type: basic,
+ username: admin1,
+ password: password
}
]
}