Skip to content

Commit

Permalink
feat(jbang): harmonize Dockerfile presence
Browse files Browse the repository at this point in the history
* All the runtimes have a Dockerfile when we export the application
* Added a simple readme.md to show quickly how to build and run an application

Closes CAMEL-21259
  • Loading branch information
squakez committed Sep 26, 2024
1 parent 652d7b0 commit 505dc1c
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package org.apache.camel.dsl.jbang.core.commands;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Comparator;
import java.util.Date;
Expand All @@ -29,6 +31,7 @@
import org.apache.camel.tooling.maven.MavenGav;
import org.apache.camel.util.CamelCaseOrderedProperties;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.IOHelper;
import picocli.CommandLine.Command;

@Command(name = "export",
Expand Down Expand Up @@ -72,6 +75,7 @@ protected Integer export() throws Exception {
return 1;
}
}

}

private void doLoadAndInitProfileProperties(File file) throws Exception {
Expand Down Expand Up @@ -234,10 +238,37 @@ int rankGroupId(MavenGav o1) {
};
}

// Maven reproducible builds: https://maven.apache.org/guides/mini/guide-reproducible-builds.html
protected String getBuildMavenProjectDate() {
// 2024-09-23T10:00:00Z
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
return sdf.format(new Date());
}

// Copy the dockerfile into the same Maven project root directory.
protected void copyDockerFiles(String buildDir) throws Exception {
File docker = new File(buildDir, "src/main/docker");
docker.mkdirs();
String[] ids = gav.split(":");
InputStream is = ExportCamelMain.class.getClassLoader().getResourceAsStream("templates/Dockerfile.tmpl");
String context = IOHelper.loadText(is);
IOHelper.close(is);

String appJar = ids[1] + "-" + ids[2] + ".jar";
context = context.replaceAll("\\{\\{ \\.AppJar }}", appJar);
IOHelper.writeText(context, new FileOutputStream(new File(docker, "Dockerfile"), false));
}

// Copy the readme.md into the same Maven project root directory.
protected void copyReadme(String buildDir, String appJar) throws Exception {
String[] ids = gav.split(":");
InputStream is = ExportCamelMain.class.getClassLoader().getResourceAsStream("templates/readme.md.tmpl");
String context = IOHelper.loadText(is);
IOHelper.close(is);

context = context.replaceAll("\\{\\{ \\.ArtifactId }}", ids[1]);
context = context.replaceAll("\\{\\{ \\.Version }}", ids[2]);
context = context.replaceAll("\\{\\{ \\.AppRuntimeJar }}", appJar);
IOHelper.writeText(context, new FileOutputStream(new File(buildDir, "readme.md"), false));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ public Integer export() throws Exception {
copyMavenWrapper();
}
}

copyDockerFiles(BUILD_DIR);
String appJar = "target" + File.separator + ids[1] + "-" + ids[2] + ".jar";
copyReadme(BUILD_DIR, appJar);
if (cleanExportDir || !exportDir.equals(".")) {
// cleaning current dir can be a bit dangerous so only clean if explicit enabled
// otherwise always clean export-dir to avoid stale data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ public Integer export() throws Exception {
return prop;
});
// copy docker files
copyDockerFiles();
copyDockerFiles(BUILD_DIR);
String appJar = "target" + File.separator + "quarkus-app" + File.separator + "quarkus-run.jar";
copyReadme(BUILD_DIR, appJar);
// gather dependencies
Set<String> deps = resolveDependencies(settings, profile);
// copy local lib JARs
Expand Down Expand Up @@ -339,16 +341,22 @@ protected String applicationPropertyLine(String key, String value) {
return super.applicationPropertyLine(key, value);
}

