Skip to content

Commit bf4b872

Browse files
authored
fix patching handling for JRF installers (#73)
- Fixes error "FMW installer is required for JRF domain" when using WDT to create a JRF domain. - Fixes ability to apply JRF latestPSU and patches for JRF installers
1 parent 141b631 commit bf4b872

File tree

8 files changed

+170
-328
lines changed

8 files changed

+170
-328
lines changed

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

Lines changed: 21 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,17 @@
1010
import java.nio.file.Paths;
1111
import java.time.Duration;
1212
import java.time.Instant;
13-
import java.util.ArrayList;
14-
import java.util.HashSet;
1513
import java.util.List;
1614
import java.util.Properties;
17-
import java.util.Set;
1815

1916
import com.oracle.weblogic.imagetool.api.model.CommandResponse;
2017
import com.oracle.weblogic.imagetool.api.model.InstallerType;
2118
import com.oracle.weblogic.imagetool.api.model.WLSInstallerType;
2219
import com.oracle.weblogic.imagetool.impl.InstallerFile;
2320
import com.oracle.weblogic.imagetool.logging.LoggingFacade;
2421
import com.oracle.weblogic.imagetool.logging.LoggingFactory;
25-
import com.oracle.weblogic.imagetool.util.ARUUtil;
2622
import com.oracle.weblogic.imagetool.util.Constants;
2723
import com.oracle.weblogic.imagetool.util.Utils;
28-
import com.oracle.weblogic.imagetool.util.ValidationResult;
2924
import picocli.CommandLine.Command;
3025
import picocli.CommandLine.Option;
3126

@@ -105,8 +100,13 @@ public CommandResponse call() throws Exception {
105100
// build wdt args if user passes --wdtModelPath
106101
cmdBuilder.addAll(handleWdtArgsIfRequired(tmpDir));
107102

103+
// If patching, patch OPatch first
104+
if (applyingPatches()) {
105+
addOPatch1394ToImage(tmpDir, opatchBugNumber);
106+
}
107+
108108
// resolve required patches
109-
cmdBuilder.addAll(handlePatchFiles(tmpDir, createPatchesTempDirectory(tmpDir)));
109+
cmdBuilder.addAll(handlePatchFiles(null));
110110

111111
// Copy wls response file to tmpDir
112112
copyResponseFilesToDir(tmpDir);
@@ -133,43 +133,6 @@ public CommandResponse call() throws Exception {
133133
+ Duration.between(startTime, endTime).getSeconds() + "s. image tag: " + imageTag);
134134
}
135135

136-
@Override
137-
List<String> handlePatchFiles(String tmpDir, Path tmpPatchesDir) throws Exception {
138-
logger.finer("Entering CreateImage.handlePatchFiles: " + tmpDir);
139-
if ((latestPSU || !patches.isEmpty()) && Utils.compareVersions(installerVersion,
140-
Constants.DEFAULT_WLS_VERSION) == 0) {
141-
addOPatch1394ToImage(tmpDir, opatchBugNumber);
142-
Set<String> toValidateSet = new HashSet<>();
143-
if (latestPSU) {
144-
toValidateSet.add(ARUUtil.getLatestPSUNumber(installerType.toString(), installerVersion,
145-
userId, password));
146-
if (installerType.toString().equals(Constants.INSTALLER_FMW)) {
147-
toValidateSet.add(ARUUtil.getLatestPSUNumber(Constants.INSTALLER_WLS, installerVersion,
148-
userId, password));
149-
}
150-
151-
}
152-
toValidateSet.addAll(patches);
153-
154-
ValidationResult validationResult = ARUUtil.validatePatches(null,
155-
new ArrayList<>(toValidateSet), installerType.toString(), installerVersion, userId,
156-
password);
157-
if (validationResult.isSuccess()) {
158-
logger.info("IMG-0006");
159-
} else {
160-
String error = validationResult.getErrorMessage();
161-
logger.severe(error);
162-
throw new IllegalArgumentException(error);
163-
}
164-
165-
166-
}
167-
//we need a local installerVersion variable for the command line Option. so propagate to super.
168-
super.installerVersion = installerVersion;
169-
logger.finer("Exiting CreateImage.handlePatchFiles: ");
170-
return super.handlePatchFiles(tmpDir, tmpPatchesDir);
171-
}
172-
173136
/**
174137
* Builds a list of {@link InstallerFile} objects based on user input which are processed.
175138
* to download the required install artifacts
@@ -180,9 +143,9 @@ List<String> handlePatchFiles(String tmpDir, Path tmpPatchesDir) throws Exceptio
180143
protected List<InstallerFile> gatherRequiredInstallers() throws Exception {
181144
logger.entering();
182145
List<InstallerFile> retVal = super.gatherRequiredInstallers();
183-
logger.finer("IMG-0001", installerType, installerVersion);
184-
retVal.add(new InstallerFile(useCache, InstallerType.fromValue(installerType.toString()), installerVersion,
185-
userId, password));
146+
logger.finer("IMG-0001", getInstallerType(), getInstallerVersion());
147+
retVal.add(new InstallerFile(useCache, InstallerType.fromValue(getInstallerType().toString()),
148+
getInstallerVersion(), userId, password));
186149
if (dockerfileOptions.installJava()) {
187150
logger.finer("IMG-0001", InstallerType.JDK, jdkVersion);
188151
retVal.add(new InstallerFile(useCache, InstallerType.JDK, jdkVersion, userId, password));
@@ -212,13 +175,21 @@ private void copyResponseFilesToDir(String dirPath) throws IOException {
212175
Utils.copyResourceAsFile("/response-files/oraInst.loc", dirPath, false);
213176
}
214177

178+
@Override
179+
public WLSInstallerType getInstallerType() {
180+
return installerType;
181+
}
182+
183+
@Override
184+
public String getInstallerVersion() {
185+
return installerVersion;
186+
}
187+
215188
@Option(
216189
names = {"--type"},
217-
description = "Installer type. Default: ${DEFAULT-VALUE}. Supported values: ${COMPLETION-CANDIDATES}",
218-
required = true,
219-
defaultValue = "wls"
190+
description = "Installer type. Default: ${DEFAULT-VALUE}. Supported values: ${COMPLETION-CANDIDATES}"
220191
)
221-
private WLSInstallerType installerType;
192+
private WLSInstallerType installerType = WLSInstallerType.WLS;
222193

223194
@Option(
224195
names = {"--version"},
@@ -242,13 +213,6 @@ private void copyResponseFilesToDir(String dirPath) throws IOException {
242213
)
243214
private String fromImage;
244215

245-
@Option(
246-
names = {"--opatchBugNumber"},
247-
description = "use this opatch patch bug number",
248-
defaultValue = "28186730"
249-
)
250-
private String opatchBugNumber;
251-
252216
@Option(
253217
names = {"--installerResponseFile"},
254218
description = "path to a response file. Override the default responses for the Oracle installer"

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

Lines changed: 76 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,14 @@
44
package com.oracle.weblogic.imagetool.cli.menu;
55

66
import java.io.File;
7-
import java.io.FileInputStream;
87
import java.io.IOException;
98
import java.nio.file.Files;
109
import java.nio.file.Path;
1110
import java.nio.file.Paths;
1211
import java.util.ArrayList;
13-
import java.util.Arrays;
1412
import java.util.Collections;
1513
import java.util.LinkedList;
1614
import java.util.List;
17-
import java.util.Properties;
1815
import java.util.concurrent.Callable;
1916
import java.util.regex.Matcher;
2017
import java.util.regex.Pattern;
@@ -38,16 +35,18 @@
3835
import com.oracle.weblogic.imagetool.util.DockerfileOptions;
3936
import com.oracle.weblogic.imagetool.util.HttpUtil;
4037
import com.oracle.weblogic.imagetool.util.Utils;
38+
import com.oracle.weblogic.imagetool.util.ValidationResult;
4139
import picocli.CommandLine.Option;
4240
import picocli.CommandLine.Unmatched;
4341

4442
public abstract class ImageOperation implements Callable<CommandResponse> {
4543

4644
private static final LoggingFacade logger = LoggingFactory.getLogger(ImageOperation.class);
4745
// DockerfileOptions provides switches and values to the customize the Dockerfile template
48-
protected DockerfileOptions dockerfileOptions;
46+
DockerfileOptions dockerfileOptions;
4947
protected CacheStore cacheStore = new CacheStoreFactory().get();
5048
private String nonProxyHosts = null;
49+
private String tempDirectory = null;
5150
boolean isCliMode;
5251
String password;
5352

@@ -89,43 +88,80 @@ private String handlePasswordOptions() throws IOException {
8988
return Utils.getPasswordFromInputs(passwordStr, passwordFile, passwordEnv);
9089
}
9190

91+
/**
92+
* Return the WLS installer type for this operation.
93+
* @return WLS, FMW, or RestrictedJRF
94+
*/
95+
public abstract WLSInstallerType getInstallerType();
96+
97+
/**
98+
* Return the WLS installer version string.
99+
* @return something like 12.2.1.3.0
100+
*/
101+
public abstract String getInstallerVersion();
102+
103+
/**
104+
* Returns true if any patches should be applied.
105+
* A PSU is considered a patch.
106+
* @return true if applying patches
107+
*/
108+
boolean applyingPatches() {
109+
return latestPSU || !patches.isEmpty();
110+
}
111+
92112
/**
93113
* Builds a list of build args to pass on to docker with the required patches.
94114
* Also, creates links to patches directory under build context instead of copying over.
95115
*
96-
* @param tmpDir build context dir
97-
* @param tmpPatchesDir patches dir under build context
98116
* @return list of strings
99117
* @throws Exception in case of error
100118
*/
101-
List<String> handlePatchFiles(String tmpDir, Path tmpPatchesDir) throws Exception {
119+
List<String> handlePatchFiles(String previousInventory) throws Exception {
102120
logger.entering();
103121
List<String> retVal = new LinkedList<>();
122+
123+
if (!applyingPatches()) {
124+
return retVal;
125+
}
126+
127+
String toPatchesPath = createPatchesTempDirectory().toAbsolutePath().toString();
128+
104129
List<String> patchLocations = new LinkedList<>();
105-
String toPatchesPath = tmpPatchesDir.toAbsolutePath().toString();
130+
131+
List<String> patchList = new ArrayList<>(patches);
106132

107133
if (latestPSU) {
108134
if (userId == null || password == null) {
109-
throw new Exception("No credentials provided. Cannot determine "
110-
+ "latestPSU");
111-
} else {
112-
String patchId = ARUUtil.getLatestPSUNumber(installerType.toString(), installerVersion,
113-
userId,
114-
password);
115-
if (Utils.isEmptyString(patchId)) {
116-
throw new Exception(String.format("Failed to find latest psu for product category %s, version %s",
117-
installerType.toString(), installerVersion));
118-
}
119-
logger.finest("Found latest PSU " + patchId);
120-
FileResolver psuResolver = new PatchFile(useCache, installerType.toString(), installerVersion,
121-
patchId, userId, password);
122-
patchLocations.add(psuResolver.resolve(cacheStore));
135+
throw new Exception("No credentials provided. Cannot determine latestPSU");
123136
}
124137

138+
// PSUs for WLS and JRF installers are considered WLS patches
139+
String patchId = ARUUtil.getLatestPSUNumber(WLSInstallerType.WLS, getInstallerVersion(), userId, password);
140+
if (Utils.isEmptyString(patchId)) {
141+
throw new Exception(String.format("Failed to find latest psu for product category %s, version %s",
142+
getInstallerType(), getInstallerVersion()));
143+
}
144+
logger.fine("Found latest PSU {0}", patchId);
145+
FileResolver psuResolver = new PatchFile(useCache, getInstallerType().toString(), getInstallerVersion(),
146+
patchId, userId, password);
147+
patchLocations.add(psuResolver.resolve(cacheStore));
148+
// Add PSU patch ID to the patchList for validation (conflict check)
149+
patchList.add(patchId);
125150
}
151+
152+
logger.info("IMG-0012");
153+
ValidationResult validationResult = ARUUtil.validatePatches(previousInventory, patchList, userId, password);
154+
if (validationResult.isSuccess()) {
155+
logger.info("IMG-0006");
156+
} else {
157+
String error = validationResult.getErrorMessage();
158+
logger.severe(error);
159+
throw new IllegalArgumentException(error);
160+
}
161+
126162
if (patches != null && !patches.isEmpty()) {
127163
for (String patchId : patches) {
128-
patchLocations.add(new PatchFile(useCache, installerType.toString(), installerVersion,
164+
patchLocations.add(new PatchFile(useCache, getInstallerType().toString(), getInstallerVersion(),
129165
patchId, userId, password).resolve(cacheStore));
130166
}
131167
}
@@ -181,14 +217,16 @@ List<String> getInitialBuildCmd() {
181217
}
182218

183219
public String getTempDirectory() throws IOException {
184-
Path tmpDir = Files.createTempDirectory(Paths.get(Utils.getBuildWorkingDir()), "wlsimgbuilder_temp");
185-
String pathAsString = tmpDir.toAbsolutePath().toString();
186-
logger.info("IMG-0003", pathAsString);
187-
return pathAsString;
220+
if (tempDirectory == null) {
221+
Path tmpDir = Files.createTempDirectory(Paths.get(Utils.getBuildWorkingDir()), "wlsimgbuilder_temp");
222+
tempDirectory = tmpDir.toAbsolutePath().toString();
223+
logger.info("IMG-0003", tempDirectory);
224+
}
225+
return tempDirectory;
188226
}
189227

190-
public Path createPatchesTempDirectory(String tmpDir) throws IOException {
191-
Path tmpPatchesDir = Files.createDirectory(Paths.get(tmpDir, "patches"));
228+
public Path createPatchesTempDirectory() throws IOException {
229+
Path tmpPatchesDir = Files.createDirectory(Paths.get(getTempDirectory(), "patches"));
192230
Files.createFile(Paths.get(tmpPatchesDir.toAbsolutePath().toString(), "dummy.txt"));
193231
return tmpPatchesDir;
194232
}
@@ -242,7 +280,7 @@ private void handleChown() {
242280
* @return list of build argument parameters for docker build
243281
* @throws Exception in case of error
244282
*/
245-
protected List<String> handleInstallerFiles(String tmpDir) throws Exception {
283+
List<String> handleInstallerFiles(String tmpDir) throws Exception {
246284

247285
logger.entering(tmpDir);
248286
List<String> retVal = new LinkedList<>();
@@ -282,31 +320,6 @@ protected List<InstallerFile> gatherRequiredInstallers() throws Exception {
282320
return result;
283321
}
284322

285-
286-
/**
287-
* Certain environment variables need to be set in docker images for WDT domains to work.
288-
*
289-
* @param wdtVariablesPath wdt variables file path.
290-
* @return list of build args
291-
* @throws IOException in case of error
292-
*/
293-
private List<String> getWdtRequiredBuildArgs(Path wdtVariablesPath) throws IOException {
294-
logger.finer("Entering CreateImage.getWdtRequiredBuildArgs: " + wdtVariablesPath.toAbsolutePath().toString());
295-
List<String> retVal = new LinkedList<>();
296-
Properties variableProps = new Properties();
297-
variableProps.load(new FileInputStream(wdtVariablesPath.toFile()));
298-
List<Object> matchingKeys = variableProps.keySet().stream().filter(
299-
x -> variableProps.getProperty(((String) x)) != null
300-
&& Constants.REQD_WDT_BUILD_ARGS.contains(((String) x).toUpperCase())
301-
).collect(Collectors.toList());
302-
matchingKeys.forEach(x -> {
303-
retVal.add(Constants.BUILD_ARG);
304-
retVal.add(((String) x).toUpperCase() + "=" + variableProps.getProperty((String) x));
305-
});
306-
logger.finer("Exiting CreateImage.getWdtRequiredBuildArgs: ");
307-
return retVal;
308-
}
309-
310323
/**
311324
* Checks whether the user requested a domain to be created with WDT.
312325
* If so, returns the required build args to pass to docker and creates required file links to pass
@@ -316,7 +329,7 @@ private List<String> getWdtRequiredBuildArgs(Path wdtVariablesPath) throws IOExc
316329
* @return list of build args
317330
* @throws IOException in case of error
318331
*/
319-
protected List<String> handleWdtArgsIfRequired(String tmpDir) throws IOException {
332+
List<String> handleWdtArgsIfRequired(String tmpDir) throws IOException {
320333
logger.entering(tmpDir);
321334

322335
List<String> retVal = new LinkedList<>();
@@ -340,7 +353,7 @@ protected List<String> handleWdtArgsIfRequired(String tmpDir) throws IOException
340353

341354
dockerfileOptions.setWdtDomainType(wdtDomainType);
342355
if (wdtDomainType != DomainType.WLS) {
343-
if (installerType != WLSInstallerType.FMW) {
356+
if (getInstallerType() != WLSInstallerType.FMW) {
344357
throw new IOException("FMW installer is required for JRF domain");
345358
}
346359
if (runRcu) {
@@ -397,10 +410,6 @@ private void addWdtUrl(String wdtKey) throws Exception {
397410
logger.exiting();
398411
}
399412

400-
WLSInstallerType installerType = WLSInstallerType.WLS;
401-
402-
String installerVersion = Constants.DEFAULT_WLS_VERSION;
403-
404413
@Option(
405414
names = {"--useCache"},
406415
paramLabel = "<Cache Policy>",
@@ -504,6 +513,12 @@ private void addWdtUrl(String wdtKey) throws Exception {
504513
private String[] osUserAndGroup;
505514

506515

516+
@Option(
517+
names = {"--opatchBugNumber"},
518+
description = "the patch number for OPatch (patching OPatch)"
519+
)
520+
String opatchBugNumber = "28186730";
521+
507522
@Option(
508523
names = {"--wdtModel"},
509524
description = "path to the WDT model file that defines the Domain to create"

0 commit comments

Comments
 (0)