1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.micronaut.build.jib;
17
18 import com.google.cloud.tools.jib.api.buildplan.*;
19 import com.google.cloud.tools.jib.buildplan.UnixPathParser;
20 import com.google.cloud.tools.jib.maven.extension.JibMavenPluginExtension;
21 import com.google.cloud.tools.jib.maven.extension.MavenData;
22 import com.google.cloud.tools.jib.plugins.extension.ExtensionLogger;
23 import io.micronaut.build.AbstractDockerMojo;
24 import io.micronaut.build.MicronautRuntime;
25 import io.micronaut.build.services.ApplicationConfigurationService;
26 import io.micronaut.build.services.JibConfigurationService;
27
28 import java.util.*;
29
30
31
32
33
34
35
36 public class JibMicronautExtension implements JibMavenPluginExtension<Void> {
37
38 public static final String DEFAULT_BASE_IMAGE = "openjdk:17-alpine";
39
40 @Override
41 public Optional<Class<Void>> getExtraConfigType() {
42 return Optional.empty();
43 }
44
45 @Override
46 public ContainerBuildPlan extendContainerBuildPlan(ContainerBuildPlan buildPlan, Map<String, String> properties,
47 Optional<Void> extraConfig, MavenData mavenData,
48 ExtensionLogger logger) {
49
50 ContainerBuildPlan.Builder builder = buildPlan.toBuilder();
51 MicronautRuntime runtime = MicronautRuntime.valueOf(mavenData.getMavenProject().getProperties().getProperty(MicronautRuntime.PROPERTY, "none").toUpperCase());
52
53 JibConfigurationService jibConfigurationService = new JibConfigurationService(mavenData.getMavenProject());
54 String from = jibConfigurationService.getFromImage().orElse(DEFAULT_BASE_IMAGE);
55
56 builder.setBaseImage(from);
57
58 ApplicationConfigurationService applicationConfigurationService = new ApplicationConfigurationService(mavenData.getMavenProject());
59 try {
60 int port = Integer.parseInt(applicationConfigurationService.getServerPort());
61 if (port > 0) {
62 logger.log(ExtensionLogger.LogLevel.LIFECYCLE, "Exposing port: " + port);
63 builder.addExposedPort(Port.tcp(port));
64 }
65 } catch (NumberFormatException e) {
66
67 logger.log(ExtensionLogger.LogLevel.LIFECYCLE, "Dynamically resolved port present. Ensure the port is correctly exposed in the <container> configuration. See https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin#example for an example.");
68 }
69
70 switch (runtime.getBuildStrategy()) {
71 case ORACLE_FUNCTION:
72 List<? extends LayerObject> originalLayers = buildPlan.getLayers();
73 builder.setLayers(Collections.emptyList());
74
75 for (LayerObject layer : originalLayers) {
76 builder.addLayer(remapLayer(layer));
77 }
78
79 List<String> cmd = jibConfigurationService.getArgs();
80 if (cmd.isEmpty()) {
81 cmd = Collections.singletonList("io.micronaut.oraclecloud.function.http.HttpFunction::handleRequest");
82 }
83
84 String projectFnVersion = determineProjectFnVersion(System.getProperty("java.version"));
85 builder.setBaseImage("fnproject/fn-java-fdk:" + projectFnVersion)
86 .setWorkingDirectory(AbsoluteUnixPath.get(jibConfigurationService.getWorkingDirectory().orElse("/function")))
87 .setEntrypoint(buildProjectFnEntrypoint(projectFnVersion))
88 .setCmd(cmd);
89 break;
90
91 case LAMBDA:
92 List<String> entrypoint = buildPlan.getEntrypoint();
93 Objects.requireNonNull(entrypoint).set(entrypoint.size() - 1, "io.micronaut.function.aws.runtime.MicronautLambdaRuntime");
94 builder.setEntrypoint(entrypoint);
95 break;
96
97 default:
98
99 }
100 return builder.build();
101 }
102
103 public static List<String> buildProjectFnEntrypoint(String projectFnVersion) {
104 List<String> entrypoint = new ArrayList<>(9);
105 if (AbstractDockerMojo.LATEST_TAG.equals(projectFnVersion)) {
106 entrypoint.add("/usr/java/latest/bin/java");
107 entrypoint.add("-XX:+UnlockExperimentalVMOptions");
108 entrypoint.add("-XX:+UseCGroupMemoryLimitForHeap");
109 entrypoint.add("-XX:-UsePerfData");
110 entrypoint.add("-XX:MaxRAMFraction=2");
111 entrypoint.add("-XX:+UseSerialGC");
112 entrypoint.add("-Xshare:on");
113 entrypoint.add("-Djava.library.path=/function/runtime/lib");
114 entrypoint.add("-cp");
115 entrypoint.add("/function/app/classes:/function/app/libs/*:/function/app/resources:/function/runtime/*");
116 entrypoint.add("com.fnproject.fn.runtime.EntryPoint");
117 } else {
118 entrypoint.add("/usr/java/latest/bin/java");
119 entrypoint.add("-XX:-UsePerfData");
120 entrypoint.add("-XX:+UseSerialGC");
121 entrypoint.add("-Xshare:on");
122 entrypoint.add("-Djava.awt.headless=true");
123 entrypoint.add("-Djava.library.path=/function/runtime/lib");
124 entrypoint.add("-cp");
125 entrypoint.add("/function/app/classes:/function/app/libs/*:/function/app/resources:/function/runtime/*");
126 entrypoint.add("com.fnproject.fn.runtime.EntryPoint");
127 }
128 return entrypoint;
129 }
130
131 public static String determineProjectFnVersion(String javaVersion) {
132 int majorVersion = Integer.parseInt(javaVersion.split("\\.")[0]);
133 if (majorVersion == 1) {
134 majorVersion = Integer.parseInt(javaVersion.split("\\.")[1]);
135 }
136 if (majorVersion >= 17) {
137 return "jre17-latest";
138 } else if (majorVersion >= 11) {
139 return "jre11-latest";
140 } else {
141 return AbstractDockerMojo.LATEST_TAG;
142 }
143 }
144
145 private LayerObject remapLayer(LayerObject layerObject) {
146 FileEntriesLayer originalLayer = (FileEntriesLayer) layerObject;
147 FileEntriesLayer.Builder builder = FileEntriesLayer.builder().setName(originalLayer.getName());
148 for (FileEntry originalEntry : originalLayer.getEntries()) {
149 builder.addEntry(remapEntry(originalEntry, layerObject.getName()));
150 }
151
152 return builder.build();
153 }
154
155 private FileEntry remapEntry(FileEntry originalEntry, String layerName) {
156 List<String> pathComponents = UnixPathParser.parse(originalEntry.getExtractionPath().toString());
157 AbsoluteUnixPath newPath;
158 if (layerName.contains("dependencies")) {
159 newPath = AbsoluteUnixPath.get("/function/app/libs/" + pathComponents.get(pathComponents.size() - 1));
160 } else {
161
162 newPath = AbsoluteUnixPath.get("/function" + originalEntry.getExtractionPath());
163 }
164
165 return new FileEntry(originalEntry.getSourceFile(), newPath, originalEntry.getPermissions(),
166 originalEntry.getModificationTime(), originalEntry.getOwnership());
167 }
168
169 }