Skip to content

Commit fce96cb

Browse files
committed
add support to set labels without selector
1 parent f9ab532 commit fce96cb

File tree

3 files changed

+144
-4
lines changed

3 files changed

+144
-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: 64 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,33 @@ 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+
105+
labelPairs := m.Labels[labelIndex]
106+
if labelPairs.Pairs == nil {
107+
labelPairs.Pairs = make(map[string]string)
108+
}
109+
return o.writeToMap(labelPairs.Pairs)
110+
}
111+
77112
if m.CommonLabels == nil {
78113
m.CommonLabels = make(map[string]string)
79114
}
115+
80116
return o.writeToMap(m.CommonLabels)
81117
}
82118

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

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)