mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 13:50:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			157 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			157 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2015 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package http
 | |
| 
 | |
| import (
 | |
| 	"crypto/tls"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"net/url"
 | |
| 	"time"
 | |
| 
 | |
| 	utilnet "k8s.io/apimachinery/pkg/util/net"
 | |
| 	"k8s.io/component-base/version"
 | |
| 	"k8s.io/kubernetes/pkg/probe"
 | |
| 
 | |
| 	"k8s.io/klog/v2"
 | |
| 	utilio "k8s.io/utils/io"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	maxRespBodyLength = 10 * 1 << 10 // 10KB
 | |
| )
 | |
| 
 | |
| // New creates Prober that will skip TLS verification while probing.
 | |
| // followNonLocalRedirects configures whether the prober should follow redirects to a different hostname.
 | |
| //   If disabled, redirects to other hosts will trigger a warning result.
 | |
| func New(followNonLocalRedirects bool) Prober {
 | |
| 	tlsConfig := &tls.Config{InsecureSkipVerify: true}
 | |
| 	return NewWithTLSConfig(tlsConfig, followNonLocalRedirects)
 | |
| }
 | |
| 
 | |
| // NewWithTLSConfig takes tls config as parameter.
 | |
| // followNonLocalRedirects configures whether the prober should follow redirects to a different hostname.
 | |
| //   If disabled, redirects to other hosts will trigger a warning result.
 | |
| func NewWithTLSConfig(config *tls.Config, followNonLocalRedirects bool) Prober {
 | |
| 	// We do not want the probe use node's local proxy set.
 | |
| 	transport := utilnet.SetTransportDefaults(
 | |
| 		&http.Transport{
 | |
| 			TLSClientConfig:   config,
 | |
| 			DisableKeepAlives: true,
 | |
| 			Proxy:             http.ProxyURL(nil),
 | |
| 		})
 | |
| 	return httpProber{transport, followNonLocalRedirects}
 | |
| }
 | |
| 
 | |
| // Prober is an interface that defines the Probe function for doing HTTP readiness/liveness checks.
 | |
| type Prober interface {
 | |
| 	Probe(url *url.URL, headers http.Header, timeout time.Duration) (probe.Result, string, error)
 | |
| }
 | |
| 
 | |
| type httpProber struct {
 | |
| 	transport               *http.Transport
 | |
| 	followNonLocalRedirects bool
 | |
| }
 | |
| 
 | |
| // Probe returns a ProbeRunner capable of running an HTTP check.
 | |
| func (pr httpProber) Probe(url *url.URL, headers http.Header, timeout time.Duration) (probe.Result, string, error) {
 | |
| 	pr.transport.DisableCompression = true // removes Accept-Encoding header
 | |
| 	client := &http.Client{
 | |
| 		Timeout:       timeout,
 | |
| 		Transport:     pr.transport,
 | |
| 		CheckRedirect: redirectChecker(pr.followNonLocalRedirects),
 | |
| 	}
 | |
| 	return DoHTTPProbe(url, headers, client)
 | |
| }
 | |
| 
 | |
| // GetHTTPInterface is an interface for making HTTP requests, that returns a response and error.
 | |
| type GetHTTPInterface interface {
 | |
| 	Do(req *http.Request) (*http.Response, error)
 | |
| }
 | |
| 
 | |
| // DoHTTPProbe checks if a GET request to the url succeeds.
 | |
| // If the HTTP response code is successful (i.e. 400 > code >= 200), it returns Success.
 | |
| // If the HTTP response code is unsuccessful or HTTP communication fails, it returns Failure.
 | |
| // This is exported because some other packages may want to do direct HTTP probes.
 | |
| func DoHTTPProbe(url *url.URL, headers http.Header, client GetHTTPInterface) (probe.Result, string, error) {
 | |
| 	req, err := http.NewRequest("GET", url.String(), nil)
 | |
| 	if err != nil {
 | |
| 		// Convert errors into failures to catch timeouts.
 | |
| 		return probe.Failure, err.Error(), nil
 | |
| 	}
 | |
| 	if headers == nil {
 | |
| 		headers = http.Header{}
 | |
| 	}
 | |
| 	if _, ok := headers["User-Agent"]; !ok {
 | |
| 		// explicitly set User-Agent so it's not set to default Go value
 | |
| 		v := version.Get()
 | |
| 		headers.Set("User-Agent", fmt.Sprintf("kube-probe/%s.%s", v.Major, v.Minor))
 | |
| 	}
 | |
| 	if _, ok := headers["Accept"]; !ok {
 | |
| 		// Accept header was not defined. accept all
 | |
| 		headers.Set("Accept", "*/*")
 | |
| 	} else if headers.Get("Accept") == "" {
 | |
| 		// Accept header was overridden but is empty. removing
 | |
| 		headers.Del("Accept")
 | |
| 	}
 | |
| 	req.Header = headers
 | |
| 	req.Host = headers.Get("Host")
 | |
| 	res, err := client.Do(req)
 | |
| 	if err != nil {
 | |
| 		// Convert errors into failures to catch timeouts.
 | |
| 		return probe.Failure, err.Error(), nil
 | |
| 	}
 | |
| 	defer res.Body.Close()
 | |
| 	b, err := utilio.ReadAtMost(res.Body, maxRespBodyLength)
 | |
| 	if err != nil {
 | |
| 		if err == utilio.ErrLimitReached {
 | |
| 			klog.V(4).Infof("Non fatal body truncation for %s, Response: %v", url.String(), *res)
 | |
| 		} else {
 | |
| 			return probe.Failure, "", err
 | |
| 		}
 | |
| 	}
 | |
| 	body := string(b)
 | |
| 	if res.StatusCode >= http.StatusOK && res.StatusCode < http.StatusBadRequest {
 | |
| 		if res.StatusCode >= http.StatusMultipleChoices { // Redirect
 | |
| 			klog.V(4).Infof("Probe terminated redirects for %s, Response: %v", url.String(), *res)
 | |
| 			return probe.Warning, fmt.Sprintf("Probe terminated redirects, Response body: %v", body), nil
 | |
| 		}
 | |
| 		klog.V(4).Infof("Probe succeeded for %s, Response: %v", url.String(), *res)
 | |
| 		return probe.Success, body, nil
 | |
| 	}
 | |
| 	klog.V(4).Infof("Probe failed for %s with request headers %v, response body: %v", url.String(), headers, body)
 | |
| 	return probe.Failure, fmt.Sprintf("HTTP probe failed with statuscode: %d", res.StatusCode), nil
 | |
| }
 | |
| 
 | |
| func redirectChecker(followNonLocalRedirects bool) func(*http.Request, []*http.Request) error {
 | |
| 	if followNonLocalRedirects {
 | |
| 		return nil // Use the default http client checker.
 | |
| 	}
 | |
| 
 | |
| 	return func(req *http.Request, via []*http.Request) error {
 | |
| 		if req.URL.Hostname() != via[0].URL.Hostname() {
 | |
| 			return http.ErrUseLastResponse
 | |
| 		}
 | |
| 		// Default behavior: stop after 10 redirects.
 | |
| 		if len(via) >= 10 {
 | |
| 			return errors.New("stopped after 10 redirects")
 | |
| 		}
 | |
| 		return nil
 | |
| 	}
 | |
| }
 |