Skip to content

Commit

Permalink
feat (jkube-kit/resource/helm) : Support for Pushing Helm Charts to O…
Browse files Browse the repository at this point in the history
…CI (eclipse-jkube#2091)

+ Add SHAUtil for generating SHA digests (used in oci helm push)
+ Add OCIRepositoryUploader as an additional HelmUploader
+ Add OCI repository related helper classes in
  `org.eclipse.jkube.kit.resource.helm.oci` package

Signed-off-by: Rohan Kumar <rohaan@redhat.com>
  • Loading branch information
rohanKanojia committed Jul 17, 2023
1 parent 552d5e9 commit 50c0dcf
Show file tree
Hide file tree
Showing 33 changed files with 1,680 additions and 126 deletions.
6 changes: 6 additions & 0 deletions jkube-kit/common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client-api</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>openshift-server-mock</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package org.eclipse.jkube.kit.common.util;

import io.fabric8.kubernetes.client.http.HttpResponse;
import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
Expand All @@ -34,15 +35,17 @@ private Fabric8HttpUtil() { }
* @param response Http Response of a particular request
* @return map containing various components of header as key value pairs
*/
public static Map<String, String> extractAuthenticationChallengeIntoMap(HttpResponse<byte[]> response) {
String wwwAuthenticateHeader = response.header(WWW_AUTHENTICATE);
String[] wwwAuthenticateHeaders = wwwAuthenticateHeader.split(",");
public static Map<String, String> extractAuthenticationChallengeIntoMap(HttpResponse<?> response) {
Map<String, String> result = new HashMap<>();
for (String challenge : wwwAuthenticateHeaders) {
if (challenge.contains("=")) {
String[] challengeParts = challenge.split("=");
if (challengeParts.length == 2) {
result.put(challengeParts[0], strip(challengeParts[1], "\""));
String wwwAuthenticateHeader = response.header(WWW_AUTHENTICATE);
if (StringUtils.isNotBlank(wwwAuthenticateHeader)) {
String[] wwwAuthenticateHeaders = wwwAuthenticateHeader.split(",");
for (String challenge : wwwAuthenticateHeaders) {
if (challenge.contains("=")) {
String[] challengeParts = challenge.split("=");
if (challengeParts.length == 2) {
result.put(challengeParts[0], strip(challengeParts[1], "\""));
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* 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.common.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class SHAUtil {
private static final int BUFFER_SIZE = 8192;
private static final String SHA_256 = "SHA-256";
private SHAUtil() { }

public static String generateSHA256(File file) throws IOException {
return generateSHA(file, SHA_256);
}

public static String generateSHA256(String input) {
MessageDigest digest = createNewMessageDigest(SHA_256);
return bytesToHex(digest.digest(input.getBytes(StandardCharsets.UTF_8)));
}

public static String generateSHA(File file, String algorithm) throws IOException {
try (InputStream input = Files.newInputStream(file.toPath())) {
MessageDigest shaDigest = createNewMessageDigest(algorithm);
byte[] buffer = new byte[BUFFER_SIZE];
int len = input.read(buffer);

while (len != -1) {
shaDigest.update(buffer, 0, len);
len = input.read(buffer);
}

return bytesToHex(shaDigest.digest());
}
}

static MessageDigest createNewMessageDigest(String algorithm) {
try {
return MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("Error while generating SHA, no such algorithm : " + algorithm);
}
}

private static String bytesToHex(byte[] hash) {
StringBuilder hexString = new StringBuilder(2 * hash.length);
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package org.eclipse.jkube.kit.common.util;

import io.fabric8.kubernetes.client.http.HttpResponse;
import io.fabric8.kubernetes.client.http.TestHttpResponse;
import org.junit.jupiter.api.Test;

import java.io.UnsupportedEncodingException;
Expand Down Expand Up @@ -53,7 +54,7 @@ void extractAuthenticationChallengeIntoMap_whenWwwHeaderProvided_thenParseDataIn
String wwwAuthenticateValue = "Bearer realm=\"https://auth.example.com/token\",service=\"registry.example.com\",scope=\"repository:myuser/test-chart:pull\"";
Map<String, List<String>> responseHeaders = new HashMap<>();
responseHeaders.put("WWW-Authenticate", Collections.singletonList(wwwAuthenticateValue));
HttpResponse<byte[]> response = new TestFabric8HttpResponse(HTTP_OK, responseHeaders, null, null);
HttpResponse<byte[]> response = new TestHttpResponse<byte[]>(responseHeaders).withCode(HTTP_OK);

// When
Map<String, String> wwwAuthenticateAsMap = Fabric8HttpUtil.extractAuthenticationChallengeIntoMap(response);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* 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.common.util;

import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Objects;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;

class SHAUtilTest {
@Test
void generateSHA256_whenFileProvided_shouldGenerateDigest() throws IOException {
// Given
File file = new File(Objects.requireNonNull(getClass().getResource("/crontab-cr.yml")).getFile());

// When
String result = SHAUtil.generateSHA256(file);

// Then
assertThat(result).isEqualTo("da723cee661dd0368f1b50274720e189372c336c2f78b9848826b7ff4f303b61");
}

@Test
void generateSHA256_whenStringProvided_shouldGenerateDigest() {
// Given + When
String result = SHAUtil.generateSHA256("somerandomstring");

// Then
assertThat(result).isEqualTo("39b5931a5dd8980254782830a1134644035573a1d75a50527e233f45f6afc5ce");
}

@Test
void createNewMessageDigest_whenValidAlgorithmProvided_thenCreateMessageDigestObj() {
// Given
String algorithm = "SHA-256";
// When
MessageDigest digest = SHAUtil.createNewMessageDigest(algorithm);
// Then
assertThat(digest.getAlgorithm()).isEqualTo(algorithm);
}

@Test
void createNewMessageDigest_whenInvalidAlgorithmProvided_thenThrowException() {
// Given
String algorithm = "invalid";
// When + Then
assertThatIllegalArgumentException()
.isThrownBy(() -> SHAUtil.createNewMessageDigest(algorithm))
.withMessage("Error while generating SHA, no such algorithm : invalid");
}
}

This file was deleted.

8 changes: 8 additions & 0 deletions jkube-kit/parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,14 @@
</dependency>
<!-- == test ====================================== -->

<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client-api</artifactId>
<type>test-jar</type>
<version>${version.kubernetes-client}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
Expand Down
7 changes: 7 additions & 0 deletions jkube-kit/resource/helm/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client-api</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@
*/
package org.eclipse.jkube.kit.resource.helm;

import org.eclipse.jkube.kit.common.KitLogger;

import java.io.File;

public class ArtifactoryHelmRepositoryUploader extends StandardRepositoryUploader {

public ArtifactoryHelmRepositoryUploader(KitLogger logger) {
super("PUT", logger, HelmRepository.HelmRepoType.ARTIFACTORY);
public ArtifactoryHelmRepositoryUploader() {
super("PUT", HelmRepository.HelmRepoType.ARTIFACTORY);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@
*/
package org.eclipse.jkube.kit.resource.helm;

import org.eclipse.jkube.kit.common.KitLogger;

import java.io.File;

public class ChartMuseumHelmRepositoryUploader extends StandardRepositoryUploader {

public ChartMuseumHelmRepositoryUploader(KitLogger logger) {
super("POST", logger, HelmRepository.HelmRepoType.CHARTMUSEUM);
public ChartMuseumHelmRepositoryUploader() {
super("POST", HelmRepository.HelmRepoType.CHARTMUSEUM);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Optional;

@Builder(toBuilder = true)
Expand Down Expand Up @@ -50,28 +47,14 @@ public void setType(String type) {
}

public enum HelmRepoType {
CHARTMUSEUM(HelmRepositoryConnectionUtils::getConnectionForUploadToChartMuseum),
ARTIFACTORY(HelmRepositoryConnectionUtils::getConnectionForUploadToArtifactory),
NEXUS(HelmRepositoryConnectionUtils::getConnectionForUploadToNexus);

private final ConnectionCreator connectionCreator;

HelmRepoType(ConnectionCreator connectionCreator) {
this.connectionCreator = connectionCreator;
}

public HttpURLConnection createConnection(File file, HelmRepository repository) throws IOException {
return connectionCreator.createConnectionForUploadToArtifactory(file, repository);
}
CHARTMUSEUM,
ARTIFACTORY,
NEXUS,
OCI;

public static HelmRepoType parseString(String repoType) {
return Optional.ofNullable(repoType).map(String::toUpperCase).map(HelmRepoType::valueOf).orElse(null);
}

@FunctionalInterface
protected interface ConnectionCreator {
HttpURLConnection createConnectionForUploadToArtifactory(File file, HelmRepository repository) throws IOException;
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ public void uploadHelmChart(HelmConfig helm) throws BadUploadException, IOExcept
private void uploadHelmChart(HelmConfig helmConfig, HelmRepository helmRepository)
throws IOException, BadUploadException {

final HelmUploaderManager helmUploaderManager = new HelmUploaderManager();
for (HelmConfig.HelmType helmType : helmConfig.getTypes()) {
final HelmUploaderManager helmUploaderManager = new HelmUploaderManager(logger);
logger.info("Uploading Helm Chart \"%s\" to %s", helmConfig.getChart(), helmRepository.getName());
logger.debug("OutputDir: %s", helmConfig.getOutputDir());

Expand All @@ -170,7 +170,7 @@ private void uploadHelmChart(HelmConfig helmConfig, HelmRepository helmRepositor
final File tarballFile = new File(tarballOutputDir, String.format("%s-%s%s.%s",
helmConfig.getChart(), helmConfig.getVersion(), resolveHelmClassifier(helmConfig), helmConfig.getChartExtension()));

helmUploaderManager.getHelmUploader(helmRepository.getType()).uploadSingle(tarballFile, helmRepository);
helmUploaderManager.getHelmUploader(helmRepository.getType()).uploadSingle(logger, tarballFile, helmRepository);
}
}

Expand Down
Loading

0 comments on commit 50c0dcf

Please sign in to comment.