Skip to content

Commit ba30c5c

Browse files
committed
add support to set labels without selector
1 parent 616c084 commit ba30c5c

File tree

3 files changed

+129
-4
lines changed

3 files changed

+129
-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: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ import (
1515
)
1616

1717
type setLabelOptions struct {
18-
metadata map[string]string
19-
mapValidator func(map[string]string) error
18+
metadata map[string]string
19+
mapValidator func(map[string]string) error
20+
labelsWithoutSelector bool
21+
includeTemplates bool
2022
}
2123

2224
// newCmdSetLabel sets one or more commonLabels to the kustomization file.
@@ -25,14 +27,20 @@ func newCmdSetLabel(fSys filesys.FileSystem, v func(map[string]string) error) *c
2527
o.mapValidator = v
2628
cmd := &cobra.Command{
2729
Use: "label",
28-
Short: "Sets one or more commonLabels in " +
30+
Short: "Sets one or more commonLabels or labels in " +
2931
konfig.DefaultKustomizationFileName(),
3032
Example: `
3133
set label {labelKey1:labelValue1} {labelKey2:labelValue2}`,
3234
RunE: func(cmd *cobra.Command, args []string) error {
3335
return o.runE(args, fSys, o.setLabels)
3436
},
3537
}
38+
cmd.Flags().BoolVar(&o.labelsWithoutSelector, "without-selector", false,
39+
"using set labels without selector option",
40+
)
41+
cmd.Flags().BoolVar(&o.includeTemplates, "include-templates", false,
42+
"include labels in templates (requires --without-selector)",
43+
)
3644
return cmd
3745
}
3846

@@ -62,6 +70,9 @@ func (o *setLabelOptions) validateAndParse(args []string) error {
6270
if len(args) < 1 {
6371
return fmt.Errorf("must specify label")
6472
}
73+
if !o.labelsWithoutSelector && o.includeTemplates {
74+
return fmt.Errorf("--without-selector flag must be specified for --include-templates to work")
75+
}
6576
m, err := util.ConvertSliceToMap(args, "label")
6677
if err != nil {
6778
return err
@@ -74,9 +85,37 @@ func (o *setLabelOptions) validateAndParse(args []string) error {
7485
}
7586

7687
func (o *setLabelOptions) setLabels(m *types.Kustomization) error {
88+
if o.labelsWithoutSelector {
89+
o.removeDuplicateLabels(m)
90+
91+
var labelPairs *types.Label
92+
for _, label := range m.Labels {
93+
label := label
94+
if !label.IncludeSelectors && label.IncludeTemplates == o.includeTemplates {
95+
labelPairs = &label
96+
break
97+
}
98+
}
99+
100+
if labelPairs != nil {
101+
if labelPairs.Pairs == nil {
102+
labelPairs.Pairs = make(map[string]string)
103+
}
104+
return o.writeToMap(labelPairs.Pairs)
105+
}
106+
107+
m.Labels = append(m.Labels, types.Label{
108+
Pairs: make(map[string]string),
109+
IncludeSelectors: false,
110+
IncludeTemplates: o.includeTemplates,
111+
})
112+
return o.writeToMap(m.Labels[len(m.Labels)-1].Pairs)
113+
}
114+
77115
if m.CommonLabels == nil {
78116
m.CommonLabels = make(map[string]string)
79117
}
118+
80119
return o.writeToMap(m.CommonLabels)
81120
}
82121

@@ -86,3 +125,31 @@ func (o *setLabelOptions) writeToMap(m map[string]string) error {
86125
}
87126
return nil
88127
}
128+
129+
// removeDuplicateLabels removes duplicate labels from commonLabels or labels
130+
func (o *setLabelOptions) removeDuplicateLabels(m *types.Kustomization) {
131+
for k := range o.metadata {
132+
// delete duplicate label from deprecated common labels
133+
delete(m.CommonLabels, k)
134+
for idx, label := range m.Labels {
135+
// delete label if it's already present in labels with mismatched includeTemplates value
136+
if label.IncludeTemplates != o.includeTemplates {
137+
m.Labels = deleteLabel(k, label, m.Labels, idx)
138+
}
139+
if label.IncludeSelectors {
140+
// delete label if it's already present in labels and includes selectors
141+
m.Labels = deleteLabel(k, label, m.Labels, idx)
142+
}
143+
}
144+
}
145+
}
146+
147+
// deleteLabel deletes label from types.Label
148+
func deleteLabel(key string, label types.Label, labels []types.Label, idx int) []types.Label {
149+
delete(label.Pairs, key)
150+
if len(label.Pairs) == 0 {
151+
// remove empty map label.Pairs from labels
152+
labels = append(labels[:idx], labels[idx+1:]...)
153+
}
154+
return labels
155+
}

kustomize/commands/edit/set/setlabel_test.go

Lines changed: 58 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,60 @@ 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+
require.Equal(t, m.CommonLabels, map[string]string{"app": "helloworld", "key1": "foo", "key2": "bar"})
206+
207+
o.metadata = map[string]string{"key2": "bar"}
208+
o.labelsWithoutSelector = true
209+
require.NoError(t, o.setLabels(m))
210+
require.Equal(t, m.CommonLabels, map[string]string{"app": "helloworld", "key1": "foo"})
211+
require.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key2": "bar"}})
212+
}

0 commit comments

Comments
 (0)