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