Import of https://github.com/ciena/voltctl at commit 40d61fbf3f910ed4017cf67c9c79e8e1f82a33a5
Change-Id: I8464c59e60d76cb8612891db3303878975b5416c
diff --git a/vendor/k8s.io/apimachinery/pkg/util/net/http.go b/vendor/k8s.io/apimachinery/pkg/util/net/http.go
new file mode 100644
index 0000000..7c2a5e6
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/util/net/http.go
@@ -0,0 +1,442 @@
+/*
+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"
+ "context"
+ "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 err == nil {
+ return false
+ }
+ 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 no custom dialer is set, use the default context dialer
+ if t.DialContext == nil && t.Dial == nil {
+ t.DialContext = defaultTransport.DialContext
+ }
+ 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(ctx context.Context, net, addr string) (net.Conn, error)
+
+func DialerFor(transport http.RoundTripper) (DialFunc, error) {
+ if transport == nil {
+ return nil, nil
+ }
+
+ switch transport := transport.(type) {
+ case *http.Transport:
+ // transport.DialContext takes precedence over transport.Dial
+ if transport.DialContext != nil {
+ return transport.DialContext, nil
+ }
+ // adapt transport.Dial to the DialWithContext signature
+ if transport.Dial != nil {
+ return func(ctx context.Context, net, addr string) (net.Conn, error) {
+ return transport.Dial(net, addr)
+ }, nil
+ }
+ // otherwise return nil
+ return nil, nil
+ case RoundTripperWrapper:
+ return DialerFor(transport.WrappedRoundTripper())
+ default:
+ return nil, fmt.Errorf("unknown transport type: %T", transport)
+ }
+}
+
+type TLSClientConfigHolder interface {
+ TLSClientConfig() *tls.Config
+}
+
+func TLSClientConfig(transport http.RoundTripper) (*tls.Config, error) {
+ if transport == nil {
+ return nil, nil
+ }
+
+ switch transport := transport.(type) {
+ case *http.Transport:
+ return transport.TLSClientConfig, nil
+ case TLSClientConfigHolder:
+ return transport.TLSClientConfig(), nil
+ case RoundTripperWrapper:
+ return TLSClientConfig(transport.WrappedRoundTripper())
+ default:
+ return nil, fmt.Errorf("unknown transport type: %T", transport)
+ }
+}
+
+func FormatURL(scheme string, host string, port int, path string) *url.URL {
+ return &url.URL{
+ Scheme: scheme,
+ Host: net.JoinHostPort(host, strconv.Itoa(port)),
+ Path: path,
+ }
+}
+
+func GetHTTPClient(req *http.Request) string {
+ if ua := req.UserAgent(); len(ua) != 0 {
+ return ua
+ }
+ return "unknown"
+}
+
+// SourceIPs splits the comma separated X-Forwarded-For header 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.
+// If requireSameHostRedirects is true, only redirects to the same host are permitted.
+func ConnectWithRedirects(originalMethod string, originalLocation *url.URL, header http.Header, originalBody io.Reader, dialer Dialer, requireSameHostRedirects bool) (net.Conn, []byte, error) {
+ const (
+ maxRedirects = 9 // Fail on the 10th redirect
+ 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
+
+ // 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)
+ }
+
+ // Only follow redirects to the same host. Otherwise, propagate the redirect response back.
+ if requireSameHostRedirects && location.Hostname() != originalLocation.Hostname() {
+ break redirectLoop
+ }
+
+ // Reset the connection.
+ intermediateConn.Close()
+ intermediateConn = nil
+ }
+
+ 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
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/util/net/interface.go b/vendor/k8s.io/apimachinery/pkg/util/net/interface.go
new file mode 100644
index 0000000..0ab9b36
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/util/net/interface.go
@@ -0,0 +1,416 @@
+/*
+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)
+}
+
+// noRoutesError can be returned by ChooseBindAddress() in case of no routes
+type noRoutesError struct {
+ message string
+}
+
+func (e noRoutesError) Error() string {
+ return e.message
+}
+
+// IsNoRoutesError checks if an error is of type noRoutesError
+func IsNoRoutesError(err error) bool {
+ if err == nil {
+ return false
+ }
+ switch err.(type) {
+ case noRoutesError:
+ return true
+ default:
+ return false
+ }
+}
+
+var (
+ v4File = RouteFile{name: ipv4RouteFile, parse: getIPv4DefaultRoutes}
+ v6File = RouteFile{name: ipv6RouteFile, parse: getIPv6DefaultRoutes}
+)
+
+func (rf RouteFile) extract() ([]Route, error) {
+ file, err := os.Open(rf.name)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+ return rf.parse(file)
+}
+
+// getIPv4DefaultRoutes obtains the IPv4 routes, and filters out non-default routes.
+func getIPv4DefaultRoutes(input io.Reader) ([]Route, error) {
+ routes := []Route{}
+ scanner := bufio.NewReader(input)
+ for {
+ line, err := scanner.ReadString('\n')
+ if err == io.EOF {
+ break
+ }
+ //ignore the headers in the route info
+ if strings.HasPrefix(line, "Iface") {
+ continue
+ }
+ fields := strings.Fields(line)
+ // Interested in fields:
+ // 0 - interface name
+ // 1 - destination address
+ // 2 - gateway
+ dest, err := parseIP(fields[1], familyIPv4)
+ if err != nil {
+ return nil, err
+ }
+ gw, err := parseIP(fields[2], familyIPv4)
+ if err != nil {
+ return nil, err
+ }
+ if !dest.Equal(net.IPv4zero) {
+ continue
+ }
+ routes = append(routes, Route{
+ Interface: fields[0],
+ Destination: dest,
+ Gateway: gw,
+ Family: familyIPv4,
+ })
+ }
+ return routes, nil
+}
+
+func getIPv6DefaultRoutes(input io.Reader) ([]Route, error) {
+ routes := []Route{}
+ scanner := bufio.NewReader(input)
+ for {
+ line, err := scanner.ReadString('\n')
+ if err == io.EOF {
+ break
+ }
+ fields := strings.Fields(line)
+ // Interested in fields:
+ // 0 - destination address
+ // 4 - gateway
+ // 9 - interface name
+ dest, err := parseIP(fields[0], familyIPv6)
+ if err != nil {
+ return nil, err
+ }
+ gw, err := parseIP(fields[4], familyIPv6)
+ if err != nil {
+ return nil, err
+ }
+ if !dest.Equal(net.IPv6zero) {
+ continue
+ }
+ if gw.Equal(net.IPv6zero) {
+ continue // loopback
+ }
+ routes = append(routes, Route{
+ Interface: fields[9],
+ Destination: dest,
+ Gateway: gw,
+ Family: familyIPv6,
+ })
+ }
+ return routes, nil
+}
+
+// parseIP takes the hex IP address string from route file and converts it
+// to a net.IP address. For IPv4, the value must be converted to big endian.
+func parseIP(str string, family AddressFamily) (net.IP, error) {
+ if str == "" {
+ return nil, fmt.Errorf("input is nil")
+ }
+ bytes, err := hex.DecodeString(str)
+ if err != nil {
+ return nil, err
+ }
+ if family == familyIPv4 {
+ if len(bytes) != net.IPv4len {
+ return nil, fmt.Errorf("invalid IPv4 address in route")
+ }
+ return net.IP([]byte{bytes[3], bytes[2], bytes[1], bytes[0]}), nil
+ }
+ // Must be IPv6
+ if len(bytes) != net.IPv6len {
+ return nil, fmt.Errorf("invalid IPv6 address in route")
+ }
+ return net.IP(bytes), nil
+}
+
+func isInterfaceUp(intf *net.Interface) bool {
+ if intf == nil {
+ return false
+ }
+ if intf.Flags&net.FlagUp != 0 {
+ 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, noRoutesError{
+ message: fmt.Sprintf("no default routes found in %q or %q", v4File.name, v6File.name),
+ }
+ }
+ return routes, nil
+}
+
+// chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
+// global IP address from the interface for the route. 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
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/util/net/port_range.go b/vendor/k8s.io/apimachinery/pkg/util/net/port_range.go
new file mode 100644
index 0000000..7b6eca8
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/util/net/port_range.go
@@ -0,0 +1,149 @@
+/*
+Copyright 2015 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package net
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+// PortRange represents a range of TCP/UDP ports. To represent a single port,
+// set Size to 1.
+type PortRange struct {
+ Base int
+ Size int
+}
+
+// Contains tests whether a given port falls within the PortRange.
+func (pr *PortRange) Contains(p int) bool {
+ return (p >= pr.Base) && ((p - pr.Base) < pr.Size)
+}
+
+// String converts the PortRange to a string representation, which can be
+// parsed by PortRange.Set or ParsePortRange.
+func (pr PortRange) String() string {
+ if pr.Size == 0 {
+ return ""
+ }
+ return fmt.Sprintf("%d-%d", pr.Base, pr.Base+pr.Size-1)
+}
+
+// Set parses a string of the form "value", "min-max", or "min+offset", inclusive at both ends, and
+// sets the PortRange from it. This is part of the flag.Value and pflag.Value
+// interfaces.
+func (pr *PortRange) Set(value string) error {
+ const (
+ SinglePortNotation = 1 << iota
+ HyphenNotation
+ PlusNotation
+ )
+
+ value = strings.TrimSpace(value)
+ hyphenIndex := strings.Index(value, "-")
+ plusIndex := strings.Index(value, "+")
+
+ if value == "" {
+ pr.Base = 0
+ pr.Size = 0
+ return nil
+ }
+
+ var err error
+ var low, high int
+ var notation int
+
+ if plusIndex == -1 && hyphenIndex == -1 {
+ notation |= SinglePortNotation
+ }
+ if hyphenIndex != -1 {
+ notation |= HyphenNotation
+ }
+ if plusIndex != -1 {
+ notation |= PlusNotation
+ }
+
+ switch notation {
+ case SinglePortNotation:
+ var port int
+ port, err = strconv.Atoi(value)
+ if err != nil {
+ return err
+ }
+ low = port
+ high = port
+ case HyphenNotation:
+ low, err = strconv.Atoi(value[:hyphenIndex])
+ if err != nil {
+ return err
+ }
+ high, err = strconv.Atoi(value[hyphenIndex+1:])
+ if err != nil {
+ return err
+ }
+ case PlusNotation:
+ var offset int
+ low, err = strconv.Atoi(value[:plusIndex])
+ if err != nil {
+ return err
+ }
+ offset, err = strconv.Atoi(value[plusIndex+1:])
+ if err != nil {
+ return err
+ }
+ high = low + offset
+ default:
+ return fmt.Errorf("unable to parse port range: %s", value)
+ }
+
+ if low > 65535 || high > 65535 {
+ return fmt.Errorf("the port range cannot be greater than 65535: %s", value)
+ }
+
+ if high < low {
+ return fmt.Errorf("end port cannot be less than start port: %s", value)
+ }
+
+ pr.Base = low
+ pr.Size = 1 + high - low
+ return nil
+}
+
+// Type returns a descriptive string about this type. This is part of the
+// pflag.Value interface.
+func (*PortRange) Type() string {
+ return "portRange"
+}
+
+// ParsePortRange parses a string of the form "min-max", inclusive at both
+// ends, and 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
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/util/net/port_split.go b/vendor/k8s.io/apimachinery/pkg/util/net/port_split.go
new file mode 100644
index 0000000..c0fd4e2
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/util/net/port_split.go
@@ -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
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/util/net/util.go b/vendor/k8s.io/apimachinery/pkg/util/net/util.go
new file mode 100644
index 0000000..8344d10
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/util/net/util.go
@@ -0,0 +1,56 @@
+/*
+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"
+ "net/url"
+ "os"
+ "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 {
+ if urlErr, ok := err.(*url.Error); ok {
+ err = urlErr.Err
+ }
+ if opErr, ok := err.(*net.OpError); ok {
+ err = opErr.Err
+ }
+ if osErr, ok := err.(*os.SyscallError); ok {
+ err = osErr.Err
+ }
+ if errno, ok := err.(syscall.Errno); ok && errno == syscall.ECONNRESET {
+ return true
+ }
+ return false
+}