org.jolokia.docker.maven.AbstractDockerMojo Maven / Gradle / Ivy
The newest version!
package org.jolokia.docker.maven;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import org.apache.maven.plugin.*;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Settings;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
import org.jolokia.docker.maven.access.*;
import org.jolokia.docker.maven.access.hc.DockerAccessWithHcClient;
import org.jolokia.docker.maven.config.ImageConfiguration;
import org.jolokia.docker.maven.config.handler.ImageConfigResolver;
import org.jolokia.docker.maven.log.LogDispatcher;
import org.jolokia.docker.maven.log.LogOutputSpecFactory;
import org.jolokia.docker.maven.service.*;
import org.jolokia.docker.maven.util.*;
/**
* Base class for this plugin.
*
* @author roland
* @since 26.03.14
*/
public abstract class AbstractDockerMojo extends AbstractMojo implements Contextualizable {
// Key for indicating that a "start" goal has run
public static final String CONTEXT_KEY_START_CALLED = "CONTEXT_KEY_DOCKER_START_CALLED";
// Key holding the log dispatcher
public static final String CONTEXT_KEY_LOG_DISPATCHER = "CONTEXT_KEY_DOCKER_LOG_DISPATCHER";
// Standard HTTPS port (IANA registered). The other 2375 with plain HTTP is used only in older
// docker installations.
public static final String DOCKER_HTTPS_PORT = "2376";
public static final String API_VERSION = "v1.18";
// Current maven project
/** @parameter default-value="${project}" */
protected MavenProject project;
// Settings holding authentication info
/** @component */
protected Settings settings;
// Handler for external configurations
/** @component */
protected ImageConfigResolver imageConfigResolver;
/** @component **/
protected ServiceHubFactory serviceHubFactory;
/** @parameter property = "docker.autoPull" default-value = "on" */
protected String autoPull;
/**
* Whether to keep the containers afters stopping (start/watch/stop)
*
* @parameter property = "docker.keepContainer" default-value = "false"
*/
protected boolean keepContainer;
/**
* Whether to remove volumes when removing the container (start/watch/stop)
*
* @parameter property = "docker.removeVolumes" defaultValue = "false"
*/
protected boolean removeVolumes;
/** @parameter property = "docker.apiVersion" */
private String apiVersion;
// URL to docker daemon
/** @parameter property = "docker.host" */
private String dockerHost;
/** @parameter property = "docker.certPath" */
private String certPath;
// If logging is enabled globally
// Whether to use color
/** @parameter property = "docker.useColor" default-value = "true" */
protected boolean useColor;
// For verbose output
/** @parameter property = "docker.verbose" default-value = "false" */
protected boolean verbose;
// The date format to use when putting out logs
/** @parameter property = "docker.logDate" */
private String logDate;
// Log to stdout regardless if log files are configured or not
/** @parameter property = "docker.logStdout" default-value = "false" */
private boolean logStdout;
// Whether to skip docker altogether
/** @parameter property = "docker.skip" default-value = "false" */
private boolean skip;
// Whether to restrict operation to a single image. This can be either
// the image or an alias name. It can also be comma separated list.
// This parameter is typically set via the command line.
/** @parameter property = "docker.image" */
private String image;
// Default registry to use if no registry is specified
/** @parameter property = "docker.registry" */
protected String registry;
// maximum connection to use in parallel for connecting the docker host
/** @parameter property = "docker.maxConnections" default-value = "100" */
private int maxConnections;
// property file to write out with port mappings
/** @parameter */
protected String portPropertyFile;
// Authentication information
/** @parameter */
Map authConfig;
// Relevant configuration to use. This includes also references to external
// images
/**
* @parameter
*/
private List images;
// Handler dealing with authentication credentials
private AuthConfigFactory authConfigFactory;
protected Logger log;
/**
* Entry point for this plugin. It will set up the helper class and then calls
* {@link #executeInternal(ServiceHub)}
* which must be implemented by subclass.
*
* @throws MojoExecutionException
* @throws MojoFailureException
*/
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
if (!skip) {
log = new AnsiLogger(getLog(), useColor, verbose);
LogOutputSpecFactory logSpecFactory = new LogOutputSpecFactory(useColor, logStdout, logDate);
validateConfiguration(log);
DockerAccess access = null;
try {
access = createDockerAccess();
ServiceHub serviceHub = serviceHubFactory.createServiceHub(access, log, logSpecFactory);
executeInternal(serviceHub);
} catch (DockerAccessException exp) {
throw new MojoExecutionException(log.errorMessage(exp.getMessage()), exp);
} finally {
if (access != null) {
access.shutdown();
}
}
}
}
private DockerAccess createDockerAccess() throws MojoExecutionException, MojoFailureException {
DockerAccess access = null;
if (isDockerAccessRequired()) {
String dockerUrl = EnvUtil.extractUrl(dockerHost);
access = createDockerAccess(dockerUrl);
setDockerHostAddressProperty(dockerUrl);
}
return access;
}
/**
* Override this if your mojo doesnt require access to a Docker host (like creating and attaching
* docker tar archives)
*
* @return true
as the default value
*/
protected boolean isDockerAccessRequired() {
return true;
}
private void validateConfiguration(Logger log) {
if (images != null) {
for (ImageConfiguration imageConfiguration : images) {
imageConfiguration.validate(log);
}
}
}
/**
* Hook for subclass for doing the real job
*
* @param serviceHub context for accessing backends
*/
protected abstract void executeInternal(ServiceHub serviceHub)
throws DockerAccessException, MojoExecutionException;
// =============================================================================================
/**
* Get all images to use. Can be restricted via -Ddocker.image to pick a one or more images. The values
* is taken as comma separated list.
*
* @return list of image configuration to use
*/
protected List getImages() {
List resolvedImages = resolveImages();
List ret = new ArrayList<>();
for (ImageConfiguration imageConfig : resolvedImages) {
if (matchesConfiguredImages(this.image, imageConfig)) {
ret.add(imageConfig);
}
}
return ret;
}
private List resolveImages() {
List ret = new ArrayList<>();
if (images != null) {
for (ImageConfiguration image : images) {
ret.addAll(imageConfigResolver.resolve(image, project.getProperties()));
}
verifyImageNames(ret);
}
return ret;
}
// Extract authentication information
private void verifyImageNames(List ret) {
for (ImageConfiguration config : ret) {
if (config.getName() == null) {
throw new IllegalArgumentException("Configuration error: must have a non-null ");
}
}
}
// Check if the provided image configuration matches the given
protected boolean matchesConfiguredImages(String imageList, ImageConfiguration imageConfig) {
if (imageList == null) {
return true;
}
Set imagesAllowed = new HashSet<>(Arrays.asList(imageList.split("\\s*,\\s*")));
return imagesAllowed.contains(imageConfig.getName()) || imagesAllowed.contains(imageConfig.getAlias());
}
private DockerAccess createDockerAccess(String baseUrl) throws MojoExecutionException {
try {
String version = (apiVersion == null) ? API_VERSION : apiVersion;
DockerAccess client = new DockerAccessWithHcClient(version, baseUrl,
EnvUtil.getCertPath(certPath), maxConnections, log);
client.start();
return client;
}
catch (IOException e) {
throw new MojoExecutionException("Cannot create docker access object ", e);
}
}
// Registry for managed containers
private void setDockerHostAddressProperty(String dockerUrl) throws MojoFailureException {
Properties props = project.getProperties();
if (props.getProperty("docker.host.address") == null) {
final String host;
try {
URI uri = new URI(dockerUrl);
if (uri.getHost() == null && uri.getScheme().equals("unix")) {
host = "localhost";
} else {
host = uri.getHost();
}
} catch (URISyntaxException e) {
throw new MojoFailureException("Cannot parse " + dockerUrl + " as URI: " + e.getMessage(), e);
}
props.setProperty("docker.host.address", host == null ? "" : host);
}
}
// =================================================================================
@Override
public void contextualize(Context context) throws ContextException {
authConfigFactory = new AuthConfigFactory((PlexusContainer) context.get(PlexusConstants.PLEXUS_KEY));
}
// =================================================================================
protected PomLabel getPomLabel() {
// Label used for this run
return new PomLabel(project.getGroupId(),project.getArtifactId(),project.getVersion());
}
protected AuthConfig prepareAuthConfig(ImageName image, String configuredRegistry, boolean isPush)
throws MojoExecutionException {
String user = isPush ? image.getUser() : null;
String registry = image.getRegistry() != null ? image.getRegistry() : configuredRegistry;
return authConfigFactory.createAuthConfig(isPush, authConfig, settings, user, registry);
}
protected LogDispatcher getLogDispatcher(ServiceHub hub) {
LogDispatcher dispatcher = (LogDispatcher) getPluginContext().get(CONTEXT_KEY_LOG_DISPATCHER);
if (dispatcher == null) {
dispatcher = new LogDispatcher(hub.getDockerAccess());
getPluginContext().put(CONTEXT_KEY_LOG_DISPATCHER, dispatcher);
}
return dispatcher;
}
/**
* Try to get the registry from various configuration parameters
*
* @param imageConfig image config which might contain the registry
* @param specificRegistry
* @return the registry found or null if none could be extracted
*/
protected String getConfiguredRegistry(ImageConfiguration imageConfig, String specificRegistry) {
return EnvUtil.findRegistry(imageConfig.getRegistry(), specificRegistry, registry);
}
/**
* Check an image, and, if autoPull
is set to true, fetch it. Otherwise if the image
* is not existent, throw an error
*
* @param hub access object to lookup an image (if autoPull is enabled)
* @param image image name
* @param registry optional registry which is used if the image itself doesn't have a registry.
* @param autoPullAlwaysAllowed whether an unconditional autopull is allowed.
*
* @throws DockerAccessException
* @throws MojoExecutionException
*/
protected void checkImageWithAutoPull(ServiceHub hub, String image, String registry,
boolean autoPullAlwaysAllowed) throws DockerAccessException, MojoExecutionException {
// TODO: further refactoring could be done to avoid referencing the QueryService here
QueryService queryService = hub.getQueryService();
if (!queryService.imageRequiresAutoPull(autoPull, image, autoPullAlwaysAllowed)) {
return;
}
DockerAccess docker = hub.getDockerAccess();
ImageName imageName = new ImageName(image);
docker.pullImage(withLatestIfNoTag(image), prepareAuthConfig(imageName, registry, false), registry);
if (registry != null && !imageName.hasRegistry()) {
// If coming from a registry which was not contained in the original name, add a tag from the
// full name with the registry to the short name with no-registry.
docker.tag(imageName.getFullName(registry), image, false);
}
}
// Fetch only latest if no tag is given
private String withLatestIfNoTag(String name) {
ImageName imageName = new ImageName(name);
return imageName.getTag() == null ? imageName.getNameWithoutTag() + ":latest" : name;
}
}