github.com/verrazzano/verrazzano@v1.7.0/pkg/k8s/ready/deployment_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 package ready 4 5 import ( 6 "context" 7 "fmt" 8 "strconv" 9 10 pkgConstants "github.com/verrazzano/verrazzano/pkg/constants" 11 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 12 "github.com/verrazzano/verrazzano/platform-operator/constants" 13 appsv1 "k8s.io/api/apps/v1" 14 corev1 "k8s.io/api/core/v1" 15 "k8s.io/apimachinery/pkg/api/errors" 16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 "k8s.io/apimachinery/pkg/types" 18 clipkg "sigs.k8s.io/controller-runtime/pkg/client" 19 ) 20 21 var veleroPodSelector = &metav1.LabelSelector{ 22 MatchLabels: map[string]string{ 23 "name": pkgConstants.Velero, 24 }, 25 } 26 27 func DeploymentsReadyBySelectors(log vzlog.VerrazzanoLogger, client clipkg.Client, expectedReplicas int32, prefix string, opts ...clipkg.ListOption) bool { 28 deploymentList := &appsv1.DeploymentList{} 29 if err := client.List(context.TODO(), deploymentList, opts...); err != nil { 30 logErrorf(log, "%s failed listing deployments for selectors %v: %v", prefix, opts, err) 31 return false 32 } 33 if deploymentList.Items == nil || len(deploymentList.Items) < 1 { 34 logProgressf(log, "%s is waiting for deployments matching selector %s to exist", prefix, opts) 35 return false 36 } 37 for idx := range deploymentList.Items { 38 deployment := &deploymentList.Items[idx] 39 namespacedName := types.NamespacedName{ 40 Namespace: deployment.Namespace, 41 Name: deployment.Name, 42 } 43 if !deploymentFullyReady(log, client, deployment, namespacedName, expectedReplicas, prefix) { 44 return false 45 } 46 } 47 return true 48 } 49 50 // DeploymentsAreReady check that the named deployments have the minimum number of specified replicas ready and available 51 func DeploymentsAreReady(log vzlog.VerrazzanoLogger, client clipkg.Client, namespacedNames []types.NamespacedName, expectedReplicas int32, prefix string) bool { 52 for _, namespacedName := range namespacedNames { 53 deployment := appsv1.Deployment{} 54 if err := client.Get(context.TODO(), namespacedName, &deployment); err != nil { 55 if errors.IsNotFound(err) { 56 logProgressf(log, "%s is waiting for deployment %v to exist", prefix, namespacedName) 57 return false 58 } 59 logErrorf(log, "%s failed getting deployment %v: %v", prefix, namespacedName, err) 60 return false 61 } 62 if !deploymentFullyReady(log, client, &deployment, namespacedName, expectedReplicas, prefix) { 63 return false 64 } 65 } 66 return true 67 } 68 69 // DoDeploymentsExist checks if all the named deployments exist 70 func DoDeploymentsExist(log vzlog.VerrazzanoLogger, client clipkg.Client, namespacedNames []types.NamespacedName, _ int32, prefix string) bool { 71 for _, namespacedName := range namespacedNames { 72 exists, err := DoesDeploymentExist(client, namespacedName) 73 if err != nil { 74 logErrorf(log, "%s failed getting deployment %v: %v", prefix, namespacedName, err) 75 return false 76 } 77 if !exists { 78 return false 79 } 80 } 81 return true 82 } 83 84 // DoesDeploymentsExist checks if the named deployment exists 85 func DoesDeploymentExist(client clipkg.Client, namespacedName types.NamespacedName) (bool, error) { 86 deployment := appsv1.Deployment{} 87 if err := client.Get(context.TODO(), namespacedName, &deployment); err != nil { 88 if errors.IsNotFound(err) { 89 return false, nil 90 } 91 return false, err 92 } 93 return true, nil 94 } 95 96 func deploymentFullyReady(log vzlog.VerrazzanoLogger, client clipkg.Client, deployment *appsv1.Deployment, namespacedName types.NamespacedName, expectedReplicas int32, prefix string) bool { 97 if deployment.Spec.Replicas != nil && *(deployment.Spec.Replicas) == 0 && expectedReplicas > 0 { 98 logProgressf(log, "%s will not reach ready state since the deployment %s has spec.replicas set to %v. Expecting %v replicas.", 99 prefix, namespacedName, *(deployment.Spec.Replicas), expectedReplicas) 100 return false 101 } 102 if deployment.Status.UpdatedReplicas < expectedReplicas { 103 logProgressf(log, "%s is waiting for deployment %s replicas to be %v. Current updated replicas is %v", prefix, namespacedName, 104 expectedReplicas, deployment.Status.UpdatedReplicas) 105 return false 106 } 107 if deployment.Status.AvailableReplicas < expectedReplicas { 108 logProgressf(log, "%s is waiting for deployment %s replicas to be %v. Current available replicas is %v", prefix, namespacedName, 109 expectedReplicas, deployment.Status.AvailableReplicas) 110 return false 111 } 112 // Velero install deploys a daemonset and deployment with common labels. The labels need to be adjusted so the pod fetch logic works 113 // as expected 114 podSelector := deployment.Spec.Selector 115 if namespacedName.Namespace == constants.VeleroNameSpace && namespacedName.Name == pkgConstants.Velero { 116 podSelector = veleroPodSelector 117 } 118 119 if !PodsReadyDeployment(log, client, namespacedName, podSelector, expectedReplicas, prefix) { 120 return false 121 } 122 logOncef(log, "%s has enough replicas for deployment %v", prefix, namespacedName) 123 return true 124 } 125 126 // PodsReadyDeployment checks for an expected number of pods to be using the latest replicaset revision and are 127 // running and ready 128 func PodsReadyDeployment(log vzlog.VerrazzanoLogger, client clipkg.Client, namespacedName types.NamespacedName, selector *metav1.LabelSelector, expectedReplicas int32, prefix string) bool { 129 // Get a list of pods for a given namespace and labels selector 130 pods := GetPodsList(log, client, namespacedName, selector) 131 if pods == nil { 132 return false 133 } 134 135 // If no pods found log a progress message and return 136 if len(pods.Items) == 0 { 137 logProgressf(log, "Found no pods with matching labels selector %v for namespace %s", selector, namespacedName.Namespace) 138 return false 139 } 140 141 // Loop through pods identifying pods that are using the latest replicaset revision 142 var savedPods []corev1.Pod 143 var savedPodTemplateHash string 144 var savedRevision int 145 for _, pod := range pods.Items { 146 // Log error and return if the pod-template-hash label is not found. This should never happen. 147 if _, ok := pod.Labels[podTemplateHashLabel]; !ok { 148 logErrorf(log, "Failed to find pod label [pod-template-hash] for pod %s/%s", pod.Namespace, pod.Name) 149 return false 150 } 151 152 if pod.Labels[podTemplateHashLabel] == savedPodTemplateHash { 153 savedPods = append(savedPods, pod) 154 continue 155 } 156 157 // Get the replica set for the pod given the pod-template-hash label 158 var rs appsv1.ReplicaSet 159 rsName := fmt.Sprintf("%s-%s", namespacedName.Name, pod.Labels[podTemplateHashLabel]) 160 err := client.Get(context.TODO(), types.NamespacedName{Namespace: namespacedName.Namespace, Name: rsName}, &rs) 161 if err != nil { 162 logErrorf(log, "Failed to get replicaset %s: %v", namespacedName, err) 163 return false 164 } 165 166 // Log error and return if the deployment.kubernetes.io/revision annotation is not found. This should never happen. 167 if _, ok := rs.Annotations[deploymentRevisionAnnotation]; !ok { 168 logErrorf(log, "Failed to find pod annotation [deployment.kubernetes.io/revision] for pod %s/%s", pod.Namespace, pod.Name) 169 return false 170 } 171 172 revision, _ := strconv.Atoi(rs.Annotations[deploymentRevisionAnnotation]) 173 if revision > savedRevision { 174 savedRevision = revision 175 savedPodTemplateHash = pod.Labels[podTemplateHashLabel] 176 savedPods = []corev1.Pod{} 177 savedPods = append(savedPods, pod) 178 } 179 } 180 181 // Make sure pods using the latest replicaset revision are ready. 182 podsReady, success := EnsurePodsAreReady(log, savedPods, expectedReplicas, prefix) 183 if !success { 184 return false 185 } 186 187 if podsReady < expectedReplicas { 188 logProgressf(log, "%s is waiting for deployment %s pods to be %v. Current available pods are %v", prefix, namespacedName, 189 expectedReplicas, podsReady) 190 return false 191 } 192 193 return true 194 }