Skip to content

Commit

Permalink
feat (jkube-kit/config/service) : Add support for Buildpacks
Browse files Browse the repository at this point in the history
+ Add new build strategy called `buildpacks`
+ Add new BuildpacksBuildService that downloads pack cli from GitHub and
  executes a command to create a container image

Signed-off-by: Rohan Kumar <[email protected]>
  • Loading branch information
rohanKanojia committed Nov 16, 2023
1 parent 33bab59 commit a1291ac
Show file tree
Hide file tree
Showing 24 changed files with 1,369 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ _**Note**_:
- Container Images generated using jkube opinionated defaults no longer contain full timestamp in `org.label-schema.build-date` label. The label contains the build date in the format `yyyy-MM-dd`.

### 1.14.0 (2023-08-31)
* Fix #439: Add new build strategy for Buildpacks
* Fix #1674: SpringBootGenerator utilizes the layered jar if present and use it as Docker layers
* Fix #1713: Add HelidonHealthCheckEnricher to add Kubernetes health checks for Helidon applications
* Fix #1714: Add HelidonGenerator to add opinionated container image for Helidon applications
Expand Down
59 changes: 59 additions & 0 deletions jkube-kit/build/service/buildpacks/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.eclipse.jkube</groupId>
<artifactId>jkube-kit-parent</artifactId>
<version>1.16-SNAPSHOT</version>
<relativePath>../../../parent/pom.xml</relativePath>
</parent>

<artifactId>jkube-kit-build-service-buildpacks</artifactId>

<name>JKube Kit :: Build :: Service :: Buildpacks</name>

<dependencies>
<dependency>
<groupId>org.eclipse.jkube</groupId>
<artifactId>jkube-kit-build-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
</dependency>

