mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 02:33:34 +00:00
rebase: update to latest github.com/openshift/api version
Also vendor all dependencies. Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
committed by
mergify[bot]
parent
ab87045afb
commit
ce603fb47e
699
api/vendor/k8s.io/apimachinery/pkg/util/net/http.go
generated
vendored
Normal file
699
api/vendor/k8s.io/apimachinery/pkg/util/net/http.go
generated
vendored
Normal file
@ -0,0 +1,699 @@
|
||||
/*
|
||||
Copyright 2016 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 net
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
"k8s.io/klog/v2"
|
||||
netutils "k8s.io/utils/net"
|
||||
)
|
||||
|
||||
// JoinPreservingTrailingSlash does a path.Join of the specified elements,
|
||||
// preserving any trailing slash on the last non-empty segment
|
||||
func JoinPreservingTrailingSlash(elem ...string) string {
|
||||
// do the basic path join
|
||||
result := path.Join(elem...)
|
||||
|
||||
// find the last non-empty segment
|
||||
for i := len(elem) - 1; i >= 0; i-- {
|
||||
if len(elem[i]) > 0 {
|
||||
// if the last segment ended in a slash, ensure our result does as well
|
||||
if strings.HasSuffix(elem[i], "/") && !strings.HasSuffix(result, "/") {
|
||||
result += "/"
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// IsTimeout returns true if the given error is a network timeout error
|
||||
func IsTimeout(err error) bool {
|
||||
var neterr net.Error
|
||||
if errors.As(err, &neterr) {
|
||||
return neterr != nil && neterr.Timeout()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsProbableEOF returns true if the given error resembles a connection termination
|
||||
// scenario that would justify assuming that the watch is empty.
|
||||
// These errors are what the Go http stack returns back to us which are general
|
||||
// connection closure errors (strongly correlated) and callers that need to
|
||||
// differentiate probable errors in connection behavior between normal "this is
|
||||
// disconnected" should use the method.
|
||||
func IsProbableEOF(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var uerr *url.Error
|
||||
if errors.As(err, &uerr) {
|
||||
err = uerr.Err
|
||||
}
|
||||
msg := err.Error()
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
return true
|
||||
case err == io.ErrUnexpectedEOF:
|
||||
return true
|
||||
case msg == "http: can't write HTTP request on broken connection":
|
||||
return true
|
||||
case strings.Contains(msg, "http2: server sent GOAWAY and closed the connection"):
|
||||
return true
|
||||
case strings.Contains(msg, "connection reset by peer"):
|
||||
return true
|
||||
case strings.Contains(strings.ToLower(msg), "use of closed network connection"):
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var defaultTransport = http.DefaultTransport.(*http.Transport)
|
||||
|
||||
// SetOldTransportDefaults applies the defaults from http.DefaultTransport
|
||||
// for the Proxy, Dial, and TLSHandshakeTimeout fields if unset
|
||||
func SetOldTransportDefaults(t *http.Transport) *http.Transport {
|
||||
if t.Proxy == nil || isDefault(t.Proxy) {
|
||||
// http.ProxyFromEnvironment doesn't respect CIDRs and that makes it impossible to exclude things like pod and service IPs from proxy settings
|
||||
// ProxierWithNoProxyCIDR allows CIDR rules in NO_PROXY
|
||||
t.Proxy = NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment)
|
||||
}
|
||||
// If no custom dialer is set, use the default context dialer
|
||||
//lint:file-ignore SA1019 Keep supporting deprecated Dial method of custom transports
|
||||
if t.DialContext == nil && t.Dial == nil {
|
||||
t.DialContext = defaultTransport.DialContext
|
||||
}
|
||||
if t.TLSHandshakeTimeout == 0 {
|
||||
t.TLSHandshakeTimeout = defaultTransport.TLSHandshakeTimeout
|
||||
}
|
||||
if t.IdleConnTimeout == 0 {
|
||||
t.IdleConnTimeout = defaultTransport.IdleConnTimeout
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// SetTransportDefaults applies the defaults from http.DefaultTransport
|
||||
// for the Proxy, Dial, and TLSHandshakeTimeout fields if unset
|
||||
func SetTransportDefaults(t *http.Transport) *http.Transport {
|
||||
t = SetOldTransportDefaults(t)
|
||||
// Allow clients to disable http2 if needed.
|
||||
if s := os.Getenv("DISABLE_HTTP2"); len(s) > 0 {
|
||||
klog.Info("HTTP2 has been explicitly disabled")
|
||||
} else if allowsHTTP2(t) {
|
||||
if err := configureHTTP2Transport(t); err != nil {
|
||||
klog.Warningf("Transport failed http2 configuration: %v", err)
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func readIdleTimeoutSeconds() int {
|
||||
ret := 30
|
||||
// User can set the readIdleTimeout to 0 to disable the HTTP/2
|
||||
// connection health check.
|
||||
if s := os.Getenv("HTTP2_READ_IDLE_TIMEOUT_SECONDS"); len(s) > 0 {
|
||||
i, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
klog.Warningf("Illegal HTTP2_READ_IDLE_TIMEOUT_SECONDS(%q): %v."+
|
||||
" Default value %d is used", s, err, ret)
|
||||
return ret
|
||||
}
|
||||
ret = i
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func pingTimeoutSeconds() int {
|
||||
ret := 15
|
||||
if s := os.Getenv("HTTP2_PING_TIMEOUT_SECONDS"); len(s) > 0 {
|
||||
i, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
klog.Warningf("Illegal HTTP2_PING_TIMEOUT_SECONDS(%q): %v."+
|
||||
" Default value %d is used", s, err, ret)
|
||||
return ret
|
||||
}
|
||||
ret = i
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func configureHTTP2Transport(t *http.Transport) error {
|
||||
t2, err := http2.ConfigureTransports(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// The following enables the HTTP/2 connection health check added in
|
||||
// https://github.com/golang/net/pull/55. The health check detects and
|
||||
// closes broken transport layer connections. Without the health check,
|
||||
// a broken connection can linger too long, e.g., a broken TCP
|
||||
// connection will be closed by the Linux kernel after 13 to 30 minutes
|
||||
// by default, which caused
|
||||
// https://github.com/kubernetes/client-go/issues/374 and
|
||||
// https://github.com/kubernetes/kubernetes/issues/87615.
|
||||
t2.ReadIdleTimeout = time.Duration(readIdleTimeoutSeconds()) * time.Second
|
||||
t2.PingTimeout = time.Duration(pingTimeoutSeconds()) * time.Second
|
||||
return nil
|
||||
}
|
||||
|
||||
func allowsHTTP2(t *http.Transport) bool {
|
||||
if t.TLSClientConfig == nil || len(t.TLSClientConfig.NextProtos) == 0 {
|
||||
// the transport expressed no NextProto preference, allow
|
||||
return true
|
||||
}
|
||||
for _, p := range t.TLSClientConfig.NextProtos {
|
||||
if p == http2.NextProtoTLS {
|
||||
// the transport explicitly allowed http/2
|
||||
return true
|
||||
}
|
||||
}
|
||||
// the transport explicitly set NextProtos and excluded http/2
|
||||
return false
|
||||
}
|
||||
|
||||
type RoundTripperWrapper interface {
|
||||
http.RoundTripper
|
||||
WrappedRoundTripper() http.RoundTripper
|
||||
}
|
||||
|
||||
type DialFunc func(ctx context.Context, net, addr string) (net.Conn, error)
|
||||
|
||||
func DialerFor(transport http.RoundTripper) (DialFunc, error) {
|
||||
if transport == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch transport := transport.(type) {
|
||||
case *http.Transport:
|
||||
// transport.DialContext takes precedence over transport.Dial
|
||||
if transport.DialContext != nil {
|
||||
return transport.DialContext, nil
|
||||
}
|
||||
// adapt transport.Dial to the DialWithContext signature
|
||||
if transport.Dial != nil {
|
||||
return func(ctx context.Context, net, addr string) (net.Conn, error) {
|
||||
return transport.Dial(net, addr)
|
||||
}, nil
|
||||
}
|
||||
// otherwise return nil
|
||||
return nil, nil
|
||||
case RoundTripperWrapper:
|
||||
return DialerFor(transport.WrappedRoundTripper())
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown transport type: %T", transport)
|
||||
}
|
||||
}
|
||||
|
||||
// CloseIdleConnectionsFor close idles connections for the Transport.
|
||||
// If the Transport is wrapped it iterates over the wrapped round trippers
|
||||
// until it finds one that implements the CloseIdleConnections method.
|
||||
// If the Transport does not have a CloseIdleConnections method
|
||||
// then this function does nothing.
|
||||
func CloseIdleConnectionsFor(transport http.RoundTripper) {
|
||||
if transport == nil {
|
||||
return
|
||||
}
|
||||
type closeIdler interface {
|
||||
CloseIdleConnections()
|
||||
}
|
||||
|
||||
switch transport := transport.(type) {
|
||||
case closeIdler:
|
||||
transport.CloseIdleConnections()
|
||||
case RoundTripperWrapper:
|
||||
CloseIdleConnectionsFor(transport.WrappedRoundTripper())
|
||||
default:
|
||||
klog.Warningf("unknown transport type: %T", transport)
|
||||
}
|
||||
}
|
||||
|
||||
type TLSClientConfigHolder interface {
|
||||
TLSClientConfig() *tls.Config
|
||||
}
|
||||
|
||||
func TLSClientConfig(transport http.RoundTripper) (*tls.Config, error) {
|
||||
if transport == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch transport := transport.(type) {
|
||||
case *http.Transport:
|
||||
return transport.TLSClientConfig, nil
|
||||
case TLSClientConfigHolder:
|
||||
return transport.TLSClientConfig(), nil
|
||||
case RoundTripperWrapper:
|
||||
return TLSClientConfig(transport.WrappedRoundTripper())
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown transport type: %T", transport)
|
||||
}
|
||||
}
|
||||
|
||||
func FormatURL(scheme string, host string, port int, path string) *url.URL {
|
||||
return &url.URL{
|
||||
Scheme: scheme,
|
||||
Host: net.JoinHostPort(host, strconv.Itoa(port)),
|
||||
Path: path,
|
||||
}
|
||||
}
|
||||
|
||||
func GetHTTPClient(req *http.Request) string {
|
||||
if ua := req.UserAgent(); len(ua) != 0 {
|
||||
return ua
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// SourceIPs splits the comma separated X-Forwarded-For header and joins it with
|
||||
// the X-Real-Ip header and/or req.RemoteAddr, ignoring invalid IPs.
|
||||
// The X-Real-Ip is omitted if it's already present in the X-Forwarded-For chain.
|
||||
// The req.RemoteAddr is always the last IP in the returned list.
|
||||
// It returns nil if all of these are empty or invalid.
|
||||
func SourceIPs(req *http.Request) []net.IP {
|
||||
var srcIPs []net.IP
|
||||
|
||||
hdr := req.Header
|
||||
// First check the X-Forwarded-For header for requests via proxy.
|
||||
hdrForwardedFor := hdr.Get("X-Forwarded-For")
|
||||
if hdrForwardedFor != "" {
|
||||
// X-Forwarded-For can be a csv of IPs in case of multiple proxies.
|
||||
// Use the first valid one.
|
||||
parts := strings.Split(hdrForwardedFor, ",")
|
||||
for _, part := range parts {
|
||||
ip := netutils.ParseIPSloppy(strings.TrimSpace(part))
|
||||
if ip != nil {
|
||||
srcIPs = append(srcIPs, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try the X-Real-Ip header.
|
||||
hdrRealIp := hdr.Get("X-Real-Ip")
|
||||
if hdrRealIp != "" {
|
||||
ip := netutils.ParseIPSloppy(hdrRealIp)
|
||||
// Only append the X-Real-Ip if it's not already contained in the X-Forwarded-For chain.
|
||||
if ip != nil && !containsIP(srcIPs, ip) {
|
||||
srcIPs = append(srcIPs, ip)
|
||||
}
|
||||
}
|
||||
|
||||
// Always include the request Remote Address as it cannot be easily spoofed.
|
||||
var remoteIP net.IP
|
||||
// Remote Address in Go's HTTP server is in the form host:port so we need to split that first.
|
||||
host, _, err := net.SplitHostPort(req.RemoteAddr)
|
||||
if err == nil {
|
||||
remoteIP = netutils.ParseIPSloppy(host)
|
||||
}
|
||||
// Fallback if Remote Address was just IP.
|
||||
if remoteIP == nil {
|
||||
remoteIP = netutils.ParseIPSloppy(req.RemoteAddr)
|
||||
}
|
||||
|
||||
// Don't duplicate remote IP if it's already the last address in the chain.
|
||||
if remoteIP != nil && (len(srcIPs) == 0 || !remoteIP.Equal(srcIPs[len(srcIPs)-1])) {
|
||||
srcIPs = append(srcIPs, remoteIP)
|
||||
}
|
||||
|
||||
return srcIPs
|
||||
}
|
||||
|
||||
// Checks whether the given IP address is contained in the list of IPs.
|
||||
func containsIP(ips []net.IP, ip net.IP) bool {
|
||||
for _, v := range ips {
|
||||
if v.Equal(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Extracts and returns the clients IP from the given request.
|
||||
// Looks at X-Forwarded-For header, X-Real-Ip header and request.RemoteAddr in that order.
|
||||
// Returns nil if none of them are set or is set to an invalid value.
|
||||
func GetClientIP(req *http.Request) net.IP {
|
||||
ips := SourceIPs(req)
|
||||
if len(ips) == 0 {
|
||||
return nil
|
||||
}
|
||||
return ips[0]
|
||||
}
|
||||
|
||||
// Prepares the X-Forwarded-For header for another forwarding hop by appending the previous sender's
|
||||
// IP address to the X-Forwarded-For chain.
|
||||
func AppendForwardedForHeader(req *http.Request) {
|
||||
// Copied from net/http/httputil/reverseproxy.go:
|
||||
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
|
||||
// If we aren't the first proxy retain prior
|
||||
// X-Forwarded-For information as a comma+space
|
||||
// separated list and fold multiple headers into one.
|
||||
if prior, ok := req.Header["X-Forwarded-For"]; ok {
|
||||
clientIP = strings.Join(prior, ", ") + ", " + clientIP
|
||||
}
|
||||
req.Header.Set("X-Forwarded-For", clientIP)
|
||||
}
|
||||
}
|
||||
|
||||
var defaultProxyFuncPointer = fmt.Sprintf("%p", http.ProxyFromEnvironment)
|
||||
|
||||
// isDefault checks to see if the transportProxierFunc is pointing to the default one
|
||||
func isDefault(transportProxier func(*http.Request) (*url.URL, error)) bool {
|
||||
transportProxierPointer := fmt.Sprintf("%p", transportProxier)
|
||||
return transportProxierPointer == defaultProxyFuncPointer
|
||||
}
|
||||
|
||||
// NewProxierWithNoProxyCIDR constructs a Proxier function that respects CIDRs in NO_PROXY and delegates if
|
||||
// no matching CIDRs are found
|
||||
func NewProxierWithNoProxyCIDR(delegate func(req *http.Request) (*url.URL, error)) func(req *http.Request) (*url.URL, error) {
|
||||
// we wrap the default method, so we only need to perform our check if the NO_PROXY (or no_proxy) envvar has a CIDR in it
|
||||
noProxyEnv := os.Getenv("NO_PROXY")
|
||||
if noProxyEnv == "" {
|
||||
noProxyEnv = os.Getenv("no_proxy")
|
||||
}
|
||||
noProxyRules := strings.Split(noProxyEnv, ",")
|
||||
|
||||
cidrs := []*net.IPNet{}
|
||||
for _, noProxyRule := range noProxyRules {
|
||||
_, cidr, _ := netutils.ParseCIDRSloppy(noProxyRule)
|
||||
if cidr != nil {
|
||||
cidrs = append(cidrs, cidr)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cidrs) == 0 {
|
||||
return delegate
|
||||
}
|
||||
|
||||
return func(req *http.Request) (*url.URL, error) {
|
||||
ip := netutils.ParseIPSloppy(req.URL.Hostname())
|
||||
if ip == nil {
|
||||
return delegate(req)
|
||||
}
|
||||
|
||||
for _, cidr := range cidrs {
|
||||
if cidr.Contains(ip) {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
return delegate(req)
|
||||
}
|
||||
}
|
||||
|
||||
// DialerFunc implements Dialer for the provided function.
|
||||
type DialerFunc func(req *http.Request) (net.Conn, error)
|
||||
|
||||
func (fn DialerFunc) Dial(req *http.Request) (net.Conn, error) {
|
||||
return fn(req)
|
||||
}
|
||||
|
||||
// Dialer dials a host and writes a request to it.
|
||||
type Dialer interface {
|
||||
// Dial connects to the host specified by req's URL, writes the request to the connection, and
|
||||
// returns the opened net.Conn.
|
||||
Dial(req *http.Request) (net.Conn, error)
|
||||
}
|
||||
|
||||
// CloneRequest creates a shallow copy of the request along with a deep copy of the Headers.
|
||||
func CloneRequest(req *http.Request) *http.Request {
|
||||
r := new(http.Request)
|
||||
|
||||
// shallow clone
|
||||
*r = *req
|
||||
|
||||
// deep copy headers
|
||||
r.Header = CloneHeader(req.Header)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// CloneHeader creates a deep copy of an http.Header.
|
||||
func CloneHeader(in http.Header) http.Header {
|
||||
out := make(http.Header, len(in))
|
||||
for key, values := range in {
|
||||
newValues := make([]string, len(values))
|
||||
copy(newValues, values)
|
||||
out[key] = newValues
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// WarningHeader contains a single RFC2616 14.46 warnings header
|
||||
type WarningHeader struct {
|
||||
// Codeindicates the type of warning. 299 is a miscellaneous persistent warning
|
||||
Code int
|
||||
// Agent contains the name or pseudonym of the server adding the Warning header.
|
||||
// A single "-" is recommended when agent is unknown.
|
||||
Agent string
|
||||
// Warning text
|
||||
Text string
|
||||
}
|
||||
|
||||
// ParseWarningHeaders extract RFC2616 14.46 warnings headers from the specified set of header values.
|
||||
// Multiple comma-separated warnings per header are supported.
|
||||
// If errors are encountered on a header, the remainder of that header are skipped and subsequent headers are parsed.
|
||||
// Returns successfully parsed warnings and any errors encountered.
|
||||
func ParseWarningHeaders(headers []string) ([]WarningHeader, []error) {
|
||||
var (
|
||||
results []WarningHeader
|
||||
errs []error
|
||||
)
|
||||
for _, header := range headers {
|
||||
for len(header) > 0 {
|
||||
result, remainder, err := ParseWarningHeader(header)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
break
|
||||
}
|
||||
results = append(results, result)
|
||||
header = remainder
|
||||
}
|
||||
}
|
||||
return results, errs
|
||||
}
|
||||
|
||||
var (
|
||||
codeMatcher = regexp.MustCompile(`^[0-9]{3}$`)
|
||||
wordDecoder = &mime.WordDecoder{}
|
||||
)
|
||||
|
||||
// ParseWarningHeader extracts one RFC2616 14.46 warning from the specified header,
|
||||
// returning an error if the header does not contain a correctly formatted warning.
|
||||
// Any remaining content in the header is returned.
|
||||
func ParseWarningHeader(header string) (result WarningHeader, remainder string, err error) {
|
||||
// https://tools.ietf.org/html/rfc2616#section-14.46
|
||||
// updated by
|
||||
// https://tools.ietf.org/html/rfc7234#section-5.5
|
||||
// https://tools.ietf.org/html/rfc7234#appendix-A
|
||||
// Some requirements regarding production and processing of the Warning
|
||||
// header fields have been relaxed, as it is not widely implemented.
|
||||
// Furthermore, the Warning header field no longer uses RFC 2047
|
||||
// encoding, nor does it allow multiple languages, as these aspects were
|
||||
// not implemented.
|
||||
//
|
||||
// Format is one of:
|
||||
// warn-code warn-agent "warn-text"
|
||||
// warn-code warn-agent "warn-text" "warn-date"
|
||||
//
|
||||
// warn-code is a three digit number
|
||||
// warn-agent is unquoted and contains no spaces
|
||||
// warn-text is quoted with backslash escaping (RFC2047-encoded according to RFC2616, not encoded according to RFC7234)
|
||||
// warn-date is optional, quoted, and in HTTP-date format (no embedded or escaped quotes)
|
||||
//
|
||||
// additional warnings can optionally be included in the same header by comma-separating them:
|
||||
// warn-code warn-agent "warn-text" "warn-date"[, warn-code warn-agent "warn-text" "warn-date", ...]
|
||||
|
||||
// tolerate leading whitespace
|
||||
header = strings.TrimSpace(header)
|
||||
|
||||
parts := strings.SplitN(header, " ", 3)
|
||||
if len(parts) != 3 {
|
||||
return WarningHeader{}, "", errors.New("invalid warning header: fewer than 3 segments")
|
||||
}
|
||||
code, agent, textDateRemainder := parts[0], parts[1], parts[2]
|
||||
|
||||
// verify code format
|
||||
if !codeMatcher.Match([]byte(code)) {
|
||||
return WarningHeader{}, "", errors.New("invalid warning header: code segment is not 3 digits between 100-299")
|
||||
}
|
||||
codeInt, _ := strconv.ParseInt(code, 10, 64)
|
||||
|
||||
// verify agent presence
|
||||
if len(agent) == 0 {
|
||||
return WarningHeader{}, "", errors.New("invalid warning header: empty agent segment")
|
||||
}
|
||||
if !utf8.ValidString(agent) || hasAnyRunes(agent, unicode.IsControl) {
|
||||
return WarningHeader{}, "", errors.New("invalid warning header: invalid agent")
|
||||
}
|
||||
|
||||
// verify textDateRemainder presence
|
||||
if len(textDateRemainder) == 0 {
|
||||
return WarningHeader{}, "", errors.New("invalid warning header: empty text segment")
|
||||
}
|
||||
|
||||
// extract text
|
||||
text, dateAndRemainder, err := parseQuotedString(textDateRemainder)
|
||||
if err != nil {
|
||||
return WarningHeader{}, "", fmt.Errorf("invalid warning header: %v", err)
|
||||
}
|
||||
// tolerate RFC2047-encoded text from warnings produced according to RFC2616
|
||||
if decodedText, err := wordDecoder.DecodeHeader(text); err == nil {
|
||||
text = decodedText
|
||||
}
|
||||
if !utf8.ValidString(text) || hasAnyRunes(text, unicode.IsControl) {
|
||||
return WarningHeader{}, "", errors.New("invalid warning header: invalid text")
|
||||
}
|
||||
result = WarningHeader{Code: int(codeInt), Agent: agent, Text: text}
|
||||
|
||||
if len(dateAndRemainder) > 0 {
|
||||
if dateAndRemainder[0] == '"' {
|
||||
// consume date
|
||||
foundEndQuote := false
|
||||
for i := 1; i < len(dateAndRemainder); i++ {
|
||||
if dateAndRemainder[i] == '"' {
|
||||
foundEndQuote = true
|
||||
remainder = strings.TrimSpace(dateAndRemainder[i+1:])
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundEndQuote {
|
||||
return WarningHeader{}, "", errors.New("invalid warning header: unterminated date segment")
|
||||
}
|
||||
} else {
|
||||
remainder = dateAndRemainder
|
||||
}
|
||||
}
|
||||
if len(remainder) > 0 {
|
||||
if remainder[0] == ',' {
|
||||
// consume comma if present
|
||||
remainder = strings.TrimSpace(remainder[1:])
|
||||
} else {
|
||||
return WarningHeader{}, "", errors.New("invalid warning header: unexpected token after warn-date")
|
||||
}
|
||||
}
|
||||
|
||||
return result, remainder, nil
|
||||
}
|
||||
|
||||
func parseQuotedString(quotedString string) (string, string, error) {
|
||||
if len(quotedString) == 0 {
|
||||
return "", "", errors.New("invalid quoted string: 0-length")
|
||||
}
|
||||
|
||||
if quotedString[0] != '"' {
|
||||
return "", "", errors.New("invalid quoted string: missing initial quote")
|
||||
}
|
||||
|
||||
quotedString = quotedString[1:]
|
||||
var remainder string
|
||||
escaping := false
|
||||
closedQuote := false
|
||||
result := &strings.Builder{}
|
||||
loop:
|
||||
for i := 0; i < len(quotedString); i++ {
|
||||
b := quotedString[i]
|
||||
switch b {
|
||||
case '"':
|
||||
if escaping {
|
||||
result.WriteByte(b)
|
||||
escaping = false
|
||||
} else {
|
||||
closedQuote = true
|
||||
remainder = strings.TrimSpace(quotedString[i+1:])
|
||||
break loop
|
||||
}
|
||||
case '\\':
|
||||
if escaping {
|
||||
result.WriteByte(b)
|
||||
escaping = false
|
||||
} else {
|
||||
escaping = true
|
||||
}
|
||||
default:
|
||||
result.WriteByte(b)
|
||||
escaping = false
|
||||
}
|
||||
}
|
||||
|
||||
if !closedQuote {
|
||||
return "", "", errors.New("invalid quoted string: missing closing quote")
|
||||
}
|
||||
return result.String(), remainder, nil
|
||||
}
|
||||
|
||||
func NewWarningHeader(code int, agent, text string) (string, error) {
|
||||
if code < 0 || code > 999 {
|
||||
return "", errors.New("code must be between 0 and 999")
|
||||
}
|
||||
if len(agent) == 0 {
|
||||
agent = "-"
|
||||
} else if !utf8.ValidString(agent) || strings.ContainsAny(agent, `\"`) || hasAnyRunes(agent, unicode.IsSpace, unicode.IsControl) {
|
||||
return "", errors.New("agent must be valid UTF-8 and must not contain spaces, quotes, backslashes, or control characters")
|
||||
}
|
||||
if !utf8.ValidString(text) || hasAnyRunes(text, unicode.IsControl) {
|
||||
return "", errors.New("text must be valid UTF-8 and must not contain control characters")
|
||||
}
|
||||
return fmt.Sprintf("%03d %s %s", code, agent, makeQuotedString(text)), nil
|
||||
}
|
||||
|
||||
func hasAnyRunes(s string, runeCheckers ...func(rune) bool) bool {
|
||||
for _, r := range s {
|
||||
for _, checker := range runeCheckers {
|
||||
if checker(r) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func makeQuotedString(s string) string {
|
||||
result := &bytes.Buffer{}
|
||||
// opening quote
|
||||
result.WriteRune('"')
|
||||
for _, c := range s {
|
||||
switch c {
|
||||
case '"', '\\':
|
||||
// escape " and \
|
||||
result.WriteRune('\\')
|
||||
result.WriteRune(c)
|
||||
default:
|
||||
// write everything else as-is
|
||||
result.WriteRune(c)
|
||||
}
|
||||
}
|
||||
// closing quote
|
||||
result.WriteRune('"')
|
||||
return result.String()
|
||||
}
|
500
api/vendor/k8s.io/apimachinery/pkg/util/net/interface.go
generated
vendored
Normal file
500
api/vendor/k8s.io/apimachinery/pkg/util/net/interface.go
generated
vendored
Normal file
@ -0,0 +1,500 @@
|
||||
/*
|
||||
Copyright 2016 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 net
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"strings"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
netutils "k8s.io/utils/net"
|
||||
)
|
||||
|
||||
type AddressFamily uint
|
||||
|
||||
const (
|
||||
familyIPv4 AddressFamily = 4
|
||||
familyIPv6 AddressFamily = 6
|
||||
)
|
||||
|
||||
type AddressFamilyPreference []AddressFamily
|
||||
|
||||
var (
|
||||
preferIPv4 = AddressFamilyPreference{familyIPv4, familyIPv6}
|
||||
preferIPv6 = AddressFamilyPreference{familyIPv6, familyIPv4}
|
||||
)
|
||||
|
||||
const (
|
||||
// LoopbackInterfaceName is the default name of the loopback interface
|
||||
LoopbackInterfaceName = "lo"
|
||||
)
|
||||
|
||||
const (
|
||||
ipv4RouteFile = "/proc/net/route"
|
||||
ipv6RouteFile = "/proc/net/ipv6_route"
|
||||
)
|
||||
|
||||
type Route struct {
|
||||
Interface string
|
||||
Destination net.IP
|
||||
Gateway net.IP
|
||||
Family AddressFamily
|
||||
}
|
||||
|
||||
type RouteFile struct {
|
||||
name string
|
||||
parse func(input io.Reader) ([]Route, error)
|
||||
}
|
||||
|
||||
// noRoutesError can be returned in case of no routes
|
||||
type noRoutesError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func (e noRoutesError) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
// IsNoRoutesError checks if an error is of type noRoutesError
|
||||
func IsNoRoutesError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
switch err.(type) {
|
||||
case noRoutesError:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
v4File = RouteFile{name: ipv4RouteFile, parse: getIPv4DefaultRoutes}
|
||||
v6File = RouteFile{name: ipv6RouteFile, parse: getIPv6DefaultRoutes}
|
||||
)
|
||||
|
||||
func (rf RouteFile) extract() ([]Route, error) {
|
||||
file, err := os.Open(rf.name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
return rf.parse(file)
|
||||
}
|
||||
|
||||
// getIPv4DefaultRoutes obtains the IPv4 routes, and filters out non-default routes.
|
||||
func getIPv4DefaultRoutes(input io.Reader) ([]Route, error) {
|
||||
routes := []Route{}
|
||||
scanner := bufio.NewReader(input)
|
||||
for {
|
||||
line, err := scanner.ReadString('\n')
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
//ignore the headers in the route info
|
||||
if strings.HasPrefix(line, "Iface") {
|
||||
continue
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
// Interested in fields:
|
||||
// 0 - interface name
|
||||
// 1 - destination address
|
||||
// 2 - gateway
|
||||
dest, err := parseIP(fields[1], familyIPv4)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gw, err := parseIP(fields[2], familyIPv4)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !dest.Equal(net.IPv4zero) {
|
||||
continue
|
||||
}
|
||||
routes = append(routes, Route{
|
||||
Interface: fields[0],
|
||||
Destination: dest,
|
||||
Gateway: gw,
|
||||
Family: familyIPv4,
|
||||
})
|
||||
}
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
func getIPv6DefaultRoutes(input io.Reader) ([]Route, error) {
|
||||
routes := []Route{}
|
||||
scanner := bufio.NewReader(input)
|
||||
for {
|
||||
line, err := scanner.ReadString('\n')
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
// Interested in fields:
|
||||
// 0 - destination address
|
||||
// 4 - gateway
|
||||
// 9 - interface name
|
||||
dest, err := parseIP(fields[0], familyIPv6)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gw, err := parseIP(fields[4], familyIPv6)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !dest.Equal(net.IPv6zero) {
|
||||
continue
|
||||
}
|
||||
if gw.Equal(net.IPv6zero) {
|
||||
continue // loopback
|
||||
}
|
||||
routes = append(routes, Route{
|
||||
Interface: fields[9],
|
||||
Destination: dest,
|
||||
Gateway: gw,
|
||||
Family: familyIPv6,
|
||||
})
|
||||
}
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
// parseIP takes the hex IP address string from route file and converts it
|
||||
// to a net.IP address. For IPv4, the value must be converted to big endian.
|
||||
func parseIP(str string, family AddressFamily) (net.IP, error) {
|
||||
if str == "" {
|
||||
return nil, fmt.Errorf("input is nil")
|
||||
}
|
||||
bytes, err := hex.DecodeString(str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if family == familyIPv4 {
|
||||
if len(bytes) != net.IPv4len {
|
||||
return nil, fmt.Errorf("invalid IPv4 address in route")
|
||||
}
|
||||
return net.IP([]byte{bytes[3], bytes[2], bytes[1], bytes[0]}), nil
|
||||
}
|
||||
// Must be IPv6
|
||||
if len(bytes) != net.IPv6len {
|
||||
return nil, fmt.Errorf("invalid IPv6 address in route")
|
||||
}
|
||||
return net.IP(bytes), nil
|
||||
}
|
||||
|
||||
func isInterfaceUp(intf *net.Interface) bool {
|
||||
if intf == nil {
|
||||
return false
|
||||
}
|
||||
if intf.Flags&net.FlagUp != 0 {
|
||||
klog.V(4).Infof("Interface %v is up", intf.Name)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isLoopbackOrPointToPoint(intf *net.Interface) bool {
|
||||
return intf.Flags&(net.FlagLoopback|net.FlagPointToPoint) != 0
|
||||
}
|
||||
|
||||
// getMatchingGlobalIP returns the first valid global unicast address of the given
|
||||
// 'family' from the list of 'addrs'.
|
||||
func getMatchingGlobalIP(addrs []net.Addr, family AddressFamily) (net.IP, error) {
|
||||
if len(addrs) > 0 {
|
||||
for i := range addrs {
|
||||
klog.V(4).Infof("Checking addr %s.", addrs[i].String())
|
||||
ip, _, err := netutils.ParseCIDRSloppy(addrs[i].String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if memberOf(ip, family) {
|
||||
if ip.IsGlobalUnicast() {
|
||||
klog.V(4).Infof("IP found %v", ip)
|
||||
return ip, nil
|
||||
} else {
|
||||
klog.V(4).Infof("Non-global unicast address found %v", ip)
|
||||
}
|
||||
} else {
|
||||
klog.V(4).Infof("%v is not an IPv%d address", ip, int(family))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// getIPFromInterface gets the IPs on an interface and returns a global unicast address, if any. The
|
||||
// interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
|
||||
func getIPFromInterface(intfName string, forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
|
||||
intf, err := nw.InterfaceByName(intfName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isInterfaceUp(intf) {
|
||||
addrs, err := nw.Addrs(intf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
klog.V(4).Infof("Interface %q has %d addresses :%v.", intfName, len(addrs), addrs)
|
||||
matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if matchingIP != nil {
|
||||
klog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intfName)
|
||||
return matchingIP, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// getIPFromLoopbackInterface gets the IPs on a loopback interface and returns a global unicast address, if any.
|
||||
// The loopback interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
|
||||
func getIPFromLoopbackInterface(forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
|
||||
intfs, err := nw.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, intf := range intfs {
|
||||
if !isInterfaceUp(&intf) {
|
||||
continue
|
||||
}
|
||||
if intf.Flags&(net.FlagLoopback) != 0 {
|
||||
addrs, err := nw.Addrs(&intf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
klog.V(4).Infof("Interface %q has %d addresses :%v.", intf.Name, len(addrs), addrs)
|
||||
matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if matchingIP != nil {
|
||||
klog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intf.Name)
|
||||
return matchingIP, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// memberOf tells if the IP is of the desired family. Used for checking interface addresses.
|
||||
func memberOf(ip net.IP, family AddressFamily) bool {
|
||||
if ip.To4() != nil {
|
||||
return family == familyIPv4
|
||||
} else {
|
||||
return family == familyIPv6
|
||||
}
|
||||
}
|
||||
|
||||
// chooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that
|
||||
// has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP.
|
||||
// addressFamilies determines whether it prefers IPv4 or IPv6
|
||||
func chooseIPFromHostInterfaces(nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
|
||||
intfs, err := nw.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(intfs) == 0 {
|
||||
return nil, fmt.Errorf("no interfaces found on host.")
|
||||
}
|
||||
for _, family := range addressFamilies {
|
||||
klog.V(4).Infof("Looking for system interface with a global IPv%d address", uint(family))
|
||||
for _, intf := range intfs {
|
||||
if !isInterfaceUp(&intf) {
|
||||
klog.V(4).Infof("Skipping: down interface %q", intf.Name)
|
||||
continue
|
||||
}
|
||||
if isLoopbackOrPointToPoint(&intf) {
|
||||
klog.V(4).Infof("Skipping: LB or P2P interface %q", intf.Name)
|
||||
continue
|
||||
}
|
||||
addrs, err := nw.Addrs(&intf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(addrs) == 0 {
|
||||
klog.V(4).Infof("Skipping: no addresses on interface %q", intf.Name)
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
ip, _, err := netutils.ParseCIDRSloppy(addr.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse CIDR for interface %q: %s", intf.Name, err)
|
||||
}
|
||||
if !memberOf(ip, family) {
|
||||
klog.V(4).Infof("Skipping: no address family match for %q on interface %q.", ip, intf.Name)
|
||||
continue
|
||||
}
|
||||
// TODO: Decide if should open up to allow IPv6 LLAs in future.
|
||||
if !ip.IsGlobalUnicast() {
|
||||
klog.V(4).Infof("Skipping: non-global address %q on interface %q.", ip, intf.Name)
|
||||
continue
|
||||
}
|
||||
klog.V(4).Infof("Found global unicast address %q on interface %q.", ip, intf.Name)
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no acceptable interface with global unicast address found on host")
|
||||
}
|
||||
|
||||
// ChooseHostInterface is a method used fetch an IP for a daemon.
|
||||
// If there is no routing info file, it will choose a global IP from the system
|
||||
// interfaces. Otherwise, it will use IPv4 and IPv6 route information to return the
|
||||
// IP of the interface with a gateway on it (with priority given to IPv4). For a node
|
||||
// with no internet connection, it returns error.
|
||||
func ChooseHostInterface() (net.IP, error) {
|
||||
return chooseHostInterface(preferIPv4)
|
||||
}
|
||||
|
||||
func chooseHostInterface(addressFamilies AddressFamilyPreference) (net.IP, error) {
|
||||
var nw networkInterfacer = networkInterface{}
|
||||
if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
|
||||
return chooseIPFromHostInterfaces(nw, addressFamilies)
|
||||
}
|
||||
routes, err := getAllDefaultRoutes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return chooseHostInterfaceFromRoute(routes, nw, addressFamilies)
|
||||
}
|
||||
|
||||
// networkInterfacer defines an interface for several net library functions. Production
|
||||
// code will forward to net library functions, and unit tests will override the methods
|
||||
// for testing purposes.
|
||||
type networkInterfacer interface {
|
||||
InterfaceByName(intfName string) (*net.Interface, error)
|
||||
Addrs(intf *net.Interface) ([]net.Addr, error)
|
||||
Interfaces() ([]net.Interface, error)
|
||||
}
|
||||
|
||||
// networkInterface implements the networkInterfacer interface for production code, just
|
||||
// wrapping the underlying net library function calls.
|
||||
type networkInterface struct{}
|
||||
|
||||
func (_ networkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||
return net.InterfaceByName(intfName)
|
||||
}
|
||||
|
||||
func (_ networkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||
return intf.Addrs()
|
||||
}
|
||||
|
||||
func (_ networkInterface) Interfaces() ([]net.Interface, error) {
|
||||
return net.Interfaces()
|
||||
}
|
||||
|
||||
// getAllDefaultRoutes obtains IPv4 and IPv6 default routes on the node. If unable
|
||||
// to read the IPv4 routing info file, we return an error. If unable to read the IPv6
|
||||
// routing info file (which is optional), we'll just use the IPv4 route information.
|
||||
// Using all the routing info, if no default routes are found, an error is returned.
|
||||
func getAllDefaultRoutes() ([]Route, error) {
|
||||
routes, err := v4File.extract()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v6Routes, _ := v6File.extract()
|
||||
routes = append(routes, v6Routes...)
|
||||
if len(routes) == 0 {
|
||||
return nil, noRoutesError{
|
||||
message: fmt.Sprintf("no default routes found in %q or %q", v4File.name, v6File.name),
|
||||
}
|
||||
}
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
// chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
|
||||
// global IP address from the interface for the route. If there are routes but no global
|
||||
// address is obtained from the interfaces, it checks if the loopback interface has a global address.
|
||||
// addressFamilies determines whether it prefers IPv4 or IPv6
|
||||
func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
|
||||
for _, family := range addressFamilies {
|
||||
klog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
|
||||
for _, route := range routes {
|
||||
if route.Family != family {
|
||||
continue
|
||||
}
|
||||
klog.V(4).Infof("Default route transits interface %q", route.Interface)
|
||||
finalIP, err := getIPFromInterface(route.Interface, family, nw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if finalIP != nil {
|
||||
klog.V(4).Infof("Found active IP %v ", finalIP)
|
||||
return finalIP, nil
|
||||
}
|
||||
// In case of network setups where default routes are present, but network
|
||||
// interfaces use only link-local addresses (e.g. as described in RFC5549).
|
||||
// the global IP is assigned to the loopback interface, and we should use it
|
||||
loopbackIP, err := getIPFromLoopbackInterface(family, nw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if loopbackIP != nil {
|
||||
klog.V(4).Infof("Found active IP %v on Loopback interface", loopbackIP)
|
||||
return loopbackIP, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
klog.V(4).Infof("No active IP found by looking at default routes")
|
||||
return nil, fmt.Errorf("unable to select an IP from default routes.")
|
||||
}
|
||||
|
||||
// ResolveBindAddress returns the IP address of a daemon, based on the given bindAddress:
|
||||
// If bindAddress is unset, it returns the host's default IP, as with ChooseHostInterface().
|
||||
// If bindAddress is unspecified or loopback, it returns the default IP of the same
|
||||
// address family as bindAddress.
|
||||
// Otherwise, it just returns bindAddress.
|
||||
func ResolveBindAddress(bindAddress net.IP) (net.IP, error) {
|
||||
addressFamilies := preferIPv4
|
||||
if bindAddress != nil && memberOf(bindAddress, familyIPv6) {
|
||||
addressFamilies = preferIPv6
|
||||
}
|
||||
|
||||
if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
|
||||
hostIP, err := chooseHostInterface(addressFamilies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bindAddress = hostIP
|
||||
}
|
||||
return bindAddress, nil
|
||||
}
|
||||
|
||||
// ChooseBindAddressForInterface choose a global IP for a specific interface, with priority given to IPv4.
|
||||
// This is required in case of network setups where default routes are present, but network
|
||||
// interfaces use only link-local addresses (e.g. as described in RFC5549).
|
||||
// e.g when using BGP to announce a host IP over link-local ip addresses and this ip address is attached to the lo interface.
|
||||
func ChooseBindAddressForInterface(intfName string) (net.IP, error) {
|
||||
var nw networkInterfacer = networkInterface{}
|
||||
for _, family := range preferIPv4 {
|
||||
ip, err := getIPFromInterface(intfName, family, nw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ip != nil {
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("unable to select an IP from %s network interface", intfName)
|
||||
}
|
149
api/vendor/k8s.io/apimachinery/pkg/util/net/port_range.go
generated
vendored
Normal file
149
api/vendor/k8s.io/apimachinery/pkg/util/net/port_range.go
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
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 net
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PortRange represents a range of TCP/UDP ports. To represent a single port,
|
||||
// set Size to 1.
|
||||
type PortRange struct {
|
||||
Base int
|
||||
Size int
|
||||
}
|
||||
|
||||
// Contains tests whether a given port falls within the PortRange.
|
||||
func (pr *PortRange) Contains(p int) bool {
|
||||
return (p >= pr.Base) && ((p - pr.Base) < pr.Size)
|
||||
}
|
||||
|
||||
// String converts the PortRange to a string representation, which can be
|
||||
// parsed by PortRange.Set or ParsePortRange.
|
||||
func (pr PortRange) String() string {
|
||||
if pr.Size == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%d-%d", pr.Base, pr.Base+pr.Size-1)
|
||||
}
|
||||
|
||||
// Set parses a string of the form "value", "min-max", or "min+offset", inclusive at both ends, and
|
||||
// sets the PortRange from it. This is part of the flag.Value and pflag.Value
|
||||
// interfaces.
|
||||
func (pr *PortRange) Set(value string) error {
|
||||
const (
|
||||
SinglePortNotation = 1 << iota
|
||||
HyphenNotation
|
||||
PlusNotation
|
||||
)
|
||||
|
||||
value = strings.TrimSpace(value)
|
||||
hyphenIndex := strings.Index(value, "-")
|
||||
plusIndex := strings.Index(value, "+")
|
||||
|
||||
if value == "" {
|
||||
pr.Base = 0
|
||||
pr.Size = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
var low, high int
|
||||
var notation int
|
||||
|
||||
if plusIndex == -1 && hyphenIndex == -1 {
|
||||
notation |= SinglePortNotation
|
||||
}
|
||||
if hyphenIndex != -1 {
|
||||
notation |= HyphenNotation
|
||||
}
|
||||
if plusIndex != -1 {
|
||||
notation |= PlusNotation
|
||||
}
|
||||
|
||||
switch notation {
|
||||
case SinglePortNotation:
|
||||
var port int
|
||||
port, err = strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
low = port
|
||||
high = port
|
||||
case HyphenNotation:
|
||||
low, err = strconv.Atoi(value[:hyphenIndex])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
high, err = strconv.Atoi(value[hyphenIndex+1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case PlusNotation:
|
||||
var offset int
|
||||
low, err = strconv.Atoi(value[:plusIndex])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
offset, err = strconv.Atoi(value[plusIndex+1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
high = low + offset
|
||||
default:
|
||||
return fmt.Errorf("unable to parse port range: %s", value)
|
||||
}
|
||||
|
||||
if low > 65535 || high > 65535 {
|
||||
return fmt.Errorf("the port range cannot be greater than 65535: %s", value)
|
||||
}
|
||||
|
||||
if high < low {
|
||||
return fmt.Errorf("end port cannot be less than start port: %s", value)
|
||||
}
|
||||
|
||||
pr.Base = low
|
||||
pr.Size = 1 + high - low
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns a descriptive string about this type. This is part of the
|
||||
// pflag.Value interface.
|
||||
func (*PortRange) Type() string {
|
||||
return "portRange"
|
||||
}
|
||||
|
||||
// ParsePortRange parses a string of the form "min-max", inclusive at both
|
||||
// ends, and initializes a new PortRange from it.
|
||||
func ParsePortRange(value string) (*PortRange, error) {
|
||||
pr := &PortRange{}
|
||||
err := pr.Set(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pr, nil
|
||||
}
|
||||
|
||||
func ParsePortRangeOrDie(value string) *PortRange {
|
||||
pr, err := ParsePortRange(value)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("couldn't parse port range %q: %v", value, err))
|
||||
}
|
||||
return pr
|
||||
}
|
78
api/vendor/k8s.io/apimachinery/pkg/util/net/port_split.go
generated
vendored
Normal file
78
api/vendor/k8s.io/apimachinery/pkg/util/net/port_split.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
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 net
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
var validSchemes = sets.NewString("http", "https", "")
|
||||
|
||||
// SplitSchemeNamePort takes a string of the following forms:
|
||||
// - "<name>", returns "", "<name>","", true
|
||||
// - "<name>:<port>", returns "", "<name>","<port>",true
|
||||
// - "<scheme>:<name>:<port>", returns "<scheme>","<name>","<port>",true
|
||||
//
|
||||
// Name must be non-empty or valid will be returned false.
|
||||
// Scheme must be "http" or "https" if specified
|
||||
// Port is returned as a string, and it is not required to be numeric (could be
|
||||
// used for a named port, for example).
|
||||
func SplitSchemeNamePort(id string) (scheme, name, port string, valid bool) {
|
||||
parts := strings.Split(id, ":")
|
||||
switch len(parts) {
|
||||
case 1:
|
||||
name = parts[0]
|
||||
case 2:
|
||||
name = parts[0]
|
||||
port = parts[1]
|
||||
case 3:
|
||||
scheme = parts[0]
|
||||
name = parts[1]
|
||||
port = parts[2]
|
||||
default:
|
||||
return "", "", "", false
|
||||
}
|
||||
|
||||
if len(name) > 0 && validSchemes.Has(scheme) {
|
||||
return scheme, name, port, true
|
||||
} else {
|
||||
return "", "", "", false
|
||||
}
|
||||
}
|
||||
|
||||
// JoinSchemeNamePort returns a string that specifies the scheme, name, and port:
|
||||
// - "<name>"
|
||||
// - "<name>:<port>"
|
||||
// - "<scheme>:<name>:<port>"
|
||||
//
|
||||
// None of the parameters may contain a ':' character
|
||||
// Name is required
|
||||
// Scheme must be "", "http", or "https"
|
||||
func JoinSchemeNamePort(scheme, name, port string) string {
|
||||
if len(scheme) > 0 {
|
||||
// Must include three segments to specify scheme
|
||||
return scheme + ":" + name + ":" + port
|
||||
}
|
||||
if len(port) > 0 {
|
||||
// Must include two segments to specify port
|
||||
return name + ":" + port
|
||||
}
|
||||
// Return name alone
|
||||
return name
|
||||
}
|
63
api/vendor/k8s.io/apimachinery/pkg/util/net/util.go
generated
vendored
Normal file
63
api/vendor/k8s.io/apimachinery/pkg/util/net/util.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright 2016 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 net
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// IPNetEqual checks if the two input IPNets are representing the same subnet.
|
||||
// For example,
|
||||
//
|
||||
// 10.0.0.1/24 and 10.0.0.0/24 are the same subnet.
|
||||
// 10.0.0.1/24 and 10.0.0.0/25 are not the same subnet.
|
||||
func IPNetEqual(ipnet1, ipnet2 *net.IPNet) bool {
|
||||
if ipnet1 == nil || ipnet2 == nil {
|
||||
return false
|
||||
}
|
||||
if reflect.DeepEqual(ipnet1.Mask, ipnet2.Mask) && ipnet1.Contains(ipnet2.IP) && ipnet2.Contains(ipnet1.IP) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns if the given err is "connection reset by peer" error.
|
||||
func IsConnectionReset(err error) bool {
|
||||
var errno syscall.Errno
|
||||
if errors.As(err, &errno) {
|
||||
return errno == syscall.ECONNRESET
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns if the given err is "http2: client connection lost" error.
|
||||
func IsHTTP2ConnectionLost(err error) bool {
|
||||
return err != nil && strings.Contains(err.Error(), "http2: client connection lost")
|
||||
}
|
||||
|
||||
// Returns if the given err is "connection refused" error
|
||||
func IsConnectionRefused(err error) bool {
|
||||
var errno syscall.Errno
|
||||
if errors.As(err, &errno) {
|
||||
return errno == syscall.ECONNREFUSED
|
||||
}
|
||||
return false
|
||||
}
|
Reference in New Issue
Block a user