github.com/sdbaiguanghe/helm@v2.16.7+incompatible/pkg/kube/wait.go (about) 1 /* 2 Copyright The Helm 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 http://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 kube // import "k8s.io/helm/pkg/kube" 18 19 import ( 20 "time" 21 22 appsv1 "k8s.io/api/apps/v1" 23 appsv1beta1 "k8s.io/api/apps/v1beta1" 24 appsv1beta2 "k8s.io/api/apps/v1beta2" 25 v1 "k8s.io/api/core/v1" 26 extensions "k8s.io/api/extensions/v1beta1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/fields" 29 "k8s.io/apimachinery/pkg/labels" 30 "k8s.io/apimachinery/pkg/util/wait" 31 "k8s.io/client-go/kubernetes" 32 deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" 33 ) 34 35 // deployment holds associated replicaSets for a deployment 36 type deployment struct { 37 replicaSets *appsv1.ReplicaSet 38 deployment *appsv1.Deployment 39 } 40 41 // waitForResources polls to get the current status of all pods, PVCs, and Services 42 // until all are ready or a timeout is reached 43 func (c *Client) waitForResources(timeout time.Duration, created Result) error { 44 c.Log("beginning wait for %d resources with timeout of %v", len(created), timeout) 45 46 kcs, err := c.KubernetesClientSet() 47 if err != nil { 48 return err 49 } 50 return wait.Poll(2*time.Second, timeout, func() (bool, error) { 51 pods := []v1.Pod{} 52 services := []v1.Service{} 53 pvc := []v1.PersistentVolumeClaim{} 54 deployments := []deployment{} 55 for _, v := range created { 56 switch value := asVersionedOrUnstructured(v).(type) { 57 case *v1.ReplicationController: 58 list, err := getPods(kcs, value.Namespace, value.Spec.Selector) 59 if err != nil { 60 return false, err 61 } 62 pods = append(pods, list...) 63 case *v1.Pod: 64 pod, err := kcs.CoreV1().Pods(value.Namespace).Get(value.Name, metav1.GetOptions{}) 65 if err != nil { 66 return false, err 67 } 68 pods = append(pods, *pod) 69 case *appsv1.Deployment: 70 currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) 71 if err != nil { 72 return false, err 73 } 74 // If paused deployment will never be ready 75 if currentDeployment.Spec.Paused { 76 continue 77 } 78 // Find RS associated with deployment 79 newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) 80 if err != nil || newReplicaSet == nil { 81 return false, err 82 } 83 newDeployment := deployment{ 84 newReplicaSet, 85 currentDeployment, 86 } 87 deployments = append(deployments, newDeployment) 88 case *appsv1beta1.Deployment: 89 currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) 90 if err != nil { 91 return false, err 92 } 93 // If paused deployment will never be ready 94 if currentDeployment.Spec.Paused { 95 continue 96 } 97 // Find RS associated with deployment 98 newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) 99 if err != nil || newReplicaSet == nil { 100 return false, err 101 } 102 newDeployment := deployment{ 103 newReplicaSet, 104 currentDeployment, 105 } 106 deployments = append(deployments, newDeployment) 107 case *appsv1beta2.Deployment: 108 currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) 109 if err != nil { 110 return false, err 111 } 112 // If paused deployment will never be ready 113 if currentDeployment.Spec.Paused { 114 continue 115 } 116 // Find RS associated with deployment 117 newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) 118 if err != nil || newReplicaSet == nil { 119 return false, err 120 } 121 newDeployment := deployment{ 122 newReplicaSet, 123 currentDeployment, 124 } 125 deployments = append(deployments, newDeployment) 126 case *extensions.Deployment: 127 currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) 128 if err != nil { 129 return false, err 130 } 131 // If paused deployment will never be ready 132 if currentDeployment.Spec.Paused { 133 continue 134 } 135 // Find RS associated with deployment 136 newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) 137 if err != nil || newReplicaSet == nil { 138 return false, err 139 } 140 newDeployment := deployment{ 141 newReplicaSet, 142 currentDeployment, 143 } 144 deployments = append(deployments, newDeployment) 145 case *extensions.DaemonSet: 146 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 147 if err != nil { 148 return false, err 149 } 150 pods = append(pods, list...) 151 case *appsv1.DaemonSet: 152 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 153 if err != nil { 154 return false, err 155 } 156 pods = append(pods, list...) 157 case *appsv1beta2.DaemonSet: 158 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 159 if err != nil { 160 return false, err 161 } 162 pods = append(pods, list...) 163 case *appsv1.StatefulSet: 164 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 165 if err != nil { 166 return false, err 167 } 168 pods = append(pods, list...) 169 case *appsv1beta1.StatefulSet: 170 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 171 if err != nil { 172 return false, err 173 } 174 pods = append(pods, list...) 175 case *appsv1beta2.StatefulSet: 176 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 177 if err != nil { 178 return false, err 179 } 180 pods = append(pods, list...) 181 case *extensions.ReplicaSet: 182 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 183 if err != nil { 184 return false, err 185 } 186 pods = append(pods, list...) 187 case *appsv1beta2.ReplicaSet: 188 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 189 if err != nil { 190 return false, err 191 } 192 pods = append(pods, list...) 193 case *appsv1.ReplicaSet: 194 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 195 if err != nil { 196 return false, err 197 } 198 pods = append(pods, list...) 199 case *v1.PersistentVolumeClaim: 200 claim, err := kcs.CoreV1().PersistentVolumeClaims(value.Namespace).Get(value.Name, metav1.GetOptions{}) 201 if err != nil { 202 return false, err 203 } 204 pvc = append(pvc, *claim) 205 case *v1.Service: 206 svc, err := kcs.CoreV1().Services(value.Namespace).Get(value.Name, metav1.GetOptions{}) 207 if err != nil { 208 return false, err 209 } 210 services = append(services, *svc) 211 } 212 } 213 isReady := c.podsReady(pods) && c.servicesReady(services) && c.volumesReady(pvc) && c.deploymentsReady(deployments) 214 return isReady, nil 215 }) 216 } 217 218 func (c *Client) podsReady(pods []v1.Pod) bool { 219 for _, pod := range pods { 220 if !isPodReady(&pod) { 221 c.Log("Pod is not ready: %s/%s", pod.GetNamespace(), pod.GetName()) 222 return false 223 } 224 } 225 return true 226 } 227 228 func (c *Client) servicesReady(svc []v1.Service) bool { 229 for _, s := range svc { 230 // ExternalName Services are external to cluster so helm shouldn't be checking to see if they're 'ready' (i.e. have an IP Set) 231 if s.Spec.Type == v1.ServiceTypeExternalName { 232 continue 233 } 234 235 // Make sure the service is not explicitly set to "None" before checking the IP 236 if s.Spec.ClusterIP != v1.ClusterIPNone && s.Spec.ClusterIP == "" { 237 c.Log("Service is not ready: %s/%s", s.GetNamespace(), s.GetName()) 238 return false 239 } 240 // This checks if the service has a LoadBalancer and that balancer has an Ingress defined 241 if s.Spec.Type == v1.ServiceTypeLoadBalancer && s.Status.LoadBalancer.Ingress == nil { 242 c.Log("Service is not ready: %s/%s", s.GetNamespace(), s.GetName()) 243 return false 244 } 245 } 246 return true 247 } 248 249 func (c *Client) volumesReady(vols []v1.PersistentVolumeClaim) bool { 250 for _, v := range vols { 251 if v.Status.Phase != v1.ClaimBound { 252 c.Log("PersistentVolumeClaim is not ready: %s/%s", v.GetNamespace(), v.GetName()) 253 return false 254 } 255 } 256 return true 257 } 258 259 func (c *Client) deploymentsReady(deployments []deployment) bool { 260 for _, v := range deployments { 261 if !(v.replicaSets.Status.ReadyReplicas >= *v.deployment.Spec.Replicas-deploymentutil.MaxUnavailable(*v.deployment)) { 262 c.Log("Deployment is not ready: %s/%s", v.deployment.GetNamespace(), v.deployment.GetName()) 263 return false 264 } 265 } 266 return true 267 } 268 269 func getPods(client kubernetes.Interface, namespace string, selector map[string]string) ([]v1.Pod, error) { 270 list, err := client.CoreV1().Pods(namespace).List(metav1.ListOptions{ 271 FieldSelector: fields.Everything().String(), 272 LabelSelector: labels.Set(selector).AsSelector().String(), 273 }) 274 return list.Items, err 275 } 276 277 func isPodReady(pod *v1.Pod) bool { 278 if &pod.Status != nil && len(pod.Status.Conditions) > 0 { 279 for _, condition := range pod.Status.Conditions { 280 if condition.Type == v1.PodReady && 281 condition.Status == v1.ConditionTrue { 282 return true 283 } 284 } 285 } 286 return false 287 }