diff --git a/providers/vultr/convert_test.go b/providers/vultr/convert_test.go index ec4cff291..259705978 100644 --- a/providers/vultr/convert_test.go +++ b/providers/vultr/convert_test.go @@ -3,8 +3,8 @@ package vultr import ( "testing" - vultr "github.com/JamesClonk/vultr/lib" "github.com/StackExchange/dnscontrol/models" + "github.com/vultr/govultr" ) func TestConversion(t *testing.T) { @@ -12,7 +12,7 @@ func TestConversion(t *testing.T) { Name: "example.com", } - records := []*vultr.DNSRecord{ + records := []*govultr.DNSRecord{ { Type: "A", Name: "", @@ -64,7 +64,7 @@ func TestConversion(t *testing.T) { t.Error("Error converting Vultr record", record) } - converted := toVultrRecord(dc, rc) + converted := toVultrRecord(dc, rc, 0) if converted.Type != record.Type || converted.Name != record.Name || converted.Data != record.Data || converted.Priority != record.Priority || converted.TTL != record.TTL { t.Error("Vultr record conversion mismatch", record, rc, converted) diff --git a/providers/vultr/vultrProvider.go b/providers/vultr/vultrProvider.go index 78eb47f6b..8e0bd0f80 100644 --- a/providers/vultr/vultrProvider.go +++ b/providers/vultr/vultrProvider.go @@ -1,16 +1,18 @@ package vultr import ( + "context" "encoding/json" "fmt" + "strconv" "strings" - vultr "github.com/JamesClonk/vultr/lib" "github.com/StackExchange/dnscontrol/models" "github.com/StackExchange/dnscontrol/providers" "github.com/StackExchange/dnscontrol/providers/diff" "github.com/miekg/dns/dnsutil" "github.com/pkg/errors" + "github.com/vultr/govultr" ) /* @@ -33,56 +35,40 @@ var features = providers.DocumentationNotes{ } func init() { - providers.RegisterDomainServiceProviderType("VULTR", NewVultr, features) + providers.RegisterDomainServiceProviderType("VULTR", NewProvider, features) } -// VultrApi represents the Vultr DNSServiceProvider -type VultrApi struct { - client *vultr.Client +// Provider represents the Vultr DNSServiceProvider. +type Provider struct { + client *govultr.Client token string } -// defaultNS are the default nameservers for Vultr +// defaultNS contains the default nameservers for Vultr. var defaultNS = []string{ "ns1.vultr.com", "ns2.vultr.com", } -// NewVultr initializes a Vultr DNSServiceProvider -func NewVultr(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) { - api := &VultrApi{ - token: m["token"], - } - - if api.token == "" { +// NewProvider initializes a Vultr DNSServiceProvider. +func NewProvider(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) { + token := m["token"] + if token == "" { return nil, errors.Errorf("Vultr API token is required") } - api.client = vultr.NewClient(api.token, nil) + client := govultr.NewClient(nil, token) + client.SetUserAgent("dnscontrol") - // Validate token - _, err := api.client.GetAccountInfo() - if err != nil { - return nil, err - } - - return api, nil + _, err := client.Account.GetInfo(context.Background()) + return &Provider{client, token}, err } -// GetDomainCorrections gets the corrections for a DomainConfig -func (api *VultrApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) { +// GetDomainCorrections gets the corrections for a DomainConfig. +func (api *Provider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) { dc.Punycode() - ok, err := api.isDomainInAccount(dc.Name) - if err != nil { - return nil, err - } - - if !ok { - return nil, errors.Errorf("%s is not a domain in the Vultr account", dc.Name) - } - - records, err := api.client.GetDNSRecords(dc.Name) + records, err := api.client.DNSRecord.List(context.Background(), dc.Name) if err != nil { return nil, err } @@ -93,46 +79,42 @@ func (api *VultrApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Co if err != nil { return nil, err } - curRecords[i] = r } - // Normalize models.PostProcessRecords(curRecords) differ := diff.New(dc) _, create, delete, modify := differ.IncrementalDiff(curRecords) - corrections := []*models.Correction{} + var corrections []*models.Correction for _, mod := range delete { - id := mod.Existing.Original.(*vultr.DNSRecord).RecordID + id := mod.Existing.Original.(*govultr.DNSRecord).RecordID corrections = append(corrections, &models.Correction{ Msg: fmt.Sprintf("%s; Vultr RecordID: %v", mod.String(), id), F: func() error { - return api.client.DeleteDNSRecord(dc.Name, id) + return api.client.DNSRecord.Delete(context.Background(), dc.Name, strconv.Itoa(id)) }, }) } for _, mod := range create { - r := toVultrRecord(dc, mod.Desired) + r := toVultrRecord(dc, mod.Desired, 0) corrections = append(corrections, &models.Correction{ Msg: mod.String(), F: func() error { - return api.client.CreateDNSRecord(dc.Name, r.Name, r.Type, r.Data, r.Priority, r.TTL) + return api.client.DNSRecord.Create(context.Background(), dc.Name, r.Type, r.Name, r.Data, r.TTL, r.Priority) }, }) } for _, mod := range modify { - id := mod.Existing.Original.(*vultr.DNSRecord).RecordID - r := toVultrRecord(dc, mod.Desired) - r.RecordID = id + r := toVultrRecord(dc, mod.Desired, mod.Existing.Original.(*govultr.DNSRecord).RecordID) corrections = append(corrections, &models.Correction{ - Msg: fmt.Sprintf("%s; Vultr RecordID: %v", mod.String(), id), + Msg: fmt.Sprintf("%s; Vultr RecordID: %v", mod.String(), r.RecordID), F: func() error { - return api.client.UpdateDNSRecord(dc.Name, *r) + return api.client.DNSRecord.Update(context.Background(), dc.Name, r) }, }) } @@ -141,60 +123,38 @@ func (api *VultrApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Co } // GetNameservers gets the Vultr nameservers for a domain -func (api *VultrApi) GetNameservers(domain string) ([]*models.Nameserver, error) { +func (api *Provider) GetNameservers(domain string) ([]*models.Nameserver, error) { return models.StringsToNameservers(defaultNS), nil } // EnsureDomainExists adds a domain to the Vutr DNS service if it does not exist -func (api *VultrApi) EnsureDomainExists(domain string) error { - ok, err := api.isDomainInAccount(domain) - if err != nil { +func (api *Provider) EnsureDomainExists(domain string) error { + if ok, err := api.isDomainInAccount(domain); err != nil { return err + } else if ok { + return nil } - if !ok { - // Vultr requires an initial IP, use a dummy one - err := api.client.CreateDNSDomain(domain, "127.0.0.1") - if err != nil { - return err - } - - ok, err := api.isDomainInAccount(domain) - if err != nil { - return err - } - if !ok { - return errors.Errorf("Unexpected error adding domain %s to Vultr account", domain) - } - } - - return nil + // Vultr requires an initial IP, use a dummy one. + return api.client.DNSDomain.Create(context.Background(), domain, "0.0.0.0") } -func (api *VultrApi) isDomainInAccount(domain string) (bool, error) { - domains, err := api.client.GetDNSDomains() +func (api *Provider) isDomainInAccount(domain string) (bool, error) { + domains, err := api.client.DNSDomain.List(context.Background()) if err != nil { return false, err } - - var vd *vultr.DNSDomain for _, d := range domains { if d.Domain == domain { - vd = &d + return true, nil } } - - if vd == nil { - return false, nil - } - - return true, nil + return false, nil } -// toRecordConfig converts a Vultr DNSRecord to a RecordConfig #rtype_variations -func toRecordConfig(dc *models.DomainConfig, r *vultr.DNSRecord) (*models.RecordConfig, error) { - origin := dc.Name - data := r.Data +// toRecordConfig converts a Vultr DNSRecord to a RecordConfig. #rtype_variations +func toRecordConfig(dc *models.DomainConfig, r *govultr.DNSRecord) (*models.RecordConfig, error) { + origin, data := dc.Name, r.Data rc := &models.RecordConfig{ TTL: uint32(r.TTL), Original: r, @@ -204,14 +164,14 @@ func toRecordConfig(dc *models.DomainConfig, r *vultr.DNSRecord) (*models.Record switch rtype := r.Type; rtype { case "CNAME", "NS": rc.Type = r.Type - // Make target into a FQDN if it is a CNAME, NS, MX, or SRV + // Make target into a FQDN if it is a CNAME, NS, MX, or SRV. if !strings.HasSuffix(data, ".") { data = data + "." } // FIXME(tlim): the AddOrigin() might be unneeded. Please test. return rc, rc.SetTarget(dnsutil.AddOrigin(data, origin)) case "CAA": - // Vultr returns in the format "[flag] [tag] [value]" + // Vultr returns CAA records in the format "[flag] [tag] [value]". return rc, rc.SetTargetCAAString(data) case "MX": if !strings.HasSuffix(data, ".") { @@ -219,10 +179,10 @@ func toRecordConfig(dc *models.DomainConfig, r *vultr.DNSRecord) (*models.Record } return rc, rc.SetTargetMX(uint16(r.Priority), data) case "SRV": - // Vultr returns in the format "[weight] [port] [target]" + // Vultr returns SRV records in the format "[weight] [port] [target]". return rc, rc.SetTargetSRVPriorityString(uint16(r.Priority), data) case "TXT": - // Remove quotes if it is a TXT + // Remove quotes if it is a TXT record. if !strings.HasPrefix(data, `"`) || !strings.HasSuffix(data, `"`) { return nil, errors.New("Unexpected lack of quotes in TXT record from Vultr") } @@ -232,21 +192,21 @@ func toRecordConfig(dc *models.DomainConfig, r *vultr.DNSRecord) (*models.Record } } -// toVultrRecord converts a RecordConfig converted by toRecordConfig back to a Vultr DNSRecord #rtype_variations -func toVultrRecord(dc *models.DomainConfig, rc *models.RecordConfig) *vultr.DNSRecord { +// toVultrRecord converts a RecordConfig converted by toRecordConfig back to a Vultr DNSRecord. #rtype_variations +func toVultrRecord(dc *models.DomainConfig, rc *models.RecordConfig, vultrID int) *govultr.DNSRecord { name := rc.GetLabel() - // Vultr uses a blank string to represent the apex domain + // Vultr uses a blank string to represent the apex domain. if name == "@" { name = "" } data := rc.GetTargetField() - // Vultr does not use a period suffix for the server for CNAME, NS, or MX + // Vultr does not use a period suffix for CNAME, NS, or MX. if strings.HasSuffix(data, ".") { data = data[:len(data)-1] } - // Vultr needs TXT record in quotes + // Vultr needs TXT record in quotes. if rc.Type == "TXT" { data = fmt.Sprintf(`"%s"`, data) } @@ -260,7 +220,8 @@ func toVultrRecord(dc *models.DomainConfig, rc *models.RecordConfig) *vultr.DNSR priority = int(rc.SrvPriority) } - r := &vultr.DNSRecord{ + r := &govultr.DNSRecord{ + RecordID: vultrID, Type: rc.Type, Name: name, Data: data, @@ -269,11 +230,7 @@ func toVultrRecord(dc *models.DomainConfig, rc *models.RecordConfig) *vultr.DNSR } switch rtype := rc.Type; rtype { // #rtype_variations case "SRV": - target := rc.GetTargetField() - if strings.HasSuffix(target, ".") { - target = target[:len(target)-1] - } - r.Data = fmt.Sprintf("%v %v %s", rc.SrvWeight, rc.SrvPort, target) + r.Data = fmt.Sprintf("%v %v %s", rc.SrvWeight, rc.SrvPort, rc.GetTargetField()) case "CAA": r.Data = fmt.Sprintf(`%v %s "%s"`, rc.CaaFlag, rc.CaaTag, rc.GetTargetField()) default: 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 f994f9242..000000000 --- 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 f44d8c81e..000000000 --- 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 e9e8e115d..000000000 --- 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 0f805a10c..000000000 --- a/vendor/github.com/JamesClonk/vultr/lib/client.go +++ /dev/null @@ -1,249 +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.13.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 -} - -// 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 apiKeyPath(path, apiKey string) string { - if strings.Contains(path, "?") { - return path + "&api_key=" + apiKey - } - return path + "?api_key=" + apiKey -} - -func (c *Client) get(path string, data interface{}) error { - req, err := c.newRequest("GET", apiPath(path), nil) - if err != nil { - return err - } - return c.do(req, data) -} - -func (c *Client) post(path string, values url.Values, data interface{}) error { - req, err := c.newRequest("POST", apiPath(path), strings.NewReader(values.Encode())) - if err != nil { - return err - } - return c.do(req, data) -} - -func (c *Client) newRequest(method string, path string, body io.Reader) (*http.Request, error) { - relPath, err := url.Parse(apiKeyPath(path, c.APIKey)) - 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("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 - } - - 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 e66275b68..000000000 --- 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 c9c54e9d5..000000000 --- a/vendor/github.com/JamesClonk/vultr/lib/firewall.go +++ /dev/null @@ -1,248 +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 subnetSize > 0 && 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 { - _, 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 4d169cb3f..000000000 --- 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 a9e1880e4..000000000 --- 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 647d253f8..000000000 --- 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 b3bef4eff..000000000 --- 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 70ceb2ede..000000000 --- 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 22097cf7d..000000000 --- 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 d6639cf1e..000000000 --- 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 418198a71..000000000 --- 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 7e9969306..000000000 --- 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 006e16f2d..000000000 --- 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/vultr/govultr/CHANGELOG.md b/vendor/github.com/vultr/govultr/CHANGELOG.md new file mode 100644 index 000000000..ca0c596a4 --- /dev/null +++ b/vendor/github.com/vultr/govultr/CHANGELOG.md @@ -0,0 +1,53 @@ +# Change Log + +## [v0.1.3](https://github.com/vultr/govultr/compare/v0.1.2..v0.1.3) (2019-06-13) +### Features +* added `GetVc2zList` to Plans to retrieve `high-frequency compute` plans [#13](https://github.com/vultr/govultr/pull/13) + +### Breaking Changes +* Renamed all variables named `vpsID` to `instanceID` [#14](https://github.com/vultr/govultr/pull/14) +* Server + * Renamed Server struct field `VpsID` to `InstanceID` [#14](https://github.com/vultr/govultr/pull/14) +* Plans + * Renamed Plan struct field `VpsID` to `PlanID` [#14](https://github.com/vultr/govultr/pull/14) + * Renamed BareMetalPlan struct field `BareMetalID` to `PlanID` [#14](https://github.com/vultr/govultr/pull/14) + * Renamed VCPlan struct field `VpsID` to `PlanID` [#14](https://github.com/vultr/govultr/pull/14) + * Renamed Plan struct field `VCPUCount` to `vCPUs` [#13](https://github.com/vultr/govultr/pull/13) + * Renamed BareMetalPlan struct field `CPUCount` to `CPUs` [#13](https://github.com/vultr/govultr/pull/13) + * Renamed VCPlan struct field `VCPUCount` to `vCPUs` [#13](https://github.com/vultr/govultr/pull/13) + * Renamed VCPlan struct field `Cost` to `Price` [#13](https://github.com/vultr/govultr/pull/13) + +## [v0.1.2](https://github.com/vultr/govultr/compare/v0.1.1..v0.1.2) (2019-05-29) +### Fixes +* Fixed Server Option `NotifyActivate` bug that ignored a `false` value +* Fixed Bare Metal Server Option `UserData` to be based64encoded +### Breaking Changes +* Renamed all methods named `GetList` to `List` +* Renamed all methods named `Destroy` to `Delete` +* Server Service + * Renamed `GetListByLabel` to `ListByLabel` + * Renamed `GetListByMainIP` to `ListByMainIP` + * Renamed `GetListByTag` to `ListByTag` +* Bare Metal Server Service + * Renamed `GetListByLabel` to `ListByLabel` + * Renamed `GetListByMainIP` to `ListByMainIP` + * Renamed `GetListByTag` to `ListByTag` + +## [v0.1.1](https://github.com/vultr/govultr/compare/v0.1.0..v0.1.1) (2019-05-20) +### Features +* add `SnapshotID` to ServerOptions as an option during server creation +* bumped default RateLimit from `.2` to `.6` seconds +### Breaking Changes +* Iso + * Renamed all instances of `Iso` to `ISO`. +* BlockStorage + * Renamed `Cost` to `CostPerMonth` + * Renamed `Size` to `SizeGB` +* BareMetal & Server + * Change `SSHKeyID` to `SSHKeyIDs` which are now `[]string` instead of `string` + * Renamed `OS` to `Os` + +## v0.1.0 (2019-05-10) +### Features +* Initial release +* Supports all available API endpoints that Vultr has to offer \ No newline at end of file diff --git a/vendor/github.com/vultr/govultr/CONTRIBUTING.md b/vendor/github.com/vultr/govultr/CONTRIBUTING.md new file mode 100644 index 000000000..228f33ac1 --- /dev/null +++ b/vendor/github.com/vultr/govultr/CONTRIBUTING.md @@ -0,0 +1,64 @@ +# Contributing to `govultr` + +We would love to get your feedback, thoughts, and overall improvements to `govultr`! + +## Overview + +- All code should run through `go fmt` +- All code **must be tested** +- All types, structs, and funcs **must be documented** for GoDocs + +## Getting started + +GoVultr supports `go modules` so you can pull down the repo outside of your `$GOPATH`. + +You can also run: +`go get -u github.com/vultr/govultr` + +## Testing + +We aim to have as much code coverage as possible. + +To run tests locally: + +```sh +go test . +``` + +If you want to get more information on your local unit tests. You can run the following: + +```sh +go test -v -coverprofile cover.out +go tool cover -html=cover.out +``` + +Upon opening a pull request we have CodeCov checks to make sure that code coverage meets a minimum requirement. In addition to CodeCov we have Travis CI that will run your unit tests on each pull request as well. + +## Versioning + +GoVultr follows [SemVer](http://semver.org/) for versioning. New functionality will result in a increment to the minor version and bug fixes will result in a increment to the patch version. + +## Releases + +Releases of new versions are done as independent pull requests and will be made by the maintainers. + +To release a new version of `govultr` we must do the following: + +- Update version number in `govultr.go` to reflect the new release version +- Make the appropriate updates to `CHANGELOG.md`. This should include the: + - Version, + - List of fix/features with accompanying pull request ID + - Description of each fix/feature + +``` +## v0.0.1 (2019-05-05) + +### Fixes +* Fixed random bug #12 + +### Features +* BareMetalServer functionality #13 +``` + +- Submit a pull request with the changes above. +- Once the pull request is merged in, create a new tag and publish. 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 be20a1280..2ea4faa68 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/README.md b/vendor/github.com/vultr/govultr/README.md new file mode 100644 index 000000000..b777b9b95 --- /dev/null +++ b/vendor/github.com/vultr/govultr/README.md @@ -0,0 +1,94 @@ +# GoVultr + +[![Build Status](https://travis-ci.org/vultr/govultr.svg?branch=master)](https://travis-ci.org/vultr/govultr) +[![GoDoc](https://godoc.org/github.com/vultr/govultr?status.svg)](https://godoc.org/github.com/vultr/govultr) +[![codecov](https://codecov.io/gh/vultr/govultr/branch/master/graph/badge.svg?token=PDJXBc7Rci)](https://codecov.io/gh/vultr/govultr) +[![Go Report Card](https://goreportcard.com/badge/github.com/vultr/govultr)](https://goreportcard.com/report/github.com/vultr/govultr) + +The official Vultr Go client - GoVultr allows you to interact with the Vultr V1 API. + +## Installation + +```sh +go get -u github.com/vultr/govultr +``` + +## Usage + +Vultr uses a PAT (Personal Access token) to interact/authenticate with the APIs. An API Key can be generated and acquired from the API menu in [settings](https://my.vultr.com/settings/#settingsapi). + +To instantiate a govultr client you invoke `NewClient()`. + +This takes in two parameters: + +- \*http.Client +- API Key + +You can define your own `http.Client` however if you pass in `nil` then you will be defaulted to use `http.DefaultClient`. For the API key, we recommend you store this as a environment variable and not hard code it. + +There are also three optional parameters you may change regarding the client: + +- BaseUrl: allows you to override the base URL that Vultr defaults to +- UserAgent: allows you to override the UserAgent that Vultr defaults to +- RateLimit: Vultr currently rate limits how fast you can make calls back to back. This lets you configure if you want a delay in between calls + +### Example Client Setup + +```go +package main + +import ( + "github.com/vultr/govultr" + "os" +) + +func main() { + apiKey := os.Getenv("VultrAPIKey") + + vultrClient := govultr.NewClient(nil, apiKey) + + // Optional changes + _ = vultrClient.SetBaseURL("https://api.vultr.com") + vultrClient.SetUserAgent("mycool-app") + vultrClient.SetRateLimit(500) +} +``` + +### Example Usage + +Create a VPS + +```go +vpsOptions := &govultr.ServerOptions{ + Label: "awesome-go-app", + Hostname: "awesome-go.com", + EnablePrivateNetwork: true, + AutoBackups: true, + EnableIPV6: true, +} + +// RegionId, VpsPlanID, OsID can be grabbed from their respective API calls +res, err := vultrClient.Server.Create(context.Background(), 1, 201, 1, vpsOptions) + +if err != nil { + fmt.Println(err) +} +``` + +## Versioning + +This project follows [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/vultr/govultr/tags). + +## Documentation + +For detailed information about our V1 API head over to our [API documentation](https://www.vultr.com/api/). + +If you want more details about this client's functionality then check out our [GoDoc](https://godoc.org/github.com/vultr/govultr) documentation. + +## Contributing + +Feel free to send pull requests our way! Please see the [contributing guidelines](CONTRIBUTING.md). + +## License + +This project is licensed under the MIT License - see the [LICENSE.md](LICENSE) file for details. diff --git a/vendor/github.com/vultr/govultr/account.go b/vendor/github.com/vultr/govultr/account.go new file mode 100644 index 000000000..33a16dfd5 --- /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 000000000..2ebfb99ab --- /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 000000000..9199bd95b --- /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 000000000..0a87c6408 --- /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 000000000..22ce4368c --- /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 000000000..c62f926d7 --- /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 000000000..dbd9913b7 --- /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 000000000..29260a5b5 --- /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 000000000..ee874d10b --- /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 000000000..ab99c1ab7 --- /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/go.mod b/vendor/github.com/vultr/govultr/go.mod new file mode 100644 index 000000000..c4dfd1d45 --- /dev/null +++ b/vendor/github.com/vultr/govultr/go.mod @@ -0,0 +1,3 @@ +module github.com/vultr/govultr + +go 1.12 diff --git a/vendor/github.com/vultr/govultr/govultr.go b/vendor/github.com/vultr/govultr/govultr.go new file mode 100644 index 000000000..fce0a9394 --- /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.3" + 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 000000000..8cedd34cd --- /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 000000000..d6c32ea2a --- /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 000000000..933ddaff3 --- /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 000000000..75287d5ad --- /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 000000000..cfdbbebc8 --- /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 000000000..ee9335c4a --- /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 000000000..787046627 --- /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 000000000..92e53d0c6 --- /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 000000000..7f6b0a194 --- /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 000000000..cff944369 --- /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 000000000..40e4040f7 --- /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 +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 2fcbc2240..3f226ef35 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -26,12 +26,6 @@ "revision": "5ea4d0ddac554439159cd6f191cb94a110d73352", "revisionTime": "2017-12-18T18:09:44Z" }, - { - "checksumSHA1": "E0NbOzBCBYViPKvM4XcloBu6GWE=", - "path": "github.com/JamesClonk/vultr/lib", - "revision": "2fd0705ce648e602e6c9c57329a174270a4f6688", - "revisionTime": "2017-08-08T19:54:39Z" - }, { "checksumSHA1": "dxnNQI0lrvE1S4lNRzJI647hQDY=", "path": "github.com/TomOnTime/utfutil", @@ -830,6 +824,12 @@ "revision": "75104e932ac2ddb944a6ea19d9f9f26316ff1145", "revisionTime": "2018-01-06T19:10:48Z" }, + { + "checksumSHA1": "IXgi3wJQDZY+XCMilbQzAWiOW94=", + "path": "github.com/vultr/govultr", + "revision": "4cfc19dfc3d058d94348267969d9a27551ff7476", + "revisionTime": "2019-07-14T13:33:55Z" + }, { "checksumSHA1": "hOQeoMW+s2bHogWrQWV2UkEKOWM=", "path": "github.com/xenolf/lego/acme",