Skip to content

Commit 617f6e8

Browse files
author
Atanas Chuchev
committed
Add label selectors support to ConfigDynamic and validation tests
1 parent 9df05b3 commit 617f6e8

2 files changed

Lines changed: 279 additions & 6 deletions

File tree

pkg/datagatherer/k8sdynamic/dynamic.go

Lines changed: 39 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,21 @@ 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+
labelSelector := labels.Everything()
230+
for _, labelSelectorString := range c.LabelSelectors {
231+
selector, err := labels.Parse(labelSelectorString)
232+
if err != nil {
233+
return nil, err
234+
}
235+
reqs, selectable := selector.Requirements()
236+
if !selectable {
237+
return nil, fmt.Errorf("invalid label selector %q: not selectable", labelSelectorString)
238+
}
239+
labelSelector = labelSelector.Add(reqs...)
212240
}
213241

214242
// init cache to store gathered resources
@@ -217,6 +245,7 @@ func (c *ConfigDynamic) newDataGathererWithClient(ctx context.Context, cl dynami
217245
newDataGatherer := &DataGathererDynamic{
218246
groupVersionResource: c.GroupVersionResource,
219247
fieldSelector: fieldSelector.String(),
248+
labelSelector: labelSelector.String(),
220249
namespaces: c.IncludeNamespaces,
221250
cache: dgCache,
222251
}
@@ -237,6 +266,7 @@ func (c *ConfigDynamic) newDataGathererWithClient(ctx context.Context, cl dynami
237266
informers.WithNamespace(metav1.NamespaceAll),
238267
informers.WithTweakListOptions(func(options *metav1.ListOptions) {
239268
options.FieldSelector = fieldSelector.String()
269+
options.LabelSelector = labelSelector.String()
240270
}),
241271
)
242272
newDataGatherer.informer = informerFunc(factory)
@@ -249,6 +279,7 @@ func (c *ConfigDynamic) newDataGathererWithClient(ctx context.Context, cl dynami
249279
metav1.NamespaceAll,
250280
func(options *metav1.ListOptions) {
251281
options.FieldSelector = fieldSelector.String()
282+
options.LabelSelector = labelSelector.String()
252283
},
253284
)
254285
newDataGatherer.informer = factory.ForResource(c.GroupVersionResource).Informer()
@@ -293,6 +324,9 @@ type DataGathererDynamic struct {
293324
// returned by the Kubernetes API.
294325
// https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/
295326
fieldSelector string
327+
// labelSelector is a label selector string used to filter resources
328+
// returned by the Kubernetes API.
329+
labelSelector string
296330
// cache holds all resources watched by the data gatherer, default object expiry time 5 minutes
297331
// 30 seconds purge time https://pkg.go.dev/github.com/patrickmn/go-cache
298332
cache *cache.Cache

0 commit comments

Comments
 (0)