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  }