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;
032
033import javax.inject.Inject;
034import java.io.File;
035import java.io.IOException;
036import java.nio.file.Files;
037import java.nio.file.LinkOption;
038import java.nio.file.StandardCopyOption;
039import java.util.List;
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    private static final List<String> SUPPORTED_JIB_BUILD_GOALS = List.of("dockerBuild", "build", "buildTar");
058
059    private final ExecutorService executorService;
060
061    @SuppressWarnings("CdiInjectionPointsInspection")
062    @Inject
063    public DockerMojo(MavenProject mavenProject, JibConfigurationService jibConfigurationService,
064                      ApplicationConfigurationService applicationConfigurationService, DockerService dockerService,
065                      MavenSession mavenSession, MojoExecution mojoExecution, ExecutorService executorService) {
066        super(mavenProject, jibConfigurationService, applicationConfigurationService, dockerService, mavenSession,
067            mojoExecution);
068        this.executorService = executorService;
069    }
070
071    @Override
072    public void execute() throws MojoExecutionException {
073        var providedDockerfile = new File(mavenProject.getBasedir(), DockerfileMojo.DOCKERFILE);
074        if (shouldBuildWithDockerfile(providedDockerfile)) {
075            var dockerfile = determineDockerfile(providedDockerfile);
076            buildDockerfile(dockerfile, providedDockerfile.exists());
077        } else {
078            validateJibBuildGoal();
079            if (jibConfigurationService.getFromImage().isEmpty()) {
080                mavenProject.getProperties().setProperty(PropertyNames.FROM_IMAGE, getBaseImage());
081            }
082            executorService.executeGoal(mavenProject, "com.google.cloud.tools:jib-maven-plugin", jibBuildGoal);
083        }
084    }
085
086    private void validateJibBuildGoal() throws MojoExecutionException {
087        if (!SUPPORTED_JIB_BUILD_GOALS.contains(jibBuildGoal)) {
088            throw new MojoExecutionException("Unsupported jib.buildGoal '" + jibBuildGoal
089                + "'. Supported values are: " + String.join(", ", SUPPORTED_JIB_BUILD_GOALS));
090        }
091    }
092
093    private File determineDockerfile(File providedDockerfile) throws MojoExecutionException {
094        if (providedDockerfile.exists()) {
095            return providedDockerfile;
096        }
097        try {
098            return dockerService.loadDockerfileAsResource(DOCKERFILE_ORACLE_CLOUD);
099        } catch (IOException e) {
100            throw new MojoExecutionException("Error loading Dockerfile", e);
101        }
102    }
103
104    private boolean shouldBuildWithDockerfile(File providedDockerfile) {
105        var runtime = MicronautRuntime.valueOf(micronautRuntime.toUpperCase());
106        return providedDockerfile.exists() || runtime.getBuildStrategy() == DockerBuildStrategy.ORACLE_FUNCTION;
107    }
108
109    private void buildDockerfile(File dockerfile, boolean providedDockerfileExists) throws MojoExecutionException {
110        try {
111            var runtime = MicronautRuntime.valueOf(micronautRuntime.toUpperCase());
112            if (runtime.getBuildStrategy() == DockerBuildStrategy.ORACLE_FUNCTION && !providedDockerfileExists) {
113                oracleCloudFunctionCmd(dockerfile);
114                DockerfileMojo.processOracleFunctionDockerfile(dockerfile);
115            }
116            getLog().info("Using Dockerfile: " + dockerfile.getAbsolutePath());
117            mavenProject.getProperties().put(PropertyNames.SKIP, "true");
118
119            copyDependencies();
120
121            String targetDir = mavenProject.getBuild().getDirectory();
122            var targetDockerfile = new File(targetDir, dockerfile.getName());
123            Files.copy(dockerfile.toPath(), targetDockerfile.toPath(), LinkOption.NOFOLLOW_LINKS,
124                StandardCopyOption.REPLACE_EXISTING);
125
126            BuildImageCmd buildImageCmd = dockerService.buildImageCmd()
127                .withDockerfile(targetDockerfile)
128                .withTags(getTags())
129                .withBaseDirectory(new File(targetDir));
130            getNetworkMode().ifPresent(buildImageCmd::withNetworkMode);
131            dockerService.buildImage(buildImageCmd);
132        } catch (IOException e) {
133            throw new MojoExecutionException(e.getMessage(), e);
134        }
135    }
136
137}