Skip to content

Commit 494672c

Browse files
porridgeclaude
andcommitted
Support array index notation in --set flag
Replace custom path parsing (strings.Split + unstructured.SetNestedField) with Helm's strvals.Parse, which handles the full --set syntax including bracket notation like `central.spec.central.defaultTLSSecret[0].name=val`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b91d393 commit 494672c

4 files changed

Lines changed: 61 additions & 45 deletions

File tree

cmd/deploy.go

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,10 @@ import (
2020
"github.com/stackrox/roxie/internal/logger"
2121
"github.com/stackrox/roxie/internal/manifest"
2222
"github.com/stackrox/roxie/internal/roxieenv"
23-
"github.com/stackrox/roxie/internal/types"
24-
2523
"github.com/stackrox/roxie/internal/stackroxversions"
24+
"github.com/stackrox/roxie/internal/types"
2625
"gopkg.in/yaml.v3"
27-
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
26+
"helm.sh/helm/v3/pkg/strvals"
2827
)
2928

3029
const (
@@ -138,28 +137,16 @@ Examples:
138137

139138
registerFlag(cmd, settings, "set", "Set expressions, e.g. securedCluster.spec.clusterName=sensor",
140139
withApplyFn("set-expression", func(config *deployer.Config, expr string) error {
141-
key, yamlValue, found := strings.Cut(expr, "=")
140+
key, _, found := strings.Cut(expr, "=")
142141
if !found {
143142
return fmt.Errorf("invalid set expression '%s': expected format 'key.path=value'", expr)
144143
}
145-
var val interface{}
146-
if err := yaml.Unmarshal([]byte(yamlValue), &val); err != nil {
147-
return fmt.Errorf("failed to unmarshal value '%s' for key '%s': %w", yamlValue, key, err)
148-
}
149-
// SetNestedField requires JSON-compatible types: float64 for numbers, not int.
150-
switch v := val.(type) {
151-
case int:
152-
val = float64(v)
153-
case int64:
154-
val = float64(v)
155-
}
156-
pathElements := strings.Split(key, ".")
157-
if len(pathElements) > 0 && pathElements[0] == "spec" {
144+
if strings.HasPrefix(key, "spec.") {
158145
return errors.New("set expression begins with 'spec.' -- it must be prefixed with 'central.' or 'securedCluster.'")
159146
}
160-
unstructuredPatch := make(map[string]interface{})
161-
if err := unstructured.SetNestedField(unstructuredPatch, val, pathElements...); err != nil {
162-
return err
147+
unstructuredPatch, err := strvals.Parse(expr)
148+
if err != nil {
149+
return fmt.Errorf("parsing set expression %q: %w", expr, err)
163150
}
164151
var patch deployer.Config
165152
if err := helpers.MapToStruct(unstructuredPatch, &patch); err != nil {

cmd/deploy_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,27 @@ central:
208208
)
209209
},
210210
},
211+
{
212+
name: "set expressions support array index notation",
213+
args: []string{
214+
"--set", "central.spec.central.defaultTLSSecret[0].name=frontend-cert",
215+
},
216+
assert: func(t *testing.T, cfg deployer.Config) {
217+
assert.Equal(t,
218+
map[string]interface{}{
219+
"central": map[string]interface{}{
220+
"defaultTLSSecret": []interface{}{
221+
map[string]interface{}{
222+
"name": "frontend-cert",
223+
},
224+
},
225+
},
226+
},
227+
cfg.Central.Spec,
228+
"Central.Spec mismatch",
229+
)
230+
},
231+
},
211232
}
212233

213234
for _, tt := range tests {

go.mod

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,23 @@ go 1.26.0
44

55
require (
66
dario.cat/mergo v1.0.2
7+
github.com/Masterminds/semver/v3 v3.5.0
78
github.com/fatih/color v1.19.0
89
github.com/google/go-containerregistry v0.21.6
910
github.com/spf13/cobra v1.10.2
1011
github.com/spf13/pflag v1.0.10
1112
github.com/stretchr/testify v1.11.1
12-
golang.org/x/term v0.43.0
13+
golang.org/x/term v0.44.0
1314
gopkg.in/yaml.v3 v3.0.1
14-
k8s.io/apimachinery v0.36.1
15+
helm.sh/helm/v3 v3.21.2
16+
k8s.io/apimachinery v0.36.2
1517
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2
1618
)
1719

1820
require (
19-
github.com/Masterminds/semver/v3 v3.5.0
2021
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
2122
github.com/docker/cli v29.4.3+incompatible // indirect
22-
github.com/docker/docker-credential-helpers v0.9.3 // indirect
23+
github.com/docker/docker-credential-helpers v0.9.5 // indirect
2324
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
2425
github.com/go-logr/logr v1.4.3 // indirect
2526
github.com/inconshreveable/mousetrap v1.1.0 // indirect
@@ -31,19 +32,21 @@ require (
3132
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
3233
github.com/opencontainers/go-digest v1.0.0 // indirect
3334
github.com/opencontainers/image-spec v1.1.1 // indirect
35+
github.com/pkg/errors v0.9.1 // indirect
3436
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
3537
github.com/sirupsen/logrus v1.9.4 // indirect
3638
github.com/x448/float16 v0.8.4 // indirect
37-
go.yaml.in/yaml/v2 v2.4.3 // indirect
38-
golang.org/x/net v0.49.0 // indirect
39-
golang.org/x/sync v0.20.0 // indirect
40-
golang.org/x/sys v0.44.0 // indirect
41-
golang.org/x/text v0.35.0 // indirect
39+
go.yaml.in/yaml/v2 v2.4.4 // indirect
40+
golang.org/x/net v0.55.0 // indirect
41+
golang.org/x/sync v0.21.0 // indirect
42+
golang.org/x/sys v0.46.0 // indirect
43+
golang.org/x/text v0.38.0 // indirect
4244
gopkg.in/inf.v0 v0.9.1 // indirect
4345
gotest.tools/v3 v3.5.2 // indirect
4446
k8s.io/klog/v2 v2.140.0 // indirect
4547
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
4648
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
4749
sigs.k8s.io/randfill v1.0.0 // indirect
4850
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
51+
sigs.k8s.io/yaml v1.6.0 // indirect
4952
)

go.sum

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
99
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1010
github.com/docker/cli v29.4.3+incompatible h1:u+UliYm2J/rYrIh2FqHQg32neRG8GjbvNuwQRTzGspU=
1111
github.com/docker/cli v29.4.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
12-
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
13-
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
12+
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
13+
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
1414
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
1515
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
1616
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
@@ -42,6 +42,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
4242
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
4343
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
4444
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
45+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
46+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
4547
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4648
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
4749
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -59,20 +61,21 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
5961
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
6062
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
6163
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
62-
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
63-
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
64+
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
65+
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
66+
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
6467
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
65-
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
66-
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
67-
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
68-
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
68+
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
69+
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
70+
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
71+
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
6972
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
70-
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
71-
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
72-
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
73-
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
74-
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
75-
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
73+
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
74+
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
75+
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
76+
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
77+
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
78+
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
7679
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
7780
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
7881
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
@@ -81,8 +84,10 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
8184
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
8285
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
8386
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
84-
k8s.io/apimachinery v0.36.1 h1:G63Gjx2W+q0YD+72Vo8oY0nDnePVwnuzTmmy5ENrVSA=
85-
k8s.io/apimachinery v0.36.1/go.mod h1:ibYOR00vW/I1kzvi5SF0dRuJ52BvKtfvRdOn35GPQ+8=
87+
helm.sh/helm/v3 v3.21.2 h1:O8ktw30zQZm4tNRavKRN44rotcHICdbSU3e6gS7CCbQ=
88+
helm.sh/helm/v3 v3.21.2/go.mod h1:Da+9m67Mzg42rQNrWAj0RvUd4tDT4dL8mrxUwvRgYWo=
89+
k8s.io/apimachinery v0.36.2 h1:0PE/W/WNy1UX61NLbXY5TMbJ6UwLL6E6lAPkYrKFxbQ=
90+
k8s.io/apimachinery v0.36.2/go.mod h1:fvf/HOLXq9RId0rnDIbN1OEBvHXdQbLMM8nu0LcBUf4=
8691
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
8792
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
8893
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg=

0 commit comments

Comments
 (0)