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