Skip to content

Commit

Permalink
feat: Introduce a new extension for generating Dockerfiles
Browse files Browse the repository at this point in the history
  • Loading branch information
iocanel committed Aug 5, 2024
1 parent b5231da commit ad21261
Show file tree
Hide file tree
Showing 25 changed files with 971 additions and 0 deletions.
25 changes: 25 additions & 0 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2817,6 +2817,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 @@ -6847,6 +6862,16 @@
<version>${project.version}</version>
</dependency>
<!-- End of Relocations, please put new extensions above this list -->
<dependency>
<groupId>io.quarkiverse.dockerfiles</groupId>
<artifactId>dockerfiles</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkiverse.dockerfiles</groupId>
<artifactId>dockerfiles-deployment</artifactId>
<version>${project.version}</version>
</dependency>

</dependencies>
</dependencyManagement>
Expand Down
84 changes: 84 additions & 0 deletions extensions/dockerfiles/cli/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<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>

<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>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</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,138 @@
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.dockerfiles.spi.GeneratedJvmDockerfileBuildItem;
import io.quarkus.dockerfiles.spi.GeneratedNativeDockerfileBuildItem;
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<Integer> {

@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<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)
.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(GeneratedJvmDockerfileBuildItem.class.getName());
resultBuildItemFQCNs.add(GeneratedDockerfile.Jvm.class.getName());
}

if (generateNativeDockerfile || hasNativeSuffix) {
resultBuildItemFQCNs.add(GeneratedNativeDockerfileBuildItem.class.getName());
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"));
}
}
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
50 changes: 50 additions & 0 deletions extensions/dockerfiles/deployment/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-dockerfiles-parent</artifactId>
<version>999-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>quarkus-dockerfiles-deployment</artifactId>
<name>Quarkus - Dockerfiles - Deployment</name>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-dockerfiles-spi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-dockerfiles</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Loading

0 comments on commit ad21261

Please sign in to comment.