pax_global_header00006660000000000000000000000064136655066630014532gustar00rootroot0000000000000052 comment=f649b7080ce76ddd2ad0f784019ca12df5141625 govultr-0.4.2/000077500000000000000000000000001366550666300132375ustar00rootroot00000000000000govultr-0.4.2/.codecov.yml000066400000000000000000000001621366550666300154610ustar00rootroot00000000000000coverage: status: project: default: target: 70% patch: default: target: 60% govultr-0.4.2/.gitignore000066400000000000000000000004121366550666300152240ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out ### Go Patch ### /vendor/ /Godeps/ .DS_Store .idea .vscode ### Misc. cmdgovultr-0.4.2/.travis.yml000066400000000000000000000003351366550666300153510ustar00rootroot00000000000000language: go go: - 1.13.x - 1.14.x - tip matrix: allow_failures: - go: tip script: - go test -race -coverprofile=coverage.txt -covermode=atomic after_success: - bash <(curl -s https://codecov.io/bash) govultr-0.4.2/CHANGELOG.md000066400000000000000000000130111366550666300150440ustar00rootroot00000000000000# Change Log ## [v0.4.2](https://github.com/vultr/govultr/compare/v0.4.1..v0.4.2) (2020-06-02) ### Bug Fix * DNS Records: Allow DNS Records on updates to have priority of 0 [#67](https://github.com/vultr/govultr/pull/67) ## [v0.4.1](https://github.com/vultr/govultr/compare/v0.4.0..v0.4.1) (2020-05-08) ### Bug Fix * LoadBalancers: Fix nil pointer in create call [#65](https://github.com/vultr/govultr/pull/65) ## [v0.4.0](https://github.com/vultr/govultr/compare/v0.3.3..v0.4.0) (2020-04-29) ### Enhancement * LoadBalancers: Proxy protocol is now available as an option [#62](https://github.com/vultr/govultr/pull/62) * LoadBalancers: Ability to attach instances during Create (note this adds a new param to create call) [#62](https://github.com/vultr/govultr/pull/62) ### Bug Fix * LoadBalancers: Create call will now properly pass your algorithm [#61](https://github.com/vultr/govultr/pull/61) ### CI/CD * Drop go 1.12 and add 1.14 [#63](https://github.com/vultr/govultr/pull/63) ## [v0.3.3](https://github.com/vultr/govultr/compare/v0.3.2..v0.3.3) (2020-04-15) ### Dependencies * go-retryablehttp 0.6.4 -> 0.6.6 [#58](https://github.com/vultr/govultr/pull/58) ## [v0.3.2](https://github.com/vultr/govultr/compare/v0.3.1..v0.3.2) (2020-03-25) ### Enhancement * Added support to live attach/detach blockstorage [#55](https://github.com/vultr/govultr/pull/55) ## [v0.3.1](https://github.com/vultr/govultr/compare/v0.3.0..v0.3.1) (2020-03-11) ### Enhancement * Added support for Load Balancers SSL Calls [#53](https://github.com/vultr/govultr/pull/53) ### Bug Fix * Fixed InstanceList type from string to int [#50](https://github.com/vultr/govultr/pull/50) * Fixed struct json marshalling issue [#48](https://github.com/vultr/govultr/pull/48) ### Dependencies * go-retryablehttp 0.6.3 -> 0.6.4 [#51](https://github.com/vultr/govultr/pull/51) ## [v0.3.0](https://github.com/vultr/govultr/compare/v0.2.0..v0.3.0) (2020-02-24) ### Enhancement * Added support for Load Balancers [#44](https://github.com/vultr/govultr/pull/44) ### Bug Fix * Fixed Object Storage Get call [#46](https://github.com/vultr/govultr/pull/46) ## [v0.2.0](https://github.com/vultr/govultr/compare/v0.1.7..v0.2.0) (2020-02-06) ### Enhancement * Added support for Object Storage [#39](https://github.com/vultr/govultr/pull/39) ## [v0.1.7](https://github.com/vultr/govultr/compare/v0.1.6..v0.1.7) (2019-11-11) ### Enhancement * Version number was missing in v0.1.6 - Attempt was made to fix however it will not work. Cutting new release to remedy this. ## [v0.1.6](https://github.com/vultr/govultr/compare/v0.1.5..v0.1.6) (2019-11-07) ### Enhancement * Retry rate-limited requests with exponential backoff[#28](https://github.com/vultr/govultr/pull/28) ## [v0.1.5](https://github.com/vultr/govultr/compare/v0.1.4..v0.1.5) (2019-10-16) ### Enhancement * Whitelisting public endpoints that do not require the api key[#24](https://github.com/vultr/govultr/pull/24) ## [v0.1.4](https://github.com/vultr/govultr/compare/v0.1.3..v0.1.4) (2019-07-14) ### Bug Fixes * Fix panic on request failure [#20](https://github.com/vultr/govultr/pull/20) ## [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 govultr-0.4.2/CONTRIBUTING.md000066400000000000000000000033771366550666300155020ustar00rootroot00000000000000# 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. govultr-0.4.2/LICENSE000066400000000000000000000020461366550666300142460ustar00rootroot00000000000000MIT License 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 in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 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. govultr-0.4.2/README.md000066400000000000000000000057361366550666300145310ustar00rootroot00000000000000# 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. govultr-0.4.2/account.go000066400000000000000000000020361366550666300152230ustar00rootroot00000000000000package 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 } govultr-0.4.2/account_test.go000066400000000000000000000024331366550666300162630ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestAccountServiceHandler_GetInfo(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/account/info", func(w http.ResponseWriter, r *http.Request) { response := ` { "balance": "-5519.11", "pending_charges": "57.03", "last_payment_date": "2014-07-18 15:31:01", "last_payment_amount": "-1.00" } ` fmt.Fprint(w, response) }) account, err := client.Account.GetInfo(ctx) if err != nil { t.Errorf("Account.GetInfo returned error: %v", err) } expected := &Account{Balance: "-5519.11", PendingCharges: "57.03", LastPaymentDate: "2014-07-18 15:31:01", LastPaymentAmount: "-1.00"} if !reflect.DeepEqual(account, expected) { t.Errorf("Account.GetInfo returned %+v, expected %+v", account, expected) } } func TestAccountServiceHandler_GetInfoEmpty(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/account/info", func(w http.ResponseWriter, r *http.Request) { response := `[]` fmt.Fprint(w, response) }) account, err := client.Account.GetInfo(ctx) if err != nil { t.Errorf("Account.GetInfo returned error: %v", err) } expected := &Account{} if !reflect.DeepEqual(account, expected) { t.Errorf("Account.GetInfo returned %+v, expected %+v", account, expected) } } govultr-0.4.2/api.go000066400000000000000000000015761366550666300143500ustar00rootroot00000000000000package 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 } govultr-0.4.2/api_test.go000066400000000000000000000015331366550666300154000ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestAPIServiceHandler_GetInfo(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/auth/info", func(writer http.ResponseWriter, request *http.Request) { response := ` { "acls": [ "subscriptions", "billing", "support", "provisioning" ], "email": "example@vultr.com", "name": "Example Account" }` fmt.Fprint(writer, response) }) apiAuth, err := client.API.GetInfo(ctx) if err != nil { t.Errorf("Account.GetInfo returned error: %v", err) } expected := &API{ACL: []string{"subscriptions", "billing", "support", "provisioning"}, Email: "example@vultr.com", Name: "Example Account"} if !reflect.DeepEqual(apiAuth, expected) { t.Errorf("API.GetInfo returned %+v, expected %+v", apiAuth, expected) } } govultr-0.4.2/application.go000066400000000000000000000023401366550666300160700ustar00rootroot00000000000000package 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 } govultr-0.4.2/application_test.go000066400000000000000000000024571366550666300171400ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestApplicationServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/app/list", func(w http.ResponseWriter, r *http.Request) { response := ` { "1": { "APPID": "1", "name": "LEMP", "short_name": "lemp", "deploy_name": "LEMP on CentOS 6 x64", "surcharge": 0 } } ` fmt.Fprint(w, response) }) apps, err := client.Application.List(ctx) if err != nil { t.Errorf("Application.List returned error: %v", err) } expected := []Application{ { AppID: "1", Name: "LEMP", ShortName: "lemp", DeployName: "LEMP on CentOS 6 x64", Surcharge: 0, }, } if !reflect.DeepEqual(apps, expected) { t.Errorf("Application.List returned %+v, expected %+v", apps, expected) } } func TestApplicationServiceHandler_ListEmpty(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/app/list", func(w http.ResponseWriter, r *http.Request) { response := `[]` fmt.Fprint(w, response) }) apps, err := client.Application.List(ctx) if err != nil { t.Errorf("Application.List returned error: %v", err) } var expected []Application if !reflect.DeepEqual(apps, expected) { t.Errorf("Application.List returned %+v, expected %+v", apps, expected) } } govultr-0.4.2/backup.go000066400000000000000000000047601366550666300150420ustar00rootroot00000000000000package 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 } govultr-0.4.2/backup_test.go000066400000000000000000000101211366550666300160650ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestBackupServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/backup/list", func(w http.ResponseWriter, r *http.Request) { response := ` { "543d34149403a": { "BACKUPID": "543d34149403a", "date_created": "2014-10-14 12:40:40", "description": "Automatic server backup", "size": "42949672960", "status": "complete" } } ` fmt.Fprint(w, response) }) backups, err := client.Backup.List(ctx) if err != nil { t.Errorf("Backup.List returned error: %v", err) } expected := []Backup{ { BackupID: "543d34149403a", DateCreated: "2014-10-14 12:40:40", Description: "Automatic server backup", Size: "42949672960", Status: "complete", }, } if !reflect.DeepEqual(backups, expected) { t.Errorf("Backup.List returned %+v, expected %+v", backups, expected) } } func TestBackupServiceHandler_ListEmpty(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/backup/list", func(w http.ResponseWriter, r *http.Request) { response := `[]` fmt.Fprint(w, response) }) backups, err := client.Backup.List(ctx) if err != nil { t.Errorf("Backup.List returned error: %v", err) } var expected []Backup if !reflect.DeepEqual(backups, expected) { t.Errorf("Backup.List returned %+v, expected %+v", backups, expected) } } func TestBackupServiceHandler_ListBySub(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/backup/list", func(w http.ResponseWriter, r *http.Request) { response := ` { "543d34149403a": { "BACKUPID": "543d34149403a", "date_created": "2014-10-14 12:40:40", "description": "Automatic server backup", "size": "42949672960", "status": "complete" } } ` fmt.Fprint(w, response) }) backups, err := client.Backup.ListBySub(ctx, "test-backupID") if err != nil { t.Errorf("Backup.ListBySub returned error: %v", err) } expected := []Backup{ { BackupID: "543d34149403a", DateCreated: "2014-10-14 12:40:40", Description: "Automatic server backup", Size: "42949672960", Status: "complete", }, } if !reflect.DeepEqual(backups, expected) { t.Errorf("Backup.ListBySub returned %+v, expected %+v", backups, expected) } } func TestBackupServiceHandler_ListBySubEmpty(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/backup/list", func(w http.ResponseWriter, r *http.Request) { response := `[]` fmt.Fprint(w, response) }) backups, err := client.Backup.ListBySub(ctx, "test-backupID") if err != nil { t.Errorf("Backup.ListBySub returned error: %v", err) } var expected []Backup if !reflect.DeepEqual(backups, expected) { t.Errorf("Backup.ListBySub returned %+v, expected %+v", backups, expected) } } func TestBackupServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/backup/list", func(w http.ResponseWriter, r *http.Request) { response := ` { "543d34149403a": { "BACKUPID": "543d34149403a", "date_created": "2014-10-14 12:40:40", "description": "Automatic server backup", "size": "42949672960", "status": "complete" } } ` fmt.Fprint(w, response) }) backup, err := client.Backup.Get(ctx, "543d34149403a") if err != nil { t.Errorf("Backup.Get returned error: %v", err) } expected := &Backup{ BackupID: "543d34149403a", DateCreated: "2014-10-14 12:40:40", Description: "Automatic server backup", Size: "42949672960", Status: "complete", } if !reflect.DeepEqual(backup, expected) { t.Errorf("Backup.Get returned %+v, expected %+v", backup, expected) } } func TestBackupServiceHandler_GetEmpty(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/backup/list", func(w http.ResponseWriter, r *http.Request) { response := `[]` fmt.Fprint(w, response) }) backup, err := client.Backup.Get(ctx, "test-backupID") if err != nil { t.Errorf("Backup.Get returned error: %v", err) } expected := &Backup{} if !reflect.DeepEqual(backup, expected) { t.Errorf("Backup.Get returned %+v, expected %+v", backup, expected) } } govultr-0.4.2/bare_metal_server.go000066400000000000000000000510751366550666300172570ustar00rootroot00000000000000package 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 } govultr-0.4.2/bare_metal_server_test.go000066400000000000000000000505371366550666300203200ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestBareMetalServerServiceHandler_AppInfo(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/get_app_info", func(writer http.ResponseWriter, request *http.Request) { response := ` { "app_info": "Minecraft Server Details\n\nYour Minecraft server is accessible at: \n\n45.74.196.185:25565\n\nYou can access the console of your Minecraft server by using the \"screen\" utility from the following login:\nUser: minecraft\nPass: NXwdsdZjwJasdZbsc\n\nRead more about this app on Vultr Docs: \n\nhttps://www.vultr.com/docs/one-click-minecraft\n" } ` fmt.Fprint(writer, response) }) appInfo, err := client.BareMetalServer.AppInfo(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.AppInfo returned error: %v", err) } expected := &AppInfo{ AppInfo: "Minecraft Server Details\n\nYour Minecraft server is accessible at: \n\n45.74.196.185:25565\n\nYou can access the console of your Minecraft server by using the \"screen\" utility from the following login:\nUser: minecraft\nPass: NXwdsdZjwJasdZbsc\n\nRead more about this app on Vultr Docs: \n\nhttps://www.vultr.com/docs/one-click-minecraft\n", } if !reflect.DeepEqual(appInfo, expected) { t.Errorf("BareMetalServer.AppInfo returned %+v, expected %+v", appInfo, expected) } } func TestBareMetalServerServiceHandler_Bandwidth(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/bandwidth", func(writer http.ResponseWriter, request *http.Request) { response := ` { "incoming_bytes": [ [ "2017-04-01", 91571055 ], [ "2017-04-02", 78355758 ], [ "2017-04-03", 85827590 ] ], "outgoing_bytes": [ [ "2017-04-01", 3084731 ], [ "2017-04-02", 1810478 ], [ "2017-04-03", 2729604 ] ] } ` fmt.Fprint(writer, response) }) bandwidth, err := client.BareMetalServer.Bandwidth(ctx, "123") if err != nil { t.Errorf("BareMetalServer.Bandwidth returned %+v", err) } expected := []map[string]string{ {"date": "2017-04-01", "incoming": "91571055", "outgoing": "3084731"}, {"date": "2017-04-02", "incoming": "78355758", "outgoing": "1810478"}, {"date": "2017-04-03", "incoming": "85827590", "outgoing": "2729604"}, } if !reflect.DeepEqual(bandwidth, expected) { t.Errorf("BareMetalServer.Bandwidth returned %+v, expected %+v", bandwidth, expected) } } func TestBareMetalServerServiceHandler_ChangeApp(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/app_change", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.ChangeApp(ctx, "900000", "15") if err != nil { t.Errorf("BareMetalServer.ChangeApp returned %+v, ", err) } } func TestBareMetalServerServiceHandler_ChangeOS(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/os_change", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.ChangeOS(ctx, "900000", "302") if err != nil { t.Errorf("BareMetalServer.ChangeOS return %+v ", err) } } func TestBareMetalServerServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/create", func(writer http.ResponseWriter, request *http.Request) { response := ` { "SUBID": "900000" } ` fmt.Fprint(writer, response) }) options := &BareMetalServerOptions{ StartupScriptID: "1", SnapshotID: "1", EnableIPV6: "yes", Label: "go-bm-test", SSHKeyIDs: []string{"6b80207b1821f"}, AppID: "1", UserData: "echo Hello World", NotifyActivate: "yes", Hostname: "test", Tag: "go-test", ReservedIPV4: "111.111.111.111", } bm, err := client.BareMetalServer.Create(ctx, "1", "1", "1", options) if err != nil { t.Errorf("BareMetalServer.Create returned error: %v", err) } expected := &BareMetalServer{BareMetalServerID: "900000"} if !reflect.DeepEqual(bm, expected) { t.Errorf("BareMetalServer.Create returned %+v, expected %+v", bm, expected) } } func TestBareMetalServerServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/destroy", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.Delete(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.Delete returned %+v, expected %+v", err, nil) } } func TestBareMetalServerServiceHandler_EnableIPV6(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/ipv6_enable", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.EnableIPV6(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.EnableIPV6 returned %+v", err) } } func TestBareMetalServerServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "900000": { "SUBID": "900000", "os": "CentOS 6 x64", "ram": "65536 MB", "disk": "2x 240 GB SSD", "main_ip": "203.0.113.10", "cpu_count": 1, "location": "New Jersey", "DCID": "1", "default_password": "ab81u!ryranq", "date_created": "2017-04-12 18:45:41", "status": "active", "netmask_v4": "255.255.255.0", "gateway_v4": "203.0.113.1", "METALPLANID": 28, "v6_networks": [ { "v6_network": "2001:DB8:9000::", "v6_main_ip": "2001:DB8:9000::100", "v6_network_size": 64 } ], "label": "my label", "tag": "my tag", "OSID": "127", "APPID": "0" } } ` fmt.Fprint(writer, response) }) bm, err := client.BareMetalServer.List(ctx) if err != nil { t.Errorf("BareMetalServer.List returned error: %v", err) } expected := []BareMetalServer{ { BareMetalServerID: "900000", Os: "CentOS 6 x64", RAM: "65536 MB", Disk: "2x 240 GB SSD", MainIP: "203.0.113.10", CPUs: 1, Location: "New Jersey", RegionID: 1, DefaultPassword: "ab81u!ryranq", DateCreated: "2017-04-12 18:45:41", Status: "active", NetmaskV4: "255.255.255.0", GatewayV4: "203.0.113.1", BareMetalPlanID: 28, V6Networks: []V6Network{ { Network: "2001:DB8:9000::", MainIP: "2001:DB8:9000::100", NetworkSize: "64", }, }, Label: "my label", Tag: "my tag", OsID: "127", AppID: "0", }, } if !reflect.DeepEqual(bm, expected) { t.Errorf("BareMetalServer.List returned %+v, expected %+v", bm, expected) } } func TestBareMetalServerServiceHandler_ListByLabel(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "900000": { "SUBID": "900000", "os": "CentOS 6 x64", "ram": "65536 MB", "disk": "2x 240 GB SSD", "main_ip": "203.0.113.10", "cpu_count": 1, "location": "New Jersey", "DCID": "1", "default_password": "ab81u!ryranq", "date_created": "2017-04-12 18:45:41", "status": "active", "netmask_v4": "255.255.255.0", "gateway_v4": "203.0.113.1", "METALPLANID": 28, "v6_networks": [ { "v6_network": "2001:DB8:9000::", "v6_main_ip": "2001:DB8:9000::100", "v6_network_size": 64 } ], "label": "my label", "tag": "my tag", "OSID": "127", "APPID": "0" } } ` fmt.Fprint(writer, response) }) bm, err := client.BareMetalServer.ListByLabel(ctx, "my label") if err != nil { t.Errorf("BareMetalServer.ListByLabel returned error: %v", err) } expected := []BareMetalServer{ { BareMetalServerID: "900000", Os: "CentOS 6 x64", RAM: "65536 MB", Disk: "2x 240 GB SSD", MainIP: "203.0.113.10", CPUs: 1, Location: "New Jersey", RegionID: 1, DefaultPassword: "ab81u!ryranq", DateCreated: "2017-04-12 18:45:41", Status: "active", NetmaskV4: "255.255.255.0", GatewayV4: "203.0.113.1", BareMetalPlanID: 28, V6Networks: []V6Network{ { Network: "2001:DB8:9000::", MainIP: "2001:DB8:9000::100", NetworkSize: "64", }, }, Label: "my label", Tag: "my tag", OsID: "127", AppID: "0", }, } if !reflect.DeepEqual(bm, expected) { t.Errorf("BareMetalServer.ListByLabel returned %+v, expected %+v", bm, expected) } } func TestBareMetalServerServiceHandler_ListByMainIP(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "900000": { "SUBID": "900000", "os": "CentOS 6 x64", "ram": "65536 MB", "disk": "2x 240 GB SSD", "main_ip": "203.0.113.10", "cpu_count": 1, "location": "New Jersey", "DCID": "1", "default_password": "ab81u!ryranq", "date_created": "2017-04-12 18:45:41", "status": "active", "netmask_v4": "255.255.255.0", "gateway_v4": "203.0.113.1", "METALPLANID": 28, "v6_networks": [ { "v6_network": "2001:DB8:9000::", "v6_main_ip": "2001:DB8:9000::100", "v6_network_size": 64 } ], "label": "my label", "tag": "my tag", "OSID": "127", "APPID": "0" } } ` fmt.Fprint(writer, response) }) bm, err := client.BareMetalServer.ListByMainIP(ctx, "203.0.113.10") if err != nil { t.Errorf("BareMetalServer.ListByMainIP returned error: %v", err) } expected := []BareMetalServer{ { BareMetalServerID: "900000", Os: "CentOS 6 x64", RAM: "65536 MB", Disk: "2x 240 GB SSD", MainIP: "203.0.113.10", CPUs: 1, Location: "New Jersey", RegionID: 1, DefaultPassword: "ab81u!ryranq", DateCreated: "2017-04-12 18:45:41", Status: "active", NetmaskV4: "255.255.255.0", GatewayV4: "203.0.113.1", BareMetalPlanID: 28, V6Networks: []V6Network{ { Network: "2001:DB8:9000::", MainIP: "2001:DB8:9000::100", NetworkSize: "64", }, }, Label: "my label", Tag: "my tag", OsID: "127", AppID: "0", }, } if !reflect.DeepEqual(bm, expected) { t.Errorf("BareMetalServer.ListByMainIP returned %+v, expected %+v", bm, expected) } } func TestBareMetalServerServiceHandler_ListByTag(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "900000": { "SUBID": "900000", "os": "CentOS 6 x64", "ram": "65536 MB", "disk": "2x 240 GB SSD", "main_ip": "203.0.113.10", "cpu_count": 1, "location": "New Jersey", "DCID": "1", "default_password": "ab81u!ryranq", "date_created": "2017-04-12 18:45:41", "status": "active", "netmask_v4": "255.255.255.0", "gateway_v4": "203.0.113.1", "METALPLANID": 28, "v6_networks": [ { "v6_network": "2001:DB8:9000::", "v6_main_ip": "2001:DB8:9000::100", "v6_network_size": 64 } ], "label": "my label", "tag": "my tag", "OSID": "127", "APPID": "0" } } ` fmt.Fprint(writer, response) }) bm, err := client.BareMetalServer.ListByTag(ctx, "my tag") if err != nil { t.Errorf("BareMetalServer.ListByTag returned error: %v", err) } expected := []BareMetalServer{ { BareMetalServerID: "900000", Os: "CentOS 6 x64", RAM: "65536 MB", Disk: "2x 240 GB SSD", MainIP: "203.0.113.10", CPUs: 1, Location: "New Jersey", RegionID: 1, DefaultPassword: "ab81u!ryranq", DateCreated: "2017-04-12 18:45:41", Status: "active", NetmaskV4: "255.255.255.0", GatewayV4: "203.0.113.1", BareMetalPlanID: 28, V6Networks: []V6Network{ { Network: "2001:DB8:9000::", MainIP: "2001:DB8:9000::100", NetworkSize: "64", }, }, Label: "my label", Tag: "my tag", OsID: "127", AppID: "0", }, } if !reflect.DeepEqual(bm, expected) { t.Errorf("BareMetalServer.ListByTag returned %+v, expected %+v", bm, expected) } } func TestBareMetalServerServiceHandler_GetServer(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "SUBID": "900000", "os": "CentOS 6 x64", "ram": "65536 MB", "disk": "2x 240 GB SSD", "main_ip": "203.0.113.10", "cpu_count": 1, "location": "New Jersey", "DCID": "1", "default_password": "ab81u!ryranq", "date_created": "2017-04-12 18:45:41", "status": "active", "netmask_v4": "255.255.255.0", "gateway_v4": "203.0.113.1", "METALPLANID": 28, "v6_networks": [ { "v6_network": "2001:DB8:9000::", "v6_main_ip": "2001:DB8:9000::100", "v6_network_size": 64 } ], "label": "my label", "tag": "my tag", "OSID": "127", "APPID": "0" } ` fmt.Fprint(writer, response) }) bm, err := client.BareMetalServer.GetServer(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.GetServer returned error: %v", err) } expected := &BareMetalServer{ BareMetalServerID: "900000", Os: "CentOS 6 x64", RAM: "65536 MB", Disk: "2x 240 GB SSD", MainIP: "203.0.113.10", CPUs: 1, Location: "New Jersey", RegionID: 1, DefaultPassword: "ab81u!ryranq", DateCreated: "2017-04-12 18:45:41", Status: "active", NetmaskV4: "255.255.255.0", GatewayV4: "203.0.113.1", BareMetalPlanID: 28, V6Networks: []V6Network{ { Network: "2001:DB8:9000::", MainIP: "2001:DB8:9000::100", NetworkSize: "64", }, }, Label: "my label", Tag: "my tag", OsID: "127", AppID: "0", } if !reflect.DeepEqual(bm, expected) { t.Errorf("BareMetalServer.GetServer returned %+v, expected %+v", bm, expected) } } func TestBareMetalServerServiceHandler_GetUserData(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/get_user_data", func(writer http.ResponseWriter, request *http.Request) { response := `{"userdata": "ZWNobyBIZWxsbyBXb3JsZA=="}` fmt.Fprint(writer, response) }) userData, err := client.BareMetalServer.GetUserData(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.GetUserData return %+v ", err) } expected := &UserData{UserData: "ZWNobyBIZWxsbyBXb3JsZA=="} if !reflect.DeepEqual(userData, expected) { t.Errorf("BareMetalServer.GetUserData returned %+v, expected %+v", userData, expected) } } func TestBareMetalServerServiceHandler_Halt(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/halt", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.Halt(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.Halt returned %+v, expected %+v", err, nil) } } func TestBareMetalServerServiceHandler_IPV4Info(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/list_ipv4", func(writer http.ResponseWriter, request *http.Request) { response := ` { "900000": [ { "ip": "203.0.113.10", "netmask": "255.255.255.0", "gateway": "203.0.113.1", "type": "main_ip" } ] } ` fmt.Fprint(writer, response) }) ipv4, err := client.BareMetalServer.IPV4Info(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.IPV4Info returned %+v", err) } expected := []BareMetalServerIPV4{ { IP: "203.0.113.10", Netmask: "255.255.255.0", Gateway: "203.0.113.1", Type: "main_ip", }, } if !reflect.DeepEqual(ipv4, expected) { t.Errorf("BareMetalServer.IPV4Info returned %+v, expected %+v", ipv4, expected) } } func TestBareMetalServerServiceHandler_IPV6Info(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/list_ipv6", func(writer http.ResponseWriter, request *http.Request) { response := ` { "900000": [ { "ip": "2001:DB8:9000::100", "network": "2001:DB8:9000::", "network_size": 64, "type": "main_ip" } ] } ` fmt.Fprint(writer, response) }) ipv4, err := client.BareMetalServer.IPV6Info(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.IPV6Info returned %+v", err) } expected := []BareMetalServerIPV6{ { IP: "2001:DB8:9000::100", Network: "2001:DB8:9000::", NetworkSize: 64, Type: "main_ip", }, } if !reflect.DeepEqual(ipv4, expected) { t.Errorf("BareMetalServer.IPV6Info returned %+v, expected %+v", ipv4, expected) } } func TestBareMetalServerServiceHandler_ListApps(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/app_change_list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "1": { "APPID": "1", "name": "LEMP", "short_name": "lemp", "deploy_name": "LEMP on CentOS 6 x64", "surcharge": 0 } } ` fmt.Fprint(writer, response) }) application, err := client.BareMetalServer.ListApps(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.ListApps returned %+v, ", err) } expected := []Application{ { AppID: "1", Name: "LEMP", ShortName: "lemp", DeployName: "LEMP on CentOS 6 x64", Surcharge: 0, }, } if !reflect.DeepEqual(application, expected) { t.Errorf("BareMetalServer.ListApps returned %+v, expected %+v", application, expected) } } func TestBareMetalServerServiceHandler_ListOS(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/os_change_list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "127": { "OSID": "127", "name": "CentOS 6 x64", "arch": "x64", "family": "centos", "windows": false, "surcharge": 0 } } ` fmt.Fprint(writer, response) }) os, err := client.BareMetalServer.ListOS(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.ListOS return %+v ", err) } expected := []OS{ { OsID: 127, Name: "CentOS 6 x64", Arch: "x64", Family: "centos", Windows: false, }, } if !reflect.DeepEqual(os, expected) { t.Errorf("BareMetalServer.ListOS returned %+v, expected %+v", os, expected) } } func TestBareMetalServerServiceHandler_Reboot(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/reboot", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.Reboot(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.Reboot returned %+v, expected %+v", err, nil) } } func TestBareMetalServerServiceHandler_Reinstall(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/reinstall", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.Reinstall(ctx, "900000") if err != nil { t.Errorf("BareMetalServer.Reinstall returned %+v, expected %+v", err, nil) } } func TestBareMetalServerServiceHandler_SetLabel(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/label_set", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.SetLabel(ctx, "900000", "new label") if err != nil { t.Errorf("BareMetalServer.SetLabel returned %+v, expected %+v", err, nil) } } func TestBareMetalServerServiceHandler_SetTag(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/tag_set", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.SetTag(ctx, "900000", "new tag") if err != nil { t.Errorf("BareMetalServer.SetTag returned %+v, expected %+v", err, nil) } } func TestBareMetalServerServiceHandler_SetUserData(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/baremetal/set_user_data", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BareMetalServer.SetUserData(ctx, "900000", "user-test-data") if err != nil { t.Errorf("Server.SetUserData return %+v ", err) } } govultr-0.4.2/block_storage.go000066400000000000000000000163061366550666300164120ustar00rootroot00000000000000package 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, liveAttach 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, liveDetach 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 // If liveAttach is set to "yes" the block storage will be attached without reloading the instance func (b *BlockStorageServiceHandler) Attach(ctx context.Context, blockID, InstanceID, liveAttach string) error { uri := "/v1/block/attach" values := url.Values{ "SUBID": {blockID}, "attach_to_SUBID": {InstanceID}, } if liveAttach == "yes" { values.Add("live", "yes") } 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 // If liveDetach is set to "yes" the block storage will be detached without reloading the instance func (b *BlockStorageServiceHandler) Detach(ctx context.Context, blockID, liveDetach string) error { uri := "/v1/block/detach" values := url.Values{ "SUBID": {blockID}, } if liveDetach == "yes" { values.Add("live", "yes") } 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 } govultr-0.4.2/block_storage_test.go000066400000000000000000000114141366550666300174440ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestBlockStorageServiceHandler_Attach(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/block/attach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BlockStorage.Attach(ctx, "123456", "876521", "yes") if err != nil { t.Errorf("BlockStorage.Attach returned %+v, expected %+v", err, nil) } } func TestBlockStorageServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/block/create", func(writer http.ResponseWriter, request *http.Request) { response := `{"SUBID": "1234566"}` fmt.Fprint(writer, response) }) blockStorage, err := client.BlockStorage.Create(ctx, 1, 10, "unit-test") if err != nil { t.Errorf("BlockStorage.Create returned error: %v", err) } expected := &BlockStorage{BlockStorageID: "1234566", RegionID: 1, SizeGB: 10, Label: "unit-test"} if !reflect.DeepEqual(blockStorage, expected) { t.Errorf("BlockStorage.Create returned %+v, expected %+v", blockStorage, expected) } } func TestBlockStorageServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/block/delete", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BlockStorage.Delete(ctx, "123456") if err != nil { t.Errorf("BlockStorage.Delete returned %+v, expected %+v", err, nil) } } func TestBlockStorageServiceHandler_Detach(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/block/detach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BlockStorage.Detach(ctx, "123456", "yes") if err != nil { t.Errorf("BlockStorage.Detach returned %+v, expected %+v", err, nil) } } func TestBlockStorageServiceHandler_SetLabel(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/block/label_set", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BlockStorage.SetLabel(ctx, "123456", "unit-test-label-setter") if err != nil { t.Errorf("BlockStorage.SetLabel returned %+v, expected %+v", err, nil) } } func TestBlockStorageServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/block/list", func(writer http.ResponseWriter, request *http.Request) { response := `[ { "SUBID": 1313216, "date_created": "2016-03-29 10:10:04", "cost_per_month": 10, "status": "pending", "size_gb": 100, "DCID": 1, "attached_to_SUBID": "100", "label": "files1" }, { "SUBID": 1313216, "date_created": "2016-03-29 10:10:04", "cost_per_month": 10, "status": "pending", "size_gb": 100, "DCID": 1, "attached_to_SUBID": null, "label": "files1" } ] ` fmt.Fprint(writer, response) }) blockStorage, err := client.BlockStorage.List(ctx) if err != nil { t.Errorf("BlockStorage.Get returned error: %v", err) } expected := []BlockStorage{ {BlockStorageID: "1313216", DateCreated: "2016-03-29 10:10:04", CostPerMonth: "10", Status: "pending", SizeGB: 100, RegionID: 1, InstanceID: "100", Label: "files1"}, {BlockStorageID: "1313216", DateCreated: "2016-03-29 10:10:04", CostPerMonth: "10", Status: "pending", SizeGB: 100, RegionID: 1, InstanceID: "", Label: "files1"}, } if !reflect.DeepEqual(blockStorage, expected) { t.Errorf("BlockStorage.Get returned %+v, expected %+v", blockStorage, expected) } } func TestBlockStorageServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/block/list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "SUBID": 1313216, "date_created": "2016-03-29 10:10:04", "cost_per_month": 10, "status": "pending", "size_gb": 100, "DCID": 1, "attached_to_SUBID": null, "label": "files1" } ` fmt.Fprint(writer, response) }) blockStorage, err := client.BlockStorage.Get(ctx, "1313216") if err != nil { t.Errorf("BlockStorage.Get returned error: %v", err) } expected := &BlockStorage{BlockStorageID: "1313216", DateCreated: "2016-03-29 10:10:04", CostPerMonth: "10", Status: "pending", SizeGB: 100, RegionID: 1, InstanceID: "", Label: "files1"} if !reflect.DeepEqual(blockStorage, expected) { t.Errorf("BlockStorage.Get returned %+v, expected %+v", blockStorage, expected) } } func TestBlockStorageServiceHandler_Resize(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/block/resize", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.BlockStorage.Resize(ctx, "123456", 50) if err != nil { t.Errorf("BlockStorage.Resize returned %+v, expected %+v", err, nil) } } govultr-0.4.2/dns_domains.go000066400000000000000000000105361366550666300160710ustar00rootroot00000000000000package 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 } govultr-0.4.2/dns_domains_test.go000066400000000000000000000100541366550666300171230ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestDNSDomainServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/dns/create_domain", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.DNSDomain.Create(ctx, "domain.com", "123456") if err != nil { t.Errorf("DNSDomain.Create returned %+v, expected %+v", err, nil) } } func TestDNSDomainServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/dns/delete_domain", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.DNSDomain.Delete(ctx, "domain.com") if err != nil { t.Errorf("DNSDomain.Delete returned %+v, expected %+v", err, nil) } } func TestDNSDomainServiceHandler_ToggleDNSSec(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/dns/dnssec_enable", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.DNSDomain.ToggleDNSSec(ctx, "domain.com", true) if err != nil { t.Errorf("DNSDomain.ToggleDNSSec returned %+v, expected %+v", err, nil) } } func TestDNSDomainServiceHandler_DNSSecInfo(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/dns/dnssec_info", func(writer http.ResponseWriter, request *http.Request) { response := ` [ "example.com IN DNSKEY 257 3 13 kRrxANp7YTGqVbaWtMy8hhsK0jcG4ajjICZKMb4fKv79Vx/RSn76vNjzIT7/Uo0BXil01Fk8RRQc4nWZctGJBA==", "example.com IN DS 27933 13 1 2d9ac457e5c11a104e25d971d0a6254562bddde7", "example.com IN DS 27933 13 2 8858e7b0dfb881280ce2ca1e0eafcd93d5b53687c21da284d4f8799ba82208a9" ] ` fmt.Fprint(writer, response) }) dnsSec, err := client.DNSDomain.DNSSecInfo(ctx, "example.com") if err != nil { t.Errorf("DNSDomain.DNSSecInfo returned %+v, expected %+v", err, nil) } expected := []string{ "example.com IN DNSKEY 257 3 13 kRrxANp7YTGqVbaWtMy8hhsK0jcG4ajjICZKMb4fKv79Vx/RSn76vNjzIT7/Uo0BXil01Fk8RRQc4nWZctGJBA==", "example.com IN DS 27933 13 1 2d9ac457e5c11a104e25d971d0a6254562bddde7", "example.com IN DS 27933 13 2 8858e7b0dfb881280ce2ca1e0eafcd93d5b53687c21da284d4f8799ba82208a9", } if !reflect.DeepEqual(dnsSec, expected) { t.Errorf("DNSDomain.DNSSecInfo returned %+v, expected %+v", dnsSec, expected) } } func TestDNSDomainServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/dns/list", func(writer http.ResponseWriter, request *http.Request) { response := `[{"domain":"example.com","date_created":"2014-12-11 16:20:59"},{"domain":"example.com","date_created":"2014-12-11 16:20:59"}]` fmt.Fprint(writer, response) }) domainList, err := client.DNSDomain.List(ctx) if err != nil { t.Errorf("DNSDomain.List returned %+v, expected %+v", err, nil) } expected := []DNSDomain{ {Domain: "example.com", DateCreated: "2014-12-11 16:20:59"}, {Domain: "example.com", DateCreated: "2014-12-11 16:20:59"}, } if !reflect.DeepEqual(domainList, expected) { t.Errorf("DNSDomain.List returned %+v, expected %+v", domainList, expected) } } func TestDNSDomainServiceHandler_GetSoa(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/dns/soa_info", func(writer http.ResponseWriter, request *http.Request) { response := `{ "nsprimary": "ns1.vultr.com", "email": "dnsadm@vultr.com" }` fmt.Fprint(writer, response) }) soa, err := client.DNSDomain.GetSoa(ctx, "example.com") if err != nil { t.Errorf("DNSDomain.GetSoa returned %+v, expected %+v", err, nil) } expected := &Soa{NsPrimary: "ns1.vultr.com", Email: "dnsadm@vultr.com"} if !reflect.DeepEqual(soa, expected) { t.Errorf("DNSDomain.GetSoa returned %+v, expected %+v", soa, expected) } } func TestDNSDomainServiceHandler_UpdateSoa(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/dns/soa_update", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.DNSDomain.UpdateSoa(ctx, "domain.com", "ns1.domain.com", "example@vultr.com") if err != nil { t.Errorf("DNSDomain.UpdateSoa returned %+v, expected %+v", err, nil) } } govultr-0.4.2/dns_records.go000066400000000000000000000064211366550666300160760ustar00rootroot00000000000000package 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 != nil { 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 } govultr-0.4.2/dns_records_test.go000066400000000000000000000042511366550666300171340ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestDNSRecordsServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/dns/create_record", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.DNSRecord.Create(ctx, "domain.com", "A", "api", "127.0.0.1", 300, 0) if err != nil { t.Errorf("DNSRecord.Create returned %+v, expected %+v", err, nil) } } func TestDNSRecordsServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/dns/delete_record", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.DNSRecord.Delete(ctx, "domain.com", "12345678") if err != nil { t.Errorf("DNSRecord.Delete returned %+v, expected %+v", err, nil) } } func TestDNSRecordsServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/dns/records", func(writer http.ResponseWriter, request *http.Request) { response := `[{"type": "A","name": "", "data": "127.0.0.1","priority": 0,"RECORDID": 1265276,"ttl": 300},{"type": "A","name": "", "data": "127.0.0.1","priority": 0,"RECORDID": 1265276,"ttl": 300}]` fmt.Fprint(writer, response) }) records, err := client.DNSRecord.List(ctx, "domain.com") if err != nil { t.Errorf("DNSRecord.List returned %+v, expected %+v", err, nil) } p := 0 expected := []DNSRecord{ {Type: "A", Name: "", Data: "127.0.0.1", Priority: &p, RecordID: 1265276, TTL: 300}, {Type: "A", Name: "", Data: "127.0.0.1", Priority: &p, RecordID: 1265276, TTL: 300}, } if !reflect.DeepEqual(records, expected) { t.Errorf("DNSRecord.List returned %+v, expected %+v", records, expected) } } func TestDNSRecordsServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/dns/update_record", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) p := 10 params := &DNSRecord{ RecordID: 14283638, Name: "api", Data: "turnip.data", TTL: 120, Priority: &p, } err := client.DNSRecord.Update(ctx, "turnip.services", params) if err != nil { t.Errorf("DNSRecord.Update returned %+v, expected %+v", err, nil) } } govultr-0.4.2/firewall_group.go000066400000000000000000000075401366550666300166150ustar00rootroot00000000000000package 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 } govultr-0.4.2/firewall_group_test.go000066400000000000000000000066141366550666300176550ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestFireWallGroupServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/firewall/group_create", func(writer http.ResponseWriter, request *http.Request) { response := `{"FIREWALLGROUPID":"1234abcd"}` fmt.Fprint(writer, response) }) firewallGroup, err := client.FirewallGroup.Create(ctx, "firewall-group-name") if err != nil { t.Errorf("FirewallGroup.Create returned error: %v", err) } expected := &FirewallGroup{FirewallGroupID: "1234abcd"} if !reflect.DeepEqual(firewallGroup, expected) { t.Errorf("FirewallGroup.Create returned %+v, expected %+v", firewallGroup, expected) } } func TestFireWallGroupServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/firewall/group_delete", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.FirewallGroup.Delete(ctx, "12345abcd") if err != nil { t.Errorf("FirewallGroup.Delete returned error: %v", err) } } func TestFireWallGroupServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/firewall/group_list", func(writer http.ResponseWriter, request *http.Request) { response := `{"1234abcd": { "FIREWALLGROUPID": "1234abcd", "description": "my http firewall","date_created": "2017-02-14 17:48:40","date_modified": "2017-02-14 17:48:40","instance_count": 2,"rule_count": 2, "max_rule_count": 50}}` fmt.Fprint(writer, response) }) firewallGroup, err := client.FirewallGroup.List(ctx) if err != nil { t.Errorf("FirewallGroup.List returned error: %v", err) } expected := []FirewallGroup{ { FirewallGroupID: "1234abcd", Description: "my http firewall", DateCreated: "2017-02-14 17:48:40", DateModified: "2017-02-14 17:48:40", InstanceCount: 2, RuleCount: 2, MaxRuleCount: 50, }, } if !reflect.DeepEqual(firewallGroup, expected) { t.Errorf("FirewallGroup.List returned %+v, expected %+v", firewallGroup, expected) } } func TestFireWallGroupServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/firewall/group_list", func(writer http.ResponseWriter, request *http.Request) { response := `{"1234abcd": { "FIREWALLGROUPID": "1234abcd", "description": "my http firewall","date_created": "2017-02-14 17:48:40","date_modified": "2017-02-14 17:48:40","instance_count": 2,"rule_count": 2, "max_rule_count": 50}}` fmt.Fprint(writer, response) }) firewallGroup, err := client.FirewallGroup.Get(ctx, "1234abcd") if err != nil { t.Errorf("FirewallGroup.Get returned error: %v", err) } expected := &FirewallGroup{ FirewallGroupID: "1234abcd", Description: "my http firewall", DateCreated: "2017-02-14 17:48:40", DateModified: "2017-02-14 17:48:40", InstanceCount: 2, RuleCount: 2, MaxRuleCount: 50, } if !reflect.DeepEqual(firewallGroup, expected) { t.Errorf("FirewallGroup.Get returned %+v, expected %+v", firewallGroup, expected) } } func TestFireWallGroupServiceHandler_ChangeDescription(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/firewall/group_set_description", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.FirewallGroup.ChangeDescription(ctx, "12345abcd", "new description") if err != nil { t.Errorf("FirewallGroup.ChangeDescription returned error: %v", err) } } govultr-0.4.2/firewall_rule.go000066400000000000000000000135611366550666300164300ustar00rootroot00000000000000package 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 } govultr-0.4.2/firewall_rule_test.go000066400000000000000000000066111366550666300174650ustar00rootroot00000000000000package govultr import ( "fmt" "net" "net/http" "reflect" "testing" ) func TestFireWallRuleServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/firewall/rule_create", func(writer http.ResponseWriter, request *http.Request) { response := `{"rulenumber": 2}` fmt.Fprint(writer, response) }) firewallRule, err := client.FirewallRule.Create(ctx, "123456", "tcp", "8080", "10.0.0.0/32", "note") if err != nil { t.Errorf("FirewallRule.Create returned error: %v", err) } expected := &FirewallRule{RuleNumber: 2} if !reflect.DeepEqual(firewallRule, expected) { t.Errorf("FirewallRule.Create returned %+v, expected %+v", firewallRule, expected) } firewallRule, err = client.FirewallRule.Create(ctx, "123456", "tcp", "8080", "::/0", "note") if err != nil { t.Errorf("FirewallRule.Create returned error: %v", err) } expected = &FirewallRule{RuleNumber: 2} if !reflect.DeepEqual(firewallRule, expected) { t.Errorf("FirewallRule.Create returned %+v, expected %+v", firewallRule, expected) } } func TestFireWallRuleServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/firewall/rule_delete", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.FirewallRule.Delete(ctx, "123456", "123") if err != nil { t.Errorf("FirewallRule.Delete returned error: %v", err) } } func TestFireWallRuleServiceHandler_GetAll(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/firewall/rule_list", func(writer http.ResponseWriter, request *http.Request) { response := `{ "1": {"rulenumber": 1,"action": "accept","protocol": "icmp","port": "","subnet": "","subnet_size": 0,"notes": ""}}` fmt.Fprint(writer, response) }) firewallRule, err := client.FirewallRule.ListByIPType(ctx, "12345", "v4") if err != nil { t.Errorf("FirewallRule.ListByIPType returned error: %v", err) } expected := []FirewallRule{ { RuleNumber: 1, Action: "accept", Protocol: "icmp", Network: nil, }, } if !reflect.DeepEqual(firewallRule, expected) { t.Errorf("FirewallRule.ListByIPType returned %+v, expected %+v", firewallRule, expected) } firewallRule, err = client.FirewallRule.ListByIPType(ctx, "12345", "v6") if err != nil { t.Errorf("FirewallRule.ListByIPType returned error: %v", err) } expected = []FirewallRule{ { RuleNumber: 1, Action: "accept", Protocol: "icmp", Network: nil, }, } if !reflect.DeepEqual(firewallRule, expected) { t.Errorf("FirewallRule.ListByIPType returned %+v, expected %+v", firewallRule, expected) } } func TestFireWallRuleServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/firewall/rule_list", func(writer http.ResponseWriter, request *http.Request) { response := `{ "1": {"rulenumber": 1,"action": "accept","protocol": "icmp","port": "8080","subnet": "10.0.0.0","subnet_size": 32,"notes": ""}}` fmt.Fprint(writer, response) }) firewallRule, err := client.FirewallRule.List(ctx, "12345") if err != nil { t.Errorf("FirewallRule.List returned error: %v", err) } _, ip, _ := net.ParseCIDR("10.0.0.0/32") expected := []FirewallRule{ { RuleNumber: 1, Action: "accept", Protocol: "icmp", Port: "8080", Network: ip, }, } if !reflect.DeepEqual(firewallRule, expected) { t.Errorf("FirewallRule.List returned %+v, expected %+v", firewallRule, expected) } } govultr-0.4.2/go.mod000066400000000000000000000001371366550666300143460ustar00rootroot00000000000000module github.com/vultr/govultr go 1.12 require github.com/hashicorp/go-retryablehttp v0.6.6 govultr-0.4.2/go.sum000066400000000000000000000026661366550666300144040ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-retryablehttp v0.6.3 h1:tuulM+WnToeqa05z83YLmKabZxrySOmJAd4mJ+s2Nfg= github.com/hashicorp/go-retryablehttp v0.6.3/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY= github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= govultr-0.4.2/govultr.go000066400000000000000000000161511366550666300152740ustar00rootroot00000000000000package govultr import ( "context" "encoding/json" "errors" "fmt" "io" "io/ioutil" "net/http" "net/url" "strings" "time" "github.com/hashicorp/go-retryablehttp" ) const ( version = "0.4.2" defaultBase = "https://api.vultr.com" userAgent = "govultr/" + version rateLimit = 600 * time.Millisecond retryLimit = 3 ) // whiteListURI is an array of endpoints that should not have the API Key passed to them var whiteListURI = [13]string{ "/v1/regions/availability", "/v1/app/list", "/v1/objectstorage/list_cluster", "/v1/os/list", "/v1/plans/list", "/v1/plans/list_baremetal", "/v1/plans/list_vc2", "/v1/plans/list_vc2z", "/v1/plans/list_vdc2", "/v1/regions/list", "/v1/regions/availability_vc2", "/v1/regions/availability_vdc2", "/v1/regions/availability_baremetal", } // 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 *retryablehttp.Client // BASE URL for APIs BaseURL *url.URL // User Agent for the client UserAgent string // API Key APIKey APIKey // 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 LoadBalancer LoadBalancerService Network NetworkService ObjectStorage ObjectStorageService 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: retryablehttp.NewClient(), BaseURL: baseURL, UserAgent: userAgent, } client.client.HTTPClient = httpClient client.client.Logger = nil client.client.ErrorHandler = client.vultrErrorHandler client.SetRetryLimit(retryLimit) client.SetRateLimit(rateLimit) client.Account = &AccountServiceHandler{client} client.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.LoadBalancer = &LoadBalancerHandler{client} client.Network = &NetworkServiceHandler{client} client.ObjectStorage = &ObjectStorageServiceHandler{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) for _, v := range whiteListURI { if v == uri { req.Header.Del("API-key") break } } 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 { rreq, err := retryablehttp.FromRequest(r) if err != nil { return err } rreq = rreq.WithContext(ctx) res, err := c.client.Do(rreq) if c.onRequestCompleted != nil { c.onRequestCompleted(r, res) } if err != nil { return err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { return err } if res.StatusCode == http.StatusOK { 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. For performance, exponential // backoff is used with the minimum wait being 2/3rds the time provided. func (c *Client) SetRateLimit(time time.Duration) { c.client.RetryWaitMin = time / 3 * 2 c.client.RetryWaitMax = time } // SetUserAgent Overrides the default UserAgent func (c *Client) SetUserAgent(ua string) { c.UserAgent = ua } // OnRequestCompleted sets the API request completion callback func (c *Client) OnRequestCompleted(rc RequestCompletionCallback) { c.onRequestCompleted = rc } // SetRetryLimit overrides the default RetryLimit func (c *Client) SetRetryLimit(n int) { c.client.RetryMax = n } func (c *Client) vultrErrorHandler(resp *http.Response, err error, numTries int) (*http.Response, error) { if resp == nil { return nil, fmt.Errorf("gave up after %d attempts, last error unavailable (resp == nil)", c.client.RetryMax+1) } buf, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("gave up after %d attempts, last error unavailable (error reading response body: %v)", c.client.RetryMax+1, err) } return nil, fmt.Errorf("gave up after %d attempts, last error: %#v", c.client.RetryMax+1, strings.TrimSpace(string(buf))) } govultr-0.4.2/govultr_test.go000066400000000000000000000142611366550666300163330ustar00rootroot00000000000000package govultr import ( "context" "errors" "fmt" "io/ioutil" "net/http" "net/http/httptest" "net/http/httputil" "net/url" "reflect" "strings" "testing" "time" ) var ( mux *http.ServeMux ctx = context.TODO() client *Client server *httptest.Server ) func setup() { mux = http.NewServeMux() server = httptest.NewServer(mux) client = NewClient(nil, "dummy-key") url, _ := url.Parse(server.URL) client.BaseURL = url } func teardown() { server.Close() } func TestNewClient(t *testing.T) { setup() defer teardown() if client.BaseURL == nil || client.BaseURL.String() != server.URL { t.Errorf("NewClient BaseURL = %v, expected %v", client.BaseURL, server.URL) } if client.APIKey.key != "dummy-key" { t.Errorf("NewClient ApiKey = %v, expected %v", client.APIKey.key, "dummy-key") } if client.UserAgent != userAgent { t.Errorf("NewClient UserAgent = %v, expected %v", client.UserAgent, userAgent) } services := []string{ "Account", } copied := reflect.ValueOf(client) copyValue := reflect.Indirect(copied) for _, s := range services { if copyValue.FieldByName(s).IsNil() { t.Errorf("c.%s shouldn't be nil", s) } } } func TestClient_DoWithContext(t *testing.T) { setup() defer teardown() type vultr struct { Bird string } mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { if method := http.MethodGet; method != request.Method { t.Errorf("Request method = %v, expecting %v", request.Method, method) } fmt.Fprint(writer, `{"Bird":"vultr"}`) }) req, _ := client.NewRequest(ctx, http.MethodGet, "/", nil) data := new(vultr) err := client.DoWithContext(context.Background(), req, data) if err != nil { t.Fatalf("DoWithContext(): %v", err) } expected := &vultr{"vultr"} if !reflect.DeepEqual(data, expected) { t.Errorf("Response body = %v, expected %v", data, expected) } } func TestClient_DoWithContextFailure(t *testing.T) { setup() defer teardown() mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { if method := http.MethodGet; method != request.Method { t.Errorf("Request method = %v, expecting %v", request.Method, method) } writer.WriteHeader(500) fmt.Fprint(writer, `{Error}`) }) req, _ := client.NewRequest(ctx, http.MethodGet, "/", nil) err := client.DoWithContext(context.Background(), req, nil) if !strings.Contains(err.Error(), "gave up after") || !strings.Contains(err.Error(), "last error") { t.Fatalf("DoWithContext(): %v: expected 'gave up after ..., last error ...'", err) } } type errRoundTripper struct{} func (errRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, errors.New("fake error") } func TestClient_DoWithContextError(t *testing.T) { setup() defer teardown() client = NewClient(&http.Client{ Transport: errRoundTripper{}, }, "dummy-key") req, _ := client.NewRequest(ctx, http.MethodGet, "/", nil) var panicked string func() { defer func() { if err := recover(); err != nil { panicked = fmt.Sprint(err) } }() client.DoWithContext(context.Background(), req, nil) }() if panicked != "" { t.Errorf("unexpected panic: %s", panicked) } } func TestClient_NewRequest(t *testing.T) { c := NewClient(nil, "dum-dum") in := "/unit" out := defaultBase + "/unit" inRequest := url.Values{ "balance": {"500"}, } outRequest := `balance=500` req, _ := c.NewRequest(ctx, http.MethodPost, in, inRequest) if req.URL.String() != out { t.Errorf("NewRequest(%v) URL = %v, expected %v", in, req.URL, out) } body, _ := ioutil.ReadAll(req.Body) if string(body) != outRequest { t.Errorf("NewRequest(%v)Body = %v, expected %v", inRequest, string(body), outRequest) } userAgent := req.Header.Get("User-Agent") if c.UserAgent != userAgent { t.Errorf("NewRequest() User-Agent = %v, expected %v", userAgent, c.UserAgent) } if c.APIKey.key != "dum-dum" { t.Errorf("NewRequest() API-Key = %v, expected %v", c.APIKey.key, "dum-dum") } contentType := req.Header.Get("Content-Type") if contentType != "application/x-www-form-urlencoded" { t.Errorf("NewRequest() Header Content Type = %v, expected %v", contentType, "application/x-www-form-urlencoded") } } func TestClient_SetBaseUrl(t *testing.T) { setup() defer teardown() base := "http://localhost/vultr" err := client.SetBaseURL(base) if err != nil { t.Fatalf("SetBaseUrl unexpected error: %v", err) } if client.BaseURL.String() != base { t.Errorf("NewClient BaseUrl = %v, expected %v", client.BaseURL, base) } } func TestClient_SetUserAgent(t *testing.T) { setup() defer teardown() ua := "vultr/testing" client.SetUserAgent(ua) if client.UserAgent != ua { t.Errorf("NewClient UserAgent = %v, expected %v", client.UserAgent, userAgent) } } func TestClient_SetRateLimit(t *testing.T) { setup() defer teardown() time := 600 * time.Millisecond client.SetRateLimit(time) if client.client.RetryWaitMax != time { t.Errorf("NewClient max RateLimit = %v, expected %v", client.client.RetryWaitMax, time) } } func TestClient_OnRequestCompleted(t *testing.T) { setup() defer teardown() var completedReq *http.Request var completedRes string type Vultr struct { Bird string } mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer, `{"Vultr":"bird"}`) }) req, _ := client.NewRequest(ctx, http.MethodGet, "/", nil) data := new(Vultr) client.OnRequestCompleted(func(request *http.Request, response *http.Response) { completedReq = req dump, err := httputil.DumpResponse(response, true) if err != nil { t.Errorf("Failed to dump response: %s", err) } completedRes = string(dump) }) err := client.DoWithContext(context.Background(), req, data) if err != nil { t.Fatalf("Do(): %v", err) } if !reflect.DeepEqual(req, completedReq) { t.Errorf("Completed request = %v, expected %v", completedReq, req) } expected := `{"Vultr":"bird"}` if !strings.Contains(completedRes, expected) { t.Errorf("expected response to contain %v, Response = %v", expected, completedRes) } } func TestClient_SetRetryLimit(t *testing.T) { setup() defer teardown() client.SetRetryLimit(4) if client.client.RetryMax != 4 { t.Errorf("NewClient RateLimit = %v, expected %v", client.client.RetryMax, 4) } } govultr-0.4.2/iso.go000066400000000000000000000056751366550666300143750ustar00rootroot00000000000000package 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 } govultr-0.4.2/iso_test.go000066400000000000000000000046561366550666300154320ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestIsoServiceHandler_CreateFromURL(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/iso/create_from_url", func(writer http.ResponseWriter, request *http.Request) { response := `{"ISOID": 24}` fmt.Fprint(writer, response) }) iso, err := client.ISO.CreateFromURL(ctx, "domain.com/coolest-iso-ever.iso") if err != nil { t.Errorf("Iso.CreateFromURL returned %+v, expected %+v", err, nil) } expected := &ISO{ISOID: 24} if !reflect.DeepEqual(iso, expected) { t.Errorf("Iso.CreateFromURL returned %+v, expected %+v", iso, expected) } } func TestIsoServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/iso/destroy", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.ISO.Delete(ctx, 24) if err != nil { t.Errorf("Iso.Delete returned %+v, expected %+v", err, nil) } } func TestIsoServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/iso/list", func(writer http.ResponseWriter, request *http.Request) { response := `{ "24": { "ISOID": 24,"date_created": "2014-04-01 14:10:09","filename": "CentOS-6.5-x86_64-minimal.iso","size": 9342976,"md5sum": "ec066","sha512sum": "1741f890bce04613f60b0","status": "complete"}}` fmt.Fprint(writer, response) }) iso, err := client.ISO.List(ctx) if err != nil { t.Errorf("Iso.List returned %+v, expected %+v", err, nil) } expected := []ISO{ {ISOID: 24, DateCreated: "2014-04-01 14:10:09", FileName: "CentOS-6.5-x86_64-minimal.iso", Size: 9342976, MD5Sum: "ec066", SHA512Sum: "1741f890bce04613f60b0", Status: "complete"}, } if !reflect.DeepEqual(iso, expected) { t.Errorf("Iso.List returned %+v, expected %+v", iso, expected) } } func TestIsoServiceHandler_GetPublicList(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/iso/list_public", func(writer http.ResponseWriter, request *http.Request) { response := `{"204515": {"ISOID": 204515,"name": "CentOS 7","description": "7 x86_64 Minimal"}}` fmt.Fprint(writer, response) }) iso, err := client.ISO.GetPublicList(ctx) if err != nil { t.Errorf("Iso.GetPublicList returned %+v, expected %+v", err, nil) } expected := []PublicISO{ {ISOID: 204515, Name: "CentOS 7", Description: "7 x86_64 Minimal"}, } if !reflect.DeepEqual(iso, expected) { t.Errorf("Iso.GetPublicList returned %+v, expected %+v", iso, expected) } } govultr-0.4.2/load_balancer.go000066400000000000000000000411471366550666300163430ustar00rootroot00000000000000package govultr import ( "context" "encoding/json" "net/http" "net/url" "strconv" ) // LoadBalancerService is the interface to interact with the server endpoints on the Vultr API // Link: https://www.vultr.com/api/#loadbalancer type LoadBalancerService interface { List(ctx context.Context) ([]LoadBalancers, error) Delete(ctx context.Context, ID int) error SetLabel(ctx context.Context, ID int, label string) error AttachedInstances(ctx context.Context, ID int) (*InstanceList, error) AttachInstance(ctx context.Context, ID, backendNode int) error DetachInstance(ctx context.Context, ID, backendNode int) error GetHealthCheck(ctx context.Context, ID int) (*HealthCheck, error) SetHealthCheck(ctx context.Context, ID int, healthConfig *HealthCheck) error GetGenericInfo(ctx context.Context, ID int) (*GenericInfo, error) ListForwardingRules(ctx context.Context, ID int) (*ForwardingRules, error) DeleteForwardingRule(ctx context.Context, ID int, RuleID string) error CreateForwardingRule(ctx context.Context, ID int, rule *ForwardingRule) (*ForwardingRule, error) GetFullConfig(ctx context.Context, ID int) (*LBConfig, error) HasSSL(ctx context.Context, ID int) (*struct { SSLInfo bool `json:"has_ssl"` }, error) Create(ctx context.Context, region int, label string, genericInfo *GenericInfo, healthCheck *HealthCheck, rules []ForwardingRule, ssl *SSL, instances *InstanceList) (*LoadBalancers, error) UpdateGenericInfo(ctx context.Context, ID int, label string, genericInfo *GenericInfo) error AddSSL(ctx context.Context, ID int, ssl *SSL) error RemoveSSL(ctx context.Context, ID int) error } // LoadBalancerHandler handles interaction with the server methods for the Vultr API type LoadBalancerHandler struct { client *Client } // LoadBalancers represent a basic structure of a load balancer type LoadBalancers struct { ID int `json:"SUBID,omitempty"` DateCreated string `json:"date_created,omitempty"` RegionID int `json:"DCID,omitempty"` Location string `json:"location,omitempty"` Label string `json:"label,omitempty"` Status string `json:"status,omitempty"` IPV4 string `json:"ipv4,omitempty"` IPV6 string `json:"ipv6,omitempty"` } // InstanceList represents instances that attached to your load balancer type InstanceList struct { InstanceList []int `json:"instance_list"` } // HealthCheck represents your health check configuration for your load balancer. type HealthCheck struct { Protocol string `json:"protocol,omitempty"` Port int `json:"port,omitempty"` Path string `json:"path,omitempty"` CheckInterval int `json:"check_interval,omitempty"` ResponseTimeout int `json:"response_timeout,omitempty"` UnhealthyThreshold int `json:"unhealthy_threshold,omitempty"` HealthyThreshold int `json:"healthy_threshold,omitempty"` } // GenericInfo represents generic configuration of your load balancer type GenericInfo struct { BalancingAlgorithm string `json:"balancing_algorithm"` SSLRedirect *bool `json:"ssl_redirect,omitempty"` StickySessions *StickySessions `json:"sticky_sessions"` ProxyProtocol *bool `json:"proxy_protocol"` } // CookieName represents cookie for your load balancer type StickySessions struct { StickySessionsEnabled string `json:"sticky_sessions"` CookieName string `json:"cookie_name"` } // ForwardingRules represent a list of forwarding rules type ForwardingRules struct { ForwardRuleList []ForwardingRule `json:"forward_rule_list"` } // ForwardingRule represent a single forwarding rule type ForwardingRule struct { RuleID string `json:"RULEID,omitempty"` FrontendProtocol string `json:"frontend_protocol,omitempty"` FrontendPort int `json:"frontend_port,omitempty"` BackendProtocol string `json:"backend_protocol,omitempty"` BackendPort int `json:"backend_port,omitempty"` } // LBConfig represents the full config with all components of a load balancer type LBConfig struct { GenericInfo `json:"generic_info"` HealthCheck `json:"health_checks_info"` SSLInfo bool `json:"has_ssl"` ForwardingRules InstanceList } // SSL represents valid SSL config type SSL struct { PrivateKey string `json:"ssl_private_key"` Certificate string `json:"ssl_certificate"` Chain string `json:"chain,omitempty"` } // List all load balancer subscriptions on the current account. func (l *LoadBalancerHandler) List(ctx context.Context) ([]LoadBalancers, error) { uri := "/v1/loadbalancer/list" req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } var lbList []LoadBalancers err = l.client.DoWithContext(ctx, req, &lbList) if err != nil { return nil, err } return lbList, nil } // Delete a load balancer subscription. func (l *LoadBalancerHandler) Delete(ctx context.Context, ID int) error { uri := "/v1/loadbalancer/destroy" values := url.Values{ "SUBID": {strconv.Itoa(ID)}, } req, err := l.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return err } err = l.client.DoWithContext(ctx, req, nil) if err != nil { return err } return nil } // SetLabel sets the label for your load balancer subscription. func (l *LoadBalancerHandler) SetLabel(ctx context.Context, ID int, label string) error { uri := "/v1/loadbalancer/label_set" values := url.Values{ "SUBID": {strconv.Itoa(ID)}, "label": {label}, } req, err := l.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return err } err = l.client.DoWithContext(ctx, req, nil) if err != nil { return nil } return nil } // AttachedInstances lists the instances that are currently attached to a load balancer subscription. func (l *LoadBalancerHandler) AttachedInstances(ctx context.Context, ID int) (*InstanceList, error) { uri := "/v1/loadbalancer/instance_list" req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } q := req.URL.Query() q.Add("SUBID", strconv.Itoa(ID)) req.URL.RawQuery = q.Encode() var instances InstanceList err = l.client.DoWithContext(ctx, req, &instances) if err != nil { return nil, err } return &instances, nil } // AttachInstance attaches a backend node to your load balancer subscription func (l *LoadBalancerHandler) AttachInstance(ctx context.Context, ID, backendNode int) error { uri := "/v1/loadbalancer/instance_attach" values := url.Values{ "SUBID": {strconv.Itoa(ID)}, "backendNode": {strconv.Itoa(backendNode)}, } req, err := l.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return err } err = l.client.DoWithContext(ctx, req, nil) if err != nil { return err } return nil } // DetachInstance detaches a backend node to your load balancer subscription func (l *LoadBalancerHandler) DetachInstance(ctx context.Context, ID, backendNode int) error { uri := "/v1/loadbalancer/instance_detach" values := url.Values{ "SUBID": {strconv.Itoa(ID)}, "backendNode": {strconv.Itoa(backendNode)}, } req, err := l.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return err } err = l.client.DoWithContext(ctx, req, nil) if err != nil { return err } return nil } // GetHealthCheck retrieves the health check configuration for your load balancer subscription. func (l *LoadBalancerHandler) GetHealthCheck(ctx context.Context, ID int) (*HealthCheck, error) { uri := "/v1/loadbalancer/health_check_info" req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } q := req.URL.Query() q.Add("SUBID", strconv.Itoa(ID)) req.URL.RawQuery = q.Encode() var healthCheck HealthCheck err = l.client.DoWithContext(ctx, req, &healthCheck) if err != nil { return nil, err } return &healthCheck, nil } // SetHealthCheck sets your health check configuration for your load balancer func (l *LoadBalancerHandler) SetHealthCheck(ctx context.Context, ID int, healthConfig *HealthCheck) error { uri := "/v1/loadbalancer/health_check_update" values := url.Values{ "SUBID": {strconv.Itoa(ID)}, } if healthConfig != nil { if healthConfig.Protocol != "" { values.Add("protocol", healthConfig.Protocol) } if healthConfig.Port != 0 { values.Add("port", strconv.Itoa(healthConfig.Port)) } if healthConfig.CheckInterval != 0 { values.Add("check_interval", strconv.Itoa(healthConfig.CheckInterval)) } if healthConfig.ResponseTimeout != 0 { values.Add("response_timeout", strconv.Itoa(healthConfig.ResponseTimeout)) } if healthConfig.UnhealthyThreshold != 0 { values.Add("unhealthy_threshold", strconv.Itoa(healthConfig.UnhealthyThreshold)) } if healthConfig.HealthyThreshold != 0 { values.Add("healthy_threshold", strconv.Itoa(healthConfig.HealthyThreshold)) } if healthConfig.Path != "" { values.Add("path", healthConfig.Path) } } req, err := l.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return err } err = l.client.DoWithContext(ctx, req, nil) if err != nil { return err } return nil } // GetGenericInfo is the generic configuration of a load balancer subscription func (l *LoadBalancerHandler) GetGenericInfo(ctx context.Context, ID int) (*GenericInfo, error) { uri := "/v1/loadbalancer/generic_info" req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } q := req.URL.Query() q.Add("SUBID", strconv.Itoa(ID)) req.URL.RawQuery = q.Encode() var info GenericInfo err = l.client.DoWithContext(ctx, req, &info) if err != nil { return nil, err } return &info, err } // ListForwardingRules lists all forwarding rules for a load balancer subscription func (l *LoadBalancerHandler) ListForwardingRules(ctx context.Context, ID int) (*ForwardingRules, error) { uri := "v1/loadbalancer/forward_rule_list" req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } q := req.URL.Query() q.Add("SUBID", strconv.Itoa(ID)) req.URL.RawQuery = q.Encode() var frList ForwardingRules err = l.client.DoWithContext(ctx, req, &frList) if err != nil { return nil, err } return &frList, nil } // DeleteForwardingRule removes a forwarding rule from a load balancer subscription func (l *LoadBalancerHandler) DeleteForwardingRule(ctx context.Context, ID int, RuleID string) error { uri := "/v1/loadbalancer/forward_rule_delete" values := url.Values{ "SUBID": {strconv.Itoa(ID)}, "RULEID": {RuleID}, } req, err := l.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return err } err = l.client.DoWithContext(ctx, req, nil) if err != nil { return err } return nil } // CreateForwardingRule will create a new forwarding rule for your load balancer subscription. // Note the RuleID will be returned in the ForwardingRule struct func (l *LoadBalancerHandler) CreateForwardingRule(ctx context.Context, ID int, rule *ForwardingRule) (*ForwardingRule, error) { uri := "/v1/loadbalancer/forward_rule_create" values := url.Values{ "SUBID": {strconv.Itoa(ID)}, "frontend_protocol": {rule.FrontendProtocol}, "backend_protocol": {rule.BackendProtocol}, "frontend_port": {strconv.Itoa(rule.FrontendPort)}, "backend_port": {strconv.Itoa(rule.BackendPort)}, } req, err := l.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return nil, err } var fr ForwardingRule err = l.client.DoWithContext(ctx, req, &fr) if err != nil { return nil, err } return &fr, nil } // GetFullConfig retrieves the entire configuration of a load balancer subscription. func (l *LoadBalancerHandler) GetFullConfig(ctx context.Context, ID int) (*LBConfig, error) { uri := "/v1/loadbalancer/conf_info" req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } q := req.URL.Query() q.Add("SUBID", strconv.Itoa(ID)) req.URL.RawQuery = q.Encode() var lbConfig LBConfig err = l.client.DoWithContext(ctx, req, &lbConfig) if err != nil { return nil, err } return &lbConfig, nil } // HasSSL retrieves whether or not your load balancer subscription has an SSL cert attached. func (l *LoadBalancerHandler) HasSSL(ctx context.Context, ID int) (*struct { SSLInfo bool `json:"has_ssl"` }, error) { uri := "/v1/loadbalancer/ssl_info" req, err := l.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } q := req.URL.Query() q.Add("SUBID", strconv.Itoa(ID)) req.URL.RawQuery = q.Encode() ssl := &struct { SSLInfo bool `json:"has_ssl"` }{} err = l.client.DoWithContext(ctx, req, ssl) if err != nil { return nil, err } return ssl, nil } // Create a load balancer func (l *LoadBalancerHandler) Create(ctx context.Context, region int, label string, genericInfo *GenericInfo, healthCheck *HealthCheck, rules []ForwardingRule, ssl *SSL, instances *InstanceList) (*LoadBalancers, error) { uri := "/v1/loadbalancer/create" values := url.Values{ "DCID": {strconv.Itoa(region)}, } if label != "" { values.Add("label", label) } // Check generic info struct if genericInfo != nil { if genericInfo.SSLRedirect != nil { if strconv.FormatBool(*genericInfo.SSLRedirect) == "true" { values.Add("config_ssl_redirect", "true") } } if genericInfo.BalancingAlgorithm != "" { values.Add("balancing_algorithm", genericInfo.BalancingAlgorithm) } if genericInfo.StickySessions != nil { if genericInfo.StickySessions.StickySessionsEnabled == "on" { values.Add("sticky_sessions", genericInfo.StickySessions.StickySessionsEnabled) values.Add("cookie_name", genericInfo.StickySessions.CookieName) } } if genericInfo.ProxyProtocol != nil { value := "off" if strconv.FormatBool(*genericInfo.ProxyProtocol) == "true" { value = "on" } values.Add("proxy_protocol", value) } } if healthCheck != nil { t, _ := json.Marshal(healthCheck) values.Add("health_check", string(t)) } if rules != nil { t, e := json.Marshal(rules) if e != nil { panic(e) } values.Add("forwarding_rules", string(t)) } if ssl != nil { values.Add("ssl_private_key", ssl.PrivateKey) values.Add("ssl_certificate", ssl.Certificate) if ssl.Chain != "" { values.Add("ssl_chain", ssl.Chain) } } if instances != nil { t, e := json.Marshal(instances.InstanceList) if e != nil { panic(e) } values.Add("attached_nodes", string(t)) } req, err := l.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return nil, err } var lb LoadBalancers err = l.client.DoWithContext(ctx, req, &lb) if err != nil { return nil, err } return &lb, nil } // UpdateGenericInfo will update portions of your generic info section func (l *LoadBalancerHandler) UpdateGenericInfo(ctx context.Context, ID int, label string, genericInfo *GenericInfo) error { uri := "/v1/loadbalancer/generic_update" values := url.Values{ "SUBID": {strconv.Itoa(ID)}, } if label != "" { values.Add("label", label) } if genericInfo != nil { if genericInfo.StickySessions != nil { values.Add("sticky_sessions", genericInfo.StickySessions.StickySessionsEnabled) values.Add("cookie_name", genericInfo.StickySessions.CookieName) } if genericInfo.SSLRedirect != nil { values.Add("ssl_redirect", strconv.FormatBool(*genericInfo.SSLRedirect)) } if genericInfo.BalancingAlgorithm != "" { values.Add("balancing_algorithm", genericInfo.BalancingAlgorithm) } if genericInfo.ProxyProtocol != nil { value := "off" if strconv.FormatBool(*genericInfo.ProxyProtocol) == "true" { value = "on" } values.Add("proxy_protocol", value) } } req, err := l.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return err } err = l.client.DoWithContext(ctx, req, nil) if err != nil { return err } return nil } // AddSSL will attach an SSL certificate to a given load balancer func (l *LoadBalancerHandler) AddSSL(ctx context.Context, ID int, ssl *SSL) error { uri := "/v1/loadbalancer/ssl_add" values := url.Values{ "SUBID": {strconv.Itoa(ID)}, "ssl_private_key": {ssl.PrivateKey}, "ssl_certificate": {ssl.Certificate}, } if ssl.Chain != "" { values.Add("ssl_chain", ssl.Chain) } req, err := l.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return err } err = l.client.DoWithContext(ctx, req, nil) if err != nil { return err } return nil } // RemoveSSL will remove an SSL certificate from a load balancer func (l *LoadBalancerHandler) RemoveSSL(ctx context.Context, ID int) error { uri := "/v1/loadbalancer/ssl_remove" values := url.Values{ "SUBID": {strconv.Itoa(ID)}, } req, err := l.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return err } err = l.client.DoWithContext(ctx, req, nil) if err != nil { return err } return nil } govultr-0.4.2/load_balancer_test.go000066400000000000000000000303711366550666300173770ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestLoadBalancerHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/list", func(writer http.ResponseWriter, request *http.Request) { response := `[{"SUBID":1317575,"date_created":"2020-01-07 17:24:23","location":"New Jersey","label":"test","status":"active"}]` fmt.Fprintf(writer, response) }) list, err := client.LoadBalancer.List(ctx) if err != nil { t.Errorf("LoadBalancer.List returned %+v", err) } expected := []LoadBalancers{ { ID: 1317575, DateCreated: "2020-01-07 17:24:23", Location: "New Jersey", Label: "test", Status: "active", RegionID: 0, IPV6: "", IPV4: "", }, } if !reflect.DeepEqual(list, expected) { t.Errorf("LoadBalancer.List returned %+v, expected %+v", list, expected) } } func TestLoadBalancerHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/destroy", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.LoadBalancer.Delete(ctx, 12345) if err != nil { t.Errorf("LoadBalancer.Delete returned %+v", err) } } func TestLoadBalancerHandler_SetLabel(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/label_set", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.LoadBalancer.SetLabel(ctx, 12345, "label") if err != nil { t.Errorf("LoadBalancer.SetLabel returned %+v", err) } } func TestLoadBalancerHandler_AttachedInstances(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/instance_list", func(writer http.ResponseWriter, request *http.Request) { response := `{"instance_list": [1234, 2341]}` fmt.Fprintf(writer, response) }) instanceList, err := client.LoadBalancer.AttachedInstances(ctx, 12345) if err != nil { t.Errorf("LoadBalancer.AttachedInstances returned %+v ", err) } expected := &InstanceList{InstanceList: []int{1234, 2341}} if !reflect.DeepEqual(instanceList, expected) { t.Errorf("LoadBalancer.AttachedInstances returned %+v, expected %+v", instanceList, expected) } } func TestLoadBalancerHandler_AttachInstance(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/instance_attach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.LoadBalancer.AttachInstance(ctx, 12345, 45678) if err != nil { t.Errorf("LoadBalancer.AttachInstance returned %+v", err) } } func TestLoadBalancerHandler_DetachInstance(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/instance_detach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.LoadBalancer.DetachInstance(ctx, 12345, 45678) if err != nil { t.Errorf("LoadBalancer.DetachInstance returned %+v", err) } } func TestLoadBalancerHandler_GetHealthCheck(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/health_check_info", func(writer http.ResponseWriter, request *http.Request) { response := `{ "protocol": "http","port": 81,"path": "/test","check_interval": 10,"response_timeout": 45,"unhealthy_threshold": 1,"healthy_threshold": 2}` fmt.Fprintf(writer, response) }) health, err := client.LoadBalancer.GetHealthCheck(ctx, 12345) if err != nil { t.Errorf("LoadBalancer.GetHealthCheck returned %+v ", err) } expected := &HealthCheck{ Protocol: "http", Port: 81, Path: "/test", CheckInterval: 10, ResponseTimeout: 45, UnhealthyThreshold: 1, HealthyThreshold: 2, } if !reflect.DeepEqual(health, expected) { t.Errorf("LoadBalancer.GetHealthCheck returned %+v, expected %+v", health, expected) } } func TestLoadBalancerHandler_SetHealthCheck(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/health_check_update", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) health := &HealthCheck{ Protocol: "HTTPS", Port: 8080, Path: "/health", CheckInterval: 4, ResponseTimeout: 5, UnhealthyThreshold: 3, HealthyThreshold: 4, } err := client.LoadBalancer.SetHealthCheck(ctx, 12345, health) if err != nil { t.Errorf("LoadBalancer.SetHealthCheck returned %+v", err) } } func TestLoadBalancerHandler_GetGenericInfo(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/generic_info", func(writer http.ResponseWriter, request *http.Request) { response := `{"balancing_algorithm":"roundrobin","ssl_redirect":false,"proxy_protocol": true,"sticky_sessions":{"cookie_name":"test"}}` fmt.Fprintf(writer, response) }) info, err := client.LoadBalancer.GetGenericInfo(ctx, 12345) if err != nil { t.Errorf("LoadBalancer.GetGenericInfo returned %+v", err) } redirect := false proxy := true expected := &GenericInfo{ BalancingAlgorithm: "roundrobin", SSLRedirect: &redirect, ProxyProtocol: &proxy, StickySessions: &StickySessions{ CookieName: "test", }, } if !reflect.DeepEqual(info, expected) { t.Errorf("LoadBalancer.GetGenericInfo returned %+v, expected %+v", info, expected) } } func TestLoadBalancerHandler_ListForwardingRules(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/forward_rule_list", func(writer http.ResponseWriter, request *http.Request) { response := `{"forward_rule_list":[{"RULEID":"0690a322c25890bc","frontend_protocol":"http","frontend_port":80,"backend_protocol":"http","backend_port":80}]}` fmt.Fprintf(writer, response) }) list, err := client.LoadBalancer.ListForwardingRules(ctx, 12345) if err != nil { t.Errorf("LoadBalancer.ListForwardingRules returned %+v", err) } expected := &ForwardingRules{ForwardRuleList: []ForwardingRule{{ RuleID: "0690a322c25890bc", FrontendProtocol: "http", FrontendPort: 80, BackendProtocol: "http", BackendPort: 80, }}} if !reflect.DeepEqual(list, expected) { t.Errorf("LoadBalancer.ListForwardingRules returned %+v, expected %+v", list, expected) } } func TestLoadBalancerHandler_DeleteForwardingRule(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/forward_rule_delete", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.LoadBalancer.DeleteForwardingRule(ctx, 12345, "abcde1234") if err != nil { t.Errorf("LoadBalancer.DeleteForwardingRule returned %+v", err) } } func TestLoadBalancerHandler_CreateForwardingRule(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/forward_rule_create", func(writer http.ResponseWriter, request *http.Request) { response := `{"RULEID": "abc123"}` fmt.Fprintf(writer, response) }) rule := &ForwardingRule{ FrontendProtocol: "http", FrontendPort: 8080, BackendProtocol: "http", BackendPort: 8080, } ruleID, err := client.LoadBalancer.CreateForwardingRule(ctx, 123, rule) if err != nil { t.Errorf("LoadBalancer.CreateForwardingRule returned %+v", err) } expected := &ForwardingRule{ RuleID: "abc123", } if !reflect.DeepEqual(ruleID, expected) { t.Errorf("LoadBalancer.CreateForwardingRule returned %+v, expected %+v", ruleID, expected) } } func TestLoadBalancerHandler_GetFullConfig(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/conf_info", func(writer http.ResponseWriter, request *http.Request) { response := `{"generic_info":{"balancing_algorithm":"roundrobin","ssl_redirect":true,"proxy_protocol": true,"sticky_sessions":{"cookie_name":"cookiename"}},"health_checks_info":{"protocol":"http","port":80,"path":"\/","check_interval":15,"response_timeout":5,"unhealthy_threshold":5,"healthy_threshold":5},"has_ssl":true,"forward_rule_list":[{"RULEID":"b06ce4cd520eea15","frontend_protocol":"http","frontend_port":80,"backend_protocol":"http","backend_port":80}],"instance_list":[1317615]}` fmt.Fprintf(writer, response) }) config, err := client.LoadBalancer.GetFullConfig(ctx, 123) if err != nil { t.Errorf("LoadBalancer.GetFullConfig returned %+v", err) } redirect := true proxy := true expected := &LBConfig{ GenericInfo: GenericInfo{ BalancingAlgorithm: "roundrobin", SSLRedirect: &redirect, ProxyProtocol: &proxy, StickySessions: &StickySessions{CookieName: "cookiename"}, }, HealthCheck: HealthCheck{ Protocol: "http", Port: 80, Path: "/", CheckInterval: 15, ResponseTimeout: 5, UnhealthyThreshold: 5, HealthyThreshold: 5, }, SSLInfo: true, ForwardingRules: ForwardingRules{ForwardRuleList: []ForwardingRule{{ RuleID: "b06ce4cd520eea15", FrontendProtocol: "http", FrontendPort: 80, BackendProtocol: "http", BackendPort: 80, }}}, InstanceList: InstanceList{InstanceList: []int{1317615}}, } if !reflect.DeepEqual(config, expected) { t.Errorf("LoadBalancer.GetFullConfigreturned %+v, expected %+v", config, expected) } } func TestLoadBalancerHandler_HasSSL(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/ssl_info", func(writer http.ResponseWriter, request *http.Request) { response := `{"has_ssl":true}` fmt.Fprintf(writer, response) }) ssl, err := client.LoadBalancer.HasSSL(ctx, 123) if err != nil { t.Errorf("LoadBalancer.HasSSL returned %+v", err) } expected := &struct { SSLInfo bool `json:"has_ssl"` }{SSLInfo: true} if !reflect.DeepEqual(ssl, expected) { t.Errorf("LoadBalancer.HasSSL returned %+v, expected %+v", ssl, expected) } } func TestLoadBalancerHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/create", func(writer http.ResponseWriter, request *http.Request) { response := `{"SUBID": 1314840}` fmt.Fprintf(writer, response) }) redirect := true proxy := true info := GenericInfo{ BalancingAlgorithm: "roundrobin", SSLRedirect: &redirect, ProxyProtocol: &proxy, StickySessions: &StickySessions{ StickySessionsEnabled: "on", CookieName: "cookie", }, } health := HealthCheck{ Protocol: "http", Port: 80, Path: "/", CheckInterval: 5, ResponseTimeout: 5, UnhealthyThreshold: 5, HealthyThreshold: 5, } rules := []ForwardingRule{ { FrontendProtocol: "https", FrontendPort: 80, BackendProtocol: "http", BackendPort: 80, }, } ssl := SSL{ PrivateKey: "key", Certificate: "cert", Chain: "chain", } instances := InstanceList{[]int{1234}} lb, err := client.LoadBalancer.Create(ctx, 1, "label", &info, &health, rules, &ssl, &instances) if err != nil { t.Errorf("LoadBalancer.Create returned %+v", err) } expected := LoadBalancers{ ID: 1314840, } if !reflect.DeepEqual(lb, &expected) { t.Errorf("LoadBalancer.Create returned %+v, expected %+v", lb, expected) } } func TestLoadBalancerHandler_UpdateGenericInfo(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/generic_update", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) redirect := true proxy := true info := GenericInfo{ BalancingAlgorithm: "roundrobin", SSLRedirect: &redirect, ProxyProtocol: &proxy, StickySessions: &StickySessions{ StickySessionsEnabled: "on", CookieName: "cookie_name", }, } err := client.LoadBalancer.UpdateGenericInfo(ctx, 12345, "label", &info) if err != nil { t.Errorf("LoadBalancer.UpdateGenericInfo returned %+v", err) } } func TestLoadBalancerHandler_AddSSL(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/ssl_add", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) ssl := &SSL{ PrivateKey: "key", Certificate: "crt", Chain: "chain", } err := client.LoadBalancer.AddSSL(ctx, 12345, ssl) if err != nil { t.Errorf("LoadBalancer.AddSSL returned %+v", err) } } func TestLoadBalancerHandler_RemoveSSL(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/loadbalancer/ssl_remove", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.LoadBalancer.RemoveSSL(ctx, 12345) if err != nil { t.Errorf("LoadBalancer.RemoveSSL returned %+v", err) } } govultr-0.4.2/network.go000066400000000000000000000054771366550666300152740ustar00rootroot00000000000000package 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 } govultr-0.4.2/network_test.go000066400000000000000000000036121366550666300163200ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestNetworkServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/network/create", func(writer http.ResponseWriter, request *http.Request) { response := ` { "NETWORKID": "net59a0526477dd3" } ` fmt.Fprint(writer, response) }) net, err := client.Network.Create(ctx, "1", "go-test", "111.111.111.111/24") if err != nil { t.Errorf("Network.Create returned %+v, expected %+v", err, nil) } expected := &Network{ NetworkID: "net59a0526477dd3", } if !reflect.DeepEqual(net, expected) { t.Errorf("Network.Create returned %+v, expected %+v", net, expected) } } func TestNetworkServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/network/destroy", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Network.Delete(ctx, "foo") if err != nil { t.Errorf("Network.Delete returned %+v, expected %+v", err, nil) } } func TestNetworkServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/network/list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "net539626f0798d7": { "DCID": "1", "NETWORKID": "net539626f0798d7", "date_created": "2017-08-25 12:23:45", "description": "test1", "v4_subnet": "10.99.0.0", "v4_subnet_mask": 24 } } ` fmt.Fprintf(writer, response) }) networks, err := client.Network.List(ctx) if err != nil { t.Errorf("Network.List returned error: %v", err) } expected := []Network{ { NetworkID: "net539626f0798d7", RegionID: "1", Description: "test1", V4Subnet: "10.99.0.0", V4SubnetMask: 24, DateCreated: "2017-08-25 12:23:45", }, } if !reflect.DeepEqual(networks, expected) { t.Errorf("Network.List returned %+v, expected %+v", networks, expected) } } govultr-0.4.2/object_storage.go000066400000000000000000000143021366550666300165600ustar00rootroot00000000000000package govultr import ( "context" "net/http" "net/url" "strconv" ) // ObjectStorageService is the interface to interact with the object storage endpoints on the Vultr API. // Link: https://www.vultr.com/api/#objectstorage type ObjectStorageService interface { Create(ctx context.Context, objectStoreClusterID int, Label string) (*struct { ID int `json:"SUBID"` }, error) Delete(ctx context.Context, id int) error SetLabel(ctx context.Context, id int, label string) error List(ctx context.Context, options *ObjectListOptions) ([]ObjectStorage, error) Get(ctx context.Context, id int) (*ObjectStorage, error) ListCluster(ctx context.Context) ([]ObjectStorageCluster, error) RegenerateKeys(ctx context.Context, id int, s3AccessKey string) (*S3Keys, error) } // ObjectStorageServiceHandler handles interaction with the firewall rule methods for the Vultr API. type ObjectStorageServiceHandler struct { client *Client } // ObjectStorage represents a Vultr Object Storage subscription. type ObjectStorage struct { ID int `json:"SUBID"` DateCreated string `json:"date_created"` ObjectStoreClusterID int `json:"OBJSTORECLUSTERID"` RegionID int `json:"DCID"` Location string Label string Status string S3Keys } // ObjectStorageCluster represents a Vultr Object Storage cluster. type ObjectStorageCluster struct { ObjectStoreClusterID int `json:"OBJSTORECLUSTERID"` RegionID int `json:"DCID"` Location string Hostname string Deploy string } // S3Keys define your api access to your cluster type S3Keys struct { S3Hostname string `json:"s3_hostname"` S3AccessKey string `json:"s3_access_key"` S3SecretKey string `json:"s3_secret_key"` } // ObjectListOptions are your optional params you have available to list data. type ObjectListOptions struct { IncludeS3 bool Label string } // Create an object storage subscription func (o *ObjectStorageServiceHandler) Create(ctx context.Context, objectStoreClusterID int, Label string) (*struct { ID int `json:"SUBID"` }, error) { uri := "/v1/objectstorage/create" values := url.Values{ "OBJSTORECLUSTERID": {strconv.Itoa(objectStoreClusterID)}, "label": {Label}, } req, err := o.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return nil, err } id := struct { ID int `json:"SUBID"` }{} err = o.client.DoWithContext(ctx, req, &id) if err != nil { return nil, err } return &id, nil } // Delete an object storage subscription. func (o *ObjectStorageServiceHandler) Delete(ctx context.Context, id int) error { uri := "/v1/objectstorage/destroy" values := url.Values{ "SUBID": {strconv.Itoa(id)}, } req, err := o.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return err } err = o.client.DoWithContext(ctx, req, nil) if err != nil { return err } return nil } // SetLabel of an object storage subscription. func (o *ObjectStorageServiceHandler) SetLabel(ctx context.Context, id int, label string) error { uri := "/v1/objectstorage/label_set" values := url.Values{ "SUBID": {strconv.Itoa(id)}, "label": {label}, } req, err := o.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return err } err = o.client.DoWithContext(ctx, req, nil) if err != nil { return err } return nil } // List returns all object storage subscriptions on the current account. This includes both pending and active subscriptions. func (o *ObjectStorageServiceHandler) List(ctx context.Context, options *ObjectListOptions) ([]ObjectStorage, error) { uri := "/v1/objectstorage/list" req, err := o.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } if options != nil { q := req.URL.Query() // default behavior is true if options.IncludeS3 == false { q.Add("include_s3", "false") } if options.Label != "" { q.Add("label", options.Label) } req.URL.RawQuery = q.Encode() } var objectStorageMap map[string]ObjectStorage err = o.client.DoWithContext(ctx, req, &objectStorageMap) if err != nil { return nil, err } var objectStorages []ObjectStorage for _, o := range objectStorageMap { objectStorages = append(objectStorages, o) } return objectStorages, nil } // Get returns a specified object storage by the provided ID func (o *ObjectStorageServiceHandler) Get(ctx context.Context, id int) (*ObjectStorage, error) { uri := "/v1/objectstorage/list" req, err := o.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } if id != 0 { q := req.URL.Query() q.Add("SUBID", strconv.Itoa(id)) req.URL.RawQuery = q.Encode() } objStorage := make(map[string]ObjectStorage) err = o.client.DoWithContext(ctx, req, &objStorage) if err != nil { return nil, err } obj := objStorage[strconv.Itoa(id)] return &obj, nil } // ListCluster returns back your object storage clusters. // Clusters may be removed over time. The "deploy" field can be used to determine whether or not new deployments are allowed in the cluster. func (o *ObjectStorageServiceHandler) ListCluster(ctx context.Context) ([]ObjectStorageCluster, error) { uri := "/v1/objectstorage/list_cluster" req, err := o.client.NewRequest(ctx, http.MethodGet, uri, nil) if err != nil { return nil, err } var objectClusterMap map[string]ObjectStorageCluster err = o.client.DoWithContext(ctx, req, &objectClusterMap) if err != nil { return nil, err } var objectStorageCluster []ObjectStorageCluster for _, o := range objectClusterMap { objectStorageCluster = append(objectStorageCluster, o) } return objectStorageCluster, nil } // RegenerateKeys of the S3 API Keys for an object storage subscription func (o *ObjectStorageServiceHandler) RegenerateKeys(ctx context.Context, id int, s3AccessKey string) (*S3Keys, error) { uri := "/v1/objectstorage/s3key_regenerate" values := url.Values{ "SUBID": {strconv.Itoa(id)}, "s3_access_key": {s3AccessKey}, } req, err := o.client.NewRequest(ctx, http.MethodPost, uri, values) if err != nil { return nil, err } s3Keys := new(S3Keys) err = o.client.DoWithContext(ctx, req, s3Keys) if err != nil { return nil, err } return s3Keys, nil } govultr-0.4.2/object_storage_test.go000066400000000000000000000123261366550666300176230ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestObjectStorageServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/objectstorage/create", func(writer http.ResponseWriter, request *http.Request) { response := `{"SUBID": 1234}` fmt.Fprint(writer, response) }) id, err := client.ObjectStorage.Create(ctx, 1, "s3 label") if err != nil { t.Errorf("ObjectStorage.Create returned %+v", err) } expected := &struct { ID int `json:"SUBID"` }{ID: 1234} if !reflect.DeepEqual(id, expected) { t.Errorf("ObjectStorage.Create returned %+v, expected %+v", id, expected) } } func TestObjectStorageServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/objectstorage/destroy", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.ObjectStorage.Delete(ctx, 1234) if err != nil { t.Errorf("ObjectStorage.Delete returned %+v", err) } } func TestObjectStorageServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/objectstorage/list", func(writer http.ResponseWriter, request *http.Request) { response := `{"1314217":{"SUBID": 1314217,"date_created": "2019-04-17 17:33:00","OBJSTORECLUSTERID": 1,"DCID": 1,"location": "New Jersey","label": "object1","status": "active","s3_hostname": "nj1.vultrobjects.com","s3_access_key": "abc1234","s3_secret_key": "def5678"}}` fmt.Fprint(writer, response) }) s3, err := client.ObjectStorage.Get(ctx, 1314217) if err != nil { t.Errorf("ObjectStorage.Get returned %+v", err) } expected := &ObjectStorage{ ID: 1314217, DateCreated: "2019-04-17 17:33:00", ObjectStoreClusterID: 1, RegionID: 1, Location: "New Jersey", Label: "object1", Status: "active", S3Keys: S3Keys{ S3Hostname: "nj1.vultrobjects.com", S3AccessKey: "abc1234", S3SecretKey: "def5678", }, } if !reflect.DeepEqual(s3, expected) { t.Errorf("ObjectStorage.Get returned %+v, expected %+v", s3, expected) } } func TestObjectStorageServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/objectstorage/list", func(writer http.ResponseWriter, request *http.Request) { response := `{"1314217": {"SUBID": 1314217,"date_created": "2019-04-17 17:33:00","OBJSTORECLUSTERID": 1,"DCID": 1,"location": "New Jersey","label": "object1","status": "active","s3_hostname": "nj1.vultrobjects.com","s3_access_key": "abc1234","s3_secret_key": "def5678"}}` fmt.Fprint(writer, response) }) options := &ObjectListOptions{ IncludeS3: false, Label: "label", } s3s, err := client.ObjectStorage.List(ctx, options) if err != nil { t.Errorf("ObjectStorage.List returned %+v", err) } expected := []ObjectStorage{ { ID: 1314217, DateCreated: "2019-04-17 17:33:00", ObjectStoreClusterID: 1, RegionID: 1, Location: "New Jersey", Label: "object1", Status: "active", S3Keys: S3Keys{ S3Hostname: "nj1.vultrobjects.com", S3AccessKey: "abc1234", S3SecretKey: "def5678", }, }, } if !reflect.DeepEqual(s3s, expected) { t.Errorf("ObjectStorage.List returned %+v, expected %+v", s3s, expected) } } func TestObjectStorageServiceHandler_ListCluster(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/objectstorage/list_cluster", func(writer http.ResponseWriter, request *http.Request) { response := `{"1": {"OBJSTORECLUSTERID": 1,"DCID": 1,"location": "New Jersey","hostname": "nj1.vultrobjects.com","deploy": "yes"}}` fmt.Fprint(writer, response) }) clusterList, err := client.ObjectStorage.ListCluster(ctx) if err != nil { t.Errorf("ObjectStorage.ListCluster returned %+v", err) } expected := []ObjectStorageCluster{ { ObjectStoreClusterID: 1, RegionID: 1, Location: "New Jersey", Hostname: "nj1.vultrobjects.com", Deploy: "yes", }, } if !reflect.DeepEqual(clusterList, expected) { t.Errorf("ObjectStorage.ListCluster returned %+v, expected %+v", clusterList, expected) } } func TestObjectStorageServiceHandler_RegenerateKeys(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/objectstorage/s3key_regenerate", func(writer http.ResponseWriter, request *http.Request) { response := `{"s3_hostname": "nj1.vultrobjects.com","s3_access_key": "abc1236","s3_secret_key": "def5679"}` fmt.Fprint(writer, response) }) s3Keys, err := client.ObjectStorage.RegenerateKeys(ctx, 1234, "acv123") if err != nil { t.Errorf("ObjectStorage.RegenerateKeys returned %+v", err) } expected := &S3Keys{ S3Hostname: "nj1.vultrobjects.com", S3AccessKey: "abc1236", S3SecretKey: "def5679", } if !reflect.DeepEqual(s3Keys, expected) { t.Errorf("ObjectStorage.RegenerateKeys returned %+v, expected %+v", s3Keys, expected) } } func TestObjectStorageServiceHandler_SetLabel(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/objectstorage/label_set", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.ObjectStorage.SetLabel(ctx, 1, "s3 label") if err != nil { t.Errorf("ObjectStorage.Create returned %+v", err) } } govultr-0.4.2/os.go000066400000000000000000000035141366550666300142120ustar00rootroot00000000000000package 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 } govultr-0.4.2/os_test.go000066400000000000000000000035561366550666300152570ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestOSServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/os/list", func(w http.ResponseWriter, r *http.Request) { response := ` { "127": { "OSID": 127, "name": "CentOS 6 x64", "arch": "x64", "family": "centos", "windows": false } } ` fmt.Fprint(w, response) }) apps, err := client.OS.List(ctx) if err != nil { t.Errorf("OS.List returned error: %v", err) } expected := []OS{ { OsID: 127, Name: "CentOS 6 x64", Arch: "x64", Family: "centos", Windows: false, }, } if !reflect.DeepEqual(apps, expected) { t.Errorf("OS.List returned %+v, expected %+v", apps, expected) } } func TestOSServiceHandler_List_StringIDs(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/os/list", func(w http.ResponseWriter, r *http.Request) { response := ` { "127": { "OSID": 127, "name": "CentOS 6 x64", "arch": "x64", "family": "centos", "windows": false } } ` fmt.Fprint(w, response) }) apps, err := client.OS.List(ctx) if err != nil { t.Errorf("OS.List returned error: %v", err) } expected := []OS{ { OsID: 127, Name: "CentOS 6 x64", Arch: "x64", Family: "centos", Windows: false, }, } if !reflect.DeepEqual(apps, expected) { t.Errorf("OS.List returned %+v, expected %+v", apps, expected) } } func TestOSServiceHandler_ListEmpty(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/os/list", func(w http.ResponseWriter, r *http.Request) { response := `[]` fmt.Fprint(w, response) }) apps, err := client.OS.List(ctx) if err != nil { t.Errorf("OS.List returned error: %v", err) } var expected []OS if !reflect.DeepEqual(apps, expected) { t.Errorf("OS.List returned %+v, expected %+v", apps, expected) } } govultr-0.4.2/plans.go000066400000000000000000000113541366550666300147070ustar00rootroot00000000000000package 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 } govultr-0.4.2/plans_test.go000066400000000000000000000116471366550666300157530ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestPlanServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/plans/list", func(writer http.ResponseWriter, request *http.Request) { response := `{"201": {"VPSPLANID": "201","name": "1024 MB RAM,25 GB SSD,1.00 TB BW","vcpu_count": "1","ram": "1024","disk": "25","bandwidth": "1.00","bandwidth_gb": "1024","price_per_month": "5.00","plan_type": "SSD","windows": false,"available_locations": [1,2,3,4,5,6]}}` fmt.Fprint(writer, response) }) plans, err := client.Plan.List(ctx, "vc2") if err != nil { t.Errorf("Plan.List returned %+v", err) } expected := []Plan{{ PlanID: 201, Name: "1024 MB RAM,25 GB SSD,1.00 TB BW", VCPUs: 1, RAM: "1024", Disk: "25", Price: "5.00", Bandwidth: "1.00", BandwidthGB: "1024", Windows: false, PlanType: "SSD", Regions: []int{1, 2, 3, 4, 5, 6}, }, } if !reflect.DeepEqual(plans, expected) { t.Errorf("Plan.List returned %+v, expected %+v", plans, expected) } } func TestPlanServiceHandler_GetBareMetalList(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/plans/list_baremetal", func(writer http.ResponseWriter, request *http.Request) { response := `{"99": {"METALPLANID": "99","name": "32768 MB RAM,4x 240 GB SSD,1.00 TB BW","cpu_count": 12,"cpu_model": "E-2186G","ram": 32768,"disk": "4x 240 GB SSD","bandwidth_tb": 1,"price_per_month": 600,"plan_type": "SSD","deprecated": false,"available_locations": [1]}}` fmt.Fprint(writer, response) }) bareMetalPlans, err := client.Plan.GetBareMetalList(ctx) if err != nil { t.Errorf("Plan.GetBareMetalList returned %+v", err) } expected := []BareMetalPlan{ { PlanID: "99", Name: "32768 MB RAM,4x 240 GB SSD,1.00 TB BW", CPUs: 12, CPUModel: "E-2186G", RAM: 32768, Disk: "4x 240 GB SSD", BandwidthTB: 1, Price: 600, PlanType: "SSD", Deprecated: false, Regions: []int{1}, }, } if !reflect.DeepEqual(bareMetalPlans, expected) { t.Errorf("Plan.GetBareMetalList returned %+v, expected %+v", bareMetalPlans, expected) } } func TestPlanServiceHandler_GetVc2List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/plans/list_vc2", func(writer http.ResponseWriter, request *http.Request) { response := `{"201": {"VPSPLANID": "201","name": "1024 MB RAM,25 GB SSD,1.00 TB BW","vcpu_count": "1","ram": "1024","disk": "25","bandwidth": "1.00","bandwidth_gb": "1024","price_per_month": "5.00","plan_type": "SSD"}}` fmt.Fprint(writer, response) }) vc2, err := client.Plan.GetVc2List(ctx) if err != nil { t.Errorf("Plan.GetVc2List returned %+v", err) } expected := []VCPlan{ { PlanID: "201", Name: "1024 MB RAM,25 GB SSD,1.00 TB BW", VCPUs: "1", RAM: "1024", Disk: "25", Bandwidth: "1.00", BandwidthGB: "1024", Price: "5.00", PlanType: "SSD", }, } if !reflect.DeepEqual(vc2, expected) { t.Errorf("Plan.GetVc2List returned %+v, expected %+v", vc2, expected) } } func TestPlanServiceHandler_GetVdc2List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/plans/list_vdc2", func(writer http.ResponseWriter, request *http.Request) { response := `{"115": {"VPSPLANID": "115","name": "8192 MB RAM,110 GB SSD,10.00 TB BW","vcpu_count": "2","ram": "8192","disk": "110","bandwidth": "10.00","bandwidth_gb": "10240","price_per_month": "60.00","plan_type": "DEDICATED"}}` fmt.Fprint(writer, response) }) vdc2, err := client.Plan.GetVdc2List(ctx) if err != nil { t.Errorf("Plan.GetVdc2List returned %+v", err) } expected := []VCPlan{ { PlanID: "115", Name: "8192 MB RAM,110 GB SSD,10.00 TB BW", VCPUs: "2", RAM: "8192", Disk: "110", Bandwidth: "10.00", BandwidthGB: "10240", Price: "60.00", PlanType: "DEDICATED", }, } if !reflect.DeepEqual(vdc2, expected) { t.Errorf("Plan.GetVdc2List returned %+v, expected %+v", vdc2, expected) } } func TestPlanServiceHandler_GetVc2zList(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/plans/list_vc2z", func(writer http.ResponseWriter, request *http.Request) { response := `{ "401": {"VPSPLANID": "401","name": "2048 MB RAM,64 GB SSD,2.00 TB BW","vcpu_count": "1","ram": "2048","disk": "64","bandwidth": "2.00","price_per_month": "12.00","plan_type": "HIGHFREQUENCY"}}` fmt.Fprint(writer, response) }) vc2z, err := client.Plan.GetVc2zList(ctx) if err != nil { t.Errorf("Plan.GetVc2zList returned %+v", err) } expected := []VCPlan{ { PlanID: "401", Name: "2048 MB RAM,64 GB SSD,2.00 TB BW", VCPUs: "1", RAM: "2048", Disk: "64", Bandwidth: "2.00", Price: "12.00", PlanType: "HIGHFREQUENCY", }, } if !reflect.DeepEqual(vc2z, expected) { t.Errorf("Plan.GetVc2zList returned %+v, expected %+v", vc2z, expected) } } govultr-0.4.2/regions.go000066400000000000000000000073641366550666300152460ustar00rootroot00000000000000package 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 } govultr-0.4.2/regions_test.go000066400000000000000000000072151366550666300163000ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestRegionServiceHandler_Availability(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/regions/availability", func(writer http.ResponseWriter, request *http.Request) { response := `[201,202,203,204,205,206,115,29,93,94,95,96,97,98,100]` fmt.Fprint(writer, response) }) region, err := client.Region.Availability(ctx, 1, "vc2") if err != nil { t.Errorf("Region.Availability returned error: %v", err) } expected := []int{201, 202, 203, 204, 205, 206, 115, 29, 93, 94, 95, 96, 97, 98, 100} if !reflect.DeepEqual(region, expected) { t.Errorf("Region.Availability returned %+v, expected %+v", region, expected) } } func TestRegionServiceHandler_BareMetalAvailability(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/regions/availability_baremetal", func(writer http.ResponseWriter, request *http.Request) { response := `[1,2,3,4]` fmt.Fprint(writer, response) }) region, err := client.Region.BareMetalAvailability(ctx, 1) if err != nil { t.Errorf("Region.BareMetalAvailability returned error: %v", err) } expected := []int{1, 2, 3, 4} if !reflect.DeepEqual(region, expected) { t.Errorf("Region.BareMetalAvailability returned %+v, expected %+v", region, expected) } } func TestRegionServiceHandler_Vc2Availability(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/regions/availability_vc2", func(writer http.ResponseWriter, request *http.Request) { response := `[115,29,93,94,95,96,97,98,100]` fmt.Fprint(writer, response) }) region, err := client.Region.Vc2Availability(ctx, 1) if err != nil { t.Errorf("Region.Vc2Availability returned error: %v", err) } expected := []int{115, 29, 93, 94, 95, 96, 97, 98, 100} if !reflect.DeepEqual(region, expected) { t.Errorf("Region.Vc2Availability returned %+v, expected %+v", region, expected) } } func TestRegionServiceHandler_Vdc2Availability(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/regions/availability_vdc2", func(writer http.ResponseWriter, request *http.Request) { response := `[115,29,93,94]` fmt.Fprint(writer, response) }) region, err := client.Region.Vdc2Availability(ctx, 1) if err != nil { t.Errorf("Region.Vdc2Availability returned error: %v", err) } expected := []int{115, 29, 93, 94} if !reflect.DeepEqual(region, expected) { t.Errorf("Region.Vdc2Availability returned %+v, expected %+v", region, expected) } } func TestRegionServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/regions/list", func(writer http.ResponseWriter, request *http.Request) { response := `{"1": {"DCID": "1","name": "New Jersey","country": "US","continent": "North America","state": "NJ","ddos_protection": true,"block_storage": true,"regioncode": "EWR"},"2": {"DCID": "1","name": "New Jersey","country": "US","continent": "North America","state": "NJ","ddos_protection": true,"block_storage": true,"regioncode": "EWR"}}` fmt.Fprint(writer, response) }) region, err := client.Region.List(ctx) if err != nil { t.Errorf("Region.List returned error: %v", err) } expected := []Region{ { RegionID: "1", Name: "New Jersey", Country: "US", Continent: "North America", State: "NJ", Ddos: true, BlockStorage: true, RegionCode: "EWR", }, { RegionID: "1", Name: "New Jersey", Country: "US", Continent: "North America", State: "NJ", Ddos: true, BlockStorage: true, RegionCode: "EWR", }, } if !reflect.DeepEqual(region, expected) { t.Errorf("Region.List returned %+v, expected %+v", region, expected) } } govultr-0.4.2/reserved_ip.go000066400000000000000000000133051366550666300160770ustar00rootroot00000000000000package 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 } govultr-0.4.2/reserved_ip_test.go000066400000000000000000000067211366550666300171420ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestReservedIPServiceHandler_Attach(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/reservedip/attach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.ReservedIP.Attach(ctx, "111.111.111.111", "1") if err != nil { t.Errorf("ReservedIP.Attach returned %+v, expected %+v", err, nil) } } func TestReservedIPServiceHandler_Convert(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/reservedip/convert", func(writer http.ResponseWriter, request *http.Request) { response := ` { "SUBID": 1312965 } ` fmt.Fprint(writer, response) }) ip, err := client.ReservedIP.Convert(ctx, "111.111.111.111", "1", "go-test") if err != nil { t.Errorf("ReservedIP.Convert returned %+v, expected %+v", err, nil) } expected := &ReservedIP{ ReservedIPID: "1312965", RegionID: 0, IPType: "", Subnet: "", SubnetSize: 0, Label: "go-test", AttachedID: "", } if !reflect.DeepEqual(ip, expected) { t.Errorf("ReservedIP.Convert returned %+v, expected %+v", ip, expected) } } func TestReservedIPServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/reservedip/create", func(writer http.ResponseWriter, request *http.Request) { response := ` { "SUBID": 1312965 } ` fmt.Fprint(writer, response) }) ip, err := client.ReservedIP.Create(ctx, 1, "v4", "go-test") if err != nil { t.Errorf("ReservedIP.Create returned %+v, expected %+v", err, nil) } expected := &ReservedIP{ ReservedIPID: "1312965", RegionID: 1, IPType: "v4", Subnet: "", SubnetSize: 0, Label: "go-test", AttachedID: "", } if !reflect.DeepEqual(ip, expected) { t.Errorf("ReservedIP.Create returned %+v, expected %+v", ip, expected) } } func TestReservedIPServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/reservedip/destroy", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.ReservedIP.Delete(ctx, "111.111.111.111") if err != nil { t.Errorf("ReservedIP.Delete returned %+v, expected %+v", err, nil) } } func TestReservedIPServiceHandler_Detach(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/reservedip/detach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.ReservedIP.Detach(ctx, "111.111.111.111", "1") if err != nil { t.Errorf("ReservedIP.Detach returned %+v, expected %+v", err, nil) } } func TestReservedIPServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/reservedip/list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "1313044": { "SUBID": 1313044, "DCID": 1, "ip_type": "v4", "subnet": "10.234.22.53", "subnet_size": 32, "label": "my first reserved ip", "attached_SUBID": 123456 } } ` fmt.Fprintf(writer, response) }) ips, err := client.ReservedIP.List(ctx) if err != nil { t.Errorf("ReservedIP.List returned error: %v", err) } expected := []ReservedIP{ { ReservedIPID: "1313044", RegionID: 1, IPType: "v4", Subnet: "10.234.22.53", SubnetSize: 32, Label: "my first reserved ip", AttachedID: "123456", }, } if !reflect.DeepEqual(ips, expected) { t.Errorf("ReservedIP.List returned %+v, expected %+v", ips, expected) } } govultr-0.4.2/server.go000066400000000000000000001044621366550666300151030ustar00rootroot00000000000000package 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 } govultr-0.4.2/server_test.go000066400000000000000000001000471366550666300161350ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestServerServiceHandler_ChangeApp(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/app_change", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.ChangeApp(ctx, "1234", "24") if err != nil { t.Errorf("Server.ChangeApp returned %+v, ", err) } } func TestServerServiceHandler_ListApps(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/app_change_list", func(writer http.ResponseWriter, request *http.Request) { response := `{"1": {"APPID": "1","name": "LEMP","short_name": "lemp","deploy_name": "LEMP on CentOS 6 x64","surcharge": 0}}` fmt.Fprint(writer, response) }) application, err := client.Server.ListApps(ctx, "1234") if err != nil { t.Errorf("Server.ListApps returned %+v, ", err) } expected := []Application{ { AppID: "1", Name: "LEMP", ShortName: "lemp", DeployName: "LEMP on CentOS 6 x64", Surcharge: 0, }, } if !reflect.DeepEqual(application, expected) { t.Errorf("Server.ListApps returned %+v, expected %+v", application, expected) } } func TestServerServiceHandler_AppInfo(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/get_app_info", func(writer http.ResponseWriter, request *http.Request) { response := `{"app_info": "test"}` fmt.Fprint(writer, response) }) appInfo, err := client.Server.AppInfo(ctx, "1234") if err != nil { t.Errorf("Server.AppInfo returned %+v, ", err) } expected := &AppInfo{AppInfo: "test"} if !reflect.DeepEqual(appInfo, expected) { t.Errorf("Server.AppInfo returned %+v, expected %+v", appInfo, expected) } } func TestServerServiceHandler_EnableBackup(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/backup_enable", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.EnableBackup(ctx, "1234") if err != nil { t.Errorf("Server.EnableBackup returned %+v, ", err) } } func TestServerServiceHandler_DisableBackup(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/backup_disable", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.DisableBackup(ctx, "1234") if err != nil { t.Errorf("Server.DisableBackup returned %+v, ", err) } } func TestServerServiceHandler_GetBackupSchedule(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/backup_get_schedule", func(writer http.ResponseWriter, request *http.Request) { response := `{ "enabled": true,"cron_type": "weekly","next_scheduled_time_utc": "2016-05-07 08:00:00","hour": 8,"dow": 6,"dom": 0}` fmt.Fprint(writer, response) }) backup, err := client.Server.GetBackupSchedule(ctx, "1234") if err != nil { t.Errorf("Server.GetBackupSchedule returned %+v, ", err) } expected := &BackupSchedule{ Enabled: true, CronType: "weekly", NextRun: "2016-05-07 08:00:00", Hour: 8, Dow: 6, Dom: 0, } if !reflect.DeepEqual(backup, expected) { t.Errorf("Server.GetBackupSchedule returned %+v, expected %+v", backup, expected) } } func TestServerServiceHandler_SetBackupSchedule(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/backup_set_schedule", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) bs := &BackupSchedule{ CronType: "", Hour: 23, Dow: 2, Dom: 3, } err := client.Server.SetBackupSchedule(ctx, "1234", bs) if err != nil { t.Errorf("Server.SetBackupSchedule returned %+v, ", err) } } func TestServerServiceHandler_RestoreBackup(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/restore_backup", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.RestoreBackup(ctx, "1234", "45a31f4") if err != nil { t.Errorf("Server.RestoreBackup returned %+v, ", err) } } func TestServerServiceHandler_RestoreSnapshot(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/restore_snapshot", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.RestoreSnapshot(ctx, "1234", "45a31f4") if err != nil { t.Errorf("Server.RestoreSnapshot returned %+v, ", err) } } func TestServerServiceHandler_SetLabel(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/label_set", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.SetLabel(ctx, "1234", "new-label") if err != nil { t.Errorf("Server.SetLabel returned %+v, ", err) } } func TestServerServiceHandler_SetTag(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/tag_set", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.SetTag(ctx, "1234", "new-tag") if err != nil { t.Errorf("Server.SetTag returned %+v, ", err) } } func TestServerServiceHandler_Neighbors(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/neighbors", func(writer http.ResponseWriter, request *http.Request) { response := `[2345,1234]` fmt.Fprint(writer, response) }) neighbors, err := client.Server.Neighbors(ctx, "1234") if err != nil { t.Errorf("Server.Neighbors returned %+v, ", err) } expected := []int{2345, 1234} if !reflect.DeepEqual(neighbors, expected) { t.Errorf("Server.Neighbors returned %+v, expected %+v", neighbors, expected) } } func TestServerServiceHandler_EnablePrivateNetwork(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/private_network_enable", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.EnablePrivateNetwork(ctx, "1234", "45a31f4") if err != nil { t.Errorf("Server.EnablePrivateNetwork returned %+v, ", err) } } func TestServerServiceHandler_DisablePrivateNetwork(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/private_network_disable", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.DisablePrivateNetwork(ctx, "1234", "45a31f4") if err != nil { t.Errorf("Server.DisablePrivateNetwork returned %+v, ", err) } } func TestServerServiceHandler_ListPrivateNetworks(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/private_networks", func(writer http.ResponseWriter, request *http.Request) { response := `{"net539626f0798d7": {"NETWORKID": "net539626f0798d7","mac_address": "5a:02:00:00:24:e9","ip_address": "10.99.0.3"}}` fmt.Fprint(writer, response) }) privateNetwork, err := client.Server.ListPrivateNetworks(ctx, "12345") if err != nil { t.Errorf("Server.ListPrivateNetworks return %+v, ", err) } expected := []PrivateNetwork{ { NetworkID: "net539626f0798d7", MacAddress: "5a:02:00:00:24:e9", IPAddress: "10.99.0.3", }, } if !reflect.DeepEqual(privateNetwork, expected) { t.Errorf("Server.ListPrivateNetworks returned %+v, expected %+v", privateNetwork, expected) } } func TestServerServiceHandler_ListUpgradePlan(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/upgrade_plan_list", func(writer http.ResponseWriter, request *http.Request) { response := `[1, 2, 3, 4]` fmt.Fprint(writer, response) }) plans, err := client.Server.ListUpgradePlan(ctx, "123") if err != nil { t.Errorf("Server.ListUpgradePlan return %+v ", err) } expected := []int{1, 2, 3, 4} if !reflect.DeepEqual(plans, expected) { t.Errorf("Server.ListUpgradePlan returned %+v, expected %+v", plans, expected) } } func TestServerServiceHandler_UpgradePlan(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/upgrade_plan", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.UpgradePlan(ctx, "12351", "123") if err != nil { t.Errorf("Server.UpgradePlan return %+v ", err) } } func TestServerServiceHandler_ListOS(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/os_change_list", func(writer http.ResponseWriter, request *http.Request) { response := `{"127": {"OSID": 127,"name": "CentOS 6 x64","arch": "x64","family": "centos","windows": false,"surcharge": "0.00"}}` fmt.Fprint(writer, response) }) os, err := client.Server.ListOS(ctx, "1234") if err != nil { t.Errorf("Server.ListOS return %+v ", err) } expected := []OS{ { OsID: 127, Name: "CentOS 6 x64", Arch: "x64", Family: "centos", Windows: false, }, } if !reflect.DeepEqual(os, expected) { t.Errorf("Server.ListOS returned %+v, expected %+v", os, expected) } } func TestServerServiceHandler_ChangeOS(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/os_change", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.ChangeOS(ctx, "1234", "1") if err != nil { t.Errorf("Server.ChangeOS return %+v ", err) } } func TestServerServiceHandler_IsoAttach(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/iso_attach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.IsoAttach(ctx, "1234", "1") if err != nil { t.Errorf("Server.IsoAttach return %+v ", err) } } func TestServerServiceHandler_IsoDetach(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/iso_detach", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.IsoDetach(ctx, "1234") if err != nil { t.Errorf("Server.IsoDetach return %+v ", err) } } func TestServerServiceHandler_IsoStatus(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/iso_status", func(writer http.ResponseWriter, request *http.Request) { response := `{"state": "ready","ISOID": "12345"}` fmt.Fprint(writer, response) }) isoStatus, err := client.Server.IsoStatus(ctx, "1234") if err != nil { t.Errorf("Server.IsoStatus return %+v ", err) } expected := &ServerIso{State: "ready", IsoID: "12345"} if !reflect.DeepEqual(isoStatus, expected) { t.Errorf("Server.IsoStatus returned %+v, expected %+v", isoStatus, expected) } } func TestServerServiceHandler_SetFirewallGroup(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/firewall_group_set", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.SetFirewallGroup(ctx, "1234", "123") if err != nil { t.Errorf("Server.SetFirewallGroup return %+v ", err) } } func TestServerServiceHandler_SetUserData(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/set_user_data", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.SetUserData(ctx, "1234", "user-test-data") if err != nil { t.Errorf("Server.SetUserData return %+v ", err) } } func TestServerServiceHandler_GetUserData(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/get_user_data", func(writer http.ResponseWriter, request *http.Request) { response := `{"userdata": "ZWNobyBIZWxsbyBXb3JsZA=="}` fmt.Fprint(writer, response) }) userData, err := client.Server.GetUserData(ctx, "1234") if err != nil { t.Errorf("Server.GetUserData return %+v ", err) } expected := &UserData{UserData: "ZWNobyBIZWxsbyBXb3JsZA=="} if !reflect.DeepEqual(userData, expected) { t.Errorf("Server.GetUserData returned %+v, expected %+v", userData, expected) } } func TestServerServiceHandler_IPV4Info(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/list_ipv4", func(writer http.ResponseWriter, request *http.Request) { response := `{ "576965": [{"ip": "123.123.123.123","netmask": "255.255.255.248","gateway": "123.123.123.1","type": "main_ip","reverse": "host1.example.com"}]}` fmt.Fprint(writer, response) }) ipv4, err := client.Server.IPV4Info(ctx, "1234", true) if err != nil { t.Errorf("Server.IPV4Info returned %+v", err) } expected := []IPV4{ { IP: "123.123.123.123", Netmask: "255.255.255.248", Gateway: "123.123.123.1", Type: "main_ip", Reverse: "host1.example.com", }, } if !reflect.DeepEqual(ipv4, expected) { t.Errorf("Server.IPV4Info returned %+v, expected %+v", ipv4, expected) } } func TestServerServiceHandler_IPV6Info(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/list_ipv6", func(writer http.ResponseWriter, request *http.Request) { response := `{"576965": [{"ip": "2001:DB8:1000::100","network": "2001:DB8:1000::","network_size": "64","type": "main_ip"}]}` fmt.Fprint(writer, response) }) ipv6, err := client.Server.IPV6Info(ctx, "1234") if err != nil { t.Errorf("Server.IPV6Info returned %+v", err) } expected := []IPV6{ { IP: "2001:DB8:1000::100", Network: "2001:DB8:1000::", NetworkSize: "64", Type: "main_ip", }, } if !reflect.DeepEqual(ipv6, expected) { t.Errorf("Server.IPV6Info returned %+v, expected %+v", ipv6, expected) } } func TestServerServiceHandler_AddIPV4(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/create_ipv4", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.AddIPV4(ctx, "1234") if err != nil { t.Errorf("Server.AddIPV4 returned %+v", err) } } func TestServerServiceHandler_DestroyIPV4(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/destroy_ipv4", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.DestroyIPV4(ctx, "1234", "192.168.0.1") if err != nil { t.Errorf("Server.DestroyIPV4 returned %+v", err) } } func TestServerServiceHandler_EnableIPV6(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/ipv6_enable", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.EnableIPV6(ctx, "1234") if err != nil { t.Errorf("Server.EnableIPV6 returned %+v", err) } } func TestServerServiceHandler_Bandwidth(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/bandwidth", func(writer http.ResponseWriter, request *http.Request) { response := `{"incoming_bytes": [["2014-06-10","81072581"]],"outgoing_bytes": [["2014-06-10","4059610"]]}` fmt.Fprint(writer, response) }) bandwidth, err := client.Server.Bandwidth(ctx, "123") if err != nil { t.Errorf("Server.Bandwidth returned %+v", err) } expected := []map[string]string{ {"date": "2014-06-10", "incoming": "81072581", "outgoing": "4059610"}, } if !reflect.DeepEqual(bandwidth, expected) { t.Errorf("Server.Bandwidth returned %+v, expected %+v", bandwidth, expected) } } func TestServerServiceHandler_ListReverseIPV6(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/reverse_list_ipv6", func(writer http.ResponseWriter, request *http.Request) { response := `{"576965": [{"ip": "2001:DB8:1000::101","reverse": "host1.example.com"}]}` fmt.Fprint(writer, response) }) reverseIPV6, err := client.Server.ListReverseIPV6(ctx, "123890") if err != nil { t.Errorf("Server.ListReverseIPV6 returned error: %v", err) } expected := []ReverseIPV6{ {IP: "2001:DB8:1000::101", Reverse: "host1.example.com"}, } if !reflect.DeepEqual(reverseIPV6, expected) { t.Errorf("Server.ListReverseIPV6 returned %+v, expected %+v", reverseIPV6, expected) } } func TestServerServiceHandler_SetDefaultReverseIPV4(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/reverse_default_ipv4", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.SetDefaultReverseIPV4(ctx, "1234", "129.123.123.1") if err != nil { t.Errorf("Server.SetDefaultReverseIPV4 returned %+v", err) } } func TestServerServiceHandler_DeleteReverseIPV6(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/reverse_delete_ipv6", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.DeleteReverseIPV6(ctx, "1234", "2001:19f0:8001:1480:5400:2ff:fe00:8228") if err != nil { t.Errorf("Server.DeleteReverseIPV6 returned %+v", err) } } func TestServerServiceHandler_SetReverseIPV4(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/reverse_set_ipv4", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.SetReverseIPV4(ctx, "1234", "192.168.0.1", "test.com") if err != nil { t.Errorf("Server.SetReverseIPV4 returned %+v", err) } } func TestServerServiceHandler_SetReverseIPV6(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/reverse_set_ipv6", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.SetReverseIPV6(ctx, "1234", "2001:19f0:8001:1480:5400:2ff:fe00:8228", "test.com") if err != nil { t.Errorf("Server.SetReverseIPV6 returned %+v", err) } } func TestServerServiceHandler_Halt(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/halt", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.Halt(ctx, "1234") if err != nil { t.Errorf("Server.Halt returned %+v", err) } } func TestServerServiceHandler_Start(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/start", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.Start(ctx, "1234") if err != nil { t.Errorf("Server.Start returned %+v", err) } } func TestServerServiceHandler_Reboot(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/reboot", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.Reboot(ctx, "1234") if err != nil { t.Errorf("Server.Reboot returned %+v", err) } } func TestServerServiceHandler_Reinstall(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/reinstall", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.Reinstall(ctx, "1234") if err != nil { t.Errorf("Server.Reinstall returned %+v", err) } } func TestServerServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/destroy", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Server.Delete(ctx, "1234") if err != nil { t.Errorf("Server.Delete returned %+v", err) } } func TestServerServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/create", func(writer http.ResponseWriter, request *http.Request) { response := `{"SUBID": "1234151"}` fmt.Fprint(writer, response) }) optionsWithPrivateNetwork := &ServerOptions{ IPXEChain: "test.org", IsoID: 1, ScriptID: "213", EnableIPV6: true, EnablePrivateNetwork: true, AutoBackups: true, UserData: "uno-dos-tres", NotifyActivate: true, DDOSProtection: true, SnapshotID: "12ab", Hostname: "hostname-3000", Tag: "tagger", Label: "label-extreme", SSHKeyIDs: []string{"1234"}, ReservedIPV4: "63.209.35.79", FirewallGroupID: "1234", AppID: "1234", } server, err := client.Server.Create(ctx, 1, 2, 3, optionsWithPrivateNetwork) if err != nil { t.Errorf("Server.Create returned %+v", err) } expected := &Server{InstanceID: "1234151"} if !reflect.DeepEqual(server, expected) { t.Errorf("Server.Create returned %+v, expected %+v", server, expected) } options := &ServerOptions{ NetworkID: []string{"1", "2", "3"}, } serverWithNetwork, err := client.Server.Create(ctx, 1, 2, 3, options) if err != nil { t.Errorf("Server.Create returned %+v", err) } if !reflect.DeepEqual(serverWithNetwork, expected) { t.Errorf("Server.Create returned %+v, expected %+v", serverWithNetwork, expected) } } func TestServerServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/list", func(writer http.ResponseWriter, request *http.Request) { response := `{"576965": {"SUBID": "576965","os": "CentOS 6 x64","ram": "4096 MB","disk": "Virtual 60 GB","main_ip": "123.123.123.123","vcpu_count": "2","location": "New Jersey","DCID": "1","default_password": "nreqnusibni","date_created": "2013-12-19 14:45:41","pending_charges": "46.67","status": "active","cost_per_month": "10.05","current_bandwidth_gb": 131.512,"allowed_bandwidth_gb": "1000","netmask_v4": "255.255.255.248","gateway_v4": "123.123.123.1","power_status": "running","server_state": "ok","VPSPLANID": "28","v6_main_ip": "2001:DB8:1000::100","v6_network_size": "64","v6_network": "2001:DB8:1000::","v6_networks": [{"v6_network": "2001:DB8:1000::","v6_main_ip": "2001:DB8:1000::100","v6_network_size": "64"}],"label": "my new server","internal_ip": "10.99.0.10","kvm_url": "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV","auto_backups": "yes","tag": "mytag","OSID": "127","APPID": "0","FIREWALLGROUPID": "0"}}` fmt.Fprint(writer, response) }) server, err := client.Server.List(ctx) if err != nil { t.Errorf("Server.List returned %+v", err) } expected := []Server{ { InstanceID: "576965", Os: "CentOS 6 x64", RAM: "4096 MB", Disk: "Virtual 60 GB", MainIP: "123.123.123.123", VPSCpus: "2", Location: "New Jersey", RegionID: "1", DefaultPassword: "nreqnusibni", Created: "2013-12-19 14:45:41", PendingCharges: "46.67", Status: "active", Cost: "10.05", CurrentBandwidth: 131.512, AllowedBandwidth: "1000", NetmaskV4: "255.255.255.248", GatewayV4: "123.123.123.1", PowerStatus: "running", ServerState: "ok", PlanID: "28", V6Networks: []V6Network{{Network: "2001:DB8:1000::", MainIP: "2001:DB8:1000::100", NetworkSize: "64"}}, Label: "my new server", InternalIP: "10.99.0.10", KVMUrl: "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", AutoBackups: "yes", Tag: "mytag", OsID: "127", AppID: "0", FirewallGroupID: "0", }, } if !reflect.DeepEqual(server, expected) { t.Errorf("Server.List returned %+v, expected %+v", server, expected) } } func TestServerServiceHandler_ListByLabel(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/list", func(writer http.ResponseWriter, request *http.Request) { response := `{"576965": {"SUBID": "576965","os": "CentOS 6 x64","ram": "4096 MB","disk": "Virtual 60 GB","main_ip": "123.123.123.123","vcpu_count": "2","location": "New Jersey","DCID": "1","default_password": "nreqnusibni","date_created": "2013-12-19 14:45:41","pending_charges": "46.67","status": "active","cost_per_month": "10.05","current_bandwidth_gb": 131.512,"allowed_bandwidth_gb": "1000","netmask_v4": "255.255.255.248","gateway_v4": "123.123.123.1","power_status": "running","server_state": "ok","VPSPLANID": "28","v6_main_ip": "2001:DB8:1000::100","v6_network_size": "64","v6_network": "2001:DB8:1000::","v6_networks": [{"v6_network": "2001:DB8:1000::","v6_main_ip": "2001:DB8:1000::100","v6_network_size": "64"}],"label": "my new server","internal_ip": "10.99.0.10","kvm_url": "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV","auto_backups": "yes","tag": "mytag","OSID": "127","APPID": "0","FIREWALLGROUPID": "0"}}` fmt.Fprint(writer, response) }) server, err := client.Server.ListByLabel(ctx, "label") if err != nil { t.Errorf("Server.ListByLabel returned %+v", err) } expected := []Server{ { InstanceID: "576965", Os: "CentOS 6 x64", RAM: "4096 MB", Disk: "Virtual 60 GB", MainIP: "123.123.123.123", VPSCpus: "2", Location: "New Jersey", RegionID: "1", DefaultPassword: "nreqnusibni", Created: "2013-12-19 14:45:41", PendingCharges: "46.67", Status: "active", Cost: "10.05", CurrentBandwidth: 131.512, AllowedBandwidth: "1000", NetmaskV4: "255.255.255.248", GatewayV4: "123.123.123.1", PowerStatus: "running", ServerState: "ok", PlanID: "28", V6Networks: []V6Network{{Network: "2001:DB8:1000::", MainIP: "2001:DB8:1000::100", NetworkSize: "64"}}, Label: "my new server", InternalIP: "10.99.0.10", KVMUrl: "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", AutoBackups: "yes", Tag: "mytag", OsID: "127", AppID: "0", FirewallGroupID: "0", }, } if !reflect.DeepEqual(server, expected) { t.Errorf("Server.ListByLabel returned %+v, expected %+v", server, expected) } } func TestServerServiceHandler_ListByMainIP(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/list", func(writer http.ResponseWriter, request *http.Request) { response := `{"576965": {"SUBID": "576965","os": "CentOS 6 x64","ram": "4096 MB","disk": "Virtual 60 GB","main_ip": "123.123.123.123","vcpu_count": "2","location": "New Jersey","DCID": "1","default_password": "nreqnusibni","date_created": "2013-12-19 14:45:41","pending_charges": "46.67","status": "active","cost_per_month": "10.05","current_bandwidth_gb": 131.512,"allowed_bandwidth_gb": "1000","netmask_v4": "255.255.255.248","gateway_v4": "123.123.123.1","power_status": "running","server_state": "ok","VPSPLANID": "28","v6_main_ip": "2001:DB8:1000::100","v6_network_size": "64","v6_network": "2001:DB8:1000::","v6_networks": [{"v6_network": "2001:DB8:1000::","v6_main_ip": "2001:DB8:1000::100","v6_network_size": "64"}],"label": "my new server","internal_ip": "10.99.0.10","kvm_url": "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV","auto_backups": "yes","tag": "mytag","OSID": "127","APPID": "0","FIREWALLGROUPID": "0"}}` fmt.Fprint(writer, response) }) server, err := client.Server.ListByMainIP(ctx, "label") if err != nil { t.Errorf("Server.ListByMainIP returned %+v", err) } expected := []Server{ { InstanceID: "576965", Os: "CentOS 6 x64", RAM: "4096 MB", Disk: "Virtual 60 GB", MainIP: "123.123.123.123", VPSCpus: "2", Location: "New Jersey", RegionID: "1", DefaultPassword: "nreqnusibni", Created: "2013-12-19 14:45:41", PendingCharges: "46.67", Status: "active", Cost: "10.05", CurrentBandwidth: 131.512, AllowedBandwidth: "1000", NetmaskV4: "255.255.255.248", GatewayV4: "123.123.123.1", PowerStatus: "running", ServerState: "ok", PlanID: "28", V6Networks: []V6Network{{Network: "2001:DB8:1000::", MainIP: "2001:DB8:1000::100", NetworkSize: "64"}}, Label: "my new server", InternalIP: "10.99.0.10", KVMUrl: "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", AutoBackups: "yes", Tag: "mytag", OsID: "127", AppID: "0", FirewallGroupID: "0", }, } if !reflect.DeepEqual(server, expected) { t.Errorf("Server.ListByMainIP returned %+v, expected %+v", server, expected) } } func TestServerServiceHandler_ListByTag(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/list", func(writer http.ResponseWriter, request *http.Request) { response := `{"576965": {"SUBID": "576965","os": "CentOS 6 x64","ram": "4096 MB","disk": "Virtual 60 GB","main_ip": "123.123.123.123","vcpu_count": "2","location": "New Jersey","DCID": "1","default_password": "nreqnusibni","date_created": "2013-12-19 14:45:41","pending_charges": "46.67","status": "active","cost_per_month": "10.05","current_bandwidth_gb": 131.512,"allowed_bandwidth_gb": "1000","netmask_v4": "255.255.255.248","gateway_v4": "123.123.123.1","power_status": "running","server_state": "ok","VPSPLANID": "28","v6_main_ip": "2001:DB8:1000::100","v6_network_size": "64","v6_network": "2001:DB8:1000::","v6_networks": [{"v6_network": "2001:DB8:1000::","v6_main_ip": "2001:DB8:1000::100","v6_network_size": "64"}],"label": "my new server","internal_ip": "10.99.0.10","kvm_url": "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV","auto_backups": "yes","tag": "mytag","OSID": "127","APPID": "0","FIREWALLGROUPID": "0"}}` fmt.Fprint(writer, response) }) server, err := client.Server.ListByTag(ctx, "label") if err != nil { t.Errorf("Server.ListByTag returned %+v", err) } expected := []Server{ { InstanceID: "576965", Os: "CentOS 6 x64", RAM: "4096 MB", Disk: "Virtual 60 GB", MainIP: "123.123.123.123", VPSCpus: "2", Location: "New Jersey", RegionID: "1", DefaultPassword: "nreqnusibni", Created: "2013-12-19 14:45:41", PendingCharges: "46.67", Status: "active", Cost: "10.05", CurrentBandwidth: 131.512, AllowedBandwidth: "1000", NetmaskV4: "255.255.255.248", GatewayV4: "123.123.123.1", PowerStatus: "running", ServerState: "ok", PlanID: "28", V6Networks: []V6Network{{Network: "2001:DB8:1000::", MainIP: "2001:DB8:1000::100", NetworkSize: "64"}}, Label: "my new server", InternalIP: "10.99.0.10", KVMUrl: "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", AutoBackups: "yes", Tag: "mytag", OsID: "127", AppID: "0", FirewallGroupID: "0", }, } if !reflect.DeepEqual(server, expected) { t.Errorf("Server.ListByTag returned %+v, expected %+v", server, expected) } } func TestServerServiceHandler_GetServer(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/server/list", func(writer http.ResponseWriter, request *http.Request) { response := `{"SUBID": "576965","os": "CentOS 6 x64","ram": "4096 MB","disk": "Virtual 60 GB","main_ip": "123.123.123.123","vcpu_count": "2","location": "New Jersey","DCID": "1","default_password": "nreqnusibni","date_created": "2013-12-19 14:45:41","pending_charges": "46.67","status": "active","cost_per_month": "10.05","current_bandwidth_gb": 131.512,"allowed_bandwidth_gb": "1000","netmask_v4": "255.255.255.248","gateway_v4": "123.123.123.1","power_status": "running","server_state": "ok","VPSPLANID": "28","v6_main_ip": "2001:DB8:1000::100","v6_network_size": "64","v6_network": "2001:DB8:1000::","v6_networks": [{"v6_network": "2001:DB8:1000::","v6_main_ip": "2001:DB8:1000::100","v6_network_size": "64"}],"label": "my new server","internal_ip": "10.99.0.10","kvm_url": "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV","auto_backups": "yes","tag": "mytag","OSID": "127","APPID": "0","FIREWALLGROUPID": "0"}` fmt.Fprint(writer, response) }) server, err := client.Server.GetServer(ctx, "1234") if err != nil { t.Errorf("Server.GetServer returned %+v", err) } expected := &Server{ InstanceID: "576965", Os: "CentOS 6 x64", RAM: "4096 MB", Disk: "Virtual 60 GB", MainIP: "123.123.123.123", VPSCpus: "2", Location: "New Jersey", RegionID: "1", DefaultPassword: "nreqnusibni", Created: "2013-12-19 14:45:41", PendingCharges: "46.67", Status: "active", Cost: "10.05", CurrentBandwidth: 131.512, AllowedBandwidth: "1000", NetmaskV4: "255.255.255.248", GatewayV4: "123.123.123.1", PowerStatus: "running", ServerState: "ok", PlanID: "28", V6Networks: []V6Network{{Network: "2001:DB8:1000::", MainIP: "2001:DB8:1000::100", NetworkSize: "64"}}, Label: "my new server", InternalIP: "10.99.0.10", KVMUrl: "https://my.vultr.com/subs/novnc/api.php?data=eawxFVZw2mXnhGUV", AutoBackups: "yes", Tag: "mytag", OsID: "127", AppID: "0", FirewallGroupID: "0", } if !reflect.DeepEqual(server, expected) { t.Errorf("Server.GetServer returned %+v, expected %+v", server, expected) } } govultr-0.4.2/snapshot.go000066400000000000000000000073001366550666300154250ustar00rootroot00000000000000package 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 } govultr-0.4.2/snapshot_test.go000066400000000000000000000065301366550666300164700ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestSnapshotServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/snapshot/create", func(writer http.ResponseWriter, request *http.Request) { response := `{"SNAPSHOTID": "1234567"}` fmt.Fprint(writer, response) }) snapshot, err := client.Snapshot.Create(ctx, "987654321", "unit-test-desc") if err != nil { t.Errorf("Snapshot.Create returned error: %v", err) } expected := &Snapshot{SnapshotID: "1234567", Description: "unit-test-desc"} if !reflect.DeepEqual(snapshot, expected) { t.Errorf("Snapshot.Create returned %+v, expected %+v", snapshot, expected) } } func TestSnapshotServiceHandler_CreateFromURL(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/snapshot/create_from_url", func(writer http.ResponseWriter, request *http.Request) { response := `{"SNAPSHOTID": "544e52f31c706"}` fmt.Fprint(writer, response) }) snapshot, err := client.Snapshot.CreateFromURL(ctx, "http://localhost/some.iso") if err != nil { t.Errorf("Snapshot.CreateFromURL returned error: %v", err) } expected := &Snapshot{SnapshotID: "544e52f31c706"} if !reflect.DeepEqual(snapshot, expected) { t.Errorf("Snapshot.CreateFromURL returned %+v, expected %+v", snapshot, expected) } } func TestSnapshotServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/snapshot/destroy", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.Snapshot.Delete(ctx, "7a05cbf361d98") if err != nil { t.Errorf("Snapshot.Delete returned %+v, expected %+v", err, nil) } } func TestSnapshotServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/snapshot/list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "5359435dc1df3": { "SNAPSHOTID": "5359435dc1df3", "date_created": "2014-04-22 16:11:46", "description": "", "size": "10000000", "status": "complete", "OSID": "127", "APPID": "0" } } ` fmt.Fprint(writer, response) }) snapshots, err := client.Snapshot.List(ctx) if err != nil { t.Errorf("Snapshot.List returned error: %v", err) } expected := []Snapshot{ {SnapshotID: "5359435dc1df3", DateCreated: "2014-04-22 16:11:46", Description: "", Size: "10000000", Status: "complete", OsID: "127", AppID: "0"}, } if !reflect.DeepEqual(snapshots, expected) { t.Errorf("Snapshot.List returned %+v, expected %+v", snapshots, expected) } } func TestSnapshotServiceHandler_Get(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/snapshot/list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "5359435dc1df3": { "SNAPSHOTID": "5359435dc1df3", "date_created": "2014-04-22 16:11:46", "description": "", "size": "10000000", "status": "complete", "OSID": "127", "APPID": "0" } } ` fmt.Fprint(writer, response) }) snapshots, err := client.Snapshot.Get(ctx, "5359435dc1df3") if err != nil { t.Errorf("Snapshot.Get returned error: %v", err) } expected := &Snapshot{SnapshotID: "5359435dc1df3", DateCreated: "2014-04-22 16:11:46", Description: "", Size: "10000000", Status: "complete", OsID: "127", AppID: "0"} if !reflect.DeepEqual(snapshots, expected) { t.Errorf("Snapshot.Get returned %+v, expected %+v", snapshots, expected) } } govultr-0.4.2/ssh_key.go000066400000000000000000000054731366550666300152440ustar00rootroot00000000000000package 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 } govultr-0.4.2/ssh_key_test.go000066400000000000000000000102031366550666300162660ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestSSHKeyServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/sshkey/create", func(writer http.ResponseWriter, request *http.Request) { response := ` { "SSHKEYID": "541b4960f23bd" } ` fmt.Fprint(writer, response) }) key, err := client.SSHKey.Create(ctx, "foo", "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyVGaw1PuEl98f4/7Kq3O9ZIvDw2OFOSXAFVqilSFNkHlefm1iMtPeqsIBp2t9cbGUf55xNDULz/bD/4BCV43yZ5lh0cUYuXALg9NI29ui7PEGReXjSpNwUD6ceN/78YOK41KAcecq+SS0bJ4b4amKZIJG3JWmDKljtv1dmSBCrTmEAQaOorxqGGBYmZS7NQumRe4lav5r6wOs8OACMANE1ejkeZsGFzJFNqvr5DuHdDL5FAudW23me3BDmrM9ifUzzjl1Jwku3bnRaCcjaxH8oTumt1a00mWci/1qUlaVFft085yvVq7KZbF2OPPbl+erDW91+EZ2FgEi+v1/CSJ5 your_username@hostname") if err != nil { t.Errorf("SSHKey.Create returned %+v, expected %+v", err, nil) } expected := &SSHKey{ SSHKeyID: "541b4960f23bd", Name: "foo", Key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyVGaw1PuEl98f4/7Kq3O9ZIvDw2OFOSXAFVqilSFNkHlefm1iMtPeqsIBp2t9cbGUf55xNDULz/bD/4BCV43yZ5lh0cUYuXALg9NI29ui7PEGReXjSpNwUD6ceN/78YOK41KAcecq+SS0bJ4b4amKZIJG3JWmDKljtv1dmSBCrTmEAQaOorxqGGBYmZS7NQumRe4lav5r6wOs8OACMANE1ejkeZsGFzJFNqvr5DuHdDL5FAudW23me3BDmrM9ifUzzjl1Jwku3bnRaCcjaxH8oTumt1a00mWci/1qUlaVFft085yvVq7KZbF2OPPbl+erDW91+EZ2FgEi+v1/CSJ5 your_username@hostname", DateCreated: "", } if !reflect.DeepEqual(key, expected) { t.Errorf("SSHKey.Create returned %+v, expected %+v", key, expected) } } func TestSSHKeyServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/sshkey/destroy", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.SSHKey.Delete(ctx, "foo") if err != nil { t.Errorf("SSHKey.Delete returned %+v, expected %+v", err, nil) } } func TestSSHKeyServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/sshkey/list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "541b4960f23bd": { "SSHKEYID": "541b4960f23bd", "date_created": null, "name": "test", "ssh_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyVGaw1PuEl98f4/7Kq3O9ZIvDw2OFOSXAFVqilSFNkHlefm1iMtPeqsIBp2t9cbGUf55xNDULz/bD/4BCV43yZ5lh0cUYuXALg9NI29ui7PEGReXjSpNwUD6ceN/78YOK41KAcecq+SS0bJ4b4amKZIJG3JWmDKljtv1dmSBCrTmEAQaOorxqGGBYmZS7NQumRe4lav5r6wOs8OACMANE1ejkeZsGFzJFNqvr5DuHdDL5FAudW23me3BDmrM9ifUzzjl1Jwku3bnRaCcjaxH8oTumt1a00mWci/1qUlaVFft085yvVq7KZbF2OPPbl+erDW91+EZ2FgEi+v1/CSJ5 your_username@hostname" } } ` fmt.Fprintf(writer, response) }) sshKeys, err := client.SSHKey.List(ctx) if err != nil { t.Errorf("SSHKey.List returned error: %v", err) } expected := []SSHKey{ { SSHKeyID: "541b4960f23bd", Name: "test", Key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyVGaw1PuEl98f4/7Kq3O9ZIvDw2OFOSXAFVqilSFNkHlefm1iMtPeqsIBp2t9cbGUf55xNDULz/bD/4BCV43yZ5lh0cUYuXALg9NI29ui7PEGReXjSpNwUD6ceN/78YOK41KAcecq+SS0bJ4b4amKZIJG3JWmDKljtv1dmSBCrTmEAQaOorxqGGBYmZS7NQumRe4lav5r6wOs8OACMANE1ejkeZsGFzJFNqvr5DuHdDL5FAudW23me3BDmrM9ifUzzjl1Jwku3bnRaCcjaxH8oTumt1a00mWci/1qUlaVFft085yvVq7KZbF2OPPbl+erDW91+EZ2FgEi+v1/CSJ5 your_username@hostname", DateCreated: "", }, } if !reflect.DeepEqual(sshKeys, expected) { t.Errorf("SSHKey.List returned %+v, expected %+v", sshKeys, expected) } } func TestSSHKeyServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/sshkey/update", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) sshKey := &SSHKey{ SSHKeyID: "561b4960f23cc", Name: "foo", Key: "ssh-rsa CCCCB3NzaC1yc2EAAAADAQABAAABAQCyVGaw1PuEl98f4/7Kq3O9ZIvDw2OFOSXAFVqilSFNkHlefm1iMtPeqsIBp2t9cbGUf55xNDULz/bD/4BCV43yZ5lh0cUYuXALg9NI29ui7PEGReXjSpNwUD6ceN/78YOK41KAcecq+SS0bJ4b4amKZIJG3JWmDKljtv1dmSBCrTmEAQaOorxqGGBYmZS7NQumRe4lav5r6wOs8OACMANE1ejkeZsGFzJFNqvr5DuHdDL5FAudW23me3BDmrM9ifUzzjl1Jwku3bnRaCcjaxH8oTumt1a00mWci/1qUlaVFft085yvVq7KZbF2OPPbl+erDW91+EZ2FgEi+v1/CSJ5 your_username@hostname", DateCreated: "", } err := client.SSHKey.Update(ctx, sshKey) if err != nil { t.Errorf("SSHKey.Update returned error: %+v", err) } } govultr-0.4.2/startup_script.go000066400000000000000000000076031366550666300166620ustar00rootroot00000000000000package 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 } govultr-0.4.2/startup_script_test.go000066400000000000000000000052531366550666300177200ustar00rootroot00000000000000package govultr import ( "context" "fmt" "net/http" "reflect" "testing" ) func TestStartupScriptServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/startupscript/create", func(writer http.ResponseWriter, request *http.Request) { response := ` { "SCRIPTID": 5 } ` fmt.Fprint(writer, response) }) s, err := client.StartupScript.Create(context.Background(), "foo", "#!/bin/bash\necho hello world > /root/hello", "pxe") if err != nil { t.Errorf("StartupScript.Create returned %+v, expected %+v", err, nil) } expected := &StartupScript{ ScriptID: "5", DateCreated: "", DateModified: "", Name: "foo", Type: "pxe", Script: "#!/bin/bash\necho hello world > /root/hello", } if !reflect.DeepEqual(s, expected) { t.Errorf("StartupScript.Create returned %+v, expected %+v", s, expected) } } func TestStartupScriptServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/startupscript/destroy", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.StartupScript.Delete(ctx, "foo") if err != nil { t.Errorf("StartupScript.Delete returned %+v, expected %+v", err, nil) } } func TestStartupScriptServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/startupscript/list", func(writer http.ResponseWriter, request *http.Request) { response := ` { "3": { "SCRIPTID": "3", "date_created": "2014-05-21 15:27:18", "date_modified": "2014-05-21 15:27:18", "name": "foo", "type": "boot", "script": "#!/bin/bash echo Hello World > /root/hello" } } ` fmt.Fprintf(writer, response) }) scripts, err := client.StartupScript.List(ctx) if err != nil { t.Errorf("StartupScript.List returned error: %v", err) } expected := []StartupScript{ { ScriptID: "3", Name: "foo", Type: "boot", Script: "#!/bin/bash echo Hello World > /root/hello", DateCreated: "2014-05-21 15:27:18", DateModified: "2014-05-21 15:27:18", }, } if !reflect.DeepEqual(scripts, expected) { t.Errorf("StartupScript.List returned %+v, expected %+v", scripts, expected) } } func TestStartupScriptServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/startupscript/update", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) script := &StartupScript{ ScriptID: "1", Name: "foo", Type: "boot", Script: "#!/bin/bash echo Hello World > /root/hello", } err := client.StartupScript.Update(ctx, script) if err != nil { t.Errorf("StartupScript.Update returned error: %+v", err) } } govultr-0.4.2/user.go000066400000000000000000000062771366550666300145600ustar00rootroot00000000000000package 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 } govultr-0.4.2/user_test.go000066400000000000000000000057321366550666300156120ustar00rootroot00000000000000package govultr import ( "fmt" "net/http" "reflect" "testing" ) func TestUserServiceHandler_Create(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/user/create", func(writer http.ResponseWriter, request *http.Request) { response := ` { "USERID": "564a1a88947b4", "api_key": "AAAAAAAA" } ` fmt.Fprint(writer, response) }) user, err := client.User.Create(ctx, "example@vultr.com", "Example User", "t0rbj0rn!", "no", []string{"support", "abuse", "alerts"}) if err != nil { t.Errorf("User.Create returned %+v, expected %+v", err, nil) } expected := &User{ UserID: "564a1a88947b4", Name: "Example User", Email: "example@vultr.com", APIEnabled: "no", ACL: []string{"support", "abuse", "alerts"}, APIKey: "AAAAAAAA", } if !reflect.DeepEqual(user, expected) { t.Errorf("User.Create returned %+v, expected %+v", user, expected) } } func TestUserServiceHandler_Delete(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/user/delete", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) err := client.User.Delete(ctx, "foo") if err != nil { t.Errorf("User.Delete returned %+v, expected %+v", err, nil) } } func TestUserServiceHandler_List(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/user/list", func(writer http.ResponseWriter, request *http.Request) { response := ` [ { "USERID": "564a1a7794d83", "name": "example user 1", "email": "example@vultr.com", "api_enabled": "yes", "acls": [ "manage_users", "subscriptions", "billing", "provisioning" ] }, { "USERID": "564a1a88947b4", "name": "example user 2", "email": "example@vultr.com", "api_enabled": "no", "acls": [ "support", "dns" ] } ] ` fmt.Fprintf(writer, response) }) Users, err := client.User.List(ctx) if err != nil { t.Errorf("User.List returned error: %v", err) } expected := []User{ { UserID: "564a1a7794d83", Name: "example user 1", Email: "example@vultr.com", APIEnabled: "yes", ACL: []string{"manage_users", "subscriptions", "billing", "provisioning"}, }, { UserID: "564a1a88947b4", Name: "example user 2", Email: "example@vultr.com", APIEnabled: "no", ACL: []string{"support", "dns"}, }, } if !reflect.DeepEqual(Users, expected) { t.Errorf("User.List returned %+v, expected %+v", Users, expected) } } func TestUserServiceHandler_Update(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v1/user/update", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprint(writer) }) user := &User{ UserID: "2e35cc32f9923", Name: "Example User", Password: "w1a4dcnst0n!", Email: "example@vultr.com", APIEnabled: "yes", ACL: []string{"support"}, } err := client.User.Update(ctx, user) if err != nil { t.Errorf("User.Update returned error: %+v", err) } }