From 87be19588b640e0279088dbc4600e4268adb57e9 Mon Sep 17 00:00:00 2001 From: David Dymko Date: Wed, 17 Jul 2019 15:01:50 -0400 Subject: [PATCH] Switch Vultr to official client (#929) --- Gopkg.lock | 26 +- Gopkg.toml | 4 + docs/content/dns/zz_gen_vultr.md | 2 +- providers/dns/vultr/vultr.go | 27 +- providers/dns/vultr/vultr.toml | 2 +- .../JamesClonk/vultr/lib/account_info.go | 71 - .../JamesClonk/vultr/lib/applications.go | 38 - .../JamesClonk/vultr/lib/block_storage.go | 210 --- .../github.com/JamesClonk/vultr/lib/client.go | 258 --- vendor/github.com/JamesClonk/vultr/lib/dns.go | 150 -- .../JamesClonk/vultr/lib/firewall.go | 252 --- vendor/github.com/JamesClonk/vultr/lib/ip.go | 192 --- vendor/github.com/JamesClonk/vultr/lib/iso.go | 44 - vendor/github.com/JamesClonk/vultr/lib/os.go | 37 - .../github.com/JamesClonk/vultr/lib/plans.go | 78 - .../JamesClonk/vultr/lib/regions.go | 44 - .../JamesClonk/vultr/lib/reservedip.go | 192 --- .../JamesClonk/vultr/lib/scripts.go | 126 -- .../JamesClonk/vultr/lib/servers.go | 561 ------- .../JamesClonk/vultr/lib/snapshots.go | 72 - .../JamesClonk/vultr/lib/sshkeys.go | 82 - vendor/github.com/juju/ratelimit/LICENSE | 191 --- vendor/github.com/juju/ratelimit/ratelimit.go | 344 ---- vendor/github.com/juju/ratelimit/reader.go | 51 - .../vultr => vultr/govultr}/LICENSE | 5 +- vendor/github.com/vultr/govultr/account.go | 45 + vendor/github.com/vultr/govultr/api.go | 44 + .../github.com/vultr/govultr/application.go | 51 + vendor/github.com/vultr/govultr/backup.go | 105 ++ .../vultr/govultr/bare_metal_server.go | 790 +++++++++ .../github.com/vultr/govultr/block_storage.go | 319 ++++ .../github.com/vultr/govultr/dns_domains.go | 209 +++ .../github.com/vultr/govultr/dns_records.go | 150 ++ .../vultr/govultr/firewall_group.go | 163 ++ .../github.com/vultr/govultr/firewall_rule.go | 265 +++ vendor/github.com/vultr/govultr/govultr.go | 222 +++ vendor/github.com/vultr/govultr/iso.go | 142 ++ vendor/github.com/vultr/govultr/network.go | 120 ++ vendor/github.com/vultr/govultr/os.go | 84 + vendor/github.com/vultr/govultr/plans.go | 199 +++ vendor/github.com/vultr/govultr/regions.go | 154 ++ .../github.com/vultr/govultr/reserved_ip.go | 273 +++ vendor/github.com/vultr/govultr/server.go | 1469 +++++++++++++++++ vendor/github.com/vultr/govultr/snapshot.go | 168 ++ vendor/github.com/vultr/govultr/ssh_key.go | 140 ++ .../vultr/govultr/startup_script.go | 172 ++ vendor/github.com/vultr/govultr/user.go | 157 ++ 47 files changed, 5471 insertions(+), 3029 deletions(-) delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/account_info.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/applications.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/block_storage.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/client.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/dns.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/firewall.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/ip.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/iso.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/os.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/plans.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/regions.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/reservedip.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/scripts.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/servers.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/snapshots.go delete mode 100644 vendor/github.com/JamesClonk/vultr/lib/sshkeys.go delete mode 100644 vendor/github.com/juju/ratelimit/LICENSE delete mode 100644 vendor/github.com/juju/ratelimit/ratelimit.go delete mode 100644 vendor/github.com/juju/ratelimit/reader.go rename vendor/github.com/{JamesClonk/vultr => vultr/govultr}/LICENSE (94%) create mode 100644 vendor/github.com/vultr/govultr/account.go create mode 100644 vendor/github.com/vultr/govultr/api.go create mode 100644 vendor/github.com/vultr/govultr/application.go create mode 100644 vendor/github.com/vultr/govultr/backup.go create mode 100644 vendor/github.com/vultr/govultr/bare_metal_server.go create mode 100644 vendor/github.com/vultr/govultr/block_storage.go create mode 100644 vendor/github.com/vultr/govultr/dns_domains.go create mode 100644 vendor/github.com/vultr/govultr/dns_records.go create mode 100644 vendor/github.com/vultr/govultr/firewall_group.go create mode 100644 vendor/github.com/vultr/govultr/firewall_rule.go create mode 100644 vendor/github.com/vultr/govultr/govultr.go create mode 100644 vendor/github.com/vultr/govultr/iso.go create mode 100644 vendor/github.com/vultr/govultr/network.go create mode 100644 vendor/github.com/vultr/govultr/os.go create mode 100644 vendor/github.com/vultr/govultr/plans.go create mode 100644 vendor/github.com/vultr/govultr/regions.go create mode 100644 vendor/github.com/vultr/govultr/reserved_ip.go create mode 100644 vendor/github.com/vultr/govultr/server.go create mode 100644 vendor/github.com/vultr/govultr/snapshot.go create mode 100644 vendor/github.com/vultr/govultr/ssh_key.go create mode 100644 vendor/github.com/vultr/govultr/startup_script.go create mode 100644 vendor/github.com/vultr/govultr/user.go diff --git a/Gopkg.lock b/Gopkg.lock index d599d5aa..e8276d00 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -55,14 +55,6 @@ revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005" version = "v0.3.1" -[[projects]] - digest = "1:ed3fc9992df610d07c85c24e0b792268cc1ce226dd9bf8cb2e6ad9a377b35415" - name = "github.com/JamesClonk/vultr" - packages = ["lib"] - pruneopts = "NUT" - revision = "fa1c0367800db75e4d10d0ec90c49a8731670224" - version = "1.15.0" - [[projects]] branch = "master" digest = "1:a4068a93355ba3cff0a719425713123d23c90010cb4d023b40c679a22465736d" @@ -367,14 +359,6 @@ revision = "1624edc4454b8682399def8740d46db5e4362ba4" version = "v1.1.5" -[[projects]] - digest = "1:8b3234b10eacd5edea45bf0c13a585b608749da23f94aaf29b46d9ef8a8babf4" - name = "github.com/juju/ratelimit" - packages = ["."] - pruneopts = "NUT" - revision = "59fac5042749a5afb9af70e813da1dd5474f0167" - version = "1.0.1" - [[projects]] branch = "master" digest = "1:ec142582cd3bb5cc29a2bc7181a6e67367b90b19f6a957ce506dcd7d1500bf95" @@ -599,6 +583,14 @@ revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1" version = "v1.20.0" +[[projects]] + digest = "1:b1d2c51a689eef501cb5726f8d9997c0ca4415cbfa7105fe6e64b1844eddf1fb" + name = "github.com/vultr/govultr" + packages = ["."] + pruneopts = "NUT" + revision = "ca447e056e08d93aa6e5b09e6ae3565dd1825281" + version = "v0.1.4" + [[projects]] digest = "1:58f2854b50ff8862eb6a347f20dedaac83e1166f4040472e17bc37736841a12f" name = "go.opencensus.io" @@ -885,7 +877,6 @@ "github.com/Azure/go-autorest/autorest/azure/auth", "github.com/Azure/go-autorest/autorest/to", "github.com/BurntSushi/toml", - "github.com/JamesClonk/vultr/lib", "github.com/OpenDNS/vegadns2client", "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1", "github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid", @@ -934,6 +925,7 @@ "github.com/transip/gotransip", "github.com/transip/gotransip/domain", "github.com/urfave/cli", + "github.com/vultr/govultr", "golang.org/x/crypto/ocsp", "golang.org/x/net/context", "golang.org/x/net/idna", diff --git a/Gopkg.toml b/Gopkg.toml index c4717326..b53f97a7 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -132,3 +132,7 @@ [[constraint]] name = "github.com/nrdcg/namesilo" version = "0.2.0" + +[[constraint]] + name = "github.com/vultr/govultr" + version = "0.1.4" diff --git a/docs/content/dns/zz_gen_vultr.md b/docs/content/dns/zz_gen_vultr.md index f458a566..7e43ee18 100644 --- a/docs/content/dns/zz_gen_vultr.md +++ b/docs/content/dns/zz_gen_vultr.md @@ -53,7 +53,7 @@ More information [here](/lego/dns/#configuration-and-credentials). ## More information - [API documentation](https://www.vultr.com/api/#dns) -- [Go client](https://github.com/JamesClonk/vultr) +- [Go client](https://github.com/vultr/govultr) diff --git a/providers/dns/vultr/vultr.go b/providers/dns/vultr/vultr.go index 94e5d863..3760663f 100644 --- a/providers/dns/vultr/vultr.go +++ b/providers/dns/vultr/vultr.go @@ -1,18 +1,20 @@ -// Package vultr implements a DNS provider for solving the DNS-01 challenge using the vultr DNS. +// Package vultr implements a DNS provider for solving the DNS-01 challenge using the Vultr DNS. // See https://www.vultr.com/api/#dns package vultr import ( + "context" "crypto/tls" "errors" "fmt" "net/http" + "strconv" "strings" "time" - vultr "github.com/JamesClonk/vultr/lib" "github.com/go-acme/lego/challenge/dns01" "github.com/go-acme/lego/platform/config/env" + "github.com/vultr/govultr" ) // Config is used to configure the creation of the DNSProvider @@ -43,7 +45,7 @@ func NewDefaultConfig() *Config { // DNSProvider is an implementation of the acme.ChallengeProvider interface. type DNSProvider struct { config *Config - client *vultr.Client + client *govultr.Client } // NewDNSProvider returns a DNSProvider instance with a configured Vultr client. @@ -70,10 +72,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return nil, fmt.Errorf("vultr: credentials missing") } - options := &vultr.Options{ - HTTPClient: config.HTTPClient, - } - client := vultr.NewClient(config.APIKey, options) + client := govultr.NewClient(config.HTTPClient, config.APIKey) return &DNSProvider{client: client, config: config}, nil } @@ -89,7 +88,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { name := d.extractRecordName(fqdn, zoneDomain) - err = d.client.CreateDNSRecord(zoneDomain, name, "TXT", `"`+value+`"`, 0, d.config.TTL) + err = d.client.DNSRecord.Create(context.Background(), zoneDomain, name, "TXT", value, d.config.TTL, 0) if err != nil { return fmt.Errorf("vultr: API call failed: %v", err) } @@ -108,7 +107,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { var allErr []string for _, rec := range records { - err := d.client.DeleteDNSRecord(zoneDomain, rec.RecordID) + err := d.client.DNSRecord.Delete(context.Background(), zoneDomain, strconv.Itoa(rec.RecordID)) if err != nil { allErr = append(allErr, err.Error()) } @@ -128,12 +127,12 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { } func (d *DNSProvider) getHostedZone(domain string) (string, error) { - domains, err := d.client.GetDNSDomains() + domains, err := d.client.DNSDomain.List(context.Background()) if err != nil { return "", fmt.Errorf("API call failed: %v", err) } - var hostedDomain vultr.DNSDomain + var hostedDomain govultr.DNSDomain for _, dom := range domains { if strings.HasSuffix(domain, dom.Domain) { if len(dom.Domain) > len(hostedDomain.Domain) { @@ -148,14 +147,14 @@ func (d *DNSProvider) getHostedZone(domain string) (string, error) { return hostedDomain.Domain, nil } -func (d *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRecord, error) { +func (d *DNSProvider) findTxtRecords(domain, fqdn string) (string, []govultr.DNSRecord, error) { zoneDomain, err := d.getHostedZone(domain) if err != nil { return "", nil, err } - var records []vultr.DNSRecord - result, err := d.client.GetDNSRecords(zoneDomain) + var records []govultr.DNSRecord + result, err := d.client.DNSRecord.List(context.Background(), zoneDomain) if err != nil { return "", records, fmt.Errorf("API call has failed: %v", err) } diff --git a/providers/dns/vultr/vultr.toml b/providers/dns/vultr/vultr.toml index b2104074..8e59a0d5 100644 --- a/providers/dns/vultr/vultr.toml +++ b/providers/dns/vultr/vultr.toml @@ -17,4 +17,4 @@ Example = '''''' [Links] API = "https://www.vultr.com/api/#dns" - GoClient = "https://github.com/JamesClonk/vultr" + GoClient = "https://github.com/vultr/govultr" diff --git a/vendor/github.com/JamesClonk/vultr/lib/account_info.go b/vendor/github.com/JamesClonk/vultr/lib/account_info.go deleted file mode 100644 index f994f924..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/account_info.go +++ /dev/null @@ -1,71 +0,0 @@ -package lib - -import ( - "encoding/json" - "fmt" - "strconv" -) - -// AccountInfo of Vultr account -type AccountInfo struct { - Balance float64 `json:"balance"` - PendingCharges float64 `json:"pending_charges"` - LastPaymentDate string `json:"last_payment_date"` - LastPaymentAmount float64 `json:"last_payment_amount"` -} - -// GetAccountInfo retrieves the Vultr account information about current balance, pending charges, etc.. -func (c *Client) GetAccountInfo() (info AccountInfo, err error) { - if err := c.get(`account/info`, &info); err != nil { - return AccountInfo{}, err - } - return -} - -// UnmarshalJSON implements json.Unmarshaller on AccountInfo. -// This is needed because the Vultr API is inconsistent in it's JSON responses for account info. -// Some fields can change type, from JSON number to JSON string and vice-versa. -func (a *AccountInfo) UnmarshalJSON(data []byte) (err error) { - if a == nil { - *a = AccountInfo{} - } - - var fields map[string]interface{} - if err := json.Unmarshal(data, &fields); err != nil { - return err - } - - value := fmt.Sprintf("%v", fields["balance"]) - if len(value) == 0 || value == "" { - value = "0" - } - b, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - a.Balance = b - - value = fmt.Sprintf("%v", fields["pending_charges"]) - if len(value) == 0 || value == "" { - value = "0" - } - pc, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - a.PendingCharges = pc - - value = fmt.Sprintf("%v", fields["last_payment_amount"]) - if len(value) == 0 || value == "" { - value = "0" - } - lpa, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - a.LastPaymentAmount = lpa - - a.LastPaymentDate = fmt.Sprintf("%v", fields["last_payment_date"]) - - return -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/applications.go b/vendor/github.com/JamesClonk/vultr/lib/applications.go deleted file mode 100644 index f44d8c81..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/applications.go +++ /dev/null @@ -1,38 +0,0 @@ -package lib - -import ( - "sort" - "strings" -) - -// Application on Vultr -type Application struct { - ID string `json:"APPID"` - Name string `json:"name"` - ShortName string `json:"short_name"` - DeployName string `json:"deploy_name"` - Surcharge float64 `json:"surcharge"` -} - -type applications []Application - -func (s applications) Len() int { return len(s) } -func (s applications) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s applications) Less(i, j int) bool { - return strings.ToLower(s[i].Name) < strings.ToLower(s[j].Name) -} - -// GetApplications returns a list of all available applications on Vultr -func (c *Client) GetApplications() ([]Application, error) { - var appMap map[string]Application - if err := c.get(`app/list`, &appMap); err != nil { - return nil, err - } - - var appList []Application - for _, app := range appMap { - appList = append(appList, app) - } - sort.Sort(applications(appList)) - return appList, nil -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/block_storage.go b/vendor/github.com/JamesClonk/vultr/lib/block_storage.go deleted file mode 100644 index e9e8e115..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/block_storage.go +++ /dev/null @@ -1,210 +0,0 @@ -package lib - -import ( - "encoding/json" - "fmt" - "net/url" - "sort" - "strconv" - "strings" -) - -// BlockStorage on Vultr account -type BlockStorage struct { - ID string `json:"SUBID,string"` - Name string `json:"label"` - RegionID int `json:"DCID,string"` - SizeGB int `json:"size_gb,string"` - Created string `json:"date_created"` - Cost string `json:"cost_per_month"` - Status string `json:"status"` - AttachedTo string `json:"attached_to_SUBID"` -} - -type blockstorages []BlockStorage - -func (b blockstorages) Len() int { return len(b) } -func (b blockstorages) Swap(i, j int) { b[i], b[j] = b[j], b[i] } -func (b blockstorages) Less(i, j int) bool { - // sort order: name, size, status - if strings.ToLower(b[i].Name) < strings.ToLower(b[j].Name) { - return true - } else if strings.ToLower(b[i].Name) > strings.ToLower(b[j].Name) { - return false - } - if b[i].SizeGB < b[j].SizeGB { - return true - } else if b[i].SizeGB > b[j].SizeGB { - return false - } - return b[i].Status < b[j].Status -} - -// UnmarshalJSON implements json.Unmarshaller on BlockStorage. -// This is needed because the Vultr API is inconsistent in it's JSON responses. -// Some fields can change type, from JSON number to JSON string and vice-versa. -func (b *BlockStorage) UnmarshalJSON(data []byte) (err error) { - if b == nil { - *b = BlockStorage{} - } - - var fields map[string]interface{} - if err := json.Unmarshal(data, &fields); err != nil { - return err - } - - value := fmt.Sprintf("%v", fields["SUBID"]) - if len(value) == 0 || value == "" || value == "0" { - b.ID = "" - } else { - id, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - b.ID = strconv.FormatFloat(id, 'f', -1, 64) - } - - value = fmt.Sprintf("%v", fields["DCID"]) - if len(value) == 0 || value == "" { - value = "0" - } - region, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - b.RegionID = int(region) - - value = fmt.Sprintf("%v", fields["size_gb"]) - if len(value) == 0 || value == "" { - value = "0" - } - size, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - b.SizeGB = int(size) - - value = fmt.Sprintf("%v", fields["attached_to_SUBID"]) - if len(value) == 0 || value == "" || value == "0" { - b.AttachedTo = "" - } else { - attached, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - b.AttachedTo = strconv.FormatFloat(attached, 'f', -1, 64) - } - - b.Name = fmt.Sprintf("%v", fields["label"]) - b.Created = fmt.Sprintf("%v", fields["date_created"]) - b.Status = fmt.Sprintf("%v", fields["status"]) - b.Cost = fmt.Sprintf("%v", fields["cost_per_month"]) - - return -} - -// GetBlockStorages returns a list of all active block storages on Vultr account -func (c *Client) GetBlockStorages() (storages []BlockStorage, err error) { - if err := c.get(`block/list`, &storages); err != nil { - return nil, err - } - sort.Sort(blockstorages(storages)) - return storages, nil -} - -// GetBlockStorage returns block storage with given ID -func (c *Client) GetBlockStorage(id string) (BlockStorage, error) { - storages, err := c.GetBlockStorages() - if err != nil { - return BlockStorage{}, err - } - - for _, s := range storages { - if s.ID == id { - return s, nil - } - } - return BlockStorage{}, fmt.Errorf("BlockStorage with ID %v not found", id) -} - -// CreateBlockStorage creates a new block storage on Vultr account -func (c *Client) CreateBlockStorage(name string, regionID, size int) (BlockStorage, error) { - values := url.Values{ - "label": {name}, - "DCID": {fmt.Sprintf("%v", regionID)}, - "size_gb": {fmt.Sprintf("%v", size)}, - } - - var storage BlockStorage - if err := c.post(`block/create`, values, &storage); err != nil { - return BlockStorage{}, err - } - storage.RegionID = regionID - storage.Name = name - storage.SizeGB = size - - return storage, nil -} - -// ResizeBlockStorage resizes an existing block storage -func (c *Client) ResizeBlockStorage(id string, size int) error { - values := url.Values{ - "SUBID": {id}, - "size_gb": {fmt.Sprintf("%v", size)}, - } - - if err := c.post(`block/resize`, values, nil); err != nil { - return err - } - return nil -} - -// LabelBlockStorage changes the label on an existing block storage -func (c *Client) LabelBlockStorage(id, name string) error { - values := url.Values{ - "SUBID": {id}, - "label": {name}, - } - - if err := c.post(`block/label_set`, values, nil); err != nil { - return err - } - return nil -} - -// AttachBlockStorage attaches block storage to an existing virtual machine -func (c *Client) AttachBlockStorage(id, serverID string) error { - values := url.Values{ - "SUBID": {id}, - "attach_to_SUBID": {serverID}, - } - - if err := c.post(`block/attach`, values, nil); err != nil { - return err - } - return nil -} - -// DetachBlockStorage detaches block storage from virtual machine -func (c *Client) DetachBlockStorage(id string) error { - values := url.Values{ - "SUBID": {id}, - } - - if err := c.post(`block/detach`, values, nil); err != nil { - return err - } - return nil -} - -// DeleteBlockStorage deletes an existing block storage -func (c *Client) DeleteBlockStorage(id string) error { - values := url.Values{ - "SUBID": {id}, - } - - if err := c.post(`block/delete`, values, nil); err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/client.go b/vendor/github.com/JamesClonk/vultr/lib/client.go deleted file mode 100644 index 6d565abc..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/client.go +++ /dev/null @@ -1,258 +0,0 @@ -package lib - -import ( - "bytes" - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "math/rand" - "net/http" - "net/url" - "strings" - "time" - - "github.com/juju/ratelimit" -) - -const ( - // Version of this libary - Version = "1.15.0" - - // APIVersion of Vultr - APIVersion = "v1" - - // DefaultEndpoint to be used - DefaultEndpoint = "https://api.vultr.com/" - - mediaType = "application/json" -) - -// retryableStatusCodes are API response status codes that indicate that -// the failed request can be retried without further actions. -var retryableStatusCodes = map[int]struct{}{ - 503: {}, // Rate limit hit - 500: {}, // Internal server error. Try again at a later time. -} - -// Client represents the Vultr API client -type Client struct { - // HTTP client for communication with the Vultr API - client *http.Client - - // User agent for HTTP client - UserAgent string - - // Endpoint URL for API requests - Endpoint *url.URL - - // API key for accessing the Vultr API - APIKey string - - // Max. number of request attempts - MaxAttempts int - - // Throttling struct - bucket *ratelimit.Bucket - - // Optional function called after every successful request made to the API - onRequestCompleted RequestCompletionCallback -} - -// RequestCompletionCallback defines the type of the request callback function -type RequestCompletionCallback func(*http.Request, *http.Response) - -// Options represents optional settings and flags that can be passed to NewClient -type Options struct { - // HTTP client for communication with the Vultr API - HTTPClient *http.Client - - // User agent for HTTP client - UserAgent string - - // Endpoint URL for API requests - Endpoint string - - // API rate limitation, calls per duration - RateLimitation time.Duration - - // Max. number of times to retry API calls - MaxRetries int -} - -// NewClient creates new Vultr API client. Options are optional and can be nil. -func NewClient(apiKey string, options *Options) *Client { - userAgent := "vultr-go/" + Version - transport := &http.Transport{ - TLSNextProto: make(map[string]func(string, *tls.Conn) http.RoundTripper), - } - client := http.DefaultClient - client.Transport = transport - endpoint, _ := url.Parse(DefaultEndpoint) - rate := 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 { - rate = options.RateLimitation - } - if options.MaxRetries != 0 { - attempts = options.MaxRetries + 1 - } - } - - return &Client{ - UserAgent: userAgent, - client: client, - Endpoint: endpoint, - APIKey: apiKey, - MaxAttempts: attempts, - bucket: ratelimit.NewBucket(rate, 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) -} - -// OnRequestCompleted sets the API request completion callback -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 { - // Throttle http requests to avoid hitting Vultr's API rate-limit - c.bucket.Wait(1) - - // Request body gets drained on each read so we - // need to save it's content for retrying requests - 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++ { - // Restore request body to the original state - 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 { - // avoid unmarshalling problem because Vultr API returns - // empty array instead of empty map when it shouldn't! - 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 -} - -// 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%. -func backoffDuration(retryCount int) time.Duration { - // Upper limit of delay at ~1 minute - if retryCount > 7 { - retryCount = 7 - } - - rand.Seed(time.Now().UnixNano()) - delay := (1 << uint(retryCount)) * (rand.Intn(150) + 500) - return time.Duration(delay) * time.Millisecond -} - -// isCodeRetryable returns true if the given status code means that we should retry. -func isCodeRetryable(statusCode int) bool { - if _, ok := retryableStatusCodes[statusCode]; ok { - return true - } - - return false -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/dns.go b/vendor/github.com/JamesClonk/vultr/lib/dns.go deleted file mode 100644 index e66275b6..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/dns.go +++ /dev/null @@ -1,150 +0,0 @@ -package lib - -import ( - "fmt" - "net/url" - "sort" - "strings" -) - -// DNSDomain represents a DNS domain on Vultr -type DNSDomain struct { - Domain string `json:"domain"` - Created string `json:"date_created"` -} - -type dnsdomains []DNSDomain - -func (d dnsdomains) Len() int { return len(d) } -func (d dnsdomains) Swap(i, j int) { d[i], d[j] = d[j], d[i] } -func (d dnsdomains) Less(i, j int) bool { - return strings.ToLower(d[i].Domain) < strings.ToLower(d[j].Domain) -} - -// DNSRecord represents a DNS record on Vultr -type DNSRecord struct { - RecordID int `json:"RECORDID"` - Type string `json:"type"` - Name string `json:"name"` - Data string `json:"data"` - Priority int `json:"priority"` - TTL int `json:"ttl"` -} - -type dnsrecords []DNSRecord - -func (d dnsrecords) Len() int { return len(d) } -func (d dnsrecords) Swap(i, j int) { d[i], d[j] = d[j], d[i] } -func (d dnsrecords) Less(i, j int) bool { - // sort order: type, data, name - if d[i].Type < d[j].Type { - return true - } else if d[i].Type > d[j].Type { - return false - } - if d[i].Data < d[j].Data { - return true - } else if d[i].Data > d[j].Data { - return false - } - return strings.ToLower(d[i].Name) < strings.ToLower(d[j].Name) -} - -// GetDNSDomains returns a list of available domains on Vultr account -func (c *Client) GetDNSDomains() (domains []DNSDomain, err error) { - if err := c.get(`dns/list`, &domains); err != nil { - return nil, err - } - sort.Sort(dnsdomains(domains)) - return domains, nil -} - -// GetDNSRecords returns a list of all DNS records of a particular domain -func (c *Client) GetDNSRecords(domain string) (records []DNSRecord, err error) { - if err := c.get(`dns/records?domain=`+domain, &records); err != nil { - return nil, err - } - sort.Sort(dnsrecords(records)) - return records, nil -} - -// CreateDNSDomain creates a new DNS domain name on Vultr -func (c *Client) CreateDNSDomain(domain, serverIP string) error { - values := url.Values{ - "domain": {domain}, - "serverip": {serverIP}, - } - - if err := c.post(`dns/create_domain`, values, nil); err != nil { - return err - } - return nil -} - -// DeleteDNSDomain deletes an existing DNS domain name -func (c *Client) DeleteDNSDomain(domain string) error { - values := url.Values{ - "domain": {domain}, - } - - if err := c.post(`dns/delete_domain`, values, nil); err != nil { - return err - } - return nil -} - -// CreateDNSRecord creates a new DNS record -func (c *Client) CreateDNSRecord(domain, name, rtype, data string, priority, ttl int) error { - values := url.Values{ - "domain": {domain}, - "name": {name}, - "type": {rtype}, - "data": {data}, - "priority": {fmt.Sprintf("%v", priority)}, - "ttl": {fmt.Sprintf("%v", ttl)}, - } - - if err := c.post(`dns/create_record`, values, nil); err != nil { - return err - } - return nil -} - -// UpdateDNSRecord updates an existing DNS record -func (c *Client) UpdateDNSRecord(domain string, dnsrecord DNSRecord) error { - values := url.Values{ - "domain": {domain}, - "RECORDID": {fmt.Sprintf("%v", dnsrecord.RecordID)}, - } - - if dnsrecord.Name != "" { - values.Add("name", dnsrecord.Name) - } - if dnsrecord.Data != "" { - values.Add("data", dnsrecord.Data) - } - if dnsrecord.Priority != 0 { - values.Add("priority", fmt.Sprintf("%v", dnsrecord.Priority)) - } - if dnsrecord.TTL != 0 { - values.Add("ttl", fmt.Sprintf("%v", dnsrecord.TTL)) - } - - if err := c.post(`dns/update_record`, values, nil); err != nil { - return err - } - return nil -} - -// DeleteDNSRecord deletes an existing DNS record -func (c *Client) DeleteDNSRecord(domain string, recordID int) error { - values := url.Values{ - "domain": {domain}, - "RECORDID": {fmt.Sprintf("%v", recordID)}, - } - - if err := c.post(`dns/delete_record`, values, nil); err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/firewall.go b/vendor/github.com/JamesClonk/vultr/lib/firewall.go deleted file mode 100644 index 08a11d14..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/firewall.go +++ /dev/null @@ -1,252 +0,0 @@ -package lib - -import ( - "encoding/json" - "fmt" - "net" - "net/url" - "sort" - "strconv" - "strings" -) - -// FirewallGroup represents a firewall group on Vultr -type FirewallGroup struct { - ID string `json:"FIREWALLGROUPID"` - Description string `json:"description"` - Created string `json:"date_created"` - Modified string `json:"date_modified"` - InstanceCount int `json:"instance_count"` - RuleCount int `json:"rule_count"` - MaxRuleCount int `json:"max_rule_count"` -} - -// FirewallRule represents a firewall rule on Vultr -type FirewallRule struct { - RuleNumber int `json:"rulenumber"` - Action string `json:"action"` - Protocol string `json:"protocol"` - Port string `json:"port"` - Network *net.IPNet -} - -type firewallGroups []FirewallGroup - -func (f firewallGroups) Len() int { return len(f) } -func (f firewallGroups) Swap(i, j int) { f[i], f[j] = f[j], f[i] } -func (f firewallGroups) Less(i, j int) bool { - // sort order: description - return strings.ToLower(f[i].Description) < strings.ToLower(f[j].Description) -} - -type firewallRules []FirewallRule - -func (r firewallRules) Len() int { return len(r) } -func (r firewallRules) Swap(i, j int) { r[i], r[j] = r[j], r[i] } -func (r firewallRules) Less(i, j int) bool { - // sort order: rule number - return r[i].RuleNumber < r[j].RuleNumber -} - -// UnmarshalJSON implements json.Unmarshaller on FirewallRule. -// This is needed because the Vultr API is inconsistent in it's JSON responses. -// Some fields can change type, from JSON number to JSON string and vice-versa. -func (r *FirewallRule) UnmarshalJSON(data []byte) (err error) { - if r == nil { - *r = FirewallRule{} - } - - var fields map[string]interface{} - if err := json.Unmarshal(data, &fields); err != nil { - return err - } - - value := fmt.Sprintf("%v", fields["rulenumber"]) - if len(value) == 0 || value == "" { - value = "0" - } - number, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - r.RuleNumber = int(number) - - value = fmt.Sprintf("%v", fields["subnet_size"]) - if len(value) == 0 || value == "" { - value = "0" - } - subnetSize, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - - r.Action = fmt.Sprintf("%v", fields["action"]) - r.Protocol = fmt.Sprintf("%v", fields["protocol"]) - r.Port = fmt.Sprintf("%v", fields["port"]) - subnet := fmt.Sprintf("%v", fields["subnet"]) - if subnet == "" { - subnet = "" - } - - if len(subnet) > 0 { - _, r.Network, err = net.ParseCIDR(fmt.Sprintf("%s/%d", subnet, subnetSize)) - if err != nil { - return fmt.Errorf("Failed to parse subnet from Vultr API") - } - } else { - // This case is used to create a valid default CIDR when the Vultr API does not return a subnet/subnet size at all, e.g. the response after creating a new rule. - _, r.Network, _ = net.ParseCIDR("0.0.0.0/0") - } - - return -} - -// GetFirewallGroups returns a list of all available firewall groups on Vultr -func (c *Client) GetFirewallGroups() ([]FirewallGroup, error) { - var groupMap map[string]FirewallGroup - if err := c.get(`firewall/group_list`, &groupMap); err != nil { - return nil, err - } - - var groupList []FirewallGroup - for _, g := range groupMap { - groupList = append(groupList, g) - } - sort.Sort(firewallGroups(groupList)) - return groupList, nil -} - -// GetFirewallGroup returns the firewall group with given ID -func (c *Client) GetFirewallGroup(id string) (FirewallGroup, error) { - groups, err := c.GetFirewallGroups() - if err != nil { - return FirewallGroup{}, err - } - - for _, g := range groups { - if g.ID == id { - return g, nil - } - } - return FirewallGroup{}, fmt.Errorf("Firewall group with ID %v not found", id) -} - -// CreateFirewallGroup creates a new firewall group in Vultr account -func (c *Client) CreateFirewallGroup(description string) (string, error) { - values := url.Values{} - - // Optional description - if len(description) > 0 { - values.Add("description", description) - } - - var result FirewallGroup - err := c.post(`firewall/group_create`, values, &result) - if err != nil { - return "", err - } - return result.ID, nil -} - -// DeleteFirewallGroup deletes an existing firewall group -func (c *Client) DeleteFirewallGroup(groupID string) error { - values := url.Values{ - "FIREWALLGROUPID": {groupID}, - } - - if err := c.post(`firewall/group_delete`, values, nil); err != nil { - return err - } - return nil -} - -// SetFirewallGroupDescription sets the description of an existing firewall group -func (c *Client) SetFirewallGroupDescription(groupID, description string) error { - values := url.Values{ - "FIREWALLGROUPID": {groupID}, - "description": {description}, - } - - if err := c.post(`firewall/group_set_description`, values, nil); err != nil { - return err - } - return nil -} - -// GetFirewallRules returns a list of rules for the given firewall group -func (c *Client) GetFirewallRules(groupID string) ([]FirewallRule, error) { - var ruleMap map[string]FirewallRule - ipTypes := []string{"v4", "v6"} - for _, ipType := range ipTypes { - args := fmt.Sprintf("direction=in&FIREWALLGROUPID=%s&ip_type=%s", - groupID, ipType) - if err := c.get(`firewall/rule_list?`+args, &ruleMap); err != nil { - return nil, err - } - } - - var ruleList []FirewallRule - for _, r := range ruleMap { - ruleList = append(ruleList, r) - } - sort.Sort(firewallRules(ruleList)) - return ruleList, nil -} - -// CreateFirewallRule creates a new firewall rule in Vultr account. -// groupID is the ID of the firewall group to create the rule in -// protocol must be one of: "icmp", "tcp", "udp", "gre" -// port can be a port number or colon separated port range (TCP/UDP only) -func (c *Client) CreateFirewallRule(groupID, protocol, port string, - network *net.IPNet) (int, error) { - ip := network.IP.String() - maskBits, _ := network.Mask.Size() - if ip == "" { - return 0, fmt.Errorf("Invalid network") - } - - var ipType string - if network.IP.To4() != nil { - ipType = "v4" - } else { - ipType = "v6" - } - - values := url.Values{ - "FIREWALLGROUPID": {groupID}, - // possible values: "in" - "direction": {"in"}, - // possible values: "icmp", "tcp", "udp", "gre" - "protocol": {protocol}, - // possible values: "v4", "v6" - "ip_type": {ipType}, - // IP address representing a subnet - "subnet": {ip}, - // IP prefix size in bits - "subnet_size": {fmt.Sprintf("%v", maskBits)}, - } - - if len(port) > 0 { - values.Add("port", port) - } - - var result FirewallRule - err := c.post(`firewall/rule_create`, values, &result) - if err != nil { - return 0, err - } - return result.RuleNumber, nil -} - -// DeleteFirewallRule deletes an existing firewall rule -func (c *Client) DeleteFirewallRule(ruleNumber int, groupID string) error { - values := url.Values{ - "FIREWALLGROUPID": {groupID}, - "rulenumber": {fmt.Sprintf("%v", ruleNumber)}, - } - - if err := c.post(`firewall/rule_delete`, values, nil); err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/ip.go b/vendor/github.com/JamesClonk/vultr/lib/ip.go deleted file mode 100644 index 4d169cb3..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/ip.go +++ /dev/null @@ -1,192 +0,0 @@ -package lib - -import ( - "fmt" - "net/url" - "sort" -) - -// IPv4 information of a virtual machine -type IPv4 struct { - IP string `json:"ip"` - Netmask string `json:"netmask"` - Gateway string `json:"gateway"` - Type string `json:"type"` - ReverseDNS string `json:"reverse"` -} - -type ipv4s []IPv4 - -func (s ipv4s) Len() int { return len(s) } -func (s ipv4s) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s ipv4s) Less(i, j int) bool { - // sort order: type, ip - if s[i].Type < s[j].Type { - return true - } else if s[i].Type > s[j].Type { - return false - } - return s[i].IP < s[j].IP -} - -// IPv6 information of a virtual machine -type IPv6 struct { - IP string `json:"ip"` - Network string `json:"network"` - NetworkSize string `json:"network_size"` - Type string `json:"type"` -} - -type ipv6s []IPv6 - -func (s ipv6s) Len() int { return len(s) } -func (s ipv6s) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s ipv6s) Less(i, j int) bool { - // sort order: type, ip - if s[i].Type < s[j].Type { - return true - } else if s[i].Type > s[j].Type { - return false - } - return s[i].IP < s[j].IP -} - -// ReverseDNSIPv6 information of a virtual machine -type ReverseDNSIPv6 struct { - IP string `json:"ip"` - ReverseDNS string `json:"reverse"` -} - -type reverseDNSIPv6s []ReverseDNSIPv6 - -func (s reverseDNSIPv6s) Len() int { return len(s) } -func (s reverseDNSIPv6s) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s reverseDNSIPv6s) Less(i, j int) bool { return s[i].IP < s[j].IP } - -// ListIPv4 lists the IPv4 information of a virtual machine -func (c *Client) ListIPv4(id string) (list []IPv4, err error) { - var ipMap map[string][]IPv4 - if err := c.get(`server/list_ipv4?SUBID=`+id, &ipMap); err != nil { - return nil, err - } - - for _, iplist := range ipMap { - for _, ip := range iplist { - list = append(list, ip) - } - } - sort.Sort(ipv4s(list)) - return list, nil -} - -// CreateIPv4 creates an IPv4 address and attaches it to a virtual machine -func (c *Client) CreateIPv4(id string, reboot bool) error { - values := url.Values{ - "SUBID": {id}, - "reboot": {fmt.Sprintf("%t", reboot)}, - } - - if err := c.post(`server/create_ipv4`, values, nil); err != nil { - return err - } - return nil -} - -// DeleteIPv4 deletes an IPv4 address and detaches it from a virtual machine -func (c *Client) DeleteIPv4(id, ip string) error { - values := url.Values{ - "SUBID": {id}, - "ip": {ip}, - } - - if err := c.post(`server/destroy_ipv4`, values, nil); err != nil { - return err - } - return nil -} - -// ListIPv6 lists the IPv4 information of a virtual machine -func (c *Client) ListIPv6(id string) (list []IPv6, err error) { - var ipMap map[string][]IPv6 - if err := c.get(`server/list_ipv6?SUBID=`+id, &ipMap); err != nil { - return nil, err - } - - for _, iplist := range ipMap { - for _, ip := range iplist { - list = append(list, ip) - } - } - sort.Sort(ipv6s(list)) - return list, nil -} - -// ListIPv6ReverseDNS lists the IPv6 reverse DNS entries of a virtual machine -func (c *Client) ListIPv6ReverseDNS(id string) (list []ReverseDNSIPv6, err error) { - var ipMap map[string][]ReverseDNSIPv6 - if err := c.get(`server/reverse_list_ipv6?SUBID=`+id, &ipMap); err != nil { - return nil, err - } - - for _, iplist := range ipMap { - for _, ip := range iplist { - list = append(list, ip) - } - } - sort.Sort(reverseDNSIPv6s(list)) - return list, nil -} - -// DeleteIPv6ReverseDNS removes a reverse DNS entry for an IPv6 address of a virtual machine -func (c *Client) DeleteIPv6ReverseDNS(id string, ip string) error { - values := url.Values{ - "SUBID": {id}, - "ip": {ip}, - } - - if err := c.post(`server/reverse_delete_ipv6`, values, nil); err != nil { - return err - } - return nil -} - -// SetIPv6ReverseDNS sets a reverse DNS entry for an IPv6 address of a virtual machine -func (c *Client) SetIPv6ReverseDNS(id, ip, entry string) error { - values := url.Values{ - "SUBID": {id}, - "ip": {ip}, - "entry": {entry}, - } - - if err := c.post(`server/reverse_set_ipv6`, values, nil); err != nil { - return err - } - return nil -} - -// DefaultIPv4ReverseDNS sets a reverse DNS entry for an IPv4 address of a virtual machine to the original setting -func (c *Client) DefaultIPv4ReverseDNS(id, ip string) error { - values := url.Values{ - "SUBID": {id}, - "ip": {ip}, - } - - if err := c.post(`server/reverse_default_ipv4`, values, nil); err != nil { - return err - } - return nil -} - -// SetIPv4ReverseDNS sets a reverse DNS entry for an IPv4 address of a virtual machine -func (c *Client) SetIPv4ReverseDNS(id, ip, entry string) error { - values := url.Values{ - "SUBID": {id}, - "ip": {ip}, - "entry": {entry}, - } - - if err := c.post(`server/reverse_set_ipv4`, values, nil); err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/iso.go b/vendor/github.com/JamesClonk/vultr/lib/iso.go deleted file mode 100644 index a9e1880e..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/iso.go +++ /dev/null @@ -1,44 +0,0 @@ -package lib - -import ( - "sort" - "strings" -) - -// ISO image on Vultr -type ISO struct { - ID int `json:"ISOID"` - Created string `json:"date_created"` - Filename string `json:"filename"` - Size int `json:"size"` - MD5sum string `json:"md5sum"` -} - -type isos []ISO - -func (s isos) Len() int { return len(s) } -func (s isos) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s isos) Less(i, j int) bool { - // sort order: filename, created - if strings.ToLower(s[i].Filename) < strings.ToLower(s[j].Filename) { - return true - } else if strings.ToLower(s[i].Filename) > strings.ToLower(s[j].Filename) { - return false - } - return s[i].Created < s[j].Created -} - -// GetISO returns a list of all ISO images on Vultr account -func (c *Client) GetISO() ([]ISO, error) { - var isoMap map[string]ISO - if err := c.get(`iso/list`, &isoMap); err != nil { - return nil, err - } - - var isoList []ISO - for _, iso := range isoMap { - isoList = append(isoList, iso) - } - sort.Sort(isos(isoList)) - return isoList, nil -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/os.go b/vendor/github.com/JamesClonk/vultr/lib/os.go deleted file mode 100644 index 647d253f..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/os.go +++ /dev/null @@ -1,37 +0,0 @@ -package lib - -import ( - "sort" - "strings" -) - -// OS image on Vultr -type OS struct { - ID int `json:"OSID"` - Name string `json:"name"` - Arch string `json:"arch"` - Family string `json:"family"` - Windows bool `json:"windows"` - Surcharge string `json:"surcharge"` -} - -type oses []OS - -func (s oses) Len() int { return len(s) } -func (s oses) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s oses) Less(i, j int) bool { return strings.ToLower(s[i].Name) < strings.ToLower(s[j].Name) } - -// GetOS returns a list of all available operating systems on Vultr -func (c *Client) GetOS() ([]OS, error) { - var osMap map[string]OS - if err := c.get(`os/list`, &osMap); err != nil { - return nil, err - } - - var osList []OS - for _, os := range osMap { - osList = append(osList, os) - } - sort.Sort(oses(osList)) - return osList, nil -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/plans.go b/vendor/github.com/JamesClonk/vultr/lib/plans.go deleted file mode 100644 index b3bef4ef..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/plans.go +++ /dev/null @@ -1,78 +0,0 @@ -package lib - -import ( - "fmt" - "sort" - "strconv" - "strings" -) - -// Plan on Vultr -type Plan struct { - ID int `json:"VPSPLANID,string"` - Name string `json:"name"` - VCpus int `json:"vcpu_count,string"` - RAM string `json:"ram"` - Disk string `json:"disk"` - Bandwidth string `json:"bandwidth"` - Price string `json:"price_per_month"` - Regions []int `json:"available_locations"` -} - -type plans []Plan - -func (p plans) Len() int { return len(p) } -func (p plans) Swap(i, j int) { p[i], p[j] = p[j], p[i] } -func (p plans) Less(i, j int) bool { - pa, _ := strconv.ParseFloat(strings.TrimSpace(p[i].Price), 64) - pb, _ := strconv.ParseFloat(strings.TrimSpace(p[j].Price), 64) - ra, _ := strconv.ParseInt(strings.TrimSpace(p[i].RAM), 10, 64) - rb, _ := strconv.ParseInt(strings.TrimSpace(p[j].RAM), 10, 64) - da, _ := strconv.ParseInt(strings.TrimSpace(p[i].Disk), 10, 64) - db, _ := strconv.ParseInt(strings.TrimSpace(p[j].Disk), 10, 64) - - // sort order: price, vcpu, ram, disk - if pa < pb { - return true - } else if pa > pb { - return false - } - - if p[i].VCpus < p[j].VCpus { - return true - } else if p[i].VCpus > p[j].VCpus { - return false - } - - if ra < rb { - return true - } else if ra > rb { - return false - } - - return da < db -} - -// GetPlans returns a list of all available plans on Vultr account -func (c *Client) GetPlans() ([]Plan, error) { - var planMap map[string]Plan - if err := c.get(`plans/list`, &planMap); err != nil { - return nil, err - } - - var p plans - for _, plan := range planMap { - p = append(p, plan) - } - - sort.Sort(plans(p)) - return p, nil -} - -// GetAvailablePlansForRegion returns available plans for specified region -func (c *Client) GetAvailablePlansForRegion(id int) (planIDs []int, err error) { - if err := c.get(fmt.Sprintf(`regions/availability?DCID=%v`, id), &planIDs); err != nil { - return nil, err - } - return -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/regions.go b/vendor/github.com/JamesClonk/vultr/lib/regions.go deleted file mode 100644 index 70ceb2ed..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/regions.go +++ /dev/null @@ -1,44 +0,0 @@ -package lib - -import "sort" - -// Region on Vultr -type Region struct { - ID int `json:"DCID,string"` - Name string `json:"name"` - Country string `json:"country"` - Continent string `json:"continent"` - State string `json:"state"` - Ddos bool `json:"ddos_protection"` - BlockStorage bool `json:"block_storage"` - Code string `json:"regioncode"` -} - -type regions []Region - -func (s regions) Len() int { return len(s) } -func (s regions) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s regions) Less(i, j int) bool { - // sort order: continent, name - if s[i].Continent < s[j].Continent { - return true - } else if s[i].Continent > s[j].Continent { - return false - } - return s[i].Name < s[j].Name -} - -// GetRegions returns a list of all available Vultr regions -func (c *Client) GetRegions() ([]Region, error) { - var regionMap map[string]Region - if err := c.get(`regions/list`, ®ionMap); err != nil { - return nil, err - } - - var regionList []Region - for _, os := range regionMap { - regionList = append(regionList, os) - } - sort.Sort(regions(regionList)) - return regionList, nil -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/reservedip.go b/vendor/github.com/JamesClonk/vultr/lib/reservedip.go deleted file mode 100644 index 22097cf7..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/reservedip.go +++ /dev/null @@ -1,192 +0,0 @@ -package lib - -import ( - "encoding/json" - "fmt" - "net/url" - "sort" - "strconv" - "strings" -) - -// IP on Vultr -type IP struct { - ID string `json:"SUBID,string"` - RegionID int `json:"DCID,string"` - IPType string `json:"ip_type"` - Subnet string `json:"subnet"` - SubnetSize int `json:"subnet_size"` - Label string `json:"label"` - AttachedTo string `json:"attached_SUBID,string"` -} - -type ips []IP - -func (s ips) Len() int { return len(s) } -func (s ips) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s ips) Less(i, j int) bool { - // sort order: label, iptype, subnet - if strings.ToLower(s[i].Label) < strings.ToLower(s[j].Label) { - return true - } else if strings.ToLower(s[i].Label) > strings.ToLower(s[j].Label) { - return false - } - if s[i].IPType < s[j].IPType { - return true - } else if s[i].IPType > s[j].IPType { - return false - } - return s[i].Subnet < s[j].Subnet -} - -// UnmarshalJSON implements json.Unmarshaller on IP. -// This is needed because the Vultr API is inconsistent in it's JSON responses. -// Some fields can change type, from JSON number to JSON string and vice-versa. -func (i *IP) UnmarshalJSON(data []byte) (err error) { - if i == nil { - *i = IP{} - } - - var fields map[string]interface{} - if err := json.Unmarshal(data, &fields); err != nil { - return err - } - - value := fmt.Sprintf("%v", fields["SUBID"]) - if len(value) == 0 || value == "" || value == "0" { - i.ID = "" - } else { - id, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - i.ID = strconv.FormatFloat(id, 'f', -1, 64) - } - - value = fmt.Sprintf("%v", fields["DCID"]) - if len(value) == 0 || value == "" { - value = "0" - } - region, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - i.RegionID = int(region) - - value = fmt.Sprintf("%v", fields["attached_SUBID"]) - if len(value) == 0 || value == "" || value == "0" || value == "false" { - i.AttachedTo = "" - } else { - attached, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - i.AttachedTo = strconv.FormatFloat(attached, 'f', -1, 64) - } - - value = fmt.Sprintf("%v", fields["subnet_size"]) - if len(value) == 0 || value == "" { - value = "0" - } - size, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - i.SubnetSize = int(size) - - i.IPType = fmt.Sprintf("%v", fields["ip_type"]) - i.Subnet = fmt.Sprintf("%v", fields["subnet"]) - i.Label = fmt.Sprintf("%v", fields["label"]) - - return -} - -// ListReservedIP returns a list of all available reserved IPs on Vultr account -func (c *Client) ListReservedIP() ([]IP, error) { - var ipMap map[string]IP - - err := c.get(`reservedip/list`, &ipMap) - if err != nil { - return nil, err - } - - ipList := make([]IP, 0) - for _, ip := range ipMap { - ipList = append(ipList, ip) - } - sort.Sort(ips(ipList)) - return ipList, nil -} - -// GetReservedIP returns reserved IP with given ID -func (c *Client) GetReservedIP(id string) (IP, error) { - var ipMap map[string]IP - - err := c.get(`reservedip/list`, &ipMap) - if err != nil { - return IP{}, err - } - if ip, ok := ipMap[id]; ok { - return ip, nil - } - return IP{}, fmt.Errorf("IP with ID %v not found", id) -} - -// CreateReservedIP creates a new reserved IP on Vultr account -func (c *Client) CreateReservedIP(regionID int, ipType string, label string) (string, error) { - values := url.Values{ - "DCID": {fmt.Sprintf("%v", regionID)}, - "ip_type": {ipType}, - } - if len(label) > 0 { - values.Add("label", label) - } - - result := IP{} - err := c.post(`reservedip/create`, values, &result) - if err != nil { - return "", err - } - return result.ID, nil -} - -// DestroyReservedIP deletes an existing reserved IP -func (c *Client) DestroyReservedIP(id string) error { - values := url.Values{ - "SUBID": {id}, - } - return c.post(`reservedip/destroy`, values, nil) -} - -// AttachReservedIP attaches a reserved IP to a virtual machine -func (c *Client) AttachReservedIP(ip string, serverID string) error { - values := url.Values{ - "ip_address": {ip}, - "attach_SUBID": {serverID}, - } - return c.post(`reservedip/attach`, values, nil) -} - -// DetachReservedIP detaches a reserved IP from an existing virtual machine -func (c *Client) DetachReservedIP(serverID string, ip string) error { - values := url.Values{ - "ip_address": {ip}, - "detach_SUBID": {serverID}, - } - return c.post(`reservedip/detach`, values, nil) -} - -// ConvertReservedIP converts an existing virtual machines IP to a reserved IP -func (c *Client) ConvertReservedIP(serverID string, ip string) (string, error) { - values := url.Values{ - "SUBID": {serverID}, - "ip_address": {ip}, - } - - result := IP{} - err := c.post(`reservedip/convert`, values, &result) - if err != nil { - return "", err - } - return result.ID, err -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/scripts.go b/vendor/github.com/JamesClonk/vultr/lib/scripts.go deleted file mode 100644 index d6639cf1..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/scripts.go +++ /dev/null @@ -1,126 +0,0 @@ -package lib - -import ( - "encoding/json" - "fmt" - "net/url" - "sort" - "strings" -) - -// StartupScript on Vultr account -type StartupScript struct { - ID string `json:"SCRIPTID"` - Name string `json:"name"` - Type string `json:"type"` - Content string `json:"script"` -} - -type startupscripts []StartupScript - -func (s startupscripts) Len() int { return len(s) } -func (s startupscripts) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s startupscripts) Less(i, j int) bool { - return strings.ToLower(s[i].Name) < strings.ToLower(s[j].Name) -} - -// UnmarshalJSON implements json.Unmarshaller on StartupScript. -// Necessary because the SCRIPTID field has inconsistent types. -func (s *StartupScript) UnmarshalJSON(data []byte) (err error) { - if s == nil { - *s = StartupScript{} - } - - var fields map[string]interface{} - if err := json.Unmarshal(data, &fields); err != nil { - return err - } - - s.ID = fmt.Sprintf("%v", fields["SCRIPTID"]) - s.Name = fmt.Sprintf("%v", fields["name"]) - s.Type = fmt.Sprintf("%v", fields["type"]) - s.Content = fmt.Sprintf("%v", fields["script"]) - - return -} - -// GetStartupScripts returns a list of all startup scripts on the current Vultr account -func (c *Client) GetStartupScripts() (scripts []StartupScript, err error) { - var scriptMap map[string]StartupScript - if err := c.get(`startupscript/list`, &scriptMap); err != nil { - return nil, err - } - - for _, script := range scriptMap { - if script.Type == "" { - script.Type = "boot" // set default script type - } - scripts = append(scripts, script) - } - sort.Sort(startupscripts(scripts)) - return scripts, nil -} - -// GetStartupScript returns the startup script with the given ID -func (c *Client) GetStartupScript(id string) (StartupScript, error) { - scripts, err := c.GetStartupScripts() - if err != nil { - return StartupScript{}, err - } - - for _, s := range scripts { - if s.ID == id { - return s, nil - } - } - return StartupScript{}, nil -} - -// CreateStartupScript creates a new startup script -func (c *Client) CreateStartupScript(name, content, scriptType string) (StartupScript, error) { - values := url.Values{ - "name": {name}, - "script": {content}, - "type": {scriptType}, - } - - var script StartupScript - if err := c.post(`startupscript/create`, values, &script); err != nil { - return StartupScript{}, err - } - script.Name = name - script.Content = content - script.Type = scriptType - - return script, nil -} - -// UpdateStartupScript updates an existing startup script -func (c *Client) UpdateStartupScript(script StartupScript) error { - values := url.Values{ - "SCRIPTID": {script.ID}, - } - if script.Name != "" { - values.Add("name", script.Name) - } - if script.Content != "" { - values.Add("script", script.Content) - } - - if err := c.post(`startupscript/update`, values, nil); err != nil { - return err - } - return nil -} - -// DeleteStartupScript deletes an existing startup script from Vultr account -func (c *Client) DeleteStartupScript(id string) error { - values := url.Values{ - "SCRIPTID": {id}, - } - - if err := c.post(`startupscript/destroy`, values, nil); err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/servers.go b/vendor/github.com/JamesClonk/vultr/lib/servers.go deleted file mode 100644 index 418198a7..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/servers.go +++ /dev/null @@ -1,561 +0,0 @@ -package lib - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "net/url" - "sort" - "strconv" - "strings" -) - -// Server (virtual machine) on Vultr account -type Server struct { - ID string `json:"SUBID"` - Name string `json:"label"` - OS string `json:"os"` - RAM string `json:"ram"` - Disk string `json:"disk"` - MainIP string `json:"main_ip"` - VCpus int `json:"vcpu_count,string"` - Location string `json:"location"` - RegionID int `json:"DCID,string"` - DefaultPassword string `json:"default_password"` - Created string `json:"date_created"` - PendingCharges float64 `json:"pending_charges"` - Status string `json:"status"` - Cost string `json:"cost_per_month"` - CurrentBandwidth float64 `json:"current_bandwidth_gb"` - AllowedBandwidth float64 `json:"allowed_bandwidth_gb,string"` - NetmaskV4 string `json:"netmask_v4"` - GatewayV4 string `json:"gateway_v4"` - PowerStatus string `json:"power_status"` - ServerState string `json:"server_state"` - PlanID int `json:"VPSPLANID,string"` - V6Networks []V6Network `json:"v6_networks"` - InternalIP string `json:"internal_ip"` - KVMUrl string `json:"kvm_url"` - AutoBackups string `json:"auto_backups"` - Tag string `json:"tag"` - OSID string `json:"OSID"` - AppID string `json:"APPID"` - FirewallGroupID string `json:"FIREWALLGROUPID"` -} - -// ServerOptions are optional parameters to be used during server creation -type ServerOptions struct { - IPXEChainURL string - ISO int - Script int - UserData string - Snapshot string - SSHKey string - ReservedIP string - IPV6 bool - PrivateNetworking bool - AutoBackups bool - DontNotifyOnActivate bool - Hostname string - Tag string - AppID string - FirewallGroupID string -} - -type servers []Server - -func (s servers) Len() int { return len(s) } -func (s servers) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s servers) Less(i, j int) bool { - // sort order: name, ip - if strings.ToLower(s[i].Name) < strings.ToLower(s[j].Name) { - return true - } else if strings.ToLower(s[i].Name) > strings.ToLower(s[j].Name) { - return false - } - return s[i].MainIP < s[j].MainIP -} - -// V6Network represents a IPv6 network of a Vultr server -type V6Network struct { - Network string `json:"v6_network"` - MainIP string `json:"v6_main_ip"` - NetworkSize string `json:"v6_network_size"` -} - -// ISOStatus represents an ISO image attached to a Vultr server -type ISOStatus struct { - State string `json:"state"` - ISOID string `json:"ISOID"` -} - -// UnmarshalJSON implements json.Unmarshaller on Server. -// This is needed because the Vultr API is inconsistent in it's JSON responses for servers. -// Some fields can change type, from JSON number to JSON string and vice-versa. -func (s *Server) UnmarshalJSON(data []byte) (err error) { - if s == nil { - *s = Server{} - } - - var fields map[string]interface{} - if err := json.Unmarshal(data, &fields); err != nil { - return err - } - - value := fmt.Sprintf("%v", fields["vcpu_count"]) - if len(value) == 0 || value == "" { - value = "0" - } - vcpu, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - s.VCpus = int(vcpu) - - value = fmt.Sprintf("%v", fields["DCID"]) - if len(value) == 0 || value == "" { - value = "0" - } - region, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - s.RegionID = int(region) - - value = fmt.Sprintf("%v", fields["VPSPLANID"]) - if len(value) == 0 || value == "" { - value = "0" - } - plan, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - s.PlanID = int(plan) - - value = fmt.Sprintf("%v", fields["pending_charges"]) - if len(value) == 0 || value == "" { - value = "0" - } - pc, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - s.PendingCharges = pc - - value = fmt.Sprintf("%v", fields["current_bandwidth_gb"]) - if len(value) == 0 || value == "" { - value = "0" - } - cb, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - s.CurrentBandwidth = cb - - value = fmt.Sprintf("%v", fields["allowed_bandwidth_gb"]) - if len(value) == 0 || value == "" { - value = "0" - } - ab, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - s.AllowedBandwidth = ab - - value = fmt.Sprintf("%v", fields["OSID"]) - if value == "" { - value = "" - } - s.OSID = value - - value = fmt.Sprintf("%v", fields["APPID"]) - if value == "" || value == "0" { - value = "" - } - s.AppID = value - - value = fmt.Sprintf("%v", fields["FIREWALLGROUPID"]) - if value == "" || value == "0" { - value = "" - } - s.FirewallGroupID = value - - s.ID = fmt.Sprintf("%v", fields["SUBID"]) - s.Name = fmt.Sprintf("%v", fields["label"]) - s.OS = fmt.Sprintf("%v", fields["os"]) - s.RAM = fmt.Sprintf("%v", fields["ram"]) - s.Disk = fmt.Sprintf("%v", fields["disk"]) - s.MainIP = fmt.Sprintf("%v", fields["main_ip"]) - s.Location = fmt.Sprintf("%v", fields["location"]) - s.DefaultPassword = fmt.Sprintf("%v", fields["default_password"]) - s.Created = fmt.Sprintf("%v", fields["date_created"]) - s.Status = fmt.Sprintf("%v", fields["status"]) - s.Cost = fmt.Sprintf("%v", fields["cost_per_month"]) - s.NetmaskV4 = fmt.Sprintf("%v", fields["netmask_v4"]) - s.GatewayV4 = fmt.Sprintf("%v", fields["gateway_v4"]) - s.PowerStatus = fmt.Sprintf("%v", fields["power_status"]) - s.ServerState = fmt.Sprintf("%v", fields["server_state"]) - - v6networks := make([]V6Network, 0) - if networks, ok := fields["v6_networks"].([]interface{}); ok { - for _, network := range networks { - if network, ok := network.(map[string]interface{}); ok { - v6network := V6Network{ - Network: fmt.Sprintf("%v", network["v6_network"]), - MainIP: fmt.Sprintf("%v", network["v6_main_ip"]), - NetworkSize: fmt.Sprintf("%v", network["v6_network_size"]), - } - v6networks = append(v6networks, v6network) - } - } - s.V6Networks = v6networks - } - - s.InternalIP = fmt.Sprintf("%v", fields["internal_ip"]) - s.KVMUrl = fmt.Sprintf("%v", fields["kvm_url"]) - s.AutoBackups = fmt.Sprintf("%v", fields["auto_backups"]) - s.Tag = fmt.Sprintf("%v", fields["tag"]) - - return -} - -// GetServers returns a list of current virtual machines on Vultr account -func (c *Client) GetServers() (serverList []Server, err error) { - var serverMap map[string]Server - if err := c.get(`server/list`, &serverMap); err != nil { - return nil, err - } - - for _, server := range serverMap { - serverList = append(serverList, server) - } - sort.Sort(servers(serverList)) - return serverList, nil -} - -// GetServersByTag returns a list of all virtual machines matching by tag -func (c *Client) GetServersByTag(tag string) (serverList []Server, err error) { - var serverMap map[string]Server - if err := c.get(`server/list?tag=`+tag, &serverMap); err != nil { - return nil, err - } - - for _, server := range serverMap { - serverList = append(serverList, server) - } - sort.Sort(servers(serverList)) - return serverList, nil -} - -// GetServer returns the virtual machine with the given ID -func (c *Client) GetServer(id string) (server Server, err error) { - if err := c.get(`server/list?SUBID=`+id, &server); err != nil { - return Server{}, err - } - return server, nil -} - -// CreateServer creates a new virtual machine on Vultr. ServerOptions are optional settings. -func (c *Client) CreateServer(name string, regionID, planID, osID int, options *ServerOptions) (Server, error) { - values := url.Values{ - "label": {name}, - "DCID": {fmt.Sprintf("%v", regionID)}, - "VPSPLANID": {fmt.Sprintf("%v", planID)}, - "OSID": {fmt.Sprintf("%v", osID)}, - } - - if options != nil { - if options.IPXEChainURL != "" { - values.Add("ipxe_chain_url", options.IPXEChainURL) - } - - if options.ISO != 0 { - values.Add("ISOID", fmt.Sprintf("%v", options.ISO)) - } - - if options.Script != 0 { - values.Add("SCRIPTID", fmt.Sprintf("%v", options.Script)) - } - - if options.UserData != "" { - values.Add("userdata", base64.StdEncoding.EncodeToString([]byte(options.UserData))) - } - - if options.Snapshot != "" { - values.Add("SNAPSHOTID", options.Snapshot) - } - - if options.SSHKey != "" { - values.Add("SSHKEYID", options.SSHKey) - } - - if options.ReservedIP != "" { - values.Add("reserved_ip_v4", options.ReservedIP) - } - - values.Add("enable_ipv6", "no") - if options.IPV6 { - values.Set("enable_ipv6", "yes") - } - - values.Add("enable_private_network", "no") - if options.PrivateNetworking { - values.Set("enable_private_network", "yes") - } - - values.Add("auto_backups", "no") - if options.AutoBackups { - values.Set("auto_backups", "yes") - } - - values.Add("notify_activate", "yes") - if options.DontNotifyOnActivate { - values.Set("notify_activate", "no") - } - - if options.Hostname != "" { - values.Add("hostname", options.Hostname) - } - - if options.Tag != "" { - values.Add("tag", options.Tag) - } - - if options.AppID != "" { - values.Add("APPID", options.AppID) - } - - if options.FirewallGroupID != "" { - values.Add("FIREWALLGROUPID", options.FirewallGroupID) - } - } - - var server Server - if err := c.post(`server/create`, values, &server); err != nil { - return Server{}, err - } - server.Name = name - server.RegionID = regionID - server.PlanID = planID - - return server, nil -} - -// RenameServer renames an existing virtual machine -func (c *Client) RenameServer(id, name string) error { - values := url.Values{ - "SUBID": {id}, - "label": {name}, - } - - if err := c.post(`server/label_set`, values, nil); err != nil { - return err - } - return nil -} - -// TagServer replaces the tag on an existing virtual machine -func (c *Client) TagServer(id, tag string) error { - values := url.Values{ - "SUBID": {id}, - "tag": {tag}, - } - - if err := c.post(`server/tag_set`, values, nil); err != nil { - return err - } - return nil -} - -// StartServer starts an existing virtual machine -func (c *Client) StartServer(id string) error { - values := url.Values{ - "SUBID": {id}, - } - - if err := c.post(`server/start`, values, nil); err != nil { - return err - } - return nil -} - -// HaltServer stops an existing virtual machine -func (c *Client) HaltServer(id string) error { - values := url.Values{ - "SUBID": {id}, - } - - if err := c.post(`server/halt`, values, nil); err != nil { - return err - } - return nil -} - -// RebootServer reboots an existing virtual machine -func (c *Client) RebootServer(id string) error { - values := url.Values{ - "SUBID": {id}, - } - - if err := c.post(`server/reboot`, values, nil); err != nil { - return err - } - return nil -} - -// ReinstallServer reinstalls the operating system on an existing virtual machine -func (c *Client) ReinstallServer(id string) error { - values := url.Values{ - "SUBID": {id}, - } - - if err := c.post(`server/reinstall`, values, nil); err != nil { - return err - } - return nil -} - -// ChangeOSofServer changes the virtual machine to a different operating system -func (c *Client) ChangeOSofServer(id string, osID int) error { - values := url.Values{ - "SUBID": {id}, - "OSID": {fmt.Sprintf("%v", osID)}, - } - - if err := c.post(`server/os_change`, values, nil); err != nil { - return err - } - return nil -} - -// ListOSforServer lists all available operating systems to which an existing virtual machine can be changed -func (c *Client) ListOSforServer(id string) (os []OS, err error) { - var osMap map[string]OS - if err := c.get(`server/os_change_list?SUBID=`+id, &osMap); err != nil { - return nil, err - } - - for _, o := range osMap { - os = append(os, o) - } - sort.Sort(oses(os)) - return os, nil -} - -// AttachISOtoServer attaches an ISO image to an existing virtual machine and reboots it -func (c *Client) AttachISOtoServer(id string, isoID int) error { - values := url.Values{ - "SUBID": {id}, - "ISOID": {fmt.Sprintf("%v", isoID)}, - } - - if err := c.post(`server/iso_attach`, values, nil); err != nil { - return err - } - return nil -} - -// DetachISOfromServer detaches the currently mounted ISO image from the virtual machine and reboots it -func (c *Client) DetachISOfromServer(id string) error { - values := url.Values{ - "SUBID": {id}, - } - - if err := c.post(`server/iso_detach`, values, nil); err != nil { - return err - } - return nil -} - -// GetISOStatusofServer retrieves the current ISO image state of an existing virtual machine -func (c *Client) GetISOStatusofServer(id string) (isoStatus ISOStatus, err error) { - if err := c.get(`server/iso_status?SUBID=`+id, &isoStatus); err != nil { - return ISOStatus{}, err - } - return isoStatus, nil -} - -// DeleteServer deletes an existing virtual machine -func (c *Client) DeleteServer(id string) error { - values := url.Values{ - "SUBID": {id}, - } - - if err := c.post(`server/destroy`, values, nil); err != nil { - return err - } - return nil -} - -// SetFirewallGroup adds a virtual machine to a firewall group -func (c *Client) SetFirewallGroup(id, firewallgroup string) error { - values := url.Values{ - "SUBID": {id}, - "FIREWALLGROUPID": {firewallgroup}, - } - - if err := c.post(`server/firewall_group_set`, values, nil); err != nil { - return err - } - return nil -} - -// UnsetFirewallGroup removes a virtual machine from a firewall group -func (c *Client) UnsetFirewallGroup(id string) error { - return c.SetFirewallGroup(id, "0") -} - -// BandwidthOfServer retrieves the bandwidth used by a virtual machine -func (c *Client) BandwidthOfServer(id string) (bandwidth []map[string]string, err error) { - var bandwidthMap map[string][][]string - if err := c.get(`server/bandwidth?SUBID=`+id, &bandwidthMap); err != nil { - return nil, err - } - - // parse incoming bytes - for _, b := range bandwidthMap["incoming_bytes"] { - bMap := make(map[string]string) - bMap["date"] = b[0] - bMap["incoming"] = b[1] - bandwidth = append(bandwidth, bMap) - } - - // parse outgoing bytes (we'll assume that incoming and outgoing dates are always a match) - for _, b := range bandwidthMap["outgoing_bytes"] { - for i := range bandwidth { - if bandwidth[i]["date"] == b[0] { - bandwidth[i]["outgoing"] = b[1] - break - } - } - } - - return bandwidth, nil -} - -// ChangeApplicationofServer changes the virtual machine to a different application -func (c *Client) ChangeApplicationofServer(id string, appID string) error { - values := url.Values{ - "SUBID": {id}, - "APPID": {appID}, - } - - if err := c.post(`server/app_change`, values, nil); err != nil { - return err - } - return nil -} - -// ListApplicationsforServer lists all available operating systems to which an existing virtual machine can be changed -func (c *Client) ListApplicationsforServer(id string) (apps []Application, err error) { - var appMap map[string]Application - if err := c.get(`server/app_change_list?SUBID=`+id, &appMap); err != nil { - return nil, err - } - - for _, app := range appMap { - apps = append(apps, app) - } - sort.Sort(applications(apps)) - return apps, nil -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/snapshots.go b/vendor/github.com/JamesClonk/vultr/lib/snapshots.go deleted file mode 100644 index 7e996930..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/snapshots.go +++ /dev/null @@ -1,72 +0,0 @@ -package lib - -import ( - "net/url" - "sort" - "strings" -) - -// Snapshot of a virtual machine on Vultr account -type Snapshot struct { - ID string `json:"SNAPSHOTID"` - Description string `json:"description"` - Size string `json:"size"` - Status string `json:"status"` - Created string `json:"date_created"` -} - -type snapshots []Snapshot - -func (s snapshots) Len() int { return len(s) } -func (s snapshots) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s snapshots) Less(i, j int) bool { - // sort order: description, created - if strings.ToLower(s[i].Description) < strings.ToLower(s[j].Description) { - return true - } else if strings.ToLower(s[i].Description) > strings.ToLower(s[j].Description) { - return false - } - return s[i].Created < s[j].Created -} - -// GetSnapshots retrieves a list of all snapshots on Vultr account -func (c *Client) GetSnapshots() (snapshotList []Snapshot, err error) { - var snapshotMap map[string]Snapshot - if err := c.get(`snapshot/list`, &snapshotMap); err != nil { - return nil, err - } - - for _, snapshot := range snapshotMap { - snapshotList = append(snapshotList, snapshot) - } - sort.Sort(snapshots(snapshotList)) - return snapshotList, nil -} - -// CreateSnapshot creates a new virtual machine snapshot -func (c *Client) CreateSnapshot(id, description string) (Snapshot, error) { - values := url.Values{ - "SUBID": {id}, - "description": {description}, - } - - var snapshot Snapshot - if err := c.post(`snapshot/create`, values, &snapshot); err != nil { - return Snapshot{}, err - } - snapshot.Description = description - - return snapshot, nil -} - -// DeleteSnapshot deletes an existing virtual machine snapshot -func (c *Client) DeleteSnapshot(id string) error { - values := url.Values{ - "SNAPSHOTID": {id}, - } - - if err := c.post(`snapshot/destroy`, values, nil); err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/JamesClonk/vultr/lib/sshkeys.go b/vendor/github.com/JamesClonk/vultr/lib/sshkeys.go deleted file mode 100644 index 006e16f2..00000000 --- a/vendor/github.com/JamesClonk/vultr/lib/sshkeys.go +++ /dev/null @@ -1,82 +0,0 @@ -package lib - -import ( - "net/url" - "sort" - "strings" -) - -// SSHKey on Vultr account -type SSHKey struct { - ID string `json:"SSHKEYID"` - Name string `json:"name"` - Key string `json:"ssh_key"` - Created string `json:"date_created"` -} - -type sshkeys []SSHKey - -func (s sshkeys) Len() int { return len(s) } -func (s sshkeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s sshkeys) Less(i, j int) bool { return strings.ToLower(s[i].Name) < strings.ToLower(s[j].Name) } - -// GetSSHKeys returns a list of SSHKeys from Vultr account -func (c *Client) GetSSHKeys() (keys []SSHKey, err error) { - var keyMap map[string]SSHKey - if err := c.get(`sshkey/list`, &keyMap); err != nil { - return nil, err - } - - for _, key := range keyMap { - keys = append(keys, key) - } - sort.Sort(sshkeys(keys)) - return keys, nil -} - -// CreateSSHKey creates new SSHKey on Vultr -func (c *Client) CreateSSHKey(name, key string) (SSHKey, error) { - values := url.Values{ - "name": {name}, - "ssh_key": {key}, - } - - var sshKey SSHKey - if err := c.post(`sshkey/create`, values, &sshKey); err != nil { - return SSHKey{}, err - } - sshKey.Name = name - sshKey.Key = key - - return sshKey, nil -} - -// UpdateSSHKey updates an existing SSHKey entry -func (c *Client) UpdateSSHKey(key SSHKey) error { - values := url.Values{ - "SSHKEYID": {key.ID}, - } - if key.Name != "" { - values.Add("name", key.Name) - } - if key.Key != "" { - values.Add("ssh_key", key.Key) - } - - if err := c.post(`sshkey/update`, values, nil); err != nil { - return err - } - return nil -} - -// DeleteSSHKey deletes an existing SSHKey from Vultr account -func (c *Client) DeleteSSHKey(id string) error { - values := url.Values{ - "SSHKEYID": {id}, - } - - if err := c.post(`sshkey/destroy`, values, nil); err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/juju/ratelimit/LICENSE b/vendor/github.com/juju/ratelimit/LICENSE deleted file mode 100644 index ade9307b..00000000 --- a/vendor/github.com/juju/ratelimit/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -All files in this repository are licensed as follows. If you contribute -to this repository, it is assumed that you license your contribution -under the same license unless you state otherwise. - -All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file. - -This software is licensed under the LGPLv3, included below. - -As a special exception to the GNU Lesser General Public License version 3 -("LGPL3"), the copyright holders of this Library give you permission to -convey to a third party a Combined Work that links statically or dynamically -to this Library without providing any Minimal Corresponding Source or -Minimal Application Code as set out in 4d or providing the installation -information set out in section 4e, provided that you comply with the other -provisions of LGPL3 and provided that you meet, for the Application the -terms and conditions of the license(s) which apply to the Application. - -Except as stated in this special exception, the provisions of LGPL3 will -continue to comply in full to this Library. If you modify this Library, you -may apply this exception to your version of this Library, but you are not -obliged to do so. If you do not wish to do so, delete this exception -statement from your version. This exception does not (and cannot) modify any -license terms which apply to the Application, with which you must still -comply. - - - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/vendor/github.com/juju/ratelimit/ratelimit.go b/vendor/github.com/juju/ratelimit/ratelimit.go deleted file mode 100644 index bd9ef103..00000000 --- a/vendor/github.com/juju/ratelimit/ratelimit.go +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright 2014 Canonical Ltd. -// Licensed under the LGPLv3 with static-linking exception. -// See LICENCE file for details. - -// Package ratelimit provides an efficient token bucket implementation -// that can be used to limit the rate of arbitrary things. -// See http://en.wikipedia.org/wiki/Token_bucket. -package ratelimit - -import ( - "math" - "strconv" - "sync" - "time" -) - -// The algorithm that this implementation uses does computational work -// only when tokens are removed from the bucket, and that work completes -// in short, bounded-constant time (Bucket.Wait benchmarks at 175ns on -// my laptop). -// -// Time is measured in equal measured ticks, a given interval -// (fillInterval) apart. On each tick a number of tokens (quantum) are -// added to the bucket. -// -// When any of the methods are called the bucket updates the number of -// tokens that are in the bucket, and it records the current tick -// number too. Note that it doesn't record the current time - by -// keeping things in units of whole ticks, it's easy to dish out tokens -// at exactly the right intervals as measured from the start time. -// -// This allows us to calculate the number of tokens that will be -// available at some time in the future with a few simple arithmetic -// operations. -// -// The main reason for being able to transfer multiple tokens on each tick -// is so that we can represent rates greater than 1e9 (the resolution of the Go -// time package) tokens per second, but it's also useful because -// it means we can easily represent situations like "a person gets -// five tokens an hour, replenished on the hour". - -// Bucket represents a token bucket that fills at a predetermined rate. -// Methods on Bucket may be called concurrently. -type Bucket struct { - clock Clock - - // startTime holds the moment when the bucket was - // first created and ticks began. - startTime time.Time - - // capacity holds the overall capacity of the bucket. - capacity int64 - - // quantum holds how many tokens are added on - // each tick. - quantum int64 - - // fillInterval holds the interval between each tick. - fillInterval time.Duration - - // mu guards the fields below it. - mu sync.Mutex - - // availableTokens holds the number of available - // tokens as of the associated latestTick. - // It will be negative when there are consumers - // waiting for tokens. - availableTokens int64 - - // latestTick holds the latest tick for which - // we know the number of tokens in the bucket. - latestTick int64 -} - -// NewBucket returns a new token bucket that fills at the -// rate of one token every fillInterval, up to the given -// maximum capacity. Both arguments must be -// positive. The bucket is initially full. -func NewBucket(fillInterval time.Duration, capacity int64) *Bucket { - return NewBucketWithClock(fillInterval, capacity, nil) -} - -// NewBucketWithClock is identical to NewBucket but injects a testable clock -// interface. -func NewBucketWithClock(fillInterval time.Duration, capacity int64, clock Clock) *Bucket { - return NewBucketWithQuantumAndClock(fillInterval, capacity, 1, clock) -} - -// rateMargin specifes the allowed variance of actual -// rate from specified rate. 1% seems reasonable. -const rateMargin = 0.01 - -// NewBucketWithRate returns a token bucket that fills the bucket -// at the rate of rate tokens per second up to the given -// maximum capacity. Because of limited clock resolution, -// at high rates, the actual rate may be up to 1% different from the -// specified rate. -func NewBucketWithRate(rate float64, capacity int64) *Bucket { - return NewBucketWithRateAndClock(rate, capacity, nil) -} - -// NewBucketWithRateAndClock is identical to NewBucketWithRate but injects a -// testable clock interface. -func NewBucketWithRateAndClock(rate float64, capacity int64, clock Clock) *Bucket { - // Use the same bucket each time through the loop - // to save allocations. - tb := NewBucketWithQuantumAndClock(1, capacity, 1, clock) - for quantum := int64(1); quantum < 1<<50; quantum = nextQuantum(quantum) { - fillInterval := time.Duration(1e9 * float64(quantum) / rate) - if fillInterval <= 0 { - continue - } - tb.fillInterval = fillInterval - tb.quantum = quantum - if diff := math.Abs(tb.Rate() - rate); diff/rate <= rateMargin { - return tb - } - } - panic("cannot find suitable quantum for " + strconv.FormatFloat(rate, 'g', -1, 64)) -} - -// nextQuantum returns the next quantum to try after q. -// We grow the quantum exponentially, but slowly, so we -// get a good fit in the lower numbers. -func nextQuantum(q int64) int64 { - q1 := q * 11 / 10 - if q1 == q { - q1++ - } - return q1 -} - -// NewBucketWithQuantum is similar to NewBucket, but allows -// the specification of the quantum size - quantum tokens -// are added every fillInterval. -func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket { - return NewBucketWithQuantumAndClock(fillInterval, capacity, quantum, nil) -} - -// NewBucketWithQuantumAndClock is like NewBucketWithQuantum, but -// also has a clock argument that allows clients to fake the passing -// of time. If clock is nil, the system clock will be used. -func NewBucketWithQuantumAndClock(fillInterval time.Duration, capacity, quantum int64, clock Clock) *Bucket { - if clock == nil { - clock = realClock{} - } - if fillInterval <= 0 { - panic("token bucket fill interval is not > 0") - } - if capacity <= 0 { - panic("token bucket capacity is not > 0") - } - if quantum <= 0 { - panic("token bucket quantum is not > 0") - } - return &Bucket{ - clock: clock, - startTime: clock.Now(), - latestTick: 0, - fillInterval: fillInterval, - capacity: capacity, - quantum: quantum, - availableTokens: capacity, - } -} - -// Wait takes count tokens from the bucket, waiting until they are -// available. -func (tb *Bucket) Wait(count int64) { - if d := tb.Take(count); d > 0 { - tb.clock.Sleep(d) - } -} - -// WaitMaxDuration is like Wait except that it will -// only take tokens from the bucket if it needs to wait -// for no greater than maxWait. It reports whether -// any tokens have been removed from the bucket -// If no tokens have been removed, it returns immediately. -func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool { - d, ok := tb.TakeMaxDuration(count, maxWait) - if d > 0 { - tb.clock.Sleep(d) - } - return ok -} - -const infinityDuration time.Duration = 0x7fffffffffffffff - -// Take takes count tokens from the bucket without blocking. It returns -// the time that the caller should wait until the tokens are actually -// available. -// -// Note that if the request is irrevocable - there is no way to return -// tokens to the bucket once this method commits us to taking them. -func (tb *Bucket) Take(count int64) time.Duration { - tb.mu.Lock() - defer tb.mu.Unlock() - d, _ := tb.take(tb.clock.Now(), count, infinityDuration) - return d -} - -// TakeMaxDuration is like Take, except that -// it will only take tokens from the bucket if the wait -// time for the tokens is no greater than maxWait. -// -// If it would take longer than maxWait for the tokens -// to become available, it does nothing and reports false, -// otherwise it returns the time that the caller should -// wait until the tokens are actually available, and reports -// true. -func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool) { - tb.mu.Lock() - defer tb.mu.Unlock() - return tb.take(tb.clock.Now(), count, maxWait) -} - -// TakeAvailable takes up to count immediately available tokens from the -// bucket. It returns the number of tokens removed, or zero if there are -// no available tokens. It does not block. -func (tb *Bucket) TakeAvailable(count int64) int64 { - tb.mu.Lock() - defer tb.mu.Unlock() - return tb.takeAvailable(tb.clock.Now(), count) -} - -// takeAvailable is the internal version of TakeAvailable - it takes the -// current time as an argument to enable easy testing. -func (tb *Bucket) takeAvailable(now time.Time, count int64) int64 { - if count <= 0 { - return 0 - } - tb.adjustavailableTokens(tb.currentTick(now)) - if tb.availableTokens <= 0 { - return 0 - } - if count > tb.availableTokens { - count = tb.availableTokens - } - tb.availableTokens -= count - return count -} - -// Available returns the number of available tokens. It will be negative -// when there are consumers waiting for tokens. Note that if this -// returns greater than zero, it does not guarantee that calls that take -// tokens from the buffer will succeed, as the number of available -// tokens could have changed in the meantime. This method is intended -// primarily for metrics reporting and debugging. -func (tb *Bucket) Available() int64 { - return tb.available(tb.clock.Now()) -} - -// available is the internal version of available - it takes the current time as -// an argument to enable easy testing. -func (tb *Bucket) available(now time.Time) int64 { - tb.mu.Lock() - defer tb.mu.Unlock() - tb.adjustavailableTokens(tb.currentTick(now)) - return tb.availableTokens -} - -// Capacity returns the capacity that the bucket was created with. -func (tb *Bucket) Capacity() int64 { - return tb.capacity -} - -// Rate returns the fill rate of the bucket, in tokens per second. -func (tb *Bucket) Rate() float64 { - return 1e9 * float64(tb.quantum) / float64(tb.fillInterval) -} - -// take is the internal version of Take - it takes the current time as -// an argument to enable easy testing. -func (tb *Bucket) take(now time.Time, count int64, maxWait time.Duration) (time.Duration, bool) { - if count <= 0 { - return 0, true - } - - tick := tb.currentTick(now) - tb.adjustavailableTokens(tick) - avail := tb.availableTokens - count - if avail >= 0 { - tb.availableTokens = avail - return 0, true - } - // Round up the missing tokens to the nearest multiple - // of quantum - the tokens won't be available until - // that tick. - - // endTick holds the tick when all the requested tokens will - // become available. - endTick := tick + (-avail+tb.quantum-1)/tb.quantum - endTime := tb.startTime.Add(time.Duration(endTick) * tb.fillInterval) - waitTime := endTime.Sub(now) - if waitTime > maxWait { - return 0, false - } - tb.availableTokens = avail - return waitTime, true -} - -// currentTick returns the current time tick, measured -// from tb.startTime. -func (tb *Bucket) currentTick(now time.Time) int64 { - return int64(now.Sub(tb.startTime) / tb.fillInterval) -} - -// adjustavailableTokens adjusts the current number of tokens -// available in the bucket at the given time, which must -// be in the future (positive) with respect to tb.latestTick. -func (tb *Bucket) adjustavailableTokens(tick int64) { - if tb.availableTokens >= tb.capacity { - return - } - tb.availableTokens += (tick - tb.latestTick) * tb.quantum - if tb.availableTokens > tb.capacity { - tb.availableTokens = tb.capacity - } - tb.latestTick = tick - return -} - -// Clock represents the passage of time in a way that -// can be faked out for tests. -type Clock interface { - // Now returns the current time. - Now() time.Time - // Sleep sleeps for at least the given duration. - Sleep(d time.Duration) -} - -// realClock implements Clock in terms of standard time functions. -type realClock struct{} - -// Now implements Clock.Now by calling time.Now. -func (realClock) Now() time.Time { - return time.Now() -} - -// Now implements Clock.Sleep by calling time.Sleep. -func (realClock) Sleep(d time.Duration) { - time.Sleep(d) -} diff --git a/vendor/github.com/juju/ratelimit/reader.go b/vendor/github.com/juju/ratelimit/reader.go deleted file mode 100644 index 6403bf78..00000000 --- a/vendor/github.com/juju/ratelimit/reader.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2014 Canonical Ltd. -// Licensed under the LGPLv3 with static-linking exception. -// See LICENCE file for details. - -package ratelimit - -import "io" - -type reader struct { - r io.Reader - bucket *Bucket -} - -// Reader returns a reader that is rate limited by -// the given token bucket. Each token in the bucket -// represents one byte. -func Reader(r io.Reader, bucket *Bucket) io.Reader { - return &reader{ - r: r, - bucket: bucket, - } -} - -func (r *reader) Read(buf []byte) (int, error) { - n, err := r.r.Read(buf) - if n <= 0 { - return n, err - } - r.bucket.Wait(int64(n)) - return n, err -} - -type writer struct { - w io.Writer - bucket *Bucket -} - -// Writer returns a reader that is rate limited by -// the given token bucket. Each token in the bucket -// represents one byte. -func Writer(w io.Writer, bucket *Bucket) io.Writer { - return &writer{ - w: w, - bucket: bucket, - } -} - -func (w *writer) Write(buf []byte) (int, error) { - w.bucket.Wait(int64(len(buf))) - return w.w.Write(buf) -} diff --git a/vendor/github.com/JamesClonk/vultr/LICENSE b/vendor/github.com/vultr/govultr/LICENSE similarity index 94% rename from vendor/github.com/JamesClonk/vultr/LICENSE rename to vendor/github.com/vultr/govultr/LICENSE index be20a128..2ea4faa6 100644 --- a/vendor/github.com/JamesClonk/vultr/LICENSE +++ b/vendor/github.com/vultr/govultr/LICENSE @@ -1,6 +1,6 @@ -The MIT License (MIT) +MIT License -Copyright (c) 2015 Fabio Berchtold +Copyright (c) 2019 Vultr Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/vendor/github.com/vultr/govultr/account.go b/vendor/github.com/vultr/govultr/account.go new file mode 100644 index 00000000..33a16dfd --- /dev/null +++ b/vendor/github.com/vultr/govultr/account.go @@ -0,0 +1,45 @@ +package govultr + +import ( + "context" + "net/http" +) + +// AccountService is the interface to interact with Accounts endpoint on the Vultr API +// Link: https://www.vultr.com/api/#account +type AccountService interface { + GetInfo(ctx context.Context) (*Account, error) +} + +// AccountServiceHandler handles interaction with the account methods for the Vultr API +type AccountServiceHandler struct { + client *Client +} + +// Account represents a Vultr account +type Account struct { + Balance string `json:"balance"` + PendingCharges string `json:"pending_charges"` + LastPaymentDate string `json:"last_payment_date"` + LastPaymentAmount string `json:"last_payment_amount"` +} + +// GetInfo Vultr account info +func (a *AccountServiceHandler) GetInfo(ctx context.Context) (*Account, error) { + + uri := "/v1/account/info" + req, err := a.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + account := new(Account) + err = a.client.DoWithContext(ctx, req, account) + + if err != nil { + return nil, err + } + + return account, nil +} diff --git a/vendor/github.com/vultr/govultr/api.go b/vendor/github.com/vultr/govultr/api.go new file mode 100644 index 00000000..2ebfb99a --- /dev/null +++ b/vendor/github.com/vultr/govultr/api.go @@ -0,0 +1,44 @@ +package govultr + +import ( + "context" + "net/http" +) + +// APIService is the interface to interact with the API endpoint on the Vultr API +// Link: https://www.vultr.com/api/#auth +type APIService interface { + GetInfo(ctx context.Context) (*API, error) +} + +// APIServiceHandler handles interaction with the API methods for the Vultr API +type APIServiceHandler struct { + client *Client +} + +// API represents Vultr API information +type API struct { + ACL []string `json:"acls"` + Email string `json:"email"` + Name string `json:"name"` +} + +// GetInfo Vultr API auth information +func (a *APIServiceHandler) GetInfo(ctx context.Context) (*API, error) { + uri := "/v1/auth/info" + + req, err := a.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + api := new(API) + err = a.client.DoWithContext(ctx, req, api) + + if err != nil { + return nil, err + } + + return api, nil +} diff --git a/vendor/github.com/vultr/govultr/application.go b/vendor/github.com/vultr/govultr/application.go new file mode 100644 index 00000000..9199bd95 --- /dev/null +++ b/vendor/github.com/vultr/govultr/application.go @@ -0,0 +1,51 @@ +package govultr + +import ( + "context" + "net/http" +) + +// ApplicationService is the interface to interact with the Application endpoint on the Vultr API +// Link: https://www.vultr.com/api/#app +type ApplicationService interface { + List(ctx context.Context) ([]Application, error) +} + +// ApplicationServiceHandler handles interaction with the application methods for the Vultr API +type ApplicationServiceHandler struct { + client *Client +} + +// Application represents a Vultr application +type Application struct { + AppID string `json:"APPID"` + Name string `json:"name"` + ShortName string `json:"short_name"` + DeployName string `json:"deploy_name"` + Surcharge float64 `json:"surcharge"` +} + +// List retrieves a list of available applications that can be launched when creating a Vultr VPS +func (a *ApplicationServiceHandler) List(ctx context.Context) ([]Application, error) { + + uri := "/v1/app/list" + req, err := a.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + appsMap := make(map[string]Application) + + err = a.client.DoWithContext(ctx, req, &appsMap) + if err != nil { + return nil, err + } + + var apps []Application + for _, app := range appsMap { + apps = append(apps, app) + } + + return apps, nil +} diff --git a/vendor/github.com/vultr/govultr/backup.go b/vendor/github.com/vultr/govultr/backup.go new file mode 100644 index 00000000..0a87c640 --- /dev/null +++ b/vendor/github.com/vultr/govultr/backup.go @@ -0,0 +1,105 @@ +package govultr + +import ( + "context" + "net/http" +) + +// BackupService is the interface to interact with the backup endpoint on the Vultr API +// Link: https://www.vultr.com/api/#backup +type BackupService interface { + List(ctx context.Context) ([]Backup, error) + Get(ctx context.Context, backupID string) (*Backup, error) + ListBySub(ctx context.Context, subID string) ([]Backup, error) +} + +// BackupServiceHandler handles interaction with the backup methods for the Vultr API +type BackupServiceHandler struct { + client *Client +} + +// Backup represents a Vultr backup +type Backup struct { + BackupID string `json:"BACKUPID"` + DateCreated string `json:"date_created"` + Description string `json:"description"` + Size string `json:"size"` + Status string `json:"status"` +} + +// List retrieves a list of all backups on the current account +func (b *BackupServiceHandler) List(ctx context.Context) ([]Backup, error) { + uri := "/v1/backup/list" + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + backupsMap := make(map[string]Backup) + err = b.client.DoWithContext(ctx, req, &backupsMap) + if err != nil { + return nil, err + } + + var backups []Backup + for _, backup := range backupsMap { + backups = append(backups, backup) + } + + return backups, nil +} + +// Get retrieves a backup that matches the given backupID +func (b *BackupServiceHandler) Get(ctx context.Context, backupID string) (*Backup, error) { + uri := "/v1/backup/list" + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("BACKUPID", backupID) + req.URL.RawQuery = q.Encode() + + backupsMap := make(map[string]Backup) + err = b.client.DoWithContext(ctx, req, &backupsMap) + if err != nil { + return nil, err + } + + backup := new(Backup) + for _, bk := range backupsMap { + backup = &bk + } + + return backup, nil +} + +// ListBySub retrieves a list of all backups on the current account that match the given subID +func (b *BackupServiceHandler) ListBySub(ctx context.Context, subID string) ([]Backup, error) { + uri := "/v1/backup/list" + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", subID) + req.URL.RawQuery = q.Encode() + + backupsMap := make(map[string]Backup) + err = b.client.DoWithContext(ctx, req, &backupsMap) + if err != nil { + return nil, err + } + + var backups []Backup + for _, backup := range backupsMap { + backups = append(backups, backup) + } + + return backups, nil +} diff --git a/vendor/github.com/vultr/govultr/bare_metal_server.go b/vendor/github.com/vultr/govultr/bare_metal_server.go new file mode 100644 index 00000000..22ce4368 --- /dev/null +++ b/vendor/github.com/vultr/govultr/bare_metal_server.go @@ -0,0 +1,790 @@ +package govultr + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "net/url" + "strconv" + "strings" +) + +// BareMetalServerService is the interface to interact with the bare metal endpoints on the Vultr API +// Link: https://www.vultr.com/api/#baremetal +type BareMetalServerService interface { + AppInfo(ctx context.Context, serverID string) (*AppInfo, error) + Bandwidth(ctx context.Context, serverID string) ([]map[string]string, error) + ChangeApp(ctx context.Context, serverID, appID string) error + ChangeOS(ctx context.Context, serverID, osID string) error + Create(ctx context.Context, regionID, planID, osID string, options *BareMetalServerOptions) (*BareMetalServer, error) + Delete(ctx context.Context, serverID string) error + EnableIPV6(ctx context.Context, serverID string) error + List(ctx context.Context) ([]BareMetalServer, error) + ListByLabel(ctx context.Context, label string) ([]BareMetalServer, error) + ListByMainIP(ctx context.Context, mainIP string) ([]BareMetalServer, error) + ListByTag(ctx context.Context, tag string) ([]BareMetalServer, error) + GetServer(ctx context.Context, serverID string) (*BareMetalServer, error) + GetUserData(ctx context.Context, serverID string) (*UserData, error) + Halt(ctx context.Context, serverID string) error + IPV4Info(ctx context.Context, serverID string) ([]BareMetalServerIPV4, error) + IPV6Info(ctx context.Context, serverID string) ([]BareMetalServerIPV6, error) + ListApps(ctx context.Context, serverID string) ([]Application, error) + ListOS(ctx context.Context, serverID string) ([]OS, error) + Reboot(ctx context.Context, serverID string) error + Reinstall(ctx context.Context, serverID string) error + SetLabel(ctx context.Context, serverID, label string) error + SetTag(ctx context.Context, serverID, tag string) error + SetUserData(ctx context.Context, serverID, userData string) error +} + +// BareMetalServerServiceHandler handles interaction with the bare metal methods for the Vultr API +type BareMetalServerServiceHandler struct { + client *Client +} + +// BareMetalServer represents a bare metal server on Vultr +type BareMetalServer struct { + BareMetalServerID string `json:"SUBID"` + Os string `json:"os"` + RAM string `json:"ram"` + Disk string `json:"disk"` + MainIP string `json:"main_ip"` + CPUs int `json:"cpu_count"` + Location string `json:"location"` + RegionID int `json:"DCID"` + DefaultPassword string `json:"default_password"` + DateCreated string `json:"date_created"` + Status string `json:"status"` + NetmaskV4 string `json:"netmask_v4"` + GatewayV4 string `json:"gateway_v4"` + BareMetalPlanID int `json:"METALPLANID"` + V6Networks []V6Network `json:"v6_networks"` + Label string `json:"label"` + Tag string `json:"tag"` + OsID string `json:"OSID"` + AppID string `json:"APPID"` +} + +// BareMetalServerOptions represents the optional parameters that can be set when creating a bare metal server +type BareMetalServerOptions struct { + StartupScriptID string + SnapshotID string + EnableIPV6 string + Label string + SSHKeyIDs []string + AppID string + UserData string + NotifyActivate string + Hostname string + Tag string + ReservedIPV4 string +} + +// BareMetalServerIPV4 represents IPV4 information for a bare metal server +type BareMetalServerIPV4 struct { + IP string `json:"ip"` + Netmask string `json:"netmask"` + Gateway string `json:"gateway"` + Type string `json:"type"` +} + +// BareMetalServerIPV6 represents IPV6 information for a bare metal server +type BareMetalServerIPV6 struct { + IP string `json:"ip"` + Network string `json:"network"` + NetworkSize int `json:"network_size"` + Type string `json:"type"` +} + +// UnmarshalJSON implements a custom unmarshaler on BareMetalServer +// This is done to help reduce data inconsistency with V1 of the Vultr API +func (b *BareMetalServer) UnmarshalJSON(data []byte) error { + if b == nil { + *b = BareMetalServer{} + } + + var v map[string]interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + + cpu, err := b.unmarshalInt(fmt.Sprintf("%v", v["cpu_count"])) + if err != nil { + return err + } + b.CPUs = cpu + + region, err := b.unmarshalInt(fmt.Sprintf("%v", v["DCID"])) + if err != nil { + return err + } + b.RegionID = region + + plan, err := b.unmarshalInt(fmt.Sprintf("%v", v["METALPLANID"])) + if err != nil { + return err + } + b.BareMetalPlanID = plan + + b.BareMetalServerID = b.unmarshalStr(fmt.Sprintf("%v", v["SUBID"])) + b.Os = b.unmarshalStr(fmt.Sprintf("%v", v["os"])) + b.RAM = b.unmarshalStr(fmt.Sprintf("%v", v["ram"])) + b.Label = b.unmarshalStr(fmt.Sprintf("%v", v["label"])) + b.Disk = b.unmarshalStr(fmt.Sprintf("%v", v["disk"])) + b.MainIP = b.unmarshalStr(fmt.Sprintf("%v", v["main_ip"])) + b.Location = b.unmarshalStr(fmt.Sprintf("%v", v["location"])) + b.DefaultPassword = b.unmarshalStr(fmt.Sprintf("%v", v["default_password"])) + b.DateCreated = b.unmarshalStr(fmt.Sprintf("%v", v["date_created"])) + b.Status = b.unmarshalStr(fmt.Sprintf("%v", v["status"])) + b.NetmaskV4 = b.unmarshalStr(fmt.Sprintf("%v", v["netmask_v4"])) + b.GatewayV4 = b.unmarshalStr(fmt.Sprintf("%v", v["gateway_v4"])) + b.Tag = b.unmarshalStr(fmt.Sprintf("%v", v["tag"])) + b.OsID = b.unmarshalStr(fmt.Sprintf("%v", v["OSID"])) + b.AppID = b.unmarshalStr(fmt.Sprintf("%v", v["APPID"])) + + v6networks := make([]V6Network, 0) + if networks, ok := v["v6_networks"].([]interface{}); ok { + for _, network := range networks { + if network, ok := network.(map[string]interface{}); ok { + v6network := V6Network{ + Network: fmt.Sprintf("%v", network["v6_network"]), + MainIP: fmt.Sprintf("%v", network["v6_main_ip"]), + NetworkSize: fmt.Sprintf("%v", network["v6_network_size"]), + } + v6networks = append(v6networks, v6network) + } + } + b.V6Networks = v6networks + } + + return nil +} + +func (b *BareMetalServer) unmarshalInt(value string) (int, error) { + if len(value) == 0 || value == "" { + value = "0" + } + + v, err := strconv.Atoi(value) + if err != nil { + return 0, err + } + + return v, nil +} + +func (b *BareMetalServer) unmarshalStr(value string) string { + if value == "" { + value = "" + } + + return value +} + +// AppInfo retrieves the application information for a given server ID +func (b *BareMetalServerServiceHandler) AppInfo(ctx context.Context, serverID string) (*AppInfo, error) { + uri := "/v1/baremetal/get_app_info" + + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", serverID) + req.URL.RawQuery = q.Encode() + + appInfo := new(AppInfo) + + err = b.client.DoWithContext(ctx, req, appInfo) + + if err != nil { + return nil, err + } + + return appInfo, nil +} + +// Bandwidth will get the bandwidth used by a bare metal server +func (b *BareMetalServerServiceHandler) Bandwidth(ctx context.Context, serverID string) ([]map[string]string, error) { + uri := "/v1/baremetal/bandwidth" + + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", serverID) + req.URL.RawQuery = q.Encode() + + var bandwidthMap map[string][][]interface{} + err = b.client.DoWithContext(ctx, req, &bandwidthMap) + + if err != nil { + return nil, err + } + + var bandwidth []map[string]string + + for _, b := range bandwidthMap["incoming_bytes"] { + inMap := make(map[string]string) + inMap["date"] = fmt.Sprintf("%v", b[0]) + var bytes int64 + switch b[1].(type) { + case float64: + bytes = int64(b[1].(float64)) + case int64: + bytes = b[1].(int64) + } + inMap["incoming"] = fmt.Sprintf("%v", bytes) + bandwidth = append(bandwidth, inMap) + } + + for _, b := range bandwidthMap["outgoing_bytes"] { + for i := range bandwidth { + if bandwidth[i]["date"] == b[0] { + var bytes int64 + switch b[1].(type) { + case float64: + bytes = int64(b[1].(float64)) + case int64: + bytes = b[1].(int64) + } + bandwidth[i]["outgoing"] = fmt.Sprintf("%v", bytes) + break + } + } + } + + return bandwidth, nil +} + +// ChangeApp changes the bare metal server to a different application. +func (b *BareMetalServerServiceHandler) ChangeApp(ctx context.Context, serverID, appID string) error { + uri := "/v1/baremetal/app_change" + + values := url.Values{ + "SUBID": {serverID}, + "APPID": {appID}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// ChangeOS changes the bare metal server to a different operating system. All data will be permanently lost. +func (b *BareMetalServerServiceHandler) ChangeOS(ctx context.Context, serverID, osID string) error { + uri := "/v1/baremetal/os_change" + + values := url.Values{ + "SUBID": {serverID}, + "OSID": {osID}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Create a new bare metal server. +func (b *BareMetalServerServiceHandler) Create(ctx context.Context, regionID, planID, osID string, options *BareMetalServerOptions) (*BareMetalServer, error) { + uri := "/v1/baremetal/create" + + values := url.Values{ + "DCID": {regionID}, + "METALPLANID": {planID}, + "OSID": {osID}, + } + + if options != nil { + if options.StartupScriptID != "" { + values.Add("SCRIPTID", options.StartupScriptID) + } + if options.SnapshotID != "" { + values.Add("SNAPSHOTID", options.SnapshotID) + } + if options.EnableIPV6 != "" { + values.Add("enable_ipv6", options.EnableIPV6) + } + if options.Label != "" { + values.Add("label", options.Label) + } + if options.SSHKeyIDs != nil && len(options.SSHKeyIDs) != 0 { + values.Add("SSHKEYID", strings.Join(options.SSHKeyIDs, ",")) + } + if options.AppID != "" { + values.Add("APPID", options.AppID) + } + if options.UserData != "" { + values.Add("userdata", base64.StdEncoding.EncodeToString([]byte(options.UserData))) + } + if options.NotifyActivate != "" { + values.Add("notify_activate", options.NotifyActivate) + } + if options.Hostname != "" { + values.Add("hostname", options.Hostname) + } + if options.Tag != "" { + values.Add("tag", options.Tag) + } + if options.ReservedIPV4 != "" { + values.Add("reserved_ip_v4", options.ReservedIPV4) + } + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + bm := new(BareMetalServer) + + err = b.client.DoWithContext(ctx, req, bm) + + if err != nil { + return nil, err + } + + return bm, nil +} + +// Delete a bare metal server. +// All data will be permanently lost, and the IP address will be released. There is no going back from this call. +func (b *BareMetalServerServiceHandler) Delete(ctx context.Context, serverID string) error { + uri := "/v1/baremetal/destroy" + + values := url.Values{ + "SUBID": {serverID}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// EnableIPV6 enables IPv6 networking on a bare metal server by assigning an IPv6 subnet to it. +// The server will not be rebooted when the subnet is assigned. +func (b *BareMetalServerServiceHandler) EnableIPV6(ctx context.Context, serverID string) error { + uri := "/v1/baremetal/ipv6_enable" + + values := url.Values{ + "SUBID": {serverID}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// List lists all bare metal servers on the current account. This includes both pending and active servers. +func (b *BareMetalServerServiceHandler) List(ctx context.Context) ([]BareMetalServer, error) { + return b.list(ctx, "", "") +} + +// ListByLabel lists all bare metal servers that match the given label on the current account. This includes both pending and active servers. +func (b *BareMetalServerServiceHandler) ListByLabel(ctx context.Context, label string) ([]BareMetalServer, error) { + return b.list(ctx, "label", label) +} + +// ListByMainIP lists all bare metal servers that match the given IP address on the current account. This includes both pending and active servers. +func (b *BareMetalServerServiceHandler) ListByMainIP(ctx context.Context, mainIP string) ([]BareMetalServer, error) { + return b.list(ctx, "main_ip", mainIP) +} + +// ListByTag lists all bare metal servers that match the given tag on the current account. This includes both pending and active servers. +func (b *BareMetalServerServiceHandler) ListByTag(ctx context.Context, tag string) ([]BareMetalServer, error) { + return b.list(ctx, "tag", tag) +} + +func (b *BareMetalServerServiceHandler) list(ctx context.Context, key, value string) ([]BareMetalServer, error) { + uri := "/v1/baremetal/list" + + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + if key != "" { + q := req.URL.Query() + q.Add(key, value) + req.URL.RawQuery = q.Encode() + } + + bmsMap := make(map[string]BareMetalServer) + err = b.client.DoWithContext(ctx, req, &bmsMap) + if err != nil { + return nil, err + } + + var bms []BareMetalServer + for _, bm := range bmsMap { + bms = append(bms, bm) + } + + return bms, nil +} + +// GetServer gets the server with the given ID +func (b *BareMetalServerServiceHandler) GetServer(ctx context.Context, serverID string) (*BareMetalServer, error) { + uri := "/v1/baremetal/list" + + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", serverID) + req.URL.RawQuery = q.Encode() + + bms := new(BareMetalServer) + err = b.client.DoWithContext(ctx, req, bms) + if err != nil { + return nil, err + } + + return bms, nil +} + +// GetUserData retrieves the (base64 encoded) user-data for this bare metal server +func (b *BareMetalServerServiceHandler) GetUserData(ctx context.Context, serverID string) (*UserData, error) { + uri := "/v1/baremetal/get_user_data" + + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", serverID) + req.URL.RawQuery = q.Encode() + + userData := new(UserData) + err = b.client.DoWithContext(ctx, req, userData) + + if err != nil { + return nil, err + } + + return userData, nil +} + +// Halt a bare metal server. +// This is a hard power off, meaning that the power to the machine is severed. +// The data on the machine will not be modified, and you will still be billed for the machine. +func (b *BareMetalServerServiceHandler) Halt(ctx context.Context, serverID string) error { + uri := "/v1/baremetal/halt" + + values := url.Values{ + "SUBID": {serverID}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// IPV4Info will List the IPv4 information of a bare metal server. +// IP information is only available for bare metal servers in the "active" state. +func (b *BareMetalServerServiceHandler) IPV4Info(ctx context.Context, serverID string) ([]BareMetalServerIPV4, error) { + uri := "/v1/baremetal/list_ipv4" + + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", serverID) + + req.URL.RawQuery = q.Encode() + + var ipMap map[string][]BareMetalServerIPV4 + err = b.client.DoWithContext(ctx, req, &ipMap) + + if err != nil { + return nil, err + } + + var ipv4 []BareMetalServerIPV4 + for _, i := range ipMap { + ipv4 = i + } + + return ipv4, nil +} + +// IPV6Info ists the IPv6 information of a bare metal server. +// IP information is only available for bare metal servers in the "active" state. +// If the bare metal server does not have IPv6 enabled, then an empty array is returned. +func (b *BareMetalServerServiceHandler) IPV6Info(ctx context.Context, serverID string) ([]BareMetalServerIPV6, error) { + uri := "/v1/baremetal/list_ipv6" + + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", serverID) + req.URL.RawQuery = q.Encode() + + var ipMap map[string][]BareMetalServerIPV6 + err = b.client.DoWithContext(ctx, req, &ipMap) + + if err != nil { + return nil, err + } + + var ipv6 []BareMetalServerIPV6 + for _, i := range ipMap { + ipv6 = i + } + + return ipv6, nil +} + +// ListApps retrieves a list of Vultr one-click applications to which a bare metal server can be changed. +// Always check against this list before trying to switch applications because it is not possible to switch between every application combination. +func (b *BareMetalServerServiceHandler) ListApps(ctx context.Context, serverID string) ([]Application, error) { + uri := "/v1/baremetal/app_change_list" + + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", serverID) + req.URL.RawQuery = q.Encode() + + var appMap map[string]Application + err = b.client.DoWithContext(ctx, req, &appMap) + + if err != nil { + return nil, err + } + + var appList []Application + for _, a := range appMap { + appList = append(appList, a) + } + + return appList, nil +} + +// ListOS retrieves a list of operating systems to which a bare metal server can be changed. +// Always check against this list before trying to switch operating systems because it is not possible to switch between every operating system combination. +func (b *BareMetalServerServiceHandler) ListOS(ctx context.Context, serverID string) ([]OS, error) { + uri := "/v1/baremetal/os_change_list" + + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", serverID) + req.URL.RawQuery = q.Encode() + + var osMap map[string]OS + err = b.client.DoWithContext(ctx, req, &osMap) + + if err != nil { + return nil, err + } + + var os []OS + for _, o := range osMap { + os = append(os, o) + } + + return os, nil +} + +// Reboot a bare metal server. This is a hard reboot, which means that the server is powered off, then back on. +func (b *BareMetalServerServiceHandler) Reboot(ctx context.Context, serverID string) error { + uri := "/v1/baremetal/reboot" + + values := url.Values{ + "SUBID": {serverID}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Reinstall the operating system on a bare metal server. +// All data will be permanently lost, but the IP address will remain the same. There is no going back from this call. +func (b *BareMetalServerServiceHandler) Reinstall(ctx context.Context, serverID string) error { + uri := "/v1/baremetal/reinstall" + + values := url.Values{ + "SUBID": {serverID}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// SetLabel sets the label of a bare metal server. +func (b *BareMetalServerServiceHandler) SetLabel(ctx context.Context, serverID, label string) error { + uri := "/v1/baremetal/label_set" + + values := url.Values{ + "SUBID": {serverID}, + "label": {label}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// SetTag sets the tag of a bare metal server. +func (b *BareMetalServerServiceHandler) SetTag(ctx context.Context, serverID, tag string) error { + uri := "/v1/baremetal/tag_set" + + values := url.Values{ + "SUBID": {serverID}, + "tag": {tag}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// SetUserData sets the user-data for this server. +// User-data is a generic data store, which some provisioning tools and cloud operating systems use as a configuration file. +// It is generally consumed only once after an instance has been launched, but individual needs may vary. +func (b *BareMetalServerServiceHandler) SetUserData(ctx context.Context, serverID, userData string) error { + uri := "/v1/baremetal/set_user_data" + + encodedUserData := base64.StdEncoding.EncodeToString([]byte(userData)) + + values := url.Values{ + "SUBID": {serverID}, + "userdata": {encodedUserData}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/vultr/govultr/block_storage.go b/vendor/github.com/vultr/govultr/block_storage.go new file mode 100644 index 00000000..c62f926d --- /dev/null +++ b/vendor/github.com/vultr/govultr/block_storage.go @@ -0,0 +1,319 @@ +package govultr + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + "strconv" +) + +// BlockStorageService is the interface to interact with Block-Storage endpoint on the Vultr API +// Link: https://www.vultr.com/api/#block +type BlockStorageService interface { + Attach(ctx context.Context, blockID, InstanceID string) error + Create(ctx context.Context, regionID, size int, label string) (*BlockStorage, error) + Delete(ctx context.Context, blockID string) error + Detach(ctx context.Context, blockID string) error + SetLabel(ctx context.Context, blockID, label string) error + List(ctx context.Context) ([]BlockStorage, error) + Get(ctx context.Context, blockID string) (*BlockStorage, error) + Resize(ctx context.Context, blockID string, size int) error +} + +// BlockStorageServiceHandler handles interaction with the block-storage methods for the Vultr API +type BlockStorageServiceHandler struct { + client *Client +} + +// BlockStorage represents Vultr Block-Storage +type BlockStorage struct { + BlockStorageID string `json:"SUBID"` + DateCreated string `json:"date_created"` + CostPerMonth string `json:"cost_per_month"` + Status string `json:"status"` + SizeGB int `json:"size_gb"` + RegionID int `json:"DCID"` + InstanceID string `json:"attached_to_SUBID"` + Label string `json:"label"` +} + +// UnmarshalJSON implements json.Unmarshaller on BlockStorage to handle the inconsistent types returned from the Vultr v1 API. +func (b *BlockStorage) UnmarshalJSON(data []byte) (err error) { + if b == nil { + *b = BlockStorage{} + } + + var v map[string]interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + + b.BlockStorageID, err = b.unmarshalStr(fmt.Sprintf("%v", v["SUBID"])) + if err != nil { + return err + } + + b.RegionID, err = b.unmarshalInt(fmt.Sprintf("%v", v["DCID"])) + if err != nil { + return err + } + + b.SizeGB, err = b.unmarshalInt(fmt.Sprintf("%v", v["size_gb"])) + if err != nil { + return err + } + + b.InstanceID, err = b.unmarshalStr(fmt.Sprintf("%v", v["attached_to_SUBID"])) + if err != nil { + return err + } + + b.CostPerMonth, err = b.unmarshalStr(fmt.Sprintf("%v", v["cost_per_month"])) + if err != nil { + return err + } + + date := fmt.Sprintf("%v", v["date_created"]) + if date == "" { + date = "" + } + b.DateCreated = date + + status := fmt.Sprintf("%v", v["status"]) + if status == "" { + status = "" + } + b.Status = status + + b.Label = fmt.Sprintf("%v", v["label"]) + + return nil +} + +func (b *BlockStorage) unmarshalInt(value string) (int, error) { + if len(value) == 0 || value == "" { + value = "0" + } + + i, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return 0, err + } + + return int(i), nil +} + +func (b *BlockStorage) unmarshalStr(value string) (string, error) { + if len(value) == 0 || value == "" || value == "0" || value == "false" { + return "", nil + } + + f, err := strconv.ParseFloat(value, 64) + if err != nil { + return "", err + } + + return strconv.FormatFloat(f, 'f', -1, 64), nil +} + +// Attach will link a given block storage to a given Vultr vps +func (b *BlockStorageServiceHandler) Attach(ctx context.Context, blockID, InstanceID string) error { + + uri := "/v1/block/attach" + + values := url.Values{ + "SUBID": {blockID}, + "attach_to_SUBID": {InstanceID}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Create builds out a block storage +func (b *BlockStorageServiceHandler) Create(ctx context.Context, regionID, sizeGB int, label string) (*BlockStorage, error) { + + uri := "/v1/block/create" + + values := url.Values{ + "DCID": {strconv.Itoa(regionID)}, + "size_gb": {strconv.Itoa(sizeGB)}, + "label": {label}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + blockStorage := new(BlockStorage) + + err = b.client.DoWithContext(ctx, req, blockStorage) + + if err != nil { + return nil, err + } + + blockStorage.RegionID = regionID + blockStorage.Label = label + blockStorage.SizeGB = sizeGB + + return blockStorage, nil +} + +// Delete will remove block storage instance from your Vultr account +func (b *BlockStorageServiceHandler) Delete(ctx context.Context, blockID string) error { + + uri := "/v1/block/delete" + + values := url.Values{ + "SUBID": {blockID}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Detach will de-link a given block storage to the Vultr vps it is attached to +func (b *BlockStorageServiceHandler) Detach(ctx context.Context, blockID string) error { + + uri := "/v1/block/detach" + + values := url.Values{ + "SUBID": {blockID}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// SetLabel allows you to set/update the label on your Vultr Block storage +func (b *BlockStorageServiceHandler) SetLabel(ctx context.Context, blockID, label string) error { + uri := "/v1/block/label_set" + + values := url.Values{ + "SUBID": {blockID}, + "label": {label}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// List returns a list of all block storage instances on your Vultr Account +func (b *BlockStorageServiceHandler) List(ctx context.Context) ([]BlockStorage, error) { + + uri := "/v1/block/list" + + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + var blockStorage []BlockStorage + err = b.client.DoWithContext(ctx, req, &blockStorage) + + if err != nil { + return nil, err + } + + return blockStorage, nil +} + +// Get returns a single block storage instance based ony our blockID you provide from your Vultr Account +func (b *BlockStorageServiceHandler) Get(ctx context.Context, blockID string) (*BlockStorage, error) { + + uri := "/v1/block/list" + + req, err := b.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", blockID) + req.URL.RawQuery = q.Encode() + + blockStorage := new(BlockStorage) + err = b.client.DoWithContext(ctx, req, blockStorage) + + if err != nil { + return nil, err + } + + return blockStorage, nil +} + +// Resize allows you to resize your Vultr block storage instance +func (b *BlockStorageServiceHandler) Resize(ctx context.Context, blockID string, sizeGB int) error { + + uri := "/v1/block/resize" + + values := url.Values{ + "SUBID": {blockID}, + "size_gb": {strconv.Itoa(sizeGB)}, + } + + req, err := b.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = b.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/vultr/govultr/dns_domains.go b/vendor/github.com/vultr/govultr/dns_domains.go new file mode 100644 index 00000000..dbd9913b --- /dev/null +++ b/vendor/github.com/vultr/govultr/dns_domains.go @@ -0,0 +1,209 @@ +package govultr + +import ( + "context" + "net/http" + "net/url" +) + +// DNSDomainService is the interface to interact with the DNS endpoints on the Vultr API +// Link: https://www.vultr.com/api/#dns +type DNSDomainService interface { + Create(ctx context.Context, domain, InstanceIP string) error + Delete(ctx context.Context, domain string) error + ToggleDNSSec(ctx context.Context, domain string, enabled bool) error + DNSSecInfo(ctx context.Context, domain string) ([]string, error) + List(ctx context.Context) ([]DNSDomain, error) + GetSoa(ctx context.Context, domain string) (*Soa, error) + UpdateSoa(ctx context.Context, domain, nsPrimary, email string) error +} + +// DNSDomainServiceHandler handles interaction with the DNS methods for the Vultr API +type DNSDomainServiceHandler struct { + client *Client +} + +// DNSDomain represents a DNS Domain entry on Vultr +type DNSDomain struct { + Domain string `json:"domain"` + DateCreated string `json:"date_created"` +} + +// Soa represents record information for a domain on Vultr +type Soa struct { + NsPrimary string `json:"nsprimary"` + Email string `json:"email"` +} + +// Create will create a DNS Domain entry on Vultr +func (d *DNSDomainServiceHandler) Create(ctx context.Context, domain, InstanceIP string) error { + + uri := "/v1/dns/create_domain" + + values := url.Values{ + "domain": {domain}, + "serverip": {InstanceIP}, + } + + req, err := d.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = d.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +//Delete will delete a domain name and all associated records +func (d *DNSDomainServiceHandler) Delete(ctx context.Context, domain string) error { + uri := "/v1/dns/delete_domain" + + values := url.Values{ + "domain": {domain}, + } + + req, err := d.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = d.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// ToggleDNSSec will enable or disable DNSSEC for a domain on Vultr +func (d *DNSDomainServiceHandler) ToggleDNSSec(ctx context.Context, domain string, enabled bool) error { + + uri := "/v1/dns/dnssec_enable" + + enable := "no" + if enabled == true { + enable = "yes" + } + + values := url.Values{ + "domain": {domain}, + "enable": {enable}, + } + + req, err := d.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = d.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// DNSSecInfo gets the DNSSec keys for a domain (if enabled) +func (d *DNSDomainServiceHandler) DNSSecInfo(ctx context.Context, domain string) ([]string, error) { + + uri := "/v1/dns/dnssec_info" + + req, err := d.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("domain", domain) + req.URL.RawQuery = q.Encode() + + var DNSSec []string + err = d.client.DoWithContext(ctx, req, &DNSSec) + + if err != nil { + return nil, err + } + + return DNSSec, nil +} + +// List gets all domains associated with the current Vultr account. +func (d *DNSDomainServiceHandler) List(ctx context.Context) ([]DNSDomain, error) { + uri := "/v1/dns/list" + + req, err := d.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + var dnsDomains []DNSDomain + err = d.client.DoWithContext(ctx, req, &dnsDomains) + + if err != nil { + return nil, err + } + + return dnsDomains, nil +} + +// GetSoa gets the SOA record information for a domain +func (d *DNSDomainServiceHandler) GetSoa(ctx context.Context, domain string) (*Soa, error) { + uri := "/v1/dns/soa_info" + + req, err := d.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("domain", domain) + req.URL.RawQuery = q.Encode() + + soa := new(Soa) + err = d.client.DoWithContext(ctx, req, soa) + + if err != nil { + return nil, err + } + + return soa, nil +} + +// UpdateSoa will update the SOA record information for a domain. +func (d *DNSDomainServiceHandler) UpdateSoa(ctx context.Context, domain, nsPrimary, email string) error { + + uri := "/v1/dns/soa_update" + + values := url.Values{ + "domain": {domain}, + "nsprimary": {nsPrimary}, + "email": {email}, + } + + req, err := d.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = d.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/vultr/govultr/dns_records.go b/vendor/github.com/vultr/govultr/dns_records.go new file mode 100644 index 00000000..29260a5b --- /dev/null +++ b/vendor/github.com/vultr/govultr/dns_records.go @@ -0,0 +1,150 @@ +package govultr + +import ( + "context" + "net/http" + "net/url" + "strconv" +) + +// DNSRecordService is the interface to interact with the DNS Records endpoints on the Vultr API +// Link: https://www.vultr.com/api/#dns +type DNSRecordService interface { + Create(ctx context.Context, domain, recordType, name, data string, ttl, priority int) error + Delete(ctx context.Context, domain, recordID string) error + List(ctx context.Context, domain string) ([]DNSRecord, error) + Update(ctx context.Context, domain string, dnsRecord *DNSRecord) error +} + +// DNSRecordsServiceHandler handles interaction with the DNS Records methods for the Vultr API +type DNSRecordsServiceHandler struct { + client *Client +} + +// DNSRecord represents a DNS record on Vultr +type DNSRecord struct { + RecordID int `json:"RECORDID"` + Type string `json:"type"` + Name string `json:"name"` + Data string `json:"data"` + Priority int `json:"priority"` + TTL int `json:"ttl"` +} + +// Create will add a DNS record. +func (d *DNSRecordsServiceHandler) Create(ctx context.Context, domain, recordType, name, data string, ttl, priority int) error { + + uri := "/v1/dns/create_record" + + values := url.Values{ + "domain": {domain}, + "name": {name}, + "type": {recordType}, + "data": {data}, + "ttl": {strconv.Itoa(ttl)}, + "priority": {strconv.Itoa(priority)}, + } + + req, err := d.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = d.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Delete will delete a domain name and all associated records. +func (d *DNSRecordsServiceHandler) Delete(ctx context.Context, domain, recordID string) error { + + uri := "/v1/dns/delete_record" + + values := url.Values{ + "domain": {domain}, + "RECORDID": {recordID}, + } + + req, err := d.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = d.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// List will list all the records associated with a particular domain on Vultr +func (d *DNSRecordsServiceHandler) List(ctx context.Context, domain string) ([]DNSRecord, error) { + + uri := "/v1/dns/records" + + req, err := d.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("domain", domain) + req.URL.RawQuery = q.Encode() + + var dnsRecord []DNSRecord + err = d.client.DoWithContext(ctx, req, &dnsRecord) + + if err != nil { + return nil, err + } + + return dnsRecord, nil +} + +// Update will update a DNS record +func (d *DNSRecordsServiceHandler) Update(ctx context.Context, domain string, dnsRecord *DNSRecord) error { + + uri := "/v1/dns/update_record" + + values := url.Values{ + "domain": {domain}, + "RECORDID": {strconv.Itoa(dnsRecord.RecordID)}, + } + + // Optional + if dnsRecord.Name != "" { + values.Add("name", dnsRecord.Name) + } + if dnsRecord.Data != "" { + values.Add("data", dnsRecord.Data) + } + if dnsRecord.TTL != 0 { + values.Add("ttl", strconv.Itoa(dnsRecord.TTL)) + } + if dnsRecord.Priority != 0 { + values.Add("priority", strconv.Itoa(dnsRecord.Priority)) + } + + req, err := d.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = d.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/vultr/govultr/firewall_group.go b/vendor/github.com/vultr/govultr/firewall_group.go new file mode 100644 index 00000000..ee874d10 --- /dev/null +++ b/vendor/github.com/vultr/govultr/firewall_group.go @@ -0,0 +1,163 @@ +package govultr + +import ( + "context" + "net/http" + "net/url" +) + +// FirewallGroupService is the interface to interact with the firewall group endpoints on the Vultr API +// Link: https://www.vultr.com/api/#firewall +type FirewallGroupService interface { + Create(ctx context.Context, description string) (*FirewallGroup, error) + Delete(ctx context.Context, groupID string) error + List(ctx context.Context) ([]FirewallGroup, error) + Get(ctx context.Context, groupID string) (*FirewallGroup, error) + ChangeDescription(ctx context.Context, groupID, description string) error +} + +// FireWallGroupServiceHandler handles interaction with the firewall group methods for the Vultr API +type FireWallGroupServiceHandler struct { + client *Client +} + +// FirewallGroup represents a Vultr firewall group +type FirewallGroup struct { + FirewallGroupID string `json:"FIREWALLGROUPID"` + Description string `json:"description"` + DateCreated string `json:"date_created"` + DateModified string `json:"date_modified"` + InstanceCount int `json:"instance_count"` + RuleCount int `json:"rule_count"` + MaxRuleCount int `json:"max_rule_count"` +} + +// Create will create a new firewall group on your Vultr account +func (f *FireWallGroupServiceHandler) Create(ctx context.Context, description string) (*FirewallGroup, error) { + + uri := "/v1/firewall/group_create" + + values := url.Values{ + "description": {description}, + } + + req, err := f.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + firewall := new(FirewallGroup) + err = f.client.DoWithContext(ctx, req, firewall) + + if err != nil { + return nil, err + } + + return firewall, nil +} + +// Delete will delete a firewall group from your Vultr account +func (f *FireWallGroupServiceHandler) Delete(ctx context.Context, groupID string) error { + + uri := "/v1/firewall/group_delete" + + values := url.Values{ + "FIREWALLGROUPID": {groupID}, + } + + req, err := f.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = f.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// List will return a list of all firewall groups on your Vultr account +func (f *FireWallGroupServiceHandler) List(ctx context.Context) ([]FirewallGroup, error) { + + uri := "/v1/firewall/group_list" + + req, err := f.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + var firewallGroupMap map[string]FirewallGroup + err = f.client.DoWithContext(ctx, req, &firewallGroupMap) + + if err != nil { + return nil, err + } + + var firewallGroup []FirewallGroup + for _, f := range firewallGroupMap { + firewallGroup = append(firewallGroup, f) + } + + return firewallGroup, nil +} + +// Get will return a firewall group based on provided groupID from your Vultr account +func (f *FireWallGroupServiceHandler) Get(ctx context.Context, groupID string) (*FirewallGroup, error) { + + uri := "/v1/firewall/group_list" + + req, err := f.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("FIREWALLGROUPID", groupID) + req.URL.RawQuery = q.Encode() + + var firewallGroupMap map[string]FirewallGroup + err = f.client.DoWithContext(ctx, req, &firewallGroupMap) + + if err != nil { + return nil, err + } + + firewallGroup := new(FirewallGroup) + for _, f := range firewallGroupMap { + firewallGroup = &f + } + + return firewallGroup, nil +} + +// ChangeDescription will change the description of a firewall group +func (f *FireWallGroupServiceHandler) ChangeDescription(ctx context.Context, groupID, description string) error { + + uri := "/v1/firewall/group_set_description" + + values := url.Values{ + "FIREWALLGROUPID": {groupID}, + "description": {description}, + } + + req, err := f.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = f.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/vultr/govultr/firewall_rule.go b/vendor/github.com/vultr/govultr/firewall_rule.go new file mode 100644 index 00000000..ab99c1ab --- /dev/null +++ b/vendor/github.com/vultr/govultr/firewall_rule.go @@ -0,0 +1,265 @@ +package govultr + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net" + "net/http" + "net/url" + "strconv" +) + +// FireWallRuleService is the interface to interact with the firewall rule endpoints on the Vultr API +// Link: https://www.vultr.com/api/#firewall +type FireWallRuleService interface { + Create(ctx context.Context, groupID, protocol, port, network, notes string) (*FirewallRule, error) + Delete(ctx context.Context, groupID, ruleID string) error + ListByIPType(ctx context.Context, groupID, ipType string) ([]FirewallRule, error) + List(ctx context.Context, groupID string) ([]FirewallRule, error) +} + +// FireWallRuleServiceHandler handles interaction with the firewall rule methods for the Vultr API +type FireWallRuleServiceHandler struct { + client *Client +} + +// FirewallRule represents a Vultr firewall rule +type FirewallRule struct { + RuleNumber int `json:"rulenumber"` + Action string `json:"action"` + Protocol string `json:"protocol"` + Port string `json:"port"` + Network *net.IPNet `json:"network"` + Notes string `json:"notes"` +} + +// UnmarshalJSON implements a custom unmarshaler on FirewallRule +// This is done to help reduce data inconsistency with V1 of the Vultr API +// It also merges the subnet & subnet_mask into a single type of *net.IPNet +func (f *FirewallRule) UnmarshalJSON(data []byte) (err error) { + if f == nil { + *f = FirewallRule{} + } + + // Pull out all of the data that was given to us and put it into a map + var fields map[string]interface{} + err = json.Unmarshal(data, &fields) + + if err != nil { + return err + } + + // Unmarshal RuleNumber + value := fmt.Sprintf("%v", fields["rulenumber"]) + number, _ := strconv.Atoi(value) + f.RuleNumber = number + + // Unmarshal all other strings + + action := fmt.Sprintf("%v", fields["action"]) + if action == "" { + action = "" + } + f.Action = action + + protocol := fmt.Sprintf("%v", fields["protocol"]) + if protocol == "" { + protocol = "" + } + f.Protocol = protocol + + port := fmt.Sprintf("%v", fields["port"]) + if port == "" { + port = "" + } + f.Port = port + + notes := fmt.Sprintf("%v", fields["notes"]) + if notes == "" { + notes = "" + } + f.Notes = notes + + // Unmarshal subnet_size & subnet and convert to *net.IP + value = fmt.Sprintf("%v", fields["subnet_size"]) + if len(value) == 0 || value == "" { + value = "0" + } + subnetSize, _ := strconv.Atoi(value) + + subnet := fmt.Sprintf("%v", fields["subnet"]) + if subnet == "" { + subnet = "" + } + + if len(subnet) > 0 { + _, ipNet, err := net.ParseCIDR(fmt.Sprintf("%s/%d", subnet, subnetSize)) + + if err != nil { + return errors.New("an issue has occurred while parsing subnet") + } + + f.Network = ipNet + } + + return +} + +// Create will create a rule in a firewall group. +func (f *FireWallRuleServiceHandler) Create(ctx context.Context, groupID, protocol, port, cdirBlock, notes string) (*FirewallRule, error) { + + uri := "/v1/firewall/rule_create" + + ip, ipNet, err := net.ParseCIDR(cdirBlock) + + if err != nil { + return nil, err + } + + values := url.Values{ + "FIREWALLGROUPID": {groupID}, + "direction": {"in"}, + "protocol": {protocol}, + "subnet": {ip.String()}, + } + + // mask + mask, _ := ipNet.Mask.Size() + values.Add("subnet_size", strconv.Itoa(mask)) + + // ip Type + if ipNet.IP.To4() != nil { + values.Add("ip_type", "v4") + } else { + values.Add("ip_type", "v6") + } + + // Optional params + if port != "" { + values.Add("port", port) + } + + if notes != "" { + values.Add("notes", notes) + } + + req, err := f.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + firewallRule := new(FirewallRule) + err = f.client.DoWithContext(ctx, req, firewallRule) + + if err != nil { + return nil, err + } + + return firewallRule, nil +} + +// Delete will delete a firewall rule on your Vultr account +func (f *FireWallRuleServiceHandler) Delete(ctx context.Context, groupID, ruleID string) error { + + uri := "/v1/firewall/rule_delete" + + values := url.Values{ + "FIREWALLGROUPID": {groupID}, + "rulenumber": {ruleID}, + } + + req, err := f.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = f.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// List will list the current firewall rules in a firewall group. +// ipType values that can be passed in are "v4", "v6" +func (f *FireWallRuleServiceHandler) ListByIPType(ctx context.Context, groupID, ipType string) ([]FirewallRule, error) { + + uri := "/v1/firewall/rule_list" + + req, err := f.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("FIREWALLGROUPID", groupID) + q.Add("direction", "in") + q.Add("ip_type", ipType) + req.URL.RawQuery = q.Encode() + var firewallRuleMap map[string]FirewallRule + + err = f.client.DoWithContext(ctx, req, &firewallRuleMap) + + if err != nil { + return nil, err + } + + var firewallRule []FirewallRule + for _, f := range firewallRuleMap { + firewallRule = append(firewallRule, f) + } + + return firewallRule, nil +} + +// List will return both ipv4 an ipv6 firewall rules that are defined within a firewall group +func (f *FireWallRuleServiceHandler) List(ctx context.Context, groupID string) ([]FirewallRule, error) { + uri := "/v1/firewall/rule_list" + + req, err := f.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("FIREWALLGROUPID", groupID) + q.Add("direction", "in") + q.Add("ip_type", "v4") + + req.URL.RawQuery = q.Encode() + + var firewallRuleMap map[string]FirewallRule + + // V4 call + err = f.client.DoWithContext(ctx, req, &firewallRuleMap) + + if err != nil { + return nil, err + } + + // V6 call + q.Del("ip_type") + q.Add("ip_type", "v6") + req.URL.RawQuery = q.Encode() + + err = f.client.DoWithContext(ctx, req, &firewallRuleMap) + + if err != nil { + return nil, err + } + + var firewallRule []FirewallRule + for _, f := range firewallRuleMap { + firewallRule = append(firewallRule, f) + } + + return firewallRule, nil +} diff --git a/vendor/github.com/vultr/govultr/govultr.go b/vendor/github.com/vultr/govultr/govultr.go new file mode 100644 index 00000000..b0520893 --- /dev/null +++ b/vendor/github.com/vultr/govultr/govultr.go @@ -0,0 +1,222 @@ +package govultr + +import ( + "context" + "encoding/json" + "errors" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + "time" +) + +const ( + version = "0.1.4" + defaultBase = "https://api.vultr.com" + userAgent = "govultr/" + version + rateLimit = 600 * time.Millisecond +) + +// APIKey contains a users API Key for interacting with the API +type APIKey struct { + // API Key + key string +} + +// Client manages interaction with the Vultr V1 API +type Client struct { + // Http Client used to interact with the Vultr V1 API + client *http.Client + + // BASE URL for APIs + BaseURL *url.URL + + // User Agent for the client + UserAgent string + + // API Key + APIKey APIKey + + // API Rate Limit - Vultr rate limits based on time + RateLimit time.Duration + + // Services used to interact with the API + Account AccountService + API APIService + Application ApplicationService + Backup BackupService + BareMetalServer BareMetalServerService + BlockStorage BlockStorageService + DNSDomain DNSDomainService + DNSRecord DNSRecordService + FirewallGroup FirewallGroupService + FirewallRule FireWallRuleService + ISO ISOService + Network NetworkService + OS OSService + Plan PlanService + Region RegionService + ReservedIP ReservedIPService + Server ServerService + Snapshot SnapshotService + SSHKey SSHKeyService + StartupScript StartupScriptService + User UserService + + // 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, key string) *Client { + + if httpClient == nil { + httpClient = http.DefaultClient + } + + baseURL, _ := url.Parse(defaultBase) + + client := &Client{ + client: httpClient, + BaseURL: baseURL, + UserAgent: userAgent, + RateLimit: rateLimit, + } + + client.Account = &AccountServiceHandler{client} + client.API = &APIServiceHandler{client} + client.Application = &ApplicationServiceHandler{client} + client.Backup = &BackupServiceHandler{client} + client.BareMetalServer = &BareMetalServerServiceHandler{client} + client.BlockStorage = &BlockStorageServiceHandler{client} + client.DNSDomain = &DNSDomainServiceHandler{client} + client.DNSRecord = &DNSRecordsServiceHandler{client} + client.FirewallGroup = &FireWallGroupServiceHandler{client} + client.FirewallRule = &FireWallRuleServiceHandler{client} + client.ISO = &ISOServiceHandler{client} + client.Network = &NetworkServiceHandler{client} + client.OS = &OSServiceHandler{client} + client.Plan = &PlanServiceHandler{client} + client.Region = &RegionServiceHandler{client} + client.Server = &ServerServiceHandler{client} + client.ReservedIP = &ReservedIPServiceHandler{client} + client.Snapshot = &SnapshotServiceHandler{client} + client.SSHKey = &SSHKeyServiceHandler{client} + client.StartupScript = &StartupScriptServiceHandler{client} + client.User = &UserServiceHandler{client} + + apiKey := APIKey{key: key} + client.APIKey = apiKey + + return client +} + +// NewRequest creates an API Request +func (c *Client) NewRequest(ctx context.Context, method, uri string, body url.Values) (*http.Request, error) { + + path, err := url.Parse(uri) + resolvedURL := c.BaseURL.ResolveReference(path) + + if err != nil { + return nil, err + } + + var reqBody io.Reader + + if body != nil { + reqBody = strings.NewReader(body.Encode()) + } else { + reqBody = nil + } + + req, err := http.NewRequest(method, resolvedURL.String(), reqBody) + + if err != nil { + return nil, err + } + + req.Header.Add("API-key", c.APIKey.key) + req.Header.Add("User-Agent", c.UserAgent) + req.Header.Add("Accept", "application/json") + + if req.Method == "POST" { + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + } + + 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 { + + // Sleep this call + time.Sleep(c.RateLimit) + + req := r.WithContext(ctx) + res, err := c.client.Do(req) + + if c.onRequestCompleted != nil { + c.onRequestCompleted(req, 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 { + if data != nil { + if string(body) == "[]" { + data = nil + } else { + 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 +func (c *Client) SetRateLimit(time time.Duration) { + c.RateLimit = 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 +} diff --git a/vendor/github.com/vultr/govultr/iso.go b/vendor/github.com/vultr/govultr/iso.go new file mode 100644 index 00000000..8cedd34c --- /dev/null +++ b/vendor/github.com/vultr/govultr/iso.go @@ -0,0 +1,142 @@ +package govultr + +import ( + "context" + "net/http" + "net/url" + "strconv" +) + +// ISOService is the interface to interact with the ISO endpoints on the Vultr API +// Link: https://www.vultr.com/api/#ISO +type ISOService interface { + CreateFromURL(ctx context.Context, ISOURL string) (*ISO, error) + Delete(ctx context.Context, ISOID int) error + List(ctx context.Context) ([]ISO, error) + GetPublicList(ctx context.Context) ([]PublicISO, error) +} + +// ISOServiceHandler handles interaction with the ISO methods for the Vultr API +type ISOServiceHandler struct { + Client *Client +} + +// ISO represents ISOs currently available on this account. +type ISO struct { + ISOID int `json:"ISOID"` + DateCreated string `json:"date_created"` + FileName string `json:"filename"` + Size int `json:"size"` + MD5Sum string `json:"md5sum"` + SHA512Sum string `json:"sha512sum"` + Status string `json:"status"` +} + +// PublicISO represents public ISOs offered in the Vultr ISO library. +type PublicISO struct { + ISOID int `json:"ISOID"` + Name string `json:"name"` + Description string `json:"description"` +} + +// CreateFromURL will create a new ISO image on your account +func (i *ISOServiceHandler) CreateFromURL(ctx context.Context, ISOURL string) (*ISO, error) { + + uri := "/v1/iso/create_from_url" + + values := url.Values{ + "url": {ISOURL}, + } + + req, err := i.Client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + iso := new(ISO) + err = i.Client.DoWithContext(ctx, req, iso) + + if err != nil { + return nil, err + } + + return iso, nil +} + +// Delete will delete an ISO image from your account +func (i *ISOServiceHandler) Delete(ctx context.Context, isoID int) error { + + uri := "/v1/iso/destroy" + + values := url.Values{ + "ISOID": {strconv.Itoa(isoID)}, + } + + req, err := i.Client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = i.Client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// List will list all ISOs currently available on your account +func (i *ISOServiceHandler) List(ctx context.Context) ([]ISO, error) { + + uri := "/v1/iso/list" + + req, err := i.Client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + var ISOMap map[string]ISO + err = i.Client.DoWithContext(ctx, req, &ISOMap) + + if err != nil { + return nil, err + } + + var iso []ISO + for _, i := range ISOMap { + iso = append(iso, i) + } + + return iso, nil +} + +// GetPublicList will list public ISOs offered in the Vultr ISO library. +func (i *ISOServiceHandler) GetPublicList(ctx context.Context) ([]PublicISO, error) { + + uri := "/v1/iso/list_public" + + req, err := i.Client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + var ISOMap map[string]PublicISO + err = i.Client.DoWithContext(ctx, req, &ISOMap) + + if err != nil { + return nil, err + } + + var publicISO []PublicISO + + for _, p := range ISOMap { + publicISO = append(publicISO, p) + } + + return publicISO, nil +} diff --git a/vendor/github.com/vultr/govultr/network.go b/vendor/github.com/vultr/govultr/network.go new file mode 100644 index 00000000..d6c32ea2 --- /dev/null +++ b/vendor/github.com/vultr/govultr/network.go @@ -0,0 +1,120 @@ +package govultr + +import ( + "context" + "net" + "net/http" + "net/url" + "strconv" +) + +// NetworkService is the interface to interact with the network endpoints on the Vultr API +// Link: https://www.vultr.com/api/#network +type NetworkService interface { + Create(ctx context.Context, regionID, description, cidrBlock string) (*Network, error) + Delete(ctx context.Context, networkID string) error + List(ctx context.Context) ([]Network, error) +} + +// NetworkServiceHandler handles interaction with the network methods for the Vultr API +type NetworkServiceHandler struct { + client *Client +} + +// Network represents a Vultr private network +type Network struct { + NetworkID string `json:"NETWORKID"` + RegionID string `json:"DCID"` + Description string `json:"description"` + V4Subnet string `json:"v4_subnet"` + V4SubnetMask int `json:"v4_subnet_mask"` + DateCreated string `json:"date_created"` +} + +// Create a new private network. A private network can only be used at the location for which it was created. +func (n *NetworkServiceHandler) Create(ctx context.Context, regionID, description, cidrBlock string) (*Network, error) { + + uri := "/v1/network/create" + + values := url.Values{ + "DCID": {regionID}, + } + + // Optional + if cidrBlock != "" { + _, ipNet, err := net.ParseCIDR(cidrBlock) + if err != nil { + return nil, err + } + if v4Subnet := ipNet.IP.To4(); v4Subnet != nil { + values.Add("v4_subnet", v4Subnet.String()) + } + mask, _ := ipNet.Mask.Size() + values.Add("v4_subnet_mask", strconv.Itoa(mask)) + } + + if description != "" { + values.Add("description", description) + } + + req, err := n.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + network := new(Network) + err = n.client.DoWithContext(ctx, req, network) + + if err != nil { + return nil, err + } + + return network, nil +} + +// Delete a private network. Before deleting, a network must be disabled from all instances. See https://www.vultr.com/api/#server_private_network_disable +func (n *NetworkServiceHandler) Delete(ctx context.Context, networkID string) error { + uri := "/v1/network/destroy" + + values := url.Values{ + "NETWORKID": {networkID}, + } + + req, err := n.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = n.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// List lists all private networks on the current account +func (n *NetworkServiceHandler) List(ctx context.Context) ([]Network, error) { + uri := "/v1/network/list" + + req, err := n.client.NewRequest(ctx, http.MethodGet, uri, nil) + if err != nil { + return nil, err + } + + var networkMap map[string]Network + err = n.client.DoWithContext(ctx, req, &networkMap) + if err != nil { + return nil, err + } + + var networks []Network + for _, network := range networkMap { + networks = append(networks, network) + } + + return networks, nil +} diff --git a/vendor/github.com/vultr/govultr/os.go b/vendor/github.com/vultr/govultr/os.go new file mode 100644 index 00000000..933ddaff --- /dev/null +++ b/vendor/github.com/vultr/govultr/os.go @@ -0,0 +1,84 @@ +package govultr + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strconv" +) + +// OSService is the interface to interact with the operating system endpoint on the Vultr API +// Link: https://www.vultr.com/api/#os +type OSService interface { + List(ctx context.Context) ([]OS, error) +} + +// OSServiceHandler handles interaction with the operating system methods for the Vultr API +type OSServiceHandler struct { + client *Client +} + +// OS represents a Vultr operating system +type OS struct { + OsID int `json:"OSID"` + Name string `json:"name"` + Arch string `json:"arch"` + Family string `json:"family"` + Windows bool `json:"windows"` +} + +// UnmarshalJSON implements json.Unmarshaller on OS to handle the inconsistent types returned from the Vultr API. +func (o *OS) UnmarshalJSON(data []byte) (err error) { + if o == nil { + *o = OS{} + } + + var v map[string]interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + + i, err := strconv.Atoi(fmt.Sprintf("%v", v["OSID"])) + if err != nil { + return err + } + o.OsID = i + + value := fmt.Sprintf("%v", v["windows"]) + o.Windows = false + if value == "true" { + o.Windows = true + } + + o.Name = fmt.Sprintf("%v", v["name"]) + o.Arch = fmt.Sprintf("%v", v["arch"]) + o.Family = fmt.Sprintf("%v", v["family"]) + + return nil +} + +// List retrieves a list of available operating systems. +// If the Windows flag is true, a Windows license will be included with the instance, which will increase the cost. +func (o *OSServiceHandler) List(ctx context.Context) ([]OS, error) { + uri := "/v1/os/list" + req, err := o.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + osMap := make(map[string]OS) + + err = o.client.DoWithContext(ctx, req, &osMap) + if err != nil { + return nil, err + } + + var oses []OS + for _, os := range osMap { + oses = append(oses, os) + } + + return oses, nil +} diff --git a/vendor/github.com/vultr/govultr/plans.go b/vendor/github.com/vultr/govultr/plans.go new file mode 100644 index 00000000..75287d5a --- /dev/null +++ b/vendor/github.com/vultr/govultr/plans.go @@ -0,0 +1,199 @@ +package govultr + +import ( + "context" + "net/http" +) + +// PlanService is the interface to interact with the Plans endpoints on the Vultr API +// Link: https://www.vultr.com/api/#plans +type PlanService interface { + List(ctx context.Context, planType string) ([]Plan, error) + GetBareMetalList(ctx context.Context) ([]BareMetalPlan, error) + GetVc2List(ctx context.Context) ([]VCPlan, error) + GetVdc2List(ctx context.Context) ([]VCPlan, error) + GetVc2zList(ctx context.Context) ([]VCPlan, error) +} + +// PlanServiceHandler handles interaction with the Plans methods for the Vultr API +type PlanServiceHandler struct { + Client *Client +} + +// Plan represents available Plans that Vultr offers +type Plan struct { + PlanID int `json:"VPSPLANID,string"` + Name string `json:"name"` + VCPUs int `json:"vcpu_count,string"` + RAM string `json:"ram"` + Disk string `json:"disk"` + Bandwidth string `json:"bandwidth"` + BandwidthGB string `json:"bandwidth_gb"` + Price string `json:"price_per_month"` + Windows bool `json:"windows"` + PlanType string `json:"plan_type"` + Regions []int `json:"available_locations"` + Deprecated bool `json:"deprecated"` +} + +// BareMetalPlan represents bare metal plans +type BareMetalPlan struct { + PlanID string `json:"METALPLANID"` + Name string `json:"name"` + CPUs int `json:"cpu_count"` + CPUModel string `json:"cpu_model"` + RAM int `json:"ram"` + Disk string `json:"disk"` + BandwidthTB int `json:"bandwidth_tb"` + Price int `json:"price_per_month"` + PlanType string `json:"plan_type"` + Deprecated bool `json:"deprecated"` + Regions []int `json:"available_locations"` +} + +// VCPlan represents either a vdc2 or a vc2 plan +type VCPlan struct { + PlanID string `json:"VPSPLANID"` + Name string `json:"name"` + VCPUs string `json:"vcpu_count"` + RAM string `json:"ram"` + Disk string `json:"disk"` + Bandwidth string `json:"bandwidth"` + BandwidthGB string `json:"bandwidth_gb"` + Price string `json:"price_per_month"` + PlanType string `json:"plan_type"` +} + +// List retrieves a list of all active plans. +// planType is optional - pass an empty string to get all plans +func (p *PlanServiceHandler) List(ctx context.Context, planType string) ([]Plan, error) { + + uri := "/v1/plans/list" + + req, err := p.Client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + if planType != "" { + q := req.URL.Query() + q.Add("type", planType) + req.URL.RawQuery = q.Encode() + } + + var planMap map[string]Plan + err = p.Client.DoWithContext(ctx, req, &planMap) + + if err != nil { + return nil, err + } + + var plans []Plan + for _, p := range planMap { + plans = append(plans, p) + } + + return plans, nil +} + +// GetBareMetalList retrieves a list of all active bare metal plans. +func (p *PlanServiceHandler) GetBareMetalList(ctx context.Context) ([]BareMetalPlan, error) { + + uri := "/v1/plans/list_baremetal" + + req, err := p.Client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + var bareMetalMap map[string]BareMetalPlan + err = p.Client.DoWithContext(ctx, req, &bareMetalMap) + + if err != nil { + return nil, err + } + + var bareMetalPlan []BareMetalPlan + for _, b := range bareMetalMap { + bareMetalPlan = append(bareMetalPlan, b) + } + + return bareMetalPlan, nil +} + +// GetVc2List retrieve a list of all active vc2 plans. +func (p *PlanServiceHandler) GetVc2List(ctx context.Context) ([]VCPlan, error) { + uri := "/v1/plans/list_vc2" + + req, err := p.Client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + var vc2Plan map[string]VCPlan + err = p.Client.DoWithContext(ctx, req, &vc2Plan) + + if err != nil { + return nil, err + } + + var vc2 []VCPlan + for _, p := range vc2Plan { + vc2 = append(vc2, p) + } + + return vc2, nil +} + +// GetVdc2List Retrieve a list of all active vdc2 plans +func (p *PlanServiceHandler) GetVdc2List(ctx context.Context) ([]VCPlan, error) { + uri := "/v1/plans/list_vdc2" + + req, err := p.Client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + var vdc2Map map[string]VCPlan + err = p.Client.DoWithContext(ctx, req, &vdc2Map) + + if err != nil { + return nil, err + } + + var vdc2 []VCPlan + for _, p := range vdc2Map { + vdc2 = append(vdc2, p) + } + + return vdc2, nil +} + +// GetVc2zList Retrieve a list of all active vc2z plans (high frequency) +func (p *PlanServiceHandler) GetVc2zList(ctx context.Context) ([]VCPlan, error) { + uri := "/v1/plans/list_vc2z" + + req, err := p.Client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + var vc2zMap map[string]VCPlan + err = p.Client.DoWithContext(ctx, req, &vc2zMap) + + if err != nil { + return nil, err + } + + var vc2z []VCPlan + for _, p := range vc2zMap { + vc2z = append(vc2z, p) + } + + return vc2z, nil +} diff --git a/vendor/github.com/vultr/govultr/regions.go b/vendor/github.com/vultr/govultr/regions.go new file mode 100644 index 00000000..cfdbbebc --- /dev/null +++ b/vendor/github.com/vultr/govultr/regions.go @@ -0,0 +1,154 @@ +package govultr + +import ( + "context" + "net/http" + "strconv" +) + +// RegionService is the interface to interact with Region endpoints on the Vultr API +// Link: https://www.vultr.com/api/#regions +type RegionService interface { + Availability(ctx context.Context, regionID int, planType string) ([]int, error) + BareMetalAvailability(ctx context.Context, regionID int) ([]int, error) + Vc2Availability(ctx context.Context, regionID int) ([]int, error) + Vdc2Availability(ctx context.Context, regionID int) ([]int, error) + List(ctx context.Context) ([]Region, error) +} + +// RegionServiceHandler handles interaction with the region methods for the Vultr API +type RegionServiceHandler struct { + Client *Client +} + +// Region represents a Vultr region +type Region struct { + RegionID string `json:"DCID"` + Name string `json:"name"` + Country string `json:"country"` + Continent string `json:"continent"` + State string `json:"state"` + Ddos bool `json:"ddos_protection"` + BlockStorage bool `json:"block_storage"` + RegionCode string `json:"regioncode"` +} + +// Availability retrieves a list of the VPSPLANIDs currently available for a given location. +func (r *RegionServiceHandler) Availability(ctx context.Context, regionID int, planType string) ([]int, error) { + + uri := "/v1/regions/availability" + + req, err := r.Client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("DCID", strconv.Itoa(regionID)) + + // Optional planType filter + if planType != "" { + q.Add("type", planType) + } + req.URL.RawQuery = q.Encode() + + var regions []int + err = r.Client.DoWithContext(ctx, req, ®ions) + + if err != nil { + return nil, err + } + + return regions, nil +} + +// BareMetalAvailability retrieve a list of the METALPLANIDs currently available for a given location. +func (r *RegionServiceHandler) BareMetalAvailability(ctx context.Context, regionID int) ([]int, error) { + + uri := "/v1/regions/availability_baremetal" + + regions, err := r.instanceAvailability(ctx, uri, regionID) + + if err != nil { + return nil, err + } + + return regions, nil +} + +// Vc2Availability retrieve a list of the vc2 VPSPLANIDs currently available for a given location. +func (r *RegionServiceHandler) Vc2Availability(ctx context.Context, regionID int) ([]int, error) { + + uri := "/v1/regions/availability_vc2" + + regions, err := r.instanceAvailability(ctx, uri, regionID) + + if err != nil { + return nil, err + } + + return regions, nil +} + +// Vdc2Availability retrieves a list of the vdc2 VPSPLANIDs currently available for a given location. +func (r *RegionServiceHandler) Vdc2Availability(ctx context.Context, regionID int) ([]int, error) { + + uri := "/v1/regions/availability_vdc2" + + regions, err := r.instanceAvailability(ctx, uri, regionID) + + if err != nil { + return nil, err + } + + return regions, nil +} + +// List retrieves a list of all active regions +func (r *RegionServiceHandler) List(ctx context.Context) ([]Region, error) { + + uri := "/v1/regions/list" + + req, err := r.Client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + var regionsMap map[string]Region + err = r.Client.DoWithContext(ctx, req, ®ionsMap) + + if err != nil { + return nil, err + } + + var region []Region + for _, r := range regionsMap { + region = append(region, r) + } + + return region, nil +} + +// instanceAvailability keeps the similar calls dry +func (r *RegionServiceHandler) instanceAvailability(ctx context.Context, uri string, regionID int) ([]int, error) { + req, err := r.Client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("DCID", strconv.Itoa(regionID)) + req.URL.RawQuery = q.Encode() + + var regions []int + err = r.Client.DoWithContext(ctx, req, ®ions) + + if err != nil { + return nil, err + } + + return regions, nil +} diff --git a/vendor/github.com/vultr/govultr/reserved_ip.go b/vendor/github.com/vultr/govultr/reserved_ip.go new file mode 100644 index 00000000..ee9335c4 --- /dev/null +++ b/vendor/github.com/vultr/govultr/reserved_ip.go @@ -0,0 +1,273 @@ +package govultr + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + "strconv" +) + +// ReservedIPService is the interface to interact with the reserved IP endpoints on the Vultr API +// Link: https://www.vultr.com/api/#reservedip +type ReservedIPService interface { + Attach(ctx context.Context, ip, InstanceID string) error + Convert(ctx context.Context, ip, InstanceID, label string) (*ReservedIP, error) + Create(ctx context.Context, regionID int, ipType, label string) (*ReservedIP, error) + Delete(ctx context.Context, ip string) error + Detach(ctx context.Context, ip, InstanceID string) error + List(ctx context.Context) ([]ReservedIP, error) +} + +// ReservedIPServiceHandler handles interaction with the reserved IP methods for the Vultr API +type ReservedIPServiceHandler struct { + client *Client +} + +// ReservedIP represents an reserved IP on Vultr +type ReservedIP struct { + ReservedIPID string `json:"SUBID"` + RegionID int `json:"DCID"` + IPType string `json:"ip_type"` + Subnet string `json:"subnet"` + SubnetSize int `json:"subnet_size"` + Label string `json:"label"` + AttachedID string `json:"attached_SUBID"` +} + +// UnmarshalJSON implements json.Unmarshaller on ReservedIP to handle the inconsistent types returned from the Vultr API. +func (r *ReservedIP) UnmarshalJSON(data []byte) (err error) { + if r == nil { + *r = ReservedIP{} + } + + var v map[string]interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + + r.ReservedIPID, err = r.unmarshalStr(fmt.Sprintf("%v", v["SUBID"])) + if err != nil { + return err + } + + r.AttachedID, err = r.unmarshalStr(fmt.Sprintf("%v", v["attached_SUBID"])) + if err != nil { + return err + } + + r.RegionID, err = r.unmarshalInt(fmt.Sprintf("%v", v["DCID"])) + if err != nil { + return err + } + + r.SubnetSize, err = r.unmarshalInt(fmt.Sprintf("%v", v["subnet_size"])) + if err != nil { + return err + } + + if r.Subnet = fmt.Sprintf("%v", v["subnet"]); r.Subnet == "" { + r.Subnet = "" + } + + if r.IPType = fmt.Sprintf("%v", v["ip_type"]); r.IPType == "" { + r.IPType = "" + } + + if r.Label = fmt.Sprintf("%v", v["label"]); r.Label == "" { + r.Label = "" + } + + return nil +} + +func (r *ReservedIP) unmarshalInt(value string) (int, error) { + if len(value) == 0 || value == "" { + value = "0" + } + + i, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return 0, err + } + + return int(i), nil +} + +func (r *ReservedIP) unmarshalStr(value string) (string, error) { + if len(value) == 0 || value == "" || value == "0" || value == "false" { + return "", nil + } + + f, err := strconv.ParseFloat(value, 64) + if err != nil { + return "", err + } + + return strconv.FormatFloat(f, 'f', -1, 64), nil +} + +// Attach a reserved IP to an existing subscription +func (r *ReservedIPServiceHandler) Attach(ctx context.Context, ip, InstanceID string) error { + uri := "/v1/reservedip/attach" + + values := url.Values{ + "ip_address": {ip}, + "attach_SUBID": {InstanceID}, + } + + req, err := r.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = r.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Convert an existing IP on a subscription to a reserved IP. +func (r *ReservedIPServiceHandler) Convert(ctx context.Context, ip, InstanceID, label string) (*ReservedIP, error) { + uri := "/v1/reservedip/convert" + + values := url.Values{ + "SUBID": {InstanceID}, + "ip_address": {ip}, + } + + if label != "" { + values.Add("label", label) + } + + req, err := r.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + rip := new(ReservedIP) + + err = r.client.DoWithContext(ctx, req, rip) + + if err != nil { + return nil, err + } + + rip.Label = label + + return rip, nil +} + +// Create adds the specified reserved IP to your Vultr account +func (r *ReservedIPServiceHandler) Create(ctx context.Context, regionID int, ipType, label string) (*ReservedIP, error) { + + uri := "/v1/reservedip/create" + + values := url.Values{ + "DCID": {strconv.Itoa(regionID)}, + "ip_type": {ipType}, + } + + if label != "" { + values.Add("label", label) + } + + req, err := r.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + rip := new(ReservedIP) + + err = r.client.DoWithContext(ctx, req, rip) + + if err != nil { + return nil, err + } + + rip.RegionID = regionID + rip.IPType = ipType + rip.Label = label + + return rip, nil +} + +// Delete removes the specified reserved IP from your Vultr account +func (r *ReservedIPServiceHandler) Delete(ctx context.Context, ip string) error { + + uri := "/v1/reservedip/destroy" + + values := url.Values{ + "ip_address": {ip}, + } + + req, err := r.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = r.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Detach a reserved IP from an existing subscription. +func (r *ReservedIPServiceHandler) Detach(ctx context.Context, ip, InstanceID string) error { + uri := "/v1/reservedip/detach" + + values := url.Values{ + "ip_address": {ip}, + "detach_SUBID": {InstanceID}, + } + + req, err := r.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = r.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// List lists all the reserved IPs associated with your Vultr account +func (r *ReservedIPServiceHandler) List(ctx context.Context) ([]ReservedIP, error) { + + uri := "/v1/reservedip/list" + + req, err := r.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + ipMap := make(map[string]ReservedIP) + err = r.client.DoWithContext(ctx, req, &ipMap) + if err != nil { + return nil, err + } + + var ips []ReservedIP + for _, ip := range ipMap { + ips = append(ips, ip) + } + + return ips, nil +} diff --git a/vendor/github.com/vultr/govultr/server.go b/vendor/github.com/vultr/govultr/server.go new file mode 100644 index 00000000..78704662 --- /dev/null +++ b/vendor/github.com/vultr/govultr/server.go @@ -0,0 +1,1469 @@ +package govultr + +import ( + "context" + "encoding/base64" + "net/http" + "net/url" + "strconv" + "strings" +) + +// ServerService is the interface to interact with the server endpoints on the Vultr API +// Link: https://www.vultr.com/api/#server +type ServerService interface { + ChangeApp(ctx context.Context, instanceID, appID string) error + ListApps(ctx context.Context, instanceID string) ([]Application, error) + AppInfo(ctx context.Context, instanceID string) (*AppInfo, error) + EnableBackup(ctx context.Context, instanceID string) error + DisableBackup(ctx context.Context, instanceID string) error + GetBackupSchedule(ctx context.Context, instanceID string) (*BackupSchedule, error) + SetBackupSchedule(ctx context.Context, instanceID string, backup *BackupSchedule) error + RestoreBackup(ctx context.Context, instanceID, backupID string) error + RestoreSnapshot(ctx context.Context, instanceID, snapshotID string) error + SetLabel(ctx context.Context, instanceID, label string) error + SetTag(ctx context.Context, instanceID, tag string) error + Neighbors(ctx context.Context, instanceID string) ([]int, error) + EnablePrivateNetwork(ctx context.Context, instanceID, networkID string) error + DisablePrivateNetwork(ctx context.Context, instanceID, networkID string) error + ListPrivateNetworks(ctx context.Context, instanceID string) ([]PrivateNetwork, error) + ListUpgradePlan(ctx context.Context, instanceID string) ([]int, error) + UpgradePlan(ctx context.Context, instanceID, vpsPlanID string) error + ListOS(ctx context.Context, instanceID string) ([]OS, error) + ChangeOS(ctx context.Context, instanceID, osID string) error + IsoAttach(ctx context.Context, instanceID, isoID string) error + IsoDetach(ctx context.Context, instanceID string) error + IsoStatus(ctx context.Context, instanceID string) (*ServerIso, error) + SetFirewallGroup(ctx context.Context, instanceID, firewallGroupID string) error + GetUserData(ctx context.Context, instanceID string) (*UserData, error) + SetUserData(ctx context.Context, instanceID, userData string) error + IPV4Info(ctx context.Context, instanceID string, public bool) ([]IPV4, error) + IPV6Info(ctx context.Context, instanceID string) ([]IPV6, error) + AddIPV4(ctx context.Context, instanceID string) error + DestroyIPV4(ctx context.Context, instanceID, ip string) error + EnableIPV6(ctx context.Context, instanceID string) error + Bandwidth(ctx context.Context, instanceID string) ([]map[string]string, error) + ListReverseIPV6(ctx context.Context, instanceID string) ([]ReverseIPV6, error) + SetDefaultReverseIPV4(ctx context.Context, instanceID, ip string) error + DeleteReverseIPV6(ctx context.Context, instanceID, ip string) error + SetReverseIPV4(ctx context.Context, instanceID, ipv4, entry string) error + SetReverseIPV6(ctx context.Context, instanceID, ipv6, entry string) error + Start(ctx context.Context, instanceID string) error + Halt(ctx context.Context, instanceID string) error + Reboot(ctx context.Context, instanceID string) error + Reinstall(ctx context.Context, instanceID string) error + Delete(ctx context.Context, instanceID string) error + Create(ctx context.Context, regionID, vpsPlanID, osID int, options *ServerOptions) (*Server, error) + List(ctx context.Context) ([]Server, error) + ListByLabel(ctx context.Context, label string) ([]Server, error) + ListByMainIP(ctx context.Context, mainIP string) ([]Server, error) + ListByTag(ctx context.Context, tag string) ([]Server, error) + GetServer(ctx context.Context, instanceID string) (*Server, error) +} + +// ServerServiceHandler handles interaction with the server methods for the Vultr API +type ServerServiceHandler struct { + client *Client +} + +// AppInfo represents information about the application on your VPS +type AppInfo struct { + AppInfo string `json:"app_info"` +} + +// BackupSchedule represents a schedule of a backup that runs on a VPS +type BackupSchedule struct { + Enabled bool `json:"enabled"` + CronType string `json:"cron_type"` + NextRun string `json:"next_scheduled_time_utc"` + Hour int `json:"hour"` + Dow int `json:"dow"` + Dom int `json:"dom"` +} + +// PrivateNetwork represents a private network attached to a VPS +type PrivateNetwork struct { + NetworkID string `json:"NETWORKID"` + MacAddress string `json:"mac_address"` + IPAddress string `json:"ip_address"` +} + +// ServerIso represents a iso attached to a VPS +type ServerIso struct { + State string `json:"state"` + IsoID string `json:"ISOID"` +} + +// UserData represents the user data you can give a VPS +type UserData struct { + UserData string `json:"userdata"` +} + +// IPV4 represents IPV4 information for a VPS +type IPV4 struct { + IP string `json:"ip"` + Netmask string `json:"netmask"` + Gateway string `json:"gateway"` + Type string `json:"type"` + Reverse string `json:"reverse"` +} + +// IPV6 represents IPV6 information for a VPS +type IPV6 struct { + IP string `json:"ip"` + Network string `json:"network"` + NetworkSize string `json:"network_size"` + Type string `json:"type"` +} + +// ReverseIPV6 represents IPV6 reverse DNS entries +type ReverseIPV6 struct { + IP string `json:"ip"` + Reverse string `json:"reverse"` +} + +// Server represents a VPS +type Server struct { + InstanceID string `json:"SUBID"` + Os string `json:"os"` + RAM string `json:"ram"` + Disk string `json:"disk"` + MainIP string `json:"main_ip"` + VPSCpus string `json:"vcpu_count"` + Location string `json:"location"` + RegionID string `json:"DCID"` + DefaultPassword string `json:"default_password"` + Created string `json:"date_created"` + PendingCharges string `json:"pending_charges"` + Status string `json:"status"` + Cost string `json:"cost_per_month"` + CurrentBandwidth float64 `json:"current_bandwidth_gb"` + AllowedBandwidth string `json:"allowed_bandwidth_gb"` + NetmaskV4 string `json:"netmask_v4"` + GatewayV4 string `json:"gateway_v4"` + PowerStatus string `json:"power_status"` + ServerState string `json:"server_state"` + PlanID string `json:"VPSPLANID"` + V6Networks []V6Network `json:"v6_networks"` + Label string `json:"label"` + InternalIP string `json:"internal_ip"` + KVMUrl string `json:"kvm_url"` + AutoBackups string `json:"auto_backups"` + Tag string `json:"tag"` + OsID string `json:"OSID"` + AppID string `json:"APPID"` + FirewallGroupID string `json:"FIREWALLGROUPID"` +} + +// V6Network represents an IPV6 network on a VPS +type V6Network struct { + Network string `json:"v6_network"` + MainIP string `json:"v6_main_ip"` + NetworkSize string `json:"v6_network_size"` +} + +// ServerOptions are all optional fields that can be used during vps creation +type ServerOptions struct { + IPXEChain string + IsoID int + SnapshotID string + ScriptID string + EnableIPV6 bool + EnablePrivateNetwork bool + NetworkID []string + Label string + SSHKeyIDs []string + AutoBackups bool + AppID string + UserData string + NotifyActivate bool + DDOSProtection bool + ReservedIPV4 string + Hostname string + Tag string + FirewallGroupID string +} + +// ChangeApp changes the VPS to a different application. +func (s *ServerServiceHandler) ChangeApp(ctx context.Context, instanceID, appID string) error { + + uri := "/v1/server/app_change" + + values := url.Values{ + "SUBID": {instanceID}, + "APPID": {appID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// ListApps retrieves a list of applications to which a virtual machine can be changed. +func (s *ServerServiceHandler) ListApps(ctx context.Context, instanceID string) ([]Application, error) { + + uri := "/v1/server/app_change_list" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", instanceID) + req.URL.RawQuery = q.Encode() + + var appMap map[string]Application + err = s.client.DoWithContext(ctx, req, &appMap) + + if err != nil { + return nil, err + } + + var appList []Application + for _, a := range appMap { + appList = append(appList, a) + } + + return appList, nil +} + +// AppInfo retrieves the application information for a given VPS ID +func (s *ServerServiceHandler) AppInfo(ctx context.Context, instanceID string) (*AppInfo, error) { + + uri := "/v1/server/get_app_info" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", instanceID) + req.URL.RawQuery = q.Encode() + + appInfo := new(AppInfo) + + err = s.client.DoWithContext(ctx, req, appInfo) + + if err != nil { + return nil, err + } + + return appInfo, nil +} + +// EnableBackup enables automatic backups on a given VPS +func (s *ServerServiceHandler) EnableBackup(ctx context.Context, instanceID string) error { + + uri := "/v1/server/backup_enable" + + values := url.Values{ + "SUBID": {instanceID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// DisableBackup disable automatic backups on a given VPS +func (s *ServerServiceHandler) DisableBackup(ctx context.Context, instanceID string) error { + + uri := "/v1/server/backup_disable" + + values := url.Values{ + "SUBID": {instanceID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// GetBackupSchedule retrieves the backup schedule for a given vps - all time values are in UTC +func (s *ServerServiceHandler) GetBackupSchedule(ctx context.Context, instanceID string) (*BackupSchedule, error) { + + uri := "/v1/server/backup_get_schedule" + + values := url.Values{ + "SUBID": {instanceID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + backup := new(BackupSchedule) + err = s.client.DoWithContext(ctx, req, backup) + + if err != nil { + return nil, err + } + + return backup, nil +} + +// SetBackupSchedule sets the backup schedule for a given vps - all time values are in UTC +func (s *ServerServiceHandler) SetBackupSchedule(ctx context.Context, instanceID string, backup *BackupSchedule) error { + + uri := "/v1/server/backup_set_schedule" + + values := url.Values{ + "SUBID": {instanceID}, + "cron_type": {backup.CronType}, + "hour": {strconv.Itoa(backup.Hour)}, + "dow": {strconv.Itoa(backup.Dow)}, + "dom": {strconv.Itoa(backup.Dom)}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// RestoreBackup will restore the specified backup to the given VPS +func (s *ServerServiceHandler) RestoreBackup(ctx context.Context, instanceID, backupID string) error { + + uri := "/v1/server/restore_backup" + + values := url.Values{ + "SUBID": {instanceID}, + "BACKUPID": {backupID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// RestoreSnapshot will restore the specified snapshot to the given VPS +func (s *ServerServiceHandler) RestoreSnapshot(ctx context.Context, instanceID, snapshotID string) error { + + uri := "/v1/server/restore_snapshot" + + values := url.Values{ + "SUBID": {instanceID}, + "SNAPSHOTID": {snapshotID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// SetLabel will set a label for a given VPS +func (s *ServerServiceHandler) SetLabel(ctx context.Context, instanceID, label string) error { + + uri := "/v1/server/label_set" + + values := url.Values{ + "SUBID": {instanceID}, + "label": {label}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// SetTag will set a tag for a given VPS +func (s *ServerServiceHandler) SetTag(ctx context.Context, instanceID, tag string) error { + + uri := "/v1/server/tag_set" + + values := url.Values{ + "SUBID": {instanceID}, + "tag": {tag}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Neighbors will determine what other vps are hosted on the same physical host as a given vps. +func (s *ServerServiceHandler) Neighbors(ctx context.Context, instanceID string) ([]int, error) { + + uri := "/v1/server/neighbors" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", instanceID) + req.URL.RawQuery = q.Encode() + + var neighbors []int + err = s.client.DoWithContext(ctx, req, &neighbors) + + if err != nil { + return nil, err + } + + return neighbors, nil +} + +// EnablePrivateNetwork enables private networking on a server. +// The server will be automatically rebooted to complete the request. +// No action occurs if private networking was already enabled +func (s *ServerServiceHandler) EnablePrivateNetwork(ctx context.Context, instanceID, networkID string) error { + + uri := "/v1/server/private_network_enable" + + values := url.Values{ + "SUBID": {instanceID}, + "NETWORKID": {networkID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// DisablePrivateNetwork removes a private network from a server. +// The server will be automatically rebooted to complete the request. +func (s *ServerServiceHandler) DisablePrivateNetwork(ctx context.Context, instanceID, networkID string) error { + + uri := "/v1/server/private_network_disable" + + values := url.Values{ + "SUBID": {instanceID}, + "NETWORKID": {networkID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// ListPrivateNetworks will list private networks attached to a vps +func (s *ServerServiceHandler) ListPrivateNetworks(ctx context.Context, instanceID string) ([]PrivateNetwork, error) { + + uri := "/v1/server/private_networks" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", instanceID) + req.URL.RawQuery = q.Encode() + + var networkMap map[string]PrivateNetwork + err = s.client.DoWithContext(ctx, req, &networkMap) + + if err != nil { + return nil, err + } + + var privateNetworks []PrivateNetwork + for _, p := range networkMap { + privateNetworks = append(privateNetworks, p) + } + + return privateNetworks, nil +} + +// ListUpgradePlan Retrieve a list of the planIDs for which the vps can be upgraded. +// An empty response array means that there are currently no upgrades available +func (s *ServerServiceHandler) ListUpgradePlan(ctx context.Context, instanceID string) ([]int, error) { + + uri := "/v1/server/upgrade_plan_list" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", instanceID) + req.URL.RawQuery = q.Encode() + + var plans []int + err = s.client.DoWithContext(ctx, req, &plans) + + if err != nil { + return nil, err + } + + return plans, nil +} + +// UpgradePlan will upgrade the plan of a virtual machine. +// The vps will be rebooted upon a successful upgrade. +func (s *ServerServiceHandler) UpgradePlan(ctx context.Context, instanceID, vpsPlanID string) error { + + uri := "/v1/server/upgrade_plan" + + values := url.Values{ + "SUBID": {instanceID}, + "VPSPLANID": {vpsPlanID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// ListOS retrieves a list of operating systems to which the VPS can be changed to. +func (s *ServerServiceHandler) ListOS(ctx context.Context, instanceID string) ([]OS, error) { + + uri := "/v1/server/os_change_list" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", instanceID) + req.URL.RawQuery = q.Encode() + + var osMap map[string]OS + err = s.client.DoWithContext(ctx, req, &osMap) + + if err != nil { + return nil, err + } + + var os []OS + for _, o := range osMap { + os = append(os, o) + } + + return os, nil +} + +// ChangeOS changes the VPS to a different operating system. +// All data will be permanently lost. +func (s *ServerServiceHandler) ChangeOS(ctx context.Context, instanceID, osID string) error { + + uri := "/v1/server/os_change" + + values := url.Values{ + "SUBID": {instanceID}, + "OSID": {osID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// IsoAttach will attach an ISO to the given VPS and reboot it +func (s *ServerServiceHandler) IsoAttach(ctx context.Context, instanceID, isoID string) error { + + uri := "/v1/server/iso_attach" + + values := url.Values{ + "SUBID": {instanceID}, + "ISOID": {isoID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// IsoDetach will detach the currently mounted ISO and reboot the server. +func (s *ServerServiceHandler) IsoDetach(ctx context.Context, instanceID string) error { + + uri := "/v1/server/iso_detach" + + values := url.Values{ + "SUBID": {instanceID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// IsoStatus retrieves the current ISO state for a given VPS. +// The returned state may be one of: ready | isomounting | isomounted. +func (s *ServerServiceHandler) IsoStatus(ctx context.Context, instanceID string) (*ServerIso, error) { + + uri := "/v1/server/iso_status" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", instanceID) + req.URL.RawQuery = q.Encode() + + serverIso := new(ServerIso) + err = s.client.DoWithContext(ctx, req, serverIso) + + if err != nil { + return nil, err + } + + return serverIso, nil +} + +// SetFirewallGroup will set, change, or remove the firewall group currently applied to a vps. +// A value of "0" means "no firewall group" +func (s *ServerServiceHandler) SetFirewallGroup(ctx context.Context, instanceID, firewallGroupID string) error { + + uri := "/v1/server/firewall_group_set" + + values := url.Values{ + "SUBID": {instanceID}, + "FIREWALLGROUPID": {firewallGroupID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// SetUserData sets the user-data for this subscription. +// User-data is a generic data store, which some provisioning tools and cloud operating systems use as a configuration file. +// It is generally consumed only once after an instance has been launched, but individual needs may vary. +func (s *ServerServiceHandler) SetUserData(ctx context.Context, instanceID, userData string) error { + + uri := "/v1/server/set_user_data" + + encodedUserData := base64.StdEncoding.EncodeToString([]byte(userData)) + + values := url.Values{ + "SUBID": {instanceID}, + "userdata": {encodedUserData}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// GetUserData retrieves the (base64 encoded) user-data for this VPS +func (s *ServerServiceHandler) GetUserData(ctx context.Context, instanceID string) (*UserData, error) { + + uri := "/v1/server/get_user_data" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", instanceID) + req.URL.RawQuery = q.Encode() + + userData := new(UserData) + err = s.client.DoWithContext(ctx, req, userData) + + if err != nil { + return nil, err + } + + return userData, nil +} + +// IPV4Info will list the IPv4 information of a virtual machine. +// Public if set to 'true', includes information about the public network adapter (such as MAC address) with the "main_ip" entry. +func (s *ServerServiceHandler) IPV4Info(ctx context.Context, instanceID string, public bool) ([]IPV4, error) { + + uri := "/v1/server/list_ipv4" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", instanceID) + + if public == true { + q.Add("public_network", instanceID) + } + + req.URL.RawQuery = q.Encode() + + var ipMap map[string][]IPV4 + err = s.client.DoWithContext(ctx, req, &ipMap) + + if err != nil { + return nil, err + } + + var ipv4 []IPV4 + for _, i := range ipMap { + ipv4 = i + } + + return ipv4, nil +} + +// IPV6Info will list the IPv6 information of a virtual machine. +// If the virtual machine does not have IPv6 enabled, then an empty array is returned. +func (s *ServerServiceHandler) IPV6Info(ctx context.Context, instanceID string) ([]IPV6, error) { + uri := "/v1/server/list_ipv6" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", instanceID) + req.URL.RawQuery = q.Encode() + + var ipMap map[string][]IPV6 + err = s.client.DoWithContext(ctx, req, &ipMap) + + if err != nil { + return nil, err + } + + var ipv6 []IPV6 + for _, i := range ipMap { + ipv6 = i + } + + return ipv6, nil +} + +// AddIPV4 will add a new IPv4 address to a server. +func (s *ServerServiceHandler) AddIPV4(ctx context.Context, instanceID string) error { + + uri := "/v1/server/create_ipv4" + + values := url.Values{ + "SUBID": {instanceID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// DestroyIPV4 removes a secondary IPv4 address from a server. +// Your server will be hard-restarted. We suggest halting the machine gracefully before removing IPs. +func (s *ServerServiceHandler) DestroyIPV4(ctx context.Context, instanceID, ip string) error { + + uri := "/v1/server/destroy_ipv4" + + values := url.Values{ + "SUBID": {instanceID}, + "ip": {ip}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// EnableIPV6 enables IPv6 networking on a server by assigning an IPv6 subnet to it. +func (s *ServerServiceHandler) EnableIPV6(ctx context.Context, instanceID string) error { + + uri := "/v1/server/ipv6_enable" + + values := url.Values{ + "SUBID": {instanceID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Bandwidth will get the bandwidth used by a VPS +func (s *ServerServiceHandler) Bandwidth(ctx context.Context, instanceID string) ([]map[string]string, error) { + + uri := "/v1/server/bandwidth" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", instanceID) + req.URL.RawQuery = q.Encode() + + var bandwidthMap map[string][][]string + err = s.client.DoWithContext(ctx, req, &bandwidthMap) + + if err != nil { + return nil, err + } + + var bandwidth []map[string]string + + for _, b := range bandwidthMap["incoming_bytes"] { + inMap := make(map[string]string) + inMap["date"] = b[0] + inMap["incoming"] = b[1] + bandwidth = append(bandwidth, inMap) + } + + for _, b := range bandwidthMap["outgoing_bytes"] { + for i := range bandwidth { + if bandwidth[i]["date"] == b[0] { + bandwidth[i]["outgoing"] = b[1] + break + } + } + } + + return bandwidth, nil +} + +// ListReverseIPV6 List the IPv6 reverse DNS entries of a virtual machine. +// Reverse DNS entries are only available for virtual machines in the "active" state. +// If the virtual machine does not have IPv6 enabled, then an empty array is returned. +func (s *ServerServiceHandler) ListReverseIPV6(ctx context.Context, instanceID string) ([]ReverseIPV6, error) { + + uri := "/v1/server/reverse_list_ipv6" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", instanceID) + req.URL.RawQuery = q.Encode() + + var reverseMap map[string][]ReverseIPV6 + err = s.client.DoWithContext(ctx, req, &reverseMap) + + if err != nil { + return nil, err + } + + var reverseIPV6 []ReverseIPV6 + for _, r := range reverseMap { + + if len(r) == 0 { + break + } + + for _, i := range r { + reverseIPV6 = append(reverseIPV6, i) + } + } + + return reverseIPV6, nil +} + +// SetDefaultReverseIPV4 will set a reverse DNS entry for an IPv4 address of a virtual machine to the original setting. +// Upon success, DNS changes may take 6-12 hours to become active. +func (s *ServerServiceHandler) SetDefaultReverseIPV4(ctx context.Context, instanceID, ip string) error { + + uri := "/v1/server/reverse_default_ipv4" + + values := url.Values{ + "SUBID": {instanceID}, + "ip": {ip}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// DeleteReverseIPV6 Remove a reverse DNS entry for an IPv6 address of a VPS. +// Upon success, DNS changes may take 6-12 hours to become active. +func (s *ServerServiceHandler) DeleteReverseIPV6(ctx context.Context, instanceID, ip string) error { + + uri := "/v1/server/reverse_delete_ipv6" + + values := url.Values{ + "SUBID": {instanceID}, + "ip": {ip}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// SetReverseIPV4 will set a reverse DNS entry for an IPv4 address of a virtual machine. +// Upon success, DNS changes may take 6-12 hours to become active. +func (s *ServerServiceHandler) SetReverseIPV4(ctx context.Context, instanceID, ipv4, entry string) error { + + uri := "/v1/server/reverse_set_ipv4" + + values := url.Values{ + "SUBID": {instanceID}, + "ip": {ipv4}, + "entry": {entry}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// SetReverseIPV6 will set a reverse DNS entry for an IPv4 address of a virtual machine. +// Upon success, DNS changes may take 6-12 hours to become active. +func (s *ServerServiceHandler) SetReverseIPV6(ctx context.Context, instanceID, ipv6, entry string) error { + uri := "/v1/server/reverse_set_ipv6" + + values := url.Values{ + "SUBID": {instanceID}, + "ip": {ipv6}, + "entry": {entry}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Start will start a vps. If the machine is already running, it will be restarted. +func (s *ServerServiceHandler) Start(ctx context.Context, instanceID string) error { + uri := "/v1/server/start" + + values := url.Values{ + "SUBID": {instanceID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Halt will halt a virtual machine. This is a hard power off +func (s *ServerServiceHandler) Halt(ctx context.Context, instanceID string) error { + + uri := "/v1/server/halt" + + values := url.Values{ + "SUBID": {instanceID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Reboot will reboot a VPS. This is a hard reboot +func (s *ServerServiceHandler) Reboot(ctx context.Context, instanceID string) error { + + uri := "/v1/server/reboot" + + values := url.Values{ + "SUBID": {instanceID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Reinstall will reinstall the operating system on a VPS. +func (s *ServerServiceHandler) Reinstall(ctx context.Context, instanceID string) error { + uri := "/v1/server/reinstall" + + values := url.Values{ + "SUBID": {instanceID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Delete a VPS. All data will be permanently lost, and the IP address will be released +func (s *ServerServiceHandler) Delete(ctx context.Context, instanceID string) error { + + uri := "/v1/server/destroy" + + values := url.Values{ + "SUBID": {instanceID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// Create will create a new VPS +// In order to create a server using a snapshot, use OSID 164 and specify a SNAPSHOTID. +// Similarly, to create a server using an ISO use OSID 159 and specify an ISOID. +func (s *ServerServiceHandler) Create(ctx context.Context, regionID, vpsPlanID, osID int, options *ServerOptions) (*Server, error) { + + uri := "/v1/server/create" + + values := url.Values{ + "DCID": {strconv.Itoa(regionID)}, + "VPSPLANID": {strconv.Itoa(vpsPlanID)}, + "OSID": {strconv.Itoa(osID)}, + } + + if options != nil { + if options.IPXEChain != "" { + values.Add("ipxe_chain_url", options.IPXEChain) + } + + if options.IsoID != 0 { + values.Add("ISOID", strconv.Itoa(options.IsoID)) + } + + if options.SnapshotID != "" { + values.Add("SNAPSHOTID", options.SnapshotID) + } + + if options.ScriptID != "" { + values.Add("SCRIPTID", options.ScriptID) + } + + if options.EnableIPV6 == true { + values.Add("enable_ipv6", "yes") + } + + // Use either EnabledPrivateNetwork or NetworkIDs, not both + if options.EnablePrivateNetwork == true { + values.Add("enable_private_network", "yes") + } else { + if options.NetworkID != nil && len(options.NetworkID) != 0 { + for _, n := range options.NetworkID { + values.Add("NETWORKID[]", n) + } + } + } + + if options.Label != "" { + values.Add("label", options.Label) + } + + if options.SSHKeyIDs != nil && len(options.SSHKeyIDs) != 0 { + values.Add("SSHKEYID", strings.Join(options.SSHKeyIDs, ",")) + } + + if options.AutoBackups == true { + values.Add("auto_backups", "yes") + } + + if options.AppID != "" { + values.Add("APPID", options.AppID) + } + + if options.UserData != "" { + values.Add("userdata", base64.StdEncoding.EncodeToString([]byte(options.UserData))) + } + + if options.NotifyActivate == true { + values.Add("notify_activate", "yes") + } else if options.NotifyActivate == false { + values.Add("notify_activate", "no") + } + + if options.DDOSProtection == true { + values.Add("ddos_protection", "yes") + } + + if options.ReservedIPV4 != "" { + values.Add("reserved_ip_v4", options.ReservedIPV4) + } + + if options.Hostname != "" { + values.Add("hostname", options.Hostname) + } + + if options.Tag != "" { + values.Add("tag", options.Tag) + } + + if options.FirewallGroupID != "" { + values.Add("FIREWALLGROUPID", options.FirewallGroupID) + } + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + server := new(Server) + err = s.client.DoWithContext(ctx, req, server) + + if err != nil { + return nil, err + } + + return server, nil +} + +// List lists all VPS on the current account. This includes both pending and active servers. +func (s *ServerServiceHandler) List(ctx context.Context) ([]Server, error) { + return s.list(ctx, "", "") +} + +// ListByLabel lists all VPS that match the given label on the current account. This includes both pending and active servers. +func (s *ServerServiceHandler) ListByLabel(ctx context.Context, label string) ([]Server, error) { + return s.list(ctx, "label", label) +} + +// ListByMainIP lists all VPS that match the given IP address on the current account. This includes both pending and active servers. +func (s *ServerServiceHandler) ListByMainIP(ctx context.Context, mainIP string) ([]Server, error) { + return s.list(ctx, "main_ip", mainIP) +} + +// ListByTag lists all VPS that match the given tag on the current account. This includes both pending and active servers. +func (s *ServerServiceHandler) ListByTag(ctx context.Context, tag string) ([]Server, error) { + return s.list(ctx, "tag", tag) +} + +// list is used to consolidate the optional params to get a VPS +func (s *ServerServiceHandler) list(ctx context.Context, key, value string) ([]Server, error) { + + uri := "/v1/server/list" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + if key != "" { + q := req.URL.Query() + q.Add(key, value) + req.URL.RawQuery = q.Encode() + } + + var serverMap map[string]Server + err = s.client.DoWithContext(ctx, req, &serverMap) + + if err != nil { + return nil, err + } + + var servers []Server + for _, s := range serverMap { + servers = append(servers, s) + } + + return servers, nil +} + +// GetServer will get the server with the given instanceID +func (s *ServerServiceHandler) GetServer(ctx context.Context, instanceID string) (*Server, error) { + + uri := "/v1/server/list" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + q := req.URL.Query() + q.Add("SUBID", instanceID) + req.URL.RawQuery = q.Encode() + + server := new(Server) + err = s.client.DoWithContext(ctx, req, server) + + if err != nil { + return nil, err + } + + return server, nil + +} diff --git a/vendor/github.com/vultr/govultr/snapshot.go b/vendor/github.com/vultr/govultr/snapshot.go new file mode 100644 index 00000000..92e53d0c --- /dev/null +++ b/vendor/github.com/vultr/govultr/snapshot.go @@ -0,0 +1,168 @@ +package govultr + +import ( + "context" + "net/http" + "net/url" +) + +// SnapshotService is the interface to interact with Snapshot endpoints on the Vultr API +// Link: https://www.vultr.com/api/#snapshot +type SnapshotService interface { + Create(ctx context.Context, InstanceID, description string) (*Snapshot, error) + CreateFromURL(ctx context.Context, snapshotURL string) (*Snapshot, error) + Delete(ctx context.Context, snapshotID string) error + List(ctx context.Context) ([]Snapshot, error) + Get(ctx context.Context, snapshotID string) (*Snapshot, error) +} + +// SnapshotServiceHandler handles interaction with the snapshot methods for the Vultr API +type SnapshotServiceHandler struct { + Client *Client +} + +// Snapshot represents a Vultr snapshot +type Snapshot struct { + SnapshotID string `json:"SNAPSHOTID"` + DateCreated string `json:"date_created"` + Description string `json:"description"` + Size string `json:"size"` + Status string `json:"status"` + OsID string `json:"OSID"` + AppID string `json:"APPID"` +} + +// Snapshots represent a collection of snapshots +type Snapshots []Snapshot + +// Create makes a snapshot of a provided server +func (s *SnapshotServiceHandler) Create(ctx context.Context, InstanceID, description string) (*Snapshot, error) { + + uri := "/v1/snapshot/create" + + values := url.Values{ + "SUBID": {InstanceID}, + "description": {description}, + } + + req, err := s.Client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + snapshot := new(Snapshot) + err = s.Client.DoWithContext(ctx, req, snapshot) + + if err != nil { + return nil, err + } + + snapshot.Description = description + return snapshot, nil +} + +// CreateFromURL will create a snapshot based on an image iso from a URL you provide +func (s *SnapshotServiceHandler) CreateFromURL(ctx context.Context, snapshotURL string) (*Snapshot, error) { + uri := "/v1/snapshot/create_from_url" + + values := url.Values{ + "url": {snapshotURL}, + } + + req, err := s.Client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + snapshot := new(Snapshot) + err = s.Client.DoWithContext(ctx, req, snapshot) + + if err != nil { + return nil, err + } + + return snapshot, nil +} + +// Delete a snapshot based on snapshotID +func (s *SnapshotServiceHandler) Delete(ctx context.Context, snapshotID string) error { + uri := "/v1/snapshot/destroy" + + values := url.Values{ + "SNAPSHOTID": {snapshotID}, + } + + req, err := s.Client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.Client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// List of snapshots details +func (s *SnapshotServiceHandler) List(ctx context.Context) ([]Snapshot, error) { + uri := "/v1/snapshot/list" + + req, err := s.Client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + snapshotMap := make(map[string]Snapshot) + err = s.Client.DoWithContext(ctx, req, &snapshotMap) + + if err != nil { + return nil, err + } + + var snapshots []Snapshot + + for _, s := range snapshotMap { + snapshots = append(snapshots, s) + } + + return snapshots, nil +} + +// Get individual details of a snapshot based on snapshotID +func (s *SnapshotServiceHandler) Get(ctx context.Context, snapshotID string) (*Snapshot, error) { + uri := "/v1/snapshot/list" + + req, err := s.Client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + if snapshotID != "" { + q := req.URL.Query() + q.Add("SNAPSHOTID", snapshotID) + req.URL.RawQuery = q.Encode() + } + + snapshotMap := make(map[string]Snapshot) + err = s.Client.DoWithContext(ctx, req, &snapshotMap) + + if err != nil { + return nil, err + } + + snapshot := new(Snapshot) + + for _, s := range snapshotMap { + snapshot = &s + } + + return snapshot, nil +} diff --git a/vendor/github.com/vultr/govultr/ssh_key.go b/vendor/github.com/vultr/govultr/ssh_key.go new file mode 100644 index 00000000..7f6b0a19 --- /dev/null +++ b/vendor/github.com/vultr/govultr/ssh_key.go @@ -0,0 +1,140 @@ +package govultr + +import ( + "context" + "net/http" + "net/url" +) + +// SSHKeyService is the interface to interact with the SSH Key endpoints on the Vultr API +// Link: https://www.vultr.com/api/#sshkey +type SSHKeyService interface { + Create(ctx context.Context, name, sshKey string) (*SSHKey, error) + Delete(ctx context.Context, sshKeyID string) error + List(ctx context.Context) ([]SSHKey, error) + Update(ctx context.Context, sshKey *SSHKey) error +} + +// SSHKeyServiceHandler handles interaction with the SSH Key methods for the Vultr API +type SSHKeyServiceHandler struct { + client *Client +} + +// SSHKey represents an SSH Key on Vultr +type SSHKey struct { + SSHKeyID string `json:"SSHKEYID"` + Name string `json:"name"` + Key string `json:"ssh_key"` + DateCreated string `json:"date_created"` +} + +// Create will add the specified SSH Key to your Vultr account +func (s *SSHKeyServiceHandler) Create(ctx context.Context, name, sshKey string) (*SSHKey, error) { + + uri := "/v1/sshkey/create" + + values := url.Values{ + "name": {name}, + "ssh_key": {sshKey}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + key := new(SSHKey) + + err = s.client.DoWithContext(ctx, req, key) + + if err != nil { + return nil, err + } + + key.Name = name + key.Key = sshKey + + return key, nil +} + +// Delete will delete the specified SHH Key from your Vultr account +func (s *SSHKeyServiceHandler) Delete(ctx context.Context, sshKeyID string) error { + + uri := "/v1/sshkey/destroy" + + values := url.Values{ + "SSHKEYID": {sshKeyID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// List will list all the SSH Keys associated with your Vultr account +func (s *SSHKeyServiceHandler) List(ctx context.Context) ([]SSHKey, error) { + + uri := "/v1/sshkey/list" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + sshKeysMap := make(map[string]SSHKey) + err = s.client.DoWithContext(ctx, req, &sshKeysMap) + if err != nil { + return nil, err + } + + var sshKeys []SSHKey + for _, key := range sshKeysMap { + sshKeys = append(sshKeys, key) + } + + return sshKeys, nil +} + +// Update will update the given SSH Key. Empty strings will be ignored. +func (s *SSHKeyServiceHandler) Update(ctx context.Context, sshKey *SSHKey) error { + + uri := "/v1/sshkey/update" + + values := url.Values{ + "SSHKEYID": {sshKey.SSHKeyID}, + } + + // Optional + if sshKey.Name != "" { + values.Add("name", sshKey.Name) + } + if sshKey.Key != "" { + values.Add("ssh_key", sshKey.Key) + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/vultr/govultr/startup_script.go b/vendor/github.com/vultr/govultr/startup_script.go new file mode 100644 index 00000000..cff94436 --- /dev/null +++ b/vendor/github.com/vultr/govultr/startup_script.go @@ -0,0 +1,172 @@ +package govultr + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" +) + +// StartupScriptService is the interface to interact with the startup script endpoints on the Vultr API +// Link: https://www.vultr.com/api/#startupscript +type StartupScriptService interface { + Create(ctx context.Context, name, script, scriptType string) (*StartupScript, error) + Delete(ctx context.Context, scriptID string) error + List(ctx context.Context) ([]StartupScript, error) + Update(ctx context.Context, script *StartupScript) error +} + +// StartupScriptServiceHandler handles interaction with the startup script methods for the Vultr API +type StartupScriptServiceHandler struct { + client *Client +} + +// StartupScript represents an startup script on Vultr +type StartupScript struct { + ScriptID string `json:"SCRIPTID"` + DateCreated string `json:"date_created"` + DateModified string `json:"date_modified"` + Name string `json:"name"` + Type string `json:"type"` + Script string `json:"script"` +} + +// UnmarshalJSON implements json.Unmarshaller on StartupScript to handle the inconsistent types returned from the Vultr API. +func (s *StartupScript) UnmarshalJSON(data []byte) (err error) { + if s == nil { + *s = StartupScript{} + } + + var v map[string]interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + + s.ScriptID = fmt.Sprintf("%v", v["SCRIPTID"]) + s.DateCreated = fmt.Sprintf("%v", v["date_created"]) + s.DateModified = fmt.Sprintf("%v", v["date_modified"]) + s.Name = fmt.Sprintf("%v", v["name"]) + s.Type = fmt.Sprintf("%v", v["type"]) + s.Script = fmt.Sprintf("%v", v["script"]) + + return nil +} + +// Create will add the specified startup script to your Vultr account +func (s *StartupScriptServiceHandler) Create(ctx context.Context, name, script, scriptType string) (*StartupScript, error) { + + uri := "/v1/startupscript/create" + + values := url.Values{ + "name": {name}, + "script": {script}, + } + + if scriptType != "" { + values.Add("type", scriptType) + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + ss := new(StartupScript) + + err = s.client.DoWithContext(ctx, req, ss) + + if err != nil { + return nil, err + } + + ss.DateCreated = "" + ss.DateModified = "" + ss.Name = name + ss.Type = scriptType + ss.Script = script + + return ss, nil +} + +// Delete will delete the specified startup script from your Vultr account +func (s *StartupScriptServiceHandler) Delete(ctx context.Context, scriptID string) error { + + uri := "/v1/startupscript/destroy" + + values := url.Values{ + "SCRIPTID": {scriptID}, + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// List will list all the startup scripts associated with your Vultr account +func (s *StartupScriptServiceHandler) List(ctx context.Context) ([]StartupScript, error) { + + uri := "/v1/startupscript/list" + + req, err := s.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + scriptsMap := make(map[string]StartupScript) + err = s.client.DoWithContext(ctx, req, &scriptsMap) + if err != nil { + return nil, err + } + + var scripts []StartupScript + for _, key := range scriptsMap { + scripts = append(scripts, key) + } + + return scripts, nil +} + +// Update will update the given startup script. Empty strings will be ignored. +func (s *StartupScriptServiceHandler) Update(ctx context.Context, script *StartupScript) error { + + uri := "/v1/startupscript/update" + + values := url.Values{ + "SCRIPTID": {script.ScriptID}, + } + + // Optional + if script.Name != "" { + values.Add("name", script.Name) + } + if script.Script != "" { + values.Add("script", script.Script) + } + + req, err := s.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = s.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/vultr/govultr/user.go b/vendor/github.com/vultr/govultr/user.go new file mode 100644 index 00000000..40e4040f --- /dev/null +++ b/vendor/github.com/vultr/govultr/user.go @@ -0,0 +1,157 @@ +package govultr + +import ( + "context" + "net/http" + "net/url" +) + +// UserService is the interface to interact with the user management endpoints on the Vultr API +// Link: https://www.vultr.com/api/#user +type UserService interface { + Create(ctx context.Context, email, name, password, apiEnabled string, acls []string) (*User, error) + Delete(ctx context.Context, userID string) error + List(ctx context.Context) ([]User, error) + Update(ctx context.Context, user *User) error +} + +// UserServiceHandler handles interaction with the user methods for the Vultr API +type UserServiceHandler struct { + client *Client +} + +// User represents an user on Vultr +type User struct { + UserID string `json:"USERID"` + Name string `json:"name"` + Email string `json:"email"` + Password string `json:"password"` + APIEnabled string `json:"api_enabled"` + ACL []string `json:"acls"` + APIKey string `json:"api_key"` +} + +// Create will add the specified user to your Vultr account +func (u *UserServiceHandler) Create(ctx context.Context, email, name, password, apiEnabled string, acls []string) (*User, error) { + + uri := "/v1/user/create" + + values := url.Values{ + "email": {email}, + "name": {name}, + "password": {password}, + "acls[]": acls, + } + + if apiEnabled != "" { + values.Add("api_enabled", apiEnabled) + } + + req, err := u.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return nil, err + } + + user := new(User) + + err = u.client.DoWithContext(ctx, req, user) + + if err != nil { + return nil, err + } + + user.Name = name + user.Email = email + user.APIEnabled = apiEnabled + user.ACL = acls + + return user, nil +} + +// Delete will remove the specified user from your Vultr account +func (u *UserServiceHandler) Delete(ctx context.Context, userID string) error { + + uri := "/v1/user/delete" + + values := url.Values{ + "USERID": {userID}, + } + + req, err := u.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = u.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +} + +// List will list all the users associated with your Vultr account +func (u *UserServiceHandler) List(ctx context.Context) ([]User, error) { + + uri := "/v1/user/list" + + req, err := u.client.NewRequest(ctx, http.MethodGet, uri, nil) + + if err != nil { + return nil, err + } + + var users []User + err = u.client.DoWithContext(ctx, req, &users) + if err != nil { + return nil, err + } + + return users, nil +} + +// Update will update the given user. Empty strings will be ignored. +func (u *UserServiceHandler) Update(ctx context.Context, user *User) error { + + uri := "/v1/user/update" + + values := url.Values{ + "USERID": {user.UserID}, + } + + // Optional + if user.Email != "" { + values.Add("email", user.Email) + } + if user.Name != "" { + values.Add("name", user.Name) + } + if user.Password != "" { + values.Add("password", user.Password) + } + if user.APIEnabled != "" { + values.Add("api_enabled", user.APIEnabled) + } + if len(user.ACL) > 0 { + for _, acl := range user.ACL { + values.Add("acls[]", acl) + } + } + + req, err := u.client.NewRequest(ctx, http.MethodPost, uri, values) + + if err != nil { + return err + } + + err = u.client.DoWithContext(ctx, req, nil) + + if err != nil { + return err + } + + return nil +}