diff --git a/jkube-kit/build/service/buildpacks/pom.xml b/jkube-kit/build/service/buildpacks/pom.xml
index 6bf5c3b47b..31f906ded3 100644
--- a/jkube-kit/build/service/buildpacks/pom.xml
+++ b/jkube-kit/build/service/buildpacks/pom.xml
@@ -74,6 +74,18 @@
false
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
+
-
diff --git a/jkube-kit/config/image/src/main/java/org/eclipse/jkube/kit/config/image/build/JKubeBuildStrategy.java b/jkube-kit/config/image/src/main/java/org/eclipse/jkube/kit/config/image/build/JKubeBuildStrategy.java
index 9bacd48741..5e2eee6e45 100644
--- a/jkube-kit/config/image/src/main/java/org/eclipse/jkube/kit/config/image/build/JKubeBuildStrategy.java
+++ b/jkube-kit/config/image/src/main/java/org/eclipse/jkube/kit/config/image/build/JKubeBuildStrategy.java
@@ -35,7 +35,12 @@ public enum JKubeBuildStrategy {
/**
* Docker build with a binary source
*/
- docker("Docker");
+ docker("Docker"),
+
+ /**
+ * BuildPacks
+ */
+ buildpacks("Buildpacks");
// Source strategy elements
public enum SourceStrategy {
diff --git a/jkube-kit/config/service/pom.xml b/jkube-kit/config/service/pom.xml
index 7a52f9ccc5..94e8a5dc6e 100644
--- a/jkube-kit/config/service/pom.xml
+++ b/jkube-kit/config/service/pom.xml
@@ -41,6 +41,10 @@
org.eclipse.jkube
jkube-kit-build-service-jib
+
+ org.eclipse.jkube
+ jkube-kit-build-service-buildpacks
+
org.eclipse.jkube
jkube-kit-helm
@@ -56,6 +60,12 @@
test
test-jar
+
+ org.eclipse.jkube
+ jkube-kit-build-service-buildpacks
+ test-jar
+ test
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/kubernetes/BuildPackBuildService.java b/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/kubernetes/BuildPackBuildService.java
new file mode 100644
index 0000000000..a9164d664b
--- /dev/null
+++ b/jkube-kit/config/service/src/main/java/org/eclipse/jkube/kit/config/service/kubernetes/BuildPackBuildService.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2019 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at:
+ *
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.jkube.kit.config.service.kubernetes;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.util.Objects;
+import java.util.Properties;
+
+import org.eclipse.jkube.kit.common.KitLogger;
+import org.eclipse.jkube.kit.common.RegistryConfig;
+import org.eclipse.jkube.kit.common.util.EnvUtil;
+import org.eclipse.jkube.kit.common.util.PropertiesUtil;
+import org.eclipse.jkube.kit.config.image.ImageConfiguration;
+import org.eclipse.jkube.kit.config.image.build.JKubeBuildStrategy;
+import org.eclipse.jkube.kit.config.service.AbstractImageBuildService;
+import org.eclipse.jkube.kit.config.service.BuildServiceConfig;
+import org.eclipse.jkube.kit.config.service.JKubeServiceException;
+import org.eclipse.jkube.kit.config.service.JKubeServiceHub;
+import org.eclipse.jkube.kit.service.buildpacks.BuildPackBuildOptions;
+import org.eclipse.jkube.kit.service.buildpacks.BuildPackCliDownloader;
+import org.eclipse.jkube.kit.service.buildpacks.controller.BuildPackCliController;
+
+import static org.apache.commons.lang3.StringUtils.strip;
+
+public class BuildPackBuildService extends AbstractImageBuildService {
+ private static final String DEFAULT_BUILDER_IMAGE = "paketobuildpacks/builder:base";
+ private static final String PACK_CONFIG_DIR = ".pack";
+ private static final String PACK_CONFIG_FILE = "config.toml";
+
+ private final BuildServiceConfig buildServiceConfig;
+ private final KitLogger kitLogger;
+ private final Properties packProperties;
+
+ public BuildPackBuildService(JKubeServiceHub jKubeServiceHub) {
+ this(jKubeServiceHub, PropertiesUtil.getPropertiesFromResource(BuildPackBuildService.class.getResource("/META-INF/jkube/pack-cli.properties")));
+ }
+
+ BuildPackBuildService(JKubeServiceHub jKubeServiceHub, Properties packProperties) {
+ super(jKubeServiceHub);
+ this.buildServiceConfig = Objects.requireNonNull(jKubeServiceHub.getBuildServiceConfig(),
+ "BuildServiceConfig is required");
+ this.kitLogger = Objects.requireNonNull(jKubeServiceHub.getLog());
+ this.packProperties = packProperties;
+
+ }
+
+ @Override
+ protected void buildSingleImage(ImageConfiguration imageConfiguration) {
+ kitLogger.info("Delegating container image building process to BuildPacks");
+ doBuildPackBuild(imageConfiguration);
+ }
+
+ private void doBuildPackBuild(ImageConfiguration imageConfiguration) {
+ BuildPackCliDownloader packCliDownloader = new BuildPackCliDownloader(kitLogger, packProperties);
+ File packCli = packCliDownloader.getPackCLIIfPresentOrDownload();
+ kitLogger.info("Using pack %s", packCli.getAbsolutePath());
+ BuildPackCliController packCliController = new BuildPackCliController(packCli, kitLogger);
+ BuildPackBuildOptions buildOptions = createBuildPackOptions(imageConfiguration);
+ packCliController.build(buildOptions);
+ }
+
+ private BuildPackBuildOptions createBuildPackOptions(ImageConfiguration imageConfiguration) {
+ Properties packConfigProperties = readLocalPackConfig();
+ String builderImage = strip(packConfigProperties.getProperty("default-builder-image", DEFAULT_BUILDER_IMAGE), "\"");
+ BuildPackBuildOptions.BuildPackBuildOptionsBuilder buildOptionsBuilder = BuildPackBuildOptions.builder();
+ buildOptionsBuilder.imageName(imageConfiguration.getName());
+ buildOptionsBuilder.builderImage(builderImage);
+ buildOptionsBuilder.creationTime("now");
+ return buildOptionsBuilder.build();
+ }
+
+ private Properties readLocalPackConfig() {
+ File packConfigDir = new File(EnvUtil.getUserHome(), PACK_CONFIG_DIR);
+ if (packConfigDir.exists() && packConfigDir.isDirectory()) {
+ File packConfig = new File(packConfigDir, PACK_CONFIG_FILE);
+ try {
+ return PropertiesUtil.getPropertiesFromResource(packConfig.toURI().toURL());
+ } catch (MalformedURLException e) {
+ kitLogger.warn("Failure in reading pack local configuration : " + e.getMessage());
+ }
+ }
+ return new Properties();
+ }
+
+ @Override
+ protected void pushSingleImage(ImageConfiguration imageConfiguration, int retries, RegistryConfig registryConfig, boolean skipTag) throws JKubeServiceException {
+ }
+
+ @Override
+ public boolean isApplicable() {
+ return buildServiceConfig.getJKubeBuildStrategy() != null &&
+ buildServiceConfig.getJKubeBuildStrategy().equals(JKubeBuildStrategy.buildpacks);
+ }
+
+ @Override
+ public void postProcess() {
+ // NOOP
+ }
+}
diff --git a/jkube-kit/config/service/src/main/resources/META-INF/jkube/build-service b/jkube-kit/config/service/src/main/resources/META-INF/jkube/build-service
index 2922021448..cb5f809098 100644
--- a/jkube-kit/config/service/src/main/resources/META-INF/jkube/build-service
+++ b/jkube-kit/config/service/src/main/resources/META-INF/jkube/build-service
@@ -1,3 +1,4 @@
org.eclipse.jkube.kit.config.service.kubernetes.JibBuildService,100
+org.eclipse.jkube.kit.config.service.kubernetes.BuildPackBuildService,110
org.eclipse.jkube.kit.config.service.openshift.OpenshiftBuildService,200
org.eclipse.jkube.kit.config.service.kubernetes.DockerBuildService,9999
\ No newline at end of file
diff --git a/jkube-kit/config/service/src/test/java/org/eclipse/jkube/kit/config/service/JKubeServiceHubBuildServiceTest.java b/jkube-kit/config/service/src/test/java/org/eclipse/jkube/kit/config/service/JKubeServiceHubBuildServiceTest.java
index 0472df2b47..9df6805012 100644
--- a/jkube-kit/config/service/src/test/java/org/eclipse/jkube/kit/config/service/JKubeServiceHubBuildServiceTest.java
+++ b/jkube-kit/config/service/src/test/java/org/eclipse/jkube/kit/config/service/JKubeServiceHubBuildServiceTest.java
@@ -21,6 +21,7 @@
import org.eclipse.jkube.kit.common.util.LazyBuilder;
import org.eclipse.jkube.kit.config.image.build.JKubeBuildStrategy;
import org.eclipse.jkube.kit.config.resource.RuntimeMode;
+import org.eclipse.jkube.kit.config.service.kubernetes.BuildPackBuildService;
import org.eclipse.jkube.kit.config.service.kubernetes.DockerBuildService;
import org.eclipse.jkube.kit.config.service.kubernetes.JibBuildService;
import org.eclipse.jkube.kit.config.service.openshift.OpenshiftBuildService;
@@ -42,6 +43,7 @@ static Stream data() {
arguments(RuntimeMode.KUBERNETES, JKubeBuildStrategy.docker, DockerBuildService.class),
arguments(RuntimeMode.KUBERNETES, JKubeBuildStrategy.s2i, DockerBuildService.class),
arguments(RuntimeMode.KUBERNETES, JKubeBuildStrategy.jib, JibBuildService.class),
+ arguments(RuntimeMode.KUBERNETES, JKubeBuildStrategy.buildpacks, BuildPackBuildService.class),
arguments(RuntimeMode.OPENSHIFT, null, OpenshiftBuildService.class),
arguments(RuntimeMode.OPENSHIFT, JKubeBuildStrategy.docker, OpenshiftBuildService.class),
arguments(RuntimeMode.OPENSHIFT, JKubeBuildStrategy.s2i, OpenshiftBuildService.class),
diff --git a/jkube-kit/config/service/src/test/java/org/eclipse/jkube/kit/config/service/kubernetes/BuildPackBuildServiceTest.java b/jkube-kit/config/service/src/test/java/org/eclipse/jkube/kit/config/service/kubernetes/BuildPackBuildServiceTest.java
new file mode 100644
index 0000000000..effbac31db
--- /dev/null
+++ b/jkube-kit/config/service/src/test/java/org/eclipse/jkube/kit/config/service/kubernetes/BuildPackBuildServiceTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2019 Red Hat, Inc.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at:
+ *
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ */
+package org.eclipse.jkube.kit.config.service.kubernetes;
+
+import org.eclipse.jkube.kit.common.JKubeConfiguration;
+import org.eclipse.jkube.kit.common.KitLogger;
+import org.eclipse.jkube.kit.common.TestHttpStaticServer;
+import org.eclipse.jkube.kit.common.util.EnvUtil;
+import org.eclipse.jkube.kit.common.util.FileUtil;
+import org.eclipse.jkube.kit.config.image.ImageConfiguration;
+import org.eclipse.jkube.kit.config.image.build.BuildConfiguration;
+import org.eclipse.jkube.kit.config.image.build.JKubeBuildStrategy;
+import org.eclipse.jkube.kit.config.resource.RuntimeMode;
+import org.eclipse.jkube.kit.config.service.BuildServiceConfig;
+import org.eclipse.jkube.kit.config.service.JKubeServiceHub;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+class BuildPackBuildServiceTest {
+ private KitLogger kitLogger;
+ private JKubeServiceHub jKubeServiceHub;
+ private static final String TEST_PACK_VERSION = "v0.32.1";
+ private ImageConfiguration imageConfiguration;
+ private BuildServiceConfig buildServiceConfig;
+
+ @TempDir
+ private File temporaryFolder;
+
+ @BeforeEach
+ void setUp() {
+ kitLogger = spy(new KitLogger.SilentLogger());
+ buildServiceConfig = BuildServiceConfig.builder()
+ .jKubeBuildStrategy(JKubeBuildStrategy.buildpacks)
+ .build();
+ jKubeServiceHub = JKubeServiceHub.builder()
+ .log(kitLogger)
+ .platformMode(RuntimeMode.KUBERNETES)
+ .buildServiceConfig(buildServiceConfig)
+ .configuration(JKubeConfiguration.builder().build())
+ .build();
+ imageConfiguration = ImageConfiguration.builder()
+ .name("foo/bar:latest")
+ .build(BuildConfiguration.builder()
+ .from("foo/base:latest")
+ .build())
+ .build();
+ Map properties = new HashMap<>();
+ properties.put("user.home", temporaryFolder.getAbsolutePath());
+ properties.put("os.name", System.getProperty("os.name"));
+ properties.put("os.arch", System.getProperty("os.arch"));
+ Map env = new HashMap<>();
+ env.put("HOME", temporaryFolder.getAbsolutePath());
+ env.put("PATH", temporaryFolder.toPath().resolve("bin").toFile().getAbsolutePath());
+ EnvUtil.overrideEnvGetter(env::get);
+ EnvUtil.overridePropertyGetter(properties::get);
+ }
+
+ @AfterEach
+ void tearDown() {
+ EnvUtil.overrideEnvGetter(System::getenv);
+ EnvUtil.overridePropertyGetter(System::getProperty);
+ }
+
+ @ParameterizedTest
+ @CsvSource({
+ "s2i,false", "jib,false", "docker,false", "buildpacks,true"
+ })
+ void isApplicable_withGivenStrategy_shouldReturnTrueOnlyForBuildPackStrategy(String buildStrategyValue, boolean expectedResult) {
+ // Given
+ jKubeServiceHub = jKubeServiceHub.toBuilder()
+ .buildServiceConfig(buildServiceConfig.toBuilder()
+ .jKubeBuildStrategy(JKubeBuildStrategy.valueOf(buildStrategyValue))
+ .build())
+ .build();
+ // When
+ final boolean result = new BuildPackBuildService(jKubeServiceHub).isApplicable();
+ // Then
+ assertThat(result).isEqualTo(expectedResult);
+ }
+
+
+ @Nested
+ @DisplayName("buildImage")
+ class BuildImage {
+ private TestHttpStaticServer server;
+ private BuildPackBuildService buildPackBuildService;
+
+ @BeforeEach
+ void setUp() throws IOException {
+ File remoteDirectory = new File(Objects.requireNonNull(getClass().getResource("/artifacts")).getFile());
+ File downloadArtifactsDir = temporaryFolder.toPath().resolve("download-artifacts").toFile();
+ FileUtil.copyDirectoryIfNotExists(remoteDirectory, downloadArtifactsDir);
+ server = new TestHttpStaticServer(downloadArtifactsDir);
+ Properties properties = new Properties();
+ properties.put("version", TEST_PACK_VERSION);
+ properties.put("windows.binary-extension", "bat");
+ properties.put("linux.artifact", String.format("http://localhost:%d/pack-%s-linux.tgz", server.getPort(), TEST_PACK_VERSION));
+ properties.put("linux-arm64.artifact", String.format("http://localhost:%d/pack-%s-linux-arm64.tgz", server.getPort(), TEST_PACK_VERSION));
+ properties.put("macos.artifact", String.format("http://localhost:%d/pack-%s-macos.tgz", server.getPort(), TEST_PACK_VERSION));
+ properties.put("macos-arm64.artifact", String.format("http://localhost:%d/pack-%s-macos-arm64.tgz", server.getPort(), TEST_PACK_VERSION));
+ properties.put("windows.artifact", String.format("http://localhost:%d/pack-%s-windows.zip", server.getPort(), TEST_PACK_VERSION));
+ buildPackBuildService = new BuildPackBuildService(jKubeServiceHub, properties);
+ }
+
+ @AfterEach
+ void tearDown() throws IOException {
+ server.close();
+ }
+
+ @Test
+ @DisplayName("When default builder configured in .pack/config.toml, then use that builder image")
+ void whenLocalPackConfigHasDefaultBuilderSet_thenUseThatBuilder() throws IOException {
+ // Given
+ givenLocalPackConfigExistsWithContent(temporaryFolder, String.format("default-builder-image=\"%s\"", "cnbs/sample-builder:bionic"));
+
+ // When
+ buildPackBuildService.buildSingleImage(imageConfiguration);
+
+ // Then
+ verify(kitLogger).info("[[s]]%s","build foo/bar:latest --builder cnbs/sample-builder:bionic --creation-time now");
+ }
+
+ @Test
+ @DisplayName("When .pack/config.toml invalid, then use opinionated builder image")
+ void whenLocalPackConfigInvalid_thenUseOpinionatedBuilderImage() throws IOException {
+ // Given
+ givenLocalPackConfigExistsWithContent(temporaryFolder, "default-builder-image@@=");
+
+ // When
+ buildPackBuildService.buildSingleImage(imageConfiguration);
+
+ // Then
+ verify(kitLogger).info("[[s]]%s","build foo/bar:latest --builder paketobuildpacks/builder:base --creation-time now");
+ }
+
+ @Test
+ @DisplayName("When no default builder configured in .pack/config.toml, then use opinionated builder image")
+ void whenLocalPackCLIAndNoDefaultBuilderInPackConfig_thenUseOpinionatedBuilderImage() {
+ // When
+ buildPackBuildService.buildSingleImage(imageConfiguration);
+
+ // Then
+ verify(kitLogger).info("[[s]]%s", "build foo/bar:latest --builder paketobuildpacks/builder:base --creation-time now");
+ }
+ }
+
+ private void givenLocalPackConfigExistsWithContent(File userHome, String content) throws IOException {
+ File packHome = new File(userHome, ".pack");
+ Files.createDirectory(packHome.toPath());
+ File packConfig = new File(packHome, "config.toml");
+ Files.write(packConfig.toPath(), content.getBytes());
+ }
+}
diff --git a/jkube-kit/parent/pom.xml b/jkube-kit/parent/pom.xml
index 74815950f6..6e337abd7d 100644
--- a/jkube-kit/parent/pom.xml
+++ b/jkube-kit/parent/pom.xml
@@ -159,6 +159,13 @@
${project.version}
test-jar
+
+ org.eclipse.jkube
+ jkube-kit-build-service-buildpacks
+ ${project.version}
+ test
+ test-jar
+
org.eclipse.jkube
@@ -215,6 +222,12 @@
${project.version}
+
+ org.eclipse.jkube
+ jkube-kit-build-service-buildpacks
+ ${project.version}
+
+
org.eclipse.jkube
jkube-kit-watcher-api
diff --git a/jkube-kit/pom.xml b/jkube-kit/pom.xml
index ec4bfc835f..5143bf4faa 100644
--- a/jkube-kit/pom.xml
+++ b/jkube-kit/pom.xml
@@ -38,11 +38,11 @@
common-maven
config/image
config/resource
- config/service
build/api
build/service/buildpacks
build/service/docker
build/service/jib
+ config/service
generator/api
generator/java-exec
generator/karaf