github.com/flant/helm@v2.8.1+incompatible/pkg/kube/wait.go (about)

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