Skip to content
Merged
10 changes: 10 additions & 0 deletions helm-java/src/main/java/com/marcnuri/helm/Helm.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,6 +60,16 @@ public DependencyCommand dependency() {
return new DependencyCommand(HelmLibHolder.INSTANCE, path);
}

/**
* Fetch release history.
*
* @param releaseName name of the release.
* @return a new {@link HistoryCommand} instance.
*/
public static HistoryCommand history(String releaseName) {
return new HistoryCommand(HelmLibHolder.INSTANCE, releaseName);
}

/**
* This commands installs the referenced chart archive.
*
Expand Down
95 changes: 95 additions & 0 deletions helm-java/src/main/java/com/marcnuri/helm/HistoryCommand.java
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
/*
* Copyright 2024 Marc Nuri
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.marcnuri.helm;

import com.marcnuri.helm.jni.HelmLib;
import com.marcnuri.helm.jni.HistoryOptions;
import java.nio.file.Path;
import java.util.List;

/**
* @author Giuseppe Cardaropoli
*/
public class HistoryCommand extends HelmCommand<List<ReleaseHistory>>{

private final String releaseName;
private int max;
private String namespace;
private Path kubeConfig;
private String kubeConfigContents;

public HistoryCommand(HelmLib helmLib, String releaseName) {
super(helmLib);
this.releaseName = releaseName;
}

@Override
public List<ReleaseHistory> call() {
return ReleaseHistory.parseMultiple(run(hl -> hl.History(new HistoryOptions(
releaseName,
max,
namespace,
toString(kubeConfig),
kubeConfigContents
))));
}

/**
* Maximum number of revisions to include in history.
* Default is 256.
*
* @param max maximum number of revisions.
* @return this {@link HistoryCommand} instance.
*/
public HistoryCommand withMax(int max) {
this.max = max;
return this;
}

/**
* Kubernetes namespace scope for this request.
*
* @param namespace the Kubernetes namespace for this request.
* @return this {@link HistoryCommand} instance.
*/
public HistoryCommand withNamespace(String namespace) {
this.namespace = namespace;
return this;
}

/**
* Set the path to the ~/.kube/config file to use.
*
* @param kubeConfig the path to Kube config file.
* @return this {@link HistoryCommand} instance.
*/
public HistoryCommand withKubeConfig(Path kubeConfig) {
this.kubeConfig = kubeConfig;
return this;
}

/**
* Set the Kube config to use.
*
* @param kubeConfigContents the contents of the Kube config file.
* @return this {@link HistoryCommand} instance.
*/
public HistoryCommand withKubeConfigContents(String kubeConfigContents) {
this.kubeConfigContents = kubeConfigContents;
return this;
}
}
105 changes: 105 additions & 0 deletions helm-java/src/main/java/com/marcnuri/helm/ReleaseHistory.java
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2024 Marc Nuri
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.marcnuri.helm;

import static com.marcnuri.helm.HelmCommand.parseUrlEncodedLines;

import com.marcnuri.helm.jni.Result;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* @author Giuseppe Cardaropoli
*/
public class ReleaseHistory {
private final int revision;
private final ZonedDateTime updated;
private final String status;
private final String chart;
private final String appVersion;

private final String description;

@SuppressWarnings("java:S107")
private ReleaseHistory(int revision, ZonedDateTime updated, String status, String chart, String appVersion, String description) {
this.revision = revision;
this.updated = updated;
this.status = status;
this.chart = chart;
this.appVersion = appVersion;
this.description = description;
}

public int getRevision() {
return revision;
}

public ZonedDateTime getUpdated() {
return updated;
}

public String getStatus() {
return status;
}

public String getChart() {
return chart;
}

public String getAppVersion() {
return appVersion;
}

public String getDescription() {
return description;
}

static List<ReleaseHistory> parseMultiple(Result result) {
if (result == null) {
throw new IllegalArgumentException("Result cannot be null");
}
final List<ReleaseHistory> releases = new ArrayList<>();
for (Map<String, String> entries : parseUrlEncodedLines(result.out)) {
releases.add(new ReleaseHistory(
parseInt(entries.get("revision")),
parseDate(entries.get("updated")),
entries.get("status"),
entries.get("chart"),
entries.get("appVersion"),
entries.get("description")
));
}
return releases;
}

private static ZonedDateTime parseDate(String date) {
if (date == null || date.isEmpty()) {
return null;
}
return ZonedDateTime.parse(date, DateTimeFormatter.RFC_1123_DATE_TIME);
}

private static int parseInt(String number) {
if (number == null || number.isEmpty()) {
return 0;
}
return Integer.parseInt(number);
}
}
138 changes: 138 additions & 0 deletions helm-java/src/test/java/com/marcnuri/helm/HelmKubernetesTest.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,6 +18,7 @@

