Skip to content

Commit

Permalink
feat (jkube-kit/config/service) : Add BuildPackBuildService (#2493)
Browse files Browse the repository at this point in the history
+ Add new enum for buildpacks in JKubeBuildStrategy
+ Add BuildPackBuildService that would be activated when build strategy
  is set to buildpacks

Signed-off-by: Rohan Kumar <[email protected]>
  • Loading branch information
rohanKanojia committed Dec 8, 2023
1 parent ed628ab commit 96fc976
Show file tree
Hide file tree
Showing 7 changed files with 303 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions jkube-kit/config/service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
<groupId>org.eclipse.jkube</groupId>
<artifactId>jkube-kit-build-service-jib</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jkube</groupId>
<artifactId>jkube-kit-build-service-buildpacks</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jkube</groupId>
<artifactId>jkube-kit-resource-helm</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* 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.controller.BuildPackCliController;

import static org.apache.commons.lang3.StringUtils.strip;
import static org.eclipse.jkube.kit.common.util.EnvUtil.findBinaryFileInUserPath;

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;

public BuildPackBuildService(JKubeServiceHub jKubeServiceHub) {
super(jKubeServiceHub);
this.buildServiceConfig = Objects.requireNonNull(jKubeServiceHub.getBuildServiceConfig(),
"BuildServiceConfig is required");
this.kitLogger = Objects.requireNonNull(jKubeServiceHub.getLog());
}

@Override
protected void buildSingleImage(ImageConfiguration imageConfiguration) {
kitLogger.info("Delegating container image building process to BuildPacks");
doBuildPackBuild(imageConfiguration);
}

private void doBuildPackBuild(ImageConfiguration imageConfiguration) {
Properties packProperties = PropertiesUtil.getPropertiesFromResource(BuildPackBuildService.class.getResource("/META-INF/jkube/pack-cli.properties"));
File packCli = getLocalPackCLI(packProperties);
kitLogger.info("Using pack %s", packCli.getAbsolutePath());
BuildPackCliController packCliController = new BuildPackCliController(packCli, kitLogger);
BuildPackBuildOptions buildOptions = createBuildPackOptions(imageConfiguration);
packCliController.build(buildOptions);
}

private File getLocalPackCLI(Properties packProperties) {
kitLogger.info("Checking for local pack CLI");
String applicableBinaryProperty = String.format("%s.binary-name", EnvUtil.isWindows() ? "windows" : "unix");
String platformBinaryName = (String) packProperties.get(applicableBinaryProperty);
File pack = findBinaryFileInUserPath(platformBinaryName);
if (pack == null) {
throw new IllegalStateException("No local pack binary found");
}
return pack;
}

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
}
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -42,6 +43,7 @@ static Stream<Arguments> 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),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* 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.KitLogger;
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.BuildConfiguration;
import org.eclipse.jkube.kit.config.image.build.JKubeBuildStrategy;
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.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.mockito.MockedStatic;

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 java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class BuildPackBuildServiceTest {
private KitLogger kitLogger;
private JKubeServiceHub mockedServiceHub;
private static final String TEST_PACK_VERSION = "v0.32.1";
private String applicablePackBinary;
private ImageConfiguration imageConfiguration;
private MockedStatic<PropertiesUtil> propertiesUtilMockedStatic;

@TempDir
private File temporaryFolder;

@BeforeEach
void setUp() {
mockedServiceHub = mock(JKubeServiceHub.class, RETURNS_DEEP_STUBS);
kitLogger = spy(new KitLogger.SilentLogger());
when(mockedServiceHub.getLog()).thenReturn(kitLogger);
imageConfiguration = ImageConfiguration.builder()
.name("foo/bar:latest")
.build(BuildConfiguration.builder()
.from("foo/base:latest")
.build())
.build();
applicablePackBinary = EnvUtil.isWindows() ? "pack.bat" : "pack";
Map<String, String> 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<String, String> 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);
propertiesUtilMockedStatic = mockStatic(PropertiesUtil.class);
}

@AfterEach
void tearDown() {
EnvUtil.overrideEnvGetter(System::getenv);
EnvUtil.overridePropertyGetter(System::getProperty);
propertiesUtilMockedStatic.close();
}

