Skip to content

Commit 58f1ee2

Browse files
authored
props: allow for custom properties (#321)
1 parent f1da0e7 commit 58f1ee2

File tree

2 files changed

+111
-33
lines changed

2 files changed

+111
-33
lines changed

src/main/java/me/itzg/helpers/properties/SetPropertiesCommand.java

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
import java.nio.file.Path;
88
import java.nio.file.StandardOpenOption;
99
import java.time.Instant;
10+
import java.util.Collections;
1011
import java.util.Map;
12+
import java.util.Map.Entry;
1113
import java.util.Objects;
1214
import java.util.Properties;
1315
import java.util.concurrent.Callable;
@@ -29,8 +31,13 @@ public class SetPropertiesCommand implements Callable<Integer> {
2931
public static final TypeReference<Map<String, PropertyDefinition>> PROPERTY_DEFINITIONS_TYPE = new TypeReference<Map<String, PropertyDefinition>>() {
3032
};
3133

32-
@Option(names = "--definitions", required = true, description = "JSON file of property names to PropertyDefinition mappings")
33-
Path propertyDefinitions;
34+
@Option(names = "--definitions", description = "JSON file of property names to PropertyDefinition mappings")
35+
Path propertyDefinitionsFile;
36+
37+
@Option(names = {"--custom-property", "--custom-properties", "-p"},
38+
split = "\n", splitSynopsisLabel = "<NL>",
39+
description = "Key=value pairs of custom properties to set")
40+
Map<String,String> customProperties;
3441

3542
@Parameters(arity = "1")
3643
Path propertiesFile;
@@ -40,13 +47,23 @@ public class SetPropertiesCommand implements Callable<Integer> {
4047

4148
@Override
4249
public Integer call() throws Exception {
43-
44-
if (!Files.exists(propertyDefinitions)) {
45-
throw new InvalidParameterException("Property definitions file does not exist");
50+
if (propertyDefinitionsFile == null && customProperties == null) {
51+
System.err.println("Either definitions or custom properties need to be provided");
52+
return ExitCode.USAGE;
4653
}
4754

48-
final Map<String, PropertyDefinition> propertyDefinitions = ObjectMappers.defaultMapper()
49-
.readValue(this.propertyDefinitions.toFile(), PROPERTY_DEFINITIONS_TYPE);
55+
final Map<String, PropertyDefinition> propertyDefinitions;
56+
if (propertyDefinitionsFile != null) {
57+
if (!Files.exists(propertyDefinitionsFile)) {
58+
throw new InvalidParameterException("Property definitions file does not exist");
59+
}
60+
61+
propertyDefinitions = ObjectMappers.defaultMapper()
62+
.readValue(this.propertyDefinitionsFile.toFile(), PROPERTY_DEFINITIONS_TYPE);
63+
}
64+
else {
65+
propertyDefinitions = Collections.emptyMap();
66+
}
5067

5168
final Properties properties = new Properties();
5269
if (Files.exists(propertiesFile)) {
@@ -55,11 +72,11 @@ public Integer call() throws Exception {
5572
}
5673
}
5774

58-
final long changes = processProperties(propertyDefinitions, properties);
75+
final long changes = processProperties(propertyDefinitions, properties, customProperties);
5976
if (changes > 0) {
6077
log.info("Created/updated {} propert{} in {}", changes, changes != 1 ? "ies":"y", propertiesFile);
6178

62-
try (OutputStream propsOut = Files.newOutputStream(propertiesFile, StandardOpenOption.TRUNCATE_EXISTING)) {
79+
try (OutputStream propsOut = Files.newOutputStream(propertiesFile, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) {
6380
properties.store(propsOut, String.format("Updated %s by mc-image-helper", Instant.now()));
6481
}
6582
}
@@ -70,40 +87,56 @@ public Integer call() throws Exception {
7087
/**
7188
* @return count of added/modified properties
7289
*/
73-
private long processProperties(Map<String, PropertyDefinition> propertyDefinitions, Properties properties) {
74-
return propertyDefinitions.entrySet().stream()
75-
.map(entry -> {
76-
final String name = entry.getKey();
77-
final PropertyDefinition definition = entry.getValue();
78-
79-
if (definition.isRemove()) {
80-
if (properties.containsKey(name)) {
81-
log.debug("Removing {}, which is marked for removal", name);
82-
properties.remove(name);
83-
return true;
84-
}
85-
else {
86-
return false;
87-
}
90+
private long processProperties(Map<String, PropertyDefinition> propertyDefinitions, Properties properties,
91+
Map<String, String> customProperties
92+
) {
93+
long modifiedViaDefinitions = 0;
94+
for (final Entry<String, PropertyDefinition> entry : propertyDefinitions.entrySet()) {
95+
final String name = entry.getKey();
96+
final PropertyDefinition definition = entry.getValue();
97+
98+
if (definition.isRemove()) {
99+
if (properties.containsKey(name)) {
100+
log.debug("Removing {}, which is marked for removal", name);
101+
properties.remove(name);
102+
++modifiedViaDefinitions;
88103
}
89-
104+
}
105+
else {
90106
final String envValue = environmentVariablesProvider.get(definition.getEnv());
91107
if (envValue != null) {
92108
final String expectedValue = mapAndValidateValue(definition, envValue);
93109

94110
final String propValue = properties.getProperty(name);
95111

96112
if (!Objects.equals(expectedValue, propValue)) {
97-
log.debug("Setting property {} to new value '{}'", name, expectedValue);
113+
log.debug("Setting property {} to new value '{}'", name, needsValueRedacted(name) ? "***" : expectedValue);
98114
properties.setProperty(name, expectedValue);
99-
return true;
115+
++modifiedViaDefinitions;
100116
}
101117
}
118+
}
119+
}
120+
121+
long modifiedViaCustom = 0;
122+
if (customProperties != null) {
123+
for (final Entry<String, String> entry : customProperties.entrySet()) {
124+
final String name = entry.getKey();
125+
final String targetValue = entry.getValue();
126+
final String propValue = properties.getProperty(name);
127+
if (!Objects.equals(targetValue, propValue)) {
128+
log.debug("Setting property {} to new value '{}'", name, targetValue);
129+
properties.setProperty(name, targetValue);
130+
++modifiedViaCustom;
131+
}
132+
}
133+
}
134+
135+
return modifiedViaDefinitions + modifiedViaCustom;
136+
}
102137

103-
return false;
104-
})
105-
.filter(modified -> modified)
106-
.count();
138+
private static boolean needsValueRedacted(String name) {
139+
return name.contains("password");
107140
}
108141

109142
private String mapAndValidateValue(PropertyDefinition definition, String value) {

src/test/java/me/itzg/helpers/properties/SetPropertiesCommandTest.java

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,7 @@ void disallowedValue() throws Exception {
111111
@Test
112112
void removesMarkedForRemoval() throws IOException {
113113
final Path hasWhiteList = Files.write(tempDir.resolve("old.properties"), Collections.singletonList("white-list=true"));
114-
final int exitCode = new CommandLine(new SetPropertiesCommand()
115-
)
114+
final int exitCode = new CommandLine(new SetPropertiesCommand())
116115
.execute(
117116
"--definitions", definitionsFile.toString(),
118117
hasWhiteList.toString()
@@ -128,6 +127,52 @@ void removesMarkedForRemoval() throws IOException {
128127
assertThat(properties).doesNotContainKey("white-list");
129128
}
130129

130+
@Test
131+
void handlesNewCustomProperty() throws IOException {
132+
final Path outputProperties = tempDir.resolve("output.properties");
133+
134+
final int exitCode = new CommandLine(new SetPropertiesCommand())
135+
.execute(
136+
"--custom-property", "key1=value1",
137+
outputProperties.toString()
138+
);
139+
140+
assertThat(exitCode).isEqualTo(ExitCode.OK);
141+
142+
final Properties properties = new Properties();
143+
try (InputStream propsIn = Files.newInputStream(outputProperties)) {
144+
properties.load(propsIn);
145+
}
146+
147+
assertThat(properties)
148+
.containsEntry("key1", "value1");
149+
}
150+
151+
@Test
152+
void handlesModifiedCustomProperties() throws IOException {
153+
final Path outputProperties = tempDir.resolve("output.properties");
154+
Files.write(outputProperties, Collections.singletonList("key1=value1"));
155+
156+
final int exitCode = new CommandLine(new SetPropertiesCommand())
157+
.execute(
158+
"--custom-property", "key1=newValue1",
159+
"--custom-property", "key2=value2",
160+
outputProperties.toString()
161+
);
162+
163+
assertThat(exitCode).isEqualTo(ExitCode.OK);
164+
165+
final Properties properties = new Properties();
166+
try (InputStream propsIn = Files.newInputStream(outputProperties)) {
167+
properties.load(propsIn);
168+
}
169+
170+
assertThat(properties)
171+
.hasSize(2)
172+
.containsEntry("key1", "newValue1")
173+
.containsEntry("key2", "value2");
174+
}
175+
131176
private void assertPropertiesEqualExcept(Properties properties, String... propertiesToIgnore) {
132177
final HashSet<Object> actualKeys = new HashSet<>(properties.keySet());
133178
Arrays.asList(propertiesToIgnore).forEach(actualKeys::remove);

0 commit comments

Comments
 (0)