github.com/verrazzano/verrazzano-monitoring-operator@v0.0.30/test/integ/util/wait.go (about)

     1  // Copyright (C) 2020, 2021, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package util
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"strings"
    11  
    12  	apiv1 "k8s.io/api/core/v1"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/apimachinery/pkg/util/wait"
    15  	"k8s.io/client-go/kubernetes"
    16  
    17  	"crypto/tls"
    18  	"net/http"
    19  	"net/url"
    20  	"os"
    21  	"time"
    22  )
    23  
    24  // DefaultRetry is the default backoff for e2e tests.
    25  var DefaultRetry = wait.Backoff{
    26  	Steps:    150,
    27  	Duration: 4 * time.Second,
    28  	Factor:   1.0,
    29  	Jitter:   0.1,
    30  }
    31  
    32  // Retry executes the provided function repeatedly, retrying until the function
    33  // returns done = true, errors, or exceeds the given timeout.
    34  func Retry(backoff wait.Backoff, fn wait.ConditionFunc) error {
    35  	var lastErr error
    36  	err := wait.ExponentialBackoff(backoff, func() (bool, error) {
    37  		done, err := fn()
    38  		if err != nil {
    39  			lastErr = err
    40  		}
    41  		return done, err
    42  	})
    43  	if err == wait.ErrWaitTimeout {
    44  		if lastErr != nil {
    45  			err = lastErr
    46  		}
    47  	}
    48  	return err
    49  }
    50  
    51  // WaitForDeploymentAvailable waits for the given deployment to reach the given number of available replicas
    52  func WaitForDeploymentAvailable(namespace string, deploymentName string, availableReplicas int32, backoff wait.Backoff, kubeClient kubernetes.Interface) error {
    53  	var err error
    54  	fmt.Printf("Waiting for deployment '%s' to reach %d available and total replicas...\n", deploymentName, availableReplicas)
    55  	err = Retry(backoff, func() (bool, error) {
    56  		deployments, err := kubeClient.AppsV1().Deployments(namespace).List(context.Background(), metav1.ListOptions{})
    57  		if err != nil {
    58  			return false, err
    59  		}
    60  		for _, deployment := range deployments.Items {
    61  			if deployment.Name == deploymentName {
    62  				if deployment.Status.AvailableReplicas == availableReplicas && deployment.Status.Replicas == availableReplicas {
    63  					return true, nil
    64  				}
    65  			}
    66  		}
    67  		return false, nil
    68  	})
    69  	return err
    70  }
    71  
    72  // WaitForStatefulSetAvailable waits for the given statefulset to reach the given number of available replicas
    73  func WaitForStatefulSetAvailable(namespace string, statefulSetName string, availableReplicas int32, backoff wait.Backoff, kubeClient kubernetes.Interface) error {
    74  	var err error
    75  	fmt.Printf("Waiting for statefulset '%s' to reach %d available and total replicas...\n", statefulSetName, availableReplicas)
    76  	err = Retry(backoff, func() (bool, error) {
    77  		statefulSets, err := kubeClient.AppsV1().StatefulSets(namespace).List(context.Background(), metav1.ListOptions{})
    78  		if err != nil {
    79  			return false, err
    80  		}
    81  		for _, statefulSet := range statefulSets.Items {
    82  			if statefulSet.Name == statefulSetName {
    83  				if statefulSet.Status.CurrentReplicas == availableReplicas && statefulSet.Status.ReadyReplicas == availableReplicas {
    84  					return true, nil
    85  				}
    86  			}
    87  		}
    88  		return false, nil
    89  	})
    90  	return err
    91  }
    92  
    93  // WaitForService waits for the given service to become available
    94  func WaitForService(namespace string, serviceName string, backoff wait.Backoff,
    95  	kubeClient kubernetes.Interface) (*apiv1.Service, error) {
    96  	var latest *apiv1.Service
    97  	var err error
    98  	fmt.Printf("Waiting for service '%s'...\n", serviceName)
    99  	err = Retry(backoff, func() (bool, error) {
   100  		services, err := kubeClient.CoreV1().Services(namespace).List(context.Background(), metav1.ListOptions{})
   101  		if err != nil {
   102  			return false, err
   103  		}
   104  		for i, service := range services.Items {
   105  			if service.Name == serviceName {
   106  				latest = &services.Items[i]
   107  				return true, nil
   108  			}
   109  		}
   110  		return false, nil
   111  	})
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	return latest, nil
   116  
   117  }
   118  
   119  // WaitForElasticSearchIndexDocCount waits for doc count to show up in elasticsearch
   120  func WaitForElasticSearchIndexDocCount(ElasticSearchIndexDocCountURL string, count string, backoff wait.Backoff) (bool, error) {
   121  	var err error
   122  	fmt.Println("Getting index doc count from elasticsearch url: " + ElasticSearchIndexDocCountURL)
   123  	err = Retry(backoff, func() (bool, error) {
   124  		resp, err := http.Get(ElasticSearchIndexDocCountURL) //nolint:gosec //#gosec G107
   125  		if err != nil {
   126  			return false, err
   127  		}
   128  
   129  		defer resp.Body.Close()
   130  		body, err := ioutil.ReadAll(resp.Body)
   131  		fmt.Println("Elasticsearch Index Doc Count output: \n" + string(body))
   132  
   133  		docCount := string(body)[strings.LastIndex(string(body), " ")+1:]
   134  		docCount = strings.TrimSuffix(docCount, "\n")
   135  		if strings.Compare(docCount, count) == 0 {
   136  			fmt.Println("  ==> Index has " + count + " docs.")
   137  			return true, nil
   138  		}
   139  		fmt.Println("Index does not have " + count + " docs. It only has " + docCount + " docs. Waiting ...")
   140  		return false, err
   141  	})
   142  
   143  	if err != nil {
   144  		return false, err
   145  	}
   146  	return true, nil
   147  
   148  }
   149  
   150  // WaitForEndpointAvailableWithAuth waits for the given endpoint to become available
   151  // if useIp is set, use the given worker ip to verify the endpoints/urlPath
   152  func WaitForEndpointAvailableWithAuth(
   153  	component string,
   154  	domainName string,
   155  	useIP string,
   156  	port int32,
   157  	path string,
   158  	expectedStatusCode int,
   159  	backoff wait.Backoff,
   160  	username string,
   161  	password string) error {
   162  
   163  	var err error
   164  	startTime := time.Now()
   165  
   166  	tr := &http.Transport{
   167  		TLSClientConfig:       &tls.Config{InsecureSkipVerify: true}, //nolint:gosec //#gosec G402
   168  		TLSHandshakeTimeout:   10 * time.Second,
   169  		ResponseHeaderTimeout: 20 * time.Second,
   170  		ExpectContinueTimeout: 1 * time.Second,
   171  	}
   172  
   173  	client := &http.Client{Transport: tr, Timeout: 300 * time.Second}
   174  
   175  	tURL := url.URL{}
   176  
   177  	// Set proxy for http client
   178  	if useIP != "localhost" {
   179  		proxyURL := os.Getenv("http_proxy")
   180  		if proxyURL != "" {
   181  			fmt.Println("Setting proxy for http clients to :" + proxyURL)
   182  			tURLProxy, _ := tURL.Parse(proxyURL)
   183  			tr.Proxy = http.ProxyURL(tURLProxy)
   184  		}
   185  	}
   186  
   187  	var myURL string
   188  
   189  	// if requesting to use a specific worker IP to verify endpoint
   190  	if useIP != "" {
   191  		myURL = fmt.Sprintf("https://%s:%d%s", useIP, port, path)
   192  	} else { // otherwise, use the dns name(component.domainName)
   193  		myURL = fmt.Sprintf("https://%s:%d%s", component+"."+domainName, port, path)
   194  	}
   195  
   196  	req, _ := http.NewRequest("GET", myURL, nil)
   197  
   198  	//Only fix the HEADER in when requesting to use a specific worker IP to verify endpoint
   199  	if useIP != "" {
   200  		req.Host = component + "." + domainName
   201  	}
   202  
   203  	req.Header.Add("Accept", "*/*")
   204  	req.SetBasicAuth(username, password)
   205  
   206  	fmt.Printf("Waiting for %s (%s) to reach status code %d...\n", component, myURL, expectedStatusCode)
   207  
   208  	err = Retry(backoff, func() (bool, error) {
   209  		resp, err := client.Do(req)
   210  		if err != nil {
   211  			panic(err)
   212  		}
   213  		defer resp.Body.Close()
   214  
   215  		if resp.StatusCode == expectedStatusCode {
   216  			return true, nil
   217  		}
   218  		return false, nil
   219  	})
   220  	fmt.Printf("Wait time: %s \n", time.Since(startTime))
   221  	if err != nil {
   222  		return err
   223  	}
   224  	return nil
   225  }
   226  
   227  // WaitForEndpointAvailable waits for the given endpoint to become available
   228  func WaitForEndpointAvailable(name string, externalIP string, port int32, urlPath string, expectedStatusCode int, backoff wait.Backoff) error {
   229  	var err error
   230  	endpointURL := fmt.Sprintf("http://%s:%d%s", externalIP, port, urlPath)
   231  	fmt.Printf("Waiting for %s (%s) to reach status code %d...\n", name, endpointURL, expectedStatusCode)
   232  	restyClient := GetClient()
   233  	restyClient.SetTimeout(10 * time.Second)
   234  	startTime := time.Now()
   235  	err = Retry(backoff, func() (bool, error) {
   236  		resp, e := restyClient.R().Get(endpointURL)
   237  		if e != nil {
   238  			fmt.Printf("error requesting URL %s: %+v", endpointURL, e)
   239  			return false, nil
   240  		}
   241  		observedStatusCode := resp.StatusCode()
   242  		if observedStatusCode != expectedStatusCode {
   243  			fmt.Printf("URL %s: expected status code %d, observed %d", endpointURL, expectedStatusCode, observedStatusCode)
   244  			return false, nil
   245  		}
   246  		return true, nil
   247  	})
   248  	fmt.Printf("Wait time: %s \n", time.Since(startTime))
   249  	if err != nil {
   250  		return err
   251  	}
   252  	return nil
   253  }