@Test
void isApplicable_withNoBuildStrategy_shouldReturnFalse() {
// When
final boolean result = new BuildPackBuildService(mockedServiceHub).isApplicable();
// Then
assertThat(result).isFalse();
}

@ParameterizedTest
@CsvSource({
"s2i,false", "jib,false", "docker,false", "buildpacks,true"
})
void isApplicable_withGivenStrategy_shouldReturnTrueOnlyForBuildPackStrategy(String buildStrategyValue, boolean expectedResult) {
// Given
when(mockedServiceHub.getBuildServiceConfig().getJKubeBuildStrategy()).thenReturn(JKubeBuildStrategy.valueOf(buildStrategyValue));
// When
final boolean result = new BuildPackBuildService(mockedServiceHub).isApplicable();
// Then
assertThat(result).isEqualTo(expectedResult);
}


@Test
void buildImage_whenLocalPackCLIAndPackConfigHasDefaultBuilderSet_thenUseThatBuilder() throws IOException {
// Given
givenPackConfigHasDefaultBuilder(temporaryFolder);
propertiesUtilMockedStatic.when(() -> PropertiesUtil.getPropertiesFromResource(any()))
.thenReturn(createPackProperties("http://localhost.idontexist"))
.thenCallRealMethod();
givenPackCliPresentOnUserMachine(String.format("/%s", applicablePackBinary));

// When
new BuildPackBuildService(mockedServiceHub).buildSingleImage(imageConfiguration);

// Then
verify(kitLogger).info("[[s]]%s","build foo/bar:latest --builder cnbs/sample-builder:bionic --creation-time now");
}

@Test
void buildImage_whenLocalPackCLIAndNoDefaultBuilderInPackConfig_thenUseOpinionatedBuilderImage() throws IOException {
propertiesUtilMockedStatic.when(() -> PropertiesUtil.getPropertiesFromResource(any()))
.thenReturn(createPackProperties("http://localhost.idontexist"));
givenPackCliPresentOnUserMachine(String.format("/%s", applicablePackBinary));

// When
new BuildPackBuildService(mockedServiceHub).buildSingleImage(imageConfiguration);

// Then
verify(kitLogger).info("[[s]]%s", "build foo/bar:latest --builder paketobuildpacks/builder:base --creation-time now");
}

private void givenPackCliPresentOnUserMachine(String packResource) throws IOException {
File bin = new File(temporaryFolder, "bin");
File pack = new File(Objects.requireNonNull(getClass().getResource(packResource)).getFile());
Files.createDirectory(bin.toPath());
Files.copy(pack.toPath(), bin.toPath().resolve(pack.getName()), COPY_ATTRIBUTES);
}

private Properties createPackProperties(String baseUrl) {
Properties properties = new Properties();
properties.put("unix.binary-name", applicablePackBinary);
properties.put("windows.binary-name", applicablePackBinary);
properties.put("version", TEST_PACK_VERSION);
properties.put("linux.artifact", baseUrl + "pack-" + TEST_PACK_VERSION + "-linux.tgz");
properties.put("linux-arm64.artifact", baseUrl + "pack-" + TEST_PACK_VERSION + "-linux-arm64.tgz");
properties.put("macos.artifact", baseUrl + "pack-" + TEST_PACK_VERSION + "-macos.tgz");
properties.put("macos-arm64.artifact", baseUrl + "pack-" + TEST_PACK_VERSION + "-macos-arm64.tgz");
properties.put("windows.artifact", baseUrl + "pack-" + TEST_PACK_VERSION + "-windows.zip");
return properties;
}

private void givenPackConfigHasDefaultBuilder(File userHome) throws IOException {
File packHome = new File(userHome, ".pack");
Files.createDirectory(packHome.toPath());
File packConfig = new File(packHome, "config.toml");
Files.write(packConfig.toPath(), String.format("default-builder-image=\"%s\"", "cnbs/sample-builder:bionic").getBytes());
}
}
6 changes: 6 additions & 0 deletions jkube-kit/parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.eclipse.jkube</groupId>
<artifactId>jkube-kit-build-service-buildpacks</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.eclipse.jkube</groupId>
<artifactId>jkube-kit-watcher-api</artifactId>
Expand Down

0 comments on commit 96fc976

Please sign in to comment.