Skip to content

Commit c157e55

Browse files
authored
additional build commands allow users to inject custom Docker commands (#87)
* New feature, additional build commands allow users to inject custom commands into the Docker build step * updated documentation for the additional commands parameter * added dry run feature for update/create to print the Dockerfile without executing it
1 parent b5566a5 commit c157e55

File tree

16 files changed

+634
-168
lines changed

16 files changed

+634
-168
lines changed

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,12 @@ public CommandResponse call() throws Exception {
113113
Utils.setOracleHome(installerResponseFile, dockerfileOptions);
114114

115115
// Create Dockerfile
116-
Utils.writeDockerfile(tmpDir + File.separator + "Dockerfile", "Create_Image.mustache",
117-
dockerfileOptions);
116+
String dockerfile = Utils.writeDockerfile(tmpDir + File.separator + "Dockerfile",
117+
"Create_Image.mustache", dockerfileOptions, dryRun);
118118

119119
// add directory to pass the context
120120
cmdBuilder.add(tmpDir);
121-
logger.info("docker cmd = " + String.join(" ", cmdBuilder));
122-
Utils.runDockerCommand(isCliMode, cmdBuilder, dockerLog);
121+
runDockerCommand(dockerfile, cmdBuilder);
123122
} catch (Exception ex) {
124123
return new CommandResponse(-1, ex.getMessage());
125124
} finally {

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

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package com.oracle.weblogic.imagetool.cli.menu;
55

66
import java.io.File;
7+
import java.io.FileNotFoundException;
78
import java.io.IOException;
89
import java.nio.file.Files;
910
import java.nio.file.Path;
@@ -31,6 +32,7 @@
3132
import com.oracle.weblogic.imagetool.logging.LoggingFacade;
3233
import com.oracle.weblogic.imagetool.logging.LoggingFactory;
3334
import com.oracle.weblogic.imagetool.util.ARUUtil;
35+
import com.oracle.weblogic.imagetool.util.AdditionalBuildCommands;
3436
import com.oracle.weblogic.imagetool.util.Constants;
3537
import com.oracle.weblogic.imagetool.util.DockerfileOptions;
3638
import com.oracle.weblogic.imagetool.util.HttpUtil;
@@ -73,6 +75,7 @@ public CommandResponse call() throws Exception {
7375
}
7476

7577
handleChown();
78+
handleAdditionalBuildCommands();
7679

7780
logger.finer("Exiting ImageOperation call ");
7881
return new CommandResponse(0, null);
@@ -272,6 +275,18 @@ private void handleChown() {
272275
dockerfileOptions.setGroupId(osUserAndGroup[1]);
273276
}
274277

278+
private void handleAdditionalBuildCommands() throws IOException {
279+
if (additionalBuildCommandsPath != null) {
280+
if (!Files.isRegularFile(additionalBuildCommandsPath)) {
281+
throw new FileNotFoundException("Additional build command file does not exist: "
282+
+ additionalBuildCommandsPath);
283+
}
284+
285+
AdditionalBuildCommands additionalBuildCommands = AdditionalBuildCommands.load(additionalBuildCommandsPath);
286+
dockerfileOptions.setAdditionalBuildCommands(additionalBuildCommands.getContents());
287+
}
288+
}
289+
275290
/**
276291
* Builds a list of build args to pass on to docker with the required installer files.
277292
* And, copies the installers over to build context dir.
@@ -413,6 +428,18 @@ public DomainType getWdtDomainType() {
413428
return wdtDomainType;
414429
}
415430

431+
void runDockerCommand(String dockerfile, List<String> command) throws IOException, InterruptedException {
432+
logger.info("docker cmd = " + String.join(" ", command));
433+
434+
if (dryRun) {
435+
System.out.println("########## BEGIN DOCKERFILE ##########");
436+
System.out.println(dockerfile);
437+
System.out.println("########## END DOCKERFILE ##########");
438+
} else {
439+
Utils.runDockerCommand(isCliMode, command, dockerLog);
440+
}
441+
}
442+
416443
@Option(
417444
names = {"--useCache"},
418445
paramLabel = "<Cache Policy>",
@@ -559,7 +586,6 @@ public DomainType getWdtDomainType() {
559586
)
560587
private boolean runRcu = false;
561588

562-
563589
@Option(
564590
names = {"--wdtDomainHome"},
565591
description = "pass to the -domain_home for wdt",
@@ -587,6 +613,18 @@ public DomainType getWdtDomainType() {
587613
)
588614
private boolean wdtStrictValidation = false;
589615

616+
@Option(
617+
names = {"--additionalBuildCommands"},
618+
description = "path to a file with additional build commands"
619+
)
620+
private Path additionalBuildCommandsPath;
621+
622+
@Option(
623+
names = {"--dryRun"},
624+
description = "Skip Docker build execution and print Dockerfile to stdout"
625+
)
626+
boolean dryRun = false;
627+
590628
@Unmatched
591629
List<String> unmatchedOptions;
592630
}

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,12 @@ public CommandResponse call() throws Exception {
152152
cmdBuilder.addAll(handlePatchFiles(lsinventoryText));
153153

154154
// create dockerfile
155-
Utils.writeDockerfile(
156-
tmpDir + File.separator + "Dockerfile", "Update_Image.mustache", dockerfileOptions);
155+
String dockerfile = Utils.writeDockerfile(tmpDir + File.separator + "Dockerfile",
156+
"Update_Image.mustache", dockerfileOptions, dryRun);
157+
157158
// add directory to pass the context
158159
cmdBuilder.add(tmpDir);
159-
160-
logger.info("docker cmd = " + String.join(" ", cmdBuilder));
161-
Utils.runDockerCommand(isCliMode, cmdBuilder, dockerLog);
162-
160+
runDockerCommand(dockerfile, cmdBuilder);
163161
} catch (Exception ex) {
164162
return new CommandResponse(-1, ex.getMessage());
165163
} finally {
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Copyright 2019, 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+
package com.oracle.weblogic.imagetool.util;
5+
6+
import java.io.BufferedReader;
7+
import java.io.IOException;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
import java.text.MessageFormat;
11+
import java.util.ArrayList;
12+
import java.util.HashMap;
13+
import java.util.List;
14+
import java.util.Map;
15+
import java.util.regex.Pattern;
16+
import java.util.stream.Collectors;
17+
18+
import com.oracle.weblogic.imagetool.logging.LoggingFacade;
19+
import com.oracle.weblogic.imagetool.logging.LoggingFactory;
20+
21+
public class AdditionalBuildCommands {
22+
private static final LoggingFacade logger = LoggingFactory.getLogger(AdditionalBuildCommands.class);
23+
24+
public static final String BEFORE_JDK = "before-jdk-install";
25+
public static final String AFTER_JDK = "after-jdk-install";
26+
public static final String BEFORE_FMW = "before-fmw-install";
27+
public static final String AFTER_FMW = "after-fmw-install";
28+
public static final String BEFORE_WDT = "before-wdt-command";
29+
public static final String AFTER_WDT = "after-wdt-command";
30+
public static final String FINAL_BLD = "final-build-commands";
31+
32+
private List<NamedPattern> sections;
33+
private Map<String, List<String>> contents;
34+
35+
private AdditionalBuildCommands() {
36+
sections = new ArrayList<>();
37+
sections.add(getPattern(BEFORE_JDK));
38+
sections.add(getPattern(AFTER_JDK));
39+
sections.add(getPattern(BEFORE_FMW));
40+
sections.add(getPattern(AFTER_FMW));
41+
sections.add(getPattern(BEFORE_WDT));
42+
sections.add(getPattern(AFTER_WDT));
43+
sections.add(getPattern(FINAL_BLD));
44+
}
45+
46+
private static NamedPattern getPattern(String key) {
47+
return new NamedPattern(key, getSectionHeaderString(key));
48+
}
49+
50+
private static String getSectionHeaderString(String key) {
51+
return MessageFormat.format("\\s*\\[\\s*{0}\\s*\\]\\s*", key);
52+
}
53+
54+
/**
55+
* Once a file is loaded, size returns the number of sections found in the file.
56+
* @return the count of sections found in the file.
57+
*/
58+
public int size() {
59+
return contents.size();
60+
}
61+
62+
public Map<String,List<String>> getContents() {
63+
return contents;
64+
}
65+
66+
/**
67+
* Once a file is loaded, getSection should return the contents of a single section, by name.
68+
* @param name the name of the section to return
69+
* @return the contents of the section requested or null if the section was not found.
70+
*/
71+
public List<String> getSection(String name) {
72+
return contents.get(name);
73+
}
74+
75+
/**
76+
* Load the contents of the additional build commands file into this object.
77+
* Existing data in this instance are replaced.
78+
* @param file Path to the additional build commands file that should be loaded.
79+
* @throws IOException if an issue occurs locating or opening the file provided
80+
*/
81+
public static AdditionalBuildCommands load(Path file) throws IOException {
82+
logger.entering(file);
83+
AdditionalBuildCommands result = new AdditionalBuildCommands();
84+
result.contents = new HashMap<>();
85+
86+
try (BufferedReader reader = Files.newBufferedReader(file)) {
87+
List<String> buffer = new ArrayList<>();
88+
String currentSection = null;
89+
String line;
90+
while ((line = reader.readLine()) != null) {
91+
logger.finest(line);
92+
String sectionStart = result.checkForSectionHeader(line);
93+
//skip any lines that come before the first section header
94+
if (currentSection != null && sectionStart == null) {
95+
//collect lines inside current section
96+
buffer.add(line);
97+
} else if (currentSection != null) {
98+
//while in a section, found next section start (save last section, and start new section)
99+
logger.fine("IMG-0015", buffer.size(), currentSection);
100+
result.contents.put(currentSection, buffer);
101+
buffer = new ArrayList<>();
102+
currentSection = sectionStart;
103+
} else if (sectionStart != null) {
104+
//current section was null, but found new section start
105+
currentSection = sectionStart;
106+
}
107+
}
108+
109+
if (currentSection != null && buffer.size() > 0) {
110+
//finished reading file, store the remaining lines that were read for the section
111+
logger.fine("IMG-0015", buffer.size(), currentSection);
112+
result.contents.put(currentSection, buffer);
113+
}
114+
} catch (IOException ioe) {
115+
logger.severe("IMG-0013", file.getFileName());
116+
throw ioe;
117+
}
118+
119+
logger.exiting();
120+
return result;
121+
}
122+
123+
private String checkForSectionHeader(String line) {
124+
//Pattern: looks like something in square brackets, ignoring whitespace
125+
Pattern sectionLike = Pattern.compile(getSectionHeaderString(".*"));
126+
if (!sectionLike.matcher(line).matches()) {
127+
//line does not look like a header "[something]"
128+
return null;
129+
}
130+
131+
for (NamedPattern section : sections) {
132+
if (section.matches(line)) {
133+
return section.getName();
134+
}
135+
}
136+
throw new IllegalArgumentException(Utils.getMessage("IMG-0014", line, validSectionNames()));
137+
}
138+
139+
/**
140+
* Returns a list of valid section names for the additional build commands file.
141+
* @return comma separated list of section names
142+
*/
143+
public String validSectionNames() {
144+
return sections.stream()
145+
.map(NamedPattern::getName)
146+
.collect(Collectors.joining(", "));
147+
}
148+
}

0 commit comments

Comments
 (0)