Skip to content

Commit 7b7814f

Browse files
infernus01tekton-robot
authored andcommitted
fix: selector-based pruning for label and annotation selectors
Signed-off-by: Shubham Bhardwaj <shubbhar@redhat.com>
1 parent f47ece1 commit 7b7814f

File tree

5 files changed

+123
-5
lines changed

5 files changed

+123
-5
lines changed

pkg/config/config.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,65 @@ func getFromPrunerConfigResourceLevelwithSelector(namespacesSpec map[string]Name
317317
return nil, ""
318318
}
319319

320+
// getMatchingSelectorFromConfig retrieves the ConfigMap's selector that matches a resource
321+
func getMatchingSelectorFromConfig(namespacesSpec map[string]NamespaceSpec, namespace, name string, selector SelectorSpec, resourceType PrunerResourceType) *SelectorSpec {
322+
prunerResourceSpec, found := namespacesSpec[namespace]
323+
if !found {
324+
return nil
325+
}
326+
327+
var resourceSpecs []ResourceSpec
328+
switch resourceType {
329+
case PrunerResourceTypePipelineRun:
330+
resourceSpecs = prunerResourceSpec.PipelineRuns
331+
case PrunerResourceTypeTaskRun:
332+
resourceSpecs = prunerResourceSpec.TaskRuns
333+
}
334+
335+
if len(selector.MatchAnnotations) == 0 && len(selector.MatchLabels) == 0 {
336+
return nil
337+
}
338+
339+
for _, resourceSpec := range resourceSpecs {
340+
for _, selectorSpec := range resourceSpec.Selector {
341+
annotationsMatch := true
342+
labelsMatch := true
343+
344+
if len(selectorSpec.MatchAnnotations) > 0 {
345+
if len(selector.MatchAnnotations) == 0 {
346+
annotationsMatch = false
347+
} else {
348+
for key, value := range selectorSpec.MatchAnnotations {
349+
if resourceAnnotationValue, exists := selector.MatchAnnotations[key]; !exists || resourceAnnotationValue != value {
350+
annotationsMatch = false
351+
break
352+
}
353+
}
354+
}
355+
}
356+
357+
if len(selectorSpec.MatchLabels) > 0 {
358+
if len(selector.MatchLabels) == 0 {
359+
labelsMatch = false
360+
} else {
361+
for key, value := range selectorSpec.MatchLabels {
362+
if resourceLabelValue, exists := selector.MatchLabels[key]; !exists || resourceLabelValue != value {
363+
labelsMatch = false
364+
break
365+
}
366+
}
367+
}
368+
}
369+
370+
if annotationsMatch && labelsMatch {
371+
return &selectorSpec
372+
}
373+
}
374+
}
375+
376+
return nil
377+
}
378+
320379
// getResourceFieldData retrieves configuration field values based on enforcedConfigLevel
321380
// Design principle: Selector support ONLY for namespace-level ConfigMaps, NOT global ConfigMaps
322381
//
@@ -650,6 +709,20 @@ func (ps *prunerConfigStore) GetTaskFailedHistoryLimitCount(namespace, name stri
650709
return getResourceFieldData(ps.globalConfig, ps.namespaceConfig, namespace, name, selector, PrunerResourceTypeTaskRun, PrunerFieldTypeFailedHistoryLimit, enforcedConfigLevel)
651710
}
652711

712+
// GetPipelineMatchingSelector returns the ConfigMap's selector that matches a PipelineRun.
713+
func (ps *prunerConfigStore) GetPipelineMatchingSelector(namespace, name string, selector SelectorSpec) *SelectorSpec {
714+
ps.mutex.RLock()
715+
defer ps.mutex.RUnlock()
716+
return getMatchingSelectorFromConfig(ps.namespaceConfig, namespace, name, selector, PrunerResourceTypePipelineRun)
717+
}
718+
719+
// GetTaskMatchingSelector returns the ConfigMap's selector that matches a TaskRun.
720+
func (ps *prunerConfigStore) GetTaskMatchingSelector(namespace, name string, selector SelectorSpec) *SelectorSpec {
721+
ps.mutex.RLock()
722+
defer ps.mutex.RUnlock()
723+
return getMatchingSelectorFromConfig(ps.namespaceConfig, namespace, name, selector, PrunerResourceTypeTaskRun)
724+
}
725+
653726
// ValidateGlobalConfig validates a GlobalConfig struct directly without ConfigMap conversion
654727
// This is a convenience function for validating global config and all nested namespace configs
655728
// without the overhead of serialization/deserialization through ConfigMaps.

