From 8b617f80b7b140d1574ba19b7c76d3b1a9430a0c Mon Sep 17 00:00:00 2001 From: Ioannis Canellos Date: Fri, 2 Aug 2024 13:48:38 +0300 Subject: [PATCH] feat: Introduce a new extension for generating Dockerfiles --- bom/application/pom.xml | 16 ++- devtools/bom-descriptor-json/pom.xml | 13 ++ docs/pom.xml | 13 ++ extensions/dockerfiles/cli/pom.xml | 106 ++++++++++++++ .../quarkus/dockerfiles/cli/Dockerfiles.java | 134 ++++++++++++++++++ .../cli/GenerateDockerfilesHandler.java | 32 +++++ .../src/main/resources/application.properties | 2 + extensions/dockerfiles/deployment/pom.xml | 50 +++++++ .../deployment/DockerfileContent.java | 33 +++++ .../deployment/DockerfilesConfiguration.java | 43 ++++++ .../deployment/DockerfilesProcessor.java | 51 +++++++ .../src/main/resources/Dockerfile.jvm | 15 ++ .../src/main/resources/Dockerfile.native | 10 ++ .../test/DockerfilesDevModeTest.java | 23 +++ .../dockerfiles/test/DockerfilesTest.java | 23 +++ extensions/dockerfiles/picocli/pom.xml | 133 +++++++++++++++++ .../quarkus/dockerfiles/cli/Dockerfiles.java | 74 ++++++++++ .../cli/GenerateDockerfilesHandler.java | 15 ++ .../src/main/resources/application.properties | 2 + extensions/dockerfiles/pom.xml | 23 +++ extensions/dockerfiles/runtime/pom.xml | 52 +++++++ .../resources/META-INF/quarkus-extension.yaml | 10 ++ extensions/dockerfiles/spi/pom.xml | 33 +++++ .../dockerfiles/spi/GeneratedDockerfile.java | 32 +++++ .../dockerfiles/spi/JvmDockerfileFrom.java | 32 +++++ .../dockerfiles/spi/NativeDockerfileFrom.java | 32 +++++ extensions/pom.xml | 3 + 27 files changed, 1004 insertions(+), 1 deletion(-) create mode 100644 extensions/dockerfiles/cli/pom.xml create mode 100644 extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/Dockerfiles.java create mode 100644 extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/GenerateDockerfilesHandler.java create mode 100644 extensions/dockerfiles/cli/src/main/resources/application.properties create mode 100644 extensions/dockerfiles/deployment/pom.xml create mode 100644 extensions/dockerfiles/deployment/src/main/java/io/quarkus/dockerfiles/deployment/DockerfileContent.java create mode 100644 extensions/dockerfiles/deployment/src/main/java/io/quarkus/dockerfiles/deployment/DockerfilesConfiguration.java create mode 100644 extensions/dockerfiles/deployment/src/main/java/io/quarkus/dockerfiles/deployment/DockerfilesProcessor.java create mode 100644 extensions/dockerfiles/deployment/src/main/resources/Dockerfile.jvm create mode 100644 extensions/dockerfiles/deployment/src/main/resources/Dockerfile.native create mode 100644 extensions/dockerfiles/deployment/src/test/java/io/quarkus/dockerfiles/test/DockerfilesDevModeTest.java create mode 100644 extensions/dockerfiles/deployment/src/test/java/io/quarkus/dockerfiles/test/DockerfilesTest.java create mode 100644 extensions/dockerfiles/picocli/pom.xml create mode 100644 extensions/dockerfiles/picocli/src/main/java/io/quarkus/dockerfiles/cli/Dockerfiles.java create mode 100644 extensions/dockerfiles/picocli/src/main/java/io/quarkus/dockerfiles/cli/GenerateDockerfilesHandler.java create mode 100644 extensions/dockerfiles/picocli/src/main/resources/application.properties create mode 100644 extensions/dockerfiles/pom.xml create mode 100644 extensions/dockerfiles/runtime/pom.xml create mode 100644 extensions/dockerfiles/runtime/src/main/resources/META-INF/quarkus-extension.yaml create mode 100644 extensions/dockerfiles/spi/pom.xml create mode 100644 extensions/dockerfiles/spi/src/main/java/io/quarkus/dockerfiles/spi/GeneratedDockerfile.java create mode 100644 extensions/dockerfiles/spi/src/main/java/io/quarkus/dockerfiles/spi/JvmDockerfileFrom.java create mode 100644 extensions/dockerfiles/spi/src/main/java/io/quarkus/dockerfiles/spi/NativeDockerfileFrom.java diff --git a/bom/application/pom.xml b/bom/application/pom.xml index aa2bd188d81c0f..b6dbc7b09d1d24 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -2817,6 +2817,21 @@ quarkus-container-image-util ${project.version} + + io.quarkus + quarkus-dockerfiles + ${project.version} + + + io.quarkus + quarkus-dockerfiles-spi + ${project.version} + + + io.quarkus + quarkus-dockerfiles-deployment + ${project.version} + io.quarkus quarkus-kubernetes @@ -6847,7 +6862,6 @@ ${project.version} - diff --git a/devtools/bom-descriptor-json/pom.xml b/devtools/bom-descriptor-json/pom.xml index 5a04b1658c04ea..8ea5305a4c3898 100644 --- a/devtools/bom-descriptor-json/pom.xml +++ b/devtools/bom-descriptor-json/pom.xml @@ -3099,6 +3099,19 @@ + + io.quarkus + quarkus-dockerfiles + ${project.version} + pom + test + + + * + * + + + diff --git a/docs/pom.xml b/docs/pom.xml index b6f31472dc38ea..212e90634bc8e2 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -3111,6 +3111,19 @@ + + io.quarkus + quarkus-dockerfiles-deployment + ${project.version} + pom + test + + + * + * + + + diff --git a/extensions/dockerfiles/cli/pom.xml b/extensions/dockerfiles/cli/pom.xml new file mode 100644 index 00000000000000..0d4e6e83938d94 --- /dev/null +++ b/extensions/dockerfiles/cli/pom.xml @@ -0,0 +1,106 @@ + + + quarkus-dockerfiles-parent + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-dockerfiles-cli + Quarkus - Dockerfiles - CLI + CLI plugin that provides commands for Dockerfile genration + + + uber-jar + + + + + io.quarkus + quarkus-picocli + + + + io.quarkus + quarkus-arc + + + + io.quarkus + quarkus-devtools-common + + + + io.quarkus + quarkus-bootstrap-maven-resolver + + + + io.quarkus + quarkus-dockerfiles-spi + + + + + io.quarkus + quarkus-picocli-deployment + pom + test + ${project.version} + + + * + * + + + + + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + generate-code + generate-code-tests + + + true + + ${settings.localRepository} + ${env.MAVEN_OPTS} + + + + + + + maven-compiler-plugin + + + -parameters + + + + + + diff --git a/extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/Dockerfiles.java b/extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/Dockerfiles.java new file mode 100644 index 00000000000000..6da273a19e0473 --- /dev/null +++ b/extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/Dockerfiles.java @@ -0,0 +1,134 @@ +package io.quarkus.dockerfiles.cli; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.Callable; +import java.util.function.Consumer; + +import io.quarkus.bootstrap.BootstrapException; +import io.quarkus.bootstrap.app.AugmentAction; +import io.quarkus.bootstrap.app.CuratedApplication; +import io.quarkus.bootstrap.app.QuarkusBootstrap; +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.QuarkusProjectHelper; +import io.quarkus.dockerfiles.spi.GeneratedDockerfile; +import io.quarkus.picocli.runtime.annotations.TopCommand; +import picocli.CommandLine.Command; +import picocli.CommandLine.ExitCode; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; + +@TopCommand +@Command(name = "dockerfiles", sortOptions = false, mixinStandardHelpOptions = false, header = "Generate Dockerfiles.", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", optionListHeading = "%nOptions:%n") +public class Dockerfiles implements Callable { + + @Option(names = { "--jvm" }, paramLabel = "", order = 5, description = "Flag to enable JVM Dockerfile generation") + boolean generateJvmDockerfile; + + @Option(names = { "--native" }, paramLabel = "", order = 5, description = "Flag to enable Native Dockerfile generation") + boolean generateNativeDockerfile; + + @Parameters(arity = "0..1", paramLabel = "GENERATION_PATH", description = " The path to generate Dockerfiles") + Optional generationPath; + + public Integer call() { + Path projectRoot = getWorkingDirectory(); + BuildTool buildTool = QuarkusProjectHelper.detectExistingBuildTool(projectRoot); + if (buildTool == null) { + System.out.println("Unable to determine the build tool used for the project at " + projectRoot); + return ExitCode.USAGE; + } + Path targetDirecotry = projectRoot.resolve(buildTool.getBuildDirectory()); + QuarkusBootstrap quarkusBootstrap = QuarkusBootstrap.builder() + .setMode(QuarkusBootstrap.Mode.PROD) + .setApplicationRoot(getWorkingDirectory()) + .setProjectRoot(getWorkingDirectory()) + .setTargetDirectory(targetDirecotry) + .setLocalProjectDiscovery(true) + .setIsolateDeployment(false) + .setBaseClassLoader(ClassLoader.getSystemClassLoader()) + .build(); + + List resultBuildItemFQCNs = new ArrayList<>(); + + boolean hasJvmSuffix = generationPath.map(p -> p.endsWith(".jvm")).orElse(false); + boolean hasNativeSuffix = generationPath.map(p -> p.endsWith(".native")).orElse(false); + boolean isDirectory = generationPath.map(p -> Paths.get(p).toFile().isDirectory()) + .orElse(Paths.get("").toFile().isDirectory()); + + // Checking + if (generateJvmDockerfile && hasNativeSuffix) { + System.out.println("Cannot generate JVM Dockerfile when the path has a .native suffix"); + return ExitCode.USAGE; + } + if (generateNativeDockerfile && hasJvmSuffix) { + System.out.println("Cannot generate Native Dockerfile when the path has a .jvm suffix"); + return ExitCode.USAGE; + } else if (generateJvmDockerfile && generateNativeDockerfile && !isDirectory) { + + } + + if (generateJvmDockerfile || hasJvmSuffix) { + resultBuildItemFQCNs.add(GeneratedDockerfile.Jvm.class.getName()); + } + + if (generateNativeDockerfile || hasNativeSuffix) { + resultBuildItemFQCNs.add(GeneratedDockerfile.Native.class.getName()); + } + + if (resultBuildItemFQCNs.isEmpty()) { + generateJvmDockerfile = true; + resultBuildItemFQCNs.add(GeneratedDockerfile.Jvm.class.getName()); + } + + Path jvmDockerfile = (isDirectory + ? generationPath.map(p -> Paths.get(p)) + : generationPath.map(Paths::get)) + .orElse(Paths.get("Dockerfile.jvm")); + + Path nativeDockerfile = (isDirectory + ? generationPath.map(p -> Paths.get(p)) + : generationPath.map(Paths::get)) + .orElse(Paths.get("Dockerfile.native")); + + try (CuratedApplication curatedApplication = quarkusBootstrap.bootstrap()) { + AugmentAction action = curatedApplication.createAugmentor(); + + action.performCustomBuild(GenerateDockerfilesHandler.class.getName(), new Consumer>() { + @Override + public void accept(List dockerfiles) { + for (GeneratedDockerfile dockerfile : dockerfiles) { + if (dockerfile instanceof GeneratedDockerfile.Jvm) { + writeStringSafe(jvmDockerfile, dockerfile.getContent()); + System.out.println("Generated JVM Dockerfile: " + jvmDockerfile); + } else if (dockerfile instanceof GeneratedDockerfile.Native) { + writeStringSafe(nativeDockerfile, dockerfile.getContent()); + System.out.println("Generated Native Dockerfile: " + nativeDockerfile); + } + } + } + }, resultBuildItemFQCNs.toArray(new String[resultBuildItemFQCNs.size()])); + + } catch (BootstrapException e) { + throw new RuntimeException(e); + } + return ExitCode.OK; + } + + private void writeStringSafe(Path p, String content) { + try { + Files.writeString(p, content); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private Path getWorkingDirectory() { + return Paths.get(System.getProperty("user.dir")); + } +} diff --git a/extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/GenerateDockerfilesHandler.java b/extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/GenerateDockerfilesHandler.java new file mode 100644 index 00000000000000..3283b3fd818c39 --- /dev/null +++ b/extensions/dockerfiles/cli/src/main/java/io/quarkus/dockerfiles/cli/GenerateDockerfilesHandler.java @@ -0,0 +1,32 @@ +package io.quarkus.dockerfiles.cli; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import io.quarkus.builder.BuildResult; +import io.quarkus.dockerfiles.spi.GeneratedDockerfile; +import io.quarkus.dockerfiles.spi.GeneratedDockerfile.Jvm; +import io.quarkus.dockerfiles.spi.GeneratedDockerfile.Native; + +public class GenerateDockerfilesHandler implements BiConsumer { + + @Override + public void accept(Object context, BuildResult buildResult) { + List dockerfiles = new ArrayList<>(); + + GeneratedDockerfile.Jvm jvmDockerfile = buildResult.consumeOptional(GeneratedDockerfile.Jvm.class); + GeneratedDockerfile.Native nativeDockerfile = buildResult.consumeOptional(GeneratedDockerfile.Native.class); + + if (jvmDockerfile != null) { + dockerfiles.add(jvmDockerfile); + } + + if (nativeDockerfile != null) { + dockerfiles.add(nativeDockerfile); + } + Consumer> consumer = (Consumer>) context; + consumer.accept(dockerfiles); + } +} diff --git a/extensions/dockerfiles/cli/src/main/resources/application.properties b/extensions/dockerfiles/cli/src/main/resources/application.properties new file mode 100644 index 00000000000000..5a1d581bb13944 --- /dev/null +++ b/extensions/dockerfiles/cli/src/main/resources/application.properties @@ -0,0 +1,2 @@ +quarkus.log.level=WARN +quarkus.banner.enabled=false diff --git a/extensions/dockerfiles/deployment/pom.xml b/extensions/dockerfiles/deployment/pom.xml new file mode 100644 index 00000000000000..ed89d6cb22736c --- /dev/null +++ b/extensions/dockerfiles/deployment/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + + io.quarkus + quarkus-dockerfiles-parent + 999-SNAPSHOT + ../pom.xml + + quarkus-dockerfiles-deployment + Quarkus - Dockerfiles - Deployment + + + + io.quarkus + quarkus-arc-deployment + + + io.quarkus + quarkus-dockerfiles-spi + + + io.quarkus + quarkus-dockerfiles + + + io.quarkus + quarkus-junit5-internal + test + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/dockerfiles/deployment/src/main/java/io/quarkus/dockerfiles/deployment/DockerfileContent.java b/extensions/dockerfiles/deployment/src/main/java/io/quarkus/dockerfiles/deployment/DockerfileContent.java new file mode 100644 index 00000000000000..6208429770f279 --- /dev/null +++ b/extensions/dockerfiles/deployment/src/main/java/io/quarkus/dockerfiles/deployment/DockerfileContent.java @@ -0,0 +1,33 @@ +package io.quarkus.dockerfiles.deployment; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.charset.Charset; + +public class DockerfileContent { + + public static String getJvmDockerfileContent(String from) { + StringBuilder sb = new StringBuilder(); + sb.append("# Generated by Quarkus\n"); + sb.append("FROM ").append(from).append("\n"); + sb.append(readResource("Dockerfile.jvm")); + return sb.toString(); + } + + public static String getNativeDockerfileContent(String from) { + StringBuilder sb = new StringBuilder(); + sb.append("# Generated by Quarkus\n"); + sb.append("FROM ").append(from).append("\n"); + sb.append(readResource("Dockerfile.native")); + return sb.toString(); + } + + private static String readResource(String resource) { + try (InputStream in = DockerfileContent.class.getClassLoader().getResourceAsStream(resource)) { + return new String(in.readAllBytes(), Charset.defaultCharset()); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } +} diff --git a/extensions/dockerfiles/deployment/src/main/java/io/quarkus/dockerfiles/deployment/DockerfilesConfiguration.java b/extensions/dockerfiles/deployment/src/main/java/io/quarkus/dockerfiles/deployment/DockerfilesConfiguration.java new file mode 100644 index 00000000000000..17bf4af0262450 --- /dev/null +++ b/extensions/dockerfiles/deployment/src/main/java/io/quarkus/dockerfiles/deployment/DockerfilesConfiguration.java @@ -0,0 +1,43 @@ +package io.quarkus.dockerfiles.deployment; + +import java.util.Optional; + +import org.eclipse.microprofile.config.ConfigProvider; + +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot +public class DockerfilesConfiguration { + + static final String DEFAULT_JVM_FROM = "registry.access.redhat.com/ubi8/openjdk-21:1.19"; + static final String DEFAULT_NATIVE_FROM = "registry.access.redhat.com/ubi8/ubi-minimal:8.9"; + + /** + * The from image to use for JVM based Dockerfiles + */ + @ConfigItem(defaultValue = "registry.access.redhat.com/ubi8/openjdk-21:1.19") + String jvmFrom; + + /** + * The from image to use for native based Dockerfiles + */ + @ConfigItem(defaultValue = "registry.access.redhat.com/ubi8/ubi-minimal:8.9") + String nativeFrom; + + static String getDefaultJvmFrom() { + return DEFAULT_JVM_FROM; + } + + Optional getConfiguredJvmFrom() { + return ConfigProvider.getConfig().getOptionalValue("quarkus.dockerfiles.jvm-from", String.class); + } + + static String getDefaultNativeFrom() { + return DEFAULT_NATIVE_FROM; + } + + Optional getConfiguredNativeFrom() { + return ConfigProvider.getConfig().getOptionalValue("quarkus.dockerfiles.native-from", String.class); + } +} diff --git a/extensions/dockerfiles/deployment/src/main/java/io/quarkus/dockerfiles/deployment/DockerfilesProcessor.java b/extensions/dockerfiles/deployment/src/main/java/io/quarkus/dockerfiles/deployment/DockerfilesProcessor.java new file mode 100644 index 00000000000000..6ac062b98b9ffe --- /dev/null +++ b/extensions/dockerfiles/deployment/src/main/java/io/quarkus/dockerfiles/deployment/DockerfilesProcessor.java @@ -0,0 +1,51 @@ +package io.quarkus.dockerfiles.deployment; + +import java.util.Optional; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.dockerfiles.spi.GeneratedDockerfile; +import io.quarkus.dockerfiles.spi.JvmDockerfileFrom; +import io.quarkus.dockerfiles.spi.NativeDockerfileFrom; + +class DockerfilesProcessor { + + private static final String FEATURE = "dockerfiles"; + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(FEATURE); + } + + @BuildStep + JvmDockerfileFrom.Effective effectiveJvmDockerfile(DockerfilesConfiguration config, + Optional selected) { + // 1. Get FROM explicitly configured + // 2. Get FROM using BuildItems + // 3. Get FROM using default value + String from = config.getConfiguredJvmFrom() + .orElse(selected.map(JvmDockerfileFrom.Selected::getFrom).orElse(DockerfilesConfiguration.getDefaultJvmFrom())); + return new JvmDockerfileFrom.Effective(from); + } + + @BuildStep + NativeDockerfileFrom.Effective effectiveNativeDockerfile(DockerfilesConfiguration config, + Optional selected) { + // 1. Get FROM explicitly configured + // 2. Get FROM using BuildItems + // 3. Get FROM using default value + String from = config.getConfiguredNativeFrom().orElse( + selected.map(NativeDockerfileFrom.Selected::getFrom).orElse(DockerfilesConfiguration.getDefaultNativeFrom())); + return new NativeDockerfileFrom.Effective(from); + } + + @BuildStep + GeneratedDockerfile.Jvm buildJvmDockerfile(JvmDockerfileFrom.Effective effective) { + return new GeneratedDockerfile.Jvm(DockerfileContent.getJvmDockerfileContent(effective.getFrom())); + } + + @BuildStep + GeneratedDockerfile.Native buildNativeDockerfile(NativeDockerfileFrom.Effective effective) { + return new GeneratedDockerfile.Native(DockerfileContent.getNativeDockerfileContent(effective.getFrom())); + } +} diff --git a/extensions/dockerfiles/deployment/src/main/resources/Dockerfile.jvm b/extensions/dockerfiles/deployment/src/main/resources/Dockerfile.jvm new file mode 100644 index 00000000000000..a302af97afbaf8 --- /dev/null +++ b/extensions/dockerfiles/deployment/src/main/resources/Dockerfile.jvm @@ -0,0 +1,15 @@ +ENV LANGUAGE='en_US:en' + + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 target/quarkus-app/*.jar /deployments/ +COPY --chown=185 target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] diff --git a/extensions/dockerfiles/deployment/src/main/resources/Dockerfile.native b/extensions/dockerfiles/deployment/src/main/resources/Dockerfile.native new file mode 100644 index 00000000000000..a6384f850f4688 --- /dev/null +++ b/extensions/dockerfiles/deployment/src/main/resources/Dockerfile.native @@ -0,0 +1,10 @@ +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/extensions/dockerfiles/deployment/src/test/java/io/quarkus/dockerfiles/test/DockerfilesDevModeTest.java b/extensions/dockerfiles/deployment/src/test/java/io/quarkus/dockerfiles/test/DockerfilesDevModeTest.java new file mode 100644 index 00000000000000..6f3ff3f7266858 --- /dev/null +++ b/extensions/dockerfiles/deployment/src/test/java/io/quarkus/dockerfiles/test/DockerfilesDevModeTest.java @@ -0,0 +1,23 @@ +package io.quarkus.dockerfiles.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; + +public class DockerfilesDevModeTest { + + // Start hot reload (DevMode) test with your extension loaded + @RegisterExtension + static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnDevModeTest() { + // Write your dev mode tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-hot-reload for more information + Assertions.assertTrue(true, "Add dev mode assertions to " + getClass().getName()); + } +} diff --git a/extensions/dockerfiles/deployment/src/test/java/io/quarkus/dockerfiles/test/DockerfilesTest.java b/extensions/dockerfiles/deployment/src/test/java/io/quarkus/dockerfiles/test/DockerfilesTest.java new file mode 100644 index 00000000000000..2bb20c7f7608b3 --- /dev/null +++ b/extensions/dockerfiles/deployment/src/test/java/io/quarkus/dockerfiles/test/DockerfilesTest.java @@ -0,0 +1,23 @@ +package io.quarkus.dockerfiles.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class DockerfilesTest { + + // Start unit test with your extension loaded + @RegisterExtension + static final QuarkusUnitTest unitTest = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnUnitTest() { + // Write your unit tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-extensions for more information + Assertions.assertTrue(true, "Add some assertions to " + getClass().getName()); + } +} diff --git a/extensions/dockerfiles/picocli/pom.xml b/extensions/dockerfiles/picocli/pom.xml new file mode 100644 index 00000000000000..a38d15850017b8 --- /dev/null +++ b/extensions/dockerfiles/picocli/pom.xml @@ -0,0 +1,133 @@ + + + quarkus-dockerfiles-parent + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-dockerfiles-cli + Quarkus - Dockerfiles - CLI + CLI plugin that provides commands for Dockerfile genration + + + + + + info.picocli + picocli + 4.7.0 + + + + io.quarkus + quarkus-core + + + + io.quarkus + quarkus-devtools-common + + + + io.quarkus + quarkus-bootstrap-maven-resolver + + + + io.quarkus + quarkus-dockerfiles-spi + + + + + + + org.eclipse.aether + aether-api + 1.1.0 + + + org.eclipse.aether + aether-impl + 1.1.0 + + + org.eclipse.aether + aether-spi + 1.1.0 + + + org.eclipse.aether + aether-connector-basic + 1.1.0 + + + org.eclipse.aether + aether-transport-file + 1.1.0 + + + org.eclipse.aether + aether-transport-http + 1.1.0 + + + org.slf4j + * + + + + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + io.quarkus.dockerfiles.cli.Dockerfiles + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + io.quarkus.dockerfiles.cli.Dockerfiles + + + + + + + + + diff --git a/extensions/dockerfiles/picocli/src/main/java/io/quarkus/dockerfiles/cli/Dockerfiles.java b/extensions/dockerfiles/picocli/src/main/java/io/quarkus/dockerfiles/cli/Dockerfiles.java new file mode 100644 index 00000000000000..416239a74acb33 --- /dev/null +++ b/extensions/dockerfiles/picocli/src/main/java/io/quarkus/dockerfiles/cli/Dockerfiles.java @@ -0,0 +1,74 @@ +package io.quarkus.dockerfiles.cli; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import io.quarkus.bootstrap.app.AugmentAction; +import io.quarkus.bootstrap.app.CuratedApplication; +import io.quarkus.bootstrap.app.QuarkusBootstrap; +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.QuarkusProjectHelper; +import io.quarkus.dockerfiles.spi.GeneratedJvmDockerfileBuildItem; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.ExitCode; + +@Command(name = "dockerfiles", sortOptions = false, mixinStandardHelpOptions = false, header = "Generate Dockerfiles.", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", optionListHeading = "%nOptions:%n") +public class Dockerfiles implements Callable { + + public static void main(String[] args) { + CommandLine.call(new Dockerfiles(), args); + } + + public Integer call() { + Path projectRoot = getWorkingDirectory(); + BuildTool buildTool = QuarkusProjectHelper.detectExistingBuildTool(projectRoot); + if (buildTool == null) { + System.out.println("Unable to determine the build tool used for the project at " + projectRoot); + return ExitCode.USAGE; + } + Path targetDirecotry = projectRoot.resolve(buildTool.getBuildDirectory()); + System.out.println( + "Detected project at: " + projectRoot.toAbsolutePath() + " with target:" + buildTool.getBuildDirectory()); + + QuarkusBootstrap quarkusBootstrap = QuarkusBootstrap.builder() + .setMode(QuarkusBootstrap.Mode.PROD) + .setApplicationRoot(getWorkingDirectory()) + .setProjectRoot(getWorkingDirectory()) + .setTargetDirectory(targetDirecotry) + .setLocalProjectDiscovery(true) + .setIsolateDeployment(false) + .build(); + + List resultBuildItemFQCNs = new ArrayList<>(); + resultBuildItemFQCNs.add(GeneratedJvmDockerfileBuildItem.class.getName()); + System.out.println("Generating Dockerfiles for the following build items: " + + resultBuildItemFQCNs.stream().collect(Collectors.joining(", "))); + + try (CuratedApplication curatedApplication = quarkusBootstrap.bootstrap()) { + AugmentAction action = curatedApplication.createAugmentor(); + AtomicReference> tooMany = new AtomicReference<>(); + + action.performCustomBuild(GenerateDockerfilesHandler.class.getName(), new Consumer>() { + @Override + public void accept(List strings) { + tooMany.set(strings); + } + }, resultBuildItemFQCNs.toArray(new String[resultBuildItemFQCNs.size()])); + + } catch (Exception e) { + throw new RuntimeException(e); + } + return ExitCode.OK; + } + + private Path getWorkingDirectory() { + return Paths.get(System.getProperty("user.dir")); + } +} diff --git a/extensions/dockerfiles/picocli/src/main/java/io/quarkus/dockerfiles/cli/GenerateDockerfilesHandler.java b/extensions/dockerfiles/picocli/src/main/java/io/quarkus/dockerfiles/cli/GenerateDockerfilesHandler.java new file mode 100644 index 00000000000000..d549d69286be98 --- /dev/null +++ b/extensions/dockerfiles/picocli/src/main/java/io/quarkus/dockerfiles/cli/GenerateDockerfilesHandler.java @@ -0,0 +1,15 @@ +package io.quarkus.dockerfiles.cli; + +import java.util.function.BiConsumer; + +import io.quarkus.builder.BuildResult; +import io.quarkus.dockerfiles.spi.GeneratedJvmDockerfileBuildItem; + +public class GenerateDockerfilesHandler implements BiConsumer { + + @Override + public void accept(Object context, BuildResult buildResult) { + GeneratedJvmDockerfileBuildItem result = buildResult.consume(GeneratedJvmDockerfileBuildItem.class); + System.out.println(result.getDockerFile()); + } +} diff --git a/extensions/dockerfiles/picocli/src/main/resources/application.properties b/extensions/dockerfiles/picocli/src/main/resources/application.properties new file mode 100644 index 00000000000000..5a1d581bb13944 --- /dev/null +++ b/extensions/dockerfiles/picocli/src/main/resources/application.properties @@ -0,0 +1,2 @@ +quarkus.log.level=WARN +quarkus.banner.enabled=false diff --git a/extensions/dockerfiles/pom.xml b/extensions/dockerfiles/pom.xml new file mode 100644 index 00000000000000..1a3e6e45f16a0c --- /dev/null +++ b/extensions/dockerfiles/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + + io.quarkus + quarkus-extensions-parent + 999-SNAPSHOT + ../pom.xml + + + quarkus-dockerfiles-parent + pom + Quarkus - DockerFiles + + + spi + deployment + runtime + cli + + + diff --git a/extensions/dockerfiles/runtime/pom.xml b/extensions/dockerfiles/runtime/pom.xml new file mode 100644 index 00000000000000..6c9b341a7a5fe2 --- /dev/null +++ b/extensions/dockerfiles/runtime/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + + io.quarkus + quarkus-dockerfiles-parent + 999-SNAPSHOT + ../pom.xml + + quarkus-dockerfiles + Quakrus - Dockerfiles - Runtime + + + + io.quarkus + quarkus-arc + + + + + + + io.quarkus + quarkus-extension-maven-plugin + + + compile + + extension-descriptor + + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/dockerfiles/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/dockerfiles/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 00000000000000..cc238918a731f5 --- /dev/null +++ b/extensions/dockerfiles/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,10 @@ +--- +artifact: ${project.groupId}:${project.artifactId}:${project.version} +name: "Dokcerfiles" +metadata: + keywords: + - "dockerfiles" + guide: "https://quarkus.io/guides/dockerfiles" + categories: + - "cloud" + status: "stable" diff --git a/extensions/dockerfiles/spi/pom.xml b/extensions/dockerfiles/spi/pom.xml new file mode 100644 index 00000000000000..f448f21c69d03e --- /dev/null +++ b/extensions/dockerfiles/spi/pom.xml @@ -0,0 +1,33 @@ + + + quarkus-dockerfiles-parent + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-dockerfiles-spi + Quarkus - Dockerfiles - SPI + SPI for Dockerfile genration + + + + io.quarkus + quarkus-core-deployment + + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + + diff --git a/extensions/dockerfiles/spi/src/main/java/io/quarkus/dockerfiles/spi/GeneratedDockerfile.java b/extensions/dockerfiles/spi/src/main/java/io/quarkus/dockerfiles/spi/GeneratedDockerfile.java new file mode 100644 index 00000000000000..871e95ca6f1e15 --- /dev/null +++ b/extensions/dockerfiles/spi/src/main/java/io/quarkus/dockerfiles/spi/GeneratedDockerfile.java @@ -0,0 +1,32 @@ +package io.quarkus.dockerfiles.spi; + +import io.quarkus.builder.item.SimpleBuildItem; + +public interface GeneratedDockerfile { + + String getContent(); + + public static final class Jvm extends SimpleBuildItem implements GeneratedDockerfile { + private final String content; + + public Jvm(String content) { + this.content = content; + } + + public String getContent() { + return content; + } + } + + public static final class Native extends SimpleBuildItem implements GeneratedDockerfile { + private final String content; + + public Native(String content) { + this.content = content; + } + + public String getContent() { + return content; + } + } +} diff --git a/extensions/dockerfiles/spi/src/main/java/io/quarkus/dockerfiles/spi/JvmDockerfileFrom.java b/extensions/dockerfiles/spi/src/main/java/io/quarkus/dockerfiles/spi/JvmDockerfileFrom.java new file mode 100644 index 00000000000000..13e877536776c5 --- /dev/null +++ b/extensions/dockerfiles/spi/src/main/java/io/quarkus/dockerfiles/spi/JvmDockerfileFrom.java @@ -0,0 +1,32 @@ +package io.quarkus.dockerfiles.spi; + +import io.quarkus.builder.item.SimpleBuildItem; + +public interface JvmDockerfileFrom { + + String getFrom(); + + public static final class Selected extends SimpleBuildItem implements JvmDockerfileFrom { + private final String from; + + public Selected(String from) { + this.from = from; + } + + public String getFrom() { + return from; + } + } + + public static final class Effective extends SimpleBuildItem implements JvmDockerfileFrom { + private final String from; + + public Effective(String from) { + this.from = from; + } + + public String getFrom() { + return from; + } + } +} diff --git a/extensions/dockerfiles/spi/src/main/java/io/quarkus/dockerfiles/spi/NativeDockerfileFrom.java b/extensions/dockerfiles/spi/src/main/java/io/quarkus/dockerfiles/spi/NativeDockerfileFrom.java new file mode 100644 index 00000000000000..4902a16e7d5b8d --- /dev/null +++ b/extensions/dockerfiles/spi/src/main/java/io/quarkus/dockerfiles/spi/NativeDockerfileFrom.java @@ -0,0 +1,32 @@ +package io.quarkus.dockerfiles.spi; + +import io.quarkus.builder.item.SimpleBuildItem; + +public interface NativeDockerfileFrom { + + String getFrom(); + + public static final class Selected extends SimpleBuildItem implements NativeDockerfileFrom { + private final String from; + + public Selected(String from) { + this.from = from; + } + + public String getFrom() { + return from; + } + } + + public static final class Effective extends SimpleBuildItem implements NativeDockerfileFrom { + private final String from; + + public Effective(String from) { + this.from = from; + } + + public String getFrom() { + return from; + } + } +} diff --git a/extensions/pom.xml b/extensions/pom.xml index b692e68b77c0b8..c2224b4a7e00fd 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -185,6 +185,9 @@ openshift-client kubernetes-service-binding + + dockerfiles + flyway flyway-postgresql