gopkg.in/dotcloud/docker.v1@v1.13.1/daemon/graphdriver/overlay2/randomid.go (about) 1 // +build linux 2 3 package overlay2 4 5 import ( 6 "crypto/rand" 7 "encoding/base32" 8 "fmt" 9 "io" 10 "os" 11 "syscall" 12 "time" 13 14 "github.com/Sirupsen/logrus" 15 ) 16 17 // generateID creates a new random string identifier with the given length 18 func generateID(l int) string { 19 const ( 20 // ensures we backoff for less than 450ms total. Use the following to 21 // select new value, in units of 10ms: 22 // n*(n+1)/2 = d -> n^2 + n - 2d -> n = (sqrt(8d + 1) - 1)/2 23 maxretries = 9 24 backoff = time.Millisecond * 10 25 ) 26 27 var ( 28 totalBackoff time.Duration 29 count int 30 retries int 31 size = (l*5 + 7) / 8 32 u = make([]byte, size) 33 ) 34 // TODO: Include time component, counter component, random component 35 36 for { 37 // This should never block but the read may fail. Because of this, 38 // we just try to read the random number generator until we get 39 // something. This is a very rare condition but may happen. 40 b := time.Duration(retries) * backoff 41 time.Sleep(b) 42 totalBackoff += b 43 44 n, err := io.ReadFull(rand.Reader, u[count:]) 45 if err != nil { 46 if retryOnError(err) && retries < maxretries { 47 count += n 48 retries++ 49 logrus.Errorf("error generating version 4 uuid, retrying: %v", err) 50 continue 51 } 52 53 // Any other errors represent a system problem. What did someone 54 // do to /dev/urandom? 55 panic(fmt.Errorf("error reading random number generator, retried for %v: %v", totalBackoff.String(), err)) 56 } 57 58 break 59 } 60 61 s := base32.StdEncoding.EncodeToString(u) 62 63 return s[:l] 64 } 65 66 // retryOnError tries to detect whether or not retrying would be fruitful. 67 func retryOnError(err error) bool { 68 switch err := err.(type) { 69 case *os.PathError: 70 return retryOnError(err.Err) // unpack the target error 71 case syscall.Errno: 72 if err == syscall.EPERM { 73 // EPERM represents an entropy pool exhaustion, a condition under 74 // which we backoff and retry. 75 return true 76 } 77 } 78 79 return false 80 }