knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/reconciler/reconcile_common.go (about)

     1  /*
     2  Copyright 2020 The Knative Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      https://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package reconciler
    18  
    19  import (
    20  	"context"
    21  	"reflect"
    22  	"time"
    23  
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  
    26  	"knative.dev/pkg/apis"
    27  	duckv1 "knative.dev/pkg/apis/duck/v1"
    28  	"knative.dev/pkg/logging"
    29  )
    30  
    31  const failedGenerationBump = "NewObservedGenFailure"
    32  
    33  // DefaultTimeout is used in some downstream reconcilers to put a context
    34  // deadline on reconciliation.  It is a variable so that it can be exposed by
    35  // entrypoints as a flag, e.g. via flag.DurationVar
    36  var DefaultTimeout = 30 * time.Second
    37  
    38  const (
    39  	// DoReconcileKind is the function name for reconciling the resource (as a leader).
    40  	DoReconcileKind = "ReconcileKind"
    41  	// DoFinalizeKind is the function name for finalizing the resource (as a leader).
    42  	DoFinalizeKind = "FinalizeKind"
    43  	// DoObserveKind is the function name for observing the resource (as a non leader).
    44  	DoObserveKind = "ObserveKind"
    45  	// DoObserveFinalizeKind is the function name for observing finalization of the resource (as a non leader).
    46  	//
    47  	// Deprecated: This will be deleted soon.
    48  	DoObserveFinalizeKind = "ObserveFinalizeKind"
    49  )
    50  
    51  // PreProcessReconcile contains logic to apply before reconciliation of a resource.
    52  func PreProcessReconcile(ctx context.Context, resource duckv1.KRShaped) {
    53  	newStatus := resource.GetStatus()
    54  
    55  	// We may be reading a version of the object that was stored at an older version
    56  	// and may not have had all of the assumed defaults specified.  This won't result
    57  	// in this getting written back to the API Server, but lets downstream logic make
    58  	// assumptions about defaulting.
    59  	if d, ok := resource.(apis.Defaultable); ok {
    60  		d.SetDefaults(ctx)
    61  	}
    62  
    63  	// Ensure conditions are initialized before we modify.
    64  	condSet := resource.GetConditionSet()
    65  	manager := condSet.Manage(newStatus)
    66  	manager.InitializeConditions()
    67  
    68  	if newStatus.ObservedGeneration != resource.GetGeneration() {
    69  		// Reset Ready/Successful to unknown. The reconciler is expected to overwrite this.
    70  		manager.MarkUnknown(condSet.GetTopLevelConditionType(), failedGenerationBump, "unsuccessfully observed a new generation")
    71  	}
    72  }
    73  
    74  // PostProcessReconcile contains logic to apply after reconciliation of a resource.
    75  func PostProcessReconcile(ctx context.Context, resource, oldResource duckv1.KRShaped) {
    76  	logger := logging.FromContext(ctx)
    77  	status := resource.GetStatus()
    78  	mgr := resource.GetConditionSet().Manage(status)
    79  
    80  	// Bump observed generation to denote that we have processed this
    81  	// generation regardless of success or failure.
    82  	status.ObservedGeneration = resource.GetGeneration()
    83  
    84  	if rc := mgr.GetTopLevelCondition(); rc == nil {
    85  		logger.Warn("A reconciliation included no top-level condition")
    86  	} else if rc.Reason == failedGenerationBump {
    87  		logger.Warn("A reconciler observed a new generation without updating the resource status")
    88  	}
    89  
    90  	groomConditionsTransitionTime(resource, oldResource)
    91  }
    92  
    93  // groomConditionsTransitionTime ensures that the LastTransitionTime only advances for resources
    94  // where the condition has changed during reconciliation. This also ensures that all advanced
    95  // conditions share the same timestamp.
    96  func groomConditionsTransitionTime(resource, oldResource duckv1.KRShaped) {
    97  	now := apis.VolatileTime{Inner: metav1.NewTime(time.Now())}
    98  	sts := resource.GetStatus()
    99  	for i := range sts.Conditions {
   100  		cond := &sts.Conditions[i]
   101  
   102  		if oldCond := oldResource.GetStatus().GetCondition(cond.Type); oldCond != nil {
   103  			cond.LastTransitionTime = oldCond.LastTransitionTime
   104  			if reflect.DeepEqual(cond, oldCond) {
   105  				continue
   106  			}
   107  		}
   108  
   109  		cond.LastTransitionTime = now
   110  	}
   111  }