Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a new extension for generating Dockerfiles #42316

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2929,6 +2929,21 @@
<artifactId>quarkus-container-image-util</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-dockerfiles</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-dockerfiles-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-dockerfiles-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes</artifactId>
Expand Down Expand Up @@ -6538,7 +6553,6 @@
<version>${project.version}</version>
</dependency>
<!-- End of Relocations, please put new extensions above this list -->

</dependencies>
</dependencyManagement>

Expand Down
13 changes: 13 additions & 0 deletions devtools/bom-descriptor-json/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,19 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-dockerfiles</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elasticsearch-java-client</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions docs/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,19 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-dockerfiles-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elasticsearch-java-client-deployment</artifactId>
Expand Down
110 changes: 110 additions & 0 deletions extensions/dockerfiles/cli/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>quarkus-dockerfiles-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>quarkus-dockerfiles-cli</artifactId>
<name>Quarkus - Dockerfiles - CLI</name>
<description>CLI plugin that provides commands for Dockerfile genration</description>

<properties>
<quarkus.package.jar.type>uber-jar</quarkus.package.jar.type>
</properties>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-picocli</artifactId>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-devtools-common</artifactId>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-maven-resolver</artifactId>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-dockerfiles-spi</artifactId>
</dependency>

<!-- This dependency is here to make sure the build order is correct-->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-picocli-deployment</artifactId>
<type>pom</type>
<scope>test</scope>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my case it was a problem to have CLI be a quarkus app. Check you can build from scratch.

<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
<configuration>
<skipOriginalJarRename>true</skipOriginalJarRename>
<environmentVariables>
<MAVEN_REPO_LOCAL>${settings.localRepository}</MAVEN_REPO_LOCAL>
<GRADLE_OPTS>${env.MAVEN_OPTS}</GRADLE_OPTS>
</environmentVariables>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package io.quarkus.dockerfiles.cli;

import java.io.IOException;
import java.io.InputStream;
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.maven.dependency.ArtifactDependency;
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/Containerfiles.", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", optionListHeading = "%nOptions:%n")
public class Dockerfiles implements Callable<Integer> {

private static final ArtifactDependency QUARKUS_DOCKERFILES = new ArtifactDependency("io.quarkus", "quarkus-dockerfiles",
null, "jar", Dockerfiles.getVersion());
private static final ArtifactDependency QUARKUS_DOCKERFILES_SPI = new ArtifactDependency("io.quarkus",
"quarkus-dockerfiles-spi", null, "jar", Dockerfiles.getVersion());

@Option(names = {
"--jvm" }, paramLabel = "", order = 5, description = "Flag to enable JVM Dockerfile generation. By default a JVM dockerfile is generated unless options imply native.")
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<String> 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)
.setForcedDependencies(List.of(QUARKUS_DOCKERFILES, QUARKUS_DOCKERFILES_SPI))
.setBaseClassLoader(ClassLoader.getSystemClassLoader())
.build();

List<String> 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<List<GeneratedDockerfile>>() {
@Override
public void accept(List<GeneratedDockerfile> 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"));
}

private static String getVersion() {
return read(Dockerfiles.class.getClassLoader().getResourceAsStream("version"));
}

private static String read(InputStream is) {
try {
return new String(is.readAllBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<Object, BuildResult> {

@Override
public void accept(Object context, BuildResult buildResult) {
List<GeneratedDockerfile> 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<List<GeneratedDockerfile>> consumer = (Consumer<List<GeneratedDockerfile>>) context;
consumer.accept(dockerfiles);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
quarkus.log.level=WARN
quarkus.banner.enabled=false
1 change: 1 addition & 0 deletions extensions/dockerfiles/cli/src/main/resources/version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
${project.version}
Loading
Loading