Skip to content

Commit 9b658db

Browse files
authored
allow usage of Mustache placeholders in additional build command file (#359)
1 parent 4dd7960 commit 9b658db

File tree

6 files changed

+100
-24
lines changed

6 files changed

+100
-24
lines changed

imagetool/src/main/java/com/oracle/weblogic/imagetool/cli/menu/CommonOptions.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ private void handleAdditionalBuildCommands() throws IOException {
7676
throw new FileNotFoundException(Utils.getMessage("IMG-0030", additionalBuildCommandsPath));
7777
}
7878

79-
AdditionalBuildCommands additionalBuildCommands = AdditionalBuildCommands.load(additionalBuildCommandsPath);
80-
dockerfileOptions.setAdditionalBuildCommands(additionalBuildCommands.getContents());
79+
AdditionalBuildCommands additionalBuildCommands = new AdditionalBuildCommands(additionalBuildCommandsPath);
80+
dockerfileOptions.setAdditionalBuildCommands(additionalBuildCommands.getContents(dockerfileOptions));
8181
}
8282

8383
if (additionalBuildFiles != null) {

imagetool/src/main/java/com/oracle/weblogic/imagetool/util/AdditionalBuildCommands.java

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,22 @@
55

66
import java.io.BufferedReader;
77
import java.io.IOException;
8+
import java.io.StringReader;
9+
import java.io.StringWriter;
810
import java.nio.file.Files;
911
import java.nio.file.Path;
1012
import java.text.MessageFormat;
1113
import java.util.ArrayList;
1214
import java.util.HashMap;
1315
import java.util.List;
1416
import java.util.Map;
17+
import java.util.concurrent.Callable;
1518
import java.util.regex.Pattern;
1619
import java.util.stream.Collectors;
1720

21+
import com.github.mustachejava.DefaultMustacheFactory;
22+
import com.github.mustachejava.Mustache;
23+
import com.github.mustachejava.MustacheFactory;
1824
import com.oracle.weblogic.imagetool.logging.LoggingFacade;
1925
import com.oracle.weblogic.imagetool.logging.LoggingFactory;
2026

@@ -30,10 +36,16 @@ public class AdditionalBuildCommands {
3036
public static final String AFTER_WDT = "after-wdt-command";
3137
public static final String FINAL_BLD = "final-build-commands";
3238

33-
private List<NamedPattern> sections;
34-
private Map<String, List<String>> contents;
39+
private final List<NamedPattern> sections;
40+
private final Map<String, List<String>> contents;
3541

36-
private AdditionalBuildCommands() {
42+
/**
43+
* Load the contents of the additional build commands file into this object.
44+
* Existing data in this instance are replaced.
45+
* @param file Path to the additional build commands file that should be loaded.
46+
* @throws IOException if an issue occurs locating or opening the file provided
47+
*/
48+
public AdditionalBuildCommands(Path file) throws IOException {
3749
sections = new ArrayList<>();
3850
sections.add(getPattern(PACKAGES));
3951
sections.add(getPattern(BEFORE_JDK));
@@ -43,6 +55,8 @@ private AdditionalBuildCommands() {
4355
sections.add(getPattern(BEFORE_WDT));
4456
sections.add(getPattern(AFTER_WDT));
4557
sections.add(getPattern(FINAL_BLD));
58+
contents = new HashMap<>();
59+
load(file);
4660
}
4761

4862
private static NamedPattern getPattern(String key) {
@@ -61,8 +75,38 @@ public int size() {
6175
return contents.size();
6276
}
6377

64-
public Map<String,List<String>> getContents() {
65-
return contents;
78+
/**
79+
* Get the contents of the file provided on the command line as additional build commands.
80+
* The file is parsed into a map based on the sections in the file. The returned map
81+
* will have one entry per section found in the provided file (such as "final-build-commands").
82+
* The Callable list value for each entry is for late resolution of the contents. If the user
83+
* provides mustache placeholders in the file, those placeholders will get resolved when the call
84+
* method is invoked and not when getContents is returned. This allows getContents to be called
85+
* before DockerfileOptions is completely populated.
86+
*
87+
* @param options The backing file that will be used to resolve any mustache placeholders in the file
88+
* @return a map by section name of the additional build file contents
89+
*/
90+
public Map<String, Callable<List<String>>> getContents(DockerfileOptions options) {
91+
Map<String, Callable<List<String>>> callableResult = new HashMap<>();
92+
for (Map.Entry<String, List<String>> entry: contents.entrySet()) {
93+
// implements the "call" method so that the contents are resolved at the time they are retrieved
94+
Callable<List<String>> value = () -> {
95+
List<String> resolvedLines = new ArrayList<>();
96+
MustacheFactory mf = new DefaultMustacheFactory();
97+
// Parse each line in the file contents using mustache and the provided "options"
98+
for (String line: entry.getValue()) {
99+
StringWriter writer = new StringWriter();
100+
Mustache mustache = mf.compile(new StringReader(line), "x");
101+
mustache.execute(writer, options);
102+
writer.flush();
103+
resolvedLines.add(writer.toString());
104+
}
105+
return resolvedLines;
106+
};
107+
callableResult.put(entry.getKey(), value);
108+
}
109+
return callableResult;
66110
}
67111

68112
/**
@@ -80,26 +124,23 @@ public List<String> getSection(String name) {
80124
* @param file Path to the additional build commands file that should be loaded.
81125
* @throws IOException if an issue occurs locating or opening the file provided
82126
*/
83-
public static AdditionalBuildCommands load(Path file) throws IOException {
127+
private void load(Path file) throws IOException {
84128
logger.entering(file);
85-
AdditionalBuildCommands result = new AdditionalBuildCommands();
86-
result.contents = new HashMap<>();
87-
88129
try (BufferedReader reader = Files.newBufferedReader(file)) {
89130
List<String> buffer = new ArrayList<>();
90131
String currentSection = null;
91132
String line;
92133
while ((line = reader.readLine()) != null) {
93134
logger.finest(line);
94-
String sectionStart = result.checkForSectionHeader(line);
135+
String sectionStart = checkForSectionHeader(line);
95136
//skip any lines that come before the first section header
96137
if (currentSection != null && sectionStart == null) {
97138
//collect lines inside current section
98139
buffer.add(line);
99140
} else if (currentSection != null) {
100141
//while in a section, found next section start (save last section, and start new section)
101142
logger.fine("IMG-0015", buffer.size(), currentSection);
102-
result.contents.put(currentSection, buffer);
143+
contents.put(currentSection, buffer);
103144
buffer = new ArrayList<>();
104145
currentSection = sectionStart;
105146
} else if (sectionStart != null) {
@@ -111,15 +152,14 @@ public static AdditionalBuildCommands load(Path file) throws IOException {
111152
if (currentSection != null && !buffer.isEmpty()) {
112153
//finished reading file, store the remaining lines that were read for the section
113154
logger.fine("IMG-0015", buffer.size(), currentSection);
114-
result.contents.put(currentSection, buffer);
155+
contents.put(currentSection, buffer);
115156
}
116157
} catch (IOException ioe) {
117158
logger.severe("IMG-0013", file.getFileName());
118159
throw ioe;
119160
}
120161

121162
logger.exiting();
122-
return result;
123163
}
124164

125165
private String checkForSectionHeader(String line) {

imagetool/src/main/java/com/oracle/weblogic/imagetool/util/DockerfileOptions.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,24 @@
1111
import java.util.List;
1212
import java.util.Map;
1313
import java.util.StringJoiner;
14+
import java.util.concurrent.Callable;
1415
import java.util.stream.Collectors;
1516

1617
import com.oracle.weblogic.imagetool.aru.AruPatch;
1718
import com.oracle.weblogic.imagetool.cli.menu.PackageManagerType;
1819
import com.oracle.weblogic.imagetool.installer.MiddlewareInstall;
1920
import com.oracle.weblogic.imagetool.installer.MiddlewareInstallPackage;
21+
import com.oracle.weblogic.imagetool.logging.LoggingFacade;
22+
import com.oracle.weblogic.imagetool.logging.LoggingFactory;
2023
import com.oracle.weblogic.imagetool.wdt.WdtOperation;
2124

2225
/**
2326
* Provides the data used by the Dockerfile templates (in mustache).
2427
*/
2528
public class DockerfileOptions {
2629

30+
private static final LoggingFacade logger = LoggingFactory.getLogger(DockerfileOptions.class);
31+
2732
private static final String DEFAULT_JAVA_HOME = "/u01/jdk";
2833
private static final String DEFAULT_ORACLE_HOME = "/u01/oracle";
2934
private static final String DEFAULT_DOMAIN_HOME = "/u01/domains/base_domain";
@@ -79,7 +84,7 @@ public class DockerfileOptions {
7984
private String wdtBase;
8085

8186
// Additional Build Commands
82-
private Map<String, List<String>> additionalBuildCommands;
87+
private Map<String, Callable<List<String>>> additionalBuildCommands;
8388

8489
/**
8590
* Options to be used with the Mustache template.
@@ -897,16 +902,21 @@ public String wlsdeploy_properties() {
897902
*
898903
* @param commands Additional build commands grouped by section.
899904
*/
900-
public DockerfileOptions setAdditionalBuildCommands(Map<String, List<String>> commands) {
905+
public DockerfileOptions setAdditionalBuildCommands(Map<String, Callable<List<String>>> commands) {
901906
additionalBuildCommands = commands;
902907
return this;
903908
}
904909

905910
private List<String> getAdditionalCommandsForSection(String sectionName) {
906-
if (additionalBuildCommands == null) {
911+
if (additionalBuildCommands == null || !additionalBuildCommands.containsKey(sectionName)) {
912+
return Collections.emptyList();
913+
}
914+
try {
915+
return additionalBuildCommands.get(sectionName).call();
916+
} catch (Exception e) {
917+
logger.severe("IMG-0114", sectionName, e.getLocalizedMessage());
907918
return Collections.emptyList();
908919
}
909-
return additionalBuildCommands.get(sectionName);
910920
}
911921

912922
@SuppressWarnings("unused")

imagetool/src/main/resources/ImageTool.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,4 @@ IMG-0110=Retries exhausted, unable to obtain patch information from Oracle
112112
IMG-0111=The directory specified with WLSIMG_BLDDIR must exist prior to running this tool: {0}
113113
IMG-0112=The value specified with WLSIMG_BLDDIR must be a directory: {0}
114114
IMG-0113=The directory specified with WLSIMG_BLDDIR must be writable: {0}
115+
IMG-0114=Unable to parse section {0} of additionalBuildCommands: {1}

imagetool/src/test/java/com/oracle/weblogic/imagetool/util/AdditionalBuildCommandsTest.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import java.io.IOException;
77
import java.nio.file.Path;
88
import java.nio.file.Paths;
9+
import java.util.Arrays;
10+
import java.util.Collections;
11+
import java.util.List;
912

1013
import org.junit.jupiter.api.Tag;
1114
import org.junit.jupiter.api.Test;
@@ -23,15 +26,15 @@ private static Path getPath(String filename) {
2326
@Test
2427
void loadSingleSectionFile() throws IOException {
2528
String filename = "one-section.txt";
26-
AdditionalBuildCommands cmds = AdditionalBuildCommands.load(getPath(filename));
29+
AdditionalBuildCommands cmds = new AdditionalBuildCommands(getPath(filename));
2730

2831
assertEquals(1, cmds.size(), "File did not have expected number of sections: " + filename);
2932
}
3033

3134
@Test
3235
void loadTwoSectionFile() throws IOException {
3336
String filename = "two-sections.txt";
34-
AdditionalBuildCommands cmds = AdditionalBuildCommands.load(getPath(filename));
37+
AdditionalBuildCommands cmds = new AdditionalBuildCommands(getPath(filename));
3538
assertEquals(2, cmds.size(), "File did not have expected number of sections: " + filename);
3639
assertEquals(3, cmds.getSection(AdditionalBuildCommands.AFTER_FMW).size(),
3740
"Wrong number of lines in AFTER_FMW section of " + filename);
@@ -40,8 +43,24 @@ void loadTwoSectionFile() throws IOException {
4043
}
4144

4245
@Test
43-
void loadBadSectionFile() throws IOException {
44-
String filename = "bad-section.txt";
45-
assertThrows(IllegalArgumentException.class, () -> AdditionalBuildCommands.load(getPath(filename)));
46+
void loadBadSectionFile() {
47+
Path file = getPath("bad-section.txt");
48+
assertThrows(IllegalArgumentException.class, () -> new AdditionalBuildCommands(file));
49+
}
50+
51+
@Test
52+
void resolveMustachePlaceHolders() throws Exception {
53+
AdditionalBuildCommands cmds = new AdditionalBuildCommands(getPath("two-mustache.txt"));
54+
DockerfileOptions options = new DockerfileOptions("123");
55+
// default Oracle Home and Java Home are expected, /u01/oracle, /u01/jdk.
56+
options.setAdditionalBuildCommands(cmds.getContents(options));
57+
58+
List<String> expected = Arrays.asList(
59+
"echo This is the Oracle Home: /u01/oracle",
60+
"echo This is the Java Home: /u01/jdk");
61+
assertEquals(expected, options.finalBuildCommands());
62+
63+
// If a section is requested that is NOT in the file, the getter should return an empty list.
64+
assertEquals(Collections.emptyList(), options.beforeFmwInstall());
4665
}
4766
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright 2022, Oracle Corporation and/or its affiliates. All rights reserved.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl.
3+
4+
[final-build-commands]
5+
echo This is the Oracle Home: {{{oracle_home}}}
6+
echo This is the Java Home: {{{java_home}}}

0 commit comments

Comments
 (0)