From 4a9b46c4b0e765fa22b6f7668d1a689e58b746a7 Mon Sep 17 00:00:00 2001 From: Cheryl King Date: Wed, 10 May 2023 14:59:35 -0500 Subject: [PATCH 1/9] Add new skip install feature flag for dev mode --- .../tools/common/plugins/util/DevUtil.java | 12 ++++++++---- .../tools/common/plugins/util/BaseDevUtilTest.java | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java index bc01c8a8..03602fe4 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java @@ -1,5 +1,5 @@ /** - * (C) Copyright IBM Corporation 2019, 2021. + * (C) Copyright IBM Corporation 2019, 2023. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -82,7 +82,6 @@ import com.sun.nio.file.SensitivityWatchEventModifier; -import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; @@ -437,10 +436,11 @@ private enum FileTrackMode { private Set testArtifactPaths; protected final File generatedFeaturesFile; private File modifiedSrcBuildFile; + private boolean skipInstallFeature; public DevUtil(File buildDirectory, File serverDirectory, File sourceDirectory, File testSourceDirectory, File configDirectory, File projectDirectory, File multiModuleProjectDirectory, List resourceDirs, - boolean hotTests, boolean skipTests, boolean skipUTs, boolean skipITs, String applicationId, + boolean hotTests, boolean skipTests, boolean skipUTs, boolean skipITs, boolean skipInstallFeature, String applicationId, long serverStartTimeout, int appStartupTimeout, int appUpdateTimeout, long compileWaitMillis, boolean libertyDebug, boolean useBuildRecompile, boolean gradle, boolean pollingTest, boolean container, File dockerfile, File dockerBuildContext, String dockerRunOpts, int dockerBuildTimeout, @@ -460,6 +460,7 @@ public DevUtil(File buildDirectory, File serverDirectory, File sourceDirectory, this.skipTests = skipTests; this.skipUTs = skipUTs; this.skipITs = skipITs; + this.skipInstallFeature = skipInstallFeature; this.applicationId = applicationId; this.serverStartTimeout = serverStartTimeout; this.appStartupTimeout = appStartupTimeout; @@ -1849,8 +1850,11 @@ public void restartServer(boolean buildContainer) throws PluginExecutionExceptio libertyCreate(); // Skip installing features on container during restart, since the Dockerfile // should have 'RUN features.sh' - if (!container) { + // Also skip install feature on restart if config parameter specified. + if (!container && !skipInstallFeature) { libertyInstallFeature(); + } else { + info("Skipping liberty:install-feature"); } libertyDeploy(); startServer(buildContainer, false); diff --git a/src/test/java/io/openliberty/tools/common/plugins/util/BaseDevUtilTest.java b/src/test/java/io/openliberty/tools/common/plugins/util/BaseDevUtilTest.java index 18e044cd..7c0b062f 100644 --- a/src/test/java/io/openliberty/tools/common/plugins/util/BaseDevUtilTest.java +++ b/src/test/java/io/openliberty/tools/common/plugins/util/BaseDevUtilTest.java @@ -1,5 +1,5 @@ /** - * (C) Copyright IBM Corporation 2019. + * (C) Copyright IBM Corporation 2019, 2023. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,13 +39,13 @@ public class DevTestUtil extends DevUtil { public DevTestUtil(File serverDirectory, File sourceDirectory, File testSourceDirectory, File configDirectory, List resourceDirs, List webResourceDirs, boolean hotTests, boolean skipTests) throws IOException { super(temp.newFolder(), serverDirectory, sourceDirectory, testSourceDirectory, configDirectory, null, null, - resourceDirs, hotTests, skipTests, false, false, null, 30, 30, 5, 500, true, false, false, false, + resourceDirs, hotTests, skipTests, false, false, false, null, 30, 30, 5, 500, true, false, false, false, false, null, null, null, 0, false, null, false, null, null, false, null, null, null, false, null, null, webResourceDirs); } public DevTestUtil(File serverDirectory, File buildDir) { super(buildDir, serverDirectory, null, null, null, null, null, - null, false, false, false, false, null, 30, 30, 5, 500, true, false, false, false, + null, false, false, false, false, false, null, 30, 30, 5, 500, true, false, false, false, false, null, null, null, 0, false, null, false, null, null, false, null, null, null, false, null, null, null); } From a423bb0326543eee8415d3682204bc023365bbb3 Mon Sep 17 00:00:00 2001 From: Cheryl King Date: Fri, 19 May 2023 14:43:00 -0500 Subject: [PATCH 2/9] logging --- .../java/io/openliberty/tools/common/plugins/util/DevUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java index 094df6af..e1a71d53 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java @@ -436,7 +436,7 @@ private enum FileTrackMode { private Set testArtifactPaths; protected final File generatedFeaturesFile; private File modifiedSrcBuildFile; - private boolean skipInstallFeature; + protected boolean skipInstallFeature; public DevUtil(File buildDirectory, File serverDirectory, File sourceDirectory, File testSourceDirectory, File configDirectory, File projectDirectory, File multiModuleProjectDirectory, List resourceDirs, From 1b1d8c186380f41fa23b070fae211984452355d8 Mon Sep 17 00:00:00 2001 From: Scott Kurz Date: Fri, 11 Aug 2023 15:54:01 -0400 Subject: [PATCH 3/9] Avoid unnecessary war:exploded calls for mere recompilation of Java classes Signed-off-by: Scott Kurz --- .../tools/common/plugins/util/DevUtil.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java index 150841a8..62214adc 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java @@ -358,7 +358,8 @@ private enum FileTrackMode { private File projectDirectory; private File multiModuleProjectDirectory; private List resourceDirs; - private List webResourceDirs; + // Not all webResource dirs need to be monitored, but those for which a Maven filtering will be applied do, since they can't be added to the loose app as source + private List monitoredWebResourceDirs; private boolean hotTests; private Path tempConfigPath; private boolean skipTests; @@ -443,7 +444,7 @@ public DevUtil(File buildDirectory, File serverDirectory, File sourceDirectory, boolean skipDefaultPorts, JavaCompilerOptions compilerOptions, boolean keepTempDockerfile, String mavenCacheLocation, List upstreamProjects, boolean recompileDependencies, String packagingType, File buildFile, Map> parentBuildFiles, boolean generateFeatures, - Set compileArtifactPaths, Set testArtifactPaths, List webResourceDirs) { + Set compileArtifactPaths, Set testArtifactPaths, List monitoredWebResourceDirs) { this.buildDirectory = buildDirectory; this.serverDirectory = serverDirectory; this.sourceDirectory = sourceDirectory; @@ -509,7 +510,7 @@ public DevUtil(File buildDirectory, File serverDirectory, File sourceDirectory, this.generateFeatures = generateFeatures; this.compileArtifactPaths = compileArtifactPaths; this.testArtifactPaths = testArtifactPaths; - this.webResourceDirs = webResourceDirs; + this.monitoredWebResourceDirs = monitoredWebResourceDirs; this.generatedFeaturesFile = new File(configDirectory, BinaryScannerUtil.GENERATED_FEATURES_FILE_PATH); this.generatedFeaturesModified = false; if (this.generateFeatures) { @@ -2835,7 +2836,7 @@ public void watchFiles(File outputDirectory, File testOutputDirectory, final Thr } HashMap webResourceMap = new HashMap(); - for (Path webResourceDir : webResourceDirs) { + for (Path webResourceDir : monitoredWebResourceDirs) { webResourceMap.put(webResourceDir, false); if (Files.exists(webResourceDir)) { registerAll(webResourceDir, executor); @@ -3033,7 +3034,7 @@ public void watchFiles(File outputDirectory, File testOutputDirectory, final Thr } // Check if webResourceDirectory has been added or deleted - for (Path webResourceDir : webResourceDirs) { + for (Path webResourceDir : monitoredWebResourceDirs) { if (!webResourceMap.get(webResourceDir) && Files.exists(webResourceDir)) { updateLooseApp(); registerAll(webResourceDir, executor); @@ -3906,7 +3907,7 @@ private void processFileChanges( // webResource file check Path webResourceParent = null; - for (Path webResourceDir : webResourceDirs) { + for (Path webResourceDir : monitoredWebResourceDirs) { if (directory.startsWith(webResourceDir)) { webResourceParent = webResourceDir; break; @@ -4082,9 +4083,6 @@ private void processFileChanges( } } } - // Update the loose app in case something changed - updateLooseApp(); - } else if (upstreamResourceParent != null && directory.startsWith(upstreamResourceParent.getCanonicalFile().toPath())) { // resources debug("Resource dir: " + upstreamResourceParent.toString()); @@ -4233,8 +4231,6 @@ private void processFileChanges( triggerJavaTestRecompile = true; } } - // Update the loose app in case something changed - updateLooseApp(); runTestThread(true, executor, numApplicationUpdatedMessages, skipUTs, false, buildFile); } } else if (fileChanged.equals(dockerfileUsed) @@ -4998,8 +4994,6 @@ protected boolean recompileJava(Collection javaFilesChanged, Set a // redeploy app after compilation if not loose application if (!isLooseApplication()) { redeployApp(); - } else { - updateLooseApp(); } if (projectName != null) { info(projectName + " source compilation was successful."); From 1c94c9247671af47751a79f4725c70babe8fe082 Mon Sep 17 00:00:00 2001 From: Scott Kurz Date: Mon, 14 Aug 2023 10:01:11 -0400 Subject: [PATCH 4/9] Expose monitored dirs field for LMP/LGP Signed-off-by: Scott Kurz --- .../io/openliberty/tools/common/plugins/util/DevUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java index 62214adc..0e7ad79c 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java @@ -357,9 +357,9 @@ private enum FileTrackMode { private File configDirectory; private File projectDirectory; private File multiModuleProjectDirectory; - private List resourceDirs; + protected List resourceDirs; // Not all webResource dirs need to be monitored, but those for which a Maven filtering will be applied do, since they can't be added to the loose app as source - private List monitoredWebResourceDirs; + protected List monitoredWebResourceDirs; private boolean hotTests; private Path tempConfigPath; private boolean skipTests; From 1a4e80c9240ea6889691656153256ba897701a68 Mon Sep 17 00:00:00 2001 From: Scott Kurz Date: Mon, 14 Aug 2023 17:36:03 -0400 Subject: [PATCH 5/9] Canonicalize path for comparisons Signed-off-by: Scott Kurz --- .../java/io/openliberty/tools/common/plugins/util/DevUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java index 0e7ad79c..82ac7411 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java @@ -3894,7 +3894,7 @@ private void processFileChanges( Path configPath = this.configDirectory.getCanonicalFile().toPath(); Path outputPath = this.outputDirectory.getCanonicalFile().toPath(); - Path directory = fileChanged.getParentFile().toPath(); + Path directory = fileChanged.getParentFile().getCanonicalFile().toPath(); // resource file check File resourceParent = null; From 4e30a48e8068afde30a530de023b73914bc727a4 Mon Sep 17 00:00:00 2001 From: Cheryl King Date: Thu, 17 Aug 2023 11:48:09 -0500 Subject: [PATCH 6/9] Update message --- .../io/openliberty/tools/common/plugins/util/DevUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java index 4b0c6a7a..8bd0fe9a 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java @@ -1849,8 +1849,8 @@ public void restartServer(boolean buildContainer) throws PluginExecutionExceptio // Also skip install feature on restart if config parameter specified. if (!container && !skipInstallFeature) { libertyInstallFeature(); - } else { - info("Skipping liberty:install-feature"); + } else if (skipInstallFeature) { + info("Skipping installation of features due to skipInstallFeature configuration."); } libertyDeploy(); startServer(buildContainer, false); From 992cd49219ccf9c4197db0d2ad461e0220d0a3e3 Mon Sep 17 00:00:00 2001 From: Cheryl King Date: Thu, 31 Aug 2023 15:19:28 -0500 Subject: [PATCH 7/9] Fix devc permissions issues for Docker --- .../tools/common/plugins/util/DevUtil.java | 5 ++++- .../tools/common/plugins/util/OSUtil.java | 12 +++++++++++- .../tools/common/plugins/util/VersionUtility.java | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java index aabd526f..fbee9fd8 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java @@ -1328,9 +1328,12 @@ private void checkDockerBuildTime(long startTime, File dockerBuildContext) { private void startContainer() { try { - if (OSUtil.isLinux()) { + if (OSUtil.isLinux() || OSUtil.isMac()) { // Added Mac since started getting permission errors for logs folder // Allow the server to write to the log files. If we don't create it here docker daemon will create it as root. runCmd("mkdir -p " + serverDirectory + "/logs"); + // Added two hidden folders since started getting permissions errors on the dropins folder. + runCmd("mkdir -p " + buildDirectory + "/" + DEVC_HIDDEN_FOLDER + "/apps"); + runCmd("mkdir -p " + buildDirectory + "/" + DEVC_HIDDEN_FOLDER + "/dropins"); } info("Starting Docker container..."); diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/OSUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/OSUtil.java index a18425be..58199194 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/OSUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/OSUtil.java @@ -1,5 +1,5 @@ /** - * (C) Copyright IBM Corporation 2018, 2020. + * (C) Copyright IBM Corporation 2018, 2023. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,4 +38,14 @@ public static boolean isLinux() { return osName.indexOf("linux") >= 0; } + /** + * Determines if the current OS is a MAC OS. + * + * @return true if running on Mac, false otherwise + */ + public static boolean isMac() { + String osName = System.getProperty("os.name", "unknown").toLowerCase(); + return osName.indexOf("mac") >= 0; + } + } diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/VersionUtility.java b/src/main/java/io/openliberty/tools/common/plugins/util/VersionUtility.java index 45a48e11..9e10460a 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/VersionUtility.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/VersionUtility.java @@ -50,6 +50,7 @@ public static int compareArtifactVersion(String currentVersion, String compareVe Version minVersion = new Version(majorVersion, minorVersion, patchLevel, null, null, null); // check for and strip off any classifier + currentVersion = currentVersion.trim(); // guard against trailing blank space if (currentVersion.contains("-")) { currentVersion = currentVersion.substring(0, currentVersion.indexOf("-")); } From 415d3e0f10762ff8e3752a83294093ade64ac193 Mon Sep 17 00:00:00 2001 From: Cheryl King Date: Fri, 15 Sep 2023 15:58:20 -0500 Subject: [PATCH 8/9] Fix NPE on stream close --- .../common/plugins/util/PrepareFeatureUtil.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/PrepareFeatureUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/PrepareFeatureUtil.java index 8c73ef7b..040ee02f 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/PrepareFeatureUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/PrepareFeatureUtil.java @@ -250,6 +250,7 @@ public File generateJson(String targetJsonFile, Map esaFileMap) th } } catch (PrivilegedActionException e) { + debug(e); throw new PluginExecutionException("Could not load the jar " + installJarFile.getAbsolutePath(), e); } File targetFile = new File(targetJsonFile); @@ -267,10 +268,17 @@ public File generateJson(String targetJsonFile, Map esaFileMap) th debug(e); throw new PluginExecutionException("Cannot read or create json file " + targetJsonFile, e); } finally { - try { - instream.close(); - outstream.close(); - } catch (IOException e) { + if (instream != null) { + try { + instream.close(); + } catch (IOException e) { + } + } + if (outstream != null) { + try { + outstream.close(); + } catch (IOException e) { + } } } } From 79f69e4c165845e678a93c00ffe0293fd62cceae Mon Sep 17 00:00:00 2001 From: Matt Bowersox Date: Tue, 19 Sep 2023 14:58:12 -0500 Subject: [PATCH 9/9] Update container functions to use Podman (#411) * Update container functions to use Podman * Add containerEngine property to devcmetadata.xml and refactor container command logic * Use CONTAINER_TIMEOUT instead of DOCKER_TIMEOUT --- .gitignore | 1 + .../util/AbstractContainerSupportUtil.java | 118 +++- .../tools/common/plugins/util/DevUtil.java | 625 +++++++++--------- .../plugins/util/InstallFeatureUtil.java | 9 +- .../common/plugins/util/VersionUtility.java | 4 +- ...a => DevUtilPrepareContainerfileTest.java} | 158 ++--- 6 files changed, 509 insertions(+), 406 deletions(-) rename src/test/java/io/openliberty/tools/common/plugins/util/{DevUtilPrepareDockerfileTest.java => DevUtilPrepareContainerfileTest.java} (61%) diff --git a/.gitignore b/.gitignore index 24b512a3..2696a35a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .project .settings target +.DS_Store # Compiled class file *.class diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/AbstractContainerSupportUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/AbstractContainerSupportUtil.java index 0071d255..04ae4efb 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/AbstractContainerSupportUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/AbstractContainerSupportUtil.java @@ -1,3 +1,19 @@ +/** + * (C) Copyright IBM Corporation 2020, 2023. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package io.openliberty.tools.common.plugins.util; import java.io.BufferedReader; @@ -7,6 +23,14 @@ public abstract class AbstractContainerSupportUtil { + private static final String CONTAINER_DOCKER_PREFIX = "docker "; + private static final String CONTAINER_PODMAN_PREFIX = "podman "; + + protected static final int CONTAINER_TIMEOUT = 20; // seconds + + private boolean checkedContainerType = false; + protected boolean isDocker = true; + /** * Log debug * @param msg @@ -21,21 +45,77 @@ public abstract class AbstractContainerSupportUtil { */ public abstract void error(String msg, Throwable e); + /** + * Log info + * @param msg + */ + public abstract void info(String msg); + + protected String getContainerCommandPrefix() throws PluginExecutionException { + if (!checkedContainerType) { + checkDockerVersion(); + } + + return isDocker ? CONTAINER_DOCKER_PREFIX : CONTAINER_PODMAN_PREFIX; + } + + /** + * Retrieve the current docker version and compare to a known value. + * The Maven class ComparableVersion allows for numbers, letters and certain words. + * Throw an exception if there is a problem with the version. + */ + private static final String MIN_DOCKER_VERSION = "18.03.0"; // Must use Docker 18.03.0 or higher + protected void checkDockerVersion() throws PluginExecutionException { + String versionCmd = "docker version --format {{.Client.Version}}"; + String dockerVersion = execContainerCmd(versionCmd, CONTAINER_TIMEOUT); + if (dockerVersion == null) { + checkPodmanVersion(); // Check Podman version if no Docker + return; + } + debug("Detected Docker version: " + dockerVersion); + + if (VersionUtility.compareArtifactVersion(dockerVersion, MIN_DOCKER_VERSION, false) < 0) { + checkPodmanVersion(); // Check that bad Docker version isn't just a Podman version + if (!isDocker) { + return; + } + throw new PluginExecutionException("The detected Docker client version number is not supported:" + dockerVersion.trim() + ". Docker version must be " + MIN_DOCKER_VERSION + " or higher."); + } + isDocker = true; + checkedContainerType = true; + } + + private static final String MIN_PODMAN_VERSION = "4.4.4"; // Must use Docker 4.4.4 or higher + private void checkPodmanVersion() throws PluginExecutionException { + String versionCmd = "podman version --format {{.Client.Version}}"; + String podmanVersion = execContainerCmd(versionCmd, CONTAINER_TIMEOUT); + if (podmanVersion == null) { + return; // Can't tell if the version is valid. + } + debug("Detected Podman version: " + podmanVersion); + + if (VersionUtility.compareArtifactVersion(podmanVersion, MIN_PODMAN_VERSION, false) < 0) { + throw new PluginExecutionException("The detected Podman client version number is not supported:" + podmanVersion.trim() + ". Podman version must be " + MIN_PODMAN_VERSION + " or higher."); + } + isDocker = false; + checkedContainerType = true; + } + /** * @param timeout unit is seconds * @return the stdout of the command or null for no output on stdout */ - protected String execDockerCmd(String command, int timeout, boolean throwExceptionOnError) { + protected String execContainerCmd(String command, int timeout, boolean throwExceptionOnError) { String result = null; try { - debug("execDocker, timeout=" + timeout + ", cmd=" + command); + debug("execContainer, timeout=" + timeout + ", cmd=" + command); Process p = Runtime.getRuntime().exec(command); p.waitFor(timeout, TimeUnit.SECONDS); // After waiting for the process, handle the error case and normal termination. if (p.exitValue() != 0) { - debug("Error running docker command, return value="+p.exitValue()); + debug("Error running container command, return value="+p.exitValue()); // read messages from standard err char[] d = new char[1023]; new InputStreamReader(p.getErrorStream()).read(d); @@ -48,22 +128,40 @@ protected String execDockerCmd(String command, int timeout, boolean throwExcepti } result = readStdOut(p); } catch (IllegalThreadStateException e) { - // the timeout was too short and the docker command has not yet completed. There is no exit value. + // the timeout was too short and the container command has not yet completed. There is no exit value. debug("IllegalThreadStateException, message="+e.getMessage()); - error("The docker command did not complete within the timeout period: " + timeout + " seconds.", e); - throw new RuntimeException("The docker command did not complete within the timeout period: " + timeout + " seconds. "); + error("The container command did not complete within the timeout period: " + timeout + " seconds.", e); + throw new RuntimeException("The container command did not complete within the timeout period: " + timeout + " seconds. "); } catch (InterruptedException e) { // If a runtime exception occurred in the server task, log and rethrow - error("An interruption error occurred while running a docker command: " + e.getMessage(), e); + error("An interruption error occurred while running a container command: " + e.getMessage(), e); throw new RuntimeException(e.getMessage()); } catch (IOException e) { - // If a runtime exception occurred in the server task, log and rethrow - error("An error occurred while running a docker command: " + e.getMessage(), e); - throw new RuntimeException(e.getMessage()); + // Logging IOExceptions in info stream. This is thrown if Docker or Podman are not installed on the system. + info("An error occurred while running a container command: " + e.getMessage()); + info("This message will occur when Docker or Podman are not installed."); } return result; } + protected String execContainerCmd(String command, int timeout) { + return execContainerCmd(command, timeout, true); + } + + protected String execContainerCmdWithPrefix(String command, int timeout) { + return execContainerCmdWithPrefix(command, timeout, true); + } + + protected String execContainerCmdWithPrefix(String command, int timeout, boolean throwExceptionOnError) { + try { + String containerCommand = getContainerCommandPrefix() + " " + command; + return execContainerCmd(containerCommand, timeout, throwExceptionOnError); + } catch (PluginExecutionException pe) { + error("Error while determining container command prefix.", pe); + return pe.getMessage(); + } + } + protected String readStdOut(Process p) throws IOException, InterruptedException { String result = null; // Read all the output on stdout and return it to the caller diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java index fbee9fd8..da8c5d8e 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/DevUtil.java @@ -33,6 +33,7 @@ import java.net.ServerSocket; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -80,6 +81,7 @@ import com.sun.nio.file.SensitivityWatchEventModifier; +import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; @@ -117,6 +119,8 @@ public abstract class DevUtil extends AbstractContainerSupportUtil { public static final String DEVMODE_PROJECT_ROOT = "io.openliberty.tools.projectRoot"; private static final String GENERATED_HEADER_REGEX = "# Generated by liberty-.*-plugin"; private static final String DEVMODE_CONTAINER_BASE_NAME = "liberty-dev"; + private static final String DEVC_CONTAINER_DOCKER = "docker"; + private static final String DEVC_CONTAINER_PODMAN = "podman"; private static final String DEVMODE_IMAGE_SUFFIX = "-dev-mode"; public static final String SKIP_BETA_INSTALL_WARNING = "skipBetaInstallFeatureWarning"; public static final String DEVC_HIDDEN_FOLDER = ".libertyDevc"; @@ -134,7 +138,6 @@ public abstract class DevUtil extends AbstractContainerSupportUtil { private static final String[] DEFAULT_COMPILER_OPTIONS = new String[] { "-g", "-parameters" }; private static final int LIBERTY_DEFAULT_HTTP_PORT = 9080; private static final int LIBERTY_DEFAULT_HTTPS_PORT = 9443; - private static final int DOCKER_TIMEOUT = 20; // seconds /** * Log debug @@ -398,22 +401,22 @@ private enum FileTrackMode { private final boolean container; private String imageName; private String containerName; - private File dockerfile; - private File dockerBuildContext; - private Path tempDockerfilePath = null; - private String dockerRunOpts; - private volatile Process dockerRunProcess; - private File defaultDockerfile; - private int dockerBuildTimeout; + private File containerfile; + private File containerBuildContext; + private Path tempContainerfilePath = null; + private String containerRunOpts; + private volatile Process containerRunProcess; + private File defaultContainerfile; + private int containerBuildTimeout; private boolean skipDefaultPorts; - private boolean keepTempDockerfile; + private boolean keepTempContainerfile; protected List srcMount = new ArrayList(); protected List destMount = new ArrayList(); private boolean firstStartup = true; - private Set dockerfileDirectoriesToWatch = new HashSet(); - private Set dockerfileDirectoriesTracked = new HashSet(); - private Set dockerfileDirectoriesWatchKeys = new HashSet(); - private Set dockerfileDirectoriesFileObservers = new HashSet(); + private Set containerfileDirectoriesToWatch = new HashSet(); + private Set containerfileDirectoriesTracked = new HashSet(); + private Set containerfileDirectoriesWatchKeys = new HashSet(); + private Set containerfileDirectoriesFileObservers = new HashSet(); private JavaCompilerOptions compilerOptions; private final String mavenCacheLocation; private AtomicBoolean externalContainerShutdown; @@ -434,6 +437,7 @@ private enum FileTrackMode { private Set testArtifactPaths; protected final File generatedFeaturesFile; private File modifiedSrcBuildFile; + protected boolean skipInstallFeature; public DevUtil(File buildDirectory, File serverDirectory, File sourceDirectory, File testSourceDirectory, @@ -441,8 +445,8 @@ public DevUtil(File buildDirectory, File serverDirectory, File sourceDirectory, boolean hotTests, boolean skipTests, boolean skipUTs, boolean skipITs, boolean skipInstallFeature, String applicationId, long serverStartTimeout, int appStartupTimeout, int appUpdateTimeout, long compileWaitMillis, boolean libertyDebug, boolean useBuildRecompile, boolean gradle, boolean pollingTest, boolean container, - File dockerfile, File dockerBuildContext, String dockerRunOpts, int dockerBuildTimeout, - boolean skipDefaultPorts, JavaCompilerOptions compilerOptions, boolean keepTempDockerfile, + File containerfile, File containerBuildContext, String containerRunOpts, int containerBuildTimeout, + boolean skipDefaultPorts, JavaCompilerOptions compilerOptions, boolean keepTempContainerfile, String mavenCacheLocation, List upstreamProjects, boolean recompileDependencies, String packagingType, File buildFile, Map> parentBuildFiles, boolean generateFeatures, Set compileArtifactPaths, Set testArtifactPaths, List monitoredWebResourceDirs) { @@ -481,20 +485,27 @@ public DevUtil(File buildDirectory, File serverDirectory, File sourceDirectory, this.trackingMode = FileTrackMode.NOT_SET; } this.container = container; - this.dockerfile = dockerfile; - this.dockerBuildContext = dockerBuildContext; - this.dockerRunOpts = dockerRunOpts; + this.containerfile = containerfile; + this.containerBuildContext = containerBuildContext; + this.containerRunOpts = containerRunOpts; if (projectDirectory != null) { - this.defaultDockerfile = new File(projectDirectory, "Dockerfile"); + //Use Containerfile if it exists, but default to Dockerfile if both present or neither exist + File defaultDockerFile = new File(projectDirectory, "Dockerfile"); + File userDefaultContainerFile = new File(projectDirectory, "Containerfile"); + if (!defaultDockerFile.exists() && userDefaultContainerFile.exists()) { + this.defaultContainerfile = userDefaultContainerFile; + } else { + this.defaultContainerfile = defaultDockerFile; + } } - if (dockerBuildTimeout < 1) { - this.dockerBuildTimeout = 600; + if (containerBuildTimeout < 1) { + this.containerBuildTimeout = 600; } else { - this.dockerBuildTimeout = dockerBuildTimeout; + this.containerBuildTimeout = containerBuildTimeout; } this.skipDefaultPorts = skipDefaultPorts; this.compilerOptions = compilerOptions; - this.keepTempDockerfile = keepTempDockerfile; + this.keepTempContainerfile = keepTempContainerfile; this.mavenCacheLocation = mavenCacheLocation; this.upstreamProjects = upstreamProjects; this.recompileDependencies = recompileDependencies; @@ -755,21 +766,21 @@ public void startServer(boolean buildContainer, boolean pullParentImage) throws checkDockerVersion(); } - // build Docker image if in container mode + // build container image if in container mode if (container && buildContainer) { - File dockerfileToUse = getDockerfile(); - debug("Dockerfile to use: " + dockerfileToUse); - if (dockerfileToUse.exists()) { - // The build context comes from the specified dockerBuildContext (or the user's Dockerfile location by default) - File buildContext = dockerBuildContext == null ? dockerfileToUse.getParentFile() : dockerBuildContext; + File containerfileToUse = getContainerfile(); + debug("Containerfile to use: " + containerfileToUse); + if (containerfileToUse.exists()) { + // The build context comes from the specified containerBuildContext (or the user's Containerfile location by default) + File buildContext = containerBuildContext == null ? containerfileToUse.getParentFile() : containerBuildContext; String buildContextString = buildContext.getAbsolutePath(); - debug("Docker build context: " + buildContextString); + debug("Container build context: " + buildContextString); - File tempDockerfile = prepareTempDockerfile(dockerfileToUse, buildContextString); - buildDockerImage(tempDockerfile, dockerfileToUse, pullParentImage, buildContext); + File tempContainerfile = prepareTempContainerfile(containerfileToUse, buildContextString); + buildContainerImage(tempContainerfile, containerfileToUse, pullParentImage, buildContext); } else { - // this message is mainly for the default dockerfile scenario, since the dockerfile parameter was already validated in Maven/Gradle plugin. - throw new PluginExecutionException("No Dockerfile was found at " + dockerfileToUse.getAbsolutePath() + ". Create a Dockerfile at the specified location to use dev mode with container support. For an example of how to configure a Dockerfile, see https://github.com/OpenLiberty/ci.docker"); + // this message is mainly for the default containerfile scenario, since the containerfile parameter was already validated in Maven/Gradle plugin. + throw new PluginExecutionException("No Containerfile or Dockerfile was found at " + containerfileToUse.getAbsolutePath() + ". Create a Containerfile/Dockerfile at the specified location to use dev mode with container support. For an example of how to configure a Dockerfile, see https://github.com/OpenLiberty/ci.docker"); } } @@ -803,6 +814,9 @@ public void run() { error(e2.getMessage()); serverThreadException = e2; } + } catch (PluginExecutionException pe) { + error(pe.getMessage()); + serverThreadException = pe; } } }); @@ -926,49 +940,31 @@ public void onFileChange(File file) { } } - /** - * Retrieve the current docker version and compare to a known value. - * The Maven class ComparableVersion allows for numbers, letters and certain words. - * Throw an exception if there is a problem with the version. - */ - private static final String MIN_DOCKER_VERSION = "18.03.0"; // Must use Docker 18.03.0 or higher - private void checkDockerVersion() throws PluginExecutionException { - String versionCmd = "docker version --format {{.Client.Version}}"; - String dockerVersion = execDockerCmd(versionCmd, DOCKER_TIMEOUT); - if (dockerVersion == null) { - return; // can't tell if the version is valid. - } - debug("Detected Docker version >" + dockerVersion); - - if (VersionUtility.compareArtifactVersion(dockerVersion, MIN_DOCKER_VERSION, false) < 0) { - throw new PluginExecutionException("The detected Docker client version number is not supported:" + dockerVersion.trim() + ". Docker version must be 18.03.0 or higher."); - } - } - private File getDockerfile() { - return dockerfile != null ? dockerfile : defaultDockerfile; + private File getContainerfile() { + return containerfile != null ? containerfile : defaultContainerfile; } - protected List readDockerfile(File dockerfile) throws PluginExecutionException { - // Convert Dockerfile to List of strings for each line - List dockerfileLines = null; + protected List readContainerfile(File containerfile) throws PluginExecutionException { + // Convert Containerfile to List of strings for each line + List containerfileLines = null; try { - dockerfileLines = Files.readAllLines(dockerfile.toPath()); + containerfileLines = Files.readAllLines(containerfile.toPath()); } catch (IOException e) { - error("Failed to read Dockerfile located at " + dockerfile); - throw new PluginExecutionException("Could not read Dockerfile " + dockerfile + ": " + e.getMessage(), e); + error("Failed to read Containerfile located at " + containerfile); + throw new PluginExecutionException("Could not read Containerfile " + containerfile + ": " + e.getMessage(), e); } - return dockerfileLines; + return containerfileLines; } /** - * Get escape character from the escape directive at the top of the Dockerfile. + * Get escape character from the escape directive at the top of the Containerfile. * Docker documents a couple of directives, but it seems escape must always be the first line to work. */ - protected static char getEscapeCharacter(List dockerfileLines) throws PluginExecutionException { - if (dockerfileLines.size() > 0) { + protected static char getEscapeCharacter(List containerfileLines) throws PluginExecutionException { + if (containerfileLines.size() > 0) { // Remove white space from the beginning and end of the line - String pendingLine = dockerfileLines.get(0).trim(); + String pendingLine = containerfileLines.get(0).trim(); int directiveSymbolIndex = pendingLine.indexOf("#"); if (directiveSymbolIndex >= 0) { String contentAfterSymbol = pendingLine.substring(directiveSymbolIndex + 1, pendingLine.length()); @@ -979,7 +975,7 @@ protected static char getEscapeCharacter(List dockerfileLines) throws Pl String escapeChar = split[1].trim(); if (escapeChar.length() > 0) { // Get the first char, and don't validate here whether it's a valid escape char - // but just let Docker fail the build if it determines that it is invalid. + // but just let Docker/Podman fail the build if it determines that it is invalid. return escapeChar.charAt(0); } } @@ -992,10 +988,10 @@ protected static char getEscapeCharacter(List dockerfileLines) throws Pl * Trim all lines and get them without comments or empty lines after the first FROM command * (so that directives at the beginning of the file are preserved) */ - protected static List getCleanedLines(List dockerfileLines) throws PluginExecutionException { + protected static List getCleanedLines(List containerfileLines) throws PluginExecutionException { List result = new ArrayList(); boolean fromFound = false; - for (String line : dockerfileLines) { + for (String line : containerfileLines) { // Remove white space from the beginning and end of the line String pendingLine = line.trim(); if (!fromFound) { @@ -1023,17 +1019,17 @@ protected static List getCleanedLines(List dockerfileLines) thro /** * Combine multi-line commands into single lines. Requires that getCleanedLines() be called first. */ - protected static List getCombinedLines(List dockerfileLines, char escape) throws PluginExecutionException { + protected static List getCombinedLines(List containerfileLines, char escape) throws PluginExecutionException { List result = new ArrayList(); int i = 0; - while (i < dockerfileLines.size()) { - String pendingLine = dockerfileLines.get(i).trim(); + while (i < containerfileLines.size()) { + String pendingLine = containerfileLines.get(i).trim(); int multilineIndex; int j = i+1; - while (pendingLine.length() > 0 && !pendingLine.startsWith("#") && (pendingLine.charAt(pendingLine.length() - 1) == escape) && j < dockerfileLines.size()) { + while (pendingLine.length() > 0 && !pendingLine.startsWith("#") && (pendingLine.charAt(pendingLine.length() - 1) == escape) && j < containerfileLines.size()) { multilineIndex = pendingLine.length() - 1; String contentBeforeSymbol = pendingLine.substring(0, multilineIndex); - String nextLine = dockerfileLines.get(j); + String nextLine = containerfileLines.get(j); String combined = contentBeforeSymbol + nextLine; pendingLine = combined.trim(); // trim the combined string to remove whitespace around any further line escapes j++; @@ -1044,27 +1040,27 @@ protected static List getCombinedLines(List dockerfileLines, cha return result; } - protected void removeWarFileLines(List dockerfileLines) throws PluginExecutionException { - removeFileExtensionLines(dockerfileLines, ".war"); + protected void removeWarFileLines(List containerfileLines) throws PluginExecutionException { + removeFileExtensionLines(containerfileLines, ".war"); } - protected void removeEarFileLines(List dockerfileLines) throws PluginExecutionException { - removeFileExtensionLines(dockerfileLines, ".ear"); + protected void removeEarFileLines(List containerfileLines) throws PluginExecutionException { + removeFileExtensionLines(containerfileLines, ".ear"); } - private void removeFileExtensionLines(List dockerfileLines, String extension) throws PluginExecutionException { + private void removeFileExtensionLines(List containerfileLines, String extension) throws PluginExecutionException { List fileExtensionLines = new ArrayList(); - for (String line : dockerfileLines) { + for (String line : containerfileLines) { // Remove white space from the beginning and end of the line String trimLine = line.trim(); if (!trimLine.startsWith("#") && trimLine.toLowerCase().contains(extension)) { - // Break the Dockerfile line down into segments based on any amount of whitespace. + // Break the Containerfile line down into segments based on any amount of whitespace. // The command must be to the left of any comments. String[] cmdSegments = trimLine.split("#")[0].split("\\s+"); // if the line starts with COPY and the second to last segment ends with extension, it is a COPY line of that file type if (cmdSegments[0].equalsIgnoreCase("COPY") || cmdSegments[0].equalsIgnoreCase("ADD")) { if (cmdSegments.length < 3) { - throw new PluginExecutionException("Incorrect syntax on this line in the Dockerfile: '" + line + + throw new PluginExecutionException("Incorrect syntax on this line in the Containerfile: '" + line + "'. There must be at least two arguments for the COPY or ADD command, a source path and a destination path."); } if (cmdSegments[cmdSegments.length - 2].toLowerCase().endsWith(extension)) { @@ -1074,7 +1070,7 @@ private void removeFileExtensionLines(List dockerfileLines, String exten } } debug(extension + " file lines: " + fileExtensionLines.toString()); - dockerfileLines.removeAll(fileExtensionLines); + containerfileLines.removeAll(fileExtensionLines); } /** @@ -1083,26 +1079,26 @@ private void removeFileExtensionLines(List dockerfileLines, String exten *
* Note: lines must have been trimmed and cleaned of comments using getCleanedLines() before calling this. */ - protected void disableOpenJ9SCC(List dockerfileLines) { + protected void disableOpenJ9SCC(List containerfileLines) { final String RUN_CONFIGURE_COMMAND_LOWERCASE = "run configure.sh"; - for (int i=0; i dockerfileLines) { + protected void detectFeaturesSh(List containerfileLines) { // Reset features.sh warning flag shownFeaturesShWarning.set(false); final String FEATURES_SH_COMMAND_LOWERCASE = "run features.sh"; - for (int i=0; i dockerfileLines) { return; } } - // if not detected, reset to false in case the Dockerfile is being rebuilt + // if not detected, reset to false in case the Containerfile is being rebuilt debug("Did not find RUN features.sh command."); hasFeaturesSh.set(false); } - protected void processCopyLines(List dockerfileLines, String buildContext) throws PluginExecutionException { + protected void processCopyLines(List containerfileLines, String buildContext) throws PluginExecutionException { srcMount.clear(); destMount.clear(); - for (String line : dockerfileLines) { + for (String line : containerfileLines) { // Remove white space from the beginning and end of the line String trimLine = line.trim(); if (!trimLine.startsWith("#")) { - // Break the Dockerfile line down into segments based on any amount of whitespace. + // Break the Containerfile line down into segments based on any amount of whitespace. // The command must be to the left of any comments. String[] cmdSegments = trimLine.split("#")[0].split("\\s+"); // If the line starts with COPY or ADD if (cmdSegments[0].equalsIgnoreCase("COPY") || cmdSegments[0].equalsIgnoreCase("ADD")) { if (cmdSegments.length < 3) { // preliminary check but some of these segments could be options - throw new PluginExecutionException("Incorrect syntax on this line in the Dockerfile: '" + line + + throw new PluginExecutionException("Incorrect syntax on this line in the Containerfile: '" + line + "'. There must be at least two arguments for the COPY or ADD command, a source path and a destination path."); } if (line.contains("$")) { - warn("The Dockerfile line '" + line + "' will not be able to be hot deployed to the dev mode container. Dev mode does not currently support environment variables in COPY or ADD commands. If you make changes to files specified by this line, type 'r' and press Enter to rebuild the Docker image and restart the container."); + warn("The Containerfile line '" + line + "' will not be able to be hot deployed to the dev mode container. Dev mode does not currently support environment variables in COPY or ADD commands. If you make changes to files specified by this line, type 'r' and press Enter to rebuild the container image and restart the container."); continue; } List srcOrDestArguments = new ArrayList(); @@ -1141,7 +1137,7 @@ protected void processCopyLines(List dockerfileLines, String buildContex String segment = cmdSegments[i]; if (segment.startsWith("--from")) { // multi-stage build, COPY only (not ADD), give a warning - warn("The Dockerfile line '" + line + "' will not be able to be hot deployed to the dev mode container. Dev mode does not currently support hot deployment with multi-stage COPY commands."); + warn("The Containerfile line '" + line + "' will not be able to be hot deployed to the dev mode container. Dev mode does not currently support hot deployment with multi-stage COPY commands."); skipLine = true; // don't mount the dirs in this COPY command break; } else if (segment.startsWith("--")) { @@ -1154,7 +1150,7 @@ protected void processCopyLines(List dockerfileLines, String buildContex continue; } if (srcOrDestArguments.size() < 2) { // proper check for number of src and dest args - throw new PluginExecutionException("Incorrect syntax on this line in the Dockerfile: '" + line + + throw new PluginExecutionException("Incorrect syntax on this line in the Containerfile: '" + line + "'. There must be at least two arguments for the COPY or ADD command, a source path and a destination path."); } // dest is the last argument @@ -1168,19 +1164,19 @@ protected void processCopyLines(List dockerfileLines, String buildContex String sourcePath = buildContext + "/" + src; File sourceFile = new File(sourcePath); if (src.contains("*") || src.contains("?")) { - warn("The COPY or ADD source " + src + " in the Dockerfile line '" + line + "' will not be able to be hot deployed to the dev mode container. Dev mode does not currently support wildcards in the COPY or ADD commands. If you make changes to files specified by this line, type 'r' and press Enter to rebuild the Docker image and restart the container."); + warn("The COPY or ADD source " + src + " in the Containerfile line '" + line + "' will not be able to be hot deployed to the dev mode container. Dev mode does not currently support wildcards in the COPY or ADD commands. If you make changes to files specified by this line, type 'r' and press Enter to rebuild the container image and restart the container."); } else if (sourceFile.isDirectory() || cmdSegments[0].equalsIgnoreCase("ADD")) { - synchronized(dockerfileDirectoriesToWatch) { + synchronized(containerfileDirectoriesToWatch) { try { - dockerfileDirectoriesToWatch.add(sourceFile.getCanonicalFile().toPath()); - debug("COPY/ADD line=" + line + ", src=" + sourcePath + ", added to dockerfileDirectoriesToWatch: " + sourceFile); + containerfileDirectoriesToWatch.add(sourceFile.getCanonicalFile().toPath()); + debug("COPY/ADD line=" + line + ", src=" + sourcePath + ", added to containerfileDirectoriesToWatch: " + sourceFile); } catch (IOException e) { - // Do not fail here. Let the Docker build fail instead. - error("Could not resolve the canonical path of the directory specified in the Dockerfile: " + sourcePath, e); + // Do not fail here. Let the Docker/Podman build fail instead. + error("Could not resolve the canonical path of the directory specified in the Containerfile: " + sourcePath, e); } } } else { - // No need to validate existence of the file, just let the Docker build fail + // No need to validate existence of the file, just let the Docker/Podman build fail String destMountString = formatDestMount(dest, sourceFile); srcMount.add(sourcePath); destMount.add(destMountString); @@ -1210,7 +1206,7 @@ private String formatDestMount(String destMountString, File srcMountFile) { } /** - * Check the name used in Dockerfile for URL e.g. ADD https://repo.maven.apache.org/maven2/postgres9.jar /lib/ + * Check the name used in Containerfile for URL e.g. ADD https://repo.maven.apache.org/maven2/postgres9.jar /lib/ * @param name the source name in a copy or add command * @return true if the name is a URL */ @@ -1223,46 +1219,46 @@ private boolean isURL(String name) { return true; } - protected File prepareTempDockerfile(File dockerfile, String buildContextString) throws PluginExecutionException { - // Create a temp Dockerfile to build image from + protected File prepareTempContainerfile(File containerfile, String buildContextString) throws PluginExecutionException { + // Create a temp Containerfile to build image from - List dockerfileLines = readDockerfile(dockerfile); - char escape = getEscapeCharacter(dockerfileLines); - dockerfileLines = getCleanedLines(dockerfileLines); - dockerfileLines = getCombinedLines(dockerfileLines, escape); - removeWarFileLines(dockerfileLines); - removeEarFileLines(dockerfileLines); - processCopyLines(dockerfileLines, buildContextString); - detectFeaturesSh(dockerfileLines); - disableOpenJ9SCC(dockerfileLines); - for (String line : dockerfileLines) { + List containerfileLines = readContainerfile(containerfile); + char escape = getEscapeCharacter(containerfileLines); + containerfileLines = getCleanedLines(containerfileLines); + containerfileLines = getCombinedLines(containerfileLines, escape); + removeWarFileLines(containerfileLines); + removeEarFileLines(containerfileLines); + processCopyLines(containerfileLines, buildContextString); + detectFeaturesSh(containerfileLines); + disableOpenJ9SCC(containerfileLines); + for (String line : containerfileLines) { debug(line); } - File tempDockerfile = null; + File tempContainerfile = null; try { - debug("Creating temp Dockerfile..."); + debug("Creating temp Containerfile..."); File devcHiddenFolder = new File(buildDirectory, DEVC_HIDDEN_FOLDER); devcHiddenFolder.mkdirs(); - tempDockerfile = File.createTempFile("tempDockerfile", "", devcHiddenFolder); - debug("temp Dockerfile: " + tempDockerfile); - tempDockerfilePath = tempDockerfile.toPath(); // save name to clean up later - if (keepTempDockerfile) { - info("Keeping temporary Dockerfile: "+tempDockerfilePath); + tempContainerfile = File.createTempFile("tempContainerfile", "", devcHiddenFolder); + debug("temp Containerfile: " + tempContainerfile); + tempContainerfilePath = tempContainerfile.toPath(); // save name to clean up later + if (keepTempContainerfile) { + info("Keeping temporary Containerfile: "+tempContainerfilePath); } else { - // set the tempDockerfile to be deleted when the JVM exits - tempDockerfile.deleteOnExit(); + // set the tempContainerfile to be deleted when the JVM exits + tempContainerfile.deleteOnExit(); } - Files.write(tempDockerfile.toPath(), dockerfileLines, StandardCharsets.UTF_8); + Files.write(tempContainerfile.toPath(), containerfileLines, StandardCharsets.UTF_8); } catch (IOException e) { - error("Failed to create temp Dockerfile"); - throw new PluginExecutionException("Could not create temp Dockerfile: " + e.getMessage(), e); + error("Failed to create temp Containerfile"); + throw new PluginExecutionException("Could not create temp Containerfile: " + e.getMessage(), e); } - return tempDockerfile; + return tempContainerfile; } - private void buildDockerImage(File tempDockerfile, File userDockerfile, boolean pullParentImage, File buildContext) throws PluginExecutionException { - info("Building Docker image..."); + private void buildContainerImage(File tempContainerfile, File userContainerfile, boolean pullParentImage, File buildContext) throws PluginExecutionException { + info("Building container image..."); try { imageName = getProjectName() + DEVMODE_IMAGE_SUFFIX; @@ -1270,63 +1266,65 @@ private void buildDockerImage(File tempDockerfile, File userDockerfile, boolean imageName = imageName.replaceAll("[^a-zA-Z0-9]", "-").replaceAll("^[\\-]+", "").toLowerCase(); StringBuilder sb = new StringBuilder(); - sb.append("docker build "); + sb.append(getContainerCommandPrefix()); + sb.append(" build "); if (pullParentImage) { sb.append("--pull "); } - sb.append("-f " + tempDockerfile + " -t " + imageName + " " + buildContext.getAbsolutePath()); + sb.append("-f " + tempContainerfile + " -t " + imageName + " " + buildContext.getAbsolutePath()); String buildCmd = sb.toString(); info(buildCmd); if (hasFeaturesSh.get()) { - info("The RUN features.sh command is detected in the Dockerfile and extra time may be necessary when installing features."); + info("The RUN features.sh command is detected in the Containerfile and extra time may be necessary when installing features."); } long startTime = System.currentTimeMillis(); - execDockerCmdAndLog(getRunProcess(buildCmd), dockerBuildTimeout); - checkDockerBuildTime(startTime, buildContext); - info("Completed building Docker image."); + execContainerCmdAndLog(getRunProcess(buildCmd), containerBuildTimeout); + checkContainerBuildTime(startTime, buildContext); + info("Completed building container image."); } catch (IllegalThreadStateException e) { - // the timeout was too short and the docker command has not yet completed. + // the timeout was too short and the container command has not yet completed. debug("IllegalThreadStateException, message="+e.getMessage()); - throw new PluginExecutionException("The docker build command did not complete within the timeout period: " + dockerBuildTimeout + " seconds. " + - "Use the dockerBuildTimeout option to specify a longer period or " + - "add files not needed in the container to the .dockerignore file", e); + throw new PluginExecutionException("The container build command did not complete within the timeout period: " + containerBuildTimeout + " seconds. " + + "Use the containerBuildTimeout option to specify a longer period or " + + "add files not needed in the container to the .containerignore file", e); } catch (IOException e) { - error("Input or output error building Docker image: " + e.getMessage()); + error("Input or output error building container image: " + e.getMessage()); throw new RuntimeException(e); } catch (InterruptedException e) { - debug("Thread InterruptedException while building the Docker image: " + e.getMessage()); - throw new PluginExecutionException("Could not build Docker image using Dockerfile: " + - userDockerfile.getAbsolutePath() + ". Address the following docker build error and then start dev mode again: " + e.getMessage(), e); + debug("Thread InterruptedException while building the container image: " + e.getMessage()); + throw new PluginExecutionException("Could not build container image using Containerfile: " + + userContainerfile.getAbsolutePath() + ". Address the following container build error and then start dev mode again: " + e.getMessage(), e); } catch (RuntimeException r) { - debug("RuntimeException building Docker image: " + r.getMessage()); - throw new PluginExecutionException("Could not build Docker image using Dockerfile: " + - userDockerfile.getAbsolutePath() + ". Address the following docker build error and then start dev mode again: " + r.getMessage(), r); + debug("RuntimeException building container image: " + r.getMessage()); + throw new PluginExecutionException("Could not build container image using Containerfile: " + + userContainerfile.getAbsolutePath() + ". Address the following container build error and then start dev mode again: " + r.getMessage(), r); } } - // Suggest a performance improvement if docker build takes too long. + // Suggest a performance improvement if container build takes too long. private static final long DOCKER_BUILD_SOFT_TIMEOUT = 30000; // millis - private void checkDockerBuildTime(long startTime, File dockerBuildContext) { + private void checkContainerBuildTime(long startTime, File containerBuildContext) { if (System.currentTimeMillis() - startTime < DOCKER_BUILD_SOFT_TIMEOUT) { return; } - debug("checkDockerBuildTime, dockerBuildContext=" + dockerBuildContext.getAbsolutePath()); - File dockerIgnore = new File(dockerBuildContext, ".dockerignore"); + debug("checkContainerBuildTime, containerBuildContext=" + containerBuildContext.getAbsolutePath()); + //TODO: ADD DOCKERIGNORE VS CONTAINERIGNORE CHECK + File dockerIgnore = new File(containerBuildContext, ".dockerignore"); if (!dockerIgnore.exists()) { // provide some advice String buildContextPath; try { - buildContextPath = dockerBuildContext.getCanonicalPath(); + buildContextPath = containerBuildContext.getCanonicalPath(); } catch (IOException e) { - buildContextPath = dockerBuildContext.getAbsolutePath(); + buildContextPath = containerBuildContext.getAbsolutePath(); } - warn("The docker build command took longer than " + DOCKER_BUILD_SOFT_TIMEOUT / 1000 + " seconds. " + + warn("The container build command took longer than " + DOCKER_BUILD_SOFT_TIMEOUT / 1000 + " seconds. " + "You may increase performance by adding unneeded files and directories " + "such as any Liberty runtime directories to a .dockerignore file in " + buildContextPath + "."); } } - private void startContainer() { + private void startContainer() throws PluginExecutionException { try { if (OSUtil.isLinux() || OSUtil.isMac()) { // Added Mac since started getting permission errors for logs folder // Allow the server to write to the log files. If we don't create it here docker daemon will create it as root. @@ -1336,11 +1334,11 @@ private void startContainer() { runCmd("mkdir -p " + buildDirectory + "/" + DEVC_HIDDEN_FOLDER + "/dropins"); } - info("Starting Docker container..."); + info("Starting container..."); String startContainerCommand = getContainerCommand(); info(startContainerCommand); - dockerRunProcess = getRunProcess(startContainerCommand); - execDockerCmdAndLog(dockerRunProcess, 0); + containerRunProcess = getRunProcess(startContainerCommand); + execContainerCmdAndLog(containerRunProcess, 0); } catch (IOException e) { error("Error starting container: " + e.getMessage()); throw new RuntimeException(e); @@ -1348,12 +1346,12 @@ private void startContainer() { error("Thread was interrupted while starting the container: " + e.getMessage()); } catch (RuntimeException r) { try { - // remove container in case of an error trying to run the container because the docker run --rm will not rm the container - String dockerRmCmd = "docker container rm " + containerName; - execDockerCmd(dockerRmCmd, DOCKER_TIMEOUT); + // remove container in case of an error trying to run the container because the container run --rm will not rm the container + String containerRmCmd = "container rm " + containerName; + execContainerCmdWithPrefix(containerRmCmd, CONTAINER_TIMEOUT); } catch (Exception e) { - // do not report the "docker container rm" error so that we can instead report the startContainer() error - debug("Exception running docker container rm:", e); + // do not report the "container rm" error so that we can instead report the startContainer() error + debug("Exception running container command rm:", e); } throw r; } @@ -1376,7 +1374,7 @@ private Process getRunProcess(String command) throws IOException { return processBuilder.start(); } - private void execDockerCmdAndLog(final Process startingProcess, int timeout) throws InterruptedException { + private void execContainerCmdAndLog(final Process startingProcess, int timeout) throws InterruptedException { Thread logCopyInputThread = new Thread(new Runnable() { @Override public void run() { @@ -1406,7 +1404,7 @@ public void run() { setDevStop(true); // indicate intentional shutdown externalContainerShutdown.set(true); // container shut down by external command } - debug("Unexpected exit running docker command, return value=" + startingProcess.exitValue()); + debug("Unexpected exit running container command, return value=" + startingProcess.exitValue()); // show first message from standard err String errorMessage = new String(firstErrorLine).trim() + " RC=" + startingProcess.exitValue(); writeDevcMetadata(false); @@ -1449,14 +1447,14 @@ private String copyStreamToBuildLog(InputStream stream, boolean info) { // Look for JVM version error in the line alertOnServerError(line, "JVMCFRE003", - "Java classes were compiled with a higher version of Java than the JVM in the container. To resolve this issue, set the source and target Java versions in your Gradle build to correspond to the Java version used in your Dockerfile or its parent image, then restart dev mode.", + "Java classes were compiled with a higher version of Java than the JVM in the container. To resolve this issue, set the source and target Java versions in your Gradle build to correspond to the Java version used in your Containerfile/Dockerfile or its parent image, then restart dev mode.", // Maven project should be cleaned before restarting dev mode, otherwise compile does not realize Java version settings have changed - "Java classes were compiled with a higher version of Java than the JVM in the container. To resolve this issue, set the source and target Java versions in your Maven build to correspond to the Java version used in your Dockerfile or its parent image, then clean the project output and restart dev mode.", + "Java classes were compiled with a higher version of Java than the JVM in the container. To resolve this issue, set the source and target Java versions in your Maven build to correspond to the Java version used in your Containerfile/Dockerfile or its parent image, then clean the project output and restart dev mode.", false); - // Look for features not available message during server startup if features.sh was not defined in Dockerfile + // Look for features not available message during server startup if features.sh was not defined in Containerfile if (!serverFullyStarted.get() && !hasFeaturesSh.get() && !shownFeaturesShWarning.get()) { - String errMsg = "Feature definitions were not found in the container. To install features to the container, specify 'RUN features.sh' in your Dockerfile. For an example of how to configure a Dockerfile, see https://github.com/OpenLiberty/ci.docker"; + String errMsg = "Feature definitions were not found in the container. To install features to the container, specify 'RUN features.sh' in your Containerfile/Dockerfile. For an example of how to configure a Containerfile/Dockerfile, see https://github.com/OpenLiberty/ci.docker"; shownFeaturesShWarning.set(alertOnServerError(line, "CWWKF0001E", errMsg, errMsg, true)); } } @@ -1503,30 +1501,26 @@ private String[] getCommandTokens(String command) { return commandTokens; } - private void stopContainer() { + private void stopContainer() throws PluginExecutionException { try { serverFullyStarted.set(false); - // see if docker run command (container) is still running before trying to stop it. - if (dockerRunProcess != null && dockerRunProcess.isAlive()) { + // see if container run command (container) is still running before trying to stop it. + if (containerRunProcess != null && containerRunProcess.isAlive()) { info("Stopping container..."); - String dockerStopCmd = "docker stop " + containerName; + String containerStopCmd = "stop " + containerName; debug("Stopping container " + containerName); - execDockerCmd(dockerStopCmd, DOCKER_TIMEOUT + 20); // allow extra time for server shutdown + execContainerCmdWithPrefix(containerStopCmd, CONTAINER_TIMEOUT + 20); // allow extra time for server shutdown writeDevcMetadata(false); } } catch (RuntimeException r) { error("Error stopping container: " + r.getMessage()); throw r; } finally { - dockerRunProcess = null; + containerRunProcess = null; } } - private String execDockerCmd(String command, int timeout) { - return execDockerCmd(command, timeout, true); - } - /** * Get the root directory for mounting loose app in container. This is the longest common directory between the projectDirectory and multiModuleProjectDirectory. * @@ -1559,13 +1553,13 @@ protected static File getLongestCommonDir(File dir1, File dir2) { } /** - * Build a docker run command with all the ports and directories required to run Open Liberty + * Build a container run command with all the ports and directories required to run Open Liberty * inside a container. Also included is the image name and the server run command to override * the CMD attribute of the Open Liberty docker image. * @return the command string to use to start the container */ - private String getContainerCommand() { - StringBuilder command = new StringBuilder("docker run --rm"); + private String getContainerCommand() throws IOException, PluginExecutionException { + StringBuilder command = new StringBuilder(getContainerCommandPrefix() + "run --rm"); if (!skipDefaultPorts) { int httpPortToUse, httpsPortToUse; try { @@ -1602,31 +1596,34 @@ private String getContainerCommand() { } // mount potential directories containing .war.xml from devc specific folder - override /config/apps and /config/dropins - command.append(" -v " + buildDirectory + "/" + DEVC_HIDDEN_FOLDER + "/apps:/config/apps"); - command.append(" -v " + buildDirectory + "/" + DEVC_HIDDEN_FOLDER + "/dropins:/config/dropins"); + File tempApps = new File(buildDirectory, DEVC_HIDDEN_FOLDER + "/apps"); + File tempDropins = new File(buildDirectory, DEVC_HIDDEN_FOLDER + "/dropins"); + command.append(" -v " + tempApps + ":/config/apps"); + command.append(" -v " + tempDropins + ":/config/dropins"); // mount the loose application resources in the container using the appropriate project root File looseApplicationProjectRoot = getLooseAppProjectRoot(projectDirectory, multiModuleProjectDirectory); command.append(" -v " + looseApplicationProjectRoot.getAbsolutePath() + ":" + DEVMODE_DIR_NAME); // mount the server logs directory over the /logs used by the open liberty container as defined by the LOG_DIR env. var. - command.append(" -v " + serverDirectory.getAbsolutePath() + "/logs:/logs"); + File logsDir = new File(serverDirectory.getAbsolutePath(), "logs"); + command.append(" -v " + logsDir + ":/logs"); // mount the Maven .m2 cache directory for featureUtility to use. For now, featureUtility does not support Gradle cache. command.append(" -v " + mavenCacheLocation + ":/devmode-maven-cache"); - // mount all files from COPY commands in the Dockerfile to allow for hot deployment + // mount all files from COPY commands in the Containerfile to allow for hot deployment command.append(getCopiedFiles()); // Add a --user option when running Linux command.append(getUserId()); // Do not generate a name if the user has specified a name - String name = getDockerOption("--name"); + String name = getContainerOption("--name"); if (name == null || name.isEmpty()) { if (name != null && name.isEmpty()) { - error("The Docker option --name is specified with an unsupported value: empty string."); - // now generate a name so that the Docker errors make some sense to the user. + error("The container option --name is specified with an unsupported value: empty string."); + // now generate a name so that the container errors make some sense to the user. } containerName = generateNewContainerName(); command.append(" --name " + containerName); @@ -1636,8 +1633,8 @@ private String getContainerCommand() { debug("containerName: " + containerName + "."); // Allow the user to add their own options to this command via a system property. - if (dockerRunOpts != null) { - command.append(" "+dockerRunOpts); + if (containerRunOpts != null) { + command.append(" "+containerRunOpts); } // Options must precede this in any order. Image name and command code follows. @@ -1652,17 +1649,17 @@ private String getContainerCommand() { } /** - * Obtain a given Docker run option from the dockerRunOpts parameter - * @param optionName the name of the option to extract from the dockerRunOpts + * Obtain a given container run option from the containerRunOpts parameter + * @param optionName the name of the option to extract from the containerRunOpts * @return a string representation of the value of the option or null * * The option of interest must not use a quoted string. */ - private String getDockerOption(String optionName) { - if (dockerRunOpts == null || dockerRunOpts.isEmpty()) { + private String getContainerOption(String optionName) { + if (containerRunOpts == null || containerRunOpts.isEmpty()) { return null; } - String[] options = dockerRunOpts.split("\\s+"); // split on whitespace + String[] options = containerRunOpts.split("\\s+"); // split on whitespace for (int i = 0; i < options.length; i++) { if (options[i].equals(optionName)) { // --name ABC format return (i < options.length - 1) ? options[i+1] : null; @@ -1673,10 +1670,10 @@ private String getDockerOption(String optionName) { return null; } - private String generateNewContainerName() { - String dockerContNamesCmd = "docker ps -a --format \"{{.Names}}\""; - debug("docker container names list command: " + dockerContNamesCmd); - String result = execDockerCmd(dockerContNamesCmd, DOCKER_TIMEOUT); + private String generateNewContainerName() throws PluginExecutionException { + String containerContNamesCmd = "ps -a --format \"{{.Names}}\""; + debug("container names list command: " + containerContNamesCmd); + String result = execContainerCmdWithPrefix(containerContNamesCmd, CONTAINER_TIMEOUT); if (result == null) { return DEVMODE_CONTAINER_BASE_NAME; } @@ -1712,10 +1709,10 @@ private String generateNewContainerName() { * @param contName name of the container to check for networks * @return a String array containing the names of the networks the specified container is connected to */ - private String[] getContainerNetworks(String contName) { - String dockerNetworkCmd = "docker inspect -f '{{.NetworkSettings.Networks}}' " + contName; - String cmdResult = execDockerCmd(dockerNetworkCmd, DOCKER_TIMEOUT, false); - if (cmdResult == null || cmdResult.contains(" RC=")) { // RC is added in execDockerCmd if there is an error + private String[] getContainerNetworks(String contName) throws PluginExecutionException { + String containerNetworkCmd = "inspect -f '{{.NetworkSettings.Networks}}' " + contName; + String cmdResult = execContainerCmdWithPrefix(containerNetworkCmd, CONTAINER_TIMEOUT, false); + if (cmdResult == null || cmdResult.contains(" RC=")) { // RC is added in execContainerCmd if there is an error warn("Unable to retrieve container networks."); return null; } @@ -1725,17 +1722,17 @@ private String[] getContainerNetworks(String contName) { } /** - * Parses Docker network names from a "docker inspect" command result on a container. - * @param dockerResult the result from the command "docker inspect -f '{{.NetworkSettings.Networks}}' containerName". - * The dockerResult must not contain surrounding quotes or leading/trailing whitespace. - * @return a String array containing the names of the networks contained in the dockerResult parameter + * Parses container network names from a "docker inspect" command result on a container. + * @param containerResult the result from the command "docker inspect -f '{{.NetworkSettings.Networks}}' containerName" + * -> containerResult must not contain surrounding quotes or leading/trailing whitespace + * @return a String array containing the names of the networks contained in the containerResult parameter */ - protected static String[] parseNetworks(String dockerResult) { - // Example dockerResult value: map[bridge:0xc000622000 myNet:0xc0006220c0 otherNet:0xc000622180] - if (!dockerResult.matches("map\\[(.*?)\\]")) { + protected static String[] parseNetworks(String containerResult) { + // Example containerResult value: map[bridge:0xc000622000 myNet:0xc0006220c0 otherNet:0xc000622180] + if (!containerResult.matches("map\\[(.*?)\\]")) { return null; } - String networkMap = dockerResult.substring(dockerResult.indexOf("[")+1, dockerResult.indexOf("]")); + String networkMap = containerResult.substring(containerResult.indexOf("[")+1, containerResult.indexOf("]")); String[] networkHex = networkMap.split(" "); String[] networks = new String[networkHex.length]; for (int i=0; i < networkHex.length; i++) { @@ -1744,12 +1741,12 @@ protected static String[] parseNetworks(String dockerResult) { return networks; } - private String getContainerIPAddress(String contName, String network) { - String dockerIPAddressCmd = "docker inspect -f '{{.NetworkSettings.Networks." + network + ".IPAddress}}' " + contName; - String result = execDockerCmd(dockerIPAddressCmd, DOCKER_TIMEOUT, false); - if (result == null || result.contains(" RC=")) { // RC is added in execDockerCmd if there is an error + private String getContainerIPAddress(String contName, String network) throws PluginExecutionException { + String containerIPAddressCmd = "inspect -f '{{.NetworkSettings.Networks." + network + ".IPAddress}}' " + contName; + String result = execContainerCmdWithPrefix(containerIPAddressCmd, CONTAINER_TIMEOUT, false); + if (result == null || result.contains(" RC=")) { // RC is added in execContainerCmd if there is an error warn("Unable to retrieve container IP address for network '" + network + "'."); - return ""; // this is what Docker displays when an IP address it not found for a network + return ""; // this is what Docker/Podman displays when an IP address it not found for a network } return removeSurroundingQuotes(result.trim()); } @@ -1768,15 +1765,15 @@ private String getCopiedFiles() { if (new File(srcMount.get(i)).exists()) { // only Files are in this list param.append(" -v ").append(srcMount.get(i)).append(":").append(destMount.get(i)); } else { - error("A file referenced by the Dockerfile is not found: " + srcMount.get(i) + - ". Update the Dockerfile or ensure the file is in the correct location."); + error("A file referenced by the Containerfile is not found: " + srcMount.get(i) + + ". Update the Containerfile or ensure the file is in the correct location."); } } return param.toString(); } private String getUserId() { - if (OSUtil.isLinux()) { + if (OSUtil.isLinux() || !isDocker) { try { String id = runCmd("id -u"); if (id != null) { @@ -1816,7 +1813,7 @@ public void restartServer() throws PluginExecutionException { /** * Stop the server, set up Liberty and restart it. - * @param buildContainer Force a Docker build when in container mode. Ignored otherwise. + * @param buildContainer Force a container build when in container mode. Ignored otherwise. */ public void restartServer(boolean buildContainer) throws PluginExecutionException { info("Restarting server..."); @@ -1848,7 +1845,7 @@ public void restartServer(boolean buildContainer) throws PluginExecutionExceptio // suppress install feature warning System.setProperty(SKIP_BETA_INSTALL_WARNING, Boolean.TRUE.toString()); libertyCreate(); - // Skip installing features on container during restart, since the Dockerfile + // Skip installing features on container during restart, since the Containerfile/Dockerfile // should have 'RUN features.sh' // Also skip install feature on restart if config parameter specified. if (!container && !skipInstallFeature) { @@ -1920,7 +1917,7 @@ protected int parseHostName(String webAppMessage) throws PluginExecutionExceptio return portPrefixIndex; } - protected void parseHttpPort(String webAppMessage, int portPrefixIndex) { + protected void parseHttpPort(String webAppMessage, int portPrefixIndex) throws PluginExecutionException { if (!webAppMessage.contains(HTTP_PREFIX)) { return; } @@ -1992,15 +1989,15 @@ private String getPortFromMessageTokens(String[] messageTokens) throws PluginExe return null; } - private String findLocalPort(String internalContainerPort) { - String dockerPortCmd = "docker port " + containerName + " " + internalContainerPort; - String cmdResult = execDockerCmd(dockerPortCmd, DOCKER_TIMEOUT, false); + private String findLocalPort(String internalContainerPort) throws PluginExecutionException { + String containerPortCmd = "port " + containerName + " " + internalContainerPort; + String cmdResult = execContainerCmdWithPrefix(containerPortCmd, CONTAINER_TIMEOUT, false); if (cmdResult == null) { warn("Unable to retrieve locally mapped port."); return null; } - if (cmdResult.contains(" RC=")) { // This piece of the string is added in execDockerCmd if there is an error - warn("Unable to retrieve locally mapped port. Docker result: \"" + cmdResult.split(" RC=")[0] + "\". Ensure the Docker ports are mapped correctly."); + if (cmdResult.contains(" RC=")) { // This piece of the string is added in execContainerCmd if there is an error + warn("Unable to retrieve locally mapped port. Container result: \"" + cmdResult.split(" RC=")[0] + "\". Ensure the container ports are mapped correctly."); return null; } String[] cmdResultSplit = cmdResult.split(":"); @@ -2028,7 +2025,7 @@ public void cleanUpServerEnv() { // Delete server.env file serverEnvFile.delete(); } - } catch (Exception e) { + } catch (IOException e) { error("Could not retrieve server.env: " + e.getMessage()); } } @@ -2040,22 +2037,22 @@ public void cleanUpTempConfig() { try { FileUtils.deleteDirectory(tempConfig); debug("Successfully deleted liberty:dev temporary configuration folder"); - } catch (Exception e) { + } catch (IOException e) { warn("Could not delete liberty:dev temporary configuration folder: " + e.getMessage()); } } } } - public void cleanUpTempDockerfile() { - if (!keepTempDockerfile && tempDockerfilePath != null) { - File tempDockerfile = tempDockerfilePath.toFile(); - if (tempDockerfile.exists()) { + public void cleanUpTempContainerfile() { + if (!keepTempContainerfile && tempContainerfilePath != null) { + File tempContainerfile = tempContainerfilePath.toFile(); + if (tempContainerfile.exists()) { try { - Files.delete(tempDockerfilePath); - debug("Successfully deleted dev mode temporary Dockerfile"); - } catch (Exception e) { - warn("Could not delete dev mode temporary Dockerfile: " + e.getMessage()); + Files.delete(tempContainerfilePath); + debug("Successfully deleted dev mode temporary Containernerfile"); + } catch (IOException e) { + warn("Could not delete dev mode temporary Containerfile: " + e.getMessage()); } } } @@ -2088,28 +2085,29 @@ private void runShutdownHook(final ThreadPoolExecutor executor) { if (trackingMode == FileTrackMode.POLLING || trackingMode == FileTrackMode.NOT_SET) { disablePolling(); } - + setDevStop(true); cleanUpTempConfig(); cleanUpServerEnv(); - + if (hotkeyReader != null) { hotkeyReader.shutdown(); } - + // shutdown tests executor.shutdown(); - + + // stopping server if (container) { - cleanUpTempDockerfile(); + cleanUpTempContainerfile(); stopContainer(); } else { stopServer(); } } - } catch (Exception e) { - warn("Received exception during server shutdown and cleanup: " + e.getMessage()); + } catch (PluginExecutionException pe) { + error("Exception during container shutdown. Container may have shutdown incorrectly. " + pe.getMessage()); } } @@ -2327,6 +2325,8 @@ public void runHotkeyReaderThread(ThreadPoolExecutor executor) { firstStartup = false; } catch (InterruptedException e) { debug("Interrupted while waiting to determine whether input can be read", e); + } catch (PluginExecutionException pe) { + error(pe.getMessage()); } } @@ -2338,7 +2338,7 @@ public void runHotkeyReaderThread(ThreadPoolExecutor executor) { * @param inputUnavailable If true, indicates that the console is non-interactive so hotkey messages should not be printed. * @param startup If true, include attention barriers (asterisks lines) and overall dev mode startup messages such as list of hotkeys and ports. */ - private void printDevModeMessages(boolean inputUnavailable, boolean startup) { + private void printDevModeMessages(boolean inputUnavailable, boolean startup) throws PluginExecutionException { // the following will be printed only on startup or restart if (startup) { // print barrier header @@ -2381,9 +2381,9 @@ private void printDevModeMessages(boolean inputUnavailable, boolean startup) { if (containerHttpPort != null) { if (httpPort != null) { if (!nonDefaultHttpPortUsed) { - info(formatAttentionMessage("Internal container HTTP port [ " + containerHttpPort + " ] is mapped to Docker host port [ " + httpPort + " ]")); + info(formatAttentionMessage("Internal container HTTP port [ " + containerHttpPort + " ] is mapped to container host port [ " + httpPort + " ]")); } else { - info(formatAttentionMessage("Internal container HTTP port [ " + containerHttpPort + " ] is mapped to Docker host port [ " + httpPort + " ] <")); + info(formatAttentionMessage("Internal container HTTP port [ " + containerHttpPort + " ] is mapped to container host port [ " + httpPort + " ] <")); } } else { info(formatAttentionMessage("Internal container HTTP port: [ " + containerHttpPort + " ]")); @@ -2392,9 +2392,9 @@ private void printDevModeMessages(boolean inputUnavailable, boolean startup) { if (containerHttpsPort != null) { if (httpsPort != null) { if (!nonDefaultHttpsPortUsed) { - info(formatAttentionMessage("Internal container HTTPS port [ " + containerHttpsPort + " ] is mapped to Docker host port [ " + httpsPort + " ]")); + info(formatAttentionMessage("Internal container HTTPS port [ " + containerHttpsPort + " ] is mapped to container host port [ " + httpsPort + " ]")); } else { - info(formatAttentionMessage("Internal container HTTPS port [ " + containerHttpsPort + " ] is mapped to Docker host port [ " + httpsPort + " ] <")); + info(formatAttentionMessage("Internal container HTTPS port [ " + containerHttpsPort + " ] is mapped to container host port [ " + httpsPort + " ] <")); } } else { info(formatAttentionMessage("Internal container HTTPS port: [ " + containerHttpsPort + " ]")); @@ -2403,19 +2403,19 @@ private void printDevModeMessages(boolean inputUnavailable, boolean startup) { if (libertyDebug) { int debugPort = (alternativeDebugPort == -1 ? libertyDebugPort : alternativeDebugPort); if (!nonDefaultDebugPortUsed) { - info(formatAttentionMessage("Liberty debug port mapped to Docker host port: [ " + debugPort + " ]")); + info(formatAttentionMessage("Liberty debug port mapped to container host port: [ " + debugPort + " ]")); } else { - info(formatAttentionMessage("Liberty debug port mapped to Docker host port: [ " + debugPort + " ] <")); + info(formatAttentionMessage("Liberty debug port mapped to container host port: [ " + debugPort + " ] <")); } } info(formatAttentionMessage("")); - info(formatAttentionTitle("Docker network information:")); + info(formatAttentionTitle("Container network information:")); info(formatAttentionMessage("Container name: [ " + containerName + " ]")); String[] networks = getContainerNetworks(containerName); if (networks != null) { for (String network : networks) { - info(formatAttentionMessage("IP address [ " + getContainerIPAddress(containerName, network) + " ] on Docker network [ " + network + " ]")); + info(formatAttentionMessage("IP address [ " + getContainerIPAddress(containerName, network) + " ] on container network [ " + network + " ]")); } } } @@ -2455,7 +2455,7 @@ private void printHelpMessages() { printFeatureGenerationHotkeys(); printTestsMessage(true); if (container) { - info(formatAttentionMessage("r - rebuild the Docker image and restart the container, type 'r' and press Enter.")); + info(formatAttentionMessage("r - rebuild the container image and restart the container, type 'r' and press Enter.")); } else { info(formatAttentionMessage("r - restart the server, type 'r' and press Enter.")); } @@ -2673,7 +2673,7 @@ private void readInput() { File bootstrapPropertiesFileParent; File jvmOptionsFile; File jvmOptionsFileParent; - File dockerfileUsed; + File containerfileUsed; File looseAppFile; WatchService watcher; boolean lastChangeCompiled; // tracks when the module containing the latest src file change is compiled successfully @@ -2689,6 +2689,10 @@ private void readInput() { * @param outputDirectory * @param testOutputDirectory * @param executor + * @param compileArtifactPaths Compile classpath elements, or null if this + * DevUtil instance has useBuildRecompile=true + * @param testArtifactPaths Test classpath elements, or null if this + * DevUtil instance has useBuildRecompile=true * @param serverXmlFile Can be null when using the server.xml from the * configDirectory, which has a default value. * @param bootstrapPropertiesFile @@ -2702,7 +2706,7 @@ public void watchFiles(File outputDirectory, File testOutputDirectory, final Thr this.serverXmlFile = serverXmlFile; this.bootstrapPropertiesFile = bootstrapPropertiesFile; this.jvmOptionsFile = jvmOptionsFile; - this.dockerfileUsed = null; + this.containerfileUsed = null; this.initialCompile = true; this.lastChangeCompiled = false; this.disableDependencyCompile = false; @@ -2830,8 +2834,8 @@ public void watchFiles(File outputDirectory, File testOutputDirectory, final Thr } if (container) { - dockerfileUsed = getDockerfile(); - registerSingleFile(dockerfileUsed, executor); + containerfileUsed = getContainerfile(); + registerSingleFile(containerfileUsed, executor); } HashMap resourceMap = new HashMap(); @@ -2867,20 +2871,20 @@ public void watchFiles(File outputDirectory, File testOutputDirectory, final Thr checkStopDevMode(true); if (container) { - synchronized(dockerfileDirectoriesToWatch) { - if (!dockerfileDirectoriesToWatch.isEmpty()) { - for (Path path : dockerfileDirectoriesToWatch) { + synchronized(containerfileDirectoriesToWatch) { + if (!containerfileDirectoriesToWatch.isEmpty()) { + for (Path path : containerfileDirectoriesToWatch) { File f = path.toFile(); if (f.isDirectory()) { - debug("Registering path from dockerfileDirectoriesToWatch: " + path); + debug("Registering path from containerfileDirectoriesToWatch: " + path); registerAll(path, executor, true); } else { - debug("Registering file path from dockerfileDirectoriesToWatch: " + path); + debug("Registering file path from containerfileDirectoriesToWatch: " + path); registerSingleFile(f, executor, true); } - dockerfileDirectoriesTracked.add(path); + containerfileDirectoriesTracked.add(path); } - dockerfileDirectoriesToWatch.clear(); + containerfileDirectoriesToWatch.clear(); } } } @@ -3188,7 +3192,7 @@ public void watchFiles(File outputDirectory, File testOutputDirectory, final Thr /** * - * @return {@code Collection} of class paths + * @return Collection of class paths * @throws IOException */ public Collection getJavaSourceClassPaths() throws IOException { @@ -3284,8 +3288,8 @@ public boolean accept(File file) { debug("Adding single file observer for: " + registerFile.toString()); FileAlterationObserver observer = addFileAlterationObserver(executor, parentPath, singleFileFilter); if (removeOnContainerRebuild) { - debug("Adding file to dockerfileDirectoriesFileObservers: " + registerFile.toString()); - dockerfileDirectoriesFileObservers.add(observer); + debug("Adding file to containerfileDirectoriesFileObservers: " + registerFile.toString()); + containerfileDirectoriesFileObservers.add(observer); } } catch (Exception e) { error("Could not observe single file " + registerFile.toString(), e); @@ -3300,8 +3304,8 @@ public boolean accept(File file) { StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_CREATE }, SensitivityWatchEventModifier.HIGH); if (removeOnContainerRebuild) { - debug("Adding file to dockerfileDirectoriesWatchKeys: " + registerFile.getName()); - dockerfileDirectoriesWatchKeys.add(key); + debug("Adding file to containerfileDirectoriesWatchKeys: " + registerFile.getName()); + containerfileDirectoriesWatchKeys.add(key); } } } @@ -4185,8 +4189,8 @@ private void processFileChanges( && fileChanged.getCanonicalPath().endsWith(bootstrapPropertiesFile.getName())) { // This is for bootstrap.properties outside of the config folder // restart server to load new properties - if (isDockerfileDirectoryChanged(fileChanged)) { - untrackDockerfileDirectoriesAndRestart(); + if (isContainerfileDirectoryChanged(fileChanged)) { + untrackContainerfileDirectoriesAndRestart(); } else { restartServer(false); } @@ -4195,8 +4199,8 @@ private void processFileChanges( && fileChanged.getCanonicalPath().endsWith(jvmOptionsFile.getName())) { // This is for jvm.options outside of the config folder // restart server to load new options - if (isDockerfileDirectoryChanged(fileChanged)) { - untrackDockerfileDirectoriesAndRestart(); + if (isContainerfileDirectoryChanged(fileChanged)) { + untrackContainerfileDirectoriesAndRestart(); } else { restartServer(false); } @@ -4241,19 +4245,19 @@ private void processFileChanges( } runTestThread(true, executor, numApplicationUpdatedMessages, skipUTs, false, buildFile); } - } else if (fileChanged.equals(dockerfileUsed) - && directory.startsWith(dockerfileUsed.getParentFile().getCanonicalFile().toPath()) - && changeType == ChangeType.MODIFY) { // dockerfile - untrackDockerfileDirectoriesAndRestart(); // untrack all Dockerfile directories, then rebuild container and restart + } else if (fileChanged.equals(containerfileUsed) + && directory.startsWith(containerfileUsed.getParentFile().getCanonicalFile().toPath()) + && changeType == ChangeType.MODIFY) { // containerfile + untrackContainerfileDirectoriesAndRestart(); // untrack all Containerfile directories, then rebuild container and restart } else if (propertyFilesMap != null && propertyFilesMap.keySet().contains(fileChanged)) { // properties file boolean reloadedPropertyFile = reloadPropertyFile(fileChanged); // run all tests on properties file change if (reloadedPropertyFile) { runTestThread(true, executor, numApplicationUpdatedMessages, skipUTs, false, buildFile); } - } else if (isDockerfileDirectoryChanged(fileChanged)) { - // If contents within a directory specified in a Dockerfile COPY command were changed, and not already processed by one of the other conditions above. - untrackDockerfileDirectoriesAndRestart(); + } else if (isContainerfileDirectoryChanged(fileChanged)) { + // If contents within a directory specified in a Containerfile COPY command were changed, and not already processed by one of the other conditions above. + untrackContainerfileDirectoriesAndRestart(); } else if (!recompileDependencies && generateFeatures && directory.startsWith(outputPath)) { // track Java source class files if generateFeatures=true && recompileDependencies=false // When recompileDependencies=true and class files are tracked, there are too many recompiled class files and feature generation runs too often @@ -4338,8 +4342,8 @@ private void processConfigFileChange(File fileChanged, ChangeType changeType, Th updateExistingFeatures(); } - if (isDockerfileDirectoryChanged(serverDirectory, fileChanged)) { - untrackDockerfileDirectoriesAndRestart(); + if (isContainerfileDirectoryChanged(serverDirectory, fileChanged)) { + untrackContainerfileDirectoriesAndRestart(); } else { if (changeType == ChangeType.CREATE) { redeployApp(); @@ -4377,8 +4381,8 @@ && serverFeaturesModified()) { optimizeGenerateFeatures(); } // Let this restart if needed for container mode. Otherwise, nothing else needs to be done for config file delete. - if (isDockerfileDirectoryChanged(serverDirectory, fileChanged)) { - untrackDockerfileDirectoriesAndRestart(); + if (isContainerfileDirectoryChanged(serverDirectory, fileChanged)) { + untrackContainerfileDirectoriesAndRestart(); } else { if (fileChanged.getName().equals("server.env")) { // re-enable debug variables in server.env @@ -4432,21 +4436,21 @@ private ProjectModule getFirstProjectModule(ProjectModule project, File buildFil protected abstract void resourceDeleted(File fileChanged, File resourceParent, File outputDirectory) throws IOException; /** - * Unwatches all directories that were specified in Dockerfile COPY commands, then does a container + * Unwatches all directories that were specified in Containerfile COPY commands, then does a container * rebuild and restart. * * @throws PluginExecutionException */ - private void untrackDockerfileDirectoriesAndRestart() throws PluginExecutionException { - // Cancel and clear any WatchKeys that were added for to the Dockerfile directories - for (WatchKey key : dockerfileDirectoriesWatchKeys) { + private void untrackContainerfileDirectoriesAndRestart() throws PluginExecutionException { + // Cancel and clear any WatchKeys that were added for to the Containerfile directories + for (WatchKey key : containerfileDirectoriesWatchKeys) { key.cancel(); } - dockerfileDirectoriesWatchKeys.clear(); + containerfileDirectoriesWatchKeys.clear(); - // Cancel and clear any FileAlterationObservers that were added for the Dockerfile directories + // Cancel and clear any FileAlterationObservers that were added for the Containerfile directories synchronized (cancelledFileObservers) { - for (FileAlterationObserver observer : dockerfileDirectoriesFileObservers) { + for (FileAlterationObserver observer : containerfileDirectoriesFileObservers) { // add the observer to be cancelled cancelledFileObservers.add(observer); try { @@ -4457,32 +4461,32 @@ private void untrackDockerfileDirectoriesAndRestart() throws PluginExecutionExce } } } - dockerfileDirectoriesFileObservers.clear(); + containerfileDirectoriesFileObservers.clear(); // Untrack the directories - dockerfileDirectoriesTracked.clear(); + containerfileDirectoriesTracked.clear(); restartServer(true); } /** - * If container mode, check if any of the files are within a directory specified in one of the Dockerfile's + * If container mode, check if any of the files are within a directory specified in one of the Containerfile's * COPY commands. If not container mode, does nothing. * * @param file The files to check, in the same order. - * @return true if container mode and any of the files are within a directory specified in one of the Dockerfile's COPY commands. + * @return true if container mode and any of the files are within a directory specified in one of the Containerfile's COPY commands. * @throws IOException if there was an error getting canonical paths */ - private boolean isDockerfileDirectoryChanged(File... files) throws IOException { - // Check for directory content changes from directories specified in Dockerfile - if (container && !dockerfileDirectoriesTracked.isEmpty()) { - for (Path trackedPath : dockerfileDirectoriesTracked) { + private boolean isContainerfileDirectoryChanged(File... files) throws IOException { + // Check for directory content changes from directories specified in Containerfile + if (container && !containerfileDirectoriesTracked.isEmpty()) { + for (Path trackedPath : containerfileDirectoriesTracked) { Path logsPath = new File(serverDirectory, "logs").getCanonicalFile().toPath(); for (File file : files) { // if the file's path is a child of the tracked path, except for the server logs folder or if it's the loose application itself Path filePath = file.getCanonicalFile().toPath(); if (filePath.startsWith(trackedPath) && !filePath.startsWith(logsPath) && !filePath.toString().endsWith(".war.xml") && !filePath.toString().endsWith(".ear.xml")) { - debug("isDockerfileDirectoryChanged=true for directory " + trackedPath + " with file " + file); + debug("isContainerfileDirectoryChanged=true for directory " + trackedPath + " with file " + file); return true; } } @@ -4559,10 +4563,10 @@ public boolean accept(File pathname) { String serverDirName = serverDirectory.getName(); // skip: // - ignore list - // - workarea, messaging and logs dirs from the server directory, since those can be + // - workarea, messaging, and logs dirs from the server directory, since those can be // changing boolean skip = ignoreFileOrDir(pathname) || (pathname.isDirectory() && - (name.equals("workarea") || name.equals("logs") || (name.equals("messaging") && parent.equals(serverDirName)))); + (name.equals("workarea") || name.equals("logs") || (name.equals("messaging") && parent.equals(serverDirName)))); return !skip; } }, true); @@ -4770,8 +4774,8 @@ public boolean accept(File file) { debug("Adding subdirectory to file observers: " + dir.toString()); FileAlterationObserver observer = addFileAlterationObserver(executor, dir.toString(), singleDirectoryFilter); if (removeOnContainerRebuild) { - debug("Adding to dockerfileDirectoriesFileObservers: " + dir); - dockerfileDirectoriesFileObservers.add(observer); + debug("Adding to containerfileDirectoriesFileObservers: " + dir); + containerfileDirectoriesFileObservers.add(observer); } } catch (Exception e) { error("Could not observe directory " + dir.toString(), e); @@ -4785,8 +4789,8 @@ public boolean accept(File file) { StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_CREATE }, SensitivityWatchEventModifier.HIGH); if (removeOnContainerRebuild) { - debug("Adding to dockerfileDirectoriesWatchKeys: " + dir); - dockerfileDirectoriesWatchKeys.add(key); + debug("Adding to containerfileDirectoriesWatchKeys: " + dir); + containerfileDirectoriesWatchKeys.add(key); } } return FileVisitResult.CONTINUE; @@ -5658,6 +5662,7 @@ public void writeDevcMetadata(boolean alive) { XMLStreamWriter metadataWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(new FileWriter(metaFile)) ; metadataWriter.writeStartDocument(); metadataWriter.writeStartElement("devcModeMetaData"); + writeElement(metadataWriter, "containerEngine", isDocker ? DEVC_CONTAINER_DOCKER : DEVC_CONTAINER_PODMAN); writeElement(metadataWriter, "containerName", containerName != null ? containerName : DEVMODE_CONTAINER_BASE_NAME); writeElement(metadataWriter, "containerAlive", String.valueOf(alive)); metadataWriter.writeEndElement(); diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/InstallFeatureUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/InstallFeatureUtil.java index c705e643..041694a9 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/InstallFeatureUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/InstallFeatureUtil.java @@ -1098,7 +1098,7 @@ public static String productInfo(File installDirectory, String action) throws Pl } } - private void installFeaturesOnContainer(List features, boolean acceptLicense) { + private void installFeaturesOnContainer(List features, boolean acceptLicense) throws PluginExecutionException { if (features == null || features.isEmpty()) { debug("Skipping installing features on container " + containerName + " since no features were specified."); return; @@ -1111,13 +1111,13 @@ private void installFeaturesOnContainer(List features, boolean acceptLic featureList.append(feature).append(" "); } - String featureUtilityCommand = "docker exec -e FEATURE_LOCAL_REPO=/devmode-maven-cache " + containerName + " featureUtility installFeature " + featureList; + String featureUtilityCommand = getContainerCommandPrefix() + " exec -e FEATURE_LOCAL_REPO=/devmode-maven-cache " + containerName + " featureUtility installFeature " + featureList; if (acceptLicense) { featureUtilityCommand += "--acceptLicense"; } - String cmdResult = execDockerCmd(featureUtilityCommand, 600, false); - if (cmdResult.contains(" RC=")) { // This piece of the string is added in execDockerCmd if there is an error + String cmdResult = execContainerCmd(featureUtilityCommand, 600, false); + if (cmdResult.contains(" RC=")) { // This piece of the string is added in execContainerCmd if there is an error if (cmdResult.contains("CWWKF1250I")) { // The features are already installed message debug(cmdResult); @@ -1140,5 +1140,4 @@ private boolean isFeatureConflict(String exceptionMessage) { Matcher m = conflictPattern.matcher(exceptionMessage); return m.find(); } - } diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/VersionUtility.java b/src/main/java/io/openliberty/tools/common/plugins/util/VersionUtility.java index 9e10460a..f2cbbffa 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/VersionUtility.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/VersionUtility.java @@ -43,7 +43,7 @@ public static int compareArtifactVersion(String currentVersion, String compareVe * @return integer representing result of comparison */ public static int compareArtifactVersion(String currentVersion, String compareVersion, boolean isLibertyVersion) { - String[] compareVersionArray = compareVersion.split("\\."); + String[] compareVersionArray = compareVersion.trim().split("\\."); int majorVersion = Integer.parseInt(compareVersionArray[0]); int minorVersion = isLibertyVersion ? 0 : Integer.parseInt(compareVersionArray[1]); int patchLevel = isLibertyVersion ? Integer.parseInt(compareVersionArray[3]) : Integer.parseInt(compareVersionArray[2]); @@ -54,7 +54,7 @@ public static int compareArtifactVersion(String currentVersion, String compareVe if (currentVersion.contains("-")) { currentVersion = currentVersion.substring(0, currentVersion.indexOf("-")); } - String[] currentVersionArray = currentVersion.split("\\."); + String[] currentVersionArray = currentVersion.trim().split("\\."); majorVersion = Integer.parseInt(currentVersionArray[0]); minorVersion = isLibertyVersion ? 0 : Integer.parseInt(currentVersionArray[1]); patchLevel = isLibertyVersion ? Integer.parseInt(currentVersionArray[3]) : Integer.parseInt(currentVersionArray[2]); diff --git a/src/test/java/io/openliberty/tools/common/plugins/util/DevUtilPrepareDockerfileTest.java b/src/test/java/io/openliberty/tools/common/plugins/util/DevUtilPrepareContainerfileTest.java similarity index 61% rename from src/test/java/io/openliberty/tools/common/plugins/util/DevUtilPrepareDockerfileTest.java rename to src/test/java/io/openliberty/tools/common/plugins/util/DevUtilPrepareContainerfileTest.java index 1e3317bc..3ca00e4a 100644 --- a/src/test/java/io/openliberty/tools/common/plugins/util/DevUtilPrepareDockerfileTest.java +++ b/src/test/java/io/openliberty/tools/common/plugins/util/DevUtilPrepareContainerfileTest.java @@ -1,5 +1,5 @@ /** - * (C) Copyright IBM Corporation 2020. + * (C) Copyright IBM Corporation 2020, 2023. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import org.junit.Before; import org.junit.Test; -public class DevUtilPrepareDockerfileTest extends BaseDevUtilTest { +public class DevUtilPrepareContainerfileTest extends BaseDevUtilTest { DevUtil util; File dockerfiles = new File("src/test/resources/dockerbuild"); @@ -47,12 +47,12 @@ public void tearDown() { } } - private void testPrepareDockerfile(String testFile, String expectedFile) throws PluginExecutionException, IOException { + private void testPrepareContainerfile(String testFile, String expectedFile) throws PluginExecutionException, IOException { File test = new File(dockerfiles, testFile); File expected = new File(dockerfiles, expectedFile); - result = util.prepareTempDockerfile(test, dockerfiles.getAbsolutePath()); + result = util.prepareTempContainerfile(test, dockerfiles.getAbsolutePath()); // trim the overall file content string since the file write can insert an extra line break at the end - assertEquals(util.readDockerfile(expected), util.readDockerfile(result)); + assertEquals(util.readContainerfile(expected), util.readContainerfile(result)); } @Test @@ -85,61 +85,61 @@ public void testCleanAndCombine() throws Exception { } @Test - public void testBasicDockerfile() throws Exception { - testPrepareDockerfile("basic.txt", "basic-expected.txt"); + public void testBasicContainerfile() throws Exception { + testPrepareContainerfile("basic.txt", "basic-expected.txt"); assertTrue(util.srcMount.get(0).endsWith("server.xml")); assertTrue(util.destMount.get(0).endsWith("/config/server.xml")); } @Test - public void testBasicDockerfileFlow() throws Exception { + public void testBasicContainerfileFlow() throws Exception { File dockerfile = new File(dockerfiles, "basic.txt"); - List dockerfileLines = util.readDockerfile(dockerfile); - - List expectedDockerfileLines = new ArrayList(); - expectedDockerfileLines.add("FROM openliberty/open-liberty:kernel-java8-openj9-ubi"); - expectedDockerfileLines.add(""); - expectedDockerfileLines.add("# Add my app and config"); - expectedDockerfileLines.add("COPY --chown=1001:0 Sample1.war /config/dropins/"); - expectedDockerfileLines.add("COPY --chown=1001:0 server.xml /config/"); - expectedDockerfileLines.add(""); - expectedDockerfileLines.add("# Default setting for the verbose option"); - expectedDockerfileLines.add("ARG VERBOSE=false"); - expectedDockerfileLines.add(""); - expectedDockerfileLines.add("# This script will add the requested XML snippets, grow image to be fit-for-purpose and apply interim fixes"); - expectedDockerfileLines.add("RUN configure.sh"); - assertEquals(expectedDockerfileLines, dockerfileLines); + List dockerfileLines = util.readContainerfile(dockerfile); + + List expectedContainerfileLines = new ArrayList(); + expectedContainerfileLines.add("FROM openliberty/open-liberty:kernel-java8-openj9-ubi"); + expectedContainerfileLines.add(""); + expectedContainerfileLines.add("# Add my app and config"); + expectedContainerfileLines.add("COPY --chown=1001:0 Sample1.war /config/dropins/"); + expectedContainerfileLines.add("COPY --chown=1001:0 server.xml /config/"); + expectedContainerfileLines.add(""); + expectedContainerfileLines.add("# Default setting for the verbose option"); + expectedContainerfileLines.add("ARG VERBOSE=false"); + expectedContainerfileLines.add(""); + expectedContainerfileLines.add("# This script will add the requested XML snippets, grow image to be fit-for-purpose and apply interim fixes"); + expectedContainerfileLines.add("RUN configure.sh"); + assertEquals(expectedContainerfileLines, dockerfileLines); char escape = util.getEscapeCharacter(dockerfileLines); assertEquals(String.valueOf('\\'), String.valueOf(escape)); dockerfileLines = util.getCleanedLines(dockerfileLines); List cleanedLines = new ArrayList(); - expectedDockerfileLines.clear(); - expectedDockerfileLines.add("FROM openliberty/open-liberty:kernel-java8-openj9-ubi"); - expectedDockerfileLines.add("COPY --chown=1001:0 Sample1.war /config/dropins/"); - expectedDockerfileLines.add("COPY --chown=1001:0 server.xml /config/"); - expectedDockerfileLines.add("ARG VERBOSE=false"); - expectedDockerfileLines.add("RUN configure.sh"); - assertEquals(expectedDockerfileLines, dockerfileLines); + expectedContainerfileLines.clear(); + expectedContainerfileLines.add("FROM openliberty/open-liberty:kernel-java8-openj9-ubi"); + expectedContainerfileLines.add("COPY --chown=1001:0 Sample1.war /config/dropins/"); + expectedContainerfileLines.add("COPY --chown=1001:0 server.xml /config/"); + expectedContainerfileLines.add("ARG VERBOSE=false"); + expectedContainerfileLines.add("RUN configure.sh"); + assertEquals(expectedContainerfileLines, dockerfileLines); dockerfileLines = util.getCombinedLines(dockerfileLines, escape); - expectedDockerfileLines.clear(); - expectedDockerfileLines.add("FROM openliberty/open-liberty:kernel-java8-openj9-ubi"); - expectedDockerfileLines.add("COPY --chown=1001:0 Sample1.war /config/dropins/"); - expectedDockerfileLines.add("COPY --chown=1001:0 server.xml /config/"); - expectedDockerfileLines.add("ARG VERBOSE=false"); - expectedDockerfileLines.add("RUN configure.sh"); - assertEquals(expectedDockerfileLines, dockerfileLines); + expectedContainerfileLines.clear(); + expectedContainerfileLines.add("FROM openliberty/open-liberty:kernel-java8-openj9-ubi"); + expectedContainerfileLines.add("COPY --chown=1001:0 Sample1.war /config/dropins/"); + expectedContainerfileLines.add("COPY --chown=1001:0 server.xml /config/"); + expectedContainerfileLines.add("ARG VERBOSE=false"); + expectedContainerfileLines.add("RUN configure.sh"); + assertEquals(expectedContainerfileLines, dockerfileLines); util.removeWarFileLines(dockerfileLines); - expectedDockerfileLines.clear(); - expectedDockerfileLines.add("FROM openliberty/open-liberty:kernel-java8-openj9-ubi"); - expectedDockerfileLines.add("COPY --chown=1001:0 server.xml /config/"); - expectedDockerfileLines.add("ARG VERBOSE=false"); - expectedDockerfileLines.add("RUN configure.sh"); - assertEquals(expectedDockerfileLines, dockerfileLines); + expectedContainerfileLines.clear(); + expectedContainerfileLines.add("FROM openliberty/open-liberty:kernel-java8-openj9-ubi"); + expectedContainerfileLines.add("COPY --chown=1001:0 server.xml /config/"); + expectedContainerfileLines.add("ARG VERBOSE=false"); + expectedContainerfileLines.add("RUN configure.sh"); + assertEquals(expectedContainerfileLines, dockerfileLines); util.processCopyLines(dockerfileLines, dockerfile.getParent()); assertTrue(util.srcMount.get(0).endsWith("server.xml")); @@ -154,18 +154,18 @@ public void testBasicDockerfileFlow() throws Exception { assertEquals(1, util.destMount.size()); util.disableOpenJ9SCC(dockerfileLines); - expectedDockerfileLines.clear(); - expectedDockerfileLines.add("FROM openliberty/open-liberty:kernel-java8-openj9-ubi"); - expectedDockerfileLines.add("COPY --chown=1001:0 server.xml /config/"); - expectedDockerfileLines.add("ARG VERBOSE=false"); - expectedDockerfileLines.add("ENV OPENJ9_SCC=false"); - expectedDockerfileLines.add("RUN configure.sh"); - assertEquals(expectedDockerfileLines, dockerfileLines); + expectedContainerfileLines.clear(); + expectedContainerfileLines.add("FROM openliberty/open-liberty:kernel-java8-openj9-ubi"); + expectedContainerfileLines.add("COPY --chown=1001:0 server.xml /config/"); + expectedContainerfileLines.add("ARG VERBOSE=false"); + expectedContainerfileLines.add("ENV OPENJ9_SCC=false"); + expectedContainerfileLines.add("RUN configure.sh"); + assertEquals(expectedContainerfileLines, dockerfileLines); } @Test - public void testMultilineDockerfile() throws Exception { - testPrepareDockerfile("multiline.txt", "multiline-expected.txt"); + public void testMultilineContainerfile() throws Exception { + testPrepareContainerfile("multiline.txt", "multiline-expected.txt"); assertTrue(util.srcMount.get(0).endsWith("file1.xml")); assertTrue(util.destMount.get(0).endsWith("/config/filenameWithoutExtension")); assertTrue(util.srcMount.get(1).endsWith("file2.xml")); @@ -173,8 +173,8 @@ public void testMultilineDockerfile() throws Exception { } @Test - public void testMultilineEscapeDockerfile() throws Exception { - testPrepareDockerfile("multilineEscape.txt", "multilineEscape-expected.txt"); + public void testMultilineEscapeContainerfile() throws Exception { + testPrepareContainerfile("multilineEscape.txt", "multilineEscape-expected.txt"); assertTrue(util.srcMount.get(0).endsWith("server.xml")); assertTrue(util.destMount.get(0).endsWith("c:\\config\\server.xml")); } @@ -201,7 +201,7 @@ public void testGetEscapeChar() throws Exception { @Test public void testCopyParsing() throws Exception { - testPrepareDockerfile("copyParsing.txt", "copyParsing-expected.txt"); + testPrepareContainerfile("copyParsing.txt", "copyParsing-expected.txt"); assertTrue(util.srcMount.get(0).endsWith("file1.xml")); assertTrue(util.destMount.get(0).endsWith("/config/file1.xml")); assertTrue(util.srcMount.get(1).endsWith("file2.xml")); @@ -211,9 +211,9 @@ public void testCopyParsing() throws Exception { } @Test - public void testDockerBuildContext() throws Exception { + public void testContainerBuildContext() throws Exception { File test = new File(dockerfiles, "dockerBuildContext.txt"); - result = util.prepareTempDockerfile(test, new File("my/context").getAbsolutePath()); + result = util.prepareTempContainerfile(test, new File("my/context").getAbsolutePath()); // use Paths comparison to be OS agnostic assertTrue(Paths.get(util.srcMount.get(0)).endsWith("my/context/path1/path2/file1.xml")); assertTrue(util.destMount.get(0).endsWith("/config/file1.xml")); @@ -226,52 +226,52 @@ public void testDockerBuildContext() throws Exception { @Test public void testDisableOpenJ9SCC_lowercase() throws Exception { List dockerfileLines = new ArrayList(); - List expectedDockerfileLines = new ArrayList(); + List expectedContainerfileLines = new ArrayList(); dockerfileLines.add("FROM openliberty/open-liberty"); dockerfileLines.add("run configure.sh"); util.disableOpenJ9SCC(dockerfileLines); - expectedDockerfileLines.add("FROM openliberty/open-liberty"); - expectedDockerfileLines.add("ENV OPENJ9_SCC=false"); - expectedDockerfileLines.add("run configure.sh"); - assertEquals(expectedDockerfileLines, dockerfileLines); + expectedContainerfileLines.add("FROM openliberty/open-liberty"); + expectedContainerfileLines.add("ENV OPENJ9_SCC=false"); + expectedContainerfileLines.add("run configure.sh"); + assertEquals(expectedContainerfileLines, dockerfileLines); } @Test public void testDisableOpenJ9SCC_uppercase() throws Exception { List dockerfileLines = new ArrayList(); - List expectedDockerfileLines = new ArrayList(); + List expectedContainerfileLines = new ArrayList(); dockerfileLines.add("FROM openliberty/open-liberty"); dockerfileLines.add("RUN configure.sh"); util.disableOpenJ9SCC(dockerfileLines); - expectedDockerfileLines.add("FROM openliberty/open-liberty"); - expectedDockerfileLines.add("ENV OPENJ9_SCC=false"); - expectedDockerfileLines.add("RUN configure.sh"); - assertEquals(expectedDockerfileLines, dockerfileLines); + expectedContainerfileLines.add("FROM openliberty/open-liberty"); + expectedContainerfileLines.add("ENV OPENJ9_SCC=false"); + expectedContainerfileLines.add("RUN configure.sh"); + assertEquals(expectedContainerfileLines, dockerfileLines); } @Test public void testDisableOpenJ9SCC_mixedcase() throws Exception { List dockerfileLines = new ArrayList(); - List expectedDockerfileLines = new ArrayList(); + List expectedContainerfileLines = new ArrayList(); dockerfileLines.add("FROM openliberty/open-liberty"); dockerfileLines.add("RuN configure.sh"); util.disableOpenJ9SCC(dockerfileLines); - expectedDockerfileLines.add("FROM openliberty/open-liberty"); - expectedDockerfileLines.add("ENV OPENJ9_SCC=false"); - expectedDockerfileLines.add("RuN configure.sh"); - assertEquals(expectedDockerfileLines, dockerfileLines); + expectedContainerfileLines.add("FROM openliberty/open-liberty"); + expectedContainerfileLines.add("ENV OPENJ9_SCC=false"); + expectedContainerfileLines.add("RuN configure.sh"); + assertEquals(expectedContainerfileLines, dockerfileLines); } @Test public void testDisableOpenJ9SCC_negative() throws Exception { List dockerfileLines = new ArrayList(); - List expectedDockerfileLines = new ArrayList(); + List expectedContainerfileLines = new ArrayList(); dockerfileLines.add("FROM openliberty/open-liberty"); dockerfileLines.add("RUN configure.shaaaaaaaaaa"); util.disableOpenJ9SCC(dockerfileLines); - expectedDockerfileLines.add("FROM openliberty/open-liberty"); - expectedDockerfileLines.add("RUN configure.shaaaaaaaaaa"); - assertEquals(expectedDockerfileLines, dockerfileLines); + expectedContainerfileLines.add("FROM openliberty/open-liberty"); + expectedContainerfileLines.add("RUN configure.shaaaaaaaaaa"); + assertEquals(expectedContainerfileLines, dockerfileLines); } @Test @@ -298,12 +298,12 @@ public void testDetectFeaturesSh() throws Exception { @Test public void testRemoveEarFileLines() throws Exception { List dockerfileLines = new ArrayList(); - List expectedDockerfileLines = new ArrayList(); + List expectedContainerfileLines = new ArrayList(); dockerfileLines.add("FROM openliberty/open-liberty"); dockerfileLines.add("COPY --chown=1001:0 target/guide-maven-multimodules-ear.ear /config/apps/"); util.removeEarFileLines(dockerfileLines); - expectedDockerfileLines.add("FROM openliberty/open-liberty"); - assertEquals(expectedDockerfileLines, dockerfileLines); + expectedContainerfileLines.add("FROM openliberty/open-liberty"); + assertEquals(expectedContainerfileLines, dockerfileLines); } } \ No newline at end of file