Skip to content

Commit

Permalink
feat (jkube-kit/build/service/buildpacks) : Add BuildPackCliDownloader
Browse files Browse the repository at this point in the history
- Add a class BuildPackCliDownloader whose responsibility would be
  downloading pack binary
- In order to check whether already downloaded binary is working, add
  step for executing `pack --version` command

Signed-off-by: Rohan Kumar <rohaan@redhat.com>
  • Loading branch information
rohanKanojia authored and manusa committed Jan 17, 2024
1 parent 31ef8be commit a1fcb57
Show file tree
Hide file tree
Showing 26 changed files with 951 additions and 5 deletions.
38 changes: 38 additions & 0 deletions jkube-kit/build/service/buildpacks/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,43 @@
<name>JKube Kit :: Build :: Service :: Buildpacks</name>

<dependencies>
<dependency>
<groupId>org.eclipse.jkube</groupId>
<artifactId>jkube-kit-build-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jkube</groupId>
<artifactId>jkube-kit-common</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
</dependency>
</dependencies>

<build>
<resources>
<!-- Copy over default pack properties defined above -->
<resource>
<directory>src/main/resources-filtered</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* 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.KitLogger;
import org.eclipse.jkube.kit.common.util.FileUtil;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;

import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static org.eclipse.jkube.kit.common.util.EnvUtil.findBinaryFileInUserPath;
import static org.eclipse.jkube.kit.common.util.EnvUtil.getProcessorArchitecture;
import static org.eclipse.jkube.kit.common.util.EnvUtil.isMacOs;
import static org.eclipse.jkube.kit.common.util.EnvUtil.isWindows;
import static org.eclipse.jkube.kit.common.util.EnvUtil.getUserHome;
import static org.eclipse.jkube.kit.common.util.IoUtil.downloadArchive;
import static org.eclipse.jkube.kit.common.util.SemanticVersionUtil.removeBuildMetadata;

