Add build-image support to the maven plugin
Add a new `build-image` goal to the Maven plugin to allow Docker images to be create via using the cloud native buildpack. See gh-19830pull/19835/head
parent
16e6bc89ed
commit
cb4928ad51
@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.maven;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.Artifact;
|
||||||
|
import org.apache.maven.model.Dependency;
|
||||||
|
import org.apache.maven.plugin.MojoExecutionException;
|
||||||
|
import org.apache.maven.plugins.annotations.Component;
|
||||||
|
import org.apache.maven.plugins.annotations.Parameter;
|
||||||
|
import org.apache.maven.project.MavenProject;
|
||||||
|
import org.apache.maven.project.MavenProjectHelper;
|
||||||
|
import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
|
||||||
|
import org.apache.maven.shared.artifact.filter.collection.ScopeFilter;
|
||||||
|
|
||||||
|
import org.springframework.boot.loader.tools.Layout;
|
||||||
|
import org.springframework.boot.loader.tools.LayoutFactory;
|
||||||
|
import org.springframework.boot.loader.tools.Layouts.Expanded;
|
||||||
|
import org.springframework.boot.loader.tools.Layouts.Jar;
|
||||||
|
import org.springframework.boot.loader.tools.Layouts.LayeredJar;
|
||||||
|
import org.springframework.boot.loader.tools.Layouts.None;
|
||||||
|
import org.springframework.boot.loader.tools.Layouts.War;
|
||||||
|
import org.springframework.boot.loader.tools.Libraries;
|
||||||
|
import org.springframework.boot.loader.tools.Packager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for classes that work with an {@link Packager}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Maven project.
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@Parameter(defaultValue = "${project}", readonly = true, required = true)
|
||||||
|
protected MavenProject project;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maven project helper utils.
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
protected MavenProjectHelper projectHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the main class. If not specified the first compiled class found that
|
||||||
|
* contains a 'main' method will be used.
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@Parameter
|
||||||
|
private String mainClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of archive (which corresponds to how the dependencies are laid out inside
|
||||||
|
* it). Possible values are JAR, LAYERED_JAR, WAR, ZIP, DIR, NONE. Defaults to a guess
|
||||||
|
* based on the archive type.
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@Parameter(property = "spring-boot.repackage.layout")
|
||||||
|
private LayoutType layout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The layout factory that will be used to create the executable archive if no
|
||||||
|
* explicit layout is set. Alternative layouts implementations can be provided by 3rd
|
||||||
|
* parties.
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
@Parameter
|
||||||
|
private LayoutFactory layoutFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclude Spring Boot devtools from the repackaged archive.
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
|
@Parameter(property = "spring-boot.repackage.excludeDevtools", defaultValue = "true")
|
||||||
|
private boolean excludeDevtools = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include system scoped dependencies.
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
@Parameter(defaultValue = "false")
|
||||||
|
public boolean includeSystemScope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a {@link Packager} configured for this MOJO.
|
||||||
|
* @param <P> the packager type
|
||||||
|
* @param supplier a packager supplier
|
||||||
|
* @return a configured packager
|
||||||
|
*/
|
||||||
|
protected <P extends Packager> P getConfiguredPackager(Supplier<P> supplier) {
|
||||||
|
P packager = supplier.get();
|
||||||
|
packager.setLayoutFactory(this.layoutFactory);
|
||||||
|
packager.addMainClassTimeoutWarningListener(new LoggingMainClassTimeoutWarningListener(this::getLog));
|
||||||
|
packager.setMainClass(this.mainClass);
|
||||||
|
if (this.layout != null) {
|
||||||
|
getLog().info("Layout: " + this.layout);
|
||||||
|
packager.setLayout(this.layout.layout());
|
||||||
|
}
|
||||||
|
return packager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return {@link Libraries} that the packager can use.
|
||||||
|
* @param unpacks any libraries that require unpack
|
||||||
|
* @return the libraries to use
|
||||||
|
* @throws MojoExecutionException on execution error
|
||||||
|
*/
|
||||||
|
protected final Libraries getLibraries(Collection<Dependency> unpacks) throws MojoExecutionException {
|
||||||
|
Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(), getFilters(getAdditionalFilters()));
|
||||||
|
Libraries libraries = new ArtifactsLibraries(artifacts, unpacks, getLog());
|
||||||
|
return libraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArtifactsFilter[] getAdditionalFilters() {
|
||||||
|
List<ArtifactsFilter> filters = new ArrayList<>();
|
||||||
|
if (this.excludeDevtools) {
|
||||||
|
Exclude exclude = new Exclude();
|
||||||
|
exclude.setGroupId("org.springframework.boot");
|
||||||
|
exclude.setArtifactId("spring-boot-devtools");
|
||||||
|
ExcludeFilter filter = new ExcludeFilter(exclude);
|
||||||
|
filters.add(filter);
|
||||||
|
}
|
||||||
|
if (!this.includeSystemScope) {
|
||||||
|
filters.add(new ScopeFilter(null, Artifact.SCOPE_SYSTEM));
|
||||||
|
}
|
||||||
|
return filters.toArray(new ArtifactsFilter[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Archive layout types.
|
||||||
|
*/
|
||||||
|
public enum LayoutType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jar Layout.
|
||||||
|
*/
|
||||||
|
JAR(new Jar()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Layered Jar Layout.
|
||||||
|
*/
|
||||||
|
LAYERED_JAR(new LayeredJar()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* War Layout.
|
||||||
|
*/
|
||||||
|
WAR(new War()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zip Layout.
|
||||||
|
*/
|
||||||
|
ZIP(new Expanded()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dir Layout.
|
||||||
|
*/
|
||||||
|
DIR(new Expanded()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No Layout.
|
||||||
|
*/
|
||||||
|
NONE(new None());
|
||||||
|
|
||||||
|
private final Layout layout;
|
||||||
|
|
||||||
|
LayoutType(Layout layout) {
|
||||||
|
this.layout = layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Layout layout() {
|
||||||
|
return this.layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,250 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.maven;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
|
||||||
|
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||||
|
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||||
|
import org.apache.commons.compress.archivers.tar.TarConstants;
|
||||||
|
import org.apache.maven.plugin.MojoExecutionException;
|
||||||
|
import org.apache.maven.plugin.MojoFailureException;
|
||||||
|
import org.apache.maven.plugin.logging.Log;
|
||||||
|
import org.apache.maven.plugins.annotations.Execute;
|
||||||
|
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||||
|
import org.apache.maven.plugins.annotations.Mojo;
|
||||||
|
import org.apache.maven.plugins.annotations.Parameter;
|
||||||
|
import org.apache.maven.plugins.annotations.ResolutionScope;
|
||||||
|
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.build.AbstractBuildLog;
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.build.BuildLog;
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.build.BuildRequest;
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.build.Builder;
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.docker.TotalProgressEvent;
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.io.Owner;
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.io.TarArchive;
|
||||||
|
import org.springframework.boot.loader.tools.EntryWriter;
|
||||||
|
import org.springframework.boot.loader.tools.ImagePackager;
|
||||||
|
import org.springframework.boot.loader.tools.Libraries;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package an application into a OCI image using a
|
||||||
|
* <a href="https://buildpacks.io">buildpack</a>.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
@Mojo(name = "build-image", defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true, threadSafe = true,
|
||||||
|
requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
|
||||||
|
requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME)
|
||||||
|
@Execute(phase = LifecyclePhase.PACKAGE)
|
||||||
|
public class BuildImageMojo extends AbstractPackagerMojo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory containing the JAR.
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
@Parameter(defaultValue = "${project.build.directory}", required = true)
|
||||||
|
private File sourceDirectory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the JAR.
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
@Parameter(defaultValue = "${project.build.finalName}", readonly = true)
|
||||||
|
private String finalName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skip the execution.
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
@Parameter(property = "spring-boot.build-image.skip", defaultValue = "false")
|
||||||
|
private boolean skip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classifier used when finding the source jar.
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
@Parameter
|
||||||
|
private String classifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image configuration operations.
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
@Parameter
|
||||||
|
private Image image;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws MojoExecutionException, MojoFailureException {
|
||||||
|
if (this.project.getPackaging().equals("pom")) {
|
||||||
|
getLog().debug("build-image goal could not be applied to pom project.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.skip) {
|
||||||
|
getLog().debug("skipping build-image as per configuration.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buildImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildImage() throws MojoExecutionException {
|
||||||
|
Libraries libraries = getLibraries(Collections.emptySet());
|
||||||
|
try {
|
||||||
|
Builder builder = new Builder(new MojoBuildLog(this::getLog));
|
||||||
|
BuildRequest request = getBuildRequest(libraries);
|
||||||
|
builder.build(request);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new MojoExecutionException(ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BuildRequest getBuildRequest(Libraries libraries) {
|
||||||
|
Function<Owner, TarArchive> content = (owner) -> getApplicationContent(owner, libraries);
|
||||||
|
return ((this.image != null) ? this.image : new Image()).getBuildRequest(this.project.getArtifact(), content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TarArchive getApplicationContent(Owner owner, Libraries libraries) {
|
||||||
|
ImagePackager packager = getConfiguredPackager(() -> new ImagePackager(getJarFile()));
|
||||||
|
return new PackagedTarArchive(owner, libraries, packager);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getJarFile() {
|
||||||
|
// We can use 'project.getArtifact().getFile()' because that was done in a
|
||||||
|
// forked lifecyle and is now null
|
||||||
|
StringBuilder name = new StringBuilder(this.finalName);
|
||||||
|
if (StringUtils.hasText(this.classifier)) {
|
||||||
|
name.append("-").append(this.classifier);
|
||||||
|
}
|
||||||
|
name.append(".jar");
|
||||||
|
return new File(this.sourceDirectory, name.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link BuildLog} backed by Mojo logging.
|
||||||
|
*/
|
||||||
|
private static class MojoBuildLog extends AbstractBuildLog {
|
||||||
|
|
||||||
|
private static final long THRESHOLD = Duration.ofSeconds(2).toMillis();
|
||||||
|
|
||||||
|
private final Supplier<Log> log;
|
||||||
|
|
||||||
|
MojoBuildLog(Supplier<Log> log) {
|
||||||
|
this.log = log;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void log(String message) {
|
||||||
|
this.log.get().info(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Consumer<TotalProgressEvent> getProgressConsumer(String message) {
|
||||||
|
return new ProgressLog(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ProgressLog implements Consumer<TotalProgressEvent> {
|
||||||
|
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
private long last;
|
||||||
|
|
||||||
|
ProgressLog(String message) {
|
||||||
|
this.message = message;
|
||||||
|
this.last = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(TotalProgressEvent progress) {
|
||||||
|
log(progress.getPercent());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void log(int percent) {
|
||||||
|
if (percent == 100 || (System.currentTimeMillis() - this.last) > THRESHOLD) {
|
||||||
|
MojoBuildLog.this.log.get().info(this.message + " " + percent + "%");
|
||||||
|
this.last = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter class to expose the packaged jar as a {@link TarArchive}.
|
||||||
|
*/
|
||||||
|
static class PackagedTarArchive implements TarArchive {
|
||||||
|
|
||||||
|
static final long NORMALIZED_MOD_TIME = TarArchive.NORMALIZED_TIME.toEpochMilli();
|
||||||
|
|
||||||
|
private final Owner owner;
|
||||||
|
|
||||||
|
private final Libraries libraries;
|
||||||
|
|
||||||
|
private final ImagePackager packager;
|
||||||
|
|
||||||
|
PackagedTarArchive(Owner owner, Libraries libraries, ImagePackager packager) {
|
||||||
|
this.owner = owner;
|
||||||
|
this.libraries = libraries;
|
||||||
|
this.packager = packager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(OutputStream outputStream) throws IOException {
|
||||||
|
TarArchiveOutputStream tar = new TarArchiveOutputStream(outputStream);
|
||||||
|
this.packager.packageImage(this.libraries, (entry, entryWriter) -> write(entry, entryWriter, tar));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void write(ZipEntry jarEntry, EntryWriter entryWriter, TarArchiveOutputStream tar) {
|
||||||
|
try {
|
||||||
|
TarArchiveEntry tarEntry = convert(jarEntry);
|
||||||
|
tar.putArchiveEntry(tarEntry);
|
||||||
|
if (tarEntry.isFile()) {
|
||||||
|
entryWriter.write(tar);
|
||||||
|
}
|
||||||
|
tar.closeArchiveEntry();
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TarArchiveEntry convert(ZipEntry entry) {
|
||||||
|
byte linkFlag = (entry.isDirectory()) ? TarConstants.LF_DIR : TarConstants.LF_NORMAL;
|
||||||
|
TarArchiveEntry tarEntry = new TarArchiveEntry(entry.getName(), linkFlag, true);
|
||||||
|
tarEntry.setUserId(this.owner.getUid());
|
||||||
|
tarEntry.setGroupId(this.owner.getGid());
|
||||||
|
tarEntry.setModTime(NORMALIZED_MOD_TIME);
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
tarEntry.setSize(entry.getSize());
|
||||||
|
}
|
||||||
|
return tarEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.maven;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.Artifact;
|
||||||
|
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.build.BuildRequest;
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.docker.type.ImageName;
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.docker.type.ImageReference;
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.io.Owner;
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.io.TarArchive;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image configuration options.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
public class Image {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the created image.
|
||||||
|
*/
|
||||||
|
String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The builder used to create the image.
|
||||||
|
*/
|
||||||
|
String builder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Environment properties that should be passed to the builder.
|
||||||
|
*/
|
||||||
|
Map<String, String> env;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the cache should be cleaned before building.
|
||||||
|
*/
|
||||||
|
boolean cleanCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If verbose logging is required.
|
||||||
|
*/
|
||||||
|
boolean verboseLogging;
|
||||||
|
|
||||||
|
BuildRequest getBuildRequest(Artifact artifact, Function<Owner, TarArchive> applicationContent) {
|
||||||
|
return customize(BuildRequest.of(getOrDeduceName(artifact), applicationContent));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageReference getOrDeduceName(Artifact artifact) {
|
||||||
|
if (StringUtils.hasText(this.name)) {
|
||||||
|
return ImageReference.of(this.name);
|
||||||
|
}
|
||||||
|
ImageName imageName = ImageName.of(artifact.getArtifactId());
|
||||||
|
return ImageReference.of(imageName, artifact.getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
private BuildRequest customize(BuildRequest request) {
|
||||||
|
if (StringUtils.hasText(this.builder)) {
|
||||||
|
request = request.withBuilder(ImageReference.of(this.builder));
|
||||||
|
}
|
||||||
|
if (this.env != null && !this.env.isEmpty()) {
|
||||||
|
request = request.withEnv(this.env);
|
||||||
|
}
|
||||||
|
request = request.withCleanCache(this.cleanCache);
|
||||||
|
request = request.withVerboseLogging(this.verboseLogging);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.maven;
|
||||||
|
|
||||||
|
import org.springframework.boot.loader.tools.Layout;
|
||||||
|
import org.springframework.boot.loader.tools.Layouts.Expanded;
|
||||||
|
import org.springframework.boot.loader.tools.Layouts.Jar;
|
||||||
|
import org.springframework.boot.loader.tools.Layouts.None;
|
||||||
|
import org.springframework.boot.loader.tools.Layouts.War;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Archive layout types.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
public enum LayoutType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jar Layout.
|
||||||
|
*/
|
||||||
|
JAR(new Jar()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* War Layout.
|
||||||
|
*/
|
||||||
|
WAR(new War()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zip Layout.
|
||||||
|
*/
|
||||||
|
ZIP(new Expanded()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dir Layout.
|
||||||
|
*/
|
||||||
|
DIR(new Expanded()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No Layout.
|
||||||
|
*/
|
||||||
|
NONE(new None());
|
||||||
|
|
||||||
|
private final Layout layout;
|
||||||
|
|
||||||
|
LayoutType(Layout layout) {
|
||||||
|
this.layout = layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Layout layout() {
|
||||||
|
return this.layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.maven;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.apache.maven.plugin.logging.Log;
|
||||||
|
|
||||||
|
import org.springframework.boot.loader.tools.Packager.MainClassTimeoutWarningListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link MainClassTimeoutWarningListener} backed by a supplied Maven {@link Log}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
class LoggingMainClassTimeoutWarningListener implements MainClassTimeoutWarningListener {
|
||||||
|
|
||||||
|
private final Supplier<Log> log;
|
||||||
|
|
||||||
|
LoggingMainClassTimeoutWarningListener(Supplier<Log> log) {
|
||||||
|
this.log = log;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleTimeoutWarning(long duration, String mainMethod) {
|
||||||
|
this.log.get().warn("Searching for the main-class is taking some time, "
|
||||||
|
+ "consider using the mainClass configuration parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.maven;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.Artifact;
|
||||||
|
import org.apache.maven.artifact.DefaultArtifact;
|
||||||
|
import org.apache.maven.artifact.handler.DefaultArtifactHandler;
|
||||||
|
import org.apache.maven.artifact.versioning.VersionRange;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.build.BuildRequest;
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.io.Owner;
|
||||||
|
import org.springframework.boot.cloudnativebuildpack.io.TarArchive;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link Image}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
class ImageTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getBuildRequestWhenNameIsNullDeducesName() {
|
||||||
|
BuildRequest request = new Image().getBuildRequest(createArtifact(), mockAplicationContent());
|
||||||
|
assertThat(request.getName().toString()).isEqualTo("docker.io/library/my-app:0.0.1-SNAPSHOT");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getBuildEquestWhenNameIsSetUsesName() {
|
||||||
|
Image image = new Image();
|
||||||
|
image.name = "demo";
|
||||||
|
BuildRequest request = image.getBuildRequest(createArtifact(), mockAplicationContent());
|
||||||
|
assertThat(request.getName().toString()).isEqualTo("docker.io/library/demo:latest");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getBuildRequestWhenNoCustomizationsUsesDefaults() {
|
||||||
|
BuildRequest request = new Image().getBuildRequest(createArtifact(), mockAplicationContent());
|
||||||
|
assertThat(request.getName().toString()).isEqualTo("docker.io/library/my-app:0.0.1-SNAPSHOT");
|
||||||
|
assertThat(request.getBuilder().toString()).isEqualTo("docker.io/cloudfoundry/cnb:0.0.43-bionic");
|
||||||
|
assertThat(request.getEnv()).isEmpty();
|
||||||
|
assertThat(request.isCleanCache()).isFalse();
|
||||||
|
assertThat(request.isVerboseLogging()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getBuildRequestWhenHasBuilderUsesBuilder() {
|
||||||
|
Image image = new Image();
|
||||||
|
image.builder = "springboot/builder:2.2.x";
|
||||||
|
BuildRequest request = image.getBuildRequest(createArtifact(), mockAplicationContent());
|
||||||
|
assertThat(request.getBuilder().toString()).isEqualTo("docker.io/springboot/builder:2.2.x");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getBuildRequestWhenHasEnvUsesEnv() {
|
||||||
|
Image image = new Image();
|
||||||
|
image.env = Collections.singletonMap("test", "test");
|
||||||
|
BuildRequest request = image.getBuildRequest(createArtifact(), mockAplicationContent());
|
||||||
|
assertThat(request.getEnv()).containsExactly(entry("test", "test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getBuildRequestWhenHasCleanCacheUsesCleanCache() {
|
||||||
|
Image image = new Image();
|
||||||
|
image.cleanCache = true;
|
||||||
|
BuildRequest request = image.getBuildRequest(createArtifact(), mockAplicationContent());
|
||||||
|
assertThat(request.isCleanCache()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getBuildRequestWhenHasVerboseLoggingUsesVerboseLogging() {
|
||||||
|
Image image = new Image();
|
||||||
|
image.verboseLogging = true;
|
||||||
|
BuildRequest request = image.getBuildRequest(createArtifact(), mockAplicationContent());
|
||||||
|
assertThat(request.isVerboseLogging()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Artifact createArtifact() {
|
||||||
|
return new DefaultArtifact("com.example", "my-app", VersionRange.createFromVersion("0.0.1-SNAPSHOT"), "compile",
|
||||||
|
"jar", null, new DefaultArtifactHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function<Owner, TarArchive> mockAplicationContent() {
|
||||||
|
return (owner) -> null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue