Skip to content

Commit

Permalink
Add cloud native buildpack module
Browse files Browse the repository at this point in the history
Add a Java implementation of the buildpacks.io specification allowing
projects to be packaged into OCI containers. The `builder` class
provides a Java equivalent of `pack build` command and is based on
the `pack` CLI Go code published at https://github.com/buildpacks/pack.

Closes spring-projectsgh-19828
  • Loading branch information
philwebb committed Jan 21, 2020
1 parent 7fe79f3 commit aa19547
Show file tree
Hide file tree
Showing 152 changed files with 14,613 additions and 2 deletions.
4 changes: 2 additions & 2 deletions eclipse/spring-boot-project.setup
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
xmlns:setup.p2="http://www.eclipse.org/oomph/setup/p2/1.0"
xmlns:setup.workingsets="http://www.eclipse.org/oomph/setup/workingsets/1.0"
xmlns:workingsets="http://www.eclipse.org/oomph/workingsets/1.0"
xsi:schemaLocation="http://www.eclipse.org/oomph/setup/jdt/1.0 https://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/JDT.ecore http://www.eclipse.org/buildship/oomph/1.0 https://raw.githubusercontent.com/eclipse/buildship/master/org.eclipse.buildship.oomph/model/GradleImport-1.0.ecore http://www.eclipse.org/oomph/predicates/1.0 https://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/Predicates.ecore http://www.eclipse.org/oomph/setup/workingsets/1.0 https://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/SetupWorkingSets.ecore http://www.eclipse.org/oomph/workingsets/1.0 https://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/WorkingSets.ecore"
xsi:schemaLocation="http://www.eclipse.org/oomph/setup/jdt/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/JDT.ecore http://www.eclipse.org/buildship/oomph/1.0 https://raw.githubusercontent.com/eclipse/buildship/master/org.eclipse.buildship.oomph/model/GradleImport-1.0.ecore http://www.eclipse.org/oomph/predicates/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/Predicates.ecore http://www.eclipse.org/oomph/setup/workingsets/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/SetupWorkingSets.ecore http://www.eclipse.org/oomph/workingsets/1.0 http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/models/WorkingSets.ecore"
name="spring.boot.2.3.x"
label="Spring Boot 2.3.x">
<setupTask
Expand Down Expand Up @@ -127,7 +127,7 @@
name="spring-boot-tools">
<predicate
xsi:type="predicates:NamePredicate"
pattern="spring-boot-(tools|antlib|configuration-.*|loader|.*-tools|.*-plugin|autoconfigure-processor)"/>
pattern="spring-boot-(tools|antlib|configuration-.*|loader|.*-tools|.*-plugin|autoconfigure-processor|cloudnativebuildpack)"/>
</workingSet>
<workingSet
name="spring-boot-starters">
Expand Down
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ include 'spring-boot-project:spring-boot-dependencies'
include 'spring-boot-project:spring-boot-parent'
include 'spring-boot-project:spring-boot-tools:spring-boot-antlib'
include 'spring-boot-project:spring-boot-tools:spring-boot-autoconfigure-processor'
include 'spring-boot-project:spring-boot-tools:spring-boot-cloudnativebuildpack'
include 'spring-boot-project:spring-boot-tools:spring-boot-configuration-metadata'
include 'spring-boot-project:spring-boot-tools:spring-boot-configuration-processor'
include 'spring-boot-project:spring-boot-tools:spring-boot-gradle-plugin'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
plugins {
id 'java-library'
id 'org.springframework.boot.conventions'
id 'org.springframework.boot.deployed'
id 'org.springframework.boot.internal-dependency-management'
}

description = 'Spring Boot Cloud Native Buildpack'

dependencies {
api platform(project(':spring-boot-project:spring-boot-parent'))
api 'com.fasterxml.jackson.core:jackson-databind'
api 'com.fasterxml.jackson.module:jackson-module-parameter-names'
api 'net.java.dev.jna:jna-platform'
api 'org.apache.commons:commons-compress:1.19'
api 'org.apache.httpcomponents:httpclient'
api 'org.springframework:spring-core'

testImplementation project(':spring-boot-project:spring-boot-tools:spring-boot-test-support')
testImplementation 'com.jayway.jsonpath:json-path'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.testcontainers:testcontainers'
testImplementation 'org.hamcrest:hamcrest'
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.mockito:mockito-junit-jupiter'
testImplementation 'org.skyscreamer:jsonassert'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* 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.cloudnativebuildpack.build;

import java.util.List;
import java.util.function.Consumer;

import org.springframework.boot.cloudnativebuildpack.docker.LogUpdateEvent;
import org.springframework.boot.cloudnativebuildpack.docker.TotalProgressEvent;
import org.springframework.boot.cloudnativebuildpack.docker.type.Image;
import org.springframework.boot.cloudnativebuildpack.docker.type.ImageReference;
import org.springframework.boot.cloudnativebuildpack.docker.type.VolumeName;

/**
* Base class for {@link BuildLog} implementations.
*
* @author Phillip Webb
* @since 2.3.0
*/
public abstract class AbstractBuildLog implements BuildLog {

@Override
public void start(BuildRequest request) {
log("Building image '" + request.getName() + "'");
log();
}

@Override
public Consumer<TotalProgressEvent> pullingBuilder(BuildRequest request, ImageReference imageReference) {
return getProgressConsumer(" > Pulling builder image '" + imageReference + "'");
}

@Override
public void pulledBulder(BuildRequest request, Image image) {
log(" > Pulled builder image '" + getDigest(image) + "'");
}

@Override
public Consumer<TotalProgressEvent> pullingRunImage(BuildRequest request, ImageReference imageReference) {
return getProgressConsumer(" > Pulling run image '" + imageReference + "'");
}

@Override
public void pulledRunImage(BuildRequest request, Image image) {
log(" > Pulled run image '" + getDigest(image) + "'");
}

@Override
public void executingLifecycle(BuildRequest request, LifecycleVersion version, VolumeName buildCacheVolume) {
log(" > Executing lifecycle version " + version);
log(" > Using build cache volume '" + buildCacheVolume + "'");
}

@Override
public Consumer<LogUpdateEvent> runningPhase(BuildRequest request, String name) {
log();
log(" > Running " + name);
String prefix = String.format(" %-14s", "[" + name + "] ");
return (event) -> log(prefix + event);
}

@Override
public void executedLifecycle(BuildRequest request) {
log();
log("Successfully built image '" + request.getName() + "'");
log();
}

private String getDigest(Image image) {
List<String> digests = image.getDigests();
return (digests.isEmpty() ? "" : digests.get(0));
}

protected void log() {
log("");
}

protected abstract void log(String message);

protected abstract Consumer<TotalProgressEvent> getProgressConsumer(String message);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* 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.cloudnativebuildpack.build;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.util.Assert;

/**
* API Version number comprised a major and minor value.
*
* @author Phillip Webb
*/
final class ApiVersion {

/**
* The platform API version supported by this release.
*/
static final ApiVersion PLATFORM = new ApiVersion(0, 1);

private static final Pattern PATTERN = Pattern.compile("^v?(\\d+)\\.(\\d*)$");

private final int major;

private final int minor;

private ApiVersion(int major, int minor) {
this.major = major;
this.minor = minor;
}

/**
* Return the major version number.
* @return the major version
*/
int getMajor() {
return this.major;
}

/**
* Return the minor version number.
* @return the minor version
*/
int getMinor() {
return this.minor;
}

/**
* Assert that this API version supports the specified version.
* @param other the version to check against
* @see #supports(ApiVersion)
*/
void assertSupports(ApiVersion other) {
if (!supports(other)) {
throw new IllegalStateException(
"Version '" + other + "' is not supported by this version ('" + this + "')");
}
}

/**
* Returns if this API version supports the given version. A {@code 0.x} matches only
* the same version number. A 1.x or higher release matches when the versions have the
* same major version and a minor that is equal or greater.
* @param other the version to check against
* @return of the specified API is supported
* @see #assertSupports(ApiVersion)
*/
boolean supports(ApiVersion other) {
if (equals(other)) {
return true;
}
if (this.major == 0 || this.major != other.major) {
return false;
}
return this.minor >= other.minor;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
ApiVersion other = (ApiVersion) obj;
return (this.major == other.major) && (this.minor == other.minor);
}

@Override
public int hashCode() {
return this.major * 31 + this.minor;
}

@Override
public String toString() {
return "v" + this.major + "." + this.minor;
}

/**
* Factory method to parse a string into an {@link ApiVersion} instance.
* @param value the value to parse.
* @return the corresponding {@link ApiVersion}
* @throws IllegalArgumentException if the value could not be parsed
*/
static ApiVersion parse(String value) {
Assert.hasText(value, "Value must not be empty");
Matcher matcher = PATTERN.matcher(value);
Assert.isTrue(matcher.matches(), "Malformed version number '" + value + "'");
try {
int major = Integer.parseInt(matcher.group(1));
int minor = Integer.parseInt(matcher.group(2));
return new ApiVersion(major, minor);
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException("Malformed version number '" + value + "'", ex);
}
}

}
Loading

0 comments on commit aa19547

Please sign in to comment.