k8s.io/kubernetes@v1.29.3/test/e2e_node/services/util.go (about) 1 /* 2 Copyright 2016 The Kubernetes 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 services 18 19 import ( 20 "crypto/tls" 21 "fmt" 22 "net/http" 23 "os" 24 "os/signal" 25 "syscall" 26 "time" 27 28 "k8s.io/klog/v2" 29 30 "k8s.io/kubernetes/test/e2e/framework" 31 ) 32 33 // terminationSignals are signals that cause the program to exit in the 34 // supported platforms (linux, darwin, windows). 35 var terminationSignals = []os.Signal{syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT} 36 37 // waitForTerminationSignal waits for termination signal. 38 func waitForTerminationSignal() { 39 sig := make(chan os.Signal, 1) 40 signal.Notify(sig, terminationSignals...) 41 <-sig 42 } 43 44 // readinessCheck checks whether services are ready via the supplied health 45 // check URLs. Once there is an error in errCh, the function will stop waiting 46 // and return the error. 47 func readinessCheck(name string, urls []string, errCh <-chan error) error { 48 klog.Infof("Running readiness check for service %q", name) 49 50 insecureTransport := http.DefaultTransport.(*http.Transport).Clone() 51 insecureTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} 52 insecureHTTPClient := &http.Client{ 53 Transport: insecureTransport, 54 } 55 56 endTime := time.Now().Add(*serverStartTimeout) 57 blockCh := make(chan error) 58 defer close(blockCh) 59 for endTime.After(time.Now()) { 60 select { 61 // We *always* want to run the health check if there is no error on the channel. 62 // With systemd, reads from errCh report nil because cmd.Run() waits 63 // on systemd-run, rather than the service process. systemd-run quickly 64 // exits with status 0, causing the channel to be closed with no error. In 65 // this case, you want to wait for the health check to complete, rather 66 // than returning from readinessCheck as soon as the channel is closed. 67 case err, ok := <-errCh: 68 if ok { // The channel is not closed, this is a real error 69 if err != nil { // If there is an error, return it 70 return err 71 } 72 // If not, keep checking readiness. 73 } else { // The channel is closed, this is only a zero value. 74 // Replace the errCh with blockCh to avoid busy loop, 75 // and keep checking readiness. 76 errCh = blockCh 77 } 78 case <-time.After(time.Second): 79 ready := true 80 for _, url := range urls { 81 if !healthCheck(insecureHTTPClient, url) { 82 ready = false 83 break 84 } 85 } 86 if ready { 87 return nil 88 } 89 } 90 } 91 return fmt.Errorf("e2e service %q readiness check timeout %v", name, *serverStartTimeout) 92 } 93 94 // Perform a health check. Anything other than a 200-response is treated as a failure. 95 // Only returns non-recoverable errors. 96 func healthCheck(client *http.Client, url string) bool { 97 req, err := http.NewRequest("HEAD", url, nil) 98 if err != nil { 99 return false 100 } 101 req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", framework.TestContext.BearerToken)) 102 resp, err := client.Do(req) 103 if err != nil { 104 klog.Warningf("Health check on %q failed, error=%v", url, err) 105 } else if resp.StatusCode != http.StatusOK { 106 klog.Warningf("Health check on %q failed, status=%d", url, resp.StatusCode) 107 } 108 return err == nil && resp.StatusCode == http.StatusOK 109 }