Skip to content

Commit 45e0e64

Browse files
authored
Merge pull request #768 from jetstack/feature/label-selector
Label selectors
2 parents 9df05b3 + 8692cfc commit 45e0e64

2 files changed

Lines changed: 280 additions & 6 deletions

File tree

pkg/datagatherer/k8sdynamic/dynamic.go

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import (
4949
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5050
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
5151
"k8s.io/apimachinery/pkg/fields"
52+
"k8s.io/apimachinery/pkg/labels"
5253
"k8s.io/apimachinery/pkg/runtime"
5354
"k8s.io/apimachinery/pkg/runtime/schema"
5455
"k8s.io/client-go/dynamic"
@@ -77,6 +78,8 @@ type ConfigDynamic struct {
7778
IncludeNamespaces []string `yaml:"include-namespaces"`
7879
// FieldSelectors is a list of field selectors to use when listing this resource
7980
FieldSelectors []string `yaml:"field-selectors"`
81+
// LabelSelectors is a list of label selectors to use when listing this resource
82+
LabelSelectors []string `yaml:"label-selectors"`
8083
}
8184

8285
// UnmarshalYAML unmarshals the ConfigDynamic resolving GroupVersionResource.
@@ -91,6 +94,7 @@ func (c *ConfigDynamic) UnmarshalYAML(unmarshal func(any) error) error {
9194
ExcludeNamespaces []string `yaml:"exclude-namespaces"`
9295
IncludeNamespaces []string `yaml:"include-namespaces"`
9396
FieldSelectors []string `yaml:"field-selectors"`
97+
LabelSelectors []string `yaml:"label-selectors"`
9498
}{}
9599
err := unmarshal(&aux)
96100
if err != nil {
@@ -104,6 +108,7 @@ func (c *ConfigDynamic) UnmarshalYAML(unmarshal func(any) error) error {
104108
c.ExcludeNamespaces = aux.ExcludeNamespaces
105109
c.IncludeNamespaces = aux.IncludeNamespaces
106110
c.FieldSelectors = aux.FieldSelectors
111+
c.LabelSelectors = aux.LabelSelectors
107112

108113
return nil
109114
}
@@ -119,16 +124,26 @@ func (c *ConfigDynamic) validate() error {
119124
errs = append(errs, "invalid configuration: GroupVersionResource.Resource cannot be empty")
120125
}
121126

122-
for i, selectorString := range c.FieldSelectors {
123-
if selectorString == "" {
127+
for i, fieldSelectorString := range c.FieldSelectors {
128+
if fieldSelectorString == "" {
124129
errs = append(errs, fmt.Sprintf("invalid field selector %d: must not be empty", i))
125130
}
126-
_, err := fields.ParseSelector(selectorString)
131+
_, err := fields.ParseSelector(fieldSelectorString)
127132
if err != nil {
128133
errs = append(errs, fmt.Sprintf("invalid field selector %d: %s", i, err))
129134
}
130135
}
131136

137+
for i, labelSelectorString := range c.LabelSelectors {
138+
if labelSelectorString == "" {
139+
errs = append(errs, fmt.Sprintf("invalid label selector %d: must not be empty", i))
140+
}
141+
_, err := labels.Parse(labelSelectorString)
142+
if err != nil {
143+
errs = append(errs, fmt.Sprintf("invalid label selector %d: %s", i, err))
144+
}
145+
}
146+
132147
if len(errs) > 0 {
133148
return errors.New(strings.Join(errs, ", "))
134149
}
@@ -207,8 +222,22 @@ func (c *ConfigDynamic) newDataGathererWithClient(ctx context.Context, cl dynami
207222
// Add any custom field selectors to the excluded namespaces selector
208223
// The selectors have already been validated, so it is safe to use
209224
// ParseSelectorOrDie here.
210-
for _, selectorString := range c.FieldSelectors {
211-
fieldSelector = fields.AndSelectors(fieldSelector, fields.ParseSelectorOrDie(selectorString))
225+
for _, fieldSelectorString := range c.FieldSelectors {
226+
fieldSelector = fields.AndSelectors(fieldSelector, fields.ParseSelectorOrDie(fieldSelectorString))
227+
}
228+
229+
// Add any custom label selectors
230+
// The selectors have already been validated, so Parse is expected to
231+
// succeed; any parse error is treated as a programming error.
232+
labelSelector := labels.Everything()
233+
for _, labelSelectorString := range c.LabelSelectors {
234+
selector, err := labels.Parse(labelSelectorString)
235+
if err != nil {
236+
panic(fmt.Sprintf("PROGRAMMING ERROR: should have been caught in validation: "+
237+
"failed to parse validated label selector %q: %v", labelSelectorString, err))
238+
}
239+
reqs, _ := selector.Requirements()
240+
labelSelector = labelSelector.Add(reqs...)
212241
}
213242

214243
// init cache to store gathered resources
@@ -217,6 +246,7 @@ func (c *ConfigDynamic) newDataGathererWithClient(ctx context.Context, cl dynami
217246
newDataGatherer := &DataGathererDynamic{
218247
groupVersionResource: c.GroupVersionResource,
219248
fieldSelector: fieldSelector.String(),
249+
labelSelector: labelSelector.String(),
220250
namespaces: c.IncludeNamespaces,
221251
cache: dgCache,
222252
}
@@ -237,6 +267,7 @@ func (c *ConfigDynamic) newDataGathererWithClient(ctx context.Context, cl dynami
237267
informers.WithNamespace(metav1.NamespaceAll),
238268
informers.WithTweakListOptions(func(options *metav1.ListOptions) {
239269
options.FieldSelector = fieldSelector.String()
270+
options.LabelSelector = labelSelector.String()
240271
}),
241272
)
242273
newDataGatherer.informer = informerFunc(factory)
@@ -249,6 +280,7 @@ func (c *ConfigDynamic) newDataGathererWithClient(ctx context.Context, cl dynami
249280
metav1.NamespaceAll,
250281
func(options *metav1.ListOptions) {
251282
options.FieldSelector = fieldSelector.String()
283+
options.LabelSelector = labelSelector.String()
252284
},
253285
)
254286
newDataGatherer.informer = factory.ForResource(c.GroupVersionResource).Informer()
@@ -293,6 +325,9 @@ type DataGathererDynamic struct {
293325
// returned by the Kubernetes API.
294326
// https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/
295327
fieldSelector string
328+
// labelSelector is a label selector string used to filter resources
329+
// returned by the Kubernetes API.
330+
labelSelector string
296331
// cache holds all resources watched by the data gatherer, default object expiry time 5 minutes
297332
// 30 seconds purge time https://pkg.go.dev/github.com/patrickmn/go-cache
298333
cache *cache.Cache

0 commit comments

Comments
 (0)