mirror of https://github.com/fluxcd/flux2.git
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			150 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
			
		
		
	
	
			150 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
| /*
 | |
| Copyright 2023 The Flux 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 main
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 
 | |
| 	kstatus "github.com/fluxcd/cli-utils/pkg/kstatus/status"
 | |
| 	apimeta "k8s.io/apimachinery/pkg/api/meta"
 | |
| 	"k8s.io/apimachinery/pkg/types"
 | |
| 	"k8s.io/apimachinery/pkg/util/wait"
 | |
| 	"sigs.k8s.io/controller-runtime/pkg/client"
 | |
| 
 | |
| 	"github.com/fluxcd/pkg/apis/meta"
 | |
| 	"github.com/fluxcd/pkg/runtime/object"
 | |
| 	"github.com/fluxcd/pkg/runtime/patch"
 | |
| )
 | |
| 
 | |
| // objectStatusType is the type of object in terms of status when computing the
 | |
| // readiness of an object. Readiness check method depends on the type of object.
 | |
| // For a dynamic object, Ready status condition is considered only for the
 | |
| // latest generation of the object. For a static object that don't have any
 | |
| // condition, the object generation is not considered.
 | |
| type objectStatusType int
 | |
| 
 | |
| const (
 | |
| 	objectStatusDynamic objectStatusType = iota
 | |
| 	objectStatusStatic
 | |
| )
 | |
| 
 | |
| // isObjectReady determines if an object is ready using the kstatus.Compute()
 | |
| // result. statusType helps differenciate between static and dynamic objects to
 | |
| // accurately check the object's readiness. A dynamic object may have some extra
 | |
| // considerations depending on the object.
 | |
| func isObjectReady(obj client.Object, statusType objectStatusType) (bool, error) {
 | |
| 	observedGen, err := object.GetStatusObservedGeneration(obj)
 | |
| 	if err != nil && err != object.ErrObservedGenerationNotFound {
 | |
| 		return false, err
 | |
| 	}
 | |
| 
 | |
| 	if statusType == objectStatusDynamic {
 | |
| 		// Object not reconciled yet.
 | |
| 		if observedGen < 1 {
 | |
| 			return false, nil
 | |
| 		}
 | |
| 
 | |
| 		cobj, ok := obj.(meta.ObjectWithConditions)
 | |
| 		if !ok {
 | |
| 			return false, fmt.Errorf("unable to get conditions from object")
 | |
| 		}
 | |
| 
 | |
| 		if c := apimeta.FindStatusCondition(cobj.GetConditions(), meta.ReadyCondition); c != nil {
 | |
| 			// Ensure that the ready condition is for the latest generation of
 | |
| 			// the object.
 | |
| 			// NOTE: Some APIs like ImageUpdateAutomation and HelmRelease don't
 | |
| 			// support per condition observed generation yet. Per condition
 | |
| 			// observed generation for them are always zero.
 | |
| 			// There are two strategies used across different object kinds to
 | |
| 			// check the latest ready condition:
 | |
| 			//   - check that the ready condition's generation matches the
 | |
| 			//     object's generation.
 | |
| 			//   - check that the observed generation of the object in the
 | |
| 			//     status matches the object's generation.
 | |
| 			//
 | |
| 			// TODO: Once ImageUpdateAutomation and HelmRelease APIs have per
 | |
| 			// condition observed generation, remove the object's observed
 | |
| 			// generation and object's generation check (the second condition
 | |
| 			// below). Also, try replacing this readiness check function with
 | |
| 			// fluxcd/pkg/ssa's ResourceManager.Wait(), which uses kstatus
 | |
| 			// internally to check readiness of the objects.
 | |
| 			if c.ObservedGeneration != 0 && c.ObservedGeneration != obj.GetGeneration() {
 | |
| 				return false, nil
 | |
| 			}
 | |
| 			if c.ObservedGeneration == 0 && observedGen != obj.GetGeneration() {
 | |
| 				return false, nil
 | |
| 			}
 | |
| 		} else {
 | |
| 			return false, nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	u, err := patch.ToUnstructured(obj)
 | |
| 	if err != nil {
 | |
| 		return false, err
 | |
| 	}
 | |
| 	result, err := kstatus.Compute(u)
 | |
| 	if err != nil {
 | |
| 		return false, err
 | |
| 	}
 | |
| 	switch result.Status {
 | |
| 	case kstatus.CurrentStatus:
 | |
| 		return true, nil
 | |
| 	case kstatus.InProgressStatus:
 | |
| 		return false, nil
 | |
| 	default:
 | |
| 		return false, fmt.Errorf("%s", result.Message)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // isObjectReadyConditionFunc returns a wait.ConditionFunc to be used with
 | |
| // wait.Poll* while polling for an object with dynamic status to be ready.
 | |
| func isObjectReadyConditionFunc(kubeClient client.Client, namespaceName types.NamespacedName, obj client.Object) wait.ConditionWithContextFunc {
 | |
| 	return func(ctx context.Context) (bool, error) {
 | |
| 		err := kubeClient.Get(ctx, namespaceName, obj)
 | |
| 		if err != nil {
 | |
| 			return false, err
 | |
| 		}
 | |
| 
 | |
| 		return isObjectReady(obj, objectStatusDynamic)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // isStaticObjectReadyConditionFunc returns a wait.ConditionFunc to be used with
 | |
| // wait.Poll* while polling for an object with static or no status to be
 | |
| // ready.
 | |
| func isStaticObjectReadyConditionFunc(kubeClient client.Client, namespaceName types.NamespacedName, obj client.Object) wait.ConditionWithContextFunc {
 | |
| 	return func(ctx context.Context) (bool, error) {
 | |
| 		err := kubeClient.Get(ctx, namespaceName, obj)
 | |
| 		if err != nil {
 | |
| 			return false, err
 | |
| 		}
 | |
| 
 | |
| 		return isObjectReady(obj, objectStatusStatic)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // kstatusCompute returns the kstatus computed result of a given object.
 | |
| func kstatusCompute(obj client.Object) (result *kstatus.Result, err error) {
 | |
| 	u, err := patch.ToUnstructured(obj)
 | |
| 	if err != nil {
 | |
| 		return result, err
 | |
| 	}
 | |
| 	return kstatus.Compute(u)
 | |
| }
 |