Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.fabric8.maven.docker.service.BuildService Maven / Gradle / Ivy
package io.fabric8.maven.docker.service;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonParseException;
import io.fabric8.maven.docker.access.BuildOptions;
import io.fabric8.maven.docker.access.DockerAccess;
import io.fabric8.maven.docker.access.DockerAccessException;
import io.fabric8.maven.docker.assembly.DockerAssemblyManager;
import io.fabric8.maven.docker.config.AssemblyConfiguration;
import io.fabric8.maven.docker.config.BuildImageConfiguration;
import io.fabric8.maven.docker.config.CleanupMode;
import io.fabric8.maven.docker.config.ConfigHelper;
import io.fabric8.maven.docker.config.ImageConfiguration;
import io.fabric8.maven.docker.model.ImageArchiveManifest;
import io.fabric8.maven.docker.model.ImageArchiveManifestEntry;
import io.fabric8.maven.docker.service.helper.BuildArgResolver;
import io.fabric8.maven.docker.util.DockerFileUtil;
import io.fabric8.maven.docker.util.EnvUtil;
import io.fabric8.maven.docker.util.ImageArchiveUtil;
import io.fabric8.maven.docker.util.ImageName;
import io.fabric8.maven.docker.util.Logger;
import io.fabric8.maven.docker.util.MojoParameters;
import io.fabric8.maven.docker.util.NamePatternUtil;
import org.apache.maven.plugin.MojoExecutionException;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.PatternSyntaxException;
public class BuildService {
private final DockerAccess docker;
private final QueryService queryService;
private final ArchiveService archiveService;
private final RegistryService registryService;
private final Logger log;
BuildService(DockerAccess docker, QueryService queryService, RegistryService registryService, ArchiveService archiveService, Logger log) {
this.docker = docker;
this.queryService = queryService;
this.registryService = registryService;
this.archiveService = archiveService;
this.log = log;
}
/**
* Pull the base image if needed and run the build.
*
* @param imageConfig the image configuration
* @param buildContext the build context
* @throws DockerAccessException
* @throws MojoExecutionException
*/
public void buildImage(ImageConfiguration imageConfig, ImagePullManager imagePullManager, BuildContext buildContext, File buildArchiveFile)
throws DockerAccessException, MojoExecutionException {
BuildArgResolver buildArgResolver = new BuildArgResolver(log);
Map buildArgsFromExternalSources = buildArgResolver.resolveBuildArgs(buildContext);
if (imagePullManager != null) {
autoPullBaseImage(imageConfig, imagePullManager, buildContext, prepareBuildArgs(buildArgsFromExternalSources, imageConfig.getBuildConfiguration()));
autoPullCacheFromImage(imageConfig, imagePullManager, buildContext);
}
buildImage(imageConfig, buildContext.getMojoParameters(), ConfigHelper.isNoCache(imageConfig), checkForSquash(imageConfig), buildArgsFromExternalSources, buildArchiveFile);
}
/**
* Create docker archive for building image
*
* @param imageConfiguration image configuration
* @param buildContext docker build context
* @param archivePath build archive only flag, it can have values TRUE or FALSE and also
* it can hold path to archive where it might get copied over
* @return tarball for docker image
* @throws MojoExecutionException in case any exception comes during building tarball
*/
public File buildArchive(ImageConfiguration imageConfiguration, BuildContext buildContext, String archivePath)
throws MojoExecutionException {
String imageName = imageConfiguration.getName();
ImageName.validate(imageName);
BuildImageConfiguration buildConfig = imageConfiguration.getBuildConfiguration();
MojoParameters params = buildContext.getMojoParameters();
if (buildConfig.getDockerArchive() != null) {
return buildConfig.getAbsoluteDockerTarPath(params);
}
long time = System.currentTimeMillis();
File dockerArchive = archiveService.createArchive(imageName, buildConfig, params, log);
log.info("%s: Created %s in %s", imageConfiguration.getDescription(), dockerArchive.getName(), EnvUtil.formatDurationTill(time));
// Copy created tarball to directory if specified
try {
copyDockerArchive(imageConfiguration, dockerArchive, archivePath);
} catch (IOException exception) {
throw new MojoExecutionException("Error while copying created tar to specified buildArchive path: " + archivePath,
exception);
}
return dockerArchive;
}
public void copyDockerArchive(ImageConfiguration imageConfiguration, File dockerArchive, String archivePath) throws IOException {
if (archivePath != null && !archivePath.isEmpty()) {
Files.copy(dockerArchive.toPath(), new File(archivePath, dockerArchive.getName()).toPath());
log.info("%s: Copied created tarball to %s", imageConfiguration.getDescription(), archivePath);
}
}
public void tagImage(ImageConfiguration imageConfig) throws DockerAccessException {
List tags = imageConfig.getBuildConfiguration().getTags();
if (!tags.isEmpty()) {
String imageName = imageConfig.getName();
log.info("%s: Tag with %s", imageConfig.getDescription(), String.join(",", tags));
BuildImageConfiguration buildConfig = imageConfig.getBuildConfiguration();
for (String tag : tags) {
tagImage(imageName, tag, null, buildConfig.cleanupMode());
}
}
}
/**
* Build an image
*
* @param imageConfig the image configuration
* @param params mojo params for the project
* @param noCache if not null, dictate the caching behaviour. Otherwise its taken from the build configuration
* @param buildArgs docker build args
* @throws DockerAccessException
* @throws MojoExecutionException
*/
protected void buildImage(ImageConfiguration imageConfig, MojoParameters params, boolean noCache, boolean squash, Map buildArgs, File dockerArchive)
throws DockerAccessException, MojoExecutionException {
String imageName = imageConfig.getName();
ImageName.validate(imageName);
BuildImageConfiguration buildConfig = imageConfig.getBuildConfiguration();
String oldImageId = null;
CleanupMode cleanupMode = buildConfig.cleanupMode();
if (cleanupMode.isRemove()) {
oldImageId = queryService.getImageId(imageName);
}
if (buildConfig.getDockerArchive() != null) {
File tarArchive = buildConfig.getAbsoluteDockerTarPath(params);
String archiveImageName = getArchiveImageName(buildConfig, tarArchive);
long time = System.currentTimeMillis();
docker.loadImage(imageName, tarArchive);
log.info("%s: Loaded tarball in %s", buildConfig.getDockerArchive(), EnvUtil.formatDurationTill(time));
if (archiveImageName != null && !archiveImageName.equals(imageName)) {
docker.tag(archiveImageName, imageName, true);
}
return;
}
Map mergedBuildMap = prepareBuildArgs(buildArgs, buildConfig);
// auto is now supported by docker, consider switching?
BuildOptions opts =
new BuildOptions(buildConfig.getBuildOptions())
.dockerfile(buildConfig.getDockerfileName())
.forceRemove(cleanupMode.isRemove())
.noCache(noCache)
.squash(squash)
.cacheFrom(buildConfig.getCacheFrom())
.network(buildConfig.getNetwork())
.buildArgs(mergedBuildMap);
String newImageId = doBuildImage(imageName, dockerArchive, opts);
log.info("%s: Built image %s", imageConfig.getDescription(), newImageId);
removeDanglingImage(imageName, oldImageId, newImageId, cleanupMode, true);
}
public void tagImage(String imageName, String tag, String repo, CleanupMode cleanupMode) throws DockerAccessException {
if (tag != null) {
String fullImageName = new ImageName(imageName, tag).getNameWithOptionalRepository(repo);
String oldImageId = null;
if (cleanupMode.isRemove()) {
oldImageId = queryService.getImageId(fullImageName);
}
docker.tag(imageName, fullImageName, true);
log.info("Tagging image %s successful!", fullImageName);
String newImageId = queryService.getImageId(fullImageName);
removeDanglingImage(fullImageName, oldImageId, newImageId, cleanupMode, false);
}
}
static Map prepareBuildArgs(Map buildArgs, BuildImageConfiguration buildConfig) {
ImmutableMap.Builder builder = ImmutableMap.builder().putAll(Optional.ofNullable(buildArgs).orElse(Collections.emptyMap()));
if (buildConfig.getArgs() != null) {
builder.putAll(buildConfig.getArgs());
}
return builder.build();
}
private String getArchiveImageName(BuildImageConfiguration buildConfig, File tarArchive) throws MojoExecutionException {
if (buildConfig.getLoadNamePattern() == null || buildConfig.getLoadNamePattern().length() == 0) {
return null;
}
ImageArchiveManifest manifest;
try {
manifest = readArchiveManifest(tarArchive);
} catch (IOException | JsonParseException e) {
throw new MojoExecutionException("Unable to read image manifest in archive " + buildConfig.getDockerArchive(), e);
}
String archiveImageName;
try {
archiveImageName = matchArchiveImagesToPattern(buildConfig.getLoadNamePattern(), manifest);
} catch (PatternSyntaxException e) {
throw new MojoExecutionException("Unable to interpret loadNamePattern " + buildConfig.getLoadNamePattern(), e);
}
if (archiveImageName == null) {
throw new MojoExecutionException("No image in the archive has a tag that matches pattern " + buildConfig.getLoadNamePattern());
}
return archiveImageName;
}
private ImageArchiveManifest readArchiveManifest(File tarArchive) throws IOException, JsonParseException {
long time = System.currentTimeMillis();
ImageArchiveManifest manifest = ImageArchiveUtil.readManifest(tarArchive);
log.info("%s: Read archive manifest in %s", tarArchive, EnvUtil.formatDurationTill(time));
// Show the results of reading the manifest to users trying to debug their configuration
if (log.isDebugEnabled()) {
for (ImageArchiveManifestEntry entry : manifest.getEntries()) {
log.debug("Entry ID: %s has %d repo tag(s)", entry.getId(), entry.getRepoTags().size());
for (String repoTag : entry.getRepoTags()) {
log.debug("Repo Tag: %s", repoTag);
}
}
}
return manifest;
}
private String matchArchiveImagesToPattern(String imageNamePattern, ImageArchiveManifest manifest) {
String imageNameRegex = NamePatternUtil.convertNamePattern(imageNamePattern);
log.debug("Image name regex is %s", imageNameRegex);
Map entries = ImageArchiveUtil.findEntriesByRepoTagPattern(imageNameRegex, manifest);
// Show the matches from the manifest to users trying to debug their configuration
if (log.isDebugEnabled()) {
for (Map.Entry entry : entries.entrySet()) {
log.debug("Repo tag pattern matched %s referring to image %s", entry.getKey(), entry.getValue().getId());
}
}
if (!entries.isEmpty()) {
Map.Entry matchedEntry = entries.entrySet().iterator().next();
if (ImageArchiveUtil.mapEntriesById(entries.values()).size() > 1) {
log.warn("Multiple image ids matched pattern %s: using tag %s associated with id %s",
imageNamePattern, matchedEntry.getKey(), matchedEntry.getValue().getId());
} else {
log.info("Using image tag %s from archive", matchedEntry.getKey());
}
return matchedEntry.getKey();
}
return null;
}
private String doBuildImage(String imageName, File dockerArchive, BuildOptions options)
throws DockerAccessException, MojoExecutionException {
docker.buildImage(imageName, dockerArchive, options);
return queryService.getImageId(imageName);
}
private void autoPullBaseImage(ImageConfiguration imageConfig, ImagePullManager imagePullManager, BuildContext buildContext, Map buildArgs)
throws DockerAccessException, MojoExecutionException {
BuildImageConfiguration buildConfig = imageConfig.getBuildConfiguration();
CleanupMode cleanupMode = buildConfig.cleanupMode();
if (buildConfig.getDockerArchive() != null) {
// No auto pull needed in archive mode
return;
}
List fromImages;
if (buildConfig.isDockerFileMode()) {
fromImages = extractBaseFromDockerfile(buildConfig, buildContext.getMojoParameters(), buildArgs);
} else {
fromImages = new LinkedList<>();
String baseImage = extractBaseFromConfiguration(buildConfig);
if (baseImage != null) {
fromImages.add(baseImage);
}
}
for (String fromImage : fromImages) {
if (fromImage != null && !DockerAssemblyManager.SCRATCH_IMAGE.equals(fromImage)) {
String oldImageId = null;
if (cleanupMode.isRemove()) {
oldImageId = queryService.getImageId(fromImage);
}
registryService.pullImageWithPolicy(fromImage, imagePullManager, buildContext.getRegistryConfig(), buildConfig);
String newImageId = queryService.getImageId(fromImage);
removeDanglingImage(fromImage, oldImageId, newImageId, cleanupMode, false);
}
}
}
private void autoPullCacheFromImage(ImageConfiguration imageConfig, ImagePullManager imagePullManager, BuildContext buildContext) throws DockerAccessException, MojoExecutionException {
if (imageConfig.getBuildConfiguration().getCacheFrom() == null) {
return;
}
BuildImageConfiguration buildConfig = imageConfig.getBuildConfiguration();
CleanupMode cleanupMode = buildConfig.cleanupMode();
for (String cacheFromImage : buildConfig.getCacheFrom()) {
String oldImageId = null;
if (cleanupMode.isRemove()) {
oldImageId = queryService.getImageId(cacheFromImage);
}
try {
registryService.pullImageWithPolicy(cacheFromImage, imagePullManager, buildContext.getRegistryConfig(), buildConfig);
} catch (DockerAccessException e) {
log.warn("Could not pull cacheFrom image: '%s'. Reason: %s", cacheFromImage, e.getMessage());
}
String newImageId = queryService.getImageId(cacheFromImage);
removeDanglingImage(cacheFromImage, oldImageId, newImageId, cleanupMode, false);
}
}
private String extractBaseFromConfiguration(BuildImageConfiguration buildConfig) {
String fromImage;
fromImage = buildConfig.getFrom();
if (fromImage == null) {
List assemblyConfig = buildConfig.getAllAssemblyConfigurations();
if (assemblyConfig.isEmpty()) {
fromImage = DockerAssemblyManager.DEFAULT_DATA_BASE_IMAGE;
}
}
return fromImage;
}
static List extractBaseFromDockerfile(BuildImageConfiguration buildConfig, MojoParameters mojoParameters, Map buildArgs) {
if (buildConfig.getDockerFile() == null || !buildConfig.getAbsoluteDockerFilePath(mojoParameters).exists()) {
if (buildConfig.getFrom() != null && !buildConfig.getFrom().isEmpty()) {
return Collections.singletonList(buildConfig.getFrom());
}
return Collections.emptyList();
}
List fromImage;
try {
File fullDockerFilePath = buildConfig.getAbsoluteDockerFilePath(mojoParameters);
fromImage = DockerFileUtil.extractBaseImages(
fullDockerFilePath,
DockerFileUtil.createInterpolator(mojoParameters, buildConfig.getFilter()),
buildArgs);
} catch (IOException e) {
// Cant extract base image, so we wont try an auto pull. An error will occur later anyway when
// building the image, so we are passive here.
return Collections.emptyList();
}
return fromImage;
}
private void removeDanglingImage(String oldImageName, String oldImageId, String newImageId, CleanupMode cleanupMode, boolean force) throws DockerAccessException {
if (oldImageId != null && !oldImageId.equals(newImageId)) {
if (force) {
removeImage(oldImageName, oldImageId, cleanupMode, true);
} else {
// Verify that the image is indeed dangling and remove it (or skip removal altogether).
List oldImageTags = docker.getImageTags(oldImageId);
if (oldImageTags != null) {
if (oldImageTags.isEmpty()) {
removeImage(oldImageName, oldImageId, cleanupMode, false);
} else {
log.warn("%s: Skipped removing image %s; still tagged with: ", oldImageName, oldImageId, String.join(",", oldImageTags));
}
}
}
}
}
private void removeImage(String oldImageName, String oldImageId, CleanupMode cleanupMode, boolean force) throws DockerAccessException {
try {
docker.removeImage(oldImageId, force);
log.info("%s: Removed dangling image %s", oldImageName, oldImageId);
} catch (DockerAccessException exp) {
if (cleanupMode == CleanupMode.TRY_TO_REMOVE) {
log.warn("%s: %s (dangling image)%s", oldImageName, exp.getMessage(),
(exp.getCause() != null ? " [" + exp.getCause().getMessage() + "]" : ""));
} else {
throw exp;
}
}
}
private boolean checkForSquash(ImageConfiguration imageConfig) {
String squash = System.getProperty("docker.squash");
if (squash != null) {
return squash.length() == 0 || Boolean.valueOf(squash);
} else {
BuildImageConfiguration buildConfig = imageConfig.getBuildConfiguration();
return buildConfig.squash();
}
}
// ===========================================
public static class BuildContext implements Serializable {
private MojoParameters mojoParameters;
private Map buildArgs;
private RegistryService.RegistryConfig registryConfig;
public BuildContext() {
}
public MojoParameters getMojoParameters() {
return mojoParameters;
}
public Map getBuildArgs() {
return buildArgs;
}
public RegistryService.RegistryConfig getRegistryConfig() {
return registryConfig;
}
public static class Builder {
private BuildContext context;
public Builder() {
this.context = new BuildContext();
}
public Builder(BuildContext context) {
this.context = context;
}
public Builder mojoParameters(MojoParameters mojoParameters) {
context.mojoParameters = mojoParameters;
return this;
}
public Builder buildArgs(Map buildArgs) {
context.buildArgs = buildArgs;
return this;
}
public Builder registryConfig(RegistryService.RegistryConfig registryConfig) {
context.registryConfig = registryConfig;
return this;
}
public BuildContext build() {
return context;
}
}
}
}