github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/provider/azure/utils.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package azure 5 6 import ( 7 "fmt" 8 "math/rand" 9 "net/http" 10 "time" 11 12 "github.com/Azure/go-autorest/autorest" 13 "github.com/Azure/go-autorest/autorest/to" 14 "github.com/juju/errors" 15 "github.com/juju/retry" 16 "github.com/juju/utils" 17 "github.com/juju/utils/clock" 18 ) 19 20 const ( 21 retryDelay = 5 * time.Second 22 maxRetryDelay = 1 * time.Minute 23 maxRetryDuration = 5 * time.Minute 24 ) 25 26 func toTags(tags *map[string]*string) map[string]string { 27 if tags == nil { 28 return nil 29 } 30 return to.StringMap(*tags) 31 } 32 33 // randomAdminPassword returns a random administrator password for 34 // Windows machines. 35 func randomAdminPassword() string { 36 // We want at least one each of lower-alpha, upper-alpha, and digit. 37 // Allocate 16 of each (randomly), and then the remaining characters 38 // will be randomly chosen from the full set. 39 validRunes := append(utils.LowerAlpha, utils.Digits...) 40 validRunes = append(validRunes, utils.UpperAlpha...) 41 42 lowerAlpha := utils.RandomString(16, utils.LowerAlpha) 43 upperAlpha := utils.RandomString(16, utils.UpperAlpha) 44 digits := utils.RandomString(16, utils.Digits) 45 mixed := utils.RandomString(16, validRunes) 46 password := []rune(lowerAlpha + upperAlpha + digits + mixed) 47 for i := len(password) - 1; i >= 1; i-- { 48 j := rand.Intn(i + 1) 49 password[i], password[j] = password[j], password[i] 50 } 51 return string(password) 52 } 53 54 // callAPIFunc is a function type that should wrap any any 55 // Azure Resource Manager API calls. 56 type callAPIFunc func(func() (autorest.Response, error)) error 57 58 // backoffAPIRequestCaller is a type whose "call" method can 59 // be used as a callAPIFunc. 60 type backoffAPIRequestCaller struct { 61 clock clock.Clock 62 } 63 64 // call will call the supplied function, with exponential backoff 65 // as long as the request returns an http.StatusTooManyRequests 66 // status. 67 func (c backoffAPIRequestCaller) call(f func() (autorest.Response, error)) error { 68 var resp *http.Response 69 return retry.Call(retry.CallArgs{ 70 Func: func() error { 71 autorestResp, err := f() 72 resp = autorestResp.Response 73 return err 74 }, 75 IsFatalError: func(err error) bool { 76 return resp == nil || !autorest.ResponseHasStatusCode(resp, http.StatusTooManyRequests) 77 }, 78 NotifyFunc: func(err error, attempt int) { 79 logger.Debugf("attempt %d: %v", attempt, err) 80 }, 81 Attempts: -1, 82 Delay: retryDelay, 83 MaxDelay: maxRetryDelay, 84 MaxDuration: maxRetryDuration, 85 BackoffFunc: retry.DoubleDelay, 86 Clock: c.clock, 87 }) 88 } 89 90 // deleteResource deletes a resource with the given name from the resource 91 // group, using the provided "Deleter". If the resource does not exist, an 92 // error satisfying errors.IsNotFound will be returned. 93 func deleteResource(callAPI callAPIFunc, deleter resourceDeleter, resourceGroup, name string) error { 94 var result autorest.Response 95 if err := callAPI(func() (autorest.Response, error) { 96 var err error 97 result, err = deleter.Delete(resourceGroup, name, nil) 98 return result, err 99 }); err != nil { 100 if result.Response != nil && result.StatusCode == http.StatusNotFound { 101 return errors.NewNotFound(err, fmt.Sprintf("resource %q not found", name)) 102 } 103 return errors.Annotate(err, "canceling deployment") 104 } 105 return nil 106 } 107 108 type resourceDeleter interface { 109 Delete(resourceGroup, name string, cancel <-chan struct{}) (autorest.Response, error) 110 }