123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- /*
- 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"
- 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) {
- 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 _, ok := headers["User-Agent"]; !ok {
- if headers == nil {
- headers = http.Header{}
- }
- // 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))
- }
- req.Header = headers
- if headers.Get("Host") != "" {
- 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, 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
- }
- }
|