import com.dajudge.kindcontainer.KindContainer;
import com.dajudge.kindcontainer.KindContainerVersion;
import org.assertj.core.api.AssertionsForClassTypes;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
Expand DownExpand Up@@ -879,4 +880,141 @@ void missingRelease() {
}
}
}

@Nested
class History {

@Nested
class Valid {

@Test
void afterInstall() {
helm.install()
.withKubeConfig(kubeConfigFile)
.withName("test-history-after-install")
.call();

List<ReleaseHistory> releaseHistories = Helm.history("test-history-after-install")
.withKubeConfig(kubeConfigFile)
.call();

assertThat(releaseHistories)
.hasSize(1)
.first()
.returns(1, ReleaseHistory::getRevision)
.extracting(ReleaseHistory::getDescription).asString()
.containsIgnoringCase("Install complete");
}

@Test
void afterUpgrade() {
helm.install()
.withKubeConfig(kubeConfigFile)
.withName("test-history-after-install-and-upgrade")
.call();

helm.upgrade()
.withKubeConfig(kubeConfigFile)
.withName("test-history-after-install-and-upgrade")
.set("image.tag", "latest")
.call();

List<ReleaseHistory> releaseHistories = Helm.history("test-history-after-install-and-upgrade")
.withKubeConfig(kubeConfigFile)
.call();

assertThat(releaseHistories)
.hasSize(2)
.satisfiesExactly(
first -> assertThat(first.getDescription()).containsIgnoringCase("Install complete"),
second -> assertThat(second.getDescription()).containsIgnoringCase("Upgrade complete")
);
}

@Test
void withMax() {
helm.install()
.withKubeConfig(kubeConfigFile)
.withName("test-history-with-max")
.call();

helm.upgrade()
.withKubeConfig(kubeConfigFile)
.withName("test-history-with-max")
.set("image.tag", "v1")
.call();

helm.upgrade()
.withKubeConfig(kubeConfigFile)
.withName("test-history-with-max")
.set("image.tag", "v2")
.call();

List<ReleaseHistory> releaseHistories = Helm.history("test-history-with-max")
.withKubeConfig(kubeConfigFile)
.withMax(2)
.call();

assertThat(releaseHistories)
.hasSize(2)
.satisfiesExactly(
first -> assertThat(first.getRevision()).isEqualTo(2),
second -> assertThat(second.getRevision()).isEqualTo(3)
);
}

@Test
void withNamespace() {
helm.install()
.withKubeConfig(kubeConfigFile)
.withName("test-history-with-namespace")
.withNamespace("history-namespace")
.createNamespace()
.call();

List<ReleaseHistory> releaseHistories = Helm.history("test-history-with-namespace")
.withKubeConfig(kubeConfigFile)
.withNamespace("history-namespace")
.call();

assertThat(releaseHistories)
.hasSize(1)
.first()
.returns(1, ReleaseHistory::getRevision);
}

@Test
void withKubeConfigContents() {
helm.install()
.withKubeConfig(kubeConfigFile)
.withName("test-history-with-kube-config-contents")
.call();

List<ReleaseHistory> releaseHistories = Helm.history("test-history-with-kube-config-contents")
.withKubeConfigContents(kubeConfigContents)
.call();

assertThat(releaseHistories)
.hasSize(1)
.first()
.returns(1, ReleaseHistory::getRevision);
}

}

@Nested
class Invalid {

@Test
void nonExistentRelease() {
AssertionsForClassTypes.assertThatThrownBy(() ->
Helm.history("non-existent-release")
.withKubeConfig(kubeConfigFile)
.call()
)
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("release: not found");
}
}
}
}
2 changes: 2 additions & 0 deletions lib/api/src/main/java/com/marcnuri/helm/jni/HelmLib.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,6 +33,8 @@ public interface HelmLib extends Library {

Result DependencyUpdate(DependencyOptions options);

Result History(HistoryOptions options);

Result Install(InstallOptions options);

Result Lint(LintOptions options);
Expand Down
Loading
Loading