</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* 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.service.buildpacks;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.jkube.kit.common.ExternalCommand;
import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.config.image.ImageName;
import org.eclipse.jkube.kit.config.image.build.BuildPackConfiguration;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class BuildPackBuildImageCommand extends ExternalCommand {
private final String imageName;
private final KitLogger logger;
private final String cliPath;
private final BuildPackConfiguration buildpackConfiguration;

protected BuildPackBuildImageCommand(KitLogger log, String imageName, String cliPath, BuildPackConfiguration buildpackConfiguration) {
super(log);
this.logger = log;
this.imageName = imageName;
this.cliPath = cliPath;
this.buildpackConfiguration = buildpackConfiguration;
}

@Override
protected String[] getArgs() {
List<String> args = new ArrayList<>();
args.add(cliPath);
args.add("build");
args.add(imageName);
args.addAll(extractStringArg("--builder", buildpackConfiguration.getBuilderImage()));
args.addAll(extractStringArg("--buildpack-registry", buildpackConfiguration.getRegistry()));
args.addAll(extractStringArg("--cache", buildpackConfiguration.getCache()));
args.addAll(extractStringArg("--cache-image", buildpackConfiguration.getCacheImage()));
args.addAll(extractStringArg("--creation-time", buildpackConfiguration.getCreationTime()));
args.addAll(extractStringArg("--default-process", buildpackConfiguration.getDefaultProcess()));
args.addAll(extractStringArg("--descriptor", buildpackConfiguration.getDescriptor()));
args.addAll(extractStringArg("--docker-host", buildpackConfiguration.getDockerHost()));
args.addAll(extractStringArg("--lifecycle-image", buildpackConfiguration.getLifecycleImage()));
args.addAll(extractStringArg("--network", buildpackConfiguration.getNetwork()));
args.addAll(extractStringArg("--path", buildpackConfiguration.getPath()));
args.addAll(extractStringArg("--previous-image", buildpackConfiguration.getPreviousImage()));
args.addAll(extractStringArg("--pull-policy", buildpackConfiguration.getPullPolicy()));
args.addAll(extractStringArg("--report-output-dir", buildpackConfiguration.getReportOutputDir()));
args.addAll(extractStringArg("--run-image", buildpackConfiguration.getRunImage()));
args.addAll(extractStringArg("--sbom-output-dir", buildpackConfiguration.getSbomOutputDir()));
args.addAll(extractStringArg("--workspace", buildpackConfiguration.getWorkspace()));
args.addAll(extractBooleanArg("--clear-cache", buildpackConfiguration.isClearCache()));
args.addAll(extractBooleanArg("--publish", buildpackConfiguration.isPublish()));
args.addAll(extractBooleanArg("--trust-builder", buildpackConfiguration.isTrustBuilder()));
args.addAll(extractRepeatedArgsForListElements("--env-file", buildpackConfiguration.getEnvFiles()));
args.addAll(extractRepeatedArgsForListElements("--post-buildpack", buildpackConfiguration.getPostBuildpacks()));
args.addAll(extractRepeatedArgsForListElements("--pre-buildpack", buildpackConfiguration.getPreBuildpacks()));
args.addAll(extractRepeatedArgsForListElements("--volume", buildpackConfiguration.getVolumes()));
args.addAll(extractRepeatedArgsForListElements("--buildpack", buildpackConfiguration.getBuildpacks()));
args.addAll(extractRepeatedArgsForListElements("--extension", buildpackConfiguration.getExtensions()));
if (buildpackConfiguration.getEnv() != null) {
List<String> keyValueEntryList = buildpackConfiguration.getEnv().entrySet().stream()
.map(this::getSingleEnvArg)
.collect(Collectors.toList());
args.addAll(extractRepeatedArgsForListElements("--env", keyValueEntryList));
}
if (buildpackConfiguration.getGid() > 0) {
args.add("--gid");
args.add(String.valueOf(buildpackConfiguration.getGid()));
}
if (buildpackConfiguration.getTags() != null && !buildpackConfiguration.getTags().isEmpty()) {
ImageName specifiedImageName = new ImageName(imageName);
List<String> imageNameWithAdditionalTags = buildpackConfiguration.getTags().stream()
.map(t -> specifiedImageName.getNameWithoutTag() + ":" + t)
.collect(Collectors.toList());
args.addAll(extractRepeatedArgsForListElements("--tag", imageNameWithAdditionalTags));
}

return args.toArray(new String[0]);
}

@Override
protected void processLine(String line) {
logger.info("[[s]]%s", line);
}

private List<String> extractBooleanArg(String flag, boolean flagValue) {
List<String> args = new ArrayList<>();
if (flagValue) {
args.add(flag);
}
return args;
}

private List<String> extractStringArg(String flag, String flagValue) {
List<String> args = new ArrayList<>();
if (StringUtils.isNotBlank(flagValue)) {
args.add(flag);
args.add(flagValue);
}
return args;
}

private List<String> extractRepeatedArgsForListElements(String flag, List<String> flagValues) {
List<String> args = new ArrayList<>();
if (flagValues != null && !flagValues.isEmpty()) {
flagValues.forEach(v -> args.addAll(extractStringArg(flag, v)));
}
return args;
}

private String getSingleEnvArg(Map.Entry<String, String> e) {
String entryKeyValue = e.getKey();
if (StringUtils.isNotBlank(e.getValue())) {
entryKeyValue = entryKeyValue + "=" + e.getValue();
}
return entryKeyValue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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.service.buildpacks;

import java.io.File;
import java.io.IOException;

import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.common.util.FileUtil;
import org.eclipse.jkube.kit.config.image.build.BuildPackConfiguration;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;

import static org.eclipse.jkube.kit.common.util.GitHubCliDownloaderUtil.downloadCLIFromGitHub;
import static org.eclipse.jkube.kit.common.util.GitHubCliDownloaderUtil.getApplicableBinary;
import static org.eclipse.jkube.kit.common.util.GitHubCliDownloaderUtil.getDownloadPlatform;

public class BuildPackBuildUtil {
private static final String DEFAULT_PACK_CLI_VERSION = "v0.31.0";
private static final String PACK_CLI_DOWNLOADS_LINK = "https://github.com/buildpacks/pack/releases/download/%s";
private static final String PACK_CLI_ARTIFACT_PREFIX = "pack";
private static final String PACK_UNIX_TAR_SUFFIX = ".tgz";
private static final String PACK_WINDOWS_ZIP_SUFFIX = ".zip";

private BuildPackBuildUtil() { }

public static void buildImage(KitLogger kitLogger, File baseDirectory, String outputDirectory, String imageName, BuildPackConfiguration buildPackConfiguration) {
String cliPath = getPackCLIIfPresentOrDownload(kitLogger, baseDirectory, new File(baseDirectory, outputDirectory));
executeBuildPacksCLIBuildImageTask(kitLogger, cliPath, imageName, buildPackConfiguration);
}

private static void executeBuildPacksCLIBuildImageTask(KitLogger kitLogger, String cliPath, String imageName, BuildPackConfiguration buildPackConfiguration) {
BuildPackBuildImageCommand buildPackBuildImageCommand = new BuildPackBuildImageCommand(kitLogger, imageName, cliPath, buildPackConfiguration);
try {
kitLogger.info("Using pack CLI %s", cliPath);
buildPackBuildImageCommand.execute();
} catch (IOException e) {
throw new IllegalStateException("Failure in executing pack command", e);
}
}

private static String getPackCLIIfPresentOrDownload(KitLogger kitLogger, File baseDir, File outputDirectory) {
try {
String packCliDownloadBaseUrl = String.format(PACK_CLI_DOWNLOADS_LINK, DEFAULT_PACK_CLI_VERSION);
String packCliArtifactSuffix = SystemUtils.IS_OS_WINDOWS ? PACK_WINDOWS_ZIP_SUFFIX : PACK_UNIX_TAR_SUFFIX;
String downloadedPackCliPath = getExpectedPackCliDownloadedPath(outputDirectory);
if (StringUtils.isBlank(downloadedPackCliPath)) {
kitLogger.info("Downloading pack CLI %s", DEFAULT_PACK_CLI_VERSION);
downloadedPackCliPath = downloadCLIFromGitHub(kitLogger, packCliDownloadBaseUrl, getDownloadPlatform(), PACK_CLI_ARTIFACT_PREFIX, DEFAULT_PACK_CLI_VERSION, packCliArtifactSuffix, outputDirectory);
}
return FileUtil.getRelativeFilePath(baseDir.getAbsolutePath(), downloadedPackCliPath);
} catch (IOException ioException) {
kitLogger.info("Not able to download pack CLI : " + ioException.getMessage());
kitLogger.info("Checking for local pack CLI");
String packCliFoundLocally = checkPackCLIPresentOnMachine(kitLogger);
if (StringUtils.isNotBlank(packCliFoundLocally)) {
return packCliFoundLocally;
}
throw new IllegalStateException("No local pack binary found, Not able to download pack CLI : " + ioException.getMessage());
}
}

private static String getExpectedPackCliDownloadedPath(File outputDirectory) {
if (outputDirectory != null && outputDirectory.exists()) {
File extractedPackDir = new File(outputDirectory, String.format("pack-%s-%s", DEFAULT_PACK_CLI_VERSION, getDownloadPlatform()));
if (extractedPackDir.exists()) {
File[] foundPackCliList = extractedPackDir.listFiles(f -> f.getName().startsWith("pack") && f.canExecute());
if (foundPackCliList != null && foundPackCliList.length > 0) {
return foundPackCliList[0].getAbsolutePath();
}
}
}
return null;
}

private static String checkPackCLIPresentOnMachine(KitLogger kitLogger) {
String packCLIVersion = getLocalPackCLIVersion(kitLogger);
if (StringUtils.isNotBlank(packCLIVersion)) {
return getApplicableBinary(PACK_CLI_ARTIFACT_PREFIX);
}
return null;
}

private static String getLocalPackCLIVersion(KitLogger kitLogger) {
BuildPackVersionCommand versionCommand = new BuildPackVersionCommand(kitLogger);
try {
versionCommand.execute();
return versionCommand.getVersion();
} catch (IOException e) {
kitLogger.info("pack CLI not found");
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.service.buildpacks;

import lombok.Getter;
import org.eclipse.jkube.kit.common.ExternalCommand;
import org.eclipse.jkube.kit.common.KitLogger;

import static org.eclipse.jkube.kit.common.util.GitHubCliDownloaderUtil.getApplicableBinary;

@Getter
public class BuildPackVersionCommand extends ExternalCommand {
private String version;
private static final String PACK_CLI_ARTIFACT_PREFIX = "pack";

protected BuildPackVersionCommand(KitLogger log) {
super(log);
}

@Override
protected String[] getArgs() {
return new String[] { getApplicableBinary(PACK_CLI_ARTIFACT_PREFIX), "--version" };
}

@Override
protected void processLine(String line) {
version = line;
}
}
Loading

0 comments on commit a1291ac

Please sign in to comment.