...

Source file src/k8s.io/kubernetes/pkg/probe/http/http.go

Documentation: k8s.io/kubernetes/pkg/probe/http

     1  /*
     2  Copyright 2015 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 http
    18  
    19  import (
    20  	"crypto/tls"
    21  	"errors"
    22  	"fmt"
    23  	"net/http"
    24  	"time"
    25  
    26  	utilnet "k8s.io/apimachinery/pkg/util/net"
    27  	"k8s.io/kubernetes/pkg/probe"
    28  
    29  	"k8s.io/klog/v2"
    30  	utilio "k8s.io/utils/io"
    31  )
    32  
    33  const (
    34  	maxRespBodyLength = 10 * 1 << 10 // 10KB
    35  )
    36  
    37  // New creates Prober that will skip TLS verification while probing.
    38  // followNonLocalRedirects configures whether the prober should follow redirects to a different hostname.
    39  // If disabled, redirects to other hosts will trigger a warning result.
    40  func New(followNonLocalRedirects bool) Prober {
    41  	tlsConfig := &tls.Config{InsecureSkipVerify: true}
    42  	return NewWithTLSConfig(tlsConfig, followNonLocalRedirects)
    43  }
    44  
    45  // NewWithTLSConfig takes tls config as parameter.
    46  // followNonLocalRedirects configures whether the prober should follow redirects to a different hostname.
    47  // If disabled, redirects to other hosts will trigger a warning result.
    48  func NewWithTLSConfig(config *tls.Config, followNonLocalRedirects bool) Prober {
    49  	// We do not want the probe use node's local proxy set.
    50  	transport := utilnet.SetTransportDefaults(
    51  		&http.Transport{
    52  			TLSClientConfig:    config,
    53  			DisableKeepAlives:  true,
    54  			Proxy:              http.ProxyURL(nil),
    55  			DisableCompression: true, // removes Accept-Encoding header
    56  			// DialContext creates unencrypted TCP connections
    57  			// and is also used by the transport for HTTPS connection
    58  			DialContext: probe.ProbeDialer().DialContext,
    59  		})
    60  
    61  	return httpProber{transport, followNonLocalRedirects}
    62  }
    63  
    64  // Prober is an interface that defines the Probe function for doing HTTP readiness/liveness checks.
    65  type Prober interface {
    66  	Probe(req *http.Request, timeout time.Duration) (probe.Result, string, error)
    67  }
    68  
    69  type httpProber struct {
    70  	transport               *http.Transport
    71  	followNonLocalRedirects bool
    72  }
    73  
    74  // Probe returns a ProbeRunner capable of running an HTTP check.
    75  func (pr httpProber) Probe(req *http.Request, timeout time.Duration) (probe.Result, string, error) {
    76  	client := &http.Client{
    77  		Timeout:       timeout,
    78  		Transport:     pr.transport,
    79  		CheckRedirect: RedirectChecker(pr.followNonLocalRedirects),
    80  	}
    81  	return DoHTTPProbe(req, client)
    82  }
    83  
    84  // GetHTTPInterface is an interface for making HTTP requests, that returns a response and error.
    85  type GetHTTPInterface interface {
    86  	Do(req *http.Request) (*http.Response, error)
    87  }
    88  
    89  // DoHTTPProbe checks if a GET request to the url succeeds.
    90  // If the HTTP response code is successful (i.e. 400 > code >= 200), it returns Success.
    91  // If the HTTP response code is unsuccessful or HTTP communication fails, it returns Failure.
    92  // This is exported because some other packages may want to do direct HTTP probes.
    93  func DoHTTPProbe(req *http.Request, client GetHTTPInterface) (probe.Result, string, error) {
    94  	url := req.URL
    95  	headers := req.Header
    96  	res, err := client.Do(req)
    97  	if err != nil {
    98  		// Convert errors into failures to catch timeouts.
    99  		return probe.Failure, err.Error(), nil
   100  	}
   101  	defer res.Body.Close()
   102  	b, err := utilio.ReadAtMost(res.Body, maxRespBodyLength)
   103  	if err != nil {
   104  		if err == utilio.ErrLimitReached {
   105  			klog.V(4).Infof("Non fatal body truncation for %s, Response: %v", url.String(), *res)
   106  		} else {
   107  			return probe.Failure, "", err
   108  		}
   109  	}
   110  	body := string(b)
   111  	if res.StatusCode >= http.StatusOK && res.StatusCode < http.StatusBadRequest {
   112  		if res.StatusCode >= http.StatusMultipleChoices { // Redirect
   113  			klog.V(4).Infof("Probe terminated redirects for %s, Response: %v", url.String(), *res)
   114  			return probe.Warning, fmt.Sprintf("Probe terminated redirects, Response body: %v", body), nil
   115  		}
   116  		klog.V(4).Infof("Probe succeeded for %s, Response: %v", url.String(), *res)
   117  		return probe.Success, body, nil
   118  	}
   119  	klog.V(4).Infof("Probe failed for %s with request headers %v, response body: %v", url.String(), headers, body)
   120  	// Note: Until https://issue.k8s.io/99425 is addressed, this user-facing failure message must not contain the response body.
   121  	failureMsg := fmt.Sprintf("HTTP probe failed with statuscode: %d", res.StatusCode)
   122  	return probe.Failure, failureMsg, nil
   123  }
   124  
   125  // RedirectChecker returns a function that can be used to check HTTP redirects.
   126  func RedirectChecker(followNonLocalRedirects bool) func(*http.Request, []*http.Request) error {
   127  	if followNonLocalRedirects {
   128  		return nil // Use the default http client checker.
   129  	}
   130  
   131  	return func(req *http.Request, via []*http.Request) error {
   132  		if req.URL.Hostname() != via[0].URL.Hostname() {
   133  			return http.ErrUseLastResponse
   134  		}
   135  		// Default behavior: stop after 10 redirects.
   136  		if len(via) >= 10 {
   137  			return errors.New("stopped after 10 redirects")
   138  		}
   139  		return nil
   140  	}
   141  }
   142  

View as plain text