1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.micronaut.build;
17
18 import io.micronaut.build.jib.JibMicronautExtension;
19 import io.micronaut.build.services.ApplicationConfigurationService;
20 import io.micronaut.build.services.DockerService;
21 import io.micronaut.build.services.JibConfigurationService;
22 import org.apache.maven.plugin.MojoExecutionException;
23 import org.apache.maven.plugins.annotations.Mojo;
24 import org.apache.maven.plugins.annotations.ResolutionScope;
25 import org.apache.maven.project.MavenProject;
26
27 import javax.inject.Inject;
28 import java.io.File;
29 import java.io.IOException;
30 import java.nio.file.Files;
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.Optional;
34 import java.util.stream.Collectors;
35
36
37
38
39
40
41
42
43
44
45 @Mojo(name = "dockerfile", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
46 public class DockerfileMojo extends AbstractDockerMojo {
47
48 public static final String DOCKERFILE = "Dockerfile";
49 public static final String DOCKERFILE_AWS_CUSTOM_RUNTIME = "DockerfileAwsCustomRuntime";
50 public static final String DOCKERFILE_NATIVE = "DockerfileNative";
51 public static final String DOCKERFILE_CRAC = "DockerfileCrac";
52 public static final String DOCKERFILE_CRAC_CHECKPOINT = "DockerfileCracCheckpoint";
53 public static final String DOCKERFILE_CRAC_CHECKPOINT_FILE = "Dockerfile.crac.checkpoint";
54 public static final String DOCKERFILE_NATIVE_DISTROLESS = "DockerfileNativeDistroless";
55 public static final String DOCKERFILE_NATIVE_STATIC = "DockerfileNativeStatic";
56 public static final String DOCKERFILE_NATIVE_ORACLE_CLOUD = "DockerfileNativeOracleCloud";
57
58 @Inject
59 public DockerfileMojo(MavenProject mavenProject, DockerService dockerService, JibConfigurationService jibConfigurationService,
60 ApplicationConfigurationService applicationConfigurationService) {
61 super(mavenProject, jibConfigurationService, applicationConfigurationService, dockerService);
62 }
63
64 @Override
65 public void execute() throws MojoExecutionException {
66 MicronautRuntime runtime = MicronautRuntime.valueOf(micronautRuntime.toUpperCase());
67 Packaging packaging = Packaging.of(mavenProject.getPackaging());
68 try {
69 copyDependencies();
70 Optional<File> dockerfile;
71
72 switch (packaging) {
73 case DOCKER_NATIVE:
74 dockerfile = buildDockerfileNative(runtime);
75 break;
76
77 case DOCKER:
78 dockerfile = buildDockerfile(runtime);
79 break;
80
81 case DOCKER_CRAC:
82 dockerfile = buildCracDockerfile(runtime);
83 break;
84
85 default:
86 throw new MojoExecutionException("Packaging is set to [" + packaging + "]. To generate a Dockerfile, set the packaging to either [" + Packaging.DOCKER.id() + "] or [" + Packaging.DOCKER_NATIVE.id() + "]");
87 }
88
89 dockerfile.ifPresent(file -> getLog().info("Dockerfile written to: " + file.getAbsolutePath()));
90
91 } catch (IOException e) {
92 throw new MojoExecutionException(e.getMessage(), e);
93 }
94 }
95
96 private Optional<File> buildDockerfile(MicronautRuntime runtime) throws IOException {
97 File dockerfile;
98 switch (runtime.getBuildStrategy()) {
99 case ORACLE_FUNCTION:
100 dockerfile = dockerService.loadDockerfileAsResource("DockerfileOracleCloud");
101 processOracleFunctionDockerfile(dockerfile);
102 break;
103 case LAMBDA:
104 case DEFAULT:
105 default:
106 dockerfile = dockerService.loadDockerfileAsResource(DOCKERFILE);
107 processDockerfile(dockerfile);
108 break;
109 }
110 return Optional.ofNullable(dockerfile);
111 }
112
113 private Optional<File> buildCracDockerfile(MicronautRuntime runtime) throws IOException, MojoExecutionException {
114 File dockerfile;
115 switch (runtime.getBuildStrategy()) {
116 case ORACLE_FUNCTION:
117 throw new MojoExecutionException("Oracle Functions are currently unsupported");
118 case LAMBDA:
119 throw new MojoExecutionException("Lambda Functions are currently unsupported");
120 case DEFAULT:
121 default:
122 dockerfile = dockerService.loadDockerfileAsResource(DOCKERFILE_CRAC_CHECKPOINT, DOCKERFILE_CRAC_CHECKPOINT_FILE);
123 processDockerfile(dockerfile);
124 dockerfile = dockerService.loadDockerfileAsResource(DOCKERFILE_CRAC);
125 processDockerfile(dockerfile);
126 break;
127 }
128 return Optional.ofNullable(dockerfile);
129 }
130
131 private void processOracleFunctionDockerfile(File dockerfile) throws IOException {
132 if (dockerfile != null) {
133 List<String> allLines = Files.readAllLines(dockerfile.toPath());
134 String projectFnVersion = JibMicronautExtension.determineProjectFnVersion(System.getProperty("java.version"));
135 allLines.add(0, allLines.remove(0) + projectFnVersion);
136 String entrypoint = JibMicronautExtension.buildProjectFnEntrypoint(projectFnVersion)
137 .stream()
138 .map(s -> "\"" + s + "\"")
139 .collect(Collectors.joining(", "));
140
141 allLines.add("ENTRYPOINT [" + entrypoint + "]");
142
143 Files.write(dockerfile.toPath(), allLines);
144 }
145 }
146
147 private Optional<File> buildDockerfileNative(MicronautRuntime runtime) throws IOException {
148 File dockerfile;
149 switch (runtime.getBuildStrategy()) {
150 case LAMBDA:
151 dockerfile = dockerService.loadDockerfileAsResource(DOCKERFILE_AWS_CUSTOM_RUNTIME);
152 break;
153
154 case ORACLE_FUNCTION:
155 dockerfile = dockerService.loadDockerfileAsResource(DOCKERFILE_NATIVE_ORACLE_CLOUD);
156 break;
157
158 case DEFAULT:
159 default:
160 String dockerfileName = DOCKERFILE_NATIVE;
161 if (staticNativeImage) {
162 getLog().info("Generating a static native image");
163 dockerfileName = DockerfileMojo.DOCKERFILE_NATIVE_STATIC;
164 } else if (baseImageRun.contains("distroless")) {
165 getLog().info("Generating a mostly static native image");
166 dockerfileName = DockerfileMojo.DOCKERFILE_NATIVE_DISTROLESS;
167 }
168 dockerfile = dockerService.loadDockerfileAsResource(dockerfileName);
169 break;
170 }
171 processDockerfile(dockerfile);
172 return Optional.ofNullable(dockerfile);
173
174 }
175
176 private void processDockerfile(File dockerfile) throws IOException {
177 if (dockerfile != null) {
178 List<String> allLines = Files.readAllLines(dockerfile.toPath());
179 List<String> result = new ArrayList<>();
180
181 for (String line : allLines) {
182 if (!line.startsWith("ARG")) {
183 if (line.contains("BASE_IMAGE_RUN")) {
184 result.add(line.replace("${BASE_IMAGE_RUN}", baseImageRun));
185 } else if (line.contains("BASE_IMAGE")) {
186 result.add(line.replace("${BASE_IMAGE}", getFrom()));
187 } else if (line.contains("EXTRA_CMD")) {
188 if (baseImageRun.contains("alpine-glibc")) {
189 result.add("RUN apk update && apk add libstdc++");
190 } else {
191 result.add("");
192 }
193 } else if (line.contains("GRAALVM_") || line.contains("CLASS_NAME")) {
194 String graalVmBuildArgs = getGraalVmBuildArgs();
195 if (baseImageRun.contains("distroless") && !graalVmBuildArgs.contains(MOSTLY_STATIC_NATIVE_IMAGE_GRAALVM_FLAG)) {
196 graalVmBuildArgs = MOSTLY_STATIC_NATIVE_IMAGE_GRAALVM_FLAG + " " + graalVmBuildArgs;
197 }
198
199 result.add(line
200 .replace("${GRAALVM_VERSION}", graalVmVersion())
201 .replace("${GRAALVM_JVM_VERSION}", graalVmJvmVersion())
202 .replace("${GRAALVM_ARCH}", graalVmArch())
203 .replace("${GRAALVM_ARGS}", graalVmBuildArgs)
204 .replace("${CLASS_NAME}", mainClass)
205 );
206 } else if (line.contains("PORT")) {
207 result.add(line.replace("${PORT}", getPort()));
208 } else {
209 result.add(line);
210 }
211 }
212 }
213
214 if (appArguments != null && !appArguments.isEmpty()) {
215 getLog().info("Using application arguments: " + appArguments);
216 result.add(getCmd());
217 }
218
219 Files.write(dockerfile.toPath(), result);
220 }
221 }
222 }