pkg/config/history_limiter.go

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type HistoryLimiterResourceFuncs interface {
5050
IsCompleted(resource metav1.Object) bool
5151
GetDefaultLabelKey() string
5252
GetEnforcedConfigLevel(namespace, name string, selectors SelectorSpec) EnforcedConfigLevel
53+
GetMatchingSelector(namespace, name string, selectors SelectorSpec) *SelectorSpec
5354
}
5455

5556
// HistoryLimiter is a struct that encapsulates functionality for managing resources
@@ -276,15 +277,45 @@ func (hl *HistoryLimiter) doResourceCleanup(ctx context.Context, resource metav1
276277
label := fmt.Sprintf("%s=%s", labelKey, resourceName)
277278
resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), label)
278279
case "identifiedBy_resource_selector":
279-
// Filter by selector labels (namespace-level enforcement with selectors)
280+
// Filter by the ConfigMap's selector labels only
281+
matchingSelector := hl.resourceFn.GetMatchingSelector(resource.GetNamespace(), resourceName, resourceSelectors)
280282
labelSelector := ""
281-
for k, v := range resourceLabels {
282-
if labelSelector != "" {
283-
labelSelector += ","
283+
if matchingSelector != nil && len(matchingSelector.MatchLabels) > 0 {
284+
for k, v := range matchingSelector.MatchLabels {
285+
if labelSelector != "" {
286+
labelSelector += ","
287+
}
288+
labelSelector += fmt.Sprintf("%s=%s", k, v)
284289
}
285-
labelSelector += fmt.Sprintf("%s=%s", k, v)
286290
}
291+
logger.Debugw("listing resources with selector from ConfigMap",
292+
"resource", hl.resourceFn.Type(),
293+
"namespace", resource.GetNamespace(),
294+
"labelSelector", labelSelector)
287295
resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), labelSelector)
296+
if err != nil {
297+
return err
298+
}
299+
if matchingSelector != nil && len(matchingSelector.MatchAnnotations) > 0 {
300+
filteredResources := []metav1.Object{}
301+
for _, res := range resources {
302+
resAnnotations := res.GetAnnotations()
303+
if resAnnotations == nil {
304+
continue
305+
}
306+
matches := true
307+
for k, v := range matchingSelector.MatchAnnotations {
308+
if resAnnotations[k] != v {
309+
matches = false
310+
break
311+
}
312+
}
313+
if matches {
314+
filteredResources = append(filteredResources, res)
315+
}
316+
}
317+
resources = filteredResources
318+
}
288319
case "identifiedBy_resource_ann":
289320
// Filter by annotations (converted to labels for listing)
290321
labelSelector := ""

pkg/config/history_limiter_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ func (m *mockResourceFuncs) GetEnforcedConfigLevel(_, _ string, _ SelectorSpec)
9999
return m.enforceLevel
100100
}
101101

102+
func (m *mockResourceFuncs) GetMatchingSelector(_, _ string, _ SelectorSpec) *SelectorSpec {
103+
return nil // Return nil to list all resources in namespace for tests
104+
}
105+
102106
func TestNewHistoryLimiter(t *testing.T) {
103107
tests := []struct {
104108
name string

pkg/reconciler/pipelinerun/reconciler.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,3 +452,8 @@ func (prf *PrFuncs) GetFailedHistoryLimitCount(namespace, name string, selectors
452452
func (prf *PrFuncs) GetEnforcedConfigLevel(namespace, name string, selectors config.SelectorSpec) config.EnforcedConfigLevel {
453453
return config.PrunerConfigStore.GetPipelineEnforcedConfigLevel(namespace, name, selectors)
454454
}
455+
456+
// GetMatchingSelector returns the ConfigMap's selector that matches a PipelineRun.
457+
func (prf *PrFuncs) GetMatchingSelector(namespace, name string, selectors config.SelectorSpec) *config.SelectorSpec {
458+
return config.PrunerConfigStore.GetPipelineMatchingSelector(namespace, name, selectors)
459+
}

pkg/reconciler/taskrun/reconciler.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,3 +444,8 @@ func (trf *TrFuncs) GetFailedHistoryLimitCount(namespace, name string, selectors
444444
func (trf *TrFuncs) GetEnforcedConfigLevel(namespace, name string, selectors config.SelectorSpec) config.EnforcedConfigLevel {
445445
return config.PrunerConfigStore.GetTaskEnforcedConfigLevel(namespace, name, selectors)
446446
}
447+
448+
// GetMatchingSelector returns the ConfigMap's selector that matches a TaskRun.
449+
func (trf *TrFuncs) GetMatchingSelector(namespace, name string, selectors config.SelectorSpec) *config.SelectorSpec {
450+
return config.PrunerConfigStore.GetTaskMatchingSelector(namespace, name, selectors)
451+
}

0 commit comments

Comments
 (0)