From 54e367bb32d9b694c2052fd73029ee4265b8568e Mon Sep 17 00:00:00 2001 From: "Lauri Huotari (Github Mrflatt)" Date: Sat, 3 Aug 2024 11:48:16 +0300 Subject: [PATCH] add support to set labels without selector --- kustomize/commands/edit/add/addmetadata.go | 2 +- kustomize/commands/edit/set/setlabel.go | 66 +++++++++++++++++++- kustomize/commands/edit/set/setlabel_test.go | 59 +++++++++++++++++ 3 files changed, 123 insertions(+), 4 deletions(-) diff --git a/kustomize/commands/edit/add/addmetadata.go b/kustomize/commands/edit/add/addmetadata.go index 93cf91a006..1dfdda27fb 100644 --- a/kustomize/commands/edit/add/addmetadata.go +++ b/kustomize/commands/edit/add/addmetadata.go @@ -70,7 +70,7 @@ func newCmdAddLabel(fSys filesys.FileSystem, v func(map[string]string) error) *c o.mapValidator = v cmd := &cobra.Command{ Use: "label", - Short: "Adds one or more commonLabels to " + + Short: "Adds one or more commonLabels or labels to " + konfig.DefaultKustomizationFileName(), Example: ` add label {labelKey1:labelValue1} {labelKey2:labelValue2}`, diff --git a/kustomize/commands/edit/set/setlabel.go b/kustomize/commands/edit/set/setlabel.go index 0d2c6c2c81..cf7a96520b 100644 --- a/kustomize/commands/edit/set/setlabel.go +++ b/kustomize/commands/edit/set/setlabel.go @@ -15,8 +15,10 @@ import ( ) type setLabelOptions struct { - metadata map[string]string - mapValidator func(map[string]string) error + metadata map[string]string + mapValidator func(map[string]string) error + labelsWithoutSelector bool + includeTemplates bool } // newCmdSetLabel sets one or more commonLabels to the kustomization file. @@ -25,7 +27,7 @@ func newCmdSetLabel(fSys filesys.FileSystem, v func(map[string]string) error) *c o.mapValidator = v cmd := &cobra.Command{ Use: "label", - Short: "Sets one or more commonLabels in " + + Short: "Sets one or more commonLabels or labels in " + konfig.DefaultKustomizationFileName(), Example: ` set label {labelKey1:labelValue1} {labelKey2:labelValue2}`, @@ -33,6 +35,12 @@ func newCmdSetLabel(fSys filesys.FileSystem, v func(map[string]string) error) *c return o.runE(args, fSys, o.setLabels) }, } + cmd.Flags().BoolVar(&o.labelsWithoutSelector, "without-selector", false, + "using set labels without selector option", + ) + cmd.Flags().BoolVar(&o.includeTemplates, "include-templates", false, + "include labels in templates (requires --without-selector)", + ) return cmd } @@ -62,6 +70,9 @@ func (o *setLabelOptions) validateAndParse(args []string) error { if len(args) < 1 { return fmt.Errorf("must specify label") } + if !o.labelsWithoutSelector && o.includeTemplates { + return fmt.Errorf("--without-selector flag must be specified for --include-templates to work") + } m, err := util.ConvertSliceToMap(args, "label") if err != nil { return err @@ -74,9 +85,36 @@ func (o *setLabelOptions) validateAndParse(args []string) error { } func (o *setLabelOptions) setLabels(m *types.Kustomization) error { + if o.labelsWithoutSelector { + o.removeDuplicateLabels(m) + + var labelPairs *types.Label + for _, label := range m.Labels { + if !label.IncludeSelectors && label.IncludeTemplates == o.includeTemplates { + labelPairs = &label + break + } + } + + if labelPairs != nil { + if labelPairs.Pairs == nil { + labelPairs.Pairs = make(map[string]string) + } + return o.writeToMap(labelPairs.Pairs) + } + + m.Labels = append(m.Labels, types.Label{ + Pairs: make(map[string]string), + IncludeSelectors: false, + IncludeTemplates: o.includeTemplates, + }) + return o.writeToMap(m.Labels[len(m.Labels)-1].Pairs) + } + if m.CommonLabels == nil { m.CommonLabels = make(map[string]string) } + return o.writeToMap(m.CommonLabels) } @@ -86,3 +124,25 @@ func (o *setLabelOptions) writeToMap(m map[string]string) error { } return nil } + +func (o *setLabelOptions) removeDuplicateLabels(m *types.Kustomization) { + for k := range o.metadata { + delete(m.CommonLabels, k) + for idx, label := range m.Labels { + if label.IncludeTemplates != o.includeTemplates { + m.Labels = deleteLabel(k, label, m.Labels, idx) + } + if label.IncludeSelectors { + m.Labels = deleteLabel(k, label, m.Labels, idx) + } + } + } +} + +func deleteLabel(key string, label types.Label, labels []types.Label, idx int) []types.Label { + delete(label.Pairs, key) + if len(label.Pairs) == 0 { + labels = append(labels[:idx], labels[idx+1:]...) + } + return labels +} diff --git a/kustomize/commands/edit/set/setlabel_test.go b/kustomize/commands/edit/set/setlabel_test.go index b1a3facf67..d88d2769a9 100644 --- a/kustomize/commands/edit/set/setlabel_test.go +++ b/kustomize/commands/edit/set/setlabel_test.go @@ -6,6 +6,8 @@ package set import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/kustomize/v5/commands/internal/kustfile" @@ -152,3 +154,60 @@ func TestSetLabelExisting(t *testing.T) { t.Errorf("unexpected error: %v", err.Error()) } } + +func TestSetLabelWithoutSelector(t *testing.T) { + var o setLabelOptions + o.metadata = map[string]string{"key1": "foo", "key2": "bar"} + o.labelsWithoutSelector = true + + m := makeKustomization(t) + require.NoError(t, o.setLabels(m)) + assert.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo", "key2": "bar"}}) +} + +func TestSetLabelWithoutSelectorWithExistingLabels(t *testing.T) { + var o setLabelOptions + o.metadata = map[string]string{"key1": "foo", "key2": "bar"} + o.labelsWithoutSelector = true + + m := makeKustomization(t) + require.NoError(t, o.setLabels(m)) + assert.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo", "key2": "bar"}}) + + o.metadata = map[string]string{"key3": "foobar"} + require.NoError(t, o.setLabels(m)) + assert.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo", "key2": "bar", "key3": "foobar"}}) +} + +func TestSetLabelWithoutSelectorWithDuplicateLabel(t *testing.T) { + var o setLabelOptions + o.metadata = map[string]string{"key1": "foo", "key2": "bar"} + o.labelsWithoutSelector = true + o.includeTemplates = true + + m := makeKustomization(t) + require.NoError(t, o.setLabels(m)) + assert.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo", "key2": "bar"}, IncludeTemplates: true}) + + o.metadata = map[string]string{"key2": "bar"} + o.includeTemplates = false + require.NoError(t, o.setLabels(m)) + assert.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo"}, IncludeTemplates: true}) + assert.Equal(t, m.Labels[1], types.Label{Pairs: map[string]string{"key2": "bar"}}) +} + +func TestSetLabelWithoutSelectorWithCommonLabel(t *testing.T) { + var o setLabelOptions + o.metadata = map[string]string{"key1": "foo", "key2": "bar"} + + m := makeKustomization(t) + require.NoError(t, o.setLabels(m)) + assert.Empty(t, m.Labels) + assert.Equal(t, m.CommonLabels, map[string]string{"app": "helloworld", "key1": "foo", "key2": "bar"}) + + o.metadata = map[string]string{"key2": "bar"} + o.labelsWithoutSelector = true + require.NoError(t, o.setLabels(m)) + assert.Equal(t, m.CommonLabels, map[string]string{"app": "helloworld", "key1": "foo"}) + assert.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key2": "bar"}}) +}