func backoffDuration(retryCount int) time.Duration {
	if retryCount > 7 {
		retryCount = 7
	}
	rand.Seed(time.Now().UnixNano())
	delay := (1 << uint(retryCount)) * (rand.Intn(150) + 500)
	return time.Duration(delay) * time.Millisecond
}
backoffDuration returns the duration to wait before retrying the request.
Duration is an exponential function of the retry count with a jitter of ~0-30%.
package lib
import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
	"golang.org/x/time/rate"
)
const (
	Version = "2.0.3"
	APIVersion = "v1"
	DefaultEndpoint = "https://api.vultr.com/"
	mediaType = "application/json"
)
var retryableStatusCodes = map[int]struct{}{
	503: {},  
	500: {},  
}
type Client struct {
	client *http.Client
	UserAgent string
	Endpoint *url.URL
	APIKey string
	MaxAttempts int
	limiter *rate.Limiter
	onRequestCompleted RequestCompletionCallback
}
type RequestCompletionCallback func(*http.Request, *http.Response)
type Options struct {
	HTTPClient *http.Client
	UserAgent string
	Endpoint string
	RateLimitation time.Duration
	MaxRetries int
}
func NewClient(apiKey string, options *Options) *Client {
	userAgent := "vultr-go/" + Version
	transport := &http.Transport{
		TLSNextProto: make(map[string]func(string, *tls.Conn) http.RoundTripper),
	}
	if len(os.Getenv("http_proxy")) > 0 {
		transport.Proxy = http.ProxyFromEnvironment
	}
	client := http.DefaultClient
	client.Transport = transport
	endpoint, _ := url.Parse(DefaultEndpoint)
	rateLimit := 505 * time.Millisecond
	attempts := 1
	if options != nil {
		if options.HTTPClient != nil {
			client = options.HTTPClient
		}
		if options.UserAgent != "" {
			userAgent = options.UserAgent
		}
		if options.Endpoint != "" {
			endpoint, _ = url.Parse(options.Endpoint)
		}
		if options.RateLimitation != 0 {
			rateLimit = options.RateLimitation
		}
		if options.MaxRetries != 0 {
			attempts = options.MaxRetries + 1
		}
	}
	return &Client{
		UserAgent:   userAgent,
		client:      client,
		Endpoint:    endpoint,
		APIKey:      apiKey,
		MaxAttempts: attempts,
		limiter: rate.NewLimiter(rate.Limit(float64(time.Second)/float64(rateLimit)), 1),
	}
}
func apiPath(path string) string {
	return fmt.Sprintf("/%s/%s", APIVersion, path)
}
func (c *Client) get(path string, data interface{}) error {
	req, err := c.newRequest("GET", apiPath(path), nil)
	if err != nil {
		return err
	}
	return c.do(req, data)
}
func (c *Client) post(path string, values url.Values, data interface{}) error {
	req, err := c.newRequest("POST", apiPath(path), strings.NewReader(values.Encode()))
	if err != nil {
		return err
	}
	return c.do(req, data)
}
func (c *Client) OnRequestCompleted(rc RequestCompletionCallback) {
	c.onRequestCompleted = rc
}
func (c *Client) newRequest(method string, path string, body io.Reader) (*http.Request, error) {
	relPath, err := url.Parse(path)
	if err != nil {
		return nil, err
	}
	url := c.Endpoint.ResolveReference(relPath)
	req, err := http.NewRequest(method, url.String(), body)
	if err != nil {
		return nil, err
	}
	req.Header.Add("API-Key", c.APIKey)
	req.Header.Add("User-Agent", c.UserAgent)
	req.Header.Add("Accept", mediaType)
	if req.Method == "POST" {
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	}
	return req, nil
}
func (c *Client) do(req *http.Request, data interface{}) error {
	c.limiter.Wait(context.Background())  
	var err error
	var requestBody []byte
	if req.Body != nil {
		requestBody, err = ioutil.ReadAll(req.Body)
		if err != nil {
			return fmt.Errorf("Error reading request body: %v", err)
		}
		req.Body.Close()
	}
	var apiError error
	for tryCount := 1; tryCount <= c.MaxAttempts; tryCount++ {
		if requestBody != nil {
			req.Body = ioutil.NopCloser(bytes.NewBuffer(requestBody))
		}
		resp, err := c.client.Do(req)
		if err != nil {
			return err
		}
		if c.onRequestCompleted != nil {
			c.onRequestCompleted(req, resp)
		}
		body, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			return err
		}
		if resp.StatusCode == http.StatusOK {
			if data != nil {
				if string(body) == `[]` {
					data = nil
				} else {
					if err := json.Unmarshal(body, data); err != nil {
						return err
					}
				}
			}
			return nil
		}
		apiError = errors.New(string(body))
		if !isCodeRetryable(resp.StatusCode) {
			break
		}
		delay := backoffDuration(tryCount)
		time.Sleep(delay)
	}
	return apiError
}