github.com/latiif/helm@v2.15.0+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 "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 ingresses := []extensions.Ingress{} 56 for _, v := range created { 57 switch value := asVersionedOrUnstructured(v).(type) { 58 case *v1.ReplicationController: 59 list, err := getPods(kcs, value.Namespace, value.Spec.Selector) 60 if err != nil { 61 return false, err 62 } 63 pods = append(pods, list...) 64 case *v1.Pod: 65 pod, err := kcs.CoreV1().Pods(value.Namespace).Get(value.Name, metav1.GetOptions{}) 66 if err != nil { 67 return false, err 68 } 69 pods = append(pods, *pod) 70 case *appsv1.Deployment: 71 currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) 72 if err != nil { 73 return false, err 74 } 75 // If paused deployment will never be ready 76 if currentDeployment.Spec.Paused { 77 continue 78 } 79 // Find RS associated with deployment 80 newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) 81 if err != nil || newReplicaSet == nil { 82 return false, err 83 } 84 newDeployment := deployment{ 85 newReplicaSet, 86 currentDeployment, 87 } 88 deployments = append(deployments, newDeployment) 89 case *appsv1beta1.Deployment: 90 currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) 91 if err != nil { 92 return false, err 93 } 94 // If paused deployment will never be ready 95 if currentDeployment.Spec.Paused { 96 continue 97 } 98 // Find RS associated with deployment 99 newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) 100 if err != nil || newReplicaSet == nil { 101 return false, err 102 } 103 newDeployment := deployment{ 104 newReplicaSet, 105 currentDeployment, 106 } 107 deployments = append(deployments, newDeployment) 108 case *appsv1beta2.Deployment: 109 currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) 110 if err != nil { 111 return false, err 112 } 113 // If paused deployment will never be ready 114 if currentDeployment.Spec.Paused { 115 continue 116 } 117 // Find RS associated with deployment 118 newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) 119 if err != nil || newReplicaSet == nil { 120 return false, err 121 } 122 newDeployment := deployment{ 123 newReplicaSet, 124 currentDeployment, 125 } 126 deployments = append(deployments, newDeployment) 127 case *extensions.Deployment: 128 currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) 129 if err != nil { 130 return false, err 131 } 132 // If paused deployment will never be ready 133 if currentDeployment.Spec.Paused { 134 continue 135 } 136 // Find RS associated with deployment 137 newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) 138 if err != nil || newReplicaSet == nil { 139 return false, err 140 } 141 newDeployment := deployment{ 142 newReplicaSet, 143 currentDeployment, 144 } 145 deployments = append(deployments, newDeployment) 146 case *extensions.DaemonSet: 147 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 148 if err != nil { 149 return false, err 150 } 151 pods = append(pods, list...) 152 case *appsv1.DaemonSet: 153 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 154 if err != nil { 155 return false, err 156 } 157 pods = append(pods, list...) 158 case *appsv1beta2.DaemonSet: 159 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 160 if err != nil { 161 return false, err 162 } 163 pods = append(pods, list...) 164 case *appsv1.StatefulSet: 165 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 166 if err != nil { 167 return false, err 168 } 169 pods = append(pods, list...) 170 case *appsv1beta1.StatefulSet: 171 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 172 if err != nil { 173 return false, err 174 } 175 pods = append(pods, list...) 176 case *appsv1beta2.StatefulSet: 177 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 178 if err != nil { 179 return false, err 180 } 181 pods = append(pods, list...) 182 case *extensions.ReplicaSet: 183 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 184 if err != nil { 185 return false, err 186 } 187 pods = append(pods, list...) 188 case *appsv1beta2.ReplicaSet: 189 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 190 if err != nil { 191 return false, err 192 } 193 pods = append(pods, list...) 194 case *appsv1.ReplicaSet: 195 list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels) 196 if err != nil { 197 return false, err 198 } 199 pods = append(pods, list...) 200 case *v1.PersistentVolumeClaim: 201 claim, err := kcs.CoreV1().PersistentVolumeClaims(value.Namespace).Get(value.Name, metav1.GetOptions{}) 202 if err != nil { 203 return false, err 204 } 205 pvc = append(pvc, *claim) 206 case *v1.Service: 207 svc, err := kcs.CoreV1().Services(value.Namespace).Get(value.Name, metav1.GetOptions{}) 208 if err != nil { 209 return false, err 210 } 211 services = append(services, *svc) 212 case *extensions.Ingress: 213 ingress, err := kcs.ExtensionsV1beta1().Ingresses(value.Namespace).Get(value.Name, metav1.GetOptions{}) 214 if err != nil { 215 return false, err 216 } 217 ingresses = append(ingresses, *ingress) 218 } 219 } 220 isReady := c.podsReady(pods) && c.servicesReady(services) && c.volumesReady(pvc) && c.deploymentsReady(deployments) && c.ingressesReady(ingresses) 221 return isReady, nil 222 }) 223 } 224 225 func (c *Client) podsReady(pods []v1.Pod) bool { 226 for _, pod := range pods { 227 if !isPodReady(&pod) { 228 c.Log("Pod is not ready: %s/%s", pod.GetNamespace(), pod.GetName()) 229 return false 230 } 231 } 232 return true 233 } 234 235 func (c *Client) servicesReady(svc []v1.Service) bool { 236 for _, s := range svc { 237 // ExternalName Services are external to cluster so helm shouldn't be checking to see if they're 'ready' (i.e. have an IP Set) 238 if s.Spec.Type == v1.ServiceTypeExternalName { 239 continue 240 } 241 242 // Make sure the service is not explicitly set to "None" before checking the IP 243 if s.Spec.ClusterIP != v1.ClusterIPNone && s.Spec.ClusterIP == "" { 244 c.Log("Service is not ready: %s/%s", s.GetNamespace(), s.GetName()) 245 return false 246 } 247 // This checks if the service has a LoadBalancer and that balancer has an Ingress defined 248 if s.Spec.Type == v1.ServiceTypeLoadBalancer && s.Status.LoadBalancer.Ingress == nil { 249 c.Log("Service is not ready: %s/%s", s.GetNamespace(), s.GetName()) 250 return false 251 } 252 } 253 return true 254 } 255 256 func (c *Client) volumesReady(vols []v1.PersistentVolumeClaim) bool { 257 for _, v := range vols { 258 if v.Status.Phase != v1.ClaimBound { 259 c.Log("PersistentVolumeClaim is not ready: %s/%s", v.GetNamespace(), v.GetName()) 260 return false 261 } 262 } 263 return true 264 } 265 266 func (c *Client) deploymentsReady(deployments []deployment) bool { 267 for _, v := range deployments { 268 if !(v.replicaSets.Status.ReadyReplicas >= *v.deployment.Spec.Replicas-deploymentutil.MaxUnavailable(*v.deployment)) { 269 c.Log("Deployment is not ready: %s/%s", v.deployment.GetNamespace(), v.deployment.GetName()) 270 return false 271 } 272 } 273 return true 274 } 275 276 func getPods(client kubernetes.Interface, namespace string, selector map[string]string) ([]v1.Pod, error) { 277 list, err := client.CoreV1().Pods(namespace).List(metav1.ListOptions{ 278 FieldSelector: fields.Everything().String(), 279 LabelSelector: labels.Set(selector).AsSelector().String(), 280 }) 281 return list.Items, err 282 } 283 284 func isPodReady(pod *v1.Pod) bool { 285 if &pod.Status != nil && len(pod.Status.Conditions) > 0 { 286 for _, condition := range pod.Status.Conditions { 287 if condition.Type == v1.PodReady && 288 condition.Status == v1.ConditionTrue { 289 return true 290 } 291 } 292 } 293 return false 294 } 295 296 func (c *Client) ingressesReady(ingresses []extensions.Ingress) bool { 297 for _, ingress := range ingresses { 298 if &ingress.Status == nil || len(ingress.Status.LoadBalancer.Ingress) == 0 { 299 c.Log("Ingress is not ready: %s/%s", ingress.GetNamespace(), ingress.GetName()) 300 return false 301 } 302 } 303 return true 304 }