mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
vendor files
This commit is contained in:
51
vendor/k8s.io/apimachinery/pkg/util/net/BUILD
generated
vendored
Normal file
51
vendor/k8s.io/apimachinery/pkg/util/net/BUILD
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"http_test.go",
|
||||
"interface_test.go",
|
||||
"port_range_test.go",
|
||||
"port_split_test.go",
|
||||
"util_test.go",
|
||||
],
|
||||
importpath = "k8s.io/apimachinery/pkg/util/net",
|
||||
library = ":go_default_library",
|
||||
deps = ["//vendor/github.com/spf13/pflag:go_default_library"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"http.go",
|
||||
"interface.go",
|
||||
"port_range.go",
|
||||
"port_split.go",
|
||||
"util.go",
|
||||
],
|
||||
importpath = "k8s.io/apimachinery/pkg/util/net",
|
||||
deps = [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/net/http2:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
422
vendor/k8s.io/apimachinery/pkg/util/net/http.go
generated
vendored
Normal file
422
vendor/k8s.io/apimachinery/pkg/util/net/http.go
generated
vendored
Normal file
@ -0,0 +1,422 @@
|
||||
/*
|
||||
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"
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 uerr, ok := err.(*url.Error); ok {
|
||||
err = uerr.Err
|
||||
}
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
return true
|
||||
case err.Error() == "http: can't write HTTP request on broken connection":
|
||||
return true
|
||||
case strings.Contains(err.Error(), "connection reset by peer"):
|
||||
return true
|
||||
case strings.Contains(strings.ToLower(err.Error()), "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 t.Dial == nil {
|
||||
t.Dial = defaultTransport.Dial
|
||||
}
|
||||
if t.TLSHandshakeTimeout == 0 {
|
||||
t.TLSHandshakeTimeout = defaultTransport.TLSHandshakeTimeout
|
||||
}
|
||||
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 {
|
||||
glog.Infof("HTTP2 has been explicitly disabled")
|
||||
} else {
|
||||
if err := http2.ConfigureTransport(t); err != nil {
|
||||
glog.Warningf("Transport failed http2 configuration: %v", err)
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
type RoundTripperWrapper interface {
|
||||
http.RoundTripper
|
||||
WrappedRoundTripper() http.RoundTripper
|
||||
}
|
||||
|
||||
type DialFunc func(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:
|
||||
return transport.Dial, nil
|
||||
case RoundTripperWrapper:
|
||||
return DialerFor(transport.WrappedRoundTripper())
|
||||
default:
|
||||
return nil, fmt.Errorf("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 userAgent, ok := req.Header["User-Agent"]; ok {
|
||||
if len(userAgent) > 0 {
|
||||
return userAgent[0]
|
||||
}
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// SourceIPs splits the comma separated X-Forwarded-For header or returns the X-Real-Ip header or req.RemoteAddr,
|
||||
// in that order, ignoring invalid IPs. It returns nil if all of these are empty or invalid.
|
||||
func SourceIPs(req *http.Request) []net.IP {
|
||||
hdr := req.Header
|
||||
// First check the X-Forwarded-For header for requests via proxy.
|
||||
hdrForwardedFor := hdr.Get("X-Forwarded-For")
|
||||
forwardedForIPs := []net.IP{}
|
||||
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 := net.ParseIP(strings.TrimSpace(part))
|
||||
if ip != nil {
|
||||
forwardedForIPs = append(forwardedForIPs, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(forwardedForIPs) > 0 {
|
||||
return forwardedForIPs
|
||||
}
|
||||
|
||||
// Try the X-Real-Ip header.
|
||||
hdrRealIp := hdr.Get("X-Real-Ip")
|
||||
if hdrRealIp != "" {
|
||||
ip := net.ParseIP(hdrRealIp)
|
||||
if ip != nil {
|
||||
return []net.IP{ip}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to Remote Address in request, which will give the correct client IP when there is no proxy.
|
||||
// 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 {
|
||||
if remoteIP := net.ParseIP(host); remoteIP != nil {
|
||||
return []net.IP{remoteIP}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback if Remote Address was just IP.
|
||||
if remoteIP := net.ParseIP(req.RemoteAddr); remoteIP != nil {
|
||||
return []net.IP{remoteIP}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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, _ := net.ParseCIDR(noProxyRule)
|
||||
if cidr != nil {
|
||||
cidrs = append(cidrs, cidr)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cidrs) == 0 {
|
||||
return delegate
|
||||
}
|
||||
|
||||
return func(req *http.Request) (*url.URL, error) {
|
||||
ip := net.ParseIP(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)
|
||||
}
|
||||
|
||||
// ConnectWithRedirects uses dialer to send req, following up to 10 redirects (relative to
|
||||
// originalLocation). It returns the opened net.Conn and the raw response bytes.
|
||||
func ConnectWithRedirects(originalMethod string, originalLocation *url.URL, header http.Header, originalBody io.Reader, dialer Dialer) (net.Conn, []byte, error) {
|
||||
const (
|
||||
maxRedirects = 10
|
||||
maxResponseSize = 16384 // play it safe to allow the potential for lots of / large headers
|
||||
)
|
||||
|
||||
var (
|
||||
location = originalLocation
|
||||
method = originalMethod
|
||||
intermediateConn net.Conn
|
||||
rawResponse = bytes.NewBuffer(make([]byte, 0, 256))
|
||||
body = originalBody
|
||||
)
|
||||
|
||||
defer func() {
|
||||
if intermediateConn != nil {
|
||||
intermediateConn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
redirectLoop:
|
||||
for redirects := 0; ; redirects++ {
|
||||
if redirects > maxRedirects {
|
||||
return nil, nil, fmt.Errorf("too many redirects (%d)", redirects)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, location.String(), body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
req.Header = header
|
||||
|
||||
intermediateConn, err = dialer.Dial(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Peek at the backend response.
|
||||
rawResponse.Reset()
|
||||
respReader := bufio.NewReader(io.TeeReader(
|
||||
io.LimitReader(intermediateConn, maxResponseSize), // Don't read more than maxResponseSize bytes.
|
||||
rawResponse)) // Save the raw response.
|
||||
resp, err := http.ReadResponse(respReader, nil)
|
||||
if err != nil {
|
||||
// Unable to read the backend response; let the client handle it.
|
||||
glog.Warningf("Error reading backend response: %v", err)
|
||||
break redirectLoop
|
||||
}
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusFound:
|
||||
// Redirect, continue.
|
||||
default:
|
||||
// Don't redirect.
|
||||
break redirectLoop
|
||||
}
|
||||
|
||||
// Redirected requests switch to "GET" according to the HTTP spec:
|
||||
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3
|
||||
method = "GET"
|
||||
// don't send a body when following redirects
|
||||
body = nil
|
||||
|
||||
resp.Body.Close() // not used
|
||||
|
||||
// Reset the connection.
|
||||
intermediateConn.Close()
|
||||
intermediateConn = nil
|
||||
|
||||
// Prepare to follow the redirect.
|
||||
redirectStr := resp.Header.Get("Location")
|
||||
if redirectStr == "" {
|
||||
return nil, nil, fmt.Errorf("%d response missing Location header", resp.StatusCode)
|
||||
}
|
||||
// We have to parse relative to the current location, NOT originalLocation. For example,
|
||||
// if we request http://foo.com/a and get back "http://bar.com/b", the result should be
|
||||
// http://bar.com/b. If we then make that request and get back a redirect to "/c", the result
|
||||
// should be http://bar.com/c, not http://foo.com/c.
|
||||
location, err = location.Parse(redirectStr)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("malformed Location header: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
connToReturn := intermediateConn
|
||||
intermediateConn = nil // Don't close the connection when we return it.
|
||||
return connToReturn, rawResponse.Bytes(), nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
282
vendor/k8s.io/apimachinery/pkg/util/net/http_test.go
generated
vendored
Normal file
282
vendor/k8s.io/apimachinery/pkg/util/net/http_test.go
generated
vendored
Normal file
@ -0,0 +1,282 @@
|
||||
// +build go1.8
|
||||
|
||||
/*
|
||||
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 (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetClientIP(t *testing.T) {
|
||||
ipString := "10.0.0.1"
|
||||
ip := net.ParseIP(ipString)
|
||||
invalidIPString := "invalidIPString"
|
||||
testCases := []struct {
|
||||
Request http.Request
|
||||
ExpectedIP net.IP
|
||||
}{
|
||||
{
|
||||
Request: http.Request{},
|
||||
},
|
||||
{
|
||||
Request: http.Request{
|
||||
Header: map[string][]string{
|
||||
"X-Real-Ip": {ipString},
|
||||
},
|
||||
},
|
||||
ExpectedIP: ip,
|
||||
},
|
||||
{
|
||||
Request: http.Request{
|
||||
Header: map[string][]string{
|
||||
"X-Real-Ip": {invalidIPString},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Request: http.Request{
|
||||
Header: map[string][]string{
|
||||
"X-Forwarded-For": {ipString},
|
||||
},
|
||||
},
|
||||
ExpectedIP: ip,
|
||||
},
|
||||
{
|
||||
Request: http.Request{
|
||||
Header: map[string][]string{
|
||||
"X-Forwarded-For": {invalidIPString},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Request: http.Request{
|
||||
Header: map[string][]string{
|
||||
"X-Forwarded-For": {invalidIPString + "," + ipString},
|
||||
},
|
||||
},
|
||||
ExpectedIP: ip,
|
||||
},
|
||||
{
|
||||
Request: http.Request{
|
||||
// RemoteAddr is in the form host:port
|
||||
RemoteAddr: ipString + ":1234",
|
||||
},
|
||||
ExpectedIP: ip,
|
||||
},
|
||||
{
|
||||
Request: http.Request{
|
||||
RemoteAddr: invalidIPString,
|
||||
},
|
||||
},
|
||||
{
|
||||
Request: http.Request{
|
||||
Header: map[string][]string{
|
||||
"X-Forwarded-For": {invalidIPString},
|
||||
},
|
||||
// RemoteAddr is in the form host:port
|
||||
RemoteAddr: ipString,
|
||||
},
|
||||
ExpectedIP: ip,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range testCases {
|
||||
if a, e := GetClientIP(&test.Request), test.ExpectedIP; reflect.DeepEqual(e, a) != true {
|
||||
t.Fatalf("test case %d failed. expected: %v, actual: %v", i, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendForwardedForHeader(t *testing.T) {
|
||||
testCases := []struct {
|
||||
addr, forwarded, expected string
|
||||
}{
|
||||
{"1.2.3.4:8000", "", "1.2.3.4"},
|
||||
{"1.2.3.4:8000", "8.8.8.8", "8.8.8.8, 1.2.3.4"},
|
||||
{"1.2.3.4:8000", "8.8.8.8, 1.2.3.4", "8.8.8.8, 1.2.3.4, 1.2.3.4"},
|
||||
{"1.2.3.4:8000", "foo,bar", "foo,bar, 1.2.3.4"},
|
||||
}
|
||||
for i, test := range testCases {
|
||||
req := &http.Request{
|
||||
RemoteAddr: test.addr,
|
||||
Header: make(http.Header),
|
||||
}
|
||||
if test.forwarded != "" {
|
||||
req.Header.Set("X-Forwarded-For", test.forwarded)
|
||||
}
|
||||
|
||||
AppendForwardedForHeader(req)
|
||||
actual := req.Header.Get("X-Forwarded-For")
|
||||
if actual != test.expected {
|
||||
t.Errorf("[%d] Expected %q, Got %q", i, test.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxierWithNoProxyCIDR(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
noProxy string
|
||||
url string
|
||||
|
||||
expectedDelegated bool
|
||||
}{
|
||||
{
|
||||
name: "no env",
|
||||
url: "https://192.168.143.1/api",
|
||||
expectedDelegated: true,
|
||||
},
|
||||
{
|
||||
name: "no cidr",
|
||||
noProxy: "192.168.63.1",
|
||||
url: "https://192.168.143.1/api",
|
||||
expectedDelegated: true,
|
||||
},
|
||||
{
|
||||
name: "hostname",
|
||||
noProxy: "192.168.63.0/24,192.168.143.0/24",
|
||||
url: "https://my-hostname/api",
|
||||
expectedDelegated: true,
|
||||
},
|
||||
{
|
||||
name: "match second cidr",
|
||||
noProxy: "192.168.63.0/24,192.168.143.0/24",
|
||||
url: "https://192.168.143.1/api",
|
||||
expectedDelegated: false,
|
||||
},
|
||||
{
|
||||
name: "match second cidr with host:port",
|
||||
noProxy: "192.168.63.0/24,192.168.143.0/24",
|
||||
url: "https://192.168.143.1:8443/api",
|
||||
expectedDelegated: false,
|
||||
},
|
||||
{
|
||||
name: "IPv6 cidr",
|
||||
noProxy: "2001:db8::/48",
|
||||
url: "https://[2001:db8::1]/api",
|
||||
expectedDelegated: false,
|
||||
},
|
||||
{
|
||||
name: "IPv6+port cidr",
|
||||
noProxy: "2001:db8::/48",
|
||||
url: "https://[2001:db8::1]:8443/api",
|
||||
expectedDelegated: false,
|
||||
},
|
||||
{
|
||||
name: "IPv6, not matching cidr",
|
||||
noProxy: "2001:db8::/48",
|
||||
url: "https://[2001:db8:1::1]/api",
|
||||
expectedDelegated: true,
|
||||
},
|
||||
{
|
||||
name: "IPv6+port, not matching cidr",
|
||||
noProxy: "2001:db8::/48",
|
||||
url: "https://[2001:db8:1::1]:8443/api",
|
||||
expectedDelegated: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
os.Setenv("NO_PROXY", test.noProxy)
|
||||
actualDelegated := false
|
||||
proxyFunc := NewProxierWithNoProxyCIDR(func(req *http.Request) (*url.URL, error) {
|
||||
actualDelegated = true
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
req, err := http.NewRequest("GET", test.url, nil)
|
||||
if err != nil {
|
||||
t.Errorf("%s: unexpected err: %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
if _, err := proxyFunc(req); err != nil {
|
||||
t.Errorf("%s: unexpected err: %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if test.expectedDelegated != actualDelegated {
|
||||
t.Errorf("%s: expected %v, got %v", test.name, test.expectedDelegated, actualDelegated)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type fakeTLSClientConfigHolder struct {
|
||||
called bool
|
||||
}
|
||||
|
||||
func (f *fakeTLSClientConfigHolder) TLSClientConfig() *tls.Config {
|
||||
f.called = true
|
||||
return nil
|
||||
}
|
||||
func (f *fakeTLSClientConfigHolder) RoundTrip(*http.Request) (*http.Response, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestTLSClientConfigHolder(t *testing.T) {
|
||||
rt := &fakeTLSClientConfigHolder{}
|
||||
TLSClientConfig(rt)
|
||||
|
||||
if !rt.called {
|
||||
t.Errorf("didn't find tls config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinPreservingTrailingSlash(t *testing.T) {
|
||||
tests := []struct {
|
||||
a string
|
||||
b string
|
||||
want string
|
||||
}{
|
||||
// All empty
|
||||
{"", "", ""},
|
||||
|
||||
// Empty a
|
||||
{"", "/", "/"},
|
||||
{"", "foo", "foo"},
|
||||
{"", "/foo", "/foo"},
|
||||
{"", "/foo/", "/foo/"},
|
||||
|
||||
// Empty b
|
||||
{"/", "", "/"},
|
||||
{"foo", "", "foo"},
|
||||
{"/foo", "", "/foo"},
|
||||
{"/foo/", "", "/foo/"},
|
||||
|
||||
// Both populated
|
||||
{"/", "/", "/"},
|
||||
{"foo", "foo", "foo/foo"},
|
||||
{"/foo", "/foo", "/foo/foo"},
|
||||
{"/foo/", "/foo/", "/foo/foo/"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
name := fmt.Sprintf("%q+%q=%q", tt.a, tt.b, tt.want)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if got := JoinPreservingTrailingSlash(tt.a, tt.b); got != tt.want {
|
||||
t.Errorf("JoinPreservingTrailingSlash() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
392
vendor/k8s.io/apimachinery/pkg/util/net/interface.go
generated
vendored
Normal file
392
vendor/k8s.io/apimachinery/pkg/util/net/interface.go
generated
vendored
Normal file
@ -0,0 +1,392 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type AddressFamily uint
|
||||
|
||||
const (
|
||||
familyIPv4 AddressFamily = 4
|
||||
familyIPv6 AddressFamily = 6
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
glog.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 {
|
||||
glog.V(4).Infof("Checking addr %s.", addrs[i].String())
|
||||
ip, _, err := net.ParseCIDR(addrs[i].String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if memberOf(ip, family) {
|
||||
if ip.IsGlobalUnicast() {
|
||||
glog.V(4).Infof("IP found %v", ip)
|
||||
return ip, nil
|
||||
} else {
|
||||
glog.V(4).Infof("Non-global unicast address found %v", ip)
|
||||
}
|
||||
} else {
|
||||
glog.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
|
||||
}
|
||||
glog.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 {
|
||||
glog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intfName)
|
||||
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.
|
||||
// Searches for IPv4 addresses, and then IPv6 addresses.
|
||||
func chooseIPFromHostInterfaces(nw networkInterfacer) (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 []AddressFamily{familyIPv4, familyIPv6} {
|
||||
glog.V(4).Infof("Looking for system interface with a global IPv%d address", uint(family))
|
||||
for _, intf := range intfs {
|
||||
if !isInterfaceUp(&intf) {
|
||||
glog.V(4).Infof("Skipping: down interface %q", intf.Name)
|
||||
continue
|
||||
}
|
||||
if isLoopbackOrPointToPoint(&intf) {
|
||||
glog.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 {
|
||||
glog.V(4).Infof("Skipping: no addresses on interface %q", intf.Name)
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
ip, _, err := net.ParseCIDR(addr.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to parse CIDR for interface %q: %s", intf.Name, err)
|
||||
}
|
||||
if !memberOf(ip, family) {
|
||||
glog.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() {
|
||||
glog.V(4).Infof("Skipping: non-global address %q on interface %q.", ip, intf.Name)
|
||||
continue
|
||||
}
|
||||
glog.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) {
|
||||
var nw networkInterfacer = networkInterface{}
|
||||
if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
|
||||
return chooseIPFromHostInterfaces(nw)
|
||||
}
|
||||
routes, err := getAllDefaultRoutes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return chooseHostInterfaceFromRoute(routes, nw)
|
||||
}
|
||||
|
||||
// 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, fmt.Errorf("No default routes.")
|
||||
}
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
// chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
|
||||
// global IP address from the interface for the route. Will first look all each IPv4 route for
|
||||
// an IPv4 IP, and then will look at each IPv6 route for an IPv6 IP.
|
||||
func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer) (net.IP, error) {
|
||||
for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
|
||||
glog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
|
||||
for _, route := range routes {
|
||||
if route.Family != family {
|
||||
continue
|
||||
}
|
||||
glog.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 {
|
||||
glog.V(4).Infof("Found active IP %v ", finalIP)
|
||||
return finalIP, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
glog.V(4).Infof("No active IP found by looking at default routes")
|
||||
return nil, fmt.Errorf("unable to select an IP from default routes.")
|
||||
}
|
||||
|
||||
// If bind-address is usable, return it directly
|
||||
// If bind-address is not usable (unset, 0.0.0.0, or loopback), we will use the host's default
|
||||
// interface.
|
||||
func ChooseBindAddress(bindAddress net.IP) (net.IP, error) {
|
||||
if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
|
||||
hostIP, err := ChooseHostInterface()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bindAddress = hostIP
|
||||
}
|
||||
return bindAddress, nil
|
||||
}
|
725
vendor/k8s.io/apimachinery/pkg/util/net/interface_test.go
generated
vendored
Normal file
725
vendor/k8s.io/apimachinery/pkg/util/net/interface_test.go
generated
vendored
Normal file
@ -0,0 +1,725 @@
|
||||
/*
|
||||
Copyright 2014 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"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const gatewayfirst = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
||||
eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
|
||||
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
|
||||
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
||||
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
||||
`
|
||||
const gatewaylast = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
||||
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
||||
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
||||
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
|
||||
eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
|
||||
`
|
||||
const gatewaymiddle = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
||||
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
|
||||
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
||||
eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
|
||||
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
||||
`
|
||||
const noInternetConnection = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
||||
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
||||
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
||||
`
|
||||
const nothing = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
||||
`
|
||||
const badDestination = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
||||
eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
|
||||
eth3 0000FE0AA1 00000000 0001 0 0 0 0080FFFF 0 0 0
|
||||
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
||||
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
||||
`
|
||||
const badGateway = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
||||
eth3 00000000 0100FE0AA1 0003 0 0 1024 00000000 0 0 0
|
||||
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
|
||||
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
||||
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
||||
`
|
||||
const route_Invalidhex = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
||||
eth3 00000000 0100FE0AA 0003 0 0 1024 00000000 0 0 0
|
||||
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
|
||||
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
|
||||
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
|
||||
`
|
||||
|
||||
const v6gatewayfirst = `00000000000000000000000000000000 00 00000000000000000000000000000000 00 20010001000000000000000000000001 00000064 00000000 00000000 00000003 eth3
|
||||
20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
|
||||
00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
|
||||
`
|
||||
const v6gatewaylast = `20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
|
||||
00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
|
||||
00000000000000000000000000000000 00 00000000000000000000000000000000 00 20010001000000000000000000000001 00000064 00000000 00000000 00000003 eth3
|
||||
`
|
||||
const v6gatewaymiddle = `20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
|
||||
00000000000000000000000000000000 00 00000000000000000000000000000000 00 20010001000000000000000000000001 00000064 00000000 00000000 00000003 eth3
|
||||
00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
|
||||
`
|
||||
const v6noDefaultRoutes = `00000000000000000000000000000000 60 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
|
||||
20010001000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00000001 docker0
|
||||
20010002000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
|
||||
fe800000000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001 eth3
|
||||
`
|
||||
const v6nothing = ``
|
||||
const v6badDestination = `2001000200000000 7a 00000000000000000000000000000000 00 00000000000000000000000000000000 00000400 00000000 00000000 00200200 lo
|
||||
`
|
||||
const v6badGateway = `00000000000000000000000000000000 00 00000000000000000000000000000000 00 200100010000000000000000000000000012 00000064 00000000 00000000 00000003 eth3
|
||||
`
|
||||
const v6route_Invalidhex = `000000000000000000000000000000000 00 00000000000000000000000000000000 00 fe80000000000000021fcafffea0ec00 00000064 00000000 00000000 00000003 enp1s0f0
|
||||
|
||||
`
|
||||
|
||||
const (
|
||||
flagUp = net.FlagUp | net.FlagBroadcast | net.FlagMulticast
|
||||
flagDown = net.FlagBroadcast | net.FlagMulticast
|
||||
flagLoopback = net.FlagUp | net.FlagLoopback
|
||||
flagP2P = net.FlagUp | net.FlagPointToPoint
|
||||
)
|
||||
|
||||
func makeIntf(index int, name string, flags net.Flags) net.Interface {
|
||||
mac := net.HardwareAddr{0, 0x32, 0x7d, 0x69, 0xf7, byte(0x30 + index)}
|
||||
return net.Interface{
|
||||
Index: index,
|
||||
MTU: 1500,
|
||||
Name: name,
|
||||
HardwareAddr: mac,
|
||||
Flags: flags}
|
||||
}
|
||||
|
||||
var (
|
||||
downIntf = makeIntf(1, "eth3", flagDown)
|
||||
loopbackIntf = makeIntf(1, "lo", flagLoopback)
|
||||
p2pIntf = makeIntf(1, "lo", flagP2P)
|
||||
upIntf = makeIntf(1, "eth3", flagUp)
|
||||
)
|
||||
|
||||
var (
|
||||
ipv4Route = Route{Interface: "eth3", Destination: net.ParseIP("0.0.0.0"), Gateway: net.ParseIP("10.254.0.1"), Family: familyIPv4}
|
||||
ipv6Route = Route{Interface: "eth3", Destination: net.ParseIP("::"), Gateway: net.ParseIP("2001:1::1"), Family: familyIPv6}
|
||||
)
|
||||
|
||||
var (
|
||||
noRoutes = []Route{}
|
||||
routeV4 = []Route{ipv4Route}
|
||||
routeV6 = []Route{ipv6Route}
|
||||
bothRoutes = []Route{ipv4Route, ipv6Route}
|
||||
)
|
||||
|
||||
func TestGetIPv4Routes(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tcase string
|
||||
route string
|
||||
count int
|
||||
expected *Route
|
||||
errStrFrag string
|
||||
}{
|
||||
{"gatewayfirst", gatewayfirst, 1, &ipv4Route, ""},
|
||||
{"gatewaymiddle", gatewaymiddle, 1, &ipv4Route, ""},
|
||||
{"gatewaylast", gatewaylast, 1, &ipv4Route, ""},
|
||||
{"no routes", nothing, 0, nil, ""},
|
||||
{"badDestination", badDestination, 0, nil, "invalid IPv4"},
|
||||
{"badGateway", badGateway, 0, nil, "invalid IPv4"},
|
||||
{"route_Invalidhex", route_Invalidhex, 0, nil, "odd length hex string"},
|
||||
{"no default routes", noInternetConnection, 0, nil, ""},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
r := strings.NewReader(tc.route)
|
||||
routes, err := getIPv4DefaultRoutes(r)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), tc.errStrFrag) {
|
||||
t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
|
||||
}
|
||||
} else if tc.errStrFrag != "" {
|
||||
t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
|
||||
} else {
|
||||
if tc.count != len(routes) {
|
||||
t.Errorf("case[%s]: expected %d routes, have %v", tc.tcase, tc.count, routes)
|
||||
} else if tc.count == 1 {
|
||||
if !tc.expected.Gateway.Equal(routes[0].Gateway) {
|
||||
t.Errorf("case[%s]: expected %v, got %v .err : %v", tc.tcase, tc.expected, routes, err)
|
||||
}
|
||||
if !routes[0].Destination.Equal(net.IPv4zero) {
|
||||
t.Errorf("case[%s}: destination is not for default route (not zero)", tc.tcase)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIPv6Routes(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tcase string
|
||||
route string
|
||||
count int
|
||||
expected *Route
|
||||
errStrFrag string
|
||||
}{
|
||||
{"v6 gatewayfirst", v6gatewayfirst, 1, &ipv6Route, ""},
|
||||
{"v6 gatewaymiddle", v6gatewaymiddle, 1, &ipv6Route, ""},
|
||||
{"v6 gatewaylast", v6gatewaylast, 1, &ipv6Route, ""},
|
||||
{"v6 no routes", v6nothing, 0, nil, ""},
|
||||
{"v6 badDestination", v6badDestination, 0, nil, "invalid IPv6"},
|
||||
{"v6 badGateway", v6badGateway, 0, nil, "invalid IPv6"},
|
||||
{"v6 route_Invalidhex", v6route_Invalidhex, 0, nil, "odd length hex string"},
|
||||
{"v6 no default routes", v6noDefaultRoutes, 0, nil, ""},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
r := strings.NewReader(tc.route)
|
||||
routes, err := getIPv6DefaultRoutes(r)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), tc.errStrFrag) {
|
||||
t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
|
||||
}
|
||||
} else if tc.errStrFrag != "" {
|
||||
t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
|
||||
} else {
|
||||
if tc.count != len(routes) {
|
||||
t.Errorf("case[%s]: expected %d routes, have %v", tc.tcase, tc.count, routes)
|
||||
} else if tc.count == 1 {
|
||||
if !tc.expected.Gateway.Equal(routes[0].Gateway) {
|
||||
t.Errorf("case[%s]: expected %v, got %v .err : %v", tc.tcase, tc.expected, routes, err)
|
||||
}
|
||||
if !routes[0].Destination.Equal(net.IPv6zero) {
|
||||
t.Errorf("case[%s}: destination is not for default route (not zero)", tc.tcase)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseIP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tcase string
|
||||
ip string
|
||||
family AddressFamily
|
||||
success bool
|
||||
expected net.IP
|
||||
}{
|
||||
{"empty", "", familyIPv4, false, nil},
|
||||
{"too short", "AA", familyIPv4, false, nil},
|
||||
{"too long", "0011223344", familyIPv4, false, nil},
|
||||
{"invalid", "invalid!", familyIPv4, false, nil},
|
||||
{"zero", "00000000", familyIPv4, true, net.IP{0, 0, 0, 0}},
|
||||
{"ffff", "FFFFFFFF", familyIPv4, true, net.IP{0xff, 0xff, 0xff, 0xff}},
|
||||
{"valid v4", "12345678", familyIPv4, true, net.IP{120, 86, 52, 18}},
|
||||
{"valid v6", "fe800000000000000000000000000000", familyIPv6, true, net.IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"v6 too short", "fe80000000000000021fcafffea0ec0", familyIPv6, false, nil},
|
||||
{"v6 too long", "fe80000000000000021fcafffea0ec002", familyIPv6, false, nil},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
ip, err := parseIP(tc.ip, tc.family)
|
||||
if !ip.Equal(tc.expected) {
|
||||
t.Errorf("case[%v]: expected %q, got %q . err : %v", tc.tcase, tc.expected, ip, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsInterfaceUp(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tcase string
|
||||
intf *net.Interface
|
||||
expected bool
|
||||
}{
|
||||
{"up", &net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}, true},
|
||||
{"down", &net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: 0}, false},
|
||||
{"no interface", nil, false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
it := isInterfaceUp(tc.intf)
|
||||
if it != tc.expected {
|
||||
t.Errorf("case[%v]: expected %v, got %v .", tc.tcase, tc.expected, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type addrStruct struct{ val string }
|
||||
|
||||
func (a addrStruct) Network() string {
|
||||
return a.val
|
||||
}
|
||||
func (a addrStruct) String() string {
|
||||
return a.val
|
||||
}
|
||||
|
||||
func TestFinalIP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tcase string
|
||||
addr []net.Addr
|
||||
family AddressFamily
|
||||
expected net.IP
|
||||
}{
|
||||
{"no ipv4", []net.Addr{addrStruct{val: "2001::5/64"}}, familyIPv4, nil},
|
||||
{"no ipv6", []net.Addr{addrStruct{val: "10.128.0.4/32"}}, familyIPv6, nil},
|
||||
{"invalidV4CIDR", []net.Addr{addrStruct{val: "10.20.30.40.50/24"}}, familyIPv4, nil},
|
||||
{"invalidV6CIDR", []net.Addr{addrStruct{val: "fe80::2f7:67fff:fe6e:2956/64"}}, familyIPv6, nil},
|
||||
{"loopback", []net.Addr{addrStruct{val: "127.0.0.1/24"}}, familyIPv4, nil},
|
||||
{"loopbackv6", []net.Addr{addrStruct{val: "::1/128"}}, familyIPv6, nil},
|
||||
{"link local v4", []net.Addr{addrStruct{val: "169.254.1.10/16"}}, familyIPv4, nil},
|
||||
{"link local v6", []net.Addr{addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}}, familyIPv6, nil},
|
||||
{"ip4", []net.Addr{addrStruct{val: "10.254.12.132/17"}}, familyIPv4, net.ParseIP("10.254.12.132")},
|
||||
{"ip6", []net.Addr{addrStruct{val: "2001::5/64"}}, familyIPv6, net.ParseIP("2001::5")},
|
||||
|
||||
{"no addresses", []net.Addr{}, familyIPv4, nil},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
ip, err := getMatchingGlobalIP(tc.addr, tc.family)
|
||||
if !ip.Equal(tc.expected) {
|
||||
t.Errorf("case[%v]: expected %v, got %v .err : %v", tc.tcase, tc.expected, ip, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddrs(t *testing.T) {
|
||||
var nw networkInterfacer = validNetworkInterface{}
|
||||
intf := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: 0}
|
||||
addrs, err := nw.Addrs(&intf)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error got : %v", err)
|
||||
}
|
||||
if len(addrs) != 2 {
|
||||
t.Errorf("expected addrs: 2 got null")
|
||||
}
|
||||
}
|
||||
|
||||
// Has a valid IPv4 address (IPv6 is LLA)
|
||||
type validNetworkInterface struct {
|
||||
}
|
||||
|
||||
func (_ validNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||
return &upIntf, nil
|
||||
}
|
||||
func (_ validNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||
var ifat []net.Addr
|
||||
ifat = []net.Addr{
|
||||
addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}, addrStruct{val: "10.254.71.145/17"}}
|
||||
return ifat, nil
|
||||
}
|
||||
func (_ validNetworkInterface) Interfaces() ([]net.Interface, error) {
|
||||
return []net.Interface{upIntf}, nil
|
||||
}
|
||||
|
||||
// Both IPv4 and IPv6 addresses (expecting IPv4 to be used)
|
||||
type v4v6NetworkInterface struct {
|
||||
}
|
||||
|
||||
func (_ v4v6NetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||
return &upIntf, nil
|
||||
}
|
||||
func (_ v4v6NetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||
var ifat []net.Addr
|
||||
ifat = []net.Addr{
|
||||
addrStruct{val: "2001::10/64"}, addrStruct{val: "10.254.71.145/17"}}
|
||||
return ifat, nil
|
||||
}
|
||||
func (_ v4v6NetworkInterface) Interfaces() ([]net.Interface, error) {
|
||||
return []net.Interface{upIntf}, nil
|
||||
}
|
||||
|
||||
// Interface with only IPv6 address
|
||||
type ipv6NetworkInterface struct {
|
||||
}
|
||||
|
||||
func (_ ipv6NetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||
return &upIntf, nil
|
||||
}
|
||||
func (_ ipv6NetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||
var ifat []net.Addr
|
||||
ifat = []net.Addr{addrStruct{val: "2001::200/64"}}
|
||||
return ifat, nil
|
||||
}
|
||||
|
||||
func (_ ipv6NetworkInterface) Interfaces() ([]net.Interface, error) {
|
||||
return []net.Interface{upIntf}, nil
|
||||
}
|
||||
|
||||
// Only with link local addresses
|
||||
type networkInterfaceWithOnlyLinkLocals struct {
|
||||
}
|
||||
|
||||
func (_ networkInterfaceWithOnlyLinkLocals) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||
return &upIntf, nil
|
||||
}
|
||||
func (_ networkInterfaceWithOnlyLinkLocals) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||
var ifat []net.Addr
|
||||
ifat = []net.Addr{addrStruct{val: "169.254.162.166/16"}, addrStruct{val: "fe80::200/10"}}
|
||||
return ifat, nil
|
||||
}
|
||||
func (_ networkInterfaceWithOnlyLinkLocals) Interfaces() ([]net.Interface, error) {
|
||||
return []net.Interface{upIntf}, nil
|
||||
}
|
||||
|
||||
// Unable to get interface(s)
|
||||
type failGettingNetworkInterface struct {
|
||||
}
|
||||
|
||||
func (_ failGettingNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||
return nil, fmt.Errorf("unable get Interface")
|
||||
}
|
||||
func (_ failGettingNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (_ failGettingNetworkInterface) Interfaces() ([]net.Interface, error) {
|
||||
return nil, fmt.Errorf("mock failed getting all interfaces")
|
||||
}
|
||||
|
||||
// No interfaces
|
||||
type noNetworkInterface struct {
|
||||
}
|
||||
|
||||
func (_ noNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||
return nil, fmt.Errorf("no such network interface")
|
||||
}
|
||||
func (_ noNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (_ noNetworkInterface) Interfaces() ([]net.Interface, error) {
|
||||
return []net.Interface{}, nil
|
||||
}
|
||||
|
||||
// Interface is down
|
||||
type downNetworkInterface struct {
|
||||
}
|
||||
|
||||
func (_ downNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||
return &downIntf, nil
|
||||
}
|
||||
func (_ downNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||
var ifat []net.Addr
|
||||
ifat = []net.Addr{
|
||||
addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}, addrStruct{val: "10.254.71.145/17"}}
|
||||
return ifat, nil
|
||||
}
|
||||
func (_ downNetworkInterface) Interfaces() ([]net.Interface, error) {
|
||||
return []net.Interface{downIntf}, nil
|
||||
}
|
||||
|
||||
// Loopback interface
|
||||
type loopbackNetworkInterface struct {
|
||||
}
|
||||
|
||||
func (_ loopbackNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||
return &loopbackIntf, nil
|
||||
}
|
||||
func (_ loopbackNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||
var ifat []net.Addr
|
||||
ifat = []net.Addr{
|
||||
addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"}}
|
||||
return ifat, nil
|
||||
}
|
||||
func (_ loopbackNetworkInterface) Interfaces() ([]net.Interface, error) {
|
||||
return []net.Interface{loopbackIntf}, nil
|
||||
}
|
||||
|
||||
// Point to point interface
|
||||
type p2pNetworkInterface struct {
|
||||
}
|
||||
|
||||
func (_ p2pNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||
return &p2pIntf, nil
|
||||
}
|
||||
func (_ p2pNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||
var ifat []net.Addr
|
||||
ifat = []net.Addr{
|
||||
addrStruct{val: "::1/128"}, addrStruct{val: "127.0.0.1/8"}}
|
||||
return ifat, nil
|
||||
}
|
||||
func (_ p2pNetworkInterface) Interfaces() ([]net.Interface, error) {
|
||||
return []net.Interface{p2pIntf}, nil
|
||||
}
|
||||
|
||||
// Unable to get IP addresses for interface
|
||||
type networkInterfaceFailGetAddrs struct {
|
||||
}
|
||||
|
||||
func (_ networkInterfaceFailGetAddrs) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||
return &upIntf, nil
|
||||
}
|
||||
func (_ networkInterfaceFailGetAddrs) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||
return nil, fmt.Errorf("unable to get Addrs")
|
||||
}
|
||||
func (_ networkInterfaceFailGetAddrs) Interfaces() ([]net.Interface, error) {
|
||||
return []net.Interface{upIntf}, nil
|
||||
}
|
||||
|
||||
// No addresses for interface
|
||||
type networkInterfaceWithNoAddrs struct {
|
||||
}
|
||||
|
||||
func (_ networkInterfaceWithNoAddrs) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||
return &upIntf, nil
|
||||
}
|
||||
func (_ networkInterfaceWithNoAddrs) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||
ifat := []net.Addr{}
|
||||
return ifat, nil
|
||||
}
|
||||
func (_ networkInterfaceWithNoAddrs) Interfaces() ([]net.Interface, error) {
|
||||
return []net.Interface{upIntf}, nil
|
||||
}
|
||||
|
||||
// Invalid addresses for interface
|
||||
type networkInterfaceWithInvalidAddr struct {
|
||||
}
|
||||
|
||||
func (_ networkInterfaceWithInvalidAddr) InterfaceByName(intfName string) (*net.Interface, error) {
|
||||
return &upIntf, nil
|
||||
}
|
||||
func (_ networkInterfaceWithInvalidAddr) Addrs(intf *net.Interface) ([]net.Addr, error) {
|
||||
var ifat []net.Addr
|
||||
ifat = []net.Addr{addrStruct{val: "10.20.30.40.50/24"}}
|
||||
return ifat, nil
|
||||
}
|
||||
func (_ networkInterfaceWithInvalidAddr) Interfaces() ([]net.Interface, error) {
|
||||
return []net.Interface{upIntf}, nil
|
||||
}
|
||||
|
||||
func TestGetIPFromInterface(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tcase string
|
||||
nwname string
|
||||
family AddressFamily
|
||||
nw networkInterfacer
|
||||
expected net.IP
|
||||
errStrFrag string
|
||||
}{
|
||||
{"ipv4", "eth3", familyIPv4, validNetworkInterface{}, net.ParseIP("10.254.71.145"), ""},
|
||||
{"ipv6", "eth3", familyIPv6, ipv6NetworkInterface{}, net.ParseIP("2001::200"), ""},
|
||||
{"no ipv4", "eth3", familyIPv4, ipv6NetworkInterface{}, nil, ""},
|
||||
{"no ipv6", "eth3", familyIPv6, validNetworkInterface{}, nil, ""},
|
||||
{"I/F down", "eth3", familyIPv4, downNetworkInterface{}, nil, ""},
|
||||
{"I/F get fail", "eth3", familyIPv4, noNetworkInterface{}, nil, "no such network interface"},
|
||||
{"fail get addr", "eth3", familyIPv4, networkInterfaceFailGetAddrs{}, nil, "unable to get Addrs"},
|
||||
{"bad addr", "eth3", familyIPv4, networkInterfaceWithInvalidAddr{}, nil, "invalid CIDR"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
ip, err := getIPFromInterface(tc.nwname, tc.family, tc.nw)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), tc.errStrFrag) {
|
||||
t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
|
||||
}
|
||||
} else if tc.errStrFrag != "" {
|
||||
t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
|
||||
} else if !ip.Equal(tc.expected) {
|
||||
t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChooseHostInterfaceFromRoute(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tcase string
|
||||
routes []Route
|
||||
nw networkInterfacer
|
||||
expected net.IP
|
||||
}{
|
||||
{"ipv4", routeV4, validNetworkInterface{}, net.ParseIP("10.254.71.145")},
|
||||
{"ipv6", routeV6, ipv6NetworkInterface{}, net.ParseIP("2001::200")},
|
||||
{"prefer ipv4", bothRoutes, v4v6NetworkInterface{}, net.ParseIP("10.254.71.145")},
|
||||
{"all LLA", routeV4, networkInterfaceWithOnlyLinkLocals{}, nil},
|
||||
{"no routes", noRoutes, validNetworkInterface{}, nil},
|
||||
{"fail get IP", routeV4, networkInterfaceFailGetAddrs{}, nil},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
ip, err := chooseHostInterfaceFromRoute(tc.routes, tc.nw)
|
||||
if !ip.Equal(tc.expected) {
|
||||
t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemberOf(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tcase string
|
||||
ip net.IP
|
||||
family AddressFamily
|
||||
expected bool
|
||||
}{
|
||||
{"ipv4 is 4", net.ParseIP("10.20.30.40"), familyIPv4, true},
|
||||
{"ipv4 is 6", net.ParseIP("10.10.10.10"), familyIPv6, false},
|
||||
{"ipv6 is 4", net.ParseIP("2001::100"), familyIPv4, false},
|
||||
{"ipv6 is 6", net.ParseIP("2001::100"), familyIPv6, true},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
if memberOf(tc.ip, tc.family) != tc.expected {
|
||||
t.Errorf("case[%s]: expected %+v", tc.tcase, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIPFromHostInterfaces(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tcase string
|
||||
nw networkInterfacer
|
||||
expected net.IP
|
||||
errStrFrag string
|
||||
}{
|
||||
{"fail get I/Fs", failGettingNetworkInterface{}, nil, "failed getting all interfaces"},
|
||||
{"no interfaces", noNetworkInterface{}, nil, "no interfaces"},
|
||||
{"I/F not up", downNetworkInterface{}, nil, "no acceptable"},
|
||||
{"loopback only", loopbackNetworkInterface{}, nil, "no acceptable"},
|
||||
{"P2P I/F only", p2pNetworkInterface{}, nil, "no acceptable"},
|
||||
{"fail get addrs", networkInterfaceFailGetAddrs{}, nil, "unable to get Addrs"},
|
||||
{"no addresses", networkInterfaceWithNoAddrs{}, nil, "no acceptable"},
|
||||
{"invalid addr", networkInterfaceWithInvalidAddr{}, nil, "invalid CIDR"},
|
||||
{"no matches", networkInterfaceWithOnlyLinkLocals{}, nil, "no acceptable"},
|
||||
{"ipv4", validNetworkInterface{}, net.ParseIP("10.254.71.145"), ""},
|
||||
{"ipv6", ipv6NetworkInterface{}, net.ParseIP("2001::200"), ""},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
ip, err := chooseIPFromHostInterfaces(tc.nw)
|
||||
if !ip.Equal(tc.expected) {
|
||||
t.Errorf("case[%s]: expected %+v, got %+v with err : %v", tc.tcase, tc.expected, ip, err)
|
||||
}
|
||||
if err != nil && !strings.Contains(err.Error(), tc.errStrFrag) {
|
||||
t.Errorf("case[%s]: unable to find %q in error string %q", tc.tcase, tc.errStrFrag, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeRouteFile(content string, t *testing.T) (*os.File, error) {
|
||||
routeFile, err := ioutil.TempFile("", "route")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := routeFile.Write([]byte(content)); err != nil {
|
||||
return routeFile, err
|
||||
}
|
||||
err = routeFile.Close()
|
||||
return routeFile, err
|
||||
}
|
||||
|
||||
func TestFailGettingIPv4Routes(t *testing.T) {
|
||||
defer func() { v4File.name = ipv4RouteFile }()
|
||||
|
||||
// Try failure to open file (should not occur, as caller ensures we have IPv4 route file, but being thorough)
|
||||
v4File.name = "no-such-file"
|
||||
errStrFrag := "no such file"
|
||||
_, err := v4File.extract()
|
||||
if err == nil {
|
||||
fmt.Errorf("Expected error trying to read non-existent v4 route file")
|
||||
}
|
||||
if !strings.Contains(err.Error(), errStrFrag) {
|
||||
t.Errorf("Unable to find %q in error string %q", errStrFrag, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailGettingIPv6Routes(t *testing.T) {
|
||||
defer func() { v6File.name = ipv6RouteFile }()
|
||||
|
||||
// Try failure to open file (this would be ignored by caller)
|
||||
v6File.name = "no-such-file"
|
||||
errStrFrag := "no such file"
|
||||
_, err := v6File.extract()
|
||||
if err == nil {
|
||||
fmt.Errorf("Expected error trying to read non-existent v6 route file")
|
||||
}
|
||||
if !strings.Contains(err.Error(), errStrFrag) {
|
||||
t.Errorf("Unable to find %q in error string %q", errStrFrag, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllDefaultRoutesFailNoV4RouteFile(t *testing.T) {
|
||||
defer func() { v4File.name = ipv4RouteFile }()
|
||||
|
||||
// Should not occur, as caller ensures we have IPv4 route file, but being thorough
|
||||
v4File.name = "no-such-file"
|
||||
errStrFrag := "no such file"
|
||||
_, err := getAllDefaultRoutes()
|
||||
if err == nil {
|
||||
fmt.Errorf("Expected error trying to read non-existent v4 route file")
|
||||
}
|
||||
if !strings.Contains(err.Error(), errStrFrag) {
|
||||
t.Errorf("Unable to find %q in error string %q", errStrFrag, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllDefaultRoutes(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tcase string
|
||||
v4Info string
|
||||
v6Info string
|
||||
count int
|
||||
expected []Route
|
||||
errStrFrag string
|
||||
}{
|
||||
{"no routes", noInternetConnection, v6noDefaultRoutes, 0, nil, "No default routes"},
|
||||
{"only v4 route", gatewayfirst, v6noDefaultRoutes, 1, routeV4, ""},
|
||||
{"only v6 route", noInternetConnection, v6gatewayfirst, 1, routeV6, ""},
|
||||
{"v4 and v6 routes", gatewayfirst, v6gatewayfirst, 2, bothRoutes, ""},
|
||||
}
|
||||
defer func() {
|
||||
v4File.name = ipv4RouteFile
|
||||
v6File.name = ipv6RouteFile
|
||||
}()
|
||||
|
||||
for _, tc := range testCases {
|
||||
routeFile, err := makeRouteFile(tc.v4Info, t)
|
||||
if routeFile != nil {
|
||||
defer os.Remove(routeFile.Name())
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("case[%s]: test setup failure for IPv4 route file: %v", tc.tcase, err)
|
||||
}
|
||||
v4File.name = routeFile.Name()
|
||||
v6routeFile, err := makeRouteFile(tc.v6Info, t)
|
||||
if v6routeFile != nil {
|
||||
defer os.Remove(v6routeFile.Name())
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("case[%s]: test setup failure for IPv6 route file: %v", tc.tcase, err)
|
||||
}
|
||||
v6File.name = v6routeFile.Name()
|
||||
|
||||
routes, err := getAllDefaultRoutes()
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), tc.errStrFrag) {
|
||||
t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
|
||||
}
|
||||
} else if tc.errStrFrag != "" {
|
||||
t.Errorf("case[%s]: Error %q expected, but not seen", tc.tcase, tc.errStrFrag)
|
||||
} else {
|
||||
if tc.count != len(routes) {
|
||||
t.Errorf("case[%s]: expected %d routes, have %v", tc.tcase, tc.count, routes)
|
||||
}
|
||||
for i, expected := range tc.expected {
|
||||
if !expected.Gateway.Equal(routes[i].Gateway) {
|
||||
t.Errorf("case[%s]: at %d expected %v, got %v .err : %v", tc.tcase, i, tc.expected, routes, err)
|
||||
}
|
||||
zeroIP := net.IPv4zero
|
||||
if expected.Family == familyIPv6 {
|
||||
zeroIP = net.IPv6zero
|
||||
}
|
||||
if !routes[i].Destination.Equal(zeroIP) {
|
||||
t.Errorf("case[%s}: at %d destination is not for default route (not %v)", tc.tcase, i, zeroIP)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
113
vendor/k8s.io/apimachinery/pkg/util/net/port_range.go
generated
vendored
Normal file
113
vendor/k8s.io/apimachinery/pkg/util/net/port_range.go
generated
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
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 "min-max", 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 {
|
||||
value = strings.TrimSpace(value)
|
||||
|
||||
// TODO: Accept "80" syntax
|
||||
// TODO: Accept "80+8" syntax
|
||||
|
||||
if value == "" {
|
||||
pr.Base = 0
|
||||
pr.Size = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
hyphenIndex := strings.Index(value, "-")
|
||||
if hyphenIndex == -1 {
|
||||
return fmt.Errorf("expected hyphen in port range")
|
||||
}
|
||||
|
||||
var err error
|
||||
var low int
|
||||
var high int
|
||||
low, err = strconv.Atoi(value[:hyphenIndex])
|
||||
if err == nil {
|
||||
high, err = strconv.Atoi(value[hyphenIndex+1:])
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse port range: %s: %v", value, err)
|
||||
}
|
||||
|
||||
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 initializs 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
|
||||
}
|
69
vendor/k8s.io/apimachinery/pkg/util/net/port_range_test.go
generated
vendored
Normal file
69
vendor/k8s.io/apimachinery/pkg/util/net/port_range_test.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
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 (
|
||||
"testing"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func TestPortRange(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
success bool
|
||||
expected string
|
||||
included int
|
||||
excluded int
|
||||
}{
|
||||
{"100-200", true, "100-200", 200, 201},
|
||||
{" 100-200 ", true, "100-200", 200, 201},
|
||||
{"0-0", true, "0-0", 0, 1},
|
||||
{"", true, "", -1, 0},
|
||||
{"100", false, "", -1, -1},
|
||||
{"100 - 200", false, "", -1, -1},
|
||||
{"-100", false, "", -1, -1},
|
||||
{"100-", false, "", -1, -1},
|
||||
{"200-100", false, "", -1, -1},
|
||||
{"60000-70000", false, "", -1, -1},
|
||||
{"70000-80000", false, "", -1, -1},
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
tc := &testCases[i]
|
||||
pr := &PortRange{}
|
||||
var f flag.Value = pr
|
||||
err := f.Set(tc.input)
|
||||
if err != nil && tc.success == true {
|
||||
t.Errorf("expected success, got %q", err)
|
||||
continue
|
||||
} else if err == nil && tc.success == false {
|
||||
t.Errorf("expected failure")
|
||||
continue
|
||||
} else if tc.success {
|
||||
if f.String() != tc.expected {
|
||||
t.Errorf("expected %q, got %q", tc.expected, f.String())
|
||||
}
|
||||
if tc.included >= 0 && !pr.Contains(tc.included) {
|
||||
t.Errorf("expected %q to include %d", f.String(), tc.included)
|
||||
}
|
||||
if tc.excluded >= 0 && pr.Contains(tc.excluded) {
|
||||
t.Errorf("expected %q to exclude %d", f.String(), tc.excluded)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
77
vendor/k8s.io/apimachinery/pkg/util/net/port_split.go
generated
vendored
Normal file
77
vendor/k8s.io/apimachinery/pkg/util/net/port_split.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
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
|
||||
}
|
121
vendor/k8s.io/apimachinery/pkg/util/net/port_split_test.go
generated
vendored
Normal file
121
vendor/k8s.io/apimachinery/pkg/util/net/port_split_test.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
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 (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSplitSchemeNamePort(t *testing.T) {
|
||||
table := []struct {
|
||||
in string
|
||||
name, port, scheme string
|
||||
valid bool
|
||||
normalized bool
|
||||
}{
|
||||
{
|
||||
in: "aoeu:asdf",
|
||||
name: "aoeu",
|
||||
port: "asdf",
|
||||
valid: true,
|
||||
normalized: true,
|
||||
}, {
|
||||
in: "http:aoeu:asdf",
|
||||
scheme: "http",
|
||||
name: "aoeu",
|
||||
port: "asdf",
|
||||
valid: true,
|
||||
normalized: true,
|
||||
}, {
|
||||
in: "https:aoeu:",
|
||||
scheme: "https",
|
||||
name: "aoeu",
|
||||
port: "",
|
||||
valid: true,
|
||||
normalized: false,
|
||||
}, {
|
||||
in: "https:aoeu:asdf",
|
||||
scheme: "https",
|
||||
name: "aoeu",
|
||||
port: "asdf",
|
||||
valid: true,
|
||||
normalized: true,
|
||||
}, {
|
||||
in: "aoeu:",
|
||||
name: "aoeu",
|
||||
valid: true,
|
||||
normalized: false,
|
||||
}, {
|
||||
in: "aoeu",
|
||||
name: "aoeu",
|
||||
valid: true,
|
||||
normalized: true,
|
||||
}, {
|
||||
in: ":asdf",
|
||||
valid: false,
|
||||
}, {
|
||||
in: "aoeu:asdf:htns",
|
||||
valid: false,
|
||||
}, {
|
||||
in: "http::asdf",
|
||||
valid: false,
|
||||
}, {
|
||||
in: "http::",
|
||||
valid: false,
|
||||
}, {
|
||||
in: "",
|
||||
valid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, item := range table {
|
||||
scheme, name, port, valid := SplitSchemeNamePort(item.in)
|
||||
if e, a := item.scheme, scheme; e != a {
|
||||
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
|
||||
}
|
||||
if e, a := item.name, name; e != a {
|
||||
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
|
||||
}
|
||||
if e, a := item.port, port; e != a {
|
||||
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
|
||||
}
|
||||
if e, a := item.valid, valid; e != a {
|
||||
t.Errorf("%q: Wanted %t, got %t", item.in, e, a)
|
||||
}
|
||||
|
||||
// Make sure valid items round trip through JoinSchemeNamePort
|
||||
if item.valid {
|
||||
out := JoinSchemeNamePort(scheme, name, port)
|
||||
if item.normalized && out != item.in {
|
||||
t.Errorf("%q: Wanted %s, got %s", item.in, item.in, out)
|
||||
}
|
||||
scheme, name, port, valid := SplitSchemeNamePort(out)
|
||||
if e, a := item.scheme, scheme; e != a {
|
||||
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
|
||||
}
|
||||
if e, a := item.name, name; e != a {
|
||||
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
|
||||
}
|
||||
if e, a := item.port, port; e != a {
|
||||
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
|
||||
}
|
||||
if e, a := item.valid, valid; e != a {
|
||||
t.Errorf("%q: Wanted %t, got %t", item.in, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
46
vendor/k8s.io/apimachinery/pkg/util/net/util.go
generated
vendored
Normal file
46
vendor/k8s.io/apimachinery/pkg/util/net/util.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
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 (
|
||||
"net"
|
||||
"reflect"
|
||||
"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 {
|
||||
opErr, ok := err.(*net.OpError)
|
||||
if ok && opErr.Err.Error() == syscall.ECONNRESET.Error() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
68
vendor/k8s.io/apimachinery/pkg/util/net/util_test.go
generated
vendored
Normal file
68
vendor/k8s.io/apimachinery/pkg/util/net/util_test.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
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 (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getIPNet(cidr string) *net.IPNet {
|
||||
_, ipnet, _ := net.ParseCIDR(cidr)
|
||||
return ipnet
|
||||
}
|
||||
|
||||
func TestIPNetEqual(t *testing.T) {
|
||||
testCases := []struct {
|
||||
ipnet1 *net.IPNet
|
||||
ipnet2 *net.IPNet
|
||||
expect bool
|
||||
}{
|
||||
//null case
|
||||
{
|
||||
getIPNet("10.0.0.1/24"),
|
||||
getIPNet(""),
|
||||
false,
|
||||
},
|
||||
{
|
||||
getIPNet("10.0.0.0/24"),
|
||||
getIPNet("10.0.0.0/24"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
getIPNet("10.0.0.0/24"),
|
||||
getIPNet("10.0.0.1/24"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
getIPNet("10.0.0.0/25"),
|
||||
getIPNet("10.0.0.0/24"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
getIPNet("10.0.1.0/24"),
|
||||
getIPNet("10.0.0.0/24"),
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if tc.expect != IPNetEqual(tc.ipnet1, tc.ipnet2) {
|
||||
t.Errorf("Expect equality of %s and %s be to %v", tc.ipnet1.String(), tc.ipnet2.String(), tc.expect)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user