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 3d2b36504d IGNITE-19315 Validate node configuration on node start 
(#2058)
3d2b36504d is described below

commit 3d2b36504dc6cf76827fbcc926bd7b99ef17f845
Author: Ivan Gagarkin <[email protected]>
AuthorDate: Tue May 16 16:28:36 2023 +0400

    IGNITE-19315 Validate node configuration on node start (#2058)
---
 .../configuration/ConfigurationChanger.java        |  32 ++++---
 .../configuration/ConfigurationChangerTest.java    |  17 ++--
 .../configuration/ConfigurationRegistryTest.java   |  12 ++-
 .../storage/TestConfigurationStorage.java          |   5 +
 .../java/org/apache/ignite/lang/ErrorGroups.java   |   5 +
 .../ItNodeBootstrapConfigurationTest.java          | 103 +++++++++++++++++++++
 .../ItSslConfigurationValidationTest.java          |   2 -
 .../configuration/NodeConfigParseException.java    |  31 +++++++
 .../storage/LocalFileConfigurationStorage.java     |   8 +-
 9 files changed, 191 insertions(+), 24 deletions(-)

diff --git 
a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationChanger.java
 
b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationChanger.java
index efed03a968..09ced7d79d 100644
--- 
a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationChanger.java
+++ 
b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationChanger.java
@@ -242,8 +242,7 @@ public abstract class ConfigurationChanger implements 
DynamicConfigurationChange
     /**
      * Start component.
      */
-    // ConfigurationChangeException, really?
-    public void start() throws ConfigurationChangeException {
+    public void start() {
         Data data;
 
         try {
@@ -275,6 +274,9 @@ public abstract class ConfigurationChanger implements 
DynamicConfigurationChange
         //Workaround for distributed configuration.
         addDefaults(superRoot);
 
+        // Validate the restored configuration.
+        validateConfiguration(new SuperRoot(rootCreator()), superRoot);
+
         storageRoots = new StorageRoots(superRoot, data.changeId());
 
         storage.registerConfigurationListener(configurationStorageListener());
@@ -588,17 +590,7 @@ public abstract class ConfigurationChanger implements 
DynamicConfigurationChange
 
             dropNulls(changes);
 
-            List<ValidationIssue> validationIssues = ValidationUtil.validate(
-                    curRoots,
-                    changes,
-                    this::getRootNode,
-                    cachedAnnotations,
-                    validators
-            );
-
-            if (!validationIssues.isEmpty()) {
-                throw new ConfigurationValidationException(validationIssues);
-            }
+            validateConfiguration(curRoots, changes);
 
             // "allChanges" map can be empty here in case the given update 
matches the current state of the local configuration. We
             // still try to write the empty update, because local 
configuration can be obsolete. If this is the case, then the CAS will
@@ -619,6 +611,20 @@ public abstract class ConfigurationChanger implements 
DynamicConfigurationChange
         }
     }
 
+    private void validateConfiguration(SuperRoot curRoots, SuperRoot changes) {
+        List<ValidationIssue> validationIssues = ValidationUtil.validate(
+                curRoots,
+                changes,
+                this::getRootNode,
+                cachedAnnotations,
+                validators
+        );
+
+        if (!validationIssues.isEmpty()) {
+            throw new ConfigurationValidationException(validationIssues);
+        }
+    }
+
     private ConfigurationStorageListener configurationStorageListener() {
         return new ConfigurationStorageListener() {
             @Override
diff --git 
a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/ConfigurationChangerTest.java
 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/ConfigurationChangerTest.java
index 38bea01769..b48b8a308c 100644
--- 
a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/ConfigurationChangerTest.java
+++ 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/ConfigurationChangerTest.java
@@ -23,13 +23,14 @@ import static java.util.concurrent.TimeUnit.SECONDS;
 import static 
org.apache.ignite.configuration.annotation.ConfigurationType.LOCAL;
 import static org.apache.ignite.internal.configuration.FirstConfiguration.KEY;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willBe;
+import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.anEmptyMap;
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.emptyString;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.fail;
 
@@ -95,12 +96,12 @@ public class ConfigurationChangerTest {
      */
     @Config
     public static class SecondConfigurationSchema {
-        @Value
+        @Value(hasDefault = true)
         @Immutable
-        public int intCfg;
+        public int intCfg = 0;
 
-        @Value
-        public String strCfg;
+        @Value(hasDefault = true)
+        public String strCfg = "";
     }
 
     /**
@@ -212,7 +213,9 @@ public class ConfigurationChangerTest {
             /** {@inheritDoc} */
             @Override
             public void validate(MaybeInvalid annotation, 
ValidationContext<Object> ctx) {
-                ctx.addIssue(new ValidationIssue("key", "foo"));
+                if (ctx.getNewValue().equals("2")) {
+                    ctx.addIssue(new ValidationIssue("key", "foo"));
+                }
             }
         };
 
@@ -274,7 +277,7 @@ public class ConfigurationChangerTest {
 
         FirstView newRoot = (FirstView) changer.getRootNode(KEY);
         assertNotNull(newRoot.child());
-        assertNull(newRoot.child().strCfg());
+        assertThat(newRoot.child().strCfg(), is(emptyString()));
     }
 
     /**
diff --git 
a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/ConfigurationRegistryTest.java
 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/ConfigurationRegistryTest.java
index bb6ca661ff..e7148eeb1c 100644
--- 
a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/ConfigurationRegistryTest.java
+++ 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/ConfigurationRegistryTest.java
@@ -28,7 +28,9 @@ import static org.junit.jupiter.api.Assertions.assertNotSame;
 import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+import java.io.Serializable;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.function.Consumer;
@@ -137,10 +139,18 @@ public class ConfigurationRegistryTest {
 
     @Test
     void testComplicatedPolymorphicConfig() throws Exception {
+
+        Map<String, Serializable> bootstrapConfig = Map.of(
+                "sixth.entity.poly.strVal", "val",
+                "sixth.poly.strVal", "val",
+                "sixth.entity.poly.intVal", 1,
+                "sixth.poly.intVal", 1
+        );
+
         ConfigurationRegistry registry = new ConfigurationRegistry(
                 List.of(SixthRootConfiguration.KEY),
                 Set.of(),
-                new TestConfigurationStorage(LOCAL),
+                new TestConfigurationStorage(LOCAL, bootstrapConfig),
                 List.of(),
                 List.of(Fourth0PolymorphicConfigurationSchema.class)
         );
diff --git 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/storage/TestConfigurationStorage.java
 
b/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/storage/TestConfigurationStorage.java
index 997026312c..37aa61facc 100644
--- 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/storage/TestConfigurationStorage.java
+++ 
b/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/storage/TestConfigurationStorage.java
@@ -54,7 +54,12 @@ public class TestConfigurationStorage implements 
ConfigurationStorage {
      * @param type Configuration type.
      */
     public TestConfigurationStorage(ConfigurationType type) {
+        this(type, Map.of());
+    }
+
+    public TestConfigurationStorage(ConfigurationType type, Map<String, 
Serializable> data) {
         configurationType = type;
+        map.putAll(data);
     }
 
     @Override
diff --git a/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java 
b/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
index f10da443ed..1f5cbaa0f9 100755
--- a/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
+++ b/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
@@ -374,6 +374,11 @@ public class ErrorGroups {
          * Config write error.
          */
         public static final int CONFIG_WRITE_ERR = 
NODE_CONFIGURATION_ERR_GROUP.registerErrorCode(3);
+
+        /**
+         * Config parse error.
+         */
+        public static final int CONFIG_PARSE_ERR = 
NODE_CONFIGURATION_ERR_GROUP.registerErrorCode(4);
     }
 
     /**
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItNodeBootstrapConfigurationTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItNodeBootstrapConfigurationTest.java
new file mode 100644
index 0000000000..675c08133c
--- /dev/null
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItNodeBootstrapConfigurationTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.configuration;
+
+import static 
org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrowsWithCause;
+import static 
org.apache.ignite.internal.testframework.IgniteTestUtils.testNodeName;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.nio.file.Path;
+import java.util.UUID;
+import 
org.apache.ignite.configuration.validation.ConfigurationValidationException;
+import org.apache.ignite.internal.testframework.TestIgnitionManager;
+import org.apache.ignite.internal.testframework.WorkDirectory;
+import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
+import org.apache.ignite.lang.IgniteException;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * Integration tests for the node bootstrap configuration.
+ */
+@ExtendWith(WorkDirectoryExtension.class)
+public class ItNodeBootstrapConfigurationTest {
+
+    @WorkDirectory
+    private Path workDir;
+
+    @Test
+    public void wrongConfigurationFormat(TestInfo testInfo) {
+        String config = UUID.randomUUID().toString();
+
+        IgniteException igniteException = assertThrows(
+                IgniteException.class,
+                () -> TestIgnitionManager.start(testNodeName(testInfo, 0), 
config, workDir)
+        );
+        assertThat(igniteException.getCause(), 
is(instanceOf(NodeConfigParseException.class)));
+        assertThat(
+                igniteException.getCause().getMessage(),
+                containsString("Failed to parse config content from file " + 
workDir + "/ignite-config.conf")
+        );
+    }
+
+    @Test
+    public void illegalConfigurationValueType(TestInfo testInfo) {
+        String config =
+                "{\n"
+                + "  rest: {\n"
+                + "    ssl: {\n"
+                + "      enabled: true,\n"
+                + "      clientAuth: none,\n"
+                + "      keyStore: {\n"
+                + "        path: 123\n"
+                + "      }\n"
+                + "    }\n"
+                + "  }\n"
+                + "}";
+
+        assertThrowsWithCause(
+                () -> TestIgnitionManager.start(testNodeName(testInfo, 0), 
config, workDir),
+                IllegalArgumentException.class,
+                "'String' is expected as a type for the 
'rest.ssl.keyStore.path' configuration value");
+    }
+
+    @Test
+    public void illegalConfigurationValue(TestInfo testInfo) {
+        String config = "{\n"
+                + "  rest: {\n"
+                + "    ssl: {\n"
+                + "      enabled: true,\n"
+                + "      clientAuth: none,\n"
+                + "      keyStore: {\n"
+                + "        path: bad_path\n"
+                + "      }\n"
+                + "    }\n"
+                + "  }\n"
+                + "}";
+
+        assertThrowsWithCause(
+                () -> TestIgnitionManager.start(testNodeName(testInfo, 0), 
config, workDir),
+                ConfigurationValidationException.class,
+                "Validation did not pass for keys: [rest.ssl.keyStore, Key 
store file doesn't exist at bad_path]");
+    }
+}
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItSslConfigurationValidationTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItSslConfigurationValidationTest.java
index db1144c138..36b858f33d 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItSslConfigurationValidationTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItSslConfigurationValidationTest.java
@@ -25,7 +25,6 @@ import 
org.apache.ignite.configuration.validation.ConfigurationValidationExcepti
 import org.apache.ignite.internal.testframework.TestIgnitionManager;
 import org.apache.ignite.internal.testframework.WorkDirectory;
 import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.TestInfo;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -35,7 +34,6 @@ import org.junit.jupiter.params.provider.ValueSource;
  * Integration test for checking SSL configuration validation.
  */
 @ExtendWith(WorkDirectoryExtension.class)
-@Disabled("https://issues.apache.org/jira/browse/IGNITE-19315";)
 public class ItSslConfigurationValidationTest {
     @ParameterizedTest
     @ValueSource(strings = {"clientConnector", "network", "rest"})
diff --git 
a/modules/runner/src/main/java/org/apache/ignite/internal/configuration/NodeConfigParseException.java
 
b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/NodeConfigParseException.java
new file mode 100644
index 0000000000..0e9fc495c7
--- /dev/null
+++ 
b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/NodeConfigParseException.java
@@ -0,0 +1,31 @@
+/*
+ * 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.configuration;
+
+import org.apache.ignite.lang.ErrorGroups.NodeConfiguration;
+import org.apache.ignite.lang.IgniteException;
+
+/**
+ * Exception that gets thrown when a node bootstrap configuration file is 
malformed.
+ */
+public class NodeConfigParseException extends IgniteException {
+
+    public NodeConfigParseException(String msg, Throwable cause) {
+        super(NodeConfiguration.CONFIG_PARSE_ERR, msg, cause);
+    }
+}
diff --git 
a/modules/runner/src/main/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorage.java
 
b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorage.java
index dee1865dd9..b52d5715a3 100644
--- 
a/modules/runner/src/main/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorage.java
+++ 
b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorage.java
@@ -23,6 +23,7 @@ import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.fi
 import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.toPrefixMap;
 
 import com.typesafe.config.Config;
+import com.typesafe.config.ConfigException;
 import com.typesafe.config.ConfigFactory;
 import com.typesafe.config.ConfigObject;
 import com.typesafe.config.ConfigParseOptions;
@@ -49,6 +50,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
 import org.apache.ignite.configuration.annotation.ConfigurationType;
 import org.apache.ignite.internal.configuration.ConfigurationTreeGenerator;
 import org.apache.ignite.internal.configuration.NodeConfigCreateException;
+import org.apache.ignite.internal.configuration.NodeConfigParseException;
 import org.apache.ignite.internal.configuration.NodeConfigWriteException;
 import org.apache.ignite.internal.configuration.SuperRoot;
 import org.apache.ignite.internal.configuration.hocon.HoconConverter;
@@ -134,7 +136,11 @@ public class LocalFileConfigurationStorage implements 
ConfigurationStorage {
     private Config readHoconFromFile() {
         checkAndRestoreConfigFile();
 
-        return ConfigFactory.parseFile(configPath.toFile(), 
ConfigParseOptions.defaults().setAllowMissing(false));
+        try {
+            return ConfigFactory.parseFile(configPath.toFile(), 
ConfigParseOptions.defaults().setAllowMissing(false));
+        } catch (ConfigException.Parse e) {
+            throw new NodeConfigParseException("Failed to parse config content 
from file " + configPath, e);
+        }
     }
 
     @Override

Reply via email to