|
19 | 19 |
|
20 | 20 | package org.apache.iotdb.db.utils; |
21 | 21 |
|
| 22 | +import org.apache.iotdb.commons.conf.CommonConfig; |
22 | 23 | import org.apache.iotdb.commons.conf.ConfigurationFileUtils; |
23 | 24 | import org.apache.iotdb.db.utils.constant.TestConstant; |
24 | 25 |
|
25 | 26 | import org.junit.After; |
26 | 27 | import org.junit.Assert; |
27 | 28 | import org.junit.Test; |
28 | 29 |
|
| 30 | +import java.io.BufferedReader; |
29 | 31 | import java.io.File; |
30 | 32 | import java.io.FileReader; |
31 | 33 | import java.io.IOException; |
| 34 | +import java.io.InputStream; |
| 35 | +import java.io.InputStreamReader; |
32 | 36 | import java.nio.file.Files; |
| 37 | +import java.util.HashSet; |
33 | 38 | import java.util.Map; |
34 | 39 | import java.util.Objects; |
35 | 40 | import java.util.Properties; |
| 41 | +import java.util.Set; |
36 | 42 |
|
37 | 43 | public class ConfigurationFileUtilsTest { |
38 | 44 |
|
@@ -89,6 +95,82 @@ public void checkIoTDBSystemTemplateFileFormat() throws IOException { |
89 | 95 | } |
90 | 96 | } |
91 | 97 |
|
| 98 | + /** |
| 99 | + * Regression guard for V2-995: a configuration item whose {@code effectiveMode} allows {@code set |
| 100 | + * configuration} (i.e. anything except {@code FIRST_START}) must have its {@code key=value} line |
| 101 | + * left uncommented in the template. {@code getConfigurationItemsFromTemplate} only parses |
| 102 | + * uncommented lines, so a commented item never enters {@code configuration2DefaultValue}; {@code |
| 103 | + * filterInvalidConfigItems} then treats it as undefined and {@code set configuration} rejects it |
| 104 | + * with "immutable or undefined" — silently disabling the advertised dynamic-config entry point. |
| 105 | + * This was the root cause for {@code enable_topology_probing} (hot_reload) and the two {@code |
| 106 | + * topology_probing_*} items (restart). |
| 107 | + */ |
| 108 | + @Test |
| 109 | + public void checkSettableItemsAreUncommentedInTemplate() throws IOException { |
| 110 | + // Keys whose value line appears uncommented at least once. Some keys (e.g. dn_data_dirs) list a |
| 111 | + // commented Windows-path variant next to an uncommented Unix-path variant; only the uncommented |
| 112 | + // one is parsed, so such keys are fine and must not be flagged. |
| 113 | + Set<String> uncommentedKeys = new HashSet<>(); |
| 114 | + // Keys with a commented value line whose enclosing block declares a settable effectiveMode. |
| 115 | + Set<String> commentedSettableKeys = new HashSet<>(); |
| 116 | + try (InputStream inputStream = |
| 117 | + ConfigurationFileUtilsTest.class |
| 118 | + .getClassLoader() |
| 119 | + .getResourceAsStream(CommonConfig.SYSTEM_CONFIG_TEMPLATE_NAME); |
| 120 | + BufferedReader reader = |
| 121 | + new BufferedReader(new InputStreamReader(Objects.requireNonNull(inputStream)))) { |
| 122 | + String line; |
| 123 | + ConfigurationFileUtils.EffectiveModeType currentMode = null; |
| 124 | + while ((line = reader.readLine()) != null) { |
| 125 | + line = line.trim(); |
| 126 | + if (line.isEmpty()) { |
| 127 | + // Blank line separates configuration blocks; reset the accumulated effectiveMode. |
| 128 | + currentMode = null; |
| 129 | + continue; |
| 130 | + } |
| 131 | + if (line.startsWith("# effectiveMode:")) { |
| 132 | + currentMode = |
| 133 | + ConfigurationFileUtils.EffectiveModeType.getEffectiveMode( |
| 134 | + line.substring("# effectiveMode:".length()).trim()); |
| 135 | + continue; |
| 136 | + } |
| 137 | + // Detect the (possibly commented) "key=value" line that closes a block. |
| 138 | + String stripped = line.startsWith("#") ? line.substring(1).trim() : line; |
| 139 | + int equalsIndex = stripped.indexOf('='); |
| 140 | + boolean isValueLine = |
| 141 | + equalsIndex > 0 && stripped.substring(0, equalsIndex).trim().matches("[a-zA-Z0-9_.]+"); |
| 142 | + if (!isValueLine) { |
| 143 | + continue; |
| 144 | + } |
| 145 | + String key = stripped.substring(0, equalsIndex).trim(); |
| 146 | + if (line.startsWith("#")) { |
| 147 | + // FIRST_START items are intentionally rejected by 'set configuration'; UNKNOWN items have |
| 148 | + // no declared mode. Only flag items that 'set configuration' is supposed to accept. |
| 149 | + if (isSettableByConfiguration(currentMode)) { |
| 150 | + commentedSettableKeys.add(key); |
| 151 | + } |
| 152 | + } else { |
| 153 | + uncommentedKeys.add(key); |
| 154 | + } |
| 155 | + // A value line ends the current block's effectiveMode scope. |
| 156 | + currentMode = null; |
| 157 | + } |
| 158 | + } |
| 159 | + commentedSettableKeys.removeAll(uncommentedKeys); |
| 160 | + Assert.assertTrue( |
| 161 | + "configuration items settable via 'set configuration' must be uncommented in the template " |
| 162 | + + "so the command can reach them instead of rejecting them as undefined; " |
| 163 | + + "commented settable items found: " |
| 164 | + + commentedSettableKeys, |
| 165 | + commentedSettableKeys.isEmpty()); |
| 166 | + } |
| 167 | + |
| 168 | + private static boolean isSettableByConfiguration(ConfigurationFileUtils.EffectiveModeType mode) { |
| 169 | + return mode == ConfigurationFileUtils.EffectiveModeType.HOT_RELOAD |
| 170 | + || mode == ConfigurationFileUtils.EffectiveModeType.RESTART |
| 171 | + || mode == ConfigurationFileUtils.EffectiveModeType.FIRST_START_OR_SET_CONFIGURATION; |
| 172 | + } |
| 173 | + |
92 | 174 | private void generateFile(File file, String content) throws IOException { |
93 | 175 | Files.write(file.toPath(), content.getBytes()); |
94 | 176 | } |
|
0 commit comments