Ignore empty custom configuration YAML files

We're shipping default custom configuration files that only contain a
comment, so the system should work in that case the same as if the file
didn't exist at all.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=145594737
This commit is contained in:
mcilwain 2017-01-25 14:10:24 -08:00 committed by Ben McIlwain
parent 0b1781b110
commit a8aeff96f6
2 changed files with 36 additions and 10 deletions

View file

@ -18,6 +18,7 @@ import static google.registry.config.RegistryEnvironment.UNITTEST;
import static google.registry.util.FormattingLogger.getLoggerForCallerClass; import static google.registry.util.FormattingLogger.getLoggerForCallerClass;
import static google.registry.util.ResourceUtils.readResourceUtf8; import static google.registry.util.ResourceUtils.readResourceUtf8;
import com.google.common.base.Optional;
import com.google.common.io.CharStreams; import com.google.common.io.CharStreams;
import google.registry.util.FormattingLogger; import google.registry.util.FormattingLogger;
import java.io.File; import java.io.File;
@ -84,19 +85,30 @@ public final class YamlUtils {
* <p>Only maps are handled recursively; lists are simply overridden in place as-is. * <p>Only maps are handled recursively; lists are simply overridden in place as-is.
*/ */
static String mergeYaml(String defaultYaml, String customYaml) { static String mergeYaml(String defaultYaml, String customYaml) {
Yaml yaml = new Yaml();
Map<String, Object> yamlMap = loadAsMap(yaml, defaultYaml).get();
try { try {
Yaml yaml = new Yaml(); Optional<Map<String, Object>> customMap = loadAsMap(yaml, customYaml);
Map<String, Object> defaultObj = loadAsMap(yaml, defaultYaml); if (customMap.isPresent()) {
Map<String, Object> customObj = loadAsMap(yaml, customYaml); yamlMap = mergeMaps(yamlMap, customMap.get());
Object mergedObj = mergeMaps(defaultObj, customObj); logger.infofmt("Successfully loaded custom YAML configuration file.");
logger.infofmt("Successfully loaded custom YAML configuration file."); } else {
return yaml.dump(mergedObj); logger.infofmt("Ignoring empty custom YAML configuration file.");
}
return yaml.dump(yamlMap);
} catch (Exception e) { } catch (Exception e) {
throw new IllegalStateException("Fatal error: Custom YAML configuration file is invalid", e); throw new IllegalStateException("Fatal error: Custom YAML configuration file is invalid", e);
} }
} }
private static Object mergeMaps(Map<String, Object> defaultMap, Map<String, Object> customMap) { /**
* Recursively merges a custom map into a default map, and returns the merged result.
*
* <p>All keys in the default map that are also specified in the custom map are overridden with
* the custom map's value. This runs recursively on all contained maps.
*/
private static Map<String, Object> mergeMaps(
Map<String, Object> defaultMap, Map<String, Object> customMap) {
for (String key : defaultMap.keySet()) { for (String key : defaultMap.keySet()) {
if (!customMap.containsKey(key)) { if (!customMap.containsKey(key)) {
continue; continue;
@ -113,9 +125,15 @@ public final class YamlUtils {
return defaultMap; return defaultMap;
} }
/**
* Returns a structured map loaded from a YAML config string.
*
* <p>If the YAML string is empty or does not contain any data (e.g. it's only comments), then
* absent is returned.
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static Map<String, Object> loadAsMap(Yaml yaml, String yamlString) { private static Optional<Map<String, Object>> loadAsMap(Yaml yaml, String yamlString) {
return (Map<String, Object>) yaml.load(yamlString); return Optional.fromNullable((Map<String, Object>) yaml.load(yamlString));
} }
private YamlUtils() {} private YamlUtils() {}

View file

@ -17,6 +17,7 @@ package google.registry.config;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.config.YamlUtils.mergeYaml; import static google.registry.config.YamlUtils.mergeYaml;
import com.google.common.base.Joiner;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.JUnit4; import org.junit.runners.JUnit4;
@ -48,7 +49,14 @@ public class YamlUtilsTest {
.isEqualTo(join("non: ay", "list: [crackle, pop var]")); .isEqualTo(join("non: ay", "list: [crackle, pop var]"));
} }
@Test
public void testSuccess_mergeEmptyMap_isNoop() {
String defaultYaml = join("one: ay", "two: bee", "three: sea");
assertThat(mergeYaml(defaultYaml, "# Just a comment\n"))
.isEqualTo("{one: ay, two: bee, three: sea}\n");
}
private static String join(CharSequence... strings) { private static String join(CharSequence... strings) {
return String.join("\n", strings) + "\n"; return Joiner.on('\n').join(strings) + "\n";
} }
} }