diff --git a/plugin/src/main/java/org/wildfly/plugin/provision/AbstractProvisionServerMojo.java b/plugin/src/main/java/org/wildfly/plugin/provision/AbstractProvisionServerMojo.java index a75e04a7..3d2040cd 100644 --- a/plugin/src/main/java/org/wildfly/plugin/provision/AbstractProvisionServerMojo.java +++ b/plugin/src/main/java/org/wildfly/plugin/provision/AbstractProvisionServerMojo.java @@ -130,7 +130,7 @@ abstract class AbstractProvisionServerMojo extends AbstractMojo { * By default the server is provisioned into the {@code target/server} directory. */ @Parameter(alias = "provisioning-dir", property = PropertyNames.WILDFLY_PROVISIONING_DIR, defaultValue = Utils.WILDFLY_DEFAULT_DIR) - private String provisioningDir; + protected String provisioningDir; /** * Set to {@code true} if you want to delete the existing server referenced from the {@code provisioningDir} and provision a new one, diff --git a/plugin/src/main/java/org/wildfly/plugin/provision/ApplicationImageInfo.java b/plugin/src/main/java/org/wildfly/plugin/provision/ApplicationImageInfo.java new file mode 100644 index 00000000..74308f51 --- /dev/null +++ b/plugin/src/main/java/org/wildfly/plugin/provision/ApplicationImageInfo.java @@ -0,0 +1,101 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2022, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.wildfly.plugin.provision; + +/** + * This class holds all configuration to build and push application image + * from the {@code image} goal. + */ +public class ApplicationImageInfo { + + /** + * Whether the application image should be built (default is {@code true} + */ + protected boolean build = true; + + /** + * Whether the application image should be pushed (default is {@code false} + */ + protected boolean push = false; + + /** + * Determine which WildFly Runtime image to use so that the application runs with the specified JDK. + * The default is "11". Accepted values are "11", "17". + */ + private String jdkVersion = "11"; + + /** + * The group part of the name of the application image. + */ + private String group; + + /** + * The name part of the application image. If not set, the value of the artifactId (in lower case) is used. + */ + private String name; + + /** + * The tag part of the application image (default is @{code latest}. + */ + private String tag = "latest"; + + /** + * The container registry. + * + * If set, the registry is added to the application name. + * If the image is pushed and the registry is not set, it defaults to "docker.io" to login to the registry + */ + protected String registry; + + /** + * The user name to login to the container registry. + */ + protected String user; + + /** + * The user password to login to the container registry. + */ + protected String password; + + /** + * The binary used to build and push images (default is "docker"). + */ + protected String dockerBinary = "docker"; + + String getApplicationImageName(String artifactId) { + String registry = this.registry != null ? this.registry + "/" : ""; + String group = this.group != null ? this.group + "/" : ""; + String imageName = this.name != null ? this.name : artifactId.toLowerCase(); + String tag = this.tag; + + return registry + group + imageName + ":" + tag; + } + + String getWildFlyRuntimeImage() { + switch (jdkVersion) { + case "17": + return "quay.io/wildfly/wildfly-runtime-jdk17:latest"; + default: + return "quay.io/wildfly/wildfly-runtime-jdk11:latest"; + } + } +} diff --git a/plugin/src/main/java/org/wildfly/plugin/provision/ApplicationImageMojo.java b/plugin/src/main/java/org/wildfly/plugin/provision/ApplicationImageMojo.java new file mode 100644 index 00000000..c2bd9c58 --- /dev/null +++ b/plugin/src/main/java/org/wildfly/plugin/provision/ApplicationImageMojo.java @@ -0,0 +1,220 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2022, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.wildfly.plugin.provision; + +import static java.lang.String.format; +import static java.lang.String.join; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.Arrays; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; + +/** + * Build (and push) an application image containing the provisioned server and the deployment. + * + * The {@code}image{@code} goal extends the {@code}package{@code} goal, building and pushing the image occurs after the server + * is provisioned and the deployment deployed in it. + * + * The {@code}image{@code} goal relies on a Docker binary to execute all image commands (build, login, push). + * + * @since 4.0 + */ +@Mojo(name = "image", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, defaultPhase = LifecyclePhase.PACKAGE) +public class ApplicationImageMojo extends PackageServerMojo { + + public static final int DOCKER_CMD_CHECK_TIMEOUT = 3000; + + /** + * The configuration of the application image. + * + * The {@code image} goal accepts the following configuration: + * + *
+     * <image>
+     *   <!-- (optional) set it to false to skip build the application image (true by default) -->
+     *   <build>true</build>
+     *
+     *   <!-- (optional) set it to true to (login and) push the application image to the container registry (false by default).
+     *
+     *     If user and password are not specified, the image goal will not attempt to login to the container
+     *     registry prior to pushing the image.
+     *     The login to the container registry must then be performed before Maven is run.
+     *     -->
+     *   <push>true</push>
+     *
+     *   <!-- (optional) The binary used to perform image commands (build, login, push) (default is "docker") -->
+     *   <docker-binary>docker</docker-binary>
+     *
+     *   <!-- (optional) the JDK version used by the application. Default is "11", Allowed values are "11" and "17 -->
+     *   <jdk-version>11</jdk-version>
+     *
+     *   <!-- (optional) The group part of the name of the application image -->
+     *   <group>${user.name}</group>
+     *
+     *   <!-- (optional) The name part of the application image. If not set, the value of the artifactId (in lower case) is used -->
+     *   <name>${project.artifactId}</name>
+     *
+     *   <!-- (optional) The tag part of the application image (default is "latest") -->
+     *   <tag>latest</tag>
+     *
+     *   <!-- (optional) The container registry. If set, the registry is added to the application name.
+     *     If the image is pushed and the registry is not set, it defaults to "docker.io" to login to the registry
+     *     -->
+     *   <registry>quay.io</registry>
+     *
+     *   <!-- (optional) The user name to login to the container registry (if push is enabled). -->
+     *   <user>${user.name}</user>
+     *
+     *   <!-- (optional) The password login to the container registry (if push is enabled) -->
+     *   <password>${my.secret.password}</password>
+     * </image>
+     * 
+ */ + @Parameter(alias = "image") + private ApplicationImageInfo image; + + @Override + protected String getGoal() { + return "image"; + } + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + super.execute(); + + if (image == null) { + image = new ApplicationImageInfo(); + } + + if (!image.build) { + return; + } + + if (!isDockerBinaryAvailable(image.dockerBinary)) { + throw new MojoExecutionException(String.format("Unable to build application image with %s. Please check your %s installation", + image.dockerBinary, image.dockerBinary)); + } + + String image = this.image.getApplicationImageName(project.getArtifactId()); + String runtimeImage = this.image.getWildFlyRuntimeImage(); + + try { + boolean buildSuccess = buildApplicationImage(image, runtimeImage); + if (!buildSuccess) { + throw new MojoExecutionException(String.format("Unable to build application image %s", image)); + } + getLog().info(String.format("Successfully built application image %s", image)); + + if (this.image.push) { + logToRegistry(); + + boolean pushSuccess = pushApplicationImage(image); + if (!pushSuccess) { + throw new MojoExecutionException(String.format("Unable to push application image %s", image)); + } + getLog().info(String.format("Successfully pushed application image %s", image)); + } + } catch (IOException e) { + MojoExecutionException ex = new MojoExecutionException(e.getLocalizedMessage()); + ex.initCause(e); + throw ex; + } + } + + private void logToRegistry() throws MojoExecutionException { + String registry = image.registry; + if (registry == null) { + getLog().info("Registry was not set. Using docker.io"); + } + if (image.user != null && image.password != null) { + String[] dockerArgs = new String[] { + "login", registry, + "-u", image.user, + "-p", image.password + }; + boolean loginSuccessful = ExecUtil.exec(getLog(), image.dockerBinary, dockerArgs); + if (!loginSuccessful) { + throw new MojoExecutionException(String.format("Could not log to the container registry with the command %s %s %s", + image.dockerBinary, + String.join(" ", Arrays.copyOf(dockerArgs, dockerArgs.length -1)), + "*******")); + } + } + } + + private boolean buildApplicationImage(String image, String runtimeImage) throws IOException { + getLog().info(format("Building application image %s using %s.", image, this.image.dockerBinary)); + getLog().info(format("Base image is %s", runtimeImage)); + + generateDockerfile(runtimeImage, Paths.get(project.getBuild().getDirectory()), provisioningDir); + + String[] dockerArgs = new String[] {"build", "-t", image, "."}; + + getLog().info(format("Executing the following command to build application image: '%s %s'", this.image.dockerBinary, join(" ", dockerArgs))); + return ExecUtil.exec(getLog(), Paths.get(project.getBuild().getDirectory()).toFile(), this.image.dockerBinary, dockerArgs); + + } + + private boolean pushApplicationImage(String image) { + getLog().info(format("Pushing application image %s using %s.", image, this.image.dockerBinary)); + + String[] dockerArgs = new String[] {"push", image}; + + getLog().info(format("Executing the following command to push application image: '%s %s'", this.image.dockerBinary, join(" ", dockerArgs))); + return ExecUtil.exec(getLog(), Paths.get("target").toFile(), this.image.dockerBinary, dockerArgs); + } + + private void generateDockerfile(String runtimeImage, Path targetDir, String wildflyDirectory) throws IOException { + Files.writeString(targetDir.resolve("Dockerfile"), + "FROM " + runtimeImage + "\n" + + "COPY --chown=jboss:root " + wildflyDirectory + " $JBOSS_HOME\n" + + "RUN chmod -R ug+rwX $JBOSS_HOME", + StandardCharsets.UTF_8); + } + + private boolean isDockerBinaryAvailable(String dockerBinary) { + try { + if (!ExecUtil.execSilentWithTimeout(Duration.ofMillis(DOCKER_CMD_CHECK_TIMEOUT), dockerBinary, "-v")) { + + getLog().warn(format("'%s -v' returned an error code. Make sure your %s binary is correct", dockerBinary, dockerBinary)); + return false; + } + } catch (Exception e) { + getLog().warn(format("No %s binary found or general error: %s", dockerBinary, e)); + return false; + } + + return true; + } + +} diff --git a/plugin/src/main/java/org/wildfly/plugin/provision/ExecUtil.java b/plugin/src/main/java/org/wildfly/plugin/provision/ExecUtil.java new file mode 100644 index 00000000..a51969bf --- /dev/null +++ b/plugin/src/main/java/org/wildfly/plugin/provision/ExecUtil.java @@ -0,0 +1,161 @@ +// copied and adapted from Quarkus ExecUtil.java class at https://github.com/quarkusio/quarkus/blob/main/core/deployment/src/main/java/io/quarkus/deployment/util/ExecUtil.java + +package org.wildfly.plugin.provision; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import org.apache.maven.plugin.logging.Log; + +public class ExecUtil { + + private static final int PROCESS_CHECK_INTERVAL = 500; + + private static class HandleOutput implements Runnable { + + private final InputStream is; + private Log log; + + HandleOutput(InputStream is, org.apache.maven.plugin.logging.Log log) { + this.is = is; + this.log = log; + } + + @Override + public void run() { + try (InputStreamReader isr = new InputStreamReader(is); + BufferedReader reader = new BufferedReader(isr)) { + + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + if (log != null) { + log.info(line); + } + } + } catch (IOException e) { + log.error("Failed to handle output", e); + } + } + } + + /** + * Execute the specified command from within the current directory. + * + * @param command The command + * @param args The command arguments + * @return true if commands where executed successfully + */ + public static boolean exec(Log log, String command, String... args) { + return exec(log, new File("."), command, args); + } + + /** + * Execute silently the specified command until the given timeout from within the current directory. + * + * @param timeout The timeout + * @param command The command + * @param args The command arguments + * @return true if commands where executed successfully + */ + public static boolean execSilentWithTimeout(Duration timeout, String command, String... args) { + return execWithTimeout(null, new File("."), timeout, command, args); + } + + /** + * Execute the specified command from within the specified directory. + * The method allows specifying an output filter that processes the command output. + * + * @param directory The directory + * @param command The command + * @param args The command arguments + * @return true if commands where executed successfully + */ + public static boolean exec(Log log, File directory, String command, + String... args) { + try { + Process process = startProcess(directory, command, args); + new HandleOutput(process.getInputStream(), log).run(); + process.waitFor(); + return process.exitValue() == 0; + } catch (InterruptedException e) { + return false; + } + } + + /** + * Execute the specified command until the given timeout from within the specified directory. + * The method allows specifying an output filter that processes the command output. + * + * @param directory The directory + * @param timeout The timeout + * @param command The command + * @param args The command arguments + * @return true if commands where executed successfully + */ + public static boolean execWithTimeout(Log log, File directory, + Duration timeout, String command, String... args) { + try { + Process process = startProcess(directory, command, args); + Thread t = new Thread(new HandleOutput(process.getInputStream(), log)); + t.setName("Process stdout"); + t.setDaemon(true); + t.start(); + process.waitFor(timeout.toMillis(), TimeUnit.MILLISECONDS); + destroyProcess(process); + return process.exitValue() == 0; + } catch (InterruptedException e) { + return false; + } + } + + /** + * Start a process executing given command with arguments within the specified directory. + * + * @param directory The directory + * @param command The command + * @param args The command arguments + * @return the process + */ + public static Process startProcess(File directory, String command, String... args) { + try { + String[] cmd = new String[args.length + 1]; + cmd[0] = command; + if (args.length > 0) { + System.arraycopy(args, 0, cmd, 1, args.length); + } + return new ProcessBuilder() + .directory(directory) + .command(cmd) + .redirectErrorStream(true) + .start(); + } catch (IOException e) { + throw new RuntimeException("Input/Output error while executing command.", e); + } + } + + /** + * Kill the process, if still alive, kill it forcibly + * + * @param process the process to kill + */ + public static void destroyProcess(Process process) { + process.destroy(); + int i = 0; + while (process.isAlive() && i++ < 10) { + try { + process.waitFor(PROCESS_CHECK_INTERVAL, TimeUnit.MILLISECONDS); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + } + } + + if (process.isAlive()) { + process.destroyForcibly(); + } + } + +} \ No newline at end of file diff --git a/plugin/src/site/markdown/image-example.md.vm b/plugin/src/site/markdown/image-example.md.vm new file mode 100644 index 00000000..efb9e0e2 --- /dev/null +++ b/plugin/src/site/markdown/image-example.md.vm @@ -0,0 +1,168 @@ +# Build and push your application in a container image + +The `image` goal allows you to build an application image that contains a server and your application. +This application image contains your application ready to run on the cloud in a containerized platform such as Kubernetes. + +The operations related to the image are executing using a Docker binary. By default, it uses `docker` but it is possible to configure it +with the `` element to use another binary such as `podman`. + +By default, the `image` goal will only build the application image. +To push it to a container registry, you must configure the `` element with to `true`. + +#[[##]]# Build an application image + +The example below shows how to build an application image. + +```xml + + my-app + ... + + ... + + ... + + ${project.groupId} + ${project.artifactId} + ${project.version} + + + + + wildfly@maven(org.jboss.universe:community-universe) + + + + + jaxrs-server + + + + + + image + + + + + ... + + ... + +... + +``` + +Once `mvn package` is executed, a container image is available in with the docker binary with the name `my-app:latest`. +The name of the image is based on the `` value of the Maven module. + +You can then run the image locally using Docker: + +``` +docker run -p 8080:8080 my-app:latest +``` + +#[[##]]# Push an application image to a container registry. + +The example below shows how to configure the `image` goal to push the applicaition image to a container registry: + +```xml + + my-app + ... + + ... + + ... + + ${project.groupId} + ${project.artifactId} + ${project.version} + + + + wildfly@maven(org.jboss.universe:community-universe) + + + + jaxrs-server + + + true + quay.io + ${user.name} + ${user.name} + ${my.secret.password} + + + + + + image + + + + + ... + + ... + +... + +``` + +When `mvn package` is executed, the `image` goal will build the image with the name `quay.io/jdoe/my-app` (`jdoe` being the name +of the user running the Maven commands). +Before the image is pushed, it will login to `quay.io` using the credentials specified by `` and ``. +As it is not recommended to store credentials in clear in the `pom.xml`, we use a System propery (`my.secret.password`) to pass it when Maven is executed +(with `mvn package -Dmy.secret.password="********"). + + +#[[##]]# Configure the JDK version used by the application image. + +The application image is based on a runtime image provided by WildFly that contains all the runtimes required to run WildFly and your application.Push an application image to a container registry. +At the moment, WildFly provides runtime images for OpenJDK 11 and OpenJDK 17. + +By default, the `image` goal uses the OpenJDK 11 runtime image. It is possible to use the OpenJDK 17 image instead by configuring the `` to `17` as shown in the example below: + +```xml + + my-app + ... + + ... + + ... + + ${project.groupId} + ${project.artifactId} + ${project.version} + + + + wildfly@maven(org.jboss.universe:community-universe) + + + + jaxrs-server + + + 17 + + + + + + image + + + + + ... + + ... + +... + +``` \ No newline at end of file diff --git a/plugin/src/site/markdown/index.md.vm b/plugin/src/site/markdown/index.md.vm index 1f4326c2..a40610a4 100644 --- a/plugin/src/site/markdown/index.md.vm +++ b/plugin/src/site/markdown/index.md.vm @@ -21,6 +21,8 @@ * [${pluginPrefix}:provision](./provision-mojo.html) produces a server installation using Galleon. + * [${pluginPrefix}:image](./image-mojo.html) build (and push) a container image to run your application on the cloud. + * [${pluginPrefix}:redeploy](./redeploy-mojo.html) redeploys the application. * [${pluginPrefix}:redeploy-only](./redeploy-only-mojo.html) redeploys the application invoking no other goals by default. @@ -74,3 +76,5 @@ Of course, patches are welcome, too. Contributors can check out the project from * [Packaging the application and server Example](./package-example.html) * [Provisioning a server Example](./provision-example.html) + + * [Building (and pushing) an application image Example](./image-example.html) diff --git a/plugin/src/site/site.xml b/plugin/src/site/site.xml index a2384dd8..868093eb 100644 --- a/plugin/src/site/site.xml +++ b/plugin/src/site/site.xml @@ -62,6 +62,7 @@ + diff --git a/tests/standalone-tests/src/test/java/org/wildfly/plugin/provision/ImageTest.java b/tests/standalone-tests/src/test/java/org/wildfly/plugin/provision/ImageTest.java new file mode 100644 index 00000000..23387a4b --- /dev/null +++ b/tests/standalone-tests/src/test/java/org/wildfly/plugin/provision/ImageTest.java @@ -0,0 +1,83 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2021, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.wildfly.plugin.provision; + + +import static org.junit.Assume.assumeTrue; +import static org.wildfly.plugin.provision.ExecUtil.exec; +import static org.wildfly.plugin.provision.ExecUtil.execSilentWithTimeout; + +import java.nio.file.Path; +import java.time.Duration; + +import org.apache.maven.plugin.Mojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.junit.BeforeClass; +import org.junit.Test; +import org.wildfly.plugin.tests.AbstractProvisionConfiguredMojoTestCase; +import org.wildfly.plugin.tests.AbstractWildFlyMojoTest; + +public class ImageTest extends AbstractProvisionConfiguredMojoTestCase { + + public ImageTest() { + super("wildfly-maven-plugin"); + } + + @BeforeClass + public static void checkDockerInstallation() { + assumeTrue("Docker is not present in the installation, skipping the tests", + execSilentWithTimeout(Duration.ofMillis(3000), + "docker", "-v")); + } + + @Test + public void testBuildImage() throws Exception { + try { + assertTrue( + execSilentWithTimeout(Duration.ofMillis(3000), + "docker", "-v")); + assertFalse( + exec(null, + "docker", "inspect", "wildfly-maven-plugin/testing")); + + final Mojo imageMojo = lookupConfiguredMojo(AbstractWildFlyMojoTest.getPomFile("image-pom.xml").toFile(), "image"); + + imageMojo.execute(); + Path jbossHome = AbstractWildFlyMojoTest.getBaseDir().resolve("target").resolve("image-server"); + assertTrue(jbossHome.toFile().exists()); + + assertTrue( + exec(null, + "docker", "inspect", "wildfly-maven-plugin/testing")); + } finally { + + exec(null, + "docker", "rmi", "wildfly-maven-plugin/testing"); + } + } + + @Test(expected = MojoExecutionException.class) + public void testBuildImageWithUnknownDockerBinary() throws Exception { + final Mojo imageMojo = lookupConfiguredMojo(AbstractWildFlyMojoTest.getPomFile("image-unknown-docker-binary-pom.xml").toFile(), "image"); + imageMojo.execute(); + } +} diff --git a/tests/standalone-tests/src/test/resources/test-project/image-pom.xml b/tests/standalone-tests/src/test/resources/test-project/image-pom.xml new file mode 100644 index 00000000..a8135155 --- /dev/null +++ b/tests/standalone-tests/src/test/resources/test-project/image-pom.xml @@ -0,0 +1,50 @@ + + + + 4.0.0 + testing + testing + 0.1.0-SNAPSHOT + + + + + org.wildfly.plugins + wildfly-maven-plugin + + + + wildfly@maven(org.jboss.universe:community-universe)#WF_VERSION + + + + jaxrs-server + + image-server + + wildfly-maven-plugin + + + + + + + \ No newline at end of file diff --git a/tests/standalone-tests/src/test/resources/test-project/image-unknown-docker-binary-pom.xml b/tests/standalone-tests/src/test/resources/test-project/image-unknown-docker-binary-pom.xml new file mode 100644 index 00000000..cf9cd84d --- /dev/null +++ b/tests/standalone-tests/src/test/resources/test-project/image-unknown-docker-binary-pom.xml @@ -0,0 +1,51 @@ + + + + 4.0.0 + testing + testing + 0.1.0-SNAPSHOT + + + + + org.wildfly.plugins + wildfly-maven-plugin + + + + wildfly@maven(org.jboss.universe:community-universe)#WF_VERSION + + + + jaxrs-server + + image-server + + wildfly-maven-plugin + focker-is-not-docker + + + + + + + \ No newline at end of file