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 3 commits 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 @@ -2859,6 +2859,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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
* This system property is used in rare by libraries (Truffle for example) to create their own ClassLoaders.
* The value of the system property is simply best effort, as there is no way to faithfully represent
* the Quarkus ClassLoader hierarchies in a system property value.
*
* @deprecated This was initially added to support Truffle, but it is no longer needed so the build item should not be used
Copy link
Member

Choose a reason for hiding this comment

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

Why is this part of this PR?

*/
@Deprecated(forRemoval = true)
public final class SetClassPathSystemPropBuildItem extends MultiBuildItem {
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.util.List;
import java.util.stream.Stream;

import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
Expand All @@ -15,17 +14,9 @@
import io.quarkus.maven.dependency.ResolvedDependency;
import io.quarkus.runtime.ClassPathSystemPropertyRecorder;

@SuppressWarnings("removal")
public class ClassPathSystemPropBuildStep {

@BuildStep
public void produce(BuildProducer<SetClassPathSystemPropBuildItem> producer, CurateOutcomeBuildItem curateOutcome) {
boolean truffleUsed = curateOutcome.getApplicationModel().getDependencies().stream()
.anyMatch(d -> d.getGroupId().equals("org.graalvm.polyglot"));
if (truffleUsed) {
producer.produce(new SetClassPathSystemPropBuildItem());
}
}

@BuildStep
@Record(ExecutionTime.STATIC_INIT)
public void set(List<SetClassPathSystemPropBuildItem> setCPItems,
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,154 @@
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.", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", optionListHeading = "%nOptions:%n")
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
@Command(name = "dockerfiles", sortOptions = false, mixinStandardHelpOptions = false, header = "Generate Dockerfiles.", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", optionListHeading = "%nOptions:%n")
@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")
Copy link
Member

Choose a reason for hiding this comment

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

Does it generate all the variants or a single one (in this case, which one?)

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")
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
@Parameters(arity = "0..1", paramLabel = "GENERATION_PATH", description = " The path to generate Dockerfiles")
@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);
}
}
}
Loading
Loading