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.PushImageCmd;
019import com.github.dockerjava.api.model.AuthConfig;
020import com.google.cloud.tools.jib.api.Credential;
021import com.google.cloud.tools.jib.api.ImageReference;
022import com.google.cloud.tools.jib.api.LogEvent;
023import com.google.cloud.tools.jib.frontend.CredentialRetrieverFactory;
024import com.google.cloud.tools.jib.maven.MavenProjectProperties;
025import com.google.cloud.tools.jib.registry.credentials.CredentialRetrievalException;
026import io.micronaut.maven.jib.JibConfigurationService;
027import io.micronaut.maven.services.ApplicationConfigurationService;
028import io.micronaut.maven.services.DockerService;
029import org.apache.maven.execution.MavenSession;
030import org.apache.maven.plugin.MojoExecution;
031import org.apache.maven.plugin.MojoExecutionException;
032import org.apache.maven.plugin.MojoFailureException;
033import org.apache.maven.plugins.annotations.Mojo;
034import org.apache.maven.project.MavenProject;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038import javax.inject.Inject;
039import java.util.Optional;
040import java.util.Set;
041import java.util.stream.Stream;
042
043/**
044 * <p>Implementation of the <code>deploy</code> lifecycle for pushing Docker images</p>
045 * <p><strong>WARNING</strong>: this goal is not intended to be executed directly. Instead, Execute the <code>deploy</code>
046 * phase specifying the packaging type, eg:</p>
047 *
048 * <pre>mvn deploy -Dpackaging=docker-native</pre>
049 *
050 * @author Álvaro Sánchez-Mariscal
051 * @since 1.1
052 */
053@Mojo(name = "docker-push")
054public class DockerPushMojo extends AbstractDockerMojo {
055
056    private static final Logger LOG = LoggerFactory.getLogger(DockerPushMojo.class);
057
058    @Inject
059    public DockerPushMojo(MavenProject mavenProject, JibConfigurationService jibConfigurationService,
060                          ApplicationConfigurationService applicationConfigurationService, DockerService dockerService,
061                          MavenSession mavenSession, MojoExecution mojoExecution) {
062        super(mavenProject, jibConfigurationService, applicationConfigurationService, dockerService, mavenSession, mojoExecution);
063    }
064
065    @Override
066    public void execute() throws MojoExecutionException, MojoFailureException {
067        Packaging packaging = Packaging.of(mavenProject.getPackaging());
068        if (packaging == Packaging.DOCKER || packaging == Packaging.DOCKER_NATIVE || packaging == Packaging.DOCKER_CRAC) {
069            Set<String> images = getTags();
070
071            // getTags() will automatically generate an image name if none is specified
072            // To maintain error compatibility, check that an image name has been
073            // manually specified.
074            if (jibConfigurationService.getToImage().isPresent()) {
075                for (String taggedImage : images) {
076                    getLog().info("Pushing image: " + taggedImage);
077                    try (PushImageCmd pushImageCmd = dockerService.pushImageCmd(taggedImage)) {
078                        ImageReference imageReference = ImageReference.parse(taggedImage);
079                        CredentialRetrieverFactory factory = CredentialRetrieverFactory.forImage(imageReference, this::logEvent);
080                        Credential credentialHelperCredential = Stream
081                            .of(factory.wellKnownCredentialHelpers(), factory.googleApplicationDefaultCredentials())
082                            .map(retriever -> {
083                                try {
084                                    return retriever.retrieve();
085                                } catch (CredentialRetrievalException e) {
086                                    return Optional.<Credential>empty();
087                                }
088                            })
089                            .filter(Optional::isPresent)
090                            .map(Optional::get)
091                            .findFirst()
092                            .orElse(factory.dockerConfig().retrieve().orElse(null));
093
094                        Credential credential = jibConfigurationService.getToCredentials().orElse(credentialHelperCredential);
095                        if (credential != null) {
096                            AuthConfig authConfig = dockerService.getAuthConfigFor(taggedImage, credential.getUsername(), credential.getPassword());
097                            pushImageCmd.withAuthConfig(authConfig);
098                        }
099
100                        pushImageCmd.start().awaitCompletion();
101                    } catch (InterruptedException e) {
102                        Thread.currentThread().interrupt();
103                    } catch (Exception e) {
104                        throw new MojoExecutionException(e.getMessage(), e);
105                    }
106                }
107            } else {
108                throw new MojoFailureException("The plugin " + MavenProjectProperties.PLUGIN_KEY + " is misconfigured. Missing <to> tag");
109            }
110        } else {
111            throw new MojoFailureException("The <packaging> must be set to either [" + Packaging.DOCKER.id() + "] or [" + Packaging.DOCKER_NATIVE.id() + "]");
112        }
113    }
114
115    private void logEvent(LogEvent logEvent) {
116        if (logEvent.getLevel().equals(LogEvent.Level.DEBUG)) {
117            LOG.debug(logEvent.getMessage());
118        } else {
119            LOG.info(logEvent.getMessage());
120        }
121    }
122}