Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,65 @@ func getFromPrunerConfigResourceLevelwithSelector(namespacesSpec map[string]Name
return nil, ""
}

// getMatchingSelectorFromConfig retrieves the ConfigMap's selector that matches a resource
func getMatchingSelectorFromConfig(namespacesSpec map[string]NamespaceSpec, namespace, name string, selector SelectorSpec, resourceType PrunerResourceType) *SelectorSpec {
prunerResourceSpec, found := namespacesSpec[namespace]
if !found {
return nil
}

var resourceSpecs []ResourceSpec
switch resourceType {
case PrunerResourceTypePipelineRun:
resourceSpecs = prunerResourceSpec.PipelineRuns
case PrunerResourceTypeTaskRun:
resourceSpecs = prunerResourceSpec.TaskRuns
}

if len(selector.MatchAnnotations) == 0 && len(selector.MatchLabels) == 0 {
return nil
}

for _, resourceSpec := range resourceSpecs {
for _, selectorSpec := range resourceSpec.Selector {
annotationsMatch := true
labelsMatch := true

if len(selectorSpec.MatchAnnotations) > 0 {
if len(selector.MatchAnnotations) == 0 {
annotationsMatch = false
} else {
for key, value := range selectorSpec.MatchAnnotations {
if resourceAnnotationValue, exists := selector.MatchAnnotations[key]; !exists || resourceAnnotationValue != value {
annotationsMatch = false
break
}
}
}
}

if len(selectorSpec.MatchLabels) > 0 {
if len(selector.MatchLabels) == 0 {
labelsMatch = false
} else {
for key, value := range selectorSpec.MatchLabels {
if resourceLabelValue, exists := selector.MatchLabels[key]; !exists || resourceLabelValue != value {
labelsMatch = false
break
}
}
}
}

if annotationsMatch && labelsMatch {
return &selectorSpec
}
}
}

return nil
}

// getResourceFieldData retrieves configuration field values based on enforcedConfigLevel
// Design principle: Selector support ONLY for namespace-level ConfigMaps, NOT global ConfigMaps
//
Expand Down Expand Up @@ -650,6 +709,20 @@ func (ps *prunerConfigStore) GetTaskFailedHistoryLimitCount(namespace, name stri
return getResourceFieldData(ps.globalConfig, ps.namespaceConfig, namespace, name, selector, PrunerResourceTypeTaskRun, PrunerFieldTypeFailedHistoryLimit, enforcedConfigLevel)
}

// GetPipelineMatchingSelector returns the ConfigMap's selector that matches a PipelineRun.
func (ps *prunerConfigStore) GetPipelineMatchingSelector(namespace, name string, selector SelectorSpec) *SelectorSpec {
ps.mutex.RLock()
defer ps.mutex.RUnlock()
return getMatchingSelectorFromConfig(ps.namespaceConfig, namespace, name, selector, PrunerResourceTypePipelineRun)
}

// GetTaskMatchingSelector returns the ConfigMap's selector that matches a TaskRun.
func (ps *prunerConfigStore) GetTaskMatchingSelector(namespace, name string, selector SelectorSpec) *SelectorSpec {
ps.mutex.RLock()
defer ps.mutex.RUnlock()
return getMatchingSelectorFromConfig(ps.namespaceConfig, namespace, name, selector, PrunerResourceTypeTaskRun)
}

// ValidateGlobalConfig validates a GlobalConfig struct directly without ConfigMap conversion
// This is a convenience function for validating global config and all nested namespace configs
// without the overhead of serialization/deserialization through ConfigMaps.
Expand Down
41 changes: 36 additions & 5 deletions pkg/config/history_limiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type HistoryLimiterResourceFuncs interface {
IsCompleted(resource metav1.Object) bool
GetDefaultLabelKey() string
GetEnforcedConfigLevel(namespace, name string, selectors SelectorSpec) EnforcedConfigLevel
GetMatchingSelector(namespace, name string, selectors SelectorSpec) *SelectorSpec
}

