From 510d2e4ef8e2a61a9d2fe16fa4e62df8be2a05af Mon Sep 17 00:00:00 2001 From: Antonio Fernandez Alhambra Date: Fri, 6 Feb 2026 17:03:00 +0100 Subject: [PATCH] feat: add support for "helm get values" command --- .../java/com/marcnuri/helm/GetCommand.java | 137 ++++++++++++++++++ .../src/main/java/com/marcnuri/helm/Helm.java | 11 ++ .../com/marcnuri/helm/HelmKubernetesTest.java | 85 +++++++++++ .../marcnuri/helm/jni/GetValuesOptions.java | 48 ++++++ .../java/com/marcnuri/helm/jni/HelmLib.java | 3 + native/internal/helm/get.go | 65 +++++++++ native/main.go | 23 +++ 7 files changed, 372 insertions(+) create mode 100644 helm-java/src/main/java/com/marcnuri/helm/GetCommand.java create mode 100644 lib/api/src/main/java/com/marcnuri/helm/jni/GetValuesOptions.java create mode 100644 native/internal/helm/get.go diff --git a/helm-java/src/main/java/com/marcnuri/helm/GetCommand.java b/helm-java/src/main/java/com/marcnuri/helm/GetCommand.java new file mode 100644 index 00000000..e25199d1 --- /dev/null +++ b/helm-java/src/main/java/com/marcnuri/helm/GetCommand.java @@ -0,0 +1,137 @@ +/* + * Copyright 2026 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.GetValuesOptions; +import com.marcnuri.helm.jni.HelmLib; + +import java.nio.file.Path; + +/** + * This command consists of multiple subcommands which can be used to get extended information about the release. + * + * @author Antonio Fernandez Alhambra + */ +public class GetCommand { + + private final HelmLib helmLib; + private final String releaseName; + + GetCommand(HelmLib helmLib, String releaseName) { + this.helmLib = helmLib; + this.releaseName = releaseName; + } + + /** + * This command downloads the values file for a given release. + * + * @return the {@link GetValuesSubcommand} subcommand. + */ + public GetValuesSubcommand values() { + return new GetValuesSubcommand(helmLib, releaseName); + } + + public static final class GetValuesSubcommand extends HelmCommand { + + private final String releaseName; + private boolean allValues; + private int revision; + private String namespace; + private Path kubeConfig; + private String kubeConfigContents; + + private GetValuesSubcommand(HelmLib helmLib, String releaseName) { + super(helmLib); + this.releaseName = releaseName; + } + + /** + * Execute the get values subcommand. + * + * @return a {@link String} containing the values in YAML format. + */ + @Override + public String call() { + return run(hl -> hl.GetValues(new GetValuesOptions( + releaseName, + toInt(allValues), + revision, + namespace, + toString(kubeConfig), + kubeConfigContents + ))).out; + } + + /** + * Dump all (computed) values. + *

+ * When set, all computed values are returned, including the default values from the chart. + * + * @return this {@link GetValuesSubcommand} instance. + */ + public GetValuesSubcommand allValues() { + this.allValues = true; + return this; + } + + /** + * Get the named release with revision. + *

+ * If not specified, the latest release is returned. + * + * @param revision the revision number. + * @return this {@link GetValuesSubcommand} instance. + */ + public GetValuesSubcommand withRevision(int revision) { + this.revision = revision; + return this; + } + + /** + * Kubernetes namespace scope for this request. + * + * @param namespace the Kubernetes namespace for this request. + * @return this {@link GetValuesSubcommand} instance. + */ + public GetValuesSubcommand withNamespace(String namespace) { + this.namespace = namespace; + return this; + } + + /** + * Set the path ./kube/config file to use. + * + * @param kubeConfig the path to kube config file. + * @return this {@link GetValuesSubcommand} instance. + */ + public GetValuesSubcommand 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 GetValuesSubcommand} instance. + */ + public GetValuesSubcommand withKubeConfigContents(String kubeConfigContents) { + this.kubeConfigContents = kubeConfigContents; + return this; + } + } +} diff --git a/helm-java/src/main/java/com/marcnuri/helm/Helm.java b/helm-java/src/main/java/com/marcnuri/helm/Helm.java index 3370fe28..2a29a2d4 100644 --- a/helm-java/src/main/java/com/marcnuri/helm/Helm.java +++ b/helm-java/src/main/java/com/marcnuri/helm/Helm.java @@ -24,6 +24,7 @@ /** * @author Marc Nuri * @author Andres F. Vallecilla + * @author Antonio Fernandez Alhambra */ public class Helm { @@ -96,6 +97,16 @@ public static ListCommand list() { return new ListCommand(HelmLibHolder.INSTANCE); } + /** + * This command consists of multiple subcommands which can be used to get extended information about the release. + * + * @param releaseName the name of the release. + * @return the {@link GetCommand} command. + */ + public static GetCommand get(String releaseName) { + return new GetCommand(HelmLibHolder.INSTANCE, releaseName); + } + /** * This command packages a chart into a versioned chart archive file. * If a path is given, this will look at that path for a chart (which must contain a Chart.yaml file) and then package that directory. diff --git a/helm-java/src/test/java/com/marcnuri/helm/HelmKubernetesTest.java b/helm-java/src/test/java/com/marcnuri/helm/HelmKubernetesTest.java index a2ed6ae3..b1d81df8 100644 --- a/helm-java/src/test/java/com/marcnuri/helm/HelmKubernetesTest.java +++ b/helm-java/src/test/java/com/marcnuri/helm/HelmKubernetesTest.java @@ -762,4 +762,89 @@ void withInstallAndInvalidKubeVersion() { } } } + + @Nested + class Get { + + @Nested + class Values { + + @Test + void returnsUserSuppliedValues() { + helm.install() + .withKubeConfig(kubeConfigFile) + .withName("get-values-user-supplied") + .set("replicaCount", "3") + .call(); + final String result = Helm.get("get-values-user-supplied").values() + .withKubeConfig(kubeConfigFile) + .call(); + assertThat(result) + .contains("replicaCount: 3"); + } + + @Test + void allValuesIncludesDefaultValues() { + helm.install() + .withKubeConfig(kubeConfigFile) + .withName("get-values-all") + .set("replicaCount", "2") + .call(); + final String result = Helm.get("get-values-all").values() + .withKubeConfig(kubeConfigFile) + .allValues() + .call(); + assertThat(result) + .contains("replicaCount: 2") + .contains("serviceAccount:"); + } + + @Test + void withRevision() { + helm.install() + .withKubeConfig(kubeConfigFile) + .withName("get-values-revision") + .set("replicaCount", "1") + .call(); + helm.upgrade() + .withKubeConfig(kubeConfigFile) + .withName("get-values-revision") + .set("replicaCount", "5") + .call(); + final String result = Helm.get("get-values-revision").values() + .withKubeConfig(kubeConfigFile) + .withRevision(1) + .call(); + assertThat(result) + .contains("replicaCount: 1"); + } + + @Test + void withNamespace() { + helm.install() + .withKubeConfig(kubeConfigFile) + .withName("get-values-namespace") + .withNamespace("default") + .call(); + final String result = Helm.get("get-values-namespace").values() + .withKubeConfig(kubeConfigFile) + .withNamespace("default") + .call(); + assertThat(result).isNotNull(); + } + } + + @Nested + class Invalid { + + @Test + void missingRelease() { + final GetCommand.GetValuesSubcommand getValues = Helm.get("non-existent-release").values() + .withKubeConfig(kubeConfigFile); + assertThatThrownBy(getValues::call) + .message() + .contains("not found"); + } + } + } } diff --git a/lib/api/src/main/java/com/marcnuri/helm/jni/GetValuesOptions.java b/lib/api/src/main/java/com/marcnuri/helm/jni/GetValuesOptions.java new file mode 100644 index 00000000..b8eead0f --- /dev/null +++ b/lib/api/src/main/java/com/marcnuri/helm/jni/GetValuesOptions.java @@ -0,0 +1,48 @@ +/* + * Copyright 2026 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.jni; + +import com.sun.jna.Structure; + +/** + * @author Antonio Fernandez Alhambra + */ +@Structure.FieldOrder({ + "releaseName", + "allValues", + "revision", + "namespace", + "kubeConfig", + "kubeConfigContents" +}) +public class GetValuesOptions extends Structure { + public String releaseName; + public int allValues; + public int revision; + public String namespace; + public String kubeConfig; + public String kubeConfigContents; + + public GetValuesOptions(String releaseName, int allValues, int revision, String namespace, String kubeConfig, String kubeConfigContents) { + this.releaseName = releaseName; + this.allValues = allValues; + this.revision = revision; + this.namespace = namespace; + this.kubeConfig = kubeConfig; + this.kubeConfigContents = kubeConfigContents; + } +} diff --git a/lib/api/src/main/java/com/marcnuri/helm/jni/HelmLib.java b/lib/api/src/main/java/com/marcnuri/helm/jni/HelmLib.java index 971ded15..e16b9dbe 100644 --- a/lib/api/src/main/java/com/marcnuri/helm/jni/HelmLib.java +++ b/lib/api/src/main/java/com/marcnuri/helm/jni/HelmLib.java @@ -21,6 +21,7 @@ /** * @author Marc Nuri * @author Andres F. Vallecilla + * @author Antonio Fernandez Alhambra */ public interface HelmLib extends Library { @@ -38,6 +39,8 @@ public interface HelmLib extends Library { Result List(ListOptions options); + Result GetValues(GetValuesOptions options); + Result Package(PackageOptions options); Result Push(PushOptions options); diff --git a/native/internal/helm/get.go b/native/internal/helm/get.go new file mode 100644 index 00000000..0d4a927e --- /dev/null +++ b/native/internal/helm/get.go @@ -0,0 +1,65 @@ +/* + * Copyright 2026 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 helm + +import ( + "encoding/json" + + "helm.sh/helm/v3/pkg/action" + "sigs.k8s.io/yaml" +) + +type GetValuesOptions struct { + ReleaseName string + AllValues bool + Revision int + Namespace string + KubeConfig string + KubeConfigContents string +} + +func GetValues(options *GetValuesOptions) (string, error) { + cfg, err := NewCfg(&CfgOptions{ + KubeConfig: options.KubeConfig, + KubeConfigContents: options.KubeConfigContents, + Namespace: options.Namespace, + }) + if err != nil { + return "", err + } + client := action.NewGetValues(cfg) + client.AllValues = options.AllValues + if options.Revision > 0 { + client.Version = options.Revision + } + + values, err := client.Run(options.ReleaseName) + if err != nil { + return "", err + } + + // Convert values to YAML format (default Helm output format) + jsonBytes, err := json.Marshal(values) + if err != nil { + return "", err + } + yamlBytes, err := yaml.JSONToYAML(jsonBytes) + if err != nil { + return "", err + } + return string(yamlBytes), nil +} diff --git a/native/main.go b/native/main.go index 6912eeed..e568880a 100644 --- a/native/main.go +++ b/native/main.go @@ -194,6 +194,15 @@ struct TestOptions { int debug; }; +struct GetValuesOptions { + char* releaseName; + int allValues; + int revision; + char* namespace; + char* kubeConfig; + char* kubeConfigContents; +}; + struct UninstallOptions { char* releaseName; int dryRun; @@ -415,6 +424,20 @@ func List(options *C.struct_ListOptions) C.Result { }) } +//export GetValues +func GetValues(options *C.struct_GetValuesOptions) C.Result { + return runCommand(func() (string, error) { + return helm.GetValues(&helm.GetValuesOptions{ + ReleaseName: C.GoString(options.releaseName), + AllValues: options.allValues == 1, + Revision: int(options.revision), + Namespace: C.GoString(options.namespace), + KubeConfig: C.GoString(options.kubeConfig), + KubeConfigContents: C.GoString(options.kubeConfigContents), + }) + }) +} + //export Package func Package(options *C.struct_PackageOptions) C.Result { return runCommand(func() (string, error) {