From 5b7ec545c5ecf52bc593e07530a69f952ccfdd9f Mon Sep 17 00:00:00 2001 From: Luke Farnell Date: Sat, 8 Apr 2017 15:52:57 -0400 Subject: [PATCH] Added monitoring and updated godo --- builder/digitalocean/artifact.go | 4 +- builder/digitalocean/config.go | 1 + builder/digitalocean/step_create_droplet.go | 8 +- builder/digitalocean/step_create_ssh_key.go | 7 +- builder/digitalocean/step_droplet_info.go | 4 +- builder/digitalocean/step_power_off.go | 6 +- builder/digitalocean/step_shutdown.go | 6 +- builder/digitalocean/step_snapshot.go | 6 +- builder/digitalocean/wait.go | 10 +- .../github.com/digitalocean/godo/CHANGELOG.md | 10 + vendor/github.com/digitalocean/godo/README.md | 8 +- .../github.com/digitalocean/godo/account.go | 9 +- vendor/github.com/digitalocean/godo/action.go | 21 +- .../digitalocean/godo/certificates.go | 120 ++++++++ .../github.com/digitalocean/godo/domains.go | 59 ++-- .../digitalocean/godo/droplet_actions.go | 255 +++++++++++----- .../github.com/digitalocean/godo/droplets.go | 244 ++++++++++------ .../digitalocean/godo/floating_ips.go | 29 +- .../digitalocean/godo/floating_ips_actions.go | 45 +-- vendor/github.com/digitalocean/godo/godo.go | 75 ++++- .../digitalocean/godo/image_actions.go | 48 ++- vendor/github.com/digitalocean/godo/images.go | 67 +++-- vendor/github.com/digitalocean/godo/keys.go | 71 ++--- vendor/github.com/digitalocean/godo/links.go | 11 +- .../digitalocean/godo/load_balancers.go | 275 ++++++++++++++++++ .../github.com/digitalocean/godo/regions.go | 12 +- vendor/github.com/digitalocean/godo/sizes.go | 8 +- .../github.com/digitalocean/godo/snapshots.go | 139 +++++++++ .../github.com/digitalocean/godo/storage.go | 238 +++++++++++++++ .../digitalocean/godo/storage_actions.go | 128 ++++++++ .../github.com/digitalocean/godo/strings.go | 99 ++++--- vendor/github.com/digitalocean/godo/tags.go | 207 +++++++++++++ vendor/vendor.json | 5 +- 33 files changed, 1829 insertions(+), 406 deletions(-) create mode 100644 vendor/github.com/digitalocean/godo/CHANGELOG.md create mode 100644 vendor/github.com/digitalocean/godo/certificates.go create mode 100644 vendor/github.com/digitalocean/godo/load_balancers.go create mode 100644 vendor/github.com/digitalocean/godo/snapshots.go create mode 100644 vendor/github.com/digitalocean/godo/storage.go create mode 100644 vendor/github.com/digitalocean/godo/storage_actions.go create mode 100644 vendor/github.com/digitalocean/godo/tags.go diff --git a/builder/digitalocean/artifact.go b/builder/digitalocean/artifact.go index 8f6e81161..bd8668066 100644 --- a/builder/digitalocean/artifact.go +++ b/builder/digitalocean/artifact.go @@ -1,6 +1,7 @@ package digitalocean import ( + "context" "fmt" "log" "strconv" @@ -44,7 +45,8 @@ func (a *Artifact) State(name string) interface{} { } func (a *Artifact) Destroy() error { + ctx := context.TODO() log.Printf("Destroying image: %d (%s)", a.snapshotId, a.snapshotName) - _, err := a.client.Images.Delete(a.snapshotId) + _, err := a.client.Images.Delete(ctx, a.snapshotId) return err } diff --git a/builder/digitalocean/config.go b/builder/digitalocean/config.go index de623ca6d..0447e9cdf 100644 --- a/builder/digitalocean/config.go +++ b/builder/digitalocean/config.go @@ -27,6 +27,7 @@ type Config struct { Image string `mapstructure:"image"` PrivateNetworking bool `mapstructure:"private_networking"` + Monitoring bool `mapstructure:"monitoring"` SnapshotName string `mapstructure:"snapshot_name"` StateTimeout time.Duration `mapstructure:"state_timeout"` DropletName string `mapstructure:"droplet_name"` diff --git a/builder/digitalocean/step_create_droplet.go b/builder/digitalocean/step_create_droplet.go index a2e3c2b24..2b83eac61 100644 --- a/builder/digitalocean/step_create_droplet.go +++ b/builder/digitalocean/step_create_droplet.go @@ -1,6 +1,7 @@ package digitalocean import ( + "context" "fmt" "io/ioutil" @@ -19,6 +20,7 @@ func (s *stepCreateDroplet) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) c := state.Get("config").(Config) sshKeyId := state.Get("ssh_key_id").(int) + ctx := context.TODO() // Create the droplet based on configuration ui.Say("Creating droplet...") @@ -34,7 +36,7 @@ func (s *stepCreateDroplet) Run(state multistep.StateBag) multistep.StepAction { userData = string(contents) } - droplet, _, err := client.Droplets.Create(&godo.DropletCreateRequest{ + droplet, _, err := client.Droplets.Create(ctx, &godo.DropletCreateRequest{ Name: c.DropletName, Region: c.Region, Size: c.Size, @@ -45,6 +47,7 @@ func (s *stepCreateDroplet) Run(state multistep.StateBag) multistep.StepAction { {ID: sshKeyId}, }, PrivateNetworking: c.PrivateNetworking, + Monitoring: c.Monitoring, UserData: userData, }) if err != nil { @@ -71,10 +74,11 @@ func (s *stepCreateDroplet) Cleanup(state multistep.StateBag) { client := state.Get("client").(*godo.Client) ui := state.Get("ui").(packer.Ui) + ctx := context.TODO() // Destroy the droplet we just created ui.Say("Destroying droplet...") - _, err := client.Droplets.Delete(s.dropletId) + _, err := client.Droplets.Delete(ctx, s.dropletId) if err != nil { ui.Error(fmt.Sprintf( "Error destroying droplet. Please destroy it manually: %s", err)) diff --git a/builder/digitalocean/step_create_ssh_key.go b/builder/digitalocean/step_create_ssh_key.go index b639e1edf..926552a75 100644 --- a/builder/digitalocean/step_create_ssh_key.go +++ b/builder/digitalocean/step_create_ssh_key.go @@ -1,6 +1,7 @@ package digitalocean import ( + "context" "crypto/rand" "crypto/rsa" "crypto/x509" @@ -27,6 +28,7 @@ type stepCreateSSHKey struct { func (s *stepCreateSSHKey) Run(state multistep.StateBag) multistep.StepAction { client := state.Get("client").(*godo.Client) ui := state.Get("ui").(packer.Ui) + ctx := context.TODO() ui.Say("Creating temporary ssh key for droplet...") @@ -52,7 +54,7 @@ func (s *stepCreateSSHKey) Run(state multistep.StateBag) multistep.StepAction { name := fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) // Create the key! - key, _, err := client.Keys.Create(&godo.KeyCreateRequest{ + key, _, err := client.Keys.Create(ctx, &godo.KeyCreateRequest{ Name: name, PublicKey: pub_sshformat, }) @@ -107,9 +109,10 @@ func (s *stepCreateSSHKey) Cleanup(state multistep.StateBag) { client := state.Get("client").(*godo.Client) ui := state.Get("ui").(packer.Ui) + ctx := context.TODO() ui.Say("Deleting temporary ssh key...") - _, err := client.Keys.DeleteByID(s.keyId) + _, err := client.Keys.DeleteByID(ctx, s.keyId) if err != nil { log.Printf("Error cleaning up ssh key: %s", err) ui.Error(fmt.Sprintf( diff --git a/builder/digitalocean/step_droplet_info.go b/builder/digitalocean/step_droplet_info.go index c7b128218..393d28ff4 100644 --- a/builder/digitalocean/step_droplet_info.go +++ b/builder/digitalocean/step_droplet_info.go @@ -1,6 +1,7 @@ package digitalocean import ( + "context" "fmt" "github.com/digitalocean/godo" @@ -15,6 +16,7 @@ func (s *stepDropletInfo) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) c := state.Get("config").(Config) dropletID := state.Get("droplet_id").(int) + ctx := context.TODO() ui.Say("Waiting for droplet to become active...") @@ -27,7 +29,7 @@ func (s *stepDropletInfo) Run(state multistep.StateBag) multistep.StepAction { } // Set the IP on the state for later - droplet, _, err := client.Droplets.Get(dropletID) + droplet, _, err := client.Droplets.Get(ctx, dropletID) if err != nil { err := fmt.Errorf("Error retrieving droplet: %s", err) state.Put("error", err) diff --git a/builder/digitalocean/step_power_off.go b/builder/digitalocean/step_power_off.go index 9b697dbf0..9e8fd5e2b 100644 --- a/builder/digitalocean/step_power_off.go +++ b/builder/digitalocean/step_power_off.go @@ -1,6 +1,7 @@ package digitalocean import ( + "context" "fmt" "log" @@ -16,8 +17,9 @@ func (s *stepPowerOff) Run(state multistep.StateBag) multistep.StepAction { c := state.Get("config").(Config) ui := state.Get("ui").(packer.Ui) dropletId := state.Get("droplet_id").(int) + ctx := context.TODO() - droplet, _, err := client.Droplets.Get(dropletId) + droplet, _, err := client.Droplets.Get(ctx, dropletId) if err != nil { err := fmt.Errorf("Error checking droplet state: %s", err) state.Put("error", err) @@ -32,7 +34,7 @@ func (s *stepPowerOff) Run(state multistep.StateBag) multistep.StepAction { // Pull the plug on the Droplet ui.Say("Forcefully shutting down Droplet...") - _, _, err = client.DropletActions.PowerOff(dropletId) + _, _, err = client.DropletActions.PowerOff(ctx, dropletId) if err != nil { err := fmt.Errorf("Error powering off droplet: %s", err) state.Put("error", err) diff --git a/builder/digitalocean/step_shutdown.go b/builder/digitalocean/step_shutdown.go index 64b681ed9..b91558823 100644 --- a/builder/digitalocean/step_shutdown.go +++ b/builder/digitalocean/step_shutdown.go @@ -1,6 +1,7 @@ package digitalocean import ( + "context" "fmt" "log" "time" @@ -17,13 +18,14 @@ func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction { c := state.Get("config").(Config) ui := state.Get("ui").(packer.Ui) dropletId := state.Get("droplet_id").(int) + ctx := context.TODO() // Gracefully power off the droplet. We have to retry this a number // of times because sometimes it says it completed when it actually // did absolutely nothing (*ALAKAZAM!* magic!). We give up after // a pretty arbitrary amount of time. ui.Say("Gracefully shutting down droplet...") - _, _, err := client.DropletActions.Shutdown(dropletId) + _, _, err := client.DropletActions.Shutdown(ctx, dropletId) if err != nil { // If we get an error the first time, actually report it err := fmt.Errorf("Error shutting down droplet: %s", err) @@ -50,7 +52,7 @@ func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction { for attempts := 2; attempts > 0; attempts++ { log.Printf("ShutdownDroplet attempt #%d...", attempts) - _, _, err := client.DropletActions.Shutdown(dropletId) + _, _, err := client.DropletActions.Shutdown(ctx, dropletId) if err != nil { log.Printf("Shutdown retry error: %s", err) } diff --git a/builder/digitalocean/step_snapshot.go b/builder/digitalocean/step_snapshot.go index ce97f3702..6299e5582 100644 --- a/builder/digitalocean/step_snapshot.go +++ b/builder/digitalocean/step_snapshot.go @@ -1,6 +1,7 @@ package digitalocean import ( + "context" "errors" "fmt" "log" @@ -18,9 +19,10 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) c := state.Get("config").(Config) dropletId := state.Get("droplet_id").(int) + ctx := context.TODO() ui.Say(fmt.Sprintf("Creating snapshot: %v", c.SnapshotName)) - action, _, err := client.DropletActions.Snapshot(dropletId, c.SnapshotName) + action, _, err := client.DropletActions.Snapshot(ctx, dropletId, c.SnapshotName) if err != nil { err := fmt.Errorf("Error creating snapshot: %s", err) state.Put("error", err) @@ -51,7 +53,7 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction { } log.Printf("Looking up snapshot ID for snapshot: %s", c.SnapshotName) - images, _, err := client.Droplets.Snapshots(dropletId, nil) + images, _, err := client.Droplets.Snapshots(ctx, dropletId, nil) if err != nil { err := fmt.Errorf("Error looking up snapshot ID: %s", err) state.Put("error", err) diff --git a/builder/digitalocean/wait.go b/builder/digitalocean/wait.go index 35446674c..0acf0d963 100644 --- a/builder/digitalocean/wait.go +++ b/builder/digitalocean/wait.go @@ -1,6 +1,7 @@ package digitalocean import ( + "context" "fmt" "log" "time" @@ -12,6 +13,7 @@ import ( // avoid "pending" errors when making state changes. func waitForDropletUnlocked( client *godo.Client, dropletId int, timeout time.Duration) error { + ctx := context.TODO() done := make(chan struct{}) defer close(done) @@ -22,7 +24,7 @@ func waitForDropletUnlocked( attempts += 1 log.Printf("[DEBUG] Checking droplet lock state... (attempt: %d)", attempts) - droplet, _, err := client.Droplets.Get(dropletId) + droplet, _, err := client.Droplets.Get(ctx, dropletId) if err != nil { result <- err return @@ -62,6 +64,7 @@ func waitForDropletUnlocked( func waitForDropletState( desiredState string, dropletId int, client *godo.Client, timeout time.Duration) error { + ctx := context.TODO() done := make(chan struct{}) defer close(done) @@ -72,7 +75,7 @@ func waitForDropletState( attempts += 1 log.Printf("Checking droplet status... (attempt: %d)", attempts) - droplet, _, err := client.Droplets.Get(dropletId) + droplet, _, err := client.Droplets.Get(ctx, dropletId) if err != nil { result <- err return @@ -112,6 +115,7 @@ func waitForDropletState( func waitForActionState( desiredState string, dropletId, actionId int, client *godo.Client, timeout time.Duration) error { + ctx := context.TODO() done := make(chan struct{}) defer close(done) @@ -122,7 +126,7 @@ func waitForActionState( attempts += 1 log.Printf("Checking action status... (attempt: %d)", attempts) - action, _, err := client.DropletActions.Get(dropletId, actionId) + action, _, err := client.DropletActions.Get(ctx, dropletId, actionId) if err != nil { result <- err return diff --git a/vendor/github.com/digitalocean/godo/CHANGELOG.md b/vendor/github.com/digitalocean/godo/CHANGELOG.md new file mode 100644 index 000000000..71886a354 --- /dev/null +++ b/vendor/github.com/digitalocean/godo/CHANGELOG.md @@ -0,0 +1,10 @@ +# Change Log + +## [v1.0.0] - 2017-03-10 + +### Added +- #130 Add Convert to ImageActionsService. - @xmudrii +- #126 Add CertificatesService for managing certificates with the DigitalOcean API. - @viola +- #125 Add LoadBalancersService for managing load balancers with the DigitalOcean API. - @viola +- #122 Add GetVolumeByName to StorageService. - @protochron +- #113 Add context.Context to all calls. - @aybabtme diff --git a/vendor/github.com/digitalocean/godo/README.md b/vendor/github.com/digitalocean/godo/README.md index 03d0c0768..4d5cdf83e 100644 --- a/vendor/github.com/digitalocean/godo/README.md +++ b/vendor/github.com/digitalocean/godo/README.md @@ -65,7 +65,9 @@ createRequest := &godo.DropletCreateRequest{ }, } -newDroplet, _, err := client.Droplets.Create(createRequest) +ctx := context.TODO() + +newDroplet, _, err := client.Droplets.Create(ctx, createRequest) if err != nil { fmt.Printf("Something bad happened: %s\n\n", err) @@ -78,14 +80,14 @@ if err != nil { If a list of items is paginated by the API, you must request pages individually. For example, to fetch all Droplets: ```go -func DropletList(client *godo.Client) ([]godo.Droplet, error) { +func DropletList(ctx context.Context, client *godo.Client) ([]godo.Droplet, error) { // create a list to hold our droplets list := []godo.Droplet{} // create options. initially, these will be blank opt := &godo.ListOptions{} for { - droplets, resp, err := client.Droplets.List(opt) + droplets, resp, err := client.Droplets.List(ctx, opt) if err != nil { return nil, err } diff --git a/vendor/github.com/digitalocean/godo/account.go b/vendor/github.com/digitalocean/godo/account.go index 3c2a1c3af..18eed9712 100644 --- a/vendor/github.com/digitalocean/godo/account.go +++ b/vendor/github.com/digitalocean/godo/account.go @@ -1,10 +1,12 @@ package godo +import "context" + // AccountService is an interface for interfacing with the Account // endpoints of the DigitalOcean API // See: https://developers.digitalocean.com/documentation/v2/#account type AccountService interface { - Get() (*Account, *Response, error) + Get(context.Context) (*Account, *Response, error) } // AccountServiceOp handles communication with the Account related methods of @@ -35,10 +37,11 @@ func (r Account) String() string { } // Get DigitalOcean account info -func (s *AccountServiceOp) Get() (*Account, *Response, error) { +func (s *AccountServiceOp) Get(ctx context.Context) (*Account, *Response, error) { + path := "v2/account" - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } diff --git a/vendor/github.com/digitalocean/godo/action.go b/vendor/github.com/digitalocean/godo/action.go index e19493b92..9baef2147 100644 --- a/vendor/github.com/digitalocean/godo/action.go +++ b/vendor/github.com/digitalocean/godo/action.go @@ -1,6 +1,9 @@ package godo -import "fmt" +import ( + "context" + "fmt" +) const ( actionsBasePath = "v2/actions" @@ -15,8 +18,8 @@ const ( // ActionsService handles communction with action related methods of the // DigitalOcean API: https://developers.digitalocean.com/documentation/v2#actions type ActionsService interface { - List(*ListOptions) ([]Action, *Response, error) - Get(int) (*Action, *Response, error) + List(context.Context, *ListOptions) ([]Action, *Response, error) + Get(context.Context, int) (*Action, *Response, error) } // ActionsServiceOp handles communition with the image action related methods of the @@ -33,7 +36,7 @@ type actionsRoot struct { } type actionRoot struct { - Event Action `json:"action"` + Event *Action `json:"action"` } // Action represents a DigitalOcean Action @@ -50,14 +53,14 @@ type Action struct { } // List all actions -func (s *ActionsServiceOp) List(opt *ListOptions) ([]Action, *Response, error) { +func (s *ActionsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Action, *Response, error) { path := actionsBasePath path, err := addOptions(path, opt) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -75,13 +78,13 @@ func (s *ActionsServiceOp) List(opt *ListOptions) ([]Action, *Response, error) { } // Get an action by ID. -func (s *ActionsServiceOp) Get(id int) (*Action, *Response, error) { +func (s *ActionsServiceOp) Get(ctx context.Context, id int) (*Action, *Response, error) { if id < 1 { return nil, nil, NewArgError("id", "cannot be less than 1") } path := fmt.Sprintf("%s/%d", actionsBasePath, id) - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -92,7 +95,7 @@ func (s *ActionsServiceOp) Get(id int) (*Action, *Response, error) { return nil, resp, err } - return &root.Event, resp, err + return root.Event, resp, err } func (a Action) String() string { diff --git a/vendor/github.com/digitalocean/godo/certificates.go b/vendor/github.com/digitalocean/godo/certificates.go new file mode 100644 index 000000000..b48e80ec3 --- /dev/null +++ b/vendor/github.com/digitalocean/godo/certificates.go @@ -0,0 +1,120 @@ +package godo + +import ( + "context" + "path" +) + +const certificatesBasePath = "/v2/certificates" + +// CertificatesService is an interface for managing certificates with the DigitalOcean API. +// See: https://developers.digitalocean.com/documentation/v2/#certificates +type CertificatesService interface { + Get(context.Context, string) (*Certificate, *Response, error) + List(context.Context, *ListOptions) ([]Certificate, *Response, error) + Create(context.Context, *CertificateRequest) (*Certificate, *Response, error) + Delete(context.Context, string) (*Response, error) +} + +// Certificate represents a DigitalOcean certificate configuration. +type Certificate struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + NotAfter string `json:"not_after,omitempty"` + SHA1Fingerprint string `json:"sha1_fingerprint,omitempty"` + Created string `json:"created_at,omitempty"` +} + +// CertificateRequest represents configuration for a new certificate. +type CertificateRequest struct { + Name string `json:"name,omitempty"` + PrivateKey string `json:"private_key,omitempty"` + LeafCertificate string `json:"leaf_certificate,omitempty"` + CertificateChain string `json:"certificate_chain,omitempty"` +} + +type certificateRoot struct { + Certificate *Certificate `json:"certificate"` +} + +type certificatesRoot struct { + Certificates []Certificate `json:"certificates"` + Links *Links `json:"links"` +} + +// CertificatesServiceOp handles communication with certificates methods of the DigitalOcean API. +type CertificatesServiceOp struct { + client *Client +} + +var _ CertificatesService = &CertificatesServiceOp{} + +// Get an existing certificate by its identifier. +func (c *CertificatesServiceOp) Get(ctx context.Context, cID string) (*Certificate, *Response, error) { + urlStr := path.Join(certificatesBasePath, cID) + + req, err := c.client.NewRequest(ctx, "GET", urlStr, nil) + if err != nil { + return nil, nil, err + } + + root := new(certificateRoot) + resp, err := c.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.Certificate, resp, nil +} + +// List all certificates. +func (c *CertificatesServiceOp) List(ctx context.Context, opt *ListOptions) ([]Certificate, *Response, error) { + urlStr, err := addOptions(certificatesBasePath, opt) + if err != nil { + return nil, nil, err + } + + req, err := c.client.NewRequest(ctx, "GET", urlStr, nil) + if err != nil { + return nil, nil, err + } + + root := new(certificatesRoot) + resp, err := c.client.Do(req, root) + if err != nil { + return nil, resp, err + } + if l := root.Links; l != nil { + resp.Links = l + } + + return root.Certificates, resp, nil +} + +// Create a new certificate with provided configuration. +func (c *CertificatesServiceOp) Create(ctx context.Context, cr *CertificateRequest) (*Certificate, *Response, error) { + req, err := c.client.NewRequest(ctx, "POST", certificatesBasePath, cr) + if err != nil { + return nil, nil, err + } + + root := new(certificateRoot) + resp, err := c.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.Certificate, resp, nil +} + +// Delete a certificate by its identifier. +func (c *CertificatesServiceOp) Delete(ctx context.Context, cID string) (*Response, error) { + urlStr := path.Join(certificatesBasePath, cID) + + req, err := c.client.NewRequest(ctx, "DELETE", urlStr, nil) + if err != nil { + return nil, err + } + + return c.client.Do(req, nil) +} diff --git a/vendor/github.com/digitalocean/godo/domains.go b/vendor/github.com/digitalocean/godo/domains.go index 31de17868..fdc2a84c6 100644 --- a/vendor/github.com/digitalocean/godo/domains.go +++ b/vendor/github.com/digitalocean/godo/domains.go @@ -1,6 +1,9 @@ package godo -import "fmt" +import ( + "context" + "fmt" +) const domainsBasePath = "v2/domains" @@ -8,16 +11,16 @@ const domainsBasePath = "v2/domains" // See: https://developers.digitalocean.com/documentation/v2#domains and // https://developers.digitalocean.com/documentation/v2#domain-records type DomainsService interface { - List(*ListOptions) ([]Domain, *Response, error) - Get(string) (*Domain, *Response, error) - Create(*DomainCreateRequest) (*Domain, *Response, error) - Delete(string) (*Response, error) + List(context.Context, *ListOptions) ([]Domain, *Response, error) + Get(context.Context, string) (*Domain, *Response, error) + Create(context.Context, *DomainCreateRequest) (*Domain, *Response, error) + Delete(context.Context, string) (*Response, error) - Records(string, *ListOptions) ([]DomainRecord, *Response, error) - Record(string, int) (*DomainRecord, *Response, error) - DeleteRecord(string, int) (*Response, error) - EditRecord(string, int, *DomainRecordEditRequest) (*DomainRecord, *Response, error) - CreateRecord(string, *DomainRecordEditRequest) (*DomainRecord, *Response, error) + Records(context.Context, string, *ListOptions) ([]DomainRecord, *Response, error) + Record(context.Context, string, int) (*DomainRecord, *Response, error) + DeleteRecord(context.Context, string, int) (*Response, error) + EditRecord(context.Context, string, int, *DomainRecordEditRequest) (*DomainRecord, *Response, error) + CreateRecord(context.Context, string, *DomainRecordEditRequest) (*DomainRecord, *Response, error) } // DomainsServiceOp handles communication with the domain related methods of the @@ -88,14 +91,14 @@ func (d Domain) String() string { } // List all domains. -func (s DomainsServiceOp) List(opt *ListOptions) ([]Domain, *Response, error) { +func (s DomainsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Domain, *Response, error) { path := domainsBasePath path, err := addOptions(path, opt) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -113,14 +116,14 @@ func (s DomainsServiceOp) List(opt *ListOptions) ([]Domain, *Response, error) { } // Get individual domain. It requires a non-empty domain name. -func (s *DomainsServiceOp) Get(name string) (*Domain, *Response, error) { +func (s *DomainsServiceOp) Get(ctx context.Context, name string) (*Domain, *Response, error) { if len(name) < 1 { return nil, nil, NewArgError("name", "cannot be an empty string") } path := fmt.Sprintf("%s/%s", domainsBasePath, name) - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -135,14 +138,14 @@ func (s *DomainsServiceOp) Get(name string) (*Domain, *Response, error) { } // Create a new domain -func (s *DomainsServiceOp) Create(createRequest *DomainCreateRequest) (*Domain, *Response, error) { +func (s *DomainsServiceOp) Create(ctx context.Context, createRequest *DomainCreateRequest) (*Domain, *Response, error) { if createRequest == nil { return nil, nil, NewArgError("createRequest", "cannot be nil") } path := domainsBasePath - req, err := s.client.NewRequest("POST", path, createRequest) + req, err := s.client.NewRequest(ctx, "POST", path, createRequest) if err != nil { return nil, nil, err } @@ -156,14 +159,14 @@ func (s *DomainsServiceOp) Create(createRequest *DomainCreateRequest) (*Domain, } // Delete domain -func (s *DomainsServiceOp) Delete(name string) (*Response, error) { +func (s *DomainsServiceOp) Delete(ctx context.Context, name string) (*Response, error) { if len(name) < 1 { return nil, NewArgError("name", "cannot be an empty string") } path := fmt.Sprintf("%s/%s", domainsBasePath, name) - req, err := s.client.NewRequest("DELETE", path, nil) + req, err := s.client.NewRequest(ctx, "DELETE", path, nil) if err != nil { return nil, err } @@ -184,7 +187,7 @@ func (d DomainRecordEditRequest) String() string { } // Records returns a slice of DomainRecords for a domain -func (s *DomainsServiceOp) Records(domain string, opt *ListOptions) ([]DomainRecord, *Response, error) { +func (s *DomainsServiceOp) Records(ctx context.Context, domain string, opt *ListOptions) ([]DomainRecord, *Response, error) { if len(domain) < 1 { return nil, nil, NewArgError("domain", "cannot be an empty string") } @@ -195,7 +198,7 @@ func (s *DomainsServiceOp) Records(domain string, opt *ListOptions) ([]DomainRec return nil, nil, err } - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -213,7 +216,7 @@ func (s *DomainsServiceOp) Records(domain string, opt *ListOptions) ([]DomainRec } // Record returns the record id from a domain -func (s *DomainsServiceOp) Record(domain string, id int) (*DomainRecord, *Response, error) { +func (s *DomainsServiceOp) Record(ctx context.Context, domain string, id int) (*DomainRecord, *Response, error) { if len(domain) < 1 { return nil, nil, NewArgError("domain", "cannot be an empty string") } @@ -224,7 +227,7 @@ func (s *DomainsServiceOp) Record(domain string, id int) (*DomainRecord, *Respon path := fmt.Sprintf("%s/%s/records/%d", domainsBasePath, domain, id) - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -239,7 +242,7 @@ func (s *DomainsServiceOp) Record(domain string, id int) (*DomainRecord, *Respon } // DeleteRecord deletes a record from a domain identified by id -func (s *DomainsServiceOp) DeleteRecord(domain string, id int) (*Response, error) { +func (s *DomainsServiceOp) DeleteRecord(ctx context.Context, domain string, id int) (*Response, error) { if len(domain) < 1 { return nil, NewArgError("domain", "cannot be an empty string") } @@ -250,7 +253,7 @@ func (s *DomainsServiceOp) DeleteRecord(domain string, id int) (*Response, error path := fmt.Sprintf("%s/%s/records/%d", domainsBasePath, domain, id) - req, err := s.client.NewRequest("DELETE", path, nil) + req, err := s.client.NewRequest(ctx, "DELETE", path, nil) if err != nil { return nil, err } @@ -261,7 +264,7 @@ func (s *DomainsServiceOp) DeleteRecord(domain string, id int) (*Response, error } // EditRecord edits a record using a DomainRecordEditRequest -func (s *DomainsServiceOp) EditRecord( +func (s *DomainsServiceOp) EditRecord(ctx context.Context, domain string, id int, editRequest *DomainRecordEditRequest, @@ -280,7 +283,7 @@ func (s *DomainsServiceOp) EditRecord( path := fmt.Sprintf("%s/%s/records/%d", domainsBasePath, domain, id) - req, err := s.client.NewRequest("PUT", path, editRequest) + req, err := s.client.NewRequest(ctx, "PUT", path, editRequest) if err != nil { return nil, nil, err } @@ -295,7 +298,7 @@ func (s *DomainsServiceOp) EditRecord( } // CreateRecord creates a record using a DomainRecordEditRequest -func (s *DomainsServiceOp) CreateRecord( +func (s *DomainsServiceOp) CreateRecord(ctx context.Context, domain string, createRequest *DomainRecordEditRequest) (*DomainRecord, *Response, error) { if len(domain) < 1 { @@ -307,7 +310,7 @@ func (s *DomainsServiceOp) CreateRecord( } path := fmt.Sprintf("%s/%s/records", domainsBasePath, domain) - req, err := s.client.NewRequest("POST", path, createRequest) + req, err := s.client.NewRequest(ctx, "POST", path, createRequest) if err != nil { return nil, nil, err diff --git a/vendor/github.com/digitalocean/godo/droplet_actions.go b/vendor/github.com/digitalocean/godo/droplet_actions.go index 7012aee7f..cd8b94e2f 100644 --- a/vendor/github.com/digitalocean/godo/droplet_actions.go +++ b/vendor/github.com/digitalocean/godo/droplet_actions.go @@ -1,6 +1,7 @@ package godo import ( + "context" "fmt" "net/url" ) @@ -8,33 +9,42 @@ import ( // ActionRequest reprents DigitalOcean Action Request type ActionRequest map[string]interface{} -// DropletActionsService is an interface for interfacing with the droplet actions +// DropletActionsService is an interface for interfacing with the Droplet actions // endpoints of the DigitalOcean API // See: https://developers.digitalocean.com/documentation/v2#droplet-actions type DropletActionsService interface { - Shutdown(int) (*Action, *Response, error) - PowerOff(int) (*Action, *Response, error) - PowerOn(int) (*Action, *Response, error) - PowerCycle(int) (*Action, *Response, error) - Reboot(int) (*Action, *Response, error) - Restore(int, int) (*Action, *Response, error) - Resize(int, string, bool) (*Action, *Response, error) - Rename(int, string) (*Action, *Response, error) - Snapshot(int, string) (*Action, *Response, error) - EnableBackups(int) (*Action, *Response, error) - DisableBackups(int) (*Action, *Response, error) - PasswordReset(int) (*Action, *Response, error) - RebuildByImageID(int, int) (*Action, *Response, error) - RebuildByImageSlug(int, string) (*Action, *Response, error) - ChangeKernel(int, int) (*Action, *Response, error) - EnableIPv6(int) (*Action, *Response, error) - EnablePrivateNetworking(int) (*Action, *Response, error) - Upgrade(int) (*Action, *Response, error) - Get(int, int) (*Action, *Response, error) - GetByURI(string) (*Action, *Response, error) + Shutdown(context.Context, int) (*Action, *Response, error) + ShutdownByTag(context.Context, string) (*Action, *Response, error) + PowerOff(context.Context, int) (*Action, *Response, error) + PowerOffByTag(context.Context, string) (*Action, *Response, error) + PowerOn(context.Context, int) (*Action, *Response, error) + PowerOnByTag(context.Context, string) (*Action, *Response, error) + PowerCycle(context.Context, int) (*Action, *Response, error) + PowerCycleByTag(context.Context, string) (*Action, *Response, error) + Reboot(context.Context, int) (*Action, *Response, error) + Restore(context.Context, int, int) (*Action, *Response, error) + Resize(context.Context, int, string, bool) (*Action, *Response, error) + Rename(context.Context, int, string) (*Action, *Response, error) + Snapshot(context.Context, int, string) (*Action, *Response, error) + SnapshotByTag(context.Context, string, string) (*Action, *Response, error) + EnableBackups(context.Context, int) (*Action, *Response, error) + EnableBackupsByTag(context.Context, string) (*Action, *Response, error) + DisableBackups(context.Context, int) (*Action, *Response, error) + DisableBackupsByTag(context.Context, string) (*Action, *Response, error) + PasswordReset(context.Context, int) (*Action, *Response, error) + RebuildByImageID(context.Context, int, int) (*Action, *Response, error) + RebuildByImageSlug(context.Context, int, string) (*Action, *Response, error) + ChangeKernel(context.Context, int, int) (*Action, *Response, error) + EnableIPv6(context.Context, int) (*Action, *Response, error) + EnableIPv6ByTag(context.Context, string) (*Action, *Response, error) + EnablePrivateNetworking(context.Context, int) (*Action, *Response, error) + EnablePrivateNetworkingByTag(context.Context, string) (*Action, *Response, error) + Upgrade(context.Context, int) (*Action, *Response, error) + Get(context.Context, int, int) (*Action, *Response, error) + GetByURI(context.Context, string) (*Action, *Response, error) } -// DropletActionsServiceOp handles communication with the droplet action related +// DropletActionsServiceOp handles communication with the Droplet action related // methods of the DigitalOcean API. type DropletActionsServiceOp struct { client *Client @@ -43,131 +53,189 @@ type DropletActionsServiceOp struct { var _ DropletActionsService = &DropletActionsServiceOp{} // Shutdown a Droplet -func (s *DropletActionsServiceOp) Shutdown(id int) (*Action, *Response, error) { +func (s *DropletActionsServiceOp) Shutdown(ctx context.Context, id int) (*Action, *Response, error) { request := &ActionRequest{"type": "shutdown"} - return s.doAction(id, request) + return s.doAction(ctx, id, request) +} + +// ShutdownByTag shuts down Droplets matched by a Tag. +func (s *DropletActionsServiceOp) ShutdownByTag(ctx context.Context, tag string) (*Action, *Response, error) { + request := &ActionRequest{"type": "shutdown"} + return s.doActionByTag(ctx, tag, request) } // PowerOff a Droplet -func (s *DropletActionsServiceOp) PowerOff(id int) (*Action, *Response, error) { +func (s *DropletActionsServiceOp) PowerOff(ctx context.Context, id int) (*Action, *Response, error) { request := &ActionRequest{"type": "power_off"} - return s.doAction(id, request) + return s.doAction(ctx, id, request) +} + +// PowerOffByTag powers off Droplets matched by a Tag. +func (s *DropletActionsServiceOp) PowerOffByTag(ctx context.Context, tag string) (*Action, *Response, error) { + request := &ActionRequest{"type": "power_off"} + return s.doActionByTag(ctx, tag, request) } // PowerOn a Droplet -func (s *DropletActionsServiceOp) PowerOn(id int) (*Action, *Response, error) { +func (s *DropletActionsServiceOp) PowerOn(ctx context.Context, id int) (*Action, *Response, error) { request := &ActionRequest{"type": "power_on"} - return s.doAction(id, request) + return s.doAction(ctx, id, request) +} + +// PowerOnByTag powers on Droplets matched by a Tag. +func (s *DropletActionsServiceOp) PowerOnByTag(ctx context.Context, tag string) (*Action, *Response, error) { + request := &ActionRequest{"type": "power_on"} + return s.doActionByTag(ctx, tag, request) } // PowerCycle a Droplet -func (s *DropletActionsServiceOp) PowerCycle(id int) (*Action, *Response, error) { +func (s *DropletActionsServiceOp) PowerCycle(ctx context.Context, id int) (*Action, *Response, error) { request := &ActionRequest{"type": "power_cycle"} - return s.doAction(id, request) + return s.doAction(ctx, id, request) +} + +// PowerCycleByTag power cycles Droplets matched by a Tag. +func (s *DropletActionsServiceOp) PowerCycleByTag(ctx context.Context, tag string) (*Action, *Response, error) { + request := &ActionRequest{"type": "power_cycle"} + return s.doActionByTag(ctx, tag, request) } // Reboot a Droplet -func (s *DropletActionsServiceOp) Reboot(id int) (*Action, *Response, error) { +func (s *DropletActionsServiceOp) Reboot(ctx context.Context, id int) (*Action, *Response, error) { request := &ActionRequest{"type": "reboot"} - return s.doAction(id, request) + return s.doAction(ctx, id, request) } // Restore an image to a Droplet -func (s *DropletActionsServiceOp) Restore(id, imageID int) (*Action, *Response, error) { +func (s *DropletActionsServiceOp) Restore(ctx context.Context, id, imageID int) (*Action, *Response, error) { requestType := "restore" request := &ActionRequest{ "type": requestType, "image": float64(imageID), } - return s.doAction(id, request) + return s.doAction(ctx, id, request) } // Resize a Droplet -func (s *DropletActionsServiceOp) Resize(id int, sizeSlug string, resizeDisk bool) (*Action, *Response, error) { +func (s *DropletActionsServiceOp) Resize(ctx context.Context, id int, sizeSlug string, resizeDisk bool) (*Action, *Response, error) { requestType := "resize" request := &ActionRequest{ "type": requestType, "size": sizeSlug, "disk": resizeDisk, } - return s.doAction(id, request) + return s.doAction(ctx, id, request) } // Rename a Droplet -func (s *DropletActionsServiceOp) Rename(id int, name string) (*Action, *Response, error) { +func (s *DropletActionsServiceOp) Rename(ctx context.Context, id int, name string) (*Action, *Response, error) { requestType := "rename" request := &ActionRequest{ "type": requestType, "name": name, } - return s.doAction(id, request) + return s.doAction(ctx, id, request) } // Snapshot a Droplet. -func (s *DropletActionsServiceOp) Snapshot(id int, name string) (*Action, *Response, error) { +func (s *DropletActionsServiceOp) Snapshot(ctx context.Context, id int, name string) (*Action, *Response, error) { requestType := "snapshot" request := &ActionRequest{ "type": requestType, "name": name, } - return s.doAction(id, request) + return s.doAction(ctx, id, request) } -// EnableBackups enables backups for a droplet. -func (s *DropletActionsServiceOp) EnableBackups(id int) (*Action, *Response, error) { +// SnapshotByTag snapshots Droplets matched by a Tag. +func (s *DropletActionsServiceOp) SnapshotByTag(ctx context.Context, tag string, name string) (*Action, *Response, error) { + requestType := "snapshot" + request := &ActionRequest{ + "type": requestType, + "name": name, + } + return s.doActionByTag(ctx, tag, request) +} + +// EnableBackups enables backups for a Droplet. +func (s *DropletActionsServiceOp) EnableBackups(ctx context.Context, id int) (*Action, *Response, error) { request := &ActionRequest{"type": "enable_backups"} - return s.doAction(id, request) + return s.doAction(ctx, id, request) } -// DisableBackups disables backups for a droplet. -func (s *DropletActionsServiceOp) DisableBackups(id int) (*Action, *Response, error) { +// EnableBackupsByTag enables backups for Droplets matched by a Tag. +func (s *DropletActionsServiceOp) EnableBackupsByTag(ctx context.Context, tag string) (*Action, *Response, error) { + request := &ActionRequest{"type": "enable_backups"} + return s.doActionByTag(ctx, tag, request) +} + +// DisableBackups disables backups for a Droplet. +func (s *DropletActionsServiceOp) DisableBackups(ctx context.Context, id int) (*Action, *Response, error) { request := &ActionRequest{"type": "disable_backups"} - return s.doAction(id, request) + return s.doAction(ctx, id, request) } -// PasswordReset resets the password for a droplet. -func (s *DropletActionsServiceOp) PasswordReset(id int) (*Action, *Response, error) { +// DisableBackupsByTag disables backups for Droplet matched by a Tag. +func (s *DropletActionsServiceOp) DisableBackupsByTag(ctx context.Context, tag string) (*Action, *Response, error) { + request := &ActionRequest{"type": "disable_backups"} + return s.doActionByTag(ctx, tag, request) +} + +// PasswordReset resets the password for a Droplet. +func (s *DropletActionsServiceOp) PasswordReset(ctx context.Context, id int) (*Action, *Response, error) { request := &ActionRequest{"type": "password_reset"} - return s.doAction(id, request) + return s.doAction(ctx, id, request) } -// RebuildByImageID rebuilds a droplet droplet from an image with a given id. -func (s *DropletActionsServiceOp) RebuildByImageID(id, imageID int) (*Action, *Response, error) { +// RebuildByImageID rebuilds a Droplet from an image with a given id. +func (s *DropletActionsServiceOp) RebuildByImageID(ctx context.Context, id, imageID int) (*Action, *Response, error) { request := &ActionRequest{"type": "rebuild", "image": imageID} - return s.doAction(id, request) + return s.doAction(ctx, id, request) } -// RebuildByImageSlug rebuilds a droplet from an image with a given slug. -func (s *DropletActionsServiceOp) RebuildByImageSlug(id int, slug string) (*Action, *Response, error) { +// RebuildByImageSlug rebuilds a Droplet from an Image matched by a given Slug. +func (s *DropletActionsServiceOp) RebuildByImageSlug(ctx context.Context, id int, slug string) (*Action, *Response, error) { request := &ActionRequest{"type": "rebuild", "image": slug} - return s.doAction(id, request) + return s.doAction(ctx, id, request) } -// ChangeKernel changes the kernel for a droplet. -func (s *DropletActionsServiceOp) ChangeKernel(id, kernelID int) (*Action, *Response, error) { +// ChangeKernel changes the kernel for a Droplet. +func (s *DropletActionsServiceOp) ChangeKernel(ctx context.Context, id, kernelID int) (*Action, *Response, error) { request := &ActionRequest{"type": "change_kernel", "kernel": kernelID} - return s.doAction(id, request) + return s.doAction(ctx, id, request) } -// EnableIPv6 enables IPv6 for a droplet. -func (s *DropletActionsServiceOp) EnableIPv6(id int) (*Action, *Response, error) { +// EnableIPv6 enables IPv6 for a Droplet. +func (s *DropletActionsServiceOp) EnableIPv6(ctx context.Context, id int) (*Action, *Response, error) { request := &ActionRequest{"type": "enable_ipv6"} - return s.doAction(id, request) + return s.doAction(ctx, id, request) } -// EnablePrivateNetworking enables private networking for a droplet. -func (s *DropletActionsServiceOp) EnablePrivateNetworking(id int) (*Action, *Response, error) { +// EnableIPv6ByTag enables IPv6 for Droplets matched by a Tag. +func (s *DropletActionsServiceOp) EnableIPv6ByTag(ctx context.Context, tag string) (*Action, *Response, error) { + request := &ActionRequest{"type": "enable_ipv6"} + return s.doActionByTag(ctx, tag, request) +} + +// EnablePrivateNetworking enables private networking for a Droplet. +func (s *DropletActionsServiceOp) EnablePrivateNetworking(ctx context.Context, id int) (*Action, *Response, error) { request := &ActionRequest{"type": "enable_private_networking"} - return s.doAction(id, request) + return s.doAction(ctx, id, request) } -// Upgrade a droplet. -func (s *DropletActionsServiceOp) Upgrade(id int) (*Action, *Response, error) { +// EnablePrivateNetworkingByTag enables private networking for Droplets matched by a Tag. +func (s *DropletActionsServiceOp) EnablePrivateNetworkingByTag(ctx context.Context, tag string) (*Action, *Response, error) { + request := &ActionRequest{"type": "enable_private_networking"} + return s.doActionByTag(ctx, tag, request) +} + +// Upgrade a Droplet. +func (s *DropletActionsServiceOp) Upgrade(ctx context.Context, id int) (*Action, *Response, error) { request := &ActionRequest{"type": "upgrade"} - return s.doAction(id, request) + return s.doAction(ctx, id, request) } -func (s *DropletActionsServiceOp) doAction(id int, request *ActionRequest) (*Action, *Response, error) { +func (s *DropletActionsServiceOp) doAction(ctx context.Context, id int, request *ActionRequest) (*Action, *Response, error) { if id < 1 { return nil, nil, NewArgError("id", "cannot be less than 1") } @@ -178,7 +246,7 @@ func (s *DropletActionsServiceOp) doAction(id int, request *ActionRequest) (*Act path := dropletActionPath(id) - req, err := s.client.NewRequest("POST", path, request) + req, err := s.client.NewRequest(ctx, "POST", path, request) if err != nil { return nil, nil, err } @@ -189,11 +257,36 @@ func (s *DropletActionsServiceOp) doAction(id int, request *ActionRequest) (*Act return nil, resp, err } - return &root.Event, resp, err + return root.Event, resp, err } -// Get an action for a particular droplet by id. -func (s *DropletActionsServiceOp) Get(dropletID, actionID int) (*Action, *Response, error) { +func (s *DropletActionsServiceOp) doActionByTag(ctx context.Context, tag string, request *ActionRequest) (*Action, *Response, error) { + if tag == "" { + return nil, nil, NewArgError("tag", "cannot be empty") + } + + if request == nil { + return nil, nil, NewArgError("request", "request can't be nil") + } + + path := dropletActionPathByTag(tag) + + req, err := s.client.NewRequest(ctx, "POST", path, request) + if err != nil { + return nil, nil, err + } + + root := new(actionRoot) + resp, err := s.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.Event, resp, err +} + +// Get an action for a particular Droplet by id. +func (s *DropletActionsServiceOp) Get(ctx context.Context, dropletID, actionID int) (*Action, *Response, error) { if dropletID < 1 { return nil, nil, NewArgError("dropletID", "cannot be less than 1") } @@ -203,22 +296,22 @@ func (s *DropletActionsServiceOp) Get(dropletID, actionID int) (*Action, *Respon } path := fmt.Sprintf("%s/%d", dropletActionPath(dropletID), actionID) - return s.get(path) + return s.get(ctx, path) } -// GetByURI gets an action for a particular droplet by id. -func (s *DropletActionsServiceOp) GetByURI(rawurl string) (*Action, *Response, error) { +// GetByURI gets an action for a particular Droplet by id. +func (s *DropletActionsServiceOp) GetByURI(ctx context.Context, rawurl string) (*Action, *Response, error) { u, err := url.Parse(rawurl) if err != nil { return nil, nil, err } - return s.get(u.Path) + return s.get(ctx, u.Path) } -func (s *DropletActionsServiceOp) get(path string) (*Action, *Response, error) { - req, err := s.client.NewRequest("GET", path, nil) +func (s *DropletActionsServiceOp) get(ctx context.Context, path string) (*Action, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -229,10 +322,14 @@ func (s *DropletActionsServiceOp) get(path string) (*Action, *Response, error) { return nil, resp, err } - return &root.Event, resp, err + return root.Event, resp, err } func dropletActionPath(dropletID int) string { return fmt.Sprintf("v2/droplets/%d/actions", dropletID) } + +func dropletActionPathByTag(tag string) string { + return fmt.Sprintf("v2/droplets/actions?tag_name=%s", tag) +} diff --git a/vendor/github.com/digitalocean/godo/droplets.go b/vendor/github.com/digitalocean/godo/droplets.go index 5ba7c27d4..b145d9805 100644 --- a/vendor/github.com/digitalocean/godo/droplets.go +++ b/vendor/github.com/digitalocean/godo/droplets.go @@ -1,6 +1,7 @@ package godo import ( + "context" "encoding/json" "errors" "fmt" @@ -10,23 +11,25 @@ const dropletBasePath = "v2/droplets" var errNoNetworks = errors.New("no networks have been defined") -// DropletsService is an interface for interfacing with the droplet +// DropletsService is an interface for interfacing with the Droplet // endpoints of the DigitalOcean API // See: https://developers.digitalocean.com/documentation/v2#droplets type DropletsService interface { - List(*ListOptions) ([]Droplet, *Response, error) - Get(int) (*Droplet, *Response, error) - Create(*DropletCreateRequest) (*Droplet, *Response, error) - CreateMultiple(*DropletMultiCreateRequest) ([]Droplet, *Response, error) - Delete(int) (*Response, error) - Kernels(int, *ListOptions) ([]Kernel, *Response, error) - Snapshots(int, *ListOptions) ([]Image, *Response, error) - Backups(int, *ListOptions) ([]Image, *Response, error) - Actions(int, *ListOptions) ([]Action, *Response, error) - Neighbors(int) ([]Droplet, *Response, error) + List(context.Context, *ListOptions) ([]Droplet, *Response, error) + ListByTag(context.Context, string, *ListOptions) ([]Droplet, *Response, error) + Get(context.Context, int) (*Droplet, *Response, error) + Create(context.Context, *DropletCreateRequest) (*Droplet, *Response, error) + CreateMultiple(context.Context, *DropletMultiCreateRequest) ([]Droplet, *Response, error) + Delete(context.Context, int) (*Response, error) + DeleteByTag(context.Context, string) (*Response, error) + Kernels(context.Context, int, *ListOptions) ([]Kernel, *Response, error) + Snapshots(context.Context, int, *ListOptions) ([]Image, *Response, error) + Backups(context.Context, int, *ListOptions) ([]Image, *Response, error) + Actions(context.Context, int, *ListOptions) ([]Action, *Response, error) + Neighbors(context.Context, int) ([]Droplet, *Response, error) } -// DropletsServiceOp handles communication with the droplet related methods of the +// DropletsServiceOp handles communication with the Droplet related methods of the // DigitalOcean API. type DropletsServiceOp struct { client *Client @@ -36,23 +39,26 @@ var _ DropletsService = &DropletsServiceOp{} // Droplet represents a DigitalOcean Droplet type Droplet struct { - ID int `json:"id,float64,omitempty"` - Name string `json:"name,omitempty"` - Memory int `json:"memory,omitempty"` - Vcpus int `json:"vcpus,omitempty"` - Disk int `json:"disk,omitempty"` - Region *Region `json:"region,omitempty"` - Image *Image `json:"image,omitempty"` - Size *Size `json:"size,omitempty"` - SizeSlug string `json:"size_slug,omitempty"` - BackupIDs []int `json:"backup_ids,omitempty"` - SnapshotIDs []int `json:"snapshot_ids,omitempty"` - Locked bool `json:"locked,bool,omitempty"` - Status string `json:"status,omitempty"` - Networks *Networks `json:"networks,omitempty"` - ActionIDs []int `json:"action_ids,omitempty"` - Created string `json:"created_at,omitempty"` - Kernel *Kernel `json:"kernel, omitempty"` + ID int `json:"id,float64,omitempty"` + Name string `json:"name,omitempty"` + Memory int `json:"memory,omitempty"` + Vcpus int `json:"vcpus,omitempty"` + Disk int `json:"disk,omitempty"` + Region *Region `json:"region,omitempty"` + Image *Image `json:"image,omitempty"` + Size *Size `json:"size,omitempty"` + SizeSlug string `json:"size_slug,omitempty"` + BackupIDs []int `json:"backup_ids,omitempty"` + NextBackupWindow *BackupWindow `json:"next_backup_window,omitempty"` + SnapshotIDs []int `json:"snapshot_ids,omitempty"` + Features []string `json:"features,omitempty"` + Locked bool `json:"locked,bool,omitempty"` + Status string `json:"status,omitempty"` + Networks *Networks `json:"networks,omitempty"` + Created string `json:"created_at,omitempty"` + Kernel *Kernel `json:"kernel,omitempty"` + Tags []string `json:"tags,omitempty"` + VolumeIDs []string `json:"volume_ids"` } // PublicIPv4 returns the public IPv4 address for the Droplet. @@ -85,15 +91,15 @@ func (d *Droplet) PrivateIPv4() (string, error) { return "", nil } -// PublicIPv6 returns the private IPv6 address for the Droplet. +// PublicIPv6 returns the public IPv6 address for the Droplet. func (d *Droplet) PublicIPv6() (string, error) { if d.Networks == nil { return "", errNoNetworks } - for _, v4 := range d.Networks.V6 { - if v4.Type == "public" { - return v4.IPAddress, nil + for _, v6 := range d.Networks.V6 { + if v6.Type == "public" { + return v6.IPAddress, nil } } @@ -107,6 +113,12 @@ type Kernel struct { Version string `json:"version,omitempty"` } +// BackupWindow object +type BackupWindow struct { + Start *Timestamp `json:"start,omitempty"` + End *Timestamp `json:"end,omitempty"` +} + // Convert Droplet to a string func (d Droplet) String() string { return Stringify(d) @@ -128,7 +140,7 @@ type kernelsRoot struct { Links *Links `json:"links"` } -type snapshotsRoot struct { +type dropletSnapshotsRoot struct { Snapshots []Image `json:"snapshots,omitempty"` Links *Links `json:"links"` } @@ -154,6 +166,27 @@ func (d DropletCreateImage) MarshalJSON() ([]byte, error) { return json.Marshal(d.ID) } +// DropletCreateVolume identifies a volume to attach for the create request. It +// prefers Name over ID, +type DropletCreateVolume struct { + ID string + Name string +} + +// MarshalJSON returns an object with either the name or id of the volume. It +// returns the id if the name is empty. +func (d DropletCreateVolume) MarshalJSON() ([]byte, error) { + if d.Name != "" { + return json.Marshal(struct { + Name string `json:"name"` + }{Name: d.Name}) + } + + return json.Marshal(struct { + ID string `json:"id"` + }{ID: d.ID}) +} + // DropletCreateSSHKey identifies a SSH Key for the create request. It prefers fingerprint over ID. type DropletCreateSSHKey struct { ID int @@ -170,7 +203,7 @@ func (d DropletCreateSSHKey) MarshalJSON() ([]byte, error) { return json.Marshal(d.ID) } -// DropletCreateRequest represents a request to create a droplet. +// DropletCreateRequest represents a request to create a Droplet. type DropletCreateRequest struct { Name string `json:"name"` Region string `json:"region"` @@ -180,10 +213,13 @@ type DropletCreateRequest struct { Backups bool `json:"backups"` IPv6 bool `json:"ipv6"` PrivateNetworking bool `json:"private_networking"` + Monitoring bool `json:"monitoring"` UserData string `json:"user_data,omitempty"` + Volumes []DropletCreateVolume `json:"volumes,omitempty"` + Tags []string `json:"tags"` } -// DropletMultiCreateRequest is a request to create multiple droplets. +// DropletMultiCreateRequest is a request to create multiple Droplets. type DropletMultiCreateRequest struct { Names []string `json:"names"` Region string `json:"region"` @@ -193,7 +229,9 @@ type DropletMultiCreateRequest struct { Backups bool `json:"backups"` IPv6 bool `json:"ipv6"` PrivateNetworking bool `json:"private_networking"` + Monitoring bool `json:"monitoring"` UserData string `json:"user_data,omitempty"` + Tags []string `json:"tags"` } func (d DropletCreateRequest) String() string { @@ -204,13 +242,13 @@ func (d DropletMultiCreateRequest) String() string { return Stringify(d) } -// Networks represents the droplet's networks +// Networks represents the Droplet's Networks. type Networks struct { V4 []NetworkV4 `json:"v4,omitempty"` V6 []NetworkV6 `json:"v6,omitempty"` } -// NetworkV4 represents a DigitalOcean IPv4 Network +// NetworkV4 represents a DigitalOcean IPv4 Network. type NetworkV4 struct { IPAddress string `json:"ip_address,omitempty"` Netmask string `json:"netmask,omitempty"` @@ -234,40 +272,56 @@ func (n NetworkV6) String() string { return Stringify(n) } -// List all droplets -func (s *DropletsServiceOp) List(opt *ListOptions) ([]Droplet, *Response, error) { +// Performs a list request given a path. +func (s *DropletsServiceOp) list(ctx context.Context, path string) ([]Droplet, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", path, nil) + if err != nil { + return nil, nil, err + } + + root := new(dropletsRoot) + resp, err := s.client.Do(req, root) + if err != nil { + return nil, resp, err + } + if l := root.Links; l != nil { + resp.Links = l + } + + return root.Droplets, resp, err +} + +// List all Droplets. +func (s *DropletsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Droplet, *Response, error) { path := dropletBasePath path, err := addOptions(path, opt) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", path, nil) + return s.list(ctx, path) +} + +// ListByTag lists all Droplets matched by a Tag. +func (s *DropletsServiceOp) ListByTag(ctx context.Context, tag string, opt *ListOptions) ([]Droplet, *Response, error) { + path := fmt.Sprintf("%s?tag_name=%s", dropletBasePath, tag) + path, err := addOptions(path, opt) if err != nil { return nil, nil, err } - root := new(dropletsRoot) - resp, err := s.client.Do(req, root) - if err != nil { - return nil, resp, err - } - if l := root.Links; l != nil { - resp.Links = l - } - - return root.Droplets, resp, err + return s.list(ctx, path) } -// Get individual droplet -func (s *DropletsServiceOp) Get(dropletID int) (*Droplet, *Response, error) { +// Get individual Droplet. +func (s *DropletsServiceOp) Get(ctx context.Context, dropletID int) (*Droplet, *Response, error) { if dropletID < 1 { return nil, nil, NewArgError("dropletID", "cannot be less than 1") } path := fmt.Sprintf("%s/%d", dropletBasePath, dropletID) - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -281,15 +335,15 @@ func (s *DropletsServiceOp) Get(dropletID int) (*Droplet, *Response, error) { return root.Droplet, resp, err } -// Create droplet -func (s *DropletsServiceOp) Create(createRequest *DropletCreateRequest) (*Droplet, *Response, error) { +// Create Droplet +func (s *DropletsServiceOp) Create(ctx context.Context, createRequest *DropletCreateRequest) (*Droplet, *Response, error) { if createRequest == nil { return nil, nil, NewArgError("createRequest", "cannot be nil") } path := dropletBasePath - req, err := s.client.NewRequest("POST", path, createRequest) + req, err := s.client.NewRequest(ctx, "POST", path, createRequest) if err != nil { return nil, nil, err } @@ -306,15 +360,15 @@ func (s *DropletsServiceOp) Create(createRequest *DropletCreateRequest) (*Drople return root.Droplet, resp, err } -// CreateMultiple creates multiple droplets. -func (s *DropletsServiceOp) CreateMultiple(createRequest *DropletMultiCreateRequest) ([]Droplet, *Response, error) { +// CreateMultiple creates multiple Droplets. +func (s *DropletsServiceOp) CreateMultiple(ctx context.Context, createRequest *DropletMultiCreateRequest) ([]Droplet, *Response, error) { if createRequest == nil { return nil, nil, NewArgError("createRequest", "cannot be nil") } path := dropletBasePath - req, err := s.client.NewRequest("POST", path, createRequest) + req, err := s.client.NewRequest(ctx, "POST", path, createRequest) if err != nil { return nil, nil, err } @@ -331,15 +385,9 @@ func (s *DropletsServiceOp) CreateMultiple(createRequest *DropletMultiCreateRequ return root.Droplets, resp, err } -// Delete droplet -func (s *DropletsServiceOp) Delete(dropletID int) (*Response, error) { - if dropletID < 1 { - return nil, NewArgError("dropletID", "cannot be less than 1") - } - - path := fmt.Sprintf("%s/%d", dropletBasePath, dropletID) - - req, err := s.client.NewRequest("DELETE", path, nil) +// Performs a delete request given a path +func (s *DropletsServiceOp) delete(ctx context.Context, path string) (*Response, error) { + req, err := s.client.NewRequest(ctx, "DELETE", path, nil) if err != nil { return nil, err } @@ -349,8 +397,30 @@ func (s *DropletsServiceOp) Delete(dropletID int) (*Response, error) { return resp, err } -// Kernels lists kernels available for a droplet. -func (s *DropletsServiceOp) Kernels(dropletID int, opt *ListOptions) ([]Kernel, *Response, error) { +// Delete Droplet. +func (s *DropletsServiceOp) Delete(ctx context.Context, dropletID int) (*Response, error) { + if dropletID < 1 { + return nil, NewArgError("dropletID", "cannot be less than 1") + } + + path := fmt.Sprintf("%s/%d", dropletBasePath, dropletID) + + return s.delete(ctx, path) +} + +// DeleteByTag deletes Droplets matched by a Tag. +func (s *DropletsServiceOp) DeleteByTag(ctx context.Context, tag string) (*Response, error) { + if tag == "" { + return nil, NewArgError("tag", "cannot be empty") + } + + path := fmt.Sprintf("%s?tag_name=%s", dropletBasePath, tag) + + return s.delete(ctx, path) +} + +// Kernels lists kernels available for a Droplet. +func (s *DropletsServiceOp) Kernels(ctx context.Context, dropletID int, opt *ListOptions) ([]Kernel, *Response, error) { if dropletID < 1 { return nil, nil, NewArgError("dropletID", "cannot be less than 1") } @@ -361,7 +431,7 @@ func (s *DropletsServiceOp) Kernels(dropletID int, opt *ListOptions) ([]Kernel, return nil, nil, err } - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -375,8 +445,8 @@ func (s *DropletsServiceOp) Kernels(dropletID int, opt *ListOptions) ([]Kernel, return root.Kernels, resp, err } -// Actions lists the actions for a droplet. -func (s *DropletsServiceOp) Actions(dropletID int, opt *ListOptions) ([]Action, *Response, error) { +// Actions lists the actions for a Droplet. +func (s *DropletsServiceOp) Actions(ctx context.Context, dropletID int, opt *ListOptions) ([]Action, *Response, error) { if dropletID < 1 { return nil, nil, NewArgError("dropletID", "cannot be less than 1") } @@ -387,7 +457,7 @@ func (s *DropletsServiceOp) Actions(dropletID int, opt *ListOptions) ([]Action, return nil, nil, err } - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -404,8 +474,8 @@ func (s *DropletsServiceOp) Actions(dropletID int, opt *ListOptions) ([]Action, return root.Actions, resp, err } -// Backups lists the backups for a droplet. -func (s *DropletsServiceOp) Backups(dropletID int, opt *ListOptions) ([]Image, *Response, error) { +// Backups lists the backups for a Droplet. +func (s *DropletsServiceOp) Backups(ctx context.Context, dropletID int, opt *ListOptions) ([]Image, *Response, error) { if dropletID < 1 { return nil, nil, NewArgError("dropletID", "cannot be less than 1") } @@ -416,7 +486,7 @@ func (s *DropletsServiceOp) Backups(dropletID int, opt *ListOptions) ([]Image, * return nil, nil, err } - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -433,8 +503,8 @@ func (s *DropletsServiceOp) Backups(dropletID int, opt *ListOptions) ([]Image, * return root.Backups, resp, err } -// Snapshots lists the snapshots available for a droplet. -func (s *DropletsServiceOp) Snapshots(dropletID int, opt *ListOptions) ([]Image, *Response, error) { +// Snapshots lists the snapshots available for a Droplet. +func (s *DropletsServiceOp) Snapshots(ctx context.Context, dropletID int, opt *ListOptions) ([]Image, *Response, error) { if dropletID < 1 { return nil, nil, NewArgError("dropletID", "cannot be less than 1") } @@ -445,12 +515,12 @@ func (s *DropletsServiceOp) Snapshots(dropletID int, opt *ListOptions) ([]Image, return nil, nil, err } - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } - root := new(snapshotsRoot) + root := new(dropletSnapshotsRoot) resp, err := s.client.Do(req, root) if err != nil { return nil, resp, err @@ -462,15 +532,15 @@ func (s *DropletsServiceOp) Snapshots(dropletID int, opt *ListOptions) ([]Image, return root.Snapshots, resp, err } -// Neighbors lists the neighbors for a droplet. -func (s *DropletsServiceOp) Neighbors(dropletID int) ([]Droplet, *Response, error) { +// Neighbors lists the neighbors for a Droplet. +func (s *DropletsServiceOp) Neighbors(ctx context.Context, dropletID int) ([]Droplet, *Response, error) { if dropletID < 1 { return nil, nil, NewArgError("dropletID", "cannot be less than 1") } path := fmt.Sprintf("%s/%d/neighbors", dropletBasePath, dropletID) - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -484,8 +554,8 @@ func (s *DropletsServiceOp) Neighbors(dropletID int) ([]Droplet, *Response, erro return root.Droplets, resp, err } -func (s *DropletsServiceOp) dropletActionStatus(uri string) (string, error) { - action, _, err := s.client.DropletActions.GetByURI(uri) +func (s *DropletsServiceOp) dropletActionStatus(ctx context.Context, uri string) (string, error) { + action, _, err := s.client.DropletActions.GetByURI(ctx, uri) if err != nil { return "", err diff --git a/vendor/github.com/digitalocean/godo/floating_ips.go b/vendor/github.com/digitalocean/godo/floating_ips.go index bad04b96c..13e983261 100644 --- a/vendor/github.com/digitalocean/godo/floating_ips.go +++ b/vendor/github.com/digitalocean/godo/floating_ips.go @@ -1,6 +1,9 @@ package godo -import "fmt" +import ( + "context" + "fmt" +) const floatingBasePath = "v2/floating_ips" @@ -8,10 +11,10 @@ const floatingBasePath = "v2/floating_ips" // endpoints of the Digital Ocean API. // See: https://developers.digitalocean.com/documentation/v2#floating-ips type FloatingIPsService interface { - List(*ListOptions) ([]FloatingIP, *Response, error) - Get(string) (*FloatingIP, *Response, error) - Create(*FloatingIPCreateRequest) (*FloatingIP, *Response, error) - Delete(string) (*Response, error) + List(context.Context, *ListOptions) ([]FloatingIP, *Response, error) + Get(context.Context, string) (*FloatingIP, *Response, error) + Create(context.Context, *FloatingIPCreateRequest) (*FloatingIP, *Response, error) + Delete(context.Context, string) (*Response, error) } // FloatingIPsServiceOp handles communication with the floating IPs related methods of the @@ -52,14 +55,14 @@ type FloatingIPCreateRequest struct { } // List all floating IPs. -func (f *FloatingIPsServiceOp) List(opt *ListOptions) ([]FloatingIP, *Response, error) { +func (f *FloatingIPsServiceOp) List(ctx context.Context, opt *ListOptions) ([]FloatingIP, *Response, error) { path := floatingBasePath path, err := addOptions(path, opt) if err != nil { return nil, nil, err } - req, err := f.client.NewRequest("GET", path, nil) + req, err := f.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -77,10 +80,10 @@ func (f *FloatingIPsServiceOp) List(opt *ListOptions) ([]FloatingIP, *Response, } // Get an individual floating IP. -func (f *FloatingIPsServiceOp) Get(ip string) (*FloatingIP, *Response, error) { +func (f *FloatingIPsServiceOp) Get(ctx context.Context, ip string) (*FloatingIP, *Response, error) { path := fmt.Sprintf("%s/%s", floatingBasePath, ip) - req, err := f.client.NewRequest("GET", path, nil) + req, err := f.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -96,10 +99,10 @@ func (f *FloatingIPsServiceOp) Get(ip string) (*FloatingIP, *Response, error) { // Create a floating IP. If the DropletID field of the request is not empty, // the floating IP will also be assigned to the droplet. -func (f *FloatingIPsServiceOp) Create(createRequest *FloatingIPCreateRequest) (*FloatingIP, *Response, error) { +func (f *FloatingIPsServiceOp) Create(ctx context.Context, createRequest *FloatingIPCreateRequest) (*FloatingIP, *Response, error) { path := floatingBasePath - req, err := f.client.NewRequest("POST", path, createRequest) + req, err := f.client.NewRequest(ctx, "POST", path, createRequest) if err != nil { return nil, nil, err } @@ -117,10 +120,10 @@ func (f *FloatingIPsServiceOp) Create(createRequest *FloatingIPCreateRequest) (* } // Delete a floating IP. -func (f *FloatingIPsServiceOp) Delete(ip string) (*Response, error) { +func (f *FloatingIPsServiceOp) Delete(ctx context.Context, ip string) (*Response, error) { path := fmt.Sprintf("%s/%s", floatingBasePath, ip) - req, err := f.client.NewRequest("DELETE", path, nil) + req, err := f.client.NewRequest(ctx, "DELETE", path, nil) if err != nil { return nil, err } diff --git a/vendor/github.com/digitalocean/godo/floating_ips_actions.go b/vendor/github.com/digitalocean/godo/floating_ips_actions.go index f435384eb..5afa051d7 100644 --- a/vendor/github.com/digitalocean/godo/floating_ips_actions.go +++ b/vendor/github.com/digitalocean/godo/floating_ips_actions.go @@ -1,15 +1,18 @@ package godo -import "fmt" +import ( + "context" + "fmt" +) // FloatingIPActionsService is an interface for interfacing with the // floating IPs actions endpoints of the Digital Ocean API. // See: https://developers.digitalocean.com/documentation/v2#floating-ips-action type FloatingIPActionsService interface { - Assign(ip string, dropletID int) (*Action, *Response, error) - Unassign(ip string) (*Action, *Response, error) - Get(ip string, actionID int) (*Action, *Response, error) - List(ip string, opt *ListOptions) ([]Action, *Response, error) + Assign(ctx context.Context, ip string, dropletID int) (*Action, *Response, error) + Unassign(ctx context.Context, ip string) (*Action, *Response, error) + Get(ctx context.Context, ip string, actionID int) (*Action, *Response, error) + List(ctx context.Context, ip string, opt *ListOptions) ([]Action, *Response, error) } // FloatingIPActionsServiceOp handles communication with the floating IPs @@ -19,41 +22,41 @@ type FloatingIPActionsServiceOp struct { } // Assign a floating IP to a droplet. -func (s *FloatingIPActionsServiceOp) Assign(ip string, dropletID int) (*Action, *Response, error) { +func (s *FloatingIPActionsServiceOp) Assign(ctx context.Context, ip string, dropletID int) (*Action, *Response, error) { request := &ActionRequest{ "type": "assign", "droplet_id": dropletID, } - return s.doAction(ip, request) + return s.doAction(ctx, ip, request) } // Unassign a floating IP from the droplet it is currently assigned to. -func (s *FloatingIPActionsServiceOp) Unassign(ip string) (*Action, *Response, error) { +func (s *FloatingIPActionsServiceOp) Unassign(ctx context.Context, ip string) (*Action, *Response, error) { request := &ActionRequest{"type": "unassign"} - return s.doAction(ip, request) + return s.doAction(ctx, ip, request) } // Get an action for a particular floating IP by id. -func (s *FloatingIPActionsServiceOp) Get(ip string, actionID int) (*Action, *Response, error) { +func (s *FloatingIPActionsServiceOp) Get(ctx context.Context, ip string, actionID int) (*Action, *Response, error) { path := fmt.Sprintf("%s/%d", floatingIPActionPath(ip), actionID) - return s.get(path) + return s.get(ctx, path) } // List the actions for a particular floating IP. -func (s *FloatingIPActionsServiceOp) List(ip string, opt *ListOptions) ([]Action, *Response, error) { +func (s *FloatingIPActionsServiceOp) List(ctx context.Context, ip string, opt *ListOptions) ([]Action, *Response, error) { path := floatingIPActionPath(ip) path, err := addOptions(path, opt) if err != nil { return nil, nil, err } - return s.list(path) + return s.list(ctx, path) } -func (s *FloatingIPActionsServiceOp) doAction(ip string, request *ActionRequest) (*Action, *Response, error) { +func (s *FloatingIPActionsServiceOp) doAction(ctx context.Context, ip string, request *ActionRequest) (*Action, *Response, error) { path := floatingIPActionPath(ip) - req, err := s.client.NewRequest("POST", path, request) + req, err := s.client.NewRequest(ctx, "POST", path, request) if err != nil { return nil, nil, err } @@ -64,11 +67,11 @@ func (s *FloatingIPActionsServiceOp) doAction(ip string, request *ActionRequest) return nil, resp, err } - return &root.Event, resp, err + return root.Event, resp, err } -func (s *FloatingIPActionsServiceOp) get(path string) (*Action, *Response, error) { - req, err := s.client.NewRequest("GET", path, nil) +func (s *FloatingIPActionsServiceOp) get(ctx context.Context, path string) (*Action, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -79,11 +82,11 @@ func (s *FloatingIPActionsServiceOp) get(path string) (*Action, *Response, error return nil, resp, err } - return &root.Event, resp, err + return root.Event, resp, err } -func (s *FloatingIPActionsServiceOp) list(path string) ([]Action, *Response, error) { - req, err := s.client.NewRequest("GET", path, nil) +func (s *FloatingIPActionsServiceOp) list(ctx context.Context, path string) ([]Action, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } diff --git a/vendor/github.com/digitalocean/godo/godo.go b/vendor/github.com/digitalocean/godo/godo.go index 47d61391f..0f9d7a91b 100644 --- a/vendor/github.com/digitalocean/godo/godo.go +++ b/vendor/github.com/digitalocean/godo/godo.go @@ -2,6 +2,7 @@ package godo import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -17,7 +18,7 @@ import ( ) const ( - libraryVersion = "0.1.0" + libraryVersion = "1.0.0" defaultBaseURL = "https://api.digitalocean.com/" userAgent = "godo/" + libraryVersion mediaType = "application/json" @@ -55,6 +56,12 @@ type Client struct { Sizes SizesService FloatingIPs FloatingIPsService FloatingIPActions FloatingIPActionsService + Snapshots SnapshotsService + Storage StorageService + StorageActions StorageActionsService + Tags TagsService + LoadBalancers LoadBalancersService + Certificates CertificatesService // Optional function called after every successful request made to the DO APIs onRequestCompleted RequestCompletionCallback @@ -93,7 +100,10 @@ type ErrorResponse struct { Response *http.Response // Error message - Message string + Message string `json:"message"` + + // RequestID returned from the API, useful to contact support. + RequestID string `json:"request_id"` } // Rate contains the rate limit for the current client. @@ -149,21 +159,63 @@ func NewClient(httpClient *http.Client) *Client { c.Domains = &DomainsServiceOp{client: c} c.Droplets = &DropletsServiceOp{client: c} c.DropletActions = &DropletActionsServiceOp{client: c} + c.FloatingIPs = &FloatingIPsServiceOp{client: c} + c.FloatingIPActions = &FloatingIPActionsServiceOp{client: c} c.Images = &ImagesServiceOp{client: c} c.ImageActions = &ImageActionsServiceOp{client: c} c.Keys = &KeysServiceOp{client: c} c.Regions = &RegionsServiceOp{client: c} + c.Snapshots = &SnapshotsServiceOp{client: c} c.Sizes = &SizesServiceOp{client: c} - c.FloatingIPs = &FloatingIPsServiceOp{client: c} - c.FloatingIPActions = &FloatingIPActionsServiceOp{client: c} + c.Storage = &StorageServiceOp{client: c} + c.StorageActions = &StorageActionsServiceOp{client: c} + c.Tags = &TagsServiceOp{client: c} + c.LoadBalancers = &LoadBalancersServiceOp{client: c} + c.Certificates = &CertificatesServiceOp{client: c} return c } +// ClientOpt are options for New. +type ClientOpt func(*Client) error + +// New returns a new DIgitalOcean API client instance. +func New(httpClient *http.Client, opts ...ClientOpt) (*Client, error) { + c := NewClient(httpClient) + for _, opt := range opts { + if err := opt(c); err != nil { + return nil, err + } + } + + return c, nil +} + +// SetBaseURL is a client option for setting the base URL. +func SetBaseURL(bu string) ClientOpt { + return func(c *Client) error { + u, err := url.Parse(bu) + if err != nil { + return err + } + + c.BaseURL = u + return nil + } +} + +// SetUserAgent is a client option for setting the user agent. +func SetUserAgent(ua string) ClientOpt { + return func(c *Client) error { + c.UserAgent = fmt.Sprintf("%s+%s", ua, c.UserAgent) + return nil + } +} + // NewRequest creates an API request. A relative URL can be provided in urlStr, which will be resolved to the // BaseURL of the Client. Relative URLS should always be specified without a preceding slash. If specified, the // value pointed to by body is JSON encoded and included in as the request body. -func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) { +func (c *Client) NewRequest(ctx context.Context, method, urlStr string, body interface{}) (*http.Request, error) { rel, err := url.Parse(urlStr) if err != nil { return nil, err @@ -173,7 +225,7 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ buf := new(bytes.Buffer) if body != nil { - err := json.NewEncoder(buf).Encode(body) + err = json.NewEncoder(buf).Encode(body) if err != nil { return nil, err } @@ -184,9 +236,10 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ return nil, err } + req = req.WithContext(ctx) req.Header.Add("Content-Type", mediaType) req.Header.Add("Accept", mediaType) - req.Header.Add("User-Agent", userAgent) + req.Header.Add("User-Agent", c.UserAgent) return req, nil } @@ -265,12 +318,12 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) { if v != nil { if w, ok := v.(io.Writer); ok { - _, err := io.Copy(w, resp.Body) + _, err = io.Copy(w, resp.Body) if err != nil { return nil, err } } else { - err := json.NewDecoder(resp.Body).Decode(v) + err = json.NewDecoder(resp.Body).Decode(v) if err != nil { return nil, err } @@ -280,6 +333,10 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) { return response, err } func (r *ErrorResponse) Error() string { + if r.RequestID != "" { + return fmt.Sprintf("%v %v: %d (request %q) %v", + r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, r.RequestID, r.Message) + } return fmt.Sprintf("%v %v: %d %v", r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, r.Message) } diff --git a/vendor/github.com/digitalocean/godo/image_actions.go b/vendor/github.com/digitalocean/godo/image_actions.go index 2d0538567..c4201c0de 100644 --- a/vendor/github.com/digitalocean/godo/image_actions.go +++ b/vendor/github.com/digitalocean/godo/image_actions.go @@ -1,13 +1,17 @@ package godo -import "fmt" +import ( + "context" + "fmt" +) // ImageActionsService is an interface for interfacing with the image actions // endpoints of the DigitalOcean API // See: https://developers.digitalocean.com/documentation/v2#image-actions type ImageActionsService interface { - Get(int, int) (*Action, *Response, error) - Transfer(int, *ActionRequest) (*Action, *Response, error) + Get(context.Context, int, int) (*Action, *Response, error) + Transfer(context.Context, int, *ActionRequest) (*Action, *Response, error) + Convert(context.Context, int) (*Action, *Response, error) } // ImageActionsServiceOp handles communition with the image action related methods of the @@ -19,7 +23,7 @@ type ImageActionsServiceOp struct { var _ ImageActionsService = &ImageActionsServiceOp{} // Transfer an image -func (i *ImageActionsServiceOp) Transfer(imageID int, transferRequest *ActionRequest) (*Action, *Response, error) { +func (i *ImageActionsServiceOp) Transfer(ctx context.Context, imageID int, transferRequest *ActionRequest) (*Action, *Response, error) { if imageID < 1 { return nil, nil, NewArgError("imageID", "cannot be less than 1") } @@ -30,7 +34,7 @@ func (i *ImageActionsServiceOp) Transfer(imageID int, transferRequest *ActionReq path := fmt.Sprintf("v2/images/%d/actions", imageID) - req, err := i.client.NewRequest("POST", path, transferRequest) + req, err := i.client.NewRequest(ctx, "POST", path, transferRequest) if err != nil { return nil, nil, err } @@ -41,11 +45,37 @@ func (i *ImageActionsServiceOp) Transfer(imageID int, transferRequest *ActionReq return nil, resp, err } - return &root.Event, resp, err + return root.Event, resp, err +} + +// Convert an image to a snapshot +func (i *ImageActionsServiceOp) Convert(ctx context.Context, imageID int) (*Action, *Response, error) { + if imageID < 1 { + return nil, nil, NewArgError("imageID", "cannont be less than 1") + } + + path := fmt.Sprintf("v2/images/%d/actions", imageID) + + convertRequest := &ActionRequest{ + "type": "convert", + } + + req, err := i.client.NewRequest(ctx, "POST", path, convertRequest) + if err != nil { + return nil, nil, err + } + + root := new(actionRoot) + resp, err := i.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.Event, resp, err } // Get an action for a particular image by id. -func (i *ImageActionsServiceOp) Get(imageID, actionID int) (*Action, *Response, error) { +func (i *ImageActionsServiceOp) Get(ctx context.Context, imageID, actionID int) (*Action, *Response, error) { if imageID < 1 { return nil, nil, NewArgError("imageID", "cannot be less than 1") } @@ -56,7 +86,7 @@ func (i *ImageActionsServiceOp) Get(imageID, actionID int) (*Action, *Response, path := fmt.Sprintf("v2/images/%d/actions/%d", imageID, actionID) - req, err := i.client.NewRequest("GET", path, nil) + req, err := i.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -67,5 +97,5 @@ func (i *ImageActionsServiceOp) Get(imageID, actionID int) (*Action, *Response, return nil, resp, err } - return &root.Event, resp, err + return root.Event, resp, err } diff --git a/vendor/github.com/digitalocean/godo/images.go b/vendor/github.com/digitalocean/godo/images.go index 96894f886..c808af6ec 100644 --- a/vendor/github.com/digitalocean/godo/images.go +++ b/vendor/github.com/digitalocean/godo/images.go @@ -1,6 +1,9 @@ package godo -import "fmt" +import ( + "context" + "fmt" +) const imageBasePath = "v2/images" @@ -8,14 +11,14 @@ const imageBasePath = "v2/images" // endpoints of the DigitalOcean API // See: https://developers.digitalocean.com/documentation/v2#images type ImagesService interface { - List(*ListOptions) ([]Image, *Response, error) - ListDistribution(opt *ListOptions) ([]Image, *Response, error) - ListApplication(opt *ListOptions) ([]Image, *Response, error) - ListUser(opt *ListOptions) ([]Image, *Response, error) - GetByID(int) (*Image, *Response, error) - GetBySlug(string) (*Image, *Response, error) - Update(int, *ImageUpdateRequest) (*Image, *Response, error) - Delete(int) (*Response, error) + List(context.Context, *ListOptions) ([]Image, *Response, error) + ListDistribution(ctx context.Context, opt *ListOptions) ([]Image, *Response, error) + ListApplication(ctx context.Context, opt *ListOptions) ([]Image, *Response, error) + ListUser(ctx context.Context, opt *ListOptions) ([]Image, *Response, error) + GetByID(context.Context, int) (*Image, *Response, error) + GetBySlug(context.Context, string) (*Image, *Response, error) + Update(context.Context, int, *ImageUpdateRequest) (*Image, *Response, error) + Delete(context.Context, int) (*Response, error) } // ImagesServiceOp handles communication with the image related methods of the @@ -45,7 +48,7 @@ type ImageUpdateRequest struct { } type imageRoot struct { - Image Image + Image *Image } type imagesRoot struct { @@ -63,48 +66,48 @@ func (i Image) String() string { } // List lists all the images available. -func (s *ImagesServiceOp) List(opt *ListOptions) ([]Image, *Response, error) { - return s.list(opt, nil) +func (s *ImagesServiceOp) List(ctx context.Context, opt *ListOptions) ([]Image, *Response, error) { + return s.list(ctx, opt, nil) } // ListDistribution lists all the distribution images. -func (s *ImagesServiceOp) ListDistribution(opt *ListOptions) ([]Image, *Response, error) { +func (s *ImagesServiceOp) ListDistribution(ctx context.Context, opt *ListOptions) ([]Image, *Response, error) { listOpt := listImageOptions{Type: "distribution"} - return s.list(opt, &listOpt) + return s.list(ctx, opt, &listOpt) } // ListApplication lists all the application images. -func (s *ImagesServiceOp) ListApplication(opt *ListOptions) ([]Image, *Response, error) { +func (s *ImagesServiceOp) ListApplication(ctx context.Context, opt *ListOptions) ([]Image, *Response, error) { listOpt := listImageOptions{Type: "application"} - return s.list(opt, &listOpt) + return s.list(ctx, opt, &listOpt) } // ListUser lists all the user images. -func (s *ImagesServiceOp) ListUser(opt *ListOptions) ([]Image, *Response, error) { +func (s *ImagesServiceOp) ListUser(ctx context.Context, opt *ListOptions) ([]Image, *Response, error) { listOpt := listImageOptions{Private: true} - return s.list(opt, &listOpt) + return s.list(ctx, opt, &listOpt) } // GetByID retrieves an image by id. -func (s *ImagesServiceOp) GetByID(imageID int) (*Image, *Response, error) { +func (s *ImagesServiceOp) GetByID(ctx context.Context, imageID int) (*Image, *Response, error) { if imageID < 1 { return nil, nil, NewArgError("imageID", "cannot be less than 1") } - return s.get(interface{}(imageID)) + return s.get(ctx, interface{}(imageID)) } // GetBySlug retrieves an image by slug. -func (s *ImagesServiceOp) GetBySlug(slug string) (*Image, *Response, error) { +func (s *ImagesServiceOp) GetBySlug(ctx context.Context, slug string) (*Image, *Response, error) { if len(slug) < 1 { return nil, nil, NewArgError("slug", "cannot be blank") } - return s.get(interface{}(slug)) + return s.get(ctx, interface{}(slug)) } // Update an image name. -func (s *ImagesServiceOp) Update(imageID int, updateRequest *ImageUpdateRequest) (*Image, *Response, error) { +func (s *ImagesServiceOp) Update(ctx context.Context, imageID int, updateRequest *ImageUpdateRequest) (*Image, *Response, error) { if imageID < 1 { return nil, nil, NewArgError("imageID", "cannot be less than 1") } @@ -114,7 +117,7 @@ func (s *ImagesServiceOp) Update(imageID int, updateRequest *ImageUpdateRequest) } path := fmt.Sprintf("%s/%d", imageBasePath, imageID) - req, err := s.client.NewRequest("PUT", path, updateRequest) + req, err := s.client.NewRequest(ctx, "PUT", path, updateRequest) if err != nil { return nil, nil, err } @@ -125,18 +128,18 @@ func (s *ImagesServiceOp) Update(imageID int, updateRequest *ImageUpdateRequest) return nil, resp, err } - return &root.Image, resp, err + return root.Image, resp, err } // Delete an image. -func (s *ImagesServiceOp) Delete(imageID int) (*Response, error) { +func (s *ImagesServiceOp) Delete(ctx context.Context, imageID int) (*Response, error) { if imageID < 1 { return nil, NewArgError("imageID", "cannot be less than 1") } path := fmt.Sprintf("%s/%d", imageBasePath, imageID) - req, err := s.client.NewRequest("DELETE", path, nil) + req, err := s.client.NewRequest(ctx, "DELETE", path, nil) if err != nil { return nil, err } @@ -147,10 +150,10 @@ func (s *ImagesServiceOp) Delete(imageID int) (*Response, error) { } // Helper method for getting an individual image -func (s *ImagesServiceOp) get(ID interface{}) (*Image, *Response, error) { +func (s *ImagesServiceOp) get(ctx context.Context, ID interface{}) (*Image, *Response, error) { path := fmt.Sprintf("%s/%v", imageBasePath, ID) - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -161,11 +164,11 @@ func (s *ImagesServiceOp) get(ID interface{}) (*Image, *Response, error) { return nil, resp, err } - return &root.Image, resp, err + return root.Image, resp, err } // Helper method for listing images -func (s *ImagesServiceOp) list(opt *ListOptions, listOpt *listImageOptions) ([]Image, *Response, error) { +func (s *ImagesServiceOp) list(ctx context.Context, opt *ListOptions, listOpt *listImageOptions) ([]Image, *Response, error) { path := imageBasePath path, err := addOptions(path, opt) if err != nil { @@ -176,7 +179,7 @@ func (s *ImagesServiceOp) list(opt *ListOptions, listOpt *listImageOptions) ([]I return nil, nil, err } - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } diff --git a/vendor/github.com/digitalocean/godo/keys.go b/vendor/github.com/digitalocean/godo/keys.go index 8dc50f8a0..7b336c4ce 100644 --- a/vendor/github.com/digitalocean/godo/keys.go +++ b/vendor/github.com/digitalocean/godo/keys.go @@ -1,6 +1,9 @@ package godo -import "fmt" +import ( + "context" + "fmt" +) const keysBasePath = "v2/account/keys" @@ -8,14 +11,14 @@ const keysBasePath = "v2/account/keys" // endpoints of the DigitalOcean API // See: https://developers.digitalocean.com/documentation/v2#keys type KeysService interface { - List(*ListOptions) ([]Key, *Response, error) - GetByID(int) (*Key, *Response, error) - GetByFingerprint(string) (*Key, *Response, error) - Create(*KeyCreateRequest) (*Key, *Response, error) - UpdateByID(int, *KeyUpdateRequest) (*Key, *Response, error) - UpdateByFingerprint(string, *KeyUpdateRequest) (*Key, *Response, error) - DeleteByID(int) (*Response, error) - DeleteByFingerprint(string) (*Response, error) + List(context.Context, *ListOptions) ([]Key, *Response, error) + GetByID(context.Context, int) (*Key, *Response, error) + GetByFingerprint(context.Context, string) (*Key, *Response, error) + Create(context.Context, *KeyCreateRequest) (*Key, *Response, error) + UpdateByID(context.Context, int, *KeyUpdateRequest) (*Key, *Response, error) + UpdateByFingerprint(context.Context, string, *KeyUpdateRequest) (*Key, *Response, error) + DeleteByID(context.Context, int) (*Response, error) + DeleteByFingerprint(context.Context, string) (*Response, error) } // KeysServiceOp handles communication with key related method of the @@ -45,7 +48,7 @@ type keysRoot struct { } type keyRoot struct { - SSHKey Key `json:"ssh_key"` + SSHKey *Key `json:"ssh_key"` } func (s Key) String() string { @@ -59,14 +62,14 @@ type KeyCreateRequest struct { } // List all keys -func (s *KeysServiceOp) List(opt *ListOptions) ([]Key, *Response, error) { +func (s *KeysServiceOp) List(ctx context.Context, opt *ListOptions) ([]Key, *Response, error) { path := keysBasePath path, err := addOptions(path, opt) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -84,8 +87,8 @@ func (s *KeysServiceOp) List(opt *ListOptions) ([]Key, *Response, error) { } // Performs a get given a path -func (s *KeysServiceOp) get(path string) (*Key, *Response, error) { - req, err := s.client.NewRequest("GET", path, nil) +func (s *KeysServiceOp) get(ctx context.Context, path string) (*Key, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } @@ -96,36 +99,36 @@ func (s *KeysServiceOp) get(path string) (*Key, *Response, error) { return nil, resp, err } - return &root.SSHKey, resp, err + return root.SSHKey, resp, err } // GetByID gets a Key by id -func (s *KeysServiceOp) GetByID(keyID int) (*Key, *Response, error) { +func (s *KeysServiceOp) GetByID(ctx context.Context, keyID int) (*Key, *Response, error) { if keyID < 1 { return nil, nil, NewArgError("keyID", "cannot be less than 1") } path := fmt.Sprintf("%s/%d", keysBasePath, keyID) - return s.get(path) + return s.get(ctx, path) } // GetByFingerprint gets a Key by by fingerprint -func (s *KeysServiceOp) GetByFingerprint(fingerprint string) (*Key, *Response, error) { +func (s *KeysServiceOp) GetByFingerprint(ctx context.Context, fingerprint string) (*Key, *Response, error) { if len(fingerprint) < 1 { return nil, nil, NewArgError("fingerprint", "cannot not be empty") } path := fmt.Sprintf("%s/%s", keysBasePath, fingerprint) - return s.get(path) + return s.get(ctx, path) } // Create a key using a KeyCreateRequest -func (s *KeysServiceOp) Create(createRequest *KeyCreateRequest) (*Key, *Response, error) { +func (s *KeysServiceOp) Create(ctx context.Context, createRequest *KeyCreateRequest) (*Key, *Response, error) { if createRequest == nil { return nil, nil, NewArgError("createRequest", "cannot be nil") } - req, err := s.client.NewRequest("POST", keysBasePath, createRequest) + req, err := s.client.NewRequest(ctx, "POST", keysBasePath, createRequest) if err != nil { return nil, nil, err } @@ -136,11 +139,11 @@ func (s *KeysServiceOp) Create(createRequest *KeyCreateRequest) (*Key, *Response return nil, resp, err } - return &root.SSHKey, resp, err + return root.SSHKey, resp, err } // UpdateByID updates a key name by ID. -func (s *KeysServiceOp) UpdateByID(keyID int, updateRequest *KeyUpdateRequest) (*Key, *Response, error) { +func (s *KeysServiceOp) UpdateByID(ctx context.Context, keyID int, updateRequest *KeyUpdateRequest) (*Key, *Response, error) { if keyID < 1 { return nil, nil, NewArgError("keyID", "cannot be less than 1") } @@ -150,7 +153,7 @@ func (s *KeysServiceOp) UpdateByID(keyID int, updateRequest *KeyUpdateRequest) ( } path := fmt.Sprintf("%s/%d", keysBasePath, keyID) - req, err := s.client.NewRequest("PUT", path, updateRequest) + req, err := s.client.NewRequest(ctx, "PUT", path, updateRequest) if err != nil { return nil, nil, err } @@ -161,11 +164,11 @@ func (s *KeysServiceOp) UpdateByID(keyID int, updateRequest *KeyUpdateRequest) ( return nil, resp, err } - return &root.SSHKey, resp, err + return root.SSHKey, resp, err } // UpdateByFingerprint updates a key name by fingerprint. -func (s *KeysServiceOp) UpdateByFingerprint(fingerprint string, updateRequest *KeyUpdateRequest) (*Key, *Response, error) { +func (s *KeysServiceOp) UpdateByFingerprint(ctx context.Context, fingerprint string, updateRequest *KeyUpdateRequest) (*Key, *Response, error) { if len(fingerprint) < 1 { return nil, nil, NewArgError("fingerprint", "cannot be empty") } @@ -175,7 +178,7 @@ func (s *KeysServiceOp) UpdateByFingerprint(fingerprint string, updateRequest *K } path := fmt.Sprintf("%s/%s", keysBasePath, fingerprint) - req, err := s.client.NewRequest("PUT", path, updateRequest) + req, err := s.client.NewRequest(ctx, "PUT", path, updateRequest) if err != nil { return nil, nil, err } @@ -186,12 +189,12 @@ func (s *KeysServiceOp) UpdateByFingerprint(fingerprint string, updateRequest *K return nil, resp, err } - return &root.SSHKey, resp, err + return root.SSHKey, resp, err } // Delete key using a path -func (s *KeysServiceOp) delete(path string) (*Response, error) { - req, err := s.client.NewRequest("DELETE", path, nil) +func (s *KeysServiceOp) delete(ctx context.Context, path string) (*Response, error) { + req, err := s.client.NewRequest(ctx, "DELETE", path, nil) if err != nil { return nil, err } @@ -202,21 +205,21 @@ func (s *KeysServiceOp) delete(path string) (*Response, error) { } // DeleteByID deletes a key by its id -func (s *KeysServiceOp) DeleteByID(keyID int) (*Response, error) { +func (s *KeysServiceOp) DeleteByID(ctx context.Context, keyID int) (*Response, error) { if keyID < 1 { return nil, NewArgError("keyID", "cannot be less than 1") } path := fmt.Sprintf("%s/%d", keysBasePath, keyID) - return s.delete(path) + return s.delete(ctx, path) } // DeleteByFingerprint deletes a key by its fingerprint -func (s *KeysServiceOp) DeleteByFingerprint(fingerprint string) (*Response, error) { +func (s *KeysServiceOp) DeleteByFingerprint(ctx context.Context, fingerprint string) (*Response, error) { if len(fingerprint) < 1 { return nil, NewArgError("fingerprint", "cannot be empty") } path := fmt.Sprintf("%s/%s", keysBasePath, fingerprint) - return s.delete(path) + return s.delete(ctx, path) } diff --git a/vendor/github.com/digitalocean/godo/links.go b/vendor/github.com/digitalocean/godo/links.go index 463510523..209844172 100644 --- a/vendor/github.com/digitalocean/godo/links.go +++ b/vendor/github.com/digitalocean/godo/links.go @@ -1,6 +1,7 @@ package godo import ( + "context" "net/url" "strconv" ) @@ -58,11 +59,7 @@ func (l *Links) IsLastPage() bool { } func (p *Pages) isLast() bool { - if p.Last == "" { - return true - } - - return false + return p.Last == "" } func pageForURL(urlText string) (int, error) { @@ -81,6 +78,6 @@ func pageForURL(urlText string) (int, error) { } // Get a link action by id. -func (la *LinkAction) Get(client *Client) (*Action, *Response, error) { - return client.Actions.Get(la.ID) +func (la *LinkAction) Get(ctx context.Context, client *Client) (*Action, *Response, error) { + return client.Actions.Get(ctx, la.ID) } diff --git a/vendor/github.com/digitalocean/godo/load_balancers.go b/vendor/github.com/digitalocean/godo/load_balancers.go new file mode 100644 index 000000000..48b9c2654 --- /dev/null +++ b/vendor/github.com/digitalocean/godo/load_balancers.go @@ -0,0 +1,275 @@ +package godo + +import ( + "context" + "fmt" +) + +const loadBalancersBasePath = "/v2/load_balancers" +const forwardingRulesPath = "forwarding_rules" +const dropletsPath = "droplets" + +// LoadBalancersService is an interface for managing load balancers with the DigitalOcean API. +// See: https://developers.digitalocean.com/documentation/v2#load-balancers +type LoadBalancersService interface { + Get(context.Context, string) (*LoadBalancer, *Response, error) + List(context.Context, *ListOptions) ([]LoadBalancer, *Response, error) + Create(context.Context, *LoadBalancerRequest) (*LoadBalancer, *Response, error) + Update(ctx context.Context, lbID string, lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error) + Delete(ctx context.Context, lbID string) (*Response, error) + AddDroplets(ctx context.Context, lbID string, dropletIDs ...int) (*Response, error) + RemoveDroplets(ctx context.Context, lbID string, dropletIDs ...int) (*Response, error) + AddForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error) + RemoveForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error) +} + +// LoadBalancer represents a DigitalOcean load balancer configuration. +type LoadBalancer struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + IP string `json:"ip,omitempty"` + Algorithm string `json:"algorithm,omitempty"` + Status string `json:"status,omitempty"` + Created string `json:"created_at,omitempty"` + ForwardingRules []ForwardingRule `json:"forwarding_rules,omitempty"` + HealthCheck *HealthCheck `json:"health_check,omitempty"` + StickySessions *StickySessions `json:"sticky_sessions,omitempty"` + Region *Region `json:"region,omitempty"` + DropletIDs []int `json:"droplet_ids,omitempty"` + Tag string `json:"tag,omitempty"` + RedirectHttpToHttps bool `json:"redirect_http_to_https,omitempty"` +} + +// String creates a human-readable description of a LoadBalancer. +func (l LoadBalancer) String() string { + return Stringify(l) +} + +// ForwardingRule represents load balancer forwarding rules. +type ForwardingRule struct { + EntryProtocol string `json:"entry_protocol,omitempty"` + EntryPort int `json:"entry_port,omitempty"` + TargetProtocol string `json:"target_protocol,omitempty"` + TargetPort int `json:"target_port,omitempty"` + CertificateID string `json:"certificate_id,omitempty"` + TlsPassthrough bool `json:"tls_passthrough,omitempty"` +} + +// String creates a human-readable description of a ForwardingRule. +func (f ForwardingRule) String() string { + return Stringify(f) +} + +// HealthCheck represents optional load balancer health check rules. +type HealthCheck struct { + Protocol string `json:"protocol,omitempty"` + Port int `json:"port,omitempty"` + Path string `json:"path,omitempty"` + CheckIntervalSeconds int `json:"check_interval_seconds,omitempty"` + ResponseTimeoutSeconds int `json:"response_timeout_seconds,omitempty"` + HealthyThreshold int `json:"healthy_threshold,omitempty"` + UnhealthyThreshold int `json:"unhealthy_threshold,omitempty"` +} + +// String creates a human-readable description of a HealthCheck. +func (h HealthCheck) String() string { + return Stringify(h) +} + +// StickySessions represents optional load balancer session affinity rules. +type StickySessions struct { + Type string `json:"type,omitempty"` + CookieName string `json:"cookie_name,omitempty"` + CookieTtlSeconds int `json:"cookie_ttl_seconds,omitempty"` +} + +// String creates a human-readable description of a StickySessions instance. +func (s StickySessions) String() string { + return Stringify(s) +} + +// LoadBalancerRequest represents the configuration to be applied to an existing or a new load balancer. +type LoadBalancerRequest struct { + Name string `json:"name,omitempty"` + Algorithm string `json:"algorithm,omitempty"` + Region string `json:"region,omitempty"` + ForwardingRules []ForwardingRule `json:"forwarding_rules,omitempty"` + HealthCheck *HealthCheck `json:"health_check,omitempty"` + StickySessions *StickySessions `json:"sticky_sessions,omitempty"` + DropletIDs []int `json:"droplet_ids,omitempty"` + Tag string `json:"tag,omitempty"` + RedirectHttpToHttps bool `json:"redirect_http_to_https,omitempty"` +} + +// String creates a human-readable description of a LoadBalancerRequest. +func (l LoadBalancerRequest) String() string { + return Stringify(l) +} + +type forwardingRulesRequest struct { + Rules []ForwardingRule `json:"forwarding_rules,omitempty"` +} + +func (l forwardingRulesRequest) String() string { + return Stringify(l) +} + +type dropletIDsRequest struct { + IDs []int `json:"droplet_ids,omitempty"` +} + +func (l dropletIDsRequest) String() string { + return Stringify(l) +} + +type loadBalancersRoot struct { + LoadBalancers []LoadBalancer `json:"load_balancers"` + Links *Links `json:"links"` +} + +type loadBalancerRoot struct { + LoadBalancer *LoadBalancer `json:"load_balancer"` +} + +// LoadBalancersServiceOp handles communication with load balancer-related methods of the DigitalOcean API. +type LoadBalancersServiceOp struct { + client *Client +} + +var _ LoadBalancersService = &LoadBalancersServiceOp{} + +// Get an existing load balancer by its identifier. +func (l *LoadBalancersServiceOp) Get(ctx context.Context, lbID string) (*LoadBalancer, *Response, error) { + path := fmt.Sprintf("%s/%s", loadBalancersBasePath, lbID) + + req, err := l.client.NewRequest(ctx, "GET", path, nil) + if err != nil { + return nil, nil, err + } + + root := new(loadBalancerRoot) + resp, err := l.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.LoadBalancer, resp, err +} + +// List load balancers, with optional pagination. +func (l *LoadBalancersServiceOp) List(ctx context.Context, opt *ListOptions) ([]LoadBalancer, *Response, error) { + path, err := addOptions(loadBalancersBasePath, opt) + if err != nil { + return nil, nil, err + } + + req, err := l.client.NewRequest(ctx, "GET", path, nil) + if err != nil { + return nil, nil, err + } + + root := new(loadBalancersRoot) + resp, err := l.client.Do(req, root) + if err != nil { + return nil, resp, err + } + if l := root.Links; l != nil { + resp.Links = l + } + + return root.LoadBalancers, resp, err +} + +// Create a new load balancer with a given configuration. +func (l *LoadBalancersServiceOp) Create(ctx context.Context, lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error) { + req, err := l.client.NewRequest(ctx, "POST", loadBalancersBasePath, lbr) + if err != nil { + return nil, nil, err + } + + root := new(loadBalancerRoot) + resp, err := l.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.LoadBalancer, resp, err +} + +// Update an existing load balancer with new configuration. +func (l *LoadBalancersServiceOp) Update(ctx context.Context, lbID string, lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error) { + path := fmt.Sprintf("%s/%s", loadBalancersBasePath, lbID) + + req, err := l.client.NewRequest(ctx, "PUT", path, lbr) + if err != nil { + return nil, nil, err + } + + root := new(loadBalancerRoot) + resp, err := l.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.LoadBalancer, resp, err +} + +// Delete a load balancer by its identifier. +func (l *LoadBalancersServiceOp) Delete(ctx context.Context, ldID string) (*Response, error) { + path := fmt.Sprintf("%s/%s", loadBalancersBasePath, ldID) + + req, err := l.client.NewRequest(ctx, "DELETE", path, nil) + if err != nil { + return nil, err + } + + return l.client.Do(req, nil) +} + +// AddDroplets adds droplets to a load balancer. +func (l *LoadBalancersServiceOp) AddDroplets(ctx context.Context, lbID string, dropletIDs ...int) (*Response, error) { + path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, dropletsPath) + + req, err := l.client.NewRequest(ctx, "POST", path, &dropletIDsRequest{IDs: dropletIDs}) + if err != nil { + return nil, err + } + + return l.client.Do(req, nil) +} + +// RemoveDroplets removes droplets from a load balancer. +func (l *LoadBalancersServiceOp) RemoveDroplets(ctx context.Context, lbID string, dropletIDs ...int) (*Response, error) { + path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, dropletsPath) + + req, err := l.client.NewRequest(ctx, "DELETE", path, &dropletIDsRequest{IDs: dropletIDs}) + if err != nil { + return nil, err + } + + return l.client.Do(req, nil) +} + +// AddForwardingRules adds forwarding rules to a load balancer. +func (l *LoadBalancersServiceOp) AddForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error) { + path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, forwardingRulesPath) + + req, err := l.client.NewRequest(ctx, "POST", path, &forwardingRulesRequest{Rules: rules}) + if err != nil { + return nil, err + } + + return l.client.Do(req, nil) +} + +// RemoveForwardingRules removes forwarding rules from a load balancer. +func (l *LoadBalancersServiceOp) RemoveForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error) { + path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, forwardingRulesPath) + + req, err := l.client.NewRequest(ctx, "DELETE", path, &forwardingRulesRequest{Rules: rules}) + if err != nil { + return nil, err + } + + return l.client.Do(req, nil) +} diff --git a/vendor/github.com/digitalocean/godo/regions.go b/vendor/github.com/digitalocean/godo/regions.go index e44e8bcd6..ccfe02926 100644 --- a/vendor/github.com/digitalocean/godo/regions.go +++ b/vendor/github.com/digitalocean/godo/regions.go @@ -1,10 +1,12 @@ package godo +import "context" + // RegionsService is an interface for interfacing with the regions // endpoints of the DigitalOcean API // See: https://developers.digitalocean.com/documentation/v2#regions type RegionsService interface { - List(*ListOptions) ([]Region, *Response, error) + List(context.Context, *ListOptions) ([]Region, *Response, error) } // RegionsServiceOp handles communication with the region related methods of the @@ -29,23 +31,19 @@ type regionsRoot struct { Links *Links `json:"links"` } -type regionRoot struct { - Region Region -} - func (r Region) String() string { return Stringify(r) } // List all regions -func (s *RegionsServiceOp) List(opt *ListOptions) ([]Region, *Response, error) { +func (s *RegionsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Region, *Response, error) { path := "v2/regions" path, err := addOptions(path, opt) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } diff --git a/vendor/github.com/digitalocean/godo/sizes.go b/vendor/github.com/digitalocean/godo/sizes.go index 7f454e7eb..b69579278 100644 --- a/vendor/github.com/digitalocean/godo/sizes.go +++ b/vendor/github.com/digitalocean/godo/sizes.go @@ -1,10 +1,12 @@ package godo +import "context" + // SizesService is an interface for interfacing with the size // endpoints of the DigitalOcean API // See: https://developers.digitalocean.com/documentation/v2#sizes type SizesService interface { - List(*ListOptions) ([]Size, *Response, error) + List(context.Context, *ListOptions) ([]Size, *Response, error) } // SizesServiceOp handles communication with the size related methods of the @@ -38,14 +40,14 @@ type sizesRoot struct { } // List all images -func (s *SizesServiceOp) List(opt *ListOptions) ([]Size, *Response, error) { +func (s *SizesServiceOp) List(ctx context.Context, opt *ListOptions) ([]Size, *Response, error) { path := "v2/sizes" path, err := addOptions(path, opt) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", path, nil) + req, err := s.client.NewRequest(ctx, "GET", path, nil) if err != nil { return nil, nil, err } diff --git a/vendor/github.com/digitalocean/godo/snapshots.go b/vendor/github.com/digitalocean/godo/snapshots.go new file mode 100644 index 000000000..5a9e5c5d8 --- /dev/null +++ b/vendor/github.com/digitalocean/godo/snapshots.go @@ -0,0 +1,139 @@ +package godo + +import ( + "context" + "fmt" +) + +const snapshotBasePath = "v2/snapshots" + +// SnapshotsService is an interface for interfacing with the snapshots +// endpoints of the DigitalOcean API +// See: https://developers.digitalocean.com/documentation/v2#snapshots +type SnapshotsService interface { + List(context.Context, *ListOptions) ([]Snapshot, *Response, error) + ListVolume(context.Context, *ListOptions) ([]Snapshot, *Response, error) + ListDroplet(context.Context, *ListOptions) ([]Snapshot, *Response, error) + Get(context.Context, string) (*Snapshot, *Response, error) + Delete(context.Context, string) (*Response, error) +} + +// SnapshotsServiceOp handles communication with the snapshot related methods of the +// DigitalOcean API. +type SnapshotsServiceOp struct { + client *Client +} + +var _ SnapshotsService = &SnapshotsServiceOp{} + +// Snapshot represents a DigitalOcean Snapshot +type Snapshot struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + ResourceID string `json:"resource_id,omitempty"` + ResourceType string `json:"resource_type,omitempty"` + Regions []string `json:"regions,omitempty"` + MinDiskSize int `json:"min_disk_size,omitempty"` + SizeGigaBytes float64 `json:"size_gigabytes,omitempty"` + Created string `json:"created_at,omitempty"` +} + +type snapshotRoot struct { + Snapshot *Snapshot `json:"snapshot"` +} + +type snapshotsRoot struct { + Snapshots []Snapshot `json:"snapshots"` + Links *Links `json:"links,omitempty"` +} + +type listSnapshotOptions struct { + ResourceType string `url:"resource_type,omitempty"` +} + +func (s Snapshot) String() string { + return Stringify(s) +} + +// List lists all the snapshots available. +func (s *SnapshotsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Snapshot, *Response, error) { + return s.list(ctx, opt, nil) +} + +// ListDroplet lists all the Droplet snapshots. +func (s *SnapshotsServiceOp) ListDroplet(ctx context.Context, opt *ListOptions) ([]Snapshot, *Response, error) { + listOpt := listSnapshotOptions{ResourceType: "droplet"} + return s.list(ctx, opt, &listOpt) +} + +// ListVolume lists all the volume snapshots. +func (s *SnapshotsServiceOp) ListVolume(ctx context.Context, opt *ListOptions) ([]Snapshot, *Response, error) { + listOpt := listSnapshotOptions{ResourceType: "volume"} + return s.list(ctx, opt, &listOpt) +} + +// Get retrieves an snapshot by id. +func (s *SnapshotsServiceOp) Get(ctx context.Context, snapshotID string) (*Snapshot, *Response, error) { + return s.get(ctx, snapshotID) +} + +// Delete an snapshot. +func (s *SnapshotsServiceOp) Delete(ctx context.Context, snapshotID string) (*Response, error) { + path := fmt.Sprintf("%s/%s", snapshotBasePath, snapshotID) + + req, err := s.client.NewRequest(ctx, "DELETE", path, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + + return resp, err +} + +// Helper method for getting an individual snapshot +func (s *SnapshotsServiceOp) get(ctx context.Context, ID string) (*Snapshot, *Response, error) { + path := fmt.Sprintf("%s/%s", snapshotBasePath, ID) + + req, err := s.client.NewRequest(ctx, "GET", path, nil) + if err != nil { + return nil, nil, err + } + + root := new(snapshotRoot) + resp, err := s.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.Snapshot, resp, err +} + +// Helper method for listing snapshots +func (s *SnapshotsServiceOp) list(ctx context.Context, opt *ListOptions, listOpt *listSnapshotOptions) ([]Snapshot, *Response, error) { + path := snapshotBasePath + path, err := addOptions(path, opt) + if err != nil { + return nil, nil, err + } + path, err = addOptions(path, listOpt) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", path, nil) + if err != nil { + return nil, nil, err + } + + root := new(snapshotsRoot) + resp, err := s.client.Do(req, root) + if err != nil { + return nil, resp, err + } + if l := root.Links; l != nil { + resp.Links = l + } + + return root.Snapshots, resp, err +} diff --git a/vendor/github.com/digitalocean/godo/storage.go b/vendor/github.com/digitalocean/godo/storage.go new file mode 100644 index 000000000..00d5157d6 --- /dev/null +++ b/vendor/github.com/digitalocean/godo/storage.go @@ -0,0 +1,238 @@ +package godo + +import ( + "context" + "fmt" + "time" +) + +const ( + storageBasePath = "v2" + storageAllocPath = storageBasePath + "/volumes" + storageSnapPath = storageBasePath + "/snapshots" +) + +// StorageService is an interface for interfacing with the storage +// endpoints of the Digital Ocean API. +// See: https://developers.digitalocean.com/documentation/v2#storage +type StorageService interface { + ListVolumes(context.Context, *ListVolumeParams) ([]Volume, *Response, error) + GetVolume(context.Context, string) (*Volume, *Response, error) + CreateVolume(context.Context, *VolumeCreateRequest) (*Volume, *Response, error) + DeleteVolume(context.Context, string) (*Response, error) + ListSnapshots(ctx context.Context, volumeID string, opts *ListOptions) ([]Snapshot, *Response, error) + GetSnapshot(context.Context, string) (*Snapshot, *Response, error) + CreateSnapshot(context.Context, *SnapshotCreateRequest) (*Snapshot, *Response, error) + DeleteSnapshot(context.Context, string) (*Response, error) +} + +// StorageServiceOp handles communication with the storage volumes related methods of the +// DigitalOcean API. +type StorageServiceOp struct { + client *Client +} + +// ListVolumeParams stores the options you can set for a ListVolumeCall +type ListVolumeParams struct { + Region string `json:"region"` + Name string `json:"name"` + ListOptions *ListOptions `json:"list_options,omitempty"` +} + +var _ StorageService = &StorageServiceOp{} + +// Volume represents a Digital Ocean block store volume. +type Volume struct { + ID string `json:"id"` + Region *Region `json:"region"` + Name string `json:"name"` + SizeGigaBytes int64 `json:"size_gigabytes"` + Description string `json:"description"` + DropletIDs []int `json:"droplet_ids"` + CreatedAt time.Time `json:"created_at"` +} + +func (f Volume) String() string { + return Stringify(f) +} + +type storageVolumesRoot struct { + Volumes []Volume `json:"volumes"` + Links *Links `json:"links"` +} + +type storageVolumeRoot struct { + Volume *Volume `json:"volume"` + Links *Links `json:"links,omitempty"` +} + +// VolumeCreateRequest represents a request to create a block store +// volume. +type VolumeCreateRequest struct { + Region string `json:"region"` + Name string `json:"name"` + Description string `json:"description"` + SizeGigaBytes int64 `json:"size_gigabytes"` + SnapshotID string `json:"snapshot_id"` +} + +// ListVolumes lists all storage volumes. +func (svc *StorageServiceOp) ListVolumes(ctx context.Context, params *ListVolumeParams) ([]Volume, *Response, error) { + path := storageAllocPath + if params != nil { + if params.Region != "" && params.Name != "" { + path = fmt.Sprintf("%s?name=%s®ion=%s", path, params.Name, params.Region) + } + + if params.ListOptions != nil { + var err error + path, err = addOptions(path, params.ListOptions) + if err != nil { + return nil, nil, err + } + } + } + + req, err := svc.client.NewRequest(ctx, "GET", path, nil) + if err != nil { + return nil, nil, err + } + + root := new(storageVolumesRoot) + resp, err := svc.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + if l := root.Links; l != nil { + resp.Links = l + } + + return root.Volumes, resp, nil +} + +// CreateVolume creates a storage volume. The name must be unique. +func (svc *StorageServiceOp) CreateVolume(ctx context.Context, createRequest *VolumeCreateRequest) (*Volume, *Response, error) { + path := storageAllocPath + + req, err := svc.client.NewRequest(ctx, "POST", path, createRequest) + if err != nil { + return nil, nil, err + } + + root := new(storageVolumeRoot) + resp, err := svc.client.Do(req, root) + if err != nil { + return nil, resp, err + } + return root.Volume, resp, nil +} + +// GetVolume retrieves an individual storage volume. +func (svc *StorageServiceOp) GetVolume(ctx context.Context, id string) (*Volume, *Response, error) { + path := fmt.Sprintf("%s/%s", storageAllocPath, id) + + req, err := svc.client.NewRequest(ctx, "GET", path, nil) + if err != nil { + return nil, nil, err + } + + root := new(storageVolumeRoot) + resp, err := svc.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.Volume, resp, nil +} + +// DeleteVolume deletes a storage volume. +func (svc *StorageServiceOp) DeleteVolume(ctx context.Context, id string) (*Response, error) { + path := fmt.Sprintf("%s/%s", storageAllocPath, id) + + req, err := svc.client.NewRequest(ctx, "DELETE", path, nil) + if err != nil { + return nil, err + } + return svc.client.Do(req, nil) +} + +// SnapshotCreateRequest represents a request to create a block store +// volume. +type SnapshotCreateRequest struct { + VolumeID string `json:"volume_id"` + Name string `json:"name"` + Description string `json:"description"` +} + +// ListSnapshots lists all snapshots related to a storage volume. +func (svc *StorageServiceOp) ListSnapshots(ctx context.Context, volumeID string, opt *ListOptions) ([]Snapshot, *Response, error) { + path := fmt.Sprintf("%s/%s/snapshots", storageAllocPath, volumeID) + path, err := addOptions(path, opt) + if err != nil { + return nil, nil, err + } + + req, err := svc.client.NewRequest(ctx, "GET", path, nil) + if err != nil { + return nil, nil, err + } + + root := new(snapshotsRoot) + resp, err := svc.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + if l := root.Links; l != nil { + resp.Links = l + } + + return root.Snapshots, resp, nil +} + +// CreateSnapshot creates a snapshot of a storage volume. +func (svc *StorageServiceOp) CreateSnapshot(ctx context.Context, createRequest *SnapshotCreateRequest) (*Snapshot, *Response, error) { + path := fmt.Sprintf("%s/%s/snapshots", storageAllocPath, createRequest.VolumeID) + + req, err := svc.client.NewRequest(ctx, "POST", path, createRequest) + if err != nil { + return nil, nil, err + } + + root := new(snapshotRoot) + resp, err := svc.client.Do(req, root) + if err != nil { + return nil, resp, err + } + return root.Snapshot, resp, nil +} + +// GetSnapshot retrieves an individual snapshot. +func (svc *StorageServiceOp) GetSnapshot(ctx context.Context, id string) (*Snapshot, *Response, error) { + path := fmt.Sprintf("%s/%s", storageSnapPath, id) + + req, err := svc.client.NewRequest(ctx, "GET", path, nil) + if err != nil { + return nil, nil, err + } + + root := new(snapshotRoot) + resp, err := svc.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.Snapshot, resp, nil +} + +// DeleteSnapshot deletes a snapshot. +func (svc *StorageServiceOp) DeleteSnapshot(ctx context.Context, id string) (*Response, error) { + path := fmt.Sprintf("%s/%s", storageSnapPath, id) + + req, err := svc.client.NewRequest(ctx, "DELETE", path, nil) + if err != nil { + return nil, err + } + return svc.client.Do(req, nil) +} diff --git a/vendor/github.com/digitalocean/godo/storage_actions.go b/vendor/github.com/digitalocean/godo/storage_actions.go new file mode 100644 index 000000000..21f82659f --- /dev/null +++ b/vendor/github.com/digitalocean/godo/storage_actions.go @@ -0,0 +1,128 @@ +package godo + +import ( + "context" + "fmt" +) + +// StorageActionsService is an interface for interfacing with the +// storage actions endpoints of the Digital Ocean API. +// See: https://developers.digitalocean.com/documentation/v2#storage-actions +type StorageActionsService interface { + Attach(ctx context.Context, volumeID string, dropletID int) (*Action, *Response, error) + DetachByDropletID(ctx context.Context, volumeID string, dropletID int) (*Action, *Response, error) + Get(ctx context.Context, volumeID string, actionID int) (*Action, *Response, error) + List(ctx context.Context, volumeID string, opt *ListOptions) ([]Action, *Response, error) + Resize(ctx context.Context, volumeID string, sizeGigabytes int, regionSlug string) (*Action, *Response, error) +} + +// StorageActionsServiceOp handles communication with the storage volumes +// action related methods of the DigitalOcean API. +type StorageActionsServiceOp struct { + client *Client +} + +// StorageAttachment represents the attachement of a block storage +// volume to a specific Droplet under the device name. +type StorageAttachment struct { + DropletID int `json:"droplet_id"` +} + +// Attach a storage volume to a Droplet. +func (s *StorageActionsServiceOp) Attach(ctx context.Context, volumeID string, dropletID int) (*Action, *Response, error) { + request := &ActionRequest{ + "type": "attach", + "droplet_id": dropletID, + } + return s.doAction(ctx, volumeID, request) +} + +// DetachByDropletID a storage volume from a Droplet by Droplet ID. +func (s *StorageActionsServiceOp) DetachByDropletID(ctx context.Context, volumeID string, dropletID int) (*Action, *Response, error) { + request := &ActionRequest{ + "type": "detach", + "droplet_id": dropletID, + } + return s.doAction(ctx, volumeID, request) +} + +// Get an action for a particular storage volume by id. +func (s *StorageActionsServiceOp) Get(ctx context.Context, volumeID string, actionID int) (*Action, *Response, error) { + path := fmt.Sprintf("%s/%d", storageAllocationActionPath(volumeID), actionID) + return s.get(ctx, path) +} + +// List the actions for a particular storage volume. +func (s *StorageActionsServiceOp) List(ctx context.Context, volumeID string, opt *ListOptions) ([]Action, *Response, error) { + path := storageAllocationActionPath(volumeID) + path, err := addOptions(path, opt) + if err != nil { + return nil, nil, err + } + + return s.list(ctx, path) +} + +// Resize a storage volume. +func (s *StorageActionsServiceOp) Resize(ctx context.Context, volumeID string, sizeGigabytes int, regionSlug string) (*Action, *Response, error) { + request := &ActionRequest{ + "type": "resize", + "size_gigabytes": sizeGigabytes, + "region": regionSlug, + } + return s.doAction(ctx, volumeID, request) +} + +func (s *StorageActionsServiceOp) doAction(ctx context.Context, volumeID string, request *ActionRequest) (*Action, *Response, error) { + path := storageAllocationActionPath(volumeID) + + req, err := s.client.NewRequest(ctx, "POST", path, request) + if err != nil { + return nil, nil, err + } + + root := new(actionRoot) + resp, err := s.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.Event, resp, err +} + +func (s *StorageActionsServiceOp) get(ctx context.Context, path string) (*Action, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", path, nil) + if err != nil { + return nil, nil, err + } + + root := new(actionRoot) + resp, err := s.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.Event, resp, err +} + +func (s *StorageActionsServiceOp) list(ctx context.Context, path string) ([]Action, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", path, nil) + if err != nil { + return nil, nil, err + } + + root := new(actionsRoot) + resp, err := s.client.Do(req, root) + if err != nil { + return nil, resp, err + } + if l := root.Links; l != nil { + resp.Links = l + } + + return root.Actions, resp, err +} + +func storageAllocationActionPath(volumeID string) string { + return fmt.Sprintf("%s/%s/actions", storageAllocPath, volumeID) +} diff --git a/vendor/github.com/digitalocean/godo/strings.go b/vendor/github.com/digitalocean/godo/strings.go index bbdbc9241..4a8bfb636 100644 --- a/vendor/github.com/digitalocean/godo/strings.go +++ b/vendor/github.com/digitalocean/godo/strings.go @@ -3,6 +3,7 @@ package godo import ( "bytes" "fmt" + "io" "reflect" ) @@ -17,7 +18,7 @@ func Stringify(message interface{}) string { } // stringifyValue was graciously cargoculted from the goprotubuf library -func stringifyValue(w *bytes.Buffer, val reflect.Value) { +func stringifyValue(w io.Writer, val reflect.Value) { if val.Kind() == reflect.Ptr && val.IsNil() { _, _ = w.Write([]byte("")) return @@ -29,55 +30,63 @@ func stringifyValue(w *bytes.Buffer, val reflect.Value) { case reflect.String: fmt.Fprintf(w, `"%s"`, v) case reflect.Slice: - _, _ = w.Write([]byte{'['}) - for i := 0; i < v.Len(); i++ { - if i > 0 { - _, _ = w.Write([]byte{' '}) - } - - stringifyValue(w, v.Index(i)) - } - - _, _ = w.Write([]byte{']'}) + stringifySlice(w, v) return case reflect.Struct: - if v.Type().Name() != "" { - _, _ = w.Write([]byte(v.Type().String())) - } - - // special handling of Timestamp values - if v.Type() == timestampType { - fmt.Fprintf(w, "{%s}", v.Interface()) - return - } - - _, _ = w.Write([]byte{'{'}) - - var sep bool - for i := 0; i < v.NumField(); i++ { - fv := v.Field(i) - if fv.Kind() == reflect.Ptr && fv.IsNil() { - continue - } - if fv.Kind() == reflect.Slice && fv.IsNil() { - continue - } - - if sep { - _, _ = w.Write([]byte(", ")) - } else { - sep = true - } - - _, _ = w.Write([]byte(v.Type().Field(i).Name)) - _, _ = w.Write([]byte{':'}) - stringifyValue(w, fv) - } - - _, _ = w.Write([]byte{'}'}) + stringifyStruct(w, v) default: if v.CanInterface() { fmt.Fprint(w, v.Interface()) } } } + +func stringifySlice(w io.Writer, v reflect.Value) { + _, _ = w.Write([]byte{'['}) + for i := 0; i < v.Len(); i++ { + if i > 0 { + _, _ = w.Write([]byte{' '}) + } + + stringifyValue(w, v.Index(i)) + } + + _, _ = w.Write([]byte{']'}) +} + +func stringifyStruct(w io.Writer, v reflect.Value) { + if v.Type().Name() != "" { + _, _ = w.Write([]byte(v.Type().String())) + } + + // special handling of Timestamp values + if v.Type() == timestampType { + fmt.Fprintf(w, "{%s}", v.Interface()) + return + } + + _, _ = w.Write([]byte{'{'}) + + var sep bool + for i := 0; i < v.NumField(); i++ { + fv := v.Field(i) + if fv.Kind() == reflect.Ptr && fv.IsNil() { + continue + } + if fv.Kind() == reflect.Slice && fv.IsNil() { + continue + } + + if sep { + _, _ = w.Write([]byte(", ")) + } else { + sep = true + } + + _, _ = w.Write([]byte(v.Type().Field(i).Name)) + _, _ = w.Write([]byte{':'}) + stringifyValue(w, fv) + } + + _, _ = w.Write([]byte{'}'}) +} diff --git a/vendor/github.com/digitalocean/godo/tags.go b/vendor/github.com/digitalocean/godo/tags.go new file mode 100644 index 000000000..391864434 --- /dev/null +++ b/vendor/github.com/digitalocean/godo/tags.go @@ -0,0 +1,207 @@ +package godo + +import ( + "context" + "fmt" +) + +const tagsBasePath = "v2/tags" + +// TagsService is an interface for interfacing with the tags +// endpoints of the DigitalOcean API +// See: https://developers.digitalocean.com/documentation/v2#tags +type TagsService interface { + List(context.Context, *ListOptions) ([]Tag, *Response, error) + Get(context.Context, string) (*Tag, *Response, error) + Create(context.Context, *TagCreateRequest) (*Tag, *Response, error) + Delete(context.Context, string) (*Response, error) + + TagResources(context.Context, string, *TagResourcesRequest) (*Response, error) + UntagResources(context.Context, string, *UntagResourcesRequest) (*Response, error) +} + +// TagsServiceOp handles communication with tag related method of the +// DigitalOcean API. +type TagsServiceOp struct { + client *Client +} + +var _ TagsService = &TagsServiceOp{} + +// ResourceType represents a class of resource, currently only droplet are supported +type ResourceType string + +const ( + //DropletResourceType holds the string representing our ResourceType of Droplet. + DropletResourceType ResourceType = "droplet" +) + +// Resource represent a single resource for associating/disassociating with tags +type Resource struct { + ID string `json:"resource_id,omit_empty"` + Type ResourceType `json:"resource_type,omit_empty"` +} + +// TaggedResources represent the set of resources a tag is attached to +type TaggedResources struct { + Droplets *TaggedDropletsResources `json:"droplets,omitempty"` +} + +// TaggedDropletsResources represent the droplet resources a tag is attached to +type TaggedDropletsResources struct { + Count int `json:"count,float64,omitempty"` + LastTagged *Droplet `json:"last_tagged,omitempty"` +} + +// Tag represent DigitalOcean tag +type Tag struct { + Name string `json:"name,omitempty"` + Resources *TaggedResources `json:"resources,omitempty"` +} + +//TagCreateRequest represents the JSON structure of a request of that type. +type TagCreateRequest struct { + Name string `json:"name"` +} + +// TagResourcesRequest represents the JSON structure of a request of that type. +type TagResourcesRequest struct { + Resources []Resource `json:"resources"` +} + +// UntagResourcesRequest represents the JSON structure of a request of that type. +type UntagResourcesRequest struct { + Resources []Resource `json:"resources"` +} + +type tagsRoot struct { + Tags []Tag `json:"tags"` + Links *Links `json:"links"` +} + +type tagRoot struct { + Tag *Tag `json:"tag"` +} + +// List all tags +func (s *TagsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Tag, *Response, error) { + path := tagsBasePath + path, err := addOptions(path, opt) + + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", path, nil) + if err != nil { + return nil, nil, err + } + + root := new(tagsRoot) + resp, err := s.client.Do(req, root) + if err != nil { + return nil, resp, err + } + if l := root.Links; l != nil { + resp.Links = l + } + + return root.Tags, resp, err +} + +// Get a single tag +func (s *TagsServiceOp) Get(ctx context.Context, name string) (*Tag, *Response, error) { + path := fmt.Sprintf("%s/%s", tagsBasePath, name) + + req, err := s.client.NewRequest(ctx, "GET", path, nil) + if err != nil { + return nil, nil, err + } + + root := new(tagRoot) + resp, err := s.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.Tag, resp, err +} + +// Create a new tag +func (s *TagsServiceOp) Create(ctx context.Context, createRequest *TagCreateRequest) (*Tag, *Response, error) { + if createRequest == nil { + return nil, nil, NewArgError("createRequest", "cannot be nil") + } + + req, err := s.client.NewRequest(ctx, "POST", tagsBasePath, createRequest) + if err != nil { + return nil, nil, err + } + + root := new(tagRoot) + resp, err := s.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.Tag, resp, err +} + +// Delete an existing tag +func (s *TagsServiceOp) Delete(ctx context.Context, name string) (*Response, error) { + if name == "" { + return nil, NewArgError("name", "cannot be empty") + } + + path := fmt.Sprintf("%s/%s", tagsBasePath, name) + req, err := s.client.NewRequest(ctx, "DELETE", path, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + + return resp, err +} + +// TagResources associates resources with a given Tag. +func (s *TagsServiceOp) TagResources(ctx context.Context, name string, tagRequest *TagResourcesRequest) (*Response, error) { + if name == "" { + return nil, NewArgError("name", "cannot be empty") + } + + if tagRequest == nil { + return nil, NewArgError("tagRequest", "cannot be nil") + } + + path := fmt.Sprintf("%s/%s/resources", tagsBasePath, name) + req, err := s.client.NewRequest(ctx, "POST", path, tagRequest) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + + return resp, err +} + +// UntagResources dissociates resources with a given Tag. +func (s *TagsServiceOp) UntagResources(ctx context.Context, name string, untagRequest *UntagResourcesRequest) (*Response, error) { + if name == "" { + return nil, NewArgError("name", "cannot be empty") + } + + if untagRequest == nil { + return nil, NewArgError("tagRequest", "cannot be nil") + } + + path := fmt.Sprintf("%s/%s/resources", tagsBasePath, name) + req, err := s.client.NewRequest(ctx, "DELETE", path, untagRequest) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + + return resp, err +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 94fce7ed1..01a407f75 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -313,10 +313,11 @@ "revision": "d2709f9f1f31ebcda9651b03077758c1f3a0018c" }, { - "checksumSHA1": "tPavbOLszj9IKgu9ba2TIuRlWLc=", + "checksumSHA1": "W1LGm0UNirwMDVCMFv5vZrOpUJI=", "comment": "v0.9.0-24-g6ca5b77", "path": "github.com/digitalocean/godo", - "revision": "6ca5b770f203b82a0fca68d0941736458efa8a4f" + "revision": "4c04abe183f449bd9ede285f0e5c7ee575d0dbe4", + "revisionTime": "2017-04-07T15:15:42Z" }, { "checksumSHA1": "GCskdwYAPW2S34918Z5CgNMJ2Wc=",