public class BuildPackCliDownloader {
private static final String JKUBE_PACK_DIR = ".jkube";
private static final String PACK_UNIX_CLI_NAME = "pack";
private static final String PACK_DEFAULT_CLI_VERSION_PROPERTY = "version";
private static final String PACK_CLI_LINUX_ARTIFACT = "linux.artifact";
private static final String PACK_CLI_LINUX_ARM64_ARTIFACT = "linux-arm64.artifact";
private static final String PACK_CLI_MACOS_ARTIFACT = "macos.artifact";
private static final String PACK_CLI_MACOS_ARM64_ARTIFACT = "macos-arm64.artifact";
private static final String PACK_CLI_WINDOWS_ARTIFACT = "windows.artifact";

private final KitLogger kitLogger;
private final String packCliVersion;
private final Properties packProperties;
private final File jKubeUserHomeDir;

public BuildPackCliDownloader(KitLogger kitLogger, Properties packProperties) {
this.kitLogger = kitLogger;
this.packProperties = packProperties;
packCliVersion = (String) packProperties.get(PACK_DEFAULT_CLI_VERSION_PROPERTY);
jKubeUserHomeDir = new File(getUserHome(), JKUBE_PACK_DIR);
}

public File getPackCLIIfPresentOrDownload() {
try {
File pack = resolveBinaryLocation().toFile();
if (!(pack.exists() && isValid(pack))) {
downloadPackCli();
}
return pack;
} catch (IOException ioException) {
kitLogger.warn("Not able to download pack CLI : " + ioException.getMessage());
kitLogger.info("Checking for local pack CLI");
return getLocalPackCLI();
}
}

private void downloadPackCli() throws IOException {
File tempDownloadDirectory = FileUtil.createTempDirectory();
FileUtil.createDirectory(jKubeUserHomeDir);
URL downloadUrl = new URL(inferApplicableDownloadArtifactUrl());
Path packInJKubeDir = resolveBinaryLocation();
kitLogger.info("Downloading pack CLI %s", packCliVersion);

downloadArchive(downloadUrl, tempDownloadDirectory);

File packInExtractedArchive = new File(tempDownloadDirectory, packInJKubeDir.toFile().getName());
if (!packInExtractedArchive.exists()) {
throw new IllegalStateException("Unable to find " + packInJKubeDir.toFile().getName() + " in downloaded artifact");
}
if (!packInExtractedArchive.canExecute() && !packInExtractedArchive.setExecutable(true)) {
throw new IllegalStateException("Failure in setting execute permission in " + packInExtractedArchive.getAbsolutePath());
}
Files.copy(packInExtractedArchive.toPath(), packInJKubeDir, REPLACE_EXISTING, COPY_ATTRIBUTES);
FileUtil.cleanDirectory(tempDownloadDirectory);
}

private String inferApplicableDownloadArtifactUrl() {
boolean isProcessorArchitectureArm = getProcessorArchitecture().equals("aarch64");
if (isWindows()) {
return (String) packProperties.get(PACK_CLI_WINDOWS_ARTIFACT);
} else if (isMacOs() && isProcessorArchitectureArm) {
return (String) packProperties.get(PACK_CLI_MACOS_ARM64_ARTIFACT);
} else if(isMacOs()) {
return (String) packProperties.get(PACK_CLI_MACOS_ARTIFACT);
} else if (isProcessorArchitectureArm) {
return (String) packProperties.get(PACK_CLI_LINUX_ARM64_ARTIFACT);
}
return (String) packProperties.get(PACK_CLI_LINUX_ARTIFACT);
}

private File getLocalPackCLI() {
File packCliFoundOnUserPath = checkPackCLIPresentOnMachine();
if (packCliFoundOnUserPath == null) {
throw new IllegalStateException("No local pack binary found");
}
return packCliFoundOnUserPath;
}

private File checkPackCLIPresentOnMachine() {
File packCliFoundOnUserPath = findBinaryFileInUserPath(resolveBinaryLocation().toFile().getName());
if (packCliFoundOnUserPath != null && isValid(packCliFoundOnUserPath)) {
return packCliFoundOnUserPath;
}
return null;
}

public boolean isValid(File packCli) {
AtomicReference<String> versionRef = new AtomicReference<>();
BuildPackCommand versionCommand = new BuildPackCommand(kitLogger, packCli, Collections.singletonList("--version"), versionRef::set);
try {
versionCommand.execute();
String version = removeBuildMetadata(versionRef.get());
return StringUtils.isNotBlank(version) && packCliVersion.equals(version);
} catch (IOException e) {
return false;
}
}

private Path resolveBinaryLocation() {
String binaryName = PACK_UNIX_CLI_NAME;
if (isWindows()) {
binaryName = PACK_UNIX_CLI_NAME + "." + packProperties.getProperty("windows.binary-extension");
}
return jKubeUserHomeDir.toPath().resolve(binaryName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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.eclipse.jkube.kit.common.ExternalCommand;
import org.eclipse.jkube.kit.common.KitLogger;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class BuildPackCommand extends ExternalCommand {
private final File packCli;
private final List<String> commandLineArgs;
private final StringBuilder errorBuilder;
private final Consumer<String> commandOutputConsumer;

public BuildPackCommand(KitLogger log, File packCli, List<String> cmdArgs, Consumer<String> outputConsumer) {
super(log);
this.packCli = packCli;
this.commandLineArgs = cmdArgs;
this.commandOutputConsumer = outputConsumer;
this.errorBuilder = new StringBuilder();
}

@Override
public String[] getArgs() {
List<String> args = new ArrayList<>();
args.add(packCli.getAbsolutePath());
args.addAll(commandLineArgs);
return args.toArray(new String[0]);
}

@Override
public void processLine(String line) {
commandOutputConsumer.accept(line);
}

@Override
public void processError(String error) {
errorBuilder.append(error);
}

public String getError() {
return errorBuilder.toString();
}

public int getExitCode() {
return getStatusCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#
# 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
#

version=${pack.version}
linux.artifact=${pack.linux.artifact}
linux-arm64.artifact=${pack.linux-arm64.artifact}
macos.artifact=${pack.macos.artifact}
macos-arm64.artifact=${pack.macos-arm64.artifact}
windows.artifact=${pack.windows.pack.artifact}
windows.binary-extension=${pack.windows.binary-extension}
Loading

0 comments on commit a1fcb57

Please sign in to comment.