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  }