Added monitoring and updated godo
This commit is contained in:
parent
790ac07d2d
commit
5b7ec545c5
|
@ -1,6 +1,7 @@
|
||||||
package digitalocean
|
package digitalocean
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -44,7 +45,8 @@ func (a *Artifact) State(name string) interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Artifact) Destroy() error {
|
func (a *Artifact) Destroy() error {
|
||||||
|
ctx := context.TODO()
|
||||||
log.Printf("Destroying image: %d (%s)", a.snapshotId, a.snapshotName)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ type Config struct {
|
||||||
Image string `mapstructure:"image"`
|
Image string `mapstructure:"image"`
|
||||||
|
|
||||||
PrivateNetworking bool `mapstructure:"private_networking"`
|
PrivateNetworking bool `mapstructure:"private_networking"`
|
||||||
|
Monitoring bool `mapstructure:"monitoring"`
|
||||||
SnapshotName string `mapstructure:"snapshot_name"`
|
SnapshotName string `mapstructure:"snapshot_name"`
|
||||||
StateTimeout time.Duration `mapstructure:"state_timeout"`
|
StateTimeout time.Duration `mapstructure:"state_timeout"`
|
||||||
DropletName string `mapstructure:"droplet_name"`
|
DropletName string `mapstructure:"droplet_name"`
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package digitalocean
|
package digitalocean
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -19,6 +20,7 @@ func (s *stepCreateDroplet) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
c := state.Get("config").(Config)
|
c := state.Get("config").(Config)
|
||||||
sshKeyId := state.Get("ssh_key_id").(int)
|
sshKeyId := state.Get("ssh_key_id").(int)
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
// Create the droplet based on configuration
|
// Create the droplet based on configuration
|
||||||
ui.Say("Creating droplet...")
|
ui.Say("Creating droplet...")
|
||||||
|
@ -34,7 +36,7 @@ func (s *stepCreateDroplet) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
userData = string(contents)
|
userData = string(contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
droplet, _, err := client.Droplets.Create(&godo.DropletCreateRequest{
|
droplet, _, err := client.Droplets.Create(ctx, &godo.DropletCreateRequest{
|
||||||
Name: c.DropletName,
|
Name: c.DropletName,
|
||||||
Region: c.Region,
|
Region: c.Region,
|
||||||
Size: c.Size,
|
Size: c.Size,
|
||||||
|
@ -45,6 +47,7 @@ func (s *stepCreateDroplet) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
{ID: sshKeyId},
|
{ID: sshKeyId},
|
||||||
},
|
},
|
||||||
PrivateNetworking: c.PrivateNetworking,
|
PrivateNetworking: c.PrivateNetworking,
|
||||||
|
Monitoring: c.Monitoring,
|
||||||
UserData: userData,
|
UserData: userData,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -71,10 +74,11 @@ func (s *stepCreateDroplet) Cleanup(state multistep.StateBag) {
|
||||||
|
|
||||||
client := state.Get("client").(*godo.Client)
|
client := state.Get("client").(*godo.Client)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
// Destroy the droplet we just created
|
// Destroy the droplet we just created
|
||||||
ui.Say("Destroying droplet...")
|
ui.Say("Destroying droplet...")
|
||||||
_, err := client.Droplets.Delete(s.dropletId)
|
_, err := client.Droplets.Delete(ctx, s.dropletId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ui.Error(fmt.Sprintf(
|
ui.Error(fmt.Sprintf(
|
||||||
"Error destroying droplet. Please destroy it manually: %s", err))
|
"Error destroying droplet. Please destroy it manually: %s", err))
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package digitalocean
|
package digitalocean
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
@ -27,6 +28,7 @@ type stepCreateSSHKey struct {
|
||||||
func (s *stepCreateSSHKey) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *stepCreateSSHKey) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
client := state.Get("client").(*godo.Client)
|
client := state.Get("client").(*godo.Client)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
ui.Say("Creating temporary ssh key for droplet...")
|
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())
|
name := fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
|
||||||
|
|
||||||
// Create the key!
|
// Create the key!
|
||||||
key, _, err := client.Keys.Create(&godo.KeyCreateRequest{
|
key, _, err := client.Keys.Create(ctx, &godo.KeyCreateRequest{
|
||||||
Name: name,
|
Name: name,
|
||||||
PublicKey: pub_sshformat,
|
PublicKey: pub_sshformat,
|
||||||
})
|
})
|
||||||
|
@ -107,9 +109,10 @@ func (s *stepCreateSSHKey) Cleanup(state multistep.StateBag) {
|
||||||
|
|
||||||
client := state.Get("client").(*godo.Client)
|
client := state.Get("client").(*godo.Client)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
ui.Say("Deleting temporary ssh key...")
|
ui.Say("Deleting temporary ssh key...")
|
||||||
_, err := client.Keys.DeleteByID(s.keyId)
|
_, err := client.Keys.DeleteByID(ctx, s.keyId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error cleaning up ssh key: %s", err)
|
log.Printf("Error cleaning up ssh key: %s", err)
|
||||||
ui.Error(fmt.Sprintf(
|
ui.Error(fmt.Sprintf(
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package digitalocean
|
package digitalocean
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/digitalocean/godo"
|
"github.com/digitalocean/godo"
|
||||||
|
@ -15,6 +16,7 @@ func (s *stepDropletInfo) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
c := state.Get("config").(Config)
|
c := state.Get("config").(Config)
|
||||||
dropletID := state.Get("droplet_id").(int)
|
dropletID := state.Get("droplet_id").(int)
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
ui.Say("Waiting for droplet to become active...")
|
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
|
// Set the IP on the state for later
|
||||||
droplet, _, err := client.Droplets.Get(dropletID)
|
droplet, _, err := client.Droplets.Get(ctx, dropletID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error retrieving droplet: %s", err)
|
err := fmt.Errorf("Error retrieving droplet: %s", err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package digitalocean
|
package digitalocean
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
@ -16,8 +17,9 @@ func (s *stepPowerOff) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
c := state.Get("config").(Config)
|
c := state.Get("config").(Config)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
dropletId := state.Get("droplet_id").(int)
|
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 {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error checking droplet state: %s", err)
|
err := fmt.Errorf("Error checking droplet state: %s", err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
|
@ -32,7 +34,7 @@ func (s *stepPowerOff) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
|
||||||
// Pull the plug on the Droplet
|
// Pull the plug on the Droplet
|
||||||
ui.Say("Forcefully shutting down Droplet...")
|
ui.Say("Forcefully shutting down Droplet...")
|
||||||
_, _, err = client.DropletActions.PowerOff(dropletId)
|
_, _, err = client.DropletActions.PowerOff(ctx, dropletId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error powering off droplet: %s", err)
|
err := fmt.Errorf("Error powering off droplet: %s", err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package digitalocean
|
package digitalocean
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
@ -17,13 +18,14 @@ func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
c := state.Get("config").(Config)
|
c := state.Get("config").(Config)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
dropletId := state.Get("droplet_id").(int)
|
dropletId := state.Get("droplet_id").(int)
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
// Gracefully power off the droplet. We have to retry this a number
|
// Gracefully power off the droplet. We have to retry this a number
|
||||||
// of times because sometimes it says it completed when it actually
|
// of times because sometimes it says it completed when it actually
|
||||||
// did absolutely nothing (*ALAKAZAM!* magic!). We give up after
|
// did absolutely nothing (*ALAKAZAM!* magic!). We give up after
|
||||||
// a pretty arbitrary amount of time.
|
// a pretty arbitrary amount of time.
|
||||||
ui.Say("Gracefully shutting down droplet...")
|
ui.Say("Gracefully shutting down droplet...")
|
||||||
_, _, err := client.DropletActions.Shutdown(dropletId)
|
_, _, err := client.DropletActions.Shutdown(ctx, dropletId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If we get an error the first time, actually report it
|
// If we get an error the first time, actually report it
|
||||||
err := fmt.Errorf("Error shutting down droplet: %s", err)
|
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++ {
|
for attempts := 2; attempts > 0; attempts++ {
|
||||||
log.Printf("ShutdownDroplet attempt #%d...", attempts)
|
log.Printf("ShutdownDroplet attempt #%d...", attempts)
|
||||||
_, _, err := client.DropletActions.Shutdown(dropletId)
|
_, _, err := client.DropletActions.Shutdown(ctx, dropletId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Shutdown retry error: %s", err)
|
log.Printf("Shutdown retry error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package digitalocean
|
package digitalocean
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
@ -18,9 +19,10 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
c := state.Get("config").(Config)
|
c := state.Get("config").(Config)
|
||||||
dropletId := state.Get("droplet_id").(int)
|
dropletId := state.Get("droplet_id").(int)
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Creating snapshot: %v", c.SnapshotName))
|
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 {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error creating snapshot: %s", err)
|
err := fmt.Errorf("Error creating snapshot: %s", err)
|
||||||
state.Put("error", 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)
|
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 {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error looking up snapshot ID: %s", err)
|
err := fmt.Errorf("Error looking up snapshot ID: %s", err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package digitalocean
|
package digitalocean
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
@ -12,6 +13,7 @@ import (
|
||||||
// avoid "pending" errors when making state changes.
|
// avoid "pending" errors when making state changes.
|
||||||
func waitForDropletUnlocked(
|
func waitForDropletUnlocked(
|
||||||
client *godo.Client, dropletId int, timeout time.Duration) error {
|
client *godo.Client, dropletId int, timeout time.Duration) error {
|
||||||
|
ctx := context.TODO()
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
defer close(done)
|
defer close(done)
|
||||||
|
|
||||||
|
@ -22,7 +24,7 @@ func waitForDropletUnlocked(
|
||||||
attempts += 1
|
attempts += 1
|
||||||
|
|
||||||
log.Printf("[DEBUG] Checking droplet lock state... (attempt: %d)", attempts)
|
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 {
|
if err != nil {
|
||||||
result <- err
|
result <- err
|
||||||
return
|
return
|
||||||
|
@ -62,6 +64,7 @@ func waitForDropletUnlocked(
|
||||||
func waitForDropletState(
|
func waitForDropletState(
|
||||||
desiredState string, dropletId int,
|
desiredState string, dropletId int,
|
||||||
client *godo.Client, timeout time.Duration) error {
|
client *godo.Client, timeout time.Duration) error {
|
||||||
|
ctx := context.TODO()
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
defer close(done)
|
defer close(done)
|
||||||
|
|
||||||
|
@ -72,7 +75,7 @@ func waitForDropletState(
|
||||||
attempts += 1
|
attempts += 1
|
||||||
|
|
||||||
log.Printf("Checking droplet status... (attempt: %d)", attempts)
|
log.Printf("Checking droplet status... (attempt: %d)", attempts)
|
||||||
droplet, _, err := client.Droplets.Get(dropletId)
|
droplet, _, err := client.Droplets.Get(ctx, dropletId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result <- err
|
result <- err
|
||||||
return
|
return
|
||||||
|
@ -112,6 +115,7 @@ func waitForDropletState(
|
||||||
func waitForActionState(
|
func waitForActionState(
|
||||||
desiredState string, dropletId, actionId int,
|
desiredState string, dropletId, actionId int,
|
||||||
client *godo.Client, timeout time.Duration) error {
|
client *godo.Client, timeout time.Duration) error {
|
||||||
|
ctx := context.TODO()
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
defer close(done)
|
defer close(done)
|
||||||
|
|
||||||
|
@ -122,7 +126,7 @@ func waitForActionState(
|
||||||
attempts += 1
|
attempts += 1
|
||||||
|
|
||||||
log.Printf("Checking action status... (attempt: %d)", attempts)
|
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 {
|
if err != nil {
|
||||||
result <- err
|
result <- err
|
||||||
return
|
return
|
||||||
|
|
|
@ -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
|
|
@ -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 {
|
if err != nil {
|
||||||
fmt.Printf("Something bad happened: %s\n\n", err)
|
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:
|
If a list of items is paginated by the API, you must request pages individually. For example, to fetch all Droplets:
|
||||||
|
|
||||||
```go
|
```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
|
// create a list to hold our droplets
|
||||||
list := []godo.Droplet{}
|
list := []godo.Droplet{}
|
||||||
|
|
||||||
// create options. initially, these will be blank
|
// create options. initially, these will be blank
|
||||||
opt := &godo.ListOptions{}
|
opt := &godo.ListOptions{}
|
||||||
for {
|
for {
|
||||||
droplets, resp, err := client.Droplets.List(opt)
|
droplets, resp, err := client.Droplets.List(ctx, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package godo
|
package godo
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// AccountService is an interface for interfacing with the Account
|
// AccountService is an interface for interfacing with the Account
|
||||||
// endpoints of the DigitalOcean API
|
// endpoints of the DigitalOcean API
|
||||||
// See: https://developers.digitalocean.com/documentation/v2/#account
|
// See: https://developers.digitalocean.com/documentation/v2/#account
|
||||||
type AccountService interface {
|
type AccountService interface {
|
||||||
Get() (*Account, *Response, error)
|
Get(context.Context) (*Account, *Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccountServiceOp handles communication with the Account related methods of
|
// AccountServiceOp handles communication with the Account related methods of
|
||||||
|
@ -35,10 +37,11 @@ func (r Account) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get DigitalOcean account info
|
// Get DigitalOcean account info
|
||||||
func (s *AccountServiceOp) Get() (*Account, *Response, error) {
|
func (s *AccountServiceOp) Get(ctx context.Context) (*Account, *Response, error) {
|
||||||
|
|
||||||
path := "v2/account"
|
path := "v2/account"
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package godo
|
package godo
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
actionsBasePath = "v2/actions"
|
actionsBasePath = "v2/actions"
|
||||||
|
@ -15,8 +18,8 @@ const (
|
||||||
// ActionsService handles communction with action related methods of the
|
// ActionsService handles communction with action related methods of the
|
||||||
// DigitalOcean API: https://developers.digitalocean.com/documentation/v2#actions
|
// DigitalOcean API: https://developers.digitalocean.com/documentation/v2#actions
|
||||||
type ActionsService interface {
|
type ActionsService interface {
|
||||||
List(*ListOptions) ([]Action, *Response, error)
|
List(context.Context, *ListOptions) ([]Action, *Response, error)
|
||||||
Get(int) (*Action, *Response, error)
|
Get(context.Context, int) (*Action, *Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionsServiceOp handles communition with the image action related methods of the
|
// ActionsServiceOp handles communition with the image action related methods of the
|
||||||
|
@ -33,7 +36,7 @@ type actionsRoot struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type actionRoot struct {
|
type actionRoot struct {
|
||||||
Event Action `json:"action"`
|
Event *Action `json:"action"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action represents a DigitalOcean Action
|
// Action represents a DigitalOcean Action
|
||||||
|
@ -50,14 +53,14 @@ type Action struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all actions
|
// 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 := actionsBasePath
|
||||||
path, err := addOptions(path, opt)
|
path, err := addOptions(path, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -75,13 +78,13 @@ func (s *ActionsServiceOp) List(opt *ListOptions) ([]Action, *Response, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get an action by ID.
|
// 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 {
|
if id < 1 {
|
||||||
return nil, nil, NewArgError("id", "cannot be less than 1")
|
return nil, nil, NewArgError("id", "cannot be less than 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/%d", actionsBasePath, id)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -92,7 +95,7 @@ func (s *ActionsServiceOp) Get(id int) (*Action, *Response, error) {
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &root.Event, resp, err
|
return root.Event, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a Action) String() string {
|
func (a Action) String() string {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
package godo
|
package godo
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
const domainsBasePath = "v2/domains"
|
const domainsBasePath = "v2/domains"
|
||||||
|
|
||||||
|
@ -8,16 +11,16 @@ const domainsBasePath = "v2/domains"
|
||||||
// See: https://developers.digitalocean.com/documentation/v2#domains and
|
// See: https://developers.digitalocean.com/documentation/v2#domains and
|
||||||
// https://developers.digitalocean.com/documentation/v2#domain-records
|
// https://developers.digitalocean.com/documentation/v2#domain-records
|
||||||
type DomainsService interface {
|
type DomainsService interface {
|
||||||
List(*ListOptions) ([]Domain, *Response, error)
|
List(context.Context, *ListOptions) ([]Domain, *Response, error)
|
||||||
Get(string) (*Domain, *Response, error)
|
Get(context.Context, string) (*Domain, *Response, error)
|
||||||
Create(*DomainCreateRequest) (*Domain, *Response, error)
|
Create(context.Context, *DomainCreateRequest) (*Domain, *Response, error)
|
||||||
Delete(string) (*Response, error)
|
Delete(context.Context, string) (*Response, error)
|
||||||
|
|
||||||
Records(string, *ListOptions) ([]DomainRecord, *Response, error)
|
Records(context.Context, string, *ListOptions) ([]DomainRecord, *Response, error)
|
||||||
Record(string, int) (*DomainRecord, *Response, error)
|
Record(context.Context, string, int) (*DomainRecord, *Response, error)
|
||||||
DeleteRecord(string, int) (*Response, error)
|
DeleteRecord(context.Context, string, int) (*Response, error)
|
||||||
EditRecord(string, int, *DomainRecordEditRequest) (*DomainRecord, *Response, error)
|
EditRecord(context.Context, string, int, *DomainRecordEditRequest) (*DomainRecord, *Response, error)
|
||||||
CreateRecord(string, *DomainRecordEditRequest) (*DomainRecord, *Response, error)
|
CreateRecord(context.Context, string, *DomainRecordEditRequest) (*DomainRecord, *Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DomainsServiceOp handles communication with the domain related methods of the
|
// DomainsServiceOp handles communication with the domain related methods of the
|
||||||
|
@ -88,14 +91,14 @@ func (d Domain) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all domains.
|
// 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 := domainsBasePath
|
||||||
path, err := addOptions(path, opt)
|
path, err := addOptions(path, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
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.
|
// 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 {
|
if len(name) < 1 {
|
||||||
return nil, nil, NewArgError("name", "cannot be an empty string")
|
return nil, nil, NewArgError("name", "cannot be an empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/%s", domainsBasePath, name)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -135,14 +138,14 @@ func (s *DomainsServiceOp) Get(name string) (*Domain, *Response, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new domain
|
// 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 {
|
if createRequest == nil {
|
||||||
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
path := domainsBasePath
|
path := domainsBasePath
|
||||||
|
|
||||||
req, err := s.client.NewRequest("POST", path, createRequest)
|
req, err := s.client.NewRequest(ctx, "POST", path, createRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -156,14 +159,14 @@ func (s *DomainsServiceOp) Create(createRequest *DomainCreateRequest) (*Domain,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete 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 {
|
if len(name) < 1 {
|
||||||
return nil, NewArgError("name", "cannot be an empty string")
|
return nil, NewArgError("name", "cannot be an empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/%s", domainsBasePath, name)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -184,7 +187,7 @@ func (d DomainRecordEditRequest) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Records returns a slice of DomainRecords for a domain
|
// 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 {
|
if len(domain) < 1 {
|
||||||
return nil, nil, NewArgError("domain", "cannot be an empty string")
|
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
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
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
|
// 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 {
|
if len(domain) < 1 {
|
||||||
return nil, nil, NewArgError("domain", "cannot be an empty string")
|
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)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
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
|
// 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 {
|
if len(domain) < 1 {
|
||||||
return nil, NewArgError("domain", "cannot be an empty string")
|
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)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -261,7 +264,7 @@ func (s *DomainsServiceOp) DeleteRecord(domain string, id int) (*Response, error
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditRecord edits a record using a DomainRecordEditRequest
|
// EditRecord edits a record using a DomainRecordEditRequest
|
||||||
func (s *DomainsServiceOp) EditRecord(
|
func (s *DomainsServiceOp) EditRecord(ctx context.Context,
|
||||||
domain string,
|
domain string,
|
||||||
id int,
|
id int,
|
||||||
editRequest *DomainRecordEditRequest,
|
editRequest *DomainRecordEditRequest,
|
||||||
|
@ -280,7 +283,7 @@ func (s *DomainsServiceOp) EditRecord(
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/%s/records/%d", domainsBasePath, domain, id)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -295,7 +298,7 @@ func (s *DomainsServiceOp) EditRecord(
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateRecord creates a record using a DomainRecordEditRequest
|
// CreateRecord creates a record using a DomainRecordEditRequest
|
||||||
func (s *DomainsServiceOp) CreateRecord(
|
func (s *DomainsServiceOp) CreateRecord(ctx context.Context,
|
||||||
domain string,
|
domain string,
|
||||||
createRequest *DomainRecordEditRequest) (*DomainRecord, *Response, error) {
|
createRequest *DomainRecordEditRequest) (*DomainRecord, *Response, error) {
|
||||||
if len(domain) < 1 {
|
if len(domain) < 1 {
|
||||||
|
@ -307,7 +310,7 @@ func (s *DomainsServiceOp) CreateRecord(
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/%s/records", domainsBasePath, domain)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package godo
|
package godo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
@ -8,33 +9,42 @@ import (
|
||||||
// ActionRequest reprents DigitalOcean Action Request
|
// ActionRequest reprents DigitalOcean Action Request
|
||||||
type ActionRequest map[string]interface{}
|
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
|
// endpoints of the DigitalOcean API
|
||||||
// See: https://developers.digitalocean.com/documentation/v2#droplet-actions
|
// See: https://developers.digitalocean.com/documentation/v2#droplet-actions
|
||||||
type DropletActionsService interface {
|
type DropletActionsService interface {
|
||||||
Shutdown(int) (*Action, *Response, error)
|
Shutdown(context.Context, int) (*Action, *Response, error)
|
||||||
PowerOff(int) (*Action, *Response, error)
|
ShutdownByTag(context.Context, string) (*Action, *Response, error)
|
||||||
PowerOn(int) (*Action, *Response, error)
|
PowerOff(context.Context, int) (*Action, *Response, error)
|
||||||
PowerCycle(int) (*Action, *Response, error)
|
PowerOffByTag(context.Context, string) (*Action, *Response, error)
|
||||||
Reboot(int) (*Action, *Response, error)
|
PowerOn(context.Context, int) (*Action, *Response, error)
|
||||||
Restore(int, int) (*Action, *Response, error)
|
PowerOnByTag(context.Context, string) (*Action, *Response, error)
|
||||||
Resize(int, string, bool) (*Action, *Response, error)
|
PowerCycle(context.Context, int) (*Action, *Response, error)
|
||||||
Rename(int, string) (*Action, *Response, error)
|
PowerCycleByTag(context.Context, string) (*Action, *Response, error)
|
||||||
Snapshot(int, string) (*Action, *Response, error)
|
Reboot(context.Context, int) (*Action, *Response, error)
|
||||||
EnableBackups(int) (*Action, *Response, error)
|
Restore(context.Context, int, int) (*Action, *Response, error)
|
||||||
DisableBackups(int) (*Action, *Response, error)
|
Resize(context.Context, int, string, bool) (*Action, *Response, error)
|
||||||
PasswordReset(int) (*Action, *Response, error)
|
Rename(context.Context, int, string) (*Action, *Response, error)
|
||||||
RebuildByImageID(int, int) (*Action, *Response, error)
|
Snapshot(context.Context, int, string) (*Action, *Response, error)
|
||||||
RebuildByImageSlug(int, string) (*Action, *Response, error)
|
SnapshotByTag(context.Context, string, string) (*Action, *Response, error)
|
||||||
ChangeKernel(int, int) (*Action, *Response, error)
|
EnableBackups(context.Context, int) (*Action, *Response, error)
|
||||||
EnableIPv6(int) (*Action, *Response, error)
|
EnableBackupsByTag(context.Context, string) (*Action, *Response, error)
|
||||||
EnablePrivateNetworking(int) (*Action, *Response, error)
|
DisableBackups(context.Context, int) (*Action, *Response, error)
|
||||||
Upgrade(int) (*Action, *Response, error)
|
DisableBackupsByTag(context.Context, string) (*Action, *Response, error)
|
||||||
Get(int, int) (*Action, *Response, error)
|
PasswordReset(context.Context, int) (*Action, *Response, error)
|
||||||
GetByURI(string) (*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.
|
// methods of the DigitalOcean API.
|
||||||
type DropletActionsServiceOp struct {
|
type DropletActionsServiceOp struct {
|
||||||
client *Client
|
client *Client
|
||||||
|
@ -43,131 +53,189 @@ type DropletActionsServiceOp struct {
|
||||||
var _ DropletActionsService = &DropletActionsServiceOp{}
|
var _ DropletActionsService = &DropletActionsServiceOp{}
|
||||||
|
|
||||||
// Shutdown a Droplet
|
// 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"}
|
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
|
// 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"}
|
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
|
// 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"}
|
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
|
// 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"}
|
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
|
// 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"}
|
request := &ActionRequest{"type": "reboot"}
|
||||||
return s.doAction(id, request)
|
return s.doAction(ctx, id, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore an image to a Droplet
|
// 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"
|
requestType := "restore"
|
||||||
request := &ActionRequest{
|
request := &ActionRequest{
|
||||||
"type": requestType,
|
"type": requestType,
|
||||||
"image": float64(imageID),
|
"image": float64(imageID),
|
||||||
}
|
}
|
||||||
return s.doAction(id, request)
|
return s.doAction(ctx, id, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize a Droplet
|
// 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"
|
requestType := "resize"
|
||||||
request := &ActionRequest{
|
request := &ActionRequest{
|
||||||
"type": requestType,
|
"type": requestType,
|
||||||
"size": sizeSlug,
|
"size": sizeSlug,
|
||||||
"disk": resizeDisk,
|
"disk": resizeDisk,
|
||||||
}
|
}
|
||||||
return s.doAction(id, request)
|
return s.doAction(ctx, id, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rename a Droplet
|
// 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"
|
requestType := "rename"
|
||||||
request := &ActionRequest{
|
request := &ActionRequest{
|
||||||
"type": requestType,
|
"type": requestType,
|
||||||
"name": name,
|
"name": name,
|
||||||
}
|
}
|
||||||
return s.doAction(id, request)
|
return s.doAction(ctx, id, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snapshot a Droplet.
|
// 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"
|
requestType := "snapshot"
|
||||||
request := &ActionRequest{
|
request := &ActionRequest{
|
||||||
"type": requestType,
|
"type": requestType,
|
||||||
"name": name,
|
"name": name,
|
||||||
}
|
}
|
||||||
return s.doAction(id, request)
|
return s.doAction(ctx, id, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnableBackups enables backups for a droplet.
|
// SnapshotByTag snapshots Droplets matched by a Tag.
|
||||||
func (s *DropletActionsServiceOp) EnableBackups(id int) (*Action, *Response, error) {
|
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"}
|
request := &ActionRequest{"type": "enable_backups"}
|
||||||
return s.doAction(id, request)
|
return s.doAction(ctx, id, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisableBackups disables backups for a droplet.
|
// EnableBackupsByTag enables backups for Droplets matched by a Tag.
|
||||||
func (s *DropletActionsServiceOp) DisableBackups(id int) (*Action, *Response, error) {
|
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"}
|
request := &ActionRequest{"type": "disable_backups"}
|
||||||
return s.doAction(id, request)
|
return s.doAction(ctx, id, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PasswordReset resets the password for a droplet.
|
// DisableBackupsByTag disables backups for Droplet matched by a Tag.
|
||||||
func (s *DropletActionsServiceOp) PasswordReset(id int) (*Action, *Response, error) {
|
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"}
|
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.
|
// RebuildByImageID rebuilds a Droplet from an image with a given id.
|
||||||
func (s *DropletActionsServiceOp) RebuildByImageID(id, imageID int) (*Action, *Response, error) {
|
func (s *DropletActionsServiceOp) RebuildByImageID(ctx context.Context, id, imageID int) (*Action, *Response, error) {
|
||||||
request := &ActionRequest{"type": "rebuild", "image": imageID}
|
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.
|
// RebuildByImageSlug rebuilds a Droplet from an Image matched by a given Slug.
|
||||||
func (s *DropletActionsServiceOp) RebuildByImageSlug(id int, slug string) (*Action, *Response, error) {
|
func (s *DropletActionsServiceOp) RebuildByImageSlug(ctx context.Context, id int, slug string) (*Action, *Response, error) {
|
||||||
request := &ActionRequest{"type": "rebuild", "image": slug}
|
request := &ActionRequest{"type": "rebuild", "image": slug}
|
||||||
return s.doAction(id, request)
|
return s.doAction(ctx, id, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeKernel changes the kernel for a droplet.
|
// ChangeKernel changes the kernel for a Droplet.
|
||||||
func (s *DropletActionsServiceOp) ChangeKernel(id, kernelID int) (*Action, *Response, error) {
|
func (s *DropletActionsServiceOp) ChangeKernel(ctx context.Context, id, kernelID int) (*Action, *Response, error) {
|
||||||
request := &ActionRequest{"type": "change_kernel", "kernel": kernelID}
|
request := &ActionRequest{"type": "change_kernel", "kernel": kernelID}
|
||||||
return s.doAction(id, request)
|
return s.doAction(ctx, id, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnableIPv6 enables IPv6 for a droplet.
|
// EnableIPv6 enables IPv6 for a Droplet.
|
||||||
func (s *DropletActionsServiceOp) EnableIPv6(id int) (*Action, *Response, error) {
|
func (s *DropletActionsServiceOp) EnableIPv6(ctx context.Context, id int) (*Action, *Response, error) {
|
||||||
request := &ActionRequest{"type": "enable_ipv6"}
|
request := &ActionRequest{"type": "enable_ipv6"}
|
||||||
return s.doAction(id, request)
|
return s.doAction(ctx, id, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnablePrivateNetworking enables private networking for a droplet.
|
// EnableIPv6ByTag enables IPv6 for Droplets matched by a Tag.
|
||||||
func (s *DropletActionsServiceOp) EnablePrivateNetworking(id int) (*Action, *Response, error) {
|
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"}
|
request := &ActionRequest{"type": "enable_private_networking"}
|
||||||
return s.doAction(id, request)
|
return s.doAction(ctx, id, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upgrade a droplet.
|
// EnablePrivateNetworkingByTag enables private networking for Droplets matched by a Tag.
|
||||||
func (s *DropletActionsServiceOp) Upgrade(id int) (*Action, *Response, error) {
|
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"}
|
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 {
|
if id < 1 {
|
||||||
return nil, nil, NewArgError("id", "cannot be less than 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)
|
path := dropletActionPath(id)
|
||||||
|
|
||||||
req, err := s.client.NewRequest("POST", path, request)
|
req, err := s.client.NewRequest(ctx, "POST", path, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -189,11 +257,36 @@ func (s *DropletActionsServiceOp) doAction(id int, request *ActionRequest) (*Act
|
||||||
return nil, resp, err
|
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) doActionByTag(ctx context.Context, tag string, request *ActionRequest) (*Action, *Response, error) {
|
||||||
func (s *DropletActionsServiceOp) Get(dropletID, actionID int) (*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 {
|
if dropletID < 1 {
|
||||||
return nil, nil, NewArgError("dropletID", "cannot be less than 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)
|
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.
|
// GetByURI gets an action for a particular Droplet by id.
|
||||||
func (s *DropletActionsServiceOp) GetByURI(rawurl string) (*Action, *Response, error) {
|
func (s *DropletActionsServiceOp) GetByURI(ctx context.Context, rawurl string) (*Action, *Response, error) {
|
||||||
u, err := url.Parse(rawurl)
|
u, err := url.Parse(rawurl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.get(u.Path)
|
return s.get(ctx, u.Path)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DropletActionsServiceOp) get(path string) (*Action, *Response, error) {
|
func (s *DropletActionsServiceOp) get(ctx context.Context, path string) (*Action, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -229,10 +322,14 @@ func (s *DropletActionsServiceOp) get(path string) (*Action, *Response, error) {
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &root.Event, resp, err
|
return root.Event, resp, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func dropletActionPath(dropletID int) string {
|
func dropletActionPath(dropletID int) string {
|
||||||
return fmt.Sprintf("v2/droplets/%d/actions", dropletID)
|
return fmt.Sprintf("v2/droplets/%d/actions", dropletID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dropletActionPathByTag(tag string) string {
|
||||||
|
return fmt.Sprintf("v2/droplets/actions?tag_name=%s", tag)
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package godo
|
package godo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -10,23 +11,25 @@ const dropletBasePath = "v2/droplets"
|
||||||
|
|
||||||
var errNoNetworks = errors.New("no networks have been defined")
|
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
|
// endpoints of the DigitalOcean API
|
||||||
// See: https://developers.digitalocean.com/documentation/v2#droplets
|
// See: https://developers.digitalocean.com/documentation/v2#droplets
|
||||||
type DropletsService interface {
|
type DropletsService interface {
|
||||||
List(*ListOptions) ([]Droplet, *Response, error)
|
List(context.Context, *ListOptions) ([]Droplet, *Response, error)
|
||||||
Get(int) (*Droplet, *Response, error)
|
ListByTag(context.Context, string, *ListOptions) ([]Droplet, *Response, error)
|
||||||
Create(*DropletCreateRequest) (*Droplet, *Response, error)
|
Get(context.Context, int) (*Droplet, *Response, error)
|
||||||
CreateMultiple(*DropletMultiCreateRequest) ([]Droplet, *Response, error)
|
Create(context.Context, *DropletCreateRequest) (*Droplet, *Response, error)
|
||||||
Delete(int) (*Response, error)
|
CreateMultiple(context.Context, *DropletMultiCreateRequest) ([]Droplet, *Response, error)
|
||||||
Kernels(int, *ListOptions) ([]Kernel, *Response, error)
|
Delete(context.Context, int) (*Response, error)
|
||||||
Snapshots(int, *ListOptions) ([]Image, *Response, error)
|
DeleteByTag(context.Context, string) (*Response, error)
|
||||||
Backups(int, *ListOptions) ([]Image, *Response, error)
|
Kernels(context.Context, int, *ListOptions) ([]Kernel, *Response, error)
|
||||||
Actions(int, *ListOptions) ([]Action, *Response, error)
|
Snapshots(context.Context, int, *ListOptions) ([]Image, *Response, error)
|
||||||
Neighbors(int) ([]Droplet, *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.
|
// DigitalOcean API.
|
||||||
type DropletsServiceOp struct {
|
type DropletsServiceOp struct {
|
||||||
client *Client
|
client *Client
|
||||||
|
@ -36,23 +39,26 @@ var _ DropletsService = &DropletsServiceOp{}
|
||||||
|
|
||||||
// Droplet represents a DigitalOcean Droplet
|
// Droplet represents a DigitalOcean Droplet
|
||||||
type Droplet struct {
|
type Droplet struct {
|
||||||
ID int `json:"id,float64,omitempty"`
|
ID int `json:"id,float64,omitempty"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Memory int `json:"memory,omitempty"`
|
Memory int `json:"memory,omitempty"`
|
||||||
Vcpus int `json:"vcpus,omitempty"`
|
Vcpus int `json:"vcpus,omitempty"`
|
||||||
Disk int `json:"disk,omitempty"`
|
Disk int `json:"disk,omitempty"`
|
||||||
Region *Region `json:"region,omitempty"`
|
Region *Region `json:"region,omitempty"`
|
||||||
Image *Image `json:"image,omitempty"`
|
Image *Image `json:"image,omitempty"`
|
||||||
Size *Size `json:"size,omitempty"`
|
Size *Size `json:"size,omitempty"`
|
||||||
SizeSlug string `json:"size_slug,omitempty"`
|
SizeSlug string `json:"size_slug,omitempty"`
|
||||||
BackupIDs []int `json:"backup_ids,omitempty"`
|
BackupIDs []int `json:"backup_ids,omitempty"`
|
||||||
SnapshotIDs []int `json:"snapshot_ids,omitempty"`
|
NextBackupWindow *BackupWindow `json:"next_backup_window,omitempty"`
|
||||||
Locked bool `json:"locked,bool,omitempty"`
|
SnapshotIDs []int `json:"snapshot_ids,omitempty"`
|
||||||
Status string `json:"status,omitempty"`
|
Features []string `json:"features,omitempty"`
|
||||||
Networks *Networks `json:"networks,omitempty"`
|
Locked bool `json:"locked,bool,omitempty"`
|
||||||
ActionIDs []int `json:"action_ids,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
Created string `json:"created_at,omitempty"`
|
Networks *Networks `json:"networks,omitempty"`
|
||||||
Kernel *Kernel `json:"kernel, 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.
|
// PublicIPv4 returns the public IPv4 address for the Droplet.
|
||||||
|
@ -85,15 +91,15 @@ func (d *Droplet) PrivateIPv4() (string, error) {
|
||||||
return "", nil
|
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) {
|
func (d *Droplet) PublicIPv6() (string, error) {
|
||||||
if d.Networks == nil {
|
if d.Networks == nil {
|
||||||
return "", errNoNetworks
|
return "", errNoNetworks
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v4 := range d.Networks.V6 {
|
for _, v6 := range d.Networks.V6 {
|
||||||
if v4.Type == "public" {
|
if v6.Type == "public" {
|
||||||
return v4.IPAddress, nil
|
return v6.IPAddress, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +113,12 @@ type Kernel struct {
|
||||||
Version string `json:"version,omitempty"`
|
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
|
// Convert Droplet to a string
|
||||||
func (d Droplet) String() string {
|
func (d Droplet) String() string {
|
||||||
return Stringify(d)
|
return Stringify(d)
|
||||||
|
@ -128,7 +140,7 @@ type kernelsRoot struct {
|
||||||
Links *Links `json:"links"`
|
Links *Links `json:"links"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type snapshotsRoot struct {
|
type dropletSnapshotsRoot struct {
|
||||||
Snapshots []Image `json:"snapshots,omitempty"`
|
Snapshots []Image `json:"snapshots,omitempty"`
|
||||||
Links *Links `json:"links"`
|
Links *Links `json:"links"`
|
||||||
}
|
}
|
||||||
|
@ -154,6 +166,27 @@ func (d DropletCreateImage) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(d.ID)
|
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.
|
// DropletCreateSSHKey identifies a SSH Key for the create request. It prefers fingerprint over ID.
|
||||||
type DropletCreateSSHKey struct {
|
type DropletCreateSSHKey struct {
|
||||||
ID int
|
ID int
|
||||||
|
@ -170,7 +203,7 @@ func (d DropletCreateSSHKey) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(d.ID)
|
return json.Marshal(d.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DropletCreateRequest represents a request to create a droplet.
|
// DropletCreateRequest represents a request to create a Droplet.
|
||||||
type DropletCreateRequest struct {
|
type DropletCreateRequest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
|
@ -180,10 +213,13 @@ type DropletCreateRequest struct {
|
||||||
Backups bool `json:"backups"`
|
Backups bool `json:"backups"`
|
||||||
IPv6 bool `json:"ipv6"`
|
IPv6 bool `json:"ipv6"`
|
||||||
PrivateNetworking bool `json:"private_networking"`
|
PrivateNetworking bool `json:"private_networking"`
|
||||||
|
Monitoring bool `json:"monitoring"`
|
||||||
UserData string `json:"user_data,omitempty"`
|
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 {
|
type DropletMultiCreateRequest struct {
|
||||||
Names []string `json:"names"`
|
Names []string `json:"names"`
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
|
@ -193,7 +229,9 @@ type DropletMultiCreateRequest struct {
|
||||||
Backups bool `json:"backups"`
|
Backups bool `json:"backups"`
|
||||||
IPv6 bool `json:"ipv6"`
|
IPv6 bool `json:"ipv6"`
|
||||||
PrivateNetworking bool `json:"private_networking"`
|
PrivateNetworking bool `json:"private_networking"`
|
||||||
|
Monitoring bool `json:"monitoring"`
|
||||||
UserData string `json:"user_data,omitempty"`
|
UserData string `json:"user_data,omitempty"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d DropletCreateRequest) String() string {
|
func (d DropletCreateRequest) String() string {
|
||||||
|
@ -204,13 +242,13 @@ func (d DropletMultiCreateRequest) String() string {
|
||||||
return Stringify(d)
|
return Stringify(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Networks represents the droplet's networks
|
// Networks represents the Droplet's Networks.
|
||||||
type Networks struct {
|
type Networks struct {
|
||||||
V4 []NetworkV4 `json:"v4,omitempty"`
|
V4 []NetworkV4 `json:"v4,omitempty"`
|
||||||
V6 []NetworkV6 `json:"v6,omitempty"`
|
V6 []NetworkV6 `json:"v6,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkV4 represents a DigitalOcean IPv4 Network
|
// NetworkV4 represents a DigitalOcean IPv4 Network.
|
||||||
type NetworkV4 struct {
|
type NetworkV4 struct {
|
||||||
IPAddress string `json:"ip_address,omitempty"`
|
IPAddress string `json:"ip_address,omitempty"`
|
||||||
Netmask string `json:"netmask,omitempty"`
|
Netmask string `json:"netmask,omitempty"`
|
||||||
|
@ -234,40 +272,56 @@ func (n NetworkV6) String() string {
|
||||||
return Stringify(n)
|
return Stringify(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all droplets
|
// Performs a list request given a path.
|
||||||
func (s *DropletsServiceOp) List(opt *ListOptions) ([]Droplet, *Response, error) {
|
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 := dropletBasePath
|
||||||
path, err := addOptions(path, opt)
|
path, err := addOptions(path, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
root := new(dropletsRoot)
|
return s.list(ctx, path)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get individual droplet
|
// Get individual Droplet.
|
||||||
func (s *DropletsServiceOp) Get(dropletID int) (*Droplet, *Response, error) {
|
func (s *DropletsServiceOp) Get(ctx context.Context, dropletID int) (*Droplet, *Response, error) {
|
||||||
if dropletID < 1 {
|
if dropletID < 1 {
|
||||||
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
|
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/%d", dropletBasePath, dropletID)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -281,15 +335,15 @@ func (s *DropletsServiceOp) Get(dropletID int) (*Droplet, *Response, error) {
|
||||||
return root.Droplet, resp, err
|
return root.Droplet, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create droplet
|
// Create Droplet
|
||||||
func (s *DropletsServiceOp) Create(createRequest *DropletCreateRequest) (*Droplet, *Response, error) {
|
func (s *DropletsServiceOp) Create(ctx context.Context, createRequest *DropletCreateRequest) (*Droplet, *Response, error) {
|
||||||
if createRequest == nil {
|
if createRequest == nil {
|
||||||
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
path := dropletBasePath
|
path := dropletBasePath
|
||||||
|
|
||||||
req, err := s.client.NewRequest("POST", path, createRequest)
|
req, err := s.client.NewRequest(ctx, "POST", path, createRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -306,15 +360,15 @@ func (s *DropletsServiceOp) Create(createRequest *DropletCreateRequest) (*Drople
|
||||||
return root.Droplet, resp, err
|
return root.Droplet, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateMultiple creates multiple droplets.
|
// CreateMultiple creates multiple Droplets.
|
||||||
func (s *DropletsServiceOp) CreateMultiple(createRequest *DropletMultiCreateRequest) ([]Droplet, *Response, error) {
|
func (s *DropletsServiceOp) CreateMultiple(ctx context.Context, createRequest *DropletMultiCreateRequest) ([]Droplet, *Response, error) {
|
||||||
if createRequest == nil {
|
if createRequest == nil {
|
||||||
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
path := dropletBasePath
|
path := dropletBasePath
|
||||||
|
|
||||||
req, err := s.client.NewRequest("POST", path, createRequest)
|
req, err := s.client.NewRequest(ctx, "POST", path, createRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -331,15 +385,9 @@ func (s *DropletsServiceOp) CreateMultiple(createRequest *DropletMultiCreateRequ
|
||||||
return root.Droplets, resp, err
|
return root.Droplets, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete droplet
|
// Performs a delete request given a path
|
||||||
func (s *DropletsServiceOp) Delete(dropletID int) (*Response, error) {
|
func (s *DropletsServiceOp) delete(ctx context.Context, path string) (*Response, error) {
|
||||||
if dropletID < 1 {
|
req, err := s.client.NewRequest(ctx, "DELETE", path, nil)
|
||||||
return nil, NewArgError("dropletID", "cannot be less than 1")
|
|
||||||
}
|
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/%d", dropletBasePath, dropletID)
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("DELETE", path, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -349,8 +397,30 @@ func (s *DropletsServiceOp) Delete(dropletID int) (*Response, error) {
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kernels lists kernels available for a droplet.
|
// Delete Droplet.
|
||||||
func (s *DropletsServiceOp) Kernels(dropletID int, opt *ListOptions) ([]Kernel, *Response, error) {
|
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 {
|
if dropletID < 1 {
|
||||||
return nil, nil, NewArgError("dropletID", "cannot be less than 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
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -375,8 +445,8 @@ func (s *DropletsServiceOp) Kernels(dropletID int, opt *ListOptions) ([]Kernel,
|
||||||
return root.Kernels, resp, err
|
return root.Kernels, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions lists the actions for a droplet.
|
// Actions lists the actions for a Droplet.
|
||||||
func (s *DropletsServiceOp) Actions(dropletID int, opt *ListOptions) ([]Action, *Response, error) {
|
func (s *DropletsServiceOp) Actions(ctx context.Context, dropletID int, opt *ListOptions) ([]Action, *Response, error) {
|
||||||
if dropletID < 1 {
|
if dropletID < 1 {
|
||||||
return nil, nil, NewArgError("dropletID", "cannot be less than 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
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -404,8 +474,8 @@ func (s *DropletsServiceOp) Actions(dropletID int, opt *ListOptions) ([]Action,
|
||||||
return root.Actions, resp, err
|
return root.Actions, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backups lists the backups for a droplet.
|
// Backups lists the backups for a Droplet.
|
||||||
func (s *DropletsServiceOp) Backups(dropletID int, opt *ListOptions) ([]Image, *Response, error) {
|
func (s *DropletsServiceOp) Backups(ctx context.Context, dropletID int, opt *ListOptions) ([]Image, *Response, error) {
|
||||||
if dropletID < 1 {
|
if dropletID < 1 {
|
||||||
return nil, nil, NewArgError("dropletID", "cannot be less than 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
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -433,8 +503,8 @@ func (s *DropletsServiceOp) Backups(dropletID int, opt *ListOptions) ([]Image, *
|
||||||
return root.Backups, resp, err
|
return root.Backups, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snapshots lists the snapshots available for a droplet.
|
// Snapshots lists the snapshots available for a Droplet.
|
||||||
func (s *DropletsServiceOp) Snapshots(dropletID int, opt *ListOptions) ([]Image, *Response, error) {
|
func (s *DropletsServiceOp) Snapshots(ctx context.Context, dropletID int, opt *ListOptions) ([]Image, *Response, error) {
|
||||||
if dropletID < 1 {
|
if dropletID < 1 {
|
||||||
return nil, nil, NewArgError("dropletID", "cannot be less than 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
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
root := new(snapshotsRoot)
|
root := new(dropletSnapshotsRoot)
|
||||||
resp, err := s.client.Do(req, root)
|
resp, err := s.client.Do(req, root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
|
@ -462,15 +532,15 @@ func (s *DropletsServiceOp) Snapshots(dropletID int, opt *ListOptions) ([]Image,
|
||||||
return root.Snapshots, resp, err
|
return root.Snapshots, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Neighbors lists the neighbors for a droplet.
|
// Neighbors lists the neighbors for a Droplet.
|
||||||
func (s *DropletsServiceOp) Neighbors(dropletID int) ([]Droplet, *Response, error) {
|
func (s *DropletsServiceOp) Neighbors(ctx context.Context, dropletID int) ([]Droplet, *Response, error) {
|
||||||
if dropletID < 1 {
|
if dropletID < 1 {
|
||||||
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
|
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/%d/neighbors", dropletBasePath, dropletID)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -484,8 +554,8 @@ func (s *DropletsServiceOp) Neighbors(dropletID int) ([]Droplet, *Response, erro
|
||||||
return root.Droplets, resp, err
|
return root.Droplets, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DropletsServiceOp) dropletActionStatus(uri string) (string, error) {
|
func (s *DropletsServiceOp) dropletActionStatus(ctx context.Context, uri string) (string, error) {
|
||||||
action, _, err := s.client.DropletActions.GetByURI(uri)
|
action, _, err := s.client.DropletActions.GetByURI(ctx, uri)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package godo
|
package godo
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
const floatingBasePath = "v2/floating_ips"
|
const floatingBasePath = "v2/floating_ips"
|
||||||
|
|
||||||
|
@ -8,10 +11,10 @@ const floatingBasePath = "v2/floating_ips"
|
||||||
// endpoints of the Digital Ocean API.
|
// endpoints of the Digital Ocean API.
|
||||||
// See: https://developers.digitalocean.com/documentation/v2#floating-ips
|
// See: https://developers.digitalocean.com/documentation/v2#floating-ips
|
||||||
type FloatingIPsService interface {
|
type FloatingIPsService interface {
|
||||||
List(*ListOptions) ([]FloatingIP, *Response, error)
|
List(context.Context, *ListOptions) ([]FloatingIP, *Response, error)
|
||||||
Get(string) (*FloatingIP, *Response, error)
|
Get(context.Context, string) (*FloatingIP, *Response, error)
|
||||||
Create(*FloatingIPCreateRequest) (*FloatingIP, *Response, error)
|
Create(context.Context, *FloatingIPCreateRequest) (*FloatingIP, *Response, error)
|
||||||
Delete(string) (*Response, error)
|
Delete(context.Context, string) (*Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FloatingIPsServiceOp handles communication with the floating IPs related methods of the
|
// FloatingIPsServiceOp handles communication with the floating IPs related methods of the
|
||||||
|
@ -52,14 +55,14 @@ type FloatingIPCreateRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all floating IPs.
|
// 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 := floatingBasePath
|
||||||
path, err := addOptions(path, opt)
|
path, err := addOptions(path, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := f.client.NewRequest("GET", path, nil)
|
req, err := f.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -77,10 +80,10 @@ func (f *FloatingIPsServiceOp) List(opt *ListOptions) ([]FloatingIP, *Response,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get an individual floating IP.
|
// 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)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
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,
|
// Create a floating IP. If the DropletID field of the request is not empty,
|
||||||
// the floating IP will also be assigned to the droplet.
|
// 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
|
path := floatingBasePath
|
||||||
|
|
||||||
req, err := f.client.NewRequest("POST", path, createRequest)
|
req, err := f.client.NewRequest(ctx, "POST", path, createRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -117,10 +120,10 @@ func (f *FloatingIPsServiceOp) Create(createRequest *FloatingIPCreateRequest) (*
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a floating IP.
|
// 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)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
package godo
|
package godo
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// FloatingIPActionsService is an interface for interfacing with the
|
// FloatingIPActionsService is an interface for interfacing with the
|
||||||
// floating IPs actions endpoints of the Digital Ocean API.
|
// floating IPs actions endpoints of the Digital Ocean API.
|
||||||
// See: https://developers.digitalocean.com/documentation/v2#floating-ips-action
|
// See: https://developers.digitalocean.com/documentation/v2#floating-ips-action
|
||||||
type FloatingIPActionsService interface {
|
type FloatingIPActionsService interface {
|
||||||
Assign(ip string, dropletID int) (*Action, *Response, error)
|
Assign(ctx context.Context, ip string, dropletID int) (*Action, *Response, error)
|
||||||
Unassign(ip string) (*Action, *Response, error)
|
Unassign(ctx context.Context, ip string) (*Action, *Response, error)
|
||||||
Get(ip string, actionID int) (*Action, *Response, error)
|
Get(ctx context.Context, ip string, actionID int) (*Action, *Response, error)
|
||||||
List(ip string, opt *ListOptions) ([]Action, *Response, error)
|
List(ctx context.Context, ip string, opt *ListOptions) ([]Action, *Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FloatingIPActionsServiceOp handles communication with the floating IPs
|
// FloatingIPActionsServiceOp handles communication with the floating IPs
|
||||||
|
@ -19,41 +22,41 @@ type FloatingIPActionsServiceOp struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign a floating IP to a droplet.
|
// 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{
|
request := &ActionRequest{
|
||||||
"type": "assign",
|
"type": "assign",
|
||||||
"droplet_id": dropletID,
|
"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.
|
// 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"}
|
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.
|
// 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)
|
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.
|
// 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 := floatingIPActionPath(ip)
|
||||||
path, err := addOptions(path, opt)
|
path, err := addOptions(path, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
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)
|
path := floatingIPActionPath(ip)
|
||||||
|
|
||||||
req, err := s.client.NewRequest("POST", path, request)
|
req, err := s.client.NewRequest(ctx, "POST", path, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -64,11 +67,11 @@ func (s *FloatingIPActionsServiceOp) doAction(ip string, request *ActionRequest)
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &root.Event, resp, err
|
return root.Event, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FloatingIPActionsServiceOp) get(path string) (*Action, *Response, error) {
|
func (s *FloatingIPActionsServiceOp) get(ctx context.Context, path string) (*Action, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -79,11 +82,11 @@ func (s *FloatingIPActionsServiceOp) get(path string) (*Action, *Response, error
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &root.Event, resp, err
|
return root.Event, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FloatingIPActionsServiceOp) list(path string) ([]Action, *Response, error) {
|
func (s *FloatingIPActionsServiceOp) list(ctx context.Context, path string) ([]Action, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package godo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -17,7 +18,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
libraryVersion = "0.1.0"
|
libraryVersion = "1.0.0"
|
||||||
defaultBaseURL = "https://api.digitalocean.com/"
|
defaultBaseURL = "https://api.digitalocean.com/"
|
||||||
userAgent = "godo/" + libraryVersion
|
userAgent = "godo/" + libraryVersion
|
||||||
mediaType = "application/json"
|
mediaType = "application/json"
|
||||||
|
@ -55,6 +56,12 @@ type Client struct {
|
||||||
Sizes SizesService
|
Sizes SizesService
|
||||||
FloatingIPs FloatingIPsService
|
FloatingIPs FloatingIPsService
|
||||||
FloatingIPActions FloatingIPActionsService
|
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
|
// Optional function called after every successful request made to the DO APIs
|
||||||
onRequestCompleted RequestCompletionCallback
|
onRequestCompleted RequestCompletionCallback
|
||||||
|
@ -93,7 +100,10 @@ type ErrorResponse struct {
|
||||||
Response *http.Response
|
Response *http.Response
|
||||||
|
|
||||||
// Error message
|
// 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.
|
// 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.Domains = &DomainsServiceOp{client: c}
|
||||||
c.Droplets = &DropletsServiceOp{client: c}
|
c.Droplets = &DropletsServiceOp{client: c}
|
||||||
c.DropletActions = &DropletActionsServiceOp{client: c}
|
c.DropletActions = &DropletActionsServiceOp{client: c}
|
||||||
|
c.FloatingIPs = &FloatingIPsServiceOp{client: c}
|
||||||
|
c.FloatingIPActions = &FloatingIPActionsServiceOp{client: c}
|
||||||
c.Images = &ImagesServiceOp{client: c}
|
c.Images = &ImagesServiceOp{client: c}
|
||||||
c.ImageActions = &ImageActionsServiceOp{client: c}
|
c.ImageActions = &ImageActionsServiceOp{client: c}
|
||||||
c.Keys = &KeysServiceOp{client: c}
|
c.Keys = &KeysServiceOp{client: c}
|
||||||
c.Regions = &RegionsServiceOp{client: c}
|
c.Regions = &RegionsServiceOp{client: c}
|
||||||
|
c.Snapshots = &SnapshotsServiceOp{client: c}
|
||||||
c.Sizes = &SizesServiceOp{client: c}
|
c.Sizes = &SizesServiceOp{client: c}
|
||||||
c.FloatingIPs = &FloatingIPsServiceOp{client: c}
|
c.Storage = &StorageServiceOp{client: c}
|
||||||
c.FloatingIPActions = &FloatingIPActionsServiceOp{client: c}
|
c.StorageActions = &StorageActionsServiceOp{client: c}
|
||||||
|
c.Tags = &TagsServiceOp{client: c}
|
||||||
|
c.LoadBalancers = &LoadBalancersServiceOp{client: c}
|
||||||
|
c.Certificates = &CertificatesServiceOp{client: c}
|
||||||
|
|
||||||
return 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
|
// 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
|
// 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.
|
// 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)
|
rel, err := url.Parse(urlStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -173,7 +225,7 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
if body != nil {
|
if body != nil {
|
||||||
err := json.NewEncoder(buf).Encode(body)
|
err = json.NewEncoder(buf).Encode(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -184,9 +236,10 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req = req.WithContext(ctx)
|
||||||
req.Header.Add("Content-Type", mediaType)
|
req.Header.Add("Content-Type", mediaType)
|
||||||
req.Header.Add("Accept", mediaType)
|
req.Header.Add("Accept", mediaType)
|
||||||
req.Header.Add("User-Agent", userAgent)
|
req.Header.Add("User-Agent", c.UserAgent)
|
||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,12 +318,12 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
|
||||||
|
|
||||||
if v != nil {
|
if v != nil {
|
||||||
if w, ok := v.(io.Writer); ok {
|
if w, ok := v.(io.Writer); ok {
|
||||||
_, err := io.Copy(w, resp.Body)
|
_, err = io.Copy(w, resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err := json.NewDecoder(resp.Body).Decode(v)
|
err = json.NewDecoder(resp.Body).Decode(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -280,6 +333,10 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
func (r *ErrorResponse) Error() string {
|
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",
|
return fmt.Sprintf("%v %v: %d %v",
|
||||||
r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, r.Message)
|
r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, r.Message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
package godo
|
package godo
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// ImageActionsService is an interface for interfacing with the image actions
|
// ImageActionsService is an interface for interfacing with the image actions
|
||||||
// endpoints of the DigitalOcean API
|
// endpoints of the DigitalOcean API
|
||||||
// See: https://developers.digitalocean.com/documentation/v2#image-actions
|
// See: https://developers.digitalocean.com/documentation/v2#image-actions
|
||||||
type ImageActionsService interface {
|
type ImageActionsService interface {
|
||||||
Get(int, int) (*Action, *Response, error)
|
Get(context.Context, int, int) (*Action, *Response, error)
|
||||||
Transfer(int, *ActionRequest) (*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
|
// ImageActionsServiceOp handles communition with the image action related methods of the
|
||||||
|
@ -19,7 +23,7 @@ type ImageActionsServiceOp struct {
|
||||||
var _ ImageActionsService = &ImageActionsServiceOp{}
|
var _ ImageActionsService = &ImageActionsServiceOp{}
|
||||||
|
|
||||||
// Transfer an image
|
// 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 {
|
if imageID < 1 {
|
||||||
return nil, nil, NewArgError("imageID", "cannot be less than 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)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -41,11 +45,37 @@ func (i *ImageActionsServiceOp) Transfer(imageID int, transferRequest *ActionReq
|
||||||
return nil, resp, err
|
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.
|
// 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 {
|
if imageID < 1 {
|
||||||
return nil, nil, NewArgError("imageID", "cannot be less than 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)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -67,5 +97,5 @@ func (i *ImageActionsServiceOp) Get(imageID, actionID int) (*Action, *Response,
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &root.Event, resp, err
|
return root.Event, resp, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package godo
|
package godo
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
const imageBasePath = "v2/images"
|
const imageBasePath = "v2/images"
|
||||||
|
|
||||||
|
@ -8,14 +11,14 @@ const imageBasePath = "v2/images"
|
||||||
// endpoints of the DigitalOcean API
|
// endpoints of the DigitalOcean API
|
||||||
// See: https://developers.digitalocean.com/documentation/v2#images
|
// See: https://developers.digitalocean.com/documentation/v2#images
|
||||||
type ImagesService interface {
|
type ImagesService interface {
|
||||||
List(*ListOptions) ([]Image, *Response, error)
|
List(context.Context, *ListOptions) ([]Image, *Response, error)
|
||||||
ListDistribution(opt *ListOptions) ([]Image, *Response, error)
|
ListDistribution(ctx context.Context, opt *ListOptions) ([]Image, *Response, error)
|
||||||
ListApplication(opt *ListOptions) ([]Image, *Response, error)
|
ListApplication(ctx context.Context, opt *ListOptions) ([]Image, *Response, error)
|
||||||
ListUser(opt *ListOptions) ([]Image, *Response, error)
|
ListUser(ctx context.Context, opt *ListOptions) ([]Image, *Response, error)
|
||||||
GetByID(int) (*Image, *Response, error)
|
GetByID(context.Context, int) (*Image, *Response, error)
|
||||||
GetBySlug(string) (*Image, *Response, error)
|
GetBySlug(context.Context, string) (*Image, *Response, error)
|
||||||
Update(int, *ImageUpdateRequest) (*Image, *Response, error)
|
Update(context.Context, int, *ImageUpdateRequest) (*Image, *Response, error)
|
||||||
Delete(int) (*Response, error)
|
Delete(context.Context, int) (*Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImagesServiceOp handles communication with the image related methods of the
|
// ImagesServiceOp handles communication with the image related methods of the
|
||||||
|
@ -45,7 +48,7 @@ type ImageUpdateRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type imageRoot struct {
|
type imageRoot struct {
|
||||||
Image Image
|
Image *Image
|
||||||
}
|
}
|
||||||
|
|
||||||
type imagesRoot struct {
|
type imagesRoot struct {
|
||||||
|
@ -63,48 +66,48 @@ func (i Image) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// List lists all the images available.
|
// List lists all the images available.
|
||||||
func (s *ImagesServiceOp) List(opt *ListOptions) ([]Image, *Response, error) {
|
func (s *ImagesServiceOp) List(ctx context.Context, opt *ListOptions) ([]Image, *Response, error) {
|
||||||
return s.list(opt, nil)
|
return s.list(ctx, opt, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListDistribution lists all the distribution images.
|
// 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"}
|
listOpt := listImageOptions{Type: "distribution"}
|
||||||
return s.list(opt, &listOpt)
|
return s.list(ctx, opt, &listOpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListApplication lists all the application images.
|
// 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"}
|
listOpt := listImageOptions{Type: "application"}
|
||||||
return s.list(opt, &listOpt)
|
return s.list(ctx, opt, &listOpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListUser lists all the user images.
|
// 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}
|
listOpt := listImageOptions{Private: true}
|
||||||
return s.list(opt, &listOpt)
|
return s.list(ctx, opt, &listOpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByID retrieves an image by id.
|
// 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 {
|
if imageID < 1 {
|
||||||
return nil, nil, NewArgError("imageID", "cannot be less than 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.
|
// 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 {
|
if len(slug) < 1 {
|
||||||
return nil, nil, NewArgError("slug", "cannot be blank")
|
return nil, nil, NewArgError("slug", "cannot be blank")
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.get(interface{}(slug))
|
return s.get(ctx, interface{}(slug))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update an image name.
|
// 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 {
|
if imageID < 1 {
|
||||||
return nil, nil, NewArgError("imageID", "cannot be less than 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)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -125,18 +128,18 @@ func (s *ImagesServiceOp) Update(imageID int, updateRequest *ImageUpdateRequest)
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &root.Image, resp, err
|
return root.Image, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete an image.
|
// 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 {
|
if imageID < 1 {
|
||||||
return nil, NewArgError("imageID", "cannot be less than 1")
|
return nil, NewArgError("imageID", "cannot be less than 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/%d", imageBasePath, imageID)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -147,10 +150,10 @@ func (s *ImagesServiceOp) Delete(imageID int) (*Response, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method for getting an individual image
|
// 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)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -161,11 +164,11 @@ func (s *ImagesServiceOp) get(ID interface{}) (*Image, *Response, error) {
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &root.Image, resp, err
|
return root.Image, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method for listing images
|
// 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 := imageBasePath
|
||||||
path, err := addOptions(path, opt)
|
path, err := addOptions(path, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -176,7 +179,7 @@ func (s *ImagesServiceOp) list(opt *ListOptions, listOpt *listImageOptions) ([]I
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package godo
|
package godo
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
const keysBasePath = "v2/account/keys"
|
const keysBasePath = "v2/account/keys"
|
||||||
|
|
||||||
|
@ -8,14 +11,14 @@ const keysBasePath = "v2/account/keys"
|
||||||
// endpoints of the DigitalOcean API
|
// endpoints of the DigitalOcean API
|
||||||
// See: https://developers.digitalocean.com/documentation/v2#keys
|
// See: https://developers.digitalocean.com/documentation/v2#keys
|
||||||
type KeysService interface {
|
type KeysService interface {
|
||||||
List(*ListOptions) ([]Key, *Response, error)
|
List(context.Context, *ListOptions) ([]Key, *Response, error)
|
||||||
GetByID(int) (*Key, *Response, error)
|
GetByID(context.Context, int) (*Key, *Response, error)
|
||||||
GetByFingerprint(string) (*Key, *Response, error)
|
GetByFingerprint(context.Context, string) (*Key, *Response, error)
|
||||||
Create(*KeyCreateRequest) (*Key, *Response, error)
|
Create(context.Context, *KeyCreateRequest) (*Key, *Response, error)
|
||||||
UpdateByID(int, *KeyUpdateRequest) (*Key, *Response, error)
|
UpdateByID(context.Context, int, *KeyUpdateRequest) (*Key, *Response, error)
|
||||||
UpdateByFingerprint(string, *KeyUpdateRequest) (*Key, *Response, error)
|
UpdateByFingerprint(context.Context, string, *KeyUpdateRequest) (*Key, *Response, error)
|
||||||
DeleteByID(int) (*Response, error)
|
DeleteByID(context.Context, int) (*Response, error)
|
||||||
DeleteByFingerprint(string) (*Response, error)
|
DeleteByFingerprint(context.Context, string) (*Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeysServiceOp handles communication with key related method of the
|
// KeysServiceOp handles communication with key related method of the
|
||||||
|
@ -45,7 +48,7 @@ type keysRoot struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type keyRoot struct {
|
type keyRoot struct {
|
||||||
SSHKey Key `json:"ssh_key"`
|
SSHKey *Key `json:"ssh_key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Key) String() string {
|
func (s Key) String() string {
|
||||||
|
@ -59,14 +62,14 @@ type KeyCreateRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all keys
|
// 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 := keysBasePath
|
||||||
path, err := addOptions(path, opt)
|
path, err := addOptions(path, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -84,8 +87,8 @@ func (s *KeysServiceOp) List(opt *ListOptions) ([]Key, *Response, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Performs a get given a path
|
// Performs a get given a path
|
||||||
func (s *KeysServiceOp) get(path string) (*Key, *Response, error) {
|
func (s *KeysServiceOp) get(ctx context.Context, path string) (*Key, *Response, error) {
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -96,36 +99,36 @@ func (s *KeysServiceOp) get(path string) (*Key, *Response, error) {
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &root.SSHKey, resp, err
|
return root.SSHKey, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByID gets a Key by id
|
// 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 {
|
if keyID < 1 {
|
||||||
return nil, nil, NewArgError("keyID", "cannot be less than 1")
|
return nil, nil, NewArgError("keyID", "cannot be less than 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/%d", keysBasePath, keyID)
|
path := fmt.Sprintf("%s/%d", keysBasePath, keyID)
|
||||||
return s.get(path)
|
return s.get(ctx, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByFingerprint gets a Key by by fingerprint
|
// 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 {
|
if len(fingerprint) < 1 {
|
||||||
return nil, nil, NewArgError("fingerprint", "cannot not be empty")
|
return nil, nil, NewArgError("fingerprint", "cannot not be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/%s", keysBasePath, fingerprint)
|
path := fmt.Sprintf("%s/%s", keysBasePath, fingerprint)
|
||||||
return s.get(path)
|
return s.get(ctx, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a key using a KeyCreateRequest
|
// 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 {
|
if createRequest == nil {
|
||||||
return nil, nil, NewArgError("createRequest", "cannot be 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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -136,11 +139,11 @@ func (s *KeysServiceOp) Create(createRequest *KeyCreateRequest) (*Key, *Response
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &root.SSHKey, resp, err
|
return root.SSHKey, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateByID updates a key name by ID.
|
// 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 {
|
if keyID < 1 {
|
||||||
return nil, nil, NewArgError("keyID", "cannot be less than 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)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -161,11 +164,11 @@ func (s *KeysServiceOp) UpdateByID(keyID int, updateRequest *KeyUpdateRequest) (
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &root.SSHKey, resp, err
|
return root.SSHKey, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateByFingerprint updates a key name by fingerprint.
|
// 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 {
|
if len(fingerprint) < 1 {
|
||||||
return nil, nil, NewArgError("fingerprint", "cannot be empty")
|
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)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -186,12 +189,12 @@ func (s *KeysServiceOp) UpdateByFingerprint(fingerprint string, updateRequest *K
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &root.SSHKey, resp, err
|
return root.SSHKey, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete key using a path
|
// Delete key using a path
|
||||||
func (s *KeysServiceOp) delete(path string) (*Response, error) {
|
func (s *KeysServiceOp) delete(ctx context.Context, path string) (*Response, error) {
|
||||||
req, err := s.client.NewRequest("DELETE", path, nil)
|
req, err := s.client.NewRequest(ctx, "DELETE", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -202,21 +205,21 @@ func (s *KeysServiceOp) delete(path string) (*Response, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteByID deletes a key by its id
|
// 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 {
|
if keyID < 1 {
|
||||||
return nil, NewArgError("keyID", "cannot be less than 1")
|
return nil, NewArgError("keyID", "cannot be less than 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/%d", keysBasePath, keyID)
|
path := fmt.Sprintf("%s/%d", keysBasePath, keyID)
|
||||||
return s.delete(path)
|
return s.delete(ctx, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteByFingerprint deletes a key by its fingerprint
|
// 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 {
|
if len(fingerprint) < 1 {
|
||||||
return nil, NewArgError("fingerprint", "cannot be empty")
|
return nil, NewArgError("fingerprint", "cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("%s/%s", keysBasePath, fingerprint)
|
path := fmt.Sprintf("%s/%s", keysBasePath, fingerprint)
|
||||||
return s.delete(path)
|
return s.delete(ctx, path)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package godo
|
package godo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
@ -58,11 +59,7 @@ func (l *Links) IsLastPage() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pages) isLast() bool {
|
func (p *Pages) isLast() bool {
|
||||||
if p.Last == "" {
|
return p.Last == ""
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func pageForURL(urlText string) (int, error) {
|
func pageForURL(urlText string) (int, error) {
|
||||||
|
@ -81,6 +78,6 @@ func pageForURL(urlText string) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a link action by id.
|
// Get a link action by id.
|
||||||
func (la *LinkAction) Get(client *Client) (*Action, *Response, error) {
|
func (la *LinkAction) Get(ctx context.Context, client *Client) (*Action, *Response, error) {
|
||||||
return client.Actions.Get(la.ID)
|
return client.Actions.Get(ctx, la.ID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -1,10 +1,12 @@
|
||||||
package godo
|
package godo
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// RegionsService is an interface for interfacing with the regions
|
// RegionsService is an interface for interfacing with the regions
|
||||||
// endpoints of the DigitalOcean API
|
// endpoints of the DigitalOcean API
|
||||||
// See: https://developers.digitalocean.com/documentation/v2#regions
|
// See: https://developers.digitalocean.com/documentation/v2#regions
|
||||||
type RegionsService interface {
|
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
|
// RegionsServiceOp handles communication with the region related methods of the
|
||||||
|
@ -29,23 +31,19 @@ type regionsRoot struct {
|
||||||
Links *Links `json:"links"`
|
Links *Links `json:"links"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type regionRoot struct {
|
|
||||||
Region Region
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Region) String() string {
|
func (r Region) String() string {
|
||||||
return Stringify(r)
|
return Stringify(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all regions
|
// 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 := "v2/regions"
|
||||||
path, err := addOptions(path, opt)
|
path, err := addOptions(path, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package godo
|
package godo
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// SizesService is an interface for interfacing with the size
|
// SizesService is an interface for interfacing with the size
|
||||||
// endpoints of the DigitalOcean API
|
// endpoints of the DigitalOcean API
|
||||||
// See: https://developers.digitalocean.com/documentation/v2#sizes
|
// See: https://developers.digitalocean.com/documentation/v2#sizes
|
||||||
type SizesService interface {
|
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
|
// SizesServiceOp handles communication with the size related methods of the
|
||||||
|
@ -38,14 +40,14 @@ type sizesRoot struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all images
|
// 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 := "v2/sizes"
|
||||||
path, err := addOptions(path, opt)
|
path, err := addOptions(path, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", path, nil)
|
req, err := s.client.NewRequest(ctx, "GET", path, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package godo
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ func Stringify(message interface{}) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// stringifyValue was graciously cargoculted from the goprotubuf library
|
// 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() {
|
if val.Kind() == reflect.Ptr && val.IsNil() {
|
||||||
_, _ = w.Write([]byte("<nil>"))
|
_, _ = w.Write([]byte("<nil>"))
|
||||||
return
|
return
|
||||||
|
@ -29,55 +30,63 @@ func stringifyValue(w *bytes.Buffer, val reflect.Value) {
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
fmt.Fprintf(w, `"%s"`, v)
|
fmt.Fprintf(w, `"%s"`, v)
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
_, _ = w.Write([]byte{'['})
|
stringifySlice(w, v)
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
if i > 0 {
|
|
||||||
_, _ = w.Write([]byte{' '})
|
|
||||||
}
|
|
||||||
|
|
||||||
stringifyValue(w, v.Index(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _ = w.Write([]byte{']'})
|
|
||||||
return
|
return
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
if v.Type().Name() != "" {
|
stringifyStruct(w, v)
|
||||||
_, _ = 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{'}'})
|
|
||||||
default:
|
default:
|
||||||
if v.CanInterface() {
|
if v.CanInterface() {
|
||||||
fmt.Fprint(w, v.Interface())
|
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{'}'})
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -313,10 +313,11 @@
|
||||||
"revision": "d2709f9f1f31ebcda9651b03077758c1f3a0018c"
|
"revision": "d2709f9f1f31ebcda9651b03077758c1f3a0018c"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "tPavbOLszj9IKgu9ba2TIuRlWLc=",
|
"checksumSHA1": "W1LGm0UNirwMDVCMFv5vZrOpUJI=",
|
||||||
"comment": "v0.9.0-24-g6ca5b77",
|
"comment": "v0.9.0-24-g6ca5b77",
|
||||||
"path": "github.com/digitalocean/godo",
|
"path": "github.com/digitalocean/godo",
|
||||||
"revision": "6ca5b770f203b82a0fca68d0941736458efa8a4f"
|
"revision": "4c04abe183f449bd9ede285f0e5c7ee575d0dbe4",
|
||||||
|
"revisionTime": "2017-04-07T15:15:42Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "GCskdwYAPW2S34918Z5CgNMJ2Wc=",
|
"checksumSHA1": "GCskdwYAPW2S34918Z5CgNMJ2Wc=",
|
||||||
|
|
Loading…
Reference in New Issue