Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat (jkube-kit/config/service) : Add BuildPackBuildService (#2493) #2448

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.common.util.FileUtil;
import org.eclipse.jkube.kit.common.util.PropertiesUtil;

import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -51,10 +52,19 @@ public class BuildPackCliDownloader {
private final Properties packProperties;
private final File jKubeUserHomeDir;

public BuildPackCliDownloader(KitLogger kitLogger) {
this(kitLogger, null);
}

public BuildPackCliDownloader(KitLogger kitLogger, Properties packProperties) {
this.kitLogger = kitLogger;
this.packProperties = packProperties;
packCliVersion = (String) packProperties.get(PACK_DEFAULT_CLI_VERSION_PROPERTY);
if (packProperties != null) {
this.packProperties = packProperties;
} else {
this.packProperties = PropertiesUtil
.getPropertiesFromResource(BuildPackCliDownloader.class.getResource("/META-INF/jkube/pack-cli.properties"));
}
packCliVersion = this.packProperties.getProperty(PACK_DEFAULT_CLI_VERSION_PROPERTY);
jKubeUserHomeDir = new File(getUserHome(), JKUBE_PACK_DIR);
}

Expand Down Expand Up @@ -141,4 +151,4 @@ private Path resolveBinaryLocation() {
}
return jKubeUserHomeDir.toPath().resolve(binaryName);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -46,6 +48,18 @@ public static Properties getPropertiesFromResource(URL resource) {
return ret;
}

public static Properties readProperties(Path properties) {
final Properties ret = new Properties();
if (properties != null && properties.toFile().exists() && properties.toFile().isFile()) {
try (InputStream stream = Files.newInputStream(properties)) {
ret.load(stream);
} catch (IOException e) {
throw new IllegalStateException("Error while reading properties from file " + properties, e);
}
}
return ret;
}

/**
* Return first Non-Null set property from a set of provided properties
*
Expand Down
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-helm</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* 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.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.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.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;
import static org.eclipse.jkube.kit.common.util.EnvUtil.getUserHome;
import static org.eclipse.jkube.kit.common.util.PropertiesUtil.readProperties;

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 BuildPackCliDownloader buildPackCliDownloader;

public BuildPackBuildService(JKubeServiceHub jKubeServiceHub) {
this(jKubeServiceHub, null);
}

BuildPackBuildService(JKubeServiceHub jKubeServiceHub, Properties packProperties) {
super(jKubeServiceHub);
this.buildServiceConfig = Objects.requireNonNull(jKubeServiceHub.getBuildServiceConfig(),
"BuildServiceConfig is required");
this.kitLogger = Objects.requireNonNull(jKubeServiceHub.getLog());
if (packProperties == null) {
this.buildPackCliDownloader = new BuildPackCliDownloader(kitLogger);
} else {
this.buildPackCliDownloader = new BuildPackCliDownloader(kitLogger, packProperties);
}
}

@Override
protected void buildSingleImage(ImageConfiguration imageConfiguration) {
kitLogger.info("Delegating container image building process to BuildPacks");
final File packCli = buildPackCliDownloader.getPackCLIIfPresentOrDownload();
kitLogger.info("Using pack %s", packCli.getAbsolutePath());
final String builderImage;
final Properties localPackConfig =
readProperties(getUserHome().toPath().resolve(PACK_CONFIG_DIR).resolve(PACK_CONFIG_FILE));
if (localPackConfig.get("default-builder-image") != null) {
builderImage = strip(localPackConfig.getProperty("default-builder-image"), "\"");
} else {
builderImage = DEFAULT_BUILDER_IMAGE;
}
new BuildPackCliController(packCli, kitLogger)
.build(BuildPackBuildOptions.builder()
.imageName(imageConfiguration.getName())
.builderImage(builderImage)
.creationTime("now")
.build());
}

@Override
protected void pushSingleImage(ImageConfiguration imageConfiguration, int retries, RegistryConfig registryConfig, boolean skipTag) {
// NO OP
}

@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,190 @@
/*
* 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.TestHttpBuildPacksArtifactsServer;
import org.eclipse.jkube.kit.common.util.EnvUtil;
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.Properties;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

class BuildPackBuildServiceTest {

private static final String TEST_PACK_VERSION = "v0.32.1";
private KitLogger kitLogger;
private JKubeServiceHub jKubeServiceHub;
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<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);
}

@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 TestHttpBuildPacksArtifactsServer server;
private BuildPackBuildService buildPackBuildService;

@BeforeEach
void setUp() {
server = new TestHttpBuildPacksArtifactsServer();
Properties packProperties = new Properties();
packProperties.put("version", TEST_PACK_VERSION);
packProperties.put("windows.binary-extension", "bat");
buildPackBuildService = new BuildPackBuildService(jKubeServiceHub, packProperties);
packProperties.put("linux.artifact", server.getLinuxArtifactUrl());
packProperties.put("linux-arm64.artifact", server.getLinuxArm64ArtifactUrl());
packProperties.put("macos.artifact", server.getMacosArtifactUrl());
packProperties.put("macos-arm64.artifact", server.getMacosArm64ArtifactUrl());
packProperties.put("windows.artifact", server.getWindowsArtifactUrl());
packProperties.put("windows.binary-extension", "bat");
}

@AfterEach
void tearDown() throws IOException {
server.close();
}

@Nested
@DisplayName("local .pack/config.toml exists")
class LocalPackConfigExists {
private File localPackConfig;

@BeforeEach
void setUp() throws IOException {
File packHome = new File(temporaryFolder, ".pack");
Files.createDirectory(packHome.toPath());
localPackConfig = new File(packHome, "config.toml");
}

@Test
@DisplayName("When default builder configured in .pack/config.toml, then use that builder image")
void whenLocalPackConfigHasDefaultBuilderSet_thenUseThatBuilder() throws IOException {
// Given
Files.write(localPackConfig.toPath(), String.format("default-builder-image=\"%s\"", "cnbs/sample-builder:bionic").getBytes());

// 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
Files.write(localPackConfig.toPath(), "default-builder-image@@=".getBytes());

// When
buildPackBuildService.buildSingleImage(imageConfiguration);

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

@Nested
@DisplayName("Local .pack/config.toml absent")
class LocalPackConfigAbsent {
@Test
@DisplayName("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");
}
}
}
}
Loading