diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationRegistry.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationRegistry.java index c5a17433a52..4da72657fdb 100644 --- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationRegistry.java +++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationRegistry.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; +import org.apache.ignite.configuration.ConfigurationModule; import org.apache.ignite.configuration.ConfigurationTree; import org.apache.ignite.configuration.KeyIgnorer; import org.apache.ignite.configuration.RootKey; @@ -264,4 +265,29 @@ public KeyIgnorer keyIgnorer() { public long notificationCount() { return changer.notificationCount(); } + + /** + * Creates an instance of {@code ConfigurationRegistry}. + * + * @param configurationModule the module containing configuration root keys, migration logic, and deleted prefixes. + * @param storage the storage system to persist configuration data. + * @param generator the generator responsible for creating the configuration tree structure. + * @param configurationValidator the validator ensuring the correctness of configuration updates. + * @return a new {@code ConfigurationRegistry} instance initialized with the specified parameters. + */ + public static ConfigurationRegistry create( + ConfigurationModule configurationModule, + ConfigurationStorage storage, + ConfigurationTreeGenerator generator, + ConfigurationValidator configurationValidator + ) { + return new ConfigurationRegistry( + configurationModule.rootKeys(), + storage, + generator, + configurationValidator, + configurationModule::migrateDeprecatedConfigurations, + KeyIgnorer.fromDeletedPrefixes(configurationModule.deletedPrefixes()) + ); + } } diff --git a/modules/runner/build.gradle b/modules/runner/build.gradle index 5c7a7d3825c..fb38f6c2eac 100644 --- a/modules/runner/build.gradle +++ b/modules/runner/build.gradle @@ -131,6 +131,7 @@ dependencies { testImplementation libs.awaitility integrationTestAnnotationProcessor project(':ignite-configuration-annotation-processor') + integrationTestAnnotationProcessor libs.auto.service integrationTestAnnotationProcessor libs.jmh.annotation.processor integrationTestImplementation project(':ignite-partition-distribution') integrationTestImplementation project(':ignite-jdbc') @@ -198,6 +199,7 @@ dependencies { integrationTestImplementation testFixtures(project(':ignite-raft')) integrationTestImplementation testFixtures(project(':ignite-replicator')) integrationTestImplementation testFixtures(project(':ignite-catalog')) + integrationTestImplementation libs.auto.service.annotations integrationTestImplementation libs.awaitility integrationTestImplementation libs.rocksdb.jni integrationTestImplementation libs.disruptor diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItDeletedPrefixConfigurationTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItDeletedPrefixConfigurationTest.java new file mode 100644 index 00000000000..f3ea751b560 --- /dev/null +++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItDeletedPrefixConfigurationTest.java @@ -0,0 +1,101 @@ +/* + * 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.configuration.TestDistributedDeletedPrefixConfigurationModule.DELETED_DISTRIBUTED_PREFIX; +import static org.apache.ignite.internal.configuration.TestLocalDeletedPrefixConfigurationModule.DELETED_LOCAL_PREFIX; +import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.typesafe.config.parser.ConfigDocument; +import com.typesafe.config.parser.ConfigDocumentFactory; +import org.apache.ignite.configuration.ConfigurationModule; +import org.apache.ignite.configuration.KeyIgnorer; +import org.apache.ignite.internal.ClusterPerClassIntegrationTest; +import org.apache.ignite.internal.app.IgniteImpl; +import org.apache.ignite.internal.configuration.presentation.HoconPresentation; +import org.junit.jupiter.api.Test; + +/** + * Integration tests verifying that {@link IgniteImpl} correctly wires deleted prefixes from each {@link ConfigurationModule} + * into the corresponding {@link ConfigurationRegistry} via {@link ConfigurationRegistry#create}. + */ +public class ItDeletedPrefixConfigurationTest extends ClusterPerClassIntegrationTest { + @Override + protected int initialNodes() { + return 1; + } + + @Override + protected String getNodeBootstrapConfigTemplate() { + ConfigDocument document = ConfigDocumentFactory.parseString(super.getNodeBootstrapConfigTemplate()) + .withValueText("ignite.testDeletedLocalProp", "old_value"); + return document.render(); + } + + /** + * Verifies that the local configuration registry's {@link KeyIgnorer} recognizes the local deleted prefix + * but not the distributed deleted prefix. + */ + @Test + void testLocalRegistryKeyIgnorerRecognizesLocalDeletedPrefix() { + ConfigurationRegistry nodeConfig = igniteImpl(0).nodeConfiguration(); + + assertTrue(nodeConfig.keyIgnorer().shouldIgnore(DELETED_LOCAL_PREFIX), + "Local registry must ignore local deleted prefix"); + assertFalse(nodeConfig.keyIgnorer().shouldIgnore(DELETED_DISTRIBUTED_PREFIX), + "Local registry must NOT ignore distributed deleted prefix"); + } + + /** + * Verifies that the distributed configuration registry's {@link KeyIgnorer} recognizes the distributed deleted prefix + * but not the local deleted prefix. + */ + @Test + void testDistributedRegistryKeyIgnorerRecognizesDistributedDeletedPrefix() { + ConfigurationRegistry clusterConfig = igniteImpl(0).clusterConfiguration(); + + assertTrue(clusterConfig.keyIgnorer().shouldIgnore(DELETED_DISTRIBUTED_PREFIX), + "Distributed registry must ignore distributed deleted prefix"); + assertFalse(clusterConfig.keyIgnorer().shouldIgnore(DELETED_LOCAL_PREFIX), + "Distributed registry must NOT ignore local deleted prefix"); + } + + /** + * Verifies that the node starts and the cluster initializes with deleted properties in both local and distributed configs. + */ + @Test + void testNodeStartsAndInitializesWithDeletedProperties() { + // Verify the deleted local property was dropped and is absent from local config. + String localConfigHocon = new HoconPresentation(igniteImpl(0).nodeConfiguration()).represent(); + assertFalse(localConfigHocon.contains("testDeletedLocalProp"), + "Deleted local property must not be present in local config after startup"); + + // Verify that applying the deleted distributed property via HoconPresentation succeeds. + HoconPresentation clusterPresentation = new HoconPresentation(igniteImpl(0).clusterConfiguration()); + assertThat(clusterPresentation.update("ignite.testDeletedDistribProp = \"old_value\""), + willCompleteSuccessfully()); + + // Verify the deleted distributed property was dropped and is absent from distributed config. + String clusterConfigHocon = clusterPresentation.represent(); + assertFalse(clusterConfigHocon.contains("testDeletedDistribProp"), + "Deleted distributed property must not be present in distributed config after update"); + } +} diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/TestDistributedDeletedPrefixConfigurationModule.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/TestDistributedDeletedPrefixConfigurationModule.java new file mode 100644 index 00000000000..0887f8520a4 --- /dev/null +++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/TestDistributedDeletedPrefixConfigurationModule.java @@ -0,0 +1,42 @@ +/* + * 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 com.google.auto.service.AutoService; +import java.util.Collection; +import java.util.Set; +import org.apache.ignite.configuration.ConfigurationModule; +import org.apache.ignite.configuration.annotation.ConfigurationType; + +/** + * Test {@link ConfigurationModule} with a DISTRIBUTED deleted prefix. + */ +@AutoService(ConfigurationModule.class) +public class TestDistributedDeletedPrefixConfigurationModule implements ConfigurationModule { + static final String DELETED_DISTRIBUTED_PREFIX = "ignite.testDeletedDistribProp"; + + @Override + public ConfigurationType type() { + return ConfigurationType.DISTRIBUTED; + } + + @Override + public Collection deletedPrefixes() { + return Set.of(DELETED_DISTRIBUTED_PREFIX); + } +} diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/TestLocalDeletedPrefixConfigurationModule.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/TestLocalDeletedPrefixConfigurationModule.java new file mode 100644 index 00000000000..8248ef20e71 --- /dev/null +++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/TestLocalDeletedPrefixConfigurationModule.java @@ -0,0 +1,42 @@ +/* + * 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 com.google.auto.service.AutoService; +import java.util.Collection; +import java.util.Set; +import org.apache.ignite.configuration.ConfigurationModule; +import org.apache.ignite.configuration.annotation.ConfigurationType; + +/** + * Test {@link ConfigurationModule} with a LOCAL deleted prefix. + */ +@AutoService(ConfigurationModule.class) +public class TestLocalDeletedPrefixConfigurationModule implements ConfigurationModule { + static final String DELETED_LOCAL_PREFIX = "ignite.testDeletedLocalProp"; + + @Override + public ConfigurationType type() { + return ConfigurationType.LOCAL; + } + + @Override + public Collection deletedPrefixes() { + return Set.of(DELETED_LOCAL_PREFIX); + } +} diff --git a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java index ce7c2cfda0d..39de9eee97b 100644 --- a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java +++ b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java @@ -65,7 +65,6 @@ import org.apache.ignite.client.handler.configuration.ClientConnectorExtensionConfiguration; import org.apache.ignite.compute.IgniteCompute; import org.apache.ignite.configuration.ConfigurationDynamicDefaultsPatcher; -import org.apache.ignite.configuration.KeyIgnorer; import org.apache.ignite.internal.catalog.CatalogManager; import org.apache.ignite.internal.catalog.CatalogManagerImpl; import org.apache.ignite.internal.catalog.DataNodesAwarePartitionCountCalculator; @@ -585,13 +584,11 @@ public class IgniteImpl implements Ignite { ConfigurationValidator localConfigurationValidator = ConfigurationValidatorImpl.withDefaultValidators(localConfigurationGenerator, modules.local().validators()); - nodeConfigRegistry = new ConfigurationRegistry( - modules.local().rootKeys(), + nodeConfigRegistry = ConfigurationRegistry.create( + modules.local(), localFileConfigurationStorage, localConfigurationGenerator, - localConfigurationValidator, - modules.local()::migrateDeprecatedConfigurations, - KeyIgnorer.fromDeletedPrefixes(modules.local().deletedPrefixes()) + localConfigurationValidator ); // Start local configuration to be able to read all local properties. @@ -810,13 +807,11 @@ public class IgniteImpl implements Ignite { cfgStorage = new DistributedConfigurationStorage(name, metaStorageMgr); - clusterConfigRegistry = new ConfigurationRegistry( - modules.distributed().rootKeys(), + clusterConfigRegistry = ConfigurationRegistry.create( + modules.distributed(), cfgStorage, distributedConfigurationGenerator, - distributedCfgValidator, - modules.distributed()::migrateDeprecatedConfigurations, - KeyIgnorer.fromDeletedPrefixes(modules.local().deletedPrefixes()) + distributedCfgValidator ); metricManager.configure(clusterConfigRegistry.getConfiguration(MetricExtensionConfiguration.KEY).metrics());