001/* 002 * Copyright 2017-2022 original authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.micronaut.maven; 017 018import com.google.cloud.tools.jib.api.ImageReference; 019import com.google.cloud.tools.jib.api.InvalidImageReferenceException; 020import com.google.common.io.FileWriteMode; 021import io.micronaut.core.util.StringUtils; 022import io.micronaut.maven.core.MicronautRuntime; 023import io.micronaut.maven.jib.JibConfigurationService; 024import io.micronaut.maven.jib.JibMicronautExtension; 025import io.micronaut.maven.services.ApplicationConfigurationService; 026import io.micronaut.maven.services.DockerService; 027import org.apache.maven.artifact.Artifact; 028import org.apache.maven.artifact.versioning.ArtifactVersion; 029import org.apache.maven.artifact.versioning.DefaultArtifactVersion; 030import org.apache.maven.execution.MavenSession; 031import org.apache.maven.plugin.MojoExecutionException; 032import org.apache.maven.plugin.MojoExecution; 033import org.apache.maven.plugin.PluginParameterExpressionEvaluator; 034import org.apache.maven.plugins.annotations.Parameter; 035import org.apache.maven.project.MavenProject; 036 037import java.io.File; 038import java.io.IOException; 039import java.net.URI; 040import java.net.URISyntaxException; 041import java.nio.charset.Charset; 042import java.nio.file.Files; 043import java.nio.file.StandardCopyOption; 044import java.util.ArrayList; 045import java.util.Arrays; 046import java.util.HashSet; 047import java.util.List; 048import java.util.Map; 049import java.util.NavigableSet; 050import java.util.Optional; 051import java.util.Set; 052import java.util.TreeSet; 053import java.util.stream.Collectors; 054 055import static io.micronaut.maven.services.ApplicationConfigurationService.DEFAULT_PORT; 056 057/** 058 * Abstract base class for mojos related to Docker files and builds. 059 * 060 * @author Álvaro Sánchez-Mariscal 061 * @author Iván López 062 * @since 1.1 063 */ 064public abstract class AbstractDockerMojo extends AbstractMicronautMojo { 065 066 public static final String LATEST_TAG = "latest"; 067 public static final String DEFAULT_BASE_IMAGE_GRAALVM_RUN = "cgr.dev/chainguard/wolfi-base@sha256:52e71f61c6afd1f8d2625cff4465d8ecee156668ca665f7e9c582d1cc914eb6a"; 068 public static final String DEFAULT_BASE_IMAGE_GRAALVM_BUILD = "container-registry.oracle.com/graalvm/native-image"; 069 public static final String MOSTLY_STATIC_NATIVE_IMAGE_GRAALVM_FLAG = "-H:+StaticExecutableWithDynamicLibC"; 070 public static final String ARM_ARCH = "aarch64"; 071 public static final String X86_64_ARCH = "x64"; 072 public static final String ORACLE_CLOUD_FUNCTION_DEFAULT_CMD = "CMD [\"io.micronaut.oraclecloud.function.http.HttpFunction::handleRequest\"]"; 073 public static final String GDS_DOWNLOAD_URL = "https://gds.oracle.com/download/graal/%s/latest-gftc/graalvm-jdk-%s_linux-%s_bin.tar.gz"; 074 public static final String LAMBDA_BOOTSTRAP_DOCKER_COMMAND_PLACEHOLDER = "${LAMBDA_BOOTSTRAP_DOCKER_COMMAND}"; 075 static final String JIB_FROM_IMAGE_PROPERTY = "jib.from.image"; 076 private static final String DEPENDENCY_DIRECTORY = "dependency"; 077 private static final String RELEASE_DEPENDENCY_DIRECTORY = "release"; 078 private static final String SNAPSHOT_DEPENDENCY_DIRECTORY = "snapshot"; 079 private static final NavigableSet<Integer> GRAALVM_VERSIONS = new TreeSet<>(Set.of(25)); 080 private static final List<String> DEFAULT_LAMBDA_BOOTSTRAP_ARGUMENTS = List.of( 081 "-XX:MaximumHeapSizePercent=80", 082 "-Dio.netty.allocator.numDirectArenas=0", 083 "-Dio.netty.noPreferDirect=true", 084 "-Djava.library.path=$(pwd)" 085 ); 086 087 protected final MavenProject mavenProject; 088 protected final MavenSession mavenSession; 089 protected final JibConfigurationService jibConfigurationService; 090 protected final ApplicationConfigurationService applicationConfigurationService; 091 protected final DockerService dockerService; 092 protected final PluginParameterExpressionEvaluator expressionEvaluator; 093 094 095 /** 096 * Additional arguments that will be passed to the <code>native-image</code> executable. Note that this will only 097 * be used when using a packaging of type <code>docker-native</code>. For <code>native-image</code> packaging 098 * you should use the 099 * <a href="https://www.graalvm.org/reference-manual/native-image/NativeImageMavenPlugin/#maven-plugin-customization"> 100 * Native Image Maven Plugin 101 * </a> configuration options. 102 */ 103 @Parameter(property = "micronaut.native-image.args") 104 protected List<String> nativeImageBuildArgs; 105 106 /** 107 * List of additional arguments that will be passed to the application. 108 */ 109 @Parameter(property = RunMojo.MN_APP_ARGS) 110 protected List<String> appArguments; 111 112 /** 113 * Additional arguments that will be appended to the generated AWS Lambda native bootstrap command. 114 * 115 * @since 5.0.0 116 */ 117 @Parameter(property = "micronaut.lambda.bootstrap.args") 118 protected List<String> lambdaBootstrapArguments; 119 120 /** 121 * The main class of the application, as defined in the 122 * <a href="https://www.mojohaus.org/exec-maven-plugin/java-mojo.html#mainClass">Exec Maven Plugin</a>. 123 */ 124 @Parameter(defaultValue = RunMojo.EXEC_MAIN_CLASS, required = true) 125 protected String mainClass; 126 127 /** 128 * Whether to produce a static native image when using <code>docker-native</code> packaging. 129 */ 130 @Parameter(defaultValue = "false", property = "micronaut.native-image.static") 131 protected Boolean staticNativeImage; 132 133 /** 134 * The target runtime of the application. 135 */ 136 @Parameter(property = MicronautRuntime.PROPERTY, defaultValue = "NONE") 137 protected String micronautRuntime; 138 139 /** 140 * The Docker image used to run the native image. 141 * 142 * @since 1.2 143 */ 144 @Parameter(property = "micronaut.native-image.base-image-run", defaultValue = DEFAULT_BASE_IMAGE_GRAALVM_RUN) 145 protected String baseImageRun; 146 147 /** 148 * The builder-stage base image used to build the native image for docker-native packaging variants. 149 * 150 * @since 5.0.0 151 */ 152 @Parameter(property = "micronaut.native-image.base-image") 153 protected String baseImage; 154 155 /** 156 * The version of Oracle Linux to use as a native-compile base when building a native image inside a Docker container. 157 */ 158 @Parameter(property = "micronaut.native-image.ol.version", defaultValue = "ol9") 159 protected String oracleLinuxVersion; 160 161 /** 162 * Networking mode for the RUN instructions during build. 163 * 164 * @since 4.0.0 165 */ 166 @Parameter(property = "docker.networkMode") 167 protected String networkMode; 168 169 /** 170 * <p> 171 * Jib goal used to build Docker images for {@code docker} packaging. 172 * </p> 173 * <p> 174 * Defaults to {@code dockerBuild}. Set it to {@code buildTar} or {@code build} to avoid talking to a local Docker daemon during {@code package}. 175 * </p> 176 * 177 * @since 5.0.0 178 */ 179 @Parameter(property = "jib.buildGoal", defaultValue = "dockerBuild") 180 protected String jibBuildGoal; 181 182 protected AbstractDockerMojo(MavenProject mavenProject, JibConfigurationService jibConfigurationService, 183 ApplicationConfigurationService applicationConfigurationService, 184 DockerService dockerService, MavenSession mavenSession, MojoExecution mojoExecution) { 185 this.mavenProject = mavenProject; 186 this.mavenSession = mavenSession; 187 this.jibConfigurationService = jibConfigurationService; 188 this.applicationConfigurationService = applicationConfigurationService; 189 this.dockerService = dockerService; 190 this.expressionEvaluator = new PluginParameterExpressionEvaluator(mavenSession, mojoExecution); 191 } 192 193 /** 194 * @return the Java version from either the <code>maven.compiler.target</code> property or the <code>java.version</code> property. 195 */ 196 protected ArtifactVersion javaVersion() { 197 return new DefaultArtifactVersion(getJdkVersion()); 198 } 199 200 private String getJdkVersion() { 201 var releaseVersion = getPropertyValue(mavenProject, "maven.compiler.release"); 202 var targetVersion = getPropertyValue(mavenProject, "maven.compiler.target"); 203 return releaseVersion.or(() -> targetVersion).orElseGet(() -> System.getProperty("java.version")); 204 } 205 206 private static Optional<String> getPropertyValue(MavenProject project, String propertName) { 207 var systemProperty = Optional.of(propertName).map(System::getProperty); 208 var properties = project.getProperties(); 209 var projectProperty = Optional.of(propertName).map(properties::getProperty); 210 return systemProperty.or(() -> projectProperty); 211 } 212 213 /** 214 * @return the JVM version to use for GraalVM. 215 */ 216 protected String graalVmJvmVersion() { 217 return Integer.toString(resolveGraalVersion()); 218 } 219 220 /** 221 * @return the GraalVM download URL depending on the Java version. 222 */ 223 protected String graalVmDownloadUrl() { 224 Integer version = resolveGraalVersion(); 225 226 return GDS_DOWNLOAD_URL.formatted(version, version, graalVmArch()); 227 } 228 229 private Integer resolveGraalVersion() { 230 int target = javaVersion().getMajorVersion(); 231 Integer version = GRAALVM_VERSIONS.floor(target); 232 233 return version != null ? version : GRAALVM_VERSIONS.first(); 234 } 235 236 /** 237 * @return the OS architecture to use for GraalVM depending on the <code>os.arch</code> system property. 238 */ 239 protected String graalVmArch() { 240 return isArm() ? ARM_ARCH : X86_64_ARCH; 241 } 242 243 /** 244 * @return the base FROM image for the native image. 245 */ 246 protected String getFrom() { 247 return getJibFromImageSystemProperty() 248 .or(() -> Optional.ofNullable(baseImage).filter(StringUtils::hasText)) 249 .or(() -> getFromImage().filter(StringUtils::hasText)) 250 .orElse(DEFAULT_BASE_IMAGE_GRAALVM_BUILD + ":" + graalVmTag(graalVmJvmVersion(), staticNativeImage, oracleLinuxVersion)); 251 } 252 253 /** 254 * @param graalVmJvmVersion the JVM version string 255 * @param staticNativeImage whether to produce a static native image 256 * @param oracleLinuxVersion the Oracle Linux version to use 257 * @return the GraalVM Docker image tag based on the provided parameters 258 */ 259 protected String graalVmTag(String graalVmJvmVersion, Boolean staticNativeImage, String oracleLinuxVersion) { 260 String suffix = Boolean.TRUE.equals(staticNativeImage) 261 ? "-muslib" + (StringUtils.hasText(oracleLinuxVersion) ? "-" + oracleLinuxVersion : "") 262 : (StringUtils.hasText(oracleLinuxVersion) ? "-" + oracleLinuxVersion : ""); 263 return graalVmJvmVersion + suffix; 264 } 265 266 /** 267 * Check os.arch against known ARM architecture identifiers. 268 * 269 * @return true if we think we're running on an arm JDK 270 */ 271 protected boolean isArm() { 272 return switch (System.getProperty("os.arch")) { 273 case ARM_ARCH, "arm64" -> true; 274 default -> false; 275 }; 276 } 277 278 /** 279 * @return the base image from the jib configuration (if any). 280 */ 281 protected Optional<String> getFromImage() { 282 return jibConfigurationService.getFromImage(); 283 } 284 285 /** 286 * @return the base image from the Jib system property override, if any. 287 */ 288 protected Optional<String> getJibFromImageSystemProperty() { 289 return Optional.ofNullable(System.getProperty(JIB_FROM_IMAGE_PROPERTY)) 290 .filter(StringUtils::hasText); 291 } 292 293 /** 294 * @return the Docker image tags by looking at the Jib plugin configuration. 295 */ 296 protected Set<String> getTags() { 297 var tags = new HashSet<String>(); 298 Optional<String> toImageOptional = jibConfigurationService.getToImage(); 299 String imageName = mavenProject.getArtifactId(); 300 if (toImageOptional.isPresent()) { 301 String toImage = toImageOptional.get(); 302 if (toImage.contains(":")) { 303 tags.add(toImage); 304 imageName = toImageOptional.get().split(":")[0]; 305 } else { 306 tags.add(toImage + ":" + LATEST_TAG); 307 imageName = toImage; 308 } 309 } else { 310 tags.add(imageName + ":" + LATEST_TAG); 311 } 312 for (String tag : jibConfigurationService.getTags()) { 313 if (LATEST_TAG.equals(tag) && tags.stream().anyMatch(t -> t.contains(LATEST_TAG))) { 314 continue; 315 } 316 tags.add(String.format("%s:%s", imageName, tag)); 317 } 318 return tags.stream() 319 .map(this::evaluateExpression) 320 .collect(Collectors.toSet()); 321 } 322 323 private String evaluateExpression(String expression) { 324 try { 325 return expressionEvaluator.evaluate(expression, String.class).toString(); 326 } catch (Exception e) { 327 return expression; 328 } 329 } 330 331 /** 332 * @return the application ports to expose by looking at the Jib configuration or the application configuration. 333 */ 334 protected String getPorts() { 335 return jibConfigurationService.getPorts().orElseGet(() -> { 336 String port = applicationConfigurationService.getServerPort(); 337 return "-1".equals(port) ? DEFAULT_PORT : port; 338 }); 339 } 340 341 /** 342 * Copy project dependencies to a <code>target/dependency</code> directory. 343 */ 344 protected void copyDependencies() throws IOException { 345 var imageClasspathScopes = Arrays.asList(Artifact.SCOPE_COMPILE, Artifact.SCOPE_RUNTIME); 346 var target = new File(mavenProject.getBuild().getDirectory(), DEPENDENCY_DIRECTORY).toPath(); 347 Files.createDirectories(target); 348 Files.createDirectories(target.resolve(RELEASE_DEPENDENCY_DIRECTORY)); 349 Files.createDirectories(target.resolve(SNAPSHOT_DEPENDENCY_DIRECTORY)); 350 for (Artifact dependency : mavenProject.getArtifacts()) { 351 if (!imageClasspathScopes.contains(dependency.getScope())) { 352 continue; 353 } 354 var dependencyFile = dependency.getFile().toPath(); 355 var dependencyName = dependency.getFile().getName(); 356 var layeredPath = target.resolve(dependencyLayerDirectory(dependency)).resolve(dependencyName); 357 Files.copy(dependencyFile, layeredPath, StandardCopyOption.REPLACE_EXISTING); 358 Files.copy(dependencyFile, target.resolve(dependencyName), StandardCopyOption.REPLACE_EXISTING); 359 } 360 } 361 362 private static String dependencyLayerDirectory(Artifact dependency) { 363 return dependency.isSnapshot() ? SNAPSHOT_DEPENDENCY_DIRECTORY : RELEASE_DEPENDENCY_DIRECTORY; 364 } 365 366 /** 367 * @return the Docker CMD command. 368 */ 369 protected String getCmd() throws MojoExecutionException { 370 var escapedArguments = new ArrayList<String>(appArguments.size()); 371 for (String argument : appArguments) { 372 escapedArguments.add(jsonStringLiteral("mn.app.args", argument)); 373 } 374 return "CMD [" + String.join(", ", escapedArguments) + "]"; 375 } 376 377 /** 378 * @return the generated AWS Lambda bootstrap command. 379 */ 380 protected String getLambdaBootstrapCommand() throws MojoExecutionException { 381 var command = new StringBuilder("./func"); 382 for (String bootstrapArgument : DEFAULT_LAMBDA_BOOTSTRAP_ARGUMENTS) { 383 command.append(' ').append(bootstrapArgument); 384 } 385 if (lambdaBootstrapArguments != null && !lambdaBootstrapArguments.isEmpty()) { 386 for (String lambdaBootstrapArgument : lambdaBootstrapArguments) { 387 command.append(' ').append(escapeBootstrapArgument(lambdaBootstrapArgument)); 388 } 389 } 390 return command.toString(); 391 } 392 393 /** 394 * Applies the generated AWS Lambda bootstrap script to a dockerfile template. 395 * 396 * @param dockerfile the docker file 397 */ 398 protected void lambdaBootstrapCommand(File dockerfile) throws IOException, MojoExecutionException { 399 if (dockerfile == null) { 400 return; 401 } 402 String lambdaBootstrapDockerCommand = getLambdaBootstrapDockerCommand(); 403 if (lambdaBootstrapArguments != null && !lambdaBootstrapArguments.isEmpty()) { 404 getLog().info("Using AWS Lambda bootstrap arguments: " + lambdaBootstrapArguments); 405 } 406 var allLines = Files.readAllLines(dockerfile.toPath()); 407 var result = new ArrayList<String>(allLines.size()); 408 for (String line : allLines) { 409 if (line.contains(LAMBDA_BOOTSTRAP_DOCKER_COMMAND_PLACEHOLDER)) { 410 result.add(line.replace(LAMBDA_BOOTSTRAP_DOCKER_COMMAND_PLACEHOLDER, lambdaBootstrapDockerCommand)); 411 } else { 412 result.add(line); 413 } 414 } 415 Files.write(dockerfile.toPath(), result); 416 } 417 418 private String getLambdaBootstrapDockerCommand() throws MojoExecutionException { 419 var quotedLines = new ArrayList<String>(); 420 for (String line : List.of("#!/bin/sh", "set -euo pipefail", getLambdaBootstrapCommand())) { 421 quotedLines.add(quoteShellLiteral("AWS Lambda bootstrap command", line)); 422 } 423 return "printf '%s\\n' " + String.join(" ", quotedLines) + " > bootstrap"; 424 } 425 426 private static String escapeBootstrapArgument(String argument) throws MojoExecutionException { 427 String sanitized = validateDockerfileValue("micronaut.lambda.bootstrap.args", argument); 428 if (isShellSafe(sanitized)) { 429 return sanitized; 430 } 431 return quoteShellLiteral("micronaut.lambda.bootstrap.args", sanitized); 432 } 433 434 private static boolean isShellSafe(String argument) { 435 if (argument == null || argument.isEmpty()) { 436 return false; 437 } 438 for (int i = 0; i < argument.length(); i++) { 439 char c = argument.charAt(i); 440 if (!Character.isLetterOrDigit(c) 441 && "_@%+=:,./-".indexOf(c) == -1) { 442 return false; 443 } 444 } 445 return true; 446 } 447 448 static String quoteShellLiteral(String source, String value) throws MojoExecutionException { 449 validateDockerfileValue(source, value); 450 return "'" + value.replace("'", "'\"'\"'") + "'"; 451 } 452 453 protected static String validateDockerfileValue(String source, String value) throws MojoExecutionException { 454 if (value == null) { 455 throw new MojoExecutionException(source + " must not be null when generating a Dockerfile"); 456 } 457 for (int i = 0; i < value.length(); i++) { 458 char c = value.charAt(i); 459 if (Character.isISOControl(c)) { 460 throw new MojoExecutionException(source + " contains an unsupported control character at index " + i 461 + " and cannot be written into a generated Dockerfile"); 462 } 463 } 464 return value; 465 } 466 467 protected static String validateImageReference(String source, String value) throws MojoExecutionException { 468 String sanitized = validateDockerfileValue(source, value); 469 try { 470 ImageReference.parse(sanitized); 471 } catch (InvalidImageReferenceException e) { 472 throw new MojoExecutionException(source + " is not a valid Docker image reference: " + sanitized, e); 473 } 474 return sanitized; 475 } 476 477 protected static String validateExposedPorts(String source, String value) throws MojoExecutionException { 478 String sanitized = validateDockerfileValue(source, value); 479 for (String token : sanitized.split("\\s+")) { 480 if (token.isEmpty()) { 481 continue; 482 } 483 if (!token.matches("\\d+(/(?:tcp|udp))?")) { 484 throw new MojoExecutionException(source + " contains an invalid exposed port token: " + token); 485 } 486 } 487 return sanitized; 488 } 489 490 protected static String validateDownloadUrl(String source, String value) throws MojoExecutionException { 491 String sanitized = validateDockerfileValue(source, value); 492 try { 493 URI uri = new URI(sanitized); 494 String scheme = uri.getScheme(); 495 if (!"https".equalsIgnoreCase(scheme) && !"http".equalsIgnoreCase(scheme)) { 496 throw new MojoExecutionException(source + " must use an http or https URL: " + sanitized); 497 } 498 if (!uri.isAbsolute()) { 499 throw new MojoExecutionException(source + " must be an absolute URL: " + sanitized); 500 } 501 } catch (URISyntaxException e) { 502 throw new MojoExecutionException(source + " is not a valid URL: " + sanitized, e); 503 } 504 return sanitized; 505 } 506 507 protected static String jsonStringLiteral(String source, String value) throws MojoExecutionException { 508 return "\"" + escapeJsonString(source, value) + "\""; 509 } 510 511 protected static String escapeJsonString(String source, String value) throws MojoExecutionException { 512 String sanitized = validateDockerfileValue(source, value); 513 var result = new StringBuilder(sanitized.length() + 8); 514 for (int i = 0; i < sanitized.length(); i++) { 515 char c = sanitized.charAt(i); 516 switch (c) { 517 case '"' -> result.append("\\\""); 518 case '\\' -> result.append("\\\\"); 519 case '\b' -> result.append("\\b"); 520 case '\f' -> result.append("\\f"); 521 case '\n' -> result.append("\\n"); 522 case '\r' -> result.append("\\r"); 523 case '\t' -> result.append("\\t"); 524 default -> { 525 if (c < 0x20) { 526 result.append(String.format("\\u%04x", (int) c)); 527 } else { 528 result.append(c); 529 } 530 } 531 } 532 } 533 return result.toString(); 534 } 535 536 protected static String shellLiteral(String source, String value) throws MojoExecutionException { 537 return quoteShellLiteral(source, validateDockerfileValue(source, value)); 538 } 539 540 protected static String escapeShellDoubleQuoted(String source, String value) throws MojoExecutionException { 541 return validateDockerfileValue(source, value) 542 .replace("\\", "\\\\") 543 .replace("\"", "\\\"") 544 .replace("$", "\\$") 545 .replace("`", "\\`"); 546 } 547 548 /** 549 * @return Networking mode for the RUN instructions during build (if any). 550 */ 551 protected Optional<String> getNetworkMode() { 552 return Optional.ofNullable(networkMode); 553 } 554 555 /** 556 * @return Map of proxy-related build arguments for Docker builds. 557 */ 558 protected Map<String, String> getProxyBuildArgs() { 559 var proxyArgs = new java.util.HashMap<String, String>(); 560 561 // HTTP proxy configuration from standard JVM properties 562 String httpProxyHost = System.getProperty("http.proxyHost"); 563 String httpProxyPort = System.getProperty("http.proxyPort", "80"); 564 if (StringUtils.hasText(httpProxyHost)) { 565 String httpProxy = "http://" + httpProxyHost + ":" + httpProxyPort; 566 proxyArgs.put("HTTP_PROXY", httpProxy); 567 proxyArgs.put("http_proxy", httpProxy); 568 } 569 570 // HTTPS proxy configuration from standard JVM properties 571 String httpsProxyHost = System.getProperty("https.proxyHost"); 572 String httpsProxyPort = System.getProperty("https.proxyPort", "443"); 573 if (StringUtils.hasText(httpsProxyHost)) { 574 String httpsProxy = "http://" + httpsProxyHost + ":" + httpsProxyPort; 575 proxyArgs.put("HTTPS_PROXY", httpsProxy); 576 proxyArgs.put("https_proxy", httpsProxy); 577 } 578 579 // No proxy configuration from standard JVM properties 580 String nonProxyHosts = System.getProperty("http.nonProxyHosts"); 581 if (StringUtils.hasText(nonProxyHosts)) { 582 // Convert Java format (e.g., "*.company.com|localhost") to standard format (e.g., "*.company.com,localhost") 583 String noProxy = nonProxyHosts.replace("|", ","); 584 proxyArgs.put("NO_PROXY", noProxy); 585 proxyArgs.put("no_proxy", noProxy); 586 } 587 588 return proxyArgs; 589 } 590 591 /** 592 * @return the base image to use for the Dockerfile. 593 */ 594 protected String getBaseImage() { 595 return JibMicronautExtension.determineBaseImage(JibMicronautExtension.getJdkVersion(mavenSession), MicronautRuntime.valueOf(micronautRuntime.toUpperCase()).getBuildStrategy()); 596 } 597 598 /** 599 * Adds cmd to docker oracle cloud function file. 600 * 601 * @param dockerfile the docker file 602 */ 603 protected void oracleCloudFunctionCmd(File dockerfile) throws IOException, MojoExecutionException { 604 if (appArguments != null && !appArguments.isEmpty()) { 605 getLog().info("Using application arguments: " + appArguments); 606 com.google.common.io.Files.asCharSink(dockerfile, Charset.defaultCharset(), FileWriteMode.APPEND) 607 .write(System.lineSeparator() + getCmd()); 608 } else { 609 com.google.common.io.Files.asCharSink(dockerfile, Charset.defaultCharset(), FileWriteMode.APPEND).write(System.lineSeparator() + ORACLE_CLOUD_FUNCTION_DEFAULT_CMD); 610 } 611 } 612 613}