diff options
author | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2019-08-06 14:30:35 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-06 14:30:35 +0200 |
commit | 32c4af492ac205cd727a5901de1ecccf8c95ae02 (patch) | |
tree | a5edf803c3fd2e29e8d7d61f784b418fed763b70 | |
parent | 289ceb51e7515642be88bfd6a633296a24afcbbd (diff) | |
parent | 34e395fbea9711a10dfd43f9080c68dda420557e (diff) |
Merge pull request #10176 from vespa-engine/bjorncs/default-env-writer
Add helper to rewrite default-env.txt files
4 files changed, 189 insertions, 0 deletions
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/DefaultEnvWriter.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/DefaultEnvWriter.java new file mode 100644 index 00000000000..83ac3eeeaf4 --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/DefaultEnvWriter.java @@ -0,0 +1,121 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.node.admin.task.util; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import static com.yahoo.vespa.hosted.node.admin.task.util.file.IOExceptionUtil.ifExists; +import static com.yahoo.yolean.Exceptions.uncheck; +import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; +import static java.util.stream.Collectors.joining; + +/** + * Rewrites default-env.txt files. + * + * @author bjorncs + */ +public class DefaultEnvWriter { + + private final Map<String, Operation> operations = new LinkedHashMap<>(); + + public DefaultEnvWriter addOverride(String name, String value) { + return addOperation("override", name, value); + } + + public DefaultEnvWriter addFallback(String name, String value) { + return addOperation("fallback", name, value); + } + + public DefaultEnvWriter addUnset(String name) { + return addOperation("unset", name, null); + } + + private DefaultEnvWriter addOperation(String action, String name, String value) { + if (operations.containsKey(name)) { + throw new IllegalArgumentException(String.format("Operation on variable '%s' already added", name)); + } + operations.put(name, new Operation(action, name, value)); + return this; + } + + /** + * Updates or created a default-env.txt file + * + * @return true if the file was modified + */ + public boolean updateFile(Path defaultEnvFile) { + List<String> currentDefaultEnvLines = ifExists(() -> Files.readAllLines(defaultEnvFile)).orElse(List.of()); + List<String> newDefaultEnvLines = generateContent(currentDefaultEnvLines); + if (currentDefaultEnvLines.equals(newDefaultEnvLines)) { + return false; + } else { + Path tempFile = Paths.get(defaultEnvFile.toString() + ".tmp"); + uncheck(() -> Files.write(tempFile, newDefaultEnvLines)); + uncheck(() -> Files.move(tempFile, defaultEnvFile, ATOMIC_MOVE)); + return true; + } + } + + /** + * @return generated default-env.txt content + */ + public String generateContent() { + return generateContent(List.of()).stream() + .collect(joining(System.lineSeparator(), "", System.lineSeparator())); + } + + private List<String> generateContent(List<String> currentDefaultEnvLines) { + List<String> newDefaultEnvLines = new ArrayList<>(); + Set<String> seenNames = new TreeSet<>(); + for (String line : currentDefaultEnvLines) { + String[] items = line.split(" "); + if (items.length < 2) { + throw new IllegalArgumentException(String.format("Invalid line in file '%s': %s", currentDefaultEnvLines, line)); + } + String name = items[1]; + if (!seenNames.contains(name)) { // implicitly removes duplicated variables + seenNames.add(name); + Operation operation = operations.get(name); + if (operation != null) { + newDefaultEnvLines.add(operation.toLine()); + } else { + newDefaultEnvLines.add(line); + } + } + } + for (var operation : operations.values()) { + if (!seenNames.contains(operation.name)) { + newDefaultEnvLines.add(operation.toLine()); + } + } + return newDefaultEnvLines; + } + + private static class Operation { + final String action; + final String name; + final String value; + + Operation(String action, String name, String value) { + this.action = action; + this.name = name; + this.value = value; + } + + String toLine() { + if (action.equals("unset")) { + return "unset " + name; + } + return action + " " + name + " " + value; + } + } +} + + diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/DefaultEnvWriterTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/DefaultEnvWriterTest.java new file mode 100644 index 00000000000..5d687c06e94 --- /dev/null +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/task/util/DefaultEnvWriterTest.java @@ -0,0 +1,59 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.node.admin.task.util; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author bjorncs + */ +public class DefaultEnvWriterTest { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private static final Path EXAMPLE_FILE = Paths.get(DefaultEnvWriterTest.class.getResource("/default-env-example.txt").getFile()); + private static final Path EXPECTED_RESULT_FILE = Paths.get(DefaultEnvWriterTest.class.getResource("/default-env-rewritten.txt").getFile()); + + @Test + public void default_env_is_correctly_rewritten() throws IOException { + Path tempFile = temporaryFolder.newFile().toPath(); + Files.copy(EXAMPLE_FILE, tempFile, REPLACE_EXISTING); + + DefaultEnvWriter writer = new DefaultEnvWriter(); + writer.addOverride("VESPA_HOSTNAME", "my-new-hostname"); + writer.addFallback("VESPA_CONFIGSERVER", "new-fallback-configserver"); + writer.addOverride("VESPA_TLS_CONFIG_FILE", "/override/path/to/config.file"); + + boolean modified = writer.updateFile(tempFile); + + assertTrue(modified); + assertEquals(Files.readString(EXPECTED_RESULT_FILE), Files.readString(tempFile)); + + modified = writer.updateFile(tempFile); + assertFalse(modified); + assertEquals(Files.readString(EXPECTED_RESULT_FILE), Files.readString(tempFile)); + } + + @Test + public void generates_default_env_content() throws IOException { + DefaultEnvWriter writer = new DefaultEnvWriter(); + writer.addOverride("VESPA_HOSTNAME", "my-new-hostname"); + writer.addFallback("VESPA_CONFIGSERVER", "new-fallback-configserver"); + writer.addOverride("VESPA_TLS_CONFIG_FILE", "/override/path/to/config.file"); + writer.addUnset("VESPA_LEGACY_OPTION"); + String generatedContent = writer.generateContent(); + assertEquals(Files.readString(EXPECTED_RESULT_FILE), generatedContent); + } +}
\ No newline at end of file diff --git a/node-admin/src/test/resources/default-env-example.txt b/node-admin/src/test/resources/default-env-example.txt new file mode 100644 index 00000000000..debae073271 --- /dev/null +++ b/node-admin/src/test/resources/default-env-example.txt @@ -0,0 +1,5 @@ +override VESPA_HOSTNAME myhostname +fallback VESPA_CONFIGSERVER fallback-configserver +fallback VESPA_TLS_CONFIG_FILE /fallback/path/to/config.file +unset VESPA_LEGACY_OPTION +fallback VESPA_LEGACY_OPTION duplicated-variable
\ No newline at end of file diff --git a/node-admin/src/test/resources/default-env-rewritten.txt b/node-admin/src/test/resources/default-env-rewritten.txt new file mode 100644 index 00000000000..94a91f4e793 --- /dev/null +++ b/node-admin/src/test/resources/default-env-rewritten.txt @@ -0,0 +1,4 @@ +override VESPA_HOSTNAME my-new-hostname +fallback VESPA_CONFIGSERVER new-fallback-configserver +override VESPA_TLS_CONFIG_FILE /override/path/to/config.file +unset VESPA_LEGACY_OPTION |