@@ -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