1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.micronaut.build;
17
18 import com.github.dockerjava.api.command.BuildImageCmd;
19 import com.google.cloud.tools.jib.api.ImageReference;
20 import com.google.cloud.tools.jib.api.InvalidImageReferenceException;
21 import com.google.common.io.FileWriteMode;
22 import com.google.common.io.Files;
23 import io.micronaut.build.services.ApplicationConfigurationService;
24 import io.micronaut.build.services.DockerService;
25 import io.micronaut.build.services.JibConfigurationService;
26 import io.micronaut.core.util.StringUtils;
27 import org.apache.maven.plugin.MojoExecutionException;
28 import org.apache.maven.plugins.annotations.Mojo;
29 import org.apache.maven.plugins.annotations.ResolutionScope;
30 import org.apache.maven.project.MavenProject;
31
32 import javax.inject.Inject;
33 import java.io.File;
34 import java.io.IOException;
35 import java.nio.charset.Charset;
36 import java.util.Set;
37
38
39
40
41
42
43
44
45
46
47
48
49 @Mojo(name = DockerNativeMojo.DOCKER_NATIVE_PACKAGING, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
50 public class DockerNativeMojo extends AbstractDockerMojo {
51
52 public static final String DOCKER_NATIVE_PACKAGING = "docker-native";
53 public static final String GRAALVM_ARGS = "GRAALVM_ARGS";
54 public static final String MICRONAUT_PARENT = "io.micronaut:micronaut-parent";
55 public static final String MICRONAUT_VERSION = "micronaut.version";
56
57 @SuppressWarnings("CdiInjectionPointsInspection")
58 @Inject
59 public DockerNativeMojo(MavenProject mavenProject, JibConfigurationService jibConfigurationService,
60 ApplicationConfigurationService applicationConfigurationService, DockerService dockerService) {
61 super(mavenProject, jibConfigurationService, applicationConfigurationService, dockerService);
62 }
63
64 @Override
65 public void execute() throws MojoExecutionException {
66 checkJavaVersion();
67 checkGraalVm();
68
69 try {
70 copyDependencies();
71
72 MicronautRuntime runtime = MicronautRuntime.valueOf(micronautRuntime.toUpperCase());
73
74 switch (runtime.getBuildStrategy()) {
75 case LAMBDA:
76 buildDockerNativeLambda();
77 break;
78
79 case ORACLE_FUNCTION:
80 buildOracleCloud();
81 break;
82
83 case DEFAULT:
84 default:
85 buildDockerNative();
86 break;
87 }
88
89
90 } catch (InvalidImageReferenceException iire) {
91 String message = "Invalid image reference "
92 + iire.getInvalidReference()
93 + ", perhaps you should check that the reference is formatted correctly according to " +
94 "https://docs.docker.com/engine/reference/commandline/tag/#extended-description" +
95 "\nFor example, slash-separated name components cannot have uppercase letters";
96 throw new MojoExecutionException(message);
97 } catch (IOException | IllegalArgumentException e) {
98 throw new MojoExecutionException(e.getMessage(), e);
99 }
100 }
101
102 private void checkGraalVm() throws MojoExecutionException {
103 String micronautVersion = mavenProject.getProperties().getProperty(MICRONAUT_VERSION);
104 if (mavenProject.hasParent()) {
105 String ga = mavenProject.getParent().getGroupId() + ":" + mavenProject.getParent().getArtifactId();
106 if (MICRONAUT_PARENT.equals(ga)) {
107 String micronautParentVersion = mavenProject.getModel().getParent().getVersion();
108 if (micronautVersion.equals(micronautParentVersion)) {
109 if (!mavenProject.getInjectedProfileIds().get("io.micronaut:micronaut-parent:" + micronautParentVersion).contains("graalvm")) {
110 String javaVendor = System.getProperty("java.vendor", "");
111 if (javaVendor.toLowerCase().contains("graalvm")) {
112 throw new MojoExecutionException("The [graalvm] profile was not activated automatically because the native-image component is not installed (or not found in your path). Either activate the profile manually (-Pgraalvm) or install the native-image component (gu install native-image), and try again");
113 } else {
114 throw new MojoExecutionException("The [graalvm] profile was not activated automatically because you are not using a GraalVM JDK. Activate the profile manually (-Pgraalvm) and try again");
115 }
116 }
117 } else {
118 String message = String.format("The %s version (%s) differs from the %s property (%s). Please, make sure both refer to the same version", MICRONAUT_PARENT, micronautParentVersion, MICRONAUT_VERSION, micronautVersion);
119 throw new MojoExecutionException(message);
120 }
121 } else {
122 getLog().warn("The parent POM of this project is not set to " + MICRONAUT_PARENT);
123 }
124 } else {
125 getLog().warn("This project has no parent POM defined. To avoid build problems, please set the parent to " + MICRONAUT_PARENT);
126 }
127 }
128
129 private void checkJavaVersion() throws MojoExecutionException {
130 if (javaVersion().getMajorVersion() > 17) {
131 throw new MojoExecutionException("To build native images you must set the Java target byte code level to Java 17 or below");
132 }
133 }
134
135 private void buildDockerNativeLambda() throws IOException {
136 BuildImageCmd buildImageCmd = dockerService.buildImageCmd(DockerfileMojo.DOCKERFILE_AWS_CUSTOM_RUNTIME)
137 .withBuildArg("GRAALVM_VERSION", graalVmVersion())
138 .withBuildArg("GRAALVM_JVM_VERSION", graalVmJvmVersion())
139 .withBuildArg("GRAALVM_ARCH", graalVmArch());
140
141 getLog().info("Using GRAALVM_VERSION: " + graalVmVersion());
142 getLog().info("Using GRAALVM_JVM_VERSION: " + graalVmJvmVersion());
143 getLog().info("Using GRAALVM_ARCH: " + graalVmArch());
144
145
146
147
148 getLog().info("Using CLASS_NAME: " + mainClass);
149 buildImageCmd.withBuildArg("CLASS_NAME", mainClass);
150
151 String graalVmBuildArgs = getGraalVmBuildArgs();
152 if (graalVmBuildArgs != null && !graalVmBuildArgs.isEmpty()) {
153 getLog().info("Using GRAALVM_ARGS: " + graalVmBuildArgs);
154 buildImageCmd = buildImageCmd.withBuildArg(GRAALVM_ARGS, graalVmBuildArgs);
155 }
156
157 String imageId = dockerService.buildImage(buildImageCmd);
158 File functionZip = dockerService.copyFromContainer(imageId, "/function/function.zip");
159 getLog().info("AWS Lambda Custom Runtime ZIP: " + functionZip.getPath());
160 }
161
162 private void buildDockerNative() throws IOException, InvalidImageReferenceException {
163 String dockerfileName = DockerfileMojo.DOCKERFILE_NATIVE;
164 if (staticNativeImage) {
165 getLog().info("Generating a static native image");
166 dockerfileName = DockerfileMojo.DOCKERFILE_NATIVE_STATIC;
167 } else if (baseImageRun.contains("distroless")) {
168 getLog().info("Generating a mostly static native image");
169 dockerfileName = DockerfileMojo.DOCKERFILE_NATIVE_DISTROLESS;
170 }
171
172 buildDockerfile(dockerfileName, true);
173 }
174
175 private void buildOracleCloud() throws IOException, InvalidImageReferenceException {
176 buildDockerfile(DockerfileMojo.DOCKERFILE_NATIVE_ORACLE_CLOUD, false);
177 }
178
179 private void buildDockerfile(String dockerfileName, boolean passClassName) throws IOException, InvalidImageReferenceException {
180 Set<String> tags = getTags();
181 for (String tag : tags) {
182 ImageReference.parse(tag);
183 }
184
185 String from = getFrom();
186 String port = getPort();
187 getLog().info("Exposing port: " + port);
188
189 File dockerfile = dockerService.loadDockerfileAsResource(dockerfileName);
190
191 if (appArguments != null && !appArguments.isEmpty()) {
192 getLog().info("Using application arguments: " + appArguments);
193 Files.asCharSink(dockerfile, Charset.defaultCharset(), FileWriteMode.APPEND).write(System.lineSeparator() + getCmd());
194 }
195
196 BuildImageCmd buildImageCmd = dockerService.buildImageCmd()
197 .withDockerfile(dockerfile)
198 .withTags(getTags())
199 .withBuildArg("BASE_IMAGE", from)
200 .withBuildArg("PORT", port);
201
202 getLog().info("Using BASE_IMAGE: " + from);
203 if (StringUtils.isNotEmpty(baseImageRun) && !staticNativeImage) {
204 getLog().info("Using BASE_IMAGE_RUN: " + baseImageRun);
205 buildImageCmd.withBuildArg("BASE_IMAGE_RUN", baseImageRun);
206 }
207
208 if (baseImageRun.contains("alpine-glibc")) {
209 buildImageCmd.withBuildArg("EXTRA_CMD", "apk update && apk add libstdc++");
210 } else {
211 buildImageCmd.withBuildArg("EXTRA_CMD", "");
212 }
213
214 if (passClassName) {
215 getLog().info("Using CLASS_NAME: " + mainClass);
216 buildImageCmd = buildImageCmd.withBuildArg("CLASS_NAME", mainClass);
217 }
218
219 String graalVmBuildArgs = getGraalVmBuildArgs();
220 if (baseImageRun.contains("distroless") && !graalVmBuildArgs.contains(MOSTLY_STATIC_NATIVE_IMAGE_GRAALVM_FLAG)) {
221 graalVmBuildArgs = MOSTLY_STATIC_NATIVE_IMAGE_GRAALVM_FLAG + " " + graalVmBuildArgs;
222 }
223
224 if (graalVmBuildArgs != null && !graalVmBuildArgs.isEmpty()) {
225 getLog().info("Using GRAALVM_ARGS: " + graalVmBuildArgs);
226 buildImageCmd = buildImageCmd.withBuildArg(GRAALVM_ARGS, graalVmBuildArgs);
227 }
228
229 dockerService.buildImage(buildImageCmd);
230 }
231
232 }