github.com/verrazzano/verrazzano@v1.7.0/pkg/k8s/ready/statefulset_ready.go (about)

     1  // Copyright (c) 2021, 2023, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package ready
     5  
     6  import (
     7  	"context"
     8  
     9  	"github.com/verrazzano/verrazzano/pkg/log/vzlog"
    10  
    11  	appsv1 "k8s.io/api/apps/v1"
    12  	corev1 "k8s.io/api/core/v1"
    13  	"k8s.io/apimachinery/pkg/api/errors"
    14  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  	"k8s.io/apimachinery/pkg/types"
    16  	"sigs.k8s.io/controller-runtime/pkg/client"
    17  )
    18  
    19  // pod label used to identify the controllerRevision resource for daemonsets and statefulsets
    20  const controllerRevisionHashLabel = "controller-revision-hash"
    21  
    22  // StatefulSetsAreReady Check that the named statefulsets have the minimum number of specified replicas ready and available
    23  func StatefulSetsAreReady(log vzlog.VerrazzanoLogger, client client.Client, namespacedNames []types.NamespacedName, expectedReplicas int32, prefix string) bool {
    24  	for _, namespacedName := range namespacedNames {
    25  		statefulset := appsv1.StatefulSet{}
    26  		if err := client.Get(context.TODO(), namespacedName, &statefulset); err != nil {
    27  			if errors.IsNotFound(err) {
    28  				log.Progressf("%s is waiting for statefulset %v to exist", prefix, namespacedName)
    29  				// StatefulSet not found
    30  				return false
    31  			}
    32  			log.Errorf("Failed getting statefulset %v: %v", namespacedName, err)
    33  			return false
    34  		}
    35  		if statefulset.Status.UpdatedReplicas < expectedReplicas {
    36  			log.Progressf("%s is waiting for statefulset %s replicas to be %v. Current updated replicas is %v", prefix, namespacedName,
    37  				expectedReplicas, statefulset.Status.UpdatedReplicas)
    38  			return false
    39  		}
    40  		if statefulset.Status.ReadyReplicas < expectedReplicas {
    41  			log.Progressf("%s is waiting for statefulset %s replicas to be %v. Current ready replicas is %v", prefix, namespacedName,
    42  				expectedReplicas, statefulset.Status.ReadyReplicas)
    43  			return false
    44  		}
    45  		if !PodsReadyStatefulSet(log, client, namespacedName, statefulset.Spec.Selector, expectedReplicas, prefix) {
    46  			return false
    47  		}
    48  		log.Oncef("%s has enough replicas for statefulsets %v", prefix, namespacedName)
    49  	}
    50  	return true
    51  }
    52  
    53  // DoStatefulSetsExist checks if all the named statefulsets exist
    54  func DoStatefulSetsExist(log vzlog.VerrazzanoLogger, client client.Client, namespacedNames []types.NamespacedName, _ int32, prefix string) bool {
    55  	for _, namespacedName := range namespacedNames {
    56  		exists, err := DoesStatefulsetExist(client, namespacedName)
    57  		if err != nil {
    58  			logErrorf(log, "%s failed getting statefulset %v: %v", prefix, namespacedName, err)
    59  			return false
    60  		}
    61  		if !exists {
    62  			logProgressf(log, "%s is waiting for statefulset %v to exist", prefix, namespacedName)
    63  			return false
    64  		}
    65  	}
    66  	return true
    67  }
    68  
    69  // DoesStatefulsetExist checks if the named statefulset exists
    70  func DoesStatefulsetExist(client client.Client, namespacedName types.NamespacedName) (bool, error) {
    71  	sts := appsv1.StatefulSet{}
    72  	if err := client.Get(context.TODO(), namespacedName, &sts); err != nil {
    73  		if errors.IsNotFound(err) {
    74  			return false, nil
    75  		}
    76  		return false, err
    77  	}
    78  	return true, nil
    79  }
    80  
    81  // PodsReadyStatefulSet checks for an expected number of pods to be using the latest controllerRevision resource and are
    82  // running and ready
    83  func PodsReadyStatefulSet(log vzlog.VerrazzanoLogger, client client.Client, namespacedName types.NamespacedName, selector *metav1.LabelSelector, expectedReplicas int32, prefix string) bool {
    84  	// Get a list of pods for a given namespace and labels selector
    85  	pods := GetPodsList(log, client, namespacedName, selector)
    86  	if pods == nil {
    87  		return false
    88  	}
    89  
    90  	// If no pods found log a progress message and return
    91  	if len(pods.Items) == 0 {
    92  		log.Progressf("Found no pods with matching labels selector %v for namespace %s", selector, namespacedName.Namespace)
    93  		return false
    94  	}
    95  
    96  	// Loop through pods identifying pods that are using the latest controllerRevision resource
    97  	var savedPods []corev1.Pod
    98  	var savedRevision int64
    99  	var savedControllerRevisionHash string
   100  	for _, pod := range pods.Items {
   101  		// Log error and return if the controller-revision-hash label is not found.  This should never happen.
   102  		if _, ok := pod.Labels[controllerRevisionHashLabel]; !ok {
   103  			log.Errorf("Failed to find pod label [controller-revision-hash] for pod %s/%s", pod.Namespace, pod.Name)
   104  			return false
   105  		}
   106  
   107  		if pod.Labels[controllerRevisionHashLabel] == savedControllerRevisionHash {
   108  			savedPods = append(savedPods, pod)
   109  			continue
   110  		}
   111  
   112  		// Get the controllerRevision resource for the pod given the controller-revision-hash label
   113  		var cr appsv1.ControllerRevision
   114  		err := client.Get(context.TODO(), types.NamespacedName{Namespace: namespacedName.Namespace, Name: pod.Labels[controllerRevisionHashLabel]}, &cr)
   115  		if err != nil {
   116  			log.Errorf("Failed to get controllerRevision %s: %v", namespacedName, err)
   117  			return false
   118  		}
   119  
   120  		if cr.Revision > savedRevision {
   121  			savedRevision = cr.Revision
   122  			savedControllerRevisionHash = pod.Labels[controllerRevisionHashLabel]
   123  			savedPods = []corev1.Pod{}
   124  			savedPods = append(savedPods, pod)
   125  		}
   126  	}
   127  	// Make sure pods using the latest controllerRevision resource are ready.
   128  	podsReady, success := EnsurePodsAreReady(log, savedPods, expectedReplicas, prefix)
   129  	if !success {
   130  		return false
   131  	}
   132  
   133  	if podsReady < expectedReplicas {
   134  		log.Progressf("%s is waiting for statefulset %s pods to be %v. Current available pods are %v", prefix, namespacedName,
   135  			expectedReplicas, podsReady)
   136  		return false
   137  	}
   138  
   139  	return true
   140  }