...

Source file src/helm.sh/helm/v3/pkg/kube/wait.go

Documentation: helm.sh/helm/v3/pkg/kube

     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 "helm.sh/helm/v3/pkg/kube"
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net/http"
    23  	"time"
    24  
    25  	"github.com/pkg/errors"
    26  	appsv1 "k8s.io/api/apps/v1"
    27  	appsv1beta1 "k8s.io/api/apps/v1beta1"
    28  	appsv1beta2 "k8s.io/api/apps/v1beta2"
    29  	batchv1 "k8s.io/api/batch/v1"
    30  	corev1 "k8s.io/api/core/v1"
    31  	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
    32  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    33  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    34  	"k8s.io/apimachinery/pkg/labels"
    35  	"k8s.io/apimachinery/pkg/runtime"
    36  	"k8s.io/cli-runtime/pkg/resource"
    37  
    38  	"k8s.io/apimachinery/pkg/util/wait"
    39  )
    40  
    41  type waiter struct {
    42  	c       ReadyChecker
    43  	timeout time.Duration
    44  	log     func(string, ...interface{})
    45  }
    46  
    47  // waitForResources polls to get the current status of all pods, PVCs, Services and
    48  // Jobs(optional) until all are ready or a timeout is reached
    49  func (w *waiter) waitForResources(created ResourceList) error {
    50  	w.log("beginning wait for %d resources with timeout of %v", len(created), w.timeout)
    51  
    52  	ctx, cancel := context.WithTimeout(context.Background(), w.timeout)
    53  	defer cancel()
    54  
    55  	numberOfErrors := make([]int, len(created))
    56  	for i := range numberOfErrors {
    57  		numberOfErrors[i] = 0
    58  	}
    59  
    60  	return wait.PollUntilContextCancel(ctx, 2*time.Second, true, func(ctx context.Context) (bool, error) {
    61  		waitRetries := 30
    62  		for i, v := range created {
    63  			ready, err := w.c.IsReady(ctx, v)
    64  
    65  			if waitRetries > 0 && w.isRetryableError(err, v) {
    66  				numberOfErrors[i]++
    67  				if numberOfErrors[i] > waitRetries {
    68  					w.log("Max number of retries reached")
    69  					return false, err
    70  				}
    71  				w.log("Retrying as current number of retries %d less than max number of retries %d", numberOfErrors[i]-1, waitRetries)
    72  				return false, nil
    73  			}
    74  			numberOfErrors[i] = 0
    75  			if !ready {
    76  				return false, err
    77  			}
    78  		}
    79  		return true, nil
    80  	})
    81  }
    82  
    83  func (w *waiter) isRetryableError(err error, resource *resource.Info) bool {
    84  	if err == nil {
    85  		return false
    86  	}
    87  	w.log("Error received when checking status of resource %s. Error: '%s', Resource details: '%s'", resource.Name, err, resource)
    88  	if ev, ok := err.(*apierrors.StatusError); ok {
    89  		statusCode := ev.Status().Code
    90  		retryable := w.isRetryableHTTPStatusCode(statusCode)
    91  		w.log("Status code received: %d. Retryable error? %t", statusCode, retryable)
    92  		return retryable
    93  	}
    94  	w.log("Retryable error? %t", true)
    95  	return true
    96  }
    97  
    98  func (w *waiter) isRetryableHTTPStatusCode(httpStatusCode int32) bool {
    99  	return httpStatusCode == 0 || httpStatusCode == http.StatusTooManyRequests || (httpStatusCode >= 500 && httpStatusCode != http.StatusNotImplemented)
   100  }
   101  
   102  // waitForDeletedResources polls to check if all the resources are deleted or a timeout is reached
   103  func (w *waiter) waitForDeletedResources(deleted ResourceList) error {
   104  	w.log("beginning wait for %d resources to be deleted with timeout of %v", len(deleted), w.timeout)
   105  
   106  	ctx, cancel := context.WithTimeout(context.Background(), w.timeout)
   107  	defer cancel()
   108  
   109  	return wait.PollUntilContextCancel(ctx, 2*time.Second, true, func(_ context.Context) (bool, error) {
   110  		for _, v := range deleted {
   111  			err := v.Get()
   112  			if err == nil || !apierrors.IsNotFound(err) {
   113  				return false, err
   114  			}
   115  		}
   116  		return true, nil
   117  	})
   118  }
   119  
   120  // SelectorsForObject returns the pod label selector for a given object
   121  //
   122  // Modified version of https://github.com/kubernetes/kubernetes/blob/v1.14.1/pkg/kubectl/polymorphichelpers/helpers.go#L84
   123  func SelectorsForObject(object runtime.Object) (selector labels.Selector, err error) {
   124  	switch t := object.(type) {
   125  	case *extensionsv1beta1.ReplicaSet:
   126  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   127  	case *appsv1.ReplicaSet:
   128  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   129  	case *appsv1beta2.ReplicaSet:
   130  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   131  	case *corev1.ReplicationController:
   132  		selector = labels.SelectorFromSet(t.Spec.Selector)
   133  	case *appsv1.StatefulSet:
   134  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   135  	case *appsv1beta1.StatefulSet:
   136  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   137  	case *appsv1beta2.StatefulSet:
   138  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   139  	case *extensionsv1beta1.DaemonSet:
   140  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   141  	case *appsv1.DaemonSet:
   142  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   143  	case *appsv1beta2.DaemonSet:
   144  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   145  	case *extensionsv1beta1.Deployment:
   146  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   147  	case *appsv1.Deployment:
   148  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   149  	case *appsv1beta1.Deployment:
   150  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   151  	case *appsv1beta2.Deployment:
   152  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   153  	case *batchv1.Job:
   154  		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
   155  	case *corev1.Service:
   156  		if t.Spec.Selector == nil || len(t.Spec.Selector) == 0 {
   157  			return nil, fmt.Errorf("invalid service '%s': Service is defined without a selector", t.Name)
   158  		}
   159  		selector = labels.SelectorFromSet(t.Spec.Selector)
   160  
   161  	default:
   162  		return nil, fmt.Errorf("selector for %T not implemented", object)
   163  	}
   164  
   165  	return selector, errors.Wrap(err, "invalid label selector")
   166  }
   167  

View as plain text