Skip to content
Open
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
10 changes: 10 additions & 0 deletions helm-java/src/main/java/com/marcnuri/helm/Helm.java
Original file line number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 number Diff line number Diff 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 Down Expand 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 number Diff line number Diff 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