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  }