Skip to content

Commit d71902f

Browse files
committed
feat: add support to set labels without selector
1 parent de01137 commit d71902f

File tree

3 files changed

+143
-4
lines changed

3 files changed

+143
-4
lines changed

kustomize/commands/edit/add/addmetadata.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func newCmdAddLabel(fSys filesys.FileSystem, v func(map[string]string) error) *c
7070
o.mapValidator = v
7171
cmd := &cobra.Command{
7272
Use: "label",
73-
Short: "Adds one or more commonLabels to " +
73+
Short: "Adds one or more commonLabels or labels to " +
7474
konfig.DefaultKustomizationFileName(),
7575
Example: `
7676
add label {labelKey1:labelValue1} {labelKey2:labelValue2}`,

kustomize/commands/edit/set/setlabel.go

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package set
55

66
import (
77
"fmt"
8+
"slices"
89

910
"github.com/spf13/cobra"
1011
"sigs.k8s.io/kustomize/api/konfig"
@@ -15,8 +16,10 @@ import (
1516
)
1617

1718
type setLabelOptions struct {
18-
metadata map[string]string
19-
mapValidator func(map[string]string) error
19+
metadata map[string]string
20+
mapValidator func(map[string]string) error
21+
labelsWithoutSelector bool
22+
includeTemplates bool
2023
}
2124

2225
// newCmdSetLabel sets one or more commonLabels to the kustomization file.
@@ -25,14 +28,20 @@ func newCmdSetLabel(fSys filesys.FileSystem, v func(map[string]string) error) *c
2528
o.mapValidator = v
2629
cmd := &cobra.Command{
2730
Use: "label",
28-
Short: "Sets one or more commonLabels in " +
31+
Short: "Sets one or more commonLabels or labels in " +
2932
konfig.DefaultKustomizationFileName(),
3033
Example: `
3134
set label {labelKey1:labelValue1} {labelKey2:labelValue2}`,
3235
RunE: func(cmd *cobra.Command, args []string) error {
3336
return o.runE(args, fSys, o.setLabels)
3437
},
3538
}
39+
cmd.Flags().BoolVar(&o.labelsWithoutSelector, "without-selector", false,
40+
"using set labels without selector option",
41+
)
42+
cmd.Flags().BoolVar(&o.includeTemplates, "include-templates", false,
43+
"include labels in templates (requires --without-selector)",
44+
)
3645
return cmd
3746
}
3847

@@ -62,6 +71,9 @@ func (o *setLabelOptions) validateAndParse(args []string) error {
6271
if len(args) < 1 {
6372
return fmt.Errorf("must specify label")
6473
}
74+
if !o.labelsWithoutSelector && o.includeTemplates {
75+
return fmt.Errorf("--without-selector flag must be specified for --include-templates to work")
76+
}
6577
m, err := util.ConvertSliceToMap(args, "label")
6678
if err != nil {
6779
return err
@@ -74,9 +86,32 @@ func (o *setLabelOptions) validateAndParse(args []string) error {
7486
}
7587

7688
func (o *setLabelOptions) setLabels(m *types.Kustomization) error {
89+
o.removeDuplicateLabels(m)
90+
if o.labelsWithoutSelector {
91+
labelIndex := slices.IndexFunc(m.Labels, func(label types.Label) bool {
92+
return !label.IncludeSelectors && label.IncludeTemplates == o.includeTemplates
93+
})
94+
95+
if labelIndex == -1 {
96+
m.Labels = append(m.Labels, types.Label{
97+
Pairs: make(map[string]string),
98+
IncludeSelectors: false,
99+
IncludeTemplates: o.includeTemplates,
100+
})
101+
return o.writeToMap(m.Labels[len(m.Labels)-1].Pairs)
102+
}
103+
104+
labelPairs := m.Labels[labelIndex]
105+
if labelPairs.Pairs == nil {
106+
labelPairs.Pairs = make(map[string]string)
107+
}
108+
return o.writeToMap(labelPairs.Pairs)
109+
}
110+
77111
if m.CommonLabels == nil {
78112
m.CommonLabels = make(map[string]string)
79113
}
114+
80115
return o.writeToMap(m.CommonLabels)
81116
}
82117

@@ -86,3 +121,28 @@ func (o *setLabelOptions) writeToMap(m map[string]string) error {
86121
}
87122
return nil
88123
}
124+
125+
// removeDuplicateLabels removes duplicate labels from commonLabels or labels
126+
func (o *setLabelOptions) removeDuplicateLabels(m *types.Kustomization) {
127+
for k := range o.metadata {
128+
// delete duplicate label from deprecated common labels
129+
//nolint:staticcheck
130+
delete(m.CommonLabels, k)
131+
for idx, label := range m.Labels {
132+
// delete label if it's already present in labels
133+
if _, found := label.Pairs[k]; found {
134+
m.Labels = deleteLabel(k, label, m.Labels, idx)
135+
}
136+
}
137+
}
138+
}
139+
140+
// deleteLabel deletes label from types.Label
141+
func deleteLabel(key string, label types.Label, labels []types.Label, idx int) []types.Label {
142+
delete(label.Pairs, key)
143+
if len(label.Pairs) == 0 {
144+
// remove empty map label.Pairs from labels
145+
labels = append(labels[:idx], labels[idx+1:]...)
146+
}
147+
return labels
148+
}

kustomize/commands/edit/set/setlabel_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package set
66
import (
77
"testing"
88

9+
"github.com/stretchr/testify/require"
910
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
1011
"sigs.k8s.io/kustomize/api/types"
1112
"sigs.k8s.io/kustomize/kustomize/v5/commands/internal/kustfile"
@@ -152,3 +153,81 @@ func TestSetLabelExisting(t *testing.T) {
152153
t.Errorf("unexpected error: %v", err.Error())
153154
}
154155
}
156+
157+
func TestSetLabelWithoutSelector(t *testing.T) {
158+
var o setLabelOptions
159+
o.metadata = map[string]string{"key1": "foo", "key2": "bar"}
160+
o.labelsWithoutSelector = true
161+
162+
m := makeKustomization(t)
163+
require.NoError(t, o.setLabels(m))
164+
require.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo", "key2": "bar"}})
165+
}
166+
167+
func TestSetLabelWithoutSelectorWithExistingLabels(t *testing.T) {
168+
var o setLabelOptions
169+
o.metadata = map[string]string{"key1": "foo", "key2": "bar"}
170+
o.labelsWithoutSelector = true
171+
172+
m := makeKustomization(t)
173+
require.NoError(t, o.setLabels(m))
174+
require.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo", "key2": "bar"}})
175+
176+
o.metadata = map[string]string{"key3": "foobar"}
177+
require.NoError(t, o.setLabels(m))
178+
require.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo", "key2": "bar", "key3": "foobar"}})
179+
}
180+
181+
func TestSetLabelWithoutSelectorWithDuplicateLabel(t *testing.T) {
182+
var o setLabelOptions
183+
o.metadata = map[string]string{"key1": "foo", "key2": "bar"}
184+
o.labelsWithoutSelector = true
185+
o.includeTemplates = true
186+
187+
m := makeKustomization(t)
188+
require.NoError(t, o.setLabels(m))
189+
require.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo", "key2": "bar"}, IncludeTemplates: true})
190+
191+
o.metadata = map[string]string{"key2": "bar"}
192+
o.includeTemplates = false
193+
require.NoError(t, o.setLabels(m))
194+
require.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo"}, IncludeTemplates: true})
195+
require.Equal(t, m.Labels[1], types.Label{Pairs: map[string]string{"key2": "bar"}})
196+
}
197+
198+
func TestSetLabelWithoutSelectorWithCommonLabel(t *testing.T) {
199+
var o setLabelOptions
200+
o.metadata = map[string]string{"key1": "foo", "key2": "bar"}
201+
202+
m := makeKustomization(t)
203+
require.NoError(t, o.setLabels(m))
204+
require.Empty(t, m.Labels)
205+
//nolint:staticcheck
206+
require.Equal(t, m.CommonLabels, map[string]string{"app": "helloworld", "key1": "foo", "key2": "bar"})
207+
208+
o.metadata = map[string]string{"key2": "bar"}
209+
o.labelsWithoutSelector = true
210+
require.NoError(t, o.setLabels(m))
211+
//nolint:staticcheck
212+
require.Equal(t, m.CommonLabels, map[string]string{"app": "helloworld", "key1": "foo"})
213+
require.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key2": "bar"}})
214+
}
215+
216+
func TestSetLabelCommonLabelWithWithoutSelector(t *testing.T) {
217+
var o setLabelOptions
218+
o.metadata = map[string]string{"key1": "foo", "key2": "bar"}
219+
220+
m := makeKustomization(t)
221+
o.labelsWithoutSelector = true
222+
require.NoError(t, o.setLabels(m))
223+
require.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo", "key2": "bar"}})
224+
//nolint:staticcheck
225+
require.Equal(t, m.CommonLabels, map[string]string{"app": "helloworld"})
226+
227+
o.metadata = map[string]string{"key2": "bar2"}
228+
o.labelsWithoutSelector = false
229+
require.NoError(t, o.setLabels(m))
230+
require.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo"}})
231+
//nolint:staticcheck
232+
require.Equal(t, m.CommonLabels, map[string]string{"app": "helloworld", "key2": "bar2"})
233+
}

0 commit comments

Comments
 (0)