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 e19bfb4888 IGNITE-23793 Add exception handler for config option
mismatching (#4795)
e19bfb4888 is described below
commit e19bfb4888ea91c52cf69f440acc8043e3359335
Author: Mikhail <[email protected]>
AuthorDate: Mon Dec 2 17:46:50 2024 +0300
IGNITE-23793 Add exception handler for config option mismatching (#4795)
---
.../cli/call/cluster/ClusterInitCallInput.java | 15 +++++++
.../commands/cluster/init/ClusterInitOptions.java | 32 +++++++++++++-
.../cluster/init/ClusterInitReplCommand.java | 28 ++++++++++++-
...seException.java => ConfigAsPathException.java} | 18 ++++++--
.../cluster/init/ConfigAsPathExceptionHandler.java | 49 ++++++++++++++++++++++
.../cluster/init/ConfigFileParseException.java | 5 +++
.../handler/DefaultExceptionHandlers.java | 2 +
.../internal/cli/core/flow/builder/Flows.java | 17 ++++++++
8 files changed, 160 insertions(+), 6 deletions(-)
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
index 24444e40c0..c4c882c292 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
@@ -125,6 +125,21 @@ public class ClusterInitCallInput implements CallInput {
return this;
}
+ public ClusterInitCallInputBuilder metaStorageNodes(List<String>
metaStorageNodes) {
+ this.metaStorageNodes = metaStorageNodes;
+ return this;
+ }
+
+ public ClusterInitCallInputBuilder cmgNodes(List<String> cmgNodes) {
+ this.cmgNodes = cmgNodes;
+ return this;
+ }
+
+ public ClusterInitCallInputBuilder clusterName(String clusterName) {
+ this.clusterName = clusterName;
+ return this;
+ }
+
/**
* Extract cluster initialization options.
*
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
index e69fe1e8a7..f0f041bf64 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
@@ -38,6 +38,9 @@ import com.typesafe.config.ConfigRenderOptions;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -138,7 +141,11 @@ public class ClusterInitOptions {
if (clusterConfigOptions == null) {
return null;
} else if (clusterConfigOptions.config != null) {
- return clusterConfigOptions.config;
+ String config = clusterConfigOptions.config;
+ if (checkConfigAsPath(config)) {
+ throw new ConfigAsPathException(config);
+ }
+ return config;
} else if (clusterConfigOptions.files != null) {
Config config = ConfigFactory.empty();
@@ -160,6 +167,29 @@ public class ClusterInitOptions {
}
}
+ String readConfigAsPath() {
+ if (clusterConfigOptions == null || clusterConfigOptions.config ==
null) {
+ throw new ConfigFileParseException("Couldn't parse cluster
configuration file.");
+ }
+ Path file = Paths.get(clusterConfigOptions.config);
+ try {
+ String content = Files.readString(file);
+ return
ConfigFactory.parseString(content).root().render(ConfigRenderOptions.concise().setFormatted(true).setJson(true));
+ } catch (IOException e) {
+ throw new IgniteCliException("Couldn't read cluster configuration
file " + file, e);
+ } catch (ConfigException e) {
+ throw new ConfigFileParseException("Couldn't parse cluster
configuration file " + file, e);
+ }
+ }
+
+ private static boolean checkConfigAsPath(String config) {
+ try {
+ Paths.get(config);
+ return true;
+ } catch (InvalidPathException e) {
+ return false;
+ }
+ }
private static class DuplicatesChecker {
private final String optionToCheck;
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitReplCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitReplCommand.java
index b59616ddd6..0cf94f8eff 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitReplCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitReplCommand.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.cli.commands.cluster.init;
+import static
org.apache.ignite.internal.cli.core.style.component.QuestionUiComponent.fromYesNoQuestion;
import static picocli.CommandLine.Command;
import jakarta.inject.Inject;
@@ -25,7 +26,9 @@ import
org.apache.ignite.internal.cli.call.cluster.ClusterInitCallInput;
import org.apache.ignite.internal.cli.commands.BaseCommand;
import org.apache.ignite.internal.cli.commands.cluster.ClusterUrlMixin;
import
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion;
+import org.apache.ignite.internal.cli.core.flow.builder.FlowBuilder;
import org.apache.ignite.internal.cli.core.flow.builder.Flows;
+import org.apache.ignite.internal.cli.core.style.component.QuestionUiComponent;
import picocli.CommandLine.Mixin;
/**
@@ -50,12 +53,35 @@ public class ClusterInitReplCommand extends BaseCommand
implements Runnable {
@Override
public void run() {
runFlow(question.askQuestionIfNotConnected(clusterUrl.getClusterUrl())
- .map(this::buildCallInput)
+ .then(askQuestionIfConfigIsPath().build())
.then(Flows.fromCall(call))
.print()
);
}
+
+ private FlowBuilder<String, ClusterInitCallInput>
askQuestionIfConfigIsPath() {
+ try {
+ clusterInitOptions.clusterConfiguration();
+ return Flows.from(this::buildCallInput);
+ } catch (ConfigAsPathException e) {
+ QuestionUiComponent questionUiComponent = fromYesNoQuestion(
+ "It seems that you passed the path to the config file to
the config content option. "
+ + "Do you want this file to be read as a config?"
+ );
+
+ return Flows.acceptQuestion(questionUiComponent,
+ clusterUrl -> ClusterInitCallInput.builder()
+
.clusterConfiguration(clusterInitOptions.readConfigAsPath())
+ .cmgNodes(clusterInitOptions.cmgNodes())
+
.metaStorageNodes(clusterInitOptions.metaStorageNodes())
+ .clusterName(clusterInitOptions.clusterName())
+ .clusterUrl(clusterUrl)
+ .build()
+ );
+ }
+ }
+
private ClusterInitCallInput buildCallInput(String clusterUrl) {
return ClusterInitCallInput.builder()
.clusterUrl(clusterUrl)
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigAsPathException.java
similarity index 69%
copy from
modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
copy to
modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigAsPathException.java
index c551bbb7b6..1eda6e210f 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigAsPathException.java
@@ -17,9 +17,19 @@
package org.apache.ignite.internal.cli.commands.cluster.init;
-/** Exception thrown when config file parse failed. */
-public class ConfigFileParseException extends RuntimeException {
- public ConfigFileParseException(String message, Throwable cause) {
- super(message, cause);
+/**
+ * Exception throws when config file path passed to config content option.
+ */
+public class ConfigAsPathException extends RuntimeException {
+ private static final long serialVersionUID = -7683264630128999379L;
+
+ private final String path;
+
+ public ConfigAsPathException(String path) {
+ this.path = path;
+ }
+
+ public String path() {
+ return path;
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigAsPathExceptionHandler.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigAsPathExceptionHandler.java
new file mode 100644
index 0000000000..adfb4bd0b5
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigAsPathExceptionHandler.java
@@ -0,0 +1,49 @@
+/*
+ * 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.cluster.init;
+
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_CONFIG_FILE_OPTION;
+
+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.style.component.ErrorUiComponent;
+import org.apache.ignite.internal.cli.core.style.element.UiElements;
+
+/**
+ * Handler for {@link ConfigAsPathException}.
+ */
+public class ConfigAsPathExceptionHandler implements
ExceptionHandler<ConfigAsPathException> {
+ @Override
+ public int handle(ExceptionWriter err, ConfigAsPathException e) {
+ err.write(
+ ErrorUiComponent.builder()
+ .header(String.format("Failed to parse configuration
file."
+ + " Looks like config file path
passed."
+ + " Did you mean %s option?",
+
UiElements.command(CLUSTER_CONFIG_FILE_OPTION)))
+ .build().render()
+ );
+
+ return 1;
+ }
+
+ @Override
+ public Class<ConfigAsPathException> applicableException() {
+ return ConfigAsPathException.class;
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
index c551bbb7b6..631130ac14 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ConfigFileParseException.java
@@ -19,6 +19,11 @@ package org.apache.ignite.internal.cli.commands.cluster.init;
/** Exception thrown when config file parse failed. */
public class ConfigFileParseException extends RuntimeException {
+ public ConfigFileParseException(String message) {
+ super(message);
+ }
+
+
public ConfigFileParseException(String message, Throwable cause) {
super(message, cause);
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/DefaultExceptionHandlers.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/DefaultExceptionHandlers.java
index 983949c9c2..f52af34b36 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/DefaultExceptionHandlers.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/DefaultExceptionHandlers.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.cli.core.exception.handler;
+import
org.apache.ignite.internal.cli.commands.cluster.init.ConfigAsPathExceptionHandler;
import
org.apache.ignite.internal.cli.commands.cluster.init.ConfigParseExceptionHandler;
import org.apache.ignite.internal.cli.core.exception.ExceptionHandlers;
@@ -41,5 +42,6 @@ public final class DefaultExceptionHandlers extends
ExceptionHandlers {
addExceptionHandler(new UnitAlreadyExistsExceptionHandler());
addExceptionHandler(new FileNotFoundExceptionHandler());
addExceptionHandler(new ConfigParseExceptionHandler());
+ addExceptionHandler(new ConfigAsPathExceptionHandler());
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/flow/builder/Flows.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/flow/builder/Flows.java
index 08ccf949d7..e03b3bafc0 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/flow/builder/Flows.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/flow/builder/Flows.java
@@ -171,6 +171,23 @@ public final class Flows {
return acceptQuestion(question.render(), onAccept);
}
+ /**
+ * Create new {@link FlowBuilder} which starts from yes/no question and
pass the result of the {@code onAccept}
+ * call on positive answer or interrupts the flow on negative answer.
+ *
+ * @param question question UI component.
+ * @param onAccept mapping function.
+ * @param <I> input type.
+ * @param <O> output type.
+ * @return new {@link FlowBuilder}.
+ */
+ public static <I, O> FlowBuilder<I, O> acceptQuestion(QuestionUiComponent
question, Function<I, O> onAccept) {
+ return question(question.render(),
+ List.of(new AcceptedQuestionAnswer<>((a, i) ->
onAccept.apply(i)),
+ new InterruptQuestionAnswer<>())
+ );
+ }
+
/**
* Create new {@link Flow} which asks questions and returns the result of
answer.
*