Skip to content

Commit

Permalink
fix: make helm.SetValue more robust (#1848)
Browse files Browse the repository at this point in the history
* fix: make helm.SetValue more robust

* f
  • Loading branch information
emosbaugh authored Feb 7, 2025
1 parent 9d1ec8d commit 5d60593
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 25 deletions.
2 changes: 1 addition & 1 deletion pkg/addons/adminconsole/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (a *AdminConsole) GenerateHelmValues(ctx context.Context, kcli client.Clien

copiedValues["extraEnv"] = extraEnv

copiedValues, err = helm.SetValue(copiedValues, "kurlProxy.nodePort", runtimeconfig.AdminConsolePort())
err = helm.SetValue(copiedValues, "kurlProxy.nodePort", runtimeconfig.AdminConsolePort())
if err != nil {
return nil, errors.Wrap(err, "set kurlProxy.nodePort")
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/addons/openebs/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func (o *OpenEBS) GenerateHelmValues(ctx context.Context, kcli client.Client, ov
return nil, errors.Wrap(err, "unmarshal helm values")
}

copiedValues, err = helm.SetValue(copiedValues, `["localpv-provisioner"].localpv.basePath`, runtimeconfig.EmbeddedClusterOpenEBSLocalSubDir())
err = helm.SetValue(copiedValues, "localpv-provisioner.localpv.basePath", runtimeconfig.EmbeddedClusterOpenEBSLocalSubDir())
if err != nil {
return nil, errors.Wrap(err, "set localpv-provisioner.localpv.basePath")
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/addons/seaweedfs/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ func (s *SeaweedFS) GenerateHelmValues(ctx context.Context, kcli client.Client,
}

dataPath := filepath.Join(runtimeconfig.EmbeddedClusterSeaweedfsSubDir(), "ssd")
copiedValues, err = helm.SetValue(copiedValues, "master.data.hostPathPrefix", dataPath)
err = helm.SetValue(copiedValues, "master.data.hostPathPrefix", dataPath)
if err != nil {
return nil, errors.Wrap(err, "set helm values global.data.hostPathPrefix")
}

logsPath := filepath.Join(runtimeconfig.EmbeddedClusterSeaweedfsSubDir(), "storage")
copiedValues, err = helm.SetValue(copiedValues, "master.logs.hostPathPrefix", logsPath)
err = helm.SetValue(copiedValues, "master.logs.hostPathPrefix", logsPath)
if err != nil {
return nil, errors.Wrap(err, "set helm values global.logs.hostPathPrefix")
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/addons/velero/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (v *Velero) GenerateHelmValues(ctx context.Context, kcli client.Client, ove
}

podVolumePath := filepath.Join(runtimeconfig.EmbeddedClusterK0sSubDir(), "kubelet/pods")
copiedValues, err = helm.SetValue(copiedValues, "nodeAgent.podVolumePath", podVolumePath)
err = helm.SetValue(copiedValues, "nodeAgent.podVolumePath", podVolumePath)
if err != nil {
return nil, errors.Wrap(err, "set helm value nodeAgent.podVolumePath")
}
Expand Down
29 changes: 15 additions & 14 deletions pkg/helm/values.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package helm

import (
"encoding/json"
"fmt"

jsonpatch "github.com/evanphx/json-patch"
"github.com/k0sproject/dig"
"github.com/ohler55/ojg/jp"
"gopkg.in/yaml.v3"
"helm.sh/helm/v3/pkg/strvals"
k8syaml "sigs.k8s.io/yaml"
)

// UnmarshalValues unmarshals the given JSON compatible YAML string into a map[string]interface{}.
func UnmarshalValues(valuesYaml string) (map[string]interface{}, error) {
newValuesMap := map[string]interface{}{}
if err := yaml.Unmarshal([]byte(valuesYaml), &newValuesMap); err != nil {
Expand All @@ -18,6 +20,7 @@ func UnmarshalValues(valuesYaml string) (map[string]interface{}, error) {
return newValuesMap, nil
}

// MarshalValues marshals the given map[string]interface{} into a JSON compatible YAML string.
func MarshalValues(values map[string]interface{}) (string, error) {
newValuesYaml, err := yaml.Marshal(values)
if err != nil {
Expand All @@ -26,25 +29,22 @@ func MarshalValues(values map[string]interface{}) (string, error) {
return string(newValuesYaml), nil
}

// SetValue sets the value at the given path in the values map.
// NOTE: this function does not support creating new maps. It only supports setting values in
// existing ones.
func SetValue(values map[string]interface{}, path string, newValue interface{}) (map[string]interface{}, error) {
newValuesMap := dig.Mapping(values)

x, err := jp.ParseString(path)
// SetValue sets the value at the given path in the values map. It uses the notation defined by
// helm "--set-json" flag.
func SetValue(values map[string]interface{}, path string, newValue interface{}) error {
val, err := json.Marshal(newValue)
if err != nil {
return nil, fmt.Errorf("parse json path %q: %w", path, err)
return fmt.Errorf("parse value: %w", err)
}

err = x.Set(newValuesMap, newValue)
s := fmt.Sprintf("%s=%s", path, val)
err = strvals.ParseJSON(s, values)
if err != nil {
return nil, fmt.Errorf("set json path %q to %q: %w", path, newValue, err)
return fmt.Errorf("helm set json: %w", err)
}

return newValuesMap, nil
return nil
}

// GetValue gets the value at the given JSON path in the values map.
func GetValue(values map[string]interface{}, path string) (interface{}, error) {
x, err := jp.ParseString(path)
if err != nil {
Expand All @@ -57,6 +57,7 @@ func GetValue(values map[string]interface{}, path string) (interface{}, error) {
return v[0], nil
}

// PatchValues patches the values map with the given RFC6902 JSON patch.
func PatchValues(values map[string]interface{}, patchYAML string) (map[string]interface{}, error) {
if len(patchYAML) == 0 {
return values, nil
Expand Down
166 changes: 162 additions & 4 deletions pkg/helm/values_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package helm
import (
"reflect"
"testing"

"github.com/stretchr/testify/assert"
)

func TestSetValue(t *testing.T) {
Expand Down Expand Up @@ -65,17 +67,173 @@ func TestSetValue(t *testing.T) {
},
wantErr: false,
},
{
name: "test create new map",
args: args{
values: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "new value",
},
},
path: "foo.baz",
newValue: map[string]interface{}{"buzz": "fizz"},
},
want: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "new value",
"baz": map[string]interface{}{
"buzz": "fizz",
},
},
},
},
{
name: `can set adminconsole "kurlProxy.nodePort"`,
args: args{
values: map[string]interface{}{
"kurlProxy": map[string]interface{}{
"enabled": true,
"nodePort": 30000,
},
},
path: "kurlProxy.nodePort",
newValue: 30001,
},
want: map[string]interface{}{
"kurlProxy": map[string]interface{}{
"enabled": true,
"nodePort": float64(30001),
},
},
wantErr: false,
},
{
name: `can set openebs "localpv-provisioner.localpv.basePath"`,
args: args{
values: map[string]interface{}{
"localpv-provisioner": map[string]interface{}{
"analytics": map[string]interface{}{
"enabled": false,
},
"localpv": map[string]interface{}{
"image": map[string]interface{}{
"registry": "proxy.replicated.com/anonymous/",
},
"basePath": "/var/lib/embedded-cluster/openebs-local",
},
},
},
path: "localpv-provisioner.localpv.basePath",
newValue: "/var/ec/openebs-local",
},
want: map[string]interface{}{
"localpv-provisioner": map[string]interface{}{
"analytics": map[string]interface{}{
"enabled": false,
},
"localpv": map[string]interface{}{
"image": map[string]interface{}{
"registry": "proxy.replicated.com/anonymous/",
},
"basePath": "/var/ec/openebs-local",
},
},
},
wantErr: false,
},
{
name: `can set seaweedfs "master.data.hostPathPrefix"`,
args: args{
values: map[string]interface{}{
"master": map[string]interface{}{
"replicas": 1,
"nodeSelector": nil,
"data": map[string]interface{}{
"hostPathPrefix": "/var/lib/embedded-cluster/seaweedfs/ssd",
},
"logs": map[string]interface{}{
"hostPathPrefix": "/var/lib/embedded-cluster/seaweedfs/storage",
},
},
},
path: "master.data.hostPathPrefix",
newValue: "/var/ec/seaweedfs/ssd",
},
want: map[string]interface{}{
"master": map[string]interface{}{
"replicas": 1,
"nodeSelector": nil,
"data": map[string]interface{}{
"hostPathPrefix": "/var/ec/seaweedfs/ssd",
},
"logs": map[string]interface{}{
"hostPathPrefix": "/var/lib/embedded-cluster/seaweedfs/storage",
},
},
},
wantErr: false,
},
{
name: `can set seaweedfs "master.logs.hostPathPrefix"`,
args: args{
values: map[string]interface{}{
"master": map[string]interface{}{
"replicas": 1,
"nodeSelector": nil,
"data": map[string]interface{}{
"hostPathPrefix": "/var/lib/embedded-cluster/seaweedfs/ssd",
},
"logs": map[string]interface{}{
"hostPathPrefix": "/var/lib/embedded-cluster/seaweedfs/storage",
},
},
},
path: "master.logs.hostPathPrefix",
newValue: "/var/ec/seaweedfs/storage",
},
want: map[string]interface{}{
"master": map[string]interface{}{
"replicas": 1,
"nodeSelector": nil,
"data": map[string]interface{}{
"hostPathPrefix": "/var/lib/embedded-cluster/seaweedfs/ssd",
},
"logs": map[string]interface{}{
"hostPathPrefix": "/var/ec/seaweedfs/storage",
},
},
},
wantErr: false,
},
{
name: `can set velero "nodeAgent.podVolumePath"`,
args: args{
values: map[string]interface{}{
"nodeAgent": map[string]interface{}{
"podVolumePath": "/var/lib/embedded-cluster/k0s/kubelet/pods",
},
"snapshotsEnabled": false,
},
path: "nodeAgent.podVolumePath",
newValue: "/var/ec/k0s/kubelet/pods",
},
want: map[string]interface{}{
"nodeAgent": map[string]interface{}{
"podVolumePath": "/var/ec/k0s/kubelet/pods",
},
"snapshotsEnabled": false,
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := SetValue(tt.args.values, tt.args.path, tt.args.newValue)
err := SetValue(tt.args.values, tt.args.path, tt.args.newValue)
if (err != nil) != tt.wantErr {
t.Errorf("SetValue() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("SetValue() = %v, want %v", got, tt.want)
}
assert.Equal(t, tt.want, tt.args.values)
})
}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/dryrun/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func TestDefaultInstallation(t *testing.T) {
adminConsoleOpts := hcli.Calls[3].Arguments[1].(helm.InstallOptions)
assert.Equal(t, "admin-console", adminConsoleOpts.ReleaseName)
assertHelmValues(t, adminConsoleOpts.Values, map[string]interface{}{
"kurlProxy.nodePort": int(30000),
"kurlProxy.nodePort": float64(30000),
})

// --- validate os env --- //
Expand Down Expand Up @@ -278,7 +278,7 @@ func TestCustomPortsInstallation(t *testing.T) {
adminConsoleOpts := hcli.Calls[3].Arguments[1].(helm.InstallOptions)
assert.Equal(t, "admin-console", adminConsoleOpts.ReleaseName)
assertHelmValues(t, adminConsoleOpts.Values, map[string]interface{}{
"kurlProxy.nodePort": int(30002),
"kurlProxy.nodePort": float64(30002),
})

// --- validate host preflight spec --- //
Expand Down

0 comments on commit 5d60593

Please sign in to comment.