diff --git a/pkg/client/fake/client_test.go b/pkg/client/fake/client_test.go index b83aa09846..63d64d061c 100644 --- a/pkg/client/fake/client_test.go +++ b/pkg/client/fake/client_test.go @@ -122,8 +122,8 @@ var _ = Describe("Fake client", func() { Expect(list.Items).To(HaveLen(2)) }) - It("should support filtering by labels", func() { - By("Listing deployments with a particular label") + It("should support filtering by labels and their values", func() { + By("Listing deployments with a particular label and value") list := &appsv1.DeploymentList{} err := cl.List(nil, list, client.InNamespace("ns1"), client.MatchingLabels(map[string]string{ @@ -134,6 +134,16 @@ var _ = Describe("Fake client", func() { Expect(list.Items).To(ConsistOf(*dep2)) }) + It("should support filtering by label existence", func() { + By("Listing deployments with a particular label") + list := &appsv1.DeploymentList{} + err := cl.List(nil, list, client.InNamespace("ns1"), + client.HasLabels{"test-label"}) + Expect(err).To(BeNil()) + Expect(list.Items).To(HaveLen(1)) + Expect(list.Items).To(ConsistOf(*dep2)) + }) + It("should be able to Create", func() { By("Creating a new configmap") newcm := &corev1.ConfigMap{ diff --git a/pkg/client/options.go b/pkg/client/options.go index cd25b85033..c3c4a52e99 100644 --- a/pkg/client/options.go +++ b/pkg/client/options.go @@ -20,6 +20,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/selection" ) // {{{ "Functional" Option Interfaces @@ -388,6 +389,25 @@ func (m MatchingLabels) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) { m.ApplyToList(&opts.ListOptions) } +// HasLabels filters the list/delete operation checking if the set of labels exists +// without checking their values. +type HasLabels []string + +func (m HasLabels) ApplyToList(opts *ListOptions) { + sel := labels.NewSelector() + for _, label := range m { + r, err := labels.NewRequirement(label, selection.Exists, nil) + if err == nil { + sel = sel.Add(*r) + } + } + opts.LabelSelector = sel +} + +func (m HasLabels) ApplyToDeleteAllOf(opts *DeleteAllOfOptions) { + m.ApplyToList(&opts.ListOptions) +} + // MatchingLabelsSelector filters the list/delete operation on the given label // selector (or index in the case of cached lists). A struct is used because // labels.Selector is an interface, which cannot be aliased.