Skip to content

feat: add support to set labels without selector #5741

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion kustomize/commands/edit/add/addmetadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
Expand Down
67 changes: 64 additions & 3 deletions kustomize/commands/edit/set/setlabel.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import (
"fmt"
"slices"

"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/api/konfig"
Expand All @@ -15,8 +16,10 @@
)

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.
Expand All @@ -25,14 +28,20 @@
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}`,
RunE: func(cmd *cobra.Command, args []string) error {
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
}

Expand Down Expand Up @@ -62,6 +71,9 @@
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
Expand All @@ -74,9 +86,33 @@
}

func (o *setLabelOptions) setLabels(m *types.Kustomization) error {
o.removeDuplicateLabels(m)
if o.labelsWithoutSelector {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This follows kustomize edit add label behaviour, only adds new labels field if --without-selector flag is present, otherwise edits deprecated labels.

When should old behaviour removed? Meaning kustomize edit add label and kustomize edit set label with or without --without-selector adds new labels fields instead of commoLabels

labelIndex := slices.IndexFunc(m.Labels, func(label types.Label) bool {
return !label.IncludeSelectors && label.IncludeTemplates == o.includeTemplates
})

if labelIndex == -1 {
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)

}

Check failure on line 103 in kustomize/commands/edit/set/setlabel.go

View workflow job for this annotation

GitHub Actions / Lint

unnecessary trailing newline (whitespace)

labelPairs := m.Labels[labelIndex]
if labelPairs.Pairs == nil {
labelPairs.Pairs = make(map[string]string)
}
return o.writeToMap(labelPairs.Pairs)
}

if m.CommonLabels == nil {
m.CommonLabels = make(map[string]string)
}

return o.writeToMap(m.CommonLabels)
}

Expand All @@ -86,3 +122,28 @@
}
return nil
}

// removeDuplicateLabels removes duplicate labels from commonLabels or labels
func (o *setLabelOptions) removeDuplicateLabels(m *types.Kustomization) {
for k := range o.metadata {
// delete duplicate label from deprecated common labels
//nolint:staticcheck
delete(m.CommonLabels, k)
for idx, label := range m.Labels {
// delete label if it's already present in labels
if _, found := label.Pairs[k]; found {
m.Labels = deleteLabel(k, label, m.Labels, idx)
}
}
}
}

// deleteLabel deletes label from types.Label
func deleteLabel(key string, label types.Label, labels []types.Label, idx int) []types.Label {
delete(label.Pairs, key)
if len(label.Pairs) == 0 {
// remove empty map label.Pairs from labels
labels = append(labels[:idx], labels[idx+1:]...)
}
return labels
}
79 changes: 79 additions & 0 deletions kustomize/commands/edit/set/setlabel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package set
import (
"testing"

"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"
Expand Down Expand Up @@ -152,3 +153,81 @@ 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))
require.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))
require.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))
require.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))
require.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))
require.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo"}, IncludeTemplates: true})
require.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))
require.Empty(t, m.Labels)
//nolint:staticcheck
require.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))
//nolint:staticcheck
require.Equal(t, m.CommonLabels, map[string]string{"app": "helloworld", "key1": "foo"})
require.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key2": "bar"}})
}

func TestSetLabelCommonLabelWithWithoutSelector(t *testing.T) {
var o setLabelOptions
o.metadata = map[string]string{"key1": "foo", "key2": "bar"}

m := makeKustomization(t)
o.labelsWithoutSelector = true
require.NoError(t, o.setLabels(m))
require.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo", "key2": "bar"}})
//nolint:staticcheck
require.Equal(t, m.CommonLabels, map[string]string{"app": "helloworld"})

o.metadata = map[string]string{"key2": "bar2"}
o.labelsWithoutSelector = false
require.NoError(t, o.setLabels(m))
require.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"key1": "foo"}})
//nolint:staticcheck
require.Equal(t, m.CommonLabels, map[string]string{"app": "helloworld", "key2": "bar2"})
}
Loading