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.github.dockerjava.api.command.BuildImageCmd;
019import com.google.cloud.tools.jib.plugins.common.PropertyNames;
020import io.micronaut.maven.core.DockerBuildStrategy;
021import io.micronaut.maven.core.MicronautRuntime;
022import io.micronaut.maven.jib.JibConfigurationService;
023import io.micronaut.maven.services.ApplicationConfigurationService;
024import io.micronaut.maven.services.DockerService;
025import io.micronaut.maven.services.ExecutorService;
026import org.apache.maven.execution.MavenSession;
027import org.apache.maven.plugin.MojoExecution;
028import org.apache.maven.plugin.MojoExecutionException;
029import org.apache.maven.plugins.annotations.Mojo;
030import org.apache.maven.plugins.annotations.ResolutionScope;
031import org.apache.maven.project.MavenProject;
032import org.apache.maven.shared.invoker.MavenInvocationException;
033
034import javax.inject.Inject;
035import java.io.File;
036import java.io.IOException;
037import java.nio.file.Files;
038import java.nio.file.LinkOption;
039import java.nio.file.StandardCopyOption;
040
041import static io.micronaut.maven.DockerfileMojo.DOCKERFILE_ORACLE_CLOUD;
042
043/**
044 * <p>Allows using a provided Dockerfile.</p>
045 * <p><strong>WARNING</strong>: this goal is not intended to be executed directly. Instead, specify the packaging type
046 * using the <code>packaging</code> property, eg:</p>
047 *
048 * <pre>mvn package -Dpackaging=docker</pre>
049 *
050 * @author Álvaro Sánchez-Mariscal
051 * @since 1.1
052 */
053@Mojo(name = DockerMojo.DOCKER_PACKAGING, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
054public class DockerMojo extends AbstractDockerMojo {
055
056    public static final String DOCKER_PACKAGING = "docker";
057
058    private final ExecutorService executorService;
059
060    @SuppressWarnings("CdiInjectionPointsInspection")
061    @Inject
062    public DockerMojo(MavenProject mavenProject, JibConfigurationService jibConfigurationService,
063                      ApplicationConfigurationService applicationConfigurationService, DockerService dockerService,
064                      MavenSession mavenSession, MojoExecution mojoExecution, ExecutorService executorService) {
065        super(mavenProject, jibConfigurationService, applicationConfigurationService, dockerService, mavenSession,
066            mojoExecution);
067        this.executorService = executorService;
068    }
069
070    @Override
071    public void execute() throws MojoExecutionException {
072        var providedDockerfile = new File(mavenProject.getBasedir(), DockerfileMojo.DOCKERFILE);
073        if (shouldBuildWithDockerfile(providedDockerfile)) {
074            var dockerfile = determineDockerfile(providedDockerfile);
075            buildDockerfile(dockerfile, providedDockerfile.exists());
076        } else {
077            if (jibConfigurationService.getFromImage().isEmpty()) {
078                System.setProperty(PropertyNames.FROM_IMAGE, getBaseImage());
079            }
080            try {
081                String pluginGoalKey = "jib:" + jibBuildGoal;
082                getLog().info("Invoking " + pluginGoalKey);
083                var result = executorService.invokeGoal("com.google.cloud.tools:jib-maven-plugin", jibBuildGoal);
084                if (result.getExitCode() != 0) {
085                    for (String line : result.outputHandler().getOutput()) {
086                        getLog().error(line);
087                    }
088                    throw new MojoExecutionException("jib-maven-plugin failed, check logs above for details");
089                } else {
090                    for (String line : result.outputHandler().getOutput(pluginGoalKey)) {
091                        getLog().info(line);
092                    }
093                }
094            } catch (MavenInvocationException e) {
095                throw new MojoExecutionException("Could not build docker image", e);
096            }
097        }
098    }
099
100    private File determineDockerfile(File providedDockerfile) throws MojoExecutionException {
101        if (providedDockerfile.exists()) {
102            return providedDockerfile;
103        }
104        try {
105            return dockerService.loadDockerfileAsResource(DOCKERFILE_ORACLE_CLOUD);
106        } catch (IOException e) {
107            throw new MojoExecutionException("Error loading Dockerfile", e);
108        }
109    }
110
111    private boolean shouldBuildWithDockerfile(File providedDockerfile) {
112        var runtime = MicronautRuntime.valueOf(micronautRuntime.toUpperCase());
113        return providedDockerfile.exists() || runtime.getBuildStrategy() == DockerBuildStrategy.ORACLE_FUNCTION;
114    }
115
116    private void buildDockerfile(File dockerfile, boolean providedDockerfileExists) throws MojoExecutionException {
117        try {
118            var runtime = MicronautRuntime.valueOf(micronautRuntime.toUpperCase());
119            if (runtime.getBuildStrategy() == DockerBuildStrategy.ORACLE_FUNCTION && !providedDockerfileExists) {
120                oracleCloudFunctionCmd(dockerfile);
121                DockerfileMojo.processOracleFunctionDockerfile(dockerfile);
122            }
123            getLog().info("Using Dockerfile: " + dockerfile.getAbsolutePath());
124            mavenProject.getProperties().put(PropertyNames.SKIP, "true");
125
126            copyDependencies();
127
128            String targetDir = mavenProject.getBuild().getDirectory();
129            var targetDockerfile = new File(targetDir, dockerfile.getName());
130            Files.copy(dockerfile.toPath(), targetDockerfile.toPath(), LinkOption.NOFOLLOW_LINKS,
131                StandardCopyOption.REPLACE_EXISTING);
132
133            BuildImageCmd buildImageCmd = dockerService.buildImageCmd()
134                .withDockerfile(targetDockerfile)
135                .withTags(getTags())
136                .withBaseDirectory(new File(targetDir));
137            getNetworkMode().ifPresent(buildImageCmd::withNetworkMode);
138            dockerService.buildImage(buildImageCmd);
139        } catch (IOException e) {
140            throw new MojoExecutionException(e.getMessage(), e);
141        }
142    }
143
144}