private void copyDockerFiles() throws Exception {
File docker = new File(BUILD_DIR, "src/main/docker");
@Override
protected void copyDockerFiles(String buildDir) throws Exception {
File docker = new File(buildDir, "src/main/docker");
docker.mkdirs();
// copy files
InputStream is = ExportQuarkus.class.getClassLoader().getResourceAsStream("quarkus-docker/Dockerfile.jvm");
// Deprecated, use Dockerfile instead
IOHelper.copyAndCloseInput(is, new FileOutputStream(new File(docker, "Dockerfile.jvm")));
is = ExportQuarkus.class.getClassLoader().getResourceAsStream("quarkus-docker/Dockerfile.jvm");
IOHelper.copyAndCloseInput(is, new FileOutputStream(new File(docker, "Dockerfile")));
// Deprecated, to be removed in the future
is = ExportQuarkus.class.getClassLoader().getResourceAsStream("quarkus-docker/Dockerfile.legacy-jar");
IOHelper.copyAndCloseInput(is, new FileOutputStream(new File(docker, "Dockerfile.legacy-jar")));
is = ExportQuarkus.class.getClassLoader().getResourceAsStream("quarkus-docker/Dockerfile.native");
IOHelper.copyAndCloseInput(is, new FileOutputStream(new File(docker, "Dockerfile.native")));
// Deprecated, to be removed in the future
is = ExportQuarkus.class.getClassLoader().getResourceAsStream("quarkus-docker/Dockerfile.native-micro");
IOHelper.copyAndCloseInput(is, new FileOutputStream(new File(docker, "Dockerfile.native-micro")));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ public Integer export() throws Exception {
copyGradleWrapper();
}
}

copyDockerFiles(BUILD_DIR);
String appJar = "target" + File.separator + ids[1] + "-" + ids[2] + ".jar";
copyReadme(BUILD_DIR, appJar);
if (cleanExportDir || !exportDir.equals(".")) {
// cleaning current dir can be a bit dangerous so only clean if explicit enabled
// otherwise always clean export-dir to avoid stale data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
# limitations under the License.
#

#
# WARNING: this image is deprecated and won't be used in future versions: use Dockerfile instead
#

####
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@
# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus
#
###
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.8

# Use an UBI image
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.10
# Use a micro image
# FROM quay.io/quarkus/quarkus-micro-image:2.0
WORKDIR /work/
RUN chown 1001 /work \
&& chmod "g+rwX" /work \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
# limitations under the License.
#

#
# WARNING: this image is deprecated and won't be used in future versions: use Dockerfile.native instead
#

####
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
# It uses a micro base image, tuned for Quarkus native executables.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

####
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
#
# Before building the container image run:
#
# ./mvnw package
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/code-with-quarkus-jvm .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus-jvm
#
# If you want to include the debug port into your docker image
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
# when running the container
#
# Then run the container using :
#
# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus-jvm
#
# This image uses the `run-java.sh` script to run the application.
# This scripts computes the command line to execute your Java application, and
# includes memory/GC tuning.
# You can configure the behavior using the following environment properties:
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
# in JAVA_OPTS (example: "-Dsome.property=foo")
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
# used to calculate a default maximal heap memory based on a containers restriction.
# If used in a container without any memory constraints for the container then this
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
# of the container available memory as set here. The default is `50` which means 50%
# of the available memory is used as an upper boundary. You can skip this mechanism by
# setting this value to `0` in which case no `-Xmx` option is added.
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
# is used to calculate a default initial heap memory based on the maximum heap memory.
# If used in a container without any memory constraints for the container then this
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
# is used as the initial heap size. You can skip this mechanism by setting this value
# to `0` in which case no `-Xms` option is added (example: "25")
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
# This is used to calculate the maximum value of the initial heap memory. If used in
# a container without any memory constraints for the container then this option has
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
# here. The default is 4096MB which means the calculated value of `-Xms` never will
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
# when things are happening. This option, if set to true, will set
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
# true").
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
# (example: "20")
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
# (example: "40")
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
# (example: "4")
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
# previous GC times. (example: "90")
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
# contain the necessary JRE command-line options to specify the required GC, which
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
# - HTTPS_PROXY: The location of the https proxy. (example: "[email protected]:8080")
# - HTTP_PROXY: The location of the http proxy. (example: "[email protected]:8080")
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
# accessed directly. (example: "foo.example.com,bar.example.com")
#
###
FROM registry.access.redhat.com/ubi8/openjdk-17:1.20

COPY --chown=185 target/{{ .AppJar }} /deployments/

# Uncomment to expose any given port
# EXPOSE 8080
USER 185
# Uncomment to provide any Java option
# ENV JAVA_OPTS=""
ENV JAVA_APP_JAR="/deployments/{{ .AppJar }}"

ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# How to build and run a Camel application

This project was generated using [Camel Jbang](https://camel.apache.org/manual/camel-jbang.html). Please, refer the the online documentation for learning more about how to configure the export of your Camel application.

This is a brief guide explaining how to build, "containerize" and run your Camel application.

## Build the Maven project

```bash
./mvnw clean package
```

The application could now immediately run:

```bash
java -jar {{ .AppRuntimeJar }}
```

## Create a Docker container

You can create a container image directly from the `src/main/docker` resources. Here you have a precompiled base configuration which can be enhanced with any further required configuration.

```bash
docker build -f src/main/docker/Dockerfile -t {{ .ArtifactId }}:{{ .Version }} .
```

Once the application is published, you can run it directly from the container:

```bash
docker run -it {{ .ArtifactId }}:{{ .Version }}
```
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,22 @@ private String toGAV(Dependency d) {

@ParameterizedTest
@MethodSource("runtimeProvider")
public void shouldGenerateReproducibleBuild(RuntimeType rt) throws Exception {
public void shouldGenerateContent(RuntimeType rt) throws Exception {
Export command = createCommand(rt, new String[] { "classpath:route.yaml" },
"--gav=examples:route:1.0.0", "--dir=" + workingDir, "--quiet");
int exit = command.doCall();

Assertions.assertEquals(0, exit);
// In this test we can validate any generic resource that must be created along the export.
// Exporting once to reduce the time to execute the test and the resource required to test.

// Reproducible build
Model model = readMavenModel();
Assertions.assertNotNull(model.getProperties().get("project.build.outputTimestamp"));
// Dockerfile
Assertions.assertTrue(new File(workingDir + "/src/main/docker", "Dockerfile").exists());
// Readme
Assertions.assertTrue(new File(workingDir, "readme.md").exists());
}

}

0 comments on commit 505dc1c

Please sign in to comment.