// HistoryLimiter is a struct that encapsulates functionality for managing resources
Expand Down Expand Up @@ -276,15 +277,45 @@ func (hl *HistoryLimiter) doResourceCleanup(ctx context.Context, resource metav1
label := fmt.Sprintf("%s=%s", labelKey, resourceName)
resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), label)
case "identifiedBy_resource_selector":
// Filter by selector labels (namespace-level enforcement with selectors)
// Filter by the ConfigMap's selector labels only
matchingSelector := hl.resourceFn.GetMatchingSelector(resource.GetNamespace(), resourceName, resourceSelectors)
labelSelector := ""
for k, v := range resourceLabels {
if labelSelector != "" {
labelSelector += ","
if matchingSelector != nil && len(matchingSelector.MatchLabels) > 0 {
for k, v := range matchingSelector.MatchLabels {
if labelSelector != "" {
labelSelector += ","
}
labelSelector += fmt.Sprintf("%s=%s", k, v)
}
labelSelector += fmt.Sprintf("%s=%s", k, v)
}
logger.Debugw("listing resources with selector from ConfigMap",
"resource", hl.resourceFn.Type(),
"namespace", resource.GetNamespace(),
"labelSelector", labelSelector)
resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), labelSelector)
if err != nil {
return err
}
if matchingSelector != nil && len(matchingSelector.MatchAnnotations) > 0 {
filteredResources := []metav1.Object{}
for _, res := range resources {
resAnnotations := res.GetAnnotations()
if resAnnotations == nil {
continue
}
matches := true
for k, v := range matchingSelector.MatchAnnotations {
if resAnnotations[k] != v {
matches = false
break
}
}
if matches {
filteredResources = append(filteredResources, res)
}
}
resources = filteredResources
}
case "identifiedBy_resource_ann":
// Filter by annotations (converted to labels for listing)
labelSelector := ""
Expand Down
4 changes: 4 additions & 0 deletions pkg/config/history_limiter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ func (m *mockResourceFuncs) GetEnforcedConfigLevel(_, _ string, _ SelectorSpec)
return m.enforceLevel
}

func (m *mockResourceFuncs) GetMatchingSelector(_, _ string, _ SelectorSpec) *SelectorSpec {
return nil // Return nil to list all resources in namespace for tests
}

func TestNewHistoryLimiter(t *testing.T) {
tests := []struct {
name string
Expand Down
5 changes: 5 additions & 0 deletions pkg/reconciler/pipelinerun/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,3 +452,8 @@ func (prf *PrFuncs) GetFailedHistoryLimitCount(namespace, name string, selectors
func (prf *PrFuncs) GetEnforcedConfigLevel(namespace, name string, selectors config.SelectorSpec) config.EnforcedConfigLevel {
return config.PrunerConfigStore.GetPipelineEnforcedConfigLevel(namespace, name, selectors)
}

// GetMatchingSelector returns the ConfigMap's selector that matches a PipelineRun.
func (prf *PrFuncs) GetMatchingSelector(namespace, name string, selectors config.SelectorSpec) *config.SelectorSpec {
return config.PrunerConfigStore.GetPipelineMatchingSelector(namespace, name, selectors)
}
5 changes: 5 additions & 0 deletions pkg/reconciler/taskrun/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,3 +444,8 @@ func (trf *TrFuncs) GetFailedHistoryLimitCount(namespace, name string, selectors
func (trf *TrFuncs) GetEnforcedConfigLevel(namespace, name string, selectors config.SelectorSpec) config.EnforcedConfigLevel {
return config.PrunerConfigStore.GetTaskEnforcedConfigLevel(namespace, name, selectors)
}

// GetMatchingSelector returns the ConfigMap's selector that matches a TaskRun.
func (trf *TrFuncs) GetMatchingSelector(namespace, name string, selectors config.SelectorSpec) *config.SelectorSpec {
return config.PrunerConfigStore.GetTaskMatchingSelector(namespace, name, selectors)
}
Loading