-
Notifications
You must be signed in to change notification settings - Fork 20
Description
Description
The helm history command is currently not implemented in helm-java. This command prints historical revisions for a given release, which is essential for auditing deployments and identifying which revision to rollback to.
Background
The helm history command displays a table with:
- REVISION: Version number of the release
- UPDATED: Timestamp of the change
- STATUS: Current state (deployed, superseded, failed, etc.)
- CHART: Chart name and version
- APP VERSION: Application version
- DESCRIPTION: Change description (e.g., "Initial install", "Upgraded successfully", "Rolled back to 2")
This command is commonly used in conjunction with helm rollback to identify target revisions. See the official documentation.
Related Issues
This addresses part of issue #97 which mentions missing history command but lacks implementation details.
Proposed API
Following the existing patterns in the codebase (similar to ListCommand), the implementation should provide a fluent API:
// Basic usage - returns list of release revisions
List<ReleaseHistory> history = Helm.history("my-release")
.withKubeConfig(kubeConfigPath)
.call();
// With namespace
List<ReleaseHistory> history = Helm.history("my-release")
.withNamespace("my-namespace")
.withKubeConfig(kubeConfigPath)
.call();
// Limit number of revisions
List<ReleaseHistory> history = Helm.history("my-release")
.withMax(10)
.withKubeConfig(kubeConfigPath)
.call();
// Using kubeconfig contents
List<ReleaseHistory> history = Helm.history("my-release")
.withKubeConfigContents(kubeConfigYaml)
.call();Implementation Guide
1. Create ReleaseHistory result class (helm-java/src/main/java/com/marcnuri/helm/ReleaseHistory.java)
package com.marcnuri.helm;
import java.time.ZonedDateTime;
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;
// Constructor, getters, and static parse methods
// Similar pattern to Release.parseMultiple()
}2. Create Go Options struct and function (native/internal/helm/history.go)
package helm
import (
"bytes"
"fmt"
"net/url"
"helm.sh/helm/v3/pkg/action"
)
type HistoryOptions struct {
ReleaseName string
Max int
Namespace string
KubeConfig string
KubeConfigContents string
}
func History(options *HistoryOptions) (string, error) {
cfg, err := NewCfg(&CfgOptions{
KubeConfig: options.KubeConfig,
KubeConfigContents: options.KubeConfigContents,
Namespace: options.Namespace,
})
if err != nil {
return "", err
}
client := action.NewHistory(cfg)
if options.Max > 0 {
client.Max = options.Max
} else {
client.Max = 256 // Default from Helm CLI
}
releases, err := client.Run(options.ReleaseName)
if err != nil {
return "", err
}
// Format output similar to List command (URL-encoded lines)
var out bytes.Buffer
for _, rel := range releases {
out.WriteString(fmt.Sprintf("revision=%d&updated=%s&status=%s&chart=%s&appVersion=%s&description=%s\n",
rel.Version,
url.QueryEscape(rel.Info.LastDeployed.Format(time.RFC1123Z)),
url.QueryEscape(rel.Info.Status.String()),
url.QueryEscape(formatChartname(rel.Chart)),
url.QueryEscape(rel.Chart.AppVersion()),
url.QueryEscape(rel.Info.Description),
))
}
return out.String(), nil
}3. Add CGO export in native/main.go
Add the C struct definition:
struct HistoryOptions {
char* releaseName;
int max;
char* namespace;
char* kubeConfig;
char* kubeConfigContents;
};Add the export function:
//export History
func History(options *C.struct_HistoryOptions) C.Result {
return result(helm.History(&helm.HistoryOptions{
ReleaseName: C.GoString(options.releaseName),
Max: int(options.max),
Namespace: C.GoString(options.namespace),
KubeConfig: C.GoString(options.kubeConfig),
KubeConfigContents: C.GoString(options.kubeConfigContents),
}))
}4. Create JNA Options class (lib/api/src/main/java/com/marcnuri/helm/jni/HistoryOptions.java)
package com.marcnuri.helm.jni;
import com.sun.jna.Structure;
@Structure.FieldOrder({
"releaseName",
"max",
"namespace",
"kubeConfig",
"kubeConfigContents"
})
public class HistoryOptions extends Structure {
public String releaseName;
public int max;
public String namespace;
public String kubeConfig;
public String kubeConfigContents;
public HistoryOptions(String releaseName, int max, String namespace,
String kubeConfig, String kubeConfigContents) {
this.releaseName = releaseName;
this.max = max;
this.namespace = namespace;
this.kubeConfig = kubeConfig;
this.kubeConfigContents = kubeConfigContents;
}
}5. Add method to HelmLib interface (lib/api/src/main/java/com/marcnuri/helm/jni/HelmLib.java)
Result History(HistoryOptions options);6. Create HistoryCommand class (helm-java/src/main/java/com/marcnuri/helm/HistoryCommand.java)
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;
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;
}
}7. Add factory method in Helm.java
/**
* 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.helmLib(), releaseName);
}8. Add tests (helm-java/src/test/java/com/marcnuri/helm/HelmHistoryTest.java)
Acceptance Criteria
- Create
ReleaseHistoryresult class inhelm-javamodule - Create
HistoryOptionsGo struct innative/internal/helm/history.go - Implement
Historyfunction in Go usingaction.NewHistory - Add CGO export
Historyinnative/main.go - Create
HistoryOptions.javaJNA structure inlib/api - Add
Historymethod toHelmLibinterface - Create
HistoryCommand.javainhelm-javamodule - Add
history(String releaseName)factory method toHelm.java - Add unit tests for the new command
- Add integration tests using Testcontainers/KinD (test with multiple upgrades to generate history)
Tests
Following the project's testing philosophy (black-box, no mocks, nested structure):
HelmHistoryTestValidafterInstall- History shows single revision after fresh installafterUpgrade- History shows multiple revisions after upgradewithMax- Limit number of returned revisionswithNamespace- Get history with explicit namespacewithKubeConfigContents- Use inline kubeconfig
InvalidnonExistentRelease- Should throw appropriate exception
Additional Information
- CLI Reference: https://helm.sh/docs/helm/helm_history/
- Helm SDK: Uses
action.NewHistoryfromhelm.sh/helm/v3/pkg/action - Priority: High - Essential for auditing and rollback workflows
- Complexity: Medium - Requires new result type (
ReleaseHistory) - Related: Should be implemented before or alongside
helm rollback(some commands seem to have not been implemented yet, such as update, history, and rollback. #97)
Example CLI Output
$ helm history angry-bird
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 1.0 Initial install
2 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 1.0 Upgraded successfully
3 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 1.0 Rolled back to 2
4 Mon Oct 3 10:15:13 2016 deployed alpine-0.1.0 1.0 Upgraded successfully