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 }