diff --git a/go.mod b/go.mod index 0a59aa238e..63ba3e3931 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/manifestival/manifestival v0.7.2 github.com/markbates/inflect v1.0.4 github.com/openshift-pipelines/pipelines-as-code v0.37.7 - github.com/openshift-pipelines/tektoncd-pruner v0.0.0-20250711075231-9c8624123820 + github.com/openshift-pipelines/tektoncd-pruner v0.0.0 github.com/openshift/api v0.0.0-20240521185306-0314f31e7774 github.com/openshift/apiserver-library-go v0.0.0-20230816171015-6bfafa975bfb github.com/openshift/client-go v0.0.0-20240523113335-452272e0496d diff --git a/go.sum b/go.sum index 0dec97bbab..58fe8bd71a 100644 --- a/go.sum +++ b/go.sum @@ -980,8 +980,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/openshift-pipelines/tektoncd-pruner v0.0.0-20250711075231-9c8624123820 h1:+/fO+q9MDSvTWGHrX4pm2ZZsnmyRKLhgTEMLXNwZ51c= -github.com/openshift-pipelines/tektoncd-pruner v0.0.0-20250711075231-9c8624123820/go.mod h1:Za44xopzfXjqqOzpn1t46BWr5aLL3KjJLt9pMsCzvL0= +github.com/openshift-pipelines/tektoncd-pruner v0.0.0 h1:RGmBW/Ex2STaBP6IegHehqKu/k6S+cvJDVofgkVoYuE= +github.com/openshift-pipelines/tektoncd-pruner v0.0.0/go.mod h1:UIp2hT6RNlfXpca0Dz2V67TwcmwP1ivwbkcniwsf7FY= github.com/openshift/api v0.0.0-20240521185306-0314f31e7774 h1:SsoLnIil/D0FcjUbQ9Z8h95B7rxFvrso2X6OQjR8jPw= github.com/openshift/api v0.0.0-20240521185306-0314f31e7774/go.mod h1:7Hm1kLJGxWT6eysOpD2zUztdn+w91eiERn6KtI5o9aw= github.com/openshift/apiserver-library-go v0.0.0-20230816171015-6bfafa975bfb h1:UMgJny13BBcHpY+JQ9Eg1Dm9+J7nWO3eqPvV1Zpd49A= diff --git a/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/config.go b/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/config.go index 9edb08bb8e..00a44ee03c 100644 --- a/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/config.go +++ b/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/config.go @@ -64,9 +64,14 @@ const ( // ResourceSpec is used to hold the config of a specific resource type ResourceSpec struct { - Name string `yaml:"name"` // Exact name of the parent Pipeline or Task - Selector []SelectorSpec `yaml:"selector,omitempty"` // Supports selection based on labels and annotations. If Name is given, Name taskes precedence - PrunerConfig `yaml:",inline"` + // EnforcedConfigLevel allowed values: global, namespace, resource (default: resource) + Name string `yaml:"name"` // Exact name of the parent Pipeline or Task + Selector []SelectorSpec `yaml:"selector,omitempty"` // Supports selection based on labels and annotations. If Name is given, Name taskes precedence + EnforcedConfigLevel *EnforcedConfigLevel `yaml:"enforcedConfigLevel"` + TTLSecondsAfterFinished *int32 `yaml:"ttlSecondsAfterFinished"` + SuccessfulHistoryLimit *int32 `yaml:"successfulHistoryLimit"` + FailedHistoryLimit *int32 `yaml:"failedHistoryLimit"` + HistoryLimit *int32 `yaml:"historyLimit"` } // SelectorSpec allows specifying selectors for matching resources like PipelineRun or TaskRun @@ -78,30 +83,31 @@ type SelectorSpec struct { // NamespaceSpec is used to hold the pruning config of a specific namespace and its resources type NamespaceSpec struct { - PrunerConfig `yaml:",inline"` - PipelineRuns []ResourceSpec `yaml:"pipelineRuns"` - TaskRuns []ResourceSpec `yaml:"taskRuns"` -} - -type GlobalConfig struct { - PrunerConfig `yaml:",inline"` - Namespaces map[string]NamespaceSpec `yaml:"namespaces" json:"namespaces"` + // EnforcedConfigLevel allowed values: global, namespace, resource (default: resource) + EnforcedConfigLevel *EnforcedConfigLevel `yaml:"enforcedConfigLevel"` + TTLSecondsAfterFinished *int32 `yaml:"ttlSecondsAfterFinished"` + SuccessfulHistoryLimit *int32 `yaml:"successfulHistoryLimit"` + FailedHistoryLimit *int32 `yaml:"failedHistoryLimit"` + HistoryLimit *int32 `yaml:"historyLimit"` + PipelineRuns []ResourceSpec `yaml:"pipelineRuns"` + TaskRuns []ResourceSpec `yaml:"taskRuns"` } // PrunerConfig used to hold the cluster-wide pruning config as well as namespace specific pruning config type PrunerConfig struct { // EnforcedConfigLevel allowed values: global, namespace, resource (default: resource) - EnforcedConfigLevel *EnforcedConfigLevel `yaml:"enforcedConfigLevel" json:"enforcedConfigLevel"` - TTLSecondsAfterFinished *int32 `yaml:"ttlSecondsAfterFinished" json:"ttlSecondsAfterFinished"` - SuccessfulHistoryLimit *int32 `yaml:"successfulHistoryLimit" json:"successfulHistoryLimit"` - FailedHistoryLimit *int32 `yaml:"failedHistoryLimit" json:"failedHistoryLimit"` - HistoryLimit *int32 `yaml:"historyLimit" json:"historyLimit"` + EnforcedConfigLevel *EnforcedConfigLevel `yaml:"enforcedConfigLevel"` + TTLSecondsAfterFinished *int32 `yaml:"ttlSecondsAfterFinished"` + SuccessfulHistoryLimit *int32 `yaml:"successfulHistoryLimit"` + FailedHistoryLimit *int32 `yaml:"failedHistoryLimit"` + HistoryLimit *int32 `yaml:"historyLimit"` + Namespaces map[string]NamespaceSpec `yaml:"namespaces"` } // prunerConfigStore defines the store structure to hold config from ConfigMap type prunerConfigStore struct { mutex sync.RWMutex - globalConfig GlobalConfig + globalConfig PrunerConfig } var ( @@ -118,7 +124,7 @@ func (ps *prunerConfigStore) LoadGlobalConfig(ctx context.Context, configMap *co // Log the current state of globalConfig and namespacedConfig before updating logger.Debugw("Loading global config", "oldGlobalConfig", ps.globalConfig) - globalConfig := &GlobalConfig{} + globalConfig := &PrunerConfig{} if configMap.Data != nil && configMap.Data[PrunerGlobalConfigKey] != "" { err := yaml.Unmarshal([]byte(configMap.Data[PrunerGlobalConfigKey]), globalConfig) if err != nil { @@ -138,25 +144,6 @@ func (ps *prunerConfigStore) LoadGlobalConfig(ctx context.Context, configMap *co return nil } -// loads config from configMap (global-config) should be called on startup and if there is a change detected on the ConfigMap -func (ps *prunerConfigStore) WorkerCount(ctx context.Context, configMap *corev1.ConfigMap) (count int, err error) { - logger := logging.FromContext(ctx) - - // Log the current state of globalConfig and namespacedConfig before updating - logger.Debugw("get worker count to concurrently cleanup namesapces", "nsCleanupConcurrentWorkerCount", configMap.Data["WorkerCountForNamespaceCleanup"]) - - if configMap.Data != nil && configMap.Data["WorkerCountForNamespaceCleanup"] != "" { - count, err = GetEnvValueAsInt("WorkerCountForNamespaceCleanup", DefaultWorkerCountForNamespaceCleanup) - if err != nil { - return 0, err - } - } else { - count = DefaultWorkerCountForNamespaceCleanup - } - logger.Debugw("get worker count to concurrently cleanup namesapces", "nsCleanupConcurrentWorkerCount", count) - return count, nil -} - func getFromPrunerConfigResourceLevelwithSelector(namespacesSpec map[string]NamespaceSpec, namespace, name string, selector SelectorSpec, resourceType PrunerResourceType, fieldType PrunerFieldType) (*int32, string) { prunerResourceSpec, found := namespacesSpec[namespace] if !found { @@ -260,87 +247,47 @@ func getFromPrunerConfigResourceLevelwithSelector(namespacesSpec map[string]Name return nil, "" } -func getResourceFieldData(globalSpec GlobalConfig, namespace, name string, selector SelectorSpec, resourceType PrunerResourceType, fieldType PrunerFieldType, enforcedConfigLevel EnforcedConfigLevel) (*int32, string) { +func getResourceFieldData(globalSpec PrunerConfig, namespace, name string, selector SelectorSpec, resourceType PrunerResourceType, fieldType PrunerFieldType, enforcedConfigLevel EnforcedConfigLevel) (*int32, string) { var fieldData *int32 var identified_by string switch enforcedConfigLevel { case EnforcedConfigLevelResource: - // First try resource level + // get from resource level fieldData, identified_by = getFromPrunerConfigResourceLevelwithSelector(globalSpec.Namespaces, namespace, name, selector, resourceType, fieldType) - if fieldData != nil { - return fieldData, identified_by - } - // If no resource level config found, try namespace level - spec, found := globalSpec.Namespaces[namespace] - if found { - switch fieldType { - case PrunerFieldTypeTTLSecondsAfterFinished: - fieldData = spec.TTLSecondsAfterFinished - - case PrunerFieldTypeSuccessfulHistoryLimit: - if spec.SuccessfulHistoryLimit != nil { - fieldData = spec.SuccessfulHistoryLimit - } else { - fieldData = spec.HistoryLimit - } - - case PrunerFieldTypeFailedHistoryLimit: - if spec.FailedHistoryLimit != nil { - fieldData = spec.FailedHistoryLimit - } else { - fieldData = spec.HistoryLimit - } - } - identified_by = "identified_by_ns" - } else { - // If no namespace level config found, try global level - switch fieldType { - case PrunerFieldTypeTTLSecondsAfterFinished: - fieldData = globalSpec.TTLSecondsAfterFinished - - case PrunerFieldTypeSuccessfulHistoryLimit: - if globalSpec.SuccessfulHistoryLimit != nil { - fieldData = globalSpec.SuccessfulHistoryLimit - } else { - fieldData = globalSpec.HistoryLimit - } + fallthrough - case PrunerFieldTypeFailedHistoryLimit: - if globalSpec.FailedHistoryLimit != nil { - fieldData = globalSpec.FailedHistoryLimit - } else { - fieldData = globalSpec.HistoryLimit - } - } - identified_by = "identified_by_global" - } - return fieldData, identified_by case EnforcedConfigLevelNamespace: - // get it from global spec, namespace root level - spec, found := globalSpec.Namespaces[namespace] - if found { - switch fieldType { - case PrunerFieldTypeTTLSecondsAfterFinished: - fieldData = spec.TTLSecondsAfterFinished + if fieldData == nil { + // get it from global spec, namespace root level + spec, found := globalSpec.Namespaces[namespace] + if found { + switch fieldType { + case PrunerFieldTypeTTLSecondsAfterFinished: + fieldData = spec.TTLSecondsAfterFinished - case PrunerFieldTypeSuccessfulHistoryLimit: - if spec.SuccessfulHistoryLimit != nil { - fieldData = spec.SuccessfulHistoryLimit - } else { - fieldData = spec.HistoryLimit - } + case PrunerFieldTypeSuccessfulHistoryLimit: + if spec.SuccessfulHistoryLimit != nil { + fieldData = spec.SuccessfulHistoryLimit + } else { + fieldData = spec.HistoryLimit + } - case PrunerFieldTypeFailedHistoryLimit: - if spec.FailedHistoryLimit != nil { - fieldData = spec.FailedHistoryLimit - } else { - fieldData = spec.HistoryLimit + case PrunerFieldTypeFailedHistoryLimit: + if spec.FailedHistoryLimit != nil { + fieldData = spec.FailedHistoryLimit + } else { + fieldData = spec.HistoryLimit + } } } identified_by = "identified_by_ns" - } else { - // If no namespace level config found, try global level + } + fallthrough + + case EnforcedConfigLevelGlobal: + if fieldData == nil { + // get it from global spec, root level switch fieldType { case PrunerFieldTypeTTLSecondsAfterFinished: fieldData = globalSpec.TTLSecondsAfterFinished @@ -359,31 +306,9 @@ func getResourceFieldData(globalSpec GlobalConfig, namespace, name string, selec fieldData = globalSpec.HistoryLimit } } - identified_by = "identified_by_global" - } - return fieldData, identified_by - - case EnforcedConfigLevelGlobal: - // get it from global spec, root level - switch fieldType { - case PrunerFieldTypeTTLSecondsAfterFinished: - fieldData = globalSpec.TTLSecondsAfterFinished - - case PrunerFieldTypeSuccessfulHistoryLimit: - if globalSpec.SuccessfulHistoryLimit != nil { - fieldData = globalSpec.SuccessfulHistoryLimit - } else { - fieldData = globalSpec.HistoryLimit - } - - case PrunerFieldTypeFailedHistoryLimit: - if globalSpec.FailedHistoryLimit != nil { - fieldData = globalSpec.FailedHistoryLimit - } else { - fieldData = globalSpec.HistoryLimit - } } identified_by = "identified_by_global" + } return fieldData, identified_by @@ -391,78 +316,81 @@ func getResourceFieldData(globalSpec GlobalConfig, namespace, name string, selec func (ps *prunerConfigStore) GetEnforcedConfigLevelFromNamespaceSpec(namespacesSpec map[string]NamespaceSpec, namespace, name string, selector SelectorSpec, resourceType PrunerResourceType) *EnforcedConfigLevel { var enforcedConfigLevel *EnforcedConfigLevel + var resourceSpecs []ResourceSpec + var namespaceSpec NamespaceSpec + var found bool - namespaceSpec, found := ps.globalConfig.Namespaces[namespace] - if !found { - return nil - } + namespaceSpec, found = ps.globalConfig.Namespaces[namespace] + if found { + switch resourceType { + case PrunerResourceTypePipelineRun: + resourceSpecs = namespaceSpec.PipelineRuns - // Get the appropriate resource specs based on type - var resourceSpecs []ResourceSpec - switch resourceType { - case PrunerResourceTypePipelineRun: - resourceSpecs = namespaceSpec.PipelineRuns - case PrunerResourceTypeTaskRun: - resourceSpecs = namespaceSpec.TaskRuns - } + case PrunerResourceTypeTaskRun: + resourceSpecs = namespaceSpec.TaskRuns + } - // Try to find resource level config first - if name != "" && (len(selector.MatchAnnotations) == 0 && len(selector.MatchLabels) == 0) { - // Search by exact name - for _, resourceSpec := range resourceSpecs { - if resourceSpec.Name == name { - enforcedConfigLevel = resourceSpec.EnforcedConfigLevel - if enforcedConfigLevel != nil { - return enforcedConfigLevel + if name != "" { + for _, resourceSpec := range resourceSpecs { + if resourceSpec.Name == name { + // if found on resource level + enforcedConfigLevel = resourceSpec.EnforcedConfigLevel + if enforcedConfigLevel != nil { + return enforcedConfigLevel + } + break } - break } - } - } else if len(selector.MatchAnnotations) > 0 || len(selector.MatchLabels) > 0 { - // Search by selectors - for _, resourceSpec := range resourceSpecs { - for _, selectorSpec := range resourceSpec.Selector { - // Try annotation matching first - if len(selector.MatchAnnotations) > 0 { - match := true - for key, value := range selector.MatchAnnotations { - if resourceAnnotationValue, exists := selectorSpec.MatchAnnotations[key]; !exists || resourceAnnotationValue != value { - match = false + } else if len(selector.MatchAnnotations) > 0 || len(selector.MatchLabels) > 0 { + // If name is not provided, we proceed with selector matching + + for _, resourceSpec := range resourceSpecs { + // Check if the resourceSpec matches the provided selector by annotations or labels + for _, selectorSpec := range resourceSpec.Selector { + // Match by annotations if provided in the selector + if len(selector.MatchAnnotations) > 0 { + match := true + for key, value := range selector.MatchAnnotations { + if resourceAnnotationValue, exists := selectorSpec.MatchAnnotations[key]; !exists || resourceAnnotationValue != value { + match = false + break + } + } + if match { + enforcedConfigLevel = resourceSpec.EnforcedConfigLevel + if enforcedConfigLevel != nil { + return enforcedConfigLevel + } break } } - if match { - enforcedConfigLevel = resourceSpec.EnforcedConfigLevel - if enforcedConfigLevel != nil { - return enforcedConfigLevel + // Match by labels if provided in the selector + if len(selector.MatchLabels) > 0 { + match := true + for key, value := range selector.MatchLabels { + if resourceLabelValue, exists := selectorSpec.MatchLabels[key]; !exists || resourceLabelValue != value { + match = false + break + } } - break - } - } - - // Try label matching if no annotation match - if len(selector.MatchLabels) > 0 { - match := true - for key, value := range selector.MatchLabels { - if resourceLabelValue, exists := selectorSpec.MatchLabels[key]; !exists || resourceLabelValue != value { - match = false + if match { + enforcedConfigLevel = resourceSpec.EnforcedConfigLevel + if enforcedConfigLevel != nil { + return enforcedConfigLevel + } break } } - if match { - enforcedConfigLevel = resourceSpec.EnforcedConfigLevel - if enforcedConfigLevel != nil { - return enforcedConfigLevel - } - break - } } } } + // get it from namespace root level + enforcedConfigLevel = namespaceSpec.EnforcedConfigLevel + if enforcedConfigLevel != nil { + return enforcedConfigLevel + } } - - // If no resource level config found or it was nil, return namespace level - return namespaceSpec.EnforcedConfigLevel + return nil //yet to include a default enforced config global option. For now,only if it set in teh config map this would work } func (ps *prunerConfigStore) getEnforcedConfigLevel(namespace, name string, selector SelectorSpec, resourceType PrunerResourceType) EnforcedConfigLevel { diff --git a/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/config_defaults.go b/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/config_defaults.go deleted file mode 100644 index c457b441c6..0000000000 --- a/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/config_defaults.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2025 The Tekton Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -import "knative.dev/pkg/ptr" - -func (pc *PrunerConfig) SetDefaults() { - if pc.HistoryLimit == nil { - pc.HistoryLimit = ptr.Int32(DefaultHistoryLimit) - } - if pc.EnforcedConfigLevel == nil { - v := EnforcedConfigLevelGlobal - pc.EnforcedConfigLevel = &v - } -} diff --git a/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/constants.go b/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/constants.go index 82e9c4e3e6..abd18ce8b3 100644 --- a/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/constants.go +++ b/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/constants.go @@ -96,13 +96,6 @@ const ( // DefaultGCInterval represents // interval in seconds for the periodic cleanup i.e garbage collector to run DefaultPeriodicCleanupIntervalSeconds = 600 // 10 minutes - - // DefaultWorkerCountForNamespaceCleanup represents the number of workers to be used - // for cleaning up resources in a namespace concurrently - DefaultWorkerCountForNamespaceCleanup = 5 - - // DefaultHistoryLimit represents the default history limit for successful and failed resources - DefaultHistoryLimit = 100 ) // GetEnvValueAsInt fetches the value of an environment variable and converts it to an integer diff --git a/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/history_limiter.go b/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/history_limiter.go index 5a340e0bf3..25d013c08f 100644 --- a/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/history_limiter.go +++ b/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/history_limiter.go @@ -20,7 +20,6 @@ import ( "context" "encoding/json" "fmt" - "math" "slices" "strconv" "time" @@ -78,7 +77,7 @@ func NewHistoryLimiter(resourceFn HistoryLimiterResourceFuncs) (*HistoryLimiter, // or failed resources func (hl *HistoryLimiter) ProcessEvent(ctx context.Context, resource metav1.Object) error { logger := logging.FromContext(ctx) - logger.Debugw("processing an event for limit logic", "resource", hl.resourceFn.Type(), "namespace", resource.GetNamespace(), "name", resource.GetName()) + logger.Debugw("processing an event", "resource", hl.resourceFn.Type(), "namespace", resource.GetNamespace(), "name", resource.GetName()) // if the resource is on deletion state, no action needed if resource.GetDeletionTimestamp() != nil { @@ -191,111 +190,198 @@ func (hl *HistoryLimiter) isSuccessfulResource(resource metav1.Object) bool { return hl.resourceFn.IsCompleted(resource) && hl.resourceFn.IsSuccessful(resource) } -func (hl *HistoryLimiter) doResourceCleanup(ctx context.Context, resource metav1.Object, historyLimitAnnotation string, getHistoryLimitFn func(string, string, SelectorSpec) (*int32, string), getResourceFilterFn func(metav1.Object) bool) error { +/*func (hl *HistoryLimiter) doResourceCleanup_old(ctx context.Context, resource metav1.Object, historyLimitAnnotation string, getHistoryLimitFn func(string, string, SelectorSpec) *int32, getResourceFilterFn func(metav1.Object) bool) error { logger := logging.FromContext(ctx) // get the label key and resource name labelKey := getResourceNameLabelKey(resource, hl.resourceFn.GetDefaultLabelKey()) resourceName := getResourceName(resource, labelKey) - // Get Annotations and Labels resourceAnnotations := resource.GetAnnotations() resourceLabels := resource.GetLabels() // Construct the selectors with both matchLabels and matchAnnotations resourceSelectors := SelectorSpec{} + if len(resourceAnnotations) > 0 { resourceSelectors.MatchAnnotations = resourceAnnotations } + if len(resourceLabels) > 0 { resourceSelectors.MatchLabels = resourceLabels } - // Get enforced config level first + // step1: evaluate the configstore to get the enforcedConfigLevel. enforcedConfigLevel := hl.resourceFn.GetEnforcedConfigLevel(resource.GetNamespace(), resourceName, resourceSelectors) logger.Debugw("enforcedConfigLevel for the resource is", "resourceName", resourceName, "enforcedlevel", enforcedConfigLevel) - // Get configured history limit + // 5. Get History Limit: var historyLimit *int32 - var identifiedBy string - configHistoryLimit, configIdentifiedBy := getHistoryLimitFn(resource.GetNamespace(), resourceName, resourceSelectors) - - // For resource-level enforcement, check annotation only if it matches config annotations := resource.GetAnnotations() if enforcedConfigLevel == EnforcedConfigLevelResource && len(annotations) != 0 && annotations[historyLimitAnnotation] != "" { - annotationLimit, err := strconv.Atoi(annotations[historyLimitAnnotation]) + _limit, err := strconv.Atoi(annotations[historyLimitAnnotation]) if err != nil { - logger.Errorw("error converting history limit annotation to int", - "resource", hl.resourceFn.Type(), - "namespace", resource.GetNamespace(), - "name", resource.GetName(), - "annotation", historyLimitAnnotation, - "value", annotations[historyLimitAnnotation], + logger.Errorw("error on converting history limit to int", "resource", hl.resourceFn.Type(), + "namespace", resource.GetNamespace(), "name", resource.GetName(), "historyLimitAnnotation", historyLimitAnnotation, + "historyLimitValue", annotations[historyLimitAnnotation], zap.Error(err)) return err } - // Check bounds before converting to int32 - if annotationLimit < 0 || annotationLimit > math.MaxInt32 { - logger.Errorw("history limit annotation value out of bounds for int32", - "resource", hl.resourceFn.Type(), - "namespace", resource.GetNamespace(), - "name", resource.GetName(), - "annotation", historyLimitAnnotation, - "value", annotationLimit) - return fmt.Errorf("history limit value %d is out of bounds for type int32", annotationLimit) - } - - // Only use annotation value if it matches configured value - if configHistoryLimit != nil && annotationLimit == int(*configHistoryLimit) { - historyLimit = ptr.Int32(int32(annotationLimit)) - identifiedBy = "identifiedBy_resource_ann" - } else { - historyLimit = configHistoryLimit - identifiedBy = configIdentifiedBy - } + historyLimit = ptr.Int32(int32(_limit)) } else { - historyLimit = configHistoryLimit - identifiedBy = configIdentifiedBy + historyLimit = getHistoryLimitFn(resource.GetNamespace(), resourceName, resourceSelectors) } - logger.Debugw("historylimit for the resource", "resourcename", resourceName, "limit", historyLimit, "identifiedBy", identifiedBy) + logger.Debugw("historylimit for the resource", "resourcename", resourceName, "limit", historyLimit) if historyLimit == nil || *historyLimit < 0 { return nil } - // List Resources (using appropriate selector based on enforcement level and identifier) + // 6. List Resources (using matchLabels or label selector): var resources []metav1.Object var err error - if enforcedConfigLevel == EnforcedConfigLevelResource { - switch identifiedBy { - case "identifiedBy_resource_name": - label := fmt.Sprintf("%s=%s", labelKey, resourceName) - resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), label) - case "identifiedBy_resource_ann": - labelSelector := "" - for k, v := range resourceAnnotations { - if labelSelector != "" { - labelSelector += "," - } - labelSelector += fmt.Sprintf("%s=%s", k, v) + if len(resourceLabels) > 0 { + labelSelector := "" + for k, v := range resourceLabels { + if labelSelector != "" { + labelSelector += "," } - resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), labelSelector) - case "identifiedBy_resource_label": - labelSelector := "" - for k, v := range resourceLabels { - if labelSelector != "" { - labelSelector += "," - } - labelSelector += fmt.Sprintf("%s=%s", k, v) + labelSelector += fmt.Sprintf("%s=%s", k, v) + } + resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), labelSelector) + } else { + label := fmt.Sprintf("%s=%s", labelKey, resourceName) + resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), label) + } + + if err != nil { + return err + } + + // 7. Filter, Sort, and Delete: + resourcesFiltered := []metav1.Object{} + for _, res := range resources { + if getResourceFilterFn(res) { + resourcesFiltered = append(resourcesFiltered, res) + } + } + resources = resourcesFiltered + + if int(*historyLimit) > len(resources) { + return nil + } + + slices.SortStableFunc(resources, func(a, b metav1.Object) int { + objA := a.GetCreationTimestamp() + objB := b.GetCreationTimestamp() + if objA.Time.Before(objB.Time) { + return 1 + } else if objA.Time.After(objB.Time) { + return -1 + } + return 0 + }) + + var selectionForDeletion []metav1.Object + + if *historyLimit == 0 { + selectionForDeletion = resources + } else { + selectionForDeletion = resources[*historyLimit:] + } + + for _, _res := range selectionForDeletion { + logger.Debugw("deleting a resource", + "resource", hl.resourceFn.Type(), "namespace", _res.GetNamespace(), "name", _res.GetName(), + "resourceCreationTimestamp", _res.GetCreationTimestamp(), + ) + err := hl.resourceFn.Delete(ctx, _res.GetNamespace(), _res.GetName()) + if err != nil { + if errors.IsNotFound(err) { + return nil } - resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), labelSelector) - default: - resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), "") + logger.Errorw("error on removing a resource", + "resource", hl.resourceFn.Type(), "namespace", _res.GetNamespace(), "name", _res.GetName(), + zap.Error(err), + ) + } + } + + return nil +} +*/ + +func (hl *HistoryLimiter) doResourceCleanup(ctx context.Context, resource metav1.Object, historyLimitAnnotation string, getHistoryLimitFn func(string, string, SelectorSpec) (*int32, string), getResourceFilterFn func(metav1.Object) bool) error { + logger := logging.FromContext(ctx) + + // get the label key and resource name + labelKey := getResourceNameLabelKey(resource, hl.resourceFn.GetDefaultLabelKey()) + resourceName := getResourceName(resource, labelKey) + // Get Annotations and Labels + resourceAnnotations := resource.GetAnnotations() + resourceLabels := resource.GetLabels() + + // Construct the selectors with both matchLabels and matchAnnotations + resourceSelectors := SelectorSpec{} + + if len(resourceAnnotations) > 0 { + resourceSelectors.MatchAnnotations = resourceAnnotations + } + + if len(resourceLabels) > 0 { + resourceSelectors.MatchLabels = resourceLabels + } + + // step1: evaluate the configstore to get the enforcedConfigLevel. + enforcedConfigLevel := hl.resourceFn.GetEnforcedConfigLevel(resource.GetNamespace(), resourceName, resourceSelectors) + logger.Debugw("enforcedConfigLevel for the resource is", "resourceName", resourceName, "enforcedlevel", enforcedConfigLevel) + + // 5. Get History Limit: + var historyLimit *int32 + + var identifiedBy string + + annotations := resource.GetAnnotations() + if enforcedConfigLevel == EnforcedConfigLevelResource && len(annotations) != 0 && annotations[historyLimitAnnotation] != "" { + _limit, err := strconv.Atoi(annotations[historyLimitAnnotation]) + if err != nil { + logger.Errorw("error on converting history limit to int", "resource", hl.resourceFn.Type(), + "namespace", resource.GetNamespace(), "name", resource.GetName(), "historyLimitAnnotation", historyLimitAnnotation, + "historyLimitValue", annotations[historyLimitAnnotation], + zap.Error(err)) + return err } + historyLimit = ptr.Int32(int32(_limit)) } else { - // For namespace or global level, list all resources in namespace + historyLimit, identifiedBy = getHistoryLimitFn(resource.GetNamespace(), resourceName, resourceSelectors) + } + + logger.Debugw("historylimit for the resource", "resourcename", resourceName, "limit", historyLimit, "identifiedBy", identifiedBy) + + if historyLimit == nil || *historyLimit < 0 { + return nil + } + + // 6. List Resources (using matchLabels or label selector): + var resources []metav1.Object + var err error + + label := fmt.Sprintf("%s=%s", labelKey, resourceName) + + if enforcedConfigLevel == EnforcedConfigLevelResource && len(annotations) != 0 && annotations[historyLimitAnnotation] != "" { + resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), label) + } else if enforcedConfigLevel == EnforcedConfigLevelResource && identifiedBy == "identifiedBy_resource_name" { + resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), label) + } else if enforcedConfigLevel == EnforcedConfigLevelResource && identifiedBy == "identifiedBy_resource_ann" { + resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), label) //This needs a fix. We need to pass the annotations + } else if enforcedConfigLevel == EnforcedConfigLevelResource && identifiedBy == "identifiedBy_resource_label" { + label := fmt.Sprintf("%s=%s", labelKey, resourceName) + resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), label) //This needs a fix. We need to pass the labels + } else if identifiedBy == "identified_by_ns" { + resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), "") + } else if identifiedBy == "identified_by_global" { resources, err = hl.resourceFn.List(ctx, resource.GetNamespace(), "") } @@ -303,7 +389,7 @@ func (hl *HistoryLimiter) doResourceCleanup(ctx context.Context, resource metav1 return err } - // Filter resources by status (success/failed) + // 7. Filter, Sort, and Delete: resourcesFiltered := []metav1.Object{} for _, res := range resources { if getResourceFilterFn(res) { @@ -316,7 +402,6 @@ func (hl *HistoryLimiter) doResourceCleanup(ctx context.Context, resource metav1 return nil } - // Sort resources by creation timestamp (newest first) slices.SortStableFunc(resources, func(a, b metav1.Object) int { objA := a.GetCreationTimestamp() objB := b.GetCreationTimestamp() @@ -328,33 +413,28 @@ func (hl *HistoryLimiter) doResourceCleanup(ctx context.Context, resource metav1 return 0 }) - // Select resources to delete (keep newest up to historyLimit) var selectionForDeletion []metav1.Object + if *historyLimit == 0 { selectionForDeletion = resources } else { selectionForDeletion = resources[*historyLimit:] } - // Delete selected resources - for _, res := range selectionForDeletion { - logger.Debugw("deleting resource", - "resource", hl.resourceFn.Type(), - "namespace", res.GetNamespace(), - "name", res.GetName(), - "creationTimestamp", res.GetCreationTimestamp(), + for _, _res := range selectionForDeletion { + logger.Debugw("deleting a resource", + "resource", hl.resourceFn.Type(), "namespace", _res.GetNamespace(), "name", _res.GetName(), + "resourceCreationTimestamp", _res.GetCreationTimestamp(), ) - if err := hl.resourceFn.Delete(ctx, res.GetNamespace(), res.GetName()); err != nil { + err := hl.resourceFn.Delete(ctx, _res.GetNamespace(), _res.GetName()) + if err != nil { if errors.IsNotFound(err) { - continue + return nil } - logger.Errorw("error deleting resource", - "resource", hl.resourceFn.Type(), - "namespace", res.GetNamespace(), - "name", res.GetName(), + logger.Errorw("error on removing a resource", + "resource", hl.resourceFn.Type(), "namespace", _res.GetNamespace(), "name", _res.GetName(), zap.Error(err), ) - return err } } diff --git a/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/ttl_handler.go b/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/ttl_handler.go index af249672cf..8794b109ac 100644 --- a/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/ttl_handler.go +++ b/vendor/github.com/openshift-pipelines/tektoncd-pruner/pkg/config/ttl_handler.go @@ -31,13 +31,6 @@ import ( "knative.dev/pkg/logging" ) -const ( - // NoTTL represents a TTL value that indicates no TTL should be applied - NoTTL = "-1" - // DefaultTTL represents the default TTL duration if none is specified - DefaultTTL = 0 -) - // TTLResourceFuncs defines the set of functions that should be implemented for // resources that are subject to Time-To-Live (TTL) management, including determining // whether a resource is completed, updating or deleting the resource, and handling @@ -85,13 +78,22 @@ func NewTTLHandler(clock clockUtil.Clock, resourceFn TTLResourceFuncs) (*TTLHand // It evaluates the resource's state, checks whether it should be cleaned up, // and updates the TTL annotation if needed func (th *TTLHandler) ProcessEvent(ctx context.Context, resource metav1.Object) error { + logger := logging.FromContext(ctx) + logger.Debugw("processing an event for TTLLOGIC", + "resource", th.resourceFn.Type(), "namespace", resource.GetNamespace(), "name", resource.GetName(), + ) + // if a resource is in deletion state, no further action needed if resource.GetDeletionTimestamp() != nil { + logger.Debugw("resource is in deletion state, no action needed", + "resource", th.resourceFn.Type(), "namespace", resource.GetNamespace(), "name", resource.GetName()) return nil } // if a resource is not completed state, no further action needed if !th.resourceFn.IsCompleted(resource) && th.resourceFn.Ignore(resource) { + logger.Debugw("resource is ignored", + "resource", th.resourceFn.Type(), "namespace", resource.GetNamespace(), "name", resource.GetName()) return nil } @@ -107,163 +109,189 @@ func (th *TTLHandler) ProcessEvent(ctx context.Context, resource metav1.Object) } return th.removeResource(ctx, resource) + } -// updateAnnotationTTLSeconds updates the TTL annotation of a resource if needed +// updates the TTL of a Resource func (th *TTLHandler) updateAnnotationTTLSeconds(ctx context.Context, resource metav1.Object) error { logger := logging.FromContext(ctx) + needsUpdate := false + // get the annotations + annotations := resource.GetAnnotations() + if annotations == nil { + needsUpdate = true + annotations = map[string]string{} + } + if annotations[AnnotationTTLSecondsAfterFinished] == "" { + needsUpdate = true + } - // get resource name and selectors first to avoid redundant work if no update needed + // get resource name, with user defined label key, if not available, go with default label key labelKey := getResourceNameLabelKey(resource, th.resourceFn.GetDefaultLabelKey()) resourceName := getResourceName(resource, labelKey) - resourceSelectors := th.getResourceSelectors(resource) - - // Check enforced config level early - enforcedLevel := th.resourceFn.GetEnforcedConfigLevel(resource.GetNamespace(), resourceName, resourceSelectors) - logger.Debugw("checking TTL configuration", - "resource", th.resourceFn.Type(), - "namespace", resource.GetNamespace(), - "name", resourceName, - "enforced_level", enforcedLevel) - - // Check if update is needed - if !th.needsTTLUpdate(resource, enforcedLevel) { - return nil - } - // Get TTL value - ttl, identifiedBy := th.resourceFn.GetTTLSecondsAfterFinished(resource.GetNamespace(), resourceName, resourceSelectors) - logger.Debugw("TTL configuration found", - "ttl", ttl, - "source", identifiedBy, - "resource", th.resourceFn.Type(), - "namespace", resource.GetNamespace(), - "name", resourceName) + // Construct the selectors with both matchLabels and matchAnnotations + resourceSelectors := SelectorSpec{} - // Get latest version of resource to avoid conflicts - resourceLatest, err := th.resourceFn.Get(ctx, resource.GetNamespace(), resource.GetName()) - if err != nil { - if errors.IsNotFound(err) { - return nil - } - return fmt.Errorf("failed to get resource: %w", err) + // Get Annotations and Labels + resourceAnnotations := resource.GetAnnotations() + if len(resourceAnnotations) > 0 { + resourceSelectors.MatchAnnotations = resourceAnnotations } - // Update annotations - annotations := resourceLatest.GetAnnotations() - if annotations == nil { - annotations = make(map[string]string) + resourceLabels := resource.GetLabels() + if len(resourceLabels) > 0 { + resourceSelectors.MatchLabels = resourceLabels + } + + th_enforcedlevel := th.resourceFn.GetEnforcedConfigLevel(resource.GetNamespace(), resourceName, resourceSelectors) + logger.Debugw("CHECKING-ENFORCED-CONFIG-TTL", "enforced_level", th_enforcedlevel, + "namespace", resource.GetNamespace(), "name", resourceName, "selectors", resourceSelectors) + + // if the "enforceConfigLevel" is not resource level, do not consider ttl from the resource annotation + // take it from namespace config or global config + if th.resourceFn.GetEnforcedConfigLevel(resource.GetNamespace(), resourceName, resourceSelectors) != EnforcedConfigLevelResource { + needsUpdate = true } - if ttl == nil { - // If TTL is nil, remove the annotation if it exists - if _, exists := annotations[AnnotationTTLSecondsAfterFinished]; exists { - delete(annotations, AnnotationTTLSecondsAfterFinished) - logger.Debugw("removing TTL annotation - no TTL configuration found", - "resource", th.resourceFn.Type(), - "namespace", resource.GetNamespace(), - "name", resource.GetName()) + if needsUpdate { + ttl, _ := th.resourceFn.GetTTLSecondsAfterFinished(resource.GetNamespace(), resourceName, resourceSelectors) + logger.Debugw("TTL processing", "ttl", ttl, "namespace", resource.GetNamespace(), "name", resourceName, "selectors", resourceSelectors) + if ttl == nil { + logger.Debugw("ttl is not defined for this resource, no further action needed", + "resource", th.resourceFn.Type(), "namespace", resource.GetNamespace(), "name", resource.GetName(), + "resourceLabelKey", labelKey, "resourceLabelValue", resourceName, + ) + return nil } - } else { - // Set new TTL annotation newTTL := strconv.Itoa(int(*ttl)) - currentTTL, hasCurrentTTL := annotations[AnnotationTTLSecondsAfterFinished] - if !hasCurrentTTL || currentTTL != newTTL { - annotations[AnnotationTTLSecondsAfterFinished] = newTTL - logger.Debugw("updating TTL annotation", - "resource", th.resourceFn.Type(), - "namespace", resource.GetNamespace(), - "name", resource.GetName(), - "oldTTL", currentTTL, - "newTTL", newTTL, - "hadPreviousTTL", hasCurrentTTL) - } else { + previousTTL := annotations[AnnotationTTLSecondsAfterFinished] + if newTTL == previousTTL { + // there is no change on the TTL, update action not needed return nil } - } - - patchData := map[string]interface{}{ - "metadata": map[string]interface{}{ - "annotations": annotations, - }, - } + /* + resource.SetAnnotations(annotations) + logger.Debugw("updating ttl of a resource", + "resource", th.resourceFn.Type(), "namespace", resource.GetNamespace(), "name", resource.GetName(), "ttl", ttl, + ) + return th.resourceFn.Update(ctx, resource) + */ + // Prepare the annotation update + resourceLatest, err := th.resourceFn.Get(ctx, resource.GetNamespace(), resource.GetName()) + if err != nil { + if errors.IsNotFound(err) { + return err + } + logger.Errorw("error getting resource", "resource", th.resourceFn.Type(), + "namespace", resource.GetNamespace(), "name", resource.GetName(), zap.Error(err)) + return err + } + annotations := resourceLatest.GetAnnotations() + if annotations == nil { + annotations = map[string]string{} + } + annotations[AnnotationTTLSecondsAfterFinished] = newTTL + // Create a patch with the new annotations + patchData := map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": annotations, + }, + } - patchBytes, err := json.Marshal(patchData) - if err != nil { - return fmt.Errorf("failed to marshal patch data: %w", err) - } + // Convert patchData to JSON + patchBytes, err := json.Marshal(patchData) + if err != nil { + logger.Errorw("error marshaling patch data", zap.Error(err)) + return err + } - if err := th.resourceFn.Patch(ctx, resourceLatest.GetNamespace(), resourceLatest.GetName(), patchBytes); err != nil { - return fmt.Errorf("failed to patch resource with TTL annotation: %w", err) + // Apply the patch + err = th.resourceFn.Patch(ctx, resourceLatest.GetNamespace(), resourceLatest.GetName(), patchBytes) + if err != nil { + logger.Errorw("error patching resource with 'mark as processed' annotation", + "resource", th.resourceFn.Type(), "namespace", resourceLatest.GetNamespace(), "name", resourceLatest.GetName(), zap.Error(err)) + } } - return nil } // needsCleanup checks whether a Resource has finished and has a TTL set. func (th *TTLHandler) needsCleanup(resource metav1.Object) bool { - // Check completion state first as it's likely to be the most expensive operation - if !th.resourceFn.IsCompleted(resource) { - return false - } - // get the annotations annotations := resource.GetAnnotations() + // if there is no annotations present, the resource is not available for cleanup if annotations == nil { return false } + // if there is no ttl present, the resource is not available for cleanup [or] + // if the ttl is "-1", no further action needed on this Resource + if annotations[AnnotationTTLSecondsAfterFinished] == "" || annotations[AnnotationTTLSecondsAfterFinished] == "-1" { + return false + } - ttlValue := annotations[AnnotationTTLSecondsAfterFinished] - return ttlValue != "" && ttlValue != NoTTL + // if the resource is not in completed state, cleanup not needed + if !th.resourceFn.IsCompleted(resource) { + return false + } + + return true } -// removeResource checks the TTL and deletes the Resource if it has expired +// checks the ttl and deletes the Resource, if the Resource reaches the expire time func (th *TTLHandler) removeResource(ctx context.Context, resource metav1.Object) error { logger := logging.FromContext(ctx) - logger.Debugw("checking resource cleanup eligibility", - "resourceType", th.resourceFn.Type(), - "namespace", resource.GetNamespace(), - "name", resource.GetName(), + logger.Debugw("checking if the resource is ready for cleanup", + "resource", th.resourceFn.Type(), "namespace", resource.GetNamespace(), "name", resource.GetName(), ) // check the resource ttl status expiredAt, err := th.processTTL(logger, resource) if err != nil { - return fmt.Errorf("failed to process TTL: %w", err) - } - if expiredAt == nil { + return err + } else if expiredAt == nil { return nil } - // Verify TTL hasn't been modified before deletion + // The Resource's TTL is assumed to have expired, but the Resource TTL might be stale. + // Before deleting the Resource, do a final sanity check. + // If TTL is modified before we do this check, we cannot be sure if the TTL truly expires. + // The latest Resource may have a different UID, but it's fine because the checks will be run again. freshResource, err := th.resourceFn.Get(ctx, resource.GetNamespace(), resource.GetName()) + if errors.IsNotFound(err) { + return nil + } if err != nil { - if errors.IsNotFound(err) { - return nil - } - return fmt.Errorf("failed to get fresh resource: %w", err) + return err } - + // use the latest Resource TTL to see if the TTL truly expires. expiredAt, err = th.processTTL(logger, freshResource) if err != nil { - return fmt.Errorf("failed to process TTL for fresh resource: %w", err) - } - if expiredAt == nil { + return err + } else if expiredAt == nil { return nil } - logger.Debugw("cleaning up expired resource", - "resourceType", th.resourceFn.Type(), - "namespace", resource.GetNamespace(), - "name", resource.GetName(), - "expiredAt", expiredAt, + // TODO: Cascade deletes the Resources if TTL truly expires. + // policy := metav1.DeletePropagationForeground + // options := &client.DeleteOptions{ + // PropagationPolicy: &policy, + // Preconditions: &metav1.Preconditions{UID: &fresh.UID}, + // } + logger.Debugw("cleaning up a resource", + "resource", th.resourceFn.Type(), "namespace", resource.GetNamespace(), "name", resource.GetName(), ) - - if err := th.resourceFn.Delete(ctx, resource.GetNamespace(), resource.GetName()); err != nil { + err = th.resourceFn.Delete(ctx, resource.GetNamespace(), resource.GetName()) + if err != nil { + // ignore the error, if the resource is not found if errors.IsNotFound(err) { return nil } - return fmt.Errorf("failed to delete resource: %w", err) + logger.Error("error on removing a resource", + "resource", th.resourceFn.Type(), "namespace", resource.GetNamespace(), "name", resource.GetName(), + zap.Error(err), + ) + return err } return nil } @@ -342,14 +370,8 @@ func (th *TTLHandler) getTTLSeconds(resource metav1.Object) (*time.Duration, err ttl, err := strconv.Atoi(ttlString) if err != nil { - return nil, fmt.Errorf("invalid TTL value %q: %w", ttlString, err) - } - - // Check for negative TTL values (except -1 which means no TTL) - if ttl < -1 { - return nil, fmt.Errorf("TTL value %d must be >= -1", ttl) + return nil, err } - ttlDuration := time.Duration(ttl) * time.Second return &ttlDuration, nil } @@ -362,44 +384,3 @@ func (th *TTLHandler) enqueueAfter(logger *zap.SugaredLogger, resource metav1.Ob ) return controller.NewRequeueAfter(after) } - -// getResourceSelectors constructs the selector spec for a resource -func (th *TTLHandler) getResourceSelectors(resource metav1.Object) SelectorSpec { - selectors := SelectorSpec{} - if annotations := resource.GetAnnotations(); len(annotations) > 0 { - selectors.MatchAnnotations = annotations - } - if labels := resource.GetLabels(); len(labels) > 0 { - selectors.MatchLabels = labels - } - return selectors -} - -// needsTTLUpdate determines if a resource needs its TTL annotation updated -func (th *TTLHandler) needsTTLUpdate(resource metav1.Object, enforcedLevel EnforcedConfigLevel) bool { - annotations := resource.GetAnnotations() - if annotations == nil { - return true - } - - currentTTL, exists := annotations[AnnotationTTLSecondsAfterFinished] - if !exists { - return true - } - - // Get the current TTL from config - labelKey := getResourceNameLabelKey(resource, th.resourceFn.GetDefaultLabelKey()) - resourceName := getResourceName(resource, labelKey) - resourceSelectors := th.getResourceSelectors(resource) - - configTTL, _ := th.resourceFn.GetTTLSecondsAfterFinished(resource.GetNamespace(), resourceName, resourceSelectors) - - // If there's no config TTL, we should remove the annotation - if configTTL == nil { - return true - } - - // Compare current TTL with config TTL - configTTLStr := strconv.Itoa(int(*configTTL)) - return currentTTL != configTTLStr -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 4e98f20815..9e96c59a85 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1073,8 +1073,8 @@ github.com/openshift-pipelines/pipelines-as-code/pkg/cli github.com/openshift-pipelines/pipelines-as-code/pkg/configutil github.com/openshift-pipelines/pipelines-as-code/pkg/hub/vars github.com/openshift-pipelines/pipelines-as-code/pkg/params/settings -# github.com/openshift-pipelines/tektoncd-pruner v0.0.0-20250711075231-9c8624123820 -## explicit; go 1.24.0 +# github.com/openshift-pipelines/tektoncd-pruner v0.0.0 +## explicit; go 1.23.0 github.com/openshift-pipelines/tektoncd-pruner/pkg/config # github.com/openshift/api v0.0.0-20240521185306-0314f31e7774 ## explicit; go 1.22.0