vendor files

This commit is contained in:
Serguei Bezverkhi
2018-01-09 13:57:14 -05:00
parent 558bc6c02a
commit 7b24313bd6
16547 changed files with 4527373 additions and 0 deletions

51
vendor/k8s.io/apimachinery/pkg/util/net/BUILD generated vendored Normal file
View 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
View 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
View 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
View 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
}

View 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
View 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
}

View 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
View 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
}

View 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
View 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
View 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)
}
}
}