github.com/sdbaiguanghe/helm@v2.16.7+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  	v1 "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  		for _, v := range created {
    56  			switch value := asVersionedOrUnstructured(v).(type) {
    57  			case *v1.ReplicationController:
    58  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector)
    59  				if err != nil {
    60  					return false, err
    61  				}
    62  				pods = append(pods, list...)
    63  			case *v1.Pod:
    64  				pod, err := kcs.CoreV1().Pods(value.Namespace).Get(value.Name, metav1.GetOptions{})
    65  				if err != nil {
    66  					return false, err
    67  				}
    68  				pods = append(pods, *pod)
    69  			case *appsv1.Deployment:
    70  				currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{})
    71  				if err != nil {
    72  					return false, err
    73  				}
    74  				// If paused deployment will never be ready
    75  				if currentDeployment.Spec.Paused {
    76  					continue
    77  				}
    78  				// Find RS associated with deployment
    79  				newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1())
    80  				if err != nil || newReplicaSet == nil {
    81  					return false, err
    82  				}
    83  				newDeployment := deployment{
    84  					newReplicaSet,
    85  					currentDeployment,
    86  				}
    87  				deployments = append(deployments, newDeployment)
    88  			case *appsv1beta1.Deployment:
    89  				currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{})
    90  				if err != nil {
    91  					return false, err
    92  				}
    93  				// If paused deployment will never be ready
    94  				if currentDeployment.Spec.Paused {
    95  					continue
    96  				}
    97  				// Find RS associated with deployment
    98  				newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1())
    99  				if err != nil || newReplicaSet == nil {
   100  					return false, err
   101  				}
   102  				newDeployment := deployment{
   103  					newReplicaSet,
   104  					currentDeployment,
   105  				}
   106  				deployments = append(deployments, newDeployment)
   107  			case *appsv1beta2.Deployment:
   108  				currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{})
   109  				if err != nil {
   110  					return false, err
   111  				}
   112  				// If paused deployment will never be ready
   113  				if currentDeployment.Spec.Paused {
   114  					continue
   115  				}
   116  				// Find RS associated with deployment
   117  				newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1())
   118  				if err != nil || newReplicaSet == nil {
   119  					return false, err
   120  				}
   121  				newDeployment := deployment{
   122  					newReplicaSet,
   123  					currentDeployment,
   124  				}
   125  				deployments = append(deployments, newDeployment)
   126  			case *extensions.Deployment:
   127  				currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{})
   128  				if err != nil {
   129  					return false, err
   130  				}
   131  				// If paused deployment will never be ready
   132  				if currentDeployment.Spec.Paused {
   133  					continue
   134  				}
   135  				// Find RS associated with deployment
   136  				newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1())
   137  				if err != nil || newReplicaSet == nil {
   138  					return false, err
   139  				}
   140  				newDeployment := deployment{
   141  					newReplicaSet,
   142  					currentDeployment,
   143  				}
   144  				deployments = append(deployments, newDeployment)
   145  			case *extensions.DaemonSet:
   146  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
   147  				if err != nil {
   148  					return false, err
   149  				}
   150  				pods = append(pods, list...)
   151  			case *appsv1.DaemonSet:
   152  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
   153  				if err != nil {
   154  					return false, err
   155  				}
   156  				pods = append(pods, list...)
   157  			case *appsv1beta2.DaemonSet:
   158  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
   159  				if err != nil {
   160  					return false, err
   161  				}
   162  				pods = append(pods, list...)
   163  			case *appsv1.StatefulSet:
   164  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
   165  				if err != nil {
   166  					return false, err
   167  				}
   168  				pods = append(pods, list...)
   169  			case *appsv1beta1.StatefulSet:
   170  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
   171  				if err != nil {
   172  					return false, err
   173  				}
   174  				pods = append(pods, list...)
   175  			case *appsv1beta2.StatefulSet:
   176  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
   177  				if err != nil {
   178  					return false, err
   179  				}
   180  				pods = append(pods, list...)
   181  			case *extensions.ReplicaSet:
   182  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
   183  				if err != nil {
   184  					return false, err
   185  				}
   186  				pods = append(pods, list...)
   187  			case *appsv1beta2.ReplicaSet:
   188  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
   189  				if err != nil {
   190  					return false, err
   191  				}
   192  				pods = append(pods, list...)
   193  			case *appsv1.ReplicaSet:
   194  				list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
   195  				if err != nil {
   196  					return false, err
   197  				}
   198  				pods = append(pods, list...)
   199  			case *v1.PersistentVolumeClaim:
   200  				claim, err := kcs.CoreV1().PersistentVolumeClaims(value.Namespace).Get(value.Name, metav1.GetOptions{})
   201  				if err != nil {
   202  					return false, err
   203  				}
   204  				pvc = append(pvc, *claim)
   205  			case *v1.Service:
   206  				svc, err := kcs.CoreV1().Services(value.Namespace).Get(value.Name, metav1.GetOptions{})
   207  				if err != nil {
   208  					return false, err
   209  				}
   210  				services = append(services, *svc)
   211  			}
   212  		}
   213  		isReady := c.podsReady(pods) && c.servicesReady(services) && c.volumesReady(pvc) && c.deploymentsReady(deployments)
   214  		return isReady, nil
   215  	})
   216  }
   217  
   218  func (c *Client) podsReady(pods []v1.Pod) bool {
   219  	for _, pod := range pods {
   220  		if !isPodReady(&pod) {
   221  			c.Log("Pod is not ready: %s/%s", pod.GetNamespace(), pod.GetName())
   222  			return false
   223  		}
   224  	}
   225  	return true
   226  }
   227  
   228  func (c *Client) servicesReady(svc []v1.Service) bool {
   229  	for _, s := range svc {
   230  		// ExternalName Services are external to cluster so helm shouldn't be checking to see if they're 'ready' (i.e. have an IP Set)
   231  		if s.Spec.Type == v1.ServiceTypeExternalName {
   232  			continue
   233  		}
   234  
   235  		// Make sure the service is not explicitly set to "None" before checking the IP
   236  		if s.Spec.ClusterIP != v1.ClusterIPNone && s.Spec.ClusterIP == "" {
   237  			c.Log("Service is not ready: %s/%s", s.GetNamespace(), s.GetName())
   238  			return false
   239  		}
   240  		// This checks if the service has a LoadBalancer and that balancer has an Ingress defined
   241  		if s.Spec.Type == v1.ServiceTypeLoadBalancer && s.Status.LoadBalancer.Ingress == nil {
   242  			c.Log("Service is not ready: %s/%s", s.GetNamespace(), s.GetName())
   243  			return false
   244  		}
   245  	}
   246  	return true
   247  }
   248  
   249  func (c *Client) volumesReady(vols []v1.PersistentVolumeClaim) bool {
   250  	for _, v := range vols {
   251  		if v.Status.Phase != v1.ClaimBound {
   252  			c.Log("PersistentVolumeClaim is not ready: %s/%s", v.GetNamespace(), v.GetName())
   253  			return false
   254  		}
   255  	}
   256  	return true
   257  }
   258  
   259  func (c *Client) deploymentsReady(deployments []deployment) bool {
   260  	for _, v := range deployments {
   261  		if !(v.replicaSets.Status.ReadyReplicas >= *v.deployment.Spec.Replicas-deploymentutil.MaxUnavailable(*v.deployment)) {
   262  			c.Log("Deployment is not ready: %s/%s", v.deployment.GetNamespace(), v.deployment.GetName())
   263  			return false
   264  		}
   265  	}
   266  	return true
   267  }
   268  
   269  func getPods(client kubernetes.Interface, namespace string, selector map[string]string) ([]v1.Pod, error) {
   270  	list, err := client.CoreV1().Pods(namespace).List(metav1.ListOptions{
   271  		FieldSelector: fields.Everything().String(),
   272  		LabelSelector: labels.Set(selector).AsSelector().String(),
   273  	})
   274  	return list.Items, err
   275  }
   276  
   277  func isPodReady(pod *v1.Pod) bool {
   278  	if &pod.Status != nil && len(pod.Status.Conditions) > 0 {
   279  		for _, condition := range pod.Status.Conditions {
   280  			if condition.Type == v1.PodReady &&
   281  				condition.Status == v1.ConditionTrue {
   282  				return true
   283  			}
   284  		}
   285  	}
   286  	return false
   287  }