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 }