github.com/darkowlzz/helm@v2.5.1-0.20171213183701-6707fe0468d4+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  	appsv1beta1 "k8s.io/api/apps/v1beta1"
    23  	appsv1beta2 "k8s.io/api/apps/v1beta2"
    24  	"k8s.io/api/core/v1"
    25  	extensions "k8s.io/api/extensions/v1beta1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/fields"
    28  	"k8s.io/apimachinery/pkg/labels"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	"k8s.io/apimachinery/pkg/util/wait"
    31  	"k8s.io/client-go/kubernetes"
    32  	"k8s.io/kubernetes/pkg/api/v1/helper"
    33  	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
    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 *extensions.ReplicaSet
    40  	deployment  *extensions.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  			obj, err := c.AsVersionedObject(v.Object)
    59  			if err != nil && !runtime.IsNotRegisteredError(err) {
    60  				return false, err
    61  			}
    62  			switch value := obj.(type) {
    63  			case *v1.ReplicationController:
    64  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector)
    65  				if err != nil {
    66  					return false, err
    67  				}
    68  				pods = append(pods, list...)
    69  			case *v1.Pod:
    70  				pod, err := kcs.Core().Pods(value.Namespace).Get(value.Name, metav1.GetOptions{})
    71  				if err != nil {
    72  					return false, err
    73  				}
    74  				pods = append(pods, *pod)
    75  			case *extensions.Deployment:
    76  				currentDeployment, err := kcs.Extensions().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{})
    77  				if err != nil {
    78  					return false, err
    79  				}
    80  				// Find RS associated with deployment
    81  				newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.ExtensionsV1beta1())
    82  				if err != nil || newReplicaSet == nil {
    83  					return false, err
    84  				}
    85  				newDeployment := deployment{
    86  					newReplicaSet,
    87  					currentDeployment,
    88  				}
    89  				deployments = append(deployments, newDeployment)
    90  			case *extensions.DaemonSet:
    91  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
    92  				if err != nil {
    93  					return false, err
    94  				}
    95  				pods = append(pods, list...)
    96  			case *appsv1beta1.StatefulSet:
    97  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
    98  				if err != nil {
    99  					return false, err
   100  				}
   101  				pods = append(pods, list...)
   102  			case *appsv1beta2.StatefulSet:
   103  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
   104  				if err != nil {
   105  					return false, err
   106  				}
   107  				pods = append(pods, list...)
   108  			case *extensions.ReplicaSet:
   109  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
   110  				if err != nil {
   111  					return false, err
   112  				}
   113  				pods = append(pods, list...)
   114  			case *v1.PersistentVolumeClaim:
   115  				claim, err := kcs.Core().PersistentVolumeClaims(value.Namespace).Get(value.Name, metav1.GetOptions{})
   116  				if err != nil {
   117  					return false, err
   118  				}
   119  				pvc = append(pvc, *claim)
   120  			case *v1.Service:
   121  				svc, err := kcs.Core().Services(value.Namespace).Get(value.Name, metav1.GetOptions{})
   122  				if err != nil {
   123  					return false, err
   124  				}
   125  				services = append(services, *svc)
   126  			}
   127  		}
   128  		isReady := c.podsReady(pods) && c.servicesReady(services) && c.volumesReady(pvc) && c.deploymentsReady(deployments)
   129  		return isReady, nil
   130  	})
   131  }
   132  
   133  func (c *Client) podsReady(pods []v1.Pod) bool {
   134  	for _, pod := range pods {
   135  		if !podutil.IsPodReady(&pod) {
   136  			c.Log("Pod is not ready: %s/%s", pod.GetNamespace(), pod.GetName())
   137  			return false
   138  		}
   139  	}
   140  	return true
   141  }
   142  
   143  func (c *Client) servicesReady(svc []v1.Service) bool {
   144  	for _, s := range svc {
   145  		// ExternalName Services are external to cluster so helm shouldn't be checking to see if they're 'ready' (i.e. have an IP Set)
   146  		if s.Spec.Type == v1.ServiceTypeExternalName {
   147  			continue
   148  		}
   149  
   150  		// Make sure the service is not explicitly set to "None" before checking the IP
   151  		if s.Spec.ClusterIP != v1.ClusterIPNone && !helper.IsServiceIPSet(&s) {
   152  			c.Log("Service is not ready: %s/%s", s.GetNamespace(), s.GetName())
   153  			return false
   154  		}
   155  		// This checks if the service has a LoadBalancer and that balancer has an Ingress defined
   156  		if s.Spec.Type == v1.ServiceTypeLoadBalancer && s.Status.LoadBalancer.Ingress == nil {
   157  			c.Log("Service is not ready: %s/%s", s.GetNamespace(), s.GetName())
   158  			return false
   159  		}
   160  	}
   161  	return true
   162  }
   163  
   164  func (c *Client) volumesReady(vols []v1.PersistentVolumeClaim) bool {
   165  	for _, v := range vols {
   166  		if v.Status.Phase != v1.ClaimBound {
   167  			c.Log("PersistentVolumeClaim is not ready: %s/%s", v.GetNamespace(), v.GetName())
   168  			return false
   169  		}
   170  	}
   171  	return true
   172  }
   173  
   174  func (c *Client) deploymentsReady(deployments []deployment) bool {
   175  	for _, v := range deployments {
   176  		if !(v.replicaSets.Status.ReadyReplicas >= *v.deployment.Spec.Replicas-deploymentutil.MaxUnavailable(*v.deployment)) {
   177  			c.Log("Deployment is not ready: %s/%s", v.deployment.GetNamespace(), v.deployment.GetName())
   178  			return false
   179  		}
   180  	}
   181  	return true
   182  }
   183  
   184  func getPods(client kubernetes.Interface, namespace string, selector map[string]string) ([]v1.Pod, error) {
   185  	list, err := client.Core().Pods(namespace).List(metav1.ListOptions{
   186  		FieldSelector: fields.Everything().String(),
   187  		LabelSelector: labels.Set(selector).AsSelector().String(),
   188  	})
   189  	return list.Items, err
   190  }