1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
|
package govultr
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
"github.com/hashicorp/go-retryablehttp"
)
const (
version = "2.17.2"
defaultBase = "https://api.vultr.com"
userAgent = "govultr/" + version
rateLimit = 500 * time.Millisecond
retryLimit = 3
)
// APIKey contains a users API Key for interacting with the API
type APIKey struct {
// API Key
key string
}
// RequestBody is used to create JSON bodies for one off calls
type RequestBody map[string]interface{}
// Client manages interaction with the Vultr V1 API
type Client struct {
// Http Client used to interact with the Vultr V1 API
client *retryablehttp.Client
// BASE URL for APIs
BaseURL *url.URL
// User Agent for the client
UserAgent string
// Services used to interact with the API
Account AccountService
Application ApplicationService
Backup BackupService
BareMetalServer BareMetalServerService
Billing BillingService
BlockStorage BlockStorageService
Domain DomainService
DomainRecord DomainRecordService
FirewallGroup FirewallGroupService
FirewallRule FireWallRuleService
Instance InstanceService
ISO ISOService
Kubernetes KubernetesService
LoadBalancer LoadBalancerService
// Deprecated: Network should no longer be used. Instead, use VPC.
Network NetworkService
ObjectStorage ObjectStorageService
OS OSService
Plan PlanService
Region RegionService
ReservedIP ReservedIPService
Snapshot SnapshotService
SSHKey SSHKeyService
StartupScript StartupScriptService
User UserService
VPC VPCService
// Optional function called after every successful request made to the Vultr API
onRequestCompleted RequestCompletionCallback
}
// RequestCompletionCallback defines the type of the request callback function
type RequestCompletionCallback func(*http.Request, *http.Response)
// NewClient returns a Vultr API Client
func NewClient(httpClient *http.Client) *Client {
if httpClient == nil {
httpClient = http.DefaultClient
}
baseURL, _ := url.Parse(defaultBase)
client := &Client{
client: retryablehttp.NewClient(),
BaseURL: baseURL,
UserAgent: userAgent,
}
client.client.HTTPClient = httpClient
client.client.Logger = nil
client.client.ErrorHandler = client.vultrErrorHandler
client.SetRetryLimit(retryLimit)
client.SetRateLimit(rateLimit)
client.Account = &AccountServiceHandler{client}
client.Application = &ApplicationServiceHandler{client}
client.Backup = &BackupServiceHandler{client}
client.BareMetalServer = &BareMetalServerServiceHandler{client}
client.Billing = &BillingServiceHandler{client}
client.BlockStorage = &BlockStorageServiceHandler{client}
client.Domain = &DomainServiceHandler{client}
client.DomainRecord = &DomainRecordsServiceHandler{client}
client.FirewallGroup = &FireWallGroupServiceHandler{client}
client.FirewallRule = &FireWallRuleServiceHandler{client}
client.Instance = &InstanceServiceHandler{client}
client.ISO = &ISOServiceHandler{client}
client.Kubernetes = &KubernetesHandler{client}
client.LoadBalancer = &LoadBalancerHandler{client}
client.Network = &NetworkServiceHandler{client}
client.ObjectStorage = &ObjectStorageServiceHandler{client}
client.OS = &OSServiceHandler{client}
client.Plan = &PlanServiceHandler{client}
client.Region = &RegionServiceHandler{client}
client.ReservedIP = &ReservedIPServiceHandler{client}
client.Snapshot = &SnapshotServiceHandler{client}
client.SSHKey = &SSHKeyServiceHandler{client}
client.StartupScript = &StartupScriptServiceHandler{client}
client.User = &UserServiceHandler{client}
client.VPC = &VPCServiceHandler{client}
return client
}
// NewRequest creates an API Request
func (c *Client) NewRequest(ctx context.Context, method, uri string, body interface{}) (*http.Request, error) {
resolvedURL, err := c.BaseURL.Parse(uri)
if err != nil {
return nil, err
}
buf := new(bytes.Buffer)
if body != nil {
if err = json.NewEncoder(buf).Encode(body); err != nil {
return nil, err
}
}
req, err := http.NewRequest(method, resolvedURL.String(), buf)
if err != nil {
return nil, err
}
req.Header.Add("User-Agent", c.UserAgent)
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/json")
return req, nil
}
// DoWithContext sends an API Request and returns back the response. The API response is checked to see if it was
// a successful call. A successful call is then checked to see if we need to unmarshal since some resources
// have their own implements of unmarshal.
func (c *Client) DoWithContext(ctx context.Context, r *http.Request, data interface{}) error {
rreq, err := retryablehttp.FromRequest(r)
if err != nil {
return err
}
rreq = rreq.WithContext(ctx)
res, err := c.client.Do(rreq)
if c.onRequestCompleted != nil {
c.onRequestCompleted(r, res)
}
if err != nil {
return err
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
if res.StatusCode >= http.StatusOK && res.StatusCode <= http.StatusNoContent {
if data != nil {
if err := json.Unmarshal(body, data); err != nil {
return err
}
}
return nil
}
return errors.New(string(body))
}
// SetBaseURL Overrides the default BaseUrl
func (c *Client) SetBaseURL(baseURL string) error {
updatedURL, err := url.Parse(baseURL)
if err != nil {
return err
}
c.BaseURL = updatedURL
return nil
}
// SetRateLimit Overrides the default rateLimit. For performance, exponential
// backoff is used with the minimum wait being 2/3rds the time provided.
func (c *Client) SetRateLimit(time time.Duration) {
c.client.RetryWaitMin = time / 3 * 2
c.client.RetryWaitMax = time
}
// SetUserAgent Overrides the default UserAgent
func (c *Client) SetUserAgent(ua string) {
c.UserAgent = ua
}
// OnRequestCompleted sets the API request completion callback
func (c *Client) OnRequestCompleted(rc RequestCompletionCallback) {
c.onRequestCompleted = rc
}
// SetRetryLimit overrides the default RetryLimit
func (c *Client) SetRetryLimit(n int) {
c.client.RetryMax = n
}
func (c *Client) vultrErrorHandler(resp *http.Response, err error, numTries int) (*http.Response, error) {
if resp == nil {
if err != nil {
return nil, fmt.Errorf("gave up after %d attempts, last error : %s", numTries, err.Error())
}
return nil, fmt.Errorf("gave up after %d attempts, last error unavailable (resp == nil)", numTries)
}
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("gave up after %d attempts, last error unavailable (error reading response body: %v)", numTries, err)
}
return nil, fmt.Errorf("gave up after %d attempts, last error: %#v", numTries, strings.TrimSpace(string(buf)))
}
// BoolToBoolPtr helper function that returns a pointer from your bool value
func BoolToBoolPtr(value bool) *bool {
return &value
}
// StringToStringPtr helper function that returns a pointer from your string value
func StringToStringPtr(value string) *string {
return &value
}
// IntToIntPtr helper function that returns a pointer from your string value
func IntToIntPtr(value int) *int {
return &value
}
|