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  }