github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/util.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors All rights reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package util
    18  
    19  import (
    20  	"bufio"
    21  	"encoding/hex"
    22  	"fmt"
    23  	"io"
    24  	"math"
    25  	"net"
    26  	"net/http"
    27  	"os"
    28  	"path"
    29  	"reflect"
    30  	"regexp"
    31  	"runtime"
    32  	"strconv"
    33  	"strings"
    34  	"time"
    35  
    36  	"k8s.io/kubernetes/pkg/util/intstr"
    37  
    38  	"github.com/golang/glog"
    39  )
    40  
    41  // For testing, bypass HandleCrash.
    42  var ReallyCrash bool
    43  
    44  // For any test of the style:
    45  //   ...
    46  //   <- time.After(timeout):
    47  //      t.Errorf("Timed out")
    48  // The value for timeout should effectively be "forever." Obviously we don't want our tests to truly lock up forever, but 30s
    49  // is long enough that it is effectively forever for the things that can slow down a run on a heavily contended machine
    50  // (GC, seeks, etc), but not so long as to make a developer ctrl-c a test run if they do happen to break that test.
    51  var ForeverTestTimeout = time.Second * 30
    52  
    53  // PanicHandlers is a list of functions which will be invoked when a panic happens.
    54  var PanicHandlers = []func(interface{}){logPanic}
    55  
    56  // HandleCrash simply catches a crash and logs an error. Meant to be called via defer.
    57  // Additional context-specific handlers can be provided, and will be called in case of panic
    58  func HandleCrash(additionalHandlers ...func(interface{})) {
    59  	if ReallyCrash {
    60  		return
    61  	}
    62  	if r := recover(); r != nil {
    63  		for _, fn := range PanicHandlers {
    64  			fn(r)
    65  		}
    66  		for _, fn := range additionalHandlers {
    67  			fn(r)
    68  		}
    69  	}
    70  }
    71  
    72  // logPanic logs the caller tree when a panic occurs.
    73  func logPanic(r interface{}) {
    74  	callers := ""
    75  	for i := 0; true; i++ {
    76  		_, file, line, ok := runtime.Caller(i)
    77  		if !ok {
    78  			break
    79  		}
    80  		callers = callers + fmt.Sprintf("%v:%v\n", file, line)
    81  	}
    82  	glog.Errorf("Recovered from panic: %#v (%v)\n%v", r, r, callers)
    83  }
    84  
    85  // ErrorHandlers is a list of functions which will be invoked when an unreturnable
    86  // error occurs.
    87  var ErrorHandlers = []func(error){logError}
    88  
    89  // HandlerError is a method to invoke when a non-user facing piece of code cannot
    90  // return an error and needs to indicate it has been ignored. Invoking this method
    91  // is preferable to logging the error - the default behavior is to log but the
    92  // errors may be sent to a remote server for analysis.
    93  func HandleError(err error) {
    94  	for _, fn := range ErrorHandlers {
    95  		fn(err)
    96  	}
    97  }
    98  
    99  // logError prints an error with the call stack of the location it was reported
   100  func logError(err error) {
   101  	glog.ErrorDepth(2, err)
   102  }
   103  
   104  // NeverStop may be passed to Until to make it never stop.
   105  var NeverStop <-chan struct{} = make(chan struct{})
   106  
   107  // Forever is syntactic sugar on top of Until
   108  func Forever(f func(), period time.Duration) {
   109  	Until(f, period, NeverStop)
   110  }
   111  
   112  // Until loops until stop channel is closed, running f every period.
   113  // Catches any panics, and keeps going. f may not be invoked if
   114  // stop channel is already closed. Pass NeverStop to Until if you
   115  // don't want it stop.
   116  func Until(f func(), period time.Duration, stopCh <-chan struct{}) {
   117  	select {
   118  	case <-stopCh:
   119  		return
   120  	default:
   121  	}
   122  
   123  	for {
   124  		func() {
   125  			defer HandleCrash()
   126  			f()
   127  		}()
   128  		select {
   129  		case <-stopCh:
   130  			return
   131  		case <-time.After(period):
   132  		}
   133  	}
   134  }
   135  
   136  func GetIntOrPercentValue(intOrStr *intstr.IntOrString) (int, bool, error) {
   137  	switch intOrStr.Type {
   138  	case intstr.Int:
   139  		return intOrStr.IntVal, false, nil
   140  	case intstr.String:
   141  		s := strings.Replace(intOrStr.StrVal, "%", "", -1)
   142  		v, err := strconv.Atoi(s)
   143  		if err != nil {
   144  			return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err)
   145  		}
   146  		return v, true, nil
   147  	}
   148  	return 0, false, fmt.Errorf("invalid value: neither int nor percentage")
   149  }
   150  
   151  func GetValueFromPercent(percent int, value int) int {
   152  	return int(math.Ceil(float64(percent) * (float64(value)) / 100))
   153  }
   154  
   155  // Takes a list of strings and compiles them into a list of regular expressions
   156  func CompileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) {
   157  	regexps := []*regexp.Regexp{}
   158  	for _, regexpStr := range regexpStrings {
   159  		r, err := regexp.Compile(regexpStr)
   160  		if err != nil {
   161  			return []*regexp.Regexp{}, err
   162  		}
   163  		regexps = append(regexps, r)
   164  	}
   165  	return regexps, nil
   166  }
   167  
   168  // Detects if using systemd as the init system
   169  // Please note that simply reading /proc/1/cmdline can be misleading because
   170  // some installation of various init programs can automatically make /sbin/init
   171  // a symlink or even a renamed version of their main program.
   172  // TODO(dchen1107): realiably detects the init system using on the system:
   173  // systemd, upstart, initd, etc.
   174  func UsingSystemdInitSystem() bool {
   175  	if _, err := os.Stat("/run/systemd/system"); err == nil {
   176  		return true
   177  	}
   178  
   179  	return false
   180  }
   181  
   182  // Tests whether all pointer fields in a struct are nil.  This is useful when,
   183  // for example, an API struct is handled by plugins which need to distinguish
   184  // "no plugin accepted this spec" from "this spec is empty".
   185  //
   186  // This function is only valid for structs and pointers to structs.  Any other
   187  // type will cause a panic.  Passing a typed nil pointer will return true.
   188  func AllPtrFieldsNil(obj interface{}) bool {
   189  	v := reflect.ValueOf(obj)
   190  	if !v.IsValid() {
   191  		panic(fmt.Sprintf("reflect.ValueOf() produced a non-valid Value for %#v", obj))
   192  	}
   193  	if v.Kind() == reflect.Ptr {
   194  		if v.IsNil() {
   195  			return true
   196  		}
   197  		v = v.Elem()
   198  	}
   199  	for i := 0; i < v.NumField(); i++ {
   200  		if v.Field(i).Kind() == reflect.Ptr && !v.Field(i).IsNil() {
   201  			return false
   202  		}
   203  	}
   204  	return true
   205  }
   206  
   207  // Splits a fully qualified name and returns its namespace and name.
   208  // Assumes that the input 'str' has been validated.
   209  func SplitQualifiedName(str string) (string, string) {
   210  	parts := strings.Split(str, "/")
   211  	if len(parts) < 2 {
   212  		return "", str
   213  	}
   214  
   215  	return parts[0], parts[1]
   216  }
   217  
   218  // Joins 'namespace' and 'name' and returns a fully qualified name
   219  // Assumes that the input is valid.
   220  func JoinQualifiedName(namespace, name string) string {
   221  	return path.Join(namespace, name)
   222  }
   223  
   224  type Route struct {
   225  	Interface   string
   226  	Destination net.IP
   227  	Gateway     net.IP
   228  	// TODO: add more fields here if needed
   229  }
   230  
   231  func getRoutes(input io.Reader) ([]Route, error) {
   232  	routes := []Route{}
   233  	if input == nil {
   234  		return nil, fmt.Errorf("input is nil")
   235  	}
   236  	scanner := bufio.NewReader(input)
   237  	for {
   238  		line, err := scanner.ReadString('\n')
   239  		if err == io.EOF {
   240  			break
   241  		}
   242  		//ignore the headers in the route info
   243  		if strings.HasPrefix(line, "Iface") {
   244  			continue
   245  		}
   246  		fields := strings.Fields(line)
   247  		routes = append(routes, Route{})
   248  		route := &routes[len(routes)-1]
   249  		route.Interface = fields[0]
   250  		ip, err := parseIP(fields[1])
   251  		if err != nil {
   252  			return nil, err
   253  		}
   254  		route.Destination = ip
   255  		ip, err = parseIP(fields[2])
   256  		if err != nil {
   257  			return nil, err
   258  		}
   259  		route.Gateway = ip
   260  	}
   261  	return routes, nil
   262  }
   263  
   264  func parseIP(str string) (net.IP, error) {
   265  	if str == "" {
   266  		return nil, fmt.Errorf("input is nil")
   267  	}
   268  	bytes, err := hex.DecodeString(str)
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  	//TODO add ipv6 support
   273  	if len(bytes) != net.IPv4len {
   274  		return nil, fmt.Errorf("only IPv4 is supported")
   275  	}
   276  	bytes[0], bytes[1], bytes[2], bytes[3] = bytes[3], bytes[2], bytes[1], bytes[0]
   277  	return net.IP(bytes), nil
   278  }
   279  
   280  func isInterfaceUp(intf *net.Interface) bool {
   281  	if intf == nil {
   282  		return false
   283  	}
   284  	if intf.Flags&net.FlagUp != 0 {
   285  		glog.V(4).Infof("Interface %v is up", intf.Name)
   286  		return true
   287  	}
   288  	return false
   289  }
   290  
   291  //getFinalIP method receives all the IP addrs of a Interface
   292  //and returns a nil if the address is Loopback, Ipv6, link-local or nil.
   293  //It returns a valid IPv4 if an Ipv4 address is found in the array.
   294  func getFinalIP(addrs []net.Addr) (net.IP, error) {
   295  	if len(addrs) > 0 {
   296  		for i := range addrs {
   297  			glog.V(4).Infof("Checking addr  %s.", addrs[i].String())
   298  			ip, _, err := net.ParseCIDR(addrs[i].String())
   299  			if err != nil {
   300  				return nil, err
   301  			}
   302  			//Only IPv4
   303  			//TODO : add IPv6 support
   304  			if ip.To4() != nil {
   305  				if !ip.IsLoopback() && !ip.IsLinkLocalMulticast() && !ip.IsLinkLocalUnicast() {
   306  					glog.V(4).Infof("IP found %v", ip)
   307  					return ip, nil
   308  				} else {
   309  					glog.V(4).Infof("Loopback/link-local found %v", ip)
   310  				}
   311  			} else {
   312  				glog.V(4).Infof("%v is not a valid IPv4 address", ip)
   313  			}
   314  
   315  		}
   316  	}
   317  	return nil, nil
   318  }
   319  
   320  func getIPFromInterface(intfName string, nw networkInterfacer) (net.IP, error) {
   321  	intf, err := nw.InterfaceByName(intfName)
   322  	if err != nil {
   323  		return nil, err
   324  	}
   325  	if isInterfaceUp(intf) {
   326  		addrs, err := nw.Addrs(intf)
   327  		if err != nil {
   328  			return nil, err
   329  		}
   330  		glog.V(4).Infof("Interface %q has %d addresses :%v.", intfName, len(addrs), addrs)
   331  		finalIP, err := getFinalIP(addrs)
   332  		if err != nil {
   333  			return nil, err
   334  		}
   335  		if finalIP != nil {
   336  			glog.V(4).Infof("valid IPv4 address for interface %q found as %v.", intfName, finalIP)
   337  			return finalIP, nil
   338  		}
   339  	}
   340  
   341  	return nil, nil
   342  }
   343  
   344  func flagsSet(flags net.Flags, test net.Flags) bool {
   345  	return flags&test != 0
   346  }
   347  
   348  func flagsClear(flags net.Flags, test net.Flags) bool {
   349  	return flags&test == 0
   350  }
   351  
   352  func chooseHostInterfaceNativeGo() (net.IP, error) {
   353  	intfs, err := net.Interfaces()
   354  	if err != nil {
   355  		return nil, err
   356  	}
   357  	i := 0
   358  	var ip net.IP
   359  	for i = range intfs {
   360  		if flagsSet(intfs[i].Flags, net.FlagUp) && flagsClear(intfs[i].Flags, net.FlagLoopback|net.FlagPointToPoint) {
   361  			addrs, err := intfs[i].Addrs()
   362  			if err != nil {
   363  				return nil, err
   364  			}
   365  			if len(addrs) > 0 {
   366  				for _, addr := range addrs {
   367  					if addrIP, _, err := net.ParseCIDR(addr.String()); err == nil {
   368  						if addrIP.To4() != nil {
   369  							ip = addrIP.To4()
   370  							if !ip.IsLinkLocalMulticast() && !ip.IsLinkLocalUnicast() {
   371  								break
   372  							}
   373  						}
   374  					}
   375  				}
   376  				if ip != nil {
   377  					// This interface should suffice.
   378  					break
   379  				}
   380  			}
   381  		}
   382  	}
   383  	if ip == nil {
   384  		return nil, fmt.Errorf("no acceptable interface from host")
   385  	}
   386  	glog.V(4).Infof("Choosing interface %s (IP %v) as default", intfs[i].Name, ip)
   387  	return ip, nil
   388  }
   389  
   390  //ChooseHostInterface is a method used fetch an IP for a daemon.
   391  //It uses data from /proc/net/route file.
   392  //For a node with no internet connection ,it returns error
   393  //For a multi n/w interface node it returns the IP of the interface with gateway on it.
   394  func ChooseHostInterface() (net.IP, error) {
   395  	inFile, err := os.Open("/proc/net/route")
   396  	if err != nil {
   397  		if os.IsNotExist(err) {
   398  			return chooseHostInterfaceNativeGo()
   399  		}
   400  		return nil, err
   401  	}
   402  	defer inFile.Close()
   403  	var nw networkInterfacer = networkInterface{}
   404  	return chooseHostInterfaceFromRoute(inFile, nw)
   405  }
   406  
   407  type networkInterfacer interface {
   408  	InterfaceByName(intfName string) (*net.Interface, error)
   409  	Addrs(intf *net.Interface) ([]net.Addr, error)
   410  }
   411  
   412  type networkInterface struct{}
   413  
   414  func (_ networkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
   415  	intf, err := net.InterfaceByName(intfName)
   416  	if err != nil {
   417  		return nil, err
   418  	}
   419  	return intf, nil
   420  }
   421  
   422  func (_ networkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
   423  	addrs, err := intf.Addrs()
   424  	if err != nil {
   425  		return nil, err
   426  	}
   427  	return addrs, nil
   428  }
   429  
   430  func chooseHostInterfaceFromRoute(inFile io.Reader, nw networkInterfacer) (net.IP, error) {
   431  	routes, err := getRoutes(inFile)
   432  	if err != nil {
   433  		return nil, err
   434  	}
   435  	zero := net.IP{0, 0, 0, 0}
   436  	var finalIP net.IP
   437  	for i := range routes {
   438  		//find interface with gateway
   439  		if routes[i].Destination.Equal(zero) {
   440  			glog.V(4).Infof("Default route transits interface %q", routes[i].Interface)
   441  			finalIP, err := getIPFromInterface(routes[i].Interface, nw)
   442  			if err != nil {
   443  				return nil, err
   444  			}
   445  			if finalIP != nil {
   446  				glog.V(4).Infof("Choosing IP %v ", finalIP)
   447  				return finalIP, nil
   448  			}
   449  		}
   450  	}
   451  	glog.V(4).Infof("No valid IP found")
   452  	if finalIP == nil {
   453  		return nil, fmt.Errorf("Unable to select an IP.")
   454  	}
   455  	return nil, nil
   456  }
   457  
   458  func GetClient(req *http.Request) string {
   459  	if userAgent, ok := req.Header["User-Agent"]; ok {
   460  		if len(userAgent) > 0 {
   461  			return userAgent[0]
   462  		}
   463  	}
   464  	return "unknown"
   465  }
   466  
   467  func ShortenString(str string, n int) string {
   468  	if len(str) <= n {
   469  		return str
   470  	} else {
   471  		return str[:n]
   472  	}
   473  }
   474  
   475  func FileExists(filename string) (bool, error) {
   476  	if _, err := os.Stat(filename); os.IsNotExist(err) {
   477  		return false, nil
   478  	} else if err != nil {
   479  		return false, err
   480  	}
   481  	return true, nil
   482  }
   483  
   484  // borrowed from ioutil.ReadDir
   485  // ReadDir reads the directory named by dirname and returns
   486  // a list of directory entries, minus those with lstat errors
   487  func ReadDirNoExit(dirname string) ([]os.FileInfo, []error, error) {
   488  	if dirname == "" {
   489  		dirname = "."
   490  	}
   491  
   492  	f, err := os.Open(dirname)
   493  	if err != nil {
   494  		return nil, nil, err
   495  	}
   496  	defer f.Close()
   497  
   498  	names, err := f.Readdirnames(-1)
   499  	list := make([]os.FileInfo, 0, len(names))
   500  	errs := make([]error, 0, len(names))
   501  	for _, filename := range names {
   502  		fip, lerr := os.Lstat(dirname + "/" + filename)
   503  		if os.IsNotExist(lerr) {
   504  			// File disappeared between readdir + stat.
   505  			// Just treat it as if it didn't exist.
   506  			continue
   507  		}
   508  
   509  		list = append(list, fip)
   510  		errs = append(errs, lerr)
   511  	}
   512  
   513  	return list, errs, nil
   514  }