github.com/caicloud/helm@v2.5.0+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  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	"k8s.io/apimachinery/pkg/fields"
    24  	"k8s.io/apimachinery/pkg/labels"
    25  	"k8s.io/apimachinery/pkg/runtime"
    26  	"k8s.io/apimachinery/pkg/util/wait"
    27  	"k8s.io/kubernetes/pkg/api/v1"
    28  	apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
    29  	extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
    30  	"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
    31  	core "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1"
    32  	extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1"
    33  	internalclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
    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  	cs, err := c.ClientSet()
    49  	if err != nil {
    50  		return err
    51  	}
    52  	client := versionedClientsetForDeployment(cs)
    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(client, 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 := client.Core().Pods(value.Namespace).Get(value.Name, metav1.GetOptions{})
    72  				if err != nil {
    73  					return false, err
    74  				}
    75  				pods = append(pods, *pod)
    76  			case (*extensions.Deployment):
    77  				currentDeployment, err := client.Extensions().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, client)
    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 (*extensions.DaemonSet):
    92  				list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
    93  				if err != nil {
    94  					return false, err
    95  				}
    96  				pods = append(pods, list...)
    97  			case (*apps.StatefulSet):
    98  				list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
    99  				if err != nil {
   100  					return false, err
   101  				}
   102  				pods = append(pods, list...)
   103  			case (*extensions.ReplicaSet):
   104  				list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
   105  				if err != nil {
   106  					return false, err
   107  				}
   108  				pods = append(pods, list...)
   109  			case (*v1.PersistentVolumeClaim):
   110  				claim, err := client.Core().PersistentVolumeClaims(value.Namespace).Get(value.Name, metav1.GetOptions{})
   111  				if err != nil {
   112  					return false, err
   113  				}
   114  				pvc = append(pvc, *claim)
   115  			case (*v1.Service):
   116  				svc, err := client.Core().Services(value.Namespace).Get(value.Name, metav1.GetOptions{})
   117  				if err != nil {
   118  					return false, err
   119  				}
   120  				services = append(services, *svc)
   121  			}
   122  		}
   123  		isReady := podsReady(pods) && servicesReady(services) && volumesReady(pvc) && deploymentsReady(deployments)
   124  		c.Log("resources ready: %v", isReady)
   125  		return isReady, nil
   126  	})
   127  }
   128  
   129  func podsReady(pods []v1.Pod) bool {
   130  	for _, pod := range pods {
   131  		if !v1.IsPodReady(&pod) {
   132  			return false
   133  		}
   134  	}
   135  	return true
   136  }
   137  
   138  func servicesReady(svc []v1.Service) bool {
   139  	for _, s := range svc {
   140  		// ExternalName Services are external to cluster so helm shouldn't be checking to see if they're 'ready' (i.e. have an IP Set)
   141  		if s.Spec.Type == v1.ServiceTypeExternalName {
   142  			continue
   143  		}
   144  
   145  		// Make sure the service is not explicitly set to "None" before checking the IP
   146  		if s.Spec.ClusterIP != v1.ClusterIPNone && !v1.IsServiceIPSet(&s) {
   147  			return false
   148  		}
   149  		// This checks if the service has a LoadBalancer and that balancer has an Ingress defined
   150  		if s.Spec.Type == v1.ServiceTypeLoadBalancer && s.Status.LoadBalancer.Ingress == nil {
   151  			return false
   152  		}
   153  	}
   154  	return true
   155  }
   156  
   157  func volumesReady(vols []v1.PersistentVolumeClaim) bool {
   158  	for _, v := range vols {
   159  		if v.Status.Phase != v1.ClaimBound {
   160  			return false
   161  		}
   162  	}
   163  	return true
   164  }
   165  
   166  func deploymentsReady(deployments []deployment) bool {
   167  	for _, v := range deployments {
   168  		if !(v.replicaSets.Status.ReadyReplicas >= *v.deployment.Spec.Replicas-deploymentutil.MaxUnavailable(*v.deployment)) {
   169  			return false
   170  		}
   171  	}
   172  	return true
   173  }
   174  
   175  func getPods(client clientset.Interface, namespace string, selector map[string]string) ([]v1.Pod, error) {
   176  	list, err := client.Core().Pods(namespace).List(metav1.ListOptions{
   177  		FieldSelector: fields.Everything().String(),
   178  		LabelSelector: labels.Set(selector).AsSelector().String(),
   179  	})
   180  	return list.Items, err
   181  }
   182  
   183  func versionedClientsetForDeployment(internalClient internalclientset.Interface) clientset.Interface {
   184  	if internalClient == nil {
   185  		return &clientset.Clientset{}
   186  	}
   187  	return &clientset.Clientset{
   188  		CoreV1Client:            core.New(internalClient.Core().RESTClient()),
   189  		ExtensionsV1beta1Client: extensionsclient.New(internalClient.Extensions().RESTClient()),
   190  